What is the correct way to rotate bitmap every frame in main game loop?
What I have tried:
I created rotated bitmap every frame:
Bitmap image, tmp;
Matrix m;
...
public void mainGameLoop(Canvas c){
m.reset();
m.postRotate(angle);
tmp = Bitmap.createBitmap(image, 0, 0, width, height, m, true);
c.drawBitmap(tmp, 50, 50, null);
}
However, because Bitmap.createBitmap creates bitmap every frame Garbage Collector works so madly, it causes low FPS.
I have tried saving bitmap in array at all angles, and just taking bitmap which I want to. However, this needs a lot of RAM and app just couldn't launch at high screen resolutions.
If you have any ideas, please comment below.
Some think like this:
Bitmap image;
Matrix m;
.
.
.
public YourClass(){
m = new Matrix();
image = Bitmap.createBitmap(image, 0, 0, width, height,);
}
.
.
.
public void mainGameLoop(Canvas c){
m.setRotate(angle, imageCenterX, imageCenterY);
yourCanvas.drawBitmap(image, m, null);
}
Related
I am developing an Instagram like application for learning image processing and android. But I am stuck, I have a problem implementing Grayscale Filter in my application. I am trying a simple approach for now to convert individual pixels in a Bitmap to Grayscale.
Here's the whole class I am writing to apply various filters to an Image:
package com.dosa2.photoeditor.ImageEffects;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
public class ImageEffects {
Bitmap bitmap;
int width, height;
public ImageEffects(Bitmap bitmap) {
this.bitmap = bitmap;
width = bitmap.getWidth();
height = bitmap.getHeight();
}
public Bitmap toGrayscale() {
Bitmap resultBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(resultBitmap);
Paint p = new Paint();
ColorMatrix cm = new ColorMatrix();
cm.setSaturation(0);
ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
p.setColorFilter(f);
c.drawBitmap(bitmap, 0, 0, p);
return resultBitmap;
}
public Bitmap toGrayscale2() {
Bitmap resultBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
for(int i=0;i<height;i++) {
for (int j=0;i<width;j++) {
int c = bitmap.getPixel(i,j);
resultBitmap.setPixel(i, j, (Color.red(c)+Color.blue(c)+Color.green(c)/3));
}
}
return resultBitmap;
}
}
I have tried 2 methods to convert the Bitmap into Grayscale. The former seems to be working(but I am not able to understand it) and the latter is not.
Can anyone help me out? And do mention if there's an easier way to manipulate Images in Android.
The error (or at least one of them...) is in one of your for loops:
for (int j=0;i<width;j++)
should be
for (int j=0;j<width;j++)
to prevent an indefinite loop.
Your method "toGrayscale" is using the ColorMatrix class, which I think internally uses the RenderScript API to do the rendering (or at least GPU shaders). RenderScript is the Android's computing API (which is similar to OpenCL, in fact, is a layer that works on OpenCL), so you are not using only the CPU to do the color filtering, you are using even the GPU or other DSPs your device may have. The second method "toGrayscale2" is slower because you are using only the CPU to convert your Bitmap to grayscale (pixel by pixel) and you shouldn't use it. Check this presentation (it's very intersting) in order to understand a bit more how your first method works, the link points to the page 12 which is about color filtering, but you should view it entirely in order to understand better.
I have created a small applicaiton (a game), that reuses the same set of images several times. SO i thought i should create a class that is responsible for loading all of the different images once, from which i can then access them statically from other classes. However, i believe this may have caused an issue with the Garbage Collector, which causes my App to lag whenever the GC is run. Here is What the ImgLoader class looks like:
public class ImgLoader extends View {
public static Bitmap tree1;
public ImgLoader(Context context) {
super(context);
loadImgs();
}
public void loadImgs() {
System.gc(); // Manually Call GC
// TREES
tree1 = BitmapFactory.decodeResource(getResources(), R.drawable.tree);
tree1 = getResizedBitmap(tree1, MainActivity.height / 2,
MainActivity.width / 10);
}
public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// CREATE A MATRIX FOR THE MANIPULATION
Matrix matrix = new Matrix();
// RESIZE THE BIT MAP
matrix.postScale(scaleWidth, scaleHeight);
// "RECREATE" THE NEW BITMAP
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height,
matrix, false);
return resizedBitmap;
}
public static Bitmap RotateBitmap(Bitmap source, float angle) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(),
source.getHeight(), matrix, true);
}
}
Note, i only use one Bitmap for the question. Essentially, when i want to use an image in my application, i say:
object.image = ImgLoader.tree1;
What is an alternative to doing this, that is not so memory intensive?
Thank you!
This seems like very bad practice to me, especially because you are loading multiple Bitmaps and keep static references of them.
Since even small Bitmaps consume considerably large amounts of memory, you will run into OutOfMemoryErrors very soon.
A Bitmap 512x512 already consumes 1 Megabyte of RAM (in 32 Bit) color. You can load your Bitmaps in RGB_565 (16 Bit) to reduce memory consuption.
Why not just load the Bitmap from the Resources when you need it?
try getting them in InputStream format ... they will be in byte .. but personnaly,( and its really personnally ) i prefer to load every image when i need it instead of leaving them in a memory .. for future use , the number of images can increase , plus , as u mentionned and i noticed in your code , the images are store locally ( not doenloaded ) so it shldnt b a big problem to reload them .. but again its my own opinion
Don't think it has anything to do with the gc; you are loading images when you manually call the gc.
I believe it's the loading of images that is causing your app to lag. You could load the bitmap asynchronously.
I'm working on a simple 2D game, rendering via the Java2D API. I've noticed that when I try to draw on integrated graphics card the performance crashes.
I've tested this game on both my main rig with a newer ATI Radeon and my 5 year old laptop which also has an (incredibly antiquated) Radeon. On both I get good FPS, but when I try to use my Intel i5's onboard HD 4000 graphics, it crawls at around 20 FPS.
I'm using Full Screen Exclusive mode.
At any given moment, I am rendering approximately 1000 images at once.
Annoyingly, when I try to getAvailableAcceleratedMemory() it just returns -1 for this card, and it seems to refuse to accelerate any images.
Does anyone have any ideas how to fix this issue?
Rendering code:
Graphics g = bufferStrategy.getDrawGraphics();
g.drawImage(img, x, y, img.getWidth(), img.getHeight(), null)
g.dispose();
bufferStrategy.show();
Image Loading code:
BufferedImage I = null;
I = ImageIO.read(new File(currentFolder+imgPath));
imgMap.put(imgIdentifier, I);
The images are stored in a hashmap of BufferedImages identified by strings, so when an entity needs to draw and image it just gets it out of the hashmap and draws it. In the current case, the entities are mostly floor and wall tiles, so they never change (and thus don't have to get the image from the hashmap other than the very first time).
EDIT - I've incorporated MadProgrammer's method, but it didn't change my FPS.
This is an example of converting an image to a compatiable image...not an answer in of itself
This is some of the library code that I use...
public static BufferedImage createCompatibleImage(BufferedImage image) {
BufferedImage target = createCompatibleImage(image, image.getWidth(), image.getHeight());
Graphics2D g2d = target.createGraphics();
g2d.drawImage(image, 0, 0, null);
g2d.dispose();
return target;
}
public static BufferedImage createCompatibleImage(BufferedImage image,
int width, int height) {
return getGraphicsConfiguration().createCompatibleImage(width, height, image.getTransparency());
}
public static GraphicsConfiguration getGraphicsConfiguration() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}
I would do something like...
I = createCompatibleImage(ImageIO.read(new File(currentFolder+imgPath)));
imgMap.put(imgIdentifier, I);
The following code defines my Bitmap:
Resources res = context.getResources();
mBackground = BitmapFactory.decodeResource(res, R.drawable.background);
// scale bitmap
int h = 800; // height in pixels
int w = 480; // width in pixels
// Make sure w and h are in the correct order
Bitmap scaled = Bitmap.createScaledBitmap(mBackground, w, h, true);
... And the following code is used to execute/draw it (the unscaled Bitmap):
canvas.drawBitmap(mBackground, 0, 0, null);
My question is, how might I set it to draw the scaled Bitmap returned in the form of Bitmap scaled, and not the original?
Define a new class member variable:
Bitmap mScaledBackground;
Then, assign your newly created scaled bitmap to it:
mScaledBackground = scaled;
Then, call in your draw method:
canvas.drawBitmap(mScaledBackground, 0, 0, null);
Note that it is not a good idea to hard-code screen size in the way you did in your snippet above. Better would be to fetch your device screen size in the following way:
int width = getWindowManager().getDefaultDisplay().getWidth();
int height = getWindowManager().getDefaultDisplay().getHeight();
And it would be probably better not to declare a new bitmap for the only purpose of drawing your original background in a scaled way. Bitmaps consume a lot of precious resources, and usually a phone is limited to a few MB of Bitmaps you can load before your app ungracefully fails. Instead you could do something like this:
Rect src = new Rect(0, 0, bitmap.getWidth() - 1, bitmap.getHeight() - 1);
Rect dest = new Rect(0, 0, width - 1, height - 1);
canvas.drawBitmap(mBackground, src, dest, null);
To draw the scaled bitmap you want save your scaled bitmap in a field somewhere (here called mScaled) and call:
canvas.drawBitmap(mScaled, 0, 0, null);
in your draw method (or wherever you call it right now).
I am looking to take a view hierarchy and turn it into a Bitmap. Using the view.getDrawingCache() does exactly what I want it to do but will not work if I try that before the view is drawn. Is there another function that will do the same thing but before it is actually drawn? I would like to create the view and turn it into a bitmap to display and discard the actual view. If there is a way to force the view to be drawn that might work as well. Any ideas?
I found this in a previous post:
public static Bitmap loadBitmapFromView(View v) {
Bitmap b = Bitmap.createBitmap( v.getLayoutParams().width, v.getLayoutParams().height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
v.layout(0, 0, v.getLayoutParams().width, v.getLayoutParams().height);
v.draw(c);
return b;
}
This looks promising but when I use it all I get is a semi transparent black box.
Alright I figured it out. You can use this code, slightly modified from this post.
public static Bitmap loadBitmapFromView(View v) {
Bitmap b = Bitmap.createBitmap( v.getLayoutParams().width, v.getLayoutParams().height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
v.measure(v.getLayoutParams().width, v.getLayoutParams().height); //Change from original post
v.layout(0, 0, v.getLayoutParams().width, v.getLayoutParams().height);
v.draw(c);
return b;
}