#include "assets/image_format.h" #include #include namespace pp::assets { namespace { constexpr std::array png_signature { std::byte { 0x89 }, std::byte { 0x50 }, std::byte { 0x4e }, std::byte { 0x47 }, std::byte { 0x0d }, std::byte { 0x0a }, std::byte { 0x1a }, std::byte { 0x0a }, }; [[nodiscard]] bool starts_with(std::span bytes, std::span prefix) noexcept { if (bytes.size() < prefix.size()) { return false; } for (std::size_t i = 0; i < prefix.size(); ++i) { if (bytes[i] != prefix[i]) { return false; } } return true; } [[nodiscard]] bool is_prefix_of(std::span bytes, std::span signature) noexcept { if (bytes.size() >= signature.size()) { return false; } for (std::size_t i = 0; i < bytes.size(); ++i) { if (bytes[i] != signature[i]) { return false; } } return true; } } pp::foundation::Result detect_image_format(std::span bytes) noexcept { if (bytes.empty()) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("image data must not be empty")); } if (starts_with(bytes, png_signature)) { return pp::foundation::Result::success(ImageFormat::png); } if (is_prefix_of(bytes, png_signature)) { return pp::foundation::Result::failure( pp::foundation::Status::out_of_range("image data is a truncated PNG signature")); } if (bytes.size() < 3U) { return pp::foundation::Result::failure( pp::foundation::Status::out_of_range("image data is too short to identify")); } if (bytes[0] == std::byte { 0xff } && bytes[1] == std::byte { 0xd8 } && bytes[2] == std::byte { 0xff }) { return pp::foundation::Result::success(ImageFormat::jpeg); } return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("unsupported image signature")); } const char* image_format_name(ImageFormat format) noexcept { switch (format) { case ImageFormat::png: return "png"; case ImageFormat::jpeg: return "jpeg"; } return "unknown"; } }