How to swap regions in bitmap - android

ex.:
I have a bitmap size 500x500. And on this bitmap I have coordinates to 2 regions.
one region is at X=10, Y=10, size 10x10
second region is at X=400, Y=400, size 10x10
What would be the best way to swap those two regions in the bitmap.

You can do it trough Canvas.
Something like:
Bitmap swapped = Bitmap.createBitmap(origin.getWidth(), origin.getHeight(), origin.getConfig());
Canvas drawer = new Canvas(swapped);
drawer.drawBitmap(origin, new Rect(0,0,100,100), new Rect(100,100,100,100), paint);
drawer.drawBitmap(origin, new Rect(100,100,100,100), new Rect(0,0,100,100), paint);
At that point your 'swapped' Bitmap will have the origin pieces drawed in different regions.
For more see the Canvas documentation:
http://developer.android.com/reference/android/graphics/Canvas.html#drawBitmap(android.graphics.Bitmap, android.graphics.Matrix, android.graphics.Paint)

Hm, a simple "cruel" approach can do the work:
Load the bitmap in an 2-dimensional array and swap your cells.
It will take roughly around: 500x500x4 bytes which is a bit less than a 1 mega-byte of memory, which is nothing for android phones nowdays since app have at least 8/16 mgb of ram at their use.(on weaker phones)
Also the operations will be quite fast, even if you do a bunch of processing of the bit maps, such as resizing and so on.....
If you wont best performance you can use native code, there are some libraries for processing bitmaps that are quite memory and cpu efficient.

The best way would be the same as switching any type of data:
make a temporary bitmap to hold area1 data and put there the data.
put area2 data into area1.
put the temporary bitmap data into area2 , and recycle the temporary bitmap.
Here's a sample code that I've written. It's not tested, but should work:
Bitmap origin=...;
Rect r1=...,r2=... ; //assumption: both rectangles are of the same size
//copy from region1 to temp bitmap
Bitmap temp= Bitmap.createBitmap(origin,r1.left,r1.top,r1.width(),r1.height());
//copy from region2 into region1
Canvas canvas=new Canvas(origin);
canvas.drawBitmap(origin, r2, r1, new Paint());
//copy from temp bitmap to region2
canvas.drawBitmap(temp, new Rect(0,0,r2.width(),r2.height()), r2, paint);
temp.recycle();
An alternative way (which might be better in terms of speed and/or memory) would be to use int array instead of a new bitmap object, but I think this method is easy to understand.
Here's the alternative:
Bitmap origin=...;
Rect r1=...,r2=... ; //assumption: both rectangles are of the same size
//copy from region1 to temp pixels
int[] pixels=new int[r1.width()*r1.height()];
origin.getPixels ( pixels, 0, origin.getWidth(), r1.left, r1.top, r1.width(), r1.height());
//copy from region2 into region1
Canvas canvas=new Canvas(origin);
canvas.drawBitmap(origin, r2, r1, new Paint());
//copy from temp pixels to region2
origin.setPixels (pixels, 0, origin.getWidth(), r2.left, r2.top, r2.width(), r2.height());
I hope I didn't make any mistakes here, since I haven't tested it.

Related

How to create canvas with large Bitmap, draw on it, and then scale to screen size to conserve memory?

