summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format2
-rw-r--r--.gitignore4
-rw-r--r--Makefile34
-rw-r--r--README.md21
-rw-r--r--android/cpp/jni.cpp49
-rw-r--r--android/cpp/native_map_view.cpp31
-rw-r--r--android/java/MapboxGLAndroidSDK/build.gradle8
-rw-r--r--android/java/MapboxGLAndroidSDK/gradle.properties6
-rw-r--r--android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/NativeMapView.java51
-rw-r--r--android/java/MapboxGLAndroidSDKTestApp/build.gradle16
-rwxr-xr-xandroid/scripts/common.sh31
-rwxr-xr-xandroid/scripts/run-build.sh2
-rw-r--r--bin/render.cpp34
-rwxr-xr-xconfigure16
-rw-r--r--docker/linux/Dockerfile19
-rw-r--r--docker/linux/run-clang.sh15
-rwxr-xr-xdocker/linux/run-gcc.sh15
-rwxr-xr-xdocker/linux/test.sh26
-rw-r--r--gyp/cache-sqlite.gypi3
-rw-r--r--gyp/common.gypi1
-rw-r--r--gyp/core.gypi3
-rw-r--r--gyp/headless-glx.gypi6
-rw-r--r--gyp/http-curl.gypi43
-rw-r--r--gyp/http-nsurl.gypi10
-rw-r--r--gyp/platform-osx.gypi2
-rw-r--r--include/mbgl/android/native_map_view.hpp11
-rw-r--r--include/mbgl/map/annotation.hpp72
-rw-r--r--include/mbgl/map/map.hpp75
-rw-r--r--include/mbgl/map/view.hpp20
-rw-r--r--include/mbgl/platform/android/log_android.hpp24
-rw-r--r--include/mbgl/platform/darwin/log_nslog.hpp21
-rw-r--r--include/mbgl/platform/default/glfw_view.hpp25
-rw-r--r--include/mbgl/platform/default/headless_view.hpp13
-rw-r--r--include/mbgl/platform/default/log_stderr.hpp21
-rw-r--r--include/mbgl/platform/log.hpp39
-rw-r--r--include/mbgl/platform/platform.hpp4
-rw-r--r--include/mbgl/storage/default/request.hpp18
-rw-r--r--include/mbgl/storage/default/shared_request_base.hpp37
-rw-r--r--include/mbgl/storage/default_file_source.hpp18
-rw-r--r--include/mbgl/storage/file_source.hpp12
-rw-r--r--include/mbgl/util/constants.hpp3
-rw-r--r--include/mbgl/util/exception.hpp27
-rw-r--r--include/mbgl/util/geo.hpp15
-rw-r--r--include/mbgl/util/uv.hpp74
-rw-r--r--include/mbgl/util/variant.hpp187
-rw-r--r--include/mbgl/util/variant_io.hpp41
-rw-r--r--ios/app/MBXViewController.mm3
-rw-r--r--ios/app/img/Icon-60@3x.pngbin0 -> 1300 bytes
-rw-r--r--ios/app/img/Icon-76@3x.pngbin0 -> 1741 bytes
-rw-r--r--ios/app/img/Icon-Small@3x.pngbin0 -> 807 bytes
-rw-r--r--ios/app/img/Icon-Spotlight-40@3x.pngbin0 -> 961 bytes
-rw-r--r--ios/app/img/locateUser@3x.pngbin0 -> 521 bytes
-rw-r--r--ios/app/mapboxgl-app.gyp1
-rw-r--r--linux/main.cpp6
-rw-r--r--linux/mapboxgl-app.gyp9
-rw-r--r--macosx/main.mm4
-rw-r--r--macosx/mapboxgl-app.gyp2
-rw-r--r--platform/android/log_android.cpp39
-rw-r--r--platform/darwin/asset_root.mm11
-rw-r--r--platform/darwin/http_request_nsurl.mm3
-rw-r--r--platform/darwin/image.mm1
-rw-r--r--platform/darwin/log_nslog.mm34
-rw-r--r--platform/default/glfw_view.cpp49
-rw-r--r--platform/default/headless_view.cpp37
-rw-r--r--platform/default/image.cpp8
-rw-r--r--platform/default/log_stderr.cpp25
-rw-r--r--platform/default/png_reader.cpp4
-rw-r--r--platform/default/sqlite3.cpp9
-rw-r--r--platform/default/sqlite_cache.cpp4
-rw-r--r--platform/ios/MGLMapView.mm93
-rw-r--r--platform/ios/resources/Compass@3x.pngbin0 -> 2540 bytes
-rw-r--r--platform/ios/resources/mapbox@3x.pngbin0 -> 14888 bytes
-rw-r--r--src/mbgl/geometry/geometry.hpp77
-rw-r--r--src/mbgl/geometry/glyph_atlas.cpp56
-rw-r--r--src/mbgl/geometry/glyph_atlas.hpp36
-rw-r--r--src/mbgl/geometry/line_atlas.cpp3
-rw-r--r--src/mbgl/geometry/sprite_atlas.cpp118
-rw-r--r--src/mbgl/map/annotation.cpp190
-rw-r--r--src/mbgl/map/environment.cpp136
-rw-r--r--src/mbgl/map/environment.hpp62
-rw-r--r--src/mbgl/map/geometry_tile.cpp17
-rw-r--r--src/mbgl/map/geometry_tile.hpp57
-rw-r--r--src/mbgl/map/live_tile.cpp56
-rw-r--r--src/mbgl/map/live_tile.hpp53
-rw-r--r--src/mbgl/map/live_tile_data.cpp66
-rw-r--r--src/mbgl/map/live_tile_data.hpp31
-rw-r--r--src/mbgl/map/map.cpp296
-rw-r--r--src/mbgl/map/raster_tile_data.cpp9
-rw-r--r--src/mbgl/map/raster_tile_data.hpp8
-rw-r--r--src/mbgl/map/source.cpp80
-rw-r--r--src/mbgl/map/source.hpp27
-rw-r--r--src/mbgl/map/sprite.cpp37
-rw-r--r--src/mbgl/map/sprite.hpp9
-rw-r--r--src/mbgl/map/tile_data.cpp20
-rw-r--r--src/mbgl/map/tile_data.hpp13
-rw-r--r--src/mbgl/map/tile_parser.cpp58
-rw-r--r--src/mbgl/map/tile_parser.hpp22
-rw-r--r--src/mbgl/map/vector_tile.cpp280
-rw-r--r--src/mbgl/map/vector_tile.hpp109
-rw-r--r--src/mbgl/map/vector_tile_data.cpp34
-rw-r--r--src/mbgl/map/vector_tile_data.hpp17
-rw-r--r--src/mbgl/map/view.cpp21
-rw-r--r--src/mbgl/platform/log.cpp62
-rw-r--r--src/mbgl/renderer/bucket.hpp3
-rw-r--r--src/mbgl/renderer/debug_bucket.cpp3
-rw-r--r--src/mbgl/renderer/debug_bucket.hpp5
-rw-r--r--src/mbgl/renderer/fill_bucket.cpp44
-rw-r--r--src/mbgl/renderer/fill_bucket.hpp11
-rw-r--r--src/mbgl/renderer/line_bucket.cpp36
-rw-r--r--src/mbgl/renderer/line_bucket.hpp11
-rw-r--r--src/mbgl/renderer/painter.cpp80
-rw-r--r--src/mbgl/renderer/painter.hpp18
-rw-r--r--src/mbgl/renderer/painter_fill.cpp4
-rw-r--r--src/mbgl/renderer/painter_line.cpp4
-rw-r--r--src/mbgl/renderer/painter_raster.cpp8
-rw-r--r--src/mbgl/renderer/painter_symbol.cpp4
-rw-r--r--src/mbgl/renderer/raster_bucket.cpp3
-rw-r--r--src/mbgl/renderer/raster_bucket.hpp5
-rw-r--r--src/mbgl/renderer/symbol_bucket.cpp70
-rw-r--r--src/mbgl/renderer/symbol_bucket.hpp43
-rw-r--r--src/mbgl/shader/dot_shader.cpp5
-rw-r--r--src/mbgl/shader/gaussian_shader.cpp7
-rw-r--r--src/mbgl/shader/icon_shader.cpp5
-rw-r--r--src/mbgl/shader/line_shader.cpp5
-rw-r--r--src/mbgl/shader/linejoin_shader.cpp5
-rw-r--r--src/mbgl/shader/linepattern_shader.cpp5
-rw-r--r--src/mbgl/shader/linesdf_shader.cpp5
-rw-r--r--src/mbgl/shader/outline_shader.cpp5
-rw-r--r--src/mbgl/shader/pattern_shader.cpp5
-rw-r--r--src/mbgl/shader/plain_shader.cpp5
-rw-r--r--src/mbgl/shader/raster_shader.cpp7
-rw-r--r--src/mbgl/shader/sdf_shader.cpp5
-rw-r--r--src/mbgl/shader/shader.cpp18
-rw-r--r--src/mbgl/shader/shader.hpp1
-rw-r--r--src/mbgl/storage/default_file_source.cpp62
-rw-r--r--src/mbgl/storage/request.cpp105
-rw-r--r--src/mbgl/style/filter_expression.cpp3
-rw-r--r--src/mbgl/style/style_bucket.hpp8
-rw-r--r--src/mbgl/style/style_layer.hpp2
-rw-r--r--src/mbgl/style/style_parser.cpp37
-rw-r--r--src/mbgl/style/types.hpp4
-rw-r--r--src/mbgl/style/value.cpp27
-rw-r--r--src/mbgl/style/value.hpp2
-rw-r--r--src/mbgl/text/glyph_store.cpp30
-rw-r--r--src/mbgl/text/glyph_store.hpp16
-rw-r--r--src/mbgl/util/clip_ids.cpp3
-rw-r--r--src/mbgl/util/compression.cpp (renamed from platform/default/compression.cpp)2
-rw-r--r--src/mbgl/util/compression.hpp (renamed from platform/default/compression.hpp)0
-rw-r--r--src/mbgl/util/constants.cpp2
-rw-r--r--src/mbgl/util/mapbox.cpp30
-rw-r--r--src/mbgl/util/mapbox.hpp2
-rw-r--r--src/mbgl/util/raster.cpp3
-rw-r--r--src/mbgl/util/scaling.cpp111
-rw-r--r--src/mbgl/util/scaling.hpp23
-rw-r--r--src/mbgl/util/token.hpp8
-rw-r--r--src/mbgl/util/uv.cpp55
-rw-r--r--src/mbgl/util/uv_detail.hpp35
m---------styles0
-rw-r--r--test/fixtures/fixture_log.cpp96
-rw-r--r--test/fixtures/fixture_log.hpp52
-rw-r--r--test/fixtures/fixture_log_observer.cpp68
-rw-r--r--test/fixtures/fixture_log_observer.hpp47
-rw-r--r--test/fixtures/sprites/atlas_reference.binbin0 -> 8137 bytes
-rw-r--r--test/fixtures/sprites/atlas_reference.pngbin0 -> 9109 bytes
-rw-r--r--test/fixtures/sprites/bright.binbin0 -> 68824 bytes
-rw-r--r--test/fixtures/sprites/convert_sprite.js19
-rw-r--r--test/fixtures/util.cpp4
-rw-r--r--test/headless/headless.cpp5
-rw-r--r--test/miscellaneous/bilinear.cpp53
-rw-r--r--test/miscellaneous/mapbox.cpp69
-rw-r--r--test/miscellaneous/style_parser.cpp12
-rw-r--r--test/storage/cache_response.cpp5
-rw-r--r--test/storage/cache_revalidate.cpp17
-rw-r--r--test/storage/directory_reading.cpp7
-rw-r--r--test/storage/file_reading.cpp17
-rw-r--r--test/storage/http_cancel.cpp14
-rw-r--r--test/storage/http_coalescing.cpp4
-rw-r--r--test/storage/http_environment.cpp54
-rw-r--r--test/storage/http_error.cpp18
-rw-r--r--test/storage/http_header_parsing.cpp10
-rw-r--r--test/storage/http_load.cpp6
-rw-r--r--test/storage/http_noloop.cpp5
-rw-r--r--test/storage/http_other_loop.cpp5
-rw-r--r--test/storage/http_reading.cpp17
-rwxr-xr-xtest/storage/server.js7
-rw-r--r--test/test.gyp16
186 files changed, 3270 insertions, 1969 deletions
diff --git a/.clang-format b/.clang-format
index 86d562e74e..ff0f39e73b 100644
--- a/.clang-format
+++ b/.clang-format
@@ -10,7 +10,7 @@ AllowShortFunctionsOnASingleLine: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
AlwaysBreakTemplateDeclarations: true
NamespaceIndentation: None
-PointerBindsToType: false
+PointerBindsToType: true
SpacesInParentheses: false
BreakBeforeBraces: Attach
ColumnLimit: 100
diff --git a/.gitignore b/.gitignore
index 3ee0136c6c..d8959d4f3b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,14 +4,18 @@
*.actual.png
*.diff.png
*.pyc
+/android/debug
+/android/sdk
/mason_packages
/config/*.gypi
/build
/macosx/build
/linux/build
/ios/build
+/ios/app/build
/test/build
/test/node_modules
+/test/fixtures/*/*_actual.*
/include/mbgl/shader/shaders.hpp
/src/shader/shaders_gl.cpp
/src/shader/shaders_gles2.cpp
diff --git a/Makefile b/Makefile
index 82870d3ef9..33135ae96e 100644
--- a/Makefile
+++ b/Makefile
@@ -2,12 +2,13 @@ BUILDTYPE ?= Release
PYTHON ?= python
PREFIX ?= /usr/local
ANDROID_ABI ?= arm-v7
-JOBS ?= 1
ifeq ($(shell uname -s), Darwin)
HOST ?= osx
+JOBS ?= $(shell sysctl -n hw.ncpu)
endif
HOST ?= linux
+JOBS ?= 1
all: mbgl
@@ -65,7 +66,7 @@ xtest-proj: Xcode/test
open ./build/osx/test/test.xcodeproj
xtest: Xcode/test
- xcodebuild -project ./build/osx/test/test.xcodeproj -configuration $(BUILDTYPE) -target test -jobs `sysctl -n hw.ncpu`
+ xcodebuild -project ./build/osx/test/test.xcodeproj -configuration $(BUILDTYPE) -target test -jobs $(JOBS)
xtest-%: xtest
./scripts/run_tests.sh "build/osx/Build/Products/$(BUILDTYPE)/test" --gtest_filter=$*
@@ -94,7 +95,7 @@ xosx-proj: Xcode/osx
open ./build/osx/macosx/mapboxgl-app.xcodeproj
xosx: Xcode/osx
- xcodebuild -project ./build/osx/macosx/mapboxgl-app.xcodeproj -configuration $(BUILDTYPE) -target osxapp -jobs `sysctl -n hw.ncpu`
+ xcodebuild -project ./build/osx/macosx/mapboxgl-app.xcodeproj -configuration $(BUILDTYPE) -target osxapp -jobs $(JOBS)
run-xosx: xosx
"build/osx/Build/Products/$(BUILDTYPE)/Mapbox GL.app/Contents/MacOS/Mapbox GL"
@@ -115,10 +116,10 @@ ios-proj: Xcode/ios
open ./build/ios/ios/app/mapboxgl-app.xcodeproj
ios: Xcode/ios
- xcodebuild -sdk iphoneos ARCHS="arm64 armv7 armv7s" PROVISIONING_PROFILE="2b532944-bf3d-4bf4-aa6c-a81676984ae8" -project ./build/ios/ios/app/mapboxgl-app.xcodeproj -configuration Release -target iosapp -jobs `sysctl -n hw.ncpu`
+ xcodebuild -sdk iphoneos ARCHS="arm64 armv7 armv7s" PROVISIONING_PROFILE="2b532944-bf3d-4bf4-aa6c-a81676984ae8" -project ./build/ios/ios/app/mapboxgl-app.xcodeproj -configuration Release -target iosapp -jobs $(JOBS)
isim: Xcode/ios
- xcodebuild -sdk iphonesimulator ARCHS="x86_64 i386" -project ./build/ios/ios/app/mapboxgl-app.xcodeproj -configuration Debug -target iosapp -jobs `sysctl -n hw.ncpu`
+ xcodebuild -sdk iphonesimulator ARCHS="x86_64 i386" -project ./build/ios/ios/app/mapboxgl-app.xcodeproj -configuration Debug -target iosapp -jobs $(JOBS)
# Legacy name
iproj: ios-proj
@@ -173,16 +174,35 @@ android-lib: android-lib-$(ANDROID_ABI)
# Builds the selected/default Android library
android: android-lib-$(ANDROID_ABI)
- cd android/java && ./gradlew --parallel-threads=$(JOBS) build
+ cd android/java && ./gradlew --parallel-threads=$(JOBS) assemble$(BUILDTYPE)
# Builds all android architectures.
android-all: $(ANDROID_ABIS)
- cd android/java && ./gradlew --parallel-threads=$(JOBS) build
+ cd android/java && ./gradlew --parallel-threads=$(JOBS) assemble$(BUILDTYPE)
# Full build of GL Core for each architecture, build the Android Library, publish to Maven Central
android-deploy: $(ANDROID_ABIS)
cd android/java/MapboxGLAndroidSDK && chmod ugo+x deploy.sh && ./deploy.sh
+android-project: android-lib
+
+##### Render builds ############################################################
+
+.PRECIOUS: Makefile/render
+Makefile/render: bin/render.gyp config/$(HOST).gypi
+ deps/run_gyp bin/render.gyp $(CONFIG_$(HOST)) $(LIBS_$(HOST)) --generator-output=./build/$(HOST) -f make
+
+render: Makefile/render
+ $(MAKE) -C build/$(HOST) BUILDTYPE=$(BUILDTYPE) mbgl-render
+
+.PRECIOUS: Xcode/render
+Xcode/render: bin/render.gyp config/osx.gypi styles/styles
+ deps/run_gyp bin/render.gyp $(CONFIG_osx) $(LIBS_osx) --generator-output=./build/osx -f xcode
+
+.PHONY: xrender-proj
+xrender-proj: Xcode/render
+ open ./build/osx/bin/render.xcodeproj
+
##### Maintenace operations ####################################################
diff --git a/README.md b/README.md
index 05a9d41c0b..09759d84ef 100644
--- a/README.md
+++ b/README.md
@@ -46,7 +46,24 @@ Target OS X: 10.9+
## iOS
-If you merely want to install the library for iOS and try it out as an Objective-C consumer, run `./scripts/package_ios.sh`. It requires the Boost headers to be installed, so use [Homebrew](http://brew.sh/) to install them (`brew install boost`). The packaging script will produce the statically-linked `libMapboxGL.a`, `MapboxGL.bundle` for resources, and a `Headers` folder.
+If you merely want to install the library for iOS and try it out as an Objective-C consumer:
+
+0. Use [Homebrew](http://brew.sh/) to install Boost headers: `brew install boost`.
+1. Run `./scripts/package_ios.sh`. The packaging script will produce the statically-linked `libMapboxGL.a`, `MapboxGL.bundle` for resources, and a `Headers` folder.
+2. Copy the contents of `build/ios/pkg/static` into your project. It should happen automatically, but ensure that:
+ - `Headers` is in your *Header Search Paths* (`HEADER_SEARCH_PATHS`) build setting.
+ - `MapboxGL.bundle` is in your target's *Copy Bundle Resources* build phase.
+ - `libMapboxGL.a` is in your target's *Link Binary With Libraries* build phase.
+3. Add the following Cocoa framework dependencies to your target's *Link Binary With Libraries* build phase:
+ - `GLKit.framework`
+ - `ImageIO.framework`
+ - `MobileCoreServices.framework`
+ - `SystemConfiguration.framework`
+ - `libc++.dylib`
+ - `libsqlite3.dylib`
+ - `libz.dylib`
+4. [Set the Mapbox API access token](#mapbox-api-access-tokens).
+5. `#import "MGLMapView.h"`
If you want to build from source and/or contribute to development of the project, run `make iproj`, which will create and open an Xcode project which can build the entire library from source as well as an Objective-C test app.
@@ -86,7 +103,7 @@ Finally, install Boost. If you're running Ubuntu 12.04 or older, you need to ins
Otherwise, you can just install
- sudo apt-get install libboost-dev
+ sudo apt-get install libboost-dev libboost-program-options-dev
Once you're done installing the build dependencies, you can get started by running
diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp
index 37c790a426..9cde5d385d 100644
--- a/android/cpp/jni.cpp
+++ b/android/cpp/jni.cpp
@@ -15,7 +15,6 @@
#include <mbgl/android/jni.hpp>
#include <mbgl/android/native_map_view.hpp>
#include <mbgl/map/map.hpp>
-#include <mbgl/platform/android/log_android.hpp>
#include <mbgl/platform/event.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/storage/network_status.hpp>
@@ -291,18 +290,11 @@ void JNICALL nativeRun(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
nativeMapView->getMap().run();
}
-void JNICALL nativeRerender(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeRerender");
- assert(nativeMapViewPtr != 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().rerender();
-}
-
void JNICALL nativeUpdate(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeUpdate");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().update();
+ nativeMapView->getMap().triggerUpdate();
}
void JNICALL nativeTerminate(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
@@ -312,32 +304,6 @@ void JNICALL nativeTerminate(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
nativeMapView->getMap().terminate();
}
-jboolean JNICALL nativeNeedsSwap(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeNeedsSwap");
- assert(nativeMapViewPtr != 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- return nativeMapView->getMap().needsSwap();
-}
-
-void JNICALL nativeSwapped(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeSwapped");
- assert(nativeMapViewPtr != 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().swapped();
-}
-
-void JNICALL nativeResize(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jint width, jint height,
- jfloat ratio) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeResize");
- assert(nativeMapViewPtr != 0);
- assert(width >= 0);
- assert(height >= 0);
- assert(width <= UINT16_MAX);
- assert(height <= UINT16_MAX);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().resize(width, height, ratio);
-}
-
void JNICALL nativeResize(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jint width, jint height,
jfloat ratio, jint fbWidth, jint fbHeight) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeResize");
@@ -351,7 +317,7 @@ void JNICALL nativeResize(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jint
assert(fbWidth <= UINT16_MAX);
assert(fbHeight <= UINT16_MAX);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().resize(width, height, ratio, fbWidth, fbHeight);
+ nativeMapView->resize(width, height, ratio, fbWidth, fbHeight);
}
void JNICALL nativeRemoveClass(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jstring clazz) {
@@ -839,8 +805,6 @@ jobject JNICALL nativeLatLngForPixel(JNIEnv *env, jobject obj, jlong nativeMapVi
extern "C" {
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
- mbgl::Log::Set<mbgl::AndroidLogBackend>();
-
mbgl::Log::Debug(mbgl::Event::JNI, "JNI_OnLoad");
JNIEnv *env = nullptr;
@@ -1014,7 +978,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
// NOTE: if you get java.lang.UnsatisfiedLinkError you likely forgot to set the size of the
// array correctly (too large)
- std::array<JNINativeMethod, 67> methods = {{ // Can remove the extra brace in C++14
+ std::array<JNINativeMethod, 63> methods = {{ // Can remove the extra brace in C++14
{"nativeCreate", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J",
reinterpret_cast<void *>(&nativeCreate)},
{"nativeDestroy", "(J)V", reinterpret_cast<void *>(&nativeDestroy)},
@@ -1030,15 +994,8 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
{"nativePause", "(J)V", reinterpret_cast<void *>(&nativePause)},
{"nativeResume", "(J)V", reinterpret_cast<void *>(&nativeResume)},
{"nativeRun", "(J)V", reinterpret_cast<void *>(&nativeRun)},
- {"nativeRerender", "(J)V", reinterpret_cast<void *>(&nativeRerender)},
{"nativeUpdate", "(J)V", reinterpret_cast<void *>(&nativeUpdate)},
{"nativeTerminate", "(J)V", reinterpret_cast<void *>(&nativeTerminate)},
- {"nativeNeedsSwap", "(J)Z", reinterpret_cast<void *>(&nativeNeedsSwap)},
- {"nativeSwapped", "(J)V", reinterpret_cast<void *>(&nativeSwapped)},
- {"nativeResize", "(JIIF)V",
- reinterpret_cast<void *>(
- static_cast<void JNICALL (*)(JNIEnv *, jobject, jlong, jint, jint, jfloat)>(
- &nativeResize))},
{"nativeResize", "(JIIFII)V",
reinterpret_cast<void *>(static_cast<void JNICALL (
*)(JNIEnv *, jobject, jlong, jint, jint, jfloat, jint, jint)>(&nativeResize))},
diff --git a/android/cpp/native_map_view.cpp b/android/cpp/native_map_view.cpp
index cd3445e24e..39a777bff2 100644
--- a/android/cpp/native_map_view.cpp
+++ b/android/cpp/native_map_view.cpp
@@ -16,6 +16,9 @@
#include <mbgl/platform/gl.hpp>
#include <mbgl/util/std.hpp>
+
+pthread_once_t loadGLExtensions = PTHREAD_ONCE_INIT;
+
namespace mbgl {
namespace android {
@@ -118,16 +121,17 @@ void NativeMapView::deactivate() {
}
}
-void NativeMapView::swap() {
- mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::swap");
+void NativeMapView::invalidate() {
+ mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::invalidate");
+
+ if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) {
+ map.render();
- if (map.needsSwap() && (display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) {
if (!eglSwapBuffers(display, surface)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d",
eglGetError());
throw new std::runtime_error("eglSwapBuffers() failed");
}
- map.swapped();
updateFps();
} else {
mbgl::Log::Info(mbgl::Event::Android, "Not swapping as we are not ready");
@@ -296,6 +300,8 @@ void NativeMapView::terminateContext() {
context = EGL_NO_CONTEXT;
}
+void loadExtensions();
+
void NativeMapView::createSurface(ANativeWindow *window_) {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::createSurface");
@@ -337,7 +343,7 @@ void NativeMapView::createSurface(ANativeWindow *window_) {
}
log_gl_string(GL_EXTENSIONS, "Extensions");
- loadExtensions();
+ pthread_once(&loadGLExtensions, loadExtensions);
if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
@@ -553,7 +559,6 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint numConfi
// Sort the configs to find the best one
configList.sort();
- usingDepth24 = std::get<1>(configList.front()) == Format24Depth8Stencil;
bool isConformant = !std::get<2>(configList.front());
bool isCaveat = std::get<3>(configList.front());
int configNum = std::get<4>(configList.front());
@@ -588,7 +593,7 @@ void NativeMapView::start() {
map.start(true);
}
-void NativeMapView::loadExtensions() {
+void loadExtensions() {
const GLubyte *str = glGetString(GL_EXTENSIONS);
if (str == nullptr) {
mbgl::Log::Error(mbgl::Event::OpenGL, "glGetString(GL_EXTENSIONS) returned error %d",
@@ -620,12 +625,7 @@ void NativeMapView::loadExtensions() {
}
if (extensions.find("GL_OES_depth24") != std::string::npos) {
- mbgl::Log::Info(mbgl::Event::OpenGL, "Using GL_OES_depth24.");
- if (usingDepth24) {
- gl::isDepth24Supported = true;
- } else {
- mbgl::Log::Info(mbgl::Event::OpenGL, "Preferring 16 bit depth.");
- }
+ gl::isDepth24Supported = true;
}
if (extensions.find("GL_KHR_debug") != std::string::npos) {
@@ -823,5 +823,10 @@ void NativeMapView::updateFps() {
}
env = nullptr;
}
+
+void NativeMapView::resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight) {
+ View::resize(width, height, ratio, fbWidth, fbHeight);
+}
+
}
}
diff --git a/android/java/MapboxGLAndroidSDK/build.gradle b/android/java/MapboxGLAndroidSDK/build.gradle
index 192bc2bd7a..901aa6d9d7 100644
--- a/android/java/MapboxGLAndroidSDK/build.gradle
+++ b/android/java/MapboxGLAndroidSDK/build.gradle
@@ -3,8 +3,8 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.1.0'
- classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.10.1'
+ classpath 'com.android.tools.build:gradle:1.1.3'
+ classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.0'
}
}
@@ -29,8 +29,8 @@ repositories {
dependencies {
compile 'commons-validator:commons-validator:1.4.1'
- compile 'com.android.support:support-annotations:21.0.3'
- compile 'com.android.support:support-v4:21.0.3'
+ compile 'com.android.support:support-annotations:22.0.0'
+ compile 'com.android.support:support-v4:22.0.0'
compile 'com.squareup.okhttp:okhttp:2.2.0'
}
diff --git a/android/java/MapboxGLAndroidSDK/gradle.properties b/android/java/MapboxGLAndroidSDK/gradle.properties
index ed8f9fb5ad..79e2dadca7 100644
--- a/android/java/MapboxGLAndroidSDK/gradle.properties
+++ b/android/java/MapboxGLAndroidSDK/gradle.properties
@@ -13,9 +13,9 @@ POM_DEVELOPER_ID=mapbox
POM_DEVELOPER_NAME=Mapbox
ANDROID_MIN_SDK=8
-ANDROID_BUILD_TARGET_SDK_VERSION=21
-ANDROID_BUILD_TOOLS_VERSION=21.1.2
-ANDROID_BUILD_SDK_VERSION=21
+ANDROID_BUILD_TARGET_SDK_VERSION=22
+ANDROID_BUILD_TOOLS_VERSION=22.0.0
+ANDROID_BUILD_SDK_VERSION=22
POM_NAME=Mapbox GL Android SDK
POM_ARTIFACT_ID=mapbox-gl-android-sdk
diff --git a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/NativeMapView.java b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/NativeMapView.java
index ffb439737e..186b391a84 100644
--- a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/NativeMapView.java
+++ b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/NativeMapView.java
@@ -93,52 +93,10 @@ class NativeMapView {
nativeRun(mNativeMapViewPtr);
}
- public void rerender() {
- nativeRerender(mNativeMapViewPtr);
- }
-
public void update() {
nativeUpdate(mNativeMapViewPtr);
}
- public void terminate() {
- nativeTerminate(mNativeMapViewPtr);
- }
-
- public boolean needsSwap() {
- return nativeNeedsSwap(mNativeMapViewPtr);
- }
-
- public void swapped() {
- nativeSwapped(mNativeMapViewPtr);
- }
-
- public void resize(int width, int height) {
- resize(width, height, 1.0f);
- }
-
- public void resize(int width, int height, float ratio) {
- if (width < 0) {
- throw new IllegalArgumentException("width cannot be negative.");
- }
-
- if (height < 0) {
- throw new IllegalArgumentException("height cannot be negative.");
- }
-
- if (width > 65535) {
- throw new IllegalArgumentException(
- "width cannot be greater than 65535.");
- }
-
- if (height > 65535) {
- throw new IllegalArgumentException(
- "height cannot be greater than 65535.");
- }
-
- nativeResize(mNativeMapViewPtr, width, height, ratio);
- }
-
public void resize(int width, int height, float ratio, int fbWidth,
int fbHeight) {
if (width < 0) {
@@ -466,19 +424,10 @@ class NativeMapView {
private native void nativeRun(long nativeMapViewPtr);
- private native void nativeRerender(long nativeMapViewPtr);
-
private native void nativeUpdate(long nativeMapViewPtr);
private native void nativeTerminate(long nativeMapViewPtr);
- private native boolean nativeNeedsSwap(long nativeMapViewPtr);
-
- private native void nativeSwapped(long nativeMapViewPtr);
-
- private native void nativeResize(long nativeMapViewPtr, int width,
- int height, float ratio);
-
private native void nativeResize(long nativeMapViewPtr, int width,
int height, float ratio, int fbWidth, int fbHeight);
diff --git a/android/java/MapboxGLAndroidSDKTestApp/build.gradle b/android/java/MapboxGLAndroidSDKTestApp/build.gradle
index 1aac498870..7cdd4fc916 100644
--- a/android/java/MapboxGLAndroidSDKTestApp/build.gradle
+++ b/android/java/MapboxGLAndroidSDKTestApp/build.gradle
@@ -3,8 +3,8 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.1.0'
- classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.10.1'
+ classpath 'com.android.tools.build:gradle:1.1.3'
+ classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.0'
}
}
@@ -30,8 +30,8 @@ gradle.projectsEvaluated {
}
android {
- compileSdkVersion 21
- buildToolsVersion "21.1.2"
+ compileSdkVersion 22
+ buildToolsVersion "22.0.0"
repositories {
mavenCentral()
@@ -40,7 +40,7 @@ android {
defaultConfig {
applicationId "com.mapbox.mapboxgl.testapp"
minSdkVersion 8
- targetSdkVersion 21
+ targetSdkVersion 22
versionCode 4
versionName "0.1.3"
}
@@ -74,9 +74,9 @@ dependencies {
compile(project(':MapboxGLAndroidSDK')) {
transitive = true
}
- compile 'com.android.support:support-annotations:21.0.0'
- compile 'com.android.support:support-v4:21.0.3'
- compile 'com.android.support:appcompat-v7:21.0.3'
+ compile 'com.android.support:support-annotations:22.0.0'
+ compile 'com.android.support:support-v4:22.0.0'
+ compile 'com.android.support:appcompat-v7:22.0.0'
compile 'com.mapzen.android:lost:1.0.0'
}
diff --git a/android/scripts/common.sh b/android/scripts/common.sh
index 48b441ba59..0d610909bc 100755
--- a/android/scripts/common.sh
+++ b/android/scripts/common.sh
@@ -4,16 +4,37 @@ set -e
set -o pipefail
NAME=$1
-export JOBS=`nproc`
+
+case `uname -s` in
+ 'Darwin') export JOBS=`sysctl -n hw.ncpu` ;;
+ 'Linux') export JOBS=`nproc` ;;
+ *) export JOBS=1 ;;
+esac
+
export CC=clang CXX=clang++
export MASON_PLATFORM=android
-mkdir ./android/java/MapboxGLAndroidSDKTestApp/src/main/res/raw/
+mkdir -p ./android/java/MapboxGLAndroidSDKTestApp/src/main/res/raw/
echo "${MAPBOX_ACCESS_TOKEN}" >> ./android/java/MapboxGLAndroidSDKTestApp/src/main/res/raw/token.txt
make android -j$JOBS BUILDTYPE=$BUILDTYPE JOBS=$JOBS
-aws s3 cp ./build/android-${ANDROID_ABI}/${BUILDTYPE}/lib.target/libmapbox-gl.so s3://mapbox/mapbox-gl-native/android/build/${NAME}/libmapbox-gl.so
-aws s3 cp ./android/java/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug.apk s3://mapbox/mapbox-gl-native/android/build/${NAME}/MapboxGLAndroidSDKTestApp-debug.apk
-aws s3 cp ./android/java/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-release-unsigned.apk s3://mapbox/mapbox-gl-native/android/build/${NAME}/MapboxGLAndroidSDKTestApp-release-unsigned.apk
+S3_PREFIX=s3://mapbox/mapbox-gl-native/android/build/${NAME}
+APK_OUTPUTS=./android/java/MapboxGLAndroidSDKTestApp/build/outputs/apk
+
+# Upload the shared object.
+aws s3 cp \
+ ./build/android-${ANDROID_ABI}/${BUILDTYPE}/lib.target/libmapbox-gl.so \
+ ${S3_PREFIX}/libmapbox-gl.so
+
+# Upload either the debug or the release build
+if [ ${BUILDTYPE} == "Debug" ] ; then
+ aws s3 cp \
+ ${APK_OUTPUTS}/MapboxGLAndroidSDKTestApp-debug.apk \
+ ${S3_PREFIX}/MapboxGLAndroidSDKTestApp-debug.apk
+elif [ ${BUILDTYPE} == "Release" ] ; then
+ aws s3 cp \
+ ${APK_OUTPUTS}/MapboxGLAndroidSDKTestApp-release-unsigned.apk \
+ ${S3_PREFIX}/MapboxGLAndroidSDKTestApp-release-unsigned.apk
+fi
diff --git a/android/scripts/run-build.sh b/android/scripts/run-build.sh
index 43d58622e9..9fa15a02bf 100755
--- a/android/scripts/run-build.sh
+++ b/android/scripts/run-build.sh
@@ -6,7 +6,7 @@ set -o pipefail
cwd=$(pwd)
region="us-east-1"
-region_ami="ami-f0ed9098"
+region_ami="ami-022e0c6a"
sleep=10
instance_name="android-gl-build-$TRAVIS_REPO_SLUG-$TRAVIS_JOB_NUMBER"
echo $ami_name
diff --git a/bin/render.cpp b/bin/render.cpp
index c791152d1d..01f6929092 100644
--- a/bin/render.cpp
+++ b/bin/render.cpp
@@ -3,22 +3,20 @@
#include <mbgl/util/std.hpp>
#include <mbgl/util/io.hpp>
-#include <rapidjson/document.h>
-#include <rapidjson/writer.h>
-#include <rapidjson/stringbuffer.h>
-
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/headless_display.hpp>
+#include <mbgl/platform/log.hpp>
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/storage/default/sqlite_cache.hpp>
-#if __APPLE__
-#include <mbgl/platform/darwin/log_nslog.hpp>
-#else
-#include <mbgl/platform/default/log_stderr.hpp>
+#pragma GCC diagnostic push
+#ifndef __clang__
+#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
+#pragma GCC diagnostic ignored "-Wshadow"
#endif
-
#include <boost/program_options.hpp>
+#pragma GCC diagnostic pop
+
namespace po = boost::program_options;
#include <cassert>
@@ -32,8 +30,8 @@ int main(int argc, char *argv[]) {
double zoom = 0;
double bearing = 0;
- int width = 256;
- int height = 256;
+ int width = 512;
+ int height = 512;
double pixelRatio = 1.0;
std::string output = "out.png";
std::string cache_file = "cache.sqlite";
@@ -68,13 +66,6 @@ int main(int argc, char *argv[]) {
using namespace mbgl;
-
-#if __APPLE__
- Log::Set<NSLogBackend>();
-#else
- Log::Set<StderrLogBackend>();
-#endif
-
mbgl::SQLiteCache cache(cache_file);
mbgl::DefaultFileSource fileSource(&cache);
@@ -99,17 +90,14 @@ int main(int argc, char *argv[]) {
map.setClasses(classes);
view.resize(width, height, pixelRatio);
- map.resize(width, height, pixelRatio);
- map.setLatLonZoom(LatLng(lat, lon), zoom);
+ map.setLatLngZoom({ lat, lon }, zoom);
map.setBearing(bearing);
- std::unique_ptr<uint32_t[]> pixels;
-
// Run the loop. It will terminate when we don't have any further listeners.
map.run();
// Get the data from the GPU.
- pixels = view.readPixels();
+ auto pixels = view.readPixels();
const unsigned int w = width * pixelRatio;
const unsigned int h = height * pixelRatio;
diff --git a/configure b/configure
index 378adcf292..1d68f5e54b 100755
--- a/configure
+++ b/configure
@@ -37,7 +37,7 @@ case ${MASON_PLATFORM} in
LIBZIP_VERSION=0.11.2
;;
*)
- GLFW_VERSION=e1ae9af5
+ GLFW_VERSION=3.1
SQLITE_VERSION=3.8.8.1
LIBPNG_VERSION=1.6.16
LIBJPEG_VERSION=v9a
@@ -50,6 +50,12 @@ case ${MASON_PLATFORM} in
;;
esac
+if [ ${MASON_PLATFORM} == 'linux' ] ; then
+ MESA_VERSION=10.4.3
+ mason install mesa ${MESA_VERSION}
+ export PKG_CONFIG_PATH=`mason prefix mesa ${MESA_VERSION}`/lib/pkgconfig
+fi
+
function abort { >&2 echo -e "\033[1m\033[31m$1\033[0m"; exit 1; }
if [ -z ${CONFIG_FILE} ]; then
@@ -121,6 +127,14 @@ if [ ! -z ${GLFW_VERSION} ]; then
CONFIG+=" 'glfw3_ldflags%': $(quote_flags $(mason ldflags glfw ${GLFW_VERSION})),"$LN
fi
+if [ ${MASON_PLATFORM} == 'linux' ]; then
+ CONFIG+=" 'opengl_cflags%': $(quote_flags $(pkg-config gl x11 --cflags)),"$LN
+ CONFIG+=" 'opengl_ldflags%': $(quote_flags $(pkg-config gl x11 --libs)),"$LN
+else
+ CONFIG+=" 'opengl_cflags%': $(quote_flags),"$LN
+ CONFIG+=" 'opengl_ldflags%': $(quote_flags),"$LN
+fi
+
if [ ! -z ${LIBPNG_VERSION} ]; then
mason install libpng ${LIBPNG_VERSION}
CONFIG+=" 'png_static_libs%': $(quote_flags $(mason static_libs libpng ${LIBPNG_VERSION})),"$LN
diff --git a/docker/linux/Dockerfile b/docker/linux/Dockerfile
new file mode 100644
index 0000000000..0d23fe1e79
--- /dev/null
+++ b/docker/linux/Dockerfile
@@ -0,0 +1,19 @@
+FROM ubuntu:12.04
+
+RUN apt-get update -y && \
+ apt-get install -y build-essential git-core python-pip python-software-properties software-properties-common && \
+ rm -rf /var/lib/apt/lists/*
+
+RUN add-apt-repository --yes ppa:ubuntu-toolchain-r/test && \
+ add-apt-repository --yes ppa:boost-latest/ppa && \
+ apt-get update -y && \
+ apt-get -y install gcc-4.8 g++-4.8 curl zlib1g-dev automake gdb libtool xutils-dev make cmake pkg-config python-pip libboost1.55-dev libcurl4-openssl-dev libpng-dev libsqlite3-dev mesa-utils libxi-dev x11proto-randr-dev x11proto-xext-dev libxrandr-dev x11proto-xf86vidmode-dev libxxf86vm-dev libxcursor-dev libxinerama-dev xvfb llvm-3.4 && \
+ pip install awscli
+
+RUN apt-get install -y imagemagick
+
+RUN useradd -ms /bin/bash mapbox
+
+USER mapbox
+ENV HOME /home/mapbox
+WORKDIR /home/mapbox
diff --git a/docker/linux/run-clang.sh b/docker/linux/run-clang.sh
new file mode 100644
index 0000000000..cb9f6f417f
--- /dev/null
+++ b/docker/linux/run-clang.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+docker build \
+ -t mapbox/gl-native:linux \
+ docker/linux
+
+docker run \
+ -i \
+ -e "CXX=clang++" \
+ -v `pwd`:/home/mapbox/build \
+ -t mapbox/gl-native:linux \
+ build/docker/linux/test.sh
diff --git a/docker/linux/run-gcc.sh b/docker/linux/run-gcc.sh
new file mode 100755
index 0000000000..093771f1a4
--- /dev/null
+++ b/docker/linux/run-gcc.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+docker build \
+ -t mapbox/gl-native:linux \
+ docker/linux
+
+docker run \
+ -i \
+ -e "CXX=g++" \
+ -v `pwd`:/home/mapbox/build \
+ -t mapbox/gl-native:linux \
+ build/docker/linux/test.sh
diff --git a/docker/linux/test.sh b/docker/linux/test.sh
new file mode 100755
index 0000000000..9ef9bf51f5
--- /dev/null
+++ b/docker/linux/test.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+export TRAVIS_OS_NAME=linux
+
+cd build
+
+source ./scripts/local_mason.sh
+mason install mesa 10.4.3
+export LD_LIBRARY_PATH=`mason prefix mesa 10.4.3`/lib
+
+export DISPLAY=:99.0
+Xvfb :99 -ac -screen 0 1024x768x24 &
+
+if [[ ${CXX} == "g++" ]]; then
+ export CXX="g++-4.8"
+ export CC="gcc-4.8"
+fi
+
+source ./scripts/install_node.sh
+
+make linux -j`nproc`
+make test-* -j`nproc`
+./scripts/compare_images.sh
diff --git a/gyp/cache-sqlite.gypi b/gyp/cache-sqlite.gypi
index 53af8dab59..a9d21924c2 100644
--- a/gyp/cache-sqlite.gypi
+++ b/gyp/cache-sqlite.gypi
@@ -10,12 +10,11 @@
'../platform/default/sqlite_cache.cpp',
'../platform/default/sqlite3.hpp',
'../platform/default/sqlite3.cpp',
- '../platform/default/compression.hpp',
- '../platform/default/compression.cpp',
],
'include_dirs': [
'../include',
+ '../src',
],
'variables': {
diff --git a/gyp/common.gypi b/gyp/common.gypi
index 7cf13fff6e..01f1451dd0 100644
--- a/gyp/common.gypi
+++ b/gyp/common.gypi
@@ -7,7 +7,6 @@
'conditions': [
['OS=="mac"', {
'xcode_settings': {
- 'MACOSX_DEPLOYMENT_TARGET': '10.9',
'CLANG_CXX_LIBRARY': 'libc++',
'CLANG_CXX_LANGUAGE_STANDARD': 'c++11',
'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
diff --git a/gyp/core.gypi b/gyp/core.gypi
index 9a3b07d0bb..246419371c 100644
--- a/gyp/core.gypi
+++ b/gyp/core.gypi
@@ -29,14 +29,17 @@
'variables': {
'cflags_cc': [
'<@(uv_cflags)',
+ '<@(opengl_cflags)',
'<@(boost_cflags)',
],
'cflags': [
'<@(uv_cflags)',
+ '<@(opengl_cflags)',
'-fPIC'
],
'ldflags': [
'<@(uv_ldflags)',
+ '<@(opengl_ldflags)',
],
'libraries': [
'<@(uv_static_libs)',
diff --git a/gyp/headless-glx.gypi b/gyp/headless-glx.gypi
index 06062be75f..5b9f3e136f 100644
--- a/gyp/headless-glx.gypi
+++ b/gyp/headless-glx.gypi
@@ -13,6 +13,12 @@
'include_dirs': [
'../include',
],
+
+ 'cflags_cc': [ '<@(opengl_cflags)' ],
+
+ 'link_settings': {
+ 'libraries': [ '<@(opengl_ldflags)' ],
+ },
},
],
}
diff --git a/gyp/http-curl.gypi b/gyp/http-curl.gypi
index bfb2054d55..c97ad370b5 100644
--- a/gyp/http-curl.gypi
+++ b/gyp/http-curl.gypi
@@ -14,6 +14,25 @@
'../include',
],
+ 'variables': {
+ 'cflags_cc': [
+ '<@(uv_cflags)',
+ '<@(curl_cflags)',
+ '<@(boost_cflags)',
+ ],
+ 'ldflags': [
+ '<@(uv_ldflags)',
+ '<@(curl_ldflags)',
+ ],
+ 'libraries': [
+ '<@(uv_static_libs)',
+ '<@(curl_static_libs)',
+ ],
+ 'defines': [
+ '-DMBGL_HTTP_CURL'
+ ],
+ },
+
'conditions': [
['host == "android"', {
'variables': {
@@ -32,19 +51,17 @@
}],
],
- 'variables': {
- 'cflags_cc': [
- '<@(uv_cflags)',
- '<@(curl_cflags)',
- '<@(boost_cflags)',
- ],
- 'ldflags': [
- '<@(uv_ldflags)',
- '<@(curl_ldflags)',
- ],
- 'libraries': [
- '<@(uv_static_libs)',
- '<@(curl_static_libs)',
+ 'direct_dependent_settings': {
+ 'conditions': [
+ ['OS == "mac"', {
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [ '<@(defines)' ],
+ 'OTHER_CPLUSPLUSFLAGS': [ '<@(defines)' ],
+ }
+ }, {
+ 'cflags': [ '<@(defines)' ],
+ 'cflags_cc': [ '<@(defines)' ],
+ }]
],
},
diff --git a/gyp/http-nsurl.gypi b/gyp/http-nsurl.gypi
index 4205f59d81..5a079fdeeb 100644
--- a/gyp/http-nsurl.gypi
+++ b/gyp/http-nsurl.gypi
@@ -25,6 +25,9 @@
'libraries': [
'<@(uv_static_libs)',
],
+ 'defines': [
+ '-DMBGL_HTTP_NSURL'
+ ],
},
'xcode_settings': {
@@ -32,6 +35,13 @@
'CLANG_ENABLE_OBJC_ARC': 'NO',
},
+ 'direct_dependent_settings': {
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [ '<@(defines)' ],
+ 'OTHER_CPLUSPLUSFLAGS': [ '<@(defines)' ],
+ }
+ },
+
'link_settings': {
'libraries': [ '<@(libraries)' ],
'xcode_settings': {
diff --git a/gyp/platform-osx.gypi b/gyp/platform-osx.gypi
index 011f26e836..39e18bb839 100644
--- a/gyp/platform-osx.gypi
+++ b/gyp/platform-osx.gypi
@@ -29,6 +29,8 @@
'-framework Foundation',
'-framework ImageIO',
'-framework CoreServices',
+ '-framework OpenGL',
+ '-framework ApplicationServices',
],
},
diff --git a/include/mbgl/android/native_map_view.hpp b/include/mbgl/android/native_map_view.hpp
index bfe544c2b0..62446bf15a 100644
--- a/include/mbgl/android/native_map_view.hpp
+++ b/include/mbgl/android/native_map_view.hpp
@@ -22,10 +22,9 @@ public:
void activate() override;
void deactivate() override;
-
- void swap() override;
-
void notify() override;
+ void invalidate() override;
+
void notifyMapChange(mbgl::MapChange change, std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero()) override;
mbgl::Map &getMap();
@@ -49,11 +48,11 @@ public:
void enableFps(bool enable);
void updateFps();
+ void resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight);
+
private:
EGLConfig chooseConfig(const EGLConfig configs[], EGLint numConfigs);
- void loadExtensions();
-
bool inEmulator();
private:
@@ -78,8 +77,6 @@ private:
bool firstTime = false;
- bool usingDepth24 = false;
-
bool fpsEnabled = false;
double fps = 0.0;
};
diff --git a/include/mbgl/map/annotation.hpp b/include/mbgl/map/annotation.hpp
new file mode 100644
index 0000000000..e88d98b5c6
--- /dev/null
+++ b/include/mbgl/map/annotation.hpp
@@ -0,0 +1,72 @@
+#ifndef MBGL_MAP_ANNOTATIONS
+#define MBGL_MAP_ANNOTATIONS
+
+#include <mbgl/map/tile.hpp>
+#include <mbgl/map/live_tile.hpp>
+#include <mbgl/util/geo.hpp>
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/std.hpp>
+#include <mbgl/util/vec.hpp>
+
+#include <string>
+#include <vector>
+#include <map>
+#include <mutex>
+#include <memory>
+
+namespace mbgl {
+
+class Annotation;
+class Map;
+
+typedef std::vector<LatLng> AnnotationSegment;
+
+enum class AnnotationType : uint8_t {
+ Point,
+ Shape
+};
+
+class AnnotationManager : private util::noncopyable {
+public:
+ AnnotationManager();
+
+ void setDefaultPointAnnotationSymbol(std::string& symbol) { defaultPointAnnotationSymbol = symbol; }
+ std::pair<std::vector<Tile::ID>, std::vector<uint32_t>> addPointAnnotations(std::vector<LatLng>, std::vector<std::string>& symbols, const Map&);
+ std::vector<Tile::ID> removeAnnotations(std::vector<uint32_t>);
+ std::vector<uint32_t> getAnnotationsInBounds(LatLngBounds, const Map&) const;
+ LatLngBounds getBoundsForAnnotations(std::vector<uint32_t>) const;
+
+ const std::unique_ptr<LiveTile>& getTile(Tile::ID const& id);
+
+private:
+ uint32_t nextID() { return nextID_++; }
+ static vec2<double> projectPoint(LatLng& point);
+
+private:
+ std::mutex mtx;
+ std::string defaultPointAnnotationSymbol;
+ std::map<uint32_t, std::unique_ptr<Annotation>> annotations;
+ std::map<Tile::ID, std::pair<std::vector<uint32_t>, std::unique_ptr<LiveTile>>> annotationTiles;
+ std::unique_ptr<LiveTile> nullTile;
+ uint32_t nextID_ = 0;
+};
+
+class Annotation : private util::noncopyable {
+ friend class AnnotationManager;
+public:
+ Annotation(AnnotationType, std::vector<AnnotationSegment>);
+
+private:
+ LatLng getPoint() const;
+ LatLngBounds getBounds() const { return bounds; }
+
+private:
+ const AnnotationType type = AnnotationType::Point;
+ const std::vector<AnnotationSegment> geometry;
+ std::map<Tile::ID, std::vector<std::weak_ptr<const LiveTileFeature>>> tileFeatures;
+ LatLngBounds bounds;
+};
+
+}
+
+#endif
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index 844970bb8f..86c89f769d 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -36,8 +36,12 @@ class View;
class GlyphAtlas;
class SpriteAtlas;
class LineAtlas;
+class Environment;
+class AnnotationManager;
class Map : private util::noncopyable {
+ friend class View;
+
public:
explicit Map(View&, FileSource&);
~Map();
@@ -62,22 +66,21 @@ public:
// frame is completely rendered.
void run();
- // Triggers a lazy rerender: only performs a render when the map is not clean.
- void rerender();
+ // Triggers a synchronous or asynchronous render.
+ void renderSync();
- // Forces a map update: always triggers a rerender.
- void update();
+ // Unconditionally performs a render with the current map state. May only be called from the Map
+ // thread.
+ void render();
- // Releases resources immediately
- void terminate();
+ // Notifies the Map thread that the state has changed and an update might be necessary.
+ void triggerUpdate();
- // Controls buffer swapping.
- bool needsSwap();
- void swapped();
+ // Triggers a render. Can be called from any thread.
+ void triggerRender();
- // Size
- void resize(uint16_t width, uint16_t height, float ratio = 1);
- void resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight);
+ // Releases resources immediately
+ void terminate();
// Styling
void addClass(const std::string&);
@@ -98,7 +101,7 @@ public:
// Position
void moveBy(double dx, double dy, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero());
void setLatLng(LatLng latLng, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero());
- inline const LatLng getLatLng() const { return state.getLatLng(); }
+ LatLng getLatLng() const;
void startPanning();
void stopPanning();
void resetPosition();
@@ -138,6 +141,15 @@ public:
inline const vec2<double> pixelForLatLng(const LatLng latLng) const { return state.pixelForLatLng(latLng); }
inline const LatLng latLngForPixel(const vec2<double> pixel) const { return state.latLngForPixel(pixel); }
+ // Annotations
+ void setDefaultPointAnnotationSymbol(std::string&);
+ uint32_t addPointAnnotation(LatLng, std::string& symbol);
+ std::vector<uint32_t> addPointAnnotations(std::vector<LatLng>, std::vector<std::string>& symbols);
+ void removeAnnotation(uint32_t);
+ void removeAnnotations(std::vector<uint32_t>);
+ std::vector<uint32_t> getAnnotationsInBounds(LatLngBounds) const;
+ LatLngBounds getBoundsForAnnotations(std::vector<uint32_t>) const;
+
// Debug
void setDebug(bool value);
void toggleDebug();
@@ -145,8 +157,13 @@ public:
inline const TransformState &getState() const { return state; }
inline std::chrono::steady_clock::time_point getTime() const { return animationTime; }
+ inline AnnotationManager& getAnnotationManager() const { return *annotationManager; }
private:
+ // This may only be called by the View object.
+ void resize(uint16_t width, uint16_t height, float ratio = 1);
+ void resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight);
+
util::ptr<Sprite> getSprite();
uv::worker& getWorker();
@@ -164,8 +181,7 @@ private:
// the stylesheet.
void prepare();
- // Unconditionally performs a render with the current map state.
- void render();
+ void updateAnnotationTiles(std::vector<Tile::ID>&);
enum class Mode : uint8_t {
None, // we're not doing any processing
@@ -175,13 +191,14 @@ private:
Mode mode = Mode::None;
-public: // TODO: make private again
- std::unique_ptr<uv::loop> loop;
+ const std::unique_ptr<Environment> env;
+ View &view;
private:
std::unique_ptr<uv::worker> workers;
std::thread thread;
std::unique_ptr<uv::async> asyncTerminate;
+ std::unique_ptr<uv::async> asyncUpdate;
std::unique_ptr<uv::async> asyncRender;
bool terminating = false;
@@ -192,28 +209,14 @@ private:
std::mutex mutexPause;
std::condition_variable condPause;
- // If cleared, the next time the render thread attempts to render the map, it will *actually*
- // render the map.
- std::atomic_flag isClean = ATOMIC_FLAG_INIT;
-
- // If this flag is cleared, the current back buffer is ready for being swapped with the front
- // buffer (i.e. it has rendered data).
- std::atomic_flag isSwapped = ATOMIC_FLAG_INIT;
-
- // This is cleared once the current front buffer has been presented and the back buffer is
- // ready for rendering.
- std::atomic_flag isRendered = ATOMIC_FLAG_INIT;
+ // Used to signal that rendering completed.
+ bool rendered = false;
+ std::condition_variable condRendered;
+ std::mutex mutexRendered;
// Stores whether the map thread has been stopped already.
std::atomic_bool isStopped;
- View &view;
-
-#ifdef DEBUG
- const std::thread::id mainThread;
- std::thread::id mapThread;
-#endif
-
Transform transform;
TransformState state;
@@ -226,8 +229,8 @@ private:
util::ptr<Sprite> sprite;
const std::unique_ptr<LineAtlas> lineAtlas;
util::ptr<TexturePool> texturePool;
-
const std::unique_ptr<Painter> painter;
+ util::ptr<AnnotationManager> annotationManager;
std::string styleURL;
std::string styleJSON = "";
diff --git a/include/mbgl/map/view.hpp b/include/mbgl/map/view.hpp
index b94b8c0b93..1ee9d300c5 100644
--- a/include/mbgl/map/view.hpp
+++ b/include/mbgl/map/view.hpp
@@ -22,13 +22,7 @@ enum MapChange : uint8_t {
class View {
public:
- virtual void initialize(Map *map_) {
- map = map_;
- }
-
- // Called from the render (=GL) thread. Signals that the context should
- // swap the front and the back buffer.
- virtual void swap() = 0;
+ virtual void initialize(Map *map_);
// Called from the render thread. Makes the GL context active in the current
// thread. This is typically just called once at the beginning of the
@@ -41,10 +35,20 @@ public:
virtual void notify() = 0;
+ // Called from the render thread. The implementation must trigger a rerender.
+ // (i.e. map->renderSync() or map->renderAsync() must be called as a result of this)
+ virtual void invalidate() = 0;
+
// Notifies a watcher of map x/y/scale/rotation changes.
// Must only be called from the same thread that caused the change.
// Must not be called from the render thread.
- virtual void notifyMapChange(MapChange change, std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero()) = 0;
+ virtual void notifyMapChange(
+ MapChange change,
+ std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero());
+
+protected:
+ // Resizes the view
+ void resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight);
protected:
mbgl::Map *map = nullptr;
diff --git a/include/mbgl/platform/android/log_android.hpp b/include/mbgl/platform/android/log_android.hpp
deleted file mode 100644
index 94d90a9b36..0000000000
--- a/include/mbgl/platform/android/log_android.hpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef MBGL_PLATFORM_ANDROID_LOG_ANDROID
-#define MBGL_PLATFORM_ANDROID_LOG_ANDROID
-
-#include <mbgl/platform/log.hpp>
-
-namespace mbgl {
-
-class AndroidLogBackend : public LogBackend {
-private:
- int severityToPriority(EventSeverity severity);
-
-public:
- inline ~AndroidLogBackend() = default;
-
- void record(EventSeverity severity, Event event, const std::string &msg);
- void record(EventSeverity severity, Event event, const char* format, ...);
- void record(EventSeverity severity, Event event, int64_t code);
- void record(EventSeverity severity, Event event, int64_t code, const std::string &msg);
-};
-
-
-}
-
-#endif
diff --git a/include/mbgl/platform/darwin/log_nslog.hpp b/include/mbgl/platform/darwin/log_nslog.hpp
deleted file mode 100644
index d40f963036..0000000000
--- a/include/mbgl/platform/darwin/log_nslog.hpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef MBGL_COMMON_NSLOG_LOG
-#define MBGL_COMMON_NSLOG_LOG
-
-#include <mbgl/platform/log.hpp>
-
-namespace mbgl {
-
-class NSLogBackend : public LogBackend {
-public:
- inline ~NSLogBackend() = default;
-
- void record(EventSeverity severity, Event event, const std::string &msg);
- void record(EventSeverity severity, Event event, const char* format, ...);
- void record(EventSeverity severity, Event event, int64_t code);
- void record(EventSeverity severity, Event event, int64_t code, const std::string &msg);
-};
-
-
-}
-
-#endif
diff --git a/include/mbgl/platform/default/glfw_view.hpp b/include/mbgl/platform/default/glfw_view.hpp
index 7156d4ff1f..234568e4b7 100644
--- a/include/mbgl/platform/default/glfw_view.hpp
+++ b/include/mbgl/platform/default/glfw_view.hpp
@@ -13,20 +13,17 @@ public:
GLFWView(bool fullscreen = false);
~GLFWView();
- void initialize(mbgl::Map *map);
- void swap();
- void activate();
- void deactivate();
- void notify();
- void notifyMapChange(mbgl::MapChange change, std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero());
-
- static void key(GLFWwindow *window, int key, int scancode, int action, int mods);
- static void scroll(GLFWwindow *window, double xoffset, double yoffset);
- static void resize(GLFWwindow *window, int width, int height);
- static void mouseClick(GLFWwindow *window, int button, int action, int modifiers);
- static void mouseMove(GLFWwindow *window, double x, double y);
-
- static void eventloop(void *arg);
+ void initialize(mbgl::Map *map) override;
+ void activate() override;
+ void deactivate() override;
+ void notify() override;
+ void invalidate() override;
+
+ static void onKey(GLFWwindow *window, int key, int scancode, int action, int mods);
+ static void onScroll(GLFWwindow *window, double xoffset, double yoffset);
+ static void onResize(GLFWwindow *window, int width, int height);
+ static void onMouseClick(GLFWwindow *window, int button, int action, int modifiers);
+ static void onMouseMove(GLFWwindow *window, double x, double y);
int run();
void fps();
diff --git a/include/mbgl/platform/default/headless_view.hpp b/include/mbgl/platform/default/headless_view.hpp
index ba318c2b41..5ba6709a4f 100644
--- a/include/mbgl/platform/default/headless_view.hpp
+++ b/include/mbgl/platform/default/headless_view.hpp
@@ -13,7 +13,7 @@ typedef long unsigned int XID;
typedef XID GLXPbuffer;
#endif
-#include <mbgl/map/view.hpp>
+#include <mbgl/mbgl.hpp>
#include <mbgl/platform/gl.hpp>
#include <memory>
@@ -34,11 +34,10 @@ public:
void resize(uint16_t width, uint16_t height, float pixelRatio);
std::unique_ptr<uint32_t[]> readPixels();
- void notify();
- void notifyMapChange(MapChange change, std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero());
- void activate();
- void deactivate();
- void swap();
+ void activate() override;
+ void deactivate() override;
+ void notify() override;
+ void invalidate() override;
private:
void clearBuffers();
@@ -60,6 +59,8 @@ private:
GLXPbuffer glxPbuffer = 0;
#endif
+ bool extensionsLoaded = false;
+
GLuint fbo = 0;
GLuint fboDepthStencil = 0;
GLuint fboColor = 0;
diff --git a/include/mbgl/platform/default/log_stderr.hpp b/include/mbgl/platform/default/log_stderr.hpp
deleted file mode 100644
index 45f76f0d1a..0000000000
--- a/include/mbgl/platform/default/log_stderr.hpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef MBGL_COMMON_STDERR_LOG
-#define MBGL_COMMON_STDERR_LOG
-
-#include <mbgl/platform/log.hpp>
-
-namespace mbgl {
-
-class StderrLogBackend : public LogBackend {
-public:
- inline ~StderrLogBackend() = default;
-
- void record(EventSeverity severity, Event event, const std::string &msg);
- void record(EventSeverity severity, Event event, const char* format, ...);
- void record(EventSeverity severity, Event event, int64_t code);
- void record(EventSeverity severity, Event event, int64_t code, const std::string &msg);
-};
-
-
-}
-
-#endif
diff --git a/include/mbgl/platform/log.hpp b/include/mbgl/platform/log.hpp
index b95895fd10..5d287eb572 100644
--- a/include/mbgl/platform/log.hpp
+++ b/include/mbgl/platform/log.hpp
@@ -10,16 +10,19 @@
namespace mbgl {
-class LogBackend {
+class Log {
public:
- virtual inline ~LogBackend() = default;
- virtual void record(EventSeverity severity, Event event, const std::string &msg) = 0;
- virtual void record(EventSeverity severity, Event event, const char* format, ...) = 0;
- virtual void record(EventSeverity severity, Event event, int64_t code) = 0;
- virtual void record(EventSeverity severity, Event event, int64_t code, const std::string &msg) = 0;
-};
+ class Observer {
+ public:
+ virtual ~Observer() = default;
+
+ // When an observer is set, this function will be called for every log
+ // message. Returning true will consume the message.
+ virtual bool onRecord(EventSeverity severity, Event event, int64_t code, const std::string &msg) = 0;
+ };
+
+ static void setObserver(std::unique_ptr<Observer> Observer);
-class Log {
private:
template <typename T, size_t N>
constexpr static bool includes(const T e, const T (&l)[N], const size_t i = 0) {
@@ -52,20 +55,20 @@ public:
if (!includes(severity, disabledEventSeverities) &&
!includes(event, disabledEvents) &&
!includes({ severity, event }, disabledEventPermutations)) {
- if (Backend) {
- Backend->record(severity, event, ::std::forward<Args>(args)...);
- }
+ record(severity, event, ::std::forward<Args>(args)...);
}
}
- template<typename T, typename ...Args>
- static inline const T &Set(Args&& ...args) {
- Backend = util::make_unique<T>(::std::forward<Args>(args)...);
- return *dynamic_cast<T *>(Backend.get());
- }
-
private:
- static std::unique_ptr<LogBackend> Backend;
+ static void record(EventSeverity severity, Event event, const std::string &msg);
+ static void record(EventSeverity severity, Event event, const char* format, ...);
+ static void record(EventSeverity severity, Event event, int64_t code);
+ static void record(EventSeverity severity, Event event, int64_t code, const std::string &msg);
+
+ // This method is the data sink that must be implemented by each platform we
+ // support. It should ideally output the error message in a human readable
+ // format to the developer.
+ static void platformRecord(EventSeverity severity, const std::string &msg);
};
}
diff --git a/include/mbgl/platform/platform.hpp b/include/mbgl/platform/platform.hpp
index ea630c0956..ac90d0d3d0 100644
--- a/include/mbgl/platform/platform.hpp
+++ b/include/mbgl/platform/platform.hpp
@@ -24,10 +24,10 @@ const std::string &applicationRoot();
const std::string &assetRoot();
// Shows an alpha image with the specified dimensions in a named window.
-void show_debug_image(std::string name, const char *data, size_t width, size_t height);
+void showDebugImage(std::string name, const char *data, size_t width, size_t height);
// Shows an alpha image with the specified dimensions in a named window.
-void show_color_debug_image(std::string name, const char *data, size_t logical_width, size_t logical_height, size_t width, size_t height);
+void showColorDebugImage(std::string name, const char *data, size_t logical_width, size_t logical_height, size_t width, size_t height);
}
}
diff --git a/include/mbgl/storage/default/request.hpp b/include/mbgl/storage/default/request.hpp
index 648585f304..00157329be 100644
--- a/include/mbgl/storage/default/request.hpp
+++ b/include/mbgl/storage/default/request.hpp
@@ -6,6 +6,8 @@
#include <mbgl/util/util.hpp>
#include <mbgl/util/noncopyable.hpp>
+#include <mutex>
+#include <thread>
#include <functional>
#include <memory>
@@ -15,13 +17,14 @@ typedef struct uv_loop_s uv_loop_t;
namespace mbgl {
class Response;
+class Environment;
class Request : private util::noncopyable {
MBGL_STORE_THREAD(tid)
public:
using Callback = std::function<void(const Response &)>;
- Request(const Resource &resource, uv_loop_t *loop, Callback callback);
+ Request(const Resource &resource, uv_loop_t *loop, const Environment &env, Callback callback);
public:
// May be called from any thread.
@@ -34,17 +37,22 @@ public:
private:
~Request();
void invoke();
- static void notifyCallback(uv_async_t *async);
- static void cancelCallback(uv_async_t *async);
+ void notifyCallback();
private:
- uv_async_t *notifyAsync = nullptr;
- uv_async_t *destructAsync = nullptr;
+ uv_async_t *async = nullptr;
+ struct Canceled;
+ std::unique_ptr<Canceled> canceled;
Callback callback;
std::shared_ptr<const Response> response;
public:
const Resource resource;
+
+ // The environment ref is used to associate requests with a particular environment. This allows
+ // us to only terminate requests associated with that environment, e.g. when the map the env
+ // belongs to is discarded.
+ const Environment &env;
};
}
diff --git a/include/mbgl/storage/default/shared_request_base.hpp b/include/mbgl/storage/default/shared_request_base.hpp
index 7745bef2b5..59e38efc2f 100644
--- a/include/mbgl/storage/default/shared_request_base.hpp
+++ b/include/mbgl/storage/default/shared_request_base.hpp
@@ -4,11 +4,13 @@
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/file_cache.hpp>
#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/storage/default/request.hpp>
#include <mbgl/util/util.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <string>
#include <set>
+#include <vector>
#include <cassert>
typedef struct uv_loop_s uv_loop_t;
@@ -34,7 +36,7 @@ public:
MBGL_VERIFY_THREAD(tid);
if (source) {
- source->notify(this, observers, std::shared_ptr<const Response>(response.release()),
+ source->notify(this, observers, std::shared_ptr<const Response>(std::move(response)),
hint);
}
}
@@ -45,20 +47,12 @@ public:
observers.insert(request);
}
- void unsubscribeAll() {
- MBGL_VERIFY_THREAD(tid);
-
- source = nullptr;
- observers.clear();
- cancel();
- }
-
void unsubscribe(Request *request) {
MBGL_VERIFY_THREAD(tid);
observers.erase(request);
- if (observers.empty()) {
+ if (abandoned()) {
// There are no observers anymore. We are initiating cancelation.
if (source) {
// First, remove this SharedRequestBase from the source.
@@ -70,6 +64,29 @@ public:
}
}
+ bool abandoned() const {
+ return observers.empty();
+ }
+
+ std::vector<Request *> removeAllInEnvironment(const Environment &env) {
+ MBGL_VERIFY_THREAD(tid);
+
+ std::vector<Request *> result;
+
+ // Removes all Requests in the supplied environment and returns a list
+ // of them.
+ util::erase_if(observers, [&](Request *req) -> bool {
+ if (&req->env == &env) {
+ result.push_back(req);
+ return true;
+ } else {
+ return false;
+ }
+ });
+
+ return result;
+ }
+
protected:
virtual ~SharedRequestBase() {
MBGL_VERIFY_THREAD(tid);
diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp
index 86e2414041..7aab54f731 100644
--- a/include/mbgl/storage/default_file_source.hpp
+++ b/include/mbgl/storage/default_file_source.hpp
@@ -20,13 +20,15 @@ class DefaultFileSource : public FileSource {
public:
DefaultFileSource(FileCache *cache, const std::string &root = "");
DefaultFileSource(FileCache *cache, uv_loop_t *loop, const std::string &root = "");
- ~DefaultFileSource();
+ ~DefaultFileSource() override;
- Request *request(const Resource &resource, uv_loop_t *loop, Callback callback);
- void cancel(Request *request);
- void request(const Resource &resource, Callback callback);
+ Request *request(const Resource &resource, uv_loop_t *loop, const Environment &env,
+ Callback callback) override;
+ void cancel(Request *request) override;
+ void request(const Resource &resource, const Environment &env, Callback callback) override;
+
+ void abort(const Environment &env) override;
- enum class CacheHint : uint8_t { Full, Refresh, No };
void notify(SharedRequestBase *sharedRequest, const std::set<Request *> &observers,
std::shared_ptr<const Response> response, FileCache::Hint hint);
@@ -39,14 +41,16 @@ private:
struct RemoveRequestAction;
struct ResultAction;
struct StopAction;
- using Action =
- mapbox::util::variant<AddRequestAction, RemoveRequestAction, ResultAction, StopAction>;
+ struct AbortAction;
+ using Action = mapbox::util::variant<AddRequestAction, RemoveRequestAction, ResultAction,
+ StopAction, AbortAction>;
using Queue = util::AsyncQueue<Action>;
void process(AddRequestAction &action);
void process(RemoveRequestAction &action);
void process(ResultAction &action);
void process(StopAction &action);
+ void process(AbortAction &action);
SharedRequestBase *find(const Resource &resource);
diff --git a/include/mbgl/storage/file_source.hpp b/include/mbgl/storage/file_source.hpp
index 8517d6e4a6..30e88c39f6 100644
--- a/include/mbgl/storage/file_source.hpp
+++ b/include/mbgl/storage/file_source.hpp
@@ -15,6 +15,7 @@ typedef struct uv_loop_s uv_loop_t;
namespace mbgl {
class Request;
+class Environment;
class FileSource : private util::noncopyable {
protected:
@@ -27,12 +28,19 @@ public:
// These can be called from any thread. The callback will be invoked in the loop.
// You can only cancel a request from the same thread it was created in.
- virtual Request *request(const Resource &resource, uv_loop_t *loop, Callback callback) = 0;
+ virtual Request *request(const Resource &resource, uv_loop_t *loop, const Environment &env,
+ Callback callback) = 0;
virtual void cancel(Request *request) = 0;
// These can be called from any thread. The callback will be invoked in an arbitrary other thread.
// You cannot cancel these requests.
- virtual void request(const Resource &resource, Callback callback) = 0;
+ virtual void request(const Resource &resource, const Environment &env, Callback callback) = 0;
+
+ // This can be called from any thread. All requests with the environment pointer env should be
+ // notified as errored. Note that this is /different/ from canceling requests; a canceled
+ // request's callback is never called, while an aborted request's callback is called with
+ // a error message.
+ virtual void abort(const Environment &env) = 0;
};
}
diff --git a/include/mbgl/util/constants.hpp b/include/mbgl/util/constants.hpp
index 069e6e41ae..9e0856b68a 100644
--- a/include/mbgl/util/constants.hpp
+++ b/include/mbgl/util/constants.hpp
@@ -2,6 +2,7 @@
#define MBGL_UTIL_CONSTANTS
#include <cmath>
+#include <string>
namespace mbgl {
@@ -15,6 +16,8 @@ extern const double M2PI;
extern const double EARTH_RADIUS_M;
extern const double LATITUDE_MAX;
+extern const std::string ANNOTATIONS_POINTS_LAYER_ID;
+
}
namespace debug {
diff --git a/include/mbgl/util/exception.hpp b/include/mbgl/util/exception.hpp
new file mode 100644
index 0000000000..0b4403270c
--- /dev/null
+++ b/include/mbgl/util/exception.hpp
@@ -0,0 +1,27 @@
+#ifndef MBGL_UTIL_EXCEPTION
+#define MBGL_UTIL_EXCEPTION
+
+#include <stdexcept>
+
+namespace mbgl {
+namespace util {
+
+struct Exception : std::runtime_error {
+ inline Exception(const char *msg) : std::runtime_error(msg) {}
+ inline Exception(const std::string &msg) : std::runtime_error(msg) {}
+};
+
+struct MisuseException : Exception {
+ inline MisuseException(const char *msg) : Exception(msg) {}
+ inline MisuseException(const std::string &msg) : Exception(msg) {}
+};
+
+struct ShaderException : Exception {
+ inline ShaderException(const char *msg) : Exception(msg) {}
+ inline ShaderException(const std::string &msg) : Exception(msg) {}
+};
+
+}
+}
+
+#endif
diff --git a/include/mbgl/util/geo.hpp b/include/mbgl/util/geo.hpp
index 1d9986bd91..b99a6e6614 100644
--- a/include/mbgl/util/geo.hpp
+++ b/include/mbgl/util/geo.hpp
@@ -19,6 +19,21 @@ struct ProjectedMeters {
: northing(n), easting(e) {}
};
+struct LatLngBounds {
+ LatLng sw = {90, 180};
+ LatLng ne = {-90, -180};
+
+ inline LatLngBounds(LatLng sw_ = {90, 180}, LatLng ne_ = {-90, -180})
+ : sw(sw_), ne(ne_) {}
+
+ inline void extend(const LatLng& point) {
+ if (point.latitude < sw.latitude) sw.latitude = point.latitude;
+ if (point.latitude > ne.latitude) ne.latitude = point.latitude;
+ if (point.longitude < sw.longitude) sw.longitude = point.longitude;
+ if (point.longitude > ne.longitude) ne.longitude = point.longitude;
+ }
+};
+
}
#endif
diff --git a/include/mbgl/util/uv.hpp b/include/mbgl/util/uv.hpp
index 85f93e78bd..6f0a916040 100644
--- a/include/mbgl/util/uv.hpp
+++ b/include/mbgl/util/uv.hpp
@@ -2,6 +2,7 @@
#define MBGL_UTIL_UV
#include <string>
+#include <memory>
typedef struct uv_handle_s uv_handle_t;
typedef struct uv_async_s uv_async_t;
@@ -20,6 +21,79 @@ class worker;
class mutex;
class cond;
+class lock {
+public:
+ lock(mutex &);
+ lock(const std::unique_ptr<mutex> &);
+ lock(const lock &) = delete;
+ lock(lock &&lock);
+ lock &operator=(const lock &lock) = delete;
+ lock &operator=(lock &&lock);
+ ~lock();
+private:
+ mutex *mtx = nullptr;
+};
+
+class readlock {
+public:
+ readlock(rwlock &);
+ readlock(const std::unique_ptr<rwlock> &);
+ readlock(const readlock &) = delete;
+ readlock(readlock &&lock);
+ readlock &operator=(const readlock &lock) = delete;
+ readlock &operator=(readlock &&lock);
+ ~readlock();
+private:
+ rwlock *mtx = nullptr;
+};
+
+class writelock {
+public:
+ writelock(rwlock &);
+ writelock(const std::unique_ptr<rwlock> &);
+ writelock(const writelock &) = delete;
+ writelock(writelock &&lock);
+ writelock &operator=(const writelock &lock) = delete;
+ writelock &operator=(writelock &&lock);
+ ~writelock();
+private:
+ rwlock *mtx = nullptr;
+};
+
+template <class T>
+class exclusive {
+public:
+ exclusive(T& val, mutex &mtx) : ptr(&val), lock(mtx) {}
+ exclusive(T *val, mutex &mtx) : ptr(val), lock(mtx) {}
+ exclusive(mutex &mtx) : lock(mtx) {}
+ exclusive(const std::unique_ptr<mutex> &mtx) : lock(mtx) {}
+ exclusive(const exclusive &) = delete;
+ exclusive(exclusive &&) = default;
+ exclusive &operator=(const exclusive &) = delete;
+ exclusive &operator=(exclusive &&) = default;
+
+ T *operator->() { return ptr; }
+ const T *operator->() const { return ptr; }
+ T *operator*() { return ptr; }
+ const T *operator*() const { return ptr; }
+ operator T&() { return *ptr; }
+ operator const T&() const { return *ptr; }
+
+ void operator<<(T& val) { operator<<(&val); }
+ void operator<<(T *val) {
+ if (ptr) {
+ throw std::runtime_error("exclusive<> was assigned before");
+ }
+ ptr = val;
+ }
+
+private:
+ T *ptr = nullptr;
+ class lock lock;
+};
+
+
+
const char *getFileRequestError(uv_fs_t *req);
template <typename T>
diff --git a/include/mbgl/util/variant.hpp b/include/mbgl/util/variant.hpp
index 411f1918d5..a3cdea63a7 100644
--- a/include/mbgl/util/variant.hpp
+++ b/include/mbgl/util/variant.hpp
@@ -251,13 +251,13 @@ struct unwrapper<recursive_wrapper<T>>
};
-template <typename F, typename V, typename...Types>
+template <typename F, typename V, typename R, typename...Types>
struct dispatcher;
-template <typename F, typename V, typename T, typename...Types>
-struct dispatcher<F, V, T, Types...>
+template <typename F, typename V, typename R, typename T, typename...Types>
+struct dispatcher<F, V, R, T, Types...>
{
- using result_type = typename detail::result_of_unary_visit<F, V>::type;
+ using result_type = R;
VARIANT_INLINE static result_type apply_const(V const& v, F f)
{
if (v.get_type_index() == sizeof...(Types))
@@ -266,7 +266,7 @@ struct dispatcher<F, V, T, Types...>
}
else
{
- return dispatcher<F, V, Types...>::apply_const(v, f);
+ return dispatcher<F, V, R, Types...>::apply_const(v, f);
}
}
@@ -278,15 +278,15 @@ struct dispatcher<F, V, T, Types...>
}
else
{
- return dispatcher<F, V, Types...>::apply(v, f);
+ return dispatcher<F, V, R, Types...>::apply(v, f);
}
}
};
-template<typename F, typename V>
-struct dispatcher<F, V>
+template<typename F, typename V, typename R>
+struct dispatcher<F, V, R>
{
- using result_type = typename detail::result_of_unary_visit<F, V>::type;
+ using result_type = R;
VARIANT_INLINE static result_type apply_const(V const&, F)
{
throw std::runtime_error(std::string("unary dispatch: FAIL ") + typeid(V).name());
@@ -299,13 +299,13 @@ struct dispatcher<F, V>
};
-template <typename F, typename V, typename T, typename...Types>
+template <typename F, typename V, typename R, typename T, typename...Types>
struct binary_dispatcher_rhs;
-template <typename F, typename V, typename T0, typename T1, typename...Types>
-struct binary_dispatcher_rhs<F, V, T0, T1, Types...>
+template <typename F, typename V, typename R, typename T0, typename T1, typename...Types>
+struct binary_dispatcher_rhs<F, V, R, T0, T1, Types...>
{
- using result_type = typename detail::result_of_binary_visit<F, V>::type;
+ using result_type = R;
VARIANT_INLINE static result_type apply_const(V const& lhs, V const& rhs, F f)
{
if (rhs.get_type_index() == sizeof...(Types)) // call binary functor
@@ -315,7 +315,7 @@ struct binary_dispatcher_rhs<F, V, T0, T1, Types...>
}
else
{
- return binary_dispatcher_rhs<F, V, T0, Types...>::apply_const(lhs, rhs, f);
+ return binary_dispatcher_rhs<F, V, R, T0, Types...>::apply_const(lhs, rhs, f);
}
}
@@ -328,16 +328,16 @@ struct binary_dispatcher_rhs<F, V, T0, T1, Types...>
}
else
{
- return binary_dispatcher_rhs<F, V, T0, Types...>::apply(lhs, rhs, f);
+ return binary_dispatcher_rhs<F, V, R, T0, Types...>::apply(lhs, rhs, f);
}
}
};
-template<typename F, typename V, typename T>
-struct binary_dispatcher_rhs<F, V, T>
+template<typename F, typename V, typename R, typename T>
+struct binary_dispatcher_rhs<F, V, R, T>
{
- using result_type = typename detail::result_of_binary_visit<F, V>::type;
+ using result_type = R;
VARIANT_INLINE static result_type apply_const(V const&, V const&, F)
{
throw std::runtime_error("binary dispatch: FAIL");
@@ -349,13 +349,13 @@ struct binary_dispatcher_rhs<F, V, T>
};
-template <typename F, typename V, typename T, typename...Types>
+template <typename F, typename V, typename R, typename T, typename...Types>
struct binary_dispatcher_lhs;
-template <typename F, typename V, typename T0, typename T1, typename...Types>
-struct binary_dispatcher_lhs<F, V, T0, T1, Types...>
+template <typename F, typename V, typename R, typename T0, typename T1, typename...Types>
+struct binary_dispatcher_lhs<F, V, R, T0, T1, Types...>
{
- using result_type = typename detail::result_of_binary_visit<F, V>::type;
+ using result_type = R;
VARIANT_INLINE static result_type apply_const(V const& lhs, V const& rhs, F f)
{
if (lhs.get_type_index() == sizeof...(Types)) // call binary functor
@@ -364,7 +364,7 @@ struct binary_dispatcher_lhs<F, V, T0, T1, Types...>
}
else
{
- return binary_dispatcher_lhs<F, V, T0, Types...>::apply_const(lhs, rhs, f);
+ return binary_dispatcher_lhs<F, V, R, T0, Types...>::apply_const(lhs, rhs, f);
}
}
@@ -376,16 +376,16 @@ struct binary_dispatcher_lhs<F, V, T0, T1, Types...>
}
else
{
- return binary_dispatcher_lhs<F, V, T0, Types...>::apply(lhs, rhs, f);
+ return binary_dispatcher_lhs<F, V, R, T0, Types...>::apply(lhs, rhs, f);
}
}
};
-template<typename F, typename V, typename T>
-struct binary_dispatcher_lhs<F, V, T>
+template<typename F, typename V, typename R, typename T>
+struct binary_dispatcher_lhs<F, V, R, T>
{
- using result_type = typename detail::result_of_binary_visit<F, V>::type;
+ using result_type = R;
VARIANT_INLINE static result_type apply_const(V const&, V const&, F)
{
throw std::runtime_error("binary dispatch: FAIL");
@@ -397,13 +397,13 @@ struct binary_dispatcher_lhs<F, V, T>
}
};
-template <typename F, typename V, typename...Types>
+template <typename F, typename V, typename R, typename...Types>
struct binary_dispatcher;
-template <typename F, typename V, typename T, typename...Types>
-struct binary_dispatcher<F, V, T, Types...>
+template <typename F, typename V, typename R, typename T, typename...Types>
+struct binary_dispatcher<F, V, R, T, Types...>
{
- using result_type = typename detail::result_of_binary_visit<F, V>::type;
+ using result_type = R;
VARIANT_INLINE static result_type apply_const(V const& v0, V const& v1, F f)
{
if (v0.get_type_index() == sizeof...(Types))
@@ -414,14 +414,14 @@ struct binary_dispatcher<F, V, T, Types...>
}
else
{
- return binary_dispatcher_rhs<F, V, T, Types...>::apply_const(v0, v1, f);
+ return binary_dispatcher_rhs<F, V, R, T, Types...>::apply_const(v0, v1, f);
}
}
else if (v1.get_type_index() == sizeof...(Types))
{
- return binary_dispatcher_lhs<F, V, T, Types...>::apply_const(v0, v1, f);
+ return binary_dispatcher_lhs<F, V, R, T, Types...>::apply_const(v0, v1, f);
}
- return binary_dispatcher<F, V, Types...>::apply_const(v0, v1, f);
+ return binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, f);
}
VARIANT_INLINE static result_type apply(V & v0, V & v1, F f)
@@ -434,21 +434,21 @@ struct binary_dispatcher<F, V, T, Types...>
}
else
{
- return binary_dispatcher_rhs<F, V, T, Types...>::apply(v0, v1, f);
+ return binary_dispatcher_rhs<F, V, R, T, Types...>::apply(v0, v1, f);
}
}
else if (v1.get_type_index() == sizeof...(Types))
{
- return binary_dispatcher_lhs<F, V, T, Types...>::apply(v0, v1, f);
+ return binary_dispatcher_lhs<F, V, R, T, Types...>::apply(v0, v1, f);
}
- return binary_dispatcher<F, V, Types...>::apply(v0, v1, f);
+ return binary_dispatcher<F, V, R, Types...>::apply(v0, v1, f);
}
};
-template<typename F, typename V>
-struct binary_dispatcher<F, V>
+template<typename F, typename V, typename R>
+struct binary_dispatcher<F, V, R>
{
- using result_type = typename detail::result_of_binary_visit<F, V>::type;
+ using result_type = R;
VARIANT_INLINE static result_type apply_const(V const&, V const&, F)
{
throw std::runtime_error("binary dispatch: FAIL");
@@ -497,24 +497,6 @@ private:
Variant const& lhs_;
};
-// operator<< helper
-template <typename Out>
-class printer
-{
-public:
- explicit printer(Out & out)
- : out_(out) {}
- printer& operator=(printer const&) = delete;
-
-// visitor
- template <typename T>
- void operator()(T const& operand) const
- {
- out_ << operand;
- }
-private:
- Out & out_;
-};
} // namespace detail
@@ -536,7 +518,6 @@ private:
public:
-
VARIANT_INLINE variant()
: type_index(sizeof...(Types) - 1)
{
@@ -620,7 +601,8 @@ public:
type_index = detail::direct_type<T, Types...>::index;
}
- template<typename T>
+ // get<T>()
+ template<typename T, typename std::enable_if<(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T& get()
{
if (type_index == detail::direct_type<T, Types...>::index)
@@ -629,11 +611,13 @@ public:
}
else
{
- throw std::runtime_error("in get()");
+ throw std::runtime_error("in get<T>()");
}
}
- template<typename T>
+ template <typename T, typename std::enable_if<
+ (detail::direct_type<T, Types...>::index != detail::invalid_value)
+ >::type* = nullptr>
VARIANT_INLINE T const& get() const
{
if (type_index == detail::direct_type<T, Types...>::index)
@@ -642,31 +626,72 @@ public:
}
else
{
- throw std::runtime_error("in get()");
+ throw std::runtime_error("in get<T>()");
+ }
+ }
+
+ // get<T>() - T stored as recursive_wrapper<T>
+ template <typename T, typename std::enable_if<
+ (detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)
+ >::type* = nullptr>
+ VARIANT_INLINE T& get()
+ {
+ if (type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index)
+ {
+ return (*reinterpret_cast<recursive_wrapper<T>*>(&data)).get();
+ }
+ else
+ {
+ throw std::runtime_error("in get<T>()");
}
}
+ template <typename T,typename std::enable_if<
+ (detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)
+ >::type* = nullptr>
+ VARIANT_INLINE T const& get() const
+ {
+ if (type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index)
+ {
+ return (*reinterpret_cast<recursive_wrapper<T> const*>(&data)).get();
+ }
+ else
+ {
+ throw std::runtime_error("in get<T>()");
+ }
+ }
VARIANT_INLINE std::size_t get_type_index() const
{
return type_index;
}
+ VARIANT_INLINE int which() const noexcept
+ {
+ return static_cast<int>(sizeof...(Types) - type_index - 1);
+ }
+
// visitor
// unary
template <typename F, typename V>
auto VARIANT_INLINE
static visit(V const& v, F f)
- -> decltype(detail::dispatcher<F, V, Types...>::apply_const(v, f))
+ -> decltype(detail::dispatcher<F, V,
+ typename detail::result_of_unary_visit<F,
+ typename detail::select_type<0, Types...>::type>::type, Types...>::apply_const(v, f))
{
- return detail::dispatcher<F, V, Types...>::apply_const(v, f);
+ using R = typename detail::result_of_unary_visit<F, typename detail::select_type<0, Types...>::type>::type;
+ return detail::dispatcher<F, V, R, Types...>::apply_const(v, f);
}
// non-const
template <typename F, typename V>
auto VARIANT_INLINE
static visit(V & v, F f)
- -> decltype(detail::dispatcher<F, V, Types...>::apply(v, f))
+ -> decltype(detail::dispatcher<F, V,
+ typename detail::result_of_unary_visit<F,
+ typename detail::select_type<0, Types...>::type>::type, Types...>::apply(v, f))
{
- return detail::dispatcher<F, V, Types...>::apply(v, f);
+ using R = typename detail::result_of_unary_visit<F, typename detail::select_type<0, Types...>::type>::type;
+ return detail::dispatcher<F, V, R, Types...>::apply(v, f);
}
// binary
@@ -674,17 +699,23 @@ public:
template <typename F, typename V>
auto VARIANT_INLINE
static binary_visit(V const& v0, V const& v1, F f)
- -> decltype(detail::binary_dispatcher<F, V, Types...>::apply_const(v0, v1, f))
+ -> decltype(detail::binary_dispatcher<F, V,
+ typename detail::result_of_binary_visit<F,
+ typename detail::select_type<0, Types...>::type>::type, Types...>::apply_const(v0, v1, f))
{
- return detail::binary_dispatcher<F, V, Types...>::apply_const(v0, v1, f);
+ using R = typename detail::result_of_binary_visit<F,typename detail::select_type<0, Types...>::type>::type;
+ return detail::binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, f);
}
// non-const
template <typename F, typename V>
auto VARIANT_INLINE
static binary_visit(V& v0, V& v1, F f)
- -> decltype(detail::binary_dispatcher<F, V, Types...>::apply(v0, v1, f))
+ -> decltype(detail::binary_dispatcher<F, V,
+ typename detail::result_of_binary_visit<F,
+ typename detail::select_type<0, Types...>::type>::type, Types...>::apply(v0, v1, f))
{
- return detail::binary_dispatcher<F, V, Types...>::apply(v0, v1, f);
+ using R = typename detail::result_of_binary_visit<F,typename detail::select_type<0, Types...>::type>::type;
+ return detail::binary_dispatcher<F, V, R, Types...>::apply(v0, v1, f);
}
~variant() noexcept
@@ -745,28 +776,18 @@ auto VARIANT_INLINE static apply_visitor(F f, V & v0, V & v1) -> decltype(V::bin
// getter interface
template<typename ResultType, typename T>
-ResultType & get(T & var)
+ResultType & get(T & var)
{
return var.template get<ResultType>();
}
template<typename ResultType, typename T>
-ResultType const& get(T const& var)
+ResultType const& get(T const& var)
{
return var.template get<ResultType>();
}
-// operator<<
-template <typename charT, typename traits, typename... Types>
-VARIANT_INLINE std::basic_ostream<charT, traits>&
-operator<< (std::basic_ostream<charT, traits>& out, variant<Types...> const& rhs)
-{
- detail::printer<std::basic_ostream<charT, traits>> visitor(out);
- apply_visitor(visitor, rhs);
- return out;
-}
-
}}
#endif // MAPBOX_UTIL_VARIANT_HPP
diff --git a/include/mbgl/util/variant_io.hpp b/include/mbgl/util/variant_io.hpp
new file mode 100644
index 0000000000..224732d97c
--- /dev/null
+++ b/include/mbgl/util/variant_io.hpp
@@ -0,0 +1,41 @@
+#ifndef MAPBOX_UTIL_VARIANT_IO_HPP
+#define MAPBOX_UTIL_VARIANT_IO_HPP
+
+#include "variant.hpp"
+
+namespace mapbox { namespace util {
+
+namespace detail {
+// operator<< helper
+template <typename Out>
+class printer
+{
+public:
+ explicit printer(Out & out)
+ : out_(out) {}
+ printer& operator=(printer const&) = delete;
+
+// visitor
+ template <typename T>
+ void operator()(T const& operand) const
+ {
+ out_ << operand;
+ }
+private:
+ Out & out_;
+};
+}
+
+// operator<<
+template <typename charT, typename traits, typename... Types>
+VARIANT_INLINE std::basic_ostream<charT, traits>&
+operator<< (std::basic_ostream<charT, traits>& out, variant<Types...> const& rhs)
+{
+ detail::printer<std::basic_ostream<charT, traits>> visitor(out);
+ apply_visitor(visitor, rhs);
+ return out;
+}
+
+}}
+
+#endif //MAPBOX_UTIL_VARIANT_IO_HPP
diff --git a/ios/app/MBXViewController.mm b/ios/app/MBXViewController.mm
index 0e0e73c4db..98b337f927 100644
--- a/ios/app/MBXViewController.mm
+++ b/ios/app/MBXViewController.mm
@@ -12,7 +12,8 @@ static NSArray *const kStyleNames = @[
@"Bright",
@"Basic",
@"Outdoors",
- @"Satellite"
+ @"Satellite",
+ @"Hybrid",
];
static NSString *const kStyleVersion = @"v7";
diff --git a/ios/app/img/Icon-60@3x.png b/ios/app/img/Icon-60@3x.png
new file mode 100644
index 0000000000..b00d479839
--- /dev/null
+++ b/ios/app/img/Icon-60@3x.png
Binary files differ
diff --git a/ios/app/img/Icon-76@3x.png b/ios/app/img/Icon-76@3x.png
new file mode 100644
index 0000000000..64edff1835
--- /dev/null
+++ b/ios/app/img/Icon-76@3x.png
Binary files differ
diff --git a/ios/app/img/Icon-Small@3x.png b/ios/app/img/Icon-Small@3x.png
new file mode 100644
index 0000000000..e16f3fa6a9
--- /dev/null
+++ b/ios/app/img/Icon-Small@3x.png
Binary files differ
diff --git a/ios/app/img/Icon-Spotlight-40@3x.png b/ios/app/img/Icon-Spotlight-40@3x.png
new file mode 100644
index 0000000000..a3789dcb78
--- /dev/null
+++ b/ios/app/img/Icon-Spotlight-40@3x.png
Binary files differ
diff --git a/ios/app/img/locateUser@3x.png b/ios/app/img/locateUser@3x.png
new file mode 100644
index 0000000000..206a5ae80f
--- /dev/null
+++ b/ios/app/img/locateUser@3x.png
Binary files differ
diff --git a/ios/app/mapboxgl-app.gyp b/ios/app/mapboxgl-app.gyp
index 7287932fc5..21af226621 100644
--- a/ios/app/mapboxgl-app.gyp
+++ b/ios/app/mapboxgl-app.gyp
@@ -33,6 +33,7 @@
'xcode_settings': {
'SDKROOT': 'iphoneos',
'SUPPORTED_PLATFORMS': 'iphonesimulator iphoneos',
+ 'IPHONEOS_DEPLOYMENT_TARGET': '7.0',
'INFOPLIST_FILE': 'app-info.plist',
'TARGETED_DEVICE_FAMILY': '1,2',
'COMBINE_HIDPI_IMAGES': 'NO', # don't merge @2x.png images into .tiff files
diff --git a/linux/main.cpp b/linux/main.cpp
index 6688905ea9..697941164a 100644
--- a/linux/main.cpp
+++ b/linux/main.cpp
@@ -1,9 +1,9 @@
#include <mbgl/mbgl.hpp>
#include <mbgl/util/uv.hpp>
+#include <mbgl/platform/log.hpp>
#include <mbgl/platform/platform.hpp>
#include <mbgl/platform/default/settings_json.hpp>
#include <mbgl/platform/default/glfw_view.hpp>
-#include <mbgl/platform/default/log_stderr.hpp>
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/storage/default/sqlite_cache.hpp>
@@ -17,7 +17,7 @@ GLFWView *view = nullptr;
void quit_handler(int) {
if (view) {
- fprintf(stderr, "waiting for quit...\n");
+ mbgl::Log::Info(mbgl::Event::Setup, "waiting for quit...");
glfwSetWindowShouldClose(view->window, true);
glfwPostEmptyEvent();
} else {
@@ -26,8 +26,6 @@ void quit_handler(int) {
}
int main(int argc, char *argv[]) {
- mbgl::Log::Set<mbgl::StderrLogBackend>();
-
int fullscreen_flag = 0;
std::string style;
diff --git a/linux/mapboxgl-app.gyp b/linux/mapboxgl-app.gyp
index afcc3a83a3..433a061e43 100644
--- a/linux/mapboxgl-app.gyp
+++ b/linux/mapboxgl-app.gyp
@@ -40,19 +40,18 @@
['OS == "mac"', {
'libraries': [ '<@(libraries)' ],
'xcode_settings': {
+ 'SDKROOT': 'macosx',
+ 'SUPPORTED_PLATFORMS':'macosx',
'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags_cc)' ],
'OTHER_LDFLAGS': [ '<@(ldflags)' ],
+ 'SDKROOT': 'macosx',
+ 'MACOSX_DEPLOYMENT_TARGET': '10.9',
}
}, {
'cflags_cc': [ '<@(cflags_cc)' ],
'libraries': [ '<@(libraries)', '<@(ldflags)' ],
}]
],
-
- 'copies': [{
- 'files': [ '../styles/styles' ],
- 'destination': '<(PRODUCT_DIR)'
- }],
},
],
}
diff --git a/macosx/main.mm b/macosx/main.mm
index f59fbda6a8..087544f7c4 100644
--- a/macosx/main.mm
+++ b/macosx/main.mm
@@ -1,6 +1,6 @@
+#include <mbgl/platform/log.hpp>
#include <mbgl/platform/platform.hpp>
#include <mbgl/platform/darwin/settings_nsuserdefaults.hpp>
-#include <mbgl/platform/darwin/log_nslog.hpp>
#include <mbgl/platform/darwin/Reachability.h>
#include <mbgl/platform/default/glfw_view.hpp>
#include <mbgl/storage/default_file_source.hpp>
@@ -101,8 +101,6 @@ const std::string &defaultCacheDatabase() {
}
int main() {
- mbgl::Log::Set<mbgl::NSLogBackend>();
-
GLFWView view;
mbgl::SQLiteCache cache(defaultCacheDatabase());
diff --git a/macosx/mapboxgl-app.gyp b/macosx/mapboxgl-app.gyp
index 37280286e5..39aef6435b 100644
--- a/macosx/mapboxgl-app.gyp
+++ b/macosx/mapboxgl-app.gyp
@@ -9,7 +9,7 @@
'product_extension': 'app',
'mac_bundle': 1,
'mac_bundle_resources': [
- 'Icon.icns',
+ 'Icon.icns'
],
'dependencies': [
diff --git a/platform/android/log_android.cpp b/platform/android/log_android.cpp
index 5e40ce33bd..e5c8cfd812 100644
--- a/platform/android/log_android.cpp
+++ b/platform/android/log_android.cpp
@@ -1,15 +1,12 @@
-#include <mbgl/platform/android/log_android.hpp>
-
-#include <iostream>
-#include <cstdarg>
-#define __STDC_FORMAT_MACROS // NDK bug workaround: https://code.google.com/p/android/issues/detail?id=72349
-#include <cinttypes>
+#include <mbgl/platform/log.hpp>
#include <android/log.h>
namespace mbgl {
-int AndroidLogBackend::severityToPriority(EventSeverity severity) {
+namespace {
+
+int severityToPriority(EventSeverity severity) {
switch(severity) {
case EventSeverity::Debug:
return ANDROID_LOG_DEBUG;
@@ -28,32 +25,10 @@ int AndroidLogBackend::severityToPriority(EventSeverity severity) {
}
}
-void AndroidLogBackend::record(EventSeverity severity, Event event, const std::string &msg) {
- __android_log_print(severityToPriority(severity), EventClass(event).c_str(), "%s", msg.c_str());
-}
-
-void AndroidLogBackend::record(EventSeverity severity, Event event, const char* format, ...) {
- va_list args;
- va_start(args, format);
-
- const int len = vsnprintf(nullptr, 0, format, args) + 1;
- char* buf = new char[len];
- vsnprintf(buf, len, format, args);
-
- va_end(args);
-
- __android_log_print(severityToPriority(severity), EventClass(event).c_str(), "%s", buf);
-
- delete buf;
- buf = nullptr;
-}
-
-void AndroidLogBackend::record(EventSeverity severity, Event event, int64_t code) {
- __android_log_print(severityToPriority(severity), EventClass(event).c_str(), "(%" PRId64 ")", code);
-}
+} // namespace
-void AndroidLogBackend::record(EventSeverity severity, Event event, int64_t code, const std::string &msg) {
- __android_log_print(severityToPriority(severity), EventClass(event).c_str(), "(%" PRId64 ") %s", code, msg.c_str());
+void Log::platformRecord(EventSeverity severity, const std::string &msg) {
+ __android_log_print(severityToPriority(severity), "mbgl", "%s", msg.c_str());
}
}
diff --git a/platform/darwin/asset_root.mm b/platform/darwin/asset_root.mm
index 375975a84b..4dc6b58dc2 100644
--- a/platform/darwin/asset_root.mm
+++ b/platform/darwin/asset_root.mm
@@ -2,13 +2,22 @@
#include <mbgl/platform/platform.hpp>
+@interface MGLApplicationRootBundleCanary : NSObject
+@end
+
+@implementation MGLApplicationRootBundleCanary
+@end
+
namespace mbgl {
namespace platform {
// Returns the path to the root folder of the application.
const std::string &assetRoot() {
static const std::string root = []() -> std::string {
- NSString *path = [[[NSBundle mainBundle] resourceURL] path];
+ NSString *path = [[NSBundle bundleForClass:[MGLApplicationRootBundleCanary class]] pathForResource:@"MapboxGL" ofType:@"bundle"];
+ if (!path) {
+ path = [[[NSBundle mainBundle] resourceURL] path];
+ }
return {[path cStringUsingEncoding : NSUTF8StringEncoding],
[path lengthOfBytesUsingEncoding:NSUTF8StringEncoding]};
}();
diff --git a/platform/darwin/http_request_nsurl.mm b/platform/darwin/http_request_nsurl.mm
index 83c010f8b8..638b5062d4 100644
--- a/platform/darwin/http_request_nsurl.mm
+++ b/platform/darwin/http_request_nsurl.mm
@@ -188,6 +188,7 @@ void HTTPRequestImpl::handleResponse() {
}
context->removeRequest(request);
+ request->ptr = nullptr;
delete request;
request = nullptr;
}
@@ -206,6 +207,8 @@ void HTTPRequestImpl::cancel() {
[task cancel];
[task release];
task = nullptr;
+ } else {
+ delete this;
}
}
diff --git a/platform/darwin/image.mm b/platform/darwin/image.mm
index 4819d2cd3c..a9044774dc 100644
--- a/platform/darwin/image.mm
+++ b/platform/darwin/image.mm
@@ -108,7 +108,6 @@ Image::Image(const std::string &source_data) {
CFRelease(data);
width = 0;
height = 0;
- img.release();
return;
}
diff --git a/platform/darwin/log_nslog.mm b/platform/darwin/log_nslog.mm
index a82d78a01f..a2e31968ab 100644
--- a/platform/darwin/log_nslog.mm
+++ b/platform/darwin/log_nslog.mm
@@ -1,41 +1,13 @@
-#include <mbgl/platform/darwin/log_nslog.hpp>
+#include <mbgl/platform/log.hpp>
#import <Foundation/Foundation.h>
-#include <cstdarg>
-
namespace mbgl {
-void NSLogBackend::record(EventSeverity severity, Event event, const std::string &msg) {
- NSString *message =
- [[NSString alloc] initWithBytes:msg.data() length:msg.size() encoding:NSUTF8StringEncoding];
- NSLog(@"[%s] %s: %@", EventSeverityClass(severity).c_str(), EventClass(event).c_str(), message);
-}
-
-void NSLogBackend::record(EventSeverity severity, Event event, const char *format, ...) {
- va_list args;
- va_start(args, format);
- const size_t len = vsnprintf(NULL, 0, format, args);
- va_end(args);
- std::unique_ptr<char[]> buffer(new char[len + 1]);
- va_start(args, format);
- vsnprintf(buffer.get(), len + 1, format, args);
- va_end(args);
- NSLog(@"[%s] %s: %s", EventSeverityClass(severity).c_str(), EventClass(event).c_str(),
- buffer.get());
-}
-
-void NSLogBackend::record(EventSeverity severity, Event event, int64_t code) {
- NSLog(@"[%s] %s: (%lld)", EventSeverityClass(severity).c_str(), EventClass(event).c_str(),
- code);
-}
-
-void NSLogBackend::record(EventSeverity severity, Event event, int64_t code,
- const std::string &msg) {
+void Log::platformRecord(EventSeverity severity, const std::string &msg) {
NSString *message =
[[NSString alloc] initWithBytes:msg.data() length:msg.size() encoding:NSUTF8StringEncoding];
- NSLog(@"[%s] %s: (%lld) %@", EventSeverityClass(severity).c_str(), EventClass(event).c_str(),
- code, message);
+ NSLog(@"[%s] %@", EventSeverityClass(severity).c_str(), message);
}
}
diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp
index 8306229d4a..601ae0ec2d 100644
--- a/platform/default/glfw_view.cpp
+++ b/platform/default/glfw_view.cpp
@@ -2,6 +2,8 @@
#include <mbgl/platform/gl.hpp>
#include <mbgl/platform/log.hpp>
+pthread_once_t loadGLExtensions = PTHREAD_ONCE_INIT;
+
GLFWView::GLFWView(bool fullscreen_) : fullscreen(fullscreen_) {
#ifdef NVIDIA
glDiscardFramebufferEXT = reinterpret_cast<PFNGLDISCARDFRAMEBUFFEREXTPROC>(glfwGetProcAddress("glDiscardFramebufferEXT"));
@@ -23,7 +25,7 @@ void GLFWView::initialize(mbgl::Map *map_) {
glfwSetErrorCallback(glfwError);
if (!glfwInit()) {
- fprintf(stderr, "Failed to initialize glfw\n");
+ mbgl::Log::Error(mbgl::Event::OpenGL, "failed to initialize glfw");
exit(1);
}
@@ -52,7 +54,7 @@ void GLFWView::initialize(mbgl::Map *map_) {
window = glfwCreateWindow(1024, 768, "Mapbox GL", monitor, NULL);
if (!window) {
glfwTerminate();
- fprintf(stderr, "Failed to initialize window\n");
+ mbgl::Log::Error(mbgl::Event::OpenGL, "failed to initialize window");
exit(1);
}
@@ -62,17 +64,17 @@ void GLFWView::initialize(mbgl::Map *map_) {
int width, height;
glfwGetWindowSize(window, &width, &height);
- resize(window, width, height);
+ onResize(window, width, height);
- glfwSetCursorPosCallback(window, mouseMove);
- glfwSetMouseButtonCallback(window, mouseClick);
- glfwSetWindowSizeCallback(window, resize);
- glfwSetFramebufferSizeCallback(window, resize);
- glfwSetScrollCallback(window, scroll);
- glfwSetKeyCallback(window, key);
+ glfwSetCursorPosCallback(window, onMouseMove);
+ glfwSetMouseButtonCallback(window, onMouseClick);
+ glfwSetWindowSizeCallback(window, onResize);
+ glfwSetFramebufferSizeCallback(window, onResize);
+ glfwSetScrollCallback(window, onScroll);
+ glfwSetKeyCallback(window, onKey);
- const std::string extensions = reinterpret_cast<const char *>(MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS)));
- {
+ pthread_once(&loadGLExtensions, [] {
+ const std::string extensions = reinterpret_cast<const char *>(MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS)));
using namespace mbgl;
if (extensions.find("GL_KHR_debug") != std::string::npos) {
@@ -152,12 +154,12 @@ void GLFWView::initialize(mbgl::Map *map_) {
// Require packed depth stencil
gl::isPackedDepthStencilSupported = true;
gl::isDepth24Supported = true;
- }
+ });
glfwMakeContextCurrent(nullptr);
}
-void GLFWView::key(GLFWwindow *window, int key, int /*scancode*/, int action, int mods) {
+void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action, int mods) {
GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
if (action == GLFW_RELEASE) {
@@ -190,7 +192,7 @@ void GLFWView::key(GLFWwindow *window, int key, int /*scancode*/, int action, in
}
}
-void GLFWView::scroll(GLFWwindow *window, double /*xOffset*/, double yOffset) {
+void GLFWView::onScroll(GLFWwindow *window, double /*xOffset*/, double yOffset) {
GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
double delta = yOffset * 40;
@@ -213,16 +215,16 @@ void GLFWView::scroll(GLFWwindow *window, double /*xOffset*/, double yOffset) {
view->map->scaleBy(scale, view->lastX, view->lastY);
}
-void GLFWView::resize(GLFWwindow *window, int width, int height ) {
+void GLFWView::onResize(GLFWwindow *window, int width, int height ) {
GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
int fbWidth, fbHeight;
glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
- view->map->resize(width, height, static_cast<float>(fbWidth) / static_cast<float>(width), fbWidth, fbHeight);
+ view->resize(width, height, static_cast<float>(fbWidth) / static_cast<float>(width), fbWidth, fbHeight);
}
-void GLFWView::mouseClick(GLFWwindow *window, int button, int action, int modifiers) {
+void GLFWView::onMouseClick(GLFWwindow *window, int button, int action, int modifiers) {
GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
if (button == GLFW_MOUSE_BUTTON_RIGHT ||
@@ -249,7 +251,7 @@ void GLFWView::mouseClick(GLFWwindow *window, int button, int action, int modifi
}
}
-void GLFWView::mouseMove(GLFWwindow *window, double x, double y) {
+void GLFWView::onMouseMove(GLFWwindow *window, double x, double y) {
GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
if (view->tracking) {
double dx = x - view->lastX;
@@ -295,16 +297,13 @@ void GLFWView::notify() {
glfwPostEmptyEvent();
}
-void GLFWView::swap() {
+void GLFWView::invalidate() {
+ assert(map);
+ map->render();
glfwSwapBuffers(window);
- map->swapped();
fps();
}
-void GLFWView::notifyMapChange(mbgl::MapChange /*change*/, std::chrono::steady_clock::duration /*delay*/) {
- // no-op
-}
-
void GLFWView::fps() {
static int frames = 0;
static double timeElapsed = 0;
@@ -313,7 +312,7 @@ void GLFWView::fps() {
double currentTime = glfwGetTime();
if (currentTime - timeElapsed >= 1) {
- fprintf(stderr, "FPS: %4.2f\n", frames / (currentTime - timeElapsed));
+ mbgl::Log::Info(mbgl::Event::OpenGL, "FPS: %4.2f", frames / (currentTime - timeElapsed));
timeElapsed = currentTime;
frames = 0;
}
diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp
index 656a774390..df85ee714c 100644
--- a/platform/default/headless_view.cpp
+++ b/platform/default/headless_view.cpp
@@ -10,6 +10,8 @@
#include <cstring>
#include <cassert>
+pthread_once_t loadGLExtensions = PTHREAD_ONCE_INIT;
+
#ifdef MBGL_USE_CGL
#include <CoreFoundation/CoreFoundation.h>
@@ -48,10 +50,18 @@ HeadlessView::HeadlessView(std::shared_ptr<HeadlessDisplay> display)
}
void HeadlessView::loadExtensions() {
+ if (extensionsLoaded) {
+ return;
+ }
+
activate();
- const char *extensionPtr = reinterpret_cast<const char *>(MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS)));
- if (extensionPtr) {
+ pthread_once(&loadGLExtensions, [] {
+ const char *extensionPtr = reinterpret_cast<const char *>(MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS)));
+
+ if (!extensionPtr) {
+ return;
+ }
const std::string extensions = extensionPtr;
#ifdef MBGL_USE_CGL
@@ -78,16 +88,22 @@ void HeadlessView::loadExtensions() {
assert(gl::IsVertexArray != nullptr);
}
#endif
- }
+ });
// HeadlessView requires packed depth stencil
gl::isPackedDepthStencilSupported = true;
gl::isDepth24Supported = true;
+ extensionsLoaded = true;
+
deactivate();
}
void HeadlessView::createContext() {
+ if (!display_) {
+ throw std::runtime_error("Display is not set");
+ }
+
#if MBGL_USE_CGL
CGLError error = CGLCreateContext(display_->pixelFormat, NULL, &glContext);
if (error != kCGLNoError) {
@@ -109,7 +125,7 @@ void HeadlessView::createContext() {
glContext = glXCreateNewContext(xDisplay, fbConfigs[0], GLX_RGBA_TYPE, None, True);
if (glContext) {
if (!glXIsDirect(xDisplay, glContext)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "Failed to create direct OpenGL Legacy context");
+ Log::Error(Event::OpenGL, "failed to create direct OpenGL Legacy context");
glXDestroyContext(xDisplay, glContext);
glContext = 0;
}
@@ -131,7 +147,7 @@ void HeadlessView::createContext() {
#endif
}
-void HeadlessView::resize(uint16_t width, uint16_t height, float pixelRatio) {
+void HeadlessView::resize(const uint16_t width, const uint16_t height, const float pixelRatio) {
clearBuffers();
width_ = width;
@@ -177,6 +193,8 @@ void HeadlessView::resize(uint16_t width, uint16_t height, float pixelRatio) {
throw std::runtime_error(error.str());
}
+ View::resize(width, height, pixelRatio, w, h);
+
deactivate();
}
@@ -246,10 +264,6 @@ void HeadlessView::notify() {
// no-op
}
-void HeadlessView::notifyMapChange(mbgl::MapChange /*change*/, std::chrono::steady_clock::duration /*delay*/) {
- // no-op
-}
-
void HeadlessView::activate() {
#if MBGL_USE_CGL
CGLError error = CGLSetCurrentContext(glContext);
@@ -280,6 +294,9 @@ void HeadlessView::deactivate() {
#endif
}
-void HeadlessView::swap() {}
+void HeadlessView::invalidate() {
+ assert(map);
+ map->render();
+}
}
diff --git a/platform/default/image.cpp b/platform/default/image.cpp
index 311aa2ed72..1a10d78ffa 100644
--- a/platform/default/image.cpp
+++ b/platform/default/image.cpp
@@ -32,14 +32,14 @@ std::string compress_png(int width, int height, void *rgba) {
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");
+ Log::Error(Event::Image, "couldn't create png_ptr");
return "";
}
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");
+ Log::Error(Event::Image, "couldn't create info_ptr");
return "";
}
@@ -87,7 +87,7 @@ Image::Image(std::string const& data)
}
catch (ImageReaderException const& ex)
{
- fprintf(stderr, "Image: %s\n", ex.what());
+ Log::Error(Event::Image, ex.what());
img.reset();
width = 0;
height = 0;
@@ -95,7 +95,7 @@ Image::Image(std::string const& data)
}
catch (...) // catch the rest
{
- fprintf(stderr, "Image: exception in constructor");
+ Log::Error(Event::Image, "exception in constructor");
img.reset();
width = 0;
height = 0;
diff --git a/platform/default/log_stderr.cpp b/platform/default/log_stderr.cpp
index 62938b6d63..f7ef341845 100644
--- a/platform/default/log_stderr.cpp
+++ b/platform/default/log_stderr.cpp
@@ -1,30 +1,11 @@
-#include <mbgl/platform/default/log_stderr.hpp>
+#include <mbgl/platform/log.hpp>
#include <iostream>
-#include <cstdarg>
namespace mbgl {
-void StderrLogBackend::record(EventSeverity severity, Event event, const std::string &msg) {
- std::cerr << "[" << severity << "] " << event << ": " << msg << std::endl;
-}
-
-void StderrLogBackend::record(EventSeverity severity, Event event, const char* format, ...) {
- std::cerr << "[" << severity << "] " << event << ": ";
- va_list args;
- va_start(args, format);
- vfprintf(stderr, format, args);
- va_end(args);
- std::cerr << std::endl;
-}
-
-void StderrLogBackend::record(EventSeverity severity, Event event, int64_t code) {
- std::cerr << "[" << severity << "] " << event << ": (" << code << ")" << std::endl;
-}
-
-void StderrLogBackend::record(EventSeverity severity, Event event, int64_t code, const std::string &msg) {
- std::cerr << "[" << severity << "] " << event << ": (" << code << ") " << msg << std::endl;
-
+void Log::platformRecord(EventSeverity severity, const std::string &msg) {
+ std::cerr << "[" << severity << "] " << msg << std::endl;
}
}
diff --git a/platform/default/png_reader.cpp b/platform/default/png_reader.cpp
index cf96ca2363..91fe007bcf 100644
--- a/platform/default/png_reader.cpp
+++ b/platform/default/png_reader.cpp
@@ -1,4 +1,5 @@
#include <mbgl/platform/default/png_reader.hpp>
+#include <mbgl/platform/log.hpp>
#include <iostream>
extern "C"
{
@@ -23,8 +24,7 @@ void user_error_fn(png_structp /*png_ptr*/, png_const_charp error_msg)
void user_warning_fn(png_structp /*png_ptr*/, png_const_charp warning_msg)
{
- fprintf(stderr, "ImageReader (PNG): %s\n", warning_msg);
-
+ Log::Warning(Event::Image, "ImageReader (PNG): %s", warning_msg);
}
template <typename T>
diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp
index 6a27314c56..19ecaba0bf 100644
--- a/platform/default/sqlite3.cpp
+++ b/platform/default/sqlite3.cpp
@@ -6,20 +6,13 @@
// Check sqlite3 library version.
const static bool sqliteVersionCheck = []() {
- if (sqlite3_libversion_number() != SQLITE_VERSION_NUMBER) {
+ if (sqlite3_libversion_number() / 1000000 != SQLITE_VERSION_NUMBER / 1000000) {
char message[96];
snprintf(message, 96,
"sqlite3 libversion mismatch: headers report %d, but library reports %d",
SQLITE_VERSION_NUMBER, sqlite3_libversion_number());
throw std::runtime_error(message);
}
- if (strcmp(sqlite3_sourceid(), SQLITE_SOURCE_ID) != 0) {
- char message[256];
- snprintf(message, 256,
- "sqlite3 sourceid mismatch: headers report \"%s\", but library reports \"%s\"",
- SQLITE_SOURCE_ID, sqlite3_sourceid());
- throw std::runtime_error(message);
- }
return true;
}();
diff --git a/platform/default/sqlite_cache.cpp b/platform/default/sqlite_cache.cpp
index ab1ee040ff..b8d47159ce 100644
--- a/platform/default/sqlite_cache.cpp
+++ b/platform/default/sqlite_cache.cpp
@@ -5,10 +5,10 @@
#include <mbgl/util/util.hpp>
#include <mbgl/util/async_queue.hpp>
#include <mbgl/util/variant.hpp>
+#include <mbgl/util/compression.hpp>
#include <mbgl/platform/log.hpp>
#include "sqlite3.hpp"
-#include "compression.hpp"
#include <uv.h>
@@ -163,7 +163,7 @@ void SQLiteCache::createDatabase() {
db->exec(sql);
} catch (mapbox::sqlite::Exception &ex) {
Log::Error(Event::Database, "Failed to create database: %s", ex.what());
- db.release();
+ db.reset();
}
}
}
diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm
index e68ba6c93f..0d530ca1cf 100644
--- a/platform/ios/MGLMapView.mm
+++ b/platform/ios/MGLMapView.mm
@@ -1,6 +1,6 @@
#import "MGLMapView.h"
-#import <mbgl/platform/darwin/log_nslog.hpp>
+#import <mbgl/platform/log.hpp>
#import <mbgl/platform/gl.hpp>
#import <GLKit/GLKit.h>
@@ -37,6 +37,7 @@ const std::string &defaultCacheDatabase() {
return path;
}
+static dispatch_once_t loadGLExtensions;
extern NSString *const MGLStyleKeyGeneric;
extern NSString *const MGLStyleKeyFill;
@@ -65,6 +66,7 @@ NSTimeInterval const MGLAnimationDuration = 0.3;
@property (nonatomic) UIPinchGestureRecognizer *pinch;
@property (nonatomic) UIRotationGestureRecognizer *rotate;
@property (nonatomic) UILongPressGestureRecognizer *quickZoom;
+@property (nonatomic) NSMutableArray *bundledStyleNames;
@property (nonatomic, readonly) NSDictionary *allowedStyleTypes;
@property (nonatomic) CGPoint centerPoint;
@property (nonatomic) CGFloat scale;
@@ -93,6 +95,8 @@ NSTimeInterval const MGLAnimationDuration = 0.3;
@implementation MGLMapView
+@synthesize bundledStyleNames=_bundledStyleNames;
+
#pragma mark - Setup & Teardown -
@dynamic debugActive;
@@ -177,17 +181,13 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (BOOL)commonInit
{
- // set logging backend
- //
- mbgl::Log::Set<mbgl::NSLogBackend>();
-
// create context
//
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if ( ! _context)
{
- mbgl::Log::Error(mbgl::Event::Setup, "Failed to create OpenGL ES context");
+ mbgl::Log::Error(mbgl::Event::Setup, "failed to create OpenGL ES context");
return NO;
}
@@ -200,7 +200,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
//
_glView = [[GLKView alloc] initWithFrame:self.bounds context:_context];
_glView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- _glView.enableSetNeedsDisplay = NO;
+ _glView.enableSetNeedsDisplay = YES;
_glView.drawableStencilFormat = GLKViewDrawableStencilFormat8;
_glView.drawableDepthFormat = GLKViewDrawableDepthFormat16;
if ([UIScreen instancesRespondToSelector:@selector(nativeScale)]) {
@@ -210,11 +210,14 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
[_glView bindDrawable];
[self addSubview:_glView];
+ _glView.contentMode = UIViewContentModeCenter;
+ [self setBackgroundColor:[UIColor clearColor]];
// load extensions
//
- const std::string extensions = (char *)glGetString(GL_EXTENSIONS);
- {
+ dispatch_once(&loadGLExtensions, ^{
+ const std::string extensions = (char *)glGetString(GL_EXTENSIONS);
+
using namespace mbgl;
if (extensions.find("GL_OES_vertex_array_object") != std::string::npos) {
@@ -231,7 +234,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
if (extensions.find("GL_OES_depth24") != std::string::npos) {
gl::isDepth24Supported = YES;
}
- }
+ });
// setup mbgl map
//
@@ -239,7 +242,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
mbglFileCache = new mbgl::SQLiteCache(defaultCacheDatabase());
mbglFileSource = new mbgl::DefaultFileSource(mbglFileCache);
mbglMap = new mbgl::Map(*mbglView, *mbglFileSource);
- mbglMap->resize(self.bounds.size.width, self.bounds.size.height, _glView.contentScaleFactor, _glView.drawableWidth, _glView.drawableHeight);
+ mbglView->resize(self.bounds.size.width, self.bounds.size.height, _glView.contentScaleFactor, _glView.drawableWidth, _glView.drawableHeight);
// Notify map object when network reachability status changes.
[[NSNotificationCenter defaultCenter] addObserver:self
@@ -487,16 +490,18 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
[super updateConstraints];
}
+// This is the delegate of the GLKView object's display call.
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
- mbglMap->resize(rect.size.width, rect.size.height, view.contentScaleFactor, view.drawableWidth, view.drawableHeight);
+ mbglView->resize(rect.size.width, rect.size.height, view.contentScaleFactor, view.drawableWidth, view.drawableHeight);
+ mbglMap->renderSync();
}
+// This gets called when the view dimension changes, e.g. because the device is being rotated.
- (void)layoutSubviews
{
- mbglMap->update();
-
[super layoutSubviews];
+ mbglMap->triggerUpdate();
}
#pragma mark - Life Cycle -
@@ -936,16 +941,39 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (NSArray *)bundledStyleNames
{
- NSString *stylesPath = [[MGLMapView resourceBundlePath] stringByAppendingString:@"/styles"];
+ if (!_bundledStyleNames) {
+ NSString *stylesPath = [[MGLMapView resourceBundlePath] stringByAppendingString:@"/styles"];
+
+ _bundledStyleNames = [NSMutableArray array];
+
+ NSArray *bundledStyleNamesWithExtensions = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:stylesPath error:nil];
+ NSString *hybridStylePrefix = @"hybrid-";
+ NSString *satelliteStylePrefix = @"satellite-";
+ for (NSString *fileName in bundledStyleNamesWithExtensions) {
+ NSString *styleName = [fileName stringByDeletingPathExtension];
+ [_bundledStyleNames addObject:styleName];
- NSArray *styleNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:stylesPath error:nil];
+ // Add satellite raster & "hybrid" (satellite raster + vector contours & labels)
+ if ([styleName hasPrefix:satelliteStylePrefix]) {
+ [_bundledStyleNames addObject:[hybridStylePrefix stringByAppendingString:[styleName substringFromIndex:[satelliteStylePrefix length]]]];
+ }
+ }
+ }
- return styleNames;
+ return [NSArray arrayWithArray:_bundledStyleNames];
}
- (void)useBundledStyleNamed:(NSString *)styleName
{
+ NSString *hybridStylePrefix = @"hybrid-";
+ BOOL isHybrid = [styleName hasPrefix:hybridStylePrefix];
+ if (isHybrid) {
+ styleName = [@"satellite-" stringByAppendingString:[styleName substringFromIndex:[hybridStylePrefix length]]];
+ }
[self setStyleURL:[NSString stringWithFormat:@"styles/%@.json", styleName]];
+ if (isHybrid) {
+ [self setStyleClasses:@[@"contours", @"labels"]];
+ }
}
- (NSArray *)getStyleOrderedLayerNames
@@ -1003,12 +1031,12 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
return returnArray;
}
-- (void)setAppliedStyleClasses:(NSArray *)appliedClasses
+- (void)setStyleClasses:(NSArray *)appliedClasses
{
- [self setAppliedStyleClasses:appliedClasses transitionDuration:0];
+ [self setStyleClasses:appliedClasses transitionDuration:0];
}
-- (void)setAppliedStyleClasses:(NSArray *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration
+- (void)setStyleClasses:(NSArray *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration
{
std::vector<std::string> newAppliedClasses;
@@ -1578,13 +1606,10 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
return resourceBundlePath;
}
-- (void)swap
+- (void)invalidate
{
- if (mbglMap->needsSwap())
- {
- [self.glView display];
- mbglMap->swapped();
- }
+ // This is run in the main/UI thread.
+ [self.glView setNeedsDisplay];
}
class MBGLView : public mbgl::View
@@ -1594,12 +1619,12 @@ class MBGLView : public mbgl::View
virtual ~MBGLView() {}
- void notify()
+ void notify() override
{
// no-op
}
- void notifyMapChange(mbgl::MapChange change, std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero())
+ void notifyMapChange(mbgl::MapChange change, std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero()) override
{
if (delay != std::chrono::steady_clock::duration::zero())
{
@@ -1621,19 +1646,23 @@ class MBGLView : public mbgl::View
}
}
- void activate()
+ void activate() override
{
[EAGLContext setCurrentContext:nativeView.context];
}
- void deactivate()
+ void deactivate() override
{
[EAGLContext setCurrentContext:nil];
}
- void swap()
+ void resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight) {
+ View::resize(width, height, ratio, fbWidth, fbHeight);
+ }
+
+ void invalidate() override
{
- [nativeView performSelectorOnMainThread:@selector(swap)
+ [nativeView performSelectorOnMainThread:@selector(invalidate)
withObject:nil
waitUntilDone:NO];
}
diff --git a/platform/ios/resources/Compass@3x.png b/platform/ios/resources/Compass@3x.png
new file mode 100644
index 0000000000..470cd3d087
--- /dev/null
+++ b/platform/ios/resources/Compass@3x.png
Binary files differ
diff --git a/platform/ios/resources/mapbox@3x.png b/platform/ios/resources/mapbox@3x.png
new file mode 100644
index 0000000000..5a2afcb17b
--- /dev/null
+++ b/platform/ios/resources/mapbox@3x.png
Binary files differ
diff --git a/src/mbgl/geometry/geometry.hpp b/src/mbgl/geometry/geometry.hpp
deleted file mode 100644
index 484d17b36d..0000000000
--- a/src/mbgl/geometry/geometry.hpp
+++ /dev/null
@@ -1,77 +0,0 @@
-#ifndef MBGL_GEOMETRY_GEOMETRY
-#define MBGL_GEOMETRY_GEOMETRY
-
-#include <mbgl/util/pbf.hpp>
-#include <mbgl/util/noncopyable.hpp>
-
-#include <cstdlib>
-
-namespace mbgl {
-
-class Geometry : private util::noncopyable {
-
-public:
- inline explicit Geometry(pbf& data);
-
- enum command : uint8_t {
- end = 0,
- move_to = 1,
- line_to = 2,
- close = 7
- };
-
- inline command next(int32_t &rx, int32_t &ry);
-
-private:
- pbf& data;
- uint8_t cmd;
- uint32_t length;
- int32_t x, y;
- int32_t ox, oy;
-};
-
-Geometry::Geometry(pbf& data_)
- : data(data_),
- cmd(1),
- length(0),
- x(0), y(0),
- ox(0), oy(0) {}
-
-Geometry::command Geometry::next(int32_t &rx, int32_t &ry) {
- if (data.data < data.end) {
- if (length == 0) {
- uint32_t cmd_length = static_cast<uint32_t>(data.varint());
- cmd = cmd_length & 0x7;
- length = cmd_length >> 3;
- }
-
- --length;
-
- if (cmd == move_to || cmd == line_to) {
- rx = (x += data.svarint());
- ry = (y += data.svarint());
-
- if (cmd == move_to) {
- ox = x;
- oy = y;
- return move_to;
- } else {
- return line_to;
- }
- } else if (cmd == close) {
- rx = ox;
- ry = oy;
- return close;
- } else {
- fprintf(stderr, "unknown command: %d\n", cmd);
- // TODO: gracefully handle geometry parse failures
- return end;
- }
- } else {
- return end;
- }
-}
-
-}
-
-#endif
diff --git a/src/mbgl/geometry/glyph_atlas.cpp b/src/mbgl/geometry/glyph_atlas.cpp
index fd429d41a3..f690004b52 100644
--- a/src/mbgl/geometry/glyph_atlas.cpp
+++ b/src/mbgl/geometry/glyph_atlas.cpp
@@ -1,7 +1,7 @@
#include <mbgl/geometry/glyph_atlas.hpp>
-#include <mbgl/map/vector_tile.hpp>
#include <mbgl/platform/gl.hpp>
+#include <mbgl/platform/log.hpp>
#include <mbgl/platform/platform.hpp>
#include <cassert>
@@ -18,26 +18,43 @@ GlyphAtlas::GlyphAtlas(uint16_t width_, uint16_t height_)
dirty(true) {
}
-Rect<uint16_t> GlyphAtlas::addGlyph(uint64_t tile_id, const std::string& face_name,
- const SDFGlyph& glyph)
+void GlyphAtlas::addGlyphs(uintptr_t tileUID,
+ const std::u32string& text,
+ const std::string& stackName,
+ const FontStack& fontStack,
+ GlyphPositions& face)
{
std::lock_guard<std::mutex> lock(mtx);
- return addGlyph_impl(tile_id, face_name, glyph);
+
+ const std::map<uint32_t, SDFGlyph>& sdfs = fontStack.getSDFs();
+
+ for (uint32_t chr : text)
+ {
+ auto sdf_it = sdfs.find(chr);
+ if (sdf_it == sdfs.end()) {
+ continue;
+ }
+
+ const SDFGlyph& sdf = sdf_it->second;
+ Rect<uint16_t> rect = addGlyph(tileUID, stackName, sdf);
+ face.emplace(chr, Glyph{rect, sdf.metrics});
+ }
}
-Rect<uint16_t> GlyphAtlas::addGlyph_impl(uint64_t tile_id, const std::string& face_name,
+Rect<uint16_t> GlyphAtlas::addGlyph(uintptr_t tileUID,
+ const std::string& stackName,
const SDFGlyph& glyph)
{
// Use constant value for now.
const uint8_t buffer = 3;
- std::map<uint32_t, GlyphValue>& face = index[face_name];
+ std::map<uint32_t, GlyphValue>& face = index[stackName];
std::map<uint32_t, GlyphValue>::iterator it = face.find(glyph.id);
// The glyph is already in this texture.
if (it != face.end()) {
GlyphValue& value = it->second;
- value.ids.insert(tile_id);
+ value.ids.insert(tileUID);
return value.rect;
}
@@ -61,14 +78,14 @@ Rect<uint16_t> GlyphAtlas::addGlyph_impl(uint64_t tile_id, const std::string& fa
Rect<uint16_t> rect = bin.allocate(pack_width, pack_height);
if (rect.w == 0) {
- fprintf(stderr, "glyph bitmap overflow");
+ Log::Error(Event::OpenGL, "glyph bitmap overflow");
return rect;
}
assert(rect.x + rect.w <= width);
assert(rect.y + rect.h <= height);
- face.emplace(glyph.id, GlyphValue { rect, tile_id });
+ face.emplace(glyph.id, GlyphValue { rect, tileUID });
// Copy the bitmap
char *target = data.get();
@@ -86,31 +103,14 @@ Rect<uint16_t> GlyphAtlas::addGlyph_impl(uint64_t tile_id, const std::string& fa
return rect;
}
-void GlyphAtlas::addGlyphs(uint64_t tileid, std::u32string const& text, std::string const& stackname, FontStack const& fontStack, GlyphPositions & face)
-{
- std::lock_guard<std::mutex> lock(mtx);
-
- std::map<uint32_t, SDFGlyph> const& sdfs = fontStack.getSDFs();
- for (uint32_t chr : text)
- {
- auto sdf_it = sdfs.find(chr);
- if (sdf_it != sdfs.end())
- {
- SDFGlyph const& sdf = sdf_it->second;
- Rect<uint16_t> rect = addGlyph_impl(tileid, stackname, sdf);
- face.emplace(chr, Glyph{rect, sdf.metrics});
- }
- }
-}
-
-void GlyphAtlas::removeGlyphs(uint64_t tile_id) {
+void GlyphAtlas::removeGlyphs(uintptr_t tileUID) {
std::lock_guard<std::mutex> lock(mtx);
for (auto& faces : index) {
std::map<uint32_t, GlyphValue>& face = faces.second;
for (auto it = face.begin(); it != face.end(); /* we advance in the body */) {
GlyphValue& value = it->second;
- value.ids.erase(tile_id);
+ value.ids.erase(tileUID);
if (!value.ids.size()) {
const Rect<uint16_t>& rect = value.rect;
diff --git a/src/mbgl/geometry/glyph_atlas.hpp b/src/mbgl/geometry/glyph_atlas.hpp
index 7b3c223fe5..a25c735a8e 100644
--- a/src/mbgl/geometry/glyph_atlas.hpp
+++ b/src/mbgl/geometry/glyph_atlas.hpp
@@ -15,32 +15,32 @@ namespace mbgl {
class GlyphAtlas : public util::noncopyable {
public:
-
-private:
- struct GlyphValue {
- GlyphValue(const Rect<uint16_t>& rect_, uint64_t id)
- : rect(rect_), ids({ id }) {}
- Rect<uint16_t> rect;
- std::set<uint64_t> ids;
- };
-
- Rect<uint16_t> addGlyph_impl(uint64_t tile_id, const std::string& face_name,
- const SDFGlyph& glyph);
-public:
GlyphAtlas(uint16_t width, uint16_t height);
- Rect<uint16_t> addGlyph(uint64_t tile_id, const std::string& face_name,
- const SDFGlyph& glyph);
- void addGlyphs(uint64_t tileid, std::u32string const& text, std::string const& stackname,
- FontStack const& fontStack, GlyphPositions & face);
- void removeGlyphs(uint64_t tile_id);
+ void addGlyphs(uintptr_t tileUID,
+ const std::u32string& text,
+ const std::string& stackName,
+ const FontStack&,
+ GlyphPositions&);
+ void removeGlyphs(uintptr_t tileUID);
+
void bind();
-public:
const uint16_t width = 0;
const uint16_t height = 0;
private:
+ struct GlyphValue {
+ GlyphValue(const Rect<uint16_t>& rect_, uintptr_t id)
+ : rect(rect_), ids({ id }) {}
+ Rect<uint16_t> rect;
+ std::set<uintptr_t> ids;
+ };
+
+ Rect<uint16_t> addGlyph(uintptr_t tileID,
+ const std::string& stackName,
+ const SDFGlyph&);
+
std::mutex mtx;
BinPack<uint16_t> bin;
std::map<std::string, std::map<uint32_t, GlyphValue>> index;
diff --git a/src/mbgl/geometry/line_atlas.cpp b/src/mbgl/geometry/line_atlas.cpp
index b396d93259..bf6944fbc0 100644
--- a/src/mbgl/geometry/line_atlas.cpp
+++ b/src/mbgl/geometry/line_atlas.cpp
@@ -1,5 +1,6 @@
#include <mbgl/geometry/line_atlas.hpp>
#include <mbgl/platform/gl.hpp>
+#include <mbgl/platform/log.hpp>
#include <mbgl/platform/platform.hpp>
#include <sstream>
@@ -46,7 +47,7 @@ LinePatternPos LineAtlas::addDash(const std::vector<float> &dasharray, bool roun
const uint8_t offset = 128;
if (nextRow + dashheight > height) {
- fprintf(stderr, "[WARNING] line atlas bitmap overflow\n");
+ Log::Warning(Event::OpenGL, "line atlas bitmap overflow");
return LinePatternPos();
}
diff --git a/src/mbgl/geometry/sprite_atlas.cpp b/src/mbgl/geometry/sprite_atlas.cpp
index 93440b0e6c..dce772f2e4 100644
--- a/src/mbgl/geometry/sprite_atlas.cpp
+++ b/src/mbgl/geometry/sprite_atlas.cpp
@@ -1,9 +1,11 @@
#include <mbgl/geometry/sprite_atlas.hpp>
#include <mbgl/platform/gl.hpp>
+#include <mbgl/platform/log.hpp>
#include <mbgl/platform/platform.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/constants.hpp>
+#include <mbgl/util/scaling.hpp>
#include <mbgl/map/sprite.hpp>
@@ -65,34 +67,6 @@ bool SpriteAtlas::resize(const float newRatio) {
return dirty;
}
-void copy_bitmap(const uint32_t *src, const int src_stride, const int src_x, const int src_y,
- uint32_t *dst, const int dst_stride, const int dst_height, const int dst_x, const int dst_y,
- const int width, const int height, const bool wrap) {
- if (wrap) {
-
- for (int y = -1; y <= height; y++) {
- int dst_y_wrapped = (y + dst_y + dst_height) % dst_height;
- int src_y_wrapped = ((y + height) % height) + src_y;
- int srcI = src_y_wrapped * src_stride + src_x;
- int dstI = dst_y_wrapped * dst_stride;
- for (int x = -1; x <= width; x++) {
- int dst_x_wrapped = (x + dst_x + dst_stride) % dst_stride;
- int src_x_wrapped = (x + width) % width;
- dst[dstI + dst_x_wrapped] = src[srcI + src_x_wrapped];
- }
- }
-
- } else {
- dst += dst_y * dst_stride + dst_x;
- src += src_y * src_stride + src_x;
- for (int y = 0; y < height; y++, src += src_stride, dst += dst_stride) {
- for (int x = 0; x < width; x++) {
- dst[x] = src[x];
- }
- }
- }
-}
-
Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(const size_t pixel_width, const size_t pixel_height) {
// Increase to next number divisible by 4, but at least 1.
// This is so we can scale down the texture coordinates and pack them
@@ -129,7 +103,7 @@ Rect<SpriteAtlas::dimension> SpriteAtlas::getImage(const std::string& name, cons
Rect<dimension> rect = allocateImage(pos.width / pos.pixelRatio, pos.height / pos.pixelRatio);
if (rect.w == 0) {
if (debug::spriteWarnings) {
- fprintf(stderr, "[WARNING] sprite atlas bitmap overflow\n");
+ Log::Warning(Event::Sprite, "sprite atlas bitmap overflow");
}
return rect;
}
@@ -175,25 +149,53 @@ void SpriteAtlas::allocate() {
void SpriteAtlas::copy(const Rect<dimension>& dst, const SpritePosition& src, const bool wrap) {
if (!sprite->raster) return;
- const uint32_t *src_img = reinterpret_cast<const uint32_t *>(sprite->raster->getData());
- if (!src_img) return;
+
+ const uint32_t *srcData = reinterpret_cast<const uint32_t *>(sprite->raster->getData());
+ if (!srcData) return;
+ const vec2<uint32_t> srcSize { sprite->raster->getWidth(), sprite->raster->getHeight() };
+ const Rect<uint32_t> srcPos { src.x, src.y, src.width, src.height };
+
allocate();
- uint32_t *dst_img = reinterpret_cast<uint32_t *>(data);
-
- copy_bitmap(
- /* source buffer */ src_img,
- /* source stride */ sprite->raster->getWidth(),
- /* source x */ src.x,
- /* source y */ src.y,
- /* dest buffer */ dst_img,
- /* dest stride */ width * pixelRatio,
- /* dest height */ height * pixelRatio,
- /* dest x */ dst.x * pixelRatio,
- /* dest y */ dst.y * pixelRatio,
- /* icon dimension */ src.width,
- /* icon dimension */ src.height,
- /* wrap padding */ wrap
- );
+ uint32_t *dstData = reinterpret_cast<uint32_t *>(data);
+ const vec2<uint32_t> dstSize { static_cast<unsigned int>(width * pixelRatio),
+ static_cast<unsigned int>(height * pixelRatio) };
+ const Rect<uint32_t> dstPos { static_cast<uint32_t>(dst.x * pixelRatio),
+ static_cast<uint32_t>(dst.y * pixelRatio),
+ static_cast<uint32_t>(dst.originalW * pixelRatio),
+ static_cast<uint32_t>(dst.originalH * pixelRatio) };
+
+ util::bilinearScale(srcData, srcSize, srcPos, dstData, dstSize, dstPos);
+
+ // Add borders around the copied image if required.
+ if (wrap) {
+ // We're copying from the same image so we don't have to scale again.
+ const uint32_t border = 1;
+ // Left border
+ if (dstPos.x >= border) {
+ util::nearestNeighborScale(
+ dstData, dstSize, { dstPos.x + dstPos.w - border - 1, dstPos.y, border, dstPos.h },
+ dstData, dstSize, { dstPos.x - border, dstPos.y, border, dstPos.h });
+ }
+ // Right border
+ util::nearestNeighborScale(dstData, dstSize, { dstPos.x, dstPos.y, border, dstPos.h },
+ dstData, dstSize,
+ { dstPos.x + dstPos.w, dstPos.y, border, dstPos.h });
+
+ // Top border
+ if (dstPos.y >= border) {
+ util::nearestNeighborScale(
+ dstData, dstSize, { dstPos.x - border, dstPos.y + dstPos.h - border - 1,
+ dstPos.w + 2 * border, border },
+ dstData, dstSize,
+ { dstPos.x - border, dstPos.y - border, dstPos.w + 2 * border, border });
+ }
+
+ // Bottom border
+ util::nearestNeighborScale(
+ dstData, dstSize, { dstPos.x - border, dstPos.y, dstPos.w + 2 * border, border },
+ dstData, dstSize,
+ { dstPos.x - border, dstPos.y + dstPos.h, dstPos.w + 2 * border, border });
+ }
dirty = true;
}
@@ -210,7 +212,7 @@ void SpriteAtlas::setSprite(util::ptr<Sprite> sprite_) {
const SpritePosition& src = sprite->getSpritePosition(name);
if (!src) {
if (debug::spriteWarnings) {
- fprintf(stderr, "[WARNING] sprite doesn't have image with name '%s'\n", name.c_str());
+ Log::Warning(Event::Sprite, "sprite doesn't have image with name '%s'", name.c_str());
}
return true;
}
@@ -220,7 +222,7 @@ void SpriteAtlas::setSprite(util::ptr<Sprite> sprite_) {
return true;
} else {
if (debug::spriteWarnings) {
- fprintf(stderr, "[WARNING] sprite icon dimension mismatch\n");
+ Log::Warning(Event::Sprite, "sprite icon dimension mismatch");
}
return false;
}
@@ -235,8 +237,10 @@ void SpriteAtlas::bind(bool linear) {
#ifndef GL_ES_VERSION_2_0
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
#endif
- MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT));
- MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT));
+ // We are using clamp to edge here since OpenGL ES doesn't allow GL_REPEAT on NPOT textures.
+ // We use those when the pixelRatio isn't a power of two, e.g. on iPhone 6 Plus.
+ 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));
first = true;
} else {
MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
@@ -254,7 +258,7 @@ void SpriteAtlas::bind(bool linear) {
allocate();
if (first) {
- glTexImage2D(
+ MBGL_CHECK_ERROR(glTexImage2D(
GL_TEXTURE_2D, // GLenum target
0, // GLint level
GL_RGBA, // GLint internalformat
@@ -264,9 +268,9 @@ void SpriteAtlas::bind(bool linear) {
GL_RGBA, // GLenum format
GL_UNSIGNED_BYTE, // GLenum type
data // const GLvoid * data
- );
+ ));
} else {
- glTexSubImage2D(
+ MBGL_CHECK_ERROR(glTexSubImage2D(
GL_TEXTURE_2D, // GLenum target
0, // GLint level
0, // GLint xoffset
@@ -276,12 +280,14 @@ void SpriteAtlas::bind(bool linear) {
GL_RGBA, // GLenum format
GL_UNSIGNED_BYTE, // GLenum type
data // const GLvoid *pixels
- );
+ ));
}
dirty = false;
- // platform::show_color_debug_image("Sprite Atlas", reinterpret_cast<const char *>(data), width, height, width * pixelRatio, height * pixelRatio);
+#ifndef GL_ES_VERSION_2_0
+ // platform::showColorDebugImage("Sprite Atlas", reinterpret_cast<const char *>(data), width * pixelRatio, height * pixelRatio, width * pixelRatio, height * pixelRatio);
+#endif
}
};
diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp
new file mode 100644
index 0000000000..0d6da5781e
--- /dev/null
+++ b/src/mbgl/map/annotation.cpp
@@ -0,0 +1,190 @@
+#include <mbgl/map/annotation.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/util/ptr.hpp>
+#include <mbgl/util/std.hpp>
+
+#include <algorithm>
+#include <memory>
+
+using namespace mbgl;
+
+Annotation::Annotation(AnnotationType type_, std::vector<AnnotationSegment> geometry_)
+ : type(type_),
+ geometry(geometry_) {
+ if (type == AnnotationType::Point) {
+ bounds = LatLngBounds(getPoint(), getPoint());
+ } else {
+ for (auto segment : geometry) {
+ for (auto point : segment) {
+ bounds.extend(point);
+ }
+ }
+ }
+}
+
+LatLng Annotation::getPoint() const {
+ return geometry[0][0];
+}
+
+AnnotationManager::AnnotationManager()
+ : nullTile(util::make_unique<LiveTile>()) {}
+
+vec2<double> AnnotationManager::projectPoint(LatLng& point) {
+ double sine = std::sin(point.latitude * M_PI / 180);
+ double x = point.longitude / 360 + 0.5;
+ double y = 0.5 - 0.25 * std::log((1 + sine) / (1 - sine)) / M_PI;
+ return vec2<double>(x, y);
+}
+
+std::pair<std::vector<Tile::ID>, std::vector<uint32_t>> AnnotationManager::addPointAnnotations(std::vector<LatLng> points, std::vector<std::string>& symbols, const Map& map) {
+
+ uint16_t extent = 4096;
+
+ std::vector<uint32_t> annotationIDs(points.size());
+ std::vector<Tile::ID> affectedTiles;
+
+ for (uint32_t i = 0; i < points.size(); ++i) {
+ uint32_t annotationID = nextID();
+
+ auto anno_it = annotations.emplace(annotationID, util::make_unique<Annotation>(AnnotationType::Point, std::vector<AnnotationSegment>({{ points[i] }})));
+
+ uint8_t maxZoom = map.getMaxZoom();
+
+ uint32_t z2 = 1 << maxZoom;
+
+ vec2<double> p = projectPoint(points[i]);
+
+ uint32_t x = p.x * z2;
+ uint32_t y = p.y * z2;
+
+ for (int8_t z = maxZoom; z >= 0; z--) {
+ affectedTiles.emplace_back(z, x, y);
+ Tile::ID tileID = affectedTiles.back();
+
+ Coordinate coordinate(extent * (p.x * z2 - x), extent * (p.y * z2 - y));
+
+ GeometryCollection geometries({{ {{ coordinate }} }});
+
+ std::map<std::string, std::string> properties = {{ "sprite", (symbols[i].length() ? symbols[i] : defaultPointAnnotationSymbol) }};
+
+ auto feature = std::make_shared<const LiveTileFeature>(FeatureType::Point,
+ geometries,
+ properties);
+
+ auto tile_it = annotationTiles.find(tileID);
+ if (tile_it != annotationTiles.end()) {
+ // get point layer & add feature
+ auto layer = tile_it->second.second->getMutableLayer(util::ANNOTATIONS_POINTS_LAYER_ID);
+ layer->addFeature(feature);
+ // record annotation association with tile
+ tile_it->second.first.push_back(annotationID);
+ } else {
+ // create point layer & add feature
+ util::ptr<LiveTileLayer> layer = std::make_shared<LiveTileLayer>();
+ layer->addFeature(feature);
+ // create tile & record annotation association
+ auto tile_pos = annotationTiles.emplace(tileID, std::make_pair(std::vector<uint32_t>({ annotationID }), util::make_unique<LiveTile>()));
+ // add point layer to tile
+ tile_pos.first->second.second->addLayer(util::ANNOTATIONS_POINTS_LAYER_ID, layer);
+ }
+
+ // record annotation association with tile feature
+ anno_it.first->second->tileFeatures.emplace(tileID, std::vector<std::weak_ptr<const LiveTileFeature>>({ feature }));
+
+ z2 /= 2;
+ x /= 2;
+ y /= 2;
+ }
+
+ annotationIDs.push_back(annotationID);
+ }
+
+ return std::make_pair(affectedTiles, annotationIDs);
+}
+
+std::vector<Tile::ID> AnnotationManager::removeAnnotations(std::vector<uint32_t> ids) {
+ std::vector<Tile::ID> affectedTiles;
+
+ for (auto& annotationID : ids) {
+ auto annotation_it = annotations.find(annotationID);
+ if (annotation_it != annotations.end()) {
+ auto& annotation = annotation_it->second;
+ for (auto& tile_it : annotationTiles) {
+ auto& tileAnnotations = tile_it.second.first;
+ util::erase_if(tileAnnotations, tileAnnotations.begin(),
+ tileAnnotations.end(), [&](const uint32_t annotationID_) -> bool {
+ return (annotationID_ == annotationID);
+ });
+ auto features_it = annotation->tileFeatures.find(tile_it.first);
+ if (features_it != annotation->tileFeatures.end()) {
+ auto layer = tile_it.second.second->getMutableLayer(util::ANNOTATIONS_POINTS_LAYER_ID);
+ auto& features = features_it->second;
+ layer->removeFeature(features[0]);
+ affectedTiles.push_back(tile_it.first);
+ }
+ }
+ annotations.erase(annotationID);
+ }
+ }
+
+ return affectedTiles;
+}
+
+std::vector<uint32_t> AnnotationManager::getAnnotationsInBounds(LatLngBounds queryBounds, const Map& map) const {
+ uint8_t z = map.getMaxZoom();
+ uint32_t z2 = 1 << z;
+ vec2<double> swPoint = projectPoint(queryBounds.sw);
+ vec2<double> nePoint = projectPoint(queryBounds.ne);
+
+ // tiles number y from top down
+ Tile::ID nwTile(z, swPoint.x * z2, nePoint.y * z2);
+ Tile::ID seTile(z, nePoint.x * z2, swPoint.y * z2);
+
+ std::vector<uint32_t> matchingAnnotations;
+
+ for (auto& tile : annotationTiles) {
+ Tile::ID id = tile.first;
+ if (id.z == z) {
+ if (id.x >= nwTile.x && id.x <= seTile.x && id.y >= nwTile.y && id.y <= seTile.y) {
+ if (id.x > nwTile.x && id.x < seTile.x && id.y > nwTile.y && id.y < seTile.y) {
+ // trivial accept; grab all of the tile's annotations
+ std::copy(tile.second.first.begin(), tile.second.first.end(), std::back_inserter(matchingAnnotations));
+ } else {
+ // check tile's annotations' bounding boxes
+ std::copy_if(tile.second.first.begin(), tile.second.first.end(),
+ std::back_inserter(matchingAnnotations), [&](const uint32_t annotationID) -> bool {
+ LatLngBounds annoBounds = this->annotations.find(annotationID)->second->getBounds();
+ return (annoBounds.sw.latitude >= queryBounds.sw.latitude &&
+ annoBounds.ne.latitude <= queryBounds.ne.latitude &&
+ annoBounds.sw.longitude >= queryBounds.sw.longitude &&
+ annoBounds.ne.longitude <= queryBounds.ne.longitude);
+ });
+ }
+ }
+ }
+ }
+
+ return matchingAnnotations;
+}
+
+LatLngBounds AnnotationManager::getBoundsForAnnotations(std::vector<uint32_t> ids) const {
+ LatLngBounds bounds;
+ for (auto id : ids) {
+ auto annotation_it = annotations.find(id);
+ if (annotation_it != annotations.end()) {
+ bounds.extend(annotation_it->second->getPoint());
+ }
+ }
+
+ return bounds;
+}
+
+const std::unique_ptr<LiveTile>& AnnotationManager::getTile(Tile::ID const& id) {
+ std::lock_guard<std::mutex> lock(mtx);
+
+ auto tile_it = annotationTiles.find(id);
+ if (tile_it != annotationTiles.end()) {
+ return tile_it->second.second;
+ }
+ return nullTile;
+}
diff --git a/src/mbgl/map/environment.cpp b/src/mbgl/map/environment.cpp
new file mode 100644
index 0000000000..1aea5aa0c9
--- /dev/null
+++ b/src/mbgl/map/environment.cpp
@@ -0,0 +1,136 @@
+#include <mbgl/map/environment.hpp>
+#include <mbgl/storage/file_source.hpp>
+
+#include <uv.h>
+
+#include <atomic>
+#include <cassert>
+#include <mutex>
+#include <unordered_map>
+
+namespace mbgl {
+
+namespace {
+
+class ThreadInfoStore {
+private:
+ struct ThreadInfo {
+ Environment* env;
+ ThreadType type;
+ std::string name;
+ };
+
+public:
+ ThreadInfoStore() {
+ registerThread(nullptr, ThreadType::Main, "Main");
+ }
+
+ ~ThreadInfoStore() {
+ unregisterThread();
+ assert(threadSet.size() == 0);
+ }
+
+ void registerThread(Environment* env, ThreadType type, const std::string& name) {
+ std::lock_guard<std::mutex> lock(mtx);
+
+ // FIXME: We should never need to overwrite a thread here and we only allow
+ // this today because on the Static mode, the Map thread and the Main thread
+ // are same. Replace this with emplace() when this gets fixed.
+ threadSet[std::this_thread::get_id()] = ThreadInfo{ env, type, name };
+ }
+
+ void unregisterThread() {
+ std::lock_guard<std::mutex> lock(mtx);
+
+ ThreadSet::iterator it = threadSet.find(std::this_thread::get_id());
+ if (it != threadSet.end()) {
+ threadSet.erase(it);
+ }
+ }
+
+ const ThreadInfo& getThreadInfo() const {
+ static ThreadInfo emptyInfo;
+ std::lock_guard<std::mutex> lock(mtx);
+
+ ThreadSet::const_iterator it = threadSet.find(std::this_thread::get_id());
+ if (it != threadSet.end()) {
+ return it->second;
+ } else {
+ return emptyInfo;
+ }
+ }
+
+private:
+ typedef std::unordered_map<std::thread::id, ThreadInfo> ThreadSet;
+ ThreadSet threadSet;
+
+ mutable std::mutex mtx;
+};
+
+unsigned makeEnvironmentID() {
+ static std::atomic<unsigned> id(0);
+ return id++;
+}
+
+ThreadInfoStore threadInfoStore;
+
+} // namespace
+
+Environment::Scope::Scope(Environment& env, ThreadType type, const std::string& name)
+ : id(std::this_thread::get_id()) {
+ threadInfoStore.registerThread(&env, type, name);
+}
+
+Environment::Scope::~Scope() {
+ assert(id == std::this_thread::get_id());
+ threadInfoStore.unregisterThread();
+}
+
+Environment::Environment(FileSource& fs)
+ : id(makeEnvironmentID()), fileSource(fs), loop(uv_loop_new()) {
+}
+
+Environment& Environment::Get() {
+ Environment* env = threadInfoStore.getThreadInfo().env;
+ assert(env);
+
+ return *env;
+}
+
+bool Environment::inScope() {
+ return threadInfoStore.getThreadInfo().env;
+}
+
+bool Environment::currentlyOn(ThreadType type) {
+ return static_cast<uint8_t>(threadInfoStore.getThreadInfo().type) & static_cast<uint8_t>(type);
+}
+
+std::string Environment::threadName() {
+ return threadInfoStore.getThreadInfo().name;
+}
+
+unsigned Environment::getID() const {
+ return id;
+}
+
+void Environment::requestAsync(const Resource& resource,
+ std::function<void(const Response&)> callback) {
+ fileSource.request(resource, *this, std::move(callback));
+}
+
+Request* Environment::request(const Resource& resource,
+ std::function<void(const Response&)> callback) {
+ assert(currentlyOn(ThreadType::Map));
+ return fileSource.request(resource, loop, *this, std::move(callback));
+}
+
+void Environment::cancelRequest(Request* req) {
+ assert(currentlyOn(ThreadType::Map));
+ fileSource.cancel(req);
+}
+
+void Environment::terminate() {
+ fileSource.abort(*this);
+}
+
+}
diff --git a/src/mbgl/map/environment.hpp b/src/mbgl/map/environment.hpp
new file mode 100644
index 0000000000..b631abf13d
--- /dev/null
+++ b/src/mbgl/map/environment.hpp
@@ -0,0 +1,62 @@
+#ifndef MBGL_MAP_MAP_ENVIRONMENT
+#define MBGL_MAP_MAP_ENVIRONMENT
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/util.hpp>
+
+#include <thread>
+#include <functional>
+
+typedef struct uv_loop_s uv_loop_t;
+
+namespace mbgl {
+
+class FileSource;
+class Request;
+class Response;
+struct Resource;
+
+enum class ThreadType : uint8_t {
+ Unknown = 0,
+ Main = 1 << 0,
+ Map = 1 << 1,
+ TileWorker = 1 << 2,
+};
+
+class Environment final : private util::noncopyable {
+public:
+ class Scope final {
+ public:
+ Scope(Environment&, ThreadType, const std::string& name);
+ ~Scope();
+
+ private:
+ std::thread::id id;
+ };
+
+ Environment(FileSource&);
+
+ static Environment& Get();
+ static bool inScope();
+ static bool currentlyOn(ThreadType);
+ static std::string threadName();
+
+ unsigned getID() const;
+ void requestAsync(const Resource&, std::function<void(const Response&)>);
+ Request* request(const Resource&, std::function<void(const Response&)>);
+ void cancelRequest(Request*);
+
+ // Request to terminate the environment.
+ void terminate();
+
+private:
+ unsigned id;
+ FileSource& fileSource;
+
+public:
+ uv_loop_t* const loop;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/map/geometry_tile.cpp b/src/mbgl/map/geometry_tile.cpp
new file mode 100644
index 0000000000..785845b4db
--- /dev/null
+++ b/src/mbgl/map/geometry_tile.cpp
@@ -0,0 +1,17 @@
+#include <mbgl/map/geometry_tile.hpp>
+#include <mbgl/style/filter_expression.hpp>
+#include <mbgl/style/filter_expression_private.hpp>
+
+namespace mbgl {
+
+mapbox::util::optional<Value> GeometryTileFeatureExtractor::getValue(const std::string& key) const {
+ if (key == "$type") {
+ return Value(uint64_t(feature.getType()));
+ }
+
+ return feature.getValue(key);
+}
+
+template bool evaluate(const FilterExpression&, const GeometryTileFeatureExtractor&);
+
+}
diff --git a/src/mbgl/map/geometry_tile.hpp b/src/mbgl/map/geometry_tile.hpp
new file mode 100644
index 0000000000..dc1ef10725
--- /dev/null
+++ b/src/mbgl/map/geometry_tile.hpp
@@ -0,0 +1,57 @@
+#ifndef MBGL_MAP_GEOMETRY_TILE
+#define MBGL_MAP_GEOMETRY_TILE
+
+#include <mbgl/style/value.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/ptr.hpp>
+#include <mbgl/util/variant.hpp>
+#include <mbgl/util/vec.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace mbgl {
+
+enum class FeatureType : uint8_t {
+ Unknown = 0,
+ Point = 1,
+ LineString = 2,
+ Polygon = 3
+};
+
+typedef std::vector<std::vector<Coordinate>> GeometryCollection;
+
+class GeometryTileFeature : private util::noncopyable {
+public:
+ virtual FeatureType getType() const = 0;
+ virtual mapbox::util::optional<Value> getValue(const std::string& key) const = 0;
+ virtual GeometryCollection getGeometries() const = 0;
+};
+
+class GeometryTileLayer : private util::noncopyable {
+public:
+ virtual std::size_t featureCount() const = 0;
+ virtual util::ptr<const GeometryTileFeature> getFeature(std::size_t) const = 0;
+};
+
+class GeometryTile : private util::noncopyable {
+public:
+ virtual util::ptr<GeometryTileLayer> getLayer(const std::string&) const = 0;
+};
+
+class GeometryTileFeatureExtractor {
+public:
+ GeometryTileFeatureExtractor(const GeometryTileFeature& feature_)
+ : feature(feature_) {}
+
+ mapbox::util::optional<Value> getValue(const std::string& key) const;
+
+private:
+ const GeometryTileFeature& feature;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/map/live_tile.cpp b/src/mbgl/map/live_tile.cpp
new file mode 100644
index 0000000000..06337af184
--- /dev/null
+++ b/src/mbgl/map/live_tile.cpp
@@ -0,0 +1,56 @@
+#include <mbgl/map/live_tile.hpp>
+#include <mbgl/util/constants.hpp>
+
+namespace mbgl {
+
+LiveTileFeature::LiveTileFeature(FeatureType type_, GeometryCollection geometries_, std::map<std::string, std::string> properties_)
+ : type(type_),
+ properties(properties_),
+ geometries(geometries_) {}
+
+mapbox::util::optional<Value> LiveTileFeature::getValue(const std::string& key) const {
+ auto it = properties.find(key);
+ if (it != properties.end()) {
+ return mapbox::util::optional<Value>(it->second);
+ }
+ return mapbox::util::optional<Value>();
+}
+
+LiveTileLayer::LiveTileLayer() {}
+
+void LiveTileLayer::prepareToAddFeatures(size_t count) {
+ features.reserve(features.size() + count);
+}
+
+void LiveTileLayer::addFeature(util::ptr<const LiveTileFeature> feature) {
+ features.push_back(std::move(feature));
+}
+
+void LiveTileLayer::removeFeature(util::ptr<const LiveTileFeature> feature) {
+ for (auto it = features.begin(); it != features.end(); ++it) {
+ if (feature == *it) {
+ features.erase(it);
+ return;
+ }
+ }
+}
+
+LiveTile::LiveTile() {}
+
+void LiveTile::addLayer(const std::string& name, util::ptr<LiveTileLayer> layer) {
+ layers.emplace(name, std::move(layer));
+}
+
+util::ptr<GeometryTileLayer> LiveTile::getLayer(const std::string& name) const {
+ return getMutableLayer(name);
+}
+
+util::ptr<LiveTileLayer> LiveTile::getMutableLayer(const std::string& name) const {
+ auto layer_it = layers.find(name);
+ if (layer_it != layers.end()) {
+ return layer_it->second;
+ }
+ return nullptr;
+}
+
+}
diff --git a/src/mbgl/map/live_tile.hpp b/src/mbgl/map/live_tile.hpp
new file mode 100644
index 0000000000..6a24518d85
--- /dev/null
+++ b/src/mbgl/map/live_tile.hpp
@@ -0,0 +1,53 @@
+#ifndef MBGL_MAP_LIVE_TILE
+#define MBGL_MAP_LIVE_TILE
+
+#include <map>
+
+#include <mbgl/map/geometry_tile.hpp>
+
+namespace mbgl {
+
+class LiveTileFeature : public GeometryTileFeature {
+public:
+ LiveTileFeature(FeatureType, GeometryCollection, std::map<std::string, std::string> properties = {{}});
+
+ FeatureType getType() const override { return type; }
+ mapbox::util::optional<Value> getValue(const std::string&) const override;
+ GeometryCollection getGeometries() const override { return geometries; }
+
+private:
+ FeatureType type = FeatureType::Unknown;
+ std::map<std::string, std::string> properties;
+ GeometryCollection geometries;
+};
+
+ class LiveTileLayer : public GeometryTileLayer {
+public:
+ LiveTileLayer();
+
+ void prepareToAddFeatures(size_t count);
+ void addFeature(util::ptr<const LiveTileFeature>);
+ void removeFeature(util::ptr<const LiveTileFeature>);
+ std::size_t featureCount() const override { return features.size(); }
+ util::ptr<const GeometryTileFeature> getFeature(std::size_t i) const override { return features[i]; }
+
+private:
+ std::vector<util::ptr<const LiveTileFeature>> features;
+};
+
+class LiveTile : public GeometryTile {
+public:
+ LiveTile();
+
+ void addLayer(const std::string&, util::ptr<LiveTileLayer>);
+ util::ptr<GeometryTileLayer> getLayer(const std::string&) const override;
+ util::ptr<LiveTileLayer> getMutableLayer(const std::string&) const;
+ bool operator()(const LiveTile&) const { return layers.size() > 0; }
+
+private:
+ std::map<std::string, util::ptr<LiveTileLayer>> layers;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/map/live_tile_data.cpp b/src/mbgl/map/live_tile_data.cpp
new file mode 100644
index 0000000000..192efb7dcf
--- /dev/null
+++ b/src/mbgl/map/live_tile_data.cpp
@@ -0,0 +1,66 @@
+#include <mbgl/map/annotation.hpp>
+#include <mbgl/map/live_tile_data.hpp>
+#include <mbgl/map/tile_parser.hpp>
+#include <mbgl/style/style_source.hpp>
+#include <mbgl/map/vector_tile.hpp>
+#include <mbgl/platform/log.hpp>
+
+using namespace mbgl;
+
+LiveTileData::LiveTileData(Tile::ID const& id_,
+ AnnotationManager& annotationManager_,
+ float mapMaxZoom,
+ util::ptr<Style> style_,
+ GlyphAtlas& glyphAtlas_,
+ GlyphStore& glyphStore_,
+ SpriteAtlas& spriteAtlas_,
+ util::ptr<Sprite> sprite_,
+ const SourceInfo& source_)
+ : VectorTileData::VectorTileData(id_, mapMaxZoom, style_, glyphAtlas_, glyphStore_,
+ spriteAtlas_, sprite_, source_),
+ annotationManager(annotationManager_) {
+ // live features are always ready
+ state = State::loaded;
+}
+
+LiveTileData::~LiveTileData() {}
+
+void LiveTileData::parse() {
+ if (state != State::loaded) {
+ return;
+ }
+
+ try {
+ if (!style) {
+ throw std::runtime_error("style isn't present in LiveTileData object anymore");
+ }
+
+ if (source.type == SourceType::Annotations) {
+ const std::unique_ptr<LiveTile>& tile = annotationManager.getTile(id);
+
+ if (tile) {
+ // Parsing creates state that is encapsulated in TileParser. While parsing,
+ // the TileParser object writes results into this objects. All other state
+ // is going to be discarded afterwards.
+ TileParser parser(*tile, *this, style, glyphAtlas, glyphStore, spriteAtlas, sprite);
+
+ // Clear the style so that we don't have a cycle in the shared_ptr references.
+ style.reset();
+
+ parser.parse();
+ } else {
+ state = State::obsolete;
+ }
+ } else {
+ throw std::runtime_error("unknown live tile source type");
+ }
+ } catch (const std::exception& ex) {
+ Log::Error(Event::ParseTile, "Live-parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what());
+ state = State::obsolete;
+ return;
+ }
+
+ if (state != State::obsolete) {
+ state = State::parsed;
+ }
+}
diff --git a/src/mbgl/map/live_tile_data.hpp b/src/mbgl/map/live_tile_data.hpp
new file mode 100644
index 0000000000..7874d6ff55
--- /dev/null
+++ b/src/mbgl/map/live_tile_data.hpp
@@ -0,0 +1,31 @@
+#ifndef MBGL_MAP_LIVE_TILE_DATA
+#define MBGL_MAP_LIVE_TILE_DATA
+
+#include <mbgl/map/vector_tile_data.hpp>
+
+namespace mbgl {
+
+class AnnotationManager;
+
+class LiveTileData : public VectorTileData {
+public:
+ LiveTileData(Tile::ID const&,
+ AnnotationManager&,
+ float mapMaxZoom,
+ util::ptr<Style>,
+ GlyphAtlas&,
+ GlyphStore&,
+ SpriteAtlas&,
+ util::ptr<Sprite>,
+ const SourceInfo&);
+ ~LiveTileData();
+
+ void parse() override;
+
+private:
+ AnnotationManager& annotationManager;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index ed20ec24a6..22e6cbcebb 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -1,8 +1,10 @@
#include <mbgl/map/map.hpp>
+#include <mbgl/map/environment.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/platform/platform.hpp>
#include <mbgl/map/source.hpp>
#include <mbgl/renderer/painter.hpp>
+#include <mbgl/map/annotation.hpp>
#include <mbgl/map/sprite.hpp>
#include <mbgl/util/transition.hpp>
#include <mbgl/util/math.hpp>
@@ -25,6 +27,7 @@
#include <mbgl/util/string.hpp>
#include <mbgl/util/uv.hpp>
#include <mbgl/util/mapbox.hpp>
+#include <mbgl/util/exception.hpp>
#include <algorithm>
#include <iostream>
@@ -57,26 +60,19 @@ const static bool uvVersionCheck = []() {
using namespace mbgl;
Map::Map(View& view_, FileSource& fileSource_)
- : loop(util::make_unique<uv::loop>()),
+ : env(util::make_unique<Environment>(fileSource_)),
view(view_),
-#ifdef DEBUG
- mainThread(std::this_thread::get_id()),
- mapThread(mainThread),
-#endif
transform(view_),
fileSource(fileSource_),
glyphAtlas(util::make_unique<GlyphAtlas>(1024, 1024)),
- glyphStore(std::make_shared<GlyphStore>(fileSource)),
+ glyphStore(std::make_shared<GlyphStore>(*env)),
spriteAtlas(util::make_unique<SpriteAtlas>(512, 512)),
lineAtlas(util::make_unique<LineAtlas>(512, 512)),
texturePool(std::make_shared<TexturePool>()),
- painter(util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas))
+ painter(util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas)),
+ annotationManager(util::make_unique<AnnotationManager>())
{
view.initialize(this);
- // Make sure that we're doing an initial drawing in all cases.
- isClean.clear();
- isRendered.clear();
- isSwapped.test_and_set();
}
Map::~Map() {
@@ -92,7 +88,7 @@ Map::~Map() {
texturePool.reset();
workers.reset();
- uv_run(**loop, UV_RUN_DEFAULT);
+ uv_run(env->loop, UV_RUN_DEFAULT);
}
uv::worker &Map::getWorker() {
@@ -101,7 +97,7 @@ uv::worker &Map::getWorker() {
}
void Map::start(bool startPaused) {
- assert(std::this_thread::get_id() == mainThread);
+ assert(Environment::currentlyOn(ThreadType::Main));
assert(mode == Mode::None);
// When starting map rendering in another thread, we perform async/continuously
@@ -112,8 +108,8 @@ void Map::start(bool startPaused) {
isStopped = false;
// Setup async notifications
- asyncTerminate = util::make_unique<uv::async>(**loop, [this]() {
- assert(std::this_thread::get_id() == mapThread);
+ asyncTerminate = util::make_unique<uv::async>(env->loop, [this]() {
+ assert(Environment::currentlyOn(ThreadType::Map));
// Remove all of these to make sure they are destructed in the correct thread.
style.reset();
@@ -124,26 +120,30 @@ void Map::start(bool startPaused) {
// Closes all open handles on the loop. This means that the loop will automatically terminate.
asyncRender.reset();
+ asyncUpdate.reset();
asyncTerminate.reset();
});
- asyncRender = util::make_unique<uv::async>(**loop, [this]() {
- assert(std::this_thread::get_id() == mapThread);
+ asyncUpdate = util::make_unique<uv::async>(env->loop, [this] {
+ assert(Environment::currentlyOn(ThreadType::Map));
if (state.hasSize()) {
- if (isRendered.test_and_set() == false) {
- prepare();
- if (isClean.test_and_set() == false) {
- render();
- isSwapped.clear();
- view.swap();
- } else {
- // We set the rendered flag in the test above, so we have to reset it
- // now that we're not actually rendering because the map is clean.
- isRendered.clear();
- }
- }
+ prepare();
+ }
+ });
+
+ asyncRender = util::make_unique<uv::async>(env->loop, [this] {
+ // Must be called in Map thread.
+ assert(Environment::currentlyOn(ThreadType::Map));
+
+ render();
+
+ // Finally, notify all listeners that we have finished rendering this frame.
+ {
+ std::lock_guard<std::mutex> lk(mutexRendered);
+ rendered = true;
}
+ condRendered.notify_all();
});
// Do we need to pause first?
@@ -152,20 +152,12 @@ void Map::start(bool startPaused) {
}
thread = std::thread([this]() {
-#ifdef DEBUG
- mapThread = std::this_thread::get_id();
-#endif
-
#ifdef __APPLE__
pthread_setname_np("Map");
#endif
run();
-#ifdef DEBUG
- mapThread = std::thread::id();
-#endif
-
// Make sure that the stop() function knows when to stop invoking the callback function.
isStopped = true;
view.notify();
@@ -173,8 +165,7 @@ void Map::start(bool startPaused) {
}
void Map::stop(std::function<void ()> callback) {
- assert(std::this_thread::get_id() == mainThread);
- assert(mainThread != mapThread);
+ assert(Environment::currentlyOn(ThreadType::Main));
assert(mode == Mode::Continuous);
asyncTerminate->send();
@@ -201,14 +192,14 @@ void Map::stop(std::function<void ()> callback) {
}
void Map::pause(bool waitForPause) {
- assert(std::this_thread::get_id() == mainThread);
+ assert(Environment::currentlyOn(ThreadType::Main));
assert(mode == Mode::Continuous);
mutexRun.lock();
pausing = true;
mutexRun.unlock();
- uv_stop(**loop);
- rerender(); // Needed to ensure uv_stop is seen and uv_run exits, otherwise we deadlock on wait_for_pause
+ uv_stop(env->loop);
+ triggerUpdate(); // Needed to ensure uv_stop is seen and uv_run exits, otherwise we deadlock on wait_for_pause
if (waitForPause) {
std::unique_lock<std::mutex> lockPause (mutexPause);
@@ -219,7 +210,7 @@ void Map::pause(bool waitForPause) {
}
void Map::resume() {
- assert(std::this_thread::get_id() == mainThread);
+ assert(Environment::currentlyOn(ThreadType::Main));
assert(mode == Mode::Continuous);
mutexRun.lock();
@@ -229,21 +220,32 @@ void Map::resume() {
}
void Map::run() {
+ ThreadType threadType = ThreadType::Map;
+ std::string threadName("Map");
+
if (mode == Mode::None) {
-#ifdef DEBUG
- mapThread = mainThread;
-#endif
mode = Mode::Static;
+
+ // FIXME: Threads should have only one purpose. When running on Static mode,
+ // we are currently not spawning a Map thread and running the code on the
+ // Main thread, thus, the Main thread in this case is both Main and Map thread.
+ threadType = static_cast<ThreadType>(static_cast<uint8_t>(threadType) | static_cast<uint8_t>(ThreadType::Main));
+ threadName += "andMain";
}
- assert(std::this_thread::get_id() == mapThread);
+
+ Environment::Scope scope(*env, threadType, threadName);
if (mode == Mode::Continuous) {
checkForPause();
}
+ if (mode == Mode::Static && !style && styleURL.empty()) {
+ throw util::Exception("Style is not set");
+ }
+
view.activate();
- workers = util::make_unique<uv::worker>(**loop, 4, "Tile Worker");
+ workers = util::make_unique<uv::worker>(env->loop, 4, "Tile Worker");
setup();
prepare();
@@ -251,29 +253,50 @@ void Map::run() {
if (mode == Mode::Continuous) {
terminating = false;
while(!terminating) {
- uv_run(**loop, UV_RUN_DEFAULT);
+ uv_run(env->loop, UV_RUN_DEFAULT);
checkForPause();
}
} else {
- uv_run(**loop, UV_RUN_DEFAULT);
+ uv_run(env->loop, UV_RUN_DEFAULT);
}
// Run the event loop once more to make sure our async delete handlers are called.
- uv_run(**loop, UV_RUN_ONCE);
+ uv_run(env->loop, UV_RUN_ONCE);
// If the map rendering wasn't started asynchronously, we perform one render
// *after* all events have been processed.
if (mode == Mode::Static) {
render();
-#ifdef DEBUG
- mapThread = std::thread::id();
-#endif
mode = Mode::None;
}
view.deactivate();
}
+void Map::renderSync() {
+ // Must be called in UI thread.
+ assert(Environment::currentlyOn(ThreadType::Main));
+
+ triggerRender();
+
+ std::unique_lock<std::mutex> lock(mutexRendered);
+ condRendered.wait(lock, [this] { return rendered; });
+ rendered = false;
+}
+
+void Map::triggerUpdate() {
+ if (mode == Mode::Static) {
+ prepare();
+ } else if (asyncUpdate) {
+ asyncUpdate->send();
+ }
+}
+
+void Map::triggerRender() {
+ assert(asyncRender);
+ asyncRender->send();
+}
+
void Map::checkForPause() {
std::unique_lock<std::mutex> lockRun (mutexRun);
while (pausing) {
@@ -294,32 +317,6 @@ void Map::checkForPause() {
mutexPause.unlock();
}
-void Map::rerender() {
- if (mode == Mode::Static) {
- prepare();
- } else if (mode == Mode::Continuous) {
- // We only send render events if we want to continuously update the map
- // (== async rendering).
- if (asyncRender) {
- asyncRender->send();
- }
- }
-}
-
-void Map::update() {
- isClean.clear();
- rerender();
-}
-
-bool Map::needsSwap() {
- return isSwapped.test_and_set() == false;
-}
-
-void Map::swapped() {
- isRendered.clear();
- rerender();
-}
-
void Map::terminate() {
assert(painter);
painter->terminate();
@@ -329,7 +326,7 @@ void Map::terminate() {
#pragma mark - Setup
void Map::setup() {
- assert(std::this_thread::get_id() == mapThread);
+ assert(Environment::currentlyOn(ThreadType::Map));
assert(painter);
painter->setup();
}
@@ -344,7 +341,6 @@ void Map::setStyleURL(const std::string &url) {
}
}
-
void Map::setStyleJSON(std::string newStyleJSON, const std::string &base) {
// TODO: Make threadsafe.
styleJSON.swap(newStyleJSON);
@@ -361,7 +357,7 @@ void Map::setStyleJSON(std::string newStyleJSON, const std::string &base) {
const std::string glyphURL = util::mapbox::normalizeGlyphsURL(style->glyph_url, getAccessToken());
glyphStore->setURL(glyphURL);
- update();
+ triggerUpdate();
}
std::string Map::getStyleJSON() const {
@@ -371,8 +367,8 @@ std::string Map::getStyleJSON() const {
util::ptr<Sprite> Map::getSprite() {
const float pixelRatio = state.getPixelRatio();
const std::string &sprite_url = style->getSpriteURL();
- if (!sprite || sprite->pixelRatio != pixelRatio) {
- sprite = Sprite::Create(sprite_url, pixelRatio, fileSource);
+ if (!sprite || !sprite->hasPixelRatio(pixelRatio)) {
+ sprite = Sprite::Create(sprite_url, pixelRatio, *env);
}
return sprite;
@@ -387,7 +383,7 @@ void Map::resize(uint16_t width, uint16_t height, float ratio) {
void Map::resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight) {
if (transform.resize(width, height, ratio, fbWidth, fbHeight)) {
- update();
+ triggerUpdate();
}
}
@@ -396,7 +392,7 @@ void Map::resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth,
void Map::cancelTransitions() {
transform.cancelTransitions();
- update();
+ triggerUpdate();
}
@@ -404,29 +400,33 @@ void Map::cancelTransitions() {
void Map::moveBy(double dx, double dy, std::chrono::steady_clock::duration duration) {
transform.moveBy(dx, dy, duration);
- update();
+ triggerUpdate();
}
void Map::setLatLng(LatLng latLng, std::chrono::steady_clock::duration duration) {
transform.setLatLng(latLng, duration);
- update();
+ triggerUpdate();
+}
+
+LatLng Map::getLatLng() const {
+ return state.getLatLng();
}
void Map::startPanning() {
transform.startPanning();
- update();
+ triggerUpdate();
}
void Map::stopPanning() {
transform.stopPanning();
- update();
+ triggerUpdate();
}
void Map::resetPosition() {
transform.setAngle(0);
transform.setLatLng(LatLng(0, 0));
transform.setZoom(0);
- update();
+ triggerUpdate();
}
@@ -434,12 +434,12 @@ void Map::resetPosition() {
void Map::scaleBy(double ds, double cx, double cy, std::chrono::steady_clock::duration duration) {
transform.scaleBy(ds, cx, cy, duration);
- update();
+ triggerUpdate();
}
void Map::setScale(double scale, double cx, double cy, std::chrono::steady_clock::duration duration) {
transform.setScale(scale, cx, cy, duration);
- update();
+ triggerUpdate();
}
double Map::getScale() const {
@@ -448,7 +448,7 @@ double Map::getScale() const {
void Map::setZoom(double zoom, std::chrono::steady_clock::duration duration) {
transform.setZoom(zoom, duration);
- update();
+ triggerUpdate();
}
double Map::getZoom() const {
@@ -457,7 +457,7 @@ double Map::getZoom() const {
void Map::setLatLngZoom(LatLng latLng, double zoom, std::chrono::steady_clock::duration duration) {
transform.setLatLngZoom(latLng, zoom, duration);
- update();
+ triggerUpdate();
}
void Map::resetZoom() {
@@ -466,12 +466,12 @@ void Map::resetZoom() {
void Map::startScaling() {
transform.startScaling();
- update();
+ triggerUpdate();
}
void Map::stopScaling() {
transform.stopScaling();
- update();
+ triggerUpdate();
}
double Map::getMinZoom() const {
@@ -487,17 +487,17 @@ double Map::getMaxZoom() const {
void Map::rotateBy(double sx, double sy, double ex, double ey, std::chrono::steady_clock::duration duration) {
transform.rotateBy(sx, sy, ex, ey, duration);
- update();
+ triggerUpdate();
}
void Map::setBearing(double degrees, std::chrono::steady_clock::duration duration) {
transform.setAngle(-degrees * M_PI / 180, duration);
- update();
+ triggerUpdate();
}
void Map::setBearing(double degrees, double cx, double cy) {
transform.setAngle(-degrees * M_PI / 180, cx, cy);
- update();
+ triggerUpdate();
}
double Map::getBearing() const {
@@ -506,17 +506,17 @@ double Map::getBearing() const {
void Map::resetNorth() {
transform.setAngle(0, std::chrono::milliseconds(500));
- update();
+ triggerUpdate();
}
void Map::startRotating() {
transform.startRotating();
- update();
+ triggerUpdate();
}
void Map::stopRotating() {
transform.stopRotating();
- update();
+ triggerUpdate();
}
#pragma mark - Access Token
@@ -529,13 +529,64 @@ const std::string &Map::getAccessToken() const {
return accessToken;
}
+#pragma mark - Annotations
+
+void Map::setDefaultPointAnnotationSymbol(std::string& symbol) {
+ assert(Environment::currentlyOn(ThreadType::Main));
+ annotationManager->setDefaultPointAnnotationSymbol(symbol);
+}
+
+uint32_t Map::addPointAnnotation(LatLng point, std::string& symbol) {
+ assert(Environment::currentlyOn(ThreadType::Main));
+ std::vector<LatLng> points({ point });
+ std::vector<std::string> symbols({ symbol });
+ return addPointAnnotations(points, symbols)[0];
+}
+
+std::vector<uint32_t> Map::addPointAnnotations(std::vector<LatLng> points, std::vector<std::string>& symbols) {
+ assert(Environment::currentlyOn(ThreadType::Main));
+ auto result = annotationManager->addPointAnnotations(points, symbols, *this);
+ updateAnnotationTiles(result.first);
+ return result.second;
+}
+
+void Map::removeAnnotation(uint32_t annotation) {
+ assert(Environment::currentlyOn(ThreadType::Main));
+ removeAnnotations({ annotation });
+}
+
+void Map::removeAnnotations(std::vector<uint32_t> annotations) {
+ assert(Environment::currentlyOn(ThreadType::Main));
+ auto result = annotationManager->removeAnnotations(annotations);
+ updateAnnotationTiles(result);
+}
+
+std::vector<uint32_t> Map::getAnnotationsInBounds(LatLngBounds bounds) const {
+ assert(Environment::currentlyOn(ThreadType::Main));
+ return annotationManager->getAnnotationsInBounds(bounds, *this);
+}
+
+LatLngBounds Map::getBoundsForAnnotations(std::vector<uint32_t> annotations) const {
+ assert(Environment::currentlyOn(ThreadType::Main));
+ return annotationManager->getBoundsForAnnotations(annotations);
+}
+
+void Map::updateAnnotationTiles(std::vector<Tile::ID>& ids) {
+ for (const auto &source : activeSources) {
+ if (source->info.type == SourceType::Annotations) {
+ source->source->invalidateTiles(*this, ids);
+ return;
+ }
+ }
+}
+
#pragma mark - Toggles
void Map::setDebug(bool value) {
debug = value;
assert(painter);
painter->setDebug(debug);
- update();
+ triggerUpdate();
}
void Map::toggleDebug() {
@@ -552,7 +603,7 @@ void Map::addClass(const std::string& klass) {
if (style) {
style->cascadeClasses(classes);
if (style->hasTransitions()) {
- update();
+ triggerUpdate();
}
}
}
@@ -563,7 +614,7 @@ void Map::removeClass(const std::string& klass) {
if (style) {
style->cascadeClasses(classes);
if (style->hasTransitions()) {
- update();
+ triggerUpdate();
}
}
}
@@ -573,7 +624,7 @@ void Map::setClasses(const std::vector<std::string>& classes_) {
if (style) {
style->cascadeClasses(classes);
if (style->hasTransitions()) {
- update();
+ triggerUpdate();
}
}
}
@@ -598,7 +649,7 @@ std::chrono::steady_clock::duration Map::getDefaultTransitionDuration() {
}
void Map::updateSources() {
- assert(std::this_thread::get_id() == mapThread);
+ assert(Environment::currentlyOn(ThreadType::Map));
// First, disable all existing sources.
for (const auto& source : activeSources) {
@@ -613,7 +664,7 @@ void Map::updateSources() {
if (source->enabled) {
if (!source->source) {
source->source = std::make_shared<Source>(source->info);
- source->source->load(*this, fileSource);
+ source->source->load(*this, *env);
}
} else {
source->source.reset();
@@ -627,6 +678,7 @@ void Map::updateSources() {
}
void Map::updateSources(const util::ptr<StyleLayerGroup> &group) {
+ assert(Environment::currentlyOn(ThreadType::Map));
if (!group) {
return;
}
@@ -635,23 +687,28 @@ void Map::updateSources(const util::ptr<StyleLayerGroup> &group) {
if (layer->bucket && layer->bucket->style_source) {
(*activeSources.emplace(layer->bucket->style_source).first)->enabled = true;
}
+
}
}
void Map::updateTiles() {
- for (const auto& source : activeSources) {
- source->source->update(*this, getWorker(),
- style, *glyphAtlas, *glyphStore,
- *spriteAtlas, getSprite(),
- *texturePool, fileSource, ***loop, [this](){ update(); });
+ assert(Environment::currentlyOn(ThreadType::Map));
+ for (const auto &source : activeSources) {
+ source->source->update(*this, getWorker(), style, *glyphAtlas, *glyphStore,
+ *spriteAtlas, getSprite(), *texturePool, [this]() {
+ assert(Environment::currentlyOn(ThreadType::Map));
+ triggerUpdate();
+ });
}
}
void Map::prepare() {
+ assert(Environment::currentlyOn(ThreadType::Map));
+
if (!style) {
style = std::make_shared<Style>();
- fileSource.request({ Resource::Kind::JSON, styleURL}, **loop, [&](const Response &res) {
+ env->request({ Resource::Kind::JSON, styleURL}, [&](const Response &res) {
if (res.status == Response::Successful) {
// Calculate the base
const size_t pos = styleURL.rfind('/');
@@ -684,14 +741,19 @@ void Map::prepare() {
spriteAtlas->setSprite(getSprite());
updateTiles();
+
+ if (mode == Mode::Continuous) {
+ view.invalidate();
+ }
}
void Map::render() {
+ assert(Environment::currentlyOn(ThreadType::Map));
assert(painter);
painter->render(*style, activeSources,
state, animationTime);
// Schedule another rerender when we definitely need a next frame.
if (transform.needsTransition() || style->hasTransitions()) {
- update();
+ triggerUpdate();
}
}
diff --git a/src/mbgl/map/raster_tile_data.cpp b/src/mbgl/map/raster_tile_data.cpp
index 524475f30c..b8862e6dd8 100644
--- a/src/mbgl/map/raster_tile_data.cpp
+++ b/src/mbgl/map/raster_tile_data.cpp
@@ -4,10 +4,9 @@
using namespace mbgl;
-
-RasterTileData::RasterTileData(Tile::ID const& id_, TexturePool& texturePool, const SourceInfo& source_, FileSource& fileSource_)
- : TileData(id_, source_, fileSource_),
- bucket(texturePool, layout) {
+RasterTileData::RasterTileData(Tile::ID const &id_, TexturePool &texturePool,
+ const SourceInfo &source_)
+ : TileData(id_, source_), bucket(texturePool, layout) {
}
RasterTileData::~RasterTileData() {
@@ -25,7 +24,7 @@ void RasterTileData::parse() {
}
}
-void RasterTileData::render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix) {
+void RasterTileData::render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) {
bucket.render(painter, layer_desc, id, matrix);
}
diff --git a/src/mbgl/map/raster_tile_data.hpp b/src/mbgl/map/raster_tile_data.hpp
index a3c9f08cde..76bc1bb5aa 100644
--- a/src/mbgl/map/raster_tile_data.hpp
+++ b/src/mbgl/map/raster_tile_data.hpp
@@ -17,12 +17,12 @@ class RasterTileData : public TileData {
friend class TileParser;
public:
- RasterTileData(Tile::ID const& id, TexturePool&, const SourceInfo&, FileSource &);
+ RasterTileData(Tile::ID const &id, TexturePool &, const SourceInfo &);
~RasterTileData();
- virtual void parse();
- virtual void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix);
- virtual bool hasData(StyleLayer const& layer_desc) const;
+ void parse() override;
+ void render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) override;
+ bool hasData(StyleLayer const &layer_desc) const override;
protected:
StyleLayoutRaster layout;
diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp
index 508bbe4137..3036342d78 100644
--- a/src/mbgl/map/source.cpp
+++ b/src/mbgl/map/source.cpp
@@ -1,12 +1,14 @@
#include <mbgl/map/source.hpp>
#include <mbgl/map/map.hpp>
+#include <mbgl/map/environment.hpp>
#include <mbgl/map/transform.hpp>
#include <mbgl/renderer/painter.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/raster.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/texture_pool.hpp>
-#include <mbgl/storage/file_source.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
#include <mbgl/util/vec.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/std.hpp>
@@ -19,6 +21,7 @@
#include <mbgl/map/vector_tile_data.hpp>
#include <mbgl/map/raster_tile_data.hpp>
+#include <mbgl/map/live_tile_data.hpp>
#include <algorithm>
@@ -32,7 +35,7 @@ Source::Source(SourceInfo& info_)
// Note: This is a separate function that must be called exactly once after creation
// The reason this isn't part of the constructor is that calling shared_from_this() in
// the constructor fails.
-void Source::load(Map& map, FileSource& fileSource) {
+void Source::load(Map &map, Environment &env) {
if (info.url.empty()) {
loaded = true;
return;
@@ -41,7 +44,7 @@ void Source::load(Map& map, FileSource& fileSource) {
util::ptr<Source> source = shared_from_this();
const std::string url = util::mapbox::normalizeSourceURL(info.url, map.getAccessToken());
- fileSource.request({ Resource::Kind::JSON, url }, **map.loop, [source, &map](const Response &res) {
+ env.request({ Resource::Kind::JSON, url }, [source, &map](const Response &res) {
if (res.status != Response::Successful) {
Log::Warning(Event::General, "Failed to load source TileJSON: %s", res.message.c_str());
return;
@@ -58,8 +61,7 @@ void Source::load(Map& map, FileSource& fileSource) {
source->info.parseTileJSONProperties(d);
source->loaded = true;
- map.update();
-
+ map.triggerUpdate();
});
}
@@ -95,8 +97,8 @@ void Source::drawClippingMasks(Painter &painter) {
}
}
-void Source::render(Painter &painter, util::ptr<StyleLayer> layer_desc) {
- gl::group group(std::string { "layer: " } + layer_desc->id);
+void Source::render(Painter &painter, const StyleLayer &layer_desc) {
+ gl::group group(std::string { "layer: " } + layer_desc.id);
for (const std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair : tiles) {
Tile &tile = *pair.second;
if (tile.data && tile.data->state == TileData::State::parsed) {
@@ -105,7 +107,7 @@ void Source::render(Painter &painter, util::ptr<StyleLayer> layer_desc) {
}
}
-void Source::render(Painter &painter, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) {
+void Source::render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id, const mat4 &matrix) {
auto it = tiles.find(id);
if (it != tiles.end() && it->second->data && it->second->data->state == TileData::State::parsed) {
painter.renderTileLayer(*it->second, layer_desc, matrix);
@@ -153,13 +155,11 @@ TileData::State Source::hasTile(const Tile::ID& id) {
return TileData::State::invalid;
}
-TileData::State Source::addTile(Map& map, uv::worker& worker,
- util::ptr<Style> style,
- GlyphAtlas& glyphAtlas, GlyphStore& glyphStore,
- SpriteAtlas& spriteAtlas, util::ptr<Sprite> sprite,
- FileSource& fileSource, uv_loop_t &loop, TexturePool& texturePool,
- const Tile::ID& id,
- std::function<void ()> callback) {
+TileData::State Source::addTile(Map &map, uv::worker &worker,
+ util::ptr<Style> style, GlyphAtlas &glyphAtlas,
+ GlyphStore &glyphStore, SpriteAtlas &spriteAtlas,
+ util::ptr<Sprite> sprite, TexturePool &texturePool,
+ const Tile::ID &id, std::function<void()> callback) {
const TileData::State state = hasTile(id);
if (state != TileData::State::invalid) {
@@ -187,17 +187,22 @@ TileData::State Source::addTile(Map& map, uv::worker& worker,
if (!new_tile.data) {
// If we don't find working tile data, we're just going to load it.
if (info.type == SourceType::Vector) {
- new_tile.data = std::make_shared<VectorTileData>(normalized_id, map.getMaxZoom(), style,
- glyphAtlas, glyphStore,
- spriteAtlas, sprite,
- info, fileSource);
+ new_tile.data =
+ std::make_shared<VectorTileData>(normalized_id, map.getMaxZoom(), style, glyphAtlas,
+ glyphStore, spriteAtlas, sprite, info);
+ new_tile.data->request(worker, map.getState().getPixelRatio(), callback);
} else if (info.type == SourceType::Raster) {
- new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info, fileSource);
+ new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info);
+ new_tile.data->request(worker, map.getState().getPixelRatio(), callback);
+ } else if (info.type == SourceType::Annotations) {
+ AnnotationManager& annotationManager = map.getAnnotationManager();
+ new_tile.data = std::make_shared<LiveTileData>(normalized_id, annotationManager,
+ map.getMaxZoom(), style, glyphAtlas,
+ glyphStore, spriteAtlas, sprite, info);
+ new_tile.data->reparse(worker, callback);
} else {
throw std::runtime_error("source type not implemented");
}
-
- new_tile.data->request(worker, loop, map.getState().getPixelRatio(), callback);
tile_data.emplace(new_tile.data->id, new_tile.data);
}
@@ -283,14 +288,18 @@ bool Source::findLoadedParent(const Tile::ID& id, int32_t minCoveringZoom, std::
return false;
}
-void Source::update(Map& map, uv::worker& worker,
+void Source::update(Map &map,
+ uv::worker &worker,
util::ptr<Style> style,
- GlyphAtlas& glyphAtlas, GlyphStore& glyphStore,
- SpriteAtlas& spriteAtlas, util::ptr<Sprite> sprite,
- TexturePool& texturePool, FileSource& fileSource, uv_loop_t& loop,
- std::function<void ()> callback) {
- if (!loaded || map.getTime() <= updated)
+ GlyphAtlas &glyphAtlas,
+ GlyphStore &glyphStore,
+ SpriteAtlas &spriteAtlas,
+ util::ptr<Sprite> sprite,
+ TexturePool &texturePool,
+ std::function<void()> callback) {
+ if (!loaded || map.getTime() <= updated) {
return;
+ }
bool changed = false;
@@ -308,11 +317,8 @@ void Source::update(Map& map, uv::worker& worker,
// Add existing child/parent tiles if the actual tile is not yet loaded
for (const Tile::ID& id : required) {
- const TileData::State state = addTile(map, worker, style,
- glyphAtlas, glyphStore,
- spriteAtlas, sprite,
- fileSource, loop, texturePool,
- id, callback);
+ const TileData::State state = addTile(map, worker, style, glyphAtlas, glyphStore,
+ spriteAtlas, sprite, texturePool, id, callback);
if (state != TileData::State::parsed) {
// The tile we require is not yet loaded. Try to find a parent or
@@ -367,4 +373,12 @@ void Source::update(Map& map, uv::worker& worker,
updated = map.getTime();
}
+void Source::invalidateTiles(Map& map, std::vector<Tile::ID>& ids) {
+ for (auto& id : ids) {
+ tiles.erase(id);
+ tile_data.erase(id);
+ }
+ map.triggerUpdate();
+}
+
}
diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp
index 3649837a58..f5ffd29adc 100644
--- a/src/mbgl/map/source.hpp
+++ b/src/mbgl/map/source.hpp
@@ -18,11 +18,11 @@
namespace mbgl {
class Map;
+class Environment;
class GlyphAtlas;
class GlyphStore;
class SpriteAtlas;
class Sprite;
-class FileSource;
class TexturePool;
class Style;
class Painter;
@@ -34,19 +34,16 @@ class Source : public std::enable_shared_from_this<Source>, private util::noncop
public:
Source(SourceInfo&);
- void load(Map&, FileSource&);
- void update(Map&, uv::worker&,
- util::ptr<Style>,
- GlyphAtlas&, GlyphStore&,
- SpriteAtlas&, util::ptr<Sprite>,
- TexturePool&, FileSource&, uv_loop_t& loop,
- std::function<void ()> callback);
+ void load(Map &, Environment &);
+ void update(Map &, uv::worker &, util::ptr<Style>, GlyphAtlas &, GlyphStore &,
+ SpriteAtlas &, util::ptr<Sprite>, TexturePool &, std::function<void()> callback);
+ void invalidateTiles(Map&, std::vector<Tile::ID>&);
void updateMatrices(const mat4 &projMatrix, const TransformState &transform);
void drawClippingMasks(Painter &painter);
size_t getTileCount() const;
- void render(Painter &painter, util::ptr<StyleLayer> layer_desc);
- void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix);
+ void render(Painter &painter, const StyleLayer &layer_desc);
+ void render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id, const mat4 &matrix);
void finishRender(Painter &painter);
std::forward_list<Tile::ID> getIDs() const;
@@ -59,13 +56,9 @@ private:
int32_t coveringZoomLevel(const TransformState&) const;
std::forward_list<Tile::ID> coveringTiles(const TransformState&) const;
- TileData::State addTile(Map&, uv::worker&,
- util::ptr<Style>,
- GlyphAtlas&, GlyphStore&,
- SpriteAtlas&, util::ptr<Sprite>,
- FileSource&, uv_loop_t &, TexturePool&,
- const Tile::ID&,
- std::function<void ()> callback);
+ TileData::State addTile(Map &, uv::worker &, util::ptr<Style>, GlyphAtlas &,
+ GlyphStore &, SpriteAtlas &, util::ptr<Sprite>, TexturePool &,
+ const Tile::ID &, std::function<void()> callback);
TileData::State hasTile(const Tile::ID& id);
diff --git a/src/mbgl/map/sprite.cpp b/src/mbgl/map/sprite.cpp
index 9543fe083a..8883ee092d 100644
--- a/src/mbgl/map/sprite.cpp
+++ b/src/mbgl/map/sprite.cpp
@@ -5,7 +5,9 @@
#include <string>
#include <mbgl/platform/platform.hpp>
-#include <mbgl/storage/file_source.hpp>
+#include <mbgl/map/environment.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/util/std.hpp>
@@ -22,15 +24,15 @@ SpritePosition::SpritePosition(uint16_t x_, uint16_t y_, uint16_t width_, uint16
sdf(sdf_) {
}
-util::ptr<Sprite> Sprite::Create(const std::string& base_url, float pixelRatio, FileSource& fileSource) {
+util::ptr<Sprite> Sprite::Create(const std::string &base_url, float pixelRatio, Environment &env) {
util::ptr<Sprite> sprite(std::make_shared<Sprite>(Key(), base_url, pixelRatio));
- sprite->load(fileSource);
+ sprite->load(env);
return sprite;
}
Sprite::Sprite(const Key &, const std::string& base_url, float pixelRatio_)
: valid(base_url.length() > 0),
- pixelRatio(pixelRatio_),
+ pixelRatio(pixelRatio_ > 1 ? 2 : 1),
spriteURL(base_url + (pixelRatio_ > 1 ? "@2x" : "") + ".png"),
jsonURL(base_url + (pixelRatio_ > 1 ? "@2x" : "") + ".json"),
raster(),
@@ -39,6 +41,11 @@ Sprite::Sprite(const Key &, const std::string& base_url, float pixelRatio_)
future(promise.get_future()) {
}
+bool Sprite::hasPixelRatio(float ratio) const {
+ return pixelRatio == (ratio > 1 ? 2 : 1);
+}
+
+
void Sprite::waitUntilLoaded() const {
future.wait();
}
@@ -51,8 +58,7 @@ Sprite::operator bool() const {
// Note: This is a separate function that must be called exactly once after creation
// The reason this isn't part of the constructor is that calling shared_from_this() in
// the constructor fails.
-void Sprite::load(FileSource& fileSource) {
-
+void Sprite::load(Environment &env) {
if (!valid) {
// Treat a non-existent sprite as a successfully loaded empty sprite.
loadedImage = true;
@@ -63,30 +69,26 @@ void Sprite::load(FileSource& fileSource) {
util::ptr<Sprite> sprite = shared_from_this();
- fileSource.request({ Resource::Kind::JSON, jsonURL }, [sprite](const Response &res) {
+ env.request({ Resource::Kind::JSON, jsonURL }, [sprite](const Response &res) {
if (res.status == Response::Successful) {
sprite->body = res.data;
sprite->parseJSON();
- sprite->complete();
} else {
Log::Warning(Event::Sprite, "Failed to load sprite info: %s", res.message.c_str());
- if (!sprite->future.valid()) {
- sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message)));
- }
}
+ sprite->loadedJSON = true;
+ sprite->complete();
});
- fileSource.request({ Resource::Kind::Image, spriteURL }, [sprite](const Response &res) {
+ env.request({ Resource::Kind::Image, spriteURL }, [sprite](const Response &res) {
if (res.status == Response::Successful) {
sprite->image = res.data;
sprite->parseImage();
- sprite->complete();
} else {
Log::Warning(Event::Sprite, "Failed to load sprite image: %s", res.message.c_str());
- if (!sprite->future.valid()) {
- sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message)));
- }
}
+ sprite->loadedImage = true;
+ sprite->complete();
});
}
@@ -106,7 +108,6 @@ void Sprite::parseImage() {
raster.reset();
}
image.clear();
- loadedImage = true;
}
void Sprite::parseJSON() {
@@ -141,8 +142,6 @@ void Sprite::parseJSON() {
} else {
Log::Warning(Event::Sprite, "sprite JSON root is not an object");
}
-
- loadedJSON = true;
}
const SpritePosition &Sprite::getSpritePosition(const std::string& name) const {
diff --git a/src/mbgl/map/sprite.hpp b/src/mbgl/map/sprite.hpp
index d4b54ba1b5..6c1b3ba8e3 100644
--- a/src/mbgl/map/sprite.hpp
+++ b/src/mbgl/map/sprite.hpp
@@ -14,7 +14,7 @@
namespace mbgl {
-class FileSource;
+class Environment;
class SpritePosition {
public:
@@ -34,14 +34,17 @@ public:
class Sprite : public std::enable_shared_from_this<Sprite>, private util::noncopyable {
private:
struct Key {};
- void load(FileSource& fileSource);
+ void load(Environment &env);
public:
Sprite(const Key &, const std::string& base_url, float pixelRatio);
- static util::ptr<Sprite> Create(const std::string& base_url, float pixelRatio, FileSource& fileSource);
+ static util::ptr<Sprite>
+ Create(const std::string &base_url, float pixelRatio, Environment &env);
const SpritePosition &getSpritePosition(const std::string& name) const;
+ bool hasPixelRatio(float ratio) const;
+
void waitUntilLoaded() const;
bool isLoaded() const;
diff --git a/src/mbgl/map/tile_data.cpp b/src/mbgl/map/tile_data.cpp
index 9d48041239..aed182671b 100644
--- a/src/mbgl/map/tile_data.cpp
+++ b/src/mbgl/map/tile_data.cpp
@@ -1,21 +1,23 @@
#include <mbgl/map/tile_data.hpp>
#include <mbgl/map/map.hpp>
+#include <mbgl/map/environment.hpp>
#include <mbgl/style/style_source.hpp>
#include <mbgl/util/token.hpp>
#include <mbgl/util/string.hpp>
+#include <mbgl/util/mapbox.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/platform/log.hpp>
using namespace mbgl;
-TileData::TileData(Tile::ID const& id_, const SourceInfo& source_, FileSource& fileSource_)
+TileData::TileData(Tile::ID const& id_, const SourceInfo& source_)
: id(id_),
name(id),
state(State::initial),
source(source_),
- fileSource(fileSource_),
+ env(Environment::Get()),
debugBucket(debugFontBuffer) {
// Initialize tile debug coordinates
debugFontBuffer.addText(name.c_str(), 50, 200, 5);
@@ -23,7 +25,7 @@ TileData::TileData(Tile::ID const& id_, const SourceInfo& source_, FileSource& f
TileData::~TileData() {
if (req) {
- fileSource.cancel(req);
+ env.cancelRequest(req);
}
}
@@ -31,12 +33,12 @@ const std::string TileData::toString() const {
return std::string { "[tile " } + name + "]";
}
-void TileData::request(uv::worker &worker, uv_loop_t &loop,
- float pixelRatio, std::function<void()> callback) {
+void TileData::request(uv::worker &worker, float pixelRatio, std::function<void()> callback) {
if (source.tiles.empty())
return;
std::string url = source.tiles[(id.x + id.y) % source.tiles.size()];
+ url = util::mapbox::normalizeTileURL(url, source.url, source.type);
url = util::replaceTokens(url, [&](const std::string &token) -> std::string {
if (token == "z") return util::toString(id.z);
if (token == "x") return util::toString(id.x);
@@ -53,9 +55,8 @@ void TileData::request(uv::worker &worker, uv_loop_t &loop,
state = State::loading;
- // Note: Somehow this feels slower than the change to request_http()
std::weak_ptr<TileData> weak_tile = shared_from_this();
- req = fileSource.request({ Resource::Kind::Tile, url }, &loop, [weak_tile, url, callback, &worker](const Response &res) {
+ req = env.request({ Resource::Kind::Tile, url }, [weak_tile, url, callback, &worker](const Response &res) {
util::ptr<TileData> tile = weak_tile.lock();
if (!tile || tile->state == State::obsolete) {
// noop. Tile is obsolete and we're now just waiting for the refcount
@@ -84,7 +85,7 @@ void TileData::cancel() {
state = State::obsolete;
}
if (req) {
- fileSource.cancel(req);
+ env.cancelRequest(req);
req = nullptr;
}
}
@@ -95,7 +96,8 @@ void TileData::reparse(uv::worker& worker, std::function<void()> callback)
// the after work handler
new uv::work<util::ptr<TileData>>(
worker,
- [](util::ptr<TileData>& tile) {
+ [this](util::ptr<TileData>& tile) {
+ Environment::Scope scope(env, ThreadType::TileWorker, "TileWorker_" + tile->name);
tile->parse();
},
[callback](util::ptr<TileData>&) {
diff --git a/src/mbgl/map/tile_data.hpp b/src/mbgl/map/tile_data.hpp
index a83a4648dd..cce346d835 100644
--- a/src/mbgl/map/tile_data.hpp
+++ b/src/mbgl/map/tile_data.hpp
@@ -23,7 +23,7 @@ typedef struct uv_loop_s uv_loop_t;
namespace mbgl {
class Map;
-class FileSource;
+class Environment;
class Painter;
class SourceInfo;
class StyleLayer;
@@ -48,10 +48,10 @@ public:
};
public:
- TileData(Tile::ID const& id, const SourceInfo&, FileSource&);
+ TileData(Tile::ID const &id, const SourceInfo &);
~TileData();
- void request(uv::worker&, uv_loop_t&, float pixelRatio, std::function<void ()> callback);
+ void request(uv::worker&, float pixelRatio, std::function<void ()> callback);
void reparse(uv::worker&, std::function<void ()> callback);
void cancel();
const std::string toString() const;
@@ -62,9 +62,8 @@ public:
// Override this in the child class.
virtual void parse() = 0;
- virtual void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix) = 0;
- virtual bool hasData(StyleLayer const& layer_desc) const = 0;
-
+ virtual void render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) = 0;
+ virtual bool hasData(StyleLayer const &layer_desc) const = 0;
public:
const Tile::ID id;
@@ -73,7 +72,7 @@ public:
public:
const SourceInfo& source;
- FileSource& fileSource;
+ Environment& env;
protected:
Request *req = nullptr;
diff --git a/src/mbgl/map/tile_parser.cpp b/src/mbgl/map/tile_parser.cpp
index 43e754978b..fa7f4c093f 100644
--- a/src/mbgl/map/tile_parser.cpp
+++ b/src/mbgl/map/tile_parser.cpp
@@ -1,5 +1,6 @@
#include <mbgl/map/tile_parser.hpp>
#include <mbgl/map/vector_tile_data.hpp>
+#include <mbgl/platform/log.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/style/style_layer_group.hpp>
@@ -16,7 +17,6 @@
#include <mbgl/text/collision.hpp>
#include <mbgl/text/glyph.hpp>
#include <mbgl/map/map.hpp>
-
#include <mbgl/util/std.hpp>
#include <mbgl/util/utf.hpp>
@@ -29,14 +29,14 @@ namespace mbgl {
// its header file.
TileParser::~TileParser() = default;
-TileParser::TileParser(const std::string& rawData_,
+TileParser::TileParser(const GeometryTile& geometryTile_,
VectorTileData& tile_,
const util::ptr<const Style>& style_,
GlyphAtlas& glyphAtlas_,
GlyphStore& glyphStore_,
SpriteAtlas& spriteAtlas_,
const util::ptr<Sprite>& sprite_)
- : vectorTile(pbf((const uint8_t *)rawData_.data(), rawData_.size())),
+ : geometryTile(geometryTile_),
tile(tile_),
style(style_),
glyphAtlas(glyphAtlas_),
@@ -85,7 +85,7 @@ void TileParser::parseStyleLayers(util::ptr<const StyleLayerGroup> group) {
}
}
} else {
- fprintf(stderr, "[WARNING] layer '%s' does not have buckets\n", layer_desc->id.c_str());
+ Log::Warning(Event::ParseTile, "layer '%s' does not have buckets", layer_desc->id.c_str());
}
}
}
@@ -187,24 +187,24 @@ std::unique_ptr<Bucket> TileParser::createBucket(const StyleBucket &bucketDesc)
if (tile.id.z >= std::ceil(bucketDesc.max_zoom)) return nullptr;
if (bucketDesc.visibility == mbgl::VisibilityType::None) return nullptr;
- auto layer_it = vectorTile.layers.find(bucketDesc.source_layer);
- if (layer_it != vectorTile.layers.end()) {
- const VectorTileLayer &layer = layer_it->second;
+ auto layer = geometryTile.getLayer(bucketDesc.source_layer);
+ if (layer) {
if (bucketDesc.type == StyleLayerType::Fill) {
- return createFillBucket(layer, bucketDesc);
+ return createFillBucket(*layer, bucketDesc);
} else if (bucketDesc.type == StyleLayerType::Line) {
- return createLineBucket(layer, bucketDesc);
+ return createLineBucket(*layer, bucketDesc);
} else if (bucketDesc.type == StyleLayerType::Symbol) {
- return createSymbolBucket(layer, bucketDesc);
+ return createSymbolBucket(*layer, bucketDesc);
} else if (bucketDesc.type == StyleLayerType::Raster) {
return nullptr;
} else {
- fprintf(stderr, "[WARNING] unknown bucket render type for layer '%s' (source layer '%s')\n", bucketDesc.name.c_str(), bucketDesc.source_layer.c_str());
+ Log::Warning(Event::ParseTile, "unknown bucket render type for layer '%s' (source layer '%s')",
+ bucketDesc.name.c_str(), bucketDesc.source_layer.c_str());
}
} else {
// The layer specified in the bucket does not exist. Do nothing.
if (debug::tileParseWarnings) {
- fprintf(stderr, "[WARNING] layer '%s' does not exist in tile %d/%d/%d\n",
+ Log::Warning(Event::ParseTile, "layer '%s' does not exist in tile %d/%d/%d",
bucketDesc.source_layer.c_str(), tile.id.z, tile.id.x, tile.id.y);
}
}
@@ -213,25 +213,23 @@ std::unique_ptr<Bucket> TileParser::createBucket(const StyleBucket &bucketDesc)
}
template <class Bucket>
-void TileParser::addBucketGeometries(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter) {
- FilteredVectorTileLayer filtered_layer(layer, filter);
- for (pbf feature : filtered_layer) {
+void TileParser::addBucketGeometries(Bucket& bucket, const GeometryTileLayer& layer, const FilterExpression &filter) {
+ for (std::size_t i = 0; i < layer.featureCount(); i++) {
+ auto feature = layer.getFeature(i);
+
if (obsolete())
return;
- while (feature.next(4)) { // geometry
- pbf geometry_pbf = feature.message();
- if (geometry_pbf) {
- bucket->addGeometry(geometry_pbf);
- } else if (debug::tileParseWarnings) {
- fprintf(stderr, "[WARNING] geometry is empty\n");
- }
- }
+ GeometryTileFeatureExtractor extractor(*feature);
+ if (!evaluate(filter, extractor))
+ continue;
+
+ bucket->addGeometry(feature->getGeometries());
}
}
-std::unique_ptr<Bucket> TileParser::createFillBucket(const VectorTileLayer &layer,
- const StyleBucket &bucket_desc) {
+std::unique_ptr<Bucket> TileParser::createFillBucket(const GeometryTileLayer& layer,
+ const StyleBucket& bucket_desc) {
auto fill = parseStyleLayoutFill(bucket_desc, tile.id.z);
auto bucket = util::make_unique<FillBucket>(std::move(fill),
tile.fillVertexBuffer,
@@ -241,8 +239,8 @@ std::unique_ptr<Bucket> TileParser::createFillBucket(const VectorTileLayer &laye
return obsolete() ? nullptr : std::move(bucket);
}
-std::unique_ptr<Bucket> TileParser::createLineBucket(const VectorTileLayer &layer,
- const StyleBucket &bucket_desc) {
+std::unique_ptr<Bucket> TileParser::createLineBucket(const GeometryTileLayer& layer,
+ const StyleBucket& bucket_desc) {
auto line = parseStyleLayoutLine(bucket_desc, tile.id.z);
auto bucket = util::make_unique<LineBucket>(std::move(line),
tile.lineVertexBuffer,
@@ -252,12 +250,12 @@ std::unique_ptr<Bucket> TileParser::createLineBucket(const VectorTileLayer &laye
return obsolete() ? nullptr : std::move(bucket);
}
-std::unique_ptr<Bucket> TileParser::createSymbolBucket(const VectorTileLayer &layer,
- const StyleBucket &bucket_desc) {
+std::unique_ptr<Bucket> TileParser::createSymbolBucket(const GeometryTileLayer& layer,
+ const StyleBucket& bucket_desc) {
auto symbol = parseStyleLayoutSymbol(bucket_desc, tile.id.z);
auto bucket = util::make_unique<SymbolBucket>(std::move(symbol), *collision);
bucket->addFeatures(
- layer, bucket_desc.filter, tile.id, spriteAtlas, *sprite, glyphAtlas, glyphStore);
+ layer, bucket_desc.filter, reinterpret_cast<uintptr_t>(&tile), spriteAtlas, *sprite, glyphAtlas, glyphStore);
return obsolete() ? nullptr : std::move(bucket);
}
}
diff --git a/src/mbgl/map/tile_parser.hpp b/src/mbgl/map/tile_parser.hpp
index 228557846c..0ad42fdc91 100644
--- a/src/mbgl/map/tile_parser.hpp
+++ b/src/mbgl/map/tile_parser.hpp
@@ -1,6 +1,7 @@
#ifndef MBGL_MAP_TILE_PARSER
#define MBGL_MAP_TILE_PARSER
+#include <mbgl/map/geometry_tile.hpp>
#include <mbgl/map/vector_tile.hpp>
#include <mbgl/style/filter_expression.hpp>
#include <mbgl/style/class_properties.hpp>
@@ -8,6 +9,7 @@
#include <mbgl/text/glyph.hpp>
#include <mbgl/util/ptr.hpp>
#include <mbgl/util/noncopyable.hpp>
+
#include <cstdint>
#include <iosfwd>
#include <string>
@@ -15,7 +17,6 @@
namespace mbgl {
class Bucket;
-class TexturePool;
class FontStack;
class GlyphAtlas;
class GlyphStore;
@@ -30,12 +31,10 @@ class StyleLayoutSymbol;
class StyleLayerGroup;
class VectorTileData;
class Collision;
-class TexturePool;
-class TileParser : private util::noncopyable
-{
+class TileParser : private util::noncopyable {
public:
- TileParser(const std::string& rawData,
+ TileParser(const GeometryTile& geometryTile,
VectorTileData& tile,
const util::ptr<const Style>& style,
GlyphAtlas& glyphAtlas,
@@ -51,15 +50,16 @@ private:
bool obsolete() const;
void parseStyleLayers(util::ptr<const StyleLayerGroup> group);
- std::unique_ptr<Bucket> createBucket(const StyleBucket& bucketDesc);
- std::unique_ptr<Bucket> createFillBucket(const VectorTileLayer& layer, const StyleBucket& bucketDesc);
- std::unique_ptr<Bucket> createLineBucket(const VectorTileLayer& layer, const StyleBucket& bucketDesc);
- std::unique_ptr<Bucket> createSymbolBucket(const VectorTileLayer& layer, const StyleBucket& bucketDesc);
+ std::unique_ptr<Bucket> createBucket(const StyleBucket&);
+ std::unique_ptr<Bucket> createFillBucket(const GeometryTileLayer&, const StyleBucket&);
+ std::unique_ptr<Bucket> createLineBucket(const GeometryTileLayer&, const StyleBucket&);
+ std::unique_ptr<Bucket> createSymbolBucket(const GeometryTileLayer&, const StyleBucket&);
- template <class Bucket> void addBucketGeometries(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression& filter);
+ template <class Bucket>
+ void addBucketGeometries(Bucket&, const GeometryTileLayer&, const FilterExpression&);
private:
- const VectorTile vectorTile;
+ const GeometryTile& geometryTile;
VectorTileData& tile;
// Cross-thread shared data.
diff --git a/src/mbgl/map/vector_tile.cpp b/src/mbgl/map/vector_tile.cpp
index ac7134fb0c..dca2168a47 100644
--- a/src/mbgl/map/vector_tile.cpp
+++ b/src/mbgl/map/vector_tile.cpp
@@ -1,29 +1,42 @@
#include <mbgl/map/vector_tile.hpp>
-#include <mbgl/style/filter_expression_private.hpp>
-#include <algorithm>
-#include <iostream>
-
-using namespace mbgl;
-
-
-std::ostream& mbgl::operator<<(std::ostream& os, const FeatureType& type) {
- switch (type) {
- case FeatureType::Unknown: return os << "Unknown";
- case FeatureType::Point: return os << "Point";
- case FeatureType::LineString: return os << "LineString";
- case FeatureType::Polygon: return os << "Polygon";
- default: return os << "Invalid";
+namespace mbgl {
+
+Value parseValue(pbf data) {
+ while (data.next())
+ {
+ switch (data.tag)
+ {
+ case 1: // string_value
+ return data.string();
+ case 2: // float_value
+ return static_cast<double>(data.float32());
+ case 3: // double_value
+ return data.float64();
+ case 4: // int_value
+ return data.varint<int64_t>();
+ case 5: // uint_value
+ return data.varint<uint64_t>();
+ case 6: // sint_value
+ return data.svarint<int64_t>();
+ case 7: // bool_value
+ return data.boolean();
+ default:
+ data.skip();
+ break;
+ }
}
+ return false;
}
-VectorTileFeature::VectorTileFeature(pbf feature, const VectorTileLayer& layer) {
- while (feature.next()) {
- if (feature.tag == 1) { // id
- id = feature.varint<uint64_t>();
- } else if (feature.tag == 2) { // tags
+VectorTileFeature::VectorTileFeature(pbf feature_pbf, const VectorTileLayer& layer_)
+ : layer(layer_) {
+ while (feature_pbf.next()) {
+ if (feature_pbf.tag == 1) { // id
+ id = feature_pbf.varint<uint64_t>();
+ } else if (feature_pbf.tag == 2) { // tags
// tags are packed varints. They should have an even length.
- pbf tags = feature.message();
+ pbf tags = feature_pbf.message();
while (tags) {
uint32_t tag_key = tags.varint();
@@ -31,184 +44,123 @@ VectorTileFeature::VectorTileFeature(pbf feature, const VectorTileLayer& layer)
throw std::runtime_error("feature referenced out of range key");
}
- if (tags) {
- uint32_t tag_val = tags.varint();
- if (layer.values.size() <= tag_val) {
- throw std::runtime_error("feature referenced out of range value");
- }
-
- properties.emplace(layer.keys[tag_key], layer.values[tag_val]);
- } else {
+ if (!tags) {
throw std::runtime_error("uneven number of feature tag ids");
}
- }
- } else if (feature.tag == 3) { // type
- type = (FeatureType)feature.varint();
- } else if (feature.tag == 4) { // geometry
- geometry = feature.message();
- } else {
- feature.skip();
- }
- }
-}
-
-
-std::ostream& mbgl::operator<<(std::ostream& os, const VectorTileFeature& feature) {
- os << "Feature(" << feature.id << "): " << feature.type << std::endl;
- for (const auto& prop : feature.properties) {
- os << " - " << prop.first << ": " << prop.second << std::endl;
- }
- return os;
-}
-
-
-VectorTile::VectorTile() {}
+ uint32_t tag_val = tags.varint();
+ if (layer.values.size() <= tag_val) {
+ throw std::runtime_error("feature referenced out of range value");
+ }
-VectorTile::VectorTile(pbf tile) {
- while (tile.next()) {
- if (tile.tag == 3) { // layer
- VectorTileLayer layer(tile.message());
- layers.emplace(layer.name, std::forward<VectorTileLayer>(layer));
+ properties.emplace(tag_key, tag_val);
+ }
+ } else if (feature_pbf.tag == 3) { // type
+ type = (FeatureType)feature_pbf.varint();
+ } else if (feature_pbf.tag == 4) { // geometry
+ geometry_pbf = feature_pbf.message();
} else {
- tile.skip();
+ feature_pbf.skip();
}
}
}
-VectorTile& VectorTile::operator=(VectorTile && other) {
- if (this != &other) {
- layers.swap(other.layers);
+mapbox::util::optional<Value> VectorTileFeature::getValue(const std::string& key) const {
+ auto keyIter = layer.keys.find(key);
+ if (keyIter == layer.keys.end()) {
+ return mapbox::util::optional<Value>();
}
- return *this;
-}
-
-VectorTileLayer::VectorTileLayer(pbf layer) : data(layer) {
- std::vector<std::string> stacks;
-
- while (layer.next()) {
- if (layer.tag == 1) { // name
- name = layer.string();
- } else if (layer.tag == 3) { // keys
- keys.emplace_back(layer.string());
- key_index.emplace(keys.back(), keys.size() - 1);
- } else if (layer.tag == 4) { // values
- values.emplace_back(std::move(parseValue(layer.message())));
- } else if (layer.tag == 5) { // extent
- extent = layer.varint();
- } else {
- layer.skip();
- }
+ auto propIter = properties.find(keyIter->second);
+ if (propIter == properties.end()) {
+ return mapbox::util::optional<Value>();
}
+ return layer.values[propIter->second];
}
-FilteredVectorTileLayer::FilteredVectorTileLayer(const VectorTileLayer& layer_, const FilterExpression &filterExpression_)
- : layer(layer_),
- filterExpression(filterExpression_) {
-}
+GeometryCollection VectorTileFeature::getGeometries() const {
+ pbf data(geometry_pbf);
+ uint8_t cmd = 1;
+ uint32_t length = 0;
+ int32_t x = 0;
+ int32_t y = 0;
-FilteredVectorTileLayer::iterator FilteredVectorTileLayer::begin() const {
- return iterator(*this, layer.data);
-}
+ GeometryCollection lines;
-FilteredVectorTileLayer::iterator FilteredVectorTileLayer::end() const {
- return iterator(*this, pbf(layer.data.end, 0));
-}
+ lines.emplace_back();
+ std::vector<Coordinate>* line = &lines.back();
-FilteredVectorTileLayer::iterator::iterator(const FilteredVectorTileLayer& parent_, const pbf& data_)
- : parent(parent_),
- feature(pbf()),
- data(data_) {
- operator++();
-}
+ while (data.data < data.end) {
+ if (length == 0) {
+ uint32_t cmd_length = data.varint();
+ cmd = cmd_length & 0x7;
+ length = cmd_length >> 3;
+ }
-VectorTileTagExtractor::VectorTileTagExtractor(const VectorTileLayer &layer) : layer_(layer) {}
+ --length;
+ if (cmd == 1 || cmd == 2) {
+ x += data.svarint();
+ y += data.svarint();
-void VectorTileTagExtractor::setTags(const pbf &pbf) {
- tags_ = pbf;
-}
+ if (cmd == 1 && !line->empty()) { // moveTo
+ lines.emplace_back();
+ line = &lines.back();
+ }
-mapbox::util::optional<Value> VectorTileTagExtractor::getValue(const std::string &key) const {
- if (key == "$type") {
- return Value(uint64_t(type_));
- }
+ line->emplace_back(x, y);
- mapbox::util::optional<Value> value;
-
- auto field_it = layer_.key_index.find(key);
- if (field_it != layer_.key_index.end()) {
- const uint32_t filter_key = field_it->second;
-
- // Now loop through all the key/value pair tags.
- // tags are packed varints. They should have an even length.
- pbf tags_pbf = tags_;
- uint32_t tag_key, tag_val;
- while (tags_pbf) {
- tag_key = tags_pbf.varint();
- if (!tags_pbf) {
- // This should not happen; otherwise the vector tile is invalid.
- fprintf(stderr, "[WARNING] uneven number of feature tag ids\n");
- return value;
- }
- // Note: We need to run this command in all cases, even if the keys don't match.
- tag_val = tags_pbf.varint();
-
- if (tag_key == filter_key) {
- if (layer_.values.size() > tag_val) {
- value = layer_.values[tag_val];
- } else {
- fprintf(stderr, "[WARNING] feature references out of range value\n");
- break;
- }
+ } else if (cmd == 7) { // closePolygon
+ if (!line->empty()) {
+ line->push_back((*line)[0]);
}
+
+ } else {
+ throw std::runtime_error("unknown command");
}
}
- return value;
+ return lines;
}
-void VectorTileTagExtractor::setType(FeatureType type) {
- type_ = type;
-}
-
-template bool mbgl::evaluate(const FilterExpression&, const VectorTileTagExtractor&);
-
-void FilteredVectorTileLayer::iterator::operator++() {
- valid = false;
-
- const FilterExpression &expression = parent.filterExpression;
-
- while (data.next(2)) { // feature
- feature = data.message();
- pbf feature_pbf = feature;
-
- VectorTileTagExtractor extractor(parent.layer);
-
- // Retrieve the basic information
- while (feature_pbf.next()) {
- if (feature_pbf.tag == 2) { // tags
- extractor.setTags(feature_pbf.message());
- } else if (feature_pbf.tag == 3) { // geometry type
- extractor.setType(FeatureType(feature_pbf.varint()));
- } else {
- feature_pbf.skip();
- }
+VectorTile::VectorTile(pbf tile_pbf) {
+ while (tile_pbf.next()) {
+ if (tile_pbf.tag == 3) { // layer
+ util::ptr<VectorTileLayer> layer = std::make_shared<VectorTileLayer>(tile_pbf.message());
+ layers.emplace(layer->name, layer);
+ } else {
+ tile_pbf.skip();
}
+ }
+}
- if (evaluate(expression, extractor)) {
- valid = true;
- return; // data loop
+util::ptr<GeometryTileLayer> VectorTile::getLayer(const std::string& name) const {
+ auto layer_it = layers.find(name);
+ if (layer_it != layers.end()) {
+ return layer_it->second;
+ }
+ return nullptr;
+}
+
+VectorTileLayer::VectorTileLayer(pbf layer_pbf) {
+ while (layer_pbf.next()) {
+ if (layer_pbf.tag == 1) { // name
+ name = layer_pbf.string();
+ } else if (layer_pbf.tag == 2) { // feature
+ features.push_back(layer_pbf.message());
+ } else if (layer_pbf.tag == 3) { // keys
+ keys.emplace(layer_pbf.string(), keys.size());
+ } else if (layer_pbf.tag == 4) { // values
+ values.emplace_back(std::move(parseValue(layer_pbf.message())));
+ } else if (layer_pbf.tag == 5) { // extent
+ extent = layer_pbf.varint();
} else {
- valid = false;
+ layer_pbf.skip();
}
}
}
-bool FilteredVectorTileLayer::iterator::operator!=(const iterator& other) const {
- return !(data.data == other.data.data && data.end == other.data.end && valid == other.valid);
+util::ptr<const GeometryTileFeature> VectorTileLayer::getFeature(std::size_t i) const {
+ return std::make_shared<VectorTileFeature>(features.at(i), *this);
}
-const pbf& FilteredVectorTileLayer::iterator:: operator*() const {
- return feature;
}
diff --git a/src/mbgl/map/vector_tile.hpp b/src/mbgl/map/vector_tile.hpp
index 2d02ba3a0b..9a23c3b189 100644
--- a/src/mbgl/map/vector_tile.hpp
+++ b/src/mbgl/map/vector_tile.hpp
@@ -1,117 +1,58 @@
#ifndef MBGL_MAP_VECTOR_TILE
#define MBGL_MAP_VECTOR_TILE
-#include <mbgl/style/filter_expression.hpp>
-#include <mbgl/style/value.hpp>
-#include <mbgl/text/glyph.hpp>
+#include <mbgl/map/geometry_tile.hpp>
#include <mbgl/util/pbf.hpp>
-#include <mbgl/util/optional.hpp>
-#include <cstdint>
-#include <iosfwd>
-#include <map>
-#include <string>
#include <unordered_map>
-#include <vector>
namespace mbgl {
class VectorTileLayer;
-enum class FeatureType {
- Unknown = 0,
- Point = 1,
- LineString = 2,
- Polygon = 3
-};
-
-std::ostream& operator<<(std::ostream&, const FeatureType& type);
-
-class VectorTileFeature {
+class VectorTileFeature : public GeometryTileFeature {
public:
- VectorTileFeature(pbf feature, const VectorTileLayer& layer);
-
- uint64_t id = 0;
- FeatureType type = FeatureType::Unknown;
- std::map<std::string, Value> properties;
- pbf geometry;
-};
+ VectorTileFeature(pbf, const VectorTileLayer&);
-std::ostream& operator<<(std::ostream&, const VectorTileFeature& feature);
-
-
-class VectorTileTagExtractor {
-public:
- VectorTileTagExtractor(const VectorTileLayer &layer);
-
- void setTags(const pbf &pbf);
- mapbox::util::optional<Value> getValue(const std::string &key) const;
- void setType(FeatureType type);
- FeatureType getType() const;
+ FeatureType getType() const override { return type; }
+ mapbox::util::optional<Value> getValue(const std::string&) const override;
+ GeometryCollection getGeometries() const override;
private:
- const VectorTileLayer &layer_;
- pbf tags_;
- FeatureType type_ = FeatureType::Unknown;
+ const VectorTileLayer& layer;
+ uint64_t id = 0;
+ FeatureType type = FeatureType::Unknown;
+ std::unordered_map<uint32_t, uint32_t> properties;
+ pbf geometry_pbf;
};
-/*
- * Allows iterating over the features of a VectorTileLayer using a
- * BucketDescription as filter. Only features matching the descriptions will
- * be returned (as pbf).
- */
-class FilteredVectorTileLayer {
-public:
- class iterator {
- public:
- iterator(const FilteredVectorTileLayer& filter, const pbf& data);
- void operator++();
- bool operator!=(const iterator& other) const;
- const pbf& operator*() const;
-
- private:
- const FilteredVectorTileLayer& parent;
- bool valid = false;
- pbf feature;
- pbf data;
- };
-
+class VectorTileLayer : public GeometryTileLayer {
public:
- FilteredVectorTileLayer(const VectorTileLayer& layer, const FilterExpression &filterExpression);
+ VectorTileLayer(pbf);
- iterator begin() const;
- iterator end() const;
+ std::size_t featureCount() const override { return features.size(); }
+ util::ptr<const GeometryTileFeature> getFeature(std::size_t) const override;
private:
- const VectorTileLayer& layer;
- const FilterExpression& filterExpression;
-};
-
-std::ostream& operator<<(std::ostream&, const PositionedGlyph& placement);
+ friend class VectorTile;
+ friend class VectorTileFeature;
-class VectorTileLayer {
-public:
- VectorTileLayer(pbf data);
-
- const pbf data;
std::string name;
uint32_t extent = 4096;
- std::vector<std::string> keys;
- std::unordered_map<std::string, uint32_t> key_index;
+ std::unordered_map<std::string, uint32_t> keys;
std::vector<Value> values;
- std::map<std::string, std::map<Value, Shaping>> shaping;
+ std::vector<pbf> features;
};
-class VectorTile {
+class VectorTile : public GeometryTile {
public:
- VectorTile();
- VectorTile(pbf data);
- VectorTile& operator=(VectorTile&& other);
-
- std::map<std::string, const VectorTileLayer> layers;
-};
+ VectorTile(pbf);
+ util::ptr<GeometryTileLayer> getLayer(const std::string&) const override;
+private:
+ std::unordered_map<std::string, util::ptr<GeometryTileLayer>> layers;
+};
}
diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp
index 5f6b249070..3bed7a1c32 100644
--- a/src/mbgl/map/vector_tile_data.cpp
+++ b/src/mbgl/map/vector_tile_data.cpp
@@ -1,20 +1,24 @@
#include <mbgl/map/vector_tile_data.hpp>
#include <mbgl/map/tile_parser.hpp>
#include <mbgl/util/std.hpp>
-#include <mbgl/map/map.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/style/style_bucket.hpp>
+#include <mbgl/style/style_source.hpp>
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/platform/log.hpp>
+#include <mbgl/util/pbf.hpp>
using namespace mbgl;
VectorTileData::VectorTileData(Tile::ID const& id_,
- float mapMaxZoom, util::ptr<Style> style_,
- GlyphAtlas& glyphAtlas_, GlyphStore& glyphStore_,
- SpriteAtlas& spriteAtlas_, util::ptr<Sprite> sprite_,
- const SourceInfo& source_, FileSource &fileSource_)
- : TileData(id_, source_, fileSource_),
+ float mapMaxZoom,
+ util::ptr<Style> style_,
+ GlyphAtlas& glyphAtlas_,
+ GlyphStore& glyphStore_,
+ SpriteAtlas& spriteAtlas_,
+ util::ptr<Sprite> sprite_,
+ const SourceInfo& source_)
+ : TileData(id_, source_),
glyphAtlas(glyphAtlas_),
glyphStore(glyphStore_),
spriteAtlas(spriteAtlas_),
@@ -24,10 +28,9 @@ VectorTileData::VectorTileData(Tile::ID const& id_,
}
VectorTileData::~VectorTileData() {
- glyphAtlas.removeGlyphs(id.to_uint64());
+ glyphAtlas.removeGlyphs(reinterpret_cast<uintptr_t>(this));
}
-
void VectorTileData::parse() {
if (state != State::loaded) {
return;
@@ -41,9 +44,10 @@ void VectorTileData::parse() {
// Parsing creates state that is encapsulated in TileParser. While parsing,
// the TileParser object writes results into this objects. All other state
// is going to be discarded afterwards.
- TileParser parser(data, *this, style,
- glyphAtlas, glyphStore,
- spriteAtlas, sprite);
+ VectorTile vectorTile(pbf((const uint8_t *)data.data(), data.size()));
+ const VectorTile* vt = &vectorTile;
+ TileParser parser(*vt, *this, style, glyphAtlas, glyphStore, spriteAtlas, sprite);
+
// Clear the style so that we don't have a cycle in the shared_ptr references.
style.reset();
@@ -59,9 +63,9 @@ void VectorTileData::parse() {
}
}
-void VectorTileData::render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix) {
- if (state == State::parsed && layer_desc->bucket) {
- auto databucket_it = buckets.find(layer_desc->bucket->name);
+void VectorTileData::render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) {
+ if (state == State::parsed && layer_desc.bucket) {
+ auto databucket_it = buckets.find(layer_desc.bucket->name);
if (databucket_it != buckets.end()) {
assert(databucket_it->second);
databucket_it->second->render(painter, layer_desc, id, matrix);
@@ -69,7 +73,7 @@ void VectorTileData::render(Painter &painter, util::ptr<StyleLayer> layer_desc,
}
}
-bool VectorTileData::hasData(StyleLayer const& layer_desc) const {
+bool VectorTileData::hasData(const StyleLayer &layer_desc) const {
if (state == State::parsed && layer_desc.bucket) {
auto databucket_it = buckets.find(layer_desc.bucket->name);
if (databucket_it != buckets.end()) {
diff --git a/src/mbgl/map/vector_tile_data.hpp b/src/mbgl/map/vector_tile_data.hpp
index 9ed0e25cd6..7f43d824ac 100644
--- a/src/mbgl/map/vector_tile_data.hpp
+++ b/src/mbgl/map/vector_tile_data.hpp
@@ -31,15 +31,18 @@ class VectorTileData : public TileData {
public:
VectorTileData(Tile::ID const&,
- float mapMaxZoom, util::ptr<Style>,
- GlyphAtlas&, GlyphStore&,
- SpriteAtlas&, util::ptr<Sprite>,
- const SourceInfo&, FileSource &);
+ float mapMaxZoom,
+ util::ptr<Style>,
+ GlyphAtlas&,
+ GlyphStore&,
+ SpriteAtlas&,
+ util::ptr<Sprite>,
+ const SourceInfo&);
~VectorTileData();
- virtual void parse();
- virtual void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix);
- virtual bool hasData(StyleLayer const& layer_desc) const;
+ void parse() override;
+ void render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) override;
+ bool hasData(StyleLayer const& layer_desc) const override;
protected:
// Holds the actual geometries in this tile.
diff --git a/src/mbgl/map/view.cpp b/src/mbgl/map/view.cpp
new file mode 100644
index 0000000000..21ba4daf36
--- /dev/null
+++ b/src/mbgl/map/view.cpp
@@ -0,0 +1,21 @@
+#include <mbgl/map/view.hpp>
+#include <mbgl/map/map.hpp>
+
+namespace mbgl {
+
+void View::initialize(Map *map_) {
+ assert(map_);
+ map = map_;
+}
+
+void View::resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight) {
+ assert(map);
+ map->resize(width, height, ratio, fbWidth, fbHeight);
+}
+
+void View::notifyMapChange(MapChange, std::chrono::steady_clock::duration) {
+ // no-op
+}
+
+
+}
diff --git a/src/mbgl/platform/log.cpp b/src/mbgl/platform/log.cpp
index b83c7a9322..d6cbc4fd11 100644
--- a/src/mbgl/platform/log.cpp
+++ b/src/mbgl/platform/log.cpp
@@ -1,7 +1,67 @@
#include <mbgl/platform/log.hpp>
+#include <mbgl/map/environment.hpp>
+
+#include <cstdarg>
+#include <sstream>
+
namespace mbgl {
-std::unique_ptr<LogBackend> Log::Backend;
+namespace {
+
+static std::unique_ptr<Log::Observer> currentObserver;
+
+}
+
+void Log::setObserver(std::unique_ptr<Observer> observer) {
+ currentObserver = std::move(observer);
+}
+
+void Log::record(EventSeverity severity, Event event, const std::string &msg) {
+ record(severity, event, -1, msg);
+}
+
+void Log::record(EventSeverity severity, Event event, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ char msg[4096];
+ vsnprintf(msg, sizeof(msg), format, args);
+ va_end(args);
+
+ record(severity, event, -1, msg);
+}
+
+void Log::record(EventSeverity severity, Event event, int64_t code) {
+ record(severity, event, code, std::string());
+}
+
+void Log::record(EventSeverity severity, Event event, int64_t code, const std::string &msg) {
+ if (currentObserver && currentObserver->onRecord(severity, event, code, msg)) {
+ return;
+ }
+
+ std::stringstream logStream;
+
+ if (Environment::inScope()) {
+ logStream << "{" << Environment::Get().getID() << "}";
+ }
+
+ const std::string threadName = Environment::threadName();
+ if (!threadName.empty()) {
+ logStream << "{" << threadName << "}";
+ }
+
+ logStream << "[" << event << "]";
+
+ if (code >= 0) {
+ logStream << "(" << code << ")";
+ }
+
+ if (!msg.empty()) {
+ logStream << ": " << msg;
+ }
+
+ platformRecord(severity, logStream.str());
+}
}
diff --git a/src/mbgl/renderer/bucket.hpp b/src/mbgl/renderer/bucket.hpp
index 696bfb1110..f59ae65be0 100644
--- a/src/mbgl/renderer/bucket.hpp
+++ b/src/mbgl/renderer/bucket.hpp
@@ -13,7 +13,8 @@ class StyleLayer;
class Bucket : private util::noncopyable {
public:
- virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) = 0;
+ virtual void render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id,
+ const mat4 &matrix) = 0;
virtual bool hasData() const = 0;
virtual ~Bucket() {}
diff --git a/src/mbgl/renderer/debug_bucket.cpp b/src/mbgl/renderer/debug_bucket.cpp
index b7fa1b605e..3c56401e1e 100644
--- a/src/mbgl/renderer/debug_bucket.cpp
+++ b/src/mbgl/renderer/debug_bucket.cpp
@@ -13,7 +13,8 @@ DebugBucket::DebugBucket(DebugFontBuffer& fontBuffer_)
: fontBuffer(fontBuffer_) {
}
-void DebugBucket::render(Painter& painter, util::ptr<StyleLayer> /*layer_desc*/, const Tile::ID& /*id*/, const mat4 &matrix) {
+void DebugBucket::render(Painter &painter, const StyleLayer & /*layer_desc*/,
+ const Tile::ID & /*id*/, const mat4 &matrix) {
painter.renderDebugText(*this, matrix);
}
diff --git a/src/mbgl/renderer/debug_bucket.hpp b/src/mbgl/renderer/debug_bucket.hpp
index fb6cfb4cae..d23248841b 100644
--- a/src/mbgl/renderer/debug_bucket.hpp
+++ b/src/mbgl/renderer/debug_bucket.hpp
@@ -19,8 +19,9 @@ class DebugBucket : public Bucket {
public:
DebugBucket(DebugFontBuffer& fontBuffer);
- virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
- virtual bool hasData() const;
+ void render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id,
+ const mat4 &matrix) override;
+ bool hasData() const override;
void drawLines(PlainShader& shader);
void drawPoints(PlainShader& shader);
diff --git a/src/mbgl/renderer/fill_bucket.cpp b/src/mbgl/renderer/fill_bucket.cpp
index fca4ee3135..26d0d1f4be 100644
--- a/src/mbgl/renderer/fill_bucket.cpp
+++ b/src/mbgl/renderer/fill_bucket.cpp
@@ -1,16 +1,12 @@
#include <mbgl/renderer/fill_bucket.hpp>
#include <mbgl/geometry/fill_buffer.hpp>
#include <mbgl/geometry/elements_buffer.hpp>
-#include <mbgl/geometry/geometry.hpp>
-
#include <mbgl/renderer/painter.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layout.hpp>
-#include <mbgl/map/vector_tile.hpp>
#include <mbgl/util/std.hpp>
-
#include <mbgl/platform/gl.hpp>
-
+#include <mbgl/platform/log.hpp>
#include <cassert>
@@ -69,27 +65,16 @@ FillBucket::~FillBucket() {
}
}
-void FillBucket::addGeometry(pbf& geom) {
- Geometry::command cmd;
-
- Coordinate coord;
- Geometry geometry(geom);
- int32_t x, y;
- while ((cmd = geometry.next(x, y)) != Geometry::end) {
- if (cmd == Geometry::move_to) {
- if (line.size()) {
- clipper.AddPath(line, ClipperLib::ptSubject, true);
- line.clear();
- hasVertices = true;
- }
+void FillBucket::addGeometry(const GeometryCollection& geometryCollection) {
+ for (auto& line_ : geometryCollection) {
+ for (auto& v : line_) {
+ line.emplace_back(v.x, v.y);
+ }
+ if (line.size()) {
+ clipper.AddPath(line, ClipperLib::ptSubject, true);
+ line.clear();
+ hasVertices = true;
}
- line.emplace_back(x, y);
- }
-
- if (line.size()) {
- clipper.AddPath(line, ClipperLib::ptSubject, true);
- line.clear();
- hasVertices = true;
}
tessellate();
@@ -189,12 +174,12 @@ void FillBucket::tessellate() {
} else {
#if defined(DEBUG)
// TODO: We're missing a vertex that was not part of the line.
- fprintf(stderr, "undefined element buffer\n");
+ Log::Error(Event::OpenGL, "undefined element buffer");
#endif
}
} else {
#if defined(DEBUG)
- fprintf(stderr, "undefined element buffer\n");
+ Log::Error(Event::OpenGL, "undefined element buffer");
#endif
}
}
@@ -203,7 +188,7 @@ void FillBucket::tessellate() {
triangleGroup.elements_length += triangle_count;
} else {
#if defined(DEBUG)
- fprintf(stderr, "tessellation failed\n");
+ Log::Error(Event::OpenGL, "tessellation failed");
#endif
}
@@ -213,7 +198,8 @@ void FillBucket::tessellate() {
lineGroup.vertex_length += total_vertex_count;
}
-void FillBucket::render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
+void FillBucket::render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id,
+ const mat4 &matrix) {
painter.renderFill(*this, layer_desc, id, matrix);
}
diff --git a/src/mbgl/renderer/fill_bucket.hpp b/src/mbgl/renderer/fill_bucket.hpp
index 0e3f38cd50..46b8f53857 100644
--- a/src/mbgl/renderer/fill_bucket.hpp
+++ b/src/mbgl/renderer/fill_bucket.hpp
@@ -2,6 +2,7 @@
#define MBGL_RENDERER_FILLBUCKET
#include <mbgl/renderer/bucket.hpp>
+#include <mbgl/map/geometry_tile.hpp>
#include <mbgl/geometry/elements_buffer.hpp>
#include <mbgl/geometry/fill_buffer.hpp>
#include <mbgl/style/style_bucket.hpp>
@@ -27,7 +28,6 @@ class BucketDescription;
class OutlineShader;
class PlainShader;
class PatternShader;
-struct pbf;
class FillBucket : public Bucket {
@@ -43,12 +43,13 @@ public:
FillVertexBuffer &vertexBuffer,
TriangleElementsBuffer &triangleElementsBuffer,
LineElementsBuffer &lineElementsBuffer);
- ~FillBucket();
+ ~FillBucket() override;
- virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
- virtual bool hasData() const;
+ void render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id,
+ const mat4 &matrix) override;
+ bool hasData() const override;
- void addGeometry(pbf& data);
+ void addGeometry(const GeometryCollection&);
void tessellate();
void drawElements(PlainShader& shader);
diff --git a/src/mbgl/renderer/line_bucket.cpp b/src/mbgl/renderer/line_bucket.cpp
index e181ac77b4..6eea0d8d0f 100644
--- a/src/mbgl/renderer/line_bucket.cpp
+++ b/src/mbgl/renderer/line_bucket.cpp
@@ -1,12 +1,9 @@
#include <mbgl/renderer/line_bucket.hpp>
-#include <mbgl/geometry/elements_buffer.hpp>
-#include <mbgl/geometry/geometry.hpp>
+#include <mbgl/geometry/elements_buffer.hpp>
#include <mbgl/renderer/painter.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layout.hpp>
-#include <mbgl/map/vector_tile.hpp>
-
#include <mbgl/util/math.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/platform/gl.hpp>
@@ -37,28 +34,6 @@ LineBucket::~LineBucket() {
// Do not remove. header file only contains forward definitions to unique pointers.
}
-
-void LineBucket::addGeometry(pbf& geom) {
- std::vector<Coordinate> line;
- Geometry::command cmd;
-
- Coordinate coord;
- Geometry geometry(geom);
- int32_t x, y;
- while ((cmd = geometry.next(x, y)) != Geometry::end) {
- if (cmd == Geometry::move_to) {
- if (!line.empty()) {
- addGeometry(line);
- line.clear();
- }
- }
- line.emplace_back(x, y);
- }
- if (line.size()) {
- addGeometry(line);
- }
-}
-
struct TriangleElement {
TriangleElement(uint16_t a_, uint16_t b_, uint16_t c_) : a(a_), b(b_), c(c_) {}
uint16_t a, b, c;
@@ -66,6 +41,12 @@ struct TriangleElement {
typedef uint16_t PointElement;
+void LineBucket::addGeometry(const GeometryCollection& geometryCollection) {
+ for (auto& line : geometryCollection) {
+ addGeometry(line);
+ }
+}
+
void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
auto &layout = *styleLayout;
// TODO: use roundLimit
@@ -351,7 +332,8 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
}
}
-void LineBucket::render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
+void LineBucket::render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id,
+ const mat4 &matrix) {
painter.renderLine(*this, layer_desc, id, matrix);
}
diff --git a/src/mbgl/renderer/line_bucket.hpp b/src/mbgl/renderer/line_bucket.hpp
index 3f022ce0ef..25b2190176 100644
--- a/src/mbgl/renderer/line_bucket.hpp
+++ b/src/mbgl/renderer/line_bucket.hpp
@@ -2,6 +2,7 @@
#define MBGL_RENDERER_LINEBUCKET
#include <mbgl/renderer/bucket.hpp>
+#include <mbgl/map/geometry_tile.hpp>
#include <mbgl/geometry/vao.hpp>
#include <mbgl/geometry/elements_buffer.hpp>
#include <mbgl/geometry/line_buffer.hpp>
@@ -20,7 +21,6 @@ class LineShader;
class LinejoinShader;
class LineSDFShader;
class LinepatternShader;
-struct pbf;
class LineBucket : public Bucket {
typedef ElementGroup<3> triangle_group_type;
@@ -31,12 +31,13 @@ public:
LineVertexBuffer &vertexBuffer,
TriangleElementsBuffer &triangleElementsBuffer,
PointElementsBuffer &pointElementsBuffer);
- ~LineBucket();
+ ~LineBucket() override;
- virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
- virtual bool hasData() const;
+ void render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id,
+ const mat4 &matrix) override;
+ bool hasData() const override;
- void addGeometry(pbf& data);
+ void addGeometry(const GeometryCollection&);
void addGeometry(const std::vector<Coordinate>& line);
bool hasPoints() const;
diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp
index 044604f5b4..544198fe61 100644
--- a/src/mbgl/renderer/painter.cpp
+++ b/src/mbgl/renderer/painter.cpp
@@ -1,4 +1,5 @@
#include <mbgl/renderer/painter.hpp>
+#include <mbgl/platform/log.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/style/style_layer_group.hpp>
@@ -234,9 +235,11 @@ void Painter::render(const Style& style, const std::set<util::ptr<StyleSource>>&
frameHistory.record(time, state.getNormalizedZoom());
// Actually render the layers
- if (debug::renderTree) { std::cout << "{" << std::endl; indent++; }
- renderLayers(style.layers);
- if (debug::renderTree) { std::cout << "}" << std::endl; indent--; }
+ if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; }
+ if (style.layers) {
+ renderLayers(*style.layers);
+ }
+ if (debug::renderTree) { Log::Info(Event::Render, "}"); indent--; }
// Finalize the rendering, e.g. by calling debug render calls per tile.
// This guarantees that we have at least one function per tile called.
@@ -247,73 +250,68 @@ void Painter::render(const Style& style, const std::set<util::ptr<StyleSource>>&
}
}
-void Painter::renderLayers(util::ptr<StyleLayerGroup> group) {
- if (!group) {
- // Make sure that we actually do have a layer group.
- return;
- }
-
+void Painter::renderLayers(const StyleLayerGroup &group) {
// TODO: Correctly compute the number of layers recursively beforehand.
- float strata_thickness = 1.0f / (group->layers.size() + 1);
+ float strata_thickness = 1.0f / (group.layers.size() + 1);
// - FIRST PASS ------------------------------------------------------------
// Render everything top-to-bottom by using reverse iterators. Render opaque
// objects first.
if (debug::renderTree) {
- std::cout << std::string(indent++ * 4, ' ') << "OPAQUE {" << std::endl;
+ Log::Info(Event::Render, "%*s%s", indent++ * 4, "", "OPAQUE {");
}
int i = 0;
- for (auto it = group->layers.rbegin(), end = group->layers.rend(); it != end; ++it, ++i) {
+ for (auto it = group.layers.rbegin(), end = group.layers.rend(); it != end; ++it, ++i) {
setOpaque();
setStrata(i * strata_thickness);
- renderLayer(*it);
+ renderLayer(**it);
}
if (debug::renderTree) {
- std::cout << std::string(--indent * 4, ' ') << "}" << std::endl;
+ Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}");
}
// - SECOND PASS -----------------------------------------------------------
// Make a second pass, rendering translucent objects. This time, we render
// bottom-to-top.
if (debug::renderTree) {
- std::cout << std::string(indent++ * 4, ' ') << "TRANSLUCENT {" << std::endl;
+ Log::Info(Event::Render, "%*s%s", indent++ * 4, "", "TRANSLUCENT {");
}
--i;
- for (auto it = group->layers.begin(), end = group->layers.end(); it != end; ++it, --i) {
+ for (auto it = group.layers.begin(), end = group.layers.end(); it != end; ++it, --i) {
setTranslucent();
setStrata(i * strata_thickness);
- renderLayer(*it);
+ renderLayer(**it);
}
if (debug::renderTree) {
- std::cout << std::string(--indent * 4, ' ') << "}" << std::endl;
+ Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}");
}
}
-void Painter::renderLayer(util::ptr<StyleLayer> layer_desc, const Tile::ID* id, const mat4* matrix) {
- if (layer_desc->bucket->visibility == VisibilityType::None) return;
- if (layer_desc->type == StyleLayerType::Background) {
+void Painter::renderLayer(const StyleLayer &layer_desc, const Tile::ID* id, const mat4* matrix) {
+ if (layer_desc.bucket->visibility == VisibilityType::None) return;
+ if (layer_desc.type == StyleLayerType::Background) {
// This layer defines a background color/image.
if (debug::renderTree) {
- std::cout << std::string(indent * 4, ' ') << "- " << layer_desc->id << " ("
- << layer_desc->type << ")" << std::endl;
+ Log::Info(Event::Render, "%*s- %s (%s)", indent * 4, "", layer_desc.id.c_str(),
+ StyleLayerTypeClass(layer_desc.type).c_str());
}
renderBackground(layer_desc);
} else {
// This is a singular layer.
- if (!layer_desc->bucket) {
- fprintf(stderr, "[WARNING] layer '%s' is missing bucket\n", layer_desc->id.c_str());
+ if (!layer_desc.bucket) {
+ Log::Warning(Event::Render, "layer '%s' is missing bucket", layer_desc.id.c_str());
return;
}
- if (!layer_desc->bucket->style_source) {
- fprintf(stderr, "[WARNING] can't find source for layer '%s'\n", layer_desc->id.c_str());
+ if (!layer_desc.bucket->style_source) {
+ Log::Warning(Event::Render, "can't find source for layer '%s'", layer_desc.id.c_str());
return;
}
- StyleSource const& style_source = *layer_desc->bucket->style_source;
+ StyleSource const& style_source = *layer_desc.bucket->style_source;
// Skip this layer if there is no data.
if (!style_source.source) {
@@ -324,36 +322,36 @@ void Painter::renderLayer(util::ptr<StyleLayer> layer_desc, const Tile::ID* id,
// This may occur when there /is/ a bucket created for this layer, but the min/max-zoom
// is set to a fractional value, or value that is larger than the source maxzoom.
const double zoom = state.getZoom();
- if (layer_desc->bucket->min_zoom > zoom ||
- layer_desc->bucket->max_zoom <= zoom) {
+ if (layer_desc.bucket->min_zoom > zoom ||
+ layer_desc.bucket->max_zoom <= zoom) {
return;
}
// Abort early if we can already deduce from the bucket type that
// we're not going to render anything anyway during this pass.
- switch (layer_desc->type) {
+ switch (layer_desc.type) {
case StyleLayerType::Fill:
- if (!layer_desc->getProperties<FillProperties>().isVisible()) return;
+ if (!layer_desc.getProperties<FillProperties>().isVisible()) return;
break;
case StyleLayerType::Line:
if (pass == RenderPass::Opaque) return;
- if (!layer_desc->getProperties<LineProperties>().isVisible()) return;
+ if (!layer_desc.getProperties<LineProperties>().isVisible()) return;
break;
case StyleLayerType::Symbol:
if (pass == RenderPass::Opaque) return;
- if (!layer_desc->getProperties<SymbolProperties>().isVisible()) return;
+ if (!layer_desc.getProperties<SymbolProperties>().isVisible()) return;
break;
case StyleLayerType::Raster:
if (pass == RenderPass::Opaque) return;
- if (!layer_desc->getProperties<RasterProperties>().isVisible()) return;
+ if (!layer_desc.getProperties<RasterProperties>().isVisible()) return;
break;
default:
break;
}
if (debug::renderTree) {
- std::cout << std::string(indent * 4, ' ') << "- " << layer_desc->id << " ("
- << layer_desc->type << ")" << std::endl;
+ Log::Info(Event::Render, "%*s- %s (%s)", indent * 4, "", layer_desc.id.c_str(),
+ StyleLayerTypeClass(layer_desc.type).c_str());
}
if (!id) {
style_source.source->render(*this, layer_desc);
@@ -363,17 +361,17 @@ void Painter::renderLayer(util::ptr<StyleLayer> layer_desc, const Tile::ID* id,
}
}
-void Painter::renderTileLayer(const Tile& tile, util::ptr<StyleLayer> layer_desc, const mat4 &matrix) {
+void Painter::renderTileLayer(const Tile& tile, const StyleLayer &layer_desc, const mat4 &matrix) {
assert(tile.data);
- if (tile.data->hasData(*layer_desc) || layer_desc->type == StyleLayerType::Raster) {
+ if (tile.data->hasData(layer_desc) || layer_desc.type == StyleLayerType::Raster) {
gl::group group(std::string { "render " } + tile.data->name);
prepareTile(tile);
tile.data->render(*this, layer_desc, matrix);
}
}
-void Painter::renderBackground(util::ptr<StyleLayer> layer_desc) {
- const BackgroundProperties& properties = layer_desc->getProperties<BackgroundProperties>();
+void Painter::renderBackground(const StyleLayer &layer_desc) {
+ const BackgroundProperties& properties = layer_desc.getProperties<BackgroundProperties>();
if (properties.image.to.size()) {
if ((properties.opacity >= 1.0f) != (pass == RenderPass::Opaque))
diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp
index f38a9f0559..9c9c6a4642 100644
--- a/src/mbgl/renderer/painter.hpp
+++ b/src/mbgl/renderer/painter.hpp
@@ -81,11 +81,11 @@ public:
TransformState state,
std::chrono::steady_clock::time_point time);
- void renderLayers(util::ptr<StyleLayerGroup> group);
- void renderLayer(util::ptr<StyleLayer> layer_desc, const Tile::ID* id = nullptr, const mat4* matrix = nullptr);
+ void renderLayers(const StyleLayerGroup &group);
+ void renderLayer(const StyleLayer &layer_desc, const Tile::ID* id = nullptr, const mat4* matrix = nullptr);
// Renders a particular layer from a tile.
- void renderTileLayer(const Tile& tile, util::ptr<StyleLayer> layer_desc, const mat4 &matrix);
+ void renderTileLayer(const Tile& tile, const StyleLayer &layer_desc, const mat4 &matrix);
// Renders debug information for a tile.
void renderTileDebug(const Tile& tile);
@@ -95,11 +95,11 @@ public:
void renderDebugText(DebugBucket& bucket, const mat4 &matrix);
void renderDebugText(const std::vector<std::string> &strings);
- void renderFill(FillBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
- void renderLine(LineBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
- void renderSymbol(SymbolBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
- void renderRaster(RasterBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
- void renderBackground(util::ptr<StyleLayer> layer_desc);
+ void renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const Tile::ID& id, const mat4 &matrix);
+ void renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const Tile::ID& id, const mat4 &matrix);
+ void renderSymbol(SymbolBucket& bucket, const StyleLayer &layer_desc, const Tile::ID& id, const mat4 &matrix);
+ void renderRaster(RasterBucket& bucket, const StyleLayer &layer_desc, const Tile::ID& id, const mat4 &matrix);
+ void renderBackground(const StyleLayer &layer_desc);
float saturationFactor(float saturation);
float contrastFactor(float contrast);
@@ -109,7 +109,7 @@ public:
void renderPrerenderedTexture(RasterBucket &bucket, const mat4 &matrix, const RasterProperties& properties);
- void createPrerendered(RasterBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id);
+ void createPrerendered(RasterBucket& bucket, const StyleLayer &layer_desc, const Tile::ID& id);
void resize();
diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp
index 36e8994fcb..53fa337bed 100644
--- a/src/mbgl/renderer/painter_fill.cpp
+++ b/src/mbgl/renderer/painter_fill.cpp
@@ -11,11 +11,11 @@
using namespace mbgl;
-void Painter::renderFill(FillBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
+void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const Tile::ID& id, const mat4 &matrix) {
// Abort early.
if (!bucket.hasData()) return;
- const FillProperties &properties = layer_desc->getProperties<FillProperties>();
+ const FillProperties &properties = layer_desc.getProperties<FillProperties>();
mat4 vtxMatrix = translatedMatrix(matrix, properties.translate, id, properties.translateAnchor);
Color fill_color = properties.fill_color;
diff --git a/src/mbgl/renderer/painter_line.cpp b/src/mbgl/renderer/painter_line.cpp
index fc4000276f..36b8c3b689 100644
--- a/src/mbgl/renderer/painter_line.cpp
+++ b/src/mbgl/renderer/painter_line.cpp
@@ -10,12 +10,12 @@
using namespace mbgl;
-void Painter::renderLine(LineBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
+void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const Tile::ID& id, const mat4 &matrix) {
// Abort early.
if (pass == RenderPass::Opaque) return;
if (!bucket.hasData()) return;
- const auto &properties = layer_desc->getProperties<LineProperties>();
+ const auto &properties = layer_desc.getProperties<LineProperties>();
const auto &layout = *bucket.styleLayout;
// the distance over which the line edge fades out.
diff --git a/src/mbgl/renderer/painter_raster.cpp b/src/mbgl/renderer/painter_raster.cpp
index 4ece54977b..5e72676b92 100644
--- a/src/mbgl/renderer/painter_raster.cpp
+++ b/src/mbgl/renderer/painter_raster.cpp
@@ -8,14 +8,12 @@
using namespace mbgl;
-void Painter::renderRaster(RasterBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID&, const mat4 &matrix) {
+void Painter::renderRaster(RasterBucket& bucket, const StyleLayer &layer_desc, const Tile::ID&, const mat4 &matrix) {
if (pass != RenderPass::Translucent) return;
- const RasterProperties &properties = layer_desc->getProperties<RasterProperties>();
+ const RasterProperties &properties = layer_desc.getProperties<RasterProperties>();
if (bucket.hasData()) {
- depthMask(false);
-
useProgram(rasterShader->program);
rasterShader->u_matrix = matrix;
rasterShader->u_buffer = 0;
@@ -29,8 +27,6 @@ void Painter::renderRaster(RasterBucket& bucket, util::ptr<StyleLayer> layer_des
depthRange(strata + strata_epsilon, 1.0f);
bucket.drawRaster(*rasterShader, tileStencilBuffer, coveringRasterArray);
-
- depthMask(true);
}
}
diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp
index a4af17b4eb..9b533699e0 100644
--- a/src/mbgl/renderer/painter_symbol.cpp
+++ b/src/mbgl/renderer/painter_symbol.cpp
@@ -112,13 +112,13 @@ void Painter::renderSDF(SymbolBucket &bucket,
}
}
-void Painter::renderSymbol(SymbolBucket &bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) {
+void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, const Tile::ID &id, const mat4 &matrix) {
// Abort early.
if (pass == RenderPass::Opaque) {
return;
}
- const auto &properties = layer_desc->getProperties<SymbolProperties>();
+ const auto &properties = layer_desc.getProperties<SymbolProperties>();
const auto &layout = *bucket.styleLayout;
MBGL_CHECK_ERROR(glDisable(GL_STENCIL_TEST));
diff --git a/src/mbgl/renderer/raster_bucket.cpp b/src/mbgl/renderer/raster_bucket.cpp
index 50af7e4cb4..b16303c84f 100644
--- a/src/mbgl/renderer/raster_bucket.cpp
+++ b/src/mbgl/renderer/raster_bucket.cpp
@@ -8,7 +8,8 @@ RasterBucket::RasterBucket(TexturePool& texturePool, const StyleLayoutRaster& la
raster(texturePool) {
}
-void RasterBucket::render(Painter &painter, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) {
+void RasterBucket::render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id,
+ const mat4 &matrix) {
painter.renderRaster(*this, layer_desc, id, matrix);
}
diff --git a/src/mbgl/renderer/raster_bucket.hpp b/src/mbgl/renderer/raster_bucket.hpp
index 1f589e5b6c..168685b163 100644
--- a/src/mbgl/renderer/raster_bucket.hpp
+++ b/src/mbgl/renderer/raster_bucket.hpp
@@ -18,8 +18,9 @@ class RasterBucket : public Bucket {
public:
RasterBucket(TexturePool&, const StyleLayoutRaster&);
- virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
- virtual bool hasData() const;
+ void render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id,
+ const mat4 &matrix) override;
+ bool hasData() const override;
bool setImage(const std::string &data);
diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp
index 25eecfc622..ad54581e10 100644
--- a/src/mbgl/renderer/symbol_bucket.cpp
+++ b/src/mbgl/renderer/symbol_bucket.cpp
@@ -1,10 +1,10 @@
-#include <mbgl/style/style_layout.hpp>
#include <mbgl/renderer/symbol_bucket.hpp>
+#include <mbgl/map/geometry_tile.hpp>
+#include <mbgl/style/style_layout.hpp>
#include <mbgl/geometry/text_buffer.hpp>
#include <mbgl/geometry/icon_buffer.hpp>
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
-#include <mbgl/geometry/geometry.hpp>
#include <mbgl/geometry/anchor.hpp>
#include <mbgl/geometry/resample.hpp>
#include <mbgl/renderer/painter.hpp>
@@ -18,6 +18,7 @@
#include <mbgl/util/token.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/merge_lines.hpp>
+#include <mbgl/util/std.hpp>
namespace mbgl {
@@ -30,8 +31,8 @@ SymbolBucket::~SymbolBucket() {
// Do not remove. header file only contains forward definitions to unique pointers.
}
-void SymbolBucket::render(Painter &painter, util::ptr<StyleLayer> layer_desc,
- const Tile::ID &id, const mat4 &matrix) {
+void SymbolBucket::render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id,
+ const mat4 &matrix) {
painter.renderSymbol(*this, layer_desc, id, matrix);
}
@@ -41,14 +42,8 @@ bool SymbolBucket::hasTextData() const { return !text.groups.empty(); }
bool SymbolBucket::hasIconData() const { return !icon.groups.empty(); }
-void SymbolBucket::addGlyphsToAtlas(uint64_t tileid, const std::string stackname,
- const std::u32string &text, const FontStack &fontStack,
- GlyphAtlas &glyphAtlas, GlyphPositions &face) {
- glyphAtlas.addGlyphs(tileid, text, stackname, fontStack,face);
-}
-
-std::vector<SymbolFeature> SymbolBucket::processFeatures(const VectorTileLayer &layer,
- const FilterExpression &filter,
+std::vector<SymbolFeature> SymbolBucket::processFeatures(const GeometryTileLayer& layer,
+ const FilterExpression& filter,
GlyphStore &glyphStore,
const Sprite &sprite) {
auto &layout = *styleLayout;
@@ -64,14 +59,22 @@ std::vector<SymbolFeature> SymbolBucket::processFeatures(const VectorTileLayer &
// Determine and load glyph ranges
std::set<GlyphRange> ranges;
- FilteredVectorTileLayer filtered_layer(layer, filter);
- for (const pbf &feature_pbf : filtered_layer) {
- const VectorTileFeature feature{feature_pbf, layer};
+ for (std::size_t i = 0; i < layer.featureCount(); i++) {
+ auto feature = layer.getFeature(i);
+
+ GeometryTileFeatureExtractor extractor(*feature);
+ if (!evaluate(filter, extractor))
+ continue;
SymbolFeature ft;
+ auto getValue = [&feature](const std::string& key) -> std::string {
+ auto value = feature->getValue(key);
+ return value ? toString(*value) : std::string();
+ };
+
if (has_text) {
- std::string u8string = util::replaceTokens(layout.text.field, feature.properties);
+ std::string u8string = util::replaceTokens(layout.text.field, getValue);
if (layout.text.transform == TextTransformType::Uppercase) {
u8string = platform::uppercase(u8string);
@@ -90,25 +93,19 @@ std::vector<SymbolFeature> SymbolBucket::processFeatures(const VectorTileLayer &
}
if (has_icon) {
- ft.sprite = util::replaceTokens(layout.icon.image, feature.properties);
+ ft.sprite = util::replaceTokens(layout.icon.image, getValue);
}
if (ft.label.length() || ft.sprite.length()) {
auto &multiline = ft.geometry;
- // Decode line
- Geometry::command cmd;
- pbf geom(feature.geometry);
- Geometry geometry(geom);
- bool first = true;
- int32_t x, y;
- while ((cmd = geometry.next(x, y)) != Geometry::end) {
- if (first || cmd == Geometry::move_to) {
- multiline.emplace_back();
- first = false;
+ GeometryCollection geometryCollection = feature->getGeometries();
+ for (auto& line : geometryCollection) {
+ multiline.emplace_back();
+ for (auto& point : line) {
+ multiline.back().emplace_back(point.x, point.y);
}
- multiline.back().emplace_back(x, y);
}
features.push_back(std::move(ft));
@@ -125,9 +122,13 @@ std::vector<SymbolFeature> SymbolBucket::processFeatures(const VectorTileLayer &
return features;
}
-void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpression &filter,
- const Tile::ID &id, SpriteAtlas &spriteAtlas, Sprite &sprite,
- GlyphAtlas & glyphAtlas, GlyphStore &glyphStore) {
+void SymbolBucket::addFeatures(const GeometryTileLayer& layer,
+ const FilterExpression& filter,
+ uintptr_t tileUID,
+ SpriteAtlas& spriteAtlas,
+ Sprite& sprite,
+ GlyphAtlas& glyphAtlas,
+ GlyphStore& glyphStore) {
auto &layout = *styleLayout;
const std::vector<SymbolFeature> features = processFeatures(layer, filter, glyphStore, sprite);
@@ -172,7 +173,7 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress
if (layout.text.justify == TextJustifyType::Right) justify = 1;
else if (layout.text.justify == TextJustifyType::Left) justify = 0;
- const FontStack &fontStack = glyphStore.getFontStack(layout.text.font);
+ const auto &fontStack = glyphStore.getFontStack(layout.text.font);
for (const SymbolFeature &feature : features) {
if (!feature.geometry.size()) continue;
@@ -183,7 +184,7 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress
// if feature has text, shape the text
if (feature.label.length()) {
- shaping = fontStack.getShaping(
+ shaping = fontStack->getShaping(
/* string */ feature.label,
/* maxWidth: ems */ layout.text.max_width * 24,
/* lineHeight: ems */ layout.text.line_height * 24,
@@ -195,8 +196,7 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress
// Add the glyphs we need for this label to the glyph atlas.
if (shaping.size()) {
- SymbolBucket::addGlyphsToAtlas(id.to_uint64(), layout.text.font, feature.label, fontStack,
- glyphAtlas, face);
+ glyphAtlas.addGlyphs(tileUID, feature.label, layout.text.font, fontStack, face);
}
}
diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp
index 8ad420cde0..b7291c186c 100644
--- a/src/mbgl/renderer/symbol_bucket.hpp
+++ b/src/mbgl/renderer/symbol_bucket.hpp
@@ -2,14 +2,15 @@
#define MBGL_RENDERER_SYMBOLBUCKET
#include <mbgl/renderer/bucket.hpp>
+#include <mbgl/map/geometry_tile.hpp>
#include <mbgl/geometry/vao.hpp>
#include <mbgl/geometry/elements_buffer.hpp>
#include <mbgl/geometry/text_buffer.hpp>
#include <mbgl/geometry/icon_buffer.hpp>
-#include <mbgl/map/vector_tile.hpp>
#include <mbgl/text/types.hpp>
#include <mbgl/text/glyph.hpp>
#include <mbgl/style/style_bucket.hpp>
+#include <mbgl/util/ptr.hpp>
#include <memory>
#include <map>
@@ -56,40 +57,38 @@ class SymbolBucket : public Bucket {
public:
SymbolBucket(std::unique_ptr<const StyleLayoutSymbol> styleLayout, Collision &collision);
- ~SymbolBucket();
-
- virtual void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix);
- virtual bool hasData() const;
- virtual bool hasTextData() const;
- virtual bool hasIconData() const;
-
- void addFeatures(const VectorTileLayer &layer, const FilterExpression &filter,
- const Tile::ID &id, SpriteAtlas &spriteAtlas, Sprite &sprite,
- GlyphAtlas &glyphAtlas, GlyphStore &glyphStore);
-
- void addGlyphs(const PlacedGlyphs &glyphs, float placementZoom, PlacementRange placementRange,
- float zoom);
+ ~SymbolBucket() override;
+
+ void render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id,
+ const mat4 &matrix) override;
+ bool hasData() const override;
+ bool hasTextData() const;
+ bool hasIconData() const;
+
+ void addFeatures(const GeometryTileLayer&,
+ const FilterExpression&,
+ uintptr_t tileUID,
+ SpriteAtlas&,
+ Sprite&,
+ GlyphAtlas&,
+ GlyphStore&);
void drawGlyphs(SDFShader& shader);
void drawIcons(SDFShader& shader);
void drawIcons(IconShader& shader);
private:
-
- std::vector<SymbolFeature> processFeatures(const VectorTileLayer &layer, const FilterExpression &filter, GlyphStore &glyphStore, const Sprite &sprite);
-
+ std::vector<SymbolFeature> processFeatures(const GeometryTileLayer&,
+ const FilterExpression&,
+ GlyphStore&,
+ const Sprite&);
void addFeature(const std::vector<Coordinate> &line, const Shaping &shaping, const GlyphPositions &face, const Rect<uint16_t> &image);
-
// Adds placed items to the buffer.
template <typename Buffer, typename GroupType>
void addSymbols(Buffer &buffer, const PlacedGlyphs &symbols, float scale, PlacementRange placementRange);
- // Adds glyphs to the glyph atlas so that they have a left/top/width/height coordinates associated to them that we can use for writing to a buffer.
- static void addGlyphsToAtlas(uint64_t tileid, const std::string stackname, const std::u32string &string,
- const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face);
-
public:
const std::unique_ptr<const StyleLayoutSymbol> styleLayout;
bool sdfIcons = false;
diff --git a/src/mbgl/shader/dot_shader.cpp b/src/mbgl/shader/dot_shader.cpp
index 08b55a566b..57a60cc519 100644
--- a/src/mbgl/shader/dot_shader.cpp
+++ b/src/mbgl/shader/dot_shader.cpp
@@ -12,11 +12,6 @@ DotShader::DotShader()
shaders[DOT_SHADER].vertex,
shaders[DOT_SHADER].fragment
) {
- if (!valid) {
- fprintf(stderr, "invalid dot shader\n");
- return;
- }
-
a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
}
diff --git a/src/mbgl/shader/gaussian_shader.cpp b/src/mbgl/shader/gaussian_shader.cpp
index 483de4867e..b7d593f2e4 100644
--- a/src/mbgl/shader/gaussian_shader.cpp
+++ b/src/mbgl/shader/gaussian_shader.cpp
@@ -12,13 +12,6 @@ GaussianShader::GaussianShader()
shaders[GAUSSIAN_SHADER].vertex,
shaders[GAUSSIAN_SHADER].fragment
) {
- if (!valid) {
-#if defined(DEBUG)
- fprintf(stderr, "invalid raster shader\n");
-#endif
- return;
- }
-
a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
}
diff --git a/src/mbgl/shader/icon_shader.cpp b/src/mbgl/shader/icon_shader.cpp
index 0dfd67e25b..0f626ec9e7 100644
--- a/src/mbgl/shader/icon_shader.cpp
+++ b/src/mbgl/shader/icon_shader.cpp
@@ -12,11 +12,6 @@ IconShader::IconShader()
shaders[ICON_SHADER].vertex,
shaders[ICON_SHADER].fragment
) {
- if (!valid) {
- fprintf(stderr, "invalid icon shader\n");
- return;
- }
-
a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
a_offset = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_offset"));
a_data1 = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_data1"));
diff --git a/src/mbgl/shader/line_shader.cpp b/src/mbgl/shader/line_shader.cpp
index 432a64f695..c2364c9bea 100644
--- a/src/mbgl/shader/line_shader.cpp
+++ b/src/mbgl/shader/line_shader.cpp
@@ -12,11 +12,6 @@ LineShader::LineShader()
shaders[LINE_SHADER].vertex,
shaders[LINE_SHADER].fragment
) {
- if (!valid) {
- fprintf(stderr, "invalid line shader\n");
- return;
- }
-
a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
a_data = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_data"));
}
diff --git a/src/mbgl/shader/linejoin_shader.cpp b/src/mbgl/shader/linejoin_shader.cpp
index 5b9e7ba20a..b3c5638b5d 100644
--- a/src/mbgl/shader/linejoin_shader.cpp
+++ b/src/mbgl/shader/linejoin_shader.cpp
@@ -12,11 +12,6 @@ LinejoinShader::LinejoinShader()
shaders[LINEJOIN_SHADER].vertex,
shaders[LINEJOIN_SHADER].fragment
) {
- if (!valid) {
- fprintf(stderr, "invalid line shader\n");
- return;
- }
-
a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
}
diff --git a/src/mbgl/shader/linepattern_shader.cpp b/src/mbgl/shader/linepattern_shader.cpp
index c45378378d..7bfd1412db 100644
--- a/src/mbgl/shader/linepattern_shader.cpp
+++ b/src/mbgl/shader/linepattern_shader.cpp
@@ -12,11 +12,6 @@ LinepatternShader::LinepatternShader()
shaders[LINEPATTERN_SHADER].vertex,
shaders[LINEPATTERN_SHADER].fragment
) {
- if (!valid) {
- fprintf(stderr, "invalid line pattern shader\n");
- return;
- }
-
a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
a_data = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_data"));
}
diff --git a/src/mbgl/shader/linesdf_shader.cpp b/src/mbgl/shader/linesdf_shader.cpp
index 9802afb532..bd2137de30 100644
--- a/src/mbgl/shader/linesdf_shader.cpp
+++ b/src/mbgl/shader/linesdf_shader.cpp
@@ -12,11 +12,6 @@ LineSDFShader::LineSDFShader()
shaders[LINESDF_SHADER].vertex,
shaders[LINESDF_SHADER].fragment
) {
- if (!valid) {
- fprintf(stderr, "invalid line shader\n");
- return;
- }
-
a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
a_data = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_data"));
}
diff --git a/src/mbgl/shader/outline_shader.cpp b/src/mbgl/shader/outline_shader.cpp
index 98d208297c..ee2a6bcd40 100644
--- a/src/mbgl/shader/outline_shader.cpp
+++ b/src/mbgl/shader/outline_shader.cpp
@@ -12,11 +12,6 @@ OutlineShader::OutlineShader()
shaders[OUTLINE_SHADER].vertex,
shaders[OUTLINE_SHADER].fragment
) {
- if (!valid) {
- fprintf(stderr, "invalid outline shader\n");
- return;
- }
-
a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
}
diff --git a/src/mbgl/shader/pattern_shader.cpp b/src/mbgl/shader/pattern_shader.cpp
index 0a6013b17d..66369d528a 100644
--- a/src/mbgl/shader/pattern_shader.cpp
+++ b/src/mbgl/shader/pattern_shader.cpp
@@ -12,11 +12,6 @@ PatternShader::PatternShader()
shaders[PATTERN_SHADER].vertex,
shaders[PATTERN_SHADER].fragment
) {
- if (!valid) {
- fprintf(stderr, "invalid pattern shader\n");
- return;
- }
-
a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
}
diff --git a/src/mbgl/shader/plain_shader.cpp b/src/mbgl/shader/plain_shader.cpp
index ce7ddae918..93dbb42a57 100644
--- a/src/mbgl/shader/plain_shader.cpp
+++ b/src/mbgl/shader/plain_shader.cpp
@@ -12,11 +12,6 @@ PlainShader::PlainShader()
shaders[PLAIN_SHADER].vertex,
shaders[PLAIN_SHADER].fragment
) {
- if (!valid) {
- fprintf(stderr, "invalid plain shader\n");
- return;
- }
-
a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
}
diff --git a/src/mbgl/shader/raster_shader.cpp b/src/mbgl/shader/raster_shader.cpp
index 14c6ddc7c4..2d69549209 100644
--- a/src/mbgl/shader/raster_shader.cpp
+++ b/src/mbgl/shader/raster_shader.cpp
@@ -12,13 +12,6 @@ RasterShader::RasterShader()
shaders[RASTER_SHADER].vertex,
shaders[RASTER_SHADER].fragment
) {
- if (!valid) {
-#if defined(DEBUG)
- fprintf(stderr, "invalid raster shader\n");
-#endif
- return;
- }
-
a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
}
diff --git a/src/mbgl/shader/sdf_shader.cpp b/src/mbgl/shader/sdf_shader.cpp
index 757884f39c..ca1dab58dd 100644
--- a/src/mbgl/shader/sdf_shader.cpp
+++ b/src/mbgl/shader/sdf_shader.cpp
@@ -12,11 +12,6 @@ SDFShader::SDFShader()
shaders[SDF_SHADER].vertex,
shaders[SDF_SHADER].fragment
) {
- if (!valid) {
- fprintf(stderr, "invalid sdf shader\n");
- return;
- }
-
a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
a_offset = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_offset"));
a_data1 = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_data1"));
diff --git a/src/mbgl/shader/shader.cpp b/src/mbgl/shader/shader.cpp
index 550c159665..a079409aa0 100644
--- a/src/mbgl/shader/shader.cpp
+++ b/src/mbgl/shader/shader.cpp
@@ -1,6 +1,7 @@
#include <mbgl/shader/shader.hpp>
#include <mbgl/platform/gl.hpp>
#include <mbgl/util/stopwatch.hpp>
+#include <mbgl/util/exception.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/platform/platform.hpp>
@@ -14,7 +15,6 @@ using namespace mbgl;
Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSource)
: name(name_),
- valid(false),
program(0) {
util::stopwatch stopwatch("shader compilation", Event::Shader);
@@ -26,7 +26,7 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo
Log::Error(Event::Shader, "Vertex shader %s failed to compile: %s", name, vertSource);
MBGL_CHECK_ERROR(glDeleteProgram(program));
program = 0;
- return;
+ throw util::ShaderException(std::string { "Vertex shader " } + name + " failed to compile");
}
if (!compileShader(&fragShader, GL_FRAGMENT_SHADER, fragSource)) {
@@ -35,7 +35,7 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo
vertShader = 0;
MBGL_CHECK_ERROR(glDeleteProgram(program));
program = 0;
- return;
+ throw util::ShaderException(std::string { "Fragment shader " } + name + " failed to compile");
}
// Attach shaders
@@ -51,8 +51,8 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo
if (status == 0) {
GLint logLength;
MBGL_CHECK_ERROR(glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength));
+ std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength);
if (logLength > 0) {
- std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength);
MBGL_CHECK_ERROR(glGetProgramInfoLog(program, logLength, &logLength, log.get()));
Log::Error(Event::Shader, "Program failed to link: %s", log.get());
}
@@ -63,7 +63,7 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo
fragShader = 0;
MBGL_CHECK_ERROR(glDeleteProgram(program));
program = 0;
- return;
+ throw util::ShaderException(std::string { "Program " } + name + " failed to link: " + log.get());
}
}
@@ -76,8 +76,8 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo
if (status == 0) {
GLint logLength;
MBGL_CHECK_ERROR(glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength));
+ std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength);
if (logLength > 0) {
- std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength);
MBGL_CHECK_ERROR(glGetProgramInfoLog(program, logLength, &logLength, log.get()));
Log::Error(Event::Shader, "Program failed to validate: %s", log.get());
}
@@ -88,6 +88,7 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo
fragShader = 0;
MBGL_CHECK_ERROR(glDeleteProgram(program));
program = 0;
+ throw util::ShaderException(std::string { "Program " } + name + " failed to link: " + log.get());
}
}
@@ -96,8 +97,6 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo
MBGL_CHECK_ERROR(glDeleteShader(vertShader));
MBGL_CHECK_ERROR(glDetachShader(program, fragShader));
MBGL_CHECK_ERROR(glDeleteShader(fragShader));
-
- valid = true;
}
@@ -128,7 +127,7 @@ bool Shader::compileShader(GLuint *shader, GLenum type, const GLchar *source) {
MBGL_CHECK_ERROR(glGetShaderiv(*shader, GL_COMPILE_STATUS, &status));
if (status == GL_FALSE) {
- Log::Error(Event::Shader, "Shader %s failed to compile.", name, type);
+ Log::Error(Event::Shader, "Shader %s failed to compile.", name);
MBGL_CHECK_ERROR(glDeleteShader(*shader));
*shader = 0;
return false;
@@ -141,6 +140,5 @@ Shader::~Shader() {
if (program) {
MBGL_CHECK_ERROR(glDeleteProgram(program));
program = 0;
- valid = false;
}
}
diff --git a/src/mbgl/shader/shader.hpp b/src/mbgl/shader/shader.hpp
index 965180ff96..b3d9b4e46c 100644
--- a/src/mbgl/shader/shader.hpp
+++ b/src/mbgl/shader/shader.hpp
@@ -13,7 +13,6 @@ public:
Shader(const char *name, const char *vertex, const char *fragment);
~Shader();
const char *name;
- bool valid;
uint32_t program;
inline uint32_t getID() const {
diff --git a/src/mbgl/storage/default_file_source.cpp b/src/mbgl/storage/default_file_source.cpp
index c6b201b559..9f70ac9943 100644
--- a/src/mbgl/storage/default_file_source.cpp
+++ b/src/mbgl/storage/default_file_source.cpp
@@ -50,6 +50,10 @@ struct DefaultFileSource::ResultAction {
struct DefaultFileSource::StopAction {
};
+struct DefaultFileSource::AbortAction {
+ const Environment &env;
+};
+
DefaultFileSource::DefaultFileSource(FileCache *cache_, const std::string &root)
: assetRoot(root.empty() ? platform::assetRoot() : root),
@@ -105,8 +109,9 @@ SharedRequestBase *DefaultFileSource::find(const Resource &resource) {
return nullptr;
}
-Request *DefaultFileSource::request(const Resource &resource, uv_loop_t *l, Callback callback) {
- auto req = new Request(resource, l, std::move(callback));
+Request *DefaultFileSource::request(const Resource &resource, uv_loop_t *l, const Environment &env,
+ Callback callback) {
+ auto req = new Request(resource, l, env, std::move(callback));
// This function can be called from any thread. Make sure we're executing the actual call in the
// file source loop by sending it over the queue. It will be processed in processAction().
@@ -114,8 +119,9 @@ Request *DefaultFileSource::request(const Resource &resource, uv_loop_t *l, Call
return req;
}
-void DefaultFileSource::request(const Resource &resource, Callback callback) {
- auto req = new Request(resource, nullptr, std::move(callback));
+void DefaultFileSource::request(const Resource &resource, const Environment &env,
+ Callback callback) {
+ auto req = new Request(resource, nullptr, env, std::move(callback));
// This function can be called from any thread. Make sure we're executing the actual call in the
// file source loop by sending it over the queue. It will be processed in processAction().
@@ -130,6 +136,11 @@ void DefaultFileSource::cancel(Request *req) {
queue->send(RemoveRequestAction{ req });
}
+void DefaultFileSource::abort(const Environment &env) {
+ queue->send(AbortAction{ env });
+}
+
+
void DefaultFileSource::process(AddRequestAction &action) {
const Resource &resource = action.request->resource;
@@ -209,18 +220,45 @@ void DefaultFileSource::process(ResultAction &action) {
}
}
+// A stop action means the file source is about to be destructed. We need to cancel all requests
+// for all environments.
void DefaultFileSource::process(StopAction &) {
- // Cancel all remaining requests.
- for (auto it : pending) {
- it.second->unsubscribeAll();
- }
- pending.clear();
-
+ // There may not be any pending requests in this file source anymore. You must terminate all
+ // Map objects before deleting the FileSource.
+ assert(pending.empty());
assert(queue);
queue->stop();
queue = nullptr;
}
+// Aborts all requests that are part of the current environment.
+void DefaultFileSource::process(AbortAction &action) {
+ // Construct a cancellation response.
+ auto res = util::make_unique<Response>();
+ res->status = Response::Error;
+ res->message = "Environment is terminating";
+ std::shared_ptr<const Response> response = std::move(res);
+
+ // Iterate through all pending requests and remove them in case they're abandoned.
+ util::erase_if(pending, [&](const std::pair<Resource, SharedRequestBase *> &it) -> bool {
+ // Obtain all pending requests that are in the current environment.
+ const auto aborted = it.second->removeAllInEnvironment(action.env);
+
+ // Notify all observers.
+ for (auto req : aborted) {
+ req->notify(response);
+ }
+
+ // Finally, remove all requests that are now abandoned.
+ if (it.second->abandoned()) {
+ it.second->cancel();
+ return true;
+ } else {
+ return false;
+ }
+ });
+}
+
void DefaultFileSource::notify(SharedRequestBase *sharedRequest,
const std::set<Request *> &observers,
std::shared_ptr<const Response> response, FileCache::Hint hint) {
@@ -235,8 +273,8 @@ void DefaultFileSource::notify(SharedRequestBase *sharedRequest,
}
// Notify all observers.
- for (auto it : observers) {
- it->notify(response);
+ for (auto req : observers) {
+ req->notify(response);
}
}
diff --git a/src/mbgl/storage/request.cpp b/src/mbgl/storage/request.cpp
index de18138ec2..ed7f625e86 100644
--- a/src/mbgl/storage/request.cpp
+++ b/src/mbgl/storage/request.cpp
@@ -3,6 +3,7 @@
#include <mbgl/storage/response.hpp>
#include <mbgl/util/util.hpp>
+#include <mbgl/util/std.hpp>
#include <mbgl/util/uv.hpp>
#include <uv.h>
@@ -12,39 +13,39 @@
namespace mbgl {
+struct Request::Canceled { std::mutex mutex; bool confirmed = false; };
+
// Note: This requires that loop is running in the current thread (or not yet running).
-Request::Request(const Resource &resource_, uv_loop_t *loop, Callback callback_)
- : callback(callback_), resource(resource_) {
+Request::Request(const Resource &resource_, uv_loop_t *loop, const Environment &env_, Callback callback_)
+ : callback(callback_), resource(resource_), env(env_) {
// When there is no loop supplied (== nullptr), the callback will be fired in an arbitrary
// thread (the thread notify() is called from) rather than kicking back to the calling thread.
if (loop) {
- notifyAsync = new uv_async_t;
- notifyAsync->data = nullptr;
+ async = new uv_async_t;
+ async->data = this;
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
- uv_async_init(loop, notifyAsync, [](uv_async_t *async, int) { notifyCallback(async); });
+ uv_async_init(loop, async, [](uv_async_t *a, int) { reinterpret_cast<Request *>(a->data)->notifyCallback(); });
#else
- uv_async_init(loop, notifyAsync, notifyCallback);
+ uv_async_init(loop, async, [](uv_async_t *a) { reinterpret_cast<Request *>(a->data)->notifyCallback(); });
#endif
}
}
-void Request::notifyCallback(uv_async_t *async) {
- auto request = reinterpret_cast<Request *>(async->data);
- uv::close(async);
- assert(request);
- MBGL_VERIFY_THREAD(request->tid)
-
- if (!request->destructAsync) {
- // Call the callback with the result data. This will also delete this object. We haven't
- // created a cancel request, so this is safe since it won't be accessed in the future.
- // It is up to the user to not call cancel() on this Request object after the response was
- // delivered.
- request->invoke();
+// Called in the originating thread.
+void Request::notifyCallback() {
+ MBGL_VERIFY_THREAD(tid)
+ if (!canceled) {
+ invoke();
} else {
- // Otherwise, we're waiting for for the destruct notification to be delivered in order
- // to delete the Request object. We're doing this since we can't know whether the
- // DefaultFileSource is still sending a cancel event, which means this object must still
- // exist.
+ bool destroy = false;
+ {
+ std::unique_lock<std::mutex> lock(canceled->mutex);
+ destroy = canceled->confirmed;
+ }
+ // Don't delete right way, because we have to unlock the mutex before deleting.
+ if (destroy) {
+ delete this;
+ }
}
}
@@ -59,15 +60,21 @@ void Request::invoke() {
}
Request::~Request() {
+ if (async) {
+ uv_close(reinterpret_cast<uv_handle_t*>(async), [](uv_handle_t* handle) {
+ delete reinterpret_cast<uv_async_t*>(handle);
+ });
+ }
}
+// Called in the FileSource thread.
void Request::notify(const std::shared_ptr<const Response> &response_) {
+ assert(!response);
response = response_;
assert(response);
- if (notifyAsync) {
- assert(!notifyAsync->data);
- notifyAsync->data = this;
- uv_async_send(notifyAsync);
+
+ if (async) {
+ uv_async_send(async);
} else {
// This request is not cancelable. This means that the callback will be executed in an
// arbitrary thread (== FileSource thread).
@@ -75,45 +82,25 @@ void Request::notify(const std::shared_ptr<const Response> &response_) {
}
}
+// Called in the originating thread.
void Request::cancel() {
MBGL_VERIFY_THREAD(tid)
- assert(notifyAsync);
- assert(!destructAsync);
- destructAsync = new uv_async_t;
- destructAsync->data = nullptr;
-#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
- uv_async_init(notifyAsync->loop, destructAsync, [](uv_async_t *async, int) { cancelCallback(async); });
-#else
- uv_async_init(notifyAsync->loop, destructAsync, cancelCallback);
-#endif
+ assert(async);
+ assert(!canceled);
+ canceled = util::make_unique<Canceled>();
}
-void Request::cancelCallback(uv_async_t *async) {
- // The destructAsync will be invoked *after* the notifyAsync callback has already run.
- auto request = reinterpret_cast<Request *>(async->data);
- uv::close(async);
- assert(request);
- MBGL_VERIFY_THREAD(request->tid)
- delete request;
-}
-// This gets called from the FileSource thread, and will only ever be invoked after cancel() was called
-// in the original requesting thread.
+// Called in the FileSource thread.
+// Will only ever be invoked after cancel() was called in the original requesting thread.
void Request::destruct() {
- assert(notifyAsync);
- assert(destructAsync);
-
- if (!notifyAsync->data) {
- // The async hasn't been triggered yet, but we need to so that it'll close the handle. The
- // callback will not delete this object since we have a destructAsync handle as well.
- notifyAsync->data = this;
- uv_async_send(notifyAsync);
- }
-
- // This will finally destruct this object.
- assert(!destructAsync->data);
- destructAsync->data = this;
- uv_async_send(destructAsync);
+ assert(async);
+ assert(canceled);
+ std::unique_lock<std::mutex> lock(canceled->mutex);
+ canceled->confirmed = true;
+ uv_async_send(async);
+ // after this method returns, the FileSource thread has no knowledge of
+ // this object anymore.
}
}
diff --git a/src/mbgl/style/filter_expression.cpp b/src/mbgl/style/filter_expression.cpp
index 7d4f60b3ed..684715b026 100644
--- a/src/mbgl/style/filter_expression.cpp
+++ b/src/mbgl/style/filter_expression.cpp
@@ -1,4 +1,5 @@
-#include <mbgl/map/vector_tile.hpp>
+#include <mbgl/style/filter_expression.hpp>
+#include <mbgl/map/geometry_tile.hpp>
#include <mbgl/platform/log.hpp>
namespace mbgl {
diff --git a/src/mbgl/style/style_bucket.hpp b/src/mbgl/style/style_bucket.hpp
index 17924490be..b79b324b0c 100644
--- a/src/mbgl/style/style_bucket.hpp
+++ b/src/mbgl/style/style_bucket.hpp
@@ -1,23 +1,23 @@
#ifndef MBGL_STYLE_STYLE_BUCKET
#define MBGL_STYLE_STYLE_BUCKET
-#include <mbgl/style/types.hpp>
#include <mbgl/style/filter_expression.hpp>
-#include <mbgl/style/style_source.hpp>
#include <mbgl/style/class_properties.hpp>
-#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/ptr.hpp>
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/uv.hpp>
namespace mbgl {
-class Source;
+class StyleSource;
class StyleBucket : public util::noncopyable {
public:
typedef util::ptr<StyleBucket> Ptr;
inline StyleBucket(StyleLayerType type_) : type(type_) {}
+
const StyleLayerType type;
std::string name;
util::ptr<StyleSource> style_source;
diff --git a/src/mbgl/style/style_layer.hpp b/src/mbgl/style/style_layer.hpp
index 1dadfed94b..6e1fc7912b 100644
--- a/src/mbgl/style/style_layer.hpp
+++ b/src/mbgl/style/style_layer.hpp
@@ -24,7 +24,7 @@ class StyleLayer {
public:
StyleLayer(const std::string &id, std::map<ClassID, ClassProperties> &&styles);
- template <typename T> const T &getProperties() {
+ template <typename T> const T &getProperties() const {
if (properties.is<T>()) {
return properties.get<T>();
} else {
diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp
index 4745cf2833..f2bd8e6c88 100644
--- a/src/mbgl/style/style_parser.cpp
+++ b/src/mbgl/style/style_parser.cpp
@@ -4,6 +4,7 @@
#include <mbgl/util/constants.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/vec.hpp>
+#include <mbgl/util/uv_detail.hpp>
#include <mbgl/platform/log.hpp>
#include <csscolorparser/csscolorparser.hpp>
@@ -28,6 +29,37 @@ void StyleParser::parse(JSVal document) {
if (document.HasMember("layers")) {
root = createLayers(document["layers"]);
parseLayers();
+
+ // create point annotations layer
+ //
+ std::string id = util::ANNOTATIONS_POINTS_LAYER_ID;
+
+ std::map<ClassID, ClassProperties> paints;
+ util::ptr<StyleLayer> annotations = std::make_shared<StyleLayer>(id, std::move(paints));
+ annotations->type = StyleLayerType::Symbol;
+ layers.emplace(id, std::pair<JSVal, util::ptr<StyleLayer>> { JSVal(id), annotations });
+ root->layers.emplace_back(annotations);
+
+ util::ptr<StyleBucket> pointBucket = std::make_shared<StyleBucket>(annotations->type);
+ pointBucket->name = annotations->id;
+ pointBucket->source_layer = annotations->id;
+
+ rapidjson::Document d;
+ rapidjson::Value iconImage(rapidjson::kObjectType);
+ iconImage.AddMember("icon-image", "{sprite}", d.GetAllocator());
+ parseLayout(iconImage, pointBucket);
+ rapidjson::Value iconOverlap(rapidjson::kObjectType);
+ iconOverlap.AddMember("icon-allow-overlap", true, d.GetAllocator());
+ parseLayout(iconOverlap, pointBucket);
+
+ SourceInfo& info = sources.emplace(id, std::make_shared<StyleSource>()).first->second->info;
+ info.type = SourceType::Annotations;
+
+ auto source_it = sources.find(id);
+ pointBucket->style_source = source_it->second;
+ annotations->bucket = pointBucket;
+ //
+ // end point annotations
}
if (document.HasMember("sprite")) {
@@ -74,7 +106,7 @@ template<> bool StyleParser::parseRenderProperty(JSVal value, bool &target, cons
target = property.GetBool();
return true;
} else {
- fprintf(stderr, "[WARNING] '%s' must be a boolean\n", name);
+ Log::Warning(Event::ParseStyle, "'%s' must be a boolean", name);
}
}
return false;
@@ -319,7 +351,6 @@ template <> inline float defaultBaseValue<Color>() { return 1.0; }
template <typename T>
std::tuple<bool, Function<T>> StyleParser::parseFunction(JSVal value, const char *property_name) {
-
if (!value.IsObject()) {
return std::tuple<bool, Function<T>> { true, ConstantFunction<T>(std::get<1>(parseProperty<T>(value, property_name))) };
}
@@ -946,7 +977,7 @@ void StyleParser::parseBucket(JSVal value, util::ptr<StyleLayer> &layer) {
if (value.HasMember("maxzoom")) {
JSVal max_zoom = value["maxzoom"];
if (max_zoom.IsNumber()) {
- bucket->min_zoom = max_zoom.GetDouble();
+ bucket->max_zoom = max_zoom.GetDouble();
} else {
Log::Warning(Event::ParseStyle, "maxzoom of layer %s must be numeric", layer->id.c_str());
}
diff --git a/src/mbgl/style/types.hpp b/src/mbgl/style/types.hpp
index 78938a2823..3b24d63998 100644
--- a/src/mbgl/style/types.hpp
+++ b/src/mbgl/style/types.hpp
@@ -48,7 +48,8 @@ enum class SourceType : uint8_t {
Vector,
Raster,
GeoJSON,
- Video
+ Video,
+ Annotations
};
MBGL_DEFINE_ENUM_CLASS(SourceTypeClass, SourceType, {
@@ -56,6 +57,7 @@ MBGL_DEFINE_ENUM_CLASS(SourceTypeClass, SourceType, {
{ SourceType::Raster, "raster" },
{ SourceType::GeoJSON, "geojson" },
{ SourceType::Video, "video" },
+ { SourceType::Annotations, "annotations" },
});
// -------------------------------------------------------------------------------------------------
diff --git a/src/mbgl/style/value.cpp b/src/mbgl/style/value.cpp
index ae51ce3783..41730470ed 100644
--- a/src/mbgl/style/value.cpp
+++ b/src/mbgl/style/value.cpp
@@ -1,33 +1,6 @@
#include <mbgl/style/value.hpp>
#include <mbgl/util/string.hpp>
-mbgl::Value mbgl::parseValue(pbf data) {
- while (data.next())
- {
- switch (data.tag)
- {
- case 1: // string_value
- return data.string();
- case 2: // float_value
- return static_cast<double>(data.float32());
- case 3: // double_value
- return data.float64();
- case 4: // int_value
- return data.varint<int64_t>();
- case 5: // uint_value
- return data.varint<uint64_t>();
- case 6: // sint_value
- return data.svarint<int64_t>();
- case 7: // bool_value
- return data.boolean();
- default:
- data.skip();
- break;
- }
- }
- return false;
-}
-
std::string mbgl::toString(const mbgl::Value& value) {
if (value.is<std::string>()) return value.get<std::string>();
else if (value.is<bool>()) return value.get<bool>() ? "true" : "false";
diff --git a/src/mbgl/style/value.hpp b/src/mbgl/style/value.hpp
index 87d6f4cda3..8b0b21c20c 100644
--- a/src/mbgl/style/value.hpp
+++ b/src/mbgl/style/value.hpp
@@ -2,7 +2,6 @@
#define MBGL_STYLE_VALUE
#include <mbgl/util/variant.hpp>
-#include <mbgl/util/pbf.hpp>
#include <rapidjson/document.h>
#include <cstdlib>
@@ -14,7 +13,6 @@ typedef mapbox::util::variant<bool, int64_t, uint64_t, double, std::string> Valu
std::string toString(const Value &value);
-Value parseValue(pbf data);
Value parseValue(const rapidjson::Value&);
namespace util {
diff --git a/src/mbgl/text/glyph_store.cpp b/src/mbgl/text/glyph_store.cpp
index 0d9e70d556..ab1776c04b 100644
--- a/src/mbgl/text/glyph_store.cpp
+++ b/src/mbgl/text/glyph_store.cpp
@@ -1,5 +1,6 @@
#include <mbgl/text/glyph_store.hpp>
+#include <mbgl/map/environment.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/utf.hpp>
@@ -137,9 +138,11 @@ void FontStack::lineWrap(Shaping &shaping, const float lineHeight, const float m
align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line);
}
-GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, FileSource& fileSource)
- : future(promise.get_future().share())
-{
+GlyphPBF::GlyphPBF(const std::string &glyphURL,
+ const std::string &fontStack,
+ GlyphRange glyphRange,
+ Environment &env)
+ : future(promise.get_future().share()) {
// Load the glyph set URL
std::string url = util::replaceTokens(glyphURL, [&](const std::string &name) -> std::string {
if (name == "fontstack") return util::percentEncode(fontStack);
@@ -148,7 +151,7 @@ GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, Gl
});
// The prepare call jumps back to the main thread.
- fileSource.request({ Resource::Kind::Glyphs, url }, [&, url](const Response &res) {
+ env.requestAsync({ Resource::Kind::Glyphs, url }, [&, url](const Response &res) {
if (res.status != Response::Successful) {
// Something went wrong with loading the glyph pbf. Pass on the error to the future listeners.
const std::string msg = std::string { "[ERROR] failed to load glyphs: " } + res.message;
@@ -223,7 +226,7 @@ void GlyphPBF::parse(FontStack &stack) {
data.clear();
}
-GlyphStore::GlyphStore(FileSource& fileSource_) : fileSource(fileSource_) {}
+GlyphStore::GlyphStore(Environment& env_) : env(env_), mtx(util::make_unique<uv::mutex>()) {}
void GlyphStore::setURL(const std::string &url) {
glyphURL = url;
@@ -237,15 +240,14 @@ void GlyphStore::waitForGlyphRanges(const std::string &fontStack, const std::set
return;
}
- FontStack *stack = nullptr;
+ uv::exclusive<FontStack> stack(mtx);
std::vector<std::shared_future<GlyphPBF &>> futures;
futures.reserve(glyphRanges.size());
{
- std::lock_guard<std::mutex> lock(mtx);
auto &rangeSets = ranges[fontStack];
- stack = &createFontStack(fontStack);
+ stack << createFontStack(fontStack);
// Attempt to load the glyph range. If the GlyphSet already exists, we are getting back
// the same shared_future.
@@ -258,7 +260,7 @@ void GlyphStore::waitForGlyphRanges(const std::string &fontStack, const std::set
// When we get a result (or the GlyphSet is aready loaded), we are attempting to parse the
// GlyphSet.
for (std::shared_future<GlyphPBF &> &future : futures) {
- future.get().parse(*stack);
+ future.get().parse(stack);
}
}
@@ -266,7 +268,7 @@ std::shared_future<GlyphPBF &> GlyphStore::loadGlyphRange(const std::string &fon
auto range_it = rangeSets.find(range);
if (range_it == rangeSets.end()) {
// We don't have this glyph set yet for this font stack.
- range_it = rangeSets.emplace(range, util::make_unique<GlyphPBF>(glyphURL, fontStack, range, fileSource)).first;
+ range_it = rangeSets.emplace(range, util::make_unique<GlyphPBF>(glyphURL, fontStack, range, env)).first;
}
return range_it->second->getFuture();
@@ -277,12 +279,14 @@ FontStack &GlyphStore::createFontStack(const std::string &fontStack) {
if (stack_it == stacks.end()) {
stack_it = stacks.emplace(fontStack, util::make_unique<FontStack>()).first;
}
+
return *stack_it->second.get();
}
-FontStack &GlyphStore::getFontStack(const std::string &fontStack) {
- std::lock_guard<std::mutex> lock(mtx);
- return createFontStack(fontStack);
+uv::exclusive<FontStack> GlyphStore::getFontStack(const std::string &fontStack) {
+ uv::exclusive<FontStack> stack(mtx);
+ stack << createFontStack(fontStack);
+ return stack;
}
diff --git a/src/mbgl/text/glyph_store.hpp b/src/mbgl/text/glyph_store.hpp
index 95ab92f307..ee2097536c 100644
--- a/src/mbgl/text/glyph_store.hpp
+++ b/src/mbgl/text/glyph_store.hpp
@@ -2,9 +2,9 @@
#define MBGL_TEXT_GLYPH_STORE
#include <mbgl/text/glyph.hpp>
-#include <mbgl/util/pbf.hpp>
#include <mbgl/util/vec.hpp>
#include <mbgl/util/ptr.hpp>
+#include <mbgl/util/uv.hpp>
#include <cstdint>
#include <vector>
@@ -16,6 +16,7 @@
namespace mbgl {
class FileSource;
+class Environment;
class SDFGlyph {
public:
@@ -48,7 +49,10 @@ private:
class GlyphPBF {
public:
- GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, FileSource& fileSource);
+ GlyphPBF(const std::string &glyphURL,
+ const std::string &fontStack,
+ GlyphRange glyphRange,
+ Environment &env);
private:
GlyphPBF(const GlyphPBF &) = delete;
@@ -71,12 +75,12 @@ private:
// Manages Glyphrange PBF loading.
class GlyphStore {
public:
- GlyphStore(FileSource& fileSource);
+ GlyphStore(Environment &);
// Block until all specified GlyphRanges of the specified font stack are loaded.
void waitForGlyphRanges(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges);
- FontStack &getFontStack(const std::string &fontStack);
+ uv::exclusive<FontStack> getFontStack(const std::string &fontStack);
void setURL(const std::string &url);
@@ -87,10 +91,10 @@ private:
FontStack &createFontStack(const std::string &fontStack);
std::string glyphURL;
- FileSource& fileSource;
+ Environment &env;
std::unordered_map<std::string, std::map<GlyphRange, std::unique_ptr<GlyphPBF>>> ranges;
std::unordered_map<std::string, std::unique_ptr<FontStack>> stacks;
- std::mutex mtx;
+ std::unique_ptr<uv::mutex> mtx;
};
diff --git a/src/mbgl/util/clip_ids.cpp b/src/mbgl/util/clip_ids.cpp
index 9c391c38ad..8b1dcf8dd0 100644
--- a/src/mbgl/util/clip_ids.cpp
+++ b/src/mbgl/util/clip_ids.cpp
@@ -1,6 +1,7 @@
#include <mbgl/util/clip_ids.hpp>
#include <mbgl/map/tile.hpp>
+#include <mbgl/platform/log.hpp>
#include <mbgl/util/math.hpp>
#include <list>
@@ -89,7 +90,7 @@ void ClipIDGenerator::update(std::forward_list<Tile *> tiles) {
}
if (bit_offset > 8) {
- fprintf(stderr, "stencil mask overflow\n");
+ Log::Error(Event::OpenGL, "stencil mask overflow");
}
}
diff --git a/platform/default/compression.cpp b/src/mbgl/util/compression.cpp
index c8b38e742f..3a1658b8f6 100644
--- a/platform/default/compression.cpp
+++ b/src/mbgl/util/compression.cpp
@@ -87,7 +87,7 @@ std::string decompress(const std::string &raw) {
inflateEnd(&inflate_stream);
if (code != Z_STREAM_END) {
- throw std::runtime_error(inflate_stream.msg);
+ throw std::runtime_error(inflate_stream.msg ? inflate_stream.msg : "decompression error");
}
return result;
diff --git a/platform/default/compression.hpp b/src/mbgl/util/compression.hpp
index a33b2476a7..a33b2476a7 100644
--- a/platform/default/compression.hpp
+++ b/src/mbgl/util/compression.hpp
diff --git a/src/mbgl/util/constants.cpp b/src/mbgl/util/constants.cpp
index ccdbeba23a..ae5b21ddc4 100644
--- a/src/mbgl/util/constants.cpp
+++ b/src/mbgl/util/constants.cpp
@@ -8,6 +8,8 @@ const double mbgl::util::M2PI = 2 * M_PI;
const double mbgl::util::EARTH_RADIUS_M = 6378137;
const double mbgl::util::LATITUDE_MAX = 85.05112878;
+const std::string mbgl::util::ANNOTATIONS_POINTS_LAYER_ID = "com.mapbox.annotations.points";
+
#if defined(DEBUG)
const bool mbgl::debug::tileParseWarnings = false;
const bool mbgl::debug::styleParseWarnings = false;
diff --git a/src/mbgl/util/mapbox.cpp b/src/mbgl/util/mapbox.cpp
index 277b647f34..5d9c630c11 100644
--- a/src/mbgl/util/mapbox.cpp
+++ b/src/mbgl/util/mapbox.cpp
@@ -26,8 +26,7 @@ std::string normalizeSourceURL(const std::string& url, const std::string& access
// TileJSON requests need a secure flag appended to their URLs so
// that the server knows to send SSL-ified resource references.
- if (url.compare(0, 5, "https") == 0)
- result += "&secure";
+ result += "&secure";
return result;
}
@@ -39,6 +38,33 @@ std::string normalizeGlyphsURL(const std::string& url, const std::string& access
return normalizeURL(url, accessToken);
}
+std::string normalizeTileURL(const std::string& url, const std::string& sourceURL, SourceType sourceType) {
+ if (sourceURL.empty() || sourceURL.compare(0, mapbox.length(), mapbox) != 0 ||
+ sourceType != SourceType::Raster) {
+ return url;
+ }
+
+ std::string::size_type queryIdx = url.rfind("?");
+ // Trim off the right end but never touch anything before the extension dot.
+ std::string urlSansParams((queryIdx == std::string::npos) ? url : url.substr(0, queryIdx));
+
+ while (!urlSansParams.empty() && isdigit(urlSansParams.back())) {
+ urlSansParams.pop_back();
+ }
+
+ std::string::size_type basenameIdx = url.rfind("/", queryIdx);
+ std::string::size_type extensionIdx = url.rfind(".", queryIdx);
+ if (basenameIdx == std::string::npos || extensionIdx == std::string::npos ||
+ basenameIdx > extensionIdx) {
+ // No file extension: probably not a file name we can tack a ratio onto.
+ return url;
+ }
+
+ std::string normalizedURL(url);
+ normalizedURL.insert(extensionIdx, "{ratio}");
+ return normalizedURL;
+}
+
}
}
}
diff --git a/src/mbgl/util/mapbox.hpp b/src/mbgl/util/mapbox.hpp
index 0fbb9a91ed..8ad92c0b40 100644
--- a/src/mbgl/util/mapbox.hpp
+++ b/src/mbgl/util/mapbox.hpp
@@ -2,6 +2,7 @@
#define MBGL_UTIL_MAPBOX
#include <string>
+#include <mbgl/style/types.hpp>
namespace mbgl {
namespace util {
@@ -9,6 +10,7 @@ namespace mapbox {
std::string normalizeSourceURL(const std::string& url, const std::string& accessToken);
std::string normalizeGlyphsURL(const std::string& url, const std::string& accessToken);
+std::string normalizeTileURL(const std::string& url, const std::string& sourceURL, SourceType sourceType);
}
}
diff --git a/src/mbgl/util/raster.cpp b/src/mbgl/util/raster.cpp
index 724936972c..72e266af94 100644
--- a/src/mbgl/util/raster.cpp
+++ b/src/mbgl/util/raster.cpp
@@ -1,5 +1,6 @@
#include <mbgl/platform/platform.hpp>
#include <mbgl/platform/gl.hpp>
+#include <mbgl/platform/log.hpp>
#include <mbgl/util/raster.hpp>
#include <mbgl/util/uv_detail.hpp>
@@ -40,7 +41,7 @@ bool Raster::load(const std::string &data) {
void Raster::bind(bool linear) {
if (!width || !height) {
- fprintf(stderr, "trying to bind texture without dimension\n");
+ Log::Error(Event::OpenGL, "trying to bind texture without dimension");
return;
}
diff --git a/src/mbgl/util/scaling.cpp b/src/mbgl/util/scaling.cpp
new file mode 100644
index 0000000000..a554b2e137
--- /dev/null
+++ b/src/mbgl/util/scaling.cpp
@@ -0,0 +1,111 @@
+#include "scaling.hpp"
+
+namespace {
+
+using namespace mbgl;
+
+inline uint8_t bilinearInterpolate(uint8_t tl, uint8_t tr, uint8_t bl, uint8_t br, double dx, double dy) {
+ const double t = dx * (tr - tl) + tl;
+ const double b = dx * (br - bl) + bl;
+ return t + dy * (b - t);
+}
+
+template <size_t i>
+inline const uint8_t& b(const uint32_t& w) {
+ return reinterpret_cast<const uint8_t*>(&w)[i];
+}
+
+template <size_t i>
+inline uint8_t& b(uint32_t& w) {
+ return reinterpret_cast<uint8_t*>(&w)[i];
+}
+
+vec2<double> getFactor(const Rect<uint32_t>& srcPos, const Rect<uint32_t>& dstPos) {
+ return {
+ double(srcPos.w) / dstPos.w,
+ double(srcPos.h) / dstPos.h
+ };
+}
+
+vec2<uint32_t> getBounds(const vec2<uint32_t>& srcSize, const Rect<uint32_t>& srcPos,
+ const vec2<uint32_t>& dstSize, const Rect<uint32_t>& dstPos,
+ const vec2<double>& factor) {
+ if (srcPos.x > srcSize.x || srcPos.y > srcSize.y ||
+ dstPos.x > dstSize.x || dstPos.y > dstSize.y) {
+ // Source or destination position is out of range.
+ return { 0, 0 };
+ }
+
+ // Make sure we don't read/write values out of range.
+ return { std::min(uint32_t(double(srcSize.x - srcPos.x) / factor.x),
+ std::min(dstSize.x - dstPos.x, dstPos.w)),
+ std::min(uint32_t(double(srcSize.y - srcPos.y) / factor.y),
+ std::min(dstSize.y - dstPos.y, dstPos.h)) };
+}
+}
+
+namespace mbgl {
+namespace util {
+
+void bilinearScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize,
+ const Rect<uint32_t>& srcPos, uint32_t* dstData, const vec2<uint32_t>& dstSize,
+ const Rect<uint32_t>& dstPos) {
+ const auto factor = getFactor(srcPos, dstPos);
+ const auto bounds = getBounds(srcSize, srcPos, dstSize, dstPos, factor);
+
+ double fractSrcY = srcPos.y;
+ double fractSrcX;
+ uint32_t x, y;
+ size_t i = dstSize.x * dstPos.y + dstPos.x;
+ for (y = 0; y < bounds.y; y++) {
+ fractSrcX = srcPos.x;
+ const uint32_t srcY0 = fractSrcY;
+ const uint32_t srcY1 = std::min(srcY0 + 1, srcSize.y - 1);
+ for (x = 0; x < bounds.x; x++) {
+ const uint32_t srcX0 = fractSrcX;
+ const uint32_t srcX1 = std::min(srcX0 + 1, srcSize.x - 1);
+
+ const uint32_t tl = srcData[srcSize.x * srcY0 + srcX0];
+ const uint32_t tr = srcData[srcSize.x * srcY0 + srcX1];
+ const uint32_t bl = srcData[srcSize.x * srcY1 + srcX0];
+ const uint32_t br = srcData[srcSize.x * srcY1 + srcX1];
+
+ const double dx = fractSrcX - srcX0;
+ const double dy = fractSrcY - srcY0;
+ uint32_t& dst = dstData[i + x];
+ b<0>(dst) = bilinearInterpolate(b<0>(tl), b<0>(tr), b<0>(bl), b<0>(br), dx, dy);
+ b<1>(dst) = bilinearInterpolate(b<1>(tl), b<1>(tr), b<1>(bl), b<1>(br), dx, dy);
+ b<2>(dst) = bilinearInterpolate(b<2>(tl), b<2>(tr), b<2>(bl), b<2>(br), dx, dy);
+ b<3>(dst) = bilinearInterpolate(b<3>(tl), b<3>(tr), b<3>(bl), b<3>(br), dx, dy);
+ fractSrcX += factor.x;
+ }
+ i += dstSize.x;
+ fractSrcY += factor.y;
+ }
+}
+
+void nearestNeighborScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize,
+ const Rect<uint32_t>& srcPos, uint32_t* dstData,
+ const vec2<uint32_t>& dstSize, const Rect<uint32_t>& dstPos) {
+ const auto factor = getFactor(srcPos, dstPos);
+ const auto bounds = getBounds(srcSize, srcPos, dstSize, dstPos, factor);
+
+ double fractSrcY = srcPos.y;
+ double fractSrcX;
+ size_t i = dstSize.x * dstPos.y + dstPos.x;
+ uint32_t srcY;
+ uint32_t x, y;
+ for (y = 0; y < bounds.y; y++) {
+ fractSrcX = srcPos.x;
+ srcY = srcSize.x * uint32_t(fractSrcY);
+ for (x = 0; x < bounds.x; x++) {
+ dstData[i + x] = srcData[srcY + uint32_t(fractSrcX)];
+ fractSrcX += factor.x;
+ }
+ i += dstSize.x;
+ fractSrcY += factor.y;
+ }
+}
+
+}
+} \ No newline at end of file
diff --git a/src/mbgl/util/scaling.hpp b/src/mbgl/util/scaling.hpp
new file mode 100644
index 0000000000..d2625e9219
--- /dev/null
+++ b/src/mbgl/util/scaling.hpp
@@ -0,0 +1,23 @@
+#ifndef MBGL_UTIL_SCALING
+#define MBGL_UTIL_SCALING
+
+
+#include <mbgl/util/vec.hpp>
+#include <mbgl/util/rect.hpp>
+
+#include <cstdint>
+
+namespace mbgl {
+namespace util {
+
+void bilinearScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize,
+ const Rect<uint32_t>& srcPos, uint32_t* dstData, const vec2<uint32_t>& dstSize,
+ const Rect<uint32_t>& dstPos);
+
+void nearestNeighborScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize,
+ const Rect<uint32_t>& srcPos, uint32_t* dstData,
+ const vec2<uint32_t>& dstSize, const Rect<uint32_t>& dstPos);
+}
+}
+
+#endif
diff --git a/src/mbgl/util/token.hpp b/src/mbgl/util/token.hpp
index 455190aabf..a2cc267bfe 100644
--- a/src/mbgl/util/token.hpp
+++ b/src/mbgl/util/token.hpp
@@ -38,14 +38,6 @@ std::string replaceTokens(const std::string &source, const Lookup &lookup) {
return result;
}
-template <typename T>
-inline std::string replaceTokens(const std::string &source, const std::map<std::string, T> &properties) {
- return replaceTokens(source, [&properties](const std::string &token) -> std::string {
- const auto it_prop = properties.find(token);
- return it_prop != properties.end() ? toString(it_prop->second) : "";
- });
-}
-
} // end namespace util
} // end namespace mbgl
diff --git a/src/mbgl/util/uv.cpp b/src/mbgl/util/uv.cpp
index a993e6b962..d465dfd963 100644
--- a/src/mbgl/util/uv.cpp
+++ b/src/mbgl/util/uv.cpp
@@ -1,4 +1,5 @@
#include <mbgl/util/uv.hpp>
+#include <mbgl/util/uv_detail.hpp>
#include <uv.h>
@@ -22,6 +23,60 @@ std::string cwd() {
#endif
}
+
+lock::lock(mutex &mtx_) : mtx(&mtx_) {
+ if (mtx) { mtx->lock(); }
+}
+lock::lock(const std::unique_ptr<mutex> &mtx_) : mtx(mtx_.get()) {
+ if (mtx) { mtx->lock(); }
+}
+lock::~lock() {
+ if (mtx) { mtx->unlock(); }
+}
+lock::lock(lock &&other) {
+ std::swap(mtx, other.mtx);
+}
+lock &lock::operator=(lock &&other) {
+ std::swap(mtx, other.mtx);
+ return *this;
+}
+
+
+readlock::readlock(rwlock &mtx_) : mtx(&mtx_) {
+ if (mtx) { mtx->rdlock(); }
+}
+readlock::readlock(const std::unique_ptr<rwlock> &mtx_) : mtx(mtx_.get()) {
+ if (mtx) { mtx->rdlock(); }
+}
+readlock::~readlock() {
+ if (mtx) { mtx->rdunlock(); }
+}
+readlock::readlock(readlock &&lock) {
+ std::swap(mtx, lock.mtx);
+}
+readlock &readlock::operator=(readlock &&lock) {
+ std::swap(mtx, lock.mtx);
+ return *this;
+}
+
+
+writelock::writelock(rwlock &mtx_) : mtx(&mtx_) {
+ if (mtx) { mtx->wrlock(); }
+}
+writelock::writelock(const std::unique_ptr<rwlock> &mtx_) : mtx(mtx_.get()) {
+ if (mtx) { mtx->wrlock(); }
+}
+writelock::~writelock() {
+ if (mtx) { mtx->wrunlock(); }
+}
+writelock::writelock(writelock &&lock) {
+ std::swap(mtx, lock.mtx);
+}
+writelock &writelock::operator=(writelock &&lock) {
+ std::swap(mtx, lock.mtx);
+ return *this;
+}
+
const char *getFileRequestError(uv_fs_t *req) {
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
return uv_strerror(uv_last_error(req->loop));
diff --git a/src/mbgl/util/uv_detail.hpp b/src/mbgl/util/uv_detail.hpp
index 99f5edc145..6ae3713e09 100644
--- a/src/mbgl/util/uv_detail.hpp
+++ b/src/mbgl/util/uv_detail.hpp
@@ -1,6 +1,7 @@
#ifndef MBGL_UTIL_UV_DETAIL
#define MBGL_UTIL_UV_DETAIL
+#include <mbgl/util/uv.hpp>
#include <mbgl/util/uv-worker.h>
#include <mbgl/util/noncopyable.hpp>
@@ -85,6 +86,20 @@ private:
std::function<void ()> fn;
};
+class mutex : public mbgl::util::noncopyable {
+public:
+ inline mutex() {
+ if (uv_mutex_init(&mtx) != 0) {
+ throw std::runtime_error("failed to initialize mutex lock");
+ }
+ }
+ inline ~mutex() { uv_mutex_destroy(&mtx); }
+ inline void lock() { uv_mutex_lock(&mtx); }
+ inline void unlock() { uv_mutex_unlock(&mtx); }
+private:
+ uv_mutex_t mtx;
+};
+
class rwlock : public mbgl::util::noncopyable {
public:
inline rwlock() {
@@ -102,26 +117,6 @@ private:
uv_rwlock_t mtx;
};
-class readlock : public mbgl::util::noncopyable {
-public:
- inline readlock(rwlock &mtx_) : mtx(mtx_) { mtx.rdlock(); }
- inline readlock(const std::unique_ptr<rwlock> &mtx_) : mtx(*mtx_) { mtx.rdlock(); }
- inline ~readlock() { mtx.rdunlock(); }
-
-private:
- rwlock &mtx;
-};
-
-class writelock : public mbgl::util::noncopyable {
-public:
- inline writelock(rwlock &mtx_) : mtx(mtx_) { mtx.wrlock(); }
- inline writelock(const std::unique_ptr<rwlock> &mtx_) : mtx(*mtx_) { mtx.wrlock(); }
- inline ~writelock() { mtx.wrunlock(); }
-
-private:
- rwlock &mtx;
-};
-
class worker : public mbgl::util::noncopyable {
public:
inline worker(uv_loop_t *loop, unsigned int count, const char *name = nullptr) : w(new uv_worker_t) {
diff --git a/styles b/styles
-Subproject 25b1b7dff37a18151e3286144bc8013b432a886
+Subproject c1303933798e1bb48649d8a64b71038b4d3ed0a
diff --git a/test/fixtures/fixture_log.cpp b/test/fixtures/fixture_log.cpp
deleted file mode 100644
index 02715fd43d..0000000000
--- a/test/fixtures/fixture_log.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-#include "fixture_log.hpp"
-
-namespace mbgl {
-
-FixtureLogBackend::LogMessage::LogMessage(EventSeverity severity_, Event event_, int64_t code_,
- const std::string &msg_)
- : severity(severity_), event(event_), code(code_), msg(msg_) {
-}
-FixtureLogBackend::LogMessage::LogMessage(EventSeverity severity_, Event event_, int64_t code_)
- : severity(severity_), event(event_), code(code_), msg() {
-}
-FixtureLogBackend::LogMessage::LogMessage(EventSeverity severity_, Event event_,
- const std::string &msg_)
- : severity(severity_), event(event_), code(), msg(msg_) {
-}
-
-FixtureLogBackend::LogMessage::LogMessage(EventSeverity severity_, Event event_)
- : severity(severity_), event(event_), code(), msg() {
-}
-
-bool FixtureLogBackend::LogMessage::operator==(const LogMessage &rhs) const {
- return (!severity || !rhs.severity || severity.get() == rhs.severity.get()) &&
- (!event || !rhs.event || event.get() == rhs.event.get()) &&
- (!code || !rhs.code || code.get() == rhs.code.get()) &&
- (!msg || !rhs.msg || msg.get() == rhs.msg.get());
-}
-
-FixtureLogBackend::~FixtureLogBackend() {
- std::cerr << unchecked();
-}
-
-void FixtureLogBackend::record(EventSeverity severity, Event event, const std::string &msg) {
- messages.emplace_back(severity, event, msg);
-}
-
-void FixtureLogBackend::record(EventSeverity severity, Event event, const char *format, ...) {
- va_list args;
- va_start(args, format);
- const size_t len = vsnprintf(NULL, 0, format, args);
- va_end(args);
- std::unique_ptr<char[]> buffer(new char[len + 1]);
- va_start(args, format);
- vsnprintf(buffer.get(), len + 1, format, args);
- va_end(args);
- messages.emplace_back(severity, event, std::string{ buffer.get(), len });
-}
-
-void FixtureLogBackend::record(EventSeverity severity, Event event, int64_t code) {
- messages.emplace_back(severity, event, code);
-}
-
-void FixtureLogBackend::record(EventSeverity severity, Event event, int64_t code,
- const std::string &msg) {
- messages.emplace_back(severity, event, code, msg);
-}
-
-size_t FixtureLogBackend::count(const LogMessage &message) const {
- size_t message_count = 0;
- for (const LogMessage &msg : messages) {
- if (msg == message) {
- message_count++;
- msg.checked = true;
- }
- }
- return message_count;
-}
-
-std::vector<FixtureLogBackend::LogMessage> FixtureLogBackend::unchecked() const {
- std::vector<LogMessage> unchecked_messages;
- for (const LogMessage &msg : messages) {
- if (!msg.checked) {
- unchecked_messages.push_back(msg);
- msg.checked = true;
- }
- }
- return unchecked_messages;
-}
-
-::std::ostream &operator<<(::std::ostream &os,
- const std::vector<FixtureLogBackend::LogMessage> &messages) {
- for (const FixtureLogBackend::LogMessage &message : messages) {
- os << "- " << message;
- }
- return os;
-}
-
-::std::ostream &operator<<(::std::ostream &os, const FixtureLogBackend::LogMessage &message) {
- os << "[\"" << message.severity.get() << "\", \"" << message.event.get() << "\"";
- if (message.code)
- os << ", " << message.code.get();
- if (message.msg)
- os << ", \"" << message.msg.get() << "\"";
- return os << "]" << std::endl;
-}
-
-} \ No newline at end of file
diff --git a/test/fixtures/fixture_log.hpp b/test/fixtures/fixture_log.hpp
deleted file mode 100644
index bc7c9fab31..0000000000
--- a/test/fixtures/fixture_log.hpp
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef MBGL_TEST_FIXTURE_LOG
-#define MBGL_TEST_FIXTURE_LOG
-
-#include <mbgl/platform/log.hpp>
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/optional.hpp>
-
-#include <vector>
-#include <cstdarg>
-#include <iostream>
-
-namespace mbgl {
-
-class FixtureLogBackend : public LogBackend, private util::noncopyable {
-public:
- struct LogMessage {
- LogMessage(EventSeverity severity_, Event event_, int64_t code_, const std::string &msg_);
- LogMessage(EventSeverity severity_, Event event_, int64_t code_);
- LogMessage(EventSeverity severity_, Event event_, const std::string &msg_);
- LogMessage(EventSeverity severity_, Event event_);
-
- bool operator==(const LogMessage &rhs) const;
-
- const mapbox::util::optional<EventSeverity> severity;
- const mapbox::util::optional<Event> event;
- const mapbox::util::optional<int64_t> code;
- const mapbox::util::optional<std::string> msg;
-
- mutable bool checked = false;
- };
-
- ~FixtureLogBackend();
-
- inline void record(EventSeverity severity, Event event, const std::string &msg);
- inline void record(EventSeverity severity, Event event, const char *format, ...);
- inline void record(EventSeverity severity, Event event, int64_t code);
- inline void record(EventSeverity severity, Event event, int64_t code, const std::string &msg);
-
- size_t count(const LogMessage &message) const;
- std::vector<LogMessage> unchecked() const;
-
-public:
- std::vector<LogMessage> messages;
-};
-
-::std::ostream &operator<<(::std::ostream &os,
- const std::vector<FixtureLogBackend::LogMessage> &messages);
-::std::ostream &operator<<(::std::ostream &os, const FixtureLogBackend::LogMessage &message);
-
-}
-
-#endif
diff --git a/test/fixtures/fixture_log_observer.cpp b/test/fixtures/fixture_log_observer.cpp
new file mode 100644
index 0000000000..08a6415643
--- /dev/null
+++ b/test/fixtures/fixture_log_observer.cpp
@@ -0,0 +1,68 @@
+#include "fixture_log_observer.hpp"
+#include "../fixtures/util.hpp"
+
+namespace mbgl {
+
+FixtureLogObserver::LogMessage::LogMessage(EventSeverity severity_, Event event_, int64_t code_,
+ const std::string &msg_)
+ : severity(severity_), event(event_), code(code_), msg(msg_) {
+}
+
+bool FixtureLogObserver::LogMessage::operator==(const LogMessage &rhs) const {
+ return (!severity || !rhs.severity || severity.get() == rhs.severity.get()) &&
+ (!event || !rhs.event || event.get() == rhs.event.get()) &&
+ (!code || !rhs.code || code.get() == rhs.code.get()) &&
+ (!msg || !rhs.msg || msg.get() == rhs.msg.get());
+}
+
+FixtureLogObserver::~FixtureLogObserver() {
+ std::cerr << unchecked();
+}
+
+bool FixtureLogObserver::onRecord(EventSeverity severity, Event event, int64_t code,
+ const std::string &msg) {
+ messages.emplace_back(severity, event, code, msg);
+
+ return true;
+}
+
+size_t FixtureLogObserver::count(const LogMessage &message) const {
+ size_t message_count = 0;
+ for (const LogMessage &msg : messages) {
+ if (msg == message) {
+ message_count++;
+ msg.checked = true;
+ }
+ }
+ return message_count;
+}
+
+std::vector<FixtureLogObserver::LogMessage> FixtureLogObserver::unchecked() const {
+ std::vector<LogMessage> unchecked_messages;
+ for (const LogMessage &msg : messages) {
+ if (!msg.checked) {
+ unchecked_messages.push_back(msg);
+ msg.checked = true;
+ }
+ }
+ return unchecked_messages;
+}
+
+::std::ostream &operator<<(::std::ostream &os,
+ const std::vector<FixtureLogObserver::LogMessage> &messages) {
+ for (const FixtureLogObserver::LogMessage &message : messages) {
+ os << "- " << message;
+ }
+ return os;
+}
+
+::std::ostream &operator<<(::std::ostream &os, const FixtureLogObserver::LogMessage &message) {
+ os << "[\"" << message.severity.get() << "\", \"" << message.event.get() << "\"";
+ if (message.code)
+ os << ", " << message.code.get();
+ if (message.msg)
+ os << ", \"" << message.msg.get() << "\"";
+ return os << "]" << std::endl;
+}
+
+}
diff --git a/test/fixtures/fixture_log_observer.hpp b/test/fixtures/fixture_log_observer.hpp
new file mode 100644
index 0000000000..bdb7ea2ca3
--- /dev/null
+++ b/test/fixtures/fixture_log_observer.hpp
@@ -0,0 +1,47 @@
+#ifndef MBGL_TEST_FIXTURE_LOG_OBSERVER
+#define MBGL_TEST_FIXTURE_LOG_OBSERVER
+
+#include <mbgl/platform/log.hpp>
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <vector>
+#include <cstdarg>
+#include <iostream>
+
+namespace mbgl {
+
+class FixtureLogObserver : public Log::Observer, private util::noncopyable {
+public:
+ struct LogMessage {
+ LogMessage(EventSeverity severity_, Event event_, int64_t code_, const std::string &msg_);
+
+ bool operator==(const LogMessage &rhs) const;
+
+ const mapbox::util::optional<EventSeverity> severity;
+ const mapbox::util::optional<Event> event;
+ const mapbox::util::optional<int64_t> code;
+ const mapbox::util::optional<std::string> msg;
+
+ mutable bool checked = false;
+ };
+
+ ~FixtureLogObserver();
+
+ // Log::Observer implementation
+ virtual bool onRecord(EventSeverity severity, Event event, int64_t code, const std::string &msg) override;
+
+ size_t count(const LogMessage &message) const;
+ std::vector<LogMessage> unchecked() const;
+
+public:
+ std::vector<LogMessage> messages;
+};
+
+::std::ostream &operator<<(::std::ostream &os,
+ const std::vector<FixtureLogObserver::LogMessage> &messages);
+::std::ostream &operator<<(::std::ostream &os, const FixtureLogObserver::LogMessage &message);
+
+}
+
+#endif
diff --git a/test/fixtures/sprites/atlas_reference.bin b/test/fixtures/sprites/atlas_reference.bin
new file mode 100644
index 0000000000..57eb28bd93
--- /dev/null
+++ b/test/fixtures/sprites/atlas_reference.bin
Binary files differ
diff --git a/test/fixtures/sprites/atlas_reference.png b/test/fixtures/sprites/atlas_reference.png
new file mode 100644
index 0000000000..86fad30983
--- /dev/null
+++ b/test/fixtures/sprites/atlas_reference.png
Binary files differ
diff --git a/test/fixtures/sprites/bright.bin b/test/fixtures/sprites/bright.bin
new file mode 100644
index 0000000000..3aee130074
--- /dev/null
+++ b/test/fixtures/sprites/bright.bin
Binary files differ
diff --git a/test/fixtures/sprites/convert_sprite.js b/test/fixtures/sprites/convert_sprite.js
new file mode 100644
index 0000000000..ba4ff5c29f
--- /dev/null
+++ b/test/fixtures/sprites/convert_sprite.js
@@ -0,0 +1,19 @@
+// Converts a PNG image to a custom "image format" that has a uint32_t width/height prefix and then
+// raw RGBA data. We can't use the built-in PNG reading routines because they are reading
+// premultiplied images by default.
+
+var fs = require('fs');
+var zlib = require('zlib');
+var PNG = require('png-js');
+var png = PNG.load('styles/sprites/bright.png');
+png.decodePixels(function(data) {
+ var result = new Buffer(8 + data.length);
+ result.writeUInt32BE(png.width, 0);
+ result.writeUInt32BE(png.height, 4);
+ data.copy(result, 8);
+
+ zlib.deflate(result, function(err, data) {
+ if (err) throw err;
+ fs.writeFileSync('test/fixtures/sprites/bright.bin', data);
+ });
+});
diff --git a/test/fixtures/util.cpp b/test/fixtures/util.cpp
index 7434393556..7d9bbbfad7 100644
--- a/test/fixtures/util.cpp
+++ b/test/fixtures/util.cpp
@@ -1,5 +1,7 @@
#include "util.hpp"
+#include <mbgl/platform/log.hpp>
+
#include <csignal>
namespace mbgl {
@@ -17,7 +19,7 @@ pid_t startServer(const char *executable) {
int ret = execv(executable, args);
// This call should not return. In case execve failed, we exit anyway.
if (ret < 0) {
- fprintf(stderr, "Failed to start server: %s\n", strerror(errno));
+ Log::Error(Event::Setup, "failed to start server: %s", strerror(errno));
}
exit(0);
} else {
diff --git a/test/headless/headless.cpp b/test/headless/headless.cpp
index b92002aa23..c47b9349a8 100644
--- a/test/headless/headless.cpp
+++ b/test/headless/headless.cpp
@@ -1,5 +1,5 @@
#include "../fixtures/util.hpp"
-#include "../fixtures/fixture_log.hpp"
+#include "../fixtures/fixture_log_observer.hpp"
#include <mbgl/map/map.hpp>
#include <mbgl/util/image.hpp>
@@ -101,7 +101,7 @@ TEST_P(HeadlessTest, render) {
ASSERT_FALSE(infoDoc.HasParseError());
ASSERT_TRUE(infoDoc.IsObject());
- Log::Set<FixtureLogBackend>();
+ Log::setObserver(util::make_unique<FixtureLogObserver>());
Log::Info(Event::General, "test fixture %s", base.c_str());
@@ -146,7 +146,6 @@ TEST_P(HeadlessTest, render) {
map.setStyleJSON(style, "test/suite");
view.resize(width, height, pixelRatio);
- map.resize(width, height, pixelRatio);
map.setLatLngZoom(mbgl::LatLng(latitude, longitude), zoom);
map.setBearing(bearing);
diff --git a/test/miscellaneous/bilinear.cpp b/test/miscellaneous/bilinear.cpp
new file mode 100644
index 0000000000..b7730303a0
--- /dev/null
+++ b/test/miscellaneous/bilinear.cpp
@@ -0,0 +1,53 @@
+#include "../fixtures/util.hpp"
+#include <mbgl/util/compression.hpp>
+#include <mbgl/util/scaling.hpp>
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/std.hpp>
+
+#include <algorithm>
+#include <cstring>
+
+using namespace mbgl;
+
+TEST(Bilinear, Scaling) {
+ // We're reading from a custom "image format" that has a uint32_t width/height prefix and then
+ // raw RGBA data. We can't use the built-in PNG reading routines because they are reading
+ // premultiplied images by default.
+ const std::string sprite = util::decompress(util::read_file("test/fixtures/sprites/bright.bin"));
+ const uint8_t *src = reinterpret_cast<const uint8_t *>(sprite.data());
+ ASSERT_GT(sprite.length(), 8u);
+ const uint32_t width = src[0] << 24 | src[1] << 16 | src[2] << 8 | src[3];
+ const uint32_t height = src[4] << 24 | src[5] << 16 | src[6] << 8 | src[7];
+ ASSERT_EQ(sprite.length(), 2 * sizeof(uint32_t) + width * height * sizeof(uint32_t));
+
+ 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 = util::make_unique<uint32_t[]>(dstSize.x * dstSize.y);
+ uint32_t *dstData = dst.get();
+ std::fill(dstData, dstData + dstSize.x * dstSize.y, 0xFFFF00FF);
+
+ util::bilinearScale(srcData, srcSize, { 0, 0, 24, 24 }, dstData, dstSize, { 8, 8, 24, 24 });
+ util::bilinearScale(srcData, srcSize, { 26, 0, 24, 24 }, dstData, dstSize, { 0, 40, 48, 48 });
+ util::bilinearScale(srcData, srcSize, { 26, 26, 24, 24 }, dstData, dstSize, { 52, 40, 36, 36 });
+ util::bilinearScale(srcData, srcSize, { 26, 26, 24, 24 }, dstData, dstSize, { 52, 40, 36, 36 });
+ util::bilinearScale(srcData, srcSize, { 104, 0, 24, 24 }, dstData, dstSize, { 96, 0, 48, 48 });
+ util::bilinearScale(srcData, srcSize, { 52, 260, 24, 24 }, dstData, dstSize, { 108, 108, 38, 38 });
+ util::bilinearScale(srcData, srcSize, { 380, 0, 24, 24 }, dstData, dstSize, { 36, 0, 24, 24 });
+ util::bilinearScale(srcData, srcSize, { 396, 396, 24, 24 }, dstData, dstSize, { 0, 0, 50, 50 });
+ util::bilinearScale(srcData, srcSize, { 380, 182, 12, 12 }, dstData, dstSize, { 52, 80, 24, 24 });
+
+ // From the bottom
+ util::bilinearScale(srcData, srcSize, { 252, 380, 12, 12 }, dstData, dstSize, { 0, 90, 12, 12 });
+ util::bilinearScale(srcData, srcSize, { 252, 380, 12, 12 }, dstData, dstSize, { 18, 90, 24, 24 });
+
+ 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, dstData));
+ 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"));
+
+ EXPECT_EQ(reference.size(), data.size());
+ EXPECT_TRUE(0 == std::memcmp(data.data(), reference.data(), data.size()));
+}
diff --git a/test/miscellaneous/mapbox.cpp b/test/miscellaneous/mapbox.cpp
new file mode 100644
index 0000000000..926c842e22
--- /dev/null
+++ b/test/miscellaneous/mapbox.cpp
@@ -0,0 +1,69 @@
+#include "../fixtures/util.hpp"
+
+#include <mbgl/platform/log.hpp>
+#include <mbgl/util/mapbox.hpp>
+#include <regex>
+#include <iostream>
+
+using namespace mbgl;
+
+TEST(Mapbox, SourceURL) {
+ EXPECT_EQ(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", "key"), "https://api.tiles.mapbox.com/v4/user.map.json?access_token=key&secure");
+ EXPECT_EQ(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", "token"), "https://api.tiles.mapbox.com/v4/user.map.json?access_token=token&secure");
+ EXPECT_THROW(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", ""), std::runtime_error);
+}
+
+TEST(Mapbox, GlyphsURL) {
+ EXPECT_EQ(mbgl::util::mapbox::normalizeGlyphsURL("mapbox://fontstack/{fontstack}/{range}.pbf", "key"), "https://api.tiles.mapbox.com/v4/fontstack/{fontstack}/{range}.pbf?access_token=key");
+ EXPECT_EQ(mbgl::util::mapbox::normalizeGlyphsURL("http://path", "key"), "http://path");
+}
+
+TEST(Mapbox, TileURL) {
+ try {
+ EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.png", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.png");
+ EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.png32", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.png32");
+ EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.png70", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.png70");
+ EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.png?access_token=foo", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.png?access_token=foo");
+ EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png", "http://path", SourceType::Raster), "http://path.png");
+ EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png", "", SourceType::Raster), "http://path.png");
+ EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.png", "mapbox://user.map", SourceType::Vector), "http://path.png/tile.png");
+ EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.pbf", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.pbf");
+ EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.pbf", "mapbox://user.map", SourceType::Vector), "http://path.png/tile.pbf");
+ EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.pbf?access_token=foo", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.pbf?access_token=foo");
+ EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.pbf?access_token=foo.png", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.pbf?access_token=foo.png");
+ EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.pbf?access_token=foo.png/bar", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.pbf?access_token=foo.png/bar");
+ EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.pbf?access_token=foo.png/bar.png", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.pbf?access_token=foo.png/bar.png");
+ } catch (const std::regex_error& e) {
+ const char *error = "unknown";
+ switch (e.code()) {
+ case std::regex_constants::error_collate:
+ error = "error_collate"; break;
+ case std::regex_constants::error_ctype:
+ error = "error_ctype"; break;
+ case std::regex_constants::error_escape:
+ error = "error_escape"; break;
+ case std::regex_constants::error_backref:
+ error = "error_backref"; break;
+ case std::regex_constants::error_paren:
+ error = "error_paren"; break;
+ case std::regex_constants::error_brace:
+ error = "error_brace"; break;
+ case std::regex_constants::error_badbrace:
+ error = "error_badbrace"; break;
+ case std::regex_constants::error_range:
+ error = "error_range"; break;
+ case std::regex_constants::error_space:
+ error = "error_space"; break;
+ case std::regex_constants::error_badrepeat:
+ error = "error_badrepeat"; break;
+ case std::regex_constants::error_complexity:
+ error = "error_complexity"; break;
+ case std::regex_constants::error_stack:
+ error = "error_stack"; break;
+ default:
+ break;
+ }
+ mbgl::Log::Error(mbgl::Event::General, "regex_error caught: %s - %s (%d)", e.what(), error, e.code());
+ throw e;
+ }
+}
diff --git a/test/miscellaneous/style_parser.cpp b/test/miscellaneous/style_parser.cpp
index 2cb3056204..f5b9bb7e63 100644
--- a/test/miscellaneous/style_parser.cpp
+++ b/test/miscellaneous/style_parser.cpp
@@ -5,7 +5,7 @@
#include <rapidjson/document.h>
-#include "../fixtures/fixture_log.hpp"
+#include "../fixtures/fixture_log_observer.hpp"
#include <iostream>
#include <fstream>
@@ -36,7 +36,8 @@ TEST_P(StyleParserTest, ParseStyle) {
std::stringstream stylejson;
stylejson << stylefile.rdbuf();
- const FixtureLogBackend &log = Log::Set<FixtureLogBackend>();
+ FixtureLogObserver* observer = new FixtureLogObserver();
+ Log::setObserver(std::unique_ptr<Log::Observer>(observer));
Style style;
style.loadJSON((const uint8_t *)stylejson.str().c_str());
@@ -54,17 +55,18 @@ TEST_P(StyleParserTest, ParseStyle) {
ASSERT_EQ(true, js_entry.IsArray());
const uint32_t count = js_entry[rapidjson::SizeType(0)].GetUint();
- const FixtureLogBackend::LogMessage message {
+ const FixtureLogObserver::LogMessage message {
EventSeverityClass(js_entry[rapidjson::SizeType(1)].GetString()),
EventClass(js_entry[rapidjson::SizeType(2)].GetString()),
+ int64_t(-1),
js_entry[rapidjson::SizeType(3)].GetString()
};
- EXPECT_EQ(count, log.count(message)) << "Message: " << message << std::endl;
+ EXPECT_EQ(count, observer->count(message)) << "Message: " << message << std::endl;
}
}
- const auto &unchecked = log.unchecked();
+ const auto &unchecked = observer->unchecked();
if (unchecked.size()) {
std::cerr << "Unchecked Log Messages (" << base << "/" << name << "): " << std::endl << unchecked;
}
diff --git a/test/storage/cache_response.cpp b/test/storage/cache_response.cpp
index a0b5ba31c1..ac0dc4c565 100644
--- a/test/storage/cache_response.cpp
+++ b/test/storage/cache_response.cpp
@@ -14,8 +14,9 @@ TEST_F(Storage, CacheResponse) {
DefaultFileSource fs(&cache, uv_default_loop());
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/cache" };
+ auto &env = *static_cast<const Environment *>(nullptr);
- fs.request(resource, uv_default_loop(), [&](const Response &res) {
+ fs.request(resource, uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Response 1", res.data);
EXPECT_LT(0, res.expires);
@@ -23,7 +24,7 @@ TEST_F(Storage, CacheResponse) {
EXPECT_EQ("", res.etag);
EXPECT_EQ("", res.message);
- fs.request(resource, uv_default_loop(), [&, res](const Response &res2) {
+ fs.request(resource, uv_default_loop(), env, [&, res](const Response &res2) {
EXPECT_EQ(res.status, res2.status);
EXPECT_EQ(res.data, res2.data);
EXPECT_EQ(res.expires, res2.expires);
diff --git a/test/storage/cache_revalidate.cpp b/test/storage/cache_revalidate.cpp
index 530b7325b5..bd32042b94 100644
--- a/test/storage/cache_revalidate.cpp
+++ b/test/storage/cache_revalidate.cpp
@@ -15,8 +15,10 @@ TEST_F(Storage, CacheRevalidate) {
SQLiteCache cache(":memory:");
DefaultFileSource fs(&cache);
+ auto &env = *static_cast<const Environment *>(nullptr);
+
const Resource revalidateSame { Resource::Unknown, "http://127.0.0.1:3000/revalidate-same" };
- fs.request(revalidateSame, uv_default_loop(), [&](const Response &res) {
+ fs.request(revalidateSame, uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Response", res.data);
EXPECT_EQ(0, res.expires);
@@ -24,7 +26,7 @@ TEST_F(Storage, CacheRevalidate) {
EXPECT_EQ("snowfall", res.etag);
EXPECT_EQ("", res.message);
- fs.request(revalidateSame, uv_default_loop(), [&, res](const Response &res2) {
+ fs.request(revalidateSame, uv_default_loop(), env, [&, res](const Response &res2) {
EXPECT_EQ(Response::Successful, res2.status);
EXPECT_EQ("Response", res2.data);
// We use this to indicate that a 304 reply came back.
@@ -38,8 +40,9 @@ TEST_F(Storage, CacheRevalidate) {
});
});
- const Resource revalidateModified { Resource::Unknown, "http://127.0.0.1:3000/revalidate-modified" };
- fs.request(revalidateModified, uv_default_loop(), [&](const Response &res) {
+ const Resource revalidateModified{ Resource::Unknown,
+ "http://127.0.0.1:3000/revalidate-modified" };
+ fs.request(revalidateModified, uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Response", res.data);
EXPECT_EQ(0, res.expires);
@@ -47,7 +50,7 @@ TEST_F(Storage, CacheRevalidate) {
EXPECT_EQ("", res.etag);
EXPECT_EQ("", res.message);
- fs.request(revalidateModified, uv_default_loop(), [&, res](const Response &res2) {
+ fs.request(revalidateModified, uv_default_loop(), env, [&, res](const Response &res2) {
EXPECT_EQ(Response::Successful, res2.status);
EXPECT_EQ("Response", res2.data);
// We use this to indicate that a 304 reply came back.
@@ -61,7 +64,7 @@ TEST_F(Storage, CacheRevalidate) {
});
const Resource revalidateEtag { Resource::Unknown, "http://127.0.0.1:3000/revalidate-etag" };
- fs.request(revalidateEtag, uv_default_loop(), [&](const Response &res) {
+ fs.request(revalidateEtag, uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Response 1", res.data);
EXPECT_EQ(0, res.expires);
@@ -69,7 +72,7 @@ TEST_F(Storage, CacheRevalidate) {
EXPECT_EQ("response-1", res.etag);
EXPECT_EQ("", res.message);
- fs.request(revalidateEtag, uv_default_loop(), [&, res](const Response &res2) {
+ fs.request(revalidateEtag, uv_default_loop(), env, [&, res](const Response &res2) {
EXPECT_EQ(Response::Successful, res2.status);
EXPECT_EQ("Response 2", res2.data);
EXPECT_EQ(0, res2.expires);
diff --git a/test/storage/directory_reading.cpp b/test/storage/directory_reading.cpp
index 3ee4dd1721..a955648462 100644
--- a/test/storage/directory_reading.cpp
+++ b/test/storage/directory_reading.cpp
@@ -15,7 +15,10 @@ TEST_F(Storage, AssetReadDirectory) {
DefaultFileSource fs(nullptr, uv_default_loop());
#endif
- fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage" }, uv_default_loop(), [&](const Response &res) {
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage" }, uv_default_loop(),
+ env, [&](const Response &res) {
EXPECT_EQ(Response::Error, res.status);
EXPECT_EQ(0ul, res.data.size());
EXPECT_EQ(0, res.expires);
@@ -24,7 +27,7 @@ TEST_F(Storage, AssetReadDirectory) {
#ifdef MBGL_ASSET_ZIP
EXPECT_EQ("No such file", res.message);
#elif MBGL_ASSET_FS
- EXPECT_EQ("illegal operation on a directory", res.message);
+ EXPECT_EQ("illegal operation on a directory", res.message);
#endif
ReadDirectory.finish();
});
diff --git a/test/storage/file_reading.cpp b/test/storage/file_reading.cpp
index 7e14fdcb15..cca072b27a 100644
--- a/test/storage/file_reading.cpp
+++ b/test/storage/file_reading.cpp
@@ -16,9 +16,10 @@ TEST_F(Storage, AssetEmptyFile) {
DefaultFileSource fs(nullptr, uv_default_loop());
#endif
- fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/empty" },
- uv_default_loop(),
- [&](const Response &res) {
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/empty" }, uv_default_loop(),
+ env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ(0ul, res.data.size());
EXPECT_EQ(0, res.expires);
@@ -42,9 +43,10 @@ TEST_F(Storage, AssetNonEmptyFile) {
DefaultFileSource fs(nullptr, uv_default_loop());
#endif
+ auto &env = *static_cast<const Environment *>(nullptr);
+
fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/nonempty" },
- uv_default_loop(),
- [&](const Response &res) {
+ uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ(16ul, res.data.size());
EXPECT_EQ(0, res.expires);
@@ -69,9 +71,10 @@ TEST_F(Storage, AssetNonExistentFile) {
DefaultFileSource fs(nullptr, uv_default_loop());
#endif
+ auto &env = *static_cast<const Environment *>(nullptr);
+
fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/does_not_exist" },
- uv_default_loop(),
- [&](const Response &res) {
+ uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Error, res.status);
EXPECT_EQ(0ul, res.data.size());
EXPECT_EQ(0, res.expires);
diff --git a/test/storage/http_cancel.cpp b/test/storage/http_cancel.cpp
index 7e485ec42d..d0dac8ccdb 100644
--- a/test/storage/http_cancel.cpp
+++ b/test/storage/http_cancel.cpp
@@ -14,9 +14,11 @@ TEST_F(Storage, HTTPCancel) {
DefaultFileSource fs(nullptr, uv_default_loop());
- auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), [&](const Response &) {
- ADD_FAILURE() << "Callback should not be called";
- });
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ auto req =
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env,
+ [&](const Response &) { ADD_FAILURE() << "Callback should not be called"; });
fs.cancel(req);
HTTPCancel.finish();
@@ -31,11 +33,13 @@ TEST_F(Storage, HTTPCancelMultiple) {
DefaultFileSource fs(nullptr, uv_default_loop());
+ auto &env = *static_cast<const Environment *>(nullptr);
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" };
- auto req2 = fs.request(resource, uv_default_loop(), [&](const Response &) {
+
+ auto req2 = fs.request(resource, uv_default_loop(), env, [&](const Response &) {
ADD_FAILURE() << "Callback should not be called";
});
- fs.request(resource, uv_default_loop(), [&](const Response &res) {
+ fs.request(resource, uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
EXPECT_EQ(0, res.expires);
diff --git a/test/storage/http_coalescing.cpp b/test/storage/http_coalescing.cpp
index 592c65f8d6..9eaea70e11 100644
--- a/test/storage/http_coalescing.cpp
+++ b/test/storage/http_coalescing.cpp
@@ -14,6 +14,8 @@ TEST_F(Storage, HTTPCoalescing) {
DefaultFileSource fs(nullptr, uv_default_loop());
+ auto &env = *static_cast<const Environment *>(nullptr);
+
static const Response *reference = nullptr;
const auto complete = [&](const Response &res) {
@@ -41,7 +43,7 @@ TEST_F(Storage, HTTPCoalescing) {
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" };
for (int i = 0; i < total; i++) {
- fs.request(resource, uv_default_loop(), complete);
+ fs.request(resource, uv_default_loop(), env, complete);
}
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
diff --git a/test/storage/http_environment.cpp b/test/storage/http_environment.cpp
new file mode 100644
index 0000000000..efd7b78ad4
--- /dev/null
+++ b/test/storage/http_environment.cpp
@@ -0,0 +1,54 @@
+#include "storage.hpp"
+
+#include <uv.h>
+
+#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/storage/network_status.hpp>
+
+#include <cmath>
+
+TEST_F(Storage, HTTPCancelEnvironment) {
+ SCOPED_TEST(HTTPRetainedEnvironment)
+ SCOPED_TEST(HTTPCanceledEnvironment)
+
+ using namespace mbgl;
+
+ DefaultFileSource fs(nullptr, uv_default_loop());
+
+ // Create two fake environment pointers. The FileSource implementation treats these as opaque
+ // pointers and doesn't reach into them.
+ auto &env1 = *reinterpret_cast<const Environment *>(1);
+ auto &env2 = *reinterpret_cast<const Environment *>(2);
+
+ const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/delayed" };
+
+ // Environment 1
+ fs.request(resource, uv_default_loop(), env1, [&](const Response &res) {
+ // This environment gets aborted below. This means the request is marked as failing and
+ // will return an error here.
+ EXPECT_EQ(Response::Error, res.status);
+ EXPECT_EQ("", res.data);
+ EXPECT_EQ(0, res.expires);
+ EXPECT_EQ(0, res.modified);
+ EXPECT_EQ("", res.etag);
+ EXPECT_EQ("Environment is terminating", res.message);
+ HTTPCanceledEnvironment.finish();
+ });
+
+ // Environment 2
+ fs.request(resource, uv_default_loop(), env2, [&](const Response &res) {
+ // The same request as above, but in a different environment which doesn't get aborted. This
+ // means the request should succeed.
+ EXPECT_EQ(Response::Successful, res.status);
+ EXPECT_EQ("Response", res.data);
+ EXPECT_EQ(0, res.expires);
+ EXPECT_EQ(0, res.modified);
+ EXPECT_EQ("", res.etag);
+ EXPECT_EQ("", res.message);
+ HTTPRetainedEnvironment.finish();
+ });
+
+ fs.abort(env1);
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
diff --git a/test/storage/http_error.cpp b/test/storage/http_error.cpp
index 498f1eae6b..abaeff5396 100644
--- a/test/storage/http_error.cpp
+++ b/test/storage/http_error.cpp
@@ -22,9 +22,12 @@ TEST_F(Storage, HTTPError) {
DefaultFileSource fs(nullptr, uv_default_loop());
+ auto &env = *static_cast<const Environment *>(nullptr);
+
auto start = uv_hrtime();
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, uv_default_loop(), [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, uv_default_loop(),
+ env, [&](const Response &res) {
const auto duration = double(uv_hrtime() - start) / 1e9;
EXPECT_LT(1, duration) << "Backoff timer didn't wait 1 second";
EXPECT_GT(1.2, duration) << "Backoff timer fired too late";
@@ -38,13 +41,21 @@ TEST_F(Storage, HTTPError) {
HTTPTemporaryError.finish();
});
- fs.request({ Resource::Unknown, "http://127.0.0.1:3001/" }, uv_default_loop(), [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3001/" }, uv_default_loop(), env,
+ [&](const Response &res) {
const auto duration = double(uv_hrtime() - start) / 1e9;
// 1.5 seconds == 4 retries, with a 500ms timeout (see above).
EXPECT_LT(1.5, duration) << "Resource wasn't retried the correct number of times";
EXPECT_GT(1.7, duration) << "Resource wasn't retried the correct number of times";
EXPECT_EQ(Response::Error, res.status);
- EXPECT_TRUE(res.message == "Couldn't connect to server: couldn't connect to host" || res.message == "The operation couldn’t be completed. (NSURLErrorDomain error -1004.)") << res.message;
+#ifdef MBGL_HTTP_NSURL
+ EXPECT_STREQ(res.message.c_str(), "The operation couldn’t be completed. (NSURLErrorDomain error -1004.)");
+#elif MBGL_HTTP_CURL
+ const std::string prefix { "Couldn't connect to server: " };
+ EXPECT_STREQ(prefix.c_str(), res.message.substr(0, prefix.size()).c_str()) << "Full message is: \"" << res.message << "\"";
+#else
+ FAIL();
+#endif
EXPECT_EQ("", res.data);
EXPECT_EQ(0, res.expires);
EXPECT_EQ(0, res.modified);
@@ -52,7 +63,6 @@ TEST_F(Storage, HTTPError) {
HTTPConnectionError.finish();
});
-
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
uv_timer_stop(&statusChange);
diff --git a/test/storage/http_header_parsing.cpp b/test/storage/http_header_parsing.cpp
index 655348c877..271460387c 100644
--- a/test/storage/http_header_parsing.cpp
+++ b/test/storage/http_header_parsing.cpp
@@ -14,7 +14,11 @@ TEST_F(Storage, HTTPHeaderParsing) {
DefaultFileSource fs(nullptr, uv_default_loop());
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?modified=1420794326&expires=1420797926&etag=foo" }, uv_default_loop(), [&](const Response &res) {
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown,
+ "http://127.0.0.1:3000/test?modified=1420794326&expires=1420797926&etag=foo" },
+ uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
EXPECT_EQ(1420797926, res.expires);
@@ -24,11 +28,11 @@ TEST_F(Storage, HTTPHeaderParsing) {
HTTPExpiresTest.finish();
});
-
int64_t now = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?cachecontrol=max-age=120" }, uv_default_loop(), [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?cachecontrol=max-age=120" },
+ uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
EXPECT_GT(2, std::abs(res.expires - now - 120)) << "Expiration date isn't about 120 seconds in the future";
diff --git a/test/storage/http_load.cpp b/test/storage/http_load.cpp
index fff662da29..2680daf93b 100644
--- a/test/storage/http_load.cpp
+++ b/test/storage/http_load.cpp
@@ -11,13 +11,17 @@ TEST_F(Storage, HTTPLoad) {
DefaultFileSource fs(nullptr, uv_default_loop());
+ auto &env = *static_cast<const Environment *>(nullptr);
+
const int concurrency = 50;
const int max = 10000;
int number = 1;
std::function<void()> req = [&]() {
const auto current = number++;
- fs.request({ Resource::Unknown, std::string("http://127.0.0.1:3000/load/") + std::to_string(current) }, uv_default_loop(), [&, current](const Response &res) {
+ fs.request({ Resource::Unknown,
+ std::string("http://127.0.0.1:3000/load/") + std::to_string(current) },
+ uv_default_loop(), env, [&, current](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ(std::string("Request ") + std::to_string(current), res.data);
EXPECT_EQ(0, res.expires);
diff --git a/test/storage/http_noloop.cpp b/test/storage/http_noloop.cpp
index c71fd0bc26..cd4ebeb2c4 100644
--- a/test/storage/http_noloop.cpp
+++ b/test/storage/http_noloop.cpp
@@ -12,6 +12,8 @@ TEST_F(Storage, HTTPNoLoop) {
DefaultFileSource fs(nullptr);
+ auto &env = *static_cast<const Environment *>(nullptr);
+
const auto mainThread = uv_thread_self();
// Async handle that keeps the main loop alive until the thread finished
@@ -20,7 +22,8 @@ TEST_F(Storage, HTTPNoLoop) {
uv::close(as);
});
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, nullptr, env,
+ [&](const Response &res) {
EXPECT_NE(uv_thread_self(), mainThread) << "Response was called in the same thread";
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
diff --git a/test/storage/http_other_loop.cpp b/test/storage/http_other_loop.cpp
index 06cc6f7476..9ad673cf79 100644
--- a/test/storage/http_other_loop.cpp
+++ b/test/storage/http_other_loop.cpp
@@ -12,7 +12,10 @@ TEST_F(Storage, HTTPOtherLoop) {
// This file source launches a separate thread to do the processing.
DefaultFileSource fs(nullptr);
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), [&](const Response &res) {
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env,
+ [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
EXPECT_EQ(0, res.expires);
diff --git a/test/storage/http_reading.cpp b/test/storage/http_reading.cpp
index ad87db15ff..a6a5775825 100644
--- a/test/storage/http_reading.cpp
+++ b/test/storage/http_reading.cpp
@@ -12,9 +12,12 @@ TEST_F(Storage, HTTPReading) {
DefaultFileSource fs(nullptr, uv_default_loop());
+ auto &env = *static_cast<const Environment *>(nullptr);
+
const auto mainThread = uv_thread_self();
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env,
+ [&](const Response &res) {
EXPECT_EQ(uv_thread_self(), mainThread);
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
@@ -25,7 +28,8 @@ TEST_F(Storage, HTTPReading) {
HTTPTest.finish();
});
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/doesnotexist" }, uv_default_loop(), [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/doesnotexist" }, uv_default_loop(),
+ env, [&](const Response &res) {
EXPECT_EQ(uv_thread_self(), mainThread);
EXPECT_EQ(Response::Error, res.status);
EXPECT_EQ("HTTP status code 404", res.message);
@@ -45,7 +49,10 @@ TEST_F(Storage, HTTPNoCallback) {
DefaultFileSource fs(nullptr, uv_default_loop());
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), nullptr);
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env,
+ nullptr);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
@@ -59,7 +66,9 @@ TEST_F(Storage, HTTPNoCallbackNoLoop) {
DefaultFileSource fs(nullptr, uv_default_loop());
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, nullptr);
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, env, nullptr);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
diff --git a/test/storage/server.js b/test/storage/server.js
index 33d5a82985..1ee5363196 100755
--- a/test/storage/server.js
+++ b/test/storage/server.js
@@ -85,6 +85,13 @@ app.get('/temporary-error', function(req, res) {
temporaryErrorCounter++;
});
+app.get('/delayed', function(req, res) {
+ setTimeout(function() {
+ res.status(200).send('Response');
+ }, 200);
+});
+
+
app.get('/load/:number(\\d+)', function(req, res) {
res.send('Request ' + req.params.number);
});
diff --git a/test/test.gyp b/test/test.gyp
index 1d80a4f830..c333a40064 100644
--- a/test/test.gyp
+++ b/test/test.gyp
@@ -32,15 +32,17 @@
'fixtures/main.cpp',
'fixtures/util.hpp',
'fixtures/util.cpp',
- 'fixtures/fixture_log.hpp',
- 'fixtures/fixture_log.cpp',
+ 'fixtures/fixture_log_observer.hpp',
+ 'fixtures/fixture_log_observer.cpp',
'headless/headless.cpp',
'miscellaneous/clip_ids.cpp',
+ 'miscellaneous/bilinear.cpp',
'miscellaneous/comparisons.cpp',
'miscellaneous/enums.cpp',
'miscellaneous/functions.cpp',
+ 'miscellaneous/mapbox.cpp',
'miscellaneous/merge_lines.cpp',
'miscellaneous/rotation_range.cpp',
'miscellaneous/style_parser.cpp',
@@ -56,6 +58,7 @@
'storage/file_reading.cpp',
'storage/http_cancel.cpp',
'storage/http_coalescing.cpp',
+ 'storage/http_environment.cpp',
'storage/http_error.cpp',
'storage/http_header_parsing.cpp',
'storage/http_load.cpp',
@@ -65,22 +68,15 @@
],
'libraries': [
'<@(uv_static_libs)',
- '<@(glfw3_static_libs)',
- '<@(sqlite3_static_libs)',
- '<@(zlib_static_libs)',
- '<@(curl_static_libs)',
],
'variables': {
'cflags_cc': [
'<@(uv_cflags)',
+ '<@(opengl_cflags)',
'<@(boost_cflags)',
],
'ldflags': [
'<@(uv_ldflags)',
- '<@(glfw3_ldflags)',
- '<@(sqlite3_ldflags)',
- '<@(zlib_ldflags)',
- '<@(curl_ldflags)',
],
},
'conditions': [