/// Frame::ScanForScanHeader // Start parsing a hidden scan, let it be either the residual or the refinement // scan. bool Frame::ScanForScanHeader(class ByteStream *stream) { LONG data; // data = stream->GetWord(); if (data != 0xffda) { JPG_WARN(MALFORMED_STREAM,"Frame::StartParseHiddenScan","Start of Scan SOS marker missing"); // Advance to the next marker if there is something next. // do { stream->LastUnDo(); do { data = stream->Get(); } while(data != 0xff && data != ByteStream::EOF); // if (data == ByteStream::EOF) break; // Try from the main stream stream->LastUnDo(); // // If this is SOS, we recovered. Maybe. data = stream->GetWord(); if (data == ByteStream::EOF) break; // Check for the proper marker. } while(data != 0xffda); } return (data == 0xffda)?(true):(false); }
/// Frame::PostImageHeight // Define the image size if it is not yet known here. This is // called whenever the DNL marker is parsed in. void Frame::PostImageHeight(ULONG height) { assert(height > 0 && m_pImage); if (m_ulHeight == 0) { m_ulHeight = height; m_pImage->PostImageHeight(height); } else if (m_ulHeight == height) { JPG_WARN(MALFORMED_STREAM,"Frame::PostImageHeight", "found a double DNL marker for a frame, frame size is known already"); } else { JPG_THROW(MALFORMED_STREAM,"Frame::PostImageHeight", "found a double DNL marker for a frame, indicating an inconsistent frame height"); } }
// // Compute the Golomb parameter from the context. UBYTE GolombParameter(UWORD context) const { UBYTE k; for(k = 0;(m_lN[context] << k) < m_lA[context] && k < 24;k++) { } if (unlikely(k == 24)) { JPG_WARN(MALFORMED_STREAM,"JPEGLSScan::GolombParameter", "Golomb coding parameter of JPEG LS stream run out of bounds, " "synchronization lost"); return 0; } return k; }
/// DecoderStream::Append // Given a byte stream and a size, // attach as many bytes in the byte stream to the decoder stream. // The bytes are read from the byte stream and feed into here. // Returns false on error. bool DecoderStream::Append(class ByteStream *from,ULONG read_size,ULONG index) { // // First, check whether we provide any bytes at all. If not, bail out. if (read_size) { ULONG size; struct BufferNode *bn; // Now allocate a new buffer node of the given priority and link it in. bn = BufferNode::AddBuffer(m_pEnviron,m_pBufferList,index,read_size); // And read the data into the buffer. size = from->Read(bn->bn_pucBuffer,read_size); if (size != read_size) { if (size < read_size) { // fill the remaining part with zeros. memset(bn->bn_pucBuffer + size,0,read_size - size); } // Support truncated streams, but warn! JPG_WARN(UNEXPECTED_EOF, "DecoderStream::Append", "unexpected EOF on pulling encoded data"); return false; } } return true; }
/// Frame::ParseTrailer // Parse off the EOI marker at the end of the image. Return false // if there are no more scans in the file, true otherwise. bool Frame::ParseTrailer(class ByteStream *io) { do { LONG marker = io->PeekWord(); switch(marker) { case 0xffc0: case 0xffc1: case 0xffc2: case 0xffc3: case 0xffc9: case 0xffca: case 0xffcb: case 0xfff7: // All non-differential frames, may not appear in a hierarchical process. JPG_WARN(MALFORMED_STREAM,"Frame::ParseTrailer", "found a non-differential frame start behind the initial frame"); return true; case 0xffd9: // The EOI still needs to be seen by the image. case 0xffc5: case 0xffc6: case 0xffc7: case 0xffcd: case 0xffce: case 0xffcf: case 0xffdf: // EXP-marker also terminates this frame. // All differential frame starts: The frame ends here. // If we have a residual scan that is not yet parsed off, // create it now and parse it last so it sees the original // data. if (m_pTables->RefinementDataOf() && !m_bCreatedRefinement) { assert(m_pImage); if (dynamic_cast<class BlockBitmapRequester *>(m_pImage)) { LONG data; do { data = m_pTables->RefinementDataOf()->StreamOf()->PeekWord(); if (data == 0xffff) { m_pTables->RefinementDataOf()->StreamOf()->Get(); // Filler byte } } while(data == 0xffff); if (data != ByteStream::EOF && data != 0xffd9) { m_bBuildRefinement = true; // // Come here again when the refinement is parsed off. return true; } else { // Do not come again, refinement is there already. m_bCreatedRefinement = true; } } } if (m_pTables->ResidualDataOf() && !m_bCreatedResidual) { assert(m_pImage); if (dynamic_cast<class BlockBitmapRequester *>(m_pImage)) { LONG data; do { data = m_pTables->ResidualDataOf()->StreamOf()->PeekWord(); if (data == 0xffff) { m_pTables->ResidualDataOf()->StreamOf()->Get(); // Filler byte } } while(data == 0xffff); if (data != ByteStream::EOF && data != 0xffd9) { m_bBuildResidual = true; // // Come here again when the residual is parsed off. return true; } else { // Do not come again, refinement is there already. m_bCreatedResidual = true; } } } return false; case 0xffff: // A filler byte. Remove the filler, try again. io->Get(); break; case 0xffd0: case 0xffd1: case 0xffd2: case 0xffd3: case 0xffd4: case 0xffd5: case 0xffd6: case 0xffd7: // Restart markers. io->GetWord(); JPG_WARN(MALFORMED_STREAM,"Frame::ParseTrailer","found a stray restart marker segment, ignoring"); break; case ByteStream::EOF: JPG_WARN(MALFORMED_STREAM,"Frame::ParseTrailer", "expecting an EOI marker at the end of the stream"); return false; default: if (marker < 0xff00) { JPG_WARN(MALFORMED_STREAM,"Frame::ParseTrailer", "expecting a marker or marker segment - stream is out of sync"); // Advance to the next marker and see how it goes from there... io->Get(); // Remove the invalid thing. do { marker = io->Get(); } while(marker != 0xff && marker != ByteStream::EOF); // if (marker == ByteStream::EOF) { JPG_WARN(UNEXPECTED_EOF,"Frame::ParseTrailer", "run into an EOF while scanning for the next marker"); return false; } io->LastUnDo(); // Continue parsing, check what the next marker might be. } else if (marker < 0xffc0) { JPG_WARN(MALFORMED_STREAM,"Frame::ParseTrailer", "detected an unknown marker - stream is out of sync"); return true; } else { return true; } } } while(true); return true; // code never goes here. }
/// Tables::ParseTables // Parse off tables, including an application marker, // comment, huffman tables or quantization tables. // Returns on the first unknown marker. void Tables::ParseTables(class ByteStream *io) { do { LONG marker = io->PeekWord(); switch(marker) { case 0xffdb: // DQT io->GetWord(); if (m_pQuant == NULL) m_pQuant = new(m_pEnviron) Quantization(m_pEnviron); m_pQuant->ParseMarker(io); break; case 0xffc4: // DHT io->GetWord(); if (m_pHuffman == NULL) m_pHuffman = new(m_pEnviron) HuffmanTable(m_pEnviron); m_pHuffman->ParseMarker(io); break; case 0xffcc: // DAC io->GetWord(); if (m_pConditioner == NULL) m_pConditioner = new(m_pEnviron) class ACTable(m_pEnviron); m_pConditioner->ParseMarker(io); break; case 0xffdd: // DRI io->GetWord(); if (m_pRestart == NULL) m_pRestart = new(m_pEnviron) class RestartIntervalMarker(m_pEnviron); m_pRestart->ParseMarker(io); break; case 0xfffe: // COM { LONG size; io->GetWord(); size = io->GetWord(); // Application marker. if (size == ByteStream::EOF) JPG_THROW(UNEXPECTED_EOF,"Tables::ParseTables","COM marker incomplete, stream truncated"); // if (size <= 0x02) JPG_THROW(MALFORMED_STREAM,"Tables::ParseTables","COM marker size out of range"); // // Just skip the contents. For now. More later on. io->SkipBytes(size - 2); } break; case 0xfff8: // LSE: JPEG LS extensions marker. { io->GetWord(); LONG len = io->GetWord(); if (len > 3) { UBYTE id = io->Get(); if (id == 1) { // Thresholds marker. if (m_pThresholds == NULL) m_pThresholds = new(m_pEnviron) class Thresholds(m_pEnviron); m_pThresholds->ParseMarker(io,len); break; } else if (id == 2 || id == 3) { JPG_THROW(NOT_IMPLEMENTED,"Tables::ParseTables", "JPEG LS mapping tables are not implemented by this code, sorry"); } else if (id == 4) { JPG_THROW(NOT_IMPLEMENTED,"Tables::ParseTables", "JPEG LS size extensions are not implemented by this code, sorry"); } else if (id == 0x0d) { // LS Reversible Color transformation if (m_pLSColorTrafo) JPG_THROW(MALFORMED_STREAM,"Tables::ParseTables", "found duplicate JPEG LS color transformation specification"); m_pLSColorTrafo = new(m_pEnviron) class LSColorTrafo(m_pEnviron); m_pLSColorTrafo->ParseMarker(io,len); break; } else { JPG_WARN(NOT_IMPLEMENTED,"Tables::ParseMarker", "skipping over unknown JPEG LS extensions marker"); } } if (len <= 0x02) JPG_THROW(MALFORMED_STREAM,"Tables::ParseTables","marker size out of range"); // // Just skip the contents. For now. More later on. io->SkipBytes(len - 2); } break; case 0xffe0: // APP0: Maybe the JFIF marker. { io->GetWord(); LONG len = io->GetWord(); if (len >= 2 + 5 + 2 + 1 + 2 + 2 + 1 + 1) { const char *id = "JFIF"; while(*id) { len--; if (io->Get() != *id) break; id++; } if (*id == 0) { len--; if (io->Get() == 0) { if (m_pResolutionInfo == NULL) m_pResolutionInfo = new(m_pEnviron) class JFIFMarker(m_pEnviron); m_pResolutionInfo->ParseMarker(io,len + 5); break; } } } if (len <= 0x02) JPG_THROW(MALFORMED_STREAM,"Tables::ParseTables","marker size out of range"); // // Just skip the contents. For now. More later on. io->SkipBytes(len - 2); } break; case 0xffe1: // APP1: Maybe the EXIF marker. { io->GetWord(); LONG len = io->GetWord(); if (len >= 2 + 4 + 2 + 2 + 2 + 4 + 2) { const char *id = "Exif"; while(*id) { len--; if (io->Get() != *id) break; id++; } if (*id == 0) { len -= 2; if (io->GetWord() == 0) { if (m_pCameraInfo == NULL) m_pCameraInfo = new(m_pEnviron) class EXIFMarker(m_pEnviron); m_pCameraInfo->ParseMarker(io,len + 4 + 2); break; } } } if (len <= 0x02) JPG_THROW(MALFORMED_STREAM,"Tables::ParseTables","marker size out of range"); // // Just skip the contents. For now. More later on. io->SkipBytes(len - 2); } break; case 0xffe9: // APP9: Application markers defined here. { UWORD len; io->GetWord(); len = io->GetWord(); if (len > 2 + 2 + 4) { UWORD id = io->GetWord(); if (id == (('J' << 8) | ('P'))) { LONG type = io->GetWord() << 16; type |= io->GetWord(); if (type == JPG_MAKEID('S','E','R','M')) { if (m_pLosslessMarker == NULL) m_pLosslessMarker = new(m_pEnviron) class LosslessMarker(m_pEnviron); m_pLosslessMarker->ParseMarker(io,len); if (m_bForceFixpoint) { delete m_pLosslessMarker;m_pLosslessMarker = NULL; } break; } else if (type == JPG_MAKEID('R','E','S','I')) { if (m_pResidualData == NULL) m_pResidualData = new(m_pEnviron) class ResidualMarker(m_pEnviron, ResidualMarker::Residual); // // Parse off the residual data. m_pResidualData->ParseMarker(io,len); break; } else if (type == JPG_MAKEID('F','I','N','E')) { if (m_pRefinementData == NULL) m_pRefinementData = new(m_pEnviron) class ResidualMarker(m_pEnviron, ResidualMarker::Refinement); // m_pRefinementData->ParseMarker(io,len); break; } else if (type == JPG_MAKEID('S','P','E','C')) { if (m_pResidualSpecs == NULL) m_pResidualSpecs = new(m_pEnviron) class ResidualSpecsMarker(m_pEnviron); // // Parse off the residual specifications. m_pResidualSpecs->ParseMarker(io,len); break; } else if (type == JPG_MAKEID('T','O','N','E')) { class ToneMappingMarker *tone = new(m_pEnviron) class ToneMappingMarker(m_pEnviron); // // Append to the end of the list. tone->NextOf() = m_pToneMappingMarker; m_pToneMappingMarker = tone; // // Parse the stuff off. tone->ParseMarker(io,len); break; } len -= 2 + 4; // bytes already read. } else { len -= 2; } } if (len <= 0x02) JPG_THROW(MALFORMED_STREAM,"Tables::ParseTables","marker size out of range"); // // Just skip the contents. For now. More later on. io->SkipBytes(len - 2); } break; case 0xffee: // APP14: Maybe the adobe marker. { io->GetWord(); LONG len = io->GetWord(); if (len == 2 + 5 + 2 + 2 + 2 + 1) { const char *id = "Adobe"; while(*id) { len--; if (io->Get() != *id) break; id++; } if (*id == 0) { if (m_pColorInfo == NULL) m_pColorInfo = new(m_pEnviron) class AdobeMarker(m_pEnviron); m_pColorInfo->ParseMarker(io,len + 5); break; } } if (len <= 0x02) JPG_THROW(MALFORMED_STREAM,"Tables::ParseTables","marker size out of range"); // // Just skip the contents. For now. More later on. io->SkipBytes(len - 2); } break; case 0xffc0: case 0xffc1: case 0xffc2: case 0xffc3: case 0xffc5: case 0xffc6: case 0xffc7: case 0xffc8: case 0xffc9: case 0xffca: case 0xffcb: case 0xffcd: case 0xffce: case 0xffcf: // all start of frame markers. case 0xffda: // Start of scan. case 0xffde: // DHP case 0xfff7: // JPEG LS SOS return; case 0xffff: // A filler byte followed by a marker. Skip. io->Get(); break; case 0xffd0: case 0xffd1: case 0xffd2: case 0xffd3: case 0xffd4: case 0xffd5: case 0xffd6: case 0xffd7: // Restart markers. io->GetWord(); JPG_WARN(MALFORMED_STREAM,"Tables::ParseTables","found a stray restart marker segment, ignoring"); break; default: if (marker >= 0xffc0 && (marker < 0xffd0 || marker >= 0xffd8) && marker < 0xfff0) { LONG size; io->GetWord(); size = io->GetWord(); // Application marker. if (size == ByteStream::EOF) JPG_THROW(UNEXPECTED_EOF,"Tables::ParseTables","marker incomplete, stream truncated"); // if (size <= 0x02) JPG_THROW(MALFORMED_STREAM,"Tables::ParseTables","marker size out of range"); // // Just skip the contents. For now. More later on. io->SkipBytes(size - 2); } else { LONG dt; // JPG_WARN(MALFORMED_STREAM,"Tables::ParseTables", "found invalid marker, probably a marker size is out of range"); // Advance to the next marker manually. io->Get(); do { dt = io->Get(); } while(dt != 0xff && dt != ByteStream::EOF); // if (dt == 0xff) { io->LastUnDo(); } else { return; } } } } while(true); }
/// EntropyParser::ParseRestartMarker // Parse the restart marker or resync at the restart marker. void EntropyParser::ParseRestartMarker(class ByteStream *io) { LONG dt = io->PeekWord(); while(dt == 0xffff) { // Found a filler byte. Skip over and try again. io->Get(); dt = io->PeekWord(); } if (dt == 0xffdc && m_bScanForDNL) { ParseDNLMarker(io); } else if (dt == m_usNextRestartMarker) { // Everything worked fine! Continue going after removing the marker. io->GetWord(); Restart(); m_usNextRestartMarker = (m_usNextRestartMarker + 1) & 0xfff7; m_usMCUsToGo = m_usRestartInterval; m_bSegmentIsValid = true; } else { JPG_WARN(MALFORMED_STREAM,"EntropyParser::ParseRestartMarker", "entropy coder is out of sync, trying to advance to the next marker"); // As said... // do { dt = io->Get(); if (dt == ByteStream::EOF) { // Outch, run completely out of data. JPG_THROW(UNEXPECTED_EOF,"EntropyParser::ParseRestartMarker", "run into end of file while trying to resync the entropy parser"); // // Code never goes here... return; } else if (dt == 0xff) { // Could be a marker. io->LastUnDo(); dt = io->PeekWord(); // Depends now on the marker. if (dt >= 0xffd0 && dt < 0xffd8) { // Is a restart marker. If this is the correct one, just leave, // the entropy coder was behind and we are then again up at the // correct index. if (dt == m_usNextRestartMarker) { io->GetWord(); Restart(); m_usNextRestartMarker = (m_usNextRestartMarker + 1) & 0xfff7; m_usMCUsToGo = m_usRestartInterval; m_bSegmentIsValid = true; return; } else if (((dt - m_usNextRestartMarker) & 0x07) >= 4) { // Here dt is *likely* behind, i.e. we need to skip more // data to advance to the correct restart marker. io->GetWord(); // Remove the marker and keep going. } else { // Here dt is likely ahead, that is, the entropy decoder // should better skip the next entropy coded segment // completely and then should re-enter to re-examine whether // the marker fits. Keep the marker in the stream, then, but // do not continue to decode. m_bSegmentIsValid = false; m_usNextRestartMarker = (m_usNextRestartMarker + 1) & 0xfff7; m_usMCUsToGo = m_usRestartInterval; // Do not run into a restart as this may pull bytes. return; } } else if (dt >= 0xffc0 && dt < 0xfff0) { // Is apparently some other marker, i.e. we are at the end of // the segment. Continue skipping until the end is reached and // the parser run out of fun... m_bSegmentIsValid = false; m_usNextRestartMarker = (m_usNextRestartMarker + 1) & 0xfff7; m_usMCUsToGo = m_usRestartInterval; // Do not run into a restart as this may pull bytes. return; } else { // Some garbadge data, or a 0xff00. Just eat it up, and continue // scanning. Note that a single Get is used here to eventually // skip over a "fill byte". io->Get(); } } } while(true); } }