Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
6417 ashmew2 1
/*
2
 * rdjpgcom.c
3
 *
4
 * Copyright (C) 1994-1997, 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 a very simple stand-alone application that displays
9
 * the text in COM (comment) markers in a JFIF file.
10
 * This may be useful as an example of the minimum logic needed to parse
11
 * JPEG markers.
12
 */
13
 
14
#define JPEG_CJPEG_DJPEG	/* to get the command-line config symbols */
15
#include "jinclude.h"		/* get auto-config symbols,  */
16
 
17
#include 		/* to declare isupper(), tolower() */
18
#ifdef USE_SETMODE
19
#include 		/* to declare setmode()'s parameter macros */
20
/* If you have setmode() but not , just delete this line: */
21
#include 			/* to declare setmode() */
22
#endif
23
 
24
#ifdef USE_CCOMMAND		/* command-line reader for Macintosh */
25
#ifdef __MWERKS__
26
#include               /* Metrowerks needs this */
27
#include 		/* ... and this */
28
#endif
29
#ifdef THINK_C
30
#include 		/* Think declares it here */
31
#endif
32
#endif
33
 
34
#ifdef DONT_USE_B_MODE		/* define mode parameters for fopen() */
35
#define READ_BINARY	"r"
36
#else
37
#ifdef VMS			/* VMS is very nonstandard */
38
#define READ_BINARY	"rb", "ctx=stm"
39
#else				/* standard ANSI-compliant case */
40
#define READ_BINARY	"rb"
41
#endif
42
#endif
43
 
44
#ifndef EXIT_FAILURE		/* define exit() codes if not provided */
45
#define EXIT_FAILURE  1
46
#endif
47
#ifndef EXIT_SUCCESS
48
#ifdef VMS
49
#define EXIT_SUCCESS  1		/* VMS is very nonstandard */
50
#else
51
#define EXIT_SUCCESS  0
52
#endif
53
#endif
54
 
55
 
56
/*
57
 * These macros are used to read the input file.
58
 * To reuse this code in another application, you might need to change these.
59
 */
60
 
61
static FILE * infile;		/* input JPEG file */
62
 
63
/* Return next input byte, or EOF if no more */
64
#define NEXTBYTE()  getc(infile)
65
 
66
 
67
/* Error exit handler */
68
#define ERREXIT(msg)  (fprintf(stderr, "%s\n", msg), exit(EXIT_FAILURE))
69
 
70
 
71
/* Read one byte, testing for EOF */
72
static int
73
read_1_byte (void)
74
{
75
  int c;
76
 
77
  c = NEXTBYTE();
78
  if (c == EOF)
79
    ERREXIT("Premature EOF in JPEG file");
80
  return c;
81
}
82
 
83
/* Read 2 bytes, convert to unsigned int */
84
/* All 2-byte quantities in JPEG markers are MSB first */
85
static unsigned int
86
read_2_bytes (void)
87
{
88
  int c1, c2;
89
 
90
  c1 = NEXTBYTE();
91
  if (c1 == EOF)
92
    ERREXIT("Premature EOF in JPEG file");
93
  c2 = NEXTBYTE();
94
  if (c2 == EOF)
95
    ERREXIT("Premature EOF in JPEG file");
96
  return (((unsigned int) c1) << 8) + ((unsigned int) c2);
97
}
98
 
99
 
100
/*
101
 * JPEG markers consist of one or more 0xFF bytes, followed by a marker
102
 * code byte (which is not an FF).  Here are the marker codes of interest
103
 * in this program.  (See jdmarker.c for a more complete list.)
104
 */
105
 
106
#define M_SOF0  0xC0		/* Start Of Frame N */
107
#define M_SOF1  0xC1		/* N indicates which compression process */
108
#define M_SOF2  0xC2		/* Only SOF0-SOF2 are now in common use */
109
#define M_SOF3  0xC3
110
#define M_SOF5  0xC5		/* NB: codes C4 and CC are NOT SOF markers */
111
#define M_SOF6  0xC6
112
#define M_SOF7  0xC7
113
#define M_SOF9  0xC9
114
#define M_SOF10 0xCA
115
#define M_SOF11 0xCB
116
#define M_SOF13 0xCD
117
#define M_SOF14 0xCE
118
#define M_SOF15 0xCF
119
#define M_SOI   0xD8		/* Start Of Image (beginning of datastream) */
120
#define M_EOI   0xD9		/* End Of Image (end of datastream) */
121
#define M_SOS   0xDA		/* Start Of Scan (begins compressed data) */
122
#define M_APP0	0xE0		/* Application-specific marker, type N */
123
#define M_APP12	0xEC		/* (we don't bother to list all 16 APPn's) */
124
#define M_COM   0xFE		/* COMment */
125
 
