1 /*****************************************************************************
2                                 pnmtojpeg
3 ******************************************************************************
4   This program is part of the Netpbm package.
5 
6   This program converts from the PNM formats to the JFIF format
7   which is based on JPEG.
8 
9   This program is by Bryan Henderson on 2000.03.06, but is derived
10   with permission from the program cjpeg, which is in the Independent
11   Jpeg Group's JPEG library package.  Under the terms of that permission,
12   redistribution of this software is restricted as described in the
13   file README.JPEG.
14 
15   Copyright (C) 1991-1998, Thomas G. Lane.
16 
17 *****************************************************************************/
18 
19 #define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
20 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
21 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
22 
23 #include <ctype.h>		/* to declare isdigit(), etc. */
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <stdio.h>
28 #include <assert.h>
29 /* Note: jpeglib.h prerequires stdlib.h and ctype.h.  It should include them
30    itself, but doesn't.
31 */
32 #include <jpeglib.h>
33 
34 #include "pm_c_util.h"
35 #include "pnm.h"
36 #include "shhopt.h"
37 #include "mallocvar.h"
38 #include "nstring.h"
39 
40 #define EXIT_WARNING 2   /* Goes with EXIT_SUCCESS, EXIT_FAILURE in stdlib.h */
41 
42 enum restart_unit {RESTART_MCU, RESTART_ROW, RESTART_NONE};
43 enum density_unit {DEN_UNSPECIFIED, DEN_DOTS_PER_INCH, DEN_DOTS_PER_CM};
44 
45 struct density {
46     enum density_unit unit;
47         /* The units of density for 'horiz', 'vert' */
48     unsigned short horiz;  /* Not 0 */
49         /* Horizontal density, in units specified by 'unit' */
50     unsigned short vert;   /* Not 0 */
51         /* Same as 'horiz', but vertical */
52 };
53 
54 struct cmdlineInfo {
55     /* All the information the user supplied in the command line,
56        in a form easy for the program to use.
57      */
58     char *input_filespec;
59     unsigned int verbose;
60     unsigned int quality;
61     unsigned int force_baseline;
62     unsigned int progressive;
63     unsigned int arith_code;
64     J_DCT_METHOD dct_method;
65     unsigned int grayscale;
66     unsigned int rgb;
67     long int max_memory_to_use;
68     unsigned int trace_level;
69     char *qslots;
70     char *qtablefile;
71     char *sample;
72     char *scans;
73     int smoothing_factor;
74     unsigned int optimize;
75     unsigned int restart_value;
76     enum restart_unit restart_unit;
77     char *restart;
78     char *comment;              /* NULL if none */
79     const char *exif_filespec;  /* NULL if none */
80     unsigned int density_spec;
81         /* boolean: JFIF should specify a density.  If false, 'density'
82            is undefined.
83         */
84     struct density density;
85 };
86 
87 static void
interpret_maxmemory(const char * const maxmemory,long int * const max_memory_to_use_p)88 interpret_maxmemory (const char * const maxmemory,
89                      long int * const max_memory_to_use_p) {
90     long int lval;
91     char ch;
92 
93     if (maxmemory == NULL) {
94         *max_memory_to_use_p = -1;  /* unspecified */
95     } else if (sscanf(maxmemory, "%ld%c", &lval, &ch) < 1) {
96         pm_error("Invalid value for --maxmemory option: '%s'.", maxmemory);
97         exit(EXIT_FAILURE);
98     } else {
99         if (ch == 'm' || ch == 'M') lval *= 1000L;
100         *max_memory_to_use_p = lval * 1000L;
101     }
102 }
103 
104 
105 
106 static void
interpret_restart(const char * const restart,unsigned int * const restart_value_p,enum restart_unit * const restart_unit_p)107 interpret_restart(const char * const restart,
108                   unsigned int * const restart_value_p,
109                   enum restart_unit * const restart_unit_p) {
110 /*----------------------------------------------------------------------------
111    Interpret the restart command line option.  Return values suitable
112    for plugging into a jpeg_compress_struct to control compression.
113 -----------------------------------------------------------------------------*/
114     if (restart == NULL) {
115         /* No --restart option.  Set default */
116         *restart_unit_p = RESTART_NONE;
117     } else {
118         /* Restart interval in MCU rows (or in MCUs with 'b'). */
119         long lval;
120         char ch;
121         unsigned int matches;
122 
123         matches= sscanf(restart, "%ld%c", &lval, &ch);
124         if (matches == 0)
125             pm_error("Invalid value for the --restart option : '%s'.",
126                      restart);
127         else {
128             if (lval < 0 || lval > 65535L) {
129                 pm_error("--restart value %ld is out of range.", lval);
130                 exit(EXIT_FAILURE);
131             } else {
132                 if (matches == 1) {
133                     *restart_value_p = lval;
134                     *restart_unit_p = RESTART_ROW;
135                 } else {
136                     if (ch == 'b' || ch == 'B') {
137                         *restart_value_p = lval;
138                         *restart_unit_p = RESTART_MCU;
139                     } else pm_error("Invalid --restart value '%s'.", restart);
140                 }
141             }
142         }
143     }
144 }
145 
146 
147 
148 
149 static void
interpret_density(const char * const densityString,struct density * const densityP)150 interpret_density(const char *        const densityString,
151                   struct density *    const densityP) {
152 /*----------------------------------------------------------------------------
153    Interpret the value of the "-density" option.
154 -----------------------------------------------------------------------------*/
155     if (strlen(densityString) < 1)
156         pm_error("-density value cannot be null.");
157     else {
158         char * unitName;  /* malloc'ed */
159         int matched;
160         int horiz, vert;
161 
162         unitName = malloc(strlen(densityString)+1);
163 
164         matched = sscanf(densityString, "%dx%d%s", &horiz, &vert, unitName);
165 
166         if (matched < 2)
167             pm_error("Invalid format for density option value '%s'.  It "
168                      "should follow the example '3x2' or '3x2dpi' or "
169                      "'3x2dpcm'.", densityString);
170         else {
171             if (horiz <= 0 || horiz >= 1<<16)
172                 pm_error("Horizontal density %d is outside the range 1-65535",
173                          horiz);
174             else if (vert <= 0 || vert >= 1<<16)
175                 pm_error("Vertical density %d is outside the range 1-65535",
176                          vert);
177             else {
178                 densityP->horiz = horiz;
179                 densityP->vert  = vert;
180 
181                 if (matched < 3)
182                     densityP->unit = DEN_UNSPECIFIED;
183                 else {
184                     if (streq(unitName, "dpi") || streq(unitName, "DPI"))
185                         densityP->unit = DEN_DOTS_PER_INCH;
186                     else if (streq(unitName, "dpcm") ||
187                              streq(unitName, "DPCM"))
188                         densityP->unit = DEN_DOTS_PER_CM;
189                     else
190                         pm_error("Unrecognized unit '%s' in the density value "
191                                  "'%s'.  I recognize only 'dpi' and 'dpcm'",
192                                  unitName, densityString);
193                 }
194             }
195         }
196         free(unitName);
197     }
198 }
199 
200 
201 
202 static void
parseCommandLine(const int argc,char ** argv,struct cmdlineInfo * cmdlineP)203 parseCommandLine(const int argc, char ** argv,
204                    struct cmdlineInfo *cmdlineP) {
205 /*----------------------------------------------------------------------------
206    Note that many of the strings that this function returns in the
207    *cmdlineP structure are actually in the supplied argv array.  And
208    sometimes, one of these strings is actually just a suffix of an entry
209    in argv!
210 
211    On the other hand, unlike other option processing functions, we do
212    not change argv at all.
213 -----------------------------------------------------------------------------*/
214     optEntry *option_def = malloc(100*sizeof(optEntry));
215         /* Instructions to OptParseOptions3 on how to parse our options.
216          */
217     optStruct3 opt;
218 
219     int i;  /* local loop variable */
220 
221     const char *dctval;
222     const char *maxmemory;
223     const char *restart;
224     const char *density;
225 
226     unsigned int qualitySpec, smoothSpec;
227 
228     unsigned int option_def_index;
229 
230     int argc_parse;       /* argc, except we modify it as we parse */
231     char ** argv_parse;
232         /* argv, except we modify it as we parse */
233 
234     MALLOCARRAY(argv_parse, argc + 1);  /* +1 for the terminating null ptr */
235 
236     option_def_index = 0;   /* incremented by OPTENTRY */
237     OPTENT3(0, "verbose",     OPT_FLAG,   NULL, &cmdlineP->verbose,        0);
238     OPTENT3(0, "quality",     OPT_UINT,   &cmdlineP->quality,
239             &qualitySpec,        0);
240     OPTENT3(0, "baseline",    OPT_FLAG,   NULL, &cmdlineP->force_baseline, 0);
241     OPTENT3(0, "progressive", OPT_FLAG,   NULL, &cmdlineP->progressive,    0);
242     OPTENT3(0, "arithmetic",  OPT_FLAG,   NULL, &cmdlineP->arith_code,     0);
243     OPTENT3(0, "dct",         OPT_STRING, &dctval, NULL,                    0);
244     OPTENT3(0, "grayscale",   OPT_FLAG,   NULL, &cmdlineP->grayscale,      0);
245     OPTENT3(0, "greyscale",   OPT_FLAG,   NULL, &cmdlineP->grayscale,      0);
246     OPTENT3(0, "rgb",         OPT_FLAG,   NULL, &cmdlineP->rgb,            0);
247     OPTENT3(0, "maxmemory",   OPT_STRING, &maxmemory, NULL,                 0);
248     OPTENT3(0, "tracelevel",  OPT_UINT,   &cmdlineP->trace_level, NULL,    0);
249     OPTENT3(0, "qslots",      OPT_STRING, &cmdlineP->qslots,      NULL,    0);
250     OPTENT3(0, "qtables",     OPT_STRING, &cmdlineP->qtablefile,  NULL,    0);
251     OPTENT3(0, "sample",      OPT_STRING, &cmdlineP->sample,      NULL,    0);
252     OPTENT3(0, "scans",       OPT_STRING, &cmdlineP->scans,       NULL,    0);
253     OPTENT3(0, "smooth",      OPT_UINT,   &cmdlineP->smoothing_factor,
254             &smoothSpec,  0);
255     OPTENT3(0, "optimize",    OPT_FLAG,   NULL, &cmdlineP->optimize,       0);
256     OPTENT3(0, "optimise",    OPT_FLAG,   NULL, &cmdlineP->optimize,       0);
257     OPTENT3(0, "restart",     OPT_STRING, &restart, NULL,                   0);
258     OPTENT3(0, "comment",     OPT_STRING, &cmdlineP->comment, NULL,        0);
259     OPTENT3(0, "exif",        OPT_STRING, &cmdlineP->exif_filespec, NULL,  0);
260     OPTENT3(0, "density",     OPT_STRING, &density,
261             &cmdlineP->density_spec, 0);
262 
263     /* Set the defaults */
264     dctval = NULL;
265     maxmemory = NULL;
266     cmdlineP->trace_level = 0;
267     cmdlineP->qslots = NULL;
268     cmdlineP->qtablefile = NULL;
269     cmdlineP->sample = NULL;
270     cmdlineP->scans = NULL;
271     restart = NULL;
272     cmdlineP->comment = NULL;
273     cmdlineP->exif_filespec = NULL;
274 
275     /* Make private copy of arguments for pm_optParseOptions to corrupt */
276     argc_parse = argc;
277     for (i=0; i < argc+1; i++) argv_parse[i] = argv[i];
278 
279     opt.opt_table = option_def;
280     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
281     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
282 
283     pm_optParseOptions3(&argc_parse, argv_parse, opt, sizeof(opt), 0);
284 
285     if (!qualitySpec)
286         cmdlineP->quality = -1;  /* unspecified */
287 
288     if (!smoothSpec)
289         cmdlineP->smoothing_factor = -1;
290 
291     if (cmdlineP->rgb && cmdlineP->grayscale)
292         pm_error("You can't specify both -rgb and -grayscale");
293 
294     if (argc_parse - 1 == 0)
295         cmdlineP->input_filespec = strdup("-");  /* he wants stdin */
296     else if (argc_parse - 1 == 1)
297         cmdlineP->input_filespec = strdup(argv_parse[1]);
298     else
299         pm_error("Too many arguments.  The only argument accepted "
300                  "is the input file specification.");
301     if (dctval == NULL)
302         cmdlineP->dct_method = JDCT_DEFAULT;
303     else {
304         if (streq(dctval, "int"))
305             cmdlineP->dct_method = JDCT_ISLOW;
306         else if (streq(dctval, "fast"))
307             cmdlineP->dct_method = JDCT_IFAST;
308         else if (streq(dctval, "float"))
309             cmdlineP->dct_method = JDCT_FLOAT;
310         else
311             pm_error("Invalid value for the --dct option: '%s'.", dctval);
312     }
313 
314     interpret_maxmemory(maxmemory, &cmdlineP->max_memory_to_use);
315     interpret_restart(restart, &cmdlineP->restart_value,
316                       &cmdlineP->restart_unit);
317     if (cmdlineP->density_spec)
318         interpret_density(density, &cmdlineP->density);
319 
320     if (cmdlineP->smoothing_factor > 100)
321         pm_error("Smoothing factor %d is greater than 100 (%%).",
322                  cmdlineP->smoothing_factor);
323 
324     if (streq(cmdlineP->input_filespec, "=") &&
325         cmdlineP->exif_filespec &&
326         streq(cmdlineP->exif_filespec, "-"))
327 
328         pm_error("Cannot have both input image and exif header be from "
329                  "Standard Input.");
330 
331 
332     free(argv_parse);
333 }
334 
335 
336 
337 static void
report_compressor(const struct jpeg_compress_struct cinfo)338 report_compressor(const struct jpeg_compress_struct cinfo) {
339 
340     if (cinfo.scan_info == NULL)
341         pm_message("No scan script is being used");
342     else {
343         int i;
344         pm_message("A scan script with %d entries is being used:",
345                    cinfo.num_scans);
346         for (i = 0; i < cinfo.num_scans; i++) {
347             int j;
348             pm_message("    Scan %2d: Ss=%2d Se=%2d Ah=%2d Al=%2d  "
349                        "%d components",
350                        i,
351                        cinfo.scan_info[i].Ss,
352                        cinfo.scan_info[i].Se,
353                        cinfo.scan_info[i].Ah,
354                        cinfo.scan_info[i].Al,
355                        cinfo.scan_info[i].comps_in_scan
356                        );
357             for (j = 0; j < cinfo.scan_info[i].comps_in_scan; j++)
358                 pm_message("        Color component %d index: %d", j,
359                            cinfo.scan_info[i].component_index[j]);
360         }
361     }
362 }
363 
364 
365 
366 static void
setup_jpeg_source_parameters(struct jpeg_compress_struct * const cinfoP,int const width,int const height,int const format)367 setup_jpeg_source_parameters(struct jpeg_compress_struct * const cinfoP,
368                              int const width, int const height,
369                              int const format) {
370 /*----------------------------------------------------------------------------
371    Set up in the compressor descriptor *cinfoP the description of the
372    source image as required by the compressor.
373 -----------------------------------------------------------------------------*/
374 
375     switch PNM_FORMAT_TYPE(format) {
376     case PBM_TYPE:
377     case PGM_TYPE:
378         cinfoP->in_color_space = JCS_GRAYSCALE;
379         cinfoP->input_components = 1;
380         break;
381     case PPM_TYPE:
382         cinfoP->in_color_space = JCS_RGB;
383         cinfoP->input_components = 3;
384         break;
385     default:
386         pm_error("INTERNAL ERROR; invalid format in "
387                  "setup_jpeg_source_parameters()");
388     }
389 }
390 
391 
392 
393 static void
setup_jpeg_density(struct jpeg_compress_struct * const cinfoP,struct density const density)394 setup_jpeg_density(struct jpeg_compress_struct * const cinfoP,
395                    struct density                const density) {
396 /*----------------------------------------------------------------------------
397    Set up in the compressor descriptor *cinfoP the density information
398    'density'.
399 -----------------------------------------------------------------------------*/
400     switch(density.unit) {
401     case DEN_UNSPECIFIED:   cinfoP->density_unit = 0; break;
402     case DEN_DOTS_PER_INCH: cinfoP->density_unit = 1; break;
403     case DEN_DOTS_PER_CM:   cinfoP->density_unit = 2; break;
404     }
405 
406     cinfoP->X_density = density.horiz;
407     cinfoP->Y_density = density.vert;
408 }
409 
410 
411 
412 /*----------------------------------------------------------------------------
413    The functions below here are essentially the file rdswitch.c from
414    the JPEG library.  They perform the functions specified by the following
415    pnmtojpeg options:
416 
417    -qtables file          Read quantization tables from text file
418    -scans file            Read scan script from text file
419    -qslots N[,N,...]      Set component quantization table selectors
420    -sample HxV[,HxV,...]  Set component sampling factors
421 -----------------------------------------------------------------------------*/
422 
423 static int
text_getc(FILE * file)424 text_getc (FILE * file)
425 /* Read next char, skipping over any comments (# to end of line) */
426 /* A comment/newline sequence is returned as a newline */
427 {
428     register int ch;
429 
430     ch = getc(file);
431     if (ch == '#') {
432         do {
433             ch = getc(file);
434         } while (ch != '\n' && ch != EOF);
435     }
436     return ch;
437 }
438 
439 
440 static boolean
readTextInteger(FILE * const fileP,long * const resultP,int * const termcharP)441 readTextInteger(FILE * const fileP,
442                 long * const resultP,
443                 int *  const termcharP) {
444 /*----------------------------------------------------------------------------
445    Read the next unsigned decimal integer from file 'fileP', skipping
446    white space as necessary.  Return it as *resultP.
447 
448    Also read one character after the integer and return it as *termcharP.
449 
450    If there is no character after the integer, return *termcharP == EOF.
451 
452    Iff the next thing in the file is not a valid unsigned decimal integer,
453    return FALSE.
454 -----------------------------------------------------------------------------*/
455     int ch;
456     boolean retval;
457 
458     /* Skip any leading whitespace, detect EOF */
459     do {
460         ch = text_getc(fileP);
461     } while (isspace(ch));
462 
463     if (!isdigit(ch))
464         retval = FALSE;
465     else {
466         long val;
467         val = ch - '0';  /* initial value */
468         while ((ch = text_getc(fileP)) != EOF) {
469             if (! isdigit(ch))
470                 break;
471             val *= 10;
472             val += ch - '0';
473         }
474         *resultP = val;
475         retval = TRUE;
476     }
477     *termcharP = ch;
478     return retval;
479 }
480 
481 
482 static boolean
read_scan_integer(FILE * file,long * result,int * termchar)483 read_scan_integer (FILE * file, long * result, int * termchar)
484 /* Variant of readTextInteger that always looks for a non-space termchar;
485  * this simplifies parsing of punctuation in scan scripts.
486  */
487 {
488     register int ch;
489 
490     if (! readTextInteger(file, result, termchar))
491         return FALSE;
492     ch = *termchar;
493     while (ch != EOF && isspace(ch))
494         ch = text_getc(file);
495     if (isdigit(ch)) {		/* oops, put it back */
496         if (ungetc(ch, file) == EOF)
497             return FALSE;
498         ch = ' ';
499     } else {
500         /* Any separators other than ';' and ':' are ignored;
501          * this allows user to insert commas, etc, if desired.
502          */
503         if (ch != EOF && ch != ';' && ch != ':')
504             ch = ' ';
505     }
506     *termchar = ch;
507     return TRUE;
508 }
509 
510 
511 
512 static boolean
read_scan_script(j_compress_ptr const cinfo,const char * const filename)513 read_scan_script(j_compress_ptr const cinfo,
514                  const char *   const filename) {
515 /*----------------------------------------------------------------------------
516   Read a scan script from the specified text file.
517   Each entry in the file defines one scan to be emitted.
518   Entries are separated by semicolons ';'.
519   An entry contains one to four component indexes,
520   optionally followed by a colon ':' and four progressive-JPEG parameters.
521   The component indexes denote which component(s) are to be transmitted
522   in the current scan.  The first component has index 0.
523   Sequential JPEG is used if the progressive-JPEG parameters are omitted.
524   The file is free format text: any whitespace may appear between numbers
525   and the ':' and ';' punctuation marks.  Also, other punctuation (such
526   as commas or dashes) can be placed between numbers if desired.
527   Comments preceded by '#' may be included in the file.
528   Note: we do very little validity checking here;
529   jcmaster.c will validate the script parameters.
530 -----------------------------------------------------------------------------*/
531     FILE * fp;
532     unsigned int nscans;
533     unsigned int ncomps;
534     int termchar;
535     long val;
536 #define MAX_SCANS  100      /* quite arbitrary limit */
537     jpeg_scan_info scans[MAX_SCANS];
538 
539     fp = fopen(filename, "r");
540     if (fp == NULL) {
541         pm_message("Can't open scan definition file %s", filename);
542         return FALSE;
543     }
544     nscans = 0;
545 
546     while (read_scan_integer(fp, &val, &termchar)) {
547         ++nscans;  /* We got another scan */
548         if (nscans > MAX_SCANS) {
549             pm_message("Too many scans defined in file %s", filename);
550             fclose(fp);
551             return FALSE;
552         }
553         scans[nscans-1].component_index[0] = (int) val;
554         ncomps = 1;
555         while (termchar == ' ') {
556             if (ncomps >= MAX_COMPS_IN_SCAN) {
557                 pm_message("Too many components in one scan in file %s",
558                            filename);
559                 fclose(fp);
560                 return FALSE;
561             }
562             if (! read_scan_integer(fp, &val, &termchar))
563                 goto bogus;
564             scans[nscans-1].component_index[ncomps] = (int) val;
565             ++ncomps;
566         }
567         scans[nscans-1].comps_in_scan = ncomps;
568         if (termchar == ':') {
569             if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
570                 goto bogus;
571             scans[nscans-1].Ss = (int) val;
572             if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
573                 goto bogus;
574             scans[nscans-1].Se = (int) val;
575             if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
576                 goto bogus;
577             scans[nscans-1].Ah = (int) val;
578             if (! read_scan_integer(fp, &val, &termchar))
579                 goto bogus;
580             scans[nscans-1].Al = (int) val;
581         } else {
582             /* set non-progressive parameters */
583             scans[nscans-1].Ss = 0;
584             scans[nscans-1].Se = DCTSIZE2-1;
585             scans[nscans-1].Ah = 0;
586             scans[nscans-1].Al = 0;
587         }
588         if (termchar != ';' && termchar != EOF) {
589         bogus:
590             pm_message("Invalid scan entry format in file %s", filename);
591             fclose(fp);
592             return FALSE;
593         }
594     }
595 
596     if (termchar != EOF) {
597         pm_message("Non-numeric data in file %s", filename);
598         fclose(fp);
599         return FALSE;
600     }
601 
602     if (nscans > 0) {
603         /* Stash completed scan list in cinfo structure.  NOTE: in
604            this program, JPOOL_IMAGE is the right lifetime for this
605            data, but if you want to compress multiple images you'd
606            want JPOOL_PERMANENT.
607         */
608         const unsigned int scan_info_size = nscans * sizeof(jpeg_scan_info);
609         jpeg_scan_info * const scan_info =
610             (jpeg_scan_info *)
611             (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
612                                         scan_info_size);
613         memcpy(scan_info, scans, scan_info_size);
614         cinfo->scan_info = scan_info;
615         cinfo->num_scans = nscans;
616     }
617 
618     fclose(fp);
619     return TRUE;
620 }
621 
622 
623 
624 static boolean
read_quant_tables(j_compress_ptr cinfo,char * filename,int scale_factor,boolean force_baseline)625 read_quant_tables (j_compress_ptr cinfo, char * filename,
626                    int scale_factor, boolean force_baseline)
627 /* Read a set of quantization tables from the specified file.
628  * The file is plain ASCII text: decimal numbers with whitespace between.
629  * Comments preceded by '#' may be included in the file.
630  * There may be one to NUM_QUANT_TBLS tables in the file, each of 64 values.
631  * The tables are implicitly numbered 0,1,etc.
632  * NOTE: does not affect the qslots mapping, which will default to selecting
633  * table 0 for luminance (or primary) components, 1 for chrominance components.
634  * You must use -qslots if you want a different component->table mapping.
635  */
636 {
637     FILE * fp;
638     boolean retval;
639 
640     fp = fopen(filename, "rb");
641     if (fp == NULL) {
642         pm_message("Can't open table file %s", filename);
643         retval = FALSE;
644     } else {
645         boolean eof, error;
646         unsigned int tblno;
647 
648         for (tblno = 0, eof = FALSE, error = FALSE; !eof && !error; ++tblno) {
649             long val;
650             int termchar;
651             boolean gotOne;
652 
653             gotOne = readTextInteger(fp, &val, &termchar);
654             if (gotOne) {
655                 /* read 1st element of table */
656                 if (tblno >= NUM_QUANT_TBLS) {
657                     pm_message("Too many tables in file %s", filename);
658                     error = TRUE;
659                 } else {
660                     unsigned int table[DCTSIZE2];
661                     unsigned int i;
662 
663                     table[0] = (unsigned int) val;
664                     for (i = 1; i < DCTSIZE2 && !error; ++i) {
665                         if (! readTextInteger(fp, &val, &termchar)) {
666                             pm_message("Invalid table data in file %s",
667                                        filename);
668                             error = TRUE;
669                         } else
670                             table[i] = (unsigned int) val;
671                     }
672                     if (!error)
673                         jpeg_add_quant_table(
674                             cinfo, tblno, table, scale_factor, force_baseline);
675                 }
676             } else {
677                 if (termchar == EOF)
678                     eof = TRUE;
679                 else {
680                     pm_message("Non-numeric data in file %s", filename);
681                     error = TRUE;
682                 }
683             }
684         }
685 
686         fclose(fp);
687         retval = !error;
688     }
689 
690     return retval;
691 }
692 
693 
694 
695 static boolean
set_quant_slots(j_compress_ptr cinfo,char * arg)696 set_quant_slots (j_compress_ptr cinfo, char *arg)
697 /* Process a quantization-table-selectors parameter string, of the form
698  *     N[,N,...]
699  * If there are more components than parameters, the last value is replicated.
700  */
701 {
702     int val = 0;			/* default table # */
703     int ci;
704     char ch;
705 
706     for (ci = 0; ci < MAX_COMPONENTS; ci++) {
707         if (*arg) {
708             ch = ',';			/* if not set by sscanf, will be ',' */
709             if (sscanf(arg, "%d%c", &val, &ch) < 1)
710                 return FALSE;
711             if (ch != ',')		/* syntax check */
712                 return FALSE;
713             if (val < 0 || val >= NUM_QUANT_TBLS) {
714                 pm_message("Invalid quantization table number: %d.  "
715                            "JPEG quantization tables are numbered 0..%d",
716                            val, NUM_QUANT_TBLS - 1);
717                 return FALSE;
718             }
719             cinfo->comp_info[ci].quant_tbl_no = val;
720             while (*arg && *arg++ != ',')
721                 /* advance to next segment of arg string */
722                 ;
723         } else {
724             /* reached end of parameter, set remaining components to last tbl*/
725             cinfo->comp_info[ci].quant_tbl_no = val;
726         }
727     }
728     return TRUE;
729 }
730 
731 
732 static boolean
set_sample_factors(j_compress_ptr cinfo,char * arg)733 set_sample_factors (j_compress_ptr cinfo, char *arg)
734 /* Process a sample-factors parameter string, of the form
735  *     HxV[,HxV,...]
736  * If there are more components than parameters, "1x1" is assumed for the rest.
737  */
738 {
739     int ci, val1, val2;
740     char ch1, ch2;
741 
742     for (ci = 0; ci < MAX_COMPONENTS; ci++) {
743         if (*arg) {
744             ch2 = ',';		/* if not set by sscanf, will be ',' */
745             if (sscanf(arg, "%d%c%d%c", &val1, &ch1, &val2, &ch2) < 3)
746                 return FALSE;
747             if ((ch1 != 'x' && ch1 != 'X') || ch2 != ',') /* syntax check */
748                 return FALSE;
749             if (val1 <= 0 || val1 > 4) {
750                 pm_message("Invalid sampling factor: %d.  "
751                            "JPEG sampling factors must be 1..4", val1);
752                 return FALSE;
753             }
754             if (val2 <= 0 || val2 > 4) {
755                 pm_message("Invalid sampling factor: %d.  "
756                            "JPEG sampling factors must be 1..4", val2);
757                 return FALSE;
758             }
759             cinfo->comp_info[ci].h_samp_factor = val1;
760             cinfo->comp_info[ci].v_samp_factor = val2;
761             while (*arg && *arg++ != ',')
762                 /* advance to next segment of arg string */
763                 ;
764         } else {
765             /* reached end of parameter, set remaining components
766                to 1x1 sampling */
767             cinfo->comp_info[ci].h_samp_factor = 1;
768             cinfo->comp_info[ci].v_samp_factor = 1;
769         }
770     }
771     return TRUE;
772 }
773 
774 
775 
776 static void
setup_jpeg(struct jpeg_compress_struct * const cinfoP,struct jpeg_error_mgr * const jerrP,struct cmdlineInfo const cmdline,int const width,int const height,pixval const maxval,int const input_fmt,FILE * const output_file)777 setup_jpeg(struct jpeg_compress_struct * const cinfoP,
778            struct jpeg_error_mgr       * const jerrP,
779            struct cmdlineInfo            const cmdline,
780            int                           const width,
781            int                           const height,
782            pixval                        const maxval,
783            int                           const input_fmt,
784            FILE *                        const output_file) {
785 
786     int quality;
787     int q_scale_factor;
788 
789     /* Initialize the JPEG compression object with default error handling. */
790     cinfoP->err = jpeg_std_error(jerrP);
791     jpeg_create_compress(cinfoP);
792 
793     setup_jpeg_source_parameters(cinfoP, width, height, input_fmt);
794 
795     jpeg_set_defaults(cinfoP);
796 
797     cinfoP->data_precision = BITS_IN_JSAMPLE;
798         /* we always rescale data to this */
799     cinfoP->image_width = (unsigned int) width;
800     cinfoP->image_height = (unsigned int) height;
801 
802     cinfoP->arith_code = cmdline.arith_code;
803     cinfoP->dct_method = cmdline.dct_method;
804     if (cmdline.trace_level == 0 && cmdline.verbose)
805         cinfoP->err->trace_level = 1;
806     else cinfoP->err->trace_level = cmdline.trace_level;
807     if (cmdline.grayscale)
808         jpeg_set_colorspace(cinfoP, JCS_GRAYSCALE);
809     else if (cmdline.rgb)
810         /* This is not legal if the input is not JCS_RGB too, i.e. it's PPM */
811         jpeg_set_colorspace(cinfoP, JCS_RGB);
812     else
813         /* This default will be based on the in_color_space set above */
814         jpeg_default_colorspace(cinfoP);
815     if (cmdline.max_memory_to_use != -1)
816         cinfoP->mem->max_memory_to_use = cmdline.max_memory_to_use;
817     cinfoP->optimize_coding = cmdline.optimize;
818     if (cmdline.quality == -1) {
819         quality = 75;
820         q_scale_factor = 100;
821     } else {
822         quality = cmdline.quality;
823         q_scale_factor = jpeg_quality_scaling(cmdline.quality);
824     }
825     if (cmdline.smoothing_factor != -1)
826         cinfoP->smoothing_factor = cmdline.smoothing_factor;
827 
828     /* Set quantization tables for selected quality. */
829     /* Some or all may be overridden if user specified --qtables. */
830     jpeg_set_quality(cinfoP, quality, cmdline.force_baseline);
831 
832     if (cmdline.qtablefile != NULL) {
833         if (! read_quant_tables(cinfoP, cmdline.qtablefile,
834                                 q_scale_factor, cmdline.force_baseline))
835             pm_error("Can't use quantization table file '%s'.",
836                      cmdline.qtablefile);
837     }
838 
839     if (cmdline.qslots != NULL) {
840         if (! set_quant_slots(cinfoP, cmdline.qslots))
841             pm_error("Bad quantization-table-selectors parameter string '%s'.",
842                      cmdline.qslots);
843     }
844 
845     if (cmdline.sample != NULL) {
846         if (! set_sample_factors(cinfoP, cmdline.sample))
847             pm_error("Bad sample-factors parameters string '%s'.",
848                      cmdline.sample);
849     }
850 
851     if (cmdline.progressive)
852         jpeg_simple_progression(cinfoP);
853 
854     if (cmdline.density_spec)
855         setup_jpeg_density(cinfoP, cmdline.density);
856 
857     if (cmdline.scans != NULL) {
858         if (! read_scan_script(cinfoP, cmdline.scans)) {
859             pm_message("Error in scan script '%s'.", cmdline.scans);
860         }
861     }
862 
863     /* Specify data destination for compression */
864     jpeg_stdio_dest(cinfoP, output_file);
865 
866     if (cmdline.verbose) report_compressor(*cinfoP);
867 
868     /* Start compressor */
869     jpeg_start_compress(cinfoP, TRUE);
870 
871 }
872 
873 
874 
875 static void
write_exif_header(struct jpeg_compress_struct * const cinfoP,const char * const exif_filespec)876 write_exif_header(struct jpeg_compress_struct * const cinfoP,
877                   const char * const exif_filespec) {
878 /*----------------------------------------------------------------------------
879    Generate an APP1 marker in the JFIF output that is an Exif header.
880 
881    The contents of the Exif header are in the file with filespec
882    'exif_filespec' (file spec and contents are not validated).
883 
884    exif_filespec = "-" means Standard Input.
885 
886    If the file contains just two bytes of zero, don't write any marker
887    but don't recognize any error either.
888 -----------------------------------------------------------------------------*/
889     FILE * exif_file;
890     unsigned short length;
891 
892     exif_file = pm_openr(exif_filespec);
893 
894     pm_readbigshort(exif_file, (short*)&length);
895 
896     if (length == 0) {
897         /* Special value meaning "no header" */
898     } else if (length < 3)
899         pm_error("Invalid length %u at start of exif file", length);
900     else {
901         unsigned char * exif_data;
902         size_t rc;
903         size_t const data_length = length - 2;
904             /* Subtract 2 byte length field*/
905 
906         assert(data_length > 0);
907 
908         exif_data = malloc(data_length);
909         if (exif_data == NULL)
910             pm_error("Unable to allocate %u bytes for exif header buffer",
911                      (unsigned)data_length);
912 
913         rc = fread(exif_data, 1, data_length, exif_file);
914 
915         if (rc != data_length)
916             pm_error("Premature end of file on exif header file.  Should be "
917                      "%u bytes of data, read only %u",
918                      (unsigned)data_length, (unsigned)rc);
919 
920         jpeg_write_marker(cinfoP, JPEG_APP0+1,
921                           (const JOCTET *) exif_data, data_length);
922 
923         free(exif_data);
924     }
925 
926     pm_close(exif_file);
927 }
928 
929 
930 
931 static void
compute_rescaling_array(JSAMPLE ** const rescale_p,const pixval maxval,const struct jpeg_compress_struct cinfo)932 compute_rescaling_array(JSAMPLE ** const rescale_p, const pixval maxval,
933                         const struct jpeg_compress_struct cinfo) {
934 /*----------------------------------------------------------------------------
935    Compute the rescaling array for a maximum pixval of 'maxval'.
936    Allocate the memory for it too.
937 -----------------------------------------------------------------------------*/
938   const long half_maxval = maxval / 2;
939   long val;
940 
941   *rescale_p = (JSAMPLE *)
942     (cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_IMAGE,
943                               (size_t) (((long) maxval + 1L) *
944                                         sizeof(JSAMPLE)));
945   for (val = 0; val <= maxval; val++) {
946     /* The multiplication here must be done in 32 bits to avoid overflow */
947     (*rescale_p)[val] = (JSAMPLE) ((val*MAXJSAMPLE + half_maxval)/maxval);
948   }
949 }
950 
951 
952 
953 static void
translate_row(const pixel pnm_buffer[],JSAMPLE jpeg_buffer[],int const width,int const input_components,const JSAMPLE translate[])954 translate_row(const pixel pnm_buffer[],
955               JSAMPLE jpeg_buffer[],
956               int const width,
957               int const input_components,
958               const JSAMPLE translate[]) {
959 /*----------------------------------------------------------------------------
960    Convert the input row, in pnm format, to an output row in JPEG compressor
961    input format.
962 
963    This is a byte for byte copy, translated through the array 'translate'.
964 -----------------------------------------------------------------------------*/
965   unsigned int column;
966   /* I'm not sure why the JPEG library data structures don't have some kind
967      of pixel data structure (such that a row buffer is an array of pixels,
968      rather than an array of samples).  But because of this, we have to
969      index jpeg_buffer the old fashioned way.
970      */
971 
972   switch (input_components) {
973   case 1:
974       for (column = 0; column < width; column++)
975           jpeg_buffer[column] = translate[(int)PNM_GET1(pnm_buffer[column])];
976       break;
977   case 3:
978       for (column = 0; column < width; column++) {
979           jpeg_buffer[column*3+0] =
980               translate[(int)PPM_GETR(pnm_buffer[column])];
981           jpeg_buffer[column*3+1] =
982               translate[(int)PPM_GETG(pnm_buffer[column])];
983           jpeg_buffer[column*3+2] =
984               translate[(int)PPM_GETB(pnm_buffer[column])];
985       }
986       break;
987   default:
988       pm_error("INTERNAL ERROR: invalid number of input components in "
989                "translate_row()");
990   }
991 
992 }
993 
994 
995 
996 static void
convert_scanlines(struct jpeg_compress_struct * const cinfo_p,FILE * const input_file,pixval const maxval,int const input_fmt,JSAMPLE xlate_table[])997 convert_scanlines(struct jpeg_compress_struct * const cinfo_p,
998                   FILE *                        const input_file,
999                   pixval                        const maxval,
1000                   int                           const input_fmt,
1001                   JSAMPLE                             xlate_table[]){
1002 /*----------------------------------------------------------------------------
1003    Read scan lines from the input file, which is already opened in the
1004    netpbm library sense and ready for reading, and write them to the
1005    output JPEG object.  Translate the pnm sample values to JPEG sample
1006    values through the thable xlate_table[].
1007 -----------------------------------------------------------------------------*/
1008   xel * pnm_buffer;
1009     /* contains the row of the input image currently being processed,
1010        in pnm_readpnmrow format
1011     */
1012   JSAMPARRAY buffer;
1013     /* Row 0 of this array contains the row of the output image currently
1014        being processed, in JPEG compressor input format.  The array only
1015        has that one row.
1016     */
1017 
1018   /* Allocate the libpnm output and compressor input buffers */
1019   buffer = (*cinfo_p->mem->alloc_sarray)
1020     ((j_common_ptr) cinfo_p, JPOOL_IMAGE,
1021      (unsigned int) cinfo_p->image_width * cinfo_p->input_components,
1022      (unsigned int) 1);
1023 
1024   pnm_buffer = pnm_allocrow(cinfo_p->image_width);
1025 
1026   while (cinfo_p->next_scanline < cinfo_p->image_height) {
1027     if (cinfo_p->err->trace_level > 1)
1028         pm_message("Converting Row %d...", cinfo_p->next_scanline);
1029     pnm_readpnmrow(input_file, pnm_buffer, cinfo_p->image_width,
1030                    maxval, input_fmt);
1031     translate_row(pnm_buffer, buffer[0],
1032                   cinfo_p->image_width, cinfo_p->input_components,
1033                   xlate_table);
1034     jpeg_write_scanlines(cinfo_p, buffer, 1);
1035     if (cinfo_p->err->trace_level > 1)
1036         pm_message("Done.");
1037   }
1038 
1039   pnm_freerow(pnm_buffer);
1040   /* Don't worry about the compressor input buffer; it gets freed
1041      automatically
1042   */
1043 }
1044 
1045 
1046 
1047 int
main(int argc,char ** argv)1048 main(int     argc,
1049      char ** argv) {
1050 
1051     struct cmdlineInfo cmdline;
1052     struct jpeg_compress_struct cinfo;
1053     struct jpeg_error_mgr jerr;
1054     FILE * input_file;
1055     FILE * output_file;
1056     int height;
1057         /* height of the input image in rows, as specified by its header */
1058     int width;
1059         /* width of the input image in columns, as specified by its header */
1060     pixval maxval;
1061         /* maximum value of an input pixel component, as specified by header */
1062     int input_fmt;
1063         /* The input format, as determined by its header.  */
1064     JSAMPLE *rescale;         /* => maxval-remapping array, or NULL */
1065         /* This is an array that maps each possible pixval in the input to
1066            a new value such that while the range of the input values is
1067            0 .. maxval, the range of the output values is 0 .. MAXJSAMPLE.
1068         */
1069 
1070     pnm_init(&argc, argv);
1071 
1072     parseCommandLine(argc, argv, &cmdline);
1073 
1074     input_file = pm_openr(cmdline.input_filespec);
1075     free(cmdline.input_filespec);
1076 
1077     output_file = stdout;
1078 
1079     /* Open the pnm input */
1080     pnm_readpnminit(input_file, &width, &height, &maxval, &input_fmt);
1081     if (cmdline.verbose) {
1082         pm_message("Input file has format %c%c.\n"
1083                    "It has %d rows of %d columns of pixels "
1084                    "with max sample value of %d.",
1085                    (char) (input_fmt/256), (char) (input_fmt % 256),
1086                    height, width, maxval);
1087     }
1088 
1089     setup_jpeg(&cinfo, &jerr, cmdline, width, height, maxval, input_fmt,
1090                output_file);
1091 
1092     compute_rescaling_array(&rescale, maxval, cinfo);
1093 
1094     if (cmdline.comment)
1095         jpeg_write_marker(&cinfo, JPEG_COM, (const JOCTET *) cmdline.comment,
1096                           strlen(cmdline.comment));
1097 
1098     if (cmdline.exif_filespec)
1099         write_exif_header(&cinfo, cmdline.exif_filespec);
1100 
1101     /* Translate and copy over the actual scanlines */
1102     convert_scanlines(&cinfo, input_file, maxval, input_fmt, rescale);
1103 
1104     /* Finish compression and release memory */
1105     jpeg_finish_compress(&cinfo);
1106     jpeg_destroy_compress(&cinfo);
1107 
1108     /* Close files, if we opened them */
1109     if (input_file != stdin)
1110         pm_close(input_file);
1111 
1112     /* Program may have exited with non-zero completion code via
1113        various function calls above.
1114     */
1115     return jerr.num_warnings > 0 ? EXIT_WARNING : EXIT_SUCCESS;
1116 }
1117 
1118 
1119 
1120