Simply put, I have a Bitmap resource that is 1800 x 1800 pix due to the detail I need. I create a canvas from the Bitmap and then draw on it. After drawing is complete, It's attached to an ImageView. It works fine on devices with large Heaps but on small devices, it crashes. The Bitmap needs to be the same size for all devices when added to the canvas because the coordinates that I draw to are precise locations on the Bitmap.
Here is my code
initialBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.VeryLargeBitmap);
mutableBitmap = initialBitmap.copy(Bitmap.Config.RGB_565, true);
canvas = new Canvas(mutableBitmap);
....draw stuff here
canvas.drawLine(x, y, x2, y2, paint);
ImageView.setImageBitmap(mutableBitmap);
ImageView..setAdjustViewBounds(true);
I'm sure there is a better way. I have looked into OpenGL but have not tried it yet. It looks to complex for what I'm trying to accomplish.
BitmapFactory.Options o = new BitmapFactory.Options();
o.inMutable = true;
initialBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.VeryLargeBitmap, o);
Doing this should remove the need to copy the bitmap to an immutable one. When you're done with it (saved to file or ready for a new one) do this:
initialBitmap.recycle();
initialBitmap = null;
To remove any reference to it (NOTE: recycle may not be necessary but I like it "to make sure").
EDIT:
Special note is that creating a Bitmap is CPU intensive so it'd be best to decode it in a thread and start drawing when it's ready. You should never create a Bitmap in an onDraw or draw method.

Calculate if bitmap will fit in memory before creating it

I have some text data that i would like to convert to a bitmap.
in order to do that i need to create a bitmap then bind the canvas to it on draw the text using the canvas..
what i want to do is, before creating the bitmap, calculate the size of the bitmap that i need and check if it can fit in the memory. if not i will split the text.
Is there any way to check if a bitmap fits in the memory before creating it ?
I need a possible implementation of a function like this:
private boolean bitmapWillFitInMemory(int width,int height, Bitmap.Config config)
Thanks :)
this might help you to determine how much memory left for your application after calculating your bitmap size:
Double allocated = new Double(Debug.getNativeHeapAllocatedSize())/new Double((1048576));
Double available = new Double(Debug.getNativeHeapSize())/1048576.0;
Double free = new Double(Debug.getNativeHeapFreeSize())/1048576.0;

Drawing bitmaps with alpha channel: please advise... (some solutions and speed issues)

