Commit 53971d50be39448c980d842a1fe1f525b4cee597
1 parent
d9c90497
Add Pl_TIFFPredictor
Showing
12 changed files
with
213 additions
and
3 deletions
libqpdf/Pl_TIFFPredictor.cc
0 → 100644
| 1 | +#include <qpdf/Pl_TIFFPredictor.hh> | |
| 2 | +#include <qpdf/QTC.hh> | |
| 3 | +#include <qpdf/BitStream.hh> | |
| 4 | +#include <qpdf/BitWriter.hh> | |
| 5 | +#include <stdexcept> | |
| 6 | +#include <vector> | |
| 7 | +#include <string.h> | |
| 8 | +#include <limits.h> | |
| 9 | + | |
| 10 | +Pl_TIFFPredictor::Pl_TIFFPredictor(char const* identifier, Pipeline* next, | |
| 11 | + action_e action, unsigned int columns, | |
| 12 | + unsigned int samples_per_pixel, | |
| 13 | + unsigned int bits_per_sample) : | |
| 14 | + Pipeline(identifier, next), | |
| 15 | + action(action), | |
| 16 | + columns(columns), | |
| 17 | + samples_per_pixel(samples_per_pixel), | |
| 18 | + bits_per_sample(bits_per_sample), | |
| 19 | + cur_row(0), | |
| 20 | + pos(0) | |
| 21 | +{ | |
| 22 | + if (samples_per_pixel < 1) | |
| 23 | + { | |
| 24 | + throw std::runtime_error( | |
| 25 | + "TIFFPredictor created with invalid samples_per_pixel"); | |
| 26 | + } | |
| 27 | + if ((bits_per_sample < 1) || | |
| 28 | + (bits_per_sample > (8 * (sizeof(unsigned long long))))) | |
| 29 | + { | |
| 30 | + throw std::runtime_error( | |
| 31 | + "TIFFPredictor created with invalid bits_per_sample"); | |
| 32 | + } | |
| 33 | + unsigned long long bpr = | |
| 34 | + ((columns * bits_per_sample * samples_per_pixel) + 7) / 8; | |
| 35 | + if ((bpr == 0) || (bpr > (UINT_MAX - 1))) | |
| 36 | + { | |
| 37 | + throw std::runtime_error( | |
| 38 | + "TIFFPredictor created with invalid columns value"); | |
| 39 | + } | |
| 40 | + this->bytes_per_row = bpr & UINT_MAX; | |
| 41 | + this->cur_row = new unsigned char[this->bytes_per_row]; | |
| 42 | + memset(this->cur_row, 0, this->bytes_per_row); | |
| 43 | +} | |
| 44 | + | |
| 45 | +Pl_TIFFPredictor::~Pl_TIFFPredictor() | |
| 46 | +{ | |
| 47 | + delete [] cur_row; | |
| 48 | +} | |
| 49 | + | |
| 50 | +void | |
| 51 | +Pl_TIFFPredictor::write(unsigned char* data, size_t len) | |
| 52 | +{ | |
| 53 | + size_t left = this->bytes_per_row - this->pos; | |
| 54 | + size_t offset = 0; | |
| 55 | + while (len >= left) | |
| 56 | + { | |
| 57 | + // finish off current row | |
| 58 | + memcpy(this->cur_row + this->pos, data + offset, left); | |
| 59 | + offset += left; | |
| 60 | + len -= left; | |
| 61 | + | |
| 62 | + processRow(); | |
| 63 | + | |
| 64 | + // Prepare for next row | |
| 65 | + memset(this->cur_row, 0, this->bytes_per_row); | |
| 66 | + left = this->bytes_per_row; | |
| 67 | + this->pos = 0; | |
| 68 | + } | |
| 69 | + if (len) | |
| 70 | + { | |
| 71 | + memcpy(this->cur_row + this->pos, data + offset, len); | |
| 72 | + } | |
| 73 | + this->pos += len; | |
| 74 | +} | |
| 75 | + | |
| 76 | +void | |
| 77 | +Pl_TIFFPredictor::processRow() | |
| 78 | +{ | |
| 79 | + QTC::TC("libtests", "Pl_TIFFPredictor processRow", | |
| 80 | + (action == a_decode ? 0 : 1)); | |
| 81 | + BitWriter bw(this->getNext()); | |
| 82 | + BitStream in(this->cur_row, this->bytes_per_row); | |
| 83 | + std::vector<long long> prev; | |
| 84 | + for (unsigned int i = 0; i < this->samples_per_pixel; ++i) | |
| 85 | + { | |
| 86 | + long long sample = in.getBitsSigned(this->bits_per_sample); | |
| 87 | + bw.writeBitsSigned(sample, this->bits_per_sample); | |
| 88 | + prev.push_back(sample); | |
| 89 | + } | |
| 90 | + for (unsigned int col = 1; col < this->columns; ++col) | |
| 91 | + { | |
| 92 | + for (unsigned int i = 0; i < this->samples_per_pixel; ++i) | |
| 93 | + { | |
| 94 | + long long sample = in.getBitsSigned(this->bits_per_sample); | |
| 95 | + long long new_sample = sample; | |
| 96 | + if (action == a_encode) | |
| 97 | + { | |
| 98 | + new_sample -= prev[i]; | |
| 99 | + prev[i] = sample; | |
| 100 | + } | |
| 101 | + else | |
| 102 | + { | |
| 103 | + new_sample += prev[i]; | |
| 104 | + prev[i] = new_sample; | |
| 105 | + } | |
| 106 | + bw.writeBitsSigned(new_sample, this->bits_per_sample); | |
| 107 | + } | |
| 108 | + } | |
| 109 | + bw.flush(); | |
| 110 | +} | |
| 111 | + | |
| 112 | +void | |
| 113 | +Pl_TIFFPredictor::finish() | |
| 114 | +{ | |
| 115 | + if (this->pos) | |
| 116 | + { | |
| 117 | + // write partial row | |
| 118 | + processRow(); | |
| 119 | + } | |
| 120 | + this->pos = 0; | |
| 121 | + memset(this->cur_row, 0, this->bytes_per_row); | |
| 122 | + getNext()->finish(); | |
| 123 | +} | ... | ... |
libqpdf/build.mk
libqpdf/qpdf/Pl_TIFFPredictor.hh
0 → 100644
| 1 | +#ifndef __PL_TIFFPREDICTOR_HH__ | |
| 2 | +#define __PL_TIFFPREDICTOR_HH__ | |
| 3 | + | |
| 4 | +// This pipeline reverses the application of a TIFF predictor as | |
| 5 | +// described in the TIFF specification. | |
| 6 | + | |
| 7 | +#include <qpdf/Pipeline.hh> | |
| 8 | + | |
| 9 | +class Pl_TIFFPredictor: public Pipeline | |
| 10 | +{ | |
| 11 | + public: | |
| 12 | + enum action_e { a_encode, a_decode }; | |
| 13 | + | |
| 14 | + QPDF_DLL | |
| 15 | + Pl_TIFFPredictor(char const* identifier, Pipeline* next, | |
| 16 | + action_e action, unsigned int columns, | |
| 17 | + unsigned int samples_per_pixel = 1, | |
| 18 | + unsigned int bits_per_sample = 8); | |
| 19 | + QPDF_DLL | |
| 20 | + virtual ~Pl_TIFFPredictor(); | |
| 21 | + | |
| 22 | + QPDF_DLL | |
| 23 | + virtual void write(unsigned char* data, size_t len); | |
| 24 | + QPDF_DLL | |
| 25 | + virtual void finish(); | |
| 26 | + | |
| 27 | + private: | |
| 28 | + void processRow(); | |
| 29 | + | |
| 30 | + action_e action; | |
| 31 | + unsigned int columns; | |
| 32 | + unsigned int bytes_per_row; | |
| 33 | + unsigned int samples_per_pixel; | |
| 34 | + unsigned int bits_per_sample; | |
| 35 | + unsigned char* cur_row; | |
| 36 | + size_t pos; | |
| 37 | +}; | |
| 38 | + | |
| 39 | +#endif // __PL_TIFFPREDICTOR_HH__ | ... | ... |
libtests/libtests.testcov
libtests/predictors.cc
| 1 | 1 | #include <qpdf/Pl_PNGFilter.hh> |
| 2 | +#include <qpdf/Pl_TIFFPredictor.hh> | |
| 2 | 3 | #include <qpdf/Pl_StdioFile.hh> |
| 3 | 4 | #include <qpdf/QUtil.hh> |
| 4 | 5 | |
| ... | ... | @@ -23,6 +24,13 @@ void run(char const* filename, char const* filter, |
| 23 | 24 | encode ? Pl_PNGFilter::a_encode : Pl_PNGFilter::a_decode, |
| 24 | 25 | columns, samples_per_pixel, bits_per_sample); |
| 25 | 26 | } |
| 27 | + else if (strcmp(filter, "tiff") == 0) | |
| 28 | + { | |
| 29 | + pl = new Pl_TIFFPredictor( | |
| 30 | + "png", out, | |
| 31 | + encode ? Pl_TIFFPredictor::a_encode : Pl_TIFFPredictor::a_decode, | |
| 32 | + columns, samples_per_pixel, bits_per_sample); | |
| 33 | + } | |
| 26 | 34 | else |
| 27 | 35 | { |
| 28 | 36 | std::cerr << "unknown filter " << filter << std::endl; | ... | ... |
libtests/qtest/predictors.test
| ... | ... | @@ -53,7 +53,7 @@ $td->runtest("check output", |
| 53 | 53 | {$td->FILE => "out"}, |
| 54 | 54 | {$td->FILE => "in2"}); |
| 55 | 55 | |
| 56 | -my @other = ( | |
| 56 | +my @other_png = ( | |
| 57 | 57 | '01--32-3-16', |
| 58 | 58 | '02--32-1-8', |
| 59 | 59 | '03--32-3-8', |
| ... | ... | @@ -68,7 +68,7 @@ my @other = ( |
| 68 | 68 | '12--32-1-4', |
| 69 | 69 | ); |
| 70 | 70 | |
| 71 | -foreach my $i (@other) | |
| 71 | +foreach my $i (@other_png) | |
| 72 | 72 | { |
| 73 | 73 | $i =~ m/^.*?--(\d+)-(\d+)-(\d+)$/ or die; |
| 74 | 74 | my $columns = $1; |
| ... | ... | @@ -85,9 +85,41 @@ foreach my $i (@other) |
| 85 | 85 | {$td->FILE => "$i.decoded"}); |
| 86 | 86 | } |
| 87 | 87 | |
| 88 | +my @tiff = ( | |
| 89 | + '01--16-1-8', | |
| 90 | + '02--8-2-4', | |
| 91 | + '03--4-1-16', | |
| 92 | + ); | |
| 93 | + | |
| 94 | +foreach my $i (@tiff) | |
| 95 | +{ | |
| 96 | + $i =~ m/^.*?--(\d+)-(\d+)-(\d+)$/ or die; | |
| 97 | + my $columns = $1; | |
| 98 | + my $samples_per_pixel = $2; | |
| 99 | + my $bits_per_sample = $3; | |
| 100 | + $td->runtest("decode tiff $i", | |
| 101 | + {$td->COMMAND => "predictors tiff decode tiff-$i.data" . | |
| 102 | + " $columns $samples_per_pixel $bits_per_sample"}, | |
| 103 | + {$td->STRING => "done\n", | |
| 104 | + $td->EXIT_STATUS => 0}, | |
| 105 | + $td->NORMALIZE_NEWLINES); | |
| 106 | + $td->runtest("check output for tiff-$i", | |
| 107 | + {$td->FILE => "out"}, | |
| 108 | + {$td->FILE => "tiff-$i.decoded"}); | |
| 109 | + $td->runtest("encode tiff $i", | |
| 110 | + {$td->COMMAND => "predictors tiff encode tiff-$i.decoded" . | |
| 111 | + " $columns $samples_per_pixel $bits_per_sample"}, | |
| 112 | + {$td->STRING => "done\n", | |
| 113 | + $td->EXIT_STATUS => 0}, | |
| 114 | + $td->NORMALIZE_NEWLINES); | |
| 115 | + $td->runtest("check output for tiff-$i", | |
| 116 | + {$td->FILE => "out"}, | |
| 117 | + {$td->FILE => "tiff-$i.data"}); | |
| 118 | +} | |
| 119 | + | |
| 88 | 120 | cleanup(); |
| 89 | 121 | |
| 90 | -$td->report(8 + (2 * scalar(@other))); | |
| 122 | +$td->report(8 + (2 * scalar(@other_png)) + (4 * scalar(@tiff))); | |
| 91 | 123 | |
| 92 | 124 | sub cleanup |
| 93 | 125 | { | ... | ... |
libtests/qtest/predictors/tiff-01--16-1-8.data
0 → 100644
No preview for this file type
libtests/qtest/predictors/tiff-01--16-1-8.decoded
0 → 100644
No preview for this file type
libtests/qtest/predictors/tiff-02--8-2-4.data
0 → 100644
No preview for this file type
libtests/qtest/predictors/tiff-02--8-2-4.decoded
0 → 100644
No preview for this file type
libtests/qtest/predictors/tiff-03--4-1-16.data
0 → 100644
libtests/qtest/predictors/tiff-03--4-1-16.decoded
0 → 100644