//------------------------------------------------------------------------------
// emImage.h
//
// Copyright (C) 2001,2003-2008 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;


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 Empty 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) throw(emString);
		// 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, int tgaSize,
	                 int channelCount=-1) throw(emString);
		// -------------------------------------------------------------
		// 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 Empty();
		// 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.

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::Empty()
{
	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);
}


#endif
