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