apexify.js 4.9.26 → 4.9.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +437 -47
- package/dist/cjs/Canvas/ApexPainter.d.ts +122 -78
- package/dist/cjs/Canvas/ApexPainter.d.ts.map +1 -1
- package/dist/cjs/Canvas/ApexPainter.js +461 -352
- package/dist/cjs/Canvas/ApexPainter.js.map +1 -1
- package/dist/cjs/Canvas/utils/Background/bg.d.ts +23 -11
- package/dist/cjs/Canvas/utils/Background/bg.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/Background/bg.js +174 -107
- package/dist/cjs/Canvas/utils/Background/bg.js.map +1 -1
- package/dist/cjs/Canvas/utils/Custom/customLines.js +2 -2
- package/dist/cjs/Canvas/utils/Custom/customLines.js.map +1 -1
- package/dist/cjs/Canvas/utils/Image/imageFilters.d.ts +11 -0
- package/dist/cjs/Canvas/utils/Image/imageFilters.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Image/imageFilters.js +307 -0
- package/dist/cjs/Canvas/utils/Image/imageFilters.js.map +1 -0
- package/dist/cjs/Canvas/utils/Image/imageProperties.d.ts +47 -112
- package/dist/cjs/Canvas/utils/Image/imageProperties.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/Image/imageProperties.js +229 -560
- package/dist/cjs/Canvas/utils/Image/imageProperties.js.map +1 -1
- package/dist/cjs/Canvas/utils/Image/professionalImageFilters.d.ts +11 -0
- package/dist/cjs/Canvas/utils/Image/professionalImageFilters.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Image/professionalImageFilters.js +351 -0
- package/dist/cjs/Canvas/utils/Image/professionalImageFilters.js.map +1 -0
- package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.d.ts +11 -0
- package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.js +215 -0
- package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.js.map +1 -0
- package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts +71 -0
- package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.js +392 -0
- package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.js.map +1 -0
- package/dist/cjs/Canvas/utils/Shapes/shapes.d.ts +29 -0
- package/dist/cjs/Canvas/utils/Shapes/shapes.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Shapes/shapes.js +334 -0
- package/dist/cjs/Canvas/utils/Shapes/shapes.js.map +1 -0
- package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.d.ts +127 -0
- package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.js +365 -0
- package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.js.map +1 -0
- package/dist/cjs/Canvas/utils/types.d.ts +227 -131
- package/dist/cjs/Canvas/utils/types.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/types.js +0 -1
- package/dist/cjs/Canvas/utils/types.js.map +1 -1
- package/dist/cjs/Canvas/utils/utils.d.ts +7 -4
- package/dist/cjs/Canvas/utils/utils.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/utils.js +17 -7
- package/dist/cjs/Canvas/utils/utils.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/Canvas/ApexPainter.d.ts +122 -78
- package/dist/esm/Canvas/ApexPainter.d.ts.map +1 -1
- package/dist/esm/Canvas/ApexPainter.js +461 -352
- package/dist/esm/Canvas/ApexPainter.js.map +1 -1
- package/dist/esm/Canvas/utils/Background/bg.d.ts +23 -11
- package/dist/esm/Canvas/utils/Background/bg.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/Background/bg.js +174 -107
- package/dist/esm/Canvas/utils/Background/bg.js.map +1 -1
- package/dist/esm/Canvas/utils/Custom/customLines.js +2 -2
- package/dist/esm/Canvas/utils/Custom/customLines.js.map +1 -1
- package/dist/esm/Canvas/utils/Image/imageFilters.d.ts +11 -0
- package/dist/esm/Canvas/utils/Image/imageFilters.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Image/imageFilters.js +307 -0
- package/dist/esm/Canvas/utils/Image/imageFilters.js.map +1 -0
- package/dist/esm/Canvas/utils/Image/imageProperties.d.ts +47 -112
- package/dist/esm/Canvas/utils/Image/imageProperties.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/Image/imageProperties.js +229 -560
- package/dist/esm/Canvas/utils/Image/imageProperties.js.map +1 -1
- package/dist/esm/Canvas/utils/Image/professionalImageFilters.d.ts +11 -0
- package/dist/esm/Canvas/utils/Image/professionalImageFilters.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Image/professionalImageFilters.js +351 -0
- package/dist/esm/Canvas/utils/Image/professionalImageFilters.js.map +1 -0
- package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.d.ts +11 -0
- package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.js +215 -0
- package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.js.map +1 -0
- package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts +71 -0
- package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.js +392 -0
- package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.js.map +1 -0
- package/dist/esm/Canvas/utils/Shapes/shapes.d.ts +29 -0
- package/dist/esm/Canvas/utils/Shapes/shapes.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Shapes/shapes.js +334 -0
- package/dist/esm/Canvas/utils/Shapes/shapes.js.map +1 -0
- package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.d.ts +127 -0
- package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.js +365 -0
- package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.js.map +1 -0
- package/dist/esm/Canvas/utils/types.d.ts +227 -131
- package/dist/esm/Canvas/utils/types.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/types.js +0 -1
- package/dist/esm/Canvas/utils/types.js.map +1 -1
- package/dist/esm/Canvas/utils/utils.d.ts +7 -4
- package/dist/esm/Canvas/utils/utils.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/utils.js +17 -7
- package/dist/esm/Canvas/utils/utils.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/lib/Canvas/ApexPainter.ts +1325 -1218
- package/lib/Canvas/utils/Background/bg.ts +247 -173
- package/lib/Canvas/utils/Custom/customLines.ts +3 -3
- package/lib/Canvas/utils/Image/imageFilters.ts +356 -0
- package/lib/Canvas/utils/Image/imageProperties.ts +322 -775
- package/lib/Canvas/utils/Image/professionalImageFilters.ts +391 -0
- package/lib/Canvas/utils/Image/simpleProfessionalFilters.ts +229 -0
- package/lib/Canvas/utils/Patterns/enhancedPatternRenderer.ts +444 -0
- package/lib/Canvas/utils/Shapes/shapes.ts +528 -0
- package/lib/Canvas/utils/Texts/enhancedTextRenderer.ts +478 -0
- package/lib/Canvas/utils/types.ts +301 -117
- package/lib/Canvas/utils/utils.ts +85 -72
- package/package.json +106 -188
|
@@ -1,602 +1,271 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildPath = buildPath;
|
|
7
|
+
exports.applyRotation = applyRotation;
|
|
8
|
+
exports.createGradientFill = createGradientFill;
|
|
9
|
+
exports.fitInto = fitInto;
|
|
10
|
+
exports.loadImageCached = loadImageCached;
|
|
3
11
|
exports.applyShadow = applyShadow;
|
|
4
|
-
exports.applyZoom = applyZoom;
|
|
5
12
|
exports.applyStroke = applyStroke;
|
|
6
|
-
exports.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* @param ctx The canvas rendering context.
|
|
15
|
-
* @param shadow The shadow properties.
|
|
16
|
-
* @param x The x-coordinate of the shape.
|
|
17
|
-
* @param y The y-coordinate of the shape.
|
|
18
|
-
* @param width The width of the shape.
|
|
19
|
-
* @param height The height of the shape.
|
|
20
|
-
*/
|
|
21
|
-
function applyShadow(ctx, shadow, x, y, width, height) {
|
|
22
|
-
ctx.save();
|
|
23
|
-
if (shadow) {
|
|
24
|
-
ctx.globalAlpha = shadow.opacity ?? 1;
|
|
25
|
-
ctx.filter = `blur(${shadow.blur ?? 0}px)`;
|
|
26
|
-
const shadowX = x + (shadow.offsetX ?? 0);
|
|
27
|
-
const shadowY = y + (shadow.offsetY ?? 0);
|
|
28
|
-
objectRadius(ctx, shadowX, shadowY, width, height, shadow.borderRadius ?? 2);
|
|
29
|
-
ctx.fillStyle = shadow.gradient
|
|
30
|
-
? createGradient(ctx, shadow.gradient, shadowX, shadowY, shadowX + width, shadowY + height)
|
|
31
|
-
: shadow.color || "transparent";
|
|
32
|
-
ctx.fill();
|
|
13
|
+
exports.drawBoxBackground = drawBoxBackground;
|
|
14
|
+
function buildPath(ctx, x, y, w, h, radius = 0, borderPos = "all") {
|
|
15
|
+
ctx.beginPath();
|
|
16
|
+
if (radius === "circular") {
|
|
17
|
+
const r = Math.min(w, h) / 2;
|
|
18
|
+
ctx.arc(x + w / 2, y + h / 2, r, 0, Math.PI * 2);
|
|
19
|
+
ctx.closePath();
|
|
20
|
+
return;
|
|
33
21
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
22
|
+
if (!radius || radius <= 0) {
|
|
23
|
+
ctx.rect(x, y, w, h);
|
|
24
|
+
ctx.closePath();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const br = Math.min(radius, w / 2, h / 2);
|
|
28
|
+
const sel = new Set(borderPos.toLowerCase().split(",").map(s => s.trim()));
|
|
29
|
+
const has = (name) => sel.has("all") || sel.has(name) ||
|
|
30
|
+
(name === "top-left" && (sel.has("top") || sel.has("left"))) ||
|
|
31
|
+
(name === "top-right" && (sel.has("top") || sel.has("right"))) ||
|
|
32
|
+
(name === "bottom-right" && (sel.has("bottom") || sel.has("right"))) ||
|
|
33
|
+
(name === "bottom-left" && (sel.has("bottom") || sel.has("left")));
|
|
34
|
+
const tl = has("top-left") ? br : 0;
|
|
35
|
+
const tr = has("top-right") ? br : 0;
|
|
36
|
+
const brR = has("bottom-right") ? br : 0;
|
|
37
|
+
const bl = has("bottom-left") ? br : 0;
|
|
38
|
+
ctx.moveTo(x + tl, y);
|
|
39
|
+
ctx.lineTo(x + w - tr, y);
|
|
40
|
+
if (tr)
|
|
41
|
+
ctx.arcTo(x + w, y, x + w, y + tr, tr);
|
|
42
|
+
ctx.lineTo(x + w, y + h - brR);
|
|
43
|
+
if (brR)
|
|
44
|
+
ctx.arcTo(x + w, y + h, x + w - brR, y + h, brR);
|
|
45
|
+
ctx.lineTo(x + bl, y + h);
|
|
46
|
+
if (bl)
|
|
47
|
+
ctx.arcTo(x, y + h, x, y + h - bl, bl);
|
|
48
|
+
ctx.lineTo(x, y + tl);
|
|
49
|
+
if (tl)
|
|
50
|
+
ctx.arcTo(x, y, x + tl, y, tl);
|
|
51
|
+
ctx.closePath();
|
|
37
52
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
* @param ctx The canvas rendering context.
|
|
41
|
-
* @param zoom An object with scale (zoom factor) and the x/y coordinates that act as the zoom origin.
|
|
42
|
-
*/
|
|
43
|
-
function applyZoom(ctx, zoom) {
|
|
44
|
-
if (!zoom)
|
|
53
|
+
function applyRotation(ctx, deg, x, y, w, h) {
|
|
54
|
+
if (!deg)
|
|
45
55
|
return;
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
ctx.translate(
|
|
50
|
-
|
|
51
|
-
|
|
56
|
+
const cx = x + w / 2, cy = y + h / 2;
|
|
57
|
+
ctx.translate(cx, cy);
|
|
58
|
+
ctx.rotate((deg * Math.PI) / 180);
|
|
59
|
+
ctx.translate(-cx, -cy);
|
|
60
|
+
}
|
|
61
|
+
function rotatePoint(x, y, px, py, deg = 0) {
|
|
62
|
+
if (!deg)
|
|
63
|
+
return [x, y];
|
|
64
|
+
const a = (deg * Math.PI) / 180;
|
|
65
|
+
const dx = x - px, dy = y - py;
|
|
66
|
+
return [px + dx * Math.cos(a) - dy * Math.sin(a),
|
|
67
|
+
py + dx * Math.sin(a) + dy * Math.cos(a)];
|
|
52
68
|
}
|
|
53
69
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* @param stroke The stroke properties.
|
|
59
|
-
* @param x The x-coordinate of the shape.
|
|
60
|
-
* @param y The y-coordinate of the shape.
|
|
61
|
-
* @param width The width of the shape.
|
|
62
|
-
* @param height The height of the shape.
|
|
63
|
-
* @param blur Optional blur effect on the stroke.
|
|
70
|
+
* Build a gradient in **rect-local coordinates**:
|
|
71
|
+
* - Defaults for coords use rect {w,h}
|
|
72
|
+
* - Rotation pivot defaults to rect center
|
|
73
|
+
* - Offsets are applied by adding rect.x/rect.y to all points
|
|
64
74
|
*/
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
: stroke.color || "transparent";
|
|
75
|
-
ctx.lineWidth = stroke.width && stroke.width > 0 ? stroke.width : 2;
|
|
76
|
-
ctx.beginPath();
|
|
77
|
-
const borderPos = stroke.position || 0;
|
|
78
|
-
// Adjust the bounding box by borderPos
|
|
79
|
-
// - Positive borderPos moves stroke outward
|
|
80
|
-
// - Negative borderPos moves stroke inward
|
|
81
|
-
const offsetX = x - borderPos;
|
|
82
|
-
const offsetY = y - borderPos;
|
|
83
|
-
const offsetW = width + 2 * borderPos;
|
|
84
|
-
const offsetH = height + 2 * borderPos;
|
|
85
|
-
ctx.beginPath();
|
|
86
|
-
// For known shapes (circle, star, etc.), re-draw with adjusted bounding box
|
|
87
|
-
if ([
|
|
88
|
-
"heart", "arrow", "circle", "star",
|
|
89
|
-
"pentagon", "hexagon", "heptagon", "octagon",
|
|
90
|
-
"diamond", "trapezoid", "kite",
|
|
91
|
-
].includes(shapeName)) {
|
|
92
|
-
switch (shapeName) {
|
|
93
|
-
case "circle":
|
|
94
|
-
// Circle centered in the new bounding box
|
|
95
|
-
ctx.arc(offsetX + offsetW / 2, offsetY + offsetH / 2, offsetW / 2, // radius
|
|
96
|
-
0, Math.PI * 2);
|
|
97
|
-
break;
|
|
98
|
-
case "star":
|
|
99
|
-
drawStar(ctx, offsetX, offsetY, offsetW, offsetH);
|
|
100
|
-
break;
|
|
101
|
-
case "arrow":
|
|
102
|
-
drawArrow(ctx, offsetX, offsetY, offsetW, offsetH);
|
|
103
|
-
break;
|
|
104
|
-
case "pentagon":
|
|
105
|
-
case "hexagon":
|
|
106
|
-
case "heptagon":
|
|
107
|
-
case "octagon": {
|
|
108
|
-
const sides = parseInt(shapeName.replace(/\D/g, ""), 10);
|
|
109
|
-
drawPolygon(ctx, offsetX, offsetY, offsetW, offsetH, sides);
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
case "diamond":
|
|
113
|
-
ctx.moveTo(offsetX + offsetW / 2, offsetY);
|
|
114
|
-
ctx.lineTo(offsetX + offsetW, offsetY + offsetH / 2);
|
|
115
|
-
ctx.lineTo(offsetX + offsetW / 2, offsetY + offsetH);
|
|
116
|
-
ctx.lineTo(offsetX, offsetY + offsetH / 2);
|
|
117
|
-
ctx.closePath();
|
|
118
|
-
break;
|
|
119
|
-
case "trapezoid": {
|
|
120
|
-
const topWidth = offsetW * 0.6;
|
|
121
|
-
const offsetVal = (offsetW - topWidth) / 2;
|
|
122
|
-
ctx.moveTo(offsetX + offsetVal, offsetY);
|
|
123
|
-
ctx.lineTo(offsetX + offsetVal + topWidth, offsetY);
|
|
124
|
-
ctx.lineTo(offsetX + offsetW, offsetY + offsetH);
|
|
125
|
-
ctx.lineTo(offsetX, offsetY + offsetH);
|
|
126
|
-
ctx.closePath();
|
|
127
|
-
break;
|
|
128
|
-
}
|
|
129
|
-
case "heart":
|
|
130
|
-
drawHeart(ctx, offsetX, offsetY, offsetW, offsetH);
|
|
131
|
-
break;
|
|
132
|
-
case "kite":
|
|
133
|
-
ctx.moveTo(offsetX + offsetW / 2, offsetY);
|
|
134
|
-
ctx.lineTo(offsetX + offsetW, offsetY + offsetH / 2);
|
|
135
|
-
ctx.lineTo(offsetX + offsetW / 2, offsetY + offsetH);
|
|
136
|
-
ctx.lineTo(offsetX, offsetY + offsetH / 2);
|
|
137
|
-
ctx.closePath();
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
ctx.globalCompositeOperation = "source-atop";
|
|
143
|
-
objectRadius(ctx, offsetX, offsetY, offsetW, offsetH, stroke.borderRadius || 2, stroke.borderPosition);
|
|
75
|
+
function createGradientFill(ctx, g, rect) {
|
|
76
|
+
const { x, y, w, h } = rect;
|
|
77
|
+
if (g.type === "linear") {
|
|
78
|
+
const { startX = 0, startY = 0, endX = w, endY = 0, rotate = 0, pivotX = w / 2, pivotY = h / 2, colors } = g;
|
|
79
|
+
const [sx, sy] = rotatePoint(startX, startY, pivotX, pivotY, rotate);
|
|
80
|
+
const [ex, ey] = rotatePoint(endX, endY, pivotX, pivotY, rotate);
|
|
81
|
+
const grad = ctx.createLinearGradient(x + sx, y + sy, x + ex, y + ey);
|
|
82
|
+
colors.forEach(cs => grad.addColorStop(cs.stop, cs.color));
|
|
83
|
+
return grad;
|
|
144
84
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
85
|
+
// radial
|
|
86
|
+
const { startX = w / 2, startY = h / 2, startRadius = 0, endX = w / 2, endY = h / 2, endRadius = Math.max(w, h) / 2, rotate = 0, pivotX = w / 2, pivotY = h / 2, colors } = g;
|
|
87
|
+
const [sx, sy] = rotatePoint(startX, startY, pivotX, pivotY, rotate);
|
|
88
|
+
const [ex, ey] = rotatePoint(endX, endY, pivotX, pivotY, rotate);
|
|
89
|
+
const grad = ctx.createRadialGradient(x + sx, y + sy, startRadius, x + ex, y + ey, endRadius);
|
|
90
|
+
colors.forEach(cs => grad.addColorStop(cs.stop, cs.color));
|
|
91
|
+
return grad;
|
|
148
92
|
}
|
|
149
|
-
function
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
case
|
|
163
|
-
|
|
93
|
+
function fitInto(boxX, boxY, boxW, boxH, imgW, imgH, fit = "fill", align = "center") {
|
|
94
|
+
let dx = boxX, dy = boxY, dw = boxW, dh = boxH, sx = 0, sy = 0, sw = imgW, sh = imgH;
|
|
95
|
+
if (fit === "fill") {
|
|
96
|
+
return { dx, dy, dw, dh, sx, sy, sw, sh };
|
|
97
|
+
}
|
|
98
|
+
const s = fit === "contain"
|
|
99
|
+
? Math.min(boxW / imgW, boxH / imgH)
|
|
100
|
+
: Math.max(boxW / imgW, boxH / imgH);
|
|
101
|
+
dw = imgW * s;
|
|
102
|
+
dh = imgH * s;
|
|
103
|
+
const cx = boxX + (boxW - dw) / 2;
|
|
104
|
+
const cy = boxY + (boxH - dh) / 2;
|
|
105
|
+
switch (align) {
|
|
106
|
+
case "top-left":
|
|
107
|
+
dx = boxX;
|
|
108
|
+
dy = boxY;
|
|
164
109
|
break;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
ctx.lineTo(x, y + height);
|
|
169
|
-
ctx.lineTo(x + width, y + height);
|
|
170
|
-
ctx.closePath();
|
|
110
|
+
case "top":
|
|
111
|
+
dx = cx;
|
|
112
|
+
dy = boxY;
|
|
171
113
|
break;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
case 'heptagon':
|
|
176
|
-
case 'octagon': {
|
|
177
|
-
const sides = parseInt(shapeName.replace(/\D/g, ""), 10);
|
|
178
|
-
drawPolygon(ctx, x, y, width, height, sides);
|
|
114
|
+
case "top-right":
|
|
115
|
+
dx = boxX + boxW - dw;
|
|
116
|
+
dy = boxY;
|
|
179
117
|
break;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
118
|
+
case "left":
|
|
119
|
+
dx = boxX;
|
|
120
|
+
dy = cy;
|
|
183
121
|
break;
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
ctx.lineTo(x + width, y + height / 2);
|
|
188
|
-
ctx.lineTo(x + width / 2, y + height);
|
|
189
|
-
ctx.lineTo(x, y + height / 2);
|
|
190
|
-
ctx.closePath();
|
|
122
|
+
case "center":
|
|
123
|
+
dx = cx;
|
|
124
|
+
dy = cy;
|
|
191
125
|
break;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
126
|
+
case "right":
|
|
127
|
+
dx = boxX + boxW - dw;
|
|
128
|
+
dy = cy;
|
|
195
129
|
break;
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
130
|
+
case "bottom-left":
|
|
131
|
+
dx = boxX;
|
|
132
|
+
dy = boxY + boxH - dh;
|
|
199
133
|
break;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
134
|
+
case "bottom":
|
|
135
|
+
dx = cx;
|
|
136
|
+
dy = boxY + boxH - dh;
|
|
203
137
|
break;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
ctx.lineTo(x + width, y + height / 2);
|
|
208
|
-
ctx.lineTo(x + width / 2, y + height);
|
|
209
|
-
ctx.lineTo(x, y + height / 2);
|
|
210
|
-
ctx.closePath();
|
|
138
|
+
case "bottom-right":
|
|
139
|
+
dx = boxX + boxW - dw;
|
|
140
|
+
dy = boxY + boxH - dh;
|
|
211
141
|
break;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const offset = (width - topWidth) / 2;
|
|
216
|
-
ctx.moveTo(x + offset, y);
|
|
217
|
-
ctx.lineTo(x + offset + topWidth, y);
|
|
218
|
-
ctx.lineTo(x + width, y + height);
|
|
219
|
-
ctx.lineTo(x, y + height);
|
|
220
|
-
ctx.closePath();
|
|
142
|
+
default:
|
|
143
|
+
dx = cx;
|
|
144
|
+
dy = cy;
|
|
221
145
|
break;
|
|
222
|
-
}
|
|
223
|
-
default: {
|
|
224
|
-
ctx.restore();
|
|
225
|
-
throw new Error(`Unsupported shape: ${shapeName}`);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
if (isFilled) {
|
|
229
|
-
if (borderRadius && shapeName !== 'circle' && shapeName !== 'oval') {
|
|
230
|
-
objectRadius(ctx, x, y, width, height, borderRadius, borderPosition);
|
|
231
|
-
}
|
|
232
|
-
let fillStyle = color;
|
|
233
|
-
if (gradient) {
|
|
234
|
-
fillStyle = createGradient(ctx, gradient, x, y, x + width, y + height);
|
|
235
|
-
}
|
|
236
|
-
if (filling && filling.percentage <= 100) {
|
|
237
|
-
ctx.save();
|
|
238
|
-
const isCustomShape = ["heart", "arrow", "star", "pentagon", "hexagon", "heptagon", "octagon", "diamond", "trapezoid", "kite", "oval", "circle"].includes(shapeName);
|
|
239
|
-
if (isCustomShape) {
|
|
240
|
-
ctx.clip();
|
|
241
|
-
}
|
|
242
|
-
let fillX = x;
|
|
243
|
-
let fillY = y;
|
|
244
|
-
let fillWidth = width;
|
|
245
|
-
let fillHeight = height;
|
|
246
|
-
switch (filling.rotation) {
|
|
247
|
-
case 0:
|
|
248
|
-
fillHeight = (filling.percentage / 100) * height;
|
|
249
|
-
fillY = y + height - fillHeight;
|
|
250
|
-
break;
|
|
251
|
-
case 180:
|
|
252
|
-
fillHeight = (filling.percentage / 100) * height;
|
|
253
|
-
break;
|
|
254
|
-
case 90:
|
|
255
|
-
fillWidth = (filling.percentage / 100) * width;
|
|
256
|
-
break;
|
|
257
|
-
case 270:
|
|
258
|
-
fillWidth = (filling.percentage / 100) * width;
|
|
259
|
-
fillX = x + width - fillWidth;
|
|
260
|
-
break;
|
|
261
|
-
default:
|
|
262
|
-
console.warn(`Unsupported filling rotation: ${filling.rotation}, defaulting to 0 (Bottom to Top).`);
|
|
263
|
-
fillHeight = (filling.percentage / 100) * height;
|
|
264
|
-
fillY = y + height - fillHeight;
|
|
265
|
-
}
|
|
266
|
-
ctx.beginPath();
|
|
267
|
-
ctx.rect(fillX, fillY, fillWidth, fillHeight);
|
|
268
|
-
ctx.fillStyle = fillStyle;
|
|
269
|
-
ctx.fill();
|
|
270
|
-
ctx.restore();
|
|
271
|
-
}
|
|
272
|
-
else {
|
|
273
|
-
ctx.fillStyle = fillStyle;
|
|
274
|
-
ctx.fill();
|
|
275
|
-
}
|
|
276
146
|
}
|
|
277
|
-
|
|
278
|
-
if (stroke) {
|
|
279
|
-
if (stroke?.opacity)
|
|
280
|
-
ctx.globalAlpha = stroke.opacity;
|
|
281
|
-
applyStroke(ctx, stroke, x, y, width, height, shapeName);
|
|
282
|
-
ctx.globalAlpha = 1.0;
|
|
283
|
-
}
|
|
284
|
-
ctx.restore();
|
|
147
|
+
return { dx, dy, dw, dh, sx, sy, sw, sh };
|
|
285
148
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
149
|
+
// utils/imageCache.ts
|
|
150
|
+
const canvas_1 = require("@napi-rs/canvas");
|
|
151
|
+
const path_1 = __importDefault(require("path"));
|
|
152
|
+
const cache = new Map();
|
|
153
|
+
function loadImageCached(src) {
|
|
154
|
+
if (Buffer.isBuffer(src))
|
|
155
|
+
return (0, canvas_1.loadImage)(src);
|
|
156
|
+
const key = src.startsWith("http") ? src : path_1.default.resolve(process.cwd(), src);
|
|
157
|
+
if (!cache.has(key))
|
|
158
|
+
cache.set(key, (0, canvas_1.loadImage)(key));
|
|
159
|
+
return cache.get(key);
|
|
294
160
|
}
|
|
295
|
-
|
|
296
|
-
function
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const shaftWidth = width * 0.25;
|
|
309
|
-
const headWidth = width * 0.5;
|
|
310
|
-
const headHeight = height * 0.6;
|
|
311
|
-
ctx.beginPath();
|
|
312
|
-
ctx.moveTo(x, y + height / 2 - shaftWidth / 2);
|
|
313
|
-
ctx.lineTo(x + width - headWidth, y + height / 2 - shaftWidth / 2);
|
|
314
|
-
ctx.lineTo(x + width - headWidth, y);
|
|
315
|
-
ctx.lineTo(x + width, y + height / 2);
|
|
316
|
-
ctx.lineTo(x + width - headWidth, y + height);
|
|
317
|
-
ctx.lineTo(x + width - headWidth, y + height / 2 + shaftWidth / 2);
|
|
318
|
-
ctx.lineTo(x, y + height / 2 + shaftWidth / 2);
|
|
319
|
-
ctx.closePath();
|
|
320
|
-
}
|
|
321
|
-
function drawStar(ctx, x, y, width, height) {
|
|
322
|
-
const cx = x + width / 2;
|
|
323
|
-
const cy = y + height / 2;
|
|
324
|
-
const size = Math.min(width, height);
|
|
325
|
-
const outerRadius = size / 2;
|
|
326
|
-
const innerRadius = outerRadius * 0.5;
|
|
327
|
-
const rotationOffset = -Math.PI / 2;
|
|
328
|
-
ctx.beginPath();
|
|
329
|
-
for (let i = 0; i < 5; i++) {
|
|
330
|
-
let angle = (i * (Math.PI * 2)) / 5 + rotationOffset;
|
|
331
|
-
ctx.lineTo(cx + outerRadius * Math.cos(angle), cy + outerRadius * Math.sin(angle));
|
|
332
|
-
angle += Math.PI / 5;
|
|
333
|
-
ctx.lineTo(cx + innerRadius * Math.cos(angle), cy + innerRadius * Math.sin(angle));
|
|
334
|
-
}
|
|
335
|
-
ctx.closePath();
|
|
336
|
-
}
|
|
337
|
-
function createGradient(ctx, gradientOptions, startX, startY, endX, endY) {
|
|
338
|
-
if (!gradientOptions || !gradientOptions.type || !gradientOptions.colors) {
|
|
339
|
-
throw new Error("Invalid gradient options. Provide a valid object with type and colors properties.");
|
|
340
|
-
}
|
|
341
|
-
if (!Array.isArray(gradientOptions.colors)) {
|
|
342
|
-
throw new Error("Invalid gradient options. The colors property should be an array of color stops.");
|
|
343
|
-
}
|
|
344
|
-
if (gradientOptions.type === "linear") {
|
|
345
|
-
if (typeof startX !== "number" ||
|
|
346
|
-
typeof startY !== "number" ||
|
|
347
|
-
typeof endX !== "number" ||
|
|
348
|
-
typeof endY !== "number") {
|
|
349
|
-
throw new Error("Invalid gradient options for linear gradient. Numeric values are required for startX, startY, endX, and endY.");
|
|
350
|
-
}
|
|
351
|
-
if (typeof gradientOptions.rotate === "number") {
|
|
352
|
-
const centerX = (startX + endX) / 2;
|
|
353
|
-
const centerY = (startY + endY) / 2;
|
|
354
|
-
const dx = endX - startX;
|
|
355
|
-
const dy = endY - startY;
|
|
356
|
-
const length = Math.sqrt(dx * dx + dy * dy);
|
|
357
|
-
const angleRad = (gradientOptions.rotate * Math.PI) / 180;
|
|
358
|
-
startX = centerX - (length / 2) * Math.cos(angleRad);
|
|
359
|
-
startY = centerY - (length / 2) * Math.sin(angleRad);
|
|
360
|
-
endX = centerX + (length / 2) * Math.cos(angleRad);
|
|
361
|
-
endY = centerY + (length / 2) * Math.sin(angleRad);
|
|
362
|
-
}
|
|
363
|
-
const gradient = ctx.createLinearGradient(startX, startY, endX, endY);
|
|
364
|
-
for (const colorStop of gradientOptions.colors) {
|
|
365
|
-
if (typeof colorStop.stop !== "number" ||
|
|
366
|
-
typeof colorStop.color !== "string") {
|
|
367
|
-
throw new Error("Invalid color stop. Each color stop should have a numeric stop value and a color string.");
|
|
368
|
-
}
|
|
369
|
-
gradient.addColorStop(colorStop.stop, colorStop.color);
|
|
370
|
-
}
|
|
371
|
-
return gradient;
|
|
372
|
-
}
|
|
373
|
-
else if (gradientOptions.type === "radial") {
|
|
374
|
-
if (typeof gradientOptions.startX !== "number" ||
|
|
375
|
-
typeof gradientOptions.startY !== "number" ||
|
|
376
|
-
typeof gradientOptions.startRadius !== "number" ||
|
|
377
|
-
typeof gradientOptions.endX !== "number" ||
|
|
378
|
-
typeof gradientOptions.endY !== "number" ||
|
|
379
|
-
typeof gradientOptions.endRadius !== "number") {
|
|
380
|
-
throw new Error("Invalid gradient options for radial gradient. Numeric values are required for startX, startY, startRadius, endX, endY, and endRadius.");
|
|
381
|
-
}
|
|
382
|
-
const gradient = ctx.createRadialGradient(gradientOptions.startX, gradientOptions.startY, gradientOptions.startRadius, gradientOptions.endX, gradientOptions.endY, gradientOptions.endRadius);
|
|
383
|
-
for (const colorStop of gradientOptions.colors) {
|
|
384
|
-
if (typeof colorStop.stop !== "number" ||
|
|
385
|
-
typeof colorStop.color !== "string") {
|
|
386
|
-
throw new Error("Invalid color stop. Each color stop should have a numeric stop value and a color string.");
|
|
387
|
-
}
|
|
388
|
-
gradient.addColorStop(colorStop.stop, colorStop.color);
|
|
389
|
-
}
|
|
390
|
-
return gradient;
|
|
161
|
+
// Single implementation handling both
|
|
162
|
+
function applyShadow(ctx, a, b, c, d, e, f, g) {
|
|
163
|
+
let rect;
|
|
164
|
+
let shadow;
|
|
165
|
+
let radius;
|
|
166
|
+
let borderPos;
|
|
167
|
+
// Detect which overload we’re in
|
|
168
|
+
if (typeof a === "object" && "x" in a && "w" in a) {
|
|
169
|
+
// (ctx, rect, shadow)
|
|
170
|
+
rect = a;
|
|
171
|
+
shadow = b;
|
|
172
|
+
radius = shadow?.borderRadius ?? 0;
|
|
173
|
+
borderPos = shadow?.borderPosition ?? "all";
|
|
391
174
|
}
|
|
392
175
|
else {
|
|
393
|
-
|
|
176
|
+
// (ctx, shadow, x, y, w, h, radius?, borderPos?)
|
|
177
|
+
shadow = a;
|
|
178
|
+
rect = { x: b, y: c, w: d, h: e };
|
|
179
|
+
radius = f ?? shadow?.borderRadius ?? 0;
|
|
180
|
+
borderPos = g ?? shadow?.borderPosition ?? "all";
|
|
394
181
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
* @param rotation The rotation angle in degrees.
|
|
400
|
-
* @param x The x-coordinate of the center of rotation.
|
|
401
|
-
* @param y The y-coordinate of the center of rotation.
|
|
402
|
-
* @param width The width of the shape.
|
|
403
|
-
* @param height The height of the shape.
|
|
404
|
-
*/
|
|
405
|
-
function applyRotation(ctx, rotation, x, y, width, height) {
|
|
406
|
-
const rotationX = x + width / 2;
|
|
407
|
-
const rotationY = y + height / 2;
|
|
408
|
-
ctx.translate(rotationX, rotationY);
|
|
409
|
-
ctx.rotate((rotation * Math.PI) / 180);
|
|
410
|
-
ctx.translate(-rotationX, -rotationY);
|
|
411
|
-
}
|
|
412
|
-
/**
|
|
413
|
-
* Applies border radius to the canvas context with selective corner support.
|
|
414
|
-
*
|
|
415
|
-
* @param ctx The canvas rendering context.
|
|
416
|
-
* @param image The image to be drawn (will be drawn after clipping).
|
|
417
|
-
* @param x The x-coordinate of the shape.
|
|
418
|
-
* @param y The y-coordinate of the shape.
|
|
419
|
-
* @param width The width of the shape.
|
|
420
|
-
* @param height The height of the shape.
|
|
421
|
-
* @param borderRadius The border radius value (number or "circular").
|
|
422
|
-
* @param borderPosition The sides or corners to round.
|
|
423
|
-
* Valid values include:
|
|
424
|
-
* - "all"
|
|
425
|
-
* - "top", "bottom", "left", "right"
|
|
426
|
-
* - "top-left", "top-right", "bottom-left", "bottom-right"
|
|
427
|
-
* - Or a comma‑separated list (e.g., "top, left, bottom")
|
|
428
|
-
*/
|
|
429
|
-
function imageRadius(ctx, image, x, y, width, height, borderRadius, borderPosition = "all") {
|
|
182
|
+
if (!shadow)
|
|
183
|
+
return;
|
|
184
|
+
const { color = "rgba(0,0,0,1)", gradient, opacity = 0.4, offsetX = 0, offsetY = 0, blur = 20 } = shadow;
|
|
185
|
+
const r = { x: rect.x + offsetX, y: rect.y + offsetY, w: rect.w, h: rect.h };
|
|
430
186
|
ctx.save();
|
|
431
|
-
ctx.
|
|
432
|
-
if (
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
const br = Math.min(borderRadius, width / 2, height / 2);
|
|
439
|
-
const selectedPositions = new Set(borderPosition.toLowerCase().split(",").map((s) => s.trim()));
|
|
440
|
-
const roundTopLeft = selectedPositions.has("all") || selectedPositions.has("top-left") || (selectedPositions.has("top") && selectedPositions.has("left"));
|
|
441
|
-
const roundTopRight = selectedPositions.has("all") || selectedPositions.has("top-right") || (selectedPositions.has("top") && selectedPositions.has("right"));
|
|
442
|
-
const roundBottomRight = selectedPositions.has("all") || selectedPositions.has("bottom-right") || (selectedPositions.has("bottom") && selectedPositions.has("right"));
|
|
443
|
-
const roundBottomLeft = selectedPositions.has("all") || selectedPositions.has("bottom-left") || (selectedPositions.has("bottom") && selectedPositions.has("left"));
|
|
444
|
-
const tl = roundTopLeft ? br : 0;
|
|
445
|
-
const tr = roundTopRight ? br : 0;
|
|
446
|
-
const brR = roundBottomRight ? br : 0;
|
|
447
|
-
const bl = roundBottomLeft ? br : 0;
|
|
448
|
-
ctx.moveTo(x + tl, y);
|
|
449
|
-
ctx.lineTo(x + width - tr, y);
|
|
450
|
-
if (tr > 0)
|
|
451
|
-
ctx.arc(x + width - tr, y + tr, tr, -Math.PI / 2, 0, false);
|
|
452
|
-
ctx.lineTo(x + width, y + height - brR);
|
|
453
|
-
if (brR > 0)
|
|
454
|
-
ctx.arc(x + width - brR, y + height - brR, brR, 0, Math.PI / 2, false);
|
|
455
|
-
ctx.lineTo(x + bl, y + height);
|
|
456
|
-
if (bl > 0)
|
|
457
|
-
ctx.arc(x + bl, y + height - bl, bl, Math.PI / 2, Math.PI, false);
|
|
458
|
-
ctx.lineTo(x, y + tl);
|
|
459
|
-
if (tl > 0)
|
|
460
|
-
ctx.arc(x + tl, y + tl, tl, Math.PI, -Math.PI / 2, false);
|
|
461
|
-
ctx.closePath();
|
|
462
|
-
ctx.clip();
|
|
187
|
+
ctx.globalAlpha = opacity;
|
|
188
|
+
if (blur > 0)
|
|
189
|
+
ctx.filter = `blur(${blur}px)`;
|
|
190
|
+
buildPath(ctx, r.x, r.y, r.w, r.h, radius, borderPos);
|
|
191
|
+
if (gradient) {
|
|
192
|
+
const gfill = createGradientFill(ctx, gradient, r);
|
|
193
|
+
ctx.fillStyle = gfill;
|
|
463
194
|
}
|
|
464
195
|
else {
|
|
465
|
-
ctx.
|
|
466
|
-
ctx.clip();
|
|
196
|
+
ctx.fillStyle = color;
|
|
467
197
|
}
|
|
468
|
-
ctx.
|
|
198
|
+
ctx.fill();
|
|
199
|
+
ctx.filter = "none";
|
|
200
|
+
ctx.globalAlpha = 1;
|
|
469
201
|
ctx.restore();
|
|
470
202
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
* - "top-left", "top-right", "bottom-left", "bottom-right"
|
|
484
|
-
* - Or a comma-separated list, e.g. "top-left, bottom-right" or "top, left, bottom"
|
|
485
|
-
*/
|
|
486
|
-
function objectRadius(ctx, x, y, width, height, borderRadius = 0.1, borderPosition = "all") {
|
|
487
|
-
ctx.beginPath();
|
|
488
|
-
if (borderRadius === "circular") {
|
|
489
|
-
// Draw a circular shape
|
|
490
|
-
const circleRadius = Math.min(width, height) / 2;
|
|
491
|
-
ctx.arc(x + width / 2, y + height / 2, circleRadius, 0, 2 * Math.PI);
|
|
492
|
-
}
|
|
493
|
-
else if (borderRadius > 0) {
|
|
494
|
-
const br = Math.min(borderRadius, width / 2, height / 2);
|
|
495
|
-
const selectedPositions = new Set(borderPosition.toLowerCase().split(",").map((s) => s.trim()));
|
|
496
|
-
// **Correct Grouping**
|
|
497
|
-
const roundTopLeft = selectedPositions.has("all") ||
|
|
498
|
-
selectedPositions.has("top-left") ||
|
|
499
|
-
selectedPositions.has("top") ||
|
|
500
|
-
selectedPositions.has("left");
|
|
501
|
-
const roundTopRight = selectedPositions.has("all") ||
|
|
502
|
-
selectedPositions.has("top-right") ||
|
|
503
|
-
selectedPositions.has("top") ||
|
|
504
|
-
selectedPositions.has("right");
|
|
505
|
-
const roundBottomRight = selectedPositions.has("all") ||
|
|
506
|
-
selectedPositions.has("bottom-right") ||
|
|
507
|
-
selectedPositions.has("bottom") ||
|
|
508
|
-
selectedPositions.has("right");
|
|
509
|
-
const roundBottomLeft = selectedPositions.has("all") ||
|
|
510
|
-
selectedPositions.has("bottom-left") ||
|
|
511
|
-
selectedPositions.has("bottom") ||
|
|
512
|
-
selectedPositions.has("left");
|
|
513
|
-
// **Assign correct radii**
|
|
514
|
-
const tl = roundTopLeft ? br : 0;
|
|
515
|
-
const tr = roundTopRight ? br : 0;
|
|
516
|
-
const brR = roundBottomRight ? br : 0;
|
|
517
|
-
const bl = roundBottomLeft ? br : 0;
|
|
518
|
-
// **Draw rounded rectangle**
|
|
519
|
-
ctx.moveTo(x + tl, y);
|
|
520
|
-
ctx.lineTo(x + width - tr, y);
|
|
521
|
-
if (tr > 0)
|
|
522
|
-
ctx.arc(x + width - tr, y + tr, tr, -Math.PI / 2, 0, false);
|
|
523
|
-
ctx.lineTo(x + width, y + height - brR);
|
|
524
|
-
if (brR > 0)
|
|
525
|
-
ctx.arc(x + width - brR, y + height - brR, brR, 0, Math.PI / 2, false);
|
|
526
|
-
ctx.lineTo(x + bl, y + height);
|
|
527
|
-
if (bl > 0)
|
|
528
|
-
ctx.arc(x + bl, y + height - bl, bl, Math.PI / 2, Math.PI, false);
|
|
529
|
-
ctx.lineTo(x, y + tl);
|
|
530
|
-
if (tl > 0)
|
|
531
|
-
ctx.arc(x + tl, y + tl, tl, Math.PI, -Math.PI / 2, false);
|
|
203
|
+
// Single implementation handling both
|
|
204
|
+
function applyStroke(ctx, a, b, c, d, e, f, g) {
|
|
205
|
+
let rect;
|
|
206
|
+
let stroke;
|
|
207
|
+
let radius;
|
|
208
|
+
let borderPos;
|
|
209
|
+
if (typeof a === "object" && "x" in a && "w" in a) {
|
|
210
|
+
// (ctx, rect, stroke)
|
|
211
|
+
rect = a;
|
|
212
|
+
stroke = b;
|
|
213
|
+
radius = stroke?.borderRadius ?? 0;
|
|
214
|
+
borderPos = stroke?.borderPosition ?? "all";
|
|
532
215
|
}
|
|
533
216
|
else {
|
|
534
|
-
//
|
|
535
|
-
|
|
217
|
+
// (ctx, stroke, x, y, w, h, radius?, borderPos?)
|
|
218
|
+
stroke = a;
|
|
219
|
+
rect = { x: b, y: c, w: d, h: e };
|
|
220
|
+
radius = f ?? stroke?.borderRadius ?? 0;
|
|
221
|
+
borderPos = g ?? stroke?.borderPosition ?? "all";
|
|
536
222
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
function bilinearInterpolate(corners, t, u) {
|
|
547
|
-
const top = {
|
|
548
|
-
x: corners[0].x * (1 - t) + corners[1].x * t,
|
|
549
|
-
y: corners[0].y * (1 - t) + corners[1].y * t,
|
|
550
|
-
};
|
|
551
|
-
const bottom = {
|
|
552
|
-
x: corners[3].x * (1 - t) + corners[2].x * t,
|
|
553
|
-
y: corners[3].y * (1 - t) + corners[2].y * t,
|
|
554
|
-
};
|
|
555
|
-
return {
|
|
556
|
-
x: top.x * (1 - u) + bottom.x * u,
|
|
557
|
-
y: top.y * (1 - u) + bottom.y * u,
|
|
223
|
+
if (!stroke)
|
|
224
|
+
return;
|
|
225
|
+
const { color = "#000", gradient, width = 2, position = 0, blur = 0, opacity = 1 } = stroke;
|
|
226
|
+
// expand/shrink by `position`
|
|
227
|
+
const r = {
|
|
228
|
+
x: rect.x - position,
|
|
229
|
+
y: rect.y - position,
|
|
230
|
+
w: rect.w + position * 2,
|
|
231
|
+
h: rect.h + position * 2
|
|
558
232
|
};
|
|
233
|
+
ctx.save();
|
|
234
|
+
if (blur > 0)
|
|
235
|
+
ctx.filter = `blur(${blur}px)`;
|
|
236
|
+
ctx.globalAlpha = opacity;
|
|
237
|
+
buildPath(ctx, r.x, r.y, r.w, r.h, radius, borderPos);
|
|
238
|
+
ctx.lineWidth = width;
|
|
239
|
+
if (gradient) {
|
|
240
|
+
const gstroke = createGradientFill(ctx, gradient, r);
|
|
241
|
+
ctx.strokeStyle = gstroke;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
ctx.strokeStyle = color;
|
|
245
|
+
}
|
|
246
|
+
ctx.stroke();
|
|
247
|
+
ctx.filter = "none";
|
|
248
|
+
ctx.globalAlpha = 1;
|
|
249
|
+
ctx.restore();
|
|
559
250
|
}
|
|
560
|
-
/**
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
* @param gridRows Number of rows to subdivide (default: 10).
|
|
574
|
-
*/
|
|
575
|
-
async function applyPerspective(ctx, image, x, y, width, height, perspective, gridCols = 10, gridRows = 10) {
|
|
576
|
-
const cellWidth = width / gridCols;
|
|
577
|
-
const cellHeight = height / gridRows;
|
|
578
|
-
for (let row = 0; row < gridRows; row++) {
|
|
579
|
-
for (let col = 0; col < gridCols; col++) {
|
|
580
|
-
const sx = x + col * cellWidth;
|
|
581
|
-
const sy = y + row * cellHeight;
|
|
582
|
-
const t0 = col / gridCols;
|
|
583
|
-
const t1 = (col + 1) / gridCols;
|
|
584
|
-
const u0 = row / gridRows;
|
|
585
|
-
const u1 = (row + 1) / gridRows;
|
|
586
|
-
const destTL = bilinearInterpolate([perspective.topLeft, perspective.topRight, perspective.bottomRight, perspective.bottomLeft], t0, u0);
|
|
587
|
-
const destTR = bilinearInterpolate([perspective.topLeft, perspective.topRight, perspective.bottomRight, perspective.bottomLeft], t1, u0);
|
|
588
|
-
const destBL = bilinearInterpolate([perspective.topLeft, perspective.topRight, perspective.bottomRight, perspective.bottomLeft], t0, u1);
|
|
589
|
-
const a = (destTR.x - destTL.x) / cellWidth;
|
|
590
|
-
const b = (destTR.y - destTL.y) / cellWidth;
|
|
591
|
-
const c = (destBL.x - destTL.x) / cellHeight;
|
|
592
|
-
const d = (destBL.y - destTL.y) / cellHeight;
|
|
593
|
-
const e = destTL.x;
|
|
594
|
-
const f = destTL.y;
|
|
595
|
-
ctx.save();
|
|
596
|
-
ctx.setTransform(a, b, c, d, e, f);
|
|
597
|
-
ctx.drawImage(image, sx, sy, cellWidth, cellHeight, 0, 0, cellWidth, cellHeight);
|
|
598
|
-
ctx.restore();
|
|
599
|
-
}
|
|
251
|
+
/** Optional “box background” under the bitmap, inside the image clip */
|
|
252
|
+
function drawBoxBackground(ctx, rect, boxBg, borderRadius, borderPosition) {
|
|
253
|
+
if (!boxBg)
|
|
254
|
+
return;
|
|
255
|
+
const { color, gradient } = boxBg;
|
|
256
|
+
// clip to the box radius, then fill
|
|
257
|
+
ctx.save();
|
|
258
|
+
buildPath(ctx, rect.x, rect.y, rect.w, rect.h, borderRadius ?? 0, borderPosition ?? "all");
|
|
259
|
+
ctx.clip();
|
|
260
|
+
if (gradient) {
|
|
261
|
+
const g = createGradientFill(ctx, gradient, rect);
|
|
262
|
+
ctx.fillStyle = g;
|
|
263
|
+
ctx.fillRect(rect.x, rect.y, rect.w, rect.h);
|
|
600
264
|
}
|
|
265
|
+
else if (color && color !== "transparent") {
|
|
266
|
+
ctx.fillStyle = color;
|
|
267
|
+
ctx.fillRect(rect.x, rect.y, rect.w, rect.h);
|
|
268
|
+
}
|
|
269
|
+
ctx.restore();
|
|
601
270
|
}
|
|
602
271
|
//# sourceMappingURL=imageProperties.js.map
|