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