@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
@@ -1,7 +1,7 @@
1
1
  import { Container, type ColorSource, Sprite, type Renderer, Texture } from "pixi.js";
2
2
  import { type ImageEditorContext, type Tool } from "../core/editor";
3
3
  import { type Tool as ToolbarTool, type Subtool } from "../Toolbar.svelte";
4
- import { type Command } from "../utils/commands";
4
+ import { type Command } from "../core/commands";
5
5
  /**
6
6
  * Handles adding images to the editor
7
7
  * Only a single image can be added at a time
@@ -26,10 +26,10 @@ interface BgImageCommand extends Command {
26
26
  * Initial setup for the bg command
27
27
  * @returns
28
28
  */
29
- start: () => Promise<[number, number]>;
29
+ start: () => Promise<void>;
30
30
  }
31
31
  export declare class AddImageCommand implements BgImageCommand {
32
- sprite: Sprite;
32
+ sprite: Sprite | null;
33
33
  fixed_canvas: boolean;
34
34
  context: ImageEditorContext;
35
35
  background: Blob | File | Texture;
@@ -37,6 +37,10 @@ export declare class AddImageCommand implements BgImageCommand {
37
37
  width: number;
38
38
  height: number;
39
39
  };
40
+ computed_dimensions: {
41
+ width: number;
42
+ height: number;
43
+ };
40
44
  current_scale: number;
41
45
  current_position: {
42
46
  x: number;
@@ -45,8 +49,14 @@ export declare class AddImageCommand implements BgImageCommand {
45
49
  border_region: number;
46
50
  scaled_width: number;
47
51
  scaled_height: number;
52
+ private previous_image;
53
+ name: string;
48
54
  constructor(context: ImageEditorContext, background: Blob | File | Texture, fixed_canvas: boolean, border_region?: number);
49
- start(): Promise<[number, number]>;
55
+ /**
56
+ * Creates a copy of a texture to avoid reference issues
57
+ */
58
+ private clone_texture;
59
+ start(): Promise<void>;
50
60
  private handle_image;
51
61
  execute(): Promise<void>;
52
62
  undo(): Promise<void>;
@@ -1,7 +1,7 @@
1
1
  import { Container, Graphics, Color, RenderTexture, Sprite, Texture } from "pixi.js";
2
2
  import {} from "../core/editor";
3
3
  import {} from "../Toolbar.svelte";
4
- import {} from "../utils/commands";
4
+ import {} from "../core/commands";
5
5
  import { make_graphics } from "../utils/pixi";
6
6
  import { get } from "svelte/store";
7
7
  /**
@@ -30,17 +30,21 @@ export class ImageTool {
30
30
  }
31
31
  }
32
32
  export class AddImageCommand {
33
- sprite;
33
+ sprite = null;
34
34
  fixed_canvas;
35
35
  context;
36
36
  background;
37
37
  current_canvas_size;
38
+ computed_dimensions;
38
39
  current_scale;
39
40
  current_position;
40
41
  border_region;
41
42
  scaled_width;
42
43
  scaled_height;
44
+ previous_image = null;
45
+ name;
43
46
  constructor(context, background, fixed_canvas, border_region = 0) {
47
+ this.name = "AddImage";
44
48
  this.context = context;
45
49
  this.background = background;
46
50
  this.fixed_canvas = fixed_canvas;
@@ -48,10 +52,40 @@ export class AddImageCommand {
48
52
  this.current_canvas_size = get(this.context.dimensions);
49
53
  this.current_scale = get(this.context.scale);
50
54
  this.current_position = get(this.context.position);
51
- this.sprite = new Sprite();
55
+ this.computed_dimensions = { width: 0, height: 0 };
56
+ if (this.context.background_image &&
57
+ this.context.background_image.texture) {
58
+ const bg = this.context.background_image;
59
+ const stored_border_region = bg.borderRegion || 0;
60
+ this.previous_image = {
61
+ texture: this.clone_texture(bg.texture),
62
+ width: bg.width,
63
+ height: bg.height,
64
+ x: bg.position.x,
65
+ y: bg.position.y,
66
+ border_region: stored_border_region
67
+ };
68
+ }
69
+ }
70
+ /**
71
+ * Creates a copy of a texture to avoid reference issues
72
+ */
73
+ clone_texture(texture) {
74
+ const render_texture = RenderTexture.create({
75
+ width: texture.width,
76
+ height: texture.height,
77
+ resolution: window.devicePixelRatio || 1
78
+ });
79
+ const sprite = new Sprite(texture);
80
+ const container = new Container();
81
+ container.addChild(sprite);
82
+ this.context.app.renderer.render(container, {
83
+ renderTexture: render_texture
84
+ });
85
+ container.destroy({ children: true });
86
+ return render_texture;
52
87
  }
53
88
  async start() {
54
- // Create the sprite from the blob
55
89
  let image_texture;
56
90
  if (this.background instanceof Texture) {
57
91
  image_texture = this.background;
@@ -61,27 +95,25 @@ export class AddImageCommand {
61
95
  image_texture = Texture.from(img);
62
96
  }
63
97
  this.sprite = new Sprite(image_texture);
64
- return this.handle_image();
98
+ const [width, height] = this.handle_image();
99
+ this.computed_dimensions = { width, height };
65
100
  }
66
101
  handle_image() {
67
- // Handle fixed canvas differently when border region is present
102
+ if (this.sprite === null) {
103
+ return [0, 0];
104
+ }
68
105
  if (this.fixed_canvas) {
69
- // Calculate the effective canvas size accounting for border region
70
106
  const effectiveCanvasWidth = Math.max(this.current_canvas_size.width - this.border_region * 2, 10);
71
107
  const effectiveCanvasHeight = Math.max(this.current_canvas_size.height - this.border_region * 2, 10);
72
- // Fit the image to the canvas while maintaining aspect ratio
73
108
  const { width, height, x, y } = fit_image_to_canvas(this.sprite.width, this.sprite.height, effectiveCanvasWidth, effectiveCanvasHeight);
74
109
  this.sprite.width = width;
75
110
  this.sprite.height = height;
76
- // Center the image in the canvas
77
111
  this.sprite.x = x + this.border_region;
78
112
  this.sprite.y = y + this.border_region;
79
113
  }
80
114
  else {
81
- // For non-fixed canvas, use natural dimensions plus border
82
115
  const width = this.sprite.width;
83
116
  const height = this.sprite.height;
84
- // Position at the border's offset from origin
85
117
  this.sprite.x = this.border_region;
86
118
  this.sprite.y = this.border_region;
87
119
  return [width + this.border_region * 2, height + this.border_region * 2];
@@ -89,11 +121,13 @@ export class AddImageCommand {
89
121
  return [this.current_canvas_size.width, this.current_canvas_size.height];
90
122
  }
91
123
  async execute() {
92
- // First ensure we have run start() to create the sprite and get dimensions
93
- const [width, height] = await this.start();
94
- // Update image properties with the original dimensions and center in viewport
124
+ await this.start();
125
+ if (this.sprite === null) {
126
+ return;
127
+ }
128
+ const { width, height } = this.computed_dimensions;
95
129
  await this.context.set_image_properties({
96
- scale: 1, // Start at 1:1 scale
130
+ scale: 1,
97
131
  position: {
98
132
  x: this.context.app.screen.width / 2,
99
133
  y: this.context.app.screen.height / 2
@@ -101,18 +135,57 @@ export class AddImageCommand {
101
135
  width: this.fixed_canvas ? this.current_canvas_size.width : width,
102
136
  height: this.fixed_canvas ? this.current_canvas_size.height : height
103
137
  });
104
- // Create new background layer and add the sprite
105
138
  const background_layer = this.context.layer_manager.create_background_layer(this.fixed_canvas ? this.current_canvas_size.width : width, this.fixed_canvas ? this.current_canvas_size.height : height);
106
139
  this.sprite.zIndex = 0;
107
140
  background_layer.addChild(this.sprite);
108
- // Resize and preserve content of existing layers
109
141
  this.context.layer_manager.reset_layers(this.fixed_canvas ? this.current_canvas_size.width : width, this.fixed_canvas ? this.current_canvas_size.height : height, true);
142
+ if (this.border_region > 0) {
143
+ this.sprite.borderRegion = this.border_region;
144
+ }
110
145
  this.context.set_background_image(this.sprite);
111
146
  this.context.reset();
112
147
  }
113
148
  async undo() {
114
149
  if (this.sprite) {
115
150
  this.sprite.destroy();
151
+ if (this.previous_image) {
152
+ const previous_sprite = new Sprite(this.previous_image.texture);
153
+ previous_sprite.width = this.previous_image.width;
154
+ previous_sprite.height = this.previous_image.height;
155
+ previous_sprite.position.set(this.previous_image.x, this.previous_image.y);
156
+ if (this.previous_image.border_region > 0) {
157
+ previous_sprite.borderRegion =
158
+ this.previous_image.border_region;
159
+ }
160
+ await this.context.set_image_properties({
161
+ scale: 1,
162
+ position: {
163
+ x: this.context.app.screen.width / 2,
164
+ y: this.context.app.screen.height / 2
165
+ },
166
+ width: this.previous_image.width,
167
+ height: this.previous_image.height
168
+ });
169
+ const background_layer = this.context.layer_manager.create_background_layer(this.previous_image.width, this.previous_image.height);
170
+ previous_sprite.zIndex = 0;
171
+ background_layer.addChild(previous_sprite);
172
+ this.context.layer_manager.reset_layers(this.previous_image.width, this.previous_image.height, true);
173
+ this.context.set_background_image(previous_sprite);
174
+ }
175
+ else {
176
+ await this.context.set_image_properties({
177
+ scale: 1,
178
+ position: {
179
+ x: this.context.app.screen.width / 2,
180
+ y: this.context.app.screen.height / 2
181
+ },
182
+ width: this.current_canvas_size.width,
183
+ height: this.current_canvas_size.height
184
+ });
185
+ this.context.layer_manager.create_background_layer(this.current_canvas_size.width, this.current_canvas_size.height);
186
+ this.context.layer_manager.reset_layers(this.current_canvas_size.width, this.current_canvas_size.height);
187
+ }
188
+ this.context.reset();
116
189
  }
117
190
  }
118
191
  }
@@ -129,6 +202,7 @@ export class AddImageCommand {
129
202
  export function add_bg_color(container, renderer, color, width, height, resize) {
130
203
  let sprite;
131
204
  return {
205
+ name: "AddBgColor",
132
206
  start() {
133
207
  container.removeChildren();
134
208
  const graphics = make_graphics(1);
@@ -171,7 +245,6 @@ export function fit_image_to_canvas(image_width, image_height, canvas_width, can
171
245
  new_height = canvas_height;
172
246
  new_width = canvas_height * image_aspect_ratio;
173
247
  }
174
- // center image
175
248
  const x = Math.round((canvas_width - new_width) / 2);
176
249
  const y = Math.round((canvas_height - new_height) / 2);
177
250
  return {
@@ -1,5 +1,35 @@
1
1
  import { type ImageEditorContext, type Tool } from "../core/editor";
2
2
  import { type Tool as ToolbarTool, type Subtool } from "../Toolbar.svelte";
3
+ import { type Command } from "../core/commands";
4
+ /**
5
+ * Command that handles resizing the image and supports undo/redo
6
+ */
7
+ export declare class ResizeCommand implements Command {
8
+ private context;
9
+ private original_width;
10
+ private original_height;
11
+ private original_x;
12
+ private original_y;
13
+ private new_width;
14
+ private new_height;
15
+ private new_x;
16
+ private new_y;
17
+ private reset_ui;
18
+ name: string;
19
+ constructor(context: ImageEditorContext, original_state: {
20
+ width: number;
21
+ height: number;
22
+ x: number;
23
+ y: number;
24
+ }, new_state: {
25
+ width: number;
26
+ height: number;
27
+ x: number;
28
+ y: number;
29
+ }, reset_ui: () => void);
30
+ execute(): Promise<void>;
31
+ undo(): Promise<void>;
32
+ }
3
33
  /**
4
34
  * @class ResizeTool
5
35
  * @implements {Tool}
@@ -31,6 +61,7 @@ export declare class ResizeTool implements Tool {
31
61
  private dom_mouseup_handler;
32
62
  private event_callbacks;
33
63
  private last_scale;
64
+ private original_state;
34
65
  /**
35
66
  * @method setup
36
67
  * @async