diff --git a/src/bmp.cpp b/src/bmp.cpp index 7add0630ca..202dce7a39 100644 --- a/src/bmp.cpp +++ b/src/bmp.cpp @@ -8,92 +8,32 @@ /** @file bmp.cpp Read and write support for bmps. */ #include "stdafx.h" +#include "random_access_file_type.h" #include "bmp.h" #include "core/bitmath_func.hpp" #include "safeguards.h" -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) -{ - if (buffer->read < 0) return; - - buffer->read = (int)fread(buffer->data, 1, BMP_BUFFER_SIZE, buffer->file); - buffer->pos = 0; -} - -static inline bool EndOfBuffer(BmpBuffer *buffer) -{ - if (buffer->read < 0) return false; - - if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer); - return buffer->pos == buffer->read; -} - -static inline uint8_t ReadByte(BmpBuffer *buffer) -{ - if (buffer->read < 0) return 0; - - if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer); - buffer->real_pos++; - return buffer->data[buffer->pos++]; -} - -static inline uint16_t ReadWord(BmpBuffer *buffer) -{ - uint16_t var = ReadByte(buffer); - return var | (ReadByte(buffer) << 8); -} - -static inline uint32_t ReadDword(BmpBuffer *buffer) -{ - uint32_t 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) -{ - if (fseek(buffer->file, offset, SEEK_SET) < 0) { - buffer->read = -1; - } - 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) +static inline bool BmpRead1(RandomAccessFile &file, BmpInfo &info, BmpData &data) { uint8_t pad = GB(4 - info.width / 8, 0, 2); for (uint y = info.height; y > 0; y--) { uint x = 0; uint8_t *pixel_row = &data.bitmap[(y - 1) * static_cast(info.width)]; while (x < info.width) { - if (EndOfBuffer(buffer)) return false; // the file is shorter than expected - uint8_t b = ReadByte(buffer); + if (file.AtEndOfFile()) return false; // the file is shorter than expected + uint8_t b = file.ReadByte(); for (uint 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); + file.SkipBytes(pad); } return true; } @@ -102,15 +42,15 @@ static inline bool BmpRead1(BmpBuffer *buffer, BmpInfo &info, BmpData &data) * 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) +static inline bool BmpRead4(RandomAccessFile &file, BmpInfo &info, BmpData &data) { uint8_t pad = GB(4 - info.width / 2, 0, 2); for (uint y = info.height; y > 0; y--) { uint x = 0; uint8_t *pixel_row = &data.bitmap[(y - 1) * static_cast(info.width)]; while (x < info.width) { - if (EndOfBuffer(buffer)) return false; // the file is shorter than expected - uint8_t b = ReadByte(buffer); + if (file.AtEndOfFile()) return false; // the file is shorter than expected + uint8_t b = file.ReadByte(); *pixel_row++ = GB(b, 4, 4); x++; if (x < info.width) { @@ -119,7 +59,7 @@ static inline bool BmpRead4(BmpBuffer *buffer, BmpInfo &info, BmpData &data) } } /* Padding for 32 bit align */ - SkipBytes(buffer, pad); + file.SkipBytes(pad); } return true; } @@ -128,16 +68,16 @@ static inline bool BmpRead4(BmpBuffer *buffer, BmpInfo &info, BmpData &data) * 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) +static inline bool BmpRead4Rle(RandomAccessFile &file, BmpInfo &info, BmpData &data) { uint x = 0; uint y = info.height - 1; uint8_t *pixel = &data.bitmap[y * static_cast(info.width)]; while (y != 0 || x < info.width) { - if (EndOfBuffer(buffer)) return false; // the file is shorter than expected + if (file.AtEndOfFile()) return false; // the file is shorter than expected - uint8_t n = ReadByte(buffer); - uint8_t c = ReadByte(buffer); + uint8_t n = file.ReadByte(); + uint8_t c = file.ReadByte(); if (n == 0) { switch (c) { case 0: // end of line @@ -150,9 +90,9 @@ static inline bool BmpRead4Rle(BmpBuffer *buffer, BmpInfo &info, BmpData &data) return true; case 2: { // delta - if (EndOfBuffer(buffer)) return false; - uint8_t dx = ReadByte(buffer); - uint8_t dy = ReadByte(buffer); + if (file.AtEndOfFile()) return false; + uint8_t dx = file.ReadByte(); + uint8_t dy = file.ReadByte(); /* Check for over- and underflow. */ if (x + dx >= info.width || x + dx < x || dy > y) return false; @@ -166,8 +106,8 @@ static inline bool BmpRead4Rle(BmpBuffer *buffer, BmpInfo &info, BmpData &data) default: { // uncompressed uint i = 0; while (i++ < c) { - if (EndOfBuffer(buffer) || x >= info.width) return false; - uint8_t b = ReadByte(buffer); + if (file.AtEndOfFile() || x >= info.width) return false; + uint8_t b = file.ReadByte(); *pixel++ = GB(b, 4, 4); x++; if (i++ < c) { @@ -177,7 +117,7 @@ static inline bool BmpRead4Rle(BmpBuffer *buffer, BmpInfo &info, BmpData &data) } } /* Padding for 16 bit align */ - SkipBytes(buffer, ((c + 1) / 2) % 2); + file.SkipBytes(((c + 1) / 2) % 2); break; } } @@ -202,15 +142,15 @@ static inline bool BmpRead4Rle(BmpBuffer *buffer, BmpInfo &info, BmpData &data) /** * Reads a 8 bpp bitmap */ -static inline bool BmpRead8(BmpBuffer *buffer, BmpInfo &info, BmpData &data) +static inline bool BmpRead8(RandomAccessFile &file, BmpInfo &info, BmpData &data) { uint8_t pad = GB(4 - info.width, 0, 2); for (uint y = info.height; y > 0; y--) { - if (EndOfBuffer(buffer)) return false; // the file is shorter than expected + if (file.AtEndOfFile()) return false; // the file is shorter than expected uint8_t *pixel = &data.bitmap[(y - 1) * static_cast(info.width)]; - for (uint i = 0; i < info.width; i++) *pixel++ = ReadByte(buffer); + for (uint i = 0; i < info.width; i++) *pixel++ = file.ReadByte(); /* Padding for 32 bit align */ - SkipBytes(buffer, pad); + file.SkipBytes(pad); } return true; } @@ -218,16 +158,16 @@ static inline bool BmpRead8(BmpBuffer *buffer, BmpInfo &info, BmpData &data) /** * Reads a 8-bit RLE compressed bpp bitmap */ -static inline bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo &info, BmpData &data) +static inline bool BmpRead8Rle(RandomAccessFile &file, BmpInfo &info, BmpData &data) { uint x = 0; uint y = info.height - 1; uint8_t *pixel = &data.bitmap[y * static_cast(info.width)]; while (y != 0 || x < info.width) { - if (EndOfBuffer(buffer)) return false; // the file is shorter than expected + if (file.AtEndOfFile()) return false; // the file is shorter than expected - uint8_t n = ReadByte(buffer); - uint8_t c = ReadByte(buffer); + uint8_t n = file.ReadByte(); + uint8_t c = file.ReadByte(); if (n == 0) { switch (c) { case 0: // end of line @@ -240,9 +180,9 @@ static inline bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo &info, BmpData &data) return true; case 2: { // delta - if (EndOfBuffer(buffer)) return false; - uint8_t dx = ReadByte(buffer); - uint8_t dy = ReadByte(buffer); + if (file.AtEndOfFile()) return false; + uint8_t dx = file.ReadByte(); + uint8_t dy = file.ReadByte(); /* Check for over- and underflow. */ if (x + dx >= info.width || x + dx < x || dy > y) return false; @@ -255,12 +195,12 @@ static inline bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo &info, BmpData &data) default: { // uncompressed for (uint i = 0; i < c; i++) { - if (EndOfBuffer(buffer) || x >= info.width) return false; - *pixel++ = ReadByte(buffer); + if (file.AtEndOfFile() || x >= info.width) return false; + *pixel++ = file.ReadByte(); x++; } /* Padding for 16 bit align */ - SkipBytes(buffer, c % 2); + file.SkipBytes(c % 2); break; } } @@ -280,20 +220,20 @@ static inline bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo &info, BmpData &data) /** * Reads a 24 bpp uncompressed bitmap */ -static inline bool BmpRead24(BmpBuffer *buffer, BmpInfo &info, BmpData &data) +static inline bool BmpRead24(RandomAccessFile &file, BmpInfo &info, BmpData &data) { uint8_t pad = GB(4 - info.width * 3, 0, 2); for (uint y = info.height; y > 0; --y) { uint8_t *pixel_row = &data.bitmap[(y - 1) * static_cast(info.width) * 3]; for (uint 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 + if (file.AtEndOfFile()) return false; // the file is shorter than expected + *(pixel_row + 2) = file.ReadByte(); // green + *(pixel_row + 1) = file.ReadByte(); // blue + *pixel_row = file.ReadByte(); // red pixel_row += 3; } /* Padding for 32 bit align */ - SkipBytes(buffer, pad); + file.SkipBytes(pad); } return true; } @@ -301,34 +241,34 @@ static inline bool BmpRead24(BmpBuffer *buffer, BmpInfo &info, BmpData &data) /* * Reads bitmap headers, and palette (if any) */ -bool BmpReadHeader(BmpBuffer *buffer, BmpInfo &info, BmpData &data) +bool BmpReadHeader(RandomAccessFile &file, BmpInfo &info, BmpData &data) { info = {}; /* 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); + if (file.ReadWord() != 0x4D42) return false; // signature should be 'BM' + file.SkipBytes(8); // skip file size and reserved + info.offset = file.ReadDword() + file.GetStartPos(); /* Reading info header */ - uint32_t header_size = ReadDword(buffer); + uint32_t header_size = file.ReadDword(); 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); + info.width = file.ReadWord(); + info.height = file.ReadWord(); header_size -= 8; } else { - info.width = ReadDword(buffer); - info.height = ReadDword(buffer); + info.width = file.ReadDword(); + info.height = file.ReadDword(); header_size -= 12; } - if (ReadWord(buffer) != 1) return false; // BMP can have only 1 plane + if (file.ReadWord() != 1) return false; // BMP can have only 1 plane - info.bpp = ReadWord(buffer); + info.bpp = file.ReadWord(); 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; @@ -336,7 +276,7 @@ bool BmpReadHeader(BmpBuffer *buffer, BmpInfo &info, BmpData &data) /* Reads compression method if available in info header*/ if ((header_size -= 4) >= 4) { - info.compression = ReadDword(buffer); + info.compression = file.ReadDword(); header_size -= 4; } @@ -346,9 +286,9 @@ bool BmpReadHeader(BmpBuffer *buffer, BmpInfo &info, BmpData &data) if (info.bpp <= 8) { /* Reads number of colours if available in info header */ if (header_size >= 16) { - SkipBytes(buffer, 12); // skip image size and resolution - info.palette_size = ReadDword(buffer); // number of colours in palette - SkipBytes(buffer, header_size - 16); // skip the end of info header + file.SkipBytes(12); // skip image size and resolution + info.palette_size = file.ReadDword(); // number of colours in palette + file.SkipBytes(header_size - 16); // skip the end of info header } uint maximum_palette_size = 1U << info.bpp; @@ -360,37 +300,39 @@ bool BmpReadHeader(BmpBuffer *buffer, BmpInfo &info, BmpData &data) data.palette.resize(info.palette_size); for (auto &colour : data.palette) { - colour.b = ReadByte(buffer); - colour.g = ReadByte(buffer); - colour.r = ReadByte(buffer); - if (!info.os2_bmp) SkipBytes(buffer, 1); // unused + colour.b = file.ReadByte(); + colour.g = file.ReadByte(); + colour.r = file.ReadByte(); + if (!info.os2_bmp) file.SkipBytes(1); // unused } } - return buffer->real_pos <= info.offset; + return file.GetPos() <= 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) +bool BmpReadBitmap(RandomAccessFile &file, BmpInfo &info, BmpData &data) { data.bitmap.resize(static_cast(info.width) * info.height * ((info.bpp == 24) ? 3 : 1)); /* Load image */ - SetStreamOffset(buffer, info.offset); + file.SeekTo(info.offset, SEEK_SET); 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); + case 0: // no compression + switch (info.bpp) { + case 1: return BmpRead1(file, info, data); + case 4: return BmpRead4(file, info, data); + case 8: return BmpRead8(file, info, data); + case 24: return BmpRead24(file, info, data); + default: NOT_REACHED(); + } + break; + + case 1: return BmpRead8Rle(file, info, data); // 8-bit RLE compression + case 2: return BmpRead4Rle(file, info, data); // 4-bit RLE compression default: NOT_REACHED(); - } - 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(); } } diff --git a/src/bmp.h b/src/bmp.h index de094a2b53..0e82aff004 100644 --- a/src/bmp.h +++ b/src/bmp.h @@ -11,9 +11,10 @@ #define BMP_H #include "gfx_type.h" +#include "random_access_file_type.h" struct BmpInfo { - uint32_t offset; ///< offset of bitmap data from .bmp file beginning + size_t offset; ///< offset of bitmap data from .bmp file beginning uint32_t width; ///< bitmap width uint32_t height; ///< bitmap height bool os2_bmp; ///< true if OS/2 1.x or windows 2.x bitmap @@ -27,18 +28,7 @@ struct BmpData { std::vector bitmap; }; -#define BMP_BUFFER_SIZE 1024 - -struct BmpBuffer { - uint8_t data[BMP_BUFFER_SIZE]; - int pos; - int read; - FILE *file; - uint real_pos; -}; - -void BmpInitializeBuffer(BmpBuffer *buffer, FILE *file); -bool BmpReadHeader(BmpBuffer *buffer, BmpInfo &info, BmpData &data); -bool BmpReadBitmap(BmpBuffer *buffer, BmpInfo &info, BmpData &data); +bool BmpReadHeader(RandomAccessFile &file, BmpInfo &info, BmpData &data); +bool BmpReadBitmap(RandomAccessFile &file, BmpInfo &info, BmpData &data); #endif /* BMP_H */ diff --git a/src/heightmap.cpp b/src/heightmap.cpp index 285ba5928a..4cefb79aea 100644 --- a/src/heightmap.cpp +++ b/src/heightmap.cpp @@ -263,35 +263,29 @@ static void ReadHeightmapBMPImageData(std::span map, const BmpInfo &inf */ static bool ReadHeightmapBMP(const char *filename, uint *x, uint *y, std::vector *map) { - FILE *f; - BmpInfo info; + BmpInfo info{}; BmpData data{}; - BmpBuffer buffer; - f = FioFOpenFile(filename, "rb", HEIGHTMAP_DIR); - if (f == nullptr) { + if (!FioCheckFileExists(filename, HEIGHTMAP_DIR)) { ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_PNGMAP_FILE_NOT_FOUND, WL_ERROR); return false; } - BmpInitializeBuffer(&buffer, f); + RandomAccessFile file(filename, HEIGHTMAP_DIR); - if (!BmpReadHeader(&buffer, info, data)) { + if (!BmpReadHeader(file, info, data)) { ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_BMPMAP_IMAGE_TYPE, WL_ERROR); - fclose(f); return false; } if (!IsValidHeightmapDimension(info.width, info.height)) { ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_HEIGHTMAP_TOO_LARGE, WL_ERROR); - fclose(f); return false; } if (map != nullptr) { - if (!BmpReadBitmap(&buffer, info, data)) { + if (!BmpReadBitmap(file, info, data)) { ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_BMPMAP_IMAGE_TYPE, WL_ERROR); - fclose(f); return false; } @@ -302,7 +296,6 @@ static bool ReadHeightmapBMP(const char *filename, uint *x, uint *y, std::vector *x = info.width; *y = info.height; - fclose(f); return true; }