blob: c0f8c07b84794d060a9aaed897f03cfaf75e049f [file] [log] [blame]
// 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