/* * libopenraw - ljpegdecompressor.cpp * * Copyright (C) 2007-2016 Hubert Figuiere * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ /* * Code for JPEG lossless decoding. Large parts are grabbed from the IJG * software, so: * * Copyright (C) 1991, 1992, Thomas G. Lane. * Part of the Independent JPEG Group's software. * See the file Copyright for more details. * * Copyright (c) 1993 Brian C. Smith, The Regents of the University * of California * All rights reserved. * * Copyright (c) 1994 Kongji Huang and Brian C. Smith. * Cornell University * All rights reserved. * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose, without fee, and without written agreement is * hereby granted, provided that the above copyright notice and the following * two paragraphs appear in all copies of this software. * * IN NO EVENT SHALL CORNELL UNIVERSITY BE LIABLE TO ANY PARTY FOR * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF CORNELL * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * CORNELL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND CORNELL UNIVERSITY HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ #include #include #include #include #include #include #include #include #include "rawdata.hpp" #include "exception.hpp" #include "io/stream.hpp" #include "trace.hpp" #include "ljpegdecompressor.hpp" #include "ljpegdecompressor_priv.hpp" namespace OpenRaw { using namespace Debug; namespace Internals { static void SkipVariable(IO::Stream *s); static uint16_t Get2bytes (IO::Stream * s); static int32_t NextMarker(IO::Stream * ); static void GetSoi(DecompressInfo *dcPtr); static void GetApp0(IO::Stream *); LJpegDecompressor::LJpegDecompressor(IO::Stream *stream, RawContainer *container) : Decompressor(stream, container), m_slices(), m_mcuROW1(NULL), m_mcuROW2(NULL), m_buf1(NULL), m_buf2(NULL), m_bitsLeft(0), m_getBuffer(0) { } LJpegDecompressor::~LJpegDecompressor() { if(m_mcuROW1) { free(m_mcuROW1); } if(m_mcuROW2) { free(m_mcuROW2); } if(m_buf1) { free(m_buf1); } if(m_buf2) { free(m_buf2); } } void LJpegDecompressor::setSlices(const std::vector & slices) { uint16_t n = slices[0]; m_slices.resize(n + 1); for(uint16_t i = 0; i < n; i++) { m_slices[i] = slices[1]; } m_slices[n] = slices[2]; } static uint32_t bitMask[] = { 0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff, 0x0fffffff, 0x07ffffff, 0x03ffffff, 0x01ffffff, 0x00ffffff, 0x007fffff, 0x003fffff, 0x001fffff, 0x000fffff, 0x0007ffff, 0x0003ffff, 0x0001ffff, 0x0000ffff, 0x00007fff, 0x00003fff, 0x00001fff, 0x00000fff, 0x000007ff, 0x000003ff, 0x000001ff, 0x000000ff, 0x0000007f, 0x0000003f, 0x0000001f, 0x0000000f, 0x00000007, 0x00000003, 0x00000001}; void FixHuffTbl (HuffmanTable *htbl); /* *-------------------------------------------------------------- * * FixHuffTbl -- * * Compute derived values for a Huffman table one the DHT marker * has been processed. This generates both the encoding and * decoding tables. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------- */ void FixHuffTbl (HuffmanTable *htbl) { int32_t p, i, l, lastp, si; char huffsize[257]; uint16_t huffcode[257]; uint16_t code; int32_t size; int32_t value, ll, ul; /* * Figure C.1: make table of Huffman code length for each symbol * Note that this is in code-length order. */ p = 0; for (l = 1; l <= 16; l++) { for (i = 1; i <= (int)htbl->bits[l]; i++) huffsize[p++] = (char)l; } huffsize[p] = 0; lastp = p; /* * Figure C.2: generate the codes themselves * Note that this is in code-length order. */ code = 0; si = huffsize[0]; p = 0; while (huffsize[p]) { while (((int)huffsize[p]) == si) { huffcode[p++] = code; code++; } code <<= 1; si++; } /* * Figure C.3: generate encoding tables * These are code and size indexed by symbol value * Set any codeless symbols to have code length 0; this allows * EmitBits to detect any attempt to emit such symbols. */ memset(htbl->ehufsi, 0, sizeof(htbl->ehufsi)); for (p = 0; p < lastp; p++) { htbl->ehufco[htbl->huffval[p]] = huffcode[p]; htbl->ehufsi[htbl->huffval[p]] = huffsize[p]; } /* * Figure F.15: generate decoding tables */ p = 0; for (l = 1; l <= 16; l++) { if (htbl->bits[l]) { htbl->valptr[l] = p; htbl->mincode[l] = huffcode[p]; p += htbl->bits[l]; htbl->maxcode[l] = huffcode[p - 1]; } else { htbl->maxcode[l] = -1; } } /* * We put in this value to ensure HuffDecode terminates. */ htbl->maxcode[17] = 0xFFFFFL; /* * Build the numbits, value lookup tables. * These table allow us to gather 8 bits from the bits stream, * and immediately lookup the size and value of the huffman codes. * If size is zero, it means that more than 8 bits are in the huffman * code (this happens about 3-4% of the time). */ bzero (htbl->numbits, sizeof(htbl->numbits)); for (p=0; phuffval[p]; code = huffcode[p]; ll = code << (8-size); if (size < 8) { ul = ll | bitMask[24+size]; } else { ul = ll; } for (i=ll; i<=ul; i++) { htbl->numbits[i] = size; htbl->value[i] = value; } } } } #define RST0 0xD0 /* RST0 marker code */ #if 0 /* * The following variables keep track of the input buffer * for the JPEG data, which is read by ReadJpegData. */ uint8_t inputBuffer[JPEG_BUF_SIZE]; /* Input buffer for JPEG data */ int numInputBytes; /* The total number of bytes in inputBuffer */ int maxInputBytes; /* Size of inputBuffer */ int inputBufferOffset; /* Offset of current byte */ #endif /* * Code for extracting the next N bits from the input stream. * (N never exceeds 15 for JPEG data.) * This needs to go as fast as possible! * * We read source bytes into getBuffer and dole out bits as needed. * If getBuffer already contains enough bits, they are fetched in-line * by the macros get_bits() and get_bit(). When there aren't enough bits, * fillBitBuffer is called; it will attempt to fill getBuffer to the * "high water mark", then extract the desired number of bits. The idea, * of course, is to minimize the function-call overhead cost of entering * fillBitBuffer. * On most machines MIN_GET_BITS should be 25 to allow the full 32-bit width * of getBuffer to be used. (On machines with wider words, an even larger * buffer could be used.) */ #define BITS_PER_LONG (8*sizeof(int32_t)) #define MIN_GET_BITS (BITS_PER_LONG-7) /* max value for long getBuffer */ /* * bmask[n] is mask for n rightmost bits */ static int32_t bmask[] = {0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF}; /* * Lossless JPEG specifies data precision to be from 2 to 16 bits/sample. */ #define MinPrecisionBits 2 #define MaxPrecisionBits 16 /* *-------------------------------------------------------------- * * DecoderStructInit -- * * Initalize the rest of the fields in the decompression * structure. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------- */ void LJpegDecompressor::DecoderStructInit (DecompressInfo *dcPtr) noexcept(false) { int16_t ci,i; JpegComponentInfo *compPtr; int32_t mcuSize; /* * Check sampling factor validity. */ for (ci = 0; ci < dcPtr->numComponents; ci++) { compPtr = &dcPtr->compInfo[ci]; if ((compPtr->hSampFactor != 1) || (compPtr->vSampFactor != 1)) { throw DecodingException("Error: Downsampling is not supported.\n"); } } /* * Prepare array describing MCU composition */ if (dcPtr->compsInScan == 1) { dcPtr->MCUmembership[0] = 0; } else { if (dcPtr->compsInScan > 4) { throw DecodingException("Too many components for interleaved scan"); } for (ci = 0; ci < dcPtr->compsInScan; ci++) { dcPtr->MCUmembership[ci] = ci; } } /* * Initialize mucROW1 and mcuROW2 which buffer two rows of * pixels for predictor calculation. */ if ((m_mcuROW1 = (MCU *)malloc(dcPtr->imageWidth*sizeof(MCU)))==NULL) { throw DecodingException("Not enough memory for mcuROW1\n"); } if ((m_mcuROW2 = (MCU *)malloc(dcPtr->imageWidth*sizeof(MCU)))==NULL) { throw DecodingException("Not enough memory for mcuROW2\n"); } mcuSize=dcPtr->compsInScan * sizeof(ComponentType); if ((m_buf1 = (char *)malloc(dcPtr->imageWidth*mcuSize))==NULL) { throw DecodingException("Not enough memory for buf1\n"); } if ((m_buf2 = (char *)malloc(dcPtr->imageWidth*mcuSize))==NULL) { throw DecodingException("Not enough memory for buf2\n"); } for (i=0;iimageWidth;i++) { m_mcuROW1[i]=(MCU)(m_buf1+i*mcuSize); m_mcuROW2[i]=(MCU)(m_buf2+i*mcuSize); } } /* *-------------------------------------------------------------- * * fillBitBuffer -- * * Load up the bit buffer with at least nbits * Process any stuffed bytes at this time. * * Results: * None * * Side effects: * The bitwise global variables are updated. * *-------------------------------------------------------------- */ void LJpegDecompressor::fillBitBuffer (IO::Stream * s,uint16_t nbits) { uint8_t c, c2; while (m_bitsLeft < MIN_GET_BITS) { c = s->readByte(); /* * If it's 0xFF, check and discard stuffed zero byte */ if (c == 0xFF) { c2 = s->readByte(); if (c2 != 0) { /* * Oops, it's actually a marker indicating end of * compressed data. Better put it back for use later. */ s->seek(-2, SEEK_CUR); /* * There should be enough bits still left in the data * segment; if so, just break out of the while loop. */ if (m_bitsLeft >= nbits) break; /* * Uh-oh. Corrupted data: stuff zeroes into the data * stream, since this sometimes occurs when we are on the * last show_bits(8) during decoding of the Huffman * segment. */ c = 0; } } /* * OK, load c into getBuffer */ m_getBuffer = (m_getBuffer << 8) | c; m_bitsLeft += 8; } } inline int32_t LJpegDecompressor::QuickPredict(int32_t col, int16_t curComp, MCU *curRowBuf, MCU *prevRowBuf, int32_t psv) { int32_t left,upper,diag,leftcol; int32_t predictor; leftcol=col-1; upper=prevRowBuf[col][curComp]; left=curRowBuf[leftcol][curComp]; diag=prevRowBuf[leftcol][curComp]; /* * All predictor are calculated according to psv. */ switch (psv) { case 0: predictor = 0; break; case 1: predictor = left; break; case 2: predictor = upper; break; case 3: predictor = diag; break; case 4: predictor = left+upper-diag; break; case 5: predictor = left+((upper-diag)>>1); break; case 6: predictor = upper+((left-diag)>>1); break; case 7: predictor = (left+upper)>>1; break; default: LOGWARN("Warning: Undefined PSV\n"); predictor = 0; } return predictor; } inline int32_t LJpegDecompressor::show_bits8(IO::Stream * s) { if (m_bitsLeft < 8) { fillBitBuffer(s, 8); } return (m_getBuffer >> (m_bitsLeft-8)) & 0xff; } inline void LJpegDecompressor::flush_bits(uint16_t nbits) { m_bitsLeft -= (nbits); } inline int32_t LJpegDecompressor::get_bits(uint16_t nbits) { if (m_bitsLeft < nbits) fillBitBuffer(m_stream, nbits); return ((m_getBuffer >> (m_bitsLeft -= (nbits)))) & bmask[nbits]; } inline int32_t LJpegDecompressor::get_bit() { if (!m_bitsLeft) fillBitBuffer(m_stream, 1); return (m_getBuffer >> (--m_bitsLeft)) & 1; } inline int32_t LJpegDecompressor::readBits(IO::Stream * s, uint16_t nbits) { if (m_bitsLeft < nbits) { fillBitBuffer(s, nbits); } return ((m_getBuffer >> (m_bitsLeft -= (nbits)))) & bmask[nbits]; } /* *-------------------------------------------------------------- * * PmPutRow -- * * Output one row of pixels stored in RowBuf. * * Results: * None * * Side effects: * One row of pixels are write to file pointed by outFile. * *-------------------------------------------------------------- */ inline void LJpegDecompressor::PmPutRow(MCU* RowBuf, int32_t numComp, int32_t numCol, int32_t Pt) { // TODO this might be wrong in 8 bits... // original code was using putc which *i think* was a problem for // 16bpp int32_t comp; int32_t col; uint16_t v; for (col = 0; col < numCol; col++) { for (comp = 0; comp < numComp; comp++) { v = RowBuf[col][comp]<append(v); } } // m_output->nextRow(); } /* *-------------------------------------------------------------- * * HuffDecode -- * * Taken from Figure F.16: extract next coded symbol from * input stream. This should becode a macro. * * Results: * Next coded symbol * * Side effects: * Bitstream is parsed. * *-------------------------------------------------------------- */ inline int32_t LJpegDecompressor::HuffDecode(HuffmanTable *htbl) { int32_t rv; int32_t l, temp; int32_t code; /* * If the huffman code is less than 8 bits, we can use the fast * table lookup to get its value. It's more than 8 bits about * 3-4% of the time. */ code = show_bits8(m_stream); if (htbl->numbits[code]) { flush_bits(htbl->numbits[code]); rv=htbl->value[code]; } else { flush_bits(8); l = 8; while (code > htbl->maxcode[l]) { temp = get_bit(); code = (code << 1) | temp; l++; } /* * With garbage input we may reach the sentinel value l = 17. */ if (l > 16) { //LOGWARN("Corrupt JPEG data: bad Huffman code %d\n", l); rv = 0; /* fake a zero as the safest result */ } else { rv = htbl->huffval[htbl->valptr[l] + ((int)(code - htbl->mincode[l]))]; } } return rv; } /* *-------------------------------------------------------------- * * HuffExtend -- * * Code and table for Figure F.12: extend sign bit * * Results: * The extended value. * * Side effects: * None. * *-------------------------------------------------------------- */ static const int32_t extendTest[16] = /* entry n is 2**(n-1) */ {0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000}; // we can't bitshift -1. So we use 0xffffffff as a value. // gcc complain about it otherwise. #define EXTEND(n) (int32_t)(0xffffffff << n) + 1 static const int32_t extendOffset[16] = /* entry n is (-1 << n) + 1 */ { 0, EXTEND(1), EXTEND(2), EXTEND(3), EXTEND(4), EXTEND(5), EXTEND(6), EXTEND(7), EXTEND(8), EXTEND(9), EXTEND(10), EXTEND(11), EXTEND(12), EXTEND(13), EXTEND(14), EXTEND(15) }; inline void HuffExtend(int32_t & x, int32_t s) noexcept { if ((x) < extendTest[s]) { (x) += extendOffset[s]; } } /* *-------------------------------------------------------------- * * HuffDecoderInit -- * * Initialize for a Huffman-compressed scan. * This is invoked after reading the SOS marker. * * Results: * None * * Side effects: * None. * *-------------------------------------------------------------- */ void LJpegDecompressor::HuffDecoderInit (DecompressInfo *dcPtr) noexcept(false) { int16_t ci; JpegComponentInfo *compptr; /* * Initialize static variables */ m_bitsLeft = 0; for (ci = 0; ci < dcPtr->compsInScan; ci++) { compptr = dcPtr->curCompInfo[ci]; /* * Make sure requested tables are present */ if (dcPtr->dcHuffTblPtrs[compptr->dcTblNo] == NULL) { throw DecodingException("Error: Use of undefined Huffman table\n"); } /* * Compute derived values for Huffman tables. * We may do this more than once for same table, but it's not a * big deal */ FixHuffTbl (dcPtr->dcHuffTblPtrs[compptr->dcTblNo]); } /* * Initialize restart stuff */ dcPtr->restartInRows = (dcPtr->restartInterval)/(dcPtr->imageWidth); dcPtr->restartRowsToGo = dcPtr->restartInRows; dcPtr->nextRestartNum = 0; } /* *-------------------------------------------------------------- * * ProcessRestart -- * * Check for a restart marker & resynchronize decoder. * * Results: * None. * * Side effects: * BitStream is parsed, bit buffer is reset, etc. * *-------------------------------------------------------------- */ void LJpegDecompressor::ProcessRestart (DecompressInfo *dcPtr) noexcept(false) { int32_t c, nbytes; /* * Throw away any unused bits remaining in bit buffer */ nbytes = m_bitsLeft / 8; m_bitsLeft = 0; /* * Scan for next JPEG marker */ do { do { /* skip any non-FF bytes */ nbytes++; c = m_stream->readByte(); } while (c != 0xFF); do { /* skip any duplicate FFs */ /* * we don't increment nbytes here since extra FFs are legal */ c = m_stream->readByte(); } while (c == 0xFF); } while (c == 0); /* repeat if it was a stuffed FF/00 */ if (c != (RST0 + dcPtr->nextRestartNum)) { /* * Uh-oh, the restart markers have been messed up too. * Just bail out. */ throw DecodingException("Error: Corrupt JPEG data. " "Aborting decoding...\n"); } /* * Update restart state */ dcPtr->restartRowsToGo = dcPtr->restartInRows; dcPtr->nextRestartNum = (dcPtr->nextRestartNum + 1) & 7; } /* *-------------------------------------------------------------- * * DecodeFirstRow -- * * Decode the first raster line of samples at the start of * the scan and at the beginning of each restart interval. * This includes modifying the component value so the real * value, not the difference is returned. * * Results: * None. * * Side effects: * Bitstream is parsed. * *-------------------------------------------------------------- */ void LJpegDecompressor::DecodeFirstRow(DecompressInfo *dcPtr, MCU *curRowBuf) { uint16_t curComp,ci; int32_t s,col,compsInScan,numCOL; JpegComponentInfo *compptr; int32_t Pr,Pt,d; HuffmanTable *dctbl; Pr=dcPtr->dataPrecision; Pt=dcPtr->Pt; compsInScan=dcPtr->compsInScan; numCOL=dcPtr->imageWidth; /* * the start of the scan or at the beginning of restart interval. */ for (curComp = 0; curComp < compsInScan; curComp++) { ci = dcPtr->MCUmembership[curComp]; compptr = dcPtr->curCompInfo[ci]; dctbl = dcPtr->dcHuffTblPtrs[compptr->dcTblNo]; /* * Section F.2.2.1: decode the difference */ s = HuffDecode (dctbl); if (s) { d = get_bits(s); HuffExtend(d,s); } else { d = 0; } /* * Add the predictor to the difference. */ curRowBuf[0][curComp]=d+(1<<(Pr-Pt-1)); } /* * the rest of the first row */ for (col=1; colMCUmembership[curComp]; compptr = dcPtr->curCompInfo[ci]; dctbl = dcPtr->dcHuffTblPtrs[compptr->dcTblNo]; /* * Section F.2.2.1: decode the difference */ s = HuffDecode (dctbl); if (s) { d = get_bits(s); HuffExtend(d,s); } else { d = 0; } /* * Add the predictor to the difference. */ curRowBuf[col][curComp]=d+curRowBuf[col-1][curComp]; } } if (dcPtr->restartInRows) { (dcPtr->restartRowsToGo)--; } } /* *-------------------------------------------------------------- * * DecodeImage -- * * Decode the input stream. This includes modifying * the component value so the real value, not the * difference is returned. * * Results: * None. * * Side effects: * Bitstream is parsed. * *-------------------------------------------------------------- */ void LJpegDecompressor::DecodeImage(DecompressInfo *dcPtr) { int32_t s,d,col,row; int16_t curComp, ci; HuffmanTable *dctbl; JpegComponentInfo *compptr; int32_t predictor; int32_t numCOL,numROW,compsInScan; MCU *prevRowBuf,*curRowBuf; int32_t imagewidth,Pt,psv; numCOL=imagewidth=dcPtr->imageWidth; numROW=dcPtr->imageHeight; compsInScan=dcPtr->compsInScan; Pt=dcPtr->Pt; psv=dcPtr->Ss; prevRowBuf=m_mcuROW2; curRowBuf=m_mcuROW1; /* * Decode the first row of image. Output the row and * turn this row into a previous row for later predictor * calculation. */ DecodeFirstRow(dcPtr,curRowBuf); PmPutRow(curRowBuf,compsInScan,numCOL,Pt); std::swap(prevRowBuf,curRowBuf); for (row=1; rowrestartInRows) { if (dcPtr->restartRowsToGo == 0) { ProcessRestart (dcPtr); /* * Reset predictors at restart. */ DecodeFirstRow(dcPtr,curRowBuf); PmPutRow(curRowBuf,compsInScan,numCOL,Pt); std::swap(prevRowBuf,curRowBuf); continue; } dcPtr->restartRowsToGo--; } /* * The upper neighbors are predictors for the first column. */ for (curComp = 0; curComp < compsInScan; curComp++) { ci = dcPtr->MCUmembership[curComp]; compptr = dcPtr->curCompInfo[ci]; dctbl = dcPtr->dcHuffTblPtrs[compptr->dcTblNo]; /* * Section F.2.2.1: decode the difference */ s = HuffDecode (dctbl); if (s) { d = get_bits(s); HuffExtend(d,s); } else { d = 0; } curRowBuf[0][curComp]=d+prevRowBuf[0][curComp]; } /* * For the rest of the column on this row, predictor * calculations are base on PSV. */ for (col=1; colMCUmembership[curComp]; compptr = dcPtr->curCompInfo[ci]; dctbl = dcPtr->dcHuffTblPtrs[compptr->dcTblNo]; /* * Section F.2.2.1: decode the difference */ s = HuffDecode (dctbl); if (s) { d = get_bits(s); HuffExtend(d,s); } else { d = 0; } predictor = QuickPredict(col,curComp,curRowBuf,prevRowBuf, psv); curRowBuf[col][curComp]=d+predictor; } } PmPutRow(curRowBuf,compsInScan,numCOL,Pt); std::swap(prevRowBuf,curRowBuf); } } /* *-------------------------------------------------------------- * * Get2bytes -- * * Get a 2-byte unsigned integer (e.g., a marker parameter length * field) * * Results: * Next two byte of input as an integer. * * Side effects: * Bitstream is parsed. * *-------------------------------------------------------------- */ static inline uint16_t Get2bytes (IO::Stream * s) { uint16_t a; a = s->readByte(); return (a << 8) | s->readByte(); } /* *-------------------------------------------------------------- * * SkipVariable -- * * Skip over an unknown or uninteresting variable-length marker * * Results: * None. * * Side effects: * Bitstream is parsed over marker. * * *-------------------------------------------------------------- */ static inline void SkipVariable(IO::Stream * s) { int32_t length; length = Get2bytes(s) - 2; s->seek(length, SEEK_CUR); } /* *-------------------------------------------------------------- * * GetDht -- * * Process a DHT marker * * Results: * None * * Side effects: * A huffman table is read. * Exits on error. * *-------------------------------------------------------------- */ void LJpegDecompressor::GetDht (DecompressInfo *dcPtr) noexcept(false) { int32_t length; int32_t i, index, count; length = Get2bytes(m_stream) - 2; while (length) { index = m_stream->readByte(); if (index < 0 || index >= 4) { throw DecodingException(str(boost::format("Bogus DHT index %1%") % index)); } HuffmanTable *& htblptr = dcPtr->dcHuffTblPtrs[index]; if (htblptr == NULL) { htblptr = (HuffmanTable *) malloc(sizeof (HuffmanTable)); if (htblptr==NULL) { throw DecodingException("Can't malloc HuffmanTable"); } } htblptr->bits[0] = 0; count = 0; for (i = 1; i <= 16; i++) { htblptr->bits[i] = m_stream->readByte(); count += htblptr->bits[i]; } if (count > 256) { throw DecodingException("Bogus DHT counts"); } for (i = 0; i < count; i++) htblptr->huffval[i] = m_stream->readByte(); length -= 1 + 16 + count; } } /* *-------------------------------------------------------------- * * GetDri -- * * Process a DRI marker * * Results: * None * * Side effects: * Exits on error. * Bitstream is parsed. * *-------------------------------------------------------------- */ void LJpegDecompressor::GetDri(DecompressInfo *dcPtr) noexcept(false) { if (Get2bytes(m_stream) != 4) { throw DecodingException("Bogus length in DRI"); } dcPtr->restartInterval = Get2bytes(m_stream); } /* *-------------------------------------------------------------- * * GetApp0 -- * * Process an APP0 marker. * * Results: * None * * Side effects: * Bitstream is parsed * *-------------------------------------------------------------- */ static void GetApp0(IO::Stream *s) { int32_t length; length = Get2bytes(s) - 2; s->seek(length, SEEK_CUR); } /* *-------------------------------------------------------------- * * GetSof -- * * Process a SOFn marker * * Results: * None. * * Side effects: * Bitstream is parsed * Exits on error * dcPtr structure is filled in * *-------------------------------------------------------------- */ void LJpegDecompressor::GetSof(DecompressInfo *dcPtr) noexcept(false) { int32_t length; int16_t ci; int32_t c; JpegComponentInfo *compptr; length = Get2bytes(m_stream); dcPtr->dataPrecision = m_stream->readByte(); dcPtr->imageHeight = Get2bytes(m_stream); dcPtr->imageWidth = Get2bytes(m_stream); dcPtr->numComponents = m_stream->readByte(); /* * 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 ((dcPtr->imageHeight <= 0 ) || (dcPtr->imageWidth <= 0) || (dcPtr->numComponents <= 0)) { throw DecodingException("Empty JPEG image (DNL not supported)"); } if ((dcPtr->dataPrecisiondataPrecision>MaxPrecisionBits)) { throw DecodingException("Unsupported JPEG data precision"); } if (length != (dcPtr->numComponents * 3 + 8)) { throw DecodingException("Bogus SOF length"); } dcPtr->compInfo = (JpegComponentInfo *) malloc (dcPtr->numComponents * sizeof (JpegComponentInfo)); for (ci = 0; ci < dcPtr->numComponents; ci++) { compptr = &dcPtr->compInfo[ci]; compptr->componentIndex = ci; compptr->componentId = m_stream->readByte(); c = m_stream->readByte(); compptr->hSampFactor = (int16_t)((c >> 4) & 15); compptr->vSampFactor = (int16_t)((c) & 15); (void) m_stream->readByte(); /* skip Tq */ } } /* *-------------------------------------------------------------- * * GetSos -- * * Process a SOS marker * * Results: * None. * * Side effects: * Bitstream is parsed. * Exits on error. * *-------------------------------------------------------------- */ void LJpegDecompressor::GetSos (DecompressInfo *dcPtr) noexcept(false) { int32_t length; int32_t i; uint16_t n, ci, c, cc; JpegComponentInfo *compptr; length = Get2bytes (m_stream); /* * Get the number of image components. */ n = m_stream->readByte(); dcPtr->compsInScan = n; length -= 3; if (length != (n * 2 + 3) || n < 1 || n > 4) { throw DecodingException("Bogus SOS length"); } for (i = 0; i < n; i++) { cc = m_stream->readByte(); c = m_stream->readByte(); length -= 2; for (ci = 0; ci < dcPtr->numComponents; ci++) if (cc == dcPtr->compInfo[ci].componentId) { break; } if (ci >= dcPtr->numComponents) { throw DecodingException("Invalid component number in SOS"); } compptr = &dcPtr->compInfo[ci]; dcPtr->curCompInfo[i] = compptr; compptr->dcTblNo = (c >> 4) & 15; } /* * Get the PSV, skip Se, and get the point transform parameter. */ dcPtr->Ss = m_stream->readByte(); (void)m_stream->readByte(); c = m_stream->readByte(); dcPtr->Pt = c & 0x0F; } /* *-------------------------------------------------------------- * * GetSoi -- * * Process an SOI marker * * Results: * None. * * Side effects: * Bitstream is parsed. * Exits on error. * *-------------------------------------------------------------- */ static inline void GetSoi (DecompressInfo *dcPtr) { /* * Reset all parameters that are defined to be reset by SOI */ dcPtr->restartInterval = 0; } /* *-------------------------------------------------------------- * * NextMarker -- * * Find the next JPEG marker Note that the output might not * be a valid marker code but it will never be 0 or FF * * Results: * The marker found. * * Side effects: * Bitstream is parsed. * *-------------------------------------------------------------- */ static int32_t NextMarker(IO::Stream *s) { int32_t c; do { /* * skip any non-FF bytes */ do { c = s->readByte(); } while (c != 0xFF); /* * skip any duplicate FFs without incrementing nbytes, since * extra FFs are legal */ do { c = s->readByte(); } while (c == 0xFF); } while (c == 0); /* repeat if it was a stuffed FF/00 */ return c; } /* *-------------------------------------------------------------- * * ProcessTables -- * * Scan and process JPEG markers that can appear in any order * Return when an SOI, EOI, SOFn, or SOS is found * * Results: * The marker found. * * Side effects: * Bitstream is parsed. * *-------------------------------------------------------------- */ LJpegDecompressor::JpegMarker LJpegDecompressor::ProcessTables (DecompressInfo *dcPtr) { int c; while (1) { c = NextMarker (m_stream); 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 ((JpegMarker)c); case M_DHT: GetDht (dcPtr); break; case M_DQT: LOGWARN("Not a lossless JPEG file.\n"); break; case M_DRI: GetDri (dcPtr); break; case M_APP0: GetApp0(m_stream); 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: LOGWARN("Warning: unexpected marker 0x%x", c); break; default: /* must be DNL, DHP, EXP, APPn, JPGn, COM, * or RESn */ SkipVariable (m_stream); break; } } } /* *-------------------------------------------------------------- * * ReadFileHeader -- * * Initialize and read the file header (everything through * the SOF marker). * * Results: * None * * Side effects: * Exit on error. * *-------------------------------------------------------------- */ void LJpegDecompressor::ReadFileHeader (DecompressInfo *dcPtr) noexcept(false) { int c, c2; /* * Demand an SOI marker at the start of the file --- otherwise it's * probably not a JPEG file at all. */ c = m_stream->readByte(); c2 = m_stream->readByte(); if ((c != 0xFF) || (c2 != M_SOI)) { throw DecodingException(str(boost::format("Not a JPEG file. " "marker is %1% %2%\n") % c % c2)); } GetSoi (dcPtr); /* OK, process SOI */ /* * Process markers until SOF */ c = ProcessTables (dcPtr); switch (c) { case M_SOF0: case M_SOF1: case M_SOF3: GetSof(dcPtr); break; default: LOGWARN("Unsupported SOF marker type 0x%x\n", c); break; } } /* *-------------------------------------------------------------- * * ReadScanHeader -- * * Read the start of a scan (everything through the SOS marker). * * Results: * 1 if find SOS, 0 if find EOI * * Side effects: * Bitstream is parsed, may exit on errors. * *-------------------------------------------------------------- */ int32_t LJpegDecompressor::ReadScanHeader (DecompressInfo *dcPtr) { int c; /* * Process markers until SOS or EOI */ c = ProcessTables (dcPtr); switch (c) { case M_SOS: GetSos (dcPtr); return 1; case M_EOI: return 0; default: LOGWARN("Unexpected marker 0x%x\n", c); break; } return 0; } RawDataPtr LJpegDecompressor::decompress() { DecompressInfo dcInfo; try { ReadFileHeader(&dcInfo); ReadScanHeader (&dcInfo); m_output = RawDataPtr(new RawData); m_output->setDataType(OR_DATA_TYPE_RAW); uint32_t bpc = dcInfo.dataPrecision; m_output->setBpc(bpc); m_output->setWhiteLevel((1 << bpc) - 1); /*uint16_t *dataPtr = (uint16_t*)*/ m_output->allocData(dcInfo.imageWidth * sizeof(uint16_t) * dcInfo.imageHeight * dcInfo.numComponents); LOGDBG1("dc width = %d dc height = %d\n", dcInfo.imageWidth, dcInfo.imageHeight); /* consistently the real width is the JPEG width * numComponent * at least with all the Canon. * @todo check that this is valid with DNG too. */ uint32_t width = dcInfo.imageWidth * dcInfo.numComponents; m_output->setDimensions(width, dcInfo.imageHeight); m_output->setSlices(m_slices); DecoderStructInit(&dcInfo); HuffDecoderInit(&dcInfo); DecodeImage(&dcInfo); // TODO handle the error properly } catch(...) { LOGERR("Decompression error\n"); } return std::move(m_output); } } } /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)) indent-tabs-mode:nil fill-column:80 End: */