Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
#5231: Resurrect BMP loading code from 2014.
  • Loading branch information
codereader committed May 20, 2020
1 parent 477abc6 commit bc20d6d
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 2 deletions.
2 changes: 2 additions & 0 deletions radiantcore/Makefile.am
Expand Up @@ -76,11 +76,13 @@ libradiantcore_la_SOURCES = Radiant.cpp \
fonts/GlyphSet.cpp \
fonts/FontManager.cpp \
grid/GridManager.cpp \
imagefile/BMPLoader.cpp \
imagefile/dds.cpp \
imagefile/ddslib.cpp \
imagefile/DDSImage.cpp \
imagefile/ImageLoader.cpp \
imagefile/ImageLoaderWx.cpp \
imagefile/JPEGLoader.cpp \
imagefile/TGALoader.cpp \
log/COutRedirector.cpp \
log/LogFile.cpp \
Expand Down
200 changes: 200 additions & 0 deletions radiantcore/imagefile/BMPLoader.cpp
@@ -0,0 +1,200 @@
#include "BMPLoader.h"

#include "itextstream.h"
#include "ifilesystem.h"

typedef unsigned char byte;

#include "RGBAImage.h"
#include "stream/ScopedArchiveBuffer.h"
#include "stream/PointerInputStream.h"
#include "stream/utils.h"

