import Texture from "../texture/Texture";
import { SCALE_MODES, LINE_CAP, LINE_JOIN, RENDERER_TYPE } from "../const";
import { Rectangle, Point } from "../math";
import { DisplayObject } from "../display/DisplayObject";
import { hex2string, getRGBA, getGradientColor, getBackupCanvasCtx, getCanvasBitmapStyle, createCanvas, string2hex } from "../utils";
import Sprite from "../display/Sprite";

/**
 * 继承sprite，hittest重写
 * 矢量对象，
 * 记录指令，对于复杂的不常绘制的图形，用canvas上下文的指令比较方便，比如消消乐项目时的格子地图
 * 复杂路径，自己没法确定哪些区域该填充的时候，用canvas的api
 * 支持pattern和渐变填充，与Graphic的纹理填充区分，不一致
 * anchorTexture被使用，尽量不手动修改，如要改锚点，用anchor
 * 作为Webgl模式的遮罩时，需要注意：
 * 遮罩绘制指令不变的且有矩阵变化的（比如自身或父级的位置有改变），需要shape._boundsID++，为了重新计算包围盒
 * @class Shape
 * @extends Sprite
 */
export class Shape extends Sprite {
    canvas: HTMLCanvasElement;
    context: CanvasRenderingContext2D;
    dirty: boolean;
    offsetX: number;
    offsetY: number;
    constructor() {
        super();
        this._instanceType = "Shape";
        var canvas = createCanvas();
        canvas.width = 3;
        canvas.height = 3;
        this.texture = Texture.fromCanvas(canvas, 'shapeCanvas');

        //baseTexture已自动缓存，把texture也缓存，key textCanvas+num  和baseTexture的一致
        Texture.addToCache(this.texture, this.texture.baseTexture.textureCacheIds[0]);
        this.canvas = canvas;
        this.context = canvas.getContext("2d");
        this.dirty = true;

    }

    /**
     * 一个数组，每个元素也是一个数组[类型 0是属性,1是方法,名字 执行的属性或方法名,参数]
     * 
     * @property _command
     * @private
     * @since 1.0.0
     * @type {Array}
     * @default []
     */
    private _command: any = [];

    /**
     * @property _isBitmapStroke
     * @private
     * @since 1.0.0
     */
    private _isBitmapStroke: Array<number>;
    /**
     * @property _isBitmapFill
     * @private
     * @since 1.0.0
     */
    private _isBitmapFill: Array<number>;
    /**
     * 是否对矢量使用像素碰撞 默认开启
     * @property hitTestByPixel
     * @type {boolean}
     * @default true
     * @since 1.1.0
     */
    public hitTestByPixel: boolean = true;


    /**
     * 画一个带圆角的矩形
     * @method drawRoundedRect
     * @param {number} x 点x值
     * @param {number} y 点y值
     * @param {number} w 宽
     * @param {number} h 高
     * @param {number} rTL 左上圆角半径
     * @param {number} rTR 右上圆角半径
     * @param {number} rBL 左下圆角半径
     * @param {number} rBR 右上圆角半径
     * @public
     * @since 1.0.0
     */
    public drawRoundedRect(
        x: number,
        y: number,
        w: number,
        h: number,
        rTL: number = 0,
        rTR?: number,
        rBL?: number,
        rBR?: number
    ): this {
        let max = (w < h ? w : h) / 2;
        let mTL = 0, mTR = 0, mBR = 0, mBL = 0;
        if (rTR == undefined) rTR = rTL;
        if (rBL == undefined) rBL = rTL;
        if (rBR == undefined) rBR = rTL;
        if (rTL < 0) {
            rTL *= (mTL = -1);
        }
        if (rTL > max) {
            rTL = max;
        }
        if (rTR < 0) {
            rTR *= (mTR = -1);
        }
        if (rTR > max) {
            rTR = max;
        }
        if (rBR < 0) {
            rBR *= (mBR = -1);
        }
        if (rBR > max) {
            rBR = max;
        }
        if (rBL < 0) {
            rBL *= (mBL = -1);
        }
        if (rBL > max) {
            rBL = max;
        }
        let c = this._command;
        c[c.length] = [1, "moveTo", [x + w - rTR, y]];
        c[c.length] = [1, "arcTo", [x + w + rTR * mTR, y - rTR * mTR, x + w, y + rTR, rTR]];
        c[c.length] = [1, "lineTo", [x + w, y + h - rBR]];
        c[c.length] = [1, "arcTo", [x + w + rBR * mBR, y + h + rBR * mBR, x + w - rBR, y + h, rBR]];
        c[c.length] = [1, "lineTo", [x + rBL, y + h]];
        c[c.length] = [1, "arcTo", [x - rBL * mBL, y + h + rBL * mBL, x, y + h - rBL, rBL]];
        c[c.length] = [1, "lineTo", [x, y + rTL]];
        c[c.length] = [1, "arcTo", [x - rTL * mTL, y - rTL * mTL, x + rTL, y, rTL]];
        c[c.length] = [1, "closePath", []];

        return this
    }

