I am drawing an image in android using canvas.
I'm using surfaceview's ontouch method to let the user scale and move the image.
I can already limit the scaling but I have a problem limiting the translate.
I can limit translate only if the scale of the matrix is in its original form, but if the
scale is changed I'm having a problem limiting its translation.
Related
I have a code which zooms and pans the imageview matrix, it works well but i want the imageview to not be zoomed smaller than my screen, and i don't want it to be zoomed very much, i want to set a limit for zooming and to the same thing for dragging(panning) it should pan horizontally if image's width is larger than screen, and it should pan vertically if image's height is larger than the screen, how can i achieve this result ? i tried some methods from mike Ortiz's but i couldn't get them to work.
I coded this for my app, and it's tricky to get it all right.
I created some Rects and RectFs to do a lot of the interim calculations right off the bat. It's more efficient when you don't have to allocate these on every operation.
I used Matrix.setRectToRect() to find the minimum scale factor, and 3x that for the maximum. Then after the postScale on zoom, I clamp the new [absolute] scale factor to min/max.
Also after the postScale, I also compare the rect coordinates to the screen coordinates and add a translation to keep the image corners outside the screen boundaries. This same logic is also done for dragging operations.
look into this library
https://github.com/davemorrissey/subsampling-scale-image-view
Highly configurable, easily extendable view with pan and zoom gestures for displaying huge images without loss of detail. Perfect for photo galleries, maps, building plans etc.
My question is about drawing performance. Let's say I have a Bitmap for image width=2400px and height=800px.
My Canvas is only 800px wide and 800px high. View containing the Canvas is a child of HorizontalScrollView so user can scroll to see whole image.
I load the Bitmap once and draw it every frame in onDraw method. Does the "offscreen" drawing cause performance hiccups in this scenario? If so, how to get it smoother?
Thanks.
Certainly the Bitmap size is a problem. In typical Android implementation, the texture with dimension over 4000px cannot be rendered. In some Android devices, this limit is 2000px.
Since your objective is to allow user to scroll to see the whole image, if I were you, I won't use HorizontalScrollView. Instead, you should implement a subclass of View and override the onDraw(Canvas canvas) method. You can then detect the touches and modify a Matrix. Such matrix will be used in calling Canvas.drawBitmap(Bitmap, Matrix,Paint)
Specifically for the oversize image problem, upon receiving the Bitmap object, you can slice it into 6 pieces. (2400 x 800 -> 800 x 800 x 6). Then you should control the viewport location and deduce the visible part of the image. In best case, you only need to draw 1 800x800 Bitmap. In worst case, you need to draw 4 800x800 Bitmap.
I want to apply rotation, drag and scaling on Image at same time. Image drag and Scaling works fine but as soon as I apply rotation Image quality lost. Please give me any example which have rotation, move and scaling on Image at same time and Image quality should not lost after applying transformation
Look at MapsDemo in the Maps API extension. It implements a smoothed canvas in order to rotate maps. They do this by turning on filtering in the Paint used to draw the image.
private final Paint mSmooth = new Paint(Paint.FILTER_BITMAP_FLAG);
Some quality will always be lost in rotation because the mapping from source to destination pixels can be perfect only at the 90 degree points.
BTW, instead of drag I think you mean translation.
I would like to show a zoomable and scrollable map-like SVG in my app. The only way to do this without writing your own library etc. is to use an existing library all of which seem to render the SVG to a Bitmap, which can be assigned to an ImageView, for example.
The underlying bitmap quickly gets very large which may result in an out-of-memory exception. How do I draw only part of a possibly large and/or zoomed SVG to a Bitmap? Scaling up a small bitmap looks bad and is not an option.
I am going to answer this question myself because i have been looking for an answer for a while, found it scattered all over the web (but not here) and think that it may be helpful for others.
Can you not use Google's svg-android library? You can scale the Canvas to the correct size before rendering.
Would this be like how Google maps works? They appear to tile and increase/decrease detail as you zoom in and out.
I have done something similar in the past, not sure if it is how Google did it but my scheme worked exceptionally well for me. What I did was break my map into rectangular regions, I would then calculate which regions had any portion that was visible and pull in data only from those regions. Each rectangular region was sub-divided to provide more detail if the user zoomed in. I kept subdividing down to the level I wanted, I stopped at three, because I used a 10 X 10 grid so each level was effectively a magnification of 10 - at level three I was viewing 100 times the detail I was viewing at level one which was sufficient for my needs.
I was even able to animate this so that when you zoomed in you appeared to smoothly "fly" closer to the terrain.
First of all, I am using libsvg-android which requires the NDK but which seems to be very fast, especially if you have to redraw your SVG frequently. Actually, I am using this modified version of the library which enables you to query the width and height of the SVG as specified in the SVG document.
My first approach was this:
long svgID = SvgRaster.svgAndroidCreate();
SvgRaster.svgAndroidParseBuffer(svgID, svgString);
SvgRaster.svgAndroidSetAntialiasing(svgID, true);
Bitmap bitmap = Bitmap.createBitmap(500, 500, Bitmap.Config.RGB_565);
SvgRaster.svgAndroidRenderToAreaUniform(svgID, new Canvas(bitmap), -300, -200, 1500, 1500);
First, the SVG source is parsed. Then, a Bitmap is created that will be assigned to an ImageView later on. Next, the SVG is rendered (zoomed to 1500x1500 pixels in size) to the bitmap. Note the translation (-300, -200) applied because the user has scrolled the image view.
This does not work. It seems that negative offsets are not supported.
Instead, the canvas has to be translated like this:
Canvas canvas = new Canvas(bitmap);
canvas.translate(-300, -200);
SvgRaster.svgAndroidRenderToAreaUniform(svgID, canvas, 0, 0, 1500, 1500);
This works as expected. The translation of the SVG is applied to the canvas; the scale is applied by adjusting the size of the rendered SVG. In this way, you can have a large SVG document and render the proportion you are interested in to a much smaller bitmap.
I've written an ImageView subclass which is scrollable in both the x and the y directions and zoomable. Its image resource is a low resolution raster image of the SVG. When the user scrolls or zooms the image, this version of the SVG is shown. When the user stops interacting with the image, I render the currently visible part of the SVG to a Bitmap the size of my image view and draw this instead of the low resolution image.
Ive been trying to implement a limit to prevent the user from scaling the image too much in my multitouch zoom app. Problem is, when i set the max zoom level by dumping the matrix, the image starts to translate downward once the overall scale of the image hits my limit. I believe it is doing this because the matrix is still being affected by postScale(theScaleFactorX,theScaleFactorY,myMidpointX,myMidpointY) where theScaleFactorX/Y is the amount to multiply the overall scale of the image (so if the theScaleFatorX/Y is recorded as 1.12, and the image is at .60 of its origional size, the overall zoom is now .67). It seems like some sort of math is going on thats creating this translation, and was wondering if anyone knew what it was so i can prevent it from translating, and only allow the user to zoom back out.
Still unsure how postScale affects translation, but I fixed it by having an if statement saying as long as we are within the zoom limit i set, post scale as normal. Otherwise, divide the set zoom limit by the saved global zoom level recorded on ACTION_DOWN and set the scale so it will keep the image at the proper zoom level