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.
Files changed (122) hide show
  1. package/dist/cjs/ai/functions/readFiles.js +1 -1
  2. package/dist/cjs/ai/functions/readFiles.js.map +1 -1
  3. package/dist/cjs/ai/modals-chat/electronHub/chatmodels.d.ts.map +1 -1
  4. package/dist/cjs/ai/modals-chat/electronHub/chatmodels.js +2 -1
  5. package/dist/cjs/ai/modals-chat/electronHub/chatmodels.js.map +1 -1
  6. package/dist/cjs/ai/modals-chat/electronHub/speechModels.d.ts.map +1 -1
  7. package/dist/cjs/ai/modals-chat/electronHub/speechModels.js +1 -1
  8. package/dist/cjs/ai/modals-chat/electronHub/speechModels.js.map +1 -1
  9. package/dist/cjs/canvas/ApexPainter.d.ts.map +1 -1
  10. package/dist/cjs/canvas/ApexPainter.js +27 -28
  11. package/dist/cjs/canvas/ApexPainter.js.map +1 -1
  12. package/dist/cjs/canvas/Themes/Level-Up/levelup.d.ts +11 -0
  13. package/dist/cjs/canvas/Themes/Level-Up/levelup.d.ts.map +1 -0
  14. package/dist/cjs/canvas/Themes/Level-Up/levelup.js +163 -0
  15. package/dist/cjs/canvas/Themes/Level-Up/levelup.js.map +1 -0
  16. package/dist/cjs/canvas/utils/Background/bg.d.ts +17 -10
  17. package/dist/cjs/canvas/utils/Background/bg.d.ts.map +1 -1
  18. package/dist/cjs/canvas/utils/Background/bg.js +102 -27
  19. package/dist/cjs/canvas/utils/Background/bg.js.map +1 -1
  20. package/dist/cjs/canvas/utils/Custom/customLines.d.ts.map +1 -1
  21. package/dist/cjs/canvas/utils/Custom/customLines.js +43 -19
  22. package/dist/cjs/canvas/utils/Custom/customLines.js.map +1 -1
  23. package/dist/cjs/canvas/utils/General/general functions.d.ts +6 -1
  24. package/dist/cjs/canvas/utils/General/general functions.d.ts.map +1 -1
  25. package/dist/cjs/canvas/utils/General/general functions.js +19 -20
  26. package/dist/cjs/canvas/utils/General/general functions.js.map +1 -1
  27. package/dist/cjs/canvas/utils/Image/imageProperties.d.ts +3 -9
  28. package/dist/cjs/canvas/utils/Image/imageProperties.d.ts.map +1 -1
  29. package/dist/cjs/canvas/utils/Image/imageProperties.js +220 -214
  30. package/dist/cjs/canvas/utils/Image/imageProperties.js.map +1 -1
  31. package/dist/cjs/canvas/utils/Texts/textProperties.d.ts +12 -14
  32. package/dist/cjs/canvas/utils/Texts/textProperties.d.ts.map +1 -1
  33. package/dist/cjs/canvas/utils/Texts/textProperties.js +100 -91
  34. package/dist/cjs/canvas/utils/Texts/textProperties.js.map +1 -1
  35. package/dist/cjs/canvas/utils/types.d.ts +89 -109
  36. package/dist/cjs/canvas/utils/types.d.ts.map +1 -1
  37. package/dist/cjs/canvas/utils/types.js.map +1 -1
  38. package/dist/cjs/canvas/utils/utils.d.ts +2 -4
  39. package/dist/cjs/canvas/utils/utils.d.ts.map +1 -1
  40. package/dist/cjs/canvas/utils/utils.js +2 -5
  41. package/dist/cjs/canvas/utils/utils.js.map +1 -1
  42. package/dist/cjs/index.d.ts.map +1 -1
  43. package/dist/cjs/index.js +31 -3
  44. package/dist/cjs/index.js.map +1 -1
  45. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  46. package/dist/esm/ai/functions/readFiles.js +1 -1
  47. package/dist/esm/ai/functions/readFiles.js.map +1 -1
  48. package/dist/esm/ai/modals-chat/electronHub/chatmodels.d.ts.map +1 -1
  49. package/dist/esm/ai/modals-chat/electronHub/chatmodels.js +2 -1
  50. package/dist/esm/ai/modals-chat/electronHub/chatmodels.js.map +1 -1
  51. package/dist/esm/ai/modals-chat/electronHub/speechModels.d.ts.map +1 -1
  52. package/dist/esm/ai/modals-chat/electronHub/speechModels.js +1 -1
  53. package/dist/esm/ai/modals-chat/electronHub/speechModels.js.map +1 -1
  54. package/dist/esm/canvas/ApexPainter.d.ts.map +1 -1
  55. package/dist/esm/canvas/ApexPainter.js +27 -28
  56. package/dist/esm/canvas/ApexPainter.js.map +1 -1
  57. package/dist/esm/canvas/Themes/Level-Up/levelup.d.ts +11 -0
  58. package/dist/esm/canvas/Themes/Level-Up/levelup.d.ts.map +1 -0
  59. package/dist/esm/canvas/Themes/Level-Up/levelup.js +163 -0
  60. package/dist/esm/canvas/Themes/Level-Up/levelup.js.map +1 -0
  61. package/dist/esm/canvas/utils/Background/bg.d.ts +17 -10
  62. package/dist/esm/canvas/utils/Background/bg.d.ts.map +1 -1
  63. package/dist/esm/canvas/utils/Background/bg.js +102 -27
  64. package/dist/esm/canvas/utils/Background/bg.js.map +1 -1
  65. package/dist/esm/canvas/utils/Custom/customLines.d.ts.map +1 -1
  66. package/dist/esm/canvas/utils/Custom/customLines.js +43 -19
  67. package/dist/esm/canvas/utils/Custom/customLines.js.map +1 -1
  68. package/dist/esm/canvas/utils/General/general functions.d.ts +6 -1
  69. package/dist/esm/canvas/utils/General/general functions.d.ts.map +1 -1
  70. package/dist/esm/canvas/utils/General/general functions.js +19 -20
  71. package/dist/esm/canvas/utils/General/general functions.js.map +1 -1
  72. package/dist/esm/canvas/utils/Image/imageProperties.d.ts +3 -9
  73. package/dist/esm/canvas/utils/Image/imageProperties.d.ts.map +1 -1
  74. package/dist/esm/canvas/utils/Image/imageProperties.js +220 -214
  75. package/dist/esm/canvas/utils/Image/imageProperties.js.map +1 -1
  76. package/dist/esm/canvas/utils/Texts/textProperties.d.ts +12 -14
  77. package/dist/esm/canvas/utils/Texts/textProperties.d.ts.map +1 -1
  78. package/dist/esm/canvas/utils/Texts/textProperties.js +100 -91
  79. package/dist/esm/canvas/utils/Texts/textProperties.js.map +1 -1
  80. package/dist/esm/canvas/utils/types.d.ts +89 -109
  81. package/dist/esm/canvas/utils/types.d.ts.map +1 -1
  82. package/dist/esm/canvas/utils/types.js.map +1 -1
  83. package/dist/esm/canvas/utils/utils.d.ts +2 -4
  84. package/dist/esm/canvas/utils/utils.d.ts.map +1 -1
  85. package/dist/esm/canvas/utils/utils.js +2 -5
  86. package/dist/esm/canvas/utils/utils.js.map +1 -1
  87. package/dist/esm/index.d.ts.map +1 -1
  88. package/dist/esm/index.js +31 -3
  89. package/dist/esm/index.js.map +1 -1
  90. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  91. package/lib/ai/functions/readFiles.ts +1 -1
  92. package/lib/ai/modals-chat/electronHub/chatmodels.ts +4 -2
  93. package/lib/ai/modals-chat/electronHub/speechModels.ts +2 -1
  94. package/lib/canvas/ApexPainter.ts +52 -61
  95. package/lib/canvas/Themes/Level-Up/levelup.ts +183 -0
  96. package/lib/canvas/utils/Background/bg.ts +179 -65
  97. package/lib/canvas/utils/Custom/customLines.ts +53 -20
  98. package/lib/canvas/utils/General/general functions.ts +21 -29
  99. package/lib/canvas/utils/Image/imageProperties.ts +399 -318
  100. package/lib/canvas/utils/Texts/textProperties.ts +213 -162
  101. package/lib/canvas/utils/types.ts +74 -107
  102. package/lib/canvas/utils/utils.ts +2 -5
  103. package/lib/index.ts +38 -10
  104. package/package.json +2 -2
  105. package/dist/cjs/canvas/utils/Background/circular.d.ts +0 -3
  106. package/dist/cjs/canvas/utils/Background/circular.d.ts.map +0 -1
  107. package/dist/cjs/canvas/utils/Background/circular.js +0 -13
  108. package/dist/cjs/canvas/utils/Background/circular.js.map +0 -1
  109. package/dist/cjs/canvas/utils/Background/radius.d.ts +0 -18
  110. package/dist/cjs/canvas/utils/Background/radius.d.ts.map +0 -1
  111. package/dist/cjs/canvas/utils/Background/radius.js +0 -104
  112. package/dist/cjs/canvas/utils/Background/radius.js.map +0 -1
  113. package/dist/esm/canvas/utils/Background/circular.d.ts +0 -3
  114. package/dist/esm/canvas/utils/Background/circular.d.ts.map +0 -1
  115. package/dist/esm/canvas/utils/Background/circular.js +0 -13
  116. package/dist/esm/canvas/utils/Background/circular.js.map +0 -1
  117. package/dist/esm/canvas/utils/Background/radius.d.ts +0 -18
  118. package/dist/esm/canvas/utils/Background/radius.d.ts.map +0 -1
  119. package/dist/esm/canvas/utils/Background/radius.js +0 -104
  120. package/dist/esm/canvas/utils/Background/radius.js.map +0 -1
  121. package/lib/canvas/utils/Background/circular.ts +0 -17
  122. 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, circularBorder, radiusBorder, customLines, applyRotation, applyStroke,
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 (typeof borderRadius === 'string') {
68
- circularBorder(ctx, width, height);
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
- // ctx.translate(x, y);
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, size);
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
- shadow,
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
- !source ||
496
- typeof x !== 'number' ||
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.join(process.cwd(), source);
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
- applyStroke(ctx, stroke, x, y, width, height);
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 CanvasRenderingContext2D, canvas);
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 '../types';
3
- import path from 'path';
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(ctx: SKRSContext2D, canvas: CanvasConfig): Promise<void> {
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
- const {
15
- type,
16
- startX = 0,
17
- startY = 0,
18
- endX = canvas.width || 500,
19
- endY = canvas.height || 500,
20
- startRadius = 0,
21
- endRadius = 0,
22
- colors
23
- } = canvas.gradientBg;
24
-
25
- if (!colors || colors.length === 0) {
26
- throw new Error('You need to provide colors for the gradient.');
27
- }
28
-
29
- let gradient;
30
- if (type === 'linear' || type === undefined) {
31
- gradient = ctx.createLinearGradient(startX, startY, endX, endY);
32
- } else if (type === 'radial') {
33
- gradient = ctx.createRadialGradient(startX, startY, startRadius, endX, endY, endRadius);
34
- } else {
35
- throw new Error('Unsupported gradient type.');
36
- }
37
-
38
- for (const { stop, color } of colors) {
39
- gradient.addColorStop(stop as number, color as string);
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, canvasWidth, canvasHeight);
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(ctx: SKRSContext2D, canvas: CanvasConfig): Promise<void> {
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
- // Fill the entire area starting at (0,0) (assuming translation is handled elsewhere)
60
- if (canvas.colorBg !== 'transparent') {
61
- ctx.fillStyle = canvas.colorBg as string;
62
- ctx.fillRect(0, 0, canvasWidth, canvasHeight);
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(ctx: SKRSContext2D, canvas: CanvasConfig): Promise<void> {
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
- try {
75
- let imageBuffer: string;
76
-
77
- if (canvas.customBg.startsWith('http')) {
78
- imageBuffer = canvas.customBg;
79
- } else {
80
- imageBuffer = path.join(process.cwd(), canvas.customBg);
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
- const image = await loadImage(imageBuffer);
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
- // Use canvas dimensions without additional x/y translation,
86
- // because createCanvas already translated the context.
87
- const canvasWidth = canvas.width || image.width;
88
- const canvasHeight = canvas.height || image.height;
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
- // Draw the image so that it fills the canvas area relative to (0,0)
91
- ctx.drawImage(image, 0, 0, canvasWidth, canvasHeight);
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
- } catch (error: any) {
94
- console.error('Error loading custom background image:', error.message);
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
+