Coverage Report

Created: 2026-02-14 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openexr/src/lib/OpenEXR/ImfCompressor.cpp
Line
Count
Source
1
//
2
// SPDX-License-Identifier: BSD-3-Clause
3
// Copyright (c) Contributors to the OpenEXR Project.
4
//
5
6
//-----------------------------------------------------------------------------
7
//
8
//  class Compressor
9
//
10
//-----------------------------------------------------------------------------
11
12
#include "ImfCheckedArithmetic.h"
13
#include "ImfNamespace.h"
14
#include "ImfCompressor.h"
15
#include "ImfB44Compressor.h"
16
#include "ImfDwaCompressor.h"
17
#include "ImfPizCompressor.h"
18
#include "ImfPxr24Compressor.h"
19
#include "ImfRleCompressor.h"
20
#include "ImfZipCompressor.h"
21
#include "ImfZip.h"
22
23
#include <algorithm>
24
#include <stdexcept>
25
26
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
27
28
using IMATH_NAMESPACE::Box2i;
29
30
Compressor::Compressor (
31
    const Header& hdr,
32
    exr_compression_t compression_type,
33
    size_t maxScanLineSize,
34
    int numScanLines)
35
0
    : _ctxt ("<compression>",
36
0
             ContextInitializer(),
37
0
             Context::temp_mode_t
38
0
             {})
39
0
    , _header (hdr)
40
0
    , _maxScanLineSize (maxScanLineSize)
41
0
    , _numScanLines (numScanLines)
42
0
    , _comp_type (compression_type)
43
0
{
44
0
    if (maxScanLineSize > std::numeric_limits<int>::max ())
45
0
    {
46
0
        throw IEX_NAMESPACE::OverflowExc (
47
0
            "ScanLine size too large for RleCompressor");
48
0
    }
49
0
    _ctxt.setLongNameSupport (true);
50
0
    _ctxt.addHeader (0, hdr);
51
52
0
    _store_type = _ctxt.storage (0);
53
54
0
    exr_set_zip_compression_level (_ctxt, 0, hdr.zipCompressionLevel ());
55
0
    exr_set_dwa_compression_level (_ctxt, 0, hdr.dwaCompressionLevel ());
56
57
0
    exr_compression_t hdrcomp;
58
0
    if (EXR_ERR_SUCCESS != exr_get_compression (_ctxt, 0, &hdrcomp))
59
0
        throw IEX_NAMESPACE::ArgExc ("Unable to initialize compression type");
60
61
0
    if (hdrcomp != compression_type &&
62
0
        compression_type != EXR_COMPRESSION_LAST_TYPE)
63
0
    {
64
        // no idea why this would fail, but also do need to apply it
65
        // prior to the chunk initialization to have lines per chunk, etc.
66
        // be correct
67
0
        if (EXR_ERR_SUCCESS != exr_set_compression (_ctxt, 0, compression_type))
68
0
            throw IEX_NAMESPACE::ArgExc ("Unable to initialize compression type");
69
0
    }
70
0
 }
71
72
Compressor::~Compressor ()
73
0
{
74
0
    if (_decoder_init)
75
0
        exr_decoding_destroy(_ctxt, &_decoder);
76
0
    if (_encoder_init)
77
0
        exr_encoding_destroy(_ctxt, &_encoder);
78
0
}
79
80
Compressor::Format
81
Compressor::format () const
82
0
{
83
0
    return XDR;
84
0
}
85
86
int
87
Compressor::numScanLines () const
88
0
{
89
0
    return _numScanLines;
90
0
}
91
92
int
93
Compressor::compress (
94
    const char* inPtr, int inSize, int minY, const char*& outPtr)
95
0
{
96
0
    IMATH_NAMESPACE::Box2i range = _header.dataWindow ();
97
98
0
    range.min.y = minY;
99
0
    range.max.y = minY + _numScanLines - 1;
100
101
0
    return static_cast<int> (
102
0
        runEncodeStep (inPtr, inSize, range, outPtr));
103
0
}
104
105
int
106
Compressor::uncompress (
107
    const char* inPtr, int inSize, int minY, const char*& outPtr)
