From 799c06e0387e01bdb8a10019be6192f9db00a824 Mon Sep 17 00:00:00 2001 From: Mel Date: Sat, 8 Oct 2022 03:18:18 +0200 Subject: Parse PPM images --- src/Image/PPMParser.cpp | 164 ++++++++++++++++++++++++++++++++++++++++++++++++ src/Image/PPMParser.hpp | 44 +++++++++++++ src/Image/RawImage.cpp | 17 +++++ src/Image/RawImage.hpp | 30 +++++++++ 4 files changed, 255 insertions(+) create mode 100644 src/Image/PPMParser.cpp create mode 100644 src/Image/PPMParser.hpp create mode 100644 src/Image/RawImage.cpp create mode 100644 src/Image/RawImage.hpp (limited to 'src') diff --git a/src/Image/PPMParser.cpp b/src/Image/PPMParser.cpp new file mode 100644 index 0000000..0a9da54 --- /dev/null +++ b/src/Image/PPMParser.cpp @@ -0,0 +1,164 @@ +#include +#include +#include +#include "PPMParser.hpp" + +namespace MC::Image { + +RawImage PPMParser::parse() { + auto header = parse_header(); + + std::cout << header.type << " " << header.width << " " << header.height << " " << (uint64_t)header.max_color << std::endl; + + if (header.max_color != 255) { + throw std::logic_error("PPM max color values other than 255 are not implemented."); + } + + if (header.type != P3) { + throw std::logic_error("Raw PPM not implemented."); + } + + auto pixel_count = header.width * header.height; + + RawImage image(pixel_count); + for (uint64_t pixel_index = 0; pixel_index < pixel_count; pixel_index++) { + RawImage::Pixel pixel = parse_pixel(header.max_color); + image.add(pixel); + } + + return image; +} + +PPMParser::PPMHeader PPMParser::parse_header() { + PPMHeader header{}; + + skip_whitespace(); + + auto type_part = chomp_part(); + skip_whitespace(); + + if (type_part == "P3") { + header.type = P3; + } else if (type_part == "P6") { + header.type = P6; + } else { + throw std::runtime_error("Unknown PPM type."); + } + + auto width_part = chomp_number(); + skip_whitespace(); + header.width = width_part; + + auto height_part = chomp_number(); + skip_whitespace(); + header.height = height_part; + + auto max_color_part = chomp_number(); + skip_whitespace(); + header.max_color = max_color_part; + + return header; +} + +RawImage::Pixel PPMParser::parse_pixel(uint8_t max_color) { + auto r_sample = parse_sample(); + auto g_sample = parse_sample(); + auto b_sample = parse_sample(); + + if (r_sample > max_color || g_sample > max_color || b_sample > max_color) { + throw std::runtime_error("Sample can not be greater than Maxval."); + } + + auto map_to_range = [=](uint64_t s) -> uint8_t { return (s * 255) / max_color; }; + + RawImage::Pixel pixel{}; + pixel.r = map_to_range(r_sample); + pixel.g = map_to_range(g_sample); + pixel.b = map_to_range(b_sample); + + return pixel; +} + +uint64_t PPMParser::parse_sample() { + skip_whitespace(); + auto sample = chomp_number(); + skip_whitespace(); + return sample; +} + +uint64_t PPMParser::chomp_number() { + auto raw = chomp_part(); + + uint64_t number = 0; + for (uint8_t digit_ascii : raw) { + if (digit_ascii < '0' || digit_ascii > '9') { + throw std::runtime_error("Number contains non ASCII digits."); + } + + uint8_t digit = digit_ascii - '0'; + + std::cout << "digit_ascii: " << digit_ascii << std::endl; + std::cout << "digit: " << (uint64_t)digit << std::endl; + + number *= 10; + number += digit; + + std::cout << "number: " << (uint64_t)number << std::endl; + } + + std::cout << "chomp number: " << number << std::endl; + return number; +} + +std::string_view PPMParser::chomp_part() { + uint64_t length = 0; + + while (!is_eof()) { + auto c = m_source[m_cursor + length]; + if (std::isspace(c)) { + break; + } + + length++; + } + + auto part = m_source.substr(m_cursor, length); + m_cursor += length; + + std::cout << "chomped: " << part << std::endl; + return part; +} + +void PPMParser::skip_whitespace() { + uint8_t c; + while (!is_eof()) { + c = m_source[m_cursor]; + if (c == '#') { + skip_comment(); + continue; + } + + if (!std::isspace(c)) { + break; + } + + m_cursor++; + } +} + +void PPMParser::skip_comment() { + uint8_t c = m_source[m_cursor]; + if (c != '#') { + return; + } + + while (c != '\n' && !is_eof()) { + c = m_source[++m_cursor]; + } +} + +bool PPMParser::is_eof() { + return m_cursor >= m_source.size(); +} + +} \ No newline at end of file diff --git a/src/Image/PPMParser.hpp b/src/Image/PPMParser.hpp new file mode 100644 index 0000000..7d81cb8 --- /dev/null +++ b/src/Image/PPMParser.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include "RawImage.hpp" + +namespace MC::Image { + +class PPMParser { +public: + explicit PPMParser(std::string_view source) : m_source(source) {}; + + RawImage parse(); +private: + enum PPMType { + None, + P3, + P6 + }; + + struct PPMHeader { + PPMType type; + uint32_t width; + uint32_t height; + uint8_t max_color; + }; + + PPMHeader parse_header(); + RawImage::Pixel parse_pixel(uint8_t max_color); + uint64_t parse_sample(); + + uint64_t chomp_number(); + std::string_view chomp_part(); + + void skip_whitespace(); + void skip_comment(); + + bool is_eof(); + + std::string_view m_source; + uint64_t m_cursor = 0; +}; + +} diff --git a/src/Image/RawImage.cpp b/src/Image/RawImage.cpp new file mode 100644 index 0000000..50478ad --- /dev/null +++ b/src/Image/RawImage.cpp @@ -0,0 +1,17 @@ +#include "RawImage.hpp" + +namespace MC::Image { + +void RawImage::add(RawImage::Pixel pixel) { + m_pixels.push_back(pixel); +} + +size_t RawImage::size() { + return m_pixels.size(); +} + +uint8_t* RawImage::raw() { + return (uint8_t*)m_pixels.data(); +} + +} \ No newline at end of file diff --git a/src/Image/RawImage.hpp b/src/Image/RawImage.hpp new file mode 100644 index 0000000..4414ee0 --- /dev/null +++ b/src/Image/RawImage.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +namespace MC::Image { + +class RawImage { +public: + RawImage() : m_pixels() {}; + + explicit RawImage(size_t pixel_count) : m_pixels() { + m_pixels.reserve(pixel_count); + } + + struct Pixel { + uint8_t r, g, b; + }; + + void add(Pixel pixel); + + size_t size(); + uint8_t* raw(); + +private: + std::vector m_pixels; +}; + +} -- cgit 1.4.1