no more scale factor, support for dream cursor size

Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
This commit is contained in:
Victor Seiji Hariki 2022-12-03 09:53:12 -03:00
parent 4681113f81
commit 59e783d159
6 changed files with 272 additions and 123 deletions

View file

@ -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 />

View file

@ -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);
let cb = (v) => {
stableDiffusionData[lsKey] = v;
if (lsKey) localStorage.setItem(lsKey, v);
};
if (valuecb) {
cb = (v) => {
valuecb(v);
localStorage.setItem(lsKey, v);
};
}
return createSlider(label, el, { return createSlider(label, el, {
valuecb: valuecb: cb,
valuecb ||
((v) => {
stableDiffusionData[lsKey] = v;
localStorage.setItem(lsKey, v);
}),
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);

View file

@ -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),

View file

@ -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);

View file

@ -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/")) {

View file

@ -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;