From c9261527620fb7dfd7405d69e49c7d12ee173b76 Mon Sep 17 00:00:00 2001 From: dborth Date: Sun, 21 Mar 2010 19:51:36 +0000 Subject: [PATCH] new font, rewritten PNGU, menu tweaks, optimized text code --- source/ngc/fonts/font.ttf | Bin 29244 -> 55620 bytes source/ngc/gui/gui_filebrowser.cpp | 2 +- source/ngc/gui/gui_imagedata.cpp | 35 +- source/ngc/gui/gui_text.cpp | 238 ++++----- source/ngc/menu.cpp | 2 +- source/ngc/pngu.c | 755 ++++++++++++++--------------- source/ngc/pngu.h | 50 +- 7 files changed, 490 insertions(+), 592 deletions(-) diff --git a/source/ngc/fonts/font.ttf b/source/ngc/fonts/font.ttf index 934f65ee6e0d9dbfe4919f3a67374ed4a17b1778..01ce58166c4c97ca7ed556833ac8ecd30a40823c 100644 GIT binary patch literal 55620 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

2kn2N! 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 G$ob#i=3n6e diff --git a/source/ngc/gui/gui_filebrowser.cpp b/source/ngc/gui/gui_filebrowser.cpp index 4caf25f..50f51fb 100644 --- a/source/ngc/gui/gui_filebrowser.cpp +++ b/source/ngc/gui/gui_filebrowser.cpp @@ -109,7 +109,7 @@ GuiFileBrowser::GuiFileBrowser(int w, int h) fileListText[i] = new GuiText(NULL, 20, (GXColor){0, 0, 0, 0xff}); fileListText[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); fileListText[i]->SetPosition(5,0); - fileListText[i]->SetMaxWidth(380); + fileListText[i]->SetMaxWidth(450); fileListBg[i] = new GuiImage(bgFileSelectionEntry); fileListIcon[i] = NULL; diff --git a/source/ngc/gui/gui_imagedata.cpp b/source/ngc/gui/gui_imagedata.cpp index b9fbf80..bc2f1e3 100644 --- a/source/ngc/gui/gui_imagedata.cpp +++ b/source/ngc/gui/gui_imagedata.cpp @@ -20,40 +20,7 @@ GuiImageData::GuiImageData(const u8 * i) height = 0; if(i) - { - PNGUPROP imgProp; - IMGCTX ctx = PNGU_SelectImageFromBuffer(i); - - if(!ctx) - return; - - int res = PNGU_GetImageProperties(ctx, &imgProp); - - if(res == PNGU_OK) - { - int len = (imgProp.imgWidth * imgProp.imgHeight) <<2; - if(len%32) len += (32-len%32); - data = (u8 *)memalign (32, len); - - if(data) - { - res = PNGU_DecodeTo4x4RGBA8 (ctx, imgProp.imgWidth, imgProp.imgHeight, data, 255); - - if(res == PNGU_OK) - { - width = imgProp.imgWidth; - height = imgProp.imgHeight; - DCFlushRange(data, len); - } - else - { - free(data); - data = NULL; - } - } - } - PNGU_ReleaseImageContext (ctx); - } + data = DecodePNG(i, &width, &height); } /** diff --git a/source/ngc/gui/gui_text.cpp b/source/ngc/gui/gui_text.cpp index 11d431b..9d0c9f3 100644 --- a/source/ngc/gui/gui_text.cpp +++ b/source/ngc/gui/gui_text.cpp @@ -237,126 +237,130 @@ void GuiText::Draw() fontSystem[newSize] = new FreeTypeGX(newSize); currentSize = newSize; } + + u8 maxChar; - if(maxWidth > 0) - { - char * tmpText = strdup(origText); - u8 maxChar = int((float((maxWidth<<1))) / (float(newSize))); - - if(!textDyn) - { - if(strlen(tmpText) > maxChar) - tmpText[maxChar] = 0; - textDyn = charToWideChar(tmpText); - } - - if(textScroll == SCROLL_HORIZONTAL) - { - int textlen = strlen(origText); - - if(textlen > maxChar && (FrameTimer % textScrollDelay == 0)) - { - if(textScrollInitialDelay) - { - --textScrollInitialDelay; - } - else - { - ++textScrollPos; - if(textScrollPos > textlen-1) - { - textScrollPos = 0; - textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; - } - - strncpy(tmpText, &origText[textScrollPos], maxChar-1); - tmpText[maxChar-1] = 0; - - int dynlen = strlen(tmpText); - - if(dynlen+2 < maxChar) - { - tmpText[dynlen] = ' '; - tmpText[dynlen+1] = ' '; - strncat(&tmpText[dynlen+2], origText, maxChar - dynlen - 2); - } - if(textDyn) delete[] textDyn; - textDyn = charToWideChar(tmpText); - } - } - if(textDyn) - fontSystem[currentSize]->drawText(this->GetLeft(), this->GetTop(), textDyn, c, style); - } - else if(wrap) - { - int lineheight = newSize + 6; - int txtlen = wcslen(text); - int i = 0; - int ch = 0; - int linenum = 0; - int lastSpace = -1; - int lastSpaceIndex = -1; - wchar_t * textrow[20]; - - while(ch < txtlen) - { - if(i == 0) - textrow[linenum] = new wchar_t[txtlen + 1]; - - textrow[linenum][i] = text[ch]; - textrow[linenum][i+1] = 0; - - if(text[ch] == ' ' || ch == txtlen-1) - { - if(wcslen(textrow[linenum]) >= maxChar) - { - if(lastSpace >= 0) - { - textrow[linenum][lastSpaceIndex] = 0; // discard space, and everything after - ch = lastSpace; // go backwards to the last space - lastSpace = -1; // we have used this space - lastSpaceIndex = -1; - } - ++linenum; - i = -1; - } - else if(ch == txtlen-1) - { - ++linenum; - } - } - if(text[ch] == ' ' && i >= 0) - { - lastSpace = ch; - lastSpaceIndex = i; - } - ++ch; - ++i; - } - - int voffset = 0; - - if(alignmentVert == ALIGN_MIDDLE) - voffset = (lineheight >> 1) * (1-linenum); - - int left = this->GetLeft(); - int top = this->GetTop() + voffset; - - for(i=0; i < linenum; ++i) - { - fontSystem[currentSize]->drawText(left, top+i*lineheight, textrow[i], c, style); - delete[] textrow[i]; - } - } - else - { - fontSystem[currentSize]->drawText(this->GetLeft(), this->GetTop(), textDyn, c, style); - } - free(tmpText); - } - else + if(maxWidth == 0) { fontSystem[currentSize]->drawText(this->GetLeft(), this->GetTop(), text, c, style); + goto done; } + + maxChar = int((float((maxWidth<<1))) / (float(newSize))); // approximate + + if(wrap) + { + int lineheight = newSize + 6; + int txtlen = wcslen(text); + int i = 0; + int ch = 0; + int linenum = 0; + int lastSpace = -1; + int lastSpaceIndex = -1; + wchar_t * textrow[20]; + + while(ch < txtlen) + { + if(i == 0) + textrow[linenum] = new wchar_t[txtlen + 1]; + + textrow[linenum][i] = text[ch]; + textrow[linenum][i+1] = 0; + + if(text[ch] == ' ' || ch == txtlen-1) + { + if(wcslen(textrow[linenum]) >= maxChar) + { + if(lastSpace >= 0) + { + textrow[linenum][lastSpaceIndex] = 0; // discard space, and everything after + ch = lastSpace; // go backwards to the last space + lastSpace = -1; // we have used this space + lastSpaceIndex = -1; + } + ++linenum; + i = -1; + } + else if(ch == txtlen-1) + { + ++linenum; + } + } + if(text[ch] == ' ' && i >= 0) + { + lastSpace = ch; + lastSpaceIndex = i; + } + ++ch; + ++i; + } + + int voffset = 0; + + if(alignmentVert == ALIGN_MIDDLE) + voffset = (lineheight >> 1) * (1-linenum); + + int left = this->GetLeft(); + int top = this->GetTop() + voffset; + + for(i=0; i < linenum; ++i) + { + fontSystem[currentSize]->drawText(left, top+i*lineheight, textrow[i], c, style); + delete[] textrow[i]; + } + goto done; + } + + if(textScroll == SCROLL_HORIZONTAL) + { + char *tmpText = strdup(gettext(origText)); + char *tmpText2 = strdup(tmpText); + int textlen = strlen(tmpText); + + if(textlen > maxChar && (FrameTimer % textScrollDelay == 0)) + { + if(textScrollInitialDelay) + { + --textScrollInitialDelay; + } + else + { + ++textScrollPos; + if(textScrollPos > textlen-1) + { + textScrollPos = 0; + textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; + } + + strncpy(tmpText, &tmpText2[textScrollPos], maxChar-1); + tmpText[maxChar-1] = 0; + + int dynlen = strlen(tmpText); + + if(dynlen+2 < maxChar) + { + tmpText[dynlen] = ' '; + tmpText[dynlen+1] = ' '; + strncat(&tmpText[dynlen+2], tmpText2, maxChar - dynlen - 2); + } + if(textDyn) delete[] textDyn; + textDyn = charToWideChar(tmpText); + } + } + free(tmpText); + free(tmpText2); + } + + if(!textDyn) + { + char *tmpText = strdup(gettext(origText)); + if(strlen(tmpText) > maxChar) + tmpText[maxChar] = 0; + textDyn = charToWideChar(tmpText); + free(tmpText); + } + + fontSystem[currentSize]->drawText(this->GetLeft(), this->GetTop(), textDyn, c, style); +done: this->UpdateEffects(); } diff --git a/source/ngc/menu.cpp b/source/ngc/menu.cpp index 7d27bf5..00c1321 100644 --- a/source/ngc/menu.cpp +++ b/source/ngc/menu.cpp @@ -3472,7 +3472,7 @@ static int MenuSettingsNetwork() GuiOptionBrowser optionBrowser(552, 248, &options); optionBrowser.SetPosition(0, 108); optionBrowser.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - optionBrowser.SetCol2Position(275); + optionBrowser.SetCol2Position(290); HaltGui(); GuiWindow w(screenwidth, screenheight); diff --git a/source/ngc/pngu.c b/source/ngc/pngu.c index e3a0293..f621220 100644 --- a/source/ngc/pngu.c +++ b/source/ngc/pngu.c @@ -1,31 +1,41 @@ /******************************************************************************************** - -PNGU Version : 0.2a - -Coder : frontier - -More info : http://frontier-dev.net - -Modified by Tantric, 2009 - +* +* PNGU +* +* Original author: frontier (http://frontier-dev.net) +* Modified by Tantric, 2009-2010 +* ********************************************************************************************/ + #include #include +#include #include "pngu.h" -#include "png.h" +#include // Constants -#define PNGU_SOURCE_BUFFER 1 -#define PNGU_SOURCE_DEVICE 2 +#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); -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); -void pngu_flush_data_to_buffer (png_structp png_ptr); -int pngu_clamp (int value, int min, int max); +// Return codes +#define PNGU_OK 0 +#define PNGU_ODD_WIDTH 1 +#define PNGU_ODD_STRIDE 2 +#define PNGU_INVALID_WIDTH_OR_HEIGHT 3 +#define PNGU_FILE_IS_NOT_PNG 4 +#define PNGU_UNSUPPORTED_COLOR_TYPE 5 +#define PNGU_NO_FILE_SELECTED 6 +#define PNGU_CANT_OPEN_FILE 7 +#define PNGU_CANT_READ_FILE 8 +#define PNGU_LIB_ERROR 9 + +// Color types +#define PNGU_COLOR_TYPE_GRAY 1 +#define PNGU_COLOR_TYPE_GRAY_ALPHA 2 +#define PNGU_COLOR_TYPE_PALETTE 3 +#define PNGU_COLOR_TYPE_RGB 4 +#define PNGU_COLOR_TYPE_RGB_ALPHA 5 +#define PNGU_COLOR_TYPE_UNKNOWN 6 // PNGU Image context struct struct _IMGCTX @@ -49,362 +59,42 @@ struct _IMGCTX // PNGU Implementation -IMGCTX PNGU_SelectImageFromBuffer (const void *buffer) +static void pngu_free_info (IMGCTX ctx) { - IMGCTX ctx = NULL; - - if (!buffer) - return NULL; - - ctx = malloc (sizeof (struct _IMGCTX)); - if (!ctx) - return NULL; - - ctx->buffer = (void *) buffer; - ctx->source = PNGU_SOURCE_BUFFER; - ctx->cursor = 0; - ctx->filename = NULL; - ctx->propRead = 0; - ctx->infoRead = 0; - - return ctx; -} - -IMGCTX PNGU_SelectImageFromDevice (const char *filename) -{ - IMGCTX ctx = NULL; - - if (!filename) - return NULL; - - ctx = malloc (sizeof (struct _IMGCTX)); - if (!ctx) - return NULL; - - ctx->buffer = NULL; - ctx->source = PNGU_SOURCE_DEVICE; - ctx->cursor = 0; - - ctx->filename = malloc (strlen (filename) + 1); - if (!ctx->filename) - { - free (ctx); - return NULL; - } - strcpy(ctx->filename, filename); - - ctx->propRead = 0; - ctx->infoRead = 0; - - return ctx; -} - -void PNGU_ReleaseImageContext (IMGCTX ctx) -{ - if (!ctx) - return; - - if (ctx->filename) - free (ctx->filename); - - if ((ctx->propRead) && (ctx->prop.trans)) - free (ctx->prop.trans); - - pngu_free_info (ctx); - free (ctx); -} - -int PNGU_GetImageProperties (IMGCTX ctx, PNGUPROP *imgprop) -{ - int res; - - if (!ctx->propRead) - { - res = pngu_info (ctx); - if (res != PNGU_OK) - return res; - } - - *imgprop = ctx->prop; - return PNGU_OK; -} - -int PNGU_DecodeTo4x4RGBA8 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u8 default_alpha) -{ - int result; - PNGU_u32 x, y, qwidth, qheight; - PNGU_u64 alphaMask; - - // width and height need to be divisible by four - if ((width % 4) || (height % 4)) - return PNGU_INVALID_WIDTH_OR_HEIGHT; - - result = pngu_decode (ctx, width, height, 0); - if (result != PNGU_OK) - return result; - - // Init some variables - qwidth = width >> 2; - qheight = height >> 2; - - // Check is source image has an alpha channel - if ( (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY_ALPHA) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB_ALPHA) ) - { - // Alpha channel present, copy image to the output buffer - for (y = 0; y < qheight; y++) - for (x = 0; x < qwidth; x++) - { - int blockbase = (y * qwidth + x) << 3; - - PNGU_u32 y4 = y << 2; - PNGU_u32 x16 = x << 4; - - PNGU_u64 fieldA = *((PNGU_u64 *)(ctx->row_pointers[y4]+x16)); - PNGU_u64 fieldB = *((PNGU_u64 *)(ctx->row_pointers[y4]+x16+8)); - ((PNGU_u64 *) buffer)[blockbase] = - ((fieldA & 0xFF00000000ULL) << 24) | ((fieldA & 0xFF00000000000000ULL) >> 8) | - ((fieldA & 0xFFULL) << 40) | ((fieldA & 0xFF000000ULL) << 8) | - ((fieldB & 0xFF00000000ULL) >> 8) | ((fieldB & 0xFF00000000000000ULL) >> 40) | - ((fieldB & 0xFFULL) << 8) | ((fieldB & 0xFF000000ULL) >> 24); - ((PNGU_u64 *) buffer)[blockbase+4] = - ((fieldA & 0xFFFF0000000000ULL) << 8) | ((fieldA & 0xFFFF00ULL) << 24) | - ((fieldB & 0xFFFF0000000000ULL) >> 24) | ((fieldB & 0xFFFF00ULL) >> 8); - - fieldA = *((PNGU_u64 *)(ctx->row_pointers[y4+1]+x16)); - fieldB = *((PNGU_u64 *)(ctx->row_pointers[y4+1]+x16+8)); - ((PNGU_u64 *) buffer)[blockbase+1] = - ((fieldA & 0xFF00000000ULL) << 24) | ((fieldA & 0xFF00000000000000ULL) >> 8) | - ((fieldA & 0xFFULL) << 40) | ((fieldA & 0xFF000000ULL) << 8) | - ((fieldB & 0xFF00000000ULL) >> 8) | ((fieldB & 0xFF00000000000000ULL) >> 40) | - ((fieldB & 0xFFULL) << 8) | ((fieldB & 0xFF000000ULL) >> 24); - ((PNGU_u64 *) buffer)[blockbase+5] = - ((fieldA & 0xFFFF0000000000ULL) << 8) | ((fieldA & 0xFFFF00ULL) << 24) | - ((fieldB & 0xFFFF0000000000ULL) >> 24) | ((fieldB & 0xFFFF00ULL) >> 8); - - fieldA = *((PNGU_u64 *)(ctx->row_pointers[y4+2]+x16)); - fieldB = *((PNGU_u64 *)(ctx->row_pointers[y4+2]+x16+8)); - ((PNGU_u64 *) buffer)[blockbase+2] = - ((fieldA & 0xFF00000000ULL) << 24) | ((fieldA & 0xFF00000000000000ULL) >> 8) | - ((fieldA & 0xFFULL) << 40) | ((fieldA & 0xFF000000ULL) << 8) | - ((fieldB & 0xFF00000000ULL) >> 8) | ((fieldB & 0xFF00000000000000ULL) >> 40) | - ((fieldB & 0xFFULL) << 8) | ((fieldB & 0xFF000000ULL) >> 24); - ((PNGU_u64 *) buffer)[blockbase+6] = - ((fieldA & 0xFFFF0000000000ULL) << 8) | ((fieldA & 0xFFFF00ULL) << 24) | - ((fieldB & 0xFFFF0000000000ULL) >> 24) | ((fieldB & 0xFFFF00ULL) >> 8); - - fieldA = *((PNGU_u64 *)(ctx->row_pointers[y4+3]+x16)); - fieldB = *((PNGU_u64 *)(ctx->row_pointers[y4+3]+x16+8)); - ((PNGU_u64 *) buffer)[blockbase+3] = - ((fieldA & 0xFF00000000ULL) << 24) | ((fieldA & 0xFF00000000000000ULL) >> 8) | - ((fieldA & 0xFFULL) << 40) | ((fieldA & 0xFF000000ULL) << 8) | - ((fieldB & 0xFF00000000ULL) >> 8) | ((fieldB & 0xFF00000000000000ULL) >> 40) | - ((fieldB & 0xFFULL) << 8) | ((fieldB & 0xFF000000ULL) >> 24); - ((PNGU_u64 *) buffer)[blockbase+7] = - ((fieldA & 0xFFFF0000000000ULL) << 8) | ((fieldA & 0xFFFF00ULL) << 24) | - ((fieldB & 0xFFFF0000000000ULL) >> 24) | ((fieldB & 0xFFFF00ULL) >> 8); - } - } - else - { - // No alpha channel present, copy image to the output buffer - alphaMask = (((PNGU_u64)default_alpha) << 56) | (((PNGU_u64)default_alpha) << 40) | - (((PNGU_u64)default_alpha) << 24) | (((PNGU_u64)default_alpha) << 8); - - for (y = 0; y < qheight; y++) - for (x = 0; x < qwidth; x++) - { - int blockbase = (y * qwidth + x) << 3; - - PNGU_u32 y4 = y << 2; - PNGU_u32 x12 = x * 12; - - PNGU_u64 field64 = *((PNGU_u64 *)(ctx->row_pointers[y4]+x12)); - PNGU_u64 field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y4]+x12+8)); - ((PNGU_u64 *) buffer)[blockbase] = - (((field64 & 0xFF00000000000000ULL) >> 8) | (field64 & 0xFF00000000ULL) | - ((field64 & 0xFF00ULL) << 8) | ((field32 & 0xFF0000ULL) >> 16) | alphaMask); - ((PNGU_u64 *) buffer)[blockbase+4] = - (((field64 & 0xFFFF0000000000ULL) << 8) | ((field64 & 0xFFFF0000ULL) << 16) | - ((field64 & 0xFFULL) << 24) | ((field32 & 0xFF000000ULL) >> 8) | (field32 & 0xFFFFULL)); - - field64 = *((PNGU_u64 *)(ctx->row_pointers[y4+1]+x12)); - field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y4+1]+x12+8)); - ((PNGU_u64 *) buffer)[blockbase+1] = - (((field64 & 0xFF00000000000000ULL) >> 8) | (field64 & 0xFF00000000ULL) | - ((field64 & 0xFF00ULL) << 8) | ((field32 & 0xFF0000ULL) >> 16) | alphaMask); - ((PNGU_u64 *) buffer)[blockbase+5] = - (((field64 & 0xFFFF0000000000ULL) << 8) | ((field64 & 0xFFFF0000ULL) << 16) | - ((field64 & 0xFFULL) << 24) | ((field32 & 0xFF000000ULL) >> 8) | (field32 & 0xFFFFULL)); - - field64 = *((PNGU_u64 *)(ctx->row_pointers[y4+2]+x12)); - field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y4+2]+x12+8)); - ((PNGU_u64 *) buffer)[blockbase+2] = - (((field64 & 0xFF00000000000000ULL) >> 8) | (field64 & 0xFF00000000ULL) | - ((field64 & 0xFF00ULL) << 8) | ((field32 & 0xFF0000ULL) >> 16) | alphaMask); - ((PNGU_u64 *) buffer)[blockbase+6] = - (((field64 & 0xFFFF0000000000ULL) << 8) | ((field64 & 0xFFFF0000ULL) << 16) | - ((field64 & 0xFFULL) << 24) | ((field32 & 0xFF000000ULL) >> 8) | (field32 & 0xFFFFULL)); - - field64 = *((PNGU_u64 *)(ctx->row_pointers[y4+3]+x12)); - field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y4+3]+x12+8)); - ((PNGU_u64 *) buffer)[blockbase+3] = - (((field64 & 0xFF00000000000000ULL) >> 8) | (field64 & 0xFF00000000ULL) | - ((field64 & 0xFF00ULL) << 8) | ((field32 & 0xFF0000ULL) >> 16) | alphaMask); - ((PNGU_u64 *) buffer)[blockbase+7] = - (((field64 & 0xFFFF0000000000ULL) << 8) | ((field64 & 0xFFFF0000ULL) << 16) | - ((field64 & 0xFFULL) << 24) | ((field32 & 0xFF000000ULL) >> 8) | (field32 & 0xFFFFULL)); - } - } - - // Free resources - free (ctx->img_data); - free (ctx->row_pointers); - - // Success - 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->infoRead) { if (ctx->source == PNGU_SOURCE_DEVICE) fclose (ctx->fd); - return PNGU_LIB_ERROR; + + png_destroy_read_struct (&(ctx->png_ptr), &(ctx->info_ptr), (png_infopp)NULL); + + ctx->infoRead = 0; } - - 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 >>2) + 1) <<2; // 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) +// Custom data provider function used for reading from memory buffers. +static void pngu_read_data_from_buffer (png_structp png_ptr, png_bytep data, png_size_t length) { - int res; - PNGU_u32 x,y, tmpy1, tmpy2, tmpyWid, tmpxy; - - 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++) - { - tmpy1 = y * 640*3; - tmpy2 = y%4 << 2; - tmpyWid = (((y >> 2)<<4)*width); - - for(x=0; x < width; x++) - { - offset = tmpyWid + ((x >> 2)<<6) + ((tmpy2+ x%4 ) << 1); - tmpxy = x * 3 + tmpy1; - - tmpbuffer[tmpxy ] = ptr[offset+1]; // R - tmpbuffer[tmpxy+1] = ptr[offset+32]; // G - tmpbuffer[tmpxy+2] = ptr[offset+33]; // B - } - } - - res = PNGU_EncodeFromRGB (ctx, width, height, tmpbuffer, stride); - free(tmpbuffer); - return res; + IMGCTX ctx = (IMGCTX) png_get_io_ptr (png_ptr); + memcpy (data, ctx->buffer + ctx->cursor, length); + ctx->cursor += length; } -int pngu_info (IMGCTX ctx) +// Custom data writer function used for writing to memory buffers. +static void pngu_write_data_to_buffer (png_structp png_ptr, png_bytep data, png_size_t length) +{ + IMGCTX ctx = (IMGCTX) png_get_io_ptr (png_ptr); + memcpy (ctx->buffer + ctx->cursor, data, length); + ctx->cursor += length; +} + +// Custom data flusher function used for writing to memory buffers. +static void pngu_flush_data_to_buffer (png_structp png_ptr) +{ + // Nothing to do here +} + +static int pngu_info (IMGCTX ctx) { png_byte magic[8]; png_uint_32 width; @@ -604,7 +294,7 @@ int pngu_info (IMGCTX ctx) return PNGU_OK; } -int pngu_decode (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stripAlpha) +static int pngu_decode (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stripAlpha) { png_uint_32 rowbytes; png_uint_32 i, propImgHeight; @@ -679,48 +369,313 @@ int pngu_decode (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stripAlph return PNGU_OK; } -void pngu_free_info (IMGCTX ctx) +static inline PNGU_u32 coordsRGBA8(PNGU_u32 x, PNGU_u32 y, PNGU_u32 w) { - if (ctx->infoRead) + return ((((y >> 2) * (w >> 2) + (x >> 2)) << 5) + ((y & 3) << 2) + (x & 3)) << 1; +} + +static PNGU_u8 * PNGU_DecodeTo4x4RGBA8 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u8 default_alpha) +{ + PNGU_u8 *dst; + PNGU_u32 x, y, offset; + png_byte *pixel; + + if (pngu_decode (ctx, width, height, 0) != PNGU_OK) + return NULL; + + PNGU_u32 newWidth = width; + if(newWidth%4) newWidth += (4-newWidth%4); + PNGU_u32 newHeight = height; + if(newHeight%4) newHeight += (4-newHeight%4); + + int len = (newWidth * newHeight) << 2; + if(len%32) len += (32-len%32); + dst = memalign (32, len); + + if(!dst) + return NULL; + + for (y = 0; y < newHeight; y++) + { + for (x = 0; x < newWidth; x++) + { + offset = coordsRGBA8(x, y, newWidth); + + if(y >= height || x >= width) + { + dst[offset] = 0; + dst[offset+1] = 255; + dst[offset+32] = 255; + dst[offset+33] = 255; + } + else + { + if (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY_ALPHA || + ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB_ALPHA) + { + pixel = &(ctx->row_pointers[y][x*4]); + dst[offset] = pixel[3]; // Alpha + dst[offset+1] = pixel[0]; // Red + dst[offset+32] = pixel[1]; // Green + dst[offset+33] = pixel[2]; // Blue + } + else + { + pixel = &(ctx->row_pointers[y][x*3]); + dst[offset] = default_alpha; // Alpha + dst[offset+1] = pixel[0]; // Red + dst[offset+32] = pixel[1]; // Green + dst[offset+33] = pixel[2]; // Blue + } + } + } + } + + // Free resources + free (ctx->img_data); + free (ctx->row_pointers); + + DCFlushRange(dst, len); + return dst; +} + +IMGCTX PNGU_SelectImageFromBuffer (const void *buffer) +{ + IMGCTX ctx = NULL; + + if (!buffer) + return NULL; + + ctx = malloc (sizeof (struct _IMGCTX)); + if (!ctx) + return NULL; + + ctx->buffer = (void *) buffer; + ctx->source = PNGU_SOURCE_BUFFER; + ctx->cursor = 0; + ctx->filename = NULL; + ctx->propRead = 0; + ctx->infoRead = 0; + + return ctx; +} + +IMGCTX PNGU_SelectImageFromDevice (const char *filename) +{ + IMGCTX ctx = NULL; + + if (!filename) + return NULL; + + ctx = malloc (sizeof (struct _IMGCTX)); + if (!ctx) + return NULL; + + ctx->buffer = NULL; + ctx->source = PNGU_SOURCE_DEVICE; + ctx->cursor = 0; + + ctx->filename = malloc (strlen (filename) + 1); + if (!ctx->filename) + { + free (ctx); + return NULL; + } + strcpy(ctx->filename, filename); + + ctx->propRead = 0; + ctx->infoRead = 0; + + return ctx; +} + +void PNGU_ReleaseImageContext (IMGCTX ctx) +{ + if (!ctx) + return; + + if (ctx->filename) + free (ctx->filename); + + if ((ctx->propRead) && (ctx->prop.trans)) + free (ctx->prop.trans); + + pngu_free_info (ctx); + free (ctx); +} + +int PNGU_GetImageProperties (IMGCTX ctx, PNGUPROP *imgprop) +{ + int res; + + if (!ctx->propRead) + { + res = pngu_info (ctx); + if (res != PNGU_OK) + return res; + } + + *imgprop = ctx->prop; + return PNGU_OK; +} + +PNGU_u8 * DecodePNG(const PNGU_u8 *src, int * width, int * height) +{ + PNGUPROP imgProp; + IMGCTX ctx = PNGU_SelectImageFromBuffer(src); + PNGU_u8 *dst = NULL; + + if(!ctx) + return NULL; + + if(PNGU_GetImageProperties(ctx, &imgProp) == PNGU_OK) + { + dst = PNGU_DecodeTo4x4RGBA8 (ctx, imgProp.imgWidth, imgProp.imgHeight, 255); + + *width = imgProp.imgWidth; + *height = imgProp.imgHeight; + } + + PNGU_ReleaseImageContext (ctx); + return dst; +} + +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); - - png_destroy_read_struct (&(ctx->png_ptr), &(ctx->info_ptr), (png_infopp)NULL); - - ctx->infoRead = 0; + 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 >>2) + 1) <<2; // 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; } -// Custom data provider function used for reading from memory buffers. -void pngu_read_data_from_buffer (png_structp png_ptr, png_bytep data, png_size_t length) +int PNGU_EncodeFromGXTexture (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride) { - IMGCTX ctx = (IMGCTX) png_get_io_ptr (png_ptr); - memcpy (data, ctx->buffer + ctx->cursor, length); - ctx->cursor += length; -} + int res; + PNGU_u32 x,y, tmpy1, tmpy2, tmpyWid, tmpxy; -// Custom data writer function used for writing to memory buffers. -void pngu_write_data_to_buffer (png_structp png_ptr, png_bytep data, png_size_t length) -{ - IMGCTX ctx = (IMGCTX) png_get_io_ptr (png_ptr); - memcpy (ctx->buffer + ctx->cursor, data, length); - ctx->cursor += length; -} + 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++) + { + tmpy1 = y * 640*3; + tmpy2 = y%4 << 2; + tmpyWid = (((y >> 2)<<4)*width); -// Custom data flusher function used for writing to memory buffers. -void pngu_flush_data_to_buffer (png_structp png_ptr) -{ - // Nothing to do here -} + for(x=0; x < width; x++) + { + offset = tmpyWid + ((x >> 2)<<6) + ((tmpy2+ x%4 ) << 1); + tmpxy = x * 3 + tmpy1; -// Function used in YCbYCr to RGB decoding -int pngu_clamp (int value, int min, int max) -{ - if (value < min) - value = min; - else if (value > max) - value = max; - - return value; + tmpbuffer[tmpxy ] = ptr[offset+1]; // R + tmpbuffer[tmpxy+1] = ptr[offset+32]; // G + tmpbuffer[tmpxy+2] = ptr[offset+33]; // B + } + } + + res = PNGU_EncodeFromRGB (ctx, width, height, tmpbuffer, stride); + free(tmpbuffer); + return res; } diff --git a/source/ngc/pngu.h b/source/ngc/pngu.h index 192506b..4819fbc 100644 --- a/source/ngc/pngu.h +++ b/source/ngc/pngu.h @@ -1,38 +1,15 @@ /******************************************************************************************** - -PNGU Version : 0.2a - -Coder : frontier - -More info : http://frontier-dev.net - -Modified by Tantric, 2009 - +* +* PNGU +* +* Original author: frontier (http://frontier-dev.net) +* Modified by Tantric, 2009-2010 +* ********************************************************************************************/ + #ifndef __PNGU__ #define __PNGU__ -// Return codes -#define PNGU_OK 0 -#define PNGU_ODD_WIDTH 1 -#define PNGU_ODD_STRIDE 2 -#define PNGU_INVALID_WIDTH_OR_HEIGHT 3 -#define PNGU_FILE_IS_NOT_PNG 4 -#define PNGU_UNSUPPORTED_COLOR_TYPE 5 -#define PNGU_NO_FILE_SELECTED 6 -#define PNGU_CANT_OPEN_FILE 7 -#define PNGU_CANT_READ_FILE 8 -#define PNGU_LIB_ERROR 9 - -// Color types -#define PNGU_COLOR_TYPE_GRAY 1 -#define PNGU_COLOR_TYPE_GRAY_ALPHA 2 -#define PNGU_COLOR_TYPE_PALETTE 3 -#define PNGU_COLOR_TYPE_RGB 4 -#define PNGU_COLOR_TYPE_RGB_ALPHA 5 -#define PNGU_COLOR_TYPE_UNKNOWN 6 - - #ifdef __cplusplus extern "C" { #endif @@ -67,7 +44,7 @@ struct _IMGCTX; typedef struct _IMGCTX *IMGCTX; /**************************************************************************** -* Image context handling * +* Image context handling * ****************************************************************************/ // Selects a PNG file, previosly loaded into a buffer, and creates an image context for subsequent procesing. @@ -79,22 +56,18 @@ IMGCTX PNGU_SelectImageFromDevice (const char *filename); // Frees resources associated with an image context. Always call this function when you no longer need the IMGCTX. void PNGU_ReleaseImageContext (IMGCTX ctx); - /**************************************************************************** -* Miscelaneous * +* Miscellaneous * ****************************************************************************/ // Retrieves info from selected PNG file, including image dimensions, color format, background and transparency colors. int PNGU_GetImageProperties (IMGCTX ctx, PNGUPROP *fileproperties); /**************************************************************************** -* Image conversion * +* Image conversion * ****************************************************************************/ -// Expands selected image into a 4x4 tiled RGBA8 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. -int PNGU_DecodeTo4x4RGBA8 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u8 default_alpha); - +PNGU_u8 * DecodePNG(const PNGU_u8 *src, int *width, int *height); 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); @@ -103,4 +76,3 @@ int PNGU_EncodeFromGXTexture (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void #endif #endif -