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