Why does drawing a shape to a bitmap reduce quality? - android

Why does drawing a shape to a bitmap cause the shape to be blurry versus drawing the shape straight to the canvas?

Bitmap is a rasterized computer image, consisting of points (pixels).
The Canvas class holds the "draw" calls.
A shape is an abstract definition of shape (vector-like).
drawing the shape straight to the canvas
That's not accurate, maybe you mean straight to the screen of device, which is Bitmap too.
To use Canvas for drawing, you need the canvas to host the calls, some bitmap as target, and some drawing primitive (shape is one), and Paint (contains information how to paint the drawing primitive).
Once you draw some shape into the target bitmap, it will be aliased to the pixels of target bitmap. Ie. an circle will turn into some approximation created by rectangular pixels of target bitmap.
What you probably see in your particular case is, that your Bitmap has lower resolution than screen, and when you draw that low-res bitmap to the target screen bitmap, it is upscaled by some filter which makes the upscaled picture a bit blurry to avoid big rectangular pixelation (or it may be also other way, downscaling hi-res bitmap, which contains too sharp/thin contours, which will be aliased second time when downscaled, and even with anti-aliasing filtering it will get blurred a bit). Or maybe you use some paint with settings causing blur (unlikely, can't think of creating one by accident, you would knew).
If you will use for both targets Bitmap of identical density (resolution of single pixel), and the same paint method, then the result will be same, and also drawing the shape from bitmap to bitmap as long as you will use whole pixel coordinates/size, and no filtering, again the result will be same as drawing directly to screen bitmap.
So start first by checking the size of your bitmap vs screen bitmap, and then check paint settings and additional canvas arguments to draw the bitmap, whether you don't upscale/downscale it with some kind of filtering.

Related

Selecting rotated rectangular zones of a bitmap and draw them unrotated efficiently

I need some advice regarding efficient use of canvas and matrices
I have a source bitmap "B0" loaded in memory, which is WxH
I have a bitmap B1, onto which I draw with a canvas.
There is a rotated (with an arbitrary angle) rectangular portion
(w_p*h_p) of B0. I need to get this portion and draw it, once unrotated, onto B1
I would like to do it with normal Views, canvas and matrices. Not surfaceviews, not opengl
An "already working" approach is:
Rotate the bitmap B0 to compensate for the selected zone rotation -->
we get B0_r
Calculate the translated rectangle, which will now be unrotated. We
have SrcRect_u
With a canvas, draw the selected rectangle of B0_r (srcRect_u) onto
B1
However, if B0 is large enough, the rotation operation is very expensive since it applies to the whole bitmap. Also, it means to create an intermediate bitmap each time.
I need to repeat this step in a gameloop, where this rectangle (srcRect) can be rotated and translated, so it must be a "cheap" operation to achieve this
My question: is there a better approach in terms of efficiency , using canvas, matrices and "normal" Views?
EDIT
To better illustrate what I mean, I have added some pics.
B0, with the rotated selection zone
B0 rotated. Now the selection zone is unrotated
unrotated selection zone drawn onto a part of B1
Yes. Rotate the canvas you're going to be drawing to via canvas.rotate. Then draw the subset of the bitmap you wish via drawCanvas. This will have the effect of drawing a rotated bitmap, without rotating the bitmap or creating an intermediate. If you want to draw some things rotated and some unrotated, save the matrix (via canvas.save), rotate it, draw the bitmap, then restore (via canvas.restore)

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.

How to paint bitmap only on non-painted pixels?

I have a 32-bit bitmap that I want to draw on a canvas mapped to a 32-bit offscreen bitmap. The offscreen bitmap is usually empty (just created), but sometimes I have already painted some stuff. In this case, I want my painted stuff to remain untouched when I paint this other bitmap.
Hence, if the destination pixel (x,y) is set, I want my call to drawBitmap to skip painting its source pixel (x,y).
Can this be done?

Android - Draw bitmap as one color

I have several bitmaps (game sprites) which I'd like to draw into another bitmap, however each non-transparent pixel of the source bitmap should be drawn using a single color, ignoring the original pixel color of the source. Basically, I'm trying to use the sprite as a "stamp" of a single color to be drawn into the destination bitmap.
I believe I should be using canvas.drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint), however I'm not exactly sure how I should initialize the paint object. Is this approach correct?
You don't need to perform as many steps as Romain Guy suggests, just initialize your paint with the desired color, and use Paint.setColorFilter() with PorterDuff.Mode.SRC_ATOP
myPaint.setColorFilter(new PorterDuffColorFilter(myColor, PorterDuff.Mode.SRC_ATOP));
If your destination bitmap is transparent, draw all your sprites inside that bitmap normally (you can use a null Paint.) Then, draw a filled rectangle that covers the entire bitmap, using the Porter-Duff xfermode called SrcIn (Source In.)

Android: adding a bitmap texture to a non rectangular item

I have a widget which looks like this:
Every cone is a "touchable element". Now I want to put a bitmap texture above each cone. However, bitmap images are all rectangular, so a bitmap texture above one cone would interfere with the bitmap texture above another cone.
I'm wondering what is the best solution to this approach. Should I just create an image which fits (as a rectangle) exactly above the cone and make the non used areas transparent?
A second question is, how do bitmap textures work with stretching? Because this whole circle draws itself to fit the whole screen size, while bitmap textures are pretty much one size.
Offhand I can't think of a better way to draw bitmaps over those cones than your own suggestion of using transparent zones.
However, I can help with your second question as stretching bitmaps is not hard. You've got a few options in the Canvas class. For example:
canvas.save();
canvas.scale(xRatio, yRatio);
canvas.drawBitmap(....);
canvas.restore();
You can also use a Matrix, using matrix.postScale(xRatio, yRatio), to then generate either a larger bitmap and draw it normally, or pass the matrix in to your canvas.drawBitmap(....) command to make it scale while it draws.
All of these methods assume you have access to the drawing canvas itself. If you are using a view, you can subclass it and override the onDraw(Canvas canvas) method to grab the canvas before it starts drawing it. If you're using a SurfaceHolder, then you should already know how to get the canvas.
Edit: I forgot the third method I was going to describe. You can use the canvas.drawBitmap(bitmap, srcRect, dstRect, paint) to also make the canvas scale the bitmap to fit the destination rectangle. In short, there are lots of methods to do this - pick the one that's easiest based on your application!

Categories

Resources