1 #include <mbgl/util/compression.hpp>
2 #include <mbgl/util/image.hpp>
3 #include <mbgl/util/premultiply.hpp>
4
5 #pragma GCC diagnostic push
6 #pragma GCC diagnostic ignored "-Wshadow"
7 #include <boost/crc.hpp>
8 #pragma GCC diagnostic pop
9
10 #include <cassert>
11 #include <cstring>
12
13 #define NETWORK_BYTE_UINT32(value) \
14 char(value >> 24), char(value >> 16), char(value >> 8), char(value >> 0)
15
16 namespace {
17
addChunk(std::string & png,const char * type,const char * data="",const uint32_t size=0)18 void addChunk(std::string& png, const char* type, const char* data = "", const uint32_t size = 0) {
19 assert(strlen(type) == 4);
20
21 // Checksum encompasses type + data
22 boost::crc_32_type checksum;
23 checksum.process_bytes(type, 4);
24 checksum.process_bytes(data, size);
25
26 const char length[4] = { NETWORK_BYTE_UINT32(size) };
27 const char crc[4] = { NETWORK_BYTE_UINT32(checksum.checksum()) };
28
29 png.reserve(png.size() + 4 /* length */ + 4 /* type */ + size + 4 /* CRC */);
30 png.append(length, 4);
31 png.append(type, 4);
32 png.append(data, size);
33 png.append(crc, 4);
34 }
35
36 } // namespace
37
38 namespace mbgl {
39
40 // Encode PNGs without libpng.
encodePNG(const PremultipliedImage & pre)41 std::string encodePNG(const PremultipliedImage& pre) {
42 // Make copy of the image so that we can unpremultiply it.
43 const auto src = util::unpremultiply(pre.clone());
44
45 // PNG magic bytes
46 const char preamble[8] = { char(0x89), 'P', 'N', 'G', '\r', '\n', 0x1a, '\n' };
47
48 // IHDR chunk for our RGBA image.
49 const char ihdr[13] = {
50 NETWORK_BYTE_UINT32(src.size.width), // width
51 NETWORK_BYTE_UINT32(src.size.height), // height
52 8, // bit depth == 8 bits
53 6, // color type == RGBA
54 0, // compression method == deflate
55 0, // filter method == default
56 0, // interlace method == none
57 };
58
59 // Prepare the (compressed) data chunk.
60 const auto stride = src.stride();
61 std::string idat;
62 for (uint32_t y = 0; y < src.size.height; y++) {
63 // Every scanline needs to be prefixed with one byte that indicates the filter type.
64 idat.append(1, 0); // filter type 0
65 idat.append((const char*)(src.data.get() + y * stride), stride);
66 }
67 idat = util::compress(idat);
68
69 // Assemble the PNG.
70 std::string png;
71 png.reserve((8 /* preamble */) + (12 + 13 /* IHDR */) +
72 (12 + idat.size() /* IDAT */) + (12 /* IEND */));
73 png.append(preamble, 8);
74 addChunk(png, "IHDR", ihdr, 13);
75 addChunk(png, "IDAT", idat.data(), static_cast<uint32_t>(idat.size()));
76 addChunk(png, "IEND");
77 return png;
78 }
79
80 } // namespace mbgl
81