apexify.js 4.6.2 → 4.6.3
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/dist/cjs/ai/functions/readFiles.js +1 -1
- package/dist/cjs/ai/functions/readFiles.js.map +1 -1
- package/dist/cjs/ai/modals-chat/electronHub/chatmodels.d.ts.map +1 -1
- package/dist/cjs/ai/modals-chat/electronHub/chatmodels.js +2 -1
- package/dist/cjs/ai/modals-chat/electronHub/chatmodels.js.map +1 -1
- package/dist/cjs/ai/modals-chat/electronHub/speechModels.d.ts.map +1 -1
- package/dist/cjs/ai/modals-chat/electronHub/speechModels.js +1 -1
- package/dist/cjs/ai/modals-chat/electronHub/speechModels.js.map +1 -1
- package/dist/cjs/canvas/ApexPainter.d.ts.map +1 -1
- package/dist/cjs/canvas/ApexPainter.js +27 -28
- package/dist/cjs/canvas/ApexPainter.js.map +1 -1
- package/dist/cjs/canvas/Themes/Level-Up/levelup.d.ts +11 -0
- package/dist/cjs/canvas/Themes/Level-Up/levelup.d.ts.map +1 -0
- package/dist/cjs/canvas/Themes/Level-Up/levelup.js +163 -0
- package/dist/cjs/canvas/Themes/Level-Up/levelup.js.map +1 -0
- package/dist/cjs/canvas/utils/Background/bg.d.ts +17 -10
- package/dist/cjs/canvas/utils/Background/bg.d.ts.map +1 -1
- package/dist/cjs/canvas/utils/Background/bg.js +102 -27
- package/dist/cjs/canvas/utils/Background/bg.js.map +1 -1
- package/dist/cjs/canvas/utils/Custom/customLines.d.ts.map +1 -1
- package/dist/cjs/canvas/utils/Custom/customLines.js +43 -19
- package/dist/cjs/canvas/utils/Custom/customLines.js.map +1 -1
- package/dist/cjs/canvas/utils/General/general functions.d.ts +6 -1
- package/dist/cjs/canvas/utils/General/general functions.d.ts.map +1 -1
- package/dist/cjs/canvas/utils/General/general functions.js +19 -20
- package/dist/cjs/canvas/utils/General/general functions.js.map +1 -1
- package/dist/cjs/canvas/utils/Image/imageProperties.d.ts +3 -9
- package/dist/cjs/canvas/utils/Image/imageProperties.d.ts.map +1 -1
- package/dist/cjs/canvas/utils/Image/imageProperties.js +220 -214
- package/dist/cjs/canvas/utils/Image/imageProperties.js.map +1 -1
- package/dist/cjs/canvas/utils/Texts/textProperties.d.ts +12 -14
- package/dist/cjs/canvas/utils/Texts/textProperties.d.ts.map +1 -1
- package/dist/cjs/canvas/utils/Texts/textProperties.js +100 -91
- package/dist/cjs/canvas/utils/Texts/textProperties.js.map +1 -1
- package/dist/cjs/canvas/utils/types.d.ts +89 -109
- package/dist/cjs/canvas/utils/types.d.ts.map +1 -1
- package/dist/cjs/canvas/utils/types.js.map +1 -1
- package/dist/cjs/canvas/utils/utils.d.ts +2 -4
- package/dist/cjs/canvas/utils/utils.d.ts.map +1 -1
- package/dist/cjs/canvas/utils/utils.js +2 -5
- package/dist/cjs/canvas/utils/utils.js.map +1 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +31 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/ai/functions/readFiles.js +1 -1
- package/dist/esm/ai/functions/readFiles.js.map +1 -1
- package/dist/esm/ai/modals-chat/electronHub/chatmodels.d.ts.map +1 -1
- package/dist/esm/ai/modals-chat/electronHub/chatmodels.js +2 -1
- package/dist/esm/ai/modals-chat/electronHub/chatmodels.js.map +1 -1
- package/dist/esm/ai/modals-chat/electronHub/speechModels.d.ts.map +1 -1
- package/dist/esm/ai/modals-chat/electronHub/speechModels.js +1 -1
- package/dist/esm/ai/modals-chat/electronHub/speechModels.js.map +1 -1
- package/dist/esm/canvas/ApexPainter.d.ts.map +1 -1
- package/dist/esm/canvas/ApexPainter.js +27 -28
- package/dist/esm/canvas/ApexPainter.js.map +1 -1
- package/dist/esm/canvas/Themes/Level-Up/levelup.d.ts +11 -0
- package/dist/esm/canvas/Themes/Level-Up/levelup.d.ts.map +1 -0
- package/dist/esm/canvas/Themes/Level-Up/levelup.js +163 -0
- package/dist/esm/canvas/Themes/Level-Up/levelup.js.map +1 -0
- package/dist/esm/canvas/utils/Background/bg.d.ts +17 -10
- package/dist/esm/canvas/utils/Background/bg.d.ts.map +1 -1
- package/dist/esm/canvas/utils/Background/bg.js +102 -27
- package/dist/esm/canvas/utils/Background/bg.js.map +1 -1
- package/dist/esm/canvas/utils/Custom/customLines.d.ts.map +1 -1
- package/dist/esm/canvas/utils/Custom/customLines.js +43 -19
- package/dist/esm/canvas/utils/Custom/customLines.js.map +1 -1
- package/dist/esm/canvas/utils/General/general functions.d.ts +6 -1
- package/dist/esm/canvas/utils/General/general functions.d.ts.map +1 -1
- package/dist/esm/canvas/utils/General/general functions.js +19 -20
- package/dist/esm/canvas/utils/General/general functions.js.map +1 -1
- package/dist/esm/canvas/utils/Image/imageProperties.d.ts +3 -9
- package/dist/esm/canvas/utils/Image/imageProperties.d.ts.map +1 -1
- package/dist/esm/canvas/utils/Image/imageProperties.js +220 -214
- package/dist/esm/canvas/utils/Image/imageProperties.js.map +1 -1
- package/dist/esm/canvas/utils/Texts/textProperties.d.ts +12 -14
- package/dist/esm/canvas/utils/Texts/textProperties.d.ts.map +1 -1
- package/dist/esm/canvas/utils/Texts/textProperties.js +100 -91
- package/dist/esm/canvas/utils/Texts/textProperties.js.map +1 -1
- package/dist/esm/canvas/utils/types.d.ts +89 -109
- package/dist/esm/canvas/utils/types.d.ts.map +1 -1
- package/dist/esm/canvas/utils/types.js.map +1 -1
- package/dist/esm/canvas/utils/utils.d.ts +2 -4
- package/dist/esm/canvas/utils/utils.d.ts.map +1 -1
- package/dist/esm/canvas/utils/utils.js +2 -5
- package/dist/esm/canvas/utils/utils.js.map +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +31 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/lib/ai/functions/readFiles.ts +1 -1
- package/lib/ai/modals-chat/electronHub/chatmodels.ts +4 -2
- package/lib/ai/modals-chat/electronHub/speechModels.ts +2 -1
- package/lib/canvas/ApexPainter.ts +52 -61
- package/lib/canvas/Themes/Level-Up/levelup.ts +183 -0
- package/lib/canvas/utils/Background/bg.ts +179 -65
- package/lib/canvas/utils/Custom/customLines.ts +53 -20
- package/lib/canvas/utils/General/general functions.ts +21 -29
- package/lib/canvas/utils/Image/imageProperties.ts +399 -318
- package/lib/canvas/utils/Texts/textProperties.ts +213 -162
- package/lib/canvas/utils/types.ts +74 -107
- package/lib/canvas/utils/utils.ts +2 -5
- package/lib/index.ts +38 -10
- package/package.json +2 -2
- package/dist/cjs/canvas/utils/Background/circular.d.ts +0 -3
- package/dist/cjs/canvas/utils/Background/circular.d.ts.map +0 -1
- package/dist/cjs/canvas/utils/Background/circular.js +0 -13
- package/dist/cjs/canvas/utils/Background/circular.js.map +0 -1
- package/dist/cjs/canvas/utils/Background/radius.d.ts +0 -18
- package/dist/cjs/canvas/utils/Background/radius.d.ts.map +0 -1
- package/dist/cjs/canvas/utils/Background/radius.js +0 -104
- package/dist/cjs/canvas/utils/Background/radius.js.map +0 -1
- package/dist/esm/canvas/utils/Background/circular.d.ts +0 -3
- package/dist/esm/canvas/utils/Background/circular.d.ts.map +0 -1
- package/dist/esm/canvas/utils/Background/circular.js +0 -13
- package/dist/esm/canvas/utils/Background/circular.js.map +0 -1
- package/dist/esm/canvas/utils/Background/radius.d.ts +0 -18
- package/dist/esm/canvas/utils/Background/radius.d.ts.map +0 -1
- package/dist/esm/canvas/utils/Background/radius.js +0 -104
- package/dist/esm/canvas/utils/Background/radius.js.map +0 -1
- package/lib/canvas/utils/Background/circular.ts +0 -17
- package/lib/canvas/utils/Background/radius.ts +0 -102
|
@@ -6,13 +6,10 @@ import axios from 'axios';
|
|
|
6
6
|
import fs from "fs";
|
|
7
7
|
import path from "path";
|
|
8
8
|
import { OutputFormat, CanvasConfig, TextObject, ImageProperties, GIFOptions, GIFResults, CustomOptions, cropOptions,
|
|
9
|
-
drawBackgroundGradient, drawBackgroundColor, customBackground,
|
|
9
|
+
drawBackgroundGradient, drawBackgroundColor, customBackground, customLines, applyRotation, applyStroke,
|
|
10
10
|
applyShadow, imageRadius, drawShape, drawText, converter, resizingImg, applyColorFilters, imgEffects,verticalBarChart, pieChart,
|
|
11
11
|
lineChart, cropInner, cropOuter, bgRemoval, detectColors, removeColor, dataURL, base64, arrayBuffer, blob, url, GradientConfig, Frame,
|
|
12
|
-
PatternOptions, ExtractFramesOptions,
|
|
13
|
-
applyZoom,
|
|
14
|
-
ResizeOptions,
|
|
15
|
-
applyPerspective
|
|
12
|
+
PatternOptions, ExtractFramesOptions, backgroundRadius, applyZoom, ResizeOptions, applyPerspective
|
|
16
13
|
} from "./utils/utils";
|
|
17
14
|
|
|
18
15
|
interface CanvasResults {
|
|
@@ -49,7 +46,8 @@ export class ApexPainter {
|
|
|
49
46
|
shadow,
|
|
50
47
|
borderPosition = 'all',
|
|
51
48
|
opacity = 1,
|
|
52
|
-
zoom
|
|
49
|
+
zoom,
|
|
50
|
+
blur
|
|
53
51
|
} = canvas;
|
|
54
52
|
|
|
55
53
|
const canvasInstance = createCanvas(width, height);
|
|
@@ -64,23 +62,20 @@ export class ApexPainter {
|
|
|
64
62
|
applyShadow(ctx, shadow, x, y, width, height);
|
|
65
63
|
|
|
66
64
|
ctx.save();
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
} else if (typeof borderRadius === 'number') {
|
|
70
|
-
radiusBorder(ctx, x, y, width, height, borderRadius, borderPosition);
|
|
65
|
+
if (borderRadius) {
|
|
66
|
+
backgroundRadius(ctx, x, y, width, height, borderRadius, borderPosition);
|
|
71
67
|
}
|
|
72
68
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// applyZoom(ctx, zoom);
|
|
69
|
+
ctx.translate(x, y);
|
|
76
70
|
|
|
77
71
|
if (customBg) {
|
|
78
|
-
await customBackground(ctx, canvas);
|
|
72
|
+
await customBackground(ctx, canvas, zoom, blur);
|
|
79
73
|
} else if (gradientBg) {
|
|
80
|
-
await drawBackgroundGradient(ctx, canvas);
|
|
74
|
+
await drawBackgroundGradient(ctx, canvas, zoom, blur);
|
|
81
75
|
} else {
|
|
82
|
-
await drawBackgroundColor(ctx, canvas);
|
|
76
|
+
await drawBackgroundColor(ctx, canvas, zoom, blur);
|
|
83
77
|
}
|
|
78
|
+
|
|
84
79
|
ctx.restore();
|
|
85
80
|
|
|
86
81
|
applyStroke(ctx, stroke, x, y, width, height);
|
|
@@ -165,12 +160,12 @@ export class ApexPainter {
|
|
|
165
160
|
|
|
166
161
|
GlobalFonts.registerFromPath(
|
|
167
162
|
path.join(process.cwd(), mergedTextOptions.fontPath),
|
|
168
|
-
mergedTextOptions.fontName,
|
|
163
|
+
(mergedTextOptions.fontName || 'customFont'),
|
|
169
164
|
);
|
|
170
165
|
}
|
|
171
166
|
|
|
172
167
|
const size = { width: existingImage.width, height: existingImage.height };
|
|
173
|
-
drawText(ctx, mergedTextOptions
|
|
168
|
+
drawText(ctx, mergedTextOptions);
|
|
174
169
|
}
|
|
175
170
|
|
|
176
171
|
return canvas.toBuffer("image/png");
|
|
@@ -469,87 +464,83 @@ async createGIF(gifFrames: { background: string; duration: number }[], options:
|
|
|
469
464
|
}
|
|
470
465
|
}
|
|
471
466
|
|
|
472
|
-
private async drawImage(
|
|
473
|
-
ctx: SKRSContext2D,
|
|
474
|
-
image: ImageProperties
|
|
475
|
-
): Promise<void> {
|
|
467
|
+
private async drawImage(ctx: SKRSContext2D, image: ImageProperties): Promise<void> {
|
|
476
468
|
const {
|
|
477
469
|
source,
|
|
478
|
-
x,
|
|
479
|
-
y,
|
|
480
|
-
width,
|
|
481
|
-
height,
|
|
470
|
+
x, y, width, height,
|
|
482
471
|
rotation = 0,
|
|
483
472
|
borderRadius = 0,
|
|
484
|
-
stroke,
|
|
485
|
-
|
|
486
|
-
isFilled,
|
|
487
|
-
color,
|
|
488
|
-
gradient,
|
|
473
|
+
stroke, shadow,
|
|
474
|
+
isFilled, color, gradient,
|
|
489
475
|
borderPosition = 'all',
|
|
490
476
|
opacity,
|
|
491
|
-
perspective
|
|
477
|
+
perspective,
|
|
478
|
+
blur = 0,
|
|
479
|
+
zoom,
|
|
480
|
+
filling
|
|
492
481
|
} = image;
|
|
493
482
|
|
|
494
|
-
if (
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
typeof y !== 'number' ||
|
|
498
|
-
typeof width !== 'number' ||
|
|
499
|
-
typeof height !== 'number'
|
|
500
|
-
) {
|
|
483
|
+
if (perspective) throw new Error("`perspective` Feature is under construction. Please refrain from using it.");
|
|
484
|
+
|
|
485
|
+
if (!source || typeof x !== 'number' || typeof y !== 'number' || typeof width !== 'number' || typeof height !== 'number') {
|
|
501
486
|
throw new Error('Invalid image properties: source, x, y, width, and height are required.');
|
|
502
487
|
}
|
|
503
|
-
|
|
504
|
-
const shapeNames = ['circle', 'square', 'triangle', 'pentagon', 'hexagon', 'heptagon', 'octagon', 'star', 'kite'];
|
|
488
|
+
|
|
489
|
+
const shapeNames = ['circle', 'square', 'triangle', 'pentagon', 'hexagon', 'heptagon', 'octagon', 'star', 'kite', 'arrow', 'star', 'oval', 'heart', 'diamond', 'trapezoid', 'cloud'];
|
|
505
490
|
const isShape = shapeNames.includes(source.toLowerCase());
|
|
506
|
-
|
|
491
|
+
|
|
507
492
|
if (opacity !== undefined) {
|
|
508
493
|
ctx.globalAlpha = opacity;
|
|
509
494
|
}
|
|
510
|
-
|
|
495
|
+
|
|
511
496
|
if (isShape) {
|
|
512
|
-
drawShape(ctx, { source, x, y, width, height, rotation, borderRadius, stroke, shadow, isFilled, color, gradient, borderPosition });
|
|
497
|
+
drawShape(ctx, { source, x, y, width, height, rotation, borderRadius, stroke, shadow, isFilled, color, gradient, borderPosition, filling });
|
|
513
498
|
ctx.globalAlpha = 1.0;
|
|
514
499
|
return;
|
|
515
500
|
}
|
|
516
|
-
|
|
501
|
+
|
|
517
502
|
let loadedImage: Image | undefined;
|
|
518
503
|
try {
|
|
519
504
|
if (source.startsWith('http')) {
|
|
520
505
|
loadedImage = await loadImage(source);
|
|
521
506
|
} else {
|
|
522
|
-
const imagePath = path.
|
|
507
|
+
const imagePath = path.resolve(process.cwd(), source);
|
|
523
508
|
loadedImage = await loadImage(imagePath);
|
|
524
509
|
}
|
|
525
510
|
} catch (e: any) {
|
|
526
511
|
throw new Error(`Error loading image: ${e.message}`);
|
|
527
512
|
}
|
|
528
|
-
|
|
513
|
+
|
|
529
514
|
if (!loadedImage) {
|
|
530
515
|
throw new Error('Invalid image source or failed to load image');
|
|
531
516
|
}
|
|
532
|
-
|
|
517
|
+
|
|
533
518
|
ctx.save();
|
|
519
|
+
|
|
520
|
+
const scale = zoom?.scale ?? 1;
|
|
521
|
+
const focusX = zoom?.x ?? 0.5;
|
|
522
|
+
const focusY = zoom?.y ?? 0.5;
|
|
523
|
+
|
|
524
|
+
if (scale !== 1) {
|
|
525
|
+
ctx.translate(focusX, focusY);
|
|
526
|
+
ctx.scale(scale, scale);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (blur > 0) {
|
|
530
|
+
ctx.filter = `blur(${blur}px)`;
|
|
531
|
+
}
|
|
532
|
+
|
|
534
533
|
applyRotation(ctx, rotation, x, y, width, height);
|
|
535
534
|
applyShadow(ctx, shadow, x, y, width, height);
|
|
536
535
|
|
|
537
536
|
if (perspective) {
|
|
538
|
-
throw new Error('Under construction')
|
|
539
|
-
// const offscreen = createCanvas(width, height);
|
|
540
|
-
// const offCtx = offscreen.getContext('2d') as SKRSContext2D;
|
|
541
|
-
// offCtx.clearRect(0, 0, width, height);
|
|
542
|
-
|
|
543
|
-
// await applyPerspective(offCtx, loadedImage, 0, 0, width, height, perspective, 10, 10);
|
|
544
|
-
|
|
545
|
-
// imageRadius(ctx, offscreen, x, y, width, height, borderRadius, borderPosition);
|
|
537
|
+
throw new Error('Under construction');
|
|
546
538
|
}
|
|
547
|
-
|
|
539
|
+
|
|
548
540
|
imageRadius(ctx, loadedImage, x, y, width, height, borderRadius, borderPosition);
|
|
549
|
-
|
|
550
|
-
|
|
541
|
+
applyStroke(ctx, stroke, x, y, width, height, undefined, true);
|
|
542
|
+
|
|
551
543
|
ctx.restore();
|
|
552
|
-
|
|
553
544
|
ctx.globalAlpha = 1.0;
|
|
554
545
|
}
|
|
555
546
|
|
|
@@ -997,7 +988,7 @@ async animate(
|
|
|
997
988
|
}
|
|
998
989
|
|
|
999
990
|
if (frame.onDrawCustom) {
|
|
1000
|
-
frame.onDrawCustom(ctx as unknown as
|
|
991
|
+
frame.onDrawCustom(ctx as unknown as SKRSContext2D, canvas);
|
|
1001
992
|
}
|
|
1002
993
|
|
|
1003
994
|
if (frame.transformations) {
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { ApexPainter, CanvasConfig, ImageProperties, TextObject } from "../../../index";
|
|
2
|
+
|
|
3
|
+
const Image = new ApexPainter();
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generates a level-up card.
|
|
7
|
+
* @param {string} userPfp - User profile picture URL.
|
|
8
|
+
* @param {string} username - Username.
|
|
9
|
+
* @param {string} [levelUpmsg="Well done user. You had leveled up 🔥🔥"] - Custom level-up message.
|
|
10
|
+
* @param {string} prevLevel - Previous level before leveling up.
|
|
11
|
+
* @param {string} nextLevel - Next level after leveling up.
|
|
12
|
+
* @returns {Promise<Buffer>} - The generated level-up image.
|
|
13
|
+
*/
|
|
14
|
+
export async function level_up_1(
|
|
15
|
+
userPfp: string,
|
|
16
|
+
username: string,
|
|
17
|
+
levelUpmsg: string = "Well done user. You had leveled up 🔥🔥",
|
|
18
|
+
prevLevel: string,
|
|
19
|
+
nextLevel: string
|
|
20
|
+
): Promise<Buffer> {
|
|
21
|
+
|
|
22
|
+
/** Helper function to detect Arabic text */
|
|
23
|
+
const isArabic = (text: string) => /[\u0600-\u06FF]/.test(text);
|
|
24
|
+
|
|
25
|
+
/** Helper function to shuffle an array */
|
|
26
|
+
function shuffleArray<T>(array: T[]): T[] {
|
|
27
|
+
return array.sort(() => Math.random() - 0.5);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Extracts and returns random colors from the dominant colors */
|
|
31
|
+
function getRandomColors(colors: { color: string }[]): string[] {
|
|
32
|
+
if (colors.length === 0) return ["rgb(0, 255, 247)", "rgb(157, 0, 255)", "rgb(255, 94, 0)"];
|
|
33
|
+
return shuffleArray(colors).slice(0, 5).map(c => `rgb(${c.color})`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 1. **Canvas Configuration**
|
|
37
|
+
const canvasConfig: CanvasConfig = {
|
|
38
|
+
customBg: './bg.png',
|
|
39
|
+
width: 1599, height: 876,
|
|
40
|
+
zoom: {}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const bg = await Image.createCanvas(canvasConfig);
|
|
44
|
+
|
|
45
|
+
// 2. **Extract Dominant Colors for Gradients**
|
|
46
|
+
const dominantColors = await Image.colorAnalysis(userPfp);
|
|
47
|
+
const gradientColors = getRandomColors(dominantColors).map((color, index) => ({
|
|
48
|
+
stop: index / 4, color
|
|
49
|
+
}));
|
|
50
|
+
const shadowGradient = [...gradientColors];
|
|
51
|
+
|
|
52
|
+
// 3. **User Profile Picture Configuration**
|
|
53
|
+
const imgs: ImageProperties[] = [{
|
|
54
|
+
source: userPfp,
|
|
55
|
+
width: 240, height: 240,
|
|
56
|
+
x: 235, y: 235,
|
|
57
|
+
blur: 0.4,
|
|
58
|
+
borderRadius: 'circular',
|
|
59
|
+
stroke: {
|
|
60
|
+
width: 8, position: 5, blur: 0.5,
|
|
61
|
+
borderRadius: 'circular',
|
|
62
|
+
gradient: {
|
|
63
|
+
type: 'linear',
|
|
64
|
+
startX: 200, startY: 200,
|
|
65
|
+
endX: 290, endY: 290,
|
|
66
|
+
colors: gradientColors
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
shadow: {
|
|
70
|
+
gradient: {
|
|
71
|
+
type: 'linear',
|
|
72
|
+
startX: 220, startY: 220,
|
|
73
|
+
endX: 270, endY: 270,
|
|
74
|
+
colors: shadowGradient
|
|
75
|
+
},
|
|
76
|
+
opacity: 0.7,
|
|
77
|
+
blur: 25,
|
|
78
|
+
borderRadius: 90,
|
|
79
|
+
offsetX: 0, offsetY: 0,
|
|
80
|
+
}
|
|
81
|
+
}];
|
|
82
|
+
|
|
83
|
+
const img = await Image.createImage(imgs, bg);
|
|
84
|
+
|
|
85
|
+
// 4. **Username Text Handling**
|
|
86
|
+
const maxLength = isArabic(username) ? 12 : 10;
|
|
87
|
+
let displayName = username.length > maxLength
|
|
88
|
+
? isArabic(username)
|
|
89
|
+
? '...' + username.slice(-maxLength)
|
|
90
|
+
: username.slice(0, maxLength - 3) + '...'
|
|
91
|
+
: username;
|
|
92
|
+
|
|
93
|
+
let xPos = isArabic(username) ? Math.max(480 - (displayName.length - 4) * 3, 190) - 30 : 300 - Math.min((displayName.length - 3) * 10, 110);
|
|
94
|
+
|
|
95
|
+
// 5. **Message Text Alignment**
|
|
96
|
+
const container = { width: 590, height: 235, x: 810, y: 520 };
|
|
97
|
+
const fontName = isArabic(levelUpmsg) ? 'Segoe UI' : 'Segoe UI Emoji';
|
|
98
|
+
const textX = isArabic(levelUpmsg) ? container.x + 290 : container.x + 285;
|
|
99
|
+
|
|
100
|
+
// 6. **Dynamic Level Positioning Function**
|
|
101
|
+
function getLevelPosition(level: string, isNext: boolean) {
|
|
102
|
+
let x = isNext ? 1320 : 885;
|
|
103
|
+
let y = 455;
|
|
104
|
+
let fontSize = 72;
|
|
105
|
+
|
|
106
|
+
if (level.length === 3) {
|
|
107
|
+
x = isNext ? 1323 : 888;
|
|
108
|
+
y = 455;
|
|
109
|
+
fontSize = 63;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return { x, y, fontSize };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const prevLevelConfig = getLevelPosition(prevLevel, false);
|
|
116
|
+
const nextLevelConfig = getLevelPosition(nextLevel, true);
|
|
117
|
+
|
|
118
|
+
// 7. **Text Elements Configuration**
|
|
119
|
+
const txts: TextObject[] = [
|
|
120
|
+
{
|
|
121
|
+
text: displayName,
|
|
122
|
+
x: xPos, y: 600,
|
|
123
|
+
fontSize: 54,
|
|
124
|
+
fontName: 'Segoe UI',
|
|
125
|
+
textAlign: isArabic(username) ? 'right' : 'left',
|
|
126
|
+
gradient: {
|
|
127
|
+
type: 'linear',
|
|
128
|
+
startX: 0, startY: 0,
|
|
129
|
+
endX: 0, endY: 50,
|
|
130
|
+
colors: [
|
|
131
|
+
{ stop: 0, color: '#ffffff' },
|
|
132
|
+
{ stop: 1, color: '#66e6ff' }
|
|
133
|
+
]
|
|
134
|
+
},
|
|
135
|
+
stroke: { width: 3, color: '#9d00ff' },
|
|
136
|
+
shadow: { color: '#66e6ff', opacity: 0.4, blur: 8, offsetX: 0, offsetY: 0 }
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
text: levelUpmsg,
|
|
140
|
+
x: textX, y: container.y + 60,
|
|
141
|
+
fontSize: 34,
|
|
142
|
+
fontName: fontName,
|
|
143
|
+
textAlign: 'center',
|
|
144
|
+
maxWidth: container.width - 30,
|
|
145
|
+
lineHeight: 42,
|
|
146
|
+
maxHeight: container.height - 30,
|
|
147
|
+
gradient: {
|
|
148
|
+
type: 'linear',
|
|
149
|
+
startX: 0, startY: 0,
|
|
150
|
+
endX: 0, endY: 50,
|
|
151
|
+
colors: [
|
|
152
|
+
{ stop: 0, color: '#f9ff00' },
|
|
153
|
+
{ stop: 1, color: '#ff6a00' }
|
|
154
|
+
]
|
|
155
|
+
},
|
|
156
|
+
stroke: { width: 2, color: '#ff9d00' },
|
|
157
|
+
shadow: { color: '#ffcc00', opacity: 0.5, blur: 10, offsetX: 0, offsetY: 0 }
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
text: prevLevel,
|
|
161
|
+
x: prevLevelConfig.x, y: prevLevelConfig.y,
|
|
162
|
+
fontSize: prevLevelConfig.fontSize,
|
|
163
|
+
fontName: "Bahnschrift",
|
|
164
|
+
textAlign: "center",
|
|
165
|
+
gradient: { type: "linear", startX: 0, startY: 0, endX: 0, endY: 80, colors: [{ stop: 0, color: "#00FFFF" }, { stop: 1, color: "#007BFF" }] },
|
|
166
|
+
stroke: { width: 6, color: "#0033FF" },
|
|
167
|
+
shadow: { color: "#00BFFF", opacity: 0.6, blur: 15, offsetX: 0, offsetY: 0 }
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
text: nextLevel,
|
|
171
|
+
x: nextLevelConfig.x, y: nextLevelConfig.y,
|
|
172
|
+
fontSize: nextLevelConfig.fontSize,
|
|
173
|
+
fontName: "Bahnschrift",
|
|
174
|
+
textAlign: "center",
|
|
175
|
+
gradient: { type: "linear", startX: 0, startY: 0, endX: 0, endY: 80, colors: [{ stop: 0, color: "#00FFFF" }, { stop: 1, color: "#007BFF" }] },
|
|
176
|
+
stroke: { width: 6, color: "#0033FF" },
|
|
177
|
+
shadow: { color: "#00BFFF", opacity: 0.6, blur: 15, offsetX: 0, offsetY: 0 }
|
|
178
|
+
}
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
// 8. **Render Final Image**
|
|
182
|
+
return await Image.createText(txts, img);
|
|
183
|
+
}
|
|
@@ -1,97 +1,211 @@
|
|
|
1
1
|
import { loadImage, SKRSContext2D } from "@napi-rs/canvas";
|
|
2
|
-
import { CanvasConfig } from
|
|
3
|
-
import path from
|
|
2
|
+
import { CanvasConfig } from "../types";
|
|
3
|
+
import path from "path";
|
|
4
4
|
|
|
5
|
+
interface ZoomConfig {
|
|
6
|
+
scale?: number;
|
|
7
|
+
x?: number;
|
|
8
|
+
y?: number;
|
|
9
|
+
}
|
|
5
10
|
|
|
6
11
|
/**
|
|
7
|
-
* Draws a gradient background on the canvas.
|
|
8
|
-
* No zoom logic is applied.
|
|
12
|
+
* Draws a gradient background on the canvas with optional zoom support.
|
|
9
13
|
* @param ctx The canvas rendering context.
|
|
10
14
|
* @param canvas The canvas configuration object.
|
|
15
|
+
* @param zoom Optional zoom configuration.
|
|
11
16
|
*/
|
|
12
|
-
export async function drawBackgroundGradient(
|
|
17
|
+
export async function drawBackgroundGradient(
|
|
18
|
+
ctx: SKRSContext2D,
|
|
19
|
+
canvas: CanvasConfig,
|
|
20
|
+
zoom?: ZoomConfig,
|
|
21
|
+
blur?: number
|
|
22
|
+
): Promise<void> {
|
|
13
23
|
if (canvas.gradientBg) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
24
|
+
const {
|
|
25
|
+
type,
|
|
26
|
+
startX = 0,
|
|
27
|
+
startY = 0,
|
|
28
|
+
endX = canvas.width || 500,
|
|
29
|
+
endY = canvas.height || 500,
|
|
30
|
+
startRadius = 0,
|
|
31
|
+
endRadius = 0,
|
|
32
|
+
colors
|
|
33
|
+
} = canvas.gradientBg;
|
|
34
|
+
|
|
35
|
+
if (!colors || colors.length === 0) {
|
|
36
|
+
throw new Error("You need to provide colors for the gradient.");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let gradient;
|
|
40
|
+
if (type === "linear" || type === undefined) {
|
|
41
|
+
let finalStartX = startX, finalStartY = startY, finalEndX = endX, finalEndY = endY;
|
|
42
|
+
|
|
43
|
+
if (zoom) {
|
|
44
|
+
const scale = Math.max(1, zoom.scale || 1);
|
|
45
|
+
finalStartX = (zoom.x || 0) - (endX - startX) / (2 * scale);
|
|
46
|
+
finalStartY = (zoom.y || 0) - (endY - startY) / (2 * scale);
|
|
47
|
+
finalEndX = (zoom.x || 0) + (endX - startX) / (2 * scale);
|
|
48
|
+
finalEndY = (zoom.y || 0) + (endY - startY) / (2 * scale);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
gradient = ctx.createLinearGradient(finalStartX, finalStartY, finalEndX, finalEndY);
|
|
52
|
+
} else if (type === "radial") {
|
|
53
|
+
gradient = ctx.createRadialGradient(startX, startY, startRadius, endX, endY, endRadius);
|
|
54
|
+
} else {
|
|
55
|
+
throw new Error("Unsupported gradient type.");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for (const { stop, color } of colors) {
|
|
59
|
+
gradient.addColorStop(stop as number, color as string);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (blur) {
|
|
63
|
+
ctx.filter = `blur(${blur}px)`;
|
|
40
64
|
}
|
|
41
|
-
|
|
42
|
-
const canvasWidth = canvas.width || 500;
|
|
43
|
-
const canvasHeight = canvas.height || 500;
|
|
65
|
+
|
|
44
66
|
ctx.fillStyle = gradient;
|
|
45
|
-
ctx.fillRect(0, 0,
|
|
67
|
+
ctx.fillRect(0, 0, canvas.width || 500, canvas.height || 500);
|
|
68
|
+
|
|
69
|
+
ctx.filter = "none";
|
|
46
70
|
}
|
|
47
71
|
}
|
|
48
|
-
|
|
72
|
+
|
|
49
73
|
/**
|
|
50
|
-
* Draws a solid background color on the canvas.
|
|
51
|
-
* No zoom logic is applied.
|
|
74
|
+
* Draws a solid background color on the canvas with optional zoom effect.
|
|
52
75
|
* @param ctx The canvas rendering context.
|
|
53
76
|
* @param canvas The canvas configuration object.
|
|
77
|
+
* @param zoom Optional zoom configuration.
|
|
54
78
|
*/
|
|
55
|
-
export async function drawBackgroundColor(
|
|
79
|
+
export async function drawBackgroundColor(
|
|
80
|
+
ctx: SKRSContext2D,
|
|
81
|
+
canvas: CanvasConfig,
|
|
82
|
+
zoom?: ZoomConfig,
|
|
83
|
+
blur?: number
|
|
84
|
+
): Promise<void> {
|
|
56
85
|
const canvasWidth = canvas.width || 500;
|
|
57
86
|
const canvasHeight = canvas.height || 500;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
87
|
+
|
|
88
|
+
ctx.save();
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if (blur) {
|
|
92
|
+
ctx.filter = `blur(${blur}px)`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (zoom) {
|
|
96
|
+
const scale = Math.max(1, zoom.scale || 1);
|
|
97
|
+
const zoomedWidth = canvasWidth / scale;
|
|
98
|
+
const zoomedHeight = canvasHeight / scale;
|
|
99
|
+
|
|
100
|
+
ctx.scale(scale, scale);
|
|
101
|
+
ctx.translate((zoom.x || 0) + zoomedWidth / 2, (zoom.y || 0) + zoomedHeight / 2);
|
|
63
102
|
}
|
|
103
|
+
|
|
104
|
+
if (canvas.colorBg !== "transparent") {
|
|
105
|
+
ctx.fillStyle = canvas.colorBg as string;
|
|
106
|
+
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
ctx.filter = "none";
|
|
110
|
+
ctx.restore();
|
|
64
111
|
}
|
|
65
112
|
|
|
66
113
|
/**
|
|
67
|
-
* Draws a custom background image on the canvas.
|
|
68
|
-
* No zoom logic is applied.
|
|
114
|
+
* Draws a custom background image on the canvas with optional zoom functionality.
|
|
69
115
|
* @param ctx The canvas rendering context.
|
|
70
116
|
* @param canvas The canvas configuration object.
|
|
117
|
+
* @param zoom Optional zoom configuration.
|
|
71
118
|
*/
|
|
72
|
-
export async function customBackground(
|
|
119
|
+
export async function customBackground(
|
|
120
|
+
ctx: SKRSContext2D,
|
|
121
|
+
canvas: CanvasConfig,
|
|
122
|
+
zoom?: ZoomConfig,
|
|
123
|
+
blur?: number
|
|
124
|
+
): Promise<void> {
|
|
73
125
|
if (canvas.customBg) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
126
|
+
try {
|
|
127
|
+
let imageBuffer: string;
|
|
128
|
+
|
|
129
|
+
if (canvas.customBg.startsWith("http")) {
|
|
130
|
+
imageBuffer = canvas.customBg;
|
|
131
|
+
} else {
|
|
132
|
+
imageBuffer = path.join(process.cwd(), canvas.customBg);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const image = await loadImage(imageBuffer);
|
|
136
|
+
const imgWidth = image.width;
|
|
137
|
+
const imgHeight = image.height;
|
|
138
|
+
const canvasWidth = canvas.width || imgWidth;
|
|
139
|
+
const canvasHeight = canvas.height || imgHeight;
|
|
140
|
+
|
|
141
|
+
if (blur) {
|
|
142
|
+
ctx.filter = `blur(${blur}px)`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (zoom) {
|
|
146
|
+
const scale = Math.max(1, zoom.scale || 1);
|
|
147
|
+
const cropWidth = imgWidth / scale;
|
|
148
|
+
const cropHeight = imgHeight / scale;
|
|
149
|
+
|
|
150
|
+
let sx = (zoom.x || 0) - cropWidth / 2;
|
|
151
|
+
let sy = (zoom.y || 0) - cropHeight / 2;
|
|
152
|
+
|
|
153
|
+
sx = Math.max(0, Math.min(sx, imgWidth - cropWidth));
|
|
154
|
+
sy = Math.max(0, Math.min(sy, imgHeight - cropHeight));
|
|
155
|
+
|
|
156
|
+
ctx.drawImage(image, sx, sy, cropWidth, cropHeight, 0, 0, canvasWidth, canvasHeight);
|
|
157
|
+
} else {
|
|
158
|
+
ctx.drawImage(image, 0, 0, canvasWidth, canvasHeight);
|
|
159
|
+
}
|
|
160
|
+
ctx.filter = "none";
|
|
161
|
+
} catch (error: any) {
|
|
162
|
+
console.error("Error loading custom background image:", error.message);
|
|
81
163
|
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function backgroundRadius(
|
|
168
|
+
ctx: SKRSContext2D,
|
|
169
|
+
x: number,
|
|
170
|
+
y: number,
|
|
171
|
+
width: number,
|
|
172
|
+
height: number,
|
|
173
|
+
borderRadius: number | "circular",
|
|
174
|
+
borderPosition: string = "all"
|
|
175
|
+
): void {
|
|
176
|
+
ctx.beginPath();
|
|
82
177
|
|
|
83
|
-
|
|
178
|
+
if (borderRadius === "circular") {
|
|
179
|
+
const circleRadius = Math.min(width, height) / 2;
|
|
180
|
+
ctx.arc(x + width / 2, y + height / 2, circleRadius, 0, 2 * Math.PI);
|
|
181
|
+
} else if (typeof borderRadius === "number" && borderRadius > 0) {
|
|
182
|
+
const br: number = Math.min(borderRadius, width / 2, height / 2);
|
|
183
|
+
const selectedPositions = new Set(borderPosition.toLowerCase().split(",").map((s) => s.trim()));
|
|
84
184
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
185
|
+
const roundTopLeft = selectedPositions.has("all") || selectedPositions.has("top-left") || (selectedPositions.has("top") && selectedPositions.has("left"));
|
|
186
|
+
const roundTopRight = selectedPositions.has("all") || selectedPositions.has("top-right") || (selectedPositions.has("top") && selectedPositions.has("right"));
|
|
187
|
+
const roundBottomRight = selectedPositions.has("all") || selectedPositions.has("bottom-right") || (selectedPositions.has("bottom") && selectedPositions.has("right"));
|
|
188
|
+
const roundBottomLeft = selectedPositions.has("all") || selectedPositions.has("bottom-left") || (selectedPositions.has("bottom") && selectedPositions.has("left"));
|
|
89
189
|
|
|
90
|
-
|
|
91
|
-
|
|
190
|
+
const tl = roundTopLeft ? br : 0;
|
|
191
|
+
const tr = roundTopRight ? br : 0;
|
|
192
|
+
const brR = roundBottomRight ? br : 0;
|
|
193
|
+
const bl = roundBottomLeft ? br : 0;
|
|
92
194
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
195
|
+
ctx.moveTo(x + tl, y);
|
|
196
|
+
ctx.lineTo(x + width - tr, y);
|
|
197
|
+
if (tr > 0) ctx.arc(x + width - tr, y + tr, tr, -Math.PI / 2, 0, false);
|
|
198
|
+
ctx.lineTo(x + width, y + height - brR);
|
|
199
|
+
if (brR > 0) ctx.arc(x + width - brR, y + height - brR, brR, 0, Math.PI / 2, false);
|
|
200
|
+
ctx.lineTo(x + bl, y + height);
|
|
201
|
+
if (bl > 0) ctx.arc(x + bl, y + height - bl, bl, Math.PI / 2, Math.PI, false);
|
|
202
|
+
ctx.lineTo(x, y + tl);
|
|
203
|
+
if (tl > 0) ctx.arc(x + tl, y + tl, tl, Math.PI, -Math.PI / 2, false);
|
|
204
|
+
} else {
|
|
205
|
+
ctx.rect(x, y, width, height);
|
|
96
206
|
}
|
|
97
|
-
|
|
207
|
+
|
|
208
|
+
ctx.closePath();
|
|
209
|
+
ctx.clip();
|
|
210
|
+
}
|
|
211
|
+
|