Android DrawBitMap very slow when using ARGB_8888 - android

I find that DrawBitMap is taking 50-60 ms for drawing just three bitmaps one is a rectangle occupying the full screen, one is a circle and another one is a Path. My bitmaps are created by using Canvas.drawPath, drawRect and drawCircle on a blank bitmap with the Bitmap.Config as ARGB_8888.
I am using ARGB_8888 to make the background visible to get a layering effect.
I was shocked to find the time taken as around 50ms as I thought drawBitmap would be a very simple operation. Can someone guide as to is there any fundamental mistake that I am making. Following is my code
Creating the Blank Bitmaps
Rectangle = Bitmap.createBitmap(320,480,Bitmap.Config.ARGB_8888);
Circle = Bitmap.createBitmap(70,70,Bitmap.Config.ARGB_8888);
Leaf1 = Bitmap.createBitmap(20,30,Bitmap.Config.ARGB_8888);
Drawing the Shapes on the appropriate BitMap
Canvas c = new Canvas(Rectangle);
Paint p = new Paint();
p.setAntiAlias(true);
p.setColor(0xff6e8b3e);
c.drawRect(0,0,320,480,p);
Canvas c = new Canvas(Circle);
Paint p = new Paint();
CirclePath = new Path();
p.setAntiAlias(true);
p.setColor(0xffcd661d);
System.out.println("x = "+x+" y = "+y);
CirclePath.addCircle(50,50,10,Path.Direction.CW);
c.drawPath(CirclePath,p);
Canvas c = new Canvas(Leaf1);
Paint paint = new Paint();
Path path = new Path();
paint.setAntiAlias(true);
path.moveTo((float)184.37,(float)219.15);
path.cubicTo((float)188.32,(float)219.15,(float)192.88,(float)220.44,(float)195.62,(float)223.54);
path.cubicTo((float)197.84,(float)226.05,(float)203.2,(float)229.84,(float)198.18,(float)245.98);
Drawing the BitMap in OnDraw
canvas.drawBitmap(Rectangle,0,0,p);
canvas.translate(x,y); // For animation effect
canvas.drawBitmap(Circle,0,0,p);
canvas.drawBitmap(Leaf1,0,0,p);
Now when I record the time taken for this three drawBitMap I find it is taking around 50ms
Is there something big time mistake in the code. Changing the Bitmap.Config to RGB_565 brings the time down to around 8ms but then the background is not visible and I am getting a black box around the path

Looks normal.
Canvas is very slow on transparency.
You can either try to switch to OpenGL ES or design your content with as little transparency as possible so you can use RGB_565 as often as possible.

You should always match the format of your screen. There was a very similar question recently, and Romain mentioned that blits essentially turn into memcpys if the format matches. And, of course, make sure that you're not using an esoteric blit mode.
Also, why are you using anti-aliasing if you're not scaling/rotating anything?
As for 565 not working - I'm just skimming over your code. Are you using the alpha channel? What exactly do your bitmaps look like?

One of the Android devs explains this here. To draw ARGB_8888 quickly you need to draw to a 32-bit window. See bottom of article for benchmarks.

Related

Circle on canvas has rough edges

I want a circle on the canvas in android app.
It can be done either using a bitmap of circle or actually drawing a circle.
I have done both but the circle in later has rough edges.
Why is this happening. And how can i get the Circle as i expect ?
edit:
Since android is running on phones varying in pixel density and screen size, is there a recommended method ? I want the circle to be smooth all the time.
Try
paint.setAntiAlias(true)
or set a flag during creation
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
And tell us if it works

Android custom brush pattern/image

