// Compositor.pde // Dave Bollinger, Dec 2006 // http://www.davebollinger.com // as of Processing 0123 Beta // scavenged and flattened out of half-finished // classes for this demo presentation static final int COMP_MODE_COPY = -1; static final int COMP_MODE_NORMAL = 0; static final int COMP_MODE_ADD = 1; static final int COMP_MODE_SUBTRACT = 2; static final int COMP_MODE_SUBTRACTIVE = 3; static final int COMP_MODE_DIFFERENCE = 4; static final int COMP_MODE_AVERAGE = 5; static final int COMP_MODE_NEGATE = 6; static final int COMP_MODE_MULTIPLY = 7; static final int COMP_MODE_DIVIDE = 8; static final int COMP_MODE_SCREEN = 9; static final int COMP_MODE_OVERLAY = 10; static final int COMP_MODE_HARD_LIGHT = 11; static final int COMP_MODE_SOFT_LIGHT = 12; static final int COMP_MODE_GAMMA_LIGHT = 13; static final int COMP_MODE_PIN_LIGHT = 14; static final int COMP_MODE_SHARP_LIGHT = 15; static final int COMP_MODE_VIVID_LIGHT = 16; static final int COMP_MODE_LINEAR_LIGHT = 17; static final int COMP_MODE_POWER_LIGHT = 18; static final int COMP_MODE_DARKER = 19; static final int COMP_MODE_DARKER_LUMINANCE = 20; static final int COMP_MODE_LIGHTER = 21; static final int COMP_MODE_LIGHTER_LUMINANCE = 22; static final int COMP_MODE_DODGE = 23; static final int COMP_MODE_DODGE_INVERSE = 24; static final int COMP_MODE_DODGE_SOFT = 25; static final int COMP_MODE_DODGE_LUMINANCE = 26; static final int COMP_MODE_BURN = 27; static final int COMP_MODE_BURN_INVERSE = 28; static final int COMP_MODE_BURN_SOFT = 29; static final int COMP_MODE_BURN_LUMINANCE = 30; static final int COMP_MODE_HARDMIX = 31; static final int COMP_MODE_HARDMIX2 = 32; static final int COMP_MODE_DISSOLVE = 33; static final int COMP_MODE_EXCLUSION = 34; static final int COMP_MODE_REFLECT = 35; static final int COMP_MODE_GLOW = 36; static final int COMP_MODE_COOL = 37; static final int COMP_MODE_HEAT = 38; static final int COMP_MODE_STAMP = 39; // weird stuff from here on out... static final int COMP_MODE_MODULO = 40; static final int COMP_MODE_ADD_MODULO = 41; static final int COMP_MODE_SUBTRACT_MODULO = 42; static final int COMP_MODE_BITAND = 43; static final int COMP_MODE_BITOR = 44; static final int COMP_MODE_BITXOR = 45; static final int COMP_MODE_RANDOM = 46; static final int COMP_MODE_JUST_R = 47; static final int COMP_MODE_JUST_G = 48; static final int COMP_MODE_JUST_B = 49; static final int COMP_MODE_JUST_RG = 50; static final int COMP_MODE_JUST_RB = 51; static final int COMP_MODE_JUST_BG = 52; static final int COMP_MODE_ASINB = 53; static final int COMP_MODE_BSINA = 54; static final int COMP_MODE_ASINB2 = 55; static final int COMP_MODE_BSINA2 = 56; // Not Yet Implemented // there are a number of issues to consider for the non-rgb composition modes, // not the least of which is brightness != lightness != luminance, the next // not least of which is HSB saturation != HLS saturation, and also the whole // nasty issue of gamma if we wanted to properly handle any colorspace that // contains a reasonable definition of "luminance" //--> indicates the probable implementation static final int NYI_COMP_MODE_HUE = 90; //--> H2,S1,B1 (in HSB space for P5, though PhotoShop is HSY here?) static final int NYI_COMP_MODE_SATURATION = 91; //--> H1,S2,B1 (in HSB space for P5, though PhotoShop is HSY here?) static final int NYI_COMP_MODE_BRIGHTNESS = 92; //--> H1,S1,B2 (in HSB space for P5, though PhotoShop is HSY here?) static final int NYI_COMP_MODE_COLOR = 93; //--> H2,S2,B1 (in HSB space for P5, though PhotoShop is HSY here?) static final int NYI_COMP_MODE_LUMINANCE = 94; //--> H1,S1,Y2 (in PhotoShop's HSY space) static final int NYI_COMP_MODE_LIGHTNESS = 95; //--> H1,L2,S1 (in HLS space) // last mode implemented static final int COMP_MODE_LAST = COMP_MODE_BSINA2; // for internal use only: a slot where i test new formulae static final int COMP_MODE_DEV = 100; public String compositeModeName(int mode) { switch(mode) { case COMP_MODE_COPY : return "COPY"; case COMP_MODE_NORMAL : return "NORMAL"; case COMP_MODE_ADD : return "ADD"; case COMP_MODE_SUBTRACT : return "SUBTRACT"; case COMP_MODE_SUBTRACTIVE : return "SUBTRACTIVE"; case COMP_MODE_DIFFERENCE : return "DIFFERENCE"; case COMP_MODE_AVERAGE : return "AVERAGE"; case COMP_MODE_NEGATE : return "NEGATE"; case COMP_MODE_MULTIPLY : return "MULTIPLY"; case COMP_MODE_DIVIDE : return "DIVIDE"; case COMP_MODE_SCREEN : return "SCREEN"; case COMP_MODE_OVERLAY : return "OVERLAY"; case COMP_MODE_HARD_LIGHT : return "HARD LIGHT"; case COMP_MODE_SOFT_LIGHT : return "SOFT LIGHT"; case COMP_MODE_GAMMA_LIGHT : return "GAMMA LIGHT"; case COMP_MODE_PIN_LIGHT : return "PIN LIGHT"; case COMP_MODE_SHARP_LIGHT : return "SHARP LIGHT"; case COMP_MODE_VIVID_LIGHT : return "VIVID LIGHT"; case COMP_MODE_LINEAR_LIGHT : return "LINEAR LIGHT"; case COMP_MODE_POWER_LIGHT : return "POWER LIGHT"; case COMP_MODE_DARKER : return "DARKER"; case COMP_MODE_DARKER_LUMINANCE : return "DARKER LUMINANCE"; case COMP_MODE_LIGHTER : return "LIGHTER"; case COMP_MODE_LIGHTER_LUMINANCE : return "LIGHTER LUMINANCE"; case COMP_MODE_DODGE : return "DODGE"; case COMP_MODE_DODGE_INVERSE : return "DODGE INVERSE"; case COMP_MODE_DODGE_SOFT : return "DODGE SOFT"; case COMP_MODE_DODGE_LUMINANCE : return "DODGE LUMINANCE"; case COMP_MODE_BURN : return "BURN"; case COMP_MODE_BURN_INVERSE : return "BURN INVERSE"; case COMP_MODE_BURN_SOFT : return "BURN SOFT"; case COMP_MODE_BURN_LUMINANCE : return "BURN LUMINANCE"; case COMP_MODE_HARDMIX : return "HARDMIX"; case COMP_MODE_HARDMIX2 : return "HARDMIX2"; case COMP_MODE_DISSOLVE : return "DISSOLVE"; case COMP_MODE_EXCLUSION : return "EXCLUSION"; case COMP_MODE_REFLECT : return "REFLECT"; case COMP_MODE_GLOW : return "GLOW"; case COMP_MODE_COOL : return "COOL"; case COMP_MODE_HEAT : return "HEAT"; case COMP_MODE_STAMP : return "STAMP"; case COMP_MODE_MODULO : return "MODULO"; case COMP_MODE_ADD_MODULO : return "ADD MODULO"; case COMP_MODE_SUBTRACT_MODULO : return "SUBTRACT MODULO"; case COMP_MODE_BITAND : return "BITAND"; case COMP_MODE_BITOR : return "BITOR"; case COMP_MODE_BITXOR : return "BITXOR"; case COMP_MODE_RANDOM : return "RANDOM"; case COMP_MODE_JUST_R : return "JUST R"; case COMP_MODE_JUST_G : return "JUST G"; case COMP_MODE_JUST_B : return "JUST B"; case COMP_MODE_JUST_RG : return "JUST RG"; case COMP_MODE_JUST_RB : return "JUST RB"; case COMP_MODE_JUST_BG : return "JUST BG"; case COMP_MODE_ASINB : return "ASINB"; case COMP_MODE_BSINA : return "BSINA"; case COMP_MODE_ASINB2 : return "ASINB2"; case COMP_MODE_BSINA2 : return "BSINA2"; // case COMP_MODE_DEV : return "DEV"; } return "UNKNOWN"; } public void composite(PImage src1, PImage src2, PImage dst, int mode, int t) { // KNOWN LIMITATIONS: // assert(all images != null) // assert(src1.width == src2.width == dst.width) // assert(src1.height == src2.height == dst.height) // assert(all images are argb) // assert(mode is legal) t = constrain(t,0,256); src1.loadPixels(); src2.loadPixels(); dst.loadPixels(); int temp, idx = 0; for (int y=dst.height; y>0; y--) { for (int x=dst.width; x>0; x--) { // yes, this could be optimized as switch-loop-loop instead of loop-loop-switch // but who wants to write 40 some odd loop-loops? not me. color result = 0; color c1 = src1.pixels[idx]; color c2 = src2.pixels[idx]; int a1 = ((c1 & ALPHA_MASK) >>> 24); int a2 = ((c2 & ALPHA_MASK) >>> 24); int aa = peg(a1 + (((a2 - a1) * t) >> 8)); if (aa != 0) { // when alpha is zero, entire result remains zero // "t" is the layer alpha (like the slider on a photoshop layer) // "a2" is the overlay (src2) pixel alpha (same as toxi's "f" if you're converting) // "ta2" is the two alphas combined (which would just be "f" if you're converting, as p5 doesn't have layer alpha) int ta2 = (t * a2) >> 8; // toxi's blend_* routines use the clever tick of performing their ops // in situ, on unshifted values, thus avoiding the shift down AND back. // i didn't do that here because 1) i wasn't sure that i wouldn't end // up with some ops that would overflow (at least red), and 2) it makes // it a lot easier to read. Make it right first, THEN make it fast. int r1=((c1&RED_MASK)>>16), g1=((c1&GREEN_MASK)>>8), b1=(c1&BLUE_MASK); int r2=((c2&RED_MASK)>>16), g2=((c2&GREEN_MASK)>>8), b2=(c2&BLUE_MASK); int dr=0, dg=0, db=0; switch(mode) { case COMP_MODE_COPY : // this should be special-cased at top of function dr = r2; dg = g2; db = b2; ta2 = 256; break; default : case COMP_MODE_NORMAL : dr = r2; dg = g2; db = b2; break; case COMP_MODE_ADD : dr = r1 + r2; dg = g1 + g2; db = b1 + b2; break; case COMP_MODE_SUBTRACT : dr = r1 - r2; dg = g1 - g2; db = b1 - b2; break; case COMP_MODE_SUBTRACTIVE : dr = r1 + r2 - 256; dg = g1 + g2 - 256; db = b1 + b2 - 256; break; case COMP_MODE_DIFFERENCE : dr = r1 - r2; if (dr < 0) dr = -dr; dg = g1 - g2; if (dg < 0) dg = -dg; db = b1 - b2; if (db < 0) db = -db; break; case COMP_MODE_AVERAGE : // a bit redundant - it's just a half-range "BLEND" dr = (r1 + r2) >> 1; dg = (g1 + g2) >> 1; db = (b1 + b2) >> 1; break; case COMP_MODE_NEGATE : dr = 255 - r1 - r2; if (dr < 0) dr = -dr; dg = 255 - g1 - g2; if (dg < 0) dg = -dg; db = 255 - b1 - b2; if (db < 0) db = -db; dr = 255 - dr; dg = 255 - dg; db = 255 - db; break; case COMP_MODE_MULTIPLY : dr = (r1 * r2) / 255; dg = (g1 * g2) / 255; db = (b1 * b2) / 255; break; case COMP_MODE_DIVIDE : dr = (r2==0) ? 255 : (r1 << 8) / r2; dg = (g2==0) ? 255 : (g1 << 8) / g2; db = (b2==0) ? 255 : (b1 << 8) / b2; break; case COMP_MODE_SCREEN : dr = 255 - (((255 - r1) * (255 - r2)) / 255); dg = 255 - (((255 - g1) * (255 - g2)) / 255); db = 255 - (((255 - b1) * (255 - b2)) / 255); break; case COMP_MODE_OVERLAY : dr = (r1 < 128) ? ((r1*r2)/127) : (255-(((255-r1)*(255-r2))/127)); dg = (g1 < 128) ? ((g1*g2)/127) : (255-(((255-g1)*(255-g2))/127)); db = (b1 < 128) ? ((b1*b2)/127) : (255-(((255-b1)*(255-b2))/127)); break; case COMP_MODE_HARD_LIGHT : dr = (r2 < 128) ? ((r1*r2)/127) : (255-(((255-r1)*(255-r2))/127)); dg = (g2 < 128) ? ((g1*g2)/127) : (255-(((255-g1)*(255-g2))/127)); db = (b2 < 128) ? ((b1*b2)/127) : (255-(((255-b1)*(255-b2))/127)); break; case COMP_MODE_SOFT_LIGHT : dr = ((r1*r2)>>7) + ((r1*r1)>>8) - ((r1*r1*r2)>>15); dg = ((g1*g2)>>7) + ((g1*g1)>>8) - ((g1*g1*g2)>>15); db = ((b1*b2)>>7) + ((b1*b1)>>8) - ((b1*b1*b2)>>15); break; case COMP_MODE_GAMMA_LIGHT : // if the above guess is not right for softlight then this is its bigger // and better substitute - true gamma correction. This operation ain't cheap // though, so I have to wonder if it's actually in use by any production // software for on-the-fly layer compositing. still, it's the "accurate" // way to do a "softlight" and is pretty cool if you can spare the cpu: // (i don't think even photoshop has something this cool, so SHHH! or // they'll rip it for inclusion in cs3, fargin bastages) dr = (int)(pow((float)(r1)/255.0f, 1.0f/((float)(r2)/170.0f+0.5f)) * 255.0f); dg = (int)(pow((float)(g1)/255.0f, 1.0f/((float)(g2)/170.0f+0.5f)) * 255.0f); db = (int)(pow((float)(b1)/255.0f, 1.0f/((float)(b2)/170.0f+0.5f)) * 255.0f); // (note to self: the "170.0f" and such scales [0,255] into [0.5,2.0], could adjust if more range were needed) break; case COMP_MODE_PIN_LIGHT : dr = (r1<(r2+r2-255)) ? (r2+r2-255) : ((r1>(r2<<1)) ? (r2<<1) : (r1)); dg = (g1<(g2+g2-255)) ? (g2+g2-255) : ((g1>(g2<<1)) ? (g2<<1) : (g1)); db = (b1<(b2+b2-255)) ? (b2+b2-255) : ((b1>(b2<<1)) ? (b2<<1) : (b1)); break; case COMP_MODE_SHARP_LIGHT : dr = ((r1*r2*r2)>>15) + ((r1*r1)>>8) - ((r1*r1*r2)>>15); dg = ((g1*g2*r2)>>15) + ((g1*g1)>>8) - ((g1*g1*g2)>>15); db = ((b1*b2*r2)>>15) + ((b1*b1)>>8) - ((b1*b1*b2)>>15); break; case COMP_MODE_VIVID_LIGHT : dr = (r1 < 128) ? ((r1==0) ? 127 : (255-((((255-r2)<<8)/(r1<<1))))) : ((r1==255) ? 128 : ((r2<<8)/(2*(255-r1)))); dg = (g1 < 128) ? ((g1==0) ? 127 : (255-((((255-g2)<<8)/(g1<<1))))) : ((g1==255) ? 128 : ((g2<<8)/(2*(255-g1)))); db = (b1 < 128) ? ((b1==0) ? 127 : (255-((((255-b2)<<8)/(b1<<1))))) : ((b1==255) ? 128 : ((b2<<8)/(2*(255-b1)))); break; case COMP_MODE_LINEAR_LIGHT : dr = r1 + (r2<<1) - 255; dg = g1 + (g2<<1) - 255; db = b1 + (b2<<1) - 255; break; case COMP_MODE_POWER_LIGHT : dr = ((r1*r2*r2)>>15) + ((r1*r1)>>8) - ((r1*r1*r2)>>15) + ((r2*r2)>>8); dg = ((g1*g2*r2)>>15) + ((g1*g1)>>8) - ((g1*g1*g2)>>15) + ((g2*g2)>>8); db = ((b1*b2*r2)>>15) + ((b1*b1)>>8) - ((b1*b1*b2)>>15) + ((b2*b2)>>8); break; case COMP_MODE_DARKER : dr = (r1 < r2) ? r1 : r2; dg = (g1 < g2) ? g1 : g2; db = (b1 < b2) ? b1 : b2; break; case COMP_MODE_DARKER_LUMINANCE: temp = luminance(c1) - luminance(c2); dr = (temp < 0) ? r1 : r2; dg = (temp < 0) ? g1 : g2; db = (temp < 0) ? b1 : b2; break; case COMP_MODE_LIGHTER : dr = (r1 > r2) ? r1 : r2; dg = (g1 > g2) ? g1 : g2; db = (b1 > b2) ? b1 : b2; break; case COMP_MODE_LIGHTER_LUMINANCE : temp = luminance(c1) - luminance(c2); dr = (temp > 0) ? r1 : r2; dg = (temp > 0) ? g1 : g2; db = (temp > 0) ? b1 : b2; break; case COMP_MODE_DODGE : dr = (r2==255) ? 255 : (r1 << 8) / (255 - r2); dg = (g2==255) ? 255 : (g1 << 8) / (255 - g2); db = (b2==255) ? 255 : (b1 << 8) / (255 - b2); break; case COMP_MODE_DODGE_INVERSE: dr = (r1==255) ? 255 : (r2 << 8) / (255 - r1); dg = (g1==255) ? 255 : (g2 << 8) / (255 - g1); db = (b1==255) ? 255 : (b2 << 8) / (255 - b1); break; case COMP_MODE_DODGE_SOFT: dr = (r1+r2<256) ? ((r2==255) ? 255 : (r1 << 7) / (255 - r2)) : ((r1==0) ? 255 : (255 - ((255 - r2) << 7) / r1)); dg = (g1+g2<256) ? ((g2==255) ? 255 : (g1 << 7) / (255 - g2)) : ((g1==0) ? 255 : (255 - ((255 - g2) << 7) / g1)); db = (b1+b2<256) ? ((b2==255) ? 255 : (b1 << 7) / (255 - b2)) : ((b1==0) ? 255 : (255 - ((255 - b2) << 7) / b1)); break; case COMP_MODE_DODGE_LUMINANCE : temp = (77 * r2 + 150 * g2 + 29 * b2) >> 8; dr = r1 + temp; dg = g1 + temp; db = b1 + temp; break; case COMP_MODE_BURN : dr = (r2==0) ? 0 : 255 - ((255 - r1) << 8) / r2; dg = (g2==0) ? 0 : 255 - ((255 - g1) << 8) / g2; db = (b2==0) ? 0 : 255 - ((255 - b1) << 8) / b2; break; case COMP_MODE_BURN_INVERSE : dr = (r1==0) ? 0 : 255 - ((255 - r2) << 8) / r1; dg = (g1==0) ? 0 : 255 - ((255 - g2) << 8) / g1; db = (b1==0) ? 0 : 255 - ((255 - b2) << 8) / b1; break; case COMP_MODE_BURN_SOFT: dr = (r1+r2<256) ? ((r1==255) ? 255 : (r2 << 7) / (255 - r1)) : ((r2==0) ? 0 : (255 - (((255 - r1) << 7) / r2))); dg = (r1+r2<256) ? ((g1==255) ? 255 : (g2 << 7) / (255 - g1)) : ((g2==0) ? 0 : (255 - (((255 - g1) << 7) / g2))); db = (r1+r2<256) ? ((b1==255) ? 255 : (b2 << 7) / (255 - b1)) : ((b2==0) ? 0 : (255 - (((255 - b1) << 7) / b2))); break; case COMP_MODE_BURN_LUMINANCE : temp = (77 * r2 + 150 * g2 + 29 * b2) >> 8; dr = r1 - temp; dg = g1 - temp; db = b1 - temp; break; case COMP_MODE_HARDMIX : // one-bit posterize, why did adobe even bother? dr = (r1 < r2) ? 0 : 255; dg = (g1 < g2) ? 0 : 255; db = (b1 < b2) ? 0 : 255; break; case COMP_MODE_HARDMIX2 : // two-bit posterize, just cuz, why not? dr = (r1 < (r2>>1)) ? 0 : ((r1>1)) ? 0 : ((g1>1)) ? 0 : ((b1> 8; break; case COMP_MODE_EXCLUSION : dr = r1 + r2 - ((2 * r1 * r2) / 255); dg = g1 + g2 - ((2 * g1 * g2) / 255); db = b1 + b2 - ((2 * b1 * b2) / 255); break; case COMP_MODE_REFLECT : // an exponential dodge variant, this one's got a HEMI // borrowed from somewhere on the web, credits are due, apologies for the thievery dr = (r2==255) ? 255 : (r1 * r1) / (255 - r2); dg = (g2==255) ? 255 : (g1 * g1) / (255 - g2); db = (b2==255) ? 255 : (b1 * b1) / (255 - b2); break; case COMP_MODE_GLOW : // and the corresponding burn variant, also with a HEMI // borrowed from somewhere on the web, credits are due, apologies for the thievery dr = (r1==255) ? 255 : (r2 * r2) / (255 - r1); dg = (g1==255) ? 255 : (g2 * g2) / (255 - g1); db = (b1==255) ? 255 : (b2 * b2) / (255 - b1); break; case COMP_MODE_COOL : dr = (r2==0) ? 0 : 255 - (255-r1) * (255-r1) / r2; dg = (g2==0) ? 0 : 255 - (255-g1) * (255-g1) / g2; db = (b2==0) ? 0 : 255 - (255-b1) * (255-b1) / b2; break; case COMP_MODE_HEAT : dr = (r1==0) ? 0 : 255 - (255-r2) * (255-r2) / r1; dg = (g1==0) ? 0 : 255 - (255-g2) * (255-g2) / g1; db = (b1==0) ? 0 : 255 - (255-b2) * (255-b2) / b1; break; case COMP_MODE_STAMP : dr = r1 + r2 + r2 - 256; dg = g1 + g2 + g2 - 256; db = b1 + b2 + b2 - 256; break; case COMP_MODE_MODULO : // puke dr = (r2==0) ? 0 : r1 % r2; dg = (g2==0) ? 0 : g1 % g2; db = (b2==0) ? 0 : b1 % b2; break; case COMP_MODE_ADD_MODULO : // puke++ dr = (r1 + r2) & 0xff; dg = (g1 + g2) & 0xff; db = (b1 + b2) & 0xff; break; case COMP_MODE_SUBTRACT_MODULO: // --puke dr = (r1 - r2) & 0xff; dg = (g1 - g2) & 0xff; db = (b1 - b2) & 0xff; break; case COMP_MODE_BITAND : // spew dr = r1 & r2; dg = g1 & g2; db = b1 & b2; break; case COMP_MODE_BITOR : // hurl dr = r1 | r2; dg = g1 | g2; db = b1 | b2; break; case COMP_MODE_BITXOR : // barf dr = r1 ^ r2; dg = g1 ^ g2; db = b1 ^ b2; break; case COMP_MODE_RANDOM : // gag with spoon temp = r2 - r1; dr = (temp==0) ? r2 : rng() % temp + r1; temp = g2 - g1; dg = (temp==0) ? g2 : rng() % temp + g1; temp = b2 - b1; db = (temp==0) ? b2 : rng() % temp + b1; break; case COMP_MODE_JUST_R : dr = r2; dg = g1; db = b1; break; case COMP_MODE_JUST_G : dr = r1; dg = g2; db = b1; break; case COMP_MODE_JUST_B : dr = r1; dg = g1; db = b2; break; case COMP_MODE_JUST_RG : dr = r2; dg = g2; db = b1; break; case COMP_MODE_JUST_RB : dr = r2; dg = g1; db = b2; break; case COMP_MODE_JUST_BG : dr = r1; dg = g2; db = b2; break; case COMP_MODE_ASINB : // where A OVER B, so A = src2, B = src1 dr = (int)((float)(r2) * sin((float)(r1)/255f*PI)); dg = (int)((float)(g2) * sin((float)(g1)/255f*PI)); db = (int)((float)(b2) * sin((float)(b1)/255f*PI)); break; case COMP_MODE_BSINA : // where A OVER B, so A = src2, B = src1 dr = (int)((float)(r1) * sin((float)(r2)/255f*PI)); dg = (int)((float)(g1) * sin((float)(g2)/255f*PI)); db = (int)((float)(b1) * sin((float)(b2)/255f*PI)); break; case COMP_MODE_ASINB2 : // where A OVER B, so A = src2, B = src1 dr = (int)((float)(r2) * sin((float)(r1)/255f*TWO_PI)); dg = (int)((float)(g2) * sin((float)(g1)/255f*TWO_PI)); db = (int)((float)(b2) * sin((float)(b1)/255f*TWO_PI)); break; case COMP_MODE_BSINA2 : // where A OVER B, so A = src2, B = src1 dr = (int)((float)(r1) * sin((float)(r2)/255f*TWO_PI)); dg = (int)((float)(g1) * sin((float)(g2)/255f*TWO_PI)); db = (int)((float)(b1) * sin((float)(b2)/255f*TWO_PI)); break; //--- case COMP_MODE_DEV : break; } // finally we have dr,dg,db so lerp em and clip em int rr = (r1*a1 + ((peg(dr) - r1) * ta2)) >> 8; int gg = (g1*a1 + ((peg(dg) - g1) * ta2)) >> 8; int bb = (b1*a1 + ((peg(db) - b1) * ta2)) >> 8; result = (aa<<24) | (rr<<16) | (gg<<8) | (bb); } dst.pixels[idx++] = result; } } dst.updatePixels(); } // // A HANDFUL OF UTILITY ROUTINES // private int peg(int v) { return (v < 0 ? 0 : (v > 255 ? 255 : v)); } private int luminance(color c) { int r = (c & RED_MASK) >> 16; int g = (c & GREEN_MASK) >> 8; int b = (c & BLUE_MASK); // approximates the coefficients Y = 0.299R + 0.587G + 0.114B return ((77 * r + 150 * g + 29 * b) >> 8); } // need an 8=bit rng that is small and fast, not necessarily robust, for dissolve. // (a separate rng also keeps it from stealing seeds from p5's which might pose compatibility problems) private int rngseed = 0; private int rng() { rngseed = rngseed * 1103515245 + 12345; return ((rngseed >> 16) & 0xFF); }