1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/decoder/codec.h"
16 #include "src/base/uint128.h"
17 #include "src/decoder/logical_astc_block.h"
18 #include "src/decoder/physical_astc_block.h"
19
20 #include <cstring>
21
22 namespace astc_codec {
23
24 namespace {
25 static constexpr size_t kBytesPerPixelUNORM8 = 4;
26 }
27
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)28 bool DecompressToImage(const uint8_t* astc_data, size_t astc_data_size,
29 size_t width, size_t height, Footprint footprint,
30 uint8_t* out_buffer, size_t out_buffer_size,
31 size_t out_buffer_stride) {
32 const size_t block_width = footprint.Width();
33 const size_t block_height = footprint.Height();
34 assert(block_width != 0);
35 assert(block_height != 0);
36
37 if (width == 0 || height == 0) {
38 return false;
39 }
40
41 const size_t blocks_wide = (width + block_width - 1) / block_width;
42 assert(blocks_wide != 0);
43
44 // Check that this buffer has the expected number of blocks.
45 const size_t expected_block_count =
46 ((width + block_width - 1) / block_width) *
47 ((height + block_height - 1) / block_height);
48
49 if (astc_data_size % PhysicalASTCBlock::kSizeInBytes != 0 ||
50 astc_data_size / PhysicalASTCBlock::kSizeInBytes !=
51 expected_block_count) {
52 // TODO(google): Expose error?
53 return false;
54 }
55
56 if (kBytesPerPixelUNORM8 * width > out_buffer_stride ||
57 out_buffer_stride * height < out_buffer_size) {
58 // Output buffer too small.
59 return false;
60 }
61
62 base::UInt128 block;
63 static_assert(sizeof(block) == PhysicalASTCBlock::kSizeInBytes,
64 "Block size mismatch");
65
66 for (size_t i = 0; i < astc_data_size; i += PhysicalASTCBlock::kSizeInBytes) {
67 const size_t block_index = i / PhysicalASTCBlock::kSizeInBytes;
68 const size_t block_x = block_index % blocks_wide;
69 const size_t block_y = block_index / blocks_wide;
70 memcpy(&block, astc_data + i, sizeof(block));
71
72 PhysicalASTCBlock physical_block(block);
73 auto lb = UnpackLogicalBlock(footprint, physical_block);
74 if (!lb) {
75 return false;
76 }
77
78 LogicalASTCBlock logical_block = lb.value();
79
80 for (size_t y = 0; y < block_height; ++y) {
81 const size_t py = block_height * block_y + y;
82 uint8_t* out_row = out_buffer + py * out_buffer_stride;
83
84 for (size_t x = 0; x < block_width; ++x) {
85 const size_t px = block_width * block_x + x;
86
87 // Skip out of bounds.
88 if (px >= width || py >= height) {
89 continue;
90 }
91
92 uint8_t* pixel = out_row + px * kBytesPerPixelUNORM8;
93 const RgbaColor decoded_color = logical_block.ColorAt(x, y);
94 for (size_t i = 0; i < kBytesPerPixelUNORM8; ++i) {
95 pixel[i] = static_cast<uint8_t>(decoded_color[i]);
96 }
97 }
98 }
99 }
100
101 return true;
102 }
103
DecompressToImage(const ASTCFile & file,uint8_t * out_buffer,size_t out_buffer_size,size_t out_buffer_stride)104 bool DecompressToImage(const ASTCFile& file, uint8_t* out_buffer,
105 size_t out_buffer_size, size_t out_buffer_stride) {
106 base::Optional<Footprint> footprint = file.GetFootprint();
107 if (!footprint) {
108 return false;
109 }
110
111 return DecompressToImage(
112 reinterpret_cast<const uint8_t*>(file.GetRawBlockData().c_str()),
113 file.GetRawBlockData().size(), file.GetWidth(), file.GetHeight(),
114 footprint.value(), out_buffer, out_buffer_size, out_buffer_stride);
115 }
116
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)117 bool ASTCDecompressToRGBA(const uint8_t* astc_data, size_t astc_data_size,
118 size_t width, size_t height, FootprintType footprint,
119 uint8_t* out_buffer, size_t out_buffer_size,
120 size_t out_buffer_stride) {
121 base::Optional<Footprint> footprint_opt =
122 Footprint::FromFootprintType(footprint);
123 if (!footprint_opt) {
124 return false;
125 }
126
127 return DecompressToImage(astc_data, astc_data_size, width, height,
128 footprint_opt.value(), out_buffer, out_buffer_size,
129 out_buffer_stride);
130 }
131
132 } // namespace astc_codec
133