RenderScriptIntrinsic / src / com.example.android.renderscriptintrinsic /

MainActivity.java

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