// Copyright 2018 Google LLC // // 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 // // https://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 "src/decoder/astc_file.h" #include #include #include #include namespace astc_codec { namespace { static constexpr size_t kASTCHeaderSize = 16; // Reads a value of size T from the buffer at the current offset, then // increments the offset. template inline T ReadVal(const char* file_data, size_t& offset) { T x; memcpy(&x, &file_data[offset], sizeof(T)); offset += sizeof(T); return x; } } // namespace ASTCFile::ASTCFile(Header&& header, std::string&& blocks) : header_(std::move(header)), blocks_(std::move(blocks)) {} std::unique_ptr ASTCFile::LoadFromMemory(const char* data, size_t length, std::string* error) { if (length < kASTCHeaderSize) { *error = "Incomplete header."; return nullptr; } base::Optional
header_opt = ParseHeader(data); if (!header_opt) { *error = "Invalid ASTC header."; return nullptr; } Header header = header_opt.value(); if (header.block_width_ == 0 || header.block_height_ == 0) { *error = "Invalid block size."; return nullptr; } std::string blocks(data + kASTCHeaderSize, data + length); // Check that this file has the expected number of blocks. const size_t expected_block_count = ((header.width_ + header.block_width_ - 1) / header.block_width_) * ((header.height_ + header.block_height_ - 1) / header.block_height_); if (blocks.size() % PhysicalASTCBlock::kSizeInBytes != 0 || blocks.size() / PhysicalASTCBlock::kSizeInBytes != expected_block_count) { std::stringstream ss; ss << "Unexpected file length " << blocks.size() << " expected " << kASTCHeaderSize + expected_block_count * PhysicalASTCBlock::kSizeInBytes << " bytes."; *error = ss.str(); return nullptr; } return std::unique_ptr( new ASTCFile(std::move(header), std::move(blocks))); } std::unique_ptr ASTCFile::LoadFile(const std::string& path, std::string* error) { std::ifstream is(path, std::ios::binary); if (!is) { *error = "File not found: " + path; return nullptr; } char header_data[kASTCHeaderSize] = {}; if (!is.read(header_data, kASTCHeaderSize)) { *error = "Failed to load ASTC header."; return nullptr; } base::Optional
header_opt = ParseHeader(header_data); if (!header_opt) { *error = "Invalid ASTC header."; return nullptr; } Header header = header_opt.value(); std::string blocks; { std::ostringstream ss; ss << is.rdbuf(); blocks = ss.str(); } // Check that this file has the expected number of blocks. const size_t expected_block_count = ((header.width_ + header.block_width_ - 1) / header.block_width_) * ((header.height_ + header.block_height_ - 1) / header.block_height_); if (blocks.size() % PhysicalASTCBlock::kSizeInBytes != 0 || blocks.size() / PhysicalASTCBlock::kSizeInBytes != expected_block_count) { std::stringstream ss; ss << "Unexpected file length " << blocks.size() << " expected " << kASTCHeaderSize + expected_block_count * PhysicalASTCBlock::kSizeInBytes << " bytes."; *error = ss.str(); return nullptr; } return std::unique_ptr( new ASTCFile(std::move(header), std::move(blocks))); } base::Optional ASTCFile::GetFootprint() const { return Footprint::FromDimensions(header_.block_width_, header_.block_height_); } std::string ASTCFile::GetFootprintString() const { std::stringstream footprint; footprint << header_.block_width_ << "x" << header_.block_height_; return footprint.str(); } const std::string& ASTCFile::GetRawBlockData() const { return blocks_; } PhysicalASTCBlock ASTCFile::GetBlock(size_t block_idx) const { const size_t sz = PhysicalASTCBlock::kSizeInBytes; const size_t offset = PhysicalASTCBlock::kSizeInBytes * block_idx; assert(offset <= blocks_.size() - sz); return PhysicalASTCBlock(blocks_.substr(offset, sz)); } base::Optional ASTCFile::ParseHeader(const char* header) { size_t offset = 0; // TODO(google): Handle endianness. const uint32_t magic = ReadVal(header, offset); if (magic != 0x5CA1AB13) { return {}; } const uint32_t block_width = ReadVal(header, offset); const uint32_t block_height = ReadVal(header, offset); const uint32_t block_depth = ReadVal(header, offset); uint32_t width = 0; width |= ReadVal(header, offset); width |= ReadVal(header, offset) << 8; width |= ReadVal(header, offset) << 16; uint32_t height = 0; height |= ReadVal(header, offset); height |= ReadVal(header, offset) << 8; height |= ReadVal(header, offset) << 16; uint32_t depth = 0; depth |= ReadVal(header, offset); depth |= ReadVal(header, offset) << 8; depth |= ReadVal(header, offset) << 16; assert(offset == kASTCHeaderSize); return Header(width, height, depth, block_width, block_height, block_depth); } } // namespace astc_codec