@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,327 @@
1
+ import { Container, Graphics, Sprite } from "pixi.js";
2
+ import {} from "../core/editor";
3
+ import {} from "./types";
4
+ import tinycolor from "tinycolor2";
5
+ import { clear_timeout } from "./brush-utils";
6
+ /**
7
+ * Class to handle all cursor preview-related functionality for the brush tool.
8
+ */
9
+ export class BrushCursor {
10
+ image_editor_context;
11
+ state;
12
+ scale;
13
+ // Cursor-related properties
14
+ cursor_graphics = null;
15
+ cursor_container = null;
16
+ brush_preview_container = null;
17
+ brush_preview_graphics = null;
18
+ is_preview_visible = false;
19
+ is_cursor_over_image = false;
20
+ cursor_position_check_timeout = null;
21
+ is_brush_or_erase_active = false;
22
+ // Event handlers
23
+ _bound_update_cursor = null;
24
+ _bound_check_cursor_over_image = null;
25
+ constructor(image_editor_context, state, scale) {
26
+ this.image_editor_context = image_editor_context;
27
+ this.state = state;
28
+ this.scale = scale;
29
+ this.initialize_cursor();
30
+ this.initialize_brush_preview();
31
+ }
32
+ /**
33
+ * Returns whether the cursor is currently over the image.
34
+ */
35
+ is_over_image() {
36
+ return this.is_cursor_over_image;
37
+ }
38
+ /**
39
+ * Sets up cursor-related event listeners.
40
+ */
41
+ setup_event_listeners() {
42
+ // Clean up any existing listeners
43
+ this.cleanup_event_listeners();
44
+ // Create bound event handlers
45
+ this._bound_update_cursor = this.update_cursor_position.bind(this);
46
+ this._bound_check_cursor_over_image =
47
+ this.check_cursor_over_image.bind(this);
48
+ // Add event listeners using Pixi event system
49
+ // Use app.stage for global events like pointermove
50
+ const stage = this.image_editor_context.app.stage;
51
+ stage.on("pointermove", this._bound_update_cursor);
52
+ stage.on("pointermove", this._bound_check_cursor_over_image);
53
+ // Add event listeners to the image container for enter/leave events
54
+ this.image_editor_context.image_container.on("pointerenter", this.on_image_container_pointer_enter.bind(this));
55
+ this.image_editor_context.image_container.on("pointerleave", this.on_image_container_pointer_leave.bind(this));
56
+ }
57
+ /**
58
+ * Removes all cursor-related event listeners.
59
+ */
60
+ cleanup_event_listeners() {
61
+ // Use Pixi event system for removal
62
+ const stage = this.image_editor_context.app.stage;
63
+ // Remove event listeners if they exist
64
+ if (this._bound_update_cursor) {
65
+ stage.off("pointermove", this._bound_update_cursor);
66
+ this._bound_update_cursor = null;
67
+ }
68
+ if (this._bound_check_cursor_over_image) {
69
+ stage.off("pointermove", this._bound_check_cursor_over_image);
70
+ this._bound_check_cursor_over_image = null;
71
+ }
72
+ // Remove image container event listeners
73
+ this.image_editor_context.image_container.off("pointerenter");
74
+ this.image_editor_context.image_container.off("pointerleave");
75
+ // Clear timeout
76
+ this.cursor_position_check_timeout = clear_timeout(this.cursor_position_check_timeout);
77
+ }
78
+ /**
79
+ * initializes the cursor for the brush tool.
80
+ */
81
+ initialize_cursor() {
82
+ // clean up existing cursor if it exists
83
+ if (this.cursor_container) {
84
+ if (this.cursor_container.parent) {
85
+ this.cursor_container.parent.removeChild(this.cursor_container);
86
+ }
87
+ this.cursor_container.destroy({ children: true });
88
+ this.cursor_container = null;
89
+ }
90
+ // create new cursor container and add to ui container
91
+ this.cursor_container = new Container();
92
+ this.image_editor_context.ui_container.addChild(this.cursor_container);
93
+ // create cursor graphics
94
+ this.cursor_graphics = new Graphics();
95
+ this.cursor_container.addChild(this.cursor_graphics);
96
+ // draw initial cursor
97
+ this.update_cursor_appearance();
98
+ }
99
+ /**
100
+ * updates the appearance of the cursor.
101
+ */
102
+ update_cursor_appearance() {
103
+ if (!this.cursor_graphics)
104
+ return;
105
+ // clear previous graphics
106
+ this.cursor_graphics.clear();
107
+ // set color based on mode
108
+ const cursor_color = this.state.mode === "draw"
109
+ ? tinycolor(this.state.color).toString()
110
+ : 0xffffff;
111
+ // draw circle with current brush size
112
+ this.cursor_graphics
113
+ .circle(0, 0, this.state.brush_size * this.scale)
114
+ .stroke({
115
+ width: 1.5,
116
+ color: cursor_color,
117
+ alpha: 0.8
118
+ });
119
+ // add a small dot in the center for precision
120
+ this.cursor_graphics.circle(0, 0, 1).fill({
121
+ color: cursor_color,
122
+ alpha: 0.8
123
+ });
124
+ }
125
+ /**
126
+ * updates the position of the cursor.
127
+ * @param {FederatedPointerEvent} event - The pointer event.
128
+ */
129
+ update_cursor_position(event) {
130
+ if (!this.cursor_container)
131
+ return;
132
+ // get position in image coordinates
133
+ const local_pos = this.image_editor_context.image_container.toLocal(event.global);
134
+ // convert to ui coordinates
135
+ const ui_pos = this.image_editor_context.ui_container.toLocal(this.image_editor_context.image_container.toGlobal(local_pos));
136
+ // update cursor position
137
+ this.cursor_container.position.set(ui_pos.x, ui_pos.y);
138
+ }
139
+ /**
140
+ * checks if the cursor is over the image container.
141
+ * @param {FederatedPointerEvent} event - The pointer event.
142
+ */
143
+ check_cursor_over_image(event) {
144
+ // cancel any pending timeout
145
+ if (this.cursor_position_check_timeout !== null) {
146
+ window.clearTimeout(this.cursor_position_check_timeout);
147
+ this.cursor_position_check_timeout = null;
148
+ }
149
+ // get the image container bounds in global coordinates
150
+ const image_container = this.image_editor_context.image_container;
151
+ const bounds = image_container.getBounds();
152
+ // check if cursor is over image
153
+ const was_over_image = this.is_cursor_over_image;
154
+ this.is_cursor_over_image =
155
+ event.global.x >= bounds.x &&
156
+ event.global.x <= bounds.x + bounds.width &&
157
+ event.global.y >= bounds.y &&
158
+ event.global.y <= bounds.y + bounds.height;
159
+ // update visibility if state changed
160
+ if (was_over_image !== this.is_cursor_over_image) {
161
+ this.update_cursor_and_preview_visibility();
162
+ }
163
+ }
164
+ /**
165
+ * updates the size of the cursor.
166
+ */
167
+ update_cursor_size() {
168
+ this.update_cursor_appearance();
169
+ }
170
+ /**
171
+ * Updates whether brush or erase tool is active
172
+ */
173
+ set_active(is_active) {
174
+ this.is_brush_or_erase_active = is_active;
175
+ this.update_cursor_and_preview_visibility();
176
+ }
177
+ /**
178
+ * updates the visibility of the cursor.
179
+ */
180
+ update_cursor_visibility() {
181
+ if (this.cursor_container) {
182
+ this.cursor_container.visible =
183
+ this.is_cursor_over_image && this.is_brush_or_erase_active;
184
+ }
185
+ }
186
+ /**
187
+ * Shows or hides the brush preview.
188
+ * @param {boolean} show - Whether to show the preview.
189
+ */
190
+ preview_brush(show) {
191
+ this.is_preview_visible = show;
192
+ if (this.brush_preview_container) {
193
+ this.brush_preview_container.visible =
194
+ show && this.is_brush_or_erase_active;
195
+ }
196
+ if (show) {
197
+ // update preview appearance and position
198
+ this.update_brush_preview();
199
+ this.update_brush_preview_position();
200
+ }
201
+ }
202
+ /**
203
+ * initializes the brush preview.
204
+ */
205
+ initialize_brush_preview() {
206
+ // clean up existing preview if it exists
207
+ if (this.brush_preview_container) {
208
+ if (this.brush_preview_container.parent) {
209
+ this.brush_preview_container.parent.removeChild(this.brush_preview_container);
210
+ }
211
+ this.brush_preview_container.destroy({ children: true });
212
+ this.brush_preview_container = null;
213
+ }
214
+ // create new preview container and add to ui container
215
+ this.brush_preview_container = new Container();
216
+ this.image_editor_context.ui_container.addChild(this.brush_preview_container);
217
+ // create preview graphics
218
+ this.brush_preview_graphics = new Graphics();
219
+ this.brush_preview_container.addChild(this.brush_preview_graphics);
220
+ // set initial visibility
221
+ this.brush_preview_container.visible = false;
222
+ }
223
+ /**
224
+ * updates the brush preview appearance.
225
+ */
226
+ update_brush_preview() {
227
+ if (!this.brush_preview_graphics)
228
+ return;
229
+ // clear previous graphics
230
+ this.brush_preview_graphics.clear();
231
+ // draw brush preview based on current settings
232
+ const preview_size = this.state.brush_size * this.scale;
233
+ const preview_color = this.state.mode === "draw"
234
+ ? tinycolor(this.state.color).setAlpha(this.state.opacity).toString()
235
+ : 0xffffff;
236
+ // draw circle with current brush settings
237
+ this.brush_preview_graphics.circle(0, 0, preview_size).fill({
238
+ color: preview_color,
239
+ alpha: this.state.mode === "draw" ? this.state.opacity : 0.3
240
+ });
241
+ // add outline
242
+ this.brush_preview_graphics.circle(0, 0, preview_size + 1).stroke({
243
+ width: 1,
244
+ color: 0x000000,
245
+ alpha: 0.5
246
+ });
247
+ }
248
+ /**
249
+ * updates the position of the brush preview to center of canvas.
250
+ */
251
+ update_brush_preview_position() {
252
+ if (!this.brush_preview_container)
253
+ return;
254
+ // get the center of the image in global coordinates
255
+ const image_container = this.image_editor_context.image_container;
256
+ const center_x = image_container.width / 2;
257
+ const center_y = image_container.height / 2;
258
+ const global_pos = image_container.toGlobal({ x: center_x, y: center_y });
259
+ // convert to ui coordinates
260
+ const ui_pos = this.image_editor_context.ui_container.toLocal(global_pos);
261
+ // update preview position
262
+ this.brush_preview_container.position.set(ui_pos.x, ui_pos.y);
263
+ }
264
+ /**
265
+ * Called when the pointer enters the image container.
266
+ */
267
+ on_image_container_pointer_enter() {
268
+ this.is_cursor_over_image = true;
269
+ this.update_cursor_and_preview_visibility();
270
+ }
271
+ /**
272
+ * Called when the pointer leaves the image container.
273
+ */
274
+ on_image_container_pointer_leave() {
275
+ this.is_cursor_over_image = false;
276
+ this.update_cursor_and_preview_visibility();
277
+ }
278
+ /**
279
+ * Updates both cursor and preview visibility based on cursor position.
280
+ */
281
+ update_cursor_and_preview_visibility() {
282
+ this.update_cursor_visibility();
283
+ if (this.brush_preview_container) {
284
+ this.brush_preview_container.visible =
285
+ this.is_preview_visible && this.is_brush_or_erase_active;
286
+ }
287
+ }
288
+ /**
289
+ * Cleans up all cursor resources.
290
+ */
291
+ cleanup() {
292
+ // Reset active state
293
+ this.is_brush_or_erase_active = false;
294
+ // clean up cursor
295
+ if (this.cursor_container) {
296
+ if (this.cursor_container.parent) {
297
+ this.cursor_container.parent.removeChild(this.cursor_container);
298
+ }
299
+ this.cursor_container.destroy({ children: true });
300
+ this.cursor_container = null;
301
+ }
302
+ // clean up preview
303
+ if (this.brush_preview_container) {
304
+ if (this.brush_preview_container.parent) {
305
+ this.brush_preview_container.parent.removeChild(this.brush_preview_container);
306
+ }
307
+ this.brush_preview_container.destroy({ children: true });
308
+ this.brush_preview_container = null;
309
+ }
310
+ // clear timeout
311
+ if (this.cursor_position_check_timeout !== null) {
312
+ window.clearTimeout(this.cursor_position_check_timeout);
313
+ this.cursor_position_check_timeout = null;
314
+ }
315
+ }
316
+ /**
317
+ * Updates the brush state.
318
+ */
319
+ update_state(state, scale) {
320
+ this.state = state;
321
+ this.scale = scale;
322
+ this.update_cursor_appearance();
323
+ if (this.is_preview_visible) {
324
+ this.update_brush_preview();
325
+ }
326
+ }
327
+ }
@@ -0,0 +1,71 @@
1
+ import { Application } from "pixi.js";
2
+ import { type ImageEditorContext } from "../core/editor";
3
+ /**
4
+ * Class to handle texture operations for the brush tool.
5
+ * Simplified to work directly with layer textures.
6
+ */
7
+ export declare class BrushTextures {
8
+ private image_editor_context;
9
+ private app;
10
+ private stroke_texture;
11
+ private erase_texture;
12
+ private display_container;
13
+ private stroke_container;
14
+ private stroke_graphics;
15
+ private preview_sprite;
16
+ private erase_graphics;
17
+ private dimensions;
18
+ private is_new_stroke;
19
+ private scale;
20
+ private current_opacity;
21
+ private current_mode;
22
+ private last_from_x;
23
+ private last_from_y;
24
+ private last_to_x;
25
+ private last_to_y;
26
+ private last_size;
27
+ constructor(image_editor_context: ImageEditorContext, app: Application);
28
+ /**
29
+ * Initializes textures needed for the brush tool.
30
+ */
31
+ initialize_textures(): void;
32
+ /**
33
+ * Reinitializes textures when needed (e.g., after resizing).
34
+ */
35
+ reinitialize(): void;
36
+ /**
37
+ * Cleans up texture resources.
38
+ */
39
+ cleanup_textures(): void;
40
+ /**
41
+ * Preserve the canvas state when starting a new stroke.
42
+ */
43
+ preserve_canvas_state(): void;
44
+ /**
45
+ * Resets the eraser mask.
46
+ */
47
+ reset_eraser_mask(): void;
48
+ /**
49
+ * Commits the current stroke to the active layer.
50
+ */
51
+ commit_stroke(): void;
52
+ /**
53
+ * Calculates the distance between two points.
54
+ */
55
+ private calculateDistance;
56
+ /**
57
+ * Draws a segment for the current stroke using a simple "stamp" approach.
58
+ */
59
+ draw_segment(from_x: number, from_y: number, to_x: number, to_y: number, size: number, color: string, opacity: number, mode: "draw" | "erase"): void;
60
+ /**
61
+ * Gets current dimensions of the texture.
62
+ */
63
+ get_dimensions(): {
64
+ width: number;
65
+ height: number;
66
+ };
67
+ /**
68
+ * Cleanup all resources.
69
+ */
70
+ cleanup(): void;
71
+ }