Allow opening new SVGs in the Android port
This commit is contained in:
parent
49660a6b31
commit
9025189650
|
@ -4,7 +4,7 @@ android {
|
|||
compileSdkVersion 27
|
||||
defaultConfig {
|
||||
applicationId "graphics.pathfinder.pathfinderdemo"
|
||||
minSdkVersion 21
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 27
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
@ -22,6 +22,7 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||
compile 'com.google.vr:sdk-base:1.160.0'
|
||||
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@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());
|
||||
}
|
||||
}
|
|
@ -2,6 +2,15 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="graphics.pathfinder.pathfinderdemo">
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.software.vr.mode"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.vr.high_performance"
|
||||
android:required="false" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
|
@ -10,41 +19,49 @@
|
|||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name=".PathfinderActivity"
|
||||
android:name=".PathfinderDemoActivity"
|
||||
android:configChanges="density|navigation|orientation|keyboardHidden|screenSize|uiMode"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/VrActivityTheme"
|
||||
android:resizeableActivity="false">
|
||||
android:resizeableActivity="false"
|
||||
android:theme="@style/AppTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
||||
<!-- This marks the Activity as a Daydream Activity and allows it
|
||||
to be launched from the Daydream Home. -->
|
||||
<!--
|
||||
This marks the Activity as a Daydream Activity and allows it
|
||||
to be launched from the Daydream Home.
|
||||
-->
|
||||
<category android:name="com.google.intent.category.DAYDREAM" />
|
||||
|
||||
<!-- This marks the Activity as a Cardboard Activity and allows it
|
||||
to be launched from the Cardboard app. -->
|
||||
<!--
|
||||
This marks the Activity as a Cardboard Activity and allows it
|
||||
to be launched from the Cardboard app.
|
||||
-->
|
||||
<category android:name="com.google.intent.category.CARDBOARD" />
|
||||
|
||||
<!-- This allows this Activity to be launched from the traditional
|
||||
<!--
|
||||
This allows this Activity to be launched from the traditional
|
||||
Android 2D launcher as well. Remove it if you do not want
|
||||
this Activity to be launched directly from the 2D launcher. -->
|
||||
this Activity to be launched directly from the 2D launcher.
|
||||
-->
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<service android:name=".PathfinderDemoVRListenerService"
|
||||
|
||||
<service
|
||||
android:name=".PathfinderDemoVRListenerService"
|
||||
android:label="@string/service_name"
|
||||
android:permission="android.permission.BIND_VR_LISTENER_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.vr.VrListenerService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<activity
|
||||
android:name=".PathfinderDemoFileBrowserActivity"
|
||||
android:label="@string/title_activity_pathfinder_demo_file_browser"></activity>
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-feature android:name="android.software.vr.mode" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.vr.high_performance" android:required="false"/>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1 @@
|
|||
../../../../../../resources
|
|
@ -18,17 +18,14 @@ import android.support.annotation.RequiresApi;
|
|||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import com.google.vr.cardboard.AndroidNCompat;
|
||||
|
||||
/**
|
||||
* 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 Activity {
|
||||
public class PathfinderDemoActivity extends Activity {
|
||||
private PathfinderDemoRenderer mRenderer;
|
||||
|
||||
/**
|
||||
|
@ -83,32 +80,20 @@ public class PathfinderActivity extends Activity {
|
|||
mContentView.setStereoModeEnabled(false);
|
||||
setVRMode(false);
|
||||
|
||||
/*
|
||||
// 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);
|
||||
mRenderer = new PathfinderDemoRenderer(this);
|
||||
mContentView.setRenderer(mRenderer);
|
||||
|
||||
mContentView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent event) {
|
||||
int x = Math.round(event.getX());
|
||||
int y = Math.round(event.getY());
|
||||
public boolean onTouch(final View view, final MotionEvent event) {
|
||||
final int x = Math.round(event.getX());
|
||||
final int y = Math.round(event.getY());
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
Log.i("Pathfinder", "DOWN " + x + " " + y);
|
||||
PathfinderDemoRenderer.pushMouseDownEvent(x, y);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
Log.i("Pathfinder", "MOVE " + x + " " + y);
|
||||
PathfinderDemoRenderer.pushMouseDraggedEvent(x, y);
|
||||
break;
|
||||
}
|
||||
|
@ -127,13 +112,13 @@ public class PathfinderActivity extends Activity {
|
|||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
// https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Quaternion_to_Euler_Angles_Conversion
|
||||
float[] q = event.values;
|
||||
float pitch = (float)Math.asin(2.0 * (q[0] * q[2] - q[3] * q[1]));
|
||||
float yaw = (float)Math.atan2(2.0 * (q[0] * q[3] + q[1] * q[2]),
|
||||
final float[] q = event.values;
|
||||
final float pitch = (float)Math.asin(2.0 * (q[0] * q[2] - q[3] * q[1]));
|
||||
final float yaw = (float)Math.atan2(2.0 * (q[0] * q[3] + q[1] * q[2]),
|
||||
1.0 - 2.0 * (q[2] * q[2] + q[3] * q[3]));
|
||||
|
||||
float deltaPitch = pitch - mPitch;
|
||||
float deltaYaw = yaw - mYaw;
|
||||
final float deltaPitch = pitch - mPitch;
|
||||
final float deltaYaw = yaw - mYaw;
|
||||
|
||||
mPitch = pitch;
|
||||
mYaw = yaw;
|
||||
|
@ -157,4 +142,9 @@ public class PathfinderActivity extends Activity {
|
|||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
public void presentOpenSVGDialog() {
|
||||
final Intent intent = new Intent(this, PathfinderDemoFileBrowserActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package graphics.pathfinder.pathfinderdemo;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Bundle;
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class PathfinderDemoFileBrowserActivity extends Activity {
|
||||
private ListView mBrowserView;
|
||||
|
||||
private static String SVG_RESOURCE_PATH = "svg/";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_pathfinder_demo_file_browser);
|
||||
|
||||
mBrowserView = findViewById(R.id.fileBrowserBrowser);
|
||||
|
||||
try {
|
||||
final AssetManager assetManager = getAssets();
|
||||
final String[] svgFilenames = assetManager.list("resources/" + SVG_RESOURCE_PATH);
|
||||
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(
|
||||
this,
|
||||
R.layout.layout_pathfinder_demo_file_browser_list_item,
|
||||
svgFilenames);
|
||||
mBrowserView.setAdapter(adapter);
|
||||
} catch (IOException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
|
||||
mBrowserView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
TextView textView = (TextView)view;
|
||||
PathfinderDemoRenderer.pushOpenSVGEvent(SVG_RESOURCE_PATH + textView.getText());
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
package graphics.pathfinder.pathfinderdemo;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.RequiresApi;
|
||||
|
||||
import com.google.vr.sdk.base.Eye;
|
||||
import com.google.vr.sdk.base.GvrView;
|
||||
|
@ -11,11 +10,12 @@ import com.google.vr.sdk.base.Viewport;
|
|||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
|
||||
public class PathfinderDemoRenderer extends Object implements GvrView.Renderer {
|
||||
private PathfinderActivity mActivity;
|
||||
private final PathfinderDemoActivity mActivity;
|
||||
private boolean mInitialized;
|
||||
private boolean mInVRMode;
|
||||
|
||||
private static native void init(PathfinderDemoResourceLoader resourceLoader,
|
||||
private static native void init(PathfinderDemoActivity activity,
|
||||
PathfinderDemoResourceLoader resourceLoader,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
|
@ -33,20 +33,21 @@ public class PathfinderDemoRenderer extends Object implements GvrView.Renderer {
|
|||
|
||||
public static native void pushLookEvent(float pitch, float yaw);
|
||||
|
||||
public static native void pushOpenSVGEvent(String path);
|
||||
|
||||
static {
|
||||
System.loadLibrary("pathfinder_android_demo");
|
||||
}
|
||||
|
||||
PathfinderDemoRenderer(PathfinderActivity activity) {
|
||||
PathfinderDemoRenderer(PathfinderDemoActivity activity) {
|
||||
super();
|
||||
mActivity = activity;
|
||||
mInitialized = false;
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@Override
|
||||
public void onDrawFrame(HeadTransform headTransform, Eye leftEye, Eye rightEye) {
|
||||
boolean inVR = prepareFrame() > 1;
|
||||
final boolean inVR = prepareFrame() > 1;
|
||||
if (inVR != mInVRMode) {
|
||||
mInVRMode = inVR;
|
||||
try {
|
||||
|
@ -69,7 +70,10 @@ public class PathfinderDemoRenderer extends Object implements GvrView.Renderer {
|
|||
@Override
|
||||
public void onSurfaceChanged(int width, int height) {
|
||||
if (!mInitialized) {
|
||||
init(new PathfinderDemoResourceLoader(mActivity.getAssets()), width, height);
|
||||
init(mActivity,
|
||||
new PathfinderDemoResourceLoader(mActivity.getAssets()),
|
||||
width,
|
||||
height);
|
||||
mInitialized = true;
|
||||
} else {
|
||||
pushWindowResizedEvent(width, height);
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package graphics.pathfinder.pathfinderdemo;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class PathfinderDemoResourceLoader {
|
||||
private AssetManager m_assetManager;
|
||||
|
||||
PathfinderDemoResourceLoader(AssetManager assetManager) {
|
||||
m_assetManager = assetManager;
|
||||
}
|
||||
|
||||
ByteBuffer slurp(String path) {
|
||||
try {
|
||||
InputStream inputStream = m_assetManager.open("resources/" + path);
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
while (true) {
|
||||
int nRead = inputStream.read(buffer, 0, buffer.length);
|
||||
if (nRead == -1)
|
||||
break;
|
||||
outputStream.write(buffer, 0, nRead);
|
||||
}
|
||||
|
||||
byte[] outputBytes = outputStream.toByteArray();
|
||||
ByteBuffer resultBuffer = ByteBuffer.allocateDirect(outputStream.size());
|
||||
resultBuffer.put(outputBytes);
|
||||
return resultBuffer;
|
||||
} catch (IOException exception) {
|
||||
Log.e("Pathfinder", "Resource not found: " + path);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,7 @@
|
|||
package graphics.pathfinder.pathfinderdemo;
|
||||
|
||||
import android.content.Context;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import com.google.vr.sdk.base.GvrView;
|
||||
|
||||
public class PathfinderDemoSurfaceView extends GvrView {
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package graphics.pathfinder.pathfinderdemo;
|
||||
|
||||
import android.os.Build;
|
||||
import android.service.vr.VrListenerService;
|
||||
import android.support.annotation.RequiresApi;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public class PathfinderDemoVRListenerService extends VrListenerService {
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
../../../../../../../target/aarch64-linux-android/release/libpathfinder_android_demo.so
|
|
@ -4,7 +4,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#0099cc"
|
||||
tools:context=".PathfinderActivity">
|
||||
tools:context=".PathfinderDemoActivity">
|
||||
|
||||
<!-- The primary full-screen view. This can be replaced with whatever view
|
||||
is needed to present your content, e.g. VideoView, SurfaceView,
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".PathfinderDemoFileBrowserActivity">
|
||||
|
||||
<ListView
|
||||
android:id="@+id/fileBrowserBrowser"
|
||||
style="@android:style/Widget.Material.ListView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
</ListView>
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:lineSpacingExtra="14sp"
|
||||
android:textAppearance="@android:style/TextAppearance"
|
||||
android:textSize="18sp">
|
||||
|
||||
</TextView>
|
|
@ -4,4 +4,6 @@
|
|||
<string name="dummy_button">Dummy Button</string>
|
||||
<string name="dummy_content">DUMMY\nCONTENT</string>
|
||||
<string name="service_name">PathfinderVRListenerService</string>
|
||||
<string name="title_activity_pathfinder_demo_file_browser">PathfinderDemoFileBrowserActivity
|
||||
</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
|
@ -12,9 +12,9 @@
|
|||
extern crate lazy_static;
|
||||
|
||||
use jni::{JNIEnv, JavaVM};
|
||||
use jni::objects::{GlobalRef, JByteBuffer, JClass, JObject, JValue};
|
||||
use jni::objects::{GlobalRef, JByteBuffer, JClass, JObject, JString, JValue};
|
||||
use pathfinder_demo::DemoApp;
|
||||
use pathfinder_demo::window::{Event, Window, WindowSize};
|
||||
use pathfinder_demo::window::{Event, SVGPath, Window, WindowSize};
|
||||
use pathfinder_geometry::basic::point::Point2DI32;
|
||||
use pathfinder_gl::GLVersion;
|
||||
use pathfinder_gpu::resources::ResourceLoader;
|
||||
|
@ -31,6 +31,7 @@ lazy_static! {
|
|||
|
||||
thread_local! {
|
||||
static DEMO_APP: RefCell<Option<DemoApp<WindowImpl>>> = RefCell::new(None);
|
||||
static JAVA_ACTIVITY: RefCell<Option<JavaActivity>> = RefCell::new(None);
|
||||
static JAVA_RESOURCE_LOADER: RefCell<Option<JavaResourceLoader>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
|
@ -40,12 +41,16 @@ static RESOURCE_LOADER: AndroidResourceLoader = AndroidResourceLoader;
|
|||
pub unsafe extern "system" fn
|
||||
Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_init(env: JNIEnv,
|
||||
class: JClass,
|
||||
activity: JObject,
|
||||
loader: JObject,
|
||||
width: i32,
|
||||
height: i32) {
|
||||
let logical_size = Point2DI32::new(width, height);
|
||||
let window_size = WindowSize { logical_size, backing_scale_factor: 1.0 };
|
||||
|
||||
JAVA_ACTIVITY.with(|java_activity| {
|
||||
*java_activity.borrow_mut() = Some(JavaActivity::new(env.clone(), activity));
|
||||
});
|
||||
JAVA_RESOURCE_LOADER.with(|java_resource_loader| {
|
||||
*java_resource_loader.borrow_mut() = Some(JavaResourceLoader::new(env, loader));
|
||||
});
|
||||
|
@ -139,6 +144,16 @@ pub unsafe extern "system" fn
|
|||
EVENT_QUEUE.lock().unwrap().push(Event::Look { pitch, yaw })
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "system" fn
|
||||
Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_pushOpenSVGEvent(
|
||||
env: JNIEnv,
|
||||
_: JClass,
|
||||
string: JObject) {
|
||||
let string: String = env.get_string(JString::from(string)).unwrap().into();
|
||||
EVENT_QUEUE.lock().unwrap().push(Event::OpenSVG(SVGPath::Resource(string)))
|
||||
}
|
||||
|
||||
struct WindowImpl;
|
||||
|
||||
impl Window for WindowImpl {
|
||||
|
@ -163,9 +178,16 @@ impl Window for WindowImpl {
|
|||
fn push_user_event(message_type: u32, message_data: u32) {
|
||||
}
|
||||
|
||||
fn run_open_dialog(&self, extension: &str) -> Result<PathBuf, ()> {
|
||||
// TODO(pcwalton)
|
||||
Err(())
|
||||
fn present_open_svg_dialog(&mut self) {
|
||||
JAVA_ACTIVITY.with(|java_activity| {
|
||||
let mut java_activity = java_activity.borrow_mut();
|
||||
let java_activity = java_activity.as_mut().unwrap();
|
||||
let env = java_activity.vm.get_env().unwrap();
|
||||
env.call_method(java_activity.activity.as_obj(),
|
||||
"presentOpenSVGDialog",
|
||||
"()V",
|
||||
&[]).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
fn run_save_dialog(&self, extension: &str) -> Result<PathBuf, ()> {
|
||||
|
@ -197,6 +219,20 @@ impl ResourceLoader for AndroidResourceLoader {
|
|||
}
|
||||
}
|
||||
|
||||
struct JavaActivity {
|
||||
activity: GlobalRef,
|
||||
vm: JavaVM,
|
||||
}
|
||||
|
||||
impl JavaActivity {
|
||||
fn new(env: JNIEnv, activity: JObject) -> JavaActivity {
|
||||
JavaActivity {
|
||||
activity: env.new_global_ref(activity).unwrap(),
|
||||
vm: env.get_java_vm().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct JavaResourceLoader {
|
||||
loader: GlobalRef,
|
||||
vm: JavaVM,
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
use crate::device::{GroundLineVertexArray, GroundProgram, GroundSolidVertexArray};
|
||||
use crate::ui::{DemoUI, UIAction};
|
||||
use crate::window::{Event, Keycode, Window, WindowSize};
|
||||
use crate::window::{Event, Keycode, SVGPath, Window, WindowSize};
|
||||
use clap::{App, Arg};
|
||||
use image::ColorType;
|
||||
use jemallocator;
|
||||
|
@ -328,6 +328,23 @@ impl<W> DemoApp<W> where W: Window {
|
|||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
Event::OpenSVG(ref svg_path) => {
|
||||
let built_svg = load_scene(self.window.resource_loader(), svg_path);
|
||||
self.ui.message = get_svg_building_message(&built_svg);
|
||||
|
||||
let view_box_size = view_box_size(self.ui.mode, &self.window_size);
|
||||
self.scene_view_box = built_svg.scene.view_box;
|
||||
self.scene_is_monochrome = built_svg.scene.is_monochrome();
|
||||
|
||||
self.camera = if self.ui.mode == Mode::TwoD {
|
||||
Camera::new_2d(self.scene_view_box, view_box_size)
|
||||
} else {
|
||||
Camera::new_3d(self.scene_view_box)
|
||||
};
|
||||
|
||||
self.scene_thread_proxy.load_scene(built_svg.scene, view_box_size);
|
||||
self.dirty = true;
|
||||
}
|
||||
Event::User { message_type: event_id, message_data: expected_epoch } if
|
||||
event_id == self.expire_message_event_id &&
|
||||
expected_epoch as u32 == self.message_epoch => {
|
||||
|
@ -395,7 +412,7 @@ impl<W> DemoApp<W> where W: Window {
|
|||
|
||||
let mut ui_action = UIAction::None;
|
||||
self.ui.update(&self.renderer.device,
|
||||
&self.window,
|
||||
&mut self.window,
|
||||
&mut self.renderer.debug_ui,
|
||||
&mut ui_action);
|
||||
|
||||
|
@ -539,26 +556,6 @@ impl<W> DemoApp<W> where W: Window {
|
|||
match ui_action {
|
||||
UIAction::None => {}
|
||||
|
||||
UIAction::OpenFile(ref 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;
|
||||
self.scene_is_monochrome = built_svg.scene.is_monochrome();
|
||||
|
||||
let drawable_size = self.window_size.device_size();
|
||||
self.scene_thread_proxy.set_drawable_size(drawable_size);
|
||||
|
||||
self.camera = if self.ui.mode == Mode::TwoD {
|
||||
Camera::new_2d(built_svg.scene.view_box, drawable_size)
|
||||
} else {
|
||||
Camera::new_3d(built_svg.scene.view_box)
|
||||
};
|
||||
|
||||
self.scene_thread_proxy.load_scene(built_svg.scene);
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
UIAction::TakeScreenshot(ref path) => {
|
||||
self.pending_screenshot_path = Some((*path).clone());
|
||||
self.dirty = true;
|
||||
|
@ -626,8 +623,8 @@ impl SceneThreadProxy {
|
|||
SceneThreadProxy { sender: main_to_scene_sender, receiver: scene_to_main_receiver }
|
||||
}
|
||||
|
||||
fn load_scene(&self, scene: Scene) {
|
||||
self.sender.send(MainToSceneMsg::LoadScene(scene)).unwrap();
|
||||
fn load_scene(&self, scene: Scene, view_box_size: Point2DI32) {
|
||||
self.sender.send(MainToSceneMsg::LoadScene { scene, view_box_size }).unwrap();
|
||||
}
|
||||
|
||||
fn set_drawable_size(&self, drawable_size: Point2DI32) {
|
||||
|
@ -653,7 +650,11 @@ impl SceneThread {
|
|||
fn run(mut self) {
|
||||
while let Ok(msg) = self.receiver.recv() {
|
||||
match msg {
|
||||
MainToSceneMsg::LoadScene(scene) => self.scene = scene,
|
||||
MainToSceneMsg::LoadScene { scene, view_box_size } => {
|
||||
self.scene = scene;
|
||||
self.scene.view_box = RectF32::new(Point2DF32::default(),
|
||||
view_box_size.to_f32());
|
||||
}
|
||||
MainToSceneMsg::SetDrawableSize(size) => {
|
||||
self.scene.view_box = RectF32::new(Point2DF32::default(), size.to_f32());
|
||||
}
|
||||
|
@ -677,7 +678,7 @@ impl SceneThread {
|
|||
}
|
||||
|
||||
enum MainToSceneMsg {
|
||||
LoadScene(Scene),
|
||||
LoadScene { scene: Scene, view_box_size: Point2DI32 },
|
||||
SetDrawableSize(Point2DI32),
|
||||
Build(BuildOptions),
|
||||
}
|
||||
|
@ -701,7 +702,7 @@ pub struct RenderScene {
|
|||
pub struct Options {
|
||||
jobs: Option<usize>,
|
||||
mode: Mode,
|
||||
input_path: Option<PathBuf>,
|
||||
input_path: SVGPath,
|
||||
}
|
||||
|
||||
impl Options {
|
||||
|
@ -732,7 +733,10 @@ impl Options {
|
|||
Mode::TwoD
|
||||
};
|
||||
|
||||
let input_path = matches.value_of("INPUT").map(PathBuf::from);
|
||||
let input_path = match matches.value_of("INPUT") {
|
||||
None => SVGPath::Default,
|
||||
Some(path) => SVGPath::Path(PathBuf::from(path)),
|
||||
};
|
||||
|
||||
// Set up Rayon.
|
||||
let mut thread_pool_builder = ThreadPoolBuilder::new();
|
||||
|
@ -764,19 +768,16 @@ struct RenderStats {
|
|||
stats: Stats,
|
||||
}
|
||||
|
||||
fn load_scene(resource_loader: &dyn ResourceLoader, input_path: &Option<PathBuf>) -> BuiltSVG {
|
||||
fn load_scene(resource_loader: &dyn ResourceLoader, input_path: &SVGPath) -> BuiltSVG {
|
||||
let mut data;
|
||||
match *input_path {
|
||||
Some(ref input_path) => {
|
||||
SVGPath::Default => data = resource_loader.slurp(DEFAULT_SVG_VIRTUAL_PATH).unwrap(),
|
||||
SVGPath::Resource(ref name) => data = resource_loader.slurp(name).unwrap(),
|
||||
SVGPath::Path(ref path) => {
|
||||
data = vec![];
|
||||
let mut file = match File::open(input_path) {
|
||||
Ok(file) => file,
|
||||
Err(_) => panic!(),
|
||||
File::open(path).unwrap().read_to_end(&mut data).unwrap();
|
||||
}
|
||||
};
|
||||
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())
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@ impl<D> DemoUI<D> where D: Device {
|
|||
|
||||
pub fn update<W>(&mut self,
|
||||
device: &D,
|
||||
window: &W,
|
||||
window: &mut W,
|
||||
debug_ui: &mut DebugUI<D>,
|
||||
action: &mut UIAction)
|
||||
where W: Window {
|
||||
|
@ -139,9 +139,7 @@ impl<D> DemoUI<D> where D: Device {
|
|||
if debug_ui.ui.draw_button(device, position, &self.open_texture) {
|
||||
// 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);
|
||||
}
|
||||
window.present_open_svg_dialog();
|
||||
}
|
||||
debug_ui.ui.draw_tooltip(device, "Open SVG", RectI32::new(position, button_size));
|
||||
position += Point2DI32::new(BUTTON_WIDTH + PADDING, 0);
|
||||
|
@ -337,7 +335,6 @@ impl<D> DemoUI<D> where D: Device {
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum UIAction {
|
||||
None,
|
||||
OpenFile(PathBuf),
|
||||
TakeScreenshot(PathBuf),
|
||||
ZoomIn,
|
||||
ZoomOut,
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
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 {
|
||||
|
@ -23,7 +22,7 @@ pub trait Window {
|
|||
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<PathBuf, ()>;
|
||||
fn present_open_svg_dialog(&mut self);
|
||||
fn run_save_dialog(&self, extension: &str) -> Result<PathBuf, ()>;
|
||||
}
|
||||
|
||||
|
@ -37,6 +36,7 @@ pub enum Event {
|
|||
MouseDragged(Point2DI32),
|
||||
Zoom(f32),
|
||||
Look { pitch: f32, yaw: f32 },
|
||||
OpenSVG(SVGPath),
|
||||
User { message_type: u32, message_data: u32 },
|
||||
}
|
||||
|
||||
|
@ -58,3 +58,10 @@ impl WindowSize {
|
|||
self.logical_size.to_f32().scale(self.backing_scale_factor).to_i32()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum SVGPath {
|
||||
Default,
|
||||
Resource(String),
|
||||
Path(PathBuf),
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
use nfd::Response;
|
||||
use pathfinder_demo::DemoApp;
|
||||
use pathfinder_demo::window::{Event, Keycode, Window, WindowSize};
|
||||
use pathfinder_demo::window::{Event, Keycode, SVGPath, Window, WindowSize};
|
||||
use pathfinder_geometry::basic::point::Point2DI32;
|
||||
use pathfinder_gl::GLVersion;
|
||||
use pathfinder_gpu::resources::{FilesystemResourceLoader, ResourceLoader};
|
||||
|
@ -58,6 +58,8 @@ struct WindowImpl {
|
|||
#[allow(dead_code)]
|
||||
gl_context: GLContext,
|
||||
resource_loader: FilesystemResourceLoader,
|
||||
selected_file: Option<PathBuf>,
|
||||
open_svg_message_type: u32,
|
||||
}
|
||||
|
||||
impl Window for WindowImpl {
|
||||
|
@ -96,10 +98,10 @@ impl Window for WindowImpl {
|
|||
}
|
||||
}
|
||||
|
||||
fn run_open_dialog(&self, extension: &str) -> Result<PathBuf, ()> {
|
||||
match nfd::open_file_dialog(Some(extension), None) {
|
||||
Ok(Response::Okay(file)) => Ok(PathBuf::from(file)),
|
||||
_ => Err(()),
|
||||
fn present_open_svg_dialog(&mut self) {
|
||||
if let Ok(Response::Okay(path)) = nfd::open_file_dialog(Some("svg"), None) {
|
||||
self.selected_file = Some(PathBuf::from(path));
|
||||
WindowImpl::push_user_event(self.open_svg_message_type, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,6 +116,7 @@ impl Window for WindowImpl {
|
|||
impl WindowImpl {
|
||||
fn new() -> WindowImpl {
|
||||
SDL_VIDEO.with(|sdl_video| {
|
||||
SDL_EVENT.with(|sdl_event| {
|
||||
let (window, gl_context, event_pump);
|
||||
|
||||
let gl_attributes = sdl_video.gl_attr();
|
||||
|
@ -138,7 +141,19 @@ impl WindowImpl {
|
|||
|
||||
let resource_loader = FilesystemResourceLoader::locate();
|
||||
|
||||
WindowImpl { window, event_pump, gl_context, resource_loader }
|
||||
let open_svg_message_type = unsafe {
|
||||
sdl_event.register_event().unwrap()
|
||||
};
|
||||
|
||||
WindowImpl {
|
||||
window,
|
||||
event_pump,
|
||||
gl_context,
|
||||
resource_loader,
|
||||
open_svg_message_type,
|
||||
selected_file: None,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -171,6 +186,9 @@ impl WindowImpl {
|
|||
|
||||
fn convert_sdl_event(&self, sdl_event: SDLEvent) -> Option<Event> {
|
||||
match sdl_event {
|
||||
SDLEvent::User { type_, .. } if type_ == self.open_svg_message_type => {
|
||||
Some(Event::OpenSVG(SVGPath::Path(self.selected_file.clone().unwrap())))
|
||||
}
|
||||
SDLEvent::User { type_, code, .. } => {
|
||||
Some(Event::User { message_type: type_, message_data: code as u32 })
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue