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.
Files changed (108) hide show
  1. package/README.md +437 -47
  2. package/dist/cjs/Canvas/ApexPainter.d.ts +122 -78
  3. package/dist/cjs/Canvas/ApexPainter.d.ts.map +1 -1
  4. package/dist/cjs/Canvas/ApexPainter.js +461 -352
  5. package/dist/cjs/Canvas/ApexPainter.js.map +1 -1
  6. package/dist/cjs/Canvas/utils/Background/bg.d.ts +23 -11
  7. package/dist/cjs/Canvas/utils/Background/bg.d.ts.map +1 -1
  8. package/dist/cjs/Canvas/utils/Background/bg.js +174 -107
  9. package/dist/cjs/Canvas/utils/Background/bg.js.map +1 -1
  10. package/dist/cjs/Canvas/utils/Custom/customLines.js +2 -2
  11. package/dist/cjs/Canvas/utils/Custom/customLines.js.map +1 -1
  12. package/dist/cjs/Canvas/utils/Image/imageFilters.d.ts +11 -0
  13. package/dist/cjs/Canvas/utils/Image/imageFilters.d.ts.map +1 -0
  14. package/dist/cjs/Canvas/utils/Image/imageFilters.js +307 -0
  15. package/dist/cjs/Canvas/utils/Image/imageFilters.js.map +1 -0
  16. package/dist/cjs/Canvas/utils/Image/imageProperties.d.ts +47 -112
  17. package/dist/cjs/Canvas/utils/Image/imageProperties.d.ts.map +1 -1
  18. package/dist/cjs/Canvas/utils/Image/imageProperties.js +229 -560
  19. package/dist/cjs/Canvas/utils/Image/imageProperties.js.map +1 -1
  20. package/dist/cjs/Canvas/utils/Image/professionalImageFilters.d.ts +11 -0
  21. package/dist/cjs/Canvas/utils/Image/professionalImageFilters.d.ts.map +1 -0
  22. package/dist/cjs/Canvas/utils/Image/professionalImageFilters.js +351 -0
  23. package/dist/cjs/Canvas/utils/Image/professionalImageFilters.js.map +1 -0
  24. package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.d.ts +11 -0
  25. package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.d.ts.map +1 -0
  26. package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.js +215 -0
  27. package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.js.map +1 -0
  28. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts +71 -0
  29. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts.map +1 -0
  30. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.js +392 -0
  31. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.js.map +1 -0
  32. package/dist/cjs/Canvas/utils/Shapes/shapes.d.ts +29 -0
  33. package/dist/cjs/Canvas/utils/Shapes/shapes.d.ts.map +1 -0
  34. package/dist/cjs/Canvas/utils/Shapes/shapes.js +334 -0
  35. package/dist/cjs/Canvas/utils/Shapes/shapes.js.map +1 -0
  36. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.d.ts +127 -0
  37. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.d.ts.map +1 -0
  38. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.js +365 -0
  39. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.js.map +1 -0
  40. package/dist/cjs/Canvas/utils/types.d.ts +227 -131
  41. package/dist/cjs/Canvas/utils/types.d.ts.map +1 -1
  42. package/dist/cjs/Canvas/utils/types.js +0 -1
  43. package/dist/cjs/Canvas/utils/types.js.map +1 -1
  44. package/dist/cjs/Canvas/utils/utils.d.ts +7 -4
  45. package/dist/cjs/Canvas/utils/utils.d.ts.map +1 -1
  46. package/dist/cjs/Canvas/utils/utils.js +17 -7
  47. package/dist/cjs/Canvas/utils/utils.js.map +1 -1
  48. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  49. package/dist/esm/Canvas/ApexPainter.d.ts +122 -78
  50. package/dist/esm/Canvas/ApexPainter.d.ts.map +1 -1
  51. package/dist/esm/Canvas/ApexPainter.js +461 -352
  52. package/dist/esm/Canvas/ApexPainter.js.map +1 -1
  53. package/dist/esm/Canvas/utils/Background/bg.d.ts +23 -11
  54. package/dist/esm/Canvas/utils/Background/bg.d.ts.map +1 -1
  55. package/dist/esm/Canvas/utils/Background/bg.js +174 -107
  56. package/dist/esm/Canvas/utils/Background/bg.js.map +1 -1
  57. package/dist/esm/Canvas/utils/Custom/customLines.js +2 -2
  58. package/dist/esm/Canvas/utils/Custom/customLines.js.map +1 -1
  59. package/dist/esm/Canvas/utils/Image/imageFilters.d.ts +11 -0
  60. package/dist/esm/Canvas/utils/Image/imageFilters.d.ts.map +1 -0
  61. package/dist/esm/Canvas/utils/Image/imageFilters.js +307 -0
  62. package/dist/esm/Canvas/utils/Image/imageFilters.js.map +1 -0
  63. package/dist/esm/Canvas/utils/Image/imageProperties.d.ts +47 -112
  64. package/dist/esm/Canvas/utils/Image/imageProperties.d.ts.map +1 -1
  65. package/dist/esm/Canvas/utils/Image/imageProperties.js +229 -560
  66. package/dist/esm/Canvas/utils/Image/imageProperties.js.map +1 -1
  67. package/dist/esm/Canvas/utils/Image/professionalImageFilters.d.ts +11 -0
  68. package/dist/esm/Canvas/utils/Image/professionalImageFilters.d.ts.map +1 -0
  69. package/dist/esm/Canvas/utils/Image/professionalImageFilters.js +351 -0
  70. package/dist/esm/Canvas/utils/Image/professionalImageFilters.js.map +1 -0
  71. package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.d.ts +11 -0
  72. package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.d.ts.map +1 -0
  73. package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.js +215 -0
  74. package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.js.map +1 -0
  75. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts +71 -0
  76. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts.map +1 -0
  77. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.js +392 -0
  78. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.js.map +1 -0
  79. package/dist/esm/Canvas/utils/Shapes/shapes.d.ts +29 -0
  80. package/dist/esm/Canvas/utils/Shapes/shapes.d.ts.map +1 -0
  81. package/dist/esm/Canvas/utils/Shapes/shapes.js +334 -0
  82. package/dist/esm/Canvas/utils/Shapes/shapes.js.map +1 -0
  83. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.d.ts +127 -0
  84. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.d.ts.map +1 -0
  85. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.js +365 -0
  86. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.js.map +1 -0
  87. package/dist/esm/Canvas/utils/types.d.ts +227 -131
  88. package/dist/esm/Canvas/utils/types.d.ts.map +1 -1
  89. package/dist/esm/Canvas/utils/types.js +0 -1
  90. package/dist/esm/Canvas/utils/types.js.map +1 -1
  91. package/dist/esm/Canvas/utils/utils.d.ts +7 -4
  92. package/dist/esm/Canvas/utils/utils.d.ts.map +1 -1
  93. package/dist/esm/Canvas/utils/utils.js +17 -7
  94. package/dist/esm/Canvas/utils/utils.js.map +1 -1
  95. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  96. package/lib/Canvas/ApexPainter.ts +1325 -1218
  97. package/lib/Canvas/utils/Background/bg.ts +247 -173
  98. package/lib/Canvas/utils/Custom/customLines.ts +3 -3
  99. package/lib/Canvas/utils/Image/imageFilters.ts +356 -0
  100. package/lib/Canvas/utils/Image/imageProperties.ts +322 -775
  101. package/lib/Canvas/utils/Image/professionalImageFilters.ts +391 -0
  102. package/lib/Canvas/utils/Image/simpleProfessionalFilters.ts +229 -0
  103. package/lib/Canvas/utils/Patterns/enhancedPatternRenderer.ts +444 -0
  104. package/lib/Canvas/utils/Shapes/shapes.ts +528 -0
  105. package/lib/Canvas/utils/Texts/enhancedTextRenderer.ts +478 -0
  106. package/lib/Canvas/utils/types.ts +301 -117
  107. package/lib/Canvas/utils/utils.ts +85 -72
  108. package/package.json +106 -188
