Merge pull request #3592 from JakubValtar/bugfix-pimage-blend

Fix Java2D blending issues
This commit is contained in:
Ben Fry
2015-08-13 22:34:09 -04:00
+405 -259
View File
@@ -1862,8 +1862,8 @@ public class PImage implements PConstants, Cloneable {
loadPixels();
if (src == this) {
if (intersect(sx, sy, sx2, sy2, dx, dy, dx2, dy2)) {
blit_resize(get(sx, sy, sx2 - sx, sy2 - sy),
0, 0, sx2 - sx - 1, sy2 - sy - 1,
blit_resize(get(sx, sy, sw, sh),
0, 0, sw, sh,
pixels, pixelWidth, pixelHeight, dx, dy, dx2, dy2, mode);
} else {
// same as below, except skip the loadPixels() because it'd be redundant
@@ -1967,8 +1967,8 @@ public class PImage implements PConstants, Cloneable {
destY1 = 0;
}
destW = low(destW, screenW - destX1);
destH = low(destH, screenH - destY1);
destW = min(destW, screenW - destX1);
destH = min(destH, screenH - destY1);
int destOffset = destY1 * screenW + destX1;
srcBuffer = img.pixels;
@@ -2416,21 +2416,21 @@ public class PImage implements PConstants, Cloneable {
private void filter_new_scanline() {
sX = srcXOffset;
fracV = srcYOffset & PREC_MAXVAL;
ifV = PREC_MAXVAL - fracV;
ifV = PREC_MAXVAL - fracV + 1;
v1 = (srcYOffset >> PRECISIONB) * iw;
v2 = low((srcYOffset >> PRECISIONB) + 1, ih1) * iw;
v2 = min((srcYOffset >> PRECISIONB) + 1, ih1) * iw;
}
private int filter_bilinear() {
fracU = sX & PREC_MAXVAL;
ifU = PREC_MAXVAL - fracU;
ifU = PREC_MAXVAL - fracU + 1;
ul = (ifU * ifV) >> PRECISIONB;
ll = (ifU * fracV) >> PRECISIONB;
ur = (fracU * ifV) >> PRECISIONB;
lr = (fracU * fracV) >> PRECISIONB;
ll = ifU - ul;
ur = ifV - ul;
lr = PREC_MAXVAL + 1 - ul - ll - ur;
u1 = (sX >> PRECISIONB);
u2 = low(u1 + 1, iw1);
u2 = min(u1 + 1, iw1);
// get color values of the 4 neighbouring texels
cUL = srcBuffer[v1 + u1];
@@ -2464,333 +2464,479 @@ public class PImage implements PConstants, Cloneable {
// internal blending methods
private static int low(int a, int b) {
private static int min(int a, int b) {
return (a < b) ? a : b;
}
private static int high(int a, int b) {
private static int max(int a, int b) {
return (a > b) ? a : b;
}
// davbol - added peg helper, equiv to constrain(n,0,255)
private static int peg(int n) {
return (n < 0) ? 0 : ((n > 255) ? 255 : n);
}
private static int mix(int a, int b, int f) {
return a + (((b - a) * f) >> 8);
}
/////////////////////////////////////////////////////////////
// BLEND MODE IMPLEMENTIONS
// BLEND MODE IMPLEMENTATIONS
/*
* Jakub Valtar
*
* All modes use SRC alpha to interpolate between DST and the result of
* the operation:
*
* R = (1 - SRC_ALPHA) * DST + SRC_ALPHA * <RESULT OF THE OPERATION>
*
* Comments above each mode only specify the formula of its operation.
*
* These implementations treat alpha 127 (=255/2) as a perfect 50 % mix.
*
* One alpha value between 126 and 127 is intentionally left out,
* so the step 126 -> 127 is twice as big compared to other steps.
* This is because our colors are in 0..255 range, but we divide
* by right shifting 8 places (=256) which is much faster than
* (correct) float division by 255.0f. The missing value was placed
* between 126 and 127, because limits of the range (near 0 and 255) and
* the middle value (127) have to blend correctly.
*
* Below you will often see RED and BLUE channels (RB) manipulated together
* and GREEN channel (GN) manipulated separately. It is sometimes possible
* because the operation won't use more than 16 bits, so we process the RED
* channel in the upper 16 bits and BLUE channel in the lower 16 bits. This
* decreases the number of operations per pixel and thus makes things faster.
*
* Some of the modes are hand tweaked (various +1s etc.) to be more accurate
* and to produce correct values in extremes. Below is a sketch you can use
* to check any blending function for
*
* 1) Discrepancies between color channels:
* - highlighted by the offending color
* 2) Behavior at extremes (set colorCount to 256):
* - values of all corners are printed to the console
* 3) Rounding errors:
* - set colorCount to lower value to better see color bands
*
private static int blend_blend(int a, int b) {
int f = (b & ALPHA_MASK) >>> 24;
// use powers of 2 in range 2..256
// to better see color bands
final int colorCount = 256;
return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
mix(a & RED_MASK, b & RED_MASK, f) & RED_MASK |
mix(a & GREEN_MASK, b & GREEN_MASK, f) & GREEN_MASK |
mix(a & BLUE_MASK, b & BLUE_MASK, f));
final int blockSize = 3;
void settings() {
size(blockSize * 256, blockSize * 256);
}
void setup() { }
void draw() {
noStroke();
colorMode(RGB, colorCount-1);
int alpha = (mouseX / blockSize) << 24;
int r, g, b, r2, g2, b2 = 0;
for (int x = 0; x <= 0xFF; x++) {
for (int y = 0; y <= 0xFF; y++) {
int dst = (x << 16) | (x << 8) | x;
int src = (y << 16) | (y << 8) | y | alpha;
int result = testFunction(dst, src);
r = r2 = (result >> 16 & 0xFF);
g = g2 = (result >> 8 & 0xFF);
b = b2 = (result >> 0 & 0xFF);
if (r != g && r != b) r2 = (128 + r2) % 255;
if (g != r && g != b) g2 = (128 + g2) % 255;
if (b != r && b != g) b2 = (128 + b2) % 255;
fill(r2 % colorCount, g2 % colorCount, b2 % colorCount);
rect(x * blockSize, y * blockSize, blockSize, blockSize);
}
}
println(
"alpha:", mouseX/blockSize,
"TL:", hex(get(0, 0)),
"TR:", hex(get(width-1, 0)),
"BR:", hex(get(width-1, height-1)),
"BL:", hex(get(0, height-1)));
}
int testFunction(int dst, int src) {
// your function here
return dst;
}
*
*
*/
private static final int RB_MASK = 0x00FF00FF;
private static final int GN_MASK = 0x0000FF00;
/**
* Blend
* O = S
*/
private static int blend_blend(int dst, int src) {
int a = src >>> 24;
int s_a = a + (a >= 0x7F ? 1 : 0);
int d_a = 0x100 - s_a;
return min((dst >>> 24) + a, 0xFF) << 24 |
((dst & RB_MASK) * d_a + (src & RB_MASK) * s_a) >>> 8 & RB_MASK |
((dst & GN_MASK) * d_a + (src & GN_MASK) * s_a) >>> 8 & GN_MASK;
}
/**
* additive blend with clipping
* Add
* O = MIN(D + S, 1)
*/
private static int blend_add_pin(int a, int b) {
int f = (b & ALPHA_MASK) >>> 24;
private static int blend_add_pin(int dst, int src) {
int a = src >>> 24;
return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
low(((a & RED_MASK) +
((b & RED_MASK) >> 8) * f), RED_MASK) & RED_MASK |
low(((a & GREEN_MASK) +
((b & GREEN_MASK) >> 8) * f), GREEN_MASK) & GREEN_MASK |
low((a & BLUE_MASK) +
(((b & BLUE_MASK) * f) >> 8), BLUE_MASK));
int s_a = a + (a >= 0x7F ? 1 : 0);
int rb = (dst & RB_MASK) + ((src & RB_MASK) * s_a >>> 8 & RB_MASK);
int gn = (dst & GN_MASK) + ((src & GN_MASK) * s_a >>> 8);
return min((dst >>> 24) + a, 0xFF) << 24 |
min(rb & 0xFFFF0000, RED_MASK) |
min(gn & 0x00FFFF00, GREEN_MASK) |
min(rb & 0x0000FFFF, BLUE_MASK);
}
/**
* subtractive blend with clipping
* Subtract
* O = MAX(0, D - S)
*/
private static int blend_sub_pin(int a, int b) {
int f = (b & ALPHA_MASK) >>> 24;
private static int blend_sub_pin(int dst, int src) {
int a = src >>> 24;
return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
high(((a & RED_MASK) - ((b & RED_MASK) >> 8) * f),
GREEN_MASK) & RED_MASK |
high(((a & GREEN_MASK) - ((b & GREEN_MASK) >> 8) * f),
BLUE_MASK) & GREEN_MASK |
high((a & BLUE_MASK) - (((b & BLUE_MASK) * f) >> 8), 0));
int s_a = a + (a >= 0x7F ? 1 : 0);
int rb = ((src & RB_MASK) * s_a >>> 8);
int gn = ((src & GREEN_MASK) * s_a >>> 8);
return min((dst >>> 24) + a, 0xFF) << 24 |
max((dst & RED_MASK) - (rb & RED_MASK), 0) |
max((dst & GREEN_MASK) - (gn & GREEN_MASK), 0) |
max((dst & BLUE_MASK) - (rb & BLUE_MASK), 0);
}
/**
* only returns the blended lightest colour
* Lightest
* O = MAX(D, S)
*/
private static int blend_lightest(int a, int b) {
int f = (b & ALPHA_MASK) >>> 24;
private static int blend_lightest(int dst, int src) {
int a = src >>> 24;
return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
high(a & RED_MASK, ((b & RED_MASK) >> 8) * f) & RED_MASK |
high(a & GREEN_MASK, ((b & GREEN_MASK) >> 8) * f) & GREEN_MASK |
high(a & BLUE_MASK, ((b & BLUE_MASK) * f) >> 8));
int s_a = a + (a >= 0x7F ? 1 : 0);
int d_a = 0x100 - s_a;
int rb = max(src & RED_MASK, dst & RED_MASK) |
max(src & BLUE_MASK, dst & BLUE_MASK);
int gn = max(src & GREEN_MASK, dst & GREEN_MASK);
return min((dst >>> 24) + a, 0xFF) << 24 |
((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
((dst & GN_MASK) * d_a + gn * s_a) >>> 8 & GN_MASK;
}
/**
* only returns the blended darkest colour
* Darkest
* O = MIN(D, S)
*/
private static int blend_darkest(int a, int b) {
int f = (b & ALPHA_MASK) >>> 24;
private static int blend_darkest(int dst, int src) {
int a = src >>> 24;
return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
mix(a & RED_MASK,
low(a & RED_MASK,
((b & RED_MASK) >> 8) * f), f) & RED_MASK |
mix(a & GREEN_MASK,
low(a & GREEN_MASK,
((b & GREEN_MASK) >> 8) * f), f) & GREEN_MASK |
mix(a & BLUE_MASK,
low(a & BLUE_MASK,
((b & BLUE_MASK) * f) >> 8), f));
int s_a = a + (a >= 0x7F ? 1 : 0);
int d_a = 0x100 - s_a;
int rb = min(src & RED_MASK, dst & RED_MASK) |
min(src & BLUE_MASK, dst & BLUE_MASK);
int gn = min(src & GREEN_MASK, dst & GREEN_MASK);
return min((dst >>> 24) + a, 0xFF) << 24 |
((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
((dst & GN_MASK) * d_a + gn * s_a) >>> 8 & GN_MASK;
}
/**
* returns the absolute value of the difference of the input colors
* C = |A - B|
* Difference
* O = ABS(D - S)
*/
private static int blend_difference(int a, int b) {
// setup (this portion will always be the same)
int f = (b & ALPHA_MASK) >>> 24;
int ar = (a & RED_MASK) >> 16;
int ag = (a & GREEN_MASK) >> 8;
int ab = (a & BLUE_MASK);
int br = (b & RED_MASK) >> 16;
int bg = (b & GREEN_MASK) >> 8;
int bb = (b & BLUE_MASK);
// formula:
int cr = (ar > br) ? (ar-br) : (br-ar);
int cg = (ag > bg) ? (ag-bg) : (bg-ag);
int cb = (ab > bb) ? (ab-bb) : (bb-ab);
// alpha blend (this portion will always be the same)
return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
(peg(ar + (((cr - ar) * f) >> 8)) << 16) |
(peg(ag + (((cg - ag) * f) >> 8)) << 8) |
(peg(ab + (((cb - ab) * f) >> 8)) ) );
private static int blend_difference(int dst, int src) {
int a = src >>> 24;
int s_a = a + (a >= 0x7F ? 1 : 0);
int d_a = 0x100 - s_a;
int r = (dst & RED_MASK) - (src & RED_MASK);
int b = (dst & BLUE_MASK) - (src & BLUE_MASK);
int g = (dst & GREEN_MASK) - (src & GREEN_MASK);
int rb = (r < 0 ? -r : r) |
(b < 0 ? -b : b);
int gn = (g < 0 ? -g : g);
return min((dst >>> 24) + a, 0xFF) << 24 |
((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
((dst & GN_MASK) * d_a + gn * s_a) >>> 8 & GN_MASK;
}
/**
* Cousin of difference, algorithm used here is based on a Lingo version
* found here: http://www.mediamacros.com/item/item-1006687616/
* (Not yet verified to be correct).
* Exclusion
* O = (1 - S)D + S(1 - D)
* O = D + S - 2DS
*/
private static int blend_exclusion(int a, int b) {
// setup (this portion will always be the same)
int f = (b & ALPHA_MASK) >>> 24;
int ar = (a & RED_MASK) >> 16;
int ag = (a & GREEN_MASK) >> 8;
int ab = (a & BLUE_MASK);
int br = (b & RED_MASK) >> 16;
int bg = (b & GREEN_MASK) >> 8;
int bb = (b & BLUE_MASK);
// formula:
int cr = ar + br - ((ar * br) >> 7);
int cg = ag + bg - ((ag * bg) >> 7);
int cb = ab + bb - ((ab * bb) >> 7);
// alpha blend (this portion will always be the same)
return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
(peg(ar + (((cr - ar) * f) >> 8)) << 16) |
(peg(ag + (((cg - ag) * f) >> 8)) << 8) |
(peg(ab + (((cb - ab) * f) >> 8)) ) );
private static int blend_exclusion(int dst, int src) {
int a = src >>> 24;
int s_a = a + (a >= 0x7F ? 1 : 0);
int d_a = 0x100 - s_a;
int d_rb = dst & RB_MASK;
int d_gn = dst & GN_MASK;
int s_gn = src & GN_MASK;
int f_r = (dst & RED_MASK) >> 16;
int f_b = (dst & BLUE_MASK);
int rb_sub =
((src & RED_MASK) * (f_r + (f_r >= 0x7F ? 1 : 0)) |
(src & BLUE_MASK) * (f_b + (f_b >= 0x7F ? 1 : 0)))
>>> 7 & 0x01FF01FF;
int gn_sub = s_gn * (d_gn + (d_gn >= 0x7F00 ? 0x100 : 0))
>>> 15 & 0x0001FF00;
return min((dst >>> 24) + a, 0xFF) << 24 |
(d_rb * d_a + (d_rb + (src & RB_MASK) - rb_sub) * s_a) >>> 8 & RB_MASK |
(d_gn * d_a + (d_gn + s_gn - gn_sub) * s_a) >>> 8 & GN_MASK;
}
/*
* Multiply
* O = DS
*/
private static int blend_multiply(int dst, int src) {
int a = src >>> 24;
int s_a = a + (a >= 0x7F ? 1 : 0);
int d_a = 0x100 - s_a;
int d_gn = dst & GN_MASK;
int f_r = (dst & RED_MASK) >> 16;
int f_b = (dst & BLUE_MASK);
int rb =
((src & RED_MASK) * (f_r + 1) |
(src & BLUE_MASK) * (f_b + 1))
>>> 8 & RB_MASK;
int gn =
(src & GREEN_MASK) * (d_gn + 0x100)
>>> 16 & GN_MASK;
return min((dst >>> 24) + a, 0xFF) << 24 |
((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
(d_gn * d_a + gn * s_a) >>> 8 & GN_MASK;
}
/**
* returns the product of the input colors
* C = A * B
* Screen
* O = 1 - (1 - D)(1 - S)
* O = D + S - DS
*/
private static int blend_multiply(int a, int b) {
// setup (this portion will always be the same)
int f = (b & ALPHA_MASK) >>> 24;
int ar = (a & RED_MASK) >> 16;
int ag = (a & GREEN_MASK) >> 8;
int ab = (a & BLUE_MASK);
int br = (b & RED_MASK) >> 16;
int bg = (b & GREEN_MASK) >> 8;
int bb = (b & BLUE_MASK);
// formula:
int cr = (ar * br) >> 8;
int cg = (ag * bg) >> 8;
int cb = (ab * bb) >> 8;
// alpha blend (this portion will always be the same)
return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
(peg(ar + (((cr - ar) * f) >> 8)) << 16) |
(peg(ag + (((cg - ag) * f) >> 8)) << 8) |
(peg(ab + (((cb - ab) * f) >> 8)) ) );
private static int blend_screen(int dst, int src) {
int a = src >>> 24;
int s_a = a + (a >= 0x7F ? 1 : 0);
int d_a = 0x100 - s_a;
int d_rb = dst & RB_MASK;
int d_gn = dst & GN_MASK;
int s_gn = src & GN_MASK;
int f_r = (dst & RED_MASK) >> 16;
int f_b = (dst & BLUE_MASK);
int rb_sub =
((src & RED_MASK) * (f_r + 1) |
(src & BLUE_MASK) * (f_b + 1))
>>> 8 & RB_MASK;
int gn_sub = s_gn * (d_gn + 0x100)
>>> 16 & GN_MASK;
return min((dst >>> 24) + a, 0xFF) << 24 |
(d_rb * d_a + (d_rb + (src & RB_MASK) - rb_sub) * s_a) >>> 8 & RB_MASK |
(d_gn * d_a + (d_gn + s_gn - gn_sub) * s_a) >>> 8 & GN_MASK;
}
/**
* returns the inverse of the product of the inverses of the input colors
* (the inverse of multiply). C = 1 - (1-A) * (1-B)
* Overlay
* O = 2 * MULTIPLY(D, S) = 2DS for D < 0.5
* O = 2 * SCREEN(D, S) - 1 = 2(S + D - DS) - 1 otherwise
*/
private static int blend_screen(int a, int b) {
// setup (this portion will always be the same)
int f = (b & ALPHA_MASK) >>> 24;
int ar = (a & RED_MASK) >> 16;
int ag = (a & GREEN_MASK) >> 8;
int ab = (a & BLUE_MASK);
int br = (b & RED_MASK) >> 16;
int bg = (b & GREEN_MASK) >> 8;
int bb = (b & BLUE_MASK);
// formula:
int cr = 255 - (((255 - ar) * (255 - br)) >> 8);
int cg = 255 - (((255 - ag) * (255 - bg)) >> 8);
int cb = 255 - (((255 - ab) * (255 - bb)) >> 8);
// alpha blend (this portion will always be the same)
return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
(peg(ar + (((cr - ar) * f) >> 8)) << 16) |
(peg(ag + (((cg - ag) * f) >> 8)) << 8) |
(peg(ab + (((cb - ab) * f) >> 8)) ) );
private static int blend_overlay(int dst, int src) {
int a = src >>> 24;
int s_a = a + (a >= 0x7F ? 1 : 0);
int d_a = 0x100 - s_a;
int d_r = dst & RED_MASK;
int d_g = dst & GREEN_MASK;
int d_b = dst & BLUE_MASK;
int s_r = src & RED_MASK;
int s_g = src & GREEN_MASK;
int s_b = src & BLUE_MASK;
int r = (d_r < 0x800000) ?
d_r * ((s_r >>> 16) + 1) >>> 7 :
0xFF0000 - ((0x100 - (s_r >>> 16)) * (RED_MASK - d_r) >>> 7);
int g = (d_g < 0x8000) ?
d_g * (s_g + 0x100) >>> 15 :
(0xFF00 - ((0x10000 - s_g) * (GREEN_MASK - d_g) >>> 15));
int b = (d_b < 0x80) ?
d_b * (s_b + 1) >>> 7 :
(0xFF00 - ((0x100 - s_b) * (BLUE_MASK - d_b) << 1)) >>> 8;
return min((dst >>> 24) + a, 0xFF) << 24 |
((dst & RB_MASK) * d_a + ((r | b) & RB_MASK) * s_a) >>> 8 & RB_MASK |
((dst & GN_MASK) * d_a + (g & GN_MASK) * s_a) >>> 8 & GN_MASK;
}
/**
* returns either multiply or screen for darker or lighter values of A
* (the inverse of hard light)
* C =
* A < 0.5 : 2 * A * B
* A >=0.5 : 1 - (2 * (255-A) * (255-B))
* Hard Light
* O = OVERLAY(S, D)
*
* O = 2 * MULTIPLY(D, S) = 2DS for S < 0.5
* O = 2 * SCREEN(D, S) - 1 = 2(S + D - DS) - 1 otherwise
*/
private static int blend_overlay(int a, int b) {
// setup (this portion will always be the same)
int f = (b & ALPHA_MASK) >>> 24;
int ar = (a & RED_MASK) >> 16;
int ag = (a & GREEN_MASK) >> 8;
int ab = (a & BLUE_MASK);
int br = (b & RED_MASK) >> 16;
int bg = (b & GREEN_MASK) >> 8;
int bb = (b & BLUE_MASK);
// formula:
int cr = (ar < 128) ? ((ar*br)>>7) : (255-(((255-ar)*(255-br))>>7));
int cg = (ag < 128) ? ((ag*bg)>>7) : (255-(((255-ag)*(255-bg))>>7));
int cb = (ab < 128) ? ((ab*bb)>>7) : (255-(((255-ab)*(255-bb))>>7));
// alpha blend (this portion will always be the same)
return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
(peg(ar + (((cr - ar) * f) >> 8)) << 16) |
(peg(ag + (((cg - ag) * f) >> 8)) << 8) |
(peg(ab + (((cb - ab) * f) >> 8)) ) );
private static int blend_hard_light(int dst, int src) {
int a = src >>> 24;
int s_a = a + (a >= 0x7F ? 1 : 0);
int d_a = 0x100 - s_a;
int d_r = dst & RED_MASK;
int d_g = dst & GREEN_MASK;
int d_b = dst & BLUE_MASK;
int s_r = src & RED_MASK;
int s_g = src & GREEN_MASK;
int s_b = src & BLUE_MASK;
int r = (s_r < 0x800000) ?
s_r * ((d_r >>> 16) + 1) >>> 7 :
0xFF0000 - ((0x100 - (d_r >>> 16)) * (RED_MASK - s_r) >>> 7);
int g = (s_g < 0x8000) ?
s_g * (d_g + 0x100) >>> 15 :
(0xFF00 - ((0x10000 - d_g) * (GREEN_MASK - s_g) >>> 15));
int b = (s_b < 0x80) ?
s_b * (d_b + 1) >>> 7 :
(0xFF00 - ((0x100 - d_b) * (BLUE_MASK - s_b) << 1)) >>> 8;
return min((dst >>> 24) + a, 0xFF) << 24 |
((dst & RB_MASK) * d_a + ((r | b) & RB_MASK) * s_a) >>> 8 & RB_MASK |
((dst & GN_MASK) * d_a + (g & GN_MASK) * s_a) >>> 8 & GN_MASK;
}
/**
* returns either multiply or screen for darker or lighter values of B
* (the inverse of overlay)
* C =
* B < 0.5 : 2 * A * B
* B >=0.5 : 1 - (2 * (255-A) * (255-B))
* Soft Light (Pegtop)
* O = (1 - D) * MULTIPLY(D, S) + D * SCREEN(D, S)
* O = (1 - D) * DS + D * (1 - (1 - D)(1 - S))
* O = 2DS + DD - 2DDS
*/
private static int blend_hard_light(int a, int b) {
// setup (this portion will always be the same)
int f = (b & ALPHA_MASK) >>> 24;
int ar = (a & RED_MASK) >> 16;
int ag = (a & GREEN_MASK) >> 8;
int ab = (a & BLUE_MASK);
int br = (b & RED_MASK) >> 16;
int bg = (b & GREEN_MASK) >> 8;
int bb = (b & BLUE_MASK);
// formula:
int cr = (br < 128) ? ((ar*br)>>7) : (255-(((255-ar)*(255-br))>>7));
int cg = (bg < 128) ? ((ag*bg)>>7) : (255-(((255-ag)*(255-bg))>>7));
int cb = (bb < 128) ? ((ab*bb)>>7) : (255-(((255-ab)*(255-bb))>>7));
// alpha blend (this portion will always be the same)
return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
(peg(ar + (((cr - ar) * f) >> 8)) << 16) |
(peg(ag + (((cg - ag) * f) >> 8)) << 8) |
(peg(ab + (((cb - ab) * f) >> 8)) ) );
private static int blend_soft_light(int dst, int src) {
int a = src >>> 24;
int s_a = a + (a >= 0x7F ? 1 : 0);
int d_a = 0x100 - s_a;
int d_r = dst & RED_MASK;
int d_g = dst & GREEN_MASK;
int d_b = dst & BLUE_MASK;
int s_r1 = src & RED_MASK >> 16;
int s_g1 = src & GREEN_MASK >> 8;
int s_b1 = src & BLUE_MASK;
int d_r1 = (d_r >> 16) + (s_r1 < 7F ? 1 : 0);
int d_g1 = (d_g >> 8) + (s_g1 < 7F ? 1 : 0);
int d_b1 = d_b + (s_b1 < 7F ? 1 : 0);
int r = (s_r1 * d_r >> 7) + 0xFF * d_r1 * (d_r1 + 1) -
((s_r1 * d_r1 * d_r1) << 1) & RED_MASK;
int g = (s_g1 * d_g << 1) + 0xFF * d_g1 * (d_g1 + 1) -
((s_g1 * d_g1 * d_g1) << 1) >>> 8 & GREEN_MASK;
int b = (s_b1 * d_b << 9) + 0xFF * d_b1 * (d_b1 + 1) -
((s_b1 * d_b1 * d_b1) << 1) >>> 16;
return min((dst >>> 24) + a, 0xFF) << 24 |
((dst & RB_MASK) * d_a + (r | b) * s_a) >>> 8 & RB_MASK |
((dst & GN_MASK) * d_a + g * s_a) >>> 8 & GN_MASK;
}
/**
* returns the inverse multiply plus screen, which simplifies to
* C = 2AB + A^2 - 2A^2B
* Dodge
* O = D / (1 - S)
*/
private static int blend_soft_light(int a, int b) {
// setup (this portion will always be the same)
int f = (b & ALPHA_MASK) >>> 24;
int ar = (a & RED_MASK) >> 16;
int ag = (a & GREEN_MASK) >> 8;
int ab = (a & BLUE_MASK);
int br = (b & RED_MASK) >> 16;
int bg = (b & GREEN_MASK) >> 8;
int bb = (b & BLUE_MASK);
// formula:
int cr = ((ar*br)>>7) + ((ar*ar)>>8) - ((ar*ar*br)>>15);
int cg = ((ag*bg)>>7) + ((ag*ag)>>8) - ((ag*ag*bg)>>15);
int cb = ((ab*bb)>>7) + ((ab*ab)>>8) - ((ab*ab*bb)>>15);
// alpha blend (this portion will always be the same)
return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
(peg(ar + (((cr - ar) * f) >> 8)) << 16) |
(peg(ag + (((cg - ag) * f) >> 8)) << 8) |
(peg(ab + (((cb - ab) * f) >> 8)) ) );
private static int blend_dodge(int dst, int src) {
int a = src >>> 24;
int s_a = a + (a >= 0x7F ? 1 : 0);
int d_a = 0x100 - s_a;
int r = (dst & RED_MASK) / (256 - ((src & RED_MASK) >> 16));
int g = ((dst & GREEN_MASK) << 8) / (256 - ((src & GREEN_MASK) >> 8));
int b = ((dst & BLUE_MASK) << 8) / (256 - (src & BLUE_MASK));
int rb =
(r > 0xFF00 ? 0xFF0000 : ((r << 8) & RED_MASK)) |
(b > 0x00FF ? 0x0000FF : b);
int gn =
(g > 0xFF00 ? 0x00FF00 : (g & GREEN_MASK));
return min((dst >>> 24) + a, 0xFF) << 24 |
((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
((dst & GN_MASK) * d_a + gn * s_a) >>> 8 & GN_MASK;
}
/**
* Returns the first (underlay) color divided by the inverse of
* the second (overlay) color. C = A / (255-B)
* Burn
* O = 1 - (1 - A) / B
*/
private static int blend_dodge(int a, int b) {
// setup (this portion will always be the same)
int f = (b & ALPHA_MASK) >>> 24;
int ar = (a & RED_MASK) >> 16;
int ag = (a & GREEN_MASK) >> 8;
int ab = (a & BLUE_MASK);
int br = (b & RED_MASK) >> 16;
int bg = (b & GREEN_MASK) >> 8;
int bb = (b & BLUE_MASK);
// formula:
int cr = (br==255) ? 255 : peg((ar << 8) / (255 - br)); // division requires pre-peg()-ing
int cg = (bg==255) ? 255 : peg((ag << 8) / (255 - bg)); // "
int cb = (bb==255) ? 255 : peg((ab << 8) / (255 - bb)); // "
// alpha blend (this portion will always be the same)
return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
(peg(ar + (((cr - ar) * f) >> 8)) << 16) |
(peg(ag + (((cg - ag) * f) >> 8)) << 8) |
(peg(ab + (((cb - ab) * f) >> 8)) ) );
}
private static int blend_burn(int dst, int src) {
int a = src >>> 24;
int s_a = a + (a >= 0x7F ? 1 : 0);
int d_a = 0x100 - s_a;
/**
* returns the inverse of the inverse of the first (underlay) color
* divided by the second (overlay) color. C = 255 - (255-A) / B
*/
private static int blend_burn(int a, int b) {
// setup (this portion will always be the same)
int f = (b & ALPHA_MASK) >>> 24;
int ar = (a & RED_MASK) >> 16;
int ag = (a & GREEN_MASK) >> 8;
int ab = (a & BLUE_MASK);
int br = (b & RED_MASK) >> 16;
int bg = (b & GREEN_MASK) >> 8;
int bb = (b & BLUE_MASK);
// formula:
int cr = (br==0) ? 0 : 255 - peg(((255 - ar) << 8) / br); // division requires pre-peg()-ing
int cg = (bg==0) ? 0 : 255 - peg(((255 - ag) << 8) / bg); // "
int cb = (bb==0) ? 0 : 255 - peg(((255 - ab) << 8) / bb); // "
// alpha blend (this portion will always be the same)
return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
(peg(ar + (((cr - ar) * f) >> 8)) << 16) |
(peg(ag + (((cg - ag) * f) >> 8)) << 8) |
(peg(ab + (((cb - ab) * f) >> 8)) ) );
int r = ((0xFF0000 - (dst & RED_MASK))) / (1 + (src & RED_MASK >> 16));
int g = ((0x00FF00 - (dst & GREEN_MASK)) << 8) / (1 + (src & GREEN_MASK >> 8));
int b = ((0x0000FF - (dst & BLUE_MASK)) << 8) / (1 + (src & BLUE_MASK));
int rb = RB_MASK -
(r > 0xFF00 ? 0xFF0000 : ((r << 8) & RED_MASK)) -
(b > 0x00FF ? 0x0000FF : b);
int gn = GN_MASK -
(g > 0xFF00 ? 0x00FF00 : (g & GREEN_MASK));
return min((dst >>> 24) + a, 0xFF) << 24 |
((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
((dst & GN_MASK) * d_a + gn * s_a) >>> 8 & GN_MASK;
}