I have an 8x8 Image. (bitmap - can be changed)
What I want to do is be able to draw a shape, given a Path and Paint object onto my SurfaceView.
At the moment all I can do is fill the shape with solid colour. How can I draw it with a pattern.
In the image you can see the brush pattern (The cross). It can be anything from a cross to a donut or an elf.
How would I go about drawing this pattern background.
I also eventually want to apply colours to it.
So far, my theory is to create a clip area of the shape, and tile the bitmaps until area is covered, but this is extreme overkill in processing. Nor sound ideal.
In terms of colouring, I can edit the brushes to be alpha, fill the same with background colour, then draw the images on top. The real issue it the tiling of such patterns.
Ive found a few questions of a similar nature, all unanswered, and/or not applicable to my situation. (use of xmls on views etc)
Did you checked this blog. Its using BitmapShader
Example:
//Initialize the bitmap object by loading an image from the resources folder
fillBMP = BitmapFactory.decodeResource(m_context.getResources(), R.drawable.cross);
//Initialize the BitmapShader with the Bitmap object and set the texture tile mode
fillBMPshader = new BitmapShader(fillBMP, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
fillPaint.setStyle(Paint.Style.FILL);
//Assign the 'fillBMPshader' to this paint
fillPaint.setShader(fillBMPshader);
//Draw the fill of any shape you want, using the paint object.
canvas.drawCircle(posX, posY, 100, fillPaint);

Canvas antialiasing with hardware acceleration (Android API 11 and Later)

I have a simple bitmap that I draw within a canvas & that I rotate using a matrix.
The problem I bump into is that using hardware acceleration the edges are not anti-aliased when rotated (this is working perfectly with Hardware acceleration turning off).
And of course things like “setDrawFilter” are useless as they are ignored when hardware acceleration is turned on!
canvas.setDrawFilter(new PaintFlagsDrawFilter(1, Paint.ANTI_ALIAS_FLAG));
Am I missing something or is it just a limitation of the hardware rendering method ? Is there any alternative?
Setting the antialias flag on the paint would not help anyway. To get antialiased borders on bitmap when rotating them you should add a 1px transparent border around them.
Adding this 1px transparent border is not as easy as I thought. Adding it at runtime is really messy. My app loads a lot of bitmap and to add this border I need to allocate even more bitmap to redraw them. This really bust the heap but as I took good care with avoiding any memory leak, it´s working as intended. What’s really killing me is the “lags” that this has introduced. I tracked down this fluidity lost to the GC. This last one seems to be slowing down the UI Thread when he claim back the recycled bitmaps.
Using a cache is not the solution either. This is the best way towards performance but storing 500x500 PNGs (alpha obliged) is way out of proportion.
So, here I am, hitting the wall. I haven’t yet tried a “draw time” way but my guess is that drawing to an off-screen bitmap buffer that I don’t think will be HW acc. (correct me if I’m wrong) won’t help my cause.
The Idea of having an “AA shader” is really seducing and I see here several advantage. It’s well supported by the hardware acceleration, will of course be a marvel for the memory (no more savage bitmap allocation) and could be totally independent from the bitmap scaling.
I made a quick test and this is by far the best AA (and here I mean in quality) I ever manage to get and … it’s fast… This quick wrap-up do an upper edge AA using shaders.
BitmapShader bitmapShader = new BitmapShader(mBitmap,
TileMode.CLAMP, TileMode.CLAMP);
Matrix m = new Matrix();
m.postTranslate(0, 1);
bitmapShader.setLocalMatrix(m);
final LinearGradient AAshader = new LinearGradient(0, 0, 0, 1, 0x00000000, 0xffffffff, TileMode.CLAMP);
ComposeShader compositor = new ComposeShader(bitmapShader,
AAshader, PorterDuff.Mode.DST_IN);
mPaint.setShader(compositor);
canvas.drawRect(this.getBounds(), mPaint);
Now the big drawback ! How to get a 4 edge AA :) ??? We can probably manage an upper/lower edge AA using a multi steps Gradient but this is still dirty. A nice way will be to combine the source bitmap with some kind of 9-patch to get the transparent border, but we cannot compose 2 bitmapShader… So, here I am once again. How to get this mask Shader! Damn wall ;)
Fix that worked in my case:
private void fixAntiAlias(View viewFromThe80s) {
if (Build.VERSION.SDK_INT > 10) {
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | Paint.FILTER_BITMAP_FLAG);
viewFromThe80s.setLayerType(View.LAYER_TYPE_SOFTWARE, p);
((View) viewFromThe80s.getParent()).setLayerType(View.LAYER_TYPE_SOFTWARE, p);
}
}
Details:
My textview used a 9-patch as a background so I couldn't add a transparent 1px border without impacting the 9-patch pixels. So, instead, I was able to get this working by disabling hardware acceleration for the given view and its parent:
I'm calling this right after view inflation. Specifically in onCreateViewHolder in my case (in the viewholder constructor to be exact). This fixes my rotated TextView which uses a NinePatchDrawable background, similar to the following:
<TextView
android:id="#+id/text_new_feature"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:rotation="-30"
android:background="#drawable/background_new_feature"/>