@@ -12,92 +12,489 @@ const axios_1 = __importDefault(require("axios"));
12
12
  const fs_1 = __importDefault(require("fs"));
13
13
  const path_1 = __importDefault(require("path"));
14
14
  const utils_1 = require("./utils/utils");
15
+ const enhancedTextRenderer_1 = require("./utils/Texts/enhancedTextRenderer");
16
+ const enhancedPatternRenderer_1 = require("./utils/Patterns/enhancedPatternRenderer");
15
17
  class ApexPainter {
16
18
  format;
17
19
  constructor({ type } = { type: 'buffer' }) {
18
20
  this.format = { type: type || 'buffer' };
19
21
  }
22
+ /**
23
+ * Validates image properties for required fields.
24
+ * @private
25
+ * @param ip - Image properties to validate
26
+ */
27
+ #validateImageProperties(ip) {
28
+ if (!ip.source || ip.x == null || ip.y == null) {
29
+ throw new Error("createImage: source, x, and y are required.");
30
+ }
31
+ }
32
+ /**
33
+ * Validates text properties for required fields.
34
+ * @private
35
+ * @param textProps - Text properties to validate
36
+ */
37
+ #validateTextProperties(textProps) {
38
+ if (!textProps.text || textProps.x == null || textProps.y == null) {
39
+ throw new Error("createText: text, x, and y are required.");
40
+ }
41
+ }
42
+ /**
43
+ * Renders enhanced text using the new text renderer.
44
+ * @private
45
+ * @param ctx - Canvas 2D context
46
+ * @param textProps - Text properties
47
+ */
48
+ async #renderEnhancedText(ctx, textProps) {
49
+ await enhancedTextRenderer_1.EnhancedTextRenderer.renderText(ctx, textProps);
50
+ }
20
51
  /**
21
52
  * Creates a canvas with the given configuration.
22
53
  * Applies rotation, shadow, border effects, background, and stroke.
23
54
  *
24
- * @param canvas - The configuration options for the canvas.
25
- * @returns A promise that resolves to an object containing the image buffer and the canvas config.
26
- * @throws If the 2D context cannot be retrieved.
55
+ * @param canvas - Canvas configuration object containing:
56
+ * - width: Canvas width in pixels
57
+ * - height: Canvas height in pixels
58
+ * - x: X position offset (default: 0)
59
+ * - y: Y position offset (default: 0)
60
+ * - colorBg: Solid color background (hex, rgb, rgba, hsl, etc.)
61
+ * - gradientBg: Gradient background configuration
62
+ * - customBg: Custom background image buffer
63
+ * - zoom: Canvas zoom level (default: 1)
64
+ * - pattern: Pattern background configuration
65
+ * - noise: Noise effect configuration
66
+ *
67
+ * @returns Promise<CanvasResults> - Object containing canvas buffer and configuration
68
+ *
69
+ * @throws Error if canvas configuration is invalid or conflicting
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * const result = await painter.createCanvas({
74
+ * width: 800,
75
+ * height: 600,
76
+ * colorBg: '#ffffff',
77
+ * zoom: 1.5
78
+ * });
79
+ * const buffer = result.buffer;
80
+ * ```
27
81
  */
