//------------------------------------------------------------------------------
// emImage.h
//
// Copyright (C) 2001,2003-2010,2014,2018,2020 Oliver Hamann.
//
// Homepage: http://eaglemode.sourceforge.net/
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License version 3 as published by the
// Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
// more details.
//
// You should have received a copy of the GNU General Public License version 3
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------
#ifndef emImage_h
#define emImage_h
#ifndef emColor_h
#include <emCore/emColor.h>
#endif
#ifndef emATMatrix_h
#include <emCore/emATMatrix.h>
#endif
class emRootContext;
class emPainter;
//==============================================================================
//================================== emImage ===================================
//==============================================================================
class emImage {
public:
// Class for an image with copy-on-write behavior. Such an image
// consists of a three-dimensional array of bytes. The dimensions are X,
// Y and color channel.
//
// An image can have one, two, three or four channels. Each channel has
// one byte per pixel. The meaning of the channels depends on the number
// of channels:
//
// ChannelCount = 1: Grey image without alpha
// channel 0: grey components.
// ChannelCount = 2: Grey image with alpha
// channel 0: grey components.
// channel 1: alpha components.
// ChannelCount = 3: Color image without alpha
// channel 0: red components.
// channel 1: green components.
// channel 2: blue components.
// ChannelCount = 4: Color image with alpha
// channel 0: red components.
// channel 1: green components.
// channel 2: blue components.
// channel 3: alpha components.
//
// Like in most computer graphics, the origin of the image is in the
// upper-left corner, the X-axis points to the right, and the Y-axis
// points down. Pixels are areas and not points.
emImage();
// Construct an empty image. It has Width==0, Height==0 and
// ChannelCount==1.
emImage(const emImage & img);
// Construct a copied image.
emImage(int width, int height, int channelCount);
// Construct with the given dimensions. The pixel map is not
// initialized.
// Arguments:
// width - Horizontal extent in pixels.
// height - Vertical extent in pixels.
// channelCount - Number of channels (bytes per pixel, 1-4).
~emImage();
// Destructor.
emImage & operator = (const emImage & img);
// Copy an image.
bool operator == (const emImage & image) const;
bool operator != (const emImage & image) const;
// Compare images.
void Setup(int width, int height, int channelCount);
// Set the dimensions of this image. This method does not care
// about preserving the contents.
// Arguments:
// width - Horizontal extent in pixels.
// height - Vertical extent in pixels.
// channelCount - Number of channels (bytes per pixel, 1-4).
void SetUserMap(int width, int height, int channelCount, emByte * map);
// Prepare this image for being an interface to a user allocated
// pixel map. For example, this feature could be used for
// exchanging image data between system processes through shared
// memory. You should know about the following semantics: The
// copy-on-write behavior is disabled for such a "user map
// image", and the user map feature is never copied. That means,
// when copying a user map image to another image, a deep copy
// is performed immediately and the target image will be a
// normal image. Each operation which actually changes the
// dimensions of the image, will give up the user map feature
// and revert to the normal behavior. This means for example,
// calling Setup preserves the feature if the dimensions are not
// really changed. Call Clear or '=' if you want to revert to
// the normal behavior in any case.
// Arguments:
// width - Horizontal extent of the user map in pixels.
// height - Vertical extent of the user map in pixels.
// channelCount - Number of channels in the user map (bytes
// per pixel, 1-4).
// map - The user allocated pixel map. It's what you
// will get through GetMap(). If channelCount
// is 2 or 4, the map address must be aligned
// accordingly.
bool HasUserMap() const;
// Ask whether this image interfaces a user allocated pixel map.
void TryParseXpm(const char * const * xpm, int channelCount=-1);
// Set this image by parsing an X Pixmap (XPM). The idea is to
// include an XPM file in the C++ source, and to convert it to
// an emImage at run-time using this method.
// Arguments:
// xpm - The X Pixmap data array.
// channelCount - Channel count of the returned image (1-4),
// or -1 to select the best channel count from
// the X Pixmap.
// Throws: An error message on failure.
void TryParseTga(const unsigned char * tgaData, size_t tgaSize,
int channelCount=-1);
// -------------------------------------------------------------
// This method is deprecated and should not be used any longer.
// -------------------------------------------------------------
// Set this image by parsing a Targa image (TGA). The idea is
// to convert a run-length encoded TGA file to a C source file,
// and to include that source file in the C++ source, and to
// convert it to an emImage at run-time using this function.
// Arguments:
// tgaData - Byte array with the contents of a TGA file.
// tgaSize - Number of bytes in the array.
// channelCount - Channel count of the returned image (1-4),
// or -1 to select the best channel count from
// the TGA image.
// Throws: An error message on failure.
void Clear();
// Empty this image. This is like Setup(0,0,1).
bool IsEmpty() const;
// Ask whether this image is empty. It is empty if at least one
// of width and height is zero.
int GetWidth() const;
int GetHeight() const;
int GetChannelCount() const;
// Get the dimensions of this image.
const emByte * GetMap() const;
// Get a pointer to the pixel map of this image. If ChannelCount
// is 2 or 4, the map address is aligned accordingly. At least
// because of the copy-on-write feature, the pointer is valid
// only until calling any non-const method or operator on this
// image, or giving this image as a non-const argument to any
// call in the world. Hint: Even methods like GetConverted,
// GetCropped and so on may make shallow copies, like the copy
// operator and copy constructor do.
// Index to the map is:
// (y*GetWidth()+x)*GetChannelCount()+c
// With:
// x: 0 to GetWidth()-1
// y: 0 to GetHeight()-1
// c: 0 to GetChannelCount()-1
emByte * GetWritableMap();
// Like GetMap(), but for modifying the image. The rules for
// validity of the pointer are the same as with GetMap(), but:
// The pointer must not be used for modifying after doing
// something which could have made a shallow copy of this image.
emColor GetPixel(int x, int y) const;
void SetPixel(int x, int y, emColor color);
// Get or set a pixel.
emByte GetPixelChannel(int x, int y, int channel) const;
void SetPixelChannel(int x, int y, int channel, emByte value);
// Get or set one channel of a pixel.
emColor GetPixelInterpolated(double x, double y, double w,
double h, emColor bgColor) const;
// Get an average color from a rectangular area. Performs
// bi-linear interpolation or area-sampling.
// Arguments:
// x,y,w,h - Upper-left corner and size of the rectangle.
// bgColor - A background color. It is used where the
// rectangle lies outside the image.
// Returns: The interpolated color.
void Fill(emColor color);
void Fill(int x, int y, int w, int h, emColor color);
// Fill the whole image or a rectangular area with the given
// color.
void FillChannel(int channel, emByte value=0);
void FillChannel(int x, int y, int w, int h, int channel,
emByte value);
// Like Fill, but for modifying just a single channel.
void Copy(int x, int y, const emImage & img);
void Copy(int x, int y, const emImage & img,
int srcX, int srcY, int w, int h);
// Copy a rectangular area of pixels from a source image to this
// image. Source and target may overlap.
// Arguments:
// x,y - Upper-left corner of target rectangle on
// this image.
// img - The source image.
// srcX,srcY,w,h - Upper-left corner and size of the rectangle
// on the source image, which is to be copied.
// Without these arguments, the whole source
// image is taken.
void CopyChannel(int x, int y, int channel, const emImage & img,
int srcChannel);
void CopyChannel(int x, int y, int channel, const emImage & img,
int srcX, int srcY, int w, int h, int srcChannel);
// Copy one channel of a rectangular area of pixels from a
// source image to this image. Source and target may overlap.
// Arguments:
// x,y - Upper-left corner of target rectangle on
// this image.
// channel - Target channel on this image.
// img - The source image.
// srcX,srcY,w,h - Upper-left corner and size of the rectangle
// on the source image, which is to be copied.
// Without these arguments, the whole source
// image is taken.
// srcChannel - Source channel on the given image.
void CopyTransformed(int x, int y, int w, int h,
const emATMatrix & atm, const emImage & img,
bool interpolate=false, emColor bgColor=0);
// Copy an image to this image while performing an affine
// transformation. Source and target must not overlap.
// Arguments:
// x,y,w,h - A clipping rectangle for the operation on
// this image. Exactly this area of pixels is
// modified.
// atm - A transformation matrix for transforming
// source image coordinates to coordinates of
// this image.
// img - The source image.
// interpolate - Whether to perform bi-linear interpolation.
// bgColor - A color to be used for areas outside the
// source image.
emImage GetTransformed(const emATMatrix & atm, bool interpolate=false,
emColor bgColor=0, int channelCount=-1) const;
// Get an affine transformed version of this image.
// Arguments:
// atm - A transformation matrix for transforming
// coordinates of this image to coordinates of
// the returned image. Here, any translation
// will be ignored.
// interpolate - Whether to perform bi-linear interpolation.
// bgColor - A color to be used for areas outside the
// source image.
// channelCount - Number of channels in the returned image
// (1-4), or -1 for taking the channel count of
// this image.
// Returns: The resulting image.
void CalcMinMaxRect(int * pX, int * pY, int * pW, int * pH,
emColor bgColor) const;
// Calculate the smallest rectangle which contains all pixels
// which are not equal to the given background color.
// Arguments:
// pX,pY,pW,pH - Pointers for returning the rectangle.
// bgColor - The background color.
void CalcChannelMinMaxRect(int * pX, int * pY, int * pW, int * pH,
int channel, emByte bgValue) const;
// Like CalcMinMaxRect, but for a single channel.
// Arguments:
// pX,pY,pW,pH - Pointers for returning the rectangle.
// channel - The channel to be inspected.
// bgValue - The channel value of the background color.
void CalcAlphaMinMaxRect(int * pX, int * pY, int * pW,
int * pH) const;
// Calculate the smallest rectangle which contains all pixels
// which are not zero in the alpha channel.
// Arguments:
// pX,pY,pW,pH - Pointers for returning the rectangle.
emImage GetConverted(int channelCount) const;
// Get a copy of this image, converted to the given number of
// channels.
emImage GetCropped(int x, int y, int w, int h,
int channelCount=-1) const;
// Get a sub-image of this image.
// Arguments:
// x,y,w,h - A rectangle on this image. It's the area to
// be copied to the returned image, which will
// have the size of that rectangle (after
// clipping it by the image boundaries).
// channelCount - Number of channels of the returned image
// (1-4), or -1 for taking the channel count of
// this image.
// Returns: The sub-image.
emImage GetCroppedByAlpha(int channelCount=-1) const;
// Like GetCropped with a rectangle returned by
// GetAlphaMinMaxRect.
bool PreparePainter(emPainter * painter, emRootContext & rootContext,
double clipX1=0.0, double clipY1=0.0,
double clipX2=3E9, double clipY2=3E9,
double originX=0.0, double originY=0.0,
double scaleX=1.0, double scaleY=1.0);
// Prepare the given painter for painting to this image with
// the given clipping and transformation. IMPORTANT: Currently,
// the image must have 4 channels, otherwise painting would not
// be possible. But the alpha channel cannot be painted and may
// be damaged by the painter. In a near future version of
// emPainter, the image may have to have 3 channels. And in a
// far future version, any channel count may be acceptable. If
// painting is not possible due to the channel count, the
// painter is disabled and false is returned. If you want to
// paint to an image in a portable way, please poll for a
// suitable channel count via IsChannelCountPaintable and
// prepare the image accordingly. After painting, you may
// convert the image to the desired channel count, or you could
// copy channels around. The rules for usability of the painter
// are like with a pointer returned by GetWritableMap().
static bool IsChannelCountPaintable(int channelCount);
// Ask whether PreparePainter would be successful for an image
// of the given channel count.
unsigned int GetDataRefCount() const;
// Get number of references to the data behind this image.
void MakeNonShared();
// This must be called before handing the image to another
// thread.
private:
void MakeWritable();
void FreeData();
struct SharedData {
unsigned int RefCount;
int Width;
int Height;
emByte ChannelCount;
emByte IsUsersMap;
emByte * Map;
// From here on comes the non-user map.
};
SharedData * Data;
static SharedData EmptyData;
};
#ifndef EM_NO_DATA_EXPORT
inline emImage::emImage()
{
Data=&EmptyData;
}
#endif
inline emImage::emImage(const emImage & img)
{
Data=img.Data;
Data->RefCount++;
if (Data->IsUsersMap) MakeWritable();
}
inline emImage::~emImage()
{
if (!--Data->RefCount) FreeData();
}
inline bool emImage::operator != (const emImage & image) const
{
return !(*this == image);
}
inline bool emImage::HasUserMap() const
{
return Data->IsUsersMap!=0;
}
#ifndef EM_NO_DATA_EXPORT
inline void emImage::Clear()
{
if (!--Data->RefCount) FreeData();
Data=&EmptyData;
}
#endif
inline bool emImage::IsEmpty() const
{
return Data->Width==0 || Data->Height==0;
}
inline int emImage::GetWidth() const
{
return Data->Width;
}
inline int emImage::GetHeight() const
{
return Data->Height;
}
inline int emImage::GetChannelCount() const
{
return Data->ChannelCount;
}
inline const emByte * emImage::GetMap() const
{
return Data->Map;
}
inline emByte * emImage::GetWritableMap()
{
if (Data->RefCount>1) MakeWritable();
return Data->Map;
}
inline void emImage::Fill(emColor color)
{
Fill(0,0,Data->Width,Data->Height,color);
}
inline void emImage::FillChannel(int channel, emByte value)
{
FillChannel(0,0,Data->Width,Data->Height,channel,value);
}
inline void emImage::Copy(int x, int y, const emImage & img)
{
Copy(x,y,img,0,0,img.Data->Width,img.Data->Height);
}
inline void emImage::CopyChannel(
int x, int y, int channel, const emImage & img, int srcChannel
)
{
CopyChannel(
x,y,channel,img,
0,0,img.Data->Width,img.Data->Height,
srcChannel
);
}
inline emImage emImage::GetConverted(int channelCount) const
{
return GetCropped(0,0,Data->Width,Data->Height,channelCount);
}
inline void emImage::MakeNonShared()
{
MakeWritable();
}
#endif