From a024b3c93f3d5a4b750aca415b30018b6baaa247 Mon Sep 17 00:00:00 2001 From: Christopher Roy Bratusek Date: Thu, 22 Mar 2012 20:37:28 +0100 Subject: [PATCH] updated PNGu --- source/pngu.c | 629 ++++++++++++++++++++++++++++++++++++-------------- source/pngu.h | 19 +- updates | 1 + 3 files changed, 475 insertions(+), 174 deletions(-) diff --git a/source/pngu.c b/source/pngu.c index 1a424a4..c19de93 100644 --- a/source/pngu.c +++ b/source/pngu.c @@ -6,23 +6,28 @@ Coder : frontier More info : http://frontier-dev.net -Modified by Tantric, 2009 - ********************************************************************************************/ #include #include -#include "pngu.h" +#include #include "png.h" +#include "pngu.h" +#include "pngu_impl.h" +#ifndef SAFE_FREE +#define SAFE_FREE(p) if(p){free(p);p=NULL;} +#endif +#if 0 +// moved to pngu_impl.h // Constants #define PNGU_SOURCE_BUFFER 1 #define PNGU_SOURCE_DEVICE 2 - // Prototypes of helper functions int pngu_info (IMGCTX ctx); int pngu_decode (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stripAlpha); +int pngu_decode_add_alpha (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stripAlpha, int force32bit); void pngu_free_info (IMGCTX ctx); void pngu_read_data_from_buffer (png_structp png_ptr, png_bytep data, png_size_t length); void pngu_write_data_to_buffer (png_structp png_ptr, png_bytep data, png_size_t length); @@ -37,6 +42,7 @@ struct _IMGCTX void *buffer; char *filename; PNGU_u32 cursor; + PNGU_u32 buf_size; // buffer size PNGU_u32 propRead; PNGUPROP prop; @@ -49,18 +55,19 @@ struct _IMGCTX png_bytep *row_pointers; png_bytep img_data; }; +#endif // PNGU Implementation // -IMGCTX PNGU_SelectImageFromBuffer (const void *buffer) +IMGCTX PNGU_SelectImageFromBufferX (const void *buffer, int size) { IMGCTX ctx = NULL; if (!buffer) return NULL; - ctx = malloc (sizeof (struct _IMGCTX)); + ctx = calloc (sizeof (struct _IMGCTX), 1); if (!ctx) return NULL; @@ -70,10 +77,16 @@ IMGCTX PNGU_SelectImageFromBuffer (const void *buffer) ctx->filename = NULL; ctx->propRead = 0; ctx->infoRead = 0; + ctx->buf_size = size; return ctx; } +IMGCTX PNGU_SelectImageFromBuffer (const void *buffer) +{ + return PNGU_SelectImageFromBufferX(buffer, 0); +} + IMGCTX PNGU_SelectImageFromDevice (const char *filename) { @@ -82,7 +95,7 @@ IMGCTX PNGU_SelectImageFromDevice (const char *filename) if (!filename) return NULL; - ctx = malloc (sizeof (struct _IMGCTX)); + ctx = calloc (sizeof (struct _IMGCTX), 1); if (!ctx) return NULL; @@ -685,6 +698,361 @@ int PNGU_DecodeTo4x4RGBA8 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *bu } +//######################################################################################## +//---------- Start CMPR added section --------------------------------------------------- +//######################################################################################## + +static inline PNGU_u16 rgb8ToRGB565(PNGU_u8 *color) +{ + return ((color[0] >> 3) << 11) | ((color[1] >> 2) << 5) | (color[2] >> 3); +} + +static int colorDistance(const PNGU_u8 *c0, const PNGU_u8 *c1) +{ + return (c1[0] - c0[0]) * (c1[0] - c0[0]) + (c1[1] - c0[1]) * (c1[1] - c0[1]) + (c1[2] - c0[2]) * (c1[2] - c0[2]); +} + +static void getBaseColors(PNGU_u8 *color0, PNGU_u8 *color1, const PNGU_u8 *srcBlock) +{ + int maxDistance = -1; + int i; + int j; + + for (i = 0; i < 15; ++i) + for (j = i + 1; j < 16; ++j) + { + int distance = colorDistance(srcBlock + i * 4, srcBlock + j * 4); + if (distance > maxDistance) + { + maxDistance = distance; + *(PNGU_u32 *)color0 = ((PNGU_u32 *)srcBlock)[i]; + *(PNGU_u32 *)color1 = ((PNGU_u32 *)srcBlock)[j]; + } + } + if (rgb8ToRGB565(color0) < rgb8ToRGB565(color1)) + { + PNGU_u32 tmp; + tmp = *(PNGU_u32 *)color0; + *(PNGU_u32 *)color0 = *(PNGU_u32 *)color1; + *(PNGU_u32 *)color1 = tmp; + } +} + +static PNGU_u32 colorIndices(const PNGU_u8 *color0, const PNGU_u8 *color1, const PNGU_u8 *srcBlock) +{ + PNGU_u16 colors[4][4]; + PNGU_u32 res = 0; + int i; + + // Make the 4 colors available in the block + colors[0][0] = (color0[0] & 0xF8) | (color0[0] >> 5); + colors[0][1] = (color0[1] & 0xFC) | (color0[1] >> 6); + colors[0][2] = (color0[2] & 0xF8) | (color0[2] >> 5); + colors[1][0] = (color1[0] & 0xF8) | (color1[0] >> 5); + colors[1][1] = (color1[1] & 0xFC) | (color1[1] >> 6); + colors[1][2] = (color1[2] & 0xF8) | (color1[2] >> 5); + colors[2][0] = (2 * colors[0][0] + 1 * colors[1][0]) / 3; + colors[2][1] = (2 * colors[0][1] + 1 * colors[1][1]) / 3; + colors[2][2] = (2 * colors[0][2] + 1 * colors[1][2]) / 3; + colors[3][0] = (1 * colors[0][0] + 2 * colors[1][0]) / 3; + colors[3][1] = (1 * colors[0][1] + 2 * colors[1][1]) / 3; + colors[3][2] = (1 * colors[0][2] + 2 * colors[1][2]) / 3; + for (i = 15; i >= 0; --i) + { + int c0 = srcBlock[i * 4 + 0]; + int c1 = srcBlock[i * 4 + 1]; + int c2 = srcBlock[i * 4 + 2]; + int d0 = abs(colors[0][0] - c0) + abs(colors[0][1] - c1) + abs(colors[0][2] - c2); + int d1 = abs(colors[1][0] - c0) + abs(colors[1][1] - c1) + abs(colors[1][2] - c2); + int d2 = abs(colors[2][0] - c0) + abs(colors[2][1] - c1) + abs(colors[2][2] - c2); + int d3 = abs(colors[3][0] - c0) + abs(colors[3][1] - c1) + abs(colors[3][2] - c2); + int b0 = d0 > d3; + int b1 = d1 > d2; + int b2 = d0 > d2; + int b3 = d1 > d3; + int b4 = d2 > d3; + int x0 = b1 & b2; + int x1 = b0 & b3; + int x2 = b0 & b4; + res |= (x2 | ((x0 | x1) << 1)) << ((15 - i) << 1); + } + return res; +} + +int PNGU_DecodeToCMPR_Trim(IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer) +{ + int result; + PNGU_u8 srcBlock[16 * 4]; + PNGU_u8 color0[4]; + PNGU_u8 color1[4]; + PNGU_u8 *outBuf = (PNGU_u8 *)buffer; + int ii; + int jj; + int k; + + //check for alpha channel + result = pngu_decode_add_alpha (ctx, width, height, 0, 1); + if (result != PNGU_OK) + return result; + + // trim down + width = width & ~7u; + height = height & ~7u; + + // Alpha channel present, copy image to the output buffer + for (jj = 0; jj < height; jj += 8) + for (ii = 0; ii < width; ii += 8) + for (k = 0; k < 4; ++k) + { + int j = jj + ((k >> 1) << 2); // jj + 0, jj + 0, jj + 4, jj + 4 + int i = ii + ((k & 1) << 2); // ii + 0, ii + 4, ii + 0, ii + 4 + memcpy(srcBlock, ctx->row_pointers[j] + i * 4, 16); + memcpy(srcBlock + 4 * 4, ctx->row_pointers[j + 1] + i * 4, 16); + memcpy(srcBlock + 8 * 4, ctx->row_pointers[j + 2] + i * 4, 16); + memcpy(srcBlock + 12 * 4, ctx->row_pointers[j + 3] + i * 4, 16); + getBaseColors(color0, color1, srcBlock); + *(PNGU_u16 *)outBuf = rgb8ToRGB565(color0); + outBuf += 2; + *(PNGU_u16 *)outBuf = rgb8ToRGB565(color1); + outBuf += 2; + *(PNGU_u32 *)outBuf = colorIndices(color0, color1, srcBlock); + outBuf += 4; + } + // Free resources + free (ctx->img_data); + free (ctx->row_pointers); + + // Success + return PNGU_OK; +} + + +// if width or height is not divisible by 8 +// then the remaining will be padded with last row/column +// buffer must be allocated with width and height rounded up + +int PNGU_DecodeToCMPR_Pad(IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer) +{ + int result; + PNGU_u8 srcBlock[16 * 4]; + PNGU_u8 color0[4]; + PNGU_u8 color1[4]; + PNGU_u8 *outBuf = (PNGU_u8 *)buffer; + int ii; + int jj; + int k; + + //check for alpha channel + result = pngu_decode_add_alpha (ctx, width, height, 0, 1); + if (result != PNGU_OK) + return result; + + // Alpha channel present, copy image to the output buffer + for (jj = 0; jj < height; jj += 8) { + for (ii = 0; ii < width; ii += 8) { + for (k = 0; k < 4; ++k) { + // k(i,j) + // 0(0,0) 1(4,0) + // 2(4,0) 3(4,4) + int i = ii + ((k & 1) << 2); // ii + 0, ii + 4, ii + 0, ii + 4 + int j = jj + ((k >> 1) << 2); // jj + 0, jj + 0, jj + 4, jj + 4 + int ny; // 4 lines + int px = 4; // num columns to copy + if (i >= width) i = width - 1; + if (i + px > width) px = width - i; + for (ny=0; ny<4; ny++) { + if (j >= height) j = height - 1; + memcpy(srcBlock + ny * 4 * 4, + ctx->row_pointers[j] + i * 4, px * 4); + if (px < 4) { + // repeat last column (4-px) times + int x = width - 1; + int nx; + for (nx = px; nx < 4; nx++) { + memcpy(srcBlock + ny * 4 * 4 + nx * 4, + ctx->row_pointers[j] + x * 4, 4); + } + } + j++; + } + getBaseColors(color0, color1, srcBlock); + *(PNGU_u16 *)outBuf = rgb8ToRGB565(color0); + outBuf += 2; + *(PNGU_u16 *)outBuf = rgb8ToRGB565(color1); + outBuf += 2; + *(PNGU_u32 *)outBuf = colorIndices(color0, color1, srcBlock); + outBuf += 4; + } + } + } + // Free resources + free (ctx->img_data); + free (ctx->row_pointers); + + // Success + return PNGU_OK; +} + +void ExtractBlock( PNGU_u8 *inPtr, int y, int x, PNGU_u32 width, int i, PNGU_u8 colorBlock[] ) { + PNGU_u32 offset; + PNGU_u8 r, g, b, a; + + offset = (((y >> 2)<<4)*width) + ((x >> 2)<<6) + ((((y&3) << 2) + (x&3) ) << 1); + //offset = (((y >> 2) << 4)*width) + ((x >> 2) << 6) + (((y % 4 << 2) + x % 4) << 1); + //get rgba values based on the RGBA8 offsets + a = *(inPtr+offset); + r = *(inPtr+offset+1); + g = *(inPtr+offset+32); + b = *(inPtr+offset+33); + colorBlock[i*4] = r; + colorBlock[i*4+1] = g; + colorBlock[i*4+2] = b; + colorBlock[i*4+3] = a; + +} + +/** + * by usptactical + * Converts a 4x4 RGBA8 image to CMPR. + */ +int PNGU_4x4RGBA8_To_CMPR(void *buf_rgb, PNGU_u32 width, PNGU_u32 height, void *buf_cmpr) +{ + PNGU_u8 srcBlock[16 * 4]; + PNGU_u8 color0[4]; + PNGU_u8 color1[4]; + PNGU_u8 *outBuf = (PNGU_u8 *)buf_cmpr; + PNGU_u8 *rgba = (PNGU_u8 *)buf_rgb; + int jj, ii, i, j, k; + + width = width & ~7u; + height = height & ~7u; + + // loop over blocks + //CMPR needs 4x4 block of pixels: + //image row 0: 0, 1, 2, 3 (first 16 block) + //image row 1: 0, 1, 2, 3 (second 16 block) + //image row 2: 0, 1, 2, 3 (third 16 block) + //image row 3: 0, 1, 2, 3 (last 16 block) + + //image row 0: 4, 5, 6, 7 (first 16 block) + //image row 1: 4, 5, 6, 7 (second 16 block) + //image row 2: 4, 5, 6, 7 (third 16 block) + //image row 3: 4, 5, 6, 7 (last 16 block) + + //image row 4: 0, 1, 2, 3 (first 16 block) + //image row 5: 0, 1, 2, 3 (second 16 block) + //image row 6: 0, 1, 2, 3 (third 16 block) + //image row 7: 0, 1, 2, 3 (last 16 block) + + //image row 4: 4, 5, 6, 7 (first 16 block) + //image row 5: 4, 5, 6, 7 (second 16 block) + //image row 6: 4, 5, 6, 7 (third 16 block) + //image row 7: 4, 5, 6, 7 (last 16 block) + + for(jj = 0; jj < height; jj += 8) + for(ii = 0; ii < width; ii += 8) + for (k=0; k < 4; k++) + { + j = jj + ((k >> 1) << 2); // jj + 0, jj + 0, jj + 4, jj + 4 + i = ii + ((k & 1) << 2); // ii + 0, ii + 4, ii + 0, ii + 4 + + ExtractBlock(rgba, j, i, width, 0, srcBlock); + ExtractBlock(rgba, j, i+1, width, 1, srcBlock); + ExtractBlock(rgba, j, i+2, width, 2, srcBlock); + ExtractBlock(rgba, j, i+3, width, 3, srcBlock); + + ExtractBlock(rgba, j+1, i, width, 4, srcBlock); + ExtractBlock(rgba, j+1, i+1, width, 5, srcBlock); + ExtractBlock(rgba, j+1, i+2, width, 6, srcBlock); + ExtractBlock(rgba, j+1, i+3, width, 7, srcBlock); + + ExtractBlock(rgba, j+2, i, width, 8, srcBlock); + ExtractBlock(rgba, j+2, i+1, width, 9, srcBlock); + ExtractBlock(rgba, j+2, i+2, width, 10, srcBlock); + ExtractBlock(rgba, j+2, i+3, width, 11, srcBlock); + + ExtractBlock(rgba, j+3, i, width, 12, srcBlock); + ExtractBlock(rgba, j+3, i+1, width, 13, srcBlock); + ExtractBlock(rgba, j+3, i+2, width, 14, srcBlock); + ExtractBlock(rgba, j+3, i+3, width, 15, srcBlock); + + getBaseColors(color0, color1, srcBlock); + *(PNGU_u16 *)outBuf = rgb8ToRGB565(color0); + outBuf += 2; + *(PNGU_u16 *)outBuf = rgb8ToRGB565(color1); + outBuf += 2; + *(PNGU_u32 *)outBuf = colorIndices(color0, color1, srcBlock); + outBuf += 4; + } + // Success + return PNGU_OK; +} + +// if width or height is not divisible by 8 +// then the remaining will be padded with last row/column +// buffer must be allocated with width and height rounded up +int PNGU_RGBA8_To_CMPR(void *buf_rgb, PNGU_u32 width, PNGU_u32 height, void *buf_cmpr) +{ + PNGU_u8 srcBlock[16 * 4]; + PNGU_u8 color0[4]; + PNGU_u8 color1[4]; + PNGU_u8 *src, *block; + PNGU_u8 *cmpr = (PNGU_u8 *)buf_cmpr; + PNGU_u8 *rgba = (PNGU_u8 *)buf_rgb; + int jj, ii, i, j, k; + int x, y; // counter + int px, py; // pixel coord + + for(jj = 0; jj < height; jj += 8) { + for(ii = 0; ii < width; ii += 8) { + for (k=0; k < 4; k++) { + i = ii + ((k & 1) << 2); // ii + 0, ii + 4, ii + 0, ii + 4 + j = jj + ((k >> 1) << 2); // jj + 0, jj + 0, jj + 4, jj + 4 + + block = srcBlock; + for (y=0; y<4; y++) { + py = j + y; + if (py >= height) py = height - 1; + src = rgba + py * width * 4; + for (x=0; x<4; x++) { + px = i + x; + if (px >= width) px = width - 1; + memcpy(block, src + px * 4, 4); + block += 4; + } + } + + getBaseColors(color0, color1, srcBlock); + *(PNGU_u16 *)cmpr = rgb8ToRGB565(color0); + cmpr += 2; + *(PNGU_u16 *)cmpr = rgb8ToRGB565(color1); + cmpr += 2; + *(PNGU_u32 *)cmpr = colorIndices(color0, color1, srcBlock); + cmpr += 4; + } + } + } + // Success + return PNGU_OK; +} + + +/** + * added by usptactical + * handles png error messages + */ +void user_error (png_structp png_ptr, png_const_charp c) +{ + longjmp(png_jmpbuf(png_ptr), 1); +} + + + +//######################################################################################## +//---------- End CMPR added section ----------------------------------------------------- +//######################################################################################## + + int PNGU_EncodeFromYCbYCr (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride) { png_uint_32 rowbytes; @@ -709,21 +1077,21 @@ int PNGU_EncodeFromYCbYCr (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *bu // Allocation of libpng structs ctx->png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!(ctx->png_ptr)) + if (!(ctx->png_ptr)) { if (ctx->source == PNGU_SOURCE_DEVICE) fclose (ctx->fd); - return PNGU_LIB_ERROR; + return PNGU_LIB_ERROR; } - ctx->info_ptr = png_create_info_struct (ctx->png_ptr); - if (!(ctx->info_ptr)) - { + ctx->info_ptr = png_create_info_struct (ctx->png_ptr); + if (!(ctx->info_ptr)) + { png_destroy_write_struct (&(ctx->png_ptr), (png_infopp)NULL); if (ctx->source == PNGU_SOURCE_DEVICE) fclose (ctx->fd); - return PNGU_LIB_ERROR; - } + return PNGU_LIB_ERROR; + } if (ctx->source == PNGU_SOURCE_BUFFER) { @@ -738,7 +1106,7 @@ int PNGU_EncodeFromYCbYCr (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *bu } // Setup output file properties - png_set_IHDR (ctx->png_ptr, ctx->info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, + png_set_IHDR (ctx->png_ptr, ctx->info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); // Allocate memory to store the image in RGB format @@ -797,138 +1165,6 @@ int PNGU_EncodeFromYCbYCr (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *bu return PNGU_OK; } -int PNGU_EncodeFromRGB (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride) -{ - png_uint_32 rowbytes; - PNGU_u32 y; - - // Erase from the context any readed info - pngu_free_info (ctx); - ctx->propRead = 0; - - // Check if the user has selected a file to write the image - if (ctx->source == PNGU_SOURCE_BUFFER); - - else if (ctx->source == PNGU_SOURCE_DEVICE) - { - // Open file - if (!(ctx->fd = fopen (ctx->filename, "wb"))) - return PNGU_CANT_OPEN_FILE; - } - - else - return PNGU_NO_FILE_SELECTED; - - // Allocation of libpng structs - ctx->png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!(ctx->png_ptr)) - { - if (ctx->source == PNGU_SOURCE_DEVICE) - fclose (ctx->fd); - return PNGU_LIB_ERROR; - } - - ctx->info_ptr = png_create_info_struct (ctx->png_ptr); - if (!(ctx->info_ptr)) - { - png_destroy_write_struct (&(ctx->png_ptr), (png_infopp)NULL); - if (ctx->source == PNGU_SOURCE_DEVICE) - fclose (ctx->fd); - return PNGU_LIB_ERROR; - } - - if (ctx->source == PNGU_SOURCE_BUFFER) - { - // Installation of our custom data writer function - ctx->cursor = 0; - png_set_write_fn (ctx->png_ptr, ctx, pngu_write_data_to_buffer, pngu_flush_data_to_buffer); - } - else if (ctx->source == PNGU_SOURCE_DEVICE) - { - // Default data writer uses function fwrite, so it needs to use our FILE* - png_init_io (ctx->png_ptr, ctx->fd); - } - - // Setup output file properties - png_set_IHDR (ctx->png_ptr, ctx->info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, - PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - - // Allocate memory to store the image in RGB format - rowbytes = width * 3; - if (rowbytes % 4) - rowbytes = ((rowbytes / 4) + 1) * 4; // Add extra padding so each row starts in a 4 byte boundary - - ctx->img_data = malloc(rowbytes * height); - memset(ctx->img_data, 0, rowbytes * height); - - if (!ctx->img_data) - { - png_destroy_write_struct (&(ctx->png_ptr), (png_infopp)NULL); - if (ctx->source == PNGU_SOURCE_DEVICE) - fclose (ctx->fd); - return PNGU_LIB_ERROR; - } - - ctx->row_pointers = malloc (sizeof (png_bytep) * height); - memset(ctx->row_pointers, 0, sizeof (png_bytep) * height); - - if (!ctx->row_pointers) - { - png_destroy_write_struct (&(ctx->png_ptr), (png_infopp)NULL); - if (ctx->source == PNGU_SOURCE_DEVICE) - fclose (ctx->fd); - return PNGU_LIB_ERROR; - } - - for (y = 0; y < height; y++) - { - ctx->row_pointers[y] = buffer + (y * rowbytes); - } - - // Tell libpng where is our image data - png_set_rows (ctx->png_ptr, ctx->info_ptr, ctx->row_pointers); - - // Write file header and image data - png_write_png (ctx->png_ptr, ctx->info_ptr, PNG_TRANSFORM_IDENTITY, NULL); - - // Tell libpng we have no more data to write - png_write_end (ctx->png_ptr, (png_infop) NULL); - - // Free resources - free (ctx->img_data); - free (ctx->row_pointers); - png_destroy_write_struct (&(ctx->png_ptr), &(ctx->info_ptr)); - if (ctx->source == PNGU_SOURCE_DEVICE) - fclose (ctx->fd); - - // Success - return ctx->cursor; -} - -int PNGU_EncodeFromGXTexture (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride) -{ - int x,y,res; - unsigned char * ptr = (unsigned char*)buffer; - unsigned char * tmpbuffer = (unsigned char *)malloc(width*height*3); - memset(tmpbuffer, 0, width*height*3); - png_uint_32 offset; - - for(y=0; y < height; y++) - { - for(x=0; x < width; x++) - { - offset = (((y >> 2)<<4)*width) + ((x >> 2)<<6) + (((y%4 << 2) + x%4 ) << 1); - - tmpbuffer[y*640*3+x*3] = ptr[offset+1]; // R - tmpbuffer[y*640*3+x*3+1] = ptr[offset+32]; // G - tmpbuffer[y*640*3+x*3+2] = ptr[offset+33]; // B - } - } - - res = PNGU_EncodeFromRGB (ctx, width, height, tmpbuffer, stride); - free(tmpbuffer); - return res; -} // This function is taken from a libogc example PNGU_u32 PNGU_RGB8_TO_YCbYCr (PNGU_u8 r1, PNGU_u8 g1, PNGU_u8 b1, PNGU_u8 r2, PNGU_u8 g2, PNGU_u8 b2) @@ -990,7 +1226,7 @@ int pngu_info (IMGCTX ctx) return PNGU_CANT_OPEN_FILE; // Load first 8 bytes into magic buffer - if (fread (magic, 1, 8, ctx->fd) != 8) + if (fread (magic, 1, 8, ctx->fd) != 8) { fclose (ctx->fd); return PNGU_CANT_READ_FILE; @@ -1009,21 +1245,21 @@ int pngu_info (IMGCTX ctx) // Allocation of libpng structs ctx->png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!(ctx->png_ptr)) + if (!(ctx->png_ptr)) { if (ctx->source == PNGU_SOURCE_DEVICE) fclose (ctx->fd); - return PNGU_LIB_ERROR; + return PNGU_LIB_ERROR; } - ctx->info_ptr = png_create_info_struct (ctx->png_ptr); - if (!(ctx->info_ptr)) - { + ctx->info_ptr = png_create_info_struct (ctx->png_ptr); + if (!(ctx->info_ptr)) + { if (ctx->source == PNGU_SOURCE_DEVICE) fclose (ctx->fd); - png_destroy_read_struct (&(ctx->png_ptr), (png_infopp)NULL, (png_infopp)NULL); - return PNGU_LIB_ERROR; - } + png_destroy_read_struct (&(ctx->png_ptr), (png_infopp)NULL, (png_infopp)NULL); + return PNGU_LIB_ERROR; + } if (ctx->source == PNGU_SOURCE_BUFFER) { @@ -1140,10 +1376,15 @@ int pngu_info (IMGCTX ctx) } -int pngu_decode (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stripAlpha) +int pngu_decode_add_alpha (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stripAlpha, int force32bit) { png_uint_32 rowbytes; int i; + int chunk; + int rowsLeft; + png_bytep *curRow; + int mem_err = 0; + // Read info if it hasn't been read before if (!ctx->infoRead) @@ -1161,22 +1402,43 @@ int pngu_decode (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stripAlph if ( (ctx->prop.imgColorType == PNGU_COLOR_TYPE_PALETTE) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_UNKNOWN) ) return PNGU_UNSUPPORTED_COLOR_TYPE; + //************************************************* + //* added by usptactical to catch corrupted pngs * + jmp_buf save_jmp; + memcpy(save_jmp, png_jmpbuf(ctx->png_ptr), sizeof(save_jmp)); + if (setjmp(png_jmpbuf(ctx->png_ptr))) { + error: + memcpy(png_jmpbuf(ctx->png_ptr), save_jmp, sizeof(save_jmp)); + SAFE_FREE(ctx->row_pointers); + SAFE_FREE(ctx->img_data); + pngu_free_info (ctx); + //printf("*** This is a corrupted image!!\n"); sleep(5); + return (mem_err)?PNGU_LIB_ERROR:-666; + } + //override default error handler to suppress warning messages from libpng + png_set_error_fn (ctx->png_ptr, NULL, user_error, user_error); + //************************************************* + // Scale 16 bit samples to 8 bit if (ctx->prop.imgBitDepth == 16) - png_set_strip_16 (ctx->png_ptr); + png_set_strip_16 (ctx->png_ptr); // Remove alpha channel if we don't need it if (stripAlpha && ((ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB_ALPHA) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY_ALPHA))) - png_set_strip_alpha (ctx->png_ptr); + png_set_strip_alpha (ctx->png_ptr); // Expand 1, 2 and 4 bit samples to 8 bit if (ctx->prop.imgBitDepth < 8) - png_set_packing (ctx->png_ptr); + png_set_packing (ctx->png_ptr); // Transform grayscale images to RGB if ( (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY_ALPHA) ) png_set_gray_to_rgb (ctx->png_ptr); + // Transform RBG images to RGBA + if (force32bit && (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY || ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB)) + png_set_filler(ctx->png_ptr, 0xFF, PNG_FILLER_AFTER); + // Flush transformations png_read_update_info (ctx->png_ptr, ctx->info_ptr); @@ -1188,23 +1450,44 @@ int pngu_decode (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stripAlph ctx->img_data = malloc (rowbytes * ctx->prop.imgHeight); if (!ctx->img_data) { - pngu_free_info (ctx); - return PNGU_LIB_ERROR; + //pngu_free_info (ctx); + //return PNGU_LIB_ERROR; + mem_err = 1; + goto error; } ctx->row_pointers = malloc (sizeof (png_bytep) * ctx->prop.imgHeight); if (!ctx->row_pointers) { - free (ctx->img_data); - pngu_free_info (ctx); - return PNGU_LIB_ERROR; + //free (ctx->img_data); + //pngu_free_info (ctx); + //return PNGU_LIB_ERROR; + mem_err = 1; + goto error; } - for (i = 0; i < ctx->prop.imgHeight; i++) + for (i = 0; i < (int)ctx->prop.imgHeight; i++) ctx->row_pointers[i] = ctx->img_data + (i * rowbytes); // Transform the image and copy it to our allocated memory - png_read_image (ctx->png_ptr, ctx->row_pointers); + if (png_get_interlace_type(ctx->png_ptr, ctx->info_ptr) != PNG_INTERLACE_NONE) + png_read_image (ctx->png_ptr, ctx->row_pointers); + else + { + rowsLeft = ctx->prop.imgHeight; + curRow = ctx->row_pointers; + while (rowsLeft > 0) + { + chunk = rowsLeft > 0x80 ? 0x80 : rowsLeft; + png_read_rows(ctx->png_ptr, curRow, NULL, chunk); + //usleep(1000); + curRow += chunk; + rowsLeft -= chunk; + } + } + + // restore default error handling + memcpy(png_jmpbuf(ctx->png_ptr), save_jmp, sizeof(save_jmp)); // Free resources pngu_free_info (ctx); @@ -1213,6 +1496,11 @@ int pngu_decode (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stripAlph return PNGU_OK; } +int pngu_decode (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stripAlpha) +{ + return pngu_decode_add_alpha(ctx, width, height, stripAlpha, 0); +} + void pngu_free_info (IMGCTX ctx) { @@ -1232,6 +1520,13 @@ void pngu_free_info (IMGCTX ctx) void pngu_read_data_from_buffer (png_structp png_ptr, png_bytep data, png_size_t length) { IMGCTX ctx = (IMGCTX) png_get_io_ptr (png_ptr); + if (ctx->buf_size && (ctx->cursor + length > ctx->buf_size)) + { + static char err_str[40]; + snprintf(err_str, sizeof(err_str), "read error (%x/%x)", + ctx->cursor + length, ctx->buf_size); + png_error(png_ptr, err_str); + } memcpy (data, ctx->buffer + ctx->cursor, length); ctx->cursor += length; } diff --git a/source/pngu.h b/source/pngu.h index cc8fcd8..bbffc65 100644 --- a/source/pngu.h +++ b/source/pngu.h @@ -6,8 +6,6 @@ Coder : frontier More info : http://frontier-dev.net -Modified by Tantric, 2009 - ********************************************************************************************/ #ifndef __PNGU__ #define __PNGU__ @@ -92,6 +90,7 @@ void PNGU_YCbYCr_TO_RGB8 (PNGU_u32 ycbycr, PNGU_u8 *r1, PNGU_u8 *g1, PNGU_u8 *b1 // Selects a PNG file, previosly loaded into a buffer, and creates an image context for subsequent procesing. IMGCTX PNGU_SelectImageFromBuffer (const void *buffer); +IMGCTX PNGU_SelectImageFromBufferX (const void *buffer, int size); // Selects a PNG file, from any devoptab device, and creates an image context for subsequent procesing. IMGCTX PNGU_SelectImageFromDevice (const char *filename); @@ -140,12 +139,17 @@ int PNGU_DecodeToRGBA8 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffe // Macro for decoding an image inside a buffer at given coordinates. #define PNGU_DECODE_TO_COORDS_RGBA8(ctx,coordX,coordY,imgWidth,imgHeight,default_alpha,bufferWidth,bufferHeight,buffer) \ \ - PNGU_DecodeToRGBA8 (ctx, imgWidth, imgHeight, ((void *) buffer) + (coordY) * (bufferWidth) * 2 + \ - (coordX) * 2, (bufferWidth) - (imgWidth), default_alpha) + PNGU_DecodeToRGBA8 (ctx, imgWidth, imgHeight, ((void *) buffer) + (coordY) * (bufferWidth) * 4 + \ + (coordX) * 4, (bufferWidth) - (imgWidth), default_alpha) // Expands selected image into a 4x4 tiled RGB565 buffer. You need to specify context, image dimensions // and destination address. int PNGU_DecodeTo4x4RGB565 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer); +// Compressed version (DXT1/CMPR) +int PNGU_DecodeToCMPR_Trim(IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer); +int PNGU_DecodeToCMPR_Pad(IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer); +int PNGU_4x4RGBA8_To_CMPR(void *buf_rgb, PNGU_u32 width, PNGU_u32 height, void *buf_cmpr); +int PNGU_RGBA8_To_CMPR(void *buf_rgb, PNGU_u32 width, PNGU_u32 height, void *buf_cmpr); // Expands selected image into a 4x4 tiled RGB5A3 buffer. You need to specify context, image dimensions, // destination address and default alpha value, which is used if the source image doesn't have an alpha channel. @@ -159,15 +163,16 @@ int PNGU_DecodeTo4x4RGBA8 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *bu // specify context, image dimensions, destination address and stride in pixels (stride = buffer width - image width). int PNGU_EncodeFromYCbYCr (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride); -int PNGU_EncodeFromRGB (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride); -int PNGU_EncodeFromGXTexture (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride); - // Macro for encoding an image stored into an YCbYCr buffer at given coordinates. #define PNGU_ENCODE_TO_COORDS_YCbYCr(ctx,coordX,coordY,imgWidth,imgHeight,bufferWidth,bufferHeight,buffer) \ \ PNGU_EncodeFromYCbYCr (ctx, imgWidth, imgHeight, ((void *) buffer) + (coordY) * (bufferWidth) * 2 + \ (coordX) * 2, (bufferWidth) - (imgWidth)) +PNGU_u8 * PNGU_DecodeTo4x4RGBA8_EX (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, int * dstWidth, int * dstHeight, PNGU_u8 *dstPtr); + +int PNGU_EncodeFromEFB (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stride); + #ifdef __cplusplus } #endif diff --git a/updates b/updates index c502538..e0562bb 100644 --- a/updates +++ b/updates @@ -10,6 +10,7 @@ - BUGFIX: compiled with libpng to fix a problem in PNG handling - installer now supports Classic-Controller - installer now supports GameCube-Controller +- updated PNGu //rev37: - improved update-mechanism: