@gradio/imageeditor 0.12.14 → 0.14.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.
- package/CHANGELOG.md +36 -0
- package/Example.svelte +1 -1
- package/IMAGE_EDITOR_OVERVIEW.md +224 -0
- package/ImageEditor.stories.svelte +13 -5
- package/Index.svelte +46 -45
- package/{shared/InteractiveImageEditor.svelte → InteractiveImageEditor.svelte} +65 -103
- package/README.md +2 -0
- package/dist/Example.svelte.d.ts +1 -1
- package/dist/Index.svelte +35 -33
- package/dist/Index.svelte.d.ts +25 -16
- package/dist/{shared/InteractiveImageEditor.svelte → InteractiveImageEditor.svelte} +54 -88
- package/dist/{shared/InteractiveImageEditor.svelte.d.ts → InteractiveImageEditor.svelte.d.ts} +17 -15
- package/dist/shared/Anchor.svelte +311 -0
- package/dist/shared/Anchor.svelte.d.ts +18 -0
- package/dist/shared/Controls.svelte +169 -18
- package/dist/shared/Controls.svelte.d.ts +9 -2
- package/dist/shared/{tools/IconButton.svelte → IconButton.svelte} +19 -3
- package/dist/shared/{tools/IconButton.svelte.d.ts → IconButton.svelte.d.ts} +4 -1
- package/dist/shared/ImageEditor.svelte +572 -289
- package/dist/shared/ImageEditor.svelte.d.ts +54 -56
- package/dist/shared/Layers.svelte +260 -0
- package/dist/shared/Layers.svelte.d.ts +37 -0
- package/dist/shared/Resize.svelte +218 -0
- package/dist/shared/Resize.svelte.d.ts +29 -0
- package/dist/shared/SecondaryToolbar.svelte +55 -0
- package/dist/shared/SecondaryToolbar.svelte.d.ts +37 -0
- package/dist/shared/Toolbar.svelte +337 -0
- package/dist/shared/Toolbar.svelte.d.ts +42 -0
- package/dist/shared/{tools → brush}/BrushOptions.svelte +68 -64
- package/dist/shared/{tools → brush}/BrushOptions.svelte.d.ts +9 -9
- package/dist/shared/{tools → brush}/BrushSize.svelte +2 -27
- package/dist/shared/{tools → brush}/ColorField.svelte +2 -1
- package/dist/shared/{tools → brush}/ColorField.svelte.d.ts +1 -0
- package/dist/shared/{tools → brush}/ColorPicker.svelte +81 -9
- package/dist/shared/{tools → brush}/ColorPicker.svelte.d.ts +1 -0
- package/dist/shared/{tools → brush}/ColorSwatch.svelte +78 -38
- package/dist/shared/{tools → brush}/ColorSwatch.svelte.d.ts +6 -2
- package/dist/shared/brush/brush-cursor.d.ts +101 -0
- package/dist/shared/brush/brush-cursor.js +327 -0
- package/dist/shared/brush/brush-textures.d.ts +65 -0
- package/dist/shared/brush/brush-textures.js +385 -0
- package/dist/shared/brush/brush-utils.d.ts +32 -0
- package/dist/shared/brush/brush-utils.js +59 -0
- package/dist/shared/brush/brush.d.ts +152 -0
- package/dist/shared/brush/brush.js +397 -0
- package/dist/shared/brush/types.d.ts +37 -0
- package/dist/shared/brush/types.js +1 -0
- package/dist/shared/core/editor.d.ts +261 -0
- package/dist/shared/core/editor.js +1264 -0
- package/dist/shared/crop/crop.d.ts +201 -0
- package/dist/shared/crop/crop.js +727 -0
- package/dist/shared/image/image.d.ts +89 -0
- package/dist/shared/image/image.js +183 -0
- package/dist/shared/resize/resize.d.ts +270 -0
- package/dist/shared/resize/resize.js +1413 -0
- package/dist/shared/types.d.ts +16 -0
- package/dist/shared/utils/pixi.d.ts +16 -57
- package/dist/shared/utils/pixi.js +15 -113
- package/dist/shared/zoom/zoom.d.ts +136 -0
- package/dist/shared/zoom/zoom.js +548 -0
- package/package.json +8 -7
- package/shared/Anchor.svelte +384 -0
- package/shared/Controls.svelte +178 -37
- package/shared/{tools/IconButton.svelte → IconButton.svelte} +21 -4
- package/shared/ImageEditor.svelte +712 -350
- package/shared/Layers.svelte +282 -0
- package/shared/Resize.svelte +239 -0
- package/shared/SecondaryToolbar.svelte +67 -0
- package/shared/Toolbar.svelte +386 -0
- package/shared/brush/BRUSH_TOOL.md +134 -0
- package/shared/brush/BrushOptions.svelte +200 -0
- package/shared/{tools → brush}/BrushSize.svelte +2 -30
- package/shared/{tools → brush}/ColorField.svelte +3 -1
- package/shared/{tools → brush}/ColorPicker.svelte +86 -9
- package/shared/{tools → brush}/ColorSwatch.svelte +95 -42
- package/shared/brush/brush-cursor.ts +408 -0
- package/shared/brush/brush-textures.ts +485 -0
- package/shared/brush/brush-utils.ts +76 -0
- package/shared/brush/brush.ts +510 -0
- package/shared/brush/types.ts +41 -0
- package/shared/core/EDITOR.md +182 -0
- package/shared/core/editor.ts +1788 -0
- package/shared/crop/CROP.md +139 -0
- package/shared/crop/crop.ts +1027 -0
- package/shared/image/IMAGE.md +135 -0
- package/shared/image/image.ts +298 -0
- package/shared/resize/resize.ts +1894 -0
- package/shared/types.ts +19 -0
- package/shared/utils/pixi.ts +19 -246
- package/shared/zoom/zoom.ts +729 -0
- package/dist/shared/layers/Layers.svelte +0 -231
- package/dist/shared/layers/Layers.svelte.d.ts +0 -18
- package/dist/shared/layers/utils.d.ts +0 -73
- package/dist/shared/layers/utils.js +0 -156
- package/dist/shared/tools/Brush.svelte +0 -215
- package/dist/shared/tools/Brush.svelte.d.ts +0 -29
- package/dist/shared/tools/Crop.svelte +0 -155
- package/dist/shared/tools/Crop.svelte.d.ts +0 -16
- package/dist/shared/tools/Cropper.svelte +0 -271
- package/dist/shared/tools/Cropper.svelte.d.ts +0 -41
- package/dist/shared/tools/Handle.svelte +0 -260
- package/dist/shared/tools/Handle.svelte.d.ts +0 -29
- package/dist/shared/tools/Sources.svelte +0 -257
- package/dist/shared/tools/Sources.svelte.d.ts +0 -34
- package/dist/shared/tools/Tools.svelte +0 -102
- package/dist/shared/tools/Tools.svelte.d.ts +0 -48
- package/dist/shared/tools/brush.d.ts +0 -62
- package/dist/shared/tools/brush.js +0 -133
- package/dist/shared/tools/crop.d.ts +0 -15
- package/dist/shared/tools/crop.js +0 -154
- package/dist/shared/tools/index.d.ts +0 -5
- package/dist/shared/tools/index.js +0 -5
- package/dist/shared/tools/sources.d.ts +0 -46
- package/dist/shared/tools/sources.js +0 -121
- package/dist/shared/tools/types.d.ts +0 -1
- package/shared/layers/Layers.svelte +0 -250
- package/shared/layers/utils.ts +0 -294
- package/shared/tools/Brush.svelte +0 -280
- package/shared/tools/BrushOptions.svelte +0 -180
- package/shared/tools/Crop.svelte +0 -189
- package/shared/tools/Cropper.svelte +0 -346
- package/shared/tools/Handle.svelte +0 -289
- package/shared/tools/Sources.svelte +0 -283
- package/shared/tools/Tools.svelte +0 -162
- package/shared/tools/brush.ts +0 -247
- package/shared/tools/crop.ts +0 -213
- package/shared/tools/index.ts +0 -5
- package/shared/tools/sources.ts +0 -195
- package/shared/tools/types.ts +0 -1
- /package/dist/shared/{tools → brush}/BrushSize.svelte.d.ts +0 -0
- /package/dist/shared/{tools/types.js → types.js} +0 -0
|
@@ -1,351 +1,634 @@
|
|
|
1
|
-
<script context="module">
|
|
2
|
-
import {} from "./utils/commands";
|
|
3
|
-
export const EDITOR_KEY = Symbol("editor");
|
|
4
|
-
import {} from "./tools";
|
|
1
|
+
<script context="module">export const EDITOR_KEY = Symbol("editor");
|
|
5
2
|
</script>
|
|
6
3
|
|
|
7
|
-
<script>import { onMount,
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {} from "
|
|
13
|
-
import {
|
|
4
|
+
<script>import { onMount, createEventDispatcher, tick } from "svelte";
|
|
5
|
+
import Toolbar, {} from "./Toolbar.svelte";
|
|
6
|
+
import { CropTool } from "./crop/crop";
|
|
7
|
+
import { ResizeTool } from "./resize/resize";
|
|
8
|
+
import { Webcam } from "@gradio/image";
|
|
9
|
+
import tinycolor, {} from "tinycolor2";
|
|
10
|
+
import { ZoomTool } from "./zoom/zoom";
|
|
11
|
+
import {} from "./utils/commands";
|
|
12
|
+
import { ImageEditor } from "./core/editor";
|
|
13
|
+
import {} from "./brush/types";
|
|
14
|
+
import { BrushTool } from "./brush/brush";
|
|
15
|
+
import { create_drag } from "@gradio/upload";
|
|
16
|
+
import SecondaryToolbar from "./SecondaryToolbar.svelte";
|
|
17
|
+
import { Check } from "@gradio/icons";
|
|
18
|
+
import {} from "./types";
|
|
14
19
|
import Controls from "./Controls.svelte";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
export let changeable = false;
|
|
18
|
-
export let history;
|
|
19
|
-
export let bg = false;
|
|
20
|
-
export let sources;
|
|
20
|
+
import IconButton from "./IconButton.svelte";
|
|
21
|
+
const { drag, open_file_upload } = create_drag();
|
|
21
22
|
const dispatch = createEventDispatcher();
|
|
22
|
-
export
|
|
23
|
+
export const antialias = true;
|
|
24
|
+
export const full_history = null;
|
|
25
|
+
export let changeable = false;
|
|
26
|
+
export let sources = ["upload", "webcam", "clipboard"];
|
|
27
|
+
export let transforms = ["crop", "resize"];
|
|
23
28
|
export let canvas_size;
|
|
24
|
-
export let
|
|
25
|
-
export let
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
let
|
|
30
|
-
export let
|
|
31
|
-
let
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
child_bottom: 0
|
|
44
|
-
});
|
|
45
|
-
export let canvas_height = void 0;
|
|
46
|
-
$:
|
|
47
|
-
height = $editor_box.child_height;
|
|
29
|
+
export let is_dragging = false;
|
|
30
|
+
export let background_image = false;
|
|
31
|
+
export let brush_options;
|
|
32
|
+
export let eraser_options;
|
|
33
|
+
export let fixed_canvas = false;
|
|
34
|
+
export let root;
|
|
35
|
+
export let i18n;
|
|
36
|
+
export let upload;
|
|
37
|
+
export let composite;
|
|
38
|
+
export let layers;
|
|
39
|
+
export let background;
|
|
40
|
+
export let border_region = 0;
|
|
41
|
+
export let layer_options;
|
|
42
|
+
export let current_tool;
|
|
43
|
+
export let webcam_options;
|
|
44
|
+
export let show_download_button = false;
|
|
45
|
+
export let theme_mode;
|
|
46
|
+
let pixi_target;
|
|
47
|
+
let pixi_target_crop;
|
|
48
48
|
$:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
if (layer_options) {
|
|
50
|
+
if (check_if_should_init()) {
|
|
51
|
+
editor.set_layer_options(layer_options);
|
|
52
|
+
refresh_tools();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function refresh_tools() {
|
|
56
|
+
editor.set_tool(current_tool);
|
|
57
|
+
editor.set_subtool(current_subtool);
|
|
58
|
+
}
|
|
59
|
+
function check_if_should_init() {
|
|
60
|
+
return layer_options && editor && ready;
|
|
61
|
+
}
|
|
62
|
+
export let has_drawn = false;
|
|
63
|
+
export async function get_blobs() {
|
|
64
|
+
if (!editor)
|
|
65
|
+
return { background: null, layers: [], composite: null };
|
|
66
|
+
if (!background_image && !has_drawn && !layers.length)
|
|
67
|
+
return { background: null, layers: [], composite: null };
|
|
68
|
+
const blobs = await editor.get_blobs();
|
|
69
|
+
return blobs;
|
|
70
|
+
}
|
|
71
|
+
let editor;
|
|
72
|
+
export function add_image(image) {
|
|
73
|
+
editor.add_image({ image });
|
|
74
|
+
}
|
|
75
|
+
let pending_bg;
|
|
76
|
+
export async function add_image_from_url(source) {
|
|
77
|
+
if (!editor || !source || !check_if_should_init())
|
|
78
|
+
return;
|
|
79
|
+
let url;
|
|
80
|
+
if (typeof source === "string") {
|
|
81
|
+
url = source;
|
|
82
|
+
} else if (source?.meta?._type === "gradio.FileData" && source?.url) {
|
|
83
|
+
url = source.url;
|
|
84
|
+
} else {
|
|
85
|
+
console.warn("Invalid source provided to add_image_from_url:", source);
|
|
86
|
+
return;
|
|
56
87
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
88
|
+
try {
|
|
89
|
+
pending_bg = editor.add_image_from_url(url);
|
|
90
|
+
let pending_crop = crop.add_image_from_url(url);
|
|
91
|
+
await Promise.all([pending_bg, pending_crop]);
|
|
92
|
+
crop.set_tool("image");
|
|
93
|
+
crop.set_subtool("crop");
|
|
94
|
+
background_image = true;
|
|
95
|
+
dispatch("upload");
|
|
96
|
+
dispatch("input");
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error("Error adding image from URL:", error);
|
|
64
99
|
}
|
|
65
|
-
return history2;
|
|
66
100
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
101
|
+
export async function add_layers_from_url(source) {
|
|
102
|
+
if (!editor || !source.length || !check_if_should_init())
|
|
103
|
+
return;
|
|
104
|
+
if (Array.isArray(source) && source.every((item) => item?.meta?._type === "gradio.FileData")) {
|
|
105
|
+
try {
|
|
106
|
+
await pending_bg;
|
|
107
|
+
await editor.add_layers_from_url(source.map((item) => item.url));
|
|
108
|
+
dispatch("change");
|
|
109
|
+
dispatch("input");
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error("Error adding layer from URL:", error);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
73
114
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const _sorted_contexts = $contexts.sort((a, b) => {
|
|
105
|
-
return sort_order.indexOf(a) - sort_order.indexOf(b);
|
|
115
|
+
let brush;
|
|
116
|
+
let zoom;
|
|
117
|
+
let zoom_level = 1;
|
|
118
|
+
let ready = false;
|
|
119
|
+
let mounted = false;
|
|
120
|
+
let min_zoom = true;
|
|
121
|
+
let last_dimensions = { width: 0, height: 0 };
|
|
122
|
+
async function handle_visibility_change() {
|
|
123
|
+
if (!editor || !ready || !zoom)
|
|
124
|
+
return;
|
|
125
|
+
await tick();
|
|
126
|
+
const is_visible = pixi_target.offsetParent !== null;
|
|
127
|
+
if (is_visible) {
|
|
128
|
+
const current_dimensions = pixi_target.getBoundingClientRect();
|
|
129
|
+
if (current_dimensions.width !== last_dimensions.width || current_dimensions.height !== last_dimensions.height) {
|
|
130
|
+
zoom.set_zoom("fit");
|
|
131
|
+
last_dimensions = {
|
|
132
|
+
width: current_dimensions.width,
|
|
133
|
+
height: current_dimensions.height
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
onMount(() => {
|
|
139
|
+
let intersection_observer;
|
|
140
|
+
let resize_observer;
|
|
141
|
+
init_image_editor().then(() => {
|
|
142
|
+
mounted = true;
|
|
143
|
+
intersection_observer = new IntersectionObserver(() => {
|
|
144
|
+
handle_visibility_change();
|
|
106
145
|
});
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
146
|
+
resize_observer = new ResizeObserver(() => {
|
|
147
|
+
handle_visibility_change();
|
|
148
|
+
});
|
|
149
|
+
intersection_observer.observe(pixi_target);
|
|
150
|
+
resize_observer.observe(pixi_target);
|
|
151
|
+
});
|
|
152
|
+
return () => {
|
|
153
|
+
if (intersection_observer) {
|
|
154
|
+
intersection_observer.disconnect();
|
|
111
155
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (k === "bg" && !clear_image) {
|
|
115
|
-
continue;
|
|
116
|
-
} else {
|
|
117
|
-
$init_context[k]?.(dimensions2);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
CommandManager.reset();
|
|
121
|
-
$pixi?.resize?.(...dimensions2);
|
|
156
|
+
if (resize_observer) {
|
|
157
|
+
resize_observer.disconnect();
|
|
122
158
|
}
|
|
123
|
-
|
|
159
|
+
if (editor) {
|
|
160
|
+
editor.destroy();
|
|
161
|
+
}
|
|
162
|
+
};
|
|
124
163
|
});
|
|
125
|
-
let
|
|
126
|
-
let
|
|
127
|
-
function
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
164
|
+
let crop;
|
|
165
|
+
let crop_zoom;
|
|
166
|
+
async function init_image_editor() {
|
|
167
|
+
brush = new BrushTool();
|
|
168
|
+
zoom = new ZoomTool();
|
|
169
|
+
editor = new ImageEditor({
|
|
170
|
+
target_element: pixi_target,
|
|
171
|
+
width: canvas_size[0],
|
|
172
|
+
height: canvas_size[1],
|
|
173
|
+
tools: ["image", zoom, new ResizeTool(), brush],
|
|
174
|
+
fixed_canvas,
|
|
175
|
+
border_region,
|
|
176
|
+
layer_options,
|
|
177
|
+
theme_mode
|
|
178
|
+
});
|
|
179
|
+
brush.on("change", () => {
|
|
180
|
+
has_drawn = true;
|
|
181
|
+
});
|
|
182
|
+
crop_zoom = new ZoomTool();
|
|
183
|
+
crop = new ImageEditor({
|
|
184
|
+
target_element: pixi_target_crop,
|
|
185
|
+
width: canvas_size[0],
|
|
186
|
+
height: canvas_size[1],
|
|
187
|
+
tools: ["image", crop_zoom, new CropTool()],
|
|
188
|
+
dark: true,
|
|
189
|
+
fixed_canvas: false,
|
|
190
|
+
border_region: 0,
|
|
191
|
+
pad_bottom: 40
|
|
192
|
+
});
|
|
193
|
+
editor.scale.subscribe((_scale) => {
|
|
194
|
+
zoom_level = _scale;
|
|
195
|
+
});
|
|
196
|
+
editor.min_zoom.subscribe((is_min_zoom) => {
|
|
197
|
+
min_zoom = is_min_zoom;
|
|
198
|
+
});
|
|
199
|
+
editor.dimensions.subscribe((dimensions) => {
|
|
200
|
+
last_dimensions = { ...dimensions };
|
|
201
|
+
});
|
|
202
|
+
await Promise.all([editor.ready, crop.ready]).then(() => {
|
|
203
|
+
handle_tool_change({ tool: "image" });
|
|
204
|
+
ready = true;
|
|
205
|
+
if (sources.length > 0) {
|
|
206
|
+
handle_tool_change({ tool: "image" });
|
|
207
|
+
} else {
|
|
208
|
+
handle_tool_change({ tool: "draw" });
|
|
209
|
+
}
|
|
210
|
+
crop.set_subtool("crop");
|
|
159
211
|
});
|
|
212
|
+
editor.on("change", () => {
|
|
213
|
+
dispatch("change");
|
|
214
|
+
});
|
|
215
|
+
if (background || layers.length > 0) {
|
|
216
|
+
if (background) {
|
|
217
|
+
await add_image_from_url(background);
|
|
218
|
+
}
|
|
219
|
+
if (layers.length > 0) {
|
|
220
|
+
await add_layers_from_url(layers);
|
|
221
|
+
}
|
|
222
|
+
handle_tool_change({ tool: "draw" });
|
|
223
|
+
} else if (composite) {
|
|
224
|
+
await add_image_from_url(composite);
|
|
225
|
+
handle_tool_change({ tool: "draw" });
|
|
226
|
+
}
|
|
160
227
|
}
|
|
161
228
|
$:
|
|
162
|
-
if (
|
|
163
|
-
|
|
229
|
+
if (background == null && layers.length == 0 && composite == null && editor && ready) {
|
|
230
|
+
editor.reset_canvas();
|
|
231
|
+
handle_tool_change({ tool: "image" });
|
|
232
|
+
background_image = false;
|
|
233
|
+
has_drawn = false;
|
|
164
234
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
function reposition_canvas() {
|
|
171
|
-
if (!$editor_box)
|
|
235
|
+
$:
|
|
236
|
+
current_tool === "image" && current_subtool === "crop" && crop_zoom.set_zoom("fit");
|
|
237
|
+
async function handle_files(files) {
|
|
238
|
+
if (files == null)
|
|
172
239
|
return;
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
return { background: null, layers: [], composite: null };
|
|
185
|
-
const [l, t, w, h] = $crop;
|
|
186
|
-
return $pixi?.get_blobs(
|
|
187
|
-
$pixi.get_layers(),
|
|
188
|
-
new Rectangle(
|
|
189
|
-
Math.round(0),
|
|
190
|
-
Math.round(0),
|
|
191
|
-
Math.round(1 * $dimensions[0]),
|
|
192
|
-
Math.round(1 * $dimensions[1])
|
|
193
|
-
),
|
|
194
|
-
$dimensions
|
|
195
|
-
);
|
|
240
|
+
if (!sources.includes("upload"))
|
|
241
|
+
return;
|
|
242
|
+
const _file = Array.isArray(files) ? files[0] : files;
|
|
243
|
+
await editor.add_image({ image: _file });
|
|
244
|
+
await crop.add_image({ image: _file });
|
|
245
|
+
crop.reset();
|
|
246
|
+
background_image = true;
|
|
247
|
+
handle_tool_change({ tool: "draw" });
|
|
248
|
+
dispatch("upload");
|
|
249
|
+
dispatch("input");
|
|
250
|
+
dispatch("change");
|
|
196
251
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
true,
|
|
204
|
-
orig_canvas_size ? orig_canvas_size : $dimensions
|
|
205
|
-
);
|
|
206
|
-
if (!sources.length) {
|
|
207
|
-
set_tool("draw");
|
|
208
|
-
} else {
|
|
209
|
-
set_tool("bg");
|
|
252
|
+
function handle_tool_change({ tool }) {
|
|
253
|
+
editor.set_tool(tool);
|
|
254
|
+
current_tool = tool;
|
|
255
|
+
if (tool === "image") {
|
|
256
|
+
crop.set_tool("image");
|
|
257
|
+
crop.set_subtool("crop");
|
|
210
258
|
}
|
|
211
|
-
dispatch("clear");
|
|
212
|
-
let _size = (canvas_size ? canvas_size : crop_size) || [800, 600];
|
|
213
|
-
editor_context.reset(true, _size);
|
|
214
259
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
app.resize(width, height2);
|
|
224
|
-
dimensions.set([width, height2]);
|
|
260
|
+
function handle_subtool_change({
|
|
261
|
+
tool,
|
|
262
|
+
subtool
|
|
263
|
+
}) {
|
|
264
|
+
editor.set_subtool(subtool);
|
|
265
|
+
current_subtool = subtool;
|
|
266
|
+
if (subtool === null) {
|
|
267
|
+
return;
|
|
225
268
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
});
|
|
232
|
-
resizer.observe(canvas_wrap);
|
|
233
|
-
resizer.observe(pixi_target);
|
|
234
|
-
for (const k of $contexts) {
|
|
235
|
-
if (k in $init_context && typeof $init_context[k] === "function") {
|
|
236
|
-
$init_context[k]?.($dimensions);
|
|
269
|
+
if (tool === "draw") {
|
|
270
|
+
if (subtool === "size") {
|
|
271
|
+
brush_size_visible = true;
|
|
272
|
+
} else if (subtool === "color") {
|
|
273
|
+
brush_color_visible = true;
|
|
237
274
|
}
|
|
238
275
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
276
|
+
if (tool === "erase" && subtool === "size") {
|
|
277
|
+
eraser_size_visible = true;
|
|
278
|
+
}
|
|
279
|
+
if (tool === "image" && subtool === "paste") {
|
|
280
|
+
process_clipboard();
|
|
281
|
+
}
|
|
282
|
+
if (tool === "image" && subtool === "upload") {
|
|
283
|
+
tick().then(() => {
|
|
284
|
+
disable_click = false;
|
|
285
|
+
open_file_upload();
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
let eraser_size_visible = false;
|
|
290
|
+
let selected_color;
|
|
291
|
+
let selected_size;
|
|
292
|
+
let selected_opacity = 1;
|
|
293
|
+
let selected_eraser_size;
|
|
294
|
+
$: {
|
|
295
|
+
if (brush_options) {
|
|
296
|
+
update_brush_options();
|
|
297
|
+
}
|
|
298
|
+
if (eraser_options) {
|
|
299
|
+
update_eraser_options();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function update_brush_options() {
|
|
303
|
+
const default_color = brush_options.default_color === "auto" ? brush_options.colors[0] : brush_options.default_color;
|
|
304
|
+
if (Array.isArray(default_color)) {
|
|
305
|
+
selected_color = default_color[0];
|
|
306
|
+
selected_opacity = default_color[1];
|
|
307
|
+
} else {
|
|
308
|
+
selected_color = default_color;
|
|
309
|
+
const color = tinycolor(default_color);
|
|
310
|
+
if (color.getAlpha() < 1) {
|
|
311
|
+
selected_opacity = color.getAlpha();
|
|
312
|
+
} else {
|
|
313
|
+
selected_opacity = 1;
|
|
243
314
|
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
315
|
+
}
|
|
316
|
+
selected_size = typeof brush_options.default_size === "number" ? brush_options.default_size : 25;
|
|
317
|
+
}
|
|
318
|
+
function update_eraser_options() {
|
|
319
|
+
selected_eraser_size = eraser_options.default_size === "auto" ? 25 : eraser_options.default_size;
|
|
320
|
+
}
|
|
321
|
+
let brush_size_visible = false;
|
|
322
|
+
let brush_color_visible = false;
|
|
323
|
+
$:
|
|
324
|
+
brush?.set_brush_color(
|
|
325
|
+
(() => {
|
|
326
|
+
let color_value;
|
|
327
|
+
if (selected_color === "auto") {
|
|
328
|
+
const default_color = brush_options.colors.find(
|
|
329
|
+
(color) => Array.isArray(color) ? color[0] === brush_options.default_color : color === brush_options.default_color
|
|
330
|
+
) || brush_options.colors[0];
|
|
331
|
+
color_value = Array.isArray(default_color) ? default_color[0] : default_color;
|
|
332
|
+
} else {
|
|
333
|
+
color_value = selected_color;
|
|
251
334
|
}
|
|
335
|
+
return color_value;
|
|
336
|
+
})()
|
|
337
|
+
);
|
|
338
|
+
$:
|
|
339
|
+
brush?.set_brush_size(
|
|
340
|
+
typeof selected_size === "number" ? selected_size : 25
|
|
341
|
+
);
|
|
342
|
+
$:
|
|
343
|
+
brush?.set_eraser_size(
|
|
344
|
+
typeof selected_eraser_size === "number" ? selected_eraser_size : 25
|
|
345
|
+
);
|
|
346
|
+
$:
|
|
347
|
+
disable_click = current_tool !== "image" || current_tool === "image" && background_image || current_tool === "image" && current_subtool === "webcam" || !sources.includes("upload");
|
|
348
|
+
let current_subtool = null;
|
|
349
|
+
let preview = false;
|
|
350
|
+
$:
|
|
351
|
+
brush?.preview_brush(preview);
|
|
352
|
+
$:
|
|
353
|
+
brush?.set_brush_opacity(selected_opacity);
|
|
354
|
+
function handle_zoom_change(zoom_level2) {
|
|
355
|
+
zoom.set_zoom(zoom_level2);
|
|
356
|
+
}
|
|
357
|
+
function zoom_in_out(direction) {
|
|
358
|
+
zoom.set_zoom(
|
|
359
|
+
direction === "in" ? zoom_level + (zoom_level < 1 ? 0.1 : zoom_level * 0.1) : zoom_level - (zoom_level < 1 ? 0.1 : zoom_level * 0.1)
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
async function process_clipboard() {
|
|
363
|
+
const items = await navigator.clipboard.read();
|
|
364
|
+
for (let i = 0; i < items.length; i++) {
|
|
365
|
+
const type = items[i].types.find((t) => t.startsWith("image/"));
|
|
366
|
+
if (type) {
|
|
367
|
+
const blob = await items[i].getType(type);
|
|
368
|
+
handle_files(blob);
|
|
252
369
|
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
function handle_capture(e) {
|
|
373
|
+
if (e.detail !== null) {
|
|
374
|
+
handle_files(e.detail);
|
|
375
|
+
}
|
|
376
|
+
handle_subtool_change({ tool: current_tool, subtool: null });
|
|
377
|
+
}
|
|
256
378
|
function handle_save() {
|
|
257
|
-
saved_history = $current_history;
|
|
258
379
|
dispatch("save");
|
|
259
380
|
}
|
|
260
|
-
|
|
261
|
-
|
|
381
|
+
$:
|
|
382
|
+
add_image_from_url(composite || background);
|
|
383
|
+
$:
|
|
384
|
+
add_layers_from_url(layers);
|
|
385
|
+
async function handle_crop_confirm() {
|
|
386
|
+
const { image } = await crop.get_crop_bounds();
|
|
387
|
+
if (!image)
|
|
388
|
+
return;
|
|
389
|
+
await editor.add_image({
|
|
390
|
+
image,
|
|
391
|
+
resize: false
|
|
392
|
+
});
|
|
393
|
+
handle_subtool_change({ tool: "image", subtool: null });
|
|
394
|
+
dispatch("change");
|
|
395
|
+
dispatch("input");
|
|
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);
|
|
262
410
|
}
|
|
263
411
|
</script>
|
|
264
412
|
|
|
265
|
-
<
|
|
413
|
+
<div
|
|
414
|
+
data-testid="image"
|
|
415
|
+
class="image-container"
|
|
416
|
+
class:dark-bg={current_subtool === "crop"}
|
|
417
|
+
use:drag={{
|
|
418
|
+
on_drag_change: (dragging) => (is_dragging = dragging),
|
|
419
|
+
on_files: handle_files,
|
|
420
|
+
accepted_types: "image/*",
|
|
421
|
+
disable_click: disable_click
|
|
422
|
+
}}
|
|
423
|
+
aria-label={"Click to upload or drop files"}
|
|
424
|
+
aria-dropeffect="copy"
|
|
425
|
+
>
|
|
426
|
+
{#if ready}
|
|
427
|
+
{#if current_subtool !== "crop"}
|
|
428
|
+
<Controls
|
|
429
|
+
{changeable}
|
|
430
|
+
on:set_zoom={(e) => handle_zoom_change(e.detail)}
|
|
431
|
+
on:zoom_in={() => zoom_in_out("in")}
|
|
432
|
+
on:zoom_out={() => zoom_in_out("out")}
|
|
433
|
+
{min_zoom}
|
|
434
|
+
current_zoom={zoom_level}
|
|
435
|
+
on:remove_image={() => {
|
|
436
|
+
editor.reset_canvas();
|
|
437
|
+
handle_tool_change({ tool: "image" });
|
|
438
|
+
background_image = false;
|
|
439
|
+
has_drawn = false;
|
|
440
|
+
}}
|
|
441
|
+
tool={current_tool}
|
|
442
|
+
can_save={true}
|
|
443
|
+
on:save={handle_save}
|
|
444
|
+
on:pan={(e) => {
|
|
445
|
+
handle_tool_change({ tool: "pan" });
|
|
446
|
+
}}
|
|
447
|
+
enable_download={show_download_button}
|
|
448
|
+
on:download={() => handle_download()}
|
|
449
|
+
/>
|
|
450
|
+
{/if}
|
|
266
451
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
452
|
+
{#if current_subtool !== "crop"}
|
|
453
|
+
<Toolbar
|
|
454
|
+
{sources}
|
|
455
|
+
{transforms}
|
|
456
|
+
background={background_image}
|
|
457
|
+
on:tool_change={(e) => handle_tool_change(e.detail)}
|
|
458
|
+
on:subtool_change={(e) => handle_subtool_change(e.detail)}
|
|
459
|
+
show_brush_size={brush_size_visible}
|
|
460
|
+
show_brush_color={brush_color_visible}
|
|
461
|
+
show_eraser_size={eraser_size_visible}
|
|
462
|
+
{brush_options}
|
|
463
|
+
{eraser_options}
|
|
464
|
+
bind:selected_color
|
|
465
|
+
bind:selected_size
|
|
466
|
+
bind:selected_eraser_size
|
|
467
|
+
bind:selected_opacity
|
|
468
|
+
bind:preview
|
|
469
|
+
tool={current_tool}
|
|
470
|
+
subtool={current_subtool}
|
|
471
|
+
/>
|
|
472
|
+
{/if}
|
|
473
|
+
|
|
474
|
+
{#if current_tool === "image" && current_subtool === "webcam"}
|
|
475
|
+
<div class="modal">
|
|
476
|
+
<div class="modal-inner">
|
|
477
|
+
<Webcam
|
|
478
|
+
{upload}
|
|
479
|
+
{root}
|
|
480
|
+
on:capture={handle_capture}
|
|
481
|
+
on:error
|
|
482
|
+
on:drag
|
|
483
|
+
streaming={false}
|
|
484
|
+
mode="image"
|
|
485
|
+
include_audio={false}
|
|
486
|
+
{i18n}
|
|
487
|
+
mirror_webcam={webcam_options.mirror}
|
|
488
|
+
webcam_constraints={webcam_options.constraints}
|
|
489
|
+
/>
|
|
490
|
+
</div>
|
|
491
|
+
</div>
|
|
492
|
+
{/if}
|
|
493
|
+
|
|
494
|
+
{#if current_subtool !== "crop" && !layer_options.disabled}
|
|
495
|
+
<SecondaryToolbar
|
|
496
|
+
enable_additional_layers={layer_options.allow_additional_layers}
|
|
497
|
+
layers={editor.layers}
|
|
498
|
+
on:new_layer={() => {
|
|
499
|
+
editor.add_layer();
|
|
500
|
+
}}
|
|
501
|
+
on:change_layer={(e) => {
|
|
502
|
+
editor.set_layer(e.detail);
|
|
503
|
+
if (current_tool === "draw") {
|
|
504
|
+
handle_tool_change({ tool: "draw" });
|
|
505
|
+
}
|
|
506
|
+
}}
|
|
507
|
+
on:move_layer={(e) => {
|
|
508
|
+
editor.move_layer(e.detail.id, e.detail.direction);
|
|
509
|
+
}}
|
|
510
|
+
on:delete_layer={(e) => {
|
|
511
|
+
editor.delete_layer(e.detail);
|
|
512
|
+
}}
|
|
513
|
+
on:toggle_layer_visibility={(e) => {
|
|
514
|
+
editor.toggle_layer_visibility(e.detail);
|
|
515
|
+
}}
|
|
516
|
+
/>
|
|
517
|
+
{/if}
|
|
518
|
+
{/if}
|
|
519
|
+
<div
|
|
520
|
+
class="pixi-target"
|
|
521
|
+
class:visible={current_subtool !== "crop"}
|
|
522
|
+
bind:this={pixi_target}
|
|
523
|
+
></div>
|
|
524
|
+
<div
|
|
525
|
+
class="pixi-target-crop"
|
|
526
|
+
class:visible={current_subtool === "crop"}
|
|
527
|
+
bind:this={pixi_target_crop}
|
|
528
|
+
></div>
|
|
529
|
+
|
|
530
|
+
{#if current_subtool === "crop"}
|
|
531
|
+
<div class="crop-confirm-button">
|
|
532
|
+
<IconButton
|
|
533
|
+
Icon={Check}
|
|
534
|
+
label="Confirm crop"
|
|
535
|
+
show_label={true}
|
|
536
|
+
size="large"
|
|
537
|
+
padded={true}
|
|
538
|
+
color="white"
|
|
539
|
+
background="var(--color-green-500)"
|
|
540
|
+
label_position="right"
|
|
541
|
+
on:click={handle_crop_confirm}
|
|
542
|
+
/>
|
|
284
543
|
</div>
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
class:no-border={!bg && $active_tool === "bg" && !history}
|
|
288
|
-
style:width="{$editor_box.child_width + 1}px"
|
|
289
|
-
style:height="{$editor_box.child_height + 1}px"
|
|
290
|
-
style:top="{$editor_box.child_top - $editor_box.parent_top - 0.5}px"
|
|
291
|
-
style:left="{$editor_box.child_left - $editor_box.parent_left - 0.5}px"
|
|
292
|
-
></div>
|
|
293
|
-
</div>
|
|
544
|
+
{/if}
|
|
545
|
+
<slot></slot>
|
|
294
546
|
</div>
|
|
295
547
|
|
|
296
548
|
<style>
|
|
297
|
-
.
|
|
549
|
+
.image-container {
|
|
298
550
|
display: flex;
|
|
299
|
-
width: 100%;
|
|
300
551
|
height: 100%;
|
|
301
|
-
|
|
552
|
+
flex-direction: column;
|
|
302
553
|
justify-content: center;
|
|
554
|
+
align-items: center;
|
|
555
|
+
max-height: 100%;
|
|
556
|
+
border-radius: var(--radius-sm);
|
|
303
557
|
}
|
|
304
|
-
|
|
558
|
+
|
|
559
|
+
.pixi-target {
|
|
560
|
+
width: 100%;
|
|
561
|
+
height: 100%;
|
|
305
562
|
position: absolute;
|
|
306
|
-
|
|
563
|
+
top: 0;
|
|
564
|
+
left: 0;
|
|
565
|
+
z-index: 1;
|
|
566
|
+
display: block;
|
|
567
|
+
opacity: 0;
|
|
307
568
|
pointer-events: none;
|
|
308
|
-
border-radius: var(--radius-
|
|
569
|
+
border-radius: var(--radius-sm);
|
|
309
570
|
}
|
|
310
571
|
|
|
311
|
-
.
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
572
|
+
.pixi-target-crop {
|
|
573
|
+
width: 100%;
|
|
574
|
+
height: 100%;
|
|
575
|
+
position: absolute;
|
|
576
|
+
top: 0;
|
|
577
|
+
left: 0;
|
|
578
|
+
z-index: 2;
|
|
579
|
+
display: block;
|
|
580
|
+
opacity: 0;
|
|
581
|
+
pointer-events: none;
|
|
582
|
+
border-radius: var(--radius-sm);
|
|
317
583
|
}
|
|
318
584
|
|
|
319
|
-
.
|
|
320
|
-
|
|
585
|
+
.visible {
|
|
586
|
+
opacity: 1;
|
|
587
|
+
pointer-events: auto;
|
|
321
588
|
}
|
|
322
589
|
|
|
323
|
-
.
|
|
324
|
-
|
|
325
|
-
|
|
590
|
+
.pixi-target {
|
|
591
|
+
width: 100%;
|
|
592
|
+
height: 100%;
|
|
593
|
+
position: absolute;
|
|
594
|
+
top: 0;
|
|
595
|
+
left: 0;
|
|
596
|
+
right: 0;
|
|
597
|
+
bottom: 0;
|
|
326
598
|
overflow: hidden;
|
|
327
|
-
height: fit-content;
|
|
328
|
-
width: auto;
|
|
329
599
|
}
|
|
330
600
|
|
|
331
|
-
.
|
|
601
|
+
.modal {
|
|
602
|
+
position: absolute;
|
|
603
|
+
height: 100%;
|
|
604
|
+
width: 100%;
|
|
605
|
+
left: 0;
|
|
606
|
+
right: 0;
|
|
607
|
+
margin: auto;
|
|
608
|
+
z-index: var(--layer-top);
|
|
332
609
|
display: flex;
|
|
333
|
-
justify-content: center;
|
|
334
610
|
align-items: center;
|
|
335
|
-
padding: 0 var(--spacing-xl) 0 0;
|
|
336
|
-
border: 1px solid var(--block-border-color);
|
|
337
|
-
border-radius: var(--radius-sm);
|
|
338
|
-
margin: var(--spacing-xxl) 0 var(--spacing-xxl) 0;
|
|
339
|
-
width: fit-content;
|
|
340
|
-
margin: 0 auto;
|
|
341
611
|
}
|
|
342
612
|
|
|
343
|
-
.
|
|
344
|
-
display: flex;
|
|
613
|
+
.modal-inner {
|
|
345
614
|
height: 100%;
|
|
346
|
-
|
|
347
|
-
|
|
615
|
+
width: 100%;
|
|
616
|
+
background: var(--block-background-fill);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.dark-bg {
|
|
620
|
+
background: #333;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
.crop-confirm-button {
|
|
624
|
+
position: absolute;
|
|
625
|
+
bottom: 8px;
|
|
626
|
+
left: 0;
|
|
627
|
+
right: 0;
|
|
628
|
+
margin: auto;
|
|
629
|
+
z-index: var(--layer-top);
|
|
630
|
+
display: flex;
|
|
348
631
|
align-items: center;
|
|
349
|
-
|
|
632
|
+
justify-content: center;
|
|
350
633
|
}
|
|
351
634
|
</style>
|