Merge branch 'main' into win-hooks

This commit is contained in:
Victor Seiji Hariki 2023-01-02 23:20:09 -03:00 committed by GitHub
commit ce80d9d10c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 254 additions and 19 deletions

View file

@ -22,7 +22,7 @@ this is a completely vanilla javascript and html canvas outpainting convenience
- queueable, cancelable dreams - just start a'clickin' all over the place
- arbitrary dream reticle size - draw the rectangle of your dreams
- an [effectively infinite](https://github.com/zero01101/openOutpaint/pull/108), resizable, scalable canvas for you to paint all over
- **_NOTE: v0.0.10 introduces a new "camera control" modifier key - hold [`CTRL`] ([`CMD`] on mac) and use the scrollwheel to zoom (scroll the wheel) and pan (hold the wheel button) around the canvas_**
- **_NOTE: v0.0.10 introduces a new "camera control" modifier key - hold [`CTRL`] and use the scrollwheel to zoom (scroll the wheel or use the two-finger vertical gesture on, uh, modern touchpads) and pan (hold the scrollwheel button, or if you don't have one, left-click button) around the canvas_**
- a very nicely functional and familiar layer system
- inpainting/touchup mask brush
- prompt history panel
@ -36,12 +36,14 @@ this is a completely vanilla javascript and html canvas outpainting convenience
- floating toolbox with handy keyboard shortcuts
- optional grid snapping for precision
- optional hi-res fix for blank/txt2img dreams which, if enabled, uses image width/height / 2 as firstpass size
- optional HRfix lock px to constrain maximum firstpass values
- optional overmasking for potentially better seams between outpaints - set overmask px value to 0 to disable the feature
- import arbitrary images and scale/stamp on the canvas whenever, wherever you'd like
- upscaler support for final output images
- saves your preferences/imported images to browser localstorage for maximum convenience
- reset to defaults button to unsave your preferences if things go squirrely
- floating navigable undo/redo palette with ctrl+z/y keyboard shortcuts for additional maximum convenience and desquirreliness
- optional generate-ahead function to keep crankin' out the dreams while you look through the ones that already exist
- _all this and much more for the low, low price of simply already owning an expensive GPU!_
## operation

View file

@ -304,6 +304,15 @@ input#host {
box-sizing: border-box;
}
/* Model Select */
#models-ac-select option {
background-color: #fcc;
}
#models-ac-select option.inpainting {
background-color: #cfc;
}
/* Settings button */
.ui.icon.header-button {
padding: 0;

View file

@ -100,6 +100,26 @@ div.slider-wrapper > input.text {
background-color: transparent;
}
/* Bare Select */
.bareselector {
border-radius: 5px;
background-color: white;
overflow-y: auto;
margin-top: 0;
margin-left: 0;
max-height: 200px;
min-width: 100%;
max-width: 800px;
width: fit-content;
z-index: 200;
}
/* Autocomplete Select */
div.autocomplete {
border-radius: 5px;

View file

@ -7,10 +7,10 @@
<link href="css/colors.css?v=3f81e80" rel="stylesheet" />
<link href="css/icons.css?v=a25504c" rel="stylesheet" />
<link href="css/index.css?v=ef0c943" rel="stylesheet" />
<link href="css/index.css?v=69d3b9e" rel="stylesheet" />
<link href="css/layers.css?v=b4fbf61" rel="stylesheet" />
<link href="css/ui/generic.css?v=a15ce4b" rel="stylesheet" />
<link href="css/ui/generic.css?v=79bee9b" rel="stylesheet" />
<link href="css/ui/history.css?v=0b03861" rel="stylesheet" />
<link href="css/ui/layers.css?v=4fd95fe" rel="stylesheet" />
@ -100,6 +100,7 @@
<input type="checkbox" id="cbxHRFix" onchange="changeHiResFix()" />
<label for="cbxHRFix">Auto txt2img HRfix</label>
<br />
<div id="hrFixLock"></div>
<input
type="checkbox"
id="cbxRestoreFaces"
@ -186,7 +187,7 @@
<br />
<span id="version">
<a href="https://github.com/zero01101/openOutpaint" target="_blank">
Alpha release v0.0.12.1
Alpha release v0.0.12.3
</a>
</span>
<br />
@ -310,7 +311,7 @@
<div class="ui separator"></div>
<iframe
id="page-overlay"
src="pages/configuration.html?v=b872bdc"></iframe>
src="pages/configuration.html?v=ae8af5d"></iframe>
</div>
</div>
@ -324,8 +325,8 @@
<script src="js/lib/layers.js?v=a1f8aea" type="text/javascript"></script>
<script src="js/lib/commands.js?v=00464cb" type="text/javascript"></script>
<script src="js/lib/toolbar.js?v=8a08072" type="text/javascript"></script>
<script src="js/lib/ui.js?v=8481b85" type="text/javascript"></script>
<script src="js/lib/toolbar.js?v=d483951" type="text/javascript"></script>
<script src="js/lib/ui.js?v=76ede2b" type="text/javascript"></script>
<script
src="js/initalize/layers.populate.js?v=c81f0a5"
@ -336,7 +337,7 @@
<!-- Content -->
<script src="js/prompt.js?v=7a1c68c" type="text/javascript"></script>
<script src="js/index.js?v=dd80c92" type="text/javascript"></script>
<script src="js/index.js?v=133b74b" type="text/javascript"></script>
<script
src="js/ui/floating/history.js?v=fc92d14"
@ -350,7 +351,7 @@
src="js/ui/tool/generic.js?v=f1a19a4"
type="text/javascript"></script>
<script src="js/ui/tool/dream.js?v=230e42e" type="text/javascript"></script>
<script src="js/ui/tool/dream.js?v=832aa62" type="text/javascript"></script>
<script
src="js/ui/tool/maskbrush.js?v=1e8a893"
type="text/javascript"></script>

View file

@ -531,6 +531,16 @@ const modelAutoComplete = createAutoComplete(
"Model",
document.getElementById("models-ac-select")
);
modelAutoComplete.onchange.on(({value}) => {
if (value.toLowerCase().includes("inpainting"))
document.querySelector(
"#models-ac-select input.autocomplete-text"
).style.backgroundColor = "#cfc";
else
document.querySelector(
"#models-ac-select input.autocomplete-text"
).style.backgroundColor = "#fcc";
});
const samplerAutoComplete = createAutoComplete(
"Sampler",
@ -565,8 +575,8 @@ makeSlider(
"CFG Scale",
document.getElementById("cfgScale"),
"cfg_scale",
-1,
25,
localStorage.getItem("openoutpaint/settings.min-cfg") || 1,
localStorage.getItem("openoutpaint/settings.max-cfg") || 25,
0.5,
7.0,
0.1
@ -600,7 +610,27 @@ makeSlider(
0.1
);
makeSlider("Steps", document.getElementById("steps"), "steps", 1, 70, 5, 30, 1);
makeSlider(
"Steps",
document.getElementById("steps"),
"steps",
1,
localStorage.getItem("openoutpaint/settings.max-steps") || 70,
5,
30,
1
);
makeSlider(
"HRfix Lock Px.",
document.getElementById("hrFixLock"),
"hr_fix_lock_px",
0.0,
768.0,
256.0,
0.0,
1.0
);
function changeMaskBlur() {
stableDiffusionData.mask_blur = parseInt(
@ -782,6 +812,10 @@ async function getModels() {
modelAutoComplete.options = data.map((option) => ({
name: option.title,
value: option.title,
optionelcb: (el) => {
if (option.title.toLowerCase().includes("inpainting"))
el.classList.add("inpainting");
},
}));
try {

View file

@ -188,4 +188,31 @@ const _toolbar_input = {
},
};
},
selectlist: (
state,
dataKey,
text,
options = {value, text},
defaultOptionValue,
cb = null
) => {
const selectlist = document.createElement("select");
selectlist.classList.add("bareselector");
Object.entries(options).forEach((opt) => {
var option = document.createElement("option");
option.value = opt[0];
option.text = opt[1];
selectlist.options.add(option);
});
selectlist.selectedIndex = defaultOptionValue;
selectlist.onchange = () => {
state[dataKey] = selectlist.selectedIndex;
cb && cb();
};
const label = document.createElement("label");
label.appendChild(new Text(text));
label.appendChild(selectlist);
return {selectlist, label};
},
};

View file

@ -206,7 +206,7 @@ function createSlider(name, wrapper, options = {}) {
* @param {HTMLDivElement} wrapper The div element that will wrap the input elements
* @param {object} options Extra options
* @param {boolean} options.multiple Whether multiple options can be selected
* @param {{name: string, value: string}[]} options.options Options to add to the selector
* @param {{name: string, value: string, optionelcb: (el: HTMLOptionElement) => void}[]} options.options Options to add to the selector
* @returns {AutoCompleteElement}
*/
function createAutoComplete(name, wrapper, options = {}) {
@ -293,6 +293,7 @@ function createAutoComplete(name, wrapper, options = {}) {
const optionEl = document.createElement("option");
optionEl.classList.add("autocomplete-option");
optionEl.title = title || name;
if (opt.optionelcb) opt.optionelcb(optionEl);
const option = {name, value, optionElement: optionEl};

View file

@ -375,12 +375,16 @@ const _generate = async (endpoint, request, bb, options = {}) => {
});
};
const sendInterrupt = () => {
fetch(`${host}${config.api.path}interrupt`, {method: "POST"});
};
// Add Interrupt Button
const interruptButton = makeElement("button", bb.x + bb.w - 100, bb.y + bb.h);
interruptButton.classList.add("dream-stop-btn");
interruptButton.textContent = "Interrupt";
interruptButton.addEventListener("click", () => {
fetch(`${host}${config.api.path}interrupt`, {method: "POST"});
sendInterrupt();
interruptButton.disabled = true;
});
const marchingOptions = {};
@ -390,6 +394,9 @@ const _generate = async (endpoint, request, bb, options = {}) => {
console.info(`[dream] Generating images for prompt '${request.prompt}'`);
console.debug(request);
eagerGenerateCount = toolbar._current_tool.state.eagerGenerateCount;
isDreamComplete = false;
let stopProgress = null;
try {
let stopDrawingStatus = false;
@ -428,6 +435,19 @@ const _generate = async (endpoint, request, bb, options = {}) => {
imageCollection.inputElement.removeChild(interruptButton);
}
const needMoreGenerations = () => {
return (
eagerGenerateCount > 0 &&
images.length - highestNavigatedImageIndex <= eagerGenerateCount
);
};
const isGenerationPending = () => {
return generationQueue.length > 0;
};
let highestNavigatedImageIndex = 0;
// Image navigation
const prevImg = () => {
at--;
@ -443,10 +463,16 @@ const _generate = async (endpoint, request, bb, options = {}) => {
at++;
if (at >= images.length) at = 0;
highestNavigatedImageIndex = Math.max(at, highestNavigatedImageIndex);
imageindextxt.textContent = `${at}/${images.length - 1}`;
var seed = seeds[at];
seedbtn.title = "Use seed " + seed;
redraw();
if (needMoreGenerations() && !isGenerationPending()) {
makeMore();
}
};
const applyImg = async () => {
@ -504,6 +530,11 @@ const _generate = async (endpoint, request, bb, options = {}) => {
}
nextQueue(moreQ);
//Start the next batch if we're eager-generating
if (needMoreGenerations() && !isGenerationPending() && !isDreamComplete) {
makeMore();
}
};
const discardImg = async () => {
@ -657,6 +688,10 @@ const _generate = async (endpoint, request, bb, options = {}) => {
mouse.listen.world.btn.right.onclick.clear(oncancelhandler);
mouse.listen.world.btn.middle.onclick.clear(onmorehandler);
mouse.listen.world.onwheel.clear(onwheelhandler);
isDreamComplete = true;
if (generating) {
sendInterrupt();
}
};
redraw();
@ -740,6 +775,11 @@ const _generate = async (endpoint, request, bb, options = {}) => {
imageSelectMenu.appendChild(seedbtn);
nextQueue(initialQ);
//Start the next batch after the initial generation
if (needMoreGenerations()) {
makeMore();
}
};
/**
@ -932,7 +972,7 @@ const dream_img2img_callback = (bb, resolution, state) => {
request.height = resolution.h;
request.denoising_strength = state.denoisingStrength;
request.inpainting_fill = 1; // For img2img use original
request.inpainting_fill = state.inpainting_fill; //let's see how this works //1; // For img2img use original
// Load prompt (maybe we should add some events so we don't have to do this)
request.prompt = document.getElementById("prompt").value;
@ -1186,6 +1226,7 @@ const dreamTool = () =>
state.keepUnmaskedBlur = 8;
state.overMaskPx = 20;
state.preserveMasks = false;
state.eagerGenerateCount = 0;
state.erasePrevCursor = () =>
uiCtx.clearRect(0, 0, uiCanvas.width, uiCanvas.height);
@ -1346,6 +1387,18 @@ const dreamTool = () =>
h: stableDiffusionData.height,
};
//hacky set non-square auto hrfix values
let hrLockPx =
localStorage.getItem("openoutpaint/hr_fix_lock_px") ?? 0;
stableDiffusionData.firstphase_height =
hrLockPx == 0 || resolution.h / 2 <= hrLockPx
? resolution.h / 2
: hrLockPx;
stableDiffusionData.firstphase_width =
hrLockPx == 0 || resolution.w / 2 <= hrLockPx
? resolution.w / 2
: hrLockPx;
if (global.connection === "online") {
dream_generate_callback(bb, resolution, state);
} else {
@ -1477,6 +1530,19 @@ const dreamTool = () =>
textStep: 1,
}
).slider;
// Eager generation Slider
state.ctxmenu.eagerGenerateCountLabel = _toolbar_input.slider(
state,
"eagerGenerateCount",
"Generate-ahead count",
{
min: 0,
max: 100,
step: 2,
textStep: 1,
}
).slider;
}
menu.appendChild(state.ctxmenu.cursorSizeSlider);
@ -1489,6 +1555,7 @@ const dreamTool = () =>
menu.appendChild(state.ctxmenu.preserveMasksLabel);
menu.appendChild(document.createElement("br"));
menu.appendChild(state.ctxmenu.overMaskPxLabel);
menu.appendChild(state.ctxmenu.eagerGenerateCountLabel);
},
shortcut: "D",
}
@ -1573,6 +1640,7 @@ const img2imgTool = () =>
state.keepUnmaskedBlur = 8;
state.fullResolution = false;
state.preserveMasks = false;
state.eagerGenerateCount = 0;
state.denoisingStrength = 0.7;
@ -2006,6 +2074,36 @@ const img2imgTool = () =>
textStep: 1,
}
).slider;
// inpaint fill type select list
state.ctxmenu.inpaintTypeSelect = _toolbar_input.selectlist(
state,
"inpainting_fill",
"Inpaint Type",
{
0: "fill",
1: "original (recommended)",
2: "latent noise",
3: "latent nothing",
},
1, // USE ORIGINAL FOR IMG2IMG OR ELSE but we still give you the option because we love you
() => {
stableDiffusionData.inpainting_fill = state.inpainting_fill;
}
).label;
// Eager generation Slider
state.ctxmenu.eagerGenerateCountLabel = _toolbar_input.slider(
state,
"eagerGenerateCount",
"Generate-ahead count",
{
min: 0,
max: 100,
step: 2,
textStep: 1,
}
).slider;
}
menu.appendChild(state.ctxmenu.cursorSizeSlider);
@ -2020,9 +2118,11 @@ const img2imgTool = () =>
menu.appendChild(document.createElement("br"));
menu.appendChild(state.ctxmenu.fullResolutionLabel);
menu.appendChild(document.createElement("br"));
menu.appendChild(state.ctxmenu.inpaintTypeSelect);
menu.appendChild(state.ctxmenu.denoisingStrengthSlider);
menu.appendChild(state.ctxmenu.borderMaskGradientCheckbox);
menu.appendChild(state.ctxmenu.borderMaskSlider);
menu.appendChild(state.ctxmenu.eagerGenerateCountLabel);
},
shortcut: "I",
}
@ -2030,8 +2130,7 @@ const img2imgTool = () =>
window.onbeforeunload = async () => {
// Stop current generation on page close
if (generating)
await fetch(`${host}${config.api.path}interrupt`, {method: "POST"});
if (generating) await sendInterrupt();
};
const sendSeed = (seed) => {

View file

@ -7,10 +7,10 @@
<link href="../css/colors.css?v=3f81e80" rel="stylesheet" />
<link href="../css/icons.css?v=a25504c" rel="stylesheet" />
<link href="../css/index.css?v=ef0c943" rel="stylesheet" />
<link href="../css/index.css?v=69d3b9e" rel="stylesheet" />
<link href="../css/layers.css?v=b4fbf61" rel="stylesheet" />
<link href="../css/ui/generic.css?v=a15ce4b" rel="stylesheet" />
<link href="../css/ui/generic.css?v=79bee9b" rel="stylesheet" />
<link href="../css/ui/history.css?v=0b03861" rel="stylesheet" />
<link href="../css/ui/layers.css?v=4fd95fe" rel="stylesheet" />
@ -59,10 +59,39 @@
type="number"
step="1" />
</label>
<label style="display: flex">
Max Steps:
<input
id="max-steps"
class="canvas-size-input"
type="number"
step="1"
value="70" />
</label>
<label style="display: flex">
CFG minmax:
<input
id="min-cfg"
class="canvas-size-input"
type="number"
step="0.1"
value="-1.0" />
::
<input
id="max-cfg"
class="canvas-size-input"
type="number"
step="0.1"
value="30.0" />
</label>
<p>Refresh the page to apply settings.</p>
<script>
const canvasWidth = document.getElementById("canvas-width");
const canvasHeight = document.getElementById("canvas-height");
const maxSteps = document.getElementById("max-steps");
const minCfg = document.getElementById("min-cfg");
const maxCfg = document.getElementById("max-cfg");
function writeToLocalStorage() {
localStorage.setItem(
@ -73,6 +102,9 @@
"openoutpaint/settings.canvas-height",
canvasHeight.value
);
localStorage.setItem("openoutpaint/settings.max-steps", maxSteps.value);
localStorage.setItem("openoutpaint/settings.min-cfg", minCfg.value);
localStorage.setItem("openoutpaint/settings.max-cfg", maxCfg.value);
}
// Loads values from local storage
@ -80,11 +112,20 @@
localStorage.getItem("openoutpaint/settings.canvas-width") || 2048;
canvasHeight.value =
localStorage.getItem("openoutpaint/settings.canvas-height") || 2048;
maxSteps.value =
localStorage.getItem("openoutpaint/settings.max-steps") || 70;
minCfg.value =
localStorage.getItem("openoutpaint/settings.min-cfg") || -1;
maxCfg.value =
localStorage.getItem("openoutpaint/settings.max-cfg") || 30;
writeToLocalStorage();
canvasWidth.onchange = writeToLocalStorage;
canvasHeight.onchange = writeToLocalStorage;
maxSteps.onchange = writeToLocalStorage;
minCfg.onchange = writeToLocalStorage;
maxCfg.onchange = writeToLocalStorage;
</script>
</body>
</html>

View file

@ -9,6 +9,7 @@
id="openoutpaint"
style="width: 100%; height: 800px"
src="../index.html?v=e2520a0"
src="../index.html?v=49afaa2"
frameborder="0"></iframe>
<button id="add-res">Add Resource</button>
<script>