mirror of https://github.com/mgba-emu/mgba.git
Util: PNGWrite*A is dead, long live PNGWrite*
This commit is contained in:
parent
225456a39c
commit
65f04ee408
|
@ -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;
|
||||
|
|
|
@ -12,6 +12,8 @@ CXX_GUARD_START
|
|||
|
||||
#ifdef USE_PNG
|
||||
|
||||
#include <mgba-util/image.h>
|
||||
|
||||
// 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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in New Issue