//------------------------------------------------------------------------------
// 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