aboutsummaryrefslogtreecommitdiff
path: root/src/decoder/test/image_utils.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/decoder/test/image_utils.h')
-rw-r--r--src/decoder/test/image_utils.h217
1 files changed, 217 insertions, 0 deletions
diff --git a/src/decoder/test/image_utils.h b/src/decoder/test/image_utils.h
new file mode 100644
index 0000000..718696e
--- /dev/null
+++ b/src/decoder/test/image_utils.h
@@ -0,0 +1,217 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gtest/gtest.h>
+
+#include <fstream>
+#include <vector>
+
+static constexpr size_t kMaxVectorOutput = 128;
+
+class ImageBuffer {
+ public:
+ static constexpr size_t Align = 4;
+
+ void Allocate(size_t width, size_t height, size_t bytes_per_pixel) {
+ width_ = width;
+ height_ = height;
+ bytes_per_pixel_ = bytes_per_pixel;
+ stride_ = AlignBytes(width * bytes_per_pixel);
+ data_.resize(stride_ * height);
+ }
+
+ uint8_t* operator()(size_t x, size_t y) {
+ assert(x < width_ && y < height_);
+ return &data_[y * Stride() + x * bytes_per_pixel_];
+ }
+
+ size_t Stride() const { return stride_; }
+ size_t BytesPerPixel() const { return bytes_per_pixel_; }
+
+ std::vector<uint8_t>& Data() { return data_; }
+ const std::vector<uint8_t>& Data() const { return data_; }
+ size_t DataSize() const { return data_.size(); }
+
+ private:
+ size_t AlignBytes(size_t bytes) const {
+ return (bytes + (Align - 1)) / Align * Align;
+ }
+
+ size_t width_ = 0;
+ size_t height_ = 0;
+ size_t stride_ = 0;
+ size_t bytes_per_pixel_ = 0;
+ std::vector<uint8_t> data_;
+};
+
+namespace std {
+static void PrintTo(const vector<uint8_t>& vec, ostream* os) {
+ ios::fmtflags origFlags(os->flags());
+
+ *os << '{';
+ size_t count = 0;
+ for (vector<uint8_t>::const_iterator it = vec.begin(); it != vec.end();
+ ++it, ++count) {
+ if (count > 0) {
+ *os << ", ";
+ }
+
+ if (count == kMaxVectorOutput) {
+ *os << "... ";
+ break;
+ }
+
+ if ((count % 16) == 0) {
+ *os << "\n";
+ }
+
+ if (*it == 0) {
+ *os << " ";
+ } else {
+ *os << "0x" << std::hex << std::uppercase << std::setw(2)
+ << std::setfill('0') << int(*it) << std::dec;
+ }
+ }
+
+ *os << '}';
+
+ os->flags(origFlags);
+}
+} // namespace std
+
+static std::string LoadFile(const std::string& path) {
+ std::ifstream is(path, std::ios::binary);
+ EXPECT_TRUE(is) << "Failed to load file " << path;
+ if (!is) {
+ return "";
+ }
+
+ std::ostringstream ss;
+ ss << is.rdbuf();
+ return ss.str();
+}
+
+static std::string LoadASTCFile(const std::string& basename) {
+ const std::string filename =
+ std::string("src/decoder/testdata/") + basename + ".astc";
+
+ std::string result = LoadFile(filename);
+ // Don't parse the header here, we already know what kind of astc encoding it
+ // is.
+ if (result.size() < 16) {
+ return "";
+ } else {
+ return result.substr(16);
+ }
+}
+
+void LoadGoldenBmp(const std::string& path, ImageBuffer* result) {
+ constexpr size_t kBmpHeaderSize = 54;
+
+ SCOPED_TRACE(testing::Message() << "LoadGoldenBmp " << path);
+
+ const std::string data = LoadFile(path);
+ ASSERT_FALSE(data.empty()) << "Failed to open golden image: " << path;
+
+ ASSERT_GE(data.size(), kBmpHeaderSize);
+ ASSERT_EQ('B', data[0]);
+ ASSERT_EQ('M', data[1]);
+
+ uint32_t dataPos = *reinterpret_cast<const uint32_t*>(&data[0x0A]);
+ uint32_t imageSize = *reinterpret_cast<const uint32_t*>(&data[0x22]);
+ const uint16_t bitsPerPixel = *reinterpret_cast<const uint16_t*>(&data[0x1C]);
+ int width = *reinterpret_cast<const int*>(&data[0x12]);
+ int height = *reinterpret_cast<const int*>(&data[0x16]);
+
+ SCOPED_TRACE(testing::Message()
+ << "dataPos=" << dataPos << ", imageSize=" << imageSize
+ << ", bitsPerPixel=" << bitsPerPixel << ", width=" << width
+ << ", height=" << height);
+
+ if (height < 0) {
+ height = -height;
+ }
+
+ if (imageSize == 0) {
+ imageSize = width * height * 3;
+ }
+
+ if (dataPos < kBmpHeaderSize) {
+ dataPos = kBmpHeaderSize;
+ }
+
+ ASSERT_TRUE(bitsPerPixel == 24 || bitsPerPixel == 32)
+ << "BMP bits per pixel mismatch, expected 24 or 32";
+
+ result->Allocate(width, height, bitsPerPixel == 24 ? 3 : 4);
+ ASSERT_LE(imageSize, result->DataSize());
+
+ std::vector<uint8_t>& resultData = result->Data();
+ const size_t stride = result->Stride();
+
+ // Copy the data row-by-row to make sure that stride is right.
+ for (size_t row = 0; row < static_cast<size_t>(height); ++row) {
+ memcpy(&resultData[row * stride], &data[dataPos + row * stride],
+ width * bitsPerPixel / 8);
+ }
+
+ if (bitsPerPixel == 32) {
+ // Swizzle the data from ABGR to ARGB.
+ for (size_t row = 0; row < static_cast<size_t>(height); ++row) {
+ uint8_t* rowData = resultData.data() + row * stride;
+
+ for (size_t i = 3; i < stride; i += 4) {
+ const uint8_t b = rowData[i - 3];
+ rowData[i - 3] = rowData[i - 1];
+ rowData[i - 1] = b;
+ }
+ }
+ } else {
+ // Swizzle the data from BGR to RGB.
+ for (size_t row = 0; row < static_cast<size_t>(height); ++row) {
+ uint8_t* rowData = resultData.data() + row * stride;
+
+ for (size_t i = 2; i < stride; i += 3) {
+ const uint8_t tmp = rowData[i - 2];
+ rowData[i - 2] = rowData[i];
+ rowData[i] = tmp;
+ }
+ }
+ }
+}
+
+static void CompareSumOfSquaredDifferences(const ImageBuffer& golden,
+ const ImageBuffer& image,
+ double threshold) {
+ ASSERT_EQ(golden.DataSize(), image.DataSize());
+ ASSERT_EQ(golden.Stride(), image.Stride());
+ ASSERT_EQ(golden.BytesPerPixel(), image.BytesPerPixel());
+
+ const std::vector<uint8_t>& image_data = image.Data();
+ const std::vector<uint8_t>& golden_data = golden.Data();
+
+ double sum = 0.0;
+ for (size_t i = 0; i < image_data.size(); ++i) {
+ const double diff = static_cast<double>(image_data[i]) - golden_data[i];
+ sum += diff * diff;
+ }
+
+ EXPECT_LE(sum, threshold * image_data.size())
+ << "Per pixel " << (sum / image_data.size())
+ << ", expected <= " << threshold;
+ if (sum > threshold * image_data.size()) {
+ // Fall back to comparison which will dump first chunk of vector.
+ EXPECT_EQ(golden_data, image_data);
+ }
+}