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