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
| @@ -32,6 +32,7 @@ SRCS_libqpdf = \ | @@ -32,6 +32,7 @@ SRCS_libqpdf = \ | ||
| 32 | libqpdf/Pl_RunLength.cc \ | 32 | libqpdf/Pl_RunLength.cc \ |
| 33 | libqpdf/Pl_SHA2.cc \ | 33 | libqpdf/Pl_SHA2.cc \ |
| 34 | libqpdf/Pl_StdioFile.cc \ | 34 | libqpdf/Pl_StdioFile.cc \ |
| 35 | + libqpdf/Pl_TIFFPredictor.cc \ | ||
| 35 | libqpdf/QPDF.cc \ | 36 | libqpdf/QPDF.cc \ |
| 36 | libqpdf/QPDFExc.cc \ | 37 | libqpdf/QPDFExc.cc \ |
| 37 | libqpdf/QPDFObjGen.cc \ | 38 | libqpdf/QPDFObjGen.cc \ |
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 | #include <qpdf/Pl_PNGFilter.hh> | 1 | #include <qpdf/Pl_PNGFilter.hh> |
| 2 | +#include <qpdf/Pl_TIFFPredictor.hh> | ||
| 2 | #include <qpdf/Pl_StdioFile.hh> | 3 | #include <qpdf/Pl_StdioFile.hh> |
| 3 | #include <qpdf/QUtil.hh> | 4 | #include <qpdf/QUtil.hh> |
| 4 | 5 | ||
| @@ -23,6 +24,13 @@ void run(char const* filename, char const* filter, | @@ -23,6 +24,13 @@ void run(char const* filename, char const* filter, | ||
| 23 | encode ? Pl_PNGFilter::a_encode : Pl_PNGFilter::a_decode, | 24 | encode ? Pl_PNGFilter::a_encode : Pl_PNGFilter::a_decode, |
| 24 | columns, samples_per_pixel, bits_per_sample); | 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 | else | 34 | else |
| 27 | { | 35 | { |
| 28 | std::cerr << "unknown filter " << filter << std::endl; | 36 | std::cerr << "unknown filter " << filter << std::endl; |
libtests/qtest/predictors.test
| @@ -53,7 +53,7 @@ $td->runtest("check output", | @@ -53,7 +53,7 @@ $td->runtest("check output", | ||
| 53 | {$td->FILE => "out"}, | 53 | {$td->FILE => "out"}, |
| 54 | {$td->FILE => "in2"}); | 54 | {$td->FILE => "in2"}); |
| 55 | 55 | ||
| 56 | -my @other = ( | 56 | +my @other_png = ( |
| 57 | '01--32-3-16', | 57 | '01--32-3-16', |
| 58 | '02--32-1-8', | 58 | '02--32-1-8', |
| 59 | '03--32-3-8', | 59 | '03--32-3-8', |
| @@ -68,7 +68,7 @@ my @other = ( | @@ -68,7 +68,7 @@ my @other = ( | ||
| 68 | '12--32-1-4', | 68 | '12--32-1-4', |
| 69 | ); | 69 | ); |
| 70 | 70 | ||
| 71 | -foreach my $i (@other) | 71 | +foreach my $i (@other_png) |
| 72 | { | 72 | { |
| 73 | $i =~ m/^.*?--(\d+)-(\d+)-(\d+)$/ or die; | 73 | $i =~ m/^.*?--(\d+)-(\d+)-(\d+)$/ or die; |
| 74 | my $columns = $1; | 74 | my $columns = $1; |
| @@ -85,9 +85,41 @@ foreach my $i (@other) | @@ -85,9 +85,41 @@ foreach my $i (@other) | ||
| 85 | {$td->FILE => "$i.decoded"}); | 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 | cleanup(); | 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 | sub cleanup | 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