From 65f04ee408c45166077f31e65d812a8ba59c9bbc Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 17 Apr 2023 02:03:15 -0700 Subject: [PATCH] Util: PNGWrite*A is dead, long live PNGWrite* --- include/mgba-util/image.h | 8 + include/mgba-util/image/png-io.h | 8 +- src/core/core.c | 4 +- src/core/serialize.c | 4 +- src/platform/python/mgba/png.py | 8 +- src/platform/test/cinema-main.c | 4 +- src/util/image.c | 25 +- src/util/image/png-io.c | 415 +++++++++++++++++++++++++------ src/util/test/image.c | 46 ++++ 9 files changed, 412 insertions(+), 110 deletions(-) diff --git a/include/mgba-util/image.h b/include/mgba-util/image.h index 6dd3cee73..0a1b8d7b0 100644 --- a/include/mgba-util/image.h +++ b/include/mgba-util/image.h @@ -81,6 +81,14 @@ enum mColorFormat { mCOLOR_ANY = -1 }; +#ifndef COLOR_16_BIT +#define mCOLOR_NATIVE mCOLOR_XBGR8 +#elif !defined(COLOR_5_6_5) +#define mCOLOR_NATIVE mCOLOR_BGR5 +#else +#define mCOLOR_NATIVE mCOLOR_RGB565 +#endif + struct mImage { void* data; unsigned width; diff --git a/include/mgba-util/image/png-io.h b/include/mgba-util/image/png-io.h index 4156b5d20..b33d64ce7 100644 --- a/include/mgba-util/image/png-io.h +++ b/include/mgba-util/image/png-io.h @@ -12,6 +12,8 @@ CXX_GUARD_START #ifdef USE_PNG +#include + // png.h defines its own version of restrict which conflicts with mGBA's. #ifdef restrict #undef restrict @@ -25,12 +27,10 @@ enum { }; png_structp PNGWriteOpen(struct VFile* source); -png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height); -png_infop PNGWriteHeaderA(png_structp png, unsigned width, unsigned height); +png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height, enum mColorFormat); png_infop PNGWriteHeader8(png_structp png, unsigned width, unsigned height); bool PNGWritePalette(png_structp png, png_infop info, const uint32_t* palette, unsigned entries); -bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels); -bool PNGWritePixelsA(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels); +bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels, enum mColorFormat); bool PNGWritePixels8(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels); bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data); void PNGWriteClose(png_structp png, png_infop info); diff --git a/src/core/core.c b/src/core/core.c index 8091599bf..61f29d71f 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -364,8 +364,8 @@ bool mCoreTakeScreenshotVF(struct mCore* core, struct VFile* vf) { core->currentVideoSize(core, &width, &height); core->getPixels(core, &pixels, &stride); png_structp png = PNGWriteOpen(vf); - png_infop info = PNGWriteHeader(png, width, height); - bool success = PNGWritePixels(png, width, height, stride, pixels); + png_infop info = PNGWriteHeader(png, width, height, mCOLOR_NATIVE); + bool success = PNGWritePixels(png, width, height, stride, pixels, mCOLOR_NATIVE); PNGWriteClose(png, info); return success; #else diff --git a/src/core/serialize.c b/src/core/serialize.c index 1e0c72ddf..b721cdafa 100644 --- a/src/core/serialize.c +++ b/src/core/serialize.c @@ -178,13 +178,13 @@ static bool _savePNGState(struct mCore* core, struct VFile* vf, struct mStateExt unsigned width, height; core->currentVideoSize(core, &width, &height); png_structp png = PNGWriteOpen(vf); - png_infop info = PNGWriteHeader(png, width, height); + png_infop info = PNGWriteHeader(png, width, height, mCOLOR_NATIVE); if (!png || !info) { PNGWriteClose(png, info); free(buffer); return false; } - PNGWritePixels(png, width, height, stride, pixels); + PNGWritePixels(png, width, height, stride, pixels, mCOLOR_NATIVE); PNGWriteCustomChunk(png, "gbAs", len, buffer); if (extdata) { uint32_t i; diff --git a/src/platform/python/mgba/png.py b/src/platform/python/mgba/png.py index ac8b79b86..3051ca197 100644 --- a/src/platform/python/mgba/png.py +++ b/src/platform/python/mgba/png.py @@ -21,18 +21,18 @@ class PNG: def write_header(self, image): self._png = lib.PNGWriteOpen(self._vfile.handle) if self.mode == MODE_RGB: - self._info = lib.PNGWriteHeader(self._png, image.width, image.height) + self._info = lib.PNGWriteHeader(self._png, image.width, image.height, lib.mCOLOR_XBGR8) if self.mode == MODE_RGBA: - self._info = lib.PNGWriteHeaderA(self._png, image.width, image.height) + self._info = lib.PNGWriteHeader(self._png, image.width, image.height, lib.mCOLOR_ABGR8) if self.mode == MODE_INDEX: self._info = lib.PNGWriteHeader8(self._png, image.width, image.height) return self._info != ffi.NULL def write_pixels(self, image): if self.mode == MODE_RGB: - return lib.PNGWritePixels(self._png, image.width, image.height, image.stride, image.buffer) + return lib.PNGWritePixels(self._png, image.width, image.height, image.stride, image.buffer, lib.mCOLOR_XBGR8) if self.mode == MODE_RGBA: - return lib.PNGWritePixelsA(self._png, image.width, image.height, image.stride, image.buffer) + return lib.PNGWritePixels(self._png, image.width, image.height, image.stride, image.buffer, lib.mCOLOR_ABGR8) if self.mode == MODE_INDEX: return lib.PNGWritePixels8(self._png, image.width, image.height, image.stride, image.buffer) return False diff --git a/src/platform/test/cinema-main.c b/src/platform/test/cinema-main.c index 416e8bb81..0644c4408 100644 --- a/src/platform/test/cinema-main.c +++ b/src/platform/test/cinema-main.c @@ -731,8 +731,8 @@ static struct VDir* _makeOutDir(const char* testName) { static void _writeImage(struct VFile* vf, const struct CInemaImage* image) { png_structp png = PNGWriteOpen(vf); - png_infop info = PNGWriteHeader(png, image->width, image->height); - if (!PNGWritePixels(png, image->width, image->height, image->stride, image->data)) { + png_infop info = PNGWriteHeader(png, image->width, image->height, mCOLOR_NATIVE); + if (!PNGWritePixels(png, image->width, image->height, image->stride, image->data, mCOLOR_NATIVE)) { CIerr(0, "Could not write output image\n"); } PNGWriteClose(png, info); diff --git a/src/util/image.c b/src/util/image.c index ed070f658..fa239cb90 100644 --- a/src/util/image.c +++ b/src/util/image.c @@ -208,32 +208,13 @@ bool mImageSave(const struct mImage* image, const char* path, const char* format #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); - } + info = PNGWriteHeader(png, image->width, image->height, image->format); + if (info) { + ok = PNGWritePixels(png, image->width, image->height, image->stride, image->data, image->format); } PNGWriteClose(png, info); } diff --git a/src/util/image/png-io.c b/src/util/image/png-io.c index 9af634a5c..065acdd31 100644 --- a/src/util/image/png-io.c +++ b/src/util/image/png-io.c @@ -51,12 +51,37 @@ static png_infop _pngWriteHeader(png_structp png, unsigned width, unsigned heigh return info; } -png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height) { - return _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB); -} - -png_infop PNGWriteHeaderA(png_structp png, unsigned width, unsigned height) { - return _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB_ALPHA); +png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height, enum mColorFormat fmt) { + int type; + switch (fmt) { + 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: + type = PNG_COLOR_TYPE_RGB; + break; + case mCOLOR_ABGR8: + case mCOLOR_ARGB8: + case mCOLOR_BGRA8: + case mCOLOR_RGBA8: + case mCOLOR_ARGB5: + case mCOLOR_ABGR5: + case mCOLOR_RGBA5: + case mCOLOR_BGRA5: + case mCOLOR_ANY: + type = PNG_COLOR_TYPE_RGB_ALPHA; + break; + case mCOLOR_L8: + type = PNG_COLOR_TYPE_GRAY; + break; + } + return _pngWriteHeader(png, width, height, type); } png_infop PNGWriteHeader8(png_structp png, unsigned width, unsigned height) { @@ -85,51 +110,258 @@ bool PNGWritePalette(png_structp png, png_infop info, const uint32_t* palette, u return true; } -bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels) { - png_bytep row = malloc(sizeof(png_byte) * width * 3); - if (!row) { - return false; - } - const png_byte* pixelData = pixels; - if (setjmp(png_jmpbuf(png))) { - free(row); - return false; - } - unsigned i; - for (i = 0; i < height; ++i) { - unsigned x; - for (x = 0; x < width; ++x) { -#ifdef COLOR_16_BIT - uint16_t c = ((uint16_t*) pixelData)[stride * i + x]; -#ifdef COLOR_5_6_5 - row[x * 3] = (c >> 8) & 0xF8; - row[x * 3 + 1] = (c >> 3) & 0xFC; - row[x * 3 + 2] = (c << 3) & 0xF8; -#else - row[x * 3] = (c >> 7) & 0xF8; - row[x * 3 + 1] = (c >> 2) & 0xF8; - row[x * 3 + 2] = (c << 3) & 0xF8; -#endif -#else +static void _convertRowXBGR8(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { #ifdef __BIG_ENDIAN__ - row[x * 3] = pixelData[stride * i * 4 + x * 4 + 3]; - row[x * 3 + 1] = pixelData[stride * i * 4 + x * 4 + 2]; - row[x * 3 + 2] = pixelData[stride * i * 4 + x * 4 + 1]; + row[x * 3] = pixelData[x * 4 + 3]; + row[x * 3 + 1] = pixelData[x * 4 + 2]; + row[x * 3 + 2] = pixelData[x * 4 + 1]; #else - row[x * 3] = pixelData[stride * i * 4 + x * 4]; - row[x * 3 + 1] = pixelData[stride * i * 4 + x * 4 + 1]; - row[x * 3 + 2] = pixelData[stride * i * 4 + x * 4 + 2]; + row[x * 3] = pixelData[x * 4]; + row[x * 3 + 1] = pixelData[x * 4 + 1]; + row[x * 3 + 2] = pixelData[x * 4 + 2]; #endif -#endif - } - png_write_row(png, row); } - free(row); - return true; } -bool PNGWritePixelsA(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels) { - png_bytep row = malloc(sizeof(png_byte) * width * 4); +static void _convertRowXRGB8(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { +#ifdef __BIG_ENDIAN__ + row[x * 3] = pixelData[x * 4 + 1]; + row[x * 3 + 1] = pixelData[x * 4 + 2]; + row[x * 3 + 2] = pixelData[x * 4 + 3]; +#else + row[x * 3] = pixelData[x * 4 + 2]; + row[x * 3 + 1] = pixelData[x * 4 + 1]; + row[x * 3 + 2] = pixelData[x * 4]; +#endif + } +} + +static void _convertRowBGRX8(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { +#ifdef __BIG_ENDIAN__ + row[x * 3] = pixelData[x * 4 + 2]; + row[x * 3 + 1] = pixelData[x * 4 + 1]; + row[x * 3 + 2] = pixelData[x * 4]; +#else + row[x * 3] = pixelData[x * 4 + 1]; + row[x * 3 + 1] = pixelData[x * 4 + 2]; + row[x * 3 + 2] = pixelData[x * 4 + 3]; +#endif + } +} + +static void _convertRowRGBX8(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { +#ifdef __BIG_ENDIAN__ + row[x * 3] = pixelData[x * 4]; + row[x * 3 + 1] = pixelData[x * 4 + 1]; + row[x * 3 + 2] = pixelData[x * 4 + 2]; +#else + row[x * 3] = pixelData[x * 4 + 3]; + row[x * 3 + 1] = pixelData[x * 4 + 2]; + row[x * 3 + 2] = pixelData[x * 4 + 1]; +#endif + } +} + +static void _convertRowABGR8(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { +#ifdef __BIG_ENDIAN__ + row[x * 4] = pixelData[x * 4 + 3]; + row[x * 4 + 1] = pixelData[x * 4 + 2]; + row[x * 4 + 2] = pixelData[x * 4 + 1]; + row[x * 4 + 3] = pixelData[x * 4]; +#else + row[x * 4] = pixelData[x * 4]; + row[x * 4 + 1] = pixelData[x * 4 + 1]; + row[x * 4 + 2] = pixelData[x * 4 + 2]; + row[x * 4 + 3] = pixelData[x * 4 + 3]; +#endif + } +} + +static void _convertRowARGB8(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { +#ifdef __BIG_ENDIAN__ + row[x * 4] = pixelData[x * 4 + 1]; + row[x * 4 + 1] = pixelData[x * 4 + 2]; + row[x * 4 + 2] = pixelData[x * 4 + 3]; + row[x * 4 + 3] = pixelData[x * 4]; +#else + row[x * 4] = pixelData[x * 4 + 2]; + row[x * 4 + 1] = pixelData[x * 4 + 1]; + row[x * 4 + 2] = pixelData[x * 4]; + row[x * 4 + 3] = pixelData[x * 4 + 3]; +#endif + } +} + +static void _convertRowBGRA8(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { +#ifdef __BIG_ENDIAN__ + row[x * 4] = pixelData[x * 4 + 2]; + row[x * 4 + 1] = pixelData[x * 4 + 1]; + row[x * 4 + 2] = pixelData[x * 4]; + row[x * 4 + 3] = pixelData[x * 4 + 3]; +#else + row[x * 4] = pixelData[x * 4 + 1]; + row[x * 4 + 1] = pixelData[x * 4 + 2]; + row[x * 4 + 2] = pixelData[x * 4 + 3]; + row[x * 4 + 3] = pixelData[x * 4]; +#endif + } +} + +static void _convertRowRGBA8(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { +#ifdef __BIG_ENDIAN__ + row[x * 4] = pixelData[x * 4]; + row[x * 4 + 1] = pixelData[x * 4 + 1]; + row[x * 4 + 2] = pixelData[x * 4 + 2]; + row[x * 4 + 3] = pixelData[x * 4 + 3]; +#else + row[x * 4] = pixelData[x * 4 + 3]; + row[x * 4 + 1] = pixelData[x * 4 + 2]; + row[x * 4 + 2] = pixelData[x * 4 + 1]; + row[x * 4 + 3] = pixelData[x * 4]; +#endif + } +} + +static void _convertRowRGB5(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { + uint16_t c = ((uint16_t*) pixelData)[x]; + row[x * 3] = (c >> 7) & 0xF8; + row[x * 3 + 1] = (c >> 2) & 0xF8; + row[x * 3 + 2] = (c << 3) & 0xF8; + } +} + +static void _convertRowBGR5(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { + uint16_t c = ((uint16_t*) pixelData)[x]; + row[x * 3] = (c << 3) & 0xF8; + row[x * 3 + 1] = (c >> 2) & 0xF8; + row[x * 3 + 2] = (c >> 7) & 0xF8; + } +} + +static void _convertRowARGB5(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { + uint16_t c = ((uint16_t*) pixelData)[x]; + row[x * 4] = (c >> 7) & 0xF8; + row[x * 4 + 1] = (c >> 2) & 0xF8; + row[x * 4 + 2] = (c << 3) & 0xF8; + row[x * 4 + 3] = (c >> 15) * 0xFF; + } +} + +static void _convertRowABGR5(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { + uint16_t c = ((uint16_t*) pixelData)[x]; + row[x * 4] = (c << 3) & 0xF8; + row[x * 4 + 1] = (c >> 2) & 0xF8; + row[x * 4 + 2] = (c >> 7) & 0xF8; + row[x * 4 + 3] = (c >> 15) * 0xFF; + } +} + +static void _convertRowRGBA5(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { + uint16_t c = ((uint16_t*) pixelData)[x]; + row[x * 4] = (c >> 8) & 0xF8; + row[x * 4 + 1] = (c >> 3) & 0xF8; + row[x * 4 + 2] = (c << 2) & 0xF8; + row[x * 4 + 3] = (c & 1) * 0xFF; + } +} + +static void _convertRowBGRA5(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { + uint16_t c = ((uint16_t*) pixelData)[x]; + row[x * 4] = (c << 2) & 0xF8; + row[x * 4 + 1] = (c >> 3) & 0xF8; + row[x * 4 + 2] = (c >> 8) & 0xF8; + row[x * 4 + 3] = (c & 1) * 0xFF; + } +} + +static void _convertRowRGB565(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { + uint16_t c = ((uint16_t*) pixelData)[x]; + row[x * 3] = (c >> 8) & 0xF8; + row[x * 3 + 1] = (c >> 3) & 0xFC; + row[x * 3 + 2] = (c << 3) & 0xF8; + } +} + +static void _convertRowBGR565(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { + uint16_t c = ((uint16_t*) pixelData)[x]; + row[x * 3] = (c << 3) & 0xF8; + row[x * 3 + 1] = (c >> 3) & 0xFC; + row[x * 3 + 2] = (c >> 8) & 0xF8; + } +} + +static void _convertRowBGR8(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { +#ifdef __BIG_ENDIAN__ + row[x * 3] = pixelData[x * 3 + 2]; + row[x * 3 + 1] = pixelData[x * 3 + 1]; + row[x * 3 + 2] = pixelData[x * 3]; +#else + row[x * 3] = pixelData[x * 3]; + row[x * 3 + 1] = pixelData[x * 3 + 1]; + row[x * 3 + 2] = pixelData[x * 3 + 2]; +#endif + } +} + +static void _convertRowRGB8(png_bytep row, const png_byte* pixelData, unsigned width) { + unsigned x; + for (x = 0; x < width; ++x) { +#ifdef __BIG_ENDIAN__ + row[x * 3] = pixelData[x * 3]; + row[x * 3 + 1] = pixelData[x * 3 + 1]; + row[x * 3 + 2] = pixelData[x * 3 + 2]; +#else + row[x * 3] = pixelData[x * 3 + 2]; + row[x * 3 + 1] = pixelData[x * 3 + 1]; + row[x * 3 + 2] = pixelData[x * 3]; +#endif + } +} + +bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels, enum mColorFormat fmt) { + int depth; + if (fmt == mCOLOR_L8) { + depth = 1; + } else if (mColorFormatHasAlpha(fmt)) { + depth = 4; + } else { + depth = 3; + } + png_bytep row = malloc(sizeof(png_byte) * width * depth); if (!row) { return false; } @@ -138,36 +370,71 @@ bool PNGWritePixelsA(png_structp png, unsigned width, unsigned height, unsigned free(row); return false; } + const png_byte* pixelRow = pixelData; + stride *= mColorFormatBytes(fmt); unsigned i; - for (i = 0; i < height; ++i) { - unsigned x; - for (x = 0; x < width; ++x) { -#ifdef COLOR_16_BIT - uint16_t c = ((uint16_t*) pixelData)[stride * i + x]; -#ifdef COLOR_5_6_5 - row[x * 4] = (c >> 8) & 0xF8; - row[x * 4 + 1] = (c >> 3) & 0xFC; - row[x * 4 + 2] = (c << 3) & 0xF8; - row[x * 4 + 3] = 0xFF; -#else - row[x * 4] = (c >> 7) & 0xF8; - row[x * 4 + 1] = (c >> 2) & 0xF8; - row[x * 4 + 2] = (c << 3) & 0xF8; - row[x * 4 + 3] = (c >> 15) * 0xFF; -#endif -#else -#ifdef __BIG_ENDIAN__ - row[x * 4] = pixelData[stride * i * 4 + x * 4 + 3]; - row[x * 4 + 1] = pixelData[stride * i * 4 + x * 4 + 2]; - row[x * 4 + 2] = pixelData[stride * i * 4 + x * 4 + 1]; - row[x * 4 + 3] = pixelData[stride * i * 4 + x * 4]; -#else - row[x * 4] = pixelData[stride * i * 4 + x * 4]; - row[x * 4 + 1] = pixelData[stride * i * 4 + x * 4 + 1]; - row[x * 4 + 2] = pixelData[stride * i * 4 + x * 4 + 2]; - row[x * 4 + 3] = pixelData[stride * i * 4 + x * 4 + 3]; -#endif -#endif + for (i = 0; i < height; ++i, pixelRow += stride) { + switch (fmt) { + case mCOLOR_XBGR8: + _convertRowXBGR8(row, pixelRow, width); + break; + case mCOLOR_XRGB8: + _convertRowXRGB8(row, pixelRow, width); + break; + case mCOLOR_BGRX8: + _convertRowBGRX8(row, pixelRow, width); + break; + case mCOLOR_RGBX8: + _convertRowRGBX8(row, pixelRow, width); + break; + case mCOLOR_ABGR8: + _convertRowABGR8(row, pixelRow, width); + break; + case mCOLOR_ARGB8: + _convertRowARGB8(row, pixelRow, width); + break; + case mCOLOR_BGRA8: + _convertRowBGRA8(row, pixelRow, width); + break; + case mCOLOR_RGBA8: + _convertRowRGBA8(row, pixelRow, width); + break; + case mCOLOR_RGB5: + _convertRowRGB5(row, pixelRow, width); + break; + case mCOLOR_BGR5: + _convertRowBGR5(row, pixelRow, width); + break; + case mCOLOR_ARGB5: + _convertRowARGB5(row, pixelRow, width); + break; + case mCOLOR_ABGR5: + _convertRowABGR5(row, pixelRow, width); + break; + case mCOLOR_RGBA5: + _convertRowRGBA5(row, pixelRow, width); + break; + case mCOLOR_BGRA5: + _convertRowBGRA5(row, pixelRow, width); + break; + case mCOLOR_RGB565: + _convertRowRGB565(row, pixelRow, width); + break; + case mCOLOR_BGR565: + _convertRowBGR565(row, pixelRow, width); + break; + case mCOLOR_BGR8: + _convertRowBGR8(row, pixelRow, width); + break; + case mCOLOR_RGB8: + _convertRowRGB8(row, pixelRow, width); + break; + case mCOLOR_L8: + memcpy(row, pixelRow, width); + break; + case mCOLOR_ANY: + // Invalid value + longjmp(png_jmpbuf(png), 1); } png_write_row(png, row); } diff --git a/src/util/test/image.c b/src/util/test/image.c index fde76e1c5..8ed1effee 100644 --- a/src/util/test/image.c +++ b/src/util/test/image.c @@ -551,6 +551,51 @@ M_TEST_DEFINE(savePngNonNative) { assert_int_equal(mImageGetPixel(image, 0, 0), 0x01234567); mImageDestroy(image); } + +M_TEST_DEFINE(savePngRoundTrip) { + 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_ARGB5, mCOLOR_ABGR5, + mCOLOR_RGBA5, mCOLOR_BGRA5, + mCOLOR_RGB565, mCOLOR_BGR565, + mCOLOR_RGB8, mCOLOR_BGR8, + 0 + }; + + int i; + for (i = 0; formats[i]; ++i) { + struct mImage* image = mImageCreate(2, 2, formats[i]); + mImageSetPixel(image, 0, 0, 0xFF181008); + mImageSetPixel(image, 1, 0, 0xFF100818); + mImageSetPixel(image, 0, 1, 0xFF081810); + mImageSetPixel(image, 1, 1, 0xFF181008); + assert_int_equal(mImageGetPixel(image, 0, 0), 0xFF181008); + assert_int_equal(mImageGetPixel(image, 1, 0), 0xFF100818); + assert_int_equal(mImageGetPixel(image, 0, 1), 0xFF081810); + assert_int_equal(mImageGetPixel(image, 1, 1), 0xFF181008); + + 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), 0xFF181008); + assert_int_equal(mImageGetPixel(image, 1, 0), 0xFF100818); + assert_int_equal(mImageGetPixel(image, 0, 1), 0xFF081810); + assert_int_equal(mImageGetPixel(image, 1, 1), 0xFF181008); + mImageDestroy(image); + } +} #endif M_TEST_DEFINE(convert1x1) { @@ -997,6 +1042,7 @@ M_TEST_SUITE_DEFINE(Image, cmocka_unit_test(loadPng32), cmocka_unit_test(savePngNative), cmocka_unit_test(savePngNonNative), + cmocka_unit_test(savePngRoundTrip), #endif cmocka_unit_test(convert1x1), cmocka_unit_test(convert2x1),