namespace image
{

typedef unsigned char PaletteEntry[4];
typedef struct
{
char id[2];
unsigned long fileSize;
unsigned long reserved0;
unsigned long bitmapDataOffset;
unsigned long bitmapHeaderSize;
unsigned long width;
unsigned long height;
unsigned short planes;
unsigned short bitsPerPixel;
unsigned long compression;
unsigned long bitmapDataSize;
unsigned long hRes;
unsigned long vRes;
unsigned long colors;
unsigned long importantColors;
PaletteEntry palette[256];
} BMPHeader_t;

class ReadPixel8
{
PaletteEntry* m_palette;
public:
ReadPixel8(PaletteEntry* palette) : m_palette(palette)
{
}
void operator()(stream::PointerInputStream& inputStream, byte*& pixbuf) const
{
byte palIndex;
inputStream.read(&palIndex, 1);
*pixbuf++ = m_palette[palIndex][2];
*pixbuf++ = m_palette[palIndex][1];
*pixbuf++ = m_palette[palIndex][0];
*pixbuf++ = 0xff;
}
};

class ReadPixel16
{
public:
void operator()(stream::PointerInputStream& inputStream, byte*& pixbuf) const
{
unsigned short shortPixel;
inputStream.read(reinterpret_cast<byte*>(&shortPixel), sizeof(unsigned short)); //!\todo Is this endian safe?
*pixbuf++ = static_cast<byte>(shortPixel & (31 << 10)) >> 7;
*pixbuf++ = static_cast<byte>(shortPixel & (31 << 5)) >> 2;
*pixbuf++ = static_cast<byte>(shortPixel & (31)) << 3;
*pixbuf++ = 0xff;
}
};

class ReadPixel24
{
public:
void operator()(stream::PointerInputStream& inputStream, byte*& pixbuf) const
{
byte bgr[3];
inputStream.read(bgr, 3);
*pixbuf++ = bgr[2];
*pixbuf++ = bgr[1];
*pixbuf++ = bgr[0];
*pixbuf++ = 255;
}
};

class ReadPixel32
{
public:
void operator()(stream::PointerInputStream& inputStream, byte*& pixbuf) const
{
byte bgra[4];
inputStream.read(bgra, 4);
*pixbuf++ = bgra[2];
*pixbuf++ = bgra[1];
*pixbuf++ = bgra[0];
*pixbuf++ = bgra[3];
}
};

template<typename ReadPixel>
void ReadBMP(stream::PointerInputStream& inputStream, byte* bmpRGBA, int rows, int columns, ReadPixel readPixel)
{
for (int row = rows - 1; row >= 0; row--)
{
byte* pixbuf = bmpRGBA + row * columns * 4;

for (int column = 0; column < columns; column++)
{
readPixel(inputStream, pixbuf);
}
}
}

RGBAImagePtr LoadBMPBuff(stream::PointerInputStream& inputStream, std::size_t length)
{
BMPHeader_t bmpHeader;
inputStream.read(reinterpret_cast<byte*>(bmpHeader.id), 2);
bmpHeader.fileSize = stream::readLittleEndian<uint32_t>(inputStream);
bmpHeader.reserved0 = stream::readLittleEndian<uint32_t>(inputStream);
bmpHeader.bitmapDataOffset = stream::readLittleEndian<uint32_t>(inputStream);
bmpHeader.bitmapHeaderSize = stream::readLittleEndian<uint32_t>(inputStream);
bmpHeader.width = stream::readLittleEndian<uint32_t>(inputStream);
bmpHeader.height = stream::readLittleEndian<uint32_t>(inputStream);
bmpHeader.planes = stream::readLittleEndian<uint16_t>(inputStream);
bmpHeader.bitsPerPixel = stream::readLittleEndian<uint16_t>(inputStream);
bmpHeader.compression = stream::readLittleEndian<uint32_t>(inputStream);
bmpHeader.bitmapDataSize = stream::readLittleEndian<uint32_t>(inputStream);
bmpHeader.hRes = stream::readLittleEndian<uint32_t>(inputStream);
bmpHeader.vRes = stream::readLittleEndian<uint32_t>(inputStream);
bmpHeader.colors = stream::readLittleEndian<uint32_t>(inputStream);
bmpHeader.importantColors = stream::readLittleEndian<uint32_t>(inputStream);

if (bmpHeader.bitsPerPixel == 8)
{
int paletteSize = bmpHeader.colors * 4;
inputStream.read(reinterpret_cast<byte*>(bmpHeader.palette), paletteSize);
}

if (bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M')
{
rError() << "LoadBMP: only Windows-style BMP files supported\n";
return RGBAImagePtr();
}
if (bmpHeader.fileSize != length)
{
rError() << "LoadBMP: header size does not match file size (" << bmpHeader.fileSize << " vs. " << length << ")\n";
return RGBAImagePtr();
}
if (bmpHeader.compression != 0)
{
rError() << "LoadBMP: only uncompressed BMP files supported\n";
return RGBAImagePtr();
}
if (bmpHeader.bitsPerPixel < 8)
{
rError() << "LoadBMP: monochrome and 4-bit BMP files not supported\n";
return RGBAImagePtr();
}

int columns = bmpHeader.width;
int rows = bmpHeader.height;
if (rows < 0)
rows = -rows;

RGBAImagePtr image(new RGBAImage(columns, rows));

switch (bmpHeader.bitsPerPixel)
{
case 8:
ReadBMP(inputStream, image->getMipMapPixels(0), rows, columns, ReadPixel8(bmpHeader.palette));
break;
case 16:
ReadBMP(inputStream, image->getMipMapPixels(0), rows, columns, ReadPixel16());
break;
case 24:
ReadBMP(inputStream, image->getMipMapPixels(0), rows, columns, ReadPixel24());
break;
case 32:
ReadBMP(inputStream, image->getMipMapPixels(0), rows, columns, ReadPixel32());
break;
default:
rError() << "LoadBMP: illegal pixel_size '" << bmpHeader.bitsPerPixel << "'\n";
return RGBAImagePtr();
}
return image;
}

ImagePtr BMPLoader::load(ArchiveFile& file) const
{
archive::ScopedArchiveBuffer buffer(file);

stream::PointerInputStream inputStream(buffer.buffer);
return LoadBMPBuff(inputStream, buffer.length);
}

ImageTypeLoader::Extensions BMPLoader::getExtensions() const
{
Extensions extensions;
extensions.push_back("bmp");
return extensions;
}

}
20 changes: 20 additions & 0 deletions radiantcore/imagefile/BMPLoader.h
@@ -0,0 +1,20 @@
#pragma once

#include "ImageTypeLoader.h"

namespace image
{

/**
* \brief
* ImageTypeLoader implementation for Windows Bitmap (BMP) files
*/
class BMPLoader : public ImageTypeLoader
{
public:
// ImageTypeLoader implementation
ImagePtr load(ArchiveFile& file) const override;
Extensions getExtensions() const override;
};

}
6 changes: 5 additions & 1 deletion radiantcore/imagefile/ImageLoader.cpp
Expand Up @@ -2,6 +2,7 @@
#include "ImageLoaderWx.h"
#include "TGALoader.h"
#include "JPEGLoader.h"
#include "BMPLoader.h"
#include "dds.h"

#include "ifilesystem.h"
Expand Down Expand Up @@ -36,9 +37,12 @@ void ImageLoader::addLoaderToMap(const ImageTypeLoader::Ptr& loader)

ImageLoader::ImageLoader()
{
// Wx loader (this handles regular image file types like BMP and PNG)
// Wx loader (this handles regular image file types like PNG)
addLoaderToMap(std::make_shared<ImageLoaderWx>());

// BMP handler
addLoaderToMap(std::make_shared<BMPLoader>());

// JPEG handler
addLoaderToMap(std::make_shared<JPEGLoader>());

Expand Down
1 change: 0 additions & 1 deletion radiantcore/imagefile/ImageLoaderWx.cpp
Expand Up @@ -78,7 +78,6 @@ ImagePtr ImageLoaderWx::load(ArchiveFile& file) const
ImageTypeLoader::Extensions ImageLoaderWx::getExtensions() const
{
ImageTypeLoader::Extensions extensions;
extensions.push_back("bmp");
extensions.push_back("png");
return extensions;
}
Expand Down
2 changes: 2 additions & 0 deletions tools/msvc/DarkRadiantCore.vcxproj
Expand Up @@ -62,6 +62,7 @@
<ClCompile Include="..\..\radiantcore\fonts\GlyphInfo.cpp" />
<ClCompile Include="..\..\radiantcore\fonts\GlyphSet.cpp" />
<ClCompile Include="..\..\radiantcore\grid\GridManager.cpp" />
<ClCompile Include="..\..\radiantcore\imagefile\BMPLoader.cpp" />
<ClCompile Include="..\..\radiantcore\imagefile\dds.cpp" />
<ClCompile Include="..\..\radiantcore\imagefile\DDSImage.cpp" />
<ClCompile Include="..\..\radiantcore\imagefile\ddslib.cpp" />
Expand Down Expand Up @@ -206,6 +207,7 @@
<ClInclude Include="..\..\radiantcore\fonts\GlyphSet.h" />
<ClInclude Include="..\..\radiantcore\grid\GridItem.h" />
<ClInclude Include="..\..\radiantcore\grid\GridManager.h" />
<ClInclude Include="..\..\radiantcore\imagefile\BMPLoader.h" />
<ClInclude Include="..\..\radiantcore\imagefile\dds.h" />
<ClInclude Include="..\..\radiantcore\imagefile\DDSImage.h" />
<ClInclude Include="..\..\radiantcore\imagefile\ddslib.h" />
Expand Down
6 changes: 6 additions & 0 deletions tools/msvc/DarkRadiantCore.vcxproj.filters
Expand Up @@ -469,6 +469,9 @@
<ClCompile Include="..\..\radiantcore\imagefile\JPEGLoader.cpp">
<Filter>src\imagefile</Filter>
</ClCompile>
<ClCompile Include="..\..\radiantcore\imagefile\BMPLoader.cpp">
<Filter>src\imagefile</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\radiantcore\modulesystem\ModuleLoader.h">
Expand Down Expand Up @@ -1002,5 +1005,8 @@
<ClInclude Include="..\..\radiantcore\imagefile\JPEGLoader.h">
<Filter>src\imagefile</Filter>
</ClInclude>
<ClInclude Include="..\..\radiantcore\imagefile\BMPLoader.h">
<Filter>src\imagefile</Filter>
</ClInclude>
</ItemGroup>
</Project>

0 comments on commit bc20d6d

Please sign in to comment.