    /**
     * 绘画时移动到某一点
     * @method moveTo
     * @param {number} x
     * @param {number} y
     * @public
     * @since 1.0.0
     */
    public moveTo(x: number, y: number): this {
        this._command[this._command.length] = [1, "moveTo", [x, y]];

        return this;
    }

    /**
     * 从上一点画到某一点,如果没有设置上一点，则上一点默认为(0,0)
     * @method lineTo
     * @param {number} x
     * @param {number} y
     * @public
     * @since 1.0.0
     */
    public lineTo(x: number, y: number): this {
        this._command[this._command.length] = [1, "lineTo", [x, y]];
        return this;
    }

    /**
     * 
     * https://www.jianshu.com/p/e8ea5996cd79
     * @method arcTo
     * @param {number} x1 圆弧外一点，由它连接两切点
     * @param {number} y1
     * @param {number} x2 结束点 没啥用，设啥都是那个切点
     * @param {number} y2 
     * @param {number} radius 半径
     * @public
     * @since 1.0.0
     */
    public arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): this {
        this._command[this._command.length] = [1, "arcTo", [x1, y1, x2, y2, radius]];
        return this;
    }

    /**
     * 二次贝赛尔曲线
     * 从上一点画二次贝赛尔曲线到某一点,如果没有设置上一点，则上一占默认为(0,0)
     * @method quadraticCurveTo
     * @param {number} cpX 控制点X
     * @param {number} cpX 控制点Y
     * @param {number} x 终点X
     * @param {number} y 终点Y
     * @public
     * @since 1.0.0
     */
    public quadraticCurveTo(cpX: number, cpY: number, x: number, y: number): this {
        this._command[this._command.length] = [1, "quadraticCurveTo", [cpX, cpY, x, y]];
        return this;
    }

    /**
     * 三次贝赛尔曲线
     * 从上一点画二次贝赛尔曲线到某一点,如果没有设置上一点，则上一占默认为(0,0)
     * @method bezierCurveTo
     * @param {number} cp1X 1控制点X
     * @param {number} cp1Y 1控制点Y
     * @param {number} cp2X 2控制点X
     * @param {number} cp2Y 2控制点Y
     * @param {number} x 终点X
     * @param {number} y 终点Y
     * @public
     * @since 1.0.0
     */
    public bezierCurveTo(cp1X: number, cp1Y: number, cp2X: number, cp2Y: number, x: number, y: number): this {
        this._command[this._command.length] = [1, "bezierCurveTo", [cp1X, cp1Y, cp2X, cp2Y, x, y]];
        return this;
    }

    /**
     * 闭合一个绘画路径
     * @method closePath
     * @public
     * @since 1.0.0
     */
    public closePath(): this {
        this._command[this._command.length] = [1, "closePath", []];
        return this;
    }

    /**
     * 画一个矩形
     * @method drawRect
     * @param {number} x
     * @param {number} y
     * @param {number} w
     * @param {number} h
     * @public
     * @since 1.0.0
     */
    public drawRect(x: number, y: number, w: number, h: number): this {
        let c = this._command;
        c[c.length] = [1, "moveTo", [x, y]];
        c[c.length] = [1, "lineTo", [x + w, y]];
        c[c.length] = [1, "lineTo", [x + w, y + h]];
        c[c.length] = [1, "lineTo", [x, y + h]];
        c[c.length] = [1, "closePath", []];
        return this;
    }

    /**
     * 画一个弧形
     * https://www.w3school.com.cn/tags/canvas_arc.asp
     * 
     * @method drawArc
     * @param {number} x 起始点x
     * @param {number} y 起始点y
     * @param {number} radius 半径
     * @param {number} start 开始角度 弧度
     * @param {number} end 结束角度 弧度
     * @param {number} counterclockwise False = 顺时针，true = 逆时针
     * @public
     * @since 1.0.0
     */
    public arc(
        x: number,
        y: number,
        radius: number,
        startAngle: number,
        endAngle: number,
        counterclockwise: boolean = false
    ): this {
        this._command[this._command.length] = [1, "arc", [x, y, radius, startAngle, endAngle, counterclockwise]];
        return this;
    }

    /**
     * 画一个圆
     * @method drawCircle
     * @param {number} x 圆心x
     * @param {number} y 圆心y
     * @param {number} radius 半径
     * @param {boolean} counterclockwise 半径
     * @public
     * @since 1.0.0
     */
    public drawCircle(x: number, y: number, radius: number, counterclockwise: boolean = false): this {
        this._command[this._command.length] = [1, "arc", [x, y, radius, 0, 2 * Math.PI, counterclockwise]];
        return this;
    }

    /**
     * 画一个椭圆
     * @method drawEllipse
     * @param {number} x
     * @param {number} y
     * @param {number} w
     * @param {number} h
     * @public
     * @since 1.0.0
     */
    public drawEllipse(x: number, y: number, w: number, h: number): this {
        let k = 0.5522848;
        let ox = (w / 2) * k;
        let oy = (h / 2) * k;
        let xe = x + w;
        let ye = y + h;
        let xm = x + w / 2;
        let ym = y + h / 2;
        let c = this._command;
        c[c.length] = [1, "moveTo", [x, ym]];
        c[c.length] = [1, "bezierCurveTo", [x, ym - oy, xm - ox, y, xm, y]];
        c[c.length] = [1, "bezierCurveTo", [xm + ox, y, xe, ym - oy, xe, ym]];
        c[c.length] = [1, "bezierCurveTo", [xe, ym + oy, xm + ox, ye, xm, ye]];
        c[c.length] = [1, "bezierCurveTo", [xm - ox, ye, x, ym + oy, x, ym]];
        return this;
    }

    /**
     * 清除掉之前所有绘画的东西
     * @method clear
     * @public
     * @since 1.0.0
     */
    public clear(): this {
        let s = this;
        s._command = [];
        s.dirty = true;
        return s;
    }

    /**
     * 开始绘画填充,如果想画的东西有颜色填充,一定要从此方法开始
     * @method beginFill
     * @param {number|string} color 颜色值 单色和RGBA格式
     * @public
     * @since 1.0.0
     */
    public beginFill(color: number | string = 0, alpha: number = 1): this {
        //颜色做个兼容
        if (typeof (color) == "string") color = string2hex(color);

        var sColor = getRGBA(hex2string(color), alpha);
        this._fill(sColor);
        return this;
    }

    /**
     * 线性或径向渐变填充
     * @method beginGradientFill
     * @param {Array} points 四个数字表示线性渐变参考createLinearGradient，六个数字表示径向渐变参考createRadialGradient
     * @param {Array} colors [系数, #式颜色值, 透明度]的数组，比如[[0, "#ff0000", 1],[0.5, "#00ff00", 1],[1, "#0000ff", 1]]
     * @public
     * @since 1.0.0
     */
    public beginGradientFill(points: number[], colors: [number, string, number][]): this {
        this._fill(getGradientColor(points, colors));
        return this;
    }


    /**
     * 位图填充
     * @method beginBitmapFill
     * @param {Image} image
     * @param { Array} matrix
     * @public
     * @since 1.0.0
     */
    public beginBitmapFill(image: any, matrix: Array<number>): this {
        let s = this;
        if (matrix) {
            s._isBitmapFill = matrix;
        }
        s._fill(getCanvasBitmapStyle(image));
        return s;
    }

    private _fill(fillStyle: any): void {
        let c = this._command;
        c[c.length] = [0, "fillStyle", fillStyle];
        c[c.length] = [1, "beginPath", []];
        this.dirty = true;
    }

    /**
     * 给线条着色
     * @method beginStroke
     * @param {number} color  颜色值
     * @param {number} lineWidth 宽度
     * @param {number} cap 线头的形状 0 butt 1 round 2 square 默认 butt
     * @param {number} join 线与线之间的交接处形状 0 miter 1 bevel 2 round  默认miter
     * @param {number} miter 正数,规定最大斜接长度,如果斜接长度超过 miterLimit 的值，边角会以 lineJoin 的 "bevel" 类型来显示 默认10
     * @public
     * @since 1.0.0
     */
    public beginStroke(
        color: number,
        lineWidth: number = 1,
        cap: LINE_CAP = LINE_CAP.BUTT,
        join: LINE_JOIN = LINE_JOIN.MITER,
        miter: number = 0,
        alpha: number = 1
    ): this {
        var sColor = getRGBA(hex2string(color), alpha);
        this._stroke(sColor, lineWidth, cap, join, miter);
        return this;
    }

    /**
     * 画线性或径向渐变的线条
     * @method beginGradientStroke
     * @param {Array} points 四个数字表示线性渐变参考createLinearGradient，六个数字表示径向渐变参考createRadialGradient
     * @param {Array} colors [系数, #式颜色值, 透明度]的数组，比如[[0, "#ff0000", 1],[0.5, "#00ff00", 1],[1, "#0000ff", 1]]
     * @param {number} lineWidth
     * @param {number} cap 线头的形状 0 butt 1 round 2 square 默认 butt
     * @param {number} join 线与线之间的交接处形状 0 miter 1 bevel 2 round  默认miter
     * @param {number} miter 正数,规定最大斜接长度,如果斜接长度超过 miterLimit 的值，边角会以 lineJoin 的 "bevel" 类型来显示 默认10
     * @public
     * @since 1.0.0
     */
    public beginGradientStroke(
        points: Array<number>,
        colors: any,
        lineWidth: number = 1,
        cap: LINE_CAP = LINE_CAP.BUTT,
        join: LINE_JOIN = LINE_JOIN.MITER,
        miter: number = 10
    ): this {
        this._stroke(getGradientColor(points, colors), lineWidth, cap, join, miter);
        return this;
    }

    /**
     * 线条位图填充 一般给Flash2x用
     * @method beginBitmapStroke
     * @param {Image} image
     * @param {Array} matrix
     * @param {number} lineWidth
     * @param {string} cap 线头的形状 butt round square 默认 butt
     * @param {string} join 线与线之间的交接处形状 bevel round miter 默认miter
     * @param {number} miter 正数,规定最大斜接长度,如果斜接长度超过 miterLimit 的值，边角会以 lineJoin 的 "bevel" 类型来显示 默认10
     * @public
     * @since 1.0.0
     */
    public beginBitmapStroke(
        image: any,
        matrix: Array<number>,
        lineWidth: number = 1,
        cap: LINE_CAP = LINE_CAP.BUTT,
        join: LINE_JOIN = LINE_JOIN.MITER,
        miter: number = 10
    ): this {
        let s = this;
        if (matrix) {
            s._isBitmapStroke = matrix;
        }
        s._stroke(getCanvasBitmapStyle(image), lineWidth, cap, join, miter);
        return s;
    }

    /**
     * @method _stroke
     * @param strokeStyle
     * @param {number} width
     * @param {number} cap
     * @param {number} join
     * @param {number} miter
     * @private
     * @since 1.0.0
     */
    private _stroke(strokeStyle: any, width: number, cap: LINE_CAP, join: LINE_JOIN, miter: number): void {
        let c = this._command;
        c[c.length] = [0, "lineWidth", width];
        c[c.length] = [0, "lineCap", cap];
        c[c.length] = [0, "lineJoin", join];
        c[c.length] = [0, "miterLimit", miter];
        c[c.length] = [0, "strokeStyle", strokeStyle];
        c[c.length] = [1, "beginPath", []];
        this.dirty = true;
    }

    /**
     * 结束填充
     * @method endFill
     * @public
     * @since 1.0.0
     */
    public endFill(): this {
        let s = this;
        let c = s._command;
        let m = s._isBitmapFill;
        if (m) {
            c[c.length] = [2, "setTransform", m];
        }
        c[c.length] = ([1, "fill", []]);
        if (m) {
            s._isBitmapFill = null;
        }
        return s;
    }
    /**
     * 标记是否作为遮罩，作为遮罩时不会被当作hittest检测对象，默认false，内部逻辑处理用，外部不建议修改
     */
    _isUsedToMask: boolean = false;
    /**
     * 结束画线
     * @method endStroke
     * @public
     * @since 1.0.0
     */
    public endStroke(): this {
        let s = this;
        let c = s._command;
        let m = s._isBitmapStroke;
        if (m) {
            //如果为2则还需要特别处理
            c[c.length] = [2, "setTransform", m];
        }
        c[c.length] = ([1, "stroke", []]);
        if (m) {
            s._isBitmapStroke = null;
        }
        return s;
    }

    /**
     * 重写刷新
     * @method update
     * @public
     * @since 1.0.0
     */
    public updateShape(): void {
        let s: Shape = this;
        if (!s.visible) return;
        if (!s.dirty) return;
        s.dirty = false;
        s._boundsID++;//为了Container里calculateBounds的判断;
        //更新缓存
        let cLen: number = s._command.length;
        let leftX: number;
        let leftY: number;
        let buttonRightX: number;
        let buttonRightY: number;
        let i: number;
        //确定是否有数据,如果有数据的话就计算出缓存图的宽和高
        let data: any;
        let lastX = 0;
        let lastY = 0;
        let lineWidth = 0;
        for (i = 0; i < cLen; i++) {
            data = s._command[i];
            if (data[0] == 1) {
                if (data[1] == "moveTo" || data[1] == "lineTo" || data[1] == "arcTo" || data[1] == "bezierCurveTo") {
                    if (leftX == undefined) {
                        leftX = data[2][0];
                    }
                    if (leftY == undefined) {
                        leftY = data[2][1];
                    }
                    if (buttonRightX == undefined) {
                        buttonRightX = data[2][0];
                    }
                    if (buttonRightY == undefined) {
                        buttonRightY = data[2][1];
                    }

                    if (data[1] == "bezierCurveTo") {
                        leftX = Math.min(leftX, data[2][0], data[2][2], data[2][4]);
                        leftY = Math.min(leftY, data[2][1], data[2][3], data[2][5]);
                        buttonRightX = Math.max(buttonRightX, data[2][0], data[2][2], data[2][4]);
                        buttonRightY = Math.max(buttonRightY, data[2][1], data[2][3], data[2][5]);
                        lastX = data[2][4];
                        lastY = data[2][5];
                    } else {
                        leftX = Math.min(leftX, data[2][0]);
                        leftY = Math.min(leftY, data[2][1]);
                        buttonRightX = Math.max(buttonRightX, data[2][0]);
                        buttonRightY = Math.max(buttonRightY, data[2][1]);
                        lastX = data[2][0];
                        lastY = data[2][1];
                    }
                } else if (data[1] == "quadraticCurveTo") {
                    //求中点
                    let mid1X = (lastX + data[2][0]) * 0.5;
                    let mid1Y = (lastY + data[2][1]) * 0.5;
                    let mid2X = (data[2][0] + data[2][2]) * 0.5;
                    let mid2Y = (data[2][1] + data[2][3]) * 0.5;
                    if (leftX == undefined) {
                        leftX = mid1X;
                    }
                    if (leftY == undefined) {
                        leftY = mid1Y;
                    }
                    if (buttonRightX == undefined) {
                        buttonRightX = mid1X;
                    }
                    if (buttonRightY == undefined) {
                        buttonRightY = mid1Y;
                    }
                    leftX = Math.min(leftX, mid1X, mid2X, data[2][2]);
                    leftY = Math.min(leftY, mid1Y, mid2Y, data[2][3]);
                    buttonRightX = Math.max(buttonRightX, mid1X, mid2X, data[2][2]);
                    buttonRightY = Math.max(buttonRightY, mid1Y, mid2Y, data[2][3]);
                    lastX = data[2][2];
                    lastY = data[2][3];
                } else if (data[1] == "arc") {
                    let yuanPointX = data[2][0];
                    let yuanPointY = data[2][1];
                    let radio = data[2][2];
                    let yuanLeftX = yuanPointX - radio;
                    let yuanLeftY = yuanPointY - radio;
                    let yuanBRX = yuanPointX + radio;
                    let yuanBRY = yuanPointY + radio;
                    if (leftX == undefined) {
                        leftX = yuanLeftX;
                    }
                    if (leftY == undefined) {
                        leftY = yuanLeftY;
                    }
                    if (buttonRightX == undefined) {
                        buttonRightX = yuanBRX;
                    }
                    if (buttonRightY == undefined) {
                        buttonRightY = yuanBRY;
                    }
                    leftX = Math.min(leftX, yuanLeftX);
                    leftY = Math.min(leftY, yuanLeftY);
                    buttonRightX = Math.max(buttonRightX, yuanBRX);
                    buttonRightY = Math.max(buttonRightY, yuanBRY);
                }
            } else {
                if (data[1] == "lineWidth") {
                    if (lineWidth < data[2]) {
                        lineWidth = data[2];
                    }
                }
            }
        }
        if (leftX != undefined || lineWidth > 0) {
            if (leftX == undefined) {
                leftX = 0;
                leftY = 0;
            }
            leftX -= 20 + lineWidth >> 1;
            leftY -= 20 + lineWidth >> 1;
            buttonRightX += 20 + lineWidth >> 1;
            buttonRightY += 20 + lineWidth >> 1;
            let w = buttonRightX - leftX;
            let h = buttonRightY - leftY;
            s.offsetX = leftX;
            s.offsetY = leftY;
            s._localBoundsSelf.x = leftX + 10;
            s._localBoundsSelf.y = leftY + 10;
            s._localBoundsSelf.width = w - 20;
            s._localBoundsSelf.height = h - 20;
            let _canvas: any = s.canvas;
            let ctx = s.context;
            _canvas.width = w;
            _canvas.height = h;
            ctx.clearRect(0, 0, w, h);
            ctx.setTransform(1, 0, 0, 1, -leftX, -leftY);
            s._drawShape(ctx);
            //贴图锚点修改
            s.anchorTexture.set(-leftX / w, -leftY / h) //= { x: -leftX / w, y: -leftY / h };
        } else {
            s.canvas.width = 0;
            s.canvas.height = 0;
            s.offsetX = 0;
            s.offsetY = 0;
            s._localBoundsSelf.clear();
            s.anchorTexture.set(0, 0) //= { x: 0, y: 0 };
        }
        //更新贴图
        s.texture.update();
        //这个先不管,上面的_localBoundsSelf等都已计算
        s._onTextureUpdate();//更新回去anchorTexture修改时更新了用的时原先的canvas尺寸
    }

    /**
     * @method _drawShape
     * @param ctx
     * @private
     * @return {void}
     */
    private _drawShape(ctx: any): void {
        let s = this;
        let com = s._command;
        let cLen = com.length;
        let data: any;
        let leftX: number = s.offsetX;
        let leftY: number = s.offsetY;
        for (let i = 0; i < cLen; i++) {
            data = com[i];
            if (data[0] > 0) {
                let paramsLen = data[2].length;
                if (paramsLen == 0) {
                    ctx[data[1]]();
                } else if (paramsLen == 2) {
                    ctx[data[1]](data[2][0], data[2][1]);
                } else if (paramsLen == 4) {
                    ctx[data[1]](data[2][0], data[2][1], data[2][2], data[2][3]);
                } else if (paramsLen == 5) {
                    ctx[data[1]](data[2][0], data[2][1], data[2][2], data[2][3], data[2][4]);
                } else if (paramsLen == 6) {
                    let lx = data[2][4];
                    let ly = data[2][5];
                    if (data[0] == 2) {
                        //位图填充
                        lx -= leftX;
                        ly -= leftY;
                    }
                    ctx[data[1]](data[2][0], data[2][1], data[2][2], data[2][3], lx, ly);
                }
            } else {
                ctx[data[1]] = data[2];
            }
        }
    }

    /**
     * 重写hitTestPoint
     * @method  hitTestPoint
     * @param {Point} globalPoint
     * @param {boolean} isMouseEvent
     * @return {any}
     * @public
     * @since 1.0.0
     */
    public hitTestPoint(globalPoint: Point, isMouseEvent: boolean = false): DisplayObject {
        let s: Shape = this;
        this.updateShape();//需要更新shape先
        //直接继承
        var hitResult = super.hitTestPoint(globalPoint, isMouseEvent)
        //如果这样返回都没有，直接返回null
        if (!hitResult) return null;
        //如果不是自己，是子级的。直接返回子级
        if (hitResult != s) return hitResult;
        //如果不是像素级碰撞 直接返回自己
        if (!s.hitTestByPixel) return s;
        let p: Point = globalPoint;
        //p要用到获取像素
        if (isMouseEvent) p = s.globalToLocal(globalPoint);
        let image = s._texture;
        //无图数据返回null
        if (!image || image.width == 0 || image.height == 0) return null;
        // var backupCanvas = getBackupCanvas();
        // backupCanvas.width = 1;
        // backupCanvas.height = 1;
        p.x -= s.offsetX;
        p.y -= s.offsetY;
        let ctx = getBackupCanvasCtx() //backupCanvas.getContext('2d');
        ctx.setTransform(1, 0, 0, 1, 0, 0);//先移动位置，否则颜色清除有问题,原先修改尺寸就不用
        ctx.clearRect(0, 0, 1, 1);
        ctx.setTransform(1, 0, 0, 1, -p.x, -p.y);
        ctx.drawImage(s.canvas, 0, 0);
        //取imageData对象
        var imageData = ctx.getImageData(0, 0, 1, 1);
        //容错处理(暂时淘宝小程序bug，安卓有可能取到的imageData是undefined)，按照点击了返回
        if (!imageData || !imageData.data) return s;
        //像素透明度不为0；返回自己
        if (imageData.data[3] > 0) return s;
        return null
    }

    /**
     * 如果有的话,改变矢量对象的边框或者填充的颜色.
     * @method changeColor
     * @param {Object} infoObj
     * @param {string|any} infoObj.fillColor 填充颜色值，如"#fff" 或者 "rgba(255,255,255,1)"或者是Shape.getGradientColor()方法返回的渐变对象;
     * @param {string} infoObj.strokeColor 线条颜色值，如"#fff" 或者 "rgba(255,255,255,1)";
     * @param {number} infoObj.lineWidth 线条的粗细，如"1,2,3...";
     * @public
     * @since 1.0.2
     */
    public changeColor(infoObj: any): void {
        let s: Shape = this;
        let cLen: number = s._command.length;
        let c = s._command;
        for (let i = 0; i < cLen; i++) {
            if (c[i][0] == 0) {
                if (c[i][1] == "fillStyle" && infoObj.fillColor && c[i][2] != infoObj.fillColor) {
                    c[i][2] = infoObj.fillColor;
                    s.dirty = true;
                }
                if (c[i][1] == "strokeStyle" && infoObj.strokeColor && c[i][2] != infoObj.strokeColor) {
                    c[i][2] = infoObj.strokeColor;
                    s.dirty = true;
                }
                if (c[i][1] == "lineWidth" && infoObj.lineWidth && c[i][2] != infoObj.lineWidth) {
                    c[i][2] = infoObj.lineWidth;
                    s.dirty = true;
                }
            }
        }
    }
    /**
     * 计算自身盒子前要更新shape
     */
    // _calculateBounds() {
    //     this.updateShape();
    //     super._calculateBounds();
    // }
    //为了updateShape里的_boundId++不会影响calculateBounds里的判断，用不带_的继承，TODO待测试
    calculateBounds() {
        this.updateShape();
        //在舞台上，且渲染方式是webgl，且用过遮罩的，注意，临时解决webgl，shape的遮罩移动时不生效，待考虑更优雅的方式TODO
        if (this.stage && this.stage.renderObj.type == RENDERER_TYPE.WEBGL && this._isUsedToMask) this._boundsID++;
        super.calculateBounds();
    }

    _renderCanvas(renderer) {
        this.updateShape();
        super._renderCanvas(renderer);
    }
    _renderWebGL(renderer) {
        this.updateShape();
        super._renderWebGL(renderer)
    }


    public destroy(): void {
        //清除相应的数据引用
        let s = this;
        s._command = null;
        s._isBitmapStroke = null;
        s._isBitmapFill = null;
        super.destroy();
    }
}
