diff options
author | Konstantin Käfer <mail@kkaefer.com> | 2015-02-11 19:35:11 -0800 |
---|---|---|
committer | Konstantin Käfer <mail@kkaefer.com> | 2015-02-11 19:35:39 -0800 |
commit | 8c6f71fdb26a4d7a4e2743d3661ff53adbf1658b (patch) | |
tree | 8b41684207913dd12119ffabd70970aa56da31a5 | |
parent | eb5314d20975437b10f6a1df91893165df9de41e (diff) | |
download | qtlocation-mapboxgl-8c6f71fdb26a4d7a4e2743d3661ff53adbf1658b.tar.gz |
adapt to static-render branch changes [skip ci]
still missing updated tests
-rw-r--r-- | .clang-format | 2 | ||||
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | binding.gyp | 4 | ||||
-rw-r--r-- | src/compress_png.cpp | 108 | ||||
-rw-r--r-- | src/compress_png.hpp | 8 | ||||
-rw-r--r-- | src/mbgl.cpp | 2 | ||||
-rw-r--r-- | src/node_map.cpp | 168 | ||||
-rw-r--r-- | src/node_map.hpp | 14 | ||||
-rw-r--r-- | src/node_map_render_worker.cpp | 56 | ||||
-rw-r--r-- | src/node_map_render_worker.hpp | 39 | ||||
m--------- | vendor/mbgl | 0 |
11 files changed, 274 insertions, 128 deletions
diff --git a/.clang-format b/.clang-format index 86d562e74e..109b562b59 100644 --- a/.clang-format +++ b/.clang-format @@ -2,7 +2,7 @@ Standard: Cpp11 IndentWidth: 4 AccessModifierOffset: -4 UseTab: Never -BinPackParameters: false +BinPackParameters: true AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AllowShortBlocksOnASingleLine: false @@ -32,7 +32,6 @@ global: build build: build/Makefile @node-gyp build $(DEBUG_FLAG) --clang -- -j$(JOBS) -.PHONY: vendor/mbgl vendor/mbgl: git submodule update --init diff --git a/binding.gyp b/binding.gyp index fb18393908..dc030205b3 100644 --- a/binding.gyp +++ b/binding.gyp @@ -11,12 +11,12 @@ 'sources': [ 'src/mbgl.cpp', + 'src/compress_png.hpp', + 'src/compress_png.cpp', 'src/node_file_source.hpp', 'src/node_file_source.cpp', 'src/node_map.hpp', 'src/node_map.cpp', - 'src/node_map_render_worker.hpp', - 'src/node_map_render_worker.cpp', 'src/node_request.hpp', 'src/node_request.cpp', ], diff --git a/src/compress_png.cpp b/src/compress_png.cpp new file mode 100644 index 0000000000..699df5346c --- /dev/null +++ b/src/compress_png.cpp @@ -0,0 +1,108 @@ +#include <node.h> +#include <nan.h> + +#include <mbgl/util/image.hpp> + +namespace node_mbgl { + +class CompressPNGWorker : public NanAsyncWorker { +public: + CompressPNGWorker(NanCallback *callback, v8::Local<v8::Object> buffer_, uint32_t width_, + uint32_t height_) + : NanAsyncWorker(callback), + buffer(v8::Persistent<v8::Object>::New(buffer_)), + data(node::Buffer::Data(buffer_)), + width(width_), + height(height_) { + assert(width * height * 4 == node::Buffer::Length(buffer_)); + } + + ~CompressPNGWorker() { + buffer.Dispose(); + } + + void Execute() { + result = mbgl::util::compress_png(width, height, data); + } + + void HandleOKCallback() { + NanScope(); + + auto img = new std::string(std::move(result)); + + v8::Local<v8::Value> argv[] = { + NanNull(), + NanNewBufferHandle( + const_cast<char *>(img->data()), + img->size(), + // Retain the std::string until the buffer is deleted. + [](char *, void *hint) { + delete reinterpret_cast<std::string *>(hint); + }, + img + ) + }; + + callback->Call(2, argv); + }; + +private: + // Retains the buffer while this worker is processing. The user may not modify the buffer. + v8::Persistent<v8::Object> buffer; + void *data; + const uint32_t width; + const uint32_t height; + + // Stores the compressed PNG. + std::string result; +}; + +NAN_METHOD(CompressPNG) { + NanScope(); + + if (args.Length() <= 0 || !args[0]->IsObject()) { + return NanThrowTypeError("First argument must be the data object"); + } + + uint32_t width = 0; + uint32_t height = 0; + v8::Local<v8::Object> buffer; + + auto options = args[0]->ToObject(); + if (options->Has(NanNew("width"))) { + width = options->Get(NanNew("width"))->Uint32Value(); + } + if (!width) { + NanThrowRangeError("Image width must be greater than 0"); + } + if (options->Has(NanNew("height"))) { + height = options->Get(NanNew("height"))->Uint32Value(); + } + if (!height) { + NanThrowRangeError("Image height must be greater than 0"); + } + if (options->Has(NanNew("pixels"))) { + buffer = options->Get(NanNew("pixels")).As<v8::Object>(); + } + if (!node::Buffer::HasInstance(buffer)) { + NanThrowTypeError("Pixels must be a Buffer object"); + } + if (width * height * 4 != node::Buffer::Length(buffer)) { + NanThrowError("Pixel buffer doesn't match image dimensions"); + } + + if (args.Length() < 2) { + NanThrowTypeError("Second argument must be a callback function"); + } + NanCallback *callback = new NanCallback(args[1].As<v8::Function>()); + + NanAsyncQueueWorker(new CompressPNGWorker(callback, buffer, width, height)); + NanReturnUndefined(); +} + +void InitCompressPNG(v8::Handle<v8::Object> target) { + target->Set(NanNew<v8::String>("compressPNG"), + NanNew<v8::FunctionTemplate>(CompressPNG)->GetFunction()); +} + +} diff --git a/src/compress_png.hpp b/src/compress_png.hpp new file mode 100644 index 0000000000..754b383e70 --- /dev/null +++ b/src/compress_png.hpp @@ -0,0 +1,8 @@ +#include <node.h> +#include <nan.h> + +namespace node_mbgl { + +void InitCompressPNG(v8::Handle<v8::Object> target); + +}
\ No newline at end of file diff --git a/src/mbgl.cpp b/src/mbgl.cpp index 677e72b2d1..b218382ab1 100644 --- a/src/mbgl.cpp +++ b/src/mbgl.cpp @@ -4,6 +4,7 @@ #include "node_file_source.hpp" #include "node_map.hpp" #include "node_request.hpp" +#include "compress_png.hpp" void RegisterModule(v8::Handle<v8::Object> exports) { NanScope(); @@ -11,6 +12,7 @@ void RegisterModule(v8::Handle<v8::Object> exports) { node_mbgl::NodeFileSource::Init(exports); node_mbgl::NodeMap::Init(exports); node_mbgl::NodeRequest::Init(exports); + node_mbgl::InitCompressPNG(exports); // Exports Resource constants. auto ConstantProperty = static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete); diff --git a/src/node_map.cpp b/src/node_map.cpp index 8cf9ee9b6a..8a0634a90f 100644 --- a/src/node_map.cpp +++ b/src/node_map.cpp @@ -1,13 +1,25 @@ #include "node_map.hpp" -#include "node_map_render_worker.hpp" #include "node_file_source.hpp" #include <mbgl/platform/default/headless_display.hpp> +#include <mbgl/map/still_image.hpp> +#include <mbgl/util/exception.hpp> #include <unistd.h> namespace node_mbgl { +struct NodeMap::RenderOptions { + double zoom = 0; + double bearing = 0; + double latitude = 0; + double longitude = 0; + unsigned int width = 512; + unsigned int height = 512; + float ratio = 1.0f; + std::vector<std::string> classes; +}; + //////////////////////////////////////////////////////////////////////////////////////////////// // Static Node Methods @@ -21,6 +33,7 @@ void NodeMap::Init(v8::Handle<v8::Object> target) { t->InstanceTemplate()->SetInternalFieldCount(1); t->SetClassName(NanNew("Map")); + NODE_SET_PROTOTYPE_METHOD(t, "setAccessToken", SetAccessToken); NODE_SET_PROTOTYPE_METHOD(t, "load", Load); NODE_SET_PROTOTYPE_METHOD(t, "render", Render); @@ -56,6 +69,32 @@ NAN_METHOD(NodeMap::New) { NanReturnValue(args.This()); } + +NAN_METHOD(NodeMap::SetAccessToken) { + NanScope(); + + if (args.Length() < 1) { + return NanThrowError("Requires a string as first argument"); + } + + NanUtf8String token(args[0]); + + auto nodeMap = node::ObjectWrap::Unwrap<NodeMap>(args.Holder()); + + if (nodeMap->map.isRendering()) { + return NanThrowError("Map object is currently in use"); + } + + try { + nodeMap->map.setAccessToken(std::string { *token, size_t(token.length()) }); + } catch (const std::exception &ex) { + return NanThrowError(ex.what()); + } + + + NanReturnUndefined(); +} + const std::string StringifyStyle(v8::Handle<v8::Value> styleHandle) { NanScope(); @@ -77,19 +116,22 @@ NAN_METHOD(NodeMap::Load) { if (args[0]->IsObject()) { style = StringifyStyle(args[0]); } else if (args[0]->IsString()) { - v8::Local<v8::String> toStr = args[0]->ToString(); - style.resize(toStr->Utf8Length()); - toStr->WriteUtf8(const_cast<char *>(style.data())); + NanUtf8String string(args[0]); + style = { *string, size_t(string.length()) }; } else { return NanThrowTypeError("First argument must be a string or object"); } auto nodeMap = node::ObjectWrap::Unwrap<NodeMap>(args.Holder()); + if (nodeMap->map.isRendering()) { + return NanThrowError("Map object is currently in use"); + } + try { nodeMap->map.setStyleJSON(style, "."); } catch (const std::exception &ex) { - NanThrowError(ex.what()); + return NanThrowError(ex.what()); } NanReturnUndefined(); @@ -110,9 +152,6 @@ std::unique_ptr<NodeMap::RenderOptions> NodeMap::ParseOptions(v8::Local<v8::Obje if (obj->Has(NanNew("width"))) { options->width = obj->Get(NanNew("width"))->IntegerValue(); } if (obj->Has(NanNew("height"))) { options->height = obj->Get(NanNew("height"))->IntegerValue(); } if (obj->Has(NanNew("ratio"))) { options->ratio = obj->Get(NanNew("ratio"))->IntegerValue(); } - if (obj->Has(NanNew("accessToken"))) { - options->accessToken = *NanUtf8String(obj->Get(NanNew("accessToken")->ToString())); - } if (obj->Has(NanNew("classes"))) { auto classes = obj->Get(NanNew("classes"))->ToObject().As<v8::Array>(); @@ -137,23 +176,99 @@ NAN_METHOD(NodeMap::Render) { return NanThrowTypeError("Second argument must be a callback function"); } + auto options = ParseOptions(args[0]->ToObject()); + auto nodeMap = node::ObjectWrap::Unwrap<NodeMap>(args.Holder()); - const bool wasEmpty = nodeMap->queue_.empty(); + if (nodeMap->map.isRendering()) { + return NanThrowError("Map object is currently in use"); + } - nodeMap->queue_.push(mbgl::util::make_unique<RenderWorker>( - nodeMap, - ParseOptions(args[0]->ToObject()), - new NanCallback(args[1].As<v8::Function>()))); + assert(!nodeMap->callback); + assert(!nodeMap->image); + nodeMap->callback = std::unique_ptr<NanCallback>(new NanCallback(args[1].As<v8::Function>())); - if (wasEmpty) { - // When the queue was empty, there was no action in progress, so we can start a new one. - NanAsyncQueueWorker(nodeMap->queue_.front().release()); + try { + nodeMap->startRender(std::move(options)); + } catch (mbgl::util::Exception &ex) { + return NanThrowError(ex.what()); } NanReturnUndefined(); } +void NodeMap::startRender(std::unique_ptr<NodeMap::RenderOptions> options) { + view.resize(options->width, options->height, options->ratio); + map.setClasses(options->classes); + map.setLonLatZoom(options->longitude, options->latitude, options->zoom); + map.setBearing(options->bearing); + + map.renderStill([this](std::unique_ptr<const mbgl::StillImage> result) { + assert(!image); + image = std::move(result); + uv_async_send(async); + }); + + // Retain this object, otherwise it might get destructed before we are finished rendering the + // still image. + Ref(); + + // Similarly, we're now waiting for the async to be called, so we need to make sure that it + // keeps the loop alive. + uv_ref(reinterpret_cast<uv_handle_t *>(async)); +} + +void NodeMap::renderFinished() { + NanScope(); + + // We're done with this render call, so we're unrefing so that the loop could close. + uv_unref(reinterpret_cast<uv_handle_t *>(async)); + + // There is no render pending anymore, we the GC could now delete this object if it went out + // of scope. + Unref(); + + // Move the callback and image out of the way so that the callback can start a new render call. + auto cb = std::move(callback); + auto img = std::move(image); + assert(cb); + + // These have to be empty to be prepared for the next render call. + assert(!callback); + assert(!image); + + if (img) { + v8::Local<v8::Object> result = v8::Object::New(); + result->Set(NanNew("width"), NanNew(img->width)); + result->Set(NanNew("height"), NanNew(img->height)); + + v8::Local<v8::Object> pixels = NanNewBufferHandle( + reinterpret_cast<char *>(img->pixels.get()), + size_t(img->width) * size_t(img->height) * sizeof(mbgl::StillImage::Pixel), + + // Retain the StillImage object until the buffer is deleted. + [](char *, void *hint) { + delete reinterpret_cast<std::unique_ptr<const mbgl::StillImage> *>(hint); + }, + new std::unique_ptr<const mbgl::StillImage>(std::move(img)) + ); + + result->Set(NanNew("pixels"), pixels); + + v8::Local<v8::Value> argv[] = { + NanNull(), + result, + }; + cb->Call(2, argv); + } else { + v8::Local<v8::Value> argv[] = { + NanError("Didn't get an image") + }; + cb->Call(1, argv); + } +} + + //////////////////////////////////////////////////////////////////////////////////////////////// // Instance @@ -165,21 +280,24 @@ std::shared_ptr<mbgl::HeadlessDisplay> sharedDisplay() { NodeMap::NodeMap(v8::Handle<v8::Object> source_) : view(sharedDisplay()), fs(*ObjectWrap::Unwrap<NodeFileSource>(source_)), - map(view, fs) { + map(view, fs, mbgl::Map::RenderMode::Still), + async(new uv_async_t) { source = v8::Persistent<v8::Object>::New(source_); + async->data = this; + uv_async_init(uv_default_loop(), async, [](uv_async_t *as, int) { + reinterpret_cast<NodeMap *>(as->data)->renderFinished(); + }); + + // Make sure the async handle doesn't keep the loop alive. + uv_unref(reinterpret_cast<uv_handle_t *>(async)); } NodeMap::~NodeMap() { source.Dispose(); -} -void NodeMap::processNext() { - assert(!queue_.empty()); - queue_.pop(); - if (!queue_.empty()) { - // When the queue was empty, there was no action in progress, so we can start a new one. - NanAsyncQueueWorker(queue_.front().release()); - } + uv_close(reinterpret_cast<uv_handle_t *>(async), [] (uv_handle_t *handle) { + delete reinterpret_cast<uv_async_t *>(handle); + }); } } diff --git a/src/node_map.hpp b/src/node_map.hpp index fdc625c038..614eab2803 100644 --- a/src/node_map.hpp +++ b/src/node_map.hpp @@ -23,9 +23,13 @@ class NodeMap : public node::ObjectWrap { public: static void Init(v8::Handle<v8::Object> target); static NAN_METHOD(New); + static NAN_METHOD(SetAccessToken); static NAN_METHOD(Load); static NAN_METHOD(Render); + void startRender(std::unique_ptr<NodeMap::RenderOptions> options); + void renderFinished(); + static std::unique_ptr<NodeMap::RenderOptions> ParseOptions(v8::Local<v8::Object> obj); static v8::Persistent<v8::FunctionTemplate> constructorTemplate; @@ -36,17 +40,19 @@ private: NodeMap(v8::Handle<v8::Object> source); ~NodeMap(); - void processNext(); - private: + // For retaining the FileSource object. v8::Persistent<v8::Object> source; - v8::Persistent<v8::Object> self; mbgl::HeadlessView view; NodeFileSource &fs; mbgl::Map map; - std::queue<std::unique_ptr<RenderWorker>> queue_; + std::unique_ptr<const mbgl::StillImage> image; + std::unique_ptr<NanCallback> callback; + + // Async for delivering the notifications of render completion. + uv_async_t *async; }; } // end ns node_mbgl diff --git a/src/node_map_render_worker.cpp b/src/node_map_render_worker.cpp deleted file mode 100644 index 6079e8a8cd..0000000000 --- a/src/node_map_render_worker.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "node_map_render_worker.hpp" -#include "node_file_source.hpp" - -#include <mbgl/util/image.hpp> - -namespace node_mbgl { - -NodeMap::RenderWorker::RenderWorker(NodeMap *nodeMap_, std::unique_ptr<RenderOptions> options_, - NanCallback *callback_) - : NanAsyncWorker(callback_), nodeMap(nodeMap_), options(std::move(options_)) { - nodeMap->Ref(); -} - -NodeMap::RenderWorker::~RenderWorker() { - nodeMap->Unref(); -} - -void NodeMap::RenderWorker::Execute() { - try { - nodeMap->view.resize(options->width, options->height, options->ratio); - nodeMap->map.setAccessToken(options->accessToken); - nodeMap->map.setClasses(options->classes); - nodeMap->map.setLonLatZoom(options->longitude, options->latitude, options->zoom); - nodeMap->map.setBearing(options->bearing); - - // Run the loop. It will terminate when we don't have any further listeners. - nodeMap->map.run(); - - const unsigned int width = options->width * options->ratio; - const unsigned int height = options->height * options->ratio; - auto pixels = nodeMap->view.readPixels(); - image = mbgl::util::compress_png(width, height, pixels.get()); - } catch (mbgl::exception &ex) { - SetErrorMessage(ex.what()); - } -} - -void NodeMap::RenderWorker::WorkComplete() { - NanAsyncWorker::WorkComplete(); - - // Continue processing remaining items in the queue. - nodeMap->processNext(); -} - -void NodeMap::RenderWorker::HandleOKCallback() { - NanScope(); - - v8::Local<v8::Value> argv[] = { - NanNull(), - NanNewBufferHandle(image.data(), image.length()) - }; - - callback->Call(2, argv); -}; - -} // ns node_mbgl diff --git a/src/node_map_render_worker.hpp b/src/node_map_render_worker.hpp deleted file mode 100644 index 920332f7d9..0000000000 --- a/src/node_map_render_worker.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef NODE_MBGL_NODE_MAP_RENDER_WORKER -#define NODE_MBGL_NODE_MAP_RENDER_WORKER - -#include <nan.h> - -#include "node_map.hpp" - -namespace node_mbgl { - -struct NodeMap::RenderOptions { - double zoom = 0; - double bearing = 0; - double latitude = 0; - double longitude = 0; - unsigned int width = 512; - unsigned int height = 512; - float ratio = 1.0f; - std::string accessToken; - std::vector<std::string> classes; -}; - -class NodeMap::RenderWorker : public NanAsyncWorker { -public: - RenderWorker(NodeMap *map, std::unique_ptr<RenderOptions> options, NanCallback *callback); - ~RenderWorker(); - - void Execute(); - void HandleOKCallback(); - void WorkComplete(); - -private: - NodeMap *nodeMap; - std::unique_ptr<RenderOptions> options; - std::string image; -}; - -} // ns node_mbgl - -#endif // NODE_MBGL_RENDER_WORKER diff --git a/vendor/mbgl b/vendor/mbgl -Subproject eac5f6f4540d8adb29230ef07ac46a3306abb42 +Subproject 634de692d70a52706602b2516226ba404cf51ca |