Merge branch 'main' into edit_utils
Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
This commit is contained in:
commit
096d8770f2
6 changed files with 940 additions and 1085 deletions
19
.prettierrc.json
Normal file
19
.prettierrc.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"arrowParens": "always",
|
||||
"bracketSameLine": true,
|
||||
"bracketSpacing": false,
|
||||
"embeddedLanguageFormatting": "auto",
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"insertPragma": false,
|
||||
"jsxSingleQuote": false,
|
||||
"printWidth": 80,
|
||||
"proseWrap": "preserve",
|
||||
"quoteProps": "as-needed",
|
||||
"requirePragma": false,
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5",
|
||||
"useTabs": true,
|
||||
"vueIndentScriptAndStyle": false
|
||||
}
|
14
README.md
14
README.md
|
@ -17,11 +17,12 @@ this is a completely vanilla javascript and html canvas outpainting convenience
|
|||
- easily change samplers/steps/CFG/etc options for each dream summoned from the latent void
|
||||
- optional right-click to erase output image under cursor
|
||||
- optional grid snapping for precision
|
||||
- optional overmasking for potentially better seams between outpaints and it _sorta_ works currently but it needs fixing - set overmask px value to 0 to disable the feature
|
||||
- optional overmasking for potentially better seams between outpaints - set overmask px value to 0 to disable the feature
|
||||
- optional hi-res fix for blank/txt2img dreams which, if enabled, uses image width/height / 2 as firstpass size
|
||||
- import arbitrary images and superimpose on the canvas wherever you'd like ([extra fun with transparent .pngs!](#arbitrary_transparent))
|
||||
- "temporary" monitors at the bottom to see exactly what mask/image you're feeding img2img, no i'm certainly not using them as actual imagedata sources or anything
|
||||
- saves your preferences to browser localstorage for maximum convenience
|
||||
- undo/redo
|
||||
|
||||
## operation
|
||||
|
||||
|
@ -69,7 +70,7 @@ this is a completely vanilla javascript and html canvas outpainting convenience
|
|||
- [ ] lots and lots of readme updates (ongoing)
|
||||
- [ ] comment basically everything that isn't self documenting (ongoing)
|
||||
- [ ] CHORE: refactor all the duplicated JS code (ongoing, guaranteed to get worse before it gets better)
|
||||
- [x] overmask seam of img2img - BUG: it kinda sucks currently, just moves the seam instead of fixing it, i want to try to gradient-fade the edge but filter = 'blur(Ypx)' is _awful_ for this and my remedial per-pixel loops crash the browser because i am the embodiment of inefficiency
|
||||
- [x] overmask seam of img2img ~~BUG: it kinda sucks currently, just moves the seam instead of fixing it, i want to try to gradient-fade the edge but filter = 'blur(Ypx)' is awful for this and my remedial per-pixel loops crash the browser because i am the embodiment of inefficiency~~
|
||||
- [x] split out CSS to its own file (remedial cleanup task)
|
||||
- [x] ability to blank/new canvas without making the user refresh the page because that's pretty janky
|
||||
- [ ] add error handling for async/XHR POST in case of, yknow, errors
|
||||
|
@ -84,7 +85,7 @@ this is a completely vanilla javascript and html canvas outpainting convenience
|
|||
- [ ] discrete size control for mask and target reticle, discrete x/y axes for reticle
|
||||
- [x] ~~floating/togglable menu leftnav bar with categorized/sensibly laid-out options~~
|
||||
- [ ] infinite canvas
|
||||
- [ ] global undo/redo
|
||||
- [x] ~~global undo/redo~~
|
||||
- [ ] inpainting sketch tools
|
||||
- [ ] split out JS to separation-of-concerns individual files (oh no)
|
||||
- [x] ~~something actually similar to a "user interface", preferably visually pleasant and would make my mom say "well that makes sense" if she looked at it~~
|
||||
|
@ -138,12 +139,15 @@ imported a transparent clip of a [relatively famous happy lil kitty](https://com
|
|||
- 0.0.5.5 - highly attractive and functional floating control panel which will be extremely useful for infinite canvas [dac188d](https://github.com/zero01101/openOutpaint/commit/dac188dbfb086d3063f14b1a6a6a5b3add1aa5f5)
|
||||
- 0.0.5.6 - _FINALLY_ the sliders update their values in realtime, a nice overall start on cleaning up my mess [d9fb87a](https://github.com/zero01101/openOutpaint/commit/d9fb87acec6653f19a9dac7777bd866782303ebc)
|
||||
- 0.0.5.7 - the majestic return of mask erasing, removed unnecessary overmask toggle [a96fd11](https://github.com/zero01101/openOutpaint/commit/a96fd116d750e38ce8982104ae5e5c966746fdc4)
|
||||
- 0.0.6 - absolutely brilliant undo/redo system, logical and straightforward enough to the point where even i can understand what it's doing [25681b3](https://github.com/zero01101/openOutpaint/commit/25681b3a83bbd7a1d1b3e675f26f141692d77c79)
|
||||
- 0.0.6.1 - finally think i've got overmasking working better with a bit of "humanization" to the automated masks, please play around with it and see if it's any better or just sucks in general [8002772](https://github.com/zero01101/openOutpaint/commit/8002772ee6aa4b2f5b544af82cb6d545cf81368f)
|
||||
|
||||
## collaborator credits 👑
|
||||
- [@jasonmhead](https://github.com/jasonmhead) - [the most minimal launch script](https://github.com/zero01101/openOutpaint/pull/1)
|
||||
- [@Kalekki](https://github.com/Kalekki) - all SORTS of awesome goodness, legit pull request hero: [what i was calling "smart crop"](https://github.com/zero01101/openOutpaint/pull/2), [localstorage](https://github.com/zero01101/openOutpaint/pull/5), [right-click erase](https://github.com/zero01101/openOutpaint/pull/7), [delightful floating UI](https://github.com/zero01101/openOutpaint/pull/11), [mask erase fix](https://github.com/zero01101/openOutpaint/pull/17)
|
||||
- [@seijihariki](https://github.com/seijihariki) - realtime slider value updates, gracious code cleanup ([14](https://github.com/zero01101/openOutpaint/pull/14))
|
||||
- [@lifeh2o](https://www.reddit.com/user/lifeh2o/overview) - overmasking concept that is still driving me crazy getting it to work right ([a](https://www.reddit.com/r/StableDiffusion/comments/ywf8np/i_made_a_completely_local_offline_opensource/iwl6s06/),[b](https://www.reddit.com/r/StableDiffusion/comments/ys9lhq/kollai_an_infinite_multiuser_canvas_running_on/ivzygwk/?context=3))
|
||||
- [@seijihariki](https://github.com/seijihariki) - [realtime slider value updates, gracious code cleanup](https://github.com/zero01101/openOutpaint/pull/14), [blessed undo/redo](https://github.com/zero01101/openOutpaint/pull/21)
|
||||
- [@lifeh2o](https://www.reddit.com/user/lifeh2o/overview) - overmasking concept ~~that is still driving me crazy getting it to work right~~ ([a](https://www.reddit.com/r/StableDiffusion/comments/ywf8np/i_made_a_completely_local_offline_opensource/iwl6s06/),[b](https://www.reddit.com/r/StableDiffusion/comments/ys9lhq/kollai_an_infinite_multiuser_canvas_running_on/ivzygwk/?context=3)) [possible betterness?](https://github.com/zero01101/openOutpaint/commit/8002772ee6aa4b2f5b544af82cb6d545cf81368f)
|
||||
|
||||
|
||||
## what's with the fish?
|
||||
deep aquatic life is _fascinating_ so i went with something underwater for a default prompt which led to making an _"illustration of a bright orange fish, plain blue solid background"_ favicon which led to "ok then, fish is mascot"
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
<span id="snapY"></span><br />
|
||||
<label for="heldButton">Mouse button:</label>
|
||||
<span id="heldButton"></span><br />
|
||||
<span id="version">Alpha release v0.0.5.7</span>
|
||||
<span id="version">Alpha release v0.0.6.1</span>
|
||||
<br />
|
||||
<hr>
|
||||
</div>
|
||||
|
|
217
js/commands.js
217
js/commands.js
|
@ -2,131 +2,124 @@
|
|||
* Command pattern to allow for editing history
|
||||
*/
|
||||
const commands = {
|
||||
current: -1,
|
||||
history: [],
|
||||
undo(n = 1) {
|
||||
for (var i = 0; i < n && this.current > -1; i++) {
|
||||
this.history[this.current--].undo();
|
||||
}
|
||||
},
|
||||
redo(n = 1) {
|
||||
for (var i = 0; i < n && this.current + 1 < this.history.length; i++) {
|
||||
this.history[++this.current].redo();
|
||||
}
|
||||
},
|
||||
current: -1,
|
||||
history: [],
|
||||
undo(n = 1) {
|
||||
for (var i = 0; i < n && this.current > -1; i++) {
|
||||
this.history[this.current--].undo();
|
||||
}
|
||||
},
|
||||
redo(n = 1) {
|
||||
for (var i = 0; i < n && this.current + 1 < this.history.length; i++) {
|
||||
this.history[++this.current].redo();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* These are basic commands that can be done/undone
|
||||
*
|
||||
* They must contain a 'run' method that performs the action the first time,
|
||||
* a 'undo' method that undoes that action and a 'redo' method that does the
|
||||
* action again, but without requiring parameters. 'redo' is by default the
|
||||
* same as 'run'.
|
||||
*
|
||||
* The 'run' and 'redo' functions will receive a 'options' parameter which will be
|
||||
* forwarded directly to the operation, and a 'state' parameter that
|
||||
* can be used to store state for undoing things.
|
||||
*
|
||||
* The 'state' object will be passed to the 'undo' function as well.
|
||||
*/
|
||||
createCommand(name, run, undo, redo = run) {
|
||||
const command = function runWrapper(options) {
|
||||
// Create copy of options and state object
|
||||
const copy = {};
|
||||
Object.assign(copy, options);
|
||||
const state = {};
|
||||
/**
|
||||
* These are basic commands that can be done/undone
|
||||
*
|
||||
* They must contain a 'run' method that performs the action the first time,
|
||||
* a 'undo' method that undoes that action and a 'redo' method that does the
|
||||
* action again, but without requiring parameters. 'redo' is by default the
|
||||
* same as 'run'.
|
||||
*
|
||||
* The 'run' and 'redo' functions will receive a 'options' parameter which will be
|
||||
* forwarded directly to the operation, and a 'state' parameter that
|
||||
* can be used to store state for undoing things.
|
||||
*
|
||||
* The 'state' object will be passed to the 'undo' function as well.
|
||||
*/
|
||||
createCommand(name, run, undo, redo = run) {
|
||||
const command = function runWrapper(options) {
|
||||
// Create copy of options and state object
|
||||
const copy = {};
|
||||
Object.assign(copy, options);
|
||||
const state = {};
|
||||
|
||||
// Attempt to run command
|
||||
try {
|
||||
run(copy, state);
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
`Error while running command '${name}' with options:`
|
||||
);
|
||||
console.warn(copy);
|
||||
console.warn(e);
|
||||
return;
|
||||
}
|
||||
// Attempt to run command
|
||||
try {
|
||||
run(copy, state);
|
||||
} catch (e) {
|
||||
console.warn(`Error while running command '${name}' with options:`);
|
||||
console.warn(copy);
|
||||
console.warn(e);
|
||||
return;
|
||||
}
|
||||
|
||||
const undoWrapper = () => {
|
||||
console.debug(`Undoing ${name}, currently ${commands.current}`);
|
||||
undo(state);
|
||||
};
|
||||
const redoWrapper = () => {
|
||||
console.debug(`Redoing ${name}, currently ${commands.current}`);
|
||||
redo(copy, state);
|
||||
};
|
||||
const undoWrapper = () => {
|
||||
console.debug(`Undoing ${name}, currently ${commands.current}`);
|
||||
undo(state);
|
||||
};
|
||||
const redoWrapper = () => {
|
||||
console.debug(`Redoing ${name}, currently ${commands.current}`);
|
||||
redo(copy, state);
|
||||
};
|
||||
|
||||
// Add to history
|
||||
if (commands.history.length > commands.current + 1)
|
||||
commands.history.splice(commands.current + 1);
|
||||
commands.history.push({ undo: undoWrapper, redo: redoWrapper });
|
||||
commands.current++;
|
||||
};
|
||||
// Add to history
|
||||
if (commands.history.length > commands.current + 1)
|
||||
commands.history.splice(commands.current + 1);
|
||||
commands.history.push({undo: undoWrapper, redo: redoWrapper});
|
||||
commands.current++;
|
||||
};
|
||||
|
||||
this.types[name] = command;
|
||||
this.types[name] = command;
|
||||
|
||||
return command;
|
||||
},
|
||||
runCommand(name, options) {
|
||||
this.types[name](options);
|
||||
},
|
||||
types: {},
|
||||
return command;
|
||||
},
|
||||
runCommand(name, options) {
|
||||
this.types[name](options);
|
||||
},
|
||||
types: {},
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw Image Command, used to draw a Image to a context
|
||||
*/
|
||||
commands.createCommand(
|
||||
"drawImage",
|
||||
(options, state) => {
|
||||
if (
|
||||
!options ||
|
||||
options.image === undefined ||
|
||||
options.x === undefined ||
|
||||
options.y === undefined
|
||||
)
|
||||
throw "Command drawImage requires options in the format: {image, x, y, ctx?}";
|
||||
"drawImage",
|
||||
(options, state) => {
|
||||
if (
|
||||
!options ||
|
||||
options.image === undefined ||
|
||||
options.x === undefined ||
|
||||
options.y === undefined
|
||||
)
|
||||
throw "Command drawImage requires options in the format: {image, x, y, ctx?}";
|
||||
|
||||
// Check if we have state
|
||||
if (!state.context) {
|
||||
const context = options.ctx || imgCtx;
|
||||
state.context = context;
|
||||
// Check if we have state
|
||||
if (!state.context) {
|
||||
const context = options.ctx || imgCtx;
|
||||
state.context = context;
|
||||
|
||||
// Saving what was in the canvas before the command
|
||||
const imgData = context.getImageData(
|
||||
options.x,
|
||||
options.y,
|
||||
options.image.width,
|
||||
options.image.height
|
||||
);
|
||||
state.box = {
|
||||
x: options.x,
|
||||
y: options.y,
|
||||
w: options.image.width,
|
||||
h: options.image.height,
|
||||
};
|
||||
// Create Image
|
||||
const cutout = document.createElement("canvas");
|
||||
cutout.width = state.box.w;
|
||||
cutout.height = state.box.h;
|
||||
cutout.getContext("2d").putImageData(imgData, 0, 0);
|
||||
state.original = new Image();
|
||||
state.original.src = cutout.toDataURL();
|
||||
}
|
||||
// Saving what was in the canvas before the command
|
||||
const imgData = context.getImageData(
|
||||
options.x,
|
||||
options.y,
|
||||
options.image.width,
|
||||
options.image.height
|
||||
);
|
||||
state.box = {
|
||||
x: options.x,
|
||||
y: options.y,
|
||||
w: options.image.width,
|
||||
h: options.image.height,
|
||||
};
|
||||
// Create Image
|
||||
const cutout = document.createElement("canvas");
|
||||
cutout.width = state.box.w;
|
||||
cutout.height = state.box.h;
|
||||
cutout.getContext("2d").putImageData(imgData, 0, 0);
|
||||
state.original = new Image();
|
||||
state.original.src = cutout.toDataURL();
|
||||
}
|
||||
|
||||
// Apply command
|
||||
state.context.drawImage(options.image, state.box.x, state.box.y);
|
||||
},
|
||||
(state) => {
|
||||
// Clear destination area
|
||||
state.context.clearRect(
|
||||
state.box.x,
|
||||
state.box.y,
|
||||
state.box.w,
|
||||
state.box.h
|
||||
);
|
||||
// Undo
|
||||
state.context.drawImage(state.original, state.box.x, state.box.y);
|
||||
}
|
||||
// Apply command
|
||||
state.context.drawImage(options.image, state.box.x, state.box.y);
|
||||
},
|
||||
(state) => {
|
||||
// Clear destination area
|
||||
state.context.clearRect(state.box.x, state.box.y, state.box.w, state.box.h);
|
||||
// Undo
|
||||
state.context.drawImage(state.original, state.box.x, state.box.y);
|
||||
}
|
||||
);
|
||||
|
|
1697
js/index.js
1697
js/index.js
File diff suppressed because it is too large
Load diff
|
@ -1,48 +1,48 @@
|
|||
dragElement(document.getElementById("infoContainer"));
|
||||
|
||||
function dragElement(elmnt) {
|
||||
var p1 = 0,
|
||||
p2 = 0,
|
||||
p3 = 0,
|
||||
p4 = 0;
|
||||
var draggableElements = document.getElementsByClassName('draggable');
|
||||
for (var i = 0; i < draggableElements.length; i++) {
|
||||
draggableElements[i].onmousedown = dragMouseDown;
|
||||
}
|
||||
var p1 = 0,
|
||||
p2 = 0,
|
||||
p3 = 0,
|
||||
p4 = 0;
|
||||
var draggableElements = document.getElementsByClassName("draggable");
|
||||
for (var i = 0; i < draggableElements.length; i++) {
|
||||
draggableElements[i].onmousedown = dragMouseDown;
|
||||
}
|
||||
|
||||
function dragMouseDown(e) {
|
||||
e.preventDefault();
|
||||
p3 = e.clientX;
|
||||
p4 = e.clientY;
|
||||
document.onmouseup = closeDragElement;
|
||||
document.onmousemove = elementDrag;
|
||||
}
|
||||
function dragMouseDown(e) {
|
||||
e.preventDefault();
|
||||
p3 = e.clientX;
|
||||
p4 = e.clientY;
|
||||
document.onmouseup = closeDragElement;
|
||||
document.onmousemove = elementDrag;
|
||||
}
|
||||
|
||||
function elementDrag(e) {
|
||||
e.preventDefault();
|
||||
p1 = p3 - e.clientX;
|
||||
p2 = p4 - e.clientY;
|
||||
elmnt.style.top = (elmnt.offsetTop - (p4-e.clientY)) + "px";
|
||||
elmnt.style.left = (elmnt.offsetLeft - (p3-e.clientX)) + "px";
|
||||
p3 = e.clientX;
|
||||
p4 = e.clientY;
|
||||
}
|
||||
function elementDrag(e) {
|
||||
e.preventDefault();
|
||||
p1 = p3 - e.clientX;
|
||||
p2 = p4 - e.clientY;
|
||||
elmnt.style.top = elmnt.offsetTop - (p4 - e.clientY) + "px";
|
||||
elmnt.style.left = elmnt.offsetLeft - (p3 - e.clientX) + "px";
|
||||
p3 = e.clientX;
|
||||
p4 = e.clientY;
|
||||
}
|
||||
|
||||
function closeDragElement() {
|
||||
document.onmouseup = null;
|
||||
document.onmousemove = null;
|
||||
}
|
||||
function closeDragElement() {
|
||||
document.onmouseup = null;
|
||||
document.onmousemove = null;
|
||||
}
|
||||
}
|
||||
|
||||
var coll = document.getElementsByClassName("collapsible");
|
||||
for (var i = 0; i < coll.length; i++) {
|
||||
coll[i].addEventListener("click", function () {
|
||||
this.classList.toggle("active");
|
||||
var content = this.nextElementSibling;
|
||||
if (content.style.maxHeight) {
|
||||
content.style.maxHeight = null;
|
||||
} else {
|
||||
content.style.maxHeight = content.scrollHeight + "px";
|
||||
}
|
||||
});
|
||||
}
|
||||
coll[i].addEventListener("click", function () {
|
||||
this.classList.toggle("active");
|
||||
var content = this.nextElementSibling;
|
||||
if (content.style.maxHeight) {
|
||||
content.style.maxHeight = null;
|
||||
} else {
|
||||
content.style.maxHeight = content.scrollHeight + "px";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue