~ubuntu-branches/ubuntu/wily/pngnq/wily

« back to all changes in this revision

Viewing changes to 0.4.1/pngnq.c

  • Committer: Bazaar Package Importer
  • Author(s): Nelson A. de Oliveira
  • Date: 2008-07-25 23:16:03 UTC
  • mfrom: (3.1.2 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080725231603-7zrq9thqlddnj5fq
Tags: 0.5-3
* Fix segmentation fault when printing the version number (Closes: #491860).
  Thanks Johan Thelmén!
* Bumped Standards-Version to 3.8.0 (no changes needed);
* Using debhelper compat level 7.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* pngnq.c - quantize the colors in an alphamap down to 256 using 
2
 
**  the Neuquant algorithm.
3
 
**
4
 
** Based on Greg Roelf's pngquant which was itself based on Jef Poskanzer's ppmquant.
5
 
** Uses Anthony Dekker's Neuquant algorithm extended to handle the alpha channel.
6
 
** 
7
 
**
8
 
** Copyright (C) 1989, 1991 by Jef Poskanzer.
9
 
** Copyright (C) 1997, 2000, 2002 by Greg Roelofs; based on an idea by
10
 
**                                Stefan Schneider.
11
 
** Copyright (C) 2004-2006 by Stuart Coyle
12
 
** 
13
 
** Permission to use, copy, modify, and distribute this software and its
14
 
** documentation for any purpose and without fee is hereby granted, provided
15
 
** that the above copyright notice appear in all copies and that both that
16
 
** copyright notice and this permission notice appear in supporting
17
 
** documentation.  This software is provided "as is" without express or
18
 
** implied warranty.
19
 
*/
20
 
 
21
 
/* NeuQuant Neural-Net Quantization Algorithm
22
 
 * ------------------------------------------
23
 
 *
24
 
 * Copyright (c) 1994 Anthony Dekker
25
 
 *
26
 
 * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994.
27
 
 * See "Kohonen neural networks for optimal colour quantization"
28
 
 * in "Network: Computation in Neural Systems" Vol. 5 (1994) pp 351-367.
29
 
 * for a discussion of the algorithm.
30
 
 * See also  http://members.ozemail.com.au/~dekker/NEUQUANT.HTML
31
 
 *
32
 
 * Any party obtaining a copy of these files from the author, directly or
33
 
 * indirectly, is granted, free of charge, a full and unrestricted irrevocable,
34
 
 * world-wide, paid up, royalty-free, nonexclusive right and license to deal
35
 
 * in this software and documentation files (the "Software"), including without
36
 
 * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
37
 
 * and/or sell copies of the Software, and to permit persons who receive
38
 
 * copies from any such party to do so, with the only requirement being
39
 
 * that this copyright notice remain intact.
40
 
 *
41
 
 */ 
42
 
 
43
 
#define VERSION "0.4"
44
 
#define FNMAX 1024
45
 
#define PNGNQ_USAGE "\
46
 
  usage:  pngnq [-vfhV][-s sample factor][-n colours][input files]\n\
47
 
  options:\n\
48
 
     -v Verbose mode. Prints status messages.\n\
49
 
     -f Force ovewriting of files.\n\
50
 
     -s Sample factor. The neuquant algorithm samples pixels stepping by this value.\n\
51
 
     -n Number of colours the quantized image is to contain. Range: 2 to 256. Defaults to 256.\n\
52
 
     input files: The png files to be processed. Defaults to standard input if not specified.\n\n\
53
 
     -V Print version number and library versions.\n\
54
 
     -h Print this help.\n\n\
55
 
  Quantizes a 32-bit RGBA PNG image to an 8 bit RGBA palette PNG\n\
56
 
  using the neuquant algorithm. The output file name is the input file name\n\
57
 
  extended with \"-nq8.png\"\n"
58
 
 
59
 
#include <stdlib.h>
60
 
#include <stdio.h>
61
 
#include <string.h>
62
 
#include <unistd.h>
63
 
#include <ctype.h> /* isprint() */
64
 
 
65
 
/* N.B. I haven't yet checked if this actually compiles on W32 - Stu 
66
 
   getopt will probably be an issue. 
67
 
*/
68
 
 
69
 
#ifdef WIN32            /* defined in Makefile.w32 (or use _MSC_VER for MSVC) */
70
 
#  include <fcntl.h>    /* O_BINARY */
71
 
#  include <io.h>       /* setmode() */
72
 
#  include <getopt.h> 
73
 
#endif
74
 
 
75
 
#include "png.h"
76
 
#include "neuquant32.h"
77
 
#include "rwpng.h"
78
 
 
79
 
typedef struct {
80
 
  uch r, g, b, a;
81
 
} pixel;
82
 
 
83
 
/* Image information struct */
84
 
static mainprog_info rwpng_info;
85
 
 
86
 
 
87
 
static int pngnq(char* filename, char* newext,
88
 
                 int sample_factor, int n_colors, int verbose,  
89
 
                 int using_stdin, int force);
90
 
 
91
 
int main(int argc, char** argv)
92
 
{
93
 
  int verbose = 0;
94
 
  int force = 0;
95
 
  int sample_factor = 3; /* This is a reasonable default */
96
 
 
97
 
  char *input_file_name = NULL;
98
 
  char *output_file_extension = "-nq8.png";
99
 
 
100
 
  int using_stdin = FALSE;
101
 
  int c; /* argument count */
102
 
 
103
 
  int errors = 0, file_count =0;
104
 
  int retval;
105
 
  int n_colours = 256; /* number of colours to quantize to. Default 256 */
106
 
 
107
 
  /* TODO add long options */
108
 
  /* add --version and --help */
109
 
 
110
 
  /* Parse arguments */
111
 
  while((c = getopt(argc,argv,"hVvfn:s:"))!=-1){
112
 
    switch(c){
113
 
    case 's':
114
 
      sample_factor = atoi(optarg);
115
 
      break;
116
 
    case 'v':
117
 
      verbose = 1;
118
 
      break;
119
 
    case 'f':
120
 
      force = 1;
121
 
      break;
122
 
    case 'V':
123
 
      fprintf(stderr, "pngnq %s\n",VERSION);
124
 
      rwpng_version_info();
125
 
      exit(EXIT_SUCCESS);
126
 
      break;
127
 
    case 'h':
128
 
      fprintf(stderr,PNGNQ_USAGE);
129
 
      exit(EXIT_SUCCESS);
130
 
      break;
131
 
    case 'n':
132
 
      n_colours = atoi(optarg);
133
 
      if(n_colours > 256){
134
 
        fprintf (stderr, "  -n option requested %d colors.\n  PNG indexed images cannot contain more than 256 colours.\n  Setting the number of colours to 256!\n",n_colours);
135
 
        n_colours = 256;
136
 
      }else if(n_colours<2){
137
 
        fprintf(stderr,"  -n option requested %d colors, which is silly.\n  Setting number of colors to the minimum value of 1!\n",n_colours);
138
 
        n_colours = 1;      
139
 
      }
140
 
      break;
141
 
    case '?':      
142
 
      if (isprint(optopt))
143
 
        fprintf (stderr, "  unknown option `-%c'.\n", optopt);
144
 
      else
145
 
        fprintf (stderr,
146
 
                 "  unknown option character `\\x%x'.\n",
147
 
                 optopt);
148
 
    default:
149
 
      fprintf(stderr,PNGNQ_USAGE);
150
 
      exit(EXIT_FAILURE);
151
 
    }
152
 
  }
153
 
 
154
 
  if(sample_factor<1){
155
 
    fprintf (stderr, "  sample factor must be 1 or greater. Default is 3.\n");
156
 
    exit(EXIT_FAILURE);
157
 
  }
158
 
 
159
 
  /* determine input files */
160
 
  if(optind == argc){
161
 
    using_stdin = TRUE;
162
 
    input_file_name = "stdin";
163
 
  }
164
 
  else{
165
 
    input_file_name=argv[optind];
166
 
    optind++;
167
 
  }
168
 
                
169
 
  /* Process each input file */
170
 
  while(optind<=argc){
171
 
 
172
 
    if(verbose){
173
 
      fprintf(stderr,"  quantizing: %s \n",input_file_name);
174
 
      fflush(stderr);
175
 
    }
176
 
                
177
 
    retval = pngnq(input_file_name, output_file_extension, 
178
 
                   sample_factor, n_colours, verbose, using_stdin,force);
179
 
 
180
 
    if(retval){
181
 
      errors++;
182
 
    }
183
 
                       
184
 
    input_file_name=argv[optind];
185
 
    file_count++;
186
 
    optind++;
187
 
  }
188
 
 
189
 
  if (verbose) {
190
 
    if (errors)
191
 
      fprintf(stderr, "There were errors quantizing %d file%s out of a"
192
 
              " total of %d file%s.\n",
193
 
              errors, (errors == 1)? "" : "s",
194
 
              file_count, (file_count == 1)? "" : "s");
195
 
    else
196
 
      fprintf(stderr, "No errors detected while quantizing %d image%s.\n",
197
 
              file_count, (file_count == 1)? "" : "s");
198
 
    fflush(stderr);
199
 
  }
200
 
 
201
 
  exit(errors);
202
 
 
203
 
}
204
 
 
205
 
static int pngnq(char* filename, char* newext, 
206
 
                 int sample_factor, int n_colours, int verbose, 
207
 
                 int using_stdin, int force)
208
 
{
209
 
  char outname[FNMAX];
210
 
  FILE *infile = NULL;
211
 
  FILE *outfile = NULL;
212
 
 
213
 
  int bot_idx, top_idx; /* for remapping of indices */
214
 
  int remap[MAXNETSIZE];
215
 
 
216
 
  ulg cols, rows;
217
 
  ulg row;
218
 
  unsigned char map[MAXNETSIZE][4];
219
 
  int i,x;
220
 
 
221
 
  uch *outrow = NULL; /* Output image pixels */
222
 
  uch **row_pointers=NULL; /* Pointers to rows of pixels */
223
 
  int newcolors = n_colours;
224
 
 
225
 
  if(using_stdin){      
226
 
#if defined(MSDOS) || defined(FLEXOS) || defined(OS2) || defined(WIN32)
227
 
#if (defined(__HIGHC__) && !defined(FLEXOS))
228
 
    setmode(stdin, _BINARY);
229
 
#else
230
 
    setmode(0, O_BINARY);
231
 
#endif
232
 
#endif
233
 
    infile=stdin;
234
 
  }
235
 
 
236
 
  /* Open input file. */
237
 
  else{
238
 
    if((infile = fopen(filename, "rb"))==NULL){
239
 
      fprintf(stderr,"  error: cannot open %s for reading.",filename);
240
 
      fflush(stderr);
241
 
      return 14;
242
 
    }
243
 
  }
244
 
        
245
 
  if(using_stdin){ 
246
 
#if defined(MSDOS) || defined(FLEXOS) || defined(OS2) || defined(WIN32)
247
 
#if (defined(__HIGHC__) && !defined(FLEXOS))
248
 
    setmode(stdout, _BINARY);
249
 
#else
250
 
    setmode(1, O_BINARY);
251
 
#endif
252
 
#endif
253
 
    outfile = stdout;
254
 
  }
255
 
  else{ 
256
 
 
257
 
    /* build the output filename from the input name by inserting "-nq8"
258
 
     * before the ".png" extension (or by appending that plus ".png" if
259
 
     * there isn't any extension), then make sure it doesn't exist already */
260
 
    x = strlen(filename);
261
 
    if (x > FNMAX-9) {
262
 
      fprintf(stderr,
263
 
              "  warning:  base filename [%s] will be truncated\n", filename);
264
 
      fflush(stderr);
265
 
      x = FNMAX-9;
266
 
    }
267
 
    strncpy(outname, filename, x);
268
 
    if (strncmp(outname+x-4, ".png", 4) == 0) 
269
 
      strcpy(outname+x-4, newext);
270
 
    else
271
 
      strcpy(outname+x, newext);
272
 
    if (!force) {
273
 
      if ((outfile = fopen(outname, "rb")) != NULL) {
274
 
        fprintf(stderr, "  error:  %s exists; not overwriting\n",
275
 
                outname);
276
 
        fflush(stderr);
277
 
        fclose(outfile);
278
 
        return 15;
279
 
      }
280
 
    }
281
 
 
282
 
    if ((outfile = fopen(outname, "wb")) == NULL) {
283
 
      fprintf(stderr, "  error:  cannot open %s for writing\n", outname);
284
 
      fflush(stderr);
285
 
      return 16;
286
 
    }
287
 
  }
288
 
 
289
 
 
290
 
  /* Read input file */
291
 
  rwpng_read_image(infile, &rwpng_info);
292
 
  if (!using_stdin)
293
 
    fclose(infile);
294
 
 
295
 
  if (rwpng_info.retval) {
296
 
    fprintf(stderr, "  rwpng_read_image() error\n");
297
 
    fflush(stderr);
298
 
    if (!using_stdin)
299
 
      fclose(outfile);
300
 
    return(rwpng_info.retval); 
301
 
  }
302
 
 
303
 
  cols = rwpng_info.width;
304
 
  rows = rwpng_info.height;
305
 
 
306
 
  if(!rwpng_info.rgba_data)
307
 
    {
308
 
      fprintf(stderr,"  no pixel data found.");
309
 
    }
310
 
 
311
 
  /* Start neuquant */
312
 
  initnet((unsigned char*)rwpng_info.rgba_data,rows*cols*4,sample_factor,newcolors);
313
 
  learn(verbose);
314
 
  unbiasnet();
315
 
  getcolormap((unsigned char*)map);
316
 
  inxbuild(); 
317
 
 
318
 
  /* Remap indexes so all tRNS chunks are together */
319
 
  if (verbose) {
320
 
    fprintf(stderr,
321
 
            "  remapping colormap to eliminate opaque tRNS-chunk entries...");
322
 
    fflush(stderr);
323
 
  }
324
 
  for (top_idx = newcolors-1, bot_idx = x = 0;  x < newcolors;  ++x) {
325
 
    if (map[x][3] == 255) /* maxval */
326
 
      remap[x] = top_idx--;
327
 
    else
328
 
      remap[x] = bot_idx++;
329
 
  }
330
 
  if (verbose) {
331
 
    fprintf(stderr, "%d entr%s left\n", bot_idx,
332
 
            (bot_idx == 1)? "y" : "ies");
333
 
    fflush(stderr);
334
 
  }
335
 
 
336
 
  /* sanity check:  top and bottom indices should have just crossed paths */
337
 
  if (bot_idx != top_idx + 1) {
338
 
    fprintf(stderr,
339
 
            "  internal logic error: remapped bot_idx = %d, top_idx = %d\n",
340
 
            bot_idx, top_idx);
341
 
    fflush(stderr);
342
 
    if (rwpng_info.row_pointers)
343
 
      free(rwpng_info.row_pointers);
344
 
    if (rwpng_info.rgba_data)
345
 
      free(rwpng_info.rgba_data);
346
 
    if (!using_stdin)
347
 
      fclose(outfile);
348
 
    return 18;
349
 
  }
350
 
 
351
 
  rwpng_info.sample_depth = 8;
352
 
  rwpng_info.num_palette = newcolors;
353
 
  rwpng_info.num_trans = bot_idx;
354
 
 
355
 
  /* GRR TO DO:  if bot_idx == 0, check whether all RGB samples are gray
356
 
     and if so, whether grayscale sample_depth would be same
357
 
     => skip following palette section and go grayscale */
358
 
 
359
 
  /* Remap and make palette entries */
360
 
  for (x = 0; x < newcolors; ++x) {
361
 
    rwpng_info.palette[remap[x]].red  = map[x][0];
362
 
    rwpng_info.palette[remap[x]].green = map[x][1];
363
 
    rwpng_info.palette[remap[x]].blue = map[x][2];
364
 
    rwpng_info.trans[remap[x]] = map[x][3];
365
 
  }
366
 
 
367
 
  /* Allocate memory*/
368
 
  if (rwpng_info.interlaced) {
369
 
    if ((rwpng_info.indexed_data = (uch *)malloc(rows * cols)) != NULL) {
370
 
      if ((row_pointers = (uch **)malloc(rows * sizeof(uch *))) != NULL)                                
371
 
        for (row = 0;  (ulg)row < rows;  ++row)
372
 
          row_pointers[row] = rwpng_info.indexed_data + row*cols;
373
 
    }
374
 
  } else rwpng_info.indexed_data = (uch *)malloc(cols);
375
 
        
376
 
  if (rwpng_info.indexed_data == NULL ||
377
 
      (rwpng_info.interlaced && row_pointers == NULL))
378
 
    {
379
 
      fprintf(stderr,
380
 
              "  insufficient memory for indexed data and/or row pointers\n");
381
 
      fflush(stderr);
382
 
      if (rwpng_info.row_pointers)
383
 
        free(rwpng_info.row_pointers);
384
 
      if (rwpng_info.rgba_data)
385
 
        free(rwpng_info.rgba_data);
386
 
      if (rwpng_info.indexed_data)
387
 
        free(rwpng_info.indexed_data);
388
 
      if (!using_stdin)
389
 
        fclose(outfile);
390
 
      return 17;
391
 
    }   
392
 
 
393
 
  /* Write headers and such. */
394
 
  if (rwpng_write_image_init(outfile, &rwpng_info) != 0) {
395
 
    fprintf( stderr, "  rwpng_write_image_init() error\n" );
396
 
    fflush( stderr );
397
 
    if (rwpng_info.rgba_data)
398
 
      free(rwpng_info.rgba_data);
399
 
    if (rwpng_info.row_pointers)
400
 
      free(rwpng_info.row_pointers);
401
 
    if (rwpng_info.indexed_data)
402
 
      free(rwpng_info.indexed_data);
403
 
    if (row_pointers)
404
 
      free(row_pointers);
405
 
    if (!using_stdin)
406
 
      fclose(outfile);
407
 
    return rwpng_info.retval;
408
 
  }
409
 
 
410
 
  /* Do each image row */
411
 
  for ( row = 0; (ulg)row < rows; ++row ) {
412
 
    int offset;
413
 
    outrow = rwpng_info.interlaced? row_pointers[row] :
414
 
      rwpng_info.indexed_data;
415
 
    /* Assign the new colors */
416
 
    offset = row*cols*4;
417
 
    for( i=0;i<cols;i++){
418
 
      outrow[i] = remap[inxsearch(rwpng_info.rgba_data[i*4+offset+3],
419
 
                                  rwpng_info.rgba_data[i*4+offset+2],
420
 
                                  rwpng_info.rgba_data[i*4+offset+1],
421
 
                                  rwpng_info.rgba_data[i*4+offset])];
422
 
    }
423
 
                
424
 
    /* if non-interlaced PNG, write row now */
425
 
    if (!rwpng_info.interlaced)
426
 
      rwpng_write_image_row(&rwpng_info);
427
 
  }
428
 
 
429
 
  /* now we're done with the INPUT data and row_pointers, so free 'em */
430
 
  if (rwpng_info.rgba_data) {
431
 
    free(rwpng_info.rgba_data);
432
 
    rwpng_info.rgba_data = NULL;
433
 
  }
434
 
  if (rwpng_info.row_pointers) {
435
 
    free(rwpng_info.row_pointers);
436
 
    rwpng_info.row_pointers = NULL;
437
 
  }
438
 
 
439
 
  /* write entire interlaced palette PNG, or finish/flush noninterlaced one */
440
 
  if (rwpng_info.interlaced) {
441
 
    rwpng_info.row_pointers = row_pointers;   /* now for OUTPUT data */
442
 
    rwpng_write_image_whole(&rwpng_info);
443
 
  } else rwpng_write_image_finish(&rwpng_info);
444
 
 
445
 
  if (!using_stdin)
446
 
    fclose(outfile);
447
 
 
448
 
  /* now we're done with the OUTPUT data and row_pointers, too */
449
 
  if (rwpng_info.indexed_data) {
450
 
    free(rwpng_info.indexed_data);
451
 
    rwpng_info.indexed_data = NULL;
452
 
  }
453
 
  if (row_pointers) {
454
 
    free(row_pointers);
455
 
    row_pointers = rwpng_info.row_pointers = NULL;
456
 
  }
457
 
        
458
 
  return 0;
459
 
}
460