87 lines
2.4 KiB
C++
87 lines
2.4 KiB
C++
#include <psemek/gfx/pixmap.hpp>
|
|
|
|
#include <psemek/log/log.hpp>
|
|
#include <psemek/util/at_scope_exit.hpp>
|
|
|
|
#include <png.h>
|
|
|
|
namespace psemek::gfx
|
|
{
|
|
|
|
template <typename Pixel>
|
|
basic_pixmap<Pixel> read_png_impl(std::istream & is, png_byte expected_color_type)
|
|
{
|
|
png_error_ptr error = [](png_structp, png_const_charp str)
|
|
{
|
|
throw std::runtime_error(str);
|
|
};
|
|
|
|
png_error_ptr warn = [](png_structp, png_const_charp str)
|
|
{
|
|
log::warning() << str;
|
|
};
|
|
|
|
auto png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, error, warn);
|
|
if (!png) throw std::runtime_error("png_create_read_struct returned null");
|
|
|
|
png_set_error_fn(png, nullptr, error, warn);
|
|
|
|
png_infop info = nullptr;
|
|
png_infop end_info = nullptr;
|
|
|
|
[[maybe_unused]] auto png_dtor = util::at_scope_exit([&]{
|
|
png_destroy_read_struct(&png, &info, &end_info);
|
|
});
|
|
|
|
info = png_create_info_struct(png);
|
|
if (!info) throw std::runtime_error("png_create_info_struct returned null");
|
|
|
|
end_info = png_create_info_struct(png);
|
|
if (!end_info) throw std::runtime_error("png_create_info_struct returned null");
|
|
|
|
png_rw_ptr read = [](png_structp png, png_bytep ptr, size_t count){
|
|
reinterpret_cast<std::istream *>(png_get_io_ptr(png))->read(reinterpret_cast<char *>(ptr), count);
|
|
};
|
|
png_set_read_fn(png, &is, read);
|
|
|
|
png_read_info(png, info);
|
|
|
|
auto const width = png_get_image_width(png, info);
|
|
auto const height = png_get_image_height(png, info);
|
|
auto const color_type = png_get_color_type(png, info);
|
|
auto const bit_depth = png_get_bit_depth(png, info);
|
|
|
|
if (color_type != expected_color_type)
|
|
throw std::runtime_error("PNG color type not supported");
|
|
|
|
if (bit_depth == 16)
|
|
png_set_strip_16(png);
|
|
|
|
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
|
png_set_expand_gray_1_2_4_to_8(png);
|
|
|
|
png_read_update_info(png, info);
|
|
|
|
basic_pixmap<Pixel> result({width, height});
|
|
|
|
auto const row_bytes = png_get_rowbytes(png, info);
|
|
if (row_bytes != width * sizeof(Pixel))
|
|
throw std::runtime_error("PNG row bytes mismatch");
|
|
|
|
for (std::uint32_t i = 0; i < height; ++i)
|
|
png_read_row(png, reinterpret_cast<png_bytep>(result.data() + i * width), nullptr);
|
|
|
|
return result;
|
|
}
|
|
|
|
pixmap_rgba read_png(std::istream & is)
|
|
{
|
|
return read_png_impl<color_rgba>(is, PNG_COLOR_TYPE_RGBA);
|
|
}
|
|
|
|
pixmap_monochrome read_png_monochrome(std::istream & is)
|
|
{
|
|
return read_png_impl<std::uint8_t>(is, PNG_COLOR_TYPE_GRAY);
|
|
}
|
|
|
|
}
|