实现PorterDuff模式 - 如何处理alpha

时间:2014-11-18 14:58:07

标签: java android image-processing porter-duff

我需要在Android中使用位图混合。并使其类似于iOS版本的应用程序。

iOS家伙使用的是PorterDuff模式,这些模式目前在Android PorterDuff class开箱即用。

我确实需要柔光模式,但忘掉了 - 我决定处理一些缺乏Android模式的想法。

以下是我在研究期间发现的链接:

所有这些都包含了所需的公式,这样就可以很容易地编写一个方法并实现我可能需要的每种混合模式。

然而,那就是麻烦的开始。

上面链接中的公式只处理颜色组件,将alpha放在一边,好像它是理所当然的。但事实并非如此。

到目前为止,我已经拥有了这个SO question代码了。我正在考虑重新编写overlay_byte(int sc, int dc, int sa, int da)方法来实现softlight公式,但无法弄清楚如何处理sada变量。

P上。 S。:我知道混合模式的裸java实现的性能将远离原生的Android的PorterDuff类,但是可以稍后处理......

稍后添加:

从昨天开始,我已经做了一些更深入的研究,并找到了一种在java上实现软光混合模式的方法。这里是source(当我们使用像PorterDuff.Mode.LIGHTEN这样的开箱即用模式时,它在android内部工作。)

我从那里采用softlight_byte方法和他使用的所有方法并将其翻译成java(尽我所能)。结果有效,但是给出的图像不正确:底部部分似乎没问题,但最重要的部分会被重新烧掉。这是我的代码,重写为java:

public Bitmap applyBlendMode(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;

            rS = softlight_byte(rD, rS, aS, aD);
            gS = softlight_byte(gD, gS, aS, aD);
            bS = softlight_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());
}

int softlight_byte(int sc, int dc, int sa, int da) {

    int m = (da != 0) ? dc * 256 / da : 0;

    int rc;
    if (2 * sc <= sa) {
        rc = dc * (sa + ((2 * sc - sa) * (256 - m) >> 8));
    } else if (4 * dc <= da) {
        int tmp = (4 * m * (4 * m + 256) * (m - 256) >> 16) + 7 * m;
        rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
    } else {
        int tmp = sqrtUnitByte(m) - m;
        rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
    }
    return clampDiv255Round(rc + sc * (255 - da) + dc * (255 - sa));
}

/** returns 255 * sqrt(n/255) */
private int sqrtUnitByte(int n) {
    return skSqrtBits(n, 15 + 4);
}

/** Return the integer square root of value, with a bias of bitBias */
int skSqrtBits(int value, int bitBias) {

    if (value < 0) { Log.wtf(TAG, "ASSERTION!"); } // bitBias always 15+4, no check required
    int root = 0;
    int remHi = 0;
    int remLo = value;
    do {
        root <<= 1;
        remHi = (remHi<<2) | (remLo>>30);
        remLo <<= 2;
        int testDiv = (root << 1) + 1;
        if (remHi >= testDiv) {
            remHi -= testDiv;
            root++;
        }
    } while (--bitBias >= 0);
    return root;
}

int clampDiv255Round(int prod) {
    if (prod <= 0) {
        return 0;
    } else if (prod >= 255*255) {
        return 255;
    } else {
        return skDiv255Round(prod);
    }
}

/** Just the rounding step in SkDiv255Round: round(value / 255)
 */
private static int skDiv255Round(int prod) {
    prod += 128;
    return (prod + (prod >> 8)) >> 8;
}

有人可以检查可能的错误吗?这对我来说非常重要!

P上。 S:applyBlendMode取自上面提到的SO问题,softlight_byte(rD, rS, aS, aD);实现 - 用C ++重写。

0 个答案:

没有答案