makepng.c 52.9 KB
Newer Older
J
John Bowler 已提交
1 2 3 4 5
/* makepng.c */
#define _ISOC99_SOURCE
/* Copyright: */
#define COPYRIGHT "\251 2013,2015 John Cunningham Bowler"
/*
6
 * Last changed in libpng 1.6.20 [November 24, 2015]
7 8 9 10 11 12 13
 *
 * This code is released under the libpng license.
 * For conditions of distribution and use, see the disclaimer
 * and license in png.h
 *
 * Make a test PNG image.  The arguments are as follows:
 *
J
John Bowler 已提交
14 15
 *    makepng [--sRGB|--linear|--1.8] [--tRNS] [--nofilters] \
 *       color-type bit-depth [file-name]
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
 *
 * The color-type may be numeric (and must match the numbers used by the PNG
 * specification) or one of the format names listed below.  The bit-depth is the
 * component bit depth, or the pixel bit-depth for a color-mapped image.
 *
 * Without any options no color-space information is written, with the options
 * an sRGB or the appropriate gAMA chunk is written.  "1.8" refers to the
 * display system used on older Apple computers to correct for high ambient
 * light levels in the viewing environment; it applies a transform of
 * approximately value^(1/1.45) to the color values and so a gAMA chunk of 65909
 * is written (1.45/2.2).
 *
 * The image data is generated internally.  Unless --color is given the images
 * used are as follows:
 *
 * 1 channel: a square image with a diamond, the least luminous colors are on
 *    the edge of the image, the most luminous in the center.
 *
 * 2 channels: the color channel increases in luminosity from top to bottom, the
 *    alpha channel increases in opacity from left to right.
 *
 * 3 channels: linear combinations of, from the top-left corner clockwise,
 *    black, green, white, red.
 *
 * 4 channels: linear combinations of, from the top-left corner clockwise,
 *    transparent, red, green, blue.
 *
J
John Bowler 已提交
43 44
 * For color-mapped images a four channel color-map is used and if --tRNS is
 * given the PNG file has a tRNS chunk, as follows:
45 46 47 48 49 50 51 52 53 54 55 56
 *
 * 1-bit: entry 0 is transparent-red, entry 1 is opaque-white
 * 2-bit: entry 0: transparent-green
 *        entry 1: 40%-red
 *        entry 2: 80%-blue
 *        entry 3: opaque-white
 * 4-bit: the 16 combinations of the 2-bit case
 * 8-bit: the 256 combinations of the 4-bit case
 *
 * The palette always has 2^bit-depth entries and the tRNS chunk one fewer.  The
 * image is the 1-channel diamond, but using palette index, not luminosity.
 *
J
John Bowler 已提交
57 58 59
 * For formats other than color-mapped ones if --tRNS is specified a tRNS chunk
 * is generated with all channels equal to the low bits of 0x0101.
 *
60 61 62 63 64 65 66
 * Image size is determined by the final pixel depth in bits, i.e. channels x
 * bit-depth, as follows:
 *
 * 8 bits or less:    64x64
 * 16 bits:           256x256
 * More than 16 bits: 1024x1024
 *
J
John Bowler 已提交
67 68
 * Row filtering is the libpng default but may be turned off (the 'none' filter
 * is used on every row) with the --nofilters option.
69
 *
J
John Bowler 已提交
70
 * The images are not interlaced.
71 72 73 74 75
 *
 * If file-name is given then the PNG is written to that file, else it is
 * written to stdout.  Notice that stdout is not supported on systems where, by
 * default, it assumes text output; this program makes no attempt to change the
 * text mode of stdout!
J
John Bowler 已提交
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
 *
 *    makepng --color=<color> ...
 *
 * If --color is given then the whole image has that color, color-mapped images
 * will have exactly one palette entry and all image files with be 16x16 in
 * size.  The color value is 1 to 4 decimal numbers as appropriate for the color
 * type.
 *
 *    makepng --small ...
 *
 * If --small is given the images are no larger than required to include every
 * possible pixel value for the format.
 *
 * For formats with pixels 8 bits or fewer in size the images consist of a
 * single row with 2^pixel-depth pixels, one of every possible value.
 *
 * For formats with 16-bit pixels a 256x256 image is generated containing every
 * possible pixel value.
 *
 * For larger pixel sizes a 256x256 image is generated where the first row
 * consists of each pixel that has identical byte values throughout the pixel
 * followed by rows where the byte values differ within the pixel.
 *
 * In all cases the pixel values are arranged in such a way that the SUB and UP
 * filters give byte sequences for maximal zlib compression.  By default (if
 * --nofilters is not given) the SUB filter is used on the first row and the UP
 * filter on all following rows.
 *
 * The --small option is meant to provide good test-case coverage, however the
 * images are not easy to examine visually.  Without the --small option the
 * images contain identical color values; the pixel values are adjusted
 * according to the gamma encoding with no gamma encoding being interpreted as
 * sRGB.
 *
 * LICENSING
 * =========
 *
 * This code is copyright of the authors, see the COPYRIGHT define above.  The
 * code is licensed as above, using the libpng license.  The code generates
 * images which are solely the product of the code; the options choose which of
 * the many possibilities to generate.  The images that result (but not the code
 * which generates them) are licensed as defined here:
 *
 * IMPORTANT: the COPYRIGHT #define must contain ISO-Latin-1 characters, the
 * IMAGE_LICENSING #define must contain UTF-8 characters.  The 'copyright'
 * symbol 0xA9U (\251) in ISO-Latin-1 encoding and 0xC20xA9 (\302\251) in UTF-8.
122
 */
J
John Bowler 已提交
123 124
#define IMAGE_LICENSING "Dedicated to the public domain per Creative Commons "\
    "license \"CC0 1.0\"; https://creativecommons.org/publicdomain/zero/1.0/"
125 126 127 128 129 130 131 132

#include <stddef.h> /* for offsetof */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <errno.h>
J
John Bowler 已提交
133 134
#include <assert.h>
#include <stdint.h>
135 136 137 138 139 140 141 142 143 144 145 146 147 148

#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
#  include <config.h>
#endif

/* Define the following to use this test against your installed libpng, rather
 * than the one being built here:
 */
#ifdef PNG_FREESTANDING_TESTS
#  include <png.h>
#else
#  include "../../png.h"
#endif

J
John Bowler 已提交
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
#include <zlib.h>

/* Work round for GCC complaints about casting a (double) function result to
 * an unsigned:
 */
static unsigned int
flooru(double d)
{
   d = floor(d);
   return (unsigned int)d;
}

static png_byte
floorb(double d)
{
   d = floor(d);
   return (png_byte)d;
}

168 169 170 171 172 173 174 175 176 177 178
/* This structure is used for inserting extra chunks (the --insert argument, not
 * documented above.)
 */
typedef struct chunk_insert
{
   struct chunk_insert *next;
   void               (*insert)(png_structp, png_infop, int, png_charpp);
   int                  nparams;
   png_charp            parameters[1];
} chunk_insert;

179
static unsigned int
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
channels_of_type(int color_type)
{
   if (color_type & PNG_COLOR_MASK_PALETTE)
      return 1;

   else
   {
      int channels = 1;

      if (color_type & PNG_COLOR_MASK_COLOR)
         channels = 3;

      if (color_type & PNG_COLOR_MASK_ALPHA)
         return channels + 1;

      else
         return channels;
   }
}

200
static unsigned int
201 202 203 204 205 206
pixel_depth_of_type(int color_type, int bit_depth)
{
   return channels_of_type(color_type) * bit_depth;
}

static unsigned int
J
John Bowler 已提交
207 208
image_size_of_type(int color_type, int bit_depth, unsigned int *colors,
   int small)
209 210 211 212 213 214 215 216
{
   if (*colors)
      return 16;

   else
   {
      int pixel_depth = pixel_depth_of_type(color_type, bit_depth);

J
John Bowler 已提交
217 218 219 220 221 222 223 224 225 226
      if (small)
      {
         if (pixel_depth <= 8) /* there will be one row */
            return 1 << pixel_depth;

         else
            return 256;
      }

      else if (pixel_depth < 8)
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
         return 64;

      else if (pixel_depth > 16)
         return 1024;

      else
         return 256;
   }
}

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)
{
   color->red = gamma_table[red];
   color->green = gamma_table[green];
   color->blue = gamma_table[blue];
   *trans = (png_byte)alpha;
}

static int
generate_palette(png_colorp palette, png_bytep trans, int bit_depth,
   png_const_bytep gamma_table, unsigned int *colors)
{
   /*
    * 1-bit: entry 0 is transparent-red, entry 1 is opaque-white
    * 2-bit: entry 0: transparent-green
    *        entry 1: 40%-red
    *        entry 2: 80%-blue
    *        entry 3: opaque-white
    * 4-bit: the 16 combinations of the 2-bit case
    * 8-bit: the 256 combinations of the 4-bit case
    */
   switch (colors[0])
   {
      default:
         fprintf(stderr, "makepng: --colors=...: invalid count %u\n",
            colors[0]);
         exit(1);

      case 1:
         set_color(palette+0, trans+0, colors[1], colors[1], colors[1], 255,
            gamma_table);
         return 1;

      case 2:
         set_color(palette+0, trans+0, colors[1], colors[1], colors[1],
            colors[2], gamma_table);
         return 1;

      case 3:
         set_color(palette+0, trans+0, colors[1], colors[2], colors[3], 255,
            gamma_table);
         return 1;

      case 4:
         set_color(palette+0, trans+0, colors[1], colors[2], colors[3],
            colors[4], gamma_table);
         return 1;

      case 0:
         if (bit_depth == 1)
         {
            set_color(palette+0, trans+0, 255, 0, 0, 0, gamma_table);
            set_color(palette+1, trans+1, 255, 255, 255, 255, gamma_table);
            return 2;
         }

         else
         {
            unsigned int size = 1U << (bit_depth/2); /* 2, 4 or 16 */
J
John Bowler 已提交
299 300
            unsigned int x, y;
            volatile unsigned int ip = 0;
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363

            for (x=0; x<size; ++x) for (y=0; y<size; ++y)
            {
               ip = x + (size * y);

               /* size is at most 16, so the scaled value below fits in 16 bits
                */
#              define interp(pos, c1, c2) ((pos * c1) + ((size-pos) * c2))
#              define xyinterp(x, y, c1, c2, c3, c4) (((size * size / 2) +\
                  (interp(x, c1, c2) * y + (size-y) * interp(x, c3, c4))) /\
                  (size*size))

               set_color(palette+ip, trans+ip,
                  /* color:    green, red,blue,white */
                  xyinterp(x, y,   0, 255,   0, 255),
                  xyinterp(x, y, 255,   0,   0, 255),
                  xyinterp(x, y,   0,   0, 255, 255),
                  /* alpha:        0, 102, 204, 255) */
                  xyinterp(x, y,   0, 102, 204, 255),
                  gamma_table);
            }

            return ip+1;
         }
   }
}

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)
{
   unsigned int mask = (1U << bit_depth)-1;

   x *= bit_depth;  /* Maximum x is 4*1024, maximum bit_depth is 16 */

   if (value <= mask)
   {
      png_uint_32 offset = x >> 3;

      if (offset < rowbytes && (bit_depth < 16 || offset+1 < rowbytes))
      {
         row += offset;

         switch (bit_depth)
         {
            case 1:
            case 2:
            case 4:
               /* Don't gamma correct - values get smashed */
               {
                  unsigned int shift = (8 - bit_depth) - (x & 0x7U);

                  mask <<= shift;
                  value = (value << shift) & mask;
                  *row = (png_byte)((*row & ~mask) | value);
               }
               return;

            default:
               fprintf(stderr, "makepng: bad bit depth (internal error)\n");
               exit(1);

            case 16:
J
John Bowler 已提交
364
               value = flooru(65535*pow(value/65535.,conv)+.5);
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
               *row++ = (png_byte)(value >> 8);
               *row = (png_byte)value;
               return;

            case 8:
               *row = gamma_table[value];
               return;
         }
      }

      else
      {
         fprintf(stderr, "makepng: row buffer overflow (internal error)\n");
         exit(1);
      }
   }

   else
   {
      fprintf(stderr, "makepng: component overflow (internal error)\n");
      exit(1);
   }
}

J
John Bowler 已提交
389
static int /* filter mask for row */
390 391
generate_row(png_bytep row, size_t rowbytes, unsigned int y, int color_type,
   int bit_depth, png_const_bytep gamma_table, double conv,
J
John Bowler 已提交
392
   unsigned int *colors, int small)
393
{
J
John Bowler 已提交
394
   int filters = 0; /* file *MASK*, 0 means the default, not NONE */
395
   png_uint_32 size_max =
J
John Bowler 已提交
396
      image_size_of_type(color_type, bit_depth, colors, small)-1;
397 398
   png_uint_32 depth_max = (1U << bit_depth)-1; /* up to 65536 */

J
John Bowler 已提交
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
   if (colors[0] == 0) if (small)
   {
      unsigned int pixel_depth = pixel_depth_of_type(color_type, bit_depth);

      /* For pixel depths less than 16 generate a single row containing all the
       * possible pixel values.  For 16 generate all 65536 byte pair
       * combinations in a 256x256 pixel array.
       */
      switch (pixel_depth)
      {
         case 1:
            assert(y == 0 && rowbytes == 1 && size_max == 1);
            row[0] = 0x6CU; /* binary: 01101100, only top 2 bits used */
            filters = PNG_FILTER_NONE;
            break;

         case 2:
            assert(y == 0 && rowbytes == 1 && size_max == 3);
            row[0] = 0x1BU; /* binary 00011011, all bits used */
            filters = PNG_FILTER_NONE;
            break;

         case 4:
            assert(y == 0 && rowbytes == 8 && size_max == 15);
            row[0] = 0x01U;
            row[1] = 0x23U; /* SUB gives 0x22U for all following bytes */
            row[2] = 0x45U;
            row[3] = 0x67U;
            row[4] = 0x89U;
            row[5] = 0xABU;
            row[6] = 0xCDU;
            row[7] = 0xEFU;
            filters = PNG_FILTER_SUB;
            break;

         case 8:
            /* The row will have all the pixel values in order starting with
             * '1', the SUB filter will change every byte into '1' (including
             * the last, which generates pixel value '0').  Since the SUB filter
             * has value 1 this should result in maximum compression.
             */
            assert(y == 0 && rowbytes == 256 && size_max == 255);
            for (;;)
            {
               row[size_max] = 0xFFU & (size_max+1);
               if (size_max == 0)
                  break;
               --size_max;
            }
            filters = PNG_FILTER_SUB;
            break;

         case 16:
            /* Rows are generated such that each row has a constant difference
             * between the first and second byte of each pixel and so that the
             * difference increases by 1 at each row.  The rows start with the
             * first byte value of 0 and the value increases to 255 across the
             * row.
             *
             * The difference starts at 1, so the first row is:
             *
             *     0 1 1 2 2 3 3 4 ... 254 255 255 0
             *
             * This means that running the SUB filter on the first row produces:
             *
             *   [SUB==1] 0 1 0 1 0 1...
             *
             * Then the difference is 2 on the next row, giving:
             *
             *    0 2 1 3 2 4 3 5 ... 254 0 255 1
             *
             * When the UP filter is run on this libpng produces:
             *
             *   [UP ==2] 0 1 0 1 0 1...
             *
             * And so on for all the remain rows to the final two * rows:
             *
             *    row 254: 0 255 1 0 2 1 3 2 4 3 ... 254 253 255 254
             *    row 255: 0   0 1 1 2 2 3 3 4 4 ... 254 254 255 255
             */
            assert(rowbytes == 512 && size_max == 255);
            for (;;)
            {
               row[2*size_max  ] = 0xFFU & size_max;
               row[2*size_max+1] = 0xFFU & (size_max+y+1);
               if (size_max == 0)
                  break;
               --size_max;
            }
            /* The first row must include PNG_FILTER_UP so that libpng knows we
             * need to keep it for the following row:
             */
            filters = (y == 0 ? PNG_FILTER_SUB+PNG_FILTER_UP : PNG_FILTER_UP);
            break;

         case 24:
         case 32:
         case 48:
         case 64:
            /* The rows are filled by an alogorithm similar to the above, in the
             * first row pixel bytes are all equal, increasing from 0 by 1 for
             * each pixel.  In the second row the bytes within a pixel are
             * incremented 1,3,5,7,... from the previous row byte.  Using an odd
             * number ensures all the possible byte values are used.
             */
            assert(size_max == 255 && rowbytes == 256*(pixel_depth>>3));
            pixel_depth >>= 3; /* now in bytes */
            while (rowbytes > 0)
            {
               const size_t pixel_index = --rowbytes/pixel_depth;

               if (y == 0)
                  row[rowbytes] = 0xFFU & pixel_index;

               else
               {
                  const size_t byte_offset =
                     rowbytes - pixel_index * pixel_depth;

                  row[rowbytes] =
                     0xFFU & (pixel_index + (byte_offset * 2*y) + 1);
               }
            }
            filters = (y == 0 ? PNG_FILTER_SUB+PNG_FILTER_UP : PNG_FILTER_UP);
            break;

         default:
            assert(0/*NOT REACHED*/);
      }
   }
529

J
John Bowler 已提交
530
   else switch (channels_of_type(color_type))
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
   {
   /* 1 channel: a square image with a diamond, the least luminous colors are on
    *    the edge of the image, the most luminous in the center.
    */
      case 1:
         {
            png_uint_32 x;
            png_uint_32 base = 2*size_max - abs(2*y-size_max);

            for (x=0; x<=size_max; ++x)
            {
               png_uint_32 luma = base - abs(2*x-size_max);

               /* 'luma' is now in the range 0..2*size_max, we need
                * 0..depth_max
                */
               luma = (luma*depth_max + size_max) / (2*size_max);
               set_value(row, rowbytes, x, bit_depth, luma, gamma_table, conv);
            }
         }
         break;

   /* 2 channels: the color channel increases in luminosity from top to bottom,
    *    the alpha channel increases in opacity from left to right.
    */
      case 2:
         {
            png_uint_32 alpha = (depth_max * y * 2 + size_max) / (2 * size_max);
            png_uint_32 x;

            for (x=0; x<=size_max; ++x)
            {
               set_value(row, rowbytes, 2*x, bit_depth,
                  (depth_max * x * 2 + size_max) / (2 * size_max), gamma_table,
                  conv);
               set_value(row, rowbytes, 2*x+1, bit_depth, alpha, gamma_table,
                  conv);
            }
         }
         break;

   /* 3 channels: linear combinations of, from the top-left corner clockwise,
    *    black, green, white, red.
    */
      case 3:
         {
            /* x0: the black->red scale (the value of the red component) at the
             *     start of the row (blue and green are 0).
             * x1: the green->white scale (the value of the red and blue
             *     components at the end of the row; green is depth_max).
             */
            png_uint_32 Y = (depth_max * y * 2 + size_max) / (2 * size_max);
            png_uint_32 x;

            /* Interpolate x/depth_max from start to end:
             *
             *        start end         difference
             * red:     Y    Y            0
             * green:   0   depth_max   depth_max
             * blue:    0    Y            Y
             */
            for (x=0; x<=size_max; ++x)
            {
               set_value(row, rowbytes, 3*x+0, bit_depth, /* red */ Y,
                     gamma_table, conv);
               set_value(row, rowbytes, 3*x+1, bit_depth, /* green */
                  (depth_max * x * 2 + size_max) / (2 * size_max),
                  gamma_table, conv);
               set_value(row, rowbytes, 3*x+2, bit_depth, /* blue */
                  (Y * x * 2 + size_max) / (2 * size_max),
                  gamma_table, conv);
            }
         }
         break;

   /* 4 channels: linear combinations of, from the top-left corner clockwise,
    *    transparent, red, green, blue.
    */
      case 4:
         {
            /* x0: the transparent->blue scale (the value of the blue and alpha
             *     components) at the start of the row (red and green are 0).
             * x1: the red->green scale (the value of the red and green
             *     components at the end of the row; blue is 0 and alpha is
             *     depth_max).
             */
            png_uint_32 Y = (depth_max * y * 2 + size_max) / (2 * size_max);
            png_uint_32 x;

            /* Interpolate x/depth_max from start to end:
             *
             *        start    end       difference
             * red:     0   depth_max-Y depth_max-Y
             * green:   0       Y             Y
             * blue:    Y       0            -Y
             * alpha:   Y    depth_max  depth_max-Y
             */
            for (x=0; x<=size_max; ++x)
            {
               set_value(row, rowbytes, 4*x+0, bit_depth, /* red */
                  ((depth_max-Y) * x * 2 + size_max) / (2 * size_max),
                  gamma_table, conv);
               set_value(row, rowbytes, 4*x+1, bit_depth, /* green */
                  (Y * x * 2 + size_max) / (2 * size_max),
                  gamma_table, conv);
               set_value(row, rowbytes, 4*x+2, bit_depth, /* blue */
                  Y - (Y * x * 2 + size_max) / (2 * size_max),
                  gamma_table, conv);
               set_value(row, rowbytes, 4*x+3, bit_depth, /* alpha */
                  Y + ((depth_max-Y) * x * 2 + size_max) / (2 * size_max),
                  gamma_table, conv);
            }
         }
         break;

      default:
         fprintf(stderr, "makepng: internal bad channel count\n");
         exit(2);
   }

   else if (color_type & PNG_COLOR_MASK_PALETTE)
   {
      /* Palette with fixed color: the image rows are all 0 and the image width
       * is 16.
       */
656
      memset(row, 0, rowbytes);
657 658 659 660 661 662 663
   }

   else if (colors[0] == channels_of_type(color_type))
      switch (channels_of_type(color_type))
      {
         case 1:
            {
664
               png_uint_32 luma = colors[1];
665 666 667 668 669 670 671 672 673 674
               png_uint_32 x;

               for (x=0; x<=size_max; ++x)
                  set_value(row, rowbytes, x, bit_depth, luma, gamma_table,
                     conv);
            }
            break;

         case 2:
            {
675 676
               png_uint_32 luma = colors[1];
               png_uint_32 alpha = colors[2];
677 678 679 680 681 682 683 684 685 686 687 688 689 690
               png_uint_32 x;

               for (x=0; x<size_max; ++x)
               {
                  set_value(row, rowbytes, 2*x, bit_depth, luma, gamma_table,
                     conv);
                  set_value(row, rowbytes, 2*x+1, bit_depth, alpha, gamma_table,
                     conv);
               }
            }
            break;

         case 3:
            {
691 692 693
               png_uint_32 red = colors[1];
               png_uint_32 green = colors[2];
               png_uint_32 blue = colors[3];
694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
               png_uint_32 x;

               for (x=0; x<=size_max; ++x)
               {
                  set_value(row, rowbytes, 3*x+0, bit_depth, red, gamma_table,
                     conv);
                  set_value(row, rowbytes, 3*x+1, bit_depth, green, gamma_table,
                     conv);
                  set_value(row, rowbytes, 3*x+2, bit_depth, blue, gamma_table,
                     conv);
               }
            }
            break;

         case 4:
            {
710 711 712 713
               png_uint_32 red = colors[1];
               png_uint_32 green = colors[2];
               png_uint_32 blue = colors[3];
               png_uint_32 alpha = colors[4];
714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741
               png_uint_32 x;

               for (x=0; x<=size_max; ++x)
               {
                  set_value(row, rowbytes, 4*x+0, bit_depth, red, gamma_table,
                     conv);
                  set_value(row, rowbytes, 4*x+1, bit_depth, green, gamma_table,
                     conv);
                  set_value(row, rowbytes, 4*x+2, bit_depth, blue, gamma_table,
                     conv);
                  set_value(row, rowbytes, 4*x+3, bit_depth, alpha, gamma_table,
                     conv);
               }
            }
         break;

         default:
            fprintf(stderr, "makepng: internal bad channel count\n");
            exit(2);
      }

   else
   {
      fprintf(stderr,
         "makepng: --color: count(%u) does not match channels(%u)\n",
         colors[0], channels_of_type(color_type));
      exit(1);
   }
J
John Bowler 已提交
742 743

   return filters;
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771
}


