JBIG2 - bitmap decoding

This commit is contained in:
Jakub Melka
2019-10-28 17:39:22 +01:00
parent 6a0cdec4e4
commit 34371706aa
5 changed files with 605 additions and 23 deletions

View File

@ -55,7 +55,7 @@ struct PDFCCITTFaxDecoderParameters
/// to achieve byte alignment.
bool hasEncodedByteAlign = false;
/// Flag indicating, that filter excepts the data be terminated by end of block bit pattern.
/// Flag indicating, that filter expects the data be terminated by end of block bit pattern.
/// In this case, \p rows parameter is ignored. Otherwise, rows parameter is used, or image
/// is terminated by end of data stream, whichever occurs first. The end of block is marked
/// as end-of-facsimile block (EOFB), or return to control (RTC), according the K parameter.

View File

@ -17,6 +17,7 @@
#include "pdfjbig2decoder.h"
#include "pdfexception.h"
#include "pdfccittfaxdecoder.h"
namespace pdf
{
@ -606,12 +607,12 @@ PDFImageData PDFJBIG2Decoder::decode(PDFImageData::MaskingType maskingType)
{
PDFBitWriter writer(1);
const size_t columns = m_pageBitmap.getWidth();
const size_t rows = m_pageBitmap.getHeight();
const int columns = m_pageBitmap.getWidth();
const int rows = m_pageBitmap.getHeight();
for (size_t row = 0; row < rows; ++row)
for (int row = 0; row < rows; ++row)
{
for (size_t column = 0; column < columns; ++column)
for (int column = 0; column < columns; ++column)
{
writer.write(m_pageBitmap.getPixel(column, row));
}
@ -734,7 +735,87 @@ void PDFJBIG2Decoder::processHalftoneRegion(const PDFJBIG2SegmentHeader& header)
void PDFJBIG2Decoder::processGenericRegion(const PDFJBIG2SegmentHeader& header)
{
// TODO: JBIG2 - processGenericRegion
const int segmentStartPosition = m_reader.getPosition();
PDFJBIG2RegionSegmentInformationField field = readRegionSegmentInformationField();
const uint8_t flags = m_reader.readUnsignedByte();
PDFJBIG2BitmapDecodingParameters parameters;
parameters.MMR = flags & 0b0001;
parameters.TPGDON = flags & 0b1000;
parameters.GBTEMPLATE = (flags >> 1) & 0b0011;
if ((flags & 0b11110000) != 0)
{
throw PDFException(PDFTranslationContext::tr("JBIG2 - malformed generic region flags."));
}
if (!parameters.MMR)
{
// We will use arithmetic coding, read template pixels and reset arithmetic coder state
parameters.ATXY = readATTemplatePixelPositions((parameters.GBTEMPLATE == 0) ? 4 : 1);
resetArithmeticStatesGeneric(parameters.GBTEMPLATE);
}
// Determine segment data length
const int segmentDataStartPosition = m_reader.getPosition();
const int segmentHeaderBytes = segmentDataStartPosition - segmentStartPosition;
int segmentDataBytes = 0;
if (header.isSegmentDataLengthDefined())
{
segmentDataBytes = header.getSegmentDataLength() - segmentHeaderBytes;
}
else
{
// We must find byte sequence { 0x00, 0x00 } for MMR and { 0xFF, 0xAC } for arithmetic decoder
const QByteArray* stream = m_reader.getStream();
QByteArray endSequence(2, 0);
if (!parameters.MMR)
{
endSequence[0] = char(0xFF);
endSequence[1] = char(0xAC);
}
int endPosition = stream->indexOf(endSequence);
if (endPosition == -1)
{
throw PDFException(PDFTranslationContext::tr("JBIG2 - end of data byte sequence not found for generic region."));
}
// Add end bytes (they are also a part of stream)
endPosition += endSequence.size();
segmentDataBytes = endPosition - segmentDataStartPosition;
}
parameters.data = m_reader.getStream()->mid(segmentDataStartPosition, segmentDataBytes);
parameters.width = field.width;
parameters.height = field.height;
parameters.arithmeticDecoderState = &m_arithmeticDecoderStates[Generic];
PDFJBIG2Bitmap bitmap = readBitmap(parameters);
if (bitmap.isValid())
{
if (header.isImmediate())
{
m_pageBitmap.paint(bitmap, field.offsetX, field.offsetY, field.operation, m_pageSizeUndefined, m_pageDefaultPixelValue);
}
else
{
m_segments[header.getSegmentNumber()] = std::make_unique<PDFJBIG2Bitmap>(qMove(bitmap));
}
}
else
{
throw PDFException(PDFTranslationContext::tr("JBIG2 - invalid bitmap for generic region."));
}
// Now skip the data
m_reader.skipBytes(segmentDataBytes);
if (header.isImmediate() && !header.isSegmentDataLengthDefined())
{
m_reader.skipBytes(4);
}
}
void PDFJBIG2Decoder::processGenericRefinementRegion(const PDFJBIG2SegmentHeader& header)
@ -782,6 +863,7 @@ void PDFJBIG2Decoder::processPageInformation(const PDFJBIG2SegmentHeader&)
const uint32_t correctedWidth = width;
const uint32_t correctedHeight = (height != 0xFFFFFFFF) ? height : 0;
m_pageSizeUndefined = height == 0xFFFFFFFF;
checkBitmapSize(correctedWidth);
checkBitmapSize(correctedHeight);
@ -901,6 +983,289 @@ void PDFJBIG2Decoder::processExtension(const PDFJBIG2SegmentHeader& header)
}
}
PDFJBIG2Bitmap PDFJBIG2Decoder::readBitmap(const PDFJBIG2BitmapDecodingParameters& parameters)
{
if (parameters.MMR)
{
// Use modified-modified-read (it corresponds to CCITT 2D encoding)
PDFCCITTFaxDecoderParameters ccittParameters;
ccittParameters.K = -1;
ccittParameters.columns = parameters.width;
ccittParameters.rows = parameters.height;
ccittParameters.hasEndOfBlock = false;
PDFCCITTFaxDecoder decoder(&parameters.data, ccittParameters);
PDFImageData data = decoder.decode();
PDFJBIG2Bitmap bitmap(data.getWidth(), data.getHeight(), m_pageDefaultPixelValue);
// Copy the data
PDFBitReader reader(&data.getData(), data.getBitsPerComponent());
for (unsigned int row = 0; row < data.getHeight(); ++row)
{
for (unsigned int column = 0; column < data.getWidth(); ++column)
{
bitmap.setPixel(column, row, (reader.read()) ? 0xFF : 0x00);
}
reader.alignToBytes();
}
return bitmap;
}
else
{
// Use arithmetic encoding. For templates, we fill bytes from right to left, from bottom to top bits,
// filling from lowest bit to highest bit. We will have a maximum of 16 bits.
uint8_t LTP = 0;
uint16_t LTPContext = 0;
if (parameters.TPGDON)
{
switch (parameters.GBTEMPLATE)
{
case 0:
LTPContext = 0b1010010011011001; // 16-bit context, hexadecimal value is 0x9B25
break;
case 1:
LTPContext = 0b0011110010101; // 13-bit context, hexadecimal value is 0x0795
break;
case 2:
LTPContext = 0b0011100101; // 10-bit context, hexadecimal value is 0x00E5
break;
case 3:
LTPContext = 0b0110010101; // 10-bit context, hexadecimal value is 0x0195
break;
default:
Q_ASSERT(false);
break;
}
}
PDFBitReader reader(&parameters.data, 1);
PDFJBIG2ArithmeticDecoder decoder(&reader);
decoder.initialize();
PDFJBIG2Bitmap bitmap(parameters.width, parameters.height, 0x00);
for (int y = 0; y < parameters.height; ++y)
{
// Check TPGDON prediction - if we use same pixels as in previous line
if (parameters.TPGDON)
{
LTP = LTP ^ decoder.readBit(LTPContext, parameters.arithmeticDecoderState);
if (LTP)
{
if (y > 0)
{
bitmap.copyRow(y, y - 1);
}
continue;
}
}
for (int x = 0; x < parameters.width; ++x)
{
// Check, if we have to skip pixel. Pixel should be set to 0, but it is done
// in the initialization of the bitmap.
if (parameters.SKIP && parameters.SKIP->getPixelSafe(x, y))
{
continue;
}
uint16_t pixelContext = 0;
uint16_t pixelContextShift = 0;
auto createContextBit = [&](int offsetX, int offsetY)
{
uint16_t bit = bitmap.getPixelSafe(offsetX, offsetY) ? 1 : 0;
bit = bit << pixelContextShift;
pixelContext |= bit;
++pixelContextShift;
};
// Create pixel context based on used template
switch (parameters.GBTEMPLATE)
{
case 0:
{
// 16-bit context
createContextBit(x - 1, y);
createContextBit(x - 2, y);
createContextBit(x - 3, y);
createContextBit(x - 4, y);
createContextBit(x + parameters.ATXY[0].x, y + parameters.ATXY[0].y);
createContextBit(x + 2, y - 1);
createContextBit(x + 1, y - 1);
createContextBit(x + 0, y - 1);
createContextBit(x - 1, y - 1);
createContextBit(x - 2, y - 1);
createContextBit(x + parameters.ATXY[1].x, y + parameters.ATXY[1].y);
createContextBit(x + parameters.ATXY[2].x, y + parameters.ATXY[2].y);
createContextBit(x + 1, y - 2);
createContextBit(x + 0, y - 2);
createContextBit(x - 1, y - 2);
createContextBit(x + parameters.ATXY[3].x, y + parameters.ATXY[3].y);
break;
}
case 1:
{
// 13-bit context
createContextBit(x - 1, y);
createContextBit(x - 2, y);
createContextBit(x - 3, y);
createContextBit(x + parameters.ATXY[0].x, y + parameters.ATXY[0].y);
createContextBit(x + 2, y - 1);
createContextBit(x + 1, y - 1);
createContextBit(x + 0, y - 1);
createContextBit(x - 1, y - 1);
createContextBit(x - 2, y - 1);
createContextBit(x + 2, y - 2);
createContextBit(x + 1, y - 2);
createContextBit(x + 0, y - 2);
createContextBit(x - 1, y - 2);
break;
}
case 2:
{
// 10-bit context
createContextBit(x - 1, y);
createContextBit(x - 2, y);
createContextBit(x + parameters.ATXY[0].x, y + parameters.ATXY[0].y);
createContextBit(x + 1, y - 1);
createContextBit(x + 0, y - 1);
createContextBit(x - 1, y - 1);
createContextBit(x - 2, y - 1);
createContextBit(x + 1, y - 2);
createContextBit(x + 0, y - 2);
createContextBit(x - 1, y - 2);
break;
}
case 3:
{
// 10-bit context
createContextBit(x - 1, y);
createContextBit(x - 2, y);
createContextBit(x - 3, y);
createContextBit(x - 4, y);
createContextBit(x + parameters.ATXY[0].x, y + parameters.ATXY[0].y);
createContextBit(x + 1, y - 1);
createContextBit(x + 0, y - 1);
createContextBit(x - 1, y - 1);
createContextBit(x - 2, y - 1);
createContextBit(x - 3, y - 1);
break;
}
default:
{
Q_ASSERT(false);
break;
}
}
bitmap.setPixel(x, y, (decoder.readBit(pixelContext, parameters.arithmeticDecoderState)) ? 0xFF : 0x00);
}
}
return bitmap;
}
return PDFJBIG2Bitmap();
}
PDFJBIG2RegionSegmentInformationField PDFJBIG2Decoder::readRegionSegmentInformationField()
{
PDFJBIG2RegionSegmentInformationField result;
result.width = m_reader.readUnsignedInt();
result.height = m_reader.readUnsignedInt();
result.offsetX = m_reader.readUnsignedInt();
result.offsetY = m_reader.readUnsignedInt();
// Parse flags
const uint8_t flags = m_reader.readUnsignedByte();
if ((flags & 0b11111000) != 0)
{
// This is forbidden by the specification
throw PDFException(PDFTranslationContext::tr("JBIG2 region segment information flags are invalid."));
}
switch (flags)
{
case 0:
result.operation = PDFJBIG2BitOperation::Or;
break;
case 1:
result.operation = PDFJBIG2BitOperation::And;
break;
case 2:
result.operation = PDFJBIG2BitOperation::Xor;
break;
case 3:
result.operation = PDFJBIG2BitOperation::NotXor;
break;
case 4:
result.operation = PDFJBIG2BitOperation::Replace;
break;
default:
throw PDFException(PDFTranslationContext::tr("JBIG2 region segment information - invalid bit operation mode."));
}
checkRegionSegmentInformationField(result);
return result;
}
PDFJBIG2ATPositions PDFJBIG2Decoder::readATTemplatePixelPositions(int count)
{
PDFJBIG2ATPositions result = { };
for (int i = 0; i < count; ++i)
{
result[i].x = m_reader.readSignedByte();
result[i].y = m_reader.readSignedByte();
}
return result;
}
void PDFJBIG2Decoder::resetArithmeticStatesGeneric(const uint8_t templateMode)
{
uint8_t bits = 0;
switch (templateMode)
{
case 0:
bits = 16;
break;
case 1:
bits = 13;
break;
case 2:
case 3:
bits = 10;
break;
default:
Q_ASSERT(false);
break;
}
m_arithmeticDecoderStates[Generic].reset(bits);
}
void PDFJBIG2Decoder::skipSegment(const PDFJBIG2SegmentHeader& header)
{
if (header.isSegmentDataLengthDefined())
@ -921,6 +1286,24 @@ void PDFJBIG2Decoder::checkBitmapSize(const uint32_t size)
}
}
void PDFJBIG2Decoder::checkRegionSegmentInformationField(const PDFJBIG2RegionSegmentInformationField& field)
{
checkBitmapSize(field.width);
checkBitmapSize(field.height);
checkBitmapSize(field.offsetX);
checkBitmapSize(field.offsetY);
if (field.width == 0 || field.height == 0)
{
throw PDFException(PDFTranslationContext::tr("JBIG2 invalid bitmap size (%1 x %2).").arg(field.width).arg(field.height));
}
if (field.operation == PDFJBIG2BitOperation::Invalid)
{
throw PDFException(PDFTranslationContext::tr("JBIG2 invalid bit operation."));
}
}
PDFJBIG2Bitmap::PDFJBIG2Bitmap() :
m_width(0),
m_height(0)
@ -928,20 +1311,94 @@ PDFJBIG2Bitmap::PDFJBIG2Bitmap() :
}
PDFJBIG2Bitmap::PDFJBIG2Bitmap(size_t width, size_t height) :
PDFJBIG2Bitmap::PDFJBIG2Bitmap(int width, int height) :
m_width(width),
m_height(height)
{
m_data.resize(width * height, 0);
}
PDFJBIG2Bitmap::PDFJBIG2Bitmap(size_t width, size_t height, uint8_t fill) :
PDFJBIG2Bitmap::PDFJBIG2Bitmap(int width, int height, uint8_t fill) :
m_width(width),
m_height(height)
{
m_data.resize(width * height, fill);
}
void PDFJBIG2Bitmap::paint(const PDFJBIG2Bitmap& bitmap, int offsetX, int offsetY, PDFJBIG2BitOperation operation, bool expandY, const uint8_t expandPixel)
{
if (!bitmap.isValid())
{
return;
}
// Expand, if it is allowed and target bitmap has too low height
if (expandY && offsetY + bitmap.getHeight() > m_height)
{
m_height = offsetY + bitmap.getHeight();
m_data.resize(getPixelCount(), expandPixel);
}
// Check out pathological cases
if (offsetX >= m_width || offsetY >= m_height)
{
return;
}
const int targetStartX = offsetX;
const int targetEndX = qMin(offsetX + bitmap.getWidth(), m_width);
const int targetStartY = offsetY;
const int targetEndY = qMin(offsetY + bitmap.getHeight(), m_height);
for (int targetY = targetStartY; targetY < targetEndY; ++targetY)
{
for (int targetX = targetStartX; targetX < targetEndX; ++targetX)
{
const int sourceX = targetX - targetStartX;
const int sourceY = targetY - targetStartY;
switch (operation)
{
case PDFJBIG2BitOperation::Or:
setPixel(targetX, targetY, getPixel(targetX, targetY) | bitmap.getPixel(sourceX, sourceY));
break;
case PDFJBIG2BitOperation::And:
setPixel(targetX, targetY, getPixel(targetX, targetY) & bitmap.getPixel(sourceX, sourceY));
break;
case PDFJBIG2BitOperation::Xor:
setPixel(targetX, targetY, getPixel(targetX, targetY) ^ bitmap.getPixel(sourceX, sourceY));
break;
case PDFJBIG2BitOperation::NotXor:
setPixel(targetX, targetY, getPixel(targetX, targetY) ^ (~bitmap.getPixel(sourceX, sourceY)));
break;
case PDFJBIG2BitOperation::Replace:
setPixel(targetX, targetY, bitmap.getPixel(sourceX, sourceY));
break;
default:
throw PDFException(PDFTranslationContext::tr("JBIG2 - invalid bitmap paint operation."));
}
}
}
}
void PDFJBIG2Bitmap::copyRow(int target, int source)
{
if (target < 0 || target >= m_height || source < 0 || source >= m_height)
{
throw PDFException(PDFTranslationContext::tr("JBIG2 - invalid bitmap copy row operation."));
}
auto itSource = std::next(m_data.cbegin(), source * m_width);
auto itSourceEnd = std::next(itSource, m_width);
auto itTarget = std::next(m_data.begin(), target * m_width);
std::copy(itSource, itSourceEnd, itTarget);
}
PDFJBIG2HuffmanCodeTable::PDFJBIG2HuffmanCodeTable(std::vector<PDFJBIG2HuffmanTableEntry>&& entries) :
m_entries(qMove(entries))
{

View File

@ -35,7 +35,8 @@ enum class PDFJBIG2BitOperation
Or,
And,
Xor,
NotXor
NotXor,
Replace
};
/// Arithmetic decoder state for JBIG2 data streams. It contains state for context,
@ -45,12 +46,24 @@ enum class PDFJBIG2BitOperation
class PDFJBIG2ArithmeticDecoderState
{
public:
explicit inline PDFJBIG2ArithmeticDecoderState() = default;
explicit inline PDFJBIG2ArithmeticDecoderState(size_t size) :
m_state(size, 0)
{
}
/// Resets the context
inline void reset(const uint8_t bits)
{
size_t size = (1ULL << bits);
std::fill(m_state.begin(), m_state.end(), 0);
if (m_state.size() != size)
{
m_state.resize(size, 0);
}
}
/// Returns row index to Qe value table, according to document ISO/IEC 14492:2001,
/// annex E, table E.1 (Qe values and probability estimation process).
inline uint8_t getQeRowIndex(size_t context) const
@ -86,16 +99,17 @@ private:
class PDFJBIG2ArithmeticDecoder
{
public:
explicit inline PDFJBIG2ArithmeticDecoder() :
explicit inline PDFJBIG2ArithmeticDecoder(PDFBitReader* reader) :
m_c(0),
m_a(0),
m_ct(0),
m_reader(nullptr)
m_reader(reader)
{
}
void initialize() { perform_INITDEC(); }
uint32_t readBit(size_t context, PDFJBIG2ArithmeticDecoderState* state) { return perform_DECODE(context, state); }
private:
/// Performs INITDEC operation as described in the specification
@ -220,17 +234,27 @@ class PDFJBIG2Bitmap : public PDFJBIG2Segment
{
public:
explicit PDFJBIG2Bitmap();
explicit PDFJBIG2Bitmap(size_t width, size_t height);
explicit PDFJBIG2Bitmap(size_t width, size_t height, uint8_t fill);
explicit PDFJBIG2Bitmap(int width, int height);
explicit PDFJBIG2Bitmap(int width, int height, uint8_t fill);
virtual const PDFJBIG2Bitmap* asBitmap() const override { return this; }
virtual PDFJBIG2Bitmap* asBitmap() override { return this; }
inline size_t getWidth() const { return m_width; }
inline size_t getHeight() const { return m_height; }
inline size_t getPixelCount() const { return m_width * m_height; }
inline uint8_t getPixel(size_t x, size_t y) const { return m_data[y * m_width + x]; }
inline void setPixel(size_t x, size_t y, uint8_t value) { m_data[y * m_width + x] = value; }
inline int getWidth() const { return m_width; }
inline int getHeight() const { return m_height; }
inline int getPixelCount() const { return m_width * m_height; }
inline uint8_t getPixel(int x, int y) const { return m_data[y * m_width + x]; }
inline void setPixel(int x, int y, uint8_t value) { m_data[y * m_width + x] = value; }
inline uint8_t getPixelSafe(int x, int y) const
{
if (x < 0 || x >= m_width || y < 0 || y >= m_height)
{
return 0;
}
return getPixel(x, y);
}
inline void fill(uint8_t value) { std::fill(m_data.begin(), m_data.end(), value); }
inline void fillZero() { fill(0); }
@ -238,12 +262,79 @@ public:
inline bool isValid() const { return getPixelCount() > 0; }
/// Paints another bitmap onto this bitmap. If bitmap is invalid, nothing is done.
/// If \p expandY is true, height of target bitmap is expanded to fit source draw area.
/// \param bitmap Bitmap to be painted on this
/// \param offsetX Horizontal offset of paint area
/// \param offsetY Vertical offset of paint area
/// \param operation Paint operation to be performed
/// \param expandY Expand vertically, if painted bitmap exceeds current bitmap area
/// \param expandPixel Initialize pixels by this value during expanding
void paint(const PDFJBIG2Bitmap& bitmap, int offsetX, int offsetY, PDFJBIG2BitOperation operation, bool expandY, const uint8_t expandPixel);
/// Copies data from source row to target row. If source or target row doesn't exists,
/// then exception is thrown.
/// \param target Target row
/// \param source Source row
void copyRow(int target, int source);
private:
size_t m_width;
size_t m_height;
int m_width;
int m_height;
std::vector<uint8_t> m_data;
};
/// Region segment information field, see chapter 7.4.1 in the specification
struct PDFJBIG2RegionSegmentInformationField
{
uint32_t width = 0;
uint32_t height = 0;
uint32_t offsetX = 0;
uint32_t offsetY = 0;
PDFJBIG2BitOperation operation = PDFJBIG2BitOperation::Invalid;
};
/// Info structure for adaptative template
struct PDFJBIG2ATPosition
{
int8_t x = 0;
int8_t y = 0;
};
using PDFJBIG2ATPositions = std::array<PDFJBIG2ATPosition, 4>;
/// Info structure for bitmap decoding parameters
struct PDFJBIG2BitmapDecodingParameters
{
/// Is Modified-Modified-Read encoding used? This encoding is simalr to CCITT pure 2D encoding.
bool MMR = false;
/// Is typical prediction for generic direct coding used?
bool TPGDON = false;
/// Width of the image
int width = 0;
/// Height of the image
int height = 0;
/// Template mode (not used for MMR).
uint8_t GBTEMPLATE = 0;
/// Positions of adaptative pixels
PDFJBIG2ATPositions ATXY = { };
/// Data with encoded image
QByteArray data;
/// State of arithmetic decoder
PDFJBIG2ArithmeticDecoderState* arithmeticDecoderState = nullptr;
/// Skip bitmap (pixel is skipped if corresponding pixel in the
/// skip bitmap is 1). Set to nullptr, if not used.
const PDFJBIG2Bitmap* SKIP = nullptr;
};
/// Decoder of JBIG2 data streams. Decodes the black/white monochrome image.
/// Handles also global segments. Decoder decodes data using the specification
/// ISO/IEC 14492:2001, T.88.
@ -257,7 +348,9 @@ public:
m_reader(nullptr, 8),
m_pageDefaultPixelValue(0),
m_pageDefaultCompositionOperator(PDFJBIG2BitOperation::Invalid),
m_pageDefaultCompositionOperatorOverriden(false)
m_pageDefaultCompositionOperatorOverriden(false),
m_pageSizeUndefined(false),
m_arithmeticDecoderStates()
{
}
@ -270,6 +363,12 @@ public:
private:
static constexpr const uint32_t MAX_BITMAP_SIZE = 65536;
enum ArithmeticDecoderStates
{
Generic,
EndState
};
/// Processes current data stream (reads all data from the stream, interprets
/// them as segments and processes the segments).
void processStream();
@ -288,21 +387,35 @@ private:
void processCodeTables(const PDFJBIG2SegmentHeader& header);
void processExtension(const PDFJBIG2SegmentHeader& header);
/// Reads the bitmap using decoding parameters
/// \param parameters Decoding parameters
PDFJBIG2Bitmap readBitmap(const PDFJBIG2BitmapDecodingParameters& parameters);
/// Reads the region segment information field (see chapter 7.4.1)
PDFJBIG2RegionSegmentInformationField readRegionSegmentInformationField();
/// Read adaptative pixel template positions, positions, which are not read, are filled with 0
PDFJBIG2ATPositions readATTemplatePixelPositions(int count);
/// Reset arithmetic decoder stats for generic
void resetArithmeticStatesGeneric(const uint8_t templateMode);
void skipSegment(const PDFJBIG2SegmentHeader& header);
static void checkBitmapSize(const uint32_t size);
static void checkRegionSegmentInformationField(const PDFJBIG2RegionSegmentInformationField& field);
QByteArray m_data;
QByteArray m_globalData;
PDFRenderErrorReporter* m_errorReporter;
PDFBitReader m_reader;
std::map<uint32_t, std::unique_ptr<PDFJBIG2Segment>> m_segments;
/// Page default pixel value
uint8_t m_pageDefaultPixelValue;
PDFJBIG2BitOperation m_pageDefaultCompositionOperator;
bool m_pageDefaultCompositionOperatorOverriden;
bool m_pageSizeUndefined;
PDFJBIG2Bitmap m_pageBitmap;
std::array<PDFJBIG2ArithmeticDecoderState, EndState> m_arithmeticDecoderStates;
};
} // namespace pdf

View File

@ -126,6 +126,12 @@ int32_t PDFBitReader::readSignedInt()
return *reinterpret_cast<const int32_t*>(&value);
}
int8_t PDFBitReader::readSignedByte()
{
const uint8_t value = read(8);
return *reinterpret_cast<const int8_t*>(&value);
}
PDFBitWriter::PDFBitWriter(Value bitsPerComponent) :
m_bitsPerComponent(bitsPerComponent),
m_mask((static_cast<Value>(1) << m_bitsPerComponent) - static_cast<Value>(1)),

View File

@ -138,6 +138,9 @@ public:
/// Reads signed 32-bit integer from the stream
int32_t readSignedInt();
/// Reads signed 8-bit integer from the stream
int8_t readSignedByte();
/// Reads unsigned 32-bit integer from the stream
uint32_t readUnsignedInt() { return read(32); }
@ -147,6 +150,9 @@ public:
/// Reads unsigned 8-bit integer from the stream
uint8_t readUnsignedByte() { return read(8); }
/// Return underlying byte stream
const QByteArray* getStream() const { return m_stream; }
private:
const QByteArray* m_stream;
int m_position;