@gradio/image 0.3.0-beta.7 → 0.3.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 +37 -1
- package/Image.stories.svelte +17 -5
- package/Image.test.ts +15 -10
- package/Index.svelte +34 -29
- package/README.md +47 -5
- package/package.json +9 -7
- package/shared/{ModifySketch.svelte → ClearImage.svelte} +1 -16
- package/shared/ImagePreview.svelte +8 -12
- package/shared/ImageUploader.svelte +224 -0
- package/shared/Webcam.svelte +173 -30
- package/shared/Cropper.svelte +0 -33
- package/shared/ImageEditor.svelte +0 -456
- package/shared/Sketch.svelte +0 -624
- package/shared/SketchSettings.svelte +0 -77
@@ -1,456 +0,0 @@
|
|
1
|
-
<script lang="ts">
|
2
|
-
// @ts-nocheck
|
3
|
-
import { createEventDispatcher, tick, onMount } from "svelte";
|
4
|
-
import { BlockLabel } from "@gradio/atoms";
|
5
|
-
import { Image, Sketch as SketchIcon } from "@gradio/icons";
|
6
|
-
import type { SelectData, I18nFormatter } from "@gradio/utils";
|
7
|
-
import { get_coordinates_of_clicked_image } from "./utils";
|
8
|
-
|
9
|
-
import Cropper from "./Cropper.svelte";
|
10
|
-
import Sketch from "./Sketch.svelte";
|
11
|
-
import Webcam from "./Webcam.svelte";
|
12
|
-
import ModifySketch from "./ModifySketch.svelte";
|
13
|
-
import SketchSettings from "./SketchSettings.svelte";
|
14
|
-
import {
|
15
|
-
Upload,
|
16
|
-
ModifyUpload,
|
17
|
-
type FileData,
|
18
|
-
normalise_file
|
19
|
-
} from "@gradio/upload";
|
20
|
-
|
21
|
-
export let value:
|
22
|
-
| null
|
23
|
-
| { image: string | null; mask: string | null }
|
24
|
-
| FileData;
|
25
|
-
export let label: string | undefined = undefined;
|
26
|
-
export let show_label: boolean;
|
27
|
-
|
28
|
-
export let source: "canvas" | "webcam" | "upload" = "upload";
|
29
|
-
export let tool: "editor" | "select" | "sketch" | "color-sketch" = "editor";
|
30
|
-
export let shape: [number, number];
|
31
|
-
export let streaming = false;
|
32
|
-
export let pending = false;
|
33
|
-
export let mirror_webcam: boolean;
|
34
|
-
export let brush_radius: number;
|
35
|
-
export let brush_color = "#000000";
|
36
|
-
export let mask_opacity;
|
37
|
-
export let selectable = false;
|
38
|
-
export let root: string;
|
39
|
-
export let i18n: I18nFormatter;
|
40
|
-
|
41
|
-
let sketch: Sketch;
|
42
|
-
let cropper: Cropper;
|
43
|
-
|
44
|
-
if (
|
45
|
-
value &&
|
46
|
-
(source === "upload" || source === "webcam") &&
|
47
|
-
tool === "sketch"
|
48
|
-
) {
|
49
|
-
value = { image: value as string, mask: null };
|
50
|
-
}
|
51
|
-
|
52
|
-
function handle_upload({ detail }: CustomEvent<string>): void {
|
53
|
-
if (tool === "color-sketch") {
|
54
|
-
static_image = normalise_file(detail, root, null)?.data;
|
55
|
-
} else {
|
56
|
-
value =
|
57
|
-
(source === "upload" || source === "webcam") && tool === "sketch"
|
58
|
-
? { image: normalise_file(detail, root, null), mask: null }
|
59
|
-
: normalise_file(detail, root, null);
|
60
|
-
}
|
61
|
-
|
62
|
-
dispatch("upload", normalise_file(detail, root, null));
|
63
|
-
}
|
64
|
-
|
65
|
-
function handle_clear({ detail }: CustomEvent<null>): void {
|
66
|
-
value = null;
|
67
|
-
static_image = undefined;
|
68
|
-
dispatch("clear");
|
69
|
-
}
|
70
|
-
|
71
|
-
async function handle_save(
|
72
|
-
{ detail }: { detail: string },
|
73
|
-
initial
|
74
|
-
): Promise<void> {
|
75
|
-
if (mode === "mask") {
|
76
|
-
if (source === "webcam" && initial) {
|
77
|
-
value = {
|
78
|
-
image: detail,
|
79
|
-
mask: null
|
80
|
-
};
|
81
|
-
} else {
|
82
|
-
value = {
|
83
|
-
image: typeof value === "string" ? value : value?.image || null,
|
84
|
-
mask: detail
|
85
|
-
};
|
86
|
-
}
|
87
|
-
} else if (
|
88
|
-
(source === "upload" || source === "webcam") &&
|
89
|
-
tool === "sketch"
|
90
|
-
) {
|
91
|
-
value = { image: detail, mask: null };
|
92
|
-
} else {
|
93
|
-
value = detail;
|
94
|
-
}
|
95
|
-
|
96
|
-
await tick();
|
97
|
-
|
98
|
-
dispatch(streaming ? "stream" : "edit");
|
99
|
-
}
|
100
|
-
|
101
|
-
const dispatch = createEventDispatcher<{
|
102
|
-
change: string | null;
|
103
|
-
stream: string | null;
|
104
|
-
edit: undefined;
|
105
|
-
clear: undefined;
|
106
|
-
drag: boolean;
|
107
|
-
upload: FileData;
|
108
|
-
select: SelectData;
|
109
|
-
}>();
|
110
|
-
|
111
|
-
let dragging = false;
|
112
|
-
|
113
|
-
$: dispatch("drag", dragging);
|
114
|
-
|
115
|
-
let value_: null | FileData = null;
|
116
|
-
|
117
|
-
$: if (value !== value_) {
|
118
|
-
value_ = value;
|
119
|
-
normalise_file(value_, root, null);
|
120
|
-
}
|
121
|
-
|
122
|
-
function handle_image_load(event: Event): void {
|
123
|
-
const element = event.currentTarget as HTMLImageElement;
|
124
|
-
img_width = element.naturalWidth;
|
125
|
-
img_height = element.naturalHeight;
|
126
|
-
container_height = element.getBoundingClientRect().height;
|
127
|
-
}
|
128
|
-
|
129
|
-
async function handle_sketch_clear(): Promise<void> {
|
130
|
-
dispatch("clear");
|
131
|
-
sketch.clear();
|
132
|
-
await tick();
|
133
|
-
value = null;
|
134
|
-
static_image = undefined;
|
135
|
-
}
|
136
|
-
|
137
|
-
async function handle_mask_clear(): Promise<void> {
|
138
|
-
sketch.clear_mask();
|
139
|
-
await tick();
|
140
|
-
}
|
141
|
-
|
142
|
-
let img_height = 0;
|
143
|
-
let img_width = 0;
|
144
|
-
let container_height = 0;
|
145
|
-
|
146
|
-
let mode;
|
147
|
-
|
148
|
-
$: {
|
149
|
-
if (source === "canvas" && tool === "sketch") {
|
150
|
-
mode = "bw-sketch";
|
151
|
-
} else if (tool === "color-sketch") {
|
152
|
-
mode = "color-sketch";
|
153
|
-
} else if (
|
154
|
-
(source === "upload" || source === "webcam") &&
|
155
|
-
tool === "sketch"
|
156
|
-
) {
|
157
|
-
mode = "mask";
|
158
|
-
} else {
|
159
|
-
mode = "editor";
|
160
|
-
}
|
161
|
-
}
|
162
|
-
let value_img;
|
163
|
-
let max_height;
|
164
|
-
let max_width;
|
165
|
-
|
166
|
-
let static_image = undefined;
|
167
|
-
|
168
|
-
$: {
|
169
|
-
if (value === null || (value.image === null && value.mask === null)) {
|
170
|
-
static_image = undefined;
|
171
|
-
}
|
172
|
-
}
|
173
|
-
|
174
|
-
$: {
|
175
|
-
if (cropper) {
|
176
|
-
if (value) {
|
177
|
-
cropper.image = value;
|
178
|
-
cropper.create();
|
179
|
-
} else {
|
180
|
-
cropper.destroy();
|
181
|
-
}
|
182
|
-
}
|
183
|
-
}
|
184
|
-
|
185
|
-
onMount(async () => {
|
186
|
-
if (tool === "color-sketch" && value && typeof value === "string") {
|
187
|
-
static_image = value;
|
188
|
-
await tick();
|
189
|
-
handle_image_load({ currentTarget: value_img });
|
190
|
-
}
|
191
|
-
});
|
192
|
-
|
193
|
-
function handle_click(evt: MouseEvent): void {
|
194
|
-
let coordinates = get_coordinates_of_clicked_image(evt);
|
195
|
-
if (coordinates) {
|
196
|
-
dispatch("select", { index: coordinates, value: null });
|
197
|
-
}
|
198
|
-
}
|
199
|
-
</script>
|
200
|
-
|
201
|
-
<BlockLabel
|
202
|
-
{show_label}
|
203
|
-
Icon={source === "canvas" ? SketchIcon : Image}
|
204
|
-
label={label || (source === "canvas" ? "Sketch" : "Image")}
|
205
|
-
/>
|
206
|
-
|
207
|
-
<div
|
208
|
-
data-testid="image"
|
209
|
-
class="image-container"
|
210
|
-
bind:offsetHeight={max_height}
|
211
|
-
bind:offsetWidth={max_width}
|
212
|
-
>
|
213
|
-
{#if source === "upload"}
|
214
|
-
<Upload
|
215
|
-
bind:dragging
|
216
|
-
filetype="image/*"
|
217
|
-
on:load={handle_upload}
|
218
|
-
include_file_metadata={false}
|
219
|
-
disable_click={!!value}
|
220
|
-
{root}
|
221
|
-
>
|
222
|
-
{#if (value === null && !static_image) || streaming}
|
223
|
-
<slot />
|
224
|
-
{:else if tool === "select"}
|
225
|
-
<Cropper
|
226
|
-
bind:this={cropper}
|
227
|
-
image={value_.data}
|
228
|
-
on:crop={handle_save}
|
229
|
-
/>
|
230
|
-
<ModifyUpload
|
231
|
-
{i18n}
|
232
|
-
on:clear={(e) => (handle_clear(e), (tool = "editor"))}
|
233
|
-
/>
|
234
|
-
{:else if tool === "editor"}
|
235
|
-
<ModifyUpload
|
236
|
-
{i18n}
|
237
|
-
on:edit={() => (tool = "select")}
|
238
|
-
on:clear={handle_clear}
|
239
|
-
editable
|
240
|
-
/>
|
241
|
-
|
242
|
-
<!-- TODO: fix-->
|
243
|
-
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
244
|
-
<!-- svelte-ignore a11y-no-noninteractive-element-interactions-->
|
245
|
-
<img
|
246
|
-
src={value_.data}
|
247
|
-
alt=""
|
248
|
-
class:scale-x-[-1]={source === "webcam" && mirror_webcam}
|
249
|
-
class:selectable
|
250
|
-
on:click={handle_click}
|
251
|
-
loading="lazy"
|
252
|
-
/>
|
253
|
-
{:else if (tool === "sketch" || tool === "color-sketch") && (value !== null || static_image)}
|
254
|
-
{#key static_image}
|
255
|
-
<img
|
256
|
-
bind:this={value_img}
|
257
|
-
class="absolute-img"
|
258
|
-
src={static_image || value?.image?.data || value?.data}
|
259
|
-
alt=""
|
260
|
-
on:load={handle_image_load}
|
261
|
-
class:webcam={source === "webcam" && mirror_webcam}
|
262
|
-
loading="lazy"
|
263
|
-
crossorigin="anonymous"
|
264
|
-
/>
|
265
|
-
{/key}
|
266
|
-
{#if img_width > 0}
|
267
|
-
<Sketch
|
268
|
-
{value}
|
269
|
-
bind:this={sketch}
|
270
|
-
bind:brush_radius
|
271
|
-
bind:brush_color
|
272
|
-
{mask_opacity}
|
273
|
-
on:change={handle_save}
|
274
|
-
{mode}
|
275
|
-
width={img_width || max_width}
|
276
|
-
height={img_height || max_height}
|
277
|
-
container_height={container_height || max_height}
|
278
|
-
{value_img}
|
279
|
-
{source}
|
280
|
-
{shape}
|
281
|
-
/>
|
282
|
-
<ModifySketch
|
283
|
-
show_eraser={value_img}
|
284
|
-
on:undo={() => sketch.undo()}
|
285
|
-
on:clear_mask={handle_mask_clear}
|
286
|
-
on:remove_image={handle_sketch_clear}
|
287
|
-
/>
|
288
|
-
{#if tool === "color-sketch" || tool === "sketch"}
|
289
|
-
<SketchSettings
|
290
|
-
bind:brush_radius
|
291
|
-
bind:brush_color
|
292
|
-
container_height={container_height || max_height}
|
293
|
-
img_width={img_width || max_width}
|
294
|
-
img_height={img_height || max_height}
|
295
|
-
{mode}
|
296
|
-
/>
|
297
|
-
{/if}
|
298
|
-
{/if}
|
299
|
-
{:else}
|
300
|
-
<!-- TODO: fix-->
|
301
|
-
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
302
|
-
<!-- svelte-ignore a11y-no-noninteractive-element-interactions-->
|
303
|
-
<img
|
304
|
-
src={value.image || value}
|
305
|
-
alt=""
|
306
|
-
class:webcam={source === "webcam" && mirror_webcam}
|
307
|
-
class:selectable
|
308
|
-
on:click={handle_click}
|
309
|
-
loading="lazy"
|
310
|
-
/>
|
311
|
-
{/if}
|
312
|
-
</Upload>
|
313
|
-
{:else if source === "canvas"}
|
314
|
-
<ModifySketch
|
315
|
-
on:undo={() => sketch.undo()}
|
316
|
-
on:remove_image={handle_sketch_clear}
|
317
|
-
/>
|
318
|
-
{#if tool === "color-sketch"}
|
319
|
-
<SketchSettings
|
320
|
-
bind:brush_radius
|
321
|
-
bind:brush_color
|
322
|
-
container_height={container_height || max_height}
|
323
|
-
img_width={img_width || max_width}
|
324
|
-
img_height={img_height || max_height}
|
325
|
-
/>
|
326
|
-
{/if}
|
327
|
-
<Sketch
|
328
|
-
{value}
|
329
|
-
bind:brush_radius
|
330
|
-
bind:brush_color
|
331
|
-
bind:this={sketch}
|
332
|
-
on:change={handle_save}
|
333
|
-
on:clear={handle_sketch_clear}
|
334
|
-
{mode}
|
335
|
-
width={img_width || max_width}
|
336
|
-
height={img_height || max_height}
|
337
|
-
container_height={container_height || max_height}
|
338
|
-
{shape}
|
339
|
-
/>
|
340
|
-
{:else if (value === null && !static_image) || streaming}
|
341
|
-
{#if source === "webcam" && !static_image}
|
342
|
-
<Webcam
|
343
|
-
on:capture={(e) =>
|
344
|
-
tool === "color-sketch" ? handle_upload(e) : handle_save(e, true)}
|
345
|
-
on:stream={handle_save}
|
346
|
-
on:error
|
347
|
-
{streaming}
|
348
|
-
{pending}
|
349
|
-
{mirror_webcam}
|
350
|
-
/>
|
351
|
-
{/if}
|
352
|
-
{:else if tool === "select"}
|
353
|
-
<Cropper bind:this={cropper} image={value.data} on:crop={handle_save} />
|
354
|
-
<ModifyUpload
|
355
|
-
{i18n}
|
356
|
-
on:clear={(e) => (handle_clear(e), (tool = "editor"))}
|
357
|
-
/>
|
358
|
-
{:else if tool === "editor"}
|
359
|
-
<ModifyUpload
|
360
|
-
{i18n}
|
361
|
-
on:edit={() => (tool = "select")}
|
362
|
-
on:clear={handle_clear}
|
363
|
-
editable
|
364
|
-
/>
|
365
|
-
|
366
|
-
<!-- TODO: fix -->
|
367
|
-
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
368
|
-
<!-- svelte-ignore a11y-no-noninteractive-element-interactions-->
|
369
|
-
<img
|
370
|
-
src={value_}
|
371
|
-
alt=""
|
372
|
-
class:selectable
|
373
|
-
class:webcam={source === "webcam" && mirror_webcam}
|
374
|
-
on:click={handle_click}
|
375
|
-
loading="lazy"
|
376
|
-
/>
|
377
|
-
{:else if (tool === "sketch" || tool === "color-sketch") && (value !== null || static_image)}
|
378
|
-
{#key static_image}
|
379
|
-
<img
|
380
|
-
bind:this={value_img}
|
381
|
-
class="absolute-img"
|
382
|
-
src={static_image || value_}
|
383
|
-
alt=""
|
384
|
-
on:load={handle_image_load}
|
385
|
-
class:webcam={source === "webcam" && mirror_webcam}
|
386
|
-
loading="lazy"
|
387
|
-
/>
|
388
|
-
{/key}
|
389
|
-
{#if img_width > 0}
|
390
|
-
<Sketch
|
391
|
-
{value}
|
392
|
-
bind:this={sketch}
|
393
|
-
bind:brush_radius
|
394
|
-
bind:brush_color
|
395
|
-
on:change={handle_save}
|
396
|
-
{mode}
|
397
|
-
width={img_width || max_width}
|
398
|
-
height={img_height || max_height}
|
399
|
-
container_height={container_height || max_height}
|
400
|
-
{value_img}
|
401
|
-
{source}
|
402
|
-
/>
|
403
|
-
<ModifySketch
|
404
|
-
on:undo={() => sketch.undo()}
|
405
|
-
on:remove_image={handle_sketch_clear}
|
406
|
-
/>
|
407
|
-
{#if tool === "color-sketch" || tool === "sketch"}
|
408
|
-
<SketchSettings
|
409
|
-
bind:brush_radius
|
410
|
-
bind:brush_color
|
411
|
-
container_height={container_height || max_height}
|
412
|
-
img_width={img_width || max_width}
|
413
|
-
img_height={img_height || max_height}
|
414
|
-
{mode}
|
415
|
-
/>
|
416
|
-
{/if}
|
417
|
-
{/if}
|
418
|
-
{:else}
|
419
|
-
<!-- TODO: fix -->
|
420
|
-
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
421
|
-
<!-- svelte-ignore a11y-no-noninteractive-element-interactions-->
|
422
|
-
|
423
|
-
<img
|
424
|
-
src={value_.image || value_.data}
|
425
|
-
alt=""
|
426
|
-
class:webcam={source === "webcam" && mirror_webcam}
|
427
|
-
class:selectable
|
428
|
-
on:click={handle_click}
|
429
|
-
loading="lazy"
|
430
|
-
/>
|
431
|
-
{/if}
|
432
|
-
</div>
|
433
|
-
|
434
|
-
<style>
|
435
|
-
.image-container,
|
436
|
-
img {
|
437
|
-
width: var(--size-full);
|
438
|
-
height: var(--size-full);
|
439
|
-
}
|
440
|
-
img {
|
441
|
-
object-fit: contain;
|
442
|
-
}
|
443
|
-
|
444
|
-
.selectable {
|
445
|
-
cursor: crosshair;
|
446
|
-
}
|
447
|
-
|
448
|
-
.absolute-img {
|
449
|
-
position: absolute;
|
450
|
-
opacity: 0;
|
451
|
-
}
|
452
|
-
|
453
|
-
.webcam {
|
454
|
-
transform: scaleX(-1);
|
455
|
-
}
|
456
|
-
</style>
|