| // Copyright 2014 PDFium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | |
| #include "core/fxcodec/jbig2/JBig2_Context.h" | |
| #include <string.h> | |
| #include <algorithm> | |
| #include <limits> | |
| #include <list> | |
| #include <utility> | |
| #include <vector> | |
| #include "core/fxcodec/jbig2/JBig2_ArithDecoder.h" | |
| #include "core/fxcodec/jbig2/JBig2_BitStream.h" | |
| #include "core/fxcodec/jbig2/JBig2_GrdProc.h" | |
| #include "core/fxcodec/jbig2/JBig2_GrrdProc.h" | |
| #include "core/fxcodec/jbig2/JBig2_HtrdProc.h" | |
| #include "core/fxcodec/jbig2/JBig2_PddProc.h" | |
| #include "core/fxcodec/jbig2/JBig2_SddProc.h" | |
| #include "core/fxcodec/jbig2/JBig2_TrdProc.h" | |
| #include "core/fxcrt/fx_memory_wrappers.h" | |
| #include "core/fxcrt/fx_safe_types.h" | |
| #include "core/fxcrt/pauseindicator_iface.h" | |
| #include "third_party/base/ptr_util.h" | |
| namespace { | |
| size_t GetHuffContextSize(uint8_t val) { | |
| return val == 0 ? 65536 : val == 1 ? 8192 : 1024; | |
| } | |
| size_t GetRefAggContextSize(bool val) { | |
| return val ? 1024 : 8192; | |
| } | |
| } // namespace | |
| // Implement a very small least recently used (LRU) cache. It is very | |
| // common for a JBIG2 dictionary to span multiple pages in a PDF file, | |
| // and we do not want to decode the same dictionary over and over | |
| // again. We key off of the memory location of the dictionary. The | |
| // list keeps track of the freshness of entries, with freshest ones | |
| // at the front. Even a tiny cache size like 2 makes a dramatic | |
| // difference for typical JBIG2 documents. | |
| static const size_t kSymbolDictCacheMaxSize = 2; | |
| static_assert(kSymbolDictCacheMaxSize > 0, | |
| "Symbol Dictionary Cache must have non-zero size"); | |
| // static | |
| std::unique_ptr<CJBig2_Context> CJBig2_Context::Create( | |
| pdfium::span<const uint8_t> pGlobalSpan, | |
| uint32_t dwGlobalObjNum, | |
| pdfium::span<const uint8_t> pSrcSpan, | |
| uint32_t dwSrcObjNum, | |
| std::list<CJBig2_CachePair>* pSymbolDictCache) { | |
| auto result = pdfium::WrapUnique( | |
| new CJBig2_Context(pSrcSpan, dwSrcObjNum, pSymbolDictCache, false)); | |
| if (!pGlobalSpan.empty()) { | |
| result->m_pGlobalContext = pdfium::WrapUnique(new CJBig2_Context( | |
| pGlobalSpan, dwGlobalObjNum, pSymbolDictCache, true)); | |
| } | |
| return result; | |
| } | |
| CJBig2_Context::CJBig2_Context(pdfium::span<const uint8_t> pSrcSpan, | |
| uint32_t dwObjNum, | |
| std::list<CJBig2_CachePair>* pSymbolDictCache, | |
| bool bIsGlobal) | |
| : m_pStream(std::make_unique<CJBig2_BitStream>(pSrcSpan, dwObjNum)), | |
| m_HuffmanTables(CJBig2_HuffmanTable::kNumHuffmanTables), | |
| m_bIsGlobal(bIsGlobal), | |
| m_pSymbolDictCache(pSymbolDictCache) {} | |
| CJBig2_Context::~CJBig2_Context() = default; | |
| JBig2_Result CJBig2_Context::DecodeSequential(PauseIndicatorIface* pPause) { | |
| if (m_pStream->getByteLeft() <= 0) | |
| return JBig2_Result::kEndReached; | |
| while (m_pStream->getByteLeft() >= JBIG2_MIN_SEGMENT_SIZE) { | |
| JBig2_Result nRet; | |
| if (!m_pSegment) { | |
| m_pSegment = std::make_unique<CJBig2_Segment>(); | |
| nRet = ParseSegmentHeader(m_pSegment.get()); | |
| if (nRet != JBig2_Result::kSuccess) { | |
| m_pSegment.reset(); | |
| return nRet; | |
| } | |
| m_dwOffset = m_pStream->getOffset(); | |
| } | |
| nRet = ParseSegmentData(m_pSegment.get(), pPause); | |
| if (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE) { | |
| m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; | |
| m_PauseStep = 2; | |
| return JBig2_Result::kSuccess; | |
| } | |
| if (nRet == JBig2_Result::kEndReached) { | |
| m_pSegment.reset(); | |
| return JBig2_Result::kSuccess; | |
| } | |
| if (nRet != JBig2_Result::kSuccess) { | |
| m_pSegment.reset(); | |
| return nRet; | |
| } | |
| if (m_pSegment->m_dwData_length != 0xffffffff) { | |
| m_dwOffset += m_pSegment->m_dwData_length; | |
| if (!m_dwOffset.IsValid()) | |
| return JBig2_Result::kFailure; | |
| m_pStream->setOffset(m_dwOffset.ValueOrDie()); | |
| } else { | |
| m_pStream->offset(4); | |
| } | |
| m_SegmentList.push_back(std::move(m_pSegment)); | |
| if (m_pStream->getByteLeft() > 0 && m_pPage && pPause && | |
| pPause->NeedToPauseNow()) { | |
| m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; | |
| m_PauseStep = 2; | |
| return JBig2_Result::kSuccess; | |
| } | |
| } | |
| return JBig2_Result::kSuccess; | |
| } | |
| bool CJBig2_Context::GetFirstPage(uint8_t* pBuf, | |
| int32_t width, | |
| int32_t height, | |
| int32_t stride, | |
| PauseIndicatorIface* pPause) { | |
| if (m_pGlobalContext) { | |
| JBig2_Result nRet = m_pGlobalContext->DecodeSequential(pPause); | |
| if (nRet != JBig2_Result::kSuccess) { | |
| m_ProcessingStatus = FXCODEC_STATUS_ERROR; | |
| return nRet == JBig2_Result::kSuccess; | |
| } | |
| } | |
| m_PauseStep = 0; | |
| m_pPage = std::make_unique<CJBig2_Image>(width, height, stride, pBuf); | |
| m_bBufSpecified = true; | |
| if (pPause && pPause->NeedToPauseNow()) { | |
| m_PauseStep = 1; | |
| m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; | |
| return true; | |
| } | |
| return Continue(pPause); | |
| } | |
| bool CJBig2_Context::Continue(PauseIndicatorIface* pPause) { | |
| m_ProcessingStatus = FXCODEC_STATUS_DECODE_READY; | |
| JBig2_Result nRet = JBig2_Result::kSuccess; | |
| if (m_PauseStep == 5) { | |
| m_ProcessingStatus = FXCODEC_STATUS_DECODE_FINISH; | |
| return true; | |
| } | |
| if (m_PauseStep <= 2) | |
| nRet = DecodeSequential(pPause); | |
| if (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE) | |
| return nRet == JBig2_Result::kSuccess; | |
| m_PauseStep = 5; | |
| if (!m_bBufSpecified && nRet == JBig2_Result::kSuccess) { | |
| m_ProcessingStatus = FXCODEC_STATUS_DECODE_FINISH; | |
| return true; | |
| } | |
| m_ProcessingStatus = nRet == JBig2_Result::kSuccess | |
| ? FXCODEC_STATUS_DECODE_FINISH | |
| : FXCODEC_STATUS_ERROR; | |
| return nRet == JBig2_Result::kSuccess; | |
| } | |
| CJBig2_Segment* CJBig2_Context::FindSegmentByNumber(uint32_t dwNumber) { | |
| if (m_pGlobalContext) { | |
| CJBig2_Segment* pSeg = m_pGlobalContext->FindSegmentByNumber(dwNumber); | |
| if (pSeg) | |
| return pSeg; | |
| } | |
| for (const auto& pSeg : m_SegmentList) { | |
| if (pSeg->m_dwNumber == dwNumber) | |
| return pSeg.get(); | |
| } | |
| return nullptr; | |
| } | |
| CJBig2_Segment* CJBig2_Context::FindReferredTableSegmentByIndex( | |
| CJBig2_Segment* pSegment, | |
| int32_t nIndex) { | |
| static const uint8_t kTableType = 53; | |
| int32_t count = 0; | |
| for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { | |
| CJBig2_Segment* pSeg = | |
| FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]); | |
| if (pSeg && pSeg->m_cFlags.s.type == kTableType) { | |
| if (count == nIndex) | |
| return pSeg; | |
| ++count; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| JBig2_Result CJBig2_Context::ParseSegmentHeader(CJBig2_Segment* pSegment) { | |
| if (m_pStream->readInteger(&pSegment->m_dwNumber) != 0 || | |
| m_pStream->read1Byte(&pSegment->m_cFlags.c) != 0) { | |
| return JBig2_Result::kFailure; | |
| } | |
| uint8_t cTemp = m_pStream->getCurByte(); | |
| if ((cTemp >> 5) == 7) { | |
| if (m_pStream->readInteger( | |
| (uint32_t*)&pSegment->m_nReferred_to_segment_count) != 0) { | |
| return JBig2_Result::kFailure; | |
| } | |
| pSegment->m_nReferred_to_segment_count &= 0x1fffffff; | |
| if (pSegment->m_nReferred_to_segment_count > | |
| JBIG2_MAX_REFERRED_SEGMENT_COUNT) { | |
| return JBig2_Result::kFailure; | |
| } | |
| } else { | |
| if (m_pStream->read1Byte(&cTemp) != 0) | |
| return JBig2_Result::kFailure; | |
| pSegment->m_nReferred_to_segment_count = cTemp >> 5; | |
| } | |
| uint8_t cSSize = | |
| pSegment->m_dwNumber > 65536 ? 4 : pSegment->m_dwNumber > 256 ? 2 : 1; | |
| uint8_t cPSize = pSegment->m_cFlags.s.page_association_size ? 4 : 1; | |
| if (pSegment->m_nReferred_to_segment_count) { | |
| pSegment->m_Referred_to_segment_numbers.resize( | |
| pSegment->m_nReferred_to_segment_count); | |
| for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { | |
| switch (cSSize) { | |
| case 1: | |
| if (m_pStream->read1Byte(&cTemp) != 0) | |
| return JBig2_Result::kFailure; | |
| pSegment->m_Referred_to_segment_numbers[i] = cTemp; | |
| break; | |
| case 2: | |
| uint16_t wTemp; | |
| if (m_pStream->readShortInteger(&wTemp) != 0) | |
| return JBig2_Result::kFailure; | |
| pSegment->m_Referred_to_segment_numbers[i] = wTemp; | |
| break; | |
| case 4: | |
| uint32_t dwTemp; | |
| if (m_pStream->readInteger(&dwTemp) != 0) | |
| return JBig2_Result::kFailure; | |
| pSegment->m_Referred_to_segment_numbers[i] = dwTemp; | |
| break; | |
| } | |
| if (pSegment->m_Referred_to_segment_numbers[i] >= pSegment->m_dwNumber) | |
| return JBig2_Result::kFailure; | |
| } | |
| } | |
| if (cPSize == 1) { | |
| if (m_pStream->read1Byte(&cTemp) != 0) | |
| return JBig2_Result::kFailure; | |
| pSegment->m_dwPage_association = cTemp; | |
| } else if (m_pStream->readInteger(&pSegment->m_dwPage_association) != 0) { | |
| return JBig2_Result::kFailure; | |
| } | |
| if (m_pStream->readInteger(&pSegment->m_dwData_length) != 0) | |
| return JBig2_Result::kFailure; | |
| pSegment->m_dwObjNum = m_pStream->getObjNum(); | |
| pSegment->m_dwDataOffset = m_pStream->getOffset(); | |
| pSegment->m_State = JBIG2_SEGMENT_DATA_UNPARSED; | |
| return JBig2_Result::kSuccess; | |
| } | |
| JBig2_Result CJBig2_Context::ParseSegmentData(CJBig2_Segment* pSegment, | |
| PauseIndicatorIface* pPause) { | |
| JBig2_Result ret = ProcessingParseSegmentData(pSegment, pPause); | |
| while (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE && | |
| m_pStream->getByteLeft() > 0) { | |
| ret = ProcessingParseSegmentData(pSegment, pPause); | |
| } | |
| return ret; | |
| } | |
| JBig2_Result CJBig2_Context::ProcessingParseSegmentData( | |
| CJBig2_Segment* pSegment, | |
| PauseIndicatorIface* pPause) { | |
| switch (pSegment->m_cFlags.s.type) { | |
| case 0: | |
| return ParseSymbolDict(pSegment); | |
| case 4: | |
| case 6: | |
| case 7: | |
| if (!m_bInPage) | |
| return JBig2_Result::kFailure; | |
| return ParseTextRegion(pSegment); | |
| case 16: | |
| return ParsePatternDict(pSegment, pPause); | |
| case 20: | |
| case 22: | |
| case 23: | |
| if (!m_bInPage) | |
| return JBig2_Result::kFailure; | |
| return ParseHalftoneRegion(pSegment, pPause); | |
| case 36: | |
| case 38: | |
| case 39: | |
| if (!m_bInPage) | |
| return JBig2_Result::kFailure; | |
| return ParseGenericRegion(pSegment, pPause); | |
| case 40: | |
| case 42: | |
| case 43: | |
| if (!m_bInPage) | |
| return JBig2_Result::kFailure; | |
| return ParseGenericRefinementRegion(pSegment); | |
| case 48: { | |
| uint16_t wTemp; | |
| auto pPageInfo = std::make_unique<JBig2PageInfo>(); | |
| if (m_pStream->readInteger(&pPageInfo->m_dwWidth) != 0 || | |
| m_pStream->readInteger(&pPageInfo->m_dwHeight) != 0 || | |
| m_pStream->readInteger(&pPageInfo->m_dwResolutionX) != 0 || | |
| m_pStream->readInteger(&pPageInfo->m_dwResolutionY) != 0 || | |
| m_pStream->read1Byte(&pPageInfo->m_cFlags) != 0 || | |
| m_pStream->readShortInteger(&wTemp) != 0) { | |
| return JBig2_Result::kFailure; | |
| } | |
| pPageInfo->m_bIsStriped = !!(wTemp & 0x8000); | |
| pPageInfo->m_wMaxStripeSize = wTemp & 0x7fff; | |
| bool bMaxHeight = (pPageInfo->m_dwHeight == 0xffffffff); | |
| if (bMaxHeight && !pPageInfo->m_bIsStriped) | |
| pPageInfo->m_bIsStriped = true; | |
| if (!m_bBufSpecified) { | |
| uint32_t height = | |
| bMaxHeight ? pPageInfo->m_wMaxStripeSize : pPageInfo->m_dwHeight; | |
| m_pPage = std::make_unique<CJBig2_Image>(pPageInfo->m_dwWidth, height); | |
| } | |
| if (!m_pPage->data()) { | |
| m_ProcessingStatus = FXCODEC_STATUS_ERROR; | |
| return JBig2_Result::kFailure; | |
| } | |
| m_pPage->Fill((pPageInfo->m_cFlags & 4) ? 1 : 0); | |
| m_PageInfoList.push_back(std::move(pPageInfo)); | |
| m_bInPage = true; | |
| } break; | |
| case 49: | |
| m_bInPage = false; | |
| return JBig2_Result::kEndReached; | |
| break; | |
| case 50: | |
| m_pStream->offset(pSegment->m_dwData_length); | |
| break; | |
| case 51: | |
| return JBig2_Result::kEndReached; | |
| case 52: | |
| m_pStream->offset(pSegment->m_dwData_length); | |
| break; | |
| case 53: | |
| return ParseTable(pSegment); | |
| case 62: | |
| m_pStream->offset(pSegment->m_dwData_length); | |
| break; | |
| default: | |
| break; | |
| } | |
| return JBig2_Result::kSuccess; | |
| } | |
| JBig2_Result CJBig2_Context::ParseSymbolDict(CJBig2_Segment* pSegment) { | |
| uint16_t wFlags; | |
| if (m_pStream->readShortInteger(&wFlags) != 0) | |
| return JBig2_Result::kFailure; | |
| auto pSymbolDictDecoder = std::make_unique<CJBig2_SDDProc>(); | |
| pSymbolDictDecoder->SDHUFF = wFlags & 0x0001; | |
| pSymbolDictDecoder->SDREFAGG = (wFlags >> 1) & 0x0001; | |
| pSymbolDictDecoder->SDTEMPLATE = (wFlags >> 10) & 0x0003; | |
| pSymbolDictDecoder->SDRTEMPLATE = !!((wFlags >> 12) & 0x0003); | |
| if (pSymbolDictDecoder->SDHUFF == 0) { | |
| const uint32_t dwTemp = (pSymbolDictDecoder->SDTEMPLATE == 0) ? 8 : 2; | |
| for (uint32_t i = 0; i < dwTemp; ++i) { | |
| if (m_pStream->read1Byte((uint8_t*)&pSymbolDictDecoder->SDAT[i]) != 0) | |
| return JBig2_Result::kFailure; | |
| } | |
| } | |
| if (pSymbolDictDecoder->SDREFAGG == 1 && !pSymbolDictDecoder->SDRTEMPLATE) { | |
| for (int32_t i = 0; i < 4; ++i) { | |
| if (m_pStream->read1Byte((uint8_t*)&pSymbolDictDecoder->SDRAT[i]) != 0) | |
| return JBig2_Result::kFailure; | |
| } | |
| } | |
| if (m_pStream->readInteger(&pSymbolDictDecoder->SDNUMEXSYMS) != 0 || | |
| m_pStream->readInteger(&pSymbolDictDecoder->SDNUMNEWSYMS) != 0) { | |
| return JBig2_Result::kFailure; | |
| } | |
| if (pSymbolDictDecoder->SDNUMEXSYMS > JBIG2_MAX_EXPORT_SYSMBOLS || | |
| pSymbolDictDecoder->SDNUMNEWSYMS > JBIG2_MAX_NEW_SYSMBOLS) { | |
| return JBig2_Result::kFailure; | |
| } | |
| for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { | |
| if (!FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i])) | |
| return JBig2_Result::kFailure; | |
| } | |
| CJBig2_Segment* pLRSeg = nullptr; | |
| pSymbolDictDecoder->SDNUMINSYMS = 0; | |
| for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { | |
| CJBig2_Segment* pSeg = | |
| FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]); | |
| if (pSeg->m_cFlags.s.type == 0) { | |
| pSymbolDictDecoder->SDNUMINSYMS += pSeg->m_SymbolDict->NumImages(); | |
| pLRSeg = pSeg; | |
| } | |
| } | |
| std::unique_ptr<CJBig2_Image*, FxFreeDeleter> SDINSYMS; | |
| if (pSymbolDictDecoder->SDNUMINSYMS != 0) { | |
| SDINSYMS.reset(FX_Alloc(CJBig2_Image*, pSymbolDictDecoder->SDNUMINSYMS)); | |
| uint32_t dwTemp = 0; | |
| for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { | |
| CJBig2_Segment* pSeg = | |
| FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]); | |
| if (pSeg->m_cFlags.s.type == 0) { | |
| const CJBig2_SymbolDict& dict = *pSeg->m_SymbolDict; | |
| for (size_t j = 0; j < dict.NumImages(); ++j) | |
| SDINSYMS.get()[dwTemp + j] = dict.GetImage(j); | |
| dwTemp += dict.NumImages(); | |
| } | |
| } | |
| } | |
| pSymbolDictDecoder->SDINSYMS = SDINSYMS.get(); | |
| uint8_t cSDHUFFDH = (wFlags >> 2) & 0x0003; | |
| uint8_t cSDHUFFDW = (wFlags >> 4) & 0x0003; | |
| if (pSymbolDictDecoder->SDHUFF == 1) { | |
| if (cSDHUFFDH == 2 || cSDHUFFDW == 2) | |
| return JBig2_Result::kFailure; | |
| int32_t nIndex = 0; | |
| if (cSDHUFFDH == 0) { | |
| pSymbolDictDecoder->SDHUFFDH = GetHuffmanTable(4); | |
| } else if (cSDHUFFDH == 1) { | |
| pSymbolDictDecoder->SDHUFFDH = GetHuffmanTable(5); | |
| } else { | |
| CJBig2_Segment* pSeg = | |
| FindReferredTableSegmentByIndex(pSegment, nIndex++); | |
| if (!pSeg) | |
| return JBig2_Result::kFailure; | |
| pSymbolDictDecoder->SDHUFFDH = pSeg->m_HuffmanTable.get(); | |
| } | |
| if (cSDHUFFDW == 0) { | |
| pSymbolDictDecoder->SDHUFFDW = GetHuffmanTable(2); | |
| } else if (cSDHUFFDW == 1) { | |
| pSymbolDictDecoder->SDHUFFDW = GetHuffmanTable(3); | |
| } else { | |
| CJBig2_Segment* pSeg = | |
| FindReferredTableSegmentByIndex(pSegment, nIndex++); | |
| if (!pSeg) | |
| return JBig2_Result::kFailure; | |
| pSymbolDictDecoder->SDHUFFDW = pSeg->m_HuffmanTable.get(); | |
| } | |
| uint8_t cSDHUFFBMSIZE = (wFlags >> 6) & 0x0001; | |
| if (cSDHUFFBMSIZE == 0) { | |
| pSymbolDictDecoder->SDHUFFBMSIZE = GetHuffmanTable(1); | |
| } else { | |
| CJBig2_Segment* pSeg = | |
| FindReferredTableSegmentByIndex(pSegment, nIndex++); | |
| if (!pSeg) | |
| return JBig2_Result::kFailure; | |
| pSymbolDictDecoder->SDHUFFBMSIZE = pSeg->m_HuffmanTable.get(); | |
| } | |
| if (pSymbolDictDecoder->SDREFAGG == 1) { | |
| uint8_t cSDHUFFAGGINST = (wFlags >> 7) & 0x0001; | |
| if (cSDHUFFAGGINST == 0) { | |
| pSymbolDictDecoder->SDHUFFAGGINST = GetHuffmanTable(1); | |
| } else { | |
| CJBig2_Segment* pSeg = | |
| FindReferredTableSegmentByIndex(pSegment, nIndex++); | |
| if (!pSeg) | |
| return JBig2_Result::kFailure; | |
| pSymbolDictDecoder->SDHUFFAGGINST = pSeg->m_HuffmanTable.get(); | |
| } | |
| } | |
| } | |
| const bool bUseGbContext = (pSymbolDictDecoder->SDHUFF == 0); | |
| const bool bUseGrContext = (pSymbolDictDecoder->SDREFAGG == 1); | |
| const size_t gbContextSize = | |
| GetHuffContextSize(pSymbolDictDecoder->SDTEMPLATE); | |
| const size_t grContextSize = | |
| GetRefAggContextSize(pSymbolDictDecoder->SDRTEMPLATE); | |
| std::vector<JBig2ArithCtx> gbContext; | |
| std::vector<JBig2ArithCtx> grContext; | |
| if ((wFlags & 0x0100) && pLRSeg) { | |
| if (bUseGbContext) { | |
| gbContext = pLRSeg->m_SymbolDict->GbContext(); | |
| if (gbContext.size() != gbContextSize) | |
| return JBig2_Result::kFailure; | |
| } | |
| if (bUseGrContext) { | |
| grContext = pLRSeg->m_SymbolDict->GrContext(); | |
| if (grContext.size() != grContextSize) | |
| return JBig2_Result::kFailure; | |
| } | |
| } else { | |
| if (bUseGbContext) | |
| gbContext.resize(gbContextSize); | |
| if (bUseGrContext) | |
| grContext.resize(grContextSize); | |
| } | |
| CJBig2_CacheKey key = | |
| CJBig2_CacheKey(pSegment->m_dwObjNum, pSegment->m_dwDataOffset); | |
| bool cache_hit = false; | |
| pSegment->m_nResultType = JBIG2_SYMBOL_DICT_POINTER; | |
| if (m_bIsGlobal && key.first != 0) { | |
| for (auto it = m_pSymbolDictCache->begin(); it != m_pSymbolDictCache->end(); | |
| ++it) { | |
| if (it->first == key) { | |
| pSegment->m_SymbolDict = it->second->DeepCopy(); | |
| m_pSymbolDictCache->push_front( | |
| CJBig2_CachePair(key, std::move(it->second))); | |
| m_pSymbolDictCache->erase(it); | |
| cache_hit = true; | |
| break; | |
| } | |
| } | |
| } | |
| if (!cache_hit) { | |
| if (bUseGbContext) { | |
| auto pArithDecoder = | |
| std::make_unique<CJBig2_ArithDecoder>(m_pStream.get()); | |
| pSegment->m_SymbolDict = pSymbolDictDecoder->DecodeArith( | |
| pArithDecoder.get(), &gbContext, &grContext); | |
| if (!pSegment->m_SymbolDict) | |
| return JBig2_Result::kFailure; | |
| m_pStream->alignByte(); | |
| m_pStream->offset(2); | |
| } else { | |
| pSegment->m_SymbolDict = pSymbolDictDecoder->DecodeHuffman( | |
| m_pStream.get(), &gbContext, &grContext); | |
| if (!pSegment->m_SymbolDict) | |
| return JBig2_Result::kFailure; | |
| m_pStream->alignByte(); | |
| } | |
| if (m_bIsGlobal) { | |
| std::unique_ptr<CJBig2_SymbolDict> value = | |
| pSegment->m_SymbolDict->DeepCopy(); | |
| size_t size = m_pSymbolDictCache->size(); | |
| while (size >= kSymbolDictCacheMaxSize) { | |
| m_pSymbolDictCache->pop_back(); | |
| --size; | |
| } | |
| m_pSymbolDictCache->push_front(CJBig2_CachePair(key, std::move(value))); | |
| } | |
| } | |
| if (wFlags & 0x0200) { | |
| if (bUseGbContext) | |
| pSegment->m_SymbolDict->SetGbContext(std::move(gbContext)); | |
| if (bUseGrContext) | |
| pSegment->m_SymbolDict->SetGrContext(std::move(grContext)); | |
| } | |
| return JBig2_Result::kSuccess; | |
| } | |
| JBig2_Result CJBig2_Context::ParseTextRegion(CJBig2_Segment* pSegment) { | |
| uint16_t wFlags; | |
| JBig2RegionInfo ri; | |
| if (ParseRegionInfo(&ri) != JBig2_Result::kSuccess || | |
| m_pStream->readShortInteger(&wFlags) != 0) { | |
| return JBig2_Result::kFailure; | |
| } | |
| if (!CJBig2_Image::IsValidImageSize(ri.width, ri.height)) | |
| return JBig2_Result::kFailure; | |
| auto pTRD = std::make_unique<CJBig2_TRDProc>(); | |
| pTRD->SBW = ri.width; | |
| pTRD->SBH = ri.height; | |
| pTRD->SBHUFF = wFlags & 0x0001; | |
| pTRD->SBREFINE = (wFlags >> 1) & 0x0001; | |
| uint32_t dwTemp = (wFlags >> 2) & 0x0003; | |
| pTRD->SBSTRIPS = 1 << dwTemp; | |
| pTRD->REFCORNER = (JBig2Corner)((wFlags >> 4) & 0x0003); | |
| pTRD->TRANSPOSED = (wFlags >> 6) & 0x0001; | |
| pTRD->SBCOMBOP = (JBig2ComposeOp)((wFlags >> 7) & 0x0003); | |
| pTRD->SBDEFPIXEL = (wFlags >> 9) & 0x0001; | |
| pTRD->SBDSOFFSET = (wFlags >> 10) & 0x001f; | |
| if (pTRD->SBDSOFFSET >= 0x0010) { | |
| pTRD->SBDSOFFSET = pTRD->SBDSOFFSET - 0x0020; | |
| } | |
| pTRD->SBRTEMPLATE = !!((wFlags >> 15) & 0x0001); | |
| if (pTRD->SBHUFF == 1 && m_pStream->readShortInteger(&wFlags) != 0) { | |
| return JBig2_Result::kFailure; | |
| } | |
| if (pTRD->SBREFINE == 1 && !pTRD->SBRTEMPLATE) { | |
| for (int32_t i = 0; i < 4; ++i) { | |
| if (m_pStream->read1Byte((uint8_t*)&pTRD->SBRAT[i]) != 0) | |
| return JBig2_Result::kFailure; | |
| } | |
| } | |
| if (m_pStream->readInteger(&pTRD->SBNUMINSTANCES) != 0) | |
| return JBig2_Result::kFailure; | |
| // Assume each instance takes at least 0.25 bits when encoded. That means for | |
| // a stream of length N bytes, there can be at most 32N instances. This is a | |
| // conservative estimate just to sanitize the |SBNUMINSTANCES| value. | |
| // Use FX_SAFE_INT32 to be safe, though it should never overflow because PDFs | |
| // have a maximum size of roughly 11 GB. | |
| FX_SAFE_INT32 nMaxStripInstances = m_pStream->getLength(); | |
| nMaxStripInstances *= 32; | |
| if (pTRD->SBNUMINSTANCES > nMaxStripInstances.ValueOrDie()) | |
| return JBig2_Result::kFailure; | |
| for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { | |
| if (!FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i])) | |
| return JBig2_Result::kFailure; | |
| } | |
| pTRD->SBNUMSYMS = 0; | |
| for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { | |
| CJBig2_Segment* pSeg = | |
| FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]); | |
| if (pSeg->m_cFlags.s.type == 0) { | |
| pTRD->SBNUMSYMS += pSeg->m_SymbolDict->NumImages(); | |
| } | |
| } | |
| std::unique_ptr<CJBig2_Image*, FxFreeDeleter> SBSYMS; | |
| if (pTRD->SBNUMSYMS > 0) { | |
| SBSYMS.reset(FX_Alloc(CJBig2_Image*, pTRD->SBNUMSYMS)); | |
| dwTemp = 0; | |
| for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { | |
| CJBig2_Segment* pSeg = | |
| FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]); | |
| if (pSeg->m_cFlags.s.type == 0) { | |
| const CJBig2_SymbolDict& dict = *pSeg->m_SymbolDict; | |
| for (size_t j = 0; j < dict.NumImages(); ++j) | |
| SBSYMS.get()[dwTemp + j] = dict.GetImage(j); | |
| dwTemp += dict.NumImages(); | |
| } | |
| } | |
| pTRD->SBSYMS = SBSYMS.get(); | |
| } else { | |
| pTRD->SBSYMS = nullptr; | |
| } | |
| if (pTRD->SBHUFF == 1) { | |
| std::vector<JBig2HuffmanCode> SBSYMCODES = | |
| DecodeSymbolIDHuffmanTable(pTRD->SBNUMSYMS); | |
| if (SBSYMCODES.empty()) | |
| return JBig2_Result::kFailure; | |
| m_pStream->alignByte(); | |
| pTRD->SBSYMCODES = std::move(SBSYMCODES); | |
| } else { | |
| dwTemp = 0; | |
| while ((uint32_t)(1 << dwTemp) < pTRD->SBNUMSYMS) { | |
| ++dwTemp; | |
| } | |
| pTRD->SBSYMCODELEN = (uint8_t)dwTemp; | |
| } | |
| if (pTRD->SBHUFF == 1) { | |
| uint8_t cSBHUFFFS = wFlags & 0x0003; | |
| uint8_t cSBHUFFDS = (wFlags >> 2) & 0x0003; | |
| uint8_t cSBHUFFDT = (wFlags >> 4) & 0x0003; | |
| uint8_t cSBHUFFRDW = (wFlags >> 6) & 0x0003; | |
| uint8_t cSBHUFFRDH = (wFlags >> 8) & 0x0003; | |
| uint8_t cSBHUFFRDX = (wFlags >> 10) & 0x0003; | |
| uint8_t cSBHUFFRDY = (wFlags >> 12) & 0x0003; | |
| uint8_t cSBHUFFRSIZE = (wFlags >> 14) & 0x0001; | |
| if (cSBHUFFFS == 2 || cSBHUFFRDW == 2 || cSBHUFFRDH == 2 || | |
| cSBHUFFRDX == 2 || cSBHUFFRDY == 2) { | |
| return JBig2_Result::kFailure; | |
| } | |
| int32_t nIndex = 0; | |
| if (cSBHUFFFS == 0) { | |
| pTRD->SBHUFFFS = GetHuffmanTable(6); | |
| } else if (cSBHUFFFS == 1) { | |
| pTRD->SBHUFFFS = GetHuffmanTable(7); | |
| } else { | |
| CJBig2_Segment* pSeg = | |
| FindReferredTableSegmentByIndex(pSegment, nIndex++); | |
| if (!pSeg) | |
| return JBig2_Result::kFailure; | |
| pTRD->SBHUFFFS = pSeg->m_HuffmanTable.get(); | |
| } | |
| if (cSBHUFFDS == 0) { | |
| pTRD->SBHUFFDS = GetHuffmanTable(8); | |
| } else if (cSBHUFFDS == 1) { | |
| pTRD->SBHUFFDS = GetHuffmanTable(9); | |
| } else if (cSBHUFFDS == 2) { | |
| pTRD->SBHUFFDS = GetHuffmanTable(10); | |
| } else { | |
| CJBig2_Segment* pSeg = | |
| FindReferredTableSegmentByIndex(pSegment, nIndex++); | |
| if (!pSeg) | |
| return JBig2_Result::kFailure; | |
| pTRD->SBHUFFDS = pSeg->m_HuffmanTable.get(); | |
| } | |
| if (cSBHUFFDT == 0) { | |
| pTRD->SBHUFFDT = GetHuffmanTable(11); | |
| } else if (cSBHUFFDT == 1) { | |
| pTRD->SBHUFFDT = GetHuffmanTable(12); | |
| } else if (cSBHUFFDT == 2) { | |
| pTRD->SBHUFFDT = GetHuffmanTable(13); | |
| } else { | |
| CJBig2_Segment* pSeg = | |
| FindReferredTableSegmentByIndex(pSegment, nIndex++); | |
| if (!pSeg) | |
| return JBig2_Result::kFailure; | |
| pTRD->SBHUFFDT = pSeg->m_HuffmanTable.get(); | |
| } | |
| if (cSBHUFFRDW == 0) { | |
| pTRD->SBHUFFRDW = GetHuffmanTable(14); | |
| } else if (cSBHUFFRDW == 1) { | |
| pTRD->SBHUFFRDW = GetHuffmanTable(15); | |
| } else { | |
| CJBig2_Segment* pSeg = | |
| FindReferredTableSegmentByIndex(pSegment, nIndex++); | |
| if (!pSeg) | |
| return JBig2_Result::kFailure; | |
| pTRD->SBHUFFRDW = pSeg->m_HuffmanTable.get(); | |
| } | |
| if (cSBHUFFRDH == 0) { | |
| pTRD->SBHUFFRDH = GetHuffmanTable(14); | |
| } else if (cSBHUFFRDH == 1) { | |
| pTRD->SBHUFFRDH = GetHuffmanTable(15); | |
| } else { | |
| CJBig2_Segment* pSeg = | |
| FindReferredTableSegmentByIndex(pSegment, nIndex++); | |
| if (!pSeg) | |
| return JBig2_Result::kFailure; | |
| pTRD->SBHUFFRDH = pSeg->m_HuffmanTable.get(); | |
| } | |
| if (cSBHUFFRDX == 0) { | |
| pTRD->SBHUFFRDX = GetHuffmanTable(14); | |
| } else if (cSBHUFFRDX == 1) { | |
| pTRD->SBHUFFRDX = GetHuffmanTable(15); | |
| } else { | |
| CJBig2_Segment* pSeg = | |
| FindReferredTableSegmentByIndex(pSegment, nIndex++); | |
| if (!pSeg) | |
| return JBig2_Result::kFailure; | |
| pTRD->SBHUFFRDX = pSeg->m_HuffmanTable.get(); | |
| } | |
| if (cSBHUFFRDY == 0) { | |
| pTRD->SBHUFFRDY = GetHuffmanTable(14); | |
| } else if (cSBHUFFRDY == 1) { | |
| pTRD->SBHUFFRDY = GetHuffmanTable(15); | |
| } else { | |
| CJBig2_Segment* pSeg = | |
| FindReferredTableSegmentByIndex(pSegment, nIndex++); | |
| if (!pSeg) | |
| return JBig2_Result::kFailure; | |
| pTRD->SBHUFFRDY = pSeg->m_HuffmanTable.get(); | |
| } | |
| if (cSBHUFFRSIZE == 0) { | |
| pTRD->SBHUFFRSIZE = GetHuffmanTable(1); | |
| } else { | |
| CJBig2_Segment* pSeg = | |
| FindReferredTableSegmentByIndex(pSegment, nIndex++); | |
| if (!pSeg) | |
| return JBig2_Result::kFailure; | |
| pTRD->SBHUFFRSIZE = pSeg->m_HuffmanTable.get(); | |
| } | |
| } | |
| std::unique_ptr<JBig2ArithCtx, FxFreeDeleter> grContext; | |
| if (pTRD->SBREFINE == 1) { | |
| const size_t size = GetRefAggContextSize(pTRD->SBRTEMPLATE); | |
| grContext.reset(FX_Alloc(JBig2ArithCtx, size)); | |
| } | |
| if (pTRD->SBHUFF == 0) { | |
| auto pArithDecoder = std::make_unique<CJBig2_ArithDecoder>(m_pStream.get()); | |
| pSegment->m_nResultType = JBIG2_IMAGE_POINTER; | |
| pSegment->m_Image = | |
| pTRD->DecodeArith(pArithDecoder.get(), grContext.get(), nullptr); | |
| if (!pSegment->m_Image) | |
| return JBig2_Result::kFailure; | |
| m_pStream->alignByte(); | |
| m_pStream->offset(2); | |
| } else { | |
| pSegment->m_nResultType = JBIG2_IMAGE_POINTER; | |
| pSegment->m_Image = pTRD->DecodeHuffman(m_pStream.get(), grContext.get()); | |
| if (!pSegment->m_Image) | |
| return JBig2_Result::kFailure; | |
| m_pStream->alignByte(); | |
| } | |
| if (pSegment->m_cFlags.s.type != 4) { | |
| if (!m_bBufSpecified) { | |
| const auto& pPageInfo = m_PageInfoList.back(); | |
| if ((pPageInfo->m_bIsStriped == 1) && | |
| (ri.y + ri.height > m_pPage->height())) { | |
| m_pPage->Expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0); | |
| } | |
| } | |
| m_pPage->ComposeFrom(ri.x, ri.y, pSegment->m_Image.get(), | |
| (JBig2ComposeOp)(ri.flags & 0x03)); | |
| pSegment->m_Image.reset(); | |
| } | |
| return JBig2_Result::kSuccess; | |
| } | |
| JBig2_Result CJBig2_Context::ParsePatternDict(CJBig2_Segment* pSegment, | |
| PauseIndicatorIface* pPause) { | |
| uint8_t cFlags; | |
| auto pPDD = std::make_unique<CJBig2_PDDProc>(); | |
| if (m_pStream->read1Byte(&cFlags) != 0 || | |
| m_pStream->read1Byte(&pPDD->HDPW) != 0 || | |
| m_pStream->read1Byte(&pPDD->HDPH) != 0 || | |
| m_pStream->readInteger(&pPDD->GRAYMAX) != 0) { | |
| return JBig2_Result::kFailure; | |
| } | |
| if (pPDD->GRAYMAX > JBIG2_MAX_PATTERN_INDEX) | |
| return JBig2_Result::kFailure; | |
| pPDD->HDMMR = cFlags & 0x01; | |
| pPDD->HDTEMPLATE = (cFlags >> 1) & 0x03; | |
| pSegment->m_nResultType = JBIG2_PATTERN_DICT_POINTER; | |
| if (pPDD->HDMMR == 0) { | |
| const size_t size = GetHuffContextSize(pPDD->HDTEMPLATE); | |
| std::unique_ptr<JBig2ArithCtx, FxFreeDeleter> gbContext( | |
| FX_Alloc(JBig2ArithCtx, size)); | |
| auto pArithDecoder = std::make_unique<CJBig2_ArithDecoder>(m_pStream.get()); | |
| pSegment->m_PatternDict = | |
| pPDD->DecodeArith(pArithDecoder.get(), gbContext.get(), pPause); | |
| if (!pSegment->m_PatternDict) | |
| return JBig2_Result::kFailure; | |
| m_pStream->alignByte(); | |
| m_pStream->offset(2); | |
| } else { | |
| pSegment->m_PatternDict = pPDD->DecodeMMR(m_pStream.get()); | |
| if (!pSegment->m_PatternDict) | |
| return JBig2_Result::kFailure; | |
| m_pStream->alignByte(); | |
| } | |
| return JBig2_Result::kSuccess; | |
| } | |
| JBig2_Result CJBig2_Context::ParseHalftoneRegion(CJBig2_Segment* pSegment, | |
| PauseIndicatorIface* pPause) { | |
| uint8_t cFlags; | |
| JBig2RegionInfo ri; | |
| auto pHRD = std::make_unique<CJBig2_HTRDProc>(); | |
| if (ParseRegionInfo(&ri) != JBig2_Result::kSuccess || | |
| m_pStream->read1Byte(&cFlags) != 0 || | |
| m_pStream->readInteger(&pHRD->HGW) != 0 || | |
| m_pStream->readInteger(&pHRD->HGH) != 0 || | |
| m_pStream->readInteger((uint32_t*)&pHRD->HGX) != 0 || | |
| m_pStream->readInteger((uint32_t*)&pHRD->HGY) != 0 || | |
| m_pStream->readShortInteger(&pHRD->HRX) != 0 || | |
| m_pStream->readShortInteger(&pHRD->HRY) != 0) { | |
| return JBig2_Result::kFailure; | |
| } | |
| if (!CJBig2_Image::IsValidImageSize(pHRD->HGW, pHRD->HGH)) | |
| return JBig2_Result::kFailure; | |
| if (!CJBig2_Image::IsValidImageSize(ri.width, ri.height)) | |
| return JBig2_Result::kFailure; | |
| pHRD->HBW = ri.width; | |
| pHRD->HBH = ri.height; | |
| pHRD->HMMR = cFlags & 0x01; | |
| pHRD->HTEMPLATE = (cFlags >> 1) & 0x03; | |
| pHRD->HENABLESKIP = (cFlags >> 3) & 0x01; | |
| pHRD->HCOMBOP = (JBig2ComposeOp)((cFlags >> 4) & 0x07); | |
| pHRD->HDEFPIXEL = (cFlags >> 7) & 0x01; | |
| if (pSegment->m_nReferred_to_segment_count != 1) | |
| return JBig2_Result::kFailure; | |
| CJBig2_Segment* pSeg = | |
| FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[0]); | |
| if (!pSeg || (pSeg->m_cFlags.s.type != 16)) | |
| return JBig2_Result::kFailure; | |
| const CJBig2_PatternDict* pPatternDict = pSeg->m_PatternDict.get(); | |
| if (!pPatternDict || (pPatternDict->NUMPATS == 0)) | |
| return JBig2_Result::kFailure; | |
| pHRD->HNUMPATS = pPatternDict->NUMPATS; | |
| pHRD->HPATS = &pPatternDict->HDPATS; | |
| pHRD->HPW = pPatternDict->HDPATS[0]->width(); | |
| pHRD->HPH = pPatternDict->HDPATS[0]->height(); | |
| pSegment->m_nResultType = JBIG2_IMAGE_POINTER; | |
| if (pHRD->HMMR == 0) { | |
| const size_t size = GetHuffContextSize(pHRD->HTEMPLATE); | |
| std::unique_ptr<JBig2ArithCtx, FxFreeDeleter> gbContext( | |
| FX_Alloc(JBig2ArithCtx, size)); | |
| auto pArithDecoder = std::make_unique<CJBig2_ArithDecoder>(m_pStream.get()); | |
| pSegment->m_Image = | |
| pHRD->DecodeArith(pArithDecoder.get(), gbContext.get(), pPause); | |
| if (!pSegment->m_Image) | |
| return JBig2_Result::kFailure; | |
| m_pStream->alignByte(); | |
| m_pStream->offset(2); | |
| } else { | |
| pSegment->m_Image = pHRD->DecodeMMR(m_pStream.get()); | |
| if (!pSegment->m_Image) | |
| return JBig2_Result::kFailure; | |
| m_pStream->alignByte(); | |
| } | |
| if (pSegment->m_cFlags.s.type != 20) { | |
| if (!m_bBufSpecified) { | |
| const auto& pPageInfo = m_PageInfoList.back(); | |
| if (pPageInfo->m_bIsStriped == 1 && | |
| ri.y + ri.height > m_pPage->height()) { | |
| m_pPage->Expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0); | |
| } | |
| } | |
| m_pPage->ComposeFrom(ri.x, ri.y, pSegment->m_Image.get(), | |
| (JBig2ComposeOp)(ri.flags & 0x03)); | |
| pSegment->m_Image.reset(); | |
| } | |
| return JBig2_Result::kSuccess; | |
| } | |
| JBig2_Result CJBig2_Context::ParseGenericRegion(CJBig2_Segment* pSegment, | |
| PauseIndicatorIface* pPause) { | |
| if (!m_pGRD) { | |
| auto pGRD = std::make_unique<CJBig2_GRDProc>(); | |
| uint8_t cFlags; | |
| if (ParseRegionInfo(&m_ri) != JBig2_Result::kSuccess || | |
| m_pStream->read1Byte(&cFlags) != 0) { | |
| return JBig2_Result::kFailure; | |
| } | |
| if (m_ri.height < 0 || m_ri.width < 0) | |
| return JBig2_Result::kFailure; | |
| pGRD->GBW = m_ri.width; | |
| pGRD->GBH = m_ri.height; | |
| pGRD->MMR = cFlags & 0x01; | |
| pGRD->GBTEMPLATE = (cFlags >> 1) & 0x03; | |
| pGRD->TPGDON = (cFlags >> 3) & 0x01; | |
| if (pGRD->MMR == 0) { | |
| if (pGRD->GBTEMPLATE == 0) { | |
| for (int32_t i = 0; i < 8; ++i) { | |
| if (m_pStream->read1Byte((uint8_t*)&pGRD->GBAT[i]) != 0) | |
| return JBig2_Result::kFailure; | |
| } | |
| } else { | |
| for (int32_t i = 0; i < 2; ++i) { | |
| if (m_pStream->read1Byte((uint8_t*)&pGRD->GBAT[i]) != 0) | |
| return JBig2_Result::kFailure; | |
| } | |
| } | |
| } | |
| pGRD->USESKIP = 0; | |
| m_pGRD = std::move(pGRD); | |
| } | |
| pSegment->m_nResultType = JBIG2_IMAGE_POINTER; | |
| if (m_pGRD->MMR == 0) { | |
| if (m_gbContext.empty()) | |
| m_gbContext.resize(GetHuffContextSize(m_pGRD->GBTEMPLATE)); | |
| bool bStart = !m_pArithDecoder; | |
| if (bStart) { | |
| m_pArithDecoder = std::make_unique<CJBig2_ArithDecoder>(m_pStream.get()); | |
| } | |
| { | |
| // |state.gbContext| can't exist when m_gbContext.clear() called below. | |
| CJBig2_GRDProc::ProgressiveArithDecodeState state; | |
| state.pImage = &pSegment->m_Image; | |
| state.pArithDecoder = m_pArithDecoder.get(); | |
| state.gbContext = m_gbContext.data(); | |
| state.pPause = pPause; | |
| m_ProcessingStatus = bStart ? m_pGRD->StartDecodeArith(&state) | |
| : m_pGRD->ContinueDecode(&state); | |
| if (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE) { | |
| if (pSegment->m_cFlags.s.type != 36) { | |
| if (!m_bBufSpecified) { | |
| const auto& pPageInfo = m_PageInfoList.back(); | |
| if ((pPageInfo->m_bIsStriped == 1) && | |
| (m_ri.y + m_ri.height > m_pPage->height())) { | |
| m_pPage->Expand(m_ri.y + m_ri.height, | |
| (pPageInfo->m_cFlags & 4) ? 1 : 0); | |
| } | |
| } | |
| const FX_RECT& rect = m_pGRD->GetReplaceRect(); | |
| m_pPage->ComposeFromWithRect(m_ri.x + rect.left, m_ri.y + rect.top, | |
| pSegment->m_Image.get(), rect, | |
| (JBig2ComposeOp)(m_ri.flags & 0x03)); | |
| } | |
| return JBig2_Result::kSuccess; | |
| } | |
| } | |
| m_pArithDecoder.reset(); | |
| m_gbContext.clear(); | |
| if (!pSegment->m_Image) { | |
| m_ProcessingStatus = FXCODEC_STATUS_ERROR; | |
| m_pGRD.reset(); | |
| return JBig2_Result::kFailure; | |
| } | |
| m_pStream->alignByte(); | |
| m_pStream->offset(2); | |
| } else { | |
| m_pGRD->StartDecodeMMR(&pSegment->m_Image, m_pStream.get()); | |
| if (!pSegment->m_Image) { | |
| m_pGRD.reset(); | |
| return JBig2_Result::kFailure; | |
| } | |
| m_pStream->alignByte(); | |
| } | |
| if (pSegment->m_cFlags.s.type != 36) { | |
| if (!m_bBufSpecified) { | |
| JBig2PageInfo* pPageInfo = m_PageInfoList.back().get(); | |
| if ((pPageInfo->m_bIsStriped == 1) && | |
| (m_ri.y + m_ri.height > m_pPage->height())) { | |
| m_pPage->Expand(m_ri.y + m_ri.height, | |
| (pPageInfo->m_cFlags & 4) ? 1 : 0); | |
| } | |
| } | |
| const FX_RECT& rect = m_pGRD->GetReplaceRect(); | |
| m_pPage->ComposeFromWithRect(m_ri.x + rect.left, m_ri.y + rect.top, | |
| pSegment->m_Image.get(), rect, | |
| (JBig2ComposeOp)(m_ri.flags & 0x03)); | |
| pSegment->m_Image.reset(); | |
| } | |
| m_pGRD.reset(); | |
| return JBig2_Result::kSuccess; | |
| } | |
| JBig2_Result CJBig2_Context::ParseGenericRefinementRegion( | |
| CJBig2_Segment* pSegment) { | |
| JBig2RegionInfo ri; | |
| uint8_t cFlags; | |
| if (ParseRegionInfo(&ri) != JBig2_Result::kSuccess || | |
| m_pStream->read1Byte(&cFlags) != 0) { | |
| return JBig2_Result::kFailure; | |
| } | |
| if (!CJBig2_Image::IsValidImageSize(ri.width, ri.height)) | |
| return JBig2_Result::kFailure; | |
| auto pGRRD = std::make_unique<CJBig2_GRRDProc>(); | |
| pGRRD->GRW = ri.width; | |
| pGRRD->GRH = ri.height; | |
| pGRRD->GRTEMPLATE = !!(cFlags & 0x01); | |
| pGRRD->TPGRON = (cFlags >> 1) & 0x01; | |
| if (!pGRRD->GRTEMPLATE) { | |
| for (int32_t i = 0; i < 4; ++i) { | |
| if (m_pStream->read1Byte((uint8_t*)&pGRRD->GRAT[i]) != 0) | |
| return JBig2_Result::kFailure; | |
| } | |
| } | |
| CJBig2_Segment* pSeg = nullptr; | |
| if (pSegment->m_nReferred_to_segment_count > 0) { | |
| int32_t i; | |
| for (i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { | |
| pSeg = FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[0]); | |
| if (!pSeg) | |
| return JBig2_Result::kFailure; | |
| if (pSeg->m_cFlags.s.type == 4 || pSeg->m_cFlags.s.type == 20 || | |
| pSeg->m_cFlags.s.type == 36 || pSeg->m_cFlags.s.type == 40) { | |
| break; | |
| } | |
| } | |
| if (i >= pSegment->m_nReferred_to_segment_count) | |
| return JBig2_Result::kFailure; | |
| pGRRD->GRREFERENCE = pSeg->m_Image.get(); | |
| } else { | |
| pGRRD->GRREFERENCE = m_pPage.get(); | |
| } | |
| pGRRD->GRREFERENCEDX = 0; | |
| pGRRD->GRREFERENCEDY = 0; | |
| const size_t size = GetRefAggContextSize(pGRRD->GRTEMPLATE); | |
| std::unique_ptr<JBig2ArithCtx, FxFreeDeleter> grContext( | |
| FX_Alloc(JBig2ArithCtx, size)); | |
| auto pArithDecoder = std::make_unique<CJBig2_ArithDecoder>(m_pStream.get()); | |
| pSegment->m_nResultType = JBIG2_IMAGE_POINTER; | |
| pSegment->m_Image = pGRRD->Decode(pArithDecoder.get(), grContext.get()); | |
| if (!pSegment->m_Image) | |
| return JBig2_Result::kFailure; | |
| m_pStream->alignByte(); | |
| m_pStream->offset(2); | |
| if (pSegment->m_cFlags.s.type != 40) { | |
| if (!m_bBufSpecified) { | |
| JBig2PageInfo* pPageInfo = m_PageInfoList.back().get(); | |
| if ((pPageInfo->m_bIsStriped == 1) && | |
| (ri.y + ri.height > m_pPage->height())) { | |
| m_pPage->Expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0); | |
| } | |
| } | |
| m_pPage->ComposeFrom(ri.x, ri.y, pSegment->m_Image.get(), | |
| (JBig2ComposeOp)(ri.flags & 0x03)); | |
| pSegment->m_Image.reset(); | |
| } | |
| return JBig2_Result::kSuccess; | |
| } | |
| JBig2_Result CJBig2_Context::ParseTable(CJBig2_Segment* pSegment) { | |
| pSegment->m_nResultType = JBIG2_HUFFMAN_TABLE_POINTER; | |
| pSegment->m_HuffmanTable.reset(); | |
| auto pHuff = std::make_unique<CJBig2_HuffmanTable>(m_pStream.get()); | |
| if (!pHuff->IsOK()) | |
| return JBig2_Result::kFailure; | |
| pSegment->m_HuffmanTable = std::move(pHuff); | |
| m_pStream->alignByte(); | |
| return JBig2_Result::kSuccess; | |
| } | |
| JBig2_Result CJBig2_Context::ParseRegionInfo(JBig2RegionInfo* pRI) { | |
| if (m_pStream->readInteger((uint32_t*)&pRI->width) != 0 || | |
| m_pStream->readInteger((uint32_t*)&pRI->height) != 0 || | |
| m_pStream->readInteger((uint32_t*)&pRI->x) != 0 || | |
| m_pStream->readInteger((uint32_t*)&pRI->y) != 0 || | |
| m_pStream->read1Byte(&pRI->flags) != 0) { | |
| return JBig2_Result::kFailure; | |
| } | |
| return JBig2_Result::kSuccess; | |
| } | |
| std::vector<JBig2HuffmanCode> CJBig2_Context::DecodeSymbolIDHuffmanTable( | |
| uint32_t SBNUMSYMS) { | |
| const size_t kRunCodesSize = 35; | |
| JBig2HuffmanCode huffman_codes[kRunCodesSize]; | |
| for (size_t i = 0; i < kRunCodesSize; ++i) { | |
| if (m_pStream->readNBits(4, &huffman_codes[i].codelen) != 0) | |
| return std::vector<JBig2HuffmanCode>(); | |
| } | |
| if (!HuffmanAssignCode(huffman_codes, kRunCodesSize)) | |
| return std::vector<JBig2HuffmanCode>(); | |
| std::vector<JBig2HuffmanCode> SBSYMCODES(SBNUMSYMS); | |
| int32_t run = 0; | |
| int32_t i = 0; | |
| while (i < static_cast<int>(SBNUMSYMS)) { | |
| size_t j; | |
| FX_SAFE_INT32 nSafeVal = 0; | |
| int32_t nBits = 0; | |
| uint32_t nTemp; | |
| while (true) { | |
| if (m_pStream->read1Bit(&nTemp) != 0) | |
| return std::vector<JBig2HuffmanCode>(); | |
| nSafeVal <<= 1; | |
| if (!nSafeVal.IsValid()) | |
| return std::vector<JBig2HuffmanCode>(); | |
| nSafeVal |= nTemp; | |
| ++nBits; | |
| const int32_t nVal = nSafeVal.ValueOrDie(); | |
| for (j = 0; j < kRunCodesSize; ++j) { | |
| if (nBits == huffman_codes[j].codelen && nVal == huffman_codes[j].code) | |
| break; | |
| } | |
| if (j < kRunCodesSize) | |
| break; | |
| } | |
| int32_t runcode = static_cast<int32_t>(j); | |
| if (runcode < 32) { | |
| SBSYMCODES[i].codelen = runcode; | |
| run = 0; | |
| } else if (runcode == 32) { | |
| if (m_pStream->readNBits(2, &nTemp) != 0) | |
| return std::vector<JBig2HuffmanCode>(); | |
| run = nTemp + 3; | |
| } else if (runcode == 33) { | |
| if (m_pStream->readNBits(3, &nTemp) != 0) | |
| return std::vector<JBig2HuffmanCode>(); | |
| run = nTemp + 3; | |
| } else if (runcode == 34) { | |
| if (m_pStream->readNBits(7, &nTemp) != 0) | |
| return std::vector<JBig2HuffmanCode>(); | |
| run = nTemp + 11; | |
| } | |
| if (run > 0) { | |
| if (i + run > (int)SBNUMSYMS) | |
| return std::vector<JBig2HuffmanCode>(); | |
| for (int32_t k = 0; k < run; ++k) { | |
| if (runcode == 32 && i > 0) | |
| SBSYMCODES[i + k].codelen = SBSYMCODES[i - 1].codelen; | |
| else | |
| SBSYMCODES[i + k].codelen = 0; | |
| } | |
| i += run; | |
| } else { | |
| ++i; | |
| } | |
| } | |
| if (!HuffmanAssignCode(SBSYMCODES.data(), SBNUMSYMS)) | |
| return std::vector<JBig2HuffmanCode>(); | |
| return SBSYMCODES; | |
| } | |
| const CJBig2_HuffmanTable* CJBig2_Context::GetHuffmanTable(size_t idx) { | |
| ASSERT(idx > 0); | |
| ASSERT(idx < CJBig2_HuffmanTable::kNumHuffmanTables); | |
| if (!m_HuffmanTables[idx].get()) | |
| m_HuffmanTables[idx] = std::make_unique<CJBig2_HuffmanTable>(idx); | |
| return m_HuffmanTables[idx].get(); | |
| } | |
| // static | |
| bool CJBig2_Context::HuffmanAssignCode(JBig2HuffmanCode* SBSYMCODES, | |
| uint32_t NTEMP) { | |
| int LENMAX = 0; | |
| for (uint32_t i = 0; i < NTEMP; ++i) | |
| LENMAX = std::max(SBSYMCODES[i].codelen, LENMAX); | |
| std::vector<int> LENCOUNT(LENMAX + 1); | |
| std::vector<int> FIRSTCODE(LENMAX + 1); | |
| for (uint32_t i = 0; i < NTEMP; ++i) | |
| ++LENCOUNT[SBSYMCODES[i].codelen]; | |
| LENCOUNT[0] = 0; | |
| for (int i = 1; i <= LENMAX; ++i) { | |
| FX_SAFE_INT32 shifted = FIRSTCODE[i - 1]; | |
| shifted += LENCOUNT[i - 1]; | |
| shifted <<= 1; | |
| if (!shifted.IsValid()) | |
| return false; | |
| FIRSTCODE[i] = shifted.ValueOrDie(); | |
| int CURCODE = FIRSTCODE[i]; | |
| for (uint32_t j = 0; j < NTEMP; ++j) { | |
| if (SBSYMCODES[j].codelen == i) | |
| SBSYMCODES[j].code = CURCODE++; | |
| } | |
| } | |
| return true; | |
| } |