PNGWriter.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /*
  2. * Copyright (c) 2021, Pierre Hoffmeister
  3. * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
  4. * Copyright (c) 2021, Aziz Berkay Yesilyurt <abyesilyurt@gmail.com>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/Concepts.h>
  9. #include <AK/String.h>
  10. #include <LibCompress/Zlib.h>
  11. #include <LibCrypto/Checksum/CRC32.h>
  12. #include <LibGfx/Bitmap.h>
  13. #include <LibGfx/PNGWriter.h>
  14. namespace Gfx {
  15. class PNGChunk {
  16. using data_length_type = u32;
  17. public:
  18. explicit PNGChunk(String);
  19. auto const& data() const { return m_data; };
  20. String const& type() const { return m_type; };
  21. void reserve(size_t bytes) { m_data.ensure_capacity(bytes); }
  22. template<typename T>
  23. void add_as_big_endian(T);
  24. template<typename T>
  25. void add_as_little_endian(T);
  26. void add_u8(u8);
  27. template<typename T>
  28. void add(T*, size_t);
  29. void store_type();
  30. void store_data_length();
  31. u32 crc();
  32. private:
  33. template<typename T>
  34. requires(IsUnsigned<T>) void add(T);
  35. ByteBuffer m_data;
  36. String m_type;
  37. };
  38. PNGChunk::PNGChunk(String type)
  39. : m_type(move(type))
  40. {
  41. add<data_length_type>(0);
  42. store_type();
  43. }
  44. void PNGChunk::store_type()
  45. {
  46. m_data.append(type().bytes());
  47. }
  48. void PNGChunk::store_data_length()
  49. {
  50. auto data_length = BigEndian<u32>(m_data.size() - sizeof(data_length_type) - m_type.length());
  51. __builtin_memcpy(m_data.offset_pointer(0), &data_length, sizeof(u32));
  52. }
  53. u32 PNGChunk::crc()
  54. {
  55. u32 crc = Crypto::Checksum::CRC32({ m_data.offset_pointer(sizeof(data_length_type)), m_data.size() - sizeof(data_length_type) }).digest();
  56. return crc;
  57. }
  58. template<typename T>
  59. requires(IsUnsigned<T>) void PNGChunk::add(T data)
  60. {
  61. m_data.append(&data, sizeof(T));
  62. }
  63. template<typename T>
  64. void PNGChunk::add(T* data, size_t size)
  65. {
  66. m_data.append(data, size);
  67. }
  68. template<typename T>
  69. void PNGChunk::add_as_little_endian(T data)
  70. {
  71. auto data_out = AK::convert_between_host_and_little_endian(data);
  72. add(data_out);
  73. }
  74. template<typename T>
  75. void PNGChunk::add_as_big_endian(T data)
  76. {
  77. auto data_out = AK::convert_between_host_and_big_endian(data);
  78. add(data_out);
  79. }
  80. void PNGChunk::add_u8(u8 data)
  81. {
  82. add(data);
  83. }
  84. void PNGWriter::add_chunk(PNGChunk& png_chunk)
  85. {
  86. png_chunk.store_data_length();
  87. u32 crc = png_chunk.crc();
  88. png_chunk.add_as_big_endian(crc);
  89. m_data.append(png_chunk.data().data(), png_chunk.data().size());
  90. }
  91. void PNGWriter::add_png_header()
  92. {
  93. const u8 png_header[8] = { 0x89, 'P', 'N', 'G', 13, 10, 26, 10 };
  94. m_data.append(png_header, sizeof(png_header));
  95. }
  96. void PNGWriter::add_IHDR_chunk(u32 width, u32 height, u8 bit_depth, PNG::ColorType color_type, u8 compression_method, u8 filter_method, u8 interlace_method)
  97. {
  98. PNGChunk png_chunk { "IHDR" };
  99. png_chunk.add_as_big_endian(width);
  100. png_chunk.add_as_big_endian(height);
  101. png_chunk.add_u8(bit_depth);
  102. png_chunk.add_u8(to_underlying(color_type));
  103. png_chunk.add_u8(compression_method);
  104. png_chunk.add_u8(filter_method);
  105. png_chunk.add_u8(interlace_method);
  106. add_chunk(png_chunk);
  107. }
  108. void PNGWriter::add_IEND_chunk()
  109. {
  110. PNGChunk png_chunk { "IEND" };
  111. add_chunk(png_chunk);
  112. }
  113. void PNGWriter::add_IDAT_chunk(Gfx::Bitmap const& bitmap)
  114. {
  115. PNGChunk png_chunk { "IDAT" };
  116. png_chunk.reserve(bitmap.size_in_bytes());
  117. ByteBuffer uncompressed_block_data;
  118. uncompressed_block_data.ensure_capacity(bitmap.size_in_bytes() + bitmap.height());
  119. for (int y = 0; y < bitmap.height(); ++y) {
  120. uncompressed_block_data.append(0);
  121. for (int x = 0; x < bitmap.width(); ++x) {
  122. auto pixel = bitmap.get_pixel(x, y);
  123. uncompressed_block_data.append(pixel.red());
  124. uncompressed_block_data.append(pixel.green());
  125. uncompressed_block_data.append(pixel.blue());
  126. uncompressed_block_data.append(pixel.alpha());
  127. }
  128. }
  129. auto maybe_zlib_buffer = Compress::ZlibCompressor::compress_all(uncompressed_block_data);
  130. if (!maybe_zlib_buffer.has_value()) {
  131. // FIXME: Handle errors.
  132. VERIFY_NOT_REACHED();
  133. }
  134. auto zlib_buffer = maybe_zlib_buffer.release_value();
  135. png_chunk.add(zlib_buffer.data(), zlib_buffer.size());
  136. add_chunk(png_chunk);
  137. }
  138. ByteBuffer PNGWriter::encode(Gfx::Bitmap const& bitmap)
  139. {
  140. PNGWriter writer;
  141. writer.add_png_header();
  142. writer.add_IHDR_chunk(bitmap.width(), bitmap.height(), 8, PNG::ColorType::TruecolorWithAlpha, 0, 0, 0);
  143. writer.add_IDAT_chunk(bitmap);
  144. writer.add_IEND_chunk();
  145. // FIXME: Handle OOM failure.
  146. return ByteBuffer::copy(writer.m_data).release_value_but_fixme_should_propagate_errors();
  147. }
  148. }