apexify.js 4.9.25 → 4.9.27

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 (198) hide show
  1. package/README.md +358 -47
  2. package/dist/cjs/Canvas/ApexPainter.d.ts +189 -0
  3. package/dist/cjs/Canvas/ApexPainter.d.ts.map +1 -0
  4. package/dist/{esm/canvas → cjs/Canvas}/ApexPainter.js +461 -352
  5. package/dist/cjs/Canvas/ApexPainter.js.map +1 -0
  6. package/dist/cjs/Canvas/utils/Background/bg.d.ts +43 -0
  7. package/dist/cjs/Canvas/utils/Background/bg.d.ts.map +1 -0
  8. package/dist/cjs/Canvas/utils/Background/bg.js +228 -0
  9. package/dist/cjs/Canvas/utils/Background/bg.js.map +1 -0
  10. package/dist/cjs/{canvas → Canvas}/utils/Charts/charts.d.ts.map +1 -1
  11. package/dist/{esm/canvas → cjs/Canvas}/utils/Charts/charts.js.map +1 -1
  12. package/dist/cjs/{canvas → Canvas}/utils/Custom/customLines.d.ts.map +1 -1
  13. package/dist/{esm/canvas → cjs/Canvas}/utils/Custom/customLines.js +2 -2
  14. package/dist/cjs/Canvas/utils/Custom/customLines.js.map +1 -0
  15. package/dist/cjs/{canvas → Canvas}/utils/General/conversion.d.ts.map +1 -1
  16. package/dist/cjs/{canvas → Canvas}/utils/General/conversion.js.map +1 -1
  17. package/dist/cjs/{canvas → Canvas}/utils/General/general functions.d.ts.map +1 -1
  18. package/dist/{esm/canvas → cjs/Canvas}/utils/General/general functions.js.map +1 -1
  19. package/dist/cjs/Canvas/utils/Image/imageFilters.d.ts +11 -0
  20. package/dist/cjs/Canvas/utils/Image/imageFilters.d.ts.map +1 -0
  21. package/dist/cjs/Canvas/utils/Image/imageFilters.js +307 -0
  22. package/dist/cjs/Canvas/utils/Image/imageFilters.js.map +1 -0
  23. package/dist/cjs/Canvas/utils/Image/imageProperties.d.ts +50 -0
  24. package/dist/cjs/Canvas/utils/Image/imageProperties.d.ts.map +1 -0
  25. package/dist/cjs/Canvas/utils/Image/imageProperties.js +271 -0
  26. package/dist/cjs/Canvas/utils/Image/imageProperties.js.map +1 -0
  27. package/dist/cjs/Canvas/utils/Image/professionalImageFilters.d.ts +11 -0
  28. package/dist/cjs/Canvas/utils/Image/professionalImageFilters.d.ts.map +1 -0
  29. package/dist/cjs/Canvas/utils/Image/professionalImageFilters.js +351 -0
  30. package/dist/cjs/Canvas/utils/Image/professionalImageFilters.js.map +1 -0
  31. package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.d.ts +11 -0
  32. package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.d.ts.map +1 -0
  33. package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.js +215 -0
  34. package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.js.map +1 -0
  35. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts +71 -0
  36. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts.map +1 -0
  37. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.js +392 -0
  38. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.js.map +1 -0
  39. package/dist/cjs/Canvas/utils/Shapes/shapes.d.ts +29 -0
  40. package/dist/cjs/Canvas/utils/Shapes/shapes.d.ts.map +1 -0
  41. package/dist/cjs/Canvas/utils/Shapes/shapes.js +334 -0
  42. package/dist/cjs/Canvas/utils/Shapes/shapes.js.map +1 -0
  43. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.d.ts +127 -0
  44. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.d.ts.map +1 -0
  45. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.js +365 -0
  46. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.js.map +1 -0
  47. package/dist/cjs/{canvas → Canvas}/utils/Texts/textProperties.d.ts.map +1 -1
  48. package/dist/{esm/canvas → cjs/Canvas}/utils/Texts/textProperties.js.map +1 -1
  49. package/dist/{esm/canvas → cjs/Canvas}/utils/types.d.ts +227 -131
  50. package/dist/cjs/Canvas/utils/types.d.ts.map +1 -0
  51. package/dist/cjs/{canvas → Canvas}/utils/types.js +0 -1
  52. package/dist/cjs/Canvas/utils/types.js.map +1 -0
  53. package/dist/cjs/Canvas/utils/utils.d.ts +22 -0
  54. package/dist/cjs/Canvas/utils/utils.d.ts.map +1 -0
  55. package/dist/{esm/canvas → cjs/Canvas}/utils/utils.js +17 -7
  56. package/dist/cjs/Canvas/utils/utils.js.map +1 -0
  57. package/dist/cjs/index.d.ts +6 -3
  58. package/dist/cjs/index.d.ts.map +1 -1
  59. package/dist/cjs/index.js +8 -6
  60. package/dist/cjs/index.js.map +1 -1
  61. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  62. package/dist/cjs/utils.d.ts +1 -1
  63. package/dist/cjs/utils.js +1 -1
  64. package/dist/esm/Canvas/ApexPainter.d.ts +189 -0
  65. package/dist/esm/Canvas/ApexPainter.d.ts.map +1 -0
  66. package/dist/{cjs/canvas → esm/Canvas}/ApexPainter.js +461 -352
  67. package/dist/esm/Canvas/ApexPainter.js.map +1 -0
  68. package/dist/esm/Canvas/utils/Background/bg.d.ts +43 -0
  69. package/dist/esm/Canvas/utils/Background/bg.d.ts.map +1 -0
  70. package/dist/esm/Canvas/utils/Background/bg.js +228 -0
  71. package/dist/esm/Canvas/utils/Background/bg.js.map +1 -0
  72. package/dist/esm/{canvas → Canvas}/utils/Charts/charts.d.ts.map +1 -1
  73. package/dist/{cjs/canvas → esm/Canvas}/utils/Charts/charts.js.map +1 -1
  74. package/dist/esm/{canvas → Canvas}/utils/Custom/customLines.d.ts.map +1 -1
  75. package/dist/{cjs/canvas → esm/Canvas}/utils/Custom/customLines.js +2 -2
  76. package/dist/esm/Canvas/utils/Custom/customLines.js.map +1 -0
  77. package/dist/esm/{canvas → Canvas}/utils/General/conversion.d.ts.map +1 -1
  78. package/dist/esm/{canvas → Canvas}/utils/General/conversion.js.map +1 -1
  79. package/dist/esm/{canvas → Canvas}/utils/General/general functions.d.ts.map +1 -1
  80. package/dist/{cjs/canvas → esm/Canvas}/utils/General/general functions.js.map +1 -1
  81. package/dist/esm/Canvas/utils/Image/imageFilters.d.ts +11 -0
  82. package/dist/esm/Canvas/utils/Image/imageFilters.d.ts.map +1 -0
  83. package/dist/esm/Canvas/utils/Image/imageFilters.js +307 -0
  84. package/dist/esm/Canvas/utils/Image/imageFilters.js.map +1 -0
  85. package/dist/esm/Canvas/utils/Image/imageProperties.d.ts +50 -0
  86. package/dist/esm/Canvas/utils/Image/imageProperties.d.ts.map +1 -0
  87. package/dist/esm/Canvas/utils/Image/imageProperties.js +271 -0
  88. package/dist/esm/Canvas/utils/Image/imageProperties.js.map +1 -0
  89. package/dist/esm/Canvas/utils/Image/professionalImageFilters.d.ts +11 -0
  90. package/dist/esm/Canvas/utils/Image/professionalImageFilters.d.ts.map +1 -0
  91. package/dist/esm/Canvas/utils/Image/professionalImageFilters.js +351 -0
  92. package/dist/esm/Canvas/utils/Image/professionalImageFilters.js.map +1 -0
  93. package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.d.ts +11 -0
  94. package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.d.ts.map +1 -0
  95. package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.js +215 -0
  96. package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.js.map +1 -0
  97. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts +71 -0
  98. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts.map +1 -0
  99. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.js +392 -0
  100. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.js.map +1 -0
  101. package/dist/esm/Canvas/utils/Shapes/shapes.d.ts +29 -0
  102. package/dist/esm/Canvas/utils/Shapes/shapes.d.ts.map +1 -0
  103. package/dist/esm/Canvas/utils/Shapes/shapes.js +334 -0
  104. package/dist/esm/Canvas/utils/Shapes/shapes.js.map +1 -0
  105. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.d.ts +127 -0
  106. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.d.ts.map +1 -0
  107. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.js +365 -0
  108. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.js.map +1 -0
  109. package/dist/esm/{canvas → Canvas}/utils/Texts/textProperties.d.ts.map +1 -1
  110. package/dist/{cjs/canvas → esm/Canvas}/utils/Texts/textProperties.js.map +1 -1
  111. package/dist/{cjs/canvas → esm/Canvas}/utils/types.d.ts +227 -131
  112. package/dist/esm/Canvas/utils/types.d.ts.map +1 -0
  113. package/dist/esm/{canvas → Canvas}/utils/types.js +0 -1
  114. package/dist/esm/Canvas/utils/types.js.map +1 -0
  115. package/dist/esm/Canvas/utils/utils.d.ts +22 -0
  116. package/dist/esm/Canvas/utils/utils.d.ts.map +1 -0
  117. package/dist/{cjs/canvas → esm/Canvas}/utils/utils.js +17 -7
  118. package/dist/esm/Canvas/utils/utils.js.map +1 -0
  119. package/dist/esm/index.d.ts +6 -3
  120. package/dist/esm/index.d.ts.map +1 -1
  121. package/dist/esm/index.js +8 -6
  122. package/dist/esm/index.js.map +1 -1
  123. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  124. package/dist/esm/utils.d.ts +1 -1
  125. package/dist/esm/utils.js +1 -1
  126. package/lib/{canvas → Canvas}/ApexPainter.ts +1325 -1218
  127. package/lib/Canvas/utils/Background/bg.ts +285 -0
  128. package/lib/{canvas → Canvas}/utils/Custom/customLines.ts +3 -3
  129. package/lib/Canvas/utils/Image/imageFilters.ts +356 -0
  130. package/lib/Canvas/utils/Image/imageProperties.ts +382 -0
  131. package/lib/Canvas/utils/Image/professionalImageFilters.ts +391 -0
  132. package/lib/Canvas/utils/Image/simpleProfessionalFilters.ts +229 -0
  133. package/lib/Canvas/utils/Patterns/enhancedPatternRenderer.ts +444 -0
  134. package/lib/Canvas/utils/Shapes/shapes.ts +528 -0
  135. package/lib/Canvas/utils/Texts/enhancedTextRenderer.ts +478 -0
  136. package/lib/{canvas → Canvas}/utils/types.ts +301 -117
  137. package/lib/{canvas → Canvas}/utils/utils.ts +85 -72
  138. package/lib/index.ts +8 -9
  139. package/lib/utils.ts +1 -1
  140. package/package.json +107 -191
  141. package/dist/cjs/canvas/ApexPainter.d.ts +0 -145
  142. package/dist/cjs/canvas/ApexPainter.d.ts.map +0 -1
  143. package/dist/cjs/canvas/ApexPainter.js.map +0 -1
  144. package/dist/cjs/canvas/utils/Background/bg.d.ts +0 -31
  145. package/dist/cjs/canvas/utils/Background/bg.d.ts.map +0 -1
  146. package/dist/cjs/canvas/utils/Background/bg.js +0 -161
  147. package/dist/cjs/canvas/utils/Background/bg.js.map +0 -1
  148. package/dist/cjs/canvas/utils/Custom/customLines.js.map +0 -1
  149. package/dist/cjs/canvas/utils/Image/imageProperties.d.ts +0 -115
  150. package/dist/cjs/canvas/utils/Image/imageProperties.d.ts.map +0 -1
  151. package/dist/cjs/canvas/utils/Image/imageProperties.js +0 -602
  152. package/dist/cjs/canvas/utils/Image/imageProperties.js.map +0 -1
  153. package/dist/cjs/canvas/utils/types.d.ts.map +0 -1
  154. package/dist/cjs/canvas/utils/types.js.map +0 -1
  155. package/dist/cjs/canvas/utils/utils.d.ts +0 -19
  156. package/dist/cjs/canvas/utils/utils.d.ts.map +0 -1
  157. package/dist/cjs/canvas/utils/utils.js.map +0 -1
  158. package/dist/esm/canvas/ApexPainter.d.ts +0 -145
  159. package/dist/esm/canvas/ApexPainter.d.ts.map +0 -1
  160. package/dist/esm/canvas/ApexPainter.js.map +0 -1
  161. package/dist/esm/canvas/utils/Background/bg.d.ts +0 -31
  162. package/dist/esm/canvas/utils/Background/bg.d.ts.map +0 -1
  163. package/dist/esm/canvas/utils/Background/bg.js +0 -161
  164. package/dist/esm/canvas/utils/Background/bg.js.map +0 -1
  165. package/dist/esm/canvas/utils/Custom/customLines.js.map +0 -1
  166. package/dist/esm/canvas/utils/Image/imageProperties.d.ts +0 -115
  167. package/dist/esm/canvas/utils/Image/imageProperties.d.ts.map +0 -1
  168. package/dist/esm/canvas/utils/Image/imageProperties.js +0 -602
  169. package/dist/esm/canvas/utils/Image/imageProperties.js.map +0 -1
  170. package/dist/esm/canvas/utils/types.d.ts.map +0 -1
  171. package/dist/esm/canvas/utils/types.js.map +0 -1
  172. package/dist/esm/canvas/utils/utils.d.ts +0 -19
  173. package/dist/esm/canvas/utils/utils.d.ts.map +0 -1
  174. package/dist/esm/canvas/utils/utils.js.map +0 -1
  175. package/lib/canvas/utils/Background/bg.ts +0 -211
  176. package/lib/canvas/utils/Image/imageProperties.ts +0 -835
  177. /package/dist/cjs/{canvas → Canvas}/utils/Charts/charts.d.ts +0 -0
  178. /package/dist/cjs/{canvas → Canvas}/utils/Charts/charts.js +0 -0
  179. /package/dist/cjs/{canvas → Canvas}/utils/Custom/customLines.d.ts +0 -0
  180. /package/dist/cjs/{canvas → Canvas}/utils/General/conversion.d.ts +0 -0
  181. /package/dist/cjs/{canvas → Canvas}/utils/General/conversion.js +0 -0
  182. /package/dist/cjs/{canvas → Canvas}/utils/General/general functions.d.ts +0 -0
  183. /package/dist/cjs/{canvas → Canvas}/utils/General/general functions.js +0 -0
  184. /package/dist/cjs/{canvas → Canvas}/utils/Texts/textProperties.d.ts +0 -0
  185. /package/dist/cjs/{canvas → Canvas}/utils/Texts/textProperties.js +0 -0
  186. /package/dist/esm/{canvas → Canvas}/utils/Charts/charts.d.ts +0 -0
  187. /package/dist/esm/{canvas → Canvas}/utils/Charts/charts.js +0 -0
  188. /package/dist/esm/{canvas → Canvas}/utils/Custom/customLines.d.ts +0 -0
  189. /package/dist/esm/{canvas → Canvas}/utils/General/conversion.d.ts +0 -0
  190. /package/dist/esm/{canvas → Canvas}/utils/General/conversion.js +0 -0
  191. /package/dist/esm/{canvas → Canvas}/utils/General/general functions.d.ts +0 -0
  192. /package/dist/esm/{canvas → Canvas}/utils/General/general functions.js +0 -0
  193. /package/dist/esm/{canvas → Canvas}/utils/Texts/textProperties.d.ts +0 -0
  194. /package/dist/esm/{canvas → Canvas}/utils/Texts/textProperties.js +0 -0
  195. /package/lib/{canvas → Canvas}/utils/Charts/charts.ts +0 -0
  196. /package/lib/{canvas → Canvas}/utils/General/conversion.ts +0 -0
  197. /package/lib/{canvas → Canvas}/utils/General/general functions.ts +0 -0
  198. /package/lib/{canvas → Canvas}/utils/Texts/textProperties.ts +0 -0
