blob: 3c95bbac2eddff2222963c8fb255775131f73d39 (
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
156
157
158
159
|
#include <cctype>
#include <stdexcept>
#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);
for (U64 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(U8 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 = [=](U64 s) -> U8 { return s * 255 / max_color; };
RawImage::Pixel p{
map_to_range(r_sample),
map_to_range(g_sample),
map_to_range(b_sample),
255
};
if (p.r == alpha_color.r && p.g == alpha_color.g && p.b == alpha_color.b) {
p.a = 0;
}
return p;
}
U64 PPMParser::parse_sample() {
skip_whitespace();
auto sample = chomp_number();
skip_whitespace();
return sample;
}
U64 PPMParser::chomp_number() {
auto raw = chomp_part();
U64 number = 0;
for (U8 digit_ascii : raw) {
if (digit_ascii < '0' || digit_ascii > '9') {
throw std::runtime_error("Number contains non ASCII digits.");
}
U8 digit = digit_ascii - '0';
number *= 10;
number += digit;
}
return number;
}
std::string_view PPMParser::chomp_part() {
U64 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() {
while (!is_eof()) {
U8 c = m_source[m_cursor];
if (c == '#') {
skip_comment();
continue;
}
if (!std::isspace(c)) {
break;
}
m_cursor++;
}
}
void PPMParser::skip_comment() {
U8 c = m_source[m_cursor];
if (c != '#') {
return;
}
while (c != '\n' && !is_eof()) {
c = m_source[++m_cursor];
}
}
Bool PPMParser::is_eof() const {
return m_cursor >= m_source.size();
}
}
|