I have a rather big number of small bitmaps (100+, size about 40x40) each one have some opaque and some transparent parts and i need to paint them respecting these areas.
Bitmaps are in ARGB format, 888(rgb) plus a 256bit alpha channel, standard like in PNG format.
The only (working) way i found to draw them is the following approach:
create a bitmap (ARGB_8888)
fill the bitmap with the raw data
extract the alpha layer from the bitmap
create a BitmapShader (RGB_565) based on the original bitmap
Create a paint for the bitmap which uses the created shader
Then paint the alpha mask using the paint with the special BitmapShader.
The initialization code is run only once, of course:
void initializeTile( int t ){
// Allocate the bitmap:
Bitmap original_data = Bitmap.createBitmap( tile_w, tile_h, Bitmap.Config.ARGB_8888);
// Fill with raw data (this is actually native C++ code):
populateBitmap( original_data );
// Get the alpha mask:
tile_mask[ t ] = original_data.extractAlpha();
// Create the bitmap shader:
tile_data = original_data.copy( Bitmap.Config.RGB_565, false);
// Create the shader:
BitmapShader shader = new BitmapShader(tile_data, CLAMP, CLAMP);
// Create the paint:
tile_paint[ t ] = new Paint();
tile_paint[ t ].setDither(true);
tile_paint[ t ].setAntiAlias(true);
tile_paint[ t ].setFilterBitmap(true);
tile_paint[ t ].setShader( shader );
}
And the paint code is the most simple possible, and it's in the main draw loop:
void paintTile(t){
canvas.drawBitmap( tile_mask[ t ], tile_x[ t], tile_y[ t], tile_paint[ t] );
}
Now, on phones like the Ideos (Android 2.2) it run smooth and fine, but on other phones like the top-end Samsung Galaxy SII (Android 2.3) it's crappy and slow. This does not make much sense to me...
So, what do you think of this approach? Are there better, faster, ways to achieve the same result?
And, why do you think it's so slow on modern, fast hardware? Is there any ways to improve it?
Ok, after some work i found out a better solution. I cannot answer my own questions, so please do if you know more than me.
But, in case more people needs this, i am posting my new solution, which is much faster albeit a bit more complicated. The key idea is to use the shader approach ONLY during initialization and not for painting.
To do this, i create a new bitmap which will contain the "clipped" bitmap (with all the transparent areas cleared) using the shader approach, then paint that clipped bitmap without any shader in the draw code.
void initializeTile( int t ){
// Allocate the bitmap:
Bitmap original_data = Bitmap.createBitmap( tile_w, tile_h, Bitmap.Config.ARGB_8888);
// Fill with raw data (this is actually native C++ code):
populateBitmap( original_data );
// Now make a new bitmap to be clipped:
Bitmap clipped_data = Bitmap.createBitmap( tile_w, tile_h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(clipped_data);
Paint clip_paint = new Paint();
clip_paint.setDither(true);
clip_paint.setAntiAlias(true);
clip_paint.setFilterBitmap(true);
clip_paint.setShader( new BitmapShader(original_data, CLAMP, CLAMP));
// Paint the clipped bitmap:
canvas.drawBitmap( tile_mask[ t ], 0, 0, clip_paint );
//Use the clipped bitmap as original bitmap:
tile_data[ t ] = clipped_data;
}
And also drawing code:
void paintTile(t){
canvas.drawBitmap( tile_data[ t ], tile_x[ t], tile_y[ t], null );
}
Overall, this is much faster.
Still it's unclear to me WHY Android would not paint my alpha-channelled bitmaps properly without all this mess!

Spritesheet programmatically cutting: best practices

I have a big spritesheet (3808x1632) composed by 42 frames.
I would present an animation with these frames and I use a thread to load a bitmap array with all the frames, with a splash screen waiting for its end.
I'm not using a SurfaceView (and a draw function of a canvas), I just load frame by frame in an ImageView in my main layout.
My approach is similar to Loading a large number of images from a spritesheet
The completion actually takes almost 15 seconds, not acceptable.
I use this kind of function:
for (int i=0; i<TotalFramesTeapotBG; i++) {
xStartTeapotBG = (i % framesInRowsTeapotBG) * frameWidthTeapotBG;
yStartTeapotBG = (i / framesInRowsTeapotBG) * frameHeightTeapotBG;
mVectorTeapotBG.add(Bitmap.createBitmap(framesBitmapTeapotBG, xStartTeapotBG, yStartTeapotBG, frameWidthTeapotBG, frameHeightTeapotBG));
}
framesBitmapTeapotBG is the big spritesheet.
Looking more deeply, I've read in the logcat that the createBitmap function takes a lot of time, maybe because the spritesheet is too big.
I found somewhere that I could make a window on the big spritesheet, using the rect function and canvas, creating small bitmaps to be loaded in the array, but it was not really clear. I'm talking about that post: cut the portion of bitmap
My question is: how can I speed the spritesheet cut?
Edit:
I'm trying to use this approach but I cannot see the final animation:
for (int i=0; i<TotalFramesTeapotBG; i++) {
xStartTeapotBG = (i % framesInRowsTeapotBG) * frameWidthTeapotBG;
yStartTeapotBG = (i / framesInRowsTeapotBG) * frameHeightTeapotBG;
Bitmap bmFrame = Bitmap.createBitmap(frameWidthTeapotBG, frameHeightTeapotBG, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bmFrame);
Rect src = new Rect(xStartTeapotBG, yStartTeapotBG, frameWidthTeapotBG, frameHeightTeapotBG);
Rect dst = new Rect(0, 0, frameWidthTeapotBG, frameHeightTeapotBG);
c.drawBitmap(framesBitmapTeapotBG, src, dst, null);
mVectorTeapotBG.add(bmFrame);
}
Probably, the Bitmap bmFrame is not correctly managed.
The short answer is better memory management.
The sprite sheet you're loading is huge, and then you're making a copy of it into a bunch of little bitmaps. Supposing the sprite sheet can't be any smaller, I'd suggest taking one of two approaches:
Use individual bitmaps. This will reduce the memory copies as well as the number of times Dalvik will have to grow the heap. However, these benefits may be limited by the need to load many images off the filesystem instead of just one. This would be the case in a normal computer, but Android systems may get different results since they're run off flash memory.
Blit directly from your sprite sheet. When drawing, just draw straight from sprite sheet using something like Canvas.drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint). This will reduce your file loads to one large allocation that probably only needs to happen once in the lifetime of your activity.
I think the second option is probably the better of the two since it will be easier on the memory system and be less work for the GC.
Thanks to stevehb for the suggestion, I finally got it:
for (int i = 0; i < TotalFramesTeapotBG; i++) {
xStartTeapotBG = (i % framesInRowsTeapotBG) * frameWidthTeapotBG;
yStartTeapotBG = (i / framesInRowsTeapotBG) * frameHeightTeapotBG;
Bitmap bmFrame = Bitmap.createBitmap(frameWidthTeapotBG, frameHeightTeapotBG, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bmFrame);
Rect src = new Rect(xStartTeapotBG, yStartTeapotBG, xStartTeapotBG+frameWidthTeapotBG, yStartTeapotBG+frameHeightTeapotBG);
Rect dst = new Rect(0, 0, frameWidthTeapotBG, frameHeightTeapotBG);
c.drawBitmap(framesBitmapTeapotBG, src, dst, null);
mVectorTeapotBG.add(bmFrame);
}
The computation time falls incredibly! :)
Use a LevelListDrawable. Cut the sprites into individual frames and drop them in your drawable resource directory. Either programmatically or through an xml based level-list drawable create your drawable. Then use ImageView.setImageLevel() to pick your frame.
I use a method of slicing based on rows and columns. However your sprite sheet is rather huge. You have to think you are putting that whole sheet into memory. 3808x1632x4 is the size of the image in memory.
Anyway, what I do is I take an image (lets say a 128x128) and then tell it there are 4 columns and 2 rows in the Sprite(bitmap, 4, 2) constructor. Then you can slice and dice based on that. bitmap.getWidth() / 4 etc... pretty simple stuff. However if you want to do some real stuff use OpenGL and use textures.
Oh I also forgot to mention there are some onDraw stuff that needs to happen. Basically you keep an index counter and slice a rectangle from the bitmap and draw that from a source rectangle to a destination rectangle on the canvas.

