/** \file GImage.h See G3D::GImage for details. @cite JPEG compress/decompressor is the IJG library, used in accordance with their license. @cite JPG code by John Chisholm, using the IJG Library @cite TGA code by Morgan McGuire @cite BMP code by John Chisholm, based on code by Edward "CGameProgrammer" Resnick mailto:cgp@gdnmail.net at ftp://ftp.flipcode.com/cotd/LoadPicture.txt @cite PCX format described in the ZSOFT PCX manual http://www.nist.fss.ru/hr/doc/spec/pcx.htm#2 @cite PNG compress/decompressor is the libpng library, used in accordance with their license. @cite PPM code by Morgan McGuire based on http://netpbm.sourceforge.net/doc/ppm.html \maintainer Morgan McGuire, http://graphics.cs.williams.edu \created 2002-05-27 \edited 2010-01-04 Copyright 2000-2010, Morgan McGuire. All rights reserved. */ #ifndef G3D_GImage_h #define G3D_GImage_h #include "G3D/platform.h" #include #include "G3D/Array.h" #include "G3D/g3dmath.h" #include "G3D/stringutils.h" #include "G3D/Color1uint8.h" #include "G3D/Color3uint8.h" #include "G3D/Color4uint8.h" #include "G3D/MemoryManager.h" #include "G3D/BumpMapPreprocess.h" namespace G3D { class BinaryInput; class BinaryOutput; /** Interface to image compression & file formats. Supported formats (decode and encode): Color JPEG, PNG, (Uncompressed) TGA 24, (Uncompressed) TGA 32, BMP 1, BMP 4, BMP 8, BMP 24, PPM (P6), and PPM ASCII (P1, P2, P3), which includes PPM, PGM, and PBM. (Compressed) TGA 24, (Compressed) TGA 32, 8-bit paletted PCX, 24-bit PCX, and ICO are supported for decoding only. Sample usage: \verbatim // Loading from disk: G3D::GImage im1("test.jpg"); // Loading from memory: G3D::GImage im2(data, length); // im.pixel is a pointer to RGB color data. If you want // an alpha channel, call RGBtoRGBA or RGBtoARGB for // conversion. // Saving to memory: G3D::GImage im3(width, height); // (Set the pixels of im3...) uint8* data2; int len2; im3.encode(G3D::GImage::JPEG, data2, len2); // Saving to disk im3.save("out.jpg"); \endverbatim The free Image Magick Magick Wand API (http://www.imagemagick.org/www/api/magick_wand.html) provides a more powerful API for image manipulation and wider set of image load/save formats. It is recommended over GImage (we don't include it directly in G3D because their license is more restrictive than the BSD one). \cite http://tfcduke.developpez.com/tutoriel/format/tga/fichiers/tga_specs.pdf \sa Image3, Image3uint8, Image4, Image4uint8, Image1, Image1uint8, Texture, Map2D */ class GImage { private: /** Used exclusively for allocating m_byte; this may be an implementation that allocates directly on a GPU.*/ MemoryManager::Ref m_memMan; uint8* m_byte; int m_channels; int m_width; int m_height; public: class Error { public: Error( const std::string& reason, const std::string& filename = "") : reason(reason), filename(filename) {} std::string reason; std::string filename; }; /** PGM, PPM, and PBM all come in two versions and are classified as PPM_* files */ enum Format {JPEG, BMP, TGA, PCX, ICO, PNG, PPM_BINARY, PGM_BINARY = PPM_BINARY, PPM_ASCII, PGM_ASCII = PPM_ASCII, AUTODETECT, UNKNOWN}; /** The number of channels; either 3 (RGB) or 4 (RGBA) */ inline int channels() const { return m_channels; } inline int width() const { return m_width; } inline int height() const { return m_height; } inline const uint8* byte() const { return m_byte; } /** Returns a pointer to the underlying data, which is stored in row-major order without row padding. e.g., uint8* ptr = image.rawData(); */ template inline const Type* rawData() const { return (Type*)m_byte; } /** \copybrief GImage::rawData() const */ template inline Type* rawData() { return (Type*)m_byte; } inline const Color1uint8* pixel1() const { debugAssertM(m_channels == 1, format("Tried to call GImage::pixel1 on an image with %d channels", m_channels)); return (Color1uint8*)m_byte; } inline Color1uint8* pixel1() { debugAssertM(m_channels == 1, format("Tried to call GImage::pixel1 on an image with %d channels", m_channels)); return (Color1uint8*)m_byte; } /** Returns a pointer to the upper left pixel as Color4uint8. */ inline const Color4uint8* pixel4() const { debugAssertM(m_channels == 4, format("Tried to call GImage::pixel4 on an image with %d channels", m_channels)); return (Color4uint8*)m_byte; } inline Color4uint8* pixel4() { debugAssert(m_channels == 4); return (Color4uint8*)m_byte; } /** Returns a pointer to the upper left pixel as Color3uint8. */ inline const Color3uint8* pixel3() const { debugAssertM(m_channels == 3, format("Tried to call GImage::pixel3 on an image with %d channels", m_channels)); return (Color3uint8*)m_byte; } inline Color3uint8* pixel3() { debugAssert(m_channels == 3); return (Color3uint8*)m_byte; } /** Returns the pixel at (x, y), where (0,0) is the upper left. */ inline const Color1uint8& pixel1(int x, int y) const { debugAssert(x >= 0 && x < m_width); debugAssert(y >= 0 && y < m_height); return pixel1()[x + y * m_width]; } /** Returns the pixel at (x, y), where (0,0) is the upper left. */ inline Color1uint8& pixel1(int x, int y) { debugAssert(x >= 0 && x < m_width); debugAssert(y >= 0 && y < m_height); return pixel1()[x + y * m_width]; } /** Returns the pixel at (x, y), where (0,0) is the upper left. */ inline const Color3uint8& pixel3(int x, int y) const { debugAssert(x >= 0 && x < m_width); debugAssert(y >= 0 && y < m_height); return pixel3()[x + y * m_width]; } inline Color3uint8& pixel3(int x, int y) { debugAssert(x >= 0 && x < m_width); debugAssert(y >= 0 && y < m_height); return pixel3()[x + y * m_width]; } /** Returns the pixel at (x, y), where (0,0) is the upper left. */ inline const Color4uint8& pixel4(int x, int y) const { debugAssert(x >= 0 && x < m_width); debugAssert(y >= 0 && y < m_height); return pixel4()[x + y * m_width]; } inline Color4uint8& pixel4(int x, int y) { debugAssert(x >= 0 && x < m_width); debugAssert(y >= 0 && y < m_height); return pixel4()[x + y * m_width]; } inline uint8* byte() { return m_byte; } private: void encodeBMP( BinaryOutput& out) const; /** The TGA file will be either 24- or 32-bit depending on the number of channels. */ void encodeTGA( BinaryOutput& out) const; /** Converts this image into a JPEG */ void encodeJPEG( BinaryOutput& out) const; /** Converts this image into a JPEG */ void encodePNG( BinaryOutput& out) const; void encodePPM( BinaryOutput& out) const; void encodePPMASCII( BinaryOutput& out) const; void decodeTGA( BinaryInput& input); void decodeBMP( BinaryInput& input); void decodeJPEG( BinaryInput& input); void decodePCX( BinaryInput& input); void decodeICO( BinaryInput& input); void decodePNG( BinaryInput& input); void decodePPM( BinaryInput& input); void decodePPMASCII( BinaryInput& input); /** Given [maybe] a filename, memory buffer, and [maybe] a format, returns the most likely format of this file. */ static Format resolveFormat( const std::string& filename, const uint8* data, int dataLen, Format maybeFormat); void _copy( const GImage& other); public: /** Predicts the image file format of \a filename */ static Format resolveFormat(const std::string& filename); void flipHorizontal(); void flipVertical(); void rotate90CW(int numTimes = 1); /** Create an empty image of the given size. \sa load() */ GImage( int width = 0, int height = 0, int channels = 3, const MemoryManager::Ref& m = MemoryManager::create()); /** Load an encoded image from disk and decode it. Throws GImage::Error if something goes wrong. */ GImage( const std::string& filename, Format format = AUTODETECT, const MemoryManager::Ref& m = MemoryManager::create()); /** Decodes an image stored in a buffer. */ GImage( const unsigned char*data, int length, Format format = AUTODETECT, const MemoryManager::Ref& m = MemoryManager::create()); GImage( const GImage& other, const MemoryManager::Ref& m = MemoryManager::create()); GImage& operator=(const GImage& other); /** Returns a new GImage that has 4 channels. RGB is taken from this GImage and the alpha from the red channel of the supplied image. The new GImage is passed as a reference parameter for speed. */ void insertRedAsAlpha(const GImage& alpha, GImage& output) const; /** Returns a new GImage with 3 channels, removing the alpha channel if there is one. The new GImage is passed as a reference parameter for speed. */ void stripAlpha(GImage& output) const; /** Loads an image from disk (clearing the old one first), using the existing memory manager. */ void load( const std::string& filename, Format format = AUTODETECT); /** Frees memory and resets to a 0x0 image. */ void clear(); /** Deallocates the pixels. */ virtual ~GImage(); /** Resizes the internal buffer to (\a width x \a height) with the number of \a channels specified. \param zero If true, all data is set to 0 (black). */ void resize(int width, int height, int channels, bool zero = true); /** Copies src sub-image data into dest at a certain offset. The dest variable must already contain an image that is large enough to contain the src sub-image at the specified offset. Returns true on success and false if the src sub-image cannot completely fit within dest at the specified offset. Both src and dest must have the same number of channels. */ static bool pasteSubImage( GImage& dest, const GImage& src, int destX, int destY, int srcX, int srcY, int srcWidth, int srcHeight); /** creates dest from src sub-image data. Returns true on success and false if the src sub-image is not within src. */ static bool copySubImage(GImage & dest, const GImage & src, int srcX, int srcY, int srcWidth, int srcHeight); void convertToRGBA(); void convertToRGB(); /** Averages color channels if they exist */ void convertToL8(); /** Returns true if format is supported. Format should be an extension string (e.g. "BMP"). */ static bool supportedFormat( const std::string& format); /** Converts a string to an enum, returns UNKNOWN if not recognized. */ static Format stringToFormat( const std::string& format); /** Encode and save to disk. */ void save( const std::string& filename, Format format = AUTODETECT) const; /** The caller must delete the returned buffer. TODO: provide a memory manager */ void encode( Format format, uint8*& outData, int& outLength) const; /** Does not commit the BinaryOutput when done. */ void encode( Format format, BinaryOutput& out) const; /** Decodes the buffer into this image. @param format Must be the correct format. */ void decode( BinaryInput& input, Format format); /** Returns the size of this object in bytes */ int sizeInMemory() const; /** Ok for in == out */ static void R8G8B8_to_Y8U8V8(int width, int height, const uint8* in, uint8* out); /** Ok for in == out */ static void Y8U8V8_to_R8G8B8(int width, int height, const uint8* in, uint8* out); /** @param in RGB buffer of numPixels * 3 bytes @param out Buffer of numPixels * 4 bytes @param numPixels Number of RGB pixels to convert */ static void RGBtoRGBA( const uint8* in, uint8* out, int numPixels); static void RGBtoARGB( const uint8* in, uint8* out, int numPixels); static void LtoRGB (const uint8* in, uint8* out, int numPixels); static void LtoRGBA (const uint8* in, uint8* out, int numPixels); /** Safe for in == out */ static void RGBtoBGR( const uint8* in, uint8* out, int numPixels); /** Win32 32-bit HDC format. */ static void RGBtoBGRA( const uint8* in, uint8* out, int numPixels); static void RGBAtoRGB( const uint8* in, uint8* out, int numPixels); /** Uses the red channel of the second image as an alpha channel. */ static void RGBxRGBtoRGBA( const uint8* colorRGB, const uint8* alphaRGB, uint8* out, int numPixels); /** Flips the image along the vertical axis. Safe for in == out. */ static void flipRGBVertical( const uint8* in, uint8* out, int width, int height); static void flipRGBAVertical( const uint8* in, uint8* out, int width, int height); /** Given a tangent space bump map, computes a new image where the RGB channels are a tangent space normal map and the alpha channel is the original bump map. Assumes the input image is tileable. In the resulting image, x = red = tangent, y = green = binormal, and z = blue = normal. Particularly useful as part of the idiom:
 	    GImage normal;
	    computeNormalMap(GImage(filename), normal);
	    return Texture::fromGImage(filename, normal);
    
