aboutsummaryrefslogtreecommitdiff
path: root/src/decoder/astc_file.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/decoder/astc_file.cc')
-rw-r--r--src/decoder/astc_file.cc185
1 files changed, 185 insertions, 0 deletions
diff --git a/src/decoder/astc_file.cc b/src/decoder/astc_file.cc
new file mode 100644
index 0000000..4770064
--- /dev/null
+++ b/src/decoder/astc_file.cc
@@ -0,0 +1,185 @@
+// 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 <cstring>
+#include <fstream>
+#include <memory>
+#include <sstream>
+
+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<typename T>
+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> ASTCFile::LoadFromMemory(const char* data,
+ size_t length,
+ std::string* error) {
+ if (length < kASTCHeaderSize) {
+ *error = "Incomplete header.";
+ return nullptr;
+ }
+
+ base::Optional<Header> 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<ASTCFile>(
+ new ASTCFile(std::move(header), std::move(blocks)));
+}
+
+std::unique_ptr<ASTCFile> 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> 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<ASTCFile>(
+ new ASTCFile(std::move(header), std::move(blocks)));
+}
+
+base::Optional<Footprint> 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::Header> ASTCFile::ParseHeader(const char* header) {
+ size_t offset = 0;
+ // TODO(google): Handle endianness.
+ const uint32_t magic = ReadVal<uint32_t>(header, offset);
+ if (magic != 0x5CA1AB13) {
+ return {};
+ }
+
+ const uint32_t block_width = ReadVal<uint8_t>(header, offset);
+ const uint32_t block_height = ReadVal<uint8_t>(header, offset);
+ const uint32_t block_depth = ReadVal<uint8_t>(header, offset);
+
+ uint32_t width = 0;
+ width |= ReadVal<uint8_t>(header, offset);
+ width |= ReadVal<uint8_t>(header, offset) << 8;
+ width |= ReadVal<uint8_t>(header, offset) << 16;
+
+ uint32_t height = 0;
+ height |= ReadVal<uint8_t>(header, offset);
+ height |= ReadVal<uint8_t>(header, offset) << 8;
+ height |= ReadVal<uint8_t>(header, offset) << 16;
+
+ uint32_t depth = 0;
+ depth |= ReadVal<uint8_t>(header, offset);
+ depth |= ReadVal<uint8_t>(header, offset) << 8;
+ depth |= ReadVal<uint8_t>(header, offset) << 16;
+ assert(offset == kASTCHeaderSize);
+
+ return Header(width, height, depth, block_width, block_height, block_depth);
+}
+
+} // namespace astc_codec