1 /*
2  * rdswitch.c
3  *
4  * This file was part of the Independent JPEG Group's software:
5  * Copyright (C) 1991-1996, Thomas G. Lane.
6  * libjpeg-turbo Modifications:
7  * Copyright (C) 2010, 2018, D. R. Commander.
8  * For conditions of distribution and use, see the accompanying README.ijg
9  * file.
10  *
11  * This file contains routines to process some of cjpeg's more complicated
12  * command-line switches.  Switches processed here are:
13  *      -qtables file           Read quantization tables from text file
14  *      -scans file             Read scan script from text file
15  *      -quality N[,N,...]      Set quality ratings
16  *      -qslots N[,N,...]       Set component quantization table selectors
17  *      -sample HxV[,HxV,...]   Set component sampling factors
18  */
19 
20 #include "cdjpeg.h"             /* Common decls for cjpeg/djpeg applications */
21 #include <ctype.h>              /* to declare isdigit(), isspace() */
22 
23 
24 LOCAL(int)
text_getc(FILE * file)25 text_getc(FILE *file)
26 /* Read next char, skipping over any comments (# to end of line) */
27 /* A comment/newline sequence is returned as a newline */
28 {
29   register int ch;
30 
31   ch = getc(file);
32   if (ch == '#') {
33     do {
34       ch = getc(file);
35     } while (ch != '\n' && ch != EOF);
36   }
37   return ch;
38 }
39 
40 
41 LOCAL(boolean)
read_text_integer(FILE * file,long * result,int * termchar)42 read_text_integer(FILE *file, long *result, int *termchar)
43 /* Read an unsigned decimal integer from a file, store it in result */
44 /* Reads one trailing character after the integer; returns it in termchar */
45 {
46   register int ch;
47   register long val;
48 
49   /* Skip any leading whitespace, detect EOF */
50   do {
51     ch = text_getc(file);
52     if (ch == EOF) {
53       *termchar = ch;
54       return FALSE;
55     }
56   } while (isspace(ch));
57 
58   if (!isdigit(ch)) {
59     *termchar = ch;
60     return FALSE;
61   }
62 
63   val = ch - '0';
64   while ((ch = text_getc(file)) != EOF) {
65     if (!isdigit(ch))
66       break;
67     val *= 10;
68     val += ch - '0';
69   }
70   *result = val;
71   *termchar = ch;
72   return TRUE;
73 }
74 
75 
76 #if JPEG_LIB_VERSION < 70
77 static int q_scale_factor[NUM_QUANT_TBLS] = { 100, 100, 100, 100 };
78 #endif
79 
80 GLOBAL(boolean)
read_quant_tables(j_compress_ptr cinfo,char * filename,boolean force_baseline)81 read_quant_tables(j_compress_ptr cinfo, char *filename, boolean force_baseline)
82 /* Read a set of quantization tables from the specified file.
83  * The file is plain ASCII text: decimal numbers with whitespace between.
84  * Comments preceded by '#' may be included in the file.
85  * There may be one to NUM_QUANT_TBLS tables in the file, each of 64 values.
86  * The tables are implicitly numbered 0,1,etc.
87  * NOTE: does not affect the qslots mapping, which will default to selecting
88  * table 0 for luminance (or primary) components, 1 for chrominance components.
89  * You must use -qslots if you want a different component->table mapping.
90  */
91 {
92   FILE *fp;
93   int tblno, i, termchar;
94   long val;
95   unsigned int table[DCTSIZE2];
96 
97   if ((fp = fopen(filename, "r")) == NULL) {
98     fprintf(stderr, "Can't open table file %s\n", filename);
99     return FALSE;
100   }
101   tblno = 0;
102 
103   while (read_text_integer(fp, &val, &termchar)) { /* read 1st element of table */
104     if (tblno >= NUM_QUANT_TBLS) {
105       fprintf(stderr, "Too many tables in file %s\n", filename);
106       fclose(fp);
107       return FALSE;
108     }
109     table[0] = (unsigned int)val;
110     for (i = 1; i < DCTSIZE2; i++) {
111       if (!read_text_integer(fp, &val, &termchar)) {
112         fprintf(stderr, "Invalid table data in file %s\n", filename);
113         fclose(fp);
114         return FALSE;
115       }
116       table[i] = (unsigned int)val;
117     }
118 #if JPEG_LIB_VERSION >= 70
119     jpeg_add_quant_table(cinfo, tblno, table, cinfo->q_scale_factor[tblno],
120                          force_baseline);
121 #else
122     jpeg_add_quant_table(cinfo, tblno, table, q_scale_factor[tblno],
123                          force_baseline);
124 #endif
125     tblno++;
126   }
127 
128   if (termchar != EOF) {
129     fprintf(stderr, "Non-numeric data in file %s\n", filename);
130     fclose(fp);
131     return FALSE;
132   }
133 
134   fclose(fp);
135   return TRUE;
136 }
137 
138 
139 #ifdef C_MULTISCAN_FILES_SUPPORTED
140 
141 LOCAL(boolean)
read_scan_integer(FILE * file,long * result,int * termchar)142 read_scan_integer(FILE *file, long *result, int *termchar)
143 /* Variant of read_text_integer that always looks for a non-space termchar;
144  * this simplifies parsing of punctuation in scan scripts.
145  */
146 {
147   register int ch;
148 
149   if (!read_text_integer(file, result, termchar))
150     return FALSE;
151   ch = *termchar;
152   while (ch != EOF && isspace(ch))
153     ch = text_getc(file);
154   if (isdigit(ch)) {            /* oops, put it back */
155     if (ungetc(ch, file) == EOF)
156       return FALSE;
157     ch = ' ';
158   } else {
159     /* Any separators other than ';' and ':' are ignored;
160      * this allows user to insert commas, etc, if desired.
161      */
162     if (ch != EOF && ch != ';' && ch != ':')
163       ch = ' ';
164   }
165   *termchar = ch;
166   return TRUE;
167 }
168 
169 
170 GLOBAL(boolean)
read_scan_script(j_compress_ptr cinfo,char * filename)171 read_scan_script(j_compress_ptr cinfo, char *filename)
172 /* Read a scan script from the specified text file.
173  * Each entry in the file defines one scan to be emitted.
174  * Entries are separated by semicolons ';'.
175  * An entry contains one to four component indexes,
176  * optionally followed by a colon ':' and four progressive-JPEG parameters.
177  * The component indexes denote which component(s) are to be transmitted
178  * in the current scan.  The first component has index 0.
179  * Sequential JPEG is used if the progressive-JPEG parameters are omitted.
180  * The file is free format text: any whitespace may appear between numbers
181  * and the ':' and ';' punctuation marks.  Also, other punctuation (such
182  * as commas or dashes) can be placed between numbers if desired.
183  * Comments preceded by '#' may be included in the file.
184  * Note: we do very little validity checking here;
185  * jcmaster.c will validate the script parameters.
186  */
187 {
188   FILE *fp;
189   int scanno, ncomps, termchar;
190   long val;
191   jpeg_scan_info *scanptr;
192 #define MAX_SCANS  100          /* quite arbitrary limit */
193   jpeg_scan_info scans[MAX_SCANS];
194 
195   if ((fp = fopen(filename, "r")) == NULL) {
196     fprintf(stderr, "Can't open scan definition file %s\n", filename);
197     return FALSE;
198   }
199   scanptr = scans;
200   scanno = 0;
201 
202   while (read_scan_integer(fp, &val, &termchar)) {
203     if (scanno >= MAX_SCANS) {
204       fprintf(stderr, "Too many scans defined in file %s\n", filename);
205       fclose(fp);
206       return FALSE;
207     }
208     scanptr->component_index[0] = (int)val;
209     ncomps = 1;
210     while (termchar == ' ') {
211       if (ncomps >= MAX_COMPS_IN_SCAN) {
212         fprintf(stderr, "Too many components in one scan in file %s\n",
213                 filename);
214         fclose(fp);
215         return FALSE;
216       }
217       if (!read_scan_integer(fp, &val, &termchar))
218         goto bogus;
219       scanptr->component_index[ncomps] = (int)val;
220       ncomps++;
221     }
222     scanptr->comps_in_scan = ncomps;
223     if (termchar == ':') {
224       if (!read_scan_integer(fp, &val, &termchar) || termchar != ' ')
225         goto bogus;
226       scanptr->Ss = (int)val;
227       if (!read_scan_integer(fp, &val, &termchar) || termchar != ' ')
228         goto bogus;
229       scanptr->Se = (int)val;
230       if (!read_scan_integer(fp, &val, &termchar) || termchar != ' ')
231         goto bogus;
232       scanptr->Ah = (int)val;
233       if (!read_scan_integer(fp, &val, &termchar))
234         goto bogus;
235       scanptr->Al = (int)val;
236     } else {
237       /* set non-progressive parameters */
238       scanptr->Ss = 0;
239       scanptr->Se = DCTSIZE2 - 1;
240       scanptr->Ah = 0;
241       scanptr->Al = 0;
242     }
243     if (termchar != ';' && termchar != EOF) {
244 bogus:
245       fprintf(stderr, "Invalid scan entry format in file %s\n", filename);
246       fclose(fp);
247       return FALSE;
248     }
249     scanptr++, scanno++;
250   }
251 
252   if (termchar != EOF) {
253     fprintf(stderr, "Non-numeric data in file %s\n", filename);
254     fclose(fp);
255     return FALSE;
256   }
257 
258   if (scanno > 0) {
259     /* Stash completed scan list in cinfo structure.
260      * NOTE: for cjpeg's use, JPOOL_IMAGE is the right lifetime for this data,
261      * but if you want to compress multiple images you'd want JPOOL_PERMANENT.
262      */
263     scanptr = (jpeg_scan_info *)
264       (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE,
265                                   scanno * sizeof(jpeg_scan_info));
266     MEMCOPY(scanptr, scans, scanno * sizeof(jpeg_scan_info));
267     cinfo->scan_info = scanptr;
268     cinfo->num_scans = scanno;
269   }
270 
271   fclose(fp);
272   return TRUE;
273 }
274 
275 #endif /* C_MULTISCAN_FILES_SUPPORTED */
276 
277 
278 #if JPEG_LIB_VERSION < 70
279 /* These are the sample quantization tables given in Annex K (Clause K.1) of
280  * Recommendation ITU-T T.81 (1992) | ISO/IEC 10918-1:1994.
281  * The spec says that the values given produce "good" quality, and
282  * when divided by 2, "very good" quality.
283  */
284 static const unsigned int std_luminance_quant_tbl[DCTSIZE2] = {
285   16,  11,  10,  16,  24,  40,  51,  61,
286   12,  12,  14,  19,  26,  58,  60,  55,
287   14,  13,  16,  24,  40,  57,  69,  56,
288   14,  17,  22,  29,  51,  87,  80,  62,
289   18,  22,  37,  56,  68, 109, 103,  77,
290   24,  35,  55,  64,  81, 104, 113,  92,
291   49,  64,  78,  87, 103, 121, 120, 101,
292   72,  92,  95,  98, 112, 100, 103,  99
293 };
294 static const unsigned int std_chrominance_quant_tbl[DCTSIZE2] = {
295   17,  18,  24,  47,  99,  99,  99,  99,
296   18,  21,  26,  66,  99,  99,  99,  99,
297   24,  26,  56,  99,  99,  99,  99,  99,
298   47,  66,  99,  99,  99,  99,  99,  99,
299   99,  99,  99,  99,  99,  99,  99,  99,
300   99,  99,  99,  99,  99,  99,  99,  99,
301   99,  99,  99,  99,  99,  99,  99,  99,
302   99,  99,  99,  99,  99,  99,  99,  99
303 };
304 
305 
306 LOCAL(void)
jpeg_default_qtables(j_compress_ptr cinfo,boolean force_baseline)307 jpeg_default_qtables(j_compress_ptr cinfo, boolean force_baseline)
308 {
309   jpeg_add_quant_table(cinfo, 0, std_luminance_quant_tbl, q_scale_factor[0],
310                        force_baseline);
311   jpeg_add_quant_table(cinfo, 1, std_chrominance_quant_tbl, q_scale_factor[1],
312                        force_baseline);
313 }
314 #endif
315 
316 
317 GLOBAL(boolean)
set_quality_ratings(j_compress_ptr cinfo,char * arg,boolean force_baseline)318 set_quality_ratings(j_compress_ptr cinfo, char *arg, boolean force_baseline)
319 /* Process a quality-ratings parameter string, of the form
320  *     N[,N,...]
321  * If there are more q-table slots than parameters, the last value is replicated.
322  */
323 {
324   int val = 75;                 /* default value */
325   int tblno;
326   char ch;
327 
328   for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) {
329     if (*arg) {
330       ch = ',';                 /* if not set by sscanf, will be ',' */
331       if (sscanf(arg, "%d%c", &val, &ch) < 1)
332         return FALSE;
333       if (ch != ',')            /* syntax check */
334         return FALSE;
335       /* Convert user 0-100 rating to percentage scaling */
336 #if JPEG_LIB_VERSION >= 70
337       cinfo->q_scale_factor[tblno] = jpeg_quality_scaling(val);
338 #else
339       q_scale_factor[tblno] = jpeg_quality_scaling(val);
340 #endif
341       while (*arg && *arg++ != ','); /* advance to next segment of arg
342                                         string */
343     } else {
344       /* reached end of parameter, set remaining factors to last value */
345 #if JPEG_LIB_VERSION >= 70
346       cinfo->q_scale_factor[tblno] = jpeg_quality_scaling(val);
347 #else
348       q_scale_factor[tblno] = jpeg_quality_scaling(val);
349 #endif
350     }
351   }
352   jpeg_default_qtables(cinfo, force_baseline);
353   return TRUE;
354 }
355 
356 
357 GLOBAL(boolean)
set_quant_slots(j_compress_ptr cinfo,char * arg)358 set_quant_slots(j_compress_ptr cinfo, char *arg)
359 /* Process a quantization-table-selectors parameter string, of the form
360  *     N[,N,...]
361  * If there are more components than parameters, the last value is replicated.
362  */
363 {
364   int val = 0;                  /* default table # */
365   int ci;
366   char ch;
367 
368   for (ci = 0; ci < MAX_COMPONENTS; ci++) {
369     if (*arg) {
370       ch = ',';                 /* if not set by sscanf, will be ',' */
371       if (sscanf(arg, "%d%c", &val, &ch) < 1)
372         return FALSE;
373       if (ch != ',')            /* syntax check */
374         return FALSE;
375       if (val < 0 || val >= NUM_QUANT_TBLS) {
376         fprintf(stderr, "JPEG quantization tables are numbered 0..%d\n",
377                 NUM_QUANT_TBLS - 1);
378         return FALSE;
379       }
380       cinfo->comp_info[ci].quant_tbl_no = val;
381       while (*arg && *arg++ != ','); /* advance to next segment of arg
382                                         string */
383     } else {
384       /* reached end of parameter, set remaining components to last table */
385       cinfo->comp_info[ci].quant_tbl_no = val;
386     }
387   }
388   return TRUE;
389 }
390 
391 
392 GLOBAL(boolean)
set_sample_factors(j_compress_ptr cinfo,char * arg)393 set_sample_factors(j_compress_ptr cinfo, char *arg)
394 /* Process a sample-factors parameter string, of the form
395  *     HxV[,HxV,...]
396  * If there are more components than parameters, "1x1" is assumed for the rest.
397  */
398 {
399   int ci, val1, val2;
400   char ch1, ch2;
401 
402   for (ci = 0; ci < MAX_COMPONENTS; ci++) {
403     if (*arg) {
404       ch2 = ',';                /* if not set by sscanf, will be ',' */
405       if (sscanf(arg, "%d%c%d%c", &val1, &ch1, &val2, &ch2) < 3)
406         return FALSE;
407       if ((ch1 != 'x' && ch1 != 'X') || ch2 != ',') /* syntax check */
408         return FALSE;
409       if (val1 <= 0 || val1 > 4 || val2 <= 0 || val2 > 4) {
410         fprintf(stderr, "JPEG sampling factors must be 1..4\n");
411         return FALSE;
412       }
413       cinfo->comp_info[ci].h_samp_factor = val1;
414       cinfo->comp_info[ci].v_samp_factor = val2;
415       while (*arg && *arg++ != ',');  /* advance to next segment of arg
416                                          string */
417     } else {
418       /* reached end of parameter, set remaining components to 1x1 sampling */
419       cinfo->comp_info[ci].h_samp_factor = 1;
420       cinfo->comp_info[ci].v_samp_factor = 1;
421     }
422   }
423   return TRUE;
424 }
425