aboutsummaryrefslogtreecommitdiff
path: root/src/decoder/physical_astc_block.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/decoder/physical_astc_block.cc')
-rw-r--r--src/decoder/physical_astc_block.cc761
1 files changed, 761 insertions, 0 deletions
diff --git a/src/decoder/physical_astc_block.cc b/src/decoder/physical_astc_block.cc
new file mode 100644
index 0000000..7cc4d8e
--- /dev/null
+++ b/src/decoder/physical_astc_block.cc
@@ -0,0 +1,761 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/decoder/physical_astc_block.h"
+#include "src/base/math_utils.h"
+#include "src/base/optional.h"
+#include "src/base/uint128.h"
+#include "src/decoder/integer_sequence_codec.h"
+
+#include <array>
+#include <cmath>
+
+namespace astc_codec {
+
+namespace {
+
+static_assert(static_cast<int>(ColorEndpointMode::kNumColorEndpointModes) == 16,
+ "There are only sixteen color endpoint modes defined in the "
+ "ASTC specification. If this is false, then the enum may be "
+ "incorrect.");
+
+constexpr int kASTCBlockSizeBits = 128;
+constexpr int kASTCBlockSizeBytes = kASTCBlockSizeBits / 8;
+constexpr uint32_t kVoidExtentMaskBits = 9;
+constexpr uint32_t kVoidExtentMask = 0x1FC;
+constexpr int kWeightGridMinBitLength = 24;
+constexpr int kWeightGridMaxBitLength = 96;
+constexpr int kMaxNumPartitions = 4;
+constexpr int kMaxNumWeights = 64;
+
+// These are the overall block modes defined in table C.2.8. There are 10
+// weight grid encoding schemes + void extent.
+enum class BlockMode {
+ kB4_A2,
+ kB8_A2,
+ kA2_B8,
+ kA2_B6,
+ kB2_A2,
+ k12_A2,
+ kA2_12,
+ k6_10,
+ k10_6,
+ kA6_B6,
+ kVoidExtent,
+};
+
+struct WeightGridProperties {
+ int width;
+ int height;
+ int range;
+};
+
+// Local function prototypes
+base::Optional<BlockMode> DecodeBlockMode(const base::UInt128 astc_bits);
+base::Optional<WeightGridProperties> DecodeWeightProps(
+ const base::UInt128 astc_bits, std::string* error);
+std::array<int, 4> DecodeVoidExtentCoords(const base::UInt128 astc_bits);
+bool DecodeDualPlaneBit(const base::UInt128 astc_bits);
+int DecodeNumPartitions(const base::UInt128 astc_bits);
+int DecodeNumWeightBits(const base::UInt128 astc_bits);
+int DecodeDualPlaneBitStartPos(const base::UInt128 astc_bits);
+ColorEndpointMode DecodeEndpointMode(const base::UInt128 astc_bits,
+ int partition);
+int DecodeNumColorValues(const base::UInt128 astc_bits);
+
+// Returns the block mode, if it's valid.
+base::Optional<BlockMode> DecodeBlockMode(const base::UInt128 astc_bits) {
+ using Result = base::Optional<BlockMode>;
+ const uint64_t low_bits = astc_bits.LowBits();
+ if (base::GetBits(low_bits, 0, kVoidExtentMaskBits) == kVoidExtentMask) {
+ return Result(BlockMode::kVoidExtent);
+ }
+
+ if (base::GetBits(low_bits, 0, 2) != 0) {
+ const uint64_t mode_bits = base::GetBits(low_bits, 2, 2);
+ switch (mode_bits) {
+ case 0: return Result(BlockMode::kB4_A2);
+ case 1: return Result(BlockMode::kB8_A2);
+ case 2: return Result(BlockMode::kA2_B8);
+ case 3: return base::GetBits(low_bits, 8, 1) ?
+ Result(BlockMode::kB2_A2) : Result(BlockMode::kA2_B6);
+ }
+ } else {
+ const uint64_t mode_bits = base::GetBits(low_bits, 5, 4);
+ if ((mode_bits & 0xC) == 0x0) {
+ if (base::GetBits(low_bits, 0, 4) == 0) {
+ // Reserved.
+ return Result();
+ } else {
+ return Result(BlockMode::k12_A2);
+ }
+ } else if ((mode_bits & 0xC) == 0x4) {
+ return Result(BlockMode::kA2_12);
+ } else if (mode_bits == 0xC) {
+ return Result(BlockMode::k6_10);
+ } else if (mode_bits == 0xD) {
+ return Result(BlockMode::k10_6);
+ } else if ((mode_bits & 0xC) == 0x8) {
+ return Result(BlockMode::kA6_B6);
+ }
+ }
+
+ return Result();
+}
+
+base::Optional<WeightGridProperties> DecodeWeightProps(
+ const base::UInt128 astc_bits, std::string* error) {
+ auto block_mode = DecodeBlockMode(astc_bits);
+ if (!block_mode) {
+ *error = "Reserved block mode";
+ return {};
+ }
+
+ // The dimensions of the weight grid and their range
+ WeightGridProperties props;
+
+ // Determine the weight extents based on the block mode
+ const uint32_t low_bits =
+ static_cast<uint32_t>(astc_bits.LowBits() & 0xFFFFFFFF);
+ switch (block_mode.value()) {
+ case BlockMode::kB4_A2: {
+ int a = base::GetBits(low_bits, 5, 2);
+ int b = base::GetBits(low_bits, 7, 2);
+ props.width = b + 4;
+ props.height = a + 2;
+ }
+ break;
+
+ case BlockMode::kB8_A2: {
+ int a = base::GetBits(low_bits, 5, 2);
+ int b = base::GetBits(low_bits, 7, 2);
+ props.width = b + 8;
+ props.height = a + 2;
+ }
+ break;
+
+ case BlockMode::kA2_B8: {
+ int a = base::GetBits(low_bits, 5, 2);
+ int b = base::GetBits(low_bits, 7, 2);
+ props.width = a + 2;
+ props.height = b + 8;
+ }
+ break;
+
+ case BlockMode::kA2_B6: {
+ int a = base::GetBits(low_bits, 5, 2);
+ int b = base::GetBits(low_bits, 7, 1);
+ props.width = a + 2;
+ props.height = b + 6;
+ }
+ break;
+
+ case BlockMode::kB2_A2: {
+ int a = base::GetBits(low_bits, 5, 2);
+ int b = base::GetBits(low_bits, 7, 1);
+ props.width = b + 2;
+ props.height = a + 2;
+ }
+ break;
+
+ case BlockMode::k12_A2: {
+ int a = base::GetBits(low_bits, 5, 2);
+ props.width = 12;
+ props.height = a + 2;
+ }
+ break;
+
+ case BlockMode::kA2_12: {
+ int a = base::GetBits(low_bits, 5, 2);
+ props.width = a + 2;
+ props.height = 12;
+ }
+ break;
+
+ case BlockMode::k6_10: {
+ props.width = 6;
+ props.height = 10;
+ }
+ break;
+
+ case BlockMode::k10_6: {
+ props.width = 10;
+ props.height = 6;
+ }
+ break;
+
+ case BlockMode::kA6_B6: {
+ int a = base::GetBits(low_bits, 5, 2);
+ int b = base::GetBits(low_bits, 9, 2);
+ props.width = a + 6;
+ props.height = b + 6;
+ }
+ break;
+
+ // Void extent blocks have no weight grid.
+ case BlockMode::kVoidExtent:
+ *error = "Void extent block has no weight grid";
+ return {};
+
+ // We have a valid block mode which isn't a void extent? We
+ // should be able to decode the weight grid dimensions.
+ default:
+ assert(false && "Error decoding weight grid");
+ *error = "Internal error";
+ return {};
+ }
+
+ // Determine the weight range based on the block mode
+ uint32_t r = base::GetBits(low_bits, 4, 1);
+ switch (block_mode.value()) {
+ case BlockMode::kB4_A2:
+ case BlockMode::kB8_A2:
+ case BlockMode::kA2_B8:
+ case BlockMode::kA2_B6:
+ case BlockMode::kB2_A2: {
+ r |= base::GetBits(low_bits, 0, 2) << 1;
+ }
+ break;
+
+ case BlockMode::k12_A2:
+ case BlockMode::kA2_12:
+ case BlockMode::k6_10:
+ case BlockMode::k10_6:
+ case BlockMode::kA6_B6: {
+ r |= base::GetBits(low_bits, 2, 2) << 1;
+ }
+ break;
+
+ // We have a valid block mode which doesn't have weights? We
+ // should have caught this earlier.
+ case BlockMode::kVoidExtent:
+ default:
+ assert(false && "Error decoding weight grid");
+ *error = "Internal error";
+ return {};
+ }
+
+ // Decode the range...
+ // High bit is in bit 9 unless we're using a particular block mode
+ uint32_t h = base::GetBits(low_bits, 9, 1);
+ if (block_mode == BlockMode::kA6_B6) {
+ h = 0;
+ }
+
+ // Figure out the range of the weights (Table C.2.7)
+ constexpr std::array<int, 16> kWeightRanges = {{
+ -1, -1, 1, 2, 3, 4, 5, 7, -1, -1, 9, 11, 15, 19, 23, 31
+ }};
+
+ assert(((h << 3) | r) < kWeightRanges.size());
+
+ props.range = kWeightRanges.at((h << 3) | r);
+ if (props.range < 0) {
+ *error = "Reserved range for weight bits";
+ return {};
+ }
+
+ // Error checking -- do we have too many weights?
+ int num_weights = props.width * props.height;
+ if (DecodeDualPlaneBit(astc_bits)) {
+ num_weights *= 2;
+ }
+
+ if (kMaxNumWeights < num_weights) {
+ *error = "Too many weights specified";
+ return {};
+ }
+
+ // Do we have too many weight bits?
+ const int bit_count =
+ IntegerSequenceCodec::GetBitCountForRange(num_weights, props.range);
+
+ if (bit_count < kWeightGridMinBitLength) {
+ *error = "Too few bits required for weight grid";
+ return {};
+ }
+
+ if (kWeightGridMaxBitLength < bit_count) {
+ *error = "Too many bits required for weight grid";
+ return {};
+ }
+
+ return props;
+}
+
+// Returns the four 13-bit integers that define the range of texture
+// coordinates present in a void extent block as defined in Section
+// C.2.23 of the specification. The coordinates returned are of
+// the form (min_s, max_s, min_t, max_t)
+std::array<int, 4> DecodeVoidExtentCoords(const base::UInt128 astc_bits) {
+ const uint64_t low_bits = astc_bits.LowBits();
+
+ std::array<int, 4> coords;
+ for (int i = 0; i < 4; ++i) {
+ coords[i] = static_cast<int>(base::GetBits(low_bits, 12 + 13 * i, 13));
+ }
+
+ return coords;
+}
+
+bool DecodeDualPlaneBit(const base::UInt128 astc_bits) {
+ base::Optional<BlockMode> block_mode = DecodeBlockMode(astc_bits);
+
+ // Void extent blocks certainly aren't dual-plane.
+ if (block_mode == BlockMode::kVoidExtent) {
+ return false;
+ }
+
+ // One special block mode doesn't have any dual plane bit
+ if (block_mode == BlockMode::kA6_B6) {
+ return false;
+ }
+
+ // Otherwise, dual plane is determined by the 10th bit.
+ constexpr int kDualPlaneBitPosition = 10;
+ return base::GetBits(astc_bits, kDualPlaneBitPosition, 1) != 0;
+}
+
+int DecodeNumPartitions(const base::UInt128 astc_bits) {
+ constexpr int kNumPartitionsBitPosition = 11;
+ constexpr int kNumPartitionsBitLength = 2;
+
+ // Non-void extent blocks
+ const uint64_t low_bits = astc_bits.LowBits();
+ const int num_partitions = 1 + static_cast<int>(
+ base::GetBits(low_bits,
+ kNumPartitionsBitPosition,
+ kNumPartitionsBitLength));
+ assert(num_partitions > 0);
+ assert(num_partitions <= kMaxNumPartitions);
+
+ return num_partitions;
+}
+
+int DecodeNumWeightBits(const base::UInt128 astc_bits) {
+ std::string error;
+ auto maybe_weight_props = DecodeWeightProps(astc_bits, &error);
+ if (!maybe_weight_props.hasValue()) {
+ return 0; // No weights? No weight bits...
+ }
+
+ const auto weight_props = maybe_weight_props.value();
+
+ // Figure out the number of weights
+ int num_weights = weight_props.width * weight_props.height;
+ if (DecodeDualPlaneBit(astc_bits)) {
+ num_weights *= 2;
+ }
+
+ // The number of bits is determined by the number of values
+ // that are going to be encoded using the given ise_counts.
+ return IntegerSequenceCodec::GetBitCountForRange(
+ num_weights, weight_props.range);
+}
+
+// Returns the number of bits after the weight data used to
+// store additional CEM bits.
+int DecodeNumExtraCEMBits(const base::UInt128 astc_bits) {
+ const int num_partitions = DecodeNumPartitions(astc_bits);
+
+ // Do we only have one partition?
+ if (num_partitions == 1) {
+ return 0;
+ }
+
+ // Do we have a shared CEM?
+ constexpr int kSharedCEMBitPosition = 23;
+ constexpr int kSharedCEMBitLength = 2;
+ const base::UInt128 shared_cem =
+ base::GetBits(astc_bits, kSharedCEMBitPosition, kSharedCEMBitLength);
+ if (shared_cem == 0) {
+ return 0;
+ }
+
+ const std::array<int, 4> extra_cem_bits_for_partition = {{ 0, 2, 5, 8 }};
+ return extra_cem_bits_for_partition[num_partitions - 1];
+}
+
+// Returns the starting position of the dual plane channel. This comes
+// before the weight data and extra CEM bits.
+int DecodeDualPlaneBitStartPos(const base::UInt128 astc_bits) {
+ const int start_pos = kASTCBlockSizeBits
+ - DecodeNumWeightBits(astc_bits)
+ - DecodeNumExtraCEMBits(astc_bits);
+
+ if (DecodeDualPlaneBit(astc_bits)) {
+ return start_pos - 2;
+ } else {
+ return start_pos;
+ }
+}
+
+// Decodes a CEM mode based on the partition number.
+ColorEndpointMode DecodeEndpointMode(const base::UInt128 astc_bits,
+ int partition) {
+ int num_partitions = DecodeNumPartitions(astc_bits);
+ assert(partition >= 0);
+ assert(partition < num_partitions);
+
+ // Do we only have one partition?
+ uint64_t low_bits = astc_bits.LowBits();
+ if (num_partitions == 1) {
+ uint64_t cem = base::GetBits(low_bits, 13, 4);
+ return static_cast<ColorEndpointMode>(cem);
+ }
+
+ // More than one partition ... do we have a shared CEM?
+ if (DecodeNumExtraCEMBits(astc_bits) == 0) {
+ const uint64_t shared_cem = base::GetBits(low_bits, 25, 4);
+ return static_cast<ColorEndpointMode>(shared_cem);
+ }
+
+ // More than one partition and no shared CEM...
+ uint64_t cem = base::GetBits(low_bits, 23, 6);
+ const int base_cem = static_cast<int>(((cem & 0x3) - 1) * 4);
+ cem >>= 2; // Skip the base CEM bits
+
+ // The number of extra CEM bits at the end of the weight grid is
+ // determined by the number of partitions and what the base cem mode is...
+ const int num_extra_cem_bits = DecodeNumExtraCEMBits(astc_bits);
+ const int extra_cem_start_pos = kASTCBlockSizeBits
+ - num_extra_cem_bits
+ - DecodeNumWeightBits(astc_bits);
+
+ base::UInt128 extra_cem =
+ base::GetBits(astc_bits, extra_cem_start_pos, num_extra_cem_bits);
+ cem |= extra_cem.LowBits() << 4;
+
+ // Decode C and M per Figure C.4
+ int c = -1, m = -1;
+ for (int i = 0; i < num_partitions; ++i) {
+ if (i == partition) {
+ c = cem & 0x1;
+ }
+ cem >>= 1;
+ }
+
+ for (int i = 0; i < num_partitions; ++i) {
+ if (i == partition) {
+ m = cem & 0x3;
+ }
+ cem >>= 2;
+ }
+
+ assert(c >= 0);
+ assert(m >= 0);
+
+ // Compute the mode based on C and M
+ const int mode = base_cem + 4 * c + m;
+ assert(mode < static_cast<int>(ColorEndpointMode::kNumColorEndpointModes));
+ return static_cast<ColorEndpointMode>(mode);
+}
+
+int DecodeNumColorValues(const base::UInt128 astc_bits) {
+ int num_color_values = 0;
+ auto num_partitions = DecodeNumPartitions(astc_bits);
+ for (int i = 0; i < num_partitions; ++i) {
+ ColorEndpointMode endpoint_mode = DecodeEndpointMode(astc_bits, i);
+ num_color_values += NumColorValuesForEndpointMode(endpoint_mode);
+ }
+
+ return num_color_values;
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
+static_assert(sizeof(PhysicalASTCBlock) == PhysicalASTCBlock::kSizeInBytes,
+ "The size of the struct should be the size of the block so that"
+ "we can effectively use them contiguously in memory.");
+
+PhysicalASTCBlock::PhysicalASTCBlock(const base::UInt128 astc_block)
+ : astc_bits_(astc_block) {}
+
+PhysicalASTCBlock::PhysicalASTCBlock(const std::string& encoded_block)
+ : astc_bits_([&encoded_block]() {
+ assert(encoded_block.size() == PhysicalASTCBlock::kSizeInBytes);
+ base::UInt128 astc_bits = 0;
+ int shift = 0;
+ for (const unsigned char c : encoded_block) {
+ astc_bits |= base::UInt128(static_cast<uint64_t>(c)) << shift;
+ shift += 8;
+ }
+ return astc_bits;
+ }())
+{ }
+
+base::Optional<std::string> PhysicalASTCBlock::IsIllegalEncoding() const {
+ // If the block is not a void extent block, then it must have
+ // weights specified. DecodeWeightProps will return the weight specifications
+ // if they exist and are legal according to C.2.24, and will otherwise be
+ // empty.
+ base::Optional<BlockMode> block_mode = DecodeBlockMode(astc_bits_);
+ if (block_mode != BlockMode::kVoidExtent) {
+ std::string error;
+ auto maybe_weight_props = DecodeWeightProps(astc_bits_, &error);
+ if (!maybe_weight_props.hasValue()) {
+ return error;
+ }
+ }
+
+ // Check void extent blocks...
+ if (block_mode == BlockMode::kVoidExtent) {
+ // ... for reserved bits incorrectly set
+ if (base::GetBits(astc_bits_, 10, 2) != 0x3) {
+ return std::string("Reserved bits set for void extent block");
+ }
+
+ // ... for incorrectly defined texture coordinates
+ std::array<int, 4> coords = DecodeVoidExtentCoords(astc_bits_);
+
+ bool coords_all_1s = true;
+ for (const auto coord : coords) {
+ coords_all_1s &= coord == ((1 << 13) - 1);
+ }
+
+ if (!coords_all_1s && (coords[0] >= coords[1] || coords[2] >= coords[3])) {
+ return std::string("Void extent texture coordinates are invalid");
+ }
+ }
+
+ // If the number of color values exceeds a threshold and it isn't a void
+ // extent block then we've run into an error
+ if (block_mode != BlockMode::kVoidExtent) {
+ int num_color_vals = DecodeNumColorValues(astc_bits_);
+ if (num_color_vals > 18) {
+ return std::string("Too many color values");
+ }
+
+ // The maximum number of available color bits is the number of
+ // bits between the dual plane bits and the base CEM. This must
+ // be larger than a threshold defined in C.2.24.
+
+ // Dual plane bit starts after weight bits and CEM
+ const int num_partitions = DecodeNumPartitions(astc_bits_);
+ const int dual_plane_start_pos = DecodeDualPlaneBitStartPos(astc_bits_);
+ const int color_start_bit = (num_partitions == 1) ? 17 : 29;
+
+ const int required_color_bits = ((13 * num_color_vals) + 4) / 5;
+ const int available_color_bits = dual_plane_start_pos - color_start_bit;
+ if (available_color_bits < required_color_bits) {
+ return std::string("Not enough color bits");
+ }
+
+ // If we have four partitions and a dual plane then we have a problem.
+ if (num_partitions == 4 && DecodeDualPlaneBit(astc_bits_)) {
+ return std::string("Both four partitions and dual plane specified");
+ }
+ }
+
+ // Otherwise we're OK
+ return { };
+}
+
+bool PhysicalASTCBlock::IsVoidExtent() const {
+ // If it's an error block, it's not a void extent block.
+ if (IsIllegalEncoding()) {
+ return false;
+ }
+
+ return DecodeBlockMode(astc_bits_) == BlockMode::kVoidExtent;
+}
+
+base::Optional<std::array<int, 4>> PhysicalASTCBlock::VoidExtentCoords() const {
+ if (IsIllegalEncoding() || !IsVoidExtent()) {
+ return { };
+ }
+
+ // If void extent coords are all 1's then these are not valid void extent
+ // coords
+ const uint64_t ve_mask = 0xFFFFFFFFFFFFFDFFULL;
+ const uint64_t const_blk_mode = 0xFFFFFFFFFFFFFDFCULL;
+ if ((ve_mask & astc_bits_.LowBits()) == const_blk_mode) {
+ return {};
+ }
+
+ return DecodeVoidExtentCoords(astc_bits_);
+}
+
+bool PhysicalASTCBlock::IsDualPlane() const {
+ // If it's an error block, then we aren't a dual plane block
+ if (IsIllegalEncoding()) {
+ return false;
+ }
+
+ return DecodeDualPlaneBit(astc_bits_);
+}
+
+// Returns the number of weight bits present in this block
+base::Optional<int> PhysicalASTCBlock::NumWeightBits() const {
+ // If it's an error block, then we have no weight bits.
+ if (IsIllegalEncoding()) return { };
+
+ // If it's a void extent block, we have no weight bits
+ if (IsVoidExtent()) return { };
+
+ return DecodeNumWeightBits(astc_bits_);
+}
+
+base::Optional<int> PhysicalASTCBlock::WeightStartBit() const {
+ if (IsIllegalEncoding()) return { };
+ if (IsVoidExtent()) return { };
+
+ return kASTCBlockSizeBits - DecodeNumWeightBits(astc_bits_);
+}
+
+base::Optional<std::array<int, 2>> PhysicalASTCBlock::WeightGridDims() const {
+ std::string error;
+ auto weight_props = DecodeWeightProps(astc_bits_, &error);
+
+ if (!weight_props.hasValue()) return { };
+ if (IsIllegalEncoding()) return { };
+
+ const auto props = weight_props.value();
+ return {{{ props.width, props.height }}};
+}
+
+base::Optional<int> PhysicalASTCBlock::WeightRange() const {
+ std::string error;
+ auto weight_props = DecodeWeightProps(astc_bits_, &error);
+
+ if (!weight_props.hasValue()) return { };
+ if (IsIllegalEncoding()) return { };
+
+ return weight_props.value().range;
+}
+
+base::Optional<int> PhysicalASTCBlock::DualPlaneChannel() const {
+ if (!IsDualPlane()) return { };
+
+ int dual_plane_start_pos = DecodeDualPlaneBitStartPos(astc_bits_);
+ auto plane_bits = base::GetBits(astc_bits_, dual_plane_start_pos, 2);
+ return base::Optional<int>(static_cast<int>(plane_bits.LowBits()));
+}
+
+base::Optional<int> PhysicalASTCBlock::ColorStartBit() const {
+ if (IsVoidExtent()) {
+ return 64;
+ }
+
+ auto num_partitions = NumPartitions();
+ if (!num_partitions) return { };
+
+ return (num_partitions == 1) ? 17 : 29;
+}
+
+base::Optional<int> PhysicalASTCBlock::NumColorValues() const {
+ // If we have a void extent block, then we have four color values
+ if (IsVoidExtent()) {
+ return 4;
+ }
+
+ // If we have an illegal encoding, then we have no color values
+ if (IsIllegalEncoding()) return { };
+
+ return DecodeNumColorValues(astc_bits_);
+}
+
+void PhysicalASTCBlock::GetColorValuesInfo(int* const color_bits,
+ int* const color_range) const {
+ // Figure out the range possible for the number of values we have...
+ const int dual_plane_start_pos = DecodeDualPlaneBitStartPos(astc_bits_);
+ const int max_color_bits = dual_plane_start_pos - ColorStartBit().value();
+ const int num_color_values = NumColorValues().value();
+ for (int range = 255; range > 0; --range) {
+ const int bitcount =
+ IntegerSequenceCodec::GetBitCountForRange(num_color_values, range);
+ if (bitcount <= max_color_bits) {
+ if (color_bits != nullptr) {
+ *color_bits = bitcount;
+ }
+
+ if (color_range != nullptr) {
+ *color_range = range;
+ }
+ return;
+ }
+ }
+
+ assert(false &&
+ "This means that even if we have a range of one there aren't "
+ "enough bits to store the color values, and our encoding is "
+ "illegal.");
+}
+
+base::Optional<int> PhysicalASTCBlock::NumColorBits() const {
+ if (IsIllegalEncoding()) return { };
+
+ if (IsVoidExtent()) {
+ return 64;
+ }
+
+ int color_bits;
+ GetColorValuesInfo(&color_bits, nullptr);
+ return color_bits;
+}
+
+base::Optional<int> PhysicalASTCBlock::ColorValuesRange() const {
+ if (IsIllegalEncoding()) return { };
+
+ if (IsVoidExtent()) {
+ return (1 << 16) - 1;
+ }
+
+ int color_range;
+ GetColorValuesInfo(nullptr, &color_range);
+ return color_range;
+}
+
+base::Optional<int> PhysicalASTCBlock::NumPartitions() const {
+ // Error blocks have no partitions
+ if (IsIllegalEncoding()) return { };
+
+ // Void extent blocks have no partitions either
+ if (DecodeBlockMode(astc_bits_) == BlockMode::kVoidExtent) {
+ return { };
+ }
+
+ // All others have some number of partitions
+ return DecodeNumPartitions(astc_bits_);
+}
+
+base::Optional<int> PhysicalASTCBlock::PartitionID() const {
+ auto num_partitions = NumPartitions();
+ if (!num_partitions || num_partitions == 1) return { };
+
+ const uint64_t low_bits = astc_bits_.LowBits();
+ return static_cast<int>(base::GetBits(low_bits, 13, 10));
+}
+
+base::Optional<ColorEndpointMode> PhysicalASTCBlock::GetEndpointMode(
+ int partition) const {
+ // Error block?
+ if (IsIllegalEncoding()) return { };
+
+ // Void extent blocks have no endpoint modes
+ if (DecodeBlockMode(astc_bits_) == BlockMode::kVoidExtent) {
+ return { };
+ }
+
+ // Do we even have a CEM for this partition?
+ if (partition < 0 || DecodeNumPartitions(astc_bits_) <= partition) {
+ return { };
+ }
+
+ return DecodeEndpointMode(astc_bits_, partition);
+}
+
+} // namespace astc_codec