1 /* makepng.c
2  *
3  * Copyright (c) 2013 John Cunningham Bowler
4  *
5  * Last changed in libpng 1.6.1 [March 28, 2013]
6  *
7  * This code is released under the libpng license.
8  * For conditions of distribution and use, see the disclaimer
9  * and license in png.h
10  *
11  * Make a test PNG image.  The arguments are as follows:
12  *
13  *  makepng [--sRGB|--linear|--1.8] [--color=<color>] color-type bit-depth \
14  *      [file-name]
15  *
16  * The color-type may be numeric (and must match the numbers used by the PNG
17  * specification) or one of the format names listed below.  The bit-depth is the
18  * component bit depth, or the pixel bit-depth for a color-mapped image.
19  *
20  * Without any options no color-space information is written, with the options
21  * an sRGB or the appropriate gAMA chunk is written.  "1.8" refers to the
22  * display system used on older Apple computers to correct for high ambient
23  * light levels in the viewing environment; it applies a transform of
24  * approximately value^(1/1.45) to the color values and so a gAMA chunk of 65909
25  * is written (1.45/2.2).
26  *
27  * The image data is generated internally.  Unless --color is given the images
28  * used are as follows:
29  *
30  * 1 channel: a square image with a diamond, the least luminous colors are on
31  *    the edge of the image, the most luminous in the center.
32  *
33  * 2 channels: the color channel increases in luminosity from top to bottom, the
34  *    alpha channel increases in opacity from left to right.
35  *
36  * 3 channels: linear combinations of, from the top-left corner clockwise,
37  *    black, green, white, red.
38  *
39  * 4 channels: linear combinations of, from the top-left corner clockwise,
40  *    transparent, red, green, blue.
41  *
42  * For color-mapped images a four channel color-map is used and the PNG file has
43  * a tRNS chunk, as follows:
44  *
45  * 1-bit: entry 0 is transparent-red, entry 1 is opaque-white
46  * 2-bit: entry 0: transparent-green
47  *        entry 1: 40%-red
48  *        entry 2: 80%-blue
49  *        entry 3: opaque-white
50  * 4-bit: the 16 combinations of the 2-bit case
51  * 8-bit: the 256 combinations of the 4-bit case
52  *
53  * The palette always has 2^bit-depth entries and the tRNS chunk one fewer.  The
54  * image is the 1-channel diamond, but using palette index, not luminosity.
55  *
56  * Image size is determined by the final pixel depth in bits, i.e. channels x
57  * bit-depth, as follows:
58  *
59  * 8 bits or less:    64x64
60  * 16 bits:           256x256
61  * More than 16 bits: 1024x1024
62  *
63  * Row filtering is turned off (the 'none' filter is used on every row) and the
64  * images are not interlaced.
65  *
66  * If --color is given then the whole image has that color, color-mapped images
67  * will have exactly one palette entry and all image files with be 16x16 in
68  * size.  The color value is 1 to 4 decimal numbers as appropriate for the color
69  * type.
70  *
71  * If file-name is given then the PNG is written to that file, else it is
72  * written to stdout.  Notice that stdout is not supported on systems where, by
73  * default, it assumes text output; this program makes no attempt to change the
74  * text mode of stdout!
75  */
76 #define _ISOC99_SOURCE /* for strtoull */
77 
78 #include <stddef.h> /* for offsetof */
79 #include <stdlib.h>
80 #include <stdio.h>
81 #include <string.h>
82 #include <ctype.h>
83 #include <math.h>
84 #include <errno.h>
85 
86 #if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
87 #  include <config.h>
88 #endif
89 
90 /* Define the following to use this test against your installed libpng, rather
91  * than the one being built here:
92  */
93 #ifdef PNG_FREESTANDING_TESTS
94 #  include <png.h>
95 #else
96 #  include "../../png.h"
97 #endif
98 
99 /* This structure is used for inserting extra chunks (the --insert argument, not
100  * documented above.)
101  */
102 typedef struct chunk_insert
103 {
104    struct chunk_insert *next;
105    void               (*insert)(png_structp, png_infop, int, png_charpp);
106    int                  nparams;
107    png_charp            parameters[1];
108 } chunk_insert;
109 
110 static int
channels_of_type(int color_type)111 channels_of_type(int color_type)
112 {
113    if (color_type & PNG_COLOR_MASK_PALETTE)
114       return 1;
115 
116    else
117    {
118       int channels = 1;
119 
120       if (color_type & PNG_COLOR_MASK_COLOR)
121          channels = 3;
122 
123       if (color_type & PNG_COLOR_MASK_ALPHA)
124          return channels + 1;
125 
126       else
127          return channels;
128    }
129 }
130 
131 static int
pixel_depth_of_type(int color_type,int bit_depth)132 pixel_depth_of_type(int color_type, int bit_depth)
133 {
134    return channels_of_type(color_type) * bit_depth;
135 }
136 
137 static unsigned int
image_size_of_type(int color_type,int bit_depth,unsigned int * colors)138 image_size_of_type(int color_type, int bit_depth, unsigned int *colors)
139 {
140    if (*colors)
141       return 16;
142 
143    else
144    {
145       int pixel_depth = pixel_depth_of_type(color_type, bit_depth);
146 
147       if (pixel_depth < 8)
148          return 64;
149 
150       else if (pixel_depth > 16)
151          return 1024;
152 
153       else
154          return 256;
155    }
156 }
157 
158 static void
set_color(png_colorp color,png_bytep trans,unsigned int red,unsigned int green,unsigned int blue,unsigned int alpha,png_const_bytep gamma_table)159 set_color(png_colorp color, png_bytep trans, unsigned int red,
160    unsigned int green, unsigned int blue, unsigned int alpha,
161    png_const_bytep gamma_table)
162 {
163    color->red = gamma_table[red];
164    color->green = gamma_table[green];
165    color->blue = gamma_table[blue];
166    *trans = (png_byte)alpha;
167 }
168 
169 static int
generate_palette(png_colorp palette,png_bytep trans,int bit_depth,png_const_bytep gamma_table,unsigned int * colors)170 generate_palette(png_colorp palette, png_bytep trans, int bit_depth,
171    png_const_bytep gamma_table, unsigned int *colors)
172 {
173    /*
174     * 1-bit: entry 0 is transparent-red, entry 1 is opaque-white
175     * 2-bit: entry 0: transparent-green
176     *        entry 1: 40%-red
177     *        entry 2: 80%-blue
178     *        entry 3: opaque-white
179     * 4-bit: the 16 combinations of the 2-bit case
180     * 8-bit: the 256 combinations of the 4-bit case
181     */
182    switch (colors[0])
183    {
184       default:
185          fprintf(stderr, "makepng: --colors=...: invalid count %u\n",
186             colors[0]);
187          exit(1);
188 
189       case 1:
190          set_color(palette+0, trans+0, colors[1], colors[1], colors[1], 255,
191             gamma_table);
192          return 1;
193 
194       case 2:
195          set_color(palette+0, trans+0, colors[1], colors[1], colors[1],
196             colors[2], gamma_table);
197          return 1;
198 
199       case 3:
200          set_color(palette+0, trans+0, colors[1], colors[2], colors[3], 255,
201             gamma_table);
202          return 1;
203 
204       case 4:
205          set_color(palette+0, trans+0, colors[1], colors[2], colors[3],
206             colors[4], gamma_table);
207          return 1;
208 
209       case 0:
210          if (bit_depth == 1)
211          {
212             set_color(palette+0, trans+0, 255, 0, 0, 0, gamma_table);
213             set_color(palette+1, trans+1, 255, 255, 255, 255, gamma_table);
214             return 2;
215          }
216 
217          else
218          {
219             unsigned int size = 1U << (bit_depth/2); /* 2, 4 or 16 */
220             unsigned int x, y, ip;
221 
222             for (x=0; x<size; ++x) for (y=0; y<size; ++y)
223             {
224                ip = x + (size * y);
225 
226                /* size is at most 16, so the scaled value below fits in 16 bits
227                 */
228 #              define interp(pos, c1, c2) ((pos * c1) + ((size-pos) * c2))
229 #              define xyinterp(x, y, c1, c2, c3, c4) (((size * size / 2) +\
230                   (interp(x, c1, c2) * y + (size-y) * interp(x, c3, c4))) /\
231                   (size*size))
232 
233                set_color(palette+ip, trans+ip,
234                   /* color:    green, red,blue,white */
235                   xyinterp(x, y,   0, 255,   0, 255),
236                   xyinterp(x, y, 255,   0,   0, 255),
237                   xyinterp(x, y,   0,   0, 255, 255),
238                   /* alpha:        0, 102, 204, 255) */
239                   xyinterp(x, y,   0, 102, 204, 255),
240                   gamma_table);
241             }
242 
243             return ip+1;
244          }
245    }
246 }
247 
248 static void
set_value(png_bytep row,size_t rowbytes,png_uint_32 x,unsigned int bit_depth,png_uint_32 value,png_const_bytep gamma_table,double conv)249 set_value(png_bytep row, size_t rowbytes, png_uint_32 x, unsigned int bit_depth,
250    png_uint_32 value, png_const_bytep gamma_table, double conv)
251 {
252    unsigned int mask = (1U << bit_depth)-1;
253 
254    x *= bit_depth;  /* Maximum x is 4*1024, maximum bit_depth is 16 */
255 
256    if (value <= mask)
257    {
258       png_uint_32 offset = x >> 3;
259 
260       if (offset < rowbytes && (bit_depth < 16 || offset+1 < rowbytes))
261       {
262          row += offset;
263 
264          switch (bit_depth)
265          {
266             case 1:
267             case 2:
268             case 4:
269                /* Don't gamma correct - values get smashed */
270                {
271                   unsigned int shift = (8 - bit_depth) - (x & 0x7U);
272 
273                   mask <<= shift;
274                   value = (value << shift) & mask;
275                   *row = (png_byte)((*row & ~mask) | value);
276                }
277                return;
278 
279             default:
280                fprintf(stderr, "makepng: bad bit depth (internal error)\n");
281                exit(1);
282 
283             case 16:
284                value = (unsigned int)floor(65535*pow(value/65535.,conv)+.5);
285                *row++ = (png_byte)(value >> 8);
286                *row = (png_byte)value;
287                return;
288 
289             case 8:
290                *row = gamma_table[value];
291                return;
292          }
293       }
294 
295       else
296       {
297          fprintf(stderr, "makepng: row buffer overflow (internal error)\n");
298          exit(1);
299       }
300    }
301 
302    else
303    {
304       fprintf(stderr, "makepng: component overflow (internal error)\n");
305       exit(1);
306    }
307 }
308 
309 static void
generate_row(png_bytep row,size_t rowbytes,unsigned int y,int color_type,int bit_depth,png_const_bytep gamma_table,double conv,unsigned int * colors)310 generate_row(png_bytep row, size_t rowbytes, unsigned int y, int color_type,
311    int bit_depth, png_const_bytep gamma_table, double conv,
312    unsigned int *colors)
313 {
314    png_uint_32 size_max = image_size_of_type(color_type, bit_depth, colors)-1;
315    png_uint_32 depth_max = (1U << bit_depth)-1; /* up to 65536 */
316 
317    if (colors[0] == 0) switch (channels_of_type(color_type))
318    {
319    /* 1 channel: a square image with a diamond, the least luminous colors are on
320     *    the edge of the image, the most luminous in the center.
321     */
322       case 1:
323          {
324             png_uint_32 x;
325             png_uint_32 base = 2*size_max - abs(2*y-size_max);
326 
327             for (x=0; x<=size_max; ++x)
328             {
329                png_uint_32 luma = base - abs(2*x-size_max);
330 
331                /* 'luma' is now in the range 0..2*size_max, we need
332                 * 0..depth_max
333                 */
334                luma = (luma*depth_max + size_max) / (2*size_max);
335                set_value(row, rowbytes, x, bit_depth, luma, gamma_table, conv);
336             }
337          }
338          break;
339 
340    /* 2 channels: the color channel increases in luminosity from top to bottom,
341     *    the alpha channel increases in opacity from left to right.
342     */
343       case 2:
344          {
345             png_uint_32 alpha = (depth_max * y * 2 + size_max) / (2 * size_max);
346             png_uint_32 x;
347 
348             for (x=0; x<=size_max; ++x)
349             {
350                set_value(row, rowbytes, 2*x, bit_depth,
351                   (depth_max * x * 2 + size_max) / (2 * size_max), gamma_table,
352                   conv);
353                set_value(row, rowbytes, 2*x+1, bit_depth, alpha, gamma_table,
354                   conv);
355             }
356          }
357          break;
358 
359    /* 3 channels: linear combinations of, from the top-left corner clockwise,
360     *    black, green, white, red.
361     */
362       case 3:
363          {
364             /* x0: the black->red scale (the value of the red component) at the
365              *     start of the row (blue and green are 0).
366              * x1: the green->white scale (the value of the red and blue
367              *     components at the end of the row; green is depth_max).
368              */
369             png_uint_32 Y = (depth_max * y * 2 + size_max) / (2 * size_max);
370             png_uint_32 x;
371 
372             /* Interpolate x/depth_max from start to end:
373              *
374              *        start end         difference
375              * red:     Y    Y            0
376              * green:   0   depth_max   depth_max
377              * blue:    0    Y            Y
378              */
379             for (x=0; x<=size_max; ++x)
380             {
381                set_value(row, rowbytes, 3*x+0, bit_depth, /* red */ Y,
382                      gamma_table, conv);
383                set_value(row, rowbytes, 3*x+1, bit_depth, /* green */
384                   (depth_max * x * 2 + size_max) / (2 * size_max),
385                   gamma_table, conv);
386                set_value(row, rowbytes, 3*x+2, bit_depth, /* blue */
387                   (Y * x * 2 + size_max) / (2 * size_max),
388                   gamma_table, conv);
389             }
390          }
391          break;
392 
393    /* 4 channels: linear combinations of, from the top-left corner clockwise,
394     *    transparent, red, green, blue.
395     */
396       case 4:
397          {
398             /* x0: the transparent->blue scale (the value of the blue and alpha
399              *     components) at the start of the row (red and green are 0).
400              * x1: the red->green scale (the value of the red and green
401              *     components at the end of the row; blue is 0 and alpha is
402              *     depth_max).
403              */
404             png_uint_32 Y = (depth_max * y * 2 + size_max) / (2 * size_max);
405             png_uint_32 x;
406 
407             /* Interpolate x/depth_max from start to end:
408              *
409              *        start    end       difference
410              * red:     0   depth_max-Y depth_max-Y
411              * green:   0       Y             Y
412              * blue:    Y       0            -Y
413              * alpha:   Y    depth_max  depth_max-Y
414              */
415             for (x=0; x<=size_max; ++x)
416             {
417                set_value(row, rowbytes, 4*x+0, bit_depth, /* red */
418                   ((depth_max-Y) * x * 2 + size_max) / (2 * size_max),
419                   gamma_table, conv);
420                set_value(row, rowbytes, 4*x+1, bit_depth, /* green */
421                   (Y * x * 2 + size_max) / (2 * size_max),
422                   gamma_table, conv);
423                set_value(row, rowbytes, 4*x+2, bit_depth, /* blue */
424                   Y - (Y * x * 2 + size_max) / (2 * size_max),
425                   gamma_table, conv);
426                set_value(row, rowbytes, 4*x+3, bit_depth, /* alpha */
427                   Y + ((depth_max-Y) * x * 2 + size_max) / (2 * size_max),
428                   gamma_table, conv);
429             }
430          }
431          break;
432 
433       default:
434          fprintf(stderr, "makepng: internal bad channel count\n");
435          exit(2);
436    }
437 
438    else if (color_type & PNG_COLOR_MASK_PALETTE)
439    {
440       /* Palette with fixed color: the image rows are all 0 and the image width
441        * is 16.
442        */
443       memset(row, 0, rowbytes);
444    }
445 
446    else if (colors[0] == channels_of_type(color_type))
447       switch (channels_of_type(color_type))
448       {
449          case 1:
450             {
451                const png_uint_32 luma = colors[1];
452                png_uint_32 x;
453 
454                for (x=0; x<=size_max; ++x)
455                   set_value(row, rowbytes, x, bit_depth, luma, gamma_table,
456                      conv);
457             }
458             break;
459 
460          case 2:
461             {
462                const png_uint_32 luma = colors[1];
463                const png_uint_32 alpha = colors[2];
464                png_uint_32 x;
465 
466                for (x=0; x<size_max; ++x)
467                {
468                   set_value(row, rowbytes, 2*x, bit_depth, luma, gamma_table,
469                      conv);
470                   set_value(row, rowbytes, 2*x+1, bit_depth, alpha, gamma_table,
471                      conv);
472                }
473             }
474             break;
475 
476          case 3:
477             {
478                const png_uint_32 red = colors[1];
479                const png_uint_32 green = colors[2];
480                const png_uint_32 blue = colors[3];
481                png_uint_32 x;
482 
483                for (x=0; x<=size_max; ++x)
484                {
485                   set_value(row, rowbytes, 3*x+0, bit_depth, red, gamma_table,
486                      conv);
487                   set_value(row, rowbytes, 3*x+1, bit_depth, green, gamma_table,
488                      conv);
489                   set_value(row, rowbytes, 3*x+2, bit_depth, blue, gamma_table,
490                      conv);
491                }
492             }
493             break;
494 
495          case 4:
496             {
497                const png_uint_32 red = colors[1];
498                const png_uint_32 green = colors[2];
499                const png_uint_32 blue = colors[3];
500                const png_uint_32 alpha = colors[4];
501                png_uint_32 x;
502 
503                for (x=0; x<=size_max; ++x)
504                {
505                   set_value(row, rowbytes, 4*x+0, bit_depth, red, gamma_table,
506                      conv);
507                   set_value(row, rowbytes, 4*x+1, bit_depth, green, gamma_table,
508                      conv);
509                   set_value(row, rowbytes, 4*x+2, bit_depth, blue, gamma_table,
510                      conv);
511                   set_value(row, rowbytes, 4*x+3, bit_depth, alpha, gamma_table,
512                      conv);
513                }
514             }
515          break;
516 
517          default:
518             fprintf(stderr, "makepng: internal bad channel count\n");
519             exit(2);
520       }
521 
522    else
523    {
524       fprintf(stderr,
525          "makepng: --color: count(%u) does not match channels(%u)\n",
526          colors[0], channels_of_type(color_type));
527       exit(1);
528    }
529 }
530 
531 
532 static void PNGCBAPI
makepng_warning(png_structp png_ptr,png_const_charp message)533 makepng_warning(png_structp png_ptr, png_const_charp message)
534 {
535    const char **ep = png_get_error_ptr(png_ptr);
536    const char *name;
537 
538    if (ep != NULL && *ep != NULL)
539       name = *ep;
540 
541    else
542       name = "makepng";
543 
544   fprintf(stderr, "%s: warning: %s\n", name, message);
545 }
546 
547 static void PNGCBAPI
makepng_error(png_structp png_ptr,png_const_charp message)548 makepng_error(png_structp png_ptr, png_const_charp message)
549 {
550    makepng_warning(png_ptr, message);
551    png_longjmp(png_ptr, 1);
552 }
553 
554 static int /* 0 on success, else an error code */
write_png(const char ** name,FILE * fp,int color_type,int bit_depth,volatile png_fixed_point gamma,chunk_insert * volatile insert,unsigned int filters,unsigned int * colors)555 write_png(const char **name, FILE *fp, int color_type, int bit_depth,
556    volatile png_fixed_point gamma, chunk_insert * volatile insert,
557    unsigned int filters, unsigned int *colors)
558 {
559    png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
560       name, makepng_error, makepng_warning);
561    volatile png_infop info_ptr = NULL;
562    volatile png_bytep row = NULL;
563 
564    if (png_ptr == NULL)
565    {
566       fprintf(stderr, "makepng: OOM allocating write structure\n");
567       return 1;
568    }
569 
570    if (setjmp(png_jmpbuf(png_ptr)))
571    {
572       png_structp nv_ptr = png_ptr;
573       png_infop nv_info = info_ptr;
574 
575       png_ptr = NULL;
576       info_ptr = NULL;
577       png_destroy_write_struct(&nv_ptr, &nv_info);
578       if (row != NULL) free(row);
579       return 1;
580    }
581 
582    /* Allow benign errors so that we can write PNGs with errors */
583    png_set_benign_errors(png_ptr, 1/*allowed*/);
584    png_init_io(png_ptr, fp);
585 
586    info_ptr = png_create_info_struct(png_ptr);
587    if (info_ptr == NULL)
588       png_error(png_ptr, "OOM allocating info structure");
589 
590    {
591       unsigned int size = image_size_of_type(color_type, bit_depth, colors);
592       png_fixed_point real_gamma = 45455; /* For sRGB */
593       png_byte gamma_table[256];
594       double conv;
595 
596       /* This function uses the libpng values used on read to carry extra
597        * information about the gamma:
598        */
599       if (gamma == PNG_GAMMA_MAC_18)
600          gamma = 65909;
601 
602       else if (gamma > 0 && gamma < 1000)
603          gamma = PNG_FP_1;
604 
605       if (gamma > 0)
606          real_gamma = gamma;
607 
608       {
609          unsigned int i;
610 
611          if (real_gamma == 45455) for (i=0; i<256; ++i)
612          {
613             gamma_table[i] = (png_byte)i;
614             conv = 1.;
615          }
616 
617          else
618          {
619             /* Convert 'i' from sRGB (45455) to real_gamma, this makes
620              * the images look the same regardless of the gAMA chunk.
621              */
622             conv = real_gamma;
623             conv /= 45455;
624 
625             gamma_table[0] = 0;
626 
627             for (i=1; i<255; ++i)
628                gamma_table[i] = (png_byte)floor(pow(i/255.,conv) * 255 + .5);
629 
630             gamma_table[255] = 255;
631          }
632       }
633 
634       png_set_IHDR(png_ptr, info_ptr, size, size, bit_depth, color_type,
635          PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
636 
637       if (color_type & PNG_COLOR_MASK_PALETTE)
638       {
639          int npalette;
640          png_color palette[256];
641          png_byte trans[256];
642 
643          npalette = generate_palette(palette, trans, bit_depth, gamma_table,
644             colors);
645          png_set_PLTE(png_ptr, info_ptr, palette, npalette);
646          png_set_tRNS(png_ptr, info_ptr, trans, npalette-1,
647             NULL/*transparent color*/);
648 
649          /* Reset gamma_table to prevent the image rows being changed */
650          for (npalette=0; npalette<256; ++npalette)
651             gamma_table[npalette] = (png_byte)npalette;
652       }
653 
654       if (gamma == PNG_DEFAULT_sRGB)
655          png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);
656 
657       else if (gamma > 0) /* Else don't set color space information */
658       {
659          png_set_gAMA_fixed(png_ptr, info_ptr, real_gamma);
660 
661          /* Just use the sRGB values here. */
662          png_set_cHRM_fixed(png_ptr, info_ptr,
663             /* color      x       y */
664             /* white */ 31270, 32900,
665             /* red   */ 64000, 33000,
666             /* green */ 30000, 60000,
667             /* blue  */ 15000,  6000
668          );
669       }
670 
671       /* Insert extra information. */
672       while (insert != NULL)
673       {
674          insert->insert(png_ptr, info_ptr, insert->nparams, insert->parameters);
675          insert = insert->next;
676       }
677 
678       /* Write the file header. */
679       png_write_info(png_ptr, info_ptr);
680 
681       /* Restrict the filters */
682       png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, filters);
683 
684       {
685          int passes = png_set_interlace_handling(png_ptr);
686          int pass;
687          png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
688 
689          row = malloc(rowbytes);
690 
691          if (row == NULL)
692             png_error(png_ptr, "OOM allocating row buffer");
693 
694          for (pass = 0; pass < passes; ++pass)
695          {
696             unsigned int y;
697 
698             for (y=0; y<size; ++y)
699             {
700                generate_row(row, rowbytes, y, color_type, bit_depth,
701                   gamma_table, conv, colors);
702                png_write_row(png_ptr, row);
703             }
704          }
705       }
706    }
707 
708    /* Finish writing the file. */
709    png_write_end(png_ptr, info_ptr);
710 
711    {
712       png_structp nv_ptr = png_ptr;
713       png_infop nv_info = info_ptr;
714 
715       png_ptr = NULL;
716       info_ptr = NULL;
717       png_destroy_write_struct(&nv_ptr, &nv_info);
718    }
719    free(row);
720    return 0;
721 }
722 
723 
724 static size_t
load_file(png_const_charp name,png_bytepp result)725 load_file(png_const_charp name, png_bytepp result)
726 {
727    FILE *fp = tmpfile();
728 
729    if (fp != NULL)
730    {
731       FILE *ip = fopen(name, "rb");
732 
733       if (ip != NULL)
734       {
735          size_t total = 0;
736          int ch;
737 
738          for (;;)
739          {
740             ch = getc(ip);
741             if (ch == EOF) break;
742             putc(ch, fp);
743             ++total;
744          }
745 
746          if (ferror(ip))
747          {
748             perror(name);
749             fprintf(stderr, "%s: read error\n", name);
750             (void)fclose(ip);
751          }
752 
753          else
754          {
755             (void)fclose(ip);
756 
757             if (ferror(fp))
758             {
759                perror("temporary file");
760                fprintf(stderr, "temporary file write error\n");
761             }
762 
763             else
764             {
765                rewind(fp);
766 
767                if (total > 0)
768                {
769                   /* Round up to a multiple of 4 here to allow an iCCP profile
770                    * to be padded to a 4x boundary.
771                    */
772                   png_bytep data = malloc((total+3)&~3);
773 
774                   if (data != NULL)
775                   {
776                      size_t new_size = 0;
777 
778                      for (;;)
779                      {
780                         ch = getc(fp);
781                         if (ch == EOF) break;
782                         data[new_size++] = (png_byte)ch;
783                      }
784 
785                      if (ferror(fp) || new_size != total)
786                      {
787                         perror("temporary file");
788                         fprintf(stderr, "temporary file read error\n");
789                         free(data);
790                      }
791 
792                      else
793                      {
794                         (void)fclose(fp);
795                         *result = data;
796                         return total;
797                      }
798                   }
799 
800                   else
801                      fprintf(stderr, "%s: out of memory loading file\n", name);
802                }
803 
804                else
805                   fprintf(stderr, "%s: empty file\n", name);
806             }
807          }
808       }
809 
810       else
811       {
812          perror(name);
813          fprintf(stderr, "%s: open failed\n", name);
814       }
815 
816       fclose(fp);
817    }
818 
819    else
820       fprintf(stderr, "makepng: %s: could not open temporary file\n", name);
821 
822    exit(1);
823    return 0;
824 }
825 
826 static png_size_t
load_fake(png_charp param,png_bytepp profile)827 load_fake(png_charp param, png_bytepp profile)
828 {
829    char *endptr = NULL;
830    unsigned long long int size = strtoull(param, &endptr, 0/*base*/);
831 
832    /* The 'fake' format is <number>*[string] */
833    if (endptr != NULL && *endptr == '*')
834    {
835       size_t len = strlen(++endptr);
836       size_t result = (size_t)size;
837 
838       if (len == 0) len = 1; /* capture the terminating '\0' */
839 
840       /* Now repeat that string to fill 'size' bytes. */
841       if (result == size && (*profile = malloc(result)) != NULL)
842       {
843          png_bytep out = *profile;
844 
845          if (len == 1)
846             memset(out, *endptr, result);
847 
848          else
849          {
850             while (size >= len)
851             {
852                memcpy(out, endptr, len);
853                out += len;
854                size -= len;
855             }
856             memcpy(out, endptr, size);
857          }
858 
859          return result;
860       }
861 
862       else
863       {
864          fprintf(stderr, "%s: size exceeds system limits\n", param);
865          exit(1);
866       }
867    }
868 
869    return 0;
870 }
871 
872 static void
check_param_count(int nparams,int expect)873 check_param_count(int nparams, int expect)
874 {
875    if (nparams != expect)
876    {
877       fprintf(stderr, "bad parameter count (internal error)\n");
878       exit(1);
879    }
880 }
881 
882 static void
insert_iCCP(png_structp png_ptr,png_infop info_ptr,int nparams,png_charpp params)883 insert_iCCP(png_structp png_ptr, png_infop info_ptr, int nparams,
884    png_charpp params)
885 {
886    png_bytep profile = NULL;
887    png_uint_32 proflen = 0;
888    int result;
889 
890    check_param_count(nparams, 2);
891 
892    switch (params[1][0])
893    {
894       case '<':
895          {
896             png_size_t filelen = load_file(params[1]+1, &profile);
897             if (filelen > 0xfffffffc) /* Maximum profile length */
898             {
899                fprintf(stderr, "%s: file too long (%lu) for an ICC profile\n",
900                   params[1]+1, (unsigned long)filelen);
901                exit(1);
902             }
903 
904             proflen = (png_uint_32)filelen;
905          }
906          break;
907 
908       case '0': case '1': case '2': case '3': case '4':
909       case '5': case '6': case '7': case '8': case '9':
910          {
911             png_size_t fake_len = load_fake(params[1], &profile);
912 
913             if (fake_len > 0) /* else a simple parameter */
914             {
915                if (fake_len > 0xffffffff) /* Maximum profile length */
916                {
917                   fprintf(stderr,
918                      "%s: fake data too long (%lu) for an ICC profile\n",
919                      params[1], (unsigned long)fake_len);
920                   exit(1);
921                }
922                proflen = (png_uint_32)(fake_len & ~3U);
923                /* Always fix up the profile length. */
924                png_save_uint_32(profile, proflen);
925                break;
926             }
927          }
928 
929       default:
930          fprintf(stderr, "--insert iCCP \"%s\": unrecognized\n", params[1]);
931          fprintf(stderr, "  use '<' to read a file: \"<filename\"\n");
932          exit(1);
933    }
934 
935    result = 1;
936 
937    if (proflen & 3)
938    {
939       fprintf(stderr,
940          "makepng: --insert iCCP %s: profile length made a multiple of 4\n",
941          params[1]);
942 
943       /* load_file allocates extra space for this padding, the ICC spec requires
944        * padding with zero bytes.
945        */
946       while (proflen & 3)
947          profile[proflen++] = 0;
948    }
949 
950    if (profile != NULL && proflen > 3)
951    {
952       png_uint_32 prof_header = png_get_uint_32(profile);
953 
954       if (prof_header != proflen)
955       {
956          fprintf(stderr, "--insert iCCP %s: profile length field wrong:\n",
957             params[1]);
958          fprintf(stderr, "  actual %lu, recorded value %lu (corrected)\n",
959             (unsigned long)proflen, (unsigned long)prof_header);
960          png_save_uint_32(profile, proflen);
961       }
962    }
963 
964    if (result && profile != NULL && proflen >=4)
965       png_set_iCCP(png_ptr, info_ptr, params[0], PNG_COMPRESSION_TYPE_BASE,
966          profile, proflen);
967 
968    if (profile)
969       free(profile);
970 
971    if (!result)
972       exit(1);
973 }
974 
975 static void
clear_text(png_text * text,png_charp keyword)976 clear_text(png_text *text, png_charp keyword)
977 {
978    text->compression = -1; /* none */
979    text->key = keyword;
980    text->text = NULL;
981    text->text_length = 0; /* libpng calculates this */
982    text->itxt_length = 0; /* libpng calculates this */
983    text->lang = NULL;
984    text->lang_key = NULL;
985 }
986 
987 static void
set_text(png_structp png_ptr,png_infop info_ptr,png_textp text,png_charp param)988 set_text(png_structp png_ptr, png_infop info_ptr, png_textp text,
989    png_charp param)
990 {
991    switch (param[0])
992    {
993       case '<':
994          {
995             png_bytep file = NULL;
996 
997             text->text_length = load_file(param+1, &file);
998             text->text = (png_charp)file;
999          }
1000          break;
1001 
1002       case '0': case '1': case '2': case '3': case '4':
1003       case '5': case '6': case '7': case '8': case '9':
1004          {
1005             png_bytep data = NULL;
1006             png_size_t fake_len = load_fake(param, &data);
1007 
1008             if (fake_len > 0) /* else a simple parameter */
1009             {
1010                text->text_length = fake_len;
1011                text->text = (png_charp)data;
1012                break;
1013             }
1014          }
1015 
1016       default:
1017          text->text = param;
1018          break;
1019    }
1020 
1021    png_set_text(png_ptr, info_ptr, text, 1);
1022 
1023    if (text->text != param)
1024       free(text->text);
1025 }
1026 
1027 static void
insert_tEXt(png_structp png_ptr,png_infop info_ptr,int nparams,png_charpp params)1028 insert_tEXt(png_structp png_ptr, png_infop info_ptr, int nparams,
1029    png_charpp params)
1030 {
1031    png_text text;
1032 
1033    check_param_count(nparams, 2);
1034    clear_text(&text, params[0]);
1035    set_text(png_ptr, info_ptr, &text, params[1]);
1036 }
1037 
1038 static void
insert_zTXt(png_structp png_ptr,png_infop info_ptr,int nparams,png_charpp params)1039 insert_zTXt(png_structp png_ptr, png_infop info_ptr, int nparams,
1040    png_charpp params)
1041 {
1042    png_text text;
1043 
1044    check_param_count(nparams, 2);
1045    clear_text(&text, params[0]);
1046    text.compression = 0; /* deflate */
1047    set_text(png_ptr, info_ptr, &text, params[1]);
1048 }
1049 
1050 static void
insert_iTXt(png_structp png_ptr,png_infop info_ptr,int nparams,png_charpp params)1051 insert_iTXt(png_structp png_ptr, png_infop info_ptr, int nparams,
1052    png_charpp params)
1053 {
1054    png_text text;
1055 
1056    check_param_count(nparams, 4);
1057    clear_text(&text, params[0]);
1058    text.compression = 2; /* iTXt + deflate */
1059    text.lang = params[1];/* language tag */
1060    text.lang_key = params[2]; /* translated keyword */
1061    set_text(png_ptr, info_ptr, &text, params[3]);
1062 }
1063 
1064 static void
insert_hIST(png_structp png_ptr,png_infop info_ptr,int nparams,png_charpp params)1065 insert_hIST(png_structp png_ptr, png_infop info_ptr, int nparams, png_charpp params)
1066 {
1067    int i;
1068    png_uint_16 freq[256];
1069 
1070    /* libpng takes the count from the PLTE count; we don't check it here but we
1071     * do set the array to 0 for unspecified entries.
1072     */
1073    memset(freq, 0, sizeof freq);
1074    for (i=0; i<nparams; ++i)
1075    {
1076       char *endptr = NULL;
1077       unsigned long int l = strtoul(params[i], &endptr, 0/*base*/);
1078 
1079       if (params[i][0] && *endptr == 0 && l <= 65535)
1080          freq[i] = (png_uint_16)l;
1081 
1082       else
1083       {
1084          fprintf(stderr, "hIST[%d]: %s: invalid frequency\n", i, params[i]);
1085          exit(1);
1086       }
1087    }
1088 
1089    png_set_hIST(png_ptr, info_ptr, freq);
1090 }
1091 
1092 #if 0
1093 static void
1094 insert_sPLT(png_structp png_ptr, png_infop info_ptr, int nparams, png_charpp params)
1095 {
1096    fprintf(stderr, "insert sPLT: NYI\n");
1097 }
1098 #endif
1099 
1100 static int
find_parameters(png_const_charp what,png_charp param,png_charp * list,int nparams)1101 find_parameters(png_const_charp what, png_charp param, png_charp *list,
1102    int nparams)
1103 {
1104    /* Parameters are separated by '\n' or ':' characters, up to nparams are
1105     * accepted (more is an error) and the number found is returned.
1106     */
1107    int i;
1108    for (i=0; *param && i<nparams; ++i)
1109    {
1110       list[i] = param;
1111       while (*++param) if (*param == '\n' || *param == ':')
1112       {
1113          *param++ = 0; /* Terminate last parameter */
1114          break;        /* And start a new one. */
1115       }
1116    }
1117 
1118    if (*param)
1119    {
1120       fprintf(stderr, "--insert %s: too many parameters (%s)\n", what, param);
1121       exit(1);
1122    }
1123 
1124    list[i] = NULL; /* terminates list */
1125    return i; /* number of parameters filled in */
1126 }
1127 
1128 static void
bad_parameter_count(png_const_charp what,int nparams)1129 bad_parameter_count(png_const_charp what, int nparams)
1130 {
1131    fprintf(stderr, "--insert %s: bad parameter count %d\n", what, nparams);
1132    exit(1);
1133 }
1134 
1135 static chunk_insert *
make_insert(png_const_charp what,void (* insert)(png_structp,png_infop,int,png_charpp),int nparams,png_charpp list)1136 make_insert(png_const_charp what,
1137    void (*insert)(png_structp, png_infop, int, png_charpp),
1138    int nparams, png_charpp list)
1139 {
1140    int i;
1141    chunk_insert *cip;
1142 
1143    cip = malloc(offsetof(chunk_insert,parameters) +
1144       nparams * sizeof (png_charp));
1145 
1146    if (cip == NULL)
1147    {
1148       fprintf(stderr, "--insert %s: out of memory allocating %d parameters\n",
1149          what, nparams);
1150       exit(1);
1151    }
1152 
1153    cip->next = NULL;
1154    cip->insert = insert;
1155    cip->nparams = nparams;
1156    for (i=0; i<nparams; ++i)
1157       cip->parameters[i] = list[i];
1158 
1159    return cip;
1160 }
1161 
1162 static chunk_insert *
find_insert(png_const_charp what,png_charp param)1163 find_insert(png_const_charp what, png_charp param)
1164 {
1165    png_uint_32 chunk = 0;
1166    png_charp parameter_list[1024];
1167    int i, nparams;
1168 
1169    /* Assemble the chunk name */
1170    for (i=0; i<4; ++i)
1171    {
1172       char ch = what[i];
1173 
1174       if ((ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122))
1175          chunk = (chunk << 8) + what[i];
1176 
1177       else
1178          break;
1179    }
1180 
1181    if (i < 4 || what[4] != 0)
1182    {
1183       fprintf(stderr, "makepng --insert \"%s\": invalid chunk name\n", what);
1184       exit(1);
1185    }
1186 
1187    /* Assemble the parameter list. */
1188    nparams = find_parameters(what, param, parameter_list, 1024);
1189 
1190 #  define CHUNK(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))
1191 
1192    switch (chunk)
1193    {
1194       case CHUNK(105,67,67,80):  /* iCCP */
1195          if (nparams == 2)
1196             return make_insert(what, insert_iCCP, nparams, parameter_list);
1197          break;
1198 
1199       case CHUNK(116,69,88,116): /* tEXt */
1200          if (nparams == 2)
1201             return make_insert(what, insert_tEXt, nparams, parameter_list);
1202          break;
1203 
1204       case CHUNK(122,84,88,116): /* zTXt */
1205          if (nparams == 2)
1206             return make_insert(what, insert_zTXt, nparams, parameter_list);
1207          break;
1208 
1209       case CHUNK(105,84,88,116): /* iTXt */
1210          if (nparams == 4)
1211             return make_insert(what, insert_iTXt, nparams, parameter_list);
1212          break;
1213 
1214       case CHUNK(104,73,83,84):  /* hIST */
1215          if (nparams <= 256)
1216             return make_insert(what, insert_hIST, nparams, parameter_list);
1217          break;
1218 
1219 #if 0
1220       case CHUNK(115,80,76,84):  /* sPLT */
1221          return make_insert(what, insert_sPLT, nparams, parameter_list);
1222 #endif
1223 
1224       default:
1225          fprintf(stderr, "makepng --insert \"%s\": unrecognized chunk name\n",
1226             what);
1227          exit(1);
1228    }
1229 
1230    bad_parameter_count(what, nparams);
1231    return NULL;
1232 }
1233 
1234 /* This is a not-very-good parser for a sequence of numbers (including 0).  It
1235  * doesn't accept some apparently valid things, but it accepts all the sensible
1236  * combinations.
1237  */
1238 static void
parse_color(char * arg,unsigned int * colors)1239 parse_color(char *arg, unsigned int *colors)
1240 {
1241    unsigned int ncolors = 0;
1242 
1243    while (*arg && ncolors < 4)
1244    {
1245       char *ep = arg;
1246 
1247       unsigned long ul = strtoul(arg, &ep, 0);
1248 
1249       if (ul > 65535)
1250       {
1251          fprintf(stderr, "makepng --color=...'%s': too big\n", arg);
1252          exit(1);
1253       }
1254 
1255       if (ep == arg)
1256       {
1257          fprintf(stderr, "makepng --color=...'%s': not a valid color\n", arg);
1258          exit(1);
1259       }
1260 
1261       if (*ep) ++ep; /* skip a separator */
1262       arg = ep;
1263 
1264       colors[++ncolors] = (unsigned int)ul; /* checked above */
1265    }
1266 
1267    if (*arg)
1268    {
1269       fprintf(stderr, "makepng --color=...'%s': too many values\n", arg);
1270       exit(1);
1271    }
1272 
1273    *colors = ncolors;
1274 }
1275 
1276 int
main(int argc,char ** argv)1277 main(int argc, char **argv)
1278 {
1279    FILE *fp = stdout;
1280    const char *file_name = NULL;
1281    int color_type = 8; /* invalid */
1282    int bit_depth = 32; /* invalid */
1283    unsigned int colors[5];
1284    unsigned int filters = PNG_ALL_FILTERS;
1285    png_fixed_point gamma = 0; /* not set */
1286    chunk_insert *head_insert = NULL;
1287    chunk_insert **insert_ptr = &head_insert;
1288 
1289    memset(colors, 0, sizeof colors);
1290 
1291    while (--argc > 0)
1292    {
1293       char *arg = *++argv;
1294 
1295       if (strcmp(arg, "--sRGB") == 0)
1296       {
1297          gamma = PNG_DEFAULT_sRGB;
1298          continue;
1299       }
1300 
1301       if (strcmp(arg, "--linear") == 0)
1302       {
1303          gamma = PNG_FP_1;
1304          continue;
1305       }
1306 
1307       if (strcmp(arg, "--1.8") == 0)
1308       {
1309          gamma = PNG_GAMMA_MAC_18;
1310          continue;
1311       }
1312 
1313       if (strcmp(arg, "--nofilters") == 0)
1314       {
1315          filters = PNG_FILTER_NONE;
1316          continue;
1317       }
1318 
1319       if (strncmp(arg, "--color=", 8) == 0)
1320       {
1321           parse_color(arg+8, colors);
1322           continue;
1323       }
1324 
1325       if (argc >= 3 && strcmp(arg, "--insert") == 0)
1326       {
1327          png_const_charp what = *++argv;
1328          png_charp param = *++argv;
1329          chunk_insert *new_insert;
1330 
1331          argc -= 2;
1332 
1333          new_insert = find_insert(what, param);
1334 
1335          if (new_insert != NULL)
1336          {
1337             *insert_ptr = new_insert;
1338             insert_ptr = &new_insert->next;
1339          }
1340 
1341          continue;
1342       }
1343 
1344       if (arg[0] == '-')
1345       {
1346          fprintf(stderr, "makepng: %s: invalid option\n", arg);
1347          exit(1);
1348       }
1349 
1350       if (strcmp(arg, "palette") == 0)
1351       {
1352          color_type = PNG_COLOR_TYPE_PALETTE;
1353          continue;
1354       }
1355 
1356       if (strncmp(arg, "gray", 4) == 0)
1357       {
1358          if (arg[4] == 0)
1359          {
1360             color_type = PNG_COLOR_TYPE_GRAY;
1361             continue;
1362          }
1363 
1364          else if (strcmp(arg+4, "a") == 0 ||
1365             strcmp(arg+4, "alpha") == 0 ||
1366             strcmp(arg+4, "-alpha") == 0)
1367          {
1368             color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
1369             continue;
1370          }
1371       }
1372 
1373       if (strncmp(arg, "rgb", 3) == 0)
1374       {
1375          if (arg[3] == 0)
1376          {
1377             color_type = PNG_COLOR_TYPE_RGB;
1378             continue;
1379          }
1380 
1381          else if (strcmp(arg+3, "a") == 0 ||
1382             strcmp(arg+3, "alpha") == 0 ||
1383             strcmp(arg+3, "-alpha") == 0)
1384          {
1385             color_type = PNG_COLOR_TYPE_RGB_ALPHA;
1386             continue;
1387          }
1388       }
1389 
1390       if (color_type == 8 && isdigit(arg[0]))
1391       {
1392          color_type = atoi(arg);
1393          if (color_type < 0 || color_type > 6 || color_type == 1 ||
1394             color_type == 5)
1395          {
1396             fprintf(stderr, "makepng: %s: not a valid color type\n", arg);
1397             exit(1);
1398          }
1399 
1400          continue;
1401       }
1402 
1403       if (bit_depth == 32 && isdigit(arg[0]))
1404       {
1405          bit_depth = atoi(arg);
1406          if (bit_depth <= 0 || bit_depth > 16 ||
1407             (bit_depth & -bit_depth) != bit_depth)
1408          {
1409             fprintf(stderr, "makepng: %s: not a valid bit depth\n", arg);
1410             exit(1);
1411          }
1412 
1413          continue;
1414       }
1415 
1416       if (argc == 1) /* It's the file name */
1417       {
1418          fp = fopen(arg, "wb");
1419          if (fp == NULL)
1420          {
1421             fprintf(stderr, "%s: %s: could not open\n", arg, strerror(errno));
1422             exit(1);
1423          }
1424 
1425          file_name = arg;
1426          continue;
1427       }
1428 
1429       fprintf(stderr, "makepng: %s: unknown argument\n", arg);
1430       exit(1);
1431    } /* argument while loop */
1432 
1433    if (color_type == 8 || bit_depth == 32)
1434    {
1435       fprintf(stderr, "usage: makepng [--sRGB|--linear|--1.8] "
1436          "[--color=...] color-type bit-depth [file-name]\n"
1437          "  Make a test PNG file, by default writes to stdout.\n");
1438       exit(1);
1439    }
1440 
1441    /* Check the colors */
1442    {
1443       const unsigned int lim = (color_type == PNG_COLOR_TYPE_PALETTE ? 255U :
1444          (1U<<bit_depth)-1);
1445       unsigned int i;
1446 
1447       for (i=1; i<=colors[0]; ++i)
1448          if (colors[i] > lim)
1449          {
1450             fprintf(stderr, "makepng: --color=...: %u out of range [0..%u]\n",
1451                colors[i], lim);
1452             exit(1);
1453          }
1454    }
1455 
1456    /* Restrict the filters for more speed to those we know are used for the
1457     * generated images.
1458     */
1459    if (filters == PNG_ALL_FILTERS)
1460    {
1461       if ((color_type & PNG_COLOR_MASK_PALETTE) != 0 || bit_depth < 8)
1462          filters = PNG_FILTER_NONE;
1463 
1464       else if (color_type & PNG_COLOR_MASK_COLOR) /* rgb */
1465       {
1466          if (bit_depth == 8)
1467             filters &= ~(PNG_FILTER_NONE | PNG_FILTER_AVG);
1468 
1469          else
1470             filters = PNG_FILTER_SUB | PNG_FILTER_PAETH;
1471       }
1472 
1473       else /* gray 8 or 16-bit */
1474          filters &= ~PNG_FILTER_NONE;
1475    }
1476 
1477    {
1478       int ret = write_png(&file_name, fp, color_type, bit_depth, gamma,
1479          head_insert, filters, colors);
1480 
1481       if (ret != 0 && file_name != NULL)
1482          remove(file_name);
1483 
1484       return ret;
1485    }
1486 }
1487