Update to 15.0-beta1

This commit is contained in:
dP
2024-12-25 20:34:06 +05:00
parent 46dc456049
commit a86fd7c621
963 changed files with 38064 additions and 33792 deletions
+122 -206
View File
@@ -8,97 +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 "core/alloc_func.hpp"
#include "core/mem_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 byte 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)
{
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);
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<size_t>(info.width)];
while (x < info.width) {
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;
}
@@ -107,27 +42,24 @@ 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)
{
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);
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<size_t>(info.width)];
while (x < info.width) {
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) {
if (x < info.width) {
*pixel_row++ = GB(b, 0, 4);
x++;
}
}
/* Padding for 32 bit align */
SkipBytes(buffer, pad);
file.SkipBytes(pad);
}
return true;
}
@@ -136,56 +68,56 @@ 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;
byte *pixel = &data->bitmap[y * info->width];
while (y != 0 || x < info->width) {
if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
uint y = info.height - 1;
uint8_t *pixel = &data.bitmap[y * static_cast<size_t>(info.width)];
while (y != 0 || x < info.width) {
if (file.AtEndOfFile()) return false; // the file is shorter than expected
byte n = ReadByte(buffer);
byte c = ReadByte(buffer);
uint8_t n = file.ReadByte();
uint8_t c = file.ReadByte();
if (n == 0) {
switch (c) {
case 0: // end of line
x = 0;
if (y == 0) return false;
pixel = &data->bitmap[--y * info->width];
pixel = &data.bitmap[--y * static_cast<size_t>(info.width)];
break;
case 1: // end of bitmap
return true;
case 2: { // delta
if (EndOfBuffer(buffer)) return false;
byte dx = ReadByte(buffer);
byte 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;
if (x + dx >= info.width || x + dx < x || dy > y) return false;
x += dx;
y -= dy;
pixel = &data->bitmap[y * info->width + x];
pixel = &data.bitmap[y * info.width + x];
break;
}
default: { // uncompressed
uint i = 0;
while (i++ < c) {
if (EndOfBuffer(buffer) || x >= info->width) return false;
byte 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) {
if (x >= info->width) return false;
if (x >= info.width) return false;
*pixel++ = GB(b, 0, 4);
x++;
}
}
/* Padding for 16 bit align */
SkipBytes(buffer, ((c + 1) / 2) % 2);
file.SkipBytes(((c + 1) / 2) % 2);
break;
}
}
@@ -194,10 +126,10 @@ static inline bool BmpRead4Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
* pixels to be written is higher than the remaining line width.
* Ignore the superfluous pixels instead of reporting an error. */
uint i = 0;
while (x < info->width && i++ < n) {
while (x < info.width && i++ < n) {
*pixel++ = GB(c, 4, 4);
x++;
if (x < info->width && i++ < n) {
if (x < info.width && i++ < n) {
*pixel++ = GB(c, 0, 4);
x++;
}
@@ -210,18 +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)
{
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);
uint8_t pad = GB(4 - info.width, 0, 2);
for (uint y = info.height; y > 0; y--) {
if (file.AtEndOfFile()) return false; // the file is shorter than expected
uint8_t *pixel = &data.bitmap[(y - 1) * static_cast<size_t>(info.width)];
for (uint i = 0; i < info.width; i++) *pixel++ = file.ReadByte();
/* Padding for 32 bit align */
SkipBytes(buffer, pad);
file.SkipBytes(pad);
}
return true;
}
@@ -229,49 +158,49 @@ 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;
byte *pixel = &data->bitmap[y * info->width];
while (y != 0 || x < info->width) {
if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
uint y = info.height - 1;
uint8_t *pixel = &data.bitmap[y * static_cast<size_t>(info.width)];
while (y != 0 || x < info.width) {
if (file.AtEndOfFile()) return false; // the file is shorter than expected
byte n = ReadByte(buffer);
byte c = ReadByte(buffer);
uint8_t n = file.ReadByte();
uint8_t c = file.ReadByte();
if (n == 0) {
switch (c) {
case 0: // end of line
x = 0;
if (y == 0) return false;
pixel = &data->bitmap[--y * info->width];
pixel = &data.bitmap[--y * static_cast<size_t>(info.width)];
break;
case 1: // end of bitmap
return true;
case 2: { // delta
if (EndOfBuffer(buffer)) return false;
byte dx = ReadByte(buffer);
byte 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;
if (x + dx >= info.width || x + dx < x || dy > y) return false;
x += dx;
y -= dy;
pixel = &data->bitmap[y * info->width + x];
pixel = &data.bitmap[y * static_cast<size_t>(info.width) + x];
break;
}
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;
}
}
@@ -279,7 +208,7 @@ static inline bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
/* Apparently it is common to encounter BMPs where the count of
* pixels to be written is higher than the remaining line width.
* Ignore the superfluous pixels instead of reporting an error. */
for (uint i = 0; x < info->width && i < n; i++) {
for (uint i = 0; x < info.width && i < n; i++) {
*pixel++ = c;
x++;
}
@@ -291,22 +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)
{
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
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<size_t>(info.width) * 3];
for (uint x = 0; x < info.width; ++x) {
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;
}
@@ -314,109 +241,98 @@ 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)
{
uint32_t header_size;
assert(info != nullptr);
MemSetT(info, 0);
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 */
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
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);
if (info.os2_bmp) {
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);
if (info->bpp != 1 && info->bpp != 4 && info->bpp != 8 && info->bpp != 24) {
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;
}
/* Reads compression method if available in info header*/
if ((header_size -= 4) >= 4) {
info->compression = ReadDword(buffer);
info.compression = file.ReadDword();
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;
if (info.compression > 2 || (info.compression > 0 && !(info.bpp == 4 || info.bpp == 8))) return false;
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;
if (info->palette_size == 0) info->palette_size = maximum_palette_size;
uint maximum_palette_size = 1U << info.bpp;
if (info.palette_size == 0) info.palette_size = maximum_palette_size;
/* More palette colours than palette indices is not supported. */
if (info->palette_size > maximum_palette_size) return false;
if (info.palette_size > maximum_palette_size) return false;
data->palette = CallocT<Colour>(info->palette_size);
data.palette.resize(info.palette_size);
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
for (auto &colour : data.palette) {
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)
{
assert(info != nullptr && data != nullptr);
data->bitmap = CallocT<byte>(static_cast<size_t>(info->width) * info->height * ((info->bpp == 24) ? 3 : 1));
data.bitmap.resize(static_cast<size_t>(info.width) * info.height * ((info.bpp == 24) ? 3 : 1));
/* 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);
file.SeekTo(info.offset, SEEK_SET);
switch (info.compression) {
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();
}
}
void BmpDestroyData(BmpData *data)
{
assert(data != nullptr);
free(data->palette);
free(data->bitmap);
}