HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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 "DelayedLoadMetadata.h"
13 #include <algorithm>
14 #include <iostream>
15 #include <memory>
16 #include <string>
17 #include <vector>
18 
19 
20 namespace openvdb {
22 namespace OPENVDB_VERSION_NAME {
23 namespace io {
24 
25 /// @brief OR-able bit flags for compression options on input and output streams
26 /// @details
27 /// <dl>
28 /// <dt><tt>COMPRESS_NONE</tt>
29 /// <dd>On write, don't compress data.<br>
30 /// On read, the input stream contains uncompressed data.
31 ///
32 /// <dt><tt>COMPRESS_ZIP</tt>
33 /// <dd>When writing grids other than level sets or fog volumes, apply
34 /// ZLIB compression to internal and leaf node value buffers.<br>
35 /// When reading grids other than level sets or fog volumes, indicate that
36 /// the value buffers of internal and leaf nodes are ZLIB-compressed.<br>
37 /// ZLIB compresses well but is slow.
38 ///
39 /// <dt><tt>COMPRESS_ACTIVE_MASK</tt>
40 /// <dd>When writing a grid of any class, don't output a node's inactive values
41 /// if it has two or fewer distinct values. Instead, output minimal information
42 /// to permit the lossless reconstruction of inactive values.<br>
43 /// On read, nodes might have been stored without inactive values.
44 /// Where necessary, reconstruct inactive values from available information.
45 ///
46 /// <dt><tt>COMPRESS_BLOSC</tt>
47 /// <dd>When writing grids other than level sets or fog volumes, apply
48 /// Blosc compression to internal and leaf node value buffers.<br>
49 /// When reading grids other than level sets or fog volumes, indicate that
50 /// the value buffers of internal and leaf nodes are Blosc-compressed.<br>
51 /// Blosc is much faster than ZLIB and produces comparable file sizes.
52 /// </dl>
53 enum {
55  COMPRESS_ZIP = 0x1,
58 };
59 
60 /// Return a string describing the given compression flags.
61 OPENVDB_API std::string compressionToString(uint32_t flags);
62 
63 
64 ////////////////////////////////////////
65 
66 
67 /// @internal Per-node indicator byte that specifies what additional metadata
68 /// is stored to permit reconstruction of inactive values
69 enum {
70  /*0*/ NO_MASK_OR_INACTIVE_VALS, // no inactive vals, or all inactive vals are +background
71  /*1*/ NO_MASK_AND_MINUS_BG, // all inactive vals are -background
72  /*2*/ NO_MASK_AND_ONE_INACTIVE_VAL, // all inactive vals have the same non-background val
73  /*3*/ MASK_AND_NO_INACTIVE_VALS, // mask selects between -background and +background
74  /*4*/ MASK_AND_ONE_INACTIVE_VAL, // mask selects between backgd and one other inactive val
75  /*5*/ MASK_AND_TWO_INACTIVE_VALS, // mask selects between two non-background inactive vals
76  /*6*/ NO_MASK_AND_ALL_VALS // > 2 inactive vals, so no mask compression at all
77 };
78 
79 
80 template <typename ValueT, typename MaskT>
82 {
83  // Comparison function for values
84  static inline bool eq(const ValueT& a, const ValueT& b) {
85  return math::isExactlyEqual(a, b);
86  }
87 
89  const MaskT& valueMask, const MaskT& childMask,
90  const ValueT* srcBuf, const ValueT& background)
91  {
92  /// @todo Consider all values, not just inactive values?
93  inactiveVal[0] = inactiveVal[1] = background;
94  int numUniqueInactiveVals = 0;
95  for (typename MaskT::OffIterator it = valueMask.beginOff();
96  numUniqueInactiveVals < 3 && it; ++it)
97  {
98  const Index32 idx = it.pos();
99 
100  // Skip inactive values that are actually child node pointers.
101  if (childMask.isOn(idx)) continue;
102 
103  const ValueT& val = srcBuf[idx];
104  const bool unique = !(
105  (numUniqueInactiveVals > 0 && MaskCompress::eq(val, inactiveVal[0])) ||
106  (numUniqueInactiveVals > 1 && MaskCompress::eq(val, inactiveVal[1]))
107  );
108  if (unique) {
109  if (numUniqueInactiveVals < 2) inactiveVal[numUniqueInactiveVals] = val;
110  ++numUniqueInactiveVals;
111  }
112  }
113 
115 
116  if (numUniqueInactiveVals == 1) {
117  if (!MaskCompress::eq(inactiveVal[0], background)) {
118  if (MaskCompress::eq(inactiveVal[0], math::negative(background))) {
120  } else {
122  }
123  }
124  } else if (numUniqueInactiveVals == 2) {
126  if (!MaskCompress::eq(inactiveVal[0], background) && !MaskCompress::eq(inactiveVal[1], background)) {
127  // If neither inactive value is equal to the background, both values
128  // need to be saved, along with a mask that selects between them.
130 
131  } else if (MaskCompress::eq(inactiveVal[1], background)) {
132  if (MaskCompress::eq(inactiveVal[0], math::negative(background))) {
133  // If the second inactive value is equal to the background and
134  // the first is equal to -background, neither value needs to be saved,
135  // but save a mask that selects between -background and +background.
137  } else {
138  // If the second inactive value is equal to the background, only
139  // the first value needs to be saved, along with a mask that selects
140  // between it and the background.
142  }
143  } else if (MaskCompress::eq(inactiveVal[0], background)) {
144  if (MaskCompress::eq(inactiveVal[1], math::negative(background))) {
145  // If the first inactive value is equal to the background and
146  // the second is equal to -background, neither value needs to be saved,
147  // but save a mask that selects between -background and +background.
150  } else {
151  // If the first inactive value is equal to the background, swap it
152  // with the second value and save only that value, along with a mask
153  // that selects between it and the background.
156  }
157  }
158  } else if (numUniqueInactiveVals > 2) {
160  }
161  }
162 
164  ValueT inactiveVal[2];
165 };
166 
167 
168 ////////////////////////////////////////
169 
170 
171 /// @brief RealToHalf and its specializations define a mapping from
172 /// floating-point data types to analogous half float types.
173 template<typename T>
174 struct RealToHalf {
175  enum { isReal = false }; // unless otherwise specified, type T is not a floating-point type
176  using HalfT = T; // type T's half float analogue is T itself
177  static HalfT convert(const T& val) { return val; }
178 };
179 template<> struct RealToHalf<float> {
180  enum { isReal = true };
181  using HalfT = math::half;
182  static HalfT convert(float val) { return HalfT(val); }
183 };
184 template<> struct RealToHalf<double> {
185  enum { isReal = true };
186  using HalfT = math::half;
187  // A half can only be constructed from a float, so cast the value to a float first.
188  static HalfT convert(double val) { return HalfT(float(val)); }
189 };
190 template<> struct RealToHalf<Vec2s> {
191  enum { isReal = true };
192  using HalfT = Vec2H;
193  static HalfT convert(const Vec2s& val) { return HalfT(val); }
194 };
195 template<> struct RealToHalf<Vec2d> {
196  enum { isReal = true };
197  using HalfT = Vec2H;
198  // A half can only be constructed from a float, so cast the vector's elements to floats first.
199  static HalfT convert(const Vec2d& val) { return HalfT(Vec2s(val)); }
200 };
201 template<> struct RealToHalf<Vec3s> {
202  enum { isReal = true };
203  using HalfT = Vec3H;
204  static HalfT convert(const Vec3s& val) { return HalfT(val); }
205 };
206 template<> struct RealToHalf<Vec3d> {
207  enum { isReal = true };
208  using HalfT = Vec3H;
209  // A half can only be constructed from a float, so cast the vector's elements to floats first.
210  static HalfT convert(const Vec3d& val) { return HalfT(Vec3s(val)); }
211 };
212 
213 
214 /// Return the given value truncated to 16-bit float precision.
215 template<typename T>
216 inline T
218 {
219  return T(RealToHalf<T>::convert(val));
220 }
221 
222 
223 ////////////////////////////////////////
224 
225 
226 OPENVDB_API size_t zipToStreamSize(const char* data, size_t numBytes);
227 OPENVDB_API void zipToStream(std::ostream&, const char* data, size_t numBytes);
228 OPENVDB_API void unzipFromStream(std::istream&, char* data, size_t numBytes);
229 OPENVDB_API size_t bloscToStreamSize(const char* data, size_t valSize, size_t numVals);
230 OPENVDB_API void bloscToStream(std::ostream&, const char* data, size_t valSize, size_t numVals);
231 OPENVDB_API void bloscFromStream(std::istream&, char* data, size_t numBytes);
232 
233 /// @brief Read data from a stream.
234 /// @param is the input stream
235 /// @param data the contiguous array of data to read in
236 /// @param count the number of elements to read in
237 /// @param compression whether and how the data is compressed (either COMPRESS_NONE,
238 /// COMPRESS_ZIP, COMPRESS_ACTIVE_MASK or COMPRESS_BLOSC)
239 /// @param metadata optional pointer to a DelayedLoadMetadata object that stores
240 /// the size of the compressed buffer
241 /// @param metadataOffset offset into DelayedLoadMetadata, ignored if pointer is null
242 /// @throw IoError if @a compression is COMPRESS_BLOSC but OpenVDB was compiled
243 /// without Blosc support.
244 /// @details This default implementation is instantiated only for types
245 /// whose size can be determined by the sizeof() operator.
246 template<typename T>
247 inline void
248 readData(std::istream& is, T* data, Index count, uint32_t compression,
249  DelayedLoadMetadata* metadata = nullptr, size_t metadataOffset = size_t(0))
250 {
251  const bool seek = data == nullptr;
252  if (seek) {
254  }
255  const bool hasCompression = compression & (COMPRESS_BLOSC | COMPRESS_ZIP);
256 
257  if (metadata && seek && hasCompression) {
258  size_t compressedSize = metadata->getCompressedSize(metadataOffset);
259  is.seekg(compressedSize, std::ios_base::cur);
260  } else if (compression & COMPRESS_BLOSC) {
261  bloscFromStream(is, reinterpret_cast<char*>(data), sizeof(T) * count);
262  } else if (compression & COMPRESS_ZIP) {
263  unzipFromStream(is, reinterpret_cast<char*>(data), sizeof(T) * count);
264  } else if (seek) {
265  is.seekg(sizeof(T) * count, std::ios_base::cur);
266  } else {
267  is.read(reinterpret_cast<char*>(data), sizeof(T) * count);
268  }
269 }
270 
271 /// Specialization for std::string input
272 template<>
273 inline void
274 readData<std::string>(std::istream& is, std::string* data, Index count, uint32_t /*compression*/,
275  DelayedLoadMetadata* /*metadata*/, size_t /*metadataOffset*/)
276 {
277  for (Index i = 0; i < count; ++i) {
278  size_t len = 0;
279  is >> len;
280  //data[i].resize(len);
281  //is.read(&(data[i][0]), len);
282 
283  std::string buffer(len+1, ' ');
284  is.read(&buffer[0], len+1);
285  if (data != nullptr) data[i].assign(buffer, 0, len);
286  }
287 }
288 
289 /// HalfReader wraps a static function, read(), that is analogous to readData(), above,
290 /// except that it is partially specialized for floating-point types in order to promote
291 /// 16-bit half float values to full float. A wrapper class is required because
292 /// only classes, not functions, can be partially specialized.
293 template<bool IsReal, typename T> struct HalfReader;
294 /// Partial specialization for non-floating-point types (no half to float promotion)
295 template<typename T>
296 struct HalfReader</*IsReal=*/false, T> {
297  static inline void read(std::istream& is, T* data, Index count, uint32_t compression,
298  DelayedLoadMetadata* metadata = nullptr, size_t metadataOffset = size_t(0)) {
299  readData(is, data, count, compression, metadata, metadataOffset);
300  }
301 };
302 /// Partial specialization for floating-point types
303 template<typename T>
304 struct HalfReader</*IsReal=*/true, T> {
305  using HalfT = typename RealToHalf<T>::HalfT;
306  static inline void read(std::istream& is, T* data, Index count, uint32_t compression,
307  DelayedLoadMetadata* metadata = nullptr, size_t metadataOffset = size_t(0)) {
308  if (count < 1) return;
309  if (data == nullptr) {
310  // seek mode - pass through null pointer
311  readData<HalfT>(is, nullptr, count, compression, metadata, metadataOffset);
312  } else {
313  std::vector<HalfT> halfData(count); // temp buffer into which to read half float values
314  readData<HalfT>(is, reinterpret_cast<HalfT*>(&halfData[0]), count, compression,
315  metadata, metadataOffset);
316  // Copy half float values from the temporary buffer to the full float output array.
317  std::copy(halfData.begin(), halfData.end(), data);
318  }
319  }
320 };
321 
322 
323 template<typename T>
324 inline size_t
325 writeDataSize(const T *data, Index count, uint32_t compression)
326 {
327  if (compression & COMPRESS_BLOSC) {
328  return bloscToStreamSize(reinterpret_cast<const char*>(data), sizeof(T), count);
329  } else if (compression & COMPRESS_ZIP) {
330  return zipToStreamSize(reinterpret_cast<const char*>(data), sizeof(T) * count);
331  } else {
332  return sizeof(T) * count;
333  }
334 }
335 
336 
337 /// Specialization for std::string output
338 template<>
339 inline size_t
340 writeDataSize<std::string>(const std::string* data, Index count,
341  uint32_t /*compression*/) ///< @todo add compression
342 {
343  size_t size(0);
344  for (Index i = 0; i < count; ++i) {
345  const size_t len = data[i].size();
346  size += sizeof(size_t) + (len+1);
347  }
348  return size;
349 }
350 
351 
352 /// Write data to a stream.
353 /// @param os the output stream
354 /// @param data the contiguous array of data to write
355 /// @param count the number of elements to write out
356 /// @param compression whether and how to compress the data (either COMPRESS_NONE,
357 /// COMPRESS_ZIP, COMPRESS_ACTIVE_MASK or COMPRESS_BLOSC)
358 /// @throw IoError if @a compression is COMPRESS_BLOSC but OpenVDB was compiled
359 /// without Blosc support.
360 /// @details This default implementation is instantiated only for types
361 /// whose size can be determined by the sizeof() operator.
362 template<typename T>
363 inline void
364 writeData(std::ostream &os, const T *data, Index count, uint32_t compression)
365 {
366  if (compression & COMPRESS_BLOSC) {
367  bloscToStream(os, reinterpret_cast<const char*>(data), sizeof(T), count);
368  } else if (compression & COMPRESS_ZIP) {
369  zipToStream(os, reinterpret_cast<const char*>(data), sizeof(T) * count);
370  } else {
371  os.write(reinterpret_cast<const char*>(data), sizeof(T) * count);
372  }
373 }
374 
375 /// Specialization for std::string output
376 template<>
377 inline void
378 writeData<std::string>(std::ostream& os, const std::string* data, Index count,
379  uint32_t /*compression*/) ///< @todo add compression
380 {
381  for (Index i = 0; i < count; ++i) {
382  const size_t len = data[i].size();
383  os << len;
384  os.write(data[i].c_str(), len+1);
385  //os.write(&(data[i][0]), len );
386  }
387 }
388 
389 /// HalfWriter wraps a static function, write(), that is analogous to writeData(), above,
390 /// except that it is partially specialized for floating-point types in order to quantize
391 /// floating-point values to 16-bit half float. A wrapper class is required because
392 /// only classes, not functions, can be partially specialized.
393 template<bool IsReal, typename T> struct HalfWriter;
394 /// Partial specialization for non-floating-point types (no float to half quantization)
395 template<typename T>
396 struct HalfWriter</*IsReal=*/false, T> {
397  static inline size_t writeSize(const T* data, Index count, uint32_t compression) {
398  return writeDataSize(data, count, compression);
399  }
400  static inline void write(std::ostream& os, const T* data, Index count, uint32_t compression) {
401  writeData(os, data, count, compression);
402  }
403 };
404 /// Partial specialization for floating-point types
405 template<typename T>
406 struct HalfWriter</*IsReal=*/true, T> {
407  using HalfT = typename RealToHalf<T>::HalfT;
408  static inline size_t writeSize(const T* data, Index count, uint32_t compression) {
409  if (count < 1) return size_t(0);
410  // Convert full float values to half float, then output the half float array.
411  std::vector<HalfT> halfData(count);
413  return writeDataSize<HalfT>(reinterpret_cast<const HalfT*>(&halfData[0]), count, compression);
414  }
415  static inline void write(std::ostream& os, const T* data, Index count, uint32_t compression) {
416  if (count < 1) return;
417  // Convert full float values to half float, then output the half float array.
418  std::vector<HalfT> halfData(count);
420  writeData<HalfT>(os, reinterpret_cast<const HalfT*>(&halfData[0]), count, compression);
421  }
422 };
423 #ifdef _WIN32
424 /// Specialization to avoid double to float warnings in MSVC
425 template<>
426 struct HalfWriter</*IsReal=*/true, double> {
427  using HalfT = RealToHalf<double>::HalfT;
428  static inline size_t writeSize(const double* data, Index count, uint32_t compression)
429  {
430  if (count < 1) return size_t(0);
431  // Convert full float values to half float, then output the half float array.
432  std::vector<HalfT> halfData(count);
434  return writeDataSize<HalfT>(reinterpret_cast<const HalfT*>(&halfData[0]), count, compression);
435  }
436  static inline void write(std::ostream& os, const double* data, Index count,
437  uint32_t compression)
438  {
439  if (count < 1) return;
440  // Convert full float values to half float, then output the half float array.
441  std::vector<HalfT> halfData(count);
443  writeData<HalfT>(os, reinterpret_cast<const HalfT*>(&halfData[0]), count, compression);
444  }
445 };
446 #endif // _WIN32
447 
448 
449 ////////////////////////////////////////
450 
451 
452 /// Populate the given buffer with @a destCount values of type @c ValueT
453 /// read from the given stream, taking into account that the stream might
454 /// have been compressed via one of several supported schemes.
455 /// [Mainly for internal use]
456 /// @param is a stream from which to read data (possibly compressed,
457 /// depending on the stream's compression settings)
458 /// @param destBuf a buffer into which to read values of type @c ValueT
459 /// @param destCount the number of values to be stored in the buffer
460 /// @param valueMask a bitmask (typically, a node's value mask) indicating
461 /// which positions in the buffer correspond to active values
462 /// @param fromHalf if true, read 16-bit half floats from the input stream
463 /// and convert them to full floats
464 template<typename ValueT, typename MaskT>
465 inline void
466 readCompressedValues(std::istream& is, ValueT* destBuf, Index destCount,
467  const MaskT& valueMask, bool fromHalf)
468 {
469  checkFormatVersion(is);
470 
471  // Get the stream's compression settings.
472  auto meta = getStreamMetadataPtr(is);
473  const uint32_t compression = getDataCompression(is);
474  const bool maskCompressed = compression & COMPRESS_ACTIVE_MASK;
475 
476  const bool seek = (destBuf == nullptr);
477  OPENVDB_ASSERT(!seek || (!meta || meta->seekable()));
478 
479  // Get delayed load metadata if it exists
480 
481  DelayedLoadMetadata::Ptr delayLoadMeta;
482  uint64_t leafIndex(0);
483  if (seek && meta && meta->delayedLoadMeta()) {
484  delayLoadMeta =
485  meta->gridMetadata().getMetadata<DelayedLoadMetadata>("file_delayed_load");
486  leafIndex = meta->leaf();
487  }
488 
489  int8_t metadata = NO_MASK_AND_ALL_VALS;
490 
492  // Read the flag that specifies what, if any, additional metadata
493  // (selection mask and/or inactive value(s)) is saved.
494  if (seek && !maskCompressed) {
495  is.seekg(/*bytes=*/1, std::ios_base::cur);
496  } else if (seek && delayLoadMeta) {
497  metadata = delayLoadMeta->getMask(leafIndex);
498  is.seekg(/*bytes=*/1, std::ios_base::cur);
499  } else {
500  is.read(reinterpret_cast<char*>(&metadata), /*bytes=*/1);
501  }
502  }
503 
504  ValueT background = zeroVal<ValueT>();
505  if (const void* bgPtr = getGridBackgroundValuePtr(is)) {
506  background = *static_cast<const ValueT*>(bgPtr);
507  }
508  ValueT inactiveVal1 = background;
509  ValueT inactiveVal0 =
510  ((metadata == NO_MASK_OR_INACTIVE_VALS) ? background : math::negative(background));
511 
512  if (metadata == NO_MASK_AND_ONE_INACTIVE_VAL ||
513  metadata == MASK_AND_ONE_INACTIVE_VAL ||
514  metadata == MASK_AND_TWO_INACTIVE_VALS)
515  {
516  // Read one of at most two distinct inactive values.
517  if (seek) {
518  is.seekg(/*bytes=*/sizeof(ValueT), std::ios_base::cur);
519  } else {
520  is.read(reinterpret_cast<char*>(&inactiveVal0), /*bytes=*/sizeof(ValueT));
521  }
522  if (metadata == MASK_AND_TWO_INACTIVE_VALS) {
523  // Read the second of two distinct inactive values.
524  if (seek) {
525  is.seekg(/*bytes=*/sizeof(ValueT), std::ios_base::cur);
526  } else {
527  is.read(reinterpret_cast<char*>(&inactiveVal1), /*bytes=*/sizeof(ValueT));
528  }
529  }
530  }
531 
532  MaskT selectionMask;
533  if (metadata == MASK_AND_NO_INACTIVE_VALS ||
534  metadata == MASK_AND_ONE_INACTIVE_VAL ||
535  metadata == MASK_AND_TWO_INACTIVE_VALS)
536  {
537  // For use in mask compression (only), read the bitmask that selects
538  // between two distinct inactive values.
539  if (seek) {
540  is.seekg(/*bytes=*/selectionMask.memUsage(), std::ios_base::cur);
541  } else {
542  selectionMask.load(is);
543  }
544  }
545 
546  ValueT* tempBuf = destBuf;
547  std::unique_ptr<ValueT[]> scopedTempBuf;
548 
549  Index tempCount = destCount;
550 
551  if (maskCompressed && metadata != NO_MASK_AND_ALL_VALS)
552  {
553  tempCount = valueMask.countOn();
554  if (!seek && tempCount != destCount) {
555  // If this node has inactive voxels, allocate a temporary buffer
556  // into which to read just the active values.
557  scopedTempBuf.reset(new ValueT[tempCount]);
558  tempBuf = scopedTempBuf.get();
559  }
560  }
561 
562  // Read in the buffer.
563  if (fromHalf) {
565  is, (seek ? nullptr : tempBuf), tempCount, compression, delayLoadMeta.get(), leafIndex);
566  } else {
567  readData<ValueT>(
568  is, (seek ? nullptr : tempBuf), tempCount, compression, delayLoadMeta.get(), leafIndex);
569  }
570 
571  // If mask compression is enabled and the number of active values read into
572  // the temp buffer is smaller than the size of the destination buffer,
573  // then there are missing (inactive) values.
574  if (!seek && maskCompressed && tempCount != destCount) {
575  // Restore inactive values, using the background value and, if available,
576  // the inside/outside mask. (For fog volumes, the destination buffer is assumed
577  // to be initialized to background value zero, so inactive values can be ignored.)
578  for (Index destIdx = 0, tempIdx = 0; destIdx < MaskT::SIZE; ++destIdx) {
579  if (valueMask.isOn(destIdx)) {
580  // Copy a saved active value into this node's buffer.
581  destBuf[destIdx] = tempBuf[tempIdx];
582  ++tempIdx;
583  } else {
584  // Reconstruct an unsaved inactive value and copy it into this node's buffer.
585  destBuf[destIdx] = (selectionMask.isOn(destIdx) ? inactiveVal1 : inactiveVal0);
586  }
587  }
588  }
589 }
590 
591 
592 template<typename ValueT, typename MaskT>
593 inline size_t
594 writeCompressedValuesSize(ValueT* srcBuf, Index srcCount,
595  const MaskT& valueMask, uint8_t maskMetadata, bool toHalf, uint32_t compress)
596 {
597  using NonConstValueT = typename std::remove_const<ValueT>::type;
598 
599  const bool maskCompress = compress & COMPRESS_ACTIVE_MASK;
600 
601  Index tempCount = srcCount;
602  ValueT* tempBuf = srcBuf;
603  std::unique_ptr<NonConstValueT[]> scopedTempBuf;
604 
605  if (maskCompress && maskMetadata != NO_MASK_AND_ALL_VALS) {
606 
607  tempCount = 0;
608 
609  Index64 onVoxels = valueMask.countOn();
610  if (onVoxels > Index64(0)) {
611  // Create a new array to hold just the active values.
612  scopedTempBuf.reset(new NonConstValueT[onVoxels]);
613  NonConstValueT* localTempBuf = scopedTempBuf.get();
614 
615  // Copy active values to a new, contiguous array.
616  for (typename MaskT::OnIterator it = valueMask.beginOn(); it; ++it, ++tempCount) {
617  localTempBuf[tempCount] = srcBuf[it.pos()];
618  }
619 
620  tempBuf = scopedTempBuf.get();
621  }
622  }
623 
624  // Return the buffer size.
625  if (toHalf) {
626  return HalfWriter<RealToHalf<NonConstValueT>::isReal, NonConstValueT>::writeSize(
627  tempBuf, tempCount, compress);
628  } else {
629  return writeDataSize<NonConstValueT>(tempBuf, tempCount, compress);
630  }
631 }
632 
633 
634 /// Write @a srcCount values of type @c ValueT to the given stream, optionally
635 /// after compressing the values via one of several supported schemes.
636 /// [Mainly for internal use]
637 /// @param os a stream to which to write data (possibly compressed, depending
638 /// on the stream's compression settings)
639 /// @param srcBuf a buffer containing values of type @c ValueT to be written
640 /// @param srcCount the number of values stored in the buffer
641 /// @param valueMask a bitmask (typically, a node's value mask) indicating
642 /// which positions in the buffer correspond to active values
643 /// @param childMask a bitmask (typically, a node's child mask) indicating
644 /// which positions in the buffer correspond to child node pointers
645 /// @param toHalf if true, convert floating-point values to 16-bit half floats
646 template<typename ValueT, typename MaskT>
647 inline void
648 writeCompressedValues(std::ostream& os, const ValueT* srcBuf, Index srcCount,
649  const MaskT& valueMask, const MaskT& childMask, bool toHalf)
650 {
651  // Get the stream's compression settings.
652  const uint32_t compress = getDataCompression(os);
653  const bool maskCompress = compress & COMPRESS_ACTIVE_MASK;
654 
655  Index tempCount = srcCount;
656  ValueT* tempBuf = nullptr;
657  std::unique_ptr<ValueT[]> scopedTempBuf;
658 
659  int8_t metadata = NO_MASK_AND_ALL_VALS;
660 
661  if (!maskCompress) {
662  os.write(reinterpret_cast<const char*>(&metadata), /*bytes=*/1);
663  } else {
664  // A valid level set's inactive values are either +background (outside)
665  // or -background (inside), and a fog volume's inactive values are all zero.
666  // Rather than write out all of these values, we can store just the active values
667  // (given that the value mask specifies their positions) and, if necessary,
668  // an inside/outside bitmask.
669 
670  const ValueT zero = zeroVal<ValueT>();
671  ValueT background = zero;
672  if (const void* bgPtr = getGridBackgroundValuePtr(os)) {
673  background = *static_cast<const ValueT*>(bgPtr);
674  }
675 
676  MaskCompress<ValueT, MaskT> maskCompressData(valueMask, childMask, srcBuf, background);
677  metadata = maskCompressData.metadata;
678 
679  os.write(reinterpret_cast<const char*>(&metadata), /*bytes=*/1);
680 
681  if (metadata == NO_MASK_AND_ONE_INACTIVE_VAL ||
682  metadata == MASK_AND_ONE_INACTIVE_VAL ||
683  metadata == MASK_AND_TWO_INACTIVE_VALS)
684  {
685  if (!toHalf) {
686  // Write one of at most two distinct inactive values.
687  os.write(reinterpret_cast<const char*>(&maskCompressData.inactiveVal[0]), sizeof(ValueT));
688  if (metadata == MASK_AND_TWO_INACTIVE_VALS) {
689  // Write the second of two distinct inactive values.
690  os.write(reinterpret_cast<const char*>(&maskCompressData.inactiveVal[1]), sizeof(ValueT));
691  }
692  } else {
693  // Write one of at most two distinct inactive values.
694  ValueT truncatedVal = static_cast<ValueT>(truncateRealToHalf(maskCompressData.inactiveVal[0]));
695  os.write(reinterpret_cast<const char*>(&truncatedVal), sizeof(ValueT));
696  if (metadata == MASK_AND_TWO_INACTIVE_VALS) {
697  // Write the second of two distinct inactive values.
698  truncatedVal = truncateRealToHalf(maskCompressData.inactiveVal[1]);
699  os.write(reinterpret_cast<const char*>(&truncatedVal), sizeof(ValueT));
700  }
701  }
702  }
703 
704  if (metadata == NO_MASK_AND_ALL_VALS) {
705  // If there are more than two unique inactive values, the entire input buffer
706  // needs to be saved (both active and inactive values).
707  /// @todo Save the selection mask as long as most of the inactive values
708  /// are one of two values?
709  } else {
710  // Create a new array to hold just the active values.
711  scopedTempBuf.reset(new ValueT[srcCount]);
712  tempBuf = scopedTempBuf.get();
713 
714  if (metadata == NO_MASK_OR_INACTIVE_VALS ||
715  metadata == NO_MASK_AND_MINUS_BG ||
716  metadata == NO_MASK_AND_ONE_INACTIVE_VAL)
717  {
718  // Copy active values to the contiguous array.
719  tempCount = 0;
720  for (typename MaskT::OnIterator it = valueMask.beginOn(); it; ++it, ++tempCount) {
721  tempBuf[tempCount] = srcBuf[it.pos()];
722  }
723  } else {
724  // Copy active values to a new, contiguous array and populate a bitmask
725  // that selects between two distinct inactive values.
726  MaskT selectionMask;
727  tempCount = 0;
728  for (Index srcIdx = 0; srcIdx < srcCount; ++srcIdx) {
729  if (valueMask.isOn(srcIdx)) { // active value
730  tempBuf[tempCount] = srcBuf[srcIdx];
731  ++tempCount;
732  } else { // inactive value
733  if (MaskCompress<ValueT, MaskT>::eq(srcBuf[srcIdx], maskCompressData.inactiveVal[1])) {
734  selectionMask.setOn(srcIdx); // inactive value 1
735  } // else inactive value 0
736  }
737  }
738  OPENVDB_ASSERT(tempCount == valueMask.countOn());
739 
740  // Write out the mask that selects between two inactive values.
741  selectionMask.save(os);
742  }
743  }
744  }
745 
746  // Write out the buffer.
747  if (toHalf) {
748  HalfWriter<RealToHalf<ValueT>::isReal, ValueT>::write(os,
749  bool(tempBuf) ? tempBuf : srcBuf, tempCount, compress);
750  } else {
751  writeData(os, bool(tempBuf) ? tempBuf : srcBuf, tempCount, compress);
752  }
753 }
754 
755 } // namespace io
756 } // namespace OPENVDB_VERSION_NAME
757 } // namespace openvdb
758 
759 #endif // OPENVDB_IO_COMPRESSION_HAS_BEEN_INCLUDED
type
Definition: core.h:556
math::Vec3< Half > Vec3H
Definition: Types.h:56
GLbitfield flags
Definition: glcorearb.h:1596
OPENVDB_API 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...
Store a buffer of data that can be optionally used during reading for faster delayed-load I/O perform...
bool isExactlyEqual(const T0 &a, const T1 &b)
Return true if a is exactly equal to b.
Definition: Math.h:468
void writeData(std::ostream &os, const T *data, Index count, uint32_t compression)
Definition: Compression.h:364
T negative(const T &val)
Return the unary negation of the given value.
Definition: Math.h:139
GLboolean * data
Definition: glcorearb.h:131
void swap(UT::ArraySet< Key, MULTI, MAX_LOAD_FACTOR_256, Clearer, Hash, KeyEqual > &a, UT::ArraySet< Key, MULTI, MAX_LOAD_FACTOR_256, Clearer, Hash, KeyEqual > &b)
Definition: UT_ArraySet.h:1699
OPENVDB_API void unzipFromStream(std::istream &, char *data, size_t numBytes)
OPENVDB_API std::string compressionToString(uint32_t flags)
Return a string describing the given compression flags.
OPENVDB_API uint32_t getDataCompression(std::ios_base &)
Return a bitwise OR of compression option flags (COMPRESS_ZIP, COMPRESS_ACTIVE_MASK, etc.) specifying whether and how input data is compressed or output data should be compressed.
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1222
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:246
Tto convert(const Tfrom &source)
GLuint buffer
Definition: glcorearb.h:660
OPENVDB_API void bloscFromStream(std::istream &, char *data, size_t numBytes)
OPENVDB_API void checkFormatVersion(std::ios_base &)
Throws an IoError if the file format version number is not supported.
void readData(std::istream &is, T *data, Index count, uint32_t compression, DelayedLoadMetadata *metadata=nullptr, size_t metadataOffset=size_t(0))
Read data from a stream.
Definition: Compression.h:248
#define OPENVDB_ASSERT(X)
Definition: Assert.h:41
void readCompressedValues(std::istream &is, ValueT *destBuf, Index destCount, const MaskT &valueMask, bool fromHalf)
Definition: Compression.h:466
void writeCompressedValues(std::ostream &os, const ValueT *srcBuf, Index srcCount, const MaskT &valueMask, const MaskT &childMask, bool toHalf)
Definition: Compression.h:648
static size_t writeSize(const T *data, Index count, uint32_t compression)
Definition: Compression.h:397
Vec3< double > Vec3d
Definition: NanoVDB.h:1685
OPENVDB_API size_t bloscToStreamSize(const char *data, size_t valSize, size_t numVals)
#define OPENVDB_API
Definition: Platform.h:291
static bool eq(const ValueT &a, const ValueT &b)
Definition: Compression.h:84
size_t writeCompressedValuesSize(ValueT *srcBuf, Index srcCount, const MaskT &valueMask, uint8_t maskMetadata, bool toHalf, uint32_t compress)
Definition: Compression.h:594
T truncateRealToHalf(const T &val)
Return the given value truncated to 16-bit float precision.
Definition: Compression.h:217
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, DelayedLoadMetadata *metadata=nullptr, size_t metadataOffset=size_t(0))
Definition: Compression.h:297
static size_t writeSize(const T *data, Index count, uint32_t compression)
Definition: Compression.h:408
static void read(std::istream &is, T *data, Index count, uint32_t compression, DelayedLoadMetadata *metadata=nullptr, size_t metadataOffset=size_t(0))
Definition: Compression.h:306
GLboolean GLboolean GLboolean b
Definition: glcorearb.h:1222
math::Vec2< Half > Vec2H
Definition: Types.h:47
OPENVDB_API void bloscToStream(std::ostream &, const char *data, size_t valSize, size_t numVals)
GLsizeiptr size
Definition: glcorearb.h:664
IMATH_NAMESPACE::V2f IMATH_NAMESPACE::Box2i std::string this attribute is obsolete as of OpenEXR v3 float
RealToHalf and its specializations define a mapping from floating-point data types to analogous half ...
Definition: Compression.h:174
GLuint GLfloat * val
Definition: glcorearb.h:1608
OPENVDB_API void zipToStream(std::ostream &, const char *data, size_t numBytes)
OPENVDB_API SharedPtr< StreamMetadata > getStreamMetadataPtr(std::ios_base &)
Return a shared pointer to an object that stores metadata (file format, compression scheme...
OIIO_UTIL_API const char * c_str(string_view str)
#define SIZE
Definition: simple.C:41
PUGI__FN I unique(I begin, I end)
Definition: pugixml.cpp:7464
size_t writeDataSize(const T *data, Index count, uint32_t compression)
Definition: Compression.h:325
MaskCompress(const MaskT &valueMask, const MaskT &childMask, const ValueT *srcBuf, const ValueT &background)
Definition: Compression.h:88
static void write(std::ostream &os, const T *data, Index count, uint32_t compression)
Definition: Compression.h:415
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:119
OPENVDB_API uint32_t getFormatVersion(std::ios_base &)
Return the file format version number associated with the given input stream.
static void write(std::ostream &os, const T *data, Index count, uint32_t compression)
Definition: Compression.h:400
OPENVDB_API size_t zipToStreamSize(const char *data, size_t numBytes)
GLint GLsizei count
Definition: glcorearb.h:405
Definition: format.h:1821