@gradio/imageeditor 0.12.2 → 0.12.3
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 +13 -0
- package/ImageEditor.stories.svelte +2 -0
- package/Index.svelte +19 -4
- package/dist/Index.svelte +13 -3
- package/dist/Index.svelte.d.ts +8 -4
- package/dist/shared/ImageEditor.svelte +6 -0
- package/dist/shared/ImageEditor.svelte.d.ts +1 -0
- package/dist/shared/InteractiveImageEditor.svelte +9 -2
- package/dist/shared/InteractiveImageEditor.svelte.d.ts +4 -2
- package/dist/shared/tools/Sources.svelte +9 -4
- package/dist/shared/tools/Sources.svelte.d.ts +2 -1
- package/dist/shared/tools/sources.d.ts +13 -7
- package/dist/shared/tools/sources.js +60 -28
- package/package.json +5 -5
- package/shared/ImageEditor.svelte +6 -0
- package/shared/InteractiveImageEditor.svelte +11 -3
- package/shared/tools/Sources.svelte +9 -4
- package/shared/tools/sources.ts +85 -41
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @gradio/imageeditor
|
|
2
2
|
|
|
3
|
+
## 0.12.3
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
|
|
7
|
+
- [#10357](https://github.com/gradio-app/gradio/pull/10357) [`43e7cce`](https://github.com/gradio-app/gradio/commit/43e7cce2bd8ddd274fcba890bfeaa7ead7f32434) - Fix ImageEditor Size Issues. Thanks @freddyaboulton!
|
|
8
|
+
|
|
9
|
+
### Dependency updates
|
|
10
|
+
|
|
11
|
+
- @gradio/upload@0.14.6
|
|
12
|
+
- @gradio/wasm@0.17.0
|
|
13
|
+
- @gradio/statustracker@0.10.2
|
|
14
|
+
- @gradio/image@0.20.2
|
|
15
|
+
|
|
3
16
|
## 0.12.2
|
|
4
17
|
|
|
5
18
|
### Fixes
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
interactive: "true",
|
|
43
43
|
label: "Image Editor",
|
|
44
44
|
show_label: true,
|
|
45
|
+
canvas_size: [800, 600],
|
|
45
46
|
brush: {
|
|
46
47
|
default_size: "auto",
|
|
47
48
|
colors: ["#ff0000", "#00ff00", "#0000ff"],
|
|
@@ -216,6 +217,7 @@
|
|
|
216
217
|
type: "pil",
|
|
217
218
|
placeholder: "Upload an image of a cat",
|
|
218
219
|
sources: ["upload", "webcam"],
|
|
220
|
+
canvas_size: [800, 800],
|
|
219
221
|
interactive: "true",
|
|
220
222
|
brush: {
|
|
221
223
|
default_size: "auto",
|
package/Index.svelte
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
export let root: string;
|
|
30
30
|
export let value_is_output = false;
|
|
31
31
|
|
|
32
|
-
export let height: number | undefined
|
|
32
|
+
export let height: number | undefined;
|
|
33
33
|
export let width: number | undefined;
|
|
34
34
|
|
|
35
35
|
export let _selectable = false;
|
|
@@ -55,7 +55,8 @@
|
|
|
55
55
|
export let server: {
|
|
56
56
|
accept_blobs: (a: any) => void;
|
|
57
57
|
};
|
|
58
|
-
export let canvas_size: [number, number]
|
|
58
|
+
export let canvas_size: [number, number];
|
|
59
|
+
export let fixed_canvas = false;
|
|
59
60
|
export let show_fullscreen_button = true;
|
|
60
61
|
export let full_history: any = null;
|
|
61
62
|
|
|
@@ -125,6 +126,18 @@
|
|
|
125
126
|
}
|
|
126
127
|
}
|
|
127
128
|
|
|
129
|
+
let dynamic_height: number | undefined = undefined;
|
|
130
|
+
|
|
131
|
+
// In case no height given, pick a height large enough for the entire canvas
|
|
132
|
+
// in pixi.ts, the max-height of the canvas is canvas height / pixel ratio
|
|
133
|
+
|
|
134
|
+
let safe_height_initial = Math.max(
|
|
135
|
+
canvas_size[1] / (is_browser ? window.devicePixelRatio : 1),
|
|
136
|
+
250
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
$: safe_height = Math.max((dynamic_height ?? safe_height_initial) + 100, 250);
|
|
140
|
+
|
|
128
141
|
$: has_value = value?.background || value?.layers?.length || value?.composite;
|
|
129
142
|
</script>
|
|
130
143
|
|
|
@@ -136,7 +149,7 @@
|
|
|
136
149
|
padding={false}
|
|
137
150
|
{elem_id}
|
|
138
151
|
{elem_classes}
|
|
139
|
-
|
|
152
|
+
{height}
|
|
140
153
|
{width}
|
|
141
154
|
allow_overflow={false}
|
|
142
155
|
{container}
|
|
@@ -171,7 +184,7 @@
|
|
|
171
184
|
padding={false}
|
|
172
185
|
{elem_id}
|
|
173
186
|
{elem_classes}
|
|
174
|
-
height={height ||
|
|
187
|
+
height={height || safe_height}
|
|
175
188
|
{width}
|
|
176
189
|
allow_overflow={false}
|
|
177
190
|
{container}
|
|
@@ -194,11 +207,13 @@
|
|
|
194
207
|
{crop_size}
|
|
195
208
|
{value}
|
|
196
209
|
bind:this={editor_instance}
|
|
210
|
+
bind:dynamic_height
|
|
197
211
|
{root}
|
|
198
212
|
{sources}
|
|
199
213
|
{label}
|
|
200
214
|
{show_label}
|
|
201
215
|
{height}
|
|
216
|
+
{fixed_canvas}
|
|
202
217
|
on:save={(e) => handle_save()}
|
|
203
218
|
on:edit={() => gradio.dispatch("edit")}
|
|
204
219
|
on:clear={() => gradio.dispatch("clear")}
|
package/dist/Index.svelte
CHANGED
|
@@ -18,7 +18,7 @@ export let show_label;
|
|
|
18
18
|
export let show_download_button;
|
|
19
19
|
export let root;
|
|
20
20
|
export let value_is_output = false;
|
|
21
|
-
export let height
|
|
21
|
+
export let height;
|
|
22
22
|
export let width;
|
|
23
23
|
export let _selectable = false;
|
|
24
24
|
export let container = true;
|
|
@@ -41,6 +41,7 @@ export let layers = true;
|
|
|
41
41
|
export let attached_events = [];
|
|
42
42
|
export let server;
|
|
43
43
|
export let canvas_size;
|
|
44
|
+
export let fixed_canvas = false;
|
|
44
45
|
export let show_fullscreen_button = true;
|
|
45
46
|
export let full_history = null;
|
|
46
47
|
export let gradio;
|
|
@@ -81,6 +82,13 @@ function handle_history_change() {
|
|
|
81
82
|
tick().then((_) => value_is_output = false);
|
|
82
83
|
}
|
|
83
84
|
}
|
|
85
|
+
let dynamic_height = void 0;
|
|
86
|
+
let safe_height_initial = Math.max(
|
|
87
|
+
canvas_size[1] / (is_browser ? window.devicePixelRatio : 1),
|
|
88
|
+
250
|
|
89
|
+
);
|
|
90
|
+
$:
|
|
91
|
+
safe_height = Math.max((dynamic_height ?? safe_height_initial) + 100, 250);
|
|
84
92
|
$:
|
|
85
93
|
has_value = value?.background || value?.layers?.length || value?.composite;
|
|
86
94
|
</script>
|
|
@@ -93,7 +101,7 @@ $:
|
|
|
93
101
|
padding={false}
|
|
94
102
|
{elem_id}
|
|
95
103
|
{elem_classes}
|
|
96
|
-
|
|
104
|
+
{height}
|
|
97
105
|
{width}
|
|
98
106
|
allow_overflow={false}
|
|
99
107
|
{container}
|
|
@@ -128,7 +136,7 @@ $:
|
|
|
128
136
|
padding={false}
|
|
129
137
|
{elem_id}
|
|
130
138
|
{elem_classes}
|
|
131
|
-
height={height ||
|
|
139
|
+
height={height || safe_height}
|
|
132
140
|
{width}
|
|
133
141
|
allow_overflow={false}
|
|
134
142
|
{container}
|
|
@@ -151,11 +159,13 @@ $:
|
|
|
151
159
|
{crop_size}
|
|
152
160
|
{value}
|
|
153
161
|
bind:this={editor_instance}
|
|
162
|
+
bind:dynamic_height
|
|
154
163
|
{root}
|
|
155
164
|
{sources}
|
|
156
165
|
{label}
|
|
157
166
|
{show_label}
|
|
158
167
|
{height}
|
|
168
|
+
{fixed_canvas}
|
|
159
169
|
on:save={(e) => handle_save()}
|
|
160
170
|
on:edit={() => gradio.dispatch("edit")}
|
|
161
171
|
on:clear={() => gradio.dispatch("clear")}
|
package/dist/Index.svelte.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ declare const __propDef: {
|
|
|
14
14
|
show_download_button: boolean;
|
|
15
15
|
root: string;
|
|
16
16
|
value_is_output?: boolean | undefined;
|
|
17
|
-
height
|
|
17
|
+
height: number | undefined;
|
|
18
18
|
width: number | undefined;
|
|
19
19
|
_selectable?: boolean | undefined;
|
|
20
20
|
container?: boolean | undefined;
|
|
@@ -34,7 +34,8 @@ declare const __propDef: {
|
|
|
34
34
|
server: {
|
|
35
35
|
accept_blobs: (a: any) => void;
|
|
36
36
|
};
|
|
37
|
-
canvas_size: [number, number]
|
|
37
|
+
canvas_size: [number, number];
|
|
38
|
+
fixed_canvas?: boolean | undefined;
|
|
38
39
|
show_fullscreen_button?: boolean | undefined;
|
|
39
40
|
full_history?: any;
|
|
40
41
|
gradio: Gradio<{
|
|
@@ -153,9 +154,12 @@ export default class Index extends SvelteComponent<IndexProps, IndexEvents, Inde
|
|
|
153
154
|
set server(_: {
|
|
154
155
|
accept_blobs: (a: any) => void;
|
|
155
156
|
});
|
|
156
|
-
get canvas_size(): [number, number]
|
|
157
|
+
get canvas_size(): [number, number];
|
|
157
158
|
/**accessor*/
|
|
158
|
-
set canvas_size(_: [number, number]
|
|
159
|
+
set canvas_size(_: [number, number]);
|
|
160
|
+
get fixed_canvas(): boolean | undefined;
|
|
161
|
+
/**accessor*/
|
|
162
|
+
set fixed_canvas(_: boolean | undefined);
|
|
159
163
|
get show_fullscreen_button(): boolean | undefined;
|
|
160
164
|
/**accessor*/
|
|
161
165
|
set show_fullscreen_button(_: boolean | undefined);
|
|
@@ -42,8 +42,11 @@ let editor_box = writable({
|
|
|
42
42
|
child_right: 0,
|
|
43
43
|
child_bottom: 0
|
|
44
44
|
});
|
|
45
|
+
export let canvas_height = void 0;
|
|
45
46
|
$:
|
|
46
47
|
height = $editor_box.child_height;
|
|
48
|
+
$:
|
|
49
|
+
canvas_height = $crop[3] * $editor_box.child_height + 1;
|
|
47
50
|
const crop = writable([0, 0, 1, 1]);
|
|
48
51
|
const position_spring = spring(
|
|
49
52
|
{ x: 0, y: 0 },
|
|
@@ -312,6 +315,9 @@ export function set_tool(tool) {
|
|
|
312
315
|
.container {
|
|
313
316
|
position: relative;
|
|
314
317
|
margin: var(--spacing-md);
|
|
318
|
+
/* in case the canvas_size is really small */
|
|
319
|
+
/* set min-height so that upload text does not cover the toolbar */
|
|
320
|
+
min-height: 100px;
|
|
315
321
|
}
|
|
316
322
|
|
|
317
323
|
.no-border {
|
|
@@ -54,6 +54,7 @@ declare const __propDef: {
|
|
|
54
54
|
parent_height: number;
|
|
55
55
|
full_history?: (CommandNode | null) | undefined;
|
|
56
56
|
height?: number | undefined;
|
|
57
|
+
canvas_height?: number | undefined;
|
|
57
58
|
get_blobs?: (() => Promise<ImageBlobs>) | undefined;
|
|
58
59
|
handle_remove?: (() => void) | undefined;
|
|
59
60
|
set_tool?: ((tool: tool) => void) | undefined;
|
|
@@ -31,12 +31,14 @@ export let layers;
|
|
|
31
31
|
export let accept_blobs;
|
|
32
32
|
export let status = "complete";
|
|
33
33
|
export let canvas_size;
|
|
34
|
+
export let fixed_canvas = false;
|
|
34
35
|
export let realtime;
|
|
35
36
|
export let upload;
|
|
36
37
|
export let stream_handler;
|
|
37
38
|
export let dragging;
|
|
38
39
|
export let placeholder = void 0;
|
|
39
|
-
export let
|
|
40
|
+
export let dynamic_height = void 0;
|
|
41
|
+
export let height;
|
|
40
42
|
export let full_history = null;
|
|
41
43
|
const dispatch = createEventDispatcher();
|
|
42
44
|
let editor;
|
|
@@ -149,6 +151,9 @@ async function handle_change(e) {
|
|
|
149
151
|
}
|
|
150
152
|
let active_mode = null;
|
|
151
153
|
let editor_height = height - 100;
|
|
154
|
+
let _dynamic_height;
|
|
155
|
+
$:
|
|
156
|
+
dynamic_height = _dynamic_height;
|
|
152
157
|
$:
|
|
153
158
|
[heading, paragraph] = placeholder ? inject(placeholder) : [false, false];
|
|
154
159
|
</script>
|
|
@@ -164,6 +169,7 @@ $:
|
|
|
164
169
|
crop_size={Array.isArray(crop_size) ? crop_size : undefined}
|
|
165
170
|
bind:this={editor}
|
|
166
171
|
bind:height={editor_height}
|
|
172
|
+
bind:canvas_height={_dynamic_height}
|
|
167
173
|
parent_height={height}
|
|
168
174
|
{changeable}
|
|
169
175
|
on:save
|
|
@@ -185,10 +191,11 @@ $:
|
|
|
185
191
|
{sources}
|
|
186
192
|
{upload}
|
|
187
193
|
{stream_handler}
|
|
194
|
+
{canvas_size}
|
|
188
195
|
bind:bg
|
|
189
196
|
bind:active_mode
|
|
190
197
|
background_file={value?.background || value?.composite || null}
|
|
191
|
-
|
|
198
|
+
{fixed_canvas}
|
|
192
199
|
></Sources>
|
|
193
200
|
|
|
194
201
|
{#if transforms.includes("crop")}
|
|
@@ -30,13 +30,15 @@ declare const __propDef: {
|
|
|
30
30
|
layers: boolean;
|
|
31
31
|
accept_blobs: (a: any) => void;
|
|
32
32
|
status?: ("pending" | "complete" | "error" | "generating" | "streaming") | undefined;
|
|
33
|
-
canvas_size: [number, number]
|
|
33
|
+
canvas_size: [number, number];
|
|
34
|
+
fixed_canvas?: boolean | undefined;
|
|
34
35
|
realtime: boolean;
|
|
35
36
|
upload: Client["upload"];
|
|
36
37
|
stream_handler: Client["stream"];
|
|
37
38
|
dragging: boolean;
|
|
38
39
|
placeholder?: string | undefined;
|
|
39
|
-
|
|
40
|
+
dynamic_height?: number | undefined;
|
|
41
|
+
height: any;
|
|
40
42
|
full_history?: (CommandNode | null) | undefined;
|
|
41
43
|
get_data?: (() => Promise<ImageBlobs>) | undefined;
|
|
42
44
|
image_id?: (null | string) | undefined;
|
|
@@ -24,7 +24,8 @@ export let i18n;
|
|
|
24
24
|
export let upload;
|
|
25
25
|
export let stream_handler;
|
|
26
26
|
export let dragging;
|
|
27
|
-
export let
|
|
27
|
+
export let canvas_size;
|
|
28
|
+
export let fixed_canvas = false;
|
|
28
29
|
const { active_tool } = getContext(TOOL_KEY);
|
|
29
30
|
const { pixi, dimensions, register_context, reset, editor_box } = getContext(EDITOR_KEY);
|
|
30
31
|
export let active_mode = null;
|
|
@@ -90,7 +91,8 @@ async function set_background() {
|
|
|
90
91
|
$pixi.renderer,
|
|
91
92
|
background,
|
|
92
93
|
$pixi.resize,
|
|
93
|
-
|
|
94
|
+
canvas_size,
|
|
95
|
+
fixed_canvas
|
|
94
96
|
);
|
|
95
97
|
$dimensions = await add_image.start();
|
|
96
98
|
if (should_reset) {
|
|
@@ -161,8 +163,11 @@ register_context("bg", {
|
|
|
161
163
|
class:click-disabled={!!bg ||
|
|
162
164
|
active_mode === "webcam" ||
|
|
163
165
|
$active_tool !== "bg"}
|
|
164
|
-
style:height="{
|
|
165
|
-
|
|
166
|
+
style:height="{Math.max(
|
|
167
|
+
$editor_box.child_height +
|
|
168
|
+
($editor_box.child_top - $editor_box.parent_top),
|
|
169
|
+
100
|
|
170
|
+
)}px"
|
|
166
171
|
>
|
|
167
172
|
<Upload
|
|
168
173
|
hidden={bg || active_mode === "webcam" || $active_tool !== "bg"}
|
|
@@ -12,7 +12,8 @@ declare const __propDef: {
|
|
|
12
12
|
upload: Client["upload"];
|
|
13
13
|
stream_handler: Client["stream"];
|
|
14
14
|
dragging: boolean;
|
|
15
|
-
|
|
15
|
+
canvas_size: [number, number];
|
|
16
|
+
fixed_canvas?: boolean | undefined;
|
|
16
17
|
active_mode?: ("webcam" | "color" | null) | undefined;
|
|
17
18
|
bg?: boolean | undefined;
|
|
18
19
|
};
|
|
@@ -8,14 +8,20 @@ interface BgImageCommand extends Command {
|
|
|
8
8
|
start: () => Promise<[number, number]>;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
* @param
|
|
13
|
-
* @param
|
|
14
|
-
* @param
|
|
15
|
-
* @param
|
|
16
|
-
* @returns
|
|
11
|
+
* Calculates new dimensions and position for an image to fit within a canvas while maintaining aspect ratio
|
|
12
|
+
* @param image_width Original width of the image
|
|
13
|
+
* @param image_height Original height of the image
|
|
14
|
+
* @param canvas_width Width of the canvas
|
|
15
|
+
* @param canvas_height Height of the canvas
|
|
16
|
+
* @returns Object containing new dimensions and position
|
|
17
17
|
*/
|
|
18
|
-
export declare function
|
|
18
|
+
export declare function fit_image_to_canvas(image_width: number, image_height: number, canvas_width: number, canvas_height: number): {
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
};
|
|
24
|
+
export declare function add_bg_image(container: Container, renderer: IRenderer, background: Blob | File, resize: (width: number, height: number) => void, canvas_size: [number, number], fixed_canvas: boolean): BgImageCommand;
|
|
19
25
|
/**
|
|
20
26
|
* Command that sets a background
|
|
21
27
|
*/
|
|
@@ -2,46 +2,78 @@ import { Sprite, Texture, DisplayObject, Color, RenderTexture } from "pixi.js";
|
|
|
2
2
|
import {} from "../utils/commands";
|
|
3
3
|
import { make_graphics } from "../utils/pixi";
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
* @param
|
|
7
|
-
* @param
|
|
8
|
-
* @param
|
|
9
|
-
* @param
|
|
10
|
-
* @returns
|
|
5
|
+
* Calculates new dimensions and position for an image to fit within a canvas while maintaining aspect ratio
|
|
6
|
+
* @param image_width Original width of the image
|
|
7
|
+
* @param image_height Original height of the image
|
|
8
|
+
* @param canvas_width Width of the canvas
|
|
9
|
+
* @param canvas_height Height of the canvas
|
|
10
|
+
* @returns Object containing new dimensions and position
|
|
11
11
|
*/
|
|
12
|
-
export function
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
12
|
+
export function fit_image_to_canvas(image_width, image_height, canvas_width, canvas_height) {
|
|
13
|
+
const image_aspect_ratio = image_width / image_height;
|
|
14
|
+
const canvas_aspect_ratio = canvas_width / canvas_height;
|
|
15
|
+
let new_width;
|
|
16
|
+
let new_height;
|
|
17
|
+
if (image_width <= canvas_width && image_height <= canvas_height) {
|
|
18
|
+
new_width = image_width;
|
|
19
|
+
new_height = image_height;
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
if (image_aspect_ratio > canvas_aspect_ratio) {
|
|
23
|
+
// Width is the limiting factor
|
|
24
|
+
new_width = canvas_width;
|
|
25
|
+
new_height = canvas_width / image_aspect_ratio;
|
|
23
26
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
else {
|
|
28
|
+
// Height is the limiting factor
|
|
29
|
+
new_height = canvas_height;
|
|
30
|
+
new_width = canvas_height * image_aspect_ratio;
|
|
28
31
|
}
|
|
29
|
-
return [Math.round(new_width), Math.round(new_height)];
|
|
30
32
|
}
|
|
33
|
+
// Calculate position to center the image
|
|
34
|
+
const x = Math.round((canvas_width - new_width) / 2);
|
|
35
|
+
const y = Math.round((canvas_height - new_height) / 2);
|
|
36
|
+
return {
|
|
37
|
+
width: Math.round(new_width),
|
|
38
|
+
height: Math.round(new_height),
|
|
39
|
+
x,
|
|
40
|
+
y
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export function add_bg_image(container, renderer, background, resize, canvas_size, fixed_canvas) {
|
|
44
|
+
let sprite;
|
|
31
45
|
return {
|
|
32
46
|
async start() {
|
|
33
47
|
container.removeChildren();
|
|
34
48
|
const img = await createImageBitmap(background);
|
|
35
49
|
const bitmap_texture = Texture.from(img);
|
|
36
50
|
sprite = new Sprite(bitmap_texture);
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
51
|
+
if (!fixed_canvas) {
|
|
52
|
+
canvas_size = [sprite.width, sprite.height];
|
|
53
|
+
}
|
|
54
|
+
if (fixed_canvas) {
|
|
55
|
+
const [canvas_width, canvas_height] = canvas_size;
|
|
56
|
+
const { width, height, x, y } = fit_image_to_canvas(sprite.width, sprite.height, canvas_width, canvas_height);
|
|
57
|
+
sprite.width = width;
|
|
58
|
+
sprite.height = height;
|
|
59
|
+
sprite.x = x;
|
|
60
|
+
sprite.y = y;
|
|
61
|
+
return canvas_size;
|
|
62
|
+
}
|
|
63
|
+
// Use existing max_height based scaling if no canvas_size
|
|
64
|
+
const x = fit_image_to_canvas(sprite.width, sprite.height, canvas_size[0], canvas_size[1]);
|
|
65
|
+
sprite.width = x.width;
|
|
66
|
+
sprite.height = x.height;
|
|
67
|
+
sprite.x = 0;
|
|
68
|
+
sprite.y = 0;
|
|
69
|
+
return [x.width, x.height];
|
|
41
70
|
},
|
|
42
71
|
async execute() {
|
|
43
|
-
|
|
44
|
-
|
|
72
|
+
resize(
|
|
73
|
+
// @ts-ignore
|
|
74
|
+
fixed_canvas ? canvas_size[0] : sprite.width,
|
|
75
|
+
// @ts-ignore
|
|
76
|
+
fixed_canvas ? canvas_size[1] : sprite.height);
|
|
45
77
|
sprite.zIndex = 0;
|
|
46
78
|
container.addChild(sprite);
|
|
47
79
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradio/imageeditor",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.3",
|
|
4
4
|
"description": "Gradio UI packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "",
|
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
"tinycolor2": "^1.6.0",
|
|
13
13
|
"@gradio/atoms": "^0.13.1",
|
|
14
14
|
"@gradio/client": "^1.10.0",
|
|
15
|
-
"@gradio/image": "^0.20.
|
|
15
|
+
"@gradio/image": "^0.20.2",
|
|
16
16
|
"@gradio/icons": "^0.10.0",
|
|
17
|
-
"@gradio/
|
|
18
|
-
"@gradio/
|
|
17
|
+
"@gradio/upload": "^0.14.6",
|
|
18
|
+
"@gradio/statustracker": "^0.10.2",
|
|
19
19
|
"@gradio/utils": "^0.10.0",
|
|
20
|
-
"@gradio/wasm": "^0.
|
|
20
|
+
"@gradio/wasm": "^0.17.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@gradio/preview": "^0.13.0"
|
|
@@ -100,7 +100,10 @@
|
|
|
100
100
|
child_bottom: 0
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
+
export let canvas_height = undefined;
|
|
104
|
+
|
|
103
105
|
$: height = $editor_box.child_height;
|
|
106
|
+
$: canvas_height = $crop[3] * $editor_box.child_height + 1;
|
|
104
107
|
|
|
105
108
|
const crop = writable<[number, number, number, number]>([0, 0, 1, 1]);
|
|
106
109
|
const position_spring = spring(
|
|
@@ -408,6 +411,9 @@
|
|
|
408
411
|
.container {
|
|
409
412
|
position: relative;
|
|
410
413
|
margin: var(--spacing-md);
|
|
414
|
+
/* in case the canvas_size is really small */
|
|
415
|
+
/* set min-height so that upload text does not cover the toolbar */
|
|
416
|
+
min-height: 100px;
|
|
411
417
|
}
|
|
412
418
|
|
|
413
419
|
.no-border {
|
|
@@ -50,13 +50,15 @@
|
|
|
50
50
|
| "error"
|
|
51
51
|
| "generating"
|
|
52
52
|
| "streaming" = "complete";
|
|
53
|
-
export let canvas_size: [number, number]
|
|
53
|
+
export let canvas_size: [number, number];
|
|
54
|
+
export let fixed_canvas = false;
|
|
54
55
|
export let realtime: boolean;
|
|
55
56
|
export let upload: Client["upload"];
|
|
56
57
|
export let stream_handler: Client["stream"];
|
|
57
58
|
export let dragging: boolean;
|
|
58
59
|
export let placeholder: string | undefined = undefined;
|
|
59
|
-
export let
|
|
60
|
+
export let dynamic_height: number | undefined = undefined;
|
|
61
|
+
export let height;
|
|
60
62
|
export let full_history: CommandNode | null = null;
|
|
61
63
|
|
|
62
64
|
const dispatch = createEventDispatcher<{
|
|
@@ -207,6 +209,10 @@
|
|
|
207
209
|
let active_mode: "webcam" | "color" | null = null;
|
|
208
210
|
let editor_height = height - 100;
|
|
209
211
|
|
|
212
|
+
let _dynamic_height: number;
|
|
213
|
+
|
|
214
|
+
$: dynamic_height = _dynamic_height;
|
|
215
|
+
|
|
210
216
|
$: [heading, paragraph] = placeholder ? inject(placeholder) : [false, false];
|
|
211
217
|
</script>
|
|
212
218
|
|
|
@@ -221,6 +227,7 @@
|
|
|
221
227
|
crop_size={Array.isArray(crop_size) ? crop_size : undefined}
|
|
222
228
|
bind:this={editor}
|
|
223
229
|
bind:height={editor_height}
|
|
230
|
+
bind:canvas_height={_dynamic_height}
|
|
224
231
|
parent_height={height}
|
|
225
232
|
{changeable}
|
|
226
233
|
on:save
|
|
@@ -242,10 +249,11 @@
|
|
|
242
249
|
{sources}
|
|
243
250
|
{upload}
|
|
244
251
|
{stream_handler}
|
|
252
|
+
{canvas_size}
|
|
245
253
|
bind:bg
|
|
246
254
|
bind:active_mode
|
|
247
255
|
background_file={value?.background || value?.composite || null}
|
|
248
|
-
|
|
256
|
+
{fixed_canvas}
|
|
249
257
|
></Sources>
|
|
250
258
|
|
|
251
259
|
{#if transforms.includes("crop")}
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
export let upload: Client["upload"];
|
|
29
29
|
export let stream_handler: Client["stream"];
|
|
30
30
|
export let dragging: boolean;
|
|
31
|
-
export let
|
|
31
|
+
export let canvas_size: [number, number];
|
|
32
|
+
export let fixed_canvas = false;
|
|
32
33
|
|
|
33
34
|
const { active_tool } = getContext<ToolContext>(TOOL_KEY);
|
|
34
35
|
const { pixi, dimensions, register_context, reset, editor_box } =
|
|
@@ -110,7 +111,8 @@
|
|
|
110
111
|
$pixi.renderer,
|
|
111
112
|
background,
|
|
112
113
|
$pixi.resize,
|
|
113
|
-
|
|
114
|
+
canvas_size,
|
|
115
|
+
fixed_canvas
|
|
114
116
|
);
|
|
115
117
|
$dimensions = await add_image.start();
|
|
116
118
|
|
|
@@ -187,8 +189,11 @@
|
|
|
187
189
|
class:click-disabled={!!bg ||
|
|
188
190
|
active_mode === "webcam" ||
|
|
189
191
|
$active_tool !== "bg"}
|
|
190
|
-
style:height="{
|
|
191
|
-
|
|
192
|
+
style:height="{Math.max(
|
|
193
|
+
$editor_box.child_height +
|
|
194
|
+
($editor_box.child_top - $editor_box.parent_top),
|
|
195
|
+
100
|
|
196
|
+
)}px"
|
|
192
197
|
>
|
|
193
198
|
<Upload
|
|
194
199
|
hidden={bg || active_mode === "webcam" || $active_tool !== "bg"}
|
package/shared/tools/sources.ts
CHANGED
|
@@ -21,69 +21,113 @@ interface BgImageCommand extends Command {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
* @param
|
|
26
|
-
* @param
|
|
27
|
-
* @param
|
|
28
|
-
* @param
|
|
29
|
-
* @returns
|
|
24
|
+
* Calculates new dimensions and position for an image to fit within a canvas while maintaining aspect ratio
|
|
25
|
+
* @param image_width Original width of the image
|
|
26
|
+
* @param image_height Original height of the image
|
|
27
|
+
* @param canvas_width Width of the canvas
|
|
28
|
+
* @param canvas_height Height of the canvas
|
|
29
|
+
* @returns Object containing new dimensions and position
|
|
30
30
|
*/
|
|
31
|
+
export function fit_image_to_canvas(
|
|
32
|
+
image_width: number,
|
|
33
|
+
image_height: number,
|
|
34
|
+
canvas_width: number,
|
|
35
|
+
canvas_height: number
|
|
36
|
+
): {
|
|
37
|
+
width: number;
|
|
38
|
+
height: number;
|
|
39
|
+
x: number;
|
|
40
|
+
y: number;
|
|
41
|
+
} {
|
|
42
|
+
const image_aspect_ratio = image_width / image_height;
|
|
43
|
+
const canvas_aspect_ratio = canvas_width / canvas_height;
|
|
44
|
+
|
|
45
|
+
let new_width: number;
|
|
46
|
+
let new_height: number;
|
|
47
|
+
|
|
48
|
+
if (image_width <= canvas_width && image_height <= canvas_height) {
|
|
49
|
+
new_width = image_width;
|
|
50
|
+
new_height = image_height;
|
|
51
|
+
} else {
|
|
52
|
+
if (image_aspect_ratio > canvas_aspect_ratio) {
|
|
53
|
+
// Width is the limiting factor
|
|
54
|
+
new_width = canvas_width;
|
|
55
|
+
new_height = canvas_width / image_aspect_ratio;
|
|
56
|
+
} else {
|
|
57
|
+
// Height is the limiting factor
|
|
58
|
+
new_height = canvas_height;
|
|
59
|
+
new_width = canvas_height * image_aspect_ratio;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Calculate position to center the image
|
|
64
|
+
const x = Math.round((canvas_width - new_width) / 2);
|
|
65
|
+
const y = Math.round((canvas_height - new_height) / 2);
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
width: Math.round(new_width),
|
|
69
|
+
height: Math.round(new_height),
|
|
70
|
+
x,
|
|
71
|
+
y
|
|
72
|
+
};
|
|
73
|
+
}
|
|
31
74
|
|
|
32
75
|
export function add_bg_image(
|
|
33
76
|
container: Container,
|
|
34
77
|
renderer: IRenderer,
|
|
35
78
|
background: Blob | File,
|
|
36
79
|
resize: (width: number, height: number) => void,
|
|
37
|
-
|
|
80
|
+
canvas_size: [number, number],
|
|
81
|
+
fixed_canvas: boolean
|
|
38
82
|
): BgImageCommand {
|
|
39
83
|
let sprite: Sprite & DisplayObject;
|
|
40
84
|
|
|
41
|
-
function calculate_dimensions(
|
|
42
|
-
width: number,
|
|
43
|
-
height: number,
|
|
44
|
-
max_height: number
|
|
45
|
-
): [number, number] {
|
|
46
|
-
const MAX_HEIGHT = max_height * 1.5;
|
|
47
|
-
const MAX_WIDTH = MAX_HEIGHT * 2;
|
|
48
|
-
|
|
49
|
-
let new_width = width;
|
|
50
|
-
let new_height = height;
|
|
51
|
-
|
|
52
|
-
if (height > MAX_HEIGHT) {
|
|
53
|
-
const ratio = MAX_HEIGHT / height;
|
|
54
|
-
new_width = width * ratio;
|
|
55
|
-
new_height = MAX_HEIGHT;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (new_width > MAX_WIDTH) {
|
|
59
|
-
const ratio = MAX_WIDTH / new_width;
|
|
60
|
-
new_width = MAX_WIDTH;
|
|
61
|
-
new_height = new_height * ratio;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return [Math.round(new_width), Math.round(new_height)];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
85
|
return {
|
|
68
86
|
async start() {
|
|
69
87
|
container.removeChildren();
|
|
70
88
|
const img = await createImageBitmap(background);
|
|
71
89
|
const bitmap_texture = Texture.from(img);
|
|
72
90
|
sprite = new Sprite(bitmap_texture) as Sprite & DisplayObject;
|
|
91
|
+
if (!fixed_canvas) {
|
|
92
|
+
canvas_size = [sprite.width, sprite.height];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (fixed_canvas) {
|
|
96
|
+
const [canvas_width, canvas_height] = canvas_size;
|
|
97
|
+
const { width, height, x, y } = fit_image_to_canvas(
|
|
98
|
+
sprite.width,
|
|
99
|
+
sprite.height,
|
|
100
|
+
canvas_width,
|
|
101
|
+
canvas_height
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
sprite.width = width;
|
|
105
|
+
sprite.height = height;
|
|
106
|
+
sprite.x = x;
|
|
107
|
+
sprite.y = y;
|
|
73
108
|
|
|
74
|
-
|
|
109
|
+
return canvas_size;
|
|
110
|
+
}
|
|
111
|
+
// Use existing max_height based scaling if no canvas_size
|
|
112
|
+
const x = fit_image_to_canvas(
|
|
75
113
|
sprite.width,
|
|
76
114
|
sprite.height,
|
|
77
|
-
|
|
115
|
+
canvas_size[0],
|
|
116
|
+
canvas_size[1]
|
|
78
117
|
);
|
|
79
|
-
sprite.width =
|
|
80
|
-
sprite.height =
|
|
81
|
-
|
|
82
|
-
|
|
118
|
+
sprite.width = x.width;
|
|
119
|
+
sprite.height = x.height;
|
|
120
|
+
sprite.x = 0;
|
|
121
|
+
sprite.y = 0;
|
|
122
|
+
return [x.width, x.height];
|
|
83
123
|
},
|
|
84
124
|
async execute() {
|
|
85
|
-
|
|
86
|
-
|
|
125
|
+
resize(
|
|
126
|
+
// @ts-ignore
|
|
127
|
+
fixed_canvas ? canvas_size[0] : sprite.width,
|
|
128
|
+
// @ts-ignore
|
|
129
|
+
fixed_canvas ? canvas_size[1] : sprite.height
|
|
130
|
+
);
|
|
87
131
|
|
|
88
132
|
sprite.zIndex = 0;
|
|
89
133
|
container.addChild(sprite);
|