2022-11-22 16:24:55 -06:00
|
|
|
const dream_generate_callback = (evn, state) => {
|
|
|
|
if (evn.target.id === "overlayCanvas" && !blockNewImages) {
|
|
|
|
const bb = getBoundingBox(
|
|
|
|
evn.x,
|
|
|
|
evn.y,
|
|
|
|
basePixelCount * scaleFactor,
|
|
|
|
basePixelCount * scaleFactor,
|
|
|
|
state.snapToGrid && basePixelCount
|
|
|
|
);
|
|
|
|
|
|
|
|
// Build request to the API
|
|
|
|
const request = {};
|
|
|
|
Object.assign(request, stableDiffusionData);
|
|
|
|
|
|
|
|
// Load prompt (maybe we should add some events so we don't have to do this)
|
|
|
|
request.prompt = document.getElementById("prompt").value;
|
|
|
|
request.negative_prompt = document.getElementById("negPrompt").value;
|
|
|
|
|
|
|
|
// Don't allow another image until is finished
|
|
|
|
blockNewImages = true;
|
|
|
|
|
|
|
|
// Setup marching ants
|
|
|
|
stopMarching = march(bb);
|
|
|
|
|
|
|
|
// 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
|
|
|
|
if (isCanvasBlank(bb.x, bb.y, bb.w, bb.h, imgCanvas)) {
|
|
|
|
// Dream
|
2022-11-22 20:06:37 -06:00
|
|
|
dream(bb.x, bb.y, request, {method: "txt2img", stopMarching, bb});
|
2022-11-22 16:24:55 -06:00
|
|
|
} else {
|
|
|
|
// Use img2img if not
|
|
|
|
|
|
|
|
// Temporary canvas for init image and mask generation
|
|
|
|
const auxCanvas = document.createElement("canvas");
|
|
|
|
auxCanvas.width = request.width;
|
|
|
|
auxCanvas.height = request.height;
|
|
|
|
const auxCtx = auxCanvas.getContext("2d");
|
|
|
|
|
|
|
|
auxCtx.fillStyle = "#000F";
|
|
|
|
|
|
|
|
// Get init image
|
|
|
|
auxCtx.fillRect(0, 0, bb.w, bb.h);
|
|
|
|
auxCtx.drawImage(imgCanvas, bb.x, bb.y, bb.w, bb.h, 0, 0, bb.w, bb.h);
|
|
|
|
request.init_images = [auxCanvas.toDataURL()];
|
|
|
|
|
|
|
|
// Get mask image
|
2022-11-26 13:34:12 -06:00
|
|
|
auxCtx.fillStyle = "#000F";
|
2022-11-22 16:24:55 -06:00
|
|
|
auxCtx.fillRect(0, 0, bb.w, bb.h);
|
2022-11-26 13:34:12 -06:00
|
|
|
if (state.invertMask) {
|
|
|
|
auxCtx.globalCompositeOperation = "destination-in";
|
|
|
|
auxCtx.drawImage(
|
|
|
|
maskPaintCanvas,
|
|
|
|
bb.x,
|
|
|
|
bb.y,
|
|
|
|
bb.w,
|
|
|
|
bb.h,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
bb.w,
|
|
|
|
bb.h
|
|
|
|
);
|
|
|
|
|
|
|
|
auxCtx.globalCompositeOperation = "destination-in";
|
|
|
|
auxCtx.drawImage(imgCanvas, bb.x, bb.y, bb.w, bb.h, 0, 0, bb.w, bb.h);
|
|
|
|
} else {
|
|
|
|
auxCtx.globalCompositeOperation = "destination-in";
|
|
|
|
auxCtx.drawImage(imgCanvas, bb.x, bb.y, bb.w, bb.h, 0, 0, bb.w, bb.h);
|
|
|
|
auxCtx.globalCompositeOperation = "destination-out";
|
|
|
|
auxCtx.drawImage(
|
|
|
|
maskPaintCanvas,
|
|
|
|
bb.x,
|
|
|
|
bb.y,
|
|
|
|
bb.w,
|
|
|
|
bb.h,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
bb.w,
|
|
|
|
bb.h
|
|
|
|
);
|
|
|
|
}
|
2022-11-22 16:24:55 -06:00
|
|
|
auxCtx.globalCompositeOperation = "destination-atop";
|
|
|
|
auxCtx.fillStyle = "#FFFF";
|
|
|
|
auxCtx.fillRect(0, 0, bb.w, bb.h);
|
2022-11-23 17:40:10 -06:00
|
|
|
var currentMask = auxCanvas.toDataURL();
|
|
|
|
request.mask =
|
2022-11-23 23:43:51 -06:00
|
|
|
state.overMaskPx > 0
|
|
|
|
? applyOvermask(auxCanvas, auxCtx, state.overMaskPx, currentMask)
|
2022-11-23 17:40:10 -06:00
|
|
|
: currentMask;
|
2022-11-22 16:24:55 -06:00
|
|
|
// Dream
|
2022-11-22 20:06:37 -06:00
|
|
|
dream(bb.x, bb.y, request, {method: "img2img", stopMarching, bb});
|
2022-11-22 16:24:55 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const dream_erase_callback = (evn, state) => {
|
|
|
|
const bb = getBoundingBox(
|
|
|
|
evn.x,
|
|
|
|
evn.y,
|
|
|
|
basePixelCount * scaleFactor,
|
|
|
|
basePixelCount * scaleFactor,
|
|
|
|
state.snapToGrid && basePixelCount
|
|
|
|
);
|
|
|
|
commands.runCommand("eraseImage", "Erase Area", bb);
|
|
|
|
};
|
2022-11-22 19:24:04 -06:00
|
|
|
|
2022-11-23 17:40:10 -06:00
|
|
|
function applyOvermask(canvas, ctx, px) {
|
|
|
|
// :badpokerface: look it might be all placebo but i like overmask lol
|
|
|
|
// yes it's crushingly inefficient i knooow :( must fix
|
|
|
|
// https://stackoverflow.com/a/30204783 was instrumental to this working or completely to blame for this disaster depending on your interpretation
|
|
|
|
const tmpOvermaskCanvas = document.createElement("canvas");
|
|
|
|
tmpOvermaskCanvas.width = canvas.width;
|
|
|
|
tmpOvermaskCanvas.height = canvas.height;
|
|
|
|
var ctxImgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
|
|
const omCtx = tmpOvermaskCanvas.getContext("2d");
|
|
|
|
omCtx.putImageData(ctxImgData, 0, 0);
|
|
|
|
for (i = 0; i < ctxImgData.data.length; i += 4) {
|
|
|
|
if (ctxImgData.data[i] == 255) {
|
|
|
|
// white pixel?
|
|
|
|
// just blotch all over the thing
|
|
|
|
var rando = Math.floor(Math.random() * px);
|
|
|
|
omCtx.beginPath();
|
|
|
|
omCtx.arc(
|
|
|
|
(i / 4) % tmpOvermaskCanvas.width,
|
|
|
|
Math.floor(i / 4 / tmpOvermaskCanvas.width),
|
2022-11-24 00:02:04 -06:00
|
|
|
scaleFactor +
|
|
|
|
rando +
|
|
|
|
(rando > scaleFactor ? rando / scaleFactor : scaleFactor / rando), // was 4 * sf + rando, too big, but i think i want it more ... random
|
2022-11-23 17:40:10 -06:00
|
|
|
0,
|
|
|
|
2 * Math.PI,
|
|
|
|
true
|
|
|
|
);
|
|
|
|
omCtx.fillStyle = "#FFFFFFFF";
|
|
|
|
omCtx.fill();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return tmpOvermaskCanvas.toDataURL();
|
|
|
|
}
|
|
|
|
|
2022-11-22 19:24:04 -06:00
|
|
|
/**
|
|
|
|
* Image to Image
|
|
|
|
*/
|
|
|
|
const dream_img2img_callback = (evn, state) => {
|
|
|
|
if (evn.target.id === "overlayCanvas" && !blockNewImages) {
|
|
|
|
const bb = getBoundingBox(
|
|
|
|
evn.x,
|
|
|
|
evn.y,
|
|
|
|
basePixelCount * scaleFactor,
|
|
|
|
basePixelCount * scaleFactor,
|
|
|
|
state.snapToGrid && basePixelCount
|
|
|
|
);
|
|
|
|
|
|
|
|
// Do nothing if no image exists
|
|
|
|
if (isCanvasBlank(bb.x, bb.y, bb.w, bb.h, imgCanvas)) return;
|
|
|
|
|
|
|
|
// Build request to the API
|
|
|
|
const request = {};
|
|
|
|
Object.assign(request, stableDiffusionData);
|
|
|
|
|
|
|
|
request.denoising_strength = state.denoisingStrength;
|
|
|
|
request.inpainting_fill = 1; // For img2img use original
|
|
|
|
|
|
|
|
// Load prompt (maybe we should add some events so we don't have to do this)
|
|
|
|
request.prompt = document.getElementById("prompt").value;
|
|
|
|
request.negative_prompt = document.getElementById("negPrompt").value;
|
|
|
|
|
|
|
|
// Don't allow another image until is finished
|
|
|
|
blockNewImages = true;
|
|
|
|
|
|
|
|
// Setup marching ants
|
|
|
|
stopMarching = march(bb);
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
// Temporary canvas for init image and mask generation
|
|
|
|
const auxCanvas = document.createElement("canvas");
|
|
|
|
auxCanvas.width = request.width;
|
|
|
|
auxCanvas.height = request.height;
|
|
|
|
const auxCtx = auxCanvas.getContext("2d");
|
|
|
|
|
|
|
|
auxCtx.fillStyle = "#000F";
|
|
|
|
|
|
|
|
// Get init image
|
|
|
|
auxCtx.fillRect(0, 0, bb.w, bb.h);
|
|
|
|
auxCtx.drawImage(imgCanvas, bb.x, bb.y, bb.w, bb.h, 0, 0, bb.w, bb.h);
|
|
|
|
request.init_images = [auxCanvas.toDataURL()];
|
|
|
|
|
|
|
|
// Get mask image
|
2022-11-26 13:34:12 -06:00
|
|
|
auxCtx.fillStyle = state.invertMask ? "#FFFF" : "#000F";
|
2022-11-22 19:24:04 -06:00
|
|
|
auxCtx.fillRect(0, 0, bb.w, bb.h);
|
|
|
|
auxCtx.globalCompositeOperation = "destination-out";
|
|
|
|
auxCtx.drawImage(maskPaintCanvas, bb.x, bb.y, bb.w, bb.h, 0, 0, bb.w, bb.h);
|
|
|
|
|
2022-11-26 13:34:12 -06:00
|
|
|
auxCtx.globalCompositeOperation = "destination-atop";
|
|
|
|
auxCtx.fillStyle = state.invertMask ? "#000F" : "#FFFF";
|
|
|
|
auxCtx.fillRect(0, 0, bb.w, bb.h);
|
|
|
|
|
2022-11-22 19:24:04 -06:00
|
|
|
// Border Mask
|
2022-11-26 13:34:12 -06:00
|
|
|
if (state.keepBorderSize > 0) {
|
|
|
|
auxCtx.globalCompositeOperation = "source-over";
|
2022-11-22 19:24:04 -06:00
|
|
|
auxCtx.fillStyle = "#000F";
|
2022-11-26 13:34:12 -06:00
|
|
|
auxCtx.fillRect(0, 0, state.keepBorderSize, bb.h);
|
|
|
|
auxCtx.fillRect(0, 0, bb.w, state.keepBorderSize);
|
2022-11-22 19:24:04 -06:00
|
|
|
auxCtx.fillRect(
|
2022-11-26 13:34:12 -06:00
|
|
|
bb.w - state.keepBorderSize,
|
2022-11-22 19:24:04 -06:00
|
|
|
0,
|
2022-11-26 13:34:12 -06:00
|
|
|
state.keepBorderSize,
|
2022-11-22 19:24:04 -06:00
|
|
|
bb.h
|
|
|
|
);
|
|
|
|
auxCtx.fillRect(
|
|
|
|
0,
|
2022-11-26 13:34:12 -06:00
|
|
|
bb.h - state.keepBorderSize,
|
2022-11-22 19:24:04 -06:00
|
|
|
bb.w,
|
2022-11-26 13:34:12 -06:00
|
|
|
state.keepBorderSize
|
2022-11-22 19:24:04 -06:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
request.mask = auxCanvas.toDataURL();
|
2022-11-26 13:59:24 -06:00
|
|
|
request.inpaint_full_res = state.fullResolution;
|
2022-11-22 19:24:04 -06:00
|
|
|
|
|
|
|
// Dream
|
2022-11-23 05:12:14 -06:00
|
|
|
dream(bb.x, bb.y, request, {method: "img2img", stopMarching, bb});
|
2022-11-22 19:24:04 -06:00
|
|
|
}
|
|
|
|
};
|
2022-11-24 09:30:13 -06:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Registers Tools
|
|
|
|
*/
|
|
|
|
const dreamTool = () =>
|
|
|
|
toolbar.registerTool(
|
|
|
|
"res/icons/image-plus.svg",
|
|
|
|
"Dream",
|
|
|
|
(state, opt) => {
|
|
|
|
// Draw new cursor immediately
|
|
|
|
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
|
|
|
state.mousemovecb({
|
|
|
|
...mouse.coords.canvas.pos,
|
|
|
|
target: {id: "overlayCanvas"},
|
|
|
|
});
|
|
|
|
|
|
|
|
// Start Listeners
|
|
|
|
mouse.listen.canvas.onmousemove.on(state.mousemovecb);
|
|
|
|
mouse.listen.canvas.left.onclick.on(state.dreamcb);
|
|
|
|
mouse.listen.canvas.right.onclick.on(state.erasecb);
|
|
|
|
},
|
|
|
|
(state, opt) => {
|
|
|
|
// Clear Listeners
|
|
|
|
mouse.listen.canvas.onmousemove.clear(state.mousemovecb);
|
|
|
|
mouse.listen.canvas.left.onclick.clear(state.dreamcb);
|
|
|
|
mouse.listen.canvas.right.onclick.clear(state.erasecb);
|
|
|
|
},
|
|
|
|
{
|
|
|
|
init: (state) => {
|
|
|
|
state.snapToGrid = true;
|
2022-11-26 13:34:12 -06:00
|
|
|
state.invertMask = false;
|
2022-11-24 09:30:13 -06:00
|
|
|
state.overMaskPx = 0;
|
|
|
|
state.mousemovecb = (evn) => _reticle_draw(evn, state.snapToGrid);
|
|
|
|
state.dreamcb = (evn) => {
|
|
|
|
dream_generate_callback(evn, state);
|
|
|
|
};
|
|
|
|
state.erasecb = (evn) => dream_erase_callback(evn, state);
|
|
|
|
},
|
|
|
|
populateContextMenu: (menu, state) => {
|
|
|
|
if (!state.ctxmenu) {
|
|
|
|
state.ctxmenu = {};
|
2022-11-26 13:34:12 -06:00
|
|
|
|
|
|
|
// Snap to Grid Checkbox
|
2022-11-24 09:30:13 -06:00
|
|
|
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
|
|
|
|
state,
|
|
|
|
"snapToGrid",
|
|
|
|
"Snap To Grid"
|
|
|
|
).label;
|
2022-11-26 13:34:12 -06:00
|
|
|
|
|
|
|
// Invert Mask Checkbox
|
|
|
|
state.ctxmenu.invertMaskLabel = _toolbar_input.checkbox(
|
|
|
|
state,
|
|
|
|
"invertMask",
|
|
|
|
"Invert Mask"
|
|
|
|
).label;
|
|
|
|
|
|
|
|
// Overmasking Slider
|
2022-11-24 09:30:13 -06:00
|
|
|
state.ctxmenu.overMaskPxLabel = _toolbar_input.slider(
|
|
|
|
state,
|
|
|
|
"overMaskPx",
|
|
|
|
"Overmask px",
|
|
|
|
0,
|
|
|
|
128,
|
|
|
|
1
|
|
|
|
).slider;
|
|
|
|
}
|
|
|
|
|
|
|
|
menu.appendChild(state.ctxmenu.snapToGridLabel);
|
|
|
|
menu.appendChild(document.createElement("br"));
|
2022-11-26 13:34:12 -06:00
|
|
|
menu.appendChild(state.ctxmenu.invertMaskLabel);
|
|
|
|
menu.appendChild(document.createElement("br"));
|
2022-11-24 09:30:13 -06:00
|
|
|
menu.appendChild(state.ctxmenu.overMaskPxLabel);
|
|
|
|
},
|
|
|
|
shortcut: "D",
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
const img2imgTool = () =>
|
|
|
|
toolbar.registerTool(
|
|
|
|
"res/icons/image.svg",
|
|
|
|
"Img2Img",
|
|
|
|
(state, opt) => {
|
|
|
|
// Draw new cursor immediately
|
|
|
|
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
|
|
|
state.mousemovecb({
|
|
|
|
...mouse.coords.canvas.pos,
|
|
|
|
target: {id: "overlayCanvas"},
|
|
|
|
});
|
|
|
|
|
|
|
|
// Start Listeners
|
|
|
|
mouse.listen.canvas.onmousemove.on(state.mousemovecb);
|
|
|
|
mouse.listen.canvas.left.onclick.on(state.dreamcb);
|
|
|
|
mouse.listen.canvas.right.onclick.on(state.erasecb);
|
|
|
|
},
|
|
|
|
(state, opt) => {
|
|
|
|
// Clear Listeners
|
|
|
|
mouse.listen.canvas.onmousemove.clear(state.mousemovecb);
|
|
|
|
mouse.listen.canvas.left.onclick.clear(state.dreamcb);
|
|
|
|
mouse.listen.canvas.right.onclick.clear(state.erasecb);
|
|
|
|
},
|
|
|
|
{
|
|
|
|
init: (state) => {
|
|
|
|
state.snapToGrid = true;
|
2022-11-26 13:34:12 -06:00
|
|
|
state.invertMask = true;
|
2022-11-26 13:59:24 -06:00
|
|
|
state.fullResolution = false;
|
2022-11-26 13:34:12 -06:00
|
|
|
|
2022-11-24 09:30:13 -06:00
|
|
|
state.denoisingStrength = 0.7;
|
|
|
|
|
2022-11-26 13:34:12 -06:00
|
|
|
state.keepBorderSize = 64;
|
2022-11-24 09:30:13 -06:00
|
|
|
|
|
|
|
state.mousemovecb = (evn) => {
|
|
|
|
_reticle_draw(evn, state.snapToGrid);
|
2022-11-25 16:39:38 -06:00
|
|
|
if (evn.target.id === "overlayCanvas") {
|
|
|
|
const bb = getBoundingBox(
|
|
|
|
evn.x,
|
|
|
|
evn.y,
|
|
|
|
basePixelCount * scaleFactor,
|
|
|
|
basePixelCount * scaleFactor,
|
|
|
|
state.snapToGrid && basePixelCount
|
2022-11-24 09:30:13 -06:00
|
|
|
);
|
|
|
|
|
2022-11-25 16:39:38 -06:00
|
|
|
// For displaying border mask
|
|
|
|
const auxCanvas = document.createElement("canvas");
|
|
|
|
auxCanvas.width = bb.w;
|
|
|
|
auxCanvas.height = bb.h;
|
|
|
|
const auxCtx = auxCanvas.getContext("2d");
|
|
|
|
|
2022-11-26 13:34:12 -06:00
|
|
|
if (state.keepBorderSize > 0) {
|
|
|
|
auxCtx.fillStyle = "#6A6AFF50";
|
|
|
|
auxCtx.fillRect(0, 0, state.keepBorderSize, bb.h);
|
|
|
|
auxCtx.fillRect(0, 0, bb.w, state.keepBorderSize);
|
2022-11-25 16:39:38 -06:00
|
|
|
auxCtx.fillRect(
|
2022-11-26 13:34:12 -06:00
|
|
|
bb.w - state.keepBorderSize,
|
2022-11-25 16:39:38 -06:00
|
|
|
0,
|
2022-11-26 13:34:12 -06:00
|
|
|
state.keepBorderSize,
|
2022-11-25 16:39:38 -06:00
|
|
|
bb.h
|
|
|
|
);
|
|
|
|
auxCtx.fillRect(
|
|
|
|
0,
|
2022-11-26 13:34:12 -06:00
|
|
|
bb.h - state.keepBorderSize,
|
2022-11-25 16:39:38 -06:00
|
|
|
bb.w,
|
2022-11-26 13:34:12 -06:00
|
|
|
state.keepBorderSize
|
2022-11-25 16:39:38 -06:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const tmp = ovCtx.globalAlpha;
|
|
|
|
ovCtx.globalAlpha = 0.4;
|
|
|
|
ovCtx.drawImage(auxCanvas, bb.x, bb.y);
|
|
|
|
ovCtx.globalAlpha = tmp;
|
|
|
|
}
|
2022-11-24 09:30:13 -06:00
|
|
|
};
|
|
|
|
state.dreamcb = (evn) => {
|
|
|
|
dream_img2img_callback(evn, state);
|
|
|
|
};
|
|
|
|
state.erasecb = (evn) => dream_erase_callback(evn, state);
|
|
|
|
},
|
|
|
|
populateContextMenu: (menu, state) => {
|
|
|
|
if (!state.ctxmenu) {
|
|
|
|
state.ctxmenu = {};
|
|
|
|
// Snap To Grid Checkbox
|
|
|
|
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
|
|
|
|
state,
|
|
|
|
"snapToGrid",
|
|
|
|
"Snap To Grid"
|
|
|
|
).label;
|
|
|
|
|
2022-11-26 13:34:12 -06:00
|
|
|
// Invert Mask Checkbox
|
|
|
|
state.ctxmenu.invertMaskLabel = _toolbar_input.checkbox(
|
|
|
|
state,
|
|
|
|
"invertMask",
|
|
|
|
"Invert Mask"
|
|
|
|
).label;
|
|
|
|
|
2022-11-26 13:59:24 -06:00
|
|
|
// Inpaint Full Resolution Checkbox
|
|
|
|
state.ctxmenu.fullResolutionLabel = _toolbar_input.checkbox(
|
|
|
|
state,
|
|
|
|
"fullResolution",
|
|
|
|
"Inpaint Full Resolution"
|
|
|
|
).label;
|
|
|
|
|
2022-11-24 09:30:13 -06:00
|
|
|
// Denoising Strength Slider
|
|
|
|
state.ctxmenu.denoisingStrengthSlider = _toolbar_input.slider(
|
|
|
|
state,
|
|
|
|
"denoisingStrength",
|
|
|
|
"Denoising Strength",
|
|
|
|
0,
|
|
|
|
1,
|
|
|
|
0.05
|
|
|
|
).slider;
|
|
|
|
|
|
|
|
// Border Mask Size Slider
|
|
|
|
state.ctxmenu.borderMaskSlider = _toolbar_input.slider(
|
|
|
|
state,
|
2022-11-26 13:34:12 -06:00
|
|
|
"keepBorderSize",
|
|
|
|
"Keep Border Size",
|
2022-11-24 09:30:13 -06:00
|
|
|
0,
|
|
|
|
128,
|
|
|
|
1
|
|
|
|
).slider;
|
|
|
|
}
|
|
|
|
|
|
|
|
menu.appendChild(state.ctxmenu.snapToGridLabel);
|
|
|
|
menu.appendChild(document.createElement("br"));
|
2022-11-26 13:34:12 -06:00
|
|
|
menu.appendChild(state.ctxmenu.invertMaskLabel);
|
|
|
|
menu.appendChild(document.createElement("br"));
|
2022-11-26 13:59:24 -06:00
|
|
|
menu.appendChild(state.ctxmenu.fullResolutionLabel);
|
|
|
|
menu.appendChild(document.createElement("br"));
|
2022-11-24 09:30:13 -06:00
|
|
|
menu.appendChild(state.ctxmenu.denoisingStrengthSlider);
|
|
|
|
menu.appendChild(state.ctxmenu.borderMaskSlider);
|
|
|
|
},
|
|
|
|
shortcut: "I",
|
|
|
|
}
|
|
|
|
);
|