[core] replace encodePNG() with a version that doesn't use libpng
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.
This commit is contained in:
@@ -83,6 +83,7 @@ macro(mbgl_platform_core)
|
||||
|
||||
# Image handling
|
||||
PRIVATE platform/default/image.cpp
|
||||
PRIVATE platform/default/png_writer.cpp
|
||||
PRIVATE platform/default/png_reader.cpp
|
||||
PRIVATE platform/default/jpeg_reader.cpp
|
||||
|
||||
|
||||
@@ -2,78 +2,8 @@
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/util/premultiply.hpp>
|
||||
|
||||
#include <png.h>
|
||||
|
||||
template<size_t max, typename... Args>
|
||||
static std::string sprintf(const char *msg, Args... args) {
|
||||
char res[max];
|
||||
int len = snprintf(res, sizeof(res), msg, args...);
|
||||
return std::string(res, len);
|
||||
}
|
||||
|
||||
const static bool png_version_check __attribute__((unused)) = []() {
|
||||
const png_uint_32 version = png_access_version_number();
|
||||
if (version != PNG_LIBPNG_VER) {
|
||||
throw std::runtime_error(sprintf<96>(
|
||||
"libpng version mismatch: headers report %d.%d.%d, but library reports %d.%d.%d",
|
||||
PNG_LIBPNG_VER / 10000, (PNG_LIBPNG_VER / 100) % 100, PNG_LIBPNG_VER % 100,
|
||||
version / 10000, (version / 100) % 100, version % 100));
|
||||
}
|
||||
return true;
|
||||
}();
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
std::string encodePNG(const PremultipliedImage& pre) {
|
||||
PremultipliedImage copy(pre.size);
|
||||
std::copy(pre.data.get(), pre.data.get() + pre.bytes(), copy.data.get());
|
||||
|
||||
UnassociatedImage src = util::unpremultiply(std::move(copy));
|
||||
|
||||
png_voidp error_ptr = nullptr;
|
||||
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, error_ptr, nullptr, nullptr);
|
||||
if (!png_ptr) {
|
||||
throw std::runtime_error("couldn't create png_ptr");
|
||||
}
|
||||
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!png_ptr) {
|
||||
png_destroy_write_struct(&png_ptr, (png_infopp)nullptr);
|
||||
throw std::runtime_error("couldn't create info_ptr");
|
||||
}
|
||||
|
||||
png_set_IHDR(png_ptr, info_ptr, src.size.width, src.size.height, 8, PNG_COLOR_TYPE_RGB_ALPHA,
|
||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
jmp_buf *jmp_context = (jmp_buf *)png_get_error_ptr(png_ptr);
|
||||
if (jmp_context) {
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
throw std::runtime_error("png error");
|
||||
}
|
||||
|
||||
std::string result;
|
||||
png_set_write_fn(png_ptr, &result, [](png_structp png_ptr_, png_bytep data, png_size_t length) {
|
||||
std::string *out = static_cast<std::string *>(png_get_io_ptr(png_ptr_));
|
||||
out->append(reinterpret_cast<char *>(data), length);
|
||||
}, nullptr);
|
||||
|
||||
struct ptrs {
|
||||
ptrs(size_t count) : rows(new png_bytep[count]) {}
|
||||
~ptrs() { delete[] rows; }
|
||||
png_bytep *rows = nullptr;
|
||||
} pointers(src.size.height);
|
||||
|
||||
for (size_t i = 0; i < src.size.height; i++) {
|
||||
pointers.rows[i] = src.data.get() + src.stride() * i;
|
||||
}
|
||||
|
||||
png_set_rows(png_ptr, info_ptr, pointers.rows);
|
||||
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, nullptr);
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(__ANDROID__) && !defined(__APPLE__)
|
||||
PremultipliedImage decodeWebP(const uint8_t*, size_t);
|
||||
#endif // !defined(__ANDROID__) && !defined(__APPLE__)
|
||||
|
||||
@@ -11,6 +11,24 @@ extern "C"
|
||||
#include <png.h>
|
||||
}
|
||||
|
||||
template<size_t max, typename... Args>
|
||||
static std::string sprintf(const char *msg, Args... args) {
|
||||
char res[max];
|
||||
int len = snprintf(res, sizeof(res), msg, args...);
|
||||
return std::string(res, len);
|
||||
}
|
||||
|
||||
const static bool png_version_check __attribute__((unused)) = []() {
|
||||
const png_uint_32 version = png_access_version_number();
|
||||
if (version != PNG_LIBPNG_VER) {
|
||||
throw std::runtime_error(sprintf<96>(
|
||||
"libpng version mismatch: headers report %d.%d.%d, but library reports %d.%d.%d",
|
||||
PNG_LIBPNG_VER / 10000, (PNG_LIBPNG_VER / 100) % 100, PNG_LIBPNG_VER % 100,
|
||||
version / 10000, (version / 100) % 100, version % 100));
|
||||
}
|
||||
return true;
|
||||
}();
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
static void user_error_fn(png_structp, png_const_charp error_msg) {
|
||||
|
||||
84
platform/default/png_writer.cpp
Normal file
84
platform/default/png_writer.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#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
|
||||
@@ -72,6 +72,7 @@ macro(mbgl_platform_core)
|
||||
# Image handling
|
||||
PRIVATE platform/default/image.cpp
|
||||
PRIVATE platform/default/jpeg_reader.cpp
|
||||
PRIVATE platform/default/png_writer.cpp
|
||||
PRIVATE platform/default/png_reader.cpp
|
||||
PRIVATE platform/default/webp_reader.cpp
|
||||
|
||||
|
||||
Reference in New Issue
Block a user