1
0
forked from 0ad/0ad
0ad/source/lib/res/graphics/tex_bmp.cpp

143 lines
3.2 KiB
C++
Raw Normal View History

#include "precompiled.h"
#include "lib/byte_order.h"
#include "tex_codec.h"
#pragma pack(push, 1)
struct BmpHeader
{
// BITMAPFILEHEADER
u16 bfType; // "BM"
u32 bfSize; // of file
u16 bfReserved1;
u16 bfReserved2;
u32 bfOffBits; // offset to image data
// BITMAPINFOHEADER
u32 biSize;
long biWidth;
long biHeight;
u16 biPlanes;
u16 biBitCount;
u32 biCompression;
u32 biSizeImage;
// the following are unused and zeroed when writing:
long biXPelsPerMeter;
long biYPelsPerMeter;
u32 biClrUsed;
u32 biClrImportant;
};
#pragma pack(pop)
#define BI_RGB 0 // biCompression
static int bmp_transform(Tex* t, int new_flags)
{
return TEX_CODEC_CANNOT_HANDLE;
}
// requirements: uncompressed, direct colour, bottom up
static int bmp_decode(u8* file, size_t file_size, Tex* t, const char** perr_msg)
{
// check header signature (bfType == "BM"?).
// we compare single bytes to be endian-safe.
if(file[0] != 'B' || file[1] != 'M')
return TEX_CODEC_CANNOT_HANDLE;
const size_t hdr_size = sizeof(BmpHeader);
// make sure we can access all header fields
if(file_size < hdr_size)
{
*perr_msg = "header not completely read";
fail:
return ERR_CORRUPTED;
}
const BmpHeader* hdr = (const BmpHeader*)file;
const long w = (long)read_le32(&hdr->biWidth);
const long h_ = (long)read_le32(&hdr->biHeight);
const u16 bpp = read_le16(&hdr->biBitCount);
const u32 compress = read_le32(&hdr->biCompression);
const u32 ofs = read_le32(&hdr->bfOffBits);
const int orientation = (h_ < 0)? TEX_TOP_DOWN : TEX_BOTTOM_UP;
const long h = abs(h_);
const size_t pitch = w * bpp/8;
const size_t img_size = h * pitch;
u8* const img = file + ofs;
int flags = TEX_BGR;
if(bpp == 32)
flags |= TEX_ALPHA;
// sanity checks
const char* err = 0;
if(compress != BI_RGB)
err = "compressed";
if(bpp != 24 && bpp != 32)
err = "invalid bpp (not direct colour)";
if(file_size < ofs+img_size)
err = "image not completely read";
if(err)
{
*perr_msg = err;
goto fail;
}
t->ofs = ofs;
t->w = w;
t->h = h;
t->bpp = bpp;
t->flags = flags;
tex_codec_set_orientation(t, orientation);
return 0;
}
static int bmp_encode(const char* ext, Tex* t, u8** out, size_t* out_size, const char** perr_msg)
{
if(stricmp(ext, "bmp"))
return TEX_CODEC_CANNOT_HANDLE;
size_t img_size = t->w * t->h * t->bpp/8;
const size_t hdr_size = sizeof(BmpHeader); // needed for BITMAPFILEHEADER
const size_t file_size = hdr_size + img_size;
const long h = (t->flags & TEX_TOP_DOWN)? -(long)t->h : t->h;
int transforms = t->flags;
transforms &= ~TEX_ORIENTATION; // no flip needed - we can set top-down bit.
transforms ^= TEX_BGR; // BMP is native BGR.
tex_transform(t, transforms);
const BmpHeader hdr =
{
// BITMAPFILEHEADER
0x4d42, // bfType = 'B','M'
(u32)file_size, // bfSize
0, 0, // bfReserved1,2
hdr_size, // bfOffBits
// BITMAPINFOHEADER
40, // biSize = sizeof(BITMAPINFOHEADER)
t->w,
h,
1, // biPlanes
t->bpp,
BI_RGB, // biCompression
(u32)img_size, // biSizeImage
0, 0, 0, 0 // unused (bi?PelsPerMeter, biClr*)
};
return 0;
}
TEX_CODEC_REGISTER(bmp);