108
0
{
109
0
    IMATH_NAMESPACE::Box2i range = _header.dataWindow ();
110
111
0
    range.min.y = minY;
112
0
    range.max.y = minY + _numScanLines - 1;
113
114
0
    return static_cast<int> (
115
0
        runDecodeStep (inPtr, inSize, range, outPtr));
116
0
}
117
118
int
119
Compressor::compressTile (
120
    const char* inPtr, int inSize, Box2i range, const char*& outPtr)
121
0
{
122
0
    return static_cast<int> (
123
0
        runEncodeStep (inPtr, inSize, range, outPtr));
124
0
}
125
126
int
127
Compressor::uncompressTile (
128
    const char* inPtr, int inSize, Box2i range, const char*& outPtr)
129
0
{
130
0
    return static_cast<int> (
131
0
        runDecodeStep (inPtr, inSize, range, outPtr));
132
0
}
133
134
uint64_t
135
Compressor::runEncodeStep (
136
    const char* inPtr,
137
    int inSize,
138
    IMATH_NAMESPACE::Box2i range,
139
    const char*& outPtr)
140
0
{
141
    // special case
142
0
    if (inSize == 0)
143
0
    {
144
0
        outPtr = inPtr;
145
0
        return 0;
146
0
    }
147
148
0
    exr_chunk_info_t cinfo = {0};
149
150
0
    if (EXR_ERR_SUCCESS != exr_chunk_default_initialize (
151
0
            _ctxt, 0, (const exr_attr_box2i_t*) &range, _levelX, _levelY, &cinfo))
152
0
        throw IEX_NAMESPACE::ArgExc ("Unable to initialize chunk information");
153
154
0
    cinfo.type = _store_type;
155
0
    if (!_encoder_init)
156
0
    {
157
0
        if (EXR_ERR_SUCCESS != exr_encoding_initialize(_ctxt, 0, &cinfo, &_encoder))
158
0
            throw IEX_NAMESPACE::ArgExc ("Unable to initialize encoder type");
159
160
0
        _encoder_init = true;
161
0
    }
162
0
    else
163
0
    {
164
0
        if (EXR_ERR_SUCCESS != exr_encoding_update(_ctxt, 0, &cinfo, &_encoder))
165
0
            throw IEX_NAMESPACE::ArgExc ("Unable to update encoder type");
166
0
    }
167
168
0
    _encoder.packed_buffer = const_cast<char*> (inPtr);
169
0
    _encoder.packed_bytes = inSize;
170
171
0
    if (EXR_ERR_SUCCESS != exr_compress_chunk(&_encoder))
172
0
        throw IEX_NAMESPACE::ArgExc ("Unable to run compression routine");
173
174
0
    outPtr = (const char*) _encoder.compressed_buffer;
175
176
0
    _encoder.packed_buffer = nullptr;
177
0
    _encoder.packed_bytes = 0;
178
179
0
    return _encoder.compressed_bytes;
180
0
}
181
182
////////////////////////////////////////
183
184
uint64_t
185
Compressor::runDecodeStep (
186
    const char* inPtr,
187
    int inSize,
188
    IMATH_NAMESPACE::Box2i range,
189
    const char*& outPtr)
