blob: b34f0062aab5b67dbfad357a0ec1b1fbf03e22a9 [file] [log] [blame] [edit]
// 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_GrdProc.h"
#include <array>
#include <functional>
#include <memory>
#include <utility>
#include "core/fxcodec/fax/faxmodule.h"
#include "core/fxcodec/jbig2/JBig2_ArithDecoder.h"
#include "core/fxcodec/jbig2/JBig2_BitStream.h"
#include "core/fxcodec/jbig2/JBig2_Image.h"
#include "core/fxcrt/check_op.h"
#include "core/fxcrt/pauseindicator_iface.h"
#include "core/fxcrt/zip.h"
namespace {
// TODO(npm): Name this constants better or merge some together.
constexpr std::array<const uint16_t, 3> kOptConstant1 = {
{0x9b25, 0x0795, 0x00e5}};
constexpr std::array<const uint16_t, 3> kOptConstant2 = {
{0x0006, 0x0004, 0x0001}};
constexpr std::array<const uint16_t, 3> kOptConstant3 = {
{0xf800, 0x1e00, 0x0380}};
constexpr std::array<const uint16_t, 3> kOptConstant4 = {
{0x0000, 0x0001, 0x0003}};
constexpr std::array<const uint16_t, 3> kOptConstant5 = {
{0x07f0, 0x01f8, 0x007c}};
constexpr std::array<const uint16_t, 3> kOptConstant6 = {
{0x7bf7, 0x0efb, 0x01bd}};
constexpr std::array<const uint16_t, 3> kOptConstant7 = {
{0x0800, 0x0200, 0x0080}};
constexpr std::array<const uint16_t, 3> kOptConstant8 = {
{0x0010, 0x0008, 0x0004}};
constexpr std::array<const uint16_t, 3> kOptConstant9 = {
{0x000c, 0x0009, 0x0007}};
constexpr std::array<const uint16_t, 3> kOptConstant10 = {
{0x0007, 0x000f, 0x0007}};
constexpr std::array<const uint16_t, 3> kOptConstant11 = {
{0x001f, 0x001f, 0x000f}};
constexpr std::array<const uint16_t, 3> kOptConstant12 = {
{0x000f, 0x0007, 0x0003}};
struct LineLayout {
uint32_t full_bytes;
// Range of values is [1, 8].
uint32_t last_byte_bits;
};
LineLayout GetLineLayout(uint32_t width) {
CHECK_GT(width, 0u);
// Note that this deliberately represents 8 bits as:
// full_bytes = 0 and last_byte_bits = 8.
return {.full_bytes = (width - 1) / 8,
.last_byte_bits = ((width - 1) & 7) + 1};
}
} // namespace
CJBig2_GRDProc::ProgressiveArithDecodeState::ProgressiveArithDecodeState() =
default;
CJBig2_GRDProc::ProgressiveArithDecodeState::~ProgressiveArithDecodeState() =
default;
CJBig2_GRDProc::CJBig2_GRDProc() = default;
CJBig2_GRDProc::~CJBig2_GRDProc() = default;
bool CJBig2_GRDProc::UseTemplate0Opt3() const {
return (GBAT[0] == 3) && (GBAT[1] == -1) && (GBAT[2] == -3) &&
(GBAT[3] == -1) && (GBAT[4] == 2) && (GBAT[5] == -2) &&
(GBAT[6] == -2) && (GBAT[7] == -2) && !USESKIP;
}
bool CJBig2_GRDProc::UseTemplate1Opt3() const {
return (GBAT[0] == 3) && (GBAT[1] == -1) && !USESKIP;
}
bool CJBig2_GRDProc::UseTemplate23Opt3() const {
return (GBAT[0] == 2) && (GBAT[1] == -1) && !USESKIP;
}
std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::DecodeArith(
CJBig2_ArithDecoder* pArithDecoder,
pdfium::span<JBig2ArithCtx> gbContexts) {
if (!CJBig2_Image::IsValidImageSize(GBW, GBH)) {
return std::make_unique<CJBig2_Image>(GBW, GBH);
}
switch (GBTEMPLATE) {
case 0:
return UseTemplate0Opt3()
? DecodeArithOpt3(pArithDecoder, gbContexts, 0)
: DecodeArithTemplateUnopt(pArithDecoder, gbContexts, 0);
case 1:
return UseTemplate1Opt3()
? DecodeArithOpt3(pArithDecoder, gbContexts, 1)
: DecodeArithTemplateUnopt(pArithDecoder, gbContexts, 1);
case 2:
return UseTemplate23Opt3()
? DecodeArithOpt3(pArithDecoder, gbContexts, 2)
: DecodeArithTemplateUnopt(pArithDecoder, gbContexts, 2);
default:
return UseTemplate23Opt3()
? DecodeArithTemplate3Opt3(pArithDecoder, gbContexts)
: DecodeArithTemplate3Unopt(pArithDecoder, gbContexts);
}
}
std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::DecodeArithOpt3(
CJBig2_ArithDecoder* pArithDecoder,
pdfium::span<JBig2ArithCtx> gbContexts,
int OPT) {
auto GBREG = std::make_unique<CJBig2_Image>(GBW, GBH);
if (!GBREG->has_data()) {
return nullptr;
}
int LTP = 0;
const LineLayout layout = GetLineLayout(GBW);
// TODO(npm): Why is the height only trimmed when OPT is 0?
const uint32_t height = OPT == 0 ? GBH & 0x7fffffff : GBH;
pdfium::span<uint8_t> row_write;
pdfium::span<const uint8_t> row_prev1;
pdfium::span<const uint8_t> row_prev2;
for (uint32_t h = 0; h < height; ++h) {
row_prev2 = row_prev1;
row_prev1 = row_write;
row_write = GBREG->GetLine(h);
if (TPGDON) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
LTP = LTP ^ pArithDecoder->Decode(&gbContexts[kOptConstant1[OPT]]);
if (LTP) {
GBREG->CopyLine(row_write, row_prev1);
continue;
}
}
if (h < 2) {
const bool is_second_line = h == 1;
uint32_t val_prev = 0;
if (is_second_line) {
val_prev = row_prev1[0];
}
uint32_t CONTEXT =
((val_prev >> kOptConstant4[OPT]) & kOptConstant5[OPT]);
for (uint32_t cc = 0; cc < layout.full_bytes; ++cc) {
if (is_second_line) {
val_prev = (val_prev << 8) | row_prev1[cc + 1];
}
uint8_t cVal = 0;
for (int32_t k = 7; k >= 0; --k) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
int bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
cVal |= bVal << k;
CONTEXT =
(((CONTEXT & kOptConstant6[OPT]) << 1) | bVal |
((val_prev >> (k + kOptConstant4[OPT])) & kOptConstant8[OPT]));
}
row_write[cc] = cVal;
}
val_prev <<= 8;
uint8_t cVal1 = 0;
for (uint32_t k = 0; k < layout.last_byte_bits; ++k) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
int bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
cVal1 |= bVal << (7 - k);
CONTEXT = (((CONTEXT & kOptConstant6[OPT]) << 1) | bVal |
(((val_prev >> (7 + kOptConstant4[OPT] - k))) &
kOptConstant8[OPT]));
}
row_write[layout.full_bytes] = cVal1;
continue;
}
uint32_t val_prev2 = row_prev2[0] << kOptConstant2[OPT];
uint32_t val_prev1 = row_prev1[0];
uint32_t CONTEXT = (val_prev2 & kOptConstant3[OPT]) |
((val_prev1 >> kOptConstant4[OPT]) & kOptConstant5[OPT]);
auto row_write_zip = row_write.first(layout.full_bytes);
auto row_prev1_zip = row_prev1.subspan<1u>();
auto row_prev2_zip = row_prev2.subspan<1u>();
for (auto [elem_write, elem_prev1, elem_prev2] :
fxcrt::Zip(row_write_zip, row_prev1_zip, row_prev2_zip)) {
val_prev2 = (val_prev2 << 8) | (elem_prev2 << kOptConstant2[OPT]);
val_prev1 = (val_prev1 << 8) | elem_prev1;
uint8_t cVal = 0;
for (int32_t k = 7; k >= 0; --k) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
int bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
cVal |= bVal << k;
CONTEXT =
(((CONTEXT & kOptConstant6[OPT]) << 1) | bVal |
((val_prev2 >> k) & kOptConstant7[OPT]) |
((val_prev1 >> (k + kOptConstant4[OPT])) & kOptConstant8[OPT]));
}
elem_write = cVal;
}
val_prev2 <<= 8;
val_prev1 <<= 8;
uint8_t cVal1 = 0;
for (uint32_t k = 0; k < layout.last_byte_bits; ++k) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
int bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
cVal1 |= bVal << (7 - k);
CONTEXT =
(((CONTEXT & kOptConstant6[OPT]) << 1) | bVal |
((val_prev2 >> (7 - k)) & kOptConstant7[OPT]) |
((val_prev1 >> (7 + kOptConstant4[OPT] - k)) & kOptConstant8[OPT]));
}
row_write[layout.full_bytes] = cVal1;
}
return GBREG;
}
std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::DecodeArithTemplateUnopt(
CJBig2_ArithDecoder* pArithDecoder,
pdfium::span<JBig2ArithCtx> gbContexts,
int UNOPT) {
auto GBREG = std::make_unique<CJBig2_Image>(GBW, GBH);
if (!GBREG->has_data()) {
return nullptr;
}
GBREG->Fill(false);
int LTP = 0;
const uint8_t MOD2 = UNOPT % 2;
const uint8_t DIV2 = UNOPT / 2;
const uint8_t SHIFT = 4 - UNOPT;
pdfium::span<uint8_t> row_write;
pdfium::span<const uint8_t> row_prev1;
pdfium::span<const uint8_t> row_prev2;
for (uint32_t h = 0; h < GBH; h++) {
row_prev2 = row_prev1;
row_prev1 = row_write;
row_write = GBREG->GetLine(h);
if (TPGDON) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
LTP = LTP ^ pArithDecoder->Decode(&gbContexts[kOptConstant1[UNOPT]]);
if (LTP) {
GBREG->CopyLine(row_write, row_prev1);
continue;
}
}
pdfium::span<const uint8_t> row_skip;
if (USESKIP) {
row_skip = SKIP->GetLine(h);
}
pdfium::span<const uint8_t> row_gbat0 = GBREG->GetLine(h + GBAT[1]);
pdfium::span<const uint8_t> row_gbat1;
pdfium::span<const uint8_t> row_gbat2;
pdfium::span<const uint8_t> row_gbat3;
if (UNOPT == 0) {
row_gbat1 = GBREG->GetLine(h + GBAT[3]);
row_gbat2 = GBREG->GetLine(h + GBAT[5]);
row_gbat3 = GBREG->GetLine(h + GBAT[7]);
}
uint32_t val_prev2 = GBREG->GetPixel(1 + MOD2, row_prev2);
val_prev2 |= GBREG->GetPixel(MOD2, row_prev2) << 1;
if (UNOPT == 1) {
val_prev2 |= GBREG->GetPixel(0, row_prev2) << 2;
}
uint32_t val_prev1 = GBREG->GetPixel(2 - DIV2, row_prev1);
val_prev1 |= GBREG->GetPixel(1 - DIV2, row_prev1) << 1;
if (UNOPT < 2) {
val_prev1 |= GBREG->GetPixel(0, row_prev1) << 2;
}
uint32_t val_current = 0;
for (uint32_t w = 0; w < GBW; w++) {
int bVal = 0;
if (!USESKIP || !SKIP->GetPixel(w, row_skip)) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
uint32_t CONTEXT = val_current;
CONTEXT |= GBREG->GetPixel(w + GBAT[0], row_gbat0) << SHIFT;
CONTEXT |= val_prev1 << (SHIFT + 1);
CONTEXT |= val_prev2 << kOptConstant9[UNOPT];
if (UNOPT == 0) {
CONTEXT |= GBREG->GetPixel(w + GBAT[2], row_gbat1) << 10;
CONTEXT |= GBREG->GetPixel(w + GBAT[4], row_gbat2) << 11;
CONTEXT |= GBREG->GetPixel(w + GBAT[6], row_gbat3) << 15;
}
bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
if (bVal) {
GBREG->SetPixel(w, row_write, bVal);
}
}
val_prev2 =
((val_prev2 << 1) | GBREG->GetPixel(w + 2 + MOD2, row_prev2)) &
kOptConstant10[UNOPT];
val_prev1 =
((val_prev1 << 1) | GBREG->GetPixel(w + 3 - DIV2, row_prev1)) &
kOptConstant11[UNOPT];
val_current = ((val_current << 1) | bVal) & kOptConstant12[UNOPT];
}
}
return GBREG;
}
std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::DecodeArithTemplate3Opt3(
CJBig2_ArithDecoder* pArithDecoder,
pdfium::span<JBig2ArithCtx> gbContexts) {
auto GBREG = std::make_unique<CJBig2_Image>(GBW, GBH);
if (!GBREG->has_data()) {
return nullptr;
}
int LTP = 0;
const LineLayout layout = GetLineLayout(GBW);
pdfium::span<uint8_t> row_write;
pdfium::span<const uint8_t> row_prev;
for (uint32_t h = 0; h < GBH; h++) {
row_prev = row_write;
row_write = GBREG->GetLine(h);
if (TPGDON) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
LTP = LTP ^ pArithDecoder->Decode(&gbContexts[0x0195]);
if (LTP) {
GBREG->CopyLine(row_write, row_prev);
continue;
}
}
if (h == 0) {
uint32_t CONTEXT = 0;
for (uint32_t cc = 0; cc < layout.full_bytes; cc++) {
uint8_t cVal = 0;
for (int32_t k = 7; k >= 0; k--) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
int bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
cVal |= bVal << k;
CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal;
}
row_write[cc] = cVal;
}
uint8_t cVal1 = 0;
for (uint32_t k = 0; k < layout.last_byte_bits; k++) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
int bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
cVal1 |= bVal << (7 - k);
CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal;
}
row_write[layout.full_bytes] = cVal1;
continue;
}
uint32_t val_prev = row_prev[0];
uint32_t CONTEXT = (val_prev >> 1) & 0x03f0;
for (uint32_t cc = 0; cc < layout.full_bytes; cc++) {
val_prev = (val_prev << 8) | row_prev[cc + 1];
uint8_t cVal = 0;
for (int32_t k = 7; k >= 0; k--) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
int bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
cVal |= bVal << k;
CONTEXT =
((CONTEXT & 0x01f7) << 1) | bVal | ((val_prev >> (k + 1)) & 0x0010);
}
row_write[cc] = cVal;
}
val_prev <<= 8;
uint8_t cVal1 = 0;
for (uint32_t k = 0; k < layout.last_byte_bits; k++) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
int bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
cVal1 |= bVal << (7 - k);
CONTEXT =
((CONTEXT & 0x01f7) << 1) | bVal | ((val_prev >> (8 - k)) & 0x0010);
}
row_write[layout.full_bytes] = cVal1;
}
return GBREG;
}
std::unique_ptr<CJBig2_Image> CJBig2_GRDProc::DecodeArithTemplate3Unopt(
CJBig2_ArithDecoder* pArithDecoder,
pdfium::span<JBig2ArithCtx> gbContexts) {
auto GBREG = std::make_unique<CJBig2_Image>(GBW, GBH);
if (!GBREG->has_data()) {
return nullptr;
}
GBREG->Fill(false);
int LTP = 0;
pdfium::span<uint8_t> row_write;
pdfium::span<const uint8_t> row_prev;
for (uint32_t h = 0; h < GBH; h++) {
row_prev = row_write;
row_write = GBREG->GetLine(h);
if (TPGDON) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
LTP = LTP ^ pArithDecoder->Decode(&gbContexts[0x0195]);
if (LTP) {
GBREG->CopyLine(row_write, row_prev);
continue;
}
}
pdfium::span<const uint8_t> row_skip;
if (USESKIP) {
row_skip = SKIP->GetLine(h);
}
pdfium::span<const uint8_t> row_gbat = GBREG->GetLine(h + GBAT[1]);
uint32_t val_prev = GBREG->GetPixel(1, row_prev);
val_prev |= GBREG->GetPixel(0, row_prev) << 1;
uint32_t val_current = 0;
for (uint32_t w = 0; w < GBW; w++) {
int bVal = 0;
if (!USESKIP || !SKIP->GetPixel(w, row_skip)) {
uint32_t CONTEXT = val_current;
CONTEXT |= GBREG->GetPixel(w + GBAT[0], row_gbat) << 4;
CONTEXT |= val_prev << 5;
if (pArithDecoder->IsComplete()) {
return nullptr;
}
bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
}
if (bVal) {
GBREG->SetPixel(w, row_write, bVal);
}
val_prev = ((val_prev << 1) | GBREG->GetPixel(w + 2, row_prev)) & 0x1f;
val_current = ((val_current << 1) | bVal) & 0x0f;
}
}
return GBREG;
}
FXCODEC_STATUS CJBig2_GRDProc::StartDecodeArith(
ProgressiveArithDecodeState* pState) {
if (!CJBig2_Image::IsValidImageSize(GBW, GBH)) {
progressive_status_ = FXCODEC_STATUS::kDecodeFinished;
return FXCODEC_STATUS::kDecodeFinished;
}
progressive_status_ = FXCODEC_STATUS::kDecodeReady;
std::unique_ptr<CJBig2_Image>* pImage = pState->pImage;
if (!*pImage) {
*pImage = std::make_unique<CJBig2_Image>(GBW, GBH);
}
if (!(*pImage)->has_data()) {
*pImage = nullptr;
progressive_status_ = FXCODEC_STATUS::kError;
return FXCODEC_STATUS::kError;
}
pImage->get()->Fill(false);
decode_type_ = 1;
ltp_ = 0;
line_prev2_ = {};
line_prev1_ = {};
line_ = {};
loop_index_ = 0;
return ProgressiveDecodeArith(pState);
}
FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArith(
ProgressiveArithDecodeState* pState) {
int iline = loop_index_;
using DecodeFunction = std::function<FXCODEC_STATUS(
CJBig2_GRDProc&, ProgressiveArithDecodeState*)>;
DecodeFunction func;
switch (GBTEMPLATE) {
case 0:
func = UseTemplate0Opt3()
? &CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Opt3
: &CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Unopt;
break;
case 1:
func = UseTemplate1Opt3()
? &CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Opt3
: &CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Unopt;
break;
case 2:
func = UseTemplate23Opt3()
? &CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Opt3
: &CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Unopt;
break;
default:
func = UseTemplate23Opt3()
? &CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Opt3
: &CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Unopt;
break;
}
CJBig2_Image* pImage = pState->pImage->get();
progressive_status_ = func(*this, pState);
replace_rect_.left = 0;
replace_rect_.right = pImage->width();
replace_rect_.top = iline;
replace_rect_.bottom = loop_index_;
if (progressive_status_ == FXCODEC_STATUS::kDecodeFinished) {
loop_index_ = 0;
}
return progressive_status_;
}
FXCODEC_STATUS CJBig2_GRDProc::StartDecodeMMR(
std::unique_ptr<CJBig2_Image>* pImage,
CJBig2_BitStream* pStream) {
auto image = std::make_unique<CJBig2_Image>(GBW, GBH);
auto image_span = image->span();
if (image_span.empty()) {
*pImage = nullptr;
progressive_status_ = FXCODEC_STATUS::kError;
return progressive_status_;
}
uint32_t bitpos = pStream->getBitPos();
bitpos = FaxModule::FaxG4Decode(pStream->getBufSpan(), bitpos, GBW, GBH,
image->stride(), image_span);
pStream->setBitPos(bitpos);
for (uint8_t& elem : image->span()) {
elem = ~elem;
}
progressive_status_ = FXCODEC_STATUS::kDecodeFinished;
replace_rect_.left = 0;
replace_rect_.right = image->width();
replace_rect_.top = 0;
replace_rect_.bottom = image->height();
*pImage = std::move(image);
return progressive_status_;
}
FXCODEC_STATUS CJBig2_GRDProc::ContinueDecode(
ProgressiveArithDecodeState* pState) {
if (progressive_status_ != FXCODEC_STATUS::kDecodeToBeContinued) {
return progressive_status_;
}
if (decode_type_ != 1) {
progressive_status_ = FXCODEC_STATUS::kError;
return progressive_status_;
}
return ProgressiveDecodeArith(pState);
}
void CJBig2_GRDProc::FinishDecode() {
line_prev2_ = {};
line_prev1_ = {};
line_ = {};
}
FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Opt3(
ProgressiveArithDecodeState* pState) {
CJBig2_Image* pImage = pState->pImage->get();
if (line_.empty()) {
line_ = pImage->span();
}
static constexpr TemplateOpt3Params kParams = {.tp_ctx = 0x9b25,
.context_mask = 0x7bf7,
.val1_shift = 0,
.val1_context_mask = 0x07f0,
.val1_bit_mask = 0x0010,
.val2_shift = 6,
.val2_context_mask = 0xf800,
.val2_bit_mask = 0x0800};
const LineLayout layout = GetLineLayout(GBW);
const uint32_t height = GBH & 0x7fffffff;
for (; loop_index_ < height; loop_index_++) {
if (!ProgressiveDecodeArithTemplateOpt3Helper(
pState, kParams, layout.full_bytes, layout.last_byte_bits)) {
return FXCODEC_STATUS::kError;
}
AdvanceLine(pImage);
if (pState->pPause && pState->pPause->NeedToPauseNow()) {
loop_index_++;
progressive_status_ = FXCODEC_STATUS::kDecodeToBeContinued;
return FXCODEC_STATUS::kDecodeToBeContinued;
}
}
progressive_status_ = FXCODEC_STATUS::kDecodeFinished;
return FXCODEC_STATUS::kDecodeFinished;
}
FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Unopt(
ProgressiveArithDecodeState* pState) {
CJBig2_Image* pImage = pState->pImage->get();
pdfium::span<JBig2ArithCtx> gbContexts = pState->gbContexts;
CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder;
pdfium::span<uint8_t> row_write = pImage->GetLine(loop_index_ - 1);
pdfium::span<const uint8_t> row_prev1 = pImage->GetLine(loop_index_ - 2);
pdfium::span<const uint8_t> row_prev2;
for (; loop_index_ < GBH; loop_index_++) {
row_prev2 = row_prev1;
row_prev1 = row_write;
row_write = pImage->GetLine(loop_index_);
if (TPGDON) {
if (pArithDecoder->IsComplete()) {
return FXCODEC_STATUS::kError;
}
ltp_ = ltp_ ^ pArithDecoder->Decode(&gbContexts[0x9b25]);
}
if (ltp_) {
pImage->CopyLine(row_write, row_prev1);
} else {
pdfium::span<const uint8_t> row_skip;
if (USESKIP) {
row_skip = SKIP->GetLine(loop_index_);
}
pdfium::span<const uint8_t> row_gbat0 =
pImage->GetLine(loop_index_ + GBAT[1]);
pdfium::span<const uint8_t> row_gbat1 =
pImage->GetLine(loop_index_ + GBAT[3]);
pdfium::span<const uint8_t> row_gbat2 =
pImage->GetLine(loop_index_ + GBAT[5]);
pdfium::span<const uint8_t> row_gbat3 =
pImage->GetLine(loop_index_ + GBAT[7]);
uint32_t val_prev2 = pImage->GetPixel(1, row_prev2);
val_prev2 |= pImage->GetPixel(0, row_prev2) << 1;
uint32_t val_prev1 = pImage->GetPixel(2, row_prev1);
val_prev1 |= pImage->GetPixel(1, row_prev1) << 1;
val_prev1 |= pImage->GetPixel(0, row_prev1) << 2;
uint32_t val_current = 0;
for (uint32_t w = 0; w < GBW; w++) {
int bVal = 0;
if (!USESKIP || !SKIP->GetPixel(w, row_skip)) {
uint32_t CONTEXT = val_current;
CONTEXT |= pImage->GetPixel(w + GBAT[0], row_gbat0) << 4;
CONTEXT |= val_prev1 << 5;
CONTEXT |= pImage->GetPixel(w + GBAT[2], row_gbat1) << 10;
CONTEXT |= pImage->GetPixel(w + GBAT[4], row_gbat2) << 11;
CONTEXT |= val_prev2 << 12;
CONTEXT |= pImage->GetPixel(w + GBAT[6], row_gbat3) << 15;
if (pArithDecoder->IsComplete()) {
return FXCODEC_STATUS::kError;
}
bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
}
if (bVal) {
pImage->SetPixel(w, row_write, bVal);
}
val_prev2 =
((val_prev2 << 1) | pImage->GetPixel(w + 2, row_prev2)) & 0x07;
val_prev1 =
((val_prev1 << 1) | pImage->GetPixel(w + 3, row_prev1)) & 0x1f;
val_current = ((val_current << 1) | bVal) & 0x0f;
}
}
if (pState->pPause && pState->pPause->NeedToPauseNow()) {
loop_index_++;
progressive_status_ = FXCODEC_STATUS::kDecodeToBeContinued;
return FXCODEC_STATUS::kDecodeToBeContinued;
}
}
progressive_status_ = FXCODEC_STATUS::kDecodeFinished;
return FXCODEC_STATUS::kDecodeFinished;
}
FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Opt3(
ProgressiveArithDecodeState* pState) {
CJBig2_Image* pImage = pState->pImage->get();
if (line_.empty()) {
line_ = pImage->span();
}
static constexpr TemplateOpt3Params kParams = {.tp_ctx = 0x0795,
.context_mask = 0x0efb,
.val1_shift = 1,
.val1_context_mask = 0x01f8,
.val1_bit_mask = 0x0008,
.val2_shift = 4,
.val2_context_mask = 0x1e00,
.val2_bit_mask = 0x0200};
const LineLayout layout = GetLineLayout(GBW);
for (; loop_index_ < GBH; loop_index_++) {
if (!ProgressiveDecodeArithTemplateOpt3Helper(
pState, kParams, layout.full_bytes, layout.last_byte_bits)) {
return FXCODEC_STATUS::kError;
}
AdvanceLine(pImage);
if (pState->pPause && pState->pPause->NeedToPauseNow()) {
loop_index_++;
progressive_status_ = FXCODEC_STATUS::kDecodeToBeContinued;
return FXCODEC_STATUS::kDecodeToBeContinued;
}
}
progressive_status_ = FXCODEC_STATUS::kDecodeFinished;
return FXCODEC_STATUS::kDecodeFinished;
}
FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Unopt(
ProgressiveArithDecodeState* pState) {
CJBig2_Image* pImage = pState->pImage->get();
pdfium::span<JBig2ArithCtx> gbContexts = pState->gbContexts;
CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder;
pdfium::span<uint8_t> row_write = pImage->GetLine(loop_index_ - 1);
pdfium::span<const uint8_t> row_prev1 = pImage->GetLine(loop_index_ - 2);
pdfium::span<const uint8_t> row_prev2;
for (; loop_index_ < GBH; loop_index_++) {
row_prev2 = row_prev1;
row_prev1 = row_write;
row_write = pImage->GetLine(loop_index_);
if (TPGDON) {
if (pArithDecoder->IsComplete()) {
return FXCODEC_STATUS::kError;
}
ltp_ = ltp_ ^ pArithDecoder->Decode(&gbContexts[0x0795]);
}
if (ltp_) {
pImage->CopyLine(row_write, row_prev1);
} else {
pdfium::span<const uint8_t> row_skip;
if (USESKIP) {
row_skip = SKIP->GetLine(loop_index_);
}
pdfium::span<const uint8_t> row_gbat =
pImage->GetLine(loop_index_ + GBAT[1]);
uint32_t val_prev2 = pImage->GetPixel(2, row_prev2);
val_prev2 |= pImage->GetPixel(1, row_prev2) << 1;
val_prev2 |= pImage->GetPixel(0, row_prev2) << 2;
uint32_t val_prev1 = pImage->GetPixel(2, row_prev1);
val_prev1 |= pImage->GetPixel(1, row_prev1) << 1;
val_prev1 |= pImage->GetPixel(0, row_prev1) << 2;
uint32_t val_current = 0;
for (uint32_t w = 0; w < GBW; w++) {
int bVal = 0;
if (!USESKIP || !SKIP->GetPixel(w, row_skip)) {
uint32_t CONTEXT = val_current;
CONTEXT |= pImage->GetPixel(w + GBAT[0], row_gbat) << 3;
CONTEXT |= val_prev1 << 4;
CONTEXT |= val_prev2 << 9;
if (pArithDecoder->IsComplete()) {
return FXCODEC_STATUS::kError;
}
bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
}
if (bVal) {
pImage->SetPixel(w, row_write, bVal);
}
val_prev2 =
((val_prev2 << 1) | pImage->GetPixel(w + 3, row_prev2)) & 0x0f;
val_prev1 =
((val_prev1 << 1) | pImage->GetPixel(w + 3, row_prev1)) & 0x1f;
val_current = ((val_current << 1) | bVal) & 0x07;
}
}
if (pState->pPause && pState->pPause->NeedToPauseNow()) {
loop_index_++;
progressive_status_ = FXCODEC_STATUS::kDecodeToBeContinued;
return FXCODEC_STATUS::kDecodeToBeContinued;
}
}
progressive_status_ = FXCODEC_STATUS::kDecodeFinished;
return FXCODEC_STATUS::kDecodeFinished;
}
FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Opt3(
ProgressiveArithDecodeState* pState) {
CJBig2_Image* pImage = pState->pImage->get();
if (line_.empty()) {
line_ = pImage->span();
}
static constexpr TemplateOpt3Params kParams = {.tp_ctx = 0x00e5,
.context_mask = 0x01bd,
.val1_shift = 3,
.val1_context_mask = 0x007c,
.val1_bit_mask = 0x0004,
.val2_shift = 1,
.val2_context_mask = 0x0380,
.val2_bit_mask = 0x0080};
const LineLayout layout = GetLineLayout(GBW);
for (; loop_index_ < GBH; loop_index_++) {
if (!ProgressiveDecodeArithTemplateOpt3Helper(
pState, kParams, layout.full_bytes, layout.last_byte_bits)) {
return FXCODEC_STATUS::kError;
}
AdvanceLine(pImage);
if (pState->pPause && loop_index_ % 50 == 0 &&
pState->pPause->NeedToPauseNow()) {
loop_index_++;
progressive_status_ = FXCODEC_STATUS::kDecodeToBeContinued;
return FXCODEC_STATUS::kDecodeToBeContinued;
}
}
progressive_status_ = FXCODEC_STATUS::kDecodeFinished;
return FXCODEC_STATUS::kDecodeFinished;
}
bool CJBig2_GRDProc::ProgressiveDecodeArithTemplateOpt3Helper(
ProgressiveArithDecodeState* state,
const TemplateOpt3Params& params,
uint32_t nLineBytes,
uint32_t nBitsLeft) {
if (TPGDON) {
if (state->pArithDecoder->IsComplete()) {
return false;
}
ltp_ =
ltp_ ^ state->pArithDecoder->Decode(&state->gbContexts[params.tp_ctx]);
}
if (ltp_) {
CopyPrevLine(state->pImage->get());
return true;
}
if (loop_index_ <= 1) {
const bool is_second_line = loop_index_ == 1;
uint32_t val_prev = is_second_line ? line_prev1_[0] : 0;
uint32_t CONTEXT =
(val_prev >> params.val1_shift) & params.val1_context_mask;
for (uint32_t cc = 0; cc < nLineBytes; cc++) {
if (is_second_line) {
val_prev = (val_prev << 8) | line_prev1_[cc + 1];
}
uint8_t cVal = 0;
for (int32_t k = 7; k >= 0; k--) {
if (state->pArithDecoder->IsComplete()) {
return false;
}
int bVal = state->pArithDecoder->Decode(&state->gbContexts[CONTEXT]);
cVal |= bVal << k;
CONTEXT =
((CONTEXT & params.context_mask) << 1) | bVal |
((val_prev >> (k + params.val1_shift)) & params.val1_bit_mask);
}
line_[cc] = cVal;
}
val_prev <<= 8;
uint8_t cVal1 = 0;
for (uint32_t k = 0; k < nBitsLeft; k++) {
if (state->pArithDecoder->IsComplete()) {
return false;
}
int bVal = state->pArithDecoder->Decode(&state->gbContexts[CONTEXT]);
cVal1 |= bVal << (7 - k);
CONTEXT =
((CONTEXT & params.context_mask) << 1) | bVal |
((val_prev >> (7 - k + params.val1_shift)) & params.val1_bit_mask);
}
line_[nLineBytes] = cVal1;
return true;
}
uint32_t val_prev2 = line_prev2_[0] << params.val2_shift;
uint32_t val_prev1 = line_prev1_[0];
uint32_t CONTEXT =
(val_prev2 & params.val2_context_mask) |
((val_prev1 >> params.val1_shift) & params.val1_context_mask);
for (uint32_t cc = 0; cc < nLineBytes; cc++) {
val_prev2 = (val_prev2 << 8) | (line_prev2_[cc + 1] << params.val2_shift);
val_prev1 = (val_prev1 << 8) | line_prev1_[cc + 1];
uint8_t cVal = 0;
for (int32_t k = 7; k >= 0; k--) {
if (state->pArithDecoder->IsComplete()) {
return false;
}
int bVal = state->pArithDecoder->Decode(&state->gbContexts[CONTEXT]);
cVal |= bVal << k;
CONTEXT = ((CONTEXT & params.context_mask) << 1) | bVal |
((val_prev2 >> k) & params.val2_bit_mask) |
((val_prev1 >> (k + params.val1_shift)) & params.val1_bit_mask);
}
line_[cc] = cVal;
}
val_prev2 <<= 8;
val_prev1 <<= 8;
uint8_t cVal1 = 0;
for (uint32_t k = 0; k < nBitsLeft; k++) {
if (state->pArithDecoder->IsComplete()) {
return false;
}
int bVal = state->pArithDecoder->Decode(&state->gbContexts[CONTEXT]);
cVal1 |= bVal << (7 - k);
CONTEXT =
((CONTEXT & params.context_mask) << 1) | bVal |
((val_prev2 >> (7 - k)) & params.val2_bit_mask) |
((val_prev1 >> (7 - k + params.val1_shift)) & params.val1_bit_mask);
}
line_[nLineBytes] = cVal1;
return true;
}
FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Unopt(
ProgressiveArithDecodeState* pState) {
CJBig2_Image* pImage = pState->pImage->get();
pdfium::span<JBig2ArithCtx> gbContexts = pState->gbContexts;
CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder;
pdfium::span<uint8_t> row_write = pImage->GetLine(loop_index_ - 1);
pdfium::span<const uint8_t> row_prev1 = pImage->GetLine(loop_index_ - 2);
pdfium::span<const uint8_t> row_prev2;
for (; loop_index_ < GBH; loop_index_++) {
row_prev2 = row_prev1;
row_prev1 = row_write;
row_write = pImage->GetLine(loop_index_);
if (TPGDON) {
if (pArithDecoder->IsComplete()) {
return FXCODEC_STATUS::kError;
}
ltp_ = ltp_ ^ pArithDecoder->Decode(&gbContexts[0x00e5]);
}
if (ltp_) {
pImage->CopyLine(row_write, row_prev1);
} else {
pdfium::span<const uint8_t> row_skip;
if (USESKIP) {
row_skip = SKIP->GetLine(loop_index_);
}
pdfium::span<const uint8_t> row_gbat =
pImage->GetLine(loop_index_ + GBAT[1]);
uint32_t val_prev2 = pImage->GetPixel(1, row_prev2);
val_prev2 |= pImage->GetPixel(0, row_prev2) << 1;
uint32_t val_prev1 = pImage->GetPixel(1, row_prev1);
val_prev1 |= pImage->GetPixel(0, row_prev1) << 1;
uint32_t val_current = 0;
for (uint32_t w = 0; w < GBW; w++) {
int bVal = 0;
if (!USESKIP || !SKIP->GetPixel(w, row_skip)) {
uint32_t CONTEXT = val_current;
CONTEXT |= pImage->GetPixel(w + GBAT[0], row_gbat) << 2;
CONTEXT |= val_prev1 << 3;
CONTEXT |= val_prev2 << 7;
if (pArithDecoder->IsComplete()) {
return FXCODEC_STATUS::kError;
}
bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
}
if (bVal) {
pImage->SetPixel(w, row_write, bVal);
}
val_prev2 =
((val_prev2 << 1) | pImage->GetPixel(w + 2, row_prev2)) & 0x07;
val_prev1 =
((val_prev1 << 1) | pImage->GetPixel(w + 2, row_prev1)) & 0x0f;
val_current = ((val_current << 1) | bVal) & 0x03;
}
}
if (pState->pPause && pState->pPause->NeedToPauseNow()) {
loop_index_++;
progressive_status_ = FXCODEC_STATUS::kDecodeToBeContinued;
return FXCODEC_STATUS::kDecodeToBeContinued;
}
}
progressive_status_ = FXCODEC_STATUS::kDecodeFinished;
return FXCODEC_STATUS::kDecodeFinished;
}
FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Opt3(
ProgressiveArithDecodeState* pState) {
CJBig2_Image* pImage = pState->pImage->get();
pdfium::span<JBig2ArithCtx> gbContexts = pState->gbContexts;
CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder;
if (line_.empty()) {
line_ = pImage->span();
}
const LineLayout layout = GetLineLayout(GBW);
for (; loop_index_ < GBH; loop_index_++) {
if (TPGDON) {
if (pArithDecoder->IsComplete()) {
return FXCODEC_STATUS::kError;
}
ltp_ = ltp_ ^ pArithDecoder->Decode(&gbContexts[0x0195]);
}
if (ltp_) {
CopyPrevLine(pImage);
} else {
if (loop_index_ == 0) {
uint32_t CONTEXT = 0;
for (uint32_t cc = 0; cc < layout.full_bytes; cc++) {
uint8_t cVal = 0;
for (int32_t k = 7; k >= 0; k--) {
if (pArithDecoder->IsComplete()) {
return FXCODEC_STATUS::kError;
}
int bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
cVal |= bVal << k;
CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal;
}
line_[cc] = cVal;
}
uint8_t cVal1 = 0;
for (uint32_t k = 0; k < layout.last_byte_bits; k++) {
if (pArithDecoder->IsComplete()) {
return FXCODEC_STATUS::kError;
}
int bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
cVal1 |= bVal << (7 - k);
CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal;
}
line_[layout.full_bytes] = cVal1;
} else {
uint32_t val_prev = line_prev1_[0];
uint32_t CONTEXT = (val_prev >> 1) & 0x03f0;
for (uint32_t cc = 0; cc < layout.full_bytes; cc++) {
val_prev = (val_prev << 8) | line_prev1_[cc + 1];
uint8_t cVal = 0;
for (int32_t k = 7; k >= 0; k--) {
if (pArithDecoder->IsComplete()) {
return FXCODEC_STATUS::kError;
}
int bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
cVal |= bVal << k;
CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal |
((val_prev >> (k + 1)) & 0x0010);
}
line_[cc] = cVal;
}
val_prev <<= 8;
uint8_t cVal1 = 0;
for (uint32_t k = 0; k < layout.last_byte_bits; k++) {
if (pArithDecoder->IsComplete()) {
return FXCODEC_STATUS::kError;
}
int bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
cVal1 |= bVal << (7 - k);
CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal |
((val_prev >> (8 - k)) & 0x0010);
}
line_[layout.full_bytes] = cVal1;
}
}
AdvanceLine(pImage);
if (pState->pPause && pState->pPause->NeedToPauseNow()) {
loop_index_++;
progressive_status_ = FXCODEC_STATUS::kDecodeToBeContinued;
return FXCODEC_STATUS::kDecodeToBeContinued;
}
}
progressive_status_ = FXCODEC_STATUS::kDecodeFinished;
return FXCODEC_STATUS::kDecodeFinished;
}
FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Unopt(
ProgressiveArithDecodeState* pState) {
CJBig2_Image* pImage = pState->pImage->get();
pdfium::span<JBig2ArithCtx> gbContexts = pState->gbContexts;
CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder;
pdfium::span<uint8_t> row_write = pImage->GetLine(loop_index_ - 1);
pdfium::span<const uint8_t> row_prev;
for (; loop_index_ < GBH; loop_index_++) {
row_prev = row_write;
row_write = pImage->GetLine(loop_index_);
if (TPGDON) {
if (pArithDecoder->IsComplete()) {
return FXCODEC_STATUS::kError;
}
ltp_ = ltp_ ^ pArithDecoder->Decode(&gbContexts[0x0195]);
}
if (ltp_) {
pImage->CopyLine(row_write, row_prev);
} else {
pdfium::span<const uint8_t> row_skip;
if (USESKIP) {
row_skip = SKIP->GetLine(loop_index_);
}
pdfium::span<const uint8_t> row_gbat =
pImage->GetLine(loop_index_ + GBAT[1]);
uint32_t val_prev = pImage->GetPixel(1, row_prev);
val_prev |= pImage->GetPixel(0, row_prev) << 1;
uint32_t val_current = 0;
for (uint32_t w = 0; w < GBW; w++) {
int bVal = 0;
if (!USESKIP || !SKIP->GetPixel(w, row_skip)) {
uint32_t CONTEXT = val_current;
CONTEXT |= pImage->GetPixel(w + GBAT[0], row_gbat) << 4;
CONTEXT |= val_prev << 5;
if (pArithDecoder->IsComplete()) {
return FXCODEC_STATUS::kError;
}
bVal = pArithDecoder->Decode(&gbContexts[CONTEXT]);
}
if (bVal) {
pImage->SetPixel(w, row_write, bVal);
}
val_prev = ((val_prev << 1) | pImage->GetPixel(w + 2, row_prev)) & 0x1f;
val_current = ((val_current << 1) | bVal) & 0x0f;
}
}
if (pState->pPause && pState->pPause->NeedToPauseNow()) {
loop_index_++;
progressive_status_ = FXCODEC_STATUS::kDecodeToBeContinued;
return FXCODEC_STATUS::kDecodeToBeContinued;
}
}
progressive_status_ = FXCODEC_STATUS::kDecodeFinished;
return FXCODEC_STATUS::kDecodeFinished;
}
void CJBig2_GRDProc::AdvanceLine(const CJBig2_Image* image) {
line_prev2_ = std::move(line_prev1_);
auto next_line = line_.subspan(static_cast<size_t>(image->stride()));
line_prev1_ = std::move(line_);
line_ = std::move(next_line);
}
void CJBig2_GRDProc::CopyPrevLine(CJBig2_Image* image) {
if (!line_prev1_.empty()) {
image->CopyLine(line_,
line_prev1_.first(static_cast<size_t>(image->stride())));
}
}