blob: 9a0d6007de57990ac9ca3c7161e52c6c79927a14 [file] [log] [blame]
// Copyright 2014 The PDFium Authors
// 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
#if defined(UNSAFE_BUFFERS_BUILD)
// TODO(crbug.com/pdfium/2153): resolve buffer safety issues.
#pragma allow_unsafe_buffers
#endif
#include "core/fxcodec/jbig2/JBig2_Context.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/check.h"
#include "core/fxcrt/fx_memory_wrappers.h"
#include "core/fxcrt/fx_safe_types.h"
#include "core/fxcrt/pauseindicator_iface.h"
#include "core/fxcrt/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,
uint64_t global_key,
pdfium::span<const uint8_t> pSrcSpan,
uint64_t src_key,
std::list<CJBig2_CachePair>* pSymbolDictCache) {
auto result = pdfium::WrapUnique(
new CJBig2_Context(pSrcSpan, src_key, pSymbolDictCache, false));
if (!pGlobalSpan.empty()) {
result->m_pGlobalContext = pdfium::WrapUnique(
new CJBig2_Context(pGlobalSpan, global_key, pSymbolDictCache, true));
}
return result;
}
CJBig2_Context::CJBig2_Context(pdfium::span<const uint8_t> pSrcSpan,
uint64_t src_key,
std::list<CJBig2_CachePair>* pSymbolDictCache,
bool bIsGlobal)
: m_pStream(std::make_unique<CJBig2_BitStream>(pSrcSpan, src_key)),
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_nOffset = m_pStream->getOffset();
}
nRet = ParseSegmentData(m_pSegment.get(), pPause);
if (m_ProcessingStatus == FXCODEC_STATUS::kDecodeToBeContinued) {
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) {
FX_SAFE_UINT32 new_offset = m_nOffset;
new_offset += m_pSegment->m_dwData_length;
if (!new_offset.IsValid())
return JBig2_Result::kFailure;
m_nOffset = new_offset.ValueOrDie();
m_pStream->setOffset(m_nOffset);
} 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::kDecodeToBeContinued;
m_PauseStep = 2;
return JBig2_Result::kSuccess;
}
}
return JBig2_Result::kSuccess;
}
bool CJBig2_Context::GetFirstPage(pdfium::span<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::kError;
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::kDecodeToBeContinued;
return true;
}
return Continue(pPause);
}
bool CJBig2_Context::Continue(PauseIndicatorIface* pPause) {
m_ProcessingStatus = FXCODEC_STATUS::kDecodeReady;
JBig2_Result nRet = JBig2_Result::kSuccess;
if (m_PauseStep == 5) {
m_ProcessingStatus = FXCODEC_STATUS::kDecodeFinished;
return true;
}
if (m_PauseStep <= 2)
nRet = DecodeSequential(pPause);
if (m_ProcessingStatus == FXCODEC_STATUS::kDecodeToBeContinued)
return nRet == JBig2_Result::kSuccess;
m_PauseStep = 5;
if (!m_bBufSpecified && nRet == JBig2_Result::kSuccess) {
m_ProcessingStatus = FXCODEC_STATUS::kDecodeFinished;
return true;
}
m_ProcessingStatus = nRet == JBig2_Result::kSuccess
? FXCODEC_STATUS::kDecodeFinished
: FXCODEC_STATUS::kError;
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 >
kJBig2MaxReferredSegmentCount) {
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_Key = m_pStream->getKey();
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::kDecodeToBeContinued &&
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: {
uint8_t segment_flags;
uint16_t striping_info;
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(&segment_flags) != 0 ||
m_pStream->readShortInteger(&striping_info) != 0) {
return JBig2_Result::kFailure;
}
pPageInfo->m_bDefaultPixelValue = !!(segment_flags & 4);
pPageInfo->m_bIsStriped = !!(striping_info & 0x8000);
pPageInfo->m_wMaxStripeSize = striping_info & 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::kError;
return JBig2_Result::kFailure;
}
m_pPage->Fill(pPageInfo->m_bDefaultPixelValue);
m_PageInfoList.push_back(std::move(pPageInfo));
m_bInPage = true;
break;
}
case 49:
m_bInPage = false;
return JBig2_Result::kEndReached;
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) {
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 && !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 > kJBig2MaxExportSymbols ||
pSymbolDictDecoder->SDNUMNEWSYMS > kJBig2MaxNewSymbols) {
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;
FX_SAFE_UINT32 dwNumSyms = 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) {
dwNumSyms += pSeg->m_SymbolDict->NumImages();
pLRSeg = pSeg;
}
}
pSymbolDictDecoder->SDNUMINSYMS = dwNumSyms.ValueOrDie();
std::unique_ptr<CJBig2_Image*, FxFreeDeleter> SDINSYMS;
if (pSymbolDictDecoder->SDNUMINSYMS != 0) {
SDINSYMS.reset(FX_Alloc(CJBig2_Image*, pSymbolDictDecoder->SDNUMINSYMS));
dwNumSyms = 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 (uint32_t j = 0; j < dict.NumImages(); ++j) {
uint32_t dwTemp = (dwNumSyms + j).ValueOrDie();
SDINSYMS.get()[dwTemp] = dict.GetImage(j);
}
dwNumSyms += dict.NumImages();
}
}
}
pSymbolDictDecoder->SDINSYMS = SDINSYMS.get();
uint8_t cSDHUFFDH = (wFlags >> 2) & 0x0003;
uint8_t cSDHUFFDW = (wFlags >> 4) & 0x0003;
if (pSymbolDictDecoder->SDHUFF) {
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) {
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;
const bool bUseGrContext = pSymbolDictDecoder->SDREFAGG;
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_CompoundKey key(pSegment->m_Key, 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->emplace_front(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->emplace_front(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 && m_pStream->readShortInteger(&wFlags) != 0) {
return JBig2_Result::kFailure;
}
if (pTRD->SBREFINE && !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;
}
FX_SAFE_UINT32 dwNumSyms = 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) {
dwNumSyms += pSeg->m_SymbolDict->NumImages();
}
}
pTRD->SBNUMSYMS = dwNumSyms.ValueOrDie();
std::unique_ptr<CJBig2_Image*, FxFreeDeleter> SBSYMS;
if (pTRD->SBNUMSYMS > 0) {
SBSYMS.reset(FX_Alloc(CJBig2_Image*, pTRD->SBNUMSYMS));
dwNumSyms = 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 (uint32_t j = 0; j < dict.NumImages(); ++j) {
uint32_t dwIndex = (dwNumSyms + j).ValueOrDie();
SBSYMS.get()[dwIndex] = dict.GetImage(j);
}
dwNumSyms += dict.NumImages();
}
}
pTRD->SBSYMS = SBSYMS.get();
} else {
pTRD->SBSYMS = nullptr;
}
if (pTRD->SBHUFF) {
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) {
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) {
const size_t size = GetRefAggContextSize(pTRD->SBRTEMPLATE);
grContext.reset(FX_Alloc(JBig2ArithCtx, size));
}
pSegment->m_nResultType = JBIG2_IMAGE_POINTER;
if (pTRD->SBHUFF) {
pSegment->m_Image = pTRD->DecodeHuffman(m_pStream.get(), grContext.get());
if (!pSegment->m_Image)
return JBig2_Result::kFailure;
m_pStream->alignByte();
} else {
auto pArithDecoder = std::make_unique<CJBig2_ArithDecoder>(m_pStream.get());
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);
}
if (pSegment->m_cFlags.s.type != 4) {
if (!m_bBufSpecified) {
const auto& pPageInfo = m_PageInfoList.back();
if (pPageInfo->m_bIsStriped && ri.y + ri.height > m_pPage->height())
m_pPage->Expand(ri.y + ri.height, pPageInfo->m_bDefaultPixelValue);
}
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 > kJBig2MaxPatternIndex)
return JBig2_Result::kFailure;
pPDD->HDMMR = cFlags & 0x01;
pPDD->HDTEMPLATE = (cFlags >> 1) & 0x03;
pSegment->m_nResultType = JBIG2_PATTERN_DICT_POINTER;
if (pPDD->HDMMR) {
pSegment->m_PatternDict = pPDD->DecodeMMR(m_pStream.get());
if (!pSegment->m_PatternDict)
return JBig2_Result::kFailure;
m_pStream->alignByte();
} else {
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);
}
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) {
pSegment->m_Image = pHRD->DecodeMMR(m_pStream.get());
if (!pSegment->m_Image)
return JBig2_Result::kFailure;
m_pStream->alignByte();
} else {
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);
}
if (pSegment->m_cFlags.s.type != 20) {
if (!m_bBufSpecified) {
const auto& pPageInfo = m_PageInfoList.back();
if (pPageInfo->m_bIsStriped && ri.y + ri.height > m_pPage->height())
m_pPage->Expand(ri.y + ri.height, pPageInfo->m_bDefaultPixelValue);
}
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) {
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 = false;
m_pGRD = std::move(pGRD);
}
pSegment->m_nResultType = JBIG2_IMAGE_POINTER;
if (m_pGRD->MMR) {
m_pGRD->StartDecodeMMR(&pSegment->m_Image, m_pStream.get());
if (!pSegment->m_Image) {
m_pGRD.reset();
return JBig2_Result::kFailure;
}
m_pStream->alignByte();
} else {
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::kDecodeToBeContinued) {
if (pSegment->m_cFlags.s.type != 36) {
if (!m_bBufSpecified) {
const auto& pPageInfo = m_PageInfoList.back();
if (pPageInfo->m_bIsStriped &&
m_ri.y + m_ri.height > m_pPage->height()) {
m_pPage->Expand(m_ri.y + m_ri.height,
pPageInfo->m_bDefaultPixelValue);
}
}
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::kError;
m_pGRD.reset();
return JBig2_Result::kFailure;
}
m_pStream->alignByte();
m_pStream->offset(2);
}
if (pSegment->m_cFlags.s.type != 36) {
if (!m_bBufSpecified) {
JBig2PageInfo* pPageInfo = m_PageInfoList.back().get();
if (pPageInfo->m_bIsStriped && m_ri.y + m_ri.height > m_pPage->height())
m_pPage->Expand(m_ri.y + m_ri.height, pPageInfo->m_bDefaultPixelValue);
}
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 && ri.y + ri.height > m_pPage->height())
m_pPage->Expand(ri.y + ri.height, pPageInfo->m_bDefaultPixelValue);
}
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) {
DCHECK(idx > 0);
DCHECK(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;
}