Source: next2d/display/DisplayObject.js

next2d/display/DisplayObject.js

/**
 * DisplayObject クラスは、表示リストに含めることのできるすべてのオブジェクトに関する基本クラスです。
 * DisplayObject クラス自体は、画面上でのコンテンツの描画のための API を含みません。
 * そのため、DisplayObject クラスのカスタムサブクラスを作成する場合は、
 * Shape、Sprite、Bitmap、TextField または MovieClip など、
 * 画面上にコンテンツを描画する API を持つサブクラスの 1 つを拡張する必要があります。
 *
 * The DisplayObject class is the base class for all objects that can be placed on the display list.
 * The DisplayObject class itself does not include any APIs for rendering content onscreen.
 * For that reason, if you want create a custom subclass of the DisplayObject class,
 * you will want to extend one of its subclasses that do have APIs for rendering content onscreen,
 * such as the Shape, Sprite, Bitmap, TextField, or MovieClip class.
 *
 * @class
 * @memberOf next2d.display
 * @extends  EventDispatcher
 */
class DisplayObject extends EventDispatcher
{
    /**
     * @constructor
     * @public
     */
    constructor()
    {
        super();

        /**
         * @type {number}
         * @private
         */
        this._$id = -1;

        /**
         * @type {number}
         * @private
         */
        this._$instanceId = instanceId++;

        /**
         * @type {number}
         * @private
         */
        this._$dictionaryId = 0;

        /**
         * @type {number}
         * @private
         */
        this._$characterId = 0;

        /**
         * @type {boolean}
         * @default false
         * @private
         */
        this._$active = false;

        /**
         * @type {boolean}
         * @default false
         * @private
         */
        this._$isMask = false;

        /**
         * @type {null}
         * @default null
         * @private
         */
        this._$buffer = null;

        /**
         * @type {boolean}
         * @default false
         * @private
         */
        this._$updated = true;

        /**
         * @type {boolean}
         * @default false
         * @private
         */
        this._$added = false;

        /**
         * @type {boolean}
         * @default false
         * @private
         */
        this._$addedStage = false;

        /**
         * @type {array|null}
         * @default null
         * @private
         */
        this._$filters = null;

        /**
         * @type {string|null}
         * @default null
         * @private
         */
        this._$blendMode = null;

        /**
         * @type {Sprite|null}
         * @default null
         * @private
         */
        this._$hitObject = null;

        /**
         * @type {boolean}
         * @default true
         * @private
         */
        this._$isNext = true;

        /**
         * @type {number}
         * @default 0
         * @private
         */
        this._$clipDepth = 0;

        /**
         * @type {string}
         * @default ""
         * @private
         */
        this._$name = "";

        /**
         * @type {boolean}
         * @default true
         * @private
         */
        this._$visible = true;

        /**
         * @type {DisplayObject|null}
         * @default null
         * @private
         */
        this._$mask = null;

        /**
         * @type {Rectangle|null}
         * @default null
         * @private
         */
        this._$scale9Grid = null;

        /**
         * @type {DisplayObjectContainer|null}
         * @default null
         * @private
         */
        this._$parent = null;

        /**
         * @type {Stage|null}
         * @default null
         * @private
         */
        this._$stage = null;

        /**
         * @type {Sprite|null}
         * @default null
         * @private
         */
        this._$root = null;

        /**
         * @type {number|null}
         * @default null
         * @private
         */
        this._$loaderInfo = null;

        /**
         * @type {number|null}
         * @default null
         * @private
         */
        this._$placeId = null;

        /**
         * @type {number}
         * @default null
         * @private
         */
        this._$startFrame = 1;

        /**
         * @type {number}
         * @default 0
         * @private
         */
        this._$endFrame = 0;

        /**
         * @type {Transform}
         * @private
         */
        this._$transform = new Transform(this);

        /**
         * @type {Map}
         * @default null
         * @private
         */
        this._$variables = null;

        /**
         * @type {object}
         * @default null
         * @private
         */
        this._$placeObject = null;

        /**
         * @type {number}
         * @default -1
         * @private
         */
        this._$currentPlaceId = -1;

        /**
         * @type {boolean}
         * @default false
         * @private
         */
        this._$changePlace = false;
    }

    /**
     * @description 指定されたクラスのストリングを返します。
     *              Returns the string representation of the specified class.
     *
     * @return  {string}
     * @default [class DisplayObject]
     * @method
     * @static
     */
    static toString ()
    {
        return "[class DisplayObject]";
    }

    /**
     * @description 指定されたクラスの空間名を返します。
     *              Returns the space name of the specified class.
     *
     * @return  {string}
     * @default next2d.display.DisplayObject
     * @const
     * @static
     */
    static get namespace ()
    {
        return "next2d.display.DisplayObject";
    }

    /**
     * @description 指定されたオブジェクトのストリングを返します。
     *              Returns the string representation of the specified object.
     *
     * @return  {string}
     * @default [object DisplayObject]
     * @method
     * @public
     */
    toString ()
    {
        return "[object DisplayObject]";
    }

    /**
     * @description 指定されたオブジェクトの空間名を返します。
     *              Returns the space name of the specified object.
     *
     * @return  {string}
     * @default next2d.display.DisplayObject
     * @const
     * @public
     */
    get namespace ()
    {
        return "next2d.display.DisplayObject";
    }

    /**
     * @description 指定されたオブジェクトのアルファ透明度値を示します。
     *              有効な値は 0.0(完全な透明)~ 1.0(完全な不透明)です。
     *              デフォルト値は 1.0 です。alpha が 0.0 に設定されている表示オブジェクトは、
     *              表示されない場合でも、アクティブです。
     *              Indicates the alpha transparency value of the object specified.
     *              Valid values are 0.0 (fully transparent) to 1.0 (fully opaque).
     *              The default value is 1.0. Display objects with alpha set to 0.0 are active,
     *              even though they are invisible.
     *
     * @member  {number}
     * @default 1
     * @public
     */
    get alpha ()
    {
        const colorTransform = this._$transform._$rawColorTransform();
        return colorTransform[3] + colorTransform[7] / 255;
    }
    set alpha (alpha)
    {
        alpha = Util.$clamp(alpha, 0, 1, 0);

        // clone
        const colorTransform = this._$transform.colorTransform;

        colorTransform._$colorTransform[3] = alpha;
        colorTransform._$colorTransform[7] = 0;

        this._$transform.colorTransform = colorTransform;
        Util.$poolColorTransform(colorTransform);
    }

