rotating android.graphics.picture object on SurfaceView without a bitmap conversion? - android

Can't get android.graphics.picture objects to rotate, but translates are OK. No camera involved in the following code.
For making dynamic sprites, recording drawing primitives into an android.graphics.picture object for later use seems consistently more efficient than doing individual primitive draws during onDraw. And a Picture object is very similar to a bitmap, (if not the same?) and can be saved and restored to / from a bitmap.
However, just to be able to rotate these Pictures, I'd like to avoid having to save them as Bitmaps after recording() them. That is, just use the Picture objects like bitmaps, which the canvas api seems to imply is possible. Perhaps the Picture API is less sophisticated, but more likely I'm just doing this wrong.
The canvas API for Bitmaps versus Pictures are not parallel, e.g. there is a:
canvas.drawBitmap(bitmap_Sprite, matrix, null); // Draw sprite with matrix, no Paint
And there is a:
canvas.drawPicture(picture_Sprite, destination_rectangle); Draw sprite into rect.
But there isn't a:
canvas.drawPicture(picture_Sprite, matrix ...)
Details:
Imagine both bitmap_Sprite and picture_Sprite are a "compass arrow" indicating where it's pointing during rotation.
In the onDraw() call, using the bitmap version, this works:
matrix = new matrix();
matrix.setRotate(angle); // angle is ++ during onDraw(canvas)
// Draw arrow, matrix rotates it as expected:
canvas.drawBitmap(bitmap_Sprite, matrix, null); // null = no Paint obj
But attempting the same thing using the picture object, the only way I could figure this out was to rotate the the destination_rectangle (containing the arrow) like this:
//destination_rectangle sized correctly to wrap previously recorded picture obj.
matrix2 = new matrix();
matrix2.setRotate(angle); // angle is ++ during onDraw(canvas)
matrix2.mapRect(destination_rectangle); // apply rotation
canvas.drawPicture(picture_Sprite, destination_rectangle);
But all this does is pivot the rectangle around a location, it doesn't rotate the rectangle (and the arrow) itself.
Questions:
Is using the destination rect. the wrong way to do this?
Is rotation even possible using a Picture object?
Certainly, if I can't get Picture objects to rotate, then I could just use previously created bitmaps / mipmaps directly. So then
Generally, assuming it is possible to use Picture objects the same way as Bitmaps, am I really saving overhead using Pictures which are dynamically created once versus dynamically creating them, saving them as bitmaps, and reloading the bitmaps to do the matrix stuff?(!)

just use Canvas.concat, you will probably want save/restore as well

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.

rotating and cropping a bitmap accurately and effiently in android

I'm writing an android application.By using it,a user can crop the bitmap image.But I want to add more function,that is to rotate the image before cropping.
I followed the below steps to accomplish this purpose.
built a custom view.
initialized BitmapDrawable() and a cropping rectangle(Rect()) [the rectangle determines where to cop in the bitmap]
override onTouch(MotionEvent) method for two functions
a. user can move the bitmap over the screen
b. user can transform the cropping rectangle
override onDraw(Canvas) method to update the screen.This code is for showing bitmap.
bitmap_drawable.draw(canvas,_bitmap_paint);
Problem:1
This step ,that I don't know how to do, is to show a rotated properly. I used this code to rotate the canvas in the onDraw(Canvas) method.
canvas.rotate(_angle);
But it makes all the things on the screen (including the cropping rectangle) to rotate.
Question:2
Is it a wise way to initialize my own Canvas() object to draw the bitmap on?
Note:
Here,the rotation that I want,is very accurate up to 1 degree and is performed by two-finger rotation gesture so that performance is very important and the following code(using matrix) is bad.
Matrix mat = new Matrix();
mat.preRotate(_degree, _one_finger_x, _one_finger_y);
Bitmap _new_bitmap = Bitmap.createBitmap(_old_bitmap, 0, 0, bmWidth, bmHeight, mat, true);
Therefore,I think initializing a separate Canvas() and using canvas.rotate(_degree) is very appropriate.However,this makes controlling touch events quite difficult.Any ideas?Please help.
Problem:2
Suppose that I can now rotate the bitmap separately from all objects,i.e. the cropping rectangle,etc.This step is to crop the bitmap and show the cropped result to the user efficiently.When rotate function is not included, I simply copy the color bytes within the cropping rectangle on the old bitmap as follow.
Bitmap _new_bitmap = Bitmap.createBitmap( _old_bitmap, _cropRectx, _cropRecty, _cropRectWidth, _cropRectHeight, null);
But here,from a rotated bitmap, how can I clone the color bytes through a crossing crop rectangle?
Question:2
How can I crop a rotated bitmap?

