summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/Image/PPMParser.cpp164
-rw-r--r--src/Image/PPMParser.hpp44
-rw-r--r--src/Image/RawImage.cpp17
-rw-r--r--src/Image/RawImage.hpp30
4 files changed, 255 insertions, 0 deletions
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 <cctype>
+#include <string>
+#include <iostream>
+#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 <cstdint>
+#include <string_view>
+#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 <cstdint>
+#include <cstddef>
+#include <vector>
+
+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<Pixel> m_pixels;
+};
+
+}