diff options
Diffstat (limited to 'jrdjfif.c')
-rw-r--r-- | jrdjfif.c | 733 |
1 files changed, 733 insertions, 0 deletions
diff --git a/jrdjfif.c b/jrdjfif.c new file mode 100644 index 0000000..5ff14ba --- /dev/null +++ b/jrdjfif.c @@ -0,0 +1,733 @@ +/* + * jrdjfif.c + * + * Copyright (C) 1991, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to decode standard JPEG file headers/markers. + * This will handle baseline and JFIF-convention JPEG files. + * + * This module relies on the JGETC macro and the read_jpeg_data method (which + * is provided by the user interface) to read from the JPEG data stream. + * Therefore, this module is NOT dependent on any particular assumption about + * the data source. This fact does not carry over to more complex JPEG file + * formats such as JPEG-in-TIFF; those format control modules may well need to + * assume stdio input. + * + * read_file_header assumes that reading begins at the JPEG SOI marker + * (although it will skip non-FF bytes looking for a JPEG marker). + * The user interface must position the data stream appropriately. + * + * These routines are invoked via the methods read_file_header, + * read_scan_header, read_jpeg_data, read_scan_trailer, and read_file_trailer. + */ + +#include "jinclude.h" + +#ifdef JFIF_SUPPORTED + + +typedef enum { /* JPEG marker codes */ + M_SOF0 = 0xc0, + M_SOF1 = 0xc1, + M_SOF2 = 0xc2, + M_SOF3 = 0xc3, + + M_SOF5 = 0xc5, + M_SOF6 = 0xc6, + M_SOF7 = 0xc7, + + M_JPG = 0xc8, + M_SOF9 = 0xc9, + M_SOF10 = 0xca, + M_SOF11 = 0xcb, + + M_SOF13 = 0xcd, + M_SOF14 = 0xce, + M_SOF15 = 0xcf, + + M_DHT = 0xc4, + + M_DAC = 0xcc, + + M_RST0 = 0xd0, + M_RST1 = 0xd1, + M_RST2 = 0xd2, + M_RST3 = 0xd3, + M_RST4 = 0xd4, + M_RST5 = 0xd5, + M_RST6 = 0xd6, + M_RST7 = 0xd7, + + M_SOI = 0xd8, + M_EOI = 0xd9, + M_SOS = 0xda, + M_DQT = 0xdb, + M_DNL = 0xdc, + M_DRI = 0xdd, + M_DHP = 0xde, + M_EXP = 0xdf, + + M_APP0 = 0xe0, + M_APP15 = 0xef, + + M_JPG0 = 0xf0, + M_JPG13 = 0xfd, + M_COM = 0xfe, + + M_TEM = 0x01, + + M_ERROR = 0x100 +} JPEG_MARKER; + + +/* + * Reload the input buffer after it's been emptied, and return the next byte. + * This is exported for direct use by the entropy decoder. + * See the JGETC macro for calling conditions. + * + * For this header control module, read_jpeg_data is supplied by the + * user interface. However, header formats that require random access + * to the input file would need to supply their own code. This code is + * left here to indicate what is required. + */ + +#if 0 /* not needed in this module */ + +METHODDEF int +read_jpeg_data (decompress_info_ptr cinfo) +{ + cinfo->bytes_in_buffer = fread(cinfo->input_buffer + MIN_UNGET, + 1, JPEG_BUF_SIZE, + cinfo->input_file); + + cinfo->next_input_byte = cinfo->input_buffer + MIN_UNGET; + + if (cinfo->bytes_in_buffer <= 0) + ERREXIT(cinfo->emethods, "Unexpected EOF in JPEG file"); + + return JGETC(cinfo); +} + +#endif + + +/* + * Routines to parse JPEG markers & save away the useful info. + */ + + +LOCAL INT32 +get_2bytes (decompress_info_ptr cinfo) +/* Get a 2-byte unsigned integer (e.g., a marker parameter length field) */ +{ + INT32 a; + + a = JGETC(cinfo); + return (a << 8) + JGETC(cinfo); +} + + +LOCAL void +skip_variable (decompress_info_ptr cinfo, int code) +/* Skip over an unknown or uninteresting variable-length marker */ +{ + INT32 length; + + length = get_2bytes(cinfo); + + TRACEMS2(cinfo->emethods, 1, + "Skipping marker 0x%02x, length %d", code, length); + + for (length -= 2; length > 0; length--) + (void) JGETC(cinfo); +} + + +LOCAL void +get_dht (decompress_info_ptr cinfo) +/* Process a DHT marker */ +{ + INT32 length; + UINT8 bits[17]; + UINT8 huffval[256]; + int i, index, count; + HUFF_TBL **htblptr; + + length = get_2bytes(cinfo)-2; + + while (length > 0) { + index = JGETC(cinfo); + + TRACEMS1(cinfo->emethods, 1, "Define Huffman Table 0x%02x", index); + + bits[0] = 0; + count = 0; + for (i = 1; i <= 16; i++) { + bits[i] = JGETC(cinfo); + count += bits[i]; + } + + TRACEMS8(cinfo->emethods, 2, " %3d %3d %3d %3d %3d %3d %3d %3d", + bits[1], bits[2], bits[3], bits[4], + bits[5], bits[6], bits[7], bits[8]); + TRACEMS8(cinfo->emethods, 2, " %3d %3d %3d %3d %3d %3d %3d %3d", + bits[9], bits[10], bits[11], bits[12], + bits[13], bits[14], bits[15], bits[16]); + + if (count > 256) + ERREXIT(cinfo->emethods, "Bogus DHT counts"); + + for (i = 0; i < count; i++) + huffval[i] = JGETC(cinfo); + + length -= 1 + 16 + count; + + if (index & 0x10) { /* AC table definition */ + index -= 0x10; + htblptr = &cinfo->ac_huff_tbl_ptrs[index]; + } else { /* DC table definition */ + htblptr = &cinfo->dc_huff_tbl_ptrs[index]; + } + + if (index < 0 || index >= NUM_HUFF_TBLS) + ERREXIT1(cinfo->emethods, "Bogus DHT index %d", index); + + if (*htblptr == NULL) + *htblptr = (*cinfo->emethods->alloc_small) (SIZEOF(HUFF_TBL)); + + memcpy((void *) (*htblptr)->bits, (void *) bits, + SIZEOF((*htblptr)->bits)); + memcpy((void *) (*htblptr)->huffval, (void *) huffval, + SIZEOF((*htblptr)->huffval)); + } +} + + +LOCAL void +get_dac (decompress_info_ptr cinfo) +/* Process a DAC marker */ +{ + INT32 length; + int index, val; + + length = get_2bytes(cinfo)-2; + + while (length > 0) { + index = JGETC(cinfo); + val = JGETC(cinfo); + + TRACEMS2(cinfo->emethods, 1, + "Define Arithmetic Table 0x%02x: 0x%02x", index, val); + + if (index < 0 || index >= (2*NUM_ARITH_TBLS)) + ERREXIT1(cinfo->emethods, "Bogus DAC index %d", index); + + if (index >= NUM_ARITH_TBLS) { /* define AC table */ + cinfo->arith_ac_K[index-NUM_ARITH_TBLS] = val; + } else { /* define DC table */ + cinfo->arith_dc_L[index] = val & 0x0F; + cinfo->arith_dc_U[index] = val >> 4; + if (cinfo->arith_dc_L[index] > cinfo->arith_dc_U[index]) + ERREXIT1(cinfo->emethods, "Bogus DAC value 0x%x", val); + } + + length -= 2; + } +} + + +LOCAL void +get_dqt (decompress_info_ptr cinfo) +/* Process a DQT marker */ +{ + INT32 length; + int n, i, prec; + UINT16 tmp; + QUANT_TBL_PTR quant_ptr; + + length = get_2bytes(cinfo) - 2; + + while (length > 0) { + n = JGETC(cinfo); + prec = n >> 4; + n &= 0x0F; + + TRACEMS2(cinfo->emethods, 1, + "Define Quantization Table %d precision %d", n, prec); + + if (n >= NUM_QUANT_TBLS) + ERREXIT1(cinfo->emethods, "Bogus table number %d", n); + + if (cinfo->quant_tbl_ptrs[n] == NULL) + cinfo->quant_tbl_ptrs[n] = (*cinfo->emethods->alloc_small) (SIZEOF(QUANT_TBL)); + quant_ptr = cinfo->quant_tbl_ptrs[n]; + + for (i = 0; i < DCTSIZE2; i++) { + tmp = JGETC(cinfo); + if (prec) + tmp = (tmp<<8) + JGETC(cinfo); + quant_ptr[i] = tmp; + } + + for (i = 0; i < DCTSIZE2; i += 8) { + TRACEMS8(cinfo->emethods, 2, " %4d %4d %4d %4d %4d %4d %4d %4d", + quant_ptr[i ], quant_ptr[i+1], quant_ptr[i+2], quant_ptr[i+3], + quant_ptr[i+4], quant_ptr[i+5], quant_ptr[i+6], quant_ptr[i+7]); + } + + length -= DCTSIZE2+1; + if (prec) length -= DCTSIZE2; + } +} + + +LOCAL void +get_dri (decompress_info_ptr cinfo) +/* Process a DRI marker */ +{ + if (get_2bytes(cinfo) != 4) + ERREXIT(cinfo->emethods, "Bogus length in DRI"); + + cinfo->restart_interval = get_2bytes(cinfo); + + TRACEMS1(cinfo->emethods, 1, + "Define Restart Interval %d", cinfo->restart_interval); +} + + +LOCAL void +get_app0 (decompress_info_ptr cinfo) +/* Process an APP0 marker */ +{ +#define JFIF_LEN 14 + INT32 length; + UINT8 b[JFIF_LEN]; + int buffp; + + length = get_2bytes(cinfo) - 2; + + /* See if a JFIF APP0 marker is present */ + + if (length >= JFIF_LEN) { + for (buffp = 0; buffp < JFIF_LEN; buffp++) + b[buffp] = JGETC(cinfo); + length -= JFIF_LEN; + + if (b[0]=='J' && b[1]=='F' && b[2]=='I' && b[3]=='F' && b[4]==0) { + /* Found JFIF APP0 marker: check version */ + /* Major version must be 1 */ + if (b[5] != 1) + ERREXIT2(cinfo->emethods, "Unsupported JFIF revision number %d.%02d", + b[5], b[6]); + /* Minor version should be 0 or 1, but try to process anyway if newer */ + if (b[6] != 0 && b[6] != 1) + TRACEMS2(cinfo->emethods, 0, "Warning: unknown JFIF revision number %d.%02d", + b[5], b[6]); + /* Save info */ + cinfo->density_unit = b[7]; + cinfo->X_density = (b[8] << 8) + b[9]; + cinfo->Y_density = (b[10] << 8) + b[11]; + /* Assume colorspace is YCbCr, unless UI has overridden me */ + if (cinfo->jpeg_color_space == CS_UNKNOWN) + cinfo->jpeg_color_space = CS_YCbCr; + TRACEMS3(cinfo->emethods, 1, "JFIF APP0 marker, density %dx%d %d", + cinfo->X_density, cinfo->Y_density, cinfo->density_unit); + } else { + TRACEMS(cinfo->emethods, 1, "Unknown APP0 marker (not JFIF)"); + } + } else { + TRACEMS1(cinfo->emethods, 1, + "Short APP0 marker, length %d", (int) length); + } + + while (length-- > 0) /* skip any remaining data */ + (void) JGETC(cinfo); +} + + +LOCAL void +get_sof (decompress_info_ptr cinfo, int code) +/* Process a SOFn marker */ +{ + INT32 length; + short ci; + int c; + jpeg_component_info * compptr; + + length = get_2bytes(cinfo); + + cinfo->data_precision = JGETC(cinfo); + cinfo->image_height = get_2bytes(cinfo); + cinfo->image_width = get_2bytes(cinfo); + cinfo->num_components = JGETC(cinfo); + + TRACEMS4(cinfo->emethods, 1, + "Start Of Frame 0x%02x: width=%d, height=%d, components=%d", + code, cinfo->image_width, cinfo->image_height, + cinfo->num_components); + + /* We don't support files in which the image height is initially specified */ + /* as 0 and is later redefined by DNL. As long as we have to check that, */ + /* might as well have a general sanity check. */ + if (cinfo->image_height <= 0 || cinfo->image_width <= 0 + || cinfo->num_components <= 0) + ERREXIT(cinfo->emethods, "Empty JPEG image (DNL not supported)"); + +#ifdef EIGHT_BIT_SAMPLES + if (cinfo->data_precision != 8) + ERREXIT(cinfo->emethods, "Unsupported JPEG data precision"); +#endif +#ifdef TWELVE_BIT_SAMPLES + if (cinfo->data_precision != 12) /* this needs more thought?? */ + ERREXIT(cinfo->emethods, "Unsupported JPEG data precision"); +#endif +#ifdef SIXTEEN_BIT_SAMPLES + if (cinfo->data_precision != 16) /* this needs more thought?? */ + ERREXIT(cinfo->emethods, "Unsupported JPEG data precision"); +#endif + + if (length != (cinfo->num_components * 3 + 8)) + ERREXIT(cinfo->emethods, "Bogus SOF length"); + + cinfo->comp_info = (*cinfo->emethods->alloc_small) + (cinfo->num_components * SIZEOF(jpeg_component_info)); + + for (ci = 0; ci < cinfo->num_components; ci++) { + compptr = &cinfo->comp_info[ci]; + compptr->component_index = ci; + compptr->component_id = JGETC(cinfo); + c = JGETC(cinfo); + compptr->h_samp_factor = (c >> 4) & 15; + compptr->v_samp_factor = (c ) & 15; + compptr->quant_tbl_no = JGETC(cinfo); + + TRACEMS4(cinfo->emethods, 1, " Component %d: %dhx%dv q=%d", + compptr->component_id, compptr->h_samp_factor, + compptr->v_samp_factor, compptr->quant_tbl_no); + } +} + + +LOCAL void +get_sos (decompress_info_ptr cinfo) +/* Process a SOS marker */ +{ + INT32 length; + int i, ci, n, c, cc; + jpeg_component_info * compptr; + + length = get_2bytes(cinfo); + + n = JGETC(cinfo); /* Number of components */ + cinfo->comps_in_scan = n; + length -= 3; + + if (length != (n * 2 + 3) || n < 1 || n > MAX_COMPS_IN_SCAN) + ERREXIT(cinfo->emethods, "Bogus SOS length"); + + TRACEMS1(cinfo->emethods, 1, "Start Of Scan: %d components", n); + + for (i = 0; i < n; i++) { + cc = JGETC(cinfo); + c = JGETC(cinfo); + length -= 2; + + for (ci = 0; ci < cinfo->num_components; ci++) + if (cc == cinfo->comp_info[ci].component_id) + break; + + if (ci >= cinfo->num_components) + ERREXIT(cinfo->emethods, "Invalid component number in SOS"); + + compptr = &cinfo->comp_info[ci]; + cinfo->cur_comp_info[i] = compptr; + compptr->dc_tbl_no = (c >> 4) & 15; + compptr->ac_tbl_no = (c ) & 15; + + TRACEMS3(cinfo->emethods, 1, " c%d: [dc=%d ac=%d]", cc, + compptr->dc_tbl_no, compptr->ac_tbl_no); + } + + while (length > 0) { + (void) JGETC(cinfo); + length--; + } +} + + +LOCAL void +get_soi (decompress_info_ptr cinfo) +/* Process an SOI marker */ +{ + int i; + + TRACEMS(cinfo->emethods, 1, "Start of Image"); + + /* Reset all parameters that are defined to be reset by SOI */ + + for (i = 0; i < NUM_ARITH_TBLS; i++) { + cinfo->arith_dc_L[i] = 0; + cinfo->arith_dc_U[i] = 1; + cinfo->arith_ac_K[i] = 5; + } + cinfo->restart_interval = 0; + + cinfo->density_unit = 0; /* set default JFIF APP0 values */ + cinfo->X_density = 1; + cinfo->Y_density = 1; + + cinfo->CCIR601_sampling = FALSE; /* Assume non-CCIR sampling */ +} + + +LOCAL int +next_marker (decompress_info_ptr cinfo) +/* Find the next JPEG marker */ +/* Note that the output might not be a valid marker code, */ +/* but it will never be 0 or FF */ +{ + int c, nbytes; + + nbytes = 0; + do { + do { /* skip any non-FF bytes */ + nbytes++; + c = JGETC(cinfo); + } while (c != 0xFF); + do { /* skip any duplicate FFs */ + nbytes++; + c = JGETC(cinfo); + } while (c == 0xFF); + } while (c == 0); /* repeat if it was a stuffed FF/00 */ + + if (nbytes != 2) + TRACEMS2(cinfo->emethods, 1, "Skipped %d bytes before marker 0x%02x", + nbytes-2, c); + + return c; +} + + +LOCAL JPEG_MARKER +process_tables (decompress_info_ptr cinfo) +/* Scan and process JPEG markers that can appear in any order */ +/* Return when an SOI, EOI, SOFn, or SOS is found */ +{ + int c; + + while (TRUE) { + c = next_marker(cinfo); + + switch (c) { + case M_SOF0: + case M_SOF1: + case M_SOF2: + case M_SOF3: + case M_SOF5: + case M_SOF6: + case M_SOF7: + case M_JPG: + case M_SOF9: + case M_SOF10: + case M_SOF11: + case M_SOF13: + case M_SOF14: + case M_SOF15: + case M_SOI: + case M_EOI: + case M_SOS: + return c; + + case M_DHT: + get_dht(cinfo); + break; + + case M_DAC: + get_dac(cinfo); + break; + + case M_DQT: + get_dqt(cinfo); + break; + + case M_DRI: + get_dri(cinfo); + break; + + case M_APP0: + get_app0(cinfo); + break; + + case M_RST0: /* these are all parameterless */ + case M_RST1: + case M_RST2: + case M_RST3: + case M_RST4: + case M_RST5: + case M_RST6: + case M_RST7: + case M_TEM: + TRACEMS1(cinfo->emethods, 1, "Unexpected marker 0x%02x", c); + break; + + default: /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn */ + skip_variable(cinfo, c); + break; + } + } +} + + + +/* + * Initialize and read the file header (everything through the SOF marker). + */ + +METHODDEF void +read_file_header (decompress_info_ptr cinfo) +{ + int c; + + /* Expect an SOI marker first */ + if (next_marker(cinfo) == M_SOI) + get_soi(cinfo); + else + ERREXIT(cinfo->emethods, "File does not start with JPEG SOI marker"); + + /* Process markers until SOF */ + c = process_tables(cinfo); + + switch (c) { + case M_SOF0: + case M_SOF1: + get_sof(cinfo, c); + cinfo->arith_code = FALSE; + break; + + case M_SOF9: + get_sof(cinfo, c); + cinfo->arith_code = TRUE; + break; + + default: + ERREXIT1(cinfo->emethods, "Unsupported SOF marker type 0x%02x", c); + break; + } + + /* Figure out what colorspace we have */ + /* (too bad the JPEG committee didn't provide a real way to specify this) */ + + switch (cinfo->num_components) { + case 1: + cinfo->jpeg_color_space = CS_GRAYSCALE; + break; + + case 3: + /* if we saw a JFIF marker, leave it set to YCbCr; */ + /* also leave it alone if UI has provided a value */ + if (cinfo->jpeg_color_space == CS_UNKNOWN) { + short cid0 = cinfo->comp_info[0].component_id; + short cid1 = cinfo->comp_info[1].component_id; + short cid2 = cinfo->comp_info[2].component_id; + + if (cid0 == 1 && cid1 == 2 && cid2 == 3) + cinfo->jpeg_color_space = CS_YCbCr; /* assume it's JFIF w/out marker */ + else if (cid0 == 1 && cid1 == 4 && cid2 == 5) + cinfo->jpeg_color_space = CS_YIQ; /* prototype's YIQ matrix */ + else { + TRACEMS3(cinfo->emethods, 0, + "Unrecognized component IDs %d %d %d, assuming YCbCr", + cid0, cid1, cid2); + cinfo->jpeg_color_space = CS_YCbCr; + } + } + break; + + case 4: + cinfo->jpeg_color_space = CS_CMYK; + break; + + default: + cinfo->jpeg_color_space = CS_UNKNOWN; + break; + } +} + + +/* + * Read the start of a scan (everything through the SOS marker). + * Return TRUE if find SOS, FALSE if find EOI. + */ + +METHODDEF boolean +read_scan_header (decompress_info_ptr cinfo) +{ + int c; + + /* Process markers until SOS or EOI */ + c = process_tables(cinfo); + + switch (c) { + case M_SOS: + get_sos(cinfo); + return TRUE; + + case M_EOI: + TRACEMS(cinfo->emethods, 1, "End Of Image"); + return FALSE; + + default: + ERREXIT1(cinfo->emethods, "Unexpected marker 0x%02x", c); + break; + } + return FALSE; /* keeps lint happy */ +} + + +/* + * Finish up after a compressed scan (series of read_jpeg_data calls); + * prepare for another read_scan_header call. + */ + +METHODDEF void +read_scan_trailer (decompress_info_ptr cinfo) +{ + /* no work needed */ +} + + +/* + * Finish up at the end of the file. + */ + +METHODDEF void +read_file_trailer (decompress_info_ptr cinfo) +{ + /* no work needed */ +} + + +/* + * The method selection routine for standard JPEG header reading. + * Note that this must be called by the user interface before calling + * jpeg_decompress. When a non-JFIF file is to be decompressed (TIFF, + * perhaps), the user interface must discover the file type and call + * the appropriate method selection routine. + */ + +GLOBAL void +jselrjfif (decompress_info_ptr cinfo) +{ + cinfo->methods->read_file_header = read_file_header; + cinfo->methods->read_scan_header = read_scan_header; + /* For JFIF/raw-JPEG format, the user interface supplies read_jpeg_data. */ +#if 0 + cinfo->methods->read_jpeg_data = read_jpeg_data; +#endif + cinfo->methods->read_scan_trailer = read_scan_trailer; + cinfo->methods->read_file_trailer = read_file_trailer; +} + +#endif /* JFIF_SUPPORTED */ |