    /**
     * @description 使用するブレンドモードを指定する BlendMode クラスの値です。
     *              A value from the BlendMode class that specifies which blend mode to use.
     *
     * @member  {string}
     * @default BlendMode.NORMAL
     * @public
     */
    get blendMode ()
    {
        if (this._$blendMode) {
            return this._$blendMode;
        }

        const transform = this._$transform;
        if (transform._$blendMode) {
            this._$blendMode = transform._$blendMode;
            return this._$blendMode;
        }

        const placeObject = this._$getPlaceObject();
        if (placeObject) {
            this._$blendMode = placeObject.blendMode;
            return this._$blendMode;
        }

        // create Transform
        transform._$transform();
        this._$blendMode = transform._$blendMode;
        return this._$blendMode;
    }
    set blendMode (blend_mode)
    {
        this._$transform._$transform(null, null, null, blend_mode);
        this._$blendMode = blend_mode;
    }

    /**
     * @description 表示オブジェクトに現在関連付けられている各フィルターオブジェクトが
     *              格納されているインデックス付きの配列です。
     *              An indexed array that contains each filter object
     *              currently associated with the display object.
     *
     * @member  {array}
     * @default {array}
     * @public
     */
    get filters ()
    {
        if (this._$filters) {
            return this._$filters;
        }

        const transform = this._$transform;
        if (transform._$filters) {
            this._$filters = transform._$filters.slice(0);
            return this._$filters;
        }

        const placeObject = this._$getPlaceObject();
        if (placeObject) {

            // create filter
            if (!placeObject.filters) {

                const filters = [];

                if (placeObject.surfaceFilterList) {

                    const length = placeObject.surfaceFilterList.length;
                    for (let idx = 0; idx < length; ++idx) {

                        const filter = placeObject.surfaceFilterList[idx];

                        const filterClass = next2d.filters[filter.class];

                        filters.push(
                            new (filterClass.bind.apply(filterClass, filter.params))()
                        );

                    }

                }

                placeObject.filters = filters;
            }

            if (!this._$filters) {
                this._$filters = placeObject.filters ;
            }

            return placeObject.filters.slice(0);
        }

        transform._$transform();
        this._$filters = transform._$filters;
        return this._$filters.slice(0);
    }
    set filters (filters)
    {
        if (!filters) {
            filters = Util.$getArray();
        }

        this._$transform._$transform(null, null, filters, null);
        this._$filters = filters;
    }

    /**
     * @description 表示オブジェクトの高さを示します(ピクセル単位)。
     *              Indicates the height of the display object, in pixels.
     *
     * @member {number}
     * @public
     */
    get height ()
    {
        const bounds = Util.$boundsMatrix(
            this._$getBounds(null),
            this._$transform._$rawMatrix()
        );

        const height = $Math.abs(bounds.yMax - bounds.yMin);

        // object pool
        Util.$poolBoundsObject(bounds);

        switch (height) {

            case 0:
            case Util.$Infinity:
            case -Util.$Infinity:
                return 0;

            default:
                return height;

        }
    }
    set height (height)
    {
        height = +height;
        if (!Util.$isNaN(height) && height > -1) {

            const bounds = this.rotation
                ? Util.$boundsMatrix(this._$getBounds(null), this._$transform._$rawMatrix())
                : this._$getBounds(null);

            const exHeight = $Math.abs(bounds.yMax - bounds.yMin);
            Util.$poolBoundsObject(bounds);

            switch (exHeight) {

                case 0:
                case Util.$Infinity:
                case -Util.$Infinity:
                    this.scaleY = 0;
                    break;

                default:
                    this.scaleY = height / exHeight;
                    break;

            }
        }
    }

    /**
     * @description この表示オブジェクトが属するファイルの読み込み情報を含む LoaderInfo オブジェクトを返します。
     *              Returns a LoaderInfo object containing information
     *              about loading the file to which this display object belongs.
     *
     * @member   {LoaderInfo}
     * @readonly
     * @public
     */
    get loaderInfo ()
    {
        return this._$loaderInfo;
    }

    /**
     * @description 呼び出し元の表示オブジェクトは、指定された mask オブジェクトによってマスクされます。
     *              The calling display object is masked by the specified mask object.
     *
     * @member {DisplayObject|null}
     * @public
     */
    get mask ()
    {
        return this._$mask;
    }
    set mask (mask)
    {
        if (mask === this._$mask) {
            return ;
        }

        // reset
        if (this._$mask) {
            this._$mask._$isMask = false;
            this._$mask = null;
        }

        if (mask instanceof DisplayObject) {
            mask._$isMask = true;
            this._$mask   = mask;
        }

        this._$doChanged();
    }

    /**
     * @description マウスまたはユーザー入力デバイスの x 軸の位置をピクセルで示します。
     *              Indicates the x coordinate of the mouse or user input device position, in pixels.
     *
     * @member  {number}
     * @default 0
     * @readonly
     * @public
     */
    get mouseX ()
    {
        return Util.$event
            ? this.globalToLocal(Util.$currentMousePoint()).x
            : 0;
    }

    /**
     * @description マウスまたはユーザー入力デバイスの y 軸の位置をピクセルで示します。
     *              Indicates the y coordinate of the mouse or user input device position, in pixels.
     *
     * @member  {number}
     * @default 0
     * @readonly
     * @public
     */
    get mouseY ()
    {
        return Util.$event
            ? this.globalToLocal(Util.$currentMousePoint()).y
            : 0;
    }

    /**
     * @description DisplayObject のインスタンス名を示します。
     *              Indicates the instance name of the DisplayObject.
     *
     * @member {string}
     * @public
     */
    get name ()
    {
        if (this._$name) {
            return this._$name;
        }
        return `instance${this._$instanceId}`;
    }
    set name (name)
    {
        this._$name = `${name}`;

        const parent = this._$parent;
        if (parent && parent._$names) {

            parent._$names.clear();

            const children = parent._$getChildren();
            const length = children.length;
            for (let idx = 0; idx < length; ++idx) {
                const child = children[idx];
                if (child._$name) {
                    parent._$names.set(child.name, child);
                }
            }
        }
    }

    /**
     * @description この表示オブジェクトを含む DisplayObjectContainer オブジェクトを示します。
     *              Indicates the DisplayObjectContainer object that contains this display object.
     *
     * @member  {DisplayObjectContainer|null}
     * @readonly
     * @public
     */
    get parent ()
    {
        return this._$parent;
    }

    /**
     * @description 読み込まれた SWF ファイル内の表示オブジェクトの場合、
     *              root プロパティはその SWF ファイルが表す表示リストのツリー構造部分の一番上にある表示オブジェクトとなります。
     *              For a display object in a loaded SWF file,
     *              the root property is the top-most display object
     *              in the portion of the display list's tree structure represented by that SWF file.
     *
     * @member   {DisplayObject|null}
     * @readonly
     * @public
     */
    get root ()
    {
        return this._$root;
    }

