Renderscript allocation from bitmap - android

I'm trying to get into render script and was confused regarding allocations usage. Almost all examples show next algorithm:
Create In and Out Bitmap
Create In and Out Allocation from Bitmaps In and Out correspondingly
Configure script and perform forEach method
copy result from Out allocation into bitmap using copyTo method
Something like that:
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lena);
Bitmap dstBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());
Allocation allocationIn = Allocation.createFromBitmap(renderScript, srcBitmap);
Allocation allocationOut = Allocation.createFromBitmap(renderScript, dstBitmap);
scriptColorMatrix.setGreyscale();
scriptColorMatrix.forEach(allocationIn, allocationOut);
//no difference after removing this line
allocationOut.copyTo(dstBitmap);
imagePreview.setImageBitmap(dstBitmap);
This works, but it also works even if I omit step 4 by removing:
allocationOut.copyTo(dstBitmap);
Lets go further and lower brightness after grayscale:
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lena);
Bitmap dstBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());
Allocation allocationIn = Allocation.createFromBitmap(renderScript, srcBitmap);
Allocation allocationOut = Allocation.createFromBitmap(renderScript, dstBitmap);
scriptColorMatrix.setGreyscale();
scriptColorMatrix.forEach(allocationIn, allocationOut);
//reset color matrix
scriptColorMatrix.setColorMatrix(new Matrix4f());
//adjust brightness
scriptColorMatrix.setAdd(-0.5f, -0.5f, -0.5f, 0f);
//Performing forEach vise versa (from out to in)
scriptColorMatrix.forEach(allocationOut, allocationIn);
imagePreview.setImageBitmap(srcBitmap);
Shortly describing the code above, we performed grayscale collor matrix from In allocation into Out one, and brightness adjustment in backward direction. I never called copyTo method, but at the end I've got result in srcBitmap and it was correct.
That's not the end. Lets go deeper. I'll leave only one bitmap and one Allocation:
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lena);
Allocation allocationIn = Allocation.createFromBitmap(renderScript, srcBitmap);
scriptColorMatrix.setGreyscale();
scriptColorMatrix.forEach(allocationIn, allocationIn);
//reset color matrix
scriptColorMatrix.setColorMatrix(new Matrix4f());
//adjust brightness
scriptColorMatrix.setAdd(-0.5f, -0.5f, -0.5f, 0f);
scriptColorMatrix.forEach(allocationIn, allocationIn);
imagePreview.setImageBitmap(srcBitmap);
The result is the same...
Can anybody explain why does it happen and where to use copyTo and where I can use targeting Bitmap without it?

The Allocation objects are needed in order to provide the proper mapping of Bitmap to something Renderscript understands. If you are targeting API 18 or higher, the Allocation.createFromBitmap() methods you are using are automatically giving in the flag USAGE_SHARED, which tries to have the Allocation use the same backing memory as the Bitmap object. So the two are linked but technically the copyTo() method is still needed as the RS implementation may need to synchronize it back. On some platforms this may have already happened where others may cause this to pause as DMA or other mechanisms are updating the Bitmap's backing memory with whatever changes were made by the RS code.
As for why you can reverse the in/out Allocation order when calling scripts - it's valid and up to you to get the arguments and order correct. To RS they are just objects which point to some type of backing data to be manipulated. Since both were created with the Allocation.createFromBitmap() call, they can be used as either input or output as long as the Bitmap objects are mutable.
Similarly, using the same Allocation for the input and output is not normal, but also not invalid. It just means your input is changing on the fly. As long as your script is not accessing other Elements in the data when the root function is called for a specific Element, then it should work (as you are seeing.)

Related

RenderScript copy 3D allocation in rs file

