Splitted Load_PNG to allow usage by Load_ICO

Introduces Load_PNG_Sub()
some .ico contain PNG images
This commit is contained in:
Thomas Bernard 2018-01-17 10:13:03 +01:00
parent c2486ed629
commit 37a5a0a85c

View File

@ -63,6 +63,10 @@
#include "pages.h"
#include "windows.h" // Best_color()
#ifndef __no_pnglib__
static void Load_PNG_Sub(T_IO_Context * context, FILE * file);
#endif
//////////////////////////////////// IMG ////////////////////////////////////
// -- Tester si un fichier est au format IMG --------------------------------
@ -4151,25 +4155,312 @@ int PNG_read_unknown_chunk(png_structp ptr, png_unknown_chunkp chunk)
}
png_bytep * Row_pointers;
// -- Lire un fichier au format PNG -----------------------------------------
static void Load_PNG_Sub(T_IO_Context * context, FILE * file)
{
png_structp png_ptr;
png_infop info_ptr;
// Prepare internal PNG loader
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr)
{
// Prepare internal PNG loader
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr)
{
png_byte color_type;
png_byte bit_depth;
png_voidp user_chunk_ptr;
// Setup a return point. If a pnglib loading error occurs
// in this if(), the else will be executed.
if (!setjmp(png_jmpbuf(png_ptr)))
{
png_init_io(png_ptr, file);
// Inform pnglib we already loaded the header.
png_set_sig_bytes(png_ptr, 8);
// Hook the handler for unknown chunks
user_chunk_ptr = png_get_user_chunk_ptr(png_ptr);
png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr, &PNG_read_unknown_chunk);
// This is a horrid way to pass parameters, but we don't get
// much choice. PNG loader can't be reintrant.
PNG_current_context=context;
// Load file information
png_read_info(png_ptr, info_ptr);
color_type = png_get_color_type(png_ptr,info_ptr);
bit_depth = png_get_bit_depth(png_ptr,info_ptr);
// If it's any supported file
// (Note: As of writing this, this test covers every possible
// image format of libpng)
if (color_type == PNG_COLOR_TYPE_PALETTE
|| color_type == PNG_COLOR_TYPE_GRAY
|| color_type == PNG_COLOR_TYPE_GRAY_ALPHA
|| color_type == PNG_COLOR_TYPE_RGB
|| color_type == PNG_COLOR_TYPE_RGB_ALPHA
)
{
int num_text;
png_text *text_ptr;
int unit_type;
png_uint_32 res_x;
png_uint_32 res_y;
// Comment (tEXt)
context->Comment[0]='\0'; // Clear the previous comment
if ((num_text=png_get_text(png_ptr, info_ptr, &text_ptr, NULL)))
{
while (num_text--)
{
if (!strcmp(text_ptr[num_text].key,"Title"))
{
int size;
size = Min(text_ptr[num_text].text_length, COMMENT_SIZE);
strncpy(context->Comment, text_ptr[num_text].text, size);
context->Comment[size]='\0';
break; // Skip all others tEXt chunks
}
}
}
// Pixel Ratio (pHYs)
if (png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type))
{
// Ignore unit, and use the X/Y ratio as a hint for
// WIDE or TALL pixels
if (res_x>0 && res_y>0)
{
if (res_y/res_x>1)
{
context->Ratio=PIXEL_WIDE;
}
else if (res_x/res_y>1)
{
context->Ratio=PIXEL_TALL;
}
}
}
if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA)
Pre_load(context,png_get_image_width(png_ptr,info_ptr),png_get_image_height(png_ptr,info_ptr),File_length_file(file),FORMAT_PNG,PIXEL_SIMPLE,1);
else
Pre_load(context,png_get_image_width(png_ptr,info_ptr),png_get_image_height(png_ptr,info_ptr),File_length_file(file),FORMAT_PNG,context->Ratio,0);
if (File_error==0)
{
int x,y;
png_colorp palette;
int num_palette;
png_bytep * Row_pointers = NULL;
byte row_pointers_allocated = 0;
int num_trans;
png_bytep trans;
png_color_16p trans_values;
// 16-bit images
if (bit_depth == 16)
{
// Reduce to 8-bit
png_set_strip_16(png_ptr);
}
else if (bit_depth < 8)
{
// Inform libpng we want one byte per pixel,
// even though the file was less than 8bpp
png_set_packing(png_ptr);
}
// Images with alpha channel
if (color_type & PNG_COLOR_MASK_ALPHA)
{
// Tell libpng to ignore it
png_set_strip_alpha(png_ptr);
}
// Greyscale images :
if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
{
// Map low bpp greyscales to full 8bit (0-255 range)
if (bit_depth < 8)
{
#if (PNG_LIBPNG_VER_MAJOR <= 1) && (PNG_LIBPNG_VER_MINOR < 4)
// Works well with png 1.2.8, but deprecated in 1.4 ...
png_set_gray_1_2_4_to_8(png_ptr);
#else
// ...where this seems to replace it:
png_set_expand_gray_1_2_4_to_8(png_ptr);
#endif
}
// Create greyscale palette
for (x=0;x<256;x++)
{
context->Palette[x].R=x;
context->Palette[x].G=x;
context->Palette[x].B=x;
}
}
else if (color_type == PNG_COLOR_TYPE_PALETTE) // Palette images
{
if (bit_depth < 8)
{
// Clear unused colors
if (Config.Clear_palette)
memset(context->Palette,0,sizeof(T_Palette));
}
// Get a pointer to the PNG palette
png_get_PLTE(png_ptr, info_ptr, &palette,
&num_palette);
// Copy all colors to the context
for (x=0;x<num_palette;x++)
{
context->Palette[x].R=palette[x].red;
context->Palette[x].G=palette[x].green;
context->Palette[x].B=palette[x].blue;
}
// The palette must not be freed: it is owned by libpng.
palette = NULL;
}
// Transparency (tRNS)
if (png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values))
{
if (color_type == PNG_COLOR_TYPE_PALETTE && trans!=NULL)
{
int i;
for (i=0; i<num_trans; i++)
{
if (trans[i]==0)
{
context->Transparent_color = i;
context->Background_transparent = 1;
break;
}
}
}
else if ((color_type == PNG_COLOR_TYPE_GRAY
|| color_type == PNG_COLOR_TYPE_RGB) && trans_values!=NULL)
{
// In this case, num_trans is supposed to be "1",
// and trans_values[0] contains the reference color
// (RGB triplet) that counts as transparent.
// Ideally, we should reserve this color in the palette,
// (so it's not merged and averaged with a neighbor one)
// and after creating the optimized palette, find its
// index and mark it transparent.
// Current implementation: ignore.
}
}
context->Width=png_get_image_width(png_ptr,info_ptr);
context->Height=png_get_image_height(png_ptr,info_ptr);
png_set_interlace_handling(png_ptr);
png_read_update_info(png_ptr, info_ptr);
// Allocate row pointers
Row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * context->Height);
row_pointers_allocated = 0;
/* read file */
if (!setjmp(png_jmpbuf(png_ptr)))
{
if (color_type == PNG_COLOR_TYPE_GRAY
|| color_type == PNG_COLOR_TYPE_GRAY_ALPHA
|| color_type == PNG_COLOR_TYPE_PALETTE
)
{
// 8bpp
for (y=0; y<context->Height; y++)
Row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(png_ptr,info_ptr));
row_pointers_allocated = 1;
png_read_image(png_ptr, Row_pointers);
for (y=0; y<context->Height; y++)
for (x=0; x<context->Width; x++)
Set_pixel(context, x, y, Row_pointers[y][x]);
}
else
{
switch (context->Type)
{
case CONTEXT_PREVIEW:
// 24bpp
// It's a preview
// Unfortunately we need to allocate loads of memory
for (y=0; y<context->Height; y++)
Row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(png_ptr,info_ptr));
row_pointers_allocated = 1;
png_read_image(png_ptr, Row_pointers);
for (y=0; y<context->Height; y++)
for (x=0; x<context->Width; x++)
Set_pixel_24b(context, x, y, Row_pointers[y][x*3],Row_pointers[y][x*3+1],Row_pointers[y][x*3+2]);
break;
case CONTEXT_MAIN_IMAGE:
case CONTEXT_BRUSH:
case CONTEXT_SURFACE:
// It's loading an actual image
// We'll save memory and time by writing directly into
// our pre-allocated 24bit buffer
for (y=0; y<context->Height; y++)
Row_pointers[y] = (png_byte*) (&(context->Buffer_image_24b[y * context->Width]));
png_read_image(png_ptr, Row_pointers);
break;
case CONTEXT_PALETTE:
// No pixels to draw in a palette!
break;
}
}
}
else
File_error=2;
/* cleanup heap allocation */
if (row_pointers_allocated)
{
for (y=0; y<context->Height; y++) {
free(Row_pointers[y]);
Row_pointers[y] = NULL;
}
}
free(Row_pointers);
Row_pointers = NULL;
}
else
File_error=2;
}
else
// Unsupported image type
File_error=1;
}
else
File_error=1;
}
else
File_error=1;
}
}
void Load_PNG(T_IO_Context * context)
{
FILE *file; // Fichier du fichier
char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier
byte png_header[8];
byte row_pointers_allocated;
png_bytep trans;
int num_trans;
png_color_16p trans_values;
png_structp png_ptr;
png_infop info_ptr;
byte png_header[8];
Get_full_filename(filename, context->File_name, context->File_directory);
File_error=0;
if ((file=fopen(filename, "rb")))
{
// Load header (8 first bytes)
@ -4177,292 +4468,9 @@ void Load_PNG(T_IO_Context * context)
{
// Do we recognize a png file signature ?
if ( !png_sig_cmp(png_header, 0, 8))
{
// Prepare internal PNG loader
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr)
{
// Prepare internal PNG loader
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr)
{
png_byte color_type;
png_byte bit_depth;
png_voidp user_chunk_ptr;
// Setup a return point. If a pnglib loading error occurs
// in this if(), the else will be executed.
if (!setjmp(png_jmpbuf(png_ptr)))
{
png_init_io(png_ptr, file);
// Inform pnglib we already loaded the header.
png_set_sig_bytes(png_ptr, 8);
// Hook the handler for unknown chunks
user_chunk_ptr = png_get_user_chunk_ptr(png_ptr);
png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr, &PNG_read_unknown_chunk);
// This is a horrid way to pass parameters, but we don't get
// much choice. PNG loader can't be reintrant.
PNG_current_context=context;
// Load file information
png_read_info(png_ptr, info_ptr);
color_type = png_get_color_type(png_ptr,info_ptr);
bit_depth = png_get_bit_depth(png_ptr,info_ptr);
// If it's any supported file
// (Note: As of writing this, this test covers every possible
// image format of libpng)
if (color_type == PNG_COLOR_TYPE_PALETTE
|| color_type == PNG_COLOR_TYPE_GRAY
|| color_type == PNG_COLOR_TYPE_GRAY_ALPHA
|| color_type == PNG_COLOR_TYPE_RGB
|| color_type == PNG_COLOR_TYPE_RGB_ALPHA
)
{
int num_text;
png_text *text_ptr;
int unit_type;
png_uint_32 res_x;
png_uint_32 res_y;
// Comment (tEXt)
context->Comment[0]='\0'; // Clear the previous comment
if ((num_text=png_get_text(png_ptr, info_ptr, &text_ptr, NULL)))
{
while (num_text--)
{
if (!strcmp(text_ptr[num_text].key,"Title"))
{
int size;
size = Min(text_ptr[num_text].text_length, COMMENT_SIZE);
strncpy(context->Comment, text_ptr[num_text].text, size);
context->Comment[size]='\0';
break; // Skip all others tEXt chunks
}
}
}
// Pixel Ratio (pHYs)
if (png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type))
{
// Ignore unit, and use the X/Y ratio as a hint for
// WIDE or TALL pixels
if (res_x>0 && res_y>0)
{
if (res_y/res_x>1)
{
context->Ratio=PIXEL_WIDE;
}
else if (res_x/res_y>1)
{
context->Ratio=PIXEL_TALL;
}
}
}
if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA)
Pre_load(context,png_get_image_width(png_ptr,info_ptr),png_get_image_height(png_ptr,info_ptr),File_length_file(file),FORMAT_PNG,PIXEL_SIMPLE,1);
else
Pre_load(context,png_get_image_width(png_ptr,info_ptr),png_get_image_height(png_ptr,info_ptr),File_length_file(file),FORMAT_PNG,context->Ratio,0);
if (File_error==0)
{
int x,y;
png_colorp palette;
int num_palette;
// 16-bit images
if (bit_depth == 16)
{
// Reduce to 8-bit
png_set_strip_16(png_ptr);
}
else if (bit_depth < 8)
{
// Inform libpng we want one byte per pixel,
// even though the file was less than 8bpp
png_set_packing(png_ptr);
}
// Images with alpha channel
if (color_type & PNG_COLOR_MASK_ALPHA)
{
// Tell libpng to ignore it
png_set_strip_alpha(png_ptr);
}
// Greyscale images :
if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
{
// Map low bpp greyscales to full 8bit (0-255 range)
if (bit_depth < 8)
{
#if (PNG_LIBPNG_VER_MAJOR <= 1) && (PNG_LIBPNG_VER_MINOR < 4)
// Works well with png 1.2.8, but deprecated in 1.4 ...
png_set_gray_1_2_4_to_8(png_ptr);
#else
// ...where this seems to replace it:
png_set_expand_gray_1_2_4_to_8(png_ptr);
#endif
}
// Create greyscale palette
for (x=0;x<256;x++)
{
context->Palette[x].R=x;
context->Palette[x].G=x;
context->Palette[x].B=x;
}
}
else if (color_type == PNG_COLOR_TYPE_PALETTE) // Palette images
{
if (bit_depth < 8)
{
// Clear unused colors
if (Config.Clear_palette)
memset(context->Palette,0,sizeof(T_Palette));
}
// Get a pointer to the PNG palette
png_get_PLTE(png_ptr, info_ptr, &palette,
&num_palette);
// Copy all colors to the context
for (x=0;x<num_palette;x++)
{
context->Palette[x].R=palette[x].red;
context->Palette[x].G=palette[x].green;
context->Palette[x].B=palette[x].blue;
}
// The palette must not be freed: it is owned by libpng.
palette = NULL;
}
// Transparency (tRNS)
if (png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values))
{
if (color_type == PNG_COLOR_TYPE_PALETTE && trans!=NULL)
{
int i;
for (i=0; i<num_trans; i++)
{
if (trans[i]==0)
{
context->Transparent_color = i;
context->Background_transparent = 1;
break;
}
}
}
else if ((color_type == PNG_COLOR_TYPE_GRAY
|| color_type == PNG_COLOR_TYPE_RGB) && trans_values!=NULL)
{
// In this case, num_trans is supposed to be "1",
// and trans_values[0] contains the reference color
// (RGB triplet) that counts as transparent.
// Ideally, we should reserve this color in the palette,
// (so it's not merged and averaged with a neighbor one)
// and after creating the optimized palette, find its
// index and mark it transparent.
// Current implementation: ignore.
}
}
context->Width=png_get_image_width(png_ptr,info_ptr);
context->Height=png_get_image_height(png_ptr,info_ptr);
png_set_interlace_handling(png_ptr);
png_read_update_info(png_ptr, info_ptr);
// Allocate row pointers
Row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * context->Height);
row_pointers_allocated = 0;
/* read file */
if (!setjmp(png_jmpbuf(png_ptr)))
{
if (color_type == PNG_COLOR_TYPE_GRAY
|| color_type == PNG_COLOR_TYPE_GRAY_ALPHA
|| color_type == PNG_COLOR_TYPE_PALETTE
)
{
// 8bpp
for (y=0; y<context->Height; y++)
Row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(png_ptr,info_ptr));
row_pointers_allocated = 1;
png_read_image(png_ptr, Row_pointers);
for (y=0; y<context->Height; y++)
for (x=0; x<context->Width; x++)
Set_pixel(context, x, y, Row_pointers[y][x]);
}
else
{
switch (context->Type)
{
case CONTEXT_PREVIEW:
// 24bpp
// It's a preview
// Unfortunately we need to allocate loads of memory
for (y=0; y<context->Height; y++)
Row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(png_ptr,info_ptr));
row_pointers_allocated = 1;
png_read_image(png_ptr, Row_pointers);
for (y=0; y<context->Height; y++)
for (x=0; x<context->Width; x++)
Set_pixel_24b(context, x, y, Row_pointers[y][x*3],Row_pointers[y][x*3+1],Row_pointers[y][x*3+2]);
break;
case CONTEXT_MAIN_IMAGE:
case CONTEXT_BRUSH:
case CONTEXT_SURFACE:
// It's loading an actual image
// We'll save memory and time by writing directly into
// our pre-allocated 24bit buffer
for (y=0; y<context->Height; y++)
Row_pointers[y] = (png_byte*) (&(context->Buffer_image_24b[y * context->Width]));
png_read_image(png_ptr, Row_pointers);
break;
case CONTEXT_PALETTE:
// No pixels to draw in a palette!
break;
}
}
}
else
File_error=2;
/* cleanup heap allocation */
if (row_pointers_allocated)
{
for (y=0; y<context->Height; y++) {
free(Row_pointers[y]);
Row_pointers[y] = NULL;
}
}
free(Row_pointers);
Row_pointers = NULL;
}
else
File_error=2;
}
else
// Unsupported image type
File_error=1;
}
else
File_error=1;
}
else
File_error=1;
}
}
/*Close_lecture();*/
Load_PNG_Sub(context, file);
else
File_error=2;
}
else // Lecture header impossible: Error ne modifiant pas l'image
File_error=1;
@ -4483,6 +4491,7 @@ void Save_PNG(T_IO_Context * context)
png_infop info_ptr;
png_unknown_chunk crng_chunk;
byte cycle_data[16*6]; // Storage for color-cycling data, referenced by crng_chunk
static png_bytep * Row_pointers;
Get_full_filename(filename, context->File_name, context->File_directory);
File_error=0;