PNGWriter.cpp 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. /*
  2. * Copyright (c) 2024, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/NonnullOwnPtr.h>
  7. #include <AK/Vector.h>
  8. #include <LibGfx/Bitmap.h>
  9. #include <LibGfx/ImageFormats/PNGWriter.h>
  10. #include <png.h>
  11. namespace Gfx {
  12. struct WriterContext {
  13. Vector<u8*> row_pointers;
  14. ByteBuffer png_data;
  15. };
  16. ErrorOr<ByteBuffer> PNGWriter::encode(Gfx::Bitmap const& bitmap, Options options)
  17. {
  18. auto context = make<WriterContext>();
  19. int width = bitmap.width();
  20. int height = bitmap.height();
  21. png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
  22. if (!png_ptr) {
  23. return Error::from_string_literal("Failed to create PNG write struct");
  24. }
  25. png_infop info_ptr = png_create_info_struct(png_ptr);
  26. if (!info_ptr) {
  27. png_destroy_write_struct(&png_ptr, nullptr);
  28. return Error::from_string_literal("Failed to create PNG info struct");
  29. }
  30. if (setjmp(png_jmpbuf(png_ptr))) {
  31. png_destroy_write_struct(&png_ptr, &info_ptr);
  32. return Error::from_string_literal("Error during PNG encoding");
  33. }
  34. if (options.icc_data.has_value()) {
  35. png_set_iCCP(png_ptr, info_ptr, "embedded profile", 0, options.icc_data->data(), options.icc_data->size());
  36. }
  37. if (bitmap.format() == BitmapFormat::BGRA8888 || bitmap.format() == BitmapFormat::BGRx8888) {
  38. png_set_bgr(png_ptr);
  39. }
  40. png_set_write_fn(png_ptr, &context->png_data, [](png_structp png_ptr, u8* data, size_t length) {
  41. auto* buffer = reinterpret_cast<ByteBuffer*>(png_get_io_ptr(png_ptr));
  42. buffer->append(data, length); }, nullptr);
  43. png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
  44. context->row_pointers.resize(height);
  45. for (int y = 0; y < height; ++y) {
  46. context->row_pointers[y] = const_cast<u8*>(bitmap.scanline_u8(y));
  47. }
  48. png_set_rows(png_ptr, info_ptr, context->row_pointers.data());
  49. png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, nullptr);
  50. png_destroy_write_struct(&png_ptr, &info_ptr);
  51. return context->png_data;
  52. }
  53. }