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
|
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 {
|
struct mImage {
|
||||||
void* data;
|
void* data;
|
||||||
unsigned width;
|
unsigned width;
|
||||||
|
|
|
@ -12,6 +12,8 @@ CXX_GUARD_START
|
||||||
|
|
||||||
#ifdef USE_PNG
|
#ifdef USE_PNG
|
||||||
|
|
||||||
|
#include <mgba-util/image.h>
|
||||||
|
|
||||||
// png.h defines its own version of restrict which conflicts with mGBA's.
|
// png.h defines its own version of restrict which conflicts with mGBA's.
|
||||||
#ifdef restrict
|
#ifdef restrict
|
||||||
#undef restrict
|
#undef restrict
|
||||||
|
@ -25,12 +27,10 @@ enum {
|
||||||
};
|
};
|
||||||
|
|
||||||
png_structp PNGWriteOpen(struct VFile* source);
|
png_structp PNGWriteOpen(struct VFile* source);
|
||||||
png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height);
|
png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height, enum mColorFormat);
|
||||||
png_infop PNGWriteHeaderA(png_structp png, unsigned width, unsigned height);
|
|
||||||
png_infop PNGWriteHeader8(png_structp png, unsigned width, unsigned height);
|
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 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 PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels, enum mColorFormat);
|
||||||
bool PNGWritePixelsA(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels);
|
|
||||||
bool PNGWritePixels8(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels);
|
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);
|
bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data);
|
||||||
void PNGWriteClose(png_structp png, png_infop info);
|
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->currentVideoSize(core, &width, &height);
|
||||||
core->getPixels(core, &pixels, &stride);
|
core->getPixels(core, &pixels, &stride);
|
||||||
png_structp png = PNGWriteOpen(vf);
|
png_structp png = PNGWriteOpen(vf);
|
||||||
png_infop info = PNGWriteHeader(png, width, height);
|
png_infop info = PNGWriteHeader(png, width, height, mCOLOR_NATIVE);
|
||||||
bool success = PNGWritePixels(png, width, height, stride, pixels);
|
bool success = PNGWritePixels(png, width, height, stride, pixels, mCOLOR_NATIVE);
|
||||||
PNGWriteClose(png, info);
|
PNGWriteClose(png, info);
|
||||||
return success;
|
return success;
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -178,13 +178,13 @@ static bool _savePNGState(struct mCore* core, struct VFile* vf, struct mStateExt
|
||||||
unsigned width, height;
|
unsigned width, height;
|
||||||
core->currentVideoSize(core, &width, &height);
|
core->currentVideoSize(core, &width, &height);
|
||||||
png_structp png = PNGWriteOpen(vf);
|
png_structp png = PNGWriteOpen(vf);
|
||||||
png_infop info = PNGWriteHeader(png, width, height);
|
png_infop info = PNGWriteHeader(png, width, height, mCOLOR_NATIVE);
|
||||||
if (!png || !info) {
|
if (!png || !info) {
|
||||||
PNGWriteClose(png, info);
|
PNGWriteClose(png, info);
|
||||||
free(buffer);
|
free(buffer);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
PNGWritePixels(png, width, height, stride, pixels);
|
PNGWritePixels(png, width, height, stride, pixels, mCOLOR_NATIVE);
|
||||||
PNGWriteCustomChunk(png, "gbAs", len, buffer);
|
PNGWriteCustomChunk(png, "gbAs", len, buffer);
|
||||||
if (extdata) {
|
if (extdata) {
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
|
@ -21,18 +21,18 @@ class PNG:
|
||||||
def write_header(self, image):
|
def write_header(self, image):
|
||||||
self._png = lib.PNGWriteOpen(self._vfile.handle)
|
self._png = lib.PNGWriteOpen(self._vfile.handle)
|
||||||
if self.mode == MODE_RGB:
|
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:
|
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:
|
if self.mode == MODE_INDEX:
|
||||||
self._info = lib.PNGWriteHeader8(self._png, image.width, image.height)
|
self._info = lib.PNGWriteHeader8(self._png, image.width, image.height)
|
||||||
return self._info != ffi.NULL
|
return self._info != ffi.NULL
|
||||||
|
|
||||||
def write_pixels(self, image):
|
def write_pixels(self, image):
|
||||||
if self.mode == MODE_RGB:
|
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:
|
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:
|
if self.mode == MODE_INDEX:
|
||||||
return lib.PNGWritePixels8(self._png, image.width, image.height, image.stride, image.buffer)
|
return lib.PNGWritePixels8(self._png, image.width, image.height, image.stride, image.buffer)
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -731,8 +731,8 @@ static struct VDir* _makeOutDir(const char* testName) {
|
||||||
|
|
||||||
static void _writeImage(struct VFile* vf, const struct CInemaImage* image) {
|
static void _writeImage(struct VFile* vf, const struct CInemaImage* image) {
|
||||||
png_structp png = PNGWriteOpen(vf);
|
png_structp png = PNGWriteOpen(vf);
|
||||||
png_infop info = PNGWriteHeader(png, image->width, image->height);
|
png_infop info = PNGWriteHeader(png, image->width, image->height, mCOLOR_NATIVE);
|
||||||
if (!PNGWritePixels(png, image->width, image->height, image->stride, image->data)) {
|
if (!PNGWritePixels(png, image->width, image->height, image->stride, image->data, mCOLOR_NATIVE)) {
|
||||||
CIerr(0, "Could not write output image\n");
|
CIerr(0, "Could not write output image\n");
|
||||||
}
|
}
|
||||||
PNGWriteClose(png, info);
|
PNGWriteClose(png, info);
|
||||||
|
|
|
@ -208,32 +208,13 @@ bool mImageSave(const struct mImage* image, const char* path, const char* format
|
||||||
|
|
||||||
#ifdef USE_PNG
|
#ifdef USE_PNG
|
||||||
bool mImageSavePNG(const struct mImage* image, struct VFile* vf) {
|
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_structp png = PNGWriteOpen(vf);
|
||||||
png_infop info = NULL;
|
png_infop info = NULL;
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
if (png) {
|
if (png) {
|
||||||
if (image->format == mCOLOR_XBGR8) {
|
info = PNGWriteHeader(png, image->width, image->height, image->format);
|
||||||
info = PNGWriteHeader(png, image->width, image->height);
|
|
||||||
if (info) {
|
if (info) {
|
||||||
ok = PNGWritePixels(png, image->width, image->height, image->stride, image->data);
|
ok = PNGWritePixels(png, image->width, image->height, image->stride, image->data, image->format);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
info = PNGWriteHeaderA(png, image->width, image->height);
|
|
||||||
if (info) {
|
|
||||||
ok = PNGWritePixelsA(png, image->width, image->height, image->stride, image->data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PNGWriteClose(png, info);
|
PNGWriteClose(png, info);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,12 +51,37 @@ static png_infop _pngWriteHeader(png_structp png, unsigned width, unsigned heigh
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height) {
|
png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height, enum mColorFormat fmt) {
|
||||||
return _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB);
|
int type;
|
||||||
}
|
switch (fmt) {
|
||||||
|
case mCOLOR_XBGR8:
|
||||||
png_infop PNGWriteHeaderA(png_structp png, unsigned width, unsigned height) {
|
case mCOLOR_XRGB8:
|
||||||
return _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB_ALPHA);
|
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) {
|
png_infop PNGWriteHeader8(png_structp png, unsigned width, unsigned height) {
|
||||||
|
@ -85,90 +110,332 @@ bool PNGWritePalette(png_structp png, png_infop info, const uint32_t* palette, u
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels) {
|
static void _convertRowXBGR8(png_bytep row, const png_byte* pixelData, unsigned width) {
|
||||||
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;
|
unsigned x;
|
||||||
for (x = 0; x < width; ++x) {
|
for (x = 0; x < width; ++x) {
|
||||||
#ifdef COLOR_16_BIT
|
#ifdef __BIG_ENDIAN__
|
||||||
uint16_t c = ((uint16_t*) pixelData)[stride * i + x];
|
row[x * 3] = pixelData[x * 4 + 3];
|
||||||
#ifdef COLOR_5_6_5
|
row[x * 3 + 1] = pixelData[x * 4 + 2];
|
||||||
row[x * 3] = (c >> 8) & 0xF8;
|
row[x * 3 + 2] = pixelData[x * 4 + 1];
|
||||||
row[x * 3 + 1] = (c >> 3) & 0xFC;
|
|
||||||
row[x * 3 + 2] = (c << 3) & 0xF8;
|
|
||||||
#else
|
#else
|
||||||
|
row[x * 3] = pixelData[x * 4];
|
||||||
|
row[x * 3 + 1] = pixelData[x * 4 + 1];
|
||||||
|
row[x * 3 + 2] = pixelData[x * 4 + 2];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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] = (c >> 7) & 0xF8;
|
||||||
row[x * 3 + 1] = (c >> 2) & 0xF8;
|
row[x * 3 + 1] = (c >> 2) & 0xF8;
|
||||||
row[x * 3 + 2] = (c << 3) & 0xF8;
|
row[x * 3 + 2] = (c << 3) & 0xF8;
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#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];
|
|
||||||
#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];
|
|
||||||
#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) {
|
static void _convertRowBGR5(png_bytep row, const png_byte* pixelData, unsigned width) {
|
||||||
png_bytep row = malloc(sizeof(png_byte) * width * 4);
|
|
||||||
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;
|
unsigned x;
|
||||||
for (x = 0; x < width; ++x) {
|
for (x = 0; x < width; ++x) {
|
||||||
#ifdef COLOR_16_BIT
|
uint16_t c = ((uint16_t*) pixelData)[x];
|
||||||
uint16_t c = ((uint16_t*) pixelData)[stride * i + x];
|
row[x * 3] = (c << 3) & 0xF8;
|
||||||
#ifdef COLOR_5_6_5
|
row[x * 3 + 1] = (c >> 2) & 0xF8;
|
||||||
row[x * 4] = (c >> 8) & 0xF8;
|
row[x * 3 + 2] = (c >> 7) & 0xF8;
|
||||||
row[x * 4 + 1] = (c >> 3) & 0xFC;
|
}
|
||||||
row[x * 4 + 2] = (c << 3) & 0xF8;
|
}
|
||||||
row[x * 4 + 3] = 0xFF;
|
|
||||||
#else
|
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] = (c >> 7) & 0xF8;
|
||||||
row[x * 4 + 1] = (c >> 2) & 0xF8;
|
row[x * 4 + 1] = (c >> 2) & 0xF8;
|
||||||
row[x * 4 + 2] = (c << 3) & 0xF8;
|
row[x * 4 + 2] = (c << 3) & 0xF8;
|
||||||
row[x * 4 + 3] = (c >> 15) * 0xFF;
|
row[x * 4 + 3] = (c >> 15) * 0xFF;
|
||||||
#endif
|
}
|
||||||
#else
|
}
|
||||||
|
|
||||||
|
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__
|
#ifdef __BIG_ENDIAN__
|
||||||
row[x * 4] = pixelData[stride * i * 4 + x * 4 + 3];
|
row[x * 3] = pixelData[x * 3 + 2];
|
||||||
row[x * 4 + 1] = pixelData[stride * i * 4 + x * 4 + 2];
|
row[x * 3 + 1] = pixelData[x * 3 + 1];
|
||||||
row[x * 4 + 2] = pixelData[stride * i * 4 + x * 4 + 1];
|
row[x * 3 + 2] = pixelData[x * 3];
|
||||||
row[x * 4 + 3] = pixelData[stride * i * 4 + x * 4];
|
|
||||||
#else
|
#else
|
||||||
row[x * 4] = pixelData[stride * i * 4 + x * 4];
|
row[x * 3] = pixelData[x * 3];
|
||||||
row[x * 4 + 1] = pixelData[stride * i * 4 + x * 4 + 1];
|
row[x * 3 + 1] = pixelData[x * 3 + 1];
|
||||||
row[x * 4 + 2] = pixelData[stride * i * 4 + x * 4 + 2];
|
row[x * 3 + 2] = pixelData[x * 3 + 2];
|
||||||
row[x * 4 + 3] = pixelData[stride * i * 4 + x * 4 + 3];
|
|
||||||
#endif
|
#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
|
#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;
|
||||||
|
}
|
||||||
|
const png_byte* pixelData = pixels;
|
||||||
|
if (setjmp(png_jmpbuf(png))) {
|
||||||
|
free(row);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const png_byte* pixelRow = pixelData;
|
||||||
|
stride *= mColorFormatBytes(fmt);
|
||||||
|
unsigned i;
|
||||||
|
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);
|
png_write_row(png, row);
|
||||||
}
|
}
|
||||||
free(row);
|
free(row);
|
||||||
|
|
|
@ -551,6 +551,51 @@ M_TEST_DEFINE(savePngNonNative) {
|
||||||
assert_int_equal(mImageGetPixel(image, 0, 0), 0x01234567);
|
assert_int_equal(mImageGetPixel(image, 0, 0), 0x01234567);
|
||||||
mImageDestroy(image);
|
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
|
#endif
|
||||||
|
|
||||||
M_TEST_DEFINE(convert1x1) {
|
M_TEST_DEFINE(convert1x1) {
|
||||||
|
@ -997,6 +1042,7 @@ M_TEST_SUITE_DEFINE(Image,
|
||||||
cmocka_unit_test(loadPng32),
|
cmocka_unit_test(loadPng32),
|
||||||
cmocka_unit_test(savePngNative),
|
cmocka_unit_test(savePngNative),
|
||||||
cmocka_unit_test(savePngNonNative),
|
cmocka_unit_test(savePngNonNative),
|
||||||
|
cmocka_unit_test(savePngRoundTrip),
|
||||||
#endif
|
#endif
|
||||||
cmocka_unit_test(convert1x1),
|
cmocka_unit_test(convert1x1),
|
||||||
cmocka_unit_test(convert2x1),
|
cmocka_unit_test(convert2x1),
|
||||||
|
|
Loading…
Reference in New Issue