Search code examples
androidimage-processingporter-duff

Implementing different PorterDuff modes in android


I am working on a picture editing tool in which I need to merge two images. Most of the image editing tools like gimp use PorterDuff modes for merging or blending images. I am also using the same approach in android.
As android provides limited number of PorterDuff modes, I am not able to achieve the desired result. So, I am thinking of implementing PorterDuff modes(Overlapping, Hard-Light, Soft-Light, Color-burn, Color-dodge) which are not included in android.
The problem is I don't know where to start. So, any reference or guidance in this regard will be highly appreciated.


Solution

  • This is how you can implement the Overlay PorterDuff mode in android:

    public class MyPorterDuffMode
    {
    
        public Bitmap applyOverlayMode(Bitmap srcBmp, Bitmap destBmp)
        {
            int width = srcBmp.getWidth();
            int height = srcBmp.getHeight();
            int srcPixels[] = new int[width * height];;
            int destPixels[] = new int[width * height];
            int resultPixels[] = new int[width * height];
            int aS = 0, rS = 0, gS = 0, bS = 0;
            int rgbS = 0;
            int aD = 0, rD = 0, gD = 0, bD = 0;
            int rgbD = 0;
    
            try
            {
                srcBmp.getPixels(srcPixels, 0, width, 0, 0, width, height);
                destBmp.getPixels(destPixels, 0, width, 0, 0, width, height);
                srcBmp.recycle();
                destBmp.recycle();
            }
            catch(IllegalArgumentException e)
            {
            }
            catch(ArrayIndexOutOfBoundsException e)
            {
            }
    
            for(int y = 0; y < height; y++)
            {
                for(int x = 0; x < width; x++)
                {
                    rgbS = srcPixels[y*width + x];
                    aS = (rgbS >> 24) & 0xff;
                    rS = (rgbS >> 16) & 0xff;
                    gS = (rgbS >>  8) & 0xff;
                    bS = (rgbS      ) & 0xff;
    
                    rgbD = destPixels[y*width + x];
                    aD = ((rgbD >> 24) & 0xff);
                    rD = (rgbD >> 16) & 0xff;
                    gD = (rgbD >>  8) & 0xff;
                    bD = (rgbD      )  & 0xff;
    
                    //overlay-mode
                    rS = overlay_byte(rD, rS, aS, aD);
                    gS = overlay_byte(gD, gS, aS, aD);
                    bS = overlay_byte(bD, bS, aS, aD);
                    aS = aS + aD - Math.round((aS * aD)/255f);
    
                    resultPixels[y*width + x] = ((int)aS << 24) | ((int)rS << 16) | ((int)gS << 8) | (int)bS;
                }
            }
    
            return Bitmap.createBitmap(resultPixels, width, height, srcBmp.getConfig());
        }
    
    
    
        // kOverlay_Mode
        int overlay_byte(int sc, int dc, int sa, int da) {
            int tmp = sc * (255 - da) + dc * (255 - sa);
            int rc;
            if (2 * dc <= da) {
                rc = 2 * sc * dc;
            } else {
                rc = sa * da - 2 * (da - dc) * (sa - sc);
            }
            return clamp_div255round(rc + tmp);
        }
    
    
        int clamp_div255round(int prod) {
            if (prod <= 0) {
                return 0;
            } else if (prod >= 255*255) {
                return 255;
            } else {
                return Math.round((float)prod/255);
            }
        }
    
    }
    

    Note: While extracting the bitmaps set the configuration to be ARGB_8888 and this is important.
    Any time you want to do image blending or color manipulation, you need to make sure you are in ARGB_8888 mode, not in RGB_565 mode. Up until 2.3, android will usully default to RGB_565 mode unless you explicitly tell it to do otherwise in order to save memory.

    BitmapFactory.Options opt = new BitmapFactory.Options();
    opt.inPreferredConfig = Config.ARGB_8888;