| // Copyright 2015 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 | |
| #include "core/fxcodec/jbig2/jbig2_trd_proc.h" | |
| #include <memory> | |
| #include <optional> | |
| #include "core/fxcodec/jbig2/jbig2_arith_decoder.h" | |
| #include "core/fxcodec/jbig2/jbig2_arith_int_decoder.h" | |
| #include "core/fxcodec/jbig2/jbig2_grrd_proc.h" | |
| #include "core/fxcodec/jbig2/jbig2_huffman_decoder.h" | |
| #include "core/fxcrt/fx_safe_types.h" | |
| #include "core/fxcrt/maybe_owned.h" | |
| namespace { | |
| std::optional<uint32_t> CheckTRDDimension(uint32_t dimension, int32_t delta) { | |
| FX_SAFE_UINT32 result = dimension; | |
| result += delta; | |
| if (!result.IsValid()) { | |
| return std::nullopt; | |
| } | |
| return result.ValueOrDie(); | |
| } | |
| std::optional<int32_t> CheckTRDReferenceDimension(int32_t dimension, | |
| uint32_t shift, | |
| int32_t offset) { | |
| FX_SAFE_INT32 result = offset; | |
| result += dimension >> shift; | |
| if (!result.IsValid()) { | |
| return std::nullopt; | |
| } | |
| return result.ValueOrDie(); | |
| } | |
| } // namespace | |
| JBig2IntDecoderState::JBig2IntDecoderState(uint8_t SBSYMCODELEN) | |
| : IADT(std::make_unique<CJBig2_ArithIntDecoder>()), | |
| IAFS(std::make_unique<CJBig2_ArithIntDecoder>()), | |
| IADS(std::make_unique<CJBig2_ArithIntDecoder>()), | |
| IAIT(std::make_unique<CJBig2_ArithIntDecoder>()), | |
| IARI(std::make_unique<CJBig2_ArithIntDecoder>()), | |
| IARDW(std::make_unique<CJBig2_ArithIntDecoder>()), | |
| IARDH(std::make_unique<CJBig2_ArithIntDecoder>()), | |
| IARDX(std::make_unique<CJBig2_ArithIntDecoder>()), | |
| IARDY(std::make_unique<CJBig2_ArithIntDecoder>()), | |
| IAID(std::make_unique<CJBig2_ArithIaidDecoder>(SBSYMCODELEN)) {} | |
| JBig2IntDecoderState::~JBig2IntDecoderState() = default; | |
| CJBig2_TRDProc::CJBig2_TRDProc() = default; | |
| CJBig2_TRDProc::~CJBig2_TRDProc() = default; | |
| std::unique_ptr<CJBig2_Image> CJBig2_TRDProc::DecodeHuffman( | |
| CJBig2_BitStream* pStream, | |
| pdfium::span<JBig2ArithCtx> grContexts) { | |
| auto SBREG = std::make_unique<CJBig2_Image>(SBW, SBH); | |
| if (!SBREG->has_data()) { | |
| return nullptr; | |
| } | |
| SBREG->Fill(SBDEFPIXEL); | |
| int32_t INITIAL_STRIPT; | |
| auto pHuffmanDecoder = std::make_unique<CJBig2_HuffmanDecoder>(pStream); | |
| if (pHuffmanDecoder->DecodeAValue(SBHUFFDT, &INITIAL_STRIPT) != 0) { | |
| return nullptr; | |
| } | |
| FX_SAFE_INT32 STRIPT = INITIAL_STRIPT; | |
| STRIPT *= SBSTRIPS; | |
| STRIPT = -STRIPT; | |
| FX_SAFE_INT32 FIRSTS = 0; | |
| uint32_t NINSTANCES = 0; | |
| while (NINSTANCES < SBNUMINSTANCES) { | |
| int32_t INITIAL_DT; | |
| if (pHuffmanDecoder->DecodeAValue(SBHUFFDT, &INITIAL_DT) != 0) { | |
| return nullptr; | |
| } | |
| FX_SAFE_INT32 DT = INITIAL_DT; | |
| DT *= SBSTRIPS; | |
| STRIPT += DT; | |
| bool bFirst = true; | |
| FX_SAFE_INT32 CURS = 0; | |
| for (;;) { | |
| if (bFirst) { | |
| int32_t DFS; | |
| if (pHuffmanDecoder->DecodeAValue(SBHUFFFS, &DFS) != 0) { | |
| return nullptr; | |
| } | |
| FIRSTS += DFS; | |
| CURS = FIRSTS; | |
| bFirst = false; | |
| } else { | |
| int32_t IDS; | |
| int32_t nVal = pHuffmanDecoder->DecodeAValue(SBHUFFDS, &IDS); | |
| if (nVal == kJBig2OOB) { | |
| break; | |
| } | |
| if (nVal != 0) { | |
| return nullptr; | |
| } | |
| CURS += IDS; | |
| CURS += SBDSOFFSET; | |
| } | |
| uint8_t CURT = 0; | |
| if (SBSTRIPS != 1) { | |
| uint32_t nTmp = 1; | |
| while (static_cast<uint32_t>(1 << nTmp) < SBSTRIPS) { | |
| ++nTmp; | |
| } | |
| int32_t nVal; | |
| if (pStream->readNBits(nTmp, &nVal) != 0) { | |
| return nullptr; | |
| } | |
| CURT = nVal; | |
| } | |
| FX_SAFE_INT32 SAFE_TI = STRIPT + CURT; | |
| if (!SAFE_TI.IsValid()) { | |
| return nullptr; | |
| } | |
| int32_t TI = SAFE_TI.ValueOrDie(); | |
| int32_t IDI; | |
| if (pHuffmanDecoder->DecodeAValue(&SBSYMCODES, &IDI) != 0) { | |
| return nullptr; | |
| } | |
| bool RI = false; | |
| if (SBREFINE != 0 && pStream->read1Bit(&RI) != 0) { | |
| return nullptr; | |
| } | |
| MaybeOwned<CJBig2_Image> IBI; | |
| if (RI == 0) { | |
| IBI = SBSYMS[IDI]; | |
| } else { | |
| int32_t RDWI; | |
| int32_t RDHI; | |
| int32_t RDXI; | |
| int32_t RDYI; | |
| int32_t HUFFRSIZE; | |
| if ((pHuffmanDecoder->DecodeAValue(SBHUFFRDW, &RDWI) != 0) || | |
| (pHuffmanDecoder->DecodeAValue(SBHUFFRDH, &RDHI) != 0) || | |
| (pHuffmanDecoder->DecodeAValue(SBHUFFRDX, &RDXI) != 0) || | |
| (pHuffmanDecoder->DecodeAValue(SBHUFFRDY, &RDYI) != 0) || | |
| (pHuffmanDecoder->DecodeAValue(SBHUFFRSIZE, &HUFFRSIZE) != 0)) { | |
| return nullptr; | |
| } | |
| pStream->alignByte(); | |
| uint32_t nTmp = pStream->getOffset(); | |
| CJBig2_Image* IBOI = SBSYMS[IDI]; | |
| if (!IBOI) { | |
| return nullptr; | |
| } | |
| std::optional<uint32_t> WOI = CheckTRDDimension(IBOI->width(), RDWI); | |
| std::optional<uint32_t> HOI = CheckTRDDimension(IBOI->height(), RDHI); | |
| if (!WOI.has_value() || !HOI.has_value()) { | |
| return nullptr; | |
| } | |
| std::optional<int32_t> GRREFERENCEDX = | |
| CheckTRDReferenceDimension(RDWI, 2, RDXI); | |
| std::optional<int32_t> GRREFERENCEDY = | |
| CheckTRDReferenceDimension(RDHI, 2, RDYI); | |
| if (!GRREFERENCEDX.has_value() || !GRREFERENCEDY.has_value()) { | |
| return nullptr; | |
| } | |
| auto pGRRD = std::make_unique<CJBig2_GRRDProc>(); | |
| pGRRD->GRW = WOI.value(); | |
| pGRRD->GRH = HOI.value(); | |
| pGRRD->GRTEMPLATE = SBRTEMPLATE; | |
| pGRRD->GRREFERENCE = IBOI; | |
| pGRRD->GRREFERENCEDX = GRREFERENCEDX.value(); | |
| pGRRD->GRREFERENCEDY = GRREFERENCEDY.value(); | |
| pGRRD->TPGRON = false; | |
| pGRRD->GRAT[0] = SBRAT[0]; | |
| pGRRD->GRAT[1] = SBRAT[1]; | |
| pGRRD->GRAT[2] = SBRAT[2]; | |
| pGRRD->GRAT[3] = SBRAT[3]; | |
| auto pArithDecoder = std::make_unique<CJBig2_ArithDecoder>(pStream); | |
| IBI = pGRRD->Decode(pArithDecoder.get(), grContexts); | |
| if (!IBI) { | |
| return nullptr; | |
| } | |
| pStream->alignByte(); | |
| pStream->addOffset(2); | |
| if (static_cast<uint32_t>(HUFFRSIZE) != (pStream->getOffset() - nTmp)) { | |
| return nullptr; | |
| } | |
| } | |
| if (!IBI) { | |
| continue; | |
| } | |
| uint32_t WI = IBI->width(); | |
| uint32_t HI = IBI->height(); | |
| if (TRANSPOSED == 0 && ((REFCORNER == JBIG2_CORNER_TOPRIGHT) || | |
| (REFCORNER == JBIG2_CORNER_BOTTOMRIGHT))) { | |
| CURS += WI - 1; | |
| } else if (TRANSPOSED == 1 && ((REFCORNER == JBIG2_CORNER_BOTTOMLEFT) || | |
| (REFCORNER == JBIG2_CORNER_BOTTOMRIGHT))) { | |
| CURS += HI - 1; | |
| } | |
| if (!CURS.IsValid()) { | |
| return nullptr; | |
| } | |
| int32_t SI = CURS.ValueOrDie(); | |
| ComposeData compose = GetComposeData(SI, TI, WI, HI); | |
| IBI.Get()->ComposeTo(SBREG.get(), compose.x, compose.y, SBCOMBOP); | |
| if (compose.increment) { | |
| CURS += compose.increment; | |
| } | |
| ++NINSTANCES; | |
| } | |
| } | |
| return SBREG; | |
| } | |
| std::unique_ptr<CJBig2_Image> CJBig2_TRDProc::DecodeArith( | |
| CJBig2_ArithDecoder* pArithDecoder, | |
| pdfium::span<JBig2ArithCtx> grContexts, | |
| JBig2IntDecoderState& state) { | |
| auto SBREG = std::make_unique<CJBig2_Image>(SBW, SBH); | |
| if (!SBREG->has_data()) { | |
| return nullptr; | |
| } | |
| CJBig2_ArithIntDecoder* pIADT = state.IADT.get(); | |
| int32_t INITIAL_STRIPT; | |
| if (!pIADT->Decode(pArithDecoder, &INITIAL_STRIPT)) { | |
| return nullptr; | |
| } | |
| CJBig2_ArithIntDecoder* pIAFS = state.IAFS.get(); | |
| CJBig2_ArithIntDecoder* pIADS = state.IADS.get(); | |
| CJBig2_ArithIntDecoder* pIAIT = state.IAIT.get(); | |
| CJBig2_ArithIntDecoder* pIARI = state.IARI.get(); | |
| CJBig2_ArithIntDecoder* pIARDW = state.IARDW.get(); | |
| CJBig2_ArithIntDecoder* pIARDH = state.IARDH.get(); | |
| CJBig2_ArithIntDecoder* pIARDX = state.IARDX.get(); | |
| CJBig2_ArithIntDecoder* pIARDY = state.IARDY.get(); | |
| CJBig2_ArithIaidDecoder* pIAID = state.IAID.get(); | |
| SBREG->Fill(SBDEFPIXEL); | |
| FX_SAFE_INT32 STRIPT = INITIAL_STRIPT; | |
| STRIPT *= SBSTRIPS; | |
| STRIPT = -STRIPT; | |
| FX_SAFE_INT32 FIRSTS = 0; | |
| uint32_t NINSTANCES = 0; | |
| while (NINSTANCES < SBNUMINSTANCES) { | |
| FX_SAFE_INT32 CURS = 0; | |
| int32_t INITIAL_DT; | |
| if (!pIADT->Decode(pArithDecoder, &INITIAL_DT)) { | |
| return nullptr; | |
| } | |
| FX_SAFE_INT32 DT = INITIAL_DT; | |
| DT *= SBSTRIPS; | |
| STRIPT += DT; | |
| bool bFirst = true; | |
| for (;;) { | |
| if (bFirst) { | |
| int32_t DFS; | |
| pIAFS->Decode(pArithDecoder, &DFS); | |
| FIRSTS += DFS; | |
| CURS = FIRSTS; | |
| bFirst = false; | |
| } else { | |
| int32_t IDS; | |
| if (!pIADS->Decode(pArithDecoder, &IDS)) { | |
| break; | |
| } | |
| CURS += IDS; | |
| CURS += SBDSOFFSET; | |
| } | |
| if (NINSTANCES >= SBNUMINSTANCES) { | |
| break; | |
| } | |
| int CURT = 0; | |
| if (SBSTRIPS != 1) { | |
| pIAIT->Decode(pArithDecoder, &CURT); | |
| } | |
| FX_SAFE_INT32 SAFE_TI = STRIPT + CURT; | |
| if (!SAFE_TI.IsValid()) { | |
| return nullptr; | |
| } | |
| int32_t TI = SAFE_TI.ValueOrDie(); | |
| uint32_t IDI; | |
| pIAID->Decode(pArithDecoder, &IDI); | |
| if (IDI >= SBNUMSYMS) { | |
| return nullptr; | |
| } | |
| int RI; | |
| if (SBREFINE == 0) { | |
| RI = 0; | |
| } else { | |
| pIARI->Decode(pArithDecoder, &RI); | |
| } | |
| MaybeOwned<CJBig2_Image> pIBI; | |
| if (RI == 0) { | |
| pIBI = SBSYMS[IDI]; | |
| } else { | |
| int32_t RDWI; | |
| int32_t RDHI; | |
| int32_t RDXI; | |
| int32_t RDYI; | |
| pIARDW->Decode(pArithDecoder, &RDWI); | |
| pIARDH->Decode(pArithDecoder, &RDHI); | |
| pIARDX->Decode(pArithDecoder, &RDXI); | |
| pIARDY->Decode(pArithDecoder, &RDYI); | |
| CJBig2_Image* IBOI = SBSYMS[IDI]; | |
| if (!IBOI) { | |
| return nullptr; | |
| } | |
| std::optional<uint32_t> WOI = CheckTRDDimension(IBOI->width(), RDWI); | |
| std::optional<uint32_t> HOI = CheckTRDDimension(IBOI->height(), RDHI); | |
| if (!WOI.has_value() || !HOI.has_value()) { | |
| return nullptr; | |
| } | |
| std::optional<int32_t> GRREFERENCEDX = | |
| CheckTRDReferenceDimension(RDWI, 1, RDXI); | |
| std::optional<int32_t> GRREFERENCEDY = | |
| CheckTRDReferenceDimension(RDHI, 1, RDYI); | |
| if (!GRREFERENCEDX.has_value() || !GRREFERENCEDY.has_value()) { | |
| return nullptr; | |
| } | |
| auto pGRRD = std::make_unique<CJBig2_GRRDProc>(); | |
| pGRRD->GRW = WOI.value(); | |
| pGRRD->GRH = HOI.value(); | |
| pGRRD->GRTEMPLATE = SBRTEMPLATE; | |
| pGRRD->GRREFERENCE = IBOI; | |
| pGRRD->GRREFERENCEDX = GRREFERENCEDX.value(); | |
| pGRRD->GRREFERENCEDY = GRREFERENCEDY.value(); | |
| pGRRD->TPGRON = false; | |
| pGRRD->GRAT[0] = SBRAT[0]; | |
| pGRRD->GRAT[1] = SBRAT[1]; | |
| pGRRD->GRAT[2] = SBRAT[2]; | |
| pGRRD->GRAT[3] = SBRAT[3]; | |
| pIBI = pGRRD->Decode(pArithDecoder, grContexts); | |
| } | |
| if (!pIBI) { | |
| return nullptr; | |
| } | |
| uint32_t WI = pIBI->width(); | |
| uint32_t HI = pIBI->height(); | |
| if (TRANSPOSED == 0 && ((REFCORNER == JBIG2_CORNER_TOPRIGHT) || | |
| (REFCORNER == JBIG2_CORNER_BOTTOMRIGHT))) { | |
| CURS += WI - 1; | |
| } else if (TRANSPOSED == 1 && ((REFCORNER == JBIG2_CORNER_BOTTOMLEFT) || | |
| (REFCORNER == JBIG2_CORNER_BOTTOMRIGHT))) { | |
| CURS += HI - 1; | |
| } | |
| if (!CURS.IsValid()) { | |
| return nullptr; | |
| } | |
| int32_t SI = CURS.ValueOrDie(); | |
| ComposeData compose = GetComposeData(SI, TI, WI, HI); | |
| pIBI.Get()->ComposeTo(SBREG.get(), compose.x, compose.y, SBCOMBOP); | |
| if (compose.increment) { | |
| CURS += compose.increment; | |
| } | |
| ++NINSTANCES; | |
| } | |
| } | |
| return SBREG; | |
| } | |
| CJBig2_TRDProc::ComposeData CJBig2_TRDProc::GetComposeData(int32_t SI, | |
| int32_t TI, | |
| uint32_t WI, | |
| uint32_t HI) const { | |
| ComposeData results; | |
| if (TRANSPOSED == 0) { | |
| switch (REFCORNER) { | |
| case JBIG2_CORNER_TOPLEFT: | |
| results.x = SI; | |
| results.y = TI; | |
| results.increment = WI - 1; | |
| break; | |
| case JBIG2_CORNER_TOPRIGHT: | |
| results.x = SI - WI + 1; | |
| results.y = TI; | |
| break; | |
| case JBIG2_CORNER_BOTTOMLEFT: | |
| results.x = SI; | |
| results.y = TI - HI + 1; | |
| results.increment = WI - 1; | |
| break; | |
| case JBIG2_CORNER_BOTTOMRIGHT: | |
| results.x = SI - WI + 1; | |
| results.y = TI - HI + 1; | |
| break; | |
| } | |
| } else { | |
| switch (REFCORNER) { | |
| case JBIG2_CORNER_TOPLEFT: | |
| results.x = TI; | |
| results.y = SI; | |
| results.increment = HI - 1; | |
| break; | |
| case JBIG2_CORNER_TOPRIGHT: | |
| results.x = TI - WI + 1; | |
| results.y = SI; | |
| results.increment = HI - 1; | |
| break; | |
| case JBIG2_CORNER_BOTTOMLEFT: | |
| results.x = TI; | |
| results.y = SI - HI + 1; | |
| break; | |
| case JBIG2_CORNER_BOTTOMRIGHT: | |
| results.x = TI - WI + 1; | |
| results.y = SI - HI + 1; | |
| break; | |
| } | |
| } | |
| return results; | |
| } |