@@ -0,0 +1,391 @@
1
+ import { SKRSContext2D } from '@napi-rs/canvas';
2
+ import { ImageFilter } from '../types';
3
+ import sharp from 'sharp';
4
+ import { Jimp } from 'jimp';
5
+
6
+ /**
7
+ * Applies professional image filters using Sharp and Jimp
8
+ * @param ctx Canvas 2D context
9
+ * @param filters Array of filters to apply
10
+ * @param width Canvas width
11
+ * @param height Canvas height
12
+ */
13
+ export async function applyProfessionalImageFilters(
14
+ ctx: SKRSContext2D,
15
+ filters: ImageFilter[],
16
+ width: number,
17
+ height: number
18
+ ): Promise<void> {
19
+ if (!filters || filters.length === 0) return;
20
+
21
+ try {
22
+ // Get current canvas data
23
+ const imageData = ctx.getImageData(0, 0, width, height);
24
+ const buffer = Buffer.from(new Uint8Array(imageData.data.buffer));
25
+
26
+ // Convert to Sharp-compatible format
27
+ let sharpImage = sharp(buffer, {
28
+ raw: {
29
+ width: width,
30
+ height: height,
31
+ channels: 4
32
+ }
33
+ });
34
+
35
+ // Apply each filter using Sharp
36
+ for (const filter of filters) {
37
+ switch (filter.type) {
38
+ case 'gaussianBlur':
39
+ sharpImage = await applyGaussianBlurSharp(sharpImage, filter.intensity || 0);
40
+ break;
41
+ case 'motionBlur':
42
+ sharpImage = await applyMotionBlurSharp(sharpImage, filter.intensity || 0, filter.angle || 0);
43
+ break;
44
+ case 'radialBlur':
45
+ sharpImage = await applyRadialBlurSharp(sharpImage, filter.intensity || 0, filter.centerX || width/2, filter.centerY || height/2);
46
+ break;
47
+ case 'sharpen':
48
+ sharpImage = await applySharpenSharp(sharpImage, filter.intensity || 0);
49
+ break;
50
+ case 'brightness':
51
+ sharpImage = await applyBrightnessSharp(sharpImage, filter.value || 0);
52
+ break;
53
+ case 'contrast':
54
+ sharpImage = await applyContrastSharp(sharpImage, filter.value || 0);
55
+ break;
56
+ case 'saturation':
57
+ sharpImage = await applySaturationSharp(sharpImage, filter.value || 0);
58
+ break;
59
+ case 'hueShift':
60
+ sharpImage = await applyHueShiftSharp(sharpImage, filter.value || 0);
61
+ break;
62
+ case 'grayscale':
63
+ sharpImage = await applyGrayscaleSharp(sharpImage);
64
+ break;
65
+ case 'sepia':
66
+ sharpImage = await applySepiaSharp(sharpImage);
67
+ break;
68
+ case 'invert':
69
+ sharpImage = await applyInvertSharp(sharpImage);
70
+ break;
71
+ case 'posterize':
72
+ sharpImage = await applyPosterizeSharp(sharpImage, filter.levels || 4);
73
+ break;
74
+ case 'pixelate':
75
+ sharpImage = await applyPixelateSharp(sharpImage, filter.size || 10);
76
+ break;
77
+ case 'noise':
78
+ sharpImage = await applyNoiseSharp(sharpImage, filter.intensity || 0.1);
79
+ break;
80
+ case 'grain':
81
+ sharpImage = await applyGrainSharp(sharpImage, filter.intensity || 0.05);
82
+ break;
83
+ case 'edgeDetection':
84
+ sharpImage = await applyEdgeDetectionSharp(sharpImage, filter.intensity || 1);
85
+ break;
86
+ case 'emboss':
87
+ sharpImage = await applyEmbossSharp(sharpImage, filter.intensity || 1);
88
+ break;
89
+ }
90
+ }
91
+
92
+ // Convert back to canvas format
93
+ const { data } = await sharpImage.raw().toBuffer({ resolveWithObject: true });
94
+ const newImageData = new ImageData(new Uint8ClampedArray(data), width, height);
95
+ ctx.putImageData(newImageData, 0, 0);
96
+
97
+ } catch (error) {
98
+ console.error('Error applying professional filters:', error);
99
+ // Fallback to basic filters if Sharp fails
100
+ applyBasicFilters(ctx, filters, width, height);
101
+ }
102
+ }
103
+
104
+ // Sharp-based filter implementations
105
+ async function applyGaussianBlurSharp(image: sharp.Sharp, intensity: number): Promise<sharp.Sharp> {
106
+ if (intensity > 0) {
107
+ return image.blur(intensity);
108
+ }
109
+ return image;
110
+ }
111
+
112
+ async function applyMotionBlurSharp(image: sharp.Sharp, intensity: number, angle: number): Promise<sharp.Sharp> {
113
+ if (intensity > 0) {
114
+ // Motion blur using convolution
115
+ const kernel = createMotionBlurKernel(intensity, angle);
116
+ return image.convolve(kernel);
117
+ }
118
+ return image;
119
+ }
120
+
121
+ async function applyRadialBlurSharp(image: sharp.Sharp, intensity: number, centerX: number, centerY: number): Promise<sharp.Sharp> {
122
+ if (intensity > 0) {
123
+ // Radial blur using custom kernel
124
+ const kernel = createRadialBlurKernel(intensity, centerX, centerY);
125
+ return image.convolve(kernel);
126
+ }
127
+ return image;
128
+ }
129
+
130
+ async function applySharpenSharp(image: sharp.Sharp, intensity: number): Promise<sharp.Sharp> {
131
+ if (intensity > 0) {
132
+ return image.sharpen(intensity, 1, 2);
133
+ }
134
+ return image;
135
+ }
136
+
137
+ async function applyBrightnessSharp(image: sharp.Sharp, value: number): Promise<sharp.Sharp> {
138
+ if (value !== 0) {
139
+ const brightness = Math.max(0, Math.min(2, 1 + value / 100));
140
+ return image.modulate({ brightness });
141
+ }
142
+ return image;
143
+ }
144
+
145
+ async function applyContrastSharp(image: sharp.Sharp, value: number): Promise<sharp.Sharp> {
146
+ if (value !== 0) {
147
+ const contrast = Math.max(0, Math.min(2, 1 + value / 100));
148
+ return image.linear(contrast, -(128 * contrast) + 128);
149
+ }
150
+ return image;
151
+ }
152
+
153
+ async function applySaturationSharp(image: sharp.Sharp, value: number): Promise<sharp.Sharp> {
154
+ if (value !== 0) {
155
+ const saturation = Math.max(0, Math.min(2, 1 + value / 100));
156
+ return image.modulate({ saturation });
157
+ }
158
+ return image;
159
+ }
160
+
161
+ async function applyHueShiftSharp(image: sharp.Sharp, value: number): Promise<sharp.Sharp> {
162
+ if (value !== 0) {
163
+ return image.modulate({ hue: value });
164
+ }
165
+ return image;
166
+ }
167
+
168
+ async function applyGrayscaleSharp(image: sharp.Sharp): Promise<sharp.Sharp> {
169
+ return image.grayscale();
170
+ }
171
+
172
+ async function applySepiaSharp(image: sharp.Sharp): Promise<sharp.Sharp> {
173
+ return image.recomb([
174
+ [0.393, 0.769, 0.189],
175
+ [0.349, 0.686, 0.168],
176
+ [0.272, 0.534, 0.131]
177
+ ]);
178
+ }
179
+
180
+ async function applyInvertSharp(image: sharp.Sharp): Promise<sharp.Sharp> {
181
+ return image.negate();
182
+ }
183
+
184
+ async function applyPosterizeSharp(image: sharp.Sharp, levels: number): Promise<sharp.Sharp> {
185
+ if (levels > 1) {
186
+ const step = 255 / (levels - 1);
187
+ return image.threshold(128).modulate({ saturation: 0 });
188
+ }
189
+ return image;
190
+ }
191
+
192
+ async function applyPixelateSharp(image: sharp.Sharp, size: number): Promise<sharp.Sharp> {
193
+ if (size > 1) {
194
+ const { width, height } = await image.metadata();
195
+ const scale = Math.max(1, Math.floor(Math.min(width || 1, height || 1) / size));
196
+ return image.resize({ width: scale, height: scale, kernel: sharp.kernel.nearest })
197
+ .resize({ width: width, height: height, kernel: sharp.kernel.nearest });
198
+ }
199
+ return image;
200
+ }
201
+
202
+ async function applyNoiseSharp(image: sharp.Sharp, intensity: number): Promise<sharp.Sharp> {
203
+ if (intensity > 0) {
204
+ // Add noise using Jimp for better control
205
+ const buffer = await image.png().toBuffer();
206
+ const jimpImage = await Jimp.read(buffer);
207
+
208
+ jimpImage.scan(0, 0, jimpImage.width, jimpImage.height, function (this: any, x: number, y: number, idx: number) {
209
+ const noise = (Math.random() - 0.5) * intensity * 255;
210
+ this.bitmap.data[idx] = Math.max(0, Math.min(255, this.bitmap.data[idx] + noise)); // R
211
+ this.bitmap.data[idx + 1] = Math.max(0, Math.min(255, this.bitmap.data[idx + 1] + noise)); // G
212
+ this.bitmap.data[idx + 2] = Math.max(0, Math.min(255, this.bitmap.data[idx + 2] + noise)); // B
213
+ });
214
+
215
+ const jimpBuffer = await jimpImage.getBuffer('image/png');
216
+ return sharp(jimpBuffer);
217
+ }
218
+ return image;
219
+ }
220
+
221
+ async function applyGrainSharp(image: sharp.Sharp, intensity: number): Promise<sharp.Sharp> {
222
+ if (intensity > 0) {
223
+ // Add grain using Jimp
224
+ const buffer = await image.png().toBuffer();
225
+ const jimpImage = await Jimp.read(buffer);
226
+
227
+ jimpImage.scan(0, 0, jimpImage.width, jimpImage.height, function (this: any, x: number, y: number, idx: number) {
228
+ const grain = (Math.random() - 0.5) * intensity * 100;
229
+ this.bitmap.data[idx] = Math.max(0, Math.min(255, this.bitmap.data[idx] + grain)); // R
230
+ this.bitmap.data[idx + 1] = Math.max(0, Math.min(255, this.bitmap.data[idx + 1] + grain)); // G
231
+ this.bitmap.data[idx + 2] = Math.max(0, Math.min(255, this.bitmap.data[idx + 2] + grain)); // B
232
+ });
233
+
234
+ const jimpBuffer = await jimpImage.getBuffer('image/png');
235
+ return sharp(jimpBuffer);
236
+ }
237
+ return image;
238
+ }
239
+
240
+ async function applyEdgeDetectionSharp(image: sharp.Sharp, intensity: number): Promise<sharp.Sharp> {
241
+ if (intensity > 0) {
242
+ // Edge detection using Sobel kernel
243
+ const kernel = createSobelKernel(intensity);
244
+ return image.convolve(kernel).grayscale();
245
+ }
246
+ return image;
247
+ }
248
+
249
+ async function applyEmbossSharp(image: sharp.Sharp, intensity: number): Promise<sharp.Sharp> {
250
+ if (intensity > 0) {
251
+ // Emboss using custom kernel
252
+ const kernel = createEmbossKernel(intensity);
253
+ return image.convolve(kernel);
254
+ }
255
+ return image;
256
+ }
257
+
258
+ // Kernel creation functions
259
+ function createMotionBlurKernel(intensity: number, angle: number): any {
260
+ const size = Math.max(3, Math.floor(intensity));
261
+ const kernel = Array(size * size).fill(0);
262
+ const center = Math.floor(size / 2);
263
+
264
+ // Create motion blur kernel based on angle
265
+ const radians = (angle * Math.PI) / 180;
266
+ const dx = Math.cos(radians);
267
+ const dy = Math.sin(radians);
268
+
269
+ for (let i = 0; i < size; i++) {
270
+ const x = Math.round(center + dx * (i - center));
271
+ const y = Math.round(center + dy * (i - center));
272
+ if (x >= 0 && x < size && y >= 0 && y < size) {
273
+ kernel[y * size + x] = 1 / size;
274
+ }
275
+ }
276
+
277
+ return {
278
+ width: size,
279
+ height: size,
280
+ kernel: kernel,
281
+ scale: 1,
282
+ offset: 0
283
+ };
284
+ }
285
+
286
+ function createRadialBlurKernel(intensity: number, centerX: number, centerY: number): any {
287
+ const size = Math.max(3, Math.floor(intensity));
288
+ const kernel = Array(size * size).fill(0);
289
+ const center = Math.floor(size / 2);
290
+
291
+ for (let y = 0; y < size; y++) {
292
+ for (let x = 0; x < size; x++) {
293
+ const distance = Math.sqrt((x - center) ** 2 + (y - center) ** 2);
294
+ const weight = Math.max(0, 1 - distance / center);
295
+ kernel[y * size + x] = weight;
296
+ }
297
+ }
298
+
299
+ // Normalize kernel
300
+ const sum = kernel.reduce((a, b) => a + b, 0);
301
+ for (let i = 0; i < kernel.length; i++) {
302
+ kernel[i] /= sum;
303
+ }
304
+
305
+ return {
306
+ width: size,
307
+ height: size,
308
+ kernel: kernel,
309
+ scale: 1,
310
+ offset: 0
311
+ };
312
+ }
313
+
314
+ function createSobelKernel(intensity: number): any {
315
+ // Sobel X kernel for edge detection
316
+ const kernel = [
317
+ -1, 0, 1,
318
+ -2, 0, 2,
319
+ -1, 0, 1
320
+ ].map(v => v * intensity);
321
+
322
+ return {
323
+ width: 3,
324
+ height: 3,
325
+ kernel: kernel,
326
+ scale: 1,
327
+ offset: 128
328
+ };
329
+ }
330
+
331
+ function createEmbossKernel(intensity: number): any {
332
+ const kernel = [
333
+ -2, -1, 0,
334
+ -1, 1, 1,
335
+ 0, 1, 2
336
+ ].map(v => v * intensity);
337
+
338
+ return {
339
+ width: 3,
340
+ height: 3,
341
+ kernel: kernel,
342
+ scale: 1,
343
+ offset: 128
344
+ };
345
+ }
346
+
347
+ // Fallback basic filters
348
+ function applyBasicFilters(ctx: SKRSContext2D, filters: ImageFilter[], width: number, height: number): void {
349
+ ctx.save();
350
+
351
+ for (const filter of filters) {
352
+ switch (filter.type) {
353
+ case 'gaussianBlur':
354
+ if (filter.intensity && filter.intensity > 0) {
355
+ ctx.filter = `blur(${filter.intensity}px)`;
356
+ }
357
+ break;
358
+ case 'brightness':
359
+ if (filter.value !== undefined) {
360
+ ctx.filter = `brightness(${100 + filter.value}%)`;
361
+ }
362
+ break;
363
+ case 'contrast':
364
+ if (filter.value !== undefined) {
365
+ ctx.filter = `contrast(${100 + filter.value}%)`;
366
+ }
367
+ break;
368
+ case 'saturation':
369
+ if (filter.value !== undefined) {
370
+ ctx.filter = `saturate(${100 + filter.value}%)`;
371
+ }
372
+ break;
373
+ case 'hueShift':
374
+ if (filter.value !== undefined) {
375
+ ctx.filter = `hue-rotate(${filter.value}deg)`;
376
+ }
377
+ break;
378
+ case 'grayscale':
379
+ ctx.filter = 'grayscale(100%)';
380
+ break;
381
+ case 'sepia':
382
+ ctx.filter = 'sepia(100%)';
383
+ break;
384
+ case 'invert':
385
+ ctx.filter = 'invert(100%)';
386
+ break;
387
+ }
388
+ }
389
+
390
+ ctx.restore();
391
+ }
@@ -0,0 +1,229 @@
1
+ import { SKRSContext2D } from '@napi-rs/canvas';
2
+ import { ImageFilter } from '../types';
3
+ import sharp from 'sharp';
4
+
5
+ /**
6
+ * Applies professional image filters using Sharp (simplified version)
7
+ * @param ctx Canvas 2D context
8
+ * @param filters Array of filters to apply
9
+ * @param width Canvas width
10
+ * @param height Canvas height
11
+ */
12
+ export async function applySimpleProfessionalFilters(
13
+ ctx: SKRSContext2D,
14
+ filters: ImageFilter[],
15
+ width: number,
16
+ height: number
17
+ ): Promise<void> {
18
+ if (!filters || filters.length === 0) return;
19
+
20
+ try {
21
+ // Get current canvas data
22
+ const imageData = ctx.getImageData(0, 0, width, height);
23
+ // Convert Uint8ClampedArray to Buffer for Sharp
24
+ const buffer = Buffer.from(new Uint8Array(imageData.data.buffer));
25
+
26
+ // Convert to Sharp-compatible format
27
+ let sharpImage = sharp(buffer, {
28
+ raw: {
29
+ width: width,
30
+ height: height,
31
+ channels: 4
32
+ }
33
+ });
34
+
35
+ // Apply each filter using Sharp
36
+ for (const filter of filters) {
37
+ switch (filter.type) {
38
+ case 'gaussianBlur':
39
+ if (filter.intensity && filter.intensity > 0) {
40
+ sharpImage = sharpImage.blur(filter.intensity);
41
+ }
42
+ break;
43
+ case 'sharpen':
44
+ if (filter.intensity && filter.intensity > 0) {
45
+ sharpImage = sharpImage.sharpen(filter.intensity);
46
+ }
47
+ break;
48
+ case 'brightness':
49
+ if (filter.value !== undefined && filter.value !== 0) {
50
+ const brightness = Math.max(0, Math.min(2, 1 + filter.value / 100));
51
+ sharpImage = sharpImage.modulate({ brightness });
52
+ }
53
+ break;
54
+ case 'contrast':
55
+ if (filter.value !== undefined && filter.value !== 0) {
56
+ const contrast = Math.max(0, Math.min(2, 1 + filter.value / 100));
57
+ sharpImage = sharpImage.linear(contrast, -(128 * contrast) + 128);
58
+ }
59
+ break;
60
+ case 'saturation':
61
+ if (filter.value !== undefined && filter.value !== 0) {
62
+ const saturation = Math.max(0, Math.min(2, 1 + filter.value / 100));
63
+ sharpImage = sharpImage.modulate({ saturation });
64
+ }
65
+ break;
66
+ case 'hueShift':
67
+ if (filter.value !== undefined && filter.value !== 0) {
68
+ sharpImage = sharpImage.modulate({ hue: filter.value });
69
+ }
70
+ break;
71
+ case 'grayscale':
72
+ sharpImage = sharpImage.grayscale();
73
+ break;
74
+ case 'sepia':
75
+ sharpImage = sharpImage.recomb([
76
+ [0.393, 0.769, 0.189],
77
+ [0.349, 0.686, 0.168],
78
+ [0.272, 0.534, 0.131]
79
+ ]);
80
+ break;
81
+ case 'invert':
82
+ sharpImage = sharpImage.negate();
83
+ break;
84
+ case 'posterize':
85
+ if (filter.levels && filter.levels > 1) {
86
+ sharpImage = sharpImage.threshold(128);
87
+ }
88
+ break;
89
+ case 'pixelate':
90
+ if (filter.size && filter.size > 1) {
91
+ const { width: imgWidth, height: imgHeight } = await sharpImage.metadata();
92
+ const scale = Math.max(1, Math.floor(Math.min(imgWidth || 1, imgHeight || 1) / filter.size));
93
+ sharpImage = sharpImage.resize({ width: scale, height: scale, kernel: sharp.kernel.nearest })
94
+ .resize({ width: imgWidth, height: imgHeight, kernel: sharp.kernel.nearest });
95
+ }
96
+ break;
97
+ case 'motionBlur':
98
+ case 'radialBlur':
99
+ case 'noise':
100
+ case 'grain':
101
+ case 'edgeDetection':
102
+ case 'emboss':
103
+ // Use basic canvas filters for complex effects
104
+ applyBasicCanvasFilter(ctx, filter, width, height);
105
+ return; // Skip Sharp processing for these
106
+ }
107
+ }
108
+
109
+ // Convert back to canvas format
110
+ const { data } = await sharpImage.raw().toBuffer({ resolveWithObject: true });
111
+ const newImageData = ctx.createImageData(width, height);
112
+ newImageData.data.set(new Uint8ClampedArray(data));
113
+ ctx.putImageData(newImageData, 0, 0);
114
+
115
+ } catch (error) {
116
+ console.error('Error applying professional filters:', error);
117
+ // Fallback to basic filters if Sharp fails
118
+ applyBasicCanvasFilters(ctx, filters, width, height);
119
+ }
120
+ }
121
+
122
+ // Basic canvas filter implementations for complex effects
123
+ function applyBasicCanvasFilter(ctx: SKRSContext2D, filter: ImageFilter, width: number, height: number): void {
124
+ ctx.save();
125
+
126
+ switch (filter.type) {
127
+ case 'motionBlur':
128
+ if (filter.intensity && filter.intensity > 0) {
129
+ const radians = ((filter.angle || 0) * Math.PI) / 180;
130
+ const blurX = Math.cos(radians) * filter.intensity;
131
+ const blurY = Math.sin(radians) * filter.intensity;
132
+ ctx.filter = `blur(${Math.abs(blurX)}px ${Math.abs(blurY)}px)`;
133
+ }
134
+ break;
135
+ case 'radialBlur':
136
+ if (filter.intensity && filter.intensity > 0) {
137
+ ctx.filter = `blur(${filter.intensity}px)`;
138
+ }
139
+ break;
140
+ case 'noise':
141
+ if (filter.intensity && filter.intensity > 0) {
142
+ const imageData = ctx.getImageData(0, 0, width, height);
143
+ const data = imageData.data;
144
+
145
+ for (let i = 0; i < data.length; i += 4) {
146
+ const noise = (Math.random() - 0.5) * filter.intensity * 255;
147
+ data[i] = Math.max(0, Math.min(255, data[i] + noise)); // R
148
+ data[i + 1] = Math.max(0, Math.min(255, data[i + 1] + noise)); // G
149
+ data[i + 2] = Math.max(0, Math.min(255, data[i + 2] + noise)); // B
150
+ }
151
+
152
+ ctx.putImageData(imageData, 0, 0);
153
+ }
154
+ break;
155
+ case 'grain':
156
+ if (filter.intensity && filter.intensity > 0) {
157
+ const imageData = ctx.getImageData(0, 0, width, height);
158
+ const data = imageData.data;
159
+
160
+ for (let i = 0; i < data.length; i += 4) {
161
+ const grain = (Math.random() - 0.5) * filter.intensity * 100;
162
+ data[i] = Math.max(0, Math.min(255, data[i] + grain)); // R
163
+ data[i + 1] = Math.max(0, Math.min(255, data[i + 1] + grain)); // G
164
+ data[i + 2] = Math.max(0, Math.min(255, data[i + 2] + grain)); // B
165
+ }
166
+
167
+ ctx.putImageData(imageData, 0, 0);
168
+ }
169
+ break;
170
+ case 'edgeDetection':
171
+ if (filter.intensity && filter.intensity > 0) {
172
+ ctx.filter = `contrast(${100 + filter.intensity * 50}%) brightness(${100 - filter.intensity * 20}%)`;
173
+ }
174
+ break;
175
+ case 'emboss':
176
+ if (filter.intensity && filter.intensity > 0) {
177
+ ctx.filter = `contrast(${100 + filter.intensity * 30}%) brightness(${100 + filter.intensity * 10}%)`;
178
+ }
179
+ break;
180
+ }
181
+
182
+ ctx.restore();
183
+ }
184
+
185
+ // Fallback basic filters
186
+ function applyBasicCanvasFilters(ctx: SKRSContext2D, filters: ImageFilter[], width: number, height: number): void {
187
+ ctx.save();
188
+
189
+ for (const filter of filters) {
190
+ switch (filter.type) {
191
+ case 'gaussianBlur':
192
+ if (filter.intensity && filter.intensity > 0) {
193
+ ctx.filter = `blur(${filter.intensity}px)`;
194
+ }
195
+ break;
196
+ case 'brightness':
197
+ if (filter.value !== undefined) {
198
+ ctx.filter = `brightness(${100 + filter.value}%)`;
199
+ }
200
+ break;
201
+ case 'contrast':
202
+ if (filter.value !== undefined) {
203
+ ctx.filter = `contrast(${100 + filter.value}%)`;
204
+ }
205
+ break;
206
+ case 'saturation':
207
+ if (filter.value !== undefined) {
208
+ ctx.filter = `saturate(${100 + filter.value}%)`;
209
+ }
210
+ break;
211
+ case 'hueShift':
212
+ if (filter.value !== undefined) {
213
+ ctx.filter = `hue-rotate(${filter.value}deg)`;
214
+ }
215
+ break;
216
+ case 'grayscale':
217
+ ctx.filter = 'grayscale(100%)';
218
+ break;
219
+ case 'sepia':
220
+ ctx.filter = 'sepia(100%)';
221
+ break;
222
+ case 'invert':
223
+ ctx.filter = 'invert(100%)';
224
+ break;
225
+ }
226
+ }
227
+
228
+ ctx.restore();
229
+ }