Util: Preliminary palette support

This commit is contained in:
Vicki Pfau 2023-04-17 03:00:00 -07:00
parent f8b923015b
commit 618a51cabb
3 changed files with 118 additions and 23 deletions

View File

@ -77,6 +77,7 @@ enum mColorFormat {
mCOLOR_RGB8 = 0x10000,
mCOLOR_BGR8 = 0x20000,
mCOLOR_L8 = 0x40000,
mCOLOR_PAL8 = 0x80000,
mCOLOR_ANY = -1
};
@ -91,10 +92,12 @@ enum mColorFormat {
struct mImage {
void* data;
uint32_t* palette;
unsigned width;
unsigned height;
unsigned stride;
unsigned depth;
unsigned palSize;
enum mColorFormat format;
};
@ -146,6 +149,7 @@ static inline unsigned mColorFormatBytes(enum mColorFormat format) {
case mCOLOR_BGR8:
return 3;
case mCOLOR_L8:
case mCOLOR_PAL8:
return 1;
case mCOLOR_ANY:
break;
@ -175,6 +179,7 @@ static inline bool mColorFormatHasAlpha(enum mColorFormat format) {
case mCOLOR_ABGR5:
case mCOLOR_RGBA5:
case mCOLOR_BGRA5:
case mCOLOR_PAL8:
return true;
case mCOLOR_ANY:
break;

View File

@ -135,17 +135,51 @@ static struct mImage* mImageLoadPNG(struct VFile* vf) {
case 1:
if (png_get_color_type(png, info) == PNG_COLOR_TYPE_GRAY) {
image->format = mCOLOR_L8;
image->depth = 1;
image->data = malloc(width * height);
if (!PNGReadPixels8(png, info, image->data, width, height, width)) {
free(image->data);
free(image);
PNGReadClose(png, info, end);
} else {
png_colorp palette;
png_bytep trns;
int count;
int trnsCount = 0;
image->format = mCOLOR_PAL8;
if (png_get_PLTE(png, info, &palette, &count) == 0) {
return NULL;
}
break;
if (count > 256) {
count = 256;
#ifndef NDEBUG
abort();
#endif
}
image->palette = malloc(1024);
image->palSize = count;
png_get_tRNS(png, info, &trns, &trnsCount, NULL);
int i;
for (i = 0; i < count; ++i) {
uint32_t color = palette[i].red << 16;
color |= palette[i].green << 8;
color |= palette[i].blue;
if (i < trnsCount) {
color |= trns[i] << 24;
} else {
color |= 0xFF000000;
}
image->palette[i] = color;
}
}
// Fall through
image->depth = 1;
image->data = malloc(width * height);
if (!PNGReadPixels8(png, info, image->data, width, height, width)) {
if (image->palette) {
free(image->palette);
}
free(image->data);
free(image);
PNGReadClose(png, info, end);
return NULL;
}
break;
default:
// Not supported yet
free(image);
@ -169,6 +203,10 @@ struct mImage* mImageLoadVF(struct VFile* vf) {
}
struct mImage* mImageConvertToFormat(const struct mImage* image, enum mColorFormat format) {
if (format == mCOLOR_PAL8) {
// Quantization shouldn't be handled here
return NULL;
}
struct mImage* newImage = calloc(1, sizeof(*newImage));
newImage->width = image->width;
newImage->height = image->height;
@ -186,20 +224,36 @@ struct mImage* mImageConvertToFormat(const struct mImage* image, enum mColorForm
// TODO: Implement more specializations, e.g. alpha narrowing/widening, channel swapping
size_t x, y;
for (y = 0; y < newImage->height; ++y) {
uintptr_t src = (uintptr_t) ROW(image, y);
uintptr_t dst = (uintptr_t) ROW(newImage, y);
for (x = 0; x < newImage->width; ++x, src += image->depth, dst += newImage->depth) {
uint32_t color;
GET_PIXEL(color, src, image->depth);
color = mColorConvert(color, image->format, format);
PUT_PIXEL(color, dst, newImage->depth);
if (image->format == mCOLOR_PAL8) {
for (y = 0; y < newImage->height; ++y) {
uintptr_t src = (uintptr_t) ROW(image, y);
uintptr_t dst = (uintptr_t) ROW(newImage, y);
for (x = 0; x < newImage->width; ++x, src += image->depth, dst += newImage->depth) {
uint32_t color;
GET_PIXEL(color, src, image->depth);
color = image->palette[color];
PUT_PIXEL(color, dst, newImage->depth);
}
}
} else {
for (y = 0; y < newImage->height; ++y) {
uintptr_t src = (uintptr_t) ROW(image, y);
uintptr_t dst = (uintptr_t) ROW(newImage, y);
for (x = 0; x < newImage->width; ++x, src += image->depth, dst += newImage->depth) {
uint32_t color;
GET_PIXEL(color, src, image->depth);
color = mColorConvert(color, image->format, format);
PUT_PIXEL(color, dst, newImage->depth);
}
}
}
return newImage;
}
void mImageDestroy(struct mImage* image) {
if (image->palette) {
free(image->palette);
}
free(image->data);
free(image);
}
@ -280,7 +334,12 @@ uint32_t mImageGetPixelRaw(const struct mImage* image, unsigned x, unsigned y) {
}
uint32_t mImageGetPixel(const struct mImage* image, unsigned x, unsigned y) {
return mColorConvert(mImageGetPixelRaw(image, x, y), image->format, mCOLOR_ARGB8);
uint32_t raw = mImageGetPixelRaw(image, x, y);
if (image->format == mCOLOR_PAL8) {
return image->palette[raw];
} else {
return mColorConvert(raw, image->format, mCOLOR_ARGB8);
}
}
void mImageSetPixelRaw(struct mImage* image, unsigned x, unsigned y, uint32_t color) {
@ -531,6 +590,7 @@ uint32_t mColorConvert(uint32_t color, enum mColorFormat from, enum mColorFormat
b = color;
break;
case mCOLOR_PAL8:
case mCOLOR_ANY:
return 0;
}
@ -619,6 +679,7 @@ uint32_t mColorConvert(uint32_t color, enum mColorFormat from, enum mColorFormat
// sRGB primaries in fixed point, roughly fudged to saturate to 0xFFFF
color = (55 * r + 184 * g + 18 * b) >> 8;
break;
case mCOLOR_PAL8:
case mCOLOR_ANY:
return 0;
}

View File

@ -464,9 +464,8 @@ M_TEST_DEFINE(loadPng24) {
0x51, 0xc0, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae,
0x42, 0x60, 0x82
};
size_t len = 75;
struct VFile* vf = VFileFromConstMemory(data, len);
struct VFile* vf = VFileFromConstMemory(data, sizeof(data));
struct mImage* image = mImageLoadVF(vf);
vf->close(vf);
@ -483,9 +482,8 @@ M_TEST_DEFINE(loadPng24) {
mImageDestroy(image);
}
M_TEST_DEFINE(loadPng32) {
unsigned char data[] = {
const uint8_t data[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
0x08, 0x06, 0x00, 0x00, 0x00, 0x72, 0xb6, 0x0d, 0x24, 0x00, 0x00, 0x00,
@ -494,9 +492,8 @@ M_TEST_DEFINE(loadPng32) {
0xa7, 0x5a, 0x78, 0x58, 0x7b, 0x07, 0xac, 0xe9, 0x00, 0x3d, 0x95, 0x00,
0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
unsigned int len = 83;
struct VFile* vf = VFileFromConstMemory(data, len);
struct VFile* vf = VFileFromConstMemory(data, sizeof(data));
struct mImage* image = mImageLoadVF(vf);
vf->close(vf);
@ -513,6 +510,37 @@ M_TEST_DEFINE(loadPng32) {
mImageDestroy(image);
}
M_TEST_DEFINE(loadPngPalette) {
const uint8_t data[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
0x08, 0x03, 0x00, 0x00, 0x00, 0x45, 0x68, 0xfd, 0x16, 0x00, 0x00, 0x00,
0x0c, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00,
0xff, 0x00, 0x00, 0x00, 0xff, 0x9b, 0xc0, 0x13, 0xdc, 0x00, 0x00, 0x00,
0x04, 0x74, 0x52, 0x4e, 0x53, 0x00, 0xc0, 0x80, 0x40, 0x6f, 0x63, 0x29,
0x01, 0x00, 0x00, 0x00, 0x0e, 0x49, 0x44, 0x41, 0x54, 0x08, 0x99, 0x63,
0x60, 0x60, 0x64, 0x60, 0x62, 0x06, 0x00, 0x00, 0x11, 0x00, 0x07, 0x69,
0xe2, 0x2a, 0x44, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae,
0x42, 0x60, 0x82
};
struct VFile* vf = VFileFromConstMemory(data, sizeof(data));
struct mImage* image = mImageLoadVF(vf);
vf->close(vf);
assert_non_null(image);
assert_int_equal(image->width, 2);
assert_int_equal(image->height, 2);
assert_int_equal(image->format, mCOLOR_PAL8);
assert_int_equal(mImageGetPixel(image, 0, 0), 0x00000000);
assert_int_equal(mImageGetPixel(image, 1, 0), 0xC0FF0000);
assert_int_equal(mImageGetPixel(image, 0, 1), 0x8000FF00);
assert_int_equal(mImageGetPixel(image, 1, 1), 0x400000FF);
mImageDestroy(image);
}
M_TEST_DEFINE(savePngNative) {
struct mImage* image = mImageCreate(1, 1, mCOLOR_ABGR8);
mImageSetPixel(image, 0, 0, 0x01234567);
@ -1069,6 +1097,7 @@ M_TEST_SUITE_DEFINE(Image,
#ifdef USE_PNG
cmocka_unit_test(loadPng24),
cmocka_unit_test(loadPng32),
cmocka_unit_test(loadPngPalette),
cmocka_unit_test(savePngNative),
cmocka_unit_test(savePngNonNative),
cmocka_unit_test(savePngRoundTrip),