diff options
author | John Hoford <hoford@google.com> | 2015-04-01 23:22:06 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2015-04-01 23:22:06 +0000 |
commit | 826aebc96c13d951653d14c05d9f55ec1283ec30 (patch) | |
tree | ab88a210235fa87351c6535dec943e2ec826d732 | |
parent | 9270cd93e444d11d6e1b49653613409f34a0cc35 (diff) | |
parent | fbb9dd1843197a0d2f7fcda29abbe9d170682a5d (diff) | |
download | rs-826aebc96c13d951653d14c05d9f55ec1283ec30.tar.gz |
Merge "inital checkin of RenderScript Camera Demo"
27 files changed, 2267 insertions, 0 deletions
diff --git a/java/tests/RsCameraDemo/Android.mk b/java/tests/RsCameraDemo/Android.mk new file mode 100644 index 00000000..877d2d29 --- /dev/null +++ b/java/tests/RsCameraDemo/Android.mk @@ -0,0 +1,27 @@ +# +# 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_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src) +LOCAL_SDK_VERSION := 21 + +LOCAL_PACKAGE_NAME := RsCameraDemo + +include $(BUILD_PACKAGE) diff --git a/java/tests/RsCameraDemo/AndroidManifest.xml b/java/tests/RsCameraDemo/AndroidManifest.xml new file mode 100644 index 00000000..2dd259d3 --- /dev/null +++ b/java/tests/RsCameraDemo/AndroidManifest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.example.rscamera.rscamera" >
+ <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.rscamera.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/RsCameraDemo/_index.html b/java/tests/RsCameraDemo/_index.html new file mode 100644 index 00000000..5f8c5f7e --- /dev/null +++ b/java/tests/RsCameraDemo/_index.html @@ -0,0 +1,6 @@ +<h1>RenderScript Camera Demo</h1> +<h2>Am example camera with live processing in RenderScript</h2> +<p> +This demonstrates focus peaking implemented in RenderScript +</p> + diff --git a/java/tests/RsCameraDemo/res/anim/slide_in_from_left.xml b/java/tests/RsCameraDemo/res/anim/slide_in_from_left.xml new file mode 100644 index 00000000..75d05bb9 --- /dev/null +++ b/java/tests/RsCameraDemo/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/RsCameraDemo/res/anim/slide_out_to_right.xml b/java/tests/RsCameraDemo/res/anim/slide_out_to_right.xml new file mode 100644 index 00000000..20ee06fc --- /dev/null +++ b/java/tests/RsCameraDemo/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/RsCameraDemo/res/drawable-hdpi/camera.png b/java/tests/RsCameraDemo/res/drawable-hdpi/camera.png Binary files differnew file mode 100644 index 00000000..59908a68 --- /dev/null +++ b/java/tests/RsCameraDemo/res/drawable-hdpi/camera.png diff --git a/java/tests/RsCameraDemo/res/drawable-mdpi/camera.png b/java/tests/RsCameraDemo/res/drawable-mdpi/camera.png Binary files differnew file mode 100644 index 00000000..228cb966 --- /dev/null +++ b/java/tests/RsCameraDemo/res/drawable-mdpi/camera.png diff --git a/java/tests/RsCameraDemo/res/drawable-xhdpi/camera.png b/java/tests/RsCameraDemo/res/drawable-xhdpi/camera.png Binary files differnew file mode 100644 index 00000000..ca6c9f80 --- /dev/null +++ b/java/tests/RsCameraDemo/res/drawable-xhdpi/camera.png diff --git a/java/tests/RsCameraDemo/res/drawable-xxhdpi/camera.png b/java/tests/RsCameraDemo/res/drawable-xxhdpi/camera.png Binary files differnew file mode 100644 index 00000000..6534f06a --- /dev/null +++ b/java/tests/RsCameraDemo/res/drawable-xxhdpi/camera.png diff --git a/java/tests/RsCameraDemo/res/drawable/ic_back.xml b/java/tests/RsCameraDemo/res/drawable/ic_back.xml new file mode 100644 index 00000000..1cbfdb22 --- /dev/null +++ b/java/tests/RsCameraDemo/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/RsCameraDemo/res/drawable/ic_cam.xml b/java/tests/RsCameraDemo/res/drawable/ic_cam.xml new file mode 100644 index 00000000..fbeedc68 --- /dev/null +++ b/java/tests/RsCameraDemo/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/RsCameraDemo/res/layout/activity_main.xml b/java/tests/RsCameraDemo/res/layout/activity_main.xml new file mode 100644 index 00000000..8ad2c0ef --- /dev/null +++ b/java/tests/RsCameraDemo/res/layout/activity_main.xml @@ -0,0 +1,120 @@ +<?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.rscamera.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" />
+
+
+ <ViewFlipper
+ android:layout_height="match_parent"
+ android:layout_width="0px"
+ android:id="@+id/viewFlipper"
+ android:layout_weight="1">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:id="@+id/control_bar_contents">
+
+ <Button
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:id="@+id/iso"
+ android:layout_gravity="center_horizontal"
+ android:onClick="setISO"
+ android:text="A ISO 1600" />
+
+ <Button
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:id="@+id/speed"
+ android:layout_gravity="center_horizontal"
+ android:onClick="setShutterSpeed"
+ android:text="A 1/30 s" />
+
+ <Button
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:id="@+id/focus"
+ android:layout_gravity="center_horizontal"
+ android:onClick="setFocus"
+ android:text="A 12.0m" />
+
+ <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
+ android:orientation="vertical"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:id="@+id/slide">
+
+ <ImageButton
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:id="@+id/back"
+ android:layout_gravity="center_horizontal"
+ android:onClick="back"
+ android:src="@drawable/ic_back"
+ />
+
+ <ImageButton
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:id="@+id/capture"
+ android:layout_gravity="center_horizontal"
+ android:onClick="capture"
+ android:src="@drawable/ic_cam" />
+
+ <RelativeLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+
+ <com.android.example.rscamera.VerticalSeekBar
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:id="@+id/focusbar"
+ android:layout_alignParentTop="true"
+ android:layout_centerInParent="true" />
+
+ </RelativeLayout>
+
+ </LinearLayout>
+
+ </ViewFlipper>
+
+</LinearLayout>
\ No newline at end of file diff --git a/java/tests/RsCameraDemo/res/values-v21/styles.xml b/java/tests/RsCameraDemo/res/values-v21/styles.xml new file mode 100644 index 00000000..f9166cca --- /dev/null +++ b/java/tests/RsCameraDemo/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/RsCameraDemo/res/values-w820dp/dimens.xml b/java/tests/RsCameraDemo/res/values-w820dp/dimens.xml new file mode 100644 index 00000000..62df1875 --- /dev/null +++ b/java/tests/RsCameraDemo/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/RsCameraDemo/res/values/attrs.xml b/java/tests/RsCameraDemo/res/values/attrs.xml new file mode 100644 index 00000000..92216075 --- /dev/null +++ b/java/tests/RsCameraDemo/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/RsCameraDemo/res/values/base-strings.xml b/java/tests/RsCameraDemo/res/values/base-strings.xml new file mode 100644 index 00000000..d8f8952e --- /dev/null +++ b/java/tests/RsCameraDemo/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/RsCameraDemo/res/values/strings.xml b/java/tests/RsCameraDemo/res/values/strings.xml new file mode 100644 index 00000000..21838d54 --- /dev/null +++ b/java/tests/RsCameraDemo/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/RsCameraDemo/res/values/template-dimens.xml b/java/tests/RsCameraDemo/res/values/template-dimens.xml new file mode 100644 index 00000000..291c4953 --- /dev/null +++ b/java/tests/RsCameraDemo/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/RsCameraDemo/res/values/template-styles.xml b/java/tests/RsCameraDemo/res/values/template-styles.xml new file mode 100644 index 00000000..c6dc4738 --- /dev/null +++ b/java/tests/RsCameraDemo/res/values/template-styles.xml @@ -0,0 +1,36 @@ +<!-- 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" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- 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/RsCameraDemo/src/com/android/example/rscamera/CameraOps.java b/java/tests/RsCameraDemo/src/com/android/example/rscamera/CameraOps.java new file mode 100644 index 00000000..d9ea9e82 --- /dev/null +++ b/java/tests/RsCameraDemo/src/com/android/example/rscamera/CameraOps.java @@ -0,0 +1,671 @@ +/* + * 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.rscamera; + +import android.content.ContentResolver; +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 android.widget.Toast; + +import com.android.example.rscamera.rscamera.R; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +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 ContentResolver mContentResolver; + + 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); + 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); + + mFocusDist = mFocusMin; + StreamConfigurationMap map = info.get( + CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + Size[] sizes = map.getOutputSizes(ImageFormat.JPEG); + Size largest = Collections.max(Arrays.asList(sizes), 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); + } + }); + mImageReader = ImageReader.newInstance(largest.getWidth(), largest.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, mContentResolver)); + } + + }; + + /** + * 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; + ContentResolver mContentResolver; + + public ImageSaver(Image image, String fileName, ContentResolver contentResolver) { + mImage = image; + mName = fileName; + mContentResolver = contentResolver; + } + + @Override + public void run() { + Log.v(TAG, "SAVING..."); + MediaStoreSaver.insertImage(mContentResolver, new MediaStoreSaver.StreamWriter() { + @Override + public void write(OutputStream imageOut) throws IOException { + try { + ByteBuffer buffer = mImage.getPlanes()[0].getBuffer(); + byte[] bytes = new byte[buffer.remaining()]; + buffer.get(bytes); + imageOut.write(bytes); + } finally { + mImage.close(); + } + } + }, mName, "Saved from Simple Camera Demo"); + } + } + + /** + * 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 = 1280; + final float TARGET_ASPECT = 16.f / 9.f; + final float ASPECT_TOLERANCE = 0.1f; + + + StreamConfigurationMap configs = + mCameraInfo.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + + Size[] outputSizes = configs.getOutputSizes(SurfaceHolder.class); + + Size outputSize = outputSizes[0]; + float outputAspect = (float) outputSize.getWidth() / outputSize.getHeight(); + for (Size candidateSize : outputSizes) { + if (candidateSize.getWidth() > MAX_WIDTH) continue; + float candidateAspect = (float) candidateSize.getWidth() / candidateSize.getHeight(); + boolean goodCandidateAspect = + Math.abs(candidateAspect - TARGET_ASPECT) < ASPECT_TOLERANCE; + boolean goodOutputAspect = + Math.abs(outputAspect - TARGET_ASPECT) < ASPECT_TOLERANCE; + if ((goodCandidateAspect && !goodOutputAspect) || + candidateSize.getWidth() > outputSize.getWidth()) { + outputSize = candidateSize; + outputAspect = candidateAspect; + } + } + return outputSize; + } + + /** + * 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, ContentResolver resolver) { + mSaveFileName = name; + mContentResolver = resolver; + 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, " 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, " 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; + } +}
\ No newline at end of file diff --git a/java/tests/RsCameraDemo/src/com/android/example/rscamera/CameraView.java b/java/tests/RsCameraDemo/src/com/android/example/rscamera/CameraView.java new file mode 100644 index 00000000..3f79ba77 --- /dev/null +++ b/java/tests/RsCameraDemo/src/com/android/example/rscamera/CameraView.java @@ -0,0 +1,339 @@ +/* + * 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.rscamera; + +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.rscamera.rscamera.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); + } + //MessageDialogFragment.newInstance(errorMessage).show(getFragmentManager(), FRAGMENT_DIALOG); + } + + @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() { + 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() { + // Orientation + 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.getContentResolver()); + } + + 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 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 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() { + return mProcessor.getmFps(); + } +} diff --git a/java/tests/RsCameraDemo/src/com/android/example/rscamera/FixedAspectSurfaceView.java b/java/tests/RsCameraDemo/src/com/android/example/rscamera/FixedAspectSurfaceView.java new file mode 100644 index 00000000..dd72c867 --- /dev/null +++ b/java/tests/RsCameraDemo/src/com/android/example/rscamera/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.rscamera; + +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.rscamera.rscamera.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; + } +}
\ No newline at end of file diff --git a/java/tests/RsCameraDemo/src/com/android/example/rscamera/MainActivity.java b/java/tests/RsCameraDemo/src/com/android/example/rscamera/MainActivity.java new file mode 100644 index 00000000..777fec61 --- /dev/null +++ b/java/tests/RsCameraDemo/src/com/android/example/rscamera/MainActivity.java @@ -0,0 +1,176 @@ +/*
+ * 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.rscamera;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.SeekBar;
+import android.widget.ViewFlipper;
+
+import com.android.example.rscamera.rscamera.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 ViewFlipper mViewFlipper;
+ private Button mSpeedButton;
+ private Button mISOButton;
+ private Button mFocusButton;
+ private Timer mTimer;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ mSpeedButton = (Button) findViewById(R.id.speed);
+ mISOButton = (Button) findViewById(R.id.iso);
+ mFocusButton = (Button) findViewById(R.id.focus);
+ mPreviewView = (CameraView) findViewById(R.id.preview);
+ mViewFlipper = (ViewFlipper) findViewById(R.id.viewFlipper);
+ SeekBar seekBar = (SeekBar) findViewById(R.id.focusbar);
+ seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ mPreviewView.setFocusInMeters(seekBar.getProgress() / 10.f);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+ });
+ mPreviewView.setParametersChangedCallback(new CameraView.ParametersChangedCallback() {
+ @Override
+ public void parametersChanged() {
+ update_buttons();
+ }
+ });
+ mTimer = new Timer();
+
+ mTimer.scheduleAtFixedRate(new TimerTask() {
+
+ @Override
+ public void run() {
+ // TODO Auto-generated method stub
+ runOnUiThread(new Runnable() {
+ public void run() {
+ setTitle("RS Camera (" + mPreviewView.getFps() + "fps)");
+ }
+ });
+
+ }
+ }, 250, 250);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mPreviewView.resume(this);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mPreviewView.pause();
+ }
+
+ public void setShutterSpeed(View v) {
+ if (mPreviewView.isAutoExposure()) {
+ mPreviewView.setAutoExposure(false);
+ mPreviewView.setMode(CameraView.MODE_SPEED);
+ } else {
+ mPreviewView.setMode(CameraView.MODE_NONE);
+ mPreviewView.setAutoExposure(true);
+ }
+ update_buttons();
+ }
+
+ public void setISO(View v) {
+ if (mPreviewView.isAutoExposure()) {
+ mPreviewView.setAutoExposure(false);
+ mPreviewView.setMode(CameraView.MODE_ISO);
+ } else {
+ mPreviewView.setMode(CameraView.MODE_NONE);
+ mPreviewView.setAutoExposure(true);
+ }
+ update_buttons();
+ }
+
+ public void setFocus(View v) {
+ if (mPreviewView.isAutofocus()) {
+ mPreviewView.setAutofocus(false);
+ mPreviewView.setMode(CameraView.MODE_FOCUS);
+ mViewFlipper.setInAnimation(this, R.anim.slide_in_from_left);
+ mViewFlipper.setOutAnimation(this, R.anim.slide_out_to_right);
+ mViewFlipper.showNext();
+ } else {
+ mPreviewView.setMode(CameraView.MODE_NONE);
+ mPreviewView.setAutofocus(true);
+ }
+ update_buttons();
+ }
+
+ public void back(View v) {
+ mViewFlipper.setInAnimation(this, R.anim.slide_in_from_left);
+ mViewFlipper.setOutAnimation(this, R.anim.slide_out_to_right);
+ mViewFlipper.showNext();
+ }
+
+ public void capture(View v) {
+ mPreviewView.takePicture();
+ }
+
+ private void update_buttons() {
+ byte mode = mPreviewView.getMode();
+ mSpeedButton.setElevation(mode == CameraView.MODE_SPEED ? 20 : 0);
+ mFocusButton.setElevation(mode == CameraView.MODE_FOCUS ? 20 : 0);
+ mISOButton.setElevation(mode == CameraView.MODE_ISO ? 20 : 0);
+
+ String a;
+ a = (mPreviewView.isAutoExposure()) ? "A " : " ";
+ if (ONE_SECOND > mPreviewView.getExposure()) {
+ mSpeedButton.setText(a + 1 + "/" + (ONE_SECOND / mPreviewView.getExposure()) + "s");
+ } else {
+ mSpeedButton.setText(a + (mPreviewView.getExposure() / ONE_SECOND) + "\"s");
+
+ }
+ a = (mPreviewView.isAutofocus()) ? "A " : " ";
+ DecimalFormat df = new DecimalFormat("#.###");
+ float d = mPreviewView.getFocusDist();
+ if (d < 0.01) {
+ d = 0;
+ }
+ mFocusButton.setText(a + df.format(0.1 / d) + " m");
+ a = (mPreviewView.isAutoExposure()) ? "A ISO " : " ISO ";
+ mISOButton.setText(a + mPreviewView.getIso() + " M");
+ }
+}
diff --git a/java/tests/RsCameraDemo/src/com/android/example/rscamera/MediaStoreSaver.java b/java/tests/RsCameraDemo/src/com/android/example/rscamera/MediaStoreSaver.java new file mode 100644 index 00000000..36f0f8df --- /dev/null +++ b/java/tests/RsCameraDemo/src/com/android/example/rscamera/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.rscamera; + +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/RsCameraDemo/src/com/android/example/rscamera/VerticalSeekBar.java b/java/tests/RsCameraDemo/src/com/android/example/rscamera/VerticalSeekBar.java new file mode 100644 index 00000000..1473e0ba --- /dev/null +++ b/java/tests/RsCameraDemo/src/com/android/example/rscamera/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.rscamera; + +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; + } +}
\ No newline at end of file diff --git a/java/tests/RsCameraDemo/src/com/android/example/rscamera/ViewfinderProcessor.java b/java/tests/RsCameraDemo/src/com/android/example/rscamera/ViewfinderProcessor.java new file mode 100644 index 00000000..e012e920 --- /dev/null +++ b/java/tests/RsCameraDemo/src/com/android/example/rscamera/ViewfinderProcessor.java @@ -0,0 +1,128 @@ +/* + * 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.rscamera; + +import android.graphics.ImageFormat; +import android.os.Handler; +import android.os.HandlerThread; +import android.renderscript.Allocation; +import android.renderscript.Element; +import android.renderscript.RenderScript; +import android.renderscript.Type; +import android.util.Size; +import android.view.Surface; + +/** + * Renderscript-based Focus peaking viewfinder + */ +public class ViewfinderProcessor { + int mCount; + long mLastTime; + float mFps; + private Allocation mInputAllocation; + private Allocation mOutputAllocation; + private HandlerThread mProcessingThread; + private Handler mProcessingHandler; + private ScriptC_focus_peak mScriptFocusPeak; + public ProcessingTask mProcessingTask; + + public ViewfinderProcessor(RenderScript rs, Size dimensions) { + Type.Builder yuvTypeBuilder = new Type.Builder(rs, Element.YUV(rs)); + yuvTypeBuilder.setX(dimensions.getWidth()); + yuvTypeBuilder.setY(dimensions.getHeight()); + yuvTypeBuilder.setYuvFormat(ImageFormat.YUV_420_888); + + 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); + + mProcessingThread = new HandlerThread("ViewfinderProcessor"); + mProcessingThread.start(); + mProcessingHandler = new Handler(mProcessingThread.getLooper()); + mScriptFocusPeak = new ScriptC_focus_peak(rs); + mProcessingTask = new ProcessingTask(mInputAllocation); + } + + public Surface getInputSurface() { + return mInputAllocation.getSurface(); + } + + public void setOutputSurface(Surface output) { + mOutputAllocation.setSurface(output); + } + + public float getmFps() { + return mFps; + } + + /** + * Class to process buffer from camera and output to buffer to screen + */ + class ProcessingTask implements Runnable, Allocation.OnBufferAvailableListener { + private int mPendingFrames = 0; + + private Allocation mInputAllocation; + + public ProcessingTask(Allocation input) { + mInputAllocation = input; + mInputAllocation.setOnBufferAvailableListener(this); + } + + @Override + public void onBufferAvailable(Allocation a) { + 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); + } + + // Get to newest input + for (int i = 0; i < pendingFrames; i++) { + mInputAllocation.ioReceive(); + } + mCount++; + mScriptFocusPeak.set_gCurrentFrame(mInputAllocation); + long time = System.currentTimeMillis() - mLastTime; + if (time > 1000) { + mLastTime += time; + mFps = mCount * 1000 / (float) (time); + mCount = 0; + } + // Run processing pass + mScriptFocusPeak.forEach_peak(mOutputAllocation); + mOutputAllocation.ioSend(); + } + } +}
\ No newline at end of file diff --git a/java/tests/RsCameraDemo/src/com/android/example/rscamera/focus_peak.rs b/java/tests/RsCameraDemo/src/com/android/example/rscamera/focus_peak.rs new file mode 100644 index 00000000..6513bc1b --- /dev/null +++ b/java/tests/RsCameraDemo/src/com/android/example/rscamera/focus_peak.rs @@ -0,0 +1,71 @@ +/* + * 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.rscamera) +#pragma rs_fp_relaxed + +rs_allocation gCurrentFrame; + +uchar4 __attribute__((kernel)) peak(uint32_t x, uint32_t y) { + + uchar4 curPixel; + curPixel.r = rsGetElementAtYuv_uchar_Y(gCurrentFrame, x, y); + curPixel.g = rsGetElementAtYuv_uchar_U(gCurrentFrame, x, y); + curPixel.b = rsGetElementAtYuv_uchar_V(gCurrentFrame, x, y); + + int dx = x + ((x == 0) ? 1 : -1); + int sum = 0; + int tmp; + + tmp = rsGetElementAtYuv_uchar_Y(gCurrentFrame, dx, y) - curPixel.r; + sum += tmp * tmp; + tmp = rsGetElementAtYuv_uchar_U(gCurrentFrame, dx, y) - curPixel.g; + sum += tmp * tmp; + tmp = rsGetElementAtYuv_uchar_V(gCurrentFrame, dx, y) - curPixel.b; + sum += tmp * tmp; + + + int dy = y + ((y == 0) ? 1 : -1); + tmp = rsGetElementAtYuv_uchar_Y(gCurrentFrame, x, dy) - curPixel.r; + sum += tmp * tmp; + tmp = rsGetElementAtYuv_uchar_U(gCurrentFrame, x, dy) - curPixel.g; + sum += tmp * tmp; + tmp = rsGetElementAtYuv_uchar_V(gCurrentFrame, x, dy) - curPixel.b; + sum += tmp * tmp; + + sum >>= 9; + sum *= sum * sum; + + curPixel.a = 255; + + uchar4 mergedPixel = curPixel; + + int4 rgb; + rgb.r = mergedPixel.r + + mergedPixel.b * 1436 / 1024 - 179 + sum; + rgb.g = mergedPixel.r - + mergedPixel.g * 46549 / 131072 + 44 - + mergedPixel.b * 93604 / 131072 + 91 + sum; + rgb.b = mergedPixel.r + + mergedPixel.g * 1814 / 1024 - 227; + rgb.a = 255; + + // Write out merged HDR result + uchar4 out = convert_uchar4(clamp(rgb, 0, 255)); + + return out; +}
\ No newline at end of file |