static void PNGCBAPI
makepng_warning(png_structp png_ptr, png_const_charp message)
{
   const char **ep = png_get_error_ptr(png_ptr);
   const char *name;

   if (ep != NULL && *ep != NULL)
      name = *ep;

   else
      name = "makepng";

  fprintf(stderr, "%s: warning: %s\n", name, message);
}

static void PNGCBAPI
makepng_error(png_structp png_ptr, png_const_charp message)
{
   makepng_warning(png_ptr, message);
   png_longjmp(png_ptr, 1);
}

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,
J
John Bowler 已提交
772
   unsigned int filters, unsigned int *colors, int small, int tRNS)
773
{
774
   png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
      name, makepng_error, makepng_warning);
   volatile png_infop info_ptr = NULL;
   volatile png_bytep row = NULL;

   if (png_ptr == NULL)
   {
      fprintf(stderr, "makepng: OOM allocating write structure\n");
      return 1;
   }

   if (setjmp(png_jmpbuf(png_ptr)))
   {
      png_structp nv_ptr = png_ptr;
      png_infop nv_info = info_ptr;

      png_ptr = NULL;
      info_ptr = NULL;
      png_destroy_write_struct(&nv_ptr, &nv_info);
      if (row != NULL) free(row);
      return 1;
   }

   /* Allow benign errors so that we can write PNGs with errors */
   png_set_benign_errors(png_ptr, 1/*allowed*/);
J
John Bowler 已提交
799 800 801 802 803 804 805 806 807

   /* Max out the text compression level in an attempt to make the license
    * small.   If --small then do the same for the IDAT.
    */
   if (small)
      png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);

   png_set_text_compression_level(png_ptr, Z_BEST_COMPRESSION);

808 809 810 811 812 813 814
   png_init_io(png_ptr, fp);

   info_ptr = png_create_info_struct(png_ptr);
   if (info_ptr == NULL)
      png_error(png_ptr, "OOM allocating info structure");

   {
815
      unsigned int size =
J
John Bowler 已提交
816 817
         image_size_of_type(color_type, bit_depth, colors, small);
      unsigned int ysize;
818 819 820 821
      png_fixed_point real_gamma = 45455; /* For sRGB */
      png_byte gamma_table[256];
      double conv;

J
John Bowler 已提交
822 823 824 825 826
      /* Normally images are square, but with 'small' we want to simply generate
       * all the pixel values, or all that we reasonably can:
       */
      if (small)
      {
827
         unsigned int pixel_depth =
J
John Bowler 已提交
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
            pixel_depth_of_type(color_type, bit_depth);

         if (pixel_depth <= 8U)
         {
            assert(size == (1U<<pixel_depth));
            ysize = 1U;
         }

         else
         {
            assert(size == 256U);
            ysize = 256U;
         }
      }

      else
         ysize = size;

846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876
      /* This function uses the libpng values used on read to carry extra
       * information about the gamma:
       */
      if (gamma == PNG_GAMMA_MAC_18)
         gamma = 65909;

      else if (gamma > 0 && gamma < 1000)
         gamma = PNG_FP_1;

      if (gamma > 0)
         real_gamma = gamma;

      {
         unsigned int i;

         if (real_gamma == 45455) for (i=0; i<256; ++i)
         {
            gamma_table[i] = (png_byte)i;
            conv = 1.;
         }

         else
         {
            /* Convert 'i' from sRGB (45455) to real_gamma, this makes
             * the images look the same regardless of the gAMA chunk.
             */
            conv = real_gamma;
            conv /= 45455;

            gamma_table[0] = 0;

877
            for (i=1; i<255; ++i)
J
John Bowler 已提交
878
               gamma_table[i] = floorb(pow(i/255.,conv) * 255 + .5);
879 880 881 882 883

            gamma_table[255] = 255;
         }
      }

J
John Bowler 已提交
884
      png_set_IHDR(png_ptr, info_ptr, size, ysize, bit_depth, color_type,
885 886 887 888 889 890 891 892 893 894 895
         PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

      if (color_type & PNG_COLOR_MASK_PALETTE)
      {
         int npalette;
         png_color palette[256];
         png_byte trans[256];

         npalette = generate_palette(palette, trans, bit_depth, gamma_table,
            colors);
         png_set_PLTE(png_ptr, info_ptr, palette, npalette);
J
John Bowler 已提交
896 897 898 899

         if (tRNS)
            png_set_tRNS(png_ptr, info_ptr, trans, npalette-1,
               NULL/*transparent color*/);
900 901 902 903 904 905

         /* Reset gamma_table to prevent the image rows being changed */
         for (npalette=0; npalette<256; ++npalette)
            gamma_table[npalette] = (png_byte)npalette;
      }

J
John Bowler 已提交
906 907 908 909 910 911 912 913 914 915
      else if (tRNS)
      {
         png_color_16 col;

         col.red = col.green = col.blue = col.gray =
            0x0101U & ((1U<<bit_depth)-1U);
         col.index = 0U;
         png_set_tRNS(png_ptr, info_ptr, NULL/*trans*/, 1U, &col);
      }

916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946
      if (gamma == PNG_DEFAULT_sRGB)
         png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);

      else if (gamma > 0) /* Else don't set color space information */
      {
         png_set_gAMA_fixed(png_ptr, info_ptr, real_gamma);

         /* Just use the sRGB values here. */
         png_set_cHRM_fixed(png_ptr, info_ptr,
            /* color      x       y */
            /* white */ 31270, 32900,
            /* red   */ 64000, 33000,
            /* green */ 30000, 60000,
            /* blue  */ 15000,  6000
         );
      }

      /* Insert extra information. */
      while (insert != NULL)
      {
         insert->insert(png_ptr, info_ptr, insert->nparams, insert->parameters);
         insert = insert->next;
      }

      /* Write the file header. */
      png_write_info(png_ptr, info_ptr);

      /* Restrict the filters */
      png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, filters);

      {
947 948 949 950 951
#        ifdef PNG_WRITE_INTERLACING_SUPPORTED
            int passes = png_set_interlace_handling(png_ptr);
#        else /* !WRITE_INTERLACING */
            int passes = 1;
#        endif /* !WRITE_INTERLACING */
952
         int pass;
953
         size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
954 955 956 957 958 959 960 961 962 963

         row = malloc(rowbytes);

         if (row == NULL)
            png_error(png_ptr, "OOM allocating row buffer");

         for (pass = 0; pass < passes; ++pass)
         {
            unsigned int y;

J
John Bowler 已提交
964
            for (y=0; y<ysize; ++y)
965
            {
J
John Bowler 已提交
966 967 968 969 970 971 972
               unsigned int row_filters =
                  generate_row(row, rowbytes, y, color_type, bit_depth,
                        gamma_table, conv, colors, small);

               if (row_filters != 0 && filters == PNG_ALL_FILTERS)
                  png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, row_filters);

973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
               png_write_row(png_ptr, row);
            }
         }
      }
   }

   /* Finish writing the file. */
   png_write_end(png_ptr, info_ptr);

   {
      png_structp nv_ptr = png_ptr;
      png_infop nv_info = info_ptr;

      png_ptr = NULL;
      info_ptr = NULL;
      png_destroy_write_struct(&nv_ptr, &nv_info);
   }
   free(row);
   return 0;
}


static size_t
load_file(png_const_charp name, png_bytepp result)
{
   FILE *fp = tmpfile();

   if (fp != NULL)
   {
      FILE *ip = fopen(name, "rb");

      if (ip != NULL)
      {
         size_t total = 0;
         int ch;

         for (;;)
         {
            ch = getc(ip);
            if (ch == EOF) break;
            putc(ch, fp);
            ++total;
         }

         if (ferror(ip))
         {
            perror(name);
            fprintf(stderr, "%s: read error\n", name);
            (void)fclose(ip);
         }

         else
         {
            (void)fclose(ip);

            if (ferror(fp))
            {
               perror("temporary file");
               fprintf(stderr, "temporary file write error\n");
            }

            else
            {
               rewind(fp);

               if (total > 0)
               {
                  /* Round up to a multiple of 4 here to allow an iCCP profile
                   * to be padded to a 4x boundary.
                   */
                  png_bytep data = malloc((total+3)&~3);

                  if (data != NULL)
                  {
                     size_t new_size = 0;

                     for (;;)
                     {
                        ch = getc(fp);
                        if (ch == EOF) break;
                        data[new_size++] = (png_byte)ch;
                     }

                     if (ferror(fp) || new_size != total)
                     {
                        perror("temporary file");
                        fprintf(stderr, "temporary file read error\n");
                        free(data);
                     }

                     else
                     {
                        (void)fclose(fp);
                        *result = data;
                        return total;
                     }
                  }

                  else
                     fprintf(stderr, "%s: out of memory loading file\n", name);
               }

               else
                  fprintf(stderr, "%s: empty file\n", name);
            }
         }
      }

      else
      {
         perror(name);
         fprintf(stderr, "%s: open failed\n", name);
      }

      fclose(fp);
   }

   else
      fprintf(stderr, "makepng: %s: could not open temporary file\n", name);

   exit(1);
   return 0;
}

1097
static size_t
1098 1099 1100
load_fake(png_charp param, png_bytepp profile)
{
   char *endptr = NULL;
J
John Bowler 已提交
1101
   uint64_t size = strtoull(param, &endptr, 0/*base*/);
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166

   /* The 'fake' format is <number>*[string] */
   if (endptr != NULL && *endptr == '*')
   {
      size_t len = strlen(++endptr);
      size_t result = (size_t)size;

      if (len == 0) len = 1; /* capture the terminating '\0' */

      /* Now repeat that string to fill 'size' bytes. */
      if (result == size && (*profile = malloc(result)) != NULL)
      {
         png_bytep out = *profile;

         if (len == 1)
            memset(out, *endptr, result);

         else
         {
            while (size >= len)
            {
               memcpy(out, endptr, len);
               out += len;
               size -= len;
            }
            memcpy(out, endptr, size);
         }

         return result;
      }

      else
      {
         fprintf(stderr, "%s: size exceeds system limits\n", param);
         exit(1);
      }
   }

   return 0;
}

static void
check_param_count(int nparams, int expect)
{
   if (nparams != expect)
   {
      fprintf(stderr, "bad parameter count (internal error)\n");
      exit(1);
   }
}

