mirror of https://github.com/mgba-emu/mgba.git
Util: Preliminary palette support
This commit is contained in:
parent
f8b923015b
commit
618a51cabb
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in New Issue