Java Android Bitmap references - java

I am creating this Android project with Java. However, I am wondering a bit how far the references with some Bitmap methods will go.
I have an originally Bitmap just like:
Bitmap originalBitmap = BitmapFactory.decodeResource(resources, bitmapID);
and then I send it to an object within its constructor of a basic class:
class Test
{
Bitmap memberBitmap;
Test(Bitmap b)
{
memberBitmap = b;
}
}
This far, I know memberBitmap is still a reference to originalBitmap. But what I would like to do is basically resize this Bitmap using:
memberBitmap = Bitmap.createScaledBitmap(memberBitmap, newWidth, newHeight, filter);
Have I now stored a new Bitmap to the memory or have I changed the originalBitmap?
If it's so that I have created a new Bitmap, would there be any alteration to make it update the originalBitmap instead?
Like:
memberBitmap.createScaledBitmap(memberBitmap, newWidth, newHeight, filter);

By the sound of that API, it's probably creating a new bitmap in memory. You can check this by printing out the toString() on both objects after your constructor runs and see if their memory locations are the same.
For the second question: change the originalBitmap to reference to the new Bitmap.

It is actually creating a new Bitmap. In this case, it will create a new Bitmap and you lose the reference to the old one. If you did the code like so:
memberBitmap = b;
Bitmap scaledBitmap = Bitmap.createScaledBitmap(memberBitmap, newWidth, newHeight, filter);
You'll see that memberBitmap remains unchanged and scaledBitmap is a larger or smaller version. However, one exception is if there is no scaling to be done at all in which case it simply returns the reference to memberBitmap and you'll have two references to the same object.
One way you can remove work is to create the Bitmap smaller in the first place using the BitmapFactory.Options parameters. For example:
BitmapFactory.Options o = new BitmapFactory.Options();
o.inSampleSize = 2;
Bitmap originalBitmap = BitmapFactory.decodeResource(resources, bitmapID);
This will create a Bitmap that's half the size (width and height) of the original image. Unfortunately, this is very limiting and the value of inSampleSize must be in powers of 2.

Related

How to set an Image's alpha in android