28
82
  async createCanvas(canvas) {
29
- const { width = 500, height = 500, x = 0, y = 0, rotation = 0, borderRadius = 0, customBg, gradientBg, stroke, shadow, borderPosition = 'all', opacity = 1, zoom, blur } = canvas;
30
- const canvasInstance = (0, canvas_1.createCanvas)(width, height);
31
- const ctx = canvasInstance.getContext('2d');
32
- if (!ctx) {
33
- throw new Error('Unable to get 2D rendering context from canvas');
34
- }
83
+ // Handle inherit sizing
84
+ if (canvas.customBg?.inherit) {
85
+ let p = canvas.customBg.source;
86
+ if (!/^https?:\/\//i.test(p))
87
+ p = path_1.default.join(process.cwd(), p);
88
+ try {
89
+ const img = await (0, canvas_1.loadImage)(p);
90
+ canvas.width = img.width;
91
+ canvas.height = img.height;
92
+ }
93
+ catch (e) {
94
+ console.error('inherit load failed:', e?.message ?? e);
95
+ }
96
+ }
97
+ // 2) Use final width/height after inherit
98
+ const width = canvas.width ?? 500;
99
+ const height = canvas.height ?? 500;
100
+ const { x = 0, y = 0, rotation = 0, borderRadius = 0, borderPosition = 'all', opacity = 1, colorBg, customBg, gradientBg, patternBg, noiseBg, blendMode, zoom, stroke, shadow, blur } = canvas;
101
+ // Validate background configuration
102
+ const bgSources = [
103
+ canvas.colorBg ? 'colorBg' : null,
104
+ canvas.gradientBg ? 'gradientBg' : null,
105
+ canvas.customBg ? 'customBg' : null
106
+ ].filter(Boolean);
107
+ if (bgSources.length > 1) {
108
+ throw new Error(`createCanvas: only one of colorBg, gradientBg, or customBg can be used. You provided: ${bgSources.join(', ')}`);
109
+ }
110
+ const cv = (0, canvas_1.createCanvas)(width, height);
111
+ const ctx = cv.getContext('2d');
112
+ if (!ctx)
113
+ throw new Error('Unable to get 2D context');
35
114
  ctx.globalAlpha = opacity;
36
- (0, utils_1.applyRotation)(ctx, rotation, x, y, width, height);
37
- (0, utils_1.applyShadow)(ctx, shadow, x, y, width, height);
115
+ // ---- BACKGROUND (clipped) ----
38
116
  ctx.save();
39
- if (borderRadius) {
40
- (0, utils_1.backgroundRadius)(ctx, x, y, width, height, borderRadius, borderPosition);
41
- }
117
+ (0, utils_1.applyRotation)(ctx, rotation, x, y, width, height);
118
+ (0, utils_1.buildPath)(ctx, x, y, width, height, borderRadius, borderPosition);
119
+ ctx.clip();
120
+ (0, utils_1.applyCanvasZoom)(ctx, width, height, zoom);
42
121
  ctx.translate(x, y);
43
- if (customBg) {
44
- await (0, utils_1.customBackground)(ctx, canvas, zoom, blur);
45
- }
46
- else if (gradientBg) {
47
- await (0, utils_1.drawBackgroundGradient)(ctx, canvas, zoom, blur);
48
- }
49
- else {
50
- await (0, utils_1.drawBackgroundColor)(ctx, canvas, zoom, blur);
51
- }
122
+ if (typeof blendMode === 'string') {
123
+ ctx.globalCompositeOperation = blendMode;
124
+ }
125
+ if (customBg)
126
+ await (0, utils_1.customBackground)(ctx, { ...canvas, blur });
127
+ else if (gradientBg)
128
+ await (0, utils_1.drawBackgroundGradient)(ctx, { ...canvas, blur });
129
+ else
130
+ await (0, utils_1.drawBackgroundColor)(ctx, { ...canvas, blur, colorBg: colorBg ?? '#000' });
131
+ if (patternBg)
132
+ await enhancedPatternRenderer_1.EnhancedPatternRenderer.renderPattern(ctx, cv, patternBg);
133
+ if (noiseBg)
134
+ (0, utils_1.applyNoise)(ctx, width, height, noiseBg.intensity ?? 0.05);
52
135
  ctx.restore();
53
- (0, utils_1.applyStroke)(ctx, stroke, x, y, width, height);
54
- return { buffer: canvasInstance.toBuffer('image/png'), canvas };
136
+ // Apply shadow effect
137
+ if (shadow) {
138
+ ctx.save();
139
+ (0, utils_1.buildPath)(ctx, x, y, width, height, borderRadius, borderPosition);
140
+ (0, utils_1.applyShadow)(ctx, shadow, x, y, width, height);
141
+ ctx.restore();
142
+ }
143
+ // Apply stroke effect
144
+ if (stroke) {
145
+ ctx.save();
146
+ (0, utils_1.buildPath)(ctx, x, y, width, height, borderRadius, borderPosition);
147
+ (0, utils_1.applyStroke)(ctx, stroke, x, y, width, height);
148
+ ctx.restore();
149
+ }
150
+ return { buffer: cv.toBuffer('image/png'), canvas };
55
151
  }
56
152
  /**
57
153
  * Draws one or more images (or shapes) on an existing canvas buffer.
58
- * @param images - A single image properties object or an array of them.
59
- * @param canvasBuffer - A Buffer or a CanvasResults object containing a buffer.
60
- * @returns A promise that resolves to a Buffer representing the updated canvas.
154
+ *
155
+ * @param images - Single ImageProperties object or array of ImageProperties containing:
156
+ * - source: Image path/URL/Buffer or ShapeType ('rectangle', 'circle', etc.)
157
+ * - x: X position on canvas
158
+ * - y: Y position on canvas
159
+ * - width: Image/shape width (optional, defaults to original size)
160
+ * - height: Image/shape height (optional, defaults to original size)
161
+ * - inherit: Use original image dimensions (boolean)
162
+ * - fit: Image fitting mode ('fill', 'contain', 'cover', 'scale-down', 'none')
163
+ * - align: Image alignment ('center', 'start', 'end')
164
+ * - rotation: Rotation angle in degrees (default: 0)
165
+ * - opacity: Opacity level 0-1 (default: 1)
166
+ * - blur: Blur radius in pixels (default: 0)
167
+ * - borderRadius: Border radius or 'circular' (default: 0)
168
+ * - borderPosition: Border position ('all', 'top', 'bottom', 'left', 'right')
169
+ * - filters: Array of image filters to apply
170
+ * - shape: Shape properties (when source is a shape)
171
+ * - shadow: Shadow configuration
172
+ * - stroke: Stroke configuration
173
+ * - boxBackground: Background behind image/shape
174
+ *
175
+ * @param canvasBuffer - Existing canvas buffer (Buffer) or CanvasResults object
176
+ *
177
+ * @returns Promise<Buffer> - Updated canvas buffer in PNG format
178
+ *
179
+ * @throws Error if source, x, or y are missing
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * const result = await painter.createImage([
184
+ * {
185
+ * source: 'rectangle',
186
+ * x: 100, y: 100,
187
+ * width: 200, height: 150,
188
+ * shape: { fill: true, color: '#ff6b6b' },
189
+ * shadow: { color: 'rgba(0,0,0,0.3)', offsetX: 5, offsetY: 5, blur: 10 }
190
+ * }
191
+ * ], canvasBuffer);
192
+ * ```
61
193
  */
62
194
  async createImage(images, canvasBuffer) {
63
- if (!Array.isArray(images)) {
64
- images = [images];
195
+ const list = Array.isArray(images) ? images : [images];
196
+ // Load base canvas buffer
197
+ const base = Buffer.isBuffer(canvasBuffer)
198
+ ? await (0, canvas_1.loadImage)(canvasBuffer)
199
+ : await (0, canvas_1.loadImage)(canvasBuffer.buffer);
200
+ const cv = (0, canvas_1.createCanvas)(base.width, base.height);
201
+ const ctx = cv.getContext("2d");
202
+ if (!ctx)
203
+ throw new Error("Unable to get 2D rendering context");
204
+ // Paint bg
205
+ ctx.drawImage(base, 0, 0);
206
+ // Draw each image/shape on canvas
207
+ for (const ip of list) {
208
+ await this.#drawImageBitmap(ctx, ip);
209
+ }
210
+ // Return updated buffer
211
+ return cv.toBuffer("image/png");
212
+ }
213
+ /**
214
+ * Draws a single bitmap or shape with independent shadow & stroke.
215
+ * @private
216
+ * @param ctx - Canvas 2D context
217
+ * @param ip - Image properties
218
+ */
219
+ async #drawImageBitmap(ctx, ip) {
220
+ const { source, x, y, width, height, inherit, fit = "fill", align = "center", rotation = 0, opacity = 1, blur = 0, borderRadius = 0, borderPosition = "all", shadow, stroke, boxBackground, shape, filters } = ip;
221
+ this.#validateImageProperties(ip);
222
+ // Check if source is a shape
223
+ if ((0, utils_1.isShapeSource)(source)) {
224
+ await this.#drawShape(ctx, source, x, y, width ?? 100, height ?? 100, {
225
+ ...shape,
226
+ rotation,
227
+ opacity,
228
+ blur,
229
+ borderRadius,
230
+ borderPosition,
231
+ shadow,
232
+ stroke,
233
+ boxBackground,
234
+ filters
235
+ });
236
+ return;
65
237
  }
