Util: Add image format conversion

This commit is contained in:
Vicki Pfau 2023-03-31 02:23:24 -07:00
parent d62688a0ef
commit c8ce215d58
3 changed files with 198 additions and 0 deletions

View File

@ -94,6 +94,7 @@ struct VFile;
struct mImage* mImageCreate(unsigned width, unsigned height, enum mColorFormat format);
struct mImage* mImageLoad(const char* path);
struct mImage* mImageLoadVF(struct VFile* vf);
struct mImage* mImageConvertToFormat(const struct mImage*, enum mColorFormat format);
void mImageDestroy(struct mImage*);
uint32_t mImageGetPixel(const struct mImage* image, unsigned x, unsigned y);

View File

@ -11,6 +11,8 @@
#define PIXEL(IM, X, Y) \
(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* image = calloc(1, sizeof(struct mImage));
if (!image) {
@ -107,6 +109,47 @@ struct mImage* mImageLoadVF(struct VFile* vf) {
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) {
free(image->data);
free(image);

View File

@ -502,6 +502,156 @@ M_TEST_DEFINE(loadPng32) {
}
#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,
cmocka_unit_test(pitchRead),
cmocka_unit_test(strideRead),
@ -513,4 +663,8 @@ M_TEST_SUITE_DEFINE(Image,
cmocka_unit_test(loadPng24),
cmocka_unit_test(loadPng32),
#endif
cmocka_unit_test(convert1x1),
cmocka_unit_test(convert2x1),
cmocka_unit_test(convert1x2),
cmocka_unit_test(convert2x2),
)