feat(gui): add outpainting dimension controls to inpaint tab
This commit is contained in:
parent
d9bbb9bb5a
commit
9e2921d3de
|
@ -1,4 +1,4 @@
|
|||
import { doesExist, NotImplementedError } from '@apextoaster/js-utils';
|
||||
import { doesExist } from '@apextoaster/js-utils';
|
||||
|
||||
import { ConfigParams } from '../config.js';
|
||||
|
||||
|
@ -43,13 +43,11 @@ export type Txt2ImgResponse = Required<Txt2ImgParams>;
|
|||
export interface InpaintParams extends BaseImgParams {
|
||||
mask: Blob;
|
||||
source: Blob;
|
||||
}
|
||||
|
||||
export interface OutpaintParams extends Img2ImgParams {
|
||||
up: boolean;
|
||||
down: boolean;
|
||||
left: boolean;
|
||||
right: boolean;
|
||||
left?: number;
|
||||
right?: number;
|
||||
top?: number;
|
||||
bottom?: number;
|
||||
}
|
||||
|
||||
export interface ApiResponse {
|
||||
|
@ -72,9 +70,7 @@ export interface ApiClient {
|
|||
|
||||
img2img(params: Img2ImgParams): Promise<ApiResponse>;
|
||||
txt2img(params: Txt2ImgParams): Promise<ApiResponse>;
|
||||
|
||||
inpaint(params: InpaintParams): Promise<ApiResponse>;
|
||||
outpaint(params: OutpaintParams): Promise<ApiResponse>;
|
||||
|
||||
ready(params: ApiResponse): Promise<ApiReady>;
|
||||
}
|
||||
|
@ -214,6 +210,23 @@ export function makeClient(root: string, f = fetch): ApiClient {
|
|||
|
||||
const url = makeImageURL(root, 'inpaint', params);
|
||||
|
||||
if (doesExist(params.left)) {
|
||||
url.searchParams.append('left', params.left.toFixed(0));
|
||||
}
|
||||
|
||||
|
||||
if (doesExist(params.right)) {
|
||||
url.searchParams.append('right', params.right.toFixed(0));
|
||||
}
|
||||
|
||||
if (doesExist(params.top)) {
|
||||
url.searchParams.append('top', params.top.toFixed(0));
|
||||
}
|
||||
|
||||
if (doesExist(params.bottom)) {
|
||||
url.searchParams.append('bottom', params.bottom.toFixed(0));
|
||||
}
|
||||
|
||||
const body = new FormData();
|
||||
body.append('mask', params.mask, 'mask');
|
||||
body.append('source', params.source, 'source');
|
||||
|
@ -226,9 +239,6 @@ export function makeClient(root: string, f = fetch): ApiClient {
|
|||
// eslint-disable-next-line no-return-await
|
||||
return await pending;
|
||||
},
|
||||
async outpaint() {
|
||||
throw new NotImplementedError();
|
||||
},
|
||||
async ready(params: ApiResponse): Promise<ApiReady> {
|
||||
const path = makeApiUrl(root, 'ready');
|
||||
path.searchParams.append('output', params.output.key);
|
||||
|
@ -240,7 +250,7 @@ export function makeClient(root: string, f = fetch): ApiClient {
|
|||
}
|
||||
|
||||
export async function parseApiResponse(root: string, res: Response): Promise<ApiResponse> {
|
||||
type LimitedResponse = Omit<ApiResponse, 'output'> & {output: string};
|
||||
type LimitedResponse = Omit<ApiResponse, 'output'> & { output: string };
|
||||
|
||||
if (res.status === STATUS_SUCCESS) {
|
||||
const data = await res.json() as LimitedResponse;
|
||||
|
|
|
@ -9,6 +9,7 @@ import { ClientContext, StateContext } from '../state.js';
|
|||
import { ImageControl } from './ImageControl.js';
|
||||
import { ImageInput } from './ImageInput.js';
|
||||
import { MaskCanvas } from './MaskCanvas.js';
|
||||
import { OutpaintControl } from './OutpaintControl.js';
|
||||
|
||||
const { useContext } = React;
|
||||
|
||||
|
@ -88,6 +89,7 @@ export function Inpaint(props: InpaintProps) {
|
|||
setInpaint(newParams);
|
||||
}}
|
||||
/>
|
||||
<OutpaintControl config={config} />
|
||||
<Button onClick={() => upload.mutate()}>Generate</Button>
|
||||
</Stack>
|
||||
</Box>;
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
import { mustExist } from '@apextoaster/js-utils';
|
||||
import { Stack } from '@mui/material';
|
||||
import * as React from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { useStore } from 'zustand';
|
||||
|
||||
import { ConfigParams } from '../config.js';
|
||||
import { StateContext } from '../state.js';
|
||||
import { NumericField } from './NumericField.js';
|
||||
|
||||
export interface OutpaintControlProps {
|
||||
config: ConfigParams;
|
||||
}
|
||||
|
||||
export function OutpaintControl(props: OutpaintControlProps) {
|
||||
const { config } = props;
|
||||
|
||||
const state = mustExist(useContext(StateContext));
|
||||
const params = useStore(state, (s) => s.inpaint);
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
const setInpaint = useStore(state, (s) => s.setInpaint);
|
||||
|
||||
return <Stack direction='row' spacing={4}>
|
||||
<NumericField
|
||||
label='Left'
|
||||
min={0}
|
||||
max={config.width.max}
|
||||
step={config.width.step}
|
||||
value={params.left}
|
||||
onChange={(left) => {
|
||||
setInpaint({
|
||||
left,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<NumericField
|
||||
label='Right'
|
||||
min={0}
|
||||
max={config.width.max}
|
||||
step={config.width.step}
|
||||
value={params.right}
|
||||
onChange={(right) => {
|
||||
setInpaint({
|
||||
right,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<NumericField
|
||||
label='Top'
|
||||
min={0}
|
||||
max={config.height.max}
|
||||
step={config.height.step}
|
||||
value={params.top}
|
||||
onChange={(top) => {
|
||||
setInpaint({
|
||||
top,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<NumericField
|
||||
label='Bottom'
|
||||
min={0}
|
||||
max={config.height.max}
|
||||
step={config.height.step}
|
||||
value={params.bottom}
|
||||
onChange={(bottom) => {
|
||||
setInpaint({
|
||||
bottom,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Stack>;
|
||||
}
|
|
@ -114,6 +114,10 @@ export function createStateSlices(base: ConfigParams) {
|
|||
...defaults,
|
||||
mask: null,
|
||||
source: null,
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
setInpaint(params) {
|
||||
set((prev) => ({
|
||||
|
@ -129,6 +133,10 @@ export function createStateSlices(base: ConfigParams) {
|
|||
...defaults,
|
||||
mask: null,
|
||||
source: null,
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue