#include #include #include "PPMParser.hpp" namespace MC::GFX::Image { RawImage PPMParser::parse() { auto header = parse_header(); 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(header.width, header.height, 3); 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'; number *= 10; number += digit; } 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; 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(); } }