From 4cd74c3b371cec451752e56df84fc1df83491658 Mon Sep 17 00:00:00 2001 From: Christopher Roy Bratusek Date: Sat, 15 Oct 2011 17:21:05 +0200 Subject: [PATCH] Unix line-endings --- source/Tools/RuntimeIOSPatch.c | 138 +- source/oggplayer.c | 1078 +++++++------- source/pngu.c | 2532 ++++++++++++++++---------------- svnrev/svnrev.h | 26 +- 4 files changed, 1887 insertions(+), 1887 deletions(-) diff --git a/source/Tools/RuntimeIOSPatch.c b/source/Tools/RuntimeIOSPatch.c index 844702c..8e3c373 100644 --- a/source/Tools/RuntimeIOSPatch.c +++ b/source/Tools/RuntimeIOSPatch.c @@ -1,69 +1,69 @@ -#include -#include -#include -#include -#include - -#include "RuntimeIOSPatch.h" - -#define MEM_PROT 0xD8B420A - -const u8 isfs_permissions_old[] = { 0x42, 0x8B, 0xD0, 0x01, 0x25, 0x66 }; -const u8 isfs_permissions_patch[] = { 0x42, 0x8B, 0xE0, 0x01, 0x25, 0x66 }; -const u8 setuid_old[] = { 0xD1, 0x2A, 0x1C, 0x39 }; -const u8 setuid_patch[] = { 0x46, 0xC0 }; -const u8 es_identify_old[] = { 0x28, 0x03, 0xD1, 0x23 }; -const u8 es_identify_patch[] = { 0x00, 0x00 }; -const u8 hash_old[] = { 0x20, 0x07, 0x4B, 0x0B }; -const u8 hash_patch[] = { 0x00 }; -const u8 addticket_vers_check[] = { 0xD2, 0x01, 0x4E, 0x56 }; -const u8 addticket_patch[] = { 0xE0 }; - -u32 apply_patch(const char *name, const u8 *old, u32 old_size, const u8 *patch, u32 patch_size, u32 patch_offset) -{ -// printf("Applying patch %s.....", name); - u8 *ptr = (u8 *) 0x93400000; - u32 i, found = 0; - u8 *start; - - while ((u32) ptr < (0x94000000 - old_size)) - { - if(!memcmp(ptr, old, old_size)) - { - found++; - start = ptr + patch_offset; - for (i = 0; i < patch_size; i++) - *(start + i) = patch[i]; - ptr += patch_size; - DCFlushRange((u8 *) (((u32) start) >> 5 << 5), (patch_size >> 5 << 5) + 64); - } - ptr++; - } -/* if(found) - printf("Patched\n"); - else - printf("\n"); -*/ - return found; -} - -u32 runtimePatchApply() -{ - u32 count = 0; - write16(MEM_PROT, 0); - count += apply_patch("Trucha", hash_old, - sizeof(hash_old), hash_patch, sizeof(hash_patch), 1); - count += apply_patch("ES_Identify", es_identify_old, - sizeof(es_identify_old), es_identify_patch, sizeof(es_identify_patch), - 2); - count += apply_patch("NAND Permissions", isfs_permissions_old, - sizeof(isfs_permissions_old), isfs_permissions_patch, - sizeof(isfs_permissions_patch), 0); - count += apply_patch("add ticket patch", addticket_vers_check, - sizeof(addticket_vers_check), addticket_patch, sizeof(addticket_patch), - 0); - count += apply_patch("ES_SetUID", setuid_old, sizeof(setuid_old), - setuid_patch, sizeof(setuid_patch), 0); - write16(MEM_PROT, 1); - return count; -} +#include +#include +#include +#include +#include + +#include "RuntimeIOSPatch.h" + +#define MEM_PROT 0xD8B420A + +const u8 isfs_permissions_old[] = { 0x42, 0x8B, 0xD0, 0x01, 0x25, 0x66 }; +const u8 isfs_permissions_patch[] = { 0x42, 0x8B, 0xE0, 0x01, 0x25, 0x66 }; +const u8 setuid_old[] = { 0xD1, 0x2A, 0x1C, 0x39 }; +const u8 setuid_patch[] = { 0x46, 0xC0 }; +const u8 es_identify_old[] = { 0x28, 0x03, 0xD1, 0x23 }; +const u8 es_identify_patch[] = { 0x00, 0x00 }; +const u8 hash_old[] = { 0x20, 0x07, 0x4B, 0x0B }; +const u8 hash_patch[] = { 0x00 }; +const u8 addticket_vers_check[] = { 0xD2, 0x01, 0x4E, 0x56 }; +const u8 addticket_patch[] = { 0xE0 }; + +u32 apply_patch(const char *name, const u8 *old, u32 old_size, const u8 *patch, u32 patch_size, u32 patch_offset) +{ +// printf("Applying patch %s.....", name); + u8 *ptr = (u8 *) 0x93400000; + u32 i, found = 0; + u8 *start; + + while ((u32) ptr < (0x94000000 - old_size)) + { + if(!memcmp(ptr, old, old_size)) + { + found++; + start = ptr + patch_offset; + for (i = 0; i < patch_size; i++) + *(start + i) = patch[i]; + ptr += patch_size; + DCFlushRange((u8 *) (((u32) start) >> 5 << 5), (patch_size >> 5 << 5) + 64); + } + ptr++; + } +/* if(found) + printf("Patched\n"); + else + printf("\n"); +*/ + return found; +} + +u32 runtimePatchApply() +{ + u32 count = 0; + write16(MEM_PROT, 0); + count += apply_patch("Trucha", hash_old, + sizeof(hash_old), hash_patch, sizeof(hash_patch), 1); + count += apply_patch("ES_Identify", es_identify_old, + sizeof(es_identify_old), es_identify_patch, sizeof(es_identify_patch), + 2); + count += apply_patch("NAND Permissions", isfs_permissions_old, + sizeof(isfs_permissions_old), isfs_permissions_patch, + sizeof(isfs_permissions_patch), 0); + count += apply_patch("add ticket patch", addticket_vers_check, + sizeof(addticket_vers_check), addticket_patch, sizeof(addticket_patch), + 0); + count += apply_patch("ES_SetUID", setuid_old, sizeof(setuid_old), + setuid_patch, sizeof(setuid_patch), 0); + write16(MEM_PROT, 1); + return count; +} diff --git a/source/oggplayer.c b/source/oggplayer.c index 689160d..ccbc04a 100644 --- a/source/oggplayer.c +++ b/source/oggplayer.c @@ -1,539 +1,539 @@ -/* - Copyright (c) 2008 Francisco Muņoz 'Hermes' - All rights reserved. - - Proper (standard) vorbis usage by Tantric, 2009 - Threading modifications/corrections by Tantric, 2009 - - Redistribution and use in source and binary forms, with or without modification, are - permitted provided that the following conditions are met: - - - Redistributions of source code must retain the above copyright notice, this list of - conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, this list - of conditions and the following disclaimer in the documentation and/or other - materials provided with the distribution. - - The names of the contributors may not be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef NO_SOUND - -#include "oggplayer.h" -#include -#include -#include - -/* functions to read the Ogg file from memory */ - -static struct -{ - char *mem; - int size; - int pos; -} file[4]; - -static int f_read(void * punt, int bytes, int blocks, int *f) -{ - int b; - int c; - int d; - - if (bytes * blocks <= 0) - return 0; - - blocks = bytes * blocks; - c = 0; - - while (blocks > 0) - { - b = blocks; - if (b > 4096) - b = 4096; - - if (*f >= 0x666 && *f <= 0x669) - { - d = (*f) - 0x666; - if (file[d].size == 0) - return -1; - if ((file[d].pos + b) > file[d].size) - b = file[d].size - file[d].pos; - if (b > 0) - { - memcpy(punt, file[d].mem + file[d].pos, b); - file[d].pos += b; - } - } - else - b = read(*f, ((char *) punt) + c, b); - - if (b <= 0) - { - return c / bytes; - } - c += b; - blocks -= b; - } - return c / bytes; -} - -static int f_seek(int *f, ogg_int64_t offset, int mode) -{ - if(f==NULL) return(-1); - - int k, d; - mode &= 3; - if (*f >= 0x666 && *f <= 0x669) - { - d = (*f) - 0x666; - k = 0; - - if (file[d].size == 0) - return -1; - - if (mode == 0) - { - if ((offset) >= file[d].size) - { - file[d].pos = file[d].size; - k = -1; - } - else if ((offset) < 0) - { - file[d].pos = 0; - k = -1; - } - else - file[d].pos = offset; - } - if (mode == 1) - { - if ((file[d].pos + offset) >= file[d].size) - { - file[d].pos = file[d].size; - k = -1; - } - else if ((file[d].pos + offset) < 0) - { - file[d].pos = 0; - k = -1; - } - else - file[d].pos += offset; - } - if (mode == 2) - { - - if ((file[d].size + offset) >= file[d].size) - { - file[d].pos = file[d].size; - k = -1; - } - else if ((file[d].size + offset) < 0) - { - file[d].pos = 0; - k = -1; - } - else - file[d].pos = file[d].size + offset; - } - - } - else - k = lseek(*f, (int) offset, mode); - - if (k < 0) - k = -1; - else - k = 0; - return k; -} - -static int f_close(int *f) -{ - int d; - if (*f >= 0x666 && *f <= 0x669) - { - d = (*f) - 0x666; - file[d].size = 0; - file[d].pos = 0; - if (file[d].mem) - { - file[d].mem = (void *) 0; - } - return 0; - } - else - return close(*f); - return 0; -} - -static long f_tell(int *f) -{ - int k, d; - - if (*f >= 0x666 && *f <= 0x669) - { - d = (*f) - 0x666; - k = file[d].pos; - } - else - k = lseek(*f, 0, 1); - - return (long) k; -} - -static int mem_open(char * ogg, int size) -{ - static int one = 1; - int n; - if (one) - { - one = 0; - for (n = 0; n < 4; n++) - file[n].size = 0; - } - - for (n = 0; n < 4; n++) - { - if (file[n].size == 0) - { - file[n].mem = ogg; - file[n].size = size; - file[n].pos = 0; - return (0x666 + n); - } - } - return -1; -} - -static int mem_close(int fd) -{ - if (fd >= 0x666 && fd <= 0x669) // it is a memory file descriptor? - { - fd -= 0x666; - file[fd].size = 0; - return 0; - } - else - return f_close(&fd); -} - -static ov_callbacks callbacks = { - (size_t (*)(void *, size_t, size_t, void *)) f_read, - (int (*)(void *, ogg_int64_t, int)) f_seek, - (int (*)(void *)) f_close, - (long (*)(void *)) f_tell -}; - -/* OGG control */ - -#define READ_SAMPLES 4096 // samples that it must read before to send -#define MAX_PCMOUT 4096 // minimum size to read ogg samples -typedef struct -{ - OggVorbis_File vf; - vorbis_info *vi; - int current_section; - - // OGG file operation - int fd; - int mode; - int eof; - int flag; - int volume; - int seek_time; - - /* OGG buffer control */ - short pcmout[2][READ_SAMPLES + MAX_PCMOUT * 2]; /* take 4k out of the data segment, not the stack */ - int pcmout_pos; - int pcm_indx; - -} private_data_ogg; - -static private_data_ogg private_ogg; - -// OGG thread control - -#define STACKSIZE 8192 - -static u8 oggplayer_stack[STACKSIZE]; -static lwpq_t oggplayer_queue = LWP_TQUEUE_NULL; -static lwp_t h_oggplayer = LWP_THREAD_NULL; -static int ogg_thread_running = 0; - -static void ogg_add_callback(int voice) -{ - if (!ogg_thread_running) - { - ASND_StopVoice(0); - return; - } - - if (private_ogg.flag & 128) - return; // Ogg is paused - - if (private_ogg.pcm_indx >= READ_SAMPLES) - { - if (ASND_AddVoice(0, - (void *) private_ogg.pcmout[private_ogg.pcmout_pos], - private_ogg.pcm_indx << 1) == 0) - { - private_ogg.pcmout_pos ^= 1; - private_ogg.pcm_indx = 0; - private_ogg.flag = 0; - LWP_ThreadSignal(oggplayer_queue); - } - } - else - { - if (private_ogg.flag & 64) - { - private_ogg.flag &= ~64; - LWP_ThreadSignal(oggplayer_queue); - } - } -} - -static void * ogg_player_thread(private_data_ogg * priv) -{ - int first_time = 1; - long ret; - - //init - LWP_InitQueue(&oggplayer_queue); - - priv[0].vi = ov_info(&priv[0].vf, -1); - - ASND_Pause(0); - - priv[0].pcm_indx = 0; - priv[0].pcmout_pos = 0; - priv[0].eof = 0; - priv[0].flag = 0; - priv[0].current_section = 0; - - ogg_thread_running = 1; - - while (!priv[0].eof && ogg_thread_running) - { - if (priv[0].flag) - LWP_ThreadSleep(oggplayer_queue); // wait only when i have samples to send - - if (priv[0].flag == 0) // wait to all samples are sent - { - if (ASND_TestPointer(0, priv[0].pcmout[priv[0].pcmout_pos]) - && ASND_StatusVoice(0) != SND_UNUSED) - { - priv[0].flag |= 64; - continue; - } - if (priv[0].pcm_indx < READ_SAMPLES) - { - priv[0].flag = 3; - - if (priv[0].seek_time >= 0) - { - ov_time_seek(&priv[0].vf, priv[0].seek_time); - priv[0].seek_time = -1; - } - - ret - = ov_read( - &priv[0].vf, - (void *) &priv[0].pcmout[priv[0].pcmout_pos][priv[0].pcm_indx], - MAX_PCMOUT,/*0,2,1,*/&priv[0].current_section); - priv[0].flag &= 192; - if (ret == 0) - { - /* EOF */ - if (priv[0].mode & 1) - ov_time_seek(&priv[0].vf, 0); // repeat - else - priv[0].eof = 1; // stops - } - else if (ret < 0) - { - /* error in the stream. Not a problem, just reporting it in - case we (the app) cares. In this case, we don't. */ - if (ret != OV_HOLE) - { - if (priv[0].mode & 1) - ov_time_seek(&priv[0].vf, 0); // repeat - else - priv[0].eof = 1; // stops - } - } - else - { - /* we don't bother dealing with sample rate changes, etc, but - you'll have to*/ - priv[0].pcm_indx += ret >> 1; //get 16 bits samples - } - } - else - priv[0].flag = 1; - } - - if (priv[0].flag == 1) - { - if (ASND_StatusVoice(0) == SND_UNUSED || first_time) - { - first_time = 0; - if (priv[0].vi->channels == 2) - { - ASND_SetVoice(0, VOICE_STEREO_16BIT, priv[0].vi->rate, 0, - (void *) priv[0].pcmout[priv[0].pcmout_pos], - priv[0].pcm_indx << 1, priv[0].volume, - priv[0].volume, ogg_add_callback); - priv[0].pcmout_pos ^= 1; - priv[0].pcm_indx = 0; - priv[0].flag = 0; - } - else - { - ASND_SetVoice(0, VOICE_MONO_16BIT, priv[0].vi->rate, 0, - (void *) priv[0].pcmout[priv[0].pcmout_pos], - priv[0].pcm_indx << 1, priv[0].volume, - priv[0].volume, ogg_add_callback); - priv[0].pcmout_pos ^= 1; - priv[0].pcm_indx = 0; - priv[0].flag = 0; - } - } - } - usleep(100); - } - ov_clear(&priv[0].vf); - priv[0].fd = -1; - priv[0].pcm_indx = 0; - - return 0; -} - -void StopOgg() -{ - ASND_StopVoice(0); - ogg_thread_running = 0; - - if(h_oggplayer != LWP_THREAD_NULL) - { - if(oggplayer_queue != LWP_TQUEUE_NULL) - LWP_ThreadSignal(oggplayer_queue); - LWP_JoinThread(h_oggplayer, NULL); - h_oggplayer = LWP_THREAD_NULL; - } - if(oggplayer_queue != LWP_TQUEUE_NULL) - { - LWP_CloseQueue(oggplayer_queue); - oggplayer_queue = LWP_TQUEUE_NULL; - } -} - -int PlayOgg(char * buf, int buflen, int time_pos, int mode) -{ - StopOgg(); - - private_ogg.fd = mem_open(buf, buflen); - - if (private_ogg.fd < 0) - { - private_ogg.fd = -1; - return -1; - } - - private_ogg.mode = mode; - private_ogg.eof = 0; - private_ogg.volume = 127; - private_ogg.flag = 0; - private_ogg.seek_time = -1; - - if (time_pos > 0) - private_ogg.seek_time = time_pos; - - if (ov_open_callbacks((void *) &private_ogg.fd, &private_ogg.vf, NULL, 0, callbacks) < 0) - { - mem_close(private_ogg.fd); // mem_close() can too close files from devices - private_ogg.fd = -1; - ogg_thread_running = 0; - return -1; - } - - if (LWP_CreateThread(&h_oggplayer, (void *) ogg_player_thread, - &private_ogg, oggplayer_stack, STACKSIZE, 80) == -1) - { - ogg_thread_running = 0; - ov_clear(&private_ogg.vf); - private_ogg.fd = -1; - return -1; - } - return 0; -} - -void PauseOgg(int pause) -{ - if (pause) - { - private_ogg.flag |= 128; - } - else - { - if (private_ogg.flag & 128) - { - private_ogg.flag |= 64; - private_ogg.flag &= ~128; - if (ogg_thread_running > 0) - { - LWP_ThreadSignal(oggplayer_queue); - } - } - } -} - -int StatusOgg() -{ - if (ogg_thread_running == 0) - return -1; // Error - else if (private_ogg.eof) - return 255; // EOF - else if (private_ogg.flag & 128) - return 2; // paused - else - return 1; // running -} - -void SetVolumeOgg(int volume) -{ - private_ogg.volume = volume; - ASND_ChangeVolumeVoice(0, volume, volume); -} - -s32 GetTimeOgg() -{ - int ret; - if (ogg_thread_running == 0 || private_ogg.fd < 0) - return 0; - ret = ((s32) ov_time_tell(&private_ogg.vf)); - if (ret < 0) - ret = 0; - - return ret; -} - -void SetTimeOgg(s32 time_pos) -{ - if (time_pos >= 0) - private_ogg.seek_time = time_pos; -} - -#endif +/* + Copyright (c) 2008 Francisco Muņoz 'Hermes' + All rights reserved. + + Proper (standard) vorbis usage by Tantric, 2009 + Threading modifications/corrections by Tantric, 2009 + + Redistribution and use in source and binary forms, with or without modification, are + permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution. + - The names of the contributors may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NO_SOUND + +#include "oggplayer.h" +#include +#include +#include + +/* functions to read the Ogg file from memory */ + +static struct +{ + char *mem; + int size; + int pos; +} file[4]; + +static int f_read(void * punt, int bytes, int blocks, int *f) +{ + int b; + int c; + int d; + + if (bytes * blocks <= 0) + return 0; + + blocks = bytes * blocks; + c = 0; + + while (blocks > 0) + { + b = blocks; + if (b > 4096) + b = 4096; + + if (*f >= 0x666 && *f <= 0x669) + { + d = (*f) - 0x666; + if (file[d].size == 0) + return -1; + if ((file[d].pos + b) > file[d].size) + b = file[d].size - file[d].pos; + if (b > 0) + { + memcpy(punt, file[d].mem + file[d].pos, b); + file[d].pos += b; + } + } + else + b = read(*f, ((char *) punt) + c, b); + + if (b <= 0) + { + return c / bytes; + } + c += b; + blocks -= b; + } + return c / bytes; +} + +static int f_seek(int *f, ogg_int64_t offset, int mode) +{ + if(f==NULL) return(-1); + + int k, d; + mode &= 3; + if (*f >= 0x666 && *f <= 0x669) + { + d = (*f) - 0x666; + k = 0; + + if (file[d].size == 0) + return -1; + + if (mode == 0) + { + if ((offset) >= file[d].size) + { + file[d].pos = file[d].size; + k = -1; + } + else if ((offset) < 0) + { + file[d].pos = 0; + k = -1; + } + else + file[d].pos = offset; + } + if (mode == 1) + { + if ((file[d].pos + offset) >= file[d].size) + { + file[d].pos = file[d].size; + k = -1; + } + else if ((file[d].pos + offset) < 0) + { + file[d].pos = 0; + k = -1; + } + else + file[d].pos += offset; + } + if (mode == 2) + { + + if ((file[d].size + offset) >= file[d].size) + { + file[d].pos = file[d].size; + k = -1; + } + else if ((file[d].size + offset) < 0) + { + file[d].pos = 0; + k = -1; + } + else + file[d].pos = file[d].size + offset; + } + + } + else + k = lseek(*f, (int) offset, mode); + + if (k < 0) + k = -1; + else + k = 0; + return k; +} + +static int f_close(int *f) +{ + int d; + if (*f >= 0x666 && *f <= 0x669) + { + d = (*f) - 0x666; + file[d].size = 0; + file[d].pos = 0; + if (file[d].mem) + { + file[d].mem = (void *) 0; + } + return 0; + } + else + return close(*f); + return 0; +} + +static long f_tell(int *f) +{ + int k, d; + + if (*f >= 0x666 && *f <= 0x669) + { + d = (*f) - 0x666; + k = file[d].pos; + } + else + k = lseek(*f, 0, 1); + + return (long) k; +} + +static int mem_open(char * ogg, int size) +{ + static int one = 1; + int n; + if (one) + { + one = 0; + for (n = 0; n < 4; n++) + file[n].size = 0; + } + + for (n = 0; n < 4; n++) + { + if (file[n].size == 0) + { + file[n].mem = ogg; + file[n].size = size; + file[n].pos = 0; + return (0x666 + n); + } + } + return -1; +} + +static int mem_close(int fd) +{ + if (fd >= 0x666 && fd <= 0x669) // it is a memory file descriptor? + { + fd -= 0x666; + file[fd].size = 0; + return 0; + } + else + return f_close(&fd); +} + +static ov_callbacks callbacks = { + (size_t (*)(void *, size_t, size_t, void *)) f_read, + (int (*)(void *, ogg_int64_t, int)) f_seek, + (int (*)(void *)) f_close, + (long (*)(void *)) f_tell +}; + +/* OGG control */ + +#define READ_SAMPLES 4096 // samples that it must read before to send +#define MAX_PCMOUT 4096 // minimum size to read ogg samples +typedef struct +{ + OggVorbis_File vf; + vorbis_info *vi; + int current_section; + + // OGG file operation + int fd; + int mode; + int eof; + int flag; + int volume; + int seek_time; + + /* OGG buffer control */ + short pcmout[2][READ_SAMPLES + MAX_PCMOUT * 2]; /* take 4k out of the data segment, not the stack */ + int pcmout_pos; + int pcm_indx; + +} private_data_ogg; + +static private_data_ogg private_ogg; + +// OGG thread control + +#define STACKSIZE 8192 + +static u8 oggplayer_stack[STACKSIZE]; +static lwpq_t oggplayer_queue = LWP_TQUEUE_NULL; +static lwp_t h_oggplayer = LWP_THREAD_NULL; +static int ogg_thread_running = 0; + +static void ogg_add_callback(int voice) +{ + if (!ogg_thread_running) + { + ASND_StopVoice(0); + return; + } + + if (private_ogg.flag & 128) + return; // Ogg is paused + + if (private_ogg.pcm_indx >= READ_SAMPLES) + { + if (ASND_AddVoice(0, + (void *) private_ogg.pcmout[private_ogg.pcmout_pos], + private_ogg.pcm_indx << 1) == 0) + { + private_ogg.pcmout_pos ^= 1; + private_ogg.pcm_indx = 0; + private_ogg.flag = 0; + LWP_ThreadSignal(oggplayer_queue); + } + } + else + { + if (private_ogg.flag & 64) + { + private_ogg.flag &= ~64; + LWP_ThreadSignal(oggplayer_queue); + } + } +} + +static void * ogg_player_thread(private_data_ogg * priv) +{ + int first_time = 1; + long ret; + + //init + LWP_InitQueue(&oggplayer_queue); + + priv[0].vi = ov_info(&priv[0].vf, -1); + + ASND_Pause(0); + + priv[0].pcm_indx = 0; + priv[0].pcmout_pos = 0; + priv[0].eof = 0; + priv[0].flag = 0; + priv[0].current_section = 0; + + ogg_thread_running = 1; + + while (!priv[0].eof && ogg_thread_running) + { + if (priv[0].flag) + LWP_ThreadSleep(oggplayer_queue); // wait only when i have samples to send + + if (priv[0].flag == 0) // wait to all samples are sent + { + if (ASND_TestPointer(0, priv[0].pcmout[priv[0].pcmout_pos]) + && ASND_StatusVoice(0) != SND_UNUSED) + { + priv[0].flag |= 64; + continue; + } + if (priv[0].pcm_indx < READ_SAMPLES) + { + priv[0].flag = 3; + + if (priv[0].seek_time >= 0) + { + ov_time_seek(&priv[0].vf, priv[0].seek_time); + priv[0].seek_time = -1; + } + + ret + = ov_read( + &priv[0].vf, + (void *) &priv[0].pcmout[priv[0].pcmout_pos][priv[0].pcm_indx], + MAX_PCMOUT,/*0,2,1,*/&priv[0].current_section); + priv[0].flag &= 192; + if (ret == 0) + { + /* EOF */ + if (priv[0].mode & 1) + ov_time_seek(&priv[0].vf, 0); // repeat + else + priv[0].eof = 1; // stops + } + else if (ret < 0) + { + /* error in the stream. Not a problem, just reporting it in + case we (the app) cares. In this case, we don't. */ + if (ret != OV_HOLE) + { + if (priv[0].mode & 1) + ov_time_seek(&priv[0].vf, 0); // repeat + else + priv[0].eof = 1; // stops + } + } + else + { + /* we don't bother dealing with sample rate changes, etc, but + you'll have to*/ + priv[0].pcm_indx += ret >> 1; //get 16 bits samples + } + } + else + priv[0].flag = 1; + } + + if (priv[0].flag == 1) + { + if (ASND_StatusVoice(0) == SND_UNUSED || first_time) + { + first_time = 0; + if (priv[0].vi->channels == 2) + { + ASND_SetVoice(0, VOICE_STEREO_16BIT, priv[0].vi->rate, 0, + (void *) priv[0].pcmout[priv[0].pcmout_pos], + priv[0].pcm_indx << 1, priv[0].volume, + priv[0].volume, ogg_add_callback); + priv[0].pcmout_pos ^= 1; + priv[0].pcm_indx = 0; + priv[0].flag = 0; + } + else + { + ASND_SetVoice(0, VOICE_MONO_16BIT, priv[0].vi->rate, 0, + (void *) priv[0].pcmout[priv[0].pcmout_pos], + priv[0].pcm_indx << 1, priv[0].volume, + priv[0].volume, ogg_add_callback); + priv[0].pcmout_pos ^= 1; + priv[0].pcm_indx = 0; + priv[0].flag = 0; + } + } + } + usleep(100); + } + ov_clear(&priv[0].vf); + priv[0].fd = -1; + priv[0].pcm_indx = 0; + + return 0; +} + +void StopOgg() +{ + ASND_StopVoice(0); + ogg_thread_running = 0; + + if(h_oggplayer != LWP_THREAD_NULL) + { + if(oggplayer_queue != LWP_TQUEUE_NULL) + LWP_ThreadSignal(oggplayer_queue); + LWP_JoinThread(h_oggplayer, NULL); + h_oggplayer = LWP_THREAD_NULL; + } + if(oggplayer_queue != LWP_TQUEUE_NULL) + { + LWP_CloseQueue(oggplayer_queue); + oggplayer_queue = LWP_TQUEUE_NULL; + } +} + +int PlayOgg(char * buf, int buflen, int time_pos, int mode) +{ + StopOgg(); + + private_ogg.fd = mem_open(buf, buflen); + + if (private_ogg.fd < 0) + { + private_ogg.fd = -1; + return -1; + } + + private_ogg.mode = mode; + private_ogg.eof = 0; + private_ogg.volume = 127; + private_ogg.flag = 0; + private_ogg.seek_time = -1; + + if (time_pos > 0) + private_ogg.seek_time = time_pos; + + if (ov_open_callbacks((void *) &private_ogg.fd, &private_ogg.vf, NULL, 0, callbacks) < 0) + { + mem_close(private_ogg.fd); // mem_close() can too close files from devices + private_ogg.fd = -1; + ogg_thread_running = 0; + return -1; + } + + if (LWP_CreateThread(&h_oggplayer, (void *) ogg_player_thread, + &private_ogg, oggplayer_stack, STACKSIZE, 80) == -1) + { + ogg_thread_running = 0; + ov_clear(&private_ogg.vf); + private_ogg.fd = -1; + return -1; + } + return 0; +} + +void PauseOgg(int pause) +{ + if (pause) + { + private_ogg.flag |= 128; + } + else + { + if (private_ogg.flag & 128) + { + private_ogg.flag |= 64; + private_ogg.flag &= ~128; + if (ogg_thread_running > 0) + { + LWP_ThreadSignal(oggplayer_queue); + } + } + } +} + +int StatusOgg() +{ + if (ogg_thread_running == 0) + return -1; // Error + else if (private_ogg.eof) + return 255; // EOF + else if (private_ogg.flag & 128) + return 2; // paused + else + return 1; // running +} + +void SetVolumeOgg(int volume) +{ + private_ogg.volume = volume; + ASND_ChangeVolumeVoice(0, volume, volume); +} + +s32 GetTimeOgg() +{ + int ret; + if (ogg_thread_running == 0 || private_ogg.fd < 0) + return 0; + ret = ((s32) ov_time_tell(&private_ogg.vf)); + if (ret < 0) + ret = 0; + + return ret; +} + +void SetTimeOgg(s32 time_pos) +{ + if (time_pos >= 0) + private_ogg.seek_time = time_pos; +} + +#endif diff --git a/source/pngu.c b/source/pngu.c index 8f554ed..1a424a4 100644 --- a/source/pngu.c +++ b/source/pngu.c @@ -1,1266 +1,1266 @@ -/******************************************************************************************** - -PNGU Version : 0.2a - -Coder : frontier - -More info : http://frontier-dev.net - -Modified by Tantric, 2009 - -********************************************************************************************/ -#include -#include -#include "pngu.h" -#include "png.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); -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); - - -// PNGU Image context struct -struct _IMGCTX -{ - int source; - void *buffer; - char *filename; - PNGU_u32 cursor; - - PNGU_u32 propRead; - PNGUPROP prop; - - PNGU_u32 infoRead; - png_structp png_ptr; - png_infop info_ptr; - FILE *fd; - - png_bytep *row_pointers; - png_bytep img_data; -}; - - -// PNGU Implementation // - -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; -} - - -int PNGU_DecodeToYCbYCr (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride) -{ - int result; - PNGU_u32 x, y, buffWidth; - - // width needs to be divisible by two - if (width % 2) - return PNGU_ODD_WIDTH; - - // stride needs to be divisible by two - if (stride % 2) - return PNGU_ODD_STRIDE; - - result = pngu_decode (ctx, width, height, 1); - if (result != PNGU_OK) - return result; - - // Copy image to the output buffer - buffWidth = (width + stride) / 2; - for (y = 0; y < height; y++) - for (x = 0; x < (width / 2); x++) - ((PNGU_u32 *)buffer)[y*buffWidth+x] = PNGU_RGB8_TO_YCbYCr (*(ctx->row_pointers[y]+x*6), *(ctx->row_pointers[y]+x*6+1), *(ctx->row_pointers[y]+x*6+2), - *(ctx->row_pointers[y]+x*6+3), *(ctx->row_pointers[y]+x*6+4), *(ctx->row_pointers[y]+x*6+5)); - - // Free resources - free (ctx->img_data); - free (ctx->row_pointers); - - // Success - return PNGU_OK; -} - - -int PNGU_DecodeToRGB565 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride) -{ - int result; - PNGU_u32 x, y, buffWidth; - - result = pngu_decode (ctx, width, height, 1); - if (result != PNGU_OK) - return result; - - buffWidth = width + stride; - - // Copy image to the output buffer - for (y = 0; y < height; y++) - for (x = 0; x < width; x++) - ((PNGU_u16 *)buffer)[y*buffWidth+x] = - (((PNGU_u16) (ctx->row_pointers[y][x*3] & 0xF8)) << 8) | - (((PNGU_u16) (ctx->row_pointers[y][x*3+1] & 0xFC)) << 3) | - (((PNGU_u16) (ctx->row_pointers[y][x*3+2] & 0xF8)) >> 3); - - // Free resources - free (ctx->img_data); - free (ctx->row_pointers); - - // Success - return PNGU_OK; -} - - -int PNGU_DecodeToRGBA8 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride, PNGU_u8 default_alpha) -{ - int result; - PNGU_u32 x, y, buffWidth; - - result = pngu_decode (ctx, width, height, 0); - if (result != PNGU_OK) - return result; - - buffWidth = width + stride; - - // 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 < height; y++) - memcpy (buffer + (y * buffWidth * 4), ctx->row_pointers[y], width * 4); - } - else - { - // No alpha channel present, copy image to the output buffer - for (y = 0; y < height; y++) - for (x = 0; x < width; x++) - ((PNGU_u32 *)buffer)[y*buffWidth+x] = - (((PNGU_u32) ctx->row_pointers[y][x*3]) << 24) | - (((PNGU_u32) ctx->row_pointers[y][x*3+1]) << 16) | - (((PNGU_u32) ctx->row_pointers[y][x*3+2]) << 8) | - ((PNGU_u32) default_alpha); - } - - // Free resources - free (ctx->img_data); - free (ctx->row_pointers); - - // Success - return PNGU_OK; -} - - -int PNGU_DecodeTo4x4RGB565 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer) -{ - int result; - PNGU_u32 x, y, qwidth, qheight; - - // 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, 1); - if (result != PNGU_OK) - return result; - - // Copy image to the output buffer - qwidth = width / 4; - qheight = height / 4; - - for (y = 0; y < qheight; y++) - for (x = 0; x < qwidth; x++) - { - int blockbase = (y * qwidth + x) * 4; - - PNGU_u64 field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*12)); - PNGU_u64 field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4]+x*12+8)); - ((PNGU_u64 *) buffer)[blockbase] = - (((field64 & 0xF800000000000000ULL) | ((field64 & 0xFC000000000000ULL) << 3) | ((field64 & 0xF80000000000ULL) << 5)) | - (((field64 & 0xF800000000ULL) << 8) | ((field64 & 0xFC000000ULL) << 11) | ((field64 & 0xF80000ULL) << 13)) | - (((field64 & 0xF800ULL) << 16) | ((field64 & 0xFCULL) << 19) | ((field32 & 0xF8000000ULL) >> 11)) | - (((field32 & 0xF80000ULL) >> 8) | ((field32 & 0xFC00ULL) >> 5) | ((field32 & 0xF8ULL) >> 3))); - - field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*12)); - field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+1]+x*12+8)); - ((PNGU_u64 *) buffer)[blockbase+1] = - (((field64 & 0xF800000000000000ULL) | ((field64 & 0xFC000000000000ULL) << 3) | ((field64 & 0xF80000000000ULL) << 5)) | - (((field64 & 0xF800000000ULL) << 8) | ((field64 & 0xFC000000ULL) << 11) | ((field64 & 0xF80000ULL) << 13)) | - (((field64 & 0xF800ULL) << 16) | ((field64 & 0xFCULL) << 19) | ((field32 & 0xF8000000ULL) >> 11)) | - (((field32 & 0xF80000ULL) >> 8) | ((field32 & 0xFC00ULL) >> 5) | ((field32 & 0xF8ULL) >> 3))); - - field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*12)); - field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+2]+x*12+8)); - ((PNGU_u64 *) buffer)[blockbase+2] = - (((field64 & 0xF800000000000000ULL) | ((field64 & 0xFC000000000000ULL) << 3) | ((field64 & 0xF80000000000ULL) << 5)) | - (((field64 & 0xF800000000ULL) << 8) | ((field64 & 0xFC000000ULL) << 11) | ((field64 & 0xF80000ULL) << 13)) | - (((field64 & 0xF800ULL) << 16) | ((field64 & 0xFCULL) << 19) | ((field32 & 0xF8000000ULL) >> 11)) | - (((field32 & 0xF80000ULL) >> 8) | ((field32 & 0xFC00ULL) >> 5) | ((field32 & 0xF8ULL) >> 3))); - - field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*12)); - field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+3]+x*12+8)); - ((PNGU_u64 *) buffer)[blockbase+3] = - (((field64 & 0xF800000000000000ULL) | ((field64 & 0xFC000000000000ULL) << 3) | ((field64 & 0xF80000000000ULL) << 5)) | - (((field64 & 0xF800000000ULL) << 8) | ((field64 & 0xFC000000ULL) << 11) | ((field64 & 0xF80000ULL) << 13)) | - (((field64 & 0xF800ULL) << 16) | ((field64 & 0xFCULL) << 19) | ((field32 & 0xF8000000ULL) >> 11)) | - (((field32 & 0xF80000ULL) >> 8) | ((field32 & 0xFC00ULL) >> 5) | ((field32 & 0xF8ULL) >> 3))); - } - - // Free resources - free (ctx->img_data); - free (ctx->row_pointers); - - // Success - return PNGU_OK; -} - - -int PNGU_DecodeTo4x4RGB5A3 (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 vars - qwidth = width / 4; - qheight = height / 4; - - // 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) * 4; - PNGU_u64 tmp; - - PNGU_u64 fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*16)); - PNGU_u64 fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*16+8)); - // If first pixel is opaque set MSB to 1 and encode colors in RGB555, else set MSB to 0 and encode colors in ARGB3444 - if ((fieldA & 0xE000000000ULL) == 0xE000000000ULL) - tmp = 0x8000000000000000ULL | ((fieldA & 0xF800000000000000ULL) >> 1) | ((fieldA & 0xF8000000000000ULL) << 2) | ((fieldA & 0xF80000000000ULL) << 5); - else - tmp = ((fieldA & 0xE000000000ULL) << 23) | ((fieldA & 0xF000000000000000ULL) >> 4) | (fieldA & 0xF0000000000000ULL) | ((fieldA & 0xF00000000000ULL) << 4); - - // If second pixel is opaque set MSB to 1 and encode colors in RGB555, else set MSB to 0 and encode colors in ARGB3444 - if ((fieldA & 0xE0ULL) == 0xE0ULL) - tmp = tmp | 0x800000000000ULL | ((fieldA & 0xF8000000ULL) << 15) | ((fieldA & 0xF80000ULL) << 18) | ((fieldA & 0xF800ULL) << 21); - else - tmp = tmp | ((fieldA & 0xE0ULL) << 39) | ((fieldA & 0xF0000000ULL) << 12) | ((fieldA & 0xF00000ULL) << 16) | ((fieldA & 0xF000ULL) << 20); - - // If third pixel is opaque set MSB to 1 and encode colors in RGB555, else set MSB to 0 and encode colors in ARGB3444 - if ((fieldB & 0xE000000000ULL) == 0xE000000000ULL) - tmp = tmp | 0x80000000ULL | ((fieldB & 0xF800000000000000ULL) >> 33) | ((fieldB & 0xF8000000000000ULL) >> 30) | ((fieldB & 0xF80000000000ULL) >> 27); - else - tmp = tmp | ((fieldB & 0xE000000000ULL) >> 9) | ((fieldB & 0xF000000000000000ULL) >> 36) | ((fieldB & 0xF0000000000000ULL) >> 32) | ((fieldB & 0xF00000000000ULL) >> 28); - - // If fourth pixel is opaque set MSB to 1 and encode colors in RGB555, else set MSB to 0 and encode colors in ARGB3444 - if ((fieldB & 0xE0ULL) == 0xE0ULL) - tmp = tmp | 0x8000ULL | ((fieldB & 0xF8000000ULL) >> 17) | ((fieldB & 0xF80000ULL) >> 14) | ((fieldB & 0xF800ULL) >> 11); - else - tmp = tmp | ((fieldB & 0xE0ULL) << 7) | ((fieldB & 0xF0000000ULL) >> 20) | ((fieldB & 0xF00000ULL) >> 16) | ((fieldB & 0xF000ULL) >> 12); - ((PNGU_u64 *) buffer)[blockbase] = tmp; - - fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*16)); - fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*16+8)); - if ((fieldA & 0xE000000000ULL) == 0xE000000000ULL) - // Opaque pixel, so set MSB to 1 and encode colors in RGB555 - tmp = 0x8000000000000000ULL | ((fieldA & 0xF800000000000000ULL) >> 1) | ((fieldA & 0xF8000000000000ULL) << 2) | ((fieldA & 0xF80000000000ULL) << 5); - else - // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 - tmp = ((fieldA & 0xE000000000ULL) << 23) | ((fieldA & 0xF000000000000000ULL) >> 4) | (fieldA & 0xF0000000000000ULL) | ((fieldA & 0xF00000000000ULL) << 4); - - if ((fieldA & 0xE0ULL) == 0xE0ULL) - // Opaque pixel, so set MSB to 1 and encode colors in RGB555 - tmp = tmp | 0x800000000000ULL | ((fieldA & 0xF8000000ULL) << 15) | ((fieldA & 0xF80000ULL) << 18) | ((fieldA & 0xF800ULL) << 21); - else - // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 - tmp = tmp | ((fieldA & 0xE0ULL) << 39) | ((fieldA & 0xF0000000ULL) << 12) | ((fieldA & 0xF00000ULL) << 16) | ((fieldA & 0xF000ULL) << 20); - - if ((fieldB & 0xE000000000ULL) == 0xE000000000ULL) - // Opaque pixel, so set MSB to 1 and encode colors in RGB555 - tmp = tmp | 0x80000000ULL | ((fieldB & 0xF800000000000000ULL) >> 33) | ((fieldB & 0xF8000000000000ULL) >> 30) | ((fieldB & 0xF80000000000ULL) >> 27); - else - // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 - tmp = tmp | ((fieldB & 0xE000000000ULL) >> 9) | ((fieldB & 0xF000000000000000ULL) >> 36) | ((fieldB & 0xF0000000000000ULL) >> 32) | ((fieldB & 0xF00000000000ULL) >> 28); - - if ((fieldB & 0xE0ULL) == 0xE0ULL) - // Opaque pixel, so set MSB to 1 and encode colors in RGB555 - tmp = tmp | 0x8000ULL | ((fieldB & 0xF8000000ULL) >> 17) | ((fieldB & 0xF80000ULL) >> 14) | ((fieldB & 0xF800ULL) >> 11); - else - // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 - tmp = tmp | ((fieldB & 0xE0ULL) << 7) | ((fieldB & 0xF0000000ULL) >> 20) | ((fieldB & 0xF00000ULL) >> 16) | ((fieldB & 0xF000ULL) >> 12); - ((PNGU_u64 *) buffer)[blockbase+1] = tmp; - - fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*16)); - fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*16+8)); - if ((fieldA & 0xE000000000ULL) == 0xE000000000ULL) - // Opaque pixel, so set MSB to 1 and encode colors in RGB555 - tmp = 0x8000000000000000ULL | ((fieldA & 0xF800000000000000ULL) >> 1) | ((fieldA & 0xF8000000000000ULL) << 2) | ((fieldA & 0xF80000000000ULL) << 5); - else - // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 - tmp = ((fieldA & 0xE000000000ULL) << 23) | ((fieldA & 0xF000000000000000ULL) >> 4) | (fieldA & 0xF0000000000000ULL) | ((fieldA & 0xF00000000000ULL) << 4); - - if ((fieldA & 0xE0ULL) == 0xE0ULL) - // Opaque pixel, so set MSB to 1 and encode colors in RGB555 - tmp = tmp | 0x800000000000ULL | ((fieldA & 0xF8000000ULL) << 15) | ((fieldA & 0xF80000ULL) << 18) | ((fieldA & 0xF800ULL) << 21); - else - // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 - tmp = tmp | ((fieldA & 0xE0ULL) << 39) | ((fieldA & 0xF0000000ULL) << 12) | ((fieldA & 0xF00000ULL) << 16) | ((fieldA & 0xF000ULL) << 20); - - if ((fieldB & 0xE000000000ULL) == 0xE000000000ULL) - // Opaque pixel, so set MSB to 1 and encode colors in RGB555 - tmp = tmp | 0x80000000ULL | ((fieldB & 0xF800000000000000ULL) >> 33) | ((fieldB & 0xF8000000000000ULL) >> 30) | ((fieldB & 0xF80000000000ULL) >> 27); - else - // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 - tmp = tmp | ((fieldB & 0xE000000000ULL) >> 9) | ((fieldB & 0xF000000000000000ULL) >> 36) | ((fieldB & 0xF0000000000000ULL) >> 32) | ((fieldB & 0xF00000000000ULL) >> 28); - - if ((fieldB & 0xE0ULL) == 0xE0ULL) - // Opaque pixel, so set MSB to 1 and encode colors in RGB555 - tmp = tmp | 0x8000ULL | ((fieldB & 0xF8000000ULL) >> 17) | ((fieldB & 0xF80000ULL) >> 14) | ((fieldB & 0xF800ULL) >> 11); - else - // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 - tmp = tmp | ((fieldB & 0xE0ULL) << 7) | ((fieldB & 0xF0000000ULL) >> 20) | ((fieldB & 0xF00000ULL) >> 16) | ((fieldB & 0xF000ULL) >> 12); - ((PNGU_u64 *) buffer)[blockbase+2] = tmp; - - fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*16)); - fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*16+8)); - if ((fieldA & 0xE000000000ULL) == 0xE000000000ULL) - // Opaque pixel, so set MSB to 1 and encode colors in RGB555 - tmp = 0x8000000000000000ULL | ((fieldA & 0xF800000000000000ULL) >> 1) | ((fieldA & 0xF8000000000000ULL) << 2) | ((fieldA & 0xF80000000000ULL) << 5); - else - // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 - tmp = ((fieldA & 0xE000000000ULL) << 23) | ((fieldA & 0xF000000000000000ULL) >> 4) | (fieldA & 0xF0000000000000ULL) | ((fieldA & 0xF00000000000ULL) << 4); - - if ((fieldA & 0xE0ULL) == 0xE0ULL) - // Opaque pixel, so set MSB to 1 and encode colors in RGB555 - tmp = tmp | 0x800000000000ULL | ((fieldA & 0xF8000000ULL) << 15) | ((fieldA & 0xF80000ULL) << 18) | ((fieldA & 0xF800ULL) << 21); - else - // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 - tmp = tmp | ((fieldA & 0xE0ULL) << 39) | ((fieldA & 0xF0000000ULL) << 12) | ((fieldA & 0xF00000ULL) << 16) | ((fieldA & 0xF000ULL) << 20); - - if ((fieldB & 0xE000000000ULL) == 0xE000000000ULL) - // Opaque pixel, so set MSB to 1 and encode colors in RGB555 - tmp = tmp | 0x80000000ULL | ((fieldB & 0xF800000000000000ULL) >> 33) | ((fieldB & 0xF8000000000000ULL) >> 30) | ((fieldB & 0xF80000000000ULL) >> 27); - else - // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 - tmp = tmp | ((fieldB & 0xE000000000ULL) >> 9) | ((fieldB & 0xF000000000000000ULL) >> 36) | ((fieldB & 0xF0000000000000ULL) >> 32) | ((fieldB & 0xF00000000000ULL) >> 28); - - if ((fieldB & 0xE0ULL) == 0xE0ULL) - // Opaque pixel, so set MSB to 1 and encode colors in RGB555 - tmp = tmp | 0x8000ULL | ((fieldB & 0xF8000000ULL) >> 17) | ((fieldB & 0xF80000ULL) >> 14) | ((fieldB & 0xF800ULL) >> 11); - else - // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 - tmp = tmp | ((fieldB & 0xE0ULL) << 7) | ((fieldB & 0xF0000000ULL) >> 20) | ((fieldB & 0xF00000ULL) >> 16) | ((fieldB & 0xF000ULL) >> 12); - ((PNGU_u64 *) buffer)[blockbase+3] = tmp; - } - } - else - { - // No alpha channel present, copy image to the output buffer - default_alpha = (default_alpha >> 5); - if (default_alpha == 7) - { - // The user wants an opaque texture, so set MSB to 1 and encode colors in RGB555 - alphaMask = 0x8000800080008000ULL; - - for (y = 0; y < qheight; y++) - for (x = 0; x < qwidth; x++) - { - int blockbase = (y * qwidth + x) * 4; - - PNGU_u64 field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*12)); - PNGU_u64 field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4]+x*12+8)); - ((PNGU_u64 *) buffer)[blockbase] = - alphaMask | ((field64 & 0xF800000000000000ULL) >> 1) | ((field64 & 0xF8000000000000ULL) << 2) | - ((field64 & 0xF80000000000ULL) << 5) | ((field64 & 0xF800000000ULL) << 7) | ((field64 & 0xF8000000ULL) << 10) | - ((field64 & 0xF80000ULL) << 13) | ((field64 & 0xF800ULL) << 15) | ((field64 & 0xF8ULL) << 18) | - ((field32 & 0xF8000000ULL) >> 11) | ((field32 & 0xF80000ULL) >> 9) | ((field32 & 0xF800ULL) >> 6) | ((field32 & 0xF8ULL) >> 3); - - field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*12)); - field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+1]+x*12+8)); - ((PNGU_u64 *) buffer)[blockbase+1] = - alphaMask | ((field64 & 0xF800000000000000ULL) >> 1) | ((field64 & 0xF8000000000000ULL) << 2) | - ((field64 & 0xF80000000000ULL) << 5) | ((field64 & 0xF800000000ULL) << 7) | ((field64 & 0xF8000000ULL) << 10) | - ((field64 & 0xF80000ULL) << 13) | ((field64 & 0xF800ULL) << 15) | ((field64 & 0xF8ULL) << 18) | - ((field32 & 0xF8000000ULL) >> 11) | ((field32 & 0xF80000ULL) >> 9) | ((field32 & 0xF800ULL) >> 6) | ((field32 & 0xF8ULL) >> 3); - - field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*12)); - field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+2]+x*12+8)); - ((PNGU_u64 *) buffer)[blockbase+2] = - alphaMask | ((field64 & 0xF800000000000000ULL) >> 1) | ((field64 & 0xF8000000000000ULL) << 2) | - ((field64 & 0xF80000000000ULL) << 5) | ((field64 & 0xF800000000ULL) << 7) | ((field64 & 0xF8000000ULL) << 10) | - ((field64 & 0xF80000ULL) << 13) | ((field64 & 0xF800ULL) << 15) | ((field64 & 0xF8ULL) << 18) | - ((field32 & 0xF8000000ULL) >> 11) | ((field32 & 0xF80000ULL) >> 9) | ((field32 & 0xF800ULL) >> 6) | ((field32 & 0xF8ULL) >> 3); - - field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*12)); - field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+3]+x*12+8)); - ((PNGU_u64 *) buffer)[blockbase+3] = - alphaMask | ((field64 & 0xF800000000000000ULL) >> 1) | ((field64 & 0xF8000000000000ULL) << 2) | - ((field64 & 0xF80000000000ULL) << 5) | ((field64 & 0xF800000000ULL) << 7) | ((field64 & 0xF8000000ULL) << 10) | - ((field64 & 0xF80000ULL) << 13) | ((field64 & 0xF800ULL) << 15) | ((field64 & 0xF8ULL) << 18) | - ((field32 & 0xF8000000ULL) >> 11) | ((field32 & 0xF80000ULL) >> 9) | ((field32 & 0xF800ULL) >> 6) | ((field32 & 0xF8ULL) >> 3); - } - } - else - { - // The user wants a translucid texture, so set MSB to 0 and encode colors in ARGB3444 - default_alpha = (default_alpha << 4); - 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) * 4; - - PNGU_u64 field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*12)); - PNGU_u64 field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4]+x*12+8)); - ((PNGU_u64 *) buffer)[blockbase] = - alphaMask | ((field64 & 0xF000000000000000ULL) >> 4) | (field64 & 0xF0000000000000ULL) | ((field64 & 0xF00000000000ULL) << 4) | - ((field64 & 0xF000000000ULL) << 4) | ((field64 & 0xF0000000ULL) << 8) | ((field64 & 0xF00000ULL) << 12) | - ((field64 & 0xF000ULL) << 12) | ((field64 & 0xF0ULL) << 16) | ((field32 & 0xF0000000ULL) >> 12) | - ((field32 & 0xF00000ULL) >> 12) | ((field32 & 0xF000ULL) >> 8) | ((field32 & 0xF0ULL) >> 4); - - field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*12)); - field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+1]+x*12+8)); - ((PNGU_u64 *) buffer)[blockbase+1] = - alphaMask | ((field64 & 0xF000000000000000ULL) >> 4) | (field64 & 0xF0000000000000ULL) | ((field64 & 0xF00000000000ULL) << 4) | - ((field64 & 0xF000000000ULL) << 4) | ((field64 & 0xF0000000ULL) << 8) | ((field64 & 0xF00000ULL) << 12) | - ((field64 & 0xF000ULL) << 12) | ((field64 & 0xF0ULL) << 16) | ((field32 & 0xF0000000ULL) >> 12) | - ((field32 & 0xF00000ULL) >> 12) | ((field32 & 0xF000ULL) >> 8) | ((field32 & 0xF0ULL) >> 4); - - field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*12)); - field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+2]+x*12+8)); - ((PNGU_u64 *) buffer)[blockbase+2] = - alphaMask | ((field64 & 0xF000000000000000ULL) >> 4) | (field64 & 0xF0000000000000ULL) | ((field64 & 0xF00000000000ULL) << 4) | - ((field64 & 0xF000000000ULL) << 4) | ((field64 & 0xF0000000ULL) << 8) | ((field64 & 0xF00000ULL) << 12) | - ((field64 & 0xF000ULL) << 12) | ((field64 & 0xF0ULL) << 16) | ((field32 & 0xF0000000ULL) >> 12) | - ((field32 & 0xF00000ULL) >> 12) | ((field32 & 0xF000ULL) >> 8) | ((field32 & 0xF0ULL) >> 4); - - field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*12)); - field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+3]+x*12+8)); - ((PNGU_u64 *) buffer)[blockbase+3] = - alphaMask | ((field64 & 0xF000000000000000ULL) >> 4) | (field64 & 0xF0000000000000ULL) | ((field64 & 0xF00000000000ULL) << 4) | - ((field64 & 0xF000000000ULL) << 4) | ((field64 & 0xF0000000ULL) << 8) | ((field64 & 0xF00000ULL) << 12) | - ((field64 & 0xF000ULL) << 12) | ((field64 & 0xF0ULL) << 16) | ((field32 & 0xF0000000ULL) >> 12) | - ((field32 & 0xF00000ULL) >> 12) | ((field32 & 0xF000ULL) >> 8) | ((field32 & 0xF0ULL) >> 4); - } - } - } - - // Free resources - free (ctx->img_data); - free (ctx->row_pointers); - - // Success - 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 / 4; - qheight = height / 4; - - // 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) * 8; - - PNGU_u64 fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*16)); - PNGU_u64 fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*16+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[y*4+1]+x*16)); - fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*16+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[y*4+2]+x*16)); - fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*16+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[y*4+3]+x*16)); - fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*16+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) * 8; - - PNGU_u64 field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*12)); - PNGU_u64 field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4]+x*12+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[y*4+1]+x*12)); - field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+1]+x*12+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[y*4+2]+x*12)); - field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+2]+x*12+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[y*4+3]+x*12)); - field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+3]+x*12+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_EncodeFromYCbYCr (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride) -{ - png_uint_32 rowbytes; - PNGU_u32 x, y, buffWidth; - - // 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); - 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); - 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; - } - - // Encode YCbYCr image into RGB8 format - buffWidth = (width + stride) / 2; - for (y = 0; y < height; y++) - { - ctx->row_pointers[y] = ctx->img_data + (y * rowbytes); - - for (x = 0; x < (width / 2); x++) - PNGU_YCbYCr_TO_RGB8 ( ((PNGU_u32 *)buffer)[y*buffWidth+x], - ((PNGU_u8 *) ctx->row_pointers[y]+x*6), ((PNGU_u8 *) ctx->row_pointers[y]+x*6+1), - ((PNGU_u8 *) ctx->row_pointers[y]+x*6+2), ((PNGU_u8 *) ctx->row_pointers[y]+x*6+3), - ((PNGU_u8 *) ctx->row_pointers[y]+x*6+4), ((PNGU_u8 *) ctx->row_pointers[y]+x*6+5) ); - } - - // 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 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) -{ - int y1, cb1, cr1, y2, cb2, cr2, cb, cr; - - y1 = (299 * r1 + 587 * g1 + 114 * b1) / 1000; - cb1 = (-16874 * r1 - 33126 * g1 + 50000 * b1 + 12800000) / 100000; - cr1 = (50000 * r1 - 41869 * g1 - 8131 * b1 + 12800000) / 100000; - - y2 = (299 * r2 + 587 * g2 + 114 * b2) / 1000; - cb2 = (-16874 * r2 - 33126 * g2 + 50000 * b2 + 12800000) / 100000; - cr2 = (50000 * r2 - 41869 * g2 - 8131 * b2 + 12800000) / 100000; - - cb = (cb1 + cb2) >> 1; - cr = (cr1 + cr2) >> 1; - - return (PNGU_u32) ((y1 << 24) | (cb << 16) | (y2 << 8) | cr); -} - - -void PNGU_YCbYCr_TO_RGB8 (PNGU_u32 ycbycr, PNGU_u8 *r1, PNGU_u8 *g1, PNGU_u8 *b1, PNGU_u8 *r2, PNGU_u8 *g2, PNGU_u8 *b2) -{ - PNGU_u8 *val = (PNGU_u8 *) &ycbycr; - int r, g, b; - - r = 1.371f * (val[3] - 128); - g = - 0.698f * (val[3] - 128) - 0.336f * (val[1] - 128); - b = 1.732f * (val[1] - 128); - - *r1 = pngu_clamp (val[0] + r, 0, 255); - *g1 = pngu_clamp (val[0] + g, 0, 255); - *b1 = pngu_clamp (val[0] + b, 0, 255); - - *r2 = pngu_clamp (val[2] + r, 0, 255); - *g2 = pngu_clamp (val[2] + g, 0, 255); - *b2 = pngu_clamp (val[2] + b, 0, 255); -} - - -int pngu_info (IMGCTX ctx) -{ - png_byte magic[8]; - png_uint_32 width; - png_uint_32 height; - png_color_16p background; - png_bytep trans; - png_color_16p trans_values; - int scale, i; - - // Check if there is a file selected and if it is a valid .png - if (ctx->source == PNGU_SOURCE_BUFFER) - memcpy (magic, ctx->buffer, 8); - - else if (ctx->source == PNGU_SOURCE_DEVICE) - { - // Open file - if (!(ctx->fd = fopen (ctx->filename, "rb"))) - return PNGU_CANT_OPEN_FILE; - - // Load first 8 bytes into magic buffer - if (fread (magic, 1, 8, ctx->fd) != 8) - { - fclose (ctx->fd); - return PNGU_CANT_READ_FILE; - } - } - - else - return PNGU_NO_FILE_SELECTED;; - - if (png_sig_cmp(magic, 0, 8) != 0) - { - if (ctx->source == PNGU_SOURCE_DEVICE) - fclose (ctx->fd); - return PNGU_FILE_IS_NOT_PNG; - } - - // Allocation of libpng structs - ctx->png_ptr = png_create_read_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)) - { - 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; - } - - if (ctx->source == PNGU_SOURCE_BUFFER) - { - // Installation of our custom data provider function - ctx->cursor = 0; - png_set_read_fn (ctx->png_ptr, ctx, pngu_read_data_from_buffer); - } - else if (ctx->source == PNGU_SOURCE_DEVICE) - { - // Default data provider uses function fread, so it needs to use our FILE* - png_init_io (ctx->png_ptr, ctx->fd); - png_set_sig_bytes (ctx->png_ptr, 8); // We have read 8 bytes already to check PNG authenticity - } - - // Read png header - png_read_info (ctx->png_ptr, ctx->info_ptr); - - // Query image properties if they have not been queried before - if (!ctx->propRead) - { - png_get_IHDR(ctx->png_ptr, ctx->info_ptr, &width, &height, - (int *) &(ctx->prop.imgBitDepth), - (int *) &(ctx->prop.imgColorType), - NULL, NULL, NULL); - - ctx->prop.imgWidth = width; - ctx->prop.imgHeight = height; - switch (ctx->prop.imgColorType) - { - case PNG_COLOR_TYPE_GRAY: - ctx->prop.imgColorType = PNGU_COLOR_TYPE_GRAY; - break; - case PNG_COLOR_TYPE_GRAY_ALPHA: - ctx->prop.imgColorType = PNGU_COLOR_TYPE_GRAY_ALPHA; - break; - case PNG_COLOR_TYPE_PALETTE: - ctx->prop.imgColorType = PNGU_COLOR_TYPE_PALETTE; - break; - case PNG_COLOR_TYPE_RGB: - ctx->prop.imgColorType = PNGU_COLOR_TYPE_RGB; - break; - case PNG_COLOR_TYPE_RGB_ALPHA: - ctx->prop.imgColorType = PNGU_COLOR_TYPE_RGB_ALPHA; - break; - default: - ctx->prop.imgColorType = PNGU_COLOR_TYPE_UNKNOWN; - break; - } - - // Constant used to scale 16 bit values to 8 bit values - scale = 1; - if (ctx->prop.imgBitDepth == 16) - scale = 256; - - // Query background color, if any. - ctx->prop.validBckgrnd = 0; - if (((ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB_ALPHA)) && - (png_get_bKGD (ctx->png_ptr, ctx->info_ptr, &background))) - { - ctx->prop.validBckgrnd = 1; - ctx->prop.bckgrnd.r = background->red / scale; - ctx->prop.bckgrnd.g = background->green / scale; - ctx->prop.bckgrnd.b = background->blue / scale; - } - else if (((ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY_ALPHA)) && - (png_get_bKGD (ctx->png_ptr, ctx->info_ptr, &background))) - { - ctx->prop.validBckgrnd = 1; - ctx->prop.bckgrnd.r = ctx->prop.bckgrnd.g = ctx->prop.bckgrnd.b = background->gray / scale; - } - - // Query list of transparent colors, if any. - ctx->prop.numTrans = 0; - ctx->prop.trans = NULL; - if (((ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB_ALPHA)) && - (png_get_tRNS (ctx->png_ptr, ctx->info_ptr, &trans, (int *) &(ctx->prop.numTrans), &trans_values))) - { - if (ctx->prop.numTrans) - { - ctx->prop.trans = malloc (sizeof (PNGUCOLOR) * ctx->prop.numTrans); - if (ctx->prop.trans) - for (i = 0; i < ctx->prop.numTrans; i++) - { - ctx->prop.trans[i].r = trans_values[i].red / scale; - ctx->prop.trans[i].g = trans_values[i].green / scale; - ctx->prop.trans[i].b = trans_values[i].blue / scale; - } - else - ctx->prop.numTrans = 0; - } - } - else if (((ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY_ALPHA)) && - (png_get_tRNS (ctx->png_ptr, ctx->info_ptr, &trans, (int *) &(ctx->prop.numTrans), &trans_values))) - { - if (ctx->prop.numTrans) - { - ctx->prop.trans = malloc (sizeof (PNGUCOLOR) * ctx->prop.numTrans); - if (ctx->prop.trans) - for (i = 0; i < ctx->prop.numTrans; i++) - ctx->prop.trans[i].r = ctx->prop.trans[i].g = ctx->prop.trans[i].b = - trans_values[i].gray / scale; - else - ctx->prop.numTrans = 0; - } - } - - ctx->propRead = 1; - } - - // Success - ctx->infoRead = 1; - - return PNGU_OK; -} - - -int pngu_decode (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stripAlpha) -{ - png_uint_32 rowbytes; - int i; - - // Read info if it hasn't been read before - if (!ctx->infoRead) - { - i = pngu_info (ctx); - if (i != PNGU_OK) - return i; - } - - // Check if the user has specified the real width and height of the image - if ( (ctx->prop.imgWidth != width) || (ctx->prop.imgHeight != height) ) - return PNGU_INVALID_WIDTH_OR_HEIGHT; - - // Check if color type is supported by PNGU - if ( (ctx->prop.imgColorType == PNGU_COLOR_TYPE_PALETTE) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_UNKNOWN) ) - return PNGU_UNSUPPORTED_COLOR_TYPE; - - // Scale 16 bit samples to 8 bit - if (ctx->prop.imgBitDepth == 16) - 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); - - // Expand 1, 2 and 4 bit samples to 8 bit - if (ctx->prop.imgBitDepth < 8) - 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); - - // Flush transformations - png_read_update_info (ctx->png_ptr, ctx->info_ptr); - - // Allocate memory to store the image - rowbytes = png_get_rowbytes (ctx->png_ptr, ctx->info_ptr); - 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 * ctx->prop.imgHeight); - if (!ctx->img_data) - { - pngu_free_info (ctx); - return PNGU_LIB_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; - } - - for (i = 0; i < 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); - - // Free resources - pngu_free_info (ctx); - - // Success - return PNGU_OK; -} - - -void pngu_free_info (IMGCTX ctx) -{ - if (ctx->infoRead) - { - 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; - } -} - - -// 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) -{ - IMGCTX ctx = (IMGCTX) png_get_io_ptr (png_ptr); - memcpy (data, ctx->buffer + ctx->cursor, length); - ctx->cursor += length; -} - - -// 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; -} - - -// Custom data flusher function used for writing to memory buffers. -void pngu_flush_data_to_buffer (png_structp png_ptr) -{ - // Nothing to do here -} - - -// 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; -} - +/******************************************************************************************** + +PNGU Version : 0.2a + +Coder : frontier + +More info : http://frontier-dev.net + +Modified by Tantric, 2009 + +********************************************************************************************/ +#include +#include +#include "pngu.h" +#include "png.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); +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); + + +// PNGU Image context struct +struct _IMGCTX +{ + int source; + void *buffer; + char *filename; + PNGU_u32 cursor; + + PNGU_u32 propRead; + PNGUPROP prop; + + PNGU_u32 infoRead; + png_structp png_ptr; + png_infop info_ptr; + FILE *fd; + + png_bytep *row_pointers; + png_bytep img_data; +}; + + +// PNGU Implementation // + +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; +} + + +int PNGU_DecodeToYCbYCr (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride) +{ + int result; + PNGU_u32 x, y, buffWidth; + + // width needs to be divisible by two + if (width % 2) + return PNGU_ODD_WIDTH; + + // stride needs to be divisible by two + if (stride % 2) + return PNGU_ODD_STRIDE; + + result = pngu_decode (ctx, width, height, 1); + if (result != PNGU_OK) + return result; + + // Copy image to the output buffer + buffWidth = (width + stride) / 2; + for (y = 0; y < height; y++) + for (x = 0; x < (width / 2); x++) + ((PNGU_u32 *)buffer)[y*buffWidth+x] = PNGU_RGB8_TO_YCbYCr (*(ctx->row_pointers[y]+x*6), *(ctx->row_pointers[y]+x*6+1), *(ctx->row_pointers[y]+x*6+2), + *(ctx->row_pointers[y]+x*6+3), *(ctx->row_pointers[y]+x*6+4), *(ctx->row_pointers[y]+x*6+5)); + + // Free resources + free (ctx->img_data); + free (ctx->row_pointers); + + // Success + return PNGU_OK; +} + + +int PNGU_DecodeToRGB565 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride) +{ + int result; + PNGU_u32 x, y, buffWidth; + + result = pngu_decode (ctx, width, height, 1); + if (result != PNGU_OK) + return result; + + buffWidth = width + stride; + + // Copy image to the output buffer + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + ((PNGU_u16 *)buffer)[y*buffWidth+x] = + (((PNGU_u16) (ctx->row_pointers[y][x*3] & 0xF8)) << 8) | + (((PNGU_u16) (ctx->row_pointers[y][x*3+1] & 0xFC)) << 3) | + (((PNGU_u16) (ctx->row_pointers[y][x*3+2] & 0xF8)) >> 3); + + // Free resources + free (ctx->img_data); + free (ctx->row_pointers); + + // Success + return PNGU_OK; +} + + +int PNGU_DecodeToRGBA8 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride, PNGU_u8 default_alpha) +{ + int result; + PNGU_u32 x, y, buffWidth; + + result = pngu_decode (ctx, width, height, 0); + if (result != PNGU_OK) + return result; + + buffWidth = width + stride; + + // 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 < height; y++) + memcpy (buffer + (y * buffWidth * 4), ctx->row_pointers[y], width * 4); + } + else + { + // No alpha channel present, copy image to the output buffer + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + ((PNGU_u32 *)buffer)[y*buffWidth+x] = + (((PNGU_u32) ctx->row_pointers[y][x*3]) << 24) | + (((PNGU_u32) ctx->row_pointers[y][x*3+1]) << 16) | + (((PNGU_u32) ctx->row_pointers[y][x*3+2]) << 8) | + ((PNGU_u32) default_alpha); + } + + // Free resources + free (ctx->img_data); + free (ctx->row_pointers); + + // Success + return PNGU_OK; +} + + +int PNGU_DecodeTo4x4RGB565 (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer) +{ + int result; + PNGU_u32 x, y, qwidth, qheight; + + // 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, 1); + if (result != PNGU_OK) + return result; + + // Copy image to the output buffer + qwidth = width / 4; + qheight = height / 4; + + for (y = 0; y < qheight; y++) + for (x = 0; x < qwidth; x++) + { + int blockbase = (y * qwidth + x) * 4; + + PNGU_u64 field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*12)); + PNGU_u64 field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase] = + (((field64 & 0xF800000000000000ULL) | ((field64 & 0xFC000000000000ULL) << 3) | ((field64 & 0xF80000000000ULL) << 5)) | + (((field64 & 0xF800000000ULL) << 8) | ((field64 & 0xFC000000ULL) << 11) | ((field64 & 0xF80000ULL) << 13)) | + (((field64 & 0xF800ULL) << 16) | ((field64 & 0xFCULL) << 19) | ((field32 & 0xF8000000ULL) >> 11)) | + (((field32 & 0xF80000ULL) >> 8) | ((field32 & 0xFC00ULL) >> 5) | ((field32 & 0xF8ULL) >> 3))); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+1]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+1] = + (((field64 & 0xF800000000000000ULL) | ((field64 & 0xFC000000000000ULL) << 3) | ((field64 & 0xF80000000000ULL) << 5)) | + (((field64 & 0xF800000000ULL) << 8) | ((field64 & 0xFC000000ULL) << 11) | ((field64 & 0xF80000ULL) << 13)) | + (((field64 & 0xF800ULL) << 16) | ((field64 & 0xFCULL) << 19) | ((field32 & 0xF8000000ULL) >> 11)) | + (((field32 & 0xF80000ULL) >> 8) | ((field32 & 0xFC00ULL) >> 5) | ((field32 & 0xF8ULL) >> 3))); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+2]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+2] = + (((field64 & 0xF800000000000000ULL) | ((field64 & 0xFC000000000000ULL) << 3) | ((field64 & 0xF80000000000ULL) << 5)) | + (((field64 & 0xF800000000ULL) << 8) | ((field64 & 0xFC000000ULL) << 11) | ((field64 & 0xF80000ULL) << 13)) | + (((field64 & 0xF800ULL) << 16) | ((field64 & 0xFCULL) << 19) | ((field32 & 0xF8000000ULL) >> 11)) | + (((field32 & 0xF80000ULL) >> 8) | ((field32 & 0xFC00ULL) >> 5) | ((field32 & 0xF8ULL) >> 3))); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+3]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+3] = + (((field64 & 0xF800000000000000ULL) | ((field64 & 0xFC000000000000ULL) << 3) | ((field64 & 0xF80000000000ULL) << 5)) | + (((field64 & 0xF800000000ULL) << 8) | ((field64 & 0xFC000000ULL) << 11) | ((field64 & 0xF80000ULL) << 13)) | + (((field64 & 0xF800ULL) << 16) | ((field64 & 0xFCULL) << 19) | ((field32 & 0xF8000000ULL) >> 11)) | + (((field32 & 0xF80000ULL) >> 8) | ((field32 & 0xFC00ULL) >> 5) | ((field32 & 0xF8ULL) >> 3))); + } + + // Free resources + free (ctx->img_data); + free (ctx->row_pointers); + + // Success + return PNGU_OK; +} + + +int PNGU_DecodeTo4x4RGB5A3 (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 vars + qwidth = width / 4; + qheight = height / 4; + + // 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) * 4; + PNGU_u64 tmp; + + PNGU_u64 fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*16)); + PNGU_u64 fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*16+8)); + // If first pixel is opaque set MSB to 1 and encode colors in RGB555, else set MSB to 0 and encode colors in ARGB3444 + if ((fieldA & 0xE000000000ULL) == 0xE000000000ULL) + tmp = 0x8000000000000000ULL | ((fieldA & 0xF800000000000000ULL) >> 1) | ((fieldA & 0xF8000000000000ULL) << 2) | ((fieldA & 0xF80000000000ULL) << 5); + else + tmp = ((fieldA & 0xE000000000ULL) << 23) | ((fieldA & 0xF000000000000000ULL) >> 4) | (fieldA & 0xF0000000000000ULL) | ((fieldA & 0xF00000000000ULL) << 4); + + // If second pixel is opaque set MSB to 1 and encode colors in RGB555, else set MSB to 0 and encode colors in ARGB3444 + if ((fieldA & 0xE0ULL) == 0xE0ULL) + tmp = tmp | 0x800000000000ULL | ((fieldA & 0xF8000000ULL) << 15) | ((fieldA & 0xF80000ULL) << 18) | ((fieldA & 0xF800ULL) << 21); + else + tmp = tmp | ((fieldA & 0xE0ULL) << 39) | ((fieldA & 0xF0000000ULL) << 12) | ((fieldA & 0xF00000ULL) << 16) | ((fieldA & 0xF000ULL) << 20); + + // If third pixel is opaque set MSB to 1 and encode colors in RGB555, else set MSB to 0 and encode colors in ARGB3444 + if ((fieldB & 0xE000000000ULL) == 0xE000000000ULL) + tmp = tmp | 0x80000000ULL | ((fieldB & 0xF800000000000000ULL) >> 33) | ((fieldB & 0xF8000000000000ULL) >> 30) | ((fieldB & 0xF80000000000ULL) >> 27); + else + tmp = tmp | ((fieldB & 0xE000000000ULL) >> 9) | ((fieldB & 0xF000000000000000ULL) >> 36) | ((fieldB & 0xF0000000000000ULL) >> 32) | ((fieldB & 0xF00000000000ULL) >> 28); + + // If fourth pixel is opaque set MSB to 1 and encode colors in RGB555, else set MSB to 0 and encode colors in ARGB3444 + if ((fieldB & 0xE0ULL) == 0xE0ULL) + tmp = tmp | 0x8000ULL | ((fieldB & 0xF8000000ULL) >> 17) | ((fieldB & 0xF80000ULL) >> 14) | ((fieldB & 0xF800ULL) >> 11); + else + tmp = tmp | ((fieldB & 0xE0ULL) << 7) | ((fieldB & 0xF0000000ULL) >> 20) | ((fieldB & 0xF00000ULL) >> 16) | ((fieldB & 0xF000ULL) >> 12); + ((PNGU_u64 *) buffer)[blockbase] = tmp; + + fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*16)); + fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*16+8)); + if ((fieldA & 0xE000000000ULL) == 0xE000000000ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = 0x8000000000000000ULL | ((fieldA & 0xF800000000000000ULL) >> 1) | ((fieldA & 0xF8000000000000ULL) << 2) | ((fieldA & 0xF80000000000ULL) << 5); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = ((fieldA & 0xE000000000ULL) << 23) | ((fieldA & 0xF000000000000000ULL) >> 4) | (fieldA & 0xF0000000000000ULL) | ((fieldA & 0xF00000000000ULL) << 4); + + if ((fieldA & 0xE0ULL) == 0xE0ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x800000000000ULL | ((fieldA & 0xF8000000ULL) << 15) | ((fieldA & 0xF80000ULL) << 18) | ((fieldA & 0xF800ULL) << 21); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldA & 0xE0ULL) << 39) | ((fieldA & 0xF0000000ULL) << 12) | ((fieldA & 0xF00000ULL) << 16) | ((fieldA & 0xF000ULL) << 20); + + if ((fieldB & 0xE000000000ULL) == 0xE000000000ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x80000000ULL | ((fieldB & 0xF800000000000000ULL) >> 33) | ((fieldB & 0xF8000000000000ULL) >> 30) | ((fieldB & 0xF80000000000ULL) >> 27); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldB & 0xE000000000ULL) >> 9) | ((fieldB & 0xF000000000000000ULL) >> 36) | ((fieldB & 0xF0000000000000ULL) >> 32) | ((fieldB & 0xF00000000000ULL) >> 28); + + if ((fieldB & 0xE0ULL) == 0xE0ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x8000ULL | ((fieldB & 0xF8000000ULL) >> 17) | ((fieldB & 0xF80000ULL) >> 14) | ((fieldB & 0xF800ULL) >> 11); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldB & 0xE0ULL) << 7) | ((fieldB & 0xF0000000ULL) >> 20) | ((fieldB & 0xF00000ULL) >> 16) | ((fieldB & 0xF000ULL) >> 12); + ((PNGU_u64 *) buffer)[blockbase+1] = tmp; + + fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*16)); + fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*16+8)); + if ((fieldA & 0xE000000000ULL) == 0xE000000000ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = 0x8000000000000000ULL | ((fieldA & 0xF800000000000000ULL) >> 1) | ((fieldA & 0xF8000000000000ULL) << 2) | ((fieldA & 0xF80000000000ULL) << 5); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = ((fieldA & 0xE000000000ULL) << 23) | ((fieldA & 0xF000000000000000ULL) >> 4) | (fieldA & 0xF0000000000000ULL) | ((fieldA & 0xF00000000000ULL) << 4); + + if ((fieldA & 0xE0ULL) == 0xE0ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x800000000000ULL | ((fieldA & 0xF8000000ULL) << 15) | ((fieldA & 0xF80000ULL) << 18) | ((fieldA & 0xF800ULL) << 21); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldA & 0xE0ULL) << 39) | ((fieldA & 0xF0000000ULL) << 12) | ((fieldA & 0xF00000ULL) << 16) | ((fieldA & 0xF000ULL) << 20); + + if ((fieldB & 0xE000000000ULL) == 0xE000000000ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x80000000ULL | ((fieldB & 0xF800000000000000ULL) >> 33) | ((fieldB & 0xF8000000000000ULL) >> 30) | ((fieldB & 0xF80000000000ULL) >> 27); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldB & 0xE000000000ULL) >> 9) | ((fieldB & 0xF000000000000000ULL) >> 36) | ((fieldB & 0xF0000000000000ULL) >> 32) | ((fieldB & 0xF00000000000ULL) >> 28); + + if ((fieldB & 0xE0ULL) == 0xE0ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x8000ULL | ((fieldB & 0xF8000000ULL) >> 17) | ((fieldB & 0xF80000ULL) >> 14) | ((fieldB & 0xF800ULL) >> 11); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldB & 0xE0ULL) << 7) | ((fieldB & 0xF0000000ULL) >> 20) | ((fieldB & 0xF00000ULL) >> 16) | ((fieldB & 0xF000ULL) >> 12); + ((PNGU_u64 *) buffer)[blockbase+2] = tmp; + + fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*16)); + fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*16+8)); + if ((fieldA & 0xE000000000ULL) == 0xE000000000ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = 0x8000000000000000ULL | ((fieldA & 0xF800000000000000ULL) >> 1) | ((fieldA & 0xF8000000000000ULL) << 2) | ((fieldA & 0xF80000000000ULL) << 5); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = ((fieldA & 0xE000000000ULL) << 23) | ((fieldA & 0xF000000000000000ULL) >> 4) | (fieldA & 0xF0000000000000ULL) | ((fieldA & 0xF00000000000ULL) << 4); + + if ((fieldA & 0xE0ULL) == 0xE0ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x800000000000ULL | ((fieldA & 0xF8000000ULL) << 15) | ((fieldA & 0xF80000ULL) << 18) | ((fieldA & 0xF800ULL) << 21); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldA & 0xE0ULL) << 39) | ((fieldA & 0xF0000000ULL) << 12) | ((fieldA & 0xF00000ULL) << 16) | ((fieldA & 0xF000ULL) << 20); + + if ((fieldB & 0xE000000000ULL) == 0xE000000000ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x80000000ULL | ((fieldB & 0xF800000000000000ULL) >> 33) | ((fieldB & 0xF8000000000000ULL) >> 30) | ((fieldB & 0xF80000000000ULL) >> 27); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldB & 0xE000000000ULL) >> 9) | ((fieldB & 0xF000000000000000ULL) >> 36) | ((fieldB & 0xF0000000000000ULL) >> 32) | ((fieldB & 0xF00000000000ULL) >> 28); + + if ((fieldB & 0xE0ULL) == 0xE0ULL) + // Opaque pixel, so set MSB to 1 and encode colors in RGB555 + tmp = tmp | 0x8000ULL | ((fieldB & 0xF8000000ULL) >> 17) | ((fieldB & 0xF80000ULL) >> 14) | ((fieldB & 0xF800ULL) >> 11); + else + // Tranlucid pixel, so set MSB to 0 and encode colors in ARGB3444 + tmp = tmp | ((fieldB & 0xE0ULL) << 7) | ((fieldB & 0xF0000000ULL) >> 20) | ((fieldB & 0xF00000ULL) >> 16) | ((fieldB & 0xF000ULL) >> 12); + ((PNGU_u64 *) buffer)[blockbase+3] = tmp; + } + } + else + { + // No alpha channel present, copy image to the output buffer + default_alpha = (default_alpha >> 5); + if (default_alpha == 7) + { + // The user wants an opaque texture, so set MSB to 1 and encode colors in RGB555 + alphaMask = 0x8000800080008000ULL; + + for (y = 0; y < qheight; y++) + for (x = 0; x < qwidth; x++) + { + int blockbase = (y * qwidth + x) * 4; + + PNGU_u64 field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*12)); + PNGU_u64 field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase] = + alphaMask | ((field64 & 0xF800000000000000ULL) >> 1) | ((field64 & 0xF8000000000000ULL) << 2) | + ((field64 & 0xF80000000000ULL) << 5) | ((field64 & 0xF800000000ULL) << 7) | ((field64 & 0xF8000000ULL) << 10) | + ((field64 & 0xF80000ULL) << 13) | ((field64 & 0xF800ULL) << 15) | ((field64 & 0xF8ULL) << 18) | + ((field32 & 0xF8000000ULL) >> 11) | ((field32 & 0xF80000ULL) >> 9) | ((field32 & 0xF800ULL) >> 6) | ((field32 & 0xF8ULL) >> 3); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+1]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+1] = + alphaMask | ((field64 & 0xF800000000000000ULL) >> 1) | ((field64 & 0xF8000000000000ULL) << 2) | + ((field64 & 0xF80000000000ULL) << 5) | ((field64 & 0xF800000000ULL) << 7) | ((field64 & 0xF8000000ULL) << 10) | + ((field64 & 0xF80000ULL) << 13) | ((field64 & 0xF800ULL) << 15) | ((field64 & 0xF8ULL) << 18) | + ((field32 & 0xF8000000ULL) >> 11) | ((field32 & 0xF80000ULL) >> 9) | ((field32 & 0xF800ULL) >> 6) | ((field32 & 0xF8ULL) >> 3); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+2]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+2] = + alphaMask | ((field64 & 0xF800000000000000ULL) >> 1) | ((field64 & 0xF8000000000000ULL) << 2) | + ((field64 & 0xF80000000000ULL) << 5) | ((field64 & 0xF800000000ULL) << 7) | ((field64 & 0xF8000000ULL) << 10) | + ((field64 & 0xF80000ULL) << 13) | ((field64 & 0xF800ULL) << 15) | ((field64 & 0xF8ULL) << 18) | + ((field32 & 0xF8000000ULL) >> 11) | ((field32 & 0xF80000ULL) >> 9) | ((field32 & 0xF800ULL) >> 6) | ((field32 & 0xF8ULL) >> 3); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+3]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+3] = + alphaMask | ((field64 & 0xF800000000000000ULL) >> 1) | ((field64 & 0xF8000000000000ULL) << 2) | + ((field64 & 0xF80000000000ULL) << 5) | ((field64 & 0xF800000000ULL) << 7) | ((field64 & 0xF8000000ULL) << 10) | + ((field64 & 0xF80000ULL) << 13) | ((field64 & 0xF800ULL) << 15) | ((field64 & 0xF8ULL) << 18) | + ((field32 & 0xF8000000ULL) >> 11) | ((field32 & 0xF80000ULL) >> 9) | ((field32 & 0xF800ULL) >> 6) | ((field32 & 0xF8ULL) >> 3); + } + } + else + { + // The user wants a translucid texture, so set MSB to 0 and encode colors in ARGB3444 + default_alpha = (default_alpha << 4); + 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) * 4; + + PNGU_u64 field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*12)); + PNGU_u64 field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase] = + alphaMask | ((field64 & 0xF000000000000000ULL) >> 4) | (field64 & 0xF0000000000000ULL) | ((field64 & 0xF00000000000ULL) << 4) | + ((field64 & 0xF000000000ULL) << 4) | ((field64 & 0xF0000000ULL) << 8) | ((field64 & 0xF00000ULL) << 12) | + ((field64 & 0xF000ULL) << 12) | ((field64 & 0xF0ULL) << 16) | ((field32 & 0xF0000000ULL) >> 12) | + ((field32 & 0xF00000ULL) >> 12) | ((field32 & 0xF000ULL) >> 8) | ((field32 & 0xF0ULL) >> 4); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+1]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+1] = + alphaMask | ((field64 & 0xF000000000000000ULL) >> 4) | (field64 & 0xF0000000000000ULL) | ((field64 & 0xF00000000000ULL) << 4) | + ((field64 & 0xF000000000ULL) << 4) | ((field64 & 0xF0000000ULL) << 8) | ((field64 & 0xF00000ULL) << 12) | + ((field64 & 0xF000ULL) << 12) | ((field64 & 0xF0ULL) << 16) | ((field32 & 0xF0000000ULL) >> 12) | + ((field32 & 0xF00000ULL) >> 12) | ((field32 & 0xF000ULL) >> 8) | ((field32 & 0xF0ULL) >> 4); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+2]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+2] = + alphaMask | ((field64 & 0xF000000000000000ULL) >> 4) | (field64 & 0xF0000000000000ULL) | ((field64 & 0xF00000000000ULL) << 4) | + ((field64 & 0xF000000000ULL) << 4) | ((field64 & 0xF0000000ULL) << 8) | ((field64 & 0xF00000ULL) << 12) | + ((field64 & 0xF000ULL) << 12) | ((field64 & 0xF0ULL) << 16) | ((field32 & 0xF0000000ULL) >> 12) | + ((field32 & 0xF00000ULL) >> 12) | ((field32 & 0xF000ULL) >> 8) | ((field32 & 0xF0ULL) >> 4); + + field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+3]+x*12+8)); + ((PNGU_u64 *) buffer)[blockbase+3] = + alphaMask | ((field64 & 0xF000000000000000ULL) >> 4) | (field64 & 0xF0000000000000ULL) | ((field64 & 0xF00000000000ULL) << 4) | + ((field64 & 0xF000000000ULL) << 4) | ((field64 & 0xF0000000ULL) << 8) | ((field64 & 0xF00000ULL) << 12) | + ((field64 & 0xF000ULL) << 12) | ((field64 & 0xF0ULL) << 16) | ((field32 & 0xF0000000ULL) >> 12) | + ((field32 & 0xF00000ULL) >> 12) | ((field32 & 0xF000ULL) >> 8) | ((field32 & 0xF0ULL) >> 4); + } + } + } + + // Free resources + free (ctx->img_data); + free (ctx->row_pointers); + + // Success + 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 / 4; + qheight = height / 4; + + // 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) * 8; + + PNGU_u64 fieldA = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*16)); + PNGU_u64 fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*16+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[y*4+1]+x*16)); + fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+1]+x*16+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[y*4+2]+x*16)); + fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+2]+x*16+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[y*4+3]+x*16)); + fieldB = *((PNGU_u64 *)(ctx->row_pointers[y*4+3]+x*16+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) * 8; + + PNGU_u64 field64 = *((PNGU_u64 *)(ctx->row_pointers[y*4]+x*12)); + PNGU_u64 field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4]+x*12+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[y*4+1]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+1]+x*12+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[y*4+2]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+2]+x*12+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[y*4+3]+x*12)); + field32 = (PNGU_u64) *((PNGU_u32 *)(ctx->row_pointers[y*4+3]+x*12+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_EncodeFromYCbYCr (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride) +{ + png_uint_32 rowbytes; + PNGU_u32 x, y, buffWidth; + + // 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); + 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); + 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; + } + + // Encode YCbYCr image into RGB8 format + buffWidth = (width + stride) / 2; + for (y = 0; y < height; y++) + { + ctx->row_pointers[y] = ctx->img_data + (y * rowbytes); + + for (x = 0; x < (width / 2); x++) + PNGU_YCbYCr_TO_RGB8 ( ((PNGU_u32 *)buffer)[y*buffWidth+x], + ((PNGU_u8 *) ctx->row_pointers[y]+x*6), ((PNGU_u8 *) ctx->row_pointers[y]+x*6+1), + ((PNGU_u8 *) ctx->row_pointers[y]+x*6+2), ((PNGU_u8 *) ctx->row_pointers[y]+x*6+3), + ((PNGU_u8 *) ctx->row_pointers[y]+x*6+4), ((PNGU_u8 *) ctx->row_pointers[y]+x*6+5) ); + } + + // 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 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) +{ + int y1, cb1, cr1, y2, cb2, cr2, cb, cr; + + y1 = (299 * r1 + 587 * g1 + 114 * b1) / 1000; + cb1 = (-16874 * r1 - 33126 * g1 + 50000 * b1 + 12800000) / 100000; + cr1 = (50000 * r1 - 41869 * g1 - 8131 * b1 + 12800000) / 100000; + + y2 = (299 * r2 + 587 * g2 + 114 * b2) / 1000; + cb2 = (-16874 * r2 - 33126 * g2 + 50000 * b2 + 12800000) / 100000; + cr2 = (50000 * r2 - 41869 * g2 - 8131 * b2 + 12800000) / 100000; + + cb = (cb1 + cb2) >> 1; + cr = (cr1 + cr2) >> 1; + + return (PNGU_u32) ((y1 << 24) | (cb << 16) | (y2 << 8) | cr); +} + + +void PNGU_YCbYCr_TO_RGB8 (PNGU_u32 ycbycr, PNGU_u8 *r1, PNGU_u8 *g1, PNGU_u8 *b1, PNGU_u8 *r2, PNGU_u8 *g2, PNGU_u8 *b2) +{ + PNGU_u8 *val = (PNGU_u8 *) &ycbycr; + int r, g, b; + + r = 1.371f * (val[3] - 128); + g = - 0.698f * (val[3] - 128) - 0.336f * (val[1] - 128); + b = 1.732f * (val[1] - 128); + + *r1 = pngu_clamp (val[0] + r, 0, 255); + *g1 = pngu_clamp (val[0] + g, 0, 255); + *b1 = pngu_clamp (val[0] + b, 0, 255); + + *r2 = pngu_clamp (val[2] + r, 0, 255); + *g2 = pngu_clamp (val[2] + g, 0, 255); + *b2 = pngu_clamp (val[2] + b, 0, 255); +} + + +int pngu_info (IMGCTX ctx) +{ + png_byte magic[8]; + png_uint_32 width; + png_uint_32 height; + png_color_16p background; + png_bytep trans; + png_color_16p trans_values; + int scale, i; + + // Check if there is a file selected and if it is a valid .png + if (ctx->source == PNGU_SOURCE_BUFFER) + memcpy (magic, ctx->buffer, 8); + + else if (ctx->source == PNGU_SOURCE_DEVICE) + { + // Open file + if (!(ctx->fd = fopen (ctx->filename, "rb"))) + return PNGU_CANT_OPEN_FILE; + + // Load first 8 bytes into magic buffer + if (fread (magic, 1, 8, ctx->fd) != 8) + { + fclose (ctx->fd); + return PNGU_CANT_READ_FILE; + } + } + + else + return PNGU_NO_FILE_SELECTED;; + + if (png_sig_cmp(magic, 0, 8) != 0) + { + if (ctx->source == PNGU_SOURCE_DEVICE) + fclose (ctx->fd); + return PNGU_FILE_IS_NOT_PNG; + } + + // Allocation of libpng structs + ctx->png_ptr = png_create_read_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)) + { + 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; + } + + if (ctx->source == PNGU_SOURCE_BUFFER) + { + // Installation of our custom data provider function + ctx->cursor = 0; + png_set_read_fn (ctx->png_ptr, ctx, pngu_read_data_from_buffer); + } + else if (ctx->source == PNGU_SOURCE_DEVICE) + { + // Default data provider uses function fread, so it needs to use our FILE* + png_init_io (ctx->png_ptr, ctx->fd); + png_set_sig_bytes (ctx->png_ptr, 8); // We have read 8 bytes already to check PNG authenticity + } + + // Read png header + png_read_info (ctx->png_ptr, ctx->info_ptr); + + // Query image properties if they have not been queried before + if (!ctx->propRead) + { + png_get_IHDR(ctx->png_ptr, ctx->info_ptr, &width, &height, + (int *) &(ctx->prop.imgBitDepth), + (int *) &(ctx->prop.imgColorType), + NULL, NULL, NULL); + + ctx->prop.imgWidth = width; + ctx->prop.imgHeight = height; + switch (ctx->prop.imgColorType) + { + case PNG_COLOR_TYPE_GRAY: + ctx->prop.imgColorType = PNGU_COLOR_TYPE_GRAY; + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + ctx->prop.imgColorType = PNGU_COLOR_TYPE_GRAY_ALPHA; + break; + case PNG_COLOR_TYPE_PALETTE: + ctx->prop.imgColorType = PNGU_COLOR_TYPE_PALETTE; + break; + case PNG_COLOR_TYPE_RGB: + ctx->prop.imgColorType = PNGU_COLOR_TYPE_RGB; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + ctx->prop.imgColorType = PNGU_COLOR_TYPE_RGB_ALPHA; + break; + default: + ctx->prop.imgColorType = PNGU_COLOR_TYPE_UNKNOWN; + break; + } + + // Constant used to scale 16 bit values to 8 bit values + scale = 1; + if (ctx->prop.imgBitDepth == 16) + scale = 256; + + // Query background color, if any. + ctx->prop.validBckgrnd = 0; + if (((ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB_ALPHA)) && + (png_get_bKGD (ctx->png_ptr, ctx->info_ptr, &background))) + { + ctx->prop.validBckgrnd = 1; + ctx->prop.bckgrnd.r = background->red / scale; + ctx->prop.bckgrnd.g = background->green / scale; + ctx->prop.bckgrnd.b = background->blue / scale; + } + else if (((ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY_ALPHA)) && + (png_get_bKGD (ctx->png_ptr, ctx->info_ptr, &background))) + { + ctx->prop.validBckgrnd = 1; + ctx->prop.bckgrnd.r = ctx->prop.bckgrnd.g = ctx->prop.bckgrnd.b = background->gray / scale; + } + + // Query list of transparent colors, if any. + ctx->prop.numTrans = 0; + ctx->prop.trans = NULL; + if (((ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB_ALPHA)) && + (png_get_tRNS (ctx->png_ptr, ctx->info_ptr, &trans, (int *) &(ctx->prop.numTrans), &trans_values))) + { + if (ctx->prop.numTrans) + { + ctx->prop.trans = malloc (sizeof (PNGUCOLOR) * ctx->prop.numTrans); + if (ctx->prop.trans) + for (i = 0; i < ctx->prop.numTrans; i++) + { + ctx->prop.trans[i].r = trans_values[i].red / scale; + ctx->prop.trans[i].g = trans_values[i].green / scale; + ctx->prop.trans[i].b = trans_values[i].blue / scale; + } + else + ctx->prop.numTrans = 0; + } + } + else if (((ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY_ALPHA)) && + (png_get_tRNS (ctx->png_ptr, ctx->info_ptr, &trans, (int *) &(ctx->prop.numTrans), &trans_values))) + { + if (ctx->prop.numTrans) + { + ctx->prop.trans = malloc (sizeof (PNGUCOLOR) * ctx->prop.numTrans); + if (ctx->prop.trans) + for (i = 0; i < ctx->prop.numTrans; i++) + ctx->prop.trans[i].r = ctx->prop.trans[i].g = ctx->prop.trans[i].b = + trans_values[i].gray / scale; + else + ctx->prop.numTrans = 0; + } + } + + ctx->propRead = 1; + } + + // Success + ctx->infoRead = 1; + + return PNGU_OK; +} + + +int pngu_decode (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stripAlpha) +{ + png_uint_32 rowbytes; + int i; + + // Read info if it hasn't been read before + if (!ctx->infoRead) + { + i = pngu_info (ctx); + if (i != PNGU_OK) + return i; + } + + // Check if the user has specified the real width and height of the image + if ( (ctx->prop.imgWidth != width) || (ctx->prop.imgHeight != height) ) + return PNGU_INVALID_WIDTH_OR_HEIGHT; + + // Check if color type is supported by PNGU + if ( (ctx->prop.imgColorType == PNGU_COLOR_TYPE_PALETTE) || (ctx->prop.imgColorType == PNGU_COLOR_TYPE_UNKNOWN) ) + return PNGU_UNSUPPORTED_COLOR_TYPE; + + // Scale 16 bit samples to 8 bit + if (ctx->prop.imgBitDepth == 16) + 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); + + // Expand 1, 2 and 4 bit samples to 8 bit + if (ctx->prop.imgBitDepth < 8) + 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); + + // Flush transformations + png_read_update_info (ctx->png_ptr, ctx->info_ptr); + + // Allocate memory to store the image + rowbytes = png_get_rowbytes (ctx->png_ptr, ctx->info_ptr); + 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 * ctx->prop.imgHeight); + if (!ctx->img_data) + { + pngu_free_info (ctx); + return PNGU_LIB_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; + } + + for (i = 0; i < 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); + + // Free resources + pngu_free_info (ctx); + + // Success + return PNGU_OK; +} + + +void pngu_free_info (IMGCTX ctx) +{ + if (ctx->infoRead) + { + 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; + } +} + + +// 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) +{ + IMGCTX ctx = (IMGCTX) png_get_io_ptr (png_ptr); + memcpy (data, ctx->buffer + ctx->cursor, length); + ctx->cursor += length; +} + + +// 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; +} + + +// Custom data flusher function used for writing to memory buffers. +void pngu_flush_data_to_buffer (png_structp png_ptr) +{ + // Nothing to do here +} + + +// 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; +} + diff --git a/svnrev/svnrev.h b/svnrev/svnrev.h index 61ecc1e..70a0dc8 100644 --- a/svnrev/svnrev.h +++ b/svnrev/svnrev.h @@ -1,14 +1,14 @@ -#ifndef SVNREV_H -#define SVNREV_H - -#ifdef __cplusplus -extern "C" { -#endif - - int SvnRev(); - -#ifdef __cplusplus -} -#endif - +#ifndef SVNREV_H +#define SVNREV_H + +#ifdef __cplusplus +extern "C" { +#endif + + int SvnRev(); + +#ifdef __cplusplus +} +#endif + #endif /* SVNREV_H */ \ No newline at end of file