processing/processing — pull request #2921 by JakubValtar (2014-11-01 19:08:37+00:00)
Title: Multiple blending fixes & improvements

Java2D
- Source and destination were swapped, so nothing worked as expected. Now fixed.

OpenGL
- SUBTRACT is now O = D - (S * SA).
- MULTIPLY is now O = D * S.
- Alpha is now additive for all modes. OA = SA + DA. Fixes #2807, fixes #1224. (MIN and MAX equations ignore blending parameters, they are specified only because we need to specify blending parameters for alpha and blendFuncSeparate requires all four parameters.)
- LIGHTEST, DARKEST, EXCLUSION, MULTIPLY and SCREEN: Java2D uses SRC alpha to interpolate between DST color and OUTPUT color, however, this is not possible with OpenGL pipeline so OUTPUT color is used (as if SRC alpha was max).

Alpha 255
![b1](https://cloud.githubusercontent.com/assets/3177098/4872927/bb780f7c-61f8-11e4-8d88-0459df0cc68a.png)

Alpha 80 (other modes than REPLACE, BLEND, ADD and SUBTRACT are not comparable as JAVA2D uses SRC alpha to intrpolate between DST and OUTPUT, OpenGL just uses OUTPUT)
![b2](https://cloud.githubusercontent.com/assets/3177098/4872943/23b9f244-61f9-11e4-9331-36a8f26a89ac.png)

Test code:
```java
PGraphics pg;

PGraphics java2d;
PGraphics p2d;

int[] blendModes = {REPLACE, BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, EXCLUSION, MULTIPLY, SCREEN};
String[] blendLabels = {"REPLACE", "BLEND", "ADD", "SUBTRACT", "LIGHTEST", "DARKEST", "EXCLUSION", "MULTIPLY", "SCREEN"};

public void setup() {
size(900,900, P2D);
frame.setAlwaysOnTop(true);
java2d = createGraphics(300, 800 / blendModes.length);
p2d = createGraphics(300, 800 / blendModes.length, P2D);
}

public void draw() {
background(0xFF000000);
stroke(100);
for (int x = 0; x < width; x += 20) {
line(x, 0, x, height);
}
for (int y = 0; y < width; y += 20) {
line(0, y, width, y);
}
blendMode(BLEND);
textSize(25);

for (int i = 0; i < blendModes.length; i++) {
text(blendLabels[i], 50, 50 + (i + 0.5f) * (java2d.height + 5));

text("JAVA2D", 350, 30);
drawPG(java2d, blendModes[i]);
image(java2d, 250, 50 + i * (java2d.height + 5));
flush();

text("P2D", 655, 30);
drawPG(p2d, blendModes[i]);
image(p2d, 555, 50 + i * (p2d.height + 5));
flush();
}
}

void drawPG(PGraphics pg, int blendMode) {
pg.beginDraw();
pg.noStroke();
pg.blendMode(blendMode);
switch (blendMode) {
case ADD:
case LIGHTEST:
case SCREEN:
pg.background(0);
break;
default:
pg.background(255);
}
int alpha = 80;
int green1 = blendMode == DARKEST ? 255 : 0;
int green2 = blendMode == DARKEST ? 0 : 255;
for (int x = 0; x < pg.width; x += 12) {
for(int y = 0; y < pg.height; y += 12) {
int red = (int) map(x, 0, pg.width, 0, 255);
int green = (int) map(y*x, 0, pg.height * pg.width, green1, green2);
int blue = (int) map(x, 0, pg.height, 0, 255) % 255;
pg.fill(red, green, blue, alpha);
pg.rect(x, y, 12, pg.height - y);
}
}
pg.flush();
pg.endDraw();
}
```