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
}