blob: cfbd37cc5a903e872d3eda7917b27c6c217e431a [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_GrrdProc.h"
#include <array>
#include <memory>
#include "core/fxcodec/jbig2/JBig2_ArithDecoder.h"
#include "core/fxcodec/jbig2/JBig2_BitStream.h"
#include "core/fxcodec/jbig2/JBig2_Image.h"
#include "core/fxcrt/zip.h"
CJBig2_GRRDProc::CJBig2_GRRDProc() = default;
CJBig2_GRRDProc::~CJBig2_GRRDProc() = default;
std::unique_ptr<CJBig2_Image> CJBig2_GRRDProc::Decode(
CJBig2_ArithDecoder* pArithDecoder,
pdfium::span<JBig2ArithCtx> grContexts) {
if (!CJBig2_Image::IsValidImageSize(GRW, GRH)) {
return std::make_unique<CJBig2_Image>(GRW, GRH);
}
if (!GRTEMPLATE) {
if ((GRAT[0] == -1) && (GRAT[1] == -1) && (GRAT[2] == -1) &&
(GRAT[3] == -1) && (GRREFERENCEDX == 0) &&
(GRW == (uint32_t)GRREFERENCE->width())) {
return DecodeTemplate0Opt(pArithDecoder, grContexts);
}
return DecodeTemplate0Unopt(pArithDecoder, grContexts);
}
if ((GRREFERENCEDX == 0) && (GRW == (uint32_t)GRREFERENCE->width())) {
return DecodeTemplate1Opt(pArithDecoder, grContexts);
}
return DecodeTemplate1Unopt(pArithDecoder, grContexts);
}
std::unique_ptr<CJBig2_Image> CJBig2_GRRDProc::DecodeTemplate0Unopt(
CJBig2_ArithDecoder* pArithDecoder,
pdfium::span<JBig2ArithCtx> grContexts) {
auto GRREG = std::make_unique<CJBig2_Image>(GRW, GRH);
if (!GRREG->has_data()) {
return nullptr;
}
GRREG->Fill(false);
int LTP = 0;
for (uint32_t h = 0; h < GRH; h++) {
if (TPGRON) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
LTP = LTP ^ pArithDecoder->Decode(&grContexts[0x0010]);
}
pdfium::span<uint8_t> row_write = GRREG->GetLine(h);
pdfium::span<const uint8_t> row_prev = GRREG->GetLine(h - 1);
std::array<pdfium::span<const uint8_t>, 3> row_refs_dy = GetRowRefsDy(h);
std::array<uint32_t, 5> lines;
lines[0] = GRREG->GetPixel(1, row_prev);
lines[0] |= GRREG->GetPixel(0, row_prev) << 1;
lines[1] = 0;
lines[2] = GRREFERENCE->GetPixel(-GRREFERENCEDX + 1, row_refs_dy[0]);
lines[2] |= GRREFERENCE->GetPixel(-GRREFERENCEDX, row_refs_dy[0]) << 1;
lines[3] = GRREFERENCE->GetPixel(-GRREFERENCEDX + 1, row_refs_dy[1]);
lines[3] |= GRREFERENCE->GetPixel(-GRREFERENCEDX, row_refs_dy[1]) << 1;
lines[3] |= GRREFERENCE->GetPixel(-GRREFERENCEDX - 1, row_refs_dy[1]) << 2;
lines[4] = GRREFERENCE->GetPixel(-GRREFERENCEDX + 1, row_refs_dy[2]);
lines[4] |= GRREFERENCE->GetPixel(-GRREFERENCEDX, row_refs_dy[2]) << 1;
lines[4] |= GRREFERENCE->GetPixel(-GRREFERENCEDX - 1, row_refs_dy[2]) << 2;
pdfium::span<const uint8_t> row_ref_grat =
GRREFERENCE->GetLine(h - GRREFERENCEDY + GRAT[3]);
pdfium::span<const uint8_t> row_grat = GRREG->GetLine(h + GRAT[1]);
if (!LTP) {
for (uint32_t w = 0; w < GRW; w++) {
uint32_t CONTEXT = DecodeTemplate0UnoptCalculateContext(
*GRREG, lines, w, row_ref_grat, row_grat);
if (pArithDecoder->IsComplete()) {
return nullptr;
}
int bVal = pArithDecoder->Decode(&grContexts[CONTEXT]);
DecodeTemplate0UnoptSetPixel(GRREG.get(), lines, w, bVal, row_refs_dy,
row_prev, row_write);
}
} else {
std::array<pdfium::span<const uint8_t>, 3> row_refs = GetRowRefs(h);
for (uint32_t w = 0; w < GRW; w++) {
int bVal = GRREFERENCE->GetPixel(w, row_refs[1]);
if (!TPGRON || !TypicalPrediction(w, bVal, row_refs)) {
uint32_t CONTEXT = DecodeTemplate0UnoptCalculateContext(
*GRREG, lines, w, row_ref_grat, row_grat);
if (pArithDecoder->IsComplete()) {
return nullptr;
}
bVal = pArithDecoder->Decode(&grContexts[CONTEXT]);
}
DecodeTemplate0UnoptSetPixel(GRREG.get(), lines, w, bVal, row_refs_dy,
row_prev, row_write);
}
}
}
return GRREG;
}
uint32_t CJBig2_GRRDProc::DecodeTemplate0UnoptCalculateContext(
const CJBig2_Image& GRREG,
pdfium::span<const uint32_t, 5> lines,
uint32_t w,
pdfium::span<const uint8_t> row_ref_grat,
pdfium::span<const uint8_t> row_grat) const {
uint32_t CONTEXT = lines[4];
CONTEXT |= lines[3] << 3;
CONTEXT |= lines[2] << 6;
CONTEXT |= GRREFERENCE->GetPixel(w - GRREFERENCEDX + GRAT[2], row_ref_grat)
<< 8;
CONTEXT |= lines[1] << 9;
CONTEXT |= lines[0] << 10;
CONTEXT |= GRREG.GetPixel(w + GRAT[0], row_grat) << 12;
return CONTEXT;
}
void CJBig2_GRRDProc::DecodeTemplate0UnoptSetPixel(
CJBig2_Image* GRREG,
pdfium::span<uint32_t, 5> lines,
uint32_t w,
int bVal,
pdfium::span<pdfium::span<const uint8_t>, 3> row_refs_dy,
pdfium::span<const uint8_t> row_prev,
pdfium::span<uint8_t> row_write) {
GRREG->SetPixel(w, row_write, bVal);
const int w_dx = w - GRREFERENCEDX + 2;
lines[0] = ((lines[0] << 1) | GRREG->GetPixel(w + 2, row_prev)) & 0x03;
lines[1] = ((lines[1] << 1) | bVal) & 0x01;
lines[2] =
((lines[2] << 1) | GRREFERENCE->GetPixel(w_dx, row_refs_dy[0])) & 0x03;
lines[3] =
((lines[3] << 1) | GRREFERENCE->GetPixel(w_dx, row_refs_dy[1])) & 0x07;
lines[4] =
((lines[4] << 1) | GRREFERENCE->GetPixel(w_dx, row_refs_dy[2])) & 0x07;
}
std::unique_ptr<CJBig2_Image> CJBig2_GRRDProc::DecodeTemplate0Opt(
CJBig2_ArithDecoder* pArithDecoder,
pdfium::span<JBig2ArithCtx> grContexts) {
if (!GRREFERENCE->has_data()) {
return nullptr;
}
int32_t iGRW = static_cast<int32_t>(GRW);
int32_t iGRH = static_cast<int32_t>(GRH);
auto GRREG = std::make_unique<CJBig2_Image>(iGRW, iGRH);
if (!GRREG->has_data()) {
return nullptr;
}
int LTP = 0;
const int32_t GRWR = GRREFERENCE->width();
const int32_t GRHR = GRREFERENCE->height();
if (GRREFERENCEDY < -GRHR + 1 || GRREFERENCEDY > GRHR - 1) {
GRREFERENCEDY = 0;
}
for (int32_t h = 0; h < iGRH; h++) {
if (TPGRON) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
LTP = LTP ^ pArithDecoder->Decode(&grContexts[0x0010]);
}
const bool is_first_line = h == 0;
pdfium::span<uint8_t> row_write = GRREG->GetLine(h);
pdfium::span<const uint8_t> row_prev;
uint32_t line1 = 0;
if (!is_first_line) {
row_prev = GRREG->GetLine(h - 1);
line1 = row_prev.front() << 4;
}
const int32_t reference_h = h - GRREFERENCEDY;
std::array<pdfium::span<const uint8_t>, 3> row_refs_dy;
std::array<uint32_t, 3> refs = {};
if (reference_h > 0 && reference_h < GRHR + 1) {
row_refs_dy[0] = GRREFERENCE->GetLine(reference_h - 1);
refs[0] = row_refs_dy[0].front();
}
if (reference_h > -1 && reference_h < GRHR) {
row_refs_dy[1] = GRREFERENCE->GetLine(reference_h);
refs[1] = row_refs_dy[1].front();
}
if (reference_h > -2 && reference_h < GRHR - 1) {
row_refs_dy[2] = GRREFERENCE->GetLine(reference_h + 1);
refs[2] = row_refs_dy[2].front();
}
if (!LTP) {
uint32_t CONTEXT = (line1 & 0x1c00) | (refs[0] & 0x01c0) |
((refs[1] >> 3) & 0x0038) | ((refs[2] >> 6) & 0x0007);
for (int32_t w = 0; w < iGRW; w += 8) {
int32_t nBits = iGRW - w > 8 ? 8 : iGRW - w;
if (!is_first_line) {
line1 = line1 << 8;
if (w + 8 < iGRW) {
line1 |= row_prev[w / 8 + 1] << 4;
}
}
if (h > GRHR + GRREFERENCEDY + 1) {
refs = {};
} else {
const bool next_w_in_bounds = w + 8 < GRWR;
for (auto [row_ref_dy, ref] : fxcrt::Zip(row_refs_dy, refs)) {
if (!row_ref_dy.empty()) {
ref <<= 8;
if (next_w_in_bounds) {
ref |= row_ref_dy[(w / 8) + 1];
}
}
}
}
uint8_t cVal = 0;
for (int32_t k = 0; k < nBits; k++) {
int bVal = pArithDecoder->Decode(&grContexts[CONTEXT]);
cVal |= bVal << (7 - k);
CONTEXT = ((CONTEXT & 0x0cdb) << 1) | (bVal << 9) |
((line1 >> (7 - k)) & 0x0400) |
((refs[0] >> (7 - k)) & 0x0040) |
((refs[1] >> (10 - k)) & 0x0008) |
((refs[2] >> (13 - k)) & 0x0001);
}
row_write[w / 8] = cVal;
}
} else {
std::array<pdfium::span<const uint8_t>, 3> row_refs = GetRowRefs(h);
uint32_t CONTEXT = (line1 & 0x1c00) | (refs[0] & 0x01c0) |
((refs[1] >> 3) & 0x0038) | ((refs[2] >> 6) & 0x0007);
for (int32_t w = 0; w < iGRW; w += 8) {
int32_t nBits = iGRW - w > 8 ? 8 : iGRW - w;
if (!is_first_line) {
line1 = line1 << 8;
if (w + 8 < iGRW) {
line1 |= row_prev[w / 8 + 1] << 4;
}
}
const bool next_w_in_bounds = w + 8 < GRWR;
for (auto [row_ref_dy, ref] : fxcrt::Zip(row_refs_dy, refs)) {
if (!row_ref_dy.empty()) {
ref <<= 8;
if (next_w_in_bounds) {
ref |= row_ref_dy[(w / 8) + 1];
}
}
}
uint8_t cVal = 0;
for (int32_t k = 0; k < nBits; k++) {
int bVal = GRREFERENCE->GetPixel(w + k, row_refs[1]);
if (!TPGRON || !TypicalPrediction(w + k, bVal, row_refs)) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
bVal = pArithDecoder->Decode(&grContexts[CONTEXT]);
}
cVal |= bVal << (7 - k);
CONTEXT = ((CONTEXT & 0x0cdb) << 1) | (bVal << 9) |
((line1 >> (7 - k)) & 0x0400) |
((refs[0] >> (7 - k)) & 0x0040) |
((refs[1] >> (10 - k)) & 0x0008) |
((refs[2] >> (13 - k)) & 0x0001);
}
row_write[w / 8] = cVal;
}
}
}
return GRREG;
}
std::unique_ptr<CJBig2_Image> CJBig2_GRRDProc::DecodeTemplate1Unopt(
CJBig2_ArithDecoder* pArithDecoder,
pdfium::span<JBig2ArithCtx> grContexts) {
auto GRREG = std::make_unique<CJBig2_Image>(GRW, GRH);
if (!GRREG->has_data()) {
return nullptr;
}
GRREG->Fill(false);
int LTP = 0;
for (uint32_t h = 0; h < GRH; h++) {
if (TPGRON) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
LTP = LTP ^ pArithDecoder->Decode(&grContexts[0x0008]);
}
pdfium::span<uint8_t> row_write = GRREG->GetLine(h);
pdfium::span<const uint8_t> row_prev = GRREG->GetLine(h - 1);
std::array<pdfium::span<const uint8_t>, 3> row_refs_dy = GetRowRefsDy(h);
std::array<uint32_t, 5> lines;
lines[0] = GRREG->GetPixel(1, row_prev);
lines[0] |= GRREG->GetPixel(0, row_prev) << 1;
lines[0] |= GRREG->GetPixel(-1, row_prev) << 2;
lines[1] = 0;
lines[2] = GRREFERENCE->GetPixel(-GRREFERENCEDX, row_refs_dy[0]);
lines[2] = GRREFERENCE->GetPixel(-GRREFERENCEDX, row_refs_dy[0]);
lines[3] = GRREFERENCE->GetPixel(-GRREFERENCEDX + 1, row_refs_dy[1]);
lines[3] |= GRREFERENCE->GetPixel(-GRREFERENCEDX, row_refs_dy[1]) << 1;
lines[3] |= GRREFERENCE->GetPixel(-GRREFERENCEDX - 1, row_refs_dy[1]) << 2;
lines[4] = GRREFERENCE->GetPixel(-GRREFERENCEDX + 1, row_refs_dy[2]);
lines[4] |= GRREFERENCE->GetPixel(-GRREFERENCEDX, row_refs_dy[2]) << 1;
if (!LTP) {
for (uint32_t w = 0; w < GRW; w++) {
uint32_t CONTEXT = lines[4];
CONTEXT |= lines[3] << 2;
CONTEXT |= lines[2] << 5;
CONTEXT |= lines[1] << 6;
CONTEXT |= lines[0] << 7;
if (pArithDecoder->IsComplete()) {
return nullptr;
}
int bVal = pArithDecoder->Decode(&grContexts[CONTEXT]);
GRREG->SetPixel(w, row_write, bVal);
const int w_dx = w - GRREFERENCEDX + 1;
lines[0] = ((lines[0] << 1) | GRREG->GetPixel(w + 2, row_prev)) & 0x07;
lines[1] = ((lines[1] << 1) | bVal) & 0x01;
lines[2] =
((lines[2] << 1) | GRREFERENCE->GetPixel(w_dx, row_refs_dy[0])) &
0x01;
lines[3] = ((lines[3] << 1) |
GRREFERENCE->GetPixel(w_dx + 1, row_refs_dy[1])) &
0x07;
lines[4] = ((lines[4] << 1) |
GRREFERENCE->GetPixel(w_dx + 1, row_refs_dy[2])) &
0x03;
}
} else {
std::array<pdfium::span<const uint8_t>, 3> row_refs = GetRowRefs(h);
for (uint32_t w = 0; w < GRW; w++) {
int bVal = GRREFERENCE->GetPixel(w, row_refs[1]);
if (!TPGRON || !TypicalPrediction(w, bVal, row_refs)) {
uint32_t CONTEXT = lines[4];
CONTEXT |= lines[3] << 2;
CONTEXT |= lines[2] << 5;
CONTEXT |= lines[1] << 6;
CONTEXT |= lines[0] << 7;
if (pArithDecoder->IsComplete()) {
return nullptr;
}
bVal = pArithDecoder->Decode(&grContexts[CONTEXT]);
}
GRREG->SetPixel(w, row_write, bVal);
int w_dx = w - GRREFERENCEDX + 1;
lines[0] = ((lines[0] << 1) | GRREG->GetPixel(w + 2, row_prev)) & 0x07;
lines[1] = ((lines[1] << 1) | bVal) & 0x01;
lines[2] =
((lines[2] << 1) | GRREFERENCE->GetPixel(w_dx, row_refs_dy[0])) &
0x01;
lines[3] = ((lines[3] << 1) |
GRREFERENCE->GetPixel(w_dx + 1, row_refs_dy[1])) &
0x07;
lines[4] = ((lines[4] << 1) |
GRREFERENCE->GetPixel(w_dx + 1, row_refs_dy[2])) &
0x03;
}
}
}
return GRREG;
}
std::unique_ptr<CJBig2_Image> CJBig2_GRRDProc::DecodeTemplate1Opt(
CJBig2_ArithDecoder* pArithDecoder,
pdfium::span<JBig2ArithCtx> grContexts) {
if (!GRREFERENCE->has_data()) {
return nullptr;
}
int32_t iGRW = static_cast<int32_t>(GRW);
int32_t iGRH = static_cast<int32_t>(GRH);
auto GRREG = std::make_unique<CJBig2_Image>(iGRW, iGRH);
if (!GRREG->has_data()) {
return nullptr;
}
int LTP = 0;
const int32_t GRWR = GRREFERENCE->width();
const int32_t GRHR = GRREFERENCE->height();
if (GRREFERENCEDY < -GRHR + 1 || GRREFERENCEDY > GRHR - 1) {
GRREFERENCEDY = 0;
}
for (int32_t h = 0; h < iGRH; h++) {
if (TPGRON) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
LTP = LTP ^ pArithDecoder->Decode(&grContexts[0x0008]);
}
const bool is_first_line = h == 0;
pdfium::span<uint8_t> row_write = GRREG->GetLine(h);
pdfium::span<const uint8_t> row_prev;
uint32_t line1 = 0;
if (!is_first_line) {
row_prev = GRREG->GetLine(h - 1);
line1 = row_prev.front() << 1;
}
const int32_t reference_h = h - GRREFERENCEDY;
std::array<pdfium::span<const uint8_t>, 3> row_refs_dy;
std::array<uint32_t, 3> refs = {};
if (reference_h > 0 && reference_h < GRHR + 1) {
row_refs_dy[0] = GRREFERENCE->GetLine(reference_h - 1);
refs[0] = row_refs_dy[0].front();
}
if (reference_h > -1 && reference_h < GRHR) {
row_refs_dy[1] = GRREFERENCE->GetLine(reference_h);
refs[1] = row_refs_dy[1].front();
}
if (reference_h > -2 && reference_h < GRHR - 1) {
row_refs_dy[2] = GRREFERENCE->GetLine(reference_h + 1);
refs[2] = row_refs_dy[2].front();
}
if (!LTP) {
uint32_t CONTEXT = (line1 & 0x0380) | ((refs[0] >> 2) & 0x0020) |
((refs[1] >> 4) & 0x001c) | ((refs[2] >> 6) & 0x0003);
for (int32_t w = 0; w < iGRW; w += 8) {
int32_t nBits = iGRW - w > 8 ? 8 : iGRW - w;
if (!is_first_line) {
line1 = line1 << 8;
if (w + 8 < iGRW) {
line1 |= row_prev[w / 8 + 1] << 1;
}
}
const bool next_w_in_bounds = w + 8 < GRWR;
for (auto [row_ref_dy, ref] : fxcrt::Zip(row_refs_dy, refs)) {
if (!row_ref_dy.empty()) {
ref <<= 8;
if (next_w_in_bounds) {
ref |= row_ref_dy[(w / 8) + 1];
}
}
}
uint8_t cVal = 0;
for (int32_t k = 0; k < nBits; k++) {
int bVal = pArithDecoder->Decode(&grContexts[CONTEXT]);
cVal |= bVal << (7 - k);
CONTEXT = ((CONTEXT & 0x018d) << 1) | (bVal << 6) |
((line1 >> (7 - k)) & 0x0080) |
((refs[0] >> (9 - k)) & 0x0020) |
((refs[1] >> (11 - k)) & 0x0004) |
((refs[2] >> (13 - k)) & 0x0001);
}
row_write[w / 8] = cVal;
}
} else {
std::array<pdfium::span<const uint8_t>, 3> row_refs = GetRowRefs(h);
uint32_t CONTEXT = (line1 & 0x0380) | ((refs[0] >> 2) & 0x0020) |
((refs[1] >> 4) & 0x001c) | ((refs[2] >> 6) & 0x0003);
for (int32_t w = 0; w < iGRW; w += 8) {
int32_t nBits = iGRW - w > 8 ? 8 : iGRW - w;
if (!is_first_line) {
line1 = line1 << 8;
if (w + 8 < iGRW) {
line1 |= row_prev[(w / 8) + 1] << 1;
}
}
const bool next_w_in_bounds = w + 8 < GRWR;
for (auto [row_ref_dy, ref] : fxcrt::Zip(row_refs_dy, refs)) {
if (!row_ref_dy.empty()) {
ref <<= 8;
if (next_w_in_bounds) {
ref |= row_ref_dy[(w / 8) + 1];
}
}
}
uint8_t cVal = 0;
for (int32_t k = 0; k < nBits; k++) {
int bVal = GRREFERENCE->GetPixel(w + k, row_refs[1]);
if (!TPGRON || !TypicalPrediction(w + k, bVal, row_refs)) {
if (pArithDecoder->IsComplete()) {
return nullptr;
}
bVal = pArithDecoder->Decode(&grContexts[CONTEXT]);
}
cVal |= bVal << (7 - k);
CONTEXT = ((CONTEXT & 0x018d) << 1) | (bVal << 6) |
((line1 >> (7 - k)) & 0x0080) |
((refs[0] >> (9 - k)) & 0x0020) |
((refs[1] >> (11 - k)) & 0x0004) |
((refs[2] >> (13 - k)) & 0x0001);
}
row_write[w / 8] = cVal;
}
}
}
return GRREG;
}
std::array<pdfium::span<const uint8_t>, 3> CJBig2_GRRDProc::GetRowRefs(
uint32_t h) const {
return {
GRREFERENCE->GetLine(h - 1),
GRREFERENCE->GetLine(h),
GRREFERENCE->GetLine(h + 1),
};
}
std::array<pdfium::span<const uint8_t>, 3> CJBig2_GRRDProc::GetRowRefsDy(
uint32_t h) const {
return {
GRREFERENCE->GetLine(h - GRREFERENCEDY - 1),
GRREFERENCE->GetLine(h - GRREFERENCEDY),
GRREFERENCE->GetLine(h - GRREFERENCEDY + 1),
};
}
bool CJBig2_GRRDProc::TypicalPrediction(
int x,
int val,
pdfium::span<pdfium::span<const uint8_t>, 3> row_refs) const {
return (val == GRREFERENCE->GetPixel(x - 1, row_refs[0])) &&
(val == GRREFERENCE->GetPixel(x, row_refs[0])) &&
(val == GRREFERENCE->GetPixel(x + 1, row_refs[0])) &&
(val == GRREFERENCE->GetPixel(x - 1, row_refs[1])) &&
(val == GRREFERENCE->GetPixel(x + 1, row_refs[1])) &&
(val == GRREFERENCE->GetPixel(x - 1, row_refs[2])) &&
(val == GRREFERENCE->GetPixel(x, row_refs[2])) &&
(val == GRREFERENCE->GetPixel(x + 1, row_refs[2]));
}