66
- let baseImage;
67
- if (Buffer.isBuffer(canvasBuffer)) {
68
- baseImage = await (0, canvas_1.loadImage)(canvasBuffer);
238
+ // Handle image sources
239
+ const img = await (0, utils_1.loadImageCached)(source);
240
+ // Resolve this image's destination box
241
+ const boxW = (inherit && !width) ? img.width : (width ?? img.width);
242
+ const boxH = (inherit && !height) ? img.height : (height ?? img.height);
243
+ const box = { x, y, w: boxW, h: boxH };
244
+ ctx.save();
245
+ // Rotate around the box center; affects shadow, background, bitmap, stroke uniformly
246
+ (0, utils_1.applyRotation)(ctx, rotation, box.x, box.y, box.w, box.h);
247
+ // 1) Shadow (independent) — supports gradient or color
248
+ (0, utils_1.applyShadow)(ctx, box, shadow);
249
+ // 2) Optional box background (under bitmap, inside clip) — color or gradient
250
+ (0, utils_1.drawBoxBackground)(ctx, box, boxBackground, borderRadius, borderPosition);
251
+ // 3) Clip to image border radius, then draw the bitmap with blur/opacity and fit/align
252
+ ctx.save();
253
+ (0, utils_1.buildPath)(ctx, box.x, box.y, box.w, box.h, borderRadius, borderPosition);
254
+ ctx.clip();
255
+ const { dx, dy, dw, dh, sx, sy, sw, sh } = (0, utils_1.fitInto)(box.x, box.y, box.w, box.h, img.width, img.height, fit, align);
256
+ const prevAlpha = ctx.globalAlpha;
257
+ ctx.globalAlpha = opacity ?? 1;
258
+ if ((blur ?? 0) > 0)
259
+ ctx.filter = `blur(${blur}px)`;
260
+ // Apply professional image filters BEFORE drawing
261
+ if (filters && filters.length > 0) {
262
+ await (0, utils_1.applySimpleProfessionalFilters)(ctx, filters, dw, dh);
69
263
  }
70
- else if (canvasBuffer.buffer) {
71
- baseImage = await (0, canvas_1.loadImage)(canvasBuffer.buffer);
264
+ ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
265
+ ctx.filter = "none";
266
+ ctx.globalAlpha = prevAlpha;
267
+ ctx.restore();
268
+ // 4) Stroke (independent) — supports gradient or color
269
+ (0, utils_1.applyStroke)(ctx, box, stroke);
270
+ ctx.restore();
271
+ }
272
+ /**
273
+ * Draws a shape with all effects (shadow, stroke, filters, etc.).
274
+ * @private
275
+ * @param ctx - Canvas 2D context
276
+ * @param shapeType - Type of shape to draw
277
+ * @param x - X position
278
+ * @param y - Y position
279
+ * @param width - Shape width
280
+ * @param height - Shape height
281
+ * @param options - Shape drawing options
282
+ */
283
+ async #drawShape(ctx, shapeType, x, y, width, height, options) {
284
+ const box = { x, y, w: width, h: height };
285
+ ctx.save();
286
+ // Apply rotation
287
+ if (options.rotation) {
288
+ (0, utils_1.applyRotation)(ctx, options.rotation, box.x, box.y, box.w, box.h);
289
+ }
290
+ // Apply opacity
291
+ if (options.opacity !== undefined) {
292
+ ctx.globalAlpha = options.opacity;
293
+ }
294
+ // Apply blur
295
+ if (options.blur && options.blur > 0) {
296
+ ctx.filter = `blur(${options.blur}px)`;
297
+ }
298
+ // 1) Custom Shadow for complex shapes (heart, star)
299
+ if (options.shadow && this.#isComplexShape(shapeType)) {
300
+ this.#applyShapeShadow(ctx, shapeType, x, y, width, height, options.shadow, {
301
+ radius: options.radius,
302
+ sides: options.sides,
303
+ innerRadius: options.innerRadius,
304
+ outerRadius: options.outerRadius
305
+ });
72
306
  }
