1 \/*
2  * jpegdump_main.cpp -- dump a JPEG stream
3  * by pts@math.bme.hu at Tue Jun  4 13:19:00 CEST 2002
4  */
5 
6 
7 /* Structure of a typical JPEG file:
8 (D8:SOI) (E0:APP0) (FE:COM)? (DB:DQT)*2 (C0:SOF0) (C4:DHT)*4 (DA:SOS)
9 ... (D9:EOI)
10 */
11 
12 #ifdef __GNUC__
13 #ifndef __clang__
14 #pragma implementation
15 #endif
16 #endif
17 
18 #include "config2.h"
19 #include <stdio.h>
20 
21 #if OBJDEP
22 #warning PROVIDES: jpegdump_main
23 #endif
24 
25 typedef slen_t dimen_t;
26 static const unsigned char
27     CS_UNKNOWN=0,           /* error/unspecified */
28     CS_GRAYSCALE=1,         /* monochrome */
29     CS_RGB=2,               /* red/green/blue */
30     CS_YCbCr=3,             /* Y/Cb/Cr (also known as YUV) */
31     CS_CMYK=4,              /* C/M/Y/K */
32     CS_YCCK=5,              /* Y/Cb/Cr/K */
33     CS_Indexed_RGB=12;
34 
35 
36 /* --- The following code is based on standard/image.c from PHP4 */
37 
38 /* some defines for the different JPEG block types */
39 #define M_SOF0  0xC0			/* Start Of Frame N */
40 #define M_SOF1  0xC1			/* N indicates which compression process */
41 #define M_SOF2  0xC2			/* Only SOF0-SOF2 are now in common use */
42 #define M_SOF3  0xC3
43 #define M_SOF5  0xC5			/* NB: codes C4 and CC are NOT SOF markers */
44 #define M_SOF6  0xC6
45 #define M_SOF7  0xC7
46 #define M_SOF9  0xC9
47 #define M_SOF10 0xCA
48 #define M_SOF11 0xCB
49 #define M_SOF13 0xCD
50 #define M_SOF14 0xCE
51 #define M_SOF15 0xCF
52 #define M_SOI   0xD8
53 #define M_EOI   0xD9			/* End Of Image (end of datastream) */
54 #define M_SOS   0xDA			/* Start Of Scan (begins compressed data) */
55 #define M_COM   0xFE                    /* comment */
56 #define M_DQT   0xDB			/* QuantTables */
57 #define M_DHT   0xC4			/* HuffTables */
58 #define M_APP0  0xe0
59 #define M_APP1  0xe1
60 #define M_APP2  0xe2
61 #define M_APP3  0xe3
62 #define M_APP4  0xe4
63 #define M_APP5  0xe5
64 #define M_APP6  0xe6
65 #define M_APP7  0xe7
66 #define M_APP8  0xe8
67 #define M_APP9  0xe9
68 #define M_APP10 0xea
69 #define M_APP11 0xeb
70 #define M_APP12 0xec
71 #define M_APP13 0xed
72 #define M_APP14 0xee
73 #define M_APP15 0xef
74 
75 
jai_read2(FILE * fp)76 static unsigned short jai_read2(FILE *fp) {
77   unsigned char a[ 2 ];
78 
79   /* just return 0 if we hit the end-of-file */
80   if (fread(a,sizeof(a),1,fp) != 1) return 0;
81 
82   return (((unsigned short) a[ 0 ]) << 8) + ((unsigned short) a[ 1 ]);
83 }
84 
jai_next_marker(FILE * fp)85 static unsigned int jai_next_marker(FILE *fp)
86    /* get next marker byte from file */
87 {
88   int c;
89 
90   /* skip unimportant stuff */
91 
92   c = MACRO_GETC(fp);
93 
94   while (c != 0xff) {
95     if ((c = MACRO_GETC(fp)) == EOF)
96       return M_EOI; /* we hit EOF */
97   }
98 
99   /* get marker byte, swallowing possible padding */
100   do {
101     if ((c = MACRO_GETC(fp)) == EOF)
102       return M_EOI;    /* we hit EOF */
103   } while (c == 0xff);
104 
105   // printf("marker=%02X\n", c);
106   return (unsigned int) c;
107 }
108 
jai_skip_variable(FILE * fp)109 static void jai_skip_variable(FILE *fp)
110    /* skip over a variable-length block; assumes proper length marker */
111 {
112   unsigned short length;
113 
114   printf("%lu: skip variable\n", ftell(fp));
115 
116   length = jai_read2(fp);
117   length -= 2;        /* length includes itself */
118   if (length>0) {
119     printf("... variable length=%u\n", length);
120     #if 0
121       fseek(fp, (long) length, SEEK_CUR);  /* skip the header */
122     #endif
123     while (length--!=0) MACRO_GETC(fp); /* make feof(fp) correct */
124     printf("%lu: skipped variable; feof=%u\n", ftell(fp), feof(fp));
125   }
126 }
127 
128 struct gfxinfo {
129   unsigned char bad, bpc, cpp, had_jfif, colortransform, id_rgb;
130   dimen_t height, width;
131   /** Offset of byte just _after_ the first SOF marker (FF C0 or alike)
132    * from the beginning of the file.
133    */
134   slen_t SOF_offs;
135 };
136 
137 /** main loop to parse JPEG structure */
jai_handle_jpeg(struct gfxinfo * result,FILE * fp)138 static void jai_handle_jpeg(struct gfxinfo *result, FILE *fp) {
139   unsigned int length, marker;
140   unsigned char had_adobe, id, hvs, qtn;
141 
142   result->bad=1; /* signal invalid return value */
143   result->id_rgb=0;
144   result->had_jfif=0;
145   result->colortransform=127; /* no Adobe marker yet */
146   // fseek(fp, 0L, SEEK_SET);    /* position file pointer on SOF */
147 
148   if (MACRO_GETC(fp) != 0xFF || MACRO_GETC(fp)!=M_SOI) return; /* JPEG header... */
149   printf("2: marker D8 (SOI)\n");
150 
151   for (;;) {
152     assert(!feof(fp));
153     printf("... %lu\n", ftell(fp));
154     marker=jai_next_marker(fp);
155     printf("%lu: marker %02X\n", ftell(fp), marker);
156     switch (marker) {
157     case M_SOF0:
158       if (result->bad!=1) { result->bad=4; return; } /* only one M_SOF allowed */
159       result->SOF_offs=ftell(fp);
160       /* handle SOFn block */
161       length=jai_read2(fp);
162       result->bpc = MACRO_GETC(fp);
163       result->height = jai_read2(fp);
164       result->width = jai_read2(fp);
165       result->cpp = MACRO_GETC(fp);
166       if ((length-=8)!=3U*result->cpp) return;
167       if (result->cpp==3) {
168         result->id_rgb =(id=MACRO_GETC(fp)=='R');  hvs=MACRO_GETC(fp); qtn=MACRO_GETC(fp);
169         printf("Comp#1 id=0x%02X h_samp_fact=%u v_samp_fact=%u quant_tbl_no=%u\n",
170           id, hvs>>4, hvs&15, qtn);
171         result->id_rgb&=(id=MACRO_GETC(fp)=='G');  hvs=MACRO_GETC(fp); qtn=MACRO_GETC(fp);
172         printf("Comp#2 id=0x%02X h_samp_fact=%u v_samp_fact=%u quant_tbl_no=%u\n",
173           id, hvs>>4, hvs&15, qtn);
174         result->id_rgb&=(id=MACRO_GETC(fp)=='B');  hvs=MACRO_GETC(fp); qtn=MACRO_GETC(fp);
175         printf("Comp#3 id=0x%02X h_samp_fact=%u v_samp_fact=%u quant_tbl_no=%u\n",
176           id, hvs>>4, hvs&15, qtn);
177       } else while (length--!=0) MACRO_GETC(fp);
178       if (result->cpp!=1 && result->cpp!=3 && result->cpp!=4) {
179         result->bad=5; return;
180       }
181       if (feof(fp)) { result->bad=8; return; }
182       result->bad=2;
183       break;
184     case M_SOF1:
185     case M_SOF2:
186     case M_SOF3:
187     case M_SOF5:
188     case M_SOF6:
189     case M_SOF7:
190     case M_SOF9:
191     case M_SOF10:
192     case M_SOF11:
193     case M_SOF13:
194     case M_SOF14:
195     case M_SOF15:
196       // fprintf(stderr, "SOF%u\n", marker-M_SOF0); assert(0);
197       result->bad=3;
198       return;
199     case M_SOS: /* we are about to hit image data. We're done. */
200       jai_skip_variable(fp);    /* anything else isn't interesting */
201       if (feof(fp)) { result->bad=8; return; }
202       if (result->bad==2 && !feof(fp)) result->bad=0;
203       /* Dat: we should really return() here, because the SOS marker is
204        * followed by binary data, not surrounded by markers.
205        */
206       break;
207     case M_EOI: /* end of image or EOF */
208       if (feof(fp)) result->bad=6;
209       else if (MACRO_GETC(fp)!=EOF) result->bad=7;
210       return;
211     case M_APP0: /* JFIF application-specific marker */
212       length=jai_read2(fp);
213       if (length==2+4+1+2+1+2+2+1+1) {
214         result->had_jfif=MACRO_GETC(fp)=='J' && MACRO_GETC(fp)=='F' && MACRO_GETC(fp)=='I' &&
215                          MACRO_GETC(fp)=='F' && MACRO_GETC(fp)==0;
216         length-=7;
217       } else length-=2;
218       while (length--!=0) MACRO_GETC(fp);
219       if (feof(fp)) { result->bad=8; return; }
220       break;
221     case M_APP14: /* Adobe application-specific marker */
222       length=jai_read2(fp);
223       if ((length-=2)==5+2+2+2+1) {
224         had_adobe=MACRO_GETC(fp)=='A' && MACRO_GETC(fp)=='d' && MACRO_GETC(fp)=='o' &&
225                   MACRO_GETC(fp)=='b' && MACRO_GETC(fp)=='e' && ((unsigned char)MACRO_GETC(fp))>=1;
226         MACRO_GETC(fp); MACRO_GETC(fp); MACRO_GETC(fp); MACRO_GETC(fp); MACRO_GETC(fp);
227         if (had_adobe) result->colortransform=MACRO_GETC(fp);
228                   else MACRO_GETC(fp);
229       } else while (length--!=0) MACRO_GETC(fp);
230       if (feof(fp)) { result->bad=8; return; }
231       break;
232     case M_APP1:
233     case M_APP2:
234     case M_APP3:
235     case M_APP4:
236     case M_APP5:
237     case M_APP6:
238     case M_APP7:
239     case M_APP8:
240     case M_APP9:
241     case M_APP10:
242     case M_APP11:
243     case M_APP12:
244     case M_APP13:
245     case M_APP15:
246       /* fall through */
247     default:
248       jai_skip_variable(fp);    /* anything else isn't interesting */
249       if (feof(fp)) { result->bad=8; return; }
250       break;
251   } }
252 }
253 
254 static char *jai_errors[]={
255   (char*)NULLP,
256   /*1*/ "missing SOF0 marker",
257   /*2*/ "EOF|EOI before SOS", /* "premature EOF", */
258   /*3*/ "not a Baseline JPEG (SOF must be SOF0)",
259   /*4*/ "more SOF0 markers",
260   /*5*/ "bad # components",
261   /*6*/ "EOF before EOI",
262   /*7*/ "extra bytes after EOI",
263   /*8*/ "EOF before end of parametric marker",
264 };
265 
main(int argc,char ** argv)266 int main(int argc, char **argv) {
267   struct gfxinfo gi;
268   FILE *f=argc>=2 ? fopen(argv[1], "rb"): stdin;
269   jai_handle_jpeg(&gi, f);
270   if (gi.bad==1) {
271     printf("JAI: Warning: %s.\n", jai_errors[gi.bad]);
272     gi.cpp=1;
273     gi.width=0;
274     gi.height=0;
275     gi.SOF_offs=0;
276     gi.bpc=0;
277   } else if (gi.bad!=0) { printf("JAI: %s.\n", jai_errors[gi.bad]); if (gi.bad!=1) return 1; }
278   unsigned char colorspace;
279   if (gi.cpp==1) {
280     colorspace=CS_GRAYSCALE;
281   } else if (gi.cpp==3) {
282     colorspace=CS_YCbCr;
283     if (gi.had_jfif!=0) ;
284     else if (gi.colortransform==0) colorspace=CS_RGB;
285     else if (gi.colortransform==1) ;
286     else if (gi.colortransform!=127) { printf("JAI: unknown ColorTransform: %u\n", (unsigned)gi.colortransform); return 2; }
287     else if (gi.id_rgb!=0) colorspace=CS_RGB;
288     /* Imp: check for id_ycbcr */
289     else printf("JAI: assuming YCbCr color space\n");
290   } else if (gi.cpp==4) {
291     colorspace=CS_CMYK;
292     if (gi.colortransform==0) ;
293     else if (gi.colortransform==2) colorspace=CS_YCCK;
294     else if (gi.colortransform!=127) { printf("JAI: unknown ColorTransform: %u\n", (unsigned)gi.colortransform); return 2; }
295   } else assert(0);
296   fseek(f, 0L, 2); /* EOF */
297   long flen=ftell(f); /* skip extra bytes after EOI */
298   fclose(f);
299   printf("%lu: flen\n", flen);
300   printf("bpc=%u cpp=%u had_jfif=%u colortransform=%u id_rgb=%u colorspace=%u\n",
301     gi.bpc, gi.cpp, gi.had_jfif, gi.colortransform, gi.id_rgb, colorspace);
302   printf("wd=%u ht=%u SOF_offs=%u\n", gi.width, gi.height, gi.SOF_offs);
303   return 0;
304 }
305 
306 /* __EOF__ */
307