diff --git a/libqpdf/Pl_TIFFPredictor.cc b/libqpdf/Pl_TIFFPredictor.cc new file mode 100644 index 00000000..42cfbb1a --- /dev/null +++ b/libqpdf/Pl_TIFFPredictor.cc @@ -0,0 +1,123 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +Pl_TIFFPredictor::Pl_TIFFPredictor(char const* identifier, Pipeline* next, + action_e action, unsigned int columns, + unsigned int samples_per_pixel, + unsigned int bits_per_sample) : + Pipeline(identifier, next), + action(action), + columns(columns), + samples_per_pixel(samples_per_pixel), + bits_per_sample(bits_per_sample), + cur_row(0), + pos(0) +{ + if (samples_per_pixel < 1) + { + throw std::runtime_error( + "TIFFPredictor created with invalid samples_per_pixel"); + } + if ((bits_per_sample < 1) || + (bits_per_sample > (8 * (sizeof(unsigned long long))))) + { + throw std::runtime_error( + "TIFFPredictor created with invalid bits_per_sample"); + } + unsigned long long bpr = + ((columns * bits_per_sample * samples_per_pixel) + 7) / 8; + if ((bpr == 0) || (bpr > (UINT_MAX - 1))) + { + throw std::runtime_error( + "TIFFPredictor created with invalid columns value"); + } + this->bytes_per_row = bpr & UINT_MAX; + this->cur_row = new unsigned char[this->bytes_per_row]; + memset(this->cur_row, 0, this->bytes_per_row); +} + +Pl_TIFFPredictor::~Pl_TIFFPredictor() +{ + delete [] cur_row; +} + +void +Pl_TIFFPredictor::write(unsigned char* data, size_t len) +{ + size_t left = this->bytes_per_row - this->pos; + size_t offset = 0; + while (len >= left) + { + // finish off current row + memcpy(this->cur_row + this->pos, data + offset, left); + offset += left; + len -= left; + + processRow(); + + // Prepare for next row + memset(this->cur_row, 0, this->bytes_per_row); + left = this->bytes_per_row; + this->pos = 0; + } + if (len) + { + memcpy(this->cur_row + this->pos, data + offset, len); + } + this->pos += len; +} + +void +Pl_TIFFPredictor::processRow() +{ + QTC::TC("libtests", "Pl_TIFFPredictor processRow", + (action == a_decode ? 0 : 1)); + BitWriter bw(this->getNext()); + BitStream in(this->cur_row, this->bytes_per_row); + std::vector prev; + for (unsigned int i = 0; i < this->samples_per_pixel; ++i) + { + long long sample = in.getBitsSigned(this->bits_per_sample); + bw.writeBitsSigned(sample, this->bits_per_sample); + prev.push_back(sample); + } + for (unsigned int col = 1; col < this->columns; ++col) + { + for (unsigned int i = 0; i < this->samples_per_pixel; ++i) + { + long long sample = in.getBitsSigned(this->bits_per_sample); + long long new_sample = sample; + if (action == a_encode) + { + new_sample -= prev[i]; + prev[i] = sample; + } + else + { + new_sample += prev[i]; + prev[i] = new_sample; + } + bw.writeBitsSigned(new_sample, this->bits_per_sample); + } + } + bw.flush(); +} + +void +Pl_TIFFPredictor::finish() +{ + if (this->pos) + { + // write partial row + processRow(); + } + this->pos = 0; + memset(this->cur_row, 0, this->bytes_per_row); + getNext()->finish(); +} diff --git a/libqpdf/build.mk b/libqpdf/build.mk index 75d0054e..c75c3dd9 100644 --- a/libqpdf/build.mk +++ b/libqpdf/build.mk @@ -32,6 +32,7 @@ SRCS_libqpdf = \ libqpdf/Pl_RunLength.cc \ libqpdf/Pl_SHA2.cc \ libqpdf/Pl_StdioFile.cc \ + libqpdf/Pl_TIFFPredictor.cc \ libqpdf/QPDF.cc \ libqpdf/QPDFExc.cc \ libqpdf/QPDFObjGen.cc \ diff --git a/libqpdf/qpdf/Pl_TIFFPredictor.hh b/libqpdf/qpdf/Pl_TIFFPredictor.hh new file mode 100644 index 00000000..235068ee --- /dev/null +++ b/libqpdf/qpdf/Pl_TIFFPredictor.hh @@ -0,0 +1,39 @@ +#ifndef __PL_TIFFPREDICTOR_HH__ +#define __PL_TIFFPREDICTOR_HH__ + +// This pipeline reverses the application of a TIFF predictor as +// described in the TIFF specification. + +#include + +class Pl_TIFFPredictor: public Pipeline +{ + public: + enum action_e { a_encode, a_decode }; + + QPDF_DLL + Pl_TIFFPredictor(char const* identifier, Pipeline* next, + action_e action, unsigned int columns, + unsigned int samples_per_pixel = 1, + unsigned int bits_per_sample = 8); + QPDF_DLL + virtual ~Pl_TIFFPredictor(); + + QPDF_DLL + virtual void write(unsigned char* data, size_t len); + QPDF_DLL + virtual void finish(); + + private: + void processRow(); + + action_e action; + unsigned int columns; + unsigned int bytes_per_row; + unsigned int samples_per_pixel; + unsigned int bits_per_sample; + unsigned char* cur_row; + size_t pos; +}; + +#endif // __PL_TIFFPREDICTOR_HH__ diff --git a/libtests/libtests.testcov b/libtests/libtests.testcov index 03ca98a6..8b209281 100644 --- a/libtests/libtests.testcov +++ b/libtests/libtests.testcov @@ -33,3 +33,4 @@ Pl_PNGFilter decodeSub 0 Pl_PNGFilter decodeUp 0 Pl_PNGFilter decodeAverage 0 Pl_PNGFilter decodePaeth 0 +Pl_TIFFPredictor processRow 1 diff --git a/libtests/predictors.cc b/libtests/predictors.cc index fe2b90f4..47efa287 100644 --- a/libtests/predictors.cc +++ b/libtests/predictors.cc @@ -1,4 +1,5 @@ #include +#include #include #include @@ -23,6 +24,13 @@ void run(char const* filename, char const* filter, encode ? Pl_PNGFilter::a_encode : Pl_PNGFilter::a_decode, columns, samples_per_pixel, bits_per_sample); } + else if (strcmp(filter, "tiff") == 0) + { + pl = new Pl_TIFFPredictor( + "png", out, + encode ? Pl_TIFFPredictor::a_encode : Pl_TIFFPredictor::a_decode, + columns, samples_per_pixel, bits_per_sample); + } else { std::cerr << "unknown filter " << filter << std::endl; diff --git a/libtests/qtest/predictors.test b/libtests/qtest/predictors.test index f0553472..98961d1a 100644 --- a/libtests/qtest/predictors.test +++ b/libtests/qtest/predictors.test @@ -53,7 +53,7 @@ $td->runtest("check output", {$td->FILE => "out"}, {$td->FILE => "in2"}); -my @other = ( +my @other_png = ( '01--32-3-16', '02--32-1-8', '03--32-3-8', @@ -68,7 +68,7 @@ my @other = ( '12--32-1-4', ); -foreach my $i (@other) +foreach my $i (@other_png) { $i =~ m/^.*?--(\d+)-(\d+)-(\d+)$/ or die; my $columns = $1; @@ -85,9 +85,41 @@ foreach my $i (@other) {$td->FILE => "$i.decoded"}); } +my @tiff = ( + '01--16-1-8', + '02--8-2-4', + '03--4-1-16', + ); + +foreach my $i (@tiff) +{ + $i =~ m/^.*?--(\d+)-(\d+)-(\d+)$/ or die; + my $columns = $1; + my $samples_per_pixel = $2; + my $bits_per_sample = $3; + $td->runtest("decode tiff $i", + {$td->COMMAND => "predictors tiff decode tiff-$i.data" . + " $columns $samples_per_pixel $bits_per_sample"}, + {$td->STRING => "done\n", + $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); + $td->runtest("check output for tiff-$i", + {$td->FILE => "out"}, + {$td->FILE => "tiff-$i.decoded"}); + $td->runtest("encode tiff $i", + {$td->COMMAND => "predictors tiff encode tiff-$i.decoded" . + " $columns $samples_per_pixel $bits_per_sample"}, + {$td->STRING => "done\n", + $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); + $td->runtest("check output for tiff-$i", + {$td->FILE => "out"}, + {$td->FILE => "tiff-$i.data"}); +} + cleanup(); -$td->report(8 + (2 * scalar(@other))); +$td->report(8 + (2 * scalar(@other_png)) + (4 * scalar(@tiff))); sub cleanup { diff --git a/libtests/qtest/predictors/tiff-01--16-1-8.data b/libtests/qtest/predictors/tiff-01--16-1-8.data new file mode 100644 index 00000000..30ba8de7 Binary files /dev/null and b/libtests/qtest/predictors/tiff-01--16-1-8.data differ diff --git a/libtests/qtest/predictors/tiff-01--16-1-8.decoded b/libtests/qtest/predictors/tiff-01--16-1-8.decoded new file mode 100644 index 00000000..a47c5d62 --- /dev/null +++ b/libtests/qtest/predictors/tiff-01--16-1-8.decoded @@ -0,0 +1,3 @@ + +  + WXWVUTSRQG=33467 \ No newline at end of file diff --git a/libtests/qtest/predictors/tiff-02--8-2-4.data b/libtests/qtest/predictors/tiff-02--8-2-4.data new file mode 100644 index 00000000..0861af5a Binary files /dev/null and b/libtests/qtest/predictors/tiff-02--8-2-4.data differ diff --git a/libtests/qtest/predictors/tiff-02--8-2-4.decoded b/libtests/qtest/predictors/tiff-02--8-2-4.decoded new file mode 100644 index 00000000..7386e3ec --- /dev/null +++ b/libtests/qtest/predictors/tiff-02--8-2-4.decoded @@ -0,0 +1 @@ +ª›Œ}__wª›Œ}__w \ No newline at end of file diff --git a/libtests/qtest/predictors/tiff-03--4-1-16.data b/libtests/qtest/predictors/tiff-03--4-1-16.data new file mode 100644 index 00000000..e03d809d --- /dev/null +++ b/libtests/qtest/predictors/tiff-03--4-1-16.data @@ -0,0 +1 @@ +UUÍðd 9[ \ No newline at end of file diff --git a/libtests/qtest/predictors/tiff-03--4-1-16.decoded b/libtests/qtest/predictors/tiff-03--4-1-16.decoded new file mode 100644 index 00000000..7d3c2dd9 --- /dev/null +++ b/libtests/qtest/predictors/tiff-03--4-1-16.decoded @@ -0,0 +1 @@ +UU#E‡eÀÀ \ No newline at end of file