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/codec.h"
16 #include "include/astc-codec/astc-codec.h"
17 #include "src/decoder/test/image_utils.h"
18
19 #include <gtest/gtest.h>
20
21 #include <string>
22
23 namespace astc_codec {
24
PrintTo(FootprintType footprint,std::ostream * os)25 static void PrintTo(FootprintType footprint, std::ostream* os) {
26 switch (footprint) {
27 case FootprintType::k4x4: *os << "FootprintType::k4x4"; break;
28 case FootprintType::k5x4: *os << "FootprintType::k5x4"; break;
29 case FootprintType::k5x5: *os << "FootprintType::k5x5"; break;
30 case FootprintType::k6x5: *os << "FootprintType::k6x5"; break;
31 case FootprintType::k6x6: *os << "FootprintType::k6x6"; break;
32 case FootprintType::k8x5: *os << "FootprintType::k8x5"; break;
33 case FootprintType::k8x6: *os << "FootprintType::k8x6"; break;
34 case FootprintType::k10x5: *os << "FootprintType::k10x5"; break;
35 case FootprintType::k10x6: *os << "FootprintType::k10x6"; break;
36 case FootprintType::k8x8: *os << "FootprintType::k8x8"; break;
37 case FootprintType::k10x8: *os << "FootprintType::k10x8"; break;
38 case FootprintType::k10x10: *os << "FootprintType::k10x10"; break;
39 case FootprintType::k12x10: *os << "FootprintType::k12x10"; break;
40 case FootprintType::k12x12: *os << "FootprintType::k12x12"; break;
41 default:
42 *os << "<Unexpected FootprintType "
43 << static_cast<uint32_t>(footprint) << ">";
44 }
45 }
46
47 namespace {
48
49 using ::testing::TestWithParam;
50 using ::testing::ValuesIn;
51
LoadGoldenImageWithAlpha(std::string basename)52 ImageBuffer LoadGoldenImageWithAlpha(std::string basename) {
53 const std::string filename =
54 std::string("src/decoder/testdata/") + basename + ".bmp";
55 ImageBuffer result;
56 LoadGoldenBmp(filename, &result);
57 EXPECT_EQ(result.BytesPerPixel(), 4);
58 return result;
59 }
60
61 struct ImageTestParams {
62 std::string image_name;
63 FootprintType footprint;
64 size_t width;
65 size_t height;
66 };
67
PrintTo(const ImageTestParams & params,std::ostream * os)68 static void PrintTo(const ImageTestParams& params, std::ostream* os) {
69 *os << "ImageTestParams(" << params.image_name << ", " << params.width << "x"
70 << params.height << ", ";
71 PrintTo(params.footprint, os);
72 *os << ")";
73 }
74
TEST(CodecTest,InvalidInput)75 TEST(CodecTest, InvalidInput) {
76 const size_t valid_width = 16;
77 const size_t valid_height = 16;
78 const size_t valid_stride = valid_width * 4;
79
80 const std::vector<uint8_t> data(256);
81 std::vector<uint8_t> output(valid_width * valid_height * 4);
82
83 // Invalid footprint.
84 EXPECT_FALSE(ASTCDecompressToRGBA(
85 data.data(), data.size(), valid_width, valid_height,
86 FootprintType::kCount, output.data(), output.size(), valid_stride));
87
88 // Fail for 0 width or height.
89 EXPECT_FALSE(ASTCDecompressToRGBA(data.data(), data.size(), 0, valid_height,
90 FootprintType::k4x4, output.data(),
91 output.size(), valid_stride));
92 EXPECT_FALSE(ASTCDecompressToRGBA(data.data(), data.size(), valid_width, 0,
93 FootprintType::k4x4, output.data(),
94 output.size(), valid_stride));
95
96 // Fail for data size that's not a multiple of block size.
97 EXPECT_FALSE(ASTCDecompressToRGBA(
98 data.data(), data.size() - 1, valid_width, valid_height,
99 FootprintType::k4x4, output.data(), output.size(), valid_stride));
100 // Fail for data size that doesn't match the block count.
101 EXPECT_FALSE(ASTCDecompressToRGBA(
102 data.data(), data.size() - 16, valid_width, valid_height,
103 FootprintType::k4x4, output.data(), output.size(), valid_stride));
104
105 // Fail for invalid stride.
106 EXPECT_FALSE(ASTCDecompressToRGBA(
107 data.data(), data.size(), valid_width, valid_height, FootprintType::k4x4,
108 output.data(), output.size(), valid_stride - 1));
109
110 // Fail for invalid output size.
111 EXPECT_FALSE(ASTCDecompressToRGBA(
112 data.data(), data.size(), valid_width, valid_height, FootprintType::k4x4,
113 output.data(), output.size() - 1, valid_stride));
114 }
115
116 class CodecTest : public TestWithParam<ImageTestParams> {};
117
TEST_P(CodecTest,PublicAPI)118 TEST_P(CodecTest, PublicAPI) {
119 const auto& params = GetParam();
120 const std::string astc = LoadASTCFile(params.image_name);
121
122 ImageBuffer our_decoded_image;
123 our_decoded_image.Allocate(params.width, params.height, 4);
124 ASSERT_TRUE(ASTCDecompressToRGBA(
125 reinterpret_cast<const uint8_t*>(astc.data()), astc.size(), params.width,
126 params.height, params.footprint, our_decoded_image.Data().data(),
127 our_decoded_image.DataSize(), our_decoded_image.Stride()));
128
129 // Check that the decoded image is *very* similar to the library decoding
130 // of an ASTC texture. They may not be exact due to differences in how we
131 // convert a 16-bit float to an 8-bit integer.
132 ImageBuffer decoded_image = LoadGoldenImageWithAlpha(params.image_name);
133 CompareSumOfSquaredDifferences(decoded_image, our_decoded_image, 1.0);
134 }
135
TEST_P(CodecTest,DecompressToImage)136 TEST_P(CodecTest, DecompressToImage) {
137 const auto& params = GetParam();
138
139 std::string error;
140 std::unique_ptr<ASTCFile> image_file = ASTCFile::LoadFile(
141 std::string("src/decoder/testdata/") + params.image_name + ".astc",
142 &error);
143 ASSERT_TRUE(image_file) << "Failed to load " << params.image_name << ": "
144 << error;
145
146 ASSERT_TRUE(image_file->GetFootprint());
147 EXPECT_EQ(params.footprint, image_file->GetFootprint().value().Type());
148
149 ImageBuffer our_decoded_image;
150 our_decoded_image.Allocate(image_file->GetWidth(), image_file->GetHeight(),
151 4);
152
153 ASSERT_TRUE(DecompressToImage(*image_file, our_decoded_image.Data().data(),
154 our_decoded_image.DataSize(),
155 our_decoded_image.Stride()));
156
157 // Check that the decoded image is *very* similar to the library decoding
158 // of an ASTC texture. They may not be exact due to differences in how we
159 // convert a 16-bit float to an 8-bit integer.
160 ImageBuffer decoded_image = LoadGoldenImageWithAlpha(params.image_name);
161 CompareSumOfSquaredDifferences(decoded_image, our_decoded_image, 1.0);
162 }
163
164 // Test to make sure that reading out color values from blocks in a real-world
165 // image isn't terribly wrong, either.
GetTransparentImageTestParams()166 std::vector<ImageTestParams> GetTransparentImageTestParams() {
167 return {
168 // image_name astc footprint width height
169 { "atlas_small_4x4", FootprintType::k4x4, 256, 256 },
170 { "atlas_small_5x5", FootprintType::k5x5, 256, 256 },
171 { "atlas_small_6x6", FootprintType::k6x6, 256, 256 },
172 { "atlas_small_8x8", FootprintType::k8x8, 256, 256 },
173 };
174 }
175
176 INSTANTIATE_TEST_CASE_P(Transparent, CodecTest,
177 ValuesIn(GetTransparentImageTestParams()));
178
179 } // namespace
180
181 } // namespace astc_codec
182