(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@antv/g-lite')) :
typeof define === 'function' && define.amd ? define(['exports', '@antv/g-lite'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.G = global.G || {}, global.G.CanvasRenderer = {}), global.window.G));
}(this, (function (exports, gLite) { 'use strict';
function _extends() {
_extends = Object.assign ? Object.assign.bind() : function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _inheritsLoose(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
_setPrototypeOf(subClass, superClass);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
/**
* Common utilities
* @module glMatrix
*/
var ARRAY_TYPE = typeof Float32Array !== 'undefined' ? Float32Array : Array;
if (!Math.hypot) Math.hypot = function () {
var y = 0,
i = arguments.length;
while (i--) {
y += arguments[i] * arguments[i];
}
return Math.sqrt(y);
};
/**
* 4x4 Matrix
Format: column-major, when typed out it looks like row-major
The matrices are being post multiplied.
* @module mat4
*/
/**
* Creates a new identity mat4
*
* @returns {mat4} a new 4x4 matrix
*/
function create() {
var out = new ARRAY_TYPE(16);
if (ARRAY_TYPE != Float32Array) {
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[11] = 0;
out[12] = 0;
out[13] = 0;
out[14] = 0;
}
out[0] = 1;
out[5] = 1;
out[10] = 1;
out[15] = 1;
return out;
}
/**
* Copy the values from one mat4 to another
*
* @param {mat4} out the receiving matrix
* @param {ReadonlyMat4} a the source matrix
* @returns {mat4} out
*/
function copy(out, a) {
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
out[4] = a[4];
out[5] = a[5];
out[6] = a[6];
out[7] = a[7];
out[8] = a[8];
out[9] = a[9];
out[10] = a[10];
out[11] = a[11];
out[12] = a[12];
out[13] = a[13];
out[14] = a[14];
out[15] = a[15];
return out;
}
/**
* Multiplies two mat4s
*
* @param {mat4} out the receiving matrix
* @param {ReadonlyMat4} a the first operand
* @param {ReadonlyMat4} b the second operand
* @returns {mat4} out
*/
function multiply(out, a, b) {
var a00 = a[0],
a01 = a[1],
a02 = a[2],
a03 = a[3];
var a10 = a[4],
a11 = a[5],
a12 = a[6],
a13 = a[7];
var a20 = a[8],
a21 = a[9],
a22 = a[10],
a23 = a[11];
var a30 = a[12],
a31 = a[13],
a32 = a[14],
a33 = a[15]; // Cache only the current line of the second matrix
var b0 = b[0],
b1 = b[1],
b2 = b[2],
b3 = b[3];
out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
out[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
b0 = b[4];
b1 = b[5];
b2 = b[6];
b3 = b[7];
out[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
out[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
out[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
out[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
b0 = b[8];
b1 = b[9];
b2 = b[10];
b3 = b[11];
out[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
out[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
out[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
out[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
b0 = b[12];
b1 = b[13];
b2 = b[14];
b3 = b[15];
out[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
out[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
out[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
out[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
return out;
}
/**
* Translate a mat4 by the given vector
*
* @param {mat4} out the receiving matrix
* @param {ReadonlyMat4} a the matrix to translate
* @param {ReadonlyVec3} v vector to translate by
* @returns {mat4} out
*/
function translate(out, a, v) {
var x = v[0],
y = v[1],
z = v[2];
var a00, a01, a02, a03;
var a10, a11, a12, a13;
var a20, a21, a22, a23;
if (a === out) {
out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
} else {
a00 = a[0];
a01 = a[1];
a02 = a[2];
a03 = a[3];
a10 = a[4];
a11 = a[5];
a12 = a[6];
a13 = a[7];
a20 = a[8];
a21 = a[9];
a22 = a[10];
a23 = a[11];
out[0] = a00;
out[1] = a01;
out[2] = a02;
out[3] = a03;
out[4] = a10;
out[5] = a11;
out[6] = a12;
out[7] = a13;
out[8] = a20;
out[9] = a21;
out[10] = a22;
out[11] = a23;
out[12] = a00 * x + a10 * y + a20 * z + a[12];
out[13] = a01 * x + a11 * y + a21 * z + a[13];
out[14] = a02 * x + a12 * y + a22 * z + a[14];
out[15] = a03 * x + a13 * y + a23 * z + a[15];
}
return out;
}
/**
* Creates a matrix from a vector scaling
* This is equivalent to (but much faster than):
*
* mat4.identity(dest);
* mat4.scale(dest, dest, vec);
*
* @param {mat4} out mat4 receiving operation result
* @param {ReadonlyVec3} v Scaling vector
* @returns {mat4} out
*/
function fromScaling(out, v) {
out[0] = v[0];
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = v[1];
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[10] = v[2];
out[11] = 0;
out[12] = 0;
out[13] = 0;
out[14] = 0;
out[15] = 1;
return out;
}
/**
* 3 Dimensional Vector
* @module vec3
*/
/**
* Creates a new, empty vec3
*
* @returns {vec3} a new 3D vector
*/
function create$1() {
var out = new ARRAY_TYPE(3);
if (ARRAY_TYPE != Float32Array) {
out[0] = 0;
out[1] = 0;
out[2] = 0;
}
return out;
}
/**
* Transforms the vec3 with a mat4.
* 4th vector component is implicitly '1'
*
* @param {vec3} out the receiving vector
* @param {ReadonlyVec3} a the vector to transform
* @param {ReadonlyMat4} m matrix to transform with
* @returns {vec3} out
*/
function transformMat4(out, a, m) {
var x = a[0],
y = a[1],
z = a[2];
var w = m[3] * x + m[7] * y + m[11] * z + m[15];
w = w || 1.0;
out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w;
out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w;
out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w;
return out;
}
/**
* Perform some operation over an array of vec3s.
*
* @param {Array} a the array of vectors to iterate over
* @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed
* @param {Number} offset Number of elements to skip at the beginning of the array
* @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array
* @param {Function} fn Function to call for each vector in the array
* @param {Object} [arg] additional argument to pass to fn
* @returns {Array} a
* @function
*/
var forEach = function () {
var vec = create$1();
return function (a, stride, offset, count, fn, arg) {
var i, l;
if (!stride) {
stride = 3;
}
if (!offset) {
offset = 0;
}
if (count) {
l = Math.min(count * stride + offset, a.length);
} else {
l = a.length;
}
for (i = offset; i < l; i += stride) {
vec[0] = a[i];
vec[1] = a[i + 1];
vec[2] = a[i + 2];
fn(vec, vec, arg);
a[i] = vec[0];
a[i + 1] = vec[1];
a[i + 2] = vec[2];
}
return a;
};
}();
// isFinite,
var isNil = function (value) {
/**
* isNil(null) => true
* isNil() => true
*/
return value === null || value === undefined;
};
var toString = {}.toString;
var isType = function (value, type) { return toString.call(value) === '[object ' + type + ']'; };
var isString = (function (str) {
return isType(str, 'String');
});
/**
* support 2 modes in rendering:
* * immediate
* * delayed: render at the end of frame with dirty-rectangle
*/
var CanvasRendererPlugin = /*#__PURE__*/function () {
function CanvasRendererPlugin(canvasRendererPluginOptions) {
this.canvasRendererPluginOptions = void 0;
this.context = void 0;
this.pathGeneratorFactory = void 0;
/**
* RBush used in dirty rectangle rendering
*/
this.rBush = void 0;
this.removedRBushNodeAABBs = [];
this.renderQueue = [];
/**
* This stack is only used by clipPath for now.
*/
this.restoreStack = [];
this.clearFullScreen = false;
/**
* view projection matrix
*/
this.vpMatrix = create();
this.dprMatrix = create();
this.tmpMat4 = create();
this.vec3a = create$1();
this.vec3b = create$1();
this.vec3c = create$1();
this.vec3d = create$1();
this.canvasRendererPluginOptions = canvasRendererPluginOptions;
}
var _proto = CanvasRendererPlugin.prototype;
_proto.apply = function apply(context, runtime) {
var _this = this;
this.context = context;
var config = context.config,
camera = context.camera,
renderingService = context.renderingService,
renderingContext = context.renderingContext,
rBushRoot = context.rBushRoot,
pathGeneratorFactory = context.pathGeneratorFactory;
this.rBush = rBushRoot;
this.pathGeneratorFactory = pathGeneratorFactory;
var contextService = context.contextService;
var canvas = renderingContext.root.ownerDocument.defaultView;
var handleUnmounted = function handleUnmounted(e) {
var object = e.target;
// remove r-bush node
// @ts-ignore
var rBushNode = object.rBushNode;
if (rBushNode.aabb) {
// save removed aabbs for dirty-rectangle rendering later
_this.removedRBushNodeAABBs.push(rBushNode.aabb);
}
};
var handleCulled = function handleCulled(e) {
var object = e.target;
// @ts-ignore
var rBushNode = object.rBushNode;
if (rBushNode.aabb) {
// save removed aabbs for dirty-rectangle rendering later
_this.removedRBushNodeAABBs.push(rBushNode.aabb);
}
};
renderingService.hooks.init.tap(CanvasRendererPlugin.tag, function () {
canvas.addEventListener(gLite.ElementEvent.UNMOUNTED, handleUnmounted);
canvas.addEventListener(gLite.ElementEvent.CULLED, handleCulled);
// clear fullscreen
var dpr = contextService.getDPR();
var width = config.width,
height = config.height;
var context = contextService.getContext();
_this.clearRect(context, 0, 0, width * dpr, height * dpr, config.background);
});
renderingService.hooks.destroy.tap(CanvasRendererPlugin.tag, function () {
canvas.removeEventListener(gLite.ElementEvent.UNMOUNTED, handleUnmounted);
canvas.removeEventListener(gLite.ElementEvent.CULLED, handleCulled);
// this.renderQueue = [];
// this.removedRBushNodeAABBs = [];
// this.restoreStack = [];
});
renderingService.hooks.beginFrame.tap(CanvasRendererPlugin.tag, function () {
var context = contextService.getContext();
var dpr = contextService.getDPR();
var width = config.width,
height = config.height;
var _this$canvasRendererP = _this.canvasRendererPluginOptions,
dirtyObjectNumThreshold = _this$canvasRendererP.dirtyObjectNumThreshold,
dirtyObjectRatioThreshold = _this$canvasRendererP.dirtyObjectRatioThreshold;
// some heuristic conditions such as 80% object changed
var _renderingService$get = renderingService.getStats(),
total = _renderingService$get.total,
rendered = _renderingService$get.rendered;
var ratio = rendered / total;
_this.clearFullScreen = renderingService.disableDirtyRectangleRendering() || rendered > dirtyObjectNumThreshold && ratio > dirtyObjectRatioThreshold;
if (context) {
context.resetTransform ? context.resetTransform() : context.setTransform(1, 0, 0, 1, 0, 0);
if (_this.clearFullScreen) {
_this.clearRect(context, 0, 0, width * dpr, height * dpr, config.background);
}
}
});
var renderByZIndex = function renderByZIndex(object, context) {
if (object.isVisible() && !object.isCulled()) {
_this.renderDisplayObject(object, context, _this.context, _this.restoreStack, runtime);
// if (object.renderable.) {
// if we did a full screen rendering last frame
_this.saveDirtyAABB(object);
// }
}
var sorted = object.sortable.sorted || object.childNodes;
// should account for z-index
sorted.forEach(function (child) {
renderByZIndex(child, context);
});
};
// render at the end of frame
renderingService.hooks.endFrame.tap(CanvasRendererPlugin.tag, function () {
var context = contextService.getContext();
// clear & clip dirty rectangle
var dpr = contextService.getDPR();
fromScaling(_this.dprMatrix, [dpr, dpr, 1]);
multiply(_this.vpMatrix, _this.dprMatrix, camera.getOrthoMatrix());
// if (this.clearFullScreen) {
if (_this.clearFullScreen) {
// console.log('canvas renderer fcp...');
renderByZIndex(renderingContext.root, context);
} else {
// console.log('canvas renderer next...');
// merge removed AABB
var dirtyRenderBounds = _this.safeMergeAABB.apply(_this, [_this.mergeDirtyAABBs(_this.renderQueue)].concat(_this.removedRBushNodeAABBs.map(function (_ref) {
var minX = _ref.minX,
minY = _ref.minY,
maxX = _ref.maxX,
maxY = _ref.maxY;
var aabb = new gLite.AABB();
aabb.setMinMax(
// vec3.fromValues(minX, minY, 0),
// vec3.fromValues(maxX, maxY, 0),
[minX, minY, 0], [maxX, maxY, 0]);
return aabb;
})));
_this.removedRBushNodeAABBs = [];
if (gLite.AABB.isEmpty(dirtyRenderBounds)) {
_this.renderQueue = [];
return;
}
var dirtyRect = _this.convertAABB2Rect(dirtyRenderBounds);
var x = dirtyRect.x,
y = dirtyRect.y,
width = dirtyRect.width,
height = dirtyRect.height;
var tl = transformMat4(_this.vec3a, [x, y, 0], _this.vpMatrix);
var tr = transformMat4(_this.vec3b, [x + width, y, 0], _this.vpMatrix);
var bl = transformMat4(_this.vec3c, [x, y + height, 0], _this.vpMatrix);
var br = transformMat4(_this.vec3d, [x + width, y + height, 0], _this.vpMatrix);
var minx = Math.min(tl[0], tr[0], br[0], bl[0]);
var miny = Math.min(tl[1], tr[1], br[1], bl[1]);
var maxx = Math.max(tl[0], tr[0], br[0], bl[0]);
var maxy = Math.max(tl[1], tr[1], br[1], bl[1]);
var ix = Math.floor(minx);
var iy = Math.floor(miny);
var iwidth = Math.ceil(maxx - minx);
var iheight = Math.ceil(maxy - miny);
context.save();
_this.clearRect(context, ix, iy, iwidth, iheight, config.background);
context.beginPath();
context.rect(ix, iy, iwidth, iheight);
context.clip();
// @see https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Transformations
context.setTransform(_this.vpMatrix[0], _this.vpMatrix[1], _this.vpMatrix[4], _this.vpMatrix[5], _this.vpMatrix[12], _this.vpMatrix[13]);
// draw dirty rectangle
var _config$renderer$getC = config.renderer.getConfig(),
enableDirtyRectangleRenderingDebug = _config$renderer$getC.enableDirtyRectangleRenderingDebug;
if (enableDirtyRectangleRenderingDebug) {
canvas.dispatchEvent(new gLite.CustomEvent(gLite.CanvasEvent.DIRTY_RECTANGLE, {
dirtyRect: {
x: ix,
y: iy,
width: iwidth,
height: iheight
}
}));
}
// search objects intersect with dirty rectangle
var dirtyObjects = _this.searchDirtyObjects(dirtyRenderBounds);
// do rendering
dirtyObjects
// sort by z-index
.sort(function (a, b) {
return a.sortable.renderOrder - b.sortable.renderOrder;
}).forEach(function (object) {
// culled object should not be rendered
if (object && object.isVisible() && !object.isCulled()) {
_this.renderDisplayObject(object, context, _this.context, _this.restoreStack, runtime);
}
});
context.restore();
// save dirty AABBs in last frame
_this.renderQueue.forEach(function (object) {
_this.saveDirtyAABB(object);
});
// clear queue
_this.renderQueue = [];
}
// pop restore stack, eg. root -> parent -> child
_this.restoreStack.forEach(function () {
context.restore();
});
// clear restore stack
_this.restoreStack = [];
});
renderingService.hooks.render.tap(CanvasRendererPlugin.tag, function (object) {
if (!_this.clearFullScreen) {
// render at the end of frame
_this.renderQueue.push(object);
}
});
};
_proto.clearRect = function clearRect(context, x, y, width, height, background) {
// clearRect is faster than fillRect @see https://stackoverflow.com/a/30830253
context.clearRect(x, y, width, height);
if (background) {
context.fillStyle = background;
context.fillRect(x, y, width, height);
}
};
_proto.renderDisplayObject = function renderDisplayObject(object, context, canvasContext, restoreStack, runtime) {
var nodeName = object.nodeName;
// console.log('canvas render:', object);
// restore to its ancestor
var parent = restoreStack[restoreStack.length - 1];
if (parent && !(object.compareDocumentPosition(parent) & Node.DOCUMENT_POSITION_CONTAINS)) {
context.restore();
restoreStack.pop();
}
// @ts-ignore
var styleRenderer = this.context.styleRendererFactory[nodeName];
var generatePath = this.pathGeneratorFactory[nodeName];
// clip path
var clipPath = object.parsedStyle.clipPath;
if (clipPath) {
this.applyWorldTransform(context, clipPath);
// generate path in local space
var _generatePath = this.pathGeneratorFactory[clipPath.nodeName];
if (_generatePath) {
context.save();
// save clip
restoreStack.push(object);
context.beginPath();
_generatePath(context, clipPath.parsedStyle);
context.closePath();
context.clip();
}
}
// fill & stroke
if (styleRenderer) {
this.applyWorldTransform(context, object);
context.save();
// apply attributes to context
this.applyAttributesToContext(context, object);
}
if (generatePath) {
context.beginPath();
generatePath(context, object.parsedStyle);
if (object.nodeName !== gLite.Shape.LINE && object.nodeName !== gLite.Shape.PATH && object.nodeName !== gLite.Shape.POLYLINE) {
context.closePath();
}
}
// fill & stroke
if (styleRenderer) {
styleRenderer.render(context, object.parsedStyle, object, canvasContext, this, runtime);
// restore applied attributes, eg. shadowBlur shadowColor...
context.restore();
}
// finish rendering, clear dirty flag
object.renderable.dirty = false;
};
_proto.convertAABB2Rect = function convertAABB2Rect(aabb) {
var min = aabb.getMin();
var max = aabb.getMax();
// expand the rectangle a bit to avoid artifacts
// @see https://www.yuque.com/antv/ou292n/bi8nix#ExvCu
var minX = Math.floor(min[0]);
var minY = Math.floor(min[1]);
var maxX = Math.ceil(max[0]);
var maxY = Math.ceil(max[1]);
var width = maxX - minX;
var height = maxY - minY;
return {
x: minX,
y: minY,
width: width,
height: height
};
}
/**
* TODO: merge dirty rectangles with some strategies.
* For now, we just simply merge all the rectangles into one.
* @see https://idom.me/articles/841.html
*/;
_proto.mergeDirtyAABBs = function mergeDirtyAABBs(dirtyObjects) {
// merge into a big AABB
// TODO: skip descendant if ancestor is caculated, but compareNodePosition is really slow
var aabb = new gLite.AABB();
dirtyObjects.forEach(function (object) {
var renderBounds = object.getRenderBounds();
aabb.add(renderBounds);
var dirtyRenderBounds = object.renderable.dirtyRenderBounds;
if (dirtyRenderBounds) {
aabb.add(dirtyRenderBounds);
}
});
return aabb;
};
_proto.searchDirtyObjects = function searchDirtyObjects(dirtyRectangle) {
// search in r-tree, get all affected nodes
var _dirtyRectangle$getMi = dirtyRectangle.getMin(),
minX = _dirtyRectangle$getMi[0],
minY = _dirtyRectangle$getMi[1];
var _dirtyRectangle$getMa = dirtyRectangle.getMax(),
maxX = _dirtyRectangle$getMa[0],
maxY = _dirtyRectangle$getMa[1];
var rBushNodes = this.rBush.search({
minX: minX,
minY: minY,
maxX: maxX,
maxY: maxY
});
return rBushNodes.map(function (_ref2) {
var displayObject = _ref2.displayObject;
return displayObject;
});
};
_proto.saveDirtyAABB = function saveDirtyAABB(object) {
var renderable = object.renderable;
if (!renderable.dirtyRenderBounds) {
renderable.dirtyRenderBounds = new gLite.AABB();
}
var renderBounds = object.getRenderBounds();
if (renderBounds) {
// save last dirty aabb
renderable.dirtyRenderBounds.update(renderBounds.center, renderBounds.halfExtents);
}
}
/**
* TODO: batch the same global attributes
*/;
_proto.applyAttributesToContext = function applyAttributesToContext(context, object) {
var _object$parsedStyle = object.parsedStyle,
stroke = _object$parsedStyle.stroke,
fill = _object$parsedStyle.fill,
opacity = _object$parsedStyle.opacity,
lineDash = _object$parsedStyle.lineDash,
lineDashOffset = _object$parsedStyle.lineDashOffset;
// @see https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/setLineDash
if (lineDash) {
context.setLineDash(lineDash);
}
// @see https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/lineDashOffset
if (!isNil(lineDashOffset)) {
context.lineDashOffset = lineDashOffset;
}
if (!isNil(opacity)) {
context.globalAlpha *= opacity;
}
if (!isNil(stroke) && !Array.isArray(stroke) && !stroke.isNone) {
context.strokeStyle = object.attributes.stroke;
}
if (!isNil(fill) && !Array.isArray(fill) && !fill.isNone) {
context.fillStyle = object.attributes.fill;
}
};
_proto.applyWorldTransform = function applyWorldTransform(context, object, matrix) {
var tx = 0;
var ty = 0;
var _ref3 = object.parsedStyle || {},
anchor = _ref3.anchor;
var anchorX = anchor && anchor[0] || 0;
var anchorY = anchor && anchor[1] || 0;
if (anchorX !== 0 || anchorY !== 0) {
// const bounds = object.getGeometryBounds();
var bounds = object.geometry.contentBounds;
var width = bounds && bounds.halfExtents[0] * 2 || 0;
var height = bounds && bounds.halfExtents[1] * 2 || 0;
tx = -(anchorX * width);
ty = -(anchorY * height);
}
// apply clip shape's RTS
if (matrix) {
copy(this.tmpMat4, object.getLocalTransform());
this.vec3a[0] = tx;
this.vec3a[1] = ty;
this.vec3a[2] = 0;
translate(this.tmpMat4, this.tmpMat4, this.vec3a);
multiply(this.tmpMat4, matrix, this.tmpMat4);
multiply(this.tmpMat4, this.vpMatrix, this.tmpMat4);
} else {
// apply RTS transformation in world space
copy(this.tmpMat4, object.getWorldTransform());
this.vec3a[0] = tx;
this.vec3a[1] = ty;
this.vec3a[2] = 0;
translate(this.tmpMat4, this.tmpMat4, this.vec3a);
multiply(this.tmpMat4, this.vpMatrix, this.tmpMat4);
}
// @see https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Transformations
context.setTransform(this.tmpMat4[0], this.tmpMat4[1], this.tmpMat4[4], this.tmpMat4[5], this.tmpMat4[12], this.tmpMat4[13]);
};
_proto.safeMergeAABB = function safeMergeAABB() {
var merged = new gLite.AABB();
for (var _len = arguments.length, aabbs = new Array(_len), _key = 0; _key < _len; _key++) {
aabbs[_key] = arguments[_key];
}
aabbs.forEach(function (aabb) {
merged.add(aabb);
});
return merged;
};
return CanvasRendererPlugin;
}();
CanvasRendererPlugin.tag = 'CanvasRenderer';
var DefaultRenderer = /*#__PURE__*/function () {
function DefaultRenderer(imagePool) {
this.imagePool = void 0;
this.imagePool = imagePool;
}
var _proto = DefaultRenderer.prototype;
_proto.render = function render(context, parsedStyle, object, canvasContext, plugin, runtime) {
var fill = parsedStyle.fill,
fillRule = parsedStyle.fillRule,
opacity = parsedStyle.opacity,
fillOpacity = parsedStyle.fillOpacity,
stroke = parsedStyle.stroke,
strokeOpacity = parsedStyle.strokeOpacity,
lineWidth = parsedStyle.lineWidth,
lineCap = parsedStyle.lineCap,
lineJoin = parsedStyle.lineJoin,
shadowType = parsedStyle.shadowType,
shadowColor = parsedStyle.shadowColor,
shadowBlur = parsedStyle.shadowBlur,
filter = parsedStyle.filter,
miterLimit = parsedStyle.miterLimit;
var hasFill = !isNil(fill) && !fill.isNone;
var hasStroke = !isNil(stroke) && !stroke.isNone && lineWidth > 0;
var isFillTransparent = fill.alpha === 0;
var hasFilter = !!(filter && filter.length);
var hasShadow = !isNil(shadowColor) && shadowBlur > 0;
var nodeName = object.nodeName;
var isInnerShadow = shadowType === 'inner';
var shouldDrawShadowWithStroke = hasStroke && hasShadow && (nodeName === gLite.Shape.PATH || nodeName === gLite.Shape.LINE || nodeName === gLite.Shape.POLYLINE || isFillTransparent || isInnerShadow);
if (hasFill) {
context.globalAlpha = opacity * fillOpacity;
if (!shouldDrawShadowWithStroke) {
setShadowAndFilter(object, context, hasShadow);
}
this.fill(context, object, fill, fillRule, canvasContext, plugin, runtime);
if (!shouldDrawShadowWithStroke) {
this.clearShadowAndFilter(context, hasFilter, hasShadow);
}
}
if (hasStroke) {
context.globalAlpha = opacity * strokeOpacity;
context.lineWidth = lineWidth;
if (!isNil(miterLimit)) {
context.miterLimit = miterLimit;
}
if (!isNil(lineCap)) {
context.lineCap = lineCap;
}
if (!isNil(lineJoin)) {
context.lineJoin = lineJoin;
}
if (shouldDrawShadowWithStroke) {
if (isInnerShadow) {
context.globalCompositeOperation = 'source-atop';
}
setShadowAndFilter(object, context, true);
if (isInnerShadow) {
this.stroke(context, object, stroke, canvasContext, plugin, runtime);
context.globalCompositeOperation = 'source-over';
this.clearShadowAndFilter(context, hasFilter, true);
}
}
this.stroke(context, object, stroke, canvasContext, plugin, runtime);
}
};
_proto.clearShadowAndFilter = function clearShadowAndFilter(context, hasFilter, hasShadow) {
if (hasShadow) {
context.shadowColor = 'transparent';
context.shadowBlur = 0;
}
if (hasFilter) {
// save drop-shadow filter
var oldFilter = context.filter;
if (!isNil(oldFilter) && oldFilter.indexOf('drop-shadow') > -1) {
context.filter = oldFilter.replace(/drop-shadow\([^)]*\)/, '').trim() || 'none';
}
}
};
_proto.fill = function fill(context, object, _fill, fillRule, canvasContext, plugin, runtime) {
var _this = this;
if (Array.isArray(_fill)) {
_fill.forEach(function (gradient) {
context.fillStyle = _this.getColor(gradient, object, context);
fillRule ? context.fill(fillRule) : context.fill();
});
} else {
if (gLite.isPattern(_fill)) {
context.fillStyle = this.getPattern(_fill, object, context, canvasContext, plugin, runtime);
}
fillRule ? context.fill(fillRule) : context.fill();
}
};
_proto.stroke = function stroke(context, object, _stroke, canvasContext, plugin, runtime) {
var _this2 = this;
if (Array.isArray(_stroke)) {
_stroke.forEach(function (gradient) {
context.strokeStyle = _this2.getColor(gradient, object, context);
context.stroke();
});
} else {
if (gLite.isPattern(_stroke)) {
context.strokeStyle = this.getPattern(_stroke, object, context, canvasContext, plugin, runtime);
}
context.stroke();
}
};
_proto.getPattern = function getPattern(pattern, object, context, canvasContext, plugin, runtime) {
var $offscreenCanvas;
var dpr;
if (pattern.image.nodeName === 'rect') {
var _pattern$image$parsed = pattern.image.parsedStyle,
width = _pattern$image$parsed.width,
height = _pattern$image$parsed.height;
dpr = canvasContext.contextService.getDPR();
var offscreenCanvas = canvasContext.config.offscreenCanvas;
$offscreenCanvas = runtime.offscreenCanvas.getOrCreateCanvas(offscreenCanvas);
$offscreenCanvas.width = width * dpr;
$offscreenCanvas.height = height * dpr;
var offscreenCanvasContext = runtime.offscreenCanvas.getOrCreateContext(offscreenCanvas);
var restoreStack = [];
// offscreenCanvasContext.scale(1 / dpr, 1 / dpr);
pattern.image.forEach(function (object) {
plugin.renderDisplayObject(object, offscreenCanvasContext, canvasContext, restoreStack, runtime);
});
restoreStack.forEach(function () {
offscreenCanvasContext.restore();
});
}
var canvasPattern = this.imagePool.getOrCreatePatternSync(pattern, context, $offscreenCanvas, dpr, function () {
// set dirty rectangle flag
object.renderable.dirty = true;
canvasContext.renderingService.dirtify();
});
return canvasPattern;
};
_proto.getColor = function getColor(parsedColor, object, context) {
var color;
if (parsedColor.type === gLite.GradientType.LinearGradient || parsedColor.type === gLite.GradientType.RadialGradient) {
var bounds = object.getGeometryBounds();
var width = bounds && bounds.halfExtents[0] * 2 || 1;
var height = bounds && bounds.halfExtents[1] * 2 || 1;
color = this.imagePool.getOrCreateGradient(_extends({
type: parsedColor.type
}, parsedColor.value, {
width: width,
height: height
}), context);
}
return color;
};
return DefaultRenderer;
}();
/**
* apply before fill and stroke but only once
*/
function setShadowAndFilter(object, context, hasShadow) {
var _object$parsedStyle = object.parsedStyle,
filter = _object$parsedStyle.filter,
shadowColor = _object$parsedStyle.shadowColor,
shadowBlur = _object$parsedStyle.shadowBlur,
shadowOffsetX = _object$parsedStyle.shadowOffsetX,
shadowOffsetY = _object$parsedStyle.shadowOffsetY;
if (filter && filter.length) {
// use raw filter string
context.filter = object.style.filter;
}
if (hasShadow) {
context.shadowColor = shadowColor.toString();
context.shadowBlur = shadowBlur || 0;
context.shadowOffsetX = shadowOffsetX || 0;
context.shadowOffsetY = shadowOffsetY || 0;
}
}
var ImageRenderer = /*#__PURE__*/function () {
function ImageRenderer(imagePool) {
this.imagePool = void 0;
this.imagePool = imagePool;
}
var _proto = ImageRenderer.prototype;
_proto.render = function render(context, parsedStyle, object) {
var width = parsedStyle.width,
height = parsedStyle.height,
img = parsedStyle.img,
shadowColor = parsedStyle.shadowColor,
shadowBlur = parsedStyle.shadowBlur;
var image;
var iw = width;
var ih = height;
if (isString(img)) {
// image has been loaded in `mounted` hook
image = this.imagePool.getImageSync(img);
} else {
iw || (iw = img.width);
ih || (ih = img.height);
image = img;
}
if (image) {
var hasShadow = !isNil(shadowColor) && shadowBlur > 0;
setShadowAndFilter(object, context, hasShadow);
// node-canvas will throw the following err:
// Error: Image given has not completed loading
try {
context.drawImage(image, 0, 0, iw, ih);
} catch (e) {}
}
};
return ImageRenderer;
}();
var TextRenderer = /*#__PURE__*/function () {
function TextRenderer() {}
var _proto = TextRenderer.prototype;
_proto.render = function render(context, parsedStyle, object) {
var lineWidth = parsedStyle.lineWidth,
textAlign = parsedStyle.textAlign,
textBaseline = parsedStyle.textBaseline,
lineJoin = parsedStyle.lineJoin,
miterLimit = parsedStyle.miterLimit,
letterSpacing = parsedStyle.letterSpacing,
stroke = parsedStyle.stroke,
fill = parsedStyle.fill,
fillOpacity = parsedStyle.fillOpacity,
strokeOpacity = parsedStyle.strokeOpacity,
opacity = parsedStyle.opacity,
metrics = parsedStyle.metrics,
dx = parsedStyle.dx,
dy = parsedStyle.dy,
shadowColor = parsedStyle.shadowColor,
shadowBlur = parsedStyle.shadowBlur;
var font = metrics.font,
lines = metrics.lines,
height = metrics.height,
lineHeight = metrics.lineHeight,
lineMetrics = metrics.lineMetrics;
context.font = font;
context.lineWidth = lineWidth;
context.textAlign = textAlign === 'middle' ? 'center' : textAlign;
var formattedTextBaseline = textBaseline;
if (
// formattedTextBaseline === 'bottom' ||
!gLite.runtime.enableCSSParsing && formattedTextBaseline === 'alphabetic') {
formattedTextBaseline = 'bottom';
}
context.lineJoin = lineJoin;
if (!isNil(miterLimit)) {
context.miterLimit = miterLimit;
}
var linePositionY = 0;
// handle vertical text baseline
if (textBaseline === 'middle') {
linePositionY = -height / 2 - lineHeight / 2;
} else if (textBaseline === 'bottom' || textBaseline === 'alphabetic' || textBaseline === 'ideographic') {
linePositionY = -height;
} else if (textBaseline === 'top' || textBaseline === 'hanging') {
linePositionY = -lineHeight;
}
// account for dx & dy
var offsetX = dx || 0;
linePositionY += dy || 0;
if (lines.length === 1) {
if (formattedTextBaseline === 'bottom') {
formattedTextBaseline = 'middle';
linePositionY -= 0.5 * height;
} else if (formattedTextBaseline === 'top') {
formattedTextBaseline = 'middle';
linePositionY += 0.5 * height;
}
}
context.textBaseline = formattedTextBaseline;
var hasShadow = !isNil(shadowColor) && shadowBlur > 0;
setShadowAndFilter(object, context, hasShadow);
// draw lines line by line
for (var i = 0; i < lines.length; i++) {
var linePositionX = lineWidth / 2 + offsetX;
linePositionY += lineHeight;
// no need to re-position X, cause we already set text align
// @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textAlign
if (!isNil(stroke) && !stroke.isNone && lineWidth) {
this.drawLetterSpacing(context, lines[i], lineMetrics[i], textAlign, linePositionX, linePositionY, letterSpacing, fillOpacity, strokeOpacity, opacity, true);
}
if (!isNil(fill)) {
this.drawLetterSpacing(context, lines[i], lineMetrics[i], textAlign, linePositionX, linePositionY, letterSpacing, fillOpacity, strokeOpacity, opacity);
}
}
};
_proto.drawLetterSpacing = function drawLetterSpacing(context, text, lineMetrics, textAlign, x, y, letterSpacing, fillOpacity, strokeOpacity, opacity, isStroke) {
if (isStroke === void 0) {
isStroke = false;
}
// letterSpacing of 0 means normal, render all texts directly
if (letterSpacing === 0) {
if (isStroke) {
this.strokeText(context, text, x, y, strokeOpacity);
} else {
this.fillText(context, text, x, y, fillOpacity, opacity);
}
return;
}
// draw text using left align
var currentTextAlign = context.textAlign;
context.textAlign = 'left';
var currentPosition = x;
if (textAlign === 'center' || textAlign === 'middle') {
currentPosition = x - lineMetrics.width / 2;
} else if (textAlign === 'right' || textAlign === 'end') {
currentPosition = x - lineMetrics.width;
}
var stringArray = Array.from(text);
var previousWidth = context.measureText(text).width;
var currentWidth = 0;
for (var i = 0; i < stringArray.length; ++i) {
var currentChar = stringArray[i];
if (isStroke) {
this.strokeText(context, currentChar, currentPosition, y, strokeOpacity);
} else {
this.fillText(context, currentChar, currentPosition, y, fillOpacity, opacity);
}
currentWidth = context.measureText(text.substring(i + 1)).width;
currentPosition += previousWidth - currentWidth + letterSpacing;
previousWidth = currentWidth;
}
context.textAlign = currentTextAlign;
};
_proto.fillText = function fillText(context, text, x, y, fillOpacity, opacity) {
var currentGlobalAlpha;
var applyOpacity = !isNil(fillOpacity) && fillOpacity !== 1;
if (applyOpacity) {
currentGlobalAlpha = context.globalAlpha;
context.globalAlpha = fillOpacity * opacity;
}
context.fillText(text, x, y);
if (applyOpacity) {
context.globalAlpha = currentGlobalAlpha;
}
};
_proto.strokeText = function strokeText(context, text, x, y, strokeOpacity) {
var currentGlobalAlpha;
var applyOpacity = !isNil(strokeOpacity) && strokeOpacity !== 1;
if (applyOpacity) {
currentGlobalAlpha = context.globalAlpha;
context.globalAlpha = strokeOpacity;
}
context.strokeText(text, x, y);
if (applyOpacity) {
context.globalAlpha = currentGlobalAlpha;
}
};
return TextRenderer;
}();
var RectRenderer = /*#__PURE__*/function (_DefaultRenderer) {
_inheritsLoose(RectRenderer, _DefaultRenderer);
function RectRenderer() {
return _DefaultRenderer.apply(this, arguments) || this;
}
return RectRenderer;
}(DefaultRenderer);
var CircleRenderer = /*#__PURE__*/function (_DefaultRenderer) {
_inheritsLoose(CircleRenderer, _DefaultRenderer);
function CircleRenderer() {
return _DefaultRenderer.apply(this, arguments) || this;
}
return CircleRenderer;
}(DefaultRenderer);
var EllipseRenderer = /*#__PURE__*/function (_DefaultRenderer) {
_inheritsLoose(EllipseRenderer, _DefaultRenderer);
function EllipseRenderer() {
return _DefaultRenderer.apply(this, arguments) || this;
}
return EllipseRenderer;
}(DefaultRenderer);
var LineRenderer = /*#__PURE__*/function (_DefaultRenderer) {
_inheritsLoose(LineRenderer, _DefaultRenderer);
function LineRenderer() {
return _DefaultRenderer.apply(this, arguments) || this;
}
return LineRenderer;
}(DefaultRenderer);
var PolylineRenderer = /*#__PURE__*/function (_DefaultRenderer) {
_inheritsLoose(PolylineRenderer, _DefaultRenderer);
function PolylineRenderer() {
return _DefaultRenderer.apply(this, arguments) || this;
}
return PolylineRenderer;
}(DefaultRenderer);
var PolygonRenderer = /*#__PURE__*/function (_DefaultRenderer) {
_inheritsLoose(PolygonRenderer, _DefaultRenderer);
function PolygonRenderer() {
return _DefaultRenderer.apply(this, arguments) || this;
}
return PolygonRenderer;
}(DefaultRenderer);
var PathRenderer = /*#__PURE__*/function (_DefaultRenderer) {
_inheritsLoose(PathRenderer, _DefaultRenderer);
function PathRenderer() {
return _DefaultRenderer.apply(this, arguments) || this;
}
return PathRenderer;
}(DefaultRenderer);
var Plugin = /*#__PURE__*/function (_AbstractRendererPlug) {
_inheritsLoose(Plugin, _AbstractRendererPlug);
function Plugin(options) {
var _this;
if (options === void 0) {
options = {};
}
_this = _AbstractRendererPlug.call(this) || this;
_this.options = void 0;
_this.name = 'canvas-renderer';
_this.options = options;
return _this;
}
var _proto = Plugin.prototype;
_proto.init = function init() {
var _defaultStyleRenderer;
var canvasRendererPluginOptions = _extends({
dirtyObjectNumThreshold: 500,
dirtyObjectRatioThreshold: 0.8
}, this.options);
// @ts-ignore
var imagePool = this.context.imagePool;
var defaultRenderer = new DefaultRenderer(imagePool);
var defaultStyleRendererFactory = (_defaultStyleRenderer = {}, _defaultStyleRenderer[gLite.Shape.CIRCLE] = defaultRenderer, _defaultStyleRenderer[gLite.Shape.ELLIPSE] = defaultRenderer, _defaultStyleRenderer[gLite.Shape.RECT] = defaultRenderer, _defaultStyleRenderer[gLite.Shape.IMAGE] = new ImageRenderer(imagePool), _defaultStyleRenderer[gLite.Shape.TEXT] = new TextRenderer(), _defaultStyleRenderer[gLite.Shape.LINE] = defaultRenderer, _defaultStyleRenderer[gLite.Shape.POLYLINE] = defaultRenderer, _defaultStyleRenderer[gLite.Shape.POLYGON] = defaultRenderer, _defaultStyleRenderer[gLite.Shape.PATH] = defaultRenderer, _defaultStyleRenderer[gLite.Shape.GROUP] = undefined, _defaultStyleRenderer[gLite.Shape.HTML] = undefined, _defaultStyleRenderer[gLite.Shape.MESH] = undefined, _defaultStyleRenderer);
this.context.defaultStyleRendererFactory = defaultStyleRendererFactory;
this.context.styleRendererFactory = defaultStyleRendererFactory;
this.addRenderingPlugin(new CanvasRendererPlugin(canvasRendererPluginOptions));
};
_proto.destroy = function destroy() {
this.removeAllRenderingPlugins();
delete this.context.defaultStyleRendererFactory;
delete this.context.styleRendererFactory;
};
return Plugin;
}(gLite.AbstractRendererPlugin);
exports.CircleRenderer = CircleRenderer;
exports.EllipseRenderer = EllipseRenderer;
exports.ImageRenderer = ImageRenderer;
exports.LineRenderer = LineRenderer;
exports.PathRenderer = PathRenderer;
exports.Plugin = Plugin;
exports.PolygonRenderer = PolygonRenderer;
exports.PolylineRenderer = PolylineRenderer;
exports.RectRenderer = RectRenderer;
exports.TextRenderer = TextRenderer;
Object.defineProperty(exports, '__esModule', { value: true });
})));