summaryrefslogtreecommitdiff
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
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
-rw-r--r--bin/render.cpp11
-rw-r--r--gyp/platform-android.gypi1
-rw-r--r--gyp/platform-linux.gypi1
-rw-r--r--include/mbgl/map/map.hpp4
-rw-r--r--include/mbgl/map/still_image.hpp19
-rw-r--r--include/mbgl/map/view.hpp6
-rw-r--r--include/mbgl/platform/default/headless_view.hpp2
-rw-r--r--include/mbgl/platform/default/image_reader.hpp40
-rw-r--r--include/mbgl/platform/default/jpeg_reader.hpp72
-rw-r--r--include/mbgl/platform/default/png_reader.hpp65
-rw-r--r--include/mbgl/util/image.hpp35
-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
-rw-r--r--src/mbgl/map/map_context.cpp13
-rw-r--r--src/mbgl/map/view.cpp5
-rw-r--r--src/mbgl/renderer/raster_bucket.cpp2
-rw-r--r--src/mbgl/renderer/raster_bucket.hpp2
-rw-r--r--src/mbgl/sprite/sprite_parser.cpp21
-rw-r--r--src/mbgl/sprite/sprite_parser.hpp9
-rw-r--r--src/mbgl/util/raster.cpp16
-rw-r--r--src/mbgl/util/raster.hpp4
-rw-r--r--src/mbgl/util/worker.cpp7
-rw-r--r--test/api/annotations.cpp9
-rw-r--r--test/api/api_misuse.cpp3
-rw-r--r--test/api/repeated_render.cpp27
-rw-r--r--test/miscellaneous/bilinear.cpp7
-rw-r--r--test/sprite/sprite_parser.cpp22
-rw-r--r--test/style/pending_resources.cpp3
34 files changed, 307 insertions, 696 deletions
diff --git a/bin/render.cpp b/bin/render.cpp
index 685f3ddac4..8a8bcc1a4b 100644
--- a/bin/render.cpp
+++ b/bin/render.cpp
@@ -1,5 +1,4 @@
#include <mbgl/map/map.hpp>
-#include <mbgl/map/still_image.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/io.hpp>
@@ -105,16 +104,14 @@ int main(int argc, char *argv[]) {
uv_async_t *async = new uv_async_t;
uv_async_init(uv_default_loop(), async, [](UV_ASYNC_PARAMS(as)) {
- std::unique_ptr<const StillImage> image(reinterpret_cast<const StillImage *>(as->data));
+ std::unique_ptr<UnassociatedImage> image(reinterpret_cast<UnassociatedImage*>(as->data));
uv_close(reinterpret_cast<uv_handle_t *>(as), [](uv_handle_t *handle) {
delete reinterpret_cast<uv_async_t *>(handle);
});
-
- const std::string png = util::compress_png(image->width, image->height, image->pixels.get());
- util::write_file(output, png);
+ util::write_file(output, encodePNG(*image));
});
- map.renderStill([async](std::exception_ptr error, std::unique_ptr<const StillImage> image) {
+ map.renderStill([async](std::exception_ptr error, UnassociatedImage&& image) {
try {
if (error) {
std::rethrow_exception(error);
@@ -124,7 +121,7 @@ int main(int argc, char *argv[]) {
exit(1);
}
- async->data = const_cast<StillImage *>(image.release());
+ async->data = new UnassociatedImage(std::move(image));
uv_async_send(async);
});
diff --git a/gyp/platform-android.gypi b/gyp/platform-android.gypi
index c12c7d2600..e3e1141c51 100644
--- a/gyp/platform-android.gypi
+++ b/gyp/platform-android.gypi
@@ -15,7 +15,6 @@
'../platform/default/thread.cpp',
'../platform/default/string_stdlib.cpp',
'../platform/default/image.cpp',
- '../platform/default/image_reader.cpp',
'../platform/default/png_reader.cpp',
'../platform/default/jpeg_reader.cpp',
],
diff --git a/gyp/platform-linux.gypi b/gyp/platform-linux.gypi
index f614b831e5..3ee39e2b70 100644
--- a/gyp/platform-linux.gypi
+++ b/gyp/platform-linux.gypi
@@ -16,7 +16,6 @@
'../platform/default/asset_root.cpp',
'../platform/default/thread.cpp',
'../platform/default/image.cpp',
- '../platform/default/image_reader.cpp',
'../platform/default/png_reader.cpp',
'../platform/default/jpeg_reader.cpp',
],
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index c190088088..80b50c2fa4 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -2,6 +2,7 @@
#define MBGL_MAP_MAP
#include <mbgl/util/chrono.hpp>
+#include <mbgl/util/image.hpp>
#include <mbgl/map/update.hpp>
#include <mbgl/map/mode.hpp>
#include <mbgl/util/geo.hpp>
@@ -21,7 +22,6 @@ class FileSource;
class View;
class MapData;
class MapContext;
-class StillImage;
class SpriteImage;
class Transform;
class PointAnnotation;
@@ -58,7 +58,7 @@ public:
// Register a callback that will get called (on the render thread) when all resources have
// been loaded and a complete render occurs.
- using StillImageCallback = std::function<void(std::exception_ptr, std::unique_ptr<const StillImage>)>;
+ using StillImageCallback = std::function<void (std::exception_ptr, UnassociatedImage&&)>;
void renderStill(StillImageCallback callback);
// Triggers a synchronous render.
diff --git a/include/mbgl/map/still_image.hpp b/include/mbgl/map/still_image.hpp
deleted file mode 100644
index 36922fe95a..0000000000
--- a/include/mbgl/map/still_image.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef MBGL_MAP_STILL_IMAGE
-#define MBGL_MAP_STILL_IMAGE
-
-#include <mbgl/util/noncopyable.hpp>
-
-#include <cstdint>
-
-namespace mbgl {
-
-class StillImage : util::noncopyable {
-public:
- size_t width = 0;
- size_t height = 0;
- std::unique_ptr<uint8_t[]> pixels;
-};
-
-}
-
-#endif
diff --git a/include/mbgl/map/view.hpp b/include/mbgl/map/view.hpp
index fadd035e49..68bde5fe30 100644
--- a/include/mbgl/map/view.hpp
+++ b/include/mbgl/map/view.hpp
@@ -2,14 +2,14 @@
#define MBGL_MAP_VIEW
#include <mbgl/util/chrono.hpp>
-#include <functional>
+#include <mbgl/util/image.hpp>
+#include <functional>
#include <memory>
namespace mbgl {
class Map;
-class StillImage;
enum MapChange : uint8_t {
MapChangeRegionWillChange = 0,
@@ -70,7 +70,7 @@ public:
// Reads the pixel data from the current framebuffer. If your View implementation
// doesn't support reading from the framebuffer, return a null pointer.
- virtual std::unique_ptr<StillImage> readStillImage();
+ virtual UnassociatedImage readStillImage();
// Notifies a watcher of map x/y/scale/rotation changes.
// Must only be called from the same thread that caused the change.
diff --git a/include/mbgl/platform/default/headless_view.hpp b/include/mbgl/platform/default/headless_view.hpp
index 50edc48428..8b25620524 100644
--- a/include/mbgl/platform/default/headless_view.hpp
+++ b/include/mbgl/platform/default/headless_view.hpp
@@ -39,7 +39,7 @@ public:
void invalidate() override;
void beforeRender() override;
void afterRender() override;
- std::unique_ptr<StillImage> readStillImage() override;
+ UnassociatedImage readStillImage() override;
void resizeFramebuffer();
void resize(uint16_t width, uint16_t height);
diff --git a/include/mbgl/platform/default/image_reader.hpp b/include/mbgl/platform/default/image_reader.hpp
deleted file mode 100644
index e46bb08ea2..0000000000
--- a/include/mbgl/platform/default/image_reader.hpp
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef MBGL_UTIL_IMAGE_READER_HPP
-#define MBGL_UTIL_IMAGE_READER_HPP
-
-#include <mbgl/util/noncopyable.hpp>
-// stl
-#include <stdexcept>
-#include <string>
-#include <memory>
-
-namespace mbgl { namespace util {
-
-class ImageReaderException : public std::exception
-{
-private:
- std::string message_;
-public:
- ImageReaderException(std::string const& message)
- : message_(message) {}
-
- ~ImageReaderException() throw() {}
-
- virtual const char* what() const throw()
- {
- return message_.c_str();
- }
-};
-
-struct ImageReader : private noncopyable
-{
- virtual unsigned width() const=0;
- virtual unsigned height() const=0;
- virtual std::unique_ptr<uint8_t[]> read()=0;
- virtual ~ImageReader() {}
-};
-
-std::unique_ptr<ImageReader> getImageReader(const uint8_t* data, size_t size);
-
-}}
-
-#endif
diff --git a/include/mbgl/platform/default/jpeg_reader.hpp b/include/mbgl/platform/default/jpeg_reader.hpp
deleted file mode 100644
index 2f988374c3..0000000000
--- a/include/mbgl/platform/default/jpeg_reader.hpp
+++ /dev/null
@@ -1,72 +0,0 @@
-#ifndef MBGL_UTIL_JPEG_READER_HPP
-#define MBGL_UTIL_JPEG_READER_HPP
-
-#include <mbgl/platform/default/image_reader.hpp>
-
-// jpeg
-extern "C"
-{
-#include <jpeglib.h>
-}
-
-#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
-
-namespace mbgl { namespace util {
-
-template <typename T>
-class JpegReader : public ImageReader
-{
-public:
- using source_type = T;
- using input_stream = boost::iostreams::stream<source_type>;
- const static unsigned BUF_SIZE = 4096;
-private:
- struct jpeg_stream_wrapper
- {
- jpeg_source_mgr manager;
- input_stream * stream;
- JOCTET buffer[BUF_SIZE];
- };
-
- struct jpeg_info_guard
- {
- jpeg_info_guard(jpeg_decompress_struct * cinfo)
- : i_(cinfo) {}
-
- ~jpeg_info_guard()
- {
- jpeg_destroy_decompress(i_);
- }
- jpeg_decompress_struct * i_;
- };
-
-private:
- source_type source_;
- input_stream stream_;
- unsigned width_;
- unsigned height_;
-public:
- JpegReader(const uint8_t* data, size_t size);
- ~JpegReader();
- unsigned width() const;
- unsigned height() const;
- std::unique_ptr<uint8_t[]> read();
-private:
- void init();
- static void on_error(j_common_ptr cinfo);
- static void on_error_message(j_common_ptr cinfo);
- static void init_source(j_decompress_ptr cinfo);
- static boolean fill_input_buffer(j_decompress_ptr cinfo);
- static void skip(j_decompress_ptr cinfo, long count);
- static void term(j_decompress_ptr cinfo);
- static void attach_stream(j_decompress_ptr cinfo, input_stream* in);
-};
-
-}}
-
-#endif // MBGL_UTIL_JPEG_READER_HPP
diff --git a/include/mbgl/platform/default/png_reader.hpp b/include/mbgl/platform/default/png_reader.hpp
deleted file mode 100644
index c869e78ea4..0000000000
--- a/include/mbgl/platform/default/png_reader.hpp
+++ /dev/null
@@ -1,65 +0,0 @@
-#ifndef MBGL_UTIL_PNG_READER_HPP
-#define MBGL_UTIL_PNG_READER_HPP
-
-#include <mbgl/platform/default/image_reader.hpp>
-
-extern "C"
-{
-#include <png.h>
-}
-
-#include <cstring>
-#include <memory>
-
-#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
-
-namespace mbgl { namespace util {
-
-template <typename T>
-class PngReader : public ImageReader
-{
- using source_type = T;
- using input_stream = boost::iostreams::stream<source_type>;
-
- 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) {}
-
- ~png_struct_guard()
- {
- png_destroy_read_struct(p_,i_,0);
- }
- png_structpp p_;
- png_infopp i_;
- };
-
-private:
- source_type source_;
- input_stream stream_;
- unsigned width_;
- unsigned height_;
- int bit_depth_;
- int color_type_;
- bool has_alpha_;
-public:
- PngReader(const uint8_t* data, std::size_t size);
- ~PngReader();
- unsigned width() const;
- unsigned height() const;
- std::unique_ptr<uint8_t[]> read();
-private:
- void init();
- static void png_read_data(png_structp png_ptr, png_bytep data, png_size_t length);
-};
-
-
-}}
-
-#endif // MBGL_UTIL_PNG_READER_HPP
diff --git a/include/mbgl/util/image.hpp b/include/mbgl/util/image.hpp
index fc4078501e..0604691dd1 100644
--- a/include/mbgl/util/image.hpp
+++ b/include/mbgl/util/image.hpp
@@ -5,30 +5,37 @@
#include <memory>
namespace mbgl {
-namespace util {
-
-std::string compress_png(size_t width, size_t height, const uint8_t* rgba);
+enum ImageAlphaMode {
+ Unassociated,
+ Premultiplied
+};
+template <ImageAlphaMode Mode>
class Image {
public:
- explicit Image(const std::string& img);
+ Image() {}
- inline const uint8_t* getData() const { return img.get(); }
- inline uint32_t getWidth() const { return width; }
- inline uint32_t getHeight() const { return height; }
- inline operator bool() const { return img && width && height; }
+ Image(size_t w, size_t h)
+ : width(w),
+ height(h),
+ data(std::make_unique<uint8_t[]>(size())) {}
-private:
- // loaded image dimensions
- uint32_t width = 0, height = 0;
+ size_t stride() const { return width * 4; }
+ size_t size() const { return width * height * 4; }
- // the raw image data
- std::unique_ptr<uint8_t[]> img;
+ size_t width = 0;
+ size_t height = 0;
+ std::unique_ptr<uint8_t[]> data;
};
+using UnassociatedImage = Image<ImageAlphaMode::Unassociated>;
+using PremultipliedImage = Image<ImageAlphaMode::Premultiplied>;
+
+// TODO: don't use std::string for binary data.
+PremultipliedImage decodeImage(const std::string&);
+std::string encodePNG(const UnassociatedImage&);
-}
}
#endif
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.
diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp
index cf06dd06eb..f00b3eaf55 100644
--- a/src/mbgl/map/map_context.cpp
+++ b/src/mbgl/map/map_context.cpp
@@ -1,7 +1,6 @@
#include <mbgl/map/map_context.hpp>
#include <mbgl/map/map_data.hpp>
#include <mbgl/map/view.hpp>
-#include <mbgl/map/still_image.hpp>
#include <mbgl/platform/log.hpp>
@@ -200,22 +199,22 @@ void MapContext::renderStill(const TransformState& state, const FrameData& frame
}
if (data.mode != MapMode::Still) {
- fn(std::make_exception_ptr(util::MisuseException("Map is not in still image render mode")), nullptr);
+ fn(std::make_exception_ptr(util::MisuseException("Map is not in still image render mode")), {});
return;
}
if (callback) {
- fn(std::make_exception_ptr(util::MisuseException("Map is currently rendering an image")), nullptr);
+ fn(std::make_exception_ptr(util::MisuseException("Map is currently rendering an image")), {});
return;
}
if (!style) {
- fn(std::make_exception_ptr(util::MisuseException("Map doesn't have a style")), nullptr);
+ fn(std::make_exception_ptr(util::MisuseException("Map doesn't have a style")), {});
return;
}
if (style->getLastError()) {
- fn(style->getLastError(), nullptr);
+ fn(style->getLastError(), {});
return;
}
@@ -246,7 +245,7 @@ bool MapContext::renderSync(const TransformState& state, const FrameData& frame)
painter->render(*style, frame);
if (data.mode == MapMode::Still) {
- callback(nullptr, view.readStillImage());
+ callback(nullptr, std::move(view.readStillImage()));
callback = nullptr;
}
@@ -320,7 +319,7 @@ void MapContext::onResourceLoadingFailed(std::exception_ptr error) {
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
if (data.mode == MapMode::Still && callback) {
- callback(error, nullptr);
+ callback(error, {});
callback = nullptr;
}
}
diff --git a/src/mbgl/map/view.cpp b/src/mbgl/map/view.cpp
index 5da290534a..fa8b7584cc 100644
--- a/src/mbgl/map/view.cpp
+++ b/src/mbgl/map/view.cpp
@@ -1,6 +1,5 @@
#include <mbgl/map/view.hpp>
#include <mbgl/map/map.hpp>
-#include <mbgl/map/still_image.hpp>
#include <cassert>
@@ -11,8 +10,8 @@ void View::initialize(Map *map_) {
map = map_;
}
-std::unique_ptr<StillImage> View::readStillImage() {
- return nullptr;
+UnassociatedImage View::readStillImage() {
+ return {};
}
void View::notifyMapChange(MapChange) {
diff --git a/src/mbgl/renderer/raster_bucket.cpp b/src/mbgl/renderer/raster_bucket.cpp
index 4a7dfeda05..37e5c31cc4 100644
--- a/src/mbgl/renderer/raster_bucket.cpp
+++ b/src/mbgl/renderer/raster_bucket.cpp
@@ -23,7 +23,7 @@ void RasterBucket::render(Painter& painter,
painter.renderRaster(*this, dynamic_cast<const RasterLayer&>(layer), id, matrix);
}
-bool RasterBucket::setImage(std::unique_ptr<util::Image> image) {
+bool RasterBucket::setImage(PremultipliedImage image) {
return raster.load(std::move(image));
}
diff --git a/src/mbgl/renderer/raster_bucket.hpp b/src/mbgl/renderer/raster_bucket.hpp
index 2882a94bbd..e12257d864 100644
--- a/src/mbgl/renderer/raster_bucket.hpp
+++ b/src/mbgl/renderer/raster_bucket.hpp
@@ -18,7 +18,7 @@ public:
void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
bool hasData() const override;
- bool setImage(std::unique_ptr<util::Image> image);
+ bool setImage(PremultipliedImage);
void drawRaster(RasterShader& shader, StaticVertexBuffer &vertices, VertexArrayObject &array);
diff --git a/src/mbgl/sprite/sprite_parser.cpp b/src/mbgl/sprite/sprite_parser.cpp
index a8ed4f0e12..3b6464be9e 100644
--- a/src/mbgl/sprite/sprite_parser.cpp
+++ b/src/mbgl/sprite/sprite_parser.cpp
@@ -14,7 +14,7 @@
namespace mbgl {
-SpriteImagePtr createSpriteImage(const util::Image& image,
+SpriteImagePtr createSpriteImage(const PremultipliedImage& image,
const uint16_t srcX,
const uint16_t srcY,
const uint16_t srcWidth,
@@ -37,18 +37,18 @@ SpriteImagePtr createSpriteImage(const util::Image& image,
std::string data(dstWidth * dstHeight * 4, '\0');
- auto srcData = reinterpret_cast<const uint32_t*>(image.getData());
+ auto srcData = reinterpret_cast<const uint32_t*>(image.data.get());
auto dstData = reinterpret_cast<uint32_t*>(const_cast<char*>(data.data()));
- const int32_t maxX = std::min(image.getWidth(), uint32_t(srcWidth + srcX)) - srcX;
- assert(maxX <= int32_t(image.getWidth()));
- const int32_t maxY = std::min(image.getHeight(), uint32_t(srcHeight + srcY)) - srcY;
- assert(maxY <= int32_t(image.getHeight()));
+ const int32_t maxX = std::min(uint32_t(image.width), uint32_t(srcWidth + srcX)) - srcX;
+ assert(maxX <= int32_t(image.width));
+ const int32_t maxY = std::min(uint32_t(image.height), uint32_t(srcHeight + srcY)) - srcY;
+ assert(maxY <= int32_t(image.height));
// Copy from the source image into our individual sprite image
for (uint16_t y = 0; y < maxY; ++y) {
const auto dstRow = y * dstWidth;
- const auto srcRow = (y + srcY) * image.getWidth() + srcX;
+ const auto srcRow = (y + srcY) * image.width + srcX;
for (uint16_t x = 0; x < maxX; ++x) {
dstData[dstRow + x] = srcData[srcRow + x];
}
@@ -105,10 +105,11 @@ SpriteParseResult parseSprite(const std::string& image, const std::string& json)
using namespace rapidjson;
Sprites sprites;
+ PremultipliedImage raster;
- // Parse the sprite image.
- const util::Image raster(image);
- if (!raster) {
+ try {
+ raster = decodeImage(image);
+ } catch (...) {
return std::string("Could not parse sprite image");
}
diff --git a/src/mbgl/sprite/sprite_parser.hpp b/src/mbgl/sprite/sprite_parser.hpp
index d385d77aea..468ba13c53 100644
--- a/src/mbgl/sprite/sprite_parser.hpp
+++ b/src/mbgl/sprite/sprite_parser.hpp
@@ -3,6 +3,7 @@
#include <mapbox/variant.hpp>
+#include <mbgl/util/image.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/geo.hpp>
@@ -12,18 +13,12 @@
namespace mbgl {
-namespace util {
-
-class Image;
-
-} // namespace util
-
class SpriteImage;
using SpriteImagePtr = std::shared_ptr<const SpriteImage>;
// Extracts an individual image from a spritesheet from the given location.
-SpriteImagePtr createSpriteImage(const util::Image& image,
+SpriteImagePtr createSpriteImage(const PremultipliedImage&,
uint16_t srcX,
uint16_t srcY,
uint16_t srcWidth,
diff --git a/src/mbgl/util/raster.cpp b/src/mbgl/util/raster.cpp
index 1c277040cd..c4d65ff320 100644
--- a/src/mbgl/util/raster.cpp
+++ b/src/mbgl/util/raster.cpp
@@ -25,13 +25,13 @@ bool Raster::isLoaded() const {
return loaded;
}
-bool Raster::load(std::unique_ptr<util::Image> image) {
+bool Raster::load(PremultipliedImage image) {
img = std::move(image);
- width = img->getWidth();
- height = img->getHeight();
+ width = GLsizei(img.width);
+ height = GLsizei(img.height);
std::lock_guard<std::mutex> lock(mtx);
- if (img->getData()) {
+ if (img.data.get()) {
loaded = true;
}
return loaded;
@@ -44,7 +44,7 @@ void Raster::bind(bool linear) {
return;
}
- if (img && !textured) {
+ if (img.data && !textured) {
upload();
} else if (textured) {
MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
@@ -59,7 +59,7 @@ void Raster::bind(bool linear) {
}
void Raster::upload() {
- if (img && !textured) {
+ if (img.data && !textured) {
texture = texturePool.getTextureID();
MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
#ifndef GL_ES_VERSION_2_0
@@ -67,8 +67,8 @@ void Raster::upload() {
#endif
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
- MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img->getData()));
- img.reset();
+ MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.data.get()));
+ img.data.reset();
textured = true;
}
}
diff --git a/src/mbgl/util/raster.hpp b/src/mbgl/util/raster.hpp
index adf3b2cbf7..5c65c46e9d 100644
--- a/src/mbgl/util/raster.hpp
+++ b/src/mbgl/util/raster.hpp
@@ -20,7 +20,7 @@ public:
~Raster();
// load image data
- bool load(std::unique_ptr<util::Image> image);
+ bool load(PremultipliedImage);
// bind current texture
void bind(bool linear = false);
@@ -58,7 +58,7 @@ private:
GLint filter = 0;
// the raw pixels
- std::unique_ptr<util::Image> img;
+ PremultipliedImage img;
};
}
diff --git a/src/mbgl/util/worker.cpp b/src/mbgl/util/worker.cpp
index 9940fb649c..00bdb5baee 100644
--- a/src/mbgl/util/worker.cpp
+++ b/src/mbgl/util/worker.cpp
@@ -17,8 +17,11 @@ public:
void parseRasterTile(std::unique_ptr<RasterBucket> bucket,
const std::shared_ptr<const std::string> data,
std::function<void(RasterTileParseResult)> callback) {
- std::unique_ptr<util::Image> image(new util::Image(*data));
- if (!(*image)) {
+ PremultipliedImage image;
+
+ try {
+ image = decodeImage(*data);
+ } catch (...) {
callback(RasterTileParseResult("error parsing raster image"));
}
diff --git a/test/api/annotations.cpp b/test/api/annotations.cpp
index 55f5ca6749..b16ec71ef6 100644
--- a/test/api/annotations.cpp
+++ b/test/api/annotations.cpp
@@ -4,7 +4,6 @@
#include <mbgl/annotation/shape_annotation.hpp>
#include <mbgl/sprite/sprite_image.hpp>
#include <mbgl/map/map.hpp>
-#include <mbgl/map/still_image.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/storage/default_file_source.hpp>
@@ -17,13 +16,11 @@
using namespace mbgl;
std::string renderPNG(Map& map) {
- std::promise<std::unique_ptr<const StillImage>> promise;
- map.renderStill([&](std::exception_ptr, std::unique_ptr<const StillImage> image) {
+ std::promise<UnassociatedImage> promise;
+ map.renderStill([&](std::exception_ptr, UnassociatedImage&& image) {
promise.set_value(std::move(image));
});
-
- auto result = promise.get_future().get();
- return util::compress_png(result->width, result->height, result->pixels.get());
+ return encodePNG(promise.get_future().get());
}
TEST(Annotations, PointAnnotation) {
diff --git a/test/api/api_misuse.cpp b/test/api/api_misuse.cpp
index 1f54855323..4d5fe11042 100644
--- a/test/api/api_misuse.cpp
+++ b/test/api/api_misuse.cpp
@@ -2,7 +2,6 @@
#include "../fixtures/fixture_log_observer.hpp"
#include <mbgl/map/map.hpp>
-#include <mbgl/map/still_image.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/storage/default_file_source.hpp>
@@ -46,7 +45,7 @@ TEST(API, RenderWithoutStyle) {
Map map(view, fileSource, MapMode::Still);
std::promise<std::exception_ptr> promise;
- map.renderStill([&promise](std::exception_ptr error, std::unique_ptr<const StillImage>) {
+ map.renderStill([&promise](std::exception_ptr error, UnassociatedImage&&) {
promise.set_value(error);
});
diff --git a/test/api/repeated_render.cpp b/test/api/repeated_render.cpp
index 3317b4e3a4..79349c67e7 100644
--- a/test/api/repeated_render.cpp
+++ b/test/api/repeated_render.cpp
@@ -2,7 +2,6 @@
#include "../fixtures/fixture_log_observer.hpp"
#include <mbgl/map/map.hpp>
-#include <mbgl/map/still_image.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/storage/default_file_source.hpp>
@@ -26,28 +25,26 @@ TEST(API, RepeatedRender) {
{
map.setStyleJSON(style, "");
- std::promise<std::unique_ptr<const StillImage>> promise;
- map.renderStill([&promise](std::exception_ptr, std::unique_ptr<const StillImage> image) {
+ std::promise<UnassociatedImage> promise;
+ map.renderStill([&promise](std::exception_ptr, UnassociatedImage&& image) {
promise.set_value(std::move(image));
});
- auto result = promise.get_future().get();
- ASSERT_EQ(256, result->width);
- ASSERT_EQ(512, result->height);
- const std::string png = util::compress_png(result->width, result->height, result->pixels.get());
- util::write_file("test/fixtures/api/1.png", png);
+ auto result = std::move(promise.get_future().get());
+ ASSERT_EQ(256, result.width);
+ ASSERT_EQ(512, result.height);
+ util::write_file("test/fixtures/api/1.png", encodePNG(result));
}
{
map.setStyleJSON(style, "TEST_DATA/suite");
- std::promise<std::unique_ptr<const StillImage>> promise;
- map.renderStill([&promise](std::exception_ptr, std::unique_ptr<const StillImage> image) {
+ std::promise<UnassociatedImage> promise;
+ map.renderStill([&promise](std::exception_ptr, UnassociatedImage&& image) {
promise.set_value(std::move(image));
});
- auto result = promise.get_future().get();
- ASSERT_EQ(256, result->width);
- ASSERT_EQ(512, result->height);
- const std::string png = util::compress_png(result->width, result->height, result->pixels.get());
- util::write_file("test/fixtures/api/2.png", png);
+ auto result = std::move(promise.get_future().get());
+ ASSERT_EQ(256, result.width);
+ ASSERT_EQ(512, result.height);
+ util::write_file("test/fixtures/api/2.png", encodePNG(result));
}
auto observer = Log::removeObserver();
diff --git a/test/miscellaneous/bilinear.cpp b/test/miscellaneous/bilinear.cpp
index 722e7265c0..740025bbbc 100644
--- a/test/miscellaneous/bilinear.cpp
+++ b/test/miscellaneous/bilinear.cpp
@@ -23,8 +23,9 @@ TEST(Bilinear, Scaling) {
const uint32_t *srcData = reinterpret_cast<const uint32_t *>(src + 8);
const vec2<uint32_t> srcSize { width, height };
const vec2<uint32_t> dstSize { 128, 128 };
- auto dst = std::make_unique<uint32_t[]>(dstSize.x * dstSize.y);
- uint32_t *dstData = dst.get();
+
+ UnassociatedImage dst { dstSize.x, dstSize.y };
+ uint32_t *dstData = reinterpret_cast<uint32_t*>(dst.data.get());
std::fill(dstData, dstData + dstSize.x * dstSize.y, 0xFFFF00FF);
util::bilinearScale(srcData, srcSize, { 0, 0, 24, 24 }, dstData, dstSize, { 8, 8, 24, 24 }, false);
@@ -42,7 +43,7 @@ TEST(Bilinear, Scaling) {
util::bilinearScale(srcData, srcSize, { 252, 380, 12, 12 }, dstData, dstSize, { 18, 90, 24, 24 }, false);
const std::string data { reinterpret_cast<char *>(dstData), dstSize.x * dstSize.y * sizeof(uint32_t) };
- util::write_file("test/fixtures/sprites/atlas_actual.png", util::compress_png(dstSize.x, dstSize.y, reinterpret_cast<uint8_t *>(dstData)));
+ util::write_file("test/fixtures/sprites/atlas_actual.png", encodePNG(dst));
util::write_file("test/fixtures/sprites/atlas_actual.bin", util::compress(data));
const std::string reference = util::decompress(util::read_file("test/fixtures/sprites/atlas_reference.bin"));
diff --git a/test/sprite/sprite_parser.cpp b/test/sprite/sprite_parser.cpp
index c2e4df3c58..dfa3a1055b 100644
--- a/test/sprite/sprite_parser.cpp
+++ b/test/sprite/sprite_parser.cpp
@@ -13,10 +13,10 @@ using namespace mbgl;
TEST(Sprite, SpriteImageCreationInvalid) {
FixtureLog log;
- const util::Image image_1x(util::read_file("test/fixtures/annotations/emerald.png"));
- ASSERT_TRUE(image_1x);
- ASSERT_EQ(200u, image_1x.getWidth());
- ASSERT_EQ(299u, image_1x.getHeight());
+ const PremultipliedImage image_1x = decodeImage(util::read_file("test/fixtures/annotations/emerald.png"));
+
+ ASSERT_EQ(200u, image_1x.width);
+ ASSERT_EQ(299u, image_1x.height);
ASSERT_EQ(nullptr, createSpriteImage(image_1x, 0, 0, 0, 16, 1, false)); // width == 0
ASSERT_EQ(nullptr, createSpriteImage(image_1x, 0, 0, 16, 0, 1, false)); // height == 0
@@ -34,10 +34,10 @@ TEST(Sprite, SpriteImageCreationInvalid) {
}
TEST(Sprite, SpriteImageCreation1x) {
- const util::Image image_1x(util::read_file("test/fixtures/annotations/emerald.png"));
- ASSERT_TRUE(image_1x);
- ASSERT_EQ(200u, image_1x.getWidth());
- ASSERT_EQ(299u, image_1x.getHeight());
+ const PremultipliedImage image_1x = decodeImage(util::read_file("test/fixtures/annotations/emerald.png"));
+
+ ASSERT_EQ(200u, image_1x.width);
+ ASSERT_EQ(299u, image_1x.height);
{ // "museum_icon":{"x":177,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false}
const auto sprite = createSpriteImage(image_1x, 177, 187, 18, 18, 1, false);
@@ -74,8 +74,7 @@ TEST(Sprite, SpriteImageCreation1x) {
}
TEST(Sprite, SpriteImageCreation2x) {
- const util::Image image_2x(util::read_file("test/fixtures/annotations/emerald@2x.png"));
- ASSERT_TRUE(image_2x);
+ const PremultipliedImage image_2x = decodeImage(util::read_file("test/fixtures/annotations/emerald@2x.png"));
// "museum_icon":{"x":354,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false}
const auto sprite = createSpriteImage(image_2x, 354, 374, 36, 36, 2, false);
@@ -89,8 +88,7 @@ TEST(Sprite, SpriteImageCreation2x) {
}
TEST(Sprite, SpriteImageCreation1_5x) {
- const util::Image image_2x(util::read_file("test/fixtures/annotations/emerald@2x.png"));
- ASSERT_TRUE(image_2x);
+ const PremultipliedImage image_2x = decodeImage(util::read_file("test/fixtures/annotations/emerald@2x.png"));
// "museum_icon":{"x":354,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false}
const auto sprite = createSpriteImage(image_2x, 354, 374, 36, 36, 1.5, false);
diff --git a/test/style/pending_resources.cpp b/test/style/pending_resources.cpp
index 3ba59657de..58dc72937e 100644
--- a/test/style/pending_resources.cpp
+++ b/test/style/pending_resources.cpp
@@ -3,7 +3,6 @@
#include "../fixtures/util.hpp"
#include <mbgl/map/map.hpp>
-#include <mbgl/map/still_image.hpp>
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/util/io.hpp>
@@ -40,7 +39,7 @@ TEST_P(PendingResources, DeleteMapObjectWithPendingRequest) {
const std::string style = util::read_file("test/fixtures/resources/style.json");
map->setStyleJSON(style, ".");
- map->renderStill([&endTest](std::exception_ptr, std::unique_ptr<const StillImage>) {
+ map->renderStill([&endTest](std::exception_ptr, UnassociatedImage&&) {
EXPECT_TRUE(false) << "Should never happen.";
});