| // 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 |