Android: drawing path over Bitmap is very slow - android

I am developing app which can give user ability to open picture in ImageView and then draw lines on it and also erase them. But i have one big problem: when i am loading a big image the drawing process becomes very slow. But when the image is small then drawing process is normal))).
This is an algorithm what i do to draw lines:
1. Open image from SD card (decode image size and rescale it if necessary).
2. Put Bitmap into ImageView by setImageBitmap(bitmap); method
3. Create transparent bitmap with width and height as the original bitmap has (to draw and erase lines on it).
4. Draw or erase lines on transparent bitmap (using Path class) in ImageView onDraw() method
This is my onDraw() method:
#Override
protected void onDraw(Canvas canvas) {
super.setImageMatrix(mMatrix);
super.onDraw(canvas);
if (!mFragmentImage.inDrawMode()) return;
canvas.save();
if (mFragmentImage.mShape!=null){
if (mFragmentImage.mShape.getType() == CustomShape.SHAPE_LINE) {
mOverlayCanvas.drawPath(mFragmentImage.mShape.getPath(), mFragmentImage.mShape.getPaint());
}
}
if (mOverlay!=null) canvas.drawBitmap(mOverlay, mMatrix, null);
canvas.restore();
}
Can anyone give some advices how to make drawing process faster.

Related

How to draw Bitmap from ARGB_8888 Bitmap

