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/physical_astc_block.h"
16 #include "src/base/math_utils.h"
17 #include "src/base/optional.h"
18 #include "src/base/uint128.h"
19 #include "src/decoder/integer_sequence_codec.h"
20 
21 #include <array>
22 #include <cmath>
23 
24 namespace astc_codec {
25 
26 namespace {
27 
28 static_assert(static_cast<int>(ColorEndpointMode::kNumColorEndpointModes) == 16,
29               "There are only sixteen color endpoint modes defined in the "
30               "ASTC specification. If this is false, then the enum may be "
31               "incorrect.");
32 
33 constexpr int kASTCBlockSizeBits = 128;
34 //constexpr int kASTCBlockSizeBytes = kASTCBlockSizeBits / 8;
35 constexpr uint32_t kVoidExtentMaskBits = 9;
36 constexpr uint32_t kVoidExtentMask = 0x1FC;
37 constexpr int kWeightGridMinBitLength = 24;
38 constexpr int kWeightGridMaxBitLength = 96;
39 constexpr int kMaxNumPartitions = 4;
40 constexpr int kMaxNumWeights = 64;
41 
42 // These are the overall block modes defined in table C.2.8.  There are 10
43 // weight grid encoding schemes + void extent.
44 enum class BlockMode {
45   kB4_A2,
46   kB8_A2,
47   kA2_B8,
48   kA2_B6,
49   kB2_A2,
50   k12_A2,
51   kA2_12,
52   k6_10,
53   k10_6,
54   kA6_B6,
55   kVoidExtent,
56 };
57 
58 struct WeightGridProperties {
59   int width;
60   int height;
61   int range;
62 };
63 
64 // Local function prototypes
65 base::Optional<BlockMode> DecodeBlockMode(const base::UInt128 astc_bits);
66 base::Optional<WeightGridProperties> DecodeWeightProps(
67     const base::UInt128 astc_bits, std::string* error);
68 std::array<int, 4> DecodeVoidExtentCoords(const base::UInt128 astc_bits);
69 bool DecodeDualPlaneBit(const base::UInt128 astc_bits);
70 int DecodeNumPartitions(const base::UInt128 astc_bits);
71 int DecodeNumWeightBits(const base::UInt128 astc_bits);
72 int DecodeDualPlaneBitStartPos(const base::UInt128 astc_bits);
73 ColorEndpointMode DecodeEndpointMode(const base::UInt128 astc_bits,
74                                      int partition);
75 int DecodeNumColorValues(const base::UInt128 astc_bits);
76 
77 // Returns the block mode, if it's valid.
DecodeBlockMode(const base::UInt128 astc_bits)78 base::Optional<BlockMode> DecodeBlockMode(const base::UInt128 astc_bits) {
79   using Result = base::Optional<BlockMode>;
80   const uint64_t low_bits = astc_bits.LowBits();
81   if (base::GetBits(low_bits, 0, kVoidExtentMaskBits) == kVoidExtentMask) {
82     return Result(BlockMode::kVoidExtent);
83   }
84 
85   if (base::GetBits(low_bits, 0, 2) != 0) {
86     const uint64_t mode_bits = base::GetBits(low_bits, 2, 2);
87     switch (mode_bits) {
88       case 0: return Result(BlockMode::kB4_A2);
89       case 1: return Result(BlockMode::kB8_A2);
90       case 2: return Result(BlockMode::kA2_B8);
91       case 3: return base::GetBits(low_bits, 8, 1) ?
92           Result(BlockMode::kB2_A2) : Result(BlockMode::kA2_B6);
93     }
94   } else {
95     const uint64_t mode_bits = base::GetBits(low_bits, 5, 4);
96     if ((mode_bits & 0xC) == 0x0) {
97       if (base::GetBits(low_bits, 0, 4) == 0) {
98         // Reserved.
99         return Result();
100       } else {
101         return Result(BlockMode::k12_A2);
102       }
103     } else if ((mode_bits & 0xC) == 0x4) {
104       return Result(BlockMode::kA2_12);
105     } else if (mode_bits == 0xC) {
106       return Result(BlockMode::k6_10);
107     } else if (mode_bits == 0xD) {
108       return Result(BlockMode::k10_6);
109     } else if ((mode_bits & 0xC) == 0x8) {
110       return Result(BlockMode::kA6_B6);
111     }
112   }
113 
114   return Result();
115 }
116 
DecodeWeightProps(const base::UInt128 astc_bits,std::string * error)117 base::Optional<WeightGridProperties> DecodeWeightProps(
118     const base::UInt128 astc_bits, std::string* error) {
119   auto block_mode = DecodeBlockMode(astc_bits);
120   if (!block_mode) {
121     *error = "Reserved block mode";
122     return {};
123   }
124 
125   // The dimensions of the weight grid and their range
126   WeightGridProperties props;
127 
128   // Determine the weight extents based on the block mode
129   const uint32_t low_bits =
130       static_cast<uint32_t>(astc_bits.LowBits() & 0xFFFFFFFF);
131   switch (block_mode.value()) {
132     case BlockMode::kB4_A2: {
133       int a = base::GetBits(low_bits, 5, 2);
134       int b = base::GetBits(low_bits, 7, 2);
135       props.width = b + 4;
136       props.height = a + 2;
137     }
138     break;
139 
140     case BlockMode::kB8_A2: {
141       int a = base::GetBits(low_bits, 5, 2);
142       int b = base::GetBits(low_bits, 7, 2);
143       props.width = b + 8;
144       props.height = a + 2;
145     }
146     break;
147 
148     case BlockMode::kA2_B8: {
149       int a = base::GetBits(low_bits, 5, 2);
150       int b = base::GetBits(low_bits, 7, 2);
151       props.width = a + 2;
152       props.height = b + 8;
153     }
154     break;
155 
156     case BlockMode::kA2_B6: {
157       int a = base::GetBits(low_bits, 5, 2);
158       int b = base::GetBits(low_bits, 7, 1);
159       props.width = a + 2;
160       props.height = b + 6;
161     }
162     break;
163 
164     case BlockMode::kB2_A2: {
165       int a = base::GetBits(low_bits, 5, 2);
166       int b = base::GetBits(low_bits, 7, 1);
167       props.width = b + 2;
168       props.height = a + 2;
169     }
170     break;
171 
172     case BlockMode::k12_A2: {
173       int a = base::GetBits(low_bits, 5, 2);
174       props.width = 12;
175       props.height = a + 2;
176     }
177     break;
178 
179     case BlockMode::kA2_12: {
180       int a = base::GetBits(low_bits, 5, 2);
181       props.width = a + 2;
182       props.height = 12;
183     }
184     break;
185 
186     case BlockMode::k6_10: {
187       props.width = 6;
188       props.height = 10;
189     }
190     break;
191 
192     case BlockMode::k10_6: {
193       props.width = 10;
194       props.height = 6;
195     }
196     break;
197 
198     case BlockMode::kA6_B6: {
199       int a = base::GetBits(low_bits, 5, 2);
200       int b = base::GetBits(low_bits, 9, 2);
201       props.width = a + 6;
202       props.height = b + 6;
203     }
204     break;
205 
206     // Void extent blocks have no weight grid.
207     case BlockMode::kVoidExtent:
208       *error = "Void extent block has no weight grid";
209       return {};
210 
211     // We have a valid block mode which isn't a void extent? We
212     // should be able to decode the weight grid dimensions.
213     default:
214       assert(false && "Error decoding weight grid");
215       *error = "Internal error";
216       return {};
217   }
218 
219   // Determine the weight range based on the block mode
220   uint32_t r = base::GetBits(low_bits, 4, 1);
221   switch (block_mode.value()) {
222     case BlockMode::kB4_A2:
223     case BlockMode::kB8_A2:
224     case BlockMode::kA2_B8:
225     case BlockMode::kA2_B6:
226     case BlockMode::kB2_A2: {
227       r |= base::GetBits(low_bits, 0, 2) << 1;
228     }
229     break;
230 
231     case BlockMode::k12_A2:
232     case BlockMode::kA2_12:
233     case BlockMode::k6_10:
234     case BlockMode::k10_6:
235     case BlockMode::kA6_B6:  {
236       r |= base::GetBits(low_bits, 2, 2) << 1;
237     }
238     break;
239 
240     // We have a valid block mode which doesn't have weights? We
241     // should have caught this earlier.
242     case BlockMode::kVoidExtent:
243     default:
244       assert(false && "Error decoding weight grid");
245       *error = "Internal error";
246       return {};
247   }
248 
249   // Decode the range...
250   // High bit is in bit 9 unless we're using a particular block mode
251   uint32_t h = base::GetBits(low_bits, 9, 1);
252   if (block_mode == BlockMode::kA6_B6) {
253     h = 0;
254   }
255 
256   // Figure out the range of the weights (Table C.2.7)
257   constexpr std::array<int, 16> kWeightRanges = {{
258       -1, -1, 1, 2, 3, 4, 5, 7, -1, -1, 9, 11, 15, 19, 23, 31
259     }};
260 
261   assert(((h << 3) | r) < kWeightRanges.size());
262 
263   props.range = kWeightRanges.at((h << 3) | r);
264   if (props.range < 0) {
265     *error = "Reserved range for weight bits";
266     return {};
267   }
268 
269   // Error checking -- do we have too many weights?
270   int num_weights = props.width * props.height;
271   if (DecodeDualPlaneBit(astc_bits)) {
272     num_weights *= 2;
273   }
274 
275   if (kMaxNumWeights < num_weights) {
276     *error = "Too many weights specified";
277     return {};
278   }
279 
280   // Do we have too many weight bits?
281   const int bit_count =
282       IntegerSequenceCodec::GetBitCountForRange(num_weights, props.range);
283 
284   if (bit_count < kWeightGridMinBitLength) {
285     *error = "Too few bits required for weight grid";
286     return {};
287   }
288 
289   if (kWeightGridMaxBitLength < bit_count) {
290     *error = "Too many bits required for weight grid";
291     return {};
292   }
293 
294   return props;
295 }
296 
297 // Returns the four 13-bit integers that define the range of texture
298 // coordinates present in a void extent block as defined in Section
299 // C.2.23 of the specification. The coordinates returned are of
300 // the form (min_s, max_s, min_t, max_t)
DecodeVoidExtentCoords(const base::UInt128 astc_bits)301 std::array<int, 4> DecodeVoidExtentCoords(const base::UInt128 astc_bits) {
302   const uint64_t low_bits = astc_bits.LowBits();
303 
304   std::array<int, 4> coords;
305   for (int i = 0; i < 4; ++i) {
306     coords[i] = static_cast<int>(base::GetBits(low_bits, 12 + 13 * i, 13));
307   }
308 
309   return coords;
310 }
311 
DecodeDualPlaneBit(const base::UInt128 astc_bits)312 bool DecodeDualPlaneBit(const base::UInt128 astc_bits) {
313   base::Optional<BlockMode> block_mode = DecodeBlockMode(astc_bits);
314 
315   // Void extent blocks certainly aren't dual-plane.
316   if (block_mode == BlockMode::kVoidExtent) {
317     return false;
318   }
319 
320   // One special block mode doesn't have any dual plane bit
321   if (block_mode == BlockMode::kA6_B6) {
322     return false;
323   }
324 
325   // Otherwise, dual plane is determined by the 10th bit.
326   constexpr int kDualPlaneBitPosition = 10;
327   return base::GetBits(astc_bits, kDualPlaneBitPosition, 1) != 0;
328 }
329 
DecodeNumPartitions(const base::UInt128 astc_bits)330 int DecodeNumPartitions(const base::UInt128 astc_bits) {
331   constexpr int kNumPartitionsBitPosition = 11;
332   constexpr int kNumPartitionsBitLength = 2;
333 
334   // Non-void extent blocks
335   const uint64_t low_bits = astc_bits.LowBits();
336   const int num_partitions = 1 + static_cast<int>(
337       base::GetBits(low_bits,
338                     kNumPartitionsBitPosition,
339                     kNumPartitionsBitLength));
340   assert(num_partitions > 0);
341   assert(num_partitions <= kMaxNumPartitions);
342 
343   return num_partitions;
344 }
345 
DecodeNumWeightBits(const base::UInt128 astc_bits)346 int DecodeNumWeightBits(const base::UInt128 astc_bits) {
347   std::string error;
348   auto maybe_weight_props = DecodeWeightProps(astc_bits, &error);
349   if (!maybe_weight_props.hasValue()) {
350     return 0;  // No weights? No weight bits...
351   }
352 
353   const auto weight_props = maybe_weight_props.value();
354 
355   // Figure out the number of weights
356   int num_weights = weight_props.width * weight_props.height;
357   if (DecodeDualPlaneBit(astc_bits)) {
358     num_weights *= 2;
359   }
360 
361   // The number of bits is determined by the number of values
362   // that are going to be encoded using the given ise_counts.
363   return IntegerSequenceCodec::GetBitCountForRange(
364       num_weights, weight_props.range);
365 }
366 
367 // Returns the number of bits after the weight data used to
368 // store additional CEM bits.
DecodeNumExtraCEMBits(const base::UInt128 astc_bits)369 int DecodeNumExtraCEMBits(const base::UInt128 astc_bits) {
370   const int num_partitions = DecodeNumPartitions(astc_bits);
371 
372   // Do we only have one partition?
373   if (num_partitions == 1) {
374     return 0;
375   }
376 
377   // Do we have a shared CEM?
378   constexpr int kSharedCEMBitPosition = 23;
379   constexpr int kSharedCEMBitLength = 2;
380   const base::UInt128 shared_cem =
381       base::GetBits(astc_bits, kSharedCEMBitPosition, kSharedCEMBitLength);
382   if (shared_cem == 0) {
383     return 0;
384   }
385 
386   const std::array<int, 4> extra_cem_bits_for_partition = {{ 0, 2, 5, 8 }};
387   return extra_cem_bits_for_partition[num_partitions - 1];
388 }
389 
390 // Returns the starting position of the dual plane channel. This comes
391 // before the weight data and extra CEM bits.
DecodeDualPlaneBitStartPos(const base::UInt128 astc_bits)392 int DecodeDualPlaneBitStartPos(const base::UInt128 astc_bits) {
393   const int start_pos = kASTCBlockSizeBits
394       - DecodeNumWeightBits(astc_bits)
395       - DecodeNumExtraCEMBits(astc_bits);
396 
397   if (DecodeDualPlaneBit(astc_bits)) {
398     return start_pos - 2;
399   } else {
400     return start_pos;
401   }
402 }
403 
404 // Decodes a CEM mode based on the partition number.
DecodeEndpointMode(const base::UInt128 astc_bits,int partition)405 ColorEndpointMode DecodeEndpointMode(const base::UInt128 astc_bits,
406                                      int partition) {
407   int num_partitions = DecodeNumPartitions(astc_bits);
408   assert(partition >= 0);
409   assert(partition < num_partitions);
410 
411   // Do we only have one partition?
412   uint64_t low_bits = astc_bits.LowBits();
413   if (num_partitions == 1) {
414     uint64_t cem = base::GetBits(low_bits, 13, 4);
415     return static_cast<ColorEndpointMode>(cem);
416   }
417 
418   // More than one partition ... do we have a shared CEM?
419   if (DecodeNumExtraCEMBits(astc_bits) == 0) {
420     const uint64_t shared_cem = base::GetBits(low_bits, 25, 4);
421     return static_cast<ColorEndpointMode>(shared_cem);
422   }
423 
424   // More than one partition and no shared CEM...
425   uint64_t cem = base::GetBits(low_bits, 23, 6);
426   const int base_cem = static_cast<int>(((cem & 0x3) - 1) * 4);
427   cem >>= 2;  // Skip the base CEM bits
428 
429   // The number of extra CEM bits at the end of the weight grid is
430   // determined by the number of partitions and what the base cem mode is...
431   const int num_extra_cem_bits = DecodeNumExtraCEMBits(astc_bits);
432   const int extra_cem_start_pos = kASTCBlockSizeBits
433       - num_extra_cem_bits
434       - DecodeNumWeightBits(astc_bits);
435 
436   base::UInt128 extra_cem =
437       base::GetBits(astc_bits, extra_cem_start_pos, num_extra_cem_bits);
438   cem |= extra_cem.LowBits() << 4;
439 
440   // Decode C and M per Figure C.4
441   int c = -1, m = -1;
442   for (int i = 0; i < num_partitions; ++i) {
443     if (i == partition) {
444       c = cem & 0x1;
445     }
446     cem >>= 1;
447   }
448 
449   for (int i = 0; i < num_partitions; ++i) {
450     if (i == partition) {
451       m = cem & 0x3;
452     }
453     cem >>= 2;
454   }
455 
456   assert(c >= 0);
457   assert(m >= 0);
458 
459   // Compute the mode based on C and M
460   const int mode = base_cem + 4 * c + m;
461   assert(mode < static_cast<int>(ColorEndpointMode::kNumColorEndpointModes));
462   return static_cast<ColorEndpointMode>(mode);
463 }
464 
DecodeNumColorValues(const base::UInt128 astc_bits)465 int DecodeNumColorValues(const base::UInt128 astc_bits) {
466   int num_color_values = 0;
467   auto num_partitions = DecodeNumPartitions(astc_bits);
468   for (int i = 0; i < num_partitions; ++i) {
469     ColorEndpointMode endpoint_mode = DecodeEndpointMode(astc_bits, i);
470     num_color_values += NumColorValuesForEndpointMode(endpoint_mode);
471   }
472 
473   return num_color_values;
474 }
475 
476 }  // namespace
477 
478 ////////////////////////////////////////////////////////////////////////////////
479 
480 static_assert(sizeof(PhysicalASTCBlock) == PhysicalASTCBlock::kSizeInBytes,
481               "The size of the struct should be the size of the block so that"
482               "we can effectively use them contiguously in memory.");
483 
PhysicalASTCBlock(const base::UInt128 astc_block)484 PhysicalASTCBlock::PhysicalASTCBlock(const base::UInt128 astc_block)
485     : astc_bits_(astc_block) {}
486 
PhysicalASTCBlock(const std::string & encoded_block)487 PhysicalASTCBlock::PhysicalASTCBlock(const std::string& encoded_block)
488     : astc_bits_([&encoded_block]() {
489         assert(encoded_block.size() == PhysicalASTCBlock::kSizeInBytes);
490         base::UInt128 astc_bits = 0;
491         int shift = 0;
492         for (const unsigned char c : encoded_block) {
493           astc_bits |= base::UInt128(static_cast<uint64_t>(c)) << shift;
494           shift += 8;
495         }
496         return astc_bits;
497       }())
498 { }
499 
IsIllegalEncoding() const500 base::Optional<std::string> PhysicalASTCBlock::IsIllegalEncoding() const {
501   // If the block is not a void extent block, then it must have
502   // weights specified. DecodeWeightProps will return the weight specifications
503   // if they exist and are legal according to C.2.24, and will otherwise be
504   // empty.
505   base::Optional<BlockMode> block_mode = DecodeBlockMode(astc_bits_);
506   if (block_mode != BlockMode::kVoidExtent) {
507     std::string error;
508     auto maybe_weight_props = DecodeWeightProps(astc_bits_, &error);
509     if (!maybe_weight_props.hasValue()) {
510       return error;
511     }
512   }
513 
514   // Check void extent blocks...
515   if (block_mode == BlockMode::kVoidExtent) {
516     // ... for reserved bits incorrectly set
517     if (base::GetBits(astc_bits_, 10, 2) != 0x3) {
518       return std::string("Reserved bits set for void extent block");
519     }
520 
521     // ... for incorrectly defined texture coordinates
522     std::array<int, 4> coords = DecodeVoidExtentCoords(astc_bits_);
523 
524     bool coords_all_1s = true;
525     for (const auto coord : coords) {
526       coords_all_1s &= coord == ((1 << 13) - 1);
527     }
528 
529     if (!coords_all_1s && (coords[0] >= coords[1] || coords[2] >= coords[3])) {
530       return std::string("Void extent texture coordinates are invalid");
531     }
532   }
533 
534   // If the number of color values exceeds a threshold and it isn't a void
535   // extent block then we've run into an error
536   if (block_mode != BlockMode::kVoidExtent) {
537     int num_color_vals = DecodeNumColorValues(astc_bits_);
538     if (num_color_vals > 18) {
539       return std::string("Too many color values");
540     }
541 
542     // The maximum number of available color bits is the number of
543     // bits between the dual plane bits and the base CEM. This must
544     // be larger than a threshold defined in C.2.24.
545 
546     // Dual plane bit starts after weight bits and CEM
547     const int num_partitions = DecodeNumPartitions(astc_bits_);
548     const int dual_plane_start_pos = DecodeDualPlaneBitStartPos(astc_bits_);
549     const int color_start_bit = (num_partitions == 1) ? 17 : 29;
550 
551     const int required_color_bits = ((13 * num_color_vals) + 4) / 5;
552     const int available_color_bits = dual_plane_start_pos - color_start_bit;
553     if (available_color_bits < required_color_bits) {
554       return std::string("Not enough color bits");
555     }
556 
557     // If we have four partitions and a dual plane then we have a problem.
558     if (num_partitions == 4 && DecodeDualPlaneBit(astc_bits_)) {
559       return std::string("Both four partitions and dual plane specified");
560     }
561   }
562 
563   // Otherwise we're OK
564   return { };
565 }
566 
IsVoidExtent() const567 bool PhysicalASTCBlock::IsVoidExtent() const {
568   // If it's an error block, it's not a void extent block.
569   if (IsIllegalEncoding()) {
570     return false;
571   }
572 
573   return DecodeBlockMode(astc_bits_) == BlockMode::kVoidExtent;
574 }
575 
VoidExtentCoords() const576 base::Optional<std::array<int, 4>> PhysicalASTCBlock::VoidExtentCoords() const {
577   if (IsIllegalEncoding() || !IsVoidExtent()) {
578     return { };
579   }
580 
581   // If void extent coords are all 1's then these are not valid void extent
582   // coords
583   const uint64_t ve_mask = 0xFFFFFFFFFFFFFDFFULL;
584   const uint64_t const_blk_mode = 0xFFFFFFFFFFFFFDFCULL;
585   if ((ve_mask & astc_bits_.LowBits()) == const_blk_mode) {
586     return {};
587   }
588 
589   return DecodeVoidExtentCoords(astc_bits_);
590 }
591 
IsDualPlane() const592 bool PhysicalASTCBlock::IsDualPlane() const {
593   // If it's an error block, then we aren't a dual plane block
594   if (IsIllegalEncoding()) {
595     return false;
596   }
597 
598   return DecodeDualPlaneBit(astc_bits_);
599 }
600 
601 // Returns the number of weight bits present in this block
NumWeightBits() const602 base::Optional<int> PhysicalASTCBlock::NumWeightBits() const {
603   // If it's an error block, then we have no weight bits.
604   if (IsIllegalEncoding()) return { };
605 
606   // If it's a void extent block, we have no weight bits
607   if (IsVoidExtent()) return { };
608 
609   return DecodeNumWeightBits(astc_bits_);
610 }
611 
WeightStartBit() const612 base::Optional<int> PhysicalASTCBlock::WeightStartBit() const {
613   if (IsIllegalEncoding()) return { };
614   if (IsVoidExtent()) return { };
615 
616   return kASTCBlockSizeBits - DecodeNumWeightBits(astc_bits_);
617 }
618 
WeightGridDims() const619 base::Optional<std::array<int, 2>> PhysicalASTCBlock::WeightGridDims() const {
620   std::string error;
621   auto weight_props = DecodeWeightProps(astc_bits_, &error);
622 
623   if (!weight_props.hasValue()) return { };
624   if (IsIllegalEncoding()) return { };
625 
626   const auto props = weight_props.value();
627   return {{{ props.width, props.height }}};
628 }
629 
WeightRange() const630 base::Optional<int> PhysicalASTCBlock::WeightRange() const {
631   std::string error;
632   auto weight_props = DecodeWeightProps(astc_bits_, &error);
633 
634   if (!weight_props.hasValue()) return { };
635   if (IsIllegalEncoding()) return { };
636 
637   return weight_props.value().range;
638 }
639 
DualPlaneChannel() const640 base::Optional<int> PhysicalASTCBlock::DualPlaneChannel() const {
641   if (!IsDualPlane()) return { };
642 
643   int dual_plane_start_pos = DecodeDualPlaneBitStartPos(astc_bits_);
644   auto plane_bits = base::GetBits(astc_bits_, dual_plane_start_pos, 2);
645   return base::Optional<int>(static_cast<int>(plane_bits.LowBits()));
646 }
647 
ColorStartBit() const648 base::Optional<int> PhysicalASTCBlock::ColorStartBit() const {
649   if (IsVoidExtent()) {
650     return 64;
651   }
652 
653   auto num_partitions = NumPartitions();
654   if (!num_partitions) return { };
655 
656   return (num_partitions == 1) ? 17 : 29;
657 }
658 
NumColorValues() const659 base::Optional<int> PhysicalASTCBlock::NumColorValues() const {
660   // If we have a void extent block, then we have four color values
661   if (IsVoidExtent()) {
662     return 4;
663   }
664 
665   // If we have an illegal encoding, then we have no color values
666   if (IsIllegalEncoding()) return { };
667 
668   return DecodeNumColorValues(astc_bits_);
669 }
670 
GetColorValuesInfo(int * const color_bits,int * const color_range) const671 void PhysicalASTCBlock::GetColorValuesInfo(int* const color_bits,
672                                            int* const color_range) const {
673   // Figure out the range possible for the number of values we have...
674   const int dual_plane_start_pos = DecodeDualPlaneBitStartPos(astc_bits_);
675   const int max_color_bits = dual_plane_start_pos - ColorStartBit().value();
676   const int num_color_values = NumColorValues().value();
677   for (int range = 255; range > 0; --range) {
678     const int bitcount =
679         IntegerSequenceCodec::GetBitCountForRange(num_color_values, range);
680     if (bitcount <= max_color_bits) {
681       if (color_bits != nullptr) {
682         *color_bits = bitcount;
683       }
684 
685       if (color_range != nullptr) {
686         *color_range = range;
687       }
688       return;
689     }
690   }
691 
692   assert(false &&
693          "This means that even if we have a range of one there aren't "
694          "enough bits to store the color values, and our encoding is "
695          "illegal.");
696 }
697 
NumColorBits() const698 base::Optional<int> PhysicalASTCBlock::NumColorBits() const {
699   if (IsIllegalEncoding()) return { };
700 
701   if (IsVoidExtent()) {
702     return 64;
703   }
704 
705   int color_bits;
706   GetColorValuesInfo(&color_bits, nullptr);
707   return color_bits;
708 }
709 
ColorValuesRange() const710 base::Optional<int> PhysicalASTCBlock::ColorValuesRange() const {
711   if (IsIllegalEncoding()) return { };
712 
713   if (IsVoidExtent()) {
714     return (1 << 16) - 1;
715   }
716 
717   int color_range;
718   GetColorValuesInfo(nullptr, &color_range);
719   return color_range;
720 }
721 
NumPartitions() const722 base::Optional<int> PhysicalASTCBlock::NumPartitions() const {
723   // Error blocks have no partitions
724   if (IsIllegalEncoding()) return { };
725 
726   // Void extent blocks have no partitions either
727   if (DecodeBlockMode(astc_bits_) == BlockMode::kVoidExtent) {
728     return { };
729   }
730 
731   // All others have some number of partitions
732   return DecodeNumPartitions(astc_bits_);
733 }
734 
PartitionID() const735 base::Optional<int> PhysicalASTCBlock::PartitionID() const {
736   auto num_partitions = NumPartitions();
737   if (!num_partitions || num_partitions == 1) return { };
738 
739   const uint64_t low_bits = astc_bits_.LowBits();
740   return static_cast<int>(base::GetBits(low_bits, 13, 10));
741 }
742 
GetEndpointMode(int partition) const743 base::Optional<ColorEndpointMode> PhysicalASTCBlock::GetEndpointMode(
744     int partition) const {
745   // Error block?
746   if (IsIllegalEncoding()) return { };
747 
748   // Void extent blocks have no endpoint modes
749   if (DecodeBlockMode(astc_bits_) == BlockMode::kVoidExtent) {
750     return { };
751   }
752 
753   // Do we even have a CEM for this partition?
754   if (partition < 0 || DecodeNumPartitions(astc_bits_) <= partition) {
755     return { };
756   }
757 
758   return DecodeEndpointMode(astc_bits_, partition);
759 }
760 
761 }  // namespace astc_codec
762