Commit 9a487202463c2bf05fc8fce8ae6a1005348a69a0

Authored by Casey Rojas
Committed by Jay Berkenbilt
1 parent d83f8f3b

Initial implementation of other PNG decode filters

Initial implementation provided by Casey Rojas <crojas@infotechfl.com>
Some problems are fixed in a subsequent commit.
libqpdf/Pl_PNGFilter.cc
... ... @@ -2,6 +2,7 @@
2 2 #include <stdexcept>
3 3 #include <string.h>
4 4 #include <limits.h>
  5 +#include <math.h>
5 6  
6 7 Pl_PNGFilter::Pl_PNGFilter(char const* identifier, Pipeline* next,
7 8 action_e action, unsigned int columns,
... ... @@ -13,6 +14,7 @@ Pl_PNGFilter::Pl_PNGFilter(char const* identifier, Pipeline* next,
13 14 prev_row(0),
14 15 buf1(0),
15 16 buf2(0),
  17 + bytes_per_pixel(bytes_per_pixel),
16 18 pos(0)
17 19 {
18 20 if ((columns == 0) || (columns > UINT_MAX - 1))
... ... @@ -82,40 +84,125 @@ Pl_PNGFilter::decodeRow()
82 84 int filter = this->cur_row[0];
83 85 if (this->prev_row)
84 86 {
85   - switch (filter)
86   - {
87   - case 0: // none
88   - break;
89   -
90   - case 1: // sub
91   - throw std::logic_error("sub filter not implemented");
92   - break;
93   -
94   - case 2: // up
95   - for (unsigned int i = 1; i <= this->columns; ++i)
96   - {
97   - this->cur_row[i] += this->prev_row[i];
98   - }
99   - break;
100   -
101   - case 3: // average
102   - throw std::logic_error("average filter not implemented");
103   - break;
104   -
105   - case 4: // Paeth
106   - throw std::logic_error("Paeth filter not implemented");
107   - break;
108   -
109   - default:
110   - // ignore
111   - break;
112   - }
  87 + switch (filter)
  88 + {
  89 + case 0:
  90 + break;
  91 + case 1:
  92 + this->decodeSub();
  93 + break;
  94 + case 2:
  95 + this->decodeUp();
  96 + break;
  97 + case 3:
  98 + this->decodeAverage();
  99 + break;
  100 + case 4:
  101 + this->decodePaeth();
  102 + break;
  103 + default:
  104 + // ignore
  105 + break;
  106 + }
113 107 }
114 108  
115 109 getNext()->write(this->cur_row + 1, this->columns);
116 110 }
117 111  
118 112 void
  113 +Pl_PNGFilter::decodeSub()
  114 +{
  115 + unsigned char* buffer = this->cur_row + 1;
  116 + unsigned int bpp = this->bytes_per_pixel != 0 ? this->bytes_per_pixel : 1;
  117 +
  118 + for (unsigned int i = 0; i < this->columns; ++i)
  119 + {
  120 + unsigned char left = 0;
  121 +
  122 + if (i >= bpp)
  123 + {
  124 + left = buffer[i - bpp];
  125 + }
  126 +
  127 + buffer[i] += left;
  128 + }
  129 +}
  130 +
  131 +void
  132 +Pl_PNGFilter::decodeUp()
  133 +{
  134 + unsigned char* buffer = this->cur_row + 1;
  135 + unsigned char* above_buffer = this->prev_row + 1;
  136 +
  137 + for (unsigned int i = 0; i < this->columns; ++i)
  138 + {
  139 + unsigned char up = above_buffer[i];
  140 + buffer[i] += up;
  141 + }
  142 +}
  143 +
  144 +void
  145 +Pl_PNGFilter::decodeAverage()
  146 +{
  147 + unsigned char* buffer = this->cur_row+1;
  148 + unsigned char* above_buffer = this->prev_row+1;
  149 + unsigned int bpp = this->bytes_per_pixel != 0 ? this->bytes_per_pixel : 1;
  150 +
  151 + for (unsigned int i = 0; i < this->columns; ++i)
  152 + {
  153 + int left = 0, up = 0;
  154 +
  155 + if (i >= bpp)
  156 + {
  157 + left = buffer[i - bpp];
  158 + }
  159 +
  160 + up = above_buffer[i];
  161 + buffer[i] += floor((left+up) / 2);
  162 + }
  163 +}
  164 +
  165 +void
  166 +Pl_PNGFilter::decodePaeth()
  167 +{
  168 + unsigned char* buffer = this->cur_row+1;
  169 + unsigned char* above_buffer = this->prev_row+1;
  170 + unsigned int bpp = this->bytes_per_pixel != 0 ? this->bytes_per_pixel : 1;
  171 +
  172 + for (unsigned int i = 0; i < this->columns; ++i)
  173 + {
  174 + int left = 0,
  175 + up = above_buffer[i],
  176 + upper_left = 0;
  177 +
  178 + if (i >= bpp)
  179 + {
  180 + left = buffer[i - bpp];
  181 + upper_left = above_buffer[i - bpp];
  182 + }
  183 +
  184 + buffer[i] += this->PaethPredictor(left, up, upper_left);
  185 + }
  186 +}
  187 +
  188 +int
  189 +Pl_PNGFilter::PaethPredictor(int a, int b, int c)
  190 +{
  191 + int p = a + b - c;
  192 + int pa = std::abs(p - a);
  193 + int pb = std::abs(p - b);
  194 + int pc = std::abs(p - c);
  195 +
  196 + if (pa <= pb && pa <= pc) {
  197 + return a;
  198 + }
  199 + if (pb <= pc) {
  200 + return b;
  201 + }
  202 + return c;
  203 +}
  204 +
  205 +void
119 206 Pl_PNGFilter::encodeRow()
120 207 {
121 208 // For now, hard-code to using UP filter.
... ...
libqpdf/qpdf/Pl_PNGFilter.hh
... ... @@ -4,15 +4,8 @@
4 4 // This pipeline applies or reverses the application of a PNG filter
5 5 // as described in the PNG specification.
6 6  
7   -// NOTE: In its initial implementation, it only encodes and decodes
8   -// filters "none" and "up". The primary motivation of this code is to
9   -// encode and decode PDF 1.5+ XRef streams which are often encoded
10   -// with Flate predictor 12, which corresponds to the PNG up filter.
11   -// At present, the bytes_per_pixel parameter is ignored, and an
12   -// exception is thrown if any row of the file has a filter of other
13   -// than 0 or 2. Finishing the implementation would not be difficult.
14   -// See chapter 6 of the PNG specification for a description of the
15   -// filter algorithms.
  7 +// NOTE: In its current implementation, this filter always encodes
  8 +// using the "up" filter, but it decodes all the filters.
16 9  
17 10 #include <qpdf/Pipeline.hh>
18 11  
... ... @@ -35,9 +28,14 @@ class Pl_PNGFilter: public Pipeline
35 28 virtual void finish();
36 29  
37 30 private:
  31 + void decodeSub();
  32 + void decodeUp();
  33 + void decodeAverage();
  34 + void decodePaeth();
38 35 void processRow();
39 36 void encodeRow();
40 37 void decodeRow();
  38 + int PaethPredictor(int a, int b, int c);
41 39  
42 40 action_e action;
43 41 unsigned int columns;
... ... @@ -45,6 +43,7 @@ class Pl_PNGFilter: public Pipeline
45 43 unsigned char* prev_row;
46 44 unsigned char* buf1;
47 45 unsigned char* buf2;
  46 + unsigned int bytes_per_pixel;
48 47 size_t pos;
49 48 size_t incoming;
50 49 };
... ...