blob: 886fec39b6279347a4be450f33e1261cbf5c2858 [file] [log] [blame]
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +00001/*
2 * rdswitch.c
3 *
noel@chromium.org3395bcc2014-04-14 06:56:00 +00004 * This file was part of the Independent JPEG Group's software:
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +00005 * Copyright (C) 1991-1996, Thomas G. Lane.
noel@chromium.org3395bcc2014-04-14 06:56:00 +00006 * libjpeg-turbo Modifications:
Chris Blumecca8c4d2019-03-01 01:09:50 -08007 * Copyright (C) 2010, 2018, D. R. Commander.
Tom Hudson0d47d2d2016-05-04 13:22:56 -04008 * For conditions of distribution and use, see the accompanying README.ijg
9 * file.
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000010 *
11 * This file contains routines to process some of cjpeg's more complicated
12 * command-line switches. Switches processed here are:
Tom Hudson0d47d2d2016-05-04 13:22:56 -040013 * -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
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000018 */
19
Tom Hudson0d47d2d2016-05-04 13:22:56 -040020#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */
21#include <ctype.h> /* to declare isdigit(), isspace() */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000022
23
24LOCAL(int)
Chris Blumecca8c4d2019-03-01 01:09:50 -080025text_getc(FILE *file)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000026/* 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;
Tom Hudson0d47d2d2016-05-04 13:22:56 -040030
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000031 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
41LOCAL(boolean)
Chris Blumecca8c4d2019-03-01 01:09:50 -080042read_text_integer(FILE *file, long *result, int *termchar)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000043/* 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;
Tom Hudson0d47d2d2016-05-04 13:22:56 -040048
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000049 /* 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));
Tom Hudson0d47d2d2016-05-04 13:22:56 -040057
Chris Blumecca8c4d2019-03-01 01:09:50 -080058 if (!isdigit(ch)) {
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000059 *termchar = ch;
60 return FALSE;
61 }
62
63 val = ch - '0';
64 while ((ch = text_getc(file)) != EOF) {
Chris Blumecca8c4d2019-03-01 01:09:50 -080065 if (!isdigit(ch))
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000066 break;
67 val *= 10;
68 val += ch - '0';
69 }
70 *result = val;
71 *termchar = ch;
72 return TRUE;
73}
74
75
hbono@chromium.org98626972011-08-03 03:13:08 +000076#if JPEG_LIB_VERSION < 70
Chris Blumecca8c4d2019-03-01 01:09:50 -080077static int q_scale_factor[NUM_QUANT_TBLS] = { 100, 100, 100, 100 };
hbono@chromium.org98626972011-08-03 03:13:08 +000078#endif
79
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000080GLOBAL(boolean)
Chris Blumecca8c4d2019-03-01 01:09:50 -080081read_quant_tables(j_compress_ptr cinfo, char *filename, boolean force_baseline)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000082/* 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{
Tom Hudson0d47d2d2016-05-04 13:22:56 -040092 FILE *fp;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000093 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 }
Chris Blumecca8c4d2019-03-01 01:09:50 -0800109 table[0] = (unsigned int)val;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000110 for (i = 1; i < DCTSIZE2; i++) {
Chris Blumecca8c4d2019-03-01 01:09:50 -0800111 if (!read_text_integer(fp, &val, &termchar)) {
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400112 fprintf(stderr, "Invalid table data in file %s\n", filename);
113 fclose(fp);
114 return FALSE;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000115 }
Chris Blumecca8c4d2019-03-01 01:09:50 -0800116 table[i] = (unsigned int)val;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000117 }
hbono@chromium.org98626972011-08-03 03:13:08 +0000118#if JPEG_LIB_VERSION >= 70
119 jpeg_add_quant_table(cinfo, tblno, table, cinfo->q_scale_factor[tblno],
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400120 force_baseline);
hbono@chromium.org98626972011-08-03 03:13:08 +0000121#else
122 jpeg_add_quant_table(cinfo, tblno, table, q_scale_factor[tblno],
123 force_baseline);
124#endif
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000125 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
141LOCAL(boolean)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800142read_scan_integer(FILE *file, long *result, int *termchar)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000143/* 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
Chris Blumecca8c4d2019-03-01 01:09:50 -0800149 if (!read_text_integer(file, result, termchar))
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000150 return FALSE;
151 ch = *termchar;
152 while (ch != EOF && isspace(ch))
153 ch = text_getc(file);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400154 if (isdigit(ch)) { /* oops, put it back */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000155 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
170GLOBAL(boolean)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800171read_scan_script(j_compress_ptr cinfo, char *filename)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000172/* 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{
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400188 FILE *fp;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000189 int scanno, ncomps, termchar;
190 long val;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400191 jpeg_scan_info *scanptr;
192#define MAX_SCANS 100 /* quite arbitrary limit */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000193 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 }
Chris Blumecca8c4d2019-03-01 01:09:50 -0800208 scanptr->component_index[0] = (int)val;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000209 ncomps = 1;
210 while (termchar == ' ') {
211 if (ncomps >= MAX_COMPS_IN_SCAN) {
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400212 fprintf(stderr, "Too many components in one scan in file %s\n",
213 filename);
214 fclose(fp);
215 return FALSE;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000216 }
Chris Blumecca8c4d2019-03-01 01:09:50 -0800217 if (!read_scan_integer(fp, &val, &termchar))
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400218 goto bogus;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800219 scanptr->component_index[ncomps] = (int)val;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000220 ncomps++;
221 }
222 scanptr->comps_in_scan = ncomps;
223 if (termchar == ':') {
Chris Blumecca8c4d2019-03-01 01:09:50 -0800224 if (!read_scan_integer(fp, &val, &termchar) || termchar != ' ')
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400225 goto bogus;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800226 scanptr->Ss = (int)val;
227 if (!read_scan_integer(fp, &val, &termchar) || termchar != ' ')
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400228 goto bogus;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800229 scanptr->Se = (int)val;
230 if (!read_scan_integer(fp, &val, &termchar) || termchar != ' ')
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400231 goto bogus;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800232 scanptr->Ah = (int)val;
233 if (!read_scan_integer(fp, &val, &termchar))
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400234 goto bogus;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800235 scanptr->Al = (int)val;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000236 } else {
237 /* set non-progressive parameters */
238 scanptr->Ss = 0;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800239 scanptr->Se = DCTSIZE2 - 1;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000240 scanptr->Ah = 0;
241 scanptr->Al = 0;
242 }
243 if (termchar != ';' && termchar != EOF) {
244bogus:
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 *)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800264 (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE,
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400265 scanno * sizeof(jpeg_scan_info));
266 MEMCOPY(scanptr, scans, scanno * sizeof(jpeg_scan_info));
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000267 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
hbono@chromium.org98626972011-08-03 03:13:08 +0000278#if JPEG_LIB_VERSION < 70
Chris Blumecca8c4d2019-03-01 01:09:50 -0800279/* 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.
hbono@chromium.org98626972011-08-03 03:13:08 +0000281 * The spec says that the values given produce "good" quality, and
282 * when divided by 2, "very good" quality.
283 */
284static 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};
294static 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
306LOCAL(void)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800307jpeg_default_qtables(j_compress_ptr cinfo, boolean force_baseline)
hbono@chromium.org98626972011-08-03 03:13:08 +0000308{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800309 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);
hbono@chromium.org98626972011-08-03 03:13:08 +0000313}
314#endif
315
316
317GLOBAL(boolean)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800318set_quality_ratings(j_compress_ptr cinfo, char *arg, boolean force_baseline)
hbono@chromium.org98626972011-08-03 03:13:08 +0000319/* 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{
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400324 int val = 75; /* default value */
hbono@chromium.org98626972011-08-03 03:13:08 +0000325 int tblno;
326 char ch;
327
328 for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) {
329 if (*arg) {
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400330 ch = ','; /* if not set by sscanf, will be ',' */
hbono@chromium.org98626972011-08-03 03:13:08 +0000331 if (sscanf(arg, "%d%c", &val, &ch) < 1)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400332 return FALSE;
333 if (ch != ',') /* syntax check */
334 return FALSE;
hbono@chromium.org98626972011-08-03 03:13:08 +0000335 /* 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
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100341 while (*arg && *arg++ != ','); /* advance to next segment of arg
342 string */
hbono@chromium.org98626972011-08-03 03:13:08 +0000343 } 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
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000357GLOBAL(boolean)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800358set_quant_slots(j_compress_ptr cinfo, char *arg)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000359/* 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{
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400364 int val = 0; /* default table # */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000365 int ci;
366 char ch;
367
368 for (ci = 0; ci < MAX_COMPONENTS; ci++) {
369 if (*arg) {
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400370 ch = ','; /* if not set by sscanf, will be ',' */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000371 if (sscanf(arg, "%d%c", &val, &ch) < 1)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400372 return FALSE;
373 if (ch != ',') /* syntax check */
374 return FALSE;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000375 if (val < 0 || val >= NUM_QUANT_TBLS) {
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400376 fprintf(stderr, "JPEG quantization tables are numbered 0..%d\n",
Chris Blumecca8c4d2019-03-01 01:09:50 -0800377 NUM_QUANT_TBLS - 1);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400378 return FALSE;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000379 }
380 cinfo->comp_info[ci].quant_tbl_no = val;
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100381 while (*arg && *arg++ != ','); /* advance to next segment of arg
382 string */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000383 } 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
392GLOBAL(boolean)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800393set_sample_factors(j_compress_ptr cinfo, char *arg)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000394/* 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) {
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400404 ch2 = ','; /* if not set by sscanf, will be ',' */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000405 if (sscanf(arg, "%d%c%d%c", &val1, &ch1, &val2, &ch2) < 3)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400406 return FALSE;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000407 if ((ch1 != 'x' && ch1 != 'X') || ch2 != ',') /* syntax check */
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400408 return FALSE;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000409 if (val1 <= 0 || val1 > 4 || val2 <= 0 || val2 > 4) {
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400410 fprintf(stderr, "JPEG sampling factors must be 1..4\n");
411 return FALSE;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000412 }
413 cinfo->comp_info[ci].h_samp_factor = val1;
414 cinfo->comp_info[ci].v_samp_factor = val2;
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100415 while (*arg && *arg++ != ','); /* advance to next segment of arg
416 string */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000417 } 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}