mirror of
https://github.com/mapnik/mapnik.git
synced 2026-01-18 16:27:36 +00:00
249 lines
8.2 KiB
C++
249 lines
8.2 KiB
C++
/*****************************************************************************
|
|
*
|
|
* This file is part of Mapnik (c++ mapping toolkit)
|
|
*
|
|
* Copyright (C) 2025 Artem Pavlenko
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*****************************************************************************/
|
|
|
|
// mapnik
|
|
#include <mapnik/image_reader.hpp>
|
|
#include <mapnik/image_util.hpp>
|
|
#include <mapnik/color.hpp>
|
|
// avif
|
|
#include <avif/avif.h>
|
|
// std
|
|
#include <cstdio>
|
|
#include <memory>
|
|
#include <fstream>
|
|
|
|
namespace mapnik {
|
|
|
|
using avif_decoder_ptr = std::unique_ptr<avifDecoder, void (*)(avifDecoder*)>;
|
|
using avif_image_ptr = std::unique_ptr<avifImage, void (*)(avifImage*)>;
|
|
|
|
template<typename T>
|
|
class avif_reader : public image_reader
|
|
{
|
|
public:
|
|
using buffer_policy_type = T;
|
|
|
|
private:
|
|
avif_decoder_ptr decoder_{avifDecoderCreate(), [](avifDecoder* ptr) {
|
|
avifDecoderDestroy(ptr);
|
|
}};
|
|
std::unique_ptr<buffer_policy_type> buffer_;
|
|
size_t size_ = 0;
|
|
unsigned width_ = 0;
|
|
unsigned height_ = 0;
|
|
unsigned depth_ = 8;
|
|
bool has_alpha_ = false;
|
|
|
|
public:
|
|
explicit avif_reader(std::string const& filename);
|
|
explicit avif_reader(char const* data, size_t size);
|
|
~avif_reader();
|
|
unsigned width() const final;
|
|
unsigned height() const final;
|
|
std::optional<box2d<double>> bounding_box() const override final;
|
|
inline bool has_alpha() const final { return has_alpha_; }
|
|
void read(unsigned x, unsigned y, image_rgba8& image) final;
|
|
image_any read(unsigned x, unsigned y, unsigned width, unsigned height) final;
|
|
|
|
private:
|
|
void init();
|
|
};
|
|
|
|
image_reader* create_avif_reader(std::string const& filename)
|
|
{
|
|
return new avif_reader<internal_buffer_policy>(filename);
|
|
}
|
|
|
|
image_reader* create_avif_reader2(char const* data, size_t size)
|
|
{
|
|
return new avif_reader<external_buffer_policy>(data, size);
|
|
}
|
|
void register_avif_reader()
|
|
{
|
|
[[maybe_unused]] bool const registered = register_image_reader("avif", create_avif_reader);
|
|
[[maybe_unused]] bool const registered2 = register_image_reader("avif", create_avif_reader2);
|
|
}
|
|
|
|
// ctors
|
|
template<typename T>
|
|
avif_reader<T>::avif_reader(std::string const& filename)
|
|
: buffer_(nullptr)
|
|
{
|
|
std::ifstream file(filename.c_str(), std::ios::binary);
|
|
if (!file)
|
|
{
|
|
throw image_reader_exception("AVIF: Can't read file:" + filename);
|
|
}
|
|
std::streampos beg = file.tellg();
|
|
file.seekg(0, std::ios::end);
|
|
std::streampos end = file.tellg();
|
|
std::size_t file_size = end - beg;
|
|
file.seekg(0, std::ios::beg);
|
|
|
|
auto buffer = std::make_unique<buffer_policy_type>(file_size);
|
|
file.read(reinterpret_cast<char*>(buffer->data()), buffer->size());
|
|
if (!file)
|
|
{
|
|
throw image_reader_exception("AVIF: Failed to read:" + filename);
|
|
}
|
|
|
|
buffer_ = std::move(buffer);
|
|
init();
|
|
}
|
|
|
|
template<typename T>
|
|
avif_reader<T>::avif_reader(char const* data, size_t size)
|
|
: buffer_(new buffer_policy_type(reinterpret_cast<uint8_t const*>(data), size))
|
|
{
|
|
init();
|
|
}
|
|
|
|
// dtor
|
|
template<typename T>
|
|
avif_reader<T>::~avif_reader()
|
|
{}
|
|
|
|
template<typename T>
|
|
void avif_reader<T>::init()
|
|
{
|
|
avifResult result = avifDecoderSetIOMemory(decoder_.get(), buffer_->data(), buffer_->size());
|
|
if (result != AVIF_RESULT_OK)
|
|
{
|
|
throw image_reader_exception(std::string("AVIF Reader:") + avifResultToString(result));
|
|
}
|
|
result = avifDecoderParse(decoder_.get());
|
|
if (result != AVIF_RESULT_OK)
|
|
{
|
|
throw image_reader_exception(std::string("AVIF Reader:") + avifResultToString(result));
|
|
}
|
|
width_ = decoder_->image->width;
|
|
height_ = decoder_->image->height;
|
|
depth_ = decoder_->image->depth;
|
|
has_alpha_ = decoder_->alphaPresent;
|
|
}
|
|
|
|
template<typename T>
|
|
unsigned avif_reader<T>::width() const
|
|
{
|
|
return width_;
|
|
}
|
|
|
|
template<typename T>
|
|
unsigned avif_reader<T>::height() const
|
|
{
|
|
return height_;
|
|
}
|
|
|
|
template<typename T>
|
|
std::optional<box2d<double>> avif_reader<T>::bounding_box() const
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
|
|
template<typename T>
|
|
void avif_reader<T>::read(unsigned x0, unsigned y0, image_rgba8& image)
|
|
{
|
|
bool cropped = x0 != 0 || y0 != 0 || image.width() != width_ || image.height() != height_;
|
|
avifRGBImage rgb;
|
|
std::memset(&rgb, 0, sizeof(rgb));
|
|
// read first image
|
|
if (avifDecoderNextImage(decoder_.get()) == AVIF_RESULT_OK)
|
|
{
|
|
if (!cropped)
|
|
{
|
|
avifRGBImageSetDefaults(&rgb, decoder_->image);
|
|
rgb.depth = 8;
|
|
std::uint32_t const pixelSize = avifRGBImagePixelSize(&rgb);
|
|
std::uint32_t const rowBytes = rgb.width * pixelSize;
|
|
rgb.pixels = image.bytes();
|
|
rgb.rowBytes = rowBytes;
|
|
avifResult result = avifImageYUVToRGB(decoder_->image, &rgb);
|
|
if (result != AVIF_RESULT_OK)
|
|
{
|
|
throw image_reader_exception(std::string("AVIF Reader:") + avifResultToString(result));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mapnik::avif_image_ptr crop{avifImageCreateEmpty(), [](avifImage* ptr) {
|
|
avifImageDestroy(ptr);
|
|
}};
|
|
avifPixelFormatInfo formatInfo;
|
|
avifGetPixelFormatInfo(decoder_->image->yuvFormat, &formatInfo);
|
|
std::uint32_t x_padding = x0 & formatInfo.chromaShiftX;
|
|
std::uint32_t y_padding = y0 & formatInfo.chromaShiftY;
|
|
std::uint32_t w = image.width();
|
|
std::uint32_t h = image.height();
|
|
auto r_x0 = x0 - x_padding;
|
|
auto r_y0 = y0 - y_padding;
|
|
|
|
auto r_width = std::min(decoder_->image->width, w + x_padding);
|
|
auto r_height = std::min(decoder_->image->height, h + y_padding);
|
|
avifCropRect rect = {r_x0, r_y0, r_width, r_height};
|
|
avifResult result = avifImageSetViewRect(crop.get(), decoder_->image, &rect);
|
|
if (result != AVIF_RESULT_OK)
|
|
{
|
|
image_reader_exception(std::string("AVIF Reader:") + avifResultToString(result));
|
|
}
|
|
avifRGBImageSetDefaults(&rgb, crop.get());
|
|
rgb.depth = 8;
|
|
std::uint32_t const pixelSize = avifRGBImagePixelSize(&rgb);
|
|
std::uint32_t const rowBytes = rgb.width * pixelSize;
|
|
if (x_padding == 0 && y_padding == 0)
|
|
{
|
|
rgb.pixels = image.bytes();
|
|
rgb.rowBytes = rowBytes;
|
|
result = avifImageYUVToRGB(crop.get(), &rgb);
|
|
if (result != AVIF_RESULT_OK)
|
|
{
|
|
image_reader_exception(std::string("AVIF Reader:") + avifResultToString(result));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mapnik::image_rgba8 padded_image(r_width, r_height, true, true);
|
|
rgb.pixels = padded_image.bytes();
|
|
rgb.rowBytes = rowBytes;
|
|
result = avifImageYUVToRGB(crop.get(), &rgb);
|
|
if (result != AVIF_RESULT_OK)
|
|
{
|
|
image_reader_exception(std::string("AVIF Reader:") + avifResultToString(result));
|
|
}
|
|
for (std::size_t row = 0; row < image.height(); ++row)
|
|
{
|
|
image.set_row(row, padded_image.get_row(row + y_padding) + x_padding, image.width());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
image_any avif_reader<T>::read(unsigned x, unsigned y, unsigned width, unsigned height)
|
|
{
|
|
image_rgba8 data(width, height, true, true);
|
|
read(x, y, data);
|
|
return image_any(std::move(data));
|
|
}
|
|
|
|
} // namespace mapnik
|