Adding new files (latest tinyexr library)

This commit is contained in:
Richard Geldreich
2026-04-07 16:36:17 -04:00
parent 801b67dbf3
commit ae89aab33c
2 changed files with 362 additions and 0 deletions

191
encoder/3rdparty/exr_reader.hh vendored Normal file
View File

@@ -0,0 +1,191 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2025, Syoyo Fujita and many contributors.
// All rights reserved.
//
// EXR Reader: Reader class with error stack for safe memory reading
#ifndef TINYEXR_EXR_READER_HH_
#define TINYEXR_EXR_READER_HH_
#include <vector>
#include <string>
#include "streamreader.hh"
namespace tinyexr {
// Reader class that wraps StreamReader and accumulates errors
class Reader {
public:
Reader(const uint8_t* data, size_t length, Endian endian = Endian::Little)
: stream_(data, length, endian), has_error_(false) {}
// Check if any errors have occurred
bool has_error() const { return has_error_; }
// Get all accumulated errors
const std::vector<std::string>& errors() const { return errors_; }
// Get the most recent error
std::string last_error() const {
return errors_.empty() ? "" : errors_.back();
}
// Get all errors as a single string
std::string all_errors() const {
std::string result;
for (size_t i = 0; i < errors_.size(); i++) {
if (i > 0) result += "\n";
result += errors_[i];
}
return result;
}
// Clear error stack
void clear_errors() {
errors_.clear();
has_error_ = false;
}
// Read n bytes into destination buffer
bool read(size_t n, uint8_t* dst) {
if (!stream_.read(n, dst)) {
add_error("Failed to read " + std::to_string(n) + " bytes at position " +
std::to_string(stream_.tell()));
return false;
}
return true;
}
// Read 1 byte
bool read1(uint8_t* dst) {
if (!stream_.read1(dst)) {
add_error("Failed to read 1 byte at position " +
std::to_string(stream_.tell()));
return false;
}
return true;
}
// Read 2 bytes with endian swap
bool read2(uint16_t* dst) {
if (!stream_.read2(dst)) {
add_error("Failed to read 2 bytes at position " +
std::to_string(stream_.tell()));
return false;
}
return true;
}
// Read 4 bytes with endian swap
bool read4(uint32_t* dst) {
if (!stream_.read4(dst)) {
add_error("Failed to read 4 bytes at position " +
std::to_string(stream_.tell()));
return false;
}
return true;
}
// Read 8 bytes with endian swap
bool read8(uint64_t* dst) {
if (!stream_.read8(dst)) {
add_error("Failed to read 8 bytes at position " +
std::to_string(stream_.tell()));
return false;
}
return true;
}
// Read a null-terminated string up to max_len bytes
// Returns false if no null terminator found within max_len
bool read_string(std::string* str, size_t max_len = 256) {
if (!str) {
add_error("Null pointer passed to read_string");
return false;
}
str->clear();
size_t start_pos = stream_.tell();
for (size_t i = 0; i < max_len; i++) {
uint8_t c;
if (!stream_.read1(&c)) {
add_error("Failed to read string at position " + std::to_string(start_pos));
return false;
}
if (c == '\0') {
return true;
}
str->push_back(static_cast<char>(c));
}
add_error("String not null-terminated within " + std::to_string(max_len) +
" bytes at position " + std::to_string(start_pos));
return false;
}
// Seek to absolute position
bool seek(size_t pos) {
if (!stream_.seek(pos)) {
add_error("Failed to seek to position " + std::to_string(pos));
return false;
}
return true;
}
// Seek relative to current position
bool seek_relative(int64_t offset) {
size_t current = stream_.tell();
int64_t new_pos = static_cast<int64_t>(current) + offset;
if (new_pos < 0) {
add_error("Seek would move before start of stream");
return false;
}
return seek(static_cast<size_t>(new_pos));
}
// Rewind to beginning
void rewind() {
stream_.rewind();
}
// Get current position
size_t tell() const {
return stream_.tell();
}
// Get remaining bytes
size_t remaining() const {
return stream_.remaining();
}
// Check if at end
bool eof() const {
return stream_.eof();
}
// Get total length
size_t length() const {
return stream_.length();
}
// Add a custom error message
void add_error(const std::string& msg) {
errors_.push_back(msg);
has_error_ = true;
}
// Get direct access to underlying StreamReader (use with caution)
const StreamReader& stream() const { return stream_; }
private:
StreamReader stream_;
std::vector<std::string> errors_;
bool has_error_;
};
} // namespace tinyexr
#endif // TINYEXR_EXR_READER_HH_