126
 
127
/*
128
 * Find the next JPEG marker and return its marker code.
129
 * We expect at least one FF byte, possibly more if the compressor used FFs
130
 * to pad the file.
131
 * There could also be non-FF garbage between markers.  The treatment of such
132
 * garbage is unspecified; we choose to skip over it but emit a warning msg.
133
 * NB: this routine must not be used after seeing SOS marker, since it will
134
 * not deal correctly with FF/00 sequences in the compressed image data...
135
 */
136
 
137
static int
138
next_marker (void)
139
{
140
  int c;
141
  int discarded_bytes = 0;
142
 
143
  /* Find 0xFF byte; count and skip any non-FFs. */
144
  c = read_1_byte();
145
  while (c != 0xFF) {
146
    discarded_bytes++;
147
    c = read_1_byte();
148
  }
149
  /* Get marker code byte, swallowing any duplicate FF bytes.  Extra FFs
150
   * are legal as pad bytes, so don't count them in discarded_bytes.
151
   */
152
  do {
153
    c = read_1_byte();
154
  } while (c == 0xFF);
155
 
156
  if (discarded_bytes != 0) {
157
    fprintf(stderr, "Warning: garbage data found in JPEG file\n");
158
  }
159
 
160
  return c;
161
}
162
 
163
 
164
/*
165
 * Read the initial marker, which should be SOI.
166
 * For a JFIF file, the first two bytes of the file should be literally
167
 * 0xFF M_SOI.  To be more general, we could use next_marker, but if the
168
 * input file weren't actually JPEG at all, next_marker might read the whole
169
 * file and then return a misleading error message...
170
 */
171
 
172
static int
173
first_marker (void)
174
{
175
  int c1, c2;
176
 
177
  c1 = NEXTBYTE();
178
  c2 = NEXTBYTE();
179
  if (c1 != 0xFF || c2 != M_SOI)
180
    ERREXIT("Not a JPEG file");
181
  return c2;
182
}
183
 
184
 
185
/*
186
 * Most types of marker are followed by a variable-length parameter segment.
187
 * This routine skips over the parameters for any marker we don't otherwise
188
 * want to process.
189
 * Note that we MUST skip the parameter segment explicitly in order not to
190
 * be fooled by 0xFF bytes that might appear within the parameter segment;
191
 * such bytes do NOT introduce new markers.
192
 */
193
 
194
static void
195
skip_variable (void)
196
/* Skip over an unknown or uninteresting variable-length marker */
197
{
198
  unsigned int length;
199
 
200
  /* Get the marker parameter length count */
201
  length = read_2_bytes();
202
  /* Length includes itself, so must be at least 2 */
203
  if (length < 2)
204
    ERREXIT("Erroneous JPEG marker length");
205
  length -= 2;
206
  /* Skip over the remaining bytes */
207
  while (length > 0) {
208
    (void) read_1_byte();
209
    length--;
210
  }
211
}
212
 
213
 
214
/*
215
 * Process a COM marker.
216
 * We want to print out the marker contents as legible text;
217
 * we must guard against non-text junk and varying newline representations.
218
 */
219
 
220
static void
221
process_COM (void)
222
{
223
  unsigned int length;
224
  int ch;
225
  int lastch = 0;
226
 
227
  /* Get the marker parameter length count */
228
  length = read_2_bytes();
229
  /* Length includes itself, so must be at least 2 */
230
  if (length < 2)
231
    ERREXIT("Erroneous JPEG marker length");
232
  length -= 2;
233
 
234
  while (length > 0) {
235
    ch = read_1_byte();
236
    /* Emit the character in a readable form.
237
     * Nonprintables are converted to \nnn form,
238
     * while \ is converted to \\.
239
     * Newlines in CR, CR/LF, or LF form will be printed as one newline.
240
     */
241
    if (ch == '\r') {
242
      printf("\n");
243
    } else if (ch == '\n') {
244
      if (lastch != '\r')
245
	printf("\n");
246
    } else if (ch == '\\') {
247
      printf("\\\\");
248
    } else if (isprint(ch)) {
249
      putc(ch, stdout);
250
    } else {
251
      printf("\\%03o", ch);
252
    }
253
    lastch = ch;
254
    length--;
255
  }
256
  printf("\n");
257
}
258
 
