rationale: encodePNG is only used in tests + sample apps, but shouldn't actually be part of the core library. In #7828, we're removing the libpng dependency for Android, and using a libpng-free version of encodePNG allows us to run the core unit tests on Android without pulling in libpng just for the test suite. In a next step, we should remove encodePNG() from the image.hpp header, which is a public header.
85 lines
2.8 KiB
C++
85 lines
2.8 KiB
C++
#include <mbgl/util/compression.hpp>
|
|
#include <mbgl/util/image.hpp>
|
|
#include <mbgl/util/premultiply.hpp>
|
|
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wshadow"
|
|
#include <boost/crc.hpp>
|
|
#pragma GCC diagnostic pop
|
|
|
|
#include <cassert>
|
|
#include <cstring>
|
|
|
|
#define NETWORK_BYTE_UINT32(value) \
|
|
char(value >> 24), char(value >> 16), char(value >> 8), char(value >> 0)
|
|
|
|
namespace {
|
|
|
|
void addChunk(std::string& png, const char* type, const char* data = "", const uint32_t size = 0) {
|
|
assert(strlen(type) == 4);
|
|
|
|
// Checksum encompasses type + data
|
|
boost::crc_32_type checksum;
|
|
checksum.process_bytes(type, 4);
|
|
checksum.process_bytes(data, size);
|
|
|
|
const char length[4] = { NETWORK_BYTE_UINT32(size) };
|
|
const char crc[4] = { NETWORK_BYTE_UINT32(checksum.checksum()) };
|
|
|
|
png.reserve(png.size() + 4 /* length */ + 4 /* type */ + size + 4 /* CRC */);
|
|
png.append(length, 4);
|
|
png.append(type, 4);
|
|
png.append(data, size);
|
|
png.append(crc, 4);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace mbgl {
|
|
|
|
// Encode PNGs without libpng.
|
|
std::string encodePNG(const PremultipliedImage& pre) {
|
|
const UnassociatedImage src = [&pre] {
|
|
// Make copy of the image so that we can unpremultiply it.
|
|
PremultipliedImage copy(pre.size);
|
|
std::copy(pre.data.get(), pre.data.get() + pre.bytes(), copy.data.get());
|
|
return util::unpremultiply(std::move(copy));
|
|
}();
|
|
|
|
// PNG magic bytes
|
|
const char preamble[8] = { char(0x89), 'P', 'N', 'G', '\r', '\n', 0x1a, '\n' };
|
|
|
|
// IHDR chunk for our RGBA image.
|
|
const char ihdr[13] = {
|
|
NETWORK_BYTE_UINT32(src.size.width), // width
|
|
NETWORK_BYTE_UINT32(src.size.height), // height
|
|
8, // bit depth == 8 bits
|
|
6, // color type == RGBA
|
|
0, // compression method == deflate
|
|
0, // filter method == default
|
|
0, // interlace method == none
|
|
};
|
|
|
|
// Prepare the (compressed) data chunk.
|
|
const auto stride = src.stride();
|
|
std::string idat;
|
|
for (uint32_t y = 0; y < src.size.height; y++) {
|
|
// Every scanline needs to be prefixed with one byte that indicates the filter type.
|
|
idat.append(1, 0); // filter type 0
|
|
idat.append((const char*)(src.data.get() + y * stride), stride);
|
|
}
|
|
idat = util::compress(idat);
|
|
|
|
// Assemble the PNG.
|
|
std::string png;
|
|
png.reserve((8 /* preamble */) + (12 + 13 /* IHDR */) +
|
|
(12 + idat.size() /* IDAT */) + (12 /* IEND */));
|
|
png.append(preamble, 8);
|
|
addChunk(png, "IHDR", ihdr, 13);
|
|
addChunk(png, "IDAT", idat.data(), idat.size());
|
|
addChunk(png, "IEND");
|
|
return png;
|
|
}
|
|
|
|
} // namespace mbgl
|