imagecore/image/image.h (253 lines of code) (raw):
/*
* MIT License
*
* Copyright (c) 2017 Twitter
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "imagecore/imagecore.h"
#include "imagecore/image/kernel.h"
namespace imagecore {
// translates from number of channels to primitive type
template<uint32_t channels> class toPrimType {
public:
typedef uint8_t asType;
};
template<> class toPrimType<1> {
public:
typedef uint8_t asType;
};
template<> class toPrimType<2> {
public:
typedef uint16_t asType;
};
template<> class toPrimType<4> {
public:
typedef uint32_t asType;
};
// components, with scalar and simd specializations
template<uint32_t Channels>
struct ComponentBase {
typedef toPrimType<Channels> primType;
uint8_t mChannels[Channels];
};
template<uint32_t Channels>
struct ComponentScalar : public ComponentBase<Channels> {
};
template<uint32_t Channels>
struct ComponentSIMD : public ComponentBase<Channels> {
};
enum EResizeQuality
{
kResizeQuality_Bilinear = 0,
kResizeQuality_Low,
kResizeQuality_Medium,
kResizeQuality_High,
kResizeQuality_HighSharp,
kResizeQuality_MAX
};
enum ECropGravity
{
kGravityHeuristic = 0, // top for portraits, else center
kGravityCenter,
kGravityLeft,
kGravityTop,
kGravityRight,
kGravityBottom
};
enum EImageColorModel
{
kColorModel_RGBA,
kColorModel_RGBX,
kColorModel_Grayscale,
kColorModel_YUV_420
};
enum EImageOrientation
{
kImageOrientation_Up = 1,
kImageOrientation_Down = 3,
kImageOrientation_Left = 6,
kImageOrientation_Right = 8
};
enum EResolutionUnit
{
kNone = 1,
kInches = 2,
kCm = 3
};
enum EAltitudeRef
{
kAboveSeaLevel = 0,
kBelowSeaLevel = 1
};
enum EEdgeMask
{
kEdge_None = 0x00,
kEdge_Left = 0x01,
kEdge_Top = 0x02,
kEdge_Right = 0x04,
kEdge_Bottom = 0x08,
kEdge_All = kEdge_Left | kEdge_Top | kEdge_Right | kEdge_Bottom
};
// A bounding box class
class ImageRegion
{
public:
ImageRegion(unsigned int width, unsigned int height, unsigned int left, unsigned int top)
{
m_Left = left;
m_Top = top;
m_Width = width;
m_Height = height;
}
// these return NULL if there is an error
static ImageRegion* fromString(const char* input);
static ImageRegion* fromGravity(
unsigned int width,
unsigned int height,
unsigned int targetWidth,
unsigned int targetHeight,
ECropGravity gravity);
// accessors
unsigned int left() const { return m_Left; }
unsigned int right() const { return m_Left + m_Width; }
unsigned int top() const { return m_Top; }
unsigned int bottom() const { return m_Top + m_Height; }
unsigned int width() const { return m_Width; }
unsigned int height() const { return m_Height; }
// mutators
void left(unsigned int value) { m_Left = value; }
void top(unsigned int value) { m_Top = value; }
void width(unsigned int value) { m_Width = value; }
void height(unsigned int value) { m_Height = value; }
protected:
unsigned int m_Left;
unsigned int m_Top;
unsigned int m_Width;
unsigned int m_Height;
};
class ImageGrayscale;
class ImageRGBA;
class ImageYUV;
class ImageInterleaved;
class Image
{
public:
static Image* create(EImageColorModel colorSpace, unsigned int width, unsigned int height);
static Image* create(EImageColorModel colorSpace, unsigned int width, unsigned int height, unsigned int padding, unsigned int alignment);
virtual ~Image() {}
virtual void setDimensions(unsigned int width, unsigned int height) = 0;
virtual void setDimensions(unsigned int width, unsigned int height, unsigned int padding, unsigned int alignment) = 0;
virtual void setPadding(unsigned int padding) = 0;
virtual bool resize(Image* dest, EResizeQuality quality) = 0;
virtual void reduceHalf(Image* dest) = 0;
virtual bool crop(const ImageRegion& boundingBox) = 0;
virtual void rotate(Image* dest, EImageOrientation direction) = 0;
virtual void fillPadding() = 0;
virtual void clear(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
virtual void clearRect(unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint8_t r, uint8_t g, uint8_t b, uint8_t a) = 0;
virtual void copy(Image* dest);
virtual void copyRect(Image* dest, unsigned int sourceX, unsigned int sourceY, unsigned int destX, unsigned int destY, unsigned int width, unsigned int height) = 0;
virtual Image* move() = 0;
virtual unsigned int getWidth() const = 0;
virtual unsigned int getHeight() const = 0;
virtual unsigned int getPadding() = 0;
virtual EImageColorModel getColorModel() const = 0;
virtual class ImageRGBA* asRGBA() = 0;
virtual class ImageGrayscale* asGrayscale() = 0;
virtual class ImageYUV* asYUV() = 0;
virtual class ImageYUVSemiplanar* asYUVSemiplanar() = 0;
virtual class ImageInterleaved* asInterleaved() = 0;
static bool colorModelIsRGBA(EImageColorModel colorModel)
{
return colorModel == kColorModel_RGBA || colorModel == kColorModel_RGBX;
}
static bool colorModelIsGrayscale(EImageColorModel colorModel)
{
return colorModel == kColorModel_Grayscale;
}
static bool colorModelIsInterleaved(EImageColorModel colorModel)
{
return colorModelIsRGBA(colorModel) || colorModelIsGrayscale(colorModel);
}
static bool colorModelIsYUV(EImageColorModel colorModel)
{
return colorModel == kColorModel_YUV_420;
}
static unsigned int getDownsampleFilterKernelSize(EResizeQuality quality);
static EFilterType getDownsampleFilterKernelType(EResizeQuality quality);
static unsigned int getUpsampleFilterKernelSize(EResizeQuality quality);
static EFilterType getUpsampleFilterKernelType(EResizeQuality quality);
static bool validateSize(unsigned int width, unsigned int height);
};
template <uint32_t Channels>
class ImagePlane
{
typedef toPrimType<Channels> primType;
public:
static ImagePlane* create(uint8_t* buffer, unsigned int capacity);
static ImagePlane* create(unsigned int width, unsigned int height);
static ImagePlane* create(unsigned int width, unsigned int height, unsigned int padding, unsigned int alignment);
~ImagePlane();
const uint8_t* getBytes();
uint8_t* lockRect(unsigned int width, unsigned int height, unsigned int& pitch);
uint8_t* lockRect(unsigned int x, unsigned int y, unsigned int width, unsigned int height, unsigned int& pitch);
void unlockRect();
void setDimensions(unsigned int width, unsigned int height);
void setDimensions(unsigned int width, unsigned int height, unsigned int padding, unsigned int alignment);
void setPadding(unsigned int padding);
void setOffset(unsigned int offsetX, unsigned int offsetY);
bool resize(ImagePlane* dest, EResizeQuality quality);
void reduceHalf(ImagePlane* dest);
bool downsampleFilter(ImagePlane* dest, const FilterKernelAdaptive* filterKernelX, const FilterKernelAdaptive* filterKernelY, bool unpadded);
bool crop(const ImageRegion& boundingBox);
void rotate(ImagePlane* dest, EImageOrientation direction);
void transpose(ImagePlane* dest);
void fillPadding(EEdgeMask edgeMask = kEdge_All);
void clear(typename primType::asType component);
void clearRect(unsigned int x, unsigned int y, unsigned int w, unsigned int h, typename primType::asType component);
void copyRect(ImagePlane* dest, unsigned int sourceX, unsigned int sourceY, unsigned int destX, unsigned int destY, unsigned int width, unsigned int height);
void copy(ImagePlane* dest);
unsigned int getWidth()
{
return m_Width;
}
unsigned int getHeight()
{
return m_Height;
}
unsigned int getPitch()
{
return m_Pitch;
}
unsigned int getPadding()
{
return m_Padding;
}
unsigned int getCapacity()
{
return m_Capacity;
}
unsigned int getAlignment()
{
return m_Alignment;
}
unsigned int getImageSize();
private:
ImagePlane(uint8_t* buffer, unsigned int capacity, bool ownsBuffer);
bool checkCapacity(unsigned int width, unsigned int height);
bool downsampleFilterSeperable(ImagePlane* dest, const FilterKernelAdaptive* filterKernelX, const FilterKernelAdaptive* filterKernelY, bool unpadded);
bool downsampleFilter4x4(ImagePlane* dest, const FilterKernelAdaptive* filterKernelX, const FilterKernelAdaptive* filterKernelY);
bool downsampleFilter2x2(ImagePlane* dest, const FilterKernelAdaptive* filterKernelX, const FilterKernelAdaptive* filterKernelY);
bool upsampleFilter4x4(ImagePlane* dest, const FilterKernelFixed* filterKernelX, const FilterKernelFixed* filterKernelY);
static unsigned int paddingOffset(unsigned int pitch, unsigned int pad_amount);
static unsigned int paddedPitch(unsigned int width, unsigned int pad_amount, unsigned int alignment);
static unsigned int totalImageSize(unsigned int width, unsigned int height, unsigned int padAmount, unsigned int alignment);
uint8_t* m_Buffer;
unsigned int m_Capacity;
unsigned int m_Width;
unsigned int m_Height;
unsigned int m_Pitch;
unsigned int m_Padding;
unsigned int m_OffsetX;
unsigned int m_OffsetY;
unsigned int m_Alignment;
unsigned int m_PadRegionDirty;
bool m_OwnsBuffer;
};
extern template class ImagePlane<1>;
extern template class ImagePlane<2>;
extern template class ImagePlane<4>;
typedef ImagePlane<1> ImagePlaneGrayscale;
typedef ImagePlane<1> ImagePlane8;
typedef ImagePlane<2> ImagePlane16;
typedef ImagePlane<4> ImagePlaneRGBA;
}