259
 
260
/*
261
 * Process a SOFn marker.
262
 * This code is only needed if you want to know the image dimensions...
263
 */
264
 
265
static void
266
process_SOFn (int marker)
267
{
268
  unsigned int length;
269
  unsigned int image_height, image_width;
270
  int data_precision, num_components;
271
  const char * process;
272
  int ci;
273
 
274
  length = read_2_bytes();	/* usual parameter length count */
275
 
276
  data_precision = read_1_byte();
277
  image_height = read_2_bytes();
278
  image_width = read_2_bytes();
279
  num_components = read_1_byte();
280
 
281
  switch (marker) {
282
  case M_SOF0:	process = "Baseline";  break;
283
  case M_SOF1:	process = "Extended sequential";  break;
284
  case M_SOF2:	process = "Progressive";  break;
285
  case M_SOF3:	process = "Lossless";  break;
286
  case M_SOF5:	process = "Differential sequential";  break;
287
  case M_SOF6:	process = "Differential progressive";  break;
288
  case M_SOF7:	process = "Differential lossless";  break;
289
  case M_SOF9:	process = "Extended sequential, arithmetic coding";  break;
290
  case M_SOF10:	process = "Progressive, arithmetic coding";  break;
291
  case M_SOF11:	process = "Lossless, arithmetic coding";  break;
292
  case M_SOF13:	process = "Differential sequential, arithmetic coding";  break;
293
  case M_SOF14:	process = "Differential progressive, arithmetic coding"; break;
294
  case M_SOF15:	process = "Differential lossless, arithmetic coding";  break;
295
  default:	process = "Unknown";  break;
296
  }
297
 
298
  printf("JPEG image is %uw * %uh, %d color components, %d bits per sample\n",
299
	 image_width, image_height, num_components, data_precision);
300
  printf("JPEG process: %s\n", process);
301
 
302
  if (length != (unsigned int) (8 + num_components * 3))
303
    ERREXIT("Bogus SOF marker length");
304
 
305
  for (ci = 0; ci < num_components; ci++) {
306
    (void) read_1_byte();	/* Component ID code */
307
    (void) read_1_byte();	/* H, V sampling factors */
308
    (void) read_1_byte();	/* Quantization table number */
309
  }
310
}
311
 
312
 
313
/*
314
 * Parse the marker stream until SOS or EOI is seen;
315
 * display any COM markers.
316
 * While the companion program wrjpgcom will always insert COM markers before
317
 * SOFn, other implementations might not, so we scan to SOS before stopping.
318
 * If we were only interested in the image dimensions, we would stop at SOFn.
319
 * (Conversely, if we only cared about COM markers, there would be no need
320
 * for special code to handle SOFn; we could treat it like other markers.)
321
 */
322
 
323
static int
324
scan_JPEG_header (int verbose)
325
{
326
  int marker;
327
 
328
  /* Expect SOI at start of file */
329
  if (first_marker() != M_SOI)
330
    ERREXIT("Expected SOI marker first");
331
 
332
  /* Scan miscellaneous markers until we reach SOS. */
333
  for (;;) {
334
    marker = next_marker();
335
    switch (marker) {
336
      /* Note that marker codes 0xC4, 0xC8, 0xCC are not, and must not be,
337
       * treated as SOFn.  C4 in particular is actually DHT.
338
       */
339
    case M_SOF0:		/* Baseline */
340
    case M_SOF1:		/* Extended sequential, Huffman */
341
    case M_SOF2:		/* Progressive, Huffman */
342
    case M_SOF3:		/* Lossless, Huffman */
343
    case M_SOF5:		/* Differential sequential, Huffman */
344
    case M_SOF6:		/* Differential progressive, Huffman */
345
    case M_SOF7:		/* Differential lossless, Huffman */
346
    case M_SOF9:		/* Extended sequential, arithmetic */
347
    case M_SOF10:		/* Progressive, arithmetic */
348
    case M_SOF11:		/* Lossless, arithmetic */
349
    case M_SOF13:		/* Differential sequential, arithmetic */
350
    case M_SOF14:		/* Differential progressive, arithmetic */
351
    case M_SOF15:		/* Differential lossless, arithmetic */
352
      if (verbose)
353
	process_SOFn(marker);
354
      else
355
	skip_variable();
356
      break;
357
 
358
    case M_SOS:			/* stop before hitting compressed data */
359
      return marker;
360
 
361
    case M_EOI:			/* in case it's a tables-only JPEG stream */
362
      return marker;
363
 
364
    case M_COM:
365
      process_COM();
366
      break;
367
 
368
    case M_APP12:
369
      /* Some digital camera makers put useful textual information into
370
       * APP12 markers, so we print those out too when in -verbose mode.
371
       */
372
      if (verbose) {
373
	printf("APP12 contains:\n");
374
	process_COM();
375
      } else
376
	skip_variable();
377
      break;
378
 
379
    default:			/* Anything else just gets skipped */
380
      skip_variable();		/* we assume it has a parameter count... */
381
      break;
382
    }
383
  } /* end loop */
384
}
385
 
