From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../jpeg-xl/experimental/fast_lossless/pam-input.h | 289 +++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 third_party/jpeg-xl/experimental/fast_lossless/pam-input.h (limited to 'third_party/jpeg-xl/experimental/fast_lossless/pam-input.h') diff --git a/third_party/jpeg-xl/experimental/fast_lossless/pam-input.h b/third_party/jpeg-xl/experimental/fast_lossless/pam-input.h new file mode 100644 index 0000000000..4ecbe6b72d --- /dev/null +++ b/third_party/jpeg-xl/experimental/fast_lossless/pam-input.h @@ -0,0 +1,289 @@ +// Copyright (c) the JPEG XL Project Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include + +bool error_msg(const char* message) { + fprintf(stderr, "%s\n", message); + return false; +} +#define return_on_error(X) \ + if (!X) return false; + +size_t Log2(uint32_t value) { return 31 - __builtin_clz(value); } + +struct HeaderPNM { + size_t xsize; + size_t ysize; + bool is_gray; // PGM + bool has_alpha; // PAM + size_t bits_per_sample; +}; + +class Parser { + public: + explicit Parser(uint8_t* data, size_t length) + : pos_(data), end_(data + length) {} + + // Sets "pos" to the first non-header byte/pixel on success. + bool ParseHeader(HeaderPNM* header, const uint8_t** pos) { + // codec.cc ensures we have at least two bytes => no range check here. + if (pos_[0] != 'P') return false; + const uint8_t type = pos_[1]; + pos_ += 2; + + switch (type) { + case '5': + header->is_gray = true; + return ParseHeaderPNM(header, pos); + + case '6': + header->is_gray = false; + return ParseHeaderPNM(header, pos); + + case '7': + return ParseHeaderPAM(header, pos); + } + return false; + } + + // Exposed for testing + bool ParseUnsigned(size_t* number) { + if (pos_ == end_) return error_msg("PNM: reached end before number"); + if (!IsDigit(*pos_)) return error_msg("PNM: expected unsigned number"); + + *number = 0; + while (pos_ < end_ && *pos_ >= '0' && *pos_ <= '9') { + *number *= 10; + *number += *pos_ - '0'; + ++pos_; + } + + return true; + } + + bool ParseSigned(double* number) { + if (pos_ == end_) return error_msg("PNM: reached end before signed"); + + if (*pos_ != '-' && *pos_ != '+' && !IsDigit(*pos_)) { + return error_msg("PNM: expected signed number"); + } + + // Skip sign + const bool is_neg = *pos_ == '-'; + if (is_neg || *pos_ == '+') { + ++pos_; + if (pos_ == end_) return error_msg("PNM: reached end before digits"); + } + + // Leading digits + *number = 0.0; + while (pos_ < end_ && *pos_ >= '0' && *pos_ <= '9') { + *number *= 10; + *number += *pos_ - '0'; + ++pos_; + } + + // Decimal places? + if (pos_ < end_ && *pos_ == '.') { + ++pos_; + double place = 0.1; + while (pos_ < end_ && *pos_ >= '0' && *pos_ <= '9') { + *number += (*pos_ - '0') * place; + place *= 0.1; + ++pos_; + } + } + + if (is_neg) *number = -*number; + return true; + } + + private: + static bool IsDigit(const uint8_t c) { return '0' <= c && c <= '9'; } + static bool IsLineBreak(const uint8_t c) { return c == '\r' || c == '\n'; } + static bool IsWhitespace(const uint8_t c) { + return IsLineBreak(c) || c == '\t' || c == ' '; + } + + bool SkipBlank() { + if (pos_ == end_) return error_msg("PNM: reached end before blank"); + const uint8_t c = *pos_; + if (c != ' ' && c != '\n') return error_msg("PNM: expected blank"); + ++pos_; + return true; + } + + bool SkipSingleWhitespace() { + if (pos_ == end_) return error_msg("PNM: reached end before whitespace"); + if (!IsWhitespace(*pos_)) return error_msg("PNM: expected whitespace"); + ++pos_; + return true; + } + + bool SkipWhitespace() { + if (pos_ == end_) return error_msg("PNM: reached end before whitespace"); + if (!IsWhitespace(*pos_) && *pos_ != '#') { + return error_msg("PNM: expected whitespace/comment"); + } + + while (pos_ < end_ && IsWhitespace(*pos_)) { + ++pos_; + } + + // Comment(s) + while (pos_ != end_ && *pos_ == '#') { + while (pos_ != end_ && !IsLineBreak(*pos_)) { + ++pos_; + } + // Newline(s) + while (pos_ != end_ && IsLineBreak(*pos_)) pos_++; + } + + while (pos_ < end_ && IsWhitespace(*pos_)) { + ++pos_; + } + return true; + } + + bool MatchString(const char* keyword) { + const uint8_t* ppos = pos_; + while (*keyword) { + if (ppos >= end_) return error_msg("PAM: unexpected end of input"); + if (*keyword != *ppos) return false; + ppos++; + keyword++; + } + pos_ = ppos; + return_on_error(SkipWhitespace()); + return true; + } + + bool ParseHeaderPAM(HeaderPNM* header, const uint8_t** pos) { + size_t num_channels = 3; + size_t max_val = 255; + while (!MatchString("ENDHDR")) { + return_on_error(SkipWhitespace()); + if (MatchString("WIDTH")) { + return_on_error(ParseUnsigned(&header->xsize)); + } else if (MatchString("HEIGHT")) { + return_on_error(ParseUnsigned(&header->ysize)); + } else if (MatchString("DEPTH")) { + return_on_error(ParseUnsigned(&num_channels)); + } else if (MatchString("MAXVAL")) { + return_on_error(ParseUnsigned(&max_val)); + } else if (MatchString("TUPLTYPE")) { + if (MatchString("RGB_ALPHA")) { + header->has_alpha = true; + } else if (MatchString("RGB")) { + } else if (MatchString("GRAYSCALE_ALPHA")) { + header->has_alpha = true; + header->is_gray = true; + } else if (MatchString("GRAYSCALE")) { + header->is_gray = true; + } else if (MatchString("BLACKANDWHITE_ALPHA")) { + header->has_alpha = true; + header->is_gray = true; + max_val = 1; + } else if (MatchString("BLACKANDWHITE")) { + header->is_gray = true; + max_val = 1; + } else { + return error_msg("PAM: unknown TUPLTYPE"); + } + } else { + return error_msg("PAM: unknown header keyword"); + } + } + if (num_channels != + (header->has_alpha ? 1 : 0) + (header->is_gray ? 1 : 3)) { + return error_msg("PAM: bad DEPTH"); + } + if (max_val == 0 || max_val >= 65536) { + return error_msg("PAM: bad MAXVAL"); + } + header->bits_per_sample = Log2(max_val + 1); + + *pos = pos_; + return true; + } + + bool ParseHeaderPNM(HeaderPNM* header, const uint8_t** pos) { + return_on_error(SkipWhitespace()); + return_on_error(ParseUnsigned(&header->xsize)); + + return_on_error(SkipWhitespace()); + return_on_error(ParseUnsigned(&header->ysize)); + + return_on_error(SkipWhitespace()); + size_t max_val; + return_on_error(ParseUnsigned(&max_val)); + if (max_val == 0 || max_val >= 65536) { + return error_msg("PNM: bad MaxVal"); + } + header->bits_per_sample = Log2(max_val + 1); + + return_on_error(SkipSingleWhitespace()); + + *pos = pos_; + return true; + } + + const uint8_t* pos_; + const uint8_t* const end_; +}; + +bool load_file(unsigned char** out, size_t* outsize, const char* filename) { + FILE* file; + file = fopen(filename, "rb"); + if (!file) return false; + if (fseek(file, 0, SEEK_END) != 0) { + fclose(file); + return false; + } + *outsize = ftell(file); + if (*outsize == LONG_MAX || *outsize < 9 || fseek(file, 0, SEEK_SET)) { + fclose(file); + return false; + } + *out = (unsigned char*)malloc(*outsize); + if (!(*out)) return false; + size_t readsize; + readsize = fread(*out, 1, *outsize, file); + fclose(file); + if (readsize != *outsize) return false; + return true; +} + +bool DecodePAM(const char* filename, uint8_t** buffer, size_t* w, size_t* h, + size_t* nb_chans, size_t* bitdepth) { + unsigned char* in_file; + size_t in_size; + if (!load_file(&in_file, &in_size, filename)) + return error_msg("Could not read input file"); + Parser parser(in_file, in_size); + HeaderPNM header = {}; + const uint8_t* pos = nullptr; + if (!parser.ParseHeader(&header, &pos)) return false; + + if (header.bits_per_sample == 0 || header.bits_per_sample > 16) { + return error_msg("PNM: bits_per_sample invalid (can do at most 16-bit)"); + } + *w = header.xsize; + *h = header.ysize; + *bitdepth = header.bits_per_sample; + *nb_chans = (header.is_gray ? 1 : 3) + (header.has_alpha ? 1 : 0); + + size_t pnm_remaining_size = in_file + in_size - pos; + size_t buffer_size = *w * *h * *nb_chans * (*bitdepth > 8 ? 2 : 1); + if (pnm_remaining_size < buffer_size) { + return error_msg("PNM file too small"); + } + *buffer = (uint8_t*)malloc(buffer_size); + memcpy(*buffer, pos, buffer_size); + return true; +} -- cgit v1.2.3