@gradio/imageeditor 0.12.14 → 0.13.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.
Files changed (130) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/Example.svelte +1 -1
  3. package/IMAGE_EDITOR_OVERVIEW.md +224 -0
  4. package/ImageEditor.stories.svelte +4 -4
  5. package/Index.svelte +35 -46
  6. package/{shared/InteractiveImageEditor.svelte → InteractiveImageEditor.svelte} +56 -103
  7. package/dist/Example.svelte.d.ts +1 -1
  8. package/dist/Index.svelte +29 -33
  9. package/dist/Index.svelte.d.ts +17 -16
  10. package/dist/{shared/InteractiveImageEditor.svelte → InteractiveImageEditor.svelte} +47 -88
  11. package/dist/{shared/InteractiveImageEditor.svelte.d.ts → InteractiveImageEditor.svelte.d.ts} +13 -15
  12. package/dist/shared/Anchor.svelte +311 -0
  13. package/dist/shared/Anchor.svelte.d.ts +18 -0
  14. package/dist/shared/Controls.svelte +162 -23
  15. package/dist/shared/Controls.svelte.d.ts +7 -2
  16. package/dist/shared/{tools/IconButton.svelte → IconButton.svelte} +19 -3
  17. package/dist/shared/{tools/IconButton.svelte.d.ts → IconButton.svelte.d.ts} +4 -1
  18. package/dist/shared/ImageEditor.svelte +546 -289
  19. package/dist/shared/ImageEditor.svelte.d.ts +51 -56
  20. package/dist/shared/Layers.svelte +233 -0
  21. package/dist/shared/Layers.svelte.d.ts +35 -0
  22. package/dist/shared/Resize.svelte +218 -0
  23. package/dist/shared/Resize.svelte.d.ts +29 -0
  24. package/dist/shared/SecondaryToolbar.svelte +54 -0
  25. package/dist/shared/SecondaryToolbar.svelte.d.ts +35 -0
  26. package/dist/shared/Toolbar.svelte +337 -0
  27. package/dist/shared/Toolbar.svelte.d.ts +42 -0
  28. package/dist/shared/{tools → brush}/BrushOptions.svelte +68 -64
  29. package/dist/shared/{tools → brush}/BrushOptions.svelte.d.ts +9 -9
  30. package/dist/shared/{tools → brush}/BrushSize.svelte +2 -27
  31. package/dist/shared/{tools → brush}/ColorField.svelte +2 -1
  32. package/dist/shared/{tools → brush}/ColorField.svelte.d.ts +1 -0
  33. package/dist/shared/{tools → brush}/ColorPicker.svelte +81 -9
  34. package/dist/shared/{tools → brush}/ColorPicker.svelte.d.ts +1 -0
  35. package/dist/shared/{tools → brush}/ColorSwatch.svelte +78 -38
  36. package/dist/shared/{tools → brush}/ColorSwatch.svelte.d.ts +6 -2
  37. package/dist/shared/brush/brush-cursor.d.ts +101 -0
  38. package/dist/shared/brush/brush-cursor.js +327 -0
  39. package/dist/shared/brush/brush-textures.d.ts +71 -0
  40. package/dist/shared/brush/brush-textures.js +404 -0
  41. package/dist/shared/brush/brush-utils.d.ts +32 -0
  42. package/dist/shared/brush/brush-utils.js +59 -0
  43. package/dist/shared/brush/brush.d.ts +152 -0
  44. package/dist/shared/brush/brush.js +396 -0
  45. package/dist/shared/brush/types.d.ts +37 -0
  46. package/dist/shared/brush/types.js +1 -0
  47. package/dist/shared/core/editor.d.ts +252 -0
  48. package/dist/shared/core/editor.js +1221 -0
  49. package/dist/shared/crop/crop.d.ts +201 -0
  50. package/dist/shared/crop/crop.js +727 -0
  51. package/dist/shared/image/image.d.ts +89 -0
  52. package/dist/shared/image/image.js +185 -0
  53. package/dist/shared/resize/resize.d.ts +270 -0
  54. package/dist/shared/resize/resize.js +1413 -0
  55. package/dist/shared/types.d.ts +12 -0
  56. package/dist/shared/utils/pixi.d.ts +16 -57
  57. package/dist/shared/utils/pixi.js +15 -113
  58. package/dist/shared/zoom/zoom.d.ts +136 -0
  59. package/dist/shared/zoom/zoom.js +548 -0
  60. package/package.json +10 -9
  61. package/shared/Anchor.svelte +384 -0
  62. package/shared/Controls.svelte +194 -27
  63. package/shared/{tools/IconButton.svelte → IconButton.svelte} +21 -4
  64. package/shared/ImageEditor.svelte +682 -349
  65. package/shared/Layers.svelte +249 -0
  66. package/shared/Resize.svelte +239 -0
  67. package/shared/SecondaryToolbar.svelte +61 -0
  68. package/shared/Toolbar.svelte +386 -0
  69. package/shared/brush/BRUSH_TOOL.md +134 -0
  70. package/shared/brush/BrushOptions.svelte +200 -0
  71. package/shared/{tools → brush}/BrushSize.svelte +2 -30
  72. package/shared/{tools → brush}/ColorField.svelte +3 -1
  73. package/shared/{tools → brush}/ColorPicker.svelte +86 -9
  74. package/shared/{tools → brush}/ColorSwatch.svelte +95 -42
  75. package/shared/brush/brush-cursor.ts +408 -0
  76. package/shared/brush/brush-textures.ts +508 -0
  77. package/shared/brush/brush-utils.ts +76 -0
  78. package/shared/brush/brush.ts +506 -0
  79. package/shared/brush/types.ts +41 -0
  80. package/shared/core/EDITOR.md +182 -0
  81. package/shared/core/editor.ts +1730 -0
  82. package/shared/crop/CROP.md +139 -0
  83. package/shared/crop/crop.ts +1027 -0
  84. package/shared/image/IMAGE.md +135 -0
  85. package/shared/image/image.ts +300 -0
  86. package/shared/resize/resize.ts +1894 -0
  87. package/shared/types.ts +14 -0
  88. package/shared/utils/pixi.ts +19 -246
  89. package/shared/zoom/zoom.ts +729 -0
  90. package/dist/shared/layers/Layers.svelte +0 -231
  91. package/dist/shared/layers/Layers.svelte.d.ts +0 -18
  92. package/dist/shared/layers/utils.d.ts +0 -73
  93. package/dist/shared/layers/utils.js +0 -156
  94. package/dist/shared/tools/Brush.svelte +0 -215
  95. package/dist/shared/tools/Brush.svelte.d.ts +0 -29
  96. package/dist/shared/tools/Crop.svelte +0 -155
  97. package/dist/shared/tools/Crop.svelte.d.ts +0 -16
  98. package/dist/shared/tools/Cropper.svelte +0 -271
  99. package/dist/shared/tools/Cropper.svelte.d.ts +0 -41
  100. package/dist/shared/tools/Handle.svelte +0 -260
  101. package/dist/shared/tools/Handle.svelte.d.ts +0 -29
  102. package/dist/shared/tools/Sources.svelte +0 -257
  103. package/dist/shared/tools/Sources.svelte.d.ts +0 -34
  104. package/dist/shared/tools/Tools.svelte +0 -102
  105. package/dist/shared/tools/Tools.svelte.d.ts +0 -48
  106. package/dist/shared/tools/brush.d.ts +0 -62
  107. package/dist/shared/tools/brush.js +0 -133
  108. package/dist/shared/tools/crop.d.ts +0 -15
  109. package/dist/shared/tools/crop.js +0 -154
  110. package/dist/shared/tools/index.d.ts +0 -5
  111. package/dist/shared/tools/index.js +0 -5
  112. package/dist/shared/tools/sources.d.ts +0 -46
  113. package/dist/shared/tools/sources.js +0 -121
  114. package/dist/shared/tools/types.d.ts +0 -1
  115. package/shared/layers/Layers.svelte +0 -250
  116. package/shared/layers/utils.ts +0 -294
  117. package/shared/tools/Brush.svelte +0 -280
  118. package/shared/tools/BrushOptions.svelte +0 -180
  119. package/shared/tools/Crop.svelte +0 -189
  120. package/shared/tools/Cropper.svelte +0 -346
  121. package/shared/tools/Handle.svelte +0 -289
  122. package/shared/tools/Sources.svelte +0 -283
  123. package/shared/tools/Tools.svelte +0 -162
  124. package/shared/tools/brush.ts +0 -247
  125. package/shared/tools/crop.ts +0 -213
  126. package/shared/tools/index.ts +0 -5
  127. package/shared/tools/sources.ts +0 -195
  128. package/shared/tools/types.ts +0 -1
  129. /package/dist/shared/{tools → brush}/BrushSize.svelte.d.ts +0 -0
  130. /package/dist/shared/{tools/types.js → types.js} +0 -0
