TIFF predictor

This commit is contained in:
Jakub Melka 2019-10-05 17:38:15 +02:00
parent f8d72d1960
commit f443aec09c
4 changed files with 100 additions and 5 deletions

View File

@ -77,12 +77,12 @@ PDFImage PDFImage::createImage(const PDFDocument* document, const PDFStream* str
if (isSoftMask && (imageMask || dictionary->hasKey("Mask") || dictionary->hasKey("SMask")))
{
throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Soft mask image can't have mask / soft mask itself."));
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Soft mask image can't have mask / soft mask itself."));
}
if (!isSoftMask && !matte.empty())
{
throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Regular image can't have Matte entry (used for soft masks)."));
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Regular image can't have Matte entry (used for soft masks)."));
}
// Fill Mask

View File

@ -800,9 +800,29 @@ QByteArray PDFStreamPredictor::applyTIFFPredictor(const QByteArray& data) const
{
Q_UNUSED(data);
// TODO: Implement TIFF algorithm filter
throw PDFException(PDFTranslationContext::tr("Invalid predictor algorithm."));
return QByteArray();
PDFBitWriter writer(m_bitsPerComponent);
PDFBitReader reader(&data, m_bitsPerComponent);
writer.reserve(data.size());
std::vector<uint32_t> leftValues(m_components, 0);
while (!reader.isAtEnd())
{
for (int i = 0; i < m_columns; ++i)
{
for (int componentIndex = 0; componentIndex < m_components; ++componentIndex)
{
leftValues[componentIndex] = (leftValues[componentIndex] + reader.read()) & reader.max();
writer.write(leftValues[componentIndex]);
}
}
std::fill(leftValues.begin(), leftValues.end(), 0);
reader.alignToBytes();
writer.finishLine();
}
return writer.takeByteArray();
}
QByteArray PDFCryptFilter::apply(const QByteArray& data,

View File

@ -84,4 +84,48 @@ bool PDFBitReader::isAtEnd() const
return (m_position >= m_stream->size());
}
PDFBitWriter::PDFBitWriter(Value bitsPerComponent) :
m_bitsPerComponent(bitsPerComponent),
m_mask((static_cast<Value>(1) << m_bitsPerComponent) - static_cast<Value>(1)),
m_buffer(0),
m_bitsInBuffer(0)
{
}
void PDFBitWriter::write(Value value)
{
m_buffer = (m_buffer << m_bitsPerComponent) | (value & m_mask);
m_bitsInBuffer += m_bitsPerComponent;
flush(false);
}
void PDFBitWriter::flush(bool alignToByteBoundary)
{
if (m_bitsInBuffer >= 8)
{
Value remainder = m_bitsInBuffer % 8;
Value alignedBuffer = m_buffer >> remainder;
Value bitsToWrite = m_bitsInBuffer - remainder;
Value bytesToWrite = bitsToWrite / 8;
for (Value byteIndex = 0; byteIndex < bytesToWrite; ++byteIndex)
{
const Value shift = (bytesToWrite - 1 - byteIndex) * 8;
m_outputByteArray.push_back(static_cast<const char>(static_cast<uint8_t>((alignedBuffer >> shift) & 0xFF)));
}
m_bitsInBuffer = remainder;
}
if (alignToByteBoundary && m_bitsInBuffer > 0)
{
Value missingBits = 8 - m_bitsInBuffer % 8;
m_buffer = m_buffer << missingBits;
m_bitsInBuffer += missingBits;
flush(false);
}
}
} // namespace pdf

View File

@ -128,6 +128,37 @@ private:
Value m_bitsInBuffer;
};
/// Bit writer
class PDFBitWriter
{
public:
using Value = uint64_t;
explicit PDFBitWriter(Value bitsPerComponent);
/// Writes value to the output stream
void write(Value value);
/// Finish line - align to byte boundary
void finishLine() { flush(true); }
/// Returns the result byte array
QByteArray takeByteArray() { return qMove(m_outputByteArray); }
/// Reserve memory in buffer
void reserve(int size) { m_outputByteArray.reserve(size); }
private:
void flush(bool alignToByteBoundary);
QByteArray m_outputByteArray;
Value m_bitsPerComponent;
Value m_mask;
Value m_buffer;
Value m_bitsInBuffer;
};
/// Simple class guard, for properly saving/restoring new/old value. In the constructor,
/// new value is stored in the pointer (old one is being saved), and in the destructor,
/// old value is restored. This object assumes, that value is not a null pointer.