使い処は限られているとは思いますが、色のブレンド関係のスクリプトを書いて見ました。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 – ブレンドモード詳説、を大変参考にしました。感謝です。