1*90c8c64dSAndroid Build Coastguard Worker /*
2*90c8c64dSAndroid Build Coastguard Worker  * Copyright (C) 2014 The Android Open Source Project
3*90c8c64dSAndroid Build Coastguard Worker  *
4*90c8c64dSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*90c8c64dSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*90c8c64dSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*90c8c64dSAndroid Build Coastguard Worker  *
8*90c8c64dSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*90c8c64dSAndroid Build Coastguard Worker  *
10*90c8c64dSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*90c8c64dSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*90c8c64dSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*90c8c64dSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*90c8c64dSAndroid Build Coastguard Worker  * limitations under the License.
15*90c8c64dSAndroid Build Coastguard Worker  */
16*90c8c64dSAndroid Build Coastguard Worker 
17*90c8c64dSAndroid Build Coastguard Worker package com.example.android.renderscriptintrinsic;
18*90c8c64dSAndroid Build Coastguard Worker 
19*90c8c64dSAndroid Build Coastguard Worker import android.app.Activity;
20*90c8c64dSAndroid Build Coastguard Worker import android.graphics.Bitmap;
21*90c8c64dSAndroid Build Coastguard Worker import android.graphics.BitmapFactory;
22*90c8c64dSAndroid Build Coastguard Worker import android.os.AsyncTask;
23*90c8c64dSAndroid Build Coastguard Worker import android.os.Bundle;
24*90c8c64dSAndroid Build Coastguard Worker import android.widget.CompoundButton;
25*90c8c64dSAndroid Build Coastguard Worker import android.widget.CompoundButton.OnCheckedChangeListener;
26*90c8c64dSAndroid Build Coastguard Worker import android.widget.ImageView;
27*90c8c64dSAndroid Build Coastguard Worker import android.widget.RadioButton;
28*90c8c64dSAndroid Build Coastguard Worker import android.widget.SeekBar;
29*90c8c64dSAndroid Build Coastguard Worker import android.widget.SeekBar.OnSeekBarChangeListener;
30*90c8c64dSAndroid Build Coastguard Worker import android.support.v8.renderscript.*;
31*90c8c64dSAndroid Build Coastguard Worker 
32*90c8c64dSAndroid Build Coastguard Worker public class MainActivity extends Activity {
33*90c8c64dSAndroid Build Coastguard Worker     /* Number of bitmaps that is used for renderScript thread and UI thread synchronization.
34*90c8c64dSAndroid Build Coastguard Worker        Ideally, this can be reduced to 2, however in some devices, 2 buffers still showing tierings on UI.
35*90c8c64dSAndroid Build Coastguard Worker        Investigating a root cause.
36*90c8c64dSAndroid Build Coastguard Worker      */
37*90c8c64dSAndroid Build Coastguard Worker     private final int NUM_BITMAPS = 3;
38*90c8c64dSAndroid Build Coastguard Worker     private int mCurrentBitmap = 0;
39*90c8c64dSAndroid Build Coastguard Worker     private Bitmap mBitmapIn;
40*90c8c64dSAndroid Build Coastguard Worker     private Bitmap[] mBitmapsOut;
41*90c8c64dSAndroid Build Coastguard Worker     private ImageView mImageView;
42*90c8c64dSAndroid Build Coastguard Worker 
43*90c8c64dSAndroid Build Coastguard Worker     private RenderScript mRS;
44*90c8c64dSAndroid Build Coastguard Worker     private Allocation mInAllocation;
45*90c8c64dSAndroid Build Coastguard Worker     private Allocation[] mOutAllocations;
46*90c8c64dSAndroid Build Coastguard Worker 
47*90c8c64dSAndroid Build Coastguard Worker     private ScriptIntrinsicBlur mScriptBlur;
48*90c8c64dSAndroid Build Coastguard Worker     private ScriptIntrinsicConvolve5x5 mScriptConvolve;
49*90c8c64dSAndroid Build Coastguard Worker     private ScriptIntrinsicColorMatrix mScriptMatrix;
50*90c8c64dSAndroid Build Coastguard Worker 
51*90c8c64dSAndroid Build Coastguard Worker     private final int MODE_BLUR = 0;
52*90c8c64dSAndroid Build Coastguard Worker     private final int MODE_CONVOLVE = 1;
53*90c8c64dSAndroid Build Coastguard Worker     private final int MODE_COLORMATRIX = 2;
54*90c8c64dSAndroid Build Coastguard Worker 
55*90c8c64dSAndroid Build Coastguard Worker     private int mFilterMode = MODE_BLUR;
56*90c8c64dSAndroid Build Coastguard Worker 
57*90c8c64dSAndroid Build Coastguard Worker     private RenderScriptTask mLatestTask = null;
58*90c8c64dSAndroid Build Coastguard Worker 
59*90c8c64dSAndroid Build Coastguard Worker     @Override
onCreate(Bundle savedInstanceState)60*90c8c64dSAndroid Build Coastguard Worker     protected void onCreate(Bundle savedInstanceState) {
61*90c8c64dSAndroid Build Coastguard Worker         super.onCreate(savedInstanceState);
62*90c8c64dSAndroid Build Coastguard Worker 
63*90c8c64dSAndroid Build Coastguard Worker         setContentView(R.layout.main_layout);
64*90c8c64dSAndroid Build Coastguard Worker 
65*90c8c64dSAndroid Build Coastguard Worker         /*
66*90c8c64dSAndroid Build Coastguard Worker          * Initialize UI
67*90c8c64dSAndroid Build Coastguard Worker          */
68*90c8c64dSAndroid Build Coastguard Worker 
69*90c8c64dSAndroid Build Coastguard Worker         //Set up main image view
70*90c8c64dSAndroid Build Coastguard Worker         mBitmapIn = loadBitmap(R.drawable.data);
71*90c8c64dSAndroid Build Coastguard Worker         mBitmapsOut = new Bitmap[NUM_BITMAPS];
72*90c8c64dSAndroid Build Coastguard Worker         for (int i = 0; i < NUM_BITMAPS; ++i) {
73*90c8c64dSAndroid Build Coastguard Worker             mBitmapsOut[i] = Bitmap.createBitmap(mBitmapIn.getWidth(),
74*90c8c64dSAndroid Build Coastguard Worker                     mBitmapIn.getHeight(), mBitmapIn.getConfig());
75*90c8c64dSAndroid Build Coastguard Worker         }
76*90c8c64dSAndroid Build Coastguard Worker 
77*90c8c64dSAndroid Build Coastguard Worker         mImageView = (ImageView) findViewById(R.id.imageView);
78*90c8c64dSAndroid Build Coastguard Worker         mImageView.setImageBitmap(mBitmapsOut[mCurrentBitmap]);
79*90c8c64dSAndroid Build Coastguard Worker         mCurrentBitmap += (mCurrentBitmap + 1) % NUM_BITMAPS;
80*90c8c64dSAndroid Build Coastguard Worker 
81*90c8c64dSAndroid Build Coastguard Worker         //Set up seekbar
82*90c8c64dSAndroid Build Coastguard Worker         final SeekBar seekbar = (SeekBar) findViewById(R.id.seekBar1);
83*90c8c64dSAndroid Build Coastguard Worker         seekbar.setProgress(50);
84*90c8c64dSAndroid Build Coastguard Worker         seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
85*90c8c64dSAndroid Build Coastguard Worker             public void onProgressChanged(SeekBar seekBar, int progress,
86*90c8c64dSAndroid Build Coastguard Worker                                           boolean fromUser) {
87*90c8c64dSAndroid Build Coastguard Worker                 updateImage(progress);
88*90c8c64dSAndroid Build Coastguard Worker             }
89*90c8c64dSAndroid Build Coastguard Worker 
90*90c8c64dSAndroid Build Coastguard Worker             @Override
91*90c8c64dSAndroid Build Coastguard Worker             public void onStartTrackingTouch(SeekBar seekBar) {
92*90c8c64dSAndroid Build Coastguard Worker             }
93*90c8c64dSAndroid Build Coastguard Worker 
94*90c8c64dSAndroid Build Coastguard Worker             @Override
95*90c8c64dSAndroid Build Coastguard Worker             public void onStopTrackingTouch(SeekBar seekBar) {
96*90c8c64dSAndroid Build Coastguard Worker             }
97*90c8c64dSAndroid Build Coastguard Worker         });
98*90c8c64dSAndroid Build Coastguard Worker 
99*90c8c64dSAndroid Build Coastguard Worker         //Setup effect selector
100*90c8c64dSAndroid Build Coastguard Worker         RadioButton radio0 = (RadioButton) findViewById(R.id.radio0);
101*90c8c64dSAndroid Build Coastguard Worker         radio0.setOnCheckedChangeListener(new OnCheckedChangeListener() {
102*90c8c64dSAndroid Build Coastguard Worker 
103*90c8c64dSAndroid Build Coastguard Worker             @Override
104*90c8c64dSAndroid Build Coastguard Worker             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
105*90c8c64dSAndroid Build Coastguard Worker                 if (isChecked) {
106*90c8c64dSAndroid Build Coastguard Worker                     mFilterMode = MODE_BLUR;
107*90c8c64dSAndroid Build Coastguard Worker                     updateImage(seekbar.getProgress());
108*90c8c64dSAndroid Build Coastguard Worker                 }
109*90c8c64dSAndroid Build Coastguard Worker             }
110*90c8c64dSAndroid Build Coastguard Worker         });
111*90c8c64dSAndroid Build Coastguard Worker         RadioButton radio1 = (RadioButton) findViewById(R.id.radio1);
112*90c8c64dSAndroid Build Coastguard Worker         radio1.setOnCheckedChangeListener(new OnCheckedChangeListener() {
113*90c8c64dSAndroid Build Coastguard Worker 
114*90c8c64dSAndroid Build Coastguard Worker             @Override
115*90c8c64dSAndroid Build Coastguard Worker             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
116*90c8c64dSAndroid Build Coastguard Worker                 if (isChecked) {
117*90c8c64dSAndroid Build Coastguard Worker                     mFilterMode = MODE_CONVOLVE;
118*90c8c64dSAndroid Build Coastguard Worker                     updateImage(seekbar.getProgress());
119*90c8c64dSAndroid Build Coastguard Worker                 }
120*90c8c64dSAndroid Build Coastguard Worker             }
121*90c8c64dSAndroid Build Coastguard Worker         });
122*90c8c64dSAndroid Build Coastguard Worker         RadioButton radio2 = (RadioButton) findViewById(R.id.radio2);
123*90c8c64dSAndroid Build Coastguard Worker         radio2.setOnCheckedChangeListener(new OnCheckedChangeListener() {
124*90c8c64dSAndroid Build Coastguard Worker 
125*90c8c64dSAndroid Build Coastguard Worker             @Override
126*90c8c64dSAndroid Build Coastguard Worker             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
127*90c8c64dSAndroid Build Coastguard Worker                 if (isChecked) {
128*90c8c64dSAndroid Build Coastguard Worker                     mFilterMode = MODE_COLORMATRIX;
129*90c8c64dSAndroid Build Coastguard Worker                     updateImage(seekbar.getProgress());
130*90c8c64dSAndroid Build Coastguard Worker                 }
131*90c8c64dSAndroid Build Coastguard Worker             }
132*90c8c64dSAndroid Build Coastguard Worker         });
133*90c8c64dSAndroid Build Coastguard Worker 
134*90c8c64dSAndroid Build Coastguard Worker         /*
135*90c8c64dSAndroid Build Coastguard Worker          * Create renderScript
136*90c8c64dSAndroid Build Coastguard Worker          */
137*90c8c64dSAndroid Build Coastguard Worker         createScript();
138*90c8c64dSAndroid Build Coastguard Worker 
139*90c8c64dSAndroid Build Coastguard Worker         /*
140*90c8c64dSAndroid Build Coastguard Worker          * Create thumbnails
141*90c8c64dSAndroid Build Coastguard Worker          */
142*90c8c64dSAndroid Build Coastguard Worker         createThumbnail();
143*90c8c64dSAndroid Build Coastguard Worker 
144*90c8c64dSAndroid Build Coastguard Worker 
145*90c8c64dSAndroid Build Coastguard Worker         /*
146*90c8c64dSAndroid Build Coastguard Worker          * Invoke renderScript kernel and update imageView
147*90c8c64dSAndroid Build Coastguard Worker          */
148*90c8c64dSAndroid Build Coastguard Worker         mFilterMode = MODE_BLUR;
149*90c8c64dSAndroid Build Coastguard Worker         updateImage(50);
150*90c8c64dSAndroid Build Coastguard Worker     }
151*90c8c64dSAndroid Build Coastguard Worker 
createScript()152*90c8c64dSAndroid Build Coastguard Worker     private void createScript() {
153*90c8c64dSAndroid Build Coastguard Worker         mRS = RenderScript.create(this);
154*90c8c64dSAndroid Build Coastguard Worker 
155*90c8c64dSAndroid Build Coastguard Worker         mInAllocation = Allocation.createFromBitmap(mRS, mBitmapIn);
156*90c8c64dSAndroid Build Coastguard Worker 
157*90c8c64dSAndroid Build Coastguard Worker         mOutAllocations = new Allocation[NUM_BITMAPS];
158*90c8c64dSAndroid Build Coastguard Worker         for (int i = 0; i < NUM_BITMAPS; ++i) {
159*90c8c64dSAndroid Build Coastguard Worker             mOutAllocations[i] = Allocation.createFromBitmap(mRS, mBitmapsOut[i]);
160*90c8c64dSAndroid Build Coastguard Worker         }
161*90c8c64dSAndroid Build Coastguard Worker 
162*90c8c64dSAndroid Build Coastguard Worker         /*
163*90c8c64dSAndroid Build Coastguard Worker         Create intrinsics.
164*90c8c64dSAndroid Build Coastguard Worker         RenderScript has built-in features such as blur, convolve filter etc.
165*90c8c64dSAndroid Build Coastguard Worker         These intrinsics are handy for specific operations without writing RenderScript kernel.
166*90c8c64dSAndroid Build Coastguard Worker         In the sample, it's creating blur, convolve and matrix intrinsics.
167*90c8c64dSAndroid Build Coastguard Worker          */
168*90c8c64dSAndroid Build Coastguard Worker 
169*90c8c64dSAndroid Build Coastguard Worker         mScriptBlur = ScriptIntrinsicBlur.create(mRS, Element.U8_4(mRS));
170*90c8c64dSAndroid Build Coastguard Worker         mScriptConvolve = ScriptIntrinsicConvolve5x5.create(mRS,
171*90c8c64dSAndroid Build Coastguard Worker                 Element.U8_4(mRS));
172*90c8c64dSAndroid Build Coastguard Worker         mScriptMatrix = ScriptIntrinsicColorMatrix.create(mRS,
173*90c8c64dSAndroid Build Coastguard Worker                 Element.U8_4(mRS));
174*90c8c64dSAndroid Build Coastguard Worker     }
175*90c8c64dSAndroid Build Coastguard Worker 
performFilter(Allocation inAllocation, Allocation outAllocation, Bitmap bitmapOut, float value)176*90c8c64dSAndroid Build Coastguard Worker     private void performFilter(Allocation inAllocation,
177*90c8c64dSAndroid Build Coastguard Worker                                Allocation outAllocation, Bitmap bitmapOut, float value) {
178*90c8c64dSAndroid Build Coastguard Worker         switch (mFilterMode) {
179*90c8c64dSAndroid Build Coastguard Worker             case MODE_BLUR:
180*90c8c64dSAndroid Build Coastguard Worker             /*
181*90c8c64dSAndroid Build Coastguard Worker              * Set blur kernel size
182*90c8c64dSAndroid Build Coastguard Worker              */
183*90c8c64dSAndroid Build Coastguard Worker                 mScriptBlur.setRadius(value);
184*90c8c64dSAndroid Build Coastguard Worker 
185*90c8c64dSAndroid Build Coastguard Worker             /*
186*90c8c64dSAndroid Build Coastguard Worker              * Invoke filter kernel
187*90c8c64dSAndroid Build Coastguard Worker              */
188*90c8c64dSAndroid Build Coastguard Worker                 mScriptBlur.setInput(inAllocation);
189*90c8c64dSAndroid Build Coastguard Worker                 mScriptBlur.forEach(outAllocation);
190*90c8c64dSAndroid Build Coastguard Worker                 break;
191*90c8c64dSAndroid Build Coastguard Worker             case MODE_CONVOLVE: {
192*90c8c64dSAndroid Build Coastguard Worker                 float f1 = value;
193*90c8c64dSAndroid Build Coastguard Worker                 float f2 = 1.0f - f1;
194*90c8c64dSAndroid Build Coastguard Worker 
195*90c8c64dSAndroid Build Coastguard Worker                 // Emboss filter kernel
196*90c8c64dSAndroid Build Coastguard Worker                 float coefficients[] = {-f1 * 2, 0, -f1, 0, 0, 0, -f2 * 2, -f2, 0,
197*90c8c64dSAndroid Build Coastguard Worker                         0, -f1, -f2, 1, f2, f1, 0, 0, f2, f2 * 2, 0, 0, 0, f1, 0,
198*90c8c64dSAndroid Build Coastguard Worker                         f1 * 2,};
199*90c8c64dSAndroid Build Coastguard Worker             /*
200*90c8c64dSAndroid Build Coastguard Worker              * Set kernel parameter
201*90c8c64dSAndroid Build Coastguard Worker              */
202*90c8c64dSAndroid Build Coastguard Worker                 mScriptConvolve.setCoefficients(coefficients);
203*90c8c64dSAndroid Build Coastguard Worker 
204*90c8c64dSAndroid Build Coastguard Worker             /*
205*90c8c64dSAndroid Build Coastguard Worker              * Invoke filter kernel
206*90c8c64dSAndroid Build Coastguard Worker              */
207*90c8c64dSAndroid Build Coastguard Worker                 mScriptConvolve.setInput(inAllocation);
208*90c8c64dSAndroid Build Coastguard Worker                 mScriptConvolve.forEach(outAllocation);
209*90c8c64dSAndroid Build Coastguard Worker                 break;
210*90c8c64dSAndroid Build Coastguard Worker             }
211*90c8c64dSAndroid Build Coastguard Worker             case MODE_COLORMATRIX: {
212*90c8c64dSAndroid Build Coastguard Worker             /*
213*90c8c64dSAndroid Build Coastguard Worker              * Set HUE rotation matrix
214*90c8c64dSAndroid Build Coastguard Worker              * The matrix below performs a combined operation of,
215*90c8c64dSAndroid Build Coastguard Worker              * RGB->HSV transform * HUE rotation * HSV->RGB transform
216*90c8c64dSAndroid Build Coastguard Worker              */
217*90c8c64dSAndroid Build Coastguard Worker                 float cos = (float) Math.cos((double) value);
218*90c8c64dSAndroid Build Coastguard Worker                 float sin = (float) Math.sin((double) value);
219*90c8c64dSAndroid Build Coastguard Worker                 Matrix3f mat = new Matrix3f();
220*90c8c64dSAndroid Build Coastguard Worker                 mat.set(0, 0, (float) (.299 + .701 * cos + .168 * sin));
221*90c8c64dSAndroid Build Coastguard Worker                 mat.set(1, 0, (float) (.587 - .587 * cos + .330 * sin));
222*90c8c64dSAndroid Build Coastguard Worker                 mat.set(2, 0, (float) (.114 - .114 * cos - .497 * sin));
223*90c8c64dSAndroid Build Coastguard Worker                 mat.set(0, 1, (float) (.299 - .299 * cos - .328 * sin));
224*90c8c64dSAndroid Build Coastguard Worker                 mat.set(1, 1, (float) (.587 + .413 * cos + .035 * sin));
225*90c8c64dSAndroid Build Coastguard Worker                 mat.set(2, 1, (float) (.114 - .114 * cos + .292 * sin));
226*90c8c64dSAndroid Build Coastguard Worker                 mat.set(0, 2, (float) (.299 - .3 * cos + 1.25 * sin));
227*90c8c64dSAndroid Build Coastguard Worker                 mat.set(1, 2, (float) (.587 - .588 * cos - 1.05 * sin));
228*90c8c64dSAndroid Build Coastguard Worker                 mat.set(2, 2, (float) (.114 + .886 * cos - .203 * sin));
229*90c8c64dSAndroid Build Coastguard Worker                 mScriptMatrix.setColorMatrix(mat);
230*90c8c64dSAndroid Build Coastguard Worker 
231*90c8c64dSAndroid Build Coastguard Worker             /*
232*90c8c64dSAndroid Build Coastguard Worker              * Invoke filter kernel
233*90c8c64dSAndroid Build Coastguard Worker              */
234*90c8c64dSAndroid Build Coastguard Worker                 mScriptMatrix.forEach(inAllocation, outAllocation);
235*90c8c64dSAndroid Build Coastguard Worker             }
236*90c8c64dSAndroid Build Coastguard Worker             break;
237*90c8c64dSAndroid Build Coastguard Worker         }
238*90c8c64dSAndroid Build Coastguard Worker 
239*90c8c64dSAndroid Build Coastguard Worker         /*
240*90c8c64dSAndroid Build Coastguard Worker          * Copy to bitmap and invalidate image view
241*90c8c64dSAndroid Build Coastguard Worker          */
242*90c8c64dSAndroid Build Coastguard Worker         outAllocation.copyTo(bitmapOut);
243*90c8c64dSAndroid Build Coastguard Worker     }
244*90c8c64dSAndroid Build Coastguard Worker 
245*90c8c64dSAndroid Build Coastguard Worker     /*
246*90c8c64dSAndroid Build Coastguard Worker     Convert seekBar progress parameter (0-100 in range) to parameter for each intrinsic filter.
247*90c8c64dSAndroid Build Coastguard Worker     (e.g. 1.0-25.0 in Blur filter)
248*90c8c64dSAndroid Build Coastguard Worker      */
getFilterParameter(int i)249*90c8c64dSAndroid Build Coastguard Worker     private float getFilterParameter(int i) {
250*90c8c64dSAndroid Build Coastguard Worker         float f = 0.f;
251*90c8c64dSAndroid Build Coastguard Worker         switch (mFilterMode) {
252*90c8c64dSAndroid Build Coastguard Worker             case MODE_BLUR: {
253*90c8c64dSAndroid Build Coastguard Worker                 final float max = 25.0f;
254*90c8c64dSAndroid Build Coastguard Worker                 final float min = 1.f;
255*90c8c64dSAndroid Build Coastguard Worker                 f = (float) ((max - min) * (i / 100.0) + min);
256*90c8c64dSAndroid Build Coastguard Worker             }
257*90c8c64dSAndroid Build Coastguard Worker             break;
258*90c8c64dSAndroid Build Coastguard Worker             case MODE_CONVOLVE: {
259*90c8c64dSAndroid Build Coastguard Worker                 final float max = 2.f;
260*90c8c64dSAndroid Build Coastguard Worker                 final float min = 0.f;
261*90c8c64dSAndroid Build Coastguard Worker                 f = (float) ((max - min) * (i / 100.0) + min);
262*90c8c64dSAndroid Build Coastguard Worker             }
263*90c8c64dSAndroid Build Coastguard Worker             break;
264*90c8c64dSAndroid Build Coastguard Worker             case MODE_COLORMATRIX: {
265*90c8c64dSAndroid Build Coastguard Worker                 final float max = (float) Math.PI;
266*90c8c64dSAndroid Build Coastguard Worker                 final float min = (float) -Math.PI;
267*90c8c64dSAndroid Build Coastguard Worker                 f = (float) ((max - min) * (i / 100.0) + min);
268*90c8c64dSAndroid Build Coastguard Worker             }
269*90c8c64dSAndroid Build Coastguard Worker             break;
270*90c8c64dSAndroid Build Coastguard Worker         }
271*90c8c64dSAndroid Build Coastguard Worker         return f;
272*90c8c64dSAndroid Build Coastguard Worker 
273*90c8c64dSAndroid Build Coastguard Worker     }
274*90c8c64dSAndroid Build Coastguard Worker 
275*90c8c64dSAndroid Build Coastguard Worker     /*
276*90c8c64dSAndroid Build Coastguard Worker      * In the AsyncTask, it invokes RenderScript intrinsics to do a filtering.
277*90c8c64dSAndroid Build Coastguard Worker      * After the filtering is done, an operation blocks at Allication.copyTo() in AsyncTask thread.
278*90c8c64dSAndroid Build Coastguard Worker      * Once all operation is finished at onPostExecute() in UI thread, it can invalidate and update ImageView UI.
279*90c8c64dSAndroid Build Coastguard Worker      */
280*90c8c64dSAndroid Build Coastguard Worker     private class RenderScriptTask extends AsyncTask<Float, Integer, Integer> {
281*90c8c64dSAndroid Build Coastguard Worker         Boolean issued = false;
282*90c8c64dSAndroid Build Coastguard Worker 
doInBackground(Float... values)283*90c8c64dSAndroid Build Coastguard Worker         protected Integer doInBackground(Float... values) {
284*90c8c64dSAndroid Build Coastguard Worker             int index = -1;
285*90c8c64dSAndroid Build Coastguard Worker             if (isCancelled() == false) {
286*90c8c64dSAndroid Build Coastguard Worker                 issued = true;
287*90c8c64dSAndroid Build Coastguard Worker                 index = mCurrentBitmap;
288*90c8c64dSAndroid Build Coastguard Worker 
289*90c8c64dSAndroid Build Coastguard Worker                 performFilter(mInAllocation, mOutAllocations[index], mBitmapsOut[index], values[0]);
290*90c8c64dSAndroid Build Coastguard Worker                 mCurrentBitmap = (mCurrentBitmap + 1) % NUM_BITMAPS;
291*90c8c64dSAndroid Build Coastguard Worker             }
292*90c8c64dSAndroid Build Coastguard Worker             return index;
293*90c8c64dSAndroid Build Coastguard Worker         }
294*90c8c64dSAndroid Build Coastguard Worker 
updateView(Integer result)295*90c8c64dSAndroid Build Coastguard Worker         void updateView(Integer result) {
296*90c8c64dSAndroid Build Coastguard Worker             if (result != -1) {
297*90c8c64dSAndroid Build Coastguard Worker                 // Request UI update
298*90c8c64dSAndroid Build Coastguard Worker                 mImageView.setImageBitmap(mBitmapsOut[result]);
299*90c8c64dSAndroid Build Coastguard Worker                 mImageView.invalidate();
300*90c8c64dSAndroid Build Coastguard Worker             }
301*90c8c64dSAndroid Build Coastguard Worker         }
302*90c8c64dSAndroid Build Coastguard Worker 
onPostExecute(Integer result)303*90c8c64dSAndroid Build Coastguard Worker         protected void onPostExecute(Integer result) {
304*90c8c64dSAndroid Build Coastguard Worker             updateView(result);
305*90c8c64dSAndroid Build Coastguard Worker         }
306*90c8c64dSAndroid Build Coastguard Worker 
onCancelled(Integer result)307*90c8c64dSAndroid Build Coastguard Worker         protected void onCancelled(Integer result) {
308*90c8c64dSAndroid Build Coastguard Worker             if (issued) {
309*90c8c64dSAndroid Build Coastguard Worker                 updateView(result);
310*90c8c64dSAndroid Build Coastguard Worker             }
311*90c8c64dSAndroid Build Coastguard Worker         }
312*90c8c64dSAndroid Build Coastguard Worker     }
313*90c8c64dSAndroid Build Coastguard Worker 
314*90c8c64dSAndroid Build Coastguard Worker     /*
315*90c8c64dSAndroid Build Coastguard Worker     Invoke AsynchTask and cancel previous task.
316*90c8c64dSAndroid Build Coastguard Worker     When AsyncTasks are piled up (typically in slow device with heavy kernel),
317*90c8c64dSAndroid Build Coastguard Worker     Only the latest (and already started) task invokes RenderScript operation.
318*90c8c64dSAndroid Build Coastguard Worker      */
updateImage(int progress)319*90c8c64dSAndroid Build Coastguard Worker     private void updateImage(int progress) {
320*90c8c64dSAndroid Build Coastguard Worker         float f = getFilterParameter(progress);
321*90c8c64dSAndroid Build Coastguard Worker 
322*90c8c64dSAndroid Build Coastguard Worker         if (mLatestTask != null)
323*90c8c64dSAndroid Build Coastguard Worker             mLatestTask.cancel(false);
324*90c8c64dSAndroid Build Coastguard Worker         mLatestTask = new RenderScriptTask();
325*90c8c64dSAndroid Build Coastguard Worker 
326*90c8c64dSAndroid Build Coastguard Worker         mLatestTask.execute(f);
327*90c8c64dSAndroid Build Coastguard Worker     }
328*90c8c64dSAndroid Build Coastguard Worker 
329*90c8c64dSAndroid Build Coastguard Worker     /*
330*90c8c64dSAndroid Build Coastguard Worker     Helper to load Bitmap from resource
331*90c8c64dSAndroid Build Coastguard Worker      */
loadBitmap(int resource)332*90c8c64dSAndroid Build Coastguard Worker     private Bitmap loadBitmap(int resource) {
333*90c8c64dSAndroid Build Coastguard Worker         final BitmapFactory.Options options = new BitmapFactory.Options();
334*90c8c64dSAndroid Build Coastguard Worker         options.inPreferredConfig = Bitmap.Config.ARGB_8888;
335*90c8c64dSAndroid Build Coastguard Worker         return BitmapFactory.decodeResource(getResources(), resource, options);
336*90c8c64dSAndroid Build Coastguard Worker     }
337*90c8c64dSAndroid Build Coastguard Worker 
338*90c8c64dSAndroid Build Coastguard Worker     /*
339*90c8c64dSAndroid Build Coastguard Worker     Create thumbNail for UI. It invokes RenderScript kernel synchronously in UI-thread,
340*90c8c64dSAndroid Build Coastguard Worker     which is OK for small thumbnail (but not ideal).
341*90c8c64dSAndroid Build Coastguard Worker      */
createThumbnail()342*90c8c64dSAndroid Build Coastguard Worker     private void createThumbnail() {
343*90c8c64dSAndroid Build Coastguard Worker         int width = 72;
344*90c8c64dSAndroid Build Coastguard Worker         int height = 96;
345*90c8c64dSAndroid Build Coastguard Worker         float scale = getResources().getDisplayMetrics().density;
346*90c8c64dSAndroid Build Coastguard Worker         int pixelsWidth = (int) (width * scale + 0.5f);
347*90c8c64dSAndroid Build Coastguard Worker         int pixelsHeight = (int) (height * scale + 0.5f);
348*90c8c64dSAndroid Build Coastguard Worker 
349*90c8c64dSAndroid Build Coastguard Worker         //Temporary image
350*90c8c64dSAndroid Build Coastguard Worker         Bitmap tempBitmap = Bitmap.createScaledBitmap(mBitmapIn, pixelsWidth, pixelsHeight, false);
351*90c8c64dSAndroid Build Coastguard Worker         Allocation inAllocation = Allocation.createFromBitmap(mRS, tempBitmap);
352*90c8c64dSAndroid Build Coastguard Worker 
353*90c8c64dSAndroid Build Coastguard Worker         //Create thumbnail with each RS intrinsic and set it to radio buttons
354*90c8c64dSAndroid Build Coastguard Worker         int[] modes = {MODE_BLUR, MODE_CONVOLVE, MODE_COLORMATRIX};
355*90c8c64dSAndroid Build Coastguard Worker         int[] ids = {R.id.radio0, R.id.radio1, R.id.radio2};
356*90c8c64dSAndroid Build Coastguard Worker         int[] parameter = {50, 100, 25};
357*90c8c64dSAndroid Build Coastguard Worker         for (int mode : modes) {
358*90c8c64dSAndroid Build Coastguard Worker             mFilterMode = mode;
359*90c8c64dSAndroid Build Coastguard Worker             float f = getFilterParameter(parameter[mode]);
360*90c8c64dSAndroid Build Coastguard Worker 
361*90c8c64dSAndroid Build Coastguard Worker             Bitmap destBitpmap = Bitmap.createBitmap(tempBitmap.getWidth(),
362*90c8c64dSAndroid Build Coastguard Worker                     tempBitmap.getHeight(), tempBitmap.getConfig());
363*90c8c64dSAndroid Build Coastguard Worker             Allocation outAllocation = Allocation.createFromBitmap(mRS, destBitpmap);
364*90c8c64dSAndroid Build Coastguard Worker             performFilter(inAllocation, outAllocation, destBitpmap, f);
365*90c8c64dSAndroid Build Coastguard Worker 
366*90c8c64dSAndroid Build Coastguard Worker             ThumbnailRadioButton button = (ThumbnailRadioButton) findViewById(ids[mode]);
367*90c8c64dSAndroid Build Coastguard Worker             button.setThumbnail(destBitpmap);
368*90c8c64dSAndroid Build Coastguard Worker         }
369*90c8c64dSAndroid Build Coastguard Worker     }
370*90c8c64dSAndroid Build Coastguard Worker }
371