73
- else {
74
- throw new Error('Invalid canvasBuffer provided; expected Buffer or CanvasResults with a buffer');
307
+ else if (options.shadow) {
308
+ // Use standard shadow for simple shapes
309
+ (0, utils_1.applyShadow)(ctx, box, options.shadow);
75
310
  }
76
- if (!baseImage) {
77
- throw new Error('Unable to load base image from the provided buffer');
311
+ // 2) Optional box background
312
+ if (options.boxBackground) {
313
+ (0, utils_1.drawBoxBackground)(ctx, box, options.boxBackground, options.borderRadius, options.borderPosition);
78
314
  }
79
- const canvas = (0, canvas_1.createCanvas)(baseImage.width, baseImage.height);
80
- const ctx = canvas.getContext('2d');
81
- if (!ctx) {
82
- throw new Error('Unable to get 2D rendering context from canvas');
315
+ // 3) Draw the shape
316
+ ctx.save();
317
+ if (options.borderRadius) {
318
+ (0, utils_1.buildPath)(ctx, box.x, box.y, box.w, box.h, options.borderRadius, options.borderPosition);
319
+ ctx.clip();
320
+ }
321
+ // Apply professional filters BEFORE drawing the shape
322
+ if (options.filters && options.filters.length > 0) {
323
+ await (0, utils_1.applySimpleProfessionalFilters)(ctx, options.filters, width, height);
324
+ }
325
+ (0, utils_1.drawShape)(ctx, shapeType, x, y, width, height, {
326
+ fill: options.fill,
327
+ color: options.color,
328
+ gradient: options.gradient,
329
+ radius: options.radius,
330
+ sides: options.sides,
331
+ innerRadius: options.innerRadius,
332
+ outerRadius: options.outerRadius
333
+ });
334
+ ctx.restore();
335
+ // 4) Custom Stroke for complex shapes (heart, star)
336
+ if (options.stroke && this.#isComplexShape(shapeType)) {
337
+ this.#applyShapeStroke(ctx, shapeType, x, y, width, height, options.stroke, {
338
+ radius: options.radius,
339
+ sides: options.sides,
340
+ innerRadius: options.innerRadius,
341
+ outerRadius: options.outerRadius
342
+ });
83
343
  }
84
- ctx.drawImage(baseImage, 0, 0);
85
- for (const imageProps of images) {
86
- await this.drawImage(ctx, imageProps);
344
+ else if (options.stroke) {
345
+ // Use standard stroke for simple shapes
346
+ (0, utils_1.applyStroke)(ctx, box, options.stroke);
87
347
  }
88
- return canvas.toBuffer('image/png');
348
+ // Reset filters and alpha
349
+ ctx.filter = "none";
350
+ ctx.globalAlpha = 1;
351
+ ctx.restore();
352
+ }
353
+ /**
354
+ * Checks if shape needs custom shadow/stroke (heart, star).
355
+ * @private
356
+ * @param shapeType - Type of shape
357
+ * @returns True if shape is complex and needs custom effects
358
+ */
359
+ #isComplexShape(shapeType) {
360
+ return shapeType === 'heart' || shapeType === 'star';
89
361
  }
