• 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/endpoint_codec.h"
16 #include "src/decoder/intermediate_astc_block.h"
17 #include "src/decoder/test/image_utils.h"
18 
19 #include <random>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 
24 #include <gtest/gtest.h>
25 #include <gmock/gmock.h>
26 
27 namespace astc_codec {
28 
29 namespace {
30 
31 using ::testing::AllOf;
32 using ::testing::AnyOf;
33 using ::testing::Each;
34 using ::testing::Eq;
35 using ::testing::Ge;
36 using ::testing::Le;
37 using ::testing::Ne;
38 using ::testing::Pointwise;
39 using ::testing::SizeIs;
40 using ::testing::Pair;
41 
42 constexpr std::array<EndpointEncodingMode, 6> kEndpointEncodingModes = {{
43     EndpointEncodingMode::kDirectLuma,
44     EndpointEncodingMode::kDirectLumaAlpha,
45     EndpointEncodingMode::kBaseScaleRGB,
46     EndpointEncodingMode::kBaseScaleRGBA,
47     EndpointEncodingMode::kDirectRGB,
48     EndpointEncodingMode::kDirectRGBA }};
49 
50 const std::array<std::pair<RgbaColor, RgbaColor>, 3> kBlueContractPairs = {{
51     std::make_pair(RgbaColor{{ 22, 18, 30, 59 }},
52                    RgbaColor{{ 162, 148, 155, 59 }}),
53     std::make_pair(RgbaColor{{ 22, 30, 27, 36 }},
54                    RgbaColor{{ 228, 221, 207, 36 }}),
55     std::make_pair(RgbaColor{{ 54, 60, 55, 255 }},
56                    RgbaColor{{ 23, 30, 27, 255 }})
57   }};
58 
59 // Used to directly initialize std::pairs of colors with initializer lists
60 //   e.g. MakeColors({{ r, g, b, a }}, {{ r, g, b, a }});
MakeColors(RgbaColor && a,RgbaColor && b)61 std::pair<RgbaColor, RgbaColor> MakeColors(RgbaColor&& a, RgbaColor&& b) {
62   return std::make_pair(a, b);
63 }
64 
65 // Returns |high| and |low| as they would be decoded using the quantization
66 // factor |quant| for the ColorEndpointMode |mode|.
TestColors(RgbaColor low,RgbaColor high,int quant,EndpointEncodingMode mode)67 std::pair<RgbaColor, RgbaColor> TestColors(
68     RgbaColor low, RgbaColor high, int quant, EndpointEncodingMode mode) {
69   ColorEndpointMode astc_mode;
70   std::vector<int> encoded;
71   const bool needs_swap =
72       EncodeColorsForMode(low, high, quant, mode, &astc_mode, &encoded);
73 
74   RgbaColor decoded_low, decoded_high;
75   DecodeColorsForMode(encoded, quant, astc_mode, &decoded_low, &decoded_high);
76 
77   if (needs_swap) {
78     return std::make_pair(decoded_high, decoded_low);
79   } else {
80     return std::make_pair(decoded_low, decoded_high);
81   }
82 }
83 
84 // Returns true if the argument tuple entries only differ by at most x.
85 MATCHER_P(IsCloseTo, x, "") {
86   const auto& a = ::testing::get<0>(arg);
87   const auto& b = ::testing::get<1>(arg);
88   return (a > b) ? ((a - b) <= x) : ((b - a) <= x);
89 }
90 
91 // Test to make sure that the range of values that we get as they are
92 // quantized remains within what we pass as |quant|.
TEST(EndpointCodecTest,QuantRanges)93 TEST(EndpointCodecTest, QuantRanges) {
94   const RgbaColor low {{ 0, 0, 0, 0 }};
95   const RgbaColor high {{ 255, 255, 255, 255 }};
96 
97   std::vector<int> result;
98   for (const auto& mode : kEndpointEncodingModes) {
99     for (int i = 5; i < 256; ++i) {
100       ColorEndpointMode astc_mode;
101       const bool needs_swap =
102           EncodeColorsForMode(low, high, i, mode, &astc_mode, &result);
103       EXPECT_EQ(result.size(), NumValuesForEncodingMode(mode)) << i;
104       EXPECT_EQ(result.size(), NumColorValuesForEndpointMode(astc_mode)) << i;
105 
106       // ASTC mode shouldn't use base/offset when endpoints are so far apart.
107       EXPECT_THAT(astc_mode, Ne(ColorEndpointMode::kLDRRGBBaseOffset));
108       EXPECT_THAT(astc_mode, Ne(ColorEndpointMode::kLDRRGBABaseOffset));
109 
110       EXPECT_THAT(result, Each(AllOf(Ge(0), Le(i))))
111           << "Mode: " << static_cast<int>(mode);
112       // We don't care if we need to swap the weights in this test
113       EXPECT_TRUE(needs_swap || !needs_swap);
114     }
115   }
116 }
117 
118 // Test to make sure that each mode that directly encodes colors can effectively
119 // encode both black and white
TEST(EndpointCodecTest,ExtremeDirectEncodings)120 TEST(EndpointCodecTest, ExtremeDirectEncodings) {
121   const RgbaColor kWhite {{ 255, 255, 255, 255 }};
122   const RgbaColor kBlack {{ 0, 0, 0, 255 }};
123 
124   std::vector<int> encoded;
125   for (const auto& mode : kEndpointEncodingModes) {
126     for (int i = 5; i < 256; ++i) {
127       const auto expected = std::make_pair(kWhite, kBlack);
128       EXPECT_EQ(TestColors(kWhite, kBlack, i, mode), expected)
129           << "Range: " << i << ", Mode: " << static_cast<int>(mode);
130     }
131   }
132 }
133 
134 // According to the spec, this is used for colors close to gray. The values
135 // chosen here were according to the spec.
TEST(EndpointCodecTest,UsesBlueContract)136 TEST(EndpointCodecTest, UsesBlueContract) {
137   std::vector<int> vals = { 132, 127, 116, 112, 183, 180, 31, 22 };
138   EXPECT_TRUE(UsesBlueContract(255, ColorEndpointMode::kLDRRGBDirect, vals));
139   EXPECT_TRUE(UsesBlueContract(255, ColorEndpointMode::kLDRRGBADirect, vals));
140 
141   // For the offset modes the only way to trigger the blue contract mode is if
142   // we force the subtraction in the decoding procedure (See section C.2.14 of
143   // the spec), so we need to set the 7th bit to 1 for all of the odd-numbered
144   // values
145   vals[1] &= 0xBF;
146   vals[3] &= 0xBF;
147   vals[5] &= 0xBF;
148   vals[7] &= 0xBF;
149 
150   EXPECT_FALSE(
151       UsesBlueContract(255, ColorEndpointMode::kLDRRGBBaseOffset, vals));
152   EXPECT_FALSE(
153       UsesBlueContract(255, ColorEndpointMode::kLDRRGBABaseOffset, vals));
154 
155   vals[1] |= 0x40;
156   vals[3] |= 0x40;
157   vals[5] |= 0x40;
158   vals[7] |= 0x40;
159 
160   EXPECT_TRUE(
161       UsesBlueContract(255, ColorEndpointMode::kLDRRGBBaseOffset, vals));
162   EXPECT_TRUE(
163       UsesBlueContract(255, ColorEndpointMode::kLDRRGBABaseOffset, vals));
164 
165   // All other LDR endpoint modes should return no blue contract
166   for (int max_val : { 255, 127, 11 }) {
167     for (auto mode : { ColorEndpointMode::kLDRLumaDirect,
168             ColorEndpointMode::kLDRLumaBaseOffset,
169             ColorEndpointMode::kLDRLumaAlphaDirect,
170             ColorEndpointMode::kLDRLumaAlphaBaseOffset,
171             ColorEndpointMode::kLDRRGBBaseScale,
172             ColorEndpointMode::kLDRRGBBaseScaleTwoA }) {
173       EXPECT_FALSE(UsesBlueContract(max_val, mode, vals));
174     }
175   }
176 }
177 
178 // Make sure that encoding and decoding for the direct luminance mode works.
TEST(EndpointCodecTest,LumaDirect)179 TEST(EndpointCodecTest, LumaDirect) {
180   const auto mode = EndpointEncodingMode::kDirectLuma;
181 
182   // With a 255 quantizer, all greys should be exact.
183   for (int i = 0; i < 255; ++i) {
184     for (int j = 0; j < 255; ++j) {
185       EXPECT_EQ(TestColors({{ i, i, i, 255 }}, {{ j, j, j, 255 }}, 255, mode),
186                 MakeColors({{ i, i, i, 255 }}, {{ j, j, j, 255 }}));
187     }
188   }
189 
190   // If we have almost grey, then they should encode to grey.
191   EXPECT_EQ(TestColors({{ 247, 248, 246, 255 }}, {{ 2, 3, 1, 255 }}, 255, mode),
192             MakeColors({{ 247, 247, 247, 255 }}, {{ 2, 2, 2, 255 }}));
193 
194   EXPECT_EQ(TestColors({{ 80, 80, 50, 255 }}, {{ 99, 255, 6, 255 }}, 255, mode),
195             MakeColors({{ 70, 70, 70, 255 }}, {{ 120, 120, 120, 255 }}));
196 
197   // If we have almost greys and a really small quantizer, it should be white
198   // and black (literally).
199   EXPECT_EQ(TestColors({{ 247, 248, 246, 255 }}, {{ 2, 3, 1, 255 }}, 15, mode),
200             MakeColors({{ 255, 255, 255, 255 }}, {{ 0, 0, 0, 255 }}));
201 
202   // The average of 64, 127, and 192 is 127.666..., so it should round to
203   // 130 instead of 125.
204   EXPECT_EQ(TestColors({{ 64, 127, 192, 255 }}, {{ 0, 0, 0, 255 }}, 63, mode),
205             MakeColors({{ 130, 130, 130, 255 }}, {{ 0, 0, 0, 255 }}));
206 
207   // If we have almost grey, then they should encode to grey -- similar to
208   // direct encoding since the encoded colors differ by < 63.
209   EXPECT_EQ(TestColors({{ 80, 80, 50, 255 }}, {{ 99, 255, 6, 255 }}, 255, mode),
210             MakeColors({{ 70, 70, 70, 255 }}, {{ 120, 120, 120, 255 }}));
211 
212   // Low precision colors should still encode pretty well with base/offset.
213   EXPECT_EQ(TestColors({{ 35, 36, 38, 255 }}, {{ 42, 43, 40, 255 }}, 47, mode),
214             MakeColors({{ 38, 38, 38, 255 }}, {{ 43, 43, 43, 255 }}));
215 
216   EXPECT_EQ(TestColors({{ 39, 42, 40, 255 }}, {{ 18, 20, 21, 255 }}, 39, mode),
217             MakeColors({{ 39, 39, 39, 255 }}, {{ 19, 19, 19, 255 }}));
218 }
219 
220 // Test encoding and decoding for the base-offset luminance mode.
TEST(EndpointCodecTest,LumaAlphaDirect)221 TEST(EndpointCodecTest, LumaAlphaDirect) {
222   const auto mode = EndpointEncodingMode::kDirectLumaAlpha;
223 
224   // With a 255 quantizer, all greys should be exact.
225   for (int i = 0; i < 255; ++i) {
226     for (int j = 0; j < 255; ++j) {
227       EXPECT_EQ(TestColors({{ i, i, i, j }}, {{ j, j, j, i }}, 255, mode),
228                 MakeColors({{ i, i, i, j }}, {{ j, j, j, i }}));
229     }
230   }
231 
232   // If we have almost grey, then they should encode to grey.
233   EXPECT_EQ(TestColors({{ 247, 248, 246, 250 }}, {{ 2, 3, 1, 172 }}, 255, mode),
234             MakeColors({{ 247, 247, 247, 250 }}, {{ 2, 2, 2, 172 }}));
235 
236   EXPECT_EQ(TestColors({{ 80, 80, 50, 0 }}, {{ 99, 255, 6, 255 }}, 255, mode),
237             MakeColors({{ 70, 70, 70, 0 }}, {{ 120, 120, 120, 255 }}));
238 
239   // If we have almost greys and a really small quantizer, it should be white
240   // and black (literally).
241   EXPECT_EQ(TestColors({{ 247, 248, 246, 253 }}, {{ 2, 3, 1, 3 }}, 15, mode),
242             MakeColors({{ 255, 255, 255, 255 }}, {{ 0, 0, 0, 0 }}));
243 
244   // The average of 64, 127, and 192 is 127.666..., so it should round to
245   // 130 instead of 125. The alpha in this case is independent.
246   EXPECT_EQ(TestColors({{ 64, 127, 192, 127 }}, {{ 0, 0, 0, 20 }}, 63, mode),
247             MakeColors({{ 130, 130, 130, 125 }}, {{ 0, 0, 0, 20 }}));
248 }
249 
250 // Test encoding for the direct RGB mode.
TEST(EndpointCodecTest,RGBDirect)251 TEST(EndpointCodecTest, RGBDirect) {
252   const auto mode = EndpointEncodingMode::kDirectRGB;
253 
254   // Colors should be encoded exactly with a 255 quantizer.
255   std::mt19937 random(0xdeadbeef);
256   std::uniform_int_distribution<int> byte_distribution(0, 255);
257 
258   for (int i = 0; i < 100; ++i) {
259     RgbaColor low, high;
260     for (auto& x : high) { x = byte_distribution(random); }
261     for (auto& x : low) { x = byte_distribution(random); }
262     high[3] = low[3] = 255;  // RGB Direct mode has opaque alpha.
263 
264     EXPECT_EQ(TestColors(low, high, 255, mode), std::make_pair(low, high))
265         << "Random iter: " << i;
266   }
267 
268   // For each of the following tests, order of endpoints shouldn't have any
269   // bearing on the quantization properties, so we should be able to switch
270   // endpoints as we see fit and have them generate the same flipped encoded
271   // pairs.
272 
273   EXPECT_EQ(TestColors({{ 64, 127, 192, 255 }}, {{ 0, 0, 0, 255 }}, 63, mode),
274             MakeColors({{ 65, 125, 190, 255 }}, {{ 0, 0, 0, 255 }}));
275 
276   EXPECT_EQ(TestColors({{ 0, 0, 0, 255 }}, {{ 64, 127, 192, 255 }}, 63, mode),
277             MakeColors({{ 0, 0, 0, 255 }}, {{ 65, 125, 190, 255 }}));
278 
279   EXPECT_EQ(TestColors({{ 1, 2, 94, 255 }}, {{ 168, 255, 13, 255 }}, 7, mode),
280             MakeColors({{ 0, 0, 109, 255 }}, {{ 182, 255, 0, 255 }}));
281 
282   // Colors close to grey will likely need a blue contract.
283   EXPECT_EQ(TestColors(kBlueContractPairs[0].first,
284                        kBlueContractPairs[0].second, 31, mode),
285             MakeColors({{ 24, 20, 33, 255 }}, {{ 160, 148, 156, 255 }}));
286 
287   EXPECT_EQ(TestColors(kBlueContractPairs[0].second,
288                        kBlueContractPairs[0].first, 31, mode),
289             MakeColors({{ 160, 148, 156, 255 }}, {{ 24, 20, 33, 255 }}));
290 
291   EXPECT_EQ(TestColors(kBlueContractPairs[1].first,
292                        kBlueContractPairs[1].second, 7, mode),
293             MakeColors({{ 18, 36, 36, 255 }}, {{ 237, 219, 219, 255 }}));
294 
295   EXPECT_EQ(TestColors(kBlueContractPairs[1].second,
296                        kBlueContractPairs[1].first, 7, mode),
297             MakeColors({{ 237, 219, 219, 255 }}, {{ 18, 36, 36, 255 }}));
298 
299   // Colors close to grey (and each other) will likely need a blue contract AND
300   // use the offset mode for encoding
301   EXPECT_EQ(TestColors(kBlueContractPairs[2].first,
302                        kBlueContractPairs[2].second, 31, mode),
303             MakeColors({{ 53, 59, 53, 255 }}, {{ 24, 30, 26, 255 }}));
304 
305   EXPECT_EQ(TestColors(kBlueContractPairs[2].second,
306                        kBlueContractPairs[2].first, 31, mode),
307             MakeColors({{ 24, 30, 26, 255 }}, {{ 53, 59, 53, 255 }}));
308 
309   // Colors close to each other, but not to grey will likely only use the offset
310   // mode and not the blue-contract modes.
311   EXPECT_EQ(TestColors({{ 22, 148, 30, 59 }}, {{ 162, 18, 155, 59 }}, 31, mode),
312             MakeColors({{ 24, 148, 33, 255 }}, {{ 165, 16, 156, 255 }}));
313 
314   EXPECT_EQ(TestColors({{ 162, 18, 155, 59 }}, {{ 22, 148, 30, 59 }}, 31, mode),
315             MakeColors({{ 165, 16, 156, 255 }}, {{ 24, 148, 33, 255 }}));
316 }
317 
318 // Make sure that certain endpoint pairs result in the blue-contract path as
319 // we'd expect, such that we can make sure that we're hitting all of the encode
320 // paths.
TEST(EndpointCodecTest,RGBDirectMakesBlueContract)321 TEST(EndpointCodecTest, RGBDirectMakesBlueContract) {
322   constexpr int kEndpointRange = 31;
323   for (const auto& endpoint_pair : kBlueContractPairs) {
324     ColorEndpointMode astc_mode;
325     std::vector<int> vals;
326     bool needs_swap = EncodeColorsForMode(
327         endpoint_pair.first, endpoint_pair.second,
328         kEndpointRange, EndpointEncodingMode::kDirectRGB, &astc_mode, &vals);
329     (void)(needs_swap);  // Don't really care.
330 
331     EXPECT_TRUE(UsesBlueContract(kEndpointRange, astc_mode, vals));
332   }
333 }
334 
335 // Make sure that encoding and decoding for the RGB base-scale mode works.
TEST(EndpointCodecTest,RGBBaseScale)336 TEST(EndpointCodecTest, RGBBaseScale) {
337   const auto mode = EndpointEncodingMode::kBaseScaleRGB;
338   const auto close_to = [](RgbaColor c, int x) {
339     return Pointwise(IsCloseTo(x), c);
340   };
341 
342   // Identical colors should be encoded with a 255 scale factor. Since ASTC
343   // decodes the scaled color by doing (x * s) >> 8, the decoded color will be
344   // multiplied by 255/256. This might cause rounding errors sometimes, so we
345   // check that every channel only deviates by 1.
346   std::mt19937 random(0xdeadbeef);
347   std::uniform_int_distribution<int> byte_distribution(0, 255);
348 
349   for (int i = 0; i < 100; ++i) {
350     RgbaColor color{{byte_distribution(random), byte_distribution(random),
351                      byte_distribution(random), 255}};
352     const auto test_result = TestColors(color, color, 255, mode);
353     EXPECT_THAT(test_result, Pair(close_to(color, 1), close_to(color, 1)));
354   }
355 
356   // Make sure that if we want to scale by e.g. 1/4 then we can do that exactly:
357   const RgbaColor low = {{ 20, 4, 40, 255 }};
358   const RgbaColor high = {{ 80, 16, 160, 255 }};
359   EXPECT_THAT(TestColors(low, high, 255, mode),
360               Pair(close_to(low, 0), close_to(high, 0)));
361 
362   // And if we quantize it, then we get roughly the same thing. The scale factor
363   // should be representable with most quantization levels. The problem is that
364   // if we're off on the 'high' color, then we will be off on the 'low' color.
365   EXPECT_THAT(TestColors(low, high, 127, mode),
366               Pair(close_to(low, 1), close_to(high, 1)));
367 
368   EXPECT_THAT(TestColors(low, high, 63, mode),
369               Pair(close_to(low, 1), close_to(high, 2)));
370 
371   EXPECT_THAT(TestColors(low, high, 31, mode),
372               Pair(close_to(low, 1), close_to(high, 4)));
373 
374   EXPECT_THAT(TestColors(low, high, 15, mode),
375               Pair(close_to(low, 2), close_to(high, 8)));
376 }
377 
378 // Make sure that encoding and decoding for the RGB base-offset mode works.
379 // Since we don't have a decoder, this is currently only a test that should work
380 // based on reasoning about what's written in the spec.
381 // TODO(krajcevski): Write an encoder.
TEST(EndpointCodecTest,RGBBaseOffset)382 TEST(EndpointCodecTest, RGBBaseOffset) {
383   const auto test_colors = [](const RgbaColor& low, const RgbaColor& high) {
384     const RgbaColor diff = {{ high[0] - low[0], high[1] - low[1],
385                               high[2] - low[2], high[3] - low[3] }};
386 
387     std::vector<int> vals;
388     for (int i = 0; i < 3; ++i) {
389       // If the base is "large", then it grabs it's most significant bit from
390       // the offset value. Hence, we need to save it here.
391       const bool is_large = low[i] >= 128;
392       vals.push_back((low[i] * 2) & 0xFF);
393       vals.push_back(diff[i] * 2);
394 
395       // Give the "large" bases their bits back.
396       if (is_large) {
397         vals.back() |= 0x80;
398       }
399     }
400 
401     RgbaColor dec_low, dec_high;
402     DecodeColorsForMode(vals, 255, ColorEndpointMode::kLDRRGBBaseOffset,
403                         &dec_low, &dec_high);
404 
405     EXPECT_THAT(std::make_pair(dec_low, dec_high), Pair(Eq(low), Eq(high)));
406   };
407 
408   // Test the "direct encoding" path.
409   test_colors({{ 80, 16, 112, 255 }}, {{ 87, 18, 132, 255 }});
410   test_colors({{ 80, 74, 82, 255 }}, {{ 90, 92, 110, 255 }});
411   test_colors({{ 0, 0, 0, 255 }}, {{ 2, 2, 2, 255 }});
412 
413   // Identical endpoints should always encode exactly, provided they satisfy the
414   // requirements for the base encoding.
415   std::mt19937 random(0xdeadbeef);
416   std::uniform_int_distribution<int> byte_distribution(0, 255);
417   for (int i = 0; i < 100; ++i) {
418     RgbaColor color{{byte_distribution(random), byte_distribution(random),
419                      byte_distribution(random), 255}};
420     if ((color[0] | color[1] | color[2]) & 1) {
421       continue;
422     }
423     test_colors(color, color);
424   }
425 
426   // TODO(google): Test the "blue contract" path.
427 }
428 
429 // Make sure that we can decode colors that are given to us straight out of the
430 // ASTC codec.
TEST(EndpointCodecTest,DecodeCheckerboard)431 TEST(EndpointCodecTest, DecodeCheckerboard) {
432   const RgbaColor kWhite {{ 255, 255, 255, 255 }};
433   const RgbaColor kBlack {{ 0, 0, 0, 255 }};
434 
435   const std::string astc = LoadASTCFile("checkerboard");
436   for (int i = 0; i < astc.size(); i += 16) {
437     base::UInt128 block;
438     memcpy(&block, &astc[i], sizeof(block));
439 
440     const auto intermediate = UnpackIntermediateBlock(PhysicalASTCBlock(block));
441     ASSERT_TRUE(intermediate) << "Block is void extent???";
442 
443     const auto block_data = &intermediate.value();
444     ASSERT_THAT(block_data->endpoints, SizeIs(Eq(1)));
445 
446     const int color_range = EndpointRangeForBlock(*block_data);
447     const auto& endpoints = block_data->endpoints[0];
448 
449     RgbaColor low, high;
450     DecodeColorsForMode(endpoints.colors, color_range, endpoints.mode,
451                         &low, &high);
452 
453     // Expect that the endpoints are black and white, but either order.
454     EXPECT_THAT(std::make_pair(low, high),
455                 AnyOf(
456                     Pair(Eq(kWhite), Eq(kBlack)),
457                     Pair(Eq(kBlack), Eq(kWhite))));
458   }
459 }
460 
461 }  // namespace
462 
463 }  // namespace astc_codec
464