*/ static void computeNormalMap( const class GImage& bump, class GImage& normal, const BumpMapPreprocess& preprocess = BumpMapPreprocess()); static void computeNormalMap (int width, int height, int channels, const uint8* src, GImage& normal, const BumpMapPreprocess& preprocess = BumpMapPreprocess()); /** Bayer demosaicing using the filter proposed in HIGH-QUALITY LINEAR INTERPOLATION FOR DEMOSAICING OF BAYER-PATTERNED COLOR IMAGES Henrique S. Malvar, Li-wei He, and Ross Cutler The filter wraps at the image boundaries. Assumes in != out. */ static void BAYER_G8B8_R8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out); static void BAYER_G8R8_B8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out); static void BAYER_R8G8_G8B8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out); static void BAYER_B8G8_G8R8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out); /** Fast conversion; the output has 1/2 the size of the input in each direction. Assumes in != out. See G3D::BAYER_G8B8_R8G8_to_R8G8B8_MHC for a much better result. */ static void BAYER_G8B8_R8G8_to_Quarter_R8G8B8 (int inWidth, int inHeight, const uint8* in, uint8* out); /** Attempt to undo fast conversion of G3D::BAYER_G8B8_R8G8_to_Quarter_R8G8B8; the green channel will lose data. Assumes in != out The input should have size 3 * inWidth * inHeight. The output should have size 2 * inWidth * 2 * inHeight. */ static void Quarter_R8G8B8_to_BAYER_G8B8_R8G8 (int inWidth, int inHeight, const uint8* in, uint8* out); /** Overwrites every pixel with one of the two colors in a checkerboard pattern. The fields used from the two colors depend on the current number of channels in @a im. */ static void makeCheckerboard (GImage& im, int checkerSize = 1, const Color4uint8& color1 = Color4uint8(255,255,255,255), const Color4uint8& color2 = Color4uint8(0,0,0,255)); }; } #endif