// GnashImage.h: Base class for reading image data in Gnash.
// 
//   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
//   Free Software Foundation, Inc
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
// 
// 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 for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//

// The GnashImage class and subclasses are partly based on the public domain
// work of Thatcher Ulrich <tu@tulrich.com> 2002

#ifndef GNASH_GNASHIMAGE_H
#define GNASH_GNASHIMAGE_H

#include <boost/noncopyable.hpp>
#include <cstdint>
#include <memory> 

#include "GnashEnums.h"
#include "log.h"
#include "dsodefs.h"

// Forward declarations
namespace gnash {
    class IOChannel;
}

namespace gnash {

/// Image handling functions and classes.
namespace image {

/// The types of images handled in Gnash.
enum ImageType
{
    GNASH_IMAGE_INVALID,
    TYPE_RGB,
    TYPE_RGBA
};

/// The locations of images handled in Gnash.
enum ImageLocation
{
    GNASH_IMAGE_CPU = 1,
    GNASH_IMAGE_GPU
};

inline size_t
numChannels(ImageType t)
{
    switch (t) {
        case TYPE_RGBA:
            return 4;
        case TYPE_RGB:
            return 3;
        default:
            std::abort();
    }
}

/// Base class for different types of bitmaps
//
/// 1. Bytes are packed in RGB(A) order.
/// 2. Rowstride is equal to channels * width
class DSOEXPORT GnashImage : boost::noncopyable
{
public:

    typedef std::uint8_t value_type;
    typedef std::unique_ptr<value_type[]> container_type;
    typedef value_type* iterator;
    typedef const value_type* const_iterator;

    virtual ~GnashImage() {}

    /// Return the ImageType of the image.
    //
    /// This saves guessing when dynamic_cast is used.
    ImageType type() const {
        return _type;
    }

    /// Return the ImageLocation of the image.
    //
    /// This saves guessing when dynamic_cast is used.
    ImageLocation location() const {
        return _location;
    }

    /// Get the size of the image buffer
    //
    /// @return     The size of the buffer in bytes
    size_t size() const {
        return stride() * _height;
    }

    /// Get the pitch of the image buffer
    //
    /// @return     The rowstride of the buffer in bytes
    virtual size_t stride() const {
        return _width * channels();
    }

    /// Get the number of channels
    //
    /// @return     The number of channels
    size_t channels() const {
        return numChannels(_type);
    }

    /// Get the image's width
    //
    /// @return     The image's width in pixels.
    size_t width() const {
        return _width;
    }

    /// Get the image's width
    //
    /// @return     The image's height in pixels.
    size_t height() const {
        return _height;
    }

    /// Copy image data from a buffer.
    //
    /// Note that this buffer MUST have the same rowstride and type, or
    /// unexpected things will happen. In general, it is only safe to copy
    /// from another GnashImage or unexpected things will happen. 
    ///
    /// @param data     buffer to copy data from.
    void update(const_iterator data);

    /// Copy image data from another image data
    //
    /// Note that this buffer must have the same rowstride and type
    ///
    /// @param from     image to copy data from.
    void update(const GnashImage& from);
    
    /// Access the raw data.
    virtual iterator begin() {
        return _data.get();
    }

    /// Access the raw data
    virtual const_iterator begin() const {
        return _data.get();
    }

    /// An iterator to the end of the data.
    iterator end() {
        return begin() + size();
    }

    /// An iterator to the end of the data.
    const_iterator end() const {
        return begin() + size();
    }

protected:

    /// Construct a GnashImage from a data buffer, taking ownership of the data.
    //
    /// @param data     The raw image data. This class takes ownership.
    /// @param width    The width of the image in pixels.
    /// @param height   The height of the image in pixels.
    /// @param pitch    The pitch (rowstride) of the image in bytes.
    /// @param type     The ImageType of the image.
    GnashImage(iterator data, size_t width, size_t height, ImageType type,
            ImageLocation location = GNASH_IMAGE_CPU);

    /// Construct an empty GnashImage
    //
    /// Note: there is an arbitrary limit of std::int32_t::max bytes for the
    /// total size of the bitmap constructed with this constructor.
    //
    /// @param width    The width of the image in pixels.
    /// @param height   The height of the image in pixels.
    /// @param type     The ImageType of the image.
    GnashImage(size_t width, size_t height, ImageType type,
               ImageLocation location = GNASH_IMAGE_CPU);

    /// The type of the image: RGBA or RGB.
    const ImageType _type;

    /// Image data location (CPU or GPU)
    const ImageLocation _location;

    /// Width of image, in pixels
    const size_t _width;

    /// Height of image, in pixels
    const size_t _height;

    /// Data if held in this class
    container_type _data;

};

/// 24-bit RGB bitmap
//
/// Channels are in RGB order.
class DSOEXPORT ImageRGB : public GnashImage
{
public:

    /// Create an empty RGB image with uninitialized data.
    ImageRGB(size_t width, size_t height);

    /// Create an ImageRGB taking ownership of the data.
    ImageRGB(iterator data, size_t width, size_t height)
        :
        GnashImage(data, width, height, TYPE_RGB)
    {}

