• 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 <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