Loading images to specific coordinates in SCanvasView - android

I am creating an android application and am working with canvas bitmaps (specifically the Samsung SPen SDK). I currently have the functionality I want working working, but the way the loading is implemented makes the run-time too long.
What I have:
Saving: I am saving the canvas as a bitmap and splitting this bitmap into X smaller bitmaps of a specific size (saving only the sections with writing in them). There is no problem with the efficiency of this section.
Loading::
I then take the X number of smaller bitmaps and combine them to reform the original bitmap using canvas.drawBitmap(imgs[x][y], xCoordinate, yCoordinate, null) for all of the images that have writing in them(imgs[][] = array of bitmaps).
I then display this in the foreground of the canvas using the setclearImageBitmap(BitmapName) function in the SCanvasView class. This works fine besides the fact that loading the one large image into the foreground is taking ~90% of the run time.
private void loadCanvasImage(){
Bitmap bmOverlay = Bitmap.createBitmap(mSCanvas.getWidth(), mSCanvas.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmOverlay);
int yCo = 0;
for(int yCount=0; yCount
}
Question:
I am wondering if there is a more efficient way to do this. Potentially by implementing this idea:
By adding multiple bitmaps to the canvas view (preferably foreground) at specific x,y coordinates (This may make it faster since the smaller bitmaps without writing on them will not have to be displayed).
Another method I failed to implement was to set the SCanvasView to the canvas containing the one large bitmap that I recreated. I did this by trying to override the onDraw(Canvas) method, and passing the canvas result of canvas.drawBitmap(imgs[x][y], xCoordinate, yCoordinate, null)

Related

Android drawing to bitmap canvas slow

Scenario is I have an ImageView that fills the phone screen. Every so often based on touch events I draw an offscreen bitmap and display it on the ImageView.
Code to do the display is
Bitmap b = Bitmap.createBitmap(Globals.screenWidth,Globals.screenHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(b);
Then a bunch of paint settings, drawrects, and font outputs.
Once the bitmap/canvas is updated I show it onscreen by calling
ImageView iv = (ImageView)findViewById(R.id.mainView);
iv.setImageBitmap(b);
This works but is slow. There are not a lot of drawrects and font updates (maybe at most 100 drawrects and 5 or 6 lines of text), but it can take up to 400 ms for it to draw. When it should seem instant to the user when they touch the screen.
Any other ways of getting an offscreen bitmap and/or canvas to display FAST onscreen?

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

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

Cutting out the centre region of a Bitmap and shrinking, in Android

I'm trying to dynamically create images in android by taking an existing Bitmap and removing the centre of it in order to make a "cropped" version. The resulting image's height would naturally be smaller than the original, something like the attached example.
I've got a rough way of doing this by creating two new Bitmaps from the original, one containing the top of the image above the crop section (e.g. the android's head in the example) and the other containing the remaining image below the crop section (the android's feet) using the Bitmap.createBitmap(source, x, y, width, height) method, then drawing both of these bitmaps onto a canvas of a size equal to the original image minus the removed space.
This feels a bit clunky, and as I could be calling this method several times a second, it seems wasteful to create two bitmaps each time.
I was wondering if there was a more efficient way of doing this. Something like drawing the original Bitmap onto a canvas using a Path with it's Paint's xfermode set to a
new PorterDuffXfermode(Mode.DST_OUT) in order to cut out the portion of the image I wish to delete. But this seems to clear that area and not shrink the image down i.e. it leaves a big empty gap in the Android's middle.
Any suggestions greatly appreciated!
Why do you create two bitmaps? You only need to create one bitmap and then do canvas.drawBitmap() twice.
Bitmap bmpOriginal;
Bitmap bmpDerived = Bitmap.create(...);
Canvas canvas = new Canvas(bmpDerived);
canvas.drawBitmap(bmpOriginal, rectTopSrc, rectTopDst, null);
canvas.drawBitmap(bmpOriginal, rectBottomSrc, rectBottomDst, null);
Done.

Capture camera preview and GLSurfaceView to a file

I'm trying to 'take a photo' of both the camera preview, and an overlayed GLSurfaceView.
I have the camera preview element working, via camera.takePicture() and PictureCallback(), but now need to either include the GLSurfaceView elements, or capture the current screen seperately and merge the two bitmaps into one file.
I have tried to grab an image from the surfaceView using the code below, but this just results in a null bitmap.
public Bitmap grabImage() {
this.setDrawingCacheEnabled(true);
Bitmap b = null;
try {
b = this.getDrawingCache(true);
if (b==null) {
b = this.getDrawingCache(true);
}
} catch (Exception e) {
e.printStackTrace();
}
this.setDrawingCacheEnabled(false);
return b;
}
I would appreciate any thoughts/ snippets on this. Many thanks in advance.
I've done something similar and it was a bit convoluted but not too terrible.
In my case I'm using camera preview frame which I decode to a bitmap. Then get a canvas from that bitmap pass it to call to draw() on the views (surfaceview or otherwise) that I want drawn over top of the picture.
Bitmap bm;
MySurfaceViewImpl sv;
Canvas c = new Canvas(bm);
sv.draw(c);
You will need to use your own View implementation to handle the fact that the canvas size is going to change and you'll need to rescale things between the calls to draw() that happen in the normal running of your app and the when you call it manually as the canvas from the picture size is almost certainly going to be different than what's being drawn to the screen.
Also, the primary reason I'm using preview frames rather than captured pictures is due to memory limits. Very few phones support smallish sized pictures but all support reasonable sizes for preview frames. Getting a full size camera picture into a bitmap is probably too much memory. On devices with less than 24MB heap, I'm ok with about a 600 x 480 image and about 4 views that get drawn on top of that but it gets tight.
In your case, you'll probably need to scale the bitmap down to be able to pass a canvas from it to a view.
Good luck!

Is there a way to load and draw partially a bitmap from file in Android?

Say I have a somewhat large (i.e. not fit in most phones' memory) bitmap on disk. I want to draw only parts of it on the screen in a way that isn't scaled (i.e. inSampleSize == 1)
Is there a way to load/draw just the part I want given a Rect specifying the area without loading the entire bitmap content?
I'm quite confident this is possible since you can load a really large bitmap file into an ImageView without problems so there must be some sort of a built-in way to handle large bitmaps... and after a few attempts, I've found a solution:
Instead of loading the entire bitmap and manually draw it yourself, load it as a Drawable instead:
InputStream mapInput = getResources().openRawResource(
R.drawable.transit_map);
_map = Drawable.createFromStream(mapInput, "transit_map");
_map.setBounds(0, 0, _mapDimension.width(), _mapDimension.height());
I'm using a resource file but since you can use Drawable.createFromStream to load image from any InputStream, it should works with arbitrary bitmap.
Then, use the Drawable.draw method to draw it onto the desired canvas like so:
int left = -(int) contentOffset.x;
int top = -(int) contentOffset.y;
int right = (int) (zoom * _mapDimension.width() - contentOffset.x);
int bottom = (int) (zoom * _mapDimension.height() - contentOffset.y);
_map.setBounds(left, top, right, bottom);
_map.draw(canvas);
As in the above case, You can also scale and translate the bitmap as well by manipulating the drawable's bounds and only the relevant parts of the bitmap will be loaded and drawn onto the Canvas.
The result is a pinch-zoomable view from just one single 200KB bitmap file. I've also tested this with a 22MB PNG file and it still works without any OutOfMemoryError including when screen orientation changes.
Now it's very relevant: BitmapRegionDecoder.
Note: available since Android SDK 10
It can easily be done by using RapidDecoder.
import rapid.decoder.BitmapDecoder;
Rect bounds = new Rect(10, 20, 30, 40);
Bitmap bitmap = BitmapDecoder.from("your-file.png")
.region(bounds)
.decode();
imageView.setImageBitmap(bitmap);
It supports down to Android 2.2 (API Level 8).
Generally speaking, that isn't possible, particularly since most image formats are compressed, so you don't even know which bytes to read until you've extracted the uncompressed form.
Break your image up into small tiles and load just the tiles you need to cover the region you want to display at runtime. To avoid jittery scrolling, you might also want to preload tiles that are just out of sight (the ones that border the visible tiles) on a background thread.

Categories

Resources