blob: 99c391adf1066d7a8424972c57a32add3f1f978d [file] [log] [blame]
#pragma once
//==============================================================================================
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//==============================================================================================
#include <algorithm>
#include <fstream>
#include <iostream>
#include <vector>
#include "color.h"
// Assuming 'color' is a class or struct already defined
// with overloaded operator+ and a method to output the color data
class PPMImageFile {
private:
std::string filename;
int image_width, image_height;
std::vector<color> data;
bool IsNormalized;
public:
PPMImageFile(const std::string &file_name, int width = 0, int height = 0)
: filename(file_name), image_width(width), image_height(height),
IsNormalized(false) {
data.resize(width * height);
}
color *getHostPtr() { return data.data(); }
void setData(color *C) {
for (int i = 0, e = image_width * image_height; i != e; ++i)
data[i] = C[i];
}
void normalize() {
for (auto &pixel_color : data) {
auto r = pixel_color.x();
auto g = pixel_color.y();
auto b = pixel_color.z();
// Apply a linear to gamma transform for gamma 2
r = linear_to_gamma(r);
g = linear_to_gamma(g);
b = linear_to_gamma(b);
// Write the translated [0,255] value of each color component.
static const interval intensity(0.000, 0.999);
pixel_color = color(static_cast<int>(256 * intensity.clamp(r)),
static_cast<int>(256 * intensity.clamp(g)),
static_cast<int>(256 * intensity.clamp(b)));
}
IsNormalized = true;
}
bool save() const {
if (!IsNormalized) {
std::cerr
<< "Error: Image is not normalized. Saving the unnormalized image."
<< std::endl;
return false;
}
std::ofstream file(filename);
if (!file) {
std::cerr << "File could not be opened for writing." << std::endl;
return false;
}
// PPM header
file << "P3\n" << image_width << ' ' << image_height << "\n255\n";
// Write each pixel to the file
for (const auto &pixel_color : data) {
file << pixel_color.x() << ' ' << pixel_color.y() << ' '
<< pixel_color.z() << '\n';
}
file.close();
return true;
}
bool load() {
std::ifstream file(filename);
if (!file) {
std::cerr << "File could not be opened for reading." << std::endl;
return false;
}
std::string header;
int max_val;
// Read the header and check format
file >> header;
if (header != "P3") {
std::cerr << "Unsupported file format." << std::endl;
return false;
}
// Read image dimensions and maximum value
file >> image_width >> image_height >> max_val;
// Resize the data vector to hold the image data
data.resize(image_width * image_height);
// Read pixel data
for (auto &pixel_color : data) {
int r, g, b;
file >> r >> g >> b;
pixel_color = color(r, g, b);
}
IsNormalized = true; // Assuming the loaded image is already normalized
file.close();
return true;
}
bool compare(const PPMImageFile &img, double threshold = 1e-3) const {
if (IsNormalized != img.IsNormalized) {
std::cerr << "Cannot compare " << filename << " and " << img.filename
<< " because one is normalized and the other is not."
<< std::endl;
return false;
}
if (image_width != img.image_width || image_height != img.image_height) {
std::cerr << "Images dimensions do not match." << std::endl;
return false;
}
if (IsNormalized) {
threshold *= 255.0;
}
struct Difference {
double value;
int x, y;
};
std::vector<Difference> topDifferences;
bool anySignificantDifference = false;
for (int y = 0; y < image_height; ++y) {
for (int x = 0; x < image_width; ++x) {
const color &c1 = data[y * image_width + x];
const color &c2 = img.data[y * image_width + x];
double diff =
std::max({std::abs(c1.x() - c2.x()), std::abs(c1.y() - c2.y()),
std::abs(c1.z() - c2.z())});
if (diff > threshold) {
anySignificantDifference = true;
if (topDifferences.size() < 10) {
topDifferences.push_back({diff, x, y});
sort(topDifferences.begin(), topDifferences.end(),
[](const Difference &a, const Difference &b) {
return a.value > b.value;
});
}
}
}
}
if (anySignificantDifference) {
// Output top differences
std::cout << "Top Differences between " << filename << " and "
<< img.filename << ":\n";
for (const auto &diff : topDifferences) {
std::cout << "Location (" << diff.x << ", " << diff.y << "), "
<< "Difference: " << diff.value << ", " << filename << ": "
<< data[diff.y * image_width + diff.x].toString() << ", "
<< img.filename << ": "
<< img.data[diff.y * image_width + diff.x].toString() << "\n";
}
return false;
} else {
std::cout << filename << " and " << img.filename << " are the same.\n";
return true;
}
}
};