• 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/intermediate_astc_block.h"
16 #include "src/decoder/test/image_utils.h"
17 
18 #include <gmock/gmock.h>
19 #include <gtest/gtest.h>
20 
21 #include <string>
22 
23 namespace astc_codec {
24 
25 namespace {
26 
27 using ::testing::ElementsAre;
28 using ::testing::Eq;
29 using ::testing::HasSubstr;
30 using ::testing::SizeIs;
31 using ::testing::TestWithParam;
32 using ::testing::ValuesIn;
33 
34 // Test to make sure that unpacking an error block returns false.
TEST(IntermediateASTCBlockTest,TestUnpackError)35 TEST(IntermediateASTCBlockTest, TestUnpackError) {
36   const PhysicalASTCBlock kErrorBlock(base::UInt128(0));
37   EXPECT_FALSE(UnpackVoidExtent(kErrorBlock));
38   EXPECT_FALSE(UnpackIntermediateBlock(kErrorBlock));
39 }
40 
41 // Test to make sure that if we don't populate our weight data in the
42 // intermediate block than the resulting color range should error due to the
43 // mismatch.
TEST(IntermediateASTCBlockTest,TestEndpointRangeErrorOnNotSettingWeights)44 TEST(IntermediateASTCBlockTest, TestEndpointRangeErrorOnNotSettingWeights) {
45   IntermediateBlockData data;
46   data.weight_range = 15;
47   for (auto& ep : data.endpoints) {
48     ep.mode = ColorEndpointMode::kLDRRGBDirect;
49   }
50   data.weight_grid_dim_x = 6;
51   data.weight_grid_dim_y = 6;
52   EXPECT_EQ(-1, EndpointRangeForBlock(data));
53 
54   base::UInt128 dummy;
55   auto err_str = Pack(data, &dummy);
56   EXPECT_TRUE(err_str.hasValue());
57   EXPECT_THAT(err_str.value(), HasSubstr("Incorrect number of weights"));
58 }
59 
60 // Test to make sure that if we run out of bits, then we should say so.
TEST(IntermediateASTCBlockTest,TestEndpointRangeErrorOnNotEnoughBits)61 TEST(IntermediateASTCBlockTest, TestEndpointRangeErrorOnNotEnoughBits) {
62   IntermediateBlockData data;
63   data.weight_range = 1;
64   data.partition_id = 0;
65   data.endpoints.resize(3);
66   for (auto& ep : data.endpoints) {
67     ep.mode = ColorEndpointMode::kLDRRGBDirect;
68   }
69   data.weight_grid_dim_x = 8;
70   data.weight_grid_dim_y = 8;
71   EXPECT_EQ(-2, EndpointRangeForBlock(data));
72 
73   // Resize the weights to get past the error that they do not match the grid
74   // dimensions.
75   data.weights.resize(64);
76 
77   base::UInt128 dummy;
78   auto err_str = Pack(data, &dummy);
79   EXPECT_TRUE(err_str.hasValue());
80   EXPECT_THAT(err_str.value(), HasSubstr("illegal color range"));
81 }
82 
83 // Test to make sure that as we increase the number of weights, we decrease the
84 // allowable range of colors
TEST(IntermediateASTCBlockTest,TestEndpointRangeForBlock)85 TEST(IntermediateASTCBlockTest, TestEndpointRangeForBlock) {
86   IntermediateBlockData data;
87   data.weight_range = 2;
88   data.endpoints.resize(2);
89   data.dual_plane_channel.clear();
90   for (auto& ep : data.endpoints) {
91     ep.mode = ColorEndpointMode::kLDRRGBDirect;
92   }
93 
94   // Weight params control how many weights are present in a block
95   struct WeightParams {
96     int width;
97     int height;
98 
99     // We should sort based on number of weights for these params
100     int NumWeights() const { return width * height; }
101     bool operator<(const WeightParams& other) const {
102       return NumWeights() < other.NumWeights();
103     }
104   };
105 
106   std::vector<WeightParams> weight_params;
107   for (int y = 2; y < 8; ++y) {
108     for (int x = 2; x < 8; ++x) {
109       weight_params.emplace_back(WeightParams{x, y});
110     }
111   }
112 
113   // Sort weights from fewest to largest such that the allowable color range
114   // should be monotonically decreasing
115   std::sort(weight_params.begin(), weight_params.end());
116 
117   // Keep track of the largest available color range and measure that it
118   // decreases as we add more weights to our block
119   int last_color_range = 255;
120   for (const auto& params : weight_params) {
121     data.weight_grid_dim_x = params.width;
122     data.weight_grid_dim_y = params.height;
123 
124     const int color_range = EndpointRangeForBlock(data);
125     EXPECT_LE(color_range, last_color_range);
126     last_color_range = std::min(color_range, last_color_range);
127   }
128 
129   // Make sure that we actually changed it at some point.
130   EXPECT_LT(last_color_range, 255);
131 }
132 
133 // Test to make sure that unpacking an legitimate ASTC block returns the encoded
134 // values that we expect.
TEST(IntermediateASTCBlockTest,TestUnpackNonVoidExtentBlock)135 TEST(IntermediateASTCBlockTest, TestUnpackNonVoidExtentBlock) {
136   PhysicalASTCBlock blk(0x0000000001FE000173ULL);
137   auto b = UnpackIntermediateBlock(blk);
138   ASSERT_TRUE(b);
139 
140   const auto& data = b.value();
141   EXPECT_EQ(data.weight_grid_dim_x, 6);
142   EXPECT_EQ(data.weight_grid_dim_y, 5);
143   EXPECT_EQ(data.weight_range, 7);
144 
145   EXPECT_FALSE(data.partition_id);
146   EXPECT_FALSE(data.dual_plane_channel);
147 
148   ASSERT_EQ(data.weights.size(), 30);
149   for (auto weight : data.weights) {
150     EXPECT_EQ(weight, 0);
151   }
152 
153   ASSERT_EQ(data.endpoints.size(), 1);
154   for (const auto& ep_data : data.endpoints) {
155     EXPECT_EQ(ep_data.mode, ColorEndpointMode::kLDRLumaDirect);
156     ASSERT_EQ(ep_data.colors.size(), 2);
157     EXPECT_EQ(ep_data.colors[0], 0);
158     EXPECT_EQ(ep_data.colors[1], 255);
159   }
160 }
161 
162 // Make sure that we can pack blocks that aren't void extent blocks. (In other
163 // words, can we actually deal with intermediate ASTC data).
TEST(IntermediateASTCBlockTest,TestPackNonVoidExtentBlock)164 TEST(IntermediateASTCBlockTest, TestPackNonVoidExtentBlock) {
165   IntermediateBlockData data;
166 
167   data.weight_grid_dim_x = 6;
168   data.weight_grid_dim_y = 5;
169   data.weight_range = 7;
170 
171   data.partition_id = {};
172   data.dual_plane_channel = {};
173 
174   data.weights.resize(30);
175   for (auto& weight : data.weights) {
176     weight = 0;
177   }
178 
179   data.endpoints.resize(1);
180   for (auto& ep_data : data.endpoints) {
181     ep_data.mode = ColorEndpointMode::kLDRLumaDirect;
182     ep_data.colors.resize(2);
183     ep_data.colors[0] = 0;
184     ep_data.colors[1] = 255;
185   }
186 
187   base::UInt128 packed;
188   auto error_str = Pack(data, &packed);
189   ASSERT_FALSE(error_str) << (error_str ? error_str.value() : std::string(""));
190   EXPECT_EQ(packed, 0x0000000001FE000173ULL);
191 }
192 
193 // Make sure that we can unpack void extent blocks
TEST(IntermediateASTCBlockTest,TestUnpackVoidExtentBlock)194 TEST(IntermediateASTCBlockTest, TestUnpackVoidExtentBlock) {
195   PhysicalASTCBlock void_extent_block(0xFFFFFFFFFFFFFDFCULL);
196 
197   auto b = UnpackVoidExtent(void_extent_block);
198   ASSERT_TRUE(b);
199 
200   const auto& data = b.value();
201   EXPECT_EQ(data.r, 0);
202   EXPECT_EQ(data.g, 0);
203   EXPECT_EQ(data.b, 0);
204   EXPECT_EQ(data.a, 0);
205   for (const auto& coord : data.coords) {
206     EXPECT_EQ(coord, (1 << 13) - 1);
207   }
208 
209   base::UInt128 more_interesting(0xdeadbeefdeadbeefULL, 0xFFF8003FFE000DFCULL);
210   b = UnpackVoidExtent(PhysicalASTCBlock(more_interesting));
211   ASSERT_TRUE(b);
212 
213   const auto& other_data = b.value();
214   EXPECT_EQ(other_data.r, 0xbeef);
215   EXPECT_EQ(other_data.g, 0xdead);
216   EXPECT_EQ(other_data.b, 0xbeef);
217   EXPECT_EQ(other_data.a, 0xdead);
218   EXPECT_EQ(other_data.coords[0], 0);
219   EXPECT_EQ(other_data.coords[1], 8191);
220   EXPECT_EQ(other_data.coords[2], 0);
221   EXPECT_EQ(other_data.coords[3], 8191);
222 }
223 
224 // Make sure that we can pack void extent blocks and void extent data.
TEST(IntermediateASTCBlockTest,TestPackVoidExtentBlock)225 TEST(IntermediateASTCBlockTest, TestPackVoidExtentBlock) {
226   VoidExtentData data;
227   data.r = 0;
228   data.g = 0;
229   data.b = 0;
230   data.a = 0;
231   for (auto& coord : data.coords) {
232     coord = (1 << 13) - 1;
233   }
234 
235   base::UInt128 packed;
236   auto error_str = Pack(data, &packed);
237   ASSERT_FALSE(error_str) << (error_str ? error_str.value() : std::string(""));
238   EXPECT_EQ(packed, 0xFFFFFFFFFFFFFDFCULL);
239 
240   data.r = 0xbeef;
241   data.g = 0xdead;
242   data.b = 0xbeef;
243   data.a = 0xdead;
244   data.coords[0] = 0;
245   data.coords[1] = 8191;
246   data.coords[2] = 0;
247   data.coords[3] = 8191;
248 
249   error_str = Pack(data, &packed);
250   ASSERT_FALSE(error_str) << (error_str ? error_str.value() : std::string(""));
251   EXPECT_EQ(packed,
252             base::UInt128(0xdeadbeefdeadbeefULL, 0xFFF8003FFE000DFCULL));
253 }
254 
255 // Make sure that the color endpoint mode is properly repacked. This test case
256 // was created as a bug during testing.
TEST(IntermediateASTCBlockTest,TestPackUnpackWithSameCEM)257 TEST(IntermediateASTCBlockTest, TestPackUnpackWithSameCEM) {
258   base::UInt128 orig(0xe8e8eaea20000980ULL, 0x20000200cb73f045ULL);
259 
260   auto b = UnpackIntermediateBlock(PhysicalASTCBlock(orig));
261   ASSERT_TRUE(b);
262 
263   base::UInt128 repacked;
264   auto err_str = Pack(b.value(), &repacked);
265   ASSERT_FALSE(err_str) << (err_str ? err_str.value() : std::string(""));
266 
267   EXPECT_EQ(repacked, orig);
268 
269   // Test case #2
270   orig = base::UInt128(0x3300c30700cb01c5ULL, 0x0573907b8c0f6879ULL);
271   b = UnpackIntermediateBlock(PhysicalASTCBlock(orig));
272   ASSERT_TRUE(b);
273 
274   err_str = Pack(b.value(), &repacked);
275   ASSERT_FALSE(err_str) << (err_str ? err_str.value() : std::string(""));
276   EXPECT_EQ(repacked, orig);
277 }
278 
279 // Test that we can encode/decode a block that uses a very large gap
280 // between weight and endpoint data.
TEST(IntermediateASTCBlockTest,TestPackingWithLargeGap)281 TEST(IntermediateASTCBlockTest, TestPackingWithLargeGap) {
282   // We can construct this block by doing the following:
283   //   -- choose a block mode that only gives 24 weight bits
284   //   -- choose the smallest endpoint mode: grayscale direct
285   //   -- make sure there are no partitions
286   const base::UInt128 orig(0xBEDEAD0000000000ULL, 0x0000000001FE032EULL);
287   const auto b = UnpackIntermediateBlock(PhysicalASTCBlock(orig));
288   ASSERT_TRUE(b);
289 
290   const auto& data = b.value();
291   EXPECT_EQ(data.weight_grid_dim_x, 2);
292   EXPECT_EQ(data.weight_grid_dim_y, 3);
293   EXPECT_EQ(data.weight_range, 15);
294 
295   EXPECT_FALSE(data.partition_id);
296   EXPECT_FALSE(data.dual_plane_channel);
297 
298   ASSERT_EQ(data.endpoints.size(), 1);
299   EXPECT_EQ(data.endpoints.at(0).mode, ColorEndpointMode::kLDRLumaDirect);
300 
301   ASSERT_EQ(data.endpoints.at(0).colors.size(), 2);
302   EXPECT_EQ(data.endpoints.at(0).colors.at(0), 255);
303   EXPECT_EQ(data.endpoints.at(0).colors.at(1), 0);
304 
305   // Now encode it again
306   base::UInt128 repacked;
307   const auto err_str = Pack(b.value(), &repacked);
308   EXPECT_EQ(orig, repacked) << (err_str ? err_str.value() : std::string(""));
309 }
310 
311 // Take a block that is encoded using direct luma with full byte values and see
312 // if we properly set the endpoint range.
TEST(IntermediateASTCBlockTest,TestEndpointRange)313 TEST(IntermediateASTCBlockTest, TestEndpointRange) {
314   PhysicalASTCBlock blk(0x0000000001FE000173ULL);
315   EXPECT_TRUE(blk.ColorValuesRange().hasValue());
316   EXPECT_EQ(blk.ColorValuesRange().valueOr(0), 255);
317 
318   auto b = UnpackIntermediateBlock(blk);
319   ASSERT_TRUE(b);
320 
321   const auto& data = b.value();
322   ASSERT_THAT(data.endpoints, SizeIs(1));
323   EXPECT_THAT(data.endpoints[0].mode, Eq(ColorEndpointMode::kLDRLumaDirect));
324   EXPECT_THAT(data.endpoints[0].colors, ElementsAre(0, 255));
325   EXPECT_TRUE(data.endpoint_range.hasValue());
326   EXPECT_EQ(data.endpoint_range.valueOr(0), 255);
327 }
328 
329 struct ImageTestParams {
330   std::string image_name;
331   int checkered_dim;
332 };
333 
PrintTo(const ImageTestParams & params,std::ostream * os)334 static void PrintTo(const ImageTestParams& params, std::ostream* os) {
335   *os << "ImageTestParams(" << params.image_name << ")";
336 }
337 
338 class IntermediateASTCBlockTest : public TestWithParam<ImageTestParams> { };
339 
340 // Test whether or not a real-world ASTC implementation can be unpacked and
341 // then repacked into the same implementation. In conjunction with the other
342 // tests, we make sure that we can recreate ASTC blocks that we have previously
343 // unpacked.
TEST_P(IntermediateASTCBlockTest,TestPackUnpack)344 TEST_P(IntermediateASTCBlockTest, TestPackUnpack) {
345   const auto& params = GetParam();
346   const int astc_dim = 8;
347   const int img_dim = params.checkered_dim * astc_dim;
348   const std::string astc = LoadASTCFile(params.image_name);
349 
350   // Make sure that unpacking and repacking all of the blocks works...
351   const int kNumASTCBlocks = (img_dim / astc_dim) * (img_dim / astc_dim);
352   for (int i = 0; i < kNumASTCBlocks; ++i) {
353     base::UInt128 block_bits;
354     memcpy(&block_bits, astc.data() + PhysicalASTCBlock::kSizeInBytes * i,
355            PhysicalASTCBlock::kSizeInBytes);
356 
357     const PhysicalASTCBlock block(block_bits);
358 
359     base::UInt128 repacked;
360     if (block.IsVoidExtent()) {
361       auto b = UnpackVoidExtent(block);
362       ASSERT_TRUE(b);
363 
364       auto err_str = Pack(b.value(), &repacked);
365       ASSERT_FALSE(err_str) << (err_str ? err_str.value() : std::string(""));
366     } else {
367       auto b = UnpackIntermediateBlock(block);
368       ASSERT_TRUE(b);
369 
370       // Check to see that we properly set the endpoint range when we decoded
371       // the block.
372       auto& block_data = b.value();
373       EXPECT_EQ(block_data.endpoint_range, block.ColorValuesRange());
374 
375       // Reset the endpoint range here to see if we correctly reconstruct it
376       // below
377       block_data.endpoint_range = {};
378 
379       auto err_str = Pack(b.value(), &repacked);
380       ASSERT_FALSE(err_str) << (err_str ? err_str.value() : std::string(""));
381     }
382 
383     // You would expect the following line to be enough:
384     //   EXPECT_EQ(repacked, block.GetBlockBits())
385     // ... except that the ASTC encoder makes some interesting decisions
386     // about how to encode the same logical bits. One example is that
387     // sometimes if all partitions share an endpoint mode, the encoded
388     // block will not use the shared CEM mode, and rather list each
389     // partition's mode explicitly. For that reason, we just need to make as
390     // close of an approximation as possible that we decode to the same
391     // physical values.
392 
393     PhysicalASTCBlock pb(repacked);
394     ASSERT_FALSE(pb.IsIllegalEncoding());
395 
396     base::UInt128 pb_color_mask =
397         (base::UInt128(1) << pb.NumColorBits().value()) - 1;
398     base::UInt128 pb_color_bits =
399         pb.GetBlockBits() >> pb.ColorStartBit().value();
400     pb_color_bits &= pb_color_mask;
401 
402     base::UInt128 b_color_mask =
403         (base::UInt128(1) << pb.NumColorBits().value()) - 1;
404     base::UInt128 b_color_bits =
405         block.GetBlockBits() >> block.ColorStartBit().value();
406     b_color_bits &= b_color_mask;
407 
408     EXPECT_EQ(pb_color_mask, b_color_mask);
409     EXPECT_EQ(pb_color_bits, b_color_bits);
410 
411     EXPECT_EQ(pb.IsVoidExtent(), block.IsVoidExtent());
412     EXPECT_EQ(pb.VoidExtentCoords(), block.VoidExtentCoords());
413 
414     EXPECT_EQ(pb.WeightGridDims(), block.WeightGridDims());
415     EXPECT_EQ(pb.WeightRange(), block.WeightRange());
416     EXPECT_EQ(pb.NumWeightBits(), block.NumWeightBits());
417     EXPECT_EQ(pb.WeightStartBit(), block.WeightStartBit());
418 
419     EXPECT_EQ(pb.IsDualPlane(), block.IsDualPlane());
420     EXPECT_EQ(pb.DualPlaneChannel(), block.DualPlaneChannel());
421 
422     EXPECT_EQ(pb.NumPartitions(), block.NumPartitions());
423     EXPECT_EQ(pb.PartitionID(), block.PartitionID());
424 
425     EXPECT_EQ(pb.NumColorValues(), block.NumColorValues());
426     EXPECT_EQ(pb.ColorValuesRange(), block.ColorValuesRange());
427 
428     for (int j = 0; j < pb.NumPartitions().valueOr(0); ++j) {
429       EXPECT_EQ(pb.GetEndpointMode(j), block.GetEndpointMode(j));
430     }
431   }
432 }
433 
GetImageTestParams()434 std::vector<ImageTestParams> GetImageTestParams() {
435   return {
436     // image_name       checkered_dim
437     { "checkered_4",    4  },
438     { "checkered_5",    5  },
439     { "checkered_6",    6  },
440     { "checkered_7",    7  },
441     { "checkered_8",    8  },
442     { "checkered_9",    9  },
443     { "checkered_10",   10 },
444     { "checkered_11",   11 },
445     { "checkered_12",   12 },
446   };
447 }
448 
449 INSTANTIATE_TEST_CASE_P(Checkered, IntermediateASTCBlockTest,
450                         ValuesIn(GetImageTestParams()));
451 
452 }  // namespace
453 
454 }  // namespace astc_codec
455