From 9c404dfdc1a49b8dce14c1b4999658ae19ce4187 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 8 Mar 2019 16:52:47 -0800 Subject: [PATCH] Add an Android port --- Cargo.lock | 262 +++++++++++++++++- Cargo.toml | 1 + demo/android/.gitignore | 10 + demo/android/app/.gitignore | 1 + demo/android/app/build.gradle | 31 +++ demo/android/app/proguard-rules.pro | 21 ++ .../ExampleInstrumentedTest.java | 26 ++ demo/android/app/src/main/AndroidManifest.xml | 27 ++ .../pathfinderdemo/PathfinderActivity.java | 185 +++++++++++++ .../PathfinderDemoRenderer.java | 40 +++ .../PathfinderDemoSurfaceView.java | 15 + .../drawable-v24/ic_launcher_foreground.xml | 34 +++ .../res/drawable/ic_launcher_background.xml | 170 ++++++++++++ .../main/res/layout/activity_pathfinder.xml | 38 +++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3056 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5024 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2096 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2858 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4569 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 7098 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6464 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 10676 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9250 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 15523 bytes .../android/app/src/main/res/values/attrs.xml | 12 + .../app/src/main/res/values/colors.xml | 8 + .../app/src/main/res/values/strings.xml | 6 + .../app/src/main/res/values/styles.xml | 23 ++ .../pathfinderdemo/ExampleUnitTest.java | 17 ++ demo/android/build.gradle | 27 ++ demo/android/gradle.properties | 14 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54708 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + demo/android/gradlew | 172 ++++++++++++ demo/android/gradlew.bat | 84 ++++++ demo/android/rust/Cargo.toml | 25 ++ demo/android/rust/src/lib.rs | 143 ++++++++++ demo/android/settings.gradle | 1 + demo/common/Cargo.toml | 1 - demo/common/src/device.rs | 5 +- demo/common/src/lib.rs | 64 +++-- demo/common/src/ui.rs | 26 +- demo/common/src/window.rs | 8 + demo/native/Cargo.toml | 7 + demo/native/src/main.rs | 31 ++- gl/Cargo.toml | 1 + gl/src/lib.rs | 42 ++- gpu/src/lib.rs | 56 +--- gpu/src/resources.rs | 70 +++++ renderer/src/gpu/debug.rs | 6 +- renderer/src/gpu/renderer.rs | 31 ++- .../regular.json} | 0 resources/shaders/debug_solid.fs.glsl | 2 +- resources/shaders/debug_solid.vs.glsl | 2 +- resources/shaders/debug_texture.fs.glsl | 2 +- resources/shaders/debug_texture.vs.glsl | 2 +- resources/shaders/demo_ground.fs.glsl | 2 +- resources/shaders/demo_ground.vs.glsl | 2 +- resources/shaders/fill.fs.glsl | 2 +- resources/shaders/fill.vs.glsl | 2 +- resources/shaders/mask_tile.fs.glsl | 2 +- resources/shaders/mask_tile.vs.glsl | 2 +- resources/shaders/post.fs.glsl | 2 +- resources/shaders/post.vs.glsl | 2 +- resources/shaders/solid_tile.fs.glsl | 2 +- resources/shaders/solid_tile.vs.glsl | 2 +- resources/shaders/stencil.fs.glsl | 2 +- resources/shaders/stencil.vs.glsl | 2 +- simd/src/lib.rs | 1 + ui/src/lib.rs | 19 +- 72 files changed, 1676 insertions(+), 133 deletions(-) create mode 100644 demo/android/.gitignore create mode 100644 demo/android/app/.gitignore create mode 100644 demo/android/app/build.gradle create mode 100644 demo/android/app/proguard-rules.pro create mode 100644 demo/android/app/src/androidTest/java/graphics/pathfinder/pathfinderdemo/ExampleInstrumentedTest.java create mode 100644 demo/android/app/src/main/AndroidManifest.xml create mode 100644 demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderActivity.java create mode 100644 demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoRenderer.java create mode 100644 demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoSurfaceView.java create mode 100644 demo/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 demo/android/app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 demo/android/app/src/main/res/layout/activity_pathfinder.xml create mode 100644 demo/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 demo/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 demo/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 demo/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 demo/android/app/src/main/res/values/attrs.xml create mode 100644 demo/android/app/src/main/res/values/colors.xml create mode 100644 demo/android/app/src/main/res/values/strings.xml create mode 100644 demo/android/app/src/main/res/values/styles.xml create mode 100644 demo/android/app/src/test/java/graphics/pathfinder/pathfinderdemo/ExampleUnitTest.java create mode 100644 demo/android/build.gradle create mode 100644 demo/android/gradle.properties create mode 100644 demo/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 demo/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 demo/android/gradlew create mode 100644 demo/android/gradlew.bat create mode 100644 demo/android/rust/Cargo.toml create mode 100644 demo/android/rust/src/lib.rs create mode 100644 demo/android/settings.gradle create mode 100644 gpu/src/resources.rs rename resources/{debug-font.json => debug-fonts/regular.json} (100%) diff --git a/Cargo.lock b/Cargo.lock index bd87e05b..62ba61c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,6 +5,14 @@ name = "adler32" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "aho-corasick" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "aho-corasick" version = "0.6.10" @@ -38,6 +46,11 @@ dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ascii" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "atty" version = "0.2.11" @@ -53,6 +66,28 @@ name = "autocfg" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "backtrace" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "base64" version = "0.9.3" @@ -77,6 +112,11 @@ name = "cc" version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cfg-if" version = "0.1.6" @@ -109,6 +149,18 @@ name = "color_quant" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "combine" +version = "3.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ascii 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crc32fast" version = "1.1.2" @@ -162,13 +214,25 @@ name = "demo" version = "0.1.0" dependencies = [ "gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "nfd 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "pathfinder_demo 0.1.0", "pathfinder_geometry 0.3.0", + "pathfinder_gl 0.1.0", + "pathfinder_gpu 0.1.0", "pathfinder_simd 0.3.0", "sdl2 0.32.1 (registry+https://github.com/rust-lang/crates.io-index)", "sdl2-sys 0.32.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "egl" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "khronos 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "either" version = "1.5.0" @@ -186,6 +250,14 @@ dependencies = [ "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "error-chain" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "euclid" version = "0.19.5" @@ -350,6 +422,24 @@ dependencies = [ "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "jni" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "combine 3.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "jpeg-decoder" version = "0.1.15" @@ -359,6 +449,23 @@ dependencies = [ "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "khronos" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "khronos_api" version = "2.2.0" @@ -369,6 +476,11 @@ name = "lazy_static" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "libc" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "libc" version = "0.2.48" @@ -415,6 +527,14 @@ name = "lzw" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "memchr" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "memchr" version = "2.2.0" @@ -506,6 +626,19 @@ dependencies = [ "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pathfinder_android_demo" +version = "0.1.0" +dependencies = [ + "egl 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "jni 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pathfinder_demo 0.1.0", + "pathfinder_geometry 0.3.0", + "pathfinder_gl 0.1.0", + "pathfinder_gpu 0.1.0", +] + [[package]] name = "pathfinder_demo" version = "0.1.0" @@ -514,7 +647,6 @@ dependencies = [ "gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", "jemallocator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "nfd 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "pathfinder_geometry 0.3.0", "pathfinder_gl 0.1.0", "pathfinder_gpu 0.1.0", @@ -548,6 +680,7 @@ dependencies = [ "pathfinder_gpu 0.1.0", "pathfinder_renderer 0.1.0", "pathfinder_simd 0.3.0", + "rustache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -837,6 +970,18 @@ dependencies = [ "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "regex" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "regex" version = "1.1.0" @@ -849,6 +994,11 @@ dependencies = [ "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "regex-syntax" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "regex-syntax" version = "0.6.5" @@ -865,6 +1015,25 @@ dependencies = [ "xmlparser 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rustache" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ryu" version = "0.2.7" @@ -880,6 +1049,14 @@ name = "safemem" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "same-file" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "scoped_threadpool" version = "0.1.9" @@ -1025,6 +1202,23 @@ dependencies = [ "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "thread-id" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "thread_local" version = "0.3.6" @@ -1053,6 +1247,14 @@ name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "usvg" version = "0.4.0" @@ -1067,6 +1269,11 @@ dependencies = [ "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "utf8-ranges" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "utf8-ranges" version = "1.0.2" @@ -1077,6 +1284,26 @@ name = "vec_map" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "walkdir" +version = "2.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi" version = "0.3.6" @@ -1086,6 +1313,11 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -1128,26 +1360,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" +"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" "checksum aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" +"checksum ascii 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a5fc969a8ce2c9c0c4b0429bb8431544f6658283c8326ba5ff8c762b75369335" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" +"checksum backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5a90e2b463010cd0e0ce9a11d4a9d5d58d9f41d4a6ba3dcaf9e68b466e88b4" +"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" "checksum cc 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)" = "4390a3b5f4f6bce9c1d0c00128379df433e53777fdd30e92f16a529332baec4e" +"checksum cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum color_quant 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd" +"checksum combine 3.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d2623b3542b48f4427e15ddd4995186decb594ebbd70271463886584b4a114b9" "checksum crc32fast 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e91d5240c6975ef33aeb5f148f35275c25eda8e8a5f95abe421978b05b8bf192" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" "checksum deflate 0.7.19 (registry+https://github.com/rust-lang/crates.io-index)" = "8a6abb26e16e8d419b5c78662aa9f82857c2386a073da266840e474d5055ec86" +"checksum egl 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a373bc9844200b1ff15bd1b245931d1c20d09d06e4ec09f361171f29a4b0752d" "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" "checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" +"checksum error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" "checksum euclid 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d1a7698bdda3d7444a79d33bdc96e8b518d44ea3ff101d8492a6ca1207b886ea" "checksum euclid_macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcb84c18ea5037a1c5a23039b4ff29403abce2e0d6b1daa11cf0bde2b30be15" "checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" @@ -1166,15 +1406,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" "checksum jemalloc-sys 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "bfc62c8e50e381768ce8ee0428ee53741929f7ebd73e4d83f669bcf7693e00ae" "checksum jemallocator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9f0cd42ac65f758063fea55126b0148b1ce0a6354ff78e07a4d6806bc65c4ab3" +"checksum jni 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "294eca097d1dc0bf59de5ab9f7eafa5f77129e9f6464c957ed3ddeb705fb4292" +"checksum jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" "checksum jpeg-decoder 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c8b7d43206b34b3f94ea9445174bda196e772049b9bddbc620c9d29b2d20110d" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum khronos 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c0711aaa80e6ba6eb1fa8978f1f46bfcb38ceb2f3f33f3736efbff39dac89f50" "checksum khronos_api 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "037ab472c33f67b5fbd3e9163a2645319e5356fcd355efa6d4eb7fff4bbcb554" "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" +"checksum libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "e32a70cf75e5846d53a673923498228bbec6a8624708a9ea5645f075d6276122" "checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" "checksum libflate 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "54d1ddf9c52870243c5689d7638d888331c1116aa5b398f3ba1acfa7d8758ca1" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum lyon_geom 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2b60eaa9061c87affcd671e88289ce6971324269ec6548b677e02624ef3ef63c" "checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" +"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum nfd 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8e752e3c216bc8a491c5b59fa46da10f1379ae450b19ac688e07f4bb55042e98" @@ -1213,12 +1459,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f" +"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" "checksum regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8c2f35eedad5295fdf00a63d7d4b238135723f92b434ec06774dad15c7ab0861" "checksum roxmltree 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "02660467d0c2da1b6276042501aee6e15ec5b8ff59423243f185b294cd53acf3" +"checksum rustache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c86de9443c1a5618e0d51bbd8eb6227ead9916446eb8952575a70d1ef7e00209" +"checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" +"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" "checksum safe-transmute 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9604873ffe1980bc1f179103704a65c8aca141c248d9e52b7af95ff10578166e" "checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9" +"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum sdl2 0.32.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0ebf85f207d42e4da59fa31fff977be5ff0b224873506c4bd70cc1c94b331593" @@ -1237,15 +1489,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" +"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" +"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" "checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum usvg 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ebf4d5244ba2e8305caf9de7949377794ecdea5a9e3c84fc5610d78d21f5ee" +"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f" "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 731e21c9..2f3a55f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "demo/android/rust", "demo/common", "demo/native", "geometry", diff --git a/demo/android/.gitignore b/demo/android/.gitignore new file mode 100644 index 00000000..5edb4eeb --- /dev/null +++ b/demo/android/.gitignore @@ -0,0 +1,10 @@ +*.iml +.gradle +/local.properties +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/demo/android/app/.gitignore b/demo/android/app/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/demo/android/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/demo/android/app/build.gradle b/demo/android/app/build.gradle new file mode 100644 index 00000000..7bf2f59c --- /dev/null +++ b/demo/android/app/build.gradle @@ -0,0 +1,31 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 27 + defaultConfig { + applicationId "graphics.pathfinder.pathfinderdemo" + minSdkVersion 21 + targetSdkVersion 27 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + ndk { + abiFilters "arm64-v8a" + } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.android.support:appcompat-v7:27.1.1' + implementation 'com.android.support:support-v4:27.1.1' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' +} diff --git a/demo/android/app/proguard-rules.pro b/demo/android/app/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/demo/android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/demo/android/app/src/androidTest/java/graphics/pathfinder/pathfinderdemo/ExampleInstrumentedTest.java b/demo/android/app/src/androidTest/java/graphics/pathfinder/pathfinderdemo/ExampleInstrumentedTest.java new file mode 100644 index 00000000..fb759831 --- /dev/null +++ b/demo/android/app/src/androidTest/java/graphics/pathfinder/pathfinderdemo/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package graphics.pathfinder.pathfinderdemo; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("graphics.pathfinder.pathfinderdemo", appContext.getPackageName()); + } +} diff --git a/demo/android/app/src/main/AndroidManifest.xml b/demo/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..b01420de --- /dev/null +++ b/demo/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderActivity.java b/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderActivity.java new file mode 100644 index 00000000..e07a3575 --- /dev/null +++ b/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderActivity.java @@ -0,0 +1,185 @@ +package graphics.pathfinder.pathfinderdemo; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.content.pm.PackageManager; +import android.support.annotation.NonNull; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.os.Handler; +import android.view.MotionEvent; +import android.view.View; + +/** + * An example full-screen activity that shows and hides the system UI (i.e. + * status bar and navigation/system bar) with user interaction. + */ +public class PathfinderActivity extends AppCompatActivity { + /** + * Whether or not the system UI should be auto-hidden after + * {@link #AUTO_HIDE_DELAY_MILLIS} milliseconds. + */ + private static final boolean AUTO_HIDE = true; + + /** + * If {@link #AUTO_HIDE} is set, the number of milliseconds to wait after + * user interaction before hiding the system UI. + */ + private static final int AUTO_HIDE_DELAY_MILLIS = 3000; + + /** + * Some older devices needs a small delay between UI widget updates + * and a change of the status and navigation bar. + */ + private static final int UI_ANIMATION_DELAY = 300; + private final Handler mHideHandler = new Handler(); + private PathfinderDemoSurfaceView mContentView; + private final Runnable mHidePart2Runnable = new Runnable() { + @SuppressLint("InlinedApi") + @Override + public void run() { + // Delayed removal of status and navigation bar + + // Note that some of these constants are new as of API 16 (Jelly Bean) + // and API 19 (KitKat). It is safe to use them, as they are inlined + // at compile-time and do nothing on earlier devices. + mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + } + }; + private View mControlsView; + private final Runnable mShowPart2Runnable = new Runnable() { + @Override + public void run() { + // Delayed display of UI elements + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.show(); + } + mControlsView.setVisibility(View.VISIBLE); + } + }; + private boolean mVisible; + private final Runnable mHideRunnable = new Runnable() { + @Override + public void run() { + hide(); + } + }; + /** + * Touch listener to use for in-layout UI controls to delay hiding the + * system UI. This is to prevent the jarring behavior of controls going away + * while interacting with activity UI. + */ + private final View.OnTouchListener mDelayHideTouchListener = new View.OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + if (AUTO_HIDE) { + delayedHide(AUTO_HIDE_DELAY_MILLIS); + } + return false; + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (ContextCompat.checkSelfPermission(this, + Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + String[] perms = new String[1]; + perms[0] = Manifest.permission.READ_EXTERNAL_STORAGE; + ActivityCompat.requestPermissions(this, perms, + 1); + } else { + init(); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + + if (permissions[0] == Manifest.permission.READ_EXTERNAL_STORAGE) + init(); + } + + private void init() { + setContentView(R.layout.activity_pathfinder); + + mVisible = true; + mControlsView = findViewById(R.id.fullscreen_content_controls); + mContentView = findViewById(R.id.fullscreen_content); + + // Set up the user interaction to manually show or hide the system UI. + mContentView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + toggle(); + } + }); + + mContentView.setEGLContextClientVersion(3); + mContentView.setRenderer(new PathfinderDemoRenderer(getAssets())); + } + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + + // Trigger the initial hide() shortly after the activity has been + // created, to briefly hint to the user that UI controls + // are available. + delayedHide(100); + } + + private void toggle() { + if (mVisible) { + hide(); + } else { + show(); + } + } + + private void hide() { + // Hide UI first + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.hide(); + } + mControlsView.setVisibility(View.GONE); + mVisible = false; + + // Schedule a runnable to remove the status and navigation bar after a delay + mHideHandler.removeCallbacks(mShowPart2Runnable); + mHideHandler.postDelayed(mHidePart2Runnable, UI_ANIMATION_DELAY); + } + + @SuppressLint("InlinedApi") + private void show() { + // Show the system bar + mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + mVisible = true; + + // Schedule a runnable to display UI elements after a delay + mHideHandler.removeCallbacks(mHidePart2Runnable); + mHideHandler.postDelayed(mShowPart2Runnable, UI_ANIMATION_DELAY); + } + + /** + * Schedules a call to hide() in delay milliseconds, canceling any + * previously scheduled calls. + */ + private void delayedHide(int delayMillis) { + mHideHandler.removeCallbacks(mHideRunnable); + mHideHandler.postDelayed(mHideRunnable, delayMillis); + } +} diff --git a/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoRenderer.java b/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoRenderer.java new file mode 100644 index 00000000..ed553700 --- /dev/null +++ b/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoRenderer.java @@ -0,0 +1,40 @@ +package graphics.pathfinder.pathfinderdemo; + +import android.content.res.AssetManager; +import android.opengl.GLSurfaceView; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +public class PathfinderDemoRenderer extends Object implements GLSurfaceView.Renderer { + private AssetManager m_assetManager; + + private static native void init(PathfinderDemoResourceLoader resourceLoader); + private static native void runOnce(); + + static { + System.loadLibrary("pathfinder_android_demo"); + } + + protected PathfinderDemoRenderer() {} + + PathfinderDemoRenderer(AssetManager assetManager) { + super(); + m_assetManager = assetManager; + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + init(new PathfinderDemoResourceLoader(m_assetManager)); + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + + } + + @Override + public void onDrawFrame(GL10 gl) { + runOnce(); + } +} diff --git a/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoSurfaceView.java b/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoSurfaceView.java new file mode 100644 index 00000000..d89e71ef --- /dev/null +++ b/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoSurfaceView.java @@ -0,0 +1,15 @@ +package graphics.pathfinder.pathfinderdemo; + +import android.content.Context; +import android.opengl.GLSurfaceView; +import android.util.AttributeSet; + +public class PathfinderDemoSurfaceView extends GLSurfaceView { + public PathfinderDemoSurfaceView(Context context) { + super(context); + } + + public PathfinderDemoSurfaceView(Context context, AttributeSet attrs) { + super(context, attrs); + } +} diff --git a/demo/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/demo/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..c7bd21db --- /dev/null +++ b/demo/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/demo/android/app/src/main/res/drawable/ic_launcher_background.xml b/demo/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..d5fccc53 --- /dev/null +++ b/demo/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/android/app/src/main/res/layout/activity_pathfinder.xml b/demo/android/app/src/main/res/layout/activity_pathfinder.xml new file mode 100644 index 00000000..cde78529 --- /dev/null +++ b/demo/android/app/src/main/res/layout/activity_pathfinder.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/demo/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/demo/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/demo/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/demo/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/demo/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..a2f5908281d070150700378b64a84c7db1f97aa1 GIT binary patch literal 3056 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 axPAxe%7rDusX+w*00006jj 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⁣ zwiG+x#%j}{0gU~J8;<|!B1@-XaB@{KORFwrYg_8rOv({b0EO#DbeQRm;B6_9=mXGf z-x|VL{zd`)#@yN}HkCSJbjbNlE|zL3Wm9Q8HY`sV)}3%pgN>cL^67{Z;PPL(*wT8N zUjXU{@|*hvm}({wsAC=x0^ok0%UAz0;sogW{B!nDqk|JJ5x~4NfTDgP49^zeu`csl?5mY@JdQdISc zFs!E{^grmkLnUk9 zny~m)1vws@5BFI<-0Tuo2JWX(0v`W|t(wg;s--L47WTvTMz-8l#TL^=OJNRS2?_Qj z3AKT+gvbyBi#H*-tJ%tWD|>EV3wy|8qxfzS!5RW;Jpl5*zo&^UBU=fG#2}UvRyNkK zA06Dy9;K1ca@r2T>yThYgI!ont$(G{6q#2QT+00r_x0(b)gsE`lBB?2gr55gq^D3Fi&p%E(p9>U%bv zkg1Jco(RbyTX7FDHOnl7-O@ zI$AaIl?9NJKPm(WiBP`1-#CB1QzU>&hKm)fpa5DKE{2$X0hGz-0uZ?cyTk(YC!Y&| zL=1VrNERSA5NA2jq7FACfX4JfPyj5XXl1yv0>~s;eF7L2$>&oMqeTFT2m$y7FlkON z_yurD1yIOvA;5C6016pyxBznGUt0kJ&k5r#;&>Jow`r)sp9R~PmK~lz$3xH%LT*1U zJdOyABZ3!FvNoR*vN$5ykHS8f`jA4zV+|L}i1C4`B2c{R0;UdYxaU|H)2avz@ z=mEYc|2S<+(B2Tj+FkX+2D+yFI!k9lWMA61DJ{)e;lum$(;O87?vGJJe!KtK04+N_ zI*P~t@dUb>9Xh{dbyl{-ZQ(UMgz7$|QfL5XSPkskt^NgctYC#;4WcZB1@%@wy@2t3 z2z0DI7&%b$*Aw~abe?GxE`ez@+6hOh-6*8fHRV{1os$EL@}uUZeG4h1&Be`98q*7j z=3-v+lhIjfWVo12!<>%V^a6lTgW3+_#W6n|p*~==zOH7z$0{LSZk(Tpd7EaD04hnA zL;#fxS0aD{`5^&D`}>0Uq?byDD-l2=!wm_bLcUl4gc(% za1p|itVANvFF>hghAS07Im1;IK;|b*W)}VDyI;BIp2=K*yu2a)j?B|f<44NI$NbmJ z#dE0>jI$fMr&@>4kN8MLFb4&2O9fEKaQg%(QO$4_1rVQywG^CmBLh#}_7gKW3vd?| z2?1^&KWq8}8I^_S0|)MowU_pw$q@nl@Nkn$z>BQq_KA^9yaR`(R3u{{Ig;cwt z@AJ^{ODQCm^neroM9nKNUAXi9RCK`OsP_LuR0PUR(YZCCX5dNF6VzcoK&=b^r`W?ltt|*F zpkoae%ZT{C1h~EcFui~b7fF`vb<<~j_VquuUA$}QqIKYELPp#;{u?q8Dz}WAG-(3; zjrm$i%7UbyZMM(Y{>!uJ#vNB?R~B{6Htp=>e*<{fQQ5W7V(1coCWlOON!MzZxhum| ztZBQpGR z;~#ur^&PockKdV{Q6R>o`Pl{0x!DEbpZ7y9Y;*ZvE!*gU`V1W3znva{f=?WO5I&>B z&hw6}tjECtaghm5z|C#%M;Yf_*pI^};h}Vl=^r9EN=tVDj86D;C$jIJ?K7VP+00000NkvXXu0mjf D5i!M* literal 0 HcmV?d00001 diff --git a/demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..459ca609d3ae0d3943ab44cdc27feef9256dc6d7 GIT binary patch literal 7098 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 literal 0 HcmV?d00001 diff --git a/demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..8ca12fe024be86e868d14e91120a6902f8e88ac6 GIT binary patch literal 6464 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 IDLoGUAF?I_i2wiq literal 0 HcmV?d00001 diff --git a/demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..8e19b410a1b15ff180f3dacac19395fe3046cdec GIT binary patch literal 10676 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 a9sdtN6eHRrdyqB&0000GHuP>U&@ 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 BIP(Ai literal 0 HcmV?d00001 diff --git a/demo/android/app/src/main/res/values/attrs.xml b/demo/android/app/src/main/res/values/attrs.xml new file mode 100644 index 00000000..7ce840eb --- /dev/null +++ b/demo/android/app/src/main/res/values/attrs.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/demo/android/app/src/main/res/values/colors.xml b/demo/android/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..227fd331 --- /dev/null +++ b/demo/android/app/src/main/res/values/colors.xml @@ -0,0 +1,8 @@ + + + #3F51B5 + #303F9F + #FF4081 + + #66000000 + diff --git a/demo/android/app/src/main/res/values/strings.xml b/demo/android/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..3f3defda --- /dev/null +++ b/demo/android/app/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + Pathfinder Demo + + Dummy Button + DUMMY\nCONTENT + diff --git a/demo/android/app/src/main/res/values/styles.xml b/demo/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..dc8a48c2 --- /dev/null +++ b/demo/android/app/src/main/res/values/styles.xml @@ -0,0 +1,23 @@ + + + + + + + + + + diff --git a/demo/android/app/src/test/java/graphics/pathfinder/pathfinderdemo/ExampleUnitTest.java b/demo/android/app/src/test/java/graphics/pathfinder/pathfinderdemo/ExampleUnitTest.java new file mode 100644 index 00000000..e9c873fa --- /dev/null +++ b/demo/android/app/src/test/java/graphics/pathfinder/pathfinderdemo/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package graphics.pathfinder.pathfinderdemo; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/demo/android/build.gradle b/demo/android/build.gradle new file mode 100644 index 00000000..1a3d8123 --- /dev/null +++ b/demo/android/build.gradle @@ -0,0 +1,27 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.1.2' + + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/demo/android/gradle.properties b/demo/android/gradle.properties new file mode 100644 index 00000000..ec074606 --- /dev/null +++ b/demo/android/gradle.properties @@ -0,0 +1,14 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +android.useDeprecatedNdk=true diff --git a/demo/android/gradle/wrapper/gradle-wrapper.jar b/demo/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..7a3265ee94c0ab25cf079ac8ccdf87f41d455d42 GIT binary patch literal 54708 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/demo/android/gradlew.bat b/demo/android/gradlew.bat new file mode 100644 index 00000000..e95643d6 --- /dev/null +++ b/demo/android/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/demo/android/rust/Cargo.toml b/demo/android/rust/Cargo.toml new file mode 100644 index 00000000..d2313bd6 --- /dev/null +++ b/demo/android/rust/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "pathfinder_android_demo" +version = "0.1.0" +authors = ["Patrick Walton "] +edition = "2018" + +[lib] +crate_type = ["cdylib"] + +[dependencies] +egl = "0.2" +gl = "0.6" +jni = "0.11" + +[dependencies.pathfinder_demo] +path = "../../common" + +[dependencies.pathfinder_geometry] +path = "../../../geometry" + +[dependencies.pathfinder_gl] +path = "../../../gl" + +[dependencies.pathfinder_gpu] +path = "../../../gpu" diff --git a/demo/android/rust/src/lib.rs b/demo/android/rust/src/lib.rs new file mode 100644 index 00000000..5ce0fca0 --- /dev/null +++ b/demo/android/rust/src/lib.rs @@ -0,0 +1,143 @@ +// pathfinder/demo/android/rust/src/main.rs +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use jni::{JNIEnv, JavaVM}; +use jni::objects::{GlobalRef, JByteBuffer, JClass, JObject, JValue}; +use pathfinder_demo::DemoApp; +use pathfinder_demo::window::{Event, Keycode, Window}; +use pathfinder_geometry::basic::point::Point2DI32; +use pathfinder_gl::GLVersion; +use pathfinder_gpu::resources::ResourceLoader; +use std::cell::RefCell; +use std::io::Error as IOError; +use std::os::raw::c_void; +use std::path::PathBuf; + +thread_local! { + static DEMO_APP: RefCell>> = RefCell::new(None); + static JAVA_RESOURCE_LOADER: RefCell> = RefCell::new(None); +} + +static RESOURCE_LOADER: AndroidResourceLoader = AndroidResourceLoader; + +#[no_mangle] +pub unsafe extern "system" fn + Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_init(env: JNIEnv, + class: JClass, + loader: JObject) { + JAVA_RESOURCE_LOADER.with(|java_resource_loader| { + *java_resource_loader.borrow_mut() = Some(JavaResourceLoader::new(env, loader)) + }); + DEMO_APP.with(|demo_app| *demo_app.borrow_mut() = Some(DemoApp::::new())); +} + +#[no_mangle] +pub unsafe extern "system" fn + Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_runOnce(env: JNIEnv, + class: JClass) { + DEMO_APP.with(|demo_app| { + if let Some(ref mut demo_app) = *demo_app.borrow_mut() { + demo_app.run_once(vec![]); + } + }); +} + +#[no_mangle] +pub unsafe extern "system" fn + Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_pushMouseDown(env: JNIEnv, + class: JClass, + x: i32, + y: i32) { +} + +struct WindowImpl; + +impl Window for WindowImpl { + fn new(default_framebuffer_size: Point2DI32) -> WindowImpl { + gl::load_with(|name| egl::get_proc_address(name) as *const c_void); + WindowImpl + } + + fn gl_version(&self) -> GLVersion { + GLVersion::GLES3 + } + + fn size(&self) -> Point2DI32 { + Point2DI32::new(1080, 1920) + } + + fn drawable_size(&self) -> Point2DI32 { + Point2DI32::new(1080, 1920) + } + + fn mouse_position(&self) -> Point2DI32 { + Point2DI32::new(0, 0) + } + + fn present(&self) {} + + fn resource_loader(&self) -> &dyn ResourceLoader { + &RESOURCE_LOADER + } + + fn create_user_event_id(&self) -> u32 { + 0 + } + + fn push_user_event(message_type: u32, message_data: u32) { + } + + fn run_open_dialog(&self, extension: &str) -> Result { + // TODO(pcwalton) + Err(()) + } + + fn run_save_dialog(&self, extension: &str) -> Result { + // TODO(pcwalton) + Err(()) + } +} + +struct AndroidResourceLoader; + +impl ResourceLoader for AndroidResourceLoader { + fn slurp(&self, path: &str) -> Result, IOError> { + JAVA_RESOURCE_LOADER.with(|java_resource_loader| { + let java_resource_loader = java_resource_loader.borrow(); + let java_resource_loader = java_resource_loader.as_ref().unwrap(); + let loader = java_resource_loader.loader.as_obj(); + let env = java_resource_loader.vm.get_env().unwrap(); + match env.call_method(loader, + "slurp", + "(Ljava/lang/String;)Ljava/nio/ByteBuffer;", + &[JValue::Object(*env.new_string(path).unwrap())]).unwrap() { + JValue::Object(object) => { + let byte_buffer = JByteBuffer::from(object); + Ok(Vec::from(env.get_direct_buffer_address(byte_buffer).unwrap())) + } + _ => panic!("Unexpected return value!"), + } + }) + } +} + +struct JavaResourceLoader { + loader: GlobalRef, + vm: JavaVM, +} + +impl JavaResourceLoader { + fn new(env: JNIEnv, loader: JObject) -> JavaResourceLoader { + JavaResourceLoader { + loader: env.new_global_ref(loader).unwrap(), + vm: env.get_java_vm().unwrap(), + } + } +} diff --git a/demo/android/settings.gradle b/demo/android/settings.gradle new file mode 100644 index 00000000..e7b4def4 --- /dev/null +++ b/demo/android/settings.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/demo/common/Cargo.toml b/demo/common/Cargo.toml index adac8377..fcd8225f 100644 --- a/demo/common/Cargo.toml +++ b/demo/common/Cargo.toml @@ -8,7 +8,6 @@ authors = ["Patrick Walton "] clap = "2.32" gl = "0.6" jemallocator = "0.1" -nfd = "0.0.4" rayon = "1.0" usvg = "0.4" diff --git a/demo/common/src/device.rs b/demo/common/src/device.rs index 16ffd0de..f3101b21 100644 --- a/demo/common/src/device.rs +++ b/demo/common/src/device.rs @@ -11,7 +11,8 @@ //! GPU rendering code specifically for the demo. use crate::GRIDLINE_COUNT; -use pathfinder_gpu::{BufferTarget, BufferUploadMode, Device, Resources, VertexAttrType}; +use pathfinder_gpu::resources::ResourceLoader; +use pathfinder_gpu::{BufferTarget, BufferUploadMode, Device, VertexAttrType}; pub struct GroundProgram where D: Device { pub program: D::Program, @@ -20,7 +21,7 @@ pub struct GroundProgram where D: Device { } impl GroundProgram where D: Device { - pub fn new(device: &D, resources: &Resources) -> GroundProgram { + pub fn new(device: &D, resources: &dyn ResourceLoader) -> GroundProgram { let program = device.create_program(resources, "demo_ground"); let transform_uniform = device.get_uniform(&program, "Transform"); let color_uniform = device.get_uniform(&program, "Color"); diff --git a/demo/common/src/lib.rs b/demo/common/src/lib.rs index 31fc70f4..6f121bb9 100644 --- a/demo/common/src/lib.rs +++ b/demo/common/src/lib.rs @@ -22,8 +22,9 @@ use pathfinder_geometry::basic::transform2d::Transform2DF32; use pathfinder_geometry::basic::transform3d::{Perspective, Transform3DF32}; use pathfinder_geometry::color::ColorU; use pathfinder_gl::GLDevice; -use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderState, Resources}; -use pathfinder_gpu::{StencilFunc, StencilState, UniformData}; +use pathfinder_gpu::resources::ResourceLoader; +use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderState, StencilFunc}; +use pathfinder_gpu::{StencilState, UniformData}; use pathfinder_renderer::builder::{RenderOptions, RenderTransform, SceneBuilder}; use pathfinder_renderer::gpu::renderer::Renderer; use pathfinder_renderer::gpu_data::BuiltScene; @@ -34,6 +35,9 @@ use pathfinder_svg::BuiltSVG; use pathfinder_ui::UIEvent; use rayon::ThreadPoolBuilder; use std::f32::consts::FRAC_PI_4; +use std::ffi::CString; +use std::fs::File; +use std::io::Read; use std::mem; use std::panic; use std::path::{Path, PathBuf}; @@ -46,7 +50,7 @@ use usvg::{Options as UsvgOptions, Tree}; #[global_allocator] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; -static DEFAULT_SVG_FILENAME: &'static str = "Ghostscript_Tiger.svg"; +static DEFAULT_SVG_VIRTUAL_PATH: &'static str = "svg/Ghostscript_Tiger.svg"; const MAIN_FRAMEBUFFER_WIDTH: u32 = 1067; const MAIN_FRAMEBUFFER_HEIGHT: u32 = 800; @@ -111,18 +115,18 @@ impl DemoApp where W: Window { let window = W::new(default_framebuffer_size); let expire_message_event_id = window.create_user_event_id(); - let device = GLDevice::new(); - let resources = Resources::locate(); - let options = Options::get(&resources); + let device = GLDevice::new(window.gl_version()); + let resources = window.resource_loader(); + let options = Options::get(resources); let (window_size, drawable_size) = (window.size(), window.drawable_size()); - let built_svg = load_scene(&options.input_path); + let built_svg = load_scene(resources, &options.input_path); let message = get_svg_building_message(&built_svg); let scene_view_box = built_svg.scene.view_box; let scene_is_monochrome = built_svg.scene.is_monochrome(); - let renderer = Renderer::new(device, &resources, drawable_size); + let renderer = Renderer::new(device, resources, drawable_size); let scene_thread_proxy = SceneThreadProxy::new(built_svg.scene, options.clone()); scene_thread_proxy.set_drawable_size(window.drawable_size()); @@ -132,15 +136,15 @@ impl DemoApp where W: Window { Camera::new_2d(scene_view_box, drawable_size) }; - let ground_program = GroundProgram::new(&renderer.device, &resources); + let ground_program = GroundProgram::new(&renderer.device, resources); let ground_solid_vertex_array = GroundSolidVertexArray::new(&renderer.device, &ground_program, - &renderer.quad_vertex_positions_buffer()); + &renderer.quad_vertex_positions_buffer()); let ground_line_vertex_array = GroundLineVertexArray::new(&renderer.device, &ground_program); - let mut ui = DemoUI::new(&renderer.device, &resources, options); + let mut ui = DemoUI::new(&renderer.device, resources, options); let mut message_epoch = 0; emit_message::(&mut ui, &mut message_epoch, expire_message_event_id, message); @@ -341,7 +345,10 @@ impl DemoApp where W: Window { self.ui.show_text_effects = self.scene_is_monochrome; let mut ui_action = UIAction::None; - self.ui.update(&self.renderer.device, &mut self.renderer.debug_ui, &mut ui_action); + self.ui.update(&self.renderer.device, + &self.window, + &mut self.renderer.debug_ui, + &mut ui_action); ui_event = mem::replace(&mut self.renderer.debug_ui.ui.event, UIEvent::None); self.handle_ui_action(&mut ui_action); @@ -469,7 +476,7 @@ impl DemoApp where W: Window { UIAction::None => {} UIAction::OpenFile(ref path) => { - let built_svg = load_scene(&path); + let built_svg = load_scene(self.window.resource_loader(), &Some((*path).clone())); self.ui.message = get_svg_building_message(&built_svg); self.scene_view_box = built_svg.scene.view_box; @@ -620,11 +627,11 @@ enum SceneToMainMsg { pub struct Options { jobs: Option, three_d: bool, - input_path: PathBuf, + input_path: Option, } impl Options { - fn get(resources: &Resources) -> Options { + fn get(resources: &dyn ResourceLoader) -> Options { let matches = App::new("tile-svg") .arg( Arg::with_name("jobs") @@ -643,15 +650,7 @@ impl Options { .map(|string| string.parse().unwrap()); let three_d = matches.is_present("3d"); - let input_path = match matches.value_of("INPUT") { - Some(path) => PathBuf::from(path), - None => { - let mut path = resources.resources_directory.clone(); - path.push("svg"); - path.push(DEFAULT_SVG_FILENAME); - path - } - }; + let input_path = matches.value_of("INPUT").map(PathBuf::from); // Set up Rayon. let mut thread_pool_builder = ThreadPoolBuilder::new(); @@ -664,8 +663,21 @@ impl Options { } } -fn load_scene(input_path: &Path) -> BuiltSVG { - BuiltSVG::from_tree(Tree::from_file(input_path, &UsvgOptions::default()).unwrap()) +fn load_scene(resource_loader: &dyn ResourceLoader, input_path: &Option) -> BuiltSVG { + let mut data; + match *input_path { + Some(ref input_path) => { + data = vec![]; + let mut file = match File::open(input_path) { + Ok(file) => file, + Err(err) => panic!(), + }; + file.read_to_end(&mut data).unwrap(); + } + None => data = resource_loader.slurp(DEFAULT_SVG_VIRTUAL_PATH).unwrap(), + } + + BuiltSVG::from_tree(Tree::from_data(&data, &UsvgOptions::default()).unwrap()) } fn build_scene(scene: &Scene, build_options: BuildOptions, jobs: Option) -> BuiltScene { diff --git a/demo/common/src/ui.rs b/demo/common/src/ui.rs index b2d2e298..7ef46d87 100644 --- a/demo/common/src/ui.rs +++ b/demo/common/src/ui.rs @@ -9,10 +9,11 @@ // except according to those terms. use crate::Options; -use nfd::Response; +use crate::window::Window; use pathfinder_geometry::basic::point::Point2DI32; use pathfinder_geometry::basic::rect::RectI32; -use pathfinder_gpu::{Device, Resources}; +use pathfinder_gpu::Device; +use pathfinder_gpu::resources::ResourceLoader; use pathfinder_renderer::gpu::debug::DebugUI; use pathfinder_ui::{BUTTON_HEIGHT, BUTTON_TEXT_OFFSET, BUTTON_WIDTH, FONT_ASCENT, PADDING}; use pathfinder_ui::{SWITCH_SIZE, TEXT_COLOR, TOOLTIP_HEIGHT, WINDOW_COLOR}; @@ -66,7 +67,7 @@ pub struct DemoUI where D: Device { } impl DemoUI where D: Device { - pub fn new(device: &D, resources: &Resources, options: Options) -> DemoUI { + pub fn new(device: &D, resources: &dyn ResourceLoader, options: Options) -> DemoUI { let effects_texture = device.create_texture_from_png(resources, EFFECTS_PNG_NAME); let open_texture = device.create_texture_from_png(resources, OPEN_PNG_NAME); let rotate_texture = device.create_texture_from_png(resources, ROTATE_PNG_NAME); @@ -104,7 +105,12 @@ impl DemoUI where D: Device { (self.rotation as f32 / SLIDER_WIDTH as f32 * 2.0 - 1.0) * PI } - pub fn update(&mut self, device: &D, debug_ui: &mut DebugUI, action: &mut UIAction) { + pub fn update(&mut self, + device: &D, + window: &W, + debug_ui: &mut DebugUI, + action: &mut UIAction) + where W: Window { // Draw message text. self.draw_message_text(device, debug_ui); @@ -132,8 +138,10 @@ impl DemoUI where D: Device { // Draw open button. if debug_ui.ui.draw_button(device, position, &self.open_texture) { - if let Ok(Response::Okay(file)) = nfd::open_file_dialog(Some("svg"), None) { - *action = UIAction::OpenFile(PathBuf::from(file)); + // FIXME(pcwalton): This is not sufficient for Android, where we will need to take in + // the contents of the file. + if let Ok(file) = window.run_open_dialog("svg") { + *action = UIAction::OpenFile(file); } } debug_ui.ui.draw_tooltip(device, "Open SVG", RectI32::new(position, button_size)); @@ -141,8 +149,10 @@ impl DemoUI where D: Device { // Draw screenshot button. if debug_ui.ui.draw_button(device, position, &self.screenshot_texture) { - if let Ok(Response::Okay(file)) = nfd::open_save_dialog(Some("png"), None) { - *action = UIAction::TakeScreenshot(PathBuf::from(file)); + // FIXME(pcwalton): This is not sufficient for Android, where we will need to take in + // the contents of the file. + if let Ok(file) = window.run_save_dialog("png") { + *action = UIAction::TakeScreenshot(file); } } debug_ui.ui.draw_tooltip(device, "Take Screenshot", RectI32::new(position, button_size)); diff --git a/demo/common/src/window.rs b/demo/common/src/window.rs index fb5a5e70..c07725ef 100644 --- a/demo/common/src/window.rs +++ b/demo/common/src/window.rs @@ -11,15 +11,23 @@ //! A minimal cross-platform windowing layer. use pathfinder_geometry::basic::point::Point2DI32; +use pathfinder_gl::GLVersion; +use pathfinder_gpu::resources::ResourceLoader; +use std::io::Error; +use std::path::PathBuf; pub trait Window { fn new(initial_size: Point2DI32) -> Self; + fn gl_version(&self) -> GLVersion; fn size(&self) -> Point2DI32; fn drawable_size(&self) -> Point2DI32; fn mouse_position(&self) -> Point2DI32; fn present(&self); + fn resource_loader(&self) -> &dyn ResourceLoader; fn create_user_event_id(&self) -> u32; fn push_user_event(message_type: u32, message_data: u32); + fn run_open_dialog(&self, extension: &str) -> Result; + fn run_save_dialog(&self, extension: &str) -> Result; } pub enum Event { diff --git a/demo/native/Cargo.toml b/demo/native/Cargo.toml index c31ccacc..24540905 100644 --- a/demo/native/Cargo.toml +++ b/demo/native/Cargo.toml @@ -9,6 +9,7 @@ pf-no-simd = ["pathfinder_simd/pf-no-simd"] [dependencies] gl = "0.6" +nfd = "0.0.4" sdl2 = "0.32" sdl2-sys = "0.32" @@ -18,5 +19,11 @@ path = "../common" [dependencies.pathfinder_geometry] path = "../../geometry" +[dependencies.pathfinder_gl] +path = "../../gl" + +[dependencies.pathfinder_gpu] +path = "../../gpu" + [dependencies.pathfinder_simd] path = "../../simd" diff --git a/demo/native/src/main.rs b/demo/native/src/main.rs index 8f824c94..fc3dea01 100644 --- a/demo/native/src/main.rs +++ b/demo/native/src/main.rs @@ -10,14 +10,18 @@ //! A demo app for Pathfinder using SDL 2. +use nfd::Response; use pathfinder_demo::DemoApp; use pathfinder_demo::window::{Event, Keycode, Window}; use pathfinder_geometry::basic::point::Point2DI32; +use pathfinder_gl::GLVersion; +use pathfinder_gpu::resources::{FilesystemResourceLoader, ResourceLoader}; use sdl2::{EventPump, EventSubsystem, Sdl, VideoSubsystem}; use sdl2::event::{Event as SDLEvent, WindowEvent}; use sdl2::keyboard::Keycode as SDLKeycode; use sdl2::video::{GLContext, GLProfile, Window as SDLWindow}; use sdl2_sys::{SDL_Event, SDL_UserEvent}; +use std::path::PathBuf; use std::ptr; fn main() { @@ -42,6 +46,7 @@ struct WindowImpl { event_pump: EventPump, #[allow(dead_code)] gl_context: GLContext, + resource_loader: FilesystemResourceLoader, } impl Window for WindowImpl { @@ -69,10 +74,16 @@ impl Window for WindowImpl { event_pump = SDL_CONTEXT.with(|sdl_context| sdl_context.event_pump().unwrap()); - WindowImpl { window, event_pump, gl_context } + let resource_loader = FilesystemResourceLoader::locate(); + + WindowImpl { window, event_pump, gl_context, resource_loader } }) } + fn gl_version(&self) -> GLVersion { + GLVersion::GL3 + } + fn size(&self) -> Point2DI32 { let (width, height) = self.window.size(); Point2DI32::new(width as i32, height as i32) @@ -92,6 +103,10 @@ impl Window for WindowImpl { self.window.gl_swap_window(); } + fn resource_loader(&self) -> &dyn ResourceLoader { + &self.resource_loader + } + fn create_user_event_id(&self) -> u32 { SDL_EVENT.with(|sdl_event| unsafe { sdl_event.register_event().unwrap() }) } @@ -109,6 +124,20 @@ impl Window for WindowImpl { sdl2_sys::SDL_PushEvent(&mut user_event as *mut SDL_UserEvent as *mut SDL_Event); } } + + fn run_open_dialog(&self, extension: &str) -> Result { + match nfd::open_file_dialog(Some(extension), None) { + Ok(Response::Okay(file)) => Ok(PathBuf::from(file)), + _ => Err(()), + } + } + + fn run_save_dialog(&self, extension: &str) -> Result { + match nfd::open_save_dialog(Some(extension), None) { + Ok(Response::Okay(file)) => Ok(PathBuf::from(file)), + _ => Err(()), + } + } } impl WindowImpl { diff --git a/gl/Cargo.toml b/gl/Cargo.toml index 8846a96c..ce0ae3e9 100644 --- a/gl/Cargo.toml +++ b/gl/Cargo.toml @@ -6,6 +6,7 @@ authors = ["Patrick Walton "] [dependencies] gl = "0.6" +rustache = "0.1" [dependencies.image] version = "0.21" diff --git a/gl/src/lib.rs b/gl/src/lib.rs index d284c683..10e44d19 100644 --- a/gl/src/lib.rs +++ b/gl/src/lib.rs @@ -10,23 +10,29 @@ //! An OpenGL implementation of the device abstraction. -use gl::types::{GLboolean, GLchar, GLdouble, GLenum, GLfloat, GLint, GLsizei, GLsizeiptr}; -use gl::types::{GLuint, GLvoid}; +use gl::types::{GLboolean, GLchar, GLenum, GLfloat, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid}; use pathfinder_geometry::basic::point::Point2DI32; use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, DepthFunc, Device, Primitive}; use pathfinder_gpu::{RenderState, ShaderKind, StencilFunc, TextureFormat}; use pathfinder_gpu::{UniformData, VertexAttrType}; use pathfinder_simd::default::F32x4; +use rustache::{HashBuilder, Render}; use std::ffi::CString; +use std::io::Cursor; use std::mem; use std::ptr; +use std::str; use std::time::Duration; -pub struct GLDevice; +pub struct GLDevice { + version: GLVersion, +} impl GLDevice { #[inline] - pub fn new() -> GLDevice { GLDevice } + pub fn new(version: GLVersion) -> GLDevice { + GLDevice { version } + } fn set_texture_parameters(&self, texture: &GLTexture) { self.bind_texture(texture, 0); @@ -189,7 +195,7 @@ impl Device for GLDevice { self.bind_texture(&texture, 0); gl::TexImage2D(gl::TEXTURE_2D, 0, - gl::RED as GLint, + gl::R8 as GLint, size.x() as GLsizei, size.y() as GLsizei, 0, @@ -203,6 +209,12 @@ impl Device for GLDevice { } fn create_shader_from_source(&self, name: &str, source: &[u8], kind: ShaderKind) -> GLShader { + let glsl_version_spec = self.version.to_glsl_version_spec(); + let template_input = HashBuilder::new().insert("version", glsl_version_spec); + let mut output = Cursor::new(vec![]); + template_input.render(str::from_utf8(source).unwrap(), &mut output).unwrap(); + let source = output.into_inner(); + let gl_shader_kind = match kind { ShaderKind::Vertex => gl::VERTEX_SHADER, ShaderKind::Fragment => gl::FRAGMENT_SHADER, @@ -361,7 +373,6 @@ impl Device for GLDevice { let mut gl_framebuffer = 0; unsafe { gl::GenFramebuffers(1, &mut gl_framebuffer); ck(); - assert_eq!(gl::GetError(), gl::NO_ERROR); gl::BindFramebuffer(gl::FRAMEBUFFER, gl_framebuffer); ck(); self.bind_texture(&texture, 0); gl::FramebufferTexture2D(gl::FRAMEBUFFER, @@ -469,7 +480,7 @@ impl Device for GLDevice { } if let Some(depth) = depth { gl::DepthMask(gl::TRUE); ck(); - gl::ClearDepth(depth as GLdouble); ck(); + gl::ClearDepthf(depth as _); ck(); // FIXME(pcwalton): GLES flags |= gl::DEPTH_BUFFER_BIT; } if let Some(stencil) = stencil { @@ -797,6 +808,23 @@ impl VertexAttrTypeExt for VertexAttrType { } } +/// The version/dialect of OpenGL we should render with. +pub enum GLVersion { + /// OpenGL 3.0+, core profile. + GL3, + /// OpenGL ES 3.0+. + GLES3, +} + +impl GLVersion { + fn to_glsl_version_spec(&self) -> &'static str { + match *self { + GLVersion::GL3 => "330", + GLVersion::GLES3 => "300 es", + } + } +} + // Error checking #[cfg(debug)] diff --git a/gpu/src/lib.rs b/gpu/src/lib.rs index ad2fe77c..f5112d06 100644 --- a/gpu/src/lib.rs +++ b/gpu/src/lib.rs @@ -10,6 +10,8 @@ //! Minimal abstractions over GPU device capabilities. +use crate::resources::ResourceLoader; +use image::ImageFormat; use pathfinder_geometry::basic::point::Point2DI32; use pathfinder_simd::default::F32x4; use std::env; @@ -18,6 +20,8 @@ use std::io::Read; use std::path::PathBuf; use std::time::Duration; +pub mod resources; + pub trait Device { type Buffer; type Framebuffer; @@ -91,28 +95,22 @@ pub trait Device { fn bind_framebuffer(&self, framebuffer: &Self::Framebuffer); fn bind_texture(&self, texture: &Self::Texture, unit: u32); - fn create_texture_from_png(&self, resources: &Resources, name: &str) -> Self::Texture { - let mut path = resources.resources_directory.clone(); - path.push("textures"); - path.push(format!("{}.png", name)); - - let image = image::open(&path).unwrap().to_luma(); + fn create_texture_from_png(&self, resources: &dyn ResourceLoader, name: &str) + -> Self::Texture { + let data = resources.slurp(&format!("textures/{}.png", name)).unwrap(); + let image = image::load_from_memory_with_format(&data, ImageFormat::PNG).unwrap().to_luma(); let size = Point2DI32::new(image.width() as i32, image.height() as i32); self.create_texture_from_data(size, &image) } - fn create_shader(&self, resources: &Resources, name: &str, kind: ShaderKind) -> Self::Shader { + fn create_shader(&self, resources: &dyn ResourceLoader, name: &str, kind: ShaderKind) + -> Self::Shader { let suffix = match kind { ShaderKind::Vertex => 'v', ShaderKind::Fragment => 'f' }; - let mut path = resources.resources_directory.clone(); - path.push("shaders"); - path.push(format!("{}.{}s.glsl", name, suffix)); - - let mut source = vec![]; - File::open(&path).unwrap().read_to_end(&mut source).unwrap(); + let source = resources.slurp(&format!("shaders/{}.{}s.glsl", name, suffix)).unwrap(); self.create_shader_from_source(name, &source, kind) } - fn create_program(&self, resources: &Resources, name: &str) -> Self::Program { + fn create_program(&self, resources: &dyn ResourceLoader, name: &str) -> Self::Program { let vertex_shader = self.create_shader(resources, name, ShaderKind::Vertex); let fragment_shader = self.create_shader(resources, name, ShaderKind::Fragment); self.create_program_from_shaders(name, vertex_shader, fragment_shader) @@ -243,33 +241,3 @@ impl Default for StencilFunc { StencilFunc::Always } } - -pub struct Resources { - pub resources_directory: PathBuf, -} - -impl Resources { - pub fn locate() -> Resources { - let mut parent_directory = env::current_dir().unwrap(); - loop { - // So ugly :( - let mut resources_directory = parent_directory.clone(); - resources_directory.push("resources"); - if resources_directory.is_dir() { - let mut shaders_directory = resources_directory.clone(); - let mut textures_directory = resources_directory.clone(); - shaders_directory.push("shaders"); - textures_directory.push("textures"); - if shaders_directory.is_dir() && textures_directory.is_dir() { - return Resources { resources_directory }; - } - } - - if !parent_directory.pop() { - break; - } - } - - panic!("No suitable `resources/` directory found!"); - } -} diff --git a/gpu/src/resources.rs b/gpu/src/resources.rs new file mode 100644 index 00000000..fe7ceb29 --- /dev/null +++ b/gpu/src/resources.rs @@ -0,0 +1,70 @@ +// pathfinder/gpu/src/resources.rs +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! An abstraction for reading resources. +//! +//! We can't always count on a filesystem being present. + +use crate::ShaderKind; +use std::env; +use std::fs::File; +use std::io::{Error as IOError, Read}; +use std::path::PathBuf; + +pub trait ResourceLoader { + /// This is deliberately not a `Path`, because these are virtual paths + /// that do not necessarily correspond to real paths on a filesystem. + fn slurp(&self, path: &str) -> Result, IOError>; +} + +pub struct FilesystemResourceLoader { + pub directory: PathBuf, +} + +impl FilesystemResourceLoader { + pub fn new(root: PathBuf) -> FilesystemResourceLoader { + let mut parent_directory = env::current_dir().unwrap(); + loop { + // So ugly :( + let mut resources_directory = parent_directory.clone(); + resources_directory.push("resources"); + if resources_directory.is_dir() { + let mut shaders_directory = resources_directory.clone(); + let mut textures_directory = resources_directory.clone(); + shaders_directory.push("shaders"); + textures_directory.push("textures"); + if shaders_directory.is_dir() && textures_directory.is_dir() { + return FilesystemResourceLoader { directory: resources_directory }; + } + } + + if !parent_directory.pop() { + break; + } + } + + panic!("No suitable `resources/` directory found!"); + } + + pub fn locate() -> FilesystemResourceLoader { + FilesystemResourceLoader::new(env::current_dir().unwrap()) + } +} + +impl ResourceLoader for FilesystemResourceLoader { + fn slurp(&self, virtual_path: &str) -> Result, IOError> { + let mut path = self.directory.clone(); + virtual_path.split('/').for_each(|segment| path.push(segment)); + + let mut data = vec![]; + File::open(&path)?.read_to_end(&mut data)?; + Ok(data) + } +} diff --git a/renderer/src/gpu/debug.rs b/renderer/src/gpu/debug.rs index 924fa342..4e3b274a 100644 --- a/renderer/src/gpu/debug.rs +++ b/renderer/src/gpu/debug.rs @@ -18,7 +18,8 @@ use crate::gpu_data::Stats; use pathfinder_geometry::basic::point::Point2DI32; use pathfinder_geometry::basic::rect::RectI32; -use pathfinder_gpu::{Device, Resources}; +use pathfinder_gpu::Device; +use pathfinder_gpu::resources::ResourceLoader; use pathfinder_ui::{FONT_ASCENT, LINE_HEIGHT, PADDING, UI, WINDOW_COLOR}; use std::collections::VecDeque; use std::ops::{Add, Div}; @@ -37,7 +38,8 @@ pub struct DebugUI where D: Device { } impl DebugUI where D: Device { - pub fn new(device: &D, resources: &Resources, framebuffer_size: Point2DI32) -> DebugUI { + pub fn new(device: &D, resources: &dyn ResourceLoader, framebuffer_size: Point2DI32) + -> DebugUI { let ui = UI::new(device, resources, framebuffer_size); DebugUI { ui, cpu_samples: SampleBuffer::new(), gpu_samples: SampleBuffer::new() } } diff --git a/renderer/src/gpu/renderer.rs b/renderer/src/gpu/renderer.rs index af39a007..c4548d75 100644 --- a/renderer/src/gpu/renderer.rs +++ b/renderer/src/gpu/renderer.rs @@ -15,8 +15,9 @@ use crate::scene::ObjectShader; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use pathfinder_geometry::basic::point::{Point2DI32, Point3DF32}; use pathfinder_geometry::color::ColorU; +use pathfinder_gpu::resources::ResourceLoader; use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, DepthFunc, DepthState, Device}; -use pathfinder_gpu::{Primitive, RenderState, Resources, StencilFunc, StencilState, TextureFormat}; +use pathfinder_gpu::{Primitive, RenderState, StencilFunc, StencilState, TextureFormat}; use pathfinder_gpu::{UniformData, VertexAttrType}; use pathfinder_simd::default::{F32x4, I32x4}; use std::collections::VecDeque; @@ -73,17 +74,17 @@ pub struct Renderer where D: Device { } impl Renderer where D: Device { - pub fn new(device: D, resources: &Resources, main_framebuffer_size: Point2DI32) + pub fn new(device: D, resources: &dyn ResourceLoader, main_framebuffer_size: Point2DI32) -> Renderer { - let fill_program = FillProgram::new(&device, &resources); - let solid_tile_program = SolidTileProgram::new(&device, &resources); - let mask_tile_program = MaskTileProgram::new(&device, &resources); + let fill_program = FillProgram::new(&device, resources); + let solid_tile_program = SolidTileProgram::new(&device, resources); + let mask_tile_program = MaskTileProgram::new(&device, resources); - let postprocess_program = PostprocessProgram::new(&device, &resources); - let stencil_program = StencilProgram::new(&device, &resources); + let postprocess_program = PostprocessProgram::new(&device, resources); + let stencil_program = StencilProgram::new(&device, resources); - let area_lut_texture = device.create_texture_from_png(&resources, "area-lut"); - let gamma_lut_texture = device.create_texture_from_png(&resources, "gamma-lut"); + let area_lut_texture = device.create_texture_from_png(resources, "area-lut"); + let gamma_lut_texture = device.create_texture_from_png(resources, "gamma-lut"); let quad_vertex_positions_buffer = device.create_buffer(); device.upload_to_buffer(&quad_vertex_positions_buffer, @@ -115,7 +116,7 @@ impl Renderer where D: Device { FILL_COLORS_TEXTURE_HEIGHT); let fill_colors_texture = device.create_texture(TextureFormat::RGBA8, fill_colors_size); - let debug_ui = DebugUI::new(&device, &resources, main_framebuffer_size); + let debug_ui = DebugUI::new(&device, resources, main_framebuffer_size); Renderer { device, @@ -653,7 +654,7 @@ struct FillProgram where D: Device { } impl FillProgram where D: Device { - fn new(device: &D, resources: &Resources) -> FillProgram { + fn new(device: &D, resources: &dyn ResourceLoader) -> FillProgram { let program = device.create_program(resources, "fill"); let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); let tile_size_uniform = device.get_uniform(&program, "TileSize"); @@ -672,7 +673,7 @@ struct SolidTileProgram where D: Device { } impl SolidTileProgram where D: Device { - fn new(device: &D, resources: &Resources) -> SolidTileProgram { + fn new(device: &D, resources: &dyn ResourceLoader) -> SolidTileProgram { let program = device.create_program(resources, "solid_tile"); let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); let tile_size_uniform = device.get_uniform(&program, "TileSize"); @@ -703,7 +704,7 @@ struct MaskTileProgram where D: Device { } impl MaskTileProgram where D: Device { - fn new(device: &D, resources: &Resources) -> MaskTileProgram { + fn new(device: &D, resources: &dyn ResourceLoader) -> MaskTileProgram { let program = device.create_program(resources, "mask_tile"); let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); let tile_size_uniform = device.get_uniform(&program, "TileSize"); @@ -736,7 +737,7 @@ struct PostprocessProgram where D: Device { } impl PostprocessProgram where D: Device { - fn new(device: &D, resources: &Resources) -> PostprocessProgram { + fn new(device: &D, resources: &dyn ResourceLoader) -> PostprocessProgram { let program = device.create_program(resources, "post"); let source_uniform = device.get_uniform(&program, "Source"); let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); @@ -787,7 +788,7 @@ struct StencilProgram where D: Device { } impl StencilProgram where D: Device { - fn new(device: &D, resources: &Resources) -> StencilProgram { + fn new(device: &D, resources: &dyn ResourceLoader) -> StencilProgram { let program = device.create_program(resources, "stencil"); StencilProgram { program } } diff --git a/resources/debug-font.json b/resources/debug-fonts/regular.json similarity index 100% rename from resources/debug-font.json rename to resources/debug-fonts/regular.json diff --git a/resources/shaders/debug_solid.fs.glsl b/resources/shaders/debug_solid.fs.glsl index 54fad3ca..a50236a7 100644 --- a/resources/shaders/debug_solid.fs.glsl +++ b/resources/shaders/debug_solid.fs.glsl @@ -1,4 +1,4 @@ -#version 330 +#version {{version}} // pathfinder/demo/shaders/debug_solid.fs.glsl // diff --git a/resources/shaders/debug_solid.vs.glsl b/resources/shaders/debug_solid.vs.glsl index 33b57de1..47b1f0bb 100644 --- a/resources/shaders/debug_solid.vs.glsl +++ b/resources/shaders/debug_solid.vs.glsl @@ -1,4 +1,4 @@ -#version 330 +#version {{version}} // pathfinder/demo/shaders/debug_solid.vs.glsl // diff --git a/resources/shaders/debug_texture.fs.glsl b/resources/shaders/debug_texture.fs.glsl index 944ff8ab..0095d1ca 100644 --- a/resources/shaders/debug_texture.fs.glsl +++ b/resources/shaders/debug_texture.fs.glsl @@ -1,4 +1,4 @@ -#version 330 +#version {{version}} // pathfinder/demo/shaders/debug_texture.fs.glsl // diff --git a/resources/shaders/debug_texture.vs.glsl b/resources/shaders/debug_texture.vs.glsl index c2a3e4c8..beed30cf 100644 --- a/resources/shaders/debug_texture.vs.glsl +++ b/resources/shaders/debug_texture.vs.glsl @@ -1,4 +1,4 @@ -#version 330 +#version {{version}} // pathfinder/demo/shaders/debug_texture.vs.glsl // diff --git a/resources/shaders/demo_ground.fs.glsl b/resources/shaders/demo_ground.fs.glsl index 04f056bb..5be6578d 100644 --- a/resources/shaders/demo_ground.fs.glsl +++ b/resources/shaders/demo_ground.fs.glsl @@ -1,4 +1,4 @@ -#version 330 +#version {{version}} // pathfinder/demo/resources/shaders/demo_ground.fs.glsl // diff --git a/resources/shaders/demo_ground.vs.glsl b/resources/shaders/demo_ground.vs.glsl index 5cd5bf4a..b50bd65c 100644 --- a/resources/shaders/demo_ground.vs.glsl +++ b/resources/shaders/demo_ground.vs.glsl @@ -1,4 +1,4 @@ -#version 330 +#version {{version}} // pathfinder/demo/resources/shaders/demo_ground.vs.glsl // diff --git a/resources/shaders/fill.fs.glsl b/resources/shaders/fill.fs.glsl index 0cb0ab7f..1874a60c 100644 --- a/resources/shaders/fill.fs.glsl +++ b/resources/shaders/fill.fs.glsl @@ -1,4 +1,4 @@ -#version 330 +#version {{version}} // pathfinder/demo2/stencil.fs.glsl // diff --git a/resources/shaders/fill.vs.glsl b/resources/shaders/fill.vs.glsl index c5a77496..c8a2aacb 100644 --- a/resources/shaders/fill.vs.glsl +++ b/resources/shaders/fill.vs.glsl @@ -1,4 +1,4 @@ -#version 330 +#version {{version}} // pathfinder/demo/fill.vs.glsl // diff --git a/resources/shaders/mask_tile.fs.glsl b/resources/shaders/mask_tile.fs.glsl index 4795a3b1..0a24afd6 100644 --- a/resources/shaders/mask_tile.fs.glsl +++ b/resources/shaders/mask_tile.fs.glsl @@ -1,4 +1,4 @@ -#version 330 +#version {{version}} // pathfinder/demo/resources/shaders/mask_tile.fs.glsl // diff --git a/resources/shaders/mask_tile.vs.glsl b/resources/shaders/mask_tile.vs.glsl index 21b60485..c0486f1f 100644 --- a/resources/shaders/mask_tile.vs.glsl +++ b/resources/shaders/mask_tile.vs.glsl @@ -1,4 +1,4 @@ -#version 330 +#version {{version}} // pathfinder/demo/shaders/mask_tile.vs.glsl // diff --git a/resources/shaders/post.fs.glsl b/resources/shaders/post.fs.glsl index abe49322..d645c1d0 100644 --- a/resources/shaders/post.fs.glsl +++ b/resources/shaders/post.fs.glsl @@ -1,4 +1,4 @@ -#version 330 +#version {{version}} // pathfinder/demo/shaders/post.fs.glsl // diff --git a/resources/shaders/post.vs.glsl b/resources/shaders/post.vs.glsl index 529cfe88..9d4b0adf 100644 --- a/resources/shaders/post.vs.glsl +++ b/resources/shaders/post.vs.glsl @@ -1,4 +1,4 @@ -#version 330 +#version {{version}} // pathfinder/demo/shaders/post.vs.glsl // diff --git a/resources/shaders/solid_tile.fs.glsl b/resources/shaders/solid_tile.fs.glsl index 9db70a17..129c6813 100644 --- a/resources/shaders/solid_tile.fs.glsl +++ b/resources/shaders/solid_tile.fs.glsl @@ -1,4 +1,4 @@ -#version 330 +#version {{version}} // pathfinder/demo2/opaque.fs.glsl // diff --git a/resources/shaders/solid_tile.vs.glsl b/resources/shaders/solid_tile.vs.glsl index f1b536f0..e9105322 100644 --- a/resources/shaders/solid_tile.vs.glsl +++ b/resources/shaders/solid_tile.vs.glsl @@ -1,4 +1,4 @@ -#version 330 +#version {{version}} // pathfinder/demo/resources/shaders/solid_tile.vs.glsl // diff --git a/resources/shaders/stencil.fs.glsl b/resources/shaders/stencil.fs.glsl index ddf87a8d..8781cbaa 100644 --- a/resources/shaders/stencil.fs.glsl +++ b/resources/shaders/stencil.fs.glsl @@ -1,4 +1,4 @@ -#version 330 +#version {{version}} // pathfinder/resources/shaders/stencil.fs.glsl // diff --git a/resources/shaders/stencil.vs.glsl b/resources/shaders/stencil.vs.glsl index 01938788..1fcdc3b3 100644 --- a/resources/shaders/stencil.vs.glsl +++ b/resources/shaders/stencil.vs.glsl @@ -1,4 +1,4 @@ -#version 330 +#version {{version}} // pathfinder/resources/shaders/stencil.vs.glsl // diff --git a/simd/src/lib.rs b/simd/src/lib.rs index cfae3985..d7291bf8 100644 --- a/simd/src/lib.rs +++ b/simd/src/lib.rs @@ -16,6 +16,7 @@ pub use crate::scalar as default; pub use crate::x86 as default; pub mod scalar; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub mod x86; mod extras; diff --git a/ui/src/lib.rs b/ui/src/lib.rs index 6c711d2c..694e354c 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -20,8 +20,9 @@ use hashbrown::HashMap; use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32}; use pathfinder_geometry::basic::rect::RectI32; use pathfinder_geometry::color::ColorU; +use pathfinder_gpu::resources::ResourceLoader; use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, Device, Primitive, RenderState}; -use pathfinder_gpu::{Resources, UniformData, VertexAttrType}; +use pathfinder_gpu::{UniformData, VertexAttrType}; use pathfinder_simd::default::F32x4; use serde_json; use std::fs::File; @@ -55,7 +56,7 @@ static OUTLINE_COLOR: ColorU = ColorU { r: 255, g: 255, b: 255, a: 192 }; static INVERTED_TEXT_COLOR: ColorU = ColorU { r: 0, g: 0, b: 0, a: 255 }; -static FONT_JSON_FILENAME: &'static str = "debug-font.json"; +static FONT_JSON_VIRTUAL_PATH: &'static str = "debug-fonts/regular.json"; static FONT_PNG_NAME: &'static str = "debug-font"; static CORNER_FILL_PNG_NAME: &'static str = "debug-corner-fill"; @@ -83,7 +84,7 @@ pub struct UI where D: Device { } impl UI where D: Device { - pub fn new(device: &D, resources: &Resources, framebuffer_size: Point2DI32) -> UI { + pub fn new(device: &D, resources: &dyn ResourceLoader, framebuffer_size: Point2DI32) -> UI { let texture_program = DebugTextureProgram::new(device, resources); let texture_vertex_array = DebugTextureVertexArray::new(device, &texture_program); let font = DebugFont::load(resources); @@ -509,7 +510,7 @@ struct DebugTextureProgram where D: Device { } impl DebugTextureProgram where D: Device { - fn new(device: &D, resources: &Resources) -> DebugTextureProgram { + fn new(device: &D, resources: &dyn ResourceLoader) -> DebugTextureProgram { let program = device.create_program(resources, "debug_texture"); let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); let texture_size_uniform = device.get_uniform(&program, "TextureSize"); @@ -598,7 +599,7 @@ struct DebugSolidProgram where D: Device { } impl DebugSolidProgram where D: Device { - fn new(device: &D, resources: &Resources) -> DebugSolidProgram { + fn new(device: &D, resources: &dyn ResourceLoader) -> DebugSolidProgram { let program = device.create_program(resources, "debug_solid"); let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); let color_uniform = device.get_uniform(&program, "Color"); @@ -727,10 +728,8 @@ struct DebugCharacter { } impl DebugFont { - fn load(resources: &Resources) -> DebugFont { - let mut path = resources.resources_directory.clone(); - path.push(FONT_JSON_FILENAME); - - serde_json::from_reader(BufReader::new(File::open(path).unwrap())).unwrap() + #[inline] + fn load(resources: &dyn ResourceLoader) -> DebugFont { + serde_json::from_slice(&resources.slurp(FONT_JSON_VIRTUAL_PATH).unwrap()).unwrap() } }