diff --git a/.prettierrc.json b/.prettierrc.json
new file mode 100644
index 0000000..c7dae26
--- /dev/null
+++ b/.prettierrc.json
@@ -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
+ }
\ No newline at end of file
diff --git a/README.md b/README.md
index 4069e6f..450c217 100644
--- a/README.md
+++ b/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"
diff --git a/index.html b/index.html
index a8e525c..bbaa0ec 100644
--- a/index.html
+++ b/index.html
@@ -109,7 +109,7 @@
- Alpha release v0.0.5.7
+ Alpha release v0.0.6.1
diff --git a/js/commands.js b/js/commands.js
index 5cf6854..f5594f8 100644
--- a/js/commands.js
+++ b/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);
+ }
);
diff --git a/js/index.js b/js/index.js
index 82d01ea..c939f30 100644
--- a/js/index.js
+++ b/js/index.js
@@ -4,84 +4,84 @@
window.onload = startup;
var stableDiffusionData = {
- //includes img2img data but works for txt2img just fine
- prompt: '',
- negative_prompt: '',
- seed: -1,
- cfg_scale: null,
- sampler_index: 'DDIM',
- steps: null,
- denoising_strength: 1,
- mask_blur: 0,
- batch_size: null,
- width: 512,
- height: 512,
- n_iter: null, // batch count
- mask: '',
- init_images: [],
- inpaint_full_res: false,
- inpainting_fill: 2,
- enable_hr: false,
- firstphase_width: 0,
- firstphase_height: 0,
- // here's some more fields that might be useful
+ //includes img2img data but works for txt2img just fine
+ prompt: "",
+ negative_prompt: "",
+ seed: -1,
+ cfg_scale: null,
+ sampler_index: "DDIM",
+ steps: null,
+ denoising_strength: 1,
+ mask_blur: 0,
+ batch_size: null,
+ width: 512,
+ height: 512,
+ n_iter: null, // batch count
+ mask: "",
+ init_images: [],
+ inpaint_full_res: false,
+ inpainting_fill: 2,
+ enable_hr: false,
+ firstphase_width: 0,
+ firstphase_height: 0,
+ // here's some more fields that might be useful
- // ---txt2img specific:
- // "enable_hr": false, // hires fix
- // "denoising_strength": 0, // ok this is in both txt and img2img but txt2img only applies it if enable_hr == true
- // "firstphase_width": 0, // hires fp w
- // "firstphase_height": 0, // see above s/w/h/
+ // ---txt2img specific:
+ // "enable_hr": false, // hires fix
+ // "denoising_strength": 0, // ok this is in both txt and img2img but txt2img only applies it if enable_hr == true
+ // "firstphase_width": 0, // hires fp w
+ // "firstphase_height": 0, // see above s/w/h/
- // ---img2img specific
- // "init_images": [ // imageS!??!? wtf maybe for batch img2img?? i just dump one base64 in here
- // "string"
- // ],
- // "resize_mode": 0,
- // "denoising_strength": 0.75, // yeah see
- // "mask": "string", // string is just a base64 image
- // "mask_blur": 4,
- // "inpainting_fill": 0, // 0- fill, 1- orig, 2- latent noise, 3- latent nothing
- // "inpaint_full_res": true,
- // "inpaint_full_res_padding": 0, // px
- // "inpainting_mask_invert": 0, // bool??????? wtf
- // "include_init_images": false // ??????
+ // ---img2img specific
+ // "init_images": [ // imageS!??!? wtf maybe for batch img2img?? i just dump one base64 in here
+ // "string"
+ // ],
+ // "resize_mode": 0,
+ // "denoising_strength": 0.75, // yeah see
+ // "mask": "string", // string is just a base64 image
+ // "mask_blur": 4,
+ // "inpainting_fill": 0, // 0- fill, 1- orig, 2- latent noise, 3- latent nothing
+ // "inpaint_full_res": true,
+ // "inpaint_full_res_padding": 0, // px
+ // "inpainting_mask_invert": 0, // bool??????? wtf
+ // "include_init_images": false // ??????
};
/**
* Some Utility Functions
*/
function sliderChangeHandlerFactory(
- sliderId,
- textBoxId,
- dataKey,
- defaultV,
- setter = (k, v) => (stableDiffusionData[k] = v),
- getter = (k) => stableDiffusionData[k]
+ sliderId,
+ textBoxId,
+ dataKey,
+ defaultV,
+ setter = (k, v) => (stableDiffusionData[k] = v),
+ getter = (k) => stableDiffusionData[k]
) {
- const sliderEl = document.getElementById(sliderId);
- const textBoxEl = document.getElementById(textBoxId);
- const savedValue = localStorage.getItem(dataKey);
+ const sliderEl = document.getElementById(sliderId);
+ const textBoxEl = document.getElementById(textBoxId);
+ const savedValue = localStorage.getItem(dataKey);
- if (savedValue) setter(dataKey, savedValue || defaultV);
+ if (savedValue) setter(dataKey, savedValue || defaultV);
- function changeHandler(evn) {
- const eventSource = evn && evn.srcElement;
- const value = eventSource && Number(eventSource.value);
+ function changeHandler(evn) {
+ const eventSource = evn && evn.srcElement;
+ const value = eventSource && Number(eventSource.value);
- if (value) setter(dataKey, value);
+ if (value) setter(dataKey, value);
- if (!eventSource || eventSource.id === textBoxId)
- sliderEl.value = getter(dataKey);
- setter(dataKey, Number(sliderEl.value));
- textBoxEl.value = getter(dataKey);
+ if (!eventSource || eventSource.id === textBoxId)
+ sliderEl.value = getter(dataKey);
+ setter(dataKey, Number(sliderEl.value));
+ textBoxEl.value = getter(dataKey);
- localStorage.setItem(dataKey, getter(dataKey));
- }
+ localStorage.setItem(dataKey, getter(dataKey));
+ }
- textBoxEl.onchange = changeHandler;
- sliderEl.oninput = changeHandler;
+ textBoxEl.onchange = changeHandler;
+ sliderEl.oninput = changeHandler;
- return changeHandler;
+ return changeHandler;
}
// stuff things use
@@ -89,9 +89,9 @@ var blockNewImages = false;
var returnedImages;
var imageIndex = 0;
var tmpImgXYWH = {};
-var host = '';
-var url = '/sdapi/v1/';
-var endpoint = 'txt2img';
+var host = "";
+var url = "/sdapi/v1/";
+var endpoint = "txt2img";
var frameX = 512;
var frameY = 512;
var prevMouseX = 0;
@@ -124,1019 +124,858 @@ var arbitraryImageBitmap;
var arbitraryImageBase64; // seriously js cmon work with me here
var placingArbitraryImage = false; // for when the user has loaded an existing image from their computer
var enableErasing = false; // accidental right-click erase if the user isn't trying to erase is a bad thing
+var marchOffset = 0;
+var marching = false;
+var marchCoords = {};
// info div, sometimes hidden
-let mouseXInfo = document.getElementById('mouseX');
-let mouseYInfo = document.getElementById('mouseY');
-let canvasXInfo = document.getElementById('canvasX');
-let canvasYInfo = document.getElementById('canvasY');
-let snapXInfo = document.getElementById('snapX');
-let snapYInfo = document.getElementById('snapY');
-let heldButtonInfo = document.getElementById('heldButton');
+let mouseXInfo = document.getElementById("mouseX");
+let mouseYInfo = document.getElementById("mouseY");
+let canvasXInfo = document.getElementById("canvasX");
+let canvasYInfo = document.getElementById("canvasY");
+let snapXInfo = document.getElementById("snapX");
+let snapYInfo = document.getElementById("snapY");
+let heldButtonInfo = document.getElementById("heldButton");
// canvases and related
-const ovCanvas = document.getElementById('overlayCanvas'); // where mouse cursor renders
-const ovCtx = ovCanvas.getContext('2d');
-const tgtCanvas = document.getElementById('targetCanvas'); // where "box" gets drawn before dream happens
-const tgtCtx = tgtCanvas.getContext('2d');
-const maskPaintCanvas = document.getElementById('maskPaintCanvas'); // where masking brush gets painted
-const maskPaintCtx = maskPaintCanvas.getContext('2d');
-const tempCanvas = document.getElementById('tempCanvas'); // where select/rejects get superimposed temporarily
-const tempCtx = tempCanvas.getContext('2d');
-const imgCanvas = document.getElementById('canvas'); // where dreams go
-const imgCtx = imgCanvas.getContext('2d');
-const bgCanvas = document.getElementById('backgroundCanvas'); // gray bg grid
-const bgCtx = bgCanvas.getContext('2d');
+const ovCanvas = document.getElementById("overlayCanvas"); // where mouse cursor renders
+const ovCtx = ovCanvas.getContext("2d");
+const tgtCanvas = document.getElementById("targetCanvas"); // where "box" gets drawn before dream happens
+const tgtCtx = tgtCanvas.getContext("2d");
+const maskPaintCanvas = document.getElementById("maskPaintCanvas"); // where masking brush gets painted
+const maskPaintCtx = maskPaintCanvas.getContext("2d");
+const tempCanvas = document.getElementById("tempCanvas"); // where select/rejects get superimposed temporarily
+const tempCtx = tempCanvas.getContext("2d");
+const imgCanvas = document.getElementById("canvas"); // where dreams go
+const imgCtx = imgCanvas.getContext("2d");
+const bgCanvas = document.getElementById("backgroundCanvas"); // gray bg grid
+const bgCtx = bgCanvas.getContext("2d");
function startup() {
- checkIfWebuiIsRunning();
- loadSettings();
- drawBackground();
- changeScaleFactor();
- changePaintMode();
- changeSampler();
- changeSteps();
- changeCfgScale();
- changeBatchCount();
- changeBatchSize();
- changeSnapMode();
- changeMaskBlur();
- changeSeed();
- changeOverMaskPx();
- changeHiResFix();
- changeEnableErasing();
- document.getElementById('overlayCanvas').onmousemove = mouseMove;
- document.getElementById('overlayCanvas').onmousedown = mouseDown;
- document.getElementById('overlayCanvas').onmouseup = mouseUp;
- document.getElementById('scaleFactor').value = scaleFactor;
+ checkIfWebuiIsRunning();
+ loadSettings();
+ drawBackground();
+ changeScaleFactor();
+ changePaintMode();
+ changeSampler();
+ changeSteps();
+ changeCfgScale();
+ changeBatchCount();
+ changeBatchSize();
+ changeSnapMode();
+ changeMaskBlur();
+ changeSeed();
+ changeOverMaskPx();
+ changeHiResFix();
+ changeEnableErasing();
+ document.getElementById("overlayCanvas").onmousemove = mouseMove;
+ document.getElementById("overlayCanvas").onmousedown = mouseDown;
+ document.getElementById("overlayCanvas").onmouseup = mouseUp;
+ document.getElementById("scaleFactor").value = scaleFactor;
}
function drop(imageParams) {
- const img = new Image();
- img.onload = function () {
- writeArbitraryImage(img, imageParams.x, imageParams.y);
- };
- img.src = arbitraryImageBase64;
+ const img = new Image();
+ img.onload = function () {
+ writeArbitraryImage(img, imageParams.x, imageParams.y);
+ };
+ img.src = arbitraryImageBase64;
}
function writeArbitraryImage(img, x, y) {
- commands.runCommand('drawImage', {
- x,
- y,
- image: img,
- });
- blockNewImages = false;
- placingArbitraryImage = false;
- document.getElementById('preloadImage').files = null;
+ commands.runCommand("drawImage", {
+ x,
+ y,
+ image: img,
+ });
+ blockNewImages = false;
+ placingArbitraryImage = false;
+ document.getElementById("preloadImage").files = null;
}
function dream(x, y, prompt) {
- tmpImgXYWH.x = x;
- tmpImgXYWH.y = y;
- tmpImgXYWH.w = prompt.width;
- tmpImgXYWH.h = prompt.height;
- console.log(
- 'dreaming to ' +
- host +
- url +
- endpoint +
- ':\r\n' +
- JSON.stringify(prompt)
- );
- postData(prompt).then((data) => {
- returnedImages = data.images;
- totalImagesReturned = data.images.length;
- blockNewImages = true;
- //console.log(data); // JSON data parsed by `data.json()` call
- imageAcceptReject(x, y, data);
- });
+ tmpImgXYWH.x = x;
+ tmpImgXYWH.y = y;
+ tmpImgXYWH.w = prompt.width;
+ tmpImgXYWH.h = prompt.height;
+ console.log(
+ "dreaming to " + host + url + endpoint + ":\r\n" + JSON.stringify(prompt)
+ );
+ postData(prompt).then((data) => {
+ returnedImages = data.images;
+ totalImagesReturned = data.images.length;
+ blockNewImages = true;
+ //console.log(data); // JSON data parsed by `data.json()` call
+ imageAcceptReject(x, y, data);
+ });
}
async function postData(promptData) {
- this.host = document.getElementById('host').value;
- // Default options are marked with *
- const response = await fetch(this.host + this.url + this.endpoint, {
- method: 'POST', // *GET, POST, PUT, DELETE, etc.
- mode: 'cors', // no-cors, *cors, same-origin
- cache: 'default', // *default, no-cache, reload, force-cache, only-if-cached
- credentials: 'same-origin', // include, *same-origin, omit
- headers: {
- Accept: 'application/json',
- 'Content-Type': 'application/json',
- },
- redirect: 'follow', // manual, *follow, error
- referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
- body: JSON.stringify(promptData), // body data type must match "Content-Type" header
- });
- return response.json(); // parses JSON response into native JavaScript objects
+ this.host = document.getElementById("host").value;
+ // Default options are marked with *
+ const response = await fetch(this.host + this.url + this.endpoint, {
+ method: "POST", // *GET, POST, PUT, DELETE, etc.
+ mode: "cors", // no-cors, *cors, same-origin
+ cache: "default", // *default, no-cache, reload, force-cache, only-if-cached
+ credentials: "same-origin", // include, *same-origin, omit
+ headers: {
+ Accept: "application/json",
+ "Content-Type": "application/json",
+ },
+ redirect: "follow", // manual, *follow, error
+ referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
+ body: JSON.stringify(promptData), // body data type must match "Content-Type" header
+ });
+ return response.json(); // parses JSON response into native JavaScript objects
}
function imageAcceptReject(x, y, data) {
- const img = new Image();
- img.onload = function () {
- tempCtx.drawImage(img, x, y); //imgCtx for actual image, tmp for... holding?
- var div = document.createElement('div');
- div.id = 'veryTempDiv';
- div.style.position = 'absolute';
- div.style.left = parseInt(x) + 'px';
- div.style.top = parseInt(y + data.parameters.height) + 'px';
- div.style.width = '150px';
- div.style.height = '50px';
- div.innerHTML =
- ' of ';
- document.getElementById('tempDiv').appendChild(div);
- document.getElementById('currentImgIndex').innerText = '1';
- document.getElementById('totalImgIndex').innerText =
- totalImagesReturned;
- };
- // set the image displayed as the first regardless of batch size/count
- imageIndex = 0;
- // load the image data after defining the closure
- img.src = 'data:image/png;base64,' + returnedImages[imageIndex];
+ const img = new Image();
+ img.onload = function () {
+ tempCtx.drawImage(img, x, y); //imgCtx for actual image, tmp for... holding?
+ var div = document.createElement("div");
+ div.id = "veryTempDiv";
+ div.style.position = "absolute";
+ div.style.left = parseInt(x) + "px";
+ div.style.top = parseInt(y + data.parameters.height) + "px";
+ div.style.width = "150px";
+ div.style.height = "50px";
+ div.innerHTML =
+ ' of ';
+ document.getElementById("tempDiv").appendChild(div);
+ document.getElementById("currentImgIndex").innerText = "1";
+ document.getElementById("totalImgIndex").innerText = totalImagesReturned;
+ };
+ // set the image displayed as the first regardless of batch size/count
+ imageIndex = 0;
+ // load the image data after defining the closure
+ img.src = "data:image/png;base64," + returnedImages[imageIndex];
}
function accept(evt) {
- // write image to imgcanvas
- clearBackupMask();
- placeImage();
- removeChoiceButtons();
- clearTargetMask();
- blockNewImages = false;
+ // write image to imgcanvas
+ marching = false;
+ clearBackupMask();
+ placeImage();
+ removeChoiceButtons();
+ clearTargetMask();
+ blockNewImages = false;
}
function reject(evt) {
- // remove image entirely
- restoreBackupMask();
- clearBackupMask();
- clearTargetMask();
- removeChoiceButtons();
- blockNewImages = false;
+ // remove image entirely
+ marching = false;
+ restoreBackupMask();
+ clearBackupMask();
+ clearTargetMask();
+ removeChoiceButtons();
+ blockNewImages = false;
}
function newImage(evt) {
- clearPaintedMask();
- clearBackupMask();
- clearTargetMask();
- clearImgMask();
+ clearPaintedMask();
+ clearBackupMask();
+ clearTargetMask();
+ clearImgMask();
}
function prevImg(evt) {
- if (imageIndex == 0) {
- imageIndex = totalImagesReturned;
- }
- changeImg(false);
+ if (imageIndex == 0) {
+ imageIndex = totalImagesReturned;
+ }
+ changeImg(false);
}
function nextImg(evt) {
- if (imageIndex == totalImagesReturned - 1) {
- imageIndex = -1;
- }
- changeImg(true);
+ if (imageIndex == totalImagesReturned - 1) {
+ imageIndex = -1;
+ }
+ changeImg(true);
}
function changeImg(forward) {
- const img = new Image();
- tempCtx.clearRect(0, 0, tempCtx.width, tempCtx.height);
- img.onload = function () {
- tempCtx.drawImage(img, tmpImgXYWH.x, tmpImgXYWH.y); //imgCtx for actual image, tmp for... holding?
- };
- var tmpIndex = document.getElementById('currentImgIndex');
- if (forward) {
- imageIndex++;
- } else {
- imageIndex--;
- }
- tmpIndex.innerText = imageIndex + 1;
- // load the image data after defining the closure
- img.src = 'data:image/png;base64,' + returnedImages[imageIndex]; //TODO need way to dream batches and select from results
+ const img = new Image();
+ tempCtx.clearRect(0, 0, tempCtx.width, tempCtx.height);
+ img.onload = function () {
+ tempCtx.drawImage(img, tmpImgXYWH.x, tmpImgXYWH.y); //imgCtx for actual image, tmp for... holding?
+ };
+ var tmpIndex = document.getElementById("currentImgIndex");
+ if (forward) {
+ imageIndex++;
+ } else {
+ imageIndex--;
+ }
+ tmpIndex.innerText = imageIndex + 1;
+ // load the image data after defining the closure
+ img.src = "data:image/png;base64," + returnedImages[imageIndex]; //TODO need way to dream batches and select from results
}
function removeChoiceButtons(evt) {
- const element = document.getElementById('veryTempDiv');
- element.remove();
- tempCtx.clearRect(0, 0, tempCanvas.width, tempCanvas.height);
+ const element = document.getElementById("veryTempDiv");
+ element.remove();
+ tempCtx.clearRect(0, 0, tempCanvas.width, tempCanvas.height);
}
function restoreBackupMask() {
- // reapply mask if exists
- if (backupMaskChunk != null && backupMaskX != null && backupMaskY != null) {
- // backup mask data exists
- var iData = new ImageData(
- backupMaskChunk.data,
- backupMaskChunk.height,
- backupMaskChunk.width
- );
- maskPaintCtx.putImageData(iData, backupMaskX, backupMaskY);
- }
+ // reapply mask if exists
+ if (backupMaskChunk != null && backupMaskX != null && backupMaskY != null) {
+ // backup mask data exists
+ var iData = new ImageData(
+ backupMaskChunk.data,
+ backupMaskChunk.height,
+ backupMaskChunk.width
+ );
+ maskPaintCtx.putImageData(iData, backupMaskX, backupMaskY);
+ }
}
function clearBackupMask() {
- // clear backupmask
- backupMaskChunk = null;
- backupMaskX = null;
- backupMaskY = null;
+ // clear backupmask
+ backupMaskChunk = null;
+ backupMaskX = null;
+ backupMaskY = null;
}
function clearTargetMask() {
- tgtCtx.clearRect(0, 0, tgtCanvas.width, tgtCanvas.height);
+ tgtCtx.clearRect(0, 0, tgtCanvas.width, tgtCanvas.height);
}
function clearImgMask() {
- imgCtx.clearRect(0, 0, imgCanvas.width, imgCanvas.height);
+ imgCtx.clearRect(0, 0, imgCanvas.width, imgCanvas.height);
}
function clearPaintedMask() {
- maskPaintCtx.clearRect(0, 0, maskPaintCanvas.width, maskPaintCanvas.height);
+ maskPaintCtx.clearRect(0, 0, maskPaintCanvas.width, maskPaintCanvas.height);
}
function placeImage() {
- const img = new Image();
- img.onload = function () {
- commands.runCommand('drawImage', {
- x: tmpImgXYWH.x,
- y: tmpImgXYWH.y,
- image: img,
- });
- tmpImgXYWH = {};
- returnedImages = null;
- };
- // load the image data after defining the closure
- img.src = 'data:image/png;base64,' + returnedImages[imageIndex];
+ const img = new Image();
+ img.onload = function () {
+ commands.runCommand("drawImage", {
+ x: tmpImgXYWH.x,
+ y: tmpImgXYWH.y,
+ image: img,
+ });
+ tmpImgXYWH = {};
+ returnedImages = null;
+ };
+ // load the image data after defining the closure
+ img.src = "data:image/png;base64," + returnedImages[imageIndex];
}
function sleep(ms) {
- // what was this even for, anyway?
- return new Promise((resolve) => setTimeout(resolve, ms));
+ // what was this even for, anyway?
+ return new Promise((resolve) => setTimeout(resolve, ms));
}
function snap(i, scaled = true) {
- // very cheap test proof of concept but it works surprisingly well
- var scaleOffset = 0;
- if (scaled) {
- if (scaleFactor % 2 != 0) {
- // odd number, snaps to center of cell, oops
- scaleOffset = basePixelCount / 2;
- }
- }
- var snapOffset = (i % basePixelCount) - scaleOffset;
- if (snapOffset == 0) {
- return snapOffset;
- }
- return -snapOffset;
+ // very cheap test proof of concept but it works surprisingly well
+ var scaleOffset = 0;
+ if (scaled) {
+ if (scaleFactor % 2 != 0) {
+ // odd number, snaps to center of cell, oops
+ scaleOffset = basePixelCount / 2;
+ }
+ }
+ var snapOffset = (i % basePixelCount) - scaleOffset;
+ if (snapOffset == 0) {
+ return snapOffset;
+ }
+ return -snapOffset;
+}
+
+function march() {
+ if (marching) {
+ marchOffset++;
+ if (marchOffset > 16) {
+ marchOffset = 0;
+ }
+ drawMarchingAnts();
+ setTimeout(march, 20);
+ }
+}
+
+function drawMarchingAnts() {
+ clearTargetMask();
+ tgtCtx.strokeStyle = "#333333FF"; //"#55000077";
+ tgtCtx.setLineDash([4, 2]);
+ tgtCtx.lineDashOffset = -marchOffset;
+ tgtCtx.strokeRect(marchCoords.x, marchCoords.y, marchCoords.w, marchCoords.h);
}
function mouseMove(evt) {
- const rect = ovCanvas.getBoundingClientRect(); // not-quite pixel offset was driving me insane
- const canvasOffsetX = rect.left;
- const canvasOffsetY = rect.top;
- heldButton = evt.buttons;
- mouseXInfo.innerText = mouseX = evt.clientX;
- mouseYInfo.innerText = mouseY = evt.clientY;
- canvasXInfo.innerText = canvasX = parseInt(evt.clientX - rect.left);
- canvasYInfo.innerText = canvasY = parseInt(evt.clientY - rect.top);
- snapXInfo.innerText = canvasX + snap(canvasX);
- snapYInfo.innerText = canvasY + snap(canvasY);
- heldButtonInfo.innerText = heldButton;
- ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height); // clear out the previous mouse cursor
- if (placingArbitraryImage) {
- // ugh refactor so this isn't duplicated between arbitrary image and dream reticle modes
- snapOffsetX = 0;
- snapOffsetY = 0;
- if (snapToGrid) {
- snapOffsetX = snap(canvasX, false);
- snapOffsetY = snap(canvasY, false);
- }
- finalX = snapOffsetX + canvasX;
- finalY = snapOffsetY + canvasY;
- ovCtx.drawImage(arbitraryImage, finalX, finalY);
- } else if (!paintMode) {
- // draw targeting square reticle thingy cursor
- ovCtx.strokeStyle = '#00000077';
- snapOffsetX = 0;
- snapOffsetY = 0;
- if (snapToGrid) {
- snapOffsetX = snap(canvasX);
- snapOffsetY = snap(canvasY);
- }
- finalX = snapOffsetX + canvasX;
- finalY = snapOffsetY + canvasY;
- ovCtx.strokeRect(
- parseInt(finalX - (basePixelCount * scaleFactor) / 2),
- parseInt(finalY - (basePixelCount * scaleFactor) / 2),
- basePixelCount * scaleFactor,
- basePixelCount * scaleFactor
- ); //origin is middle of the frame
- }
+ const rect = ovCanvas.getBoundingClientRect(); // not-quite pixel offset was driving me insane
+ const canvasOffsetX = rect.left;
+ const canvasOffsetY = rect.top;
+ heldButton = evt.buttons;
+ mouseXInfo.innerText = mouseX = evt.clientX;
+ mouseYInfo.innerText = mouseY = evt.clientY;
+ canvasXInfo.innerText = canvasX = parseInt(evt.clientX - rect.left);
+ canvasYInfo.innerText = canvasY = parseInt(evt.clientY - rect.top);
+ snapXInfo.innerText = canvasX + snap(canvasX);
+ snapYInfo.innerText = canvasY + snap(canvasY);
+ heldButtonInfo.innerText = heldButton;
+ ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height); // clear out the previous mouse cursor
+ if (placingArbitraryImage) {
+ // ugh refactor so this isn't duplicated between arbitrary image and dream reticle modes
+ snapOffsetX = 0;
+ snapOffsetY = 0;
+ if (snapToGrid) {
+ snapOffsetX = snap(canvasX, false);
+ snapOffsetY = snap(canvasY, false);
+ }
+ finalX = snapOffsetX + canvasX;
+ finalY = snapOffsetY + canvasY;
+ ovCtx.drawImage(arbitraryImage, finalX, finalY);
+ } else if (!paintMode) {
+ // draw targeting square reticle thingy cursor
+ ovCtx.strokeStyle = "#00000077";
+ snapOffsetX = 0;
+ snapOffsetY = 0;
+ if (snapToGrid) {
+ snapOffsetX = snap(canvasX);
+ snapOffsetY = snap(canvasY);
+ }
+ finalX = snapOffsetX + canvasX;
+ finalY = snapOffsetY + canvasY;
+ ovCtx.strokeRect(
+ parseInt(finalX - (basePixelCount * scaleFactor) / 2),
+ parseInt(finalY - (basePixelCount * scaleFactor) / 2),
+ basePixelCount * scaleFactor,
+ basePixelCount * scaleFactor
+ ); //origin is middle of the frame
+ }
}
/**
* Mask implementation
*/
mouse.listen.canvas.onmousemove.on((evn) => {
- if (paintMode && evn.target.id === 'overlayCanvas') {
- // draw big translucent red blob cursor
- ovCtx.beginPath();
- ovCtx.arc(evn.x, evn.y, 4 * scaleFactor, 0, 2 * Math.PI, true); // for some reason 4x on an arc is === to 8x on a line???
- ovCtx.fillStyle = '#FF6A6A50';
- ovCtx.fill();
- }
+ if (paintMode && evn.target.id === "overlayCanvas") {
+ // draw big translucent red blob cursor
+ ovCtx.beginPath();
+ ovCtx.arc(evn.x, evn.y, 4 * scaleFactor, 0, 2 * Math.PI, true); // for some reason 4x on an arc is === to 8x on a line???
+ ovCtx.fillStyle = "#FF6A6A50";
+ ovCtx.fill();
+ }
});
mouse.listen.canvas.left.onpaint.on((evn) => {
- if (paintMode && evn.target.id === 'overlayCanvas') {
- maskPaintCtx.globalCompositeOperation = 'source-over';
- maskPaintCtx.strokeStyle = '#FF6A6A';
+ if (paintMode && evn.target.id === "overlayCanvas") {
+ maskPaintCtx.globalCompositeOperation = "source-over";
+ maskPaintCtx.strokeStyle = "#FF6A6A";
- maskPaintCtx.lineWidth = 8 * scaleFactor;
- maskPaintCtx.beginPath();
- maskPaintCtx.moveTo(evn.px, evn.py);
- maskPaintCtx.lineTo(evn.x, evn.y);
- maskPaintCtx.lineJoin = maskPaintCtx.lineCap = 'round';
- maskPaintCtx.stroke();
- }
+ maskPaintCtx.lineWidth = 8 * scaleFactor;
+ maskPaintCtx.beginPath();
+ maskPaintCtx.moveTo(evn.px, evn.py);
+ maskPaintCtx.lineTo(evn.x, evn.y);
+ maskPaintCtx.lineJoin = maskPaintCtx.lineCap = "round";
+ maskPaintCtx.stroke();
+ }
});
mouse.listen.canvas.right.onpaint.on((evn) => {
- if (paintMode && evn.target.id === 'overlayCanvas') {
- maskPaintCtx.globalCompositeOperation = 'destination-out';
- maskPaintCtx.strokeStyle = '#FFFFFFFF';
+ if (paintMode && evn.target.id === "overlayCanvas") {
+ maskPaintCtx.globalCompositeOperation = "destination-out";
+ maskPaintCtx.strokeStyle = "#FFFFFFFF";
- maskPaintCtx.lineWidth = 8 * scaleFactor;
- maskPaintCtx.beginPath();
- maskPaintCtx.moveTo(evn.px, evn.py);
- maskPaintCtx.lineTo(evn.x, evn.y);
- maskPaintCtx.lineJoin = maskPaintCtx.lineCap = 'round';
- maskPaintCtx.stroke();
- }
+ maskPaintCtx.lineWidth = 8 * scaleFactor;
+ maskPaintCtx.beginPath();
+ maskPaintCtx.moveTo(evn.px, evn.py);
+ maskPaintCtx.lineTo(evn.x, evn.y);
+ maskPaintCtx.lineJoin = maskPaintCtx.lineCap = "round";
+ maskPaintCtx.stroke();
+ }
});
function mouseDown(evt) {
- const rect = ovCanvas.getBoundingClientRect();
- var oddOffset = 0;
- if (scaleFactor % 2 != 0) {
- oddOffset = basePixelCount / 2;
- }
- if (evt.button == 0) {
- // left click
- if (placingArbitraryImage) {
- var nextBox = {};
- nextBox.x = evt.offsetX;
- nextBox.y = evt.offsetY;
- nextBox.w = arbitraryImageData.width;
- nextBox.h = arbitraryImageData.height;
- dropTargets.push(nextBox);
- } else if (!paintMode) {
- //const rect = ovCanvas.getBoundingClientRect()
- var nextBox = {};
- nextBox.x =
- evt.clientX -
- (basePixelCount * scaleFactor) / 2 -
- rect.left +
- oddOffset; //origin is middle of the frame
- nextBox.y =
- evt.clientY -
- (basePixelCount * scaleFactor) / 2 -
- rect.top +
- oddOffset; //TODO make a way to set the origin to numpad dirs?
- nextBox.w = basePixelCount * scaleFactor;
- nextBox.h = basePixelCount * scaleFactor;
- drawTargets.push(nextBox);
- }
- } else if (evt.button == 2) {
- if (enableErasing && !paintMode) {
- // right click, also gotta make sure mask blob isn't being used as it's visually inconsistent with behavior of erased region
- ctx = imgCanvas.getContext('2d');
- if (snapToGrid) {
- ctx.clearRect(
- canvasX +
- snap(canvasX) -
- (basePixelCount * scaleFactor) / 2,
- canvasY +
- snap(canvasY) -
- (basePixelCount * scaleFactor) / 2,
- basePixelCount * scaleFactor,
- basePixelCount * scaleFactor
- );
- } else {
- ctx.clearRect(
- canvasX - (basePixelCount * scaleFactor) / 2,
- canvasY - (basePixelCount * scaleFactor) / 2,
- basePixelCount * scaleFactor,
- basePixelCount * scaleFactor
- );
- }
- }
- }
+ const rect = ovCanvas.getBoundingClientRect();
+ var oddOffset = 0;
+ if (scaleFactor % 2 != 0) {
+ oddOffset = basePixelCount / 2;
+ }
+ if (evt.button == 0) {
+ // left click
+ if (placingArbitraryImage) {
+ var nextBox = {};
+ nextBox.x = evt.offsetX;
+ nextBox.y = evt.offsetY;
+ nextBox.w = arbitraryImageData.width;
+ nextBox.h = arbitraryImageData.height;
+ dropTargets.push(nextBox);
+ } else if (!paintMode) {
+ //const rect = ovCanvas.getBoundingClientRect()
+ var nextBox = {};
+ nextBox.x =
+ evt.clientX -
+ (basePixelCount * scaleFactor) / 2 -
+ rect.left +
+ oddOffset; //origin is middle of the frame
+ nextBox.y =
+ evt.clientY - (basePixelCount * scaleFactor) / 2 - rect.top + oddOffset; //TODO make a way to set the origin to numpad dirs?
+ nextBox.w = basePixelCount * scaleFactor;
+ nextBox.h = basePixelCount * scaleFactor;
+ drawTargets.push(nextBox);
+ }
+ } else if (evt.button == 2) {
+ if (enableErasing && !paintMode) {
+ // right click, also gotta make sure mask blob isn't being used as it's visually inconsistent with behavior of erased region
+ ctx = imgCanvas.getContext("2d");
+ if (snapToGrid) {
+ ctx.clearRect(
+ canvasX + snap(canvasX) - (basePixelCount * scaleFactor) / 2,
+ canvasY + snap(canvasY) - (basePixelCount * scaleFactor) / 2,
+ basePixelCount * scaleFactor,
+ basePixelCount * scaleFactor
+ );
+ } else {
+ ctx.clearRect(
+ canvasX - (basePixelCount * scaleFactor) / 2,
+ canvasY - (basePixelCount * scaleFactor) / 2,
+ basePixelCount * scaleFactor,
+ basePixelCount * scaleFactor
+ );
+ }
+ }
+ }
}
function mouseUp(evt) {
- if (evt.button == 0) {
- // left click
- if (placingArbitraryImage) {
- // jeez i REALLY need to refactor tons of this to not be duplicated all over, that's definitely my next chore after figuring out that razza frazza overmask fade
- var target = dropTargets[dropTargets.length - 1]; //get the last one... why am i storing all of them?
- snapOffsetX = 0;
- snapOffsetY = 0;
- if (snapToGrid) {
- snapOffsetX = snap(target.x, false);
- snapOffsetY = snap(target.y, false);
- }
- finalX = snapOffsetX + target.x;
- finalY = snapOffsetY + target.y;
+ if (evt.button == 0) {
+ // left click
+ if (placingArbitraryImage) {
+ // jeez i REALLY need to refactor tons of this to not be duplicated all over, that's definitely my next chore after figuring out that razza frazza overmask fade
+ var target = dropTargets[dropTargets.length - 1]; //get the last one... why am i storing all of them?
+ snapOffsetX = 0;
+ snapOffsetY = 0;
+ if (snapToGrid) {
+ snapOffsetX = snap(target.x, false);
+ snapOffsetY = snap(target.y, false);
+ }
+ finalX = snapOffsetX + target.x;
+ finalY = snapOffsetY + target.y;
- drawThis.x = finalX;
- drawThis.y = finalY;
- drawThis.w = target.w;
- drawThis.h = target.h;
- drawIt = drawThis; // i still think this is really stupid and redundant and unnecessary and redundant
- drop(drawIt);
- } else if (paintMode) {
- clicked = false;
- return;
- } else {
- if (!blockNewImages) {
- //TODO seriously, refactor this
- blockNewImages = true;
- clearTargetMask();
- tgtCtx.strokeStyle = '#55000077';
- var drawIt = {}; //why am i doing this????
- var target = drawTargets[drawTargets.length - 1]; //get the last one... why am i storing all of them?
- var oddOffset = 0;
- if (scaleFactor % 2 != 0) {
- oddOffset = basePixelCount / 2;
- }
- snapOffsetX = 0;
- snapOffsetY = 0;
- if (snapToGrid) {
- snapOffsetX = snap(target.x);
- snapOffsetY = snap(target.y);
- }
- finalX = snapOffsetX + target.x - oddOffset;
- finalY = snapOffsetY + target.y - oddOffset;
+ drawThis.x = finalX;
+ drawThis.y = finalY;
+ drawThis.w = target.w;
+ drawThis.h = target.h;
+ drawIt = drawThis; // i still think this is really stupid and redundant and unnecessary and redundant
+ drop(drawIt);
+ } else if (paintMode) {
+ clicked = false;
+ return;
+ } else {
+ if (!blockNewImages) {
+ //TODO seriously, refactor this
+ blockNewImages = true;
+ marching = true;
+ var drawIt = {}; //why am i doing this????
+ var target = drawTargets[drawTargets.length - 1]; //get the last one... why am i storing all of them?
+ var oddOffset = 0;
+ if (scaleFactor % 2 != 0) {
+ oddOffset = basePixelCount / 2;
+ }
+ snapOffsetX = 0;
+ snapOffsetY = 0;
+ if (snapToGrid) {
+ snapOffsetX = snap(target.x);
+ snapOffsetY = snap(target.y);
+ }
+ finalX = snapOffsetX + target.x - oddOffset;
+ finalY = snapOffsetY + target.y - oddOffset;
- drawThis.x = finalX;
- drawThis.y = finalY;
- drawThis.w = target.w;
- drawThis.h = target.h;
- tgtCtx.strokeRect(finalX, finalY, target.w, target.h);
- drawIt = drawThis; //TODO this is WRONG but also explicitly only draws the last image ... i think
- //check if there's image data already there
- // console.log(downX + ":" + downY + " :: " + this.isCanvasBlank(downX, downY));
- if (
- !isCanvasBlank(
- drawIt.x,
- drawIt.y,
- drawIt.w,
- drawIt.h,
- imgCanvas
- )
- ) {
- // image exists, set up for img2img
- var mainCanvasCtx = document
- .getElementById('canvas')
- .getContext('2d');
- const imgChunk = mainCanvasCtx.getImageData(
- drawIt.x,
- drawIt.y,
- drawIt.w,
- drawIt.h
- ); // imagedata object of the image being outpainted
- const imgChunkData = imgChunk.data; // imagedata.data object, a big inconvenient uint8clampedarray
- // these are the 3 mask monitors on the bottom of the page
- var initImgCanvas = document.getElementById(
- 'initImgCanvasMonitor'
- );
- var overMaskCanvas = document.getElementById(
- 'overMaskCanvasMonitor'
- );
- overMaskCanvas.width = initImgCanvas.width = target.w; //maskCanvas.width = target.w;
- overMaskCanvas.height = initImgCanvas.height = target.h; //maskCanvas.height = target.h;
- var initImgCanvasCtx = initImgCanvas.getContext('2d');
- var overMaskCanvasCtx = overMaskCanvas.getContext('2d');
- // get blank pixels to use as mask
- const initImgData = mainCanvasCtx.createImageData(
- drawIt.w,
- drawIt.h
- );
- const overMaskImgData = overMaskCanvasCtx.createImageData(
- drawIt.w,
- drawIt.h
- );
- // cover entire masks in black before adding masked areas
+ drawThis.x = marchCoords.x = finalX;
+ drawThis.y = marchCoords.y = finalY;
+ drawThis.w = marchCoords.w = target.w;
+ drawThis.h = marchCoords.h = target.h;
+ march(finalX, finalY, target.w, target.h);
+ drawIt = drawThis; //TODO this is WRONG but also explicitly only draws the last image ... i think
+ //check if there's image data already there
+ // console.log(downX + ":" + downY + " :: " + this.isCanvasBlank(downX, downY));
+ if (!isCanvasBlank(drawIt.x, drawIt.y, drawIt.w, drawIt.h, imgCanvas)) {
+ // image exists, set up for img2img
+ var mainCanvasCtx = document
+ .getElementById("canvas")
+ .getContext("2d");
+ const imgChunk = mainCanvasCtx.getImageData(
+ drawIt.x,
+ drawIt.y,
+ drawIt.w,
+ drawIt.h
+ ); // imagedata object of the image being outpainted
+ const imgChunkData = imgChunk.data; // imagedata.data object, a big inconvenient uint8clampedarray
+ // these are the 3 mask monitors on the bottom of the page
+ var initImgCanvas = document.getElementById("initImgCanvasMonitor");
+ var overMaskCanvas = document.getElementById("overMaskCanvasMonitor");
+ overMaskCanvas.width = initImgCanvas.width = target.w; //maskCanvas.width = target.w;
+ overMaskCanvas.height = initImgCanvas.height = target.h; //maskCanvas.height = target.h;
+ var initImgCanvasCtx = initImgCanvas.getContext("2d");
+ var overMaskCanvasCtx = overMaskCanvas.getContext("2d");
+ // get blank pixels to use as mask
+ const initImgData = mainCanvasCtx.createImageData(drawIt.w, drawIt.h);
+ let overMaskImgData = overMaskCanvasCtx.createImageData(
+ drawIt.w,
+ drawIt.h
+ );
+ // cover entire masks in black before adding masked areas
- for (let i = 0; i < imgChunkData.length; i += 4) {
- // l->r, top->bottom, R G B A pixel values in a big ol array
- // make a simple mask
- if (imgChunkData[i + 3] == 0) {
- // rgba pixel values, 4th one is alpha, if it's 0 there's "nothing there" in the image display canvas and its time to outpaint
- overMaskImgData.data[i] = 255; // white mask gets painted over
- overMaskImgData.data[i + 1] = 255;
- overMaskImgData.data[i + 2] = 255;
- overMaskImgData.data[i + 3] = 255;
+ for (let i = 0; i < imgChunkData.length; i += 4) {
+ // l->r, top->bottom, R G B A pixel values in a big ol array
+ // make a simple mask
+ if (imgChunkData[i + 3] == 0) {
+ // rgba pixel values, 4th one is alpha, if it's 0 there's "nothing there" in the image display canvas and its time to outpaint
+ overMaskImgData.data[i] = 255; // white mask gets painted over
+ overMaskImgData.data[i + 1] = 255;
+ overMaskImgData.data[i + 2] = 255;
+ overMaskImgData.data[i + 3] = 255;
- initImgData.data[i] = 0; // null area on initial image becomes opaque black pixels
- initImgData.data[i + 1] = 0;
- initImgData.data[i + 2] = 0;
- initImgData.data[i + 3] = 255;
- } else {
- // leave these pixels alone
- overMaskImgData.data[i] = 0; // black mask gets ignored for in/outpainting
- overMaskImgData.data[i + 1] = 0;
- overMaskImgData.data[i + 2] = 0;
- overMaskImgData.data[i + 3] = 255; // but it still needs an opaque alpha channel
+ initImgData.data[i] = 0; // null area on initial image becomes opaque black pixels
+ initImgData.data[i + 1] = 0;
+ initImgData.data[i + 2] = 0;
+ initImgData.data[i + 3] = 255;
+ } else {
+ // leave these pixels alone
+ overMaskImgData.data[i] = 0; // black mask gets ignored for in/outpainting
+ overMaskImgData.data[i + 1] = 0;
+ overMaskImgData.data[i + 2] = 0;
+ overMaskImgData.data[i + 3] = 255; // but it still needs an opaque alpha channel
- initImgData.data[i] = imgChunkData[i]; // put the original picture back in the painted area
- initImgData.data[i + 1] = imgChunkData[i + 1];
- initImgData.data[i + 2] = imgChunkData[i + 2];
- initImgData.data[i + 3] = imgChunkData[i + 3]; //it's still RGBA so we can handily do this in nice chunks'o'4
- }
- }
- // make a list of all the white pixels to expand so we don't waste time on non-mask pixels
- let pix = { x: [], y: [], index: [] };
- var x, y, index;
- for (y = 0; y < drawIt.h; y++) {
- for (x = 0; x < drawIt.w; x++) {
- index = (y * drawIt.w + x) * 4;
- if (overMaskImgData.data[index] > 0) {
- pix.x.push(x);
- pix.y.push(y);
- pix.index.push(index);
- }
- }
- }
- for (i = 0; i < pix.index.length; i++) {
- // get the index in the stupid array
- // why? it's unused
- // var currentMaskPixelIndex = pix.index[i];
+ initImgData.data[i] = imgChunkData[i]; // put the original picture back in the painted area
+ initImgData.data[i + 1] = imgChunkData[i + 1];
+ initImgData.data[i + 2] = imgChunkData[i + 2];
+ initImgData.data[i + 3] = imgChunkData[i + 3]; //it's still RGBA so we can handily do this in nice chunks'o'4
+ }
+ }
+ if (overMaskPx > 0) {
+ // https://stackoverflow.com/a/30204783 ???? !!!!!!!!
+ overMaskCanvasCtx.fillStyle = "black";
+ overMaskCanvasCtx.fillRect(0, 0, drawIt.w, drawIt.h); // fill with black instead of null to start
+ for (i = 0; i < overMaskImgData.data.length; i += 4) {
+ if (overMaskImgData.data[i] == 255) {
+ // white pixel?
+ // just blotch all over the thing
+ var rando = Math.floor(Math.random() * overMaskPx);
+ overMaskCanvasCtx.beginPath();
+ overMaskCanvasCtx.arc(
+ (i / 4) % overMaskCanvas.width,
+ Math.floor(i / 4 / overMaskCanvas.width),
+ scaleFactor + rando, // was 4 * sf + rando, too big
+ 0,
+ 2 * Math.PI,
+ true
+ );
+ overMaskCanvasCtx.fillStyle = "#FFFFFFFF";
+ overMaskCanvasCtx.fill();
+ }
+ }
+ overMaskImgData = overMaskCanvasCtx.getImageData(
+ 0,
+ 0,
+ overMaskCanvas.width,
+ overMaskCanvas.height
+ );
+ overMaskCanvasCtx.putImageData(overMaskImgData, 0, 0);
+ }
+ // also check for painted masks in region, add them as white pixels to mask canvas
+ const maskChunk = maskPaintCtx.getImageData(
+ drawIt.x,
+ drawIt.y,
+ drawIt.w,
+ drawIt.h
+ );
+ const maskChunkData = maskChunk.data;
+ for (let i = 0; i < maskChunkData.length; i += 4) {
+ if (maskChunkData[i + 3] != 0) {
+ overMaskImgData.data[i] = 255;
+ overMaskImgData.data[i + 1] = 255;
+ overMaskImgData.data[i + 2] = 255;
+ overMaskImgData.data[i + 3] = 255;
+ }
+ }
+ // backup any painted masks ingested then them, replacable if user doesn't like resultant image
+ var clearArea = maskPaintCtx.createImageData(drawIt.w, drawIt.h);
+ backupMaskChunk = maskChunk;
+ backupMaskX = drawIt.x;
+ backupMaskY = drawIt.y;
- // for any horizontal expansion, we need to ensure that the target pixel is in the same Y row
- // horizontal left (west) is index-4 per pixel
- // horizontal right (east) is index+4 per pixel
- var currentMaskPixelY = pix.y[i];
-
- // for any vertical expansion, we need to ensure that the target pixel is in the same X column
- // vertical up (north) is index-(imagedata.width) per pixel
- // vertical down (south) is index+(imagedata.width) per pixel
- var currentMaskPixelX = pix.x[i];
-
- // i hate uint8clampedarray and math
- // primarily math
- // actually just my brain
- // ok so now lets check neighbors to see if they're in the same row/column
- for (j = overMaskPx; j > 0; j--) {
- // set a variable to the extreme end of the overmask size and work our way back inwards
- // i hate uint8clampedarray and math
- // this is so inefficient but i warned you all i'm bad at this
- //TODO refactor like all of this, it's horrible and shameful
- // BUT IT WORKS
- // but it is crushingly inefficient i'm sure
- // BUT IT WORKS and i came up with it all by myself because i'm a big boy
-
- // ... sigh it doesn't work _well_
- // just moves the seam
- //TODO find a way to fade/gradient the edge without making weird artifacts or literally crashing the browser with inefficient data storage
-
- // west
- var potentialPixelIndex =
- (currentMaskPixelY * drawIt.w +
- currentMaskPixelX) *
- 4 -
- j * 4;
- var potentialPixelX =
- (potentialPixelIndex / 4) % drawIt.w;
- var potentialPixelY = Math.floor(
- potentialPixelIndex / 4 / drawIt.w
- );
- // west/east: ENSURE SAME ROW using the y axis unintuitively
- if (potentialPixelY == currentMaskPixelY) {
- // ok then
- // ensure it's not already a mask pixel
- if (
- overMaskImgData.data[potentialPixelIndex] !=
- 255
- ) {
- // welp fingers crossed
- overMaskImgData.data[
- potentialPixelIndex
- ] = 255;
- overMaskImgData.data[
- potentialPixelIndex + 1
- ] = 255;
- overMaskImgData.data[
- potentialPixelIndex + 2
- ] = 255;
- overMaskImgData.data[
- potentialPixelIndex + 3
- ] = 255;
- }
- }
-
- // east
- var potentialPixelIndex =
- (currentMaskPixelY * drawIt.w +
- currentMaskPixelX) *
- 4 +
- j * 4;
- var potentialPixelX =
- (potentialPixelIndex / 4) % drawIt.w;
- var potentialPixelY = Math.floor(
- potentialPixelIndex / 4 / drawIt.w
- );
- if (potentialPixelY == currentMaskPixelY) {
- if (
- overMaskImgData.data[potentialPixelIndex] !=
- 255
- ) {
- overMaskImgData.data[
- potentialPixelIndex
- ] = 255;
- overMaskImgData.data[
- potentialPixelIndex + 1
- ] = 255;
- overMaskImgData.data[
- potentialPixelIndex + 2
- ] = 255;
- overMaskImgData.data[
- potentialPixelIndex + 3
- ] = 255;
- }
- }
-
- // north
- var potentialPixelIndex =
- (currentMaskPixelY * drawIt.w +
- currentMaskPixelX) *
- 4 -
- j * drawIt.w * 4;
- var potentialPixelX =
- (potentialPixelIndex / 4) % drawIt.w;
- var potentialPixelY = Math.floor(
- potentialPixelIndex / 4 / drawIt.w
- );
- // north/south: ENSURE SAME COLUMN using the x axis unintuitively
- if (potentialPixelX == currentMaskPixelX) {
- if (
- overMaskImgData.data[potentialPixelIndex] !=
- 255
- ) {
- overMaskImgData.data[
- potentialPixelIndex
- ] = 255;
- overMaskImgData.data[
- potentialPixelIndex + 1
- ] = 255;
- overMaskImgData.data[
- potentialPixelIndex + 2
- ] = 255;
- overMaskImgData.data[
- potentialPixelIndex + 3
- ] = 255;
- }
- }
-
- // south
- var potentialPixelIndex =
- (currentMaskPixelY * drawIt.w +
- currentMaskPixelX) *
- 4 +
- j * drawIt.w * 4;
- var potentialPixelX =
- (potentialPixelIndex / 4) % drawIt.w;
- var potentialPixelY = Math.floor(
- potentialPixelIndex / 4 / drawIt.w
- );
- if (potentialPixelX == currentMaskPixelX) {
- if (
- overMaskImgData.data[potentialPixelIndex] !=
- 255
- ) {
- overMaskImgData.data[
- potentialPixelIndex
- ] = 255;
- overMaskImgData.data[
- potentialPixelIndex + 1
- ] = 255;
- overMaskImgData.data[
- potentialPixelIndex + 2
- ] = 255;
- overMaskImgData.data[
- potentialPixelIndex + 3
- ] = 255;
- }
- }
- }
- }
-
- // also check for painted masks in region, add them as white pixels to mask canvas
- const maskChunk = maskPaintCtx.getImageData(
- drawIt.x,
- drawIt.y,
- drawIt.w,
- drawIt.h
- );
- const maskChunkData = maskChunk.data;
- for (let i = 0; i < maskChunkData.length; i += 4) {
- if (maskChunkData[i + 3] != 0) {
- overMaskImgData.data[i] = 255;
- overMaskImgData.data[i + 1] = 255;
- overMaskImgData.data[i + 2] = 255;
- overMaskImgData.data[i + 3] = 255;
- }
- }
- // backup any painted masks ingested then them, replacable if user doesn't like resultant image
- var clearArea = maskPaintCtx.createImageData(
- drawIt.w,
- drawIt.h
- );
- backupMaskChunk = maskChunk;
- backupMaskX = drawIt.x;
- backupMaskY = drawIt.y;
-
- var clearD = clearArea.data;
- for (let i = 0; i < clearD.length; i++) {
- clearD[i] = 0; // just null it all out
- }
- maskPaintCtx.putImageData(clearArea, drawIt.x, drawIt.y);
- // mask monitors
- overMaskCanvasCtx.putImageData(overMaskImgData, 0, 0); // :pray:
- var overMaskBase64 = overMaskCanvas.toDataURL();
- initImgCanvasCtx.putImageData(initImgData, 0, 0);
- var initImgBase64 = initImgCanvas.toDataURL();
- // anyway all that to say NOW let's run img2img
- endpoint = 'img2img';
- stableDiffusionData.mask = overMaskBase64;
- stableDiffusionData.init_images = [initImgBase64];
- // slightly more involved than txt2img
- } else {
- // time to run txt2img
- endpoint = 'txt2img';
- // easy enough
- }
- stableDiffusionData.prompt =
- document.getElementById('prompt').value;
- stableDiffusionData.negative_prompt =
- document.getElementById('negPrompt').value;
- stableDiffusionData.width = drawIt.w;
- stableDiffusionData.height = drawIt.h;
- stableDiffusionData.firstphase_height = drawIt.h / 2;
- stableDiffusionData.firstphase_width = drawIt.w / 2;
- dream(drawIt.x, drawIt.y, stableDiffusionData);
- }
- }
- }
+ var clearD = clearArea.data;
+ for (let i = 0; i < clearD.length; i++) {
+ clearD[i] = 0; // just null it all out
+ }
+ maskPaintCtx.putImageData(clearArea, drawIt.x, drawIt.y);
+ // mask monitors
+ overMaskCanvasCtx.putImageData(overMaskImgData, 0, 0); // :pray:
+ var overMaskBase64 = overMaskCanvas.toDataURL();
+ initImgCanvasCtx.putImageData(initImgData, 0, 0);
+ var initImgBase64 = initImgCanvas.toDataURL();
+ // anyway all that to say NOW let's run img2img
+ endpoint = "img2img";
+ stableDiffusionData.mask = overMaskBase64;
+ stableDiffusionData.init_images = [initImgBase64];
+ // slightly more involved than txt2img
+ } else {
+ // time to run txt2img
+ endpoint = "txt2img";
+ // easy enough
+ }
+ stableDiffusionData.prompt = document.getElementById("prompt").value;
+ stableDiffusionData.negative_prompt =
+ document.getElementById("negPrompt").value;
+ stableDiffusionData.width = drawIt.w;
+ stableDiffusionData.height = drawIt.h;
+ stableDiffusionData.firstphase_height = drawIt.h / 2;
+ stableDiffusionData.firstphase_width = drawIt.w / 2;
+ dream(drawIt.x, drawIt.y, stableDiffusionData);
+ }
+ }
+ }
}
-const changeScaleFactor = sliderChangeHandlerFactory(
- 'scaleFactor',
- 'scaleFactorTxt',
- 'scaleFactor',
- 8,
- (k, v) => (scaleFactor = v),
- (k) => scaleFactor
-);
-const changeSteps = sliderChangeHandlerFactory(
- 'steps',
- 'stepsTxt',
- 'steps',
- 30
-);
-
function changePaintMode() {
- paintMode = document.getElementById('cbxPaint').checked;
- clearTargetMask();
- ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
+ paintMode = document.getElementById("cbxPaint").checked;
+ clearTargetMask();
+ ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
}
function changeEnableErasing() {
- // yeah because this is for the image layer
- enableErasing = document.getElementById('cbxEnableErasing').checked;
- localStorage.setItem('enable_erase', enableErasing);
+ // yeah because this is for the image layer
+ enableErasing = document.getElementById("cbxEnableErasing").checked;
+ localStorage.setItem("enable_erase", enableErasing);
}
function changeSampler() {
- stableDiffusionData.sampler_index =
- document.getElementById('samplerSelect').value;
- localStorage.setItem('sampler', stableDiffusionData.sampler_index);
+ stableDiffusionData.sampler_index =
+ document.getElementById("samplerSelect").value;
+ localStorage.setItem("sampler", stableDiffusionData.sampler_index);
}
const changeCfgScale = sliderChangeHandlerFactory(
- 'cfgScale',
- 'cfgScaleTxt',
- 'cfg_scale',
- 7.0
+ "cfgScale",
+ "cfgScaleTxt",
+ "cfg_scale",
+ 7.0
);
const changeBatchSize = sliderChangeHandlerFactory(
- 'batchSize',
- 'batchSizeText',
- 'batch_size',
- 2
+ "batchSize",
+ "batchSizeText",
+ "batch_size",
+ 2
);
const changeBatchCount = sliderChangeHandlerFactory(
- 'batchCount',
- 'batchCountText',
- 'n_iter',
- 2
+ "batchCount",
+ "batchCountText",
+ "n_iter",
+ 2
+);
+const changeScaleFactor = sliderChangeHandlerFactory(
+ "scaleFactor",
+ "scaleFactorTxt",
+ "scaleFactor",
+ 8,
+ (k, v) => (scaleFactor = v),
+ (k) => scaleFactor
+);
+const changeSteps = sliderChangeHandlerFactory(
+ "steps",
+ "stepsTxt",
+ "steps",
+ 30
);
function changeSnapMode() {
- snapToGrid = document.getElementById('cbxSnap').checked;
+ snapToGrid = document.getElementById("cbxSnap").checked;
}
function changeMaskBlur() {
- stableDiffusionData.mask_blur = document.getElementById('maskBlur').value;
- localStorage.setItem('mask_blur', stableDiffusionData.mask_blur);
+ stableDiffusionData.mask_blur = document.getElementById("maskBlur").value;
+ localStorage.setItem("mask_blur", stableDiffusionData.mask_blur);
}
function changeSeed() {
- stableDiffusionData.seed = document.getElementById('seed').value;
- localStorage.setItem('seed', stableDiffusionData.seed);
+ stableDiffusionData.seed = document.getElementById("seed").value;
+ localStorage.setItem("seed", stableDiffusionData.seed);
}
function changeOverMaskPx() {
- overMaskPx = document.getElementById('overMaskPx').value;
- localStorage.setItem('overmask_px', overMaskPx);
+ overMaskPx = document.getElementById("overMaskPx").value;
+ localStorage.setItem("overmask_px", overMaskPx);
}
function changeHiResFix() {
- stableDiffusionData.enable_hr = Boolean(
- document.getElementById('cbxHRFix').checked
- );
- localStorage.setItem('enable_hr', stableDiffusionData.enable_hr);
+ stableDiffusionData.enable_hr = Boolean(
+ document.getElementById("cbxHRFix").checked
+ );
+ localStorage.setItem("enable_hr", stableDiffusionData.enable_hr);
}
function isCanvasBlank(x, y, w, h, specifiedCanvas) {
- var canvas = document.getElementById(specifiedCanvas.id);
- return !canvas
- .getContext('2d')
- .getImageData(x, y, w, h)
- .data.some((channel) => channel !== 0);
+ var canvas = document.getElementById(specifiedCanvas.id);
+ return !canvas
+ .getContext("2d")
+ .getImageData(x, y, w, h)
+ .data.some((channel) => channel !== 0);
}
function drawBackground() {
- bgCtx.lineWidth = 1;
- bgCtx.strokeStyle = '#999';
- var gridbox = bgCanvas.getBoundingClientRect();
- for (var i = 0; i < gridbox.width; i += 64) {
- bgCtx.moveTo(i, 0);
- bgCtx.lineTo(i, bgCanvas.height);
- bgCtx.stroke();
- }
- for (var i = 0; i < gridbox.height; i += 64) {
- bgCtx.moveTo(0, i);
- bgCtx.lineTo(gridbox.width, i);
- bgCtx.stroke();
- }
+ bgCtx.lineWidth = 1;
+ bgCtx.strokeStyle = "#999";
+ var gridbox = bgCanvas.getBoundingClientRect();
+ for (var i = 0; i < gridbox.width; i += 64) {
+ bgCtx.moveTo(i, 0);
+ bgCtx.lineTo(i, bgCanvas.height);
+ bgCtx.stroke();
+ }
+ for (var i = 0; i < gridbox.height; i += 64) {
+ bgCtx.moveTo(0, i);
+ bgCtx.lineTo(gridbox.width, i);
+ bgCtx.stroke();
+ }
}
function preloadImage() {
- // gonna legit scream
- document.getElementById('overlayCanvas').onmousemove = null;
- document.getElementById('overlayCanvas').onmousedown = null;
- document.getElementById('overlayCanvas').onmouseup = null;
+ // possible firefox-only bug?
+ // attempt to prevent requesting a dream if double-clicking a selected image
+ document.getElementById("overlayCanvas").onmousemove = null;
+ document.getElementById("overlayCanvas").onmousedown = null;
+ document.getElementById("overlayCanvas").onmouseup = null;
- var file = document.getElementById('preloadImage').files[0];
- var reader = new FileReader();
- reader.onload = function (evt) {
- var imgCanvas = document.createElement('canvas');
- var imgCtx = imgCanvas.getContext('2d');
- arbitraryImage = new Image();
- arbitraryImage.onload = function () {
- blockNewImages = true;
- // now put it into imagedata for canvas fun
- imgCanvas.width = arbitraryImage.width;
- imgCanvas.height = arbitraryImage.height;
- imgCtx.drawImage(arbitraryImage, 0, 0);
- arbitraryImageData = imgCtx.getImageData(
- 0,
- 0,
- arbitraryImage.width,
- arbitraryImage.height
- ); // can't use that to drawImage on a canvas...
- arbitraryImageBitmap = createImageBitmap(arbitraryImageData); // apparently that either... maybe just the raw image?
- arbitraryImageBase64 = imgCanvas.toDataURL();
- placingArbitraryImage = true;
- document.getElementById('overlayCanvas').onmousemove = mouseMove;
- document.getElementById('overlayCanvas').onmousedown = mouseDown;
- document.getElementById('overlayCanvas').onmouseup = mouseUp;
- };
- arbitraryImage.src = evt.target.result;
- };
- reader.readAsDataURL(file);
+ var file = document.getElementById("preloadImage").files[0];
+ var reader = new FileReader();
+ reader.onload = function (evt) {
+ var imgCanvas = document.createElement("canvas");
+ var imgCtx = imgCanvas.getContext("2d");
+ arbitraryImage = new Image();
+ arbitraryImage.onload = function () {
+ blockNewImages = true;
+ // now put it into imagedata for canvas fun
+ imgCanvas.width = arbitraryImage.width;
+ imgCanvas.height = arbitraryImage.height;
+ imgCtx.drawImage(arbitraryImage, 0, 0);
+ arbitraryImageData = imgCtx.getImageData(
+ 0,
+ 0,
+ arbitraryImage.width,
+ arbitraryImage.height
+ ); // can't use that to drawImage on a canvas...
+ arbitraryImageBitmap = createImageBitmap(arbitraryImageData); // apparently that either... maybe just the raw image?
+ arbitraryImageBase64 = imgCanvas.toDataURL();
+ placingArbitraryImage = true;
+ document.getElementById("overlayCanvas").onmousemove = mouseMove;
+ document.getElementById("overlayCanvas").onmousedown = mouseDown;
+ document.getElementById("overlayCanvas").onmouseup = mouseUp;
+ };
+ arbitraryImage.src = evt.target.result;
+ };
+ reader.readAsDataURL(file);
}
function downloadCanvas() {
- var link = document.createElement('a');
- link.download =
- new Date()
- .toISOString()
- .slice(0, 19)
- .replace('T', ' ')
- .replace(':', ' ') + ' openOutpaint image.png';
- var croppedCanvas = cropCanvas(imgCanvas);
- if (croppedCanvas != null) {
- link.href = croppedCanvas.toDataURL('image/png');
- link.click();
- }
+ var link = document.createElement("a");
+ link.download =
+ new Date().toISOString().slice(0, 19).replace("T", " ").replace(":", " ") +
+ " openOutpaint image.png";
+ var croppedCanvas = cropCanvas(imgCanvas);
+ if (croppedCanvas != null) {
+ link.href = croppedCanvas.toDataURL("image/png");
+ link.click();
+ }
}
function cropCanvas(sourceCanvas) {
- var w = sourceCanvas.width;
- var h = sourceCanvas.height;
- var pix = { x: [], y: [] };
- var imageData = sourceCanvas.getContext('2d').getImageData(0, 0, w, h);
- var x, y, index;
+ var w = sourceCanvas.width;
+ var h = sourceCanvas.height;
+ var pix = {x: [], y: []};
+ var imageData = sourceCanvas.getContext("2d").getImageData(0, 0, w, h);
+ var x, y, index;
- for (y = 0; y < h; y++) {
- for (x = 0; x < w; x++) {
- // lol i need to learn what this part does
- index = (y * w + x) * 4; // OHHH OK this is setting the imagedata.data uint8clampeddataarray index for the specified x/y coords
- //this part i get, this is checking that 4th RGBA byte for opacity
- if (imageData.data[index + 3] > 0) {
- pix.x.push(x);
- pix.y.push(y);
- }
- }
- }
- // ...need to learn what this part does too :badpokerface:
- // is this just determining the boundaries of non-transparent pixel data?
- pix.x.sort(function (a, b) {
- return a - b;
- });
- pix.y.sort(function (a, b) {
- return a - b;
- });
- var n = pix.x.length - 1;
- w = pix.x[n] - pix.x[0];
- h = pix.y[n] - pix.y[0];
- // yup sure looks like it
+ for (y = 0; y < h; y++) {
+ for (x = 0; x < w; x++) {
+ // lol i need to learn what this part does
+ index = (y * w + x) * 4; // OHHH OK this is setting the imagedata.data uint8clampeddataarray index for the specified x/y coords
+ //this part i get, this is checking that 4th RGBA byte for opacity
+ if (imageData.data[index + 3] > 0) {
+ pix.x.push(x);
+ pix.y.push(y);
+ }
+ }
+ }
+ // ...need to learn what this part does too :badpokerface:
+ // is this just determining the boundaries of non-transparent pixel data?
+ pix.x.sort(function (a, b) {
+ return a - b;
+ });
+ pix.y.sort(function (a, b) {
+ return a - b;
+ });
+ var n = pix.x.length - 1;
+ w = pix.x[n] - pix.x[0];
+ h = pix.y[n] - pix.y[0];
+ // yup sure looks like it
- try {
- var cut = sourceCanvas
- .getContext('2d')
- .getImageData(pix.x[0], pix.y[0], w, h);
- var cutCanvas = document.createElement('canvas');
- cutCanvas.width = w;
- cutCanvas.height = h;
- cutCanvas.getContext('2d').putImageData(cut, 0, 0);
- } catch (ex) {
- // probably empty image
- //TODO confirm edge cases?
- cutCanvas = null;
- }
- return cutCanvas;
+ try {
+ var cut = sourceCanvas
+ .getContext("2d")
+ .getImageData(pix.x[0], pix.y[0], w, h);
+ var cutCanvas = document.createElement("canvas");
+ cutCanvas.width = w;
+ cutCanvas.height = h;
+ cutCanvas.getContext("2d").putImageData(cut, 0, 0);
+ } catch (ex) {
+ // probably empty image
+ //TODO confirm edge cases?
+ cutCanvas = null;
+ }
+ return cutCanvas;
}
function checkIfWebuiIsRunning() {
- var url = document.getElementById('host').value + '/startup-events';
- fetch(url)
- .then((response) => {
- if (response.status == 200) {
- console.log('webui is running');
- }
- })
- .catch((error) => {
- alert(
- 'WebUI doesnt seem to be running, please start it and try again\nCheck console for additional info\n' +
- error
- );
- });
+ var url = document.getElementById("host").value + "/startup-events";
+ fetch(url)
+ .then((response) => {
+ if (response.status == 200) {
+ console.log("webui is running");
+ }
+ })
+ .catch((error) => {
+ alert(
+ "WebUI doesnt seem to be running, please start it and try again\nCheck console for additional info\n" +
+ error
+ );
+ });
}
function loadSettings() {
- // set default values if not set
- var _sampler =
- localStorage.getItem('sampler') == null
- ? 'DDIM'
- : localStorage.getItem('sampler');
- var _mask_blur =
- localStorage.getItem('mask_blur') == null
- ? 0
- : localStorage.getItem('mask_blur');
- var _seed =
- localStorage.getItem('seed') == null
- ? -1
- : localStorage.getItem('seed');
- var _enable_hr = Boolean(
- localStorage.getItem('enable_hr') == (null || 'false')
- ? false
- : localStorage.getItem('enable_hr')
- );
- var _enable_erase = Boolean(
- localStorage.getItem('enable_erase') == (null || 'false')
- ? false
- : localStorage.getItem('enable_erase')
- );
- var _overmask_px =
- localStorage.getItem('overmask_px') == null
- ? 0
- : localStorage.getItem('overmask_px');
+ // set default values if not set
+ var _sampler =
+ localStorage.getItem("sampler") == null
+ ? "DDIM"
+ : localStorage.getItem("sampler");
+ var _mask_blur =
+ localStorage.getItem("mask_blur") == null
+ ? 0
+ : localStorage.getItem("mask_blur");
+ var _seed =
+ localStorage.getItem("seed") == null ? -1 : localStorage.getItem("seed");
+ var _enable_hr = Boolean(
+ localStorage.getItem("enable_hr") == (null || "false")
+ ? false
+ : localStorage.getItem("enable_hr")
+ );
+ var _enable_erase = Boolean(
+ localStorage.getItem("enable_erase") == (null || "false")
+ ? false
+ : localStorage.getItem("enable_erase")
+ );
+ var _overmask_px =
+ localStorage.getItem("overmask_px") == null
+ ? 0
+ : localStorage.getItem("overmask_px");
- // set the values into the UI
- document.getElementById('samplerSelect').value = String(_sampler);
- document.getElementById('maskBlur').value = Number(_mask_blur);
- document.getElementById('seed').value = Number(_seed);
- document.getElementById('cbxHRFix').checked = Boolean(_enable_hr);
- document.getElementById('cbxEnableErasing').checked =
- Boolean(_enable_erase);
- document.getElementById('overMaskPx').value = Number(_overmask_px);
+ // set the values into the UI
+ document.getElementById("samplerSelect").value = String(_sampler);
+ document.getElementById("maskBlur").value = Number(_mask_blur);
+ document.getElementById("seed").value = Number(_seed);
+ document.getElementById("cbxHRFix").checked = Boolean(_enable_hr);
+ document.getElementById("cbxEnableErasing").checked = Boolean(_enable_erase);
+ document.getElementById("overMaskPx").value = Number(_overmask_px);
}
diff --git a/js/settingsbar.js b/js/settingsbar.js
index e50228e..84120a3 100644
--- a/js/settingsbar.js
+++ b/js/settingsbar.js
@@ -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";
- }
- });
-}
\ No newline at end of file
+ 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";
+ }
+ });
+}