mirror of https://github.com/mgba-emu/mgba.git
Util: Add image format conversion
This commit is contained in:
parent
d62688a0ef
commit
c8ce215d58
|
@ -94,6 +94,7 @@ struct VFile;
|
||||||
struct mImage* mImageCreate(unsigned width, unsigned height, enum mColorFormat format);
|
struct mImage* mImageCreate(unsigned width, unsigned height, enum mColorFormat format);
|
||||||
struct mImage* mImageLoad(const char* path);
|
struct mImage* mImageLoad(const char* path);
|
||||||
struct mImage* mImageLoadVF(struct VFile* vf);
|
struct mImage* mImageLoadVF(struct VFile* vf);
|
||||||
|
struct mImage* mImageConvertToFormat(const struct mImage*, enum mColorFormat format);
|
||||||
void mImageDestroy(struct mImage*);
|
void mImageDestroy(struct mImage*);
|
||||||
|
|
||||||
uint32_t mImageGetPixel(const struct mImage* image, unsigned x, unsigned y);
|
uint32_t mImageGetPixel(const struct mImage* image, unsigned x, unsigned y);
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#define PIXEL(IM, X, Y) \
|
#define PIXEL(IM, X, Y) \
|
||||||
(void*) (((IM)->stride * (Y) + (X)) * (IM)->depth + (uintptr_t) (IM)->data)
|
(void*) (((IM)->stride * (Y) + (X)) * (IM)->depth + (uintptr_t) (IM)->data)
|
||||||
|
|
||||||
|
#define ROW(IM, Y) PIXEL(IM, 0, Y)
|
||||||
|
|
||||||
struct mImage* mImageCreate(unsigned width, unsigned height, enum mColorFormat format) {
|
struct mImage* mImageCreate(unsigned width, unsigned height, enum mColorFormat format) {
|
||||||
struct mImage* image = calloc(1, sizeof(struct mImage));
|
struct mImage* image = calloc(1, sizeof(struct mImage));
|
||||||
if (!image) {
|
if (!image) {
|
||||||
|
@ -107,6 +109,47 @@ struct mImage* mImageLoadVF(struct VFile* vf) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct mImage* mImageConvertToFormat(const struct mImage* image, enum mColorFormat format) {
|
||||||
|
struct mImage* newImage = calloc(1, sizeof(*newImage));
|
||||||
|
newImage->width = image->width;
|
||||||
|
newImage->height = image->height;
|
||||||
|
newImage->format = format;
|
||||||
|
if (format == image->format) {
|
||||||
|
newImage->depth = image->depth;
|
||||||
|
newImage->stride = image->stride;
|
||||||
|
newImage->data = malloc(image->stride * image->height * image->depth);
|
||||||
|
memcpy(newImage->data, image->data, image->stride * image->height * image->depth);
|
||||||
|
return newImage;
|
||||||
|
}
|
||||||
|
newImage->depth = mColorFormatBytes(format);
|
||||||
|
newImage->stride = image->width;
|
||||||
|
newImage->data = malloc(image->width * image->height * newImage->depth);
|
||||||
|
|
||||||
|
// 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 = 0;
|
||||||
|
memcpy(&color, (void*) src, image->depth);
|
||||||
|
#ifdef __BIG_ENDIAN__
|
||||||
|
if (image->depth < 4) {
|
||||||
|
color >>= (32 - 8 * image->depth);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
color = mColorConvert(color, image->format, format);
|
||||||
|
#ifdef __BIG_ENDIAN__
|
||||||
|
if (newImage->depth < 4) {
|
||||||
|
color <<= (32 - 8 * newImage->depth);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
memcpy((void*) dst, &color, newImage->depth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newImage;
|
||||||
|
}
|
||||||
|
|
||||||
void mImageDestroy(struct mImage* image) {
|
void mImageDestroy(struct mImage* image) {
|
||||||
free(image->data);
|
free(image->data);
|
||||||
free(image);
|
free(image);
|
||||||
|
|
|
@ -502,6 +502,156 @@ M_TEST_DEFINE(loadPng32) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
M_TEST_DEFINE(convert1x1) {
|
||||||
|
const enum mColorFormat formats[] = {
|
||||||
|
mCOLOR_XBGR8, mCOLOR_XRGB8,
|
||||||
|
mCOLOR_BGRX8, mCOLOR_RGBX8,
|
||||||
|
mCOLOR_ABGR8, mCOLOR_ARGB8,
|
||||||
|
mCOLOR_BGRA8, mCOLOR_RGBA8,
|
||||||
|
mCOLOR_RGB5, mCOLOR_BGR5,
|
||||||
|
mCOLOR_RGB565, mCOLOR_BGR565,
|
||||||
|
mCOLOR_ARGB5, mCOLOR_ABGR5,
|
||||||
|
mCOLOR_RGBA5, mCOLOR_BGRA5,
|
||||||
|
mCOLOR_RGB8, mCOLOR_BGR8,
|
||||||
|
mCOLOR_L8,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
for (i = 0; formats[i]; ++i) {
|
||||||
|
for (j = 0; formats[j]; ++j) {
|
||||||
|
struct mImage* src = mImageCreate(1, 1, formats[i]);
|
||||||
|
mImageSetPixel(src, 0, 0, 0xFF181818);
|
||||||
|
|
||||||
|
struct mImage* dst = mImageConvertToFormat(src, formats[j]);
|
||||||
|
assert_non_null(dst);
|
||||||
|
assert_int_equal(dst->format, formats[j]);
|
||||||
|
assert_int_equal(mImageGetPixel(dst, 0, 0), 0xFF181818);
|
||||||
|
|
||||||
|
mImageDestroy(src);
|
||||||
|
mImageDestroy(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(convert2x1) {
|
||||||
|
const enum mColorFormat formats[] = {
|
||||||
|
mCOLOR_XBGR8, mCOLOR_XRGB8,
|
||||||
|
mCOLOR_BGRX8, mCOLOR_RGBX8,
|
||||||
|
mCOLOR_ABGR8, mCOLOR_ARGB8,
|
||||||
|
mCOLOR_BGRA8, mCOLOR_RGBA8,
|
||||||
|
mCOLOR_RGB5, mCOLOR_BGR5,
|
||||||
|
mCOLOR_RGB565, mCOLOR_BGR565,
|
||||||
|
mCOLOR_ARGB5, mCOLOR_ABGR5,
|
||||||
|
mCOLOR_RGBA5, mCOLOR_BGRA5,
|
||||||
|
mCOLOR_RGB8, mCOLOR_BGR8,
|
||||||
|
mCOLOR_L8,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
for (i = 0; formats[i]; ++i) {
|
||||||
|
for (j = 0; formats[j]; ++j) {
|
||||||
|
struct mImage* src = mImageCreate(2, 1, formats[i]);
|
||||||
|
mImageSetPixel(src, 0, 0, 0xFF181818);
|
||||||
|
mImageSetPixel(src, 1, 0, 0xFF101010);
|
||||||
|
|
||||||
|
struct mImage* dst = mImageConvertToFormat(src, formats[j]);
|
||||||
|
assert_non_null(dst);
|
||||||
|
assert_int_equal(dst->format, formats[j]);
|
||||||
|
assert_int_equal(mImageGetPixel(dst, 0, 0), 0xFF181818);
|
||||||
|
assert_int_equal(mImageGetPixel(dst, 1, 0), 0xFF101010);
|
||||||
|
|
||||||
|
mImageDestroy(src);
|
||||||
|
mImageDestroy(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(convert1x2) {
|
||||||
|
const enum mColorFormat formats[] = {
|
||||||
|
mCOLOR_XBGR8, mCOLOR_XRGB8,
|
||||||
|
mCOLOR_BGRX8, mCOLOR_RGBX8,
|
||||||
|
mCOLOR_ABGR8, mCOLOR_ARGB8,
|
||||||
|
mCOLOR_BGRA8, mCOLOR_RGBA8,
|
||||||
|
mCOLOR_RGB5, mCOLOR_BGR5,
|
||||||
|
mCOLOR_RGB565, mCOLOR_BGR565,
|
||||||
|
mCOLOR_ARGB5, mCOLOR_ABGR5,
|
||||||
|
mCOLOR_RGBA5, mCOLOR_BGRA5,
|
||||||
|
mCOLOR_RGB8, mCOLOR_BGR8,
|
||||||
|
mCOLOR_L8,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
for (i = 0; formats[i]; ++i) {
|
||||||
|
for (j = 0; formats[j]; ++j) {
|
||||||
|
struct mImage* src = calloc(1, sizeof(*src));
|
||||||
|
src->width = 1;
|
||||||
|
src->height = 2;
|
||||||
|
src->stride = 8; // Use an unusual stride to make sure the right parts get copied
|
||||||
|
src->format = formats[i];
|
||||||
|
src->depth = mColorFormatBytes(src->format);
|
||||||
|
src->data = calloc(src->stride * src->depth, src->height);
|
||||||
|
mImageSetPixel(src, 0, 0, 0xFF181818);
|
||||||
|
mImageSetPixel(src, 0, 1, 0xFF101010);
|
||||||
|
|
||||||
|
struct mImage* dst = mImageConvertToFormat(src, formats[j]);
|
||||||
|
assert_non_null(dst);
|
||||||
|
assert_int_equal(dst->format, formats[j]);
|
||||||
|
assert_int_equal(mImageGetPixel(dst, 0, 0), 0xFF181818);
|
||||||
|
assert_int_equal(mImageGetPixel(dst, 0, 1), 0xFF101010);
|
||||||
|
|
||||||
|
mImageDestroy(src);
|
||||||
|
mImageDestroy(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(convert2x2) {
|
||||||
|
const enum mColorFormat formats[] = {
|
||||||
|
mCOLOR_XBGR8, mCOLOR_XRGB8,
|
||||||
|
mCOLOR_BGRX8, mCOLOR_RGBX8,
|
||||||
|
mCOLOR_ABGR8, mCOLOR_ARGB8,
|
||||||
|
mCOLOR_BGRA8, mCOLOR_RGBA8,
|
||||||
|
mCOLOR_RGB5, mCOLOR_BGR5,
|
||||||
|
mCOLOR_RGB565, mCOLOR_BGR565,
|
||||||
|
mCOLOR_ARGB5, mCOLOR_ABGR5,
|
||||||
|
mCOLOR_RGBA5, mCOLOR_BGRA5,
|
||||||
|
mCOLOR_RGB8, mCOLOR_BGR8,
|
||||||
|
mCOLOR_L8,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
for (i = 0; formats[i]; ++i) {
|
||||||
|
for (j = 0; formats[j]; ++j) {
|
||||||
|
struct mImage* src = calloc(1, sizeof(*src));
|
||||||
|
src->width = 2;
|
||||||
|
src->height = 2;
|
||||||
|
src->stride = 8; // Use an unusual stride to make sure the right parts get copied
|
||||||
|
src->format = formats[i];
|
||||||
|
src->depth = mColorFormatBytes(src->format);
|
||||||
|
src->data = calloc(src->stride * src->depth, src->height);
|
||||||
|
mImageSetPixel(src, 0, 0, 0xFF181818);
|
||||||
|
mImageSetPixel(src, 0, 1, 0xFF101010);
|
||||||
|
mImageSetPixel(src, 1, 0, 0xFF000000);
|
||||||
|
mImageSetPixel(src, 1, 1, 0xFF080808);
|
||||||
|
|
||||||
|
struct mImage* dst = mImageConvertToFormat(src, formats[j]);
|
||||||
|
assert_non_null(dst);
|
||||||
|
assert_int_equal(dst->format, formats[j]);
|
||||||
|
assert_int_equal(mImageGetPixel(dst, 0, 0), 0xFF181818);
|
||||||
|
assert_int_equal(mImageGetPixel(dst, 0, 1), 0xFF101010);
|
||||||
|
assert_int_equal(mImageGetPixel(dst, 1, 0), 0xFF000000);
|
||||||
|
assert_int_equal(mImageGetPixel(dst, 1, 1), 0xFF080808);
|
||||||
|
|
||||||
|
mImageDestroy(src);
|
||||||
|
mImageDestroy(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
M_TEST_SUITE_DEFINE(Image,
|
M_TEST_SUITE_DEFINE(Image,
|
||||||
cmocka_unit_test(pitchRead),
|
cmocka_unit_test(pitchRead),
|
||||||
cmocka_unit_test(strideRead),
|
cmocka_unit_test(strideRead),
|
||||||
|
@ -513,4 +663,8 @@ M_TEST_SUITE_DEFINE(Image,
|
||||||
cmocka_unit_test(loadPng24),
|
cmocka_unit_test(loadPng24),
|
||||||
cmocka_unit_test(loadPng32),
|
cmocka_unit_test(loadPng32),
|
||||||
#endif
|
#endif
|
||||||
|
cmocka_unit_test(convert1x1),
|
||||||
|
cmocka_unit_test(convert2x1),
|
||||||
|
cmocka_unit_test(convert1x2),
|
||||||
|
cmocka_unit_test(convert2x2),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue