summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
authorJohn Firebaugh <john.firebaugh@gmail.com>2015-11-24 10:07:18 -0800
committerJohn Firebaugh <john.firebaugh@gmail.com>2015-11-25 15:57:36 -0800
commit0c1e378bc9555f6cf826bb38b1a36fa742f8ce9b (patch)
tree9aec8e4f475ff715645072503b3e0ec78f0573af /platform
parent2de0a351a0635192bd05116cebdf0103c2638d05 (diff)
downloadqtlocation-mapboxgl-0c1e378bc9555f6cf826bb38b1a36fa742f8ce9b.tar.gz
[core] Rewrite image handling
* Consolidate Image and StillImage * Typecheck unassociated vs premultiplied images * Rewrite default platform image decoding implementation
Diffstat (limited to 'platform')
-rw-r--r--platform/darwin/image.mm33
-rw-r--r--platform/default/headless_view.cpp21
-rw-r--r--platform/default/image.cpp69
-rw-r--r--platform/default/image_reader.cpp62
-rw-r--r--platform/default/jpeg_reader.cpp199
-rw-r--r--platform/default/png_reader.cpp187
-rw-r--r--platform/node/src/node_map.cpp24
-rw-r--r--platform/node/src/node_map.hpp2
8 files changed, 207 insertions, 390 deletions
diff --git a/platform/darwin/image.mm b/platform/darwin/image.mm
index 8965f1ea93..e7187366b9 100644
--- a/platform/darwin/image.mm
+++ b/platform/darwin/image.mm
@@ -9,10 +9,9 @@
#endif
namespace mbgl {
-namespace util {
-std::string compress_png(size_t width, size_t height, const uint8_t* rgba) {
- CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, rgba, width * height * 4, NULL);
+std::string encodePNG(const UnassociatedImage& src) {
+ CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, src.data.get(), src.size(), NULL);
if (!provider) {
return "";
}
@@ -23,7 +22,7 @@ std::string compress_png(size_t width, size_t height, const uint8_t* rgba) {
return "";
}
- CGImageRef image = CGImageCreate(width, height, 8, 32, 4 * width, color_space,
+ CGImageRef image = CGImageCreate(src.width, src.height, 8, 32, 4 * src.width, color_space,
kCGBitmapByteOrderDefault | kCGImageAlphaLast, provider, NULL, false,
kCGRenderingIntentDefault);
if (!image) {
@@ -66,23 +65,23 @@ std::string compress_png(size_t width, size_t height, const uint8_t* rgba) {
return result;
}
-Image::Image(const std::string &source_data) {
+PremultipliedImage decodeImage(const std::string &source_data) {
CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<const unsigned char *>(source_data.data()), source_data.size(), kCFAllocatorNull);
if (!data) {
- return;
+ throw std::runtime_error("CFDataCreateWithBytesNoCopy failed");
}
CGImageSourceRef image_source = CGImageSourceCreateWithData(data, NULL);
if (!image_source) {
CFRelease(data);
- return;
+ throw std::runtime_error("CGImageSourceCreateWithData failed");
}
CGImageRef image = CGImageSourceCreateImageAtIndex(image_source, 0, NULL);
if (!image) {
CFRelease(image_source);
CFRelease(data);
- return;
+ throw std::runtime_error("CGImageSourceCreateImageAtIndex failed");
}
CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
@@ -90,27 +89,24 @@ Image::Image(const std::string &source_data) {
CGImageRelease(image);
CFRelease(image_source);
CFRelease(data);
- return;
+ throw std::runtime_error("CGColorSpaceCreateDeviceRGB failed");
}
- width = uint32_t(CGImageGetWidth(image));
- height = uint32_t(CGImageGetHeight(image));
- CGRect rect = {{ 0, 0 }, { static_cast<CGFloat>(width), static_cast<CGFloat>(height) }};
+ PremultipliedImage result { CGImageGetWidth(image), CGImageGetHeight(image) };
- img = std::make_unique<uint8_t[]>(width * height * 4);
- CGContextRef context = CGBitmapContextCreate(img.get(), width, height, 8, width * 4,
+ CGContextRef context = CGBitmapContextCreate(result.data.get(), result.width, result.height, 8, result.stride(),
color_space, kCGImageAlphaPremultipliedLast);
if (!context) {
CGColorSpaceRelease(color_space);
CGImageRelease(image);
CFRelease(image_source);
CFRelease(data);
- width = 0;
- height = 0;
- return;
+ throw std::runtime_error("CGBitmapContextCreate failed");
}
CGContextSetBlendMode(context, kCGBlendModeCopy);
+
+ CGRect rect = {{ 0, 0 }, { static_cast<CGFloat>(result.width), static_cast<CGFloat>(result.height) }};
CGContextDrawImage(context, rect, image);
CGContextRelease(context);
@@ -118,7 +114,8 @@ Image::Image(const std::string &source_data) {
CGImageRelease(image);
CFRelease(image_source);
CFRelease(data);
+
+ return result;
}
}
-} \ No newline at end of file
diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp
index fa83f65967..d96b3ffcd1 100644
--- a/platform/default/headless_view.cpp
+++ b/platform/default/headless_view.cpp
@@ -3,9 +3,6 @@
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/log.hpp>
-#include <mbgl/map/still_image.hpp>
-
-
#include <stdexcept>
#include <sstream>
#include <string>
@@ -170,29 +167,25 @@ void HeadlessView::resize(const uint16_t width, const uint16_t height) {
needsResize = true;
}
-std::unique_ptr<StillImage> HeadlessView::readStillImage() {
+UnassociatedImage HeadlessView::readStillImage() {
assert(isActive());
const unsigned int w = dimensions[0] * pixelRatio;
const unsigned int h = dimensions[1] * pixelRatio;
- auto image = std::make_unique<StillImage>();
- image->width = w;
- image->height = h;
- image->pixels = std::make_unique<uint8_t[]>(w * h * 4);
-
- MBGL_CHECK_ERROR(glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels.get()));
+ UnassociatedImage image { w, h };
+ MBGL_CHECK_ERROR(glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image.data.get()));
- const int stride = w * 4;
- auto tmp = std::make_unique<char[]>(stride);
- char *rgba = reinterpret_cast<char *>(image->pixels.get());
+ const int stride = image.stride();
+ auto tmp = std::make_unique<uint8_t[]>(stride);
+ uint8_t* rgba = image.data.get();
for (int i = 0, j = h - 1; i < j; i++, j--) {
std::memcpy(tmp.get(), rgba + i * stride, stride);
std::memcpy(rgba + i * stride, rgba + j * stride, stride);
std::memcpy(rgba + j * stride, tmp.get(), stride);
}
- return image;
+ return std::move(image);
}
void HeadlessView::clearBuffers() {
diff --git a/platform/default/image.cpp b/platform/default/image.cpp
index 182b7d4c01..dc28c68ef9 100644
--- a/platform/default/image.cpp
+++ b/platform/default/image.cpp
@@ -1,16 +1,8 @@
#include <mbgl/util/image.hpp>
-#include <mbgl/platform/log.hpp>
#include <mbgl/util/string.hpp>
#include <png.h>
-#include <cassert>
-#include <cstdlib>
-#include <stdexcept>
-#include <cstring>
-
-#include <mbgl/platform/default/image_reader.hpp>
-
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
// Check png library version.
@@ -25,32 +17,28 @@ const static bool png_version_check = []() {
return true;
}();
#pragma GCC diagnostic pop
-
namespace mbgl {
-namespace util {
-std::string compress_png(size_t width, size_t height, const uint8_t* rgba) {
+std::string encodePNG(const UnassociatedImage& src) {
png_voidp error_ptr = 0;
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, error_ptr, NULL, NULL);
if (!png_ptr) {
- Log::Error(Event::Image, "couldn't create png_ptr");
- return "";
+ 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)0);
- Log::Error(Event::Image, "couldn't create info_ptr");
- return "";
+ throw std::runtime_error("couldn't create info_ptr");
}
- png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
+ png_set_IHDR(png_ptr, info_ptr, src.width, src.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);
- return "";
+ throw std::runtime_error("png error");
}
std::string result;
@@ -63,10 +51,10 @@ std::string compress_png(size_t width, size_t height, const uint8_t* rgba) {
ptrs(size_t count) : rows(new png_bytep[count]) {}
~ptrs() { delete[] rows; }
png_bytep *rows = nullptr;
- } pointers(height);
+ } pointers(src.height);
- for (size_t i = 0; i < height; i++) {
- pointers.rows[i] = (png_bytep)((png_bytep)rgba + width * 4 * i);
+ for (size_t i = 0; i < src.height; i++) {
+ pointers.rows[i] = src.data.get() + src.stride() * i;
}
png_set_rows(png_ptr, info_ptr, pointers.rows);
@@ -76,31 +64,28 @@ std::string compress_png(size_t width, size_t height, const uint8_t* rgba) {
return result;
}
-Image::Image(std::string const& data)
-{
- try
- {
- auto reader = getImageReader(reinterpret_cast<const uint8_t*>(data.c_str()), data.size());
- width = reader->width();
- height = reader->height();
- img = reader->read();
- }
- catch (ImageReaderException const& ex)
- {
- Log::Error(Event::Image, "%s", ex.what());
- img.reset();
- width = 0;
- height = 0;
+PremultipliedImage decodePNG(const uint8_t*, size_t);
+PremultipliedImage decodeJPEG(const uint8_t*, size_t);
+PremultipliedImage decodeImage(const std::string& string) {
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(string.data());
+ const size_t size = string.size();
+
+ if (size >= 4) {
+ unsigned int magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
+ if (magic == 0x89504E47U) {
+ return decodePNG(data, size);
+ }
}
- catch (...) // catch the rest
- {
- Log::Error(Event::Image, "exception in constructor");
- img.reset();
- width = 0;
- height = 0;
+
+ if (size >= 2) {
+ unsigned int magic = ((data[0] << 8) | data[1]) & 0xffff;
+ if (magic == 0xffd8) {
+ return decodeJPEG(data, size);
+ }
}
-}
+ throw std::runtime_error("unsupported image type");
}
+
}
diff --git a/platform/default/image_reader.cpp b/platform/default/image_reader.cpp
deleted file mode 100644
index 3d27db58c3..0000000000
--- a/platform/default/image_reader.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#include <mbgl/platform/default/image_reader.hpp>
-#include <mbgl/platform/default/png_reader.hpp>
-#include <mbgl/platform/default/jpeg_reader.hpp>
-
-#include <boost/optional.hpp>
-#include <boost/iostreams/device/array.hpp>
-
-namespace mbgl { namespace util {
-
-inline boost::optional<std::string> type_from_bytes(const uint8_t* data, size_t size)
-{
- using result_type = boost::optional<std::string>;
- if (size >= 4)
- {
- unsigned int magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
- if (magic == 0x89504E47U)
- {
- return result_type("png");
- }
- else if (magic == 0x49492A00 || magic == 0x4D4D002A)
- {
- return result_type("tiff");
- }
- }
- if (size>=2)
- {
- unsigned int magic = ((data[0] << 8) | data[1]) & 0xffff;
- if (magic == 0xffd8)
- {
- return result_type("jpeg");
- }
- }
-
- if (size>=12)
- {
- if (data[0] == 'R' && data[1] == 'I' && data[2] == 'F' && data[3] == 'F' &&
- data[8] == 'W' && data[9] == 'E' && data[10] == 'B' && data[11] == 'P')
- {
- return result_type("webp");
- }
- }
- return result_type();
-}
-
-std::unique_ptr<ImageReader> getImageReader(const uint8_t* data, size_t size)
-{
- boost::optional<std::string> type = type_from_bytes(data, size);
- if (type)
- {
- if (*type == "png")
- {
- return std::make_unique<PngReader<boost::iostreams::array_source>>(data, size);
- }
- else if (*type == "jpeg")
- {
- return std::make_unique<JpegReader<boost::iostreams::array_source>>(data, size);
- }
- }
- throw ImageReaderException("ImageReader: can't determine type from input data");
-}
-
-}}
diff --git a/platform/default/jpeg_reader.cpp b/platform/default/jpeg_reader.cpp
index a537eeeb08..73b0b9c1b2 100644
--- a/platform/default/jpeg_reader.cpp
+++ b/platform/default/jpeg_reader.cpp
@@ -1,43 +1,39 @@
-#include <mbgl/platform/default/jpeg_reader.hpp>
+#include <mbgl/util/image.hpp>
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
+#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
+#pragma GCC diagnostic ignored "-Wshadow"
+#include <boost/iostreams/stream.hpp>
+#pragma GCC diagnostic pop
-// boost
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/device/array.hpp>
+extern "C"
+{
+#include <jpeglib.h>
+}
-// std
-#include <cstdio>
-#include <memory>
+namespace mbgl {
-namespace mbgl { namespace util {
+using source_type = boost::iostreams::array_source;
+using input_stream = boost::iostreams::stream<source_type>;
-// ctor
-template <typename T>
-JpegReader<T>::JpegReader(const uint8_t* data, size_t size)
- : source_(reinterpret_cast<const char*>(data), size),
- stream_(source_),
- width_(0),
- height_(0)
-{
- if (!stream_) throw ImageReaderException("cannot open image stream");
- init();
-}
+const static unsigned BUF_SIZE = 4096;
-// dtor
-template <typename T>
-JpegReader<T>::~JpegReader() {}
+struct jpeg_stream_wrapper {
+ jpeg_source_mgr manager;
+ input_stream * stream;
+ JOCTET buffer[BUF_SIZE];
+};
-// jpeg stream wrapper
-template <typename T>
-void JpegReader<T>::init_source (j_decompress_ptr cinfo)
-{
+static void init_source(j_decompress_ptr cinfo) {
jpeg_stream_wrapper* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src);
wrap->stream->seekg(0,std::ios_base::beg);
}
-template <typename T>
-boolean JpegReader<T>::fill_input_buffer (j_decompress_ptr cinfo)
-{
+static boolean fill_input_buffer(j_decompress_ptr cinfo) {
jpeg_stream_wrapper* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src);
wrap->stream->read(reinterpret_cast<char*>(&wrap->buffer[0]),BUF_SIZE);
std::streamsize size = wrap->stream->gcount();
@@ -46,9 +42,7 @@ boolean JpegReader<T>::fill_input_buffer (j_decompress_ptr cinfo)
return (size > 0) ? TRUE : FALSE;
}
-template <typename T>
-void JpegReader<T>::skip(j_decompress_ptr cinfo, long count)
-{
+static void skip(j_decompress_ptr cinfo, long count) {
if (count <= 0) return; //A zero or negative skip count should be treated as a no-op.
jpeg_stream_wrapper* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src);
@@ -66,21 +60,14 @@ void JpegReader<T>::skip(j_decompress_ptr cinfo, long count)
}
}
-template <typename T>
-void JpegReader<T>::term (j_decompress_ptr /*cinfo*/)
-{
-// no-op
-}
+static void term(j_decompress_ptr) {}
-template <typename T>
-void JpegReader<T>::attach_stream (j_decompress_ptr cinfo, input_stream* in)
-{
- if (cinfo->src == 0)
- {
+static void attach_stream(j_decompress_ptr cinfo, input_stream* in) {
+ if (cinfo->src == 0) {
cinfo->src = (struct jpeg_source_mgr *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(jpeg_stream_wrapper));
}
- JpegReader::jpeg_stream_wrapper * src = reinterpret_cast<JpegReader::jpeg_stream_wrapper*> (cinfo->src);
+ jpeg_stream_wrapper * src = reinterpret_cast<jpeg_stream_wrapper*> (cinfo->src);
src->manager.init_source = init_source;
src->manager.fill_input_buffer = fill_input_buffer;
src->manager.skip_input_data = skip;
@@ -91,22 +78,29 @@ void JpegReader<T>::attach_stream (j_decompress_ptr cinfo, input_stream* in)
src->stream = in;
}
-template <typename T>
-void JpegReader<T>::on_error(j_common_ptr /*cinfo*/)
-{
-}
+static void on_error(j_common_ptr) {}
-template <typename T>
-void JpegReader<T>::on_error_message(j_common_ptr cinfo)
-{
+static void on_error_message(j_common_ptr cinfo) {
char buffer[JMSG_LENGTH_MAX];
(*cinfo->err->format_message)(cinfo, buffer);
- throw ImageReaderException(std::string("JPEG Reader: libjpeg could not read image: ") + buffer);
+ throw std::runtime_error(std::string("JPEG Reader: libjpeg could not read image: ") + buffer);
}
-template <typename T>
-void JpegReader<T>::init()
-{
+struct jpeg_info_guard {
+ jpeg_info_guard(jpeg_decompress_struct* cinfo)
+ : i_(cinfo) {}
+
+ ~jpeg_info_guard() {
+ jpeg_destroy_decompress(i_);
+ }
+
+ jpeg_decompress_struct* i_;
+};
+
+PremultipliedImage decodeJPEG(const uint8_t* data, size_t size) {
+ source_type source(reinterpret_cast<const char*>(data), size);
+ input_stream stream(source);
+
jpeg_decompress_struct cinfo;
jpeg_info_guard iguard(&cinfo);
jpeg_error_mgr jerr;
@@ -114,91 +108,52 @@ void JpegReader<T>::init()
jerr.error_exit = on_error;
jerr.output_message = on_error_message;
jpeg_create_decompress(&cinfo);
- attach_stream(&cinfo, &stream_);
+ attach_stream(&cinfo, &stream);
+
int ret = jpeg_read_header(&cinfo, TRUE);
if (ret != JPEG_HEADER_OK)
- throw ImageReaderException("JPEG Reader: failed to read header");
+ throw std::runtime_error("JPEG Reader: failed to read header");
+
jpeg_start_decompress(&cinfo);
- width_ = cinfo.output_width;
- height_ = cinfo.output_height;
if (cinfo.out_color_space == JCS_UNKNOWN)
- {
- throw ImageReaderException("JPEG Reader: failed to read unknown color space");
- }
- if (cinfo.output_width == 0 || cinfo.output_height == 0)
- {
- throw ImageReaderException("JPEG Reader: failed to read image size of");
- }
-}
+ throw std::runtime_error("JPEG Reader: failed to read unknown color space");
-template <typename T>
-unsigned JpegReader<T>::width() const
-{
- return width_;
-}
+ if (cinfo.output_width == 0 || cinfo.output_height == 0)
+ throw std::runtime_error("JPEG Reader: failed to read image size");
-template <typename T>
-unsigned JpegReader<T>::height() const
-{
- return height_;
-}
+ size_t width = cinfo.output_width;
+ size_t height = cinfo.output_height;
+ size_t components = cinfo.output_components;
+ size_t rowStride = components * width;
-template <typename T>
-std::unique_ptr<uint8_t[]> JpegReader<T>::read()
-{
- std::unique_ptr<uint8_t[]> image = std::make_unique<uint8_t[]>(width_ * height_ * 4);
+ PremultipliedImage image { width, height };
+ uint8_t* dst = image.data.get();
- stream_.clear();
- stream_.seekg(0, std::ios_base::beg);
+ JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, rowStride, 1);
- jpeg_decompress_struct cinfo;
- jpeg_info_guard iguard(&cinfo);
- jpeg_error_mgr jerr;
- cinfo.err = jpeg_std_error(&jerr);
- jerr.error_exit = on_error;
- jerr.output_message = on_error_message;
- jpeg_create_decompress(&cinfo);
- attach_stream(&cinfo, &stream_);
- int ret = jpeg_read_header(&cinfo, TRUE);
- if (ret != JPEG_HEADER_OK) throw ImageReaderException("JPEG Reader read(): failed to read header");
- jpeg_start_decompress(&cinfo);
- JSAMPARRAY buffer;
- int row_stride;
- unsigned char r,g,b;
- row_stride = cinfo.output_width * cinfo.output_components;
- buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
-
- const std::unique_ptr<unsigned int[]> out_row(new unsigned int[width_]);
- unsigned row = 0;
- while (cinfo.output_scanline < cinfo.output_height)
- {
+ while (cinfo.output_scanline < cinfo.output_height) {
jpeg_read_scanlines(&cinfo, buffer, 1);
- if (row < height_)
- {
- for (unsigned int x = 0; x < width_; ++x)
- {
- unsigned col = x;
- r = buffer[0][cinfo.output_components * col];
- if (cinfo.output_components > 2)
- {
- g = buffer[0][cinfo.output_components * col + 1];
- b = buffer[0][cinfo.output_components * col + 2];
- } else {
- g = r;
- b = r;
- }
- out_row[x] = (0xff << 24) | (b << 16) | (g << 8) | r;
+
+ for (size_t i = 0; i < width; ++i) {
+ dst[0] = buffer[0][components * i];
+ dst[3] = 0xFF;
+
+ if (components > 2) {
+ dst[1] = buffer[0][components * i + 1];
+ dst[2] = buffer[0][components * i + 2];
+ } else {
+ dst[1] = dst[0];
+ dst[2] = dst[0];
}
- std::copy((char*)out_row.get(), (char*)out_row.get() + width_*4, image.get() + row*width_*4);
+
+ dst += 4;
}
- ++row;
}
+
jpeg_finish_decompress(&cinfo);
- return image;
+ return std::move(image);
}
-template class JpegReader<boost::iostreams::array_source>;
-
-}}
+}
diff --git a/platform/default/png_reader.cpp b/platform/default/png_reader.cpp
index 1f7207337c..7577d9725c 100644
--- a/platform/default/png_reader.cpp
+++ b/platform/default/png_reader.cpp
@@ -1,35 +1,35 @@
-#include <mbgl/platform/default/png_reader.hpp>
+#include <mbgl/util/image.hpp>
#include <mbgl/platform/log.hpp>
-#include <iostream>
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
+#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
+#pragma GCC diagnostic ignored "-Wshadow"
+#include <boost/iostreams/stream.hpp>
+#pragma GCC diagnostic pop
+
+#include <boost/iostreams/device/file.hpp>
+#include <boost/iostreams/device/array.hpp>
+
extern "C"
{
#include <png.h>
}
-// boost
-#include <boost/iostreams/device/file.hpp>
-#include <boost/iostreams/device/array.hpp>
-#include <boost/iostreams/stream.hpp>
-
-// stl
-#include <cstring>
-#include <memory>
+namespace mbgl {
-namespace mbgl { namespace util {
+using source_type = boost::iostreams::array_source;
+using input_stream = boost::iostreams::stream<source_type>;
-void user_error_fn(png_structp /*png_ptr*/, png_const_charp error_msg)
-{
- throw ImageReaderException(std::string("failed to read invalid png: '") + error_msg + "'");
+static void user_error_fn(png_structp, png_const_charp error_msg) {
+ throw std::runtime_error(std::string("failed to read invalid png: '") + error_msg + "'");
}
-void user_warning_fn(png_structp /*png_ptr*/, png_const_charp warning_msg)
-{
+static void user_warning_fn(png_structp, png_const_charp warning_msg) {
Log::Warning(Event::Image, "ImageReader (PNG): %s", warning_msg);
}
-template <typename T>
-void PngReader<T>::png_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
-{
+static void png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
input_stream * fin = reinterpret_cast<input_stream*>(png_get_io_ptr(png_ptr));
fin->read(reinterpret_cast<char*>(data), length);
std::streamsize read_count = fin->gcount();
@@ -39,122 +39,74 @@ void PngReader<T>::png_read_data(png_structp png_ptr, png_bytep data, png_size_t
}
}
-template <typename T>
-PngReader<T>::PngReader(const uint8_t* data, std::size_t size)
- : source_(reinterpret_cast<const char*>(data), size),
- stream_(source_),
- width_(0),
- height_(0),
- bit_depth_(0),
- color_type_(0),
- has_alpha_(false)
-{
+struct png_struct_guard {
+ png_struct_guard(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
+ : p_(png_ptr_ptr),
+ i_(info_ptr_ptr) {}
- if (!stream_) throw ImageReaderException("PNG reader: cannot open image stream");
- init();
-}
+ ~png_struct_guard() {
+ png_destroy_read_struct(p_,i_,0);
+ }
+ png_structpp p_;
+ png_infopp i_;
+};
-template <typename T>
-PngReader<T>::~PngReader() {}
+PremultipliedImage decodePNG(const uint8_t* data, size_t size) {
+ source_type source(reinterpret_cast<const char*>(data), size);
+ input_stream stream(source);
+ png_byte header[8] = { 0 };
+ stream.read(reinterpret_cast<char*>(header), 8);
+ if (stream.gcount() != 8)
+ throw std::runtime_error("PNG reader: Could not read image");
-template <typename T>
-void PngReader<T>::init()
-{
- png_byte header[8];
- std::memset(header,0,8);
- stream_.read(reinterpret_cast<char*>(header),8);
- if ( stream_.gcount() != 8)
- {
- throw ImageReaderException("PNG reader: Could not read image");
- }
- int is_png=!png_sig_cmp(header,0,8);
+ int is_png = !png_sig_cmp(header, 0, 8);
if (!is_png)
- {
- throw ImageReaderException("File or stream is not a png");
- }
- png_structp png_ptr = png_create_read_struct
- (PNG_LIBPNG_VER_STRING,0,0,0);
+ throw std::runtime_error("File or stream is not a png");
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
if (!png_ptr)
- {
- throw ImageReaderException("failed to allocate png_ptr");
- }
+ throw std::runtime_error("failed to allocate png_ptr");
// catch errors in a custom way to avoid the need for setjmp
png_set_error_fn(png_ptr, png_get_error_ptr(png_ptr), user_error_fn, user_warning_fn);
png_infop info_ptr;
- png_struct_guard sguard(&png_ptr,&info_ptr);
+ png_struct_guard sguard(&png_ptr, &info_ptr);
info_ptr = png_create_info_struct(png_ptr);
- if (!info_ptr) throw ImageReaderException("failed to create info_ptr");
+ if (!info_ptr)
+ throw std::runtime_error("failed to create info_ptr");
- png_set_read_fn(png_ptr, (png_voidp)&stream_, png_read_data);
-
- png_set_sig_bytes(png_ptr,8);
+ png_set_read_fn(png_ptr, &stream, png_read_data);
+ png_set_sig_bytes(png_ptr, 8);
png_read_info(png_ptr, info_ptr);
- png_uint_32 w, h;
- png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth_, &color_type_,0,0,0);
- has_alpha_ = (color_type_ & PNG_COLOR_MASK_ALPHA) || png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
- width_=w;
- height_=h;
-}
+ unsigned width = 0;
+ unsigned height = 0;
+ int bit_depth = 0;
+ int color_type = 0;
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
-template <typename T>
-unsigned PngReader<T>::width() const
-{
- return width_;
-}
+ PremultipliedImage image { width, height };
-template <typename T>
-unsigned PngReader<T>::height() const
-{
- return height_;
-}
-
-template <typename T>
-std::unique_ptr<uint8_t[]> PngReader<T>::read()
-{
- std::unique_ptr<uint8_t[]> image = std::make_unique<uint8_t[]>(width_ * height_ * 4);
-
- stream_.clear();
- stream_.seekg(0, std::ios_base::beg);
-
- png_structp png_ptr = png_create_read_struct
- (PNG_LIBPNG_VER_STRING,0,0,0);
-
- if (!png_ptr)
- {
- throw ImageReaderException("failed to allocate png_ptr");
- }
-
- // catch errors in a custom way to avoid the need for setjmp
- png_set_error_fn(png_ptr, png_get_error_ptr(png_ptr), user_error_fn, user_warning_fn);
-
- png_infop info_ptr;
- png_struct_guard sguard(&png_ptr,&info_ptr);
- info_ptr = png_create_info_struct(png_ptr);
- if (!info_ptr) throw ImageReaderException("failed to create info_ptr");
-
- png_set_read_fn(png_ptr, (png_voidp)&stream_, png_read_data);
- png_read_info(png_ptr, info_ptr);
-
- if (color_type_ == PNG_COLOR_TYPE_PALETTE)
+ if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_expand(png_ptr);
- if (color_type_ == PNG_COLOR_TYPE_GRAY && bit_depth_ < 8)
+
+ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand(png_ptr);
+
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
png_set_expand(png_ptr);
- if (bit_depth_ == 16)
+
+ if (bit_depth == 16)
png_set_strip_16(png_ptr);
- if (color_type_ == PNG_COLOR_TYPE_GRAY ||
- color_type_ == PNG_COLOR_TYPE_GRAY_ALPHA)
+
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png_ptr);
- // quick hack -- only work in >=libpng 1.2.7
- png_set_add_alpha(png_ptr,0xff,PNG_FILLER_AFTER); //rgba
+ png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
double gamma;
if (png_get_gAMA(png_ptr, info_ptr, &gamma))
@@ -164,19 +116,20 @@ std::unique_ptr<uint8_t[]> PngReader<T>::read()
png_set_alpha_mode(png_ptr, PNG_ALPHA_PREMULTIPLIED, PNG_GAMMA_LINEAR);
#endif
- if (png_get_interlace_type(png_ptr,info_ptr) == PNG_INTERLACE_ADAM7)
- {
+ if (png_get_interlace_type(png_ptr,info_ptr) == PNG_INTERLACE_ADAM7) {
png_set_interlace_handling(png_ptr); // FIXME: libpng bug?
// according to docs png_read_image
// "..automatically handles interlacing,
// so you don't need to call png_set_interlace_handling()"
}
+
png_read_update_info(png_ptr, info_ptr);
+
// we can read whole image at once
// alloc row pointers
- const std::unique_ptr<png_bytep[]> rows(new png_bytep[height_]);
- for (unsigned row = 0; row < height_; ++row)
- rows[row] = (png_bytep)image.get() + row * width_ * 4 ;
+ const std::unique_ptr<png_bytep[]> rows(new png_bytep[height]);
+ for (unsigned row = 0; row < height; ++row)
+ rows[row] = image.data.get() + row * width * 4;
png_read_image(png_ptr, rows.get());
#ifndef PNG_ALPHA_PREMULTIPLIED
@@ -192,11 +145,9 @@ std::unique_ptr<uint8_t[]> PngReader<T>::read()
}
#endif
- png_read_end(png_ptr,0);
+ png_read_end(png_ptr, 0);
return image;
}
-template class PngReader<boost::iostreams::array_source>;
-
-}}
+}
diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp
index eb92205487..0ce7d9398b 100644
--- a/platform/node/src/node_map.cpp
+++ b/platform/node/src/node_map.cpp
@@ -3,7 +3,6 @@
#include "node_mapbox_gl_native.hpp"
#include <mbgl/platform/default/headless_display.hpp>
-#include <mbgl/map/still_image.hpp>
#include <mbgl/util/exception.hpp>
#include <mbgl/util/work_request.hpp>
@@ -281,7 +280,7 @@ NAN_METHOD(NodeMap::Render) {
auto options = ParseOptions(info[0]->ToObject());
assert(!nodeMap->callback);
- assert(!nodeMap->image);
+ assert(!nodeMap->image.data);
nodeMap->callback = std::make_unique<Nan::Callback>(info[1].As<v8::Function>());
try {
@@ -301,12 +300,12 @@ void NodeMap::startRender(std::unique_ptr<NodeMap::RenderOptions> options) {
map->setBearing(options->bearing);
map->setPitch(options->pitch);
- map->renderStill([this](const std::exception_ptr eptr, std::unique_ptr<const mbgl::StillImage> result) {
+ map->renderStill([this](const std::exception_ptr eptr, mbgl::UnassociatedImage&& result) {
if (eptr) {
error = std::move(eptr);
uv_async_send(async);
} else {
- assert(!image);
+ assert(!image.data);
image = std::move(result);
uv_async_send(async);
}
@@ -338,7 +337,7 @@ void NodeMap::renderFinished() {
// These have to be empty to be prepared for the next render call.
assert(!callback);
- assert(!image);
+ assert(!image.data);
if (error) {
std::string errorMessage;
@@ -358,17 +357,16 @@ void NodeMap::renderFinished() {
assert(!error);
cb->Call(1, argv);
- } else if (img) {
+ } else if (img.data) {
v8::Local<v8::Object> pixels = Nan::NewBuffer(
- reinterpret_cast<char *>(img->pixels.get()), img->width * img->height * 4,
-
- // Retain the StillImage object until the buffer is deleted.
- [](char *, void *hint) {
- delete reinterpret_cast<const mbgl::StillImage *>(hint);
+ reinterpret_cast<char *>(img.data.get()), img.size(),
+ // Retain the data until the buffer is deleted.
+ [](char *, void * hint) {
+ delete [] reinterpret_cast<uint8_t*>(hint);
},
- const_cast<mbgl::StillImage *>(img.get())
+ img.data.get()
).ToLocalChecked();
- img.release();
+ img.data.release();
v8::Local<v8::Value> argv[] = {
Nan::Null(),
diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp
index f610408d3f..455d38e957 100644
--- a/platform/node/src/node_map.hpp
+++ b/platform/node/src/node_map.hpp
@@ -49,7 +49,7 @@ public:
std::unique_ptr<mbgl::Map> map;
std::exception_ptr error;
- std::unique_ptr<const mbgl::StillImage> image;
+ mbgl::UnassociatedImage image;
std::unique_ptr<Nan::Callback> callback;
// Async for delivering the notifications of render completion.