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