@@ -0,0 +1,404 @@
1
+ import { Container, RenderTexture, Sprite, Graphics, Application } from "pixi.js";
2
+ import { safe_destroy, safe_remove_child } from "./brush-utils";
3
+ import {} from "../core/editor";
4
+ /**
5
+ * Class to handle texture operations for the brush tool.
6
+ * Simplified to work directly with layer textures.
7
+ */
8
+ export class BrushTextures {
9
+ image_editor_context;
10
+ app;
11
+ // Only keep what we need
12
+ stroke_texture = null;
13
+ erase_texture = null;
14
+ display_container = null;
15
+ stroke_container = null;
16
+ stroke_graphics = null;
17
+ preview_sprite = null;
18
+ erase_graphics = null;
19
+ dimensions;
20
+ // Track if a new stroke is starting
21
+ is_new_stroke = true;
22
+ // Current scale factor
23
+ scale = 1;
24
+ // Track the current drawing opacity
25
+ current_opacity = 1.0;
26
+ // Track current drawing mode
27
+ current_mode = "draw";
28
+ // Store last drawing parameters for eraser recreation
29
+ last_from_x = null;
30
+ last_from_y = null;
31
+ last_to_x = null;
32
+ last_to_y = null;
33
+ last_size = null;
34
+ constructor(image_editor_context, app) {
35
+ this.image_editor_context = image_editor_context;
36
+ this.app = app;
37
+ this.dimensions = {
38
+ width: this.image_editor_context.image_container.width,
39
+ height: this.image_editor_context.image_container.height
40
+ };
41
+ // Subscribe to scale changes
42
+ this.image_editor_context.scale.subscribe((scale) => {
43
+ this.scale = scale;
44
+ });
45
+ }
46
+ /**
47
+ * Initializes textures needed for the brush tool.
48
+ */
49
+ initialize_textures() {
50
+ // Clean up any existing resources
51
+ this.cleanup_textures();
52
+ // Get dimensions from the image container
53
+ const local_bounds = this.image_editor_context.image_container.getLocalBounds();
54
+ this.dimensions = {
55
+ width: local_bounds.width,
56
+ height: local_bounds.height
57
+ };
58
+ // Create a texture for the current stroke preview
59
+ this.stroke_texture = RenderTexture.create({
60
+ width: this.dimensions.width,
61
+ height: this.dimensions.height,
62
+ resolution: window.devicePixelRatio || 1
63
+ });
64
+ // Create a texture for erasing
65
+ this.erase_texture = RenderTexture.create({
66
+ width: this.dimensions.width,
67
+ height: this.dimensions.height,
68
+ resolution: window.devicePixelRatio || 1
69
+ });
70
+ // Create a container to display preview
71
+ this.display_container = new Container();
72
+ // Get the active layer and add the display container to it
73
+ const active_layer = this.image_editor_context.layer_manager.get_active_layer();
74
+ if (active_layer) {
75
+ active_layer.addChild(this.display_container);
76
+ }
77
+ else {
78
+ this.image_editor_context.image_container.addChild(this.display_container);
79
+ }
80
+ // Create container for the stroke graphics
81
+ this.stroke_container = new Container();
82
+ this.stroke_graphics = new Graphics();
83
+ this.stroke_container.addChild(this.stroke_graphics);
84
+ // Create graphics for eraser mask
85
+ this.erase_graphics = new Graphics();
86
+ // Create a sprite for the preview of the current stroke
87
+ this.preview_sprite = new Sprite(this.stroke_texture);
88
+ // Start with the preview sprite invisible
89
+ this.preview_sprite.alpha = 0;
90
+ this.display_container.addChild(this.preview_sprite);
91
+ // Clear the stroke texture
92
+ const clear_container = new Container();
93
+ this.app.renderer.render(clear_container, {
94
+ renderTexture: this.stroke_texture
95
+ });
96
+ this.app.renderer.render(clear_container, {
97
+ renderTexture: this.erase_texture
98
+ });
99
+ clear_container.destroy();
100
+ // Reset drawing state
101
+ this.is_new_stroke = true;
102
+ this.current_opacity = 1.0;
103
+ }
104
+ /**
105
+ * Reinitializes textures when needed (e.g., after resizing).
106
+ */
107
+ reinitialize() {
108
+ if (this.image_editor_context.image_container.width !==
109
+ this.dimensions.width ||
110
+ this.image_editor_context.image_container.height !==
111
+ this.dimensions.height) {
112
+ this.initialize_textures();
113
+ }
114
+ }
115
+ /**
116
+ * Cleans up texture resources.
117
+ */
118
+ cleanup_textures() {
119
+ // Clean up stroke texture
120
+ if (this.stroke_texture) {
121
+ this.stroke_texture.destroy();
122
+ this.stroke_texture = null;
123
+ }
124
+ // Clean up erase texture
125
+ if (this.erase_texture) {
126
+ this.erase_texture.destroy();
127
+ this.erase_texture = null;
128
+ }
129
+ // Clean up display container
130
+ if (this.display_container) {
131
+ if (this.display_container.parent) {
132
+ this.display_container.parent.removeChild(this.display_container);
133
+ }
134
+ this.display_container.destroy({ children: true });
135
+ this.display_container = null;
136
+ }
137
+ // Nullify other references
138
+ this.stroke_container = null;
139
+ this.stroke_graphics = null;
140
+ this.preview_sprite = null;
141
+ this.erase_graphics = null;
142
+ }
143
+ /**
144
+ * Preserve the canvas state when starting a new stroke.
145
+ */
146
+ preserve_canvas_state() {
147
+ // Reset for a new stroke
148
+ this.is_new_stroke = true;
149
+ }
150
+ /**
151
+ * Resets the eraser mask.
152
+ */
153
+ reset_eraser_mask() {
154
+ if (!this.erase_graphics || !this.erase_texture)
155
+ return;
156
+ // Clear and reset the eraser graphics
157
+ this.erase_graphics.clear();
158
+ this.erase_graphics.beginFill(0xffffff, 1);
159
+ this.erase_graphics.drawRect(0, 0, this.dimensions.width, this.dimensions.height);
160
+ this.erase_graphics.endFill();
161
+ // Render the eraser graphics to the erase texture
162
+ this.app.renderer.render(this.erase_graphics, {
163
+ renderTexture: this.erase_texture
164
+ });
165
+ }
166
+ /**
167
+ * Commits the current stroke to the active layer.
168
+ */
169
+ commit_stroke() {
170
+ if (!this.stroke_texture || !this.preview_sprite || !this.stroke_graphics)
171
+ return;
172
+ // Hide the preview sprite
173
+ this.preview_sprite.visible = false;
174
+ // Get the active layer's texture
175
+ const active_layer = this.image_editor_context.layer_manager.get_active_layer();
176
+ if (!active_layer)
177
+ return;
178
+ // Get all layers to find the ID of the active layer
179
+ const layers = this.image_editor_context.layer_manager.get_layers();
180
+ const layer = layers.find((l) => l.container === active_layer);
181
+ if (!layer)
182
+ return;
183
+ // Get the active layer's draw texture
184
+ const layer_textures = this.image_editor_context.layer_manager.get_layer_textures(layer.id);
185
+ if (!layer_textures)
186
+ return;
187
+ // Save the current opacity for use in committing
188
+ const currentOpacity = this.current_opacity;
189
+ // Handle drawing or erasing
190
+ if (this.current_mode === "draw") {
191
+ // Drawing mode - simple composition
192
+ const temp_container = new Container();
193
+ // Add current layer texture as base
194
+ const base_sprite = new Sprite(layer_textures.draw);
195
+ temp_container.addChild(base_sprite);
196
+ // Add the current stroke with proper opacity
197
+ const stroke_sprite = new Sprite(this.stroke_texture);
198
+ stroke_sprite.alpha = currentOpacity;
199
+ temp_container.addChild(stroke_sprite);
200
+ // Render the composited result directly to the layer texture
201
+ this.app.renderer.render(temp_container, {
202
+ renderTexture: layer_textures.draw
203
+ });
204
+ // Clean up
205
+ temp_container.destroy({ children: true });
206
+ }
207
+ else {
208
+ // Erasing mode - use proper masking with setMask
209
+ if (!this.stroke_texture)
210
+ return;
211
+ // Create a container for the erase operation
212
+ const erase_container = new Container();
213
+ // Add the current layer content
214
+ const content_sprite = new Sprite(layer_textures.draw);
215
+ erase_container.addChild(content_sprite);
216
+ // Create the mask sprite from our stroke texture
217
+ const mask_sprite = new Sprite(this.stroke_texture);
218
+ // Apply the mask to the container using setMask with inverse: true
219
+ // This will only show content where the mask is NOT (inverse: true)
220
+ erase_container.setMask({ mask: mask_sprite, inverse: true });
221
+ // Render the masked content back to layer texture
222
+ this.app.renderer.render(erase_container, {
223
+ renderTexture: layer_textures.draw
224
+ });
225
+ // Clean up
226
+ erase_container.destroy({ children: true });
227
+ }
228
+ // Clear the stroke graphics and texture for the next stroke
229
+ if (this.stroke_graphics) {
230
+ this.stroke_graphics.clear();
231
+ }
232
+ const clear_container = new Container();
233
+ this.app.renderer.render(clear_container, {
234
+ renderTexture: this.stroke_texture
235
+ });
236
+ clear_container.destroy();
237
+ // Reset state for the next stroke
238
+ this.is_new_stroke = true;
239
+ }
240
+ /**
241
+ * Calculates the distance between two points.
242
+ */
243
+ calculateDistance(x1, y1, x2, y2) {
244
+ return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
245
+ }
246
+ /**
247
+ * Draws a segment for the current stroke using a simple "stamp" approach.
248
+ */
249
+ draw_segment(from_x, from_y, to_x, to_y, size, color, opacity, mode) {
250
+ if (!this.stroke_graphics ||
251
+ !this.stroke_texture ||
252
+ !this.stroke_container ||
253
+ !this.preview_sprite)
254
+ return;
255
+ // Store the current mode and opacity
256
+ this.current_mode = mode;
257
+ this.current_opacity =
258
+ mode === "draw" ? Math.min(Math.max(opacity, 0), 1) : 0.5;
259
+ // Store parameters for potential eraser recreation
260
+ this.last_from_x = from_x;
261
+ this.last_from_y = from_y;
262
+ this.last_to_x = to_x;
263
+ this.last_to_y = to_y;
264
+ this.last_size = size;
265
+ // Apply scale to brush size for consistent rendering
266
+ const scaled_size = size;
267
+ // For new strokes, clear the graphics and texture completely
268
+ if (this.is_new_stroke) {
269
+ // Clean up for a new stroke
270
+ this.stroke_graphics.clear();
271
+ // Clear the stroke texture
272
+ const clear_container = new Container();
273
+ this.app.renderer.render(clear_container, {
274
+ renderTexture: this.stroke_texture
275
+ });
276
+ clear_container.destroy();
277
+ this.is_new_stroke = false;
278
+ }
279
+ // Set fill color and opacity for drawing
280
+ if (mode === "draw") {
281
+ // For drawing, use the specified color, but always full opacity in the graphics
282
+ // We need to convert the color string to a number
283
+ let colorValue = 0xffffff; // Default to white
284
+ try {
285
+ if (color.startsWith("#")) {
286
+ colorValue = parseInt(color.replace("#", "0x"), 16);
287
+ }
288
+ }
289
+ catch (e) {
290
+ // Default to white if there's an error
291
+ colorValue = 0xffffff;
292
+ }
293
+ this.stroke_graphics.setFillStyle({
294
+ color: colorValue,
295
+ alpha: 1.0
296
+ });
297
+ }
298
+ else {
299
+ // For erasing, always use white
300
+ this.stroke_graphics.setFillStyle({
301
+ color: 0xffffff,
302
+ alpha: 1.0
303
+ });
304
+ }
305
+ // Calculate the distance between the points
306
+ const distance = this.calculateDistance(from_x, from_y, to_x, to_y);
307
+ // If the points are the same, just draw a single circle
308
+ if (distance < 0.1) {
309
+ // Draw a circle at the point - use scaled_size as RADIUS
310
+ this.stroke_graphics.circle(from_x, from_y, scaled_size).fill();
311
+ }
312
+ else {
313
+ // Calculate how many circles to draw along the path
314
+ // Adaptive spacing that balances quality and performance
315
+ const spacing = Math.max(scaled_size / 3, 2);
316
+ const steps = Math.max(Math.ceil(distance / spacing), 2);
317
+ // Draw a circle at each point along the path
318
+ for (let i = 0; i < steps; i++) {
319
+ const t = i / (steps - 1);
320
+ const x = from_x + (to_x - from_x) * t;
321
+ const y = from_y + (to_y - from_y) * t;
322
+ // Create a circle at this point - use scaled_size as RADIUS
323
+ this.stroke_graphics.circle(x, y, scaled_size).fill();
324
+ }
325
+ }
326
+ // End the fill after all circles are drawn
327
+ this.stroke_graphics.endFill();
328
+ // Render the stroke to the texture
329
+ this.app.renderer.render(this.stroke_container, {
330
+ renderTexture: this.stroke_texture
331
+ });
332
+ // Update the preview based on mode
333
+ if (mode === "draw") {
334
+ // For drawing, show the stroke directly with proper opacity
335
+ this.preview_sprite.texture = this.stroke_texture;
336
+ this.preview_sprite.alpha = this.current_opacity;
337
+ // Set tint to white (no tint) instead of trying to tint the texture
338
+ this.preview_sprite.tint = 0xffffff;
339
+ }
340
+ else {
341
+ // For erasing, create a preview that shows what will be erased
342
+ // Get the active layer's texture for preview
343
+ const active_layer = this.image_editor_context.layer_manager.get_active_layer();
344
+ if (!active_layer)
345
+ return;
346
+ const layers = this.image_editor_context.layer_manager.get_layers();
347
+ const layer = layers.find((l) => l.container === active_layer);
348
+ if (!layer)
349
+ return;
350
+ const layer_textures = this.image_editor_context.layer_manager.get_layer_textures(layer.id);
351
+ if (!layer_textures)
352
+ return;
353
+ // Create a temporary texture for the preview
354
+ const preview_texture = RenderTexture.create({
355
+ width: this.dimensions.width,
356
+ height: this.dimensions.height,
357
+ resolution: window.devicePixelRatio || 1
358
+ });
359
+ // First, create a copy of the current layer content
360
+ const base_container = new Container();
361
+ const content_sprite = new Sprite(layer_textures.draw);
362
+ base_container.addChild(content_sprite);
363
+ this.app.renderer.render(base_container, {
364
+ renderTexture: preview_texture
365
+ });
366
+ base_container.destroy({ children: true });
367
+ // Create a container for the eraser preview with red overlay
368
+ const preview_container = new Container();
369
+ // Add our current stroke as the content
370
+ const mask_sprite = new Sprite(this.stroke_texture);
371
+ // Add a red overlay to show what's being erased
372
+ const overlay = new Graphics();
373
+ overlay.setFillStyle({ color: 0xffffff, alpha: 0.5 });
374
+ overlay.rect(0, 0, this.dimensions.width, this.dimensions.height).fill();
375
+ // Use setMask with inverse: false to only show overlay where the stroke is
376
+ // We want to show the red overlay ONLY where the stroke will erase
377
+ overlay.setMask({ mask: mask_sprite, inverse: false });
378
+ preview_container.addChild(overlay);
379
+ // Render the overlay on top of the base content
380
+ this.app.renderer.render(preview_container, {
381
+ renderTexture: preview_texture
382
+ });
383
+ // Update the preview sprite with our preview texture
384
+ this.preview_sprite.texture = preview_texture;
385
+ this.preview_sprite.alpha = 1.0;
386
+ // Clean up
387
+ preview_container.destroy({ children: true });
388
+ preview_texture.destroy();
389
+ }
390
+ this.preview_sprite.visible = true;
391
+ }
392
+ /**
393
+ * Gets current dimensions of the texture.
394
+ */
395
+ get_dimensions() {
396
+ return this.dimensions;
397
+ }
398
+ /**
399
+ * Cleanup all resources.
400
+ */
401
+ cleanup() {
402
+ this.cleanup_textures();
403
+ }
404
+ }
@@ -0,0 +1,32 @@
1
+ import { Container, Sprite, Graphics } from "pixi.js";
2
+ /**
3
+ * Sets the cursor style for a single PIXI object.
4
+ * @param child - The PIXI object to set the cursor for.
5
+ * @param cursor - The cursor style to set.
6
+ */
7
+ export declare function set_cursor(child: Container | Sprite | Graphics, cursor: "unset" | "none"): void;
8
+ /**
9
+ * Recursively sets the cursor style for PIXI objects.
10
+ * @param children - The PIXI objects to set the cursor for.
11
+ * @param cursor - The cursor style to set.
12
+ */
13
+ export declare function recurse_set_cursor(children: (Container | Sprite | Graphics)[], cursor: "unset" | "none"): void;
14
+ /**
15
+ * Safely removes a child from its parent.
16
+ * @param child - The child to remove.
17
+ */
18
+ export declare function safe_remove_child(child: Container | Sprite | Graphics | null): void;
19
+ /**
20
+ * Safely destroys a PIXI object and nullifies it.
21
+ * @param obj - The object to destroy.
22
+ * @returns null to assist with nullifying the reference.
23
+ */
24
+ export declare function safe_destroy<T extends {
25
+ destroy: (options?: any) => void;
26
+ }>(obj: T | null): null;
27
+ /**
28
+ * Clears a timeout safely and nullifies the reference.
29
+ * @param timeout - The timeout to clear.
30
+ * @returns null to assist with nullifying the reference.
31
+ */
32
+ export declare function clear_timeout(timeout: number | null): null;
@@ -0,0 +1,59 @@
1
+ import { Container, Sprite, Graphics } from "pixi.js";
2
+ /**
3
+ * Sets the cursor style for a single PIXI object.
4
+ * @param child - The PIXI object to set the cursor for.
5
+ * @param cursor - The cursor style to set.
6
+ */
7
+ export function set_cursor(child, cursor) {
8
+ if (child instanceof Container) {
9
+ child.cursor = cursor;
10
+ }
11
+ else if ("cursor" in child) {
12
+ child.cursor = cursor;
13
+ }
14
+ }
15
+ /**
16
+ * Recursively sets the cursor style for PIXI objects.
17
+ * @param children - The PIXI objects to set the cursor for.
18
+ * @param cursor - The cursor style to set.
19
+ */
20
+ export function recurse_set_cursor(children, cursor) {
21
+ for (const child of children) {
22
+ set_cursor(child, cursor);
23
+ if (child instanceof Container && child.children.length > 0) {
24
+ recurse_set_cursor(child.children, cursor);
25
+ }
26
+ }
27
+ }
28
+ /**
29
+ * Safely removes a child from its parent.
30
+ * @param child - The child to remove.
31
+ */
32
+ export function safe_remove_child(child) {
33
+ if (child && child.parent) {
34
+ child.parent.removeChild(child);
35
+ }
36
+ }
37
+ /**
38
+ * Safely destroys a PIXI object and nullifies it.
39
+ * @param obj - The object to destroy.
40
+ * @returns null to assist with nullifying the reference.
41
+ */
42
+ export function safe_destroy(obj) {
43
+ if (obj) {
44
+ safe_remove_child(obj);
45
+ obj.destroy({ children: true });
46
+ }
47
+ return null;
48
+ }
49
+ /**
50
+ * Clears a timeout safely and nullifies the reference.
51
+ * @param timeout - The timeout to clear.
52
+ * @returns null to assist with nullifying the reference.
53
+ */
54
+ export function clear_timeout(timeout) {
55
+ if (timeout !== null) {
56
+ window.clearTimeout(timeout);
57
+ }
58
+ return null;
59
+ }
@@ -0,0 +1,152 @@
1
+ import { type ImageEditorContext, type Tool } from "../core/editor";
2
+ import { type Tool as ToolbarTool, type Subtool } from "../Toolbar.svelte";
3
+ import type { ColorInput } from "tinycolor2";
4
+ /**
5
+ * BrushTool class implements the Tool interface for drawing and erasing.
6
+ * @class
7
+ * @implements {Tool}
8
+ */
9
+ export declare class BrushTool implements Tool {
10
+ /**
11
+ * The name of the tool.
12
+ * @type {string}
13
+ */
14
+ name: "brush";
15
+ /**
16
+ * The context of the image editor.
17
+ * @private
18
+ */
19
+ private image_editor_context;
20
+ /**
21
+ * The current tool selected in the toolbar.
22
+ * @private
23
+ */
24
+ private current_tool;
25
+ /**
26
+ * The current subtool selected in the toolbar.
27
+ * @private
28
+ */
29
+ private current_subtool;
30
+ /**
31
+ * The state of the brush tool.
32
+ * @private
33
+ */
34
+ private state;
35
+ /**
36
+ * Size settings for brush and eraser
37
+ * @private
38
+ */
39
+ private brush_size;
40
+ private eraser_size;
41
+ /**
42
+ * Drawing state
43
+ * @private
44
+ */
45
+ private is_drawing;
46
+ private last_x;
47
+ private last_y;
48
+ private scale;
49
+ /**
50
+ * Event listeners
51
+ * @private
52
+ */
53
+ private _bound_pointer_down;
54
+ private _bound_pointer_move;
55
+ private _bound_pointer_up;
56
+ private event_callbacks;
57
+ /**
58
+ * Utility modules
59
+ * @private
60
+ */
61
+ private brush_cursor;
62
+ private brush_textures;
63
+ /**
64
+ * Sets up the brush tool with the given context, tool, and subtool.
65
+ * @param {ImageEditorContext} context - The image editor context.
66
+ * @param {ToolbarTool} tool - The tool from the toolbar.
67
+ * @param {Subtool} subtool - The subtool from the toolbar.
68
+ * @returns {Promise<void>}
69
+ */
70
+ setup(context: ImageEditorContext, tool: ToolbarTool, subtool: Subtool): Promise<void>;
71
+ /**
72
+ * Handles cursor styles for the brush tool.
73
+ * @param {ToolbarTool} tool - The current tool.
74
+ * @private
75
+ */
76
+ private handle_cursors;
77
+ /**
78
+ * Sets the current tool and subtool.
79
+ * @param {ToolbarTool} tool - The current tool.
80
+ * @param {Subtool} subtool - The current subtool.
81
+ */
82
+ set_tool(tool: ToolbarTool, subtool: Subtool): void;
83
+ /**
84
+ * Commits any pending changes to the canvas.
85
+ * @private
86
+ */
87
+ private commit_pending_changes;
88
+ /**
89
+ * Called when the user presses the mouse button.
90
+ * @param {FederatedPointerEvent} event - The pointer event.
91
+ * @private
92
+ */
93
+ private on_pointer_down;
94
+ /**
95
+ * Called when the user moves the mouse while drawing.
96
+ * @param {FederatedPointerEvent} event - The pointer event.
97
+ * @private
98
+ */
99
+ private on_pointer_move;
100
+ /**
101
+ * Called when the user releases the mouse button.
102
+ * @private
103
+ */
104
+ private on_pointer_up;
105
+ /**
106
+ * Sets up event listeners for drawing.
107
+ * @private
108
+ */
109
+ private setup_event_listeners;
110
+ /**
111
+ * Cleans up event listeners.
112
+ * @private
113
+ */
114
+ private cleanup_event_listeners;
115
+ /**
116
+ * Sets the brush size.
117
+ * @param {number} size - The new brush size.
118
+ */
119
+ set_brush_size(size: number): void;
120
+ /**
121
+ * Sets the brush color.
122
+ * @param {string|ColorInput} color - The new brush color.
123
+ */
124
+ set_brush_color(color: string | ColorInput): void;
125
+ /**
126
+ * Sets the brush opacity.
127
+ * @param {number} opacity - The new brush opacity.
128
+ */
129
+ set_brush_opacity(opacity: number): void;
130
+ /**
131
+ * Sets the eraser size.
132
+ * @param {number} size - The new eraser size.
133
+ */
134
+ set_eraser_size(size: number): void;
135
+ /**
136
+ * Gets the current size based on mode (brush or eraser).
137
+ * @returns {number} The current size.
138
+ */
139
+ get_current_size(): number;
140
+ /**
141
+ * Shows or hides the brush preview.
142
+ * @param {boolean} show - Whether to show the preview.
143
+ */
144
+ preview_brush(show: boolean): void;
145
+ /**
146
+ * Cleans up resources used by the brush tool.
147
+ */
148
+ cleanup(): void;
149
+ on<T extends string>(event: T, callback: () => void): void;
150
+ off<T extends string>(event: T, callback: () => void): void;
151
+ private notify;
152
+ }