android - animation by drawing bitmap is not smooth

I am trying to animate several shapes(paths) by drawing them on the surface holders canvas.
At first I was drawing them as paths and everything was fine, the movement was smooth.
As I increased the number of objects(shapes) the performance decreased and I made some
tests to see if instead of drawing shapes drawing bitmaps is faster. And.. drawing
bitmaps seems to be considerable faster (less computation maybe) BUT the movement is
not smooth. It looks like the bitmaps always move from pixel to pixel instead of using anti alias to, I dont know, draw states as half pixel.
The signature of the method looks like :
canvas.drawBitmap(cloudBitmap, float left, float top, Paint p);
which suggests that I should be able to draw a bitmap at 0.5f pixels.
Any idea why ?
I think it might be due to the bitmap being drawn without filtering it for smoothness. Have you set the paint to smooth the bitmap? If not, that might be your solution.
Paint paint = new Paint();
paint.setFilterBitmap(true);

moving a path with a repeating Bitmap image in android

I'm currently writing an android side-scrolling game and am having trouble filling a path with a repeating bitmap image. I'm creating a path from a number of coordinates to make up the "ground" area. I have a character who is fixed in the middle of the canvas and screen and am moving the path to represent the movement of the character. I've been able to fill the path with a repeating image using a BitmapShader. Also I can move the path shape from side to side on the screen. However, the Bitmapshader seems to be using a default origin of 0,0 which means the shader is always drawing the ground repeating image in the same place. This means that even though the path is moving the ground repeated image never appears to move. Does anyone have any idea how to change the origin of the shader or know of a better way to fill the path with a repeating image?
Alternatively, can anyone suggest a better solution for filling a drawable shape with an image?
Thanks
Andy
Thanks, had a look at those...Replica Island seems to use OpenGL quite a lot which is a bit beyond me at present and Snake didn't quite do what I was looking for...eventually got there..
//Shape pathShape = this.getPathShape();
Bitmap groundImage = ImageHandler.getmGroundImage();
int offset = groundImage.getWidth()-(xPosition%groundImage.getWidth());
Path path = new Path();
path.moveTo(coordinates.get(0).getmX(), coordinates.get(0).getmY());
for ( ShapeCoordinate coordinate : coordinates ) {
path.lineTo(coordinate.getmX(), coordinate.getmY());
}
path.lineTo(coordinates.get(coordinates.size()-1).getmX(), mYBase);
path.lineTo(coordinates.get(0).getmX(), mYBase);
path.lineTo(coordinates.get(0).getmX(), coordinates.get(0).getmY());
path.close();
PathShape shape = new PathShape(path,canvas.getWidth(),canvas.getHeight());
BitmapShader bs = new BitmapShader(groundImage, Shader.TileMode.REPEAT,Shader.TileMode.REPEAT);
Matrix matrix = new Matrix();
matrix.reset();
matrix.setScale(1,1);
matrix.preTranslate(offset, 0);
bs.setLocalMatrix(matrix);
ShapeDrawable sd = new ShapeDrawable(shape);
sd.setColorFilter(Color.argb(255, 50*(mLevel+1), 50*(mLevel+1), 50*(mLevel+1)), Mode.LIGHTEN);
sd.getPaint().setShader(bs);
sd.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
sd.draw(canvas);
Have you looked at the Snake example in the android sdk? Also Replica Island is another example of how to do a tile engine in android.

Categories

Resources