33 auto const bytes = as_bstring_view(_view);
34 read_header(
bytes, offset);
35 read_chunks(
bytes, offset);
77 big_uint32_buf_t length;
82 big_uint32_buf_t width;
83 big_uint32_buf_t height;
86 uint8_t compression_method;
87 uint8_t filter_method;
88 uint8_t interlace_method;
92 big_uint32_buf_t gamma;
96 big_uint32_buf_t white_point_x;
97 big_uint32_buf_t white_point_y;
98 big_uint32_buf_t red_x;
99 big_uint32_buf_t red_y;
100 big_uint32_buf_t green_x;
101 big_uint32_buf_t green_y;
102 big_uint32_buf_t blue_x;
103 big_uint32_buf_t blue_y;
107 uint8_t rendering_intent;
123 int _compression_method = 0;
124 int _filter_method = 0;
125 int _interlace_method = 0;
130 int _samples_per_pixel = 0;
131 int _bits_per_pixel = 0;
132 int _bytes_per_pixel = 0;
133 int _bytes_per_line = 0;
149 auto const c =
static_cast<char>(
bytes[i]);
156 throw parse_error(
"string is not null terminated.");
159 static uint8_t paeth_predictor(uint8_t
_a, uint8_t _b, uint8_t
_c)
noexcept
161 auto const a =
static_cast<int>(
_a);
162 auto const b =
static_cast<int>(_b);
163 auto const c =
static_cast<int>(
_c);
165 auto const p = a + b - c;
166 auto const pa = std::abs(p - a);
167 auto const pb = std::abs(p - b);
168 auto const pc = std::abs(p - c);
172 }
else if (
pb <=
pc) {
181 uint16_t value =
static_cast<uint8_t
>(
bytes[offset++]);
184 value |=
static_cast<uint8_t
>(
bytes[offset++]);
202 auto IHDR_bytes = std::span<std::byte const>{};
203 auto cHRM_bytes = std::span<std::byte const>{};
204 auto gAMA_bytes = std::span<std::byte const>{};
205 auto iCCP_bytes = std::span<std::byte const>{};
206 auto sRGB_bytes = std::span<std::byte const>{};
212 hi_check(length < 0x8000'0000,
"Chunk length must be smaller than 2GB");
213 hi_check(offset + length + ssizeof(uint32_t) <=
bytes.size(),
"Chuck extents beyond file.");
215 switch (fourcc(
header->type)) {
252 hi_check(!
IHDR_bytes.empty(),
"Missing IHDR chunk.");
272 void read_IHDR(std::span<std::byte const>
bytes)
276 _width = *
ihdr->width;
277 _height = *
ihdr->height;
278 _bit_depth =
ihdr->bit_depth;
279 _color_type =
ihdr->color_type;
280 _compression_method =
ihdr->compression_method;
281 _filter_method =
ihdr->filter_method;
282 _interlace_method =
ihdr->interlace_method;
284 hi_check(_width <= 16384,
"PNG width too large.");
285 hi_check(_height <= 16384,
"PNG height too large.");
286 hi_check(_bit_depth == 8 || _bit_depth == 16,
"PNG only bit depth of 8 or 16 is implemented.");
287 hi_check(_compression_method == 0,
"Only deflate/inflate compression is allowed.");
288 hi_check(_filter_method == 0,
"Only adaptive filtering is allowed.");
289 hi_check(_interlace_method == 0,
"Only non interlaced PNG are implemented.");
291 _is_palletted = (_color_type & 1) != 0;
292 _is_color = (_color_type & 2) != 0;
293 _has_alpha = (_color_type & 4) != 0;
294 hi_check((_color_type & 0xf8) == 0,
"Invalid color type");
295 hi_check(!_is_palletted,
"Paletted images are not supported");
298 _samples_per_pixel = 1;
300 _samples_per_pixel =
static_cast<int>(_has_alpha);
301 _samples_per_pixel += _is_color ? 3 : 1;
304 _bits_per_pixel = _samples_per_pixel * _bit_depth;
305 _bytes_per_line = (_bits_per_pixel * _width + 7) / 8;
306 _stride = _bytes_per_line + 1;
307 _bytes_per_pixel =
std::max(1, _bits_per_pixel / 8);
309 generate_sRGB_transfer_function();
312 void read_cHRM(std::span<std::byte const>
bytes)
329 void read_gAMA(std::span<std::byte const>
bytes)
333 hi_check(gamma != 0.0f,
"Gamma value can not be zero");
335 generate_gamma_transfer_function(1.0f / gamma);
338 void read_iCCP(std::span<std::byte const>
bytes)
347 generate_Rec2100_transfer_function();
352 void read_sRGB(std::span<std::byte const>
bytes)
355 auto const rendering_intent =
srgb->rendering_intent;
356 hi_check(rendering_intent <= 3,
"Invalid rendering intent");
359 generate_sRGB_transfer_function();
362 void generate_sRGB_transfer_function()
noexcept
364 auto const value_range = _bit_depth == 8 ? 256 : 65536;
372 void generate_Rec2100_transfer_function()
noexcept
377 auto const value_range = _bit_depth == 8 ? 256 : 65536;
385 void generate_gamma_transfer_function(
float gamma)
noexcept
387 auto const value_range = _bit_depth == 8 ? 256 : 65536;
397 if (
ssize(_idat_chunk_data) == 1) {
408 for (
auto const chunk_data : _idat_chunk_data) {
416 void unfilter_lines(bstring&
image_data)
const
422 for (
auto y = 0
_uz; y != _height; ++y) {
423 auto const line =
image_bytes.subspan(y * _stride, _stride);
425 prev_line = line.subspan(1, _bytes_per_line);
429 void unfilter_line(std::span<uint8_t> line, std::span<uint8_t const>
prev_line)
const
435 return unfilter_line_sub(line.subspan(1, _bytes_per_line),
prev_line);
437 return unfilter_line_up(line.subspan(1, _bytes_per_line),
prev_line);
439 return unfilter_line_average(line.subspan(1, _bytes_per_line),
prev_line);
441 return unfilter_line_paeth(line.subspan(1, _bytes_per_line),
prev_line);
447 void unfilter_line_sub(std::span<uint8_t> line, std::span<uint8_t const>
prev_line)
const noexcept
449 for (
int i = 0; i != _bytes_per_line; ++i) {
450 auto const j = i - _bytes_per_pixel;
457 void unfilter_line_up(std::span<uint8_t> line, std::span<uint8_t const>
prev_line)
const noexcept
459 for (
int i = 0; i != _bytes_per_line; ++i) {
464 void unfilter_line_average(std::span<uint8_t> line, std::span<uint8_t const>
prev_line)
const noexcept
466 for (
int i = 0; i != _bytes_per_line; ++i) {
467 auto const j = i - _bytes_per_pixel;
474 void unfilter_line_paeth(std::span<uint8_t> line, std::span<uint8_t const>
prev_line)
const noexcept
476 for (
int i = 0; i != _bytes_per_line; ++i) {
477 auto const j = i - _bytes_per_pixel;
480 uint8_t
const left =
j >= 0 ? line[
j] : 0;
490 for (
int y = 0; y != _height; ++y) {
491 int inv_y = _height - y - 1;
499 void data_to_image_line(std::span<std::byte const>
bytes, std::span<sfloat_rgba16> line)
const noexcept
501 auto const alpha_mul = _bit_depth == 16 ? 1.0f / 65535.0f : 1.0f / 255.0f;
502 for (
int x = 0; x != _width; ++x) {
503 auto const value = extract_pixel_from_line(
bytes, x);
506 f32x4{_transfer_function[value.x()], _transfer_function[value.y()], _transfer_function[value.z()], 1.0f};
509 auto const alpha =
static_cast<float>(value.w()) *
alpha_mul;
516 u16x4 extract_pixel_from_line(std::span<std::byte const>
bytes,
int x)
const noexcept
518 hi_axiom(_bit_depth == 8
or _bit_depth == 16);
519 hi_axiom(
not _is_palletted);
526 ssize_t offset = x * _bytes_per_pixel;
528 r = get_sample(
bytes, offset, _bit_depth == 16);
529 g = get_sample(
bytes, offset, _bit_depth == 16);
530 b = get_sample(
bytes, offset, _bit_depth == 16);
532 r = g = b = get_sample(
bytes, offset, _bit_depth == 16);
535 a = get_sample(
bytes, offset, _bit_depth == 16);
537 a = (_bit_depth == 16) ? 65535 : 255;
540 return u16x4{r, g, b, a};