Util: Add mImage saving

This commit is contained in:
Vicki Pfau 2023-03-31 02:42:01 -07:00
parent c8ce215d58
commit 6d719b529a
3 changed files with 135 additions and 0 deletions

View File

@ -97,6 +97,8 @@ struct mImage* mImageLoadVF(struct VFile* vf);
struct mImage* mImageConvertToFormat(const struct mImage*, enum mColorFormat format);
void mImageDestroy(struct mImage*);
bool mImageSave(const struct mImage*, const char* path, const char* format);
bool mImageSaveVF(const struct mImage*, struct VFile* vf, const char* format);
uint32_t mImageGetPixel(const struct mImage* image, unsigned x, unsigned y);
uint32_t mImageGetPixelRaw(const struct mImage* image, unsigned x, unsigned y);
void mImageSetPixel(struct mImage* image, unsigned x, unsigned y, uint32_t color);
@ -136,6 +138,35 @@ static inline unsigned mColorFormatBytes(enum mColorFormat format) {
return 0;
}
static inline bool mColorFormatHasAlpha(enum mColorFormat format) {
switch (format) {
case mCOLOR_XBGR8:
case mCOLOR_XRGB8:
case mCOLOR_BGRX8:
case mCOLOR_RGBX8:
case mCOLOR_RGB5:
case mCOLOR_BGR5:
case mCOLOR_RGB565:
case mCOLOR_BGR565:
case mCOLOR_RGB8:
case mCOLOR_BGR8:
case mCOLOR_L8:
return false;
case mCOLOR_ABGR8:
case mCOLOR_ARGB8:
case mCOLOR_BGRA8:
case mCOLOR_RGBA8:
case mCOLOR_ARGB5:
case mCOLOR_ABGR5:
case mCOLOR_RGBA5:
case mCOLOR_BGRA5:
return true;
case mCOLOR_ANY:
break;
}
return false;
}
static inline color_t mColorFrom555(uint16_t value) {
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5

View File

@ -155,6 +155,66 @@ void mImageDestroy(struct mImage* image) {
free(image);
}
bool mImageSave(const struct mImage* image, const char* path, const char* format) {
struct VFile* vf = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC);
if (!vf) {
return false;
}
char extension[PATH_MAX];
if (!format) {
separatePath(path, NULL, NULL, extension);
format = extension;
}
bool success = mImageSaveVF(image, vf, format);
vf->close(vf);
return success;
}
#ifdef USE_PNG
bool mImageSavePNG(const struct mImage* image, struct VFile* vf) {
if (image->format != mCOLOR_XBGR8 && image->format != mCOLOR_ABGR8) {
struct mImage* newImage;
if (mColorFormatHasAlpha(image->format)) {
newImage = mImageConvertToFormat(image, mCOLOR_ABGR8);
} else {
newImage = mImageConvertToFormat(image, mCOLOR_XBGR8);
}
bool ret = mImageSavePNG(newImage, vf);
mImageDestroy(newImage);
return ret;
}
png_structp png = PNGWriteOpen(vf);
png_infop info = NULL;
bool ok = false;
if (png) {
if (image->format == mCOLOR_XBGR8) {
info = PNGWriteHeader(png, image->width, image->height);
if (info) {
ok = PNGWritePixels(png, image->width, image->height, image->stride, image->data);
}
} else {
info = PNGWriteHeaderA(png, image->width, image->height);
if (info) {
ok = PNGWritePixelsA(png, image->width, image->height, image->stride, image->data);
}
}
PNGWriteClose(png, info);
}
return ok;
}
#endif
bool mImageSaveVF(const struct mImage* image, struct VFile* vf, const char* format) {
#ifdef USE_PNG
if (strcasecmp(format, "png") == 0) {
return mImageSavePNG(image, vf);
}
#endif
return false;
}
uint32_t mImageGetPixelRaw(const struct mImage* image, unsigned x, unsigned y) {
if (x >= image->width || y >= image->height) {
return 0;

View File

@ -6,6 +6,9 @@
#include "util/test/suite.h"
#include <mgba-util/image.h>
#ifdef USE_PNG
#include <mgba-util/image/png-io.h>
#endif
#include <mgba-util/vfs.h>
M_TEST_DEFINE(pitchRead) {
@ -500,6 +503,45 @@ M_TEST_DEFINE(loadPng32) {
mImageDestroy(image);
}
M_TEST_DEFINE(savePngNative) {
struct mImage* image = mImageCreate(1, 1, mCOLOR_ABGR8);
mImageSetPixel(image, 0, 0, 0x01234567);
struct VFile* vf = VFileMemChunk(NULL, 0);
assert_true(mImageSaveVF(image, vf, "png"));
mImageDestroy(image);
assert_int_equal(vf->seek(vf, 0, SEEK_SET), 0);
assert_true(isPNG(vf));
assert_int_equal(vf->seek(vf, 0, SEEK_SET), 0);
image = mImageLoadVF(vf);
vf->close(vf);
assert_non_null(image);
assert_int_equal(mImageGetPixel(image, 0, 0), 0x01234567);
mImageDestroy(image);
}
M_TEST_DEFINE(savePngNonNative) {
struct mImage* image = mImageCreate(1, 1, mCOLOR_ARGB8);
mImageSetPixel(image, 0, 0, 0x01234567);
struct VFile* vf = VFileMemChunk(NULL, 0);
assert_true(mImageSaveVF(image, vf, "png"));
mImageDestroy(image);
assert_int_equal(vf->seek(vf, 0, SEEK_SET), 0);
assert_true(isPNG(vf));
assert_int_equal(vf->seek(vf, 0, SEEK_SET), 0);
image = mImageLoadVF(vf);
vf->close(vf);
assert_non_null(image);
assert_int_equal(image->format, mCOLOR_ABGR8);
assert_int_equal(mImageGetPixel(image, 0, 0), 0x01234567);
mImageDestroy(image);
}
#endif
M_TEST_DEFINE(convert1x1) {
@ -662,6 +704,8 @@ M_TEST_SUITE_DEFINE(Image,
#ifdef USE_PNG
cmocka_unit_test(loadPng24),
cmocka_unit_test(loadPng32),
cmocka_unit_test(savePngNative),
cmocka_unit_test(savePngNonNative),
#endif
cmocka_unit_test(convert1x1),
cmocka_unit_test(convert2x1),