@gradio/imageeditor 0.13.0 → 0.14.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 (36) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/ImageEditor.stories.svelte +9 -1
  3. package/Index.svelte +13 -1
  4. package/InteractiveImageEditor.svelte +10 -1
  5. package/README.md +2 -0
  6. package/dist/Index.svelte +6 -0
  7. package/dist/Index.svelte.d.ts +9 -1
  8. package/dist/InteractiveImageEditor.svelte +7 -0
  9. package/dist/InteractiveImageEditor.svelte.d.ts +5 -1
  10. package/dist/shared/Controls.svelte +13 -1
  11. package/dist/shared/Controls.svelte.d.ts +2 -0
  12. package/dist/shared/ImageEditor.svelte +29 -3
  13. package/dist/shared/ImageEditor.svelte.d.ts +5 -2
  14. package/dist/shared/Layers.svelte +29 -2
  15. package/dist/shared/Layers.svelte.d.ts +2 -0
  16. package/dist/shared/SecondaryToolbar.svelte +1 -0
  17. package/dist/shared/SecondaryToolbar.svelte.d.ts +2 -0
  18. package/dist/shared/brush/brush-textures.d.ts +0 -6
  19. package/dist/shared/brush/brush-textures.js +0 -19
  20. package/dist/shared/brush/brush.js +3 -2
  21. package/dist/shared/core/editor.d.ts +15 -6
  22. package/dist/shared/core/editor.js +76 -33
  23. package/dist/shared/image/image.js +2 -4
  24. package/dist/shared/types.d.ts +4 -0
  25. package/dist/shared/zoom/zoom.js +1 -1
  26. package/package.json +9 -9
  27. package/shared/Controls.svelte +14 -40
  28. package/shared/ImageEditor.svelte +33 -4
  29. package/shared/Layers.svelte +36 -3
  30. package/shared/SecondaryToolbar.svelte +7 -1
  31. package/shared/brush/brush-textures.ts +0 -23
  32. package/shared/brush/brush.ts +6 -2
  33. package/shared/core/editor.ts +102 -44
  34. package/shared/image/image.ts +2 -4
  35. package/shared/types.ts +5 -0
  36. package/shared/zoom/zoom.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # @gradio/imageeditor
2
2
 
