OpenVDB 14.0.0
Loading...
Searching...
No Matches
Compression.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: Apache-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 <openvdb/util/Assert.h>
11#include "io.h" // for getDataCompression(), etc.
12#include <algorithm>
13#include <iostream>
14#include <memory>
15#include <string>
16#include <vector>
17
18
19namespace openvdb {
21namespace OPENVDB_VERSION_NAME {
22namespace 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>
52enum {
57};
58
59/// Return a string describing the given compression flags.
60OPENVDB_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
68enum {
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
79template <typename ValueT, typename MaskT>
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
88 const MaskT& valueMask, const MaskT& childMask,
89 const ValueT* srcBuf, const ValueT& background)
90 {
91 /// @todo Consider all values, not just inactive values?
92 inactiveVal[0] = inactiveVal[1] = background;
93 int numUniqueInactiveVals = 0;
94 for (typename MaskT::OffIterator it = valueMask.beginOff();
95 numUniqueInactiveVals < 3 && it; ++it)
96 {
97 const Index32 idx = it.pos();
98
99 // Skip inactive values that are actually child node pointers.
100 if (childMask.isOn(idx)) continue;
101
102 const ValueT& val = srcBuf[idx];
103 const bool unique = !(
104 (numUniqueInactiveVals > 0 && MaskCompress::eq(val, inactiveVal[0])) ||
105 (numUniqueInactiveVals > 1 && MaskCompress::eq(val, inactiveVal[1]))
106 );
107 if (unique) {
108 if (numUniqueInactiveVals < 2) inactiveVal[numUniqueInactiveVals] = val;
109 ++numUniqueInactiveVals;
110 }
111 }
112
114
115 if (numUniqueInactiveVals == 1) {
116 if (!MaskCompress::eq(inactiveVal[0], background)) {
117 if (MaskCompress::eq(inactiveVal[0], math::negative(background))) {
119 } else {
121 }
122 }
123 } else if (numUniqueInactiveVals == 2) {
125 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.
129
130 } else if (MaskCompress::eq(inactiveVal[1], background)) {
131 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.
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.
141 }
142 } else if (MaskCompress::eq(inactiveVal[0], background)) {
143 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.
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]);
155 }
156 }
157 } else if (numUniqueInactiveVals > 2) {
159 }
160 }
161
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.
172template<typename T>
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};
178template<> struct RealToHalf<float> {
179 enum { isReal = true };
181 static HalfT convert(float val) { return HalfT(val); }
182};
183template<> struct RealToHalf<double> {
184 enum { isReal = true };
186 // A half can only be constructed from a float, so cast the value to a float first.
187 static HalfT convert(double val) { return HalfT(float(val)); }
188};
189template<> struct RealToHalf<Vec2s> {
190 enum { isReal = true };
191 using HalfT = Vec2H;
192 static HalfT convert(const Vec2s& val) { return HalfT(val); }
193};
194template<> 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 static HalfT convert(const Vec2d& val) { return HalfT(Vec2s(val)); }
199};
200template<> struct RealToHalf<Vec3s> {
201 enum { isReal = true };
202 using HalfT = Vec3H;
203 static HalfT convert(const Vec3s& val) { return HalfT(val); }
204};
205template<> 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 static HalfT convert(const Vec3d& val) { return HalfT(Vec3s(val)); }
210};
211
212
213/// Return the given value truncated to 16-bit float precision.
214template<typename T>
215inline T
217{
218 return T(RealToHalf<T>::convert(val));
219}
220
221
222////////////////////////////////////////
223
224
225OPENVDB_API size_t zipToStreamSize(const char* data, size_t numBytes);
226OPENVDB_API void zipToStream(std::ostream&, const char* data, size_t numBytes);
227OPENVDB_API void unzipFromStream(std::istream&, char* data, size_t numBytes);
228OPENVDB_API size_t bloscToStreamSize(const char* data, size_t valSize, size_t numVals);
229OPENVDB_API void bloscToStream(std::ostream&, const char* data, size_t valSize, size_t numVals);
230OPENVDB_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/// @details This default implementation is instantiated only for types
239/// whose size can be determined by the sizeof() operator.
240template<typename T>
241inline void
242readData(std::istream& is, T* data, Index count, uint32_t compression)
243{
244 const bool seek = data == nullptr;
245 if (seek) {
247 }
248
250 bloscFromStream(is, reinterpret_cast<char*>(data), sizeof(T) * count);
251 } else if (compression & COMPRESS_ZIP) {
252 unzipFromStream(is, reinterpret_cast<char*>(data), sizeof(T) * count);
253 } else if (seek) {
254 is.seekg(sizeof(T) * count, std::ios_base::cur);
255 } else {
256 is.read(reinterpret_cast<char*>(data), sizeof(T) * count);
257 }
258}
259
260/// Specialization for std::string input
261template<>
262inline void
263readData<std::string>(std::istream& is, std::string* data, Index count, uint32_t /*compression*/)
264{
265 for (Index i = 0; i < count; ++i) {
266 size_t len = 0;
267 is >> len;
268 //data[i].resize(len);
269 //is.read(&(data[i][0]), len);
270
271 std::string buffer(len+1, ' ');
272 is.read(&buffer[0], len+1);
273 if (data != nullptr) data[i].assign(buffer, 0, len);
274 }
275}
276
277/// HalfReader wraps a static function, read(), that is analogous to readData(), above,
278/// except that it is partially specialized for floating-point types in order to promote
279/// 16-bit half float values to full float. A wrapper class is required because
280/// only classes, not functions, can be partially specialized.
281template<bool IsReal, typename T> struct HalfReader;
282/// Partial specialization for non-floating-point types (no half to float promotion)
283template<typename T>
284struct HalfReader</*IsReal=*/false, T> {
285 static inline void read(std::istream& is, T* data, Index count, uint32_t compression) {
286 readData(is, data, count, compression);
287 }
288};
289/// Partial specialization for floating-point types
290template<typename T>
291struct HalfReader</*IsReal=*/true, T> {
292 using HalfT = typename RealToHalf<T>::HalfT;
293 static inline void read(std::istream& is, T* data, Index count, uint32_t compression) {
294 if (count < 1) return;
295 if (data == nullptr) {
296 // seek mode - pass through null pointer
297 readData<HalfT>(is, nullptr, count, compression);
298 } else {
299 std::vector<HalfT> halfData(count); // temp buffer into which to read half float values
300 readData<HalfT>(is, reinterpret_cast<HalfT*>(&halfData[0]), count, compression);
301 // Copy half float values from the temporary buffer to the full float output array.
302 std::copy(halfData.begin(), halfData.end(), data);
303 }
304 }
305};
306
307
308template<typename T>
309inline size_t
310writeDataSize(const T *data, Index count, uint32_t compression)
311{
313 return bloscToStreamSize(reinterpret_cast<const char*>(data), sizeof(T), count);
314 } else if (compression & COMPRESS_ZIP) {
315 return zipToStreamSize(reinterpret_cast<const char*>(data), sizeof(T) * count);
316 } else {
317 return sizeof(T) * count;
318 }
319}
320
321
322/// Specialization for std::string output
323template<>
324inline size_t
325writeDataSize<std::string>(const std::string* data, Index count,
326 uint32_t /*compression*/) ///< @todo add compression
327{
328 size_t size(0);
329 for (Index i = 0; i < count; ++i) {
330 const size_t len = data[i].size();
331 size += sizeof(size_t) + (len+1);
332 }
333 return size;
334}
335
336
337/// Write data to a stream.
338/// @param os the output stream
339/// @param data the contiguous array of data to write
340/// @param count the number of elements to write out
341/// @param compression whether and how to compress the data (either COMPRESS_NONE,
342/// COMPRESS_ZIP, COMPRESS_ACTIVE_MASK or COMPRESS_BLOSC)
343/// @details This default implementation is instantiated only for types
344/// whose size can be determined by the sizeof() operator.
345template<typename T>
346inline void
347writeData(std::ostream &os, const T *data, Index count, uint32_t compression)
348{
350 bloscToStream(os, reinterpret_cast<const char*>(data), sizeof(T), count);
351 } else if (compression & COMPRESS_ZIP) {
352 zipToStream(os, reinterpret_cast<const char*>(data), sizeof(T) * count);
353 } else {
354 os.write(reinterpret_cast<const char*>(data), sizeof(T) * count);
355 }
356}
357
358/// Specialization for std::string output
359template<>
360inline void
361writeData<std::string>(std::ostream& os, const std::string* data, Index count,
362 uint32_t /*compression*/) ///< @todo add compression
363{
364 for (Index i = 0; i < count; ++i) {
365 const size_t len = data[i].size();
366 os << len;
367 os.write(data[i].c_str(), len+1);
368 //os.write(&(data[i][0]), len );
369 }
370}
371
372/// HalfWriter wraps a static function, write(), that is analogous to writeData(), above,
373/// except that it is partially specialized for floating-point types in order to quantize
374/// floating-point values to 16-bit half float. A wrapper class is required because
375/// only classes, not functions, can be partially specialized.
376template<bool IsReal, typename T> struct HalfWriter;
377/// Partial specialization for non-floating-point types (no float to half quantization)
378template<typename T>
379struct HalfWriter</*IsReal=*/false, T> {
380 static inline size_t writeSize(const T* data, Index count, uint32_t compression) {
381 return writeDataSize(data, count, compression);
382 }
383 static inline void write(std::ostream& os, const T* data, Index count, uint32_t compression) {
384 writeData(os, data, count, compression);
385 }
386};
387/// Partial specialization for floating-point types
388template<typename T>
389struct HalfWriter</*IsReal=*/true, T> {
390 using HalfT = typename RealToHalf<T>::HalfT;
391 static inline size_t writeSize(const T* data, Index count, uint32_t compression) {
392 if (count < 1) return size_t(0);
393 // Convert full float values to half float, then output the half float array.
394 std::vector<HalfT> halfData(count);
395 for (Index i = 0; i < count; ++i) halfData[i] = RealToHalf<T>::convert(data[i]);
396 return writeDataSize<HalfT>(reinterpret_cast<const HalfT*>(&halfData[0]), count, compression);
397 }
398 static inline void write(std::ostream& os, const T* data, Index count, uint32_t compression) {
399 if (count < 1) return;
400 // Convert full float values to half float, then output the half float array.
401 std::vector<HalfT> halfData(count);
402 for (Index i = 0; i < count; ++i) halfData[i] = RealToHalf<T>::convert(data[i]);
403 writeData<HalfT>(os, reinterpret_cast<const HalfT*>(&halfData[0]), count, compression);
404 }
405};
406#ifdef _WIN32
407/// Specialization to avoid double to float warnings in MSVC
408template<>
409struct HalfWriter</*IsReal=*/true, double> {
410 using HalfT = RealToHalf<double>::HalfT;
411 static inline size_t writeSize(const double* data, Index count, uint32_t compression)
412 {
413 if (count < 1) return size_t(0);
414 // Convert full float values to half float, then output the half float array.
415 std::vector<HalfT> halfData(count);
416 for (Index i = 0; i < count; ++i) halfData[i] = RealToHalf<double>::convert(data[i]);
417 return writeDataSize<HalfT>(reinterpret_cast<const HalfT*>(&halfData[0]), count, compression);
418 }
419 static inline void write(std::ostream& os, const double* data, Index count,
420 uint32_t compression)
421 {
422 if (count < 1) return;
423 // Convert full float values to half float, then output the half float array.
424 std::vector<HalfT> halfData(count);
425 for (Index i = 0; i < count; ++i) halfData[i] = RealToHalf<double>::convert(data[i]);
426 writeData<HalfT>(os, reinterpret_cast<const HalfT*>(&halfData[0]), count, compression);
427 }
428};
429#endif // _WIN32
430
431
432////////////////////////////////////////
433
434
435/// Populate the given buffer with @a destCount values of type @c ValueT
436/// read from the given stream, taking into account that the stream might
437/// have been compressed via one of several supported schemes.
438/// [Mainly for internal use]
439/// @param is a stream from which to read data (possibly compressed,
440/// depending on the stream's compression settings)
441/// @param destBuf a buffer into which to read values of type @c ValueT
442/// @param destCount the number of values to be stored in the buffer
443/// @param valueMask a bitmask (typically, a node's value mask) indicating
444/// which positions in the buffer correspond to active values
445/// @param fromHalf if true, read 16-bit half floats from the input stream
446/// and convert them to full floats
447/// @param background optional background value used when mask compressed
448template<typename ValueT, typename MaskT>
449inline void
450readCompressedValues(std::istream& is, ValueT* destBuf, Index destCount,
451 const MaskT& valueMask, bool fromHalf, const ValueT* background = nullptr)
452{
454
455 // Get the stream's compression settings.
456 auto meta = getStreamMetadataPtr(is);
457 const uint32_t compression = getDataCompression(is);
458 const bool maskCompressed = compression & COMPRESS_ACTIVE_MASK;
459
460 const bool seek = (destBuf == nullptr);
461 OPENVDB_ASSERT(!seek || (!meta || meta->seekable()));
462
463 int8_t metadata = NO_MASK_AND_ALL_VALS;
464
466 // Read the flag that specifies what, if any, additional metadata
467 // (selection mask and/or inactive value(s)) is saved.
468 if (seek && !maskCompressed) {
469 is.seekg(/*bytes=*/1, std::ios_base::cur);
470 } else {
471 is.read(reinterpret_cast<char*>(&metadata), /*bytes=*/1);
472 }
473 }
474
475 ValueT bgValue = zeroVal<ValueT>();
476 if (background) {
477 bgValue = *background;
478 } else if (const void* bgPtr = getGridBackgroundValuePtr(is)) {
479 bgValue = *static_cast<const ValueT*>(bgPtr);
480 }
481 ValueT inactiveVal1 = bgValue;
482 ValueT inactiveVal0 =
483 ((metadata == NO_MASK_OR_INACTIVE_VALS) ? bgValue : math::negative(bgValue));
484
485 if (metadata == NO_MASK_AND_ONE_INACTIVE_VAL ||
486 metadata == MASK_AND_ONE_INACTIVE_VAL ||
487 metadata == MASK_AND_TWO_INACTIVE_VALS)
488 {
489 // Read one of at most two distinct inactive values.
490 if (seek) {
491 is.seekg(/*bytes=*/sizeof(ValueT), std::ios_base::cur);
492 } else {
493 is.read(reinterpret_cast<char*>(&inactiveVal0), /*bytes=*/sizeof(ValueT));
494 }
495 if (metadata == MASK_AND_TWO_INACTIVE_VALS) {
496 // Read the second of two distinct inactive values.
497 if (seek) {
498 is.seekg(/*bytes=*/sizeof(ValueT), std::ios_base::cur);
499 } else {
500 is.read(reinterpret_cast<char*>(&inactiveVal1), /*bytes=*/sizeof(ValueT));
501 }
502 }
503 }
504
505 MaskT selectionMask;
506 if (metadata == MASK_AND_NO_INACTIVE_VALS ||
507 metadata == MASK_AND_ONE_INACTIVE_VAL ||
508 metadata == MASK_AND_TWO_INACTIVE_VALS)
509 {
510 // For use in mask compression (only), read the bitmask that selects
511 // between two distinct inactive values.
512 if (seek) {
513 is.seekg(/*bytes=*/selectionMask.memUsage(), std::ios_base::cur);
514 } else {
515 selectionMask.load(is);
516 }
517 }
518
519 ValueT* tempBuf = destBuf;
520 std::unique_ptr<ValueT[]> scopedTempBuf;
521
522 Index tempCount = destCount;
523
524 if (maskCompressed && metadata != NO_MASK_AND_ALL_VALS)
525 {
526 tempCount = valueMask.countOn();
527 if (!seek && tempCount != destCount) {
528 // If this node has inactive voxels, allocate a temporary buffer
529 // into which to read just the active values.
530 scopedTempBuf.reset(new ValueT[tempCount]);
531 tempBuf = scopedTempBuf.get();
532 }
533 }
534
535 // Read in the buffer.
536 if (fromHalf) {
538 is, (seek ? nullptr : tempBuf), tempCount, compression);
539 } else {
541 is, (seek ? nullptr : tempBuf), tempCount, compression);
542 }
543
544 // If mask compression is enabled and the number of active values read into
545 // the temp buffer is smaller than the size of the destination buffer,
546 // then there are missing (inactive) values.
547 if (!seek && maskCompressed && tempCount != destCount) {
548 // Restore inactive values, using the background value and, if available,
549 // the inside/outside mask. (For fog volumes, the destination buffer is assumed
550 // to be initialized to background value zero, so inactive values can be ignored.)
551 for (Index destIdx = 0, tempIdx = 0; destIdx < MaskT::SIZE; ++destIdx) {
552 if (valueMask.isOn(destIdx)) {
553 // Copy a saved active value into this node's buffer.
554 destBuf[destIdx] = tempBuf[tempIdx];
555 ++tempIdx;
556 } else {
557 // Reconstruct an unsaved inactive value and copy it into this node's buffer.
558 destBuf[destIdx] = (selectionMask.isOn(destIdx) ? inactiveVal1 : inactiveVal0);
559 }
560 }
561 }
562}
563
564
565template<typename ValueT, typename MaskT>
566inline size_t
567writeCompressedValuesSize(ValueT* srcBuf, Index srcCount,
568 const MaskT& valueMask, uint8_t maskMetadata, bool toHalf, uint32_t compress)
569{
570 using NonConstValueT = typename std::remove_const<ValueT>::type;
571
572 const bool maskCompress = compress & COMPRESS_ACTIVE_MASK;
573
574 Index tempCount = srcCount;
575 ValueT* tempBuf = srcBuf;
576 std::unique_ptr<NonConstValueT[]> scopedTempBuf;
577
578 if (maskCompress && maskMetadata != NO_MASK_AND_ALL_VALS) {
579
580 tempCount = 0;
581
582 Index64 onVoxels = valueMask.countOn();
583 if (onVoxels > Index64(0)) {
584 // Create a new array to hold just the active values.
585 scopedTempBuf.reset(new NonConstValueT[onVoxels]);
586 NonConstValueT* localTempBuf = scopedTempBuf.get();
587
588 // Copy active values to a new, contiguous array.
589 for (typename MaskT::OnIterator it = valueMask.beginOn(); it; ++it, ++tempCount) {
590 localTempBuf[tempCount] = srcBuf[it.pos()];
591 }
592
593 tempBuf = scopedTempBuf.get();
594 }
595 }
596
597 // Return the buffer size.
598 if (toHalf) {
599 return HalfWriter<RealToHalf<NonConstValueT>::isReal, NonConstValueT>::writeSize(
600 tempBuf, tempCount, compress);
601 } else {
602 return writeDataSize<NonConstValueT>(tempBuf, tempCount, compress);
603 }
604}
605
606
607/// Write @a srcCount values of type @c ValueT to the given stream, optionally
608/// after compressing the values via one of several supported schemes.
609/// [Mainly for internal use]
610/// @param os a stream to which to write data (possibly compressed, depending
611/// on the stream's compression settings)
612/// @param srcBuf a buffer containing values of type @c ValueT to be written
613/// @param srcCount the number of values stored in the buffer
614/// @param valueMask a bitmask (typically, a node's value mask) indicating
615/// which positions in the buffer correspond to active values
616/// @param childMask a bitmask (typically, a node's child mask) indicating
617/// which positions in the buffer correspond to child node pointers
618/// @param toHalf if true, convert floating-point values to 16-bit half floats
619/// @param background optional background value used when mask compressed
620template<typename ValueT, typename MaskT>
621inline void
622writeCompressedValues(std::ostream& os, const ValueT* srcBuf, Index srcCount,
623 const MaskT& valueMask, const MaskT& childMask, bool toHalf,
624 const ValueT* background = nullptr)
625{
626 // Get the stream's compression settings.
627 const uint32_t compress = getDataCompression(os);
628 const bool maskCompress = compress & COMPRESS_ACTIVE_MASK;
629
630 Index tempCount = srcCount;
631 ValueT* tempBuf = nullptr;
632 std::unique_ptr<ValueT[]> scopedTempBuf;
633
634 int8_t metadata = NO_MASK_AND_ALL_VALS;
635
636 if (!maskCompress) {
637 os.write(reinterpret_cast<const char*>(&metadata), /*bytes=*/1);
638 } else {
639 // A valid level set's inactive values are either +background (outside)
640 // or -background (inside), and a fog volume's inactive values are all zero.
641 // Rather than write out all of these values, we can store just the active values
642 // (given that the value mask specifies their positions) and, if necessary,
643 // an inside/outside bitmask.
644
645 const ValueT zero = zeroVal<ValueT>();
646 ValueT bgValue = zero;
647 if (background) {
648 bgValue = *background;
649 } else if (const void* bgPtr = getGridBackgroundValuePtr(os)) {
650 bgValue = *static_cast<const ValueT*>(bgPtr);
651 }
652
653 MaskCompress<ValueT, MaskT> maskCompressData(valueMask, childMask, srcBuf, bgValue);
654 metadata = maskCompressData.metadata;
655
656 os.write(reinterpret_cast<const char*>(&metadata), /*bytes=*/1);
657
658 if (metadata == NO_MASK_AND_ONE_INACTIVE_VAL ||
659 metadata == MASK_AND_ONE_INACTIVE_VAL ||
660 metadata == MASK_AND_TWO_INACTIVE_VALS)
661 {
662 if (!toHalf) {
663 // Write one of at most two distinct inactive values.
664 os.write(reinterpret_cast<const char*>(&maskCompressData.inactiveVal[0]), sizeof(ValueT));
665 if (metadata == MASK_AND_TWO_INACTIVE_VALS) {
666 // Write the second of two distinct inactive values.
667 os.write(reinterpret_cast<const char*>(&maskCompressData.inactiveVal[1]), sizeof(ValueT));
668 }
669 } else {
670 // Write one of at most two distinct inactive values.
671 ValueT truncatedVal = static_cast<ValueT>(truncateRealToHalf(maskCompressData.inactiveVal[0]));
672 os.write(reinterpret_cast<const char*>(&truncatedVal), sizeof(ValueT));
673 if (metadata == MASK_AND_TWO_INACTIVE_VALS) {
674 // Write the second of two distinct inactive values.
675 truncatedVal = truncateRealToHalf(maskCompressData.inactiveVal[1]);
676 os.write(reinterpret_cast<const char*>(&truncatedVal), sizeof(ValueT));
677 }
678 }
679 }
680
681 if (metadata == NO_MASK_AND_ALL_VALS) {
682 // If there are more than two unique inactive values, the entire input buffer
683 // needs to be saved (both active and inactive values).
684 /// @todo Save the selection mask as long as most of the inactive values
685 /// are one of two values?
686 } else {
687 // Create a new array to hold just the active values.
688 scopedTempBuf.reset(new ValueT[srcCount]);
689 tempBuf = scopedTempBuf.get();
690
691 if (metadata == NO_MASK_OR_INACTIVE_VALS ||
692 metadata == NO_MASK_AND_MINUS_BG ||
694 {
695 // Copy active values to the contiguous array.
696 tempCount = 0;
697 for (typename MaskT::OnIterator it = valueMask.beginOn(); it; ++it, ++tempCount) {
698 tempBuf[tempCount] = srcBuf[it.pos()];
699 }
700 } else {
701 // Copy active values to a new, contiguous array and populate a bitmask
702 // that selects between two distinct inactive values.
703 MaskT selectionMask;
704 tempCount = 0;
705 for (Index srcIdx = 0; srcIdx < srcCount; ++srcIdx) {
706 if (valueMask.isOn(srcIdx)) { // active value
707 tempBuf[tempCount] = srcBuf[srcIdx];
708 ++tempCount;
709 } else { // inactive value
710 if (MaskCompress<ValueT, MaskT>::eq(srcBuf[srcIdx], maskCompressData.inactiveVal[1])) {
711 selectionMask.setOn(srcIdx); // inactive value 1
712 } // else inactive value 0
713 }
714 }
715 OPENVDB_ASSERT(tempCount == valueMask.countOn());
716
717 // Write out the mask that selects between two inactive values.
718 selectionMask.save(os);
719 }
720 }
721 }
722
723 // Write out the buffer.
724 if (toHalf) {
725 HalfWriter<RealToHalf<ValueT>::isReal, ValueT>::write(os,
726 bool(tempBuf) ? tempBuf : srcBuf, tempCount, compress);
727 } else {
728 writeData(os, bool(tempBuf) ? tempBuf : srcBuf, tempCount, compress);
729 }
730}
731
732} // namespace io
733} // namespace OPENVDB_VERSION_NAME
734} // namespace openvdb
735
736#endif // OPENVDB_IO_COMPRESSION_HAS_BEEN_INCLUDED
#define OPENVDB_ASSERT(X)
Definition Assert.h:41
#define OPENVDB_API
Definition Platform.h:286
Definition StreamCompression.h:34
@ COMPRESS_BLOSC
Definition Compression.h:56
@ COMPRESS_NONE
Definition Compression.h:53
@ COMPRESS_ACTIVE_MASK
Definition Compression.h:55
@ COMPRESS_ZIP
Definition Compression.h:54
size_t bloscToStreamSize(const char *data, size_t valSize, size_t numVals)
T truncateRealToHalf(const T &val)
Return the given value truncated to 16-bit float precision.
Definition Compression.h:216
size_t zipToStreamSize(const char *data, size_t numBytes)
void unzipFromStream(std::istream &, char *data, size_t numBytes)
void writeCompressedValues(std::ostream &os, const ValueT *srcBuf, Index srcCount, const MaskT &valueMask, const MaskT &childMask, bool toHalf, const ValueT *background=nullptr)
Definition Compression.h:622
void readCompressedValues(std::istream &is, ValueT *destBuf, Index destCount, const MaskT &valueMask, bool fromHalf, const ValueT *background=nullptr)
Definition Compression.h:450
void writeData(std::ostream &os, const T *data, Index count, uint32_t compression)
Definition Compression.h:347
void bloscFromStream(std::istream &, char *data, size_t numBytes)
void bloscToStream(std::ostream &, const char *data, size_t valSize, size_t numVals)
uint32_t getDataCompression(std::ios_base &)
Return a bitwise OR of compression option flags (COMPRESS_ZIP, COMPRESS_ACTIVE_MASK,...
void checkFormatVersion(std::ios_base &)
Throws an IoError if the file format version number is not supported.
uint32_t getFormatVersion(std::ios_base &)
Return the file format version number associated with the given input stream.
SharedPtr< StreamMetadata > getStreamMetadataPtr(std::ios_base &)
Return a shared pointer to an object that stores metadata (file format, compression scheme,...
std::string compressionToString(uint32_t flags)
Return a string describing the given compression flags.
@ MASK_AND_NO_INACTIVE_VALS
Definition Compression.h:72
@ MASK_AND_TWO_INACTIVE_VALS
Definition Compression.h:74
@ NO_MASK_AND_MINUS_BG
Definition Compression.h:70
@ NO_MASK_AND_ONE_INACTIVE_VAL
Definition Compression.h:71
@ MASK_AND_ONE_INACTIVE_VAL
Definition Compression.h:73
@ NO_MASK_AND_ALL_VALS
Definition Compression.h:75
@ NO_MASK_OR_INACTIVE_VALS
Definition Compression.h:69
void zipToStream(std::ostream &, const char *data, size_t numBytes)
size_t writeCompressedValuesSize(ValueT *srcBuf, Index srcCount, const MaskT &valueMask, uint8_t maskMetadata, bool toHalf, uint32_t compress)
Definition Compression.h:567
const void * getGridBackgroundValuePtr(std::ios_base &)
Return a pointer to the background value of the grid currently being read from or written to the give...
size_t writeDataSize(const T *data, Index count, uint32_t compression)
Definition Compression.h:310
void readData(std::istream &is, T *data, Index count, uint32_t compression)
Read data from a stream.
Definition Compression.h:242
Vec2< double > Vec2d
Definition Vec2.h:572
Vec2< float > Vec2s
Definition Vec2.h:571
Vec3< double > Vec3d
Definition Vec3.h:708
T negative(const T &val)
Return the unary negation of the given value.
Definition Math.h:139
bool isExactlyEqual(const T0 &a, const T1 &b)
Return true if a is exactly equal to b.
Definition Math.h:465
internal::half half
Definition HalfDecl.h:25
Vec3< float > Vec3s
Definition Vec3.h:707
Index32 Index
Definition Types.h:34
math::Vec2< Half > Vec2H
Definition Types.h:47
constexpr T zeroVal()
Return the value of type T that corresponds to zero.
Definition Math.h:71
@ OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION
Definition version.h.in:339
uint32_t Index32
Definition Types.h:32
uint64_t Index64
Definition Types.h:33
math::Vec3< Half > Vec3H
Definition Types.h:56
Definition Exceptions.h:13
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
static void read(std::istream &is, T *data, Index count, uint32_t compression)
Definition Compression.h:285
typename RealToHalf< T >::HalfT HalfT
Definition Compression.h:292
static void read(std::istream &is, T *data, Index count, uint32_t compression)
Definition Compression.h:293
Definition Compression.h:281
static size_t writeSize(const T *data, Index count, uint32_t compression)
Definition Compression.h:380
static void write(std::ostream &os, const T *data, Index count, uint32_t compression)
Definition Compression.h:383
static size_t writeSize(const T *data, Index count, uint32_t compression)
Definition Compression.h:391
typename RealToHalf< T >::HalfT HalfT
Definition Compression.h:390
static void write(std::ostream &os, const T *data, Index count, uint32_t compression)
Definition Compression.h:398
Definition Compression.h:376
Definition Compression.h:81
int8_t metadata
Definition Compression.h:162
MaskCompress(const MaskT &valueMask, const MaskT &childMask, const ValueT *srcBuf, const ValueT &background)
Definition Compression.h:87
static bool eq(const ValueT &a, const ValueT &b)
Definition Compression.h:83
ValueT inactiveVal[2]
Definition Compression.h:163
Vec2H HalfT
Definition Compression.h:196
@ isReal
Definition Compression.h:195
static HalfT convert(const Vec2d &val)
Definition Compression.h:198
Vec2H HalfT
Definition Compression.h:191
@ isReal
Definition Compression.h:190
static HalfT convert(const Vec2s &val)
Definition Compression.h:192
@ isReal
Definition Compression.h:206
Vec3H HalfT
Definition Compression.h:207
static HalfT convert(const Vec3d &val)
Definition Compression.h:209
Vec3H HalfT
Definition Compression.h:202
@ isReal
Definition Compression.h:201
static HalfT convert(const Vec3s &val)
Definition Compression.h:203
math::half HalfT
Definition Compression.h:185
@ isReal
Definition Compression.h:184
static HalfT convert(double val)
Definition Compression.h:187
static HalfT convert(float val)
Definition Compression.h:181
@ isReal
Definition Compression.h:179
math::half HalfT
Definition Compression.h:180
RealToHalf and its specializations define a mapping from floating-point data types to analogous half ...
Definition Compression.h:173
@ isReal
Definition Compression.h:174
T HalfT
Definition Compression.h:175
static HalfT convert(const T &val)
Definition Compression.h:176
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:291