package
{
    import flash.display.BitmapData;
    import flash.geom.Point;
    import flash.geom.Rectangle;

    /// 色重心を計算する
    public class ColorGravity
    {
        private static var MASKFLAG_COLOR:uint = 0xFFFF0000;        ///< 色判定する時のフラグ色 
        private static var GRAVFLAG_COLOR:uint = 0xFFFFFFFF;        ///< 色重心を判定した部分を塗りつぶす色

        /**
         * ラベリング処理を行う
         * @param [in] src ラベリング処理を行うBitmapData
         * @param [out] labelArray ラベリング結果を保存する配列。LabelDataクラスのオブジェクトが入る
         * @return ラベリング結果を書き込んだBitmapData
         */
        private static function labeling(src:BitmapData, labelArray:Array):BitmapData {

            var dst:BitmapData = src.clone(); // ソースの複製を作る
            var temp:BitmapData = new BitmapData( dst.width, 1, false, 0x000000 );
            var lno:int = 0;
            var zero:Point = new Point();
            var rect:Rectangle = dst.getColorBoundsRect( 0xffffff, MASKFLAG_COLOR, true );
            var area:Rectangle = new Rectangle( 0, 0, dst.width, 1 );

            while ( !rect.isEmpty() ){
                area.y = rect.top;
                temp.copyPixels( dst, area, zero );
                rect = temp.getColorBoundsRect( 0xffffff, MASKFLAG_COLOR, true );
                dst.floodFill( rect.x, area.y, ++lno );
                rect = dst.getColorBoundsRect( 0xffffff, MASKFLAG_COLOR, true );
            }

            // ラベル配列の作成
            for( var i:int = 1; i < lno; ++i ){
                var dots:uint = dst.threshold(dst, dst.rect, new Point(), "==", 0xff000000 + i, 0xff000000 + i);
                var labelData:LabelData = new LabelData(i, dots);
                labelArray.push(labelData);
            }
            labelArray.sort(sortOnDots);

            return dst;
        }

        /** ラベル値による画像の抜き出し
         * @param [in] lbd ラベリングデータ
         * @param [in] lno ラベル番号
         * @return 指定のラベル番号だけ抽出したイメージ
         */
        private static function extract(lbd:BitmapData, lno:int):BitmapData {
            var dst:BitmapData = new BitmapData(lbd.width, lbd.height, true, 0x00000000);
            dst.threshold(lbd, lbd.rect, new Point(), "==", 0xff000000 + lno, GRAVFLAG_COLOR);
            return dst;
        }

        /**
         * 面積数に応じて降順で配列内のLabelDataを比較する関数
         */
        private static function sortOnDots(a:LabelData, b:LabelData):int {

            if(a.dots > b.dots) {
                return -1;
            } else if(a.dots < b.dots) {
                return 1;
            } else  {
                return 0;
            }
        }

        /**
         * 色重心を計算する。指定色の中で、一番面積の広い所の重心を計算する。
         * @param [in] bitmapData 色重心を計算する元になるBitmapData
         * @param [out] gravPoint 重心の位置
         * @return 重心計算の元になるBitmapData
         */
        public static function getColorGravity( bitmapData:BitmapData, gravPoint:Point ) : BitmapData
        {
            /// 色のマスクを作成
            var maskBitmap:BitmapData = bitmapData.clone();

            /// 青系の色を透過色にする(ここの色の値を調整するとクロマキーで抜ける色が変わる)
            var THRESHOLD_RG_COLOR:uint = 0x444400;     ///< 赤、緑がこの値以上だったら透過色ではない
            var THRESHOLD_BLUE_COLOR:uint = 0x000060;   ///< 青がこの値以上でないと透過色ではない

            // まず赤、緑成分が一定値以上のところを透明に
            maskBitmap.threshold(maskBitmap, maskBitmap.rect, new Point(), ">", THRESHOLD_RG_COLOR, 0x00000000, 0x00ffff00 );
            // 次に青成分が、一定値以下のところを透明に
            maskBitmap.threshold(maskBitmap, maskBitmap.rect, new Point(), "<", THRESHOLD_BLUE_COLOR, 0x00000000, 0x000000ff );
            // 透明でないところをMASKFLAG_COLORで塗りつぶし(残ったところはかなり青いはず)
            maskBitmap.threshold(maskBitmap, maskBitmap.rect, new Point(), "!=", 0x00000000, MASKFLAG_COLOR);

            // ラベリング処理をする
            var labelArray:Array = new Array();
            var label:BitmapData = labeling(maskBitmap, labelArray);

            if(!labelArray.length){
                return null;
            }

            // 一番面積が大きかったラベルを取り出し
            var labelData:LabelData = labelArray[0];
            if(!labelData.dots){
                return null;
            }

            var gravData:BitmapData = extract(label, labelData.no);

            // 重心位置の計算
            var ax:uint = 0, ay:uint = 0;
            var rect:Rectangle = gravData.getColorBoundsRect( 0xFFFFFFFF, GRAVFLAG_COLOR, true );

            for(var y:int = rect.top; y < rect.bottom; ++y){
                for(var x:int = rect.left; x < rect.right; ++x){
                    if( gravData.getPixel32(x,y) == GRAVFLAG_COLOR ){
                        ax += x;
                        ay += y;
                    }
                }
            }

            gravPoint.x = ax/labelData.dots;
            gravPoint.y = ay/labelData.dots;

            return gravData;
        }
    }
}