static void
insert_iCCP(png_structp png_ptr, png_infop info_ptr, int nparams,
   png_charpp params)
{
   png_bytep profile = NULL;
   png_uint_32 proflen = 0;
   int result;

   check_param_count(nparams, 2);

   switch (params[1][0])
   {
      case '<':
         {
1167
            size_t filelen = load_file(params[1]+1, &profile);
1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181
            if (filelen > 0xfffffffc) /* Maximum profile length */
            {
               fprintf(stderr, "%s: file too long (%lu) for an ICC profile\n",
                  params[1]+1, (unsigned long)filelen);
               exit(1);
            }

            proflen = (png_uint_32)filelen;
         }
         break;

      case '0': case '1': case '2': case '3': case '4':
      case '5': case '6': case '7': case '8': case '9':
         {
1182
            size_t fake_len = load_fake(params[1], &profile);
1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276

            if (fake_len > 0) /* else a simple parameter */
            {
               if (fake_len > 0xffffffff) /* Maximum profile length */
               {
                  fprintf(stderr,
                     "%s: fake data too long (%lu) for an ICC profile\n",
                     params[1], (unsigned long)fake_len);
                  exit(1);
               }
               proflen = (png_uint_32)(fake_len & ~3U);
               /* Always fix up the profile length. */
               png_save_uint_32(profile, proflen);
               break;
            }
         }

      default:
         fprintf(stderr, "--insert iCCP \"%s\": unrecognized\n", params[1]);
         fprintf(stderr, "  use '<' to read a file: \"<filename\"\n");
         exit(1);
   }

   result = 1;

   if (proflen & 3)
   {
      fprintf(stderr,
         "makepng: --insert iCCP %s: profile length made a multiple of 4\n",
         params[1]);

      /* load_file allocates extra space for this padding, the ICC spec requires
       * padding with zero bytes.
       */
      while (proflen & 3)
         profile[proflen++] = 0;
   }

   if (profile != NULL && proflen > 3)
   {
      png_uint_32 prof_header = png_get_uint_32(profile);

      if (prof_header != proflen)
      {
         fprintf(stderr, "--insert iCCP %s: profile length field wrong:\n",
            params[1]);
         fprintf(stderr, "  actual %lu, recorded value %lu (corrected)\n",
            (unsigned long)proflen, (unsigned long)prof_header);
         png_save_uint_32(profile, proflen);
      }
   }

   if (result && profile != NULL && proflen >=4)
      png_set_iCCP(png_ptr, info_ptr, params[0], PNG_COMPRESSION_TYPE_BASE,
         profile, proflen);

   if (profile)
      free(profile);

   if (!result)
      exit(1);
}

static void
clear_text(png_text *text, png_charp keyword)
{
   text->compression = -1; /* none */
   text->key = keyword;
   text->text = NULL;
   text->text_length = 0; /* libpng calculates this */
   text->itxt_length = 0; /* libpng calculates this */
   text->lang = NULL;
   text->lang_key = NULL;
}

static void
set_text(png_structp png_ptr, png_infop info_ptr, png_textp text,
   png_charp param)
{
   switch (param[0])
   {
      case '<':
         {
            png_bytep file = NULL;

            text->text_length = load_file(param+1, &file);
            text->text = (png_charp)file;
         }
         break;

      case '0': case '1': case '2': case '3': case '4':
      case '5': case '6': case '7': case '8': case '9':
         {
            png_bytep data = NULL;
1277
            size_t fake_len = load_fake(param, &data);
1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335

            if (fake_len > 0) /* else a simple parameter */
            {
               text->text_length = fake_len;
               text->text = (png_charp)data;
               break;
            }
         }

      default:
         text->text = param;
         break;
   }

   png_set_text(png_ptr, info_ptr, text, 1);

   if (text->text != param)
      free(text->text);
}

static void
insert_tEXt(png_structp png_ptr, png_infop info_ptr, int nparams,
   png_charpp params)
{
   png_text text;

   check_param_count(nparams, 2);
   clear_text(&text, params[0]);
   set_text(png_ptr, info_ptr, &text, params[1]);
}

static void
insert_zTXt(png_structp png_ptr, png_infop info_ptr, int nparams,
   png_charpp params)
{
   png_text text;

   check_param_count(nparams, 2);
   clear_text(&text, params[0]);
   text.compression = 0; /* deflate */
   set_text(png_ptr, info_ptr, &text, params[1]);
}

static void
insert_iTXt(png_structp png_ptr, png_infop info_ptr, int nparams,
   png_charpp params)
{
   png_text text;

   check_param_count(nparams, 4);
   clear_text(&text, params[0]);
   text.compression = 2; /* iTXt + deflate */
   text.lang = params[1];/* language tag */
   text.lang_key = params[2]; /* translated keyword */
   set_text(png_ptr, info_ptr, &text, params[3]);
}

static void
J
John Bowler 已提交
1336 1337
insert_hIST(png_structp png_ptr, png_infop info_ptr, int nparams,
      png_charpp params)
1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363
{
   int i;
   png_uint_16 freq[256];

   /* libpng takes the count from the PLTE count; we don't check it here but we
    * do set the array to 0 for unspecified entries.
    */
   memset(freq, 0, sizeof freq);
   for (i=0; i<nparams; ++i)
   {
      char *endptr = NULL;
      unsigned long int l = strtoul(params[i], &endptr, 0/*base*/);

      if (params[i][0] && *endptr == 0 && l <= 65535)
         freq[i] = (png_uint_16)l;

      else
      {
         fprintf(stderr, "hIST[%d]: %s: invalid frequency\n", i, params[i]);
         exit(1);
      }
   }

   png_set_hIST(png_ptr, info_ptr, freq);
}

J
John Bowler 已提交
1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380
static png_byte
bval(png_const_structrp png_ptr, png_charp param, unsigned int maxval)
{
   char *endptr = NULL;
   unsigned long int l = strtoul(param, &endptr, 0/*base*/);

   if (param[0] && *endptr == 0 && l <= maxval)
      return (png_byte)l;

   else
      png_error(png_ptr, "sBIT: invalid sBIT value");
}

static void
insert_sBIT(png_structp png_ptr, png_infop info_ptr, int nparams,
      png_charpp params)
{
1381 1382
   int ct = png_get_color_type(png_ptr, info_ptr);
   int c = (ct & PNG_COLOR_MASK_COLOR ? 3 : 1) +
J
John Bowler 已提交
1383
      (ct & PNG_COLOR_MASK_ALPHA ? 1 : 0);
1384
   unsigned int maxval =
J
John Bowler 已提交
1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413
      ct & PNG_COLOR_MASK_PALETTE ? 8U : png_get_bit_depth(png_ptr, info_ptr);
   png_color_8 sBIT;

   if (nparams != c)
      png_error(png_ptr, "sBIT: incorrect parameter count");

   if (ct & PNG_COLOR_MASK_COLOR)
   {
      sBIT.red = bval(png_ptr, params[0], maxval);
      sBIT.green = bval(png_ptr, params[1], maxval);
      sBIT.blue = bval(png_ptr, params[2], maxval);
      sBIT.gray = 42;
   }

   else
   {
      sBIT.red = sBIT.green = sBIT.blue = 42;
      sBIT.gray = bval(png_ptr, params[0], maxval);
   }

   if (ct & PNG_COLOR_MASK_ALPHA)
      sBIT.alpha = bval(png_ptr, params[nparams-1], maxval);

   else
      sBIT.alpha = 42;

   png_set_sBIT(png_ptr, info_ptr, &sBIT);
}

