PFM image loader + tests
This commit is contained in:
96
openVulkanoCpp/Image/ImageLoaderPfm.cpp
Normal file
96
openVulkanoCpp/Image/ImageLoaderPfm.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "ImageLoaderPfm.hpp"
|
||||
|
||||
#include "Scene/DataFormat.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace OpenVulkano::Image
|
||||
{
|
||||
std::unique_ptr<Image> ImageLoaderPfm::loadFromFile(const std::string& filePath)
|
||||
{
|
||||
std::ifstream file(filePath, std::ios::binary);
|
||||
if (!file.is_open())
|
||||
{
|
||||
throw std::runtime_error("Failed to open PFM file: " + filePath);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> buffer((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
return loadFromMemory(buffer);
|
||||
}
|
||||
|
||||
std::unique_ptr<Image> ImageLoaderPfm::loadFromMemory(const std::vector<uint8_t>& buffer)
|
||||
{
|
||||
std::istringstream stream(std::string(buffer.begin(), buffer.end()), std::ios::binary);
|
||||
PfmImageHeader header = parseHeader(stream);
|
||||
|
||||
size_t numChannels = header.isColor ? 3 : 1;
|
||||
size_t dataSize = header.width * header.height * numChannels * sizeof(float);
|
||||
std::vector<float> data(header.width * header.height * numChannels);
|
||||
stream.read(reinterpret_cast<char*>(data.data()), dataSize);
|
||||
|
||||
if (!header.isLittleEndian)
|
||||
{
|
||||
for (float& value : data)
|
||||
{
|
||||
uint8_t* ptr = reinterpret_cast<uint8_t*>(&value);
|
||||
std::reverse(ptr, ptr + sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = 0; y < header.height / 2; ++y)
|
||||
{
|
||||
for (int x = 0; x < header.width * numChannels; ++x)
|
||||
{
|
||||
std::swap(data[y * header.width * numChannels + x],
|
||||
data[(header.height - 1 - y) * header.width * numChannels + x]);
|
||||
}
|
||||
}
|
||||
|
||||
auto image = std::make_unique<Image>();
|
||||
image->resolution = { static_cast<uint32_t>(header.width), static_cast<uint32_t>(header.height), 1 };
|
||||
image->dataFormat = header.isColor ? DataFormat::Format::R32G32B32_SFLOAT : DataFormat::Format::R32_SFLOAT;
|
||||
image->data = Array<uint8_t>(dataSize);
|
||||
memcpy(image->data.Data(), data.data(), dataSize);
|
||||
return image;
|
||||
}
|
||||
|
||||
Math::Vector2i ImageLoaderPfm::GetImageDimensions(const std::string& filename)
|
||||
{
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
if (!file.is_open())
|
||||
{
|
||||
throw std::runtime_error("Failed to open PFM file: " + filename);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> buffer((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
std::istringstream stream(std::string(buffer.begin(), buffer.end()), std::ios::binary);
|
||||
PfmImageHeader header = parseHeader(stream);
|
||||
return { header.width, header.height };
|
||||
}
|
||||
|
||||
ImageLoaderPfm::PfmImageHeader ImageLoaderPfm::parseHeader(std::istringstream& stream)
|
||||
{
|
||||
ImageLoaderPfm::PfmImageHeader header;
|
||||
std::string magic;
|
||||
stream >> magic;
|
||||
header.isColor = (magic == "PF");
|
||||
|
||||
float endianess;
|
||||
stream >> header.width >> header.height >> endianess;
|
||||
stream.ignore();
|
||||
|
||||
header.isLittleEndian = endianess < 0;
|
||||
|
||||
return header;
|
||||
}
|
||||
}
|
||||
31
openVulkanoCpp/Image/ImageLoaderPfm.hpp
Normal file
31
openVulkanoCpp/Image/ImageLoaderPfm.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Image/ImageLoader.hpp"
|
||||
|
||||
namespace OpenVulkano::Image
|
||||
{
|
||||
class ImageLoaderPfm : public IImageLoader
|
||||
{
|
||||
struct PfmImageHeader
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
bool isColor;
|
||||
bool isLittleEndian;
|
||||
};
|
||||
|
||||
public:
|
||||
std::unique_ptr<Image> loadFromFile(const std::string& filePath) override;
|
||||
std::unique_ptr<Image> loadFromMemory(const std::vector<uint8_t>& buffer) override;
|
||||
Math::Vector2i GetImageDimensions(const std::string& filename) override;
|
||||
|
||||
private:
|
||||
PfmImageHeader parseHeader(std::istringstream& stream);
|
||||
};
|
||||
}
|
||||
84
tests/Image/ImageLoaderPfmTest.cpp
Normal file
84
tests/Image/ImageLoaderPfmTest.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "Image/ImageLoaderPfm.hpp"
|
||||
#include "Scene/DataFormat.hpp"
|
||||
|
||||
using namespace OpenVulkano;
|
||||
using namespace OpenVulkano::Image;
|
||||
|
||||
TEST_CASE("ImageLoaderPfm loads PFM images", "[ImageLoaderPfm]")
|
||||
{
|
||||
ImageLoaderPfm loader;
|
||||
|
||||
SECTION("Grayscale Pf image")
|
||||
{
|
||||
std::ostringstream pfStream;
|
||||
pfStream << "Pf\n"
|
||||
<< "4 2\n"
|
||||
<< "-1.0\n";
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
float pixelValue = 1.0f;
|
||||
pfStream.write(reinterpret_cast<const char*>(&pixelValue), sizeof(float));
|
||||
}
|
||||
|
||||
std::string pfData = pfStream.str();
|
||||
std::vector<uint8_t> pfBuffer(pfData.begin(), pfData.end());
|
||||
|
||||
auto image = loader.loadFromMemory(pfBuffer);
|
||||
|
||||
REQUIRE(image != nullptr);
|
||||
REQUIRE(image->resolution.x == 4);
|
||||
REQUIRE(image->resolution.y == 2);
|
||||
REQUIRE(image->dataFormat == DataFormat::Format::R32_SFLOAT);
|
||||
|
||||
const float expectedValue = 1.0f;
|
||||
for (size_t i = 0; i < image->data.Size(); i += sizeof(float))
|
||||
{
|
||||
float value;
|
||||
std::memcpy(&value, &image->data[i], sizeof(float));
|
||||
REQUIRE(value == expectedValue);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Color PF image")
|
||||
{
|
||||
std::ostringstream pfStream;
|
||||
pfStream << "PF\n"
|
||||
<< "3 2\n"
|
||||
<< "-1.0\n";
|
||||
|
||||
for (int i = 0; i < 6 * 3; ++i)
|
||||
{
|
||||
float pixelValue = 1.0f;
|
||||
pfStream.write(reinterpret_cast<const char*>(&pixelValue), sizeof(float));
|
||||
}
|
||||
|
||||
std::string pfData = pfStream.str();
|
||||
std::vector<uint8_t> pfBuffer(pfData.begin(), pfData.end());
|
||||
|
||||
auto image = loader.loadFromMemory(pfBuffer);
|
||||
|
||||
REQUIRE(image != nullptr);
|
||||
REQUIRE(image->resolution.x == 3);
|
||||
REQUIRE(image->resolution.y == 2);
|
||||
REQUIRE(image->dataFormat == DataFormat::Format::R32G32B32_SFLOAT);
|
||||
|
||||
const float expectedValue = 1.0f;
|
||||
for (size_t i = 0; i < image->data.Size(); i += sizeof(float))
|
||||
{
|
||||
float value;
|
||||
std::memcpy(&value, &image->data[i], sizeof(float));
|
||||
REQUIRE(value == expectedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user