RenderScript allocations can be 1D, 2D or 3D. Almost all methods support these 3, but the copy method is missing 3D. According to Android developers, below are all data access functions:
rsAllocationCopy1DRange Copy consecutive cells between allocations
rsAllocationCopy2DRange Copy a rectangular region of cells between allocations
rsAllocationVLoadX Get a vector from an allocation of scalars
rsAllocationVStoreX Store a vector into an allocation of scalars
rsGetElementAt Return a cell from an allocation
rsGetElementAtYuv_uchar_U Get the U component of an allocation of YUVs
rsGetElementAtYuv_uchar_V Get the V component of an allocation of YUVs
rsGetElementAtYuv_uchar_Y Get the Y component of an allocation of YUVs
rsSample Sample a value from a texture allocation
rsSetElementAt Set a cell of an allocation
As you can see, there is no rsAllocationCopy3DRange. I tried it anyway with this code:
rsAllocationCopy3DRange(output, 0, 0, 0, 0, 0, w, h, d, data, 0, 0, 0, 0, 0);
And the compile failed.
How to copy 3D allocations? I can write a kernel function just return the input and then rsForEach that function, but seems very inefficient for a simple copy.
Why is there no 3D copy? It doesn't make sense. Why is that Java/Kotlin side has 3D copy, but rs side doesn't?

Algorithm to floodfill bitmap using android Renderscript

I an trying to make an app which fill color to images. It is working fine using Java, but due to some performance issue I want to fill bitmaps using renderscript. I googled lots of things about renderscript but I haven't got anything suitable. Can you please guys guide me how to fill bitmaps using renderscript. Any help will be appreciated. Thanks
The basic thing you'll have to do is create an Allocation for the input Bitmap then a mutable Bitmap and Allocation for the output. Assuming you have an input bitmap called inputBitmap it could look something like this:
private RenderScript mRsCtx; // RenderScript context, created during init
private ScriptC_bitmapFill mFill; // RS kernel instance, created during init
.
.
.
public Bitmap doFill(Bitmap inputBitmap) {
// Ensure your input bitmap is also in ARGB8888
Bitmap output = Bitmap.createBitmap(inputBitmap.getWidth(),
inputBitmap.getHeight(),
Bitmap.Config.ARGB_8888);
Allocation outAlloc = Allocation.createFromBitmap(mRsCtx, output);
Allocation inAlloc = Allocation.createFromBitmap(mRsCtx, inputBitmap);
// Now call your kernel then copy back the results
mFill.forEach_root(inAlloc, outAlloc);
outAlloc.copyTo(outBitmap);
return outBitmap;
}
If you are just filling the entire image or even a region, you'll then have a RS kernel which will change the pixel value at specific locations when the kernel is called for it. Here's a very simple RS kernel which just fills the entire image with a solid color:
#pragma version(1)
#pragma rs java_package_name(com.example.bitmapfill)
void root(const uchar4 *v_in, uchar4 *v_out) {
v_out->r = 0x12;
v_out->g = 0x34;
v_out->b = 0x56;
}
Note that since you're not really doing anything with the input allocation/bitmap in this case (just filling the entire thing), you could just leave out the input allocation and use the dimensions. But, if you are only going to manipulate a portion of the input (a small subsection), then you'll have to copy the other pixels from input to output instead of filling.
For additional information about RS and some of its internals, performance, etc. you may find this talk useful: https://youtu.be/3ynA92x8WQo

Android out of memory error while rotate a bitmap

