// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #ifndef NL_BITMAP_H #define NL_BITMAP_H #include "types_nl.h" #include "rgba.h" #include "debug.h" #include #include "object_vector.h" namespace NLMISC { class IStream; //------------------ DDS STUFFS -------------------- #ifndef NL_MAKEFOURCC #define NL_MAKEFOURCC(ch0, ch1, ch2, ch3) \ ((uint32)(uint8)(ch0) | ((uint32)(uint8)(ch1) << 8) | \ ((uint32)(uint8)(ch2) << 16) | ((uint32)(uint8)(ch3) << 24 )) #endif const uint32 DDS_HEADER = NL_MAKEFOURCC('D', 'D', 'S', ' '); const uint32 DXT_HEADER = NL_MAKEFOURCC('D', 'X', 'T', '\0'); const uint32 PNG_HEADER = NL_MAKEFOURCC(0x89, 'P', 'N', 'G'); const uint32 JPG_HEADER = NL_MAKEFOURCC(0xff, 0xd8, 0xff, 0xe0); // dwLinearSize is valid #define DDSD_LINEARSIZE 0x00080000l //---------------- END OF DDS STUFFS ------------------ const uint8 MAX_MIPMAP = 16; /** * Class Bitmap * * \author Stephane Coutelas * \author Nevrax France * \date 2000 */ /* *** IMPORTANT ******************** * *** IF YOU MODIFY THE STRUCTURE OF THIS CLASS, PLEASE INCREMENT IDriver::InterfaceVersion TO INVALIDATE OLD DRIVER DLL * ********************************** */ class CBitmap { protected : CObjectVector _Data[MAX_MIPMAP]; // The number of mipmaps. base image IS a mipmap. 1 means a base image with no mipmaping. uint8 _MipMapCount; bool _LoadGrayscaleAsAlpha; uint32 _Width; uint32 _Height; // don't forget to update operator=() and swap() if adding a data member private : /** * blend 2 integers between 0 and 255 . * \param n0 first integer * \param n1 second integer * \param coef coefficient for the first integer (must be in [0,256]) */ uint32 blend(uint32 &n0, uint32 &n1, uint32 coef0); /** * Read a DDS from an IStream. * The bitmap is readen as a set of bytes and stocked compressed. * Width and Height are multiple of 4. * \param IStream The stream must be in reading mode. * \return image depth * \throw EDDSBadHeader : surface is header is not valid. */ uint8 readDDS(NLMISC::IStream &f, uint mipMapSkip); /** * Read a TGA from an IStream. * TGA pictures can be in 24 or 32 bits, RLE or uncompressed * \param f IStream (must be a reading stream) * \return image depth if succeed, 0 else */ uint8 readTGA( NLMISC::IStream &f); /** * Read a PNG from an IStream. * PNG pictures can be in 24 or 32 bits * \param f IStream (must be a reading stream) * \return image depth if succeed, 0 else */ uint8 readPNG( NLMISC::IStream &f ); /** * Read a JPG from an IStream. * JPG pictures can be in 24 * \param f IStream (must be a reading stream) * \return image depth if succeed, 0 else */ uint8 readJPG( NLMISC::IStream &f ); /** * Change bitmap format * * about DXTC1 to DXTC5 : * Does nothing if the format is not DXTC1 * about alpha encoding : * alpha0 == alpha1 * code(x,y) == 7 for every (x,y) * * about luminance to alpha and alpha to luminance : * the buffer keeps unchanged * */ ///@{ bool convertToDXTC5(); bool convertToRGBA(); bool luminanceToRGBA(); bool alphaToRGBA(); bool alphaLuminanceToRGBA(); bool convertToLuminance(); bool rgbaToLuminance(); bool alphaToLuminance(); bool alphaLuminanceToLuminance(); bool convertToAlpha(); bool rgbaToAlpha(); bool luminanceToAlpha(); bool alphaLuminanceToAlpha(); bool convertToAlphaLuminance(); bool rgbaToAlphaLuminance(); bool luminanceToAlphaLuminance(); bool alphaToAlphaLuminance(); ///@} /** * Decompress bitmap compressed with S3TC DXT1 algorithm. * \param alpha if alpha is true there's alpha. */ bool decompressDXT1(bool alpha); /** * Decompress bitmap compressed with S3TC DXT3 algorithm. * \throw EAllocationFailure : can't allocate memory. */ bool decompressDXT3(); /** * Decompress bitmap compressed with S3TC DXT3 algorithm. * \throw EAllocationFailure : can't allocate memory. */ bool decompressDXT5(); /** * Extracting RGBA infos from a 16bits word. (used by S3TC decompression) * \param color a 16bits integer * \param r a CRGBA */ static void uncompress(uint16 color, NLMISC::CRGBA &); /** * The resample function * \param pSrc CRGBA array * \param pDest CRGBA array for storing resampled texture * \param nSrcWidth original width * \param nSrcHeight original height * \param nDestWidth width after resample * \param nDestHeight height after resample */ void resamplePicture32 (const NLMISC::CRGBA *pSrc, NLMISC::CRGBA *pDest, sint32 nSrcWidth, sint32 nSrcHeight, sint32 nDestWidth, sint32 nDestHeight); /** * The FAST resample function : works only when reducing the size by two * and when the image is square * \param pSrc CRGBA array * \param pDest CRGBA array for storing resampled texture * \param nSrcWidth original width * \param nSrcHeight original height * \param nDestWidth width after resample * \param nDestHeight height after resample */ void resamplePicture32Fast (const NLMISC::CRGBA *pSrc, NLMISC::CRGBA *pDest, sint32 nSrcWidth, sint32 nSrcHeight, sint32 nDestWidth, sint32 nDestHeight); /** * Quadratic interpolator * \return the interpolation in (x,y) of the values (xy**) */ float getColorInterp (float x, float y, float xy00, float xy01, float xy10, float xy11) const; /// name DXTC single texel read //@{ static CRGBA getDXTCColorFromBlock(const uint8 *block, sint x, sint y); CRGBA getDXTC1Texel(sint x, sint y, uint32 numMipMap) const; CRGBA getDXTC3Texel(sint x, sint y, uint32 numMipMap) const; CRGBA getDXTC5Texel(sint x, sint y, uint32 numMipMap) const; //@} CRGBA getRGBAPixel(sint x, sint y, uint32 numMipMap /*=0*/) const; // Make a dummy from a bitfield void makeDummyFromBitField(const uint8 bitmap[1024]); // Flip DXTC void flipHDXTCBlockColor(uint8 *bitColor, uint32 w); void flipVDXTCBlockColor(uint8 *bitColor, uint32 h); void flipHDXTCBlockAlpha3(uint8 *bitColor, uint32 w); void flipVDXTCBlockAlpha3(uint8 *bitColor, uint32 h); void flipHDXTCBlockAlpha5(uint8 *bitColor, uint32 w); void flipVDXTCBlockAlpha5(uint8 *bitColor, uint32 h); void flipDXTC(bool vertical); void flipDXTCMipMap(bool vertical, uint mm, uint type); public: enum TType { RGBA=0, Luminance, Alpha, AlphaLuminance, DXTC1, DXTC1Alpha, DXTC3, DXTC5, DsDt, ModeCount, DonTKnow=0xffffffff } PixelFormat; static const uint32 bitPerPixels[ModeCount]; static const uint32 DXTC1HEADER; static const uint32 DXTC3HEADER; static const uint32 DXTC5HEADER; // don't forget to update operator=() and swap() if adding a data member CBitmap() { _MipMapCount = 1; _Width = 0; _Height = 0; PixelFormat = RGBA; _LoadGrayscaleAsAlpha = true; } virtual ~CBitmap() { } // swap 2 bitmaps contents void swap(CBitmap &other); /** * Read a bitmap(TGA or DDS) from an IStream. * Bitmap supported are DDS (DXTC1, DXTC1 with Alpha, DXTC3, DXTC5, and * uncompressed TGA (24 and 32 bits). * \param IStream The stream must be in reading mode. * \param mipMapSkip if the file is a DDS with mipMap. N=mipMapSkip mipmaps are skipped. * \return image depth (24 or 32), or 0 if load failed * \throw ESeekFailed : seek has failed */ uint8 load(NLMISC::IStream &f, uint mipMapSkip=0); /** * Determinate the bitmap size from a bitmap(TGA or DDS) from an IStream. load just header of the file. * Bitmap supported are DDS (DXTC1, DXTC1 with Alpha, DXTC3, DXTC5, and * uncompressed TGA (24 and 32 bits). * NB: at the end, f is seeked to begin. * \param IStream The stream must be in reading mode. * \param width the width of the image. 0 if fails. * \param height the height of the image. 0 if fails. * \throw ESeekFailed : seek has failed */ static void loadSize(NLMISC::IStream &f, uint32 &width, uint32 &height); /** same than other loadSize(), but with a pathName. * \see loadSize() */ static void loadSize(const std::string &path, uint32 &retWidth, uint32 &retHeight); /** * Make a dummy "?" texture. Useful for file not found. Mode is rgba. */ void makeDummy(); /** * Make a dummy "2" texture. Useful for file not power of 2. Mode is rgba. */ void makeNonPowerOf2Dummy(); /** * Return the pixels buffer of the image, or of one of its mipmap. * Return a reference of an array in pixel format get with getPixelFormat(). * \return CObjectVector& RGBA pixels */ ///@{ CObjectVector& getPixels(uint32 numMipMap = 0) { //nlassert (numMipMap<=_MipMapCount); return _Data[numMipMap]; } const CObjectVector& getPixels(uint32 numMipMap = 0) const { //nlassert (numMipMap<=_MipMapCount); return _Data[numMipMap]; } /** Gain ownership of this texture datas. As a result, the bitmap is reseted (put in the same state than when ctor is called, e.g a single mipmap with null size) * The CObjectVector objects that contains the bitmap (one per mipmap) datas are 'swapped' with those in the array provided by the caller. * NB : The user must provide at least min(getMipMapCount(), maxMipMapCount) entries in the array * \param mipmapArray Array of mipmap that receive the bitmap datas * \param maxMipMapCount Max number of mipmap to be copied in the destination array. */ void unattachPixels(CObjectVector *mipmapDestArray, uint maxMipMapCount = MAX_MIPMAP); ///@} /** * Convert bitmap to another type * conversion to rgba always work. No-op if already rgba. * \param type new type for the bitmap * \return true if conversion succeeded, false else */ bool convertToType (TType type); /** * Return the format of pixels stored at the present time in the object buffer. * \return Pixel format (rgba luminance alpha alphaLuminance dxtc1 dxtc1Alpha dxtc3 dxtc5) */ TType getPixelFormat() const { return PixelFormat; } /** * Return the image width, or a mipmap width. * \param mipMap mipmap level * \return image width (0 if mipmap not found) */ virtual uint32 getWidth(uint32 numMipMap = 0) const; /** * Return the image height, or a mipmap height. * \param mipMap mipmap level * \return image height (0 if mipmap not found) */ virtual uint32 getHeight(uint32 numMipMap = 0) const; /** * Return the size (in pixels) of the image: <=> getHeight()*getWidth(). * \param mipMap mipmap level * \return image size (0 if mipmap not found) */ uint32 getSize(uint32 numMipMap = 0) const; /** * Return the number of mipmaps. Level0 is a mipmap... * \return number of mipmaps. 0 if no image at all. 1 if no mipmaping (for the base level). */ uint32 getMipMapCount() const { return _MipMapCount; } // Compute the number of mipmap needed for that bitmap (independently from the current number of mipmaps that have been set) uint32 computeNeededMipMapCount() const; /** * Rotate a bitmap in CCW mode. * * \see releaseMipMaps(). */ void rotateCCW(); /** * Build the mipmaps of the bitmap if they don't exist. * Work only in RGBA mode... * \see releaseMipMaps(). */ void buildMipMaps(); /** * Release the mipmaps of the bitmap if they exist. * Work for any mode. * \see buildMipMaps(). */ void releaseMipMaps(); /** * Reset the buffer. Mipmaps are deleted and bitmap is not valid anymore. * * \param type is the new type used for this texture */ void reset(TType type=RGBA); /** * Resample the bitmap. If mipmaps exist they are deleted, then rebuilt * after resampling. * \param nNewWidth width after resample * \param nNewHeight height after resample */ void resample (sint32 nNewWidth, sint32 nNewHeight); /** * Resize the bitmap. If mipmaps exist they are deleted and not rebuilt. * This is not a crop. Pixels are lost after resize. * * \param nNewWidth width after resize * \param nNewHeight height after resize * \param newType is the new type of the bitmap. If don_t_know, keep the same pixel format that before. * \param resetTo0 by default the vector are filled by 0. set false to gain performances. */ void resize (sint32 nNewWidth, sint32 nNewHeight, TType newType=DonTKnow, bool resetTo0= true); /** ADVANCED USE * Resize a single mipmap level. resize() should have been called before. * This is not a crop. Pixels are lost after resize. * No validity check is made. It is the user responsabitility fo setup correct mipmap size. * * \param numMipMap id of the mipmap * \param nNewWidth width after resize * \param nNewHeight height after resize * \param resetTo0 by default the vector are filled by 0. set false to gain performances. */ void resizeMipMap (uint32 numMipMap, sint32 nNewWidth, sint32 nNewHeight, bool resetTo0= true); /** ADVANCED USE * To use in conjunction with resizeMipMap. Setup the correct total number of mipmap * No validity check is made. It is the user responsabitility fo setup correct mipmap count. */ void setMipMapCount(uint32 mmc); /** * Write a TGA (24 or 32 bits) from the object pixels buffer. * If the current pixel format is not rgba then the method does nothing * If the pixel format is Alpha then we save in 8 bpp * \param f IStream (must be a reading stream) * \param d depth : 8 or 16 or 24 or 32 (0 for automatic) * \param upsideDown if true, the bitmap will be saved with the upside down * \return true if succeed, false else */ bool writeTGA(NLMISC::IStream &f, uint32 d=0, bool upsideDown = false); /** * Write a PNG (24 or 32 bits) from the object pixels buffer. * If the current pixel format is not rgba then the method does nothing * If the pixel format is Alpha then we save in 8 bpp * \param f IStream (must be a reading stream) * \param d depth : 8 or 16 or 24 or 32 (0 for automatic) * \return true if succeed, false else */ bool writePNG(NLMISC::IStream &f, uint32 d=0); /** * Write a JPG from the object pixels buffer. * If the current pixel format is not rgba then the method does nothing * If the pixel format is Alpha then we save in 8 bpp * \param f IStream (must be a reading stream) * \param quality 0=very bad quality 100=best quality * \return true if succeed, false else */ bool writeJPG(NLMISC::IStream &f, uint8 quality = 80); /** * Tell the bitmap to load grayscale bitmap as alpha or luminance format. * * \param loadAsAlpha is true to load grayscale bitmaps as alpha. false to load grayscale bitmaps as luminance. * default value is true. */ void loadGrayscaleAsAlpha (bool loadAsAlpha) { _LoadGrayscaleAsAlpha=loadAsAlpha; } /** * Tell if the bitmap loads grayscale bitmap as alpha or luminance format. * * \return true if the bitmap loads grayscale bitmaps as alpha, false if it loads grayscale bitmaps as luminance. */ bool isGrayscaleAsAlpha () const { return _LoadGrayscaleAsAlpha; } /** * Perform a simple blit from the source to this bitmap at the (x, y) pos * The dimension of the original bitmap are preserved * For now, this texture and the source must have the same format * With DXTC format, the dest coordinates must be a multiple of 4 * mipmap are not rebuild when present * IMPORTANT : copy to self is not handled correctly * \return true if the params were corrects and if the blit occurs. In debug build there's an assertion */ bool blit(const CBitmap *src, sint32 x, sint32 y) ; /** * Extended version of blit. The destinaion of the blit is 'this' bitmap * Source and dest rect are clamped as necessary. * For now, only RGBA is uspported (an asertion occurs otherwise) * mipmap are not updated. * IMPORTANT : copy to self is not handled correctly */ void blit(const CBitmap &src, sint srcX, sint srcY, sint srcWidth, sint srcHeight, sint destX, sint destY); /** * Get the color in the bitmap for the first mipmap * The mipmaps must be built. If not just return the bilinear at the given point. * NB: coordinates are clamped. * \param tiled If false coordinate are clamped, else the bitmap is considered to tile */ CRGBAF getColor (float x, float y) const; // Get Color with optional tiling on axis CRGBAF getColor (float x, float y, bool tileU, bool tileV) const; /** Get the pixel at the given coorrdinate. * Works in RGBA and DXTC modes. * Outside of the bitmap it returns Black (or if mipmap is not found) */ CRGBA getPixelColor(sint x, sint y, uint32 numMipMap = 0) const; /** * Horizontal flip (all the columns are flipped) * Works only with RGBA, and DXTC formats (only if w/h is a power of 2) */ void flipH(); /** * Vertical flip (all the rows are flipped) * Works only with RGBA, and DXTC formats (only if w/h is a power of 2) */ void flipV(); /** * Rotation of the bitmap of 90 degree in clockwise */ void rot90CW(); /** * Rotation of the bitmap of 90 degree in counter clockwise */ void rot90CCW(); /** Set this bitmap as the result of the blend bewteen 2 bitmap * REQUIRE : - Bm0 and Bm1 should have the same size. * - Both bitmap should be convertible to RGBA pixel format. * The result is a RGBA bitmap. * NB: this just works with the first mipmaps * \param factor The blend factor. 0 means the result is equal to Bm0, 256 means the result is equal to Bm1 * \param inputBitmapIsMutable when true, bitmap can be converted in place when needed (no copy done) */ void blend(CBitmap &Bm0, CBitmap &Bm1, uint16 factor, bool inputBitmapIsMutable = false); void getData(uint8*& extractData); void getDibData(uint8*& extractData); CBitmap& operator= (const CBitmap& from) { if (&from == this) return *this; for (uint i=0; i!=MAX_MIPMAP; ++i) { _Data[i] = from._Data[i]; // requires more than a surface copy } _MipMapCount = from._MipMapCount; _LoadGrayscaleAsAlpha = from._LoadGrayscaleAsAlpha; _Width = from._Width; _Height = from._Height; PixelFormat = from.PixelFormat; return *this; } }; } // NLMISC #endif // NL_BITMAP_H /* End of bitmap.h */