319 lines
9.5 KiB
C
319 lines
9.5 KiB
C
#include "conversion.h"
|
|
#include "draw.h"
|
|
|
|
#include <png.h>
|
|
|
|
// don't be fooled - this function always expects 64x64 input buffers. Width/height only
|
|
// control output resolution
|
|
int rgb565ToPngFile(char *filename, u16 *rgb_buf, u8 *alpha_buf, int width, int height)
|
|
{
|
|
FILE *fp = fopen(filename, "wb");
|
|
u8 *image = malloc(64 * 64 * 4);
|
|
if (!image) return -1;
|
|
|
|
int i, x, y, r, g, b, a;
|
|
|
|
for (i = 0; i < 64 * 64; ++i)
|
|
{
|
|
r = (rgb_buf[i] & 0xF800) >> 11;
|
|
g = (rgb_buf[i] & 0x07E0) >> 5;
|
|
b = (rgb_buf[i] & 0x001F);
|
|
a = (alpha_buf[i/2] >> (4*(i%2))) & 0x0F;
|
|
|
|
r = round(r * 255.0 / 31.0);
|
|
g = round(g * 255.0 / 63.0);
|
|
b = round(b * 255.0 / 31.0);
|
|
a = a * 0x11;
|
|
x = 8*((i/64)%8) + (((i%64)&0x01) >> 0) + (((i%64)&0x04) >> 1) + (((i%64)&0x10) >> 2);
|
|
y = 8*(i/512) + (((i%64)&0x02) >> 1) + (((i%64)&0x08) >> 2) + (((i%64)&0x20) >> 3);
|
|
image[y * 64 * 4 + x * 4 + 0] = r;
|
|
image[y * 64 * 4 + x * 4 + 1] = g;
|
|
image[y * 64 * 4 + x * 4 + 2] = b;
|
|
image[y * 64 * 4 + x * 4 + 3] = a;
|
|
}
|
|
|
|
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
png_infop info = png_create_info_struct(png);
|
|
setjmp(png_jmpbuf(png));
|
|
png_init_io(png, fp);
|
|
png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
|
png_write_info(png, info);
|
|
png_bytep row = malloc(4 * width * sizeof(png_byte));
|
|
for (y = 0; y < height; ++y)
|
|
{
|
|
for (x = 0; x < width; ++x)
|
|
{
|
|
row[x * 4 + 0] = image[(y * 64 + x) * 4 + 0];
|
|
row[x * 4 + 1] = image[(y * 64 + x) * 4 + 1];
|
|
row[x * 4 + 2] = image[(y * 64 + x) * 4 + 2];
|
|
row[x * 4 + 3] = image[(y * 64 + x) * 4 + 3];
|
|
}
|
|
|
|
png_write_row(png, row);
|
|
}
|
|
|
|
png_write_end(png, info);
|
|
png_free_data(png, info, PNG_FREE_ALL, -1);
|
|
png_destroy_write_struct(&png, NULL);
|
|
free(row);
|
|
free(image);
|
|
fflush(fp);
|
|
fclose(fp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pngToRGB565(char *png_buf, u64 fileSize, u16 *rgb_buf_64x64, u8 *alpha_buf_64x64, u16 *rgb_buf_32x32, u8 *alpha_buf_32x32, bool set_icon)
|
|
{
|
|
if (png_buf == NULL) return 0;
|
|
if (fileSize < 8 || png_sig_cmp((png_bytep) png_buf, 0, 8))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
u32 *buf = (u32 *) png_buf;
|
|
FILE *fp = fmemopen(buf, fileSize, "rb");
|
|
png_bytep *row_pointers = NULL;
|
|
|
|
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
png_infop info = png_create_info_struct(png);
|
|
|
|
png_init_io(png, fp);
|
|
png_read_info(png, info);
|
|
|
|
u32 width = png_get_image_width(png, info);
|
|
u32 height = png_get_image_height(png, info);
|
|
png_byte color_type = png_get_color_type(png, info);
|
|
png_byte bit_depth = png_get_bit_depth(png, info);
|
|
|
|
if (set_icon && (width != 48 || height != 48))
|
|
{
|
|
DEBUG("Invalid set icon?\n");
|
|
return 0;
|
|
}
|
|
if (!set_icon && (width < 64 || height < 64 || width % 64 != 0 || height % 64 != 0 || width > 12 * 64 || height > 6 * 64))
|
|
{
|
|
DEBUG("Invalid png found...\n");
|
|
return 0;
|
|
}
|
|
|
|
if (bit_depth == 16)
|
|
png_set_strip_16(png);
|
|
|
|
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
|
png_set_palette_to_rgb(png);
|
|
|
|
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
|
png_set_expand_gray_1_2_4_to_8(png);
|
|
|
|
if (png_get_valid(png, info, PNG_INFO_tRNS))
|
|
png_set_tRNS_to_alpha(png);
|
|
|
|
if (color_type == PNG_COLOR_TYPE_RGB ||
|
|
color_type == PNG_COLOR_TYPE_GRAY ||
|
|
color_type == PNG_COLOR_TYPE_PALETTE)
|
|
png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
|
|
|
|
if (color_type == PNG_COLOR_TYPE_GRAY ||
|
|
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
|
png_set_gray_to_rgb(png);
|
|
|
|
png_read_update_info(png, info);
|
|
|
|
u32 x, y, r, g, b, a;
|
|
|
|
memset(alpha_buf_64x64, 0, 12*6*64*64/2);
|
|
memset(alpha_buf_32x32, 0, 12*6*32*32/2);
|
|
|
|
row_pointers = malloc(sizeof(png_bytep) * height);
|
|
for (y = 0; y < height; y++)
|
|
{
|
|
row_pointers[y] = (png_byte *) malloc(png_get_rowbytes(png, info));
|
|
}
|
|
|
|
png_read_image(png, row_pointers);
|
|
|
|
png_destroy_read_struct(&png, &info, NULL);
|
|
|
|
if (fp) fclose(fp);
|
|
for (y = 0; y < height; ++y)
|
|
{
|
|
png_bytep row = row_pointers[y];
|
|
for (x = 0; x < width; ++x)
|
|
{
|
|
png_bytep px = &(row[x * 4]);
|
|
r = px[0] >> 3;
|
|
g = px[1] >> 2;
|
|
b = px[2] >> 3;
|
|
a = px[3] >> 4;
|
|
|
|
// rgb565 conversion code adapted from GYTB
|
|
int rgb565_index = 8*64*((y/8)%8) | 64*((x/8)%8) | 32*((y/4)%2) | 16*((x/4)%2) | 8*((y/2)%2) | 4*((x/2)%2) | 2*(y%2) | (x%2);
|
|
rgb565_index |= 64*64*(height/64)*(x/64) + 64*64*(y/64); // account for multiple badges from 1 image
|
|
rgb_buf_64x64[rgb565_index] = (r << 11) | (g << 5) | b;
|
|
alpha_buf_64x64[rgb565_index / 2] |= a << (4 * (x % 2));
|
|
}
|
|
}
|
|
|
|
for (y = 0; y < height; y += 2)
|
|
{
|
|
png_bytep row = row_pointers[y];
|
|
png_bytep nextrow = row_pointers[y+1];
|
|
for (x = 0; x < width; x += 2)
|
|
{
|
|
png_bytep px1 = &(row[x * 4]);
|
|
png_bytep px2 = &(nextrow[x * 4]);
|
|
png_bytep px3 = &(row[(x + 1) * 4]);
|
|
png_bytep px4 = &(nextrow[(x + 1) * 4]);
|
|
r = (px1[0] + px2[0] + px3[0] + px4[0]) >> 5;
|
|
g = (px1[1] + px2[1] + px3[1] + px4[1]) >> 4;
|
|
b = (px1[2] + px2[2] + px3[2] + px4[2]) >> 5;
|
|
a = (px1[3] + px2[3] + px3[3] + px4[3]) >> 6;
|
|
int x2 = x/2;
|
|
int y2 = y/2;
|
|
|
|
int rgb565_index = 4*64*((y2/8)%4) | 64*((x2/8)%4) | 32*((y2/4)%2) | 16*((x2/4)%2) | 8*((y2/2)%2) | 4*((x2/2)%2) | 2*(y2%2) | (x2%2);
|
|
rgb565_index |= 32*32*(height/64)*(x/64) + 32*32*(y/64);
|
|
|
|
rgb_buf_32x32[rgb565_index] = (r << 11) | (g << 5) | b;
|
|
alpha_buf_32x32[rgb565_index / 2] |= a << (4 * (x2%2));
|
|
}
|
|
}
|
|
|
|
for (y = 0; y < height; y++)
|
|
{
|
|
free(row_pointers[y]);
|
|
}
|
|
|
|
free(row_pointers);
|
|
if (!set_icon)
|
|
return (height/64)*(width/64);
|
|
else
|
|
return (height/48)*(width/48);
|
|
}
|
|
|
|
static void rotate_agbr_counterclockwise(char ** bufp, size_t size, size_t width)
|
|
{
|
|
uint32_t * buf = (uint32_t*)*bufp;
|
|
uint32_t * out = malloc(size);
|
|
|
|
size_t pixel_count = (size/4);
|
|
size_t height = pixel_count/width;
|
|
|
|
for (uint32_t h = 0; h < height; ++h)
|
|
{
|
|
for (uint32_t w = 0; w < width; ++w)
|
|
{
|
|
size_t buf_index = (w + (h * width));
|
|
size_t out_index = (height * (width-1)) - (height * w) + h;
|
|
|
|
out[out_index] = buf[buf_index];
|
|
}
|
|
}
|
|
|
|
free(*bufp);
|
|
*bufp = (char*)out;
|
|
}
|
|
|
|
size_t bin_to_abgr(char ** bufp, size_t size)
|
|
{
|
|
size_t out_size = (size / 3) * 4;
|
|
char* buf = malloc(out_size);
|
|
|
|
for (size_t i = 0, j = 0; i < size; i+=3, j+=4)
|
|
{
|
|
(buf+j)[0] = 0xFF; // A
|
|
(buf+j)[1] = (*bufp+i)[0]; // B
|
|
(buf+j)[2] = (*bufp+i)[1]; // G
|
|
(buf+j)[3] = (*bufp+i)[2]; // R
|
|
}
|
|
free(*bufp);
|
|
*bufp = buf;
|
|
|
|
// splash screens contain the raw framebuffer to put on the screen
|
|
// because the screens are mounted at a 90 degree angle we also need to rotate
|
|
// the output
|
|
rotate_agbr_counterclockwise(bufp, out_size, 240);
|
|
|
|
return out_size;
|
|
}
|
|
|
|
size_t png_to_abgr(char ** bufp, size_t size, u32 *height)
|
|
{
|
|
size_t out_size = 0;
|
|
if(size < 8 || png_sig_cmp((png_bytep)*bufp, 0, 8))
|
|
{
|
|
throw_error("Invalid preview.png", ERROR_LEVEL_WARNING);
|
|
return out_size;
|
|
}
|
|
|
|
uint32_t * buf = (uint32_t*)*bufp;
|
|
|
|
FILE * fp = fmemopen(buf, size, "rb");;
|
|
png_bytep * row_pointers = NULL;
|
|
|
|
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
png_infop info = png_create_info_struct(png);
|
|
|
|
png_init_io(png, fp);
|
|
png_read_info(png, info);
|
|
|
|
u32 width = png_get_image_width(png, info);
|
|
*height = png_get_image_height(png, info);
|
|
|
|
png_byte color_type = png_get_color_type(png, info);
|
|
png_byte bit_depth = png_get_bit_depth(png, info);
|
|
|
|
// Read any color_type into 8bit depth, ABGR format.
|
|
// See http://www.libpng.org/pub/png/libpng-manual.txt
|
|
|
|
if(bit_depth == 16)
|
|
png_set_strip_16(png);
|
|
|
|
if(color_type == PNG_COLOR_TYPE_PALETTE)
|
|
png_set_palette_to_rgb(png);
|
|
|
|
// PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth.
|
|
if(color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
|
png_set_expand_gray_1_2_4_to_8(png);
|
|
|
|
if(png_get_valid(png, info, PNG_INFO_tRNS))
|
|
png_set_tRNS_to_alpha(png);
|
|
|
|
// These color_type don't have an alpha channel then fill it with 0xff.
|
|
if(color_type == PNG_COLOR_TYPE_RGB ||
|
|
color_type == PNG_COLOR_TYPE_GRAY ||
|
|
color_type == PNG_COLOR_TYPE_PALETTE)
|
|
png_set_add_alpha(png, 0xFF, PNG_FILLER_BEFORE);
|
|
|
|
if(color_type == PNG_COLOR_TYPE_GRAY ||
|
|
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
|
png_set_gray_to_rgb(png);
|
|
|
|
//output ABGR
|
|
png_set_bgr(png);
|
|
png_set_swap_alpha(png);
|
|
|
|
png_read_update_info(png, info);
|
|
|
|
row_pointers = malloc(sizeof(png_bytep) * *height);
|
|
out_size = sizeof(u32) * (width * *height);
|
|
u32 * out = malloc(out_size);
|
|
for(u32 y = 0; y < *height; y++)
|
|
{
|
|
row_pointers[y] = (png_bytep)(out + (width * y));
|
|
}
|
|
|
|
png_read_image(png, row_pointers);
|
|
|
|
png_destroy_read_struct(&png, &info, NULL);
|
|
|
|
if (fp) fclose(fp);
|
|
if (row_pointers) free(row_pointers);
|
|
|
|
free(*bufp);
|
|
*bufp = (char*)out;
|
|
|
|
return out_size;
|
|
}
|