@@ -320,6 +320,197 @@ struct HalfReader</*IsReal=*/true, T> {
320320};
321321
322322
323+ struct ConvertingReaderBase
324+ {
325+ using Ptr = SharedPtr<ConvertingReaderBase>;
326+
327+ virtual ~ConvertingReaderBase () = default ;
328+ };
329+
330+ template <typename ValueT>
331+ struct ConvertingReader : ConvertingReaderBase
332+ {
333+ virtual void read (
334+ std::istream& is, ValueT* data, Index count, uint32_t compression,
335+ DelayedLoadMetadata* metadata = nullptr , size_t metadataOffset = size_t (0 ),
336+ bool fromHalf = false) const = 0;
337+
338+ virtual void read (std::istream& is, ValueT& data) const = 0;
339+
340+ virtual void seekElement (std::istream& is, int offset, std::ios_base::seekdir dir) const = 0;
341+
342+ static const ConvertingReader& get (std::istream& is);
343+ };
344+
345+ template <typename ValueT, typename TValueFrom>
346+ struct TypedConvertingReader : ConvertingReader<ValueT>
347+ {
348+ using HalfT = typename RealToHalf<TValueFrom>::HalfT;
349+
350+ static constexpr bool do_conversion = !std::is_same<ValueT, TValueFrom>::value;
351+ static constexpr bool from_half_flag_is_relevant = RealToHalf<TValueFrom>::isReal;
352+
353+ template <typename TRead>
354+ static void readHelper (
355+ std::istream& is, ValueT* data, Index count, uint32_t compression,
356+ DelayedLoadMetadata* metadata, size_t metadataOffset)
357+ {
358+ if constexpr (std::is_same<ValueT, TRead>::value)
359+ {
360+ // either reads or skips through compressed data (count == 0) or it
361+ // is seeking (data == nullptr)
362+ io::readData (is, data, count, compression, metadata, metadataOffset);
363+ }
364+ else
365+ {
366+ // reading
367+ if (data && count > 0 )
368+ {
369+ std::vector<TRead> buffer (count);
370+ io::readData (is, buffer.data (), count, compression, metadata, metadataOffset);
371+ std::copy (buffer.begin (), buffer.end (), data);
372+ }
373+ // either skips through compressed data (count == 0) or it is
374+ // seeking (data == nullptr)
375+ else
376+ {
377+ // reinterpret_cast<TRead*>(data) might produce not-aligned
378+ // pointer, but io::readData will not write to it, so it should
379+ // be safe. We must not skip this statement, nor we can pass
380+ // nullptr instead of data, it causes failures in the testsuite.
381+ // Now it behaves exactly as before.
382+ io::readData (is, reinterpret_cast <TRead*>(data), count,
383+ compression, metadata, metadataOffset);
384+ }
385+ }
386+ }
387+
388+ virtual void read (
389+ std::istream& is, ValueT* data, Index count, uint32_t compression,
390+ DelayedLoadMetadata* metadata = nullptr , size_t metadataOffset = size_t (0 ),
391+ bool fromHalf = false) const override
392+ {
393+ // this if-statement is here to maintain the original logic when
394+ // HalfReader<T> is being used. It can't be removed as it breaks bunch
395+ // of tests in the testsuite.
396+ if (fromHalf && from_half_flag_is_relevant && count < 1 )
397+ return ;
398+
399+ // due to std::vector<bool> being weird and not fitting the overall
400+ // templating idea here (it doesn't have data() method), the following
401+ // will fail to do non-identity conversion for bool-typed data. But for
402+ // bool-typed data there is just identity at the moment.
403+ if constexpr (from_half_flag_is_relevant)
404+ {
405+ if (fromHalf)
406+ {
407+ readHelper<HalfT>(is, data, count, compression, metadata, metadataOffset);
408+ }
409+ else
410+ {
411+ readHelper<TValueFrom>(is, data, count, compression, metadata, metadataOffset);
412+ }
413+ }
414+ else
415+ {
416+ readHelper<TValueFrom>(is, data, count, compression, metadata, metadataOffset);
417+ }
418+ }
419+
420+ virtual void read (std::istream& is, ValueT& data) const override
421+ {
422+ if constexpr (do_conversion)
423+ {
424+ TValueFrom buffer;
425+ is.read (reinterpret_cast <char *>(&buffer), /* bytes=*/ sizeof (TValueFrom));
426+ data = buffer;
427+ }
428+ else
429+ {
430+ is.read (reinterpret_cast <char *>(&data), /* bytes=*/ sizeof (ValueT));
431+ }
432+ }
433+
434+ virtual void seekElement (std::istream& is, int offset, std::ios_base::seekdir dir) const override
435+ {
436+ if constexpr (do_conversion)
437+ {
438+ is.seekg (/* bytes=*/ sizeof (TValueFrom) * offset, dir);
439+ }
440+ else
441+ {
442+ is.seekg (/* bytes=*/ sizeof (ValueT) * offset, dir);
443+ }
444+ }
445+ };
446+
447+ template <typename ValueT>
448+ inline const ConvertingReader<ValueT>& ConvertingReader<ValueT>::get(std::istream& is)
449+ {
450+ SharedPtr<io::StreamMetadata> meta = io::getStreamMetadataPtr (is);
451+
452+ auto ptr = static_cast <const ConvertingReader*>(meta->convertingReader ());
453+ if (ptr)
454+ {
455+ return *ptr;
456+ }
457+ auto ptr2 = new TypedConvertingReader<ValueT, ValueT>();
458+ meta->setConvertingReader (Ptr (ptr2));
459+ return *ptr2;
460+ }
461+
462+ struct ConvertingReaderFactory
463+ {
464+ using ReaderPtr = ConvertingReaderBase::Ptr;
465+
466+ struct FactoryEntry
467+ {
468+ ReaderPtr reader;
469+ Name new_grid_value_type;
470+ };
471+
472+ private:
473+ template <typename T1, typename T2>
474+ static Name getTypeName ()
475+ {
476+ return Name (typeNameAsString<T1>()) + Name (typeNameAsString<T2>());
477+ }
478+
479+ template <typename T1, typename T2, typename T3>
480+ static auto getMapEntry ()
481+ {
482+ return std::pair
483+ {
484+ getTypeName<T1, T2>(),
485+ FactoryEntry { ReaderPtr (new TypedConvertingReader<T3, T1>()), typeNameAsString<T3>()}
486+ };
487+ }
488+
489+ public:
490+ static FactoryEntry create (const Name& grid_value_type, const Name& desired_scalar_type)
491+ {
492+ static std::unordered_map<Name, FactoryEntry> type_map =
493+ {
494+ getMapEntry<float , Half, Half>(),
495+ getMapEntry<double , Half, Half>(),
496+ getMapEntry<Vec2f, Half, Vec2H>(),
497+ getMapEntry<Vec2d, Half, Vec2H>(),
498+ getMapEntry<Vec3f, Half, Vec3H>(),
499+ getMapEntry<Vec3d, Half, Vec3H>(),
500+ };
501+
502+ if (auto it = type_map.find (grid_value_type + desired_scalar_type); it != type_map.end ())
503+ {
504+ return it->second ;
505+ }
506+ else
507+ {
508+ return {nullptr , " " };
509+ }
510+ }
511+ };
512+
513+
323514template <typename T>
324515inline size_t
325516writeDataSize (const T *data, Index count, uint32_t compression)
@@ -474,6 +665,9 @@ readCompressedValues(std::istream& is, ValueT* destBuf, Index destCount,
474665 const bool seek = (destBuf == nullptr );
475666 OPENVDB_ASSERT (!seek || (!meta || meta->seekable ()));
476667
668+ // converting reader for reading grid values
669+ auto & convertingReader = io::ConvertingReader<ValueT>::get (is);
670+
477671 // Get delayed load metadata if it exists
478672
479673 DelayedLoadMetadata::Ptr delayLoadMeta;
@@ -512,16 +706,16 @@ readCompressedValues(std::istream& is, ValueT* destBuf, Index destCount,
512706 {
513707 // Read one of at most two distinct inactive values.
514708 if (seek) {
515- is. seekg ( /* bytes= */ sizeof (ValueT) , std::ios_base::cur);
709+ convertingReader. seekElement (is, 1 , std::ios_base::cur);
516710 } else {
517- is .read (reinterpret_cast < char *>(&inactiveVal0), /* bytes= */ sizeof (ValueT) );
711+ convertingReader .read (is, inactiveVal0 );
518712 }
519713 if (metadata == MASK_AND_TWO_INACTIVE_VALS) {
520714 // Read the second of two distinct inactive values.
521715 if (seek) {
522- is. seekg ( /* bytes= */ sizeof (ValueT) , std::ios_base::cur);
716+ convertingReader. seekElement (is, 1 , std::ios_base::cur);
523717 } else {
524- is .read (reinterpret_cast < char *>(&inactiveVal1), /* bytes= */ sizeof (ValueT) );
718+ convertingReader .read (is, inactiveVal1 );
525719 }
526720 }
527721 }
@@ -558,13 +752,8 @@ readCompressedValues(std::istream& is, ValueT* destBuf, Index destCount,
558752 }
559753
560754 // Read in the buffer.
561- if (fromHalf) {
562- HalfReader<RealToHalf<ValueT>::isReal, ValueT>::read (
563- is, (seek ? nullptr : tempBuf), tempCount, compression, delayLoadMeta.get (), leafIndex);
564- } else {
565- readData<ValueT>(
566- is, (seek ? nullptr : tempBuf), tempCount, compression, delayLoadMeta.get (), leafIndex);
567- }
755+ convertingReader.read (
756+ is, (seek ? nullptr : tempBuf), tempCount, compression, delayLoadMeta.get (), leafIndex, fromHalf);
568757
569758 // If mask compression is enabled and the number of active values read into
570759 // the temp buffer is smaller than the size of the destination buffer,
0 commit comments