some visual changes to image generation

Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
This commit is contained in:
Victor Seiji Hariki 2022-12-06 18:28:34 -03:00
parent 7bc0549337
commit 2c82c0468f
4 changed files with 168 additions and 48 deletions

View file

@ -126,20 +126,33 @@ select > option:checked::after {
} }
/* Icon button */ /* Icon button */
.ui.squaer { .ui.square {
aspect-ratio: 1; aspect-ratio: 1;
} }
.ui.button.icon { .ui.button {
display: flex;
align-items: stretch;
cursor: pointer; cursor: pointer;
padding: 0; padding: 0;
margin: 0; margin: 0;
border: 0; border: 0;
background-color: transparent; color: var(--c-text);
background-color: var(--c-primary);
transition-duration: 50ms;
}
.ui.button:hover {
background-color: var(--c-hover);
}
.ui.button:active {
background-color: var(--c-hover);
filter: brightness(120%);
}
.ui.button.icon {
display: flex;
align-items: stretch;
} }
.ui.button.icon > *:first-child { .ui.button.icon > *:first-child {
@ -155,7 +168,3 @@ select > option:checked::after {
mask-repeat: no-repeat; mask-repeat: no-repeat;
background-color: var(--c-text); background-color: var(--c-text);
} }
.ui.button.icon:hover {
background-color: var(--c-hover);
}

3
css/ui/tool/dream.css Normal file
View file

@ -0,0 +1,3 @@
.dream-interrupt-btn {
width: 100px;
}

View file

@ -17,6 +17,7 @@
<link href="css/ui/toolbar.css" rel="stylesheet" /> <link href="css/ui/toolbar.css" rel="stylesheet" />
<!-- Tool Specific CSS --> <!-- Tool Specific CSS -->
<link href="css/ui/tool/dream.css" rel="stylesheet" />
<link href="css/ui/tool/stamp.css" rel="stylesheet" /> <link href="css/ui/tool/stamp.css" rel="stylesheet" />
<link href="css/ui/tool/colorbrush.css" rel="stylesheet" /> <link href="css/ui/tool/colorbrush.css" rel="stylesheet" />

View file

@ -1,12 +1,14 @@
let blockNewImages = false; let blockNewImages = false;
let generating = false;
/** /**
* Starts progress monitoring bar * Starts progress monitoring bar
* *
* @param {BoundingBox} bb Bouding Box to draw progress to * @param {BoundingBox} bb Bouding Box to draw progress to
* @param {(data: object) => void} [oncheck] Callback function for when a progress check returns
* @returns {() => void} * @returns {() => void}
*/ */
const _monitorProgress = (bb) => { const _monitorProgress = (bb, oncheck = null) => {
const minDelay = 1000; const minDelay = 1000;
const apiURL = `${host}${url}progress?skip_current_image=true`; const apiURL = `${host}${url}progress?skip_current_image=true`;
@ -33,6 +35,8 @@ const _monitorProgress = (bb) => {
/** @type {StableDiffusionProgressResponse} */ /** @type {StableDiffusionProgressResponse} */
const data = await response.json(); const data = await response.json();
oncheck && oncheck(data);
// Draw Progress Bar // Draw Progress Bar
layer.ctx.fillStyle = "#5F5"; layer.ctx.fillStyle = "#5F5";
layer.ctx.fillRect(1, 1, bb.w * data.progress, 10); layer.ctx.fillRect(1, 1, bb.w * data.progress, 10);
@ -81,6 +85,7 @@ const _dream = async (endpoint, request) => {
/** @type {StableDiffusionResponse} */ /** @type {StableDiffusionResponse} */
let data = null; let data = null;
try { try {
generating = true;
const response = await fetch(apiURL, { const response = await fetch(apiURL, {
method: "POST", method: "POST",
headers: { headers: {
@ -92,6 +97,7 @@ const _dream = async (endpoint, request) => {
data = await response.json(); data = await response.json();
} finally { } finally {
generating = false;
} }
return data.images; return data.images;
@ -103,9 +109,15 @@ const _dream = async (endpoint, request) => {
* @param {"txt2img" | "img2img"} endpoint Endpoint to send the request to * @param {"txt2img" | "img2img"} endpoint Endpoint to send the request to
* @param {StableDiffusionRequest} request Stable diffusion request * @param {StableDiffusionRequest} request Stable diffusion request
* @param {BoundingBox} bb Generated image placement location * @param {BoundingBox} bb Generated image placement location
* @param {number} [drawEvery=0.2 / request.n_iter] Percentage delta to draw progress at (by default 20% of each iteration)
* @returns {Promise<HTMLImageElement | null>} * @returns {Promise<HTMLImageElement | null>}
*/ */
const _generate = async (endpoint, request, bb) => { const _generate = async (
endpoint,
request,
bb,
drawEvery = 0.2 / request.n_iter
) => {
const requestCopy = {...request}; const requestCopy = {...request};
// Images to select through // Images to select through
@ -120,26 +132,47 @@ const _generate = async (endpoint, request, bb) => {
after: maskPaintLayer, after: maskPaintLayer,
}); });
const redraw = () => { const makeElement = (type, x, y) => {
const el = document.createElement(type);
el.style.position = "absolute";
el.style.left = `${x}px`;
el.style.top = `${y}px`;
// We can use the input element to add interactible html elements in the world
imageCollection.inputElement.appendChild(el);
return el;
};
const redraw = (url = images[at]) => {
if (!url) return;
const image = new Image(); const image = new Image();
image.src = "data:image/png;base64," + images[at]; image.src = "data:image/png;base64," + url;
image.addEventListener("load", () => { image.addEventListener("load", () => {
layer.ctx.clearRect(0, 0, layer.canvas.width, layer.canvas.height); layer.ctx.clearRect(0, 0, layer.canvas.width, layer.canvas.height);
if (images[at]) layer.ctx.drawImage(
layer.ctx.drawImage( image,
image, 0,
0, 0,
0, image.width,
image.width, image.height,
image.height, bb.x,
bb.x, bb.y,
bb.y, bb.w,
bb.w, bb.h
bb.h );
);
}); });
}; };
// Add Interrupt Button
const interruptButton = makeElement("button", bb.x + bb.w - 100, bb.y + bb.h);
interruptButton.classList.add("dream-interrupt-btn");
interruptButton.textContent = "Interrupt";
interruptButton.addEventListener("click", () => {
fetch(`${host}${url}interrupt`, {method: "POST"});
interruptButton.disabled = true;
});
const stopMarchingAnts = march(bb); const stopMarchingAnts = march(bb);
// First Dream Run // First Dream Run
@ -148,8 +181,28 @@ const _generate = async (endpoint, request, bb) => {
let stopProgress = null; let stopProgress = null;
try { try {
stopProgress = _monitorProgress(bb); let stopDrawingStatus = false;
let lastProgress = 0;
let nextCP = drawEvery;
stopProgress = _monitorProgress(bb, (data) => {
if (stopDrawingStatus) return;
if (lastProgress < nextCP && data.progress >= nextCP) {
nextCP += drawEvery;
fetch(`${host}${url}progress?skip_current_image=false`).then(
async (response) => {
if (stopDrawingStatus) return;
const imagedata = await response.json();
redraw(imagedata.current_image);
}
);
}
lastProgress = data.progress;
});
imageCollection.inputElement.appendChild(interruptButton);
images.push(...(await _dream(endpoint, requestCopy))); images.push(...(await _dream(endpoint, requestCopy)));
stopDrawingStatus = true;
} catch (e) { } catch (e) {
alert( alert(
`Error generating images. Please try again or see consolde for more details` `Error generating images. Please try again or see consolde for more details`
@ -158,6 +211,7 @@ const _generate = async (endpoint, request, bb) => {
console.warn(e); console.warn(e);
} finally { } finally {
stopProgress(); stopProgress();
imageCollection.inputElement.removeChild(interruptButton);
} }
// Image navigation // Image navigation
@ -196,6 +250,8 @@ const _generate = async (endpoint, request, bb) => {
const makeMore = async () => { const makeMore = async () => {
try { try {
stopProgress = _monitorProgress(bb); stopProgress = _monitorProgress(bb);
interruptButton.disabled = false;
imageCollection.inputElement.appendChild(interruptButton);
images.push(...(await _dream(endpoint, requestCopy))); images.push(...(await _dream(endpoint, requestCopy)));
imageindextxt.textContent = `${at + 1}/${images.length}`; imageindextxt.textContent = `${at + 1}/${images.length}`;
} catch (e) { } catch (e) {
@ -206,6 +262,7 @@ const _generate = async (endpoint, request, bb) => {
console.warn(e); console.warn(e);
} finally { } finally {
stopProgress(); stopProgress();
imageCollection.inputElement.removeChild(interruptButton);
} }
}; };
@ -265,18 +322,6 @@ const _generate = async (endpoint, request, bb) => {
keyboard.listen.onkeyclick.clear(onarrow); keyboard.listen.onkeyclick.clear(onarrow);
}; };
const makeElement = (type, x, y) => {
const el = document.createElement(type);
el.style.position = "absolute";
el.style.left = `${x}px`;
el.style.top = `${y}px`;
// We can use the input element to add interactible html elements in the world
imageCollection.inputElement.appendChild(el);
return el;
};
redraw(); redraw();
imageSelectMenu = makeElement("div", bb.x, bb.y + bb.h); imageSelectMenu = makeElement("div", bb.x, bb.y + bb.h);
@ -541,8 +586,6 @@ const dream_img2img_callback = (evn, state) => {
// Get visible pixels // Get visible pixels
const visibleCanvas = uil.getVisible(bb); const visibleCanvas = uil.getVisible(bb);
console.debug(visibleCanvas);
// Do nothing if no image exists // Do nothing if no image exists
if (isCanvasBlank(0, 0, bb.w, bb.h, visibleCanvas)) return; if (isCanvasBlank(0, 0, bb.w, bb.h, visibleCanvas)) return;
@ -670,7 +713,7 @@ const dream_img2img_callback = (evn, state) => {
/** /**
* Dream and img2img tools * Dream and img2img tools
*/ */
const _reticle_draw = (evn, state) => { const _reticle_draw = (evn, state, textStyle = "#FFF5") => {
const bb = getBoundingBox( const bb = getBoundingBox(
evn.x, evn.x,
evn.y, evn.y,
@ -678,21 +721,56 @@ const _reticle_draw = (evn, state) => {
state.cursorSize, state.cursorSize,
state.snapToGrid && basePixelCount state.snapToGrid && basePixelCount
); );
const cvp = viewport.canvasToView(evn.x, evn.y);
const bbvp = { const bbvp = {
...viewport.canvasToView(bb.x, bb.y), ...viewport.canvasToView(bb.x, bb.y),
w: viewport.zoom * bb.w, w: viewport.zoom * bb.w,
h: viewport.zoom * bb.h, h: viewport.zoom * bb.h,
}; };
uiCtx.save();
// draw targeting square reticle thingy cursor // draw targeting square reticle thingy cursor
uiCtx.lineWidth = 1; uiCtx.lineWidth = 1;
uiCtx.strokeStyle = "#FFF"; uiCtx.strokeStyle = "#FFF";
uiCtx.strokeRect(bbvp.x, bbvp.y, bbvp.w, bbvp.h); //origin is middle of the frame uiCtx.strokeRect(bbvp.x, bbvp.y, bbvp.w, bbvp.h); //origin is middle of the frame
// Draw width and height
uiCtx.textAlign = "center";
uiCtx.fillStyle = textStyle;
uiCtx.font = `bold 20px Open Sans`;
uiCtx.translate(bbvp.x + bbvp.w / 2, bbvp.y + bbvp.h / 2);
const xshrink = Math.min(
1,
(bbvp.w - 30) / uiCtx.measureText(`${state.cursorSize}px`).width
);
const yshrink = Math.min(
1,
(bbvp.h - 30) / uiCtx.measureText(`${state.cursorSize}px`).width
);
uiCtx.font = `bold ${20 * xshrink}px Open Sans`;
uiCtx.fillText(
`${state.cursorSize}px`,
0,
bbvp.h / 2 - 10 * xshrink,
state.cursorSize
);
uiCtx.rotate(-Math.PI / 2);
uiCtx.font = `bold ${20 * yshrink}px Open Sans`;
uiCtx.fillText(
`${state.cursorSize}px`,
0,
bbvp.h / 2 - 10 * yshrink,
state.cursorSize
);
uiCtx.restore();
return () => { return () => {
uiCtx.save();
uiCtx.clearRect(bbvp.x - 10, bbvp.y - 10, bbvp.w + 20, bbvp.h + 20); uiCtx.clearRect(bbvp.x - 10, bbvp.y - 10, bbvp.w + 20, bbvp.h + 20);
uiCtx.restore();
}; };
}; };
@ -701,7 +779,6 @@ const _reticle_draw = (evn, state) => {
*/ */
const _dream_onwheel = (evn, state) => { const _dream_onwheel = (evn, state) => {
state.mousemovecb(evn);
if (!evn.evn.ctrlKey) { if (!evn.evn.ctrlKey) {
const v = const v =
state.cursorSize - state.cursorSize -
@ -761,10 +838,26 @@ const dreamTool = () =>
state.erasePrevReticle = () => state.erasePrevReticle = () =>
uiCtx.clearRect(0, 0, uiCanvas.width, uiCanvas.height); uiCtx.clearRect(0, 0, uiCanvas.width, uiCanvas.height);
state.mousemovecb = (evn) => { let lastMouseMove = {
state.erasePrevReticle(); ...mouse.coords.world.pos,
state.erasePrevReticle = _reticle_draw(evn, state);
}; };
state.mousemovecb = (evn) => {
lastMouseMove = evn;
state.erasePrevReticle();
const style =
state.cursorSize > stableDiffusionData.width
? "#FBB5"
: state.cursorSize < stableDiffusionData.width
? "#BFB5"
: "#FFF5";
state.erasePrevReticle = _reticle_draw(evn, state, style);
};
state.redraw = () => {
state.mousemovecb(lastMouseMove);
};
state.wheelcb = (evn) => { state.wheelcb = (evn) => {
_dream_onwheel(evn, state); _dream_onwheel(evn, state);
}; };
@ -887,7 +980,11 @@ const img2imgTool = () =>
state.erasePrevReticle = () => state.erasePrevReticle = () =>
uiCtx.clearRect(0, 0, uiCanvas.width, uiCanvas.height); uiCtx.clearRect(0, 0, uiCanvas.width, uiCanvas.height);
let lastMouseMove = {
...mouse.coords.world.pos,
};
state.mousemovecb = (evn) => { state.mousemovecb = (evn) => {
lastMouseMove = evn;
state.erasePrevReticle(); state.erasePrevReticle();
state.erasePrevReticle = _reticle_draw(evn, state); state.erasePrevReticle = _reticle_draw(evn, state);
const bb = getBoundingBox( const bb = getBoundingBox(
@ -989,6 +1086,11 @@ const img2imgTool = () =>
); );
} }
}; };
state.redraw = () => {
state.mousemovecb(lastMouseMove);
};
state.wheelcb = (evn) => { state.wheelcb = (evn) => {
_dream_onwheel(evn, state); _dream_onwheel(evn, state);
}; };
@ -1089,3 +1191,8 @@ const img2imgTool = () =>
shortcut: "I", shortcut: "I",
} }
); );
window.onbeforeunload = async () => {
// Stop current generation on page close
if (generating) await fetch(`${host}${url}interrupt`, {method: "POST"});
};