1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540
#if 0
static void
insert_sPLT(png_structp png_ptr, png_infop info_ptr, int nparams, png_charpp params)
{
   fprintf(stderr, "insert sPLT: NYI\n");
}
#endif

static int
find_parameters(png_const_charp what, png_charp param, png_charp *list,
   int nparams)
{
   /* Parameters are separated by '\n' or ':' characters, up to nparams are
    * accepted (more is an error) and the number found is returned.
    */
   int i;
   for (i=0; *param && i<nparams; ++i)
   {
      list[i] = param;
      while (*++param) if (*param == '\n' || *param == ':')
      {
         *param++ = 0; /* Terminate last parameter */
         break;        /* And start a new one. */
      }
   }

   if (*param)
   {
      fprintf(stderr, "--insert %s: too many parameters (%s)\n", what, param);
      exit(1);
   }

   list[i] = NULL; /* terminates list */
   return i; /* number of parameters filled in */
}

static void
bad_parameter_count(png_const_charp what, int nparams)
{
   fprintf(stderr, "--insert %s: bad parameter count %d\n", what, nparams);
   exit(1);
}

static chunk_insert *
make_insert(png_const_charp what,
   void (*insert)(png_structp, png_infop, int, png_charpp),
   int nparams, png_charpp list)
{
   int i;
   chunk_insert *cip;

   cip = malloc(offsetof(chunk_insert,parameters) +
      nparams * sizeof (png_charp));

   if (cip == NULL)
   {
      fprintf(stderr, "--insert %s: out of memory allocating %d parameters\n",
         what, nparams);
      exit(1);
   }

   cip->next = NULL;
   cip->insert = insert;
   cip->nparams = nparams;
   for (i=0; i<nparams; ++i)
      cip->parameters[i] = list[i];

   return cip;
}

static chunk_insert *
find_insert(png_const_charp what, png_charp param)
{
   png_uint_32 chunk = 0;
   png_charp parameter_list[1024];
   int i, nparams;

   /* Assemble the chunk name */
   for (i=0; i<4; ++i)
   {
      char ch = what[i];

      if ((ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122))
         chunk = (chunk << 8) + what[i];

      else
         break;
   }

   if (i < 4 || what[4] != 0)
   {
      fprintf(stderr, "makepng --insert \"%s\": invalid chunk name\n", what);
      exit(1);
   }

   /* Assemble the parameter list. */
   nparams = find_parameters(what, param, parameter_list, 1024);

#  define CHUNK(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))

   switch (chunk)
   {
      case CHUNK(105,67,67,80):  /* iCCP */
         if (nparams == 2)
            return make_insert(what, insert_iCCP, nparams, parameter_list);
         break;

      case CHUNK(116,69,88,116): /* tEXt */
         if (nparams == 2)
            return make_insert(what, insert_tEXt, nparams, parameter_list);
         break;

      case CHUNK(122,84,88,116): /* zTXt */
         if (nparams == 2)
            return make_insert(what, insert_zTXt, nparams, parameter_list);
         break;

      case CHUNK(105,84,88,116): /* iTXt */
         if (nparams == 4)
            return make_insert(what, insert_iTXt, nparams, parameter_list);
         break;

      case CHUNK(104,73,83,84):  /* hIST */
         if (nparams <= 256)
            return make_insert(what, insert_hIST, nparams, parameter_list);
         break;

J
John Bowler 已提交
1541 1542 1543 1544 1545
      case CHUNK(115,66,73,84): /* sBIT */
         if (nparams <= 4)
            return make_insert(what, insert_sBIT, nparams, parameter_list);
         break;

1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560
#if 0
      case CHUNK(115,80,76,84):  /* sPLT */
         return make_insert(what, insert_sPLT, nparams, parameter_list);
#endif

      default:
         fprintf(stderr, "makepng --insert \"%s\": unrecognized chunk name\n",
            what);
         exit(1);
   }

   bad_parameter_count(what, nparams);
   return NULL;
}

J
John Bowler 已提交
1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634
/* This is necessary because libpng expects writeable strings for things like
 * text chunks (maybe this should be fixed...)
 */
static png_charp
strstash(png_const_charp foo)
{
   /* The program indicates a memory allocation error by crashing, this is by
    * design.
    */
   if (foo != NULL)
   {
      png_charp bar = malloc(strlen(foo)+1);
      return strcpy(bar, foo);
   }

   return NULL;
}

static png_charp
strstash_list(const png_const_charp *text)
{
   size_t foo = 0;
   png_charp result, bar;
   const png_const_charp *line = text;

   while (*line != NULL)
      foo += strlen(*line++);

   result = bar = malloc(foo+1);

   line = text;
   while (*line != NULL)
   {
      foo = strlen(*line);
      memcpy(bar, *line++, foo);
      bar += foo;
   }

   *bar = 0;
   return result;
}

/* These are used to insert Copyright and Licence fields, they allow the text to
 * have \n unlike the --insert option.
 */
static chunk_insert *
add_tEXt(const char *key, const png_const_charp *text)
{
   static char what[5] = { 116, 69, 88, 116, 0 };
   png_charp parameter_list[3];

   parameter_list[0] = strstash(key);
   parameter_list[1] = strstash_list(text);
   parameter_list[2] = NULL;

   return make_insert(what, insert_tEXt, 2, parameter_list);
}

static chunk_insert *
add_iTXt(const char *key, const char *language, const char *language_key,
      const png_const_charp *text)
{
   static char what[5] = { 105, 84, 88, 116, 0 };
   png_charp parameter_list[5];

   parameter_list[0] = strstash(key);
   parameter_list[1] = strstash(language);
   parameter_list[2] = strstash(language_key);
   parameter_list[3] = strstash_list(text);
   parameter_list[4] = NULL;

   return make_insert(what, insert_iTXt, 4, parameter_list);
}

1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683
/* This is a not-very-good parser for a sequence of numbers (including 0).  It
 * doesn't accept some apparently valid things, but it accepts all the sensible
 * combinations.
 */
static void
parse_color(char *arg, unsigned int *colors)
{
   unsigned int ncolors = 0;

   while (*arg && ncolors < 4)
   {
      char *ep = arg;

      unsigned long ul = strtoul(arg, &ep, 0);

      if (ul > 65535)
      {
         fprintf(stderr, "makepng --color=...'%s': too big\n", arg);
         exit(1);
      }

      if (ep == arg)
      {
         fprintf(stderr, "makepng --color=...'%s': not a valid color\n", arg);
         exit(1);
      }

      if (*ep) ++ep; /* skip a separator */
      arg = ep;

      colors[++ncolors] = (unsigned int)ul; /* checked above */
   }

   if (*arg)
   {
      fprintf(stderr, "makepng --color=...'%s': too many values\n", arg);
      exit(1);
   }

   *colors = ncolors;
}

