UNPKG

dragonbones-runtime

Version:

the tools to build dragonbones file for diffrent framework

1,077 lines (1,031 loc) 67.3 kB
////////////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2014-present, Egret Technology. // All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the Egret nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY EGRET AND CONTRIBUTORS "AS IS" AND ANY EXPRESS // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. // IN NO EVENT SHALL EGRET AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;LOSS OF USE, DATA, // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, // EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // ////////////////////////////////////////////////////////////////////////////////////// /** * @private */ interface CanvasRenderingContext2D { imageSmoothingEnabled: boolean; $imageSmoothingEnabled: boolean; $offsetX: number; $offsetY: number; } namespace egret { let blendModes = ["source-over", "lighter", "destination-out"]; let defaultCompositeOp = "source-over"; let BLACK_COLOR = "#000000"; let CAPS_STYLES = { none: 'butt', square: 'square', round: 'round' }; let renderBufferPool: sys.RenderBuffer[] = [];//渲染缓冲区对象池 let renderBufferPool_Filters: sys.RenderBuffer[] = [];//滤镜缓冲区对象池 export class CanvasRenderer { private nestLevel: number = 0;//渲染的嵌套层次,0表示在调用堆栈的最外层。 public render(displayObject: DisplayObject, buffer: sys.RenderBuffer, matrix: Matrix, forRenderTexture?: boolean): number { this.nestLevel++; let context: CanvasRenderingContext2D = buffer.context; let root: DisplayObject = forRenderTexture ? displayObject : null; //绘制显示对象 context.transform(matrix.a, matrix.b, matrix.c, matrix.d, 0, 0); let drawCall = this.drawDisplayObject(displayObject, context, matrix.tx, matrix.ty, true); let invert = Matrix.create(); matrix.$invertInto(invert); context.transform(invert.a, invert.b, invert.c, invert.d, 0, 0); Matrix.release(invert); this.nestLevel--; if (this.nestLevel === 0) { //最大缓存6个渲染缓冲 if (renderBufferPool.length > 6) { renderBufferPool.length = 6; } let length = renderBufferPool.length; for (let i = 0; i < length; i++) { renderBufferPool[i].resize(0, 0); } } return drawCall; } /** * @private * 绘制一个显示对象 */ private drawDisplayObject(displayObject: DisplayObject, context: CanvasRenderingContext2D, offsetX: number, offsetY: number, isStage?: boolean): number { let drawCalls = 0; let node: sys.RenderNode; let displayList = displayObject.$displayList; if (displayList && !isStage) { if (displayObject.$cacheDirty || displayObject.$renderDirty || displayList.$canvasScaleX != sys.DisplayList.$canvasScaleX || displayList.$canvasScaleY != sys.DisplayList.$canvasScaleY) { drawCalls += displayList.drawToSurface(); } node = displayList.$renderNode; } else { if (displayObject.$renderDirty) { node = displayObject.$getRenderNode(); } else { node = displayObject.$renderNode; } } displayObject.$cacheDirty = false; if (node) { drawCalls++; context.$offsetX = offsetX; context.$offsetY = offsetY; switch (node.type) { case sys.RenderNodeType.BitmapNode: this.renderBitmap(<sys.BitmapNode>node, context); break; case sys.RenderNodeType.TextNode: this.renderText(<sys.TextNode>node, context); break; case sys.RenderNodeType.GraphicsNode: this.renderGraphics(<sys.GraphicsNode>node, context); break; case sys.RenderNodeType.GroupNode: this.renderGroup(<sys.GroupNode>node, context); break; case sys.RenderNodeType.MeshNode: this.renderMesh(<sys.MeshNode>node, context); break; case sys.RenderNodeType.NormalBitmapNode: this.renderNormalBitmap(<sys.NormalBitmapNode>node, context); break; } context.$offsetX = 0; context.$offsetY = 0; } if (displayList && !isStage) { return drawCalls; } let children = displayObject.$children; if (children) { let length = children.length; for (let i = 0; i < length; i++) { let child = children[i]; let offsetX2; let offsetY2; if (child.$useTranslate) { let m = child.$getMatrix(); offsetX2 = offsetX + child.$x; offsetY2 = offsetY + child.$y; context.save(); context.transform(m.a, m.b, m.c, m.d, offsetX2, offsetY2); offsetX2 = -child.$anchorOffsetX; offsetY2 = -child.$anchorOffsetY; } else { offsetX2 = offsetX + child.$x - child.$anchorOffsetX; offsetY2 = offsetY + child.$y - child.$anchorOffsetY; } let tempAlpha; if (child.$alpha != 1) { tempAlpha = context.globalAlpha; context.globalAlpha *= child.$alpha; } switch (child.$renderMode) { case RenderMode.NONE: break; case RenderMode.FILTER: drawCalls += this.drawWithFilter(child, context, offsetX2, offsetY2); break; case RenderMode.CLIP: drawCalls += this.drawWithClip(child, context, offsetX2, offsetY2); break; case RenderMode.SCROLLRECT: drawCalls += this.drawWithScrollRect(child, context, offsetX2, offsetY2); break; default: drawCalls += this.drawDisplayObject(child, context, offsetX2, offsetY2); break; } if (child.$useTranslate) { context.restore(); } else if (tempAlpha) { context.globalAlpha = tempAlpha; } } } return drawCalls; } private drawWithFilter(displayObject: DisplayObject, context: CanvasRenderingContext2D, offsetX: number, offsetY: number): number { if (Capabilities.runtimeType == RuntimeType.NATIVE) { return this.drawWithFilterNative(displayObject, context, offsetX, offsetY); } let drawCalls = 0; let filters = displayObject.$filters; let filtersLen: number = filters.length; let hasBlendMode = (displayObject.$blendMode !== 0); let compositeOp: string; if (hasBlendMode) { compositeOp = blendModes[displayObject.$blendMode]; if (!compositeOp) { compositeOp = defaultCompositeOp; } } let displayBounds = displayObject.$getOriginalBounds(); if (displayBounds.width <= 0 || displayBounds.height <= 0) { return drawCalls; } // 为显示对象创建一个新的buffer let displayBuffer = this.createRenderBuffer(displayBounds.width, displayBounds.height, true); let displayContext = displayBuffer.context; if (displayObject.$mask) { drawCalls += this.drawWithClip(displayObject, context, offsetX, offsetY); } else if (displayObject.$scrollRect || displayObject.$maskRect) { drawCalls += this.drawWithScrollRect(displayObject, context, offsetX, offsetY); } else { drawCalls += this.drawDisplayObject(displayObject, context, offsetX, offsetY); } //绘制结果到屏幕 if (drawCalls > 0) { if (hasBlendMode) { context.globalCompositeOperation = compositeOp; } drawCalls++; // 应用滤镜 let imageData = displayContext.getImageData(0, 0, displayBuffer.surface.width, displayBuffer.surface.height); for (let i = 0; i < filtersLen; i++) { let filter = filters[i]; if (filter.type == "colorTransform") { colorFilter(imageData.data, displayBuffer.surface.width, displayBuffer.surface.height, (<ColorMatrixFilter>filter).$matrix); } else if (filter.type == "blur") { blurFilter(imageData.data, displayBuffer.surface.width, displayBuffer.surface.height, (<BlurFilter>filter).$blurX, (<BlurFilter>filter).$blurY); } else if (filter.type == "glow") { let r = (<GlowFilter>filter).$red; let g = (<GlowFilter>filter).$green; let b = (<GlowFilter>filter).$blue; let a = (<GlowFilter>filter).$alpha; if ((<GlowFilter>filter).$inner || (<GlowFilter>filter).$knockout || (<DropShadowFilter>filter).$hideObject) { dropShadowFilter2(imageData.data, displayBuffer.surface.width, displayBuffer.surface.height, [r / 255, g / 255, b / 255, a], (<GlowFilter>filter).$blurX, (<GlowFilter>filter).$blurY, (<DropShadowFilter>filter).$angle ? ((<DropShadowFilter>filter).$angle / 180 * Math.PI) : 0, (<DropShadowFilter>filter).$distance || 0, (<GlowFilter>filter).$strength, (<GlowFilter>filter).$inner ? 1 : 0, (<GlowFilter>filter).$knockout ? 0 : 1, (<DropShadowFilter>filter).$hideObject ? 1 : 0); } else { // 如果没有高级效果,使用性能比较高的方式 dropShadowFilter(imageData.data, displayBuffer.surface.width, displayBuffer.surface.height, [r / 255, g / 255, b / 255, a], (<GlowFilter>filter).$blurX, (<GlowFilter>filter).$blurY, (<DropShadowFilter>filter).$angle ? ((<DropShadowFilter>filter).$angle / 180 * Math.PI) : 0, (<DropShadowFilter>filter).$distance || 0, (<GlowFilter>filter).$strength); } } else if (filter.type == "custom") { // 目前canvas渲染不支持自定义滤镜 } } displayContext.putImageData(imageData, 0, 0); context.globalAlpha = 1; // 绘制结果的时候,应用滤镜 context.drawImage(displayBuffer.surface, offsetX + displayBounds.x, offsetY + displayBounds.y); if (hasBlendMode) { context.globalCompositeOperation = defaultCompositeOp; } } renderBufferPool_Filters.push(displayBuffer); return drawCalls; } private drawWithFilterNative(displayObject: DisplayObject, context: CanvasRenderingContext2D, offsetX: number, offsetY: number): number { let drawCalls = 0; let filters = displayObject.$filters; let hasBlendMode = (displayObject.$blendMode !== 0); let compositeOp: string; if (hasBlendMode) { compositeOp = blendModes[displayObject.$blendMode]; if (!compositeOp) { compositeOp = defaultCompositeOp; } } let displayBounds = displayObject.$getOriginalBounds(); if (displayBounds.width <= 0 || displayBounds.height <= 0) { return drawCalls; } if (filters.length == 1 && filters[0].type == "colorTransform" && !displayObject.$children) { if (hasBlendMode) { context.globalCompositeOperation = compositeOp; } (<any>context).setGlobalShader(filters[0]); if (displayObject.$mask) { drawCalls += this.drawWithClip(displayObject, context, offsetX, offsetY); } else if (displayObject.$scrollRect || displayObject.$maskRect) { drawCalls += this.drawWithScrollRect(displayObject, context, offsetX, offsetY); } else { drawCalls += this.drawDisplayObject(displayObject, context, offsetX, offsetY); } (<any>context).setGlobalShader(null); if (hasBlendMode) { context.globalCompositeOperation = defaultCompositeOp; } return drawCalls; } let displayBuffer = this.createRenderBuffer(displayBounds.width, displayBounds.height); if (displayObject.$mask) { drawCalls += this.drawWithClip(displayObject, context, offsetX, offsetY); } else if (displayObject.$scrollRect || displayObject.$maskRect) { drawCalls += this.drawWithScrollRect(displayObject, context, offsetX, offsetY); } else { drawCalls += this.drawDisplayObject(displayObject, context, offsetX, offsetY); } //绘制结果到屏幕 if (drawCalls > 0) { if (hasBlendMode) { context.globalCompositeOperation = compositeOp; } drawCalls++; context.globalAlpha = 1; // 绘制结果的时候,应用滤镜 (<any>context).setGlobalShader(filters[0]); context.drawImage(displayBuffer.surface, 0, 0, displayBuffer.width, displayBuffer.height, offsetX + displayBounds.x, offsetY + displayBounds.y, displayBuffer.width, displayBuffer.height); (<any>context).setGlobalShader(null); if (hasBlendMode) { context.globalCompositeOperation = defaultCompositeOp; } } renderBufferPool.push(displayBuffer); return drawCalls; } private drawWithClip(displayObject: DisplayObject, context: CanvasRenderingContext2D, offsetX: number, offsetY: number): number { let drawCalls = 0; let hasBlendMode = (displayObject.$blendMode !== 0); let compositeOp: string; if (hasBlendMode) { compositeOp = blendModes[displayObject.$blendMode]; if (!compositeOp) { compositeOp = defaultCompositeOp; } } let scrollRect = displayObject.$scrollRect ? displayObject.$scrollRect : displayObject.$maskRect; let mask = displayObject.$mask; if (mask) { let maskRenderMatrix = mask.$getMatrix(); //遮罩scaleX或scaleY为0,放弃绘制 if ((maskRenderMatrix.a == 0 && maskRenderMatrix.b == 0) || (maskRenderMatrix.c == 0 && maskRenderMatrix.d == 0)) { return drawCalls; } } //没有遮罩,同时显示对象没有子项 if (!mask && (!displayObject.$children || displayObject.$children.length == 0)) { if (scrollRect) { context.save(); context.beginPath(); context.rect(scrollRect.x + offsetX, scrollRect.y + offsetY, scrollRect.width, scrollRect.height); context.clip(); } if (hasBlendMode) { context.globalCompositeOperation = compositeOp; } drawCalls += this.drawDisplayObject(displayObject, context, offsetX, offsetY); if (hasBlendMode) { context.globalCompositeOperation = defaultCompositeOp; } if (scrollRect) { context.restore(); } return drawCalls; } let maskRenderNode = mask.$getRenderNode(); //遮罩是单纯的填充图形,且alpha为1,性能优化 if (mask && Capabilities.$runtimeType == RuntimeType.WEB && (!mask.$children || mask.$children.length == 0) && maskRenderNode && maskRenderNode.type == sys.RenderNodeType.GraphicsNode && maskRenderNode.drawData.length == 1 && (<sys.Path2D>maskRenderNode.drawData[0]).type == sys.PathType.Fill && (<sys.FillPath>maskRenderNode.drawData[0]).fillAlpha == 1) { this.renderingMask = true; context.save(); let maskMatrix = Matrix.create(); maskMatrix.copyFrom(mask.$getConcatenatedMatrix()); mask.$getConcatenatedMatrixAt(displayObject, maskMatrix); context.transform(maskMatrix.a, maskMatrix.b, maskMatrix.c, maskMatrix.d, maskMatrix.tx, maskMatrix.ty); let calls = this.drawDisplayObject(mask, context, offsetX, offsetY); this.renderingMask = false; maskMatrix.$invertInto(maskMatrix); context.transform(maskMatrix.a, maskMatrix.b, maskMatrix.c, maskMatrix.d, maskMatrix.tx, maskMatrix.ty); Matrix.release(maskMatrix); if (scrollRect) { context.beginPath(); context.rect(scrollRect.x + offsetX, scrollRect.y + offsetY, scrollRect.width, scrollRect.height); context.clip(); } calls += this.drawDisplayObject(displayObject, context, offsetX, offsetY); context.restore(); return calls; } //todo 若显示对象是容器,同时子项有混合模式,则需要先绘制背景到displayBuffer并清除背景区域 //绘制显示对象自身,若有scrollRect,应用clip let displayBounds = displayObject.$getOriginalBounds(); let displayBuffer = this.createRenderBuffer(displayBounds.width, displayBounds.height); let displayContext: CanvasRenderingContext2D = displayBuffer.context; if (!displayContext) {//RenderContext创建失败,放弃绘制遮罩。 drawCalls += this.drawDisplayObject(displayObject, context, offsetX, offsetY); return drawCalls; } drawCalls += this.drawDisplayObject(displayObject, displayContext, 0, 0); //绘制遮罩 if (mask) { let maskMatrix = Matrix.create(); maskMatrix.copyFrom(mask.$getConcatenatedMatrix()); mask.$getConcatenatedMatrixAt(displayObject, maskMatrix); //如果只有一次绘制或是已经被cache直接绘制到displayContext if (Capabilities.$runtimeType == RuntimeType.WEB && maskRenderNode && maskRenderNode.$getRenderCount() == 1 || mask.$displayList) { displayContext.globalCompositeOperation = "destination-in"; displayContext.save(); displayContext.setTransform(maskMatrix.a, maskMatrix.b, maskMatrix.c, maskMatrix.d, maskMatrix.tx, maskMatrix.ty); drawCalls += this.drawDisplayObject(mask, displayContext, 0, 0); displayContext.restore(); } else { let maskBuffer = this.createRenderBuffer(displayBounds.width, displayBounds.height); let maskContext = maskBuffer.context; maskContext.setTransform(maskMatrix.a, maskMatrix.b, maskMatrix.c, maskMatrix.d, maskMatrix.tx, maskMatrix.ty); drawCalls += this.drawDisplayObject(mask, maskContext, -displayBounds.x, -displayBounds.y); displayContext.globalCompositeOperation = "destination-in"; displayContext.globalAlpha = 1; displayContext.drawImage(maskBuffer.surface, 0, 0); renderBufferPool.push(maskBuffer); } Matrix.release(maskMatrix); } //绘制结果到屏幕 if (drawCalls > 0) { drawCalls++; if (hasBlendMode) { context.globalCompositeOperation = compositeOp; } if (scrollRect) { context.save(); context.beginPath(); context.rect(scrollRect.x + offsetX, scrollRect.y + offsetY, scrollRect.width, scrollRect.height); context.clip(); } context.globalAlpha = 1; context.drawImage(<any>displayBuffer.surface, offsetX + displayBounds.x, offsetY + displayBounds.y); if (scrollRect) { context.restore(); } if (hasBlendMode) { context.globalCompositeOperation = defaultCompositeOp; } } renderBufferPool.push(displayBuffer); return drawCalls; } private drawWithScrollRect(displayObject: DisplayObject, context: CanvasRenderingContext2D, offsetX: number, offsetY: number): number { let drawCalls = 0; let scrollRect = displayObject.$scrollRect ? displayObject.$scrollRect : displayObject.$maskRect; if (scrollRect.isEmpty()) { return drawCalls; } if (displayObject.$scrollRect) { offsetX -= scrollRect.x; offsetY -= scrollRect.y; } //绘制显示对象自身 context.save(); context.beginPath(); context.rect(scrollRect.x + offsetX, scrollRect.y + offsetY, scrollRect.width, scrollRect.height); context.clip(); drawCalls += this.drawDisplayObject(displayObject, context, offsetX, offsetY); context.restore(); return drawCalls; } public drawNodeToBuffer(node: sys.RenderNode, buffer: sys.RenderBuffer, matrix: Matrix, forHitTest?: boolean): void { let context: CanvasRenderingContext2D = buffer.context; context.setTransform(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty); this.renderNode(node, context, forHitTest); } /** * 将一个DisplayObject绘制到渲染缓冲,用于RenderTexture绘制 * @param displayObject 要绘制的显示对象 * @param buffer 渲染缓冲 * @param matrix 要叠加的矩阵 */ public drawDisplayToBuffer(displayObject: DisplayObject, buffer: sys.RenderBuffer, matrix: Matrix): number { let context: CanvasRenderingContext2D = buffer.context; if (matrix) { context.setTransform(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty); } let node: sys.RenderNode; if (displayObject.$renderDirty) { node = displayObject.$getRenderNode(); } else { node = displayObject.$renderNode; } let drawCalls = 0; if (node) { drawCalls++; switch (node.type) { case sys.RenderNodeType.BitmapNode: this.renderBitmap(<sys.BitmapNode>node, context); break; case sys.RenderNodeType.TextNode: this.renderText(<sys.TextNode>node, context); break; case sys.RenderNodeType.GraphicsNode: this.renderGraphics(<sys.GraphicsNode>node, context); break; case sys.RenderNodeType.GroupNode: this.renderGroup(<sys.GroupNode>node, context); break; case sys.RenderNodeType.MeshNode: this.renderMesh(<sys.MeshNode>node, context); break; case sys.RenderNodeType.NormalBitmapNode: this.renderNormalBitmap(<sys.NormalBitmapNode>node, context); break; } } let children = displayObject.$children; if (children) { let length = children.length; for (let i = 0; i < length; i++) { let child = children[i]; switch (child.$renderMode) { case RenderMode.NONE: break; case RenderMode.FILTER: drawCalls += this.drawWithFilter(child, context, 0, 0); break; case RenderMode.CLIP: drawCalls += this.drawWithClip(child, context, 0, 0); break; case RenderMode.SCROLLRECT: drawCalls += this.drawWithScrollRect(child, context, 0, 0); break; default: drawCalls += this.drawDisplayObject(child, context, 0, 0); break; } } } return drawCalls; } private renderNode(node: sys.RenderNode, context: CanvasRenderingContext2D, forHitTest?: boolean): number { let drawCalls = 0; switch (node.type) { case sys.RenderNodeType.BitmapNode: drawCalls = this.renderBitmap(<sys.BitmapNode>node, context); break; case sys.RenderNodeType.TextNode: drawCalls = 1; this.renderText(<sys.TextNode>node, context); break; case sys.RenderNodeType.GraphicsNode: drawCalls = this.renderGraphics(<sys.GraphicsNode>node, context, forHitTest); break; case sys.RenderNodeType.GroupNode: drawCalls = this.renderGroup(<sys.GroupNode>node, context); break; case sys.RenderNodeType.MeshNode: drawCalls = this.renderMesh(<sys.MeshNode>node, context); break; case sys.RenderNodeType.NormalBitmapNode: drawCalls += this.renderNormalBitmap(<sys.NormalBitmapNode>node, context); break; } return drawCalls; } private renderNormalBitmap(node: sys.NormalBitmapNode, context: CanvasRenderingContext2D): number { let image = node.image; if (!image || !image.source) { return 0; } if (context.$imageSmoothingEnabled != node.smoothing) { context.imageSmoothingEnabled = node.smoothing; context.$imageSmoothingEnabled = node.smoothing; } if (node.rotated) { let sourceX = node.sourceX; let sourceY = node.sourceY; let sourceHeight = node.sourceW; let sourceWidth = node.sourceH; let offsetX = node.drawX; let offsetY = node.drawY; let destHeight = node.drawW; let destWidth = node.drawH; if ((<any>context).saveTransform) {//for native (<any>context).saveTransform(); } else { context.save(); } context.transform(0, -1, 1, 0, 0, destWidth); context.drawImage(image.source, sourceX, sourceY, sourceWidth, sourceHeight, offsetX + context.$offsetX, offsetY + context.$offsetY, destWidth, destHeight); if ((<any>context).restoreTransform) {//for native (<any>context).restoreTransform(); } else { context.restore(); } } else { context.drawImage(image.source, node.sourceX, node.sourceY, node.sourceW, node.sourceH, node.drawX + context.$offsetX, node.drawY + context.$offsetY, node.drawW, node.drawH); } return 1; } private renderBitmap(node: sys.BitmapNode, context: CanvasRenderingContext2D): number { let image = node.image; if (!image || !image.source) { return 0; } if (context.$imageSmoothingEnabled != node.smoothing) { context.imageSmoothingEnabled = node.smoothing; context.$imageSmoothingEnabled = node.smoothing; } let data = node.drawData; let length = data.length; let pos = 0; let m = node.matrix; let blendMode = node.blendMode; let alpha = node.alpha; let saved = false; let offsetX; let offsetY; if (m) { if ((<any>context).saveTransform) {//for native (<any>context).saveTransform(); } else { context.save(); } saved = true; if (context.$offsetX != 0 || context.$offsetY != 0) { context.translate(context.$offsetX, context.$offsetY); offsetX = context.$offsetX; offsetY = context.$offsetY; context.$offsetX = context.$offsetY = 0; } context.transform(m.a, m.b, m.c, m.d, m.tx, m.ty); } //这里不考虑嵌套 if (blendMode) { context.globalCompositeOperation = blendModes[blendMode]; } let originAlpha: number; if (alpha == alpha) { originAlpha = context.globalAlpha; context.globalAlpha *= alpha; } let drawCalls: number = 0; let filter = node.filter; //todo 暂时只考虑绘制一次的情况 if (filter && length == 8) { if (Capabilities.runtimeType == RuntimeType.NATIVE) { // for native egret_native.Graphics.setGlobalShader(filter); drawCalls++; if (node.rotated) { let sourceX = data[0]; let sourceY = data[1]; let sourceHeight = data[2]; let sourceWidth = data[3]; let offsetX = data[4]; let offsetY = data[5]; let destHeight = data[6]; let destWidth = data[7]; if ((<any>context).saveTransform) {//for native (<any>context).saveTransform(); } else { context.save(); } context.transform(0, -1, 1, 0, 0, destWidth); context.drawImage(image.source, sourceX, sourceY, sourceWidth, sourceHeight, offsetX + context.$offsetX, offsetY + context.$offsetY, destWidth, destHeight); if ((<any>context).restoreTransform) {//for native (<any>context).restoreTransform(); } else { context.restore(); } } else { context.drawImage(image.source, data[0], data[1], data[2], data[3], data[4] + context.$offsetX, data[5] + context.$offsetY, data[6], data[7]); } egret_native.Graphics.setGlobalShader(null); } else { let sourceX = data[0]; let sourceY = data[1]; let sourceWidth = data[2]; let sourceHeight = data[3]; let offsetX = data[4]; let offsetY = data[5]; let destWidth = data[6]; let destHeight = data[7]; if (node.rotated) { sourceWidth = data[3]; sourceHeight = data[2]; destWidth = data[7]; destHeight = data[6]; } let displayBuffer = this.createRenderBuffer(destWidth, destHeight); let displayContext = displayBuffer.context; drawCalls++; if (node.rotated) { context.transform(0, -1, 1, 0, 0, destWidth); } displayContext.drawImage(image.source, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, destWidth, destHeight); //绘制结果到屏幕 drawCalls++; // 应用滤镜 let imageData = displayContext.getImageData(0, 0, destWidth, destHeight); colorFilter(imageData.data, destWidth, destHeight, (<ColorMatrixFilter>filter).$matrix); displayContext.putImageData(imageData, 0, 0); // 绘制结果的时候,应用滤镜 context.drawImage(displayBuffer.surface, 0, 0, destWidth, destHeight, offsetX + context.$offsetX, offsetY + context.$offsetY, destWidth, destHeight); renderBufferPool.push(displayBuffer); } } else { while (pos < length) { drawCalls++; if (node.rotated) { let sourceX = data[pos++]; let sourceY = data[pos++]; let sourceHeight = data[pos++]; let sourceWidth = data[pos++]; let offsetX = data[pos++]; let offsetY = data[pos++]; let destHeight = data[pos++]; let destWidth = data[pos++]; if ((<any>context).saveTransform) {//for native (<any>context).saveTransform(); } else { context.save(); } context.transform(0, -1, 1, 0, 0, destWidth); context.drawImage(image.source, sourceX, sourceY, sourceWidth, sourceHeight, offsetX + context.$offsetX, offsetY + context.$offsetY, destWidth, destHeight); if ((<any>context).restoreTransform) {//for native (<any>context).restoreTransform(); } else { context.restore(); } } else { context.drawImage(image.source, data[pos++], data[pos++], data[pos++], data[pos++], data[pos++] + context.$offsetX, data[pos++] + context.$offsetY, data[pos++], data[pos++]); } } } if (saved) { if ((<any>context).restoreTransform) {//for native (<any>context).restoreTransform(); if (blendMode) { context.globalCompositeOperation = defaultCompositeOp; } if (alpha == alpha) { context.globalAlpha = originAlpha; } } else { context.restore(); } } else { if (blendMode) { context.globalCompositeOperation = defaultCompositeOp; } if (alpha == alpha) { context.globalAlpha = originAlpha; } } if (offsetX) { context.$offsetX = offsetX; } if (offsetY) { context.$offsetY = offsetY; } return drawCalls; } private renderMesh(node: sys.MeshNode, context: any): number { if (Capabilities.runtimeType != RuntimeType.NATIVE) { return 0; } let image = node.image; let data = node.drawData; let length = data.length; let pos = 0; let m = node.matrix; let blendMode = node.blendMode; let alpha = node.alpha; let offsetX; let offsetY; if (m) { context.saveTransform(); if (context.$offsetX != 0 || context.$offsetY != 0) { context.translate(context.$offsetX, context.$offsetY); offsetX = context.$offsetX; offsetY = context.$offsetY; context.$offsetX = context.$offsetY = 0; } context.transform(m.a, m.b, m.c, m.d, m.tx, m.ty); } if (blendMode) { context.globalCompositeOperation = blendModes[blendMode]; } let originAlpha: number; if (alpha == alpha) { originAlpha = context.globalAlpha; context.globalAlpha *= alpha; } let drawCalls: number = 0; let filter = node.filter; if (filter) { egret_native.Graphics.setGlobalShader(filter); while (pos < length) { drawCalls++; context.drawMesh(image.source, data[pos++], data[pos++], data[pos++], data[pos++], data[pos++] + context.$offsetX, data[pos++] + context.$offsetY, data[pos++], data[pos++], node.imageWidth, node.imageHeight, node.uvs, node.vertices, node.indices, node.bounds); } egret_native.Graphics.setGlobalShader(null); } else { while (pos < length) { drawCalls++; context.drawMesh(image.source, data[pos++], data[pos++], data[pos++], data[pos++], data[pos++] + context.$offsetX, data[pos++] + context.$offsetY, data[pos++], data[pos++], node.imageWidth, node.imageHeight, node.uvs, node.vertices, node.indices, node.bounds); } } if (m) { context.restoreTransform(); } if (blendMode) { context.globalCompositeOperation = defaultCompositeOp; } if (alpha == alpha) { context.globalAlpha = originAlpha; } if (offsetX) { context.$offsetX = offsetX; } if (offsetY) { context.$offsetY = offsetY; } return 1; } public renderText(node: sys.TextNode, context: CanvasRenderingContext2D): void { context.textAlign = "left"; context.textBaseline = "middle"; context.lineJoin = "round";//确保描边样式是圆角 let drawData = node.drawData; let length = drawData.length; let pos = 0; while (pos < length) { let x = drawData[pos++]; let y = drawData[pos++]; let text = drawData[pos++]; let format: sys.TextFormat = drawData[pos++]; context.font = getFontString(node, format); let textColor = format.textColor == null ? node.textColor : format.textColor; let strokeColor = format.strokeColor == null ? node.strokeColor : format.strokeColor; let stroke = format.stroke == null ? node.stroke : format.stroke; context.fillStyle = toColorString(textColor); context.strokeStyle = toColorString(strokeColor); if (stroke) { context.lineWidth = stroke * 2; context.strokeText(text, x + context.$offsetX, y + context.$offsetY); } context.fillText(text, x + context.$offsetX, y + context.$offsetY); } } private renderingMask = false; /** * @private */ public renderGraphics(node: sys.GraphicsNode, context: CanvasRenderingContext2D, forHitTest?: boolean): number { let drawData = node.drawData; let length = drawData.length; forHitTest = !!forHitTest; for (let i = 0; i < length; i++) { let path: sys.Path2D = drawData[i]; switch (path.type) { case sys.PathType.Fill: let fillPath = <sys.FillPath>path; context.fillStyle = forHitTest ? BLACK_COLOR : getRGBAString(fillPath.fillColor, fillPath.fillAlpha); this.renderPath(path, context); if (this.renderingMask) { context.clip(); } else { context.fill(); } break; case sys.PathType.GradientFill: let g = <sys.GradientFillPath>path; context.fillStyle = forHitTest ? BLACK_COLOR : getGradient(context, g.gradientType, g.colors, g.alphas, g.ratios, g.matrix); context.save(); let m = g.matrix; this.renderPath(path, context); context.transform(m.a, m.b, m.c, m.d, m.tx, m.ty); context.fill(); context.restore(); break; case sys.PathType.Stroke: let strokeFill = <sys.StrokePath>path; let lineWidth = strokeFill.lineWidth; context.lineWidth = lineWidth; context.strokeStyle = forHitTest ? BLACK_COLOR : getRGBAString(strokeFill.lineColor, strokeFill.lineAlpha); context.lineCap = CAPS_STYLES[strokeFill.caps]; context.lineJoin = strokeFill.joints; context.miterLimit = strokeFill.miterLimit; //对1像素和3像素特殊处理,向右下角偏移0.5像素,以显示清晰锐利的线条。 let isSpecialCaseWidth = lineWidth === 1 || lineWidth === 3; if (isSpecialCaseWidth) { context.translate(0.5, 0.5); } this.renderPath(path, context); context.stroke(); if (isSpecialCaseWidth) { context.translate(-0.5, -0.5); } break; } } return length == 0 ? 0 : 1; } private renderPath(path: sys.Path2D, context: CanvasRenderingContext2D): void { context.beginPath(); let data = path.$data; let commands = path.$commands; let commandCount = commands.length; let pos = 0; for (let commandIndex = 0; commandIndex < commandCount; commandIndex++) { let command = commands[commandIndex]; switch (command) { case sys.PathCommand.CubicCurveTo: context.bezierCurveTo(data[pos++] + context.$offsetX, data[pos++] + context.$offsetY, data[pos++] + context.$offsetX, data[pos++] + context.$offsetY, data[pos++] + context.$offsetX, data[pos++] + context.$offsetY); break; case sys.PathCommand.CurveTo: context.quadraticCurveTo(data[pos++] + context.$offsetX, data[pos++] + context.$offsetY, data[pos++] + context.$offsetX, data[pos++] + context.$offsetY); break; case sys.PathCommand.LineTo: context.lineTo(data[pos++] + context.$offsetX, data[pos++] + context.$offsetY); break; case sys.PathCommand.MoveTo: context.moveTo(data[pos++] + context.$offsetX, data[pos++] + context.$offsetY); break; case sys.PathCommand.SetLineDash: context.setLineDash(data[pos++]); break; } } } private renderGroup(groupNode: sys.GroupNode, context: CanvasRenderingContext2D): number { let m = groupNode.matrix; let saved = false; let offsetX; let offsetY; if (m) { if ((<any>context).saveTransform) {//for native (<any>context).saveTransform(); } else { context.save(); } saved = true; if (context.$offsetX != 0 || context.$offsetY != 0) { context.translate(context.$offsetX, context.$offsetY); offsetX = context.$offsetX; offsetY = context.$offsetY; context.$offsetX = context.$offsetY = 0; } context.transform(m.a, m.b, m.c, m.d, m.tx, m.ty); } let drawCalls: number = 0; let children = groupNode.drawData; let length = children.length; for (let i = 0; i < length; i++) { let node: sys.RenderNode = children[i]; drawCalls += this.renderNode(node, context); } if (saved) { if ((<any>context).restoreTransform) {//for native (<any>context).restoreTransform(); } else { context.restore(); } } if (offsetX) { context.$offsetX = offsetX; } if (offsetY) { context.$offsetY = offsetY; } return drawCalls; } private createRenderBuffer(width: number, height: number, useForFilters?: boolean): sys.RenderBuffer { let buffer = useForFilters ? renderBufferPool_Filters.pop() : renderBufferPool.pop(); if (buffer) { buffer.resize(width, height, true); } else { buffer = new sys.CanvasRenderBuffer(width, height); } return buffer; } } /** * @private * 获取字体字符串 */ function getFontString(node: sys.TextNode, format: sys.TextFormat): string { let italic: boolean = format.italic == null ? node.italic : format.italic; let bold: boolean = format.bold == null ? node.bold : format.bold; let size: number = format.size == null ? node.size : format.size; let fontFamily: string = format.fontFamily || node.fontFamily; let font: string = italic ? "italic " : "normal "; font += bold ? "bold " : "normal "; font += size + "px " + fontFamily; return font; } /** * @private * 获取RGBA字符串 */ function getRGBAString(color: number, alpha: number): string { let red = color >> 16; let green = (color >> 8) & 0xFF; let blue = color & 0xFF; return "rgba(" + red + "," + green + "," + blue + "," + alpha + ")"; } /** * @private * 获取渐变填充样式对象 */ function getGradient(context: CanvasRenderingContext2D, type: string, colors: number[], alphas: number[], ratios: number[], matrix: Matrix): CanvasGradient { let gradie