dragonbones-runtime
Version:
the tools to build dragonbones file for diffrent framework
1,077 lines (1,031 loc) • 67.3 kB
text/typescript
//////////////////////////////////////////////////////////////////////////////////////
//
// 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