mirror of https://github.com/mgba-emu/mgba.git
Util: Add mImage saving
This commit is contained in:
parent
c8ce215d58
commit
6d719b529a
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in New Issue