I’ve an app that places an effect on a bitmap. When the user slides the slidebar the level of distortion of the efect increases/decreases and the bitmap is then redrawn. My custom view class is called TouchView and this class gets the bitmap and calls a method from the Filters class that processes the image. I’ve had help implementing asyncTask to speed the processing up but to no avail. Is AsyncTask implemented properly. Thanks Matt.
public class TouchView extends View{
private File tempFile;
private byte[] imageArray;
private Bitmap bgr;
private Bitmap bm;
private Bitmap bgr2 = null;;
private Paint pTouch;
private int centreX = 1;
private int centreY = 1;
private int radius = 50;
private int Progress = 1;
private static final String TAG = "*********TouchView";
private Filters f = null;
private boolean AsyncRunning = false;
public TouchView(Context context) {
super(context);
// TouchView(context, null);
}
public TouchView(Context context, AttributeSet attr) {
super(context,attr);
//.....code that gets the bitmap from camera and sdcard
bm = BitmapFactory.decodeByteArray(imageArray, 0, imageArray.length, bfo);
bgr = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
bgr = bm.copy(bm.getConfig(), true);
bgr2 = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
// instantiate the image processing class
f = new Filters();
}// end of touchView constructor
public void findCirclePixels(){
//set level of distortion
float prog = (float)Progress/150000;
// call processing method on bitmap
bgr2 = f.barrel(bgr,prog);
}// end of changePixel()
}//end of onTouchEvent
public void initSlider(final HorizontalSlider slider)
{
Log.e(TAG, "******setting up slider*********** ");
slider.setOnProgressChangeListener(changeListener);
}
private OnProgressChangeListener changeListener = new OnProgressChangeListener() {
@Override
public void onProgressChanged(View v, int progress) {
// TODO Auto-generated method stub
setProgress(progress);
Log.e(TAG, "***********progress = "+Progress);
}
};
private class MyTask extends AsyncTask<Void, Void, Void> {
protected void onPreExecute() {
}
@Override
protected Void doInBackground(Void... params) {
TouchView.this.findCirclePixels();
return null;
}
protected void onPostExecute(Void result) {
TouchView.this.invalidate();
}
}
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawBitmap(bgr2, 0, 0, null);
}//end of onDraw
protected void setProgress(int progress2) {
this.Progress = progress2;
new MyTask().execute();
}
}
.
[update]
This is what i’ve got now. The app is running slower now unfortunately. Have you any further ideas that i could try?
public class TouchView extends View{
private File tempFile;
private byte[] imageArray;
private Bitmap bgr;
private Bitmap bm;
private Bitmap bgr2 = null;;
private Paint pTouch;
private int centreX = 1;
private int centreY = 1;
private int radius = 50;
private int Progress = 1;
private static final String TAG = "*********TouchView";
private Filters f = null;
private boolean AsyncRunning = false;
private MyTask mt = null;
public TouchView(Context context) {
super(context);
// TouchView(context, null);
}
public TouchView(Context context, AttributeSet attr) {
super(context,attr);
tempFile = new File(Environment.getExternalStorageDirectory().
getAbsolutePath() + "/"+"image.jpg");
imageArray = new byte[(int)tempFile.length()];
try{
InputStream is = new FileInputStream(tempFile);
BufferedInputStream bis = new BufferedInputStream(is);
DataInputStream dis = new DataInputStream(bis);
int i = 0;
while (dis.available() > 0) {
imageArray[i] = dis.readByte();
i++;
}
dis.close();
} catch (Exception e) {
e.printStackTrace();
}
BitmapFactory.Options bfo = new BitmapFactory.Options();
bfo.inSampleSize = 1;
bm = BitmapFactory.decodeByteArray(imageArray, 0, imageArray.length, bfo);
bgr = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
bgr = bm.copy(bm.getConfig(), true);
bgr2 = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
f = new Filters();
}// end of touchView constructor
public void findCirclePixels(){
float prog = (float)Progress/150000;
bgr2 = f.barrel(bgr,prog);
}// end of changePixel()
public void initSlider(final HorizontalSlider slider)
{
Log.e(TAG, "******setting up slider*********** ");
slider.setOnProgressChangeListener(changeListener);
}
private OnProgressChangeListener changeListener = new OnProgressChangeListener() {
@Override
public void onProgressChanged(View v, int progress) {
// TODO Auto-generated method stub
setProgress(progress);
Log.e(TAG, "***********progress = "+Progress);
}
};
private class MyTask extends AsyncTask<Void, Void, Void> {
protected void onPreExecute() {
}
@Override
protected Void doInBackground(Void... params) {
TouchView.this.findCirclePixels();
return null;
}
protected void onPostExecute(Void result) {
while(!isCancelled()){
TouchView.this.invalidate();
}
mt.cancel(true);
}
}
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawBitmap(bgr2, 0, 0, null);
canvas.drawCircle(centreX, centreY, radius,pTouch);
}//end of onDraw
protected void setProgress(int progress2) {
this.Progress = progress2;
mt = new MyTask();
mt.execute();
AsyncRunning = true;
}
}
.
[update 2]
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ProgressBar;
public class HorizontalSlider extends ProgressBar {
private OnProgressChangeListener listener;
private static int padding = 2;
public interface OnProgressChangeListener {
void onProgressChanged(View v, int progress);
}
/*
public HorizontalSlider(Context context, AttributeSet attrs,
Map inflateParams, int defStyle) {
super(context, attrs, inflateParams, defStyle);
}
public HorizontalSlider(Context context, AttributeSet attrs,
Map inflateParams) {
super(context, attrs, inflateParams, android.R.attr.progressBarStyleHorizontal);
}*/
public HorizontalSlider(Context context) {
super(context);
}
public HorizontalSlider(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public void setOnProgressChangeListener(OnProgressChangeListener l) {
listener = l;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN
|| action == MotionEvent.ACTION_MOVE) {
float x_mouse = event.getX() - padding;
float width = getWidth() - 2*padding;
int progress = Math.round((float) getMax() * (x_mouse / width));
if (progress < 0)
progress = 0;
this.setProgress(progress);
if (listener != null)
listener.onProgressChanged(this, progress);
}
return true;
}
}
.
[update 3]
Hi, ok i’ve changed my code to your amendements and changed i couple of lines so the view is updated by adding TouchView.this.invalidate() etc. Regarding the the horizontalSlider i’ve set it to check for only ACTION_UP when setting the progress. This way the user can move the slidebar but the view is only invalidated when the user releases the slidebar. i kinda hoped that the bitmap would be updated in real-time as the bar is moved, but the image processing class ‘Filters’ takes about 20 seconds to process a bitmap which is no good. i think i need to work on the latter as i’m sure you should be able to process a bitmap faster than that!:). If this is as fast as AsyncTask can go then i might have to think about drawing the camera bitmap first with no distortion, then create another bitmap overlay where only the circle effect is present. this way the filter cod only has maybe 1/3 of the pixels to loop through? i’ll post the code to make sure that it’s how you’ve suggested.
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import com.tecmark.HorizontalSlider.OnProgressChangeListener;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.PorterDuff.Mode;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
public class TouchView extends View{
private File tempFile;
private byte[] imageArray;
private Bitmap bgr;
private Bitmap bm;
private Bitmap bgr2 = null;;
private Paint pTouch;
private int centreX = 1;
private int centreY = 1;
private int radius = 50;
private int Progress = 1;
private static final String TAG = "*********TouchView";
private Filters f = null;
private boolean AsyncRunning = false;
private MyTask mt = null;
public TouchView(Context context) {
super(context);
// TouchView(context, null);
}
public TouchView(Context context, AttributeSet attr) {
super(context,attr);
tempFile = new File(Environment.getExternalStorageDirectory().
getAbsolutePath() + "/"+"image.jpg");
imageArray = new byte[(int)tempFile.length()];
try{
InputStream is = new FileInputStream(tempFile);
BufferedInputStream bis = new BufferedInputStream(is);
DataInputStream dis = new DataInputStream(bis);
int i = 0;
while (dis.available() > 0) {
imageArray[i] = dis.readByte();
i++;
}
dis.close();
} catch (Exception e) {
e.printStackTrace();
}
BitmapFactory.Options bfo = new BitmapFactory.Options();
bfo.inSampleSize = 1;
bm = BitmapFactory.decodeByteArray(imageArray, 0, imageArray.length, bfo);
bgr = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
bgr = bm.copy(bm.getConfig(), true);
bgr2 = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
f = new Filters();
pTouch = new Paint(Paint.ANTI_ALIAS_FLAG);
pTouch.setXfermode(new PorterDuffXfermode(Mode.SRC_OUT));
pTouch.setColor(Color.TRANSPARENT);
pTouch.setStyle(Paint.Style.STROKE);
}// end of touchView constructor
public void findCirclePixels(){
float prog = (float)Progress/150000;
bgr2 = f.barrel(bgr,prog);
}// end of changePixel()
public void initSlider(final HorizontalSlider slider)
{
// Log.e(TAG, "******setting up slider*********** ");
slider.setOnProgressChangeListener(changeListener);
}
private OnProgressChangeListener changeListener = new OnProgressChangeListener() {
@Override
public void onProgressChanged(View v, int progress) {
// TODO Auto-generated method stub
setProgress(progress);
//TouchView.this.Progress = progress;
if (mt != null) {
mt.cancel(true);
}
mt = new MyTask();
mt.execute();
}
};
private class MyTask extends AsyncTask<Void, Void, Void> {
protected void onPreExecute() {
// Log.e(TAG, "***********in PREEXECUTE");
}
@Override
protected Void doInBackground(Void... params) {
// Log.e(TAG, "***********in DOINBACKGROUND");
if(!mt.isCancelled()){
// Log.e(TAG, "***********in doInBackgroud about to call fcp()");
// Log.e(TAG, "***********in doinbackground asyncStatus = "+mt.getStatus());
TouchView.this.findCirclePixels();
Log.e(TAG, "***********in doinbackground fcp() called!!!!");
}
return null;
}
protected void onPostExecute(Void result) {
// Log.e(TAG, "***********in POSTEXECUTE");
if(!isCancelled()){
// Log.e(TAG, "***********in postExecute task not canclled and about to invalidate");
// Log.e(TAG, "***********in postexecute asyncStatus = "+mt.getStatus());
TouchView.this.invalidate();
// Log.e(TAG, "***********in postexecute invalidate() called!!!!");
// Log.e(TAG, "***********in postexecute asyncStatus = "+mt.getStatus());
}
}
}// end of mytask
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawBitmap(bgr2, 0, 0, null);
canvas.drawCircle(centreX, centreY, radius,pTouch);
}//end of onDraw
protected void setProgress(int progress2) {
//Log.e(TAG, "***********in SETPROGRESS");
this.Progress = progress2;
//Log.e(TAG, "***********in setprogress progress = "+Progress);
//Log.e(TAG, "***********in setProgress about to create mytask ");
mt = new MyTask();
//Log.e(TAG, "***********in setprogress about to execute mytask");
//Log.e(TAG, "***********in setprogress asyncStatus = "+mt.getStatus());
mt.execute();
// Log.e(TAG, "***********in setprogress asyncStatus = "+mt.getStatus());
// Log.e(TAG, "***********in setprogress mytask executed!!!!! ");
}
}
.
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_UP
/*|| action == MotionEvent.ACTION_MOVE*/) {
float x_mouse = event.getX() - padding;
float width = getWidth() - 2*padding;
int progress = Math.round((float) getMax() * (x_mouse / width));
if (progress < 0)
progress = 0;
this.setProgress(progress);
if (listener != null)
listener.onProgressChanged(this, progress);
}
return true;
}
}
It appears that for every value change in your slider, you are creating your
MyTaskand running it. Instead, one thing that might perform better is only updating your listener when the slider has been released (stopped moving). It looks likeHorizontalSlideris a custom class, so I can’t speak for what is happening in there.If a slider is being moved from left to right, it may fire off update events 100 times during that move. That would be 100 background tasks!
UPDATE
One possibility would be to
cancel()your activeMyTaskbefore you produce another one. If you take that route you need to callisCancelled()inonPostExecuteand only invalidate your view if the task is not cancelled. However, if the code infindCirclePixels()is the bottleneck, this will not work. My guess is that cancelling the active task before creating a new one will NOT solve your problem.Coding this up properly (only creating your
MyTaskwhen necessary) is still going to be the best route. Consider that for everyAsyncTaskthe OS has to spawn a new background thread, which comes with its own overhead. Don’t be fooled by the small amount of code you have put indoInBackground().UPDATE AGAIN
I have cleaned up your code and added comments for suggestion: