@leafer-draw/worker
Version:
1,367 lines (1,272 loc) • 91.8 kB
JavaScript
"use strict";
var core = require("@leafer/core");
var draw = require("@leafer-ui/draw");
exports.PathNodeHandleType = void 0;
(function(PathNodeHandleType) {
PathNodeHandleType[PathNodeHandleType["none"] = 1] = "none";
PathNodeHandleType[PathNodeHandleType["free"] = 2] = "free";
PathNodeHandleType[PathNodeHandleType["mirrorAngle"] = 3] = "mirrorAngle";
PathNodeHandleType[PathNodeHandleType["mirror"] = 4] = "mirror";
})(exports.PathNodeHandleType || (exports.PathNodeHandleType = {}));
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) {
return value instanceof P ? value : new P(function(resolve) {
resolve(value);
});
}
return new (P || (P = Promise))(function(resolve, reject) {
function fulfilled(value) {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
}
function rejected(value) {
try {
step(generator["throw"](value));
} catch (e) {
reject(e);
}
}
function step(result) {
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
}
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
class LeaferCanvas extends core.LeaferCanvasBase {
get allowBackgroundColor() {
return true;
}
init() {
this.__createView();
this.__createContext();
this.resize(this.config);
}
__createView() {
this.view = core.Platform.origin.createCanvas(1, 1);
}
updateViewSize() {
const {width: width, height: height, pixelRatio: pixelRatio} = this;
this.view.width = Math.ceil(width * pixelRatio);
this.view.height = Math.ceil(height * pixelRatio);
this.clientBounds = this.bounds;
}
}
core.canvasPatch(OffscreenCanvasRenderingContext2D.prototype);
core.canvasPatch(Path2D.prototype);
Object.assign(core.Creator, {
canvas: (options, manager) => new LeaferCanvas(options, manager),
image: options => new core.LeaferImage(options)
});
function useCanvas(_canvasType, _power) {
core.Platform.origin = {
createCanvas: (width, height) => new OffscreenCanvas(width, height),
canvasToDataURL: (canvas, type, quality) => new Promise((resolve, reject) => {
canvas.convertToBlob({
type: core.FileHelper.mimeType(type),
quality: quality
}).then(blob => {
var reader = new FileReader;
reader.onload = e => resolve(e.target.result);
reader.onerror = e => reject(e);
reader.readAsDataURL(blob);
}).catch(e => {
reject(e);
});
}),
canvasToBolb: (canvas, type, quality) => canvas.convertToBlob({
type: core.FileHelper.mimeType(type),
quality: quality
}),
canvasSaveAs: (_canvas, _filename, _quality) => new Promise(resolve => resolve()),
download(_url, _filename) {
return undefined;
},
loadImage(src, _crossOrigin, _leaferImage) {
return new Promise((resolve, reject) => {
let req = new XMLHttpRequest;
req.open("GET", core.Platform.image.getRealURL(src), true);
req.responseType = "blob";
req.onload = () => {
createImageBitmap(req.response).then(img => {
resolve(img);
}).catch(e => {
reject(e);
});
};
req.onerror = e => reject(e);
req.send();
});
},
loadContent(url_1) {
return __awaiter(this, arguments, void 0, function*(url, responseType = "text") {
const response = yield fetch(url);
if (!response.ok) throw new Error(`${response.status}`);
return yield response[responseType]();
});
}
};
core.Platform.canvas = core.Creator.canvas();
core.Platform.conicGradientSupport = !!core.Platform.canvas.context.createConicGradient;
}
core.Platform.name = "web";
core.Platform.isWorker = true;
core.Platform.backgrounder = true;
core.Platform.requestRender = function(render) {
requestAnimationFrame(render);
};
core.defineKey(core.Platform, "devicePixelRatio", {
get() {
return 1;
}
});
const {userAgent: userAgent} = navigator;
if (userAgent.indexOf("Firefox") > -1) {
core.Platform.conicGradientRotate90 = true;
core.Platform.intWheelDeltaY = true;
} else if (/iPhone|iPad|iPod/.test(navigator.userAgent) || /Macintosh/.test(navigator.userAgent) && /Version\/[\d.]+.*Safari/.test(navigator.userAgent)) {
core.Platform.fullImageShadow = true;
}
if (userAgent.indexOf("Windows") > -1) {
core.Platform.os = "Windows";
core.Platform.intWheelDeltaY = true;
} else if (userAgent.indexOf("Mac") > -1) {
core.Platform.os = "Mac";
} else if (userAgent.indexOf("Linux") > -1) {
core.Platform.os = "Linux";
}
class Watcher {
get childrenChanged() {
return this.hasAdd || this.hasRemove || this.hasVisible;
}
get updatedList() {
if (this.hasRemove && this.config.usePartLayout) {
const updatedList = new core.LeafList;
this.__updatedList.list.forEach(item => {
if (item.leafer) updatedList.add(item);
});
return updatedList;
} else {
return this.__updatedList;
}
}
constructor(target, userConfig) {
this.totalTimes = 0;
this.config = {};
this.__updatedList = new core.LeafList;
this.target = target;
if (userConfig) this.config = core.DataHelper.default(userConfig, this.config);
this.__listenEvents();
}
start() {
if (this.disabled) return;
this.running = true;
}
stop() {
this.running = false;
}
disable() {
this.stop();
this.__removeListenEvents();
this.disabled = true;
}
update() {
this.changed = true;
if (this.running) this.target.emit(core.RenderEvent.REQUEST);
}
__onAttrChange(event) {
if (this.config.usePartLayout) this.__updatedList.add(event.target);
this.update();
}
__onChildEvent(event) {
if (this.config.usePartLayout) {
if (event.type === core.ChildEvent.ADD) {
this.hasAdd = true;
this.__pushChild(event.child);
} else {
this.hasRemove = true;
this.__updatedList.add(event.parent);
}
}
this.update();
}
__pushChild(child) {
this.__updatedList.add(child);
if (child.isBranch) this.__loopChildren(child);
}
__loopChildren(parent) {
const {children: children} = parent;
for (let i = 0, len = children.length; i < len; i++) this.__pushChild(children[i]);
}
__onRquestData() {
this.target.emitEvent(new core.WatchEvent(core.WatchEvent.DATA, {
updatedList: this.updatedList
}));
this.__updatedList = new core.LeafList;
this.totalTimes++;
this.changed = this.hasVisible = this.hasRemove = this.hasAdd = false;
}
__listenEvents() {
this.__eventIds = [ this.target.on_([ [ core.PropertyEvent.CHANGE, this.__onAttrChange, this ], [ [ core.ChildEvent.ADD, core.ChildEvent.REMOVE ], this.__onChildEvent, this ], [ core.WatchEvent.REQUEST, this.__onRquestData, this ] ]) ];
}
__removeListenEvents() {
this.target.off_(this.__eventIds);
}
destroy() {
if (this.target) {
this.stop();
this.__removeListenEvents();
this.target = this.__updatedList = null;
}
}
}
const {updateAllMatrix: updateAllMatrix$1, updateBounds: updateOneBounds, updateChange: updateOneChange} = core.LeafHelper;
const {pushAllChildBranch: pushAllChildBranch, pushAllParent: pushAllParent} = core.BranchHelper;
function updateMatrix(updateList, levelList) {
let layout;
updateList.list.forEach(leaf => {
layout = leaf.__layout;
if (levelList.without(leaf) && !layout.proxyZoom) {
if (layout.matrixChanged) {
updateAllMatrix$1(leaf, true);
levelList.add(leaf);
if (leaf.isBranch) pushAllChildBranch(leaf, levelList);
pushAllParent(leaf, levelList);
} else if (layout.boundsChanged) {
levelList.add(leaf);
if (leaf.isBranch) leaf.__tempNumber = 0;
pushAllParent(leaf, levelList);
}
}
});
}
function updateBounds(boundsList) {
let list, branch, children;
boundsList.sort(true);
boundsList.levels.forEach(level => {
list = boundsList.levelMap[level];
for (let i = 0, len = list.length; i < len; i++) {
branch = list[i];
if (branch.isBranch && branch.__tempNumber) {
children = branch.children;
for (let j = 0, jLen = children.length; j < jLen; j++) {
if (!children[j].isBranch) {
updateOneBounds(children[j]);
}
}
}
updateOneBounds(branch);
}
});
}
function updateChange(updateList) {
updateList.list.forEach(updateOneChange);
}
const {worldBounds: worldBounds} = core.LeafBoundsHelper;
class LayoutBlockData {
constructor(list) {
this.updatedBounds = new core.Bounds;
this.beforeBounds = new core.Bounds;
this.afterBounds = new core.Bounds;
if (core.isArray(list)) list = new core.LeafList(list);
this.updatedList = list;
}
setBefore() {
this.beforeBounds.setListWithFn(this.updatedList.list, worldBounds);
}
setAfter() {
this.afterBounds.setListWithFn(this.updatedList.list, worldBounds);
this.updatedBounds.setList([ this.beforeBounds, this.afterBounds ]);
}
merge(data) {
this.updatedList.addList(data.updatedList.list);
this.beforeBounds.add(data.beforeBounds);
this.afterBounds.add(data.afterBounds);
this.updatedBounds.add(data.updatedBounds);
}
destroy() {
this.updatedList = null;
}
}
const {updateAllMatrix: updateAllMatrix, updateAllChange: updateAllChange} = core.LeafHelper;
const debug$1 = core.Debug.get("Layouter");
class Layouter {
constructor(target, userConfig) {
this.totalTimes = 0;
this.config = {
usePartLayout: true
};
this.__levelList = new core.LeafLevelList;
this.target = target;
if (userConfig) this.config = core.DataHelper.default(userConfig, this.config);
this.__listenEvents();
}
start() {
if (this.disabled) return;
this.running = true;
}
stop() {
this.running = false;
}
disable() {
this.stop();
this.__removeListenEvents();
this.disabled = true;
}
layout() {
if (this.layouting || !this.running) return;
const {target: target} = this;
this.times = 0;
try {
target.emit(core.LayoutEvent.START);
this.layoutOnce();
target.emitEvent(new core.LayoutEvent(core.LayoutEvent.END, this.layoutedBlocks, this.times));
} catch (e) {
debug$1.error(e);
}
this.layoutedBlocks = null;
}
layoutAgain() {
if (this.layouting) {
this.waitAgain = true;
} else {
this.layoutOnce();
}
}
layoutOnce() {
if (this.layouting) return debug$1.warn("layouting");
if (this.times > 3) return debug$1.warn("layout max times");
this.times++;
this.totalTimes++;
this.layouting = true;
this.target.emit(core.WatchEvent.REQUEST);
if (this.totalTimes > 1 && this.config.usePartLayout) {
this.partLayout();
} else {
this.fullLayout();
}
this.layouting = false;
if (this.waitAgain) {
this.waitAgain = false;
this.layoutOnce();
}
}
partLayout() {
var _a;
if (!((_a = this.__updatedList) === null || _a === void 0 ? void 0 : _a.length)) return;
const t = core.Run.start("PartLayout");
const {target: target, __updatedList: updateList} = this;
const {BEFORE: BEFORE, LAYOUT: LAYOUT, AFTER: AFTER} = core.LayoutEvent;
const blocks = this.getBlocks(updateList);
blocks.forEach(item => item.setBefore());
target.emitEvent(new core.LayoutEvent(BEFORE, blocks, this.times));
this.extraBlock = null;
updateList.sort();
updateMatrix(updateList, this.__levelList);
updateBounds(this.__levelList);
updateChange(updateList);
if (this.extraBlock) blocks.push(this.extraBlock);
blocks.forEach(item => item.setAfter());
target.emitEvent(new core.LayoutEvent(LAYOUT, blocks, this.times));
target.emitEvent(new core.LayoutEvent(AFTER, blocks, this.times));
this.addBlocks(blocks);
this.__levelList.reset();
this.__updatedList = null;
core.Run.end(t);
}
fullLayout() {
const t = core.Run.start("FullLayout");
const {target: target} = this;
const {BEFORE: BEFORE, LAYOUT: LAYOUT, AFTER: AFTER} = core.LayoutEvent;
const blocks = this.getBlocks(new core.LeafList(target));
target.emitEvent(new core.LayoutEvent(BEFORE, blocks, this.times));
Layouter.fullLayout(target);
blocks.forEach(item => {
item.setAfter();
});
target.emitEvent(new core.LayoutEvent(LAYOUT, blocks, this.times));
target.emitEvent(new core.LayoutEvent(AFTER, blocks, this.times));
this.addBlocks(blocks);
core.Run.end(t);
}
static fullLayout(target) {
updateAllMatrix(target, true);
if (target.isBranch) core.BranchHelper.updateBounds(target); else core.LeafHelper.updateBounds(target);
updateAllChange(target);
}
addExtra(leaf) {
if (!this.__updatedList.has(leaf)) {
const {updatedList: updatedList, beforeBounds: beforeBounds} = this.extraBlock || (this.extraBlock = new LayoutBlockData([]));
updatedList.length ? beforeBounds.add(leaf.__world) : beforeBounds.set(leaf.__world);
updatedList.add(leaf);
}
}
createBlock(data) {
return new LayoutBlockData(data);
}
getBlocks(list) {
return [ this.createBlock(list) ];
}
addBlocks(current) {
this.layoutedBlocks ? this.layoutedBlocks.push(...current) : this.layoutedBlocks = current;
}
__onReceiveWatchData(event) {
this.__updatedList = event.data.updatedList;
}
__listenEvents() {
this.__eventIds = [ this.target.on_([ [ core.LayoutEvent.REQUEST, this.layout, this ], [ core.LayoutEvent.AGAIN, this.layoutAgain, this ], [ core.WatchEvent.DATA, this.__onReceiveWatchData, this ] ]) ];
}
__removeListenEvents() {
this.target.off_(this.__eventIds);
}
destroy() {
if (this.target) {
this.stop();
this.__removeListenEvents();
this.target = this.config = null;
}
}
}
const debug = core.Debug.get("Renderer");
class Renderer {
get needFill() {
return !!(!this.canvas.allowBackgroundColor && this.config.fill);
}
constructor(target, canvas, userConfig) {
this.FPS = 60;
this.totalTimes = 0;
this.times = 0;
this.config = {
usePartRender: true,
ceilPartPixel: true,
maxFPS: 120
};
this.frames = [];
this.target = target;
this.canvas = canvas;
if (userConfig) this.config = core.DataHelper.default(userConfig, this.config);
this.__listenEvents();
}
start() {
this.running = true;
this.update(false);
}
stop() {
this.running = false;
}
update(change = true) {
if (!this.changed) this.changed = change;
if (!this.requestTime) this.__requestRender();
}
requestLayout() {
this.target.emit(core.LayoutEvent.REQUEST);
}
checkRender() {
if (this.running) {
const {target: target} = this;
if (target.isApp) {
target.emit(core.RenderEvent.CHILD_START, target);
target.children.forEach(leafer => {
leafer.renderer.FPS = this.FPS;
leafer.renderer.checkRender();
});
target.emit(core.RenderEvent.CHILD_END, target);
}
if (this.changed && this.canvas.view) this.render();
this.target.emit(core.RenderEvent.NEXT);
}
}
render(callback) {
if (!(this.running && this.canvas.view)) return this.update();
const {target: target} = this;
this.times = 0;
this.totalBounds = new core.Bounds;
debug.log(target.innerName, "---\x3e");
try {
this.emitRender(core.RenderEvent.START);
this.renderOnce(callback);
this.emitRender(core.RenderEvent.END, this.totalBounds);
core.ImageManager.clearRecycled();
} catch (e) {
this.rendering = false;
debug.error(e);
}
debug.log("-------------|");
}
renderAgain() {
if (this.rendering) {
this.waitAgain = true;
} else {
this.renderOnce();
}
}
renderOnce(callback) {
if (this.rendering) return debug.warn("rendering");
if (this.times > 3) return debug.warn("render max times");
this.times++;
this.totalTimes++;
this.rendering = true;
this.changed = false;
this.renderBounds = new core.Bounds;
this.renderOptions = {};
if (callback) {
this.emitRender(core.RenderEvent.BEFORE);
callback();
} else {
this.requestLayout();
if (this.ignore) {
this.ignore = this.rendering = false;
return;
}
this.emitRender(core.RenderEvent.BEFORE);
if (this.config.usePartRender && this.totalTimes > 1) {
this.partRender();
} else {
this.fullRender();
}
}
this.emitRender(core.RenderEvent.RENDER, this.renderBounds, this.renderOptions);
this.emitRender(core.RenderEvent.AFTER, this.renderBounds, this.renderOptions);
this.updateBlocks = null;
this.rendering = false;
if (this.waitAgain) {
this.waitAgain = false;
this.renderOnce();
}
}
partRender() {
const {canvas: canvas, updateBlocks: list} = this;
if (!list) return;
this.mergeBlocks();
list.forEach(block => {
if (canvas.bounds.hit(block) && !block.isEmpty()) this.clipRender(block);
});
}
clipRender(block) {
const t = core.Run.start("PartRender");
const {canvas: canvas} = this, bounds = block.getIntersect(canvas.bounds), realBounds = new core.Bounds(bounds);
canvas.save();
bounds.spread(Renderer.clipSpread).ceil();
const {ceilPartPixel: ceilPartPixel} = this.config;
canvas.clipWorld(bounds, ceilPartPixel);
canvas.clearWorld(bounds, ceilPartPixel);
this.__render(bounds, realBounds);
canvas.restore();
core.Run.end(t);
}
fullRender() {
const t = core.Run.start("FullRender");
const {canvas: canvas} = this;
canvas.save();
canvas.clear();
this.__render(canvas.bounds);
canvas.restore();
core.Run.end(t);
}
__render(bounds, realBounds) {
const {canvas: canvas, target: target} = this, includes = bounds.includes(target.__world), options = includes ? {
includes: includes
} : {
bounds: bounds,
includes: includes
};
if (this.needFill) canvas.fillWorld(bounds, this.config.fill);
if (core.Debug.showRepaint) core.Debug.drawRepaint(canvas, bounds);
if (this.config.useCellRender) options.cellList = this.getCellList();
core.Platform.render(target, canvas, options);
this.renderBounds = realBounds = realBounds || bounds;
this.renderOptions = options;
this.totalBounds.isEmpty() ? this.totalBounds = realBounds : this.totalBounds.add(realBounds);
canvas.updateRender(realBounds);
}
getCellList() {
return undefined;
}
addBlock(block, _leafList) {
if (!this.updateBlocks) this.updateBlocks = [];
this.updateBlocks.push(block);
}
mergeBlocks() {
const {updateBlocks: list} = this;
if (list) {
const bounds = new core.Bounds;
bounds.setList(list);
list.length = 0;
list.push(bounds);
}
}
__requestRender() {
const target = this.target;
if (this.requestTime || !target) return;
if (target.parentApp) return target.parentApp.requestRender(false);
this.requestTime = this.frameTime || Date.now();
const render = () => {
const nowFPS = 1e3 / ((this.frameTime = Date.now()) - this.requestTime);
const {maxFPS: maxFPS} = this.config;
if (maxFPS && nowFPS > maxFPS) return core.Platform.requestRender(render);
const {frames: frames} = this;
if (frames.length > 30) frames.shift();
frames.push(nowFPS);
this.FPS = Math.round(frames.reduce((a, b) => a + b, 0) / frames.length);
this.requestTime = 0;
this.checkRender();
};
core.Platform.requestRender(render);
}
__onResize(e) {
if (this.canvas.unreal) return;
if (e.bigger || !e.samePixelRatio) {
const {width: width, height: height} = e.old;
const bounds = new core.Bounds(0, 0, width, height);
if (!bounds.includes(this.target.__world) || this.needFill || !e.samePixelRatio) {
this.addBlock(this.canvas.bounds);
this.target.forceUpdate("surface");
return;
}
}
this.addBlock(new core.Bounds(0, 0, 1, 1));
this.update();
}
__onLayoutEnd(event) {
if (event.data) event.data.map(item => {
let empty;
const {updatedList: updatedList} = item;
if (updatedList) updatedList.list.some(leaf => {
empty = !leaf.__world.width || !leaf.__world.height;
if (empty) {
if (!leaf.isLeafer) debug.tip(leaf.innerName, ": empty");
empty = !leaf.isBranch || leaf.isBranchLeaf;
}
return empty;
});
this.addBlock(empty ? this.canvas.bounds : item.updatedBounds, updatedList);
});
}
emitRender(type, bounds, options) {
this.target.emitEvent(new core.RenderEvent(type, this.times, bounds, options));
}
__listenEvents() {
this.__eventIds = [ this.target.on_([ [ core.RenderEvent.REQUEST, this.update, this ], [ core.LayoutEvent.END, this.__onLayoutEnd, this ], [ core.RenderEvent.AGAIN, this.renderAgain, this ], [ core.ResizeEvent.RESIZE, this.__onResize, this ] ]) ];
}
__removeListenEvents() {
this.target.off_(this.__eventIds);
}
destroy() {
if (this.target) {
this.stop();
this.__removeListenEvents();
this.config = {};
this.target = this.canvas = null;
}
}
}
Renderer.clipSpread = 10;
Object.assign(core.Creator, {
watcher: (target, options) => new Watcher(target, options),
layouter: (target, options) => new Layouter(target, options),
renderer: (target, canvas, options) => new Renderer(target, canvas, options),
selector: (_target, _options) => undefined,
interaction: (_target, _canvas, _selector, _options) => undefined
});
core.Platform.layout = Layouter.fullLayout;
core.Platform.render = function(target, canvas, options) {
const topOptions = Object.assign(Object.assign({}, options), {
topRendering: true
});
options.topList = new core.LeafList;
target.__render(canvas, options);
if (options.topList.length) options.topList.forEach(item => item.__render(canvas, topOptions));
};
function fill(fill, ui, canvas, renderOptions) {
canvas.fillStyle = fill;
fillPathOrText(ui, canvas, renderOptions);
}
function fills(fills, ui, canvas, renderOptions) {
let item, originPaint, countImage;
for (let i = 0, len = fills.length; i < len; i++) {
item = fills[i], originPaint = item.originPaint;
if (item.image) {
countImage ? countImage++ : countImage = 1;
if (draw.PaintImage.checkImage(item, !ui.__.__font, ui, canvas, renderOptions)) continue;
if (!item.style) {
if (countImage === 1 && item.image.isPlacehold) ui.drawImagePlaceholder(item, canvas, renderOptions);
continue;
}
}
canvas.fillStyle = item.style;
if (item.transform || originPaint.scaleFixed) {
canvas.save();
if (item.transform) canvas.transform(item.transform);
if (originPaint.scaleFixed) {
const {scaleX: scaleX, scaleY: scaleY} = ui.getRenderScaleData(true, originPaint.scaleFixed, false);
if (scaleX !== 1) canvas.scale(scaleX, scaleY);
}
if (originPaint.blendMode) canvas.blendMode = originPaint.blendMode;
fillPathOrText(ui, canvas, renderOptions);
canvas.restore();
} else {
if (originPaint.blendMode) {
canvas.saveBlendMode(originPaint.blendMode);
fillPathOrText(ui, canvas, renderOptions);
canvas.restoreBlendMode();
} else fillPathOrText(ui, canvas, renderOptions);
}
}
}
function fillPathOrText(ui, canvas, renderOptions) {
ui.__.__font ? draw.Paint.fillText(ui, canvas, renderOptions) : ui.__.windingRule ? canvas.fill(ui.__.windingRule) : canvas.fill();
}
function fillText(ui, canvas, _renderOptions) {
const data = ui.__, {rows: rows, decorationY: decorationY} = data.__textDrawData;
if (data.__isPlacehold && data.placeholderColor) canvas.fillStyle = data.placeholderColor;
let row;
for (let i = 0, len = rows.length; i < len; i++) {
row = rows[i];
if (row.text) canvas.fillText(row.text, row.x, row.y); else if (row.data) row.data.forEach(charData => {
canvas.fillText(charData.char, charData.x, row.y);
});
}
if (decorationY) {
const {decorationColor: decorationColor, decorationHeight: decorationHeight} = data.__textDrawData;
if (decorationColor) canvas.fillStyle = decorationColor;
rows.forEach(row => decorationY.forEach(value => canvas.fillRect(row.x, row.y + value, row.width, decorationHeight)));
}
}
function stroke(stroke, ui, canvas, renderOptions) {
const data = ui.__;
if (!data.__strokeWidth) return;
if (data.__font) {
draw.Paint.strokeText(stroke, ui, canvas, renderOptions);
} else if (data.__pathForStroke) {
draw.Paint.fillStroke(stroke, ui, canvas, renderOptions);
} else {
switch (data.strokeAlign) {
case "center":
drawCenter$1(stroke, 1, ui, canvas, renderOptions);
break;
case "inside":
drawInside(stroke, ui, canvas, renderOptions);
break;
case "outside":
drawOutside(stroke, ui, canvas, renderOptions);
break;
}
}
}
function strokes(strokes, ui, canvas, renderOptions) {
draw.Paint.stroke(strokes, ui, canvas, renderOptions);
}
function drawCenter$1(stroke, strokeWidthScale, ui, canvas, renderOptions) {
const data = ui.__;
if (core.isObject(stroke)) {
draw.Paint.drawStrokesStyle(stroke, strokeWidthScale, false, ui, canvas, renderOptions);
} else {
canvas.setStroke(stroke, data.__strokeWidth * strokeWidthScale, data);
canvas.stroke();
}
if (data.__useArrow) draw.Paint.strokeArrow(stroke, ui, canvas, renderOptions);
}
function drawInside(stroke, ui, canvas, renderOptions) {
canvas.save();
canvas.clipUI(ui);
drawCenter$1(stroke, 2, ui, canvas, renderOptions);
canvas.restore();
}
function drawOutside(stroke, ui, canvas, renderOptions) {
const data = ui.__;
if (data.__fillAfterStroke) {
drawCenter$1(stroke, 2, ui, canvas, renderOptions);
} else {
const {renderBounds: renderBounds} = ui.__layout;
const out = canvas.getSameCanvas(true, true);
ui.__drawRenderPath(out);
drawCenter$1(stroke, 2, ui, out, renderOptions);
out.clipUI(data);
out.clearWorld(renderBounds);
core.LeafHelper.copyCanvasByWorld(ui, canvas, out);
out.recycle(ui.__nowWorld);
}
}
function strokeText(stroke, ui, canvas, renderOptions) {
switch (ui.__.strokeAlign) {
case "center":
drawCenter(stroke, 1, ui, canvas, renderOptions);
break;
case "inside":
drawAlign(stroke, "inside", ui, canvas, renderOptions);
break;
case "outside":
ui.__.__fillAfterStroke ? drawCenter(stroke, 2, ui, canvas, renderOptions) : drawAlign(stroke, "outside", ui, canvas, renderOptions);
break;
}
}
function drawCenter(stroke, strokeWidthScale, ui, canvas, renderOptions) {
const data = ui.__;
if (core.isObject(stroke)) {
draw.Paint.drawStrokesStyle(stroke, strokeWidthScale, true, ui, canvas, renderOptions);
} else {
canvas.setStroke(stroke, data.__strokeWidth * strokeWidthScale, data);
draw.Paint.drawTextStroke(ui, canvas, renderOptions);
}
}
function drawAlign(stroke, align, ui, canvas, renderOptions) {
const out = canvas.getSameCanvas(true, true);
out.font = ui.__.__font;
drawCenter(stroke, 2, ui, out, renderOptions);
out.blendMode = align === "outside" ? "destination-out" : "destination-in";
draw.Paint.fillText(ui, out, renderOptions);
out.blendMode = "normal";
core.LeafHelper.copyCanvasByWorld(ui, canvas, out);
out.recycle(ui.__nowWorld);
}
function drawTextStroke(ui, canvas, _renderOptions) {
let row, data = ui.__.__textDrawData;
const {rows: rows, decorationY: decorationY} = data;
for (let i = 0, len = rows.length; i < len; i++) {
row = rows[i];
if (row.text) canvas.strokeText(row.text, row.x, row.y); else if (row.data) row.data.forEach(charData => {
canvas.strokeText(charData.char, charData.x, row.y);
});
}
if (decorationY) {
const {decorationHeight: decorationHeight} = data;
rows.forEach(row => decorationY.forEach(value => canvas.strokeRect(row.x, row.y + value, row.width, decorationHeight)));
}
}
function drawStrokesStyle(strokes, strokeWidthScale, isText, ui, canvas, renderOptions) {
let item;
const data = ui.__, {__hasMultiStrokeStyle: __hasMultiStrokeStyle} = data;
__hasMultiStrokeStyle || canvas.setStroke(undefined, data.__strokeWidth * strokeWidthScale, data);
for (let i = 0, len = strokes.length; i < len; i++) {
item = strokes[i];
if (item.image && draw.PaintImage.checkImage(item, false, ui, canvas, renderOptions)) continue;
if (item.style) {
if (__hasMultiStrokeStyle) {
const {strokeStyle: strokeStyle} = item;
strokeStyle ? canvas.setStroke(item.style, data.__getRealStrokeWidth(strokeStyle) * strokeWidthScale, data, strokeStyle) : canvas.setStroke(item.style, data.__strokeWidth * strokeWidthScale, data);
} else canvas.strokeStyle = item.style;
if (item.originPaint.blendMode) {
canvas.saveBlendMode(item.originPaint.blendMode);
isText ? draw.Paint.drawTextStroke(ui, canvas, renderOptions) : canvas.stroke();
canvas.restoreBlendMode();
} else {
isText ? draw.Paint.drawTextStroke(ui, canvas, renderOptions) : canvas.stroke();
}
}
}
}
const {getSpread: getSpread, copyAndSpread: copyAndSpread, toOuterOf: toOuterOf, getOuterOf: getOuterOf, getByMove: getByMove, move: move$1, getIntersectData: getIntersectData} = core.BoundsHelper;
const tempBounds$1 = {};
function shape(ui, current, options) {
const canvas = current.getSameCanvas();
const currentBounds = current.bounds, nowWorld = ui.__nowWorld, layout = ui.__layout;
const nowWorldShapeBounds = ui.__nowWorldShapeBounds || (ui.__nowWorldShapeBounds = {});
toOuterOf(layout.strokeSpread ? (copyAndSpread(tempBounds$1, layout.boxBounds, layout.strokeSpread),
tempBounds$1) : layout.boxBounds, nowWorld, nowWorldShapeBounds);
let bounds, renderBounds, matrix, fitMatrix, shapeBounds, worldCanvas;
let {scaleX: scaleX, scaleY: scaleY} = ui.getRenderScaleData(true);
if (currentBounds.includes(nowWorldShapeBounds)) {
worldCanvas = canvas;
bounds = shapeBounds = nowWorldShapeBounds;
renderBounds = nowWorld;
} else {
let worldClipBounds;
if (core.Platform.fullImageShadow) {
worldClipBounds = nowWorldShapeBounds;
} else {
const spreadBounds = layout.renderShapeSpread ? getSpread(currentBounds, core.FourNumberHelper.swapAndScale(layout.renderShapeSpread, scaleX, scaleY)) : currentBounds;
worldClipBounds = getIntersectData(spreadBounds, nowWorldShapeBounds);
}
fitMatrix = currentBounds.getFitMatrix(worldClipBounds);
let {a: fitScaleX, d: fitScaleY} = fitMatrix;
if (fitMatrix.a < 1) {
worldCanvas = current.getSameCanvas();
ui.__renderShape(worldCanvas, options);
scaleX *= fitScaleX;
scaleY *= fitScaleY;
}
shapeBounds = getOuterOf(nowWorldShapeBounds, fitMatrix);
bounds = getByMove(shapeBounds, -fitMatrix.e, -fitMatrix.f);
renderBounds = getOuterOf(nowWorld, fitMatrix);
move$1(renderBounds, -fitMatrix.e, -fitMatrix.f);
const userMatrix = options.matrix;
if (userMatrix) {
matrix = new core.Matrix(fitMatrix);
matrix.multiply(userMatrix);
fitScaleX *= userMatrix.scaleX;
fitScaleY *= userMatrix.scaleY;
} else matrix = fitMatrix;
matrix.withScale(fitScaleX, fitScaleY);
options = Object.assign(Object.assign({}, options), {
matrix: matrix
});
}
ui.__renderShape(canvas, options);
return {
canvas: canvas,
matrix: matrix,
fitMatrix: fitMatrix,
bounds: bounds,
renderBounds: renderBounds,
worldCanvas: worldCanvas,
shapeBounds: shapeBounds,
scaleX: scaleX,
scaleY: scaleY
};
}
let recycleMap;
const {stintSet: stintSet} = core.DataHelper, {hasTransparent: hasTransparent$1} = draw.ColorConvert;
function compute(attrName, ui) {
const data = ui.__, leafPaints = [];
let paints = data.__input[attrName], isAlphaPixel, isTransparent;
if (!core.isArray(paints)) paints = [ paints ];
recycleMap = draw.PaintImage.recycleImage(attrName, data);
let maxChildStrokeWidth;
for (let i = 0, len = paints.length, item; i < len; i++) {
if (item = getLeafPaint(attrName, paints[i], ui)) {
leafPaints.push(item);
if (item.strokeStyle) {
maxChildStrokeWidth || (maxChildStrokeWidth = 1);
if (item.strokeStyle.strokeWidth) maxChildStrokeWidth = Math.max(maxChildStrokeWidth, item.strokeStyle.strokeWidth);
}
}
}
if (leafPaints.length) {
data["_" + attrName] = leafPaints;
if (leafPaints.every(item => item.isTransparent)) {
if (leafPaints.some(item => item.image)) isAlphaPixel = true;
isTransparent = true;
}
if (attrName === "fill") {
stintSet(data, "__isAlphaPixelFill", isAlphaPixel);
stintSet(data, "__isTransparentFill", isTransparent);
} else {
stintSet(data, "__isAlphaPixelStroke", isAlphaPixel);
stintSet(data, "__isTransparentStroke", isTransparent);
stintSet(data, "__hasMultiStrokeStyle", maxChildStrokeWidth);
}
} else {
data.__removePaint(attrName, false);
data["_" + attrName] = "";
}
}
function getLeafPaint(attrName, paint, ui) {
if (!core.isObject(paint) || paint.visible === false || paint.opacity === 0) return undefined;
let leafPaint;
const {boxBounds: boxBounds} = ui.__layout, {type: type} = paint;
switch (type) {
case "image":
case "film":
case "video":
if (!paint.url) return undefined;
leafPaint = draw.PaintImage.image(ui, attrName, paint, boxBounds, !recycleMap || !recycleMap[paint.url]);
if (type !== "image") draw.PaintImage[type](leafPaint);
break;
case "linear":
leafPaint = draw.PaintGradient.linearGradient(paint, boxBounds);
break;
case "radial":
leafPaint = draw.PaintGradient.radialGradient(paint, boxBounds);
break;
case "angular":
leafPaint = draw.PaintGradient.conicGradient(paint, boxBounds);
break;
case "solid":
const {color: color, opacity: opacity} = paint;
leafPaint = {
type: type,
style: draw.ColorConvert.string(color, opacity)
};
break;
default:
if (!core.isUndefined(paint.r)) leafPaint = {
type: "solid",
style: draw.ColorConvert.string(paint)
};
}
if (leafPaint) {
leafPaint.originPaint = paint;
if (core.isString(leafPaint.style) && hasTransparent$1(leafPaint.style)) leafPaint.isTransparent = true;
if (paint.style) {
if (paint.style.strokeWidth === 0) return undefined;
leafPaint.strokeStyle = paint.style;
}
}
return leafPaint;
}
const PaintModule = {
compute: compute,
fill: fill,
fills: fills,
fillPathOrText: fillPathOrText,
fillText: fillText,
stroke: stroke,
strokes: strokes,
strokeText: strokeText,
drawTextStroke: drawTextStroke,
drawStrokesStyle: drawStrokesStyle,
shape: shape
};
let cache, box = new core.Bounds;
const {isSame: isSame} = core.BoundsHelper;
function image(ui, attrName, paint, boxBounds, firstUse) {
let leafPaint, event;
const image = core.ImageManager.get(paint, paint.type);
if (cache && paint === cache.paint && isSame(boxBounds, cache.boxBounds)) {
leafPaint = cache.leafPaint;
} else {
leafPaint = {
type: paint.type,
image: image
};
if (image.hasAlphaPixel) leafPaint.isTransparent = true;
cache = image.use > 1 ? {
leafPaint: leafPaint,
paint: paint,
boxBounds: box.set(boxBounds)
} : null;
}
if (firstUse || image.loading) event = {
image: image,
attrName: attrName,
attrValue: paint
};
if (image.ready) {
checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds);
if (firstUse) {
onLoad(ui, event);
onLoadSuccess(ui, event);
}
} else if (image.error) {
if (firstUse) onLoadError(ui, event, image.error);
} else {
if (firstUse) {
ignoreRender(ui, true);
onLoad(ui, event);
}
leafPaint.loadId = image.load(() => {
ignoreRender(ui, false);
if (!ui.destroyed) {
if (checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds)) {
if (image.hasAlphaPixel) ui.__layout.hitCanvasChanged = true;
ui.forceUpdate("surface");
}
onLoadSuccess(ui, event);
}
leafPaint.loadId = undefined;
}, error => {
ignoreRender(ui, false);
onLoadError(ui, event, error);
leafPaint.loadId = undefined;
}, paint.lod && image.getThumbSize(paint.lod));
if (ui.placeholderColor) {
if (!ui.placeholderDelay) image.isPlacehold = true; else setTimeout(() => {
if (!image.ready) {
image.isPlacehold = true;
ui.forceUpdate("surface");
}
}, ui.placeholderDelay);
}
}
return leafPaint;
}
function checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds) {
let needUpdate = true;
const data = ui.__;
if (attrName === "fill" && !data.__naturalWidth) {
data.__naturalWidth = image.width / data.pixelRatio;
data.__naturalHeight = image.height / data.pixelRatio;
if (data.__autoSide) {
ui.forceUpdate("width");
core.LeafHelper.updateBounds(ui);
if (ui.__proxyData) {
ui.setProxyAttr("width", data.width);
ui.setProxyAttr("height", data.height);
}
needUpdate = false;
}
}
if (paint.mode === "brush") draw.PaintImage.brush(leafPaint);
if (!leafPaint.data) {
draw.PaintImage.createData(leafPaint, image, paint, boxBounds);
const {transform: transform} = leafPaint.data, {opacity: opacity} = paint;
const clip = (transform && !transform.onlyScale || data.path || data.cornerRadius) && !leafPaint.brush;
if (clip || opacity && opacity < 1 || paint.blendMode) leafPaint.complex = clip ? 2 : true;
}
if (paint.filter) draw.PaintImage.applyFilter(leafPaint, image, paint.filter, ui);
return needUpdate;
}
function onLoad(ui, event) {
emit(ui, core.ImageEvent.LOAD, event);
}
function onLoadSuccess(ui, event) {
emit(ui, core.ImageEvent.LOADED, event);
}
function onLoadError(ui, event, error) {
event.error = error;
ui.forceUpdate("surface");
emit(ui, core.ImageEvent.ERROR, event);
}
function emit(ui, type, data) {
if (ui.hasEvent(type)) ui.emitEvent(new core.ImageEvent(type, data));
}
function ignoreRender(ui, value) {
const {leafer: leafer} = ui;
if (leafer && leafer.viewReady) leafer.renderer.ignore = value;
}
const {get: get$3, translate: translate$1} = core.MatrixHelper;
const tempBox = new core.Bounds;
const tempScaleData = {};
const tempImage = {};
function createData(leafPaint, image, paint, box) {
leafPaint.data = draw.PaintImage.getPatternData(paint, box, image);
}
function getPatternData(paint, box, image) {
if (paint.padding) box = tempBox.set(box).shrink(paint.padding);
if (paint.mode === "strench") paint.mode = "stretch";
const {width: width, height: height} = image;
const {mode: mode, align: align, offset: offset, scale: scale, size: size, rotation: rotation, skew: skew, clipSize: clipSize, repeat: repeat, gap: gap, interlace: interlace} = paint;
const sameBox = box.width === width && box.height === height;
const data = {
mode: mode
};
const swapSize = align !== "center" && (rotation || 0) % 180 === 90;
core.BoundsHelper.set(tempImage, 0, 0, swapSize ? height : width, swapSize ? width : height);
let scaleX, scaleY;
if (!mode || mode === "cover" || mode === "fit") {
if (!sameBox || rotation) {
scaleX = scaleY = core.BoundsHelper.getFitScale(box, tempImage, mode !== "fit");
core.BoundsHelper.put(box, image, align, scaleX, false, tempImage);
core.BoundsHelper.scale(tempImage, scaleX, scaleY, true);
}
} else {
if (scale || size) {
core.MathHelper.getScaleData(scale, size, image, tempScaleData);
scaleX = tempScaleData.scaleX;
scaleY = tempScaleData.scaleY;
}
if (align || gap || repeat) {
if (scaleX) core.BoundsHelper.scale(tempImage, scaleX, scaleY, true);
if (align) core.AlignHelper.toPoint(align, tempImage, box, tempImage, true, true);
}
}
if (offset) core.PointHelper.move(tempImage, offset);
switch (mode) {
case "stretch":
if (!sameBox) {
scaleX = box.width / width, scaleY = box.height / height;
draw.PaintImage.stretchMode(data, box, scaleX, scaleY);
} else if (scaleX) scaleX = scaleY = undefined;
break;
case "normal":
case "clip":
if (tempImage.x || tempImage.y || scaleX || clipSize || rotation || skew) {
let clipScaleX, clipScaleY;
if (clipSize) clipScaleX = box.width / clipSize.width, clipScaleY = box.height / clipSize.height;
draw.PaintImage.clipMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation, skew, clipScaleX, clipScaleY);
if (clipScaleX) scaleX = scaleX ? scaleX * clipScaleX : clipScaleX, scaleY = scaleY ? scaleY * clipScaleY : clipScaleY;
}
break;
case "repeat":
case "brush":
if (!sameBox || scaleX || rotation || skew) draw.PaintImage.repeatMode(data, box, width, height, tempImage.x, tempImage.y, scaleX, scaleY, rotation, skew, align, paint.freeTransform);
if (!repeat) data.repeat = "repeat";
const count = core.isObject(repeat);
if (gap || count) data.gap = getGapData(gap, count && repeat, tempImage.width, tempImage.height, box);
break;
case "fit":
case "cover":
default:
if (scaleX) draw.PaintImage.fillOrFitMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
}
if (!data.transform) {
if (box.x || box.y) translate$1(data.transform = get$3(), box.x, box.y);
}
if (scaleX) {
data.scaleX = scaleX;
data.scaleY = scaleY;
}
if (repeat) data.repeat = core.isString(repeat) ? repeat === "x" ? "repeat-x" : "repeat-y" : "repeat";
if (interlace) data.interlace = core.isNumber(interlace) || interlace.type === "percent" ? {
type: "x",
offset: interlace
} : interlace;
return data;
}
function getGapData(gap, repeat, width, height, box) {
let xGap, yGap;
if (core.isObject(gap)) xGap = gap.x, yGap = gap.y; else xGap = yGap = gap;
return {
x: getGapValue(xGap, width, box.width, repeat && repeat.x),
y: getGapValue(yGap, height, box.height, repeat && repeat.y)
};
}
function getGapValue(gap, size, totalSize, rows) {
const auto = core.isString(gap) || rows;
const remain = rows ? totalSize - rows * size : totalSize % size;
const value = auto ? remain / ((rows || Math.floor(totalSize / size)) - 1) : gap;
return gap === "auto" ? value < 0 ? 0 : value : value;
}
let origin = {}, tempMatrix$1 = core.getMatrixData();
const {get: get$2, set: set, rotateOfOuter: rotateOfOuter$1, translate: translate, scaleOfOuter: scaleOfOuter$1, multiplyParent: multiplyParent, scale: scaleHelper, rotate: rotate, skew: skewHelper} = core.MatrixHelper;
function stretchMode(data, box, scaleX, scaleY) {
const transform = get$2(), {x: x, y: y} = box;
if (x || y) translate(transform, x, y); else if (scaleX > 0 && scaleY > 0) transform.onlyScale = true;
scaleHelper(transform, scaleX, scaleY);
data.transform = transform;
}
function fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation) {
const transform = get$2();
translate(transform, box.x + x, box.y + y);
scaleHelper(transform, scaleX, scaleY);
if (rotation) rotateOfOuter$1(transform, {
x: box.x + box.width / 2,
y: box.y + box.height / 2
}, rotation);
data.transform = transform;
}
function clipMode(data, box, x, y, scaleX, scaleY, rotation, skew, clipScaleX, clipScaleY) {
const transform = get$2();
layout(transform, box, x, y, scaleX, scaleY, rotation, skew);
if (clipScaleX) {
if (rotation || skew) {
set(tempMatrix$1);
scaleOfOuter$1(tempMatrix$1, box, clipScaleX, clipScaleY);
multiplyParent(transform, tempMatrix$1);
} else scaleOfOuter$1(transform, box, clipScaleX, clipScaleY);
}
data.transform = transform;
}
function repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, skew, align, freeTransform) {
const transform = get$2();
if (freeTransform) {
layout(transform, box, x, y, scaleX, scaleY, rotation, skew);
} else {
if (rotation) {
if (align === "center") {
rotateOfOuter$1(transform, {
x: width / 2,
y: height / 2
}, rotation);
} else {
rotate(transform, rotation);
switch (rotation) {
case 90:
translate(transform, height, 0);
break;
case 180:
translate(transform, width, height);
break;
case 270:
translate(transform, 0, width);
break;
}
}
}
origin.x = box.x