Merge branch 'master' of git.madvoxel.net:OpenVulkano/OpenVulkano
This commit is contained in:
@@ -96,6 +96,10 @@ namespace OpenVulkano
|
||||
inStream >> header;
|
||||
size_t size = header.GetElementCount();
|
||||
|
||||
image = std::make_unique<float[]>(size);
|
||||
|
||||
inStream.read(reinterpret_cast<char*>(image.get()), size * sizeof(float));
|
||||
|
||||
if ((std::endian::native == std::endian::little) != header.littleEndian)
|
||||
{
|
||||
char* data = reinterpret_cast<char*>(image.get());
|
||||
@@ -105,9 +109,6 @@ namespace OpenVulkano
|
||||
std::reverse(&data[idx], &data[idx + sizeof(float)]);
|
||||
}
|
||||
}
|
||||
|
||||
image = std::make_unique<float[]>(size);
|
||||
inStream.read(reinterpret_cast<char*>(image.get()), size * sizeof(float));
|
||||
}
|
||||
|
||||
static PfmImage ReadImage(std::istream& inStream)
|
||||
|
||||
@@ -87,8 +87,25 @@ namespace OpenVulkano
|
||||
if (val > 6) throw std::runtime_error("Malformed PNM header!");
|
||||
pnmHeader.ascii = val < 4;
|
||||
pnmHeader.color = static_cast<ColorMode>(val - 3);
|
||||
inStream >> pnmHeader.width >> pnmHeader.height;
|
||||
inStream >> pnmHeader.maxValue;
|
||||
|
||||
if (!(inStream >> pnmHeader.width >> pnmHeader.height)) [[unlikely]]
|
||||
{
|
||||
throw std::runtime_error("Malformed PNM header: Unable to read width and height!");
|
||||
}
|
||||
if (pnmHeader.width == 0 || pnmHeader.height == 0) [[unlikely]]
|
||||
{
|
||||
throw std::runtime_error("Malformed PNM header: Width and height must be positive!");
|
||||
}
|
||||
|
||||
if (!(inStream >> pnmHeader.maxValue)) [[unlikely]]
|
||||
{
|
||||
throw std::runtime_error("Malformed PNM header: Unable to read maxValue!");
|
||||
}
|
||||
if (pnmHeader.maxValue == 0 || pnmHeader.maxValue > 65535) [[unlikely]]
|
||||
{
|
||||
throw std::runtime_error("Malformed PNM header: Invalid maxValue (must be between 1 and 65535)!");
|
||||
}
|
||||
|
||||
inStream.get();
|
||||
return inStream;
|
||||
}
|
||||
|
||||
169
tests/Files/Pfm.cpp
Normal file
169
tests/Files/Pfm.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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_all.hpp>
|
||||
|
||||
#include "IO/Files/Pfm.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <iomanip>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
using namespace OpenVulkano;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool almostEqual(float a, float b, float epsilon = 0.001f) { return std::fabs(a - b) < epsilon; }
|
||||
}
|
||||
|
||||
TEST_CASE("Invalid input", "[Pfm]")
|
||||
{
|
||||
std::stringstream ss("Invalid\n1024 768\n1.0\n");
|
||||
PfmHeader header;
|
||||
|
||||
REQUIRE_THROWS_AS(ss >> header, std::runtime_error);
|
||||
}
|
||||
|
||||
TEST_CASE("Extreme maxValue handling", "[Pfm]")
|
||||
{
|
||||
PfmHeader largeMaxValueHeader(1024, 768, 1000000.0f, true);
|
||||
std::stringstream ss;
|
||||
ss << largeMaxValueHeader;
|
||||
|
||||
REQUIRE(ss.str() == "PF\n1024 768\n-1000000.0\n");
|
||||
|
||||
std::stringstream inputSS("PF\n1024 768\n-1000000.0\n");
|
||||
PfmHeader parsedHeader;
|
||||
inputSS >> parsedHeader;
|
||||
REQUIRE(parsedHeader.maxValue == 1000000.0f);
|
||||
REQUIRE(parsedHeader.littleEndian == true);
|
||||
}
|
||||
|
||||
TEST_CASE("Negative maxValue indicating endian", "[Pfm]")
|
||||
{
|
||||
std::stringstream ss("PF\n640 480\n-1.0\n");
|
||||
PfmHeader header;
|
||||
ss >> header;
|
||||
|
||||
REQUIRE(header.color == true);
|
||||
REQUIRE(header.width == 640);
|
||||
REQUIRE(header.height == 480);
|
||||
REQUIRE(header.maxValue == 1.0f);
|
||||
REQUIRE(header.littleEndian == true);
|
||||
}
|
||||
|
||||
TEST_CASE("Operator<< output precision", "[Pfm]")
|
||||
{
|
||||
PfmHeader header(640, 480, 1.234567f, true);
|
||||
std::stringstream ss;
|
||||
ss << header;
|
||||
|
||||
REQUIRE(ss.str() == "PF\n640 480\n-1.2\n");
|
||||
}
|
||||
|
||||
TEST_CASE("Zero width or height", "[Pfm]")
|
||||
{
|
||||
PfmHeader zeroWidth(0, 768, 1.0f, true);
|
||||
PfmHeader zeroHeight(1024, 0, 1.0f, false);
|
||||
|
||||
REQUIRE(zeroWidth.GetElementCount() == 0);
|
||||
REQUIRE(zeroHeight.GetElementCount() == 0);
|
||||
REQUIRE(zeroWidth.GetImageSize() == 0);
|
||||
REQUIRE(zeroHeight.GetImageSize() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Large width and height", "[Pfm]")
|
||||
{
|
||||
PfmHeader largeHeader(10000, 8000, 1.0f, true);
|
||||
|
||||
REQUIRE(largeHeader.GetElementCount() == 10000 * 8000 * 3);
|
||||
REQUIRE(largeHeader.GetImageSize() == 10000 * 8000 * 3 * sizeof(float));
|
||||
}
|
||||
|
||||
TEST_CASE("Comparing headers", "[Pfm]")
|
||||
{
|
||||
PfmHeader header1(640, 480, 1.0f, true);
|
||||
PfmHeader header2(640, 480, 1.0f, true);
|
||||
PfmHeader header3(640, 480, 1.0f, false);
|
||||
PfmHeader header4(1024, 768, 1.0f, true);
|
||||
|
||||
REQUIRE(header1.width == header2.width);
|
||||
REQUIRE(header1.height == header2.height);
|
||||
REQUIRE(header1.color == header2.color);
|
||||
|
||||
REQUIRE(header1.color != header3.color);
|
||||
REQUIRE(header1.width != header4.width);
|
||||
REQUIRE(header1.height != header4.height);
|
||||
}
|
||||
|
||||
TEST_CASE("Read with incorrect header", "[Pfm]")
|
||||
{
|
||||
std::stringstream ss("Invalid\n800 600\n-1.0\n");
|
||||
REQUIRE_THROWS_AS(PfmImage::ReadImage(ss), std::runtime_error);
|
||||
}
|
||||
|
||||
TEST_CASE("Endian little-endian", "[Pfm]")
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Pf\n2 2\n-1.0\n";
|
||||
std::vector<float> imageData = { 0.1f, 0.2f, 0.3f, 0.4f };
|
||||
ss.write(reinterpret_cast<const char*>(imageData.data()), imageData.size() * sizeof(float));
|
||||
PfmImage image = PfmImage::ReadImage(ss);
|
||||
REQUIRE(image.header.width == 2);
|
||||
REQUIRE(image.header.height == 2);
|
||||
REQUIRE(image.header.color == false);
|
||||
REQUIRE(image.header.maxValue == 1.0f);
|
||||
for (size_t i = 0; i < imageData.size(); ++i)
|
||||
{
|
||||
REQUIRE(almostEqual(image.image[i],imageData[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Read with exact data match", "[Pfm]")
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Pf\n2 2\n-1.0\n";
|
||||
std::vector<float> imageData = { 0.1f, 0.2f, 0.3f, 0.4f };
|
||||
ss.write(reinterpret_cast<const char*>(imageData.data()), imageData.size() * sizeof(float));
|
||||
|
||||
PfmImage image = PfmImage::ReadImage(ss);
|
||||
REQUIRE(image.header.width == 2);
|
||||
REQUIRE(image.header.height == 2);
|
||||
REQUIRE(image.header.color == false);
|
||||
REQUIRE(image.header.maxValue == 1.0f);
|
||||
|
||||
for (size_t i = 0; i < imageData.size(); ++i)
|
||||
{
|
||||
REQUIRE(almostEqual(image.image[i], imageData[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("ReadImage with big-endian data", "[Pfm]")
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Pf\n2 2\n1.0\n";
|
||||
std::vector<float> imageData = { 0.1f, 0.2f, 0.3f, 0.4f };
|
||||
|
||||
{
|
||||
std::vector<float> imageDataBigEndian = imageData;
|
||||
char* data = reinterpret_cast<char*>(imageDataBigEndian.data());
|
||||
for (size_t i = 0; i < imageDataBigEndian.size(); i++)
|
||||
{
|
||||
size_t idx = i * sizeof(float);
|
||||
std::reverse(&data[idx], &data[idx + sizeof(float)]);
|
||||
}
|
||||
ss.write(reinterpret_cast<const char*>(imageDataBigEndian.data()), imageDataBigEndian.size() * sizeof(float));
|
||||
}
|
||||
PfmImage image = PfmImage::ReadImage(ss);
|
||||
|
||||
for (size_t i = 0; i < imageData.size(); ++i)
|
||||
{
|
||||
REQUIRE(almostEqual(image.image[i], imageData[i]));
|
||||
}
|
||||
}
|
||||
200
tests/Files/Pnm.cpp
Normal file
200
tests/Files/Pnm.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 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_all.hpp>
|
||||
|
||||
#include "IO/Files/Pnm.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
|
||||
using namespace OpenVulkano;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool almostEqual(float a, float b, float epsilon = 0.001f) { return std::fabs(a - b) < epsilon; }
|
||||
}
|
||||
|
||||
TEST_CASE("Default constructor initializes with expected values", "[Pnm]")
|
||||
{
|
||||
PnmHeader header;
|
||||
|
||||
REQUIRE(header.width == 0);
|
||||
REQUIRE(header.height == 0);
|
||||
REQUIRE(header.maxValue == 0);
|
||||
REQUIRE(header.color == PnmHeader::ColorMode::GRAY);
|
||||
REQUIRE(header.ascii == false);
|
||||
}
|
||||
|
||||
TEST_CASE("Constructor initializes with correct values", "[Pnm]")
|
||||
{
|
||||
PnmHeader header;
|
||||
|
||||
header = PnmHeader(100, 200, true);
|
||||
|
||||
REQUIRE(header.width == 100);
|
||||
REQUIRE(header.height == 200);
|
||||
REQUIRE(header.maxValue == 255);
|
||||
REQUIRE(header.color == PnmHeader::ColorMode::COLOR);
|
||||
REQUIRE(header.ascii == false);
|
||||
|
||||
header = PnmHeader(100, 200, PnmHeader::ColorMode::BW, 127, true);
|
||||
|
||||
REQUIRE(header.width == 100);
|
||||
REQUIRE(header.height == 200);
|
||||
REQUIRE(header.maxValue == 127);
|
||||
REQUIRE(header.color == PnmHeader::ColorMode::BW);
|
||||
REQUIRE(header.ascii == true);
|
||||
}
|
||||
|
||||
TEST_CASE("GetFileType returns correct file type", "[Pnm]")
|
||||
{
|
||||
PnmHeader header;
|
||||
|
||||
header = PnmHeader(100, 200, PnmHeader::ColorMode::BW);
|
||||
REQUIRE(header.GetFileType() == "PBM");
|
||||
|
||||
header = PnmHeader(100, 200, PnmHeader::ColorMode::GRAY);
|
||||
REQUIRE(header.GetFileType() == "PGM");
|
||||
|
||||
header = PnmHeader(100, 200, PnmHeader::ColorMode::COLOR);
|
||||
REQUIRE(header.GetFileType() == "PPM");
|
||||
}
|
||||
|
||||
TEST_CASE("GetFileExtension returns correct file extension", "[Pnm]")
|
||||
{
|
||||
PnmHeader header;
|
||||
|
||||
header = PnmHeader(100, 200, PnmHeader::ColorMode::BW);
|
||||
REQUIRE(header.GetFileExtension() == ".pbm");
|
||||
|
||||
header = PnmHeader(100, 200, PnmHeader::ColorMode::GRAY);
|
||||
REQUIRE(header.GetFileExtension() == ".pgm");
|
||||
|
||||
header = PnmHeader(100, 200, PnmHeader::ColorMode::COLOR);
|
||||
REQUIRE(header.GetFileExtension() == ".ppm");
|
||||
}
|
||||
|
||||
TEST_CASE("GetMagicNumberValue and GetMagicNumberChar return correct values", "[Pnm]")
|
||||
{
|
||||
PnmHeader header;
|
||||
|
||||
header = PnmHeader(100, 200, PnmHeader::ColorMode::BW, 255, true);
|
||||
REQUIRE(header.GetMagicNumberValue() == 1);
|
||||
REQUIRE(header.GetMagicNumberChar() == '1');
|
||||
|
||||
header = PnmHeader(100, 200, PnmHeader::ColorMode::COLOR, 255, false);
|
||||
REQUIRE(header.GetMagicNumberValue() == 6);
|
||||
REQUIRE(header.GetMagicNumberChar() == '6');
|
||||
}
|
||||
|
||||
TEST_CASE("GetImageSize returns correct size", "[Pnm]")
|
||||
{
|
||||
PnmHeader header;
|
||||
header = PnmHeader(8, 8, PnmHeader::ColorMode::BW);
|
||||
REQUIRE(header.GetImageSize() == 8);
|
||||
|
||||
header = PnmHeader(100, 100, PnmHeader::ColorMode::GRAY);
|
||||
REQUIRE(header.GetImageSize() == 10000);
|
||||
|
||||
header = PnmHeader(50, 50, PnmHeader::ColorMode::COLOR);
|
||||
REQUIRE(header.GetImageSize() == 7500);
|
||||
|
||||
header = PnmHeader(50, 50, PnmHeader::ColorMode::COLOR, 65535);
|
||||
REQUIRE(header.GetImageSize() == 15000);
|
||||
}
|
||||
|
||||
TEST_CASE("Ostream operator<< outputs correct header", "[Pnm]")
|
||||
{
|
||||
PnmHeader header(100, 200, PnmHeader::ColorMode::COLOR, 255, false);
|
||||
std::stringstream ss;
|
||||
ss << header;
|
||||
|
||||
std::string expectedOutput = "P6\n100 200\n255\n";
|
||||
REQUIRE(ss.str() == expectedOutput);
|
||||
}
|
||||
|
||||
TEST_CASE("Istream operator>> reads correctly formatted PNM header", "[Pnm]")
|
||||
{
|
||||
std::string pnmData = "P6\n100 200\n255\n";
|
||||
std::stringstream ss(pnmData);
|
||||
|
||||
PnmHeader header;
|
||||
ss >> header;
|
||||
|
||||
REQUIRE(header.width == 100);
|
||||
REQUIRE(header.height == 200);
|
||||
REQUIRE(header.maxValue == 255);
|
||||
REQUIRE(header.color == PnmHeader::ColorMode::COLOR);
|
||||
REQUIRE(header.ascii == false);
|
||||
}
|
||||
|
||||
TEST_CASE("Istream operator>> throws on malformed header", "[Pnm]")
|
||||
{
|
||||
{
|
||||
std::string malformedData = "X6\n100 200\n255\n";
|
||||
std::stringstream ss(malformedData);
|
||||
|
||||
PnmHeader header;
|
||||
REQUIRE_THROWS_AS(ss >> header, std::runtime_error);
|
||||
}
|
||||
|
||||
{
|
||||
std::string malformedData = "P9\n100 200\n255\n";
|
||||
std::stringstream ss(malformedData);
|
||||
|
||||
PnmHeader header;
|
||||
REQUIRE_THROWS_AS(ss >> header, std::runtime_error);
|
||||
}
|
||||
|
||||
{
|
||||
std::string malformedData = "P6\n100\n255\n";
|
||||
std::stringstream ss(malformedData);
|
||||
|
||||
PnmHeader header;
|
||||
REQUIRE_THROWS_AS(ss >> header, std::runtime_error);
|
||||
}
|
||||
|
||||
{
|
||||
std::string malformedData = "GarbageInput";
|
||||
std::stringstream ss(malformedData);
|
||||
|
||||
PnmHeader header;
|
||||
REQUIRE_THROWS_AS(ss >> header, std::runtime_error);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Read throws on unsupported ascii mode", "[Pnm]")
|
||||
{
|
||||
std::string pnmData = "P3\n100 100\n255\n";
|
||||
std::stringstream ss(pnmData);
|
||||
|
||||
PnmImage image;
|
||||
REQUIRE_THROWS_AS(image.Read(ss), std::runtime_error);
|
||||
}
|
||||
|
||||
TEST_CASE("Read successfully reads binary image data", "[Pnm]")
|
||||
{
|
||||
std::string pnmHeader = "P6\n2 2\n255\n";
|
||||
std::string pnmImage = "\xFF\x00\x00\x00\xFF\x00\x00\x00\xFF\xFF\x00\x00";
|
||||
std::stringstream ss(pnmHeader + pnmImage);
|
||||
|
||||
PnmImage image;
|
||||
REQUIRE_NOTHROW(image.Read(ss));
|
||||
|
||||
REQUIRE(image.header.width == 2);
|
||||
REQUIRE(image.header.height == 2);
|
||||
REQUIRE(image.header.maxValue == 255);
|
||||
REQUIRE(image.header.color == PnmHeader::ColorMode::COLOR);
|
||||
|
||||
REQUIRE(image.image[0] == '\xFF');
|
||||
REQUIRE(image.image[1] == '\x00');
|
||||
REQUIRE(image.image[2] == '\x00');
|
||||
}
|
||||
Reference in New Issue
Block a user