Add semantic pixel asserts for lighting scenes
This commit is contained in:
176
tests/Rendering/integration/RenderingIntegrationImageAssert.h
Normal file
176
tests/Rendering/integration/RenderingIntegrationImageAssert.h
Normal file
@@ -0,0 +1,176 @@
|
||||
#pragma once
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
namespace RenderingIntegrationTestUtils {
|
||||
|
||||
struct PpmImage {
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
std::vector<uint8_t> rgb;
|
||||
|
||||
std::array<uint8_t, 3> GetPixel(uint32_t x, uint32_t y) const {
|
||||
const size_t index = (static_cast<size_t>(y) * width + x) * 3u;
|
||||
return { rgb[index + 0], rgb[index + 1], rgb[index + 2] };
|
||||
}
|
||||
};
|
||||
|
||||
inline std::filesystem::path GetExecutableDirectory() {
|
||||
char exePath[MAX_PATH] = {};
|
||||
const DWORD length = GetModuleFileNameA(nullptr, exePath, MAX_PATH);
|
||||
if (length == 0 || length >= MAX_PATH) {
|
||||
return std::filesystem::current_path();
|
||||
}
|
||||
|
||||
return std::filesystem::path(exePath).parent_path();
|
||||
}
|
||||
|
||||
inline std::filesystem::path ResolveRuntimePath(const char* path) {
|
||||
std::filesystem::path resolved(path);
|
||||
if (resolved.is_absolute()) {
|
||||
return resolved;
|
||||
}
|
||||
|
||||
return GetExecutableDirectory() / resolved;
|
||||
}
|
||||
|
||||
inline std::string ReadNextPpmToken(std::istream& stream) {
|
||||
std::string token;
|
||||
while (stream >> token) {
|
||||
if (!token.empty() && token[0] == '#') {
|
||||
std::string ignored;
|
||||
std::getline(stream, ignored);
|
||||
continue;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
inline PpmImage LoadPpmImage(const char* path) {
|
||||
const std::filesystem::path resolvedPath = ResolveRuntimePath(path);
|
||||
std::ifstream file(resolvedPath, std::ios::binary);
|
||||
EXPECT_TRUE(file.is_open()) << resolvedPath.string();
|
||||
|
||||
PpmImage image;
|
||||
if (!file.is_open()) {
|
||||
return image;
|
||||
}
|
||||
|
||||
const std::string magic = ReadNextPpmToken(file);
|
||||
EXPECT_EQ(magic, "P6");
|
||||
if (magic != "P6") {
|
||||
return image;
|
||||
}
|
||||
|
||||
const std::string widthToken = ReadNextPpmToken(file);
|
||||
const std::string heightToken = ReadNextPpmToken(file);
|
||||
const std::string maxValueToken = ReadNextPpmToken(file);
|
||||
EXPECT_FALSE(widthToken.empty());
|
||||
EXPECT_FALSE(heightToken.empty());
|
||||
EXPECT_FALSE(maxValueToken.empty());
|
||||
if (widthToken.empty() || heightToken.empty() || maxValueToken.empty()) {
|
||||
return image;
|
||||
}
|
||||
|
||||
image.width = static_cast<uint32_t>(std::stoul(widthToken));
|
||||
image.height = static_cast<uint32_t>(std::stoul(heightToken));
|
||||
EXPECT_EQ(std::stoul(maxValueToken), 255u);
|
||||
|
||||
file.get();
|
||||
image.rgb.resize(static_cast<size_t>(image.width) * image.height * 3u);
|
||||
file.read(reinterpret_cast<char*>(image.rgb.data()), static_cast<std::streamsize>(image.rgb.size()));
|
||||
EXPECT_EQ(file.gcount(), static_cast<std::streamsize>(image.rgb.size()));
|
||||
return image;
|
||||
}
|
||||
|
||||
inline int PixelLuminance(const std::array<uint8_t, 3>& pixel) {
|
||||
return static_cast<int>(pixel[0]) + static_cast<int>(pixel[1]) + static_cast<int>(pixel[2]);
|
||||
}
|
||||
|
||||
inline void AssertPixelInBounds(const PpmImage& image, uint32_t x, uint32_t y, const char* label) {
|
||||
ASSERT_LT(x, image.width) << label;
|
||||
ASSERT_LT(y, image.height) << label;
|
||||
}
|
||||
|
||||
inline void ExpectPixelNear(
|
||||
const PpmImage& image,
|
||||
uint32_t x,
|
||||
uint32_t y,
|
||||
const std::array<uint8_t, 3>& expected,
|
||||
uint8_t tolerance,
|
||||
const char* label) {
|
||||
AssertPixelInBounds(image, x, y, label);
|
||||
|
||||
const std::array<uint8_t, 3> actual = image.GetPixel(x, y);
|
||||
for (size_t channel = 0; channel < actual.size(); ++channel) {
|
||||
const int delta = std::abs(static_cast<int>(actual[channel]) - static_cast<int>(expected[channel]));
|
||||
EXPECT_LE(delta, static_cast<int>(tolerance))
|
||||
<< label << " @ (" << x << ", " << y << ") channel=" << channel;
|
||||
}
|
||||
}
|
||||
|
||||
inline void ExpectPixelLuminanceAtLeast(
|
||||
const PpmImage& image,
|
||||
uint32_t x,
|
||||
uint32_t y,
|
||||
int minLuminance,
|
||||
const char* label) {
|
||||
AssertPixelInBounds(image, x, y, label);
|
||||
|
||||
const std::array<uint8_t, 3> actual = image.GetPixel(x, y);
|
||||
EXPECT_GE(PixelLuminance(actual), minLuminance) << label << " @ (" << x << ", " << y << ")";
|
||||
}
|
||||
|
||||
inline void ExpectPixelLuminanceAtMost(
|
||||
const PpmImage& image,
|
||||
uint32_t x,
|
||||
uint32_t y,
|
||||
int maxLuminance,
|
||||
const char* label) {
|
||||
AssertPixelInBounds(image, x, y, label);
|
||||
|
||||
const std::array<uint8_t, 3> actual = image.GetPixel(x, y);
|
||||
EXPECT_LE(PixelLuminance(actual), maxLuminance) << label << " @ (" << x << ", " << y << ")";
|
||||
}
|
||||
|
||||
inline void ExpectPixelChannelDominates(
|
||||
const PpmImage& image,
|
||||
uint32_t x,
|
||||
uint32_t y,
|
||||
size_t dominantChannel,
|
||||
int minMargin,
|
||||
const char* label) {
|
||||
AssertPixelInBounds(image, x, y, label);
|
||||
ASSERT_LT(dominantChannel, static_cast<size_t>(3)) << label;
|
||||
|
||||
const std::array<uint8_t, 3> actual = image.GetPixel(x, y);
|
||||
int maxOtherChannel = 0;
|
||||
for (size_t channel = 0; channel < actual.size(); ++channel) {
|
||||
if (channel == dominantChannel) {
|
||||
continue;
|
||||
}
|
||||
|
||||
maxOtherChannel = std::max(maxOtherChannel, static_cast<int>(actual[channel]));
|
||||
}
|
||||
|
||||
EXPECT_GE(static_cast<int>(actual[dominantChannel]) - maxOtherChannel, minMargin)
|
||||
<< label << " @ (" << x << ", " << y << ")";
|
||||
}
|
||||
|
||||
} // namespace RenderingIntegrationTestUtils
|
||||
Reference in New Issue
Block a user