int
main(int argc, char **argv)
{
   FILE *fp = stdout;
   const char *file_name = NULL;
   int color_type = 8; /* invalid */
   int bit_depth = 32; /* invalid */
J
John Bowler 已提交
1684 1685
   int small = 0; /* make full size images */
   int tRNS = 0; /* don't output a tRNS chunk */
1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697
   unsigned int colors[5];
   unsigned int filters = PNG_ALL_FILTERS;
   png_fixed_point gamma = 0; /* not set */
   chunk_insert *head_insert = NULL;
   chunk_insert **insert_ptr = &head_insert;

   memset(colors, 0, sizeof colors);

   while (--argc > 0)
   {
      char *arg = *++argv;

J
John Bowler 已提交
1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709
      if (strcmp(arg, "--small") == 0)
      {
         small = 1;
         continue;
      }

      if (strcmp(arg, "--tRNS") == 0)
      {
         tRNS = 1;
         continue;
      }

1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849
      if (strcmp(arg, "--sRGB") == 0)
      {
         gamma = PNG_DEFAULT_sRGB;
         continue;
      }

      if (strcmp(arg, "--linear") == 0)
      {
         gamma = PNG_FP_1;
         continue;
      }

      if (strcmp(arg, "--1.8") == 0)
      {
         gamma = PNG_GAMMA_MAC_18;
         continue;
      }

      if (strcmp(arg, "--nofilters") == 0)
      {
         filters = PNG_FILTER_NONE;
         continue;
      }

      if (strncmp(arg, "--color=", 8) == 0)
      {
          parse_color(arg+8, colors);
          continue;
      }

      if (argc >= 3 && strcmp(arg, "--insert") == 0)
      {
         png_const_charp what = *++argv;
         png_charp param = *++argv;
         chunk_insert *new_insert;

         argc -= 2;

         new_insert = find_insert(what, param);

         if (new_insert != NULL)
         {
            *insert_ptr = new_insert;
            insert_ptr = &new_insert->next;
         }

         continue;
      }

      if (arg[0] == '-')
      {
         fprintf(stderr, "makepng: %s: invalid option\n", arg);
         exit(1);
      }

      if (strcmp(arg, "palette") == 0)
      {
         color_type = PNG_COLOR_TYPE_PALETTE;
         continue;
      }

      if (strncmp(arg, "gray", 4) == 0)
      {
         if (arg[4] == 0)
         {
            color_type = PNG_COLOR_TYPE_GRAY;
            continue;
         }

         else if (strcmp(arg+4, "a") == 0 ||
            strcmp(arg+4, "alpha") == 0 ||
            strcmp(arg+4, "-alpha") == 0)
         {
            color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
            continue;
         }
      }

      if (strncmp(arg, "rgb", 3) == 0)
      {
         if (arg[3] == 0)
         {
            color_type = PNG_COLOR_TYPE_RGB;
            continue;
         }

         else if (strcmp(arg+3, "a") == 0 ||
            strcmp(arg+3, "alpha") == 0 ||
            strcmp(arg+3, "-alpha") == 0)
         {
            color_type = PNG_COLOR_TYPE_RGB_ALPHA;
            continue;
         }
      }

      if (color_type == 8 && isdigit(arg[0]))
      {
         color_type = atoi(arg);
         if (color_type < 0 || color_type > 6 || color_type == 1 ||
            color_type == 5)
         {
            fprintf(stderr, "makepng: %s: not a valid color type\n", arg);
            exit(1);
         }

         continue;
      }

      if (bit_depth == 32 && isdigit(arg[0]))
      {
         bit_depth = atoi(arg);
         if (bit_depth <= 0 || bit_depth > 16 ||
            (bit_depth & -bit_depth) != bit_depth)
         {
            fprintf(stderr, "makepng: %s: not a valid bit depth\n", arg);
            exit(1);
         }

         continue;
      }

      if (argc == 1) /* It's the file name */
      {
         fp = fopen(arg, "wb");
         if (fp == NULL)
         {
            fprintf(stderr, "%s: %s: could not open\n", arg, strerror(errno));
            exit(1);
         }

         file_name = arg;
         continue;
      }

      fprintf(stderr, "makepng: %s: unknown argument\n", arg);
      exit(1);
   } /* argument while loop */

   if (color_type == 8 || bit_depth == 32)
   {
J
John Bowler 已提交
1850
      fprintf(stderr, "usage: makepng [--small] [--sRGB|--linear|--1.8] "
1851
         "[--color=...] color-type bit-depth [file-name]\n"
J
John Bowler 已提交
1852 1853
         "  Make a test PNG file, by default writes to stdout.\n"
         "  Other options are available, UTSL.\n");
1854 1855 1856 1857 1858
      exit(1);
   }

   /* Check the colors */
   {
1859
      unsigned int lim = (color_type == PNG_COLOR_TYPE_PALETTE ? 255U :
1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871
         (1U<<bit_depth)-1);
      unsigned int i;

      for (i=1; i<=colors[0]; ++i)
         if (colors[i] > lim)
         {
            fprintf(stderr, "makepng: --color=...: %u out of range [0..%u]\n",
               colors[i], lim);
            exit(1);
         }
   }

J
John Bowler 已提交
1872 1873 1874 1875 1876 1877 1878 1879 1880
   /* small and colors are incomparible (will probably crash if both are used at
    * the same time!)
    */
   if (small && colors[0] != 0)
   {
      fprintf(stderr, "makepng: --color --small: only one at a time!\n");
      exit(1);
   }

1881 1882 1883
   /* Restrict the filters for more speed to those we know are used for the
    * generated images.
    */
J
John Bowler 已提交
1884
   if (filters == PNG_ALL_FILTERS && !small/*small provides defaults*/)
1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901
   {
      if ((color_type & PNG_COLOR_MASK_PALETTE) != 0 || bit_depth < 8)
         filters = PNG_FILTER_NONE;

      else if (color_type & PNG_COLOR_MASK_COLOR) /* rgb */
      {
         if (bit_depth == 8)
            filters &= ~(PNG_FILTER_NONE | PNG_FILTER_AVG);

         else
            filters = PNG_FILTER_SUB | PNG_FILTER_PAETH;
      }

      else /* gray 8 or 16-bit */
         filters &= ~PNG_FILTER_NONE;
   }

J
John Bowler 已提交
1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915
   /* Insert standard copyright and licence text. */
   {
      static png_const_charp copyright[] =
      {
         COPYRIGHT, /* ISO-Latin-1 */
         NULL
      };
      static png_const_charp licensing[] =
      {
         IMAGE_LICENSING, /* UTF-8 */
         NULL
      };

      chunk_insert *new_insert;
1916

J
John Bowler 已提交
1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931
      new_insert = add_tEXt("Copyright", copyright);
      if (new_insert != NULL)
      {
         *insert_ptr = new_insert;
         insert_ptr = &new_insert->next;
      }

      new_insert = add_iTXt("Licensing", "en", NULL, licensing);
      if (new_insert != NULL)
      {
         *insert_ptr = new_insert;
         insert_ptr = &new_insert->next;
      }
   }

1932 1933
   {
      int ret = write_png(&file_name, fp, color_type, bit_depth, gamma,
J
John Bowler 已提交
1934
         head_insert, filters, colors, small, tRNS);
1935 1936 1937 1938 1939 1940 1941

      if (ret != 0 && file_name != NULL)
         remove(file_name);

      return ret;
   }
}