// Copyright 2016 The Draco Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_WRAP_TRANSFORM_BASE_H_ #define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_WRAP_TRANSFORM_BASE_H_ #include #include #include "draco/compression/config/compression_shared.h" #include "draco/core/macros.h" namespace draco { // PredictionSchemeWrapTransform uses the min and max bounds of the original // data to wrap stored correction values around these bounds centered at 0, // i.e., when the range of the original values O is between and // N = MAX-MIN, we can then store any correction X = O - P, as: // X + N, if X < -N / 2 // X - N, if X > N / 2 // X otherwise // To unwrap this value, the decoder then simply checks whether the final // corrected value F = P + X is out of the bounds of the input values. // All out of bounds values are unwrapped using // F + N, if F < MIN // F - N, if F > MAX // This wrapping can reduce the number of unique values, which translates to a // better entropy of the stored values and better compression rates. template class PredictionSchemeWrapTransformBase { public: PredictionSchemeWrapTransformBase() : num_components_(0), min_value_(0), max_value_(0), max_dif_(0), max_correction_(0), min_correction_(0) {} static constexpr PredictionSchemeTransformType GetType() { return PREDICTION_TRANSFORM_WRAP; } void Init(int num_components) { num_components_ = num_components; clamped_value_.resize(num_components); } bool AreCorrectionsPositive() const { return false; } inline const DataTypeT *ClampPredictedValue( const DataTypeT *predicted_val) const { for (int i = 0; i < this->num_components(); ++i) { if (predicted_val[i] > max_value_) { clamped_value_[i] = max_value_; } else if (predicted_val[i] < min_value_) { clamped_value_[i] = min_value_; } else { clamped_value_[i] = predicted_val[i]; } } return &clamped_value_[0]; } // TODO(hemmer): Consider refactoring to avoid this dummy. int quantization_bits() const { DRACO_DCHECK(false); return -1; } protected: bool InitCorrectionBounds() { const int64_t dif = static_cast(max_value_) - static_cast(min_value_); if (dif < 0 || dif >= std::numeric_limits::max()) { return false; } max_dif_ = 1 + static_cast(dif); max_correction_ = max_dif_ / 2; min_correction_ = -max_correction_; if ((max_dif_ & 1) == 0) { max_correction_ -= 1; } return true; } inline int num_components() const { return num_components_; } inline DataTypeT min_value() const { return min_value_; } inline void set_min_value(const DataTypeT &v) { min_value_ = v; } inline DataTypeT max_value() const { return max_value_; } inline void set_max_value(const DataTypeT &v) { max_value_ = v; } inline DataTypeT max_dif() const { return max_dif_; } inline DataTypeT min_correction() const { return min_correction_; } inline DataTypeT max_correction() const { return max_correction_; } private: int num_components_; DataTypeT min_value_; DataTypeT max_value_; DataTypeT max_dif_; DataTypeT max_correction_; DataTypeT min_correction_; // This is in fact just a tmp variable to avoid reallocation. mutable std::vector clamped_value_; }; } // namespace draco #endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_WRAP_TRANSFORM_BASE_H_