diff --git a/src/fileformats.c b/src/fileformats.c index 4aa5d867..1f8f7405 100644 --- a/src/fileformats.c +++ b/src/fileformats.c @@ -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;xPalette[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; iTransparent_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; yHeight; 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; yHeight; y++) + for (x=0; xWidth; 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; yHeight; 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; yHeight; y++) + for (x=0; xWidth; 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; yHeight; 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; yHeight; 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;xPalette[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; iTransparent_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; yHeight; 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; yHeight; y++) - for (x=0; xWidth; 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; yHeight; 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; yHeight; y++) - for (x=0; xWidth; 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; yHeight; 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; yHeight; 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;