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/logical_astc_block.h"
16 #include "src/decoder/endpoint_codec.h"
17 #include "src/decoder/footprint.h"
18 #include "src/decoder/integer_sequence_codec.h"
19 #include "src/decoder/quantization.h"
20 #include "src/decoder/weight_infill.h"
21
22 namespace astc_codec {
23
24 namespace {
25
GenerateSinglePartition(Footprint footprint)26 Partition GenerateSinglePartition(Footprint footprint) {
27 return Partition { footprint, /* num_parts = */ 1, /* partition_id = */ 0,
28 std::vector<int>(footprint.NumPixels(), 0) };
29 }
30
DecodeEndpoints(const IntermediateBlockData & block)31 static std::vector<EndpointPair> DecodeEndpoints(const IntermediateBlockData& block) {
32 const int endpoint_range = block.endpoint_range
33 ? block.endpoint_range.value()
34 : EndpointRangeForBlock(block);
35 assert(endpoint_range > 0);
36
37 std::vector<EndpointPair> endpoints;
38 for (const auto& eps : block.endpoints) {
39 RgbaColor decmp_one_rgba, decmp_two_rgba;
40 DecodeColorsForMode(eps.colors, endpoint_range, eps.mode,
41 &decmp_one_rgba, &decmp_two_rgba);
42 endpoints.emplace_back(decmp_one_rgba, decmp_two_rgba);
43 }
44 return endpoints;
45 }
46
DecodeEndpoints(const VoidExtentData & block)47 static std::vector<EndpointPair> DecodeEndpoints(const VoidExtentData& block) {
48 EndpointPair eps;
49 eps.first[0] = eps.second[0] = (block.r * 255) / 65535;
50 eps.first[1] = eps.second[1] = (block.g * 255) / 65535;
51 eps.first[2] = eps.second[2] = (block.b * 255) / 65535;
52 eps.first[3] = eps.second[3] = (block.a * 255) / 65535;
53
54 std::vector<EndpointPair> endpoints;
55 endpoints.emplace_back(eps);
56 return endpoints;
57 }
58
ComputePartition(const Footprint & footprint,const IntermediateBlockData & block)59 Partition ComputePartition(const Footprint& footprint,
60 const IntermediateBlockData& block) {
61 if (block.partition_id) {
62 const int part_id = block.partition_id.value();
63 const size_t num_parts = block.endpoints.size();
64 return GetASTCPartition(footprint, num_parts, part_id);
65 } else {
66 return GenerateSinglePartition(footprint);
67 }
68 }
69
ComputePartition(const Footprint & footprint,const VoidExtentData &)70 Partition ComputePartition(const Footprint& footprint, const VoidExtentData&) {
71 return GenerateSinglePartition(footprint);
72 }
73
74 } // namespace
75
76 ////////////////////////////////////////////////////////////////////////////////
77
LogicalASTCBlock(const Footprint & footprint)78 LogicalASTCBlock::LogicalASTCBlock(const Footprint& footprint)
79 : endpoints_(1),
80 weights_(footprint.NumPixels(), 0),
81 partition_(GenerateSinglePartition(footprint)) { }
82
LogicalASTCBlock(const Footprint & footprint,const IntermediateBlockData & block)83 LogicalASTCBlock::LogicalASTCBlock(const Footprint& footprint,
84 const IntermediateBlockData& block)
85 : endpoints_(DecodeEndpoints(block)),
86 partition_(ComputePartition(footprint, block)) {
87 CalculateWeights(footprint, block);
88 }
89
LogicalASTCBlock(const Footprint & footprint,const VoidExtentData & block)90 LogicalASTCBlock::LogicalASTCBlock(const Footprint& footprint,
91 const VoidExtentData& block)
92 : endpoints_(DecodeEndpoints(block)),
93 partition_(ComputePartition(footprint, block)) {
94 CalculateWeights(footprint, block);
95 }
96
CalculateWeights(const Footprint & footprint,const IntermediateBlockData & block)97 void LogicalASTCBlock::CalculateWeights(const Footprint& footprint,
98 const IntermediateBlockData& block) {
99 const int grid_size_x = block.weight_grid_dim_x;
100 const int grid_size_y = block.weight_grid_dim_y;
101 const int weight_grid_size = grid_size_x * grid_size_y;
102
103 // Either we have a dual plane and we have twice as many weights as
104 // specified or we don't
105 assert(block.dual_plane_channel
106 ? block.weights.size() == weight_grid_size * 2
107 : block.weights.size() == weight_grid_size);
108
109 std::vector<int> unquantized;
110 unquantized.reserve(weight_grid_size);
111
112 // According to C.2.16, if we have dual-plane weights, then we have two
113 // weights per texel -- one adjacent to the other. Hence, we have to skip
114 // some when we decode the separate weight values.
115 const int weight_frequency = (block.dual_plane_channel) ? 2 : 1;
116 const int weight_range = block.weight_range;
117
118 for (int i = 0; i < weight_grid_size; ++i) {
119 const int weight = block.weights[i * weight_frequency];
120 unquantized.push_back(UnquantizeWeightFromRange(weight, weight_range));
121 }
122 weights_ = InfillWeights(unquantized, footprint, grid_size_x, grid_size_y);
123
124 if (block.dual_plane_channel) {
125 SetDualPlaneChannel(block.dual_plane_channel.value());
126 for (int i = 0; i < weight_grid_size; ++i) {
127 const int weight = block.weights[i * weight_frequency + 1];
128 unquantized[i] = UnquantizeWeightFromRange(weight, weight_range);
129 }
130 dual_plane_->weights =
131 InfillWeights(unquantized, footprint, grid_size_x, grid_size_y);
132 }
133 }
134
CalculateWeights(const Footprint & footprint,const VoidExtentData &)135 void LogicalASTCBlock::CalculateWeights(const Footprint& footprint,
136 const VoidExtentData&) {
137 weights_ = std::vector<int>(footprint.NumPixels(), 0);
138 }
139
SetWeightAt(int x,int y,int weight)140 void LogicalASTCBlock::SetWeightAt(int x, int y, int weight) {
141 assert(weight >= 0);
142 assert(weight <= 64);
143 weights_.at(y * GetFootprint().Width() + x) = weight;
144 }
145
WeightAt(int x,int y) const146 int LogicalASTCBlock::WeightAt(int x, int y) const {
147 return weights_.at(y * GetFootprint().Width() + x);
148 }
149
SetDualPlaneWeightAt(int channel,int x,int y,int weight)150 void LogicalASTCBlock::SetDualPlaneWeightAt(int channel, int x, int y,
151 int weight) {
152 assert(weight >= 0);
153 assert(weight <= 64);
154
155 // If it's not a dual plane, then this has no logical meaning
156 assert(IsDualPlane());
157
158 // If it is a dual plane and the passed channel matches the query, then we
159 // return the specialized weights
160 if (dual_plane_->channel == channel) {
161 dual_plane_->weights.at(y * GetFootprint().Width() + x) = weight;
162 } else {
163 // If the channel is not the special channel, then return the general weight
164 SetWeightAt(x, y, weight);
165 }
166 }
167
DualPlaneWeightAt(int channel,int x,int y) const168 int LogicalASTCBlock::DualPlaneWeightAt(int channel, int x, int y) const {
169 // If it's not a dual plane, then we just return the weight for all channels
170 if (!IsDualPlane()) {
171 // TODO(google): Log warning, Requesting dual-plane channel for a non
172 // dual-plane block!
173 return WeightAt(x, y);
174 }
175
176 // If it is a dual plane and the passed channel matches the query, then we
177 // return the specialized weights
178 if (dual_plane_->channel == channel) {
179 return dual_plane_->weights.at(y * GetFootprint().Width() + x);
180 }
181
182 // If the channel is not the special channel, then return the general weight
183 return WeightAt(x, y);
184 }
185
SetDualPlaneChannel(int channel)186 void LogicalASTCBlock::SetDualPlaneChannel(int channel) {
187 if (channel < 0) {
188 dual_plane_.clear();
189 } else if (dual_plane_) {
190 dual_plane_->channel = channel;
191 } else {
192 dual_plane_ = DualPlaneData {channel, weights_};
193 }
194 }
195
ColorAt(int x,int y) const196 RgbaColor LogicalASTCBlock::ColorAt(int x, int y) const {
197 const auto footprint = GetFootprint();
198 assert(x >= 0); assert(x < footprint.Width());
199 assert(y >= 0); assert(y < footprint.Height());
200
201 const int texel_idx = y * footprint.Width() + x;
202 const int part = partition_.assignment[texel_idx];
203 const auto& endpoints = endpoints_[part];
204
205 RgbaColor result;
206 for (int channel = 0; channel < 4; ++channel) {
207 const int weight = (dual_plane_ && dual_plane_->channel == channel)
208 ? dual_plane_->weights[texel_idx]
209 : weights_[texel_idx];
210 const int p0 = endpoints.first[channel];
211 const int p1 = endpoints.second[channel];
212
213 assert(p0 >= 0); assert(p0 < 256);
214 assert(p1 >= 0); assert(p1 < 256);
215
216 // According to C.2.19
217 const int c0 = (p0 << 8) | p0;
218 const int c1 = (p1 << 8) | p1;
219 const int c = (c0 * (64 - weight) + c1 * weight + 32) / 64;
220 // TODO(google): Handle conversion to sRGB or FP16 per C.2.19.
221 const int quantized = ((c * 255) + 32767) / 65536;
222 assert(quantized < 256);
223 result[channel] = quantized;
224 }
225
226 return result;
227 }
228
SetPartition(const Partition & p)229 void LogicalASTCBlock::SetPartition(const Partition& p) {
230 assert(p.footprint == partition_.footprint &&
231 "New partitions may not be for a different footprint");
232 partition_ = p;
233 endpoints_.resize(p.num_parts);
234 }
235
SetEndpoints(const EndpointPair & eps,int subset)236 void LogicalASTCBlock::SetEndpoints(const EndpointPair& eps, int subset) {
237 assert(subset < partition_.num_parts);
238 assert(subset < endpoints_.size());
239
240 endpoints_[subset] = eps;
241 }
242
UnpackLogicalBlock(const Footprint & footprint,const PhysicalASTCBlock & pb)243 base::Optional<LogicalASTCBlock> UnpackLogicalBlock(
244 const Footprint& footprint, const PhysicalASTCBlock& pb) {
245 if (pb.IsVoidExtent()) {
246 base::Optional<VoidExtentData> ve = UnpackVoidExtent(pb);
247 if (!ve) {
248 return {};
249 }
250
251 return LogicalASTCBlock(footprint, ve.value());
252 } else {
253 base::Optional<IntermediateBlockData> ib = UnpackIntermediateBlock(pb);
254 if (!ib) {
255 return {};
256 }
257
258 return LogicalASTCBlock(footprint, ib.value());
259 }
260 }
261
262 } // namespace astc_codec
263