/* * Copyright (c) 2001, 2002, Eric M. Johnston * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Eric M. Johnston. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: jpeg.c,v 1.5 2002/10/15 02:57:09 ejohnst Exp $ */ /* * This module contains a minimal set of functions for parsing a JPEG * file, specific for use in extracting EXIF information. Primarily, it * reads the input file and looks for the markers which indicate the start * of APP1 and APP2 sections. * * Portions of this code were developed while referencing the public domain * 'Jhead' program (version 1.2) by Matthias Wandel . */ #include #include "jpeg.h" #include "exif.h" static FILE *infile; /* Some data we collect from a start of frame. */ static int jpg_prcsn; /* Precision. */ static int jpg_cmpnts; /* Number of components. */ static unsigned int jpg_height; /* Image height. */ static unsigned int jpg_width; /* Image width. */ static const char *jpg_prcss; /* Process. */ static int seensof; /* Process string lookup table. */ static struct jpgprocess { int sof; const char *str; } process[] = { { JPEG_M_SOF0, "Baseline" }, { JPEG_M_SOF1, "Extended sequential" }, { JPEG_M_SOF2, "Progressive" }, { JPEG_M_SOF3, "Lossless" }, { JPEG_M_SOF5, "Differential sequential" }, { JPEG_M_SOF6, "Differential progressive" }, { JPEG_M_SOF7, "Differential lossless" }, { JPEG_M_SOF9, "Extended sequential, arithmetic coding" }, { JPEG_M_SOF10, "Progressive, arithmetic coding" }, { JPEG_M_SOF11, "Lossless, arithmetic coding" }, { JPEG_M_SOF13, "Differential sequential, arithmetic coding" }, { JPEG_M_SOF14, "Differential progressive, arithmetic coding" }, { JPEG_M_SOF15, "Differential lossless, arithmetic coding" }, { JPEG_M_ERR, "Unknown" }, }; /* * Fetch one byte of the JPEG file. */ static int jpg1byte(void) { int b; b = fgetc(infile); if (b == EOF) exifdie("invalid JPEG format"); return (b); } /* * Fetch two bytes of the JPEG file. */ static unsigned int jpg2byte(void) { unsigned int b1, b2; b1 = fgetc(infile); b2 = fgetc(infile); if (b1 == EOF || b2 == EOF) exifdie("invalid JPEG format"); return ((b1 << 8) | b2); } /* * Fetch the length of a marker. */ static unsigned int mkrlen(void) { unsigned int l; /* Length includes itself. */ if ((l = jpg2byte()) < 2) exifdie("invalid JPEG marker (length mismatch)"); return (l - 2); } /* * Skip over an uninteresting marker. */ static void skipmkr(void) { unsigned int l; l = mkrlen(); while (l) { jpg1byte(); l--; } } /* * Check the first bytes of the file for a marker. */ static int topmkr(void) { if (jpg1byte() != JPEG_M_BEG) return (JPEG_M_ERR); return (jpg1byte()); } /* * Find the next JPEG marker in the file. */ static int nxtmkr(void) { int b; int bad = 0; /* Read until we see JPEG_MARKER. */ while ((b = jpg1byte()) != JPEG_M_BEG) bad++; /* Read all JPEG_M_BEGs (which may be used for padding). */ while ((b = jpg1byte()) == JPEG_M_BEG); if (bad) exifwarn("skipped spurious bytes in JPEG"); return (b); } /* * Collect image data from the start of frame. * XXX Note that we clobber any previously collected info... */ static void sofmrk(int mark) { int i; unsigned int l; l = mkrlen(); jpg_prcsn = jpg1byte(); jpg_height = jpg2byte(); jpg_width = jpg2byte(); jpg_cmpnts = jpg1byte(); for (i = 0; process[i].sof < JPEG_M_ERR; i++) if (process[i].sof == mark) break; jpg_prcss = process[i].str; /* Verify length. */ if (l != (unsigned int)(6 + jpg_cmpnts * 3)) exifdie("invalid JPEG SOF marker (length mismatch)"); /* Skip over component info we don't care about. */ for (i = 0; i < jpg_cmpnts; i++) { jpg1byte(); jpg1byte(); jpg1byte(); } seensof = TRUE; } /* * Scan through a JPEG file for markers, returning interesting ones. * Returns false when it's done with the file. */ int jpegscan(FILE *fp, int *mark, unsigned int *len, int first) { infile = fp; /* First time through. */ if (first && topmkr() != JPEG_M_SOI) { exifwarn("doesn't appear to be a JPEG file; " "searching for start of image"); if (nxtmkr() != JPEG_M_SOI) exifdie("start of image not found"); } /* Look for interesting markers. */ for (;;) { switch ((*mark = nxtmkr())) { case JPEG_M_EOI: case JPEG_M_SOS: return (FALSE); case JPEG_M_APP1: case JPEG_M_APP2: *len = mkrlen(); return (TRUE); /* We might as well collect some useful info from SOFs. */ case JPEG_M_SOF0: case JPEG_M_SOF1: case JPEG_M_SOF3: case JPEG_M_SOF5: case JPEG_M_SOF6: case JPEG_M_SOF7: case JPEG_M_SOF9: case JPEG_M_SOF10: case JPEG_M_SOF11: case JPEG_M_SOF13: case JPEG_M_SOF14: case JPEG_M_SOF15: sofmrk(*mark); break; /* Just skip past markers we don't care about. */ default: skipmkr(); } } return (FALSE); } /* * Returns some basic image info about the JPEG, gleaned from start of * frame sections. */ int jpeginfo(int *prcsn, int *cmpnts, unsigned int *height, unsigned int *width, const char *prcss) { *prcsn = jpg_prcsn; *cmpnts = jpg_cmpnts; *height = jpg_height; *width = jpg_width; prcss = jpg_prcss; return (seensof); }