/src/qpdf/libqpdf/Pl_TIFFPredictor.cc
Line | Count | Source |
1 | | #include <qpdf/Pl_TIFFPredictor.hh> |
2 | | |
3 | | #include <qpdf/BitStream.hh> |
4 | | #include <qpdf/BitWriter.hh> |
5 | | #include <qpdf/QTC.hh> |
6 | | #include <qpdf/Util.hh> |
7 | | #include <qpdf/global_private.hh> |
8 | | |
9 | | #include <climits> |
10 | | #include <stdexcept> |
11 | | |
12 | | using namespace qpdf; |
13 | | |
14 | | namespace |
15 | | { |
16 | | unsigned long long const& memory_limit = global::Limits::tiff_max_memory(); |
17 | | } // namespace |
18 | | |
19 | | Pl_TIFFPredictor::Pl_TIFFPredictor( |
20 | | char const* identifier, |
21 | | Pipeline* next, |
22 | | action_e action, |
23 | | uint32_t columns, |
24 | | uint32_t samples_per_pixel, |
25 | | uint32_t bits_per_sample) : |
26 | 9.66k | Pipeline(identifier, next), |
27 | 9.66k | action(action), |
28 | 9.66k | columns(columns), |
29 | 9.66k | samples_per_pixel(samples_per_pixel), |
30 | 9.66k | bits_per_sample(bits_per_sample) |
31 | 9.66k | { |
32 | 9.66k | util::assertion(next, "Attempt to create Pl_TIFFPredictor with nullptr as next"); |
33 | 9.66k | util::no_ci_rt_error_if( |
34 | 9.66k | samples_per_pixel < 1, "TIFFPredictor created with invalid samples_per_pixel"); |
35 | 9.66k | util::no_ci_rt_error_if( |
36 | 9.66k | bits_per_sample < 1 || bits_per_sample > (8 * (sizeof(unsigned long long))), |
37 | 9.66k | "TIFFPredictor created with invalid bits_per_sample"); |
38 | 9.66k | auto bits_per_pixel = 1ULL * bits_per_sample * samples_per_pixel; |
39 | 9.66k | util::no_ci_rt_error_if( |
40 | 9.66k | !util::fits<uint32_t>(bits_per_pixel + 7), |
41 | 9.66k | "TIFFPredictor created with bits_per_sample and samples_per_pixel values that cause " |
42 | 9.66k | "overflow"); |
43 | 9.66k | auto bpr = (columns * bits_per_pixel + 7) / 8; |
44 | 9.66k | util::no_ci_rt_error_if( |
45 | 9.66k | bpr == 0 || !util::fits<uint32_t>(bpr), "TIFFPredictor created with invalid columns value"); |
46 | 9.66k | util::no_ci_rt_error_if( |
47 | 9.66k | memory_limit > 0 && bpr > (memory_limit / 2ULL), "TIFFPredictor memory limit exceeded"); |
48 | 9.66k | bytes_per_row = static_cast<uint32_t>(bpr); |
49 | 9.66k | } |
50 | | |
51 | | void |
52 | | Pl_TIFFPredictor::setMemoryLimit(unsigned long long limit) |
53 | 0 | { |
54 | 0 | global::Limits::tiff_max_memory(limit); |
55 | 0 | } |
56 | | |
57 | | void |
58 | | Pl_TIFFPredictor::write(unsigned char const* data, size_t len) |
59 | 9.43M | { |
60 | 9.43M | auto end = data + len; |
61 | 9.43M | auto row_end = data + (bytes_per_row - cur_row.size()); |
62 | 14.3M | while (row_end <= end) { |
63 | | // finish off current row |
64 | 4.95M | cur_row.insert(cur_row.end(), data, row_end); |
65 | 4.95M | data = row_end; |
66 | 4.95M | row_end += bytes_per_row; |
67 | | |
68 | 4.95M | processRow(); |
69 | | |
70 | | // Prepare for next row |
71 | 4.95M | cur_row.clear(); |
72 | 4.95M | } |
73 | | |
74 | 9.43M | cur_row.insert(cur_row.end(), data, end); |
75 | 9.43M | } |
76 | | |
77 | | void |
78 | | Pl_TIFFPredictor::processRow() |
79 | 4.95M | { |
80 | 4.95M | QTC::TC("libtests", "Pl_TIFFPredictor processRow", (action == a_decode ? 0 : 1)); |
81 | 4.95M | previous.assign(samples_per_pixel, 0); |
82 | 4.95M | if (bits_per_sample != 8) { |
83 | 61.5k | BitWriter bw(next()); |
84 | 61.5k | BitStream in(cur_row.data(), cur_row.size()); |
85 | 15.9M | for (uint32_t col = 0; col < this->columns; ++col) { |
86 | 16.8M | for (auto& prev: previous) { |
87 | 16.8M | long long sample = in.getBitsSigned(this->bits_per_sample); |
88 | 16.8M | long long new_sample = sample; |
89 | 16.8M | if (action == a_encode) { |
90 | 0 | new_sample -= prev; |
91 | 0 | prev = sample; |
92 | 16.8M | } else { |
93 | 16.8M | new_sample += prev; |
94 | 16.8M | prev = new_sample; |
95 | 16.8M | } |
96 | 16.8M | bw.writeBitsSigned(new_sample, this->bits_per_sample); |
97 | 16.8M | } |
98 | 15.9M | } |
99 | 61.5k | bw.flush(); |
100 | 4.89M | } else { |
101 | 4.89M | out.clear(); |
102 | 4.89M | auto next_it = cur_row.begin(); |
103 | 4.89M | auto cr_end = cur_row.end(); |
104 | 4.89M | auto pr_end = previous.end(); |
105 | | |
106 | 39.2M | while (next_it != cr_end) { |
107 | 88.0M | for (auto prev = previous.begin(); prev != pr_end && next_it != cr_end; |
108 | 53.6M | ++prev, ++next_it) { |
109 | 53.6M | long long sample = *next_it; |
110 | 53.6M | long long new_sample = sample; |
111 | 53.6M | if (action == a_encode) { |
112 | 0 | new_sample -= *prev; |
113 | 0 | *prev = sample; |
114 | 53.6M | } else { |
115 | 53.6M | new_sample += *prev; |
116 | 53.6M | *prev = new_sample; |
117 | 53.6M | } |
118 | 53.6M | out.push_back(static_cast<unsigned char>(255U & new_sample)); |
119 | 53.6M | } |
120 | 34.3M | } |
121 | 4.89M | next()->write(out.data(), out.size()); |
122 | 4.89M | } |
123 | 4.95M | } |
124 | | |
125 | | void |
126 | | Pl_TIFFPredictor::finish() |
127 | 8.75k | { |
128 | 8.75k | if (!cur_row.empty()) { |
129 | | // write partial row |
130 | 5.42k | cur_row.insert(cur_row.end(), bytes_per_row - cur_row.size(), 0); |
131 | 5.42k | processRow(); |
132 | 5.42k | } |
133 | 8.75k | cur_row.clear(); |
134 | 8.75k | next()->finish(); |
135 | 8.75k | } |