What does the Android drawing cache do in the background? - android

I have an application which requires me to draw paths to a canvas. As the number of paths builds up, the cumulative drawing action takes longer, so I thought I would improve speed by using the drawing cache.
To do this I have an onDraw method looking a bit like this:
#Override
protected void onDraw(Canvas canvas) {
Path path;
Paint paint;
synchronized(mPaths) {
buildDrawingCache();
Bitmap bmp = getDrawingCache();
if(bmp!=null) canvas.drawBitmap(bmp,
new Rect(0, 0, bmp.getWidth(), bmp.getHeight()),
new Rect(getLeft(), getTop(), getRight(), getBottom()),
null);
for(int i=mLastCount; i<mPaths.size(); ++i) {
path = mPaths.get(i);
paint = mPaints.get(i);
canvas.drawPath(path, paint);
}
mLastCount = 0;
mDrawn = true;
destroyDrawingCache();
}
}
mPaths and mPaints are lists of Path and Paint objects and I have a method set up to work out how many are new so it only redraws the last one. I originally called setDrawingCacheEnabled(true) instead of buildDrawingCache() and destroyDrawingCache() but this didn't work (that may be significant).
Anyway, this code works—it produces a bitmap which I can write to the canvas, then I only draw the last path and everything is fine except that as I draw more and more paths, it still slows down, as though it were still redrawing all the paths during buildDrawingCache().
Is that what's happening? Ideally I would like the draw method to be blitting a bitmap, then placing a single path over it. If it's generating the bitmap from all the paths, every time, I might as well not bother (in fact I will probably write my own bitmap generation code if this is the case, but it seems a pain).
If it isn't what's happening, is there something else that could be causing the code to slow down?

Related

Android: Efficiently draw on images

The helper function below draws a rectangle on top of input bitmap and returns a thumbnail bitmap. However, I run into java OutOfMemory Error when I call this helper around 1000 times from an activity to populate a list of thumbnails. I tried resizing the tempScaledBitmap to 375, 500 but the quality of the thumbnail image is poor.
Also, I was unable to directly draw on inputBitmap as it was immutable.
What is an efficient way to display a list of 1000 plus thumbnails in an activity?
private static Bitmap drawOnCanvas(Bitmap inputBitmap, FramePoint[] points, ColorCode colorCode){
Bitmap tempScaledBitmap = Bitmap.createScaledBitmap(inputBitmap, 750, 1000, false);
//draw path
Canvas canvas = new Canvas(tempScaledBitmap);
// Path
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(colorCode.equals(ColorCode.GREEN)?Color.GREEN:(colorCode.equals(ColorCode.RED)?Color.RED:Color.BLUE));
paint.setStrokeWidth(5);
Path p = new Path();
p.moveTo(points[0].getPointX(), points[0].getPointY());
p.lineTo(points[1].getPointX(), points[1].getPointY());
p.lineTo(points[2].getPointX(), points[2].getPointY());
p.lineTo(points[3].getPointX(), points[3].getPointY());
p.close();
canvas.drawPath(p, paint);
return tempScaledBitmap;
}
After each call to drawOnCanvas you should recycle your bitmaps:
inputBitmap.recycle();
Also recycle the tempScaledBitmap after they are assigned to ImageViews or wherever to prevent this OOM errors.
Also, you could consider using picasso or glide. For me, it's the best option of all.

Can I draw alpha using just one bitmap?

I have the following code to draw on a canvas. That code is taken from SO and Android SDK demos and I have skimmed it down to better explain my problem. The code is essentially working, but it makes older parts of the alpha drawings get darker over time because it draws the bitmap over and over in onDraw() (which is not a problem while using solid lines as demonstrated in the SDK, but which becomes one when using alpha).
public class CanvasView extends View {
public void init() {
bitmap = Bitmap.createBitmap(1280, 720, Bitmap.Config.ARGB_8888); // a bitmap is created
canvas = new Canvas(bitmap); // and a canvas is instantiated
}
// Events
#Override public boolean onTouchEvent(#Nonnull MotionEvent event) {
float x = event.getX(); float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: // when the user touches down
path.reset(); // the previous path is reset (if any)
drawingTool.down(); // my own class which encapsulates path.moveTo()
break;
case MotionEvent.ACTION_MOVE: // when the user paints
Rect dirtyRect = drawingTool.move(x, y); // encapsulates path.quadTo() // the path is built
if (dirtyRect != null) { invalidate(dirtyRect); } // and the dirty rectangle is invalidated
break;
case MotionEvent.ACTION_UP: // when the user lifts the finger
canvas.drawPath(path, paint); // the final path is drawn
path.reset();
invalidate(); // and the full area invalidated (just to make sure the final drawing looks as it should)
break;
}
return true;
}
#Override protected void onDraw(#Nonnull Canvas canvas) { // after every move or up event
super.onDraw(canvas);
canvas.drawBitmap(bitmap, 0, 0, null); // the previous bitmap is drawn [here is the problem]
canvas.drawPath(path, paint); // and the new path is added
}
}
The problem happens in onDraw(), because the bitmap is drawn over and over. So every time a new path is finished, the previous drawing becomes darker.
I know I could take a second bitmap and cache the results after each path is drawn, and then apply that "clean" bitmap for each new path. But this would be expensive.
Is there a way to draw alpha lines without using a second bitmap or re-drawing everything after each path? I am looking for an inexpensive solution.
Problem here is that bitmap and canvas are directly coupled, so when I draw on the canvas, the result immediately reflects in the bitmap. So I can't just clear one or the other?
I played with this for some time and I realized that the solution is as simple as switching the two commands in onDraw.
Instead of
canvas.drawBitmap(bitmap, 0, 0, null);
canvas.drawPath(path, paint);
use
canvas.drawPath(path, paint);
canvas.drawBitmap(bitmap, 0, 0, null);
Drawing the bitmap last solves the problem. It is still too dark while being painted, so it needs some more tweaking, but the main problem is solved.
Plus, the good thing about it is that I won't need a second bitmap. But it is also clear that a second bitmap wouldn't have made much sense, as my bitmap is caching the image already and onDraw() just draws it into the view.
Actually you can set alpha level to Paint object. For example:
Paint transparentpaint = new Paint();
transparentpaint.setAlpha(100); // 0 - 255
canvas.drawBitmap(bitmap, 0, 0, transparentpaint);
Try to paste this instead of canvas.drawBitmap(bitmap, 0, 0, null);

