From 7875d534cb7967a737afdee090dd69e68661d4bf Mon Sep 17 00:00:00 2001 From: John Bowler Date: Mon, 7 Nov 2011 22:33:49 -0600 Subject: [PATCH] [libpng15] Simplified read/write API initial version; basic read/write tested on a variety of images, limited documentation (in the header file.) --- ANNOUNCE | 2 + CHANGES | 2 + png.c | 418 +++++++++++++++++++- png.h | 283 +++++++++++++- pngerror.c | 80 ++++ pngpriv.h | 78 ++++ pngread.c | 705 ++++++++++++++++++++++++++++++++++ pngrtran.c | 6 +- pngrutil.c | 47 ++- pngwrite.c | 550 ++++++++++++++++++++++++++ scripts/pnglibconf.dfa | 30 +- scripts/pnglibconf.h.prebuilt | 10 +- scripts/symbols.def | 7 + 13 files changed, 2186 insertions(+), 32 deletions(-) diff --git a/ANNOUNCE b/ANNOUNCE index d448ae1a1..064f01f6f 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -58,6 +58,8 @@ Version 1.5.7beta02 [November 8, 2011] Updated scripts/pnglibconf.mak and scripts/makefile.std to handle the new PNG_JOIN macro. Added versioning to pnglibconf.h comments. + Simplified read/write API initial version; basic read/write tested on + a variety of images, limited documentation (in the header file.) Send comments/corrections/commendations to png-mng-implement at lists.sf.net: (subscription required; visit diff --git a/CHANGES b/CHANGES index 392092d2a..aecd8643b 100644 --- a/CHANGES +++ b/CHANGES @@ -3703,6 +3703,8 @@ Version 1.5.7beta02 [November 8, 2011] Updated scripts/pnglibconf.mak and scripts/makefile.std to handle the new PNG_JOIN macro. Added versioning to pnglibconf.h comments. + Simplified read/write API initial version; basic read/write tested on + a variety of images, limited documentation (in the header file.) Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/png.c b/png.c index e8df2cfbc..80d12ea77 100644 --- a/png.c +++ b/png.c @@ -645,13 +645,13 @@ png_get_copyright(png_const_structp png_ptr) #else # ifdef __STDC__ return PNG_STRING_NEWLINE \ - "libpng version 1.5.7beta02 - November 4, 2011" PNG_STRING_NEWLINE \ + "libpng version 1.5.7beta02 - November 8, 2011" PNG_STRING_NEWLINE \ "Copyright (c) 1998-2011 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \ "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \ "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \ PNG_STRING_NEWLINE; # else - return "libpng version 1.5.7beta02 - November 4, 2011\ + return "libpng version 1.5.7beta02 - November 8, 2011\ Copyright (c) 1998-2011 Glenn Randers-Pehrson\ Copyright (c) 1996-1997 Andreas Dilger\ Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc."; @@ -2854,4 +2854,418 @@ png_build_gamma_table(png_structp png_ptr, int bit_depth) } } #endif /* READ_GAMMA */ + +/* sRGB support */ +#if defined PNG_SIMPLIFIED_READ_SUPPORTED ||\ + defined PNG_SIMPLIFIED_WRITE_SUPPORTED +/* sRGB conversion tables; these are machine generated with the following code. + */ +#ifdef PNG_INCLUDE_SELF_GENERATING_AND_SELF_DOCUMENTING_CODE +#define _C99_SOURCE 1 +#include +#include +#include + +static unsigned int max_input = 255*65535; + +double sRGB(unsigned int i) +{ + double l = i; + l /= max_input; + + if (l <= 0.0031308) + l *= 12.92; + + else + l = 1.055 * pow(l, 1/2.4) - 0.055; + + return l; +} + +unsigned int invsRGB(unsigned int i) +{ + double l = i/255.; + + if (l <= 0.04045) + l /= 12.92; + + else + l = pow((l+0.055)/1.055, 2.4); + + l *= 65535; + return nearbyint(l); +} + +int main(void) +{ + unsigned int i, i16; + unsigned short base[512]; + unsigned char delta[512]; + double max_error = 0; + double max_error16 = 0; + unsigned int error_count = 0; + unsigned int error_count16 = 0; + + for (i=0; i<=511; ++i) + { + double lo = 255 * sRGB(i << 15); + double hi = 255 * sRGB((i+1) << 15); + unsigned int calc; + + calc = nearbyint((lo+.5) * 256); + if (calc > 65535) + { + fprintf(stderr, "table[%d][0]: overflow %08x (%d)\n", i, calc, calc); + exit(1); + } + base[i] = calc; + + calc = nearbyint((hi-lo) * 32); + if (calc > 255) + { + fprintf(stderr, "table[%d][1]: overflow %08x (%d)\n", i, calc, calc); + exit(1); + } + delta[i] = calc; + } + + for (i=0; i <= max_input; ++i) + { + unsigned int iexact = nearbyint(255*sRGB(i)); + unsigned int icalc = base[i>>15] + (((i&0x7fff)*delta[i>>15])>>12); + icalc >>= 8; + + if (icalc != iexact) + { + double err = fabs(255*sRGB(i) - icalc); + + ++error_count; + if (err > .646) + { + printf( + "/* 0x%08x: exact: %3d, got: %3d [tables: %08x, %08x] (%f) */\n", + i, iexact, icalc, base[i>>15], delta[i>>15], err); + if (err > max_error) + max_error = err; + } + } + } + + for (i16=0; i16 <= 65535; ++i16) + { + unsigned int i = 255*i16; + unsigned int iexact = nearbyint(255*sRGB(i)); + unsigned int icalc = base[i>>15] + (((i&0x7fff)*delta[i>>15])>>12); + icalc >>= 8; + + if (icalc != iexact) + { + double err = fabs(255*sRGB(i) - icalc); + + ++error_count16; + if (err > max_error16) + max_error16 = err; + + if (abs(icalc - iexact) > 1) + printf( + "/* 0x%04x: exact: %3d, got: %3d [tables: %08x, %08x] (%f) */\n", + i16, iexact, icalc, base[i>>15], delta[i>>15], err); + } + } + + printf("/* maximum error: %g, %g%% of readings */\n", max_error, + (100.*error_count)/max_input); + printf("/* maximum 16-bit error: %g, %g%% of readings */\n", max_error16, + (100.*error_count16)/65535); + + printf("PNG_CONST png_uint_16 png_sRGB_table[256] =\n{\n "); + for (i=0; i<255; ) + { + do + { + printf("%d,", invsRGB(i++)); + } + while ((i & 0x7) != 0 && i<255); + if (i<255) printf("\n "); + } + printf("%d\n};\n\n", invsRGB(i)); + + + printf("PNG_CONST png_uint_16 png_sRGB_base[512] =\n{\n "); + for (i=0; i<511; ) + { + do + { + printf("%d,", base[i++]); + } + while ((i & 0x7) != 0 && i<511); + if (i<511) printf("\n "); + } + printf("%d\n};\n\n", base[i]); + + printf("PNG_CONST png_byte png_sRGB_delta[512] =\n{\n "); + for (i=0; i<511; ) + { + do + { + printf("%d,", delta[i++]); + } + while ((i & 0xf) != 0 && i<511); + if (i<511) printf("\n "); + } + printf("%d\n};\n\n", delta[i]); + + return 0; +} +#endif /* self documenting code */ + +/* The result is a set of tables with the following errors: */ +/* 0x000148c1: exact: 16, got: 15 [tables: 00000d36, 0000009d] (0.646071) */ +/* 0x000148c2: exact: 16, got: 15 [tables: 00000d36, 0000009d] (0.646218) */ +/* 0x000148c3: exact: 16, got: 15 [tables: 00000d36, 0000009d] (0.646365) */ +/* maximum error: 0.646365, 0.494416% of readings */ +/* maximum 16-bit error: 0.644455, 0.5066% of readings */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* The convert-to-sRGB table is only currently required for read. */ +PNG_CONST png_uint_16 png_sRGB_table[256] = +{ + 0,20,40,60,80,99,119,139, + 159,179,199,219,241,264,288,313, + 340,367,396,427,458,491,526,562, + 599,637,677,718,761,805,851,898, + 947,997,1048,1101,1156,1212,1270,1330, + 1391,1453,1517,1583,1651,1720,1790,1863, + 1937,2013,2090,2170,2250,2333,2418,2504, + 2592,2681,2773,2866,2961,3058,3157,3258, + 3360,3464,3570,3678,3788,3900,4014,4129, + 4247,4366,4488,4611,4736,4864,4993,5124, + 5257,5392,5530,5669,5810,5953,6099,6246, + 6395,6547,6700,6856,7014,7174,7335,7500, + 7666,7834,8004,8177,8352,8528,8708,8889, + 9072,9258,9445,9635,9828,10022,10219,10417, + 10619,10822,11028,11235,11446,11658,11873,12090, + 12309,12530,12754,12980,13209,13440,13673,13909, + 14146,14387,14629,14874,15122,15371,15623,15878, + 16135,16394,16656,16920,17187,17456,17727,18001, + 18277,18556,18837,19121,19407,19696,19987,20281, + 20577,20876,21177,21481,21787,22096,22407,22721, + 23038,23357,23678,24002,24329,24658,24990,25325, + 25662,26001,26344,26688,27036,27386,27739,28094, + 28452,28813,29176,29542,29911,30282,30656,31033, + 31412,31794,32179,32567,32957,33350,33745,34143, + 34544,34948,35355,35764,36176,36591,37008,37429, + 37852,38278,38706,39138,39572,40009,40449,40891, + 41337,41785,42236,42690,43147,43606,44069,44534, + 45002,45473,45947,46423,46903,47385,47871,48359, + 48850,49344,49841,50341,50844,51349,51858,52369, + 52884,53401,53921,54445,54971,55500,56032,56567, + 57105,57646,58190,58737,59287,59840,60396,60955, + 61517,62082,62650,63221,63795,64372,64952,65535 +}; +#endif /* simplified read only */ + +/* The base/delta tables are required for both read and write (but currently + * only the simplified versions.) + */ +PNG_CONST png_uint_16 png_sRGB_base[512] = +{ + 128,1782,3382,4641,5673,6563,7355,8072, + 8732,9346,9920,10463,10977,11466,11935,12384, + 12815,13232,13634,14024,14402,14768,15125,15473, + 15811,16142,16465,16781,17090,17393,17689,17980, + 18266,18546,18822,19093,19359,19621,19879,20133, + 20383,20630,20873,21113,21349,21583,21813,22040, + 22265,22487,22706,22923,23138,23350,23559,23767, + 23972,24175,24376,24575,24772,24967,25160,25352, + 25541,25729,25916,26100,26283,26465,26645,26823, + 27000,27176,27350,27523,27694,27865,28033,28201, + 28368,28533,28697,28860,29021,29182,29341,29500, + 29657,29813,29969,30123,30276,30428,30580,30730, + 30880,31028,31176,31323,31469,31614,31758,31902, + 32044,32186,32327,32468,32607,32746,32884,33021, + 33158,33294,33429,33563,33697,33830,33963,34095, + 34226,34356,34486,34616,34744,34872,35000,35127, + 35253,35379,35504,35629,35753,35876,35999,36122, + 36244,36365,36486,36606,36726,36845,36964,37083, + 37201,37318,37435,37551,37667,37783,37898,38013, + 38127,38241,38354,38467,38580,38692,38803,38915, + 39025,39136,39246,39356,39465,39574,39682,39790, + 39898,40005,40112,40219,40325,40431,40537,40642, + 40747,40851,40955,41059,41163,41266,41369,41471, + 41573,41675,41776,41878,41978,42079,42179,42279, + 42379,42478,42577,42676,42774,42873,42970,43068, + 43165,43262,43359,43455,43552,43647,43743,43838, + 43934,44028,44123,44217,44311,44405,44498,44592, + 44685,44777,44870,44962,45054,45146,45238,45329, + 45420,45511,45601,45692,45782,45872,45961,46051, + 46140,46229,46318,46406,46494,46582,46670,46758, + 46845,46933,47020,47107,47193,47280,47366,47452, + 47538,47623,47708,47794,47879,47963,48048,48132, + 48217,48301,48384,48468,48552,48635,48718,48801, + 48884,48966,49048,49131,49213,49294,49376,49457, + 49539,49620,49701,49781,49862,49942,50023,50103, + 50183,50262,50342,50421,50501,50580,50659,50738, + 50816,50895,50973,51051,51129,51207,51284,51362, + 51439,51517,51594,51670,51747,51824,51900,51977, + 52053,52129,52205,52280,52356,52431,52507,52582, + 52657,52732,52807,52881,52956,53030,53104,53178, + 53252,53326,53399,53473,53546,53620,53693,53766, + 53839,53911,53984,54056,54129,54201,54273,54345, + 54417,54489,54560,54632,54703,54774,54845,54916, + 54987,55058,55128,55199,55269,55340,55410,55480, + 55550,55619,55689,55759,55828,55897,55967,56036, + 56105,56174,56242,56311,56380,56448,56516,56585, + 56653,56721,56789,56857,56924,56992,57059,57127, + 57194,57261,57328,57395,57462,57529,57595,57662, + 57728,57795,57861,57927,57993,58059,58125,58191, + 58256,58322,58387,58453,58518,58583,58648,58713, + 58778,58843,58908,58972,59037,59101,59165,59230, + 59294,59358,59422,59486,59549,59613,59677,59740, + 59804,59867,59930,59993,60056,60119,60182,60245, + 60308,60370,60433,60495,60558,60620,60682,60744, + 60806,60868,60930,60992,61054,61115,61177,61238, + 61300,61361,61422,61483,61544,61605,61666,61727, + 61788,61848,61909,61969,62030,62090,62150,62210, + 62271,62331,62391,62450,62510,62570,62630,62689, + 62749,62808,62867,62927,62986,63045,63104,63163, + 63222,63281,63339,63398,63457,63515,63574,63632, + 63691,63749,63807,63865,63923,63981,64039,64097, + 64155,64212,64270,64328,64385,64442,64500,64557, + 64614,64671,64729,64786,64843,64899,64956,65013, + 65070,65126,65183,65239,65296,65352,65408,65465 +}; + +PNG_CONST png_byte png_sRGB_delta[512] = +{ + 207,200,157,129,111,99,90,82,77,72,68,64,61,59,56,54, + 52,50,49,47,46,45,43,42,41,40,39,39,38,37,36,36, + 35,34,34,33,33,32,32,31,31,30,30,30,29,29,28,28, + 28,27,27,27,26,26,26,26,25,25,25,25,24,24,24,24, + 23,23,23,23,23,22,22,22,22,22,22,21,21,21,21,21, + 21,20,20,20,20,20,20,20,20,19,19,19,19,19,19,19, + 19,18,18,18,18,18,18,18,18,18,18,17,17,17,17,17, + 17,17,17,17,17,17,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + +/* Hence the define in pngpriv.h to calculate the sRGB value of a linear value + * expressed as a fixed point integer scaled by 255*65535 (note that the tables + * include the +.5 to do rounding correctly.) + */ +#endif /* SIMPLIFIED READ/WRITE sRGB support */ + +/* SIMPLIFIED READ/WRITE SUPPORT */ +#if defined PNG_SIMPLIFIED_READ_SUPPORTED ||\ + defined PNG_SIMPLIFIED_WRITE_SUPPORTED +static int +png_image_free_function(png_voidp argument) +{ + png_imagep image = argument; + png_controlp cp = image->opaque; + png_control c; + + /* Double check that we have a png_ptr - it should be impossible to get here + * without one. + */ + if (cp->png_ptr == NULL) + return 0; + + /* First free any data held in the control structure. */ +# ifdef PNG_STDIO_SUPPORTED + if (cp->owned_file) + { + FILE *fp = cp->png_ptr->io_ptr; + cp->owned_file = 0; + + /* Ignore errors here. */ + if (fp != NULL) + { + cp->png_ptr->io_ptr = NULL; + (void)fclose(fp); + } + } +# endif + + /* Copy the control structure so that the original, allocated, version can be + * safely freed. Notice that a png_error here stops the remainder of the + * cleanup, but this is probably fine because that would indicate bad memory + * problems anyway. + */ + c = *cp; + image->opaque = &c; + png_free(c.png_ptr, cp); + + /* Then the structures, calling the correct API. */ + if (c.for_write) + { +# ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED + png_destroy_write_struct(&c.png_ptr, &c.info_ptr); +# else + png_error(c.png_ptr, "simplified write not supported"); +# endif + } + else + { +# ifdef PNG_SIMPLIFIED_READ_SUPPORTED + png_destroy_read_struct(&c.png_ptr, &c.info_ptr, NULL); +# else + png_error(c.png_ptr, "simplified read not supported"); +# endif + } + + /* Success. */ + return 1; +} + +void PNGAPI +png_image_free(png_imagep image) +{ + /* Safely call the real function, but only if doing so is safe at this point + * (if not inside an error handling context). Otherwise assume + * png_safe_execute will call this API after the return. + */ + if (image != NULL && image->opaque != NULL && + image->opaque->error_buf == NULL) + { + /* Ignore errors here: */ + (void)png_safe_execute(image, png_image_free_function, image); + image->opaque = NULL; + } +} + +int /* PRIVATE */ +png_image_error(png_imagep image, png_const_charp error_message) +{ + /* Utility to log an error. */ + png_safecat(image->message, sizeof image->message, 0, error_message); + image->warning_or_error = 1; + png_image_free(image); + return 0; +} + +#endif /* SIMPLIFIED READ/WRITE */ #endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ diff --git a/png.h b/png.h index 9852f7224..922fe16b3 100644 --- a/png.h +++ b/png.h @@ -1,7 +1,7 @@ /* png.h - header file for PNG reference library * - * libpng version 1.5.7beta02 - November 5, 2011 + * libpng version 1.5.7beta02 - November 8, 2011 * Copyright (c) 1998-2011 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) @@ -11,7 +11,7 @@ * Authors and maintainers: * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger - * libpng versions 0.97, January 1998, through 1.5.7beta02 - November 5, 2011: Glenn + * libpng versions 0.97, January 1998, through 1.5.7beta02 - November 8, 2011: Glenn * See also "Contributing Authors", below. * * Note about libpng version numbers: @@ -195,7 +195,7 @@ * * This code is released under the libpng license. * - * libpng versions 1.2.6, August 15, 2004, through 1.5.7beta02, November 5, 2011, are + * libpng versions 1.2.6, August 15, 2004, through 1.5.7beta02, November 8, 2011, are * Copyright (c) 2004, 2006-2011 Glenn Randers-Pehrson, and are * distributed according to the same disclaimer and license as libpng-1.2.5 * with the following individual added to the list of Contributing Authors: @@ -307,7 +307,7 @@ * Y2K compliance in libpng: * ========================= * - * November 5, 2011 + * November 8, 2011 * * Since the PNG Development group is an ad-hoc body, we can't make * an official declaration. @@ -365,12 +365,15 @@ * describes how to use libpng, and the file example.c summarizes it * with some code on which to build. This file is useful for looking * at the actual function definitions and structure components. + * + * If you just need to read a PNG file and don't want to read the documentation + * skip to the end of this file and read the section entitled 'simplified API'. */ /* Version information for png.h - this should match the version in png.c */ #define PNG_LIBPNG_VER_STRING "1.5.7beta02" #define PNG_HEADER_VERSION_STRING \ - " libpng version 1.5.7beta02 - November 5, 2011\n" + " libpng version 1.5.7beta02 - November 8, 2011\n" #define PNG_LIBPNG_VER_SONUM 15 #define PNG_LIBPNG_VER_DLLNUM 15 @@ -488,6 +491,7 @@ extern "C" { * 2. Type definitions (base types are defined in pngconf.h), structure * definitions. * 3. Exported library functions. + * 4. Simplified API. * * The library source code has additional files (principally pngpriv.h) that * allow configuration of the library. @@ -1124,6 +1128,11 @@ PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structp png_ptr)); #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED /* Reduce RGB to grayscale. */ +#define PNG_ERROR_ACTION_NONE 1 +#define PNG_ERROR_ACTION_WARN 2 +#define PNG_ERROR_ACTION_ERROR 3 +#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/ + PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structp png_ptr, int error_action, double red, double green)); PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structp png_ptr, @@ -2611,6 +2620,268 @@ PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); : (png_int_32)png_get_uint_32(buf))) #endif +/******************************************************************************* + * SIMPLIFIED API + ******************************************************************************/ +/* + * Please read the documentation in libpng-manual.txt if you don't understand + * what follows. + * + * The simplified API hides the details of both libpng and the PNG file format + * itself. It allows PNG files to be read into a very limited number of + * in-memory bitmap formats or to be written from the same formats. If these + * formats do not accomodate your needs then you can, and should, use the more + * sophisticated APIs above - these support a wide variety of in-memory formats + * and a wide variety of sophisticated transformations to those formats as well + * as a wide variety of APIs to manipulate ancilliary information. + * + * To read a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure (see below) on the stack and memset() it + * to all zero. + * 2) Call the appropriate png_image_begin_read... function. + * 3) Set the png_image 'format' member to the required format and allocate a + * buffer for the image. + * 4) Call png_image_finish_read to read the image into your buffer. + * + * To write a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure on the stack and memset() it to all zero. + * 2) Initialize the members of the structure that describe the image, setting + * the 'format' member to the format of the image in memory. + * 3) Call the appropriate png_image_write... function with a pointer to the + * image to write the PNG data. + * + * png_image is a structure that describes the in-memory formant of an image + * when it is being read or define the in-memory format of an image that you + * need to write: + */ +typedef struct png_control *png_controlp; +typedef struct +{ + png_uint_32 width; /* Image width in pixels (columns) */ + png_uint_32 height; /* Image height in pixels (rows) */ + png_uint_32 format; /* Image format as defined below */ + png_uint_32 flags; /* A bit mask containing informational flags */ + png_controlp opaque; /* Initialize to NULL, free with png_image_free */ + + /* In the event of an error or warning the following field will be set to a + * non-zero value and the 'message' field will contain a '\0' terminated + * string with the libpng error message. + */ + png_uint_32 warning_or_error; + char message[64]; +} png_image, *png_imagep; + +/* The pixels (samples) of the image have one to four channels in the range 0 to + * 1.0: + * + * 1: A single gray or luminance channel (G). + * 2: A gray/luminance channel and an alpha channel (GA). + * 3: Three red, green, blue color channels (RGB). + * 4: Three color channels and an alpha channel (RGBA). + * + * The channels are encoded in one of two ways: + * + * a) As a small integer, value 0..255, contained in a (png_byte). For the + * alpha channel the original value is simple value/255. For the color or + * luminance channels the value is encoded according to the sRGB specification + * and matches the 8-bit format expected by typical display devices. + * + * The color/gray channels are not scaled (pre-multiplied) by the alpha + * channel and are suitable for passing to color management software. + * + * b) As a value in the range 0..65535, contained in a (png_uint_16). All + * channels can be converted to the original value by dividing by 65535; all + * channels are linear. Color channels use the RGB encoding (RGB end-points) of + * the sRGB specification. This encoding is identified by the + * PNG_FORMAT_FLAG_LINEAR flag below. + * + * When an alpha channel is present it is expected to denote pixel coverage + * of the color or luminance channels and is returned as an associated alpha + * channel: the color/gray channels are scaled (pre-multiplied) the alpha value. + */ + +/* PNG_FORMAT_* + * + * #defines to be used in png_image::format. Each #define identifies a + * particular layout of channel data and, if present, alpha values. There are + * separate defines for each of the two channel encodings. + * + * A format is built up using single bit flag values. Not all combinations are + * valid: use the bit flag values below for testing a format returned by the + * read APIs, but set formats from the derived values. + * + * NOTE: libpng can be built with particular features disabled, if you see + * compiler errors because the definition of one of the following flags has been + * compiled out it is because libpng does not have the required support. It is + * possible, however, for the libpng configuration to enable the format on just + * read or just write, in that case you will may see an error at run time. You + * can guard against this by checking for the definition of: + * + * PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED + */ +#define PNG_FORMAT_FLAG_ALPHA 0x01 /* format with an alpha channel */ +#define PNG_FORMAT_FLAG_COLOR 0x02 /* color format: otherwise grayscale */ +#define PNG_FORMAT_FLAG_LINEAR 0x04 /* png_uint_16 channels else png_byte */ + +#ifdef PNG_FORMAT_BGR_SUPPORTED +# define PNG_FORMAT_FLAG_BGR 0x08 /* BGR colors, else order is RGB */ +#endif + +#ifdef PNG_FORMAT_AFIRST_SUPPORTED +# define PNG_FORMAT_FLAG_AFIRST 0x10 /* alpha channel comes first */ +#endif + +/* Supported formats are as follows. Future versions of libpng may support more + * formats, for compatibility with older versions simply check if the format + * macro is defined using #ifdef. These defines describe the in-memory layout + * of the components of the pixels of the image. + * + * First the single byte formats: + */ +#define PNG_FORMAT_GRAY 0 +#define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA +#define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_AFIRST) +#define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR +#define PNG_FORMAT_BGR (PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_BGR) +#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_AFIRST) + +/* Then the linear (png_uint_16) formats. When naming these "Y" is used to + * indicate a luminance channel. The component order within the pixel is + * always the same - there is no provision for swapping the order of the + * components in the linear format. + */ +#define PNG_FORMAT_FP_Y PNG_FORMAT_FLAG_FP +#define PNG_FORMAT_FP_Y_ALPHA (PNG_FORMAT_FLAG_FP|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_FP_RGB (PNG_FORMAT_FLAG_FP|PNG_FORMAT_FLAG_COLOR) +#define PNG_FORMAT_FP_RGB_ALPHA \ + (PNG_FORMAT_FLAG_FP|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA) + +/* PNG_IMAGE macros + * + * These are convenience macros to derive information from a png_image structure + */ +#define PNG_IMAGE_CHANNELS(fmt)\ + (1+((fmt)&(PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA))) + /* Return the total number of channels in a given format: 1..4 */ + +#define PNG_IMAGE_COMPONENT_SIZE(fmt)\ + (((fmt) & PNG_FORMAT_FLAG_LINEAR) ? sizeof (png_uint_16) : sizeof (png_byte)) + /* Return the size in bytes of a single component of a pixel in the image. */ + +#define PNG_IMAGE_PIXEL_SIZE(fmt)\ + (PNG_IMAGE_CHANNELS(fmt) * PNG_IMAGE_COMPONENT_SIZE(fmt)) + /* Return the size in bytes of a single pixel in the image. */ + +#define PNG_IMAGE_ROW_STRIDE(image)\ + (PNG_IMAGE_CHANNELS((image).format) * (image).width) + /* Return the total number of components in a single row of the image; this + * is the minimum 'row stride', the minimum count of components between each + * row. */ + +#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\ + (PNG_IMAGE_COMPONENT_SIZE((image).format) * (image).height * (row_stride)) + /* Return the size, in bytes, of an image buffer given a png_image and a row + * stride - the number of components to leave space for in each row. */ + +/* PNG_IMAGE_FLAG_* + * + * Flags containing additional information about the image are held in the + * 'flags' field of png_image. + */ +#define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 1 + /* This indicates the the RGB values of the in-memory bitmap do not + * correspond to the red, green and blue end-points defined by sRGB. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* READ APIs + * --------- + * + * The png_image passed to the read APIs must have been initialized by setting + * the png_controlp field 'opaque' to NULL (or, better, memset the whole thing.) + */ +#ifdef PNG_STDIO_SUPPORTED +PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, + const char *file_name)); + /* The named file is opened for read and the image filled in from the PNG + * header in the file. */ + +PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, + FILE* file)); + /* The PNG header is read from the stdio FILE object. */ +#endif /* PNG_STDIO_SUPPORTED */ + +PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image, + png_const_voidp memory, png_size_t size)); + /* The PNG header is read from the given memory buffer. */ + +PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image, + png_colorp background, void *buffer, png_int_32 row_stride)); + /* Finish reading the image into the supplied buffer and clean up the + * png_image structure. + * + * row_stride is the step, in png_byte or float units as appropriate, + * between adjacent rows. A positive stride indicates that the top-most row + * is first in the buffer - the normal top-down arrangement. A negative + * stride indicates that the bottom-most row is first in the buffer. + * + * background need only be supplied if an alpha channel must be removed from + * a png_byte format and the removal is to be done by compositing on a solid + * color; otherwise it may be NULL and any composition will be done directly + * onto the buffer. The value is an sRGB color to use for the background, + * for grayscale output the green channel is used. + * + * For linear output removing an alpha channel is always done by compositing + * on black. + */ + +PNG_EXPORT(238, void, png_image_free, (png_imagep image)); + /* Free any data allocated by libpng in image->opaque, setting the pointer to + * NULL. May be called at any time after the structure is initialized. */ +#endif /* PNG_SIMPLIFIED_READ_SUPPORTED */ + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +/* WRITE APIS + * ---------- + * For write you must initialize a png_image structure to describe the image to + * be written: + * + * opaque: must be initialized to NULL + * width: image width in pixels + * height: image height in rows + * format: the format of the data you wish to write + * flags: set to 0 unless one of the defined flags applies; set + * PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images where the RGB + * values do not correspond to the colors in sRGB. + */ +PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, + const char *file, int convert_to_8bit, const void *buffer, + png_int_32 row_stride)); + /* Write the image to the named file. */ + +PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, + int convert_to_8_bit, const void *buffer, png_int_32 row_stride)); + /* Write the image to the given (FILE*). */ + +/* With all write APIs if image is in one of the linear formats with + * (png_uint_16) data then setting convert_to_8_bit will cause the output to be + * a (png_byte) PNG gamma encoded according to the sRGB specification, otherwise + * a 16-bit linear encoded PNG file is written. + * + * With all APIs row_stride is handled as in the read APIs - it is the spacing + * from one row to the next in component sized units (float) and if negative + * indicates a bottom-up row layout in the buffer. + */ +#endif /* PNG_SIMPLIFIED_WRITE_SUPPORTED */ +/******************************************************************************* + * END OF SIMPLIFIED API + ******************************************************************************/ + /* Maintainer: Put new public prototypes here ^, in libpng.3, and project * defs */ @@ -2620,7 +2891,7 @@ PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); * scripts/symbols.def as well. */ #ifdef PNG_EXPORT_LAST_ORDINAL - PNG_EXPORT_LAST_ORDINAL(233); + PNG_EXPORT_LAST_ORDINAL(240); #endif #ifdef __cplusplus diff --git a/pngerror.c b/pngerror.c index 1ed9f8a2a..ab16eebf5 100644 --- a/pngerror.c +++ b/pngerror.c @@ -673,4 +673,84 @@ png_set_strip_error_numbers(png_structp png_ptr, png_uint_32 strip_mode) } } #endif + +#if defined PNG_SIMPLIFIED_READ_SUPPORTED ||\ + defined PNG_SIMPLIFIED_WRITE_SUPPORTED + /* Currently the above both depend on SETJMP_SUPPORTED, however it would be + * possible to implement without setjmp support just so long as there is some + * way to handle the error return here: + */ +PNG_FUNCTION(void /* PRIVATE */, +png_safe_error,(png_structp png_ptr, png_const_charp error_message), + PNG_NORETURN) +{ + png_imagep image = png_ptr->error_ptr; + + /* An error is always logged here, overwriting anything (typically a warning) + * that is already there: + */ + if (image != NULL) + { + png_safecat(image->message, sizeof image->message, 0, error_message); + image->warning_or_error = 1; + + /* Retrieve the jmp_buf from within the png_control */ + if (image->opaque != NULL && image->opaque->error_buf != NULL) + longjmp(image->opaque->error_buf, 1); + + /* Missing longjmp buffer, the following is to help debugging: */ + { + size_t pos = png_safecat(image->message, sizeof image->message, 0, + "bad longjmp: "); + png_safecat(image->message, sizeof image->message, pos, error_message); + } + } + + /* Here on an internal programming error. */ + abort(); +} + +#ifdef PNG_WARNINGS_SUPPORTED +void /* PRIVATE */ +png_safe_warning(png_structp png_ptr, png_const_charp warning_message) +{ + png_imagep image = png_ptr->error_ptr; + + /* A warning is just logged if there is no warning or error. */ + if (image->warning_or_error == 0) + { + png_safecat(image->message, sizeof image->message, 0, warning_message); + image->warning_or_error = 2; + } +} +#endif + +int /* PRIVATE */ +png_safe_execute(png_imagep imageIn, int (*function)(png_voidp), png_voidp arg) +{ + volatile png_imagep image = imageIn; + volatile int result; + volatile png_voidp saved_error_buf; + jmp_buf safe_jmpbuf; + + /* Safely execute function(arg) with png_error returning to this function. */ + saved_error_buf = image->opaque->error_buf; + result = setjmp(safe_jmpbuf) == 0; + + if (result) + { + + image->opaque->error_buf = safe_jmpbuf; + result = function(arg); + } + + image->opaque->error_buf = saved_error_buf; + + /* And do the cleanup prior to any failure return. */ + if (!result) + png_image_free(image); + + return result; +} +#endif /* SIMPLIFIED READ/WRITE */ #endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/pngpriv.h b/pngpriv.h index a9e68d69d..9a28382ea 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -196,6 +196,23 @@ typedef PNG_CONST png_uint_16p FAR * png_const_uint_16pp; # define PNG_STATIC static #endif +/* C99 restrict is used where possible, to do this 'restrict' is defined as + * empty if we can't be sure it is supported. configure builds have already + * done this work. + */ +#ifdef PNG_CONFIGURE_LIBPNG +# define PNG_RESTRICT restrict +#else + /* Modern compilers support restrict, but assume not for anything not + * recognized here: + */ +# if defined __GNUC__ || defined _MSC_VER || defined __WATCOMC__ +# define PNG_RESTRICT restrict +# else +# define PNG_RESTRICT +# endif +#endif + /* If warnings or errors are turned off the code is disabled or redirected here. * From 1.5.4 functions have been added to allow very limited formatting of * error and warning messages - this code will also be disabled here. @@ -498,6 +515,26 @@ typedef PNG_CONST png_uint_16p FAR * png_const_uint_16pp; abs((int)((c1).green) - (int)((c2).green)) + \ abs((int)((c1).blue) - (int)((c2).blue))) +/* Added to libpng-1.5.7: sRGB convertion tables */ +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +PNG_EXTERN /*PRIVATE*/ PNG_CONST png_uint_16 png_sRGB_table[256]; + /* Convert from an sRGB encoded value 0..255 to a 16-bit linear value, + * 0..65535. This table gives the closes 16-bit answers (no errors). + */ +#endif + +#if defined PNG_SIMPLIFIED_READ_SUPPORTED ||\ + defined PNG_SIMPLFIED_WRITE_SUPPORTED +PNG_EXTERN /*PRIVATE*/ PNG_CONST png_uint_16 png_sRGB_base[512]; +PNG_EXTERN /*PRIVATE*/ PNG_CONST png_byte png_sRGB_delta[512]; + +#define PNG_sRGB_FROM_LINEAR(linear) ((png_sRGB_base[(linear)>>15] +\ + ((((linear)&0x7fff)*png_sRGB_delta[(linear)>>15])>>12)) >> 8) + /* Given a value 'linear' in the range 0..255*65535 calculate the 8-bit sRGB + * encoded value with maximum error 0.646365. Note that the input is not a + * 16-bit value; it has been multiplied by 255! */ +#endif + /* Added to libpng-1.2.6 JB */ #define PNG_ROWBYTES(pixel_bits, width) \ ((pixel_bits) >= 8 ? \ @@ -1588,6 +1625,47 @@ PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr, int bit_depth)); #endif +/* SIMPLIFIED READ/WRITE SUPPORT */ +#if defined PNG_SIMPLIFIED_READ_SUPPORTED ||\ + defined PNG_SIMPLIFIED_WRITE_SUPPORTED +/* The internal structure that png_image::opaque points to. */ +typedef struct png_control +{ + png_structp png_ptr; + png_infop info_ptr; + png_voidp error_buf; /* Always a jmp_buf at present. */ + + png_const_bytep memory; /* Memory buffer. */ + png_size_t size; /* Size of the memory buffer. */ + + unsigned int for_write :1; /* Otherwise it is a read structure */ + unsigned int owned_file :1; /* We own the file in io_ptr */ +} png_control; + +/* Utility to safely execute a piece of libpng code catching and logging any + * errors that might occur. Returns true on success, false on failure (either + * of the function or as a result of a png_error.) + */ +PNG_FUNCTION(void, png_safe_error, (png_structp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +#ifdef PNG_WARNINGS_SUPPORTED + PNG_EXTERN void png_safe_warning(png_structp png_ptr, + png_const_charp warning_message); +#else +# define png_safe_warning 0/*dummy argument*/ +#endif + +PNG_EXTERN int png_safe_execute PNGARG((png_imagep image, + int (*function)(png_voidp), png_voidp arg)); + +/* Utility to log an error, this also cleans up the png_image, the function + * always returns 0 (false). + */ +PNG_EXTERN int png_image_error(png_imagep image, png_const_charp error_message); + +#endif /* SIMPLIFIED READ/WRITE */ + /* Maintainer: Put new private prototypes here ^ and in libpngpf.3 */ diff --git a/pngread.c b/pngread.c index 29fa92183..d96b98ae0 100644 --- a/pngread.c +++ b/pngread.c @@ -15,6 +15,9 @@ */ #include "pngpriv.h" +#if defined PNG_SIMPLIFIED_READ_SUPPORTED && defined PNG_STDIO_SUPPORTED +# include +#endif #ifdef PNG_READ_SUPPORTED @@ -1304,4 +1307,706 @@ png_read_png(png_structp png_ptr, png_infop info_ptr, } #endif /* PNG_INFO_IMAGE_SUPPORTED */ #endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* SIMPLIFIED READ + * + * This code currently relies on the sequential reader, though it could easily + * be made to work with the progressive one. + */ +/* Do all the *safe* initialization - 'safe' means that png_error won't be + * called, so setting up the jmp_buf is not required. + */ +static int +png_image_read_init(png_imagep image) +{ + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, image, + png_safe_error, png_safe_warning); + + if (png_ptr != NULL) + { + png_infop info_ptr = png_create_info_struct(png_ptr); + + if (info_ptr != NULL) + { + png_controlp control = png_malloc_warn(png_ptr, sizeof *control); + + if (control != NULL) + { + memset(control, 0, sizeof *control); + + control->png_ptr = png_ptr; + control->info_ptr = info_ptr; + control->for_write = 0; + + image->opaque = control; + return 1; + } + + /* Error clean up */ + png_destroy_info_struct(png_ptr, &info_ptr); + } + + png_destroy_read_struct(&png_ptr, NULL, NULL); + } + + return png_image_error(image, "png_image_read: out of memory"); +} + +/* Utility to find the base format of a PNG file from a png_struct. */ +static png_uint_32 +png_image_format(png_structp png_ptr, png_infop info_ptr) +{ + png_uint_32 format = 0; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + format |= PNG_FORMAT_FLAG_COLOR; + + if (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) + format |= PNG_FORMAT_FLAG_ALPHA; + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + format |= PNG_FORMAT_FLAG_ALPHA; + + if (png_ptr->bit_depth == 16) + format |= PNG_FORMAT_FLAG_LINEAR; + + return format; +} + +/* Do the main body of a 'png_image_begin_read' function; read the PNG file + * header and fill in all the information. This is executed in a safe context, + * unlike the init routine above. + */ +static int +png_image_read_header(png_voidp argument) +{ + png_imagep image = argument; + png_structp png_ptr = image->opaque->png_ptr; + png_infop info_ptr = image->opaque->info_ptr; + + png_read_info(png_ptr, info_ptr); + + /* Do this the fast way; just read directly out of png_struct. */ + image->width = png_ptr->width; + image->height = png_ptr->height; + + { + png_uint_32 format = png_image_format(png_ptr, info_ptr); + + image->format = format; + image->flags = 0; + + /* Now try to work out whether the color data does not match sRGB. */ + if ((format & PNG_FORMAT_FLAG_COLOR) != 0 && + (info_ptr->valid & PNG_INFO_sRGB) == 0) + { + /* gamma is irrelevant because libpng does gamma correction, what + * matters is if the cHRM chunk doesn't match or, in the absence of + * cRHM, if the iCCP profile looks to have different end points. + */ + if (info_ptr->valid & PNG_INFO_cHRM) + { + /* TODO: this is a copy'n'paste from pngrutil.c, make a common + * checking function. This checks for a 1% error. + */ + /* The cHRM chunk is used in preference to iCCP */ + if (PNG_OUT_OF_RANGE(info_ptr->x_white, 31270, 1000) || + PNG_OUT_OF_RANGE(info_ptr->y_white, 32900, 1000) || + PNG_OUT_OF_RANGE(info_ptr->x_red, 64000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->y_red, 33000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->x_green, 30000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->y_green, 60000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->x_blue, 15000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->y_blue, 6000, 1000)) + image->flags |= PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB; + } + + else if (info_ptr->valid & PNG_INFO_iCCP) + { +# if 0 /* TODO: IMPLEMENT THIS! */ + /* Here if we just have an iCCP chunk. */ + if (!png_iCCP_is_sRGB(png_ptr, info_ptr)) +# endif + image->flags |= PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB; + } + } + } + + return 1; +} + +#ifdef PNG_STDIO_SUPPORTED +int PNGAPI +png_image_begin_read_from_stdio(png_imagep image, FILE* file) +{ + if (image != NULL) + { + if (file != NULL) + { + if (png_image_read_init(image)) + { + /* This is slightly evil, but png_init_io doesn't do anything other + * than this and we haven't changed the standard IO functions so + * this saves a 'safe' function. + */ + image->opaque->png_ptr->io_ptr = file; + return png_safe_execute(image, png_image_read_header, image); + } + } + + else + return png_image_error(image, + "png_image_begin_read_from_stdio: invalid argument"); + } + + return 0; +} + +int PNGAPI +png_image_begin_read_from_file(png_imagep image, const char *file_name) +{ + if (image != NULL) + { + if (file_name != NULL) + { + FILE *fp = fopen(file_name, "rb"); + + if (fp != NULL) + { + if (png_image_read_init(image)) + { + image->opaque->png_ptr->io_ptr = fp; + image->opaque->owned_file = 1; + return png_safe_execute(image, png_image_read_header, image); + } + + /* Clean up: just the opened file. */ + (void)fclose(fp); + } + + else + return png_image_error(image, strerror(errno)); + } + + else + return png_image_error(image, + "png_image_begin_read_from_file: invalid argument"); + } + + return 0; +} +#endif /* PNG_STDIO_SUPPORTED */ + +static void PNGCBAPI +png_image_memory_read(png_structp png_ptr, png_bytep out, png_size_t need) +{ + if (png_ptr != NULL) + { + png_imagep image = png_ptr->io_ptr; + if (image != NULL) + { + png_controlp cp = image->opaque; + if (cp != NULL) + { + png_const_bytep memory = cp->memory; + png_size_t size = cp->size; + + if (memory != NULL && size >= need) + { + memcpy(out, memory, need); + cp->memory = memory + need; + cp->size = size - need; + return; + } + + png_error(png_ptr, "read beyond end of data"); + } + } + + png_error(png_ptr, "invalid memory read"); + } +} + +int PNGAPI png_image_begin_read_from_memory(png_imagep image, + png_const_voidp memory, png_size_t size) +{ + if (image != NULL) + { + if (memory != NULL && size > 0) + { + if (png_image_read_init(image)) + { + /* Now set the IO functions to read from the memory buffer and + * store it into io_ptr. Again do this in-place to avoid calling a + * libpng function that requires error handling. + */ + image->opaque->memory = memory; + image->opaque->size = size; + image->opaque->png_ptr->io_ptr = image; + image->opaque->png_ptr->read_data_fn = png_image_memory_read; + + return png_safe_execute(image, png_image_read_header, image); + } + } + + else + return png_image_error(image, + "png_image_begin_read_from_memory: invalid argument"); + } + + return 0; +} + +/* Arguments to png_image_finish_read: */ +typedef struct +{ + /* Arguments: */ + png_imagep image; + png_voidp buffer; + png_int_32 row_stride; + png_colorp background; + /* Local variables: */ + png_bytep local_row; + png_bytep first_row; + ptrdiff_t row_bytes; /* unsigned arithmetic step between rows */ +} png_image_read_control; + +/* Just the row reading part of png_image_read. */ +static int +png_image_read_composite(png_voidp argument) +{ + png_image_read_control *display = argument; + png_imagep image = display->image; + png_structp png_ptr = image->opaque->png_ptr; + png_byte interlace_type = png_ptr->interlaced; + int passes; + + switch (interlace_type) + { + case PNG_INTERLACE_NONE: + passes = 1; + break; + + case PNG_INTERLACE_ADAM7: + passes = PNG_INTERLACE_ADAM7_PASSES; + break; + + default: + png_error(png_ptr, "unknown interlace type"); + } + + { + png_uint_32 height = image->height; + png_uint_32 width = image->width; + unsigned int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1; + int pass; + + for (pass = 0; pass < passes; ++pass) + { + png_bytep row = display->first_row; + unsigned int startx, stepx, stepy; + png_uint_32 y; + + if (interlace_type == PNG_INTERLACE_ADAM7) + { + /* The row may be empty for a short image: */ + if (PNG_PASS_COLS(width, pass) == 0) + continue; + + startx = PNG_PASS_START_COL(pass); + stepx = PNG_PASS_COL_OFFSET(pass); + y = PNG_PASS_START_ROW(pass); + stepy = PNG_PASS_COL_OFFSET(pass); + } + + else + { + y = 0; + startx = 0; + stepx = stepy = 1; + } + + for (; ylocal_row; + png_bytep outrow = row; + png_uint_32 x; + + /* Read the row, which is packed: */ + png_read_row(png_ptr, inrow, NULL); + + /* Now do the composition on each pixel in this row. */ + for (x = startx; x 0) /* else no change to the output */ + { + unsigned int c; + + for (c=0; crow_bytes; + } + } + } + + return 1; +} + +/* The guts of png_image_finish_read as a png_safe_execute callback. */ +static int +png_image_read_end(png_voidp argument) +{ + png_image_read_control *display = argument; + png_imagep image = display->image; + png_structp png_ptr = image->opaque->png_ptr; + png_infop info_ptr = image->opaque->info_ptr; + + png_uint_32 format = image->format; + int linear = (format & PNG_FORMAT_FLAG_LINEAR) != 0; + int do_local_compose = 0; + int passes = 0; + + /* Add transforms to ensure the correct output format is produced then check + * that the required implementation support is there. Always expand; always + * need 8 bits minimum, no palette and expanded tRNS. + */ + png_set_expand(png_ptr); + + /* Now check the format to see if it was modified. */ + { + png_uint_32 base_format = png_image_format(png_ptr, info_ptr); + png_uint_32 change = format ^ base_format; + + /* Set the gamma appropriately, linear for 16-bit input, sRGB otherwise. + */ + { + png_fixed_point input_gamma_default, output_gamma; + int mode; + + if (base_format & PNG_FORMAT_FLAG_LINEAR) + input_gamma_default = PNG_GAMMA_LINEAR; + else + input_gamma_default = PNG_DEFAULT_sRGB; + + if (linear) + { + mode = PNG_ALPHA_STANDARD; + output_gamma = PNG_GAMMA_LINEAR; + } + + else + { + mode = PNG_ALPHA_PNG; + output_gamma = PNG_DEFAULT_sRGB; + } + + /* Set the mode, the default gamma for the file and then, if it + * doesn't match the default, the output gamma. + */ + png_set_alpha_mode_fixed(png_ptr, mode, input_gamma_default); + if (input_gamma_default != output_gamma) + png_set_alpha_mode(png_ptr, mode, output_gamma); + + /* If the bit-depth changes then handle that here. */ + if (change & PNG_FORMAT_FLAG_LINEAR) + { + if (linear /*16-bit output*/) + png_set_expand_16(png_ptr); + + else /* 8-bit output */ + png_set_scale_16(png_ptr); + + change &= ~PNG_FORMAT_FLAG_LINEAR; + } + } + + /* Now the background/alpha channel changes. */ + if (change & PNG_FORMAT_FLAG_ALPHA) + { + /* Removing an alpha channel requires composition for the 8-bit + * formats; for the 16-bit it is already done, above, by the + * pre-multiplication and the channel just needs to be stripped. + */ + if (base_format & PNG_FORMAT_FLAG_ALPHA) + { + if (linear) /* compose on black (well, pre-multiply) */ + png_set_strip_alpha(png_ptr); + + else if (display->background != NULL) + { + png_color_16 c; + + c.index = 0; /*unused*/ + c.red = display->background->red; + c.green = display->background->green; + c.blue = display->background->blue; + c.gray = display->background->green; + + /* This is always an 8-bit sRGB value, using the 'green' channel + * for gray is much better than calculating the luminance here; + * we can get off-by-one errors in that calculation relative to + * the app expectations and that will show up in transparent + * pixels. + */ + png_set_background_fixed(png_ptr, &c, + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); + } + + else /* compose on row: implemented below. */ + { + do_local_compose = 1; + /* This leaves the alpha channel in the output, it has to be + * removed by the code below. Set the encoding to the 'OPTIMIZE' + * one so the code only has to hack on the pixels that require + * composition. + */ + png_set_alpha_mode(png_ptr, PNG_ALPHA_OPTIMIZED, + PNG_DEFAULT_sRGB); + } + } + + else /* output needs an alpha channel */ + { + /* This is tricky because it happens before the swap operation has + * been accomplished, so always add the alpha channel after the + * component channels. + */ + png_set_add_alpha(png_ptr, 255/*opaque*/, PNG_FILLER_AFTER); + } + + change &= ~PNG_FORMAT_FLAG_ALPHA; + } + + if (change & PNG_FORMAT_FLAG_COLOR) + { + /* gray<->color transformation required. */ + if (format & PNG_FORMAT_FLAG_COLOR) + png_set_gray_to_rgb(png_ptr); + + else + png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE, + PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT); + + change &= ~PNG_FORMAT_FLAG_COLOR; + } + +# ifdef PNG_FORMAT_BGR_SUPPORTED + if (format & PNG_FORMAT_FLAG_BGR) + { + /* Check only the output format; PNG is never BGR, don't do this if + * the output is gray, but fix up the 'format' value in that case. + */ + if (format & PNG_FORMAT_FLAG_COLOR) + png_set_bgr(png_ptr); + + else + format &= ~PNG_FORMAT_FLAG_BGR; + + change &= ~PNG_FORMAT_FLAG_BGR; + } +# endif + +# ifdef PNG_FORMAT_AFIRST_SUPPORTED + if (format & PNG_FORMAT_FLAG_AFIRST) + { + /* Only relevant if there is an alpha channel - it's particularly + * important to handle this correctly because do_local_compose may + * be set above and then libpng will keep the alpha channel for this + * code to remove. + */ + if (format & PNG_FORMAT_FLAG_ALPHA) + png_set_swap_alpha(png_ptr); + + else + format &= ~PNG_FORMAT_FLAG_AFIRST; + + change &= ~PNG_FORMAT_FLAG_AFIRST; + } +# endif + + /* If the *output* is 16-bit then we need to check for a byte-swap on this + * architecture. + */ + if (linear) + { + PNG_CONST png_uint_16 le = 0x0001; + + if (*(png_const_bytep)&le) + png_set_swap(png_ptr); + } + + /* If change is not now 0 some transformation is missing - error out. */ + if (change) + png_error(png_ptr, "png_read_image: unsupported transformation"); + } + + /* Update the 'info' structure and make sure the result is as required, first + * make sure to turn on the interlace handling if it will be required + * (because it can't be turned on *after* the call to png_read_update_info!) + */ + if (!do_local_compose) + passes = png_set_interlace_handling(png_ptr); + + png_read_update_info(png_ptr, info_ptr); + + { + png_uint_32 info_format = 0; + + if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_format |= PNG_FORMAT_FLAG_COLOR; + + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + { + /* This channel may be removed below. */ + if (!do_local_compose) + info_format |= PNG_FORMAT_FLAG_ALPHA; + } + + else if (do_local_compose) /* internal error */ + png_error(png_ptr, "png_image_read: alpha channel lost"); + + if (info_ptr->bit_depth == 16) + info_format |= PNG_FORMAT_FLAG_LINEAR; + +# ifdef PNG_FORMAT_BGR_SUPPORTED + if (png_ptr->transformations & PNG_BGR) + info_format |= PNG_FORMAT_FLAG_BGR; +# endif + +# ifdef PNG_FORMAT_AFIRST_SUPPORTED + if (png_ptr->transformations & PNG_SWAP_ALPHA) + info_format |= PNG_FORMAT_FLAG_AFIRST; +# endif + + /* This is actually an internal error. */ + if (info_format != format) + png_error(png_ptr, "png_read_image: invalid transformations"); + } + + /* Now read the rows. If do_local_compose is set then it is necessary to use + * a local row buffer. The output will be GA, RGBA or BGRA and must be + * converted to G, RGB or BGR as appropriate. The 'local_row' member of the + * display acts as a flag. + */ + { + png_bytep first_row = display->buffer; + ptrdiff_t row_bytes = display->row_stride; + + if (linear) + row_bytes *= sizeof (png_uint_16); + + /* The following expression is designed to work correctly whether it gives + * a signed or an unsigned result. + */ + if (row_bytes < 0) + first_row += (image->height-1) * (-row_bytes); + + display->first_row = first_row; + display->row_bytes = row_bytes; + } + + if (do_local_compose) + { + int result; + png_bytep row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); + + display->local_row = row; + result = png_safe_execute(image, png_image_read_composite, display); + display->local_row = NULL; + png_free(png_ptr, row); + + return result; + } + + else + { + png_alloc_size_t row_bytes = display->row_bytes; + + while (--passes >= 0) + { + png_uint_32 y = image->height; + png_bytep row = display->first_row; + + while (y-- > 0) + { + png_read_row(png_ptr, row, NULL); + row += row_bytes; + } + } + + return 1; + } +} + +int PNGAPI +png_image_finish_read(png_imagep image, png_colorp background, void *buffer, + png_int_32 row_stride) +{ + if (image != NULL) + { + png_uint_32 check; + + if (row_stride < 0) + check = -row_stride; + + else + check = row_stride; + + if (buffer != NULL && check >= PNG_IMAGE_ROW_STRIDE(*image)) + { + int result; + png_image_read_control display; + + memset(&display, 0, sizeof display); + display.image = image; + display.buffer = buffer; + display.row_stride = row_stride; + display.background = background; + display.local_row = NULL; + result = png_safe_execute(image, png_image_read_end, &display); + png_image_free(image); + return result; + } + + else + return png_image_error(image, + "png_image_finish_read: invalid argument"); + } + + return 0; +} + +#endif /* PNG_SIMPLIFIED_READ_SUPPORTED */ #endif /* PNG_READ_SUPPORTED */ diff --git a/pngrtran.c b/pngrtran.c index d10f5cff5..a4a1a9ffa 100644 --- a/pngrtran.c +++ b/pngrtran.c @@ -936,15 +936,15 @@ png_set_rgb_to_gray_fixed(png_structp png_ptr, int error_action, switch(error_action) { - case 1: + case PNG_ERROR_ACTION_NONE: png_ptr->transformations |= PNG_RGB_TO_GRAY; break; - case 2: + case PNG_ERROR_ACTION_WARN: png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN; break; - case 3: + case PNG_ERROR_ACTION_ERROR: png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR; break; diff --git a/pngrutil.c b/pngrutil.c index ad43f4b59..c04974b09 100644 --- a/pngrutil.c +++ b/pngrutil.c @@ -2767,18 +2767,13 @@ png_check_chunk_name(png_structp png_ptr, png_uint_32 chunk_name) } } -/* Combines the row recently read in with the existing pixels in the - * row. This routine takes care of alpha and transparency if requested. - * This routine also handles the two methods of progressive display - * of interlaced images, depending on the mask value. - * The mask value describes which pixels are to be combined with - * the row. The pattern always repeats every 8 pixels, so just 8 - * bits are needed. A one indicates the pixel is to be combined, - * a zero indicates the pixel is to be skipped. This is in addition - * to any alpha or transparency value associated with the pixel. If - * you want all pixels to be combined, pass 0xff (255) in mask. +/* Combines the row recently read in with the existing pixels in the row. This + * routine takes care of alpha and transparency if requested. This routine also + * handles the two methods of progressive display of interlaced images, + * depending on the 'display' value; if 'display' is true then the whole row + * (dp) is filled from the start by replicating the available pixels. If + * 'display' is false only those pixels present in the pass are filled in. */ - void /* PRIVATE */ png_combine_row(png_structp png_ptr, png_bytep dp, int display) { @@ -2850,7 +2845,7 @@ png_combine_row(png_structp png_ptr, png_bytep dp, int display) if (pixel_depth < 8) { - /* For pixel depths up to 4-bpp the 8-pixel mask can be expanded to fit + /* For pixel depths up to 4 bpp the 8-pixel mask can be expanded to fit * into 32 bits, then a single loop over the bytes using the four byte * values in the 32-bit mask can be used. For the 'display' option the * expanded mask may also not require any masking within a byte. To @@ -2859,7 +2854,7 @@ png_combine_row(png_structp png_ptr, png_bytep dp, int display) * * The 'regular' case requires a mask for each of the first 6 passes, * the 'display' case does a copy for the even passes in the range - * 0..6. This has already been handled in the tst above. + * 0..6. This has already been handled in the test above. * * The masks are arranged as four bytes with the first byte to use in * the lowest bits (little-endian) regardless of the order (PACKSWAP or @@ -2867,7 +2862,7 @@ png_combine_row(png_structp png_ptr, png_bytep dp, int display) * * NOTE: the whole of this logic depends on the caller of this function * only calling it on rows appropriate to the pass. This function only - * understands the 'x' logic, the 'y' logic is handled by the caller. + * understands the 'x' logic; the 'y' logic is handled by the caller. * * The following defines allow generation of compile time constant bit * masks for each pixel depth and each possibility of swapped or not @@ -2885,7 +2880,7 @@ png_combine_row(png_structp png_ptr, png_bytep dp, int display) * the compiler even though it isn't used. Microsoft Visual C (various * versions) and the Intel C compiler are known to do this. To avoid * this the following macros are used in 1.5.6. This is a temporary - * solution to avoid destablizing the code during the release process. + * solution to avoid destabilizing the code during the release process. */ # if PNG_USE_COMPILE_TIME_MASKS # define PNG_LSR(x,s) ((x)>>((s) & 0x1f)) @@ -2930,7 +2925,7 @@ png_combine_row(png_structp png_ptr, png_bytep dp, int display) #if PNG_USE_COMPILE_TIME_MASKS /* Utility macros to construct all the masks for a depth/swap * combination. The 's' parameter says whether the format is PNG - * (big endian bytes) or not. Only the three odd numbered passes are + * (big endian bytes) or not. Only the three odd-numbered passes are * required for the display/block algorithm. */ # define S_MASKS(d,s) { S_MASK(0,d,s), S_MASK(1,d,s), S_MASK(2,d,s),\ @@ -2943,7 +2938,8 @@ png_combine_row(png_structp png_ptr, png_bytep dp, int display) /* Hence the pre-compiled masks indexed by PACKSWAP (or not), depth and * then pass: */ - static PNG_CONST png_uint_32 row_mask[2/*PACKSWAP*/][3/*depth*/][6] = { + static PNG_CONST png_uint_32 row_mask[2/*PACKSWAP*/][3/*depth*/][6] = + { /* Little-endian byte masks for PACKSWAP */ { S_MASKS(1,0), S_MASKS(2,0), S_MASKS(4,0) }, /* Normal (big-endian byte) masks - PNG format */ @@ -2953,7 +2949,8 @@ png_combine_row(png_structp png_ptr, png_bytep dp, int display) /* display_mask has only three entries for the odd passes, so index by * pass>>1. */ - static PNG_CONST png_uint_32 display_mask[2][3][3] = { + static PNG_CONST png_uint_32 display_mask[2][3][3] = + { /* Little-endian byte masks for PACKSWAP */ { B_MASKS(1,0), B_MASKS(2,0), B_MASKS(4,0) }, /* Normal (big-endian byte) masks - PNG format */ @@ -3129,7 +3126,7 @@ png_combine_row(png_structp png_ptr, png_bytep dp, int display) /* Check for double byte alignment and, if possible, use a * 16-bit copy. Don't attempt this for narrow images - ones that * are less than an interlace panel wide. Don't attempt it for - * wide bytes-to-copy either - use the png_memcpy there. + * wide bytes_to_copy either - use the png_memcpy there. */ if (bytes_to_copy < 16 /*else use png_memcpy*/ && png_isaligned(dp, png_uint_16) && @@ -3506,13 +3503,19 @@ png_read_filter_row_sub(png_row_infop row_info, png_bytep row, png_size_t istop = row_info->rowbytes; unsigned int bpp = (row_info->pixel_depth + 7) >> 3; png_bytep rp = row + bpp; +#if 0 png_bytep lp = row; +#endif PNG_UNUSED(prev_row) for (i = bpp; i < istop; i++) { +#if 0 *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff); +#else + *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); +#endif rp++; } } @@ -3540,7 +3543,9 @@ png_read_filter_row_avg(png_row_infop row_info, png_bytep row, png_size_t i; png_bytep rp = row; png_const_bytep pp = prev_row; +#if 0 png_bytep lp = row; +#endif unsigned int bpp = (row_info->pixel_depth + 7) >> 3; png_size_t istop = row_info->rowbytes - bpp; @@ -3555,7 +3560,11 @@ png_read_filter_row_avg(png_row_infop row_info, png_bytep row, for (i = 0; i < istop; i++) { *rp = (png_byte)(((int)(*rp) + +#if 0 (int)(*pp++ + *lp++) / 2 ) & 0xff); +#else + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); +#endif rp++; } diff --git a/pngwrite.c b/pngwrite.c index 107aa3443..76bdd791f 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -12,6 +12,9 @@ */ #include "pngpriv.h" +#if defined PNG_SIMPLIFIED_WRITE_SUPPORTED && defined PNG_STDIO_SUPPORTED +# include +#endif #ifdef PNG_WRITE_SUPPORTED @@ -1651,4 +1654,551 @@ png_write_png(png_structp png_ptr, png_infop info_ptr, PNG_UNUSED(params) } #endif + + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +#ifdef PNG_STDIO_SUPPORTED /* currently required for png_image_write_* */ +/* Initialize the write structure - general purpose utility. */ +static int +png_image_write_init(png_imagep image) +{ + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, image, + png_safe_error, png_safe_warning); + + if (png_ptr != NULL) + { + png_infop info_ptr = png_create_info_struct(png_ptr); + + if (info_ptr != NULL) + { + png_controlp control = png_malloc_warn(png_ptr, sizeof *control); + + if (control != NULL) + { + memset(control, 0, sizeof *control); + + control->png_ptr = png_ptr; + control->info_ptr = info_ptr; + control->for_write = 1; + + image->opaque = control; + return 1; + } + + /* Error clean up */ + png_destroy_info_struct(png_ptr, &info_ptr); + } + + png_destroy_write_struct(&png_ptr, NULL); + } + + return png_image_error(image, "png_image_read: out of memory"); +} + +/* Arguments to png_image_write_main: */ +typedef struct +{ + /* Arguments: */ + png_imagep image; + png_const_voidp buffer; + png_int_32 row_stride; + int convert_to_8bit; + /* Local variables: */ + png_const_voidp first_row; + ptrdiff_t row_bytes; + png_voidp local_row; +} png_image_write_control; + +/* Write png_uint_16 input to a 16-bit PNG, the png_ptr has already been set to + * do any necssary byte swapped. The component order is defined by the + * png_image format value. + */ +static int +png_write_image_16bit(png_voidp argument) +{ + png_image_write_control *display = argument; + png_imagep image = display->image; + png_structp png_ptr = image->opaque->png_ptr; + + png_const_uint_16p input_row = display->first_row; + png_uint_16p output_row = display->local_row; + png_uint_16p row_end; + int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1; + int aindex = 0; + png_uint_32 y = image->height; + + if (image->format & PNG_FORMAT_FLAG_ALPHA) + { + if (image->format & PNG_FORMAT_FLAG_AFIRST) + { + aindex = -1; + ++input_row; /* To point to the first component */ + ++output_row; + } + + else + aindex = channels; + } + + else + png_error(png_ptr, "png_write_image: internal call error"); + + /* Work out the output row end and count over this, note that the increment + * above to 'row' means that row_end can actually be beyond the end of the + * row, this is correct. + */ + row_end = output_row + image->width * (channels+1); + + while (y-- > 0) + { + png_const_uint_16p in_ptr = input_row; + png_uint_16p out_ptr = output_row; + + while (out_ptr < row_end) + { + png_uint_16 alpha = in_ptr[aindex]; + png_uint_32 reciprocal = 0; + int c; + + out_ptr[aindex] = alpha; + + /* Calculate a reciprocal. The correct calculation is simply + * component/alpha*65535 << 15. (I.e. 15 bits of precision), this + * allows correct rounding by adding .5 before the shift. 'reciprocal' + * is only initialized when required. + */ + if (alpha > 0 && alpha < 65535) + reciprocal = ((0xffff<<15)+(alpha>>1))/alpha; + + c = channels; + do /* always at least one channel */ + { + png_uint_16 component = *in_ptr++; + + /* The following gives 65535 for an alpha of 0, which is fine, + * otherwise if 0/0 is represented as some other value there is more + * likely to be a discontinuity which will probably damage + * compression when moving from a fully transparent area to a + * nearly transparent one. (The assumption here is that opaque + * areas tend not to be 0 intensity.) + */ + if (component >= alpha) + component = 65535; + + /* component 0 && alpha < 65535) + { + png_uint_32 calc = component * reciprocal; + calc += 16384; /* round to nearest */ + component = (png_uint_16)(calc >> 15); + } + + *out_ptr++ = component; + } + while (--c > 0); + + /* Skip to next component (skip the intervening alpha channel) */ + ++in_ptr; + ++out_ptr; + } + + png_write_row(png_ptr, (png_bytep)output_row); + input_row += display->row_bytes/(sizeof (png_uint_16)); + } + + return 1; +} + +/* Given 16-bit input (1 to 4 channels) write 8-bit output. If an alpha channel + * is present it must be removed from the components, the components are then + * written in sRGB encoding. No components are added or removed. + */ +static int +png_write_image_8bit(png_voidp argument) +{ + png_image_write_control *display = argument; + png_imagep image = display->image; + png_structp png_ptr = image->opaque->png_ptr; + + png_const_uint_16p input_row = display->first_row; + png_bytep output_row = display->local_row; + png_uint_32 y = image->height; + int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1; + + if (image->format & PNG_FORMAT_FLAG_ALPHA) + { + png_bytep row_end; + int aindex; + + if (image->format & PNG_FORMAT_FLAG_AFIRST) + { + aindex = -1; + ++input_row; /* To point to the first component */ + ++output_row; + } + + else + aindex = channels; + + /* Use row_end in place of a loop counter: */ + row_end = output_row + image->width * (channels+1); + + while (y-- > 0) + { + png_const_uint_16p in_ptr = input_row; + png_bytep out_ptr = output_row; + + if (aindex != 0) while (out_ptr < row_end) /* Alpha channel case */ + { + png_uint_16 alpha = in_ptr[aindex]; + png_uint_32 reciprocal = 0; + int c; + + /* Scale and write the alpha channel. See pngrtran.c + * png_do_scale_16_to_8 for a discussion of this calculation. The + * code here has machine native values, so use: + * + * (V * 255 + 32895) >> 16 + */ + out_ptr[aindex] = (png_byte)((alpha * 255 + 32895) >> 16); + + /* Calculate a reciprocal. As above the calculation can be done to + * 15 bits of accuracy, however the output needs to be scaled in the + * range 0..255*65535, so include that scaling here. + */ + if (alpha > 0 && alpha < 65535) + reciprocal = (((0xffff*0xff)<<7)+(alpha>>1))/alpha; + + c = channels; + do /* always at least one channel */ + { + /* Need 32 bit accuracy in the sRGB tables */ + png_uint_32 component = *in_ptr++; + + /* The following gives 65535 for an alpha of 0, which is fine, + * otherwise if 0/0 is represented as some other value there is + * more likely to be a discontinuity which will probably damage + * compression when moving from a fully transparent area to a + * nearly transparent one. (The assumption here is that opaque + * areas tend not to be 0 intensity.) + */ + if (component >= alpha) + *out_ptr++ = 255; + + /* component 0 && alpha < 65535) + { + component *= reciprocal; + component += 64; /* round to nearest */ + component >>= 7; + + /* Convert the component to sRGB. */ + *out_ptr++ = (png_byte)PNG_sRGB_FROM_LINEAR(component); + } + + else + *out_ptr++ = 0; + } + while (--c > 0); + + /* Skip to next component (skip the intervening alpha channel) */ + ++in_ptr; + ++out_ptr; + } /* while out_ptr < row_end */ + } /* while y */ + } + + else + { + /* No alpha channel, so the row_end really is the end of the row and it + * is sufficient to loop over the components one by one. + */ + png_bytep row_end = output_row + image->width * channels; + + while (y-- > 0) + { + png_const_uint_16p in_ptr = input_row; + png_bytep out_ptr = output_row; + + while (out_ptr < row_end) + { + png_uint_32 component = *in_ptr++; + + component *= 255; + *out_ptr++ = (png_byte)PNG_sRGB_FROM_LINEAR(component); + } + + png_write_row(png_ptr, output_row); + input_row += display->row_bytes/(sizeof (png_uint_16)); + } + } + + return 1; +} + +static int +png_image_write_main(png_voidp argument) +{ + png_image_write_control *display = argument; + png_imagep image = display->image; + png_structp png_ptr = image->opaque->png_ptr; + png_infop info_ptr = image->opaque->info_ptr; + png_uint_32 format = image->format; + + int linear = (format & PNG_FORMAT_FLAG_LINEAR) != 0; /* input */ + int alpha = (format & PNG_FORMAT_FLAG_ALPHA) != 0; + int write_16bit = linear && !display->convert_to_8bit; + + /* Set the required transforms then write the rows in the correct order. */ + png_set_IHDR(png_ptr, info_ptr, image->width, image->height, + write_16bit ? 16 : 8, + ((format & PNG_FORMAT_FLAG_COLOR) ? PNG_COLOR_MASK_COLOR : 0) + + ((format & PNG_FORMAT_FLAG_ALPHA) ? PNG_COLOR_MASK_ALPHA : 0), + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + /* Counter-intuitively the data transformations must be called *after* + * png_write_info, not before as in the read code, but the 'set' functions + * must still be called before. Just set the color space information, never + * write an interlaced image. + */ + if (write_16bit) + { + /* The gamma here is 1.0 (linear) and the cHRM chunk matches sRGB. */ + png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_LINEAR); + png_set_cHRM_fixed(png_ptr, info_ptr, + /* color x y */ + /* white */ 31270, 32900, + /* red */ 64000, 33000, + /* green */ 30000, 60000, + /* blue */ 15000, 6000 + ); + } + + else + png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL); + + /* Write the file header. */ + png_write_info(png_ptr, info_ptr); + + /* Now set up the data transformations (*after* the header is written), + * remove the handled transformations from the 'format' flags for checking. + */ + format &= ~(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR | + PNG_FORMAT_FLAG_ALPHA); + + /* Check for a little endian system if writing 16 bit files. */ + if (write_16bit) + { + PNG_CONST png_uint_16 le = 0x0001; + + if (*(png_const_bytep)&le) + png_set_swap(png_ptr); + } + +# ifdef PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED + if (format & PNG_FORMAT_FLAG_BGR) + { + png_set_bgr(png_ptr); + format &= ~PNG_FORMAT_FLAG_BGR; + } +# endif + +# ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED + if (format & PNG_FORMAT_FLAG_AFIRST) + { + png_set_swap_alpha(png_ptr); + format &= ~PNG_FORMAT_FLAG_AFIRST; + } +# endif + + /* That should have handled all the transforms. */ + if (format != 0) + png_error(png_ptr, "png_write_image: unsupported transformation"); + + { + png_const_bytep row = display->buffer; + ptrdiff_t row_bytes = display->row_stride; + + if (linear) + row_bytes *= sizeof (png_uint_16); + + if (row_bytes < 0) + row += (image->height-1) * (-row_bytes); + + display->first_row = row; + display->row_bytes = row_bytes; + } + + /* Check for the cases that currently require a pre-transform on the row + * before it is written. This only applies when the input is 16-bit and + * either there is an alpha channel or it is converted to 8-bit. + */ + if ((linear && alpha) || display->convert_to_8bit) + { + png_bytep row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); + int result; + + display->local_row = row; + if (write_16bit) + result = png_safe_execute(image, png_write_image_16bit, display); + else + result = png_safe_execute(image, png_write_image_8bit, display); + display->local_row = NULL; + + png_free(png_ptr, row); + + /* Skip the 'write_end' on error: */ + if (!result) + return 0; + } + + /* Otherwise this is the case where the input is in a format currently + * supported by the rest of the libpng write code; call it directly. + */ + else + { + png_const_bytep row = display->first_row; + ptrdiff_t row_bytes = display->row_bytes; + png_uint_32 y = image->height; + + while (y-- > 0) + { + png_write_row(png_ptr, row); + row += row_bytes; + } + } + + png_write_end(png_ptr, info_ptr); + return 1; +} + +int PNGAPI +png_image_write_to_stdio (png_imagep image, FILE *file, int convert_to_8bit, + const void *buffer, png_int_32 row_stride) +{ + /* Write the image to the given (FILE*). */ + if (image != NULL) + { + if (file != NULL) + { + if (png_image_write_init(image)) + { + png_image_write_control display; + int result; + + /* This is slightly evil, but png_init_io doesn't do anything other + * than this and we haven't changed the standard IO functions so + * this saves a 'safe' function. + */ + image->opaque->png_ptr->io_ptr = file; + + memset(&display, 0, sizeof display); + display.image = image; + display.buffer = buffer; + display.row_stride = row_stride; + display.convert_to_8bit = convert_to_8bit; + + result = png_safe_execute(image, png_image_write_main, &display); + png_image_free(image); + return result; + } + + else + return 0; + } + + else + return png_image_error(image, + "png_image_write_to_stdio: invalid argument"); + } + + else + return 0; +} + +int PNGAPI +png_image_write_to_file (png_imagep image, const char *file_name, + int convert_to_8bit, const void *buffer, png_int_32 row_stride) +{ + /* Write the image to the named file. */ + if (image != NULL) + { + if (file_name != NULL) + { + FILE *fp = fopen(file_name, "wb"); + + if (fp != NULL) + { + if (png_image_write_init(image)) + { + png_image_write_control display; + + image->opaque->png_ptr->io_ptr = fp; + image->opaque->owned_file = 1; + /* No need to close this file now - png_image_free will do that. + */ + + memset(&display, 0, sizeof display); + display.image = image; + display.buffer = buffer; + display.row_stride = row_stride; + display.convert_to_8bit = convert_to_8bit; + + if (png_safe_execute(image, png_image_write_main, &display)) + { + int error; /* from fflush/fclose */ + + /* Make sure the file is flushed correctly. */ + if (fflush(fp) == 0) + { + /* Steal the file pointer back to make sure it closes ok. + */ + image->opaque->png_ptr->io_ptr = NULL; + image->opaque->owned_file = 0; + + if (fclose(fp) == 0) + { + png_image_free(image); + return 1; + } + + error = errno; + } + + else + error = errno; + + return png_image_error(image, strerror(error)); + } + + else /* else cleanup has already happened */ + return 0; + } + + else + { + /* Clean up: just the opened file. */ + (void)fclose(fp); + return 0; + } + } + + else + return png_image_error(image, strerror(errno)); + } + + else + return png_image_error(image, + "png_image_write_to_file: invalid argument"); + } + + else + return 0; +} +#endif /* PNG_STDIO_SUPPORTED */ +#endif /* SIMPLIFIED_WRITE */ #endif /* PNG_WRITE_SUPPORTED */ diff --git a/scripts/pnglibconf.dfa b/scripts/pnglibconf.dfa index 07b7f6927..548c6610b 100644 --- a/scripts/pnglibconf.dfa +++ b/scripts/pnglibconf.dfa @@ -6,7 +6,7 @@ # com pnglibconf.h - library build configuration com -com libpng version 1.5.3 - July 7, 2011 +version com com Copyright (c) 1998-2011 Glenn Randers-Pehrson com @@ -576,3 +576,31 @@ option WRITE_COMPRESSED_TEXT enables WRITE_TEXT # leave the row_pointers member out of the info structure. option INFO_IMAGE + +# Simplified API options +# Read: +option SIMPLIFIED_READ requires SEQUENTIAL_READ READ_TRANSFORMS SETJMP +option SIMPLIFIED_READ enables READ_EXPAND READ_16BIT READ_EXPAND_16 +option SIMPLIFIED_READ enables READ_SCALE_16_TO_8 READ_RGB_TO_GRAY +option SIMPLIFIED_READ enables READ_ALPHA_MODE READ_BACKGROUND READ_STRIP_ALPHA +option SIMPLIFIED_READ enables READ_FILLER READ_SWAP + +option SIMPLIFIED_READ_AFIRST requires SIMPLIFIED_READ disabled +option READ_SWAP_ALPHA enables SIMPLIFIED_READ_AFIRST + +option SIMPLIFIED_READ_BGR requires SIMPLIFIED_READ disabled +option READ_BGR enables SIMPLIFIED_READ_BGR + +# Write: +option SIMPLIFIED_WRITE requires WRITE STDIO SETJMP +option SIMPLIFIED_WRITE enables WRITE_SWAP WRITE_gAMA WRITE_sRGB WRITE_cHRM + +option SIMPLIFIED_WRITE_AFIRST requires SIMPLIFIED_WRITE disabled +option WRITE_SWAP_ALPHA enables SIMPLIFIED_WRITE_AFIRST + +option SIMPLIFIED_WRITE_BGR requires SIMPLIFIED_WRITE disabled +option WRITE_BGR enables SIMPLIFIED_WRITE_BGR + +# Formats: +option FORMAT_AFIRST if SIMPLIFIED_READ_AFIRST SIMPLIFIED_WRITE_AFIRST +option FORMAT_BGR if SIMPLIFIED_READ_BGR SIMPLIFIED_WRITE_BGR diff --git a/scripts/pnglibconf.h.prebuilt b/scripts/pnglibconf.h.prebuilt index a8e12b043..5f2c5be3a 100644 --- a/scripts/pnglibconf.h.prebuilt +++ b/scripts/pnglibconf.h.prebuilt @@ -3,7 +3,7 @@ /* pnglibconf.h - library build configuration */ -/* libpng version 1.5.7beta02 - November 8, 2011 */ +/* Libpng 1.5.7beta02 - November 8, 2011 */ /* Copyright (c) 1998-2011 Glenn Randers-Pehrson */ @@ -54,6 +54,8 @@ #define PNG_FIXED_POINT_SUPPORTED #define PNG_FLOATING_ARITHMETIC_SUPPORTED #define PNG_FLOATING_POINT_SUPPORTED +#define PNG_FORMAT_AFIRST_SUPPORTED +#define PNG_FORMAT_BGR_SUPPORTED #define PNG_gAMA_SUPPORTED #define PNG_HANDLE_AS_UNKNOWN_SUPPORTED #define PNG_hIST_SUPPORTED @@ -126,6 +128,12 @@ #define PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED #define PNG_SETJMP_SUPPORTED #define PNG_SET_USER_LIMITS_SUPPORTED +#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED +#define PNG_SIMPLIFIED_READ_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_SUPPORTED #define PNG_sPLT_SUPPORTED #define PNG_sRGB_SUPPORTED #define PNG_STDIO_SUPPORTED diff --git a/scripts/symbols.def b/scripts/symbols.def index 39466d46a..564254f10 100644 --- a/scripts/symbols.def +++ b/scripts/symbols.def @@ -239,3 +239,10 @@ EXPORTS png_get_cHRM_XYZ_fixed @231 png_set_cHRM_XYZ @232 png_set_cHRM_XYZ_fixed @233 + png_image_begin_read_from_file @234 + png_image_begin_read_from_stdio @235 + png_image_begin_read_from_memory @236 + png_image_finish_read @237 + png_image_free @238 + png_image_write_to_file @239 + png_image_write_to_stdio @240