truelight@4300: /* $Id$ */ truelight@4300: belugas@6449: /** @file bmp.cpp */ belugas@6449: truelight@4300: #include "stdafx.h" truelight@4300: #include "bmp.h" rubidium@6872: #include "core/bitmath_func.hpp" rubidium@6872: #include "core/alloc_func.hpp" truelight@4300: richk@6743: void BmpInitializeBuffer(BmpBuffer *buffer, FILE *file) richk@6743: { truelight@4300: buffer->pos = -1; truelight@4300: buffer->file = file; truelight@4300: buffer->read = 0; truelight@4300: buffer->real_pos = ftell(file); truelight@4300: } truelight@4300: truelight@4300: static inline void AdvanceBuffer(BmpBuffer *buffer) truelight@4300: { truelight@4321: buffer->read = (int)fread(buffer->data, 1, BMP_BUFFER_SIZE, buffer->file); truelight@4300: buffer->pos = 0; truelight@4300: } truelight@4300: truelight@4300: static inline bool EndOfBuffer(BmpBuffer *buffer) truelight@4300: { truelight@4300: if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer); truelight@4300: return buffer->pos == buffer->read; truelight@4300: } truelight@4300: truelight@4300: static inline byte ReadByte(BmpBuffer *buffer) truelight@4300: { truelight@4300: if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer); truelight@4300: buffer->real_pos++; truelight@4300: return buffer->data[buffer->pos++]; truelight@4300: } truelight@4300: truelight@4300: static inline uint16 ReadWord(BmpBuffer *buffer) truelight@4300: { truelight@4300: uint16 var = ReadByte(buffer); truelight@4300: return var | (ReadByte(buffer) << 8); truelight@4300: } truelight@4300: truelight@4300: static inline uint32 ReadDword(BmpBuffer *buffer) truelight@4300: { truelight@4300: uint32 var = ReadWord(buffer); truelight@4300: return var | (ReadWord(buffer) << 16); truelight@4300: } truelight@4300: truelight@4300: static inline void SkipBytes(BmpBuffer *buffer, int bytes) truelight@4300: { truelight@4300: int i; truelight@4300: for (i = 0; i < bytes; i++) ReadByte(buffer); truelight@4300: } truelight@4300: truelight@4300: static inline void SetStreamOffset(BmpBuffer *buffer, int offset) truelight@4300: { truelight@4300: fseek(buffer->file, offset, SEEK_SET); truelight@4300: buffer->pos = -1; truelight@4300: buffer->real_pos = offset; truelight@4300: AdvanceBuffer(buffer); truelight@4300: } truelight@4300: truelight@4300: /** truelight@4300: * Reads a 1 bpp uncompressed bitmap truelight@4300: * The bitmap is converted to a 8 bpp bitmap truelight@4300: */ truelight@4300: static inline bool BmpRead1(BmpBuffer *buffer, BmpInfo *info, BmpData *data) truelight@4300: { truelight@4300: uint x, y, i; truelight@4300: byte pad = GB(4 - info->width / 8, 0, 2); truelight@4300: byte *pixel_row; truelight@4300: byte b; truelight@4300: for (y = info->height; y > 0; y--) { truelight@4300: x = 0; truelight@4300: pixel_row = &data->bitmap[(y - 1) * info->width]; truelight@4300: while (x < info->width) { truelight@4300: if (EndOfBuffer(buffer)) return false; // the file is shorter than expected truelight@4300: b = ReadByte(buffer); truelight@4300: for (i = 8; i > 0; i--) { truelight@4300: if (x < info->width) *pixel_row++ = GB(b, i - 1, 1); truelight@4300: x++; truelight@4300: } truelight@4300: } truelight@4300: /* Padding for 32 bit align */ truelight@4300: SkipBytes(buffer, pad); truelight@4300: } truelight@4300: return true; truelight@4300: } truelight@4300: truelight@4300: /** truelight@4300: * Reads a 4 bpp uncompressed bitmap truelight@4300: * The bitmap is converted to a 8 bpp bitmap truelight@4300: */ truelight@4300: static inline bool BmpRead4(BmpBuffer *buffer, BmpInfo *info, BmpData *data) truelight@4300: { truelight@4300: uint x, y; truelight@4300: byte pad = GB(4 - info->width / 2, 0, 2); truelight@4300: byte *pixel_row; truelight@4300: byte b; truelight@4300: for (y = info->height; y > 0; y--) { truelight@4300: x = 0; truelight@4300: pixel_row = &data->bitmap[(y - 1) * info->width]; truelight@4300: while (x < info->width) { truelight@4300: if (EndOfBuffer(buffer)) return false; // the file is shorter than expected truelight@4300: b = ReadByte(buffer); truelight@4300: *pixel_row++ = GB(b, 4, 4); truelight@4300: x++; truelight@4300: if (x < info->width) { truelight@4300: *pixel_row++ = GB(b, 0, 4); truelight@4300: x++; truelight@4300: } truelight@4300: } truelight@4300: /* Padding for 32 bit align */ truelight@4300: SkipBytes(buffer, pad); truelight@4300: } truelight@4300: return true; truelight@4300: } truelight@4300: truelight@4300: /** truelight@4300: * Reads a 4-bit RLE compressed bitmap truelight@4300: * The bitmap is converted to a 8 bpp bitmap truelight@4300: */ truelight@4300: static inline bool BmpRead4Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data) truelight@4300: { truelight@4300: uint i; truelight@4300: uint x = 0; truelight@4300: uint y = info->height - 1; truelight@4300: byte n, c, b; truelight@4300: byte *pixel = &data->bitmap[y * info->width]; truelight@4300: while (y != 0 || x < info->width) { truelight@4300: if (EndOfBuffer(buffer)) return false; // the file is shorter than expected truelight@4300: n = ReadByte(buffer); truelight@4300: c = ReadByte(buffer); truelight@4300: if (n == 0) { truelight@4300: switch (c) { truelight@4300: case 0: // end of line truelight@4300: x = 0; truelight@4300: pixel = &data->bitmap[--y * info->width]; truelight@4300: break; truelight@4300: case 1: // end of bitmap truelight@4300: x = info->width; truelight@4300: y = 0; truelight@4300: pixel = NULL; truelight@4300: break; truelight@4300: case 2: // delta truelight@4300: x += ReadByte(buffer); truelight@4300: i = ReadByte(buffer); truelight@4300: if (x >= info->width || (y == 0 && i > 0)) return false; truelight@4300: y -= i; truelight@4300: pixel = &data->bitmap[y * info->width + x]; truelight@4300: break; truelight@4300: default: // uncompressed truelight@4300: i = 0; truelight@4300: while (i++ < c) { truelight@4300: if (EndOfBuffer(buffer) || x >= info->width) return false; truelight@4300: b = ReadByte(buffer); truelight@4300: *pixel++ = GB(b, 4, 4); truelight@4300: x++; truelight@4300: if (x < info->width && i++ < c) { truelight@4300: *pixel++ = GB(b, 0, 4); truelight@4300: x++; truelight@4300: } truelight@4300: } truelight@4300: /* Padding for 16 bit align */ truelight@4300: SkipBytes(buffer, ((c + 1) / 2) % 2); truelight@4300: break; truelight@4300: } truelight@4300: } else { truelight@4300: i = 0; truelight@4300: while (i++ < n) { truelight@4300: if (EndOfBuffer(buffer) || x >= info->width) return false; truelight@4300: *pixel++ = GB(c, 4, 4); truelight@4300: x++; truelight@4300: if (x < info->width && i++ < n) { truelight@4300: *pixel++ = GB(c, 0, 4); truelight@4300: x++; truelight@4300: } truelight@4300: } truelight@4300: } truelight@4300: } truelight@4300: return true; truelight@4300: } truelight@4300: truelight@4300: /** truelight@4300: * Reads a 8 bpp bitmap truelight@4300: */ truelight@4300: static inline bool BmpRead8(BmpBuffer *buffer, BmpInfo *info, BmpData *data) truelight@4300: { truelight@4300: uint i; truelight@4300: uint y; truelight@4300: byte pad = GB(4 - info->width, 0, 2); truelight@4300: byte *pixel; truelight@4300: for (y = info->height; y > 0; y--) { truelight@4300: if (EndOfBuffer(buffer)) return false; // the file is shorter than expected truelight@4300: pixel = &data->bitmap[(y - 1) * info->width]; truelight@4300: for (i = 0; i < info->width; i++) *pixel++ = ReadByte(buffer); truelight@4300: /* Padding for 32 bit align */ truelight@4300: SkipBytes(buffer, pad); truelight@4300: } truelight@4300: return true; truelight@4300: } truelight@4300: truelight@4300: /** truelight@4300: * Reads a 8-bit RLE compressed bpp bitmap truelight@4300: */ truelight@4300: static inline bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data) truelight@4300: { truelight@4300: uint i; truelight@4300: uint x = 0; truelight@4300: uint y = info->height - 1; truelight@4300: byte n, c; truelight@4300: byte *pixel = &data->bitmap[y * info->width]; truelight@4300: while (y != 0 || x < info->width) { truelight@4300: if (EndOfBuffer(buffer)) return false; // the file is shorter than expected truelight@4300: n = ReadByte(buffer); truelight@4300: c = ReadByte(buffer); truelight@4300: if (n == 0) { truelight@4300: switch (c) { truelight@4300: case 0: // end of line truelight@4300: x = 0; truelight@4300: pixel = &data->bitmap[--y * info->width]; truelight@4300: break; truelight@4300: case 1: // end of bitmap truelight@4300: x = info->width; truelight@4300: y = 0; truelight@4300: pixel = NULL; truelight@4300: break; truelight@4300: case 2: // delta truelight@4300: x += ReadByte(buffer); truelight@4300: i = ReadByte(buffer); truelight@4300: if (x >= info->width || (y == 0 && i > 0)) return false; truelight@4300: y -= i; truelight@4300: pixel = &data->bitmap[y * info->width + x]; truelight@4300: break; truelight@4300: default: // uncompressed truelight@4300: if ((x += c) > info->width) return false; truelight@4300: for (i = 0; i < c; i++) *pixel++ = ReadByte(buffer); truelight@4300: /* Padding for 16 bit align */ truelight@4300: SkipBytes(buffer, c % 2); truelight@4300: break; truelight@4300: } truelight@4300: } else { truelight@4300: for (i = 0; i < n; i++) { truelight@4300: if (x >= info->width) return false; truelight@4300: *pixel++ = c; truelight@4300: x++; truelight@4300: } truelight@4300: } truelight@4300: } truelight@4300: return true; truelight@4300: } truelight@4300: truelight@4300: /** truelight@4300: * Reads a 24 bpp uncompressed bitmap truelight@4300: */ truelight@4300: static inline bool BmpRead24(BmpBuffer *buffer, BmpInfo *info, BmpData *data) truelight@4300: { truelight@4300: uint x, y; truelight@4300: byte pad = GB(4 - info->width * 3, 0, 2); truelight@4300: byte *pixel_row; truelight@4300: for (y = info->height; y > 0; y--) { truelight@4300: pixel_row = &data->bitmap[(y - 1) * info->width * 3]; truelight@4300: for (x = 0; x < info->width; x++) { truelight@4300: if (EndOfBuffer(buffer)) return false; // the file is shorter than expected truelight@4300: *(pixel_row + 2) = ReadByte(buffer); // green truelight@4300: *(pixel_row + 1) = ReadByte(buffer); // blue truelight@4300: *pixel_row = ReadByte(buffer); // red truelight@4300: pixel_row += 3; truelight@4300: } truelight@4300: /* Padding for 32 bit align */ truelight@4300: SkipBytes(buffer, pad); truelight@4300: } truelight@4300: return true; truelight@4300: } truelight@4300: truelight@4300: /* truelight@4300: * Reads bitmap headers, and palette (if any) truelight@4300: */ truelight@4300: bool BmpReadHeader(BmpBuffer *buffer, BmpInfo *info, BmpData *data) truelight@4300: { truelight@4300: uint32 header_size; truelight@4300: assert(info != NULL); truelight@4300: truelight@4300: /* Reading BMP header */ truelight@4300: if (ReadWord(buffer) != 0x4D42) return false; // signature should be 'BM' truelight@4300: SkipBytes(buffer, 8); // skip file size and reserved truelight@4300: info->offset = ReadDword(buffer); truelight@4300: truelight@4300: /* Reading info header */ truelight@4300: header_size = ReadDword(buffer); truelight@4300: if (header_size < 12) return false; // info header should be at least 12 bytes long truelight@4300: truelight@4300: info->os2_bmp = (header_size == 12); // OS/2 1.x or windows 2.x info header is 12 bytes long truelight@4300: truelight@4300: if (info->os2_bmp) { truelight@4300: info->width = ReadWord(buffer); truelight@4300: info->height = ReadWord(buffer); truelight@4300: header_size -= 8; truelight@4300: } else { truelight@4300: info->width = ReadDword(buffer); truelight@4300: info->height = ReadDword(buffer); truelight@4300: header_size -= 12; truelight@4300: } truelight@4300: truelight@4300: if (ReadWord(buffer) != 1) return false; // BMP can have only 1 plane truelight@4300: truelight@4300: info->bpp = ReadWord(buffer); truelight@4300: if (info->bpp != 1 && info->bpp != 4 && info->bpp != 8 && info->bpp != 24) { truelight@4300: /* Only 1 bpp, 4 bpp, 8bpp and 24 bpp bitmaps are supported */ truelight@4300: return false; truelight@4300: } truelight@4300: truelight@4300: /* Reads compression method if available in info header*/ truelight@4300: if ((header_size -= 4) >= 4) { truelight@4300: info->compression = ReadDword(buffer); truelight@4300: header_size -= 4; truelight@4300: } truelight@4300: truelight@4300: /* Only 4-bit and 8-bit rle compression is supported */ truelight@4300: if (info->compression > 2 || (info->compression > 0 && !(info->bpp == 4 || info->bpp == 8))) return false; truelight@4300: truelight@4300: if (info->bpp <= 8) { truelight@4300: uint i; truelight@4300: truelight@4300: /* Reads number of colors if available in info header */ truelight@4300: if (header_size >= 16) { truelight@4300: SkipBytes(buffer, 12); // skip image size and resolution truelight@4300: info->palette_size = ReadDword(buffer); // number of colors in palette truelight@4300: SkipBytes(buffer, header_size - 16); // skip the end of info header truelight@4300: } truelight@4300: if (info->palette_size == 0) info->palette_size = 1 << info->bpp; truelight@4300: KUDr@5860: data->palette = CallocT(info->palette_size); truelight@4300: if (data->palette == NULL) return false; truelight@4300: truelight@4300: for (i = 0; i < info->palette_size; i++) { truelight@4300: data->palette[i].b = ReadByte(buffer); truelight@4300: data->palette[i].g = ReadByte(buffer); truelight@4300: data->palette[i].r = ReadByte(buffer); truelight@4300: if (!info->os2_bmp) SkipBytes(buffer, 1); // unused truelight@4300: } truelight@4300: } truelight@4300: truelight@4300: return buffer->real_pos <= info->offset; truelight@4300: } truelight@4300: truelight@4300: /* truelight@4300: * Reads the bitmap truelight@4300: * 1 bpp and 4 bpp bitmaps are converted to 8 bpp bitmaps truelight@4300: */ truelight@4300: bool BmpReadBitmap(BmpBuffer *buffer, BmpInfo *info, BmpData *data) truelight@4300: { truelight@4300: assert(info != NULL && data != NULL); truelight@4300: rubidium@6872: data->bitmap = CallocT(info->width * info->height * ((info->bpp == 24) ? 3 : 1)); truelight@4300: if (data->bitmap == NULL) return false; truelight@4300: truelight@4300: /* Load image */ truelight@4300: SetStreamOffset(buffer, info->offset); truelight@4300: switch (info->compression) { truelight@4300: case 0: // no compression truelight@4300: switch (info->bpp) { truelight@4300: case 1: return BmpRead1(buffer, info, data); truelight@4300: case 4: return BmpRead4(buffer, info, data); truelight@4300: case 8: return BmpRead8(buffer, info, data); truelight@4300: case 24: return BmpRead24(buffer, info, data); truelight@4300: default: NOT_REACHED(); return false; truelight@4300: } truelight@4300: case 1: return BmpRead8Rle(buffer, info, data); // 8-bit RLE compression truelight@4300: case 2: return BmpRead4Rle(buffer, info, data); // 4-bit RLE compression truelight@4300: default: NOT_REACHED(); return false; truelight@4300: } truelight@4300: } truelight@4300: truelight@4300: void BmpDestroyData(BmpData *data) truelight@4300: { truelight@4300: assert(data != NULL); truelight@4300: free(data->palette); truelight@4300: free(data->bitmap); truelight@4300: }