aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCasey Rojas <crojas@infotechfl.com>2017-11-07 18:12:18 +0100
committerJay Berkenbilt <ejb@ql.org>2017-12-25 04:59:51 +0100
commit9a487202463c2bf05fc8fce8ae6a1005348a69a0 (patch)
tree2d10dbc275f29996586580e6c77da179893ed7f7
parentd83f8f3bfab7b150b6d7c566c312a7142efb8e16 (diff)
downloadqpdf-9a487202463c2bf05fc8fce8ae6a1005348a69a0.tar.zst
Initial implementation of other PNG decode filters
Initial implementation provided by Casey Rojas <crojas@infotechfl.com> Some problems are fixed in a subsequent commit.
-rw-r--r--libqpdf/Pl_PNGFilter.cc143
-rw-r--r--libqpdf/qpdf/Pl_PNGFilter.hh17
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 <stdexcept>
#include <string.h>
#include <limits.h>
+#include <math.h>
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,40 +84,125 @@ 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()
{
// For now, hard-code to using UP filter.
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 <qpdf/Pipeline.hh>
@@ -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;
};