Strange canvas/bitmap issue

I have an Activity in which the user touches the eye positions on a picture, and this is supposed to draw a little white circle over each. I have a working bit of code that, using the Android FaceDetector tools, finds the eye positions and facial midpoint and draws a rectangle. The drawing part of that code, for reference, is this:
private void drawRectangles(){
Canvas canvas = new Canvas(mBitmap);
Paint paint = new Paint();
paint.setStrokeWidth(2);
paint.setColor(Color.BLUE);
paint.setStyle(Style.STROKE);
for (int i=0; i < faceFrames.length; i++) {
RectF r = faceFrames[i];
if (r != null){
canvas.drawRect(r, paint);
Log.d(TAG, "Drew rectangle");
}
}
mImageView.setImageResource(0);
mImageView.setImageBitmap(mBitmap);
mImageView.draw(canvas);
}
That part's fine. I figured, as a method that is called from onTouchEvent, that I could use the following to draw a circle:
private void makeDrawableLayer(int x, int y, int touchCount){
if (touchCount == 1){
Bitmap eyeOneBmp = Bitmap.createBitmap(mBitmap);
Canvas c1 = new Canvas(eyeOneBmp);
c1.drawCircle(x, y, 5, eyePaint);
mImageView.setImageResource(0);
mImageView.setImageBitmap(eyeOneBmp);
mImageView.draw(c1);
}
}
Here are screen shots showing the result of each code snippet. The first picture is the rectangle drawn on the face. The second picture shows the very strange result I get when I attempt to draw using the second code snippet. Note, however, that I had specified x and y as 10, 10 for the circle's position when drawing the second output. It's the same thing when I give it the passed-in eye position coordinates, just with the pixelated circle coming from wherever the eye is.
Does anyone have any idea what the heck is going on with this behavior?
Thanks so much.
So I found that you can basically only draw one time to the canvas before needing to make a class that extends View to start calling methods from. What I ended up needing to do was: customView.prepareCircle(), customView.invalidate(), and then parentView.addView(customView). And actually, I could only prepare, invalidate, and re-add the modified custom view to the canvas once before having to make any subsequent calls from a Runnable on the UI thread. I am not under the impression this is an ideal way to do it (certainly doesn't feel elegant), but it is giving me the results I want:

Bitmap on SurfaceView Optomization

This link: http://www.mediafire.com/view/?hr441qalu6b6d7s
points to an image that show that my drawing of Bitmaps is taking a long time and resulting in lag in my application. How can I optimize this so as not to cause so much lag. Currently I have this as my canvas method:
Canvas c = holder.lockCanvas();
Paint p = new Paint();
p.setTextSize(30);
p.setColor(Color.BLACK);
new handleStuff().execute("");
//Actions End
//Background
Bitmap scaledBackground = Bitmap.createScaledBitmap(background, this.getWidth(), this.getHeight(), true);
c.drawBitmap(scaledBackground, 0, 0, null);
//Background End
My initial thoughts are that the drawing of the background every single time is what is causing that lag, but I am not sure.
Put all object creation outside of the draw method (so only create the bmp/paint etc in your init/whatever) and then use them in the draw method.
This will speed up thing and reduce memory use and reduce garbage collection ... a lot.

Draw Transparent Bitmaps

I have been facing a weird problem in the last 2-3 days. The thing I would like to do looks easy but my solutions do not respond in somehow I would like. Let me inform you about what I certainly want.
I have a picture(.png). I have split it in 5 parts and saved them as .png also. My expectation is to have a view objects displaying those 5 pictures as a picture. So I have decided to use canvas.drawBitmap() method to draw that 5 pics in onDraw() method. It works fine. However, when I wanted to resize it I have used createScaledBitmap(bitmap, width, height, boolean). I have applied this way to every 5 differen bitmap object to resize and draw in onDraw() method. Unfortunately, I have got a full picture but between every picture there was a line vercitally. I have changed the boolean value as true in the method createScaledBitmap(bitmap, width, height, boolean) then it was working well with a bad quality of the picture.
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < 5; i++) {
Bitmap image = BitmapFactory.decodeResource(getResources(),
getResources().getIdentifier(
"f" + i, "drawable", getContext().getPackageName()));
image = Bitmap.createScaledBitmap(image, getWidth(), getHeight(), true);
canvas.drawBitmap(image, new Matrix(), null);
}
}
http://imageshack.us/photo/my-images/717/96553428.png
http://imageshack.us/photo/my-images/809/33612864.png
http://imageshack.us/photo/my-images/255/53597408.png
Note : Drawing speed matters. It means it should take less than 500 ms to draw.

Categories

Resources