    /**
     * @description DisplayObject インスタンスの元の位置からの回転角を度単位で示します。
     *              Indicates the rotation of the DisplayObject instance,
     *              in degrees, from its original orientation.
     *
     * @member {number}
     * @public
     */
    get rotation ()
    {
        const matrix = this._$transform._$rawMatrix();
        return $Math.atan2(matrix[1], matrix[0]) * Util.$Rad2Deg;
    }
    set rotation (rotation)
    {
        rotation = Util.$clamp(rotation % 360, -360, 360, 0);

        const transform = this._$transform;
        const matrix    = transform.matrix;

        const scaleX = $Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b);
        const scaleY = $Math.sqrt(matrix.c * matrix.c + matrix.d * matrix.d);
        if (rotation === 0) {

            matrix.a = scaleX;
            matrix.b = 0;
            matrix.c = 0;
            matrix.d = scaleY;

        } else {

            let radianX  = $Math.atan2(matrix.b,  matrix.a);
            let radianY  = $Math.atan2(-matrix.c, matrix.d);

            const radian = rotation * Util.$Deg2Rad;
            radianY      = radianY + radian - radianX;
            radianX      = radian;

            matrix.b = scaleX * $Math.sin(radianX);
            if (matrix.b === 1 || matrix.b === -1) {
                matrix.a = 0;
            } else {
                matrix.a = scaleX * $Math.cos(radianX);
            }

            matrix.c = -scaleY * $Math.sin(radianY);
            if (matrix.c === 1 || matrix.c === -1) {
                matrix.d = 0;
            } else {
                matrix.d = scaleY * $Math.cos(radianY);
            }
        }

        transform.matrix = matrix;
        Util.$poolMatrix(matrix);
    }

    /**
     * @description 現在有効な拡大 / 縮小グリッドです。
     *              The current scaling grid that is in effect.
     *
     * @member {Rectangle}
     * @public
     */
    get scale9Grid ()
    {
        return this._$scale9Grid;
    }
    set scale9Grid (scale_9_grid)
    {
        this._$scale9Grid = null;
        if (scale_9_grid instanceof Rectangle) {
            this._$scale9Grid = scale_9_grid;
        }
    }

    /**
     * @description 基準点から適用されるオブジェクトの水平スケール(パーセンテージ)を示します。
     *              Indicates the horizontal scale (percentage)
     *              of the object as applied from the registration point.
     *
     * @member {number}
     * @public
     */
    get scaleX ()
    {
        const matrix = this._$transform._$rawMatrix();
        const xScale = $Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]);
        return 0 > matrix[0] ? xScale * -1 : xScale;
    }
    set scaleX (scale_x)
    {
        const transform = this._$transform;
        const matrix    = transform.matrix;
        if (matrix.b === 0 || Util.$isNaN(matrix.b)) {

            matrix.a = scale_x;

        } else {

            const radianX = $Math.atan2(matrix.b, matrix.a);

            matrix.b = scale_x * $Math.sin(radianX);
            matrix.a = matrix.b === 1 || matrix.b === -1
                ? 0
                : scale_x * $Math.cos(radianX);

        }

        transform.matrix = matrix;
        Util.$poolMatrix(matrix);
    }

    /**
     * @description 基準点から適用されるオブジェクトの垂直スケール(パーセンテージ)を示します。
     *              IIndicates the vertical scale (percentage)
     *              of an object as applied from the registration point.
     *
     * @member {number}
     * @public
     */
    get scaleY ()
    {
        const matrix = this._$transform._$rawMatrix();
        const yScale = $Math.sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]);
        return 0 > matrix[3] ? yScale * -1 : yScale;
    }
    set scaleY (scale_y)
    {
        const transform = this._$transform;
        const matrix    = transform.matrix;

        if (matrix.c === 0 || Util.$isNaN(matrix.c)) {

            matrix.d = scale_y;

        } else {

            const radianY = $Math.atan2(-matrix.c, matrix.d);
            matrix.c = -scale_y * $Math.sin(radianY);
            matrix.d = matrix.c === 1 || matrix.c === -1
                ? 0
                : scale_y  * $Math.cos(radianY);

        }

        transform.matrix = matrix;
        Util.$poolMatrix(matrix);
    }

    /**
     * @description 表示オブジェクトのステージです。
     *              The Stage of the display object.
     *
     * @member   {Stage}
     * @readonly
     * @public
     */
    get stage ()
    {
        if (this._$stage) {
            return this._$stage;
        }

        // find parent
        const parent = this._$parent;
        if (parent) {

            if (parent instanceof Stage) {
                return parent;
            }

            return parent._$stage;
        }

        return null;
    }

    /**
     * @description 表示オブジェクトのマトリックス、カラー変換、
     *              ピクセル境界に関係するプロパティを持つオブジェクトです。
     *              An object with properties pertaining
     *              to a display object's matrix, color transform, and pixel bounds.
     *
     * @member {Transform}
     * @public
     */
    get transform ()
    {
        return this._$transform;
    }
    set transform (transform)
    {
        if (transform instanceof Transform) {
            this._$transform = transform;
        }
    }

    /**
     * @description 表示オブジェクトが可視かどうかを示します。
     *              Whether or not the display object is visible.
     *
     * @member {boolean}
     * @public
     */
    get visible ()
    {
        return this._$visible;
    }
    set visible (visible)
    {
        visible = !!visible;
        if (this._$visible !== visible) {
            this._$doChanged();
            Util.$isUpdated = true;
        }
        this._$visible = !!visible;
    }

    /**
     * @description 表示オブジェクトの幅を示します(ピクセル単位)。
     *              Indicates the width of the display object, in pixels.
     *
     * @member {number}
     * @public
     */
    get width ()
    {
        const bounds = Util.$boundsMatrix(
            this._$getBounds(null),
            this._$transform._$rawMatrix()
        );

        const width = $Math.abs(bounds.xMax - bounds.xMin);
        Util.$poolBoundsObject(bounds);

        switch (true) {

            case width === 0:
            case width === Util.$Infinity:
            case width === -Util.$Infinity:
                return 0;

            default:
                return width;

        }
    }
    set width (width)
    {
        width = +width;
        if (!Util.$isNaN(width) && width > -1) {

            const bounds = this.rotation
                ? Util.$boundsMatrix(this._$getBounds(null), this._$transform._$rawMatrix())
                : this._$getBounds(null);

            const exWidth = $Math.abs(bounds.xMax - bounds.xMin);
            Util.$poolBoundsObject(bounds);

            switch (true) {

                case exWidth === 0:
                case exWidth === Util.$Infinity:
                case exWidth === -Util.$Infinity:
                    this.scaleX = 0;
                    break;

                default:
                    this.scaleX = width / exWidth;
                    break;

            }
        }
    }

    /**
     * @description 親 DisplayObjectContainer のローカル座標を基準にした
     *              DisplayObject インスタンスの x 座標を示します。
     *              Indicates the x coordinate
     *              of the DisplayObject instance relative to the local coordinates
     *              of the parent DisplayObjectContainer.
     *
     * @member {number}
     * @public
     */
    get x ()
    {
        return this._$transform._$rawMatrix()[4];
    }
    set x (x)
    {
        const transform = this._$transform;

        const matrix = this._$transform.matrix;

        matrix.tx = x;

        transform.matrix = matrix;
        Util.$poolMatrix(matrix);
    }

    /**
     * @description 親 DisplayObjectContainer のローカル座標を基準にした
     *              DisplayObject インスタンスの y 座標を示します。
     *              Indicates the y coordinate
     *              of the DisplayObject instance relative to the local coordinates
     *              of the parent DisplayObjectContainer.
     *
     * @member {number}
     * @public
     */
    get y ()
    {
        return this._$transform._$rawMatrix()[5];
    }
    set y (y)
    {
        const transform = this._$transform;

        const matrix = transform.matrix;

        matrix.ty = y;

        transform.matrix = matrix;
        Util.$poolMatrix(matrix);
    }

    /**
     * @description targetCoordinateSpace オブジェクトの座標系を基準にして、
     *              表示オブジェクトの領域を定義する矩形を返します。
     *              Returns a rectangle that defines the area
     *              of the display object relative to the coordinate system
     *              of the targetCoordinateSpace object.
     *
     * @param  {DisplayObject} [target=null]
     * @return {Rectangle}
     */
    getBounds (target = null)
    {
        const baseBounds = this._$getBounds(null);

        const matrix = this._$transform.concatenatedMatrix;

        // to global
        const bounds = Util.$boundsMatrix(baseBounds, matrix._$matrix);

        // pool
        Util.$poolMatrix(matrix);
        Util.$poolBoundsObject(baseBounds);

        // create bounds object
        const targetBaseBounds = Util.$getBoundsObject(
            bounds.xMin,
            bounds.xMax,
            bounds.yMin,
            bounds.yMax
        );

        // pool
        Util.$poolBoundsObject(bounds);

        if (!target) {
            target = this;
        }

        const targetMatrix = target._$transform.concatenatedMatrix;
        targetMatrix.invert();

        const resultBounds = Util.$boundsMatrix(
            targetBaseBounds, targetMatrix._$matrix
        );

        const xMin = resultBounds.xMin;
        const yMin = resultBounds.yMin;
        const xMax = resultBounds.xMax;
        const yMax = resultBounds.yMax;

        // pool
        Util.$poolBoundsObject(targetBaseBounds);
        Util.$poolBoundsObject(resultBounds);
        Util.$poolMatrix(targetMatrix);

        return new Rectangle(
            xMin, yMin,
            $Math.abs(xMax - xMin),
            $Math.abs(yMax - yMin)
        );
    }

    /**
     * @description point オブジェクトをステージ(グローバル)座標から
     *              表示オブジェクトの(ローカル)座標に変換します。
     *              Converts the point object from the Stage (global) coordinates
     *              to the display object's (local) coordinates.
     *
     * @param  {Point} point
     * @return {Point}
     * @public
     */
    globalToLocal (point)
    {
        const matrix = this._$transform.concatenatedMatrix;
        matrix.invert();

        const newPoint = new Point(
            point.x * matrix.a + point.y * matrix.c + matrix.tx,
            point.x * matrix.b + point.y * matrix.d + matrix.ty
        );

        Util.$poolMatrix(matrix);

        return newPoint;
    }

    /**
     * @description 表示オブジェクトの境界ボックスを評価して、
     *              obj 表示オブジェクトの境界ボックスと重複または交差するかどうかを調べます。
     *              Evaluates the bounding box of the display object to see
     *              if it overlaps or intersects with the bounding box of the obj display object.
     *
     * @param   {DisplayObject} object
     * @returns {boolean}
     * @public
     */
    hitTestObject (object)
    {
        const baseBounds1 = this._$getBounds(null);
        const matrix1 = this._$transform.concatenatedMatrix;
        const bounds1 = Util.$boundsMatrix(baseBounds1, matrix1._$matrix);

        // pool
        Util.$poolMatrix(matrix1);
        Util.$poolBoundsObject(baseBounds1);

        const baseBounds2 = object._$getBounds(null);
        const matrix2 = object._$transform.concatenatedMatrix;
        const bounds2 = Util.$boundsMatrix(baseBounds2, matrix2._$matrix);

        // pool
        Util.$poolMatrix(matrix2);
        Util.$poolBoundsObject(baseBounds2);

        // calc
        const sx = $Math.max(bounds1.xMin, bounds2.xMin);
        const sy = $Math.max(bounds1.yMin, bounds2.yMin);
        const ex = $Math.min(bounds1.xMax, bounds2.xMax);
        const ey = $Math.min(bounds1.yMax, bounds2.yMax);

        // pool
        Util.$poolBoundsObject(bounds1);
        Util.$poolBoundsObject(bounds2);

        return ex - sx >= 0 && ey - sy >= 0;
    }

    /**
     * @description 表示オブジェクトを評価して、x および y パラメーターで指定された
     *              ポイントと重複または交差するかどうかを調べます。
     *              Evaluates the display object to see if it overlaps
     *              or intersects with the point specified by the x and y parameters.
     *
     * @param   {number}  x
     * @param   {number}  y
     * @param   {boolean} [shape_flag=false]
     * @returns {boolean}
     * @public
     */
    hitTestPoint (x, y, shape_flag = false)
    {
        if (shape_flag) {

            let matrix = Util.$MATRIX_ARRAY_IDENTITY;
            let parent = this._$parent;

            while (parent) {

                matrix = Util.$multiplicationMatrix(
                    parent._$transform._$rawMatrix(),
                    matrix
                );

                parent = parent._$parent;
            }

            Util.$hitContext.setTransform(1, 0, 0, 1, 0, 0);
            Util.$hitContext.beginPath();
            const result = this._$hit(Util.$hitContext, matrix, { "x": x, "y": y }, true);

            Util.$poolFloat32Array6(matrix);

            return result;
        }

        const baseBounds = this._$getBounds(null);
        const bounds = Util.$boundsMatrix(baseBounds, this._$transform._$rawMatrix());

        const rectX = bounds.xMin;
        const rectY = bounds.yMin;
        const rectW = bounds.xMax - bounds.xMin;
        const rectH = bounds.yMax - bounds.yMin;

        const point = this._$parent
            ? this._$parent.globalToLocal(new Point(x, y))
            : new Point(x, y);

        // pool
        Util.$poolBoundsObject(bounds);
        Util.$poolBoundsObject(baseBounds);

        return new Rectangle(rectX, rectY, rectW, rectH).containsPoint(point);
    }

    /**
     * @description point オブジェクトを表示オブジェクトの(ローカル)座標から
     *              ステージ(グローバル)座標に変換します。
     *              Converts the point object from the display object's (local) coordinates
     *              to the Stage (global) coordinates.
     *
     *
     * @param   {Point} point
     * @returns {Point}
     * @public
     */
    localToGlobal (point)
    {
        const matrix = this
            ._$transform
            .concatenatedMatrix;

        const newPoint = new Point(
            point.x * matrix.a + point.y * matrix.c + matrix.tx,
            point.x * matrix.b + point.y * matrix.d + matrix.ty
        );

        Util.$poolMatrix(matrix);

        return newPoint;
    }

    /**
     * @description クラスのローカル変数空間から値を取得
     *              Get a value from the local variable space of the class
     *
     * @param  {*} key
     * @return {*}
     * @method
     * @public
     */
    getLocalVariable (key)
    {
        if (!this._$variables) {
            return null;
        }

        if (this._$variables.has(key)) {
            return this._$variables.get(key);
        }
    }

    /**
     * @description クラスのローカル変数空間へ値を保存
     *              Store values in the local variable space of the class
     *
     * @param  {*} key
     * @param  {*} value
     * @return {void}
     * @method
     * @public
     */
    setLocalVariable (key, value)
    {
        if (!this._$variables) {
            this._$variables = Util.$getMap();
        }
        this._$variables.set(key, value);
    }

    /**
     * @description クラスのローカル変数空間に値があるかどうかを判断します。
     *              Determines if there is a value in the local variable space of the class.
     *
     * @param  {*} key
     * @return {boolean}
     * @method
     * @public
     */
    hasLocalVariable (key)
    {
        return this._$variables
            ? this._$variables.has(key)
            : false;
    }

    /**
     * @description クラスのローカル変数空間の値を削除
     *              Remove values from the local variable space of a class
     *
     * @param  {*} key
     * @return {void}
     * @method
     * @public
     */
    deleteLocalVariable (key)
    {
        if (this._$variables && this._$variables.has(key)) {
            this._$variables.delete(key);
            if (!this._$variables.size) {
                Util.$poolMap(this._$variables);
                this._$variables = null;
            }
        }
    }

    /**
     * @description グローバル変数空間から値を取得
     *              Get a value from the global variable space
     *
     * @param  {*} key
     * @return {*}
     * @method
     * @public
     */
    getGlobalVariable (key)
    {
        if (Util.$variables.has(key)) {
            return Util.$variables.get(key);
        }
        return null;
    }

    /**
     * @description グローバル変数空間へ値を保存
     *              Save values to global variable space
     *
     * @param  {*} key
     * @param  {*} value
     * @return {void}
     * @method
     * @public
     */
    setGlobalVariable (key, value)
    {
        Util.$variables.set(key, value);
    }

    /**
     * @description グローバル変数空間に値があるかどうかを判断します。
     *              Determines if there is a value in the global variable space.
     *
     * @param  {*} key
     * @return {boolean}
     * @method
     * @public
     */
    hasGlobalVariable (key)
    {
        return Util.$variables.has(key);
    }

    /**
     * @description グローバル変数空間の値を削除
     *              Remove values from global variable space.
     *
     * @param  {*} key
     * @return {void}
     * @method
     * @public
     */
    deleteGlobalVariable (key)
    {
        if (Util.$variables.has(key)) {
            Util.$variables.delete(key);
        }
    }

    /**
     * @description グローバル変数空間に値を全てクリアします。
     *              Clear all values in the global variable space.
     *
     * @return {void}
     * @method
     * @public
     */
    clearGlobalVariable ()
    {
        return Util.$variables.clear();
    }

    /**
     * @return {object}
     * @method
     * @private
     */
    _$getPlaceObject ()
    {
        if (!this._$placeObject) {

            const index = this._$placeId;
            if (index === null) {
                return null;
            }

            const parent = this._$parent;
            if (!parent) {
                return null;
            }

            const placeMap = parent._$placeMap;
            if (!placeMap || !placeMap.length) {
                return null;
            }

            const map = placeMap[parent._$currentFrame || 1];
            if (!map) {
                return null;
            }

            const currentPlaceId  = map[index];
            this._$changePlace    = currentPlaceId !== this._$currentPlaceId;
            this._$currentPlaceId = currentPlaceId;
            this._$placeObject    = parent._$placeObjects[currentPlaceId];
        }
        return this._$placeObject;
    }

    /**
     * @return {object}
     * @method
     * @private
     */
    _$sync ()
    {
        const name = this.contentName;

        let loaderInfo = null;
        if (next2d.fw.response.has(name)) {
            loaderInfo = next2d.fw.response.get(name)._$loaderInfo;
        }

        if (!loaderInfo && next2d.fw.cache.has(name)) {
            loaderInfo = next2d.fw.cache.get(name)._$loaderInfo;
        }

        if (!loaderInfo) {
            loaderInfo = this._$loaderInfo || Util.$currentLoaderInfo;
        }

        if (!loaderInfo) {
            return null;
        }

        const characterId  = loaderInfo._$data.symbols.get(this.namespace);
        const character    = loaderInfo._$data.characters[characterId];

        this._$characterId = characterId;
        this._$loaderInfo  = loaderInfo;

        return character;
    }

    /**
     * @param  {object} tag
     * @param  {DisplayObjectContainer} parent
     * @return {object}
     * @method
     * @private
     */
    _$build (tag, parent)
    {
        const loaderInfo = parent._$loaderInfo;

        // setup
        this._$parent     = parent;
        this._$root       = parent._$root;
        this._$stage      = parent._$stage;
        this._$loaderInfo = loaderInfo;

        // bind tag data
        this._$characterId = tag.characterId | 0;
        this._$clipDepth   = tag.clipDepth | 0;
        this._$startFrame  = tag.startFrame | 0;
        this._$endFrame    = tag.endFrame | 0;
        this._$name        = tag.name || "";

        return loaderInfo._$data.characters[tag.characterId];
    }

    /**
     * @return {boolean}
     * @method
     * @private
     */
    _$isUpdated ()
    {
        return this._$updated;
    }

    /**
     * @return {void}
     * @method
     * @private
     */
    _$updateState ()
    {
        this._$isNext = true;

        let parent = this._$parent;
        if (parent) {
            parent._$updateState();
        }
    }

    /**
     * @return {object}
     * @method
     * @private
     */
    _$doChanged ()
    {
        this._$isNext  = true;
        this._$updated = true;

        let parent = this._$parent;
        if (parent) {
            parent._$doChanged();
        }
    }

    /**
     * @param  {CanvasToWebGLContext} context
     * @param  {WebGLTexture}         target_texture
     * @param  {Float32Array}         matrix
     * @param  {array}                filters
     * @param  {number}               width
     * @param  {number}               height
     * @return {WebGLTexture}
     * @method
     * @private
     */
    _$drawFilter (
        context, target_texture, matrix,
        filters, width, height
    ) {

        const cacheKeys = [this._$instanceId, "f"];
        let cache = Util.$cacheStore().get(cacheKeys);

        const updated = this._$isFilterUpdated(
            width, height, matrix, filters, true
        );

        let texture;
        if (!cache || updated) {

            // cache clear
            if (cache) {

                Util.$cacheStore().set(cacheKeys, null);
                cache.layerWidth     = 0;
                cache.layerHeight    = 0;
                cache._$offsetX      = 0;
                cache._$offsetY      = 0;
                cache.matrix         = null;
                cache.colorTransform = null;

                context
                    .frameBuffer
                    .releaseTexture(cache);

                cache = null;
            }

            texture = this._$applyFilter(
                context, filters, target_texture,
                matrix, width, height
            );

            Util.$cacheStore().set(cacheKeys, texture);
        }

        if (cache) {
            texture = cache;
        }

        return texture;
    }

    /**
     * @param   {array}  [matrix=null]
     * @returns {object}
     * @private
     */
    _$getLayerBounds (matrix = null)
    {
        const baseBounds = this._$getBounds(matrix);
        if (!matrix) {
            return baseBounds;
        }

        const filters = this._$filters || this.filters;
        const length = filters.length;
        if (!length) {
            return baseBounds;
        }

        let rect = new Rectangle(
            baseBounds.xMin,
            baseBounds.yMin,
            baseBounds.xMax - baseBounds.xMin,
            baseBounds.yMax - baseBounds.yMin
        );
        Util.$poolBoundsObject(baseBounds);
        for (let idx = 0; idx < length; ++idx) {
            rect = filters[idx]._$generateFilterRect(rect, null, null, true);
        }

        const xMin = rect._$x;
        const xMax = rect._$x + rect._$width;
        const yMin = rect._$y;
        const yMax = rect._$y + rect._$height;

        return Util.$getBoundsObject(xMin, xMax, yMin, yMax);
    }

    /**
     * @return {void}
     * @method
     * @private
     */
    _$executeAddedEvent ()
    {
        if (!this._$parent) {
            return ;
        }

        // add event
        if (!this._$added) {

            // added event
            if (this.willTrigger(Event.ADDED)) {
                this.dispatchEvent(new Event(Event.ADDED, true));
            }

            // update
            this._$added = true;
        }

        if (!this._$addedStage && this._$stage !== null) {

            if (this.willTrigger(Event.ADDED_TO_STAGE)) {
                this.dispatchEvent(new Event(Event.ADDED_TO_STAGE));
            }

            // update
            this._$addedStage = true;
        }
    }

    /**
     * @return {void}
     * @method
     * @private
     */
    _$prepareActions ()
    {
        this._$nextFrame();
    }

    /**
     * @return {boolean}
     * @method
     * @private
     */
    _$nextFrame ()
    {
        // added event
        this._$executeAddedEvent();

        this._$isNext = false;

        return false;
    }

    /**
     * @param  {array} [filters=null]
     * @return {boolean}
     * @private
     */
    _$canApply (filters = null)
    {
        if (filters) {
            for (let idx = 0; idx < filters.length; ++idx) {
                if (filters[idx]._$canApply()) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * @param  {number}       width
     * @param  {number}       height
     * @param  {Float32Array} matrix
     * @param  {array}        [filters=null]
     * @param  {boolean}      [can_apply=false]
     * @param  {number}       [position_x=0]
     * @param  {number}       [position_y=0]
     * @return {boolean}
     * @private
     */
    _$isFilterUpdated (
        width, height, matrix,
        filters = null, can_apply = false,
        position_x = 0, position_y = 0
    ) {

        // cache flag
        let updated = this._$isUpdated();
        if (updated) {
            return true;
        }

        // check filter data
        if (can_apply) {

            for (let idx = 0; idx < filters.length; ++idx) {

                if (filters[idx]._$isUpdated()) {
                    return true;
                }

            }

        }

        // check status
        const cache = Util.$cacheStore().get([this._$instanceId, "f"]);
        switch (true) {

            case cache === null:
            case cache.filterState !== can_apply:
            case cache.layerWidth  !== $Math.ceil(width):
            case cache.layerHeight !== $Math.ceil(height):
            case cache.matrix !==
            matrix[0] + "_" + matrix[1] + "_" + matrix[2] + "_" + matrix[3] + "_" +
            position_x + "_" + position_y:
                return true;

            default:
                break;

        }

        return false;
    }

    /**
     * @param  {CanvasToWebGLContext} context
     * @param  {array} filters
     * @param  {WebGLTexture} target_texture
     * @param  {Float32Array} matrix
     * @param  {number} width
     * @param  {number} height
     * @return {WebGLTexture}
     * @private
     */
    _$applyFilter (
        context, filters, target_texture,
        matrix, width, height
    ) {

        const currentAttachment = context
            .frameBuffer
            .currentAttachment;

        const buffer = context
            .frameBuffer
            .createCacheAttachment(width, height);

        context._$bind(buffer);

        Util.$resetContext(context);

        const radianX = $Math.atan2(matrix[1], matrix[0]);
        const radianY = $Math.atan2(-matrix[2], matrix[3]);
        if (radianX || radianY) {

            const w = target_texture.width  / 2;
            const h = target_texture.height / 2;

            const a = $Math.cos(radianX);
            const b = $Math.sin(radianX);
            const c = -$Math.sin(radianY);
            const d = $Math.cos(radianY);

            const baseMatrix = Util.$getFloat32Array6(
                1, 0, 0, 1, -w, -h
            );
            const parentMatrix = Util.$getFloat32Array6(
                a, b, c, d,
                (width  - target_texture.width)  / 2,
                (height - target_texture.height) / 2
            );
            const multiMatrix = Util.$multiplicationMatrix(
                parentMatrix, baseMatrix
            );

            context.setTransform(a, b, c, d,
                multiMatrix[4] + w,
                multiMatrix[5] + h
            );

            // pool
            Util.$poolFloat32Array6(baseMatrix);
            Util.$poolFloat32Array6(parentMatrix);
            Util.$poolFloat32Array6(multiMatrix);

        } else {

            context.setTransform(1, 0, 0, 1, 0, 0);

        }

        context.drawImage(target_texture,
            0, 0, target_texture.width, target_texture.height
        );

        // init
        context._$offsetX = 0;
        context._$offsetY = 0;

        let texture = null;
        for (let idx = 0; idx < filters.length; ++idx) {
            texture = filters[idx]._$applyFilter(context, matrix);
        }

        let offsetX = context._$offsetX;
        let offsetY = context._$offsetY;

        // reset
        context._$offsetX = 0;
        context._$offsetY = 0;

        // set offset
        texture._$offsetX = offsetX;
        texture._$offsetY = offsetY;

        // cache texture
        texture.matrix =
              matrix[0] + "_" + matrix[1] + "_"
            + matrix[2] + "_" + matrix[3] + "_0_0";

        texture.filterState = true;
        texture.layerWidth  = width;
        texture.layerHeight = height;

        context._$bind(currentAttachment);
        context
            .frameBuffer
            .releaseAttachment(buffer, false);

        return texture;
    }

    /**
     * @param  {CanvasToWebGLContext} context
     * @param  {Float32Array} matrix
     * @return {object}
     * @private
     */
    _$preDraw (context, matrix)
    {
        const originMatrix = this._$transform._$rawMatrix();
        const tMatrix = Util.$multiplicationMatrix(matrix, originMatrix);

        // size zero
        if (!tMatrix[0] && !tMatrix[1] || !tMatrix[2] && !tMatrix[3]) {
            return false;
        }

        // return object
        const object = Util.$getPreObject();

        // setup
        object.matrix = tMatrix;

        // check
        const filters   = this._$filters   || this.filters;
        const blendMode = this._$blendMode || this.blendMode;
        if (filters.length > 0 || blendMode !== BlendMode.NORMAL) {

            // check size
            const baseBounds = this._$getBounds(null);
            const bounds = Util.$boundsMatrix(baseBounds, tMatrix);
            const xMax   = +bounds.xMax;
            const xMin   = +bounds.xMin;
            const yMax   = +bounds.yMax;
            const yMin   = +bounds.yMin;

            // pool
            Util.$poolBoundsObject(baseBounds);
            Util.$poolBoundsObject(bounds);

            const width  = $Math.abs(xMax - xMin);
            const height = $Math.abs(yMax - yMin);
            if (0 >= width || 0 >= height) {
                return false;
            }

            if (0 > xMin + width || 0 > yMin + height) {
                return false;
            }

            const currentAttachment = context
                .frameBuffer
                .currentAttachment;
            if (xMin > currentAttachment.width
                || yMin > currentAttachment.height
            ) {
                return false;
            }

            // set origin position
            object.basePosition.x = originMatrix[4];
            object.basePosition.y = originMatrix[5];

            // check after size
            let baseLayerBounds = this._$getLayerBounds(null);
            const layerBounds = Util.$boundsMatrix(baseLayerBounds, tMatrix);

            // filter size
            let layerWidth  = $Math.abs(layerBounds.xMax - layerBounds.xMin);
            let layerHeight = $Math.abs(layerBounds.yMax - layerBounds.yMin);
            Util.$poolBoundsObject(layerBounds);

            if (layerWidth === width && layerHeight === height) {
                Util.$poolBoundsObject(baseLayerBounds);
                baseLayerBounds = null;
            }

            // move size
            let tx = tMatrix[4] - $Math.floor(xMin);
            let ty = tMatrix[5] - $Math.floor(yMin);

            let moveBounds = null;
            if (baseLayerBounds) {

                const layerMatrix = Util.$getFloat32Array6(
                    tMatrix[0], tMatrix[1], tMatrix[2], tMatrix[3], 0, 0
                );
                moveBounds = Util.$boundsMatrix(baseLayerBounds, layerMatrix);

                // pool
                Util.$poolBoundsObject(baseLayerBounds);
                Util.$poolFloat32Array6(layerMatrix);

                tx += -$Math.floor(moveBounds.xMin) - tx;
                ty += -$Math.floor(moveBounds.yMin) - ty;
            }

            let dx = $Math.floor(xMin);
            let dy = $Math.floor(yMin);
            let originX = xMin;
            let originY = yMin;

            if (moveBounds) {
                dx -= -$Math.floor(moveBounds.xMin) - (tMatrix[4] - dx);
                dy -= -$Math.floor(moveBounds.yMin) - (tMatrix[5] - dy);

                originX -= -moveBounds.xMin - (tMatrix[4] - originX);
                originY -= -moveBounds.yMin - (tMatrix[5] - originY);

                Util.$poolBoundsObject(moveBounds);
            }

            // set position
            object.position.dx = dx > 0 ? dx : 0;
            object.position.dy = dy > 0 ? dy : 0;

            // resize
            if (layerWidth + originX > currentAttachment.texture.width) {
                layerWidth -= layerWidth - currentAttachment.texture.width + originX;
            }

            if (layerHeight + originY > currentAttachment.texture.height) {
                layerHeight -= layerHeight - currentAttachment.texture.height + originY;
            }

            if (0 > dx) {
                tx += dx;
                layerWidth += originX;
            }

            if (0 > dy) {
                ty += dy;
                layerHeight += originY;
            }

            if (0 >= layerWidth || 0 >= layerHeight // size (-)
                || !layerWidth || !layerHeight // NaN or Infinity
            ) {
                Util.$poolPreObject(object);
                return false;
            }

            // start layer
            context._$startLayer(
                Util.$getBoundsObject(originX, 0, originY, 0)
            );

            // check cache
            object.canApply = this._$canApply(filters);
            let updated = this._$isFilterUpdated(
                layerWidth, layerHeight, tMatrix, filters,
                object.canApply, object.basePosition.x, object.basePosition.y
            );

            // cache
            const currentMaskBuffer = context._$cacheCurrentBuffer;
            context._$cacheCurrentBuffer = null;

            const rect = context._$cacheCurrentBounds;
            const currentMaskBounds = Util.$getBoundsObject(rect.x, rect.w, rect.y, rect.h);

            if (updated) {
                this._$buffer = context
                    .frameBuffer
                    .createCacheAttachment(
                        $Math.ceil(layerWidth),
                        $Math.ceil(layerHeight),
                        false
                    );
                context._$bind(this._$buffer);
            }

            // setup
            object.isFilter          = true;
            object.isUpdated         = updated;
            object.color             = Util.$getFloat32Array8();
            object.baseMatrix        = tMatrix;
            object.currentAttachment = currentAttachment;
            object.currentMaskBuffer = currentMaskBuffer;
            object.currentMaskBounds = currentMaskBounds;
            object.filters           = filters;
            object.blendMode         = blendMode;
            object.layerWidth        = layerWidth;
            object.layerHeight       = layerHeight;
            object.matrix            = Util.$getFloat32Array6(
                tMatrix[0], tMatrix[1], tMatrix[2], tMatrix[3], tx, ty
            );
        }

        return object;
    }

    /**
     * @param  {CanvasToWebGLContext} context
     * @param  {Float32Array} matrix
     * @param  {Float32Array} color_transform
     * @param  {object} object
     * @return {void}
     * @method
     * @private
     */
    _$postDraw (context, matrix, color_transform, object)
    {

        // cache
        const cacheKeys = [this._$instanceId, "f"];

        // cache or new texture
        let texture = null;
        if (this._$buffer) {

            texture = context
                .frameBuffer
                .getTextureFromCurrentAttachment();

            const cacheTexture = Util.$cacheStore().get(cacheKeys);
            if (cacheTexture) {
                Util.$cacheStore().set(cacheKeys, null);
                context
                    .frameBuffer
                    .releaseTexture(cacheTexture);
            }

        } else {
            texture = Util.$cacheStore().get(cacheKeys);
        }

        // blend only
        if (!object.canApply) {
            texture._$offsetX = 0;
            texture._$offsetY = 0;
        }

        // set cache offset
        let offsetX = texture._$offsetX;
        let offsetY = texture._$offsetY;

        // execute filter
        if (object.isUpdated && object.canApply) {

            // cache clear
            let cache = Util.$cacheStore().get(cacheKeys);
            if (cache) {

                // reset cache params
                Util.$cacheStore().set(cacheKeys, null);
                cache.layerWidth     = 0;
                cache.layerHeight    = 0;
                cache._$offsetX      = 0;
                cache._$offsetY      = 0;
                cache.matrix         = null;
                cache.colorTransform = null;
                context.frameBuffer.releaseTexture(cache);

                cache  = null;
            }

            // apply filter
            const length = object.filters.length;
            if (length) {

                // init
                context._$offsetX = 0;
                context._$offsetY = 0;

                for (let idx = 0; idx < length; ++idx) {
                    texture = object.filters[idx]._$applyFilter(context, matrix);
                }

                offsetX = context._$offsetX;
                offsetY = context._$offsetY;

                // reset
                context._$offsetX = 0;
                context._$offsetY = 0;

                // set offset
                texture._$offsetX = offsetX;
                texture._$offsetY = offsetY;

            }
        }

        // update cache params
        if (object.isUpdated) {

            texture.filterState = object.canApply;

            // cache texture
            const mat = object.baseMatrix;
            texture.matrix = mat[0] + "_" + mat[1] + "_" + mat[2] + "_" + mat[3]
                + "_" + object.basePosition.x + "_" + object.basePosition.y;

            texture.layerWidth  = object.layerWidth;
            texture.layerHeight = object.layerHeight;
        }

        // cache texture
        Util.$cacheStore().set(cacheKeys, texture);
        Util.$poolArray(cacheKeys);

        // set current buffer
        context._$bind(object.currentAttachment);

        // setup
        const width  = texture.width;
        const height = texture.height;

        // set
        Util.$resetContext(context);

        context._$globalAlpha = Util.$clamp(color_transform[3] + color_transform[7] / 255, 0, 1);
        context._$globalCompositeOperation = object.blendMode;

        context.setTransform(1, 0, 0, 1, 0, 0);
        context.drawImage(texture,
            -offsetX + object.position.dx,
            -offsetY + object.position.dy,
            width, height,
            color_transform
        );

        // end blend
        context._$endLayer();

        // pool buffer
        if (this._$buffer) {

            context
                .frameBuffer
                .releaseAttachment(this._$buffer, false);

            this._$buffer = null;
        }

        // reset
        context._$cacheCurrentBuffer   = object.currentMaskBuffer;
        context._$cacheCurrentBounds.x = object.currentMaskBounds.xMin;
        context._$cacheCurrentBounds.y = object.currentMaskBounds.yMin;
        context._$cacheCurrentBounds.w = object.currentMaskBounds.xMax;
        context._$cacheCurrentBounds.h = object.currentMaskBounds.yMax;

        // object pool
        Util.$poolFloat32Array8(object.color);
        Util.$poolFloat32Array6(object.matrix);
        Util.$poolFloat32Array6(object.baseMatrix);
        Util.$poolBoundsObject(object.currentMaskBounds);
        Util.$poolPreObject(object);
    }

    /**
     * @param  {Float32Array} matrix
     * @return {boolean}
     * @method
     * @private
     */
    _$shouldClip (matrix)
    {
        if (this instanceof TextField) {
            if (!this.textWidth || !this.textHeight) {
                return false;
            }
            return true;
        }

        const bounds = this._$getBounds(matrix);
        const width  = $Math.abs(bounds.xMax - bounds.xMin);
        const height = $Math.abs(bounds.yMax - bounds.yMin);
        Util.$poolBoundsObject(bounds);

        // size 0
        if (!width || !height) {
            return false;
        }
        return true;
    }

    /**
     * @param  {CanvasToWebGLContext} context
     * @param  {Float32Array} matrix
     * @return {Float32Array|boolean|null}
     * @method
     * @private
     */
    _$startClip (context, matrix)
    {
        let clipMatrix = null;

        // ネストしてない初回のマスクだけ実行
        // ネストしてる場合は初回に作られたbufferを流用
        if (!context._$cacheCurrentBuffer) {

            clipMatrix = context._$startClip(this, matrix);
            if (!clipMatrix) {
                return false;
            }

        }

        // start clip
        context._$enterClip();

        // mask start
        context._$beginClipDef();

        if (this instanceof DisplayObjectContainer) {
            context._$mask._$containerClip = true;
        }

        this._$clip(context, clipMatrix || matrix);
        this._$updated = false;

        // container clip
        if (context._$mask._$containerClip) {

            // update flag
            context._$mask._$containerClip = false;

            // execute clip
            context._$drawContainerClip();
        }

        // mask end
        context._$endClipDef();

        return clipMatrix;
    }
}