5

I am new to Android Renderscript. I need to write a convolution multiplication in RenderScript since the final application is going to run on Android. Data stream is going to be an image. More specifically, I am not able to write the core logic using forEach functionality, though I can do it in Java, but speed it too slow! Please help! Steve

user1324282
  • 51
  • 1
  • 3

1 Answers1

8

During the rsForEach call (or other Renderscript function), you can access the neighbouring pixels of the original image (or whatever type of data you are using) by binding the original image allocation to a pointer within the Renderscript where it can then be accessed as an array. Here is an example based upon the HelloCompute example:

#pragma version(1)
#pragma rs java_package_name(com.android.example.hellocompute)

rs_allocation gIn;
rs_allocation gOut;
rs_script gScript;

static int mImageWidth;
const uchar4 *gPixels;

const float4 kWhite = {
    1.0f, 1.0f, 1.0f, 1.0f
};
const float4 kBlack = {
    0.0f, 0.0f, 0.0f, 1.0f
};

void init() {
}

static const int kBlurWidth = 20;
static const float kMultiplier = 1.0f / (float)(kBlurWidth * 2 + 1);

void root(const uchar4 *v_in, uchar4 *v_out, const void *usrData, uint32_t x, uint32_t y) {
    float4 original = rsUnpackColor8888(*v_in);

    float4 colour = original * kMultiplier;

    int y_component = mImageWidth * y;

    for ( int i = -kBlurWidth; i < 0; i++) {
        float4 temp_colour;

        if ( (int)x + i >= 0) {
            temp_colour = rsUnpackColor8888(gPixels[x+i + y_component]);
        }
        else {
            temp_colour = kWhite;
        }

        colour += temp_colour * kMultiplier;
    }
    for ( int i = 1; i <= kBlurWidth; i++) {
        float4 temp_colour;

        if ( x + i < mImageWidth) {
            temp_colour = rsUnpackColor8888(gPixels[x+i + y_component]);
        }
        else {
            temp_colour = kWhite;
        }

        colour += temp_colour * kMultiplier;
    }

    colour.a = 1.0f;
    *v_out = rsPackColorTo8888(colour);
}


void filter() {
    mImageWidth = rsAllocationGetDimX(gIn);
    rsDebug("Image size is ", rsAllocationGetDimX(gIn), rsAllocationGetDimY(gOut));
    rsForEach(gScript, gIn, gOut, NULL);
}

Called from the following Java. Note the call to mScript.bind_gPixels(mInAllocation) which binds the original image data to the gPixel pointer in the Renderscript and, therefore, makes the image data available as an array.

    mRS = RenderScript.create(this);

    mInAllocation = Allocation.createFromBitmap(mRS, mBitmapIn,
                                                Allocation.MipmapControl.MIPMAP_NONE,
                                                Allocation.USAGE_SCRIPT);
    mOutAllocation = Allocation.createTyped(mRS, mInAllocation.getType());

    mScript = new ScriptC_blur(mRS, getResources(), R.raw.blur);

    mScript.bind_gPixels(mInAllocation);

    mScript.set_gIn(mInAllocation);
    mScript.set_gOut(mOutAllocation);
    mScript.set_gScript(mScript);
    mScript.invoke_filter();
    mOutAllocation.copyTo(mBitmapOut);
Massycat
  • 551
  • 3
  • 11
  • hi, thanks for your code and it works well; only problem is the root() is running per pixel in the bitmap, right? is there anyway I can make the root() running per row/column on the bitmap, so that I can take advantage of those fast blurring algorithm? – xandy May 13 '12 at 07:12
  • The rsForEach call (performing root()) will run on each element of the Allocation that you pass in. You should be able to restrict the range the rsForEach call operates over but I am having difficulty getting that to work (see [here](http://stackoverflow.com/q/10115616/1327767)). – Massycat May 16 '12 at 09:33
  • Though that is probably not what you want anyway. If you want to have root() called per image row, create an Allocation that is, something like, the indices of the row image data and call rsForEach on that. The data out argument inside root() cannot then be used to output the image data so you will have to pass in the output image Allocation in a similar way to passing in the input image, i.e. create a pointer inside the RenderScript and binding to it from the Java side. – Massycat May 16 '12 at 09:43
  • My full answer is over on your [related question](http://stackoverflow.com/a/10617141/1327767) – Massycat May 16 '12 at 12:04