blob: d7d8c6acca6c960a9cc4c307c18ef916c836fa60 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
#include <cctype>
#include <string>
#include <iostream>
#include "PPMParser.hpp"
namespace MC::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(pixel_count, 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();
}
}
|