1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.basicrenderscript;
18 
19 import android.app.Activity;
20 import android.graphics.Bitmap;
21 import android.graphics.BitmapFactory;
22 import android.os.AsyncTask;
23 import android.os.Bundle;
24 import android.widget.ImageView;
25 import android.widget.SeekBar;
26 import android.widget.SeekBar.OnSeekBarChangeListener;
27 import android.support.v8.renderscript.*;
28 
29 public class MainActivity extends Activity {
30     /* Number of bitmaps that is used for renderScript thread and UI thread synchronization.
31        Ideally, this can be reduced to 2, however in some devices, 2 buffers still showing tierings on UI.
32        Investigating a root cause.
33      */
34     private final int NUM_BITMAPS = 3;
35     private int mCurrentBitmap = 0;
36     private Bitmap mBitmapIn;
37     private Bitmap[] mBitmapsOut;
38     private ImageView mImageView;
39 
40     private RenderScript mRS;
41     private Allocation mInAllocation;
42     private Allocation[] mOutAllocations;
43     private ScriptC_saturation mScript;
44 
45     @Override
onCreate(Bundle savedInstanceState)46     protected void onCreate(Bundle savedInstanceState) {
47         super.onCreate(savedInstanceState);
48 
49         setContentView(R.layout.main_layout);
50 
51         /*
52          * Initialize UI
53          */
54         mBitmapIn = loadBitmap(R.drawable.data);
55         mBitmapsOut = new Bitmap[NUM_BITMAPS];
56         for (int i = 0; i < NUM_BITMAPS; ++i) {
57             mBitmapsOut[i] = Bitmap.createBitmap(mBitmapIn.getWidth(),
58                     mBitmapIn.getHeight(), mBitmapIn.getConfig());
59         }
60 
61         mImageView = (ImageView) findViewById(R.id.imageView);
62         mImageView.setImageBitmap(mBitmapsOut[mCurrentBitmap]);
63         mCurrentBitmap += (mCurrentBitmap + 1) % NUM_BITMAPS;
64 
65         SeekBar seekbar = (SeekBar) findViewById(R.id.seekBar1);
66         seekbar.setProgress(50);
67         seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
68             public void onProgressChanged(SeekBar seekBar, int progress,
69                                           boolean fromUser) {
70                 float max = 2.0f;
71                 float min = 0.0f;
72                 float f = (float) ((max - min) * (progress / 100.0) + min);
73                 updateImage(f);
74             }
75 
76             @Override
77             public void onStartTrackingTouch(SeekBar seekBar) {
78             }
79 
80             @Override
81             public void onStopTrackingTouch(SeekBar seekBar) {
82             }
83         });
84 
85         /*
86          * Create renderScript
87          */
88         createScript();
89 
90         /*
91          * Invoke renderScript kernel and update imageView
92          */
93         updateImage(1.0f);
94     }
95 
96     /*
97      * Initialize RenderScript
98      * In the sample, it creates RenderScript kernel that performs saturation manipulation.
99      */
createScript()100     private void createScript() {
101         //Initialize RS
102         mRS = RenderScript.create(this);
103 
104         //Allocate buffers
105         mInAllocation = Allocation.createFromBitmap(mRS, mBitmapIn);
106         mOutAllocations = new Allocation[NUM_BITMAPS];
107         for (int i = 0; i < NUM_BITMAPS; ++i) {
108             mOutAllocations[i] = Allocation.createFromBitmap(mRS, mBitmapsOut[i]);
109         }
110 
111         //Load script
112         mScript = new ScriptC_saturation(mRS);
113     }
114 
115     /*
116      * In the AsyncTask, it invokes RenderScript intrinsics to do a filtering.
117      * After the filtering is done, an operation blocks at Allication.copyTo() in AsyncTask thread.
118      * Once all operation is finished at onPostExecute() in UI thread, it can invalidate and update ImageView UI.
119      */
120     private class RenderScriptTask extends AsyncTask<Float, Integer, Integer> {
121         Boolean issued = false;
122 
doInBackground(Float... values)123         protected Integer doInBackground(Float... values) {
124             int index = -1;
125             if (isCancelled() == false) {
126                 issued = true;
127                 index = mCurrentBitmap;
128 
129                 /*
130                  * Set global variable in RS
131                  */
132                 mScript.set_saturationValue(values[0]);
133 
134                 /*
135                  * Invoke saturation filter kernel
136                  */
137                 mScript.forEach_saturation(mInAllocation, mOutAllocations[index]);
138 
139                 /*
140                  * Copy to bitmap and invalidate image view
141                  */
142                 mOutAllocations[index].copyTo(mBitmapsOut[index]);
143                 mCurrentBitmap = (mCurrentBitmap + 1) % NUM_BITMAPS;
144             }
145             return index;
146         }
147 
updateView(Integer result)148         void updateView(Integer result) {
149             if (result != -1) {
150                 // Request UI update
151                 mImageView.setImageBitmap(mBitmapsOut[result]);
152                 mImageView.invalidate();
153             }
154         }
155 
onPostExecute(Integer result)156         protected void onPostExecute(Integer result) {
157             updateView(result);
158         }
159 
onCancelled(Integer result)160         protected void onCancelled(Integer result) {
161             if (issued) {
162                 updateView(result);
163             }
164         }
165     }
166 
167     RenderScriptTask currentTask = null;
168 
169     /*
170     Invoke AsynchTask and cancel previous task.
171     When AsyncTasks are piled up (typically in slow device with heavy kernel),
172     Only the latest (and already started) task invokes RenderScript operation.
173      */
updateImage(final float f)174     private void updateImage(final float f) {
175         if (currentTask != null)
176             currentTask.cancel(false);
177         currentTask = new RenderScriptTask();
178         currentTask.execute(f);
179     }
180 
181     /*
182     Helper to load Bitmap from resource
183      */
loadBitmap(int resource)184     private Bitmap loadBitmap(int resource) {
185         final BitmapFactory.Options options = new BitmapFactory.Options();
186         options.inPreferredConfig = Bitmap.Config.ARGB_8888;
187         return BitmapFactory.decodeResource(getResources(), resource, options);
188     }
189 
190 }
191