void setup() { int ncombo = 32896; int nerrors; //============================== // the following is the qa testbed for the original blend_add_pin() // left in only for completeness, it need not be re-run // it produces 16511 incorrect results //============================== /* println("ORIGINAL"); nerrors = 0; for (int ia=0; ia<256; ia++) { color ca = color(ia); for (int ib=ia; ib<256; ib++) { color cb = color(ib); int test = blend_add_pin(ca,cb) & 0xFF; int actual = low(ia+ib,255); if (test != actual) { println(ia + " + " + ib + " != " + test); nerrors++; } } } println("nerrors = " + nerrors + " (of " + ncombo + ")"); println("------------------------------"); */ //============================== // the following is the qa testbed for the NEW_blend_add_pin() //============================== println("NEW"); nerrors = 0; for (int ia=0; ia<256; ia++) { color ca = color(ia); for (int ib=ia; ib<256; ib++) { color cb = color(ib); int test = NEW_blend_add_pin(ca,cb) & 0xFF; int actual = low(ia+ib,255); if (test != actual) { println(ia + " + " + ib + " != " + test); nerrors++; } } } println("nerrors = " + nerrors + " (of " + ncombo + ")"); println("------------------------------"); } //============================== // the following two functions were copied ONLY to overcome private access // they were not modified in any way //============================== private static int low(int a, int b) { return (a < b) ? a : b; } private static int peg(int n) { return (n < 0) ? 0 : ((n > 255) ? 255 : n); } //============================== // the following is the original blend_add_pin() copied to overcome private access // it is here ONLY for running the qa testbed // it was not modified in any way //============================== private static int blend_add_pin(int a, int b) { int f = (b & ALPHA_MASK) >>> 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)); } //============================== // this is the new blend_add_pin() // it borrows the structure used in other newer blend mode implementations // EXCEPT that it uses "/ 255" in the blend rather than ">> 8". // technically, this is the sort of math that ALL the blend modes should // be using, though the gain in accuracy will vary by formula. (those // that tend to increase RGB values, like soft_light, hard_light, overlay, // burn, etc, would likely benefit most, as opposed to those that tend to // decrease RGB values, like multiply, dodge, etc) // // some further thoughts for future consideration, as I don't know how // performance-driven is the Processing philosophy, so at some expense to // readable code you could consider... // // aside: the way to do alpha blends "correct AND fast" is to prebuild a 64K // lookup of RGB*A/255 indexed by (RGB<<8)+A [or (A<<8)+RGB], don't know if // you wanna go there though... // // aside to the aside: you could save a bit of memory if you min/max them // first and only store one diagonal "half" n(n+1)/2=32896 of that table, // trading some cpu for memory, but then not much performance gain, so why bother // // further aside: another performance trick is to prebuild a pegging table, // size 768 (256*3), containing peg(i-256), indexed by RGB+256, so for example // table looks like: 0,0,0,0,0....1,2,3,4,5....255,255,255,255,255 // with SUB: RGB0 - RGB255 = RGB-255 + IDX256 = IDX1 contains PEG0 // identity:: RGB128 = RGB128 + IDX256 = IDX384 contains PEG128 // with ADD: RGB255 + RGB255 = RGB510 + IDX256 = IDX766 contains PEG255 //============================== private static int NEW_blend_add_pin(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; int cg = ag + bg; int cb = ab + bb; // alpha blend (this portion will always be the same) // (err, NOT quite the same here - this is the "correct" version using "/ 255") return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (peg(ar + (((cr - ar) * f) / 255)) << 16) | (peg(ag + (((cg - ag) * f) / 255)) << 8) | (peg(ab + (((cb - ab) * f) / 255)) ) ); }