Commit 53971d50be39448c980d842a1fe1f525b4cee597

Authored by Jay Berkenbilt
1 parent d9c90497

Add Pl_TIFFPredictor

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
@@ -33,3 +33,4 @@ Pl_PNGFilter decodeSub 0 @@ -33,3 +33,4 @@ Pl_PNGFilter decodeSub 0
33 Pl_PNGFilter decodeUp 0 33 Pl_PNGFilter decodeUp 0
34 Pl_PNGFilter decodeAverage 0 34 Pl_PNGFilter decodeAverage 0
35 Pl_PNGFilter decodePaeth 0 35 Pl_PNGFilter decodePaeth 0
  36 +Pl_TIFFPredictor processRow 1
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-&gt;runtest(&quot;check output&quot;, @@ -53,7 +53,7 @@ $td-&gt;runtest(&quot;check output&quot;,
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
  1 +UUÍðd 9[
0 \ No newline at end of file 2 \ No newline at end of file
libtests/qtest/predictors/tiff-03--4-1-16.decoded 0 → 100644
  1 +UU#E‡eÀÀ
0 \ No newline at end of file 2 \ No newline at end of file