diff options
-rw-r--r-- | platform/android/config.cmake | 1 | ||||
-rw-r--r-- | platform/default/image.cpp | 70 | ||||
-rw-r--r-- | platform/default/png_reader.cpp | 18 | ||||
-rw-r--r-- | platform/default/png_writer.cpp | 84 | ||||
-rw-r--r-- | platform/linux/config.cmake | 1 |
5 files changed, 104 insertions, 70 deletions
diff --git a/platform/android/config.cmake b/platform/android/config.cmake index c3c461ffa1..7d522a1212 100644 --- a/platform/android/config.cmake +++ b/platform/android/config.cmake @@ -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 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 diff --git a/platform/linux/config.cmake b/platform/linux/config.cmake index 13f3edb96f..caf91fdce2 100644 --- a/platform/linux/config.cmake +++ b/platform/linux/config.cmake @@ -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 |