summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzmiao <miao.zhao@mapbox.com>2019-11-19 20:45:30 +0200
committerzmiao <miao.zhao@mapbox.com>2019-11-20 21:56:33 +0200
commit6e973e890a5b3e3fdde82e0fc62c0987a5917573 (patch)
tree4ee57d8d330ec37f995fc67b52ef8961b9ca7b03
parent7386f6cbe78f0ace5f74d3fd53b743f2e9e7efc9 (diff)
downloadqtlocation-mapboxgl-upstream/zmiao-transform-state.tar.gz
[render-test] Wrap test data inside RenderTestRunner App + Add a new test app (#15887)upstream/zmiao-transform-state
* [render-test] Wrap test resources inside app * [render-test] Add test app * fix test app failure * [render-test]add callback + add javaObjectWrapper
-rw-r--r--.gitignore10
-rw-r--r--next/platform/android/android.cmake41
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/NativeMapViewTest.kt8
-rw-r--r--platform/android/src/test/render_test_runner.cpp269
-rw-r--r--platform/default/src/mbgl/render-test/main.cpp2
-rw-r--r--platform/ios/test/MGLMapViewPitchTests.m2
-rw-r--r--render-test/android-manifest.json7
-rw-r--r--render-test/android/README.md8
-rw-r--r--render-test/android/app/build.gradle4
-rw-r--r--render-test/android/app/src/androidTest/java/android/app/NativeActivityTest.java29
-rw-r--r--render-test/android/app/src/androidTest/java/android/app/TestState.java5
-rw-r--r--render-test/android/app/src/main/AndroidManifest.xml2
-rw-r--r--render-test/android/app/src/main/assets/to_zip.txt15
-rwxr-xr-xrender-test/android/render_test_setup.sh42
-rw-r--r--render-test/include/mbgl/render_test.hpp5
-rw-r--r--render-test/manifest_parser.cpp25
-rw-r--r--render-test/manifest_parser.hpp3
-rw-r--r--render-test/render_test.cpp8
-rw-r--r--render-test/runner.cpp2
-rw-r--r--src/mbgl/map/transform.cpp76
-rw-r--r--src/mbgl/map/transform_state.cpp54
-rw-r--r--src/mbgl/map/transform_state.hpp8
22 files changed, 487 insertions, 138 deletions
diff --git a/.gitignore b/.gitignore
index 538870edf7..d81b8c9727 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,6 +38,16 @@ test/fixtures/storage/assets.zip
buck-out
.buckd
+# Android RenderTestRunner
+render-test/android/.*
+render-test/android/app/build/
+render-test/android/app/src/main/assets/data.zip
+render-test/android/local.properties
+render-test/android/app/.*
+render-test/android/package.json
+render-test/android/app/app.iml
+render-test/android/android.iml
+
# Generated file from npm/yarn
package-lock.json
diff --git a/next/platform/android/android.cmake b/next/platform/android/android.cmake
index 06183291db..e086f8b75b 100644
--- a/next/platform/android/android.cmake
+++ b/next/platform/android/android.cmake
@@ -338,6 +338,47 @@ target_link_libraries(
mbgl-render-test
)
+add_custom_command(
+ TARGET mbgl-render-test-runner PRE_BUILD
+ COMMAND
+ ${CMAKE_COMMAND}
+ -E
+ copy
+ ${MBGL_ROOT}/render-test/android-manifest.json
+ ${MBGL_ROOT}/android-manifest.json
+ COMMAND
+ ${CMAKE_COMMAND}
+ -E
+ copy
+ ${MBGL_ROOT}/platform/node/test/ignores.json
+ ${MBGL_ROOT}/ignores/ignores.json
+ COMMAND
+ ${CMAKE_COMMAND}
+ -E
+ copy
+ ${MBGL_ROOT}/render-test/linux-ignores.json
+ ${MBGL_ROOT}/ignores/linux-ignores.json
+ COMMAND
+ ${CMAKE_COMMAND}
+ -E
+ tar
+ "cf"
+ "render-test/android/app/src/main/assets/data.zip"
+ --format=zip
+ --files-from=render-test/android/app/src/main/assets/to_zip.txt
+ COMMAND
+ ${CMAKE_COMMAND}
+ -E
+ remove_directory
+ ${MBGL_ROOT}/ignores
+ COMMAND
+ ${CMAKE_COMMAND}
+ -E
+ remove
+ ${MBGL_ROOT}/android-manifest.json
+ WORKING_DIRECTORY ${MBGL_ROOT}
+)
+
# Android has no concept of MinSizeRel on android.toolchain.cmake and provides configurations tuned for binary size. We can push it a bit
# more with code folding and LTO.
set_target_properties(example-custom-layer PROPERTIES LINK_FLAGS_RELEASE "-fuse-ld=gold -O2 -flto -Wl,--icf=safe")
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/NativeMapViewTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/NativeMapViewTest.kt
index dc313b5f64..ebb8ef4e4c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/NativeMapViewTest.kt
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/NativeMapViewTest.kt
@@ -75,7 +75,7 @@ class NativeMapViewTest : AppCenter() {
val expected = BEARING_TEST
nativeMapView.setBearing(expected, 0)
val actual = nativeMapView.bearing
- assertEquals("Bearing should match", expected, actual, DELTA)
+ assertEquals("Bearing should match", expected, actual, 0.00001)
}
@Test
@@ -123,7 +123,7 @@ class NativeMapViewTest : AppCenter() {
val expected = PITCH_TEST
nativeMapView.setPitch(expected, 0)
val actual = nativeMapView.pitch
- assertEquals("Pitch should match", expected, actual, DELTA)
+ assertEquals("Pitch should match", expected, actual, 0.00001)
}
@Test
@@ -167,8 +167,8 @@ class NativeMapViewTest : AppCenter() {
val actual = nativeMapView.cameraPosition
assertEquals("Latitude should match", expected.target.latitude, actual.target.latitude, DELTA)
assertEquals("Longitude should match", expected.target.longitude, actual.target.longitude, DELTA)
- assertEquals("Bearing should match", expected.bearing, actual.bearing, DELTA)
- assertEquals("Pitch should match", expected.tilt, actual.tilt, DELTA)
+ assertEquals("Bearing should match", expected.bearing, actual.bearing, 0.00001)
+ assertEquals("Pitch should match", expected.tilt, actual.tilt, 0.00001)
assertEquals("Zoom should match", expected.zoom, actual.zoom, DELTA)
Assert.assertArrayEquals(expected.padding, actual.padding, DELTA)
}
diff --git a/platform/android/src/test/render_test_runner.cpp b/platform/android/src/test/render_test_runner.cpp
index d4554aa9de..98197ce1cc 100644
--- a/platform/android/src/test/render_test_runner.cpp
+++ b/platform/android/src/test/render_test_runner.cpp
@@ -1,15 +1,17 @@
#include <android_native_app_glue.h>
#include <mbgl/render_test.hpp>
+#include <mbgl/util/logging.hpp>
+
+#include <android/asset_manager.h>
+#include <android/log.h>
+
#include "jni.hpp"
-#include "logger.hpp"
+#include <cstdio>
+#include <functional>
#include <string>
#include <vector>
-#include <mbgl/util/logging.hpp>
-
-#include <android/log.h>
-
namespace mbgl {
namespace {
@@ -18,16 +20,12 @@ int severityToPriority(EventSeverity severity) {
switch (severity) {
case EventSeverity::Debug:
return ANDROID_LOG_DEBUG;
-
case EventSeverity::Info:
return ANDROID_LOG_INFO;
-
case EventSeverity::Warning:
return ANDROID_LOG_WARN;
-
case EventSeverity::Error:
return ANDROID_LOG_ERROR;
-
default:
return ANDROID_LOG_VERBOSE;
}
@@ -41,17 +39,252 @@ void Log::platformRecord(EventSeverity severity, const std::string& msg) {
} // namespace mbgl
+namespace {
+
+template <class T>
+class JavaWrapper {
+public:
+ JavaWrapper(JNIEnv* env_, T obj_) : env(env_), obj(obj_) {}
+ ~JavaWrapper() {
+ env->DeleteLocalRef(obj);
+ env = nullptr;
+ obj = NULL;
+ }
+ T& get() { return obj; }
+
+private:
+ JavaWrapper() = delete;
+ JNIEnv* env;
+ T obj;
+};
+
+std::string jstringToStdString(JNIEnv* env, jstring jStr) {
+ if (!jStr) {
+ return std::string();
+ }
+
+ JavaWrapper<jclass> stringClass(env, env->GetObjectClass(jStr));
+ const jmethodID getBytes = env->GetMethodID(stringClass.get(), "getBytes", "(Ljava/lang/String;)[B");
+ JavaWrapper<jbyteArray> stringJbytes(
+ env,
+ static_cast<jbyteArray>(
+ env->CallObjectMethod(jStr, getBytes, JavaWrapper<jstring>(env, env->NewStringUTF("UTF-8")).get())));
+
+ size_t length = static_cast<size_t>(env->GetArrayLength(stringJbytes.get()));
+ jbyte* pBytes = env->GetByteArrayElements(stringJbytes.get(), NULL);
+
+ std::string ret = std::string(reinterpret_cast<char*>(pBytes), length);
+ env->ReleaseByteArrayElements(stringJbytes.get(), pBytes, JNI_ABORT);
+
+ return ret;
+}
+
+void changeState(JNIEnv* env, struct android_app* app) {
+ jobject nativeActivity = app->activity->clazz;
+ jclass acl = env->GetObjectClass(nativeActivity);
+ jmethodID getClassLoader = env->GetMethodID(acl, "getClassLoader", "()Ljava/lang/ClassLoader;");
+ jobject cls = env->CallObjectMethod(nativeActivity, getClassLoader);
+ JavaWrapper<jclass> classLoader(env, env->FindClass("java/lang/ClassLoader"));
+ jmethodID findClass = env->GetMethodID(classLoader.get(), "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
+ JavaWrapper<jstring> strClassName(env, env->NewStringUTF("android.app.TestState"));
+ jclass testStateClass = static_cast<jclass>(env->CallObjectMethod(cls, findClass, strClassName.get()));
+ if (testStateClass != NULL) {
+ jfieldID id = env->GetStaticFieldID(testStateClass, "running", "Z");
+ env->SetStaticBooleanField(testStateClass, id, false);
+ }
+}
+
+bool copyFile(JNIEnv* env,
+ AAssetManager* assetManager,
+ const std::string& filePath,
+ const std::string& destinationPath,
+ const std::string& fileName) {
+ JavaWrapper<jclass> fileClass(env, env->FindClass("java/io/File"));
+ jmethodID fileCtor = env->GetMethodID(fileClass.get(), "<init>", "(Ljava/lang/String;Ljava/lang/String;)V");
+ jmethodID fileExists = env->GetMethodID(fileClass.get(), "exists", "()Z");
+
+ JavaWrapper<jstring> destination(env, env->NewStringUTF(destinationPath.c_str()));
+ JavaWrapper<jstring> file(env, env->NewStringUTF(fileName.c_str()));
+ JavaWrapper<jobject> fileToCopy(env, env->NewObject(fileClass.get(), fileCtor, destination.get(), file.get()));
+
+ bool stateOk = true;
+ if (env->CallBooleanMethod(fileToCopy.get(), fileExists)) {
+ mbgl::Log::Warning(mbgl::Event::General, "File '%s' already exists", filePath.c_str());
+ } else {
+ std::unique_ptr<AAsset, std::function<void(AAsset*)>> fileAsset(
+ AAssetManager_open(assetManager, fileName.c_str(), AASSET_MODE_BUFFER),
+ [](AAsset* asset) { AAsset_close(asset); });
+ if (fileAsset == nullptr) {
+ mbgl::Log::Warning(mbgl::Event::General, "Failed to open asset file %s", fileName.c_str());
+ return false;
+ }
+ const void* fileData = AAsset_getBuffer(fileAsset.get());
+ const off_t fileLen = AAsset_getLength(fileAsset.get());
+
+ std::unique_ptr<FILE, std::function<void(FILE*)>> newFile(std::fopen(filePath.c_str(), "w+"),
+ [](FILE* file) { std::fclose(file); });
+ stateOk = newFile != nullptr;
+
+ if (!stateOk) {
+ mbgl::Log::Warning(mbgl::Event::General, "Failed to create new file entry %s", fileName.c_str());
+ } else {
+ auto res = static_cast<off_t>(std::fwrite(fileData, sizeof(char), fileLen, newFile.get()));
+ if (fileLen != res) {
+ mbgl::Log::Warning(
+ mbgl::Event::General, "Failed to generate file entry %s from assets", fileName.c_str());
+ }
+ }
+ }
+ return stateOk;
+}
+
+void unZipFile(JNIEnv* env, const std::string& zipFilePath, const std::string& destinationPath) {
+ JavaWrapper<jclass> fileClassWrapper(env, env->FindClass("java/io/File"));
+ auto fileClass = fileClassWrapper.get();
+ jmethodID fileCtor = env->GetMethodID(fileClass, "<init>", "(Ljava/lang/String;Ljava/lang/String;)V");
+ jmethodID fileExists = env->GetMethodID(fileClass, "exists", "()Z");
+ jmethodID fileIsDirectory = env->GetMethodID(fileClass, "isDirectory", "()Z");
+ jmethodID deleteFile = env->GetMethodID(fileClass, "delete", "()Z");
+ jmethodID createNewFile = env->GetMethodID(fileClass, "createNewFile", "()Z");
+ jmethodID fileGetName = env->GetMethodID(fileClass, "getName", "()Ljava/lang/String;");
+
+ JavaWrapper<jclass> fileInputStreamWrapper(env, env->FindClass("java/io/FileInputStream"));
+ auto fileInputStream = fileInputStreamWrapper.get();
+ jmethodID finCtor = env->GetMethodID(fileInputStream, "<init>", "(Ljava/lang/String;)V");
+
+ JavaWrapper<jclass> fileOutputStreamWrapper(env, env->FindClass("java/io/FileOutputStream"));
+ auto fileOutputStream = fileOutputStreamWrapper.get();
+ jmethodID foutCtor = env->GetMethodID(fileOutputStream, "<init>", "(Ljava/io/File;)V");
+ jmethodID foutClose = env->GetMethodID(fileOutputStream, "close", "()V");
+ jmethodID foutWrite = env->GetMethodID(fileOutputStream, "write", "([BII)V");
+
+ JavaWrapper<jclass> zipInputStreamWrapper(env, env->FindClass("java/util/zip/ZipInputStream"));
+ auto zipInputStream = zipInputStreamWrapper.get();
+ jmethodID zinCtor = env->GetMethodID(zipInputStream, "<init>", "(Ljava/io/InputStream;)V");
+ jmethodID zinGetNextEntry = env->GetMethodID(zipInputStream, "getNextEntry", "()Ljava/util/zip/ZipEntry;");
+ jmethodID zinRead = env->GetMethodID(zipInputStream, "read", "([B)I");
+ jmethodID zinCloseEntry = env->GetMethodID(zipInputStream, "closeEntry", "()V");
+
+ JavaWrapper<jclass> zipEntryWrapper(env, env->FindClass("java/util/zip/ZipEntry"));
+ auto zipEntry = zipEntryWrapper.get();
+ jmethodID zipGetName = env->GetMethodID(zipEntry, "getName", "()Ljava/lang/String;");
+ jmethodID zipIsDirectory = env->GetMethodID(zipEntry, "isDirectory", "()Z");
+
+ // Unzip the resource folder to destination path
+ JavaWrapper<jstring> destination(env, env->NewStringUTF(destinationPath.c_str()));
+ JavaWrapper<jstring> zipFile(env, env->NewStringUTF(zipFilePath.c_str()));
+ JavaWrapper<jobject> fileIn(env, env->NewObject(fileInputStream, finCtor, zipFile.get()));
+ JavaWrapper<jobject> zipIn(env, env->NewObject(zipInputStream, zinCtor, fileIn.get()));
+
+ while (true) {
+ JavaWrapper<jobject> obj(env, env->CallObjectMethod(zipIn.get(), zinGetNextEntry));
+ jobject zEntry = obj.get();
+ if (zEntry == NULL) {
+ break;
+ }
+ jstring dir = static_cast<jstring>(env->CallObjectMethod(zEntry, zipGetName));
+ std::string name = jstringToStdString(env, dir);
+ bool isDir = env->CallBooleanMethod(zEntry, zipIsDirectory);
+
+ JavaWrapper<jobject> fileHandle(env, env->NewObject(fileClass, fileCtor, destination.get(), dir));
+ jobject& f = fileHandle.get();
+ std::string fileName = jstringToStdString(env, static_cast<jstring>(env->CallObjectMethod(f, fileGetName)));
+
+ if (isDir) {
+ if (!(env->CallBooleanMethod(f, fileIsDirectory))) {
+ jmethodID mkdirs = env->GetMethodID(fileClass, "mkdirs", "()Z");
+ bool success = (env->CallBooleanMethod(f, mkdirs));
+ std::string fileName =
+ jstringToStdString(env, static_cast<jstring>(env->CallObjectMethod(f, fileGetName)));
+
+ if (!success) {
+ mbgl::Log::Warning(
+ mbgl::Event::General, "Failed to create folder entry %s from zip", fileName.c_str());
+ }
+ }
+ } else if (!(env->CallBooleanMethod(f, fileExists))) {
+ bool success = env->CallBooleanMethod(f, createNewFile);
+ std::string fileName = jstringToStdString(env, static_cast<jstring>(env->CallObjectMethod(f, fileGetName)));
+
+ if (!success) {
+ mbgl::Log::Warning(mbgl::Event::General, "Failed to create folder entry %s from zip", fileName.c_str());
+ continue;
+ }
+
+ JavaWrapper<jobject> fout(env, env->NewObject(fileOutputStream, foutCtor, f));
+ JavaWrapper<jbyteArray> jBuff(env, env->NewByteArray(2048));
+
+ int count;
+ while ((count = env->CallIntMethod(zipIn.get(), zinRead, jBuff.get())) != -1) {
+ env->CallVoidMethod(fout.get(), foutWrite, jBuff.get(), 0, count);
+ }
+
+ env->CallVoidMethod(zipIn.get(), zinCloseEntry);
+ env->CallVoidMethod(fout.get(), foutClose);
+ }
+ }
+
+ JavaWrapper<jobject> fileToDelete(env, env->NewObject(fileClass, fileCtor, destination.get(), zipFile.get()));
+ if (env->CallBooleanMethod(fileToDelete.get(), fileExists)) {
+ jboolean success = (env->CallBooleanMethod(fileToDelete.get(), deleteFile));
+ if (!success) {
+ mbgl::Log::Warning(mbgl::Event::General, "Failed to delete file entry %s", zipFilePath.c_str());
+ }
+ }
+}
+
+} // namespace
+
void android_main(struct android_app* app) {
mbgl::android::theJVM = app->activity->vm;
- JNIEnv* env;
+ JNIEnv* env = nullptr;
app->activity->vm->AttachCurrentThread(&env, NULL);
- std::vector<std::string> arguments = {"mbgl-render-test-runner", "-p", "/sdcard/render-test/android-manifest.json"};
- std::vector<char*> argv;
- for (const auto& arg : arguments) {
- argv.push_back((char*)arg.data());
+ const char* storage_chars = app->activity->internalDataPath;
+ std::string storagePath(storage_chars);
+ std::string zipFile = storagePath + "/data.zip";
+
+ int outFd, outEvents;
+ struct android_poll_source* source = nullptr;
+ if (!copyFile(env, app->activity->assetManager, zipFile, storagePath, "data.zip")) {
+ mbgl::Log::Error(
+ mbgl::Event::General, "Failed to copy zip File '%s' to external storage for upzipping", zipFile.c_str());
+ } else {
+ unZipFile(env, zipFile, storagePath);
+
+ std::string configFile = storagePath + "/android-manifest.json";
+ std::vector<std::string> arguments = {"mbgl-render-test-runner", "-p", configFile};
+ std::vector<char*> argv;
+ for (const auto& arg : arguments) {
+ argv.push_back((char*)arg.data());
+ }
+ argv.push_back(nullptr);
+
+ int finishedTestCount = 0;
+ std::function<void()> testStatus = [&]() {
+ ALooper_pollAll(0, &outFd, &outEvents, reinterpret_cast<void**>(&source));
+
+ if (source != nullptr) {
+ source->process(app, source);
+ }
+
+ mbgl::Log::Info(mbgl::Event::General, "Current finished tests number is '%d' ", ++finishedTestCount);
+ };
+
+ mbgl::runRenderTests(argv.size() - 1, argv.data(), testStatus);
+ mbgl::Log::Info(mbgl::Event::General, "All tests are finished!");
+ changeState(env, app);
+ }
+ while (true) {
+ ALooper_pollAll(0, &outFd, &outEvents, reinterpret_cast<void**>(&source));
+
+ if (source != nullptr) {
+ source->process(app, source);
+ }
+ if (app->destroyRequested != 0) {
+ app->activity->vm->DetachCurrentThread();
+ mbgl::Log::Info(mbgl::Event::General, "Close the App!");
+ return;
+ }
}
- argv.push_back(nullptr);
- (void)mbgl::runRenderTests(argv.size() - 1, argv.data());
- app->activity->vm->DetachCurrentThread();
-} \ No newline at end of file
+}
diff --git a/platform/default/src/mbgl/render-test/main.cpp b/platform/default/src/mbgl/render-test/main.cpp
index 9b22b20e00..aed0e01d39 100644
--- a/platform/default/src/mbgl/render-test/main.cpp
+++ b/platform/default/src/mbgl/render-test/main.cpp
@@ -1,5 +1,5 @@
#include <mbgl/render_test.hpp>
int main(int argc, char *argv[]) {
- return mbgl::runRenderTests(argc, argv);
+ return mbgl::runRenderTests(argc, argv, []() {});
}
diff --git a/platform/ios/test/MGLMapViewPitchTests.m b/platform/ios/test/MGLMapViewPitchTests.m
index fa657eb994..0317c1f8ff 100644
--- a/platform/ios/test/MGLMapViewPitchTests.m
+++ b/platform/ios/test/MGLMapViewPitchTests.m
@@ -153,7 +153,7 @@
// Set the map camera to a pitched state, perhaps from a previous gesture or camera movement.
self.mapView.camera = [self.mapView cameraByTiltingToPitch:initialTilt];
- XCTAssertEqual(self.mapView.camera.pitch, initialTilt, @"Tilt should initially be set to %.f°.", initialTilt);
+ XCTAssertEqualWithAccuracy(self.mapView.camera.pitch, initialTilt, 10e-6, @"Tilt should initially be set to %.f°.", initialTilt);
// Initialize a tilt gesture.
MockUIPanGestureRecognizer *gesture = [[MockUIPanGestureRecognizer alloc] initWithTarget:self.mapView action:nil];
diff --git a/render-test/android-manifest.json b/render-test/android-manifest.json
index 56223d4753..7aa9eb26bc 100644
--- a/render-test/android-manifest.json
+++ b/render-test/android-manifest.json
@@ -1,7 +1,8 @@
{
"base_test_path":"mapbox-gl-js/test/integration",
- "expectation_paths":["render-test/expected/render-tests"],
- "ignore_paths":["platform/node/test/ignores.json", "render-test/linux-ignores.json", "render-test/tests/should-fail.json"],
+ "expectation_paths":[],
+ "ignore_paths":["ignores/ignores.json", "ignores/linux-ignores.json"],
"vendor_path":"vendor",
- "asset_path": "mapbox-gl-js/test/integration"
+ "asset_path": "mapbox-gl-js/test/integration",
+ "result_path": "/sdcard/"
} \ No newline at end of file
diff --git a/render-test/android/README.md b/render-test/android/README.md
index 270c970fce..4037419012 100644
--- a/render-test/android/README.md
+++ b/render-test/android/README.md
@@ -1,9 +1,3 @@
# RenderTestRunner
-This app is a purely native application, with no Java source code, that can run **mbgl-render-test-runner** on android devices.
-
-
-## Setup the test environment
-- Run render_test_setup.sh so that all the necessary test resources are pushed to the device.
-
-- Switch on storage permission of the app so that it can read/write data on SD card. \ No newline at end of file
+This app is a purely native application, with no Java source code, that can run **mbgl-render-test-runner** on android devices. \ No newline at end of file
diff --git a/render-test/android/app/build.gradle b/render-test/android/app/build.gradle
index 60609e3ba2..682af85dcf 100644
--- a/render-test/android/app/build.gradle
+++ b/render-test/android/app/build.gradle
@@ -14,6 +14,7 @@ android {
targets 'mbgl-render-test-runner'
}
}
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
externalNativeBuild {
cmake {
@@ -26,4 +27,7 @@ android {
dependencies {
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-beta01'
+ androidTestImplementation 'androidx.test:rules:1.2.0-beta01'
}
diff --git a/render-test/android/app/src/androidTest/java/android/app/NativeActivityTest.java b/render-test/android/app/src/androidTest/java/android/app/NativeActivityTest.java
new file mode 100644
index 0000000000..3d8c333902
--- /dev/null
+++ b/render-test/android/app/src/androidTest/java/android/app/NativeActivityTest.java
@@ -0,0 +1,29 @@
+package android.app;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import android.util.Log;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class NativeActivityTest {
+
+ @Rule
+ public ActivityTestRule<NativeActivity> mActivityTestRule = new ActivityTestRule<>(NativeActivity.class, false, false);
+
+ @Test(timeout = 1200000L)
+ public void runRenderTests() throws Exception {
+ Log.v("Test", "Start the test");
+ mActivityTestRule.launchActivity(null);
+ while (TestState.running) {
+ Log.v("Test", "Test is running");
+ Thread.sleep(1000L);
+ }
+ Log.v("Test", "End the test");
+ }
+} \ No newline at end of file
diff --git a/render-test/android/app/src/androidTest/java/android/app/TestState.java b/render-test/android/app/src/androidTest/java/android/app/TestState.java
new file mode 100644
index 0000000000..44a0653fb6
--- /dev/null
+++ b/render-test/android/app/src/androidTest/java/android/app/TestState.java
@@ -0,0 +1,5 @@
+package android.app;
+
+public class TestState {
+ static boolean running = true;
+} \ No newline at end of file
diff --git a/render-test/android/app/src/main/AndroidManifest.xml b/render-test/android/app/src/main/AndroidManifest.xml
index 6c7af7ed8f..8df48ef97f 100644
--- a/render-test/android/app/src/main/AndroidManifest.xml
+++ b/render-test/android/app/src/main/AndroidManifest.xml
@@ -9,7 +9,7 @@
android:fullBackupContent="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
- android:hasCode="false">
+ android:hasCode="true">
<activity android:name="android.app.NativeActivity"
android:label="@string/app_name"
diff --git a/render-test/android/app/src/main/assets/to_zip.txt b/render-test/android/app/src/main/assets/to_zip.txt
new file mode 100644
index 0000000000..7023ad9cfa
--- /dev/null
+++ b/render-test/android/app/src/main/assets/to_zip.txt
@@ -0,0 +1,15 @@
+mapbox-gl-js/test/integration/data/
+mapbox-gl-js/test/integration/video/
+mapbox-gl-js/test/integration/tilesets/
+mapbox-gl-js/test/integration/tiles/
+mapbox-gl-js/test/integration/styles/
+mapbox-gl-js/test/integration/render-tests/
+mapbox-gl-js/test/integration/query-tests/
+mapbox-gl-js/test/integration/image/
+mapbox-gl-js/test/integration/glyphs/
+mapbox-gl-js/test/integration/geojson/
+mapbox-gl-js/test/integration/sprites/
+vendor/mapbox-gl-styles/styles/
+vendor/mapbox-gl-styles/sprites/
+ignores/
+android-manifest.json \ No newline at end of file
diff --git a/render-test/android/render_test_setup.sh b/render-test/android/render_test_setup.sh
deleted file mode 100755
index 1dea44399e..0000000000
--- a/render-test/android/render_test_setup.sh
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/bin/bash
-
-adb shell rm -rf /sdcard/render-test
-adb shell mkdir /sdcard/render-test
-adb shell mkdir /sdcard/render-test/vendor
-adb shell mkdir /sdcard/render-test/ignores
-adb shell mkdir /sdcard/render-test/render-test/tests
-
-# push test sources
-adb push ../../mapbox-gl-js/test/integration/render-tests /sdcard/render-test/mapbox-gl-js/test/integration/render-tests
-adb push ../../mapbox-gl-js/test/integration/query-tests /sdcard/render-test/mapbox-gl-js/test/integration/query-tests
-adb push ../../mapbox-gl-js/test/integration/tiles /sdcard/render-test/mapbox-gl-js/test/integration/tiles
-adb push ../../mapbox-gl-js/test/integration/glyphs /sdcard/render-test/mapbox-gl-js/test/integration/glyphs
-adb push ../../mapbox-gl-js/test/integration/styles /sdcard/render-test/mapbox-gl-js/test/integration/styles
-adb push ../../mapbox-gl-js/test/integration/tilesets /sdcard/render-test/mapbox-gl-js/test/integration/tilesets
-adb push ../../mapbox-gl-js/test/integration/image /sdcard/render-test/mapbox-gl-js/test/integration/image
-adb push ../../mapbox-gl-js/test/integration/video /sdcard/render-test/mapbox-gl-js/test/integration/video
-adb push ../../vendor/mapbox-gl-styles/styles /sdcard/render-test/vendor/mapbox-gl-styles/styles
-adb push ../../vendor/mapbox-gl-styles/sprites /sdcard/render-test/vendor/mapbox-gl-styles/sprites
-adb push ../../mapbox-gl-js/test/integration/data /sdcard/render-test/mapbox-gl-js/test/integration/data
-adb push ../../mapbox-gl-js/test/integration/geojson /sdcard/render-test/mapbox-gl-js/test/integration/geojson
-mkdir sprites
-cp -r ../../mapbox-gl-js/test/integration/sprites/ sprites
-adb push sprites /sdcard/render-test/mapbox-gl-js/test/integration/sprites
-rm -rf sprites
-
-# push extra expectations
-adb push ../../render-test/expected/render-tests /sdcard/render-test/render-test/expected/render-tests
-
-# push default ignore lists
-adb shell mkdir /sdcard/render-test/platform
-adb shell mkdir /sdcard/render-test/platform/node
-adb shell mkdir /sdcard/render-test/platform/node/test
-adb push ../../platform/node/test/ignores.json /sdcard/render-test/platform/node/test
-adb shell mkdir /sdcard/render-test/render-test
-adb push ../linux-ignores.json /sdcard/render-test/render-test
-adb push ../tests/should-fail.json /sdcard/render-test/render-test/tests
-
-# push manifest
-adb push ../android-manifest.json /sdcard/render-test
-
-adb shell ls /sdcard/render-test/ \ No newline at end of file
diff --git a/render-test/include/mbgl/render_test.hpp b/render-test/include/mbgl/render_test.hpp
index 8a82079bee..1669e59932 100644
--- a/render-test/include/mbgl/render_test.hpp
+++ b/render-test/include/mbgl/render_test.hpp
@@ -1,6 +1,9 @@
#pragma once
+
+#include <functional>
+
namespace mbgl {
-int runRenderTests(int argc, char* argv[]);
+int runRenderTests(int argc, char* argv[], std::function<void()>);
} // namespace mbgl
diff --git a/render-test/manifest_parser.cpp b/render-test/manifest_parser.cpp
index 3f1f9f3866..6bba33754a 100644
--- a/render-test/manifest_parser.cpp
+++ b/render-test/manifest_parser.cpp
@@ -34,9 +34,15 @@ const std::vector<std::pair<std::string, std::string>>& Manifest::getIgnores() c
const std::string& Manifest::getTestRootPath() const {
return testRootPath;
}
+const std::string& Manifest::getAssetPath() const {
+ return assetPath;
+}
const std::string& Manifest::getManifestPath() const {
return manifestPath;
}
+const std::string& Manifest::getResultPath() const {
+ return resultPath;
+}
void Manifest::doShuffle(uint32_t seed) {
std::seed_seq sequence{seed};
@@ -239,7 +245,7 @@ mbgl::filesystem::path getValidPath(const std::string& manifestPath, const std::
result = BasePath / result;
}
if (mbgl::filesystem::exists(result)) {
- return result;
+ return result.lexically_normal();
}
mbgl::Log::Warning(mbgl::Event::General, "Invalid path is provoided inside the manifest file: %s", path.c_str());
return mbgl::filesystem::path{};
@@ -285,6 +291,18 @@ mbgl::optional<Manifest> ManifestParser::parseManifest(const std::string& manife
return mbgl::nullopt;
}
}
+ if (document.HasMember("result_path")) {
+ const auto& resultPathValue = document["result_path"];
+ if (!resultPathValue.IsString()) {
+ mbgl::Log::Warning(
+ mbgl::Event::General, "Invalid assetPath is provoided inside the manifest file: %s", filePath.c_str());
+ return mbgl::nullopt;
+ }
+ manifest.resultPath = (getValidPath(manifest.manifestPath, resultPathValue.GetString()) / "").string();
+ if (manifest.resultPath.empty()) {
+ return mbgl::nullopt;
+ }
+ }
mbgl::filesystem::path baseTestPath;
if (document.HasMember("base_test_path")) {
const auto& testPathValue = document["base_test_path"];
@@ -366,6 +384,11 @@ mbgl::optional<Manifest> ManifestParser::parseManifest(const std::string& manife
if (manifest.manifestPath.back() == '/') {
manifest.manifestPath.pop_back();
}
+ if (manifest.resultPath.empty()) {
+ manifest.resultPath = manifest.manifestPath;
+ } else if (manifest.resultPath.back() == '/') {
+ manifest.resultPath.pop_back();
+ }
std::vector<mbgl::filesystem::path> paths;
for (const auto& id : testNames) {
diff --git a/render-test/manifest_parser.hpp b/render-test/manifest_parser.hpp
index bc5adf1091..c4672fb4c5 100644
--- a/render-test/manifest_parser.hpp
+++ b/render-test/manifest_parser.hpp
@@ -17,7 +17,9 @@ public:
const std::vector<std::pair<std::string, std::string>>& getIgnores() const;
const std::vector<TestPaths>& getTestPaths() const;
const std::string& getTestRootPath() const;
+ const std::string& getAssetPath() const;
const std::string& getManifestPath() const;
+ const std::string& getResultPath() const;
void doShuffle(uint32_t seed);
std::string localizeURL(const std::string& url) const;
@@ -43,6 +45,7 @@ private:
std::string testRootPath;
std::string vendorPath;
std::string assetPath;
+ std::string resultPath;
std::vector<std::pair<std::string, std::string>> ignores;
std::vector<TestPaths> testPaths;
};
diff --git a/render-test/render_test.cpp b/render-test/render_test.cpp
index 38d6c15f3f..5753065a7e 100644
--- a/render-test/render_test.cpp
+++ b/render-test/render_test.cpp
@@ -100,7 +100,7 @@ ArgumentsTuple parseArguments(int argc, char** argv) {
} // namespace
namespace mbgl {
-int runRenderTests(int argc, char** argv) {
+int runRenderTests(int argc, char** argv, std::function<void()> testStatus) {
bool recycleMap;
bool shuffle;
uint32_t seed;
@@ -198,10 +198,12 @@ int runRenderTests(int argc, char** argv) {
}
metadatas.push_back(std::move(metadata));
+ if (testStatus) {
+ testStatus();
+ }
}
- const auto& testRootPath = manifest.getManifestPath();
const auto resultPath =
- testRootPath + "/" + (testNames.empty() ? "render-tests" : testNames.front()) + "_index.html";
+ manifest.getResultPath() + "/" + (testNames.empty() ? "render-tests" : testNames.front()) + "_index.html";
std::string resultsHTML = createResultPage(stats, metadatas, shuffle, seed);
mbgl::util::write_file(resultPath, resultsHTML);
diff --git a/render-test/runner.cpp b/render-test/runner.cpp
index 0389b83575..ce76bda157 100644
--- a/render-test/runner.cpp
+++ b/render-test/runner.cpp
@@ -596,7 +596,7 @@ bool TestRunner::runOperations(const std::string& key, TestMetadata& metadata, R
std::string imagePath = operationArray[2].GetString();
imagePath.erase(std::remove(imagePath.begin(), imagePath.end(), '"'), imagePath.end());
- const mbgl::filesystem::path filePath = mbgl::filesystem::path(manifest.getTestRootPath()) / imagePath;
+ const mbgl::filesystem::path filePath = (mbgl::filesystem::path(manifest.getAssetPath()) / imagePath);
mbgl::optional<std::string> maybeImage = mbgl::util::readFile(filePath.string());
if (!maybeImage) {
diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp
index c9d19509f4..1bda870ce5 100644
--- a/src/mbgl/map/transform.cpp
+++ b/src/mbgl/map/transform.cpp
@@ -56,6 +56,7 @@ void Transform::resize(const Size size) {
observer.onCameraWillChange(MapObserver::CameraChangeMode::Immediate);
state.setSize(size);
+ state.constrain();
observer.onCameraDidChange(MapObserver::CameraChangeMode::Immediate);
}
@@ -192,7 +193,6 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
// Minimize rotation by taking the shorter path around the circle.
bearing = _normalizeAngle(bearing, state.getBearing());
state.setBearing(_normalizeAngle(state.getBearing(), bearing));
-
const double startZoom = state.scaleZoom(state.getScale());
const double startBearing = state.getBearing();
const double startPitch = state.getPitch();
@@ -290,41 +290,45 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
state.setRotating(bearing != startBearing);
const EdgeInsets startEdgeInsets = state.getEdgeInsets();
- startTransition(camera, animation, [=](double k) {
- /// s: The distance traveled along the flight path, measured in
- /// ρ-screenfuls.
- double s = k * S;
- double us = k == 1.0 ? 1.0 : u(s);
+ startTransition(
+ camera,
+ animation,
+ [=](double k) {
+ /// s: The distance traveled along the flight path, measured in
+ /// ρ-screenfuls.
+ double s = k * S;
+ double us = k == 1.0 ? 1.0 : u(s);
+
+ // Calculate the current point and zoom level along the flight path.
+ Point<double> framePoint = util::interpolate(startPoint, endPoint, us);
+ double frameZoom =
+ linearZoomInterpolation ? util::interpolate(startZoom, zoom, k) : startZoom + state.scaleZoom(1 / w(s));
+
+ // Zoom can be NaN if size is empty.
+ if (std::isnan(frameZoom)) {
+ frameZoom = zoom;
+ }
- // Calculate the current point and zoom level along the flight path.
- Point<double> framePoint = util::interpolate(startPoint, endPoint, us);
- double frameZoom = linearZoomInterpolation ? util::interpolate(startZoom, zoom, k)
- : startZoom + state.scaleZoom(1 / w(s));
+ // Convert to geographic coordinates and set the new viewpoint.
+ LatLng frameLatLng = Projection::unproject(framePoint, startScale);
+ state.setLatLngZoom(frameLatLng, frameZoom);
- // Zoom can be NaN if size is empty.
- if (std::isnan(frameZoom)) {
- frameZoom = zoom;
- }
-
- // Convert to geographic coordinates and set the new viewpoint.
- LatLng frameLatLng = Projection::unproject(framePoint, startScale);
- state.setLatLngZoom(frameLatLng, frameZoom);
-
- if (bearing != startBearing) {
- state.setBearing(util::wrap(util::interpolate(startBearing, bearing, k), -M_PI, M_PI));
- }
- if (padding != startEdgeInsets) {
- // Interpolate edge insets
- state.setEdgeInsets({util::interpolate(startEdgeInsets.top(), padding.top(), k),
- util::interpolate(startEdgeInsets.left(), padding.left(), k),
- util::interpolate(startEdgeInsets.bottom(), padding.bottom(), k),
- util::interpolate(startEdgeInsets.right(), padding.right(), k)});
- }
- auto maxPitch = getMaxPitchForEdgeInsets(state.getEdgeInsets());
- if (pitch != startPitch || maxPitch < startPitch) {
- state.setPitch(std::min(maxPitch, util::interpolate(startPitch, pitch, k)));
- }
- }, duration);
+ if (bearing != startBearing) {
+ state.setBearing(util::wrap(util::interpolate(startBearing, bearing, k), -M_PI, M_PI));
+ }
+ if (padding != startEdgeInsets) {
+ // Interpolate edge insets
+ state.setEdgeInsets({util::interpolate(startEdgeInsets.top(), padding.top(), k),
+ util::interpolate(startEdgeInsets.left(), padding.left(), k),
+ util::interpolate(startEdgeInsets.bottom(), padding.bottom(), k),
+ util::interpolate(startEdgeInsets.right(), padding.right(), k)});
+ }
+ auto maxPitch = getMaxPitchForEdgeInsets(state.getEdgeInsets());
+ if (pitch != startPitch || maxPitch < startPitch) {
+ state.setPitch(std::min(maxPitch, util::interpolate(startPitch, pitch, k)));
+ }
+ },
+ duration);
}
#pragma mark - Position
@@ -400,6 +404,7 @@ double Transform::getPitch() const {
void Transform::setNorthOrientation(NorthOrientation orientation) {
state.setNorthOrientation(orientation);
+ state.constrain();
}
NorthOrientation Transform::getNorthOrientation() const {
@@ -410,6 +415,7 @@ NorthOrientation Transform::getNorthOrientation() const {
void Transform::setConstrainMode(mbgl::ConstrainMode mode) {
state.setConstrainMode(mode);
+ state.constrain();
}
ConstrainMode Transform::getConstrainMode() const {
@@ -432,6 +438,7 @@ void Transform::setProjectionMode(const ProjectionMode& options) {
state.setAxonometric(options.axonometric.value_or(state.getAxonometric()));
state.setXSkew(options.xSkew.value_or(state.getXSkew()));
state.setYSkew(options.ySkew.value_or(state.getYSkew()));
+ state.updateMatrix();
}
ProjectionMode Transform::getProjectionMode() const {
@@ -499,6 +506,7 @@ void Transform::startTransition(const CameraOptions& camera,
animation.transitionFinishFn();
}
observer.onCameraDidChange(isAnimated ? MapObserver::CameraChangeMode::Animated : MapObserver::CameraChangeMode::Immediate);
+ state.updateMatrix();
};
if (!isAnimated) {
diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp
index 5c2ae6abad..ccbde3c349 100644
--- a/src/mbgl/map/transform_state.cpp
+++ b/src/mbgl/map/transform_state.cpp
@@ -108,17 +108,21 @@ void TransformState::updateMatrix() {
if (size.isEmpty()) {
return;
}
- coordiMatrix = coordinatePointMatrix();
- mat4 mat = coordiMatrix;
+ if (matrixUpdated) return;
+ coordMatrix = coordinatePointMatrix();
+ mat4 mat = coordMatrix;
bool err = matrix::invert(invertedMatrix, mat);
if (err) throw std::runtime_error("failed to invert coordinatePointMatrix");
+ matrixUpdated = true;
}
void TransformState::setSize(const Size& size_) {
- size = size_;
- constrain();
+ if (size != size_) {
+ size = size_;
+ matrixUpdated = false;
+ }
updateMatrix();
}
@@ -134,8 +138,11 @@ NorthOrientation TransformState::getNorthOrientation() const {
}
void TransformState::setNorthOrientation(const NorthOrientation& val) {
- orientation = val;
- constrain();
+ if (orientation != val) {
+ orientation = val;
+ matrixUpdated = false;
+ }
+ updateMatrix();
}
double TransformState::getNorthOrientationAngle() const {
@@ -157,9 +164,13 @@ ConstrainMode TransformState::getConstrainMode() const {
}
void TransformState::setConstrainMode(const ConstrainMode& val) {
- constrainMode = val;
- constrain();
+ if (constrainMode != val) {
+ constrainMode = val;
+ matrixUpdated = false;
+ }
+ updateMatrix();
}
+
#pragma mark - ViewportMode
ViewportMode TransformState::getViewportMode() const {
@@ -182,8 +193,9 @@ CameraOptions TransformState::getCameraOptions(optional<EdgeInsets> padding) con
void TransformState::setEdgeInsets(const EdgeInsets& val) {
if (edgeInsets != val) {
edgeInsets = val;
- updateMatrix();
+ matrixUpdated = false;
}
+ updateMatrix();
}
#pragma mark - Position
@@ -267,8 +279,9 @@ float TransformState::getBearing() const {
void TransformState::setBearing(float val) {
if (bearing != val) {
bearing = val;
- updateMatrix();
+ matrixUpdated = false;
}
+ updateMatrix();
}
float TransformState::getFieldOfView() const {
@@ -286,22 +299,25 @@ float TransformState::getPitch() const {
void TransformState::setPitch(float val) {
if (pitch != val) {
pitch = val;
- updateMatrix();
+ matrixUpdated = false;
}
+ updateMatrix();
}
void TransformState::setXSkew(double val) {
if (xSkew != val) {
xSkew = val;
- updateMatrix();
+ matrixUpdated = false;
}
+ updateMatrix();
}
void TransformState::setYSkew(double val) {
if (ySkew != val) {
ySkew = val;
- updateMatrix();
+ matrixUpdated = false;
}
+ updateMatrix();
}
#pragma mark - State
@@ -343,8 +359,8 @@ ScreenCoordinate TransformState::latLngToScreenCoordinate(const LatLng& latLng)
vec4 p;
Point<double> pt = Projection::project(latLng, scale) / util::tileSize;
vec4 c = {{ pt.x, pt.y, 0, 1 }};
- matrix::transformMat4(p, c, coordiMatrix);
- return { p[0] / p[3], size.height - p[1] / p[3] };
+ matrix::transformMat4(p, c, coordMatrix);
+ return {p[0] / p[3], size.height - p[1] / p[3]};
}
TileCoordinate TransformState::screenCoordinateToTileCoordinate(const ScreenCoordinate& point, uint8_t atZoom) const {
@@ -403,7 +419,6 @@ mat4 TransformState::getPixelMatrix() const {
return m;
}
-
#pragma mark - (private helper functions)
bool TransformState::rotatedNorth() const {
@@ -413,8 +428,10 @@ bool TransformState::rotatedNorth() const {
void TransformState::constrain() {
constrain(scale, x, y);
+ matrixUpdated = false;
updateMatrix();
}
+
void TransformState::constrain(double& scale_, double& x_, double& y_) const {
if (constrainMode == ConstrainMode::None) {
return;
@@ -463,10 +480,11 @@ void TransformState::setLatLngZoom(const LatLng& latLng, double zoom) {
0.5 * Cc * std::log((1 + f) / (1 - f)),
};
setScalePoint(newScale, point);
+ matrixUpdated = false;
updateMatrix();
}
-void TransformState::setScalePoint(const double newScale, const ScreenCoordinate &point) {
+void TransformState::setScalePoint(const double newScale, const ScreenCoordinate& point) {
double constrainedScale = newScale;
ScreenCoordinate constrainedPoint = point;
constrain(constrainedScale, constrainedPoint.x, constrainedPoint.y);
@@ -499,7 +517,7 @@ float TransformState::maxPitchScaleFactor() const {
Point<double> pt = Projection::project(latLng, scale) / util::tileSize;
vec4 p = {{ pt.x, pt.y, 0, 1 }};
vec4 topPoint;
- matrix::transformMat4(topPoint, p, coordiMatrix);
+ matrix::transformMat4(topPoint, p, coordMatrix);
return topPoint[3] / getCameraToCenterDistance();
}
diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp
index 8e2b4b273c..a82f59a9b3 100644
--- a/src/mbgl/map/transform_state.hpp
+++ b/src/mbgl/map/transform_state.hpp
@@ -114,10 +114,11 @@ public:
void setLatLngZoom(const LatLng& latLng, double zoom);
-private:
+ void constrain();
void updateMatrix();
+
+private:
bool rotatedNorth() const;
- void constrain();
void constrain(double& scale, double& x, double& y) const;
// Viewport center offset, from [size.width / 2, size.height / 2], defined
@@ -173,7 +174,8 @@ private:
double Bc = Projection::worldSize(scale) / util::DEGREES_MAX;
double Cc = Projection::worldSize(scale) / util::M2PI;
- mat4 coordiMatrix;
+ bool matrixUpdated{false};
+ mat4 coordMatrix;
mat4 invertedMatrix;
};