so I'm in a bit of a pickle. I know how to set the alpha value of a bitmap in android. What I don't know how to do is make is reversible. So, let's say someone wanted to set the alpha of an image to 50%, so they do. Now lets say they wanted to set it 75% (keep in mind, this is of the original image alpha value). Currently, what I have is a function that will set the alpha value of the current image, so it would be 75% of the 50% alpha value if that makes sense. How can I make it so that it accounts for the original image?
public Pixmap setAlpha(float newAlpha) { //integer between 0-100
if (newAlpha != alpha) { //to check if the current alpha value of the image is equal to your desired alpha. to avoid always halving you alpha value
float test = newAlpha/100.0f;
float test2 = test * 255;
alpha = test2;
Bitmap newBM = Bitmap.createBitmap(backupImg.getBitmap().getWidth(),backupImg.getBitmap().getHeight(), Bitmap.Config.ARGB_8888);
Canvas cc = new Canvas(newBM);
cc.drawARGB(0,0,0,0);
Paint newPaint = new Paint();
newPaint.setAlpha((int)test2);
cc.drawBitmap(backupImg.getBitmap(), 0, 0, newPaint);
img.setBitmap(newBM);
return img;
} else {
return img;
}
}
The Pixmap part is just a custom Bitmap class. backupImg is just a copy of img, created in the constructor of the object this function belongs to.
please keep in mind that this will be a canvas based bitmap. If I recall correctly imageView's aren't drawn on the canvas? So, as a further example. Imagine a sprite drawn to the canvas that you want to alter the alpha of. So you do it using the function I've posted. Now, let's say you want to undo the changes and restore it to the sprite's original alpha, of some other value. Well, you can't because the alpha value of the image has been changed permanently. What I want to do is store reference to the original image with another variable, and refer to that whenever I need to adjust the alpha value of the image. Hopefully that makes sense
Why don't you set alpha to the ImageView instead of setting it to a bitmap.
By setting alpha to the ImageView or say any view you can reverse it easily.
Refer to this answer in order to do it.
You can do it from xml. Just add the below line in the imageview tag:-
android:alpha="0.5"
You can set alpha between 0 to 1. Above line will set alpha to half that is 0.5.
For bitmap:
Bitmap newBitmap = Bitmap.createBitmap(originalBitmap.getWidth(),
originalBitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);
Paint alphaPaint = new Paint();
alphaPaint.setAlpha(75);
canvas.drawBitmap(originalBitmap, 0, 0, alphaPaint);
I've solved the problem myself. So, there is nothing wrong with the function I wrote. The problem instead lies within how Java uses pointers. because everything is passed via reference, I was actually referencing the same object, rather than creating two separate objects. So instead of:
Bitmap oldBM = new Bitmap();
Bitmap newBM = oldBM;
you would instead want to do
Bitmap oldBM = new Bitmap();
Bitmap newBM = new Bitmap(using old bitmap's value);

Android OutOfMemoryError when trying to rotate an image

I want to display an image on screen (I have an activity that displays an image):
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
ExifInterface exif = new ExifInterface(imagePath);
int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int rotationInDegrees = exifToDegrees(rotation);
Matrix matrix = new Matrix();
matrix.preRotate(rotationInDegrees);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
The last line throws java.lang.OutOfMemoryError
I don't understand how to do this... I have to create the bitmap at first (BitmapFactory.decodeFile) but when I want to rotate it I have to provide the source bitmap already - how can I avoid it?
Thanks
You can try downsizing the image first before decoding it by adding inSampleSize in the bitmap options. as stated at the document: http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize
If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 5;
Bitmap bitmap = BitmapFactory.decodeFile(imagePath, options);
Hope that helps.
If you aren't doing any crazy things elsewhere then your image is probably just too big.
You just need to increase your heap. Add this to your manifest file:
android:largeHeap="true"
You cannot rotate the bitmap in place, because the dimensions can be different. I don't know, why it's impossible to load already rotated bitmap.
You can rotate the bitmap in the place of use. When drawing the bitmap, use the rotation matrix to have it drawn with rotation.
Other thing is that you probably should review your app and try to minimize the memory usage. Maybe the bitmaps you use are too big and you should use Options with inSampleSize set to 2 or more.

Loading Bitmaps and Storing them Statically Alternatives?

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.

Using DataBuffer of BufferedImage to set pixels

I am trying to use the underlying DataBufferByte of a BufferedImage of type TYPE_3BYTE_BGR to set pixel values as quick as possible.
Perhaps I am not understanding, but when I do the following...
byte[] imgBytes = ((DataBufferByte) img.getData().getDataBuffer()).getData();
... it seems as though I am getting a copy of the byte[] and not a reference. For example, if I run...
System.out.println(System.identityHashCode(imgBytes));
System.out.println(System.identityHashCode((DataBufferByte) img.getData().getDataBuffer()).getData());
... I get two clearly different object hashes. If I'm not mistaken, this indicates that I am not getting a reference to the underlying byte[] but rather a copy. If this is the case, how am I supposed to edit the DataBufferByte directly???
Or perhaps I am just setting the pixels wrong... When I set pixels in the imgBytes it doesn't seem to do anything to the BufferedImage. Once I get the byte[], I set each pixel value like so:
imgBytes[intOffset] = byteBlue;
imgBytes[intOffset+1] = byteGreen;
imgBytes[intOffset+2] = byteRed;
To me, this all seems fine. I can read pixels just fine this way so it seems I should be able to write them the same way!
I had the same problem. You may not use getData() but use getRaster() which gives you an array you can use to write to.
I once played around with pixel manipulations for Images in Java. Instead of directly answering your question I will offer an alternative solution to your problem. You can do the following to create an array of pixels to manipulate:
final int width = 800;
final int height = 600;
final int[] pixels = new int[width * height]; // 0xAARRGGBB
MemoryImageSource source = new MemoryImageSource(width, height, pixels, 0, width);
source.setAnimated(true);
source.setFullBufferUpdates(true);
Image image = Toolkit.getDefaultToolkit().createImage(source);
image.setAccelerationPriority(1f);
Then to draw the image, you can simply call the drawImage method from the Graphics class.
There are a few other ways to achieve what you are looking for, but this method was the simplest to me.
Here is how it's implemented in JDK7. You may have an error somewhere else if the stuff doesn't work for you.
public byte[] getData() {
theTrackable.setUntrackable();
return data;
}

Android Pass Bitmap to Native in 2.1 and lower

It's extremely easy to get the Bitmap data in the NDK when working with Android 2.2, but with 2.1 and lower, the AndroidBitmap_lockPixels function is not available. I've been searching for the past few hours, but nothing has worked.
How can I access the pixel data of a bitmap without using that function?
Create empty bitmap with dimensions of original image and ARGB_8888 format:
int width = src.getWidth();
int height = src.getHeight();
Bitmap dest = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Copy pixels from source bitmap to the int array:
int[] pixels = new int[width * height];
src.getPixels(pixels, 0, width, 0, 0, width, height);
And set these pixels to destination bitmap:
dest.setPixels(pixels, 0, width, 0, 0, width, height);
Create an IntBuffer in your Java code and pass the array down to your native library:
// this is called from native code
buffer = IntBuffer.allocate(width*height);
return buffer.array();
Use GetIntArrayElements to get an jint* and write to the array:
jint * arr = env->GetIntArrayElements((jintArray)bufferArray, NULL);
Write to the array and when finished, release:
env->ReleaseIntArrayElements((jintArray)bufferArray, arr, 0);
Notify the Java code that the array has been updated and use Canvas.drawBitmap() to draw the IntBuffer:
canvas.drawBitmap(buffer.array(), ....);
To draw to a Bitmap, initialize the canvas with the bitmap
... new Canvas(bitmap)
Someone else just asked the same question - I'll just link to it to avoid duplicating my answer:
Android rendering to live wallpapers
In any event, you probably don't want to copy the bitmap data every time you need to exchange it between Java and JNI code, so if your code is performance sensitive, this may be your only option on Android 2.1 and lower.

Categories

Resources