Search code examples
androidtype-conversionrenderscript

Renderscript conversion from ARGB8888 to RGB565


I'm receiving Bitmaps in ARGB8888 format, but I need to process it through some algorithm that only accepts RGB565. I want to convert this bitmap to the new format using Renderscript but it seems the allocation in and allocation out should be of equal (or compatible). bitmapIn is of type ARGB_8888 and bitmapOut is of type RGB_565

Caused by: android.renderscript.RSIllegalArgumentException: Allocation kind is PIXEL_RGBA, type UNSIGNED_8 of 4 bytes, passed bitmap was RGB_565

Java:

public void convert(final Bitmap bitmapIn, Bitmap bitmapOut)
{
    mInAllocation = Allocation.createFromBitmap(mRS, bitmapIn, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
    Type.Builder tb = new Type.Builder(mRS, Element.RGB_565(mRS)).setX(bitmapIn.getWidth()).setY(bitmapOut.getWidth());
    mOutAllocation = Allocation.createTyped(mRS, tb.create());
    // Call custom method (not forEach_root) so we can have uchar4 in and uchar3 out
    mScript.forEach_convert(mInAllocation, mOutAllocation);
    mOutAllocation.copyTo(bitmapOut);
}

Renderscript:

// Convert to RGB565 by quantizing the individual channels
void convert(const uchar4* v_in, uchar3* v_out)
{
    v_out->x = v_in->x >> 3;
    v_out->y = v_in->y >> 2;
    v_out->z = v_in->z >> 3;
}

Note that if I make both bitmaps ARGB_8888 and have convert() working on both uchar4* (and just copy the alpha (w) channel, then I see the bitmap being altered.

I know that 565 equals 16 bits, so in reality its more likely a uchar2, but it also is incompatible with the allocation in type.

How can one do this type conversions in Renderscript?


Solution

  • I cannot find the correct Renderscript type to use with a RGB565 image but uint16_t works (at least on a Nexus S). As in Tim Murray's answer you have to bind either the input or output Allocation separately.

    Here is the Renderscript to do the conversion where the input RGB8888 Allocation is bound separately and the rsForEach is called on the RGB565 Allocation:

    #pragma version(1)
    #pragma rs java_package_name(uk.co.massycat.renderscriptfun.rsfun)
    
    rs_allocation gInImage;
    
    void root(const uint16_t *v_in, uint16_t *v_out, const void *usrData, uint32_t x, uint32_t y) {
        uint16_t colour = 0;
    
        const uchar4 *in_pixel = rsGetElementAt(gInImage, x, y);
    
        uint32_t red = in_pixel->r;
        uint32_t green = in_pixel->g;
        uint32_t blue = in_pixel->b;
    
        red >>= 3;
        green >>= 2;
        blue >>= 3;
    
        // red (5 bits)
        colour |= red << 11;
        // green (6 bits)
        colour |= green << 5;
        // blue (5 bits)
        colour |= blue << 0;
    #if 0
        // red (5 bits)
        colour |= 0x0 << 11;
        // green (6 bits)
        colour |= 0x0 << 5;
        // blue (5 bits)
        colour |= 0x1f << 0;
    #endif
    
        *v_out = colour;
    }
    
    void doWork( rs_script script, rs_allocation alloc_out) {
        rsForEach(script, alloc_out, alloc_out);
    }
    

    When calling rsForEach on the RGB565 Allocation, on the Java side, you have to call the invoke_doWork script object method instead of the forEach_root script object method as the forEach_root call will do type checking on the RGB565 Allocation and throw an exception saying it is not compatible with U16.

    Here is the Java side:

    mInImageAllocation = Allocation.createFromBitmap(mRS, mInBitmap,
                Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
    Type.Builder tb = new Type.Builder(mRS, Element.RGB_565(mRS)).setX(mInBitmap.getWidth()).setY(mInBitmap.getHeight());
    mOutAllocation = Allocation.createTyped(mRS, tb.create());
    
    mScript.set_gInImage(mInImageAllocation);
    
    mScript.invoke_doWork(mScript, mOutAllocation);
    Bitmap tmp_bitmap = Bitmap.createBitmap(mInBitmap.getWidth(), mInBitmap.getHeight(), Bitmap.Config.RGB_565);
    mOutAllocation.copyTo(tmp_bitmap);
    
    mAnImageView.setImageBitmap(tmp_bitmap);
    mAnImageView.invalidate();