// 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 "io/ply_reader.h" #include #include #include "io/parser_utils.h" #include "io/ply_property_writer.h" namespace draco { PlyProperty::PlyProperty(const std::string &name, DataType data_type, DataType list_type) : name_(name), data_type_(data_type), list_data_type_(list_type) { data_type_num_bytes_ = DataTypeLength(data_type); list_data_type_num_bytes_ = DataTypeLength(list_type); } PlyElement::PlyElement(const std::string &name, int64_t num_entries) : name_(name), num_entries_(num_entries) {} PlyReader::PlyReader() : format_(kLittleEndian) {} bool PlyReader::Read(DecoderBuffer *buffer) { error_message_.clear(); std::string value; // The first line needs to by "ply". if (!parser::ParseString(buffer, &value) || value != "ply") { error_message_ = "Not a valid ply file."; return false; } parser::SkipLine(buffer); // The second line needs to be the format of the ply file. parser::ParseLine(buffer, &value); std::string format, version; const std::vector words = SplitWords(value); if (words.size() >= 3 && words[0] == "format") { format = words[1]; version = words[2]; } else { error_message_ = "Missing or wrong format line."; return false; } if (version != "1.0") { error_message_ = "Unsupported PLY version."; return false; // Wrong version. } if (format == "binary_big_endian") { error_message_ = "Unsupported format. Currently we support only ascii and" " binary_little_endian format."; return false; } if (format == "ascii") { format_ = kAscii; } else { format_ = kLittleEndian; } if (!ParseHeader(buffer)) return false; if (!ParsePropertiesData(buffer)) return false; return true; } bool PlyReader::ParseHeader(DecoderBuffer *buffer) { while (error_message_.length() == 0 && !ParseEndHeader(buffer)) { if (ParseElement(buffer)) continue; if (ParseProperty(buffer)) continue; parser::SkipLine(buffer); } if (error_message_.length() > 0) { printf("ERROR %s\n", error_message_.c_str()); return false; } return true; } bool PlyReader::ParseEndHeader(DecoderBuffer *buffer) { parser::SkipWhitespace(buffer); std::array c; if (!buffer->Peek(&c)) { error_message_ = "End of file reached before the end_header."; return false; } if (std::memcmp(&c[0], "end_header", 10) != 0) return false; parser::SkipLine(buffer); return true; } bool PlyReader::ParseElement(DecoderBuffer *buffer) { DecoderBuffer line_buffer(*buffer); std::string line; parser::ParseLine(&line_buffer, &line); std::string element_name; int64_t count; const std::vector words = SplitWords(line); if (words.size() >= 3 && words[0] == "element") { element_name = words[1]; const std::string count_str = words[2]; count = strtoll(count_str.c_str(), NULL, 10); } else { return false; } element_index_[element_name] = elements_.size(); elements_.emplace_back(PlyElement(element_name, count)); *buffer = line_buffer; return true; } bool PlyReader::ParseProperty(DecoderBuffer *buffer) { if (elements_.empty()) return false; // Ignore properties if there is no active element. DecoderBuffer line_buffer(*buffer); std::string line; parser::ParseLine(&line_buffer, &line); std::string data_type_str, list_type_str, property_name; bool property_search = false; const std::vector words = SplitWords(line); if (words.size() >= 3 && words[0] == "property" && words[1] != "list") { property_search = true; data_type_str = words[1]; property_name = words[2]; } bool property_list_search = false; if (words.size() >= 5 && words[0] == "property" && words[1] == "list") { property_list_search = true; list_type_str = words[2]; data_type_str = words[3]; property_name = words[4]; } if (!property_search && !property_list_search) { return false; } const DataType data_type = GetDataTypeFromString(data_type_str); if (data_type == DT_INVALID) { error_message_ = "Wrong property data type."; return true; // Parsed. } DataType list_type = DT_INVALID; if (property_list_search) { list_type = GetDataTypeFromString(list_type_str); if (list_type == DT_INVALID) { error_message_ = "Wrong property list type."; return true; // Parsed. } } elements_.back().AddProperty( PlyProperty(property_name, data_type, list_type)); *buffer = line_buffer; return true; } bool PlyReader::ParsePropertiesData(DecoderBuffer *buffer) { for (int i = 0; i < static_cast(elements_.size()); ++i) { if (format_ == kLittleEndian) { if (!ParseElementData(buffer, i)) { return false; } } else if (format_ == kAscii) { if (!ParseElementDataAscii(buffer, i)) { return false; } } } return true; } bool PlyReader::ParseElementData(DecoderBuffer *buffer, int element_index) { PlyElement &element = elements_[element_index]; for (int entry = 0; entry < element.num_entries(); ++entry) { for (int i = 0; i < element.num_properties(); ++i) { PlyProperty &prop = element.property(i); if (prop.is_list()) { // Parse the number of entries for the list element. int64_t num_entries = 0; buffer->Decode(&num_entries, prop.list_data_type_num_bytes()); // Store offset to the main data entry. prop.list_data_.push_back(prop.data_.size() / prop.data_type_num_bytes_); // Store the number of entries. prop.list_data_.push_back(num_entries); // Read and store the actual property data const int64_t num_bytes_to_read = prop.data_type_num_bytes() * num_entries; prop.data_.insert(prop.data_.end(), buffer->data_head(), buffer->data_head() + num_bytes_to_read); buffer->Advance(num_bytes_to_read); } else { // Non-list property prop.data_.insert(prop.data_.end(), buffer->data_head(), buffer->data_head() + prop.data_type_num_bytes()); buffer->Advance(prop.data_type_num_bytes()); } } } return true; } bool PlyReader::ParseElementDataAscii(DecoderBuffer *buffer, int element_index) { PlyElement &element = elements_[element_index]; for (int entry = 0; entry < element.num_entries(); ++entry) { for (int i = 0; i < element.num_properties(); ++i) { PlyProperty &prop = element.property(i); PlyPropertyWriter prop_writer(&prop); int32_t num_entries = 1; if (prop.is_list()) { parser::SkipWhitespace(buffer); // Parse the number of entries for the list element. if (!parser::ParseSignedInt(buffer, &num_entries)) return false; // Store offset to the main data entry. prop.list_data_.push_back(prop.data_.size() / prop.data_type_num_bytes_); // Store the number of entries. prop.list_data_.push_back(num_entries); } // Read and store the actual property data. for (int v = 0; v < num_entries; ++v) { parser::SkipWhitespace(buffer); if (prop.data_type() == DT_FLOAT32 || prop.data_type() == DT_FLOAT64) { float val; if (!parser::ParseFloat(buffer, &val)) return false; prop_writer.PushBackValue(val); } else { int32_t val; if (!parser::ParseSignedInt(buffer, &val)) return false; prop_writer.PushBackValue(val); } } } } return true; } std::vector PlyReader::SplitWords(const std::string &line) { std::vector output; std::string::size_type start = 0; std::string::size_type end = 0; // Check for isspace chars. while ((end = line.find_first_of(" \t\n\v\f\r", start)) != std::string::npos) { const std::string word(line.substr(start, end - start)); if (!std::all_of(word.begin(), word.end(), isspace)) output.push_back(word); start = end + 1; } const std::string last_word(line.substr(start)); if (!std::all_of(last_word.begin(), last_word.end(), isspace)) output.push_back(last_word); return output; } DataType PlyReader::GetDataTypeFromString(const std::string &name) const { if (name == "char" || name == "int8") return DT_INT8; if (name == "uchar" || name == "uint8") return DT_UINT8; if (name == "short" || name == "int16") return DT_INT16; if (name == "ushort" || name == "uint16") return DT_UINT16; if (name == "int" || name == "int32") return DT_INT32; if (name == "uint" || name == "uint32") return DT_UINT32; if (name == "float" || name == "float32") return DT_FLOAT32; if (name == "double" || name == "float64") { return DT_FLOAT64; } return DT_INVALID; } } // namespace draco