/* 
 * PROJECT: NyARToolkitCS(Extension)
 * --------------------------------------------------------------------------------
 * The NyARToolkitCS is C# edition ARToolKit class library.
 * Copyright (C)2008-2009 Ryo Iizuka
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * For further information please contact.
 *	http://nyatla.jp/nyatoolkit/
 *	<airmail(at)ebony.plala.or.jp> or <nyatla(at)nyatla.jp>
 * 
 */
using System;
using System.Collections.Generic;
using System.Text;

namespace jp.nyatla.nyartoolkit.cs.core
{
    /**
     * 疑似アフィン変換を使用して、ラスタ上の四角形から任意解像度
     * の矩形パターンを作成します。
     *
     */
    public class NyARColorPatt_PseudoAffine : INyARColorPatt
    {
        private int[] _patdata;
        private NyARBufferReader _buf_reader;
        private NyARRgbPixelReader_INT1D_X8R8G8B8_32 _pixelreader;
        private NyARIntSize _size;

        public int getWidth()
        {
            return this._size.w;
        }

        public int getHeight()
        {
            return this._size.h;
        }

        public NyARIntSize getSize()
        {
            return this._size;
        }

        public INyARBufferReader getBufferReader()
        {
            return this._buf_reader;
        }

        public INyARRgbPixelReader getRgbPixelReader()
        {
            return this._pixelreader;
        }
        NyARDoubleMatrix44 _invmat = new NyARDoubleMatrix44();
        /**
         * @param i_width
         * @param i_height
         */
        public NyARColorPatt_PseudoAffine(int i_width, int i_height)
        {
            this._size = new NyARIntSize(i_width, i_height);
            this._patdata = new int[i_height * i_width];
            this._buf_reader = new NyARBufferReader(this._patdata, NyARBufferReader.BUFFERFORMAT_INT1D_X8R8G8B8_32);
            this._pixelreader = new NyARRgbPixelReader_INT1D_X8R8G8B8_32(this._patdata, this._size);
            //疑似アフィン変換のパラメタマトリクスを計算します。
            //長方形から計算すると、有効要素がm00,m01,m02,m03,m10,m11,m20,m23,m30になります。
            NyARDoubleMatrix44 mat = this._invmat;
            mat.m00 = 0;
            mat.m01 = 0;
            mat.m02 = 0;
            mat.m03 = 1.0;
            mat.m10 = 0;
            mat.m11 = i_width - 1;
            mat.m12 = 0;
            mat.m13 = 1.0;
            mat.m20 = (i_width - 1) * (i_height - 1);
            mat.m21 = i_width - 1;
            mat.m22 = i_height - 1;
            mat.m23 = 1.0;
            mat.m30 = 0;
            mat.m31 = 0;
            mat.m32 = i_height - 1;
            mat.m33 = 1.0;
            mat.inverse(mat);
            return;
        }

        /**
         * 変換行列と頂点座標から、パラメータを計算
         * o_paramの[0..3]にはXのパラメタ、[4..7]にはYのパラメタを格納する。
         * @param i_vertex
         * @param pa
         * @param pb
         */
        private void calcPara(NyARIntPoint2d[] i_vertex, double[] o_cparam)
        {
            NyARDoubleMatrix44 invmat = this._invmat;
            double v1, v2, v4;
            //変換行列とベクトルの積から、変換パラメタを計算する。
            v1 = i_vertex[0].x;
            v2 = i_vertex[1].x;
            v4 = i_vertex[3].x;

            o_cparam[0] = invmat.m00 * v1 + invmat.m01 * v2 + invmat.m02 * i_vertex[2].x + invmat.m03 * v4;
            o_cparam[1] = invmat.m10 * v1 + invmat.m11 * v2;//m12,m13は0;
            o_cparam[2] = invmat.m20 * v1 + invmat.m23 * v4;//m21,m22は0;
            o_cparam[3] = v1;//m30は1.0で、m31,m32,m33は0

            v1 = i_vertex[0].y;
            v2 = i_vertex[1].y;
            v4 = i_vertex[3].y;

            o_cparam[4] = invmat.m00 * v1 + invmat.m01 * v2 + invmat.m02 * i_vertex[2].y + invmat.m03 * v4;
            o_cparam[5] = invmat.m10 * v1 + invmat.m11 * v2;//m12,m13は0;
            o_cparam[6] = invmat.m20 * v1 + invmat.m23 * v4;//m21,m22は0;
            o_cparam[7] = v1;//m30は1.0で、m31,m32,m33は0
            return;
        }

        /**
         * 疑似アフィン変換の変換パラメタ
         */
        private double[] _convparam = new double[8];

        public bool pickFromRaster(INyARRgbRaster image, NyARSquare i_square)
        {
            double[] conv_param = this._convparam;
            int rx2, ry2;
            rx2 = this._size.w;
            ry2 = this._size.h;
            int[] rgb_tmp = new int[3];

            INyARRgbPixelReader reader = image.getRgbPixelReader();
            // 変形先領域の頂点を取得

            //変換行列から現在の座標系への変換パラメタを作成
            calcPara(i_square.imvertex, conv_param);// 変換パラメータを求める
            for (int y = 0; y < ry2; y++)
            {
                for (int x = 0; x < rx2; x++)
                {
                    int ttx = (int)((conv_param[0] * x * y + conv_param[1] * x + conv_param[2] * y + conv_param[3]) + 0.5);
                    int tty = (int)((conv_param[4] * x * y + conv_param[5] * x + conv_param[6] * y + conv_param[7]) + 0.5);
                    reader.getPixel((int)ttx, (int)tty, rgb_tmp);
                    this._patdata[x + y * rx2] = (rgb_tmp[0] << 16) | (rgb_tmp[1] << 8) | rgb_tmp[2];
                }
            }
            return true;
        }
    }
}
