PNGWriter.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. * Copyright (c) 2021, Pierre Hoffmeister
  3. * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/String.h>
  8. #include <LibCrypto/Checksum/CRC32.h>
  9. #include <LibGfx/Bitmap.h>
  10. #include <LibGfx/PNGWriter.h>
  11. namespace Gfx {
  12. class PNGChunk {
  13. public:
  14. explicit PNGChunk(String);
  15. Vector<u8> const& data() const { return m_data; };
  16. String const& type() const { return m_type; };
  17. void add_u8(u8);
  18. void add_u16_big(u16);
  19. void add_u32_big(u32);
  20. void add_u16_little(u16);
  21. void add_u32_little(u32);
  22. private:
  23. Vector<u8> m_data;
  24. String m_type;
  25. };
  26. class NonCompressibleBlock {
  27. public:
  28. void finalize(PNGChunk&);
  29. void add_byte_to_block(u8 data, PNGChunk&);
  30. u32 adler_s1() const { return m_adler_s1; }
  31. u32 adler_s2() const { return m_adler_s2; }
  32. private:
  33. void add_block_to_chunk(PNGChunk&, bool);
  34. void update_adler(u8);
  35. bool full() { return m_non_compressible_data.size() == 65535; }
  36. Vector<u8> m_non_compressible_data;
  37. u32 m_adler_s1 { 1 };
  38. u32 m_adler_s2 { 0 };
  39. };
  40. PNGChunk::PNGChunk(String type)
  41. : m_type(move(type))
  42. {
  43. }
  44. void PNGChunk::add_u8(u8 data)
  45. {
  46. m_data.append(data);
  47. }
  48. void PNGChunk::add_u16_little(u16 data)
  49. {
  50. m_data.append(data & 0xff);
  51. m_data.append((data >> 8) & 0xff);
  52. }
  53. void PNGChunk::add_u32_little(u32 data)
  54. {
  55. m_data.append(data & 0xff);
  56. m_data.append((data >> 8) & 0xff);
  57. m_data.append((data >> 16) & 0xff);
  58. m_data.append((data >> 24) & 0xff);
  59. }
  60. void PNGChunk::add_u32_big(u32 data)
  61. {
  62. m_data.append((data >> 24) & 0xff);
  63. m_data.append((data >> 16) & 0xff);
  64. m_data.append((data >> 8) & 0xff);
  65. m_data.append(data & 0xff);
  66. }
  67. void PNGChunk::add_u16_big(u16 data)
  68. {
  69. m_data.append((data >> 8) & 0xff);
  70. m_data.append(data & 0xff);
  71. }
  72. void NonCompressibleBlock::add_byte_to_block(u8 data, PNGChunk& chunk)
  73. {
  74. m_non_compressible_data.append(data);
  75. update_adler(data);
  76. if (full()) {
  77. add_block_to_chunk(chunk, false);
  78. m_non_compressible_data.clear();
  79. }
  80. }
  81. void NonCompressibleBlock::add_block_to_chunk(PNGChunk& png_chunk, bool last)
  82. {
  83. if (last) {
  84. png_chunk.add_u8(1);
  85. } else {
  86. png_chunk.add_u8(0);
  87. }
  88. auto len = m_non_compressible_data.size();
  89. auto nlen = ~len;
  90. png_chunk.add_u16_little(len);
  91. png_chunk.add_u16_little(nlen);
  92. for (auto non_compressed_byte : m_non_compressible_data) {
  93. png_chunk.add_u8(non_compressed_byte);
  94. }
  95. }
  96. void NonCompressibleBlock::finalize(PNGChunk& chunk)
  97. {
  98. add_block_to_chunk(chunk, true);
  99. }
  100. void NonCompressibleBlock::update_adler(u8 data)
  101. {
  102. m_adler_s1 = (m_adler_s1 + data) % 65521;
  103. m_adler_s2 = (m_adler_s2 + m_adler_s1) % 65521;
  104. }
  105. void PNGWriter::add_chunk(PNGChunk const& png_chunk)
  106. {
  107. Vector<u8> combined;
  108. for (auto character : png_chunk.type()) {
  109. combined.append(character);
  110. }
  111. combined.extend(png_chunk.data());
  112. auto crc = BigEndian(Crypto::Checksum::CRC32({ (const u8*)combined.data(), combined.size() }).digest());
  113. auto data_len = BigEndian(png_chunk.data().size());
  114. ByteBuffer buf;
  115. buf.append(&data_len, sizeof(u32));
  116. buf.append(combined.data(), combined.size());
  117. buf.append(&crc, sizeof(u32));
  118. m_data.append(buf.data(), buf.size());
  119. }
  120. void PNGWriter::add_png_header()
  121. {
  122. const u8 png_header[8] = { 0x89, 'P', 'N', 'G', 13, 10, 26, 10 };
  123. m_data.append(png_header, sizeof(png_header));
  124. }
  125. void PNGWriter::add_IHDR_chunk(u32 width, u32 height, u8 bit_depth, u8 color_type, u8 compression_method, u8 filter_method, u8 interlace_method)
  126. {
  127. PNGChunk png_chunk { "IHDR" };
  128. png_chunk.add_u32_big(width);
  129. png_chunk.add_u32_big(height);
  130. png_chunk.add_u8(bit_depth);
  131. png_chunk.add_u8(color_type);
  132. png_chunk.add_u8(compression_method);
  133. png_chunk.add_u8(filter_method);
  134. png_chunk.add_u8(interlace_method);
  135. add_chunk(png_chunk);
  136. }
  137. void PNGWriter::add_IEND_chunk()
  138. {
  139. PNGChunk png_chunk { "IEND" };
  140. add_chunk(png_chunk);
  141. }
  142. void PNGWriter::add_IDAT_chunk(Gfx::Bitmap const& bitmap)
  143. {
  144. PNGChunk png_chunk { "IDAT" };
  145. u16 CMF_FLG = 0x81d;
  146. png_chunk.add_u16_big(CMF_FLG);
  147. NonCompressibleBlock non_compressible_block;
  148. for (int y = 0; y < bitmap.height(); ++y) {
  149. non_compressible_block.add_byte_to_block(0, png_chunk);
  150. for (int x = 0; x < bitmap.width(); ++x) {
  151. auto pixel = bitmap.get_pixel(x, y);
  152. non_compressible_block.add_byte_to_block(pixel.red(), png_chunk);
  153. non_compressible_block.add_byte_to_block(pixel.green(), png_chunk);
  154. non_compressible_block.add_byte_to_block(pixel.blue(), png_chunk);
  155. non_compressible_block.add_byte_to_block(pixel.alpha(), png_chunk);
  156. }
  157. }
  158. non_compressible_block.finalize(png_chunk);
  159. png_chunk.add_u16_big(non_compressible_block.adler_s2());
  160. png_chunk.add_u16_big(non_compressible_block.adler_s1());
  161. add_chunk(png_chunk);
  162. }
  163. ByteBuffer PNGWriter::encode(Gfx::Bitmap const& bitmap)
  164. {
  165. PNGWriter writer;
  166. writer.add_png_header();
  167. writer.add_IHDR_chunk(bitmap.width(), bitmap.height(), 8, 6, 0, 0, 0);
  168. writer.add_IDAT_chunk(bitmap);
  169. writer.add_IEND_chunk();
  170. return ByteBuffer::copy(writer.m_data);
  171. }
  172. }