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 |