@@ -23,21 +23,26 @@ template <typename TreeT>
2323struct WriteBuffersOp
2424{
2525 using LeafT = typename TreeT::LeafNodeType;
26+ using ValueT = typename TreeT::ValueType;
2627
27- WriteBuffersOp (std::ostream& _os, bool _saveFloatAsHalf)
28+ WriteBuffersOp (std::ostream& _os, bool _saveFloatAsHalf, const ValueT& _background )
2829 : os(_os)
29- , saveFloatAsHalf(_saveFloatAsHalf) { }
30+ , saveFloatAsHalf(_saveFloatAsHalf)
31+ , background(_background) { }
3032
3133 template <typename NodeT>
3234 void operator ()(const NodeT&, size_t ) { }
3335
3436 void operator ()(const LeafT& leaf, size_t )
3537 {
36- writeScalarLeafBuffers (leaf, os, saveFloatAsHalf);
38+ // Pass the background explicitly so leaf compression does not depend on
39+ // the stream's background pointer (which the codec path no longer sets).
40+ writeScalarLeafBuffers (leaf, os, saveFloatAsHalf, &background);
3741 }
3842
3943 std::ostream& os;
4044 const bool saveFloatAsHalf;
45+ const ValueT& background;
4146}; // struct WriteBuffersOp
4247
4348
@@ -48,13 +53,15 @@ struct ReadBuffersOp
4853 using LeafT = typename TreeT::LeafNodeType;
4954 using ValueT = typename TreeT::ValueType;
5055 using StorageLeafT = typename StorageTreeT::LeafNodeType;
56+ using StorageValueT = typename StorageTreeT::ValueType;
5157
5258 ReadBuffersOp (std::istream& _is, bool _saveFloatAsHalf, const ValueT& _background,
53- const CoordBBox* _clipBBox)
59+ const CoordBBox* _clipBBox, const StorageValueT* _storageBackground = nullptr )
5460 : is(_is)
5561 , saveFloatAsHalf(_saveFloatAsHalf)
5662 , background(_background)
57- , clipBBox(_clipBBox) { }
63+ , clipBBox(_clipBBox)
64+ , storageBackground(_storageBackground) { }
5865
5966 void operator ()(RootT& root, size_t )
6067 {
@@ -73,20 +80,22 @@ struct ReadBuffersOp
7380
7481 void operator ()(LeafT& leaf, size_t )
7582 {
76- readScalarLeafBuffers<LeafT, StorageLeafT>(leaf, is, saveFloatAsHalf, background, /* skip=*/ false , clipBBox);
83+ readScalarLeafBuffers<LeafT, StorageLeafT>(leaf, is, saveFloatAsHalf, background, /* skip=*/ false , clipBBox, storageBackground );
7784 }
7885
7986 std::istream& is;
8087 const bool saveFloatAsHalf;
8188 const ValueT& background;
8289 const CoordBBox* clipBBox = nullptr ;
90+ const StorageValueT* storageBackground = nullptr ;
8391}; // struct ReadBuffersOp
8492
8593
8694// Free-standing function for both standard and conversion codec cases
8795// Uses StorageGridT = GridT by default, but allows different storage type for conversions
8896template <typename GridT, typename StorageGridT = GridT>
89- void scalarCodecReadBuffers (GridT& grid, std::istream& is, const io::ReadOptions& options)
97+ void scalarCodecReadBuffers (GridT& grid, std::istream& is, const io::ReadOptions& options,
98+ const typename StorageGridT::TreeType::ValueType* storageBackground)
9099{
91100 if (grid.hasMultiPassIO ()) {
92101 OPENVDB_THROW (IoError, " Multi-pass IO is not supported in ScalarCodec" );
@@ -109,7 +118,7 @@ void scalarCodecReadBuffers(GridT& grid, std::istream& is, const io::ReadOptions
109118
110119 // Works for both standard (TreeT == StorageTreeT) and conversion cases
111120 ReadBuffersOp<TreeT, StorageTreeT> readBuffersOp (is, saveFloatAsHalf, tree.background (),
112- clipIndexBBox.get ());
121+ clipIndexBBox.get (), storageBackground );
113122 tools::visitNodesDepthFirst (grid.tree (), readBuffersOp, /* idx=*/ 0 , /* topDown=*/ false );
114123}
115124
@@ -123,7 +132,7 @@ void scalarCodecWriteBuffers(const GridT& grid, std::ostream& os)
123132 OPENVDB_THROW (IoError, " Multi-pass IO is not supported in ScalarCodec" );
124133 }
125134
126- WriteBuffersOp<TreeType> writeBuffersOp (os, grid.saveFloatAsHalf ());
135+ WriteBuffersOp<TreeType> writeBuffersOp (os, grid.saveFloatAsHalf (), grid. tree (). background () );
127136 tools::visitNodesDepthFirst (grid.tree (), writeBuffersOp);
128137}
129138
@@ -150,18 +159,25 @@ struct ScalarCodec final: public TopologyCodec<GridT, StorageGridT, Mode>
150159 }
151160 }
152161
153- void readBuffers (std::istream& is, io::CodecData& data, const io::ReadOptions& options, io::ReadDiagnostics&) final
162+ void readBuffers (std::istream& is, Index64 /* size */ , io::CodecData& data, const io::ReadOptions& options, io::ReadDiagnostics&) final
154163 {
164+ using StorageValueT = typename StorageGridT::TreeType::ValueType;
155165 GridT& grid = static_cast <GridT&>(*data.grid );
156- internal::scalarCodecReadBuffers<GridT, StorageGridT>(grid, is, options);
166+ auto & topoData = static_cast <TopologyCodecData<StorageValueT>&>(data);
167+ internal::scalarCodecReadBuffers<GridT, StorageGridT>(grid, is, options, &topoData.storageBackground );
157168 }
158169
159170 void writeBuffers (std::ostream& os, const GridBase& gridBase, const io::WriteOptions&) final
160171 {
161- if constexpr (Mode == io::CodecMode::ReadOnly) return ;
162-
163- const GridT& grid = static_cast <const GridT&>(gridBase);
164- internal::scalarCodecWriteBuffers (grid, os);
172+ // Note: the write body must live inside the negated if constexpr branch
173+ // so it is not instantiated for read-only codecs. A bare
174+ // `if constexpr (Mode == ReadOnly) return;` would still instantiate the
175+ // code that follows, which fails to compile for the scalar-to-mask/bool
176+ // convert codecs (their leaf buffers expose WordType*, not ValueType*).
177+ if constexpr (Mode != io::CodecMode::ReadOnly) {
178+ const GridT& grid = static_cast <const GridT&>(gridBase);
179+ internal::scalarCodecWriteBuffers (grid, os);
180+ }
165181 }
166182}; // struct ScalarCodec
167183
0 commit comments