3
+ ## 0.14.1
4
+
5
+ ### Dependency updates
6
+
7
+ - @gradio/statustracker@0.10.9
8
+ - @gradio/atoms@0.15.2
9
+ - @gradio/client@1.14.2
10
+ - @gradio/utils@0.10.2
11
+ - @gradio/upload@0.16.2
12
+ - @gradio/image@0.22.2
13
+
14
+ ## 0.14.0
15
+
16
+ ### Features
17
+
18
+ - [#10981](https://github.com/gradio-app/gradio/pull/10981) [`0db230f`](https://github.com/gradio-app/gradio/commit/0db230f5c2f19d9b3b455adb1b30ed9374ae1791) - allow users to change the visibility of layers in the image editor. Thanks @pngwn!
19
+
20
+ ### Fixes
21
+
22
+ - [#10999](https://github.com/gradio-app/gradio/pull/10999) [`8c70819`](https://github.com/gradio-app/gradio/commit/8c70819f87027b75e44f49a13050ae2c3e8b3580) - implement download button for the ImageEditor. Thanks @pngwn!
23
+ - [#10997](https://github.com/gradio-app/gradio/pull/10997) [`45d5840`](https://github.com/gradio-app/gradio/commit/45d5840fb265328f3aa0c050ce9c6b87ae7dbbc5) - When custom layers are provided to the ImageEditor, always default to the first layer. Thanks @pngwn!
24
+ - [#10979](https://github.com/gradio-app/gradio/pull/10979) [`975feee`](https://github.com/gradio-app/gradio/commit/975feee3925e5b65e52ae00da5c5066d8889ae6a) - improve webcam options for the ImageEditor. Thanks @pngwn!
25
+ - [#10995](https://github.com/gradio-app/gradio/pull/10995) [`fc59815`](https://github.com/gradio-app/gradio/commit/fc598151aa9c77a84f6e7eaf9bcc0cae6ba49369) - ensure the image editor background respects the theme mode. Thanks @pngwn!
26
+ - [#10994](https://github.com/gradio-app/gradio/pull/10994) [`714015a`](https://github.com/gradio-app/gradio/commit/714015a1868168721ae79d6b5bc10fbc5bbb4290) - ensure images uploaded to the ImageEditor correctly initialise the canvas dimensions. Thanks @pngwn!
27
+
28
+ ### Dependency updates
29
+
30
+ - @gradio/upload@0.16.1
31
+ - @gradio/atoms@0.15.1
32
+ - @gradio/statustracker@0.10.8
33
+ - @gradio/icons@0.12.0
34
+ - @gradio/image@0.22.1
35
+
3
36
  ## 0.13.0
4
37
 
5
38
  ### Features
@@ -51,6 +51,10 @@
51
51
  },
52
52
  eraser: {
53
53
  default_size: "auto"
54
+ },
55
+ webcam_options: {
56
+ mirror: true,
57
+ constraints: null
54
58
  }
55
59
  }}
56
60
  />
@@ -133,6 +137,10 @@
133
137
  interactive: false,
134
138
  label: "Image Editor",
135
139
  show_label: true,
136
- canvas_size: [800, 600]
140
+ canvas_size: [800, 600],
141
+ webcam_options: {
142
+ mirror: true,
143
+ constraints: null
144
+ }
137
145
  }}
138
146
  />
package/Index.svelte CHANGED
@@ -13,7 +13,13 @@
13
13
  import { StatusTracker } from "@gradio/statustracker";
14
14
  import type { LoadingStatus } from "@gradio/statustracker";
15
15
  import { tick } from "svelte";
16
- import type { LayerOptions, Transform, Source } from "./shared/types";
16
+ import type {
17
+ LayerOptions,
18
+ Transform,
19
+ Source,
20
+ WebcamOptions
21
+ } from "./shared/types";
22
+
17
23
  export let elem_id = "";
18
24
  export let elem_classes: string[] = [];
19
25
  export let visible = true;
@@ -66,6 +72,8 @@
66
72
  clear_status: LoadingStatus;
67
73
  }>;
68
74
  export let border_region = 0;
75
+ export let webcam_options: WebcamOptions;
76
+ export let theme_mode: "dark" | "light";
69
77
 
70
78
  let editor_instance: InteractiveImageEditor;
71
79
  let image_id: null | string = null;
@@ -235,6 +243,10 @@
235
243
  upload={(...args) => gradio.client.upload(...args)}
236
244
  {placeholder}
237
245
  {full_history}
246
+ {webcam_options}
247
+ {show_download_button}
248
+ {theme_mode}
249
+ on:download_error={(e) => gradio.dispatch("error", e.detail)}
238
250
  ></InteractiveImageEditor>
239
251
  </Block>
240
252
  {/if}
@@ -31,8 +31,10 @@
31
31
  import {
32
32
  type LayerOptions,
33
33
  type Transform,
34
- type Source
34
+ type Source,
35
+ type WebcamOptions
35
36
  } from "./shared/types";
37
+
36
38
  export let brush: IBrush;
37
39
  export let eraser: Eraser;
38
40
  export let sources: Source[];
@@ -41,6 +43,7 @@
41
43
  export let label: string | undefined = undefined;
42
44
  export let show_label: boolean;
43
45
  export let changeable = false;
46
+ export let theme_mode: "dark" | "light";
44
47
 
45
48
  export let layers: FileData[];
46
49
  export let composite: FileData | null;
@@ -58,6 +61,8 @@
58
61
  export let placeholder: string | undefined = undefined;
59
62
  export let border_region: number;
