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 <gtest/gtest.h>
16
17 #include <fstream>
18 #include <vector>
19
20 static constexpr size_t kMaxVectorOutput = 128;
21
22 class ImageBuffer {
23 public:
24 static constexpr size_t Align = 4;
25
Allocate(size_t width,size_t height,size_t bytes_per_pixel)26 void Allocate(size_t width, size_t height, size_t bytes_per_pixel) {
27 width_ = width;
28 height_ = height;
29 bytes_per_pixel_ = bytes_per_pixel;
30 stride_ = AlignBytes(width * bytes_per_pixel);
31 data_.resize(stride_ * height);
32 }
33
operator()34 uint8_t* operator()(size_t x, size_t y) {
35 assert(x < width_ && y < height_);
36 return &data_[y * Stride() + x * bytes_per_pixel_];
37 }
38
Stride()39 size_t Stride() const { return stride_; }
BytesPerPixel()40 size_t BytesPerPixel() const { return bytes_per_pixel_; }
41
Data()42 std::vector<uint8_t>& Data() { return data_; }
Data()43 const std::vector<uint8_t>& Data() const { return data_; }
DataSize()44 size_t DataSize() const { return data_.size(); }
45
46 private:
AlignBytes(size_t bytes)47 size_t AlignBytes(size_t bytes) const {
48 return (bytes + (Align - 1)) / Align * Align;
49 }
50
51 size_t width_ = 0;
52 size_t height_ = 0;
53 size_t stride_ = 0;
54 size_t bytes_per_pixel_ = 0;
55 std::vector<uint8_t> data_;
56 };
57
58 namespace std {
PrintTo(const vector<uint8_t> & vec,ostream * os)59 static void PrintTo(const vector<uint8_t>& vec, ostream* os) {
60 ios::fmtflags origFlags(os->flags());
61
62 *os << '{';
63 size_t count = 0;
64 for (vector<uint8_t>::const_iterator it = vec.begin(); it != vec.end();
65 ++it, ++count) {
66 if (count > 0) {
67 *os << ", ";
68 }
69
70 if (count == kMaxVectorOutput) {
71 *os << "... ";
72 break;
73 }
74
75 if ((count % 16) == 0) {
76 *os << "\n";
77 }
78
79 if (*it == 0) {
80 *os << " ";
81 } else {
82 *os << "0x" << std::hex << std::uppercase << std::setw(2)
83 << std::setfill('0') << int(*it) << std::dec;
84 }
85 }
86
87 *os << '}';
88
89 os->flags(origFlags);
90 }
91 } // namespace std
92
LoadFile(const std::string & path)93 static inline std::string LoadFile(const std::string& path) {
94 std::ifstream is(path, std::ios::binary);
95 EXPECT_TRUE(is) << "Failed to load file " << path;
96 if (!is) {
97 return "";
98 }
99
100 std::ostringstream ss;
101 ss << is.rdbuf();
102 return ss.str();
103 }
104
LoadASTCFile(const std::string & basename)105 static inline std::string LoadASTCFile(const std::string& basename) {
106 const std::string filename =
107 std::string("src/decoder/testdata/") + basename + ".astc";
108
109 std::string result = LoadFile(filename);
110 // Don't parse the header here, we already know what kind of astc encoding it
111 // is.
112 if (result.size() < 16) {
113 return "";
114 } else {
115 return result.substr(16);
116 }
117 }
118
LoadGoldenBmp(const std::string & path,ImageBuffer * result)119 static inline void LoadGoldenBmp(const std::string& path, ImageBuffer* result) {
120 constexpr size_t kBmpHeaderSize = 54;
121
122 SCOPED_TRACE(testing::Message() << "LoadGoldenBmp " << path);
123
124 const std::string data = LoadFile(path);
125 ASSERT_FALSE(data.empty()) << "Failed to open golden image: " << path;
126
127 ASSERT_GE(data.size(), kBmpHeaderSize);
128 ASSERT_EQ('B', data[0]);
129 ASSERT_EQ('M', data[1]);
130
131 uint32_t dataPos = *reinterpret_cast<const uint32_t*>(&data[0x0A]);
132 uint32_t imageSize = *reinterpret_cast<const uint32_t*>(&data[0x22]);
133 const uint16_t bitsPerPixel = *reinterpret_cast<const uint16_t*>(&data[0x1C]);
134 int width = *reinterpret_cast<const int*>(&data[0x12]);
135 int height = *reinterpret_cast<const int*>(&data[0x16]);
136
137 SCOPED_TRACE(testing::Message()
138 << "dataPos=" << dataPos << ", imageSize=" << imageSize
139 << ", bitsPerPixel=" << bitsPerPixel << ", width=" << width
140 << ", height=" << height);
141
142 if (height < 0) {
143 height = -height;
144 }
145
146 if (imageSize == 0) {
147 imageSize = width * height * 3;
148 }
149
150 if (dataPos < kBmpHeaderSize) {
151 dataPos = kBmpHeaderSize;
152 }
153
154 ASSERT_TRUE(bitsPerPixel == 24 || bitsPerPixel == 32)
155 << "BMP bits per pixel mismatch, expected 24 or 32";
156
157 result->Allocate(width, height, bitsPerPixel == 24 ? 3 : 4);
158 ASSERT_LE(imageSize, result->DataSize());
159
160 std::vector<uint8_t>& resultData = result->Data();
161 const size_t stride = result->Stride();
162
163 // Copy the data row-by-row to make sure that stride is right.
164 for (size_t row = 0; row < static_cast<size_t>(height); ++row) {
165 memcpy(&resultData[row * stride], &data[dataPos + row * stride],
166 width * bitsPerPixel / 8);
167 }
168
169 if (bitsPerPixel == 32) {
170 // Swizzle the data from ABGR to ARGB.
171 for (size_t row = 0; row < static_cast<size_t>(height); ++row) {
172 uint8_t* rowData = resultData.data() + row * stride;
173
174 for (size_t i = 3; i < stride; i += 4) {
175 const uint8_t b = rowData[i - 3];
176 rowData[i - 3] = rowData[i - 1];
177 rowData[i - 1] = b;
178 }
179 }
180 } else {
181 // Swizzle the data from BGR to RGB.
182 for (size_t row = 0; row < static_cast<size_t>(height); ++row) {
183 uint8_t* rowData = resultData.data() + row * stride;
184
185 for (size_t i = 2; i < stride; i += 3) {
186 const uint8_t tmp = rowData[i - 2];
187 rowData[i - 2] = rowData[i];
188 rowData[i] = tmp;
189 }
190 }
191 }
192 }
193
CompareSumOfSquaredDifferences(const ImageBuffer & golden,const ImageBuffer & image,double threshold)194 static inline void CompareSumOfSquaredDifferences(const ImageBuffer& golden,
195 const ImageBuffer& image,
196 double threshold) {
197 ASSERT_EQ(golden.DataSize(), image.DataSize());
198 ASSERT_EQ(golden.Stride(), image.Stride());
199 ASSERT_EQ(golden.BytesPerPixel(), image.BytesPerPixel());
200
201 const std::vector<uint8_t>& image_data = image.Data();
202 const std::vector<uint8_t>& golden_data = golden.Data();
203
204 double sum = 0.0;
205 for (size_t i = 0; i < image_data.size(); ++i) {
206 const double diff = static_cast<double>(image_data[i]) - golden_data[i];
207 sum += diff * diff;
208 }
209
210 EXPECT_LE(sum, threshold * image_data.size())
211 << "Per pixel " << (sum / image_data.size())
212 << ", expected <= " << threshold;
213 if (sum > threshold * image_data.size()) {
214 // Fall back to comparison which will dump first chunk of vector.
215 EXPECT_EQ(golden_data, image_data);
216 }
217 }
218