My question may be a bit unclear, but I have extended the View class and generated a number of shapes on the canvas around (0,0). I want to put this point in the middle, so I have to tell the View that it has to draw horizontally, for example, from -640 to 640 on the x-axis and vertically, for example, from -360 to 360 on the y-axis.
Is there a way to tell the view that it has to draw these pixels without changing the coordinates of the drawn shapes. I just want to tell the view that it has to draw certain coordinates.
I want to be able to change dynamically which area is drawn.
I'm not 100% what you are trying to achieve, but if you want to move and scale your shapes, you can use the canvas translate or scale methods, to move the canvas to under your shapes. Remember that it is the canvas you translate, and not the shape, so the transformations will have to be done in reverse. You should also use the canvas save and restore methods to restore your canvas position between transformations.
If you instead want to limit any drawing to an area, you can use the canvas clip-methods, for example:
canvas.clipRect(-640, -360, 640, 360);
Would case any drawing outside that rectangle to be discarded.
Related
So on my canvas I have few rectangular areas where I draw different bitmaps.
Sometimes bitmaps get outside of their respective areas. I want these bitmaps to be cut off, so only the portion of bitmap inside of it's area is drawn.
I sure can calculate it manually (srcRect and dstRect)...
But isn't there a simpler solution?
I found clipBounds, but it doesn't seem to restrict drawing area in any way.
What you want is Canvas.clipRect(). You will need to know the bounds of the clipping rectangle, of course, but you don't need to worry about calculating a custom srcRect.
To use, save() the Canvas, do a single drawBitmap(), then restore() it to get your original clipping state back:
canvas.save();
canvas.clipRect(...);
canvas.drawBitmap(...);
canvas.restore();
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)
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 Question.
I have made a custom ImageView class and inside it I have an onDraw method which will draw a circle on particular pixels (using canvas). When I use this custom imageview and open up my image I would like to set the focus on the circle that I have drawn (e.g like google maps do with your current location. The focus is set to your current point)
What the map server does on google is deliver a customized set of tiles so that the center is displayed properly, the newer version is of course vector based so they simply draw the view so it's centered where they want it.
Without knowing the details of your application you probably need
Create your own container class, probably FrameLayout
public class myMapFrameLayout extends FrameLayout {
The override either onDraw or onDispatchDraw so that you can layout your tile appropriately
Figure out where to draw your bitmap so that the x,y you need will be in the center of the screen, then draw the other tiles that you need to fill in the blank space at the coordinates required dependent on which way the tile was moved to get centered
Think of a virtual screen that is larger than the actual screen with tiles all around it that are the same size
1 2 3
4 X 5
6 7 8
Assuming that X is the size of the display and represents the current tile you need to figure out which way to move the tile, and which other tiles 1,2,3,4,5,6,7 or 8 you need to fill in the empty space caused by move
If you had to draw the tile +x from 0,0 you need some of tile 4, drawing +y from 0,0 means some of 2 and both mean 1,2,4 are all needed and so on, so figure out the combinations and load the tiles you need, and figure out the drawing positions of each. That would give you your new virtual tile with the center displayed.
That's about as efficient as you can get I think with a bitmap drawing method on the client side.
UPDATE
Since your comment indicates you have only one very large image this is going to be a bit of a problem if the x,y you need as anything closer to the edges than the size of the display
None the less you can still draw the image where you need it, just measure the screen and draw the bitmap with the target x,y in the center
So if the screen was 500x500 and your image was 5000x5000 and the center was at position x=1000 y=1000 then
drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint)
where source rectangle would be 1000-250,1000-250,500,500 and dst rectangle would be 0,0,500,500
The 250 is the center x and center y of the display, 1000 are the target x/y coordinates, and 500 is the size of display.
Again, with targets that are at the edges you are going to have a blank polygon in your screen since you dont have an infinite map tile
Alternatively you could oversize your framelayout using layoutparams and just translate the canvas in the x and y to get the canvas centered to the x,y you need using similar calcs which may be more performant, not really sure
Keep in mind you are going to be using a lot of memory if your image is really big
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.