// Copyright (C) 2002-2012 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #include "CImageLoaderBMP.h" #include "IReadFile.h" #include "SColor.h" #include "CColorConverter.h" #include "CImage.h" #include "os.h" #include "irrString.h" namespace irr { namespace video { //! constructor CImageLoaderBMP::CImageLoaderBMP() { #ifdef _DEBUG setDebugName("CImageLoaderBMP"); #endif } //! returns true if the file maybe is able to be loaded by this class //! based on the file extension (e.g. ".tga") bool CImageLoaderBMP::isALoadableFileExtension(const io::path& filename) const { return core::hasFileExtension ( filename, "bmp" ); } //! returns true if the file maybe is able to be loaded by this class bool CImageLoaderBMP::isALoadableFileFormat(io::IReadFile* file) const { u16 headerID; file->read(&headerID, sizeof(u16)); #ifdef __BIG_ENDIAN__ headerID = os::Byteswap::byteswap(headerID); #endif return headerID == 0x4d42; } void CImageLoaderBMP::decompress8BitRLE(u8*& bmpData, s32 size, s32 width, s32 height, s32 pitch) const { u8* p = bmpData; u8* newBmp = new u8[(width+pitch)*height]; u8* d = newBmp; u8* destEnd = newBmp + (width+pitch)*height; s32 line = 0; while (bmpData - p < size && d < destEnd) { if (*p == 0) { ++p; switch(*p) { case 0: // end of line ++p; ++line; d = newBmp + (line*(width+pitch)); break; case 1: // end of bmp delete [] bmpData; bmpData = newBmp; return; case 2: ++p; d +=(u8)*p; // delta ++p; d += ((u8)*p)*(width+pitch); ++p; break; default: { // absolute mode s32 count = (u8)*p; ++p; s32 readAdditional = ((2-(count%2))%2); s32 i; for (i=0; i> readShift) & 0x0f; readShift -= 4; if (readShift < 0) { ++*p; readShift = 4; } u8 mask = 0x0f << shift; *d = (*d & (~mask)) | ((color << shift) & mask); shift -= 4; if (shift < 0) { shift = 4; ++d; } } for (i=0; i> 4) & 0x0f; ++p; for (s32 i=0; iread(&header, sizeof(header)); #ifdef __BIG_ENDIAN__ header.Id = os::Byteswap::byteswap(header.Id); header.FileSize = os::Byteswap::byteswap(header.FileSize); header.BitmapDataOffset = os::Byteswap::byteswap(header.BitmapDataOffset); header.BitmapHeaderSize = os::Byteswap::byteswap(header.BitmapHeaderSize); header.Width = os::Byteswap::byteswap(header.Width); header.Height = os::Byteswap::byteswap(header.Height); header.Planes = os::Byteswap::byteswap(header.Planes); header.BPP = os::Byteswap::byteswap(header.BPP); header.Compression = os::Byteswap::byteswap(header.Compression); header.BitmapDataSize = os::Byteswap::byteswap(header.BitmapDataSize); header.PixelPerMeterX = os::Byteswap::byteswap(header.PixelPerMeterX); header.PixelPerMeterY = os::Byteswap::byteswap(header.PixelPerMeterY); header.Colors = os::Byteswap::byteswap(header.Colors); header.ImportantColors = os::Byteswap::byteswap(header.ImportantColors); #endif s32 pitch = 0; //! return if the header is false if (header.Id != 0x4d42) return 0; if (header.Compression > 2) // we'll only handle RLE-Compression { os::Printer::log("Compression mode not supported.", ELL_ERROR); return 0; } if (header.BPP > 32 || !checkImageDimensions(header.Width, header.Height)) { os::Printer::log("Rejecting BMP with unreasonable size or BPP.", ELL_ERROR); return 0; } // adjust bitmap data size to dword boundary header.BitmapDataSize += (4-(header.BitmapDataSize%4))%4; // read palette long pos = file->getPos(); constexpr s32 paletteAllocSize = 256; s32 paletteSize = (header.BitmapDataOffset - pos) / 4; paletteSize = core::clamp(paletteSize, 0, paletteAllocSize); s32* paletteData = 0; if (paletteSize) { // always allocate an 8-bit palette to ensure enough space paletteData = new s32[paletteAllocSize]; memset(paletteData, 0, paletteAllocSize * sizeof(s32)); file->read(paletteData, paletteSize * sizeof(s32)); #ifdef __BIG_ENDIAN__ for (s32 i=0; i(file->getSize()) - header.BitmapDataOffset; } file->seek(header.BitmapDataOffset); f32 t = (header.Width) * (header.BPP / 8.0f); s32 widthInBytes = (s32)t; t -= widthInBytes; if (t!=0.0f) ++widthInBytes; s32 lineData = widthInBytes + ((4-(widthInBytes%4)))%4; pitch = lineData - widthInBytes; u8* bmpData = new u8[header.BitmapDataSize]; file->read(bmpData, header.BitmapDataSize); // decompress data if needed switch(header.Compression) { case 1: // 8 bit rle decompress8BitRLE(bmpData, header.BitmapDataSize, header.Width, header.Height, pitch); break; case 2: // 4 bit rle decompress4BitRLE(bmpData, header.BitmapDataSize, header.Width, header.Height, pitch); break; } // create surface // no default constructor from packed area! ARM problem! core::dimension2d dim; dim.Width = header.Width; dim.Height = header.Height; IImage* image = 0; switch(header.BPP) { case 1: image = new CImage(ECF_A1R5G5B5, dim); if (image) CColorConverter::convert1BitTo16Bit(bmpData, (s16*)image->getData(), header.Width, header.Height, pitch, true); break; case 4: image = new CImage(ECF_A1R5G5B5, dim); if (image) CColorConverter::convert4BitTo16Bit(bmpData, (s16*)image->getData(), header.Width, header.Height, paletteData, pitch, true); break; case 8: image = new CImage(ECF_A1R5G5B5, dim); if (image) CColorConverter::convert8BitTo16Bit(bmpData, (s16*)image->getData(), header.Width, header.Height, paletteData, pitch, true); break; case 16: image = new CImage(ECF_A1R5G5B5, dim); if (image) CColorConverter::convert16BitTo16Bit((s16*)bmpData, (s16*)image->getData(), header.Width, header.Height, pitch, true); break; case 24: image = new CImage(ECF_R8G8B8, dim); if (image) CColorConverter::convert24BitTo24Bit(bmpData, (u8*)image->getData(), header.Width, header.Height, pitch, true, true); break; case 32: // thx to Reinhard Ostermeier image = new CImage(ECF_A8R8G8B8, dim); if (image) CColorConverter::convert32BitTo32Bit((s32*)bmpData, (s32*)image->getData(), header.Width, header.Height, pitch, true); break; }; // clean up delete [] paletteData; delete [] bmpData; return image; } //! creates a loader which is able to load windows bitmaps IImageLoader* createImageLoaderBMP() { return new CImageLoaderBMP; } } // end namespace video } // end namespace irr