60
63
  export let full_history: CommandNode | null = null;
64
+ export let webcam_options: WebcamOptions;
65
+ export let show_download_button = false;
61
66
 
62
67
  const dispatch = createEventDispatcher<{
63
68
  clear?: never;
@@ -220,6 +225,7 @@
220
225
  on:save
221
226
  on:change={handle_change}
222
227
  on:clear={() => dispatch("clear")}
228
+ on:download_error
223
229
  {sources}
224
230
  {full_history}
225
231
  bind:background_image
@@ -234,6 +240,9 @@
234
240
  {upload}
235
241
  bind:is_dragging
236
242
  bind:has_drawn
243
+ {webcam_options}
244
+ {show_download_button}
245
+ {theme_mode}
237
246
  >
238
247
  {#if !background_image && current_tool === "image" && !has_drawn}
239
248
  <div class="empty wrap">
package/README.md CHANGED
@@ -1 +1,3 @@
1
1
  # `@gradio/imageeditor`
2
+
3
+ hello
package/dist/Index.svelte CHANGED
@@ -42,6 +42,8 @@ export let show_fullscreen_button = true;
42
42
  export let full_history = null;
43
43
  export let gradio;
44
44
  export let border_region = 0;
45
+ export let webcam_options;
46
+ export let theme_mode;
45
47
  let editor_instance;
46
48
  let image_id = null;
47
49
  export async function get_value() {
@@ -194,6 +196,10 @@ $:
194
196
  upload={(...args) => gradio.client.upload(...args)}
195
197
  {placeholder}
196
198
  {full_history}
199
+ {webcam_options}
200
+ {show_download_button}
201
+ {theme_mode}
202
+ on:download_error={(e) => gradio.dispatch("error", e.detail)}
197
203
  ></InteractiveImageEditor>
198
204
  </Block>
199
205
  {/if}
@@ -3,7 +3,7 @@ import type { Brush, Eraser } from "./shared/brush/types";
3
3
  import type { EditorData, ImageBlobs } from "./InteractiveImageEditor.svelte";
4
4
  import type { Gradio, SelectData } from "@gradio/utils";
5
5
  import type { LoadingStatus } from "@gradio/statustracker";
6
- import type { LayerOptions, Transform, Source } from "./shared/types";
6
+ import type { LayerOptions, Transform, Source, WebcamOptions } from "./shared/types";
7
7
  declare const __propDef: {
8
8
  props: {
9
9
  elem_id?: string | undefined;
@@ -52,6 +52,8 @@ declare const __propDef: {
52
52
  clear_status: LoadingStatus;
53
53
  }>;
54
54
  border_region?: number | undefined;
55
+ webcam_options: WebcamOptions;
56
+ theme_mode: "dark" | "light";
55
57
  get_value?: (() => Promise<ImageBlobs | {
56
58
  id: string;
57
59
  }>) | undefined;
@@ -194,6 +196,12 @@ export default class Index extends SvelteComponent<IndexProps, IndexEvents, Inde
194
196
  get border_region(): number | undefined;
195
197
  /**accessor*/
196
198
  set border_region(_: number | undefined);
199
+ get webcam_options(): WebcamOptions;
200
+ /**accessor*/
201
+ set webcam_options(_: WebcamOptions);
202
+ get theme_mode(): "light" | "dark";
203
+ /**accessor*/
204
+ set theme_mode(_: "light" | "dark");
197
205
  get undefined(): any;
198
206
  /**accessor*/
199
207
  set undefined(_: any);
@@ -20,6 +20,7 @@ export let root;
20
20
  export let label = void 0;
21
21
  export let show_label;
22
22
  export let changeable = false;
23
+ export let theme_mode;
23
24
  export let layers;
24
25
  export let composite;
25
26
  export let background;
@@ -34,6 +35,8 @@ export let is_dragging;
34
35
  export let placeholder = void 0;
35
36
  export let border_region;
36
37
  export let full_history = null;
38
+ export let webcam_options;
39
+ export let show_download_button = false;
37
40
  const dispatch = createEventDispatcher();
38
41
  let editor;
39
42
  let has_drawn = false;
@@ -168,6 +171,7 @@ let current_tool;
168
171
  on:save
169
172
  on:change={handle_change}
170
173
  on:clear={() => dispatch("clear")}
174
+ on:download_error
171
175
  {sources}
172
176
  {full_history}
173
177
  bind:background_image
@@ -182,6 +186,9 @@ let current_tool;
182
186
  {upload}
183
187
  bind:is_dragging
184
188
  bind:has_drawn
189
+ {webcam_options}
190
+ {show_download_button}
191
+ {theme_mode}
185
192
  >
186
193
  {#if !background_image && current_tool === "image" && !has_drawn}
187
194
  <div class="empty wrap">
@@ -13,7 +13,7 @@ import { type I18nFormatter } from "@gradio/utils";
13
13
  import { type FileData, type Client } from "@gradio/client";
14
14
  import { type CommandNode } from "./shared/utils/commands";
15
15
  import { type Brush as IBrush, type Eraser } from "./shared/brush/types";
16
- import { type LayerOptions, type Transform, type Source } from "./shared/types";
16
+ import { type LayerOptions, type Transform, type Source, type WebcamOptions } from "./shared/types";
17
17
  declare const __propDef: {
18
18
  props: {
19
19
  brush: IBrush;
@@ -24,6 +24,7 @@ declare const __propDef: {
24
24
  label?: string | undefined;
25
25
  show_label: boolean;
26
26
  changeable?: boolean | undefined;
27
+ theme_mode: "dark" | "light";
27
28
  layers: FileData[];
28
29
  composite: FileData | null;
29
30
  background: FileData | null;
@@ -38,12 +39,15 @@ declare const __propDef: {
38
39
  placeholder?: string | undefined;
39
40
  border_region: number;
40
41
  full_history?: (CommandNode | null) | undefined;
42
+ webcam_options: WebcamOptions;
43
+ show_download_button?: boolean | undefined;
41
44
  get_data?: (() => Promise<ImageBlobs>) | undefined;
42
45
  image_id?: (null | string) | undefined;
43
46
  };
44
47
  events: {
45
48
  history: CustomEvent<import("svelte/store").Writable<CommandNode>>;
46
49
  save: CustomEvent<void>;
50
+ download_error: CustomEvent<string>;
47
51
  clear?: CustomEvent<undefined> | undefined;
48
52
  upload?: CustomEvent<undefined> | undefined;
49
53
  change?: CustomEvent<undefined> | undefined;
@@ -1,11 +1,12 @@
1
1
  <script>import { createEventDispatcher } from "svelte";
2
2
  import { IconButton, IconButtonWrapper } from "@gradio/atoms";
3
- import { Check, Trash, ZoomIn, ZoomOut, Pan } from "@gradio/icons";
3
+ import { Check, Trash, ZoomIn, ZoomOut, Pan, Download } from "@gradio/icons";
4
4
  export let can_save = false;
5
5
  export let changeable = false;
6
6
  export let current_zoom = 1;
7
7
  export let tool;
8
8
  export let min_zoom = true;
9
+ export let enable_download = false;
9
10
  const dispatch = createEventDispatcher();
10
11
  let show_zoom_popup = false;
11
12
  function handle_zoom_click(e) {
@@ -26,6 +27,17 @@ $:
26
27
  </script>
27
28
 
28
29
  <IconButtonWrapper>
30
+ {#if enable_download}
31
+ <IconButton
32
+ Icon={Download}
33
+ label="Download"
34
+ on:click={(event) => {
35
+ dispatch("download");
36
+ event.stopPropagation();
37
+ }}
38
+ />
39
+ {/if}
40
+
29
41
  <IconButton
30
42
  Icon={Pan}
31
43
  label="Pan"
@@ -6,6 +6,7 @@ declare const __propDef: {
6
6
  current_zoom?: number | undefined;
7
7
  tool: string;
8
8
  min_zoom?: boolean | undefined;
9
+ enable_download?: boolean | undefined;
9
10
  };
10
11
  events: {
11
12
  remove_image: CustomEvent<void>;
@@ -16,6 +17,7 @@ declare const __propDef: {
16
17
  zoom_out: CustomEvent<void>;
17
18
  set_zoom: CustomEvent<number | "fit">;
18
19
  pan: CustomEvent<void>;
20
+ download: CustomEvent<void>;
19
21
  } & {
20
22
  [evt: string]: CustomEvent<any>;
21
23
  };
@@ -32,7 +32,6 @@ export let brush_options;
32
32
  export let eraser_options;
33
33
  export let fixed_canvas = false;
34
34
  export let root;
35
- export let mirror_webcam = true;
36
35
  export let i18n;
37
36
  export let upload;
38
37
  export let composite;
@@ -41,6 +40,9 @@ export let background;
41
40
  export let border_region = 0;
42
41
  export let layer_options;
43
42
  export let current_tool;
43
+ export let webcam_options;
44
+ export let show_download_button = false;
45
+ export let theme_mode;
44
46
  let pixi_target;
45
47
  let pixi_target_crop;
46
48
  $:
@@ -171,7 +173,8 @@ async function init_image_editor() {
171
173
  tools: ["image", zoom, new ResizeTool(), brush],
172
174
  fixed_canvas,
173
175
  border_region,
174
- layer_options
176
+ layer_options,
177
+ theme_mode
175
178
  });
176
179
  brush.on("change", () => {
177
180
  has_drawn = true;
@@ -391,6 +394,20 @@ async function handle_crop_confirm() {
391
394
  dispatch("change");
392
395
  dispatch("input");
393
396
  }
397
+ async function handle_download() {
398
+ const blobs = await editor.get_blobs();
399
+ const blob = blobs.composite;
400
+ if (!blob) {
401
+ dispatch("download_error", "Unable to generate image to download.");
402
+ return;
403
+ }
404
+ const url = URL.createObjectURL(blob);
405
+ const link = document.createElement("a");
406
+ link.href = url;
407
+ link.download = "image.png";
408
+ link.click();
409
+ URL.revokeObjectURL(url);
410
+ }
394
411
  </script>
395
412
 
396
413
  <div
@@ -427,6 +444,8 @@ async function handle_crop_confirm() {
427
444
  on:pan={(e) => {
428
445
  handle_tool_change({ tool: "pan" });
429
446
  }}
447
+ enable_download={show_download_button}
448
+ on:download={() => handle_download()}
430
449
  />
431
450
  {/if}
432
451
 
@@ -461,11 +480,12 @@ async function handle_crop_confirm() {
461
480
  on:capture={handle_capture}
462
481
  on:error
463
482
  on:drag
464
- {mirror_webcam}
465
483
  streaming={false}
466
484
  mode="image"
467
485
  include_audio={false}
468
486
  {i18n}
487
+ mirror_webcam={webcam_options.mirror}
488
+ webcam_constraints={webcam_options.constraints}
469
489
  />
470
490
  </div>
471
491
  </div>
@@ -480,6 +500,9 @@ async function handle_crop_confirm() {
480
500
  }}
481
501
  on:change_layer={(e) => {
482
502
  editor.set_layer(e.detail);
503
+ if (current_tool === "draw") {
504
+ handle_tool_change({ tool: "draw" });
505
+ }
483
506
  }}
484
507
  on:move_layer={(e) => {
485
508
  editor.move_layer(e.detail.id, e.detail.direction);
@@ -487,6 +510,9 @@ async function handle_crop_confirm() {
487
510
  on:delete_layer={(e) => {
488
511
  editor.delete_layer(e.detail);
489
512
  }}
513
+ on:toggle_layer_visibility={(e) => {
514
+ editor.toggle_layer_visibility(e.detail);
515
+ }}
490
516
  />
491
517
  {/if}
492
518
  {/if}
@@ -6,7 +6,7 @@ import type { I18nFormatter } from "@gradio/utils";
6
6
  import type { Client, FileData } from "@gradio/client";
7
7
  import { type CommandNode } from "./utils/commands";
8
8
  import { type Brush, type Eraser } from "./brush/types";
9
- import type { LayerOptions, Source, Transform } from "./types";
9
+ import type { LayerOptions, Source, Transform, WebcamOptions } from "./types";
10
10
  import { type ImageBlobs } from "./types";
11
11
  declare const __propDef: {
12
12
  props: {
@@ -22,7 +22,6 @@ declare const __propDef: {
22
22
  eraser_options: Eraser;
23
23
  fixed_canvas?: boolean | undefined;
24
24
  root: string;
25
- mirror_webcam?: boolean | undefined;
26
25
  i18n: I18nFormatter;
27
26
  upload: Client["upload"];
28
27
  composite: FileData | null;
@@ -31,6 +30,9 @@ declare const __propDef: {
31
30
  border_region?: number | undefined;
32
31
  layer_options: LayerOptions;
33
32
  current_tool: ToolbarTool;
33
+ webcam_options: WebcamOptions;
34
+ show_download_button?: boolean | undefined;
35
+ theme_mode: "dark" | "light";
34
36
  has_drawn?: boolean | undefined;
35
37
  get_blobs?: (() => Promise<ImageBlobs>) | undefined;
36
38
  add_image?: ((image: Blob | File) => void) | undefined;
@@ -51,6 +53,7 @@ declare const __propDef: {
51
53
  history: CustomEvent<import("svelte/store").Writable<CommandNode>>;
52
54
  upload: CustomEvent<void>;
53
55
  input: CustomEvent<void>;
56
+ download_error: CustomEvent<string>;
54
57
  } & {
55
58
  [evt: string]: CustomEvent<any>;
56
59
  };
@@ -1,5 +1,13 @@
1
1
  <script>import { click_outside } from "./utils/events";
2
- import { Layers, Clear, ArrowUp, ArrowDown, Plus } from "@gradio/icons";
2
+ import {
3
+ Layers,
4
+ Clear,
5
+ ArrowUp,
6
+ ArrowDown,
7
+ Plus,
8
+ Visibility,
9
+ VisibilityOff
10
+ } from "@gradio/icons";
3
11
  import { IconButton } from "@gradio/atoms";
4
12
  import { createEventDispatcher } from "svelte";
5
13
  const dispatch = createEventDispatcher();
@@ -40,8 +48,27 @@ function delete_layer(id) {
40
48
  </button>
41
49
  {#if show_layers}
42
50
  <ul>
43
- {#each $layers.layers as { id, name, user_created }, i (i)}
51
+ {#each $layers.layers as { id, name, user_created, visible }, i (i)}
44
52
  <li>
53
+ {#if !visible}
54
+ <IconButton
55
+ Icon={VisibilityOff}
56
+ size="small"
57
+ on:click={(e) => {
58
+ e.stopPropagation();
59
+ dispatch("toggle_layer_visibility", id);
60
+ }}
61
+ />
62
+ {:else}
63
+ <IconButton
64
+ Icon={Visibility}
65
+ size="small"
66
+ on:click={(e) => {
67
+ e.stopPropagation();
68
+ dispatch("toggle_layer_visibility", id);
69
+ }}
70
+ />
71
+ {/if}
45
72
  <button
46
73
  class:selected_layer={$layers.active_layer === id}
47
74
  aria-label={`layer-${i + 1}`}
@@ -8,6 +8,7 @@ declare const __propDef: {
8
8
  name: string;
9
9
  id: string;
10
10
  user_created: boolean;
11
+ visible: boolean;
11
12
  }[];
12
13
  }>;
13
14
  enable_additional_layers?: boolean | undefined;
@@ -22,6 +23,7 @@ declare const __propDef: {
22
23
  direction: "up" | "down";
23
24
  }>;
24
25
  delete_layer: CustomEvent<string>;
26
+ toggle_layer_visibility: CustomEvent<string>;
25
27
  } & {
26
28
  [evt: string]: CustomEvent<any>;
27
29
  };
@@ -17,6 +17,7 @@ export let show_layers = false;
17
17
  on:change_layer
18
18
  on:move_layer
19
19
  on:delete_layer
20
+ on:toggle_layer_visibility
20
21
  />
21
22
  {/if}
22
23
  </div>
@@ -8,6 +8,7 @@ declare const __propDef: {
8
8
  name: string;
9
9
  id: string;
10
10
  user_created: boolean;
11
+ visible: boolean;
11
12
  }[];
12
13
  }>;
13
14
  enable_additional_layers?: boolean | undefined;
@@ -22,6 +23,7 @@ declare const __propDef: {
22
23
  direction: "up" | "down";
23
24
  }>;
24
25
  delete_layer: CustomEvent<string>;
26
+ toggle_layer_visibility: CustomEvent<string>;
25
27
  } & {
26
28
  [evt: string]: CustomEvent<any>;
27
29
  };
@@ -16,14 +16,8 @@ export declare class BrushTextures {
16
16
  private erase_graphics;
17
17
  private dimensions;
18
18
  private is_new_stroke;
19
- private scale;
20
19
  private current_opacity;
21
20
  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
21
  constructor(image_editor_context: ImageEditorContext, app: Application);
28
22
  /**
29
23
  * Initializes textures needed for the brush tool.
@@ -1,5 +1,4 @@
1
1
  import { Container, RenderTexture, Sprite, Graphics, Application } from "pixi.js";
2
- import { safe_destroy, safe_remove_child } from "./brush-utils";
3
2
  import {} from "../core/editor";
4
3
  /**
5
4
  * Class to handle texture operations for the brush tool.
@@ -19,18 +18,10 @@ export class BrushTextures {
19
18
  dimensions;
20
19
  // Track if a new stroke is starting
21
20
  is_new_stroke = true;
22
- // Current scale factor
23
- scale = 1;
24
21
  // Track the current drawing opacity
25
22
  current_opacity = 1.0;
26
23
  // Track current drawing mode
27
24
  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
25
  constructor(image_editor_context, app) {
35
26
  this.image_editor_context = image_editor_context;
36
27
  this.app = app;
@@ -38,10 +29,6 @@ export class BrushTextures {
38
29
  width: this.image_editor_context.image_container.width,
39
30
  height: this.image_editor_context.image_container.height
40
31
  };
41
- // Subscribe to scale changes
42
- this.image_editor_context.scale.subscribe((scale) => {
43
- this.scale = scale;
44
- });
45
32
  }
46
33
  /**
47
34
  * Initializes textures needed for the brush tool.
@@ -256,12 +243,6 @@ export class BrushTextures {
256
243
  this.current_mode = mode;
257
244
  this.current_opacity =
258
245
  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
246
  // Apply scale to brush size for consistent rendering
266
247
  const scaled_size = size;
267
248
  // For new strokes, clear the graphics and texture completely
@@ -171,8 +171,9 @@ export class BrushTool {
171
171
  * @private
172
172
  */
173
173
  on_pointer_down(event) {
174
- // Don't draw if not over image
175
- if (this.current_tool !== "erase" && this.current_tool !== "draw") {
174
+ const current_layer = this.image_editor_context.layer_manager.get_active_layer();
175
+ if (!current_layer?.visible ||
176
+ (this.current_tool !== "erase" && this.current_tool !== "draw")) {
176
177
  return;
177
178
  }
178
179
  if (this.brush_cursor && !this.brush_cursor.is_over_image())