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