SAMV71 Xplained Ultra Software Package 1.4

rdjpgcom.c

00001 /*
00002  * rdjpgcom.c
00003  *
00004  * Copyright (C) 1994-1997, Thomas G. Lane.
00005  * Modified 2009 by Bill Allombert, Guido Vollbeding.
00006  * This file is part of the Independent JPEG Group's software.
00007  * For conditions of distribution and use, see the accompanying README file.
00008  *
00009  * This file contains a very simple stand-alone application that displays
00010  * the text in COM (comment) markers in a JFIF file.
00011  * This may be useful as an example of the minimum logic needed to parse
00012  * JPEG markers.
00013  */
00014 
00015 #define JPEG_CJPEG_DJPEG    /* to get the command-line config symbols */
00016 #include "jinclude.h"       /* get auto-config symbols, <stdio.h> */
00017 
00018 #ifdef HAVE_LOCALE_H
00019 #include <locale.h>     /* Bill Allombert: use locale for isprint */
00020 #endif
00021 #include <ctype.h>      /* to declare isupper(), tolower() */
00022 #ifdef USE_SETMODE
00023 #include <fcntl.h>      /* to declare setmode()'s parameter macros */
00024 /* If you have setmode() but not <io.h>, just delete this line: */
00025 #include <io.h>         /* to declare setmode() */
00026 #endif
00027 
00028 #ifdef USE_CCOMMAND     /* command-line reader for Macintosh */
00029 #ifdef __MWERKS__
00030 #include <SIOUX.h>              /* Metrowerks needs this */
00031 #include <console.h>        /* ... and this */
00032 #endif
00033 #ifdef THINK_C
00034 #include <console.h>        /* Think declares it here */
00035 #endif
00036 #endif
00037 
00038 #ifdef DONT_USE_B_MODE      /* define mode parameters for fopen() */
00039 #define READ_BINARY "r"
00040 #else
00041 #ifdef VMS          /* VMS is very nonstandard */
00042 #define READ_BINARY "rb", "ctx=stm"
00043 #else               /* standard ANSI-compliant case */
00044 #define READ_BINARY "rb"
00045 #endif
00046 #endif
00047 
00048 #ifndef EXIT_FAILURE        /* define exit() codes if not provided */
00049 #define EXIT_FAILURE  1
00050 #endif
00051 #ifndef EXIT_SUCCESS
00052 #ifdef VMS
00053 #define EXIT_SUCCESS  1     /* VMS is very nonstandard */
00054 #else
00055 #define EXIT_SUCCESS  0
00056 #endif
00057 #endif
00058 
00059 
00060 /*
00061  * These macros are used to read the input file.
00062  * To reuse this code in another application, you might need to change these.
00063  */
00064 
00065 static FILE * infile;       /* input JPEG file */
00066 
00067 /* Return next input byte, or EOF if no more */
00068 #define NEXTBYTE()  getc(infile)
00069 
00070 
00071 /* Error exit handler */
00072 #define ERREXIT(msg)  (fprintf(stderr, "%s\n", msg), exit(EXIT_FAILURE))
00073 
00074 
00075 /* Read one byte, testing for EOF */
00076 static int
00077 read_1_byte (void)
00078 {
00079   int c;
00080 
00081   c = NEXTBYTE();
00082   if (c == EOF)
00083     ERREXIT("Premature EOF in JPEG file");
00084   return c;
00085 }
00086 
00087 /* Read 2 bytes, convert to unsigned int */
00088 /* All 2-byte quantities in JPEG markers are MSB first */
00089 static unsigned int
00090 read_2_bytes (void)
00091 {
00092   int c1, c2;
00093 
00094   c1 = NEXTBYTE();
00095   if (c1 == EOF)
00096     ERREXIT("Premature EOF in JPEG file");
00097   c2 = NEXTBYTE();
00098   if (c2 == EOF)
00099     ERREXIT("Premature EOF in JPEG file");
00100   return (((unsigned int) c1) << 8) + ((unsigned int) c2);
00101 }
00102 
00103 
00104 /*
00105  * JPEG markers consist of one or more 0xFF bytes, followed by a marker
00106  * code byte (which is not an FF).  Here are the marker codes of interest
00107  * in this program.  (See jdmarker.c for a more complete list.)
00108  */
00109 
00110 #define M_SOF0  0xC0        /* Start Of Frame N */
00111 #define M_SOF1  0xC1        /* N indicates which compression process */
00112 #define M_SOF2  0xC2        /* Only SOF0-SOF2 are now in common use */
00113 #define M_SOF3  0xC3
00114 #define M_SOF5  0xC5        /* NB: codes C4 and CC are NOT SOF markers */
00115 #define M_SOF6  0xC6
00116 #define M_SOF7  0xC7
00117 #define M_SOF9  0xC9
00118 #define M_SOF10 0xCA
00119 #define M_SOF11 0xCB
00120 #define M_SOF13 0xCD
00121 #define M_SOF14 0xCE
00122 #define M_SOF15 0xCF
00123 #define M_SOI   0xD8        /* Start Of Image (beginning of datastream) */
00124 #define M_EOI   0xD9        /* End Of Image (end of datastream) */
00125 #define M_SOS   0xDA        /* Start Of Scan (begins compressed data) */
00126 #define M_APP0  0xE0        /* Application-specific marker, type N */
00127 #define M_APP12 0xEC        /* (we don't bother to list all 16 APPn's) */
00128 #define M_COM   0xFE        /* COMment */
00129 
00130 
00131 /*
00132  * Find the next JPEG marker and return its marker code.
00133  * We expect at least one FF byte, possibly more if the compressor used FFs
00134  * to pad the file.
00135  * There could also be non-FF garbage between markers.  The treatment of such
00136  * garbage is unspecified; we choose to skip over it but emit a warning msg.
00137  * NB: this routine must not be used after seeing SOS marker, since it will
00138  * not deal correctly with FF/00 sequences in the compressed image data...
00139  */
00140 
00141 static int
00142 next_marker (void)
00143 {
00144   int c;
00145   int discarded_bytes = 0;
00146 
00147   /* Find 0xFF byte; count and skip any non-FFs. */
00148   c = read_1_byte();
00149   while (c != 0xFF) {
00150     discarded_bytes++;
00151     c = read_1_byte();
00152   }
00153   /* Get marker code byte, swallowing any duplicate FF bytes.  Extra FFs
00154    * are legal as pad bytes, so don't count them in discarded_bytes.
00155    */
00156   do {
00157     c = read_1_byte();
00158   } while (c == 0xFF);
00159 
00160   if (discarded_bytes != 0) {
00161     fprintf(stderr, "Warning: garbage data found in JPEG file\n");
00162   }
00163 
00164   return c;
00165 }
00166 
00167 
00168 /*
00169  * Read the initial marker, which should be SOI.
00170  * For a JFIF file, the first two bytes of the file should be literally
00171  * 0xFF M_SOI.  To be more general, we could use next_marker, but if the
00172  * input file weren't actually JPEG at all, next_marker might read the whole
00173  * file and then return a misleading error message...
00174  */
00175 
00176 static int
00177 first_marker (void)
00178 {
00179   int c1, c2;
00180 
00181   c1 = NEXTBYTE();
00182   c2 = NEXTBYTE();
00183   if (c1 != 0xFF || c2 != M_SOI)
00184     ERREXIT("Not a JPEG file");
00185   return c2;
00186 }
00187 
00188 
00189 /*
00190  * Most types of marker are followed by a variable-length parameter segment.
00191  * This routine skips over the parameters for any marker we don't otherwise
00192  * want to process.
00193  * Note that we MUST skip the parameter segment explicitly in order not to
00194  * be fooled by 0xFF bytes that might appear within the parameter segment;
00195  * such bytes do NOT introduce new markers.
00196  */
00197 
00198 static void
00199 skip_variable (void)
00200 /* Skip over an unknown or uninteresting variable-length marker */
00201 {
00202   unsigned int length;
00203 
00204   /* Get the marker parameter length count */
00205   length = read_2_bytes();
00206   /* Length includes itself, so must be at least 2 */
00207   if (length < 2)
00208     ERREXIT("Erroneous JPEG marker length");
00209   length -= 2;
00210   /* Skip over the remaining bytes */
00211   while (length > 0) {
00212     (void) read_1_byte();
00213     length--;
00214   }
00215 }
00216 
00217 
00218 /*
00219  * Process a COM marker.
00220  * We want to print out the marker contents as legible text;
00221  * we must guard against non-text junk and varying newline representations.
00222  */
00223 
00224 static void
00225 process_COM (int raw)
00226 {
00227   unsigned int length;
00228   int ch;
00229   int lastch = 0;
00230 
00231   /* Bill Allombert: set locale properly for isprint */
00232 #ifdef HAVE_LOCALE_H
00233   setlocale(LC_CTYPE, "");
00234 #endif
00235 
00236   /* Get the marker parameter length count */
00237   length = read_2_bytes();
00238   /* Length includes itself, so must be at least 2 */
00239   if (length < 2)
00240     ERREXIT("Erroneous JPEG marker length");
00241   length -= 2;
00242 
00243   while (length > 0) {
00244     ch = read_1_byte();
00245     if (raw) {
00246       putc(ch, stdout);
00247     /* Emit the character in a readable form.
00248      * Nonprintables are converted to \nnn form,
00249      * while \ is converted to \\.
00250      * Newlines in CR, CR/LF, or LF form will be printed as one newline.
00251      */
00252     } else if (ch == '\r') {
00253       printf("\n");
00254     } else if (ch == '\n') {
00255       if (lastch != '\r')
00256     printf("\n");
00257     } else if (ch == '\\') {
00258       printf("\\\\");
00259     } else if (isprint(ch)) {
00260       putc(ch, stdout);
00261     } else {
00262       printf("\\%03o", ch);
00263     }
00264     lastch = ch;
00265     length--;
00266   }
00267   printf("\n");
00268 
00269   /* Bill Allombert: revert to C locale */
00270 #ifdef HAVE_LOCALE_H
00271   setlocale(LC_CTYPE, "C");
00272 #endif
00273 }
00274 
00275 
00276 /*
00277  * Process a SOFn marker.
00278  * This code is only needed if you want to know the image dimensions...
00279  */
00280 
00281 static void
00282 process_SOFn (int marker)
00283 {
00284   unsigned int length;
00285   unsigned int image_height, image_width;
00286   int data_precision, num_components;
00287   const char * process;
00288   int ci;
00289 
00290   length = read_2_bytes();  /* usual parameter length count */
00291 
00292   data_precision = read_1_byte();
00293   image_height = read_2_bytes();
00294   image_width = read_2_bytes();
00295   num_components = read_1_byte();
00296 
00297   switch (marker) {
00298   case M_SOF0:  process = "Baseline";  break;
00299   case M_SOF1:  process = "Extended sequential";  break;
00300   case M_SOF2:  process = "Progressive";  break;
00301   case M_SOF3:  process = "Lossless";  break;
00302   case M_SOF5:  process = "Differential sequential";  break;
00303   case M_SOF6:  process = "Differential progressive";  break;
00304   case M_SOF7:  process = "Differential lossless";  break;
00305   case M_SOF9:  process = "Extended sequential, arithmetic coding";  break;
00306   case M_SOF10: process = "Progressive, arithmetic coding";  break;
00307   case M_SOF11: process = "Lossless, arithmetic coding";  break;
00308   case M_SOF13: process = "Differential sequential, arithmetic coding";  break;
00309   case M_SOF14: process = "Differential progressive, arithmetic coding"; break;
00310   case M_SOF15: process = "Differential lossless, arithmetic coding";  break;
00311   default:  process = "Unknown";  break;
00312   }
00313 
00314   printf("JPEG image is %uw * %uh, %d color components, %d bits per sample\n",
00315      image_width, image_height, num_components, data_precision);
00316   printf("JPEG process: %s\n", process);
00317 
00318   if (length != (unsigned int) (8 + num_components * 3))
00319     ERREXIT("Bogus SOF marker length");
00320 
00321   for (ci = 0; ci < num_components; ci++) {
00322     (void) read_1_byte();   /* Component ID code */
00323     (void) read_1_byte();   /* H, V sampling factors */
00324     (void) read_1_byte();   /* Quantization table number */
00325   }
00326 }
00327 
00328 
00329 /*
00330  * Parse the marker stream until SOS or EOI is seen;
00331  * display any COM markers.
00332  * While the companion program wrjpgcom will always insert COM markers before
00333  * SOFn, other implementations might not, so we scan to SOS before stopping.
00334  * If we were only interested in the image dimensions, we would stop at SOFn.
00335  * (Conversely, if we only cared about COM markers, there would be no need
00336  * for special code to handle SOFn; we could treat it like other markers.)
00337  */
00338 
00339 static int
00340 scan_JPEG_header (int verbose, int raw)
00341 {
00342   int marker;
00343 
00344   /* Expect SOI at start of file */
00345   if (first_marker() != M_SOI)
00346     ERREXIT("Expected SOI marker first");
00347 
00348   /* Scan miscellaneous markers until we reach SOS. */
00349   for (;;) {
00350     marker = next_marker();
00351     switch (marker) {
00352       /* Note that marker codes 0xC4, 0xC8, 0xCC are not, and must not be,
00353        * treated as SOFn.  C4 in particular is actually DHT.
00354        */
00355     case M_SOF0:        /* Baseline */
00356     case M_SOF1:        /* Extended sequential, Huffman */
00357     case M_SOF2:        /* Progressive, Huffman */
00358     case M_SOF3:        /* Lossless, Huffman */
00359     case M_SOF5:        /* Differential sequential, Huffman */
00360     case M_SOF6:        /* Differential progressive, Huffman */
00361     case M_SOF7:        /* Differential lossless, Huffman */
00362     case M_SOF9:        /* Extended sequential, arithmetic */
00363     case M_SOF10:       /* Progressive, arithmetic */
00364     case M_SOF11:       /* Lossless, arithmetic */
00365     case M_SOF13:       /* Differential sequential, arithmetic */
00366     case M_SOF14:       /* Differential progressive, arithmetic */
00367     case M_SOF15:       /* Differential lossless, arithmetic */
00368       if (verbose)
00369     process_SOFn(marker);
00370       else
00371     skip_variable();
00372       break;
00373 
00374     case M_SOS:         /* stop before hitting compressed data */
00375       return marker;
00376 
00377     case M_EOI:         /* in case it's a tables-only JPEG stream */
00378       return marker;
00379 
00380     case M_COM:
00381       process_COM(raw);
00382       break;
00383 
00384     case M_APP12:
00385       /* Some digital camera makers put useful textual information into
00386        * APP12 markers, so we print those out too when in -verbose mode.
00387        */
00388       if (verbose) {
00389     printf("APP12 contains:\n");
00390     process_COM(raw);
00391       } else
00392     skip_variable();
00393       break;
00394 
00395     default:            /* Anything else just gets skipped */
00396       skip_variable();      /* we assume it has a parameter count... */
00397       break;
00398     }
00399   } /* end loop */
00400 }
00401 
00402 
00403 /* Command line parsing code */
00404 
00405 static const char * progname;   /* program name for error messages */
00406 
00407 
00408 static void
00409 usage (void)
00410 /* complain about bad command line */
00411 {
00412   fprintf(stderr, "rdjpgcom displays any textual comments in a JPEG file.\n");
00413 
00414   fprintf(stderr, "Usage: %s [switches] [inputfile]\n", progname);
00415 
00416   fprintf(stderr, "Switches (names may be abbreviated):\n");
00417   fprintf(stderr, "  -raw        Display non-printable characters in comments (unsafe)\n");
00418   fprintf(stderr, "  -verbose    Also display dimensions of JPEG image\n");
00419 
00420   exit(EXIT_FAILURE);
00421 }
00422 
00423 
00424 static int
00425 keymatch (char * arg, const char * keyword, int minchars)
00426 /* Case-insensitive matching of (possibly abbreviated) keyword switches. */
00427 /* keyword is the constant keyword (must be lower case already), */
00428 /* minchars is length of minimum legal abbreviation. */
00429 {
00430   register int ca, ck;
00431   register int nmatched = 0;
00432 
00433   while ((ca = *arg++) != '\0') {
00434     if ((ck = *keyword++) == '\0')
00435       return 0;         /* arg longer than keyword, no good */
00436     if (isupper(ca))        /* force arg to lcase (assume ck is already) */
00437       ca = tolower(ca);
00438     if (ca != ck)
00439       return 0;         /* no good */
00440     nmatched++;         /* count matched characters */
00441   }
00442   /* reached end of argument; fail if it's too short for unique abbrev */
00443   if (nmatched < minchars)
00444     return 0;
00445   return 1;         /* A-OK */
00446 }
00447 
00448 
00449 /*
00450  * The main program.
00451  */
00452 
00453 int
00454 main (int argc, char **argv)
00455 {
00456   int argn;
00457   char * arg;
00458   int verbose = 0, raw = 0;
00459 
00460   /* On Mac, fetch a command line. */
00461 #ifdef USE_CCOMMAND
00462   argc = ccommand(&argv);
00463 #endif
00464 
00465   progname = argv[0];
00466   if (progname == NULL || progname[0] == 0)
00467     progname = "rdjpgcom";  /* in case C library doesn't provide it */
00468 
00469   /* Parse switches, if any */
00470   for (argn = 1; argn < argc; argn++) {
00471     arg = argv[argn];
00472     if (arg[0] != '-')
00473       break;            /* not switch, must be file name */
00474     arg++;          /* advance over '-' */
00475     if (keymatch(arg, "verbose", 1)) {
00476       verbose++;
00477     } else if (keymatch(arg, "raw", 1)) {
00478       raw = 1;
00479     } else
00480       usage();
00481   }
00482 
00483   /* Open the input file. */
00484   /* Unix style: expect zero or one file name */
00485   if (argn < argc-1) {
00486     fprintf(stderr, "%s: only one input file\n", progname);
00487     usage();
00488   }
00489   if (argn < argc) {
00490     if ((infile = fopen(argv[argn], READ_BINARY)) == NULL) {
00491       fprintf(stderr, "%s: can't open %s\n", progname, argv[argn]);
00492       exit(EXIT_FAILURE);
00493     }
00494   } else {
00495     /* default input file is stdin */
00496 #ifdef USE_SETMODE      /* need to hack file mode? */
00497     setmode(fileno(stdin), O_BINARY);
00498 #endif
00499 #ifdef USE_FDOPEN       /* need to re-open in binary mode? */
00500     if ((infile = fdopen(fileno(stdin), READ_BINARY)) == NULL) {
00501       fprintf(stderr, "%s: can't open stdin\n", progname);
00502       exit(EXIT_FAILURE);
00503     }
00504 #else
00505     infile = stdin;
00506 #endif
00507   }
00508 
00509   /* Scan the JPEG headers. */
00510   (void) scan_JPEG_header(verbose, raw);
00511 
00512   /* All done. */
00513   exit(EXIT_SUCCESS);
00514   return 0;         /* suppress no-return-value warnings */
00515 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines