/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 |