Coverage Report

Created: 2025-07-18 07:00

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