155 lines
4.2 KiB
C++
155 lines
4.2 KiB
C++
/*
|
|
* 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 <string>
|
|
#include <sstream>
|
|
#include <memory>
|
|
#include <cmath>
|
|
|
|
namespace OpenVulkano
|
|
{
|
|
//TODO handle comments
|
|
/**
|
|
* Implements Portable Anymap file headers
|
|
* See https://de.wikipedia.org/wiki/Portable_Anymap
|
|
*/
|
|
class PnmHeader
|
|
{
|
|
static constexpr std::string_view FILE_TYPES[] = { "PBM", "PGM", "PPM" };
|
|
static constexpr std::string_view FILE_EXT[] = { ".pbm", ".pgm", ".ppm" };
|
|
public:
|
|
enum class ColorMode : uint8_t { BW = 1, GRAY, COLOR };
|
|
|
|
uint32_t width, height, maxValue;
|
|
ColorMode color;
|
|
bool ascii;
|
|
|
|
constexpr PnmHeader() : width(0), height(0), maxValue(0), color(ColorMode::GRAY), ascii(false) {}
|
|
|
|
constexpr PnmHeader(uint32_t width, uint32_t height, bool color, uint32_t maxValue = 255, bool ascii = false) :
|
|
width(width), height(height), maxValue(maxValue), color(color ? ColorMode::COLOR : ColorMode::GRAY), ascii(ascii)
|
|
{}
|
|
|
|
constexpr PnmHeader(uint32_t width, uint32_t height, ColorMode color = ColorMode::COLOR, uint32_t maxValue = 255, bool ascii = false) :
|
|
width(width), height(height), maxValue(maxValue), color(color), ascii(ascii)
|
|
{}
|
|
|
|
[[nodiscard]] constexpr std::string_view GetFileType() const
|
|
{
|
|
return FILE_TYPES[static_cast<uint8_t>(color) - 1];
|
|
}
|
|
|
|
[[nodiscard]] constexpr std::string_view GetFileExtension() const
|
|
{
|
|
return FILE_EXT[static_cast<uint8_t>(color) - 1];
|
|
}
|
|
|
|
[[nodiscard]] std::string ToString() const
|
|
{
|
|
std::stringstream headerStream;
|
|
headerStream << *this;
|
|
return headerStream.str();
|
|
}
|
|
|
|
[[nodiscard]] constexpr uint8_t GetMagicNumberValue() const
|
|
{
|
|
uint8_t value = static_cast<uint8_t>(color);
|
|
if (!ascii) value += 3;
|
|
return value;
|
|
}
|
|
|
|
[[nodiscard]] constexpr char GetMagicNumberChar() const
|
|
{
|
|
return '0' + static_cast<char>(GetMagicNumberValue());
|
|
}
|
|
|
|
friend std::ostream& operator<< (std::ostream& outStream, const PnmHeader& pnmHeader)
|
|
{
|
|
outStream << 'P';
|
|
outStream << pnmHeader.GetMagicNumberChar();
|
|
outStream << '\n';
|
|
outStream << pnmHeader.width << ' ' << pnmHeader.height << '\n';
|
|
outStream << pnmHeader.maxValue << '\n';
|
|
return outStream;
|
|
}
|
|
|
|
friend std::istream& operator>> (std::istream& inStream, PnmHeader& pnmHeader)
|
|
{
|
|
std::string marker;
|
|
inStream >> marker;
|
|
if (marker.length() != 2 || marker[0] != 'P') throw std::runtime_error("Malformed PNM header!");
|
|
uint8_t val = marker[1] - '0';
|
|
if (val > 6) throw std::runtime_error("Malformed PNM header!");
|
|
pnmHeader.ascii = val < 4;
|
|
pnmHeader.color = static_cast<ColorMode>(val - 3);
|
|
|
|
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;
|
|
}
|
|
|
|
[[nodiscard]] constexpr size_t GetImageSize() const
|
|
{
|
|
size_t size = height * width;
|
|
if (color == ColorMode::COLOR)
|
|
{
|
|
size *= 3;
|
|
if (maxValue > 255) size *= 2;
|
|
return size;
|
|
}
|
|
if (color == ColorMode::BW)
|
|
{
|
|
return std::ceil(size / 8.0);
|
|
}
|
|
return size;
|
|
}
|
|
};
|
|
|
|
struct PnmImage
|
|
{
|
|
PnmHeader header;
|
|
std::unique_ptr<char[]> image;
|
|
|
|
void Read(std::istream& inStream)
|
|
{
|
|
inStream >> header;
|
|
|
|
// TODO handle ascii mode
|
|
if (header.ascii) throw std::runtime_error("ascii mode not supported!");
|
|
|
|
size_t size = header.GetImageSize();
|
|
image = std::make_unique<char[]>(size);
|
|
inStream.read(image.get(), size);
|
|
}
|
|
|
|
static PnmImage ReadImage(std::istream& inStream)
|
|
{
|
|
PnmImage image;
|
|
image.Read(inStream);
|
|
return image;
|
|
}
|
|
};
|
|
}
|