@gradio/imageeditor 0.14.5 → 0.15.1

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 (39) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/InteractiveImageEditor.svelte +4 -7
  3. package/dist/InteractiveImageEditor.svelte +4 -3
  4. package/dist/InteractiveImageEditor.svelte.d.ts +1 -1
  5. package/dist/shared/Controls.svelte +32 -1
  6. package/dist/shared/Controls.svelte.d.ts +2 -0
  7. package/dist/shared/ImageEditor.svelte +23 -1
  8. package/dist/shared/ImageEditor.svelte.d.ts +2 -1
  9. package/dist/shared/brush/brush-cursor.js +0 -42
  10. package/dist/shared/brush/brush-textures.d.ts +19 -2
  11. package/dist/shared/brush/brush-textures.js +120 -82
  12. package/dist/shared/brush/brush.js +0 -52
  13. package/dist/shared/{utils → core}/commands.d.ts +19 -58
  14. package/dist/shared/core/commands.js +70 -0
  15. package/dist/shared/core/editor.d.ts +4 -91
  16. package/dist/shared/core/editor.js +18 -708
  17. package/dist/shared/core/layers.d.ts +143 -0
  18. package/dist/shared/core/layers.js +774 -0
  19. package/dist/shared/image/image.d.ts +14 -4
  20. package/dist/shared/image/image.js +91 -18
  21. package/dist/shared/resize/resize.d.ts +31 -0
  22. package/dist/shared/resize/resize.js +127 -290
  23. package/dist/shared/zoom/zoom.js +0 -51
  24. package/package.json +7 -7
  25. package/shared/Controls.svelte +33 -1
  26. package/shared/ImageEditor.svelte +25 -9
  27. package/shared/Toolbar.svelte +1 -4
  28. package/shared/brush/ColorSwatch.svelte +0 -3
  29. package/shared/brush/brush-cursor.ts +0 -42
  30. package/shared/brush/brush-textures.ts +166 -83
  31. package/shared/brush/brush.ts +0 -53
  32. package/shared/core/commands.ts +118 -0
  33. package/shared/core/editor.ts +33 -957
  34. package/shared/core/layers.ts +1068 -0
  35. package/shared/image/image.ts +139 -21
  36. package/shared/resize/resize.ts +160 -299
  37. package/shared/zoom/zoom.ts +0 -51
  38. package/dist/shared/utils/commands.js +0 -73
  39. package/shared/utils/commands.ts +0 -174
@@ -10,7 +10,7 @@ import {
10
10
  } from "pixi.js";
11
11
  import { type ImageEditorContext, type Tool } from "../core/editor";
12
12
  import { type Tool as ToolbarTool, type Subtool } from "../Toolbar.svelte";
13
- import { type Command } from "../utils/commands";
13
+ import { type Command } from "../core/commands";
14
14
  import { make_graphics } from "../utils/pixi";
15
15
  import { get } from "svelte/store";
16
16
 
@@ -66,15 +66,16 @@ interface BgImageCommand extends Command {
66
66
  * Initial setup for the bg command
67
67
  * @returns
68
68
  */
69
- start: () => Promise<[number, number]>;
69
+ start: () => Promise<void>;
70
70
  }
71
71
 
