check in of the canny filter demo
Change-Id: I2482c9a0442fd213b9ce25f06268f0eeb765b326
diff --git a/java/tests/CannyLive/Android.mk b/java/tests/CannyLive/Android.mk
new file mode 100644
index 0000000..2e50bec
--- /dev/null
+++ b/java/tests/CannyLive/Android.mk
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-appcompat
+LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.appcompat
+LOCAL_RENDERSCRIPT_TARGET_API := 22
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+LOCAL_SDK_VERSION := 23
+
+LOCAL_PACKAGE_NAME := CannyLive
+
+include $(BUILD_PACKAGE)
diff --git a/java/tests/CannyLive/AndroidManifest.xml b/java/tests/CannyLive/AndroidManifest.xml
new file mode 100644
index 0000000..e7e3299
--- /dev/null
+++ b/java/tests/CannyLive/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.example.cannylive" >
+ <uses-feature android:name="android.hardware.camera" />
+ <uses-feature
+ android:name="android.hardware.camera.front"
+ android:required="false" />
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/camera"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name="com.android.example.cannylive.MainActivity"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/java/tests/CannyLive/_index.html b/java/tests/CannyLive/_index.html
new file mode 100644
index 0000000..a1cabef
--- /dev/null
+++ b/java/tests/CannyLive/_index.html
@@ -0,0 +1,6 @@
+<h1>RenderScript Camera Demo</h1>
+<h2>Am example camera with live processing in RenderScript</h2>
+<p>
+This demonstrates a Canny filter in RenderScript filtering the camera preview.
+</p>
+
diff --git a/java/tests/CannyLive/res/anim/slide_in_from_left.xml b/java/tests/CannyLive/res/anim/slide_in_from_left.xml
new file mode 100644
index 0000000..75d05bb
--- /dev/null
+++ b/java/tests/CannyLive/res/anim/slide_in_from_left.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+
+ <translate
+ android:fromXDelta="-100%"
+ android:toXDelta="0%"
+ android:fromYDelta="0%"
+ android:toYDelta="0%"
+ android:duration="500" />
+
+</set>
diff --git a/java/tests/CannyLive/res/anim/slide_out_to_right.xml b/java/tests/CannyLive/res/anim/slide_out_to_right.xml
new file mode 100644
index 0000000..20ee06f
--- /dev/null
+++ b/java/tests/CannyLive/res/anim/slide_out_to_right.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+ <translate
+ android:fromXDelta="0%"
+ android:toXDelta="100%"
+ android:fromYDelta="0%"
+ android:toYDelta="0%"
+ android:duration="500"/>
+
+</set>
\ No newline at end of file
diff --git a/java/tests/CannyLive/res/drawable-hdpi/camera.png b/java/tests/CannyLive/res/drawable-hdpi/camera.png
new file mode 100644
index 0000000..38935c2
--- /dev/null
+++ b/java/tests/CannyLive/res/drawable-hdpi/camera.png
Binary files differ
diff --git a/java/tests/CannyLive/res/drawable-mdpi/camera.png b/java/tests/CannyLive/res/drawable-mdpi/camera.png
new file mode 100644
index 0000000..de7c1de
--- /dev/null
+++ b/java/tests/CannyLive/res/drawable-mdpi/camera.png
Binary files differ
diff --git a/java/tests/CannyLive/res/drawable-xhdpi/camera.png b/java/tests/CannyLive/res/drawable-xhdpi/camera.png
new file mode 100644
index 0000000..296ba2e
--- /dev/null
+++ b/java/tests/CannyLive/res/drawable-xhdpi/camera.png
Binary files differ
diff --git a/java/tests/CannyLive/res/drawable-xxhdpi/camera.png b/java/tests/CannyLive/res/drawable-xxhdpi/camera.png
new file mode 100644
index 0000000..169ccfc
--- /dev/null
+++ b/java/tests/CannyLive/res/drawable-xxhdpi/camera.png
Binary files differ
diff --git a/java/tests/CannyLive/res/drawable/ic_back.xml b/java/tests/CannyLive/res/drawable/ic_back.xml
new file mode 100644
index 0000000..1cbfdb2
--- /dev/null
+++ b/java/tests/CannyLive/res/drawable/ic_back.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:autoMirrored="true"
+ >
+ <!--android:tint="?attr/colorControlNormal">-->
+ <path
+ android:pathData="M20,11L7.8,11l5.6,-5.6L12,4l-8,8l8,8l1.4,-1.4L7.8,13L20,13L20,11z"
+ android:fillColor="@android:color/black"/>
+</vector>
diff --git a/java/tests/CannyLive/res/drawable/ic_cam.xml b/java/tests/CannyLive/res/drawable/ic_cam.xml
new file mode 100644
index 0000000..fbeedc6
--- /dev/null
+++ b/java/tests/CannyLive/res/drawable/ic_cam.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.0,12.0m-3.2,0.0a3.2,3.2 0.0,1.0 1.0,6.4 0.0a3.2,3.2 0.0,1.0 1.0,-6.4 0.0"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9.0,2.0l-1.83,2.0l-3.17,0.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,12.0c0.0,1.0 0.9,2.0 2.0,2.0l16.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0l0.0,-12.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0l-3.17,0.0l-1.83,-2.0l-6.0,0.0zm3.0,15.0c-2.76,0.0 -5.0,-2.24 -5.0,-5.0s2.24,-5.0 5.0,-5.0 5.0,2.24 5.0,5.0 -2.24,5.0 -5.0,5.0z"/>
+
+</vector>
\ No newline at end of file
diff --git a/java/tests/CannyLive/res/layout/activity_main.xml b/java/tests/CannyLive/res/layout/activity_main.xml
new file mode 100644
index 0000000..60e049a
--- /dev/null
+++ b/java/tests/CannyLive/res/layout/activity_main.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ android:orientation="horizontal"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:custom="http://schemas.android.com/apk/res-auto"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/panels">
+
+ <com.android.example.cannylive.CameraView
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:id="@+id/preview"
+ custom:aspectRatio="1.333"
+ android:layout_weight="4"
+ android:layout_gravity="center_vertical" />
+
+
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_height="match_parent"
+ android:layout_width="0px"
+ android:layout_weight="1"
+ android:id="@+id/control_bar_contents">
+
+
+ <Button
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_gravity="center_horizontal"
+ android:onClick="changeEffectMode"
+ android:text="mode" />
+
+ <ImageButton
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_gravity="center_horizontal"
+ android:onClick="capture"
+ android:src="@drawable/ic_cam" />
+
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/java/tests/CannyLive/res/layout/layout.xml b/java/tests/CannyLive/res/layout/layout.xml
new file mode 100644
index 0000000..a840496
--- /dev/null
+++ b/java/tests/CannyLive/res/layout/layout.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:paddingStart="8dip"
+ android:paddingTop="6dip"
+ android:paddingBottom="6dip">
+
+ <RadioButton
+ android:id="@+id/b1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/tv1"
+ android:layout_toRightOf="@+id/b1"
+ android:text="Normal Flow"
+ android:textStyle="bold"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/tv2"
+ android:layout_toRightOf="@+id/b1"
+ android:layout_below="@+id/tv1"
+ android:text="Use this for most bugs for the best beug reporting experience\n"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <RadioButton
+ android:id="@+id/b2"
+ android:layout_below="@+id/tv2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/tv3"
+ android:text="Emergency report"
+ android:layout_toRightOf="@+id/b2"
+ android:layout_below="@+id/tv2"
+ android:textStyle="bold"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/tv4"
+ android:text="Use this for bugs about device unresponsiveness or slowness this is a striped down experience to capture the bug in and uninterrupted device state."
+ android:layout_toRightOf="@+id/b2"
+ android:layout_below="@+id/tv3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+
+</RelativeLayout>
diff --git a/java/tests/CannyLive/res/mipmap-hdpi/ic_launcher.png b/java/tests/CannyLive/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/java/tests/CannyLive/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/java/tests/CannyLive/res/mipmap-mdpi/ic_launcher.png b/java/tests/CannyLive/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/java/tests/CannyLive/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/java/tests/CannyLive/res/mipmap-xhdpi/ic_launcher.png b/java/tests/CannyLive/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/java/tests/CannyLive/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/java/tests/CannyLive/res/mipmap-xxhdpi/ic_launcher.png b/java/tests/CannyLive/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/java/tests/CannyLive/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/java/tests/CannyLive/res/mipmap-xxxhdpi/ic_launcher.png b/java/tests/CannyLive/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
--- /dev/null
+++ b/java/tests/CannyLive/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/java/tests/CannyLive/res/values-v21/styles.xml b/java/tests/CannyLive/res/values-v21/styles.xml
new file mode 100644
index 0000000..f9166cc
--- /dev/null
+++ b/java/tests/CannyLive/res/values-v21/styles.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <style name="AppTheme" parent="android:Theme.Material.Light">
+ </style>
+</resources>
diff --git a/java/tests/CannyLive/res/values-w820dp/dimens.xml b/java/tests/CannyLive/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..62df187
--- /dev/null
+++ b/java/tests/CannyLive/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+<resources>
+ <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+ (such as screen margins) for screens with more than 820dp of available width. This
+ would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/java/tests/CannyLive/res/values/attrs.xml b/java/tests/CannyLive/res/values/attrs.xml
new file mode 100644
index 0000000..9221607
--- /dev/null
+++ b/java/tests/CannyLive/res/values/attrs.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <declare-styleable name="FixedAspectSurfaceView">
+ <attr name="aspectRatio" format="float"/>
+ </declare-styleable>
+</resources>
diff --git a/java/tests/CannyLive/res/values/base-strings.xml b/java/tests/CannyLive/res/values/base-strings.xml
new file mode 100644
index 0000000..d8f8952
--- /dev/null
+++ b/java/tests/CannyLive/res/values/base-strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <string name="app_name">RS Camera</string>
+ <string name="intro_message">
+ <![CDATA[
+
+
+ This demo implements a real-time high-dynamic-range camera viewfinder, by alternating
+ the sensor\'s exposure time between two exposure values on even and odd frames, and then
+ compositing together the latest two frames whenever a new frame is captured.
+
+
+ ]]>
+ </string>
+</resources>
diff --git a/java/tests/CannyLive/res/values/colors.xml b/java/tests/CannyLive/res/values/colors.xml
new file mode 100644
index 0000000..2a12c47
--- /dev/null
+++ b/java/tests/CannyLive/res/values/colors.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+</resources>
diff --git a/java/tests/CannyLive/res/values/dimens.xml b/java/tests/CannyLive/res/values/dimens.xml
new file mode 100644
index 0000000..295b5a9
--- /dev/null
+++ b/java/tests/CannyLive/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/java/tests/CannyLive/res/values/strings.xml b/java/tests/CannyLive/res/values/strings.xml
new file mode 100644
index 0000000..21838d5
--- /dev/null
+++ b/java/tests/CannyLive/res/values/strings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+
+ <string name="help_button">Help</string>
+
+ <string-array name="mode_label_array">
+ <!-- must be in same order as ViewfinderProcessor.MODE_ ints -->
+ <item>Mode: Normal</item>
+ <item>Mode: Split</item>
+ <item>Mode: HDR</item>
+ </string-array>
+
+ <string name="auto_exposure_label">Auto exp. time:</string>
+ <string name="even_exposure_label">Even exp. time:</string>
+ <string name="odd_exposure_label">Odd exp. time:</string>
+
+ <string name="help_text">
+ <b>HDR Viewfinder Demo:</b>\n\n
+
+ Tap viewfinder to switch modes.\n\n
+
+ <b>Normal:</b> Standard camera preview\n
+ <b>Split:</b> Manual exposure control\n
+ <b>HDR:</b> Fused HDR viewfinder\n\n
+
+ Swipe up/down in Split/HDR modes to change manual exposure
+ values.\n\n
+
+ The left half of the viewfinder controls exposure time for
+ even-numbered frames, and the right half of the viewfinder
+ controls exposure time for odd-numbered frames
+ </string>
+
+ <string name="info">Info</string>
+
+ <string name="camera_no_good">No back-facing sufficiently capable camera available!</string>
+ <string name="camera_disabled">Camera is disabled by device policy</string>
+ <string name="camera_disconnected">Camera was disconnected before it was opened</string>
+ <string name="camera_error">Camera service reported an error</string>
+ <string name="camera_unknown">Unknown camera error: %s</string>
+
+</resources>
diff --git a/java/tests/CannyLive/res/values/styles.xml b/java/tests/CannyLive/res/values/styles.xml
new file mode 100644
index 0000000..12eb119
--- /dev/null
+++ b/java/tests/CannyLive/res/values/styles.xml
@@ -0,0 +1,8 @@
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="android:Theme.Light">
+ <!-- Customize your theme here. -->
+ </style>
+
+</resources>
diff --git a/java/tests/CannyLive/res/values/template-dimens.xml b/java/tests/CannyLive/res/values/template-dimens.xml
new file mode 100644
index 0000000..291c495
--- /dev/null
+++ b/java/tests/CannyLive/res/values/template-dimens.xml
@@ -0,0 +1,31 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+
+ <!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
+
+ <dimen name="margin_tiny">4dp</dimen>
+ <dimen name="margin_small">8dp</dimen>
+ <dimen name="margin_medium">16dp</dimen>
+ <dimen name="margin_large">32dp</dimen>
+ <dimen name="margin_huge">64dp</dimen>
+
+ <!-- Semantic definitions -->
+
+ <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
+ <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+
+</resources>
diff --git a/java/tests/CannyLive/res/values/template-styles.xml b/java/tests/CannyLive/res/values/template-styles.xml
new file mode 100644
index 0000000..777b347
--- /dev/null
+++ b/java/tests/CannyLive/res/values/template-styles.xml
@@ -0,0 +1,35 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+
+ <!-- Activity themes -->
+
+ <style name="Theme.Base" parent="android:Theme.Light" />
+
+ <style name="Theme.Sample" parent="Theme.Base" />
+
+ <!-- Widget styling -->
+
+ <style name="Widget" />
+
+ <style name="Widget.SampleMessage">
+ <item name="android:textAppearance">?android:textAppearanceMedium</item>
+ <item name="android:lineSpacingMultiplier">1.1</item>
+ </style>
+
+
+
+</resources>
diff --git a/java/tests/CannyLive/src/com/android/example/cannylive/CameraOps.java b/java/tests/CannyLive/src/com/android/example/cannylive/CameraOps.java
new file mode 100644
index 0000000..de119c9
--- /dev/null
+++ b/java/tests/CannyLive/src/com/android/example/cannylive/CameraOps.java
@@ -0,0 +1,708 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.example.cannylive;
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.util.Log;
+import android.util.Range;
+import android.util.Size;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Simple interface for operating the camera, with major camera operations
+ * all performed on a background handler thread.
+ */
+public class CameraOps {
+
+ private static final String TAG = "CameraOps";
+ private static final long ONE_SECOND = 1000000000;
+ public static final long CAMERA_CLOSE_TIMEOUT = 2000; // ms
+
+ private final CameraManager mCameraManager;
+ private CameraDevice mCameraDevice;
+ private CameraCaptureSession mCameraSession;
+ private List<Surface> mSurfaces;
+
+ private final ConditionVariable mCloseWaiter = new ConditionVariable();
+
+ private HandlerThread mCameraThread;
+ private Handler mCameraHandler;
+
+ private final ErrorDisplayer mErrorDisplayer;
+
+ private final CameraReadyListener mReadyListener;
+ private final Handler mReadyHandler;
+
+ private int mISOmax;
+ private int mISOmin;
+ private long mExpMax;
+ private long mExpMin;
+ private float mFocusMin;
+ private float mFocusDist = 0;
+ private int mIso;
+ boolean mAutoExposure = true;
+ boolean mAutoFocus = true;
+ private long mExposure = ONE_SECOND / 33;
+
+ private Object mAutoExposureTag = new Object();
+
+ private ImageReader mImageReader;
+ private Handler mBackgroundHandler;
+ private CameraCharacteristics mCameraInfo;
+ private HandlerThread mBackgroundThread;
+ CaptureRequest.Builder mHdrBuilder;
+ private Surface mProcessingNormalSurface;
+ CaptureRequest mPreviewRequest;
+ private String mSaveFileName;
+ private Context mContext;
+ private int mCaptureMode;
+
+ public String resume() {
+ String errorMessage = "Unknown error";
+ boolean foundCamera = false;
+ try {
+ // Find first back-facing camera that has necessary capability
+ String[] cameraIds = mCameraManager.getCameraIdList();
+ for (String id : cameraIds) {
+ CameraCharacteristics info = mCameraManager.getCameraCharacteristics(id);
+ int facing = info.get(CameraCharacteristics.LENS_FACING);
+
+ int level = info.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+ boolean hasFullLevel
+ = (level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
+
+ int[] capabilities = info.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+ int syncLatency = info.get(CameraCharacteristics.SYNC_MAX_LATENCY);
+ boolean hasManualControl = hasCapability(capabilities,
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR);
+ boolean hasEnoughCapability = hasManualControl &&
+ syncLatency == CameraCharacteristics.SYNC_MAX_LATENCY_PER_FRAME_CONTROL;
+ Range<Integer> irange;
+ Range<Long> lrange;
+
+ irange = info.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE);
+ if (irange != null) {
+ mISOmax = irange.getUpper();
+ mISOmin = irange.getLower();
+ lrange = info.get(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE);
+ mExpMax = lrange.getUpper();
+ mExpMin = lrange.getLower();
+ mFocusMin = info.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE);
+ } else {
+ mISOmax = 200;
+ mISOmin = 100;
+ mExpMax = 1000;
+ }
+ mFocusDist = mFocusMin;
+ StreamConfigurationMap map = info.get(
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ Size[] sizes = map.getOutputSizes(ImageFormat.JPEG);
+ List<Size> sizeList = Arrays.asList(sizes);
+ Collections.sort(sizeList, new Comparator<Size>() {
+ @Override
+ public int compare(Size lhs, Size rhs) {
+ int leftArea = lhs.getHeight() * lhs.getWidth();
+ int rightArea = lhs.getHeight() * lhs.getWidth();
+ return Integer.compare(leftArea, rightArea);
+ }
+ });
+ Size max = sizeList.get(0);
+ int check = 1;
+ Size big = sizeList.get(check);
+ float aspect = 16/9f;
+ Log.v(TAG,"max big "+max.getWidth()+" x "+max.getHeight());
+ for (int i = 0; i < sizeList.size(); i++) {
+ Size s = sizeList.get(i);
+ if (s.getHeight() == 720) {
+ big = s;
+ break;
+ }
+ }
+ Log.v(TAG,"BIG wil be "+big.getWidth()+" x "+big.getHeight());
+ mImageReader = ImageReader.newInstance(big.getWidth(), big.getHeight(),
+ ImageFormat.JPEG, /*maxImages*/2);
+ mImageReader.setOnImageAvailableListener(
+ mOnImageAvailableListener, mBackgroundHandler);
+
+ if (facing == CameraCharacteristics.LENS_FACING_BACK &&
+ (hasFullLevel || hasEnoughCapability)) {
+ // Found suitable camera - get info, open, and set up outputs
+ mCameraInfo = info;
+ openCamera(id);
+ foundCamera = true;
+ break;
+ }
+ }
+ if (!foundCamera) {
+ errorMessage = "no back camera";
+ }
+ } catch (CameraAccessException e) {
+ errorMessage = e.getMessage();
+ }
+ // startBackgroundThread
+ mBackgroundThread = new HandlerThread("CameraBackground");
+ mBackgroundThread.start();
+ mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
+ return (foundCamera) ? null : errorMessage;
+ }
+
+
+ private boolean hasCapability(int[] capabilities, int capability) {
+ for (int c : capabilities) {
+ if (c == capability) return true;
+ }
+ return false;
+ }
+
+ private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
+ = new ImageReader.OnImageAvailableListener() {
+
+ @Override
+ public void onImageAvailable(ImageReader reader) {
+ mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(),
+ mSaveFileName, mContext,mCaptureMode));
+ }
+
+ };
+
+ /**
+ * Saves a JPEG {@link android.media.Image} into the specified {@link java.io.File}.
+ */
+ private static class ImageSaver implements Runnable {
+ private final Image mImage;
+ private final String mName;
+ Context mContext;
+ private int mMode;
+
+ public ImageSaver(Image image, String fileName, Context context,int mode) {
+ mImage = image;
+ mName = fileName;
+ mContext = context;
+ mMode = mode;
+ }
+
+ @Override
+ public void run() {
+ Log.v(TAG, "S>> SAVING...");
+ String url = MediaStoreSaver.insertImage(mContext.getContentResolver(),
+ new MediaStoreSaver.StreamWriter() {
+ @Override
+ public void write(OutputStream imageOut) throws IOException {
+ try {
+ ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
+ byte[] bytes = new byte[buffer.remaining()];
+ Log.v(TAG, "S>> size=" + mImage.getWidth() +
+ "," + mImage.getHeight());
+ Log.v(TAG, "S>> bytes " + bytes.length +
+ " (" + bytes.length / (1024 * 1024) + "MB");
+ Log.v(TAG, "S>> bytes out " + bytes.length / mImage.getWidth());
+ buffer.get(bytes);
+ imageOut.write(bytes);
+ } finally {
+ mImage.close();
+ }
+ }
+ }, mName, "Saved from Simple Camera Demo");
+ ViewfinderProcessor.reProcessImage(mContext, url, mMode);
+ }
+ }
+
+ /**
+ * Create a new camera ops thread.
+ *
+ * @param errorDisplayer listener for displaying error messages
+ * @param readyListener listener for notifying when camera is ready for requests
+ */
+ CameraOps(CameraManager manager, ErrorDisplayer errorDisplayer,
+ CameraReadyListener readyListener) {
+ mReadyHandler = new Handler(Looper.getMainLooper());
+
+ mCameraThread = new HandlerThread("CameraOpsThread");
+ mCameraThread.start();
+
+ if (manager == null || errorDisplayer == null ||
+ readyListener == null || mReadyHandler == null) {
+ throw new IllegalArgumentException("Need valid displayer, listener, handler");
+ }
+
+ mCameraManager = manager;
+ mErrorDisplayer = errorDisplayer;
+ mReadyListener = readyListener;
+
+ }
+
+ /**
+ * Open the first backfacing camera listed by the camera manager.
+ * Displays a dialog if it cannot open a camera.
+ */
+ public void openCamera(final String cameraId) {
+ mCameraHandler = new Handler(mCameraThread.getLooper());
+
+ mCameraHandler.post(new Runnable() {
+ public void run() {
+ if (mCameraDevice != null) {
+ throw new IllegalStateException("Camera already open");
+ }
+ try {
+
+ mCameraManager.openCamera(cameraId, mCameraDeviceListener, mCameraHandler);
+ } catch (CameraAccessException e) {
+ String errorMessage = mErrorDisplayer.getErrorString(e);
+ mErrorDisplayer.showErrorDialog(errorMessage);
+ }
+ }
+ });
+ }
+
+ public void pause() {
+
+ closeCameraAndWait();
+ mBackgroundThread.quitSafely();
+ try {
+ mBackgroundThread.join();
+ mBackgroundThread = null;
+ mBackgroundHandler = null;
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public Size getBestSize() {
+ // Find a good size for output - largest 16:9 aspect ratio that's less than 720p
+ final int MAX_WIDTH = 640;
+ final float TARGET_ASPECT = 16.f / 9.f;
+
+
+ StreamConfigurationMap configs =
+ mCameraInfo.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+ Size[] outputSizes = configs.getOutputSizes(SurfaceHolder.class);
+
+ Size outputSize = null;
+ ArrayList<Size> smallEnough = new ArrayList<Size>();
+ for (Size candidateSize : outputSizes) {
+ if (candidateSize.getWidth() <= MAX_WIDTH) {
+ Log.v(TAG, "consider " + candidateSize);
+ smallEnough.add(candidateSize);
+ }
+ }
+ if (smallEnough.size() == 0) {
+ return outputSizes[outputSizes.length - 1]; //pick the smallest
+ }
+ Size maxSize = smallEnough.get(0);
+ double aspectDelta = Math.abs(maxSize.getWidth() / maxSize.getHeight() - TARGET_ASPECT);
+ for (Size candidateSize : smallEnough) {
+ if (maxSize.getWidth() < candidateSize.getWidth()) {
+ maxSize = candidateSize;
+ aspectDelta = Math.abs(maxSize.getWidth() / maxSize.getHeight() - TARGET_ASPECT);
+ }
+ if (maxSize.getWidth() == candidateSize.getWidth()) {
+ if (aspectDelta > Math.abs(candidateSize.getWidth() / candidateSize.getHeight() - TARGET_ASPECT)) {
+ maxSize = candidateSize;
+ aspectDelta = Math.abs(maxSize.getWidth() / maxSize.getHeight() - TARGET_ASPECT);
+ }
+ }
+ }
+
+ return maxSize;
+ }
+
+ /**
+ * Close the camera and wait for the close callback to be called in the camera thread.
+ * Times out after @{value CAMERA_CLOSE_TIMEOUT} ms.
+ */
+ public void closeCameraAndWait() {
+ mCloseWaiter.close();
+ mCameraHandler.post(mCloseCameraRunnable);
+ boolean closed = mCloseWaiter.block(CAMERA_CLOSE_TIMEOUT);
+ if (!closed) {
+ Log.e(TAG, "Timeout closing camera");
+ }
+ }
+
+ private Runnable mCloseCameraRunnable = new Runnable() {
+ public void run() {
+ if (mCameraDevice != null) {
+ mCameraDevice.close();
+ }
+ mCameraDevice = null;
+ mCameraSession = null;
+ mSurfaces = null;
+ }
+ };
+
+ /**
+ * Set the output Surfaces, and finish configuration if otherwise ready.
+ */
+ public void setSurface(Surface surface) {
+ final List<Surface> surfaceList = new ArrayList<Surface>();
+ surfaceList.add(surface);
+ surfaceList.add(mImageReader.getSurface());
+
+ mCameraHandler.post(new Runnable() {
+ public void run() {
+ mSurfaces = surfaceList;
+ startCameraSession();
+ }
+ });
+ }
+
+ /**
+ * Get a request builder for the current camera.
+ */
+ public CaptureRequest.Builder createCaptureRequest(int template) throws CameraAccessException {
+ CameraDevice device = mCameraDevice;
+ if (device == null) {
+ throw new IllegalStateException("Can't get requests when no camera is open");
+ }
+ return device.createCaptureRequest(template);
+ }
+
+ /**
+ * Set a repeating request.
+ */
+ public void setRepeatingRequest(final CaptureRequest request,
+ final CameraCaptureSession.CaptureCallback listener,
+ final Handler handler) {
+ mCameraHandler.post(new Runnable() {
+ public void run() {
+ try {
+ mCameraSession.setRepeatingRequest(request, listener, handler);
+ } catch (CameraAccessException e) {
+ String errorMessage = mErrorDisplayer.getErrorString(e);
+ mErrorDisplayer.showErrorDialog(errorMessage);
+ }
+ }
+ });
+ }
+
+ /**
+ * Set a repeating request.
+ */
+ public void setRepeatingBurst(final List<CaptureRequest> requests,
+ final CameraCaptureSession.CaptureCallback listener,
+ final Handler handler) {
+ mCameraHandler.post(new Runnable() {
+ public void run() {
+ try {
+ mCameraSession.setRepeatingBurst(requests, listener, handler);
+
+ } catch (CameraAccessException e) {
+ String errorMessage = mErrorDisplayer.getErrorString(e);
+ mErrorDisplayer.showErrorDialog(errorMessage);
+ }
+ }
+ });
+ }
+
+ /**
+ * Configure the camera session.
+ */
+ private void startCameraSession() {
+ // Wait until both the camera device is open and the SurfaceView is ready
+ if (mCameraDevice == null || mSurfaces == null) return;
+
+ try {
+
+ mCameraDevice.createCaptureSession(
+ mSurfaces, mCameraSessionListener, mCameraHandler);
+ } catch (CameraAccessException e) {
+ String errorMessage = mErrorDisplayer.getErrorString(e);
+ mErrorDisplayer.showErrorDialog(errorMessage);
+ mCameraDevice.close();
+ mCameraDevice = null;
+ }
+ }
+
+ /**
+ * Main listener for camera session events
+ * Invoked on mCameraThread
+ */
+ private CameraCaptureSession.StateCallback mCameraSessionListener =
+ new CameraCaptureSession.StateCallback() {
+
+ @Override
+ public void onConfigured(CameraCaptureSession session) {
+ mCameraSession = session;
+ mReadyHandler.post(new Runnable() {
+ public void run() {
+ // This can happen when the screen is turned off and turned back on.
+ if (null == mCameraDevice) {
+ return;
+ }
+
+ mReadyListener.onCameraReady();
+ }
+ });
+
+ }
+
+ @Override
+ public void onConfigureFailed(CameraCaptureSession session) {
+ mErrorDisplayer.showErrorDialog("Unable to configure the capture session");
+ mCameraDevice.close();
+ mCameraDevice = null;
+ }
+ };
+
+ /**
+ * Main listener for camera device events.
+ * Invoked on mCameraThread
+ */
+ private CameraDevice.StateCallback mCameraDeviceListener = new CameraDevice.StateCallback() {
+
+ @Override
+ public void onOpened(CameraDevice camera) {
+ mCameraDevice = camera;
+ startCameraSession();
+ }
+
+ @Override
+ public void onClosed(CameraDevice camera) {
+ mCloseWaiter.open();
+ }
+
+ @Override
+ public void onDisconnected(CameraDevice camera) {
+ mErrorDisplayer.showErrorDialog("The camera device has been disconnected.");
+ camera.close();
+ mCameraDevice = null;
+ }
+
+ @Override
+ public void onError(CameraDevice camera, int error) {
+ mErrorDisplayer.showErrorDialog("The camera encountered an error:" + error);
+ camera.close();
+ mCameraDevice = null;
+ }
+
+ };
+
+ public void captureStillPicture(int currentJpegRotation, String name, Context context, int mode) {
+ mSaveFileName = name;
+ mContext = context;
+ mCaptureMode = mode;
+ try {
+ // TODO call lock focus if we are in "AF-S(One-Shot AF) mode"
+ // TODO call precapture if we are using flash
+ // This is the CaptureRequest.Builder that we use to take a picture.
+ final CaptureRequest.Builder captureBuilder =
+ createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+ Log.v(TAG, "S>> Target " + mImageReader.getWidth() + "," + mImageReader.getHeight());
+
+ captureBuilder.addTarget(mImageReader.getSurface());
+
+ captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, currentJpegRotation);
+
+ CameraCaptureSession.CaptureCallback captureCallback
+ = new CameraCaptureSession.CaptureCallback() {
+
+ @Override
+ public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+ TotalCaptureResult result) {
+ Log.v(TAG, "S>> onCaptureCompleted");
+ setParameters();
+ }
+ };
+
+
+ setRequest(captureBuilder.build(), captureCallback, null);
+ } catch (CameraAccessException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Set a repeating request.
+ */
+ private void setRequest(final CaptureRequest request,
+ final CameraCaptureSession.CaptureCallback listener,
+ final Handler handler) {
+ mCameraHandler.post(new Runnable() {
+ public void run() {
+ try {
+ mCameraSession.stopRepeating();
+ mCameraSession.capture(request, listener, handler);
+ } catch (CameraAccessException e) {
+ String errorMessage = mErrorDisplayer.getErrorString(e);
+ mErrorDisplayer.showErrorDialog(errorMessage);
+ }
+ }
+ });
+ }
+
+ public void setUpCamera(Surface processingNormalSurface) {
+ mProcessingNormalSurface = processingNormalSurface;
+ // Ready to send requests in, so set them up
+ try {
+ CaptureRequest.Builder previewBuilder =
+ createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ previewBuilder.addTarget(mProcessingNormalSurface);
+ previewBuilder.setTag(mAutoExposureTag);
+ mPreviewRequest = previewBuilder.build();
+ mHdrBuilder = createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ mHdrBuilder.set(CaptureRequest.CONTROL_AE_MODE,
+ CaptureRequest.CONTROL_AE_MODE_OFF);
+ mHdrBuilder.addTarget(mProcessingNormalSurface);
+ setParameters();
+
+ } catch (CameraAccessException e) {
+ String errorMessage = e.getMessage();
+ // MessageDialogFragment.newInstance(errorMessage).show(getFragmentManager(), FRAGMENT_DIALOG);
+ }
+ }
+
+ /**
+ * Start running an HDR burst on a configured camera session
+ */
+ public void setParameters() {
+ if (mHdrBuilder == null) {
+ Log.v(TAG, " Camera not set up");
+ return;
+ }
+ if (mAutoExposure) {
+ mHdrBuilder.set(CaptureRequest.SENSOR_FRAME_DURATION, ONE_SECOND / 30);
+ mHdrBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, getExposure());
+ mHdrBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
+ } else {
+ mHdrBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF);
+ mHdrBuilder.set(CaptureRequest.SENSOR_FRAME_DURATION, ONE_SECOND / 30);
+ mHdrBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, getExposure());
+ mHdrBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, getIso());
+ }
+ if (mAutoFocus) {
+ mHdrBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
+ mHdrBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
+ } else {
+ mHdrBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
+ mHdrBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, getFocusDistance());
+ mHdrBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
+ }
+
+ setRepeatingRequest(mHdrBuilder.build(), mCaptureCallback, mReadyHandler);
+ }
+
+ private CameraCaptureSession.CaptureCallback mCaptureCallback
+ = new CameraCaptureSession.CaptureCallback() {
+
+ public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+ TotalCaptureResult result) {
+ }
+ };
+
+ /**
+ * Simple listener for main code to know the camera is ready for requests, or failed to
+ * start.
+ */
+ public interface CameraReadyListener {
+ public void onCameraReady();
+ }
+
+ /**
+ * Simple listener for displaying error messages
+ */
+ public interface ErrorDisplayer {
+ public void showErrorDialog(String errorMessage);
+
+ public String getErrorString(CameraAccessException e);
+ }
+
+ public float getFocusDistance() {
+ return mFocusDist;
+ }
+
+ public void setFocusDistance(float focusDistance) {
+ mFocusDist = focusDistance;
+ }
+
+ public void setIso(int iso) {
+ mIso = iso;
+ }
+
+ public boolean isAutoExposure() {
+ return mAutoExposure;
+ }
+
+ public void setAutoExposure(boolean autoExposure) {
+ mAutoExposure = autoExposure;
+ }
+
+ public boolean isAutoFocus() {
+ return mAutoFocus;
+ }
+
+ public void setAutoFocus(boolean autoFocus) {
+ mAutoFocus = autoFocus;
+ }
+
+ public int getIso() {
+ return mIso;
+ }
+
+ public long getExposure() {
+ return mExposure;
+ }
+
+ public void setExposure(long exposure) {
+ mExposure = exposure;
+ }
+
+ public int getIsoMax() {
+ return mISOmax;
+ }
+
+ public int getIsoMin() {
+ return mISOmin;
+ }
+
+ public long getExpMax() {
+ return mExpMax;
+ }
+
+ public long getExpMin() {
+ return mExpMin;
+ }
+
+ public float getFocusMin() {
+ return mFocusMin;
+ }
+}
diff --git a/java/tests/CannyLive/src/com/android/example/cannylive/CameraView.java b/java/tests/CannyLive/src/com/android/example/cannylive/CameraView.java
new file mode 100644
index 0000000..f65a829
--- /dev/null
+++ b/java/tests/CannyLive/src/com/android/example/cannylive/CameraView.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.example.cannylive;
+
+import android.app.Activity;
+import android.content.Context;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.TotalCaptureResult;
+import android.renderscript.RenderScript;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Size;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.View;
+
+import com.android.example.cannylive.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by hoford on 2/27/15.
+ */
+public class CameraView extends FixedAspectSurfaceView {
+ private static final String TAG = "CameraPreView";
+
+ private static final long MICRO_SECOND = 1000;
+ private static final long MILLI_SECOND = MICRO_SECOND * 1000;
+ private static final long ONE_SECOND = MILLI_SECOND * 1000;
+
+ private Surface mPreviewSurface;
+ ViewfinderProcessor mProcessor;
+ private Surface mProcessingNormalSurface;
+ CameraOps mCameraOps;
+ CameraManager mCameraManager;
+ Activity mActivity;
+ Context mContext;
+ byte mode = 0;
+ public static final byte MODE_NONE = 0;
+ public static final byte MODE_SPEED = 1;
+ public static final byte MODE_FOCUS = 2;
+ public static final byte MODE_ISO = 3;
+ RenderScript mRS;
+ ErrorCallback mErrorCallback;
+ ParametersChangedCallback mParametersChangedCallback;
+
+ public CameraView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+
+ mRS = RenderScript.create(mContext);
+ SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ mPreviewSurface = holder.getSurface();
+ setupProcessor();
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mPreviewSurface = null;
+ }
+ };
+ getHolder().addCallback(callback);
+ mCameraManager = (CameraManager) mContext.getSystemService(mContext.CAMERA_SERVICE);
+
+ CameraOps.ErrorDisplayer errorDisplayer = new CameraOps.ErrorDisplayer() {
+
+ @Override
+ public void showErrorDialog(String errorMessage) {
+ Log.v(TAG, "ERROR");
+ if (mErrorCallback != null) {
+ mErrorCallback.showError(errorMessage);
+ }
+ }
+
+ @Override
+ public String getErrorString(CameraAccessException e) {
+ switch (e.getReason()) {
+ case CameraAccessException.CAMERA_DISABLED:
+ return mContext.getString(R.string.camera_disabled);
+ case CameraAccessException.CAMERA_DISCONNECTED:
+ return mContext.getString(R.string.camera_disconnected);
+ case CameraAccessException.CAMERA_ERROR:
+ return mContext.getString(R.string.camera_error);
+ default:
+ return mContext.getString(R.string.camera_unknown, e.getReason());
+
+ }
+ }
+ };
+
+ CameraOps.CameraReadyListener cameraReadyListener = new CameraOps.CameraReadyListener() {
+ @Override
+ public void onCameraReady() {
+ mCameraOps.setUpCamera(mProcessingNormalSurface);
+ }
+ };
+ setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ return touchScreen(event);
+ }
+ });
+ mCameraOps = new CameraOps(mCameraManager,
+ errorDisplayer,
+ cameraReadyListener);
+ }
+
+ public void resume(Activity activity) {
+ mActivity = activity;
+
+ String errorMessage = mCameraOps.resume();
+ if (errorMessage != null) {
+ if (mErrorCallback != null) {
+ mErrorCallback.showError(errorMessage);
+ }
+ } else {
+
+ Size outputSize = mCameraOps.getBestSize();
+ mProcessor = new ViewfinderProcessor(mRS, outputSize);
+ // Configure the output view - this will fire surfaceChanged
+ setAspectRatio((float) outputSize.getWidth() / outputSize.getHeight());
+ getHolder().setFixedSize(outputSize.getWidth(), outputSize.getHeight());
+ }
+ }
+
+ public void pause() {
+ mProcessor.close();
+ mCameraOps.pause();
+ }
+
+ /**
+ * Once camera is open and output surfaces are ready, configure the RS processing
+ * and the camera device inputs/outputs.
+ */
+ private void setupProcessor() {
+ if (mProcessor == null || mPreviewSurface == null) return;
+ mProcessor.setOutputSurface(mPreviewSurface);
+ mProcessingNormalSurface = mProcessor.getInputSurface();
+ mCameraOps.setSurface(mProcessingNormalSurface);
+ }
+
+ public void takePicture(int mode) {
+ // Orientation
+ Log.v(TAG,"Taking picture");
+ int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
+ int jpegRotation = Surface.ROTATION_0;
+ switch (rotation) {
+ case 90:
+ jpegRotation = Surface.ROTATION_0;
+ break;
+ case 0:
+ jpegRotation = Surface.ROTATION_90;
+ break;
+ case 180:
+ jpegRotation = Surface.ROTATION_270;
+ break;
+ case 270:
+ jpegRotation = Surface.ROTATION_180;
+ break;
+ }
+ String name = "Simple" + System.currentTimeMillis() + ".jpg";
+ mCameraOps.captureStillPicture(jpegRotation, name, mContext, mode);
+ }
+
+ private CameraCaptureSession.CaptureCallback mPhotoCallback
+ = new CameraCaptureSession.CaptureCallback() {
+
+ public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+ TotalCaptureResult result) {
+ Log.v(TAG, "onCaptureCompleted " + result.toString());
+ }
+ };
+
+ float mDownY;
+ long mExposureDown;
+ float mFocusDistDown;
+
+ public boolean touchScreen(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ mDownY = event.getY();
+ mExposureDown = mCameraOps.getExposure();
+ mFocusDistDown = mCameraOps.getFocusDistance();
+ if (mFocusDistDown == 0.0) {
+ mFocusDistDown = 0.01f;
+ }
+ }
+ float distanceY = event.getY() - mDownY;
+ float width = getWidth();
+ float height = getHeight();
+
+ float yDistNorm = distanceY / height;
+
+ float ACCELERATION_FACTOR = 8;
+ float scaleFactor = (float) Math.pow(2.f, yDistNorm * ACCELERATION_FACTOR);
+
+ switch (mode) {
+ case MODE_SPEED:
+ long exp = (long) (mExposureDown * scaleFactor);
+ exp = Math.min(mCameraOps.getExpMax(), exp);
+ mCameraOps.setExposure(Math.max(mCameraOps.getExpMin(), exp));
+ Log.v(TAG, "mExposure =" + mCameraOps.getExposure());
+ break;
+ case MODE_FOCUS:
+ float focusDist = mFocusDistDown * scaleFactor;
+ focusDist = Math.max(0.0f, Math.min(mCameraOps.getFocusMin(), focusDist));
+ if (focusDist < 0.01) focusDist = 0;
+ mCameraOps.setFocusDistance(focusDist);
+ Log.v(TAG, "mFocusDist =" + focusDist);
+ break;
+ case MODE_ISO:
+ ACCELERATION_FACTOR = 2;
+ scaleFactor = (float) Math.pow(2.f, yDistNorm * ACCELERATION_FACTOR);
+ int iso = (int) (getIso() * scaleFactor);
+ iso = Math.min(mCameraOps.getIsoMax(), iso);
+ mCameraOps.setIso(Math.max(mCameraOps.getIsoMin(), iso));
+ break;
+ }
+
+ if (mParametersChangedCallback != null) {
+ mParametersChangedCallback.parametersChanged();
+ }
+ mCameraOps.setParameters();
+
+ return true;
+ }
+
+ public void setMode(byte mode) {
+ this.mode = mode;
+ }
+
+ public byte getMode() {
+ return mode;
+ }
+
+ public int getIso() {
+ return mCameraOps.getIso();
+ }
+
+ public void setIso(int iso) {
+ mCameraOps.setIso(iso);
+ if (mParametersChangedCallback != null) {
+ mParametersChangedCallback.parametersChanged();
+ }
+ mCameraOps.setParameters();
+ }
+
+ public long getExposure() {
+ return mCameraOps.getExposure();
+ }
+
+ public void setExposure(long exposure) {
+ mCameraOps.setExposure(exposure);
+ if (mParametersChangedCallback != null) {
+ mParametersChangedCallback.parametersChanged();
+ }
+ mCameraOps.setParameters();
+ }
+
+ public float getFocusDist() {
+ return mCameraOps.getFocusDistance();
+ }
+
+ public void setFocusInMeters(float dist) {
+ float min = mCameraOps.getFocusMin();
+ float d = 10 / (dist + 10 / min);
+ setFocusDist(d);
+ }
+
+ public void setFocusDist(float dist) {
+ mCameraOps.setFocusDistance(dist);
+ mCameraOps.setParameters();
+ }
+ public void changeEffectMode() {
+ mProcessor.changeEffectMode();
+ }
+ public float getMinFocusDistance() {
+ return mCameraOps.getFocusMin();
+ }
+
+ public void setAutofocus(boolean autofocus) {
+ mCameraOps.setAutoFocus(autofocus);
+ mCameraOps.setParameters();
+ }
+
+ public boolean isAutoExposure() {
+ return mCameraOps.isAutoExposure();
+ }
+
+ public boolean isAutofocus() {
+ return mCameraOps.isAutoFocus();
+ }
+
+ public void setAutoExposure(boolean autoExposure) {
+ mCameraOps.setAutoExposure(autoExposure);
+ mCameraOps.setParameters();
+ }
+
+ public int getEffect() {
+ return mProcessor.getMode();
+ }
+
+
+ public static interface ErrorCallback {
+ public void showError(String errorMessage);
+ }
+
+ public void setErrorCallback(ErrorCallback errorCallback) {
+ mErrorCallback = errorCallback;
+ }
+
+ public static interface ParametersChangedCallback {
+ public void parametersChanged();
+ }
+
+ public void setParametersChangedCallback(ParametersChangedCallback parametersChangedCallback) {
+ mParametersChangedCallback = parametersChangedCallback;
+ }
+
+ float getFps() {
+ if (mProcessor==null) {
+ return 0.0f;
+ }
+ return mProcessor.getmFps();
+ }
+}
diff --git a/java/tests/CannyLive/src/com/android/example/cannylive/FixedAspectSurfaceView.java b/java/tests/CannyLive/src/com/android/example/cannylive/FixedAspectSurfaceView.java
new file mode 100644
index 0000000..180b65e
--- /dev/null
+++ b/java/tests/CannyLive/src/com/android/example/cannylive/FixedAspectSurfaceView.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.example.cannylive;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+
+import com.android.example.cannylive.R;
+
+/**
+ * A SurfaceView that maintains its aspect ratio to be a desired target value.
+ * <p/>
+ * <p>Depending on the layout, the FixedAspectSurfaceView may not be able to maintain the
+ * requested aspect ratio. This can happen if both the width and the height are exactly
+ * determined by the layout. To avoid this, ensure that either the height or the width is
+ * adjustable by the view; for example, by setting the layout parameters to be WRAP_CONTENT for
+ * the dimension that is best adjusted to maintain the aspect ratio.</p>
+ */
+public class FixedAspectSurfaceView extends SurfaceView {
+
+ /**
+ * Desired width/height ratio
+ */
+ private float mAspectRatio;
+
+ private GestureDetector mGestureDetector;
+
+ public FixedAspectSurfaceView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ // Get initial aspect ratio from custom attributes
+ TypedArray a =
+ context.getTheme().obtainStyledAttributes(attrs,
+ R.styleable.FixedAspectSurfaceView, 0, 0);
+ setAspectRatio(a.getFloat(
+ R.styleable.FixedAspectSurfaceView_aspectRatio, 1.f));
+ a.recycle();
+ }
+
+ /**
+ * Set the desired aspect ratio for this view.
+ *
+ * @param aspect the desired width/height ratio in the current UI orientation. Must be a
+ * positive value.
+ */
+ public void setAspectRatio(float aspect) {
+ if (aspect <= 0) {
+ throw new IllegalArgumentException("Aspect ratio must be positive");
+ }
+ mAspectRatio = aspect;
+ requestLayout();
+ }
+
+ /**
+ * Set a gesture listener to listen for touch events
+ */
+ public void setGestureListener(Context context, GestureDetector.OnGestureListener listener) {
+ if (listener == null) {
+ mGestureDetector = null;
+ } else {
+ mGestureDetector = new GestureDetector(context, listener);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+
+ // General goal: Adjust dimensions to maintain the requested aspect ratio as much
+ // as possible. Depending on the measure specs handed down, this may not be possible
+
+ // Only set one of these to true
+ boolean scaleWidth = false;
+ boolean scaleHeight = false;
+
+ // Sort out which dimension to scale, if either can be. There are 9 combinations of
+ // possible measure specs; a few cases below handle multiple combinations
+ if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
+ // Can't adjust sizes at all, do nothing
+ } else if (widthMode == MeasureSpec.EXACTLY) {
+ // Width is fixed, heightMode either AT_MOST or UNSPECIFIED, so adjust height
+ scaleHeight = true;
+ } else if (heightMode == MeasureSpec.EXACTLY) {
+ // Height is fixed, widthMode either AT_MOST or UNSPECIFIED, so adjust width
+ scaleWidth = true;
+ } else if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
+ // Need to fit into box <= [width, height] in size.
+ // Maximize the View's area while maintaining aspect ratio
+ // This means keeping one dimension as large as possible and shrinking the other
+ float boxAspectRatio = width / (float) height;
+ if (boxAspectRatio > mAspectRatio) {
+ // Box is wider than requested aspect; pillarbox
+ scaleWidth = true;
+ } else {
+ // Box is narrower than requested aspect; letterbox
+ scaleHeight = true;
+ }
+ } else if (widthMode == MeasureSpec.AT_MOST) {
+ // Maximize width, heightSpec is UNSPECIFIED
+ scaleHeight = true;
+ } else if (heightMode == MeasureSpec.AT_MOST) {
+ // Maximize height, widthSpec is UNSPECIFIED
+ scaleWidth = true;
+ } else {
+ // Both MeasureSpecs are UNSPECIFIED. This is probably a pathological layout,
+ // with width == height == 0
+ // but arbitrarily scale height anyway
+ scaleHeight = true;
+ }
+
+ // Do the scaling
+ if (scaleWidth) {
+ width = (int) (height * mAspectRatio);
+ } else if (scaleHeight) {
+ height = (int) (width / mAspectRatio);
+ }
+
+ // Override width/height if needed for EXACTLY and AT_MOST specs
+ width = View.resolveSizeAndState(width, widthMeasureSpec, 0);
+ height = View.resolveSizeAndState(height, heightMeasureSpec, 0);
+
+ // Finally set the calculated dimensions
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (mGestureDetector != null) {
+ return mGestureDetector.onTouchEvent(event);
+ }
+ return false;
+ }
+}
diff --git a/java/tests/CannyLive/src/com/android/example/cannylive/MainActivity.java b/java/tests/CannyLive/src/com/android/example/cannylive/MainActivity.java
new file mode 100644
index 0000000..75d8d47
--- /dev/null
+++ b/java/tests/CannyLive/src/com/android/example/cannylive/MainActivity.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.example.cannylive;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.ViewFlipper;
+
+import com.android.example.cannylive.R;
+
+import java.text.DecimalFormat;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * Main Activity for this app
+ * It presents a ui for setting ISO, Shutter speed, and focus
+ */
+public class MainActivity extends Activity {
+ private static final String TAG = "MainActivity";
+ private static final long ONE_SECOND = 1000000000;
+ private CameraView mPreviewView;
+
+
+ private Timer mTimer;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(new TextView(this));
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+
+ checkPermission();
+ }
+ protected void onPermissionOK() {
+
+ setContentView(R.layout.activity_main);
+ mPreviewView = (CameraView) findViewById(R.id.preview);
+
+ mTimer = new Timer();
+
+ mTimer.scheduleAtFixedRate(new TimerTask() {
+
+ @Override
+ public void run() {
+
+ runOnUiThread(new Runnable() {
+ DecimalFormat df = new DecimalFormat("##.##");
+ public void run() {
+ setTitle("RS Camera (" + df.format( mPreviewView.getFps()) + "fps)");
+ }
+ });
+
+ }
+ }, 250, 250);
+ }
+
+ public static final int PERMISSION_CHECK_ID = 22;
+ private void checkPermission() {
+ String []permission = {
+ Manifest.permission.CAMERA,
+ Manifest.permission.RECORD_AUDIO,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ };
+ boolean permission_ok = true;
+ boolean []permissionDenied = new boolean[permission.length];
+ for (int i = 0; i < permission.length; i++) {
+ String per = permission[i];
+ int permissionCheck = ContextCompat.checkSelfPermission(this,
+ permission[i] );
+ permissionDenied[i] = permissionCheck != PackageManager.PERMISSION_GRANTED;
+ if (permissionDenied[i]) {
+ permission_ok = false;
+ }
+ }
+ Log.v(TAG, "Permission ok = " + permission_ok);
+
+ if (! permission_ok ) {
+ boolean request = false;
+ for (int i = 0; i < permissionDenied.length; i++) {
+ if ( permissionDenied[i]);
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this,
+ permission[i])) {
+ request = true;
+ }
+ }
+ if (request) {
+ Toast.makeText(this, "We need this permission", Toast.LENGTH_LONG).show();
+ finish();
+ } else {
+ ActivityCompat.requestPermissions(this,
+ permission,
+ PERMISSION_CHECK_ID);
+
+ }
+ } else {
+ onPermissionOK();
+ }
+ }
+ public void onRequestPermissionsResult(int requestCode,
+ String permissions[], int[] grantResults) {
+ switch (requestCode) {
+ case PERMISSION_CHECK_ID: {
+ // If request is cancelled, the result arrays are empty.
+ if (grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+
+ onPermissionOK();
+
+ } else {
+ Toast.makeText(this, "We need permissions", Toast.LENGTH_LONG).show();
+
+ finish();
+ }
+ return;
+ }
+
+ // other 'case' lines to check for other
+ // permissions this app might request
+ }
+ }
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (mPreviewView != null) {
+ mPreviewView.resume(this);
+ }
+ }
+
+ public void changeEffectMode(View v) {
+ mPreviewView.changeEffectMode();
+ }
+
+ @Override
+ protected void onPause() {
+ Log.v(TAG,">>>>>>>>>>>>>> onPause()");
+ super.onPause();
+ if (mPreviewView != null) {
+ mPreviewView.pause();
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ Log.v(TAG,">>>>>>>>>>>>>> onStop()");
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ Log.v(TAG,">>>>>>>>>>>>>> onDestroy()");
+
+ super.onDestroy();
+ }
+
+ public void capture(View v) {
+ mPreviewView.takePicture(mPreviewView.getEffect());
+ }
+
+}
diff --git a/java/tests/CannyLive/src/com/android/example/cannylive/MediaStoreSaver.java b/java/tests/CannyLive/src/com/android/example/cannylive/MediaStoreSaver.java
new file mode 100644
index 0000000..ab53417
--- /dev/null
+++ b/java/tests/CannyLive/src/com/android/example/cannylive/MediaStoreSaver.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.example.cannylive;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.provider.MediaStore.Images;
+
+/**
+ * Utility class to save images into android image database
+ */
+public class MediaStoreSaver {
+ public static interface StreamWriter {
+ void write(OutputStream imageOut) throws IOException;
+ }
+
+ public static final String insertImage(ContentResolver contentResolver,
+ Bitmap image,
+ String title,
+ String description) {
+ final Bitmap source = image;
+ StreamWriter streamWriter = new StreamWriter() {
+ public void write(OutputStream imageOut) {
+ source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);
+ }
+ };
+ return insertImage(contentResolver, streamWriter, title, description);
+ }
+
+
+ public static final String insertImage(ContentResolver cr,
+ StreamWriter source,
+ String title,
+ String description) {
+
+ ContentValues values = new ContentValues();
+ values.put(Images.Media.TITLE, title);
+ values.put(Images.Media.DISPLAY_NAME, title);
+ values.put(Images.Media.DESCRIPTION, description);
+ values.put(Images.Media.MIME_TYPE, "image/jpeg");
+ // Add the date meta data to ensure the image is added at the front of the gallery
+ values.put(Images.Media.DATE_ADDED, System.currentTimeMillis());
+ values.put(Images.Media.DATE_TAKEN, System.currentTimeMillis());
+
+ Uri url = null;
+ String stringUrl = null; /* value to be returned */
+
+ try {
+ url = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+
+ if (source != null) {
+ OutputStream imageOut = cr.openOutputStream(url);
+ try {
+ source.write(imageOut);
+ } finally {
+ imageOut.close();
+ }
+
+ long id = ContentUris.parseId(url);
+ // Wait until MINI_KIND thumbnail is generated.
+ Bitmap miniThumb = Images.Thumbnails.getThumbnail(cr, id, Images.Thumbnails.MINI_KIND, null);
+ // This is for backward compatibility.
+ storeThumbnail(cr, miniThumb, id, 50F, 50F, Images.Thumbnails.MICRO_KIND);
+ } else {
+ cr.delete(url, null, null);
+ url = null;
+ }
+ } catch (Exception e) {
+ if (url != null) {
+ cr.delete(url, null, null);
+ url = null;
+ }
+ }
+
+ if (url != null) {
+ stringUrl = url.toString();
+ }
+
+ return stringUrl;
+ }
+
+ /**
+ * A copy of the Android internals StoreThumbnail method, it used with the insertImage to
+ * populate the android.provider.MediaStore.Images.Media#insertImage with all the correct
+ * meta data. The StoreThumbnail method is private so it must be duplicated here.
+ *
+ * @see android.provider.MediaStore.Images.Media (StoreThumbnail private method)
+ */
+ private static final Bitmap storeThumbnail(
+ ContentResolver cr,
+ Bitmap source,
+ long id,
+ float width,
+ float height,
+ int kind) {
+
+ // create the matrix to scale it
+ Matrix matrix = new Matrix();
+
+ float scaleX = width / source.getWidth();
+ float scaleY = height / source.getHeight();
+
+ matrix.setScale(scaleX, scaleY);
+
+ Bitmap thumb = Bitmap.createBitmap(source, 0, 0,
+ source.getWidth(),
+ source.getHeight(), matrix,
+ true
+ );
+
+ ContentValues values = new ContentValues(4);
+ values.put(Images.Thumbnails.KIND, kind);
+ values.put(Images.Thumbnails.IMAGE_ID, (int) id);
+ values.put(Images.Thumbnails.HEIGHT, thumb.getHeight());
+ values.put(Images.Thumbnails.WIDTH, thumb.getWidth());
+
+ Uri url = cr.insert(Images.Thumbnails.EXTERNAL_CONTENT_URI, values);
+
+ try {
+ OutputStream thumbOut = cr.openOutputStream(url);
+ thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut);
+ thumbOut.close();
+ return thumb;
+ } catch (FileNotFoundException ex) {
+ return null;
+ } catch (IOException ex) {
+ return null;
+ }
+ }
+
+
+}
diff --git a/java/tests/CannyLive/src/com/android/example/cannylive/VerticalSeekBar.java b/java/tests/CannyLive/src/com/android/example/cannylive/VerticalSeekBar.java
new file mode 100644
index 0000000..33c4ddf
--- /dev/null
+++ b/java/tests/CannyLive/src/com/android/example/cannylive/VerticalSeekBar.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.example.cannylive;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.SeekBar;
+
+/**
+ * Class to create a vertical slider
+ */
+public class VerticalSeekBar extends SeekBar {
+
+ public VerticalSeekBar(Context context) {
+ super(context);
+ }
+
+ public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public VerticalSeekBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(h, w, oldh, oldw);
+ }
+
+ @Override
+ protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(heightMeasureSpec, widthMeasureSpec);
+ setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
+ }
+
+ protected void onDraw(Canvas c) {
+ c.rotate(-90);
+ c.translate(-getHeight(), 0);
+
+ super.onDraw(c);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (!isEnabled()) {
+ return false;
+ }
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_MOVE:
+ case MotionEvent.ACTION_UP:
+ setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
+ onSizeChanged(getWidth(), getHeight(), 0, 0);
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ break;
+ }
+ return true;
+ }
+}
diff --git a/java/tests/CannyLive/src/com/android/example/cannylive/ViewfinderProcessor.java b/java/tests/CannyLive/src/com/android/example/cannylive/ViewfinderProcessor.java
new file mode 100644
index 0000000..88cedc7
--- /dev/null
+++ b/java/tests/CannyLive/src/com/android/example/cannylive/ViewfinderProcessor.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.example.cannylive;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.ImageFormat;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.Script;
+import android.renderscript.Type;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+
+import java.io.FileNotFoundException;
+import java.text.DecimalFormat;
+
+/**
+ * Renderscript-based Focus peaking viewfinder
+ */
+public class ViewfinderProcessor {
+ private static final String TAG = "ViewfinderProcessor";
+ int mCount;
+ long mLastTime;
+ float mFps;
+ RenderScript mRs;
+ private Allocation mInputAllocation;
+ private Allocation mOutputAllocation;
+ private Allocation mBlurAllocation;
+ private Allocation mEdgeAllocation;
+ private HandlerThread mProcessingThread;
+ private Handler mProcessingHandler;
+ private ScriptC_canny mScriptCanny;
+ public ProcessingTask mProcessingTask;
+ public Allocation mHoughOutput;
+ public Allocation mHoughSlices;
+ private volatile int mMode = 1;
+ DecimalFormat df = new DecimalFormat("###.##");
+
+ public ViewfinderProcessor(RenderScript rs, Size dimensions) {
+ mRs = rs;
+ Type.Builder yuvTypeBuilder = new Type.Builder(rs, Element.YUV(rs));
+ yuvTypeBuilder.setX(dimensions.getWidth());
+ yuvTypeBuilder.setY(dimensions.getHeight());
+ yuvTypeBuilder.setYuvFormat(ImageFormat.YUV_420_888);
+ Log.d(TAG, ">>>>>>>>>>>> " + dimensions.getWidth() + "x" + dimensions.getHeight());
+ mInputAllocation = Allocation.createTyped(rs, yuvTypeBuilder.create(),
+ Allocation.USAGE_IO_INPUT | Allocation.USAGE_SCRIPT);
+
+ Type.Builder rgbTypeBuilder = new Type.Builder(rs, Element.RGBA_8888(rs));
+ rgbTypeBuilder.setX(dimensions.getWidth());
+ rgbTypeBuilder.setY(dimensions.getHeight());
+
+ mOutputAllocation = Allocation.createTyped(rs, rgbTypeBuilder.create(),
+ Allocation.USAGE_IO_OUTPUT | Allocation.USAGE_SCRIPT);
+ Type.Builder buffTypeBuilder = new Type.Builder(rs, Element.U8(rs));
+ buffTypeBuilder.setX(dimensions.getWidth());
+ buffTypeBuilder.setY(dimensions.getHeight());
+ mBlurAllocation = Allocation.createTyped(rs, buffTypeBuilder.create());
+ mEdgeAllocation = Allocation.createTyped(rs, buffTypeBuilder.create());
+
+ mProcessingThread = new HandlerThread("ViewfinderProcessor");
+ mProcessingThread.start();
+ mProcessingHandler = new Handler(mProcessingThread.getLooper());
+ mScriptCanny = new ScriptC_canny(rs);
+ mScriptCanny.set_blurImage(mBlurAllocation);
+ mScriptCanny.set_edgeImage(mEdgeAllocation);
+ mProcessingTask = new ProcessingTask(mInputAllocation);
+
+ int NO_OF_SLICES = 8;
+ int[] slices = new int[NO_OF_SLICES * 2];
+ for (int i = 0; i < NO_OF_SLICES; i++) {
+ int s1 = i * 360 / NO_OF_SLICES;
+ int s2 = ((1 + i) * 360) / NO_OF_SLICES;
+ slices[i * 2] = s1;
+ slices[i * 2 + 1] = s2;
+ }
+ Type.Builder houghSliceBuilder = new Type.Builder(rs, Element.I32_2(rs));
+ houghSliceBuilder.setX(NO_OF_SLICES);
+ mHoughSlices = Allocation.createTyped(rs, houghSliceBuilder.create(), Allocation.USAGE_SCRIPT);
+ mHoughSlices.copyFrom(slices);
+ Type.Builder houghOutputBuilder = new Type.Builder(rs, Element.U8(rs));
+ houghOutputBuilder.setX(800);
+ houghOutputBuilder.setY(360);
+ mHoughOutput = Allocation.createTyped(rs, houghOutputBuilder.create());
+ mScriptCanny.set_hough_output(mHoughOutput);
+
+ }
+
+ public Surface getInputSurface() {
+ return mInputAllocation.getSurface();
+ }
+
+ public void setOutputSurface(Surface output) {
+ mOutputAllocation.setSurface(output);
+ }
+
+ public float getmFps() {
+ return mFps;
+ }
+
+ public void changeEffectMode() {
+ mMode++;
+ }
+
+ public int getMode() {
+ return mMode;
+ }
+
+ volatile boolean mStop = false;
+
+ public void close() {
+
+ mStop = true;
+ }
+
+ /**
+ * Class to process buffer from camera and output to buffer to screen
+ */
+ class ProcessingTask implements Runnable, Allocation.OnBufferAvailableListener {
+ private int mPendingFrames = 0;
+ int mode = -1;
+ private Allocation mInputAllocation;
+
+ public ProcessingTask(Allocation input) {
+ mInputAllocation = input;
+ mInputAllocation.setOnBufferAvailableListener(this);
+ }
+
+ @Override
+ public void onBufferAvailable(Allocation a) {
+ if (mStop) {
+
+ return;
+ }
+ synchronized (this) {
+ mPendingFrames++;
+ mProcessingHandler.post(this);
+ }
+ }
+
+ @Override
+ public void run() {
+ // Find out how many frames have arrived
+ int pendingFrames;
+ synchronized (this) {
+ pendingFrames = mPendingFrames;
+ mPendingFrames = 0;
+
+ // Discard extra messages in case processing is slower than frame rate
+ mProcessingHandler.removeCallbacks(this);
+ }
+ if (mInputAllocation == null) return;
+ // Get to newest input
+ for (int i = 0; i < pendingFrames; i++) {
+ mInputAllocation.ioReceive();
+ }
+ mCount++;
+ mScriptCanny.set_gCurrentFrame(mInputAllocation);
+ long time = System.currentTimeMillis() - mLastTime;
+ if (time > 1000) {
+ mLastTime += time;
+ mFps = mCount * 1000 / (float) (time);
+ mCount = 0;
+ }
+ // Run processing pass
+ mScriptCanny.forEach_getyuv_y(mEdgeAllocation);
+
+ Script.LaunchOptions opt = new Script.LaunchOptions();
+ opt.setX(2, mBlurAllocation.getType().getX() - 2);
+ opt.setY(2, mBlurAllocation.getType().getY() - 2);
+ mScriptCanny.forEach_blur_uchar(mBlurAllocation, opt);
+
+ opt.setX(3, mBlurAllocation.getType().getX() - 3);
+ opt.setY(3, mBlurAllocation.getType().getY() - 3);
+ mScriptCanny.forEach_edge(mEdgeAllocation, opt);
+
+ opt.setX(4, mBlurAllocation.getType().getX() - 4);
+ opt.setY(4, mBlurAllocation.getType().getY() - 4);
+ mScriptCanny.forEach_thin(mBlurAllocation, opt);
+
+ opt.setX(5, mBlurAllocation.getType().getX() - 5);
+ opt.setY(5, mBlurAllocation.getType().getY() - 5);
+ mScriptCanny.forEach_hysteresis(mBlurAllocation, mEdgeAllocation, opt);
+
+ switch (mMode % 6) {
+ case 0:
+ default:
+ long mt = System.nanoTime();
+ mScriptCanny.forEach_black_uchar(mHoughOutput);
+ mScriptCanny.forEach_hough(mHoughSlices);
+ mRs.finish();
+ mt = System.nanoTime() - mt;
+ Log.v(TAG, " hough = " + df.format(mt * 1E-6) + "ms");
+ mScriptCanny.forEach_hough_map(mOutputAllocation);
+ break;
+ case 1:
+ mScriptCanny.forEach_toRGB(mOutputAllocation, opt);
+ break;
+ case 2:
+ mScriptCanny.forEach_toRGBfuzz(mOutputAllocation, opt);
+ break;
+ case 3:
+ mScriptCanny.forEach_toWhiteRGBfuzz(mOutputAllocation, opt);
+ break;
+ case 4:
+ mScriptCanny.forEach_toWhiteRGB(mOutputAllocation, opt);
+ break;
+ case 5:
+ mScriptCanny.forEach_toCartoon(mOutputAllocation, opt);
+ break;
+ }
+ mOutputAllocation.ioSend();
+ if (mStop) {
+ if (mInputAllocation != null) {
+ mInputAllocation.destroy();
+ mInputAllocation = null;
+ }
+ return;
+ }
+ }
+ }
+
+
+ public static void reProcessImage(Context context, String urlName, int type) {
+
+ ContentResolver cr = context.getContentResolver();
+ try {
+ Uri uri = Uri.parse(urlName);
+ Bitmap b = BitmapFactory.decodeStream(cr.openInputStream(uri));
+ processImage(b, context, type);
+
+ MediaStoreSaver.insertImage(cr, b, "canny", "canny filtered image");
+ } catch (FileNotFoundException e) {
+ Log.v(TAG, "S>> Could not open file ");
+ }
+
+ }
+
+ public static void processImage(Bitmap image, Context context, int mMode) {
+ RenderScript mRs = RenderScript.create(context);
+ int width = image.getWidth();
+ int height = image.getHeight();
+ Allocation img_alloc, blur_alloc, edge_alloc;
+ long time = System.nanoTime();
+ img_alloc = Allocation.createFromBitmap(mRs, image);
+
+ Type.Builder buffTypeBuilder = new Type.Builder(mRs, Element.U8(mRs));
+ buffTypeBuilder.setX(width).setY(height);
+ blur_alloc = Allocation.createTyped(mRs, buffTypeBuilder.create());
+ edge_alloc = Allocation.createTyped(mRs, buffTypeBuilder.create());
+
+
+ ScriptC_canny canny_script = new ScriptC_canny(mRs);
+ canny_script.set_blurImage(blur_alloc);
+ canny_script.set_edgeImage(edge_alloc);
+ canny_script.forEach_getLum(img_alloc, edge_alloc);
+
+ Script.LaunchOptions opt = new Script.LaunchOptions();
+ opt.setX(2, blur_alloc.getType().getX() - 2);
+ opt.setY(2, blur_alloc.getType().getY() - 2);
+ canny_script.forEach_blur_uchar(blur_alloc, opt);
+
+ opt.setX(3, blur_alloc.getType().getX() - 3);
+ opt.setY(3, blur_alloc.getType().getY() - 3);
+ canny_script.forEach_edge(edge_alloc, opt);
+
+ opt.setX(4, blur_alloc.getType().getX() - 4);
+ opt.setY(4, blur_alloc.getType().getY() - 4);
+ canny_script.forEach_thin(blur_alloc, opt);
+
+ opt.setX(5, blur_alloc.getType().getX() - 5);
+ opt.setY(5, blur_alloc.getType().getY() - 5);
+
+ canny_script.forEach_hysteresis(blur_alloc, edge_alloc, opt);
+ switch (mMode % 6) {
+ case 0:
+ case 1:
+ default:
+ canny_script.forEach_toRGB(img_alloc, opt);
+ break;
+ case 2:
+ canny_script.forEach_toRGBfuzz(img_alloc, opt);
+ break;
+ case 3:
+ canny_script.forEach_toWhiteRGBfuzz(img_alloc, opt);
+ break;
+ case 4:
+ canny_script.forEach_toWhiteRGB(img_alloc, opt);
+ break;
+ case 5:
+ canny_script.forEach_toRGBCartoon(img_alloc, img_alloc, opt);
+ break;
+ }
+ img_alloc.copyTo(image);
+ time = System.nanoTime() - time;
+ DecimalFormat df = new DecimalFormat("###.#");
+ String ts = df.format(time * 1E-6) + "ms";
+ Log.v(TAG, "processed a " + width + "x" + height + " in " + ts);
+ }
+
+}
diff --git a/java/tests/CannyLive/src/com/android/example/cannylive/canny.rs b/java/tests/CannyLive/src/com/android/example/cannylive/canny.rs
new file mode 100644
index 0000000..82b14f1
--- /dev/null
+++ b/java/tests/CannyLive/src/com/android/example/cannylive/canny.rs
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.example.cannylive)
+#pragma rs_fp_relaxed
+
+rs_allocation gCurrentFrame; // yuv
+rs_allocation gCurrentRGBFrame; // RGB
+rs_allocation blurImage; // uchar
+rs_allocation edgeImage; // uchar
+
+ushort __attribute__((kernel)) black_ushort() { return 0; }
+uchar __attribute__((kernel)) black_uchar() { return 0; }
+uchar4 __attribute__((kernel)) black_uchar4() { return 0; }
+
+uchar4 __attribute__((kernel)) toRGB(uint32_t x, uint32_t y) {
+ ushort v = rsGetElementAt_uchar(edgeImage, x, y);
+
+ if (true) {
+ uchar charv = clamp(v * 10, 0, 255);
+ uchar4 out = {charv, charv, charv, 255};
+ return out;
+ }
+}
+uchar4 __attribute__((kernel)) toWhiteRGB(uint32_t x, uint32_t y) {
+ ushort v = rsGetElementAt_uchar(edgeImage, x, y);
+
+ uchar charv = 255 - clamp(v * 10, 0, 255);
+ uchar4 out = {charv, charv, charv, 255};
+ return out;
+}
+uchar4 __attribute__((kernel)) toRGBfuzz(uint32_t x, uint32_t y) {
+ ushort v = rsGetElementAt_uchar(blurImage, x, y);
+
+ if (true) {
+ uchar charv = clamp(v * 10, 0, 255);
+ uchar4 out = {charv, charv, charv, 255};
+ return out;
+ }
+}
+uchar4 __attribute__((kernel)) toWhiteRGBfuzz(uint32_t x, uint32_t y) {
+ ushort v = rsGetElementAt_uchar(blurImage, x, y);
+
+ uchar charv = 255 - clamp(v * 10, 0, 255);
+ uchar4 out = {charv, charv, charv, 255};
+ return out;
+}
+
+uchar4 __attribute__((kernel)) toRGBCartoon(uchar4 in, uint32_t x, uint32_t y) {
+ ushort v = rsGetElementAt_uchar(blurImage, x, y);
+
+ return in - (uchar)(v * 10);
+}
+
+uchar4 __attribute__((kernel)) toCartoon(uint32_t x, uint32_t y) {
+ ushort v = rsGetElementAt_uchar(blurImage, x, y);
+
+ uchar4 yuv;
+ yuv.r =
+ clamp(rsGetElementAtYuv_uchar_Y(gCurrentFrame, x, y) - v * 10, 0, 255);
+
+ yuv.g = rsGetElementAtYuv_uchar_U(gCurrentFrame, x, y);
+ yuv.b = rsGetElementAtYuv_uchar_V(gCurrentFrame, x, y);
+ yuv.a = 255;
+
+ int4 rgb;
+ rgb.r = yuv.r + yuv.b * 1436 / 1024 - 179;
+ rgb.g = yuv.r - yuv.g * 46549 / 131072 + 44 - yuv.b * 93604 / 131072 + 91;
+ rgb.b = yuv.r + yuv.g * 1814 / 1024 - 227;
+ rgb.a = 255;
+
+ // Write out merged HDR result
+ uchar4 out = convert_uchar4(clamp(rgb, 0, 255));
+ return out;
+}
+
+static ushort getY(rs_allocation a, int x, int y) {
+ ushort3 v = convert_ushort3(rsGetElementAt_uchar4(a, x, y).xyz);
+ return v.x + v.y + v.z;
+}
+
+uchar __attribute__((kernel)) getLum(uchar4 in) {
+ return (in.x + in.y + in.z) / 3;
+}
+
+uchar __attribute__((kernel)) getyuv_y(uint32_t x, uint32_t y) {
+ return rsGetElementAtYuv_uchar_Y(gCurrentFrame, x, y);
+}
+
+uchar __attribute__((kernel)) blur_uchar(uint32_t x, uint32_t y) {
+ ushort sum = 0;
+ int x1 = x - 2;
+ int y1 = y - 2;
+ sum += 2 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 4 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 5 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 4 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 2 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ x1 = x - 2;
+ y1++;
+ sum += 4 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 9 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 12 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 9 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 4 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+
+ x1 = x - 2;
+ y1++;
+ sum += 5 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 12 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 15 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 12 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 5 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+
+ x1 = x - 2;
+ y1++;
+ sum += 4 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 9 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 12 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 9 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 4 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+
+ x1 = x - 2;
+ y1++;
+ sum += 2 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 4 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 5 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 4 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+ sum += 2 * rsGetElementAt_uchar(edgeImage, x1++, y1);
+
+ return (uchar)(sum / 159);
+}
+
+ushort __attribute__((kernel)) blurRGB(uint32_t x, uint32_t y) {
+ ushort sum = 0;
+ int x1 = x - 2;
+ int y1 = y - 2;
+ sum += 2 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 4 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 5 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 4 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 2 * getY(gCurrentRGBFrame, x1++, y1);
+ x1 = x - 2;
+ y1++;
+ sum += 4 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 9 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 12 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 9 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 4 * getY(gCurrentRGBFrame, x1++, y1);
+
+ x1 = x - 2;
+ y1++;
+ sum += 5 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 12 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 15 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 12 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 5 * getY(gCurrentRGBFrame, x1++, y1);
+
+ x1 = x - 2;
+ y1++;
+ sum += 4 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 9 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 12 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 9 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 4 * getY(gCurrentRGBFrame, x1++, y1);
+
+ x1 = x - 2;
+ y1++;
+ sum += 2 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 4 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 5 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 4 * getY(gCurrentRGBFrame, x1++, y1);
+ sum += 2 * getY(gCurrentRGBFrame, x1++, y1);
+
+ return sum / 159;
+}
+
+ushort __attribute__((kernel)) blur(uint32_t x, uint32_t y) {
+ ushort sum = 0;
+ int x1 = x - 2;
+ int y1 = y - 2;
+ sum += 2 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 4 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 5 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 4 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 2 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ x1 = x - 2;
+ y1++;
+ sum += 4 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 9 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 12 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 9 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 4 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+
+ x1 = x - 2;
+ y1++;
+ sum += 5 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 12 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 15 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 12 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 5 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+
+ x1 = x - 2;
+ y1++;
+ sum += 4 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 9 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 12 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 9 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 4 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+
+ x1 = x - 2;
+ y1++;
+ sum += 2 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 4 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 5 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 4 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+ sum += 2 * rsGetElementAtYuv_uchar_Y(gCurrentFrame, x1++, y1);
+
+ return sum / 159;
+}
+
+uchar __attribute__((kernel)) edge(uint32_t x, uint32_t y) {
+ int sum_h = 0;
+ int x1 = x - 1;
+ int y1 = y - 1;
+ sum_h += -rsGetElementAt_uchar(blurImage, x1, y1);
+ x += 2;
+ sum_h += rsGetElementAt_uchar(blurImage, x1, y1);
+ x1 = x - 1;
+ y1++;
+
+ sum_h += -2 * rsGetElementAt_uchar(blurImage, x1, y1);
+ x1 += 2;
+ sum_h += 2 * rsGetElementAt_uchar(blurImage, x1, y1);
+ x1 = x - 1;
+ y1++;
+
+ sum_h += -rsGetElementAt_uchar(blurImage, x1, y1);
+ x1 += 2;
+ sum_h += rsGetElementAt_uchar(blurImage, x1, y1);
+ int sum_v = 0;
+
+ x1 = x - 1;
+ y1 = y - 1;
+ sum_v += -rsGetElementAt_uchar(blurImage, x1++, y1);
+ sum_v += -2 * rsGetElementAt_uchar(blurImage, x1++, y1);
+ sum_v += -rsGetElementAt_uchar(blurImage, x1++, y1);
+
+ x1 = x - 1;
+ y1 += 2;
+ sum_v += rsGetElementAt_uchar(blurImage, x1++, y1);
+ sum_v += 2 * rsGetElementAt_uchar(blurImage, x1++, y1);
+ sum_v += rsGetElementAt_uchar(blurImage, x1++, y1);
+ int v = (int)native_hypot((float)sum_v, (float)sum_h);
+ v = v & (~3);
+ int dir = (int)(4 * native_atan2pi((float)sum_v, (float)sum_h) + 8.5f);
+
+ return (uchar)(v + (dir & 0x3));
+}
+
+uchar __attribute__((kernel)) thin(uint32_t x, uint32_t y) {
+ int value = rsGetElementAt_uchar(edgeImage, x, y);
+
+ int dir = value & 0x3;
+ int dx[4] = {1, 1, 0, 1};
+ int dy[4] = {0, 1, 1, -1};
+ if (value < rsGetElementAt_uchar(edgeImage, x + dx[dir], y + dy[dir])) {
+ return 0;
+ }
+ if (value < rsGetElementAt_uchar(edgeImage, x - dx[dir], y - dy[dir])) {
+ return 0;
+ }
+ return value >> 2;
+}
+
+static const short dark_line = 4;
+static const short bright_line = 16;
+#define q_add(v) \
+ { \
+ if (q_size < 64) { \
+ queue[q_start & 0x3F] = v; \
+ q_start++; \
+ q_size++; \
+ } \
+ }
+#define q_peak() (queue[q_peek_pos & 0x3F])
+#define q_pop() (q_size--, q_tmp = queue[q_end], q_end++, q_tmp)
+
+uchar __attribute__((kernel)) hysteresis(uchar in, uint32_t x, uint32_t y) {
+ short3 queue[64];
+ int q_start = 0;
+ int q_end = 0;
+ int q_size = 0;
+ short3 q_tmp;
+ int q_peek_pos = 0;
+ int dx[] = {-1, 0, 1, -1, 1, -1, 0, 1};
+ int dy[] = {-1, -1, -1, 0, 0, 1, 1, 1};
+ if (in < dark_line) {
+ return 0;
+ }
+ if (in >= bright_line) {
+ return in;
+ }
+ short3 p = {0, 0, 1};
+ q_add(p);
+
+ while (q_peek_pos != q_start) {
+ if (q_size > 60) {
+ return 0;
+ }
+ p = q_peak();
+ q_peek_pos++;
+ short cdx = p[0];
+ short cdy = p[1];
+
+ int p_dist = p.z;
+ short v;
+ while (queue[q_end & 0x3F].z < p_dist - 1) {
+ q_pop();
+ }
+ for (int i = 0; i < 8; i++) {
+ int tx = cdx + dx[i];
+ int ty = cdy + dy[i];
+ bool skip = false;
+ for (int k = q_end; k < q_start; k++) {
+ short3 tq = queue[k & 0x3F];
+ if (tq.x == tx && tq.y == ty) {
+ skip = true;
+ break;
+ }
+ }
+ if (!(tx + x >= -5 && tx <= 5 && ty >= -5 && ty <= 5)) {
+ skip = true;
+ }
+ if (skip) continue;
+
+ v = rsGetElementAt_uchar(edgeImage, tx + x, ty + y);
+ if (v >= bright_line) {
+ return in;
+ }
+ if (v >= dark_line) {
+ short3 tmp = {tx, ty, p_dist + 1};
+ q_add(tmp);
+ }
+ q_peek_pos = (q_peek_pos + 1) & 0x3F;
+ }
+ }
+ return 0;
+}
+
+rs_allocation hough_output; // width * height
+
+static inline float2 cos_sin(int n) {
+ float2 cs[] = {{1.0f,0.0f},
+ {0.9999619f,0.008726535f},{0.9998477f,0.017452406f},{0.99965733f,0.026176948f},
+ {0.99939084f,0.034899496f},{0.99904823f,0.043619387f},{0.9986295f,0.052335955f},
+ {0.9981348f,0.06104854f},{0.9975641f,0.06975647f},{0.9969173f,0.0784591f},
+ {0.9961947f,0.087155744f},{0.9953962f,0.09584575f},{0.9945219f,0.104528464f},
+ {0.9935719f,0.11320321f},{0.99254614f,0.12186934f},{0.9914449f,0.13052619f},
+ {0.99026805f,0.1391731f},{0.9890159f,0.14780942f},{0.98768836f,0.15643446f},
+ {0.9862856f,0.1650476f},{0.9848077f,0.17364818f},{0.9832549f,0.18223552f},
+ {0.98162717f,0.190809f},{0.9799247f,0.19936794f},{0.9781476f,0.20791169f},
+ {0.976296f,0.21643962f},{0.97437006f,0.22495106f},{0.9723699f,0.23344536f},
+ {0.9702957f,0.2419219f},{0.96814764f,0.25038f},{0.9659258f,0.25881904f},
+ {0.96363044f,0.26723838f},{0.9612617f,0.27563736f},{0.95881975f,0.28401536f},
+ {0.9563047f,0.2923717f},{0.95371693f,0.3007058f},{0.95105654f,0.309017f},
+ {0.94832367f,0.31730467f},{0.94551855f,0.32556817f},{0.9426415f,0.33380687f},
+ {0.9396926f,0.34202015f},{0.9366722f,0.3502074f},{0.9335804f,0.35836795f},
+ {0.9304176f,0.3665012f},{0.92718387f,0.37460658f},{0.9238795f,0.38268343f},
+ {0.92050487f,0.39073113f},{0.9170601f,0.39874908f},{0.9135454f,0.40673664f},
+ {0.9099613f,0.41469324f},{0.9063078f,0.42261827f},{0.90258527f,0.4305111f},
+ {0.89879405f,0.43837115f},{0.89493436f,0.4461978f},{0.8910065f,0.4539905f},
+ {0.8870108f,0.4617486f},{0.88294756f,0.46947157f},{0.87881714f,0.47715876f},
+ {0.8746197f,0.4848096f},{0.8703557f,0.49242356f},{0.8660254f,0.5f},
+ {0.8616292f,0.5075384f},{0.8571673f,0.5150381f},{0.85264015f,0.52249855f},
+ {0.8480481f,0.52991927f},{0.8433914f,0.53729963f},{0.83867055f,0.54463905f},
+ {0.83388585f,0.551937f},{0.82903755f,0.5591929f},{0.8241262f,0.56640625f},
+ {0.81915206f,0.57357645f},{0.8141155f,0.58070296f},{0.809017f,0.58778524f},
+ {0.80385685f,0.59482276f},{0.7986355f,0.60181504f},{0.7933533f,0.6087614f},
+ {0.7880108f,0.6156615f},{0.78260815f,0.62251467f},{0.777146f,0.6293204f},
+ {0.77162457f,0.63607824f},{0.76604444f,0.64278764f},{0.76040596f,0.64944804f},
+ {0.7547096f,0.656059f},{0.7489557f,0.66262007f},{0.7431448f,0.6691306f},
+ {0.7372773f,0.6755902f},{0.7313537f,0.6819984f},{0.7253744f,0.68835455f},
+ {0.7193398f,0.6946584f},{0.71325046f,0.70090926f},{0.70710677f,0.70710677f},
+ {0.70090926f,0.71325046f},{0.6946584f,0.7193398f},{0.68835455f,0.7253744f},
+ {0.6819984f,0.7313537f},{0.6755902f,0.7372773f},{0.6691306f,0.7431448f},
+ {0.66262007f,0.7489557f},{0.656059f,0.7547096f},{0.64944804f,0.76040596f},
+ {0.64278764f,0.76604444f},{0.63607824f,0.77162457f},{0.6293204f,0.777146f},
+ {0.62251467f,0.78260815f},{0.6156615f,0.7880108f},{0.6087614f,0.7933533f},
+ {0.60181504f,0.7986355f},{0.59482276f,0.80385685f},{0.58778524f,0.809017f},
+ {0.58070296f,0.8141155f},{0.57357645f,0.81915206f},{0.56640625f,0.8241262f},
+ {0.5591929f,0.82903755f},{0.551937f,0.83388585f},{0.54463905f,0.83867055f},
+ {0.53729963f,0.8433914f},{0.52991927f,0.8480481f},{0.52249855f,0.85264015f},
+ {0.5150381f,0.8571673f},{0.5075384f,0.8616292f},{0.5f,0.8660254f},
+ {0.49242356f,0.8703557f},{0.4848096f,0.8746197f},{0.47715876f,0.87881714f},
+ {0.46947157f,0.88294756f},{0.4617486f,0.8870108f},{0.4539905f,0.8910065f},
+ {0.4461978f,0.89493436f},{0.43837115f,0.89879405f},{0.4305111f,0.90258527f},
+ {0.42261827f,0.9063078f},{0.41469324f,0.9099613f},{0.40673664f,0.9135454f},
+ {0.39874908f,0.9170601f},{0.39073113f,0.92050487f},{0.38268343f,0.9238795f},
+ {0.37460658f,0.92718387f},{0.3665012f,0.9304176f},{0.35836795f,0.9335804f},
+ {0.3502074f,0.9366722f},{0.34202015f,0.9396926f},{0.33380687f,0.9426415f},
+ {0.32556817f,0.94551855f},{0.31730467f,0.94832367f},{0.309017f,0.95105654f},
+ {0.3007058f,0.95371693f},{0.2923717f,0.9563047f},{0.28401536f,0.95881975f},
+ {0.27563736f,0.9612617f},{0.26723838f,0.96363044f},{0.25881904f,0.9659258f},
+ {0.25038f,0.96814764f},{0.2419219f,0.9702957f},{0.23344536f,0.9723699f},
+ {0.22495106f,0.97437006f},{0.21643962f,0.976296f},{0.20791169f,0.9781476f},
+ {0.19936794f,0.9799247f},{0.190809f,0.98162717f},{0.18223552f,0.9832549f},
+ {0.17364818f,0.9848077f},{0.1650476f,0.9862856f},{0.15643446f,0.98768836f},
+ {0.14780942f,0.9890159f},{0.1391731f,0.99026805f},{0.13052619f,0.9914449f},
+ {0.12186934f,0.99254614f},{0.11320321f,0.9935719f},{0.104528464f,0.9945219f},
+ {0.09584575f,0.9953962f},{0.087155744f,0.9961947f},{0.0784591f,0.9969173f},
+ {0.06975647f,0.9975641f},{0.06104854f,0.9981348f},{0.052335955f,0.9986295f},
+ {0.043619387f,0.99904823f},{0.034899496f,0.99939084f},{0.026176948f,0.99965733f},
+ {0.017452406f,0.9998477f},{0.008726535f,0.9999619f},{6.123234E-17f,1.0f},
+ {-0.008726535f,0.9999619f},{-0.017452406f,0.9998477f},{-0.026176948f,0.99965733f},
+ {-0.034899496f,0.99939084f},{-0.043619387f,0.99904823f},{-0.052335955f,0.9986295f},
+ {-0.06104854f,0.9981348f},{-0.06975647f,0.9975641f},{-0.0784591f,0.9969173f},
+ {-0.087155744f,0.9961947f},{-0.09584575f,0.9953962f},{-0.104528464f,0.9945219f},
+ {-0.11320321f,0.9935719f},{-0.12186934f,0.99254614f},{-0.13052619f,0.9914449f},
+ {-0.1391731f,0.99026805f},{-0.14780942f,0.9890159f},{-0.15643446f,0.98768836f},
+ {-0.1650476f,0.9862856f},{-0.17364818f,0.9848077f},{-0.18223552f,0.9832549f},
+ {-0.190809f,0.98162717f},{-0.19936794f,0.9799247f},{-0.20791169f,0.9781476f},
+ {-0.21643962f,0.976296f},{-0.22495106f,0.97437006f},{-0.23344536f,0.9723699f},
+ {-0.2419219f,0.9702957f},{-0.25038f,0.96814764f},{-0.25881904f,0.9659258f},
+ {-0.26723838f,0.96363044f},{-0.27563736f,0.9612617f},{-0.28401536f,0.95881975f},
+ {-0.2923717f,0.9563047f},{-0.3007058f,0.95371693f},{-0.309017f,0.95105654f},
+ {-0.31730467f,0.94832367f},{-0.32556817f,0.94551855f},{-0.33380687f,0.9426415f},
+ {-0.34202015f,0.9396926f},{-0.3502074f,0.9366722f},{-0.35836795f,0.9335804f},
+ {-0.3665012f,0.9304176f},{-0.37460658f,0.92718387f},{-0.38268343f,0.9238795f},
+ {-0.39073113f,0.92050487f},{-0.39874908f,0.9170601f},{-0.40673664f,0.9135454f},
+ {-0.41469324f,0.9099613f},{-0.42261827f,0.9063078f},{-0.4305111f,0.90258527f},
+ {-0.43837115f,0.89879405f},{-0.4461978f,0.89493436f},{-0.4539905f,0.8910065f},
+ {-0.4617486f,0.8870108f},{-0.46947157f,0.88294756f},{-0.47715876f,0.87881714f},
+ {-0.4848096f,0.8746197f},{-0.49242356f,0.8703557f},{-0.5f,0.8660254f},
+ {-0.5075384f,0.8616292f},{-0.5150381f,0.8571673f},{-0.52249855f,0.85264015f},
+ {-0.52991927f,0.8480481f},{-0.53729963f,0.8433914f},{-0.54463905f,0.83867055f},
+ {-0.551937f,0.83388585f},{-0.5591929f,0.82903755f},{-0.56640625f,0.8241262f},
+ {-0.57357645f,0.81915206f},{-0.58070296f,0.8141155f},{-0.58778524f,0.809017f},
+ {-0.59482276f,0.80385685f},{-0.60181504f,0.7986355f},{-0.6087614f,0.7933533f},
+ {-0.6156615f,0.7880108f},{-0.62251467f,0.78260815f},{-0.6293204f,0.777146f},
+ {-0.63607824f,0.77162457f},{-0.64278764f,0.76604444f},{-0.64944804f,0.76040596f},
+ {-0.656059f,0.7547096f},{-0.66262007f,0.7489557f},{-0.6691306f,0.7431448f},
+ {-0.6755902f,0.7372773f},{-0.6819984f,0.7313537f},{-0.68835455f,0.7253744f},
+ {-0.6946584f,0.7193398f},{-0.70090926f,0.71325046f},{-0.70710677f,0.70710677f},
+ {-0.71325046f,0.70090926f},{-0.7193398f,0.6946584f},{-0.7253744f,0.68835455f},
+ {-0.7313537f,0.6819984f},{-0.7372773f,0.6755902f},{-0.7431448f,0.6691306f},
+ {-0.7489557f,0.66262007f},{-0.7547096f,0.656059f},{-0.76040596f,0.64944804f},
+ {-0.76604444f,0.64278764f},{-0.77162457f,0.63607824f},{-0.777146f,0.6293204f},
+ {-0.78260815f,0.62251467f},{-0.7880108f,0.6156615f},{-0.7933533f,0.6087614f},
+ {-0.7986355f,0.60181504f},{-0.80385685f,0.59482276f},{-0.809017f,0.58778524f},
+ {-0.8141155f,0.58070296f},{-0.81915206f,0.57357645f},{-0.8241262f,0.56640625f},
+ {-0.82903755f,0.5591929f},{-0.83388585f,0.551937f},{-0.83867055f,0.54463905f},
+ {-0.8433914f,0.53729963f},{-0.8480481f,0.52991927f},{-0.85264015f,0.52249855f},
+ {-0.8571673f,0.5150381f},{-0.8616292f,0.5075384f},{-0.8660254f,0.5f},
+ {-0.8703557f,0.49242356f},{-0.8746197f,0.4848096f},{-0.87881714f,0.47715876f},
+ {-0.88294756f,0.46947157f},{-0.8870108f,0.4617486f},{-0.8910065f,0.4539905f},
+ {-0.89493436f,0.4461978f},{-0.89879405f,0.43837115f},{-0.90258527f,0.4305111f},
+ {-0.9063078f,0.42261827f},{-0.9099613f,0.41469324f},{-0.9135454f,0.40673664f},
+ {-0.9170601f,0.39874908f},{-0.92050487f,0.39073113f},{-0.9238795f,0.38268343f},
+ {-0.92718387f,0.37460658f},{-0.9304176f,0.3665012f},{-0.9335804f,0.35836795f},
+ {-0.9366722f,0.3502074f},{-0.9396926f,0.34202015f},{-0.9426415f,0.33380687f},
+ {-0.94551855f,0.32556817f},{-0.94832367f,0.31730467f},{-0.95105654f,0.309017f},
+ {-0.95371693f,0.3007058f},{-0.9563047f,0.2923717f},{-0.95881975f,0.28401536f},
+ {-0.9612617f,0.27563736f},{-0.96363044f,0.26723838f},{-0.9659258f,0.25881904f},
+ {-0.96814764f,0.25038f},{-0.9702957f,0.2419219f},{-0.9723699f,0.23344536f},
+ {-0.97437006f,0.22495106f},{-0.976296f,0.21643962f},{-0.9781476f,0.20791169f},
+ {-0.9799247f,0.19936794f},{-0.98162717f,0.190809f},{-0.9832549f,0.18223552f},
+ {-0.9848077f,0.17364818f},{-0.9862856f,0.1650476f},{-0.98768836f,0.15643446f},
+ {-0.9890159f,0.14780942f},{-0.99026805f,0.1391731f},{-0.9914449f,0.13052619f},
+ {-0.99254614f,0.12186934f},{-0.9935719f,0.11320321f},{-0.9945219f,0.104528464f},
+ {-0.9953962f,0.09584575f},{-0.9961947f,0.087155744f},{-0.9969173f,0.0784591f},
+ {-0.9975641f,0.06975647f},{-0.9981348f,0.06104854f},{-0.9986295f,0.052335955f},
+ {-0.99904823f,0.043619387f},{-0.99939084f,0.034899496f},{-0.99965733f,0.026176948f},
+ {-0.9998477f,0.017452406f},{-0.9999619f,0.008726535f},};
+ return cs[n];
+}
+
+uchar4 __attribute__((kernel)) hough_map(uint32_t x, uint32_t y) {
+ int w = rsAllocationGetDimX(hough_output);
+ int h = rsAllocationGetDimY(hough_output);
+ int ix = clamp((int)x, 0, w - 1);
+ int iy = clamp((int)y, 0, h - 1);
+ int v = rsGetElementAt_uchar(hough_output, ix, iy);
+ uchar4 out = {clamp(v * 5, 0, 255), clamp(v, 0, 255), clamp(v, 0, 255), 255};
+ return out;
+}
+
+/* input is the range of angles to cover */
+void __attribute__((kernel)) hough(int2 in) {
+ int max_pos = rsAllocationGetDimX(hough_output);
+ int pos_shift = max_pos >> 1;
+ int start = in.x;
+ int end = in.y;
+ int width = rsAllocationGetDimX(edgeImage);
+ int height = rsAllocationGetDimY(edgeImage);
+ float half_height = height >> 1;
+ float half_width = width >> 1;
+ for (int iy = 0; iy < height; iy++) {
+ for (int ix = 0; ix < width; ix++) {
+ if (rsGetElementAt_uchar(edgeImage, ix, iy) > 20) {
+ float2 pos = {ix - half_width, iy - half_height};
+ for (int ang = start; ang < end; ang++) {
+ int r = pos_shift + (int)dot(pos, cos_sin(ang));
+ if (r >= 0 && r < max_pos) {
+ ushort tmp = rsGetElementAt_uchar(hough_output, r, ang);
+ rsSetElementAt_uchar(hough_output, tmp + 1, r, ang);
+ }
+ }
+ }
+ }
+ }
+}
+
+uchar __attribute__((kernel)) hough_thin(uchar in, int x, int y) {
+ short tmp = rsGetElementAt_ushort(hough_output, x - 1, y - 1);
+ if (tmp > in) return 0;
+ tmp = rsGetElementAt_uchar(hough_output, x + 1, y + 1);
+ if (tmp > in) return 0;
+
+ tmp = rsGetElementAt_uchar(hough_output, x, y - 1);
+ if (tmp > in) return 0;
+ tmp = rsGetElementAt_uchar(hough_output, x + 1, y - 1);
+ if (tmp > in) return 0;
+
+ tmp = rsGetElementAt_uchar(hough_output, x - 1, y);
+ if (tmp > in) return 0;
+
+ tmp = rsGetElementAt_uchar(hough_output, x + 1, y);
+ if (tmp > in) return 0;
+
+ tmp = rsGetElementAt_uchar(hough_output, x - 1, y + 1);
+ if (tmp > in) return 0;
+ tmp = rsGetElementAt_uchar(hough_output, x, y + 1);
+ if (tmp > in) return 0;
+ return in;
+}