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