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.
Related
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
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.
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.)
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;
How do I properly use the RenderScript Intrinsics.
as shown http://android-developers.blogspot.com/2013/08/renderscript-intrinsics.html
//Assuming my original Bitmap is "bm"
Bitmap outputBitmap = Bitmap.createBitmap(bm.getHeight(),
bm.getWidth(), Config.ARGB_8888);
RenderScript rs = RenderScript.create(getApplicationContext());
ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur
.create(rs, Element.U8_4(rs));
Allocation tmpIn = Allocation.createFromBitmap(rs, bm);
Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
theIntrinsic.setRadius(25.f);
theIntrinsic.setInput(tmpIn);
theIntrinsic.forEach(tmpOut);
tmpOut.copyTo(outputBitmap);
bm.recycle();
rs.destroy
I get some black areas of the outputBitmap on initial layout, but which get filled in after the user scrolls the Scroller of the HorizontalScrollView, making the drawable "refresh" itself.
I get this error too (if it helps):
09-01 05:54:11.246: E/RenderScript(11423): rsAssert failed: !mElements.size(), in frameworks/rs/rsElement.cpp at 375
any suggestions regarding proper use of the RS will help.
I think the problem is that you switched the order of the height and width arguments. It should be:
Bitmap outputBitmap = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Config.ARGB_8888);
I'm guessing you've got some issue with the UI parts rather than the RS parts. The RS parts look fine; maybe try a outputBitmap.prepareToDraw() after the RS bits have finished?
Note that in general it's not a great idea to create and destroy RS contexts in the critical path like that. There's potentially a nontrivial startup/teardown cost depending on the hardware resources that have to be allocated, so it would be much better to allocate it at startup and use it for the lifetime of the application.