// 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/codec.h" #include "src/base/uint128.h" #include "src/decoder/logical_astc_block.h" #include "src/decoder/physical_astc_block.h" #include namespace astc_codec { namespace { static constexpr size_t kBytesPerPixelUNORM8 = 4; } bool DecompressToImage(const uint8_t* astc_data, size_t astc_data_size, size_t width, size_t height, Footprint footprint, uint8_t* out_buffer, size_t out_buffer_size, size_t out_buffer_stride) { const size_t block_width = footprint.Width(); const size_t block_height = footprint.Height(); assert(block_width != 0); assert(block_height != 0); if (width == 0 || height == 0) { return false; } const size_t blocks_wide = (width + block_width - 1) / block_width; assert(blocks_wide != 0); // Check that this buffer has the expected number of blocks. const size_t expected_block_count = ((width + block_width - 1) / block_width) * ((height + block_height - 1) / block_height); if (astc_data_size % PhysicalASTCBlock::kSizeInBytes != 0 || astc_data_size / PhysicalASTCBlock::kSizeInBytes != expected_block_count) { // TODO(google): Expose error? return false; } if (kBytesPerPixelUNORM8 * width > out_buffer_stride || out_buffer_stride * height < out_buffer_size) { // Output buffer too small. return false; } base::UInt128 block; static_assert(sizeof(block) == PhysicalASTCBlock::kSizeInBytes, "Block size mismatch"); for (size_t i = 0; i < astc_data_size; i += PhysicalASTCBlock::kSizeInBytes) { const size_t block_index = i / PhysicalASTCBlock::kSizeInBytes; const size_t block_x = block_index % blocks_wide; const size_t block_y = block_index / blocks_wide; memcpy(&block, astc_data + i, sizeof(block)); PhysicalASTCBlock physical_block(block); auto lb = UnpackLogicalBlock(footprint, physical_block); if (!lb) { return false; } LogicalASTCBlock logical_block = lb.value(); for (size_t y = 0; y < block_height; ++y) { const size_t py = block_height * block_y + y; uint8_t* out_row = out_buffer + py * out_buffer_stride; for (size_t x = 0; x < block_width; ++x) { const size_t px = block_width * block_x + x; // Skip out of bounds. if (px >= width || py >= height) { continue; } uint8_t* pixel = out_row + px * kBytesPerPixelUNORM8; const RgbaColor decoded_color = logical_block.ColorAt(x, y); for (size_t i = 0; i < kBytesPerPixelUNORM8; ++i) { pixel[i] = static_cast(decoded_color[i]); } } } } return true; } bool DecompressToImage(const ASTCFile& file, uint8_t* out_buffer, size_t out_buffer_size, size_t out_buffer_stride) { base::Optional footprint = file.GetFootprint(); if (!footprint) { return false; } return DecompressToImage( reinterpret_cast(file.GetRawBlockData().c_str()), file.GetRawBlockData().size(), file.GetWidth(), file.GetHeight(), footprint.value(), out_buffer, out_buffer_size, out_buffer_stride); } bool ASTCDecompressToRGBA(const uint8_t* astc_data, size_t astc_data_size, size_t width, size_t height, FootprintType footprint, uint8_t* out_buffer, size_t out_buffer_size, size_t out_buffer_stride) { base::Optional footprint_opt = Footprint::FromFootprintType(footprint); if (!footprint_opt) { return false; } return DecompressToImage(astc_data, astc_data_size, width, height, footprint_opt.value(), out_buffer, out_buffer_size, out_buffer_stride); } } // namespace astc_codec