I am drawing paths on canvas and creating Transparent bitmap while saving and creating cropping bitmap from transparent bitmap.
See Images :
In this I mage I am drawing path on canvas and I am creating transparent bitmap and according to startX,lowestY and highestX,highestY
Bitmap cropBitmap =Bitmap.createBitmap(sourceBitmap,startX,lowestY,highestX,highestY);
When I am cropping Bitmap I want Only "Test" drawing crop bitmap.But it's giving empty bitmap. Like this
Inside red box I want cropped bitmap from transparent bitmap whatever I draw on canvas.
You cannot simply create a transparent bitmap, and assume that whatever is within the bounds of the bitmap has become a part of it meaning that the pixels are identical. There are two problems with your assumption, number 1, a transparent bitmap won't help because transparency serves as a see through bitmap which only will have alpha value, number 2, the data that is drawn with the path, has no correlation to the data of your bitmap, you initially created a bitmap with no color, except that it's completely transparent.
Here's the general code for the correct way how to achieve such a task:
class myDrawingView extends View() {
// all your class members you initialize here
#Override
public void onDraw(Canvas canvas) {
// get your width and height using startx, starty, highestx, highesty, lowesty
Bitmap buffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
canvas.setBitmap(buffer);
canvas.drawPath(//draw your path here);
invalidate();
}
The Bitmap called buffer, now is the frame buffer for all your drawing so you can draw a path and that will be rendered on the buffer you defined. And you see that no transparency was used we just simply created a bitmap with no pixels assigned to it at first, once you draw to it via the canvas, your bitmaps pixels will be whatever you drew.

Android: drawing on large bitmap, 2 layers, best way?

im working on an app, that displays large(around 2000x2000px) bitmap in imageview. This image has to be that large since user can pinch to zoom it in order to see some details. App has to be able to draw circles on that image, and also to display image alone, without circles on it. I was using 2 layers but the problem is memory since 2k x 2k px is around 16mb of memory, and creating another bitmap(another 16mb), just to draw a few circles, is pointless in my opinion. Is there any way, that you can draw simple primitives on image, and also be able to display it without primitives(circles in my case)?
Maybe somehow to store only modified pixels or sth?
Thanks!
You don't need to make another 2000x2000 Bitmap to draw those circles on. Just 'prerender' a circle, and then choose where you draw it.
I'm working under the assumption that you're drawing your 'big' image on a Canvas, since you have zooming features etc.
If you're not, you'll need to override your SurfaceView's onDraw(Canvas canvas) method so that you can access the SurfaceView Canvas. I won't go into depth about that part since again I'm assuming you have it, but if not the implementation of that function would look like this:
//Overriding SurfaceView onDraw(Canvas canvas)
#Override
protected void onDraw(Canvas surfaceCanvas) {
if(canvas == null) return; //No Canvas? No point in drawing then.
surfaceCanvas.drawColor(Color.BLACK);
//Draw your 'big' image on the SurfaceView Canvas
insertYourBigImageDrawingFunctionHere(surfaceCanvas);
//Now draw your circles at their correct positions...
insertCircleDrawingFunctionHere(surfaceCanvas);
}
Now that you have access to the SurfaceView Canvas, you can choose precisely how things are drawn on it. Like circles for example...
I want to draw your attention to the multiple Canvas' being used below (surfaceCanvas vs. circleCanvas). I once thought that Canvas was a kind-of 'one Canvas for the whole app/activity' implementation, but it isn't. You are free to create Canvas' as you please. It is merely an instance of a tool to draw onto Bitmaps. This was a HUGE revelation for me, and gave me much more robust control over how Bitmaps are composed.
public void myCircleDrawingFunction(Canvas surfaceCanvas){
//Make a new Bitmap for your circle
Bitmap.Config conf = Bitmap.Config.ARGB_4444;
tinyCircleBMP = Bitmap.createBitmap(10,10, conf);
//Make a new canvas using that Bitmap as the source...
Canvas circleCanvas = new Canvas(cacheBmp);
//Now, perform your drawing on the `Canvas`...
Paint p = new Paint();
circleCanvas.drawCircle(5, 5, 5, p);
//Now the `Bitmap` has a circle on it, draw the `Bitmap` on the `SufaceView Canvas`
surfaceCanvas.drawBitmap(tinyCircleBMP, 10, 10, p);
//Replace the '10's in the above function with relevant coordinates.
}
Now obviously, your circles will zoom/pan differently to your 'big' image, since they are no longer being drawn at the same size/position of the 'big' image. You will need to consider how to translate the positions of each circle taking into account the current scale and position of the 'big' image.
For example, if your image is zoomed in to 200%, and a circle is supposed to appear 100px from the left of the big image, then you should multiply the pixel values to take into account the zoom, like this
(PsuedoCode):
drawCircleAtX = Bitmap.left * BitmapZoomFactor
If you are using the canvas API (if not I would suggest to)? if so you are just draw your image on the canvas and then the primitive shapes on top of the same canvas before display. This way you just keep a reference of the circles position in some basic data types and scale them as the user moves around and zooms, so you know where to draw them each frame.

Android Canvas draw a multiple frame Image

I have an image with different frame to be displayed, like the following:
As you can see that image has three frames, the full heart, half heart and empty space.
Now i need to only one frame of the three. I'm just wondering if there is a method to do that using a single gif in android sdk.
For example in several language there is a method like:
canvas.drawImage(xpos,ypos,xwidth,xheight,gifx,gify,gifwidth,gifheight)
where gifx,gify,gifwidth,gifheight are the coordinates and size of the selected frame.
Ok i answer myself, i just found a solution.
In order to draw a multiframe image the following method can be used:
public void drawBitmap (Bitmap bitmap, Rect src, RectF dst, Paint paint)
Where:
Bitmap bitamp is the Bitmap resource obtained with BitmapFactory.decodeResource (or via your preferred method).
Rect src is the frame to be shown (it can be null)
Rect dst rhe rectangle that the bitmap will be scaled/translated to fit into (it can be null)
Paint paint used to draw the bitmap (it can be null)
Another way (that i didn't tested) could be using BitmapRegionDecoder first of all a new instance of the object must be created using BitmapRegionDecoder.newInstance(...)
and then the selected region of bitmap to be shown could be obtained with the method:
public Bitmap decodeRegion (Rect rect, BitmapFactory.Options options)
Where rect is the selected region to be shown. For more info on BitmapRegionDecoder:
http://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html

How to set an image to fill a canvas

I am trying to set a png image to fill the background of my canvas while still maintaining its aspect ratio. I start by converting it to a Bitmap:Then set the back ground using the setBitmap method from the Canvas class:
http://developer.android.com/reference/android/graphics/Canvas.html#Canvas(android.graphics.Bitmap)
public class PlayOn extends View{
Bitmap board;
public PlayOn(Context gamecontext) {
super(gamecontext);
board=BitmapFactory.decodeResource(getResources(),R.drawable.board_rev1);
}
#Override
protected void onDraw(Canvas mycanvas) {
super.onDraw(mycanvas);
mycanvas.setBitmap(board);
}
}
But once I go to the Activity that calls this extended View class I get an error saying my application stopped unexpectedly.
I've also tried playing around with some of the other functions in the Canvas and Bitmap class but nothing seems to work.
Please what is the best way to do this? I read on the android developer site that there is a way to set an image so that it is the canvas and other images can then be drawn inside it but I wasn't able to figure out how to do that.
Thanks!
You might want to add a Log.d to check that the board bitmap returned from
BitmapFactory.decodeResource(getResources(),R.drawable.board_rev1);
isn't null. But I am using the following to draw bitmaps to full screen views in onDraw in several applications, so if the bitmap is non-null that should work fine.
canvas.drawBitmap(mBitmap, 0, 0, null);
And there's a version of drawBitmap which scales, namely
void canvas.drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint)
Draw the specified bitmap, scaling/translating automatically to fill the
destination rectangle.
You might want to try that?

How to blit() in android?

I'm used to handle graphics with old-school libraries (allegro, GD, pygame), where if I want to copy a part of a bitmap into another... I just use blit.
I'm trying to figure out how to do that in android, and I got very confused.
So... we have these Canvas that are write-only, and Bitmaps that are read-only? It seems too stupid to be real, there must be something I'm missing, but I really can't figure it out.
edit: to be more precise... if bitmaps are read only, and canvas are write only, I can't blit A into B, and then B into C?
The code to copy one bitmap into another is like this:
Rect src = new Rect(0, 0, 50, 50);
Rect dst = new Rect(50, 50, 200, 200);
canvas.drawBitmap(originalBitmap, src, dst, null);
That specifies that you want to copy the top left corner (50x50) of a bitmap, and then stretch that into a 150x150 Bitmap and write it 50px offset from the top left corner of your canvas.
You can trigger drawing via invalidate() but I recommend using a SurfaceView if you're doing animation. The problem with invalidate is that it only draws once the thread goes idle, so you can't use it in a loop - it would only draw the last frame. Here are some links to other questions I've answered about graphics, they might be of use to explain what I mean.
How to draw a rectangle (empty or filled, and a few other options)
How to create a custom SurfaceView for animation
Links to the code for an app with randomly bouncing balls on the screen, also including touch control
Some more info about SurfaceView versus Invalidate()
Some difficulties with manually rotating things
In response to the comments, here is more information:
If you get the Canvas from a SurfaceHolder.lockCanvas() then I don't think you can copy the residual data that was in it into a Bitmap. But that's not what that control is for - you only use than when you've sorted everything out and you're ready to draw.
What you want to do is create a canvas that draws into a bitmap using
Canvas canvas = new Canvas(yourBitmap)
You can then do whatever transformations and drawing ops you want. yourBitmap will contain all the newest information. Then you use the surface holder like so:
Canvas someOtherCanvas = surfaceHolder.lockCanvas()
someOtherCanvas.drawBitmap(yourBitmap, ....)
That way you've always got yourBitmap which has whatever information in it you're trying to preserve.
In android you draw to the canvas, and when you want it to update you call invalidate which will the redraw this canvas to the screen. So I'm guessing you have overridden the onDraw method of your view so just add invalidate();
#Override
public void onDraw(Canvas canvas) {
// Draw a bitmap to the canvas at 0,0
canvas.drawBitmap(mBitmap, 0, 0, null);
// Add in your drawing functions here
super.onDraw(canvas);
// Call invalidate to draw to screen
invalidate();
}
The above code simply redraws the bitmap constantly, of course you want to add in extra thing to draw and consider using a timing function that calls invalidate so that it is not constantly running. I'd advice having a look at the lunarlander sources.

Categories

Resources