171
encoder/3rdparty/streamreader.hh vendored Normal file
View File

@@ -0,0 +1,171 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2025, Syoyo Fujita and many contributors.
// All rights reserved.
//
// StreamReader: Simple header-only stream reader with endian support
//
// Part of TinyEXR V2 API (EXPERIMENTAL)
#ifndef TINYEXR_STREAMREADER_HH_
#define TINYEXR_STREAMREADER_HH_
#include <cstdint>
#include <cstring>
namespace tinyexr {
enum class Endian {
Little,
Big,
Native
};
class StreamReader {
public:
// Constructor: takes memory address, length, and endianness
StreamReader(const uint8_t* data, size_t length, Endian endian = Endian::Little)
: data_(data), length_(length), pos_(0), endian_(endian) {
// Detect native endian
uint16_t test = 0x0001;
native_is_little_ = (*reinterpret_cast<uint8_t*>(&test) == 0x01);
// Determine if we need to swap bytes
if (endian_ == Endian::Native) {
needs_swap_ = false;
} else {
bool data_is_little = (endian_ == Endian::Little);
needs_swap_ = (data_is_little != native_is_little_);
}
}
// Read n bytes into destination buffer
// Returns false on out-of-bounds or error
bool read(size_t n, uint8_t* dst) {
if (!dst || n == 0) {
return false;
}
if (pos_ + n > length_) {
return false; // Out of bounds
}
std::memcpy(dst, data_ + pos_, n);
pos_ += n;
return true;
}
// Read 1 byte (uint8_t)
bool read1(uint8_t* dst) {
return read(1, dst);
}
// Read 2 bytes (uint16_t) with endian swap if needed
bool read2(uint16_t* dst) {
if (!dst) {
return false;
}
uint8_t buf[2];
if (!read(2, buf)) {
return false;
}
if (needs_swap_) {
*dst = static_cast<uint16_t>(buf[1]) << 8 |
static_cast<uint16_t>(buf[0]);
} else {
std::memcpy(dst, buf, 2);
}
return true;
}
// Read 4 bytes (uint32_t) with endian swap if needed
bool read4(uint32_t* dst) {
if (!dst) {
return false;
}
uint8_t buf[4];
if (!read(4, buf)) {
return false;
}
if (needs_swap_) {
*dst = static_cast<uint32_t>(buf[3]) << 24 |
static_cast<uint32_t>(buf[2]) << 16 |
static_cast<uint32_t>(buf[1]) << 8 |
static_cast<uint32_t>(buf[0]);
} else {
std::memcpy(dst, buf, 4);
}
return true;
}
// Read 8 bytes (uint64_t) with endian swap if needed
bool read8(uint64_t* dst) {
if (!dst) {
return false;
}
uint8_t buf[8];
if (!read(8, buf)) {
return false;
}
if (needs_swap_) {
*dst = static_cast<uint64_t>(buf[7]) << 56 |
static_cast<uint64_t>(buf[6]) << 48 |
static_cast<uint64_t>(buf[5]) << 40 |
static_cast<uint64_t>(buf[4]) << 32 |
static_cast<uint64_t>(buf[3]) << 24 |
static_cast<uint64_t>(buf[2]) << 16 |
static_cast<uint64_t>(buf[1]) << 8 |
static_cast<uint64_t>(buf[0]);
} else {
std::memcpy(dst, buf, 8);
}
return true;
}
// Seek to absolute position
// Returns false if position is out of bounds
bool seek(size_t pos) {
if (pos > length_) {
return false;
}
pos_ = pos;
return true;
}
// Rewind to beginning
void rewind() {
pos_ = 0;
}
// Get current position
size_t tell() const {
return pos_;
}
// Get remaining bytes
size_t remaining() const {
return length_ - pos_;
}
// Check if at end
bool eof() const {
return pos_ >= length_;
}
// Get total length
size_t length() const {
return length_;
}
private:
const uint8_t* data_;
size_t length_;
size_t pos_;
Endian endian_;
bool native_is_little_;
bool needs_swap_;
};
} // namespace tinyexr
#endif // TINYEXR_STREAMREADER_HH_