From d6e2439418a398784c32e34581917fb359b356be Mon Sep 17 00:00:00 2001
From: jamz-uk <222520099+jamz-uk@users.noreply.github.com>
Date: Fri, 22 Aug 2025 20:13:24 +0100
Subject: [PATCH] Update Auto-Image.js
---
Auto-Image.js | 143 +++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 112 insertions(+), 31 deletions(-)
diff --git a/Auto-Image.js b/Auto-Image.js
index 1c8527c..f34a371 100644
--- a/Auto-Image.js
+++ b/Auto-Image.js
@@ -987,6 +987,7 @@
customWhiteThreshold: CONFIG.WHITE_THRESHOLD,
resizeSettings: null,
originalImage: null,
+ resizeIgnoreMask: null,
}
// Placeholder for the resize preview update function
@@ -3368,13 +3369,16 @@
overflow: auto;
}
- .resize-preview {
- max-width: none;
- transition: transform 0.1s ease;
+ .resize-canvas-stack { position: relative; transform-origin: top left; }
+ .resize-base-canvas, .resize-mask-canvas {
+ position: absolute; left: 0; top: 0;
image-rendering: pixelated;
image-rendering: -moz-crisp-edges;
image-rendering: crisp-edges;
}
+ .resize-mask-canvas { pointer-events: auto; }
+ .resize-tools { display:flex; gap:8px; align-items:center; margin-top:8px; font-size:12px; }
+ .resize-tools button { padding:6px 10px; border-radius:6px; border:1px solid rgba(255,255,255,0.2); background: rgba(255,255,255,0.06); color:#fff; cursor:pointer; }
.resize-controls {
display: grid;
@@ -4382,7 +4386,14 @@
-
![Resized image preview will appear here.]()
+
+
+
+
+
+
+
+ Click or drag on the preview to toggle pixels to NOT paint.
@@ -4747,11 +4758,16 @@
const heightValue = resizeContainer.querySelector("#heightValue")
const keepAspect = resizeContainer.querySelector("#keepAspect")
const paintWhiteToggle = resizeContainer.querySelector("#paintWhiteToggle");
- const zoomSlider = resizeContainer.querySelector("#zoomSlider");
- const resizePreview = resizeContainer.querySelector("#resizePreview")
+ const zoomSlider = resizeContainer.querySelector("#zoomSlider");
+ const canvasStack = resizeContainer.querySelector('#resizeCanvasStack');
+ const baseCanvas = resizeContainer.querySelector('#resizeCanvas');
+ const maskCanvas = resizeContainer.querySelector('#maskCanvas');
+ const baseCtx = baseCanvas.getContext('2d');
+ const maskCtx = maskCanvas.getContext('2d');
const confirmResize = resizeContainer.querySelector("#confirmResize")
const cancelResize = resizeContainer.querySelector("#cancelResize")
- const downloadPreviewBtn = resizeContainer.querySelector("#downloadPreviewBtn");
+ const downloadPreviewBtn = resizeContainer.querySelector("#downloadPreviewBtn");
+ const clearIgnoredBtn = resizeContainer.querySelector('#clearIgnoredBtn');
if (compactBtn) {
compactBtn.addEventListener("click", () => {
@@ -5034,35 +5050,47 @@
let _previewTimer = null;
let _previewJobId = 0;
let _isDraggingSize = false;
+ let _zoomLevel = 1;
+ const ensureMaskSize = (w, h) => {
+ if (!state.resizeIgnoreMask || state.resizeIgnoreMask.length !== w * h) {
+ state.resizeIgnoreMask = new Uint8Array(w * h);
+ }
+ baseCanvas.width = w; baseCanvas.height = h;
+ maskCanvas.width = w; maskCanvas.height = h;
+ maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height);
+ };
_updateResizePreview = async () => {
const jobId = ++_previewJobId;
const newWidth = parseInt(widthSlider.value, 10);
const newHeight = parseInt(heightSlider.value, 10);
- const zoomLevel = parseFloat(zoomSlider.value);
+ _zoomLevel = parseFloat(zoomSlider.value);
widthValue.textContent = newWidth;
heightValue.textContent = newHeight;
- const tempCanvas = document.createElement('canvas');
- const tempCtx = tempCanvas.getContext('2d');
- tempCanvas.width = newWidth;
- tempCanvas.height = newHeight;
- tempCtx.imageSmoothingEnabled = false;
+ ensureMaskSize(newWidth, newHeight);
+ baseCtx.imageSmoothingEnabled = false;
if (!state.availableColors || state.availableColors.length === 0) {
if (baseProcessor !== processor && (!baseProcessor.img || !baseProcessor.canvas)) {
await baseProcessor.load();
}
- tempCtx.drawImage(baseProcessor.img, 0, 0, newWidth, newHeight);
- resizePreview.src = tempCanvas.toDataURL();
- resizePreview.style.transform = `scale(${zoomLevel})`;
+ baseCtx.clearRect(0,0,newWidth,newHeight);
+ baseCtx.drawImage(baseProcessor.img, 0, 0, newWidth, newHeight);
+ if (state.resizeIgnoreMask) {
+ const imgData = baseCtx.getImageData(0,0,newWidth,newHeight);
+ const m = state.resizeIgnoreMask;
+ for (let i=0;i {
@@ -5176,8 +5208,8 @@
// Zoom only affects CSS transform; avoid recomputing pixels
zoomSlider.addEventListener('input', () => {
- const zoomLevel = parseFloat(zoomSlider.value);
- resizePreview.style.transform = `scale(${zoomLevel})`;
+ _zoomLevel = parseFloat(zoomSlider.value);
+ canvasStack.style.transform = `scale(${_zoomLevel})`;
});
const schedulePreview = () => {
if (_previewTimer) clearTimeout(_previewTimer);
@@ -5201,6 +5233,33 @@
widthSlider.addEventListener("input", () => { onWidthInput(); schedulePreview(); });
heightSlider.addEventListener("input", () => { onHeightInput(); schedulePreview(); });
+ // Click/drag to toggle ignored pixels
+ let draggingMask = false;
+ const pointToggle = (clientX, clientY) => {
+ const rect = baseCanvas.getBoundingClientRect();
+ const x = Math.floor((clientX - rect.left) / parseFloat(zoomSlider.value));
+ const y = Math.floor((clientY - rect.top) / parseFloat(zoomSlider.value));
+ const w = baseCanvas.width, h = baseCanvas.height;
+ if (x < 0 || y < 0 || x >= w || y >= h) return;
+ const idx = y * w + x;
+ if (!state.resizeIgnoreMask || state.resizeIgnoreMask.length !== w * h) {
+ state.resizeIgnoreMask = new Uint8Array(w * h);
+ }
+ state.resizeIgnoreMask[idx] = state.resizeIgnoreMask[idx] ? 0 : 1;
+ // quick visual hint by updating a pixel in mask canvas
+ const color = state.resizeIgnoreMask[idx] ? 'rgba(255,0,0,0.6)' : 'rgba(0,0,0,0)';
+ if (state.resizeIgnoreMask[idx]) {
+ maskCtx.fillStyle = color; maskCtx.fillRect(x, y, 1, 1);
+ } else {
+ maskCtx.clearRect(x, y, 1, 1);
+ }
+ };
+ maskCanvas.addEventListener('mousedown', (e) => { draggingMask = true; pointToggle(e.clientX, e.clientY); _updateResizePreview(); });
+ window.addEventListener('mousemove', (e) => { if (draggingMask) { pointToggle(e.clientX, e.clientY); } });
+ window.addEventListener('mouseup', () => { if (draggingMask) { draggingMask = false; saveBotSettings(); } });
+ const clearIgnoredBtnEl = resizeContainer.querySelector('#clearIgnoredBtn');
+ if (clearIgnoredBtnEl) clearIgnoredBtnEl.addEventListener('click', () => { if (state.resizeIgnoreMask) state.resizeIgnoreMask.fill(0); maskCtx.clearRect(0,0,maskCanvas.width,maskCanvas.height); _updateResizePreview(); saveBotSettings(); });
+
confirmResize.onclick = async () => {
const newWidth = parseInt(widthSlider.value, 10);
const newHeight = parseInt(heightSlider.value, 10);
@@ -5215,10 +5274,11 @@
await baseProcessor.load();
}
tempCtx.drawImage(baseProcessor.img, 0, 0, newWidth, newHeight);
- const imgData = tempCtx.getImageData(0, 0, newWidth, newHeight);
+ const imgData = tempCtx.getImageData(0, 0, newWidth, newHeight);
const data = imgData.data;
const tThresh2 = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD;
let totalValidPixels = 0;
+ const mask = (state.resizeIgnoreMask && state.resizeIgnoreMask.length === newWidth * newHeight) ? state.resizeIgnoreMask : null;
const applyFSDitherFinal = async () => {
const w = newWidth, h = newHeight;
@@ -5230,7 +5290,8 @@
const idx = y * w + x;
const i4 = idx * 4;
const r = data[i4], g = data[i4 + 1], b = data[i4 + 2], a = data[i4 + 3];
- const isEligible = a >= tThresh2 && (state.paintWhitePixels || !Utils.isWhitePixel(r, g, b));
+ const masked = mask && mask[idx];
+ const isEligible = !masked && a >= tThresh2 && (state.paintWhitePixels || !Utils.isWhitePixel(r, g, b));
eligible[idx] = isEligible ? 1 : 0;
work[idx * 3] = r;
work[idx * 3 + 1] = g;
@@ -5286,7 +5347,8 @@
} else {
for (let i = 0; i < data.length; i += 4) {
const r = data[i], g = data[i + 1], b = data[i + 2], a = data[i + 3];
- const isTransparent = a < tThresh2;
+ const masked = mask && mask[(i>>2)];
+ const isTransparent = a < tThresh2 || masked;
const isWhiteAndSkipped = !state.paintWhitePixels && Utils.isWhitePixel(r, g, b);
if (isTransparent || isWhiteAndSkipped) {
data[i + 3] = 0; // overlay transparency
@@ -5328,10 +5390,12 @@
};
downloadPreviewBtn.onclick = () => {
- const link = document.createElement('a');
- link.download = 'wplace-preview.png';
- link.href = resizePreview.src;
- link.click();
+ try {
+ const link = document.createElement('a');
+ link.download = 'wplace-preview.png';
+ link.href = baseCanvas.toDataURL();
+ link.click();
+ } catch (e) { console.warn('Failed to download preview:', e); }
};
cancelResize.onclick = closeResizeDialog;
@@ -5409,6 +5473,8 @@
state.lastPosition = { x: 0, y: 0 }
// New image: clear previous resize settings
state.resizeSettings = null;
+ // Also clear any previous ignore mask
+ state.resizeIgnoreMask = null;
// Save original image for this browser (dataUrl + dims)
state.originalImage = { dataUrl: imageSrc, width, height };
saveBotSettings();
@@ -5994,6 +6060,10 @@
paintWhitePixels: state.paintWhitePixels,
resizeSettings: state.resizeSettings,
originalImage: state.originalImage,
+ // Save ignore mask (as base64) with its dimensions
+ resizeIgnoreMask: (state.resizeIgnoreMask && state.resizeSettings && state.resizeSettings.width * state.resizeSettings.height === state.resizeIgnoreMask.length)
+ ? { w: state.resizeSettings.width, h: state.resizeSettings.height, data: btoa(String.fromCharCode(...state.resizeIgnoreMask)) }
+ : null,
};
CONFIG.PAINTING_SPEED_ENABLED = settings.paintingSpeedEnabled;
// AUTO_CAPTCHA_ENABLED is always true - no need to save/load
@@ -6026,6 +6096,17 @@
state.paintWhitePixels = settings.paintWhitePixels ?? true;
state.resizeSettings = settings.resizeSettings ?? null;
state.originalImage = settings.originalImage ?? null;
+ // Restore ignore mask if dims match current resizeSettings
+ if (settings.resizeIgnoreMask && settings.resizeIgnoreMask.data && state.resizeSettings && settings.resizeIgnoreMask.w === state.resizeSettings.width && settings.resizeIgnoreMask.h === state.resizeSettings.height) {
+ try {
+ const bin = atob(settings.resizeIgnoreMask.data);
+ const arr = new Uint8Array(bin.length);
+ for (let i=0;i