CfgUSBLoader/source/pngu/pngu_grrlib.c
2015-01-17 10:11:08 +00:00

303 lines
7.8 KiB
C

/********************************************************************************************
PNGU libwiigui & grrlib additions
********************************************************************************************/
#include <stdio.h>
#include <malloc.h>
#include "png.h"
#include "pngu.h"
#include "pngu_impl.h"
#define _SHIFTL(v, s, w) \
((PNGU_u32) (((PNGU_u32)(v) & ((0x01 << (w)) - 1)) << (s)))
#define _SHIFTR(v, s, w) \
((PNGU_u32)(((PNGU_u32)(v) >> (s)) & ((0x01 << (w)) - 1)))
// Coded by Tantric for WiiMC (http://www.wiimc.org)
static inline PNGU_u32 coordsRGBA8(PNGU_u32 x, PNGU_u32 y, PNGU_u32 w)
{
return ((((y >> 2) * (w >> 2) + (x >> 2)) << 5) + ((y & 3) << 2) + (x & 3)) << 1;
}
// Coded by Tantric for WiiMC (http://www.wiimc.org)
PNGU_u8 * PNGU_DecodeTo4x4RGBA8_EX (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, int * dstWidth, int * dstHeight, PNGU_u8 *dstPtr)
{
PNGU_u8 default_alpha = 255; // default alpha value, which is used if the source image doesn't have an alpha channel.
PNGU_u8 *dst;
int x, y, x2=0, y2=0, offset;
int xRatio = 0, yRatio = 0;
png_byte *pixel;
if (pngu_decode (ctx, width, height, 0) != PNGU_OK)
return NULL;
int newWidth = width;
int newHeight = height;
if(width > 1024 || height > 1024)
{
float ratio = (float)width/(float)height;
if(ratio > 1)
{
newWidth = 1024;
newHeight = 1024/ratio;
}
else
{
newWidth = 1024*ratio;
newHeight = 1024;
}
xRatio = (int)((width<<16)/newWidth)+1;
yRatio = (int)((height<<16)/newHeight)+1;
}
int padWidth = newWidth;
int padHeight = newHeight;
if(padWidth%4) padWidth += (4-padWidth%4);
if(padHeight%4) padHeight += (4-padHeight%4);
int len = (padWidth * padHeight) << 2;
if(len%32) len += (32-len%32);
if(dstPtr)
dst = dstPtr; // use existing allocation
else
dst = memalign (32, len);
if(!dst)
return NULL;
for (y = 0; y < padHeight; y++)
{
for (x = 0; x < padWidth; x++)
{
offset = coordsRGBA8(x, y, padWidth);
if(y >= newHeight || x >= newWidth)
{
dst[offset] = 0;
dst[offset+1] = 255;
dst[offset+32] = 255;
dst[offset+33] = 255;
}
else
{
if(xRatio > 0)
{
x2 = ((x*xRatio)>>16);
y2 = ((y*yRatio)>>16);
}
if (ctx->prop.imgColorType == PNGU_COLOR_TYPE_GRAY_ALPHA ||
ctx->prop.imgColorType == PNGU_COLOR_TYPE_RGB_ALPHA)
{
if(xRatio > 0)
pixel = &(ctx->row_pointers[y2][x2*4]);
else
pixel = &(ctx->row_pointers[y][x*4]);
dst[offset] = pixel[3]; // Alpha
dst[offset+1] = pixel[0]; // Red
dst[offset+32] = pixel[1]; // Green
dst[offset+33] = pixel[2]; // Blue
}
else
{
if(xRatio > 0)
pixel = &(ctx->row_pointers[y2][x2*3]);
else
pixel = &(ctx->row_pointers[y][x*3]);
dst[offset] = default_alpha; // Alpha
dst[offset+1] = pixel[0]; // Red
dst[offset+32] = pixel[1]; // Green
dst[offset+33] = pixel[2]; // Blue
}
}
}
}
// Free resources
free (ctx->img_data);
free (ctx->row_pointers);
*dstWidth = padWidth;
*dstHeight = padHeight;
return dst;
}
// Coded by Tantric for libwiigui (http://code.google.com/p/libwiigui)
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 >>2) + 1) <<2; // Add extra padding so each row starts in a 4 byte boundary
ctx->img_data = malloc(rowbytes * height);
memset(ctx->img_data, 0, rowbytes * height);
if (!ctx->img_data)
{
png_destroy_write_struct (&(ctx->png_ptr), (png_infopp)NULL);
if (ctx->source == PNGU_SOURCE_DEVICE)
fclose (ctx->fd);
return PNGU_LIB_ERROR;
}
ctx->row_pointers = malloc (sizeof (png_bytep) * height);
memset(ctx->row_pointers, 0, sizeof (png_bytep) * height);
if (!ctx->row_pointers)
{
png_destroy_write_struct (&(ctx->png_ptr), (png_infopp)NULL);
if (ctx->source == PNGU_SOURCE_DEVICE)
fclose (ctx->fd);
return PNGU_LIB_ERROR;
}
for (y = 0; y < height; ++y)
{
ctx->row_pointers[y] = buffer + (y * rowbytes);
}
// Tell libpng where is our image data
png_set_rows (ctx->png_ptr, ctx->info_ptr, ctx->row_pointers);
// Write file header and image data
png_write_png (ctx->png_ptr, ctx->info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
// Tell libpng we have no more data to write
png_write_end (ctx->png_ptr, (png_infop) NULL);
// Free resources
free (ctx->img_data);
free (ctx->row_pointers);
png_destroy_write_struct (&(ctx->png_ptr), &(ctx->info_ptr));
if (ctx->source == PNGU_SOURCE_DEVICE)
fclose (ctx->fd);
// Success
return ctx->cursor;
}
// Coded by Tantric for libwiigui (http://code.google.com/p/libwiigui)
int PNGU_EncodeFromGXTexture (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, void *buffer, PNGU_u32 stride)
{
int res;
PNGU_u32 x,y, tmpy1, tmpy2, tmpyWid, tmpxy;
unsigned char * ptr = (unsigned char*)buffer;
unsigned char * tmpbuffer = (unsigned char *)malloc(width*height*3);
memset(tmpbuffer, 0, width*height*3);
png_uint_32 offset;
for(y=0; y < height; y++)
{
tmpy1 = y * 640*3;
tmpy2 = y%4 << 2;
tmpyWid = (((y >> 2)<<4)*width);
for(x=0; x < width; x++)
{
offset = tmpyWid + ((x >> 2)<<6) + ((tmpy2+ x%4 ) << 1);
tmpxy = x * 3 + tmpy1;
tmpbuffer[tmpxy ] = ptr[offset+1]; // R
tmpbuffer[tmpxy+1] = ptr[offset+32]; // G
tmpbuffer[tmpxy+2] = ptr[offset+33]; // B
}
}
res = PNGU_EncodeFromRGB (ctx, width, height, tmpbuffer, stride);
free(tmpbuffer);
return res;
}
// Coded by Crayon for GRRLIB (http://code.google.com/p/grrlib)
int PNGU_EncodeFromEFB (IMGCTX ctx, PNGU_u32 width, PNGU_u32 height, PNGU_u32 stride)
{
int res;
PNGU_u32 x,y, tmpy, tmpxy, regval, val;
unsigned char * tmpbuffer = (unsigned char *)malloc(width*height*3);
memset(tmpbuffer, 0, width*height*3);
for(y=0; y < height; y++)
{
tmpy = y * 640*3;
for(x=0; x < width; x++)
{
regval = 0xc8000000|(_SHIFTL(x,2,10));
regval = (regval&~0x3FF000)|(_SHIFTL(y,12,10));
val = *(PNGU_u32*)regval;
tmpxy = x * 3 + tmpy;
tmpbuffer[tmpxy ] = _SHIFTR(val,16,8); // R
tmpbuffer[tmpxy+1] = _SHIFTR(val,8,8); // G
tmpbuffer[tmpxy+2] = val&0xff; // B
}
}
res = PNGU_EncodeFromRGB (ctx, width, height, tmpbuffer, stride);
free(tmpbuffer);
return res;
}