Coverage Report

Created: 2026-02-26 06:40

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
8
#include <climits>
9
#include <stdexcept>
10
11
using namespace qpdf;
12
13
namespace
14
{
15
    unsigned long long memory_limit{0};
16
} // namespace
17
18
Pl_TIFFPredictor::Pl_TIFFPredictor(
19
    char const* identifier,
20
    Pipeline* next,
21
    action_e action,
22
    unsigned int columns,
23
    unsigned int samples_per_pixel,
24
    unsigned int bits_per_sample) :
25
8.83k
    Pipeline(identifier, next),
26
8.83k
    action(action),
27
8.83k
    columns(columns),
28
8.83k
    samples_per_pixel(samples_per_pixel),
29
8.83k
    bits_per_sample(bits_per_sample)
30
8.83k
{
31
8.83k
    util::assertion(next, "Attempt to create Pl_TIFFPredictor with nullptr as next");
32
8.83k
    util::no_ci_rt_error_if(
33
8.83k
        samples_per_pixel < 1, "TIFFPredictor created with invalid samples_per_pixel");
34
8.83k
    util::no_ci_rt_error_if(
35
8.83k
        bits_per_sample < 1 || bits_per_sample > (8 * (sizeof(unsigned long long))),
36
8.83k
        "TIFFPredictor created with invalid bits_per_sample");
37
8.83k
    unsigned long long bpr = ((columns * bits_per_sample * samples_per_pixel) + 7) / 8;
38
8.83k
    util::no_ci_rt_error_if(
39
8.83k
        bpr == 0 || bpr > (UINT_MAX - 1), "TIFFPredictor created with invalid columns value");
40
8.83k
    util::no_ci_rt_error_if(
41
8.83k
        memory_limit > 0 && bpr > (memory_limit / 2U), "TIFFPredictor memory limit exceeded");
42
8.83k
    this->bytes_per_row = bpr & UINT_MAX;
43
8.83k
}
44
45
void
46
Pl_TIFFPredictor::setMemoryLimit(unsigned long long limit)
47
260k
{
48
260k
    memory_limit = limit;
49
260k
}
50
51
void
52
Pl_TIFFPredictor::write(unsigned char const* data, size_t len)
53
11.4M
{
54
11.4M
    auto end = data + len;
55
11.4M
    auto row_end = data + (bytes_per_row - cur_row.size());
56
13.9M
    while (row_end <= end) {
57
        // finish off current row
58
2.49M
        cur_row.insert(cur_row.end(), data, row_end);
59
2.49M
        data = row_end;
60
2.49M
        row_end += bytes_per_row;
61
62
2.49M
        processRow();
63
64
        // Prepare for next row
65
2.49M
        cur_row.clear();
66
2.49M
    }
67
68
11.4M
    cur_row.insert(cur_row.end(), data, end);
69
11.4M
}
70
71
void
72
Pl_TIFFPredictor::processRow()
73
2.50M
{
74
2.50M
    QTC::TC("libtests", "Pl_TIFFPredictor processRow", (action == a_decode ? 0 : 1));
75
2.50M
    previous.assign(samples_per_pixel, 0);
76
2.50M
    if (bits_per_sample != 8) {
77
120k
        BitWriter bw(next());
78
120k
        BitStream in(cur_row.data(), cur_row.size());
79
12.4M
        for (unsigned int col = 0; col < this->columns; ++col) {
80
13.1M
            for (auto& prev: previous) {
81
13.1M
                long long sample = in.getBitsSigned(this->bits_per_sample);
82
13.1M
                long long new_sample = sample;
83
13.1M
                if (action == a_encode) {
84
0
                    new_sample -= prev;
85
0
                    prev = sample;
86
13.1M
                } else {
87
13.1M
                    new_sample += prev;
88
13.1M
                    prev = new_sample;
89
13.1M
                }
90
13.1M
                bw.writeBitsSigned(new_sample, this->bits_per_sample);
91
13.1M
            }
92
12.3M
        }
93
120k
        bw.flush();
94
2.38M
    } else {
95
2.38M
        out.clear();
96
2.38M
        auto next_it = cur_row.begin();
97
2.38M
        auto cr_end = cur_row.end();
98
2.38M
        auto pr_end = previous.end();
99
100
41.0M
        while (next_it != cr_end) {
101
105M
            for (auto prev = previous.begin(); prev != pr_end && next_it != cr_end;
102
67.2M
                 ++prev, ++next_it) {
103
67.2M
                long long sample = *next_it;
104
67.2M
                long long new_sample = sample;
105
67.2M
                if (action == a_encode) {
106
0
                    new_sample -= *prev;
107
0
                    *prev = sample;
108
67.2M
                } else {
109
67.2M
                    new_sample += *prev;
110
67.2M
                    *prev = new_sample;
111
67.2M
                }
112
67.2M
                out.push_back(static_cast<unsigned char>(255U & new_sample));
113
67.2M
            }
114
38.6M
        }
115
2.38M
        next()->write(out.data(), out.size());
116
2.38M
    }
117
2.50M
}
118
119
void
120
Pl_TIFFPredictor::finish()
121
8.10k
{
122
8.10k
    if (!cur_row.empty()) {
123
        // write partial row
124
5.46k
        cur_row.insert(cur_row.end(), bytes_per_row - cur_row.size(), 0);
125
5.46k
        processRow();
126
5.46k
    }
127
8.10k
    cur_row.clear();
128
8.10k
    next()->finish();
129
8.10k
}