4

In Photoshop there are a couple of blending modes, such as:

  • Color Burn
  • Multiply
  • Darken
  • Lighten
  • Overlay

(more information here: http://help.adobe.com/en_US/photoshop/cs/using/WSfd1234e1c4b69f30ea53e41001031ab64-77eba.html)

For example, for mode Color Burn "looks at the color information in each channel and darkens the base color to reflect the blend color by increasing the contrast between the two. Blending with white produces no change".

This is my code:

Bitmap blendBitmap = BitmapFactory.decodeStream(ctx.getAssets().open(filename));
Canvas canvas = new Canvas(srcBitmap);
canvas.drawBitmap(blendBitmap, 0, 0, null); // ?
p.recycle();
p = null;

Is it possible to apply, for example, Color Burn blending mode instead of simply drawing image above other image (like in this small code)?

meh
  • 22,090
  • 8
  • 48
  • 58
alwx
  • 205
  • 1
  • 3
  • 14

1 Answers1

3

It was not difficult.

I used NDK (because of performance) to manipulate with pixels. This information of blending modes was very useful: How does photoshop blend two images together?

My final solution is:

#define ChannelBlend_ColorBurn(A, B)  ((uint8_t) ((B == 0) ? B : max(0, (255 - ((255 -  
#define ChannelBlend_Alpha(A, B, O)    ((uint8_t) (O * A + (1 - O) * B))
#define ChannelBlend_AlphaF(A, B, F, O) (ChannelBlend_Alpha(F(A, B), A, O))

typedef struct {
    uint8_t red;
    uint8_t green;
    uint8_t blue;
    uint8_t alpha;
} rgba;

// Blend
JNIEXPORT void 
JNICALL Java_com_package_Filter_jniBlend(JNIEnv* env, jobject obj, jobject bitmapA,
    jobject bitmapB, jobject bitmapOut, jint mode) {

    // Properties
    AndroidBitmapInfo   infoA;
    void*               pixelsA;
    AndroidBitmapInfo   infoB;
    void*               pixelsB;
    AndroidBitmapInfo   infoOut;
    void*               pixelsOut;

    int ret;

    // Get image info
    if ((ret = AndroidBitmap_getInfo(env, bitmapA, &infoA)) < 0 ||
        (ret = AndroidBitmap_getInfo(env, bitmapB, &infoB)) < 0 ||
        (ret = AndroidBitmap_getInfo(env, bitmapOut, &infoOut)) < 0) {
        return;
    }
    // Check image
    if (infoA.format != ANDROID_BITMAP_FORMAT_RGBA_8888 ||
        infoB.format != ANDROID_BITMAP_FORMAT_RGBA_8888 ||
        infoOut.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
        return;
    }
    // Lock all images
    if ((ret = AndroidBitmap_lockPixels(env, bitmapA, &pixelsA)) < 0 ||
        (ret = AndroidBitmap_lockPixels(env, bitmapB, &pixelsB)) < 0 ||
        (ret = AndroidBitmap_lockPixels(env, bitmapOut, &pixelsOut)) < 0) {
        LOGE("Error! %d", ret);
    }

    int h = infoA.height;
    int w = infoA.width;
    int wh = w * h;

    int n;
    rgba* inputA = (rgba*) pixelsA;
    rgba* inputB = (rgba*) pixelsB;
    rgba* output = (rgba*) pixelsOut;

    rgba pA, pB;

    int x, y;
    for (y = 0; y < h; y++) {
        for (x = 0; x < w; x++) {
            n = y * w + x;

            pA = inputA[n];
            pB = inputB[n];

            float alpha = (float) pB.alpha / 255.0;

            output[n].red = ChannelBlend_AlphaF(pA.red, pB.red, ChannelBlend_ColorBurn, alpha);
            output[n].green = ChannelBlend_AlphaF(pA.green, pB.green, ChannelBlend_ColorBurn, alpha);
            output[n].blue = ChannelBlend_AlphaF(pA.blue, pB.blue, ChannelBlend_ColorBurn, alpha);
        }
    }

    // Unlocks everything
    AndroidBitmap_unlockPixels(env, bitmapA);
    AndroidBitmap_unlockPixels(env, bitmapB);
    AndroidBitmap_unlockPixels(env, bitmapOut);
}

Small remark to improve performance: After me make equation for single pixel it will be good to store value in some kind of cache to access it faster without equation next time.

Community
  • 1
  • 1
alwx
  • 205
  • 1
  • 3
  • 14
  • 1
    It worked! Sure, there are some typos like the declaration of ChannelBlend_ColorBurn, but still, thank you SO MUCH! You practically saved my app! I can finally ditch ImageMagick, thank you! – Paulo Cesar May 15 '13 at 19:07