no more scale factor, support for dream cursor size
Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
This commit is contained in:
parent
4681113f81
commit
59e783d159
6 changed files with 272 additions and 123 deletions
|
@ -86,16 +86,15 @@
|
||||||
value="-1"
|
value="-1"
|
||||||
step="1" />
|
step="1" />
|
||||||
<br />
|
<br />
|
||||||
|
<input type="checkbox" id="cbxHRFix" onchange="changeHiResFix()" />
|
||||||
|
<label for="cbxHRFix">Auto txt2img HRfix</label>
|
||||||
|
<div id="resolution"></div>
|
||||||
<div id="steps"></div>
|
<div id="steps"></div>
|
||||||
<div id="cfgScale"></div>
|
<div id="cfgScale"></div>
|
||||||
<div id="batchSize"></div>
|
<div id="batchSize"></div>
|
||||||
<div id="batchCount"></div>
|
<div id="batchCount"></div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Unsectioned -->
|
<!-- Unsectioned -->
|
||||||
<div id="scaleFactor"></div>
|
|
||||||
<input type="checkbox" id="cbxHRFix" onchange="changeHiResFix()" />
|
|
||||||
<label for="cbxHRFix">Auto txt2img HRfix</label>
|
|
||||||
<br />
|
|
||||||
<label for="maskBlur">Mask blur:</label>
|
<label for="maskBlur">Mask blur:</label>
|
||||||
<span id="maskBlurText"></span>
|
<span id="maskBlurText"></span>
|
||||||
<br />
|
<br />
|
||||||
|
|
47
js/index.js
47
js/index.js
|
@ -60,7 +60,6 @@ var frameX = 512;
|
||||||
var frameY = 512;
|
var frameY = 512;
|
||||||
var drawThis = {};
|
var drawThis = {};
|
||||||
const basePixelCount = 64; //64 px - ALWAYS 64 PX
|
const basePixelCount = 64; //64 px - ALWAYS 64 PX
|
||||||
var scaleFactor = 8; //x64 px
|
|
||||||
var snapToGrid = true;
|
var snapToGrid = true;
|
||||||
var backupMaskPaintCanvas; //???
|
var backupMaskPaintCanvas; //???
|
||||||
var backupMaskPaintCtx; //...? look i am bad at this
|
var backupMaskPaintCtx; //...? look i am bad at this
|
||||||
|
@ -117,7 +116,6 @@ function startup() {
|
||||||
changeSeed();
|
changeSeed();
|
||||||
changeOverMaskPx();
|
changeOverMaskPx();
|
||||||
changeHiResFix();
|
changeHiResFix();
|
||||||
document.getElementById("scaleFactor").value = scaleFactor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -497,15 +495,20 @@ const makeSlider = (
|
||||||
textStep = null,
|
textStep = null,
|
||||||
valuecb = null
|
valuecb = null
|
||||||
) => {
|
) => {
|
||||||
const local = localStorage.getItem(lsKey);
|
const local = lsKey && localStorage.getItem(lsKey);
|
||||||
const def = parseFloat(local === null ? defaultValue : local);
|
const def = parseFloat(local === null ? defaultValue : local);
|
||||||
return createSlider(label, el, {
|
let cb = (v) => {
|
||||||
valuecb:
|
|
||||||
valuecb ||
|
|
||||||
((v) => {
|
|
||||||
stableDiffusionData[lsKey] = v;
|
stableDiffusionData[lsKey] = v;
|
||||||
|
if (lsKey) localStorage.setItem(lsKey, v);
|
||||||
|
};
|
||||||
|
if (valuecb) {
|
||||||
|
cb = (v) => {
|
||||||
|
valuecb(v);
|
||||||
localStorage.setItem(lsKey, v);
|
localStorage.setItem(lsKey, v);
|
||||||
}),
|
};
|
||||||
|
}
|
||||||
|
return createSlider(label, el, {
|
||||||
|
valuecb: cb,
|
||||||
min,
|
min,
|
||||||
max,
|
max,
|
||||||
step,
|
step,
|
||||||
|
@ -514,6 +517,21 @@ const makeSlider = (
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
makeSlider(
|
||||||
|
"Resolution",
|
||||||
|
document.getElementById("resolution"),
|
||||||
|
"resolution",
|
||||||
|
64,
|
||||||
|
1024,
|
||||||
|
64,
|
||||||
|
512,
|
||||||
|
2,
|
||||||
|
(v) => {
|
||||||
|
stableDiffusionData.width = stableDiffusionData.height = v;
|
||||||
|
stableDiffusionData.firstphase_width =
|
||||||
|
stableDiffusionData.firstphase_height = v / 2;
|
||||||
|
}
|
||||||
|
);
|
||||||
makeSlider(
|
makeSlider(
|
||||||
"CFG Scale",
|
"CFG Scale",
|
||||||
document.getElementById("cfgScale"),
|
document.getElementById("cfgScale"),
|
||||||
|
@ -542,19 +560,6 @@ makeSlider(
|
||||||
1,
|
1,
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
makeSlider(
|
|
||||||
"Scale Factor",
|
|
||||||
document.getElementById("scaleFactor"),
|
|
||||||
"scale_factor",
|
|
||||||
1,
|
|
||||||
16,
|
|
||||||
1,
|
|
||||||
8,
|
|
||||||
null,
|
|
||||||
(v) => {
|
|
||||||
scaleFactor = v;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
makeSlider("Steps", document.getElementById("steps"), "steps", 1, 70, 5, 30, 1);
|
makeSlider("Steps", document.getElementById("steps"), "steps", 1, 70, 5, 30, 1);
|
||||||
|
|
||||||
|
|
|
@ -145,21 +145,14 @@ function makeWriteOnce(obj, name = "write-once object", exceptions = []) {
|
||||||
* Snaps a single value to an infinite grid
|
* Snaps a single value to an infinite grid
|
||||||
*
|
*
|
||||||
* @param {number} i Original value to be snapped
|
* @param {number} i Original value to be snapped
|
||||||
* @param {boolean} scaled If grid will change alignment for odd scaleFactor values (default: true)
|
* @param {number} [offset=0] Value to offset the grid. Should be in the rande [0, gridSize[
|
||||||
* @param {number} gridSize Size of the grid
|
* @param {number} [gridSize=64] Size of the grid
|
||||||
* @returns an offset, in which [i + offset = (a location snapped to the grid)]
|
* @returns an offset, in which [i + offset = (a location snapped to the grid)]
|
||||||
*/
|
*/
|
||||||
function snap(i, scaled = true, gridSize = 64) {
|
function snap(i, offset = 0, gridSize = 64) {
|
||||||
// very cheap test proof of concept but it works surprisingly well
|
const modulus = (i - offset) % gridSize;
|
||||||
var scaleOffset = 0;
|
var snapOffset = modulus;
|
||||||
if (scaled) {
|
|
||||||
if (scaleFactor % 2 != 0) {
|
|
||||||
// odd number, snaps to center of cell, oops
|
|
||||||
scaleOffset = gridSize / 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const modulus = i % gridSize;
|
|
||||||
var snapOffset = modulus - scaleOffset;
|
|
||||||
if (modulus > gridSize / 2) snapOffset = modulus - gridSize;
|
if (modulus > gridSize / 2) snapOffset = modulus - gridSize;
|
||||||
|
|
||||||
if (snapOffset == 0) {
|
if (snapOffset == 0) {
|
||||||
|
@ -175,19 +168,20 @@ function snap(i, scaled = true, gridSize = 64) {
|
||||||
* @param {number} cy - y-coordinate of the center of the box
|
* @param {number} cy - y-coordinate of the center of the box
|
||||||
* @param {number} w - the width of the box
|
* @param {number} w - the width of the box
|
||||||
* @param {height} h - the height of the box
|
* @param {height} h - the height of the box
|
||||||
* @param {number | null} gridSnap - The size of the grid to snap to
|
* @param {?number} gridSnap - The size of the grid to snap to
|
||||||
|
* @param {number} [offset=0] - How much to offset the grid by
|
||||||
* @returns {BoundingBox} - A bounding box object centered at (cx, cy)
|
* @returns {BoundingBox} - A bounding box object centered at (cx, cy)
|
||||||
*/
|
*/
|
||||||
function getBoundingBox(cx, cy, w, h, gridSnap = null) {
|
function getBoundingBox(cx, cy, w, h, gridSnap = null, offset = 0) {
|
||||||
const offset = {x: 0, y: 0};
|
const offs = {x: 0, y: 0};
|
||||||
const box = {x: 0, y: 0};
|
const box = {x: 0, y: 0};
|
||||||
|
|
||||||
if (gridSnap) {
|
if (gridSnap) {
|
||||||
offset.x = snap(cx, true, gridSnap);
|
offs.x = snap(cx, offset, gridSnap);
|
||||||
offset.y = snap(cy, true, gridSnap);
|
offs.y = snap(cy, offset, gridSnap);
|
||||||
}
|
}
|
||||||
box.x = offset.x + cx;
|
box.x = offs.x + cx;
|
||||||
box.y = offset.y + cy;
|
box.y = offs.y + cy;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: Math.floor(box.x - w / 2),
|
x: Math.floor(box.x - w / 2),
|
||||||
|
|
|
@ -125,13 +125,27 @@ const _generate = async (endpoint, request, bb) => {
|
||||||
image.src = "data:image/png;base64," + images[at];
|
image.src = "data:image/png;base64," + images[at];
|
||||||
image.addEventListener("load", () => {
|
image.addEventListener("load", () => {
|
||||||
layer.ctx.clearRect(0, 0, layer.canvas.width, layer.canvas.height);
|
layer.ctx.clearRect(0, 0, layer.canvas.width, layer.canvas.height);
|
||||||
if (images[at]) layer.ctx.drawImage(image, bb.x, bb.y);
|
if (images[at])
|
||||||
|
layer.ctx.drawImage(
|
||||||
|
image,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
image.width,
|
||||||
|
image.height,
|
||||||
|
bb.x,
|
||||||
|
bb.y,
|
||||||
|
bb.w,
|
||||||
|
bb.h
|
||||||
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const stopMarchingAnts = march(bb);
|
const stopMarchingAnts = march(bb);
|
||||||
|
|
||||||
// First Dream Run
|
// First Dream Run
|
||||||
|
console.info(`[dream] Generating images for prompt '${request.prompt}'`);
|
||||||
|
console.debug(request);
|
||||||
|
|
||||||
let stopProgress = _monitorProgress(bb);
|
let stopProgress = _monitorProgress(bb);
|
||||||
images.push(...(await _dream(endpoint, requestCopy)));
|
images.push(...(await _dream(endpoint, requestCopy)));
|
||||||
stopProgress();
|
stopProgress();
|
||||||
|
@ -161,6 +175,8 @@ const _generate = async (endpoint, request, bb) => {
|
||||||
commands.runCommand("drawImage", "Image Dream", {
|
commands.runCommand("drawImage", "Image Dream", {
|
||||||
x: bb.x,
|
x: bb.x,
|
||||||
y: bb.y,
|
y: bb.y,
|
||||||
|
w: bb.w,
|
||||||
|
h: bb.h,
|
||||||
image: img,
|
image: img,
|
||||||
});
|
});
|
||||||
clean(true);
|
clean(true);
|
||||||
|
@ -316,8 +332,8 @@ const dream_generate_callback = async (evn, state) => {
|
||||||
const bb = getBoundingBox(
|
const bb = getBoundingBox(
|
||||||
evn.x,
|
evn.x,
|
||||||
evn.y,
|
evn.y,
|
||||||
basePixelCount * scaleFactor,
|
state.cursorSize,
|
||||||
basePixelCount * scaleFactor,
|
state.cursorSize,
|
||||||
state.snapToGrid && basePixelCount
|
state.snapToGrid && basePixelCount
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -332,13 +348,6 @@ const dream_generate_callback = async (evn, state) => {
|
||||||
// Don't allow another image until is finished
|
// Don't allow another image until is finished
|
||||||
blockNewImages = true;
|
blockNewImages = true;
|
||||||
|
|
||||||
// Setup some basic information for SD
|
|
||||||
request.width = bb.w;
|
|
||||||
request.height = bb.h;
|
|
||||||
|
|
||||||
request.firstphase_width = bb.w / 2;
|
|
||||||
request.firstphase_height = bb.h / 2;
|
|
||||||
|
|
||||||
// Use txt2img if canvas is blank
|
// Use txt2img if canvas is blank
|
||||||
if (isCanvasBlank(bb.x, bb.y, bb.w, bb.h, imgCanvas)) {
|
if (isCanvasBlank(bb.x, bb.y, bb.w, bb.h, imgCanvas)) {
|
||||||
// Dream
|
// Dream
|
||||||
|
@ -355,13 +364,23 @@ const dream_generate_callback = async (evn, state) => {
|
||||||
auxCtx.fillStyle = "#000F";
|
auxCtx.fillStyle = "#000F";
|
||||||
|
|
||||||
// Get init image
|
// Get init image
|
||||||
auxCtx.fillRect(0, 0, bb.w, bb.h);
|
auxCtx.fillRect(0, 0, request.width, request.height);
|
||||||
auxCtx.drawImage(imgCanvas, bb.x, bb.y, bb.w, bb.h, 0, 0, bb.w, bb.h);
|
auxCtx.drawImage(
|
||||||
|
imgCanvas,
|
||||||
|
bb.x,
|
||||||
|
bb.y,
|
||||||
|
bb.w,
|
||||||
|
bb.h,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
request.width,
|
||||||
|
request.height
|
||||||
|
);
|
||||||
request.init_images = [auxCanvas.toDataURL()];
|
request.init_images = [auxCanvas.toDataURL()];
|
||||||
|
|
||||||
// Get mask image
|
// Get mask image
|
||||||
auxCtx.fillStyle = "#000F";
|
auxCtx.fillStyle = "#000F";
|
||||||
auxCtx.fillRect(0, 0, bb.w, bb.h);
|
auxCtx.fillRect(0, 0, request.width, request.height);
|
||||||
if (state.invertMask) {
|
if (state.invertMask) {
|
||||||
// overmasking by definition is entirely pointless with an inverted mask outpaint
|
// overmasking by definition is entirely pointless with an inverted mask outpaint
|
||||||
// since it should explicitly avoid brushed masks too, we just won't even bother
|
// since it should explicitly avoid brushed masks too, we just won't even bother
|
||||||
|
@ -374,23 +393,45 @@ const dream_generate_callback = async (evn, state) => {
|
||||||
bb.h,
|
bb.h,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
bb.w,
|
request.width,
|
||||||
bb.h
|
request.height
|
||||||
);
|
);
|
||||||
|
|
||||||
auxCtx.globalCompositeOperation = "destination-in";
|
auxCtx.globalCompositeOperation = "destination-in";
|
||||||
auxCtx.drawImage(imgCanvas, bb.x, bb.y, bb.w, bb.h, 0, 0, bb.w, bb.h);
|
auxCtx.drawImage(
|
||||||
|
imgCanvas,
|
||||||
|
bb.x,
|
||||||
|
bb.y,
|
||||||
|
bb.w,
|
||||||
|
bb.h,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
request.width,
|
||||||
|
request.height
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
auxCtx.globalCompositeOperation = "destination-in";
|
auxCtx.globalCompositeOperation = "destination-in";
|
||||||
auxCtx.drawImage(imgCanvas, bb.x, bb.y, bb.w, bb.h, 0, 0, bb.w, bb.h);
|
auxCtx.drawImage(
|
||||||
|
imgCanvas,
|
||||||
|
bb.x,
|
||||||
|
bb.y,
|
||||||
|
bb.w,
|
||||||
|
bb.h,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
request.width,
|
||||||
|
request.height
|
||||||
|
);
|
||||||
// here's where to overmask to avoid including the brushed mask
|
// here's where to overmask to avoid including the brushed mask
|
||||||
// 99% of my issues were from failing to set source-over for the overmask blotches
|
// 99% of my issues were from failing to set source-over for the overmask blotches
|
||||||
if (state.overMaskPx > 0) {
|
if (state.overMaskPx > 0) {
|
||||||
// transparent to white first
|
// transparent to white first
|
||||||
auxCtx.globalCompositeOperation = "destination-atop";
|
auxCtx.globalCompositeOperation = "destination-atop";
|
||||||
auxCtx.fillStyle = "#FFFF";
|
auxCtx.fillStyle = "#FFFF";
|
||||||
auxCtx.fillRect(0, 0, bb.w, bb.h);
|
auxCtx.fillRect(0, 0, request.width, request.height);
|
||||||
|
downloadCanvas({canvas: auxCanvas, filename: null});
|
||||||
applyOvermask(auxCanvas, auxCtx, state.overMaskPx);
|
applyOvermask(auxCanvas, auxCtx, state.overMaskPx);
|
||||||
|
downloadCanvas({canvas: auxCanvas, filename: null});
|
||||||
}
|
}
|
||||||
|
|
||||||
auxCtx.globalCompositeOperation = "destination-out"; // ???
|
auxCtx.globalCompositeOperation = "destination-out"; // ???
|
||||||
|
@ -402,13 +443,13 @@ const dream_generate_callback = async (evn, state) => {
|
||||||
bb.h,
|
bb.h,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
bb.w,
|
request.width,
|
||||||
bb.h
|
request.height
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
auxCtx.globalCompositeOperation = "destination-atop";
|
auxCtx.globalCompositeOperation = "destination-atop";
|
||||||
auxCtx.fillStyle = "#FFFF";
|
auxCtx.fillStyle = "#FFFF";
|
||||||
auxCtx.fillRect(0, 0, bb.w, bb.h);
|
auxCtx.fillRect(0, 0, request.width, request.height);
|
||||||
request.mask = auxCanvas.toDataURL();
|
request.mask = auxCanvas.toDataURL();
|
||||||
// Dream
|
// Dream
|
||||||
_generate("img2img", request, bb);
|
_generate("img2img", request, bb);
|
||||||
|
@ -419,8 +460,8 @@ const dream_erase_callback = (evn, state) => {
|
||||||
const bb = getBoundingBox(
|
const bb = getBoundingBox(
|
||||||
evn.x,
|
evn.x,
|
||||||
evn.y,
|
evn.y,
|
||||||
basePixelCount * scaleFactor,
|
state.cursorSize,
|
||||||
basePixelCount * scaleFactor,
|
state.cursorSize,
|
||||||
state.snapToGrid && basePixelCount
|
state.snapToGrid && basePixelCount
|
||||||
);
|
);
|
||||||
commands.runCommand("eraseImage", "Erase Area", bb);
|
commands.runCommand("eraseImage", "Erase Area", bb);
|
||||||
|
@ -436,14 +477,25 @@ function applyOvermask(canvas, ctx, px) {
|
||||||
if (ctxImgData.data[i] == 255) {
|
if (ctxImgData.data[i] == 255) {
|
||||||
// white pixel?
|
// white pixel?
|
||||||
// just blotch all over the thing
|
// just blotch all over the thing
|
||||||
var rando = Math.floor(Math.random() * px);
|
/**
|
||||||
|
* This should probably have a better randomness profile for the overmasking
|
||||||
|
*
|
||||||
|
* Essentially, we want to have much more smaller values for randomness than big ones,
|
||||||
|
* because big values overshadow smaller circles and kinda ignores their randomness.
|
||||||
|
*
|
||||||
|
* And also, we want the profile to become more extreme the bigger the overmask size,
|
||||||
|
* because bigger px values also make bigger circles ocuppy more horizontal space.
|
||||||
|
*/
|
||||||
|
let lowRandom =
|
||||||
|
Math.atan(Math.random() * 10 - 10) / Math.abs(Math.atan(-10)) + 1;
|
||||||
|
lowRandom = Math.pow(lowRandom, px / 8);
|
||||||
|
|
||||||
|
var rando = Math.floor(lowRandom * px);
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(
|
ctx.arc(
|
||||||
(i / 4) % canvas.width,
|
(i / 4) % canvas.width,
|
||||||
Math.floor(i / 4 / canvas.width),
|
Math.floor(i / 4 / canvas.width),
|
||||||
scaleFactor +
|
rando, // was 4 * sf + rando, too big, but i think i want it more ... random
|
||||||
rando +
|
|
||||||
(rando > scaleFactor ? rando / scaleFactor : scaleFactor / rando), // was 4 * sf + rando, too big, but i think i want it more ... random
|
|
||||||
0,
|
0,
|
||||||
2 * Math.PI,
|
2 * Math.PI,
|
||||||
true
|
true
|
||||||
|
@ -462,8 +514,8 @@ const dream_img2img_callback = (evn, state) => {
|
||||||
const bb = getBoundingBox(
|
const bb = getBoundingBox(
|
||||||
evn.x,
|
evn.x,
|
||||||
evn.y,
|
evn.y,
|
||||||
basePixelCount * scaleFactor,
|
state.cursorSize,
|
||||||
basePixelCount * scaleFactor,
|
state.cursorSize,
|
||||||
state.snapToGrid && basePixelCount
|
state.snapToGrid && basePixelCount
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -484,13 +536,6 @@ const dream_img2img_callback = (evn, state) => {
|
||||||
// Don't allow another image until is finished
|
// Don't allow another image until is finished
|
||||||
blockNewImages = true;
|
blockNewImages = true;
|
||||||
|
|
||||||
// Setup some basic information for SD
|
|
||||||
request.width = bb.w;
|
|
||||||
request.height = bb.h;
|
|
||||||
|
|
||||||
request.firstphase_width = bb.w / 2;
|
|
||||||
request.firstphase_height = bb.h / 2;
|
|
||||||
|
|
||||||
// Use img2img
|
// Use img2img
|
||||||
|
|
||||||
// Temporary canvas for init image and mask generation
|
// Temporary canvas for init image and mask generation
|
||||||
|
@ -502,36 +547,56 @@ const dream_img2img_callback = (evn, state) => {
|
||||||
auxCtx.fillStyle = "#000F";
|
auxCtx.fillStyle = "#000F";
|
||||||
|
|
||||||
// Get init image
|
// Get init image
|
||||||
auxCtx.fillRect(0, 0, bb.w, bb.h);
|
auxCtx.fillRect(0, 0, request.width, request.height);
|
||||||
auxCtx.drawImage(imgCanvas, bb.x, bb.y, bb.w, bb.h, 0, 0, bb.w, bb.h);
|
auxCtx.drawImage(
|
||||||
|
imgCanvas,
|
||||||
|
bb.x,
|
||||||
|
bb.y,
|
||||||
|
bb.w,
|
||||||
|
bb.h,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
request.width,
|
||||||
|
request.height
|
||||||
|
);
|
||||||
request.init_images = [auxCanvas.toDataURL()];
|
request.init_images = [auxCanvas.toDataURL()];
|
||||||
|
|
||||||
// Get mask image
|
// Get mask image
|
||||||
auxCtx.fillStyle = state.invertMask ? "#FFFF" : "#000F";
|
auxCtx.fillStyle = state.invertMask ? "#FFFF" : "#000F";
|
||||||
auxCtx.fillRect(0, 0, bb.w, bb.h);
|
auxCtx.fillRect(0, 0, request.width, request.height);
|
||||||
auxCtx.globalCompositeOperation = "destination-out";
|
auxCtx.globalCompositeOperation = "destination-out";
|
||||||
auxCtx.drawImage(maskPaintCanvas, bb.x, bb.y, bb.w, bb.h, 0, 0, bb.w, bb.h);
|
auxCtx.drawImage(
|
||||||
|
maskPaintCanvas,
|
||||||
|
bb.x,
|
||||||
|
bb.y,
|
||||||
|
bb.w,
|
||||||
|
bb.h,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
request.width,
|
||||||
|
request.height
|
||||||
|
);
|
||||||
|
|
||||||
auxCtx.globalCompositeOperation = "destination-atop";
|
auxCtx.globalCompositeOperation = "destination-atop";
|
||||||
auxCtx.fillStyle = state.invertMask ? "#000F" : "#FFFF";
|
auxCtx.fillStyle = state.invertMask ? "#000F" : "#FFFF";
|
||||||
auxCtx.fillRect(0, 0, bb.w, bb.h);
|
auxCtx.fillRect(0, 0, request.width, request.height);
|
||||||
|
|
||||||
// Border Mask
|
// Border Mask
|
||||||
if (state.keepBorderSize > 0) {
|
if (state.keepBorderSize > 0) {
|
||||||
auxCtx.globalCompositeOperation = "source-over";
|
auxCtx.globalCompositeOperation = "source-over";
|
||||||
auxCtx.fillStyle = "#000F";
|
auxCtx.fillStyle = "#000F";
|
||||||
auxCtx.fillRect(0, 0, state.keepBorderSize, bb.h);
|
auxCtx.fillRect(0, 0, state.keepBorderSize, request.height);
|
||||||
auxCtx.fillRect(0, 0, bb.w, state.keepBorderSize);
|
auxCtx.fillRect(0, 0, request.width, state.keepBorderSize);
|
||||||
auxCtx.fillRect(
|
auxCtx.fillRect(
|
||||||
bb.w - state.keepBorderSize,
|
request.width - state.keepBorderSize,
|
||||||
0,
|
0,
|
||||||
state.keepBorderSize,
|
state.keepBorderSize,
|
||||||
bb.h
|
request.height
|
||||||
);
|
);
|
||||||
auxCtx.fillRect(
|
auxCtx.fillRect(
|
||||||
0,
|
0,
|
||||||
bb.h - state.keepBorderSize,
|
request.height - state.keepBorderSize,
|
||||||
bb.w,
|
request.width,
|
||||||
state.keepBorderSize
|
state.keepBorderSize
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -547,13 +612,13 @@ const dream_img2img_callback = (evn, state) => {
|
||||||
/**
|
/**
|
||||||
* Dream and img2img tools
|
* Dream and img2img tools
|
||||||
*/
|
*/
|
||||||
const _reticle_draw = (evn, snapToGrid = true) => {
|
const _reticle_draw = (evn, state) => {
|
||||||
const bb = getBoundingBox(
|
const bb = getBoundingBox(
|
||||||
evn.x,
|
evn.x,
|
||||||
evn.y,
|
evn.y,
|
||||||
basePixelCount * scaleFactor,
|
state.cursorSize,
|
||||||
basePixelCount * scaleFactor,
|
state.cursorSize,
|
||||||
snapToGrid && basePixelCount
|
state.snapToGrid && basePixelCount
|
||||||
);
|
);
|
||||||
|
|
||||||
// draw targeting square reticle thingy cursor
|
// draw targeting square reticle thingy cursor
|
||||||
|
@ -566,6 +631,20 @@ const _reticle_draw = (evn, snapToGrid = true) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic wheel handler
|
||||||
|
*/
|
||||||
|
|
||||||
|
const _dream_onwheel = (evn, state) => {
|
||||||
|
if (!evn.evn.ctrlKey) {
|
||||||
|
const v =
|
||||||
|
state.cursorSize -
|
||||||
|
Math.floor(state.config.cursorSizeScrollSpeed * evn.delta);
|
||||||
|
state.cursorSize = state.setCursorSize(v + snap(v, 0, 128));
|
||||||
|
state.mousemovecb(evn);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers Tools
|
* Registers Tools
|
||||||
*/
|
*/
|
||||||
|
@ -582,6 +661,7 @@ const dreamTool = () =>
|
||||||
|
|
||||||
// Start Listeners
|
// Start Listeners
|
||||||
mouse.listen.world.onmousemove.on(state.mousemovecb);
|
mouse.listen.world.onmousemove.on(state.mousemovecb);
|
||||||
|
mouse.listen.world.onwheel.on(state.wheelcb);
|
||||||
mouse.listen.world.btn.left.onclick.on(state.dreamcb);
|
mouse.listen.world.btn.left.onclick.on(state.dreamcb);
|
||||||
mouse.listen.world.btn.right.onclick.on(state.erasecb);
|
mouse.listen.world.btn.right.onclick.on(state.erasecb);
|
||||||
|
|
||||||
|
@ -591,6 +671,7 @@ const dreamTool = () =>
|
||||||
(state, opt) => {
|
(state, opt) => {
|
||||||
// Clear Listeners
|
// Clear Listeners
|
||||||
mouse.listen.world.onmousemove.clear(state.mousemovecb);
|
mouse.listen.world.onmousemove.clear(state.mousemovecb);
|
||||||
|
mouse.listen.world.onwheel.clear(state.wheelcb);
|
||||||
mouse.listen.world.btn.left.onclick.clear(state.dreamcb);
|
mouse.listen.world.btn.left.onclick.clear(state.dreamcb);
|
||||||
mouse.listen.world.btn.right.onclick.clear(state.erasecb);
|
mouse.listen.world.btn.right.onclick.clear(state.erasecb);
|
||||||
|
|
||||||
|
@ -599,6 +680,12 @@ const dreamTool = () =>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
init: (state) => {
|
init: (state) => {
|
||||||
|
state.config = {
|
||||||
|
cursorSizeScrollSpeed: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
state.cursorSize = 512;
|
||||||
|
|
||||||
state.snapToGrid = true;
|
state.snapToGrid = true;
|
||||||
state.invertMask = false;
|
state.invertMask = false;
|
||||||
state.overMaskPx = 0;
|
state.overMaskPx = 0;
|
||||||
|
@ -608,7 +695,10 @@ const dreamTool = () =>
|
||||||
|
|
||||||
state.mousemovecb = (evn) => {
|
state.mousemovecb = (evn) => {
|
||||||
state.erasePrevReticle();
|
state.erasePrevReticle();
|
||||||
state.erasePrevReticle = _reticle_draw(evn, state.snapToGrid);
|
state.erasePrevReticle = _reticle_draw(evn, state);
|
||||||
|
};
|
||||||
|
state.wheelcb = (evn) => {
|
||||||
|
_dream_onwheel(evn, state);
|
||||||
};
|
};
|
||||||
state.dreamcb = (evn) => {
|
state.dreamcb = (evn) => {
|
||||||
dream_generate_callback(evn, state);
|
dream_generate_callback(evn, state);
|
||||||
|
@ -619,6 +709,22 @@ const dreamTool = () =>
|
||||||
if (!state.ctxmenu) {
|
if (!state.ctxmenu) {
|
||||||
state.ctxmenu = {};
|
state.ctxmenu = {};
|
||||||
|
|
||||||
|
// Cursor Size Slider
|
||||||
|
const cursorSizeSlider = _toolbar_input.slider(
|
||||||
|
state,
|
||||||
|
"cursorSize",
|
||||||
|
"Cursor Size",
|
||||||
|
{
|
||||||
|
min: 0,
|
||||||
|
max: 2048,
|
||||||
|
step: 128,
|
||||||
|
textStep: 2,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
state.setCursorSize = cursorSizeSlider.setValue;
|
||||||
|
state.ctxmenu.cursorSizeSlider = cursorSizeSlider.slider;
|
||||||
|
|
||||||
// Snap to Grid Checkbox
|
// Snap to Grid Checkbox
|
||||||
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
|
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
|
||||||
state,
|
state,
|
||||||
|
@ -643,12 +749,14 @@ const dreamTool = () =>
|
||||||
"Overmask px",
|
"Overmask px",
|
||||||
{
|
{
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 128,
|
max: 64,
|
||||||
step: 1,
|
step: 5,
|
||||||
|
textStep: 1,
|
||||||
}
|
}
|
||||||
).slider;
|
).slider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
menu.appendChild(state.ctxmenu.cursorSizeSlider);
|
||||||
menu.appendChild(state.ctxmenu.snapToGridLabel);
|
menu.appendChild(state.ctxmenu.snapToGridLabel);
|
||||||
menu.appendChild(document.createElement("br"));
|
menu.appendChild(document.createElement("br"));
|
||||||
menu.appendChild(state.ctxmenu.invertMaskLabel);
|
menu.appendChild(state.ctxmenu.invertMaskLabel);
|
||||||
|
@ -672,6 +780,7 @@ const img2imgTool = () =>
|
||||||
|
|
||||||
// Start Listeners
|
// Start Listeners
|
||||||
mouse.listen.world.onmousemove.on(state.mousemovecb);
|
mouse.listen.world.onmousemove.on(state.mousemovecb);
|
||||||
|
mouse.listen.world.onwheel.on(state.wheelcb);
|
||||||
mouse.listen.world.btn.left.onclick.on(state.dreamcb);
|
mouse.listen.world.btn.left.onclick.on(state.dreamcb);
|
||||||
mouse.listen.world.btn.right.onclick.on(state.erasecb);
|
mouse.listen.world.btn.right.onclick.on(state.erasecb);
|
||||||
|
|
||||||
|
@ -681,6 +790,7 @@ const img2imgTool = () =>
|
||||||
(state, opt) => {
|
(state, opt) => {
|
||||||
// Clear Listeners
|
// Clear Listeners
|
||||||
mouse.listen.world.onmousemove.clear(state.mousemovecb);
|
mouse.listen.world.onmousemove.clear(state.mousemovecb);
|
||||||
|
mouse.listen.world.onwheel.clear(state.wheelcb);
|
||||||
mouse.listen.world.btn.left.onclick.clear(state.dreamcb);
|
mouse.listen.world.btn.left.onclick.clear(state.dreamcb);
|
||||||
mouse.listen.world.btn.right.onclick.clear(state.erasecb);
|
mouse.listen.world.btn.right.onclick.clear(state.erasecb);
|
||||||
|
|
||||||
|
@ -689,6 +799,11 @@ const img2imgTool = () =>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
init: (state) => {
|
init: (state) => {
|
||||||
|
state.config = {
|
||||||
|
cursorSizeScrollSpeed: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
state.cursorSize = 512;
|
||||||
state.snapToGrid = true;
|
state.snapToGrid = true;
|
||||||
state.invertMask = true;
|
state.invertMask = true;
|
||||||
state.fullResolution = false;
|
state.fullResolution = false;
|
||||||
|
@ -702,40 +817,59 @@ const img2imgTool = () =>
|
||||||
|
|
||||||
state.mousemovecb = (evn) => {
|
state.mousemovecb = (evn) => {
|
||||||
state.erasePrevReticle();
|
state.erasePrevReticle();
|
||||||
state.erasePrevReticle = _reticle_draw(evn, state.snapToGrid);
|
state.erasePrevReticle = _reticle_draw(evn, state);
|
||||||
const bb = getBoundingBox(
|
const bb = getBoundingBox(
|
||||||
evn.x,
|
evn.x,
|
||||||
evn.y,
|
evn.y,
|
||||||
basePixelCount * scaleFactor,
|
state.cursorSize,
|
||||||
basePixelCount * scaleFactor,
|
state.cursorSize,
|
||||||
state.snapToGrid && basePixelCount
|
state.snapToGrid && basePixelCount
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Resolution
|
||||||
|
const request = {
|
||||||
|
width: stableDiffusionData.width,
|
||||||
|
height: stableDiffusionData.height,
|
||||||
|
};
|
||||||
|
|
||||||
// For displaying border mask
|
// For displaying border mask
|
||||||
const auxCanvas = document.createElement("canvas");
|
const auxCanvas = document.createElement("canvas");
|
||||||
auxCanvas.width = bb.w;
|
auxCanvas.width = request.width;
|
||||||
auxCanvas.height = bb.h;
|
auxCanvas.height = request.height;
|
||||||
const auxCtx = auxCanvas.getContext("2d");
|
const auxCtx = auxCanvas.getContext("2d");
|
||||||
|
|
||||||
if (state.keepBorderSize > 0) {
|
if (state.keepBorderSize > 0) {
|
||||||
auxCtx.fillStyle = "#6A6AFF30";
|
auxCtx.fillStyle = "#6A6AFF30";
|
||||||
auxCtx.fillRect(0, 0, state.keepBorderSize, bb.h);
|
auxCtx.fillRect(0, 0, state.keepBorderSize, request.height);
|
||||||
auxCtx.fillRect(0, 0, bb.w, state.keepBorderSize);
|
auxCtx.fillRect(0, 0, request.width, state.keepBorderSize);
|
||||||
auxCtx.fillRect(
|
auxCtx.fillRect(
|
||||||
bb.w - state.keepBorderSize,
|
request.width - state.keepBorderSize,
|
||||||
0,
|
0,
|
||||||
state.keepBorderSize,
|
state.keepBorderSize,
|
||||||
bb.h
|
request.height
|
||||||
);
|
);
|
||||||
auxCtx.fillRect(
|
auxCtx.fillRect(
|
||||||
0,
|
0,
|
||||||
bb.h - state.keepBorderSize,
|
request.height - state.keepBorderSize,
|
||||||
bb.w,
|
request.width,
|
||||||
state.keepBorderSize
|
state.keepBorderSize
|
||||||
);
|
);
|
||||||
ovCtx.drawImage(auxCanvas, bb.x, bb.y);
|
ovCtx.drawImage(
|
||||||
|
auxCanvas,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
request.width,
|
||||||
|
request.height,
|
||||||
|
bb.x,
|
||||||
|
bb.y,
|
||||||
|
bb.w,
|
||||||
|
bb.h
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
state.wheelcb = (evn) => {
|
||||||
|
_dream_onwheel(evn, state);
|
||||||
|
};
|
||||||
state.dreamcb = (evn) => {
|
state.dreamcb = (evn) => {
|
||||||
dream_img2img_callback(evn, state);
|
dream_img2img_callback(evn, state);
|
||||||
};
|
};
|
||||||
|
@ -744,6 +878,23 @@ const img2imgTool = () =>
|
||||||
populateContextMenu: (menu, state) => {
|
populateContextMenu: (menu, state) => {
|
||||||
if (!state.ctxmenu) {
|
if (!state.ctxmenu) {
|
||||||
state.ctxmenu = {};
|
state.ctxmenu = {};
|
||||||
|
|
||||||
|
// Cursor Size Slider
|
||||||
|
const cursorSizeSlider = _toolbar_input.slider(
|
||||||
|
state,
|
||||||
|
"cursorSize",
|
||||||
|
"Cursor Size",
|
||||||
|
{
|
||||||
|
min: 0,
|
||||||
|
max: 2048,
|
||||||
|
step: 128,
|
||||||
|
textStep: 2,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
state.setCursorSize = cursorSizeSlider.setValue;
|
||||||
|
state.ctxmenu.cursorSizeSlider = cursorSizeSlider.slider;
|
||||||
|
|
||||||
// Snap To Grid Checkbox
|
// Snap To Grid Checkbox
|
||||||
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
|
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
|
||||||
state,
|
state,
|
||||||
|
@ -795,6 +946,7 @@ const img2imgTool = () =>
|
||||||
).slider;
|
).slider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
menu.appendChild(state.ctxmenu.cursorSizeSlider);
|
||||||
menu.appendChild(state.ctxmenu.snapToGridLabel);
|
menu.appendChild(state.ctxmenu.snapToGridLabel);
|
||||||
menu.appendChild(document.createElement("br"));
|
menu.appendChild(document.createElement("br"));
|
||||||
menu.appendChild(state.ctxmenu.invertMaskLabel);
|
menu.appendChild(state.ctxmenu.invertMaskLabel);
|
||||||
|
|
|
@ -192,8 +192,8 @@ const selectTransformTool = () =>
|
||||||
let x = evn.x;
|
let x = evn.x;
|
||||||
let y = evn.y;
|
let y = evn.y;
|
||||||
if (state.snapToGrid) {
|
if (state.snapToGrid) {
|
||||||
x += snap(evn.x, true, 64);
|
x += snap(evn.x, 0, 64);
|
||||||
y += snap(evn.y, true, 64);
|
y += snap(evn.y, 0, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update scale
|
// Update scale
|
||||||
|
@ -337,8 +337,8 @@ const selectTransformTool = () =>
|
||||||
let ix = evn.ix;
|
let ix = evn.ix;
|
||||||
let iy = evn.iy;
|
let iy = evn.iy;
|
||||||
if (state.snapToGrid) {
|
if (state.snapToGrid) {
|
||||||
ix += snap(evn.ix, true, 64);
|
ix += snap(evn.ix, 0, 64);
|
||||||
iy += snap(evn.iy, true, 64);
|
iy += snap(evn.iy, 0, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If is selected, check if drag is in handles/body and act accordingly
|
// If is selected, check if drag is in handles/body and act accordingly
|
||||||
|
@ -368,8 +368,8 @@ const selectTransformTool = () =>
|
||||||
let x = evn.x;
|
let x = evn.x;
|
||||||
let y = evn.y;
|
let y = evn.y;
|
||||||
if (state.snapToGrid) {
|
if (state.snapToGrid) {
|
||||||
x += snap(evn.x, true, 64);
|
x += snap(evn.x, 0, 64);
|
||||||
y += snap(evn.y, true, 64);
|
y += snap(evn.y, 0, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are scaling, stop scaling and do some handler magic
|
// If we are scaling, stop scaling and do some handler magic
|
||||||
|
@ -502,7 +502,6 @@ const selectTransformTool = () =>
|
||||||
if (state.useClipboard) {
|
if (state.useClipboard) {
|
||||||
// If we use the clipboard, do some proccessing of clipboard data (ugly but kind of minimum required)
|
// If we use the clipboard, do some proccessing of clipboard data (ugly but kind of minimum required)
|
||||||
navigator.clipboard.read().then((items) => {
|
navigator.clipboard.read().then((items) => {
|
||||||
console.info(items[0]);
|
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
for (const type of item.types) {
|
for (const type of item.types) {
|
||||||
if (type.startsWith("image/")) {
|
if (type.startsWith("image/")) {
|
||||||
|
|
|
@ -213,8 +213,8 @@ const stampTool = () =>
|
||||||
let x = evn.x;
|
let x = evn.x;
|
||||||
let y = evn.y;
|
let y = evn.y;
|
||||||
if (state.snapToGrid) {
|
if (state.snapToGrid) {
|
||||||
x += snap(evn.x, true, 64);
|
x += snap(evn.x, 0, 64);
|
||||||
y += snap(evn.y, true, 64);
|
y += snap(evn.y, 0, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.lastMouseMove = evn;
|
state.lastMouseMove = evn;
|
||||||
|
@ -242,8 +242,8 @@ const stampTool = () =>
|
||||||
let x = evn.x;
|
let x = evn.x;
|
||||||
let y = evn.y;
|
let y = evn.y;
|
||||||
if (state.snapToGrid) {
|
if (state.snapToGrid) {
|
||||||
x += snap(evn.x, true, 64);
|
x += snap(evn.x, 0, 64);
|
||||||
y += snap(evn.y, true, 64);
|
y += snap(evn.y, 0, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
const resource = state.selected;
|
const resource = state.selected;
|
||||||
|
|
Loading…
Reference in a new issue