blob: 7aa598997282e4e3d870897644a168827e38559b [file] [log] [blame]
// Copyright 2015 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_TrdProc.h"
#include <memory>
#include "core/fxcodec/jbig2/JBig2_ArithDecoder.h"
#include "core/fxcodec/jbig2/JBig2_ArithIntDecoder.h"
#include "core/fxcodec/jbig2/JBig2_GrrdProc.h"
#include "core/fxcodec/jbig2/JBig2_HuffmanDecoder.h"
#include "core/fxcrt/fx_safe_types.h"
#include "core/fxcrt/maybe_owned.h"
#include "third_party/base/optional.h"
#include "third_party/base/ptr_util.h"
namespace {
Optional<uint32_t> CheckTRDDimension(uint32_t dimension, int32_t delta) {
FX_SAFE_UINT32 result = dimension;
result += delta;
if (!result.IsValid())
return {};
return {result.ValueOrDie()};
}
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 {};
return {result.ValueOrDie()};
}
} // namespace
JBig2IntDecoderState::JBig2IntDecoderState() = default;
JBig2IntDecoderState::~JBig2IntDecoderState() = default;
CJBig2_TRDProc::CJBig2_TRDProc() = default;
CJBig2_TRDProc::~CJBig2_TRDProc() = default;
std::unique_ptr<CJBig2_Image> CJBig2_TRDProc::DecodeHuffman(
CJBig2_BitStream* pStream,
JBig2ArithCtx* grContext) {
auto SBREG = pdfium::MakeUnique<CJBig2_Image>(SBW, SBH);
if (!SBREG->data())
return nullptr;
SBREG->Fill(SBDEFPIXEL);
int32_t INITIAL_STRIPT;
auto pHuffmanDecoder = pdfium::MakeUnique<CJBig2_HuffmanDecoder>(pStream);
if (pHuffmanDecoder->DecodeAValue(SBHUFFDT.Get(), &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.Get(), &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.Get(), &DFS) != 0)
return nullptr;
FIRSTS += DFS;
CURS = FIRSTS;
bFirst = false;
} else {
int32_t IDS;
int32_t nVal = pHuffmanDecoder->DecodeAValue(SBHUFFDS.Get(), &IDS);
if (nVal == JBIG2_OOB)
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();
FX_SAFE_INT32 nSafeVal = 0;
int32_t nBits = 0;
uint32_t IDI;
for (;;) {
uint32_t nTmp;
if (pStream->read1Bit(&nTmp) != 0)
return nullptr;
nSafeVal <<= 1;
if (!nSafeVal.IsValid())
return nullptr;
nSafeVal |= nTmp;
++nBits;
const int32_t nVal = nSafeVal.ValueOrDie();
for (IDI = 0; IDI < SBNUMSYMS; ++IDI) {
if (nBits == SBSYMCODES[IDI].codelen && nVal == SBSYMCODES[IDI].code)
break;
}
if (IDI < SBNUMSYMS)
break;
}
bool RI = 0;
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.Get(), &RDWI) != 0) ||
(pHuffmanDecoder->DecodeAValue(SBHUFFRDH.Get(), &RDHI) != 0) ||
(pHuffmanDecoder->DecodeAValue(SBHUFFRDX.Get(), &RDXI) != 0) ||
(pHuffmanDecoder->DecodeAValue(SBHUFFRDY.Get(), &RDYI) != 0) ||
(pHuffmanDecoder->DecodeAValue(SBHUFFRSIZE.Get(), &HUFFRSIZE) !=
0)) {
return nullptr;
}
pStream->alignByte();
uint32_t nTmp = pStream->getOffset();
CJBig2_Image* IBOI = SBSYMS[IDI];
if (!IBOI)
return nullptr;
Optional<uint32_t> WOI = CheckTRDDimension(IBOI->width(), RDWI);
Optional<uint32_t> HOI = CheckTRDDimension(IBOI->height(), RDHI);
if (!WOI || !HOI)
return nullptr;
Optional<int32_t> GRREFERENCEDX =
CheckTRDReferenceDimension(RDWI, 2, RDXI);
Optional<int32_t> GRREFERENCEDY =
CheckTRDReferenceDimension(RDHI, 2, RDYI);
if (!GRREFERENCEDX || !GRREFERENCEDY)
return nullptr;
auto pGRRD = pdfium::MakeUnique<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 = 0;
pGRRD->GRAT[0] = SBRAT[0];
pGRRD->GRAT[1] = SBRAT[1];
pGRRD->GRAT[2] = SBRAT[2];
pGRRD->GRAT[3] = SBRAT[3];
auto pArithDecoder = pdfium::MakeUnique<CJBig2_ArithDecoder>(pStream);
IBI = pGRRD->Decode(pArithDecoder.get(), grContext);
if (!IBI)
return nullptr;
pStream->alignByte();
pStream->offset(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,
JBig2ArithCtx* grContext,
JBig2IntDecoderState* pIDS) {
auto SBREG = pdfium::MakeUnique<CJBig2_Image>(SBW, SBH);
if (!SBREG->data())
return nullptr;
MaybeOwned<CJBig2_ArithIntDecoder> pIADT;
if (pIDS)
pIADT = pIDS->IADT;
else
pIADT = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
int32_t INITIAL_STRIPT;
if (!pIADT->Decode(pArithDecoder, &INITIAL_STRIPT))
return nullptr;
MaybeOwned<CJBig2_ArithIntDecoder> pIAFS;
MaybeOwned<CJBig2_ArithIntDecoder> pIADS;
MaybeOwned<CJBig2_ArithIntDecoder> pIAIT;
MaybeOwned<CJBig2_ArithIntDecoder> pIARI;
MaybeOwned<CJBig2_ArithIntDecoder> pIARDW;
MaybeOwned<CJBig2_ArithIntDecoder> pIARDH;
MaybeOwned<CJBig2_ArithIntDecoder> pIARDX;
MaybeOwned<CJBig2_ArithIntDecoder> pIARDY;
MaybeOwned<CJBig2_ArithIaidDecoder> pIAID;
if (pIDS) {
pIAFS = pIDS->IAFS;
pIADS = pIDS->IADS;
pIAIT = pIDS->IAIT;
pIARI = pIDS->IARI;
pIARDW = pIDS->IARDW;
pIARDH = pIDS->IARDH;
pIARDX = pIDS->IARDX;
pIARDY = pIDS->IARDY;
pIAID = pIDS->IAID;
} else {
pIAFS = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
pIADS = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
pIAIT = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
pIARI = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
pIARDW = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
pIARDH = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
pIARDX = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
pIARDY = pdfium::MakeUnique<CJBig2_ArithIntDecoder>();
pIAID = pdfium::MakeUnique<CJBig2_ArithIaidDecoder>(SBSYMCODELEN);
}
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;
Optional<uint32_t> WOI = CheckTRDDimension(IBOI->width(), RDWI);
Optional<uint32_t> HOI = CheckTRDDimension(IBOI->height(), RDHI);
if (!WOI || !HOI)
return nullptr;
Optional<int32_t> GRREFERENCEDX =
CheckTRDReferenceDimension(RDWI, 1, RDXI);
Optional<int32_t> GRREFERENCEDY =
CheckTRDReferenceDimension(RDHI, 1, RDYI);
if (!GRREFERENCEDX || !GRREFERENCEDY)
return nullptr;
auto pGRRD = pdfium::MakeUnique<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 = 0;
pGRRD->GRAT[0] = SBRAT[0];
pGRRD->GRAT[1] = SBRAT[1];
pGRRD->GRAT[2] = SBRAT[2];
pGRRD->GRAT[3] = SBRAT[3];
pIBI = pGRRD->Decode(pArithDecoder, grContext);
}
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;
}