GCC Code Coverage Report


Directory: ./
File: openvdb/openvdb/io/Compression.h
Date: 2022-07-25 17:40:05
Exec Total Coverage
Lines: 199 215 92.6%
Functions: 111 411 27.0%
Branches: 208 562 37.0%

Line Branch Exec Source
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3
4 #ifndef OPENVDB_IO_COMPRESSION_HAS_BEEN_INCLUDED
5 #define OPENVDB_IO_COMPRESSION_HAS_BEEN_INCLUDED
6
7 #include <openvdb/Types.h>
8 #include <openvdb/MetaMap.h>
9 #include <openvdb/math/Math.h> // for negative()
10 #include "io.h" // for getDataCompression(), etc.
11 #include "DelayedLoadMetadata.h"
12 #include <algorithm>
13 #include <iostream>
14 #include <memory>
15 #include <string>
16 #include <vector>
17
18
19 namespace openvdb {
20 OPENVDB_USE_VERSION_NAMESPACE
21 namespace OPENVDB_VERSION_NAME {
22 namespace io {
23
24 /// @brief OR-able bit flags for compression options on input and output streams
25 /// @details
26 /// <dl>
27 /// <dt><tt>COMPRESS_NONE</tt>
28 /// <dd>On write, don't compress data.<br>
29 /// On read, the input stream contains uncompressed data.
30 ///
31 /// <dt><tt>COMPRESS_ZIP</tt>
32 /// <dd>When writing grids other than level sets or fog volumes, apply
33 /// ZLIB compression to internal and leaf node value buffers.<br>
34 /// When reading grids other than level sets or fog volumes, indicate that
35 /// the value buffers of internal and leaf nodes are ZLIB-compressed.<br>
36 /// ZLIB compresses well but is slow.
37 ///
38 /// <dt><tt>COMPRESS_ACTIVE_MASK</tt>
39 /// <dd>When writing a grid of any class, don't output a node's inactive values
40 /// if it has two or fewer distinct values. Instead, output minimal information
41 /// to permit the lossless reconstruction of inactive values.<br>
42 /// On read, nodes might have been stored without inactive values.
43 /// Where necessary, reconstruct inactive values from available information.
44 ///
45 /// <dt><tt>COMPRESS_BLOSC</tt>
46 /// <dd>When writing grids other than level sets or fog volumes, apply
47 /// Blosc compression to internal and leaf node value buffers.<br>
48 /// When reading grids other than level sets or fog volumes, indicate that
49 /// the value buffers of internal and leaf nodes are Blosc-compressed.<br>
50 /// Blosc is much faster than ZLIB and produces comparable file sizes.
51 /// </dl>
52 enum {
53 COMPRESS_NONE = 0,
54 COMPRESS_ZIP = 0x1,
55 COMPRESS_ACTIVE_MASK = 0x2,
56 COMPRESS_BLOSC = 0x4
57 };
58
59 /// Return a string describing the given compression flags.
60 OPENVDB_API std::string compressionToString(uint32_t flags);
61
62
63 ////////////////////////////////////////
64
65
66 /// @internal Per-node indicator byte that specifies what additional metadata
67 /// is stored to permit reconstruction of inactive values
68 enum {
69 /*0*/ NO_MASK_OR_INACTIVE_VALS, // no inactive vals, or all inactive vals are +background
70 /*1*/ NO_MASK_AND_MINUS_BG, // all inactive vals are -background
71 /*2*/ NO_MASK_AND_ONE_INACTIVE_VAL, // all inactive vals have the same non-background val
72 /*3*/ MASK_AND_NO_INACTIVE_VALS, // mask selects between -background and +background
73 /*4*/ MASK_AND_ONE_INACTIVE_VAL, // mask selects between backgd and one other inactive val
74 /*5*/ MASK_AND_TWO_INACTIVE_VALS, // mask selects between two non-background inactive vals
75 /*6*/ NO_MASK_AND_ALL_VALS // > 2 inactive vals, so no mask compression at all
76 };
77
78
79 template <typename ValueT, typename MaskT>
80 struct MaskCompress
81 {
82 // Comparison function for values
83 static inline bool eq(const ValueT& a, const ValueT& b) {
84 return math::isExactlyEqual(a, b);
85 }
86
87 92659 MaskCompress(
88 const MaskT& valueMask, const MaskT& childMask,
89 const ValueT* srcBuf, const ValueT& background)
90
2/2
✓ Branch 0 taken 1660 times.
✓ Branch 1 taken 830 times.
95979 {
91 /// @todo Consider all values, not just inactive values?
92
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
92659 inactiveVal[0] = inactiveVal[1] = background;
93 int numUniqueInactiveVals = 0;
94 92659 for (typename MaskT::OffIterator it = valueMask.beginOff();
95
4/4
✓ Branch 0 taken 23368718 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 23322393 times.
✓ Branch 3 taken 46325 times.
93474753 numUniqueInactiveVals < 3 && it; ++it)
96 {
97 const Index32 idx = it.pos();
98
99 // Skip inactive values that are actually child node pointers.
100
2/2
✓ Branch 1 taken 26362 times.
✓ Branch 2 taken 23296031 times.
46644727 if (childMask.isOn(idx)) continue;
101
102 46592005 const ValueT& val = srcBuf[idx];
103
4/4
✓ Branch 0 taken 23251571 times.
✓ Branch 1 taken 44460 times.
✓ Branch 2 taken 553688 times.
✓ Branch 3 taken 53113 times.
47716672 const bool unique = !(
104
2/2
✓ Branch 0 taken 562341 times.
✓ Branch 1 taken 21780786 times.
44686207 (numUniqueInactiveVals > 0 && MaskCompress::eq(val, inactiveVal[0])) ||
105
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 553678 times.
1107368 (numUniqueInactiveVals > 1 && MaskCompress::eq(val, inactiveVal[1]))
106 );
107 if (unique) {
108
2/4
✓ Branch 0 taken 53113 times.
✓ Branch 1 taken 10 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
106228 if (numUniqueInactiveVals < 2) inactiveVal[numUniqueInactiveVals] = val;
109 106228 ++numUniqueInactiveVals;
110 }
111 }
112
113 92659 metadata = NO_MASK_OR_INACTIVE_VALS;
114
115
2/2
✓ Branch 0 taken 35807 times.
✓ Branch 1 taken 10528 times.
92659 if (numUniqueInactiveVals == 1) {
116
2/2
✓ Branch 0 taken 8178 times.
✓ Branch 1 taken 27353 times.
71059 if (!MaskCompress::eq(inactiveVal[0], background)) {
117
2/2
✓ Branch 0 taken 8177 times.
✓ Branch 1 taken 1 times.
16354 if (MaskCompress::eq(inactiveVal[0], math::negative(background))) {
118 16353 metadata = NO_MASK_AND_MINUS_BG;
119 } else {
120 1 metadata = NO_MASK_AND_ONE_INACTIVE_VAL;
121 }
122 }
123
2/2
✓ Branch 0 taken 8643 times.
✓ Branch 1 taken 1885 times.
21048 } else if (numUniqueInactiveVals == 2) {
124 metadata = NO_MASK_OR_INACTIVE_VALS;
125
4/4
✓ Branch 0 taken 4312 times.
✓ Branch 1 taken 4331 times.
✓ Branch 2 taken 4309 times.
✓ Branch 3 taken 3 times.
17280 if (!MaskCompress::eq(inactiveVal[0], background) && !MaskCompress::eq(inactiveVal[1], background)) {
126 // If neither inactive value is equal to the background, both values
127 // need to be saved, along with a mask that selects between them.
128 3 metadata = MASK_AND_TWO_INACTIVE_VALS;
129
130
2/2
✓ Branch 0 taken 4309 times.
✓ Branch 1 taken 4331 times.
17277 } else if (MaskCompress::eq(inactiveVal[1], background)) {
131
2/2
✓ Branch 0 taken 4299 times.
✓ Branch 1 taken 10 times.
8616 if (MaskCompress::eq(inactiveVal[0], math::negative(background))) {
132 // If the second inactive value is equal to the background and
133 // the first is equal to -background, neither value needs to be saved,
134 // but save a mask that selects between -background and +background.
135 8597 metadata = MASK_AND_NO_INACTIVE_VALS;
136 } else {
137 // If the second inactive value is equal to the background, only
138 // the first value needs to be saved, along with a mask that selects
139 // between it and the background.
140 19 metadata = MASK_AND_ONE_INACTIVE_VAL;
141 }
142
1/2
✓ Branch 0 taken 4331 times.
✗ Branch 1 not taken.
8661 } else if (MaskCompress::eq(inactiveVal[0], background)) {
143
2/2
✓ Branch 0 taken 4277 times.
✓ Branch 1 taken 54 times.
8661 if (MaskCompress::eq(inactiveVal[1], math::negative(background))) {
144 // If the first inactive value is equal to the background and
145 // the second is equal to -background, neither value needs to be saved,
146 // but save a mask that selects between -background and +background.
147 8553 metadata = MASK_AND_NO_INACTIVE_VALS;
148 std::swap(inactiveVal[0], inactiveVal[1]);
149 } else {
150 // If the first inactive value is equal to the background, swap it
151 // with the second value and save only that value, along with a mask
152 // that selects between it and the background.
153 std::swap(inactiveVal[0], inactiveVal[1]);
154 108 metadata = MASK_AND_ONE_INACTIVE_VAL;
155 }
156 }
157
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1875 times.
3768 } else if (numUniqueInactiveVals > 2) {
158 19 metadata = NO_MASK_AND_ALL_VALS;
159 }
160 92659 }
161
162 int8_t metadata = NO_MASK_AND_ALL_VALS;
163 ValueT inactiveVal[2];
164 };
165
166
167 ////////////////////////////////////////
168
169
170 /// @brief RealToHalf and its specializations define a mapping from
171 /// floating-point data types to analogous half float types.
172 template<typename T>
173 struct RealToHalf {
174 enum { isReal = false }; // unless otherwise specified, type T is not a floating-point type
175 using HalfT = T; // type T's half float analogue is T itself
176 static HalfT convert(const T& val) { return val; }
177 };
178 template<> struct RealToHalf<float> {
179 enum { isReal = true };
180 using HalfT = math::half;
181
1/2
✓ Branch 1 taken 524288 times.
✗ Branch 2 not taken.
524288 static HalfT convert(float val) { return HalfT(val); }
182 };
183 template<> struct RealToHalf<double> {
184 enum { isReal = true };
185 using HalfT = math::half;
186 // A half can only be constructed from a float, so cast the value to a float first.
187
2/4
✓ Branch 1 taken 524338 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 50 times.
✗ Branch 5 not taken.
524338 static HalfT convert(double val) { return HalfT(float(val)); }
188 };
189 template<> struct RealToHalf<Vec2s> {
190 enum { isReal = true };
191 using HalfT = Vec2H;
192
1/2
✓ Branch 1 taken 524288 times.
✗ Branch 2 not taken.
524288 static HalfT convert(const Vec2s& val) { return HalfT(val); }
193 };
194 template<> struct RealToHalf<Vec2d> {
195 enum { isReal = true };
196 using HalfT = Vec2H;
197 // A half can only be constructed from a float, so cast the vector's elements to floats first.
198
1/2
✓ Branch 1 taken 524288 times.
✗ Branch 2 not taken.
524288 static HalfT convert(const Vec2d& val) { return HalfT(Vec2s(val)); }
199 };
200 template<> struct RealToHalf<Vec3s> {
201 enum { isReal = true };
202 using HalfT = Vec3H;
203
1/2
✓ Branch 1 taken 652288 times.
✗ Branch 2 not taken.
652288 static HalfT convert(const Vec3s& val) { return HalfT(val); }
204 };
205 template<> struct RealToHalf<Vec3d> {
206 enum { isReal = true };
207 using HalfT = Vec3H;
208 // A half can only be constructed from a float, so cast the vector's elements to floats first.
209
1/2
✓ Branch 1 taken 524288 times.
✗ Branch 2 not taken.
524288 static HalfT convert(const Vec3d& val) { return HalfT(Vec3s(val)); }
210 };
211
212
213 /// Return the given value truncated to 16-bit float precision.
214 template<typename T>
215 inline T
216 2 truncateRealToHalf(const T& val)
217 {
218 2 return T(RealToHalf<T>::convert(val));
219 }
220
221
222 ////////////////////////////////////////
223
224
225 OPENVDB_API size_t zipToStreamSize(const char* data, size_t numBytes);
226 OPENVDB_API void zipToStream(std::ostream&, const char* data, size_t numBytes);
227 OPENVDB_API void unzipFromStream(std::istream&, char* data, size_t numBytes);
228 OPENVDB_API size_t bloscToStreamSize(const char* data, size_t valSize, size_t numVals);
229 OPENVDB_API void bloscToStream(std::ostream&, const char* data, size_t valSize, size_t numVals);
230 OPENVDB_API void bloscFromStream(std::istream&, char* data, size_t numBytes);
231
232 /// @brief Read data from a stream.
233 /// @param is the input stream
234 /// @param data the contiguous array of data to read in
235 /// @param count the number of elements to read in
236 /// @param compression whether and how the data is compressed (either COMPRESS_NONE,
237 /// COMPRESS_ZIP, COMPRESS_ACTIVE_MASK or COMPRESS_BLOSC)
238 /// @param metadata optional pointer to a DelayedLoadMetadata object that stores
239 /// the size of the compressed buffer
240 /// @param metadataOffset offset into DelayedLoadMetadata, ignored if pointer is null
241 /// @throw IoError if @a compression is COMPRESS_BLOSC but OpenVDB was compiled
242 /// without Blosc support.
243 /// @details This default implementation is instantiated only for types
244 /// whose size can be determined by the sizeof() operator.
245 template<typename T>
246 inline void
247 41986 readData(std::istream& is, T* data, Index count, uint32_t compression,
248 DelayedLoadMetadata* metadata = nullptr, size_t metadataOffset = size_t(0))
249 {
250 41986 const bool seek = data == nullptr;
251
2/2
✓ Branch 0 taken 12654 times.
✓ Branch 1 taken 8339 times.
41986 if (seek) {
252
6/14
✓ Branch 1 taken 12654 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12654 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 12654 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 12654 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 12654 times.
✓ Branch 14 taken 12654 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
75924 assert(!getStreamMetadataPtr(is) || getStreamMetadataPtr(is)->seekable());
253 }
254 41986 const bool hasCompression = compression & (COMPRESS_BLOSC | COMPRESS_ZIP);
255
256
4/4
✓ Branch 0 taken 12650 times.
✓ Branch 1 taken 8343 times.
✓ Branch 2 taken 10864 times.
✓ Branch 3 taken 1786 times.
41986 if (metadata && seek && hasCompression) {
257 21728 size_t compressedSize = metadata->getCompressedSize(metadataOffset);
258 21728 is.seekg(compressedSize, std::ios_base::cur);
259
2/2
✓ Branch 0 taken 1880 times.
✓ Branch 1 taken 8249 times.
20258 } else if (compression & COMPRESS_BLOSC) {
260 3760 bloscFromStream(is, reinterpret_cast<char*>(data), sizeof(T) * count);
261
2/2
✓ Branch 0 taken 564 times.
✓ Branch 1 taken 7685 times.
16498 } else if (compression & COMPRESS_ZIP) {
262 1128 unzipFromStream(is, reinterpret_cast<char*>(data), sizeof(T) * count);
263
2/2
✓ Branch 0 taken 1786 times.
✓ Branch 1 taken 5899 times.
15370 } else if (seek) {
264 3572 is.seekg(sizeof(T) * count, std::ios_base::cur);
265 } else {
266 11798 is.read(reinterpret_cast<char*>(data), sizeof(T) * count);
267 }
268 41986 }
269
270 /// Specialization for std::string input
271 template<>
272 inline void
273 1 readData<std::string>(std::istream& is, std::string* data, Index count, uint32_t /*compression*/,
274 DelayedLoadMetadata* /*metadata*/, size_t /*metadataOffset*/)
275 {
276
2/2
✓ Branch 0 taken 512 times.
✓ Branch 1 taken 1 times.
513 for (Index i = 0; i < count; ++i) {
277 512 size_t len = 0;
278 is >> len;
279 //data[i].resize(len);
280 //is.read(&(data[i][0]), len);
281
282 512 std::string buffer(len+1, ' ');
283
2/4
✓ Branch 1 taken 512 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 512 times.
✗ Branch 5 not taken.
512 is.read(&buffer[0], len+1);
284
2/4
✓ Branch 0 taken 512 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 512 times.
✗ Branch 4 not taken.
512 if (data != nullptr) data[i].assign(buffer, 0, len);
285 }
286 1 }
287
288 /// HalfReader wraps a static function, read(), that is analogous to readData(), above,
289 /// except that it is partially specialized for floating-point types in order to promote
290 /// 16-bit half float values to full float. A wrapper class is required because
291 /// only classes, not functions, can be partially specialized.
292 template<bool IsReal, typename T> struct HalfReader;
293 /// Partial specialization for non-floating-point types (no half to float promotion)
294 template<typename T>
295 struct HalfReader</*IsReal=*/false, T> {
296 static inline void read(std::istream& is, T* data, Index count, uint32_t compression,
297 DelayedLoadMetadata* metadata = nullptr, size_t metadataOffset = size_t(0)) {
298
2/122
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 16 taken 512 times.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✓ Branch 25 taken 512 times.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 79 not taken.
✗ Branch 80 not taken.
✗ Branch 82 not taken.
✗ Branch 83 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 91 not taken.
✗ Branch 92 not taken.
✗ Branch 94 not taken.
✗ Branch 95 not taken.
✗ Branch 97 not taken.
✗ Branch 98 not taken.
✗ Branch 100 not taken.
✗ Branch 101 not taken.
✗ Branch 103 not taken.
✗ Branch 104 not taken.
✗ Branch 106 not taken.
✗ Branch 107 not taken.
✗ Branch 109 not taken.
✗ Branch 110 not taken.
✗ Branch 112 not taken.
✗ Branch 113 not taken.
✗ Branch 115 not taken.
✗ Branch 116 not taken.
✗ Branch 118 not taken.
✗ Branch 119 not taken.
✗ Branch 121 not taken.
✗ Branch 122 not taken.
✗ Branch 124 not taken.
✗ Branch 125 not taken.
✗ Branch 127 not taken.
✗ Branch 128 not taken.
✗ Branch 130 not taken.
✗ Branch 131 not taken.
✗ Branch 133 not taken.
✗ Branch 134 not taken.
✗ Branch 136 not taken.
✗ Branch 137 not taken.
✗ Branch 139 not taken.
✗ Branch 140 not taken.
✗ Branch 142 not taken.
✗ Branch 143 not taken.
✗ Branch 145 not taken.
✗ Branch 146 not taken.
✗ Branch 148 not taken.
✗ Branch 149 not taken.
✗ Branch 151 not taken.
✗ Branch 152 not taken.
✗ Branch 154 not taken.
✗ Branch 155 not taken.
✗ Branch 157 not taken.
✗ Branch 158 not taken.
✗ Branch 160 not taken.
✗ Branch 161 not taken.
✗ Branch 163 not taken.
✗ Branch 164 not taken.
✗ Branch 166 not taken.
✗ Branch 167 not taken.
✗ Branch 169 not taken.
✗ Branch 170 not taken.
✗ Branch 172 not taken.
✗ Branch 173 not taken.
✗ Branch 175 not taken.
✗ Branch 176 not taken.
✗ Branch 178 not taken.
✗ Branch 179 not taken.
✗ Branch 181 not taken.
✗ Branch 182 not taken.
1024 readData(is, data, count, compression, metadata, metadataOffset);
299 1024 }
300 };
301 /// Partial specialization for floating-point types
302 template<typename T>
303 struct HalfReader</*IsReal=*/true, T> {
304 using HalfT = typename RealToHalf<T>::HalfT;
305 6400 static inline void read(std::istream& is, T* data, Index count, uint32_t compression,
306 DelayedLoadMetadata* metadata = nullptr, size_t metadataOffset = size_t(0)) {
307
2/2
✓ Branch 0 taken 3198 times.
✓ Branch 1 taken 2 times.
6400 if (count < 1) return;
308
2/2
✓ Branch 0 taken 125 times.
✓ Branch 1 taken 3073 times.
6396 if (data == nullptr) {
309 // seek mode - pass through null pointer
310 250 readData<HalfT>(is, nullptr, count, compression, metadata, metadataOffset);
311 } else {
312
1/2
✓ Branch 2 taken 3073 times.
✗ Branch 3 not taken.
6146 std::vector<HalfT> halfData(count); // temp buffer into which to read half float values
313
1/2
✓ Branch 1 taken 3073 times.
✗ Branch 2 not taken.
6146 readData<HalfT>(is, reinterpret_cast<HalfT*>(&halfData[0]), count, compression,
314 metadata, metadataOffset);
315 // Copy half float values from the temporary buffer to the full float output array.
316 6146 std::copy(halfData.begin(), halfData.end(), data);
317 }
318 }
319 };
320
321
322 template<typename T>
323 inline size_t
324 45246 writeDataSize(const T *data, Index count, uint32_t compression)
325 {
326
2/2
✓ Branch 0 taken 21443 times.
✓ Branch 1 taken 1180 times.
45246 if (compression & COMPRESS_BLOSC) {
327 42886 return bloscToStreamSize(reinterpret_cast<const char*>(data), sizeof(T), count);
328
1/2
✓ Branch 0 taken 1180 times.
✗ Branch 1 not taken.
2360 } else if (compression & COMPRESS_ZIP) {
329 2360 return zipToStreamSize(reinterpret_cast<const char*>(data), sizeof(T) * count);
330 } else {
331 return sizeof(T) * count;
332 }
333 }
334
335
336 /// Specialization for std::string output
337 template<>
338 inline size_t
339 writeDataSize<std::string>(const std::string* data, Index count,
340 uint32_t /*compression*/) ///< @todo add compression
341 {
342 size_t size(0);
343 for (Index i = 0; i < count; ++i) {
344 const size_t len = data[i].size();
345 size += sizeof(size_t) + (len+1);
346 }
347 return size;
348 }
349
350
351 /// Write data to a stream.
352 /// @param os the output stream
353 /// @param data the contiguous array of data to write
354 /// @param count the number of elements to write out
355 /// @param compression whether and how to compress the data (either COMPRESS_NONE,
356 /// COMPRESS_ZIP, COMPRESS_ACTIVE_MASK or COMPRESS_BLOSC)
357 /// @throw IoError if @a compression is COMPRESS_BLOSC but OpenVDB was compiled
358 /// without Blosc support.
359 /// @details This default implementation is instantiated only for types
360 /// whose size can be determined by the sizeof() operator.
361 template<typename T>
362 inline void
363 73212 writeData(std::ostream &os, const T *data, Index count, uint32_t compression)
364 {
365
2/2
✓ Branch 0 taken 21923 times.
✓ Branch 1 taken 14683 times.
73212 if (compression & COMPRESS_BLOSC) {
366 43846 bloscToStream(os, reinterpret_cast<const char*>(data), sizeof(T), count);
367
2/2
✓ Branch 0 taken 578 times.
✓ Branch 1 taken 14105 times.
29366 } else if (compression & COMPRESS_ZIP) {
368 1156 zipToStream(os, reinterpret_cast<const char*>(data), sizeof(T) * count);
369 } else {
370 28210 os.write(reinterpret_cast<const char*>(data), sizeof(T) * count);
371 }
372 73212 }
373
374 /// Specialization for std::string output
375 template<>
376 inline void
377 1 writeData<std::string>(std::ostream& os, const std::string* data, Index count,
378 uint32_t /*compression*/) ///< @todo add compression
379 {
380
2/2
✓ Branch 0 taken 512 times.
✓ Branch 1 taken 1 times.
513 for (Index i = 0; i < count; ++i) {
381 512 const size_t len = data[i].size();
382 os << len;
383 512 os.write(data[i].c_str(), len+1);
384 //os.write(&(data[i][0]), len );
385 }
386 1 }
387
388 /// HalfWriter wraps a static function, write(), that is analogous to writeData(), above,
389 /// except that it is partially specialized for floating-point types in order to quantize
390 /// floating-point values to 16-bit half float. A wrapper class is required because
391 /// only classes, not functions, can be partially specialized.
392 template<bool IsReal, typename T> struct HalfWriter;
393 /// Partial specialization for non-floating-point types (no float to half quantization)
394 template<typename T>
395 struct HalfWriter</*IsReal=*/false, T> {
396 static inline size_t writeSize(const T* data, Index count, uint32_t compression) {
397 return writeDataSize(data, count, compression);
398 }
399 static inline void write(std::ostream& os, const T* data, Index count, uint32_t compression) {
400
1/92
✓ Branch 1 taken 2048 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 79 not taken.
✗ Branch 80 not taken.
✗ Branch 82 not taken.
✗ Branch 83 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 91 not taken.
✗ Branch 92 not taken.
✗ Branch 94 not taken.
✗ Branch 95 not taken.
✗ Branch 97 not taken.
✗ Branch 98 not taken.
✗ Branch 100 not taken.
✗ Branch 101 not taken.
✗ Branch 103 not taken.
✗ Branch 104 not taken.
✗ Branch 106 not taken.
✗ Branch 107 not taken.
✗ Branch 109 not taken.
✗ Branch 110 not taken.
✗ Branch 112 not taken.
✗ Branch 113 not taken.
✗ Branch 115 not taken.
✗ Branch 116 not taken.
✗ Branch 118 not taken.
✗ Branch 119 not taken.
✗ Branch 121 not taken.
✗ Branch 122 not taken.
✗ Branch 124 not taken.
✗ Branch 125 not taken.
✗ Branch 127 not taken.
✗ Branch 128 not taken.
✗ Branch 130 not taken.
✗ Branch 131 not taken.
✗ Branch 133 not taken.
✗ Branch 134 not taken.
✗ Branch 136 not taken.
✗ Branch 137 not taken.
2048 writeData(os, data, count, compression);
401 2048 }
402 };
403 /// Partial specialization for floating-point types
404 template<typename T>
405 struct HalfWriter</*IsReal=*/true, T> {
406 using HalfT = typename RealToHalf<T>::HalfT;
407 250 static inline size_t writeSize(const T* data, Index count, uint32_t compression) {
408
1/2
✓ Branch 0 taken 125 times.
✗ Branch 1 not taken.
250 if (count < 1) return size_t(0);
409 // Convert full float values to half float, then output the half float array.
410 250 std::vector<HalfT> halfData(count);
411
3/4
✓ Branch 0 taken 64000 times.
✓ Branch 1 taken 125 times.
✓ Branch 3 taken 64000 times.
✗ Branch 4 not taken.
128250 for (Index i = 0; i < count; ++i) halfData[i] = RealToHalf<T>::convert(data[i]);
412
1/2
✓ Branch 1 taken 125 times.
✗ Branch 2 not taken.
250 return writeDataSize<HalfT>(reinterpret_cast<const HalfT*>(&halfData[0]), count, compression);
413 }
414 12542 static inline void write(std::ostream& os, const T* data, Index count, uint32_t compression) {
415
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6269 times.
12542 if (count < 1) return;
416 // Convert full float values to half float, then output the half float array.
417 12538 std::vector<HalfT> halfData(count);
418
3/4
✓ Branch 0 taken 3209728 times.
✓ Branch 1 taken 6269 times.
✓ Branch 3 taken 3209728 times.
✗ Branch 4 not taken.
6431994 for (Index i = 0; i < count; ++i) halfData[i] = RealToHalf<T>::convert(data[i]);
419
1/2
✓ Branch 1 taken 6269 times.
✗ Branch 2 not taken.
12538 writeData<HalfT>(os, reinterpret_cast<const HalfT*>(&halfData[0]), count, compression);
420 }
421 };
422 #ifdef _WIN32
423 /// Specialization to avoid double to float warnings in MSVC
424 template<>
425 struct HalfWriter</*IsReal=*/true, double> {
426 using HalfT = RealToHalf<double>::HalfT;
427 static inline size_t writeSize(const double* data, Index count, uint32_t compression)
428 {
429 if (count < 1) return size_t(0);
430 // Convert full float values to half float, then output the half float array.
431 std::vector<HalfT> halfData(count);
432 for (Index i = 0; i < count; ++i) halfData[i] = RealToHalf<double>::convert(data[i]);
433 return writeDataSize<HalfT>(reinterpret_cast<const HalfT*>(&halfData[0]), count, compression);
434 }
435 static inline void write(std::ostream& os, const double* data, Index count,
436 uint32_t compression)
437 {
438 if (count < 1) return;
439 // Convert full float values to half float, then output the half float array.
440 std::vector<HalfT> halfData(count);
441 for (Index i = 0; i < count; ++i) halfData[i] = RealToHalf<double>::convert(data[i]);
442 writeData<HalfT>(os, reinterpret_cast<const HalfT*>(&halfData[0]), count, compression);
443 }
444 };
445 #endif // _WIN32
446
447
448 ////////////////////////////////////////
449
450
451 /// Populate the given buffer with @a destCount values of type @c ValueT
452 /// read from the given stream, taking into account that the stream might
453 /// have been compressed via one of several supported schemes.
454 /// [Mainly for internal use]
455 /// @param is a stream from which to read data (possibly compressed,
456 /// depending on the stream's compression settings)
457 /// @param destBuf a buffer into which to read values of type @c ValueT
458 /// @param destCount the number of values to be stored in the buffer
459 /// @param valueMask a bitmask (typically, a node's value mask) indicating
460 /// which positions in the buffer correspond to active values
461 /// @param fromHalf if true, read 16-bit half floats from the input stream
462 /// and convert them to full floats
463 template<typename ValueT, typename MaskT>
464 inline void
465 41992 readCompressedValues(std::istream& is, ValueT* destBuf, Index destCount,
466 const MaskT& valueMask, bool fromHalf)
467 {
468 // Get the stream's compression settings.
469 41992 auto meta = getStreamMetadataPtr(is);
470
1/2
✓ Branch 1 taken 20996 times.
✗ Branch 2 not taken.
41992 const uint32_t compression = getDataCompression(is);
471 41992 const bool maskCompressed = compression & COMPRESS_ACTIVE_MASK;
472
473 41992 const bool seek = (destBuf == nullptr);
474
5/8
✓ Branch 0 taken 12654 times.
✓ Branch 1 taken 8342 times.
✓ Branch 2 taken 12654 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12654 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 12654 times.
41992 assert(!seek || (!meta || meta->seekable()));
475
476 // Get delayed load metadata if it exists
477
478 41992 DelayedLoadMetadata::Ptr delayLoadMeta;
479 uint64_t leafIndex(0);
480
5/8
✓ Branch 0 taken 12654 times.
✓ Branch 1 taken 8342 times.
✓ Branch 2 taken 12654 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12654 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 12654 times.
✗ Branch 8 not taken.
41992 if (seek && meta && meta->delayedLoadMeta()) {
481
3/6
✓ Branch 1 taken 12654 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12654 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 12654 times.
✗ Branch 9 not taken.
25308 delayLoadMeta =
482
1/2
✓ Branch 1 taken 12654 times.
✗ Branch 2 not taken.
25308 meta->gridMetadata().getMetadata<DelayedLoadMetadata>("file_delayed_load");
483
1/2
✓ Branch 1 taken 12654 times.
✗ Branch 2 not taken.
25308 leafIndex = meta->leaf();
484 }
485
486 41992 int8_t metadata = NO_MASK_AND_ALL_VALS;
487
2/4
✓ Branch 1 taken 20996 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 20996 times.
✗ Branch 4 not taken.
41992 if (getFormatVersion(is) >= OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION) {
488 // Read the flag that specifies what, if any, additional metadata
489 // (selection mask and/or inactive value(s)) is saved.
490
2/2
✓ Branch 0 taken 1770 times.
✓ Branch 1 taken 19226 times.
41992 if (seek && !maskCompressed) {
491
1/2
✓ Branch 1 taken 1770 times.
✗ Branch 2 not taken.
3540 is.seekg(/*bytes=*/1, std::ios_base::cur);
492
4/4
✓ Branch 0 taken 10884 times.
✓ Branch 1 taken 8342 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 10880 times.
38452 } else if (seek && delayLoadMeta) {
493
1/2
✓ Branch 1 taken 10880 times.
✗ Branch 2 not taken.
21760 metadata = delayLoadMeta->getMask(leafIndex);
494
1/2
✓ Branch 1 taken 10880 times.
✗ Branch 2 not taken.
21760 is.seekg(/*bytes=*/1, std::ios_base::cur);
495 } else {
496
1/2
✓ Branch 1 taken 8346 times.
✗ Branch 2 not taken.
16692 is.read(reinterpret_cast<char*>(&metadata), /*bytes=*/1);
497 }
498 }
499
500 4220 ValueT background = zeroVal<ValueT>();
501
3/4
✓ Branch 1 taken 20996 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16894 times.
✓ Branch 4 taken 4102 times.
41992 if (const void* bgPtr = getGridBackgroundValuePtr(is)) {
502 33788 background = *static_cast<const ValueT*>(bgPtr);
503 }
504
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
41992 ValueT inactiveVal1 = background;
505
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
37892 ValueT inactiveVal0 =
506
2/2
✓ Branch 0 taken 9875 times.
✓ Branch 1 taken 11121 times.
41992 ((metadata == NO_MASK_OR_INACTIVE_VALS) ? background : math::negative(background));
507
508
3/4
✓ Branch 0 taken 20996 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 20975 times.
✓ Branch 3 taken 21 times.
41992 if (metadata == NO_MASK_AND_ONE_INACTIVE_VAL ||
509
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20975 times.
41950 metadata == MASK_AND_ONE_INACTIVE_VAL ||
510 metadata == MASK_AND_TWO_INACTIVE_VALS)
511 {
512 // Read one of at most two distinct inactive values.
513
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
42 if (seek) {
514
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
42 is.seekg(/*bytes=*/sizeof(ValueT), std::ios_base::cur);
515 } else {
516 is.read(reinterpret_cast<char*>(&inactiveVal0), /*bytes=*/sizeof(ValueT));
517 }
518
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
42 if (metadata == MASK_AND_TWO_INACTIVE_VALS) {
519 // Read the second of two distinct inactive values.
520 if (seek) {
521 is.seekg(/*bytes=*/sizeof(ValueT), std::ios_base::cur);
522 } else {
523 is.read(reinterpret_cast<char*>(&inactiveVal1), /*bytes=*/sizeof(ValueT));
524 }
525 }
526 }
527
528 MaskT selectionMask;
529 41992 if (metadata == MASK_AND_NO_INACTIVE_VALS ||
530
2/2
✓ Branch 0 taken 1835 times.
✓ Branch 1 taken 19161 times.
41992 metadata == MASK_AND_ONE_INACTIVE_VAL ||
531 metadata == MASK_AND_TWO_INACTIVE_VALS)
532 {
533 // For use in mask compression (only), read the bitmask that selects
534 // between two distinct inactive values.
535
2/2
✓ Branch 0 taken 1639 times.
✓ Branch 1 taken 196 times.
3670 if (seek) {
536
1/2
✓ Branch 1 taken 1639 times.
✗ Branch 2 not taken.
3278 is.seekg(/*bytes=*/selectionMask.memUsage(), std::ios_base::cur);
537 } else {
538 selectionMask.load(is);
539 }
540 }
541
542 ValueT* tempBuf = destBuf;
543 41992 std::unique_ptr<ValueT[]> scopedTempBuf;
544
545 Index tempCount = destCount;
546
547
2/2
✓ Branch 0 taken 13345 times.
✓ Branch 1 taken 6 times.
26702 if (maskCompressed && metadata != NO_MASK_AND_ALL_VALS
548
4/6
✓ Branch 0 taken 13351 times.
✓ Branch 1 taken 7645 times.
✓ Branch 3 taken 13345 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 13345 times.
✗ Branch 6 not taken.
68682 && getFormatVersion(is) >= OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION)
549 {
550 26674 tempCount = valueMask.countOn();
551
2/2
✓ Branch 0 taken 2414 times.
✓ Branch 1 taken 10931 times.
26690 if (!seek && tempCount != destCount) {
552 // If this node has inactive voxels, allocate a temporary buffer
553 // into which to read just the active values.
554
4/8
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2410 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 24 times.
✓ Branch 4 taken 206 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
4868 scopedTempBuf.reset(new ValueT[tempCount]);
555 tempBuf = scopedTempBuf.get();
556 }
557 }
558
559 // Read in the buffer.
560
2/2
✓ Branch 0 taken 4224 times.
✓ Branch 1 taken 16772 times.
41992 if (fromHalf) {
561
3/4
✓ Branch 0 taken 4099 times.
✓ Branch 1 taken 125 times.
✓ Branch 3 taken 3200 times.
✗ Branch 4 not taken.
14598 HalfReader<RealToHalf<ValueT>::isReal, ValueT>::read(
562 is, (seek ? nullptr : tempBuf), tempCount, compression, delayLoadMeta.get(), leafIndex);
563 } else {
564
3/4
✓ Branch 0 taken 4243 times.
✓ Branch 1 taken 12529 times.
✓ Branch 3 taken 16772 times.
✗ Branch 4 not taken.
42030 readData<ValueT>(
565 is, (seek ? nullptr : tempBuf), tempCount, compression, delayLoadMeta.get(), leafIndex);
566 }
567
568 // If mask compression is enabled and the number of active values read into
569 // the temp buffer is smaller than the size of the destination buffer,
570 // then there are missing (inactive) values.
571
4/4
✓ Branch 0 taken 2467 times.
✓ Branch 1 taken 18529 times.
✓ Branch 2 taken 2414 times.
✓ Branch 3 taken 53 times.
41992 if (!seek && maskCompressed && tempCount != destCount) {
572 // Restore inactive values, using the background value and, if available,
573 // the inside/outside mask. (For fog volumes, the destination buffer is assumed
574 // to be initialized to background value zero, so inactive values can be ignored.)
575
2/2
✓ Branch 0 taken 9803520 times.
✓ Branch 1 taken 2414 times.
19611868 for (Index destIdx = 0, tempIdx = 0; destIdx < MaskT::SIZE; ++destIdx) {
576
2/2
✓ Branch 1 taken 399018 times.
✓ Branch 2 taken 9404502 times.
19607040 if (valueMask.isOn(destIdx)) {
577 // Copy a saved active value into this node's buffer.
578
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
798036 destBuf[destIdx] = tempBuf[tempIdx];
579 798036 ++tempIdx;
580 } else {
581 // Reconstruct an unsaved inactive value and copy it into this node's buffer.
582
2/4
✓ Branch 1 taken 3841697 times.
✓ Branch 2 taken 5562805 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
27734116 destBuf[destIdx] = (selectionMask.isOn(destIdx) ? inactiveVal1 : inactiveVal0);
583 }
584 }
585 }
586 1516520 }
587
588
589 template<typename ValueT, typename MaskT>
590 inline size_t
591 22623 writeCompressedValuesSize(ValueT* srcBuf, Index srcCount,
592 const MaskT& valueMask, uint8_t maskMetadata, bool toHalf, uint32_t compress)
593 {
594 using NonConstValueT = typename std::remove_const<ValueT>::type;
595
596 22623 const bool maskCompress = compress & COMPRESS_ACTIVE_MASK;
597
598 Index tempCount = srcCount;
599 ValueT* tempBuf = srcBuf;
600 22623 std::unique_ptr<NonConstValueT[]> scopedTempBuf;
601
602 22623 if (maskCompress && maskMetadata != NO_MASK_AND_ALL_VALS) {
603
604 tempCount = 0;
605
606 21441 Index64 onVoxels = valueMask.countOn();
607 21441 if (onVoxels > Index64(0)) {
608 // Create a new array to hold just the active values.
609 159761 scopedTempBuf.reset(new NonConstValueT[onVoxels]);
610 NonConstValueT* localTempBuf = scopedTempBuf.get();
611
612 // Copy active values to a new, contiguous array.
613 8358732 for (typename MaskT::OnIterator it = valueMask.beginOn(); it; ++it, ++tempCount) {
614 4157939 localTempBuf[tempCount] = srcBuf[it.pos()];
615 }
616
617 tempBuf = scopedTempBuf.get();
618 }
619 }
620
621 // Return the buffer size.
622 22623 if (toHalf) {
623 125 return HalfWriter<RealToHalf<NonConstValueT>::isReal, NonConstValueT>::writeSize(
624 tempBuf, tempCount, compress);
625 } else {
626 22498 return writeDataSize<NonConstValueT>(tempBuf, tempCount, compress);
627 }
628 }
629
630
631 /// Write @a srcCount values of type @c ValueT to the given stream, optionally
632 /// after compressing the values via one of several supported schemes.
633 /// [Mainly for internal use]
634 /// @param os a stream to which to write data (possibly compressed, depending
635 /// on the stream's compression settings)
636 /// @param srcBuf a buffer containing values of type @c ValueT to be written
637 /// @param srcCount the number of values stored in the buffer
638 /// @param valueMask a bitmask (typically, a node's value mask) indicating
639 /// which positions in the buffer correspond to active values
640 /// @param childMask a bitmask (typically, a node's child mask) indicating
641 /// which positions in the buffer correspond to child node pointers
642 /// @param toHalf if true, convert floating-point values to 16-bit half floats
643 template<typename ValueT, typename MaskT>
644 inline void
645 73218 writeCompressedValues(std::ostream& os, ValueT* srcBuf, Index srcCount,
646 const MaskT& valueMask, const MaskT& childMask, bool toHalf)
647 {
648 // Get the stream's compression settings.
649 73218 const uint32_t compress = getDataCompression(os);
650 73218 const bool maskCompress = compress & COMPRESS_ACTIVE_MASK;
651
652 Index tempCount = srcCount;
653 ValueT* tempBuf = srcBuf;
654 73218 std::unique_ptr<ValueT[]> scopedTempBuf;
655
656 73218 int8_t metadata = NO_MASK_AND_ALL_VALS;
657
658
2/2
✓ Branch 0 taken 14088 times.
✓ Branch 1 taken 22521 times.
73218 if (!maskCompress) {
659
1/2
✓ Branch 1 taken 14088 times.
✗ Branch 2 not taken.
28176 os.write(reinterpret_cast<const char*>(&metadata), /*bytes=*/1);
660 } else {
661 // A valid level set's inactive values are either +background (outside)
662 // or -background (inside), and a fog volume's inactive values are all zero.
663 // Rather than write out all of these values, we can store just the active values
664 // (given that the value mask specifies their positions) and, if necessary,
665 // an inside/outside bitmask.
666
667 928 const ValueT zero = zeroVal<ValueT>();
668
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
45042 ValueT background = zero;
669
2/4
✓ Branch 1 taken 22521 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 22521 times.
✗ Branch 4 not taken.
45042 if (const void* bgPtr = getGridBackgroundValuePtr(os)) {
670 45042 background = *static_cast<const ValueT*>(bgPtr);
671 }
672
673
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
45042 MaskCompress<ValueT, MaskT> maskCompressData(valueMask, childMask, srcBuf, background);
674 45042 metadata = maskCompressData.metadata;
675
676
1/2
✓ Branch 1 taken 22521 times.
✗ Branch 2 not taken.
45042 os.write(reinterpret_cast<const char*>(&metadata), /*bytes=*/1);
677
678
3/4
✓ Branch 0 taken 22521 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22500 times.
✓ Branch 3 taken 21 times.
45042 if (metadata == NO_MASK_AND_ONE_INACTIVE_VAL ||
679
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22500 times.
45000 metadata == MASK_AND_ONE_INACTIVE_VAL ||
680 metadata == MASK_AND_TWO_INACTIVE_VALS)
681 {
682
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
42 if (!toHalf) {
683 // Write one of at most two distinct inactive values.
684
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
42 os.write(reinterpret_cast<const char*>(&maskCompressData.inactiveVal[0]), sizeof(ValueT));
685
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
42 if (metadata == MASK_AND_TWO_INACTIVE_VALS) {
686 // Write the second of two distinct inactive values.
687 os.write(reinterpret_cast<const char*>(&maskCompressData.inactiveVal[1]), sizeof(ValueT));
688 }
689 } else {
690 // Write one of at most two distinct inactive values.
691 ValueT truncatedVal = static_cast<ValueT>(truncateRealToHalf(maskCompressData.inactiveVal[0]));
692 os.write(reinterpret_cast<const char*>(&truncatedVal), sizeof(ValueT));
693 if (metadata == MASK_AND_TWO_INACTIVE_VALS) {
694 // Write the second of two distinct inactive values.
695 truncatedVal = truncateRealToHalf(maskCompressData.inactiveVal[1]);
696 os.write(reinterpret_cast<const char*>(&truncatedVal), sizeof(ValueT));
697 }
698 }
699 }
700
701
2/2
✓ Branch 0 taken 22518 times.
✓ Branch 1 taken 3 times.
45042 if (metadata == NO_MASK_AND_ALL_VALS) {
702 // If there are more than two unique inactive values, the entire input buffer
703 // needs to be saved (both active and inactive values).
704 /// @todo Save the selection mask as long as most of the inactive values
705 /// are one of two values?
706 } else {
707 // Create a new array to hold just the active values.
708
3/4
✓ Branch 1 taken 22518 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1993728 times.
✓ Branch 4 taken 464 times.
4032492 scopedTempBuf.reset(new ValueT[srcCount]);
709 tempBuf = scopedTempBuf.get();
710
711 45036 if (metadata == NO_MASK_OR_INACTIVE_VALS ||
712
2/2
✓ Branch 0 taken 18229 times.
✓ Branch 1 taken 4289 times.
45036 metadata == NO_MASK_AND_MINUS_BG ||
713 metadata == NO_MASK_AND_ONE_INACTIVE_VAL)
714 {
715 // Copy active values to the contiguous array.
716 tempCount = 0;
717
2/2
✓ Branch 1 taken 2787656 times.
✓ Branch 2 taken 18229 times.
11223540 for (typename MaskT::OnIterator it = valueMask.beginOn(); it; ++it, ++tempCount) {
718
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
5575312 tempBuf[tempCount] = srcBuf[it.pos()];
719 }
720 } else {
721 // Copy active values to a new, contiguous array and populate a bitmask
722 // that selects between two distinct inactive values.
723 MaskT selectionMask;
724 tempCount = 0;
725
2/2
✓ Branch 0 taken 2511360 times.
✓ Branch 1 taken 4289 times.
5031298 for (Index srcIdx = 0; srcIdx < srcCount; ++srcIdx) {
726
2/2
✓ Branch 1 taken 1505555 times.
✓ Branch 2 taken 1005805 times.
5022720 if (valueMask.isOn(srcIdx)) { // active value
727
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
3011110 tempBuf[tempCount] = srcBuf[srcIdx];
728 3011110 ++tempCount;
729 } else { // inactive value
730
2/2
✓ Branch 0 taken 715219 times.
✓ Branch 1 taken 290586 times.
2011610 if (MaskCompress<ValueT, MaskT>::eq(srcBuf[srcIdx], maskCompressData.inactiveVal[1])) {
731 1430438 selectionMask.setOn(srcIdx); // inactive value 1
732 } // else inactive value 0
733 }
734 }
735
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4289 times.
8578 assert(tempCount == valueMask.countOn());
736
737 // Write out the mask that selects between two inactive values.
738 selectionMask.save(os);
739 }
740 }
741 }
742
743 // Write out the buffer.
744
2/2
✓ Branch 0 taken 8319 times.
✓ Branch 1 taken 28290 times.
73218 if (toHalf) {
745
1/2
✓ Branch 1 taken 6271 times.
✗ Branch 2 not taken.
12542 HalfWriter<RealToHalf<ValueT>::isReal, ValueT>::write(os, tempBuf, tempCount, compress);
746 } else {
747
1/2
✓ Branch 1 taken 28290 times.
✗ Branch 2 not taken.
56580 writeData(os, tempBuf, tempCount, compress);
748 }
749 73218 }
750
751 } // namespace io
752 } // namespace OPENVDB_VERSION_NAME
753 } // namespace openvdb
754
755 #endif // OPENVDB_IO_COMPRESSION_HAS_BEEN_INCLUDED
756