90
- async createText(textOptionsArray, buffer) {
362
+ /**
363
+ * Applies custom shadow for complex shapes (heart, star).
364
+ * @private
365
+ * @param ctx - Canvas 2D context
366
+ * @param shapeType - Type of shape
367
+ * @param x - X position
368
+ * @param y - Y position
369
+ * @param width - Shape width
370
+ * @param height - Shape height
371
+ * @param shadow - Shadow options
372
+ * @param shapeOptions - Shape-specific options
373
+ */
374
+ #applyShapeShadow(ctx, shapeType, x, y, width, height, shadow, shapeProps) {
375
+ const { color = "rgba(0,0,0,1)", gradient, opacity = 0.4, offsetX = 0, offsetY = 0, blur = 20 } = shadow;
376
+ ctx.save();
377
+ ctx.globalAlpha = opacity;
378
+ if (blur > 0)
379
+ ctx.filter = `blur(${blur}px)`;
380
+ // Set shadow color or gradient
381
+ if (gradient) {
382
+ const gfill = (0, utils_1.createGradientFill)(ctx, gradient, { x: x + offsetX, y: y + offsetY, w: width, h: height });
383
+ ctx.fillStyle = gfill;
384
+ }
385
+ else {
386
+ ctx.fillStyle = color;
387
+ }
388
+ // Create shadow path
389
+ (0, utils_1.createShapePath)(ctx, shapeType, x + offsetX, y + offsetY, width, height, shapeProps);
390
+ ctx.fill();
391
+ ctx.filter = "none";
392
+ ctx.globalAlpha = 1;
393
+ ctx.restore();
394
+ }
395
+ /**
396
+ * Applies custom stroke for complex shapes (heart, star).
397
+ * @private
398
+ * @param ctx - Canvas 2D context
399
+ * @param shapeType - Type of shape
400
+ * @param x - X position
401
+ * @param y - Y position
402
+ * @param width - Shape width
403
+ * @param height - Shape height
404
+ * @param stroke - Stroke options
405
+ * @param shapeOptions - Shape-specific options
406
+ */
407
+ #applyShapeStroke(ctx, shapeType, x, y, width, height, stroke, shapeProps) {
408
+ const { color = "#000", gradient, width: strokeWidth = 2, position = 0, blur = 0, opacity = 1 } = stroke;
409
+ ctx.save();
410
+ if (blur > 0)
411
+ ctx.filter = `blur(${blur}px)`;
412
+ ctx.globalAlpha = opacity;
413
+ // Set stroke color or gradient
414
+ if (gradient) {
415
+ const gstroke = (0, utils_1.createGradientFill)(ctx, gradient, { x, y, w: width, h: height });
416
+ ctx.strokeStyle = gstroke;
417
+ }
418
+ else {
419
+ ctx.strokeStyle = color;
420
+ }
421
+ ctx.lineWidth = strokeWidth;
422
+ // Create stroke path
423
+ (0, utils_1.createShapePath)(ctx, shapeType, x, y, width, height, shapeProps);
424
+ ctx.stroke();
425
+ ctx.filter = "none";
426
+ ctx.globalAlpha = 1;
427
+ ctx.restore();
428
+ }
429
+ /**
430
+ * Creates text on an existing canvas buffer with enhanced styling options.
431
+ *
432
+ * @param textArray - Single TextProperties object or array of TextProperties containing:
433
+ * - text: Text content to render (required)
434
+ * - x: X position on canvas (required)
435
+ * - y: Y position on canvas (required)
436
+ * - fontPath: Path to custom font file (.ttf, .otf, .woff, etc.)
437
+ * - fontName: Custom font name (used with fontPath)
438
+ * - fontSize: Font size in pixels (default: 16)
439
+ * - fontFamily: Font family name (e.g., 'Arial', 'Helvetica')
440
+ * - bold: Make text bold (boolean)
441
+ * - italic: Make text italic (boolean)
442
+ * - underline: Add underline decoration (boolean)
443
+ * - overline: Add overline decoration (boolean)
444
+ * - strikethrough: Add strikethrough decoration (boolean)
445
+ * - highlight: Highlight text with background color and opacity
446
+ * - lineHeight: Line height multiplier (default: 1.4)
447
+ * - letterSpacing: Space between letters in pixels
448
+ * - wordSpacing: Space between words in pixels
449
+ * - maxWidth: Maximum width for text wrapping
450
+ * - maxHeight: Maximum height for text (truncates with ellipsis)
451
+ * - textAlign: Horizontal text alignment ('left', 'center', 'right', 'start', 'end')
452
+ * - textBaseline: Vertical text baseline ('alphabetic', 'bottom', 'hanging', 'ideographic', 'middle', 'top')
453
+ * - color: Text color (hex, rgb, rgba, hsl, etc.)
454
+ * - gradient: Gradient fill for text
455
+ * - opacity: Text opacity (0-1, default: 1)
456
+ * - glow: Text glow effect with color, intensity, and opacity
457
+ * - shadow: Text shadow effect with color, offset, blur, and opacity
458
+ * - stroke: Text stroke/outline with color, width, gradient, and opacity
459
+ * - rotation: Text rotation in degrees
460
+ *
461
+ * @param canvasBuffer - Existing canvas buffer (Buffer) or CanvasResults object
462
+ *
463
+ * @returns Promise<Buffer> - Updated canvas buffer in PNG format
464
+ *
465
+ * @throws Error if text, x, or y are missing, or if canvas buffer is invalid
466
+ *
467
+ * @example
468
+ * ```typescript
469
+ * const result = await painter.createText([
470
+ * {
471
+ * text: "Hello World!",
472
+ * x: 100, y: 100,
473
+ * fontSize: 24,
474
+ * bold: true,
475
+ * color: '#ff6b6b',
476
+ * shadow: { color: 'rgba(0,0,0,0.3)', offsetX: 2, offsetY: 2, blur: 4 },
477
+ * underline: true,
478
+ * highlight: { color: '#ffff00', opacity: 0.3 }
479
+ * }
480
+ * ], canvasBuffer);
481
+ * ```
482
+ */
483
+ async createText(textArray, canvasBuffer) {
91
484
  try {
92
- if (!Array.isArray(textOptionsArray)) {
93
- textOptionsArray = [textOptionsArray];
485
+ // Ensure textArray is an array
486
+ const textList = Array.isArray(textArray) ? textArray : [textArray];
487
+ // Validate each text object
488
+ for (const textProps of textList) {
489
+ this.#validateTextProperties(textProps);
94
490
  }
491
+ // Load existing canvas buffer
95
492
  let existingImage;
96
- if (Buffer.isBuffer(buffer)) {
97
- existingImage = await (0, canvas_1.loadImage)(buffer);
493
+ if (Buffer.isBuffer(canvasBuffer)) {
494
+ existingImage = await (0, canvas_1.loadImage)(canvasBuffer);
98
495
  }
99
- else if (buffer && buffer.buffer) {
100
- existingImage = await (0, canvas_1.loadImage)(buffer.buffer);
496
+ else if (canvasBuffer && canvasBuffer.buffer) {
497
+ existingImage = await (0, canvas_1.loadImage)(canvasBuffer.buffer);
101
498
  }
102
499
  else {
103
500
  throw new Error('Invalid canvasBuffer provided. It should be a Buffer or CanvasResults object with a buffer');
@@ -105,21 +502,23 @@ class ApexPainter {
105
502
  if (!existingImage) {
106
503
  throw new Error('Unable to load image from buffer');
107
504
  }
505
+ // Create new canvas with same dimensions
108
506
  const canvas = (0, canvas_1.createCanvas)(existingImage.width, existingImage.height);
109
507
  const ctx = canvas.getContext("2d");
508
+ if (!ctx) {
509
+ throw new Error("Unable to get 2D rendering context");
510
+ }
511
+ // Draw existing image as background
110
512
  ctx.drawImage(existingImage, 0, 0);
111
- for (const textOptions of textOptionsArray) {
112
- const mergedTextOptions = textOptions;
113
- if (mergedTextOptions.fontPath) {
114
- canvas_1.GlobalFonts.registerFromPath(path_1.default.join(process.cwd(), mergedTextOptions.fontPath), (mergedTextOptions.fontName || 'customFont'));
115
- }
116
- (0, utils_1.drawText)(ctx, mergedTextOptions);
513
+ // Render each text object with enhanced features
514
+ for (const textProps of textList) {
515
+ await this.#renderEnhancedText(ctx, textProps);
117
516
  }
118
517
  return canvas.toBuffer("image/png");
119
518
  }
120
519
  catch (error) {
121
- console.error("Error loading existing image:", error);
122
- throw new Error("Invalid image buffer");
520
+ console.error("Error creating text:", error);
521
+ throw new Error("Failed to create text on canvas");
123
522
  }
124
523
  }
125
524
  async createCustom(options, buffer) {
@@ -362,60 +761,6 @@ class ApexPainter {
362
761
  }
363
762
  }
364
763
  async drawImage(ctx, image) {
365
- const { source, x, y, width, height, rotation = 0, borderRadius = 0, stroke, shadow, isFilled, color, gradient, borderPosition = 'all', opacity, perspective, blur = 0, zoom, filling } = image;
366
- if (perspective)
367
- throw new Error("`perspective` Feature is under construction. Please refrain from using it.");
368
- if (!source || typeof x !== 'number' || typeof y !== 'number' || typeof width !== 'number' || typeof height !== 'number') {
369
- throw new Error('Invalid image properties: source, x, y, width, and height are required.');
370
- }
371
- const shapeNames = ['circle', 'square', 'triangle', 'pentagon', 'hexagon', 'heptagon', 'octagon', 'star', 'kite', 'arrow', 'star', 'oval', 'heart', 'diamond', 'trapezoid', 'cloud'];
372
- const isShape = shapeNames.includes(source.toLowerCase());
373
- if (isShape) {
374
- (0, utils_1.drawShape)(ctx, { source, x, y, width, height, opacity, rotation, borderRadius, stroke, shadow, isFilled, color, gradient, borderPosition, filling });
375
- return;
376
- }
377
- let loadedImage;
378
- try {
379
- if (source.startsWith('http')) {
380
- loadedImage = await (0, canvas_1.loadImage)(source);
381
- }
382
- else {
383
- const imagePath = path_1.default.resolve(process.cwd(), source);
384
- loadedImage = await (0, canvas_1.loadImage)(imagePath);
385
- }
386
- }
387
- catch (e) {
388
- throw new Error(`Error loading image: ${e.message}`);
389
- }
390
- if (!loadedImage) {
391
- throw new Error('Invalid image source or failed to load image');
392
- }
393
- ctx.save();
394
- const scale = zoom?.scale ?? 1;
395
- const focusX = zoom?.x ?? 0.5;
396
- const focusY = zoom?.y ?? 0.5;
397
- if (scale !== 1) {
398
- ctx.translate(focusX, focusY);
399
- ctx.scale(scale, scale);
400
- }
401
- if (opacity !== undefined) {
402
- ctx.globalAlpha = opacity;
403
- }
404
- if (blur > 0) {
405
- ctx.filter = `blur(${blur}px)`;
406
- }
407
- (0, utils_1.applyRotation)(ctx, rotation, x, y, width, height);
408
- (0, utils_1.applyShadow)(ctx, shadow, x, y, width, height);
409
- if (perspective) {
410
- throw new Error('Under construction');
411
- }
412
- (0, utils_1.imageRadius)(ctx, loadedImage, x, y, width, height, borderRadius, borderPosition);
413
- ctx.globalAlpha = 1.0;
414
- if (stroke?.opacity)
415
- ctx.globalAlpha = stroke.opacity;
416
- (0, utils_1.applyStroke)(ctx, stroke, x, y, width, height, undefined);
417
- ctx.globalAlpha = 1.0;
418
- ctx.restore();
419
764
  }
420
765
  async extractFrames(videoSource, options) {
421
766
  const frames = [];
@@ -481,242 +826,6 @@ class ApexPainter {
481
826
  processVideoExtraction(videoPath, frames, options, resolve, reject);
482
827
  });
483
828
  }
484
- /**
485
- * Sets a pattern on a specific area of a buffered image.
486
- * @param {Buffer} buffer - The source image buffer.
487
- * @param {Object} options - Options to customize the pattern.
488
- * @returns {Promise<Buffer>} - The adjusted image buffer.
489
- */
490
- async patterns(buffer, options) {
491
- const img = await (0, canvas_1.loadImage)(buffer);
492
- const canvas = (0, canvas_1.createCanvas)(img.width, img.height);
493
- const ctx = canvas.getContext('2d');
494
- ctx.drawImage(img, 0, 0);
495
- const x = options.x || 0;
496
- const y = options.y || 0;
497
- const width = options.width || img.width;
498
- const height = options.height || img.height;
499
- ctx.save();
500
- ctx.beginPath();
501
- ctx.rect(x, y, width, height);
502
- ctx.clip();
503
- if (options.gradient) {
504
- await this.fillWithGradient(ctx, width, height, options.gradient, x, y);
505
- }
506
- else if (options.backgroundColor) {
507
- ctx.fillStyle = options.backgroundColor;
508
- ctx.fillRect(x, y, width, height);
509
- }
510
- switch (options.type) {
511
- case 'dots':
512
- await this.drawDotsPattern(ctx, width, height, options, x, y);
513
- break;
514
- case 'stripes':
515
- await this.drawStripesPattern(ctx, width, height, options, x, y);
516
- break;
517
- case 'grid':
518
- await this.drawGridPattern(ctx, width, height, options, x, y);
519
- break;
520
- case 'checkerboard':
521
- await this.drawCheckerboardPattern(ctx, width, height, options, x, y);
522
- break;
523
- case 'custom':
524
- await this.drawCustomPattern(ctx, width, height, options, x, y);
525
- break;
526
- case 'noise':
527
- await this.drawNoisePattern(ctx, width, height, x, y);
528
- break;
529
- case 'waves':
530
- await this.drawWavePattern(ctx, width, height, options, x, y);
531
- break;
532
- case 'diagonal-checkerboard':
533
- await this.drawDiagonalCheckerboardPattern(ctx, width, height, options, x, y);
534
- break;
535
- default:
536
- throw new Error('Invalid pattern type specified.');
537
- }
538
- ctx.restore();
539
- return canvas.toBuffer('image/png');
540
- }
541
- /**
542
- * Fills the specified area with a gradient.
543
- * @param ctx The rendering context of the canvas.
544
- * @param width The width of the area to fill.
545
- * @param height The height of the area to fill.
546
- * @param gradient The gradient options.
547
- * @param x The x offset of the area.
548
- * @param y The y offset of the area.
549
- */
550
- async fillWithGradient(ctx, width, height, gradient, x = 0, y = 0) {
551
- let grad;
552
- if (gradient.type === 'linear') {
553
- grad = ctx.createLinearGradient((gradient.startX || 0) + x, (gradient.startY || 0) + y, (gradient.endX || width) + x, (gradient.endY || height) + y);
554
- }
555
- else {
556
- grad = ctx.createRadialGradient((gradient.startX || width / 2) + x, (gradient.startY || height / 2) + y, gradient.startRadius || 0, (gradient.endX || width / 2) + x, (gradient.endY || height / 2) + y, gradient.endRadius || Math.max(width, height));
557
- }
558
- gradient.colors.forEach((stop) => {
559
- grad.addColorStop(stop.stop, stop.color);
560
- });
561
- ctx.fillStyle = grad;
562
- ctx.fillRect(x, y, width, height);
563
- }
564
- /**
565
- * Draws a dots pattern in the specified area.
566
- * @param ctx The rendering context of the canvas.
567
- * @param width The width of the area.
568
- * @param height The height of the area.
569
- * @param options Options to customize the dot pattern.
570
- * @param x The x offset of the area.
571
- * @param y The y offset of the area.
572
- */
573
- async drawDotsPattern(ctx, width, height, options, x = 0, y = 0) {
574
- const color = options.color || 'black';
575
- const radius = options.size || 5;
576
- const spacing = options.spacing || 10;
577
- ctx.fillStyle = color;
578
- for (let posY = y; posY < y + height; posY += spacing) {
579
- for (let posX = x; posX < x + width; posX += spacing) {
580
- ctx.beginPath();
581
- ctx.arc(posX, posY, radius, 0, Math.PI * 2);
582
- ctx.fill();
583
- }
584
- }
585
- }
586
- /**
587
- * Draws a stripes pattern in the specified area.
588
- * @param ctx The rendering context of the canvas.
589
- * @param width The width of the area.
590
- * @param height The height of the area.
591
- * @param options Options to customize the stripes pattern.
592
- * @param x The x offset of the area.
593
- * @param y The y offset of the area.
594
- */
595
- async drawStripesPattern(ctx, width, height, options, x = 0, y = 0) {
596
- const color = options.color || 'black';
597
- const stripeWidth = options.size || 10;
598
- const spacing = options.spacing || 20;
599
- const angle = options.angle || 0;
600
- ctx.fillStyle = color;
601
- ctx.save();
602
- ctx.translate(x + width / 2, y + height / 2);
603
- ctx.rotate((angle * Math.PI) / 180);
604
- ctx.translate(-width / 2, -height / 2);
605
- for (let posX = 0; posX < width; posX += stripeWidth + spacing) {
606
- ctx.fillRect(posX, 0, stripeWidth, height);
607
- }
608
- ctx.restore();
609
- }
610
- /**
611
- * Draws a grid pattern in the specified area.
612
- * @param ctx The rendering context of the canvas.
613
- * @param width The width of the area.
614
- * @param height The height of the area.
615
- * @param options Options to customize the grid pattern.
616
- * @param x The x offset of the area.
617
- * @param y The y offset of the area.
618
- */
619
- async drawGridPattern(ctx, width, height, options, x = 0, y = 0) {
620
- const color = options.color || 'black';
621
- const size = options.size || 20;
622
- ctx.strokeStyle = color;
623
- ctx.lineWidth = 1;
624
- for (let posX = x; posX <= x + width; posX += size) {
625
- ctx.beginPath();
626
- ctx.moveTo(posX, y);
627
- ctx.lineTo(posX, y + height);
628
- ctx.stroke();
629
- }
630
- for (let posY = y; posY <= y + height; posY += size) {
631
- ctx.beginPath();
632
- ctx.moveTo(x, posY);
633
- ctx.lineTo(x + width, posY);
634
- ctx.stroke();
635
- }
636
- }
637
- /**
638
- * Draws a checkerboard pattern in the specified area.
639
- * @param ctx The rendering context of the canvas.
640
- * @param width The width of the area.
641
- * @param height The height of the area.
642
- * @param options Options to customize the checkerboard pattern.
643
- * @param x The x offset of the area.
644
- * @param y The y offset of the area.
645
- */
646
- async drawCheckerboardPattern(ctx, width, height, options, x = 0, y = 0) {
647
- const color1 = options.color || 'black';
648
- const color2 = options.secondaryColor || 'white';
649
- const size = options.size || 20;
650
- for (let posY = y; posY < y + height; posY += size) {
651
- for (let posX = x; posX < x + width; posX += size) {
652
- ctx.fillStyle = (Math.floor(posX / size) + Math.floor(posY / size)) % 2 === 0 ? color1 : color2;
653
- ctx.fillRect(posX, posY, size, size);
654
- }
655
- }
656
- }
657
- /**
658
- * Draws a custom image pattern in the specified area.
659
- * @param ctx The rendering context of the canvas.
660
- * @param width The width of the area.
661
- * @param height The height of the area.
662
- * @param options Options to customize the custom pattern.
663
- * @param x The x offset of the area.
664
- * @param y The y offset of the area.
665
- */
666
- async drawCustomPattern(ctx, width, height, options, x = 0, y = 0) {
667
- if (!options.customPatternImage) {
668
- throw new Error('Custom pattern image path is required for custom patterns.');
669
- }
670
- const patternImage = await (0, canvas_1.loadImage)(options.customPatternImage);
671
- const pattern = ctx.createPattern(patternImage, 'repeat');
672
- ctx.fillStyle = pattern;
673
- ctx.translate(x, y);
674
- ctx.fillRect(0, 0, width, height);
675
- ctx.resetTransform();
676
- }
677
- async drawWavePattern(ctx, width, height, options, x = 0, y = 0) {
678
- const color = options.color || 'black';
679
- const waveHeight = options.size || 10;
680
- const frequency = options.spacing || 20;
681
- ctx.strokeStyle = color;
682
- ctx.lineWidth = 2;
683
- for (let posY = y; posY < y + height; posY += frequency) {
684
- ctx.beginPath();
685
- for (let posX = x; posX < x + width; posX += 5) {
686
- const waveY = posY + Math.sin(posX / frequency) * waveHeight;
687
- ctx.lineTo(posX, waveY);
688
- }
689
- ctx.stroke();
690
- }
691
- }
692
- async drawNoisePattern(ctx, width, height, x = 0, y = 0) {
693
- const imageData = ctx.createImageData(width, height);
694
- const data = imageData.data;
695
- for (let i = 0; i < data.length; i += 4) {
696
- const gray = Math.random() * 255;
697
- data[i] = gray;
698
- data[i + 1] = gray;
699
- data[i + 2] = gray;
700
- data[i + 3] = Math.random() * 255;
701
- }
702
- ctx.putImageData(imageData, x, y);
703
- }
704
- async drawDiagonalCheckerboardPattern(ctx, width, height, options, x = 0, y = 0) {
705
- const color1 = options.color || 'black';
706
- const color2 = options.secondaryColor || 'white';
707
- const size = options.size || 20;
708
- ctx.save();
709
- ctx.translate(x + width / 2, y + height / 2);
710
- ctx.rotate(Math.PI / 4);
711
- ctx.translate(-width / 2, -height / 2);
712
- for (let posY = 0; posY < height; posY += size) {
713
- for (let posX = 0; posX < width; posX += size) {
714
- ctx.fillStyle = (Math.floor(posX / size) + Math.floor(posY / size)) % 2 === 0 ? color1 : color2;
715
- ctx.fillRect(posX, posY, size, size);
716
- }
717
- }
718
- ctx.restore();
719
- }
720
829
  async masking(source, maskSource, options = { type: "alpha" }) {
721
830
  const img = await (0, canvas_1.loadImage)(source);
722
831
  const mask = await (0, canvas_1.loadImage)(maskSource);