diff --git a/libs/fonts/source/kerned_font.cpp b/libs/fonts/source/kerned_font.cpp index c9824d61..455946a1 100644 --- a/libs/fonts/source/kerned_font.cpp +++ b/libs/fonts/source/kerned_font.cpp @@ -107,7 +107,7 @@ namespace psemek::fonts { auto data = bmfont_data::parse(std::move(description)); - auto pixmap = gfx::read_png_monochrome(std::move(texture)); + auto pixmap = gfx::read_image(std::move(texture)); gfx::texture_2d atlas; atlas.load(pixmap); atlas.linear_filter(); diff --git a/libs/fonts/source/msdf_font.cpp b/libs/fonts/source/msdf_font.cpp index 7c125a4b..43678560 100644 --- a/libs/fonts/source/msdf_font.cpp +++ b/libs/fonts/source/msdf_font.cpp @@ -8,7 +8,10 @@ namespace psemek::fonts { auto data = bmfont_data::parse(std::move(description)); - auto pixmap = gfx::read_png(std::move(texture)); + gl::PixelStorei(gl::UNPACK_ALIGNMENT, 1); + gl::PixelStorei(gl::PACK_ALIGNMENT, 1); + + auto pixmap = gfx::read_image(std::move(texture)); gfx::texture_2d atlas; atlas.load(pixmap); atlas.linear_mipmap_filter(); diff --git a/libs/gfx/include/psemek/gfx/pixmap.hpp b/libs/gfx/include/psemek/gfx/pixmap.hpp index 70d58585..00566687 100644 --- a/libs/gfx/include/psemek/gfx/pixmap.hpp +++ b/libs/gfx/include/psemek/gfx/pixmap.hpp @@ -17,17 +17,7 @@ namespace psemek::gfx using pixmap_rgba = basic_pixmap; using pixmap_float = basic_pixmap; - pixmap_monochrome read_pbm(std::istream & is); - pixmap_monochrome read_pgm(std::istream & is); - pixmap_rgb read_ppm(std::istream & is); - - void write_pgm(pixmap_monochrome const & p, std::ostream & os); - void write_ppm(pixmap_rgb const & p, std::ostream & os); - - pixmap_rgba read_png(io::istream && is); - pixmap_monochrome read_png_monochrome(io::istream && is); - - void write_png(pixmap_rgba const & p, io::ostream && os); + // Image reading template basic_pixmap read_image(io::istream && is); @@ -36,6 +26,8 @@ namespace psemek::gfx extern template pixmap_rgb read_image(io::istream && is); extern template pixmap_rgba read_image(io::istream && is); + // Image writing + void write_image_png(pixmap_monochrome const & p, io::ostream && os); void write_image_png(pixmap_rgb const & p, io::ostream && os); void write_image_png(pixmap_rgba const & p, io::ostream && os); @@ -50,6 +42,11 @@ namespace psemek::gfx void write_image_hdr(basic_pixmap const & p, io::ostream && os); + void write_image_pgm(pixmap_monochrome const & p, io::ostream && os); + void write_image_ppm(pixmap_rgb const & p, io::ostream && os); + + // Utilities + template auto to_srgb(util::array pm, float g = 1.f / 2.2f) { diff --git a/libs/gfx/source/gltf_mesh.cpp b/libs/gfx/source/gltf_mesh.cpp index 165f5e69..a66ac56e 100644 --- a/libs/gfx/source/gltf_mesh.cpp +++ b/libs/gfx/source/gltf_mesh.cpp @@ -72,7 +72,7 @@ namespace psemek::gfx { auto data = uri_loader(texture.uri); auto & target = textures_.emplace_back(); - target.load_srgb(gfx::read_png(io::memory_istream(data.string_view()))); + target.load_srgb(gfx::read_image(io::memory_istream(data.string_view()))); target.linear_mipmap_filter(); target.anisotropy(); target.generate_mipmap(); diff --git a/libs/gfx/source/netpbm.cpp b/libs/gfx/source/netpbm.cpp deleted file mode 100644 index f8d401c4..00000000 --- a/libs/gfx/source/netpbm.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include -#include - -#include -#include -#include - -namespace psemek::gfx -{ - - pixmap_monochrome read_pbm(std::istream & is) - { - auto fail = [](std::string str) - { - throw util::exception("Error loading PBM image: " + str); - }; - - std::string line; - std::getline(is, line); - - bool binary = true; - - if (line == "P1") - { - binary = false; - } - else if (line == "P4") - { - binary = true; - } - else - fail("unknown format " + line); - - if (!binary) - fail("P1 format is not supported"); - - std::getline(is, line); - if (!is) - fail("stream error"); - std::istringstream sline(line); - - std::size_t width, height; - pixmap_monochrome pixmap; - - sline >> width >> height; - if (!sline) - fail("stream error"); - - pixmap.resize({width, height}); - - std::size_t bytes = width * height; - if ((bytes % 8) == 0) - bytes = bytes / 8; - else - bytes = (bytes + 7) / 8; - std::vector data(bytes); - is.read(reinterpret_cast(data.data()), bytes); - - for (std::size_t i = 0; i < width * height; ++i) - { - std::size_t b = i / 8; - std::size_t o = 7 - (i % 8); - bool const white = (data[b] & (1 << o)) == 0; - pixmap.data()[i] = white ? 255 : 0; - } - - if (!is) - fail("stream error"); - - return pixmap; - } - - pixmap_monochrome read_pgm(std::istream & is) - { - auto fail = [](std::string str) - { - throw util::exception("Error loading PGM image: " + str); - }; - - std::string line; - std::getline(is, line); - - bool binary = true; - - if (line == "P2") - { - binary = false; - } - else if (line == "P5") - { - binary = true; - } - else - fail("unknown format " + line); - - std::getline(is, line); - if (!is) - fail("stream error"); - std::istringstream sline(line); - - std::size_t width, height; - std::size_t max; - pixmap_monochrome pixmap; - - sline >> width >> height; - if (!sline) - fail("stream error"); - - std::getline(is, line); - if (!is) - fail("stream error"); - sline.clear(); - sline.str(line); - sline >> max; - if (!sline) - fail("stream error"); - - if (max != 255) - fail("max value " + std::to_string(max) + " is not supported"); - - pixmap.resize({width, height}); - - if (binary) - is.read(reinterpret_cast(pixmap.data()), width * height * sizeof(pixmap.data()[0])); - else - fail("P2 format is not supported"); - - if (!is) - fail("stream error"); - - return pixmap; - } - - pixmap_rgb read_ppm(std::istream & is) - { - auto fail = [](std::string str) - { - throw util::exception("Error loading PPM image: " + str); - }; - - std::string line; - std::getline(is, line); - - bool binary = true; - - if (line == "P3") - { - binary = false; - } - else if (line == "P6") - { - binary = true; - } - else - fail("unknown format " + line); - - std::getline(is, line); - if (!is) - fail("stream error"); - std::istringstream sline(line); - - std::size_t width, height; - std::size_t max; - pixmap_rgb pixmap; - - sline >> width >> height; - - if (!sline) - fail("stream error"); - - std::getline(is, line); - if (!is) - fail("stream error"); - sline.clear(); - sline.str(line); - sline >> max; - if (!sline) - fail("stream error"); - - if (max != 255) - fail("max value " + std::to_string(max) + " is not supported"); - - pixmap.resize({width, height}); - - if (binary) - is.read(reinterpret_cast(pixmap.data()), width * height * sizeof(pixmap.data()[0])); - else - fail("P3 format is not supported"); - - if (!is) - fail("stream error"); - - return pixmap; - } - - void write_pgm(pixmap_monochrome const & p, std::ostream & os) - { - os << "P5\n" << p.width() << " " << p.height() << "\n255\n"; - os.write(reinterpret_cast(p.data()), p.width() * p.height() * sizeof(p.data()[0])); - } - - void write_ppm(pixmap_rgb const & p, std::ostream & os) - { - os << "P6\n" << p.width() << " " << p.height() << "\n255\n"; - os.write(reinterpret_cast(p.data()), p.width() * p.height() * sizeof(p.data()[0])); - } - -} diff --git a/libs/gfx/source/painter.cpp b/libs/gfx/source/painter.cpp index bb1420fe..2867f05b 100644 --- a/libs/gfx/source/painter.cpp +++ b/libs/gfx/source/painter.cpp @@ -160,7 +160,7 @@ namespace psemek::gfx text_mesh.setup, gfx::normalized, geom::point>(); texture_mesh.setup, gfx::normalized>>(); - font_texture.load(gfx::read_png_monochrome(io::memory_istream{resource::font_9x12_png.data})); + font_texture.load(gfx::read_image(io::memory_istream{resource::font_9x12_png.data})); font_texture.bind(); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_SWIZZLE_G, gl::RED); diff --git a/libs/gfx/source/pixmap.cpp b/libs/gfx/source/pixmap.cpp index 4608c7d6..3027548c 100644 --- a/libs/gfx/source/pixmap.cpp +++ b/libs/gfx/source/pixmap.cpp @@ -116,4 +116,20 @@ namespace psemek::gfx throw util::exception("Failed to write rgb hdr"); } + void write_image_pgm(pixmap_monochrome const & p, io::ostream && os) + { + char header_buffer[256]; + int header_size = std::snprintf(header_buffer, 256, "P5\n%lu %lu\n255\n", p.width(), p.height()); + os.write(header_buffer, header_size); + os.write(reinterpret_cast(p.data()), p.width() * p.height()); + } + + void write_image_ppm(pixmap_rgb const & p, io::ostream && os) + { + char header_buffer[256]; + int header_size = std::snprintf(header_buffer, 256, "P6\n%lu %lu\n255\n", p.width(), p.height()); + os.write(header_buffer, header_size); + os.write(reinterpret_cast(p.data()), p.width() * p.height() * 3); + } + } diff --git a/libs/gfx/source/png.cpp b/libs/gfx/source/png.cpp deleted file mode 100644 index 3ba843b5..00000000 --- a/libs/gfx/source/png.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include - -#include -#include -#include - -#include - -namespace psemek::gfx -{ - - namespace - { - - template - basic_pixmap read_png_impl(io::istream & is, bool monochrome) - { - png_error_ptr error = [](png_structp, png_const_charp str) - { - throw util::exception(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 util::exception("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 util::exception("png_create_info_struct returned null"); - - end_info = png_create_info_struct(png); - if (!end_info) throw util::exception("png_create_info_struct returned null"); - - png_rw_ptr read = [](png_structp png, png_bytep ptr, size_t count){ - reinterpret_cast(png_get_io_ptr(png))->read(reinterpret_cast(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 (bit_depth == 16) - png_set_strip_16(png); - - if (monochrome && color_type != PNG_COLOR_TYPE_GRAY) - throw util::exception("Invalid color type for monochrome PNG"); - - if (color_type & PNG_COLOR_MASK_PALETTE) - png_set_palette_to_rgb(png); - - if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) - png_set_expand_gray_1_2_4_to_8(png); - - if (!(color_type & PNG_COLOR_MASK_ALPHA) && !monochrome) - png_set_add_alpha(png, 0xff, PNG_FILLER_AFTER); - - png_read_update_info(png, info); - - basic_pixmap result({width, height}); - - auto const row_bytes = png_get_rowbytes(png, info); - if (row_bytes != width * sizeof(Pixel)) - throw util::exception("PNG row bytes mismatch"); - - for (std::uint32_t i = 0; i < height; ++i) - png_read_row(png, reinterpret_cast(result.data() + i * width), nullptr); - - return result; - } - - void write_png_impl(pixmap_rgba const & p, io::ostream & os) - { - png_error_ptr error = [](png_structp, png_const_charp str) - { - throw util::exception(str); - }; - - png_error_ptr warn = [](png_structp, png_const_charp str) - { - log::warning() << str; - }; - - auto png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, error, warn); - if (!png) throw util::exception("png_create_write_struct returned null"); - - png_set_error_fn(png, nullptr, error, warn); - - png_infop info = nullptr; - - [[maybe_unused]] auto png_dtor = util::at_scope_exit([&]{ - png_destroy_write_struct(&png, &info); - }); - - info = png_create_info_struct(png); - if (!info) throw util::exception("png_create_info_struct returned null"); - - png_rw_ptr write = [](png_structp png, png_bytep ptr, size_t count){ - reinterpret_cast(png_get_io_ptr(png))->write(reinterpret_cast(ptr), count); - }; - png_flush_ptr flush = [](png_structp png){ - reinterpret_cast(png_get_io_ptr(png))->flush(); - }; - png_set_write_fn(png, &os, write, flush); - - png_set_IHDR(png, info, p.width(), p.height(), 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - - png_write_info(png, info); - - for (std::uint32_t i = 0; i < p.height(); ++i) - png_write_row(png, reinterpret_cast(p.data() + p.width() * i)); - } - - } - - pixmap_rgba read_png(io::istream && is) - { - return read_png_impl(is, false); - } - - pixmap_monochrome read_png_monochrome(io::istream && is) - { - return read_png_impl(is, true); - } - - void write_png(pixmap_rgba const & p, io::ostream && os) - { - write_png_impl(p, os); - } - -}