diff --git a/index.html b/index.html
index 6d0955c..317521b 100644
--- a/index.html
+++ b/index.html
@@ -86,16 +86,15 @@
value="-1"
step="1" />
+
+
+
-
-
-
-
diff --git a/js/index.js b/js/index.js
index 7828b35..da9d063 100644
--- a/js/index.js
+++ b/js/index.js
@@ -60,7 +60,6 @@ var frameX = 512;
var frameY = 512;
var drawThis = {};
const basePixelCount = 64; //64 px - ALWAYS 64 PX
-var scaleFactor = 8; //x64 px
var snapToGrid = true;
var backupMaskPaintCanvas; //???
var backupMaskPaintCtx; //...? look i am bad at this
@@ -117,7 +116,6 @@ function startup() {
changeSeed();
changeOverMaskPx();
changeHiResFix();
- document.getElementById("scaleFactor").value = scaleFactor;
}
/**
@@ -497,15 +495,20 @@ const makeSlider = (
textStep = null,
valuecb = null
) => {
- const local = localStorage.getItem(lsKey);
+ const local = lsKey && localStorage.getItem(lsKey);
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, {
- valuecb:
- valuecb ||
- ((v) => {
- stableDiffusionData[lsKey] = v;
- localStorage.setItem(lsKey, v);
- }),
+ valuecb: cb,
min,
max,
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(
"CFG Scale",
document.getElementById("cfgScale"),
@@ -542,19 +560,6 @@ makeSlider(
1,
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);
diff --git a/js/lib/util.js b/js/lib/util.js
index 6dd4c9a..5fa5f6d 100644
--- a/js/lib/util.js
+++ b/js/lib/util.js
@@ -145,21 +145,14 @@ function makeWriteOnce(obj, name = "write-once object", exceptions = []) {
* Snaps a single value to an infinite grid
*
* @param {number} i Original value to be snapped
- * @param {boolean} scaled If grid will change alignment for odd scaleFactor values (default: true)
- * @param {number} gridSize Size of the grid
+ * @param {number} [offset=0] Value to offset the grid. Should be in the rande [0, gridSize[
+ * @param {number} [gridSize=64] Size of the grid
* @returns an offset, in which [i + offset = (a location snapped to the grid)]
*/
-function snap(i, scaled = true, gridSize = 64) {
- // 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 = gridSize / 2;
- }
- }
- const modulus = i % gridSize;
- var snapOffset = modulus - scaleOffset;
+function snap(i, offset = 0, gridSize = 64) {
+ const modulus = (i - offset) % gridSize;
+ var snapOffset = modulus;
+
if (modulus > gridSize / 2) snapOffset = modulus - gridSize;
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} w - the width 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)
*/
-function getBoundingBox(cx, cy, w, h, gridSnap = null) {
- const offset = {x: 0, y: 0};
+function getBoundingBox(cx, cy, w, h, gridSnap = null, offset = 0) {
+ const offs = {x: 0, y: 0};
const box = {x: 0, y: 0};
if (gridSnap) {
- offset.x = snap(cx, true, gridSnap);
- offset.y = snap(cy, true, gridSnap);
+ offs.x = snap(cx, offset, gridSnap);
+ offs.y = snap(cy, offset, gridSnap);
}
- box.x = offset.x + cx;
- box.y = offset.y + cy;
+ box.x = offs.x + cx;
+ box.y = offs.y + cy;
return {
x: Math.floor(box.x - w / 2),
diff --git a/js/ui/tool/dream.js b/js/ui/tool/dream.js
index 568c7d6..0281a47 100644
--- a/js/ui/tool/dream.js
+++ b/js/ui/tool/dream.js
@@ -125,13 +125,27 @@ const _generate = async (endpoint, request, bb) => {
image.src = "data:image/png;base64," + images[at];
image.addEventListener("load", () => {
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);
// First Dream Run
+ console.info(`[dream] Generating images for prompt '${request.prompt}'`);
+ console.debug(request);
+
let stopProgress = _monitorProgress(bb);
images.push(...(await _dream(endpoint, requestCopy)));
stopProgress();
@@ -161,6 +175,8 @@ const _generate = async (endpoint, request, bb) => {
commands.runCommand("drawImage", "Image Dream", {
x: bb.x,
y: bb.y,
+ w: bb.w,
+ h: bb.h,
image: img,
});
clean(true);
@@ -316,8 +332,8 @@ const dream_generate_callback = async (evn, state) => {
const bb = getBoundingBox(
evn.x,
evn.y,
- basePixelCount * scaleFactor,
- basePixelCount * scaleFactor,
+ state.cursorSize,
+ state.cursorSize,
state.snapToGrid && basePixelCount
);
@@ -332,13 +348,6 @@ const dream_generate_callback = async (evn, state) => {
// Don't allow another image until is finished
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
if (isCanvasBlank(bb.x, bb.y, bb.w, bb.h, imgCanvas)) {
// Dream
@@ -355,13 +364,23 @@ const dream_generate_callback = async (evn, state) => {
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);
+ auxCtx.fillRect(0, 0, request.width, request.height);
+ auxCtx.drawImage(
+ imgCanvas,
+ bb.x,
+ bb.y,
+ bb.w,
+ bb.h,
+ 0,
+ 0,
+ request.width,
+ request.height
+ );
request.init_images = [auxCanvas.toDataURL()];
// Get mask image
auxCtx.fillStyle = "#000F";
- auxCtx.fillRect(0, 0, bb.w, bb.h);
+ auxCtx.fillRect(0, 0, request.width, request.height);
if (state.invertMask) {
// 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
@@ -374,23 +393,45 @@ const dream_generate_callback = async (evn, state) => {
bb.h,
0,
0,
- bb.w,
- bb.h
+ request.width,
+ request.height
);
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 {
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
// 99% of my issues were from failing to set source-over for the overmask blotches
if (state.overMaskPx > 0) {
// transparent to white first
auxCtx.globalCompositeOperation = "destination-atop";
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);
+ downloadCanvas({canvas: auxCanvas, filename: null});
}
auxCtx.globalCompositeOperation = "destination-out"; // ???
@@ -402,13 +443,13 @@ const dream_generate_callback = async (evn, state) => {
bb.h,
0,
0,
- bb.w,
- bb.h
+ request.width,
+ request.height
);
}
auxCtx.globalCompositeOperation = "destination-atop";
auxCtx.fillStyle = "#FFFF";
- auxCtx.fillRect(0, 0, bb.w, bb.h);
+ auxCtx.fillRect(0, 0, request.width, request.height);
request.mask = auxCanvas.toDataURL();
// Dream
_generate("img2img", request, bb);
@@ -419,8 +460,8 @@ const dream_erase_callback = (evn, state) => {
const bb = getBoundingBox(
evn.x,
evn.y,
- basePixelCount * scaleFactor,
- basePixelCount * scaleFactor,
+ state.cursorSize,
+ state.cursorSize,
state.snapToGrid && basePixelCount
);
commands.runCommand("eraseImage", "Erase Area", bb);
@@ -436,14 +477,25 @@ function applyOvermask(canvas, ctx, px) {
if (ctxImgData.data[i] == 255) {
// white pixel?
// 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.arc(
(i / 4) % canvas.width,
Math.floor(i / 4 / canvas.width),
- scaleFactor +
- rando +
- (rando > scaleFactor ? rando / scaleFactor : scaleFactor / rando), // was 4 * sf + rando, too big, but i think i want it more ... random
+ rando, // was 4 * sf + rando, too big, but i think i want it more ... random
0,
2 * Math.PI,
true
@@ -462,8 +514,8 @@ const dream_img2img_callback = (evn, state) => {
const bb = getBoundingBox(
evn.x,
evn.y,
- basePixelCount * scaleFactor,
- basePixelCount * scaleFactor,
+ state.cursorSize,
+ state.cursorSize,
state.snapToGrid && basePixelCount
);
@@ -484,13 +536,6 @@ const dream_img2img_callback = (evn, state) => {
// Don't allow another image until is finished
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
// Temporary canvas for init image and mask generation
@@ -502,36 +547,56 @@ const dream_img2img_callback = (evn, state) => {
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);
+ auxCtx.fillRect(0, 0, request.width, request.height);
+ auxCtx.drawImage(
+ imgCanvas,
+ bb.x,
+ bb.y,
+ bb.w,
+ bb.h,
+ 0,
+ 0,
+ request.width,
+ request.height
+ );
request.init_images = [auxCanvas.toDataURL()];
// Get mask image
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.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.fillStyle = state.invertMask ? "#000F" : "#FFFF";
- auxCtx.fillRect(0, 0, bb.w, bb.h);
+ auxCtx.fillRect(0, 0, request.width, request.height);
// Border Mask
if (state.keepBorderSize > 0) {
auxCtx.globalCompositeOperation = "source-over";
auxCtx.fillStyle = "#000F";
- auxCtx.fillRect(0, 0, state.keepBorderSize, bb.h);
- auxCtx.fillRect(0, 0, bb.w, state.keepBorderSize);
+ auxCtx.fillRect(0, 0, state.keepBorderSize, request.height);
+ auxCtx.fillRect(0, 0, request.width, state.keepBorderSize);
auxCtx.fillRect(
- bb.w - state.keepBorderSize,
+ request.width - state.keepBorderSize,
0,
state.keepBorderSize,
- bb.h
+ request.height
);
auxCtx.fillRect(
0,
- bb.h - state.keepBorderSize,
- bb.w,
+ request.height - state.keepBorderSize,
+ request.width,
state.keepBorderSize
);
}
@@ -547,13 +612,13 @@ const dream_img2img_callback = (evn, state) => {
/**
* Dream and img2img tools
*/
-const _reticle_draw = (evn, snapToGrid = true) => {
+const _reticle_draw = (evn, state) => {
const bb = getBoundingBox(
evn.x,
evn.y,
- basePixelCount * scaleFactor,
- basePixelCount * scaleFactor,
- snapToGrid && basePixelCount
+ state.cursorSize,
+ state.cursorSize,
+ state.snapToGrid && basePixelCount
);
// 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
*/
@@ -582,6 +661,7 @@ const dreamTool = () =>
// Start Listeners
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.right.onclick.on(state.erasecb);
@@ -591,6 +671,7 @@ const dreamTool = () =>
(state, opt) => {
// Clear Listeners
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.right.onclick.clear(state.erasecb);
@@ -599,6 +680,12 @@ const dreamTool = () =>
},
{
init: (state) => {
+ state.config = {
+ cursorSizeScrollSpeed: 1,
+ };
+
+ state.cursorSize = 512;
+
state.snapToGrid = true;
state.invertMask = false;
state.overMaskPx = 0;
@@ -608,7 +695,10 @@ const dreamTool = () =>
state.mousemovecb = (evn) => {
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) => {
dream_generate_callback(evn, state);
@@ -619,6 +709,22 @@ const dreamTool = () =>
if (!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
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
state,
@@ -643,12 +749,14 @@ const dreamTool = () =>
"Overmask px",
{
min: 0,
- max: 128,
- step: 1,
+ max: 64,
+ step: 5,
+ textStep: 1,
}
).slider;
}
+ menu.appendChild(state.ctxmenu.cursorSizeSlider);
menu.appendChild(state.ctxmenu.snapToGridLabel);
menu.appendChild(document.createElement("br"));
menu.appendChild(state.ctxmenu.invertMaskLabel);
@@ -672,6 +780,7 @@ const img2imgTool = () =>
// Start Listeners
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.right.onclick.on(state.erasecb);
@@ -681,6 +790,7 @@ const img2imgTool = () =>
(state, opt) => {
// Clear Listeners
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.right.onclick.clear(state.erasecb);
@@ -689,6 +799,11 @@ const img2imgTool = () =>
},
{
init: (state) => {
+ state.config = {
+ cursorSizeScrollSpeed: 1,
+ };
+
+ state.cursorSize = 512;
state.snapToGrid = true;
state.invertMask = true;
state.fullResolution = false;
@@ -702,40 +817,59 @@ const img2imgTool = () =>
state.mousemovecb = (evn) => {
state.erasePrevReticle();
- state.erasePrevReticle = _reticle_draw(evn, state.snapToGrid);
+ state.erasePrevReticle = _reticle_draw(evn, state);
const bb = getBoundingBox(
evn.x,
evn.y,
- basePixelCount * scaleFactor,
- basePixelCount * scaleFactor,
+ state.cursorSize,
+ state.cursorSize,
state.snapToGrid && basePixelCount
);
+ // Resolution
+ const request = {
+ width: stableDiffusionData.width,
+ height: stableDiffusionData.height,
+ };
+
// For displaying border mask
const auxCanvas = document.createElement("canvas");
- auxCanvas.width = bb.w;
- auxCanvas.height = bb.h;
+ auxCanvas.width = request.width;
+ auxCanvas.height = request.height;
const auxCtx = auxCanvas.getContext("2d");
if (state.keepBorderSize > 0) {
auxCtx.fillStyle = "#6A6AFF30";
- auxCtx.fillRect(0, 0, state.keepBorderSize, bb.h);
- auxCtx.fillRect(0, 0, bb.w, state.keepBorderSize);
+ auxCtx.fillRect(0, 0, state.keepBorderSize, request.height);
+ auxCtx.fillRect(0, 0, request.width, state.keepBorderSize);
auxCtx.fillRect(
- bb.w - state.keepBorderSize,
+ request.width - state.keepBorderSize,
0,
state.keepBorderSize,
- bb.h
+ request.height
);
auxCtx.fillRect(
0,
- bb.h - state.keepBorderSize,
- bb.w,
+ request.height - state.keepBorderSize,
+ request.width,
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) => {
dream_img2img_callback(evn, state);
};
@@ -744,6 +878,23 @@ const img2imgTool = () =>
populateContextMenu: (menu, state) => {
if (!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
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
state,
@@ -795,6 +946,7 @@ const img2imgTool = () =>
).slider;
}
+ menu.appendChild(state.ctxmenu.cursorSizeSlider);
menu.appendChild(state.ctxmenu.snapToGridLabel);
menu.appendChild(document.createElement("br"));
menu.appendChild(state.ctxmenu.invertMaskLabel);
diff --git a/js/ui/tool/select.js b/js/ui/tool/select.js
index 92e667b..bdf8cfa 100644
--- a/js/ui/tool/select.js
+++ b/js/ui/tool/select.js
@@ -192,8 +192,8 @@ const selectTransformTool = () =>
let x = evn.x;
let y = evn.y;
if (state.snapToGrid) {
- x += snap(evn.x, true, 64);
- y += snap(evn.y, true, 64);
+ x += snap(evn.x, 0, 64);
+ y += snap(evn.y, 0, 64);
}
// Update scale
@@ -337,8 +337,8 @@ const selectTransformTool = () =>
let ix = evn.ix;
let iy = evn.iy;
if (state.snapToGrid) {
- ix += snap(evn.ix, true, 64);
- iy += snap(evn.iy, true, 64);
+ ix += snap(evn.ix, 0, 64);
+ iy += snap(evn.iy, 0, 64);
}
// If is selected, check if drag is in handles/body and act accordingly
@@ -368,8 +368,8 @@ const selectTransformTool = () =>
let x = evn.x;
let y = evn.y;
if (state.snapToGrid) {
- x += snap(evn.x, true, 64);
- y += snap(evn.y, true, 64);
+ x += snap(evn.x, 0, 64);
+ y += snap(evn.y, 0, 64);
}
// If we are scaling, stop scaling and do some handler magic
@@ -502,7 +502,6 @@ const selectTransformTool = () =>
if (state.useClipboard) {
// If we use the clipboard, do some proccessing of clipboard data (ugly but kind of minimum required)
navigator.clipboard.read().then((items) => {
- console.info(items[0]);
for (const item of items) {
for (const type of item.types) {
if (type.startsWith("image/")) {
diff --git a/js/ui/tool/stamp.js b/js/ui/tool/stamp.js
index adc21c5..705991f 100644
--- a/js/ui/tool/stamp.js
+++ b/js/ui/tool/stamp.js
@@ -213,8 +213,8 @@ const stampTool = () =>
let x = evn.x;
let y = evn.y;
if (state.snapToGrid) {
- x += snap(evn.x, true, 64);
- y += snap(evn.y, true, 64);
+ x += snap(evn.x, 0, 64);
+ y += snap(evn.y, 0, 64);
}
state.lastMouseMove = evn;
@@ -242,8 +242,8 @@ const stampTool = () =>
let x = evn.x;
let y = evn.y;
if (state.snapToGrid) {
- x += snap(evn.x, true, 64);
- y += snap(evn.y, true, 64);
+ x += snap(evn.x, 0, 64);
+ y += snap(evn.y, 0, 64);
}
const resource = state.selected;