Coverage Report

Created: 2026-04-12 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
    unsigned int columns,
24
    unsigned int samples_per_pixel,
25
    unsigned int bits_per_sample) :
26
568
    Pipeline(identifier, next),
27
568
    action(action),
28
568
    columns(columns),
29
568
    samples_per_pixel(samples_per_pixel),
30
568
    bits_per_sample(bits_per_sample)
31
568
{
32
568
    util::assertion(next, "Attempt to create Pl_TIFFPredictor with nullptr as next");
33
568
    util::no_ci_rt_error_if(
34
568
        samples_per_pixel < 1, "TIFFPredictor created with invalid samples_per_pixel");
35
568
    util::no_ci_rt_error_if(
36
568
        bits_per_sample < 1 || bits_per_sample > (8 * (sizeof(unsigned long long))),
37
568
        "TIFFPredictor created with invalid bits_per_sample");
38
568
    unsigned long long bpr = ((columns * bits_per_sample * samples_per_pixel) + 7) / 8;
39
568
    util::no_ci_rt_error_if(
40
568
        bpr == 0 || bpr > (UINT_MAX - 1), "TIFFPredictor created with invalid columns value");
41
568
    util::no_ci_rt_error_if(
42
568
        memory_limit > 0 && bpr > (memory_limit / 2U), "TIFFPredictor memory limit exceeded");
43
568
    this->bytes_per_row = bpr & UINT_MAX;
44
568
}
45
46
void
47
Pl_TIFFPredictor::setMemoryLimit(unsigned long long limit)
48
18.8k
{
49
18.8k
    global::Limits::tiff_max_memory(limit);
50
18.8k
}
51
52
void
53
Pl_TIFFPredictor::write(unsigned char const* data, size_t len)
54
52.8k
{
55
52.8k
    auto end = data + len;
56
52.8k
    auto row_end = data + (bytes_per_row - cur_row.size());
57
146k
    while (row_end <= end) {
58
        // finish off current row
59
93.3k
        cur_row.insert(cur_row.end(), data, row_end);
60
93.3k
        data = row_end;
61
93.3k
        row_end += bytes_per_row;
62
63
93.3k
        processRow();
64
65
        // Prepare for next row
66
93.3k
        cur_row.clear();
67
93.3k
    }
68
69
52.8k
    cur_row.insert(cur_row.end(), data, end);
70
52.8k
}
71
72
void
73
Pl_TIFFPredictor::processRow()
74
93.7k
{
75
93.7k
    QTC::TC("libtests", "Pl_TIFFPredictor processRow", (action == a_decode ? 0 : 1));
76
93.7k
    previous.assign(samples_per_pixel, 0);
77
93.7k
    if (bits_per_sample != 8) {
78
41.3k
        BitWriter bw(next());
79
41.3k
        BitStream in(cur_row.data(), cur_row.size());
80
1.17M
        for (unsigned int col = 0; col < this->columns; ++col) {
81
1.13M
            for (auto& prev: previous) {
82
1.13M
                long long sample = in.getBitsSigned(this->bits_per_sample);
83
1.13M
                long long new_sample = sample;
84
1.13M
                if (action == a_encode) {
85
0
                    new_sample -= prev;
86
0
                    prev = sample;
87
1.13M
                } else {
88
1.13M
                    new_sample += prev;
89
1.13M
                    prev = new_sample;
90
1.13M
                }
91
1.13M
                bw.writeBitsSigned(new_sample, this->bits_per_sample);
92
1.13M
            }
93
1.13M
        }
94
41.3k
        bw.flush();
95
52.3k
    } else {
96
52.3k
        out.clear();
97
52.3k
        auto next_it = cur_row.begin();
98
52.3k
        auto cr_end = cur_row.end();
99
52.3k
        auto pr_end = previous.end();
100
101
1.65M
        while (next_it != cr_end) {
102
4.31M
            for (auto prev = previous.begin(); prev != pr_end && next_it != cr_end;
103
2.70M
                 ++prev, ++next_it) {
104
2.70M
                long long sample = *next_it;
105
2.70M
                long long new_sample = sample;
106
2.70M
                if (action == a_encode) {
107
0
                    new_sample -= *prev;
108
0
                    *prev = sample;
109
2.70M
                } else {
110
2.70M
                    new_sample += *prev;
111
2.70M
                    *prev = new_sample;
112
2.70M
                }
113
2.70M
                out.push_back(static_cast<unsigned char>(255U & new_sample));
114
2.70M
            }
115
1.60M
        }
116
52.3k
        next()->write(out.data(), out.size());
117
52.3k
    }
118
93.7k
}
119
120
void
121
Pl_TIFFPredictor::finish()
122
522
{
123
522
    if (!cur_row.empty()) {
124
        // write partial row
125
341
        cur_row.insert(cur_row.end(), bytes_per_row - cur_row.size(), 0);
126
341
        processRow();
127
341
    }
128
522
    cur_row.clear();
129
522
    next()->finish();
130
522
}