Commit 9a487202463c2bf05fc8fce8ae6a1005348a69a0
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.
Showing
2 changed files
with
123 additions
and
37 deletions
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 | }; | ... | ... |