@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.
- package/CHANGELOG.md +122 -0
- package/Image.stories.svelte +50 -0
- package/Image.test.ts +61 -0
- package/LICENSE +201 -0
- package/README.md +11 -0
- package/example/Image.svelte +41 -0
- package/example/index.ts +1 -0
- package/interactive/Cropper.svelte +33 -0
- package/interactive/Image.svelte +456 -0
- package/interactive/InteractiveImage.svelte +108 -0
- package/interactive/ModifySketch.svelte +45 -0
- package/interactive/Sketch.svelte +626 -0
- package/interactive/SketchSettings.svelte +77 -0
- package/interactive/Webcam.svelte +204 -0
- package/interactive/index.ts +2 -0
- package/package.json +27 -0
- package/shared/utils.ts +24 -0
- package/src/Image.svelte +0 -0
- package/static/ImagePreview.svelte +95 -0
- package/static/StaticImage.svelte +76 -0
- package/static/index.ts +1 -0
@@ -0,0 +1,626 @@
|
|
1
|
+
<script>
|
2
|
+
// @ts-nocheck
|
3
|
+
/* eslint-disable */
|
4
|
+
|
5
|
+
import { onMount, onDestroy, createEventDispatcher, tick } from "svelte";
|
6
|
+
import { fade } from "svelte/transition";
|
7
|
+
import { LazyBrush } from "lazy-brush/src";
|
8
|
+
import ResizeObserver from "resize-observer-polyfill";
|
9
|
+
|
10
|
+
const dispatch = createEventDispatcher();
|
11
|
+
|
12
|
+
export let value;
|
13
|
+
export let value_img;
|
14
|
+
export let mode = "sketch";
|
15
|
+
export let brush_color = "#0b0f19";
|
16
|
+
export let brush_radius;
|
17
|
+
export let mask_opacity = 0.7;
|
18
|
+
export let source;
|
19
|
+
|
20
|
+
export let width = 400;
|
21
|
+
export let height = 200;
|
22
|
+
export let container_height = 200;
|
23
|
+
export let shape;
|
24
|
+
|
25
|
+
$: {
|
26
|
+
if (shape && (width || height)) {
|
27
|
+
width = shape[0];
|
28
|
+
height = shape[1];
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
let mounted;
|
33
|
+
|
34
|
+
let catenary_color = "#aaa";
|
35
|
+
|
36
|
+
let canvas_width = width;
|
37
|
+
let canvas_height = height;
|
38
|
+
|
39
|
+
$: mounted && !value && clear();
|
40
|
+
|
41
|
+
let last_value_img;
|
42
|
+
|
43
|
+
$: console.log(value_img);
|
44
|
+
|
45
|
+
$: {
|
46
|
+
if (mounted && value_img !== last_value_img) {
|
47
|
+
last_value_img = value_img;
|
48
|
+
|
49
|
+
clear();
|
50
|
+
|
51
|
+
setTimeout(() => {
|
52
|
+
if (source === "webcam") {
|
53
|
+
ctx.temp.save();
|
54
|
+
ctx.temp.translate(width, 0);
|
55
|
+
ctx.temp.scale(-1, 1);
|
56
|
+
ctx.temp.drawImage(value_img, 0, 0);
|
57
|
+
ctx.temp.restore();
|
58
|
+
} else {
|
59
|
+
draw_cropped_image();
|
60
|
+
}
|
61
|
+
|
62
|
+
ctx.drawing.drawImage(canvas.temp, 0, 0, width, height);
|
63
|
+
|
64
|
+
draw_lines({ lines: lines.slice() });
|
65
|
+
trigger_on_change();
|
66
|
+
}, 50);
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
function mid_point(p1, p2) {
|
71
|
+
return {
|
72
|
+
x: p1.x + (p2.x - p1.x) / 2,
|
73
|
+
y: p1.y + (p2.y - p1.y) / 2
|
74
|
+
};
|
75
|
+
}
|
76
|
+
|
77
|
+
const canvas_types = [
|
78
|
+
{
|
79
|
+
name: "interface",
|
80
|
+
zIndex: 15
|
81
|
+
},
|
82
|
+
{
|
83
|
+
name: "mask",
|
84
|
+
zIndex: 13,
|
85
|
+
opacity: mask_opacity
|
86
|
+
},
|
87
|
+
{
|
88
|
+
name: "drawing",
|
89
|
+
zIndex: 11
|
90
|
+
},
|
91
|
+
{
|
92
|
+
name: "temp",
|
93
|
+
zIndex: 12
|
94
|
+
}
|
95
|
+
];
|
96
|
+
|
97
|
+
let canvas = {};
|
98
|
+
let ctx = {};
|
99
|
+
let points = [];
|
100
|
+
let lines = [];
|
101
|
+
let mouse_has_moved = true;
|
102
|
+
let values_changed = true;
|
103
|
+
let is_drawing = false;
|
104
|
+
let is_pressing = false;
|
105
|
+
let lazy = null;
|
106
|
+
let canvas_container = null;
|
107
|
+
let canvas_observer = null;
|
108
|
+
let line_count = 0;
|
109
|
+
|
110
|
+
function draw_cropped_image() {
|
111
|
+
if (!shape) {
|
112
|
+
ctx.temp.drawImage(value_img, 0, 0, width, height);
|
113
|
+
return;
|
114
|
+
}
|
115
|
+
|
116
|
+
let _width = value_img.naturalWidth;
|
117
|
+
let _height = value_img.naturalHeight;
|
118
|
+
|
119
|
+
const shape_ratio = shape[0] / shape[1];
|
120
|
+
const image_ratio = _width / _height;
|
121
|
+
|
122
|
+
let x = 0;
|
123
|
+
let y = 0;
|
124
|
+
|
125
|
+
if (shape_ratio < image_ratio) {
|
126
|
+
_width = shape[1] * image_ratio;
|
127
|
+
_height = shape[1];
|
128
|
+
x = (shape[0] - _width) / 2;
|
129
|
+
} else if (shape_ratio > image_ratio) {
|
130
|
+
_width = shape[0];
|
131
|
+
_height = shape[0] / image_ratio;
|
132
|
+
y = (shape[1] - _height) / 2;
|
133
|
+
} else {
|
134
|
+
x = 0;
|
135
|
+
y = 0;
|
136
|
+
_width = shape[0];
|
137
|
+
_height = shape[1];
|
138
|
+
}
|
139
|
+
|
140
|
+
ctx.temp.drawImage(value_img, x, y, _width, _height);
|
141
|
+
}
|
142
|
+
|
143
|
+
onMount(async () => {
|
144
|
+
Object.keys(canvas).forEach((key) => {
|
145
|
+
ctx[key] = canvas[key].getContext("2d");
|
146
|
+
});
|
147
|
+
|
148
|
+
await tick();
|
149
|
+
|
150
|
+
if (value_img) {
|
151
|
+
value_img.addEventListener("load", (_) => {
|
152
|
+
if (source === "webcam") {
|
153
|
+
ctx.temp.save();
|
154
|
+
ctx.temp.translate(width, 0);
|
155
|
+
ctx.temp.scale(-1, 1);
|
156
|
+
ctx.temp.drawImage(value_img, 0, 0);
|
157
|
+
ctx.temp.restore();
|
158
|
+
} else {
|
159
|
+
draw_cropped_image();
|
160
|
+
}
|
161
|
+
ctx.drawing.drawImage(canvas.temp, 0, 0, width, height);
|
162
|
+
|
163
|
+
trigger_on_change();
|
164
|
+
});
|
165
|
+
|
166
|
+
setTimeout(() => {
|
167
|
+
if (source === "webcam") {
|
168
|
+
ctx.temp.save();
|
169
|
+
ctx.temp.translate(width, 0);
|
170
|
+
ctx.temp.scale(-1, 1);
|
171
|
+
ctx.temp.drawImage(value_img, 0, 0);
|
172
|
+
ctx.temp.restore();
|
173
|
+
} else {
|
174
|
+
draw_cropped_image();
|
175
|
+
}
|
176
|
+
|
177
|
+
ctx.drawing.drawImage(canvas.temp, 0, 0, width, height);
|
178
|
+
|
179
|
+
draw_lines({ lines: lines.slice() });
|
180
|
+
trigger_on_change();
|
181
|
+
}, 100);
|
182
|
+
}
|
183
|
+
|
184
|
+
lazy = new LazyBrush({
|
185
|
+
radius: brush_radius * 0.05,
|
186
|
+
enabled: true,
|
187
|
+
initialPoint: {
|
188
|
+
x: width / 2,
|
189
|
+
y: height / 2
|
190
|
+
}
|
191
|
+
});
|
192
|
+
|
193
|
+
canvas_observer = new ResizeObserver((entries, observer, ...rest) => {
|
194
|
+
handle_canvas_resize(entries, observer);
|
195
|
+
});
|
196
|
+
canvas_observer.observe(canvas_container);
|
197
|
+
|
198
|
+
loop();
|
199
|
+
mounted = true;
|
200
|
+
|
201
|
+
requestAnimationFrame(() => {
|
202
|
+
init();
|
203
|
+
requestAnimationFrame(() => {
|
204
|
+
clear();
|
205
|
+
});
|
206
|
+
});
|
207
|
+
});
|
208
|
+
|
209
|
+
function init() {
|
210
|
+
const initX = width / 2;
|
211
|
+
const initY = height / 2;
|
212
|
+
lazy.update({ x: initX, y: initY }, { both: true });
|
213
|
+
lazy.update({ x: initX, y: initY }, { both: false });
|
214
|
+
mouse_has_moved = true;
|
215
|
+
values_changed = true;
|
216
|
+
}
|
217
|
+
|
218
|
+
onDestroy(() => {
|
219
|
+
mounted = false;
|
220
|
+
canvas_observer.unobserve(canvas_container);
|
221
|
+
});
|
222
|
+
|
223
|
+
function redraw_image(_lines) {
|
224
|
+
clear_canvas();
|
225
|
+
|
226
|
+
if (value_img) {
|
227
|
+
if (source === "webcam") {
|
228
|
+
ctx.temp.save();
|
229
|
+
ctx.temp.translate(width, 0);
|
230
|
+
ctx.temp.scale(-1, 1);
|
231
|
+
ctx.temp.drawImage(value_img, 0, 0);
|
232
|
+
ctx.temp.restore();
|
233
|
+
} else {
|
234
|
+
draw_cropped_image();
|
235
|
+
}
|
236
|
+
|
237
|
+
if (!lines || !lines.length) {
|
238
|
+
ctx.drawing.drawImage(canvas.temp, 0, 0, width, height);
|
239
|
+
}
|
240
|
+
}
|
241
|
+
|
242
|
+
draw_lines({ lines: _lines });
|
243
|
+
line_count = _lines.length;
|
244
|
+
|
245
|
+
lines = _lines;
|
246
|
+
ctx.drawing.drawImage(canvas.temp, 0, 0, width, height);
|
247
|
+
|
248
|
+
if (lines.length == 0) {
|
249
|
+
dispatch("clear");
|
250
|
+
}
|
251
|
+
}
|
252
|
+
|
253
|
+
export function clear_mask() {
|
254
|
+
const _lines = [];
|
255
|
+
|
256
|
+
redraw_image(_lines);
|
257
|
+
trigger_on_change();
|
258
|
+
}
|
259
|
+
|
260
|
+
export function undo() {
|
261
|
+
const _lines = lines.slice(0, -1);
|
262
|
+
|
263
|
+
redraw_image(_lines);
|
264
|
+
trigger_on_change();
|
265
|
+
}
|
266
|
+
|
267
|
+
let get_save_data = () => {
|
268
|
+
return JSON.stringify({
|
269
|
+
lines: lines,
|
270
|
+
width: canvas_width,
|
271
|
+
height: canvas_height
|
272
|
+
});
|
273
|
+
};
|
274
|
+
|
275
|
+
let draw_lines = ({ lines }) => {
|
276
|
+
lines.forEach((line) => {
|
277
|
+
const { points: _points, brush_color, brush_radius } = line;
|
278
|
+
draw_points({
|
279
|
+
points: _points,
|
280
|
+
brush_color,
|
281
|
+
brush_radius,
|
282
|
+
mask: mode === "mask"
|
283
|
+
});
|
284
|
+
});
|
285
|
+
|
286
|
+
saveLine({ brush_color, brush_radius });
|
287
|
+
if (mode === "mask") {
|
288
|
+
save_mask_line();
|
289
|
+
}
|
290
|
+
};
|
291
|
+
|
292
|
+
let handle_draw_start = (e) => {
|
293
|
+
e.preventDefault();
|
294
|
+
is_pressing = true;
|
295
|
+
const { x, y } = get_pointer_pos(e);
|
296
|
+
if (e.touches && e.touches.length > 0) {
|
297
|
+
lazy.update({ x, y }, { both: true });
|
298
|
+
}
|
299
|
+
handle_pointer_move(x, y);
|
300
|
+
line_count += 1;
|
301
|
+
};
|
302
|
+
|
303
|
+
let handle_draw_move = (e) => {
|
304
|
+
e.preventDefault();
|
305
|
+
const { x, y } = get_pointer_pos(e);
|
306
|
+
handle_pointer_move(x, y);
|
307
|
+
};
|
308
|
+
|
309
|
+
let handle_draw_end = (e) => {
|
310
|
+
e.preventDefault();
|
311
|
+
handle_draw_move(e);
|
312
|
+
is_drawing = false;
|
313
|
+
is_pressing = false;
|
314
|
+
saveLine();
|
315
|
+
|
316
|
+
if (mode === "mask") {
|
317
|
+
save_mask_line();
|
318
|
+
}
|
319
|
+
};
|
320
|
+
|
321
|
+
let old_width = 0;
|
322
|
+
let old_height = 0;
|
323
|
+
let old_container_height = 0;
|
324
|
+
let add_lr_border = false;
|
325
|
+
|
326
|
+
let handle_canvas_resize = async () => {
|
327
|
+
if (shape && canvas_container) {
|
328
|
+
const x = canvas_container?.getBoundingClientRect();
|
329
|
+
const shape_ratio = shape[0] / shape[1];
|
330
|
+
const container_ratio = x.width / x.height;
|
331
|
+
add_lr_border = shape_ratio < container_ratio;
|
332
|
+
}
|
333
|
+
|
334
|
+
if (
|
335
|
+
width === old_width &&
|
336
|
+
height === old_height &&
|
337
|
+
old_container_height === container_height
|
338
|
+
) {
|
339
|
+
return;
|
340
|
+
}
|
341
|
+
const dimensions = { width: width, height: height };
|
342
|
+
|
343
|
+
const container_dimensions = {
|
344
|
+
height: container_height,
|
345
|
+
width: container_height * (dimensions.width / dimensions.height)
|
346
|
+
};
|
347
|
+
|
348
|
+
await Promise.all([
|
349
|
+
set_canvas_size(canvas.interface, dimensions, container_dimensions),
|
350
|
+
set_canvas_size(canvas.drawing, dimensions, container_dimensions),
|
351
|
+
set_canvas_size(canvas.temp, dimensions, container_dimensions),
|
352
|
+
set_canvas_size(canvas.mask, dimensions, container_dimensions, false)
|
353
|
+
]);
|
354
|
+
|
355
|
+
if (!brush_radius) {
|
356
|
+
brush_radius = 20 * (dimensions.width / container_dimensions.width);
|
357
|
+
}
|
358
|
+
|
359
|
+
loop({ once: true });
|
360
|
+
|
361
|
+
setTimeout(() => {
|
362
|
+
old_height = height;
|
363
|
+
old_width = width;
|
364
|
+
old_container_height = container_height;
|
365
|
+
}, 10);
|
366
|
+
await tick();
|
367
|
+
|
368
|
+
clear();
|
369
|
+
};
|
370
|
+
|
371
|
+
$: {
|
372
|
+
if (lazy) {
|
373
|
+
init();
|
374
|
+
lazy.setRadius(brush_radius * 0.05);
|
375
|
+
}
|
376
|
+
}
|
377
|
+
|
378
|
+
$: {
|
379
|
+
if (width || height) {
|
380
|
+
handle_canvas_resize();
|
381
|
+
}
|
382
|
+
}
|
383
|
+
|
384
|
+
let set_canvas_size = async (canvas, dimensions, container, scale = true) => {
|
385
|
+
if (!mounted) return;
|
386
|
+
await tick();
|
387
|
+
|
388
|
+
const dpr = window.devicePixelRatio || 1;
|
389
|
+
canvas.width = dimensions.width * (scale ? dpr : 1);
|
390
|
+
canvas.height = dimensions.height * (scale ? dpr : 1);
|
391
|
+
|
392
|
+
const ctx = canvas.getContext("2d");
|
393
|
+
scale && ctx.scale(dpr, dpr);
|
394
|
+
|
395
|
+
canvas.style.width = `${container.width}px`;
|
396
|
+
canvas.style.height = `${container.height}px`;
|
397
|
+
};
|
398
|
+
|
399
|
+
let get_pointer_pos = (e) => {
|
400
|
+
const rect = canvas.interface.getBoundingClientRect();
|
401
|
+
|
402
|
+
let clientX = e.clientX;
|
403
|
+
let clientY = e.clientY;
|
404
|
+
if (e.changedTouches && e.changedTouches.length > 0) {
|
405
|
+
clientX = e.changedTouches[0].clientX;
|
406
|
+
clientY = e.changedTouches[0].clientY;
|
407
|
+
}
|
408
|
+
|
409
|
+
return {
|
410
|
+
x: ((clientX - rect.left) / rect.width) * width,
|
411
|
+
y: ((clientY - rect.top) / rect.height) * height
|
412
|
+
};
|
413
|
+
};
|
414
|
+
|
415
|
+
let handle_pointer_move = (x, y) => {
|
416
|
+
lazy.update({ x: x, y: y });
|
417
|
+
const is_disabled = !lazy.isEnabled();
|
418
|
+
if ((is_pressing && !is_drawing) || (is_disabled && is_pressing)) {
|
419
|
+
is_drawing = true;
|
420
|
+
points.push(lazy.brush.toObject());
|
421
|
+
}
|
422
|
+
if (is_drawing) {
|
423
|
+
points.push(lazy.brush.toObject());
|
424
|
+
draw_points({
|
425
|
+
points: points,
|
426
|
+
brush_color,
|
427
|
+
brush_radius,
|
428
|
+
mask: mode === "mask"
|
429
|
+
});
|
430
|
+
}
|
431
|
+
mouse_has_moved = true;
|
432
|
+
};
|
433
|
+
|
434
|
+
let draw_points = ({ points, brush_color, brush_radius, mask }) => {
|
435
|
+
if (!points || points.length < 2) return;
|
436
|
+
let target_ctx = mask ? ctx.mask : ctx.temp;
|
437
|
+
target_ctx.lineJoin = "round";
|
438
|
+
target_ctx.lineCap = "round";
|
439
|
+
|
440
|
+
target_ctx.strokeStyle = brush_color;
|
441
|
+
target_ctx.lineWidth = brush_radius;
|
442
|
+
let p1 = points[0];
|
443
|
+
let p2 = points[1];
|
444
|
+
target_ctx.moveTo(p2.x, p2.y);
|
445
|
+
target_ctx.beginPath();
|
446
|
+
for (var i = 1, len = points.length; i < len; i++) {
|
447
|
+
var midPoint = mid_point(p1, p2);
|
448
|
+
target_ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y);
|
449
|
+
p1 = points[i];
|
450
|
+
p2 = points[i + 1];
|
451
|
+
}
|
452
|
+
|
453
|
+
target_ctx.lineTo(p1.x, p1.y);
|
454
|
+
target_ctx.stroke();
|
455
|
+
};
|
456
|
+
|
457
|
+
let save_mask_line = () => {
|
458
|
+
if (points.length < 1) return;
|
459
|
+
points.length = 0;
|
460
|
+
|
461
|
+
trigger_on_change();
|
462
|
+
};
|
463
|
+
|
464
|
+
let saveLine = () => {
|
465
|
+
if (points.length < 1) return;
|
466
|
+
|
467
|
+
lines.push({
|
468
|
+
points: points.slice(),
|
469
|
+
brush_color: brush_color,
|
470
|
+
brush_radius
|
471
|
+
});
|
472
|
+
|
473
|
+
if (mode !== "mask") {
|
474
|
+
points.length = 0;
|
475
|
+
}
|
476
|
+
|
477
|
+
ctx.drawing.drawImage(canvas.temp, 0, 0, width, height);
|
478
|
+
|
479
|
+
trigger_on_change();
|
480
|
+
};
|
481
|
+
|
482
|
+
let trigger_on_change = () => {
|
483
|
+
const x = get_image_data();
|
484
|
+
dispatch("change", x);
|
485
|
+
};
|
486
|
+
|
487
|
+
export function clear() {
|
488
|
+
lines = [];
|
489
|
+
clear_canvas();
|
490
|
+
line_count = 0;
|
491
|
+
|
492
|
+
return true;
|
493
|
+
}
|
494
|
+
|
495
|
+
function clear_canvas() {
|
496
|
+
values_changed = true;
|
497
|
+
ctx.temp.clearRect(0, 0, width, height);
|
498
|
+
|
499
|
+
ctx.temp.fillStyle = mode === "mask" ? "transparent" : "#FFFFFF";
|
500
|
+
ctx.temp.fillRect(0, 0, width, height);
|
501
|
+
|
502
|
+
if (mode === "mask") {
|
503
|
+
ctx.mask.clearRect(0, 0, canvas.mask.width, canvas.mask.height);
|
504
|
+
}
|
505
|
+
}
|
506
|
+
|
507
|
+
let loop = ({ once = false } = {}) => {
|
508
|
+
if (mouse_has_moved || values_changed) {
|
509
|
+
const pointer = lazy.getPointerCoordinates();
|
510
|
+
const brush = lazy.getBrushCoordinates();
|
511
|
+
draw_interface(ctx.interface, pointer, brush);
|
512
|
+
mouse_has_moved = false;
|
513
|
+
values_changed = false;
|
514
|
+
}
|
515
|
+
if (!once) {
|
516
|
+
window.requestAnimationFrame(() => {
|
517
|
+
loop();
|
518
|
+
});
|
519
|
+
}
|
520
|
+
};
|
521
|
+
|
522
|
+
$: brush_dot = brush_radius * 0.075;
|
523
|
+
|
524
|
+
let draw_interface = (ctx, pointer, brush) => {
|
525
|
+
ctx.clearRect(0, 0, width, height);
|
526
|
+
|
527
|
+
// brush preview
|
528
|
+
ctx.beginPath();
|
529
|
+
ctx.fillStyle = brush_color;
|
530
|
+
ctx.arc(brush.x, brush.y, brush_radius / 2, 0, Math.PI * 2, true);
|
531
|
+
ctx.fill();
|
532
|
+
|
533
|
+
// tiny brush point dot
|
534
|
+
ctx.beginPath();
|
535
|
+
ctx.fillStyle = catenary_color;
|
536
|
+
ctx.arc(brush.x, brush.y, brush_dot, 0, Math.PI * 2, true);
|
537
|
+
ctx.fill();
|
538
|
+
};
|
539
|
+
|
540
|
+
export function get_image_data() {
|
541
|
+
return mode === "mask"
|
542
|
+
? canvas.mask.toDataURL("image/png")
|
543
|
+
: canvas.drawing.toDataURL("image/jpg");
|
544
|
+
}
|
545
|
+
</script>
|
546
|
+
|
547
|
+
<div
|
548
|
+
class="wrap"
|
549
|
+
bind:this={canvas_container}
|
550
|
+
bind:offsetWidth={canvas_width}
|
551
|
+
bind:offsetHeight={canvas_height}
|
552
|
+
>
|
553
|
+
{#if line_count === 0}
|
554
|
+
<div transition:fade={{ duration: 50 }} class="start-prompt">
|
555
|
+
Start drawing
|
556
|
+
</div>
|
557
|
+
{/if}
|
558
|
+
{#each canvas_types as { name, zIndex, opacity }}
|
559
|
+
<canvas
|
560
|
+
key={name}
|
561
|
+
style=" z-index:{zIndex};"
|
562
|
+
style:opacity
|
563
|
+
class:lr={add_lr_border}
|
564
|
+
class:tb={!add_lr_border}
|
565
|
+
bind:this={canvas[name]}
|
566
|
+
on:mousedown={name === "interface" ? handle_draw_start : undefined}
|
567
|
+
on:mousemove={name === "interface" ? handle_draw_move : undefined}
|
568
|
+
on:mouseup={name === "interface" ? handle_draw_end : undefined}
|
569
|
+
on:mouseout={name === "interface" ? handle_draw_end : undefined}
|
570
|
+
on:blur={name === "interface" ? handle_draw_end : undefined}
|
571
|
+
on:touchstart={name === "interface" ? handle_draw_start : undefined}
|
572
|
+
on:touchmove={name === "interface" ? handle_draw_move : undefined}
|
573
|
+
on:touchend={name === "interface" ? handle_draw_end : undefined}
|
574
|
+
on:touchcancel={name === "interface" ? handle_draw_end : undefined}
|
575
|
+
on:click|stopPropagation
|
576
|
+
/>
|
577
|
+
{/each}
|
578
|
+
</div>
|
579
|
+
|
580
|
+
<style>
|
581
|
+
canvas {
|
582
|
+
display: block;
|
583
|
+
position: absolute;
|
584
|
+
top: 0px;
|
585
|
+
right: 0px;
|
586
|
+
bottom: 0px;
|
587
|
+
left: 0px;
|
588
|
+
margin: auto;
|
589
|
+
}
|
590
|
+
|
591
|
+
.lr {
|
592
|
+
border-right: 1px solid var(--border-color-primary);
|
593
|
+
border-left: 1px solid var(--border-color-primary);
|
594
|
+
}
|
595
|
+
|
596
|
+
.tb {
|
597
|
+
border-top: 1px solid var(--border-color-primary);
|
598
|
+
border-bottom: 1px solid var(--border-color-primary);
|
599
|
+
}
|
600
|
+
|
601
|
+
canvas:hover {
|
602
|
+
cursor: none;
|
603
|
+
}
|
604
|
+
|
605
|
+
.wrap {
|
606
|
+
position: relative;
|
607
|
+
width: var(--size-full);
|
608
|
+
height: var(--size-full);
|
609
|
+
touch-action: none;
|
610
|
+
}
|
611
|
+
|
612
|
+
.start-prompt {
|
613
|
+
display: flex;
|
614
|
+
position: absolute;
|
615
|
+
top: 0px;
|
616
|
+
right: 0px;
|
617
|
+
bottom: 0px;
|
618
|
+
left: 0px;
|
619
|
+
justify-content: center;
|
620
|
+
align-items: center;
|
621
|
+
z-index: var(--layer-4);
|
622
|
+
touch-action: none;
|
623
|
+
pointer-events: none;
|
624
|
+
color: var(--body-text-color-subdued);
|
625
|
+
}
|
626
|
+
</style>
|
@@ -0,0 +1,77 @@
|
|
1
|
+
<script lang="ts">
|
2
|
+
import { IconButton } from "@gradio/atoms";
|
3
|
+
import { Brush, Color } from "@gradio/icons";
|
4
|
+
|
5
|
+
let show_size = false;
|
6
|
+
let show_col = false;
|
7
|
+
|
8
|
+
export let brush_radius = 20;
|
9
|
+
export let brush_color = "#000";
|
10
|
+
export let container_height: number;
|
11
|
+
export let img_width: number;
|
12
|
+
export let img_height: number;
|
13
|
+
export let mode: "mask" | "other" = "other";
|
14
|
+
|
15
|
+
$: width = container_height * (img_width / img_height);
|
16
|
+
</script>
|
17
|
+
|
18
|
+
<div class="wrap">
|
19
|
+
<span class="brush">
|
20
|
+
<IconButton
|
21
|
+
Icon={Brush}
|
22
|
+
label="Use brush"
|
23
|
+
on:click={() => (show_size = !show_size)}
|
24
|
+
/>
|
25
|
+
{#if show_size}
|
26
|
+
<input
|
27
|
+
aria-label="Brush radius"
|
28
|
+
bind:value={brush_radius}
|
29
|
+
type="range"
|
30
|
+
min={0.5 * (img_width / width)}
|
31
|
+
max={75 * (img_width / width)}
|
32
|
+
/>
|
33
|
+
{/if}
|
34
|
+
</span>
|
35
|
+
|
36
|
+
{#if mode !== "mask"}
|
37
|
+
<span class="col">
|
38
|
+
<IconButton
|
39
|
+
Icon={Color}
|
40
|
+
label="Select brush color"
|
41
|
+
on:click={() => (show_col = !show_col)}
|
42
|
+
/>
|
43
|
+
{#if show_col}
|
44
|
+
<input aria-label="Brush color" bind:value={brush_color} type="color" />
|
45
|
+
{/if}
|
46
|
+
</span>
|
47
|
+
{/if}
|
48
|
+
</div>
|
49
|
+
|
50
|
+
<style>
|
51
|
+
.wrap {
|
52
|
+
display: flex;
|
53
|
+
position: absolute;
|
54
|
+
top: var(--size-10);
|
55
|
+
right: var(--size-2);
|
56
|
+
flex-direction: column;
|
57
|
+
justify-content: flex-end;
|
58
|
+
gap: var(--spacing-sm);
|
59
|
+
z-index: var(--layer-5);
|
60
|
+
}
|
61
|
+
.brush {
|
62
|
+
top: 0;
|
63
|
+
right: 0;
|
64
|
+
}
|
65
|
+
|
66
|
+
.brush input {
|
67
|
+
position: absolute;
|
68
|
+
top: 3px;
|
69
|
+
right: calc(100% + 5px);
|
70
|
+
}
|
71
|
+
|
72
|
+
.col input {
|
73
|
+
position: absolute;
|
74
|
+
right: calc(100% + 5px);
|
75
|
+
bottom: -4px;
|
76
|
+
}
|
77
|
+
</style>
|