summaryrefslogtreecommitdiff
path: root/platform/default
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2017-01-24 18:57:44 +0100
committerKonstantin Käfer <mail@kkaefer.com>2017-01-25 10:32:23 +0100
commit1715bf9508ec1defe2e2dd1809f1e5226e24337f (patch)
tree546a982e49f5b418b1620307d4b0668df9eb5c59 /platform/default
parent1f3424fafab32431f02c6348382266437bb08aea (diff)
downloadqtlocation-mapboxgl-1715bf9508ec1defe2e2dd1809f1e5226e24337f.tar.gz
[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.
Diffstat (limited to 'platform/default')
-rw-r--r--platform/default/image.cpp70
-rw-r--r--platform/default/png_reader.cpp18
-rw-r--r--platform/default/png_writer.cpp84
3 files changed, 102 insertions, 70 deletions
diff --git a/platform/default/image.cpp b/platform/default/image.cpp
index 84db1e9c71..ad9d83a08d 100644
--- a/platform/default/image.cpp
+++ b/platform/default/image.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__)
diff --git a/platform/default/png_reader.cpp b/platform/default/png_reader.cpp
index 5ae74d74db..d694f43405 100644
--- a/platform/default/png_reader.cpp
+++ b/platform/default/png_reader.cpp
@@ -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) {
diff --git a/platform/default/png_writer.cpp b/platform/default/png_writer.cpp
new file mode 100644
index 0000000000..a620d8b986
--- /dev/null
+++ b/platform/default/png_writer.cpp
@@ -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