    virtual ~ImageRGB();
};

/// 32-bit RGBA bitmap
//
/// Channels are in RGBA order.
class DSOEXPORT ImageRGBA : public GnashImage
{

public:

    /// Create an empty RGB image with uninitialized data.
    ImageRGBA(size_t width, size_t height);

    ImageRGBA(iterator data, size_t width, size_t height)
        :
        GnashImage(data, width, height, TYPE_RGBA)
    {}
    
    ~ImageRGBA();

    /// Set pixel value 
    //
    /// TODO: move in base class ?
    ///
    void setPixel(size_t x, size_t y, value_type r, value_type g, value_type b,
            value_type a);
};

/// The base class for reading image data. 
class Input : boost::noncopyable
{
public:

    /// Construct an Input object to read from an IOChannel.
    //
    /// @param in   The stream to read data from. Ownership is shared
    ///             between caller and Input, so it is freed
    ///             automatically when the last owner is destroyed.
    Input(std::shared_ptr<IOChannel> in)
        :
        _inStream(in),
        _type(GNASH_IMAGE_INVALID)
    {}

    virtual ~Input() {}

    /// Begin processing the image data.
    virtual void read() = 0;

    /// Get the image's height in pixels.
    //
    /// @return     The height of the image in pixels.
    virtual size_t getHeight() const = 0;

    /// Get the image's width in pixels.
    //
    /// @return     The width of the image in pixels.
    virtual size_t getWidth() const = 0;

    /// Get number of components (channels)
    //
    /// @return     The number of components, e.g. 3 for RGB
    virtual size_t getComponents() const = 0;

    /// Read a scanline's worth of image data into the given buffer.
    //
    /// @param rgbData  The buffer for writing raw RGB data to.
    virtual void readScanline(unsigned char* rgbData) = 0;

    /// Get the ImageType of the image.
    //
    /// @return The type of the image. This is GNASH_IMAGE_INVALID
    ///         at least until read() has been called and reads some
    ///         valid data.
    ImageType imageType() { return _type; }

    /// \brief
    /// For reading SWF JPEG3-style image data, like ordinary JPEG, 
    /// but stores the data in ImageRGBA format.
    DSOEXPORT static std::unique_ptr<ImageRGBA> readSWFJpeg3(
            std::shared_ptr<gnash::IOChannel> in);

    /// Read image data from an IOChannel into an GnashImage.
    //
    /// @param in   The IOChannel to read the image from.
    /// @param type The type of image to read.
    /// @return     An GnashImage with the read image data. If type
    ///             is an unsupported FileType or image reading fails,
    ///             a NULL unique_ptr is returned.
    DSOEXPORT static std::unique_ptr<GnashImage> readImageData(
            std::shared_ptr<gnash::IOChannel> in, FileType type);

protected:

    std::shared_ptr<IOChannel> _inStream;

    ImageType _type;

};

// Base class for writing image data.
class Output : boost::noncopyable
{

public:

    /// Construct an Output for writing to an IOChannel
    //
    /// @param out      The gnash::IOChannel to write the image to. Ownership
    ///                 is shared.
    /// @param width    The width of the resulting image
    /// @param height   The height of the resulting image.
    Output(std::shared_ptr<IOChannel> out, size_t width, size_t height)
        :
        _width(width),
        _height(height),
        _outStream(out)
    {}

    virtual ~Output() {}
    
    /// Write RGB image data using the parameters supplied at construction.
    //
    /// @param rgbData  The raw RGB image data to write as an image.
    virtual void writeImageRGB(const unsigned char* rgbData) = 0;
    
    /// Write RGBA image data using the parameters supplied at construction.
    //
    /// @param rgbaData  The raw RGBA image data to write as an image.
    virtual void writeImageRGBA(const unsigned char* /*rgbaData*/)
    {
        log_error(_("This image format does not support writing RGBA images"));
    }

    /// Write the given image to the given IOChannel in a specified format.
    //
    /// @param type     The image format to write in (see GnashEnums.h)
    /// @param out      The IOChannel to write to.
    /// @param image    The image to write.
    /// @param quality  The quality of the image output, from 0..100. Values
    ///                 outside this range will be clamped to the minimum or
    ///                 maxium value. The quality is not used for all
    ///                 formats.
    DSOEXPORT static void writeImageData(FileType type,
            std::shared_ptr<gnash::IOChannel> out, const GnashImage& image,
            int quality);

protected:

    const size_t _width;

    const size_t _height;
    
    std::shared_ptr<IOChannel> _outStream;

};

/// Get a pointer to a given row of any image.
//
/// @param row    The index of the required row.
/// @return     A pointer to the first byte of the specified row.
inline GnashImage::iterator
scanline(GnashImage& im, size_t row)
{
    assert(row < im.height());
    return im.begin() + im.stride() * row;
}

/// Get a read-only pointer to a given row of any image.
//
/// @param y    The index of the required row.
/// @return     A read-only pointer to the first byte of the specified row.
inline GnashImage::const_iterator
scanline(const GnashImage& im, size_t row)
{
    assert(row < im.height());
    return im.begin() + im.stride() * row;
}

DSOEXPORT void mergeAlpha(ImageRGBA& im, GnashImage::const_iterator alphaData,
        const size_t bufferLength);

} // namespace image
} // namespace gnash

#endif
