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