#include<lexlib/defines.h>
#include<internal/misc.h>
#include<lexlib/image.h>
#include<internal/bmp.h>
#include<lexlib/cfile.h>
#include<lexlib/math.h>
#include<stdbool.h>
#include<limits.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
typedef struct ColorBitMask {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
uint32_t rb;
uint32_t gb;
uint32_t bb;
uint32_t ab;
uint8_t rs;
uint8_t gs;
uint8_t bs;
uint8_t as;
} ColorBitMask;
typedef union MasterHeader {
BmpCoreHeader core;
BmpInfoHeader info;
BmpInfoHeader3 info3;
BmpInfoHeader4 info4;
BmpInfoHeader5 info5;
} MasterHeader;
struct LexlibBmpContext {
struct LexlibImage *image;
size_t offset;
uint32_t width;
uint32_t height;
uint32_t stride;
uint32_t padding;
uint8_t pixelSize;
uint8_t bitdepth;
uint8_t profile;
bool reverse;
struct ColorBitMask bitMask;
struct LexlibColorF colorMax;
struct {
uint32_t *data;
uint32_t len;
uint32_t offset;
uint8_t size;
} pallette;
};
LEXLIB_INLINE unsigned bitGet(unsigned x, unsigned index);
static void ctxInit(struct LexlibBmpContext *ctx, struct LexlibImage *image);
static void ctxDestroy(struct LexlibBmpContext *ctx);
static void ctxMakeBitMask(struct LexlibBmpContext *ctx, const uint32_t *data);
static uint8_t processHead(struct LexlibBmpContext *ctx, const uint8_t *data, size_t len);
static void processPixel(struct LexlibBmpContext *ctx, const uint8_t *data, uint32_t x, uint32_t y);
static uint8_t postProcess(struct LexlibBmpContext *ctx);
#define LEXLIB_RGB_BITFIELDS 0x0A
#define LEXLIB_RGBA_BITFIELDS 0x0B
#define LEXLIB_RGB_PALETTE 0x0C
#define LEXLIB_1BPP 0x0D
#define COLORBITMASK_BGRA_8888 ((ColorBitMask){8, 8, 8, 8, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, 16, 8, 0, 24})
#define COLORBITMASK_BGRA_555 ((ColorBitMask){5, 6, 5, 8, 0x0000FC00, 0x000003E0, 0x0000001F, 0x00000000, 16, 8, 0, 00})
#define COLORBITMASK_BGRA_565 ((ColorBitMask){5, 6, 5, 8, 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000, 16, 8, 0, 00})
uint8_t lexlibImageLoadBmp(struct LexlibImage *image, const char *filename){
FILE *file = fopen(filename, "rb");
if(!file)
return LEXLIB_CANT_OPEN;
struct LexlibBmpContext ctx;
ctxInit(&ctx, image);
uint8_t buf[128] = {0};
uint8_t err = processHead(&ctx, buf, fread(buf, 1, 128, file));
if(err)
return err;
if(ctx.pallette.data){
if(fseek(file, ctx.pallette.offset, SEEK_SET))
return LEXLIB_CANT_READ;
if(fread(ctx.pallette.data, ctx.pallette.size, ctx.pallette.len, file) != ctx.pallette.len){
fclose(file);
return LEXLIB_CANT_READ;
}
}
if(fseek(file, ctx.offset, SEEK_SET))
return LEXLIB_CANT_READ;
if(lexlibImageNew(ctx.image, ctx.width, ctx.height, ctx.image->profile, 8))
return LEXLIB_OUT_OF_MEMORY;
for(uint32_t y = 0; y < image->height; y++){
for(uint32_t x = 0; x < image->width; x++){
if(!fread(buf, ctx.pixelSize, 1, file)){
fclose(file);
postProcess(&ctx);
ctxDestroy(&ctx);
return LEXLIB_PARTIAL_READ;
}
processPixel(&ctx, buf, x, y);
}
if(ctx.padding)
if(!fread(buf, ctx.padding, 1, file)){
fclose(file);
postProcess(&ctx);
ctxDestroy(&ctx);
return LEXLIB_PARTIAL_READ;
}
}
fclose(file);
err = postProcess(&ctx);
ctxDestroy(&ctx);
if(err){
lexlibImageDelete(image);
return err;
}
return LEXLIB_OK;
}
uint8_t lexlibImageLoadBmpMem(struct LexlibImage *image, const void *mem, LEXLIB_UNUSED size_t size){
const uint8_t *data = mem;
struct LexlibBmpContext ctx;
ctxInit(&ctx, image);
uint8_t err = processHead(&ctx, data, 14+56);
if(err)
return err;
if(ctx.pallette.data)
memcpy(ctx.pallette.data, data + ctx.pallette.offset, ctx.pallette.len * ctx.pallette.size);
if(lexlibImageNew(ctx.image, ctx.width, ctx.height, ctx.image->profile, 8))
return LEXLIB_OUT_OF_MEMORY;
size_t offset = ctx.offset;
for(uint32_t y = 0; y < image->height; y++){
for(uint32_t x = 0; x < image->width; x++){
if(offset >= size){
ctxDestroy(&ctx);
postProcess(&ctx);
return LEXLIB_PARTIAL_READ;
}
processPixel(&ctx, data+offset, x, y);
offset += ctx.pixelSize;
}
offset += ctx.padding;
}
err = postProcess(&ctx);
ctxDestroy(&ctx);
if(err){
lexlibImageDelete(ctx.image);
return err;
}
return LEXLIB_OK;
}
uint8_t lexlibImageSaveBmp(const struct LexlibImage *image, const char *filename){
uint8_t bmpHeader;
if(lexlibImageValidate(image))
return LEXLIB_INVALID_VALUE;
switch(image->profile){
case LEXLIB_GRAY:
case LEXLIB_RGB:
bmpHeader = LEXLIB_BMP_INFO;
break;
case LEXLIB_GRAYA:
case LEXLIB_RGBA:
bmpHeader = LEXLIB_BMP_INFO_V3;
break;
default:
bmpHeader = 0;
break;
}
return lexlibImageSaveBmpEx(image, filename, image->profile, bmpHeader);
}
uint8_t lexlibImageSaveBmpEx(const struct LexlibImage *image, const char *filename, uint8_t profile, uint8_t bmpheader){
BmpFileHeader fileHeader = {0};
MasterHeader header = {0};
uint8_t bitMaskCnt = 0;
uint32_t bitMask[4] = {0};
uint32_t pixelSize = 0;
uint32_t rowStride = 0;
uint32_t rowPadding = 0;
fileHeader.type = 0x4D42;
fileHeader.size = sizeof(BmpFileHeader);
fileHeader.reserved1 = 0;
fileHeader.reserved2 = 0;
switch(profile){
case LEXLIB_NONE:
profile = image->profile;
break;
case LEXLIB_GRAY:
case LEXLIB_GRAYA:
case LEXLIB_RGB:
case LEXLIB_RGBA:
case LEXLIB_RGB555:
case LEXLIB_RGB565:
break;
default:
return LEXLIB_INVALID_ARG;
}
switch(bmpheader){
case LEXLIB_NONE:
bmpheader = LEXLIB_BMP_INFO;
break;
case LEXLIB_BMP_INFO:
case LEXLIB_BMP_INFO_V3:
break;
case LEXLIB_BMP_CORE:
return LEXLIB_UNSUPORTED;
default:
return LEXLIB_INVALID_ARG;
}
if(bmpheader == LEXLIB_BMP_INFO){
header.info.size = sizeof(BmpInfoHeader);
header.info.width = image->width;
header.info.height = image->height;
header.info.planes = 1;
header.info.xresm = 2835;
header.info.yresm = 2835;
header.info.colorPalette = 0;
header.info.colorImportant = 0;
switch(profile){
case LEXLIB_RGB555:
header.info.bitdepth = 16;
header.info.compression = BMP_RGB;
pixelSize = 2;
break;
case LEXLIB_RGB565:
header.info.bitdepth = 16;
header.info.compression = BMP_BITFIELDS;
pixelSize = 2;
bitMaskCnt = 3;
bitMask[0] = 0x0000F800;
bitMask[1] = 0x000007E0;
bitMask[2] = 0x0000001F;
break;
case LEXLIB_GRAY:
case LEXLIB_RGB:
header.info.bitdepth = 24;
header.info.compression = BMP_RGB;
pixelSize = 3;
profile = LEXLIB_RGB;
break;
case LEXLIB_GRAYA:
case LEXLIB_RGBA:
header.info.bitdepth = 32;
header.info.compression = BMP_RGB;
pixelSize = 4;
profile = LEXLIB_RGBA;
break;
default:
return LEXLIB_UNSUPORTED;
}
}
if(bmpheader == LEXLIB_BMP_INFO_V3){
header.info3.size = sizeof(BmpInfoHeader3);
header.info3.width = image->width;
header.info3.height = image->height;
header.info3.planes = 1;
header.info3.xresm = 2835;
header.info3.yresm = 2835;
header.info3.colorPalette = 0;
header.info3.colorImportant = 0;
switch(profile){
case LEXLIB_RGB555:
header.info3.bitdepth = 16;
header.info3.compression = BMP_BITFIELDS;
pixelSize = 2;
header.info3.redbitmask = 0x00007C00;
header.info3.greenbitmask = 0x000003E0;
header.info3.bluebitmask = 0x0000001F;
header.info3.alphabitmask = 0x00000000;
break;
case LEXLIB_RGB565:
header.info3.bitdepth = 16;
header.info3.compression = BMP_BITFIELDS;
pixelSize = 2;
header.info3.redbitmask = 0x0000F800;
header.info3.greenbitmask = 0x000007E0;
header.info3.bluebitmask = 0x0000001F;
header.info3.alphabitmask = 0x00000000;
break;
case LEXLIB_GRAY:
case LEXLIB_RGB:
header.info3.bitdepth = 24;
header.info3.compression = BMP_BITFIELDS;
pixelSize = 3;
profile = LEXLIB_RGB;
header.info3.redbitmask = 0x00FF0000;
header.info3.greenbitmask = 0x0000FF00;
header.info3.bluebitmask = 0x000000FF;
header.info3.alphabitmask = 0x00000000;
break;
case LEXLIB_GRAYA:
case LEXLIB_RGBA:
header.info3.bitdepth = 32;
header.info3.compression = BMP_BITFIELDS;
pixelSize = 4;
profile = LEXLIB_RGBA;
header.info3.redbitmask = 0x00FF0000;
header.info3.greenbitmask = 0x0000FF00;
header.info3.bluebitmask = 0x000000FF;
header.info3.alphabitmask = 0xFF000000;
break;
default:
return LEXLIB_UNSUPORTED;
}
}
rowStride = ((((header.info.width * header.info.bitdepth) + 31) & ~31) >> 3);
rowPadding = (rowStride) - ceil(header.info.width * (header.info.bitdepth / 8.0f));
header.info.imagesize = image->width * image->height * image->bpp / 8;
fileHeader.size += header.info.imagesize;
fileHeader.size += header.core.size;
fileHeader.size += bitMaskCnt * sizeof(uint32_t);
fileHeader.offset = sizeof(BmpFileHeader) + header.core.size + (bitMaskCnt * sizeof(uint32_t));
FILE *file = fopen(filename, "wb");
if(!file)
return LEXLIB_CANT_WRITE;
if(fwrite(&fileHeader, sizeof(BmpFileHeader), 1, file) != 1){
fclose(file);
return LEXLIB_PARTIAL_WRITE;
}
if(fwrite(&header, header.core.size, 1, file) != 1){
fclose(file);
return LEXLIB_PARTIAL_WRITE;
}
if(fwrite(bitMask, sizeof(uint32_t), bitMaskCnt, file) != bitMaskCnt){
fclose(file);
return LEXLIB_PARTIAL_WRITE;
}
if(profile == LEXLIB_RGB){
uint32_t y = image->height;
do {
y--;
for(uint32_t x = 0; x < image->width; x++){
struct LexlibColor imgPixel;
lexlibImagePixel(image, x, y, &imgPixel);
imgPixel = lexlibColorPremultiply(imgPixel);
struct LexlibColor pixel = {
.r = imgPixel.b,
.g = imgPixel.g,
.b = imgPixel.r,
.a = imgPixel.a
};
if(fwrite(&pixel, pixelSize, 1, file) != 1){
fclose(file);
return LEXLIB_PARTIAL_WRITE;
}
}
for(uint8_t pad = 0; pad < rowPadding; pad++)
fputc(0, file);
} while(y != 0);}
if(profile == LEXLIB_RGBA){
uint32_t y = image->height;
do {
y--;
for(uint32_t x = 0; x < image->width; x++){
struct LexlibColor imgPixel;
lexlibImagePixel(image, x, y, &imgPixel);
struct LexlibColor pixel = {
.r = imgPixel.b,
.g = imgPixel.g,
.b = imgPixel.r,
.a = imgPixel.a
};
if(fwrite(&pixel, pixelSize, 1, file) != 1){
fclose(file);
return LEXLIB_PARTIAL_WRITE;
}
}
for(uint8_t pad = 0; pad < rowPadding; pad++)
fputc(0, file);
} while(y != 0);}
if(profile == LEXLIB_RGB555){
uint32_t y = image->height;
do {
y--;
for(uint32_t x = 0; x < image->width; x++){
struct LexlibColor imgPixel;
lexlibImagePixel(image, x, y, &imgPixel);
imgPixel = lexlibColorPremultiply(imgPixel);
uint16_t pixel = 0x0000;
pixel |= (uint16_t)rintf((imgPixel.r / 255.0f) * 31.0f) << 10;
pixel |= (uint16_t)rintf((imgPixel.g / 255.0f) * 31.0f) << 5;
pixel |= (uint16_t)rintf((imgPixel.b / 255.0f) * 31.0f);
if(fwrite(&pixel, pixelSize, 1, file) != 1){
fclose(file);
return LEXLIB_PARTIAL_WRITE;
}
}
for(uint8_t pad = 0; pad < rowPadding; pad++)
fputc(0, file);
} while(y != 0);}
if(profile == LEXLIB_RGB565){
uint32_t y = image->height;
do {
y--;
for(uint32_t x = 0; x < image->width; x++){
struct LexlibColor imgPixel;
lexlibImagePixel(image, x, y, &imgPixel);
imgPixel = lexlibColorPremultiply(imgPixel);
uint16_t pixel = 0x0000;
pixel |= (uint16_t)rintf((imgPixel.r / 255.0f) * 31.0f) << 11;
pixel |= (uint16_t)rintf((imgPixel.g / 255.0f) * 63.0f) << 5;
pixel |= (uint16_t)rintf((imgPixel.b / 255.0f) * 31.0f);
if(fwrite(&pixel, pixelSize, 1, file) != 1){
fclose(file);
return LEXLIB_PARTIAL_WRITE;
}
}
for(uint8_t pad = 0; pad < rowPadding; pad++)
fputc(0, file);
} while(y != 0);}
fflush(file);
fclose(file);
return LEXLIB_OK;
}
static uint8_t processHead(struct LexlibBmpContext *ctx, const uint8_t *data, size_t len){
const BmpFileHeader *fileHeader = (BmpFileHeader*)data;
const BmpCoreHeader *coreHeader = (BmpCoreHeader*)(data + sizeof(BmpFileHeader));
if(fileHeader->type != 0x4D42)
return LEXLIB_INVALID_TYPE;
ctx->offset = fileHeader->offset;
size_t headOffset = sizeof(BmpFileHeader) + coreHeader->size;
switch(coreHeader->size){
case sizeof(BmpCoreHeader):
case sizeof(BmpInfoHeader):
case sizeof(BmpInfoHeader3):
break;
case sizeof(BmpInfoHeader4):
case sizeof(BmpInfoHeader5):
return LEXLIB_UNSUPORTED;
default:
return LEXLIB_INVALID_DATA;
}
if(len < headOffset)
return LEXLIB_INVALID_LEN;
if(sizeof(BmpCoreHeader) == coreHeader->size){
if(
coreHeader->width <= 0 ||
coreHeader->height == 0 ||
coreHeader->bitdepth == 0
) return LEXLIB_UNSUPORTED;
ctx->width = coreHeader->width;
ctx->height = coreHeader->height;
ctx->stride = ((((coreHeader->width * coreHeader->bitdepth) + 31) & ~31) >> 3);
ctx->padding = ctx->stride - ceil(coreHeader->width * (coreHeader->bitdepth / 8.0f));
ctx->bitdepth = coreHeader->bitdepth;
ctx->reverse = false;
switch(coreHeader->bitdepth){
case 1:
ctx->image->profile = LEXLIB_RGB;
ctx->pixelSize = 1;
ctx->profile = LEXLIB_1BPP;
ctx->pallette.len = 2;
ctx->pallette.size = 3;
break;
default:
return LEXLIB_UNSUPORTED;
}
}
if(sizeof(BmpInfoHeader) == coreHeader->size){
BmpInfoHeader *header = (BmpInfoHeader*)coreHeader;
if(
header->width <= 0 ||
header->height == 0 ||
header->bitdepth == 0
) return LEXLIB_INVALID_DATA;
ctx->width = header->width;
ctx->height = abs(header->height);
ctx->stride = ((((header->width * header->bitdepth) + 31) & ~31) >> 3);
ctx->padding = ctx->stride - ceil(header->width * (header->bitdepth / 8.0f));
ctx->bitdepth = header->bitdepth;
ctx->reverse = header->height < 0;
ctx->pallette.len = header->colorPalette;
switch(header->compression){
case BMP_RGB:
break;
case BMP_BITFIELDS:
ctx->profile = LEXLIB_RGB_BITFIELDS;
ctxMakeBitMask(ctx, (uint32_t*)(data + headOffset));
headOffset += 12;
break;
case BMP_ALPHABITFIELDS:
ctx->profile = LEXLIB_RGBA_BITFIELDS;
ctxMakeBitMask(ctx, (uint32_t*)(data + headOffset));
headOffset += 16;
break;
default:
return LEXLIB_UNSUPORTED;
}
switch(header->bitdepth){
case 8:
ctx->image->profile = LEXLIB_RGB;
ctx->pixelSize = 1;
if(header->colorPalette){
ctx->profile = LEXLIB_RGB_PALETTE;
ctx->bitMask.rb = 0x000000FF;
}
if(header->compression)
return LEXLIB_UNSUPORTED;
break;
case 16:
ctx->image->profile = LEXLIB_RGB;
ctx->pixelSize = 2;
if(!header->compression){
ctx->profile = LEXLIB_RGB_BITFIELDS;
ctx->bitMask = COLORBITMASK_BGRA_555;
}
break;
case 24:
ctx->image->profile = LEXLIB_RGB;
ctx->pixelSize = 3;
if(!header->compression){
ctx->profile = LEXLIB_RGB;
}
break;
case 32:
ctx->image->profile = LEXLIB_RGBA;
ctx->pixelSize = 4;
if(!header->compression)
ctx->profile = LEXLIB_RGBA;
break;
default:
return LEXLIB_INVALID_DATA;
}
}
if(sizeof(BmpInfoHeader3) == coreHeader->size){
const BmpInfoHeader3* header = (const BmpInfoHeader3*)coreHeader;
if(
header->width <= 0 ||
header->height == 0 ||
header->bitdepth == 0
) return LEXLIB_INVALID_DATA;
ctx->width = header->width;
ctx->height = abs(header->height);
ctx->stride = ((((header->width * header->bitdepth) + 31) & ~31) >> 3);
ctx->padding = ctx->stride - ceil(header->width * (header->bitdepth / 8.0f));
ctx->bitdepth = header->bitdepth;
ctx->reverse = header->height < 0;
ctx->pallette.len = header->colorPalette;
switch(header->compression){
case BMP_RGB:
break;
case BMP_BITFIELDS:
case BMP_ALPHABITFIELDS:
ctx->profile = LEXLIB_RGBA_BITFIELDS;
ctxMakeBitMask(ctx, &header->redbitmask);
break;
default:
return LEXLIB_UNSUPORTED;
}
switch(header->bitdepth){
case 8:
ctx->image->profile = LEXLIB_RGB;
ctx->pixelSize = 1;
if(header->colorPalette){
ctx->profile = LEXLIB_RGB_PALETTE;
ctx->bitMask.rb = 0x000000FF;
}
if(header->compression)
return LEXLIB_UNSUPORTED;
break;
case 16:
ctx->image->profile = LEXLIB_RGB;
ctx->pixelSize = 2;
if(!header->compression){
ctx->profile = LEXLIB_RGB_BITFIELDS;
ctx->bitMask = COLORBITMASK_BGRA_555;
}
break;
case 24:
ctx->image->profile = LEXLIB_RGB;
ctx->pixelSize = 3;
if(!header->compression)
ctx->profile = LEXLIB_RGB;
break;
case 32:
ctx->image->profile = LEXLIB_RGBA;
ctx->pixelSize = 4;
if(!header->compression)
ctx->profile = LEXLIB_RGBA;
break;
default:
return LEXLIB_UNSUPORTED;
}
}
if(!ctx->offset)
ctx->offset = headOffset;
if(ctx->pallette.len){
ctx->pallette.offset = headOffset;
if(!ctx->pallette.size)
ctx->pallette.size = 4;
ctx->pallette.data = malloc(ctx->pallette.len * ctx->pallette.size);
if(!ctx->pallette.data)
return LEXLIB_OUT_OF_MEMORY;
}
return LEXLIB_OK;
}
static void processPixel(struct LexlibBmpContext *ctx, const uint8_t *data, uint32_t x, uint32_t y){
if(ctx->profile == LEXLIB_RGB){
struct LexlibColor pixel = {
.r = data[2],
.g = data[1],
.b = data[0],
.a = 0xFF
};
lexlibImagePixelSet(ctx->image, x, y, pixel, LEXLIB_NONE);
return;
}
if(ctx->profile == LEXLIB_RGBA){
struct LexlibColor pixel = {
.r = data[2],
.g = data[1],
.b = data[0],
.a = data[4]
};
lexlibImagePixelSet(ctx->image, x, y, pixel, LEXLIB_NONE);
return;
}
uint32_t bmpPixel = 0x00000000;
switch(ctx->pixelSize){
case 4:
bmpPixel |= data[3] << 24;
LEXLIB_FALLTHROUGH;
case 3:
bmpPixel |= data[2] << 16;
LEXLIB_FALLTHROUGH;
case 2:
bmpPixel |= data[1] << 8;
LEXLIB_FALLTHROUGH;
case 1:
bmpPixel |= data[0];
break;
}
if(ctx->profile == LEXLIB_RGB_BITFIELDS){
struct LexlibColor imgPixel = {
.r = rintf((((bmpPixel & ctx->bitMask.rb) >> ctx->bitMask.rs) / ctx->colorMax.r) * 255.0f),
.g = rintf((((bmpPixel & ctx->bitMask.gb) >> ctx->bitMask.gs) / ctx->colorMax.g) * 255.0f),
.b = rintf((((bmpPixel & ctx->bitMask.bb) >> ctx->bitMask.bs) / ctx->colorMax.b) * 255.0f),
.a = 0xFF
};
lexlibImagePixelSet(ctx->image, x, y, imgPixel, LEXLIB_NONE);
return;
}
if(ctx->profile == LEXLIB_RGBA_BITFIELDS){
struct LexlibColor imgPixel = {
.r = rintf((((bmpPixel & ctx->bitMask.rb) >> ctx->bitMask.rs) / ctx->colorMax.r) * 255.0f),
.g = rintf((((bmpPixel & ctx->bitMask.gb) >> ctx->bitMask.gs) / ctx->colorMax.g) * 255.0f),
.b = rintf((((bmpPixel & ctx->bitMask.bb) >> ctx->bitMask.bs) / ctx->colorMax.b) * 255.0f),
.a = rintf((((bmpPixel & ctx->bitMask.ab) >> ctx->bitMask.as) / ctx->colorMax.a) * 255.0f),
};
lexlibImagePixelSet(ctx->image, x, y, imgPixel, LEXLIB_NONE);
return;
}
if(ctx->profile == LEXLIB_RGB_PALETTE){
if(bmpPixel >= ctx->pallette.len)
return;
bmpPixel = ctx->pallette.data[bmpPixel];
struct LexlibColor imgPixel = {
.r = bmpPixel >> 16,
.g = bmpPixel >> 8,
.b = bmpPixel >> 0,
.a = 0xFF,
};
lexlibImagePixelSet(ctx->image, x, y, imgPixel, LEXLIB_NONE);
return;
}
}
static uint8_t postProcess(struct LexlibBmpContext *ctx){
if(!ctx->reverse){
uint8_t err = lexlibImageFlip(ctx->image, LEXLIB_FLIP_Y);
if(err)
return err;
}
return LEXLIB_OK;
}
LEXLIB_INLINE unsigned bitGet(unsigned x, unsigned index){
index %= CHAR_BIT * sizeof(unsigned);
return (x >> index) & 1;
}
static void ctxInit(struct LexlibBmpContext *ctx, struct LexlibImage *image){
memset(ctx, 0, sizeof(*ctx));
ctx->image = image;
}
static void ctxDestroy(struct LexlibBmpContext *ctx){
free(ctx->pallette.data);
}
static void ctxMakeBitMask(struct LexlibBmpContext *ctx, const uint32_t *data){
if(ctx->profile == LEXLIB_RGB_BITFIELDS){
ctx->bitMask.rb = data[0];
ctx->bitMask.gb = data[1];
ctx->bitMask.bb = data[2];
}
if(ctx->profile == LEXLIB_RGBA_BITFIELDS){
ctx->bitMask.rb = data[0];
ctx->bitMask.gb = data[1];
ctx->bitMask.bb = data[2];
ctx->bitMask.ab = data[3];
}
for(uint8_t i = 0; i < 32; i++){
ctx->bitMask.r += bitGet(ctx->bitMask.rb, i);
ctx->bitMask.g += bitGet(ctx->bitMask.gb, i);
ctx->bitMask.b += bitGet(ctx->bitMask.bb, i);
ctx->bitMask.a += bitGet(ctx->bitMask.ab, i);
}
for(uint8_t i = 0; i < 32; i++){
if(bitGet(ctx->bitMask.rb, i) == 0)
ctx->bitMask.rs++;
else
break;
}
for(uint8_t i = 0; i < 32; i++){
if(bitGet(ctx->bitMask.gb, i) == 0)
ctx->bitMask.gs++;
else
break;
}
for(uint8_t i = 0; i < 32; i++){
if(bitGet(ctx->bitMask.bb, i) == 0)
ctx->bitMask.bs++;
else
break;
}
for(uint8_t i = 0; i < 32; i++){
if(bitGet(ctx->bitMask.ab, i) == 0)
ctx->bitMask.as++;
else
break;
}
ctx->colorMax.r = lexlibPowu(2, ctx->bitMask.r) - 1;
ctx->colorMax.g = lexlibPowu(2, ctx->bitMask.g) - 1;
ctx->colorMax.b = lexlibPowu(2, ctx->bitMask.b) - 1;
if(ctx->bitMask.a){
ctx->colorMax.a = lexlibPowu(2, ctx->bitMask.a) - 1;
ctx->profile = LEXLIB_RGBA_BITFIELDS;
} else {
ctx->profile = LEXLIB_RGB_BITFIELDS;
}
}