Coverage Report

Created: 2026-07-25 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openexr/src/lib/OpenEXR/ImfCompression.cpp
Line
Count
Source
1
//
2
// SPDX-License-Identifier: BSD-3-Clause
3
// Copyright (c) Contributors to the OpenEXR Project.
4
//
5
// -----------------------------------------------------------------------------
6
//
7
//  HOW TO ADD NEW COMPRESSION TECHNIQUE
8
//  ====================================
9
//
10
//  1. Creating your C++ compression class
11
//
12
//      1. Implement a new src/lib/OpenEXR/Imf*Compressor.{h, cpp} class deriving
13
//         from ImfCompressor.
14
//
15
//      2. Update the build systems
16
//          * Update src/lib/OpenEXR/CMakeLists.txt to build your codec with cmake.
17
//          * Update BUILD.bazel to build your codec with bazel.
18
//          * NOTE: Both build methods must be implemented !
19
//
20
//  2. C API integration
21
//
22
//      1. src/lib/OpenEXRCore/openexr_attr.h
23
//          * Add your EXR_COMPRESSION_* to the exr_compression_t enum.
24
//
25
//      2. src/lib/OpenEXRCore/compression.c
26
//          * Update exr_compression_lines_per_chunk().
27
//          * Update exr_compress_chunk().
28
//          * Update exr_uncompress_chunk().
29
//
30
//      5. src/lib/OpenEXRCore/validation.c
31
//          * Update validate_deep_data()
32
//
33
//      6. Tests
34
//
35
//          1. src/test/OpenEXRCoreTest/compression.h
36
//              * Declare your own test*Compression().
37
//
38
//          2. src/test/OpenEXRCoreTest/compression.cpp
39
//              * Update doWriteRead().
40
//              * Implement your test*Compression() function at the end.
41
//
42
//          3. src/test/OpenEXRCoreTest/main.cpp
43
//              * Call your test function in main().
44
//
45
//          4. src/test/OpenEXRCoreTest/CMakeLists.txt
46
//              * Add your test function name to define_openexrcore_tests().
47
//
48
//  3. C++ API integration
49
//
50
//      1. src/lib/OpenEXR/ImfCompression.h
51
//         * Add your method at the end of the Compression enum below.
52
//
53
//      2. src/lib/OpenEXR/ImfCompression.cpp
54
//          * Add your CompressionDesc(s) to IdToDesc[]. This is used by the API
55
//             to check its capabilities and requirements.
56
//             NOTE: the order MUST MATCH Compression's order, as we access the
57
//                   descriptions by index.
58
//           * Update CompressionNameToId to allow the API to map from your
59
//             compressor's identifier(s) to its/their Compression value(s).
60
//
61
//      3. openexr/src/lib/OpenEXR/ImfCompressor.cpp
62
//           * Include your Imf*Compressor.h.
63
//           * Add your instantiation calls to newCompressor().
64
//           * Add your instantiation calls to newTileCompressor().
65
//
66
//      4. src/lib/OpenEXR/ImfCRgbaFile.h
67
//          * Add a IMF_*_COMPRESSION define for your compression method, making
68
//            sure to update IMF_NUM_COMPRESSION_METHODS with the proper index.
69
//
70
//      5. Tests
71
//
72
//          1. src/test/OpenEXRTest/testCompressionApi.cpp
73
//              * See the comments to update that file and ensure your method is
74
//                properly registered.
75
//
76
//  4. Documentation
77
//
78
//      1. Compression methods are listed or described in many places throughout
79
//         the docs and you will have to update all relevant *.rst files.
80
//
81
// -----------------------------------------------------------------------------
82
83
#include "ImfNamespace.h"
84
#include "ImfCompression.h"
85
#include <map>
86
#include <cctype>
87
88
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
89
90
/// Store codec properties so they may be queried in various places.
91
struct CompressionDesc
92
{
93
    std::string name;         // short name
94
    std::string desc;         // method description
95
    int         numScanlines; // number of scanlines required
96
    bool        lossy;        // true if lossy algorithm
97
    bool        deep;         // true is capable of compressing deep data
98
99
    CompressionDesc (
100
        std::string _name,
101
        std::string _desc,
102
        int         _scanlines,
103
        bool        _lossy,
104
        bool        _deep)
105
20
    {
106
20
        name         = _name;
107
20
        desc         = _desc;
108
20
        numScanlines = _scanlines;
109
20
        lossy        = _lossy;
110
20
        deep         = _deep;
111
20
    }
112
};
113
114
// NOTE: IdToDesc order MUST match Imf::Compression enum.
115
// clang-format off
116
static const CompressionDesc IdToDesc[] = {
117
    CompressionDesc (
118
        "none",
119
        "no compression.",
120
        1,
121
        false,
122
        true),
123
    CompressionDesc (
124
        "rle",
125
        "run-length encoding.",
126
        1,
127
        false,
128
        true),
129
    CompressionDesc (
130
        "zips",
131
        "zlib compression, one scan line at a time.",
132
        1,
133
        false,
134
        true),
135
    CompressionDesc (
136
        "zip",
137
        "zlib compression, in blocks of 16 scan lines.",
138
        16,
139
        false,
140
        false),
141
    CompressionDesc (
142
        "piz",
143
        "piz-based wavelet compression, in blocks of 32 scan lines.",
144
        32,
145
        false,
146
        false),
147
    CompressionDesc (
148
        "pxr24",
149
        "lossy 24-bit float compression, in blocks of 16 scan lines.",
150
        16,
151
        true,
152
        false),
153
    CompressionDesc (
154
        "b44",
155
        "lossy 4-by-4 pixel block compression, fixed compression rate.",
156
        32,
157
        true,
158
        false),
159
    CompressionDesc (
160
        "b44a",
161
        "lossy 4-by-4 pixel block compression, flat fields are compressed more.",
162
        32,
163
        true,
164
        false),
165
    CompressionDesc (
166
        "dwaa",
167
        "lossy DCT based compression, in blocks of 32 scanlines. More efficient "
168
        "for partial buffer access.",
169
        32,
170
        true,
171
        false),
172
    CompressionDesc (
173
        "dwab",
174
        "lossy DCT based compression, in blocks of 256 scanlines. More efficient "
175
        "space wise and faster to decode full frames than DWAA_COMPRESSION.",
176
        256,
177
        true,
178
        false),
179
};
180
// clang-format on
181
182
// NOTE: CompressionNameToId order MUST match Imf::Compression enum.
183
static const std::map<std::string, Compression> CompressionNameToId = {
184
    {"no", Compression::NO_COMPRESSION},
185
    {"none", Compression::NO_COMPRESSION},
186
    {"rle", Compression::RLE_COMPRESSION},
187
    {"zips", Compression::ZIPS_COMPRESSION},
188
    {"zip", Compression::ZIP_COMPRESSION},
189
    {"piz", Compression::PIZ_COMPRESSION},
190
    {"pxr24", Compression::PXR24_COMPRESSION},
191
    {"b44", Compression::B44_COMPRESSION},
192
    {"b44a", Compression::B44A_COMPRESSION},
193
    {"dwaa", Compression::DWAA_COMPRESSION},
194
    {"dwab", Compression::DWAB_COMPRESSION},
195
};
196
197
0
#define UNKNOWN_COMPRESSION_ID_MSG "INVALID COMPRESSION ID"
198
199
/// Returns a codec ID's short name (lowercase).
200
void
201
getCompressionNameFromId (Compression id, std::string& name)
202
0
{
203
0
    if (id < NO_COMPRESSION || id >= NUM_COMPRESSION_METHODS)
204
0
        name = UNKNOWN_COMPRESSION_ID_MSG;
205
0
    name = IdToDesc[static_cast<int> (id)].name;
206
0
}
207
208
/// Returns a codec ID's short description (lowercase).
209
void
210
getCompressionDescriptionFromId (Compression id, std::string& desc)
211
0
{
212
0
    if (id < NO_COMPRESSION || id >= NUM_COMPRESSION_METHODS)
213
0
        desc = UNKNOWN_COMPRESSION_ID_MSG;
214
0
    desc = IdToDesc[static_cast<int> (id)].name + ": " +
215
0
           IdToDesc[static_cast<int> (id)].desc;
216
0
}
217
218
/// Returns the codec name's ID, NUM_COMPRESSION_METHODS if not found.
219
void
220
getCompressionIdFromName (const std::string& name, Compression& id)
221
0
{
222
0
    std::string lowercaseName (name);
223
0
    for (auto& ch: lowercaseName)
224
0
        ch = std::tolower (ch);
225
226
0
    auto it = CompressionNameToId.find (lowercaseName);
227
0
    id      = it != CompressionNameToId.end ()
228
0
                  ? it->second
229
0
                  : Compression::NUM_COMPRESSION_METHODS;
230
0
}
231
232
/// Return true if a compression id exists.
233
bool
234
isValidCompression (int id)
235
0
{
236
0
    return id >= NO_COMPRESSION && id < NUM_COMPRESSION_METHODS;
237
0
}
238
239
/// Return a string enumerating all compression names, with a custom separator.
240
void
241
getCompressionNamesString (const std::string& separator, std::string& str)
242
0
{
243
0
    int i = 0;
244
0
    for (; i < static_cast<int> (NUM_COMPRESSION_METHODS) - 1; i++)
245
0
    {
246
0
        str += IdToDesc[i].name + separator;
247
0
    }
248
0
    str += IdToDesc[i].name;
249
0
}
250
251
/// Return the number of scan lines expected by a given compression method.
252
int
253
getCompressionNumScanlines (Compression id)
254
0
{
255
0
    if (id < NO_COMPRESSION || id >= NUM_COMPRESSION_METHODS) return -1;
256
0
    return IdToDesc[static_cast<int> (id)].numScanlines;
257
0
}
258
259
/// Return true is the compression method exists and doesn't preserve data integrity.
260
bool
261
isLossyCompression (Compression id)
262
0
{
263
0
    return id >= NO_COMPRESSION && id < NUM_COMPRESSION_METHODS &&
264
0
           IdToDesc[static_cast<int> (id)].lossy;
265
0
}
266
267
/// Return true is the compression method exists and supports deep data.
268
bool
269
isValidDeepCompression (Compression id)
270
0
{
271
0
    return id >= NO_COMPRESSION && id < NUM_COMPRESSION_METHODS &&
272
0
           IdToDesc[static_cast<int> (id)].deep;
273
0
}
274
275
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT