//----------------------------------------------------------------------------- // Copyright (c) 2012 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #include "core/stream/stream.h" #include "gfx/bitmap/gBitmap.h" static bool sReadBMP(Stream &stream, GBitmap *bitmap); static bool sWriteBMP(GBitmap *bitmap, Stream &stream, U32 compressionLevel); static struct _privateRegisterBMP { _privateRegisterBMP() { GBitmap::Registration reg; reg.extensions.push_back( "bmp" ); reg.readFunc = sReadBMP; reg.writeFunc = sWriteBMP; GBitmap::sRegisterFormat( reg ); } } sStaticRegisterBMP; // structures mirror those defined by the win32 API struct RGBQUAD { U8 rgbBlue; U8 rgbGreen; U8 rgbRed; U8 rgbReserved; }; struct BITMAPFILEHEADER { U16 bfType; U32 bfSize; U16 bfReserved1; U16 bfReserved2; U32 bfOffBits; }; struct BITMAPINFOHEADER { U32 biSize; S32 biWidth; S32 biHeight; U16 biPlanes; U16 biBitCount; U32 biCompression; U32 biSizeImage; S32 biXPelsPerMeter; S32 biYPelsPerMeter; U32 biClrUsed; U32 biClrImportant; }; // constants for the biCompression field #define BI_RGB 0L #define BI_RLE8 1L #define BI_RLE4 2L #define BI_BITFIELDS 3L //------------------------------------------------------------------------------ //-------------------------------------- Supplementary I/O (Partially located in // bitmapPng.cc) // static bool sReadBMP(Stream &stream, GBitmap *bitmap) { PROFILE_SCOPE(sReadBMP); BITMAPINFOHEADER bi; BITMAPFILEHEADER bf; RGBQUAD rgb[256]; stream.read(&bf.bfType); stream.read(&bf.bfSize); stream.read(&bf.bfReserved1); stream.read(&bf.bfReserved2); stream.read(&bf.bfOffBits); stream.read(&bi.biSize); stream.read(&bi.biWidth); stream.read(&bi.biHeight); stream.read(&bi.biPlanes); stream.read(&bi.biBitCount); stream.read(&bi.biCompression); stream.read(&bi.biSizeImage); stream.read(&bi.biXPelsPerMeter); stream.read(&bi.biYPelsPerMeter); stream.read(&bi.biClrUsed); stream.read(&bi.biClrImportant); GFXFormat fmt = GFXFormatR8G8B8; if(bi.biBitCount == 8) { // read in texture palette if(!bi.biClrUsed) bi.biClrUsed = 256; stream.read(sizeof(RGBQUAD) * bi.biClrUsed, rgb); } bitmap->allocateBitmap(bi.biWidth, bi.biHeight, false, fmt); U32 width = bitmap->getWidth(); U32 height = bitmap->getHeight(); U32 bytesPerPixel = bitmap->getBytesPerPixel(); for(U32 i = 0; i < bi.biHeight; i++) { U8 *rowDest = bitmap->getAddress(0, height - i - 1); if (bi.biBitCount == 8) { // use palette...don't worry about being slow for (S32 j=0; jgetAddress(0,0); for(S32 i = 0; i < width * height; i++) { U8 tmp = ptr[0]; ptr[0] = ptr[2]; ptr[2] = tmp; ptr += 3; } } // We know BMP's don't have any transparency bitmap->setHasTransparency(false); return true; } static bool sWriteBMP(GBitmap *bitmap, Stream &stream, U32 compressionLevel) { TORQUE_UNUSED( compressionLevel ); // BMP does not use compression BITMAPINFOHEADER bi; BITMAPFILEHEADER bf; bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = bitmap->getWidth(); bi.biHeight = bitmap->getHeight(); //our data is top-down bi.biPlanes = 1; if(bitmap->getFormat() == GFXFormatR8G8B8) { bi.biBitCount = 24; bi.biCompression = BI_RGB; bi.biClrUsed = 0; } else { bi.biBitCount = 0; bi.biCompression = BI_RGB; // Removes warning C4701 on line AssertISV(false, "GBitmap::writeMSBmp - only support R8G8B8 formats!"); } U32 width = bitmap->getWidth(); U32 height = bitmap->getHeight(); U32 bytesPP = bi.biBitCount >> 3; bi.biSizeImage = width * height * bytesPP; bi.biXPelsPerMeter = 0; bi.biYPelsPerMeter = 0; bi.biClrUsed = 0; bi.biClrImportant = 0; bf.bfType = makeFourCCTag('B','M',0,0); //Type of file 'BM' bf.bfOffBits= sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER) + (sizeof(RGBQUAD)*bi.biClrUsed); bf.bfSize = bf.bfOffBits + bi.biSizeImage; bf.bfReserved1 = 0; bf.bfReserved2 = 0; stream.write(bf.bfType); stream.write(bf.bfSize); stream.write(bf.bfReserved1); stream.write(bf.bfReserved2); stream.write(bf.bfOffBits); stream.write(bi.biSize); stream.write(bi.biWidth); stream.write(bi.biHeight); stream.write(bi.biPlanes); stream.write(bi.biBitCount); stream.write(bi.biCompression); stream.write(bi.biSizeImage); stream.write(bi.biXPelsPerMeter); stream.write(bi.biYPelsPerMeter); stream.write(bi.biClrUsed); stream.write(bi.biClrImportant); //write the bitmap bits U8* pMSUpsideDownBits = new U8[bi.biSizeImage]; for (U32 i = 0; i < height; i++) { const U8* pSrc = bitmap->getAddress(0, i); U8* pDst = pMSUpsideDownBits + (height - i - 1) * width * bytesPP; dMemcpy(pDst, pSrc, width * bytesPP); } stream.write(bi.biSizeImage, pMSUpsideDownBits); delete [] pMSUpsideDownBits; return stream.getStatus() == Stream::Ok; }