I am currently working on a project in which one I would like to rotate a bitmap.
The first time, I create my bitmap with the following code :
myBitmap = BitmapFactory.decodeResource(getResources(), drawableResource);
Then I rotate the bitmap using the following code :
final Matrix matrix = new Matrix();
matrix.postRotate(currentRotate);
myBitmap = Bitmap.createBitmap(myBitmap, 0, 0, directionBitmap.getWidth(), directionBitmap.getHeight(), matrix, true);
I works, but after several times, the memory increases and I have the following exception :
java.lang.OutOfMemoryError: Failed to allocate a 119071756 byte
allocation with 16775968 free bytes and 96MB until OOM
It seems that the old bitmaps are still in memory. How to delete/recycle them in order to save the memory ?
Thank your for your help.
I can suggest you to use Glide library from Github.
This library works in background threads. Anyway you can perform your rotation on Background like this:
runOnUiThread(new Runnable(final Matrix matrix = new Matrix();
matrix.postRotate(currentRotate);
myBitmap = Bitmap.createBitmap(myBitmap, 0, 0, directionBitmap.getWidth(), directionBitmap.getHeight(), matrix, true);
)
In my case, the problem was with the size of the bitmaps I was using. The bitmaps I was using were of very high pixels for the given device. In such case, Android system has to descale them to lower pixel density i.e. the one that will suit your device. When you rotate the bitmap, Android system takes up a lot of memory to descale it to lower pixels. Also, Android gets busy and in some cases results in UI thread blocking.
Also, increase the heap size in your manifest file.

Android Blur with RenderScript

I try to make a gaussian blur on an android Bitmap but I get this error:
rsAssert failed: !mTypes.size() and
rsAssert failed: !mElements.size()
Here is my code :
public Bitmap blurBitmap(Bitmap src) {
Bitmap outBitmap = src.copy(src.getConfig(), true);
final RenderScript rs = RenderScript.create(this);
final Allocation input = Allocation.createFromBitmap(rs, src);
final Allocation output = Allocation.createFromBitmap(rs, outBitmap);
final ScriptIntrinsicBlur script =
ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
script.setRadius(25f);
script.setInput(input);
script.forEach(output);
output.copyTo(outBitmap);
rs.destroy();
return outBitmap;
}
Note that I used android.support.v8.renderscript to ensure compatibility with android lower versions.
Someone would have an idea to fix it?
Thanks.
Martin
The Element arguments of ScriptIntrinsicBlur should be the same of Allocation's element, so you should use Allocation.getElement(), rather than direct Element.U8_4(rs).
final ScriptIntrinsicBlur script =
ScriptIntrinsicBlur.create(rs, input.getElement());
And you might also want move those final to class private member, since some of them might be different every time your bitmap is different.
And FYI, script.setRadius(25f) is way too high, that will cause slow calculation. If you need such heavy blur, you might consider to scale down the original bitmap at certain level, and blur it, then scale up to your canvas, which will be much faster than heavy blur for a huge image.
One more thing, if you don't care to keep the original bitmap, the allocation of input and output can be the same one, which might save some memory.
While there isn't a good way to increase blur radius, for very large images you can essentially fake it by scaling down (ex .5 original size), blurring, then scaling up to original size. Be careful not to scale too far down as the resulting bitmap will become pixelated.

OutOfMemoryError while create a bitmap

I want to send a fax from my app.
A fax document has a resolution of 1728 x 2444 pixels.
So I create a bitmap, add text and/or pictures and encode it to CCITT (Huffman):
Bitmap image = Bitmap.createBitmap(1728, 2444, Config.ALPHA_8);
Canvas canvas = new Canvas(image);
canvas.drawText("This is a fax", 100, 100, new Paint());
ByteBuffer buffer = ByteBuffer.allocateDirect(image.getWidth() * image.getHeight());
image.copyPixelsToBuffer(buffer);
image.recycle();
encodeCCITT(buffer, width, height);
This works perfect on my Galaxy SII (64 MB heap size), but not at emulator (24 MB). After creating the second fax page I get "4223232-byte external allocation too large for this process...java.lang.OutOfMemoryError" while allocating the buffer.
I already reduced color depth from ARGB_8888 (4 byte per pixel) to ALPHA_8 (1 byte), because fax pages are monochrome anyway.
I need this resolution and I need to have access to the pixels for encoding.
What is the best way?
Android doesn't support 1-Bpp bitmaps, and the Java heap size limit of 24/32/48MB is part of Android. Real devices can't allocate more than the Java heap limit no matter how much RAM they have. There appear to be only two possible solutions:
1) Work within the limitations of the Java heap.
2) Use native code (NDK).
In native code you can allocate the entire available RAM of the device. The only down side is that you will need to write your own code to edit and encode your bitmap.
In addition to BitBank's already good answer, you have to null the reference if you want the Garbage collector to actually clean up your Bitmap's references. The documentation for that method states:
This is an advanced call, and normally need not be called, since the
normal GC process will free up this memory when there are no more
references to this bitmap.
instead of copy all pixels to a ByteBuffer, you can copy step by step. Here with a int[] array. So, you need less memory:
int countLines = 100;
int[] pixels = new int[width * countLines];
for (int y = 0; y < heigth; y += countLines) {
image.getPixels(line, 0, width, 0, y, width, countLines);
// do something with pixels...
image.setPixels(line, 0, width, 0, y, width, countLines);
}

Categories

Resources