From 9a487202463c2bf05fc8fce8ae6a1005348a69a0 Mon Sep 17 00:00:00 2001 From: Casey Rojas Date: Tue, 7 Nov 2017 12:12:18 -0500 Subject: Initial implementation of other PNG decode filters Initial implementation provided by Casey Rojas Some problems are fixed in a subsequent commit. --- libqpdf/Pl_PNGFilter.cc | 143 ++++++++++++++++++++++++++++++++++--------- libqpdf/qpdf/Pl_PNGFilter.hh | 17 +++-- 2 files changed, 123 insertions(+), 37 deletions(-) diff --git a/libqpdf/Pl_PNGFilter.cc b/libqpdf/Pl_PNGFilter.cc index 78398736..edcc34cb 100644 --- a/libqpdf/Pl_PNGFilter.cc +++ b/libqpdf/Pl_PNGFilter.cc @@ -2,6 +2,7 @@ #include #include #include +#include Pl_PNGFilter::Pl_PNGFilter(char const* identifier, Pipeline* next, action_e action, unsigned int columns, @@ -13,6 +14,7 @@ Pl_PNGFilter::Pl_PNGFilter(char const* identifier, Pipeline* next, prev_row(0), buf1(0), buf2(0), + bytes_per_pixel(bytes_per_pixel), pos(0) { if ((columns == 0) || (columns > UINT_MAX - 1)) @@ -82,39 +84,124 @@ Pl_PNGFilter::decodeRow() int filter = this->cur_row[0]; if (this->prev_row) { - switch (filter) - { - case 0: // none - break; - - case 1: // sub - throw std::logic_error("sub filter not implemented"); - break; - - case 2: // up - for (unsigned int i = 1; i <= this->columns; ++i) - { - this->cur_row[i] += this->prev_row[i]; - } - break; - - case 3: // average - throw std::logic_error("average filter not implemented"); - break; - - case 4: // Paeth - throw std::logic_error("Paeth filter not implemented"); - break; - - default: - // ignore - break; - } + switch (filter) + { + case 0: + break; + case 1: + this->decodeSub(); + break; + case 2: + this->decodeUp(); + break; + case 3: + this->decodeAverage(); + break; + case 4: + this->decodePaeth(); + break; + default: + // ignore + break; + } } getNext()->write(this->cur_row + 1, this->columns); } +void +Pl_PNGFilter::decodeSub() +{ + unsigned char* buffer = this->cur_row + 1; + unsigned int bpp = this->bytes_per_pixel != 0 ? this->bytes_per_pixel : 1; + + for (unsigned int i = 0; i < this->columns; ++i) + { + unsigned char left = 0; + + if (i >= bpp) + { + left = buffer[i - bpp]; + } + + buffer[i] += left; + } +} + +void +Pl_PNGFilter::decodeUp() +{ + unsigned char* buffer = this->cur_row + 1; + unsigned char* above_buffer = this->prev_row + 1; + + for (unsigned int i = 0; i < this->columns; ++i) + { + unsigned char up = above_buffer[i]; + buffer[i] += up; + } +} + +void +Pl_PNGFilter::decodeAverage() +{ + unsigned char* buffer = this->cur_row+1; + unsigned char* above_buffer = this->prev_row+1; + unsigned int bpp = this->bytes_per_pixel != 0 ? this->bytes_per_pixel : 1; + + for (unsigned int i = 0; i < this->columns; ++i) + { + int left = 0, up = 0; + + if (i >= bpp) + { + left = buffer[i - bpp]; + } + + up = above_buffer[i]; + buffer[i] += floor((left+up) / 2); + } +} + +void +Pl_PNGFilter::decodePaeth() +{ + unsigned char* buffer = this->cur_row+1; + unsigned char* above_buffer = this->prev_row+1; + unsigned int bpp = this->bytes_per_pixel != 0 ? this->bytes_per_pixel : 1; + + for (unsigned int i = 0; i < this->columns; ++i) + { + int left = 0, + up = above_buffer[i], + upper_left = 0; + + if (i >= bpp) + { + left = buffer[i - bpp]; + upper_left = above_buffer[i - bpp]; + } + + buffer[i] += this->PaethPredictor(left, up, upper_left); + } +} + +int +Pl_PNGFilter::PaethPredictor(int a, int b, int c) +{ + int p = a + b - c; + int pa = std::abs(p - a); + int pb = std::abs(p - b); + int pc = std::abs(p - c); + + if (pa <= pb && pa <= pc) { + return a; + } + if (pb <= pc) { + return b; + } + return c; +} + void Pl_PNGFilter::encodeRow() { diff --git a/libqpdf/qpdf/Pl_PNGFilter.hh b/libqpdf/qpdf/Pl_PNGFilter.hh index 7d7ebe76..504988eb 100644 --- a/libqpdf/qpdf/Pl_PNGFilter.hh +++ b/libqpdf/qpdf/Pl_PNGFilter.hh @@ -4,15 +4,8 @@ // This pipeline applies or reverses the application of a PNG filter // as described in the PNG specification. -// NOTE: In its initial implementation, it only encodes and decodes -// filters "none" and "up". The primary motivation of this code is to -// encode and decode PDF 1.5+ XRef streams which are often encoded -// with Flate predictor 12, which corresponds to the PNG up filter. -// At present, the bytes_per_pixel parameter is ignored, and an -// exception is thrown if any row of the file has a filter of other -// than 0 or 2. Finishing the implementation would not be difficult. -// See chapter 6 of the PNG specification for a description of the -// filter algorithms. +// NOTE: In its current implementation, this filter always encodes +// using the "up" filter, but it decodes all the filters. #include @@ -35,9 +28,14 @@ class Pl_PNGFilter: public Pipeline virtual void finish(); private: + void decodeSub(); + void decodeUp(); + void decodeAverage(); + void decodePaeth(); void processRow(); void encodeRow(); void decodeRow(); + int PaethPredictor(int a, int b, int c); action_e action; unsigned int columns; @@ -45,6 +43,7 @@ class Pl_PNGFilter: public Pipeline unsigned char* prev_row; unsigned char* buf1; unsigned char* buf2; + unsigned int bytes_per_pixel; size_t pos; size_t incoming; }; -- cgit v1.2.3-54-g00ecf