Mercurial > hg > openttd
view src/bmp.cpp @ 5668:3d6d9bff3dd8 draft
(svn r8128) -Codechange: Split sprite and palette remap into separate 32 bit values.
This lets us increase the sprite width from 14 to up to 29 bits,
effectively nulling the old sprite limit. Table changes in next commit.
author | peter1138 <peter1138@openttd.org> |
---|---|
date | Sun, 14 Jan 2007 19:57:49 +0000 (2007-01-14) |
parents | 358c07fb3212 |
children | 595dc16a6fd8 |
line wrap: on
line source
/* $Id$ */ #include "stdafx.h" #include "openttd.h" #include "gfx.h" #include "bmp.h" #include "macros.h" #include "helpers.hpp" void BmpInitializeBuffer(BmpBuffer *buffer, FILE *file) { buffer->pos = -1; buffer->file = file; buffer->read = 0; buffer->real_pos = ftell(file); } static inline void AdvanceBuffer(BmpBuffer *buffer) { buffer->read = (int)fread(buffer->data, 1, BMP_BUFFER_SIZE, buffer->file); buffer->pos = 0; } static inline bool EndOfBuffer(BmpBuffer *buffer) { if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer); return buffer->pos == buffer->read; } static inline byte ReadByte(BmpBuffer *buffer) { if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer); buffer->real_pos++; return buffer->data[buffer->pos++]; } static inline uint16 ReadWord(BmpBuffer *buffer) { uint16 var = ReadByte(buffer); return var | (ReadByte(buffer) << 8); } static inline uint32 ReadDword(BmpBuffer *buffer) { uint32 var = ReadWord(buffer); return var | (ReadWord(buffer) << 16); } static inline void SkipBytes(BmpBuffer *buffer, int bytes) { int i; for (i = 0; i < bytes; i++) ReadByte(buffer); } static inline void SetStreamOffset(BmpBuffer *buffer, int offset) { fseek(buffer->file, offset, SEEK_SET); buffer->pos = -1; buffer->real_pos = offset; AdvanceBuffer(buffer); } /** * Reads a 1 bpp uncompressed bitmap * The bitmap is converted to a 8 bpp bitmap */ static inline bool BmpRead1(BmpBuffer *buffer, BmpInfo *info, BmpData *data) { uint x, y, i; byte pad = GB(4 - info->width / 8, 0, 2); byte *pixel_row; byte b; for (y = info->height; y > 0; y--) { x = 0; pixel_row = &data->bitmap[(y - 1) * info->width]; while (x < info->width) { if (EndOfBuffer(buffer)) return false; // the file is shorter than expected b = ReadByte(buffer); for (i = 8; i > 0; i--) { if (x < info->width) *pixel_row++ = GB(b, i - 1, 1); x++; } } /* Padding for 32 bit align */ SkipBytes(buffer, pad); } return true; } /** * Reads a 4 bpp uncompressed bitmap * The bitmap is converted to a 8 bpp bitmap */ static inline bool BmpRead4(BmpBuffer *buffer, BmpInfo *info, BmpData *data) { uint x, y; byte pad = GB(4 - info->width / 2, 0, 2); byte *pixel_row; byte b; for (y = info->height; y > 0; y--) { x = 0; pixel_row = &data->bitmap[(y - 1) * info->width]; while (x < info->width) { if (EndOfBuffer(buffer)) return false; // the file is shorter than expected b = ReadByte(buffer); *pixel_row++ = GB(b, 4, 4); x++; if (x < info->width) { *pixel_row++ = GB(b, 0, 4); x++; } } /* Padding for 32 bit align */ SkipBytes(buffer, pad); } return true; } /** * Reads a 4-bit RLE compressed bitmap * The bitmap is converted to a 8 bpp bitmap */ static inline bool BmpRead4Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data) { uint i; uint x = 0; uint y = info->height - 1; byte n, c, b; byte *pixel = &data->bitmap[y * info->width]; while (y != 0 || x < info->width) { if (EndOfBuffer(buffer)) return false; // the file is shorter than expected n = ReadByte(buffer); c = ReadByte(buffer); if (n == 0) { switch (c) { case 0: // end of line x = 0; pixel = &data->bitmap[--y * info->width]; break; case 1: // end of bitmap x = info->width; y = 0; pixel = NULL; break; case 2: // delta x += ReadByte(buffer); i = ReadByte(buffer); if (x >= info->width || (y == 0 && i > 0)) return false; y -= i; pixel = &data->bitmap[y * info->width + x]; break; default: // uncompressed i = 0; while (i++ < c) { if (EndOfBuffer(buffer) || x >= info->width) return false; b = ReadByte(buffer); *pixel++ = GB(b, 4, 4); x++; if (x < info->width && i++ < c) { *pixel++ = GB(b, 0, 4); x++; } } /* Padding for 16 bit align */ SkipBytes(buffer, ((c + 1) / 2) % 2); break; } } else { i = 0; while (i++ < n) { if (EndOfBuffer(buffer) || x >= info->width) return false; *pixel++ = GB(c, 4, 4); x++; if (x < info->width && i++ < n) { *pixel++ = GB(c, 0, 4); x++; } } } } return true; } /** * Reads a 8 bpp bitmap */ static inline bool BmpRead8(BmpBuffer *buffer, BmpInfo *info, BmpData *data) { uint i; uint y; byte pad = GB(4 - info->width, 0, 2); byte *pixel; for (y = info->height; y > 0; y--) { if (EndOfBuffer(buffer)) return false; // the file is shorter than expected pixel = &data->bitmap[(y - 1) * info->width]; for (i = 0; i < info->width; i++) *pixel++ = ReadByte(buffer); /* Padding for 32 bit align */ SkipBytes(buffer, pad); } return true; } /** * Reads a 8-bit RLE compressed bpp bitmap */ static inline bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data) { uint i; uint x = 0; uint y = info->height - 1; byte n, c; byte *pixel = &data->bitmap[y * info->width]; while (y != 0 || x < info->width) { if (EndOfBuffer(buffer)) return false; // the file is shorter than expected n = ReadByte(buffer); c = ReadByte(buffer); if (n == 0) { switch (c) { case 0: // end of line x = 0; pixel = &data->bitmap[--y * info->width]; break; case 1: // end of bitmap x = info->width; y = 0; pixel = NULL; break; case 2: // delta x += ReadByte(buffer); i = ReadByte(buffer); if (x >= info->width || (y == 0 && i > 0)) return false; y -= i; pixel = &data->bitmap[y * info->width + x]; break; default: // uncompressed if ((x += c) > info->width) return false; for (i = 0; i < c; i++) *pixel++ = ReadByte(buffer); /* Padding for 16 bit align */ SkipBytes(buffer, c % 2); break; } } else { for (i = 0; i < n; i++) { if (x >= info->width) return false; *pixel++ = c; x++; } } } return true; } /** * Reads a 24 bpp uncompressed bitmap */ static inline bool BmpRead24(BmpBuffer *buffer, BmpInfo *info, BmpData *data) { uint x, y; byte pad = GB(4 - info->width * 3, 0, 2); byte *pixel_row; for (y = info->height; y > 0; y--) { pixel_row = &data->bitmap[(y - 1) * info->width * 3]; for (x = 0; x < info->width; x++) { if (EndOfBuffer(buffer)) return false; // the file is shorter than expected *(pixel_row + 2) = ReadByte(buffer); // green *(pixel_row + 1) = ReadByte(buffer); // blue *pixel_row = ReadByte(buffer); // red pixel_row += 3; } /* Padding for 32 bit align */ SkipBytes(buffer, pad); } return true; } /* * Reads bitmap headers, and palette (if any) */ bool BmpReadHeader(BmpBuffer *buffer, BmpInfo *info, BmpData *data) { uint32 header_size; assert(info != NULL); /* Reading BMP header */ if (ReadWord(buffer) != 0x4D42) return false; // signature should be 'BM' SkipBytes(buffer, 8); // skip file size and reserved info->offset = ReadDword(buffer); /* Reading info header */ header_size = ReadDword(buffer); if (header_size < 12) return false; // info header should be at least 12 bytes long info->os2_bmp = (header_size == 12); // OS/2 1.x or windows 2.x info header is 12 bytes long if (info->os2_bmp) { info->width = ReadWord(buffer); info->height = ReadWord(buffer); header_size -= 8; } else { info->width = ReadDword(buffer); info->height = ReadDword(buffer); header_size -= 12; } if (ReadWord(buffer) != 1) return false; // BMP can have only 1 plane info->bpp = ReadWord(buffer); if (info->bpp != 1 && info->bpp != 4 && info->bpp != 8 && info->bpp != 24) { /* Only 1 bpp, 4 bpp, 8bpp and 24 bpp bitmaps are supported */ return false; } /* Reads compression method if available in info header*/ if ((header_size -= 4) >= 4) { info->compression = ReadDword(buffer); header_size -= 4; } /* Only 4-bit and 8-bit rle compression is supported */ if (info->compression > 2 || (info->compression > 0 && !(info->bpp == 4 || info->bpp == 8))) return false; if (info->bpp <= 8) { uint i; /* Reads number of colors if available in info header */ if (header_size >= 16) { SkipBytes(buffer, 12); // skip image size and resolution info->palette_size = ReadDword(buffer); // number of colors in palette SkipBytes(buffer, header_size - 16); // skip the end of info header } if (info->palette_size == 0) info->palette_size = 1 << info->bpp; data->palette = CallocT<Colour>(info->palette_size); if (data->palette == NULL) return false; for (i = 0; i < info->palette_size; i++) { data->palette[i].b = ReadByte(buffer); data->palette[i].g = ReadByte(buffer); data->palette[i].r = ReadByte(buffer); if (!info->os2_bmp) SkipBytes(buffer, 1); // unused } } return buffer->real_pos <= info->offset; } /* * Reads the bitmap * 1 bpp and 4 bpp bitmaps are converted to 8 bpp bitmaps */ bool BmpReadBitmap(BmpBuffer *buffer, BmpInfo *info, BmpData *data) { assert(info != NULL && data != NULL); data->bitmap = (byte*)calloc(info->width * info->height, ((info->bpp == 24) ? 3 : 1) * sizeof(byte)); if (data->bitmap == NULL) return false; /* Load image */ SetStreamOffset(buffer, info->offset); switch (info->compression) { case 0: // no compression switch (info->bpp) { case 1: return BmpRead1(buffer, info, data); case 4: return BmpRead4(buffer, info, data); case 8: return BmpRead8(buffer, info, data); case 24: return BmpRead24(buffer, info, data); default: NOT_REACHED(); return false; } case 1: return BmpRead8Rle(buffer, info, data); // 8-bit RLE compression case 2: return BmpRead4Rle(buffer, info, data); // 4-bit RLE compression default: NOT_REACHED(); return false; } } void BmpDestroyData(BmpData *data) { assert(data != NULL); free(data->palette); free(data->bitmap); }