aboutsummaryrefslogtreecommitdiff
path: root/src/decoder/test/intermediate_astc_block_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/decoder/test/intermediate_astc_block_test.cc')
-rw-r--r--src/decoder/test/intermediate_astc_block_test.cc453
1 files changed, 453 insertions, 0 deletions
diff --git a/src/decoder/test/intermediate_astc_block_test.cc b/src/decoder/test/intermediate_astc_block_test.cc
new file mode 100644
index 0000000..69935ef
--- /dev/null
+++ b/src/decoder/test/intermediate_astc_block_test.cc
@@ -0,0 +1,453 @@
+// 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/intermediate_astc_block.h"
+#include "src/decoder/test/image_utils.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <string>
+
+namespace astc_codec {
+
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::Optional;
+using ::testing::SizeIs;
+using ::testing::TestWithParam;
+using ::testing::ValuesIn;
+
+// Test to make sure that unpacking an error block returns false.
+TEST(IntermediateASTCBlockTest, TestUnpackError) {
+ const PhysicalASTCBlock kErrorBlock(base::UInt128(0));
+ EXPECT_FALSE(UnpackVoidExtent(kErrorBlock));
+ EXPECT_FALSE(UnpackIntermediateBlock(kErrorBlock));
+}
+
+// Test to make sure that if we don't populate our weight data in the
+// intermediate block than the resulting color range should error due to the
+// mismatch.
+TEST(IntermediateASTCBlockTest, TestEndpointRangeErrorOnNotSettingWeights) {
+ IntermediateBlockData data;
+ data.weight_range = 15;
+ for (auto& ep : data.endpoints) {
+ ep.mode = ColorEndpointMode::kLDRRGBDirect;
+ }
+ data.weight_grid_dim_x = 6;
+ data.weight_grid_dim_y = 6;
+ EXPECT_EQ(-1, EndpointRangeForBlock(data));
+
+ base::UInt128 dummy;
+ auto err_str = Pack(data, &dummy);
+ EXPECT_TRUE(err_str.hasValue());
+ EXPECT_THAT(err_str.value(), HasSubstr("Incorrect number of weights"));
+}
+
+// Test to make sure that if we run out of bits, then we should say so.
+TEST(IntermediateASTCBlockTest, TestEndpointRangeErrorOnNotEnoughBits) {
+ IntermediateBlockData data;
+ data.weight_range = 1;
+ data.partition_id = 0;
+ data.endpoints.resize(3);
+ for (auto& ep : data.endpoints) {
+ ep.mode = ColorEndpointMode::kLDRRGBDirect;
+ }
+ data.weight_grid_dim_x = 8;
+ data.weight_grid_dim_y = 8;
+ EXPECT_EQ(-2, EndpointRangeForBlock(data));
+
+ // Resize the weights to get past the error that they do not match the grid
+ // dimensions.
+ data.weights.resize(64);
+
+ base::UInt128 dummy;
+ auto err_str = Pack(data, &dummy);
+ EXPECT_TRUE(err_str.hasValue());
+ EXPECT_THAT(err_str.value(), HasSubstr("illegal color range"));
+}
+
+// Test to make sure that as we increase the number of weights, we decrease the
+// allowable range of colors
+TEST(IntermediateASTCBlockTest, TestEndpointRangeForBlock) {
+ IntermediateBlockData data;
+ data.weight_range = 2;
+ data.endpoints.resize(2);
+ data.dual_plane_channel.clear();
+ for (auto& ep : data.endpoints) {
+ ep.mode = ColorEndpointMode::kLDRRGBDirect;
+ }
+
+ // Weight params control how many weights are present in a block
+ struct WeightParams {
+ int width;
+ int height;
+
+ // We should sort based on number of weights for these params
+ int NumWeights() const { return width * height; }
+ bool operator<(const WeightParams& other) const {
+ return NumWeights() < other.NumWeights();
+ }
+ };
+
+ std::vector<WeightParams> weight_params;
+ for (int y = 2; y < 8; ++y) {
+ for (int x = 2; x < 8; ++x) {
+ weight_params.emplace_back(WeightParams{x, y});
+ }
+ }
+
+ // Sort weights from fewest to largest such that the allowable color range
+ // should be monotonically decreasing
+ std::sort(weight_params.begin(), weight_params.end());
+
+ // Keep track of the largest available color range and measure that it
+ // decreases as we add more weights to our block
+ int last_color_range = 255;
+ for (const auto& params : weight_params) {
+ data.weight_grid_dim_x = params.width;
+ data.weight_grid_dim_y = params.height;
+
+ const int color_range = EndpointRangeForBlock(data);
+ EXPECT_LE(color_range, last_color_range);
+ last_color_range = std::min(color_range, last_color_range);
+ }
+
+ // Make sure that we actually changed it at some point.
+ EXPECT_LT(last_color_range, 255);
+}
+
+// Test to make sure that unpacking an legitimate ASTC block returns the encoded
+// values that we expect.
+TEST(IntermediateASTCBlockTest, TestUnpackNonVoidExtentBlock) {
+ PhysicalASTCBlock blk(0x0000000001FE000173ULL);
+ auto b = UnpackIntermediateBlock(blk);
+ ASSERT_TRUE(b);
+
+ const auto& data = b.value();
+ EXPECT_EQ(data.weight_grid_dim_x, 6);
+ EXPECT_EQ(data.weight_grid_dim_y, 5);
+ EXPECT_EQ(data.weight_range, 7);
+
+ EXPECT_FALSE(data.partition_id);
+ EXPECT_FALSE(data.dual_plane_channel);
+
+ ASSERT_EQ(data.weights.size(), 30);
+ for (auto weight : data.weights) {
+ EXPECT_EQ(weight, 0);
+ }
+
+ ASSERT_EQ(data.endpoints.size(), 1);
+ for (const auto& ep_data : data.endpoints) {
+ EXPECT_EQ(ep_data.mode, ColorEndpointMode::kLDRLumaDirect);
+ ASSERT_EQ(ep_data.colors.size(), 2);
+ EXPECT_EQ(ep_data.colors[0], 0);
+ EXPECT_EQ(ep_data.colors[1], 255);
+ }
+}
+
+// Make sure that we can pack blocks that aren't void extent blocks. (In other
+// words, can we actually deal with intermediate ASTC data).
+TEST(IntermediateASTCBlockTest, TestPackNonVoidExtentBlock) {
+ IntermediateBlockData data;
+
+ data.weight_grid_dim_x = 6;
+ data.weight_grid_dim_y = 5;
+ data.weight_range = 7;
+
+ data.partition_id = {};
+ data.dual_plane_channel = {};
+
+ data.weights.resize(30);
+ for (auto& weight : data.weights) {
+ weight = 0;
+ }
+
+ data.endpoints.resize(1);
+ for (auto& ep_data : data.endpoints) {
+ ep_data.mode = ColorEndpointMode::kLDRLumaDirect;
+ ep_data.colors.resize(2);
+ ep_data.colors[0] = 0;
+ ep_data.colors[1] = 255;
+ }
+
+ base::UInt128 packed;
+ auto error_str = Pack(data, &packed);
+ ASSERT_FALSE(error_str) << (error_str ? error_str.value() : std::string(""));
+ EXPECT_EQ(packed, 0x0000000001FE000173ULL);
+}
+
+// Make sure that we can unpack void extent blocks
+TEST(IntermediateASTCBlockTest, TestUnpackVoidExtentBlock) {
+ PhysicalASTCBlock void_extent_block(0xFFFFFFFFFFFFFDFCULL);
+
+ auto b = UnpackVoidExtent(void_extent_block);
+ ASSERT_TRUE(b);
+
+ const auto& data = b.value();
+ EXPECT_EQ(data.r, 0);
+ EXPECT_EQ(data.g, 0);
+ EXPECT_EQ(data.b, 0);
+ EXPECT_EQ(data.a, 0);
+ for (const auto& coord : data.coords) {
+ EXPECT_EQ(coord, (1 << 13) - 1);
+ }
+
+ base::UInt128 more_interesting(0xdeadbeefdeadbeefULL, 0xFFF8003FFE000DFCULL);
+ b = UnpackVoidExtent(PhysicalASTCBlock(more_interesting));
+ ASSERT_TRUE(b);
+
+ const auto& other_data = b.value();
+ EXPECT_EQ(other_data.r, 0xbeef);
+ EXPECT_EQ(other_data.g, 0xdead);
+ EXPECT_EQ(other_data.b, 0xbeef);
+ EXPECT_EQ(other_data.a, 0xdead);
+ EXPECT_EQ(other_data.coords[0], 0);
+ EXPECT_EQ(other_data.coords[1], 8191);
+ EXPECT_EQ(other_data.coords[2], 0);
+ EXPECT_EQ(other_data.coords[3], 8191);
+}
+
+// Make sure that we can pack void extent blocks and void extent data.
+TEST(IntermediateASTCBlockTest, TestPackVoidExtentBlock) {
+ VoidExtentData data;
+ data.r = 0;
+ data.g = 0;
+ data.b = 0;
+ data.a = 0;
+ for (auto& coord : data.coords) {
+ coord = (1 << 13) - 1;
+ }
+
+ base::UInt128 packed;
+ auto error_str = Pack(data, &packed);
+ ASSERT_FALSE(error_str) << (error_str ? error_str.value() : std::string(""));
+ EXPECT_EQ(packed, 0xFFFFFFFFFFFFFDFCULL);
+
+ data.r = 0xbeef;
+ data.g = 0xdead;
+ data.b = 0xbeef;
+ data.a = 0xdead;
+ data.coords[0] = 0;
+ data.coords[1] = 8191;
+ data.coords[2] = 0;
+ data.coords[3] = 8191;
+
+ error_str = Pack(data, &packed);
+ ASSERT_FALSE(error_str) << (error_str ? error_str.value() : std::string(""));
+ EXPECT_EQ(packed,
+ base::UInt128(0xdeadbeefdeadbeefULL, 0xFFF8003FFE000DFCULL));
+}
+
+// Make sure that the color endpoint mode is properly repacked. This test case
+// was created as a bug during testing.
+TEST(IntermediateASTCBlockTest, TestPackUnpackWithSameCEM) {
+ base::UInt128 orig(0xe8e8eaea20000980ULL, 0x20000200cb73f045ULL);
+
+ auto b = UnpackIntermediateBlock(PhysicalASTCBlock(orig));
+ ASSERT_TRUE(b);
+
+ base::UInt128 repacked;
+ auto err_str = Pack(b.value(), &repacked);
+ ASSERT_FALSE(err_str) << (err_str ? err_str.value() : std::string(""));
+
+ EXPECT_EQ(repacked, orig);
+
+ // Test case #2
+ orig = base::UInt128(0x3300c30700cb01c5ULL, 0x0573907b8c0f6879ULL);
+ b = UnpackIntermediateBlock(PhysicalASTCBlock(orig));
+ ASSERT_TRUE(b);
+
+ err_str = Pack(b.value(), &repacked);
+ ASSERT_FALSE(err_str) << (err_str ? err_str.value() : std::string(""));
+ EXPECT_EQ(repacked, orig);
+}
+
+// Test that we can encode/decode a block that uses a very large gap
+// between weight and endpoint data.
+TEST(IntermediateASTCBlockTest, TestPackingWithLargeGap) {
+ // We can construct this block by doing the following:
+ // -- choose a block mode that only gives 24 weight bits
+ // -- choose the smallest endpoint mode: grayscale direct
+ // -- make sure there are no partitions
+ const base::UInt128 orig(0xBEDEAD0000000000ULL, 0x0000000001FE032EULL);
+ const auto b = UnpackIntermediateBlock(PhysicalASTCBlock(orig));
+ ASSERT_TRUE(b);
+
+ const auto& data = b.value();
+ EXPECT_EQ(data.weight_grid_dim_x, 2);
+ EXPECT_EQ(data.weight_grid_dim_y, 3);
+ EXPECT_EQ(data.weight_range, 15);
+
+ EXPECT_FALSE(data.partition_id);
+ EXPECT_FALSE(data.dual_plane_channel);
+
+ ASSERT_EQ(data.endpoints.size(), 1);
+ EXPECT_EQ(data.endpoints.at(0).mode, ColorEndpointMode::kLDRLumaDirect);
+
+ ASSERT_EQ(data.endpoints.at(0).colors.size(), 2);
+ EXPECT_EQ(data.endpoints.at(0).colors.at(0), 255);
+ EXPECT_EQ(data.endpoints.at(0).colors.at(1), 0);
+
+ // Now encode it again
+ base::UInt128 repacked;
+ const auto err_str = Pack(b.value(), &repacked);
+ EXPECT_EQ(orig, repacked) << (err_str ? err_str.value() : std::string(""));
+}
+
+// Take a block that is encoded using direct luma with full byte values and see
+// if we properly set the endpoint range.
+TEST(IntermediateASTCBlockTest, TestEndpointRange) {
+ PhysicalASTCBlock blk(0x0000000001FE000173ULL);
+ EXPECT_THAT(blk.ColorValuesRange(), Optional(Eq(255)));
+
+ auto b = UnpackIntermediateBlock(blk);
+ ASSERT_TRUE(b);
+
+ const auto& data = b.value();
+ ASSERT_THAT(data.endpoints, SizeIs(1));
+ EXPECT_THAT(data.endpoints[0].mode, Eq(ColorEndpointMode::kLDRLumaDirect));
+ EXPECT_THAT(data.endpoints[0].colors, ElementsAre(0, 255));
+ EXPECT_THAT(data.endpoint_range, Optional(Eq(255)));
+}
+
+struct ImageTestParams {
+ std::string image_name;
+ int checkered_dim;
+};
+
+static void PrintTo(const ImageTestParams& params, std::ostream* os) {
+ *os << "ImageTestParams(" << params.image_name << ")";
+}
+
+class IntermediateASTCBlockTest : public TestWithParam<ImageTestParams> { };
+
+// Test whether or not a real-world ASTC implementation can be unpacked and
+// then repacked into the same implementation. In conjunction with the other
+// tests, we make sure that we can recreate ASTC blocks that we have previously
+// unpacked.
+TEST_P(IntermediateASTCBlockTest, TestPackUnpack) {
+ const auto& params = GetParam();
+ const int astc_dim = 8;
+ const int img_dim = params.checkered_dim * astc_dim;
+ const std::string astc = LoadASTCFile(params.image_name);
+
+ // Make sure that unpacking and repacking all of the blocks works...
+ const int kNumASTCBlocks = (img_dim / astc_dim) * (img_dim / astc_dim);
+ for (int i = 0; i < kNumASTCBlocks; ++i) {
+ base::UInt128 block_bits;
+ memcpy(&block_bits, astc.data() + PhysicalASTCBlock::kSizeInBytes * i,
+ PhysicalASTCBlock::kSizeInBytes);
+
+ const PhysicalASTCBlock block(block_bits);
+
+ base::UInt128 repacked;
+ if (block.IsVoidExtent()) {
+ auto b = UnpackVoidExtent(block);
+ ASSERT_TRUE(b);
+
+ auto err_str = Pack(b.value(), &repacked);
+ ASSERT_FALSE(err_str) << (err_str ? err_str.value() : std::string(""));
+ } else {
+ auto b = UnpackIntermediateBlock(block);
+ ASSERT_TRUE(b);
+
+ // Check to see that we properly set the endpoint range when we decoded
+ // the block.
+ auto& block_data = b.value();
+ EXPECT_EQ(block_data.endpoint_range, block.ColorValuesRange());
+
+ // Reset the endpoint range here to see if we correctly reconstruct it
+ // below
+ block_data.endpoint_range = {};
+
+ auto err_str = Pack(b.value(), &repacked);
+ ASSERT_FALSE(err_str) << (err_str ? err_str.value() : std::string(""));
+ }
+
+ // You would expect the following line to be enough:
+ // EXPECT_EQ(repacked, block.GetBlockBits())
+ // ... except that the ASTC encoder makes some interesting decisions
+ // about how to encode the same logical bits. One example is that
+ // sometimes if all partitions share an endpoint mode, the encoded
+ // block will not use the shared CEM mode, and rather list each
+ // partition's mode explicitly. For that reason, we just need to make as
+ // close of an approximation as possible that we decode to the same
+ // physical values.
+
+ PhysicalASTCBlock pb(repacked);
+ ASSERT_FALSE(pb.IsIllegalEncoding());
+
+ base::UInt128 pb_color_mask =
+ (base::UInt128(1) << pb.NumColorBits().value()) - 1;
+ base::UInt128 pb_color_bits =
+ pb.GetBlockBits() >> pb.ColorStartBit().value();
+ pb_color_bits &= pb_color_mask;
+
+ base::UInt128 b_color_mask =
+ (base::UInt128(1) << pb.NumColorBits().value()) - 1;
+ base::UInt128 b_color_bits =
+ block.GetBlockBits() >> block.ColorStartBit().value();
+ b_color_bits &= b_color_mask;
+
+ EXPECT_EQ(pb_color_mask, b_color_mask);
+ EXPECT_EQ(pb_color_bits, b_color_bits);
+
+ EXPECT_EQ(pb.IsVoidExtent(), block.IsVoidExtent());
+ EXPECT_EQ(pb.VoidExtentCoords(), block.VoidExtentCoords());
+
+ EXPECT_EQ(pb.WeightGridDims(), block.WeightGridDims());
+ EXPECT_EQ(pb.WeightRange(), block.WeightRange());
+ EXPECT_EQ(pb.NumWeightBits(), block.NumWeightBits());
+ EXPECT_EQ(pb.WeightStartBit(), block.WeightStartBit());
+
+ EXPECT_EQ(pb.IsDualPlane(), block.IsDualPlane());
+ EXPECT_EQ(pb.DualPlaneChannel(), block.DualPlaneChannel());
+
+ EXPECT_EQ(pb.NumPartitions(), block.NumPartitions());
+ EXPECT_EQ(pb.PartitionID(), block.PartitionID());
+
+ EXPECT_EQ(pb.NumColorValues(), block.NumColorValues());
+ EXPECT_EQ(pb.ColorValuesRange(), block.ColorValuesRange());
+
+ for (int j = 0; j < pb.NumPartitions().valueOr(0); ++j) {
+ EXPECT_EQ(pb.GetEndpointMode(j), block.GetEndpointMode(j));
+ }
+ }
+}
+
+std::vector<ImageTestParams> GetImageTestParams() {
+ return {
+ // image_name checkered_dim
+ { "checkered_4", 4 },
+ { "checkered_5", 5 },
+ { "checkered_6", 6 },
+ { "checkered_7", 7 },
+ { "checkered_8", 8 },
+ { "checkered_9", 9 },
+ { "checkered_10", 10 },
+ { "checkered_11", 11 },
+ { "checkered_12", 12 },
+ };
+}
+
+INSTANTIATE_TEST_CASE_P(Checkered, IntermediateASTCBlockTest,
+ ValuesIn(GetImageTestParams()));
+
+} // namespace
+
+} // namespace astc_codec