• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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