diff options
Diffstat (limited to 'src/decoder/codec.cc')
-rw-r--r-- | src/decoder/codec.cc | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/src/decoder/codec.cc b/src/decoder/codec.cc new file mode 100644 index 0000000..c0f8c07 --- /dev/null +++ b/src/decoder/codec.cc @@ -0,0 +1,132 @@ +// 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 <cstring> + +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<uint8_t>(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> footprint = file.GetFootprint(); + if (!footprint) { + return false; + } + + return DecompressToImage( + reinterpret_cast<const uint8_t*>(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> 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 |