How to copy a smaller bitmap into a larger one?

Hopefully this should be an easy question. I'm trying to copy a series of small bitmaps into a larger one, arranging them side by side without any gaps or overlap in their pixels. For example, if I have 3 square bitmaps, I'd like to copy them into one long and thin rectangle. I know how to do the opposite, namely creating a small bitmap out of a larger one, but not this way around. What's the right command?
(If anyone's curious, I want to do this to be able to reuse some code I wrote for handling animation with a single bitmap.)
Thanks!
Create a canvas for the large bitmap, then use that to draw your small bitmaps. I'm pretty new to android, but I'm guessing that it's something like this:
Bitmap makeBigBitmap(Bitmap srcBmps[]) {
Bitmap wideBmp;
Canvas wideBmpCanvas;
Rect src, dest;
// assume all of the src bitmaps are the same height & width
wideBmp = Bitmap.createBitmap(srcBmps[0].getWidth() * srcBmps.length,
srcBmps[0].getHeight(), srcBitmaps[0].getConfig());
wideBmpCanvas = new Canvas(wideBmp);
for (int i = 0; i < srcBmps.length; i++) {
src = new Rect(0, 0, srcBmps[i].getWidth(), srcBmps[i].getHeight());
dest = new Rect(src);
dest.offset(i * srcBmps[i].getWidth(), 0);
wideBmpCanvas.drawBitmap(srcBmps[i], src, dest, null);
}
return wideBmp;
}

Categories

Resources