72
72
  export class AddImageCommand implements BgImageCommand {
73
- sprite: Sprite;
73
+ sprite: Sprite | null = null;
74
74
  fixed_canvas: boolean;
75
75
  context: ImageEditorContext;
76
76
  background: Blob | File | Texture;
77
77
  current_canvas_size: { width: number; height: number };
78
+ computed_dimensions: { width: number; height: number };
78
79
  current_scale: number;
79
80
  current_position: { x: number; y: number };
80
81
  border_region: number;
@@ -82,12 +83,23 @@ export class AddImageCommand implements BgImageCommand {
82
83
  scaled_width!: number;
83
84
  scaled_height!: number;
84
85
 
86
+ private previous_image: {
87
+ texture: Texture;
88
+ width: number;
89
+ height: number;
90
+ x: number;
91
+ y: number;
92
+ border_region: number;
93
+ } | null = null;
94
+ name: string;
95
+
85
96
  constructor(
86
97
  context: ImageEditorContext,
87
98
  background: Blob | File | Texture,
88
99
  fixed_canvas: boolean,
89
100
  border_region = 0
90
101
  ) {
102
+ this.name = "AddImage";
91
103
  this.context = context;
92
104
  this.background = background;
93
105
  this.fixed_canvas = fixed_canvas;
@@ -95,11 +107,50 @@ export class AddImageCommand implements BgImageCommand {
95
107
  this.current_canvas_size = get(this.context.dimensions);
96
108
  this.current_scale = get(this.context.scale);
97
109
  this.current_position = get(this.context.position);
98
- this.sprite = new Sprite();
110
+
111
+ this.computed_dimensions = { width: 0, height: 0 };
112
+
113
+ if (
114
+ this.context.background_image &&
115
+ this.context.background_image.texture
116
+ ) {
117
+ const bg = this.context.background_image;
118
+ const stored_border_region = (bg as any).borderRegion || 0;
119
+
120
+ this.previous_image = {
121
+ texture: this.clone_texture(bg.texture),
122
+ width: bg.width,
123
+ height: bg.height,
124
+ x: bg.position.x,
125
+ y: bg.position.y,
126
+ border_region: stored_border_region
127
+ };
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Creates a copy of a texture to avoid reference issues
133
+ */
134
+ private clone_texture(texture: Texture): Texture {
135
+ const render_texture = RenderTexture.create({
136
+ width: texture.width,
137
+ height: texture.height,
138
+ resolution: window.devicePixelRatio || 1
139
+ });
140
+
141
+ const sprite = new Sprite(texture);
142
+ const container = new Container();
143
+ container.addChild(sprite);
144
+
145
+ this.context.app.renderer.render(container, {
146
+ renderTexture: render_texture
147
+ });
148
+
149
+ container.destroy({ children: true });
150
+ return render_texture;
99
151
  }
100
152
 
101
- async start(): Promise<[number, number]> {
102
- // Create the sprite from the blob
153
+ async start(): Promise<void> {
103
154
  let image_texture: Texture;
104
155
  if (this.background instanceof Texture) {
105
156
  image_texture = this.background;
@@ -110,13 +161,15 @@ export class AddImageCommand implements BgImageCommand {
110
161
 
111
162
  this.sprite = new Sprite(image_texture);
112
163
 
113
- return this.handle_image();
164
+ const [width, height] = this.handle_image();
165
+ this.computed_dimensions = { width, height };
114
166
  }
115
167
 
116
168
  private handle_image(): [number, number] {
117
- // Handle fixed canvas differently when border region is present
169
+ if (this.sprite === null) {
170
+ return [0, 0];
171
+ }
118
172
  if (this.fixed_canvas) {
119
- // Calculate the effective canvas size accounting for border region
120
173
  const effectiveCanvasWidth = Math.max(
121
174
  this.current_canvas_size.width - this.border_region * 2,
122
175
  10
@@ -126,8 +179,6 @@ export class AddImageCommand implements BgImageCommand {
126
179
  10
127
180
  );
128
181
 
129
- // Fit the image to the canvas while maintaining aspect ratio
130
-
131
182
  const { width, height, x, y } = fit_image_to_canvas(
132
183
  this.sprite.width,
133
184
  this.sprite.height,
@@ -138,15 +189,12 @@ export class AddImageCommand implements BgImageCommand {
138
189
  this.sprite.width = width;
139
190
  this.sprite.height = height;
140
191
 
141
- // Center the image in the canvas
142
192
  this.sprite.x = x + this.border_region;
143
193
  this.sprite.y = y + this.border_region;
144
194
  } else {
145
- // For non-fixed canvas, use natural dimensions plus border
146
195
  const width = this.sprite.width;
147
196
  const height = this.sprite.height;
148
197
 
149
- // Position at the border's offset from origin
150
198
  this.sprite.x = this.border_region;
151
199
  this.sprite.y = this.border_region;
152
200
 
@@ -157,12 +205,16 @@ export class AddImageCommand implements BgImageCommand {
157
205
  }
158
206
 
159
207
  async execute(): Promise<void> {
160
- // First ensure we have run start() to create the sprite and get dimensions
161
- const [width, height] = await this.start();
208
+ await this.start();
209
+
210
+ if (this.sprite === null) {
211
+ return;
212
+ }
213
+
214
+ const { width, height } = this.computed_dimensions;
162
215
 
163
- // Update image properties with the original dimensions and center in viewport
164
216
  await this.context.set_image_properties({
165
- scale: 1, // Start at 1:1 scale
217
+ scale: 1,
166
218
  position: {
167
219
  x: this.context.app.screen.width / 2,
168
220
  y: this.context.app.screen.height / 2
@@ -171,7 +223,6 @@ export class AddImageCommand implements BgImageCommand {
171
223
  height: this.fixed_canvas ? this.current_canvas_size.height : height
172
224
  });
173
225
 
174
- // Create new background layer and add the sprite
175
226
  const background_layer = this.context.layer_manager.create_background_layer(
176
227
  this.fixed_canvas ? this.current_canvas_size.width : width,
177
228
  this.fixed_canvas ? this.current_canvas_size.height : height
@@ -179,13 +230,16 @@ export class AddImageCommand implements BgImageCommand {
179
230
  this.sprite.zIndex = 0;
180
231
  background_layer.addChild(this.sprite);
181
232
 
182
- // Resize and preserve content of existing layers
183
233
  this.context.layer_manager.reset_layers(
184
234
  this.fixed_canvas ? this.current_canvas_size.width : width,
185
235
  this.fixed_canvas ? this.current_canvas_size.height : height,
186
236
  true
187
237
  );
188
238
 
239
+ if (this.border_region > 0) {
240
+ (this.sprite as any).borderRegion = this.border_region;
241
+ }
242
+
189
243
  this.context.set_background_image(this.sprite);
190
244
 
191
245
  this.context.reset();
@@ -194,6 +248,70 @@ export class AddImageCommand implements BgImageCommand {
194
248
  async undo(): Promise<void> {
195
249
  if (this.sprite) {
196
250
  this.sprite.destroy();
251
+
252
+ if (this.previous_image) {
253
+ const previous_sprite = new Sprite(this.previous_image.texture);
254
+ previous_sprite.width = this.previous_image.width;
255
+ previous_sprite.height = this.previous_image.height;
256
+ previous_sprite.position.set(
257
+ this.previous_image.x,
258
+ this.previous_image.y
259
+ );
260
+
261
+ if (this.previous_image.border_region > 0) {
262
+ (previous_sprite as any).borderRegion =
263
+ this.previous_image.border_region;
264
+ }
265
+
266
+ await this.context.set_image_properties({
267
+ scale: 1,
268
+ position: {
269
+ x: this.context.app.screen.width / 2,
270
+ y: this.context.app.screen.height / 2
271
+ },
272
+ width: this.previous_image.width,
273
+ height: this.previous_image.height
274
+ });
275
+
276
+ const background_layer =
277
+ this.context.layer_manager.create_background_layer(
278
+ this.previous_image.width,
279
+ this.previous_image.height
280
+ );
281
+
282
+ previous_sprite.zIndex = 0;
283
+ background_layer.addChild(previous_sprite);
284
+
285
+ this.context.layer_manager.reset_layers(
286
+ this.previous_image.width,
287
+ this.previous_image.height,
288
+ true
289
+ );
290
+
291
+ this.context.set_background_image(previous_sprite);
292
+ } else {
293
+ await this.context.set_image_properties({
294
+ scale: 1,
295
+ position: {
296
+ x: this.context.app.screen.width / 2,
297
+ y: this.context.app.screen.height / 2
298
+ },
299
+ width: this.current_canvas_size.width,
300
+ height: this.current_canvas_size.height
301
+ });
302
+
303
+ this.context.layer_manager.create_background_layer(
304
+ this.current_canvas_size.width,
305
+ this.current_canvas_size.height
306
+ );
307
+
308
+ this.context.layer_manager.reset_layers(
309
+ this.current_canvas_size.width,
310
+ this.current_canvas_size.height
311
+ );
312
+ }
313
+
314
+ this.context.reset();
197
315
  }
198
316
  }
199
317
  }
@@ -229,6 +347,7 @@ export function add_bg_color(
229
347
  ): BgColorCommand {
230
348
  let sprite: Sprite;
231
349
  return {
350
+ name: "AddBgColor",
232
351
  start() {
233
352
  container.removeChildren();
234
353
  const graphics = make_graphics(1);
@@ -285,7 +404,6 @@ export function fit_image_to_canvas(
285
404
  new_width = canvas_height * image_aspect_ratio;
286
405
  }
287
406
 
288
- // center image
289
407
  const x = Math.round((canvas_width - new_width) / 2);
290
408
  const y = Math.round((canvas_height - new_height) / 2);
291
409