190
0
{
191
    // special case
192
0
    if (inSize == 0)
193
0
    {
194
0
        if (!_memory_buffer)
195
0
        {
196
0
            _buf_sz = _maxScanLineSize * _numScanLines;
197
0
            _memory_buffer.reset (new char[_buf_sz]);
198
0
        }
199
0
        outPtr = _memory_buffer.get ();
200
0
        return 0;
201
0
    }
202
203
0
    exr_result_t rv;
204
0
    exr_chunk_info_t cinfo = {0};
205
0
    uint64_t expectedOutputSize;
206
207
0
    if (EXR_ERR_SUCCESS != exr_chunk_default_initialize (
208
0
            _ctxt, 0, (const exr_attr_box2i_t*) &range, _levelX, _levelY, &cinfo))
209
0
        throw IEX_NAMESPACE::ArgExc ("Unable to initialize chunk information");
210
211
0
    if (_store_type == EXR_STORAGE_DEEP_SCANLINE ||
212
0
        _store_type == EXR_STORAGE_DEEP_TILED)
213
0
    {
214
0
        if (_expectedSize != 0)
215
0
        {
216
0
            expectedOutputSize = _expectedSize;
217
0
            cinfo.unpacked_size = _expectedSize;
218
0
        }
219
0
        else
220
0
        {
221
0
            expectedOutputSize = _maxScanLineSize;
222
0
            cinfo.unpacked_size = _maxScanLineSize;
223
0
        }
224
0
    }
225
0
    else
226
0
    {
227
0
        expectedOutputSize = cinfo.unpacked_size;
228
0
    }
229
230
0
    cinfo.packed_size = inSize;
231
0
    cinfo.type = _store_type;
232
0
    if (_buf_sz < expectedOutputSize)
233
0
    {
234
0
        uint64_t fullbuf = _maxScanLineSize * _numScanLines;
235
0
        _buf_sz = std::max (fullbuf, expectedOutputSize);
236
0
        _memory_buffer.reset (new char[_buf_sz]);
237
0
    }
238
239
0
    if (!_decoder_init)
240
0
    {
241
0
        if (EXR_ERR_SUCCESS != exr_decoding_initialize(_ctxt, 0, &cinfo, &_decoder))
242
0
            throw IEX_NAMESPACE::ArgExc ("Unable to initialize decoder type");
243
244
0
        _decoder_init = true;
245
0
    }
246
0
    else
247
0
    {
248
0
        if (EXR_ERR_SUCCESS != exr_decoding_update(_ctxt, 0, &cinfo, &_decoder))
249
0
            throw IEX_NAMESPACE::ArgExc ("Unable to update decoder");
250
0
    }
251
252
//    if (_store_type == EXR_STORAGE_DEEP_SCANLINE ||
253
//        _store_type == EXR_STORAGE_DEEP_TILED)
254
//    {
255
//        _decoder.decode_flags |= EXR_DECODE_ALLOW_SHORT_DECODE;
256
//    }
257
258
0
    _decoder.packed_buffer = const_cast<char*> (inPtr);
259
0
    _decoder.unpacked_buffer = _memory_buffer.get();
260
0
    _decoder.unpacked_alloc_size = _buf_sz;
261
262
0
    rv = exr_uncompress_chunk(&_decoder);
263
264
0
    _decoder.packed_buffer   = nullptr;
265
0
    _decoder.unpacked_buffer = nullptr;
266
0
    _decoder.unpacked_alloc_size = 0;
267
268
0
    outPtr = _memory_buffer.get ();
269
270
0
    if (EXR_ERR_SUCCESS != rv)
271
0
        throw IEX_NAMESPACE::ArgExc ("Unable to run compression routine");
272
273
0
    return _decoder.bytes_decompressed;
274
0
}
275
276
277
Compressor*
278
newCompressor (Compression c, size_t maxScanLineSize, const Header& hdr)
279
0
{
280
0
    Compressor* ret = nullptr;
281
282
    // clang-format off
283
0
    switch (c)
284
0
    {
285
0
        case RLE_COMPRESSION:
286
287
0
            ret = new RleCompressor (hdr, maxScanLineSize);
288
0
            break;
289
290
0
        case ZIPS_COMPRESSION:
291
292
0
            ret = new ZipCompressor (hdr, maxScanLineSize, 1);
293
0
            break;
294
295
0
        case ZIP_COMPRESSION:
296
297
0
            ret = new ZipCompressor (hdr, maxScanLineSize, 16);
298
0
            break;
299
300
0
        case PIZ_COMPRESSION:
301
302
0
            ret = new PizCompressor (hdr, maxScanLineSize, 32);
303
0
            break;
304
305
0
        case PXR24_COMPRESSION:
306
307
0
            ret = new Pxr24Compressor (hdr, maxScanLineSize, 16);
308
0
            break;
309
310
0
        case B44_COMPRESSION:
311
312
0
            ret = new B44Compressor (hdr, maxScanLineSize, 32, false);
313
0
            break;
314
315
0
        case B44A_COMPRESSION:
316
317
0
            ret = new B44Compressor (hdr, maxScanLineSize, 32, true);
318
0
            break;
319
320
0
        case DWAA_COMPRESSION:
321
322
0
            ret = new DwaCompressor (
323
0
                hdr,
324
0
                static_cast<int> (maxScanLineSize),
325
0
                32,
326
0
                DwaCompressor::STATIC_HUFFMAN);
327
0
            break;
328
329
0
        case DWAB_COMPRESSION:
330
331
0
            ret = new DwaCompressor (
332
0
                hdr,
333
0
                static_cast<int> (maxScanLineSize),
334
0
                256,
335
0
                DwaCompressor::STATIC_HUFFMAN);
336
0
            break;
337
338
0
        default: break;
339
0
    }
340
    // clang-format on
341
342
0
    if (ret && ret->storageType () == EXR_STORAGE_LAST_TYPE)
343
0
        ret->setStorageType (EXR_STORAGE_SCANLINE);
344
345
0
    return ret;
346
0
}
347
348
// for a given compression type, return the number of scanlines
349
// compressed into a single chunk
350
// TODO add to API and move to ImfCompressor.cpp
351
int
352
numLinesInBuffer (Compression comp)
353
0
{
354
0
    int numScanlines = getCompressionNumScanlines (comp);
355
0
    if (exr_compression_lines_per_chunk ((exr_compression_t)comp) != numScanlines)
356
0
        throw IEX_NAMESPACE::ArgExc ("Mismatch in compression lines per chunk");
357
0
    if (numScanlines < 1)
358
0
        throw IEX_NAMESPACE::ArgExc ("Unknown compression type");
359
0
    return numScanlines;
360
0
}
361
362
Compressor*
363
newTileCompressor (
364
    Compression c, size_t tileLineSize, size_t numTileLines, const Header& hdr)
