PNGWriter.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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 <LibCrypto/Checksum/CRC32.h>
  11. #include <LibGfx/Bitmap.h>
  12. #include <LibGfx/PNGWriter.h>
  13. namespace Gfx {
  14. class PNGChunk {
  15. using data_length_type = u32;
  16. public:
  17. explicit PNGChunk(String);
  18. auto const& data() const { return m_data; };
  19. String const& type() const { return m_type; };
  20. void reserve(size_t bytes) { m_data.ensure_capacity(bytes); }
  21. template<typename T>
  22. void add_as_big_endian(T);
  23. template<typename T>
  24. void add_as_little_endian(T);
  25. void add_u8(u8);
  26. template<typename T>
  27. void add(T*, size_t);
  28. void store_type();
  29. void store_data_length();
  30. u32 crc();
  31. private:
  32. template<typename T>
  33. requires(IsUnsigned<T>) void add(T);
  34. ByteBuffer m_data;
  35. String m_type;
  36. };
  37. class NonCompressibleBlock {
  38. public:
  39. void finalize(PNGChunk&);
  40. void add_byte_to_block(u8 data, PNGChunk&);
  41. u16 adler_s1() const { return m_adler_s1; }
  42. u16 adler_s2() const { return m_adler_s2; }
  43. private:
  44. void add_block_to_chunk(PNGChunk&, bool);
  45. void update_adler(u8);
  46. bool full() { return m_non_compressible_data.size() == 65535; }
  47. Vector<u8> m_non_compressible_data;
  48. u16 m_adler_s1 { 1 };
  49. u16 m_adler_s2 { 0 };
  50. };
  51. PNGChunk::PNGChunk(String type)
  52. : m_type(move(type))
  53. {
  54. add<data_length_type>(0);
  55. store_type();
  56. }
  57. void PNGChunk::store_type()
  58. {
  59. m_data.append(type().bytes());
  60. }
  61. void PNGChunk::store_data_length()
  62. {
  63. auto data_length = BigEndian<u32>(m_data.size() - sizeof(data_length_type) - m_type.length());
  64. __builtin_memcpy(m_data.offset_pointer(0), &data_length, sizeof(u32));
  65. }
  66. u32 PNGChunk::crc()
  67. {
  68. u32 crc = Crypto::Checksum::CRC32({ m_data.offset_pointer(sizeof(data_length_type)), m_data.size() - sizeof(data_length_type) }).digest();
  69. return crc;
  70. }
  71. template<typename T>
  72. requires(IsUnsigned<T>) void PNGChunk::add(T data)
  73. {
  74. m_data.append(&data, sizeof(T));
  75. }
  76. template<typename T>
  77. void PNGChunk::add(T* data, size_t size)
  78. {
  79. m_data.append(data, size);
  80. }
  81. template<typename T>
  82. void PNGChunk::add_as_little_endian(T data)
  83. {
  84. auto data_out = AK::convert_between_host_and_little_endian(data);
  85. add(data_out);
  86. }
  87. template<typename T>
  88. void PNGChunk::add_as_big_endian(T data)
  89. {
  90. auto data_out = AK::convert_between_host_and_big_endian(data);
  91. add(data_out);
  92. }
  93. void PNGChunk::add_u8(u8 data)
  94. {
  95. add(data);
  96. }
  97. void NonCompressibleBlock::add_byte_to_block(u8 data, PNGChunk& chunk)
  98. {
  99. m_non_compressible_data.append(data);
  100. update_adler(data);
  101. if (full()) {
  102. add_block_to_chunk(chunk, false);
  103. m_non_compressible_data.clear_with_capacity();
  104. }
  105. }
  106. void NonCompressibleBlock::add_block_to_chunk(PNGChunk& png_chunk, bool last)
  107. {
  108. png_chunk.add_u8(last);
  109. u16 len = m_non_compressible_data.size();
  110. u16 nlen = ~len;
  111. png_chunk.add_as_little_endian(len);
  112. png_chunk.add_as_little_endian(nlen);
  113. png_chunk.add(m_non_compressible_data.data(), m_non_compressible_data.size());
  114. }
  115. void NonCompressibleBlock::finalize(PNGChunk& chunk)
  116. {
  117. add_block_to_chunk(chunk, true);
  118. }
  119. void NonCompressibleBlock::update_adler(u8 data)
  120. {
  121. m_adler_s1 = (m_adler_s1 + data) % 65521;
  122. m_adler_s2 = (m_adler_s2 + m_adler_s1) % 65521;
  123. }
  124. void PNGWriter::add_chunk(PNGChunk& png_chunk)
  125. {
  126. png_chunk.store_data_length();
  127. u32 crc = png_chunk.crc();
  128. png_chunk.add_as_big_endian(crc);
  129. m_data.append(png_chunk.data().data(), png_chunk.data().size());
  130. }
  131. void PNGWriter::add_png_header()
  132. {
  133. const u8 png_header[8] = { 0x89, 'P', 'N', 'G', 13, 10, 26, 10 };
  134. m_data.append(png_header, sizeof(png_header));
  135. }
  136. void PNGWriter::add_IHDR_chunk(u32 width, u32 height, u8 bit_depth, u8 color_type, u8 compression_method, u8 filter_method, u8 interlace_method)
  137. {
  138. PNGChunk png_chunk { "IHDR" };
  139. png_chunk.add_as_big_endian(width);
  140. png_chunk.add_as_big_endian(height);
  141. png_chunk.add_u8(bit_depth);
  142. png_chunk.add_u8(color_type);
  143. png_chunk.add_u8(compression_method);
  144. png_chunk.add_u8(filter_method);
  145. png_chunk.add_u8(interlace_method);
  146. add_chunk(png_chunk);
  147. }
  148. void PNGWriter::add_IEND_chunk()
  149. {
  150. PNGChunk png_chunk { "IEND" };
  151. add_chunk(png_chunk);
  152. }
  153. void PNGWriter::add_IDAT_chunk(Gfx::Bitmap const& bitmap)
  154. {
  155. PNGChunk png_chunk { "IDAT" };
  156. png_chunk.reserve(bitmap.size_in_bytes());
  157. u16 CMF_FLG = 0x81d;
  158. png_chunk.add_as_big_endian(CMF_FLG);
  159. NonCompressibleBlock non_compressible_block;
  160. for (int y = 0; y < bitmap.height(); ++y) {
  161. non_compressible_block.add_byte_to_block(0, png_chunk);
  162. for (int x = 0; x < bitmap.width(); ++x) {
  163. auto pixel = bitmap.get_pixel(x, y);
  164. non_compressible_block.add_byte_to_block(pixel.red(), png_chunk);
  165. non_compressible_block.add_byte_to_block(pixel.green(), png_chunk);
  166. non_compressible_block.add_byte_to_block(pixel.blue(), png_chunk);
  167. non_compressible_block.add_byte_to_block(pixel.alpha(), png_chunk);
  168. }
  169. }
  170. non_compressible_block.finalize(png_chunk);
  171. png_chunk.add_as_big_endian(non_compressible_block.adler_s2());
  172. png_chunk.add_as_big_endian(non_compressible_block.adler_s1());
  173. add_chunk(png_chunk);
  174. }
  175. ByteBuffer PNGWriter::encode(Gfx::Bitmap const& bitmap)
  176. {
  177. PNGWriter writer;
  178. writer.add_png_header();
  179. writer.add_IHDR_chunk(bitmap.width(), bitmap.height(), 8, 6, 0, 0, 0);
  180. writer.add_IDAT_chunk(bitmap);
  181. writer.add_IEND_chunk();
  182. // FIXME: Handle OOM failure.
  183. return ByteBuffer::copy(writer.m_data).release_value_but_fixme_should_propagate_errors();
  184. }
  185. }