386
 
387
/* Command line parsing code */
388
 
389
static const char * progname;	/* program name for error messages */
390
 
391
 
392
static void
393
usage (void)
394
/* complain about bad command line */
395
{
396
  fprintf(stderr, "rdjpgcom displays any textual comments in a JPEG file.\n");
397
 
398
  fprintf(stderr, "Usage: %s [switches] [inputfile]\n", progname);
399
 
400
  fprintf(stderr, "Switches (names may be abbreviated):\n");
401
  fprintf(stderr, "  -verbose    Also display dimensions of JPEG image\n");
402
 
403
  exit(EXIT_FAILURE);
404
}
405
 
406
 
407
static int
408
keymatch (char * arg, const char * keyword, int minchars)
409
/* Case-insensitive matching of (possibly abbreviated) keyword switches. */
410
/* keyword is the constant keyword (must be lower case already), */
411
/* minchars is length of minimum legal abbreviation. */
412
{
413
  register int ca, ck;
414
  register int nmatched = 0;
415
 
416
  while ((ca = *arg++) != '\0') {
417
    if ((ck = *keyword++) == '\0')
418
      return 0;			/* arg longer than keyword, no good */
419
    if (isupper(ca))		/* force arg to lcase (assume ck is already) */
420
      ca = tolower(ca);
421
    if (ca != ck)
422
      return 0;			/* no good */
423
    nmatched++;			/* count matched characters */
424
  }
425
  /* reached end of argument; fail if it's too short for unique abbrev */
426
  if (nmatched < minchars)
427
    return 0;
428
  return 1;			/* A-OK */
429
}
430
 
431
 
432
/*
433
 * The main program.
434
 */
435
 
436
int
437
main (int argc, char **argv)
438
{
439
  int argn;
440
  char * arg;
441
  int verbose = 0;
442
 
443
  /* On Mac, fetch a command line. */
444
#ifdef USE_CCOMMAND
445
  argc = ccommand(&argv);
446
#endif
447
 
448
  progname = argv[0];
449
  if (progname == NULL || progname[0] == 0)
450
    progname = "rdjpgcom";	/* in case C library doesn't provide it */
451
 
452
  /* Parse switches, if any */
453
  for (argn = 1; argn < argc; argn++) {
454
    arg = argv[argn];
455
    if (arg[0] != '-')
456
      break;			/* not switch, must be file name */
457
    arg++;			/* advance over '-' */
458
    if (keymatch(arg, "verbose", 1)) {
459
      verbose++;
460
    } else
461
      usage();
462
  }
463
 
464
  /* Open the input file. */
465
  /* Unix style: expect zero or one file name */
466
  if (argn < argc-1) {
467
    fprintf(stderr, "%s: only one input file\n", progname);
468
    usage();
469
  }
470
  if (argn < argc) {
471
    if ((infile = fopen(argv[argn], READ_BINARY)) == NULL) {
472
      fprintf(stderr, "%s: can't open %s\n", progname, argv[argn]);
473
      exit(EXIT_FAILURE);
474
    }
475
  } else {
476
    /* default input file is stdin */
477
#ifdef USE_SETMODE		/* need to hack file mode? */
478
    setmode(fileno(stdin), O_BINARY);
479
#endif
480
#ifdef USE_FDOPEN		/* need to re-open in binary mode? */
481
    if ((infile = fdopen(fileno(stdin), READ_BINARY)) == NULL) {
482
      fprintf(stderr, "%s: can't open stdin\n", progname);
483
      exit(EXIT_FAILURE);
484
    }
485
#else
486
    infile = stdin;
487
#endif
488
  }
489
 
490
  /* Scan the JPEG headers. */
491
  (void) scan_JPEG_header(verbose);
492
 
493
  /* All done. */
494
  exit(EXIT_SUCCESS);
495
  return 0;			/* suppress no-return-value warnings */
496
}