HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Compression.h
Go to the documentation of this file.
1 ///////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2012-2018 DreamWorks Animation LLC
4 //
5 // All rights reserved. This software is distributed under the
6 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
7 //
8 // Redistributions of source code must retain the above copyright
9 // and license notice and the following restrictions and disclaimer.
10 //
11 // * Neither the name of DreamWorks Animation nor the names of
12 // its contributors may be used to endorse or promote products derived
13 // from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
20 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 // IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
27 // LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
28 //
29 ///////////////////////////////////////////////////////////////////////////
30 
31 #ifndef OPENVDB_IO_COMPRESSION_HAS_BEEN_INCLUDED
32 #define OPENVDB_IO_COMPRESSION_HAS_BEEN_INCLUDED
33 
34 #include <openvdb/Types.h>
35 #include <openvdb/math/Math.h> // for negative()
36 #include "io.h" // for getDataCompression(), etc.
37 #include <algorithm>
38 #include <iostream>
39 #include <memory>
40 #include <string>
41 #include <vector>
42 
43 
44 namespace openvdb {
46 namespace OPENVDB_VERSION_NAME {
47 namespace io {
48 
49 /// @brief OR-able bit flags for compression options on input and output streams
50 /// @details
51 /// <dl>
52 /// <dt><tt>COMPRESS_NONE</tt>
53 /// <dd>On write, don't compress data.<br>
54 /// On read, the input stream contains uncompressed data.
55 ///
56 /// <dt><tt>COMPRESS_ZIP</tt>
57 /// <dd>When writing grids other than level sets or fog volumes, apply
58 /// ZLIB compression to internal and leaf node value buffers.<br>
59 /// When reading grids other than level sets or fog volumes, indicate that
60 /// the value buffers of internal and leaf nodes are ZLIB-compressed.<br>
61 /// ZLIB compresses well but is slow.
62 ///
63 /// <dt><tt>COMPRESS_ACTIVE_MASK</tt>
64 /// <dd>When writing a grid of any class, don't output a node's inactive values
65 /// if it has two or fewer distinct values. Instead, output minimal information
66 /// to permit the lossless reconstruction of inactive values.<br>
67 /// On read, nodes might have been stored without inactive values.
68 /// Where necessary, reconstruct inactive values from available information.
69 ///
70 /// <dt><tt>COMPRESS_BLOSC</tt>
71 /// <dd>When writing grids other than level sets or fog volumes, apply
72 /// Blosc compression to internal and leaf node value buffers.<br>
73 /// When reading grids other than level sets or fog volumes, indicate that
74 /// the value buffers of internal and leaf nodes are Blosc-compressed.<br>
75 /// Blosc is much faster than ZLIB and produces comparable file sizes.
76 /// </dl>
77 enum {
79  COMPRESS_ZIP = 0x1,
82 };
83 
84 /// Return a string describing the given compression flags.
86 
87 
88 ////////////////////////////////////////
89 
90 
91 /// @internal Per-node indicator byte that specifies what additional metadata
92 /// is stored to permit reconstruction of inactive values
93 enum {
94  /*0*/ NO_MASK_OR_INACTIVE_VALS, // no inactive vals, or all inactive vals are +background
95  /*1*/ NO_MASK_AND_MINUS_BG, // all inactive vals are -background
96  /*2*/ NO_MASK_AND_ONE_INACTIVE_VAL, // all inactive vals have the same non-background val
97  /*3*/ MASK_AND_NO_INACTIVE_VALS, // mask selects between -background and +background
98  /*4*/ MASK_AND_ONE_INACTIVE_VAL, // mask selects between backgd and one other inactive val
99  /*5*/ MASK_AND_TWO_INACTIVE_VALS, // mask selects between two non-background inactive vals
100  /*6*/ NO_MASK_AND_ALL_VALS // > 2 inactive vals, so no mask compression at all
101 };
102 
103 
104 ////////////////////////////////////////
105 
106 
107 /// @brief RealToHalf and its specializations define a mapping from
108 /// floating-point data types to analogous half float types.
109 template<typename T>
110 struct RealToHalf {
111  enum { isReal = false }; // unless otherwise specified, type T is not a floating-point type
112  using HalfT = T; // type T's half float analogue is T itself
113  static HalfT convert(const T& val) { return val; }
114 };
115 template<> struct RealToHalf<float> {
116  enum { isReal = true };
117  using HalfT = half;
118  static HalfT convert(float val) { return HalfT(val); }
119 };
120 template<> struct RealToHalf<double> {
121  enum { isReal = true };
122  using HalfT = half;
123  // A half can only be constructed from a float, so cast the value to a float first.
124  static HalfT convert(double val) { return HalfT(float(val)); }
125 };
126 template<> struct RealToHalf<Vec2s> {
127  enum { isReal = true };
128  using HalfT = Vec2H;
129  static HalfT convert(const Vec2s& val) { return HalfT(val); }
130 };
131 template<> struct RealToHalf<Vec2d> {
132  enum { isReal = true };
133  using HalfT = Vec2H;
134  // A half can only be constructed from a float, so cast the vector's elements to floats first.
135  static HalfT convert(const Vec2d& val) { return HalfT(Vec2s(val)); }
136 };
137 template<> struct RealToHalf<Vec3s> {
138  enum { isReal = true };
139  using HalfT = Vec3H;
140  static HalfT convert(const Vec3s& val) { return HalfT(val); }
141 };
142 template<> struct RealToHalf<Vec3d> {
143  enum { isReal = true };
144  using HalfT = Vec3H;
145  // A half can only be constructed from a float, so cast the vector's elements to floats first.
146  static HalfT convert(const Vec3d& val) { return HalfT(Vec3s(val)); }
147 };
148 
149 
150 /// Return the given value truncated to 16-bit float precision.
151 template<typename T>
152 inline T
154 {
155  return T(RealToHalf<T>::convert(val));
156 }
157 
158 
159 ////////////////////////////////////////
160 
161 
162 OPENVDB_API void zipToStream(std::ostream&, const char* data, size_t numBytes);
163 OPENVDB_API void unzipFromStream(std::istream&, char* data, size_t numBytes);
164 OPENVDB_API void bloscToStream(std::ostream&, const char* data, size_t valSize, size_t numVals);
165 OPENVDB_API void bloscFromStream(std::istream&, char* data, size_t numBytes);
166 
167 /// @brief Read data from a stream.
168 /// @param is the input stream
169 /// @param data the contiguous array of data to read in
170 /// @param count the number of elements to read in
171 /// @param compression whether and how the data is compressed (either COMPRESS_NONE,
172 /// COMPRESS_ZIP, COMPRESS_ACTIVE_MASK or COMPRESS_BLOSC)
173 /// @throw IoError if @a compression is COMPRESS_BLOSC but OpenVDB was compiled
174 /// without Blosc support.
175 /// @details This default implementation is instantiated only for types
176 /// whose size can be determined by the sizeof() operator.
177 template<typename T>
178 inline void
179 readData(std::istream& is, T* data, Index count, uint32_t compression)
180 {
181  if (compression & COMPRESS_BLOSC) {
182  bloscFromStream(is, reinterpret_cast<char*>(data), sizeof(T) * count);
183  } else if (compression & COMPRESS_ZIP) {
184  unzipFromStream(is, reinterpret_cast<char*>(data), sizeof(T) * count);
185  } else {
186  if (data == nullptr) {
187  assert(!getStreamMetadataPtr(is) || getStreamMetadataPtr(is)->seekable());
188  is.seekg(sizeof(T) * count, std::ios_base::cur);
189  } else {
190  is.read(reinterpret_cast<char*>(data), sizeof(T) * count);
191  }
192  }
193 }
194 
195 /// Specialization for std::string input
196 template<>
197 inline void
198 readData<std::string>(std::istream& is, std::string* data, Index count, uint32_t /*compression*/)
199 {
200  for (Index i = 0; i < count; ++i) {
201  size_t len = 0;
202  is >> len;
203  //data[i].resize(len);
204  //is.read(&(data[i][0]), len);
205 
206  std::string buffer(len+1, ' ');
207  is.read(&buffer[0], len+1);
208  if (data != nullptr) data[i].assign(buffer, 0, len);
209  }
210 }
211 
212 /// HalfReader wraps a static function, read(), that is analogous to readData(), above,
213 /// except that it is partially specialized for floating-point types in order to promote
214 /// 16-bit half float values to full float. A wrapper class is required because
215 /// only classes, not functions, can be partially specialized.
216 template<bool IsReal, typename T> struct HalfReader;
217 /// Partial specialization for non-floating-point types (no half to float promotion)
218 template<typename T>
219 struct HalfReader</*IsReal=*/false, T> {
220  static inline void read(std::istream& is, T* data, Index count, uint32_t compression) {
221  readData(is, data, count, compression);
222  }
223 };
224 /// Partial specialization for floating-point types
225 template<typename T>
226 struct HalfReader</*IsReal=*/true, T> {
227  using HalfT = typename RealToHalf<T>::HalfT;
228  static inline void read(std::istream& is, T* data, Index count, uint32_t compression) {
229  if (count < 1) return;
230  if (data == nullptr) {
231  // seek mode - pass through null pointer
232  readData<HalfT>(is, nullptr, count, compression);
233  } else {
234  std::vector<HalfT> halfData(count); // temp buffer into which to read half float values
235  readData<HalfT>(is, reinterpret_cast<HalfT*>(&halfData[0]), count, compression);
236  // Copy half float values from the temporary buffer to the full float output array.
237  std::copy(halfData.begin(), halfData.end(), data);
238  }
239  }
240 };
241 
242 
243 /// Write data to a stream.
244 /// @param os the output stream
245 /// @param data the contiguous array of data to write
246 /// @param count the number of elements to write out
247 /// @param compression whether and how to compress the data (either COMPRESS_NONE,
248 /// COMPRESS_ZIP, COMPRESS_ACTIVE_MASK or COMPRESS_BLOSC)
249 /// @throw IoError if @a compression is COMPRESS_BLOSC but OpenVDB was compiled
250 /// without Blosc support.
251 /// @details This default implementation is instantiated only for types
252 /// whose size can be determined by the sizeof() operator.
253 template<typename T>
254 inline void
255 writeData(std::ostream &os, const T *data, Index count, uint32_t compression)
256 {
257  if (compression & COMPRESS_BLOSC) {
258  bloscToStream(os, reinterpret_cast<const char*>(data), sizeof(T), count);
259  } else if (compression & COMPRESS_ZIP) {
260  zipToStream(os, reinterpret_cast<const char*>(data), sizeof(T) * count);
261  } else {
262  os.write(reinterpret_cast<const char*>(data), sizeof(T) * count);
263  }
264 }
265 
266 /// Specialization for std::string output
267 template<>
268 inline void
269 writeData<std::string>(std::ostream& os, const std::string* data, Index count,
270  uint32_t /*compression*/) ///< @todo add compression
271 {
272  for (Index i = 0; i < count; ++i) {
273  const size_t len = data[i].size();
274  os << len;
275  os.write(data[i].c_str(), len+1);
276  //os.write(&(data[i][0]), len );
277  }
278 }
279 
280 /// HalfWriter wraps a static function, write(), that is analogous to writeData(), above,
281 /// except that it is partially specialized for floating-point types in order to quantize
282 /// floating-point values to 16-bit half float. A wrapper class is required because
283 /// only classes, not functions, can be partially specialized.
284 template<bool IsReal, typename T> struct HalfWriter;
285 /// Partial specialization for non-floating-point types (no float to half quantization)
286 template<typename T>
287 struct HalfWriter</*IsReal=*/false, T> {
288  static inline void write(std::ostream& os, const T* data, Index count, uint32_t compression) {
289  writeData(os, data, count, compression);
290  }
291 };
292 /// Partial specialization for floating-point types
293 template<typename T>
294 struct HalfWriter</*IsReal=*/true, T> {
295  using HalfT = typename RealToHalf<T>::HalfT;
296  static inline void write(std::ostream& os, const T* data, Index count, uint32_t compression) {
297  if (count < 1) return;
298  // Convert full float values to half float, then output the half float array.
299  std::vector<HalfT> halfData(count);
300  for (Index i = 0; i < count; ++i) halfData[i] = RealToHalf<T>::convert(data[i]);
301  writeData<HalfT>(os, reinterpret_cast<const HalfT*>(&halfData[0]), count, compression);
302  }
303 };
304 #ifdef _MSC_VER
305 /// Specialization to avoid double to float warnings in MSVC
306 template<>
307 struct HalfWriter</*IsReal=*/true, double> {
308  using HalfT = RealToHalf<double>::HalfT;
309  static inline void write(std::ostream& os, const double* data, Index count,
310  uint32_t compression)
311  {
312  if (count < 1) return;
313  // Convert full float values to half float, then output the half float array.
314  std::vector<HalfT> halfData(count);
315  for (Index i = 0; i < count; ++i) halfData[i] = RealToHalf<double>::convert(data[i]);
316  writeData<HalfT>(os, reinterpret_cast<const HalfT*>(&halfData[0]), count, compression);
317  }
318 };
319 #endif // _MSC_VER
320 
321 
322 ////////////////////////////////////////
323 
324 
325 /// Populate the given buffer with @a destCount values of type @c ValueT
326 /// read from the given stream, taking into account that the stream might
327 /// have been compressed via one of several supported schemes.
328 /// [Mainly for internal use]
329 /// @param is a stream from which to read data (possibly compressed,
330 /// depending on the stream's compression settings)
331 /// @param destBuf a buffer into which to read values of type @c ValueT
332 /// @param destCount the number of values to be stored in the buffer
333 /// @param valueMask a bitmask (typically, a node's value mask) indicating
334 /// which positions in the buffer correspond to active values
335 /// @param fromHalf if true, read 16-bit half floats from the input stream
336 /// and convert them to full floats
337 template<typename ValueT, typename MaskT>
338 inline void
339 readCompressedValues(std::istream& is, ValueT* destBuf, Index destCount,
340  const MaskT& valueMask, bool fromHalf)
341 {
342  // Get the stream's compression settings.
343  const uint32_t compression = getDataCompression(is);
344  const bool maskCompressed = compression & COMPRESS_ACTIVE_MASK;
345 
346  const bool seek = (destBuf == nullptr);
347  assert(!seek || (!getStreamMetadataPtr(is) || getStreamMetadataPtr(is)->seekable()));
348 
349  int8_t metadata = NO_MASK_AND_ALL_VALS;
351  // Read the flag that specifies what, if any, additional metadata
352  // (selection mask and/or inactive value(s)) is saved.
353  if (seek && !maskCompressed) {
354  is.seekg(/*bytes=*/1, std::ios_base::cur);
355  } else {
356  is.read(reinterpret_cast<char*>(&metadata), /*bytes=*/1);
357  }
358  }
359 
360  ValueT background = zeroVal<ValueT>();
361  if (const void* bgPtr = getGridBackgroundValuePtr(is)) {
362  background = *static_cast<const ValueT*>(bgPtr);
363  }
364  ValueT inactiveVal1 = background;
365  ValueT inactiveVal0 =
366  ((metadata == NO_MASK_OR_INACTIVE_VALS) ? background : math::negative(background));
367 
368  if (metadata == NO_MASK_AND_ONE_INACTIVE_VAL ||
369  metadata == MASK_AND_ONE_INACTIVE_VAL ||
370  metadata == MASK_AND_TWO_INACTIVE_VALS)
371  {
372  // Read one of at most two distinct inactive values.
373  if (seek) {
374  is.seekg(/*bytes=*/sizeof(ValueT), std::ios_base::cur);
375  } else {
376  is.read(reinterpret_cast<char*>(&inactiveVal0), /*bytes=*/sizeof(ValueT));
377  }
378  if (metadata == MASK_AND_TWO_INACTIVE_VALS) {
379  // Read the second of two distinct inactive values.
380  if (seek) {
381  is.seekg(/*bytes=*/sizeof(ValueT), std::ios_base::cur);
382  } else {
383  is.read(reinterpret_cast<char*>(&inactiveVal1), /*bytes=*/sizeof(ValueT));
384  }
385  }
386  }
387 
388  MaskT selectionMask;
389  if (metadata == MASK_AND_NO_INACTIVE_VALS ||
390  metadata == MASK_AND_ONE_INACTIVE_VAL ||
391  metadata == MASK_AND_TWO_INACTIVE_VALS)
392  {
393  // For use in mask compression (only), read the bitmask that selects
394  // between two distinct inactive values.
395  if (seek) {
396  is.seekg(/*bytes=*/selectionMask.memUsage(), std::ios_base::cur);
397  } else {
398  selectionMask.load(is);
399  }
400  }
401 
402  ValueT* tempBuf = destBuf;
403  std::unique_ptr<ValueT[]> scopedTempBuf;
404 
405  Index tempCount = destCount;
406 
407  if (maskCompressed && metadata != NO_MASK_AND_ALL_VALS
409  {
410  tempCount = valueMask.countOn();
411  if (!seek && tempCount != destCount) {
412  // If this node has inactive voxels, allocate a temporary buffer
413  // into which to read just the active values.
414  scopedTempBuf.reset(new ValueT[tempCount]);
415  tempBuf = scopedTempBuf.get();
416  }
417  }
418 
419  // Read in the buffer.
420  if (fromHalf) {
422  is, (seek ? nullptr : tempBuf), tempCount, compression);
423  } else {
424  readData<ValueT>(is, (seek ? nullptr : tempBuf), tempCount, compression);
425  }
426 
427  // If mask compression is enabled and the number of active values read into
428  // the temp buffer is smaller than the size of the destination buffer,
429  // then there are missing (inactive) values.
430  if (!seek && maskCompressed && tempCount != destCount) {
431  // Restore inactive values, using the background value and, if available,
432  // the inside/outside mask. (For fog volumes, the destination buffer is assumed
433  // to be initialized to background value zero, so inactive values can be ignored.)
434  for (Index destIdx = 0, tempIdx = 0; destIdx < MaskT::SIZE; ++destIdx) {
435  if (valueMask.isOn(destIdx)) {
436  // Copy a saved active value into this node's buffer.
437  destBuf[destIdx] = tempBuf[tempIdx];
438  ++tempIdx;
439  } else {
440  // Reconstruct an unsaved inactive value and copy it into this node's buffer.
441  destBuf[destIdx] = (selectionMask.isOn(destIdx) ? inactiveVal1 : inactiveVal0);
442  }
443  }
444  }
445 }
446 
447 
448 /// Write @a srcCount values of type @c ValueT to the given stream, optionally
449 /// after compressing the values via one of several supported schemes.
450 /// [Mainly for internal use]
451 /// @param os a stream to which to write data (possibly compressed, depending
452 /// on the stream's compression settings)
453 /// @param srcBuf a buffer containing values of type @c ValueT to be written
454 /// @param srcCount the number of values stored in the buffer
455 /// @param valueMask a bitmask (typically, a node's value mask) indicating
456 /// which positions in the buffer correspond to active values
457 /// @param childMask a bitmask (typically, a node's child mask) indicating
458 /// which positions in the buffer correspond to child node pointers
459 /// @param toHalf if true, convert floating-point values to 16-bit half floats
460 template<typename ValueT, typename MaskT>
461 inline void
462 writeCompressedValues(std::ostream& os, ValueT* srcBuf, Index srcCount,
463  const MaskT& valueMask, const MaskT& childMask, bool toHalf)
464 {
465  struct Local {
466  // Comparison function for values
467  static inline bool eq(const ValueT& a, const ValueT& b) {
468  return math::isExactlyEqual(a, b);
469  }
470  };
471 
472  // Get the stream's compression settings.
473  const uint32_t compress = getDataCompression(os);
474  const bool maskCompress = compress & COMPRESS_ACTIVE_MASK;
475 
476  Index tempCount = srcCount;
477  ValueT* tempBuf = srcBuf;
478  std::unique_ptr<ValueT[]> scopedTempBuf;
479 
480  int8_t metadata = NO_MASK_AND_ALL_VALS;
481 
482  if (!maskCompress) {
483  os.write(reinterpret_cast<const char*>(&metadata), /*bytes=*/1);
484  } else {
485  // A valid level set's inactive values are either +background (outside)
486  // or -background (inside), and a fog volume's inactive values are all zero.
487  // Rather than write out all of these values, we can store just the active values
488  // (given that the value mask specifies their positions) and, if necessary,
489  // an inside/outside bitmask.
490 
491  const ValueT zero = zeroVal<ValueT>();
492  ValueT background = zero;
493  if (const void* bgPtr = getGridBackgroundValuePtr(os)) {
494  background = *static_cast<const ValueT*>(bgPtr);
495  }
496 
497  /// @todo Consider all values, not just inactive values?
498  ValueT inactiveVal[2] = { background, background };
499  int numUniqueInactiveVals = 0;
500  for (typename MaskT::OffIterator it = valueMask.beginOff();
501  numUniqueInactiveVals < 3 && it; ++it)
502  {
503  const Index32 idx = it.pos();
504 
505  // Skip inactive values that are actually child node pointers.
506  if (childMask.isOn(idx)) continue;
507 
508  const ValueT& val = srcBuf[idx];
509  const bool unique = !(
510  (numUniqueInactiveVals > 0 && Local::eq(val, inactiveVal[0])) ||
511  (numUniqueInactiveVals > 1 && Local::eq(val, inactiveVal[1]))
512  );
513  if (unique) {
514  if (numUniqueInactiveVals < 2) inactiveVal[numUniqueInactiveVals] = val;
515  ++numUniqueInactiveVals;
516  }
517  }
518 
519  metadata = NO_MASK_OR_INACTIVE_VALS;
520 
521  if (numUniqueInactiveVals == 1) {
522  if (!Local::eq(inactiveVal[0], background)) {
523  if (Local::eq(inactiveVal[0], math::negative(background))) {
524  metadata = NO_MASK_AND_MINUS_BG;
525  } else {
526  metadata = NO_MASK_AND_ONE_INACTIVE_VAL;
527  }
528  }
529  } else if (numUniqueInactiveVals == 2) {
530  metadata = NO_MASK_OR_INACTIVE_VALS;
531  if (!Local::eq(inactiveVal[0], background) && !Local::eq(inactiveVal[1], background)) {
532  // If neither inactive value is equal to the background, both values
533  // need to be saved, along with a mask that selects between them.
534  metadata = MASK_AND_TWO_INACTIVE_VALS;
535 
536  } else if (Local::eq(inactiveVal[1], background)) {
537  if (Local::eq(inactiveVal[0], math::negative(background))) {
538  // If the second inactive value is equal to the background and
539  // the first is equal to -background, neither value needs to be saved,
540  // but save a mask that selects between -background and +background.
541  metadata = MASK_AND_NO_INACTIVE_VALS;
542  } else {
543  // If the second inactive value is equal to the background, only
544  // the first value needs to be saved, along with a mask that selects
545  // between it and the background.
546  metadata = MASK_AND_ONE_INACTIVE_VAL;
547  }
548  } else if (Local::eq(inactiveVal[0], background)) {
549  if (Local::eq(inactiveVal[1], math::negative(background))) {
550  // If the first inactive value is equal to the background and
551  // the second is equal to -background, neither value needs to be saved,
552  // but save a mask that selects between -background and +background.
553  metadata = MASK_AND_NO_INACTIVE_VALS;
554  std::swap(inactiveVal[0], inactiveVal[1]);
555  } else {
556  // If the first inactive value is equal to the background, swap it
557  // with the second value and save only that value, along with a mask
558  // that selects between it and the background.
559  std::swap(inactiveVal[0], inactiveVal[1]);
560  metadata = MASK_AND_ONE_INACTIVE_VAL;
561  }
562  }
563  } else if (numUniqueInactiveVals > 2) {
564  metadata = NO_MASK_AND_ALL_VALS;
565  }
566 
567  os.write(reinterpret_cast<const char*>(&metadata), /*bytes=*/1);
568 
569  if (metadata == NO_MASK_AND_ONE_INACTIVE_VAL ||
570  metadata == MASK_AND_ONE_INACTIVE_VAL ||
571  metadata == MASK_AND_TWO_INACTIVE_VALS)
572  {
573  if (!toHalf) {
574  // Write one of at most two distinct inactive values.
575  os.write(reinterpret_cast<const char*>(&inactiveVal[0]), sizeof(ValueT));
576  if (metadata == MASK_AND_TWO_INACTIVE_VALS) {
577  // Write the second of two distinct inactive values.
578  os.write(reinterpret_cast<const char*>(&inactiveVal[1]), sizeof(ValueT));
579  }
580  } else {
581  // Write one of at most two distinct inactive values.
582  ValueT truncatedVal = static_cast<ValueT>(truncateRealToHalf(inactiveVal[0]));
583  os.write(reinterpret_cast<const char*>(&truncatedVal), sizeof(ValueT));
584  if (metadata == MASK_AND_TWO_INACTIVE_VALS) {
585  // Write the second of two distinct inactive values.
586  truncatedVal = truncateRealToHalf(inactiveVal[1]);
587  os.write(reinterpret_cast<const char*>(&truncatedVal), sizeof(ValueT));
588  }
589  }
590  }
591 
592  if (metadata == NO_MASK_AND_ALL_VALS) {
593  // If there are more than two unique inactive values, the entire input buffer
594  // needs to be saved (both active and inactive values).
595  /// @todo Save the selection mask as long as most of the inactive values
596  /// are one of two values?
597  } else {
598  // Create a new array to hold just the active values.
599  scopedTempBuf.reset(new ValueT[srcCount]);
600  tempBuf = scopedTempBuf.get();
601 
602  if (metadata == NO_MASK_OR_INACTIVE_VALS ||
603  metadata == NO_MASK_AND_MINUS_BG ||
604  metadata == NO_MASK_AND_ONE_INACTIVE_VAL)
605  {
606  // Copy active values to the contiguous array.
607  tempCount = 0;
608  for (typename MaskT::OnIterator it = valueMask.beginOn(); it; ++it, ++tempCount) {
609  tempBuf[tempCount] = srcBuf[it.pos()];
610  }
611  } else {
612  // Copy active values to a new, contiguous array and populate a bitmask
613  // that selects between two distinct inactive values.
614  MaskT selectionMask;
615  tempCount = 0;
616  for (Index srcIdx = 0; srcIdx < srcCount; ++srcIdx) {
617  if (valueMask.isOn(srcIdx)) { // active value
618  tempBuf[tempCount] = srcBuf[srcIdx];
619  ++tempCount;
620  } else { // inactive value
621  if (Local::eq(srcBuf[srcIdx], inactiveVal[1])) {
622  selectionMask.setOn(srcIdx); // inactive value 1
623  } // else inactive value 0
624  }
625  }
626  assert(tempCount == valueMask.countOn());
627 
628  // Write out the mask that selects between two inactive values.
629  selectionMask.save(os);
630  }
631  }
632  }
633 
634  // Write out the buffer.
635  if (toHalf) {
636  HalfWriter<RealToHalf<ValueT>::isReal, ValueT>::write(os, tempBuf, tempCount, compress);
637  } else {
638  writeData(os, tempBuf, tempCount, compress);
639  }
640 }
641 
642 } // namespace io
643 } // namespace OPENVDB_VERSION_NAME
644 } // namespace openvdb
645 
646 #endif // OPENVDB_IO_COMPRESSION_HAS_BEEN_INCLUDED
647 
648 // Copyright (c) 2012-2018 DreamWorks Animation LLC
649 // All rights reserved. This software is distributed under the
650 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
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...
bool isExactlyEqual(const T0 &a, const T1 &b)
Return true if a is exactly equal to b.
Definition: Math.h:395
void writeData(std::ostream &os, const T *data, Index count, uint32_t compression)
Definition: Compression.h:255
T negative(const T &val)
Return the unary negation of the given value.
Definition: Math.h:108
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:1519
GLsizei const GLchar *const * string
Definition: glcorearb.h:813
OPENVDB_API void unzipFromStream(std::istream &, char *data, size_t numBytes)
void writeCompressedValues(std::ostream &os, ValueT *srcBuf, Index srcCount, const MaskT &valueMask, const MaskT &childMask, bool toHalf)
Definition: Compression.h:462
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.
png_infop png_color_16p * background
Definition: png.h:2326
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1221
GLbitfield flags
Definition: glcorearb.h:1595
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:189
static void read(std::istream &is, T *data, Index count, uint32_t compression)
Definition: Compression.h:220
void read(T &in, bool &v)
Definition: ImfXdr.h:611
GLuint buffer
Definition: glcorearb.h:659
png_uint_32 i
Definition: png.h:2877
OPENVDB_API void bloscFromStream(std::istream &, char *data, size_t numBytes)
void readCompressedValues(std::istream &is, ValueT *destBuf, Index destCount, const MaskT &valueMask, bool fromHalf)
Definition: Compression.h:339
#define OPENVDB_API
Helper macros for defining library symbol visibility.
Definition: Platform.h:194
T truncateRealToHalf(const T &val)
Return the given value truncated to 16-bit float precision.
Definition: Compression.h:153
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
GLboolean * data
Definition: glcorearb.h:130
GLboolean GLboolean GLboolean b
Definition: glcorearb.h:1221
GLint GLsizei count
Definition: glcorearb.h:404
OPENVDB_API void bloscToStream(std::ostream &, const char *data, size_t valSize, size_t numVals)
RealToHalf and its specializations define a mapping from floating-point data types to analogous half ...
Definition: Compression.h:110
GLuint GLfloat * val
Definition: glcorearb.h:1607
OPENVDB_API void zipToStream(std::ostream &, const char *data, size_t numBytes)
math::Vec3< half > Vec3H
Definition: Types.h:82
void readData(std::istream &is, T *data, Index count, uint32_t compression)
Read data from a stream.
Definition: Compression.h:179
OPENVDB_API SharedPtr< StreamMetadata > getStreamMetadataPtr(std::ios_base &)
Return a shared pointer to an object that stores metadata (file format, compression scheme...
#define SIZE
Definition: simple.C:40
static void read(std::istream &is, T *data, Index count, uint32_t compression)
Definition: Compression.h:228
void write(T &out, bool v)
Definition: ImfXdr.h:332
static void write(std::ostream &os, const T *data, Index count, uint32_t compression)
Definition: Compression.h:296
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:135
Definition: half.h:91
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:288
math::Vec2< half > Vec2H
Definition: Types.h:73