mirror of
https://github.com/Karaka-Management/cOMS.git
synced 2026-01-10 19:08:39 +00:00
335 lines
12 KiB
C
Executable File
335 lines
12 KiB
C
Executable File
/**
|
|
* Jingga
|
|
*
|
|
* @copyright Jingga
|
|
* @license OMS License 2.0
|
|
* @version 1.0.0
|
|
* @link https://jingga.app
|
|
*/
|
|
#ifndef COMS_IMAGE_BITMAP_H
|
|
#define COMS_IMAGE_BITMAP_H
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "../stdlib/Types.h"
|
|
#include "../utils/Utils.h"
|
|
#include "../utils/EndianUtils.h"
|
|
#include "Image.h"
|
|
|
|
// See: https://en.wikipedia.org/wiki/BMP_file_format
|
|
// IMPORTANT: Remember that we are not using packing for the headers
|
|
// Because of that the struct size is different from the actual header size in the file
|
|
// This means we have to manually asign the data to the headers
|
|
|
|
#define BITMAP_HEADER_SIZE 14
|
|
struct BitmapHeader {
|
|
byte identifier[2]; // 2 bytes - bitmap identifier
|
|
uint32 size; // 4 bytes - size in bytes
|
|
byte app_data[4]; // 4 bytes - generated by the image software
|
|
uint32 offset; // 4 bytes - defines starting address for pixels
|
|
};
|
|
|
|
#define DIB_BITMAP_TYPE_BITMAPCOREHEADER 12
|
|
struct DIB_BITMAPCOREHEADER {
|
|
uint32 size;
|
|
uint16 width;
|
|
uint16 height;
|
|
uint16 color_planes;
|
|
uint16 bits_per_pixel;
|
|
};
|
|
|
|
#define DIB_BITMAP_TYPE_OS21XBITMAPHEADER DIB_BITMAP_TYPE_BITMAPCOREHEADER
|
|
#define DIB_OS21XBITMAPHEADER DIB_BITMAPCOREHEADER
|
|
|
|
#define DIB_BITMAP_TYPE_BITMAPINFOHEADER 40
|
|
struct DIB_BITMAPINFOHEADER {
|
|
uint32 size;
|
|
uint32 width;
|
|
uint32 height;
|
|
uint16 color_planes;
|
|
uint16 bits_per_pixel;
|
|
uint32 compression_method;
|
|
uint32 raw_image_size;
|
|
int32 horizontal_ppm;
|
|
int32 vertical_ppm;
|
|
uint32 color_palette;
|
|
uint32 important_colors;
|
|
};
|
|
|
|
#define DIB_BITMAP_TYPE_OS22XBITMAPHEADER 64
|
|
// OR BITMAPINFOHEADER2
|
|
struct DIB_OS22XBITMAPHEADER {
|
|
uint32 size;
|
|
uint32 width;
|
|
uint32 height;
|
|
uint16 color_planes;
|
|
uint16 bits_per_pixel;
|
|
uint32 compression_method;
|
|
uint32 raw_image_size;
|
|
int32 horizontal_ppm;
|
|
int32 vertical_ppm;
|
|
uint32 color_palette;
|
|
uint32 important_colors;
|
|
|
|
uint16 units;
|
|
uint16 padding;
|
|
uint16 bit_direction;
|
|
uint16 halftoning_algorithm;
|
|
int32 halftoning_parameter_1;
|
|
int32 halftoning_parameter_2;
|
|
int32 color_encoding;
|
|
int32 application_identifier;
|
|
};
|
|
|
|
#define DIB_BITMAPINFOHEADER_COMPRESSION_BI_RGB 0x0000
|
|
#define DIB_BITMAPINFOHEADER_COMPRESSION_BI_RLE8 0x0001
|
|
#define DIB_BITMAPINFOHEADER_COMPRESSION_BI_RLE4 0x0002
|
|
#define DIB_BITMAPINFOHEADER_COMPRESSION_BI_BITFIELDS 0x0003
|
|
#define DIB_BITMAPINFOHEADER_COMPRESSION_BI_JPEG 0x0004
|
|
#define DIB_BITMAPINFOHEADER_COMPRESSION_BI_PNG 0x0005
|
|
#define DIB_BITMAPINFOHEADER_COMPRESSION_BI_ALPHABITFIELDS 0x0006
|
|
#define DIB_BITMAPINFOHEADER_COMPRESSION_BI_CMYK 0x000B
|
|
#define DIB_BITMAPINFOHEADER_COMPRESSION_BI_CMYKRLE8 0x000C
|
|
#define DIB_BITMAPINFOHEADER_COMPRESSION_BI_CMYKRLE4 0x000D
|
|
|
|
#define DIB_BITMAPINFOHEADER_HALFTONING_NONE 0
|
|
#define DIB_BITMAPINFOHEADER_HALFTONING_ERROR_DIFFUSION 1
|
|
#define DIB_BITMAPINFOHEADER_HALFTONING_PANDA 2
|
|
#define DIB_BITMAPINFOHEADER_HALFTONING_SUPER_CIRCLE 3
|
|
|
|
#define DIB_BITMAP_TYPE_BITMAPV2INFOHEADER 52
|
|
struct DIB_BITMAPV2INFOHEADER {
|
|
|
|
};
|
|
|
|
#define DIB_BITMAP_TYPE_BITMAPV3INFOHEADER 56
|
|
struct DIB_BITMAPV3INFOHEADER {
|
|
|
|
};
|
|
|
|
struct COMS_CIEXYZ {
|
|
int32 ciexyzX;
|
|
int32 ciexyzY;
|
|
int32 ciexyzZ;
|
|
};
|
|
|
|
struct COMS_CIEXYZTRIPLE {
|
|
COMS_CIEXYZ ciexyzRed;
|
|
COMS_CIEXYZ ciexyzGreen;
|
|
COMS_CIEXYZ ciexyzBlue;
|
|
};
|
|
|
|
#define DIB_BITMAP_TYPE_BITMAPV4HEADER 108
|
|
struct DIB_BITMAPV4HEADER {
|
|
int32 size;
|
|
uint32 width;
|
|
uint32 height;
|
|
uint16 color_planes;
|
|
uint16 bits_per_pixel;
|
|
int32 compression_method;
|
|
int32 raw_image_size;
|
|
int32 horizontal_ppm;
|
|
int32 vertical_ppm;
|
|
uint32 color_palette;
|
|
uint32 important_colors;
|
|
|
|
int32 bV4RedMask;
|
|
int32 bV4GreenMask;
|
|
int32 bV4BlueMask;
|
|
int32 bV4AlphaMask;
|
|
int32 bV4CSType;
|
|
COMS_CIEXYZTRIPLE bV4Endpoints;
|
|
int32 bV4GammaRed;
|
|
int32 bV4GammaGreen;
|
|
int32 bV4GammaBlue;
|
|
};
|
|
|
|
#define DIB_BITMAP_TYPE_BITMAPV5HEADER 124
|
|
struct DIB_BITMAPV5HEADER {
|
|
int32 size;
|
|
uint32 width;
|
|
uint32 height;
|
|
uint16 color_planes;
|
|
uint16 bits_per_pixel;
|
|
int32 compression_method;
|
|
int32 raw_image_size;
|
|
int32 horizontal_ppm;
|
|
int32 vertical_ppm;
|
|
uint32 color_palette;
|
|
uint32 important_colors;
|
|
|
|
int32 bV5RedMask;
|
|
int32 bV5GreenMask;
|
|
int32 bV5BlueMask;
|
|
int32 bV5AlphaMask;
|
|
int32 bV5CSType;
|
|
COMS_CIEXYZTRIPLE bV5Endpoints;
|
|
int32 bV5GammaRed;
|
|
int32 bV5GammaGreen;
|
|
int32 bV5GammaBlue;
|
|
int32 bV5Intent;
|
|
int32 bV5ProfileData;
|
|
int32 bV5ProfileSize;
|
|
int32 bV5Reserved;
|
|
};
|
|
|
|
struct Bitmap {
|
|
BitmapHeader header;
|
|
uint32 dib_header_type;
|
|
DIB_BITMAPINFOHEADER dib_header; // Despite the different header types we use this one for simplicity
|
|
uint32* extra_bit_mask; // 3-4 = 12-16 bytes
|
|
byte color_table_size;
|
|
byte* color_table;
|
|
|
|
// Structure:
|
|
// 1. pixels stored in rows
|
|
// 2. rows are padded in multiples of 4 bytes
|
|
// 3. rows start from the bottom (unless the height is negative)
|
|
// 4. pixel data is stored in ABGR (graphics libraries usually need BGRA or RGBA)
|
|
byte* pixels; // WARNING: This is not the owner of the data. The owner is the FileBody
|
|
|
|
uint32 size;
|
|
byte* data; // WARNING: This is not the owner of the data. The owner is the FileBody
|
|
};
|
|
|
|
void generate_default_bitmap_references(const FileBody* file, Bitmap* bitmap) noexcept
|
|
{
|
|
bitmap->size = (uint32) file->size;
|
|
bitmap->data = file->content;
|
|
|
|
ASSERT_SIMPLE(bitmap->size >= BITMAP_HEADER_SIZE);
|
|
|
|
// Fill header
|
|
bitmap->header.identifier[0] = *(file->content + 0);
|
|
bitmap->header.identifier[1] = *(file->content + 1);
|
|
|
|
bitmap->header.size = SWAP_ENDIAN_LITTLE(*((uint32 *) (file->content + 2)));
|
|
bitmap->header.app_data[0] = *(file->content + 6);
|
|
bitmap->header.app_data[1] = *(file->content + 7);
|
|
bitmap->header.app_data[2] = *(file->content + 8);
|
|
bitmap->header.app_data[3] = *(file->content + 9);
|
|
bitmap->header.offset = SWAP_ENDIAN_LITTLE(*((uint32 *) (file->content + 10)));
|
|
|
|
byte* dib_header_offset = file->content + BITMAP_HEADER_SIZE;
|
|
bitmap->dib_header_type = SWAP_ENDIAN_LITTLE(*((uint32 *) dib_header_offset));
|
|
|
|
byte* color_table_offset = file->content + BITMAP_HEADER_SIZE + bitmap->dib_header_type;
|
|
|
|
// Fill DIB header
|
|
switch (bitmap->dib_header_type) {
|
|
case DIB_BITMAP_TYPE_BITMAPCOREHEADER: {
|
|
bitmap->dib_header.size = SWAP_ENDIAN_LITTLE(*((uint32 *) (dib_header_offset)));
|
|
bitmap->dib_header.width = SWAP_ENDIAN_LITTLE(*((uint16 *) (dib_header_offset + 4)));
|
|
bitmap->dib_header.height = SWAP_ENDIAN_LITTLE(*((uint16 *) (dib_header_offset + 6)));
|
|
bitmap->dib_header.color_planes = SWAP_ENDIAN_LITTLE(*((uint16 *) (dib_header_offset + 8)));
|
|
bitmap->dib_header.bits_per_pixel = SWAP_ENDIAN_LITTLE(*((uint16 *) (dib_header_offset + 10)));
|
|
bitmap->dib_header.color_palette = 1U << bitmap->dib_header.bits_per_pixel;
|
|
} break;
|
|
case DIB_BITMAP_TYPE_BITMAPV5HEADER: [[fallthrough]];
|
|
case DIB_BITMAP_TYPE_BITMAPV4HEADER: [[fallthrough]];
|
|
case DIB_BITMAP_TYPE_BITMAPV3INFOHEADER: [[fallthrough]];
|
|
case DIB_BITMAP_TYPE_BITMAPV2INFOHEADER: [[fallthrough]];
|
|
case DIB_BITMAP_TYPE_OS22XBITMAPHEADER: [[fallthrough]];
|
|
case DIB_BITMAP_TYPE_BITMAPINFOHEADER: {
|
|
bitmap->dib_header.size = SWAP_ENDIAN_LITTLE(*((uint32 *) (dib_header_offset)));
|
|
bitmap->dib_header.width = SWAP_ENDIAN_LITTLE(*((int32 *) (dib_header_offset + 4)));
|
|
bitmap->dib_header.height = SWAP_ENDIAN_LITTLE(*((int32 *) (dib_header_offset + 8)));
|
|
bitmap->dib_header.color_planes = SWAP_ENDIAN_LITTLE(*((uint16 *) (dib_header_offset + 12)));
|
|
bitmap->dib_header.bits_per_pixel = SWAP_ENDIAN_LITTLE(*((uint16 *) (dib_header_offset + 14)));
|
|
bitmap->dib_header.compression_method = SWAP_ENDIAN_LITTLE(*((uint32 *) (dib_header_offset + 16)));
|
|
bitmap->dib_header.raw_image_size = SWAP_ENDIAN_LITTLE(*((uint32 *) (dib_header_offset + 20)));
|
|
bitmap->dib_header.horizontal_ppm = SWAP_ENDIAN_LITTLE(*((uint32 *) (dib_header_offset + 24)));
|
|
bitmap->dib_header.vertical_ppm = SWAP_ENDIAN_LITTLE(*((uint32 *) (dib_header_offset + 28)));
|
|
bitmap->dib_header.color_palette = SWAP_ENDIAN_LITTLE(*((uint32 *) (dib_header_offset + 32)));
|
|
bitmap->dib_header.important_colors = SWAP_ENDIAN_LITTLE(*((uint32 *) (dib_header_offset + 36)));
|
|
|
|
if (bitmap->dib_header.compression_method == DIB_BITMAPINFOHEADER_COMPRESSION_BI_BITFIELDS) {
|
|
// 12 bytes
|
|
bitmap->extra_bit_mask = (uint32 *) (dib_header_offset + DIB_BITMAP_TYPE_BITMAPINFOHEADER);
|
|
color_table_offset += 12;
|
|
} else if (bitmap->dib_header.compression_method == DIB_BITMAPINFOHEADER_COMPRESSION_BI_ALPHABITFIELDS) {
|
|
// 16 bytes
|
|
bitmap->extra_bit_mask = (uint32 *) (dib_header_offset + DIB_BITMAP_TYPE_BITMAPINFOHEADER);
|
|
color_table_offset += 16;
|
|
}
|
|
} break;
|
|
default: {
|
|
|
|
}
|
|
}
|
|
|
|
// Fill other
|
|
bitmap->color_table = color_table_offset;
|
|
bitmap->pixels = (byte *) (file->content + bitmap->header.offset);
|
|
}
|
|
|
|
void image_bmp_generate(const FileBody* src_data, Image* image) noexcept
|
|
{
|
|
// @performance We are generating the struct and then filling the data.
|
|
// There is some assignment/copy overhead
|
|
Bitmap src = {};
|
|
generate_default_bitmap_references(src_data, &src);
|
|
|
|
image->width = src.dib_header.width;
|
|
image->height = src.dib_header.height;
|
|
image->pixel_count = image->width * image->height;
|
|
|
|
// rows are 4 bytes multiples in length
|
|
uint32 width = ROUND_TO_NEAREST(src.dib_header.width, 4);
|
|
|
|
uint32 pixel_bytes = src.dib_header.bits_per_pixel / 8;
|
|
byte alpha_offset = pixel_bytes > 3;
|
|
|
|
image->image_settings |= (image->image_settings & IMAGE_SETTING_CHANNEL_COUNT) == 0
|
|
? pixel_bytes
|
|
: image->image_settings & IMAGE_SETTING_CHANNEL_COUNT;
|
|
|
|
if ((image->image_settings & IMAGE_SETTING_PIXEL_BGR)
|
|
&& (image->image_settings & IMAGE_SETTING_BOTTOM_TO_TOP)
|
|
) {
|
|
// @bug This doesn't consider the situation where we want alpha as a setting but the img doesn't have it
|
|
// @bug This also copies possible padding which will corrupt the image
|
|
memcpy((void *) image->pixels, src.pixels, image->pixel_count * pixel_bytes);
|
|
|
|
return;
|
|
}
|
|
|
|
uint32 pixel_rgb_bytes = pixel_bytes - alpha_offset;
|
|
uint32 width_pixel_bytes = width * pixel_bytes;
|
|
|
|
for (uint32 y = 0; y < src.dib_header.height; ++y) {
|
|
uint32 row_pos1 = y * width_pixel_bytes;
|
|
uint32 row_pos2 = image->image_settings & IMAGE_SETTING_BOTTOM_TO_TOP
|
|
? y * width_pixel_bytes
|
|
: (src.dib_header.height - y - 1) * width_pixel_bytes;
|
|
|
|
for (uint32 x = 0; x < width; ++x) {
|
|
if (x >= image->width) {
|
|
// Bitmaps may have padding at the end of the row
|
|
// We don't care about that
|
|
continue;
|
|
}
|
|
|
|
// Invert byte order
|
|
if (image->image_settings & IMAGE_SETTING_PIXEL_BGR) {
|
|
for (uint32 i = 0; i < pixel_rgb_bytes; ++i) {
|
|
image->pixels[row_pos1 + x * pixel_bytes + i] = src.pixels[row_pos2 + x * pixel_bytes + i];
|
|
}
|
|
} else {
|
|
for (uint32 i = 0; i < pixel_rgb_bytes; ++i) {
|
|
image->pixels[row_pos1 + x * pixel_bytes + i] = src.pixels[row_pos2 + x * pixel_bytes + pixel_rgb_bytes - i];
|
|
}
|
|
}
|
|
|
|
// Add alpha channel at end of every RGB value (either the image already contains it or we have to add it manually)
|
|
if (alpha_offset > 0) {
|
|
image->pixels[row_pos1 + x * pixel_bytes + 3] = src.pixels[row_pos2 + x * pixel_bytes + pixel_bytes + 3];
|
|
} else if ((image->image_settings & IMAGE_SETTING_CHANNEL_COUNT) == 4) {
|
|
image->pixels[row_pos1 + x * pixel_bytes + 3] = 0xFF;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif |