[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:
Konstantin Käfer
2017-01-24 18:57:44 +01:00
parent 1f3424fafa
commit 1715bf9508
5 changed files with 104 additions and 70 deletions

View File

@@ -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

View File

@@ -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__)

View File

@@ -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) {

View 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

View File

@@ -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