365
0
{
366
0
    Compressor* ret = nullptr;
367
    // clang-format off
368
0
    switch (c)
369
0
    {
370
0
        case RLE_COMPRESSION:
371
372
0
            ret = new RleCompressor (hdr, uiMult (tileLineSize, numTileLines));
373
0
            break;
374
375
0
        case ZIPS_COMPRESSION:
376
0
        case ZIP_COMPRESSION:
377
378
0
            ret = new ZipCompressor (hdr, tileLineSize, numTileLines);
379
0
            break;
380
381
0
        case PIZ_COMPRESSION:
382
383
0
            ret = new PizCompressor (hdr, tileLineSize, numTileLines);
384
0
            break;
385
386
0
        case PXR24_COMPRESSION:
387
388
0
            ret = new Pxr24Compressor (hdr, tileLineSize, numTileLines);
389
0
            break;
390
391
0
        case B44_COMPRESSION:
392
393
0
            ret = new B44Compressor (hdr, tileLineSize, numTileLines, false);
394
0
            break;
395
396
0
        case B44A_COMPRESSION:
397
398
0
            ret = new B44Compressor (hdr, tileLineSize, numTileLines, true);
399
0
            break;
400
401
0
        case DWAA_COMPRESSION:
402
403
0
            ret = new DwaCompressor (
404
0
                hdr,
405
0
                static_cast<int> (tileLineSize),
406
0
                static_cast<int> (numTileLines),
407
0
                DwaCompressor::DEFLATE);
408
0
            break;
409
410
0
        case DWAB_COMPRESSION:
411
412
0
            ret = new DwaCompressor (
413
0
                hdr,
414
0
                static_cast<int> (tileLineSize),
415
0
                static_cast<int> (numTileLines),
416
0
                DwaCompressor::STATIC_HUFFMAN);
417
0
            break;
418
419
0
        default: break;
420
0
    }
421
    // clang-format on
422
423
0
    if (ret && ret->storageType () == EXR_STORAGE_LAST_TYPE)
424
0
        ret->setStorageType (EXR_STORAGE_TILED);
425
426
0
    return ret;
427
0
}
428
429
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT