#include "Decoder/Jpeg.hpp" #include #include #include #define STB_IMAGE_WRITE_STATIC #define STB_IMAGE_WRITE_IMPLEMENTATION #define STBI_WRITE_NO_STDIO #include "stb_image_write.h" #define STB_IMAGE_STATIC #define STB_IMAGE_IMPLEMENTATION #define STBI_ONLY_JPEG #define STBI_NO_STDIO #include "stb_image.h" namespace Decoder::Jpeg { namespace { constexpr std::uint32_t MAX_DIMENSION = 0xFFFF; void appendBytes(void* context, void* data, int size) { auto* output = static_cast*>(context); const auto* bytes = static_cast(data); output->insert(output->end(), bytes, bytes + size); } } // namespace std::vector Encode(std::span pixels, std::uint32_t width, std::uint32_t height, std::uint32_t channels, int quality) { if (width == 0 || height == 0 || width > MAX_DIMENSION || height > MAX_DIMENSION) { throw std::invalid_argument("Jpeg::Encode: unsupported image size"); } if (channels != 1 && channels != 3) throw std::invalid_argument("Jpeg::Encode: channels must be 1 or 3"); if (quality < 1 || quality > 100) throw std::invalid_argument("Jpeg::Encode: quality must be 1-100"); if (pixels.size() < static_cast(width) * height * channels) { throw std::invalid_argument("Jpeg::Encode: pixel buffer is too small"); } std::vector output; const int written = stbi_write_jpg_to_func(appendBytes, &output, static_cast(width), static_cast(height), static_cast(channels), pixels.data(), quality); if (written == 0) throw std::runtime_error("Jpeg::Encode: encoding failed"); return output; } std::optional
ParseHeader(std::span jpeg) { if (jpeg.empty() || jpeg.size() > INT_MAX) return std::nullopt; int width = 0; int height = 0; int channels = 0; if (!stbi_info_from_memory(jpeg.data(), static_cast(jpeg.size()), &width, &height, &channels)) return std::nullopt; if (width <= 0 || height <= 0 || channels <= 0) return std::nullopt; return Header{static_cast(width), static_cast(height), static_cast(channels)}; } std::optional Decode(std::span jpeg) { if (jpeg.empty() || jpeg.size() > INT_MAX) return std::nullopt; int width = 0; int height = 0; int channels = 0; stbi_uc* decoded = stbi_load_from_memory(jpeg.data(), static_cast(jpeg.size()), &width, &height, &channels, 0); if (!decoded) return std::nullopt; Image image{static_cast(width), static_cast(height), static_cast(channels), {}}; image.pixels.assign(decoded, decoded + static_cast(image.width) * image.height * image.channels); stbi_image_free(decoded); return image; } } // namespace Decoder::Jpeg