Dynamically create / draw images to put in android view

I'm not sure I'm doing this the "right" way, so I'm open to other options as well. Here's what I'm trying to accomplish:
I want a view which contains a graph. The graph should be dynamically created by the app itself. The graph should be zoom-able, and will probably start out larger than the screen (800x600 or so)
I'm planning on starting out simple, just a scatter plot. Eventually, I want a scatter plot with a fit line and error bars with axis that stay on the screen while the graph is zoomed ... so that probably means three images overlaid with zoom functions tied together.
I've already built a view that can take a drawable, can use focused pinch-zoom and drag, can auto-scale images, can switch images dynamically, and takes images larger than the screen. Tying the images together shouldn't be an issue.
I can't, however, figure out how to dynamically draw simple images.
For instance: Do I get a BitMap object and draw on it pixel by pixel? I wanted to work with some of the ShapeDrawables, but it seems they can only draw a shape onto a canvas ... how then do I get a bitmap of all those shapes into my view? Or alternately, do I have to dynamically redraw /all/ of the image I want to portray in the "onDraw" routine of my view every time it moves or zooms?
I think the "perfect" solution would be to use the ShapeDrawable (or something like it to draw lines and label them) to draw the axis with the onDraw method of the view ... keep them current and at the right level ... then overlay a pre-produced image of the data points / fit curve / etc that can be zoomed and moved. That should be possible with white set to an alpha on the graph image.
PS: The graph image shouldn't actually /change/ while on the view. It's just zooming and being dragged. The axis will probably actually change with movement. So pre-producing the graph before (or immediately upon) entering the view would be optimal. But I've also noticed that scaling works really well with vector images ... which also sounds appropriate (rather than a bitmap?).
So I'm looking for some general guidance. Tried reading up on the BitMap, ShapeDrawable, Drawable, etc classes and just can't seem to find the right fit. That makes me think I'm barking up the wrong tree and someone with some more experience can point me in the right direction. Hopefully I didn't waste my time building the zoom-able view I put together yesterday :).
First off, it is never a waste of time writing code if you learned something from it. :-)
There is unfortunately still no support for drawing vector images in Android. So bitmap is what you get.
I think the bit you are missing is that you can create a Canvas any time you want to draw on a bitmap. You don't have to wait for onDraw to give you one.
So at some point (from onCreate, when data changes etc), create your own Bitmap of whatever size you want.
Here is some psuedo code (not tested)
Bitmap mGraph;
void init() {
// look at Bitmap.Config to determine config type
mGraph = new Bitmap(width, height, config);
Canvas c = new Canvas(mybits);
// use Canvas draw routines to draw your graph
}
// Then in onDraw you can draw to the on screen Canvas from your bitmap.
protected void onDraw(Canvas canvas) {
Rect dstRect = new Rect(0,0,viewWidth, viewHeight);
Rect sourceRect = new Rect();
// do something creative here to pick the source rect from your graph bitmap
// based on zoom and pan
sourceRect.set(10,10,100,100);
// draw to the screen
canvas.drawBitmap(mGraph, sourceRect, dstRect, graphPaint);
}
Hope that helps a bit.

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