diff options
author | crupest <crupest@outlook.com> | 2022-06-09 19:10:00 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2022-06-09 19:10:00 +0800 |
commit | a61a783929c1f792e58de6ec9592e5177885c1fe (patch) | |
tree | 1aa1a1bfe25bf9370ead8436d6b9fd0c20a11cd2 | |
parent | 3df871ecaa95ab13a4fa154ab9ab17708fee6f9f (diff) | |
download | cru-a61a783929c1f792e58de6ec9592e5177885c1fe.tar.gz cru-a61a783929c1f792e58de6ec9592e5177885c1fe.tar.bz2 cru-a61a783929c1f792e58de6ec9592e5177885c1fe.zip |
...
-rw-r--r-- | src/platform/graphics/cairo/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/platform/graphics/cairo/CairoImageFactory.cpp | 172 |
2 files changed, 172 insertions, 4 deletions
diff --git a/src/platform/graphics/cairo/CMakeLists.txt b/src/platform/graphics/cairo/CMakeLists.txt index 3e12b604..206fa21c 100644 --- a/src/platform/graphics/cairo/CMakeLists.txt +++ b/src/platform/graphics/cairo/CMakeLists.txt @@ -13,6 +13,8 @@ if (UNIX) find_path(GLIBCONFIG_HEADER_DIR NAMES glibconfig.h HINTS ${LIB_ARCH_DIR} PATH_SUFFIXES glib glib/include glib-2.0 glib-2.0/include REQUIRED) find_path(HARFBUZZ_HEADER_DIR NAMES hb.h PATH_SUFFIXES harfbuzz REQUIRED) find_path(PANGO_HEADER_DIR NAMES pango PATH_SUFFIXES pango pango-1.0 REQUIRED) + + find_library(LIB_PNG png REQUIRED) add_library(CruPlatformGraphicsCairo SHARED Base.cpp @@ -27,6 +29,6 @@ if (UNIX) PangoTextLayout.cpp ) target_compile_definitions(CruPlatformGraphicsCairo PRIVATE CRU_PLATFORM_GRAPHICS_CAIRO_EXPORT_API) - target_link_libraries(CruPlatformGraphicsCairo PUBLIC CruPlatformGraphics PUBLIC ${LIB_GOBJECT} ${LIB_CAIRO} ${LIB_PANGO} ${LIB_PANGOCAIRO}) + target_link_libraries(CruPlatformGraphicsCairo PUBLIC CruPlatformGraphics PUBLIC ${LIB_GOBJECT} ${LIB_CAIRO} ${LIB_PANGO} ${LIB_PANGOCAIRO} ${LIB_PNG}) target_include_directories(CruPlatformGraphicsCairo PUBLIC ${CAIRO_HEADER_DIR} ${GLIB_HEADER_DIR} ${GLIBCONFIG_HEADER_DIR} ${HARFBUZZ_HEADER_DIR} ${PANGO_HEADER_DIR}) endif() diff --git a/src/platform/graphics/cairo/CairoImageFactory.cpp b/src/platform/graphics/cairo/CairoImageFactory.cpp index 74275c45..8377911f 100644 --- a/src/platform/graphics/cairo/CairoImageFactory.cpp +++ b/src/platform/graphics/cairo/CairoImageFactory.cpp @@ -1,10 +1,163 @@ #include "cru/platform/graphics/cairo/CairoImageFactory.h" -#include <memory> #include "cru/common/Exception.h" +#include "cru/platform/Check.h" #include "cru/platform/graphics/cairo/CairoImage.h" #include "cru/platform/graphics/cairo/CairoResource.h" +#include <png.h> +#include <memory> + namespace cru::platform::graphics::cairo { + +namespace { +bool IsPngHeader(const std::byte* buffer, int size) { + return png_sig_cmp(reinterpret_cast<png_const_bytep>(buffer), 0, size) == 0; +} + +std::unique_ptr<CairoImage> DecodePng(CairoGraphicsFactory* factory, + io::Stream* stream) { + png_structp png_ptr = nullptr; + png_infop info_ptr = nullptr; + png_uint_32 width = 0; + png_uint_32 height = 0; + int bit_depth = 0; + int color_type = 0; + int interlace_type = 0; + int compression_type = 0; + int filter_type = 0; + png_bytepp row_pointers = nullptr; + + png_ptr = + png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!png_ptr) { + return nullptr; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, nullptr, nullptr); + return nullptr; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + return nullptr; + } + + png_set_read_fn(png_ptr, stream, + [](png_structp png_ptr, png_bytep data, png_size_t length) { + auto stream = + static_cast<io::Stream*>(png_get_io_ptr(png_ptr)); + stream->Read(reinterpret_cast<std::byte*>(data), length); + }); + + png_read_png(png_ptr, info_ptr, + PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_SWAP_ALPHA | + PNG_TRANSFORM_GRAY_TO_RGB, + nullptr); + + width = png_get_image_width(png_ptr, info_ptr); + height = png_get_image_height(png_ptr, info_ptr); + + row_pointers = png_get_rows(png_ptr, info_ptr); + + std::unique_ptr<CairoImage> cairo_image( + new CairoImage(factory, width, height)); + auto cairo_surface = cairo_image->GetCairoSurface(); + + auto cairo_surface_stride = cairo_image_surface_get_stride(cairo_surface); + auto cairo_surface_data = cairo_image_surface_get_data(cairo_surface); + + for (int row = 0; row < height; row++) { + auto row_p = row_pointers[row]; + auto cairo_row_data = cairo_surface_data + row * cairo_surface_stride; + memcpy(cairo_row_data, row_p, width * 4); + for (int col = 0; col < width; col++) { + std::uint8_t* pixel = + reinterpret_cast<std::uint8_t*>(cairo_row_data + col * 4); + float alpha = pixel[0] / 255.f; + pixel[1] *= alpha; + pixel[2] *= alpha; + pixel[3] *= alpha; + } + } + + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + + return cairo_image; +} + +void EncodePng(cairo_surface_t* cairo_surface, io::Stream* stream) { + auto width = cairo_image_surface_get_width(cairo_surface); + auto height = cairo_image_surface_get_height(cairo_surface); + auto cairo_surface_stride = cairo_image_surface_get_stride(cairo_surface); + auto cairo_surface_data = cairo_image_surface_get_data(cairo_surface); + + png_structp png_ptr = nullptr; + png_infop info_ptr = nullptr; + + png_ptr = + png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!png_ptr) { + return; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_write_struct(&png_ptr, nullptr); + return; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, &info_ptr); + return; + } + + png_set_write_fn( + png_ptr, stream, + [](png_structp png_ptr, png_bytep data, png_size_t length) { + auto stream = static_cast<io::Stream*>(png_get_io_ptr(png_ptr)); + stream->Write(reinterpret_cast<std::byte*>(data), length); + }, + nullptr); + + png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + std::vector<std::byte> buffer; + buffer.resize(width * height * 4); + + for (int row = 0; row < height; row++) { + auto buffer_row = buffer.data() + row * width * 4; + auto cairo_row_data = cairo_surface_data + row * cairo_surface_stride; + memcpy(buffer_row, cairo_row_data, width * 4); + for (int col = 0; col < width; col++) { + std::uint8_t* pixel = + reinterpret_cast<std::uint8_t*>(buffer_row + col * 4); + float alpha = pixel[0] / 255.f; + pixel[1] /= alpha; + pixel[2] /= alpha; + pixel[3] /= alpha; + } + } + + std::vector<png_bytep> row_pointer; + row_pointer.resize(height); + for (int row = 0; row < height; row++) { + row_pointer[row] = + reinterpret_cast<png_bytep>(buffer.data() + row * width * 4); + } + + png_set_rows(png_ptr, info_ptr, row_pointer.data()); + + png_write_png(png_ptr, info_ptr, + PNG_TRANSFORM_SWAP_ALPHA | PNG_TRANSFORM_GRAY_TO_RGB, nullptr); + + png_destroy_write_struct(&png_ptr, &info_ptr); +} +} // namespace + CairoImageFactory::CairoImageFactory(CairoGraphicsFactory* factory) : CairoResource(factory) {} @@ -12,12 +165,25 @@ CairoImageFactory::~CairoImageFactory() {} std::unique_ptr<IImage> CairoImageFactory::DecodeFromStream( io::Stream* stream) { - throw Exception(u"Not implemented."); + std::byte buffer[8]; + stream->Read(buffer, 8); + if (IsPngHeader(buffer, 8)) { + stream->Seek(0, io::Stream::SeekOrigin::Begin); + return DecodePng(GetCairoGraphicsFactory(), stream); + } + + throw Exception(u"Image format unknown. Currently only support png."); } void CairoImageFactory::EncodeToStream(IImage* image, io::Stream* stream, ImageFormat format, float quality) { - throw Exception(u"Not implemented."); + auto cairo_image = CheckPlatform<CairoImage>(image, GetPlatformId()); + + if (format == ImageFormat::Png) { + EncodePng(cairo_image->GetCairoSurface(), stream); + } + + throw Exception(u"Not implemented. Currently only support png."); } std::unique_ptr<IImage> CairoImageFactory::CreateBitmap(int width, int height) { |