apexify.js 5.1.1 → 5.2.0
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/CHANGELOG.md +240 -0
- package/README.md +248 -1105
- package/dist/cjs/Canvas/ApexPainter.d.ts +182 -204
- package/dist/cjs/Canvas/ApexPainter.d.ts.map +1 -1
- package/dist/cjs/Canvas/ApexPainter.js +482 -1286
- package/dist/cjs/Canvas/ApexPainter.js.map +1 -1
- package/dist/cjs/Canvas/extended/CanvasCreator.d.ts +33 -0
- package/dist/cjs/Canvas/extended/CanvasCreator.d.ts.map +1 -0
- package/dist/cjs/Canvas/extended/CanvasCreator.js +223 -0
- package/dist/cjs/Canvas/extended/CanvasCreator.js.map +1 -0
- package/dist/cjs/Canvas/extended/ChartCreator.d.ts +26 -0
- package/dist/cjs/Canvas/extended/ChartCreator.d.ts.map +1 -0
- package/dist/cjs/Canvas/extended/ChartCreator.js +50 -0
- package/dist/cjs/Canvas/extended/ChartCreator.js.map +1 -0
- package/dist/cjs/Canvas/extended/GIFCreator.d.ts +43 -0
- package/dist/cjs/Canvas/extended/GIFCreator.d.ts.map +1 -0
- package/dist/cjs/Canvas/extended/GIFCreator.js +157 -0
- package/dist/cjs/Canvas/extended/GIFCreator.js.map +1 -0
- package/dist/cjs/Canvas/extended/ImageCreator.d.ts +83 -0
- package/dist/cjs/Canvas/extended/ImageCreator.d.ts.map +1 -0
- package/dist/cjs/Canvas/extended/ImageCreator.js +479 -0
- package/dist/cjs/Canvas/extended/ImageCreator.js.map +1 -0
- package/dist/cjs/Canvas/extended/TextCreator.d.ts +35 -0
- package/dist/cjs/Canvas/extended/TextCreator.d.ts.map +1 -0
- package/dist/cjs/Canvas/extended/TextCreator.js +98 -0
- package/dist/cjs/Canvas/extended/TextCreator.js.map +1 -0
- package/dist/cjs/Canvas/extended/VideoCreator.d.ts +370 -0
- package/dist/cjs/Canvas/extended/VideoCreator.d.ts.map +1 -0
- package/dist/cjs/Canvas/extended/VideoCreator.js +478 -0
- package/dist/cjs/Canvas/extended/VideoCreator.js.map +1 -0
- package/dist/cjs/Canvas/utils/Background/bg.d.ts +1 -1
- package/dist/cjs/Canvas/utils/Background/bg.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/Background/bg.js +43 -7
- package/dist/cjs/Canvas/utils/Background/bg.js.map +1 -1
- package/dist/cjs/Canvas/utils/Charts/barchart.d.ts +230 -0
- package/dist/cjs/Canvas/utils/Charts/barchart.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Charts/barchart.js +1891 -0
- package/dist/cjs/Canvas/utils/Charts/barchart.js.map +1 -0
- package/dist/cjs/Canvas/utils/Charts/comparisonchart.d.ts +103 -0
- package/dist/cjs/Canvas/utils/Charts/comparisonchart.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Charts/comparisonchart.js +368 -0
- package/dist/cjs/Canvas/utils/Charts/comparisonchart.js.map +1 -0
- package/dist/cjs/Canvas/utils/Charts/horizontalbarchart.d.ts +178 -0
- package/dist/cjs/Canvas/utils/Charts/horizontalbarchart.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Charts/horizontalbarchart.js +1389 -0
- package/dist/cjs/Canvas/utils/Charts/horizontalbarchart.js.map +1 -0
- package/dist/cjs/Canvas/utils/Charts/index.d.ts +45 -0
- package/dist/cjs/Canvas/utils/Charts/index.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Charts/index.js +17 -0
- package/dist/cjs/Canvas/utils/Charts/index.js.map +1 -0
- package/dist/cjs/Canvas/utils/Charts/linechart.d.ts +216 -0
- package/dist/cjs/Canvas/utils/Charts/linechart.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Charts/linechart.js +1761 -0
- package/dist/cjs/Canvas/utils/Charts/linechart.js.map +1 -0
- package/dist/cjs/Canvas/utils/Charts/piechart.d.ts +167 -0
- package/dist/cjs/Canvas/utils/Charts/piechart.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Charts/piechart.js +794 -0
- package/dist/cjs/Canvas/utils/Charts/piechart.js.map +1 -0
- package/dist/cjs/Canvas/utils/General/batchOperations.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/General/batchOperations.js +3 -4
- package/dist/cjs/Canvas/utils/General/batchOperations.js.map +1 -1
- package/dist/cjs/Canvas/utils/General/general functions.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/General/general functions.js +62 -33
- package/dist/cjs/Canvas/utils/General/general functions.js.map +1 -1
- package/dist/cjs/Canvas/utils/General/imageStitching.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/General/imageStitching.js +3 -6
- package/dist/cjs/Canvas/utils/General/imageStitching.js.map +1 -1
- package/dist/cjs/Canvas/utils/Image/imageMasking.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/Image/imageMasking.js +5 -12
- package/dist/cjs/Canvas/utils/Image/imageMasking.js.map +1 -1
- package/dist/cjs/Canvas/utils/Image/imageProperties.d.ts +4 -4
- package/dist/cjs/Canvas/utils/Image/imageProperties.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/Image/imageProperties.js +44 -9
- package/dist/cjs/Canvas/utils/Image/imageProperties.js.map +1 -1
- package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.d.ts +5 -0
- package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.js +48 -5
- package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.js.map +1 -1
- package/dist/cjs/Canvas/utils/Texts/textProperties.d.ts +1 -1
- package/dist/cjs/Canvas/utils/Texts/textProperties.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/Texts/textProperties.js +48 -5
- package/dist/cjs/Canvas/utils/Texts/textProperties.js.map +1 -1
- package/dist/cjs/Canvas/utils/Video/videoHelpers.d.ts +489 -0
- package/dist/cjs/Canvas/utils/Video/videoHelpers.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Video/videoHelpers.js +1835 -0
- package/dist/cjs/Canvas/utils/Video/videoHelpers.js.map +1 -0
- package/dist/cjs/Canvas/utils/errorUtils.d.ts +15 -0
- package/dist/cjs/Canvas/utils/errorUtils.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/errorUtils.js +26 -0
- package/dist/cjs/Canvas/utils/errorUtils.js.map +1 -0
- package/dist/cjs/Canvas/utils/types.d.ts +17 -178
- 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 +4 -3
- package/dist/cjs/Canvas/utils/utils.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/utils.js +40 -6
- package/dist/cjs/Canvas/utils/utils.js.map +1 -1
- package/dist/cjs/index.d.ts +1 -8
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +14 -45
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/Canvas/ApexPainter.d.ts +182 -204
- package/dist/esm/Canvas/ApexPainter.d.ts.map +1 -1
- package/dist/esm/Canvas/ApexPainter.js +482 -1286
- package/dist/esm/Canvas/ApexPainter.js.map +1 -1
- package/dist/esm/Canvas/extended/CanvasCreator.d.ts +33 -0
- package/dist/esm/Canvas/extended/CanvasCreator.d.ts.map +1 -0
- package/dist/esm/Canvas/extended/CanvasCreator.js +223 -0
- package/dist/esm/Canvas/extended/CanvasCreator.js.map +1 -0
- package/dist/esm/Canvas/extended/ChartCreator.d.ts +26 -0
- package/dist/esm/Canvas/extended/ChartCreator.d.ts.map +1 -0
- package/dist/esm/Canvas/extended/ChartCreator.js +50 -0
- package/dist/esm/Canvas/extended/ChartCreator.js.map +1 -0
- package/dist/esm/Canvas/extended/GIFCreator.d.ts +43 -0
- package/dist/esm/Canvas/extended/GIFCreator.d.ts.map +1 -0
- package/dist/esm/Canvas/extended/GIFCreator.js +157 -0
- package/dist/esm/Canvas/extended/GIFCreator.js.map +1 -0
- package/dist/esm/Canvas/extended/ImageCreator.d.ts +83 -0
- package/dist/esm/Canvas/extended/ImageCreator.d.ts.map +1 -0
- package/dist/esm/Canvas/extended/ImageCreator.js +479 -0
- package/dist/esm/Canvas/extended/ImageCreator.js.map +1 -0
- package/dist/esm/Canvas/extended/TextCreator.d.ts +35 -0
- package/dist/esm/Canvas/extended/TextCreator.d.ts.map +1 -0
- package/dist/esm/Canvas/extended/TextCreator.js +98 -0
- package/dist/esm/Canvas/extended/TextCreator.js.map +1 -0
- package/dist/esm/Canvas/extended/VideoCreator.d.ts +370 -0
- package/dist/esm/Canvas/extended/VideoCreator.d.ts.map +1 -0
- package/dist/esm/Canvas/extended/VideoCreator.js +478 -0
- package/dist/esm/Canvas/extended/VideoCreator.js.map +1 -0
- package/dist/esm/Canvas/utils/Background/bg.d.ts +1 -1
- package/dist/esm/Canvas/utils/Background/bg.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/Background/bg.js +43 -7
- package/dist/esm/Canvas/utils/Background/bg.js.map +1 -1
- package/dist/esm/Canvas/utils/Charts/barchart.d.ts +230 -0
- package/dist/esm/Canvas/utils/Charts/barchart.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Charts/barchart.js +1891 -0
- package/dist/esm/Canvas/utils/Charts/barchart.js.map +1 -0
- package/dist/esm/Canvas/utils/Charts/comparisonchart.d.ts +103 -0
- package/dist/esm/Canvas/utils/Charts/comparisonchart.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Charts/comparisonchart.js +368 -0
- package/dist/esm/Canvas/utils/Charts/comparisonchart.js.map +1 -0
- package/dist/esm/Canvas/utils/Charts/horizontalbarchart.d.ts +178 -0
- package/dist/esm/Canvas/utils/Charts/horizontalbarchart.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Charts/horizontalbarchart.js +1389 -0
- package/dist/esm/Canvas/utils/Charts/horizontalbarchart.js.map +1 -0
- package/dist/esm/Canvas/utils/Charts/index.d.ts +45 -0
- package/dist/esm/Canvas/utils/Charts/index.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Charts/index.js +17 -0
- package/dist/esm/Canvas/utils/Charts/index.js.map +1 -0
- package/dist/esm/Canvas/utils/Charts/linechart.d.ts +216 -0
- package/dist/esm/Canvas/utils/Charts/linechart.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Charts/linechart.js +1761 -0
- package/dist/esm/Canvas/utils/Charts/linechart.js.map +1 -0
- package/dist/esm/Canvas/utils/Charts/piechart.d.ts +167 -0
- package/dist/esm/Canvas/utils/Charts/piechart.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Charts/piechart.js +794 -0
- package/dist/esm/Canvas/utils/Charts/piechart.js.map +1 -0
- package/dist/esm/Canvas/utils/General/batchOperations.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/General/batchOperations.js +3 -4
- package/dist/esm/Canvas/utils/General/batchOperations.js.map +1 -1
- package/dist/esm/Canvas/utils/General/general functions.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/General/general functions.js +62 -33
- package/dist/esm/Canvas/utils/General/general functions.js.map +1 -1
- package/dist/esm/Canvas/utils/General/imageStitching.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/General/imageStitching.js +3 -6
- package/dist/esm/Canvas/utils/General/imageStitching.js.map +1 -1
- package/dist/esm/Canvas/utils/Image/imageMasking.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/Image/imageMasking.js +5 -12
- package/dist/esm/Canvas/utils/Image/imageMasking.js.map +1 -1
- package/dist/esm/Canvas/utils/Image/imageProperties.d.ts +4 -4
- package/dist/esm/Canvas/utils/Image/imageProperties.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/Image/imageProperties.js +44 -9
- package/dist/esm/Canvas/utils/Image/imageProperties.js.map +1 -1
- package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.d.ts +5 -0
- package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.js +48 -5
- package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.js.map +1 -1
- package/dist/esm/Canvas/utils/Texts/textProperties.d.ts +1 -1
- package/dist/esm/Canvas/utils/Texts/textProperties.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/Texts/textProperties.js +48 -5
- package/dist/esm/Canvas/utils/Texts/textProperties.js.map +1 -1
- package/dist/esm/Canvas/utils/Video/videoHelpers.d.ts +489 -0
- package/dist/esm/Canvas/utils/Video/videoHelpers.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Video/videoHelpers.js +1835 -0
- package/dist/esm/Canvas/utils/Video/videoHelpers.js.map +1 -0
- package/dist/esm/Canvas/utils/errorUtils.d.ts +15 -0
- package/dist/esm/Canvas/utils/errorUtils.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/errorUtils.js +26 -0
- package/dist/esm/Canvas/utils/errorUtils.js.map +1 -0
- package/dist/esm/Canvas/utils/types.d.ts +17 -178
- 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 +4 -3
- package/dist/esm/Canvas/utils/utils.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/utils.js +40 -6
- package/dist/esm/Canvas/utils/utils.js.map +1 -1
- package/dist/esm/index.d.ts +1 -8
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +14 -45
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +234 -198
- package/dist/cjs/Canvas/utils/Charts/charts.d.ts +0 -13
- package/dist/cjs/Canvas/utils/Charts/charts.d.ts.map +0 -1
- package/dist/cjs/Canvas/utils/Charts/charts.js +0 -466
- package/dist/cjs/Canvas/utils/Charts/charts.js.map +0 -1
- package/dist/esm/Canvas/utils/Charts/charts.d.ts +0 -13
- package/dist/esm/Canvas/utils/Charts/charts.d.ts.map +0 -1
- package/dist/esm/Canvas/utils/Charts/charts.js +0 -466
- package/dist/esm/Canvas/utils/Charts/charts.js.map +0 -1
- package/lib/Canvas/ApexPainter.ts +0 -5414
- package/lib/Canvas/utils/Background/bg.ts +0 -285
- package/lib/Canvas/utils/Charts/charts.ts +0 -548
- package/lib/Canvas/utils/Custom/advancedLines.ts +0 -387
- package/lib/Canvas/utils/Custom/customLines.ts +0 -206
- package/lib/Canvas/utils/General/batchOperations.ts +0 -103
- package/lib/Canvas/utils/General/conversion.ts +0 -34
- package/lib/Canvas/utils/General/general functions.ts +0 -726
- package/lib/Canvas/utils/General/imageCompression.ts +0 -316
- package/lib/Canvas/utils/General/imageStitching.ts +0 -252
- package/lib/Canvas/utils/Image/imageEffects.ts +0 -175
- package/lib/Canvas/utils/Image/imageFilters.ts +0 -356
- package/lib/Canvas/utils/Image/imageMasking.ts +0 -335
- package/lib/Canvas/utils/Image/imageProperties.ts +0 -587
- package/lib/Canvas/utils/Image/professionalImageFilters.ts +0 -391
- package/lib/Canvas/utils/Image/simpleProfessionalFilters.ts +0 -229
- package/lib/Canvas/utils/Patterns/enhancedPatternRenderer.ts +0 -455
- package/lib/Canvas/utils/Shapes/shapes.ts +0 -528
- package/lib/Canvas/utils/Texts/enhancedTextRenderer.ts +0 -716
- package/lib/Canvas/utils/Texts/textPathRenderer.ts +0 -320
- package/lib/Canvas/utils/Texts/textProperties.ts +0 -231
- package/lib/Canvas/utils/types.ts +0 -983
- package/lib/Canvas/utils/utils.ts +0 -135
- package/lib/index.ts +0 -81
- package/lib/utils.ts +0 -5
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
import sharp from 'sharp';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { CompressionOptions, PaletteOptions } from '../types';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Compresses an image with quality control
|
|
7
|
-
* @param image - Image source (path, URL, or Buffer)
|
|
8
|
-
* @param options - Compression options
|
|
9
|
-
* @returns Compressed image buffer
|
|
10
|
-
*/
|
|
11
|
-
export async function compressImage(
|
|
12
|
-
image: string | Buffer,
|
|
13
|
-
options: CompressionOptions = {}
|
|
14
|
-
): Promise<Buffer> {
|
|
15
|
-
const {
|
|
16
|
-
quality = 90,
|
|
17
|
-
format = 'jpeg',
|
|
18
|
-
maxWidth,
|
|
19
|
-
maxHeight,
|
|
20
|
-
progressive = false
|
|
21
|
-
} = options;
|
|
22
|
-
|
|
23
|
-
let sharpImage: sharp.Sharp;
|
|
24
|
-
|
|
25
|
-
if (Buffer.isBuffer(image)) {
|
|
26
|
-
sharpImage = sharp(image);
|
|
27
|
-
} else if (typeof image === 'string' && image.startsWith('http')) {
|
|
28
|
-
const response = await fetch(image);
|
|
29
|
-
const buffer = await response.arrayBuffer();
|
|
30
|
-
sharpImage = sharp(Buffer.from(buffer));
|
|
31
|
-
} else {
|
|
32
|
-
const imagePath = path.join(process.cwd(), image);
|
|
33
|
-
sharpImage = sharp(imagePath);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Resize if needed
|
|
37
|
-
if (maxWidth || maxHeight) {
|
|
38
|
-
sharpImage = sharpImage.resize(maxWidth, maxHeight, {
|
|
39
|
-
fit: 'inside',
|
|
40
|
-
withoutEnlargement: true
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Convert and compress
|
|
45
|
-
switch (format) {
|
|
46
|
-
case 'jpeg':
|
|
47
|
-
return await sharpImage
|
|
48
|
-
.jpeg({ quality, progressive })
|
|
49
|
-
.toBuffer();
|
|
50
|
-
|
|
51
|
-
case 'webp':
|
|
52
|
-
return await sharpImage
|
|
53
|
-
.webp({ quality })
|
|
54
|
-
.toBuffer();
|
|
55
|
-
|
|
56
|
-
case 'avif':
|
|
57
|
-
return await sharpImage
|
|
58
|
-
.avif({ quality })
|
|
59
|
-
.toBuffer();
|
|
60
|
-
|
|
61
|
-
default:
|
|
62
|
-
return await sharpImage
|
|
63
|
-
.jpeg({ quality, progressive })
|
|
64
|
-
.toBuffer();
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Extracts color palette from an image
|
|
70
|
-
* @param image - Image source (path, URL, or Buffer)
|
|
71
|
-
* @param options - Palette extraction options
|
|
72
|
-
* @returns Array of colors with percentages
|
|
73
|
-
*/
|
|
74
|
-
export async function extractPalette(
|
|
75
|
-
image: string | Buffer,
|
|
76
|
-
options: PaletteOptions = {}
|
|
77
|
-
): Promise<Array<{ color: string; percentage: number }>> {
|
|
78
|
-
const {
|
|
79
|
-
count = 10,
|
|
80
|
-
method = 'kmeans',
|
|
81
|
-
format = 'hex'
|
|
82
|
-
} = options;
|
|
83
|
-
|
|
84
|
-
let sharpImage: sharp.Sharp;
|
|
85
|
-
|
|
86
|
-
if (Buffer.isBuffer(image)) {
|
|
87
|
-
sharpImage = sharp(image);
|
|
88
|
-
} else if (typeof image === 'string' && image.startsWith('http')) {
|
|
89
|
-
const response = await fetch(image);
|
|
90
|
-
const buffer = await response.arrayBuffer();
|
|
91
|
-
sharpImage = sharp(Buffer.from(buffer));
|
|
92
|
-
} else {
|
|
93
|
-
const imagePath = path.join(process.cwd(), image);
|
|
94
|
-
sharpImage = sharp(imagePath);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Resize for faster processing
|
|
98
|
-
const { data, info } = await sharpImage
|
|
99
|
-
.resize(200, 200, { fit: 'inside' })
|
|
100
|
-
.raw()
|
|
101
|
-
.toBuffer({ resolveWithObject: true });
|
|
102
|
-
|
|
103
|
-
const pixels: Array<{ r: number; g: number; b: number }> = [];
|
|
104
|
-
for (let i = 0; i < data.length; i += info.channels) {
|
|
105
|
-
pixels.push({
|
|
106
|
-
r: data[i],
|
|
107
|
-
g: data[i + 1],
|
|
108
|
-
b: data[i + 2]
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Extract colors based on method
|
|
113
|
-
let colors: Array<{ r: number; g: number; b: number; count: number }> = [];
|
|
114
|
-
|
|
115
|
-
if (method === 'median-cut') {
|
|
116
|
-
colors = medianCut(pixels, count);
|
|
117
|
-
} else if (method === 'octree') {
|
|
118
|
-
colors = octreeQuantization(pixels, count);
|
|
119
|
-
} else {
|
|
120
|
-
// kmeans (default)
|
|
121
|
-
colors = kmeansClustering(pixels, count);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Convert to requested format and calculate percentages
|
|
125
|
-
const totalPixels = pixels.length;
|
|
126
|
-
const palette = colors.map(color => {
|
|
127
|
-
let colorString: string;
|
|
128
|
-
|
|
129
|
-
if (format === 'hex') {
|
|
130
|
-
colorString = `#${[color.r, color.g, color.b].map(c =>
|
|
131
|
-
c.toString(16).padStart(2, '0')
|
|
132
|
-
).join('')}`;
|
|
133
|
-
} else if (format === 'rgb') {
|
|
134
|
-
colorString = `rgb(${color.r}, ${color.g}, ${color.b})`;
|
|
135
|
-
} else {
|
|
136
|
-
// hsl
|
|
137
|
-
const hsl = rgbToHsl(color.r, color.g, color.b);
|
|
138
|
-
colorString = `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return {
|
|
142
|
-
color: colorString,
|
|
143
|
-
percentage: (color.count / totalPixels) * 100
|
|
144
|
-
};
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// Sort by percentage descending
|
|
148
|
-
return palette.sort((a, b) => b.percentage - a.percentage);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* K-means clustering for color extraction
|
|
153
|
-
*/
|
|
154
|
-
function kmeansClustering(
|
|
155
|
-
pixels: Array<{ r: number; g: number; b: number }>,
|
|
156
|
-
k: number
|
|
157
|
-
): Array<{ r: number; g: number; b: number; count: number }> {
|
|
158
|
-
// Initialize centroids randomly
|
|
159
|
-
const centroids: Array<{ r: number; g: number; b: number }> = [];
|
|
160
|
-
for (let i = 0; i < k; i++) {
|
|
161
|
-
const randomPixel = pixels[Math.floor(Math.random() * pixels.length)];
|
|
162
|
-
centroids.push({ r: randomPixel.r, g: randomPixel.g, b: randomPixel.b });
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Iterate
|
|
166
|
-
for (let iter = 0; iter < 10; iter++) {
|
|
167
|
-
const clusters: Array<Array<{ r: number; g: number; b: number }>> =
|
|
168
|
-
new Array(k).fill(null).map(() => []);
|
|
169
|
-
|
|
170
|
-
// Assign pixels to nearest centroid
|
|
171
|
-
for (const pixel of pixels) {
|
|
172
|
-
let minDist = Infinity;
|
|
173
|
-
let nearestCluster = 0;
|
|
174
|
-
|
|
175
|
-
for (let i = 0; i < centroids.length; i++) {
|
|
176
|
-
const dist = Math.sqrt(
|
|
177
|
-
Math.pow(pixel.r - centroids[i].r, 2) +
|
|
178
|
-
Math.pow(pixel.g - centroids[i].g, 2) +
|
|
179
|
-
Math.pow(pixel.b - centroids[i].b, 2)
|
|
180
|
-
);
|
|
181
|
-
if (dist < minDist) {
|
|
182
|
-
minDist = dist;
|
|
183
|
-
nearestCluster = i;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
clusters[nearestCluster].push(pixel);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Update centroids
|
|
190
|
-
for (let i = 0; i < k; i++) {
|
|
191
|
-
if (clusters[i].length > 0) {
|
|
192
|
-
const avgR = clusters[i].reduce((sum, p) => sum + p.r, 0) / clusters[i].length;
|
|
193
|
-
const avgG = clusters[i].reduce((sum, p) => sum + p.g, 0) / clusters[i].length;
|
|
194
|
-
const avgB = clusters[i].reduce((sum, p) => sum + p.b, 0) / clusters[i].length;
|
|
195
|
-
centroids[i] = { r: Math.round(avgR), g: Math.round(avgG), b: Math.round(avgB) };
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Count pixels in each cluster
|
|
201
|
-
const counts: number[] = new Array(k).fill(0);
|
|
202
|
-
for (const pixel of pixels) {
|
|
203
|
-
let minDist = Infinity;
|
|
204
|
-
let nearestCluster = 0;
|
|
205
|
-
|
|
206
|
-
for (let i = 0; i < centroids.length; i++) {
|
|
207
|
-
const dist = Math.sqrt(
|
|
208
|
-
Math.pow(pixel.r - centroids[i].r, 2) +
|
|
209
|
-
Math.pow(pixel.g - centroids[i].g, 2) +
|
|
210
|
-
Math.pow(pixel.b - centroids[i].b, 2)
|
|
211
|
-
);
|
|
212
|
-
if (dist < minDist) {
|
|
213
|
-
minDist = dist;
|
|
214
|
-
nearestCluster = i;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
counts[nearestCluster]++;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return centroids.map((centroid, i) => ({
|
|
221
|
-
...centroid,
|
|
222
|
-
count: counts[i]
|
|
223
|
-
})).filter(c => c.count > 0);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Median cut algorithm for color extraction
|
|
228
|
-
*/
|
|
229
|
-
function medianCut(
|
|
230
|
-
pixels: Array<{ r: number; g: number; b: number }>,
|
|
231
|
-
count: number
|
|
232
|
-
): Array<{ r: number; g: number; b: number; count: number }> {
|
|
233
|
-
// Simplified median cut - divide color space
|
|
234
|
-
const buckets: Array<Array<{ r: number; g: number; b: number }>> = [pixels];
|
|
235
|
-
|
|
236
|
-
while (buckets.length < count && buckets.length < 8) {
|
|
237
|
-
const largestBucket = buckets.reduce((max, bucket, i) =>
|
|
238
|
-
bucket.length > buckets[max].length ? i : max, 0
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
const bucket = buckets[largestBucket];
|
|
242
|
-
if (bucket.length <= 1) break;
|
|
243
|
-
|
|
244
|
-
// Find color channel with largest range
|
|
245
|
-
const ranges = {
|
|
246
|
-
r: Math.max(...bucket.map(p => p.r)) - Math.min(...bucket.map(p => p.r)),
|
|
247
|
-
g: Math.max(...bucket.map(p => p.g)) - Math.min(...bucket.map(p => p.g)),
|
|
248
|
-
b: Math.max(...bucket.map(p => p.b)) - Math.min(...bucket.map(p => p.b))
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
const channel = ranges.r > ranges.g && ranges.r > ranges.b ? 'r' :
|
|
252
|
-
ranges.g > ranges.b ? 'g' : 'b';
|
|
253
|
-
|
|
254
|
-
// Sort by channel and split at median
|
|
255
|
-
bucket.sort((a, b) => a[channel] - b[channel]);
|
|
256
|
-
const median = Math.floor(bucket.length / 2);
|
|
257
|
-
|
|
258
|
-
buckets.splice(largestBucket, 1, bucket.slice(0, median), bucket.slice(median));
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Calculate average color for each bucket
|
|
262
|
-
return buckets.map(bucket => {
|
|
263
|
-
const avgR = Math.round(bucket.reduce((sum, p) => sum + p.r, 0) / bucket.length);
|
|
264
|
-
const avgG = Math.round(bucket.reduce((sum, p) => sum + p.g, 0) / bucket.length);
|
|
265
|
-
const avgB = Math.round(bucket.reduce((sum, p) => sum + p.b, 0) / bucket.length);
|
|
266
|
-
return {
|
|
267
|
-
r: avgR,
|
|
268
|
-
g: avgG,
|
|
269
|
-
b: avgB,
|
|
270
|
-
count: bucket.length
|
|
271
|
-
};
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Octree quantization (simplified)
|
|
277
|
-
*/
|
|
278
|
-
function octreeQuantization(
|
|
279
|
-
pixels: Array<{ r: number; g: number; b: number }>,
|
|
280
|
-
count: number
|
|
281
|
-
): Array<{ r: number; g: number; b: number; count: number }> {
|
|
282
|
-
// Simplified octree - use kmeans as fallback
|
|
283
|
-
return kmeansClustering(pixels, count);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Converts RGB to HSL
|
|
288
|
-
*/
|
|
289
|
-
function rgbToHsl(r: number, g: number, b: number): { h: number; s: number; l: number } {
|
|
290
|
-
r /= 255;
|
|
291
|
-
g /= 255;
|
|
292
|
-
b /= 255;
|
|
293
|
-
|
|
294
|
-
const max = Math.max(r, g, b);
|
|
295
|
-
const min = Math.min(r, g, b);
|
|
296
|
-
let h = 0, s = 0;
|
|
297
|
-
const l = (max + min) / 2;
|
|
298
|
-
|
|
299
|
-
if (max !== min) {
|
|
300
|
-
const d = max - min;
|
|
301
|
-
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
302
|
-
|
|
303
|
-
switch (max) {
|
|
304
|
-
case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
|
|
305
|
-
case g: h = ((b - r) / d + 2) / 6; break;
|
|
306
|
-
case b: h = ((r - g) / d + 4) / 6; break;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
return {
|
|
311
|
-
h: Math.round(h * 360),
|
|
312
|
-
s: Math.round(s * 100),
|
|
313
|
-
l: Math.round(l * 100)
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
|
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import { createCanvas, loadImage, SKRSContext2D, Image } from '@napi-rs/canvas';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import { StitchOptions, CollageLayout } from '../types';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Stitches multiple images together
|
|
8
|
-
* @param images - Array of image sources
|
|
9
|
-
* @param options - Stitching options
|
|
10
|
-
* @returns Stitched image buffer
|
|
11
|
-
*/
|
|
12
|
-
export async function stitchImages(
|
|
13
|
-
images: Array<string | Buffer>,
|
|
14
|
-
options: StitchOptions = {}
|
|
15
|
-
): Promise<Buffer> {
|
|
16
|
-
if (!images || images.length === 0) {
|
|
17
|
-
throw new Error('stitchImages: images array is required');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const {
|
|
21
|
-
direction = 'horizontal',
|
|
22
|
-
overlap = 0,
|
|
23
|
-
blend = false,
|
|
24
|
-
spacing = 0
|
|
25
|
-
} = options;
|
|
26
|
-
|
|
27
|
-
// Load all images
|
|
28
|
-
const loadedImages: Image[] = [];
|
|
29
|
-
for (const imgSource of images) {
|
|
30
|
-
let img: Image;
|
|
31
|
-
if (Buffer.isBuffer(imgSource)) {
|
|
32
|
-
img = await loadImage(imgSource);
|
|
33
|
-
} else if (imgSource.startsWith('http')) {
|
|
34
|
-
img = await loadImage(imgSource);
|
|
35
|
-
} else {
|
|
36
|
-
const imgPath = path.join(process.cwd(), imgSource);
|
|
37
|
-
img = await loadImage(fs.readFileSync(imgPath));
|
|
38
|
-
}
|
|
39
|
-
loadedImages.push(img);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (loadedImages.length === 0) {
|
|
43
|
-
throw new Error('stitchImages: No valid images loaded');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Calculate canvas dimensions
|
|
47
|
-
let canvasWidth = 0;
|
|
48
|
-
let canvasHeight = 0;
|
|
49
|
-
let maxWidth = 0;
|
|
50
|
-
let maxHeight = 0;
|
|
51
|
-
|
|
52
|
-
for (const img of loadedImages) {
|
|
53
|
-
maxWidth = Math.max(maxWidth, img.width);
|
|
54
|
-
maxHeight = Math.max(maxHeight, img.height);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (direction === 'horizontal') {
|
|
58
|
-
canvasWidth = loadedImages.reduce((sum, img) => sum + img.width, 0);
|
|
59
|
-
canvasWidth -= overlap * (loadedImages.length - 1);
|
|
60
|
-
canvasWidth += spacing * (loadedImages.length - 1);
|
|
61
|
-
canvasHeight = maxHeight;
|
|
62
|
-
} else if (direction === 'vertical') {
|
|
63
|
-
canvasWidth = maxWidth;
|
|
64
|
-
canvasHeight = loadedImages.reduce((sum, img) => sum + img.height, 0);
|
|
65
|
-
canvasHeight -= overlap * (loadedImages.length - 1);
|
|
66
|
-
canvasHeight += spacing * (loadedImages.length - 1);
|
|
67
|
-
} else if (direction === 'grid') {
|
|
68
|
-
const cols = Math.ceil(Math.sqrt(loadedImages.length));
|
|
69
|
-
const rows = Math.ceil(loadedImages.length / cols);
|
|
70
|
-
canvasWidth = maxWidth * cols + spacing * (cols - 1);
|
|
71
|
-
canvasHeight = maxHeight * rows + spacing * (rows - 1);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Create canvas
|
|
75
|
-
const canvas = createCanvas(canvasWidth, canvasHeight);
|
|
76
|
-
const ctx = canvas.getContext('2d') as SKRSContext2D;
|
|
77
|
-
if (!ctx) throw new Error("Unable to get 2D context");
|
|
78
|
-
|
|
79
|
-
// Draw images
|
|
80
|
-
let currentX = 0;
|
|
81
|
-
let currentY = 0;
|
|
82
|
-
|
|
83
|
-
for (let i = 0; i < loadedImages.length; i++) {
|
|
84
|
-
const img = loadedImages[i];
|
|
85
|
-
|
|
86
|
-
if (direction === 'horizontal') {
|
|
87
|
-
if (i > 0) {
|
|
88
|
-
currentX -= overlap;
|
|
89
|
-
currentX += spacing;
|
|
90
|
-
}
|
|
91
|
-
ctx.drawImage(img, currentX, 0, img.width, img.height);
|
|
92
|
-
currentX += img.width;
|
|
93
|
-
} else if (direction === 'vertical') {
|
|
94
|
-
if (i > 0) {
|
|
95
|
-
currentY -= overlap;
|
|
96
|
-
currentY += spacing;
|
|
97
|
-
}
|
|
98
|
-
ctx.drawImage(img, 0, currentY, img.width, img.height);
|
|
99
|
-
currentY += img.height;
|
|
100
|
-
} else if (direction === 'grid') {
|
|
101
|
-
const cols = Math.ceil(Math.sqrt(loadedImages.length));
|
|
102
|
-
const col = i % cols;
|
|
103
|
-
const row = Math.floor(i / cols);
|
|
104
|
-
const x = col * (maxWidth + spacing);
|
|
105
|
-
const y = row * (maxHeight + spacing);
|
|
106
|
-
ctx.drawImage(img, x, y, img.width, img.height);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Apply blending if enabled and not first image
|
|
110
|
-
if (blend && i > 0 && overlap > 0) {
|
|
111
|
-
ctx.globalCompositeOperation = 'multiply';
|
|
112
|
-
ctx.globalAlpha = 0.5;
|
|
113
|
-
if (direction === 'horizontal') {
|
|
114
|
-
ctx.drawImage(img, currentX - img.width - spacing + overlap, 0, img.width, img.height);
|
|
115
|
-
} else if (direction === 'vertical') {
|
|
116
|
-
ctx.drawImage(img, 0, currentY - img.height - spacing + overlap, img.width, img.height);
|
|
117
|
-
}
|
|
118
|
-
ctx.globalAlpha = 1;
|
|
119
|
-
ctx.globalCompositeOperation = 'source-over';
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return canvas.toBuffer('image/png');
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Creates an image collage
|
|
128
|
-
* @param images - Array of image sources with optional dimensions
|
|
129
|
-
* @param layout - Collage layout configuration
|
|
130
|
-
* @returns Collage image buffer
|
|
131
|
-
*/
|
|
132
|
-
export async function createCollage(
|
|
133
|
-
images: Array<{ source: string | Buffer; width?: number; height?: number }>,
|
|
134
|
-
layout: CollageLayout
|
|
135
|
-
): Promise<Buffer> {
|
|
136
|
-
if (!images || images.length === 0) {
|
|
137
|
-
throw new Error('createCollage: images array is required');
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const {
|
|
141
|
-
type = 'grid',
|
|
142
|
-
columns = 3,
|
|
143
|
-
rows = 3,
|
|
144
|
-
spacing = 10,
|
|
145
|
-
background = '#ffffff',
|
|
146
|
-
borderRadius = 0
|
|
147
|
-
} = layout;
|
|
148
|
-
|
|
149
|
-
// Load all images
|
|
150
|
-
const loadedImages: Array<{ image: Image; width: number; height: number }> = [];
|
|
151
|
-
for (const imgConfig of images) {
|
|
152
|
-
let img: Image;
|
|
153
|
-
if (Buffer.isBuffer(imgConfig.source)) {
|
|
154
|
-
img = await loadImage(imgConfig.source);
|
|
155
|
-
} else if (typeof imgConfig.source === 'string' && imgConfig.source.startsWith('http')) {
|
|
156
|
-
img = await loadImage(imgConfig.source);
|
|
157
|
-
} else {
|
|
158
|
-
const imgPath = path.join(process.cwd(), imgConfig.source as string);
|
|
159
|
-
img = await loadImage(fs.readFileSync(imgPath));
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
loadedImages.push({
|
|
163
|
-
image: img,
|
|
164
|
-
width: imgConfig.width || img.width,
|
|
165
|
-
height: imgConfig.height || img.height
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Calculate canvas dimensions
|
|
170
|
-
let canvasWidth = 0;
|
|
171
|
-
let canvasHeight = 0;
|
|
172
|
-
|
|
173
|
-
if (type === 'grid') {
|
|
174
|
-
const cellWidth = Math.max(...loadedImages.map(img => img.width));
|
|
175
|
-
const cellHeight = Math.max(...loadedImages.map(img => img.height));
|
|
176
|
-
canvasWidth = cellWidth * columns + spacing * (columns - 1);
|
|
177
|
-
canvasHeight = cellHeight * rows + spacing * (rows - 1);
|
|
178
|
-
} else if (type === 'masonry') {
|
|
179
|
-
// Masonry layout - columns with varying heights
|
|
180
|
-
const colWidths: number[] = new Array(columns).fill(0);
|
|
181
|
-
const colHeights: number[] = new Array(columns).fill(0);
|
|
182
|
-
|
|
183
|
-
for (let i = 0; i < loadedImages.length; i++) {
|
|
184
|
-
const col = i % columns;
|
|
185
|
-
colWidths[col] = Math.max(colWidths[col], loadedImages[i].width);
|
|
186
|
-
colHeights[col] += loadedImages[i].height + (i >= columns ? spacing : 0);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
canvasWidth = Math.max(...colWidths) * columns + spacing * (columns - 1);
|
|
190
|
-
canvasHeight = Math.max(...colHeights);
|
|
191
|
-
} else if (type === 'carousel') {
|
|
192
|
-
// Horizontal carousel
|
|
193
|
-
canvasWidth = loadedImages.reduce((sum, img) => sum + img.width, 0) + spacing * (loadedImages.length - 1);
|
|
194
|
-
canvasHeight = Math.max(...loadedImages.map(img => img.height));
|
|
195
|
-
} else {
|
|
196
|
-
// Custom - use provided dimensions or calculate
|
|
197
|
-
canvasWidth = 800;
|
|
198
|
-
canvasHeight = 600;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Create canvas
|
|
202
|
-
const canvas = createCanvas(canvasWidth, canvasHeight);
|
|
203
|
-
const ctx = canvas.getContext('2d') as SKRSContext2D;
|
|
204
|
-
if (!ctx) throw new Error("Unable to get 2D context");
|
|
205
|
-
|
|
206
|
-
// Draw background
|
|
207
|
-
ctx.fillStyle = background;
|
|
208
|
-
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
|
209
|
-
|
|
210
|
-
// Draw images
|
|
211
|
-
let currentX = 0;
|
|
212
|
-
let currentY = 0;
|
|
213
|
-
const colHeights: number[] = new Array(columns).fill(0);
|
|
214
|
-
|
|
215
|
-
for (let i = 0; i < loadedImages.length; i++) {
|
|
216
|
-
const imgData = loadedImages[i];
|
|
217
|
-
|
|
218
|
-
if (type === 'grid') {
|
|
219
|
-
const col = i % columns;
|
|
220
|
-
const row = Math.floor(i / columns);
|
|
221
|
-
const cellWidth = Math.max(...loadedImages.map(img => img.width));
|
|
222
|
-
const cellHeight = Math.max(...loadedImages.map(img => img.height));
|
|
223
|
-
currentX = col * (cellWidth + spacing);
|
|
224
|
-
currentY = row * (cellHeight + spacing);
|
|
225
|
-
} else if (type === 'masonry') {
|
|
226
|
-
const col = i % columns;
|
|
227
|
-
currentX = col * (Math.max(...loadedImages.map(img => img.width)) + spacing);
|
|
228
|
-
currentY = colHeights[col];
|
|
229
|
-
colHeights[col] += imgData.height + spacing;
|
|
230
|
-
} else if (type === 'carousel') {
|
|
231
|
-
if (i > 0) currentX += spacing;
|
|
232
|
-
currentY = (canvasHeight - imgData.height) / 2;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Apply border radius if specified
|
|
236
|
-
if (borderRadius > 0) {
|
|
237
|
-
ctx.save();
|
|
238
|
-
ctx.beginPath();
|
|
239
|
-
ctx.roundRect(currentX, currentY, imgData.width, imgData.height, borderRadius);
|
|
240
|
-
ctx.clip();
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
ctx.drawImage(imgData.image, currentX, currentY, imgData.width, imgData.height);
|
|
244
|
-
|
|
245
|
-
if (borderRadius > 0) {
|
|
246
|
-
ctx.restore();
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return canvas.toBuffer('image/png');
|
|
251
|
-
}
|
|
252
|
-
|