使い処は限られているとは思いますが、色のブレンド関係のスクリプトを書いて見ました。Proccessingでいうところの「blendColor()」です。
普段使っているblendModeの仕組みメモも兼ねて。
淡白なようですが以下のようなスクリプトなりました。
/** * ColorBlend * @author nutsu */ package { public class ColorBlend { public static const ADD:String = "add"; public static const SUBTRACT:String = "subtract"; public static const DARKEN:String = "darken"; public static const LIGHTEN:String = "lighten"; public static const DIFFERENCE:String = "difference" public static const MULTIPLY:String = "multiply"; public static const SCREEN:String = "screen"; public static const OVERLAY:String = "overlay"; public static const HARDLIGHT:String = "hardlight"; public static const SOFTLIGHT:String = "softlight"; public static const DODGE:String = "dodge"; public static const BURN:String = "burn"; public static const EXCLUSION:String = "exclusion"; public function ColorBlend() { throw new Error("インスタンスはつくっても意味ないです"); } /** * ブレンド * @param 色1 * @param 色2 * @param ブレンドタイプ */ public static function blend( c1:uint, c2:uint, blendkind:String ):uint { switch( blendkind ) { case ADD: return add( c1, c2 ); case SUBTRACT: return sub( c1, c2 ); case DARKEN: return dark( c1, c2 ); case LIGHTEN: return light( c1, c2 ); case DIFFERENCE: return diff( c1, c2 ); case MULTIPLY: return multi( c1, c2 ); case SCREEN: return screen( c1, c2 ); case OVERLAY: return overlay( c1, c2 ); case HARDLIGHT: return hard( c1, c2 ); case SOFTLIGHT: return soft( c1, c2 ); case DODGE: return dodge( c1, c2 ); case BURN: return burn( c1, c2 ); case EXCLUSION: return excl( c1, c2 ); default: return c2; } } /** * 加算 */ public static function add( c1:uint, c2:uint ):uint { return addRGB( (c1 & 0xff0000)>>>16, (c1 & 0x00ff00)>>>8, c1 & 0x0000ff, (c2 & 0xff0000)>>>16, (c2 & 0x00ff00)>>>8, c2 & 0x0000ff ); } public static function addRGB( r1:uint, g1:uint, b1:uint, r2:uint, g2:uint, b2:uint ):uint { return Math.min( r1 + r2, 0xff ) << 16 | Math.min( g1 + g2, 0xff ) << 8 | Math.min( b1 + b2, 0xff ); } /** * 減算 */ public static function sub( c1:uint, c2:uint ):uint { return subRGB( (c1 & 0xff0000)>>>16, (c1 & 0x00ff00)>>>8, c1 & 0x0000ff, (c2 & 0xff0000)>>>16, (c2 & 0x00ff00)>>>8, c2 & 0x0000ff ); } public static function subRGB( r1:uint, g1:uint, b1:uint, r2:uint, g2:uint, b2:uint ):uint { return Math.max( r1 - r2, 0 ) << 16 | Math.max( g1 - g2, 0 ) << 8 | Math.max( b1 - b2, 0 ); } /** * 暗い方 */ public static function dark( c1:uint, c2:uint ):uint { return darkRGB( (c1 & 0xff0000)>>>16, (c1 & 0x00ff00)>>>8, c1 & 0x0000ff, (c2 & 0xff0000)>>>16, (c2 & 0x00ff00)>>>8, c2 & 0x0000ff ); } public static function darkRGB( r1:uint, g1:uint, b1:uint, r2:uint, g2:uint, b2:uint ):uint { return Math.min( r1, r2 ) << 16 | Math.min( g1, g2 ) << 8 | Math.min( b1, b2 ); } /** * 明るい方 */ public static function light( c1:uint, c2:uint ):uint { return lightRGB( (c1 & 0xff0000)>>>16, (c1 & 0x00ff00)>>>8, c1 & 0x0000ff, (c2 & 0xff0000)>>>16, (c2 & 0x00ff00)>>>8, c2 & 0x0000ff ); } public static function lightRGB( r1:uint, g1:uint, b1:uint, r2:uint, g2:uint, b2:uint ):uint { return Math.max( r1, r2 ) << 16 | Math.max( g1, g2 ) << 8 | Math.max( b1, b2 ); } /** * 差の絶対値 */ public static function diff( c1:uint, c2:uint ):uint { return diffRGB( (c1 & 0xff0000)>>>16, (c1 & 0x00ff00)>>>8, c1 & 0x0000ff, (c2 & 0xff0000)>>>16, (c2 & 0x00ff00)>>>8, c2 & 0x0000ff ); } public static function diffRGB( r1:uint, g1:uint, b1:uint, r2:uint, g2:uint, b2:uint ):uint { return Math.abs( r1-r2 ) << 16 | Math.abs( g1-g2 ) << 8 | Math.abs( b1-b2 ); } /** * 乗算 */ public static function multi( c1:uint, c2:uint ):uint { return multiRGB( (c1 & 0xff0000)>>>16, (c1 & 0x00ff00)>>>8, c1 & 0x0000ff, (c2 & 0xff0000)>>>16, (c2 & 0x00ff00)>>>8, c2 & 0x0000ff ); } public static function multiRGB( r1:uint, g1:uint, b1:uint, r2:uint, g2:uint, b2:uint ):uint { return ( r1*r2>>>8 ) << 16 | ( g1*g2>>>8 ) << 8 | b1*b2>>>8; } /** * スクリーン */ public static function screen( c1:uint, c2:uint ):uint { return multi( c1 ^ 0xffffff, c2 ^ 0xffffff ) ^ 0xffffff; } public static function screenRGB( r1:uint, g1:uint, b1:uint, r2:uint, g2:uint, b2:uint ):uint { return multiRGB( r1^0xff, g1^0xff, b1^0xff, r2^0xff, g2^0xff, b2^0xff )^0xffffff; } /** * オーバーレイ */ public static function overlay( c1:uint, c2:uint ):uint { return overlayRGB( (c1 & 0xff0000)>>>16, (c1 & 0x00ff00)>>>8, c1 & 0x0000ff, (c2 & 0xff0000)>>>16, (c2 & 0x00ff00)>>>8, c2 & 0x0000ff ); } public static function overlayRGB( r1:uint, g1:uint, b1:uint, r2:uint, g2:uint, b2:uint ):uint { return _overlay(r1,r2)<<16 | _overlay(g1,g2)<<8 | _overlay(b1,b2); } private static function _overlay( e1:uint, e2:uint ):uint { return ( e1 < 0x80 ) ? e1*e2>>>7 : ((e1^0xff)*(e2^0xff)>>>7)^0xff; } /** * ハードライト */ public static function hard( c1:uint, c2:uint ):uint { return hardRGB( (c1 & 0xff0000)>>>16, (c1 & 0x00ff00)>>>8, c1 & 0x0000ff, (c2 & 0xff0000)>>>16, (c2 & 0x00ff00)>>>8, c2 & 0x0000ff ); } public static function hardRGB( r1:uint, g1:uint, b1:uint, r2:uint, g2:uint, b2:uint ):uint { return _hard(r1,r2)<<16 | _hard(g1,g2)<<8 | _hard(b1,b2); } private static function _hard( e1:uint, e2:uint ):uint { return ( e2 < 0x80 ) ? e1*e2>>>7 : ((e1^0xff)*(e2^0xff)>>>7)^0xff; } /** * ソフトライト */ public static function soft( c1:uint, c2:uint ):uint { return softRGB( (c1 & 0xff0000)>>>16, (c1 & 0x00ff00)>>>8, c1 & 0x0000ff, (c2 & 0xff0000)>>>16, (c2 & 0x00ff00)>>>8, c2 & 0x0000ff ); } public static function softRGB( r1:uint, g1:uint, b1:uint, r2:uint, g2:uint, b2:uint ):uint { return _soft(r1,r2)<<16 | _soft(g1,g2)<<8 | _soft(b1,b2); } private static function _soft( e1:uint, e2:uint ):uint { return Math.floor( e1 * e2 / 0xff + e1 * ( 0xff - (e1 ^ 0xff) * (e2 ^ 0xff)/0xff - e1 * e2 / 0xff )/0xff ); } /** * 覆い焼き */ public static function dodge( c1:uint, c2:uint ):uint { return dodgeRGB( (c1 & 0xff0000)>>>16, (c1 & 0x00ff00)>>>8, c1 & 0x0000ff, (c2 & 0xff0000)>>>16, (c2 & 0x00ff00)>>>8, c2 & 0x0000ff ); } public static function dodgeRGB( r1:uint, g1:uint, b1:uint, r2:uint, g2:uint, b2:uint ):uint { return _dodge(r1,r2)<<16 | _dodge(g1,g2)<<8 | _dodge(b1,b2); } private static function _dodge( e1:uint, e2:uint ):uint { var x:uint; if ( e2 == 0xff || ( x = Math.floor( e1*0xff/(e2^0xff)) ) > 0xff ) return 0xff; else return x; } /** * 焼き込み */ public static function burn( c1:uint, c2:uint ):uint { return burnRGB( (c1 & 0xff0000)>>>16, (c1 & 0x00ff00)>>>8, c1 & 0x0000ff, (c2 & 0xff0000)>>>16, (c2 & 0x00ff00)>>>8, c2 & 0x0000ff ); } public static function burnRGB( r1:uint, g1:uint, b1:uint, r2:uint, g2:uint, b2:uint ):uint { return _burn(r1,r2)<<16 | _burn(g1,g2)<<8 | _burn(b1,b2); } private static function _burn( e1:uint, e2:uint ):uint { var x:uint; if ( e2 == 0 || (x = 0xff - Math.floor( (e1^0xff)*0xff/e2 ) ) < 0 ) return 0; else return x; } /** * 除外 */ public static function excl( c1:uint, c2:uint ):uint { return exclRGB( (c1 & 0xff0000)>>>16, (c1 & 0x00ff00)>>>8, c1 & 0x0000ff, (c2 & 0xff0000)>>>16, (c2 & 0x00ff00)>>>8, c2 & 0x0000ff ); } public static function exclRGB( r1:uint, g1:uint, b1:uint, r2:uint, g2:uint, b2:uint ):uint { return _excl(r1,r2)<<16 | _excl(g1,g2)<<8 | _excl(b1,b2); } private static function _excl( e1:uint, e2:uint ):uint { return e1 + e2 - ( e1*e2>>>7 ); } } }
実際にProccesingで計算した値と比べると微妙に違う(1大きいとか)場合もありますが、だいたい合っているような…。
なお、Sharaku Image Manipulation Program – ブレンドモード、osakana.factory – ブレンドモード詳説、を大変参考にしました。感謝です。