// 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. // #include "draco/compression/point_cloud/point_cloud_decoder.h" #include "draco/metadata/metadata_decoder.h" namespace draco { PointCloudDecoder::PointCloudDecoder() : point_cloud_(nullptr), buffer_(nullptr), version_major_(0), version_minor_(0), options_(nullptr) {} Status PointCloudDecoder::DecodeHeader(DecoderBuffer *buffer, DracoHeader *out_header) { constexpr char kIoErrorMsg[] = "Failed to parse Draco header."; if (!buffer->Decode(out_header->draco_string, 5)) { return Status(Status::IO_ERROR, kIoErrorMsg); } if (memcmp(out_header->draco_string, "DRACO", 5) != 0) { return Status(Status::DRACO_ERROR, "Not a Draco file."); } if (!buffer->Decode(&(out_header->version_major))) { return Status(Status::IO_ERROR, kIoErrorMsg); } if (!buffer->Decode(&(out_header->version_minor))) { return Status(Status::IO_ERROR, kIoErrorMsg); } if (!buffer->Decode(&(out_header->encoder_type))) { return Status(Status::IO_ERROR, kIoErrorMsg); } if (!buffer->Decode(&(out_header->encoder_method))) { return Status(Status::IO_ERROR, kIoErrorMsg); } if (!buffer->Decode(&(out_header->flags))) { return Status(Status::IO_ERROR, kIoErrorMsg); } return OkStatus(); } Status PointCloudDecoder::DecodeMetadata() { std::unique_ptr metadata = std::unique_ptr(new GeometryMetadata()); MetadataDecoder metadata_decoder; if (!metadata_decoder.DecodeGeometryMetadata(buffer_, metadata.get())) { return Status(Status::DRACO_ERROR, "Failed to decode metadata."); } point_cloud_->AddMetadata(std::move(metadata)); return OkStatus(); } Status PointCloudDecoder::Decode(const DecoderOptions &options, DecoderBuffer *in_buffer, PointCloud *out_point_cloud) { options_ = &options; buffer_ = in_buffer; point_cloud_ = out_point_cloud; DracoHeader header; DRACO_RETURN_IF_ERROR(DecodeHeader(buffer_, &header)) // Sanity check that we are really using the right decoder (mostly for cases // where the Decode method was called manually outside of our main API. if (header.encoder_type != GetGeometryType()) { return Status(Status::DRACO_ERROR, "Using incompatible decoder for the input geometry."); } // TODO(ostava): We should check the method as well, but currently decoders // don't expose the decoding method id. version_major_ = header.version_major; version_minor_ = header.version_minor; const uint8_t max_supported_major_version = header.encoder_type == POINT_CLOUD ? kDracoPointCloudBitstreamVersionMajor : kDracoMeshBitstreamVersionMajor; const uint8_t max_supported_minor_version = header.encoder_type == POINT_CLOUD ? kDracoPointCloudBitstreamVersionMinor : kDracoMeshBitstreamVersionMinor; // Check for version compatibility. if (version_major_ < 1 || version_major_ > max_supported_major_version) { return Status(Status::UNKNOWN_VERSION, "Unknown major version."); } if (version_major_ == max_supported_major_version && version_minor_ > max_supported_minor_version) { return Status(Status::UNKNOWN_VERSION, "Unknown minor version."); } buffer_->set_bitstream_version( DRACO_BITSTREAM_VERSION(version_major_, version_minor_)); if (bitstream_version() >= DRACO_BITSTREAM_VERSION(1, 3) && (header.flags & METADATA_FLAG_MASK)) { DRACO_RETURN_IF_ERROR(DecodeMetadata()) } if (!InitializeDecoder()) { return Status(Status::DRACO_ERROR, "Failed to initialize the decoder."); } if (!DecodeGeometryData()) { return Status(Status::DRACO_ERROR, "Failed to decode geometry data."); } if (!DecodePointAttributes()) { return Status(Status::DRACO_ERROR, "Failed to decode point attributes."); } return OkStatus(); } bool PointCloudDecoder::DecodePointAttributes() { uint8_t num_attributes_decoders; if (!buffer_->Decode(&num_attributes_decoders)) { return false; } // Create all attribute decoders. This is implementation specific and the // derived classes can use any data encoded in the // PointCloudEncoder::EncodeAttributesEncoderIdentifier() call. for (int i = 0; i < num_attributes_decoders; ++i) { if (!CreateAttributesDecoder(i)) { return false; } } // Initialize all attributes decoders. No data is decoded here. for (auto &att_dec : attributes_decoders_) { if (!att_dec->Init(this, point_cloud_)) { return false; } } // Decode any data needed by the attribute decoders. for (int i = 0; i < num_attributes_decoders; ++i) { if (!attributes_decoders_[i]->DecodeAttributesDecoderData(buffer_)) { return false; } } // Create map between attribute and decoder ids. for (int i = 0; i < num_attributes_decoders; ++i) { const int32_t num_attributes = attributes_decoders_[i]->GetNumAttributes(); for (int j = 0; j < num_attributes; ++j) { int att_id = attributes_decoders_[i]->GetAttributeId(j); if (att_id >= attribute_to_decoder_map_.size()) { attribute_to_decoder_map_.resize(att_id + 1); } attribute_to_decoder_map_[att_id] = i; } } // Decode the actual attributes using the created attribute decoders. if (!DecodeAllAttributes()) { return false; } if (!OnAttributesDecoded()) { return false; } return true; } bool PointCloudDecoder::DecodeAllAttributes() { for (auto &att_dec : attributes_decoders_) { if (!att_dec->DecodeAttributes(buffer_)) { return false; } } return true; } const PointAttribute *PointCloudDecoder::GetPortableAttribute( int32_t parent_att_id) { if (parent_att_id < 0 || parent_att_id >= point_cloud_->num_attributes()) { return nullptr; } const int32_t parent_att_decoder_id = attribute_to_decoder_map_[parent_att_id]; return attributes_decoders_[parent_att_decoder_id]->GetPortableAttribute( parent_att_id); } } // namespace draco