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