some visual changes to image generation
Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
This commit is contained in:
parent
7bc0549337
commit
2c82c0468f
4 changed files with 168 additions and 48 deletions
|
@ -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
3
css/ui/tool/dream.css
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.dream-interrupt-btn {
|
||||||
|
width: 100px;
|
||||||
|
}
|
|
@ -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" />
|
||||||
|
|
||||||
|
|
|
@ -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,12 +132,25 @@ 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,
|
||||||
|
@ -140,6 +165,14 @@ const _generate = async (endpoint, request, bb) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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"});
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue