123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- // 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"
- #ifdef _IRR_COMPILE_WITH_BMP_LOADER_
- #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<count; ++i)
- {
- *d = *p;
- ++p;
- ++d;
- }
- for (i=0; i<readAdditional; ++i)
- ++p;
- }
- }
- }
- else
- {
- s32 count = (u8)*p; ++p;
- u8 color = *p; ++p;
- for (s32 i=0; i<count; ++i)
- {
- *d = color;
- ++d;
- }
- }
- }
- delete [] bmpData;
- bmpData = newBmp;
- }
- void CImageLoaderBMP::decompress4BitRLE(u8*& bmpData, s32 size, s32 width, s32 height, s32 pitch) const
- {
- s32 lineWidth = (width+1)/2+pitch;
- u8* p = bmpData;
- u8* newBmp = new u8[lineWidth*height];
- u8* d = newBmp;
- u8* destEnd = newBmp + lineWidth*height;
- s32 line = 0;
- s32 shift = 4;
- while (bmpData - p < size && d < destEnd)
- {
- if (*p == 0)
- {
- ++p;
- switch(*p)
- {
- case 0: // end of line
- ++p;
- ++line;
- d = newBmp + (line*lineWidth);
- shift = 4;
- break;
- case 1: // end of bmp
- delete [] bmpData;
- bmpData = newBmp;
- return;
- case 2:
- {
- ++p;
- s32 x = (u8)*p; ++p;
- s32 y = (u8)*p; ++p;
- d += x/2 + y*lineWidth;
- shift = x%2==0 ? 4 : 0;
- }
- break;
- default:
- {
- // absolute mode
- s32 count = (u8)*p; ++p;
- s32 readAdditional = ((2-((count)%2))%2);
- s32 readShift = 4;
- s32 i;
- for (i=0; i<count; ++i)
- {
- s32 color = (((u8)*p) >> 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<readAdditional; ++i)
- ++p;
- }
- }
- }
- else
- {
- s32 count = (u8)*p; ++p;
- s32 color1 = (u8)*p; color1 = color1 & 0x0f;
- s32 color2 = (u8)*p; color2 = (color2 >> 4) & 0x0f;
- ++p;
- for (s32 i=0; i<count; ++i)
- {
- u8 mask = 0x0f << shift;
- u8 toSet = (shift==0 ? color1 : color2) << shift;
- *d = (*d & (~mask)) | (toSet & mask);
- shift -= 4;
- if (shift < 0)
- {
- shift = 4;
- ++d;
- }
- }
- }
- }
- delete [] bmpData;
- bmpData = newBmp;
- }
- //! creates a surface from the file
- IImage* CImageLoaderBMP::loadImage(io::IReadFile* file, bool skip_checking) const
- {
- SBMPHeader header;
- file->read(&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;
- }
- // adjust bitmap data size to dword boundary
- header.BitmapDataSize += (4-(header.BitmapDataSize%4))%4;
- // read palette
- long pos = file->getPos();
- s32 paletteSize = (header.BitmapDataOffset - pos) / 4;
- s32* paletteData = 0;
- if (paletteSize)
- {
- paletteData = new s32[paletteSize];
- file->read(paletteData, paletteSize * sizeof(s32));
- #ifdef __BIG_ENDIAN__
- for (s32 i=0; i<paletteSize; ++i)
- paletteData[i] = os::Byteswap::byteswap(paletteData[i]);
- #endif
- }
- // read image data
- if (!header.BitmapDataSize)
- {
- // okay, lets guess the size
- // some tools simply don't set it
- header.BitmapDataSize = static_cast<u32>(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<u32> 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->lock(), header.Width, header.Height, pitch, true);
- break;
- case 4:
- image = new CImage(ECF_A1R5G5B5, dim);
- if (image)
- CColorConverter::convert4BitTo16Bit(bmpData, (s16*)image->lock(), header.Width, header.Height, paletteData, pitch, true);
- break;
- case 8:
- image = new CImage(ECF_A1R5G5B5, dim);
- if (image)
- CColorConverter::convert8BitTo16Bit(bmpData, (s16*)image->lock(), header.Width, header.Height, paletteData, pitch, true);
- break;
- case 16:
- image = new CImage(ECF_A1R5G5B5, dim);
- if (image)
- CColorConverter::convert16BitTo16Bit((s16*)bmpData, (s16*)image->lock(), header.Width, header.Height, pitch, true);
- break;
- case 24:
- image = new CImage(ECF_R8G8B8, dim);
- if (image)
- CColorConverter::convert24BitTo24Bit(bmpData, (u8*)image->lock(), 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->lock(), header.Width, header.Height, pitch, true);
- break;
- };
- if (image)
- image->unlock();
- // 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
- #endif
|