@gradio/imageeditor 0.12.13 → 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.
- package/CHANGELOG.md +23 -0
- package/Example.svelte +1 -1
- package/IMAGE_EDITOR_OVERVIEW.md +224 -0
- package/ImageEditor.stories.svelte +4 -4
- package/Index.svelte +35 -46
- package/{shared/InteractiveImageEditor.svelte → InteractiveImageEditor.svelte} +56 -103
- package/dist/Example.svelte.d.ts +1 -1
- package/dist/Index.svelte +29 -33
- package/dist/Index.svelte.d.ts +17 -16
- package/dist/{shared/InteractiveImageEditor.svelte → InteractiveImageEditor.svelte} +47 -88
- package/dist/{shared/InteractiveImageEditor.svelte.d.ts → InteractiveImageEditor.svelte.d.ts} +13 -15
- package/dist/shared/Anchor.svelte +311 -0
- package/dist/shared/Anchor.svelte.d.ts +18 -0
- package/dist/shared/Controls.svelte +162 -23
- package/dist/shared/Controls.svelte.d.ts +7 -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 +546 -289
- package/dist/shared/ImageEditor.svelte.d.ts +51 -56
- package/dist/shared/Layers.svelte +233 -0
- package/dist/shared/Layers.svelte.d.ts +35 -0
- package/dist/shared/Resize.svelte +218 -0
- package/dist/shared/Resize.svelte.d.ts +29 -0
- package/dist/shared/SecondaryToolbar.svelte +54 -0
- package/dist/shared/SecondaryToolbar.svelte.d.ts +35 -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 +71 -0
- package/dist/shared/brush/brush-textures.js +404 -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 +396 -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 +252 -0
- package/dist/shared/core/editor.js +1221 -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 +185 -0
- package/dist/shared/resize/resize.d.ts +270 -0
- package/dist/shared/resize/resize.js +1413 -0
- package/dist/shared/types.d.ts +12 -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 +11 -10
- package/shared/Anchor.svelte +384 -0
- package/shared/Controls.svelte +194 -27
- package/shared/{tools/IconButton.svelte → IconButton.svelte} +21 -4
- package/shared/ImageEditor.svelte +682 -349
- package/shared/Layers.svelte +249 -0
- package/shared/Resize.svelte +239 -0
- package/shared/SecondaryToolbar.svelte +61 -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 +508 -0
- package/shared/brush/brush-utils.ts +76 -0
- package/shared/brush/brush.ts +506 -0
- package/shared/brush/types.ts +41 -0
- package/shared/core/EDITOR.md +182 -0
- package/shared/core/editor.ts +1730 -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 +300 -0
- package/shared/resize/resize.ts +1894 -0
- package/shared/types.ts +14 -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,283 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { getContext, onMount, tick, createEventDispatcher } from "svelte";
|
|
3
|
-
import { type ToolContext, TOOL_KEY } from "./Tools.svelte";
|
|
4
|
-
import { type EditorContext, EDITOR_KEY } from "../ImageEditor.svelte";
|
|
5
|
-
import {
|
|
6
|
-
Image as ImageIcon,
|
|
7
|
-
Webcam as WebcamIcon,
|
|
8
|
-
ImagePaste
|
|
9
|
-
} from "@gradio/icons";
|
|
10
|
-
import { Upload } from "@gradio/upload";
|
|
11
|
-
import { Webcam } from "@gradio/image";
|
|
12
|
-
import { type I18nFormatter } from "@gradio/utils";
|
|
13
|
-
import { default as IconButton } from "./IconButton.svelte";
|
|
14
|
-
import { type Client } from "@gradio/client";
|
|
15
|
-
|
|
16
|
-
import { add_bg_color, add_bg_image } from "./sources";
|
|
17
|
-
import type { FileData } from "@gradio/client";
|
|
18
|
-
|
|
19
|
-
export let background_file: FileData | null;
|
|
20
|
-
export let root: string;
|
|
21
|
-
export let sources: ("upload" | "webcam" | "clipboard")[] = [
|
|
22
|
-
"upload",
|
|
23
|
-
"webcam",
|
|
24
|
-
"clipboard"
|
|
25
|
-
];
|
|
26
|
-
export let mirror_webcam = true;
|
|
27
|
-
export let i18n: I18nFormatter;
|
|
28
|
-
export let upload: Client["upload"];
|
|
29
|
-
export let stream_handler: Client["stream"];
|
|
30
|
-
export let dragging: boolean;
|
|
31
|
-
export let canvas_size: [number, number];
|
|
32
|
-
export let fixed_canvas = false;
|
|
33
|
-
|
|
34
|
-
const { active_tool } = getContext<ToolContext>(TOOL_KEY);
|
|
35
|
-
const { pixi, dimensions, register_context, reset, editor_box } =
|
|
36
|
-
getContext<EditorContext>(EDITOR_KEY);
|
|
37
|
-
|
|
38
|
-
export let active_mode: "webcam" | "color" | null = null;
|
|
39
|
-
let background: Blob | File | null;
|
|
40
|
-
|
|
41
|
-
const dispatch = createEventDispatcher<{
|
|
42
|
-
upload: never;
|
|
43
|
-
}>();
|
|
44
|
-
|
|
45
|
-
const sources_meta = {
|
|
46
|
-
upload: {
|
|
47
|
-
icon: ImageIcon,
|
|
48
|
-
label: "Upload",
|
|
49
|
-
order: 0,
|
|
50
|
-
id: "bg_upload",
|
|
51
|
-
cb() {
|
|
52
|
-
upload_component.open_file_upload();
|
|
53
|
-
|
|
54
|
-
$active_tool = "bg";
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
webcam: {
|
|
58
|
-
icon: WebcamIcon,
|
|
59
|
-
label: "Webcam",
|
|
60
|
-
order: 1,
|
|
61
|
-
id: "bg_webcam",
|
|
62
|
-
cb() {
|
|
63
|
-
active_mode = "webcam";
|
|
64
|
-
$active_tool = "bg";
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
|
-
clipboard: {
|
|
68
|
-
icon: ImagePaste,
|
|
69
|
-
label: "Paste",
|
|
70
|
-
order: 2,
|
|
71
|
-
id: "bg_clipboard",
|
|
72
|
-
cb() {
|
|
73
|
-
process_clipboard();
|
|
74
|
-
$active_tool = null;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
} as const;
|
|
78
|
-
|
|
79
|
-
$: sources_list = sources
|
|
80
|
-
.map((src) => sources_meta[src])
|
|
81
|
-
.sort((a, b) => a.order - b.order);
|
|
82
|
-
|
|
83
|
-
let upload_component: Upload;
|
|
84
|
-
|
|
85
|
-
async function process_clipboard(): Promise<void> {
|
|
86
|
-
const items = await navigator.clipboard.read();
|
|
87
|
-
|
|
88
|
-
for (let i = 0; i < items.length; i++) {
|
|
89
|
-
const type = items[i].types.find((t) => t.startsWith("image/"));
|
|
90
|
-
if (type) {
|
|
91
|
-
const blob = await items[i].getType(type);
|
|
92
|
-
|
|
93
|
-
background = blob || null;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function handle_upload(e: CustomEvent<Blob | any>): void {
|
|
99
|
-
const file_data = e.detail;
|
|
100
|
-
background = file_data;
|
|
101
|
-
active_mode = null;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
let should_reset = true;
|
|
105
|
-
|
|
106
|
-
async function set_background(): Promise<void> {
|
|
107
|
-
if (!$pixi) return;
|
|
108
|
-
if (background) {
|
|
109
|
-
const add_image = add_bg_image(
|
|
110
|
-
$pixi.background_container,
|
|
111
|
-
$pixi.renderer,
|
|
112
|
-
background,
|
|
113
|
-
$pixi.resize,
|
|
114
|
-
canvas_size,
|
|
115
|
-
fixed_canvas
|
|
116
|
-
);
|
|
117
|
-
$dimensions = await add_image.start();
|
|
118
|
-
|
|
119
|
-
if (should_reset) {
|
|
120
|
-
reset(false, $dimensions);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
add_image.execute();
|
|
124
|
-
|
|
125
|
-
should_reset = true;
|
|
126
|
-
|
|
127
|
-
await tick();
|
|
128
|
-
bg = true;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
async function process_bg_file(file: FileData | null): Promise<void> {
|
|
133
|
-
if (!file || !file.url) return;
|
|
134
|
-
should_reset = false;
|
|
135
|
-
|
|
136
|
-
const blob_res = await fetch(file.url);
|
|
137
|
-
const blob = await blob_res.blob();
|
|
138
|
-
background = blob;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function handle_key(e: KeyboardEvent): void {
|
|
142
|
-
if (e.key === "Escape") {
|
|
143
|
-
active_mode = null;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
$: background && set_background();
|
|
148
|
-
$: process_bg_file(background_file);
|
|
149
|
-
|
|
150
|
-
export let bg = false;
|
|
151
|
-
|
|
152
|
-
register_context("bg", {
|
|
153
|
-
init_fn: () => {
|
|
154
|
-
if (!$pixi) return;
|
|
155
|
-
|
|
156
|
-
const add_image = add_bg_color(
|
|
157
|
-
$pixi.background_container,
|
|
158
|
-
$pixi.renderer,
|
|
159
|
-
"black",
|
|
160
|
-
...$dimensions,
|
|
161
|
-
$pixi.resize
|
|
162
|
-
);
|
|
163
|
-
$dimensions = add_image.start();
|
|
164
|
-
add_image.execute();
|
|
165
|
-
},
|
|
166
|
-
reset_fn: () => {}
|
|
167
|
-
});
|
|
168
|
-
</script>
|
|
169
|
-
|
|
170
|
-
<svelte:window on:keydown={handle_key} />
|
|
171
|
-
|
|
172
|
-
{#if sources.length}
|
|
173
|
-
<div class="source-wrap">
|
|
174
|
-
{#each sources_list as { icon, label, id, cb } (id)}
|
|
175
|
-
<IconButton
|
|
176
|
-
Icon={icon}
|
|
177
|
-
size="medium"
|
|
178
|
-
padded={false}
|
|
179
|
-
label={label + " button"}
|
|
180
|
-
hasPopup={true}
|
|
181
|
-
transparent={true}
|
|
182
|
-
on:click={cb}
|
|
183
|
-
/>
|
|
184
|
-
{/each}
|
|
185
|
-
<span class="sep"></span>
|
|
186
|
-
</div>
|
|
187
|
-
<div
|
|
188
|
-
class="upload-container"
|
|
189
|
-
class:click-disabled={!!bg ||
|
|
190
|
-
active_mode === "webcam" ||
|
|
191
|
-
$active_tool !== "bg"}
|
|
192
|
-
style:height="{Math.max(
|
|
193
|
-
$editor_box.child_height +
|
|
194
|
-
($editor_box.child_top - $editor_box.parent_top),
|
|
195
|
-
100
|
|
196
|
-
)}px"
|
|
197
|
-
>
|
|
198
|
-
<Upload
|
|
199
|
-
hidden={bg || active_mode === "webcam" || $active_tool !== "bg"}
|
|
200
|
-
bind:this={upload_component}
|
|
201
|
-
filetype="image/*"
|
|
202
|
-
on:load={handle_upload}
|
|
203
|
-
on:error
|
|
204
|
-
bind:dragging
|
|
205
|
-
{root}
|
|
206
|
-
disable_click={!sources.includes("upload")}
|
|
207
|
-
format="blob"
|
|
208
|
-
{upload}
|
|
209
|
-
{stream_handler}
|
|
210
|
-
></Upload>
|
|
211
|
-
{#if active_mode === "webcam"}
|
|
212
|
-
<div
|
|
213
|
-
class="modal"
|
|
214
|
-
style:max-width="{$editor_box.child_width}px"
|
|
215
|
-
style:max-height="{$editor_box.child_height}px"
|
|
216
|
-
style:top="{$editor_box.child_top - $editor_box.parent_top}px"
|
|
217
|
-
>
|
|
218
|
-
<div class="modal-inner">
|
|
219
|
-
<Webcam
|
|
220
|
-
{upload}
|
|
221
|
-
{root}
|
|
222
|
-
on:capture={handle_upload}
|
|
223
|
-
on:error
|
|
224
|
-
on:drag
|
|
225
|
-
{mirror_webcam}
|
|
226
|
-
streaming={false}
|
|
227
|
-
mode="image"
|
|
228
|
-
include_audio={false}
|
|
229
|
-
{i18n}
|
|
230
|
-
/>
|
|
231
|
-
</div>
|
|
232
|
-
</div>
|
|
233
|
-
{/if}
|
|
234
|
-
</div>
|
|
235
|
-
{/if}
|
|
236
|
-
|
|
237
|
-
<style>
|
|
238
|
-
.modal {
|
|
239
|
-
position: absolute;
|
|
240
|
-
height: 100%;
|
|
241
|
-
width: 100%;
|
|
242
|
-
left: 0;
|
|
243
|
-
right: 0;
|
|
244
|
-
margin: auto;
|
|
245
|
-
z-index: var(--layer-top);
|
|
246
|
-
display: flex;
|
|
247
|
-
align-items: center;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
.modal-inner {
|
|
251
|
-
width: 100%;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
.sep {
|
|
255
|
-
height: 12px;
|
|
256
|
-
background-color: var(--block-border-color);
|
|
257
|
-
width: 1px;
|
|
258
|
-
display: block;
|
|
259
|
-
margin-left: var(--spacing-xl);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
.source-wrap {
|
|
263
|
-
display: flex;
|
|
264
|
-
justify-content: center;
|
|
265
|
-
align-items: center;
|
|
266
|
-
margin-left: var(--spacing-lg);
|
|
267
|
-
height: 100%;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
.upload-container {
|
|
271
|
-
position: absolute;
|
|
272
|
-
height: 100%;
|
|
273
|
-
flex-shrink: 1;
|
|
274
|
-
max-height: 100%;
|
|
275
|
-
width: 100%;
|
|
276
|
-
left: 0;
|
|
277
|
-
top: 0;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
.upload-container.click-disabled {
|
|
281
|
-
pointer-events: none;
|
|
282
|
-
}
|
|
283
|
-
</style>
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
<script lang="ts" context="module">
|
|
2
|
-
import type {
|
|
3
|
-
Subscriber,
|
|
4
|
-
Invalidator,
|
|
5
|
-
Unsubscriber,
|
|
6
|
-
Writable
|
|
7
|
-
} from "svelte/store";
|
|
8
|
-
import type { tool } from "../tools/";
|
|
9
|
-
|
|
10
|
-
export const TOOL_KEY = Symbol("tool");
|
|
11
|
-
|
|
12
|
-
type upload_tool = "bg_webcam" | "bg_upload" | "bg_clipboard" | "bg_color";
|
|
13
|
-
type transform_tool = "crop" | "rotate";
|
|
14
|
-
type brush_tool = "brush_color" | "brush_size";
|
|
15
|
-
type eraser_tool = "eraser_size";
|
|
16
|
-
|
|
17
|
-
export interface ToolOptions {
|
|
18
|
-
order: number;
|
|
19
|
-
label: string;
|
|
20
|
-
icon: typeof Image;
|
|
21
|
-
cb: (...args: any[]) => void;
|
|
22
|
-
id: upload_tool | transform_tool | brush_tool | eraser_tool;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface ToolMeta {
|
|
26
|
-
default: upload_tool | transform_tool | brush_tool | eraser_tool | null;
|
|
27
|
-
options: ToolOptions[];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface ToolContext {
|
|
31
|
-
register_tool: (type: tool, opts?: { cb: () => void }) => () => void;
|
|
32
|
-
active_tool: {
|
|
33
|
-
set: (tool: tool) => void;
|
|
34
|
-
subscribe(
|
|
35
|
-
this: void,
|
|
36
|
-
run: Subscriber<tool | null>,
|
|
37
|
-
invalidate?: Invalidator<tool | null>
|
|
38
|
-
): Unsubscriber;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
current_color: Writable<string>;
|
|
42
|
-
}
|
|
43
|
-
</script>
|
|
44
|
-
|
|
45
|
-
<script lang="ts">
|
|
46
|
-
import { Toolbar } from "@gradio/atoms";
|
|
47
|
-
import { default as IconButton } from "./IconButton.svelte";
|
|
48
|
-
import { getContext, setContext } from "svelte";
|
|
49
|
-
import { writable } from "svelte/store";
|
|
50
|
-
import { EDITOR_KEY, type EditorContext } from "../ImageEditor.svelte";
|
|
51
|
-
import { Image, Crop, Brush, Erase } from "@gradio/icons";
|
|
52
|
-
import { type I18nFormatter } from "@gradio/utils";
|
|
53
|
-
|
|
54
|
-
const { active_tool, toolbar_box, editor_box } =
|
|
55
|
-
getContext<EditorContext>(EDITOR_KEY);
|
|
56
|
-
|
|
57
|
-
export let i18n: I18nFormatter;
|
|
58
|
-
|
|
59
|
-
let tools: tool[] = [];
|
|
60
|
-
|
|
61
|
-
const cbs: Record<string, () => void> = {};
|
|
62
|
-
let current_color = writable("#000000");
|
|
63
|
-
const tool_context: ToolContext = {
|
|
64
|
-
current_color,
|
|
65
|
-
register_tool: (type: tool, opts?: { cb: () => void }) => {
|
|
66
|
-
tools = [...tools, type];
|
|
67
|
-
if (opts?.cb) {
|
|
68
|
-
cbs[type] = opts.cb;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return () => {
|
|
72
|
-
tools = tools.filter((tool) => tool !== type);
|
|
73
|
-
};
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
active_tool: {
|
|
77
|
-
subscribe: active_tool.subscribe,
|
|
78
|
-
set: active_tool.set
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
setContext<ToolContext>(TOOL_KEY, tool_context);
|
|
83
|
-
|
|
84
|
-
const tools_meta: Record<
|
|
85
|
-
tool,
|
|
86
|
-
{
|
|
87
|
-
order: number;
|
|
88
|
-
label: string;
|
|
89
|
-
icon: typeof Image;
|
|
90
|
-
}
|
|
91
|
-
> = {
|
|
92
|
-
crop: {
|
|
93
|
-
order: 1,
|
|
94
|
-
label: i18n("Transform"),
|
|
95
|
-
icon: Crop
|
|
96
|
-
},
|
|
97
|
-
draw: {
|
|
98
|
-
order: 2,
|
|
99
|
-
label: i18n("Draw"),
|
|
100
|
-
icon: Brush
|
|
101
|
-
},
|
|
102
|
-
erase: {
|
|
103
|
-
order: 2,
|
|
104
|
-
label: i18n("Erase"),
|
|
105
|
-
icon: Erase
|
|
106
|
-
},
|
|
107
|
-
bg: {
|
|
108
|
-
order: 0,
|
|
109
|
-
label: i18n("Background"),
|
|
110
|
-
icon: Image
|
|
111
|
-
}
|
|
112
|
-
} as const;
|
|
113
|
-
|
|
114
|
-
let toolbar_width: number;
|
|
115
|
-
let toolbar_wrap: HTMLDivElement;
|
|
116
|
-
|
|
117
|
-
$: toolbar_width, $editor_box, get_dimensions();
|
|
118
|
-
|
|
119
|
-
function get_dimensions(): void {
|
|
120
|
-
if (!toolbar_wrap) return;
|
|
121
|
-
$toolbar_box = toolbar_wrap.getBoundingClientRect();
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function handle_click(e: Event, tool: tool): void {
|
|
125
|
-
e.stopPropagation();
|
|
126
|
-
$active_tool = tool;
|
|
127
|
-
cbs[tool] && cbs[tool]();
|
|
128
|
-
}
|
|
129
|
-
</script>
|
|
130
|
-
|
|
131
|
-
<slot />
|
|
132
|
-
|
|
133
|
-
<div
|
|
134
|
-
class="toolbar-wrap"
|
|
135
|
-
bind:clientWidth={toolbar_width}
|
|
136
|
-
bind:this={toolbar_wrap}
|
|
137
|
-
>
|
|
138
|
-
<Toolbar show_border={false}>
|
|
139
|
-
{#each tools as tool (tool)}
|
|
140
|
-
<IconButton
|
|
141
|
-
highlight={$active_tool === tool}
|
|
142
|
-
on:click={(e) => handle_click(e, tool)}
|
|
143
|
-
Icon={tools_meta[tool].icon}
|
|
144
|
-
size="medium"
|
|
145
|
-
padded={false}
|
|
146
|
-
label={tools_meta[tool].label + " button"}
|
|
147
|
-
transparent={true}
|
|
148
|
-
offset={tool === "draw" ? -2 : tool === "erase" ? -6 : 0}
|
|
149
|
-
/>
|
|
150
|
-
{/each}
|
|
151
|
-
</Toolbar>
|
|
152
|
-
</div>
|
|
153
|
-
|
|
154
|
-
<style>
|
|
155
|
-
.toolbar-wrap {
|
|
156
|
-
display: flex;
|
|
157
|
-
justify-content: center;
|
|
158
|
-
align-items: center;
|
|
159
|
-
margin-left: var(--spacing-xl);
|
|
160
|
-
height: 100%;
|
|
161
|
-
}
|
|
162
|
-
</style>
|
package/shared/tools/brush.ts
DELETED
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Container,
|
|
3
|
-
Graphics,
|
|
4
|
-
Color,
|
|
5
|
-
type ColorSource,
|
|
6
|
-
type IRenderer,
|
|
7
|
-
RenderTexture,
|
|
8
|
-
Sprite
|
|
9
|
-
} from "pixi.js";
|
|
10
|
-
|
|
11
|
-
import type { LayerScene } from "../layers/utils";
|
|
12
|
-
import { type Command } from "../utils/commands";
|
|
13
|
-
import { make_graphics } from "../utils/pixi";
|
|
14
|
-
|
|
15
|
-
export interface DrawCommand extends Command {
|
|
16
|
-
/**
|
|
17
|
-
* sets the initial state to begin drawing
|
|
18
|
-
* @param options the options for drawing
|
|
19
|
-
* @returns
|
|
20
|
-
*/
|
|
21
|
-
start: (options: DrawOptions) => void;
|
|
22
|
-
/**
|
|
23
|
-
* continues drawing, smoothly interpolating between points where necessary
|
|
24
|
-
* @param options the options for drawing
|
|
25
|
-
* @returns
|
|
26
|
-
*/
|
|
27
|
-
continue: (options: Points) => void;
|
|
28
|
-
/**
|
|
29
|
-
* stops drawing
|
|
30
|
-
* @returns
|
|
31
|
-
*/
|
|
32
|
-
stop: () => void;
|
|
33
|
-
/**
|
|
34
|
-
* Whether or not the user is currently drawing.
|
|
35
|
-
*/
|
|
36
|
-
drawing: boolean;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Coordinates for a draw or erase path.
|
|
41
|
-
*/
|
|
42
|
-
interface Points {
|
|
43
|
-
/**
|
|
44
|
-
* The x coordinate of the path.
|
|
45
|
-
*/
|
|
46
|
-
x: number;
|
|
47
|
-
/**
|
|
48
|
-
* The y coordinate of the path.
|
|
49
|
-
*/
|
|
50
|
-
y: number;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Options for drawing a path.
|
|
55
|
-
*/
|
|
56
|
-
interface DrawOptions extends Points {
|
|
57
|
-
/**
|
|
58
|
-
* The size of the brush.
|
|
59
|
-
*/
|
|
60
|
-
size: number;
|
|
61
|
-
/**
|
|
62
|
-
* The color of the brush.
|
|
63
|
-
*/
|
|
64
|
-
color?: ColorSource;
|
|
65
|
-
/**
|
|
66
|
-
* The opacity of the brush.
|
|
67
|
-
*/
|
|
68
|
-
opacity: number;
|
|
69
|
-
/**
|
|
70
|
-
* Whether or not to set the initial texture.
|
|
71
|
-
*/
|
|
72
|
-
set_initial_texture?: boolean;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Draws a circle on the given graphics object.
|
|
77
|
-
* @param graphics the graphics object to draw on
|
|
78
|
-
* @param x the x coordinate of the circle
|
|
79
|
-
* @param y the y coordinate of the circle
|
|
80
|
-
* @param brush_color the color of the circle
|
|
81
|
-
* @param brush_size the radius of the circle
|
|
82
|
-
*/
|
|
83
|
-
function drawCircle(
|
|
84
|
-
graphics: Graphics,
|
|
85
|
-
x: number,
|
|
86
|
-
y: number,
|
|
87
|
-
brush_color: ColorSource = new Color("black"),
|
|
88
|
-
brush_size: number
|
|
89
|
-
): void {
|
|
90
|
-
const color = new Color(brush_color);
|
|
91
|
-
graphics.beginFill(color);
|
|
92
|
-
graphics.drawCircle(x, y, brush_size);
|
|
93
|
-
graphics.endFill();
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Interpolates between two points.
|
|
98
|
-
* @param point1 the first point
|
|
99
|
-
* @param point2 the second point
|
|
100
|
-
* @returns an array of points between the two points
|
|
101
|
-
*/
|
|
102
|
-
function interpolate(
|
|
103
|
-
point1: { x: number; y: number },
|
|
104
|
-
point2: { x: number; y: number }
|
|
105
|
-
): Points[] {
|
|
106
|
-
let points: Points[] = [];
|
|
107
|
-
const dx = point2.x - point1.x;
|
|
108
|
-
const dy = point2.y - point1.y;
|
|
109
|
-
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
110
|
-
const steps = Math.ceil(distance / 2);
|
|
111
|
-
const stepX = dx / steps;
|
|
112
|
-
const stepY = dy / steps;
|
|
113
|
-
|
|
114
|
-
for (let j = 0; j < steps; j++) {
|
|
115
|
-
const x = point1.x + j * stepX;
|
|
116
|
-
const y = point1.y + j * stepY;
|
|
117
|
-
points.push({ x, y });
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return points;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export function draw_path(
|
|
124
|
-
renderer: IRenderer,
|
|
125
|
-
stage: Container,
|
|
126
|
-
layer: LayerScene,
|
|
127
|
-
mode: "draw" | "erase"
|
|
128
|
-
): DrawCommand {
|
|
129
|
-
const paths: Points[] = [];
|
|
130
|
-
let initial_path: DrawOptions;
|
|
131
|
-
let graphics: Graphics;
|
|
132
|
-
let InitialTexture: RenderTexture;
|
|
133
|
-
|
|
134
|
-
let has_drawn = false;
|
|
135
|
-
let id = 0;
|
|
136
|
-
|
|
137
|
-
return {
|
|
138
|
-
drawing: false,
|
|
139
|
-
start: function ({
|
|
140
|
-
x,
|
|
141
|
-
y,
|
|
142
|
-
size,
|
|
143
|
-
color = new Color("black"),
|
|
144
|
-
opacity,
|
|
145
|
-
set_initial_texture = true
|
|
146
|
-
}: DrawOptions) {
|
|
147
|
-
if (set_initial_texture) {
|
|
148
|
-
InitialTexture = RenderTexture.create({
|
|
149
|
-
width: layer.draw_texture.width,
|
|
150
|
-
height: layer.draw_texture.height
|
|
151
|
-
});
|
|
152
|
-
renderer.render(layer.composite, {
|
|
153
|
-
renderTexture: InitialTexture
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
initial_path = { x, y, size, color, opacity };
|
|
157
|
-
paths.push({ x, y });
|
|
158
|
-
graphics = make_graphics(id++);
|
|
159
|
-
|
|
160
|
-
drawCircle(graphics, x, y, color, size);
|
|
161
|
-
|
|
162
|
-
renderer.render(graphics, {
|
|
163
|
-
renderTexture:
|
|
164
|
-
mode === "draw" ? layer.draw_texture : layer.erase_texture,
|
|
165
|
-
clear: false
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
this.drawing = true;
|
|
169
|
-
},
|
|
170
|
-
continue: function ({ x, y }: Points) {
|
|
171
|
-
const last_point = paths[paths.length - 1];
|
|
172
|
-
const new_points = interpolate(last_point, { x, y });
|
|
173
|
-
|
|
174
|
-
for (let i = 0; i < new_points.length; i++) {
|
|
175
|
-
const { x, y } = new_points[i];
|
|
176
|
-
drawCircle(graphics, x, y, initial_path.color, initial_path.size);
|
|
177
|
-
paths.push({ x, y });
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
renderer.render(graphics, {
|
|
181
|
-
renderTexture:
|
|
182
|
-
mode === "draw" ? layer.draw_texture : layer.erase_texture,
|
|
183
|
-
clear: false
|
|
184
|
-
});
|
|
185
|
-
graphics.clear();
|
|
186
|
-
},
|
|
187
|
-
stop: function () {
|
|
188
|
-
const current_sketch = RenderTexture.create({
|
|
189
|
-
width: layer.draw_texture.width,
|
|
190
|
-
height: layer.draw_texture.height
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
renderer.render(layer.composite, {
|
|
194
|
-
renderTexture: current_sketch
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
renderer.render(new Sprite(current_sketch), {
|
|
198
|
-
renderTexture: layer.draw_texture
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
const clear_graphics = new Graphics()
|
|
202
|
-
.beginFill(0x000000, 0) // Use a fill color with 0 alpha for transparency
|
|
203
|
-
.drawRect(0, 0, layer.erase_texture.width, layer.erase_texture.height)
|
|
204
|
-
.endFill();
|
|
205
|
-
|
|
206
|
-
renderer.render(clear_graphics, {
|
|
207
|
-
renderTexture: layer.erase_texture,
|
|
208
|
-
clear: true
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
has_drawn = true;
|
|
212
|
-
this.drawing = false;
|
|
213
|
-
},
|
|
214
|
-
execute: function () {
|
|
215
|
-
if (!has_drawn) {
|
|
216
|
-
for (let i = 1; i < paths.length; i++) {
|
|
217
|
-
const { x, y } = paths[i];
|
|
218
|
-
drawCircle(graphics, x, y, initial_path.color, initial_path.size);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
renderer.render(graphics, {
|
|
222
|
-
renderTexture:
|
|
223
|
-
mode === "draw" ? layer.draw_texture : layer.erase_texture,
|
|
224
|
-
clear: false
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
this.stop!();
|
|
228
|
-
}
|
|
229
|
-
},
|
|
230
|
-
undo: function () {
|
|
231
|
-
const clear_graphics = new Graphics()
|
|
232
|
-
.beginFill(0x000000, 0)
|
|
233
|
-
.drawRect(0, 0, layer.erase_texture.width, layer.erase_texture.height)
|
|
234
|
-
.endFill();
|
|
235
|
-
renderer.render(new Sprite(InitialTexture), {
|
|
236
|
-
renderTexture: layer.draw_texture
|
|
237
|
-
});
|
|
238
|
-
renderer.render(clear_graphics, {
|
|
239
|
-
renderTexture: layer.erase_texture,
|
|
240
|
-
clear: true
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
this.stop!();
|
|
244
|
-
has_drawn = false;
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
}
|