I am looking at one of the sample applications from Google, which deals with touch drawing using canvas:
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/TouchPaint.html
I have a few doubts:
I am not able to understand what's the role of Canvas versus the role
of the bitmap.
In the drawPoint function, I am not able to
understand this code snippet:
mCanvas.drawCircle(x, y, radius, mPaint);
mRect.set((int) (x - radius - 2), (int) (y - radius - 2),
(int) (x + radius + 2), (int) (y + radius + 2));
invalidate(mRect);
If the circle is already drawn into the canvas above, then what happens in the onDraw function where the following code is given:
canvas.drawBitmap(mBitmap, 0, 0, null);
Canvas vs Bitmap
A Bitmap is what the name suggests: A normal image as a bitmap. The Canvas class is an editor for bitmaps. You use it to change the bitmap data, it holds all drawing methods. This principle behaves similar to the shared preferences (if you already worked with them), you have a SharedPreferences class that holds the preferences, and an Editor class to change things.
Drawing the circles
This code does something similar to double buffering. drawPoint() basically draws a circle into the mBitmap object¹. But this bitmap object is not yet visible. It exists in the memory. When onDraw() is called, it has a Canvas argument that represents the drawing surface of the view. All that drawBitmap() does here is use the prepared bitmap from the memory and draw it inside the views graphical representation to make it visible.
¹ The used canvas mCanvas is tied to mBitmap inside onSizeChanged()
if you go to the developper refference:
drawBitmap(Bitmap bitmap, float left, float top, Paint paint)
Draw the
specified bitmap, with its top/left corner at (x,y), using the
specified paint, transformed by the current matrix.
Then if you see that mBitmap doesn't exist in the class , thats cause that var comes from the extend from another activity .
Canvas also has a setBitmap(Bitmap bitmap) function . Then the solution is that that paint in canvas if you have set into it a bitmap object.
From the Android SDK:
The Canvas class holds the "draw" calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).
I'm assuming you're referring to this snippet:
#Override protected void onDraw(Canvas canvas) {
if (mBitmap != null) {
canvas.drawBitmap(mBitmap, 0, 0, null);
}
}
Well it looks like an override of an inherited onDraw method which by default probably 'does nothing', hence the override to actually give it some behaviour, in this case given a non-null Bitmap instance, make the canvas draw it.
Related
I read this canvas overview :
The Canvas class holds the "draw" calls. To draw something, you need 4
basic components: A Bitmap to hold the pixels, a Canvas to host the
draw calls (writing into the bitmap), a drawing primitive (e.g. Rect,
Path, text, Bitmap), and a paint (to describe the colors and styles
for the drawing).
Can anyone explain the canvas more clearly?
And I am confused about the relationship between the canvas and matrix. Does the canvas takes the transformations from the matrix?
And I want to know if below function affect on the canvas?
canvas.drawBitmap(bitmap, matrix, paint);
In other words, is the canvas matrix different from the bitmap matrix?
I asked this, because when I'm using canvas.drawBitmap and then using canvas.concat() and then drawing any object, this object takes same transformations on canvas, so I think the canvas and bitmap have the same matrix!!
They are different. When using the canvas to draw a bitmap providing a matrix, internally, the provided matrix are concatenated to the current canvas matrix.
In other words, calling canvas.drawBitmap(rectBitmap, matrix, paint); has the same effect of:
canvas.save();
canvas.concat(matrix);
canvas.drawBitmap(rectBitmap, 0, 0, paint);
canvas.restore();
This explain why your object is taking the same transformations because you are calling canvas.concat(matrix); and after that drawing the object.
Is the Android documentation for canvas.drawBitmap wrong? It says:
public void drawBitmap (Bitmap bitmap, float left, float top, Paint paint)
Draw the specified bitmap, with its top/left corner at (x,y), using the specified paint, transformed by the current matrix.
Well, x and y don’t seem to be floats, they’re ints; is that correct?
Say I want to overlay the bitmap (which is the size of the available screen, and is bound to a canvas of the same) over the whole available screen. It seems sensible I would:
canvas.drawBitmap(myBitmap, 0, 0, mPaint);
doesn’t it?
But that doesn’t work. What does seem to work is:
canvas.drawBitmap(myBitmap, 2000000, 1000000, mPaint).
Now that statement seems to me to tell the bitmap that it should draw itself a huge distance
Outside the screen! What am I missing here?
In this method x and y are floats, not ints. But like mentioned in the documentation, the x and y coordinates of the bitmaps will be affected by the matrix currently set on the Canvas. In the case of a ScrollView for instance, the matrix could very well contain a very large translation.
What this means is that the coordinates 0, 0 will draw the bitmap at the current origin of the Canvas. That origin is defined by the matrix you can query with getMatrix().
Background: Using Canvas, Paint and Path objects I draw several geometries on the canvas, mostly polygons and circles. They fill most of the Android screen.
Question: With Mathematica I can 'fast-copy' Graphics using Translate ( in x and y direction ), after which the resulting image is automatically zoomed out such that all copies are visible. ( For example. Draw a square that fills the entire screen, copy it using (2,2) and four squares appear. ) The premise is that copying is a faster operation. - Is a similar operation possible on Android?
There's nothing as convenient as that, but to achieve the effect you can draw directly to a Bitmap and re-use it - scaling and translating it yourself.
public void onDraw(Canvas canvas) {
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas bmpCanvas = new Canvas(bmp);
// draw into bmpCanvas
// ...
// draw bitmap using
// public void drawBitmap (Bitmap bitmap, Rect src, RectF dst, Paint paint)
canvas.drawBitmap(bmp, ...);
I'm trying to drop a smooth shadow under a view element. Here's is what I've done so far:
Subclass FrameLayout (my ShadowViewport), which overrides dispatchDraw(...) and does:
Call dispatchDraw of superclass and save output as bitmap
Extract alpa from this bitmap
Blur
Draw blurred extracted alpha with some offset
Draw original view bitmap
#Override
protected void dispatchDraw(Canvas canvas) {
Log.d("ShadowViewport", "dispatchDraw");
final Bitmap output = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);
final Canvas sCanvas = new Canvas(output);
// Draw internal view in canvas
super.dispatchDraw(sCanvas);
final Bitmap original = output.copy(Config.ARGB_8888, true);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(0x55000000);
paint.setMaskFilter(new BlurMaskFilter(SHADOW_SIZE, Blur.NORMAL));
canvas.drawBitmap(output.extractAlpha(), 0, SHADOW_SIZE, paint);
canvas.drawBitmap(original, 0, 0, null);
}
Result
Find image here.
As you can see, the left box has a nice generated shadow, even for rounded corners ;)
The question
The on dispatchDraw function is called very often, for example while scrolling. Is there any way for caching?
Enabling the android:hardwareAccelerated="true" attribute in the AndroidManifest will enable automatic caching of the image. Ithe dispatchDraw method is not called for example, during scrolling.
I also think it is possible to use the setDrawingCacheEnabled on your Layout Class. To make that work you should call getDrawingCache first, and the the returned bitmap if it is not null. This seems a bit like a "poor man's" optimization, but is probably a good idea to get performance out of older phones.
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.