@gradio/image 0.3.0-beta.5

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.
@@ -0,0 +1,456 @@
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 "../shared/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.data}
305
+ alt="hello"
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>
@@ -0,0 +1,108 @@
1
+ <svelte:options accessors={true} />
2
+
3
+ <script lang="ts">
4
+ import type { Gradio, SelectData, ShareData } from "@gradio/utils";
5
+ import Image from "./Image.svelte";
6
+
7
+ import { Block } from "@gradio/atoms";
8
+
9
+ import { StatusTracker } from "@gradio/statustracker";
10
+ import type { LoadingStatus } from "@gradio/statustracker";
11
+ import { UploadText } from "@gradio/atoms";
12
+ import type { FileData } from "js/upload/src";
13
+
14
+ export let elem_id = "";
15
+ export let elem_classes: string[] = [];
16
+ export let visible = true;
17
+ export let value: null | FileData = null;
18
+ export let source: "canvas" | "webcam" | "upload" = "upload";
19
+ export let tool: "editor" | "select" | "sketch" | "color-sketch" = "editor";
20
+ export let label: string;
21
+ export let show_label: boolean;
22
+ export let streaming: boolean;
23
+ export let pending: boolean;
24
+ export let height: number | undefined;
25
+ export let width: number | undefined;
26
+ export let mirror_webcam: boolean;
27
+ export let shape: [number, number];
28
+ export let brush_radius: number;
29
+ export let brush_color: string;
30
+ export let mask_opacity: number;
31
+ export let selectable = false;
32
+ export let container = true;
33
+ export let scale: number | null = null;
34
+ export let min_width: number | undefined = undefined;
35
+ export let loading_status: LoadingStatus;
36
+ export let root: string;
37
+
38
+ export let gradio: Gradio<{
39
+ change: never;
40
+ error: string;
41
+ edit: never;
42
+ stream: never;
43
+ drag: never;
44
+ upload: never;
45
+ clear: never;
46
+ select: SelectData;
47
+ share: ShareData;
48
+ }>;
49
+
50
+ $: value, gradio.dispatch("change");
51
+ let dragging: boolean;
52
+ const FIXED_HEIGHT = 240;
53
+
54
+ $: value = !value ? null : value;
55
+ </script>
56
+
57
+ <Block
58
+ {visible}
59
+ variant={value === null && source === "upload" ? "dashed" : "solid"}
60
+ border_mode={dragging ? "focus" : "base"}
61
+ padding={false}
62
+ {elem_id}
63
+ {elem_classes}
64
+ height={height || (source === "webcam" ? undefined : FIXED_HEIGHT)}
65
+ {width}
66
+ allow_overflow={false}
67
+ {container}
68
+ {scale}
69
+ {min_width}
70
+ >
71
+ <StatusTracker
72
+ autoscroll={gradio.autoscroll}
73
+ i18n={gradio.i18n}
74
+ {...loading_status}
75
+ />
76
+
77
+ <Image
78
+ {brush_radius}
79
+ {brush_color}
80
+ {shape}
81
+ bind:value
82
+ {source}
83
+ {tool}
84
+ {selectable}
85
+ {mask_opacity}
86
+ {root}
87
+ on:edit={() => gradio.dispatch("edit")}
88
+ on:clear={() => gradio.dispatch("clear")}
89
+ on:stream={() => gradio.dispatch("stream")}
90
+ on:drag={({ detail }) => (dragging = detail)}
91
+ on:upload={() => gradio.dispatch("upload")}
92
+ on:select={({ detail }) => gradio.dispatch("select", detail)}
93
+ on:share={({ detail }) => gradio.dispatch("share", detail)}
94
+ on:error={({ detail }) => {
95
+ loading_status = loading_status || {};
96
+ loading_status.status = "error";
97
+ gradio.dispatch("error", detail);
98
+ }}
99
+ {label}
100
+ {show_label}
101
+ {pending}
102
+ {streaming}
103
+ {mirror_webcam}
104
+ i18n={gradio.i18n}
105
+ >
106
+ <UploadText i18n={gradio.i18n} type="image" />
107
+ </Image>
108
+ </Block>
@@ -0,0 +1,45 @@
1
+ <script lang="ts">
2
+ import { createEventDispatcher } from "svelte";
3
+ import { IconButton } from "@gradio/atoms";
4
+ import { Undo, Clear, Erase } from "@gradio/icons";
5
+
6
+ const dispatch = createEventDispatcher();
7
+
8
+ export let show_eraser = false;
9
+ </script>
10
+
11
+ <div>
12
+ <IconButton Icon={Undo} label="Undo" on:click={() => dispatch("undo")} />
13
+
14
+ {#if show_eraser}
15
+ <IconButton
16
+ Icon={Erase}
17
+ label="Clear"
18
+ on:click={(event) => {
19
+ dispatch("clear_mask");
20
+ event.stopPropagation();
21
+ }}
22
+ />
23
+ {/if}
24
+
25
+ <IconButton
26
+ Icon={Clear}
27
+ label="Remove Image"
28
+ on:click={(event) => {
29
+ dispatch("remove_image");
30
+ event.stopPropagation();
31
+ }}
32
+ />
33
+ </div>
34
+
35
+ <style>
36
+ div {
37
+ display: flex;
38
+ position: absolute;
39
+ top: var(--size-2);
40
+ right: var(--size-2);
41
+ justify-content: flex-end;
42
+ gap: var(--spacing-sm);
43
+ z-index: var(--layer-5);
44
+ }
45
+ </style>