From 8f23d396cd9df829a493ceb94b1fafac49874f0a Mon Sep 17 00:00:00 2001 From: 7est <30688527+7est@users.noreply.github.com> Date: Sat, 30 Aug 2025 18:53:43 +0900 Subject: [PATCH 1/4] Add option to skip transparent pixels --- Auto-Image.js | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/Auto-Image.js b/Auto-Image.js index 9f68ec5..e5b7f50 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -1181,6 +1181,7 @@ function applyTheme() { availableColors: [], activeColorPalette: [], // User-selected colors for conversion paintWhitePixels: true, // Default to ON + paintTransparentPixels: true, // Default to ON currentCharges: 0, maxCharges: 1, // Default max charges cooldown: CONFIG.COOLDOWN_DEFAULT, @@ -1495,7 +1496,7 @@ function applyTheme() { const d = cached.data; const a = d[idx + 3]; const alphaThresh = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; - if (a < alphaThresh) { + if (!state.paintTransparentPixels && a < alphaThresh) { // Treat as transparent / unavailable // Lightweight debug: show when transparency causes skip (only if verbose enabled) if (window._overlayDebug) console.debug('getTilePixelColor: transparent pixel, skipping', tileKey, x, y, a); @@ -1527,7 +1528,7 @@ function applyTheme() { const data = ctx.getImageData(x, y, 1, 1).data; const a = data[3]; const alphaThresh = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; - if (a < alphaThresh) { + if (!state.paintTransparentPixels && a < alphaThresh) { if (window._overlayDebug) console.debug('getTilePixelColor: transparent pixel (fallback), skipping', tileKey, x, y, a); return null; } @@ -3909,6 +3910,10 @@ function applyTheme() { Paint White Pixels +
@@ -4456,6 +4461,7 @@ function applyTheme() { const heightValue = resizeContainer.querySelector("#heightValue") const keepAspect = resizeContainer.querySelector("#keepAspect") const paintWhiteToggle = resizeContainer.querySelector("#paintWhiteToggle"); + const paintTransparentToggle = resizeContainer.querySelector("#paintTransparentToggle"); const zoomSlider = resizeContainer.querySelector("#zoomSlider"); const zoomValue = resizeContainer.querySelector('#zoomValue'); const zoomInBtn = resizeContainer.querySelector('#zoomInBtn'); @@ -4767,6 +4773,7 @@ function applyTheme() { zoomSlider.value = 1; if (zoomValue) zoomValue.textContent = '100%'; paintWhiteToggle.checked = state.paintWhitePixels; + paintTransparentToggle.checked = state.paintTransparentPixels; let _previewTimer = null; let _previewJobId = 0; @@ -4870,7 +4877,7 @@ function applyTheme() { 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 >= tThresh && (state.paintWhitePixels || !Utils.isWhitePixel(r, g, b)); + const isEligible = (state.paintTransparentPixels || a >= tThresh) && (state.paintWhitePixels || !Utils.isWhitePixel(r, g, b)); eligible[idx] = isEligible ? 1 : 0; work[idx * 3] = r; work[idx * 3 + 1] = g; @@ -4922,7 +4929,7 @@ function applyTheme() { } 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]; - if (a < tThresh || (!state.paintWhitePixels && Utils.isWhitePixel(r, g, b))) { + if ((!state.paintTransparentPixels && a < tThresh) || (!state.paintWhitePixels && Utils.isWhitePixel(r, g, b))) { data[i + 3] = 0; continue; } @@ -4974,6 +4981,11 @@ function applyTheme() { _updateResizePreview(); }; + paintTransparentToggle.onchange = (e) => { + state.paintTransparentPixels = e.target.checked; + _updateResizePreview(); + }; + let panX = 0, panY = 0; const clampPan = () => { const wrapRect = panStage?.getBoundingClientRect() || { width: 0, height: 0 }; @@ -5406,7 +5418,7 @@ function applyTheme() { const i4 = idx * 4; const r = data[i4], g = data[i4 + 1], b = data[i4 + 2], a = data[i4 + 3]; const masked = mask && mask[idx]; - const isEligible = !masked && a >= tThresh2 && (state.paintWhitePixels || !Utils.isWhitePixel(r, g, b)); + const isEligible = !masked && (state.paintTransparentPixels || a >= tThresh2) && (state.paintWhitePixels || !Utils.isWhitePixel(r, g, b)); eligible[idx] = isEligible ? 1 : 0; work[idx * 3] = r; work[idx * 3 + 1] = g; @@ -5463,7 +5475,7 @@ function applyTheme() { 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 masked = mask && mask[(i>>2)]; - const isTransparent = a < tThresh2 || masked; + const isTransparent = (!state.paintTransparentPixels && a < tThresh2) || masked; const isWhiteAndSkipped = !state.paintWhitePixels && Utils.isWhitePixel(r, g, b); if (isTransparent || isWhiteAndSkipped) { data[i + 3] = 0; // overlay transparency @@ -5597,7 +5609,7 @@ function applyTheme() { let totalValidPixels = 0; for (let i = 0; i < pixels.length; i += 4) { - const isTransparent = pixels[i + 3] < (state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD); + const isTransparent = !state.paintTransparentPixels && pixels[i + 3] < (state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD); const isWhiteAndSkipped = !state.paintWhitePixels && Utils.isWhitePixel(pixels[i], pixels[i + 1], pixels[i + 2]); if (!isTransparent && !isWhiteAndSkipped) { totalValidPixels++; @@ -5842,7 +5854,7 @@ function applyTheme() { const isEligibleAt = (x, y) => { const idx = (y * width + x) * 4; const r = pixels[idx], g = pixels[idx + 1], b = pixels[idx + 2], a = pixels[idx + 3]; - if (a < tThresh2) return false; + if (!state.paintTransparentPixels && a < tThresh2) return false; if (!state.paintWhitePixels && Utils.isWhitePixel(r, g, b)) return false; return true; }; @@ -5890,8 +5902,8 @@ function applyTheme() { const alpha = pixels[idx + 3] const tThresh2 = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; - if (alpha < tThresh2 || (!state.paintWhitePixels && Utils.isWhitePixel(r, g, b))) { - if (alpha < tThresh2) { + if ((!state.paintTransparentPixels && alpha < tThresh2) || (!state.paintWhitePixels && Utils.isWhitePixel(r, g, b))) { + if (!state.paintTransparentPixels && alpha < tThresh2) { skippedPixels.transparent++; } else { skippedPixels.white++; @@ -6288,6 +6300,7 @@ function applyTheme() { customTransparencyThreshold: state.customTransparencyThreshold, customWhiteThreshold: state.customWhiteThreshold, paintWhitePixels: state.paintWhitePixels, + paintTransparentPixels: state.paintTransparentPixels, resizeSettings: state.resizeSettings, originalImage: state.originalImage, // Save ignore mask (as base64) with its dimensions @@ -6333,6 +6346,7 @@ function applyTheme() { state.customTransparencyThreshold = settings.customTransparencyThreshold ?? CONFIG.TRANSPARENCY_THRESHOLD; state.customWhiteThreshold = settings.customWhiteThreshold ?? CONFIG.WHITE_THRESHOLD; state.paintWhitePixels = settings.paintWhitePixels ?? true; + state.paintTransparentPixels = settings.paintTransparentPixels ?? true; state.resizeSettings = settings.resizeSettings ?? null; state.originalImage = settings.originalImage ?? null; // Notifications From d5b5e36b1f01904c04fee2a20745356498ea82dd Mon Sep 17 00:00:00 2001 From: 7est <30688527+7est@users.noreply.github.com> Date: Sat, 30 Aug 2025 18:59:47 +0900 Subject: [PATCH 2/4] Localize resize painting options --- Auto-Image.js | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/Auto-Image.js b/Auto-Image.js index e5b7f50..be24527 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -354,6 +354,9 @@ function applyTheme() { captchaFailed: "❌ Turnstile token generation failed. Trying fallback method...", automation: "Automation", noChargesThreshold: "⌛ Waiting for charges to reach {threshold}. Currently {current}. Next in {time}...", + keepAspectRatio: "Keep Aspect Ratio", + paintWhitePixels: "Paint White Pixels", + paintTransparentPixels: "Paint Transparent Pixels", }, ru: { title: "WPlace Авто-Изображение", @@ -427,6 +430,9 @@ function applyTheme() { captchaFailed: "❌ Не удалось сгенерировать Turnstile токен. Пробую резервный метод...", automation: "Автоматизация", noChargesThreshold: "⌛ Ожидание зарядов до {threshold}. Сейчас {current}. Следующий через {time}...", + keepAspectRatio: "Сохранять пропорции", + paintWhitePixels: "Рисовать белые пиксели", + paintTransparentPixels: "Рисовать прозрачные пиксели", }, pt: { title: "WPlace Auto-Image", @@ -500,6 +506,9 @@ function applyTheme() { captchaFailed: "❌ Falha ao resolver CAPTCHA. Pinte um pixel manualmente.", automation: "Automação", noChargesThreshold: "⌛ Aguardando cargas atingirem {threshold}. Atual: {current}. Próxima em {time}...", + keepAspectRatio: "Manter proporção", + paintWhitePixels: "Pintar pixels brancos", + paintTransparentPixels: "Pintar pixels transparentes", }, vi: { title: "WPlace Auto-Image", @@ -573,6 +582,9 @@ function applyTheme() { captchaFailed: "❌ Giải CAPTCHA tự động thất bại. Vui lòng vẽ một pixel thủ công.", automation: "Tự động hóa", noChargesThreshold: "⌛ Đang chờ số lần sạc đạt {threshold}. Hiện tại {current}. Lần tiếp theo trong {time}...", + keepAspectRatio: "Giữ tỉ lệ", + paintWhitePixels: "Tô pixel trắng", + paintTransparentPixels: "Tô pixel trong suốt", }, fr: { title: "WPlace Auto-Image", @@ -646,6 +658,9 @@ function applyTheme() { captchaFailed: "❌ Échec de l'Auto-CAPTCHA. Peignez un pixel manuellement.", automation: "Automatisation", noChargesThreshold: "⌛ En attente que les charges atteignent {threshold}. Actuel: {current}. Prochaine dans {time}...", + keepAspectRatio: "Conserver les proportions", + paintWhitePixels: "Peindre les pixels blancs", + paintTransparentPixels: "Peindre les pixels transparents", }, id: { title: "WPlace Auto-Image", @@ -719,6 +734,9 @@ function applyTheme() { captchaFailed: "❌ Gagal menyelesaikan CAPTCHA. Lukis satu piksel secara manual.", automation: "Automasi", noChargesThreshold: "⌛ Menunggu muatan mencapai {threshold}. Saat ini: {current}. Berikutnya dalam {time}...", + keepAspectRatio: "Pertahankan rasio aspek", + paintWhitePixels: "Warnai piksel putih", + paintTransparentPixels: "Warnai piksel transparan", }, tr: { title: "WPlace Otomatik-Resim", @@ -790,6 +808,9 @@ function applyTheme() { captchaFailed: "❌ Oto-CAPTCHA başarısız oldu. Bir pikseli manuel boyayın.", automation: "Otomasyon", noChargesThreshold: "⌛ Hakların {threshold} seviyesine ulaşması bekleniyor. Şu anda {current}. Sonraki {time} içinde...", + keepAspectRatio: "En-Boy Oranını Koru", + paintWhitePixels: "Beyaz Pikselleri Boya", + paintTransparentPixels: "Şeffaf Pikselleri Boya", }, zh: { title: "WPlace 自动图像", @@ -866,6 +887,9 @@ function applyTheme() { captchaFailed: "❌ 令牌生成失败。尝试回退方法...", automation: "自动化", noChargesThreshold: "⌛ 等待次数达到 {threshold}。当前 {current}。下次在 {time}...", + keepAspectRatio: "保持纵横比", + paintWhitePixels: "绘制白色像素", + paintTransparentPixels: "绘制透明像素", }, "zh-tw": { title: "WPlace 自動圖像", @@ -942,6 +966,9 @@ function applyTheme() { captchaFailed: "❌ 令牌產生失敗。嘗試回退方法...", automation: "自動化", noChargesThreshold: "⌛ 等待次數達到 {threshold}。目前 {current}。下次在 {time}...", + keepAspectRatio: "保持寬高比", + paintWhitePixels: "繪製白色像素", + paintTransparentPixels: "繪製透明像素", }, ja: { title: "WPlace 自動画像", @@ -1018,6 +1045,9 @@ function applyTheme() { captchaFailed: "❌ トークン生成失敗。フォールバックを試行...", automation: "自動化", noChargesThreshold: "⌛ チャージ {threshold} を待機中。現在 {current}。次は {time} 後...", + keepAspectRatio: "アスペクト比を維持", + paintWhitePixels: "白いピクセルを描画", + paintTransparentPixels: "透明なピクセルを描画", }, ko: { title: "WPlace 자동 이미지", @@ -1094,6 +1124,9 @@ function applyTheme() { captchaFailed: "❌ 토큰 생성 실패. 폴백 시도...", automation: "자동화", noChargesThreshold: "⌛ 횟수가 {threshold} 에 도달할 때까지 대기 중. 현재 {current}. 다음 {time} 후...", + keepAspectRatio: "종횡비 유지", + paintWhitePixels: "흰색 픽셀 그리기", + paintTransparentPixels: "투명 픽셀 그리기", }, uk: { title: "WPlace Авто-Зображення", @@ -1168,6 +1201,9 @@ function applyTheme() { captchaFailed: "❌ Не вдалося згенерувати токен Turnstile. Використовую запасний метод...", automation: "Автоматизація", noChargesThreshold: "⌛ Очікування, доки заряди досягнуть {threshold}. Зараз {current}. Наступне через {time}...", + keepAspectRatio: "Зберігати співвідношення сторін", + paintWhitePixels: "Малювати білі пікселі", + paintTransparentPixels: "Малювати прозорі пікселі", }, } @@ -3904,15 +3940,15 @@ function applyTheme() {
From 7aa75f8da5aca1392368898b6504df23ebc64876 Mon Sep 17 00:00:00 2001 From: 7est <30688527+7est@users.noreply.github.com> Date: Sat, 30 Aug 2025 19:07:58 +0900 Subject: [PATCH 3/4] Add transparent pixel toggle to settings --- Auto-Image.js | 72 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/Auto-Image.js b/Auto-Image.js index be24527..65e4cae 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -3686,6 +3686,24 @@ function applyTheme() { accent-color: ${theme.highlight || '#48dbfb'}; "/> + + + +
@@ -4436,17 +4454,33 @@ function applyTheme() { }) } - const overlayOpacitySlider = settingsContainer.querySelector("#overlayOpacitySlider"); - const overlayOpacityValue = settingsContainer.querySelector("#overlayOpacityValue"); - const enableBlueMarbleToggle = settingsContainer.querySelector("#enableBlueMarbleToggle"); + const overlayOpacitySlider = settingsContainer.querySelector("#overlayOpacitySlider"); + const overlayOpacityValue = settingsContainer.querySelector("#overlayOpacityValue"); + const enableBlueMarbleToggle = settingsContainer.querySelector("#enableBlueMarbleToggle"); + const settingsPaintWhiteToggle = settingsContainer.querySelector("#settingsPaintWhiteToggle"); + const settingsPaintTransparentToggle = settingsContainer.querySelector("#settingsPaintTransparentToggle"); - if (overlayOpacitySlider && overlayOpacityValue) { - overlayOpacitySlider.addEventListener('input', (e) => { - const opacity = parseFloat(e.target.value); - state.overlayOpacity = opacity; - overlayOpacityValue.textContent = `${Math.round(opacity * 100)}%`; - }); - } + if (overlayOpacitySlider && overlayOpacityValue) { + overlayOpacitySlider.addEventListener('input', (e) => { + const opacity = parseFloat(e.target.value); + state.overlayOpacity = opacity; + overlayOpacityValue.textContent = `${Math.round(opacity * 100)}%`; + }); + } + + if (settingsPaintWhiteToggle) { + settingsPaintWhiteToggle.addEventListener('change', (e) => { + state.paintWhitePixels = e.target.checked; + saveBotSettings(); + }); + } + + if (settingsPaintTransparentToggle) { + settingsPaintTransparentToggle.addEventListener('change', (e) => { + state.paintTransparentPixels = e.target.checked; + saveBotSettings(); + }); + } // Speed slider event listener const speedSlider = settingsContainer.querySelector("#speedSlider"); @@ -5012,15 +5046,17 @@ function applyTheme() { if (!isNaN(fit) && isFinite(fit)) applyZoom(fit); }; - paintWhiteToggle.onchange = (e) => { - state.paintWhitePixels = e.target.checked; - _updateResizePreview(); - }; + paintWhiteToggle.onchange = (e) => { + state.paintWhitePixels = e.target.checked; + _updateResizePreview(); + saveBotSettings(); + }; - paintTransparentToggle.onchange = (e) => { - state.paintTransparentPixels = e.target.checked; - _updateResizePreview(); - }; + paintTransparentToggle.onchange = (e) => { + state.paintTransparentPixels = e.target.checked; + _updateResizePreview(); + saveBotSettings(); + }; let panX = 0, panY = 0; const clampPan = () => { From a5bb699da8b3ab4483236c6c699dbb414cd8c49a Mon Sep 17 00:00:00 2001 From: 7est <30688527+7est@users.noreply.github.com> Date: Sat, 30 Aug 2025 19:25:29 +0900 Subject: [PATCH 4/4] Group paint options in settings --- Auto-Image.js | 29 +++++++++++++++++++++++++++++ auto-image-styles.css | 5 +++++ themes/classic-light.css | 1 + themes/classic.css | 13 +++++++++---- 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/Auto-Image.js b/Auto-Image.js index 65e4cae..3c4d2b9 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -355,6 +355,7 @@ function applyTheme() { automation: "Automation", noChargesThreshold: "⌛ Waiting for charges to reach {threshold}. Currently {current}. Next in {time}...", keepAspectRatio: "Keep Aspect Ratio", + paintOptions: "Paint Options", paintWhitePixels: "Paint White Pixels", paintTransparentPixels: "Paint Transparent Pixels", }, @@ -431,6 +432,7 @@ function applyTheme() { automation: "Автоматизация", noChargesThreshold: "⌛ Ожидание зарядов до {threshold}. Сейчас {current}. Следующий через {time}...", keepAspectRatio: "Сохранять пропорции", + paintOptions: "Параметры рисования", paintWhitePixels: "Рисовать белые пиксели", paintTransparentPixels: "Рисовать прозрачные пиксели", }, @@ -507,6 +509,7 @@ function applyTheme() { automation: "Automação", noChargesThreshold: "⌛ Aguardando cargas atingirem {threshold}. Atual: {current}. Próxima em {time}...", keepAspectRatio: "Manter proporção", + paintOptions: "Opções de Pintura", paintWhitePixels: "Pintar pixels brancos", paintTransparentPixels: "Pintar pixels transparentes", }, @@ -583,6 +586,7 @@ function applyTheme() { automation: "Tự động hóa", noChargesThreshold: "⌛ Đang chờ số lần sạc đạt {threshold}. Hiện tại {current}. Lần tiếp theo trong {time}...", keepAspectRatio: "Giữ tỉ lệ", + paintOptions: "Tùy chọn vẽ", paintWhitePixels: "Tô pixel trắng", paintTransparentPixels: "Tô pixel trong suốt", }, @@ -659,6 +663,7 @@ function applyTheme() { automation: "Automatisation", noChargesThreshold: "⌛ En attente que les charges atteignent {threshold}. Actuel: {current}. Prochaine dans {time}...", keepAspectRatio: "Conserver les proportions", + paintOptions: "Options de peinture", paintWhitePixels: "Peindre les pixels blancs", paintTransparentPixels: "Peindre les pixels transparents", }, @@ -735,6 +740,7 @@ function applyTheme() { automation: "Automasi", noChargesThreshold: "⌛ Menunggu muatan mencapai {threshold}. Saat ini: {current}. Berikutnya dalam {time}...", keepAspectRatio: "Pertahankan rasio aspek", + paintOptions: "Opsi Mewarnai", paintWhitePixels: "Warnai piksel putih", paintTransparentPixels: "Warnai piksel transparan", }, @@ -809,6 +815,7 @@ function applyTheme() { automation: "Otomasyon", noChargesThreshold: "⌛ Hakların {threshold} seviyesine ulaşması bekleniyor. Şu anda {current}. Sonraki {time} içinde...", keepAspectRatio: "En-Boy Oranını Koru", + paintOptions: "Boyama Seçenekleri", paintWhitePixels: "Beyaz Pikselleri Boya", paintTransparentPixels: "Şeffaf Pikselleri Boya", }, @@ -888,6 +895,7 @@ function applyTheme() { automation: "自动化", noChargesThreshold: "⌛ 等待次数达到 {threshold}。当前 {current}。下次在 {time}...", keepAspectRatio: "保持纵横比", + paintOptions: "绘制选项", paintWhitePixels: "绘制白色像素", paintTransparentPixels: "绘制透明像素", }, @@ -967,6 +975,7 @@ function applyTheme() { automation: "自動化", noChargesThreshold: "⌛ 等待次數達到 {threshold}。目前 {current}。下次在 {time}...", keepAspectRatio: "保持寬高比", + paintOptions: "繪製選項", paintWhitePixels: "繪製白色像素", paintTransparentPixels: "繪製透明像素", }, @@ -1046,6 +1055,7 @@ function applyTheme() { automation: "自動化", noChargesThreshold: "⌛ チャージ {threshold} を待機中。現在 {current}。次は {time} 後...", keepAspectRatio: "アスペクト比を維持", + paintOptions: "描画オプション", paintWhitePixels: "白いピクセルを描画", paintTransparentPixels: "透明なピクセルを描画", }, @@ -1125,6 +1135,7 @@ function applyTheme() { automation: "자동화", noChargesThreshold: "⌛ 횟수가 {threshold} 에 도달할 때까지 대기 중. 현재 {current}. 다음 {time} 후...", keepAspectRatio: "종횡비 유지", + paintOptions: "그리기 옵션", paintWhitePixels: "흰색 픽셀 그리기", paintTransparentPixels: "투명 픽셀 그리기", }, @@ -1202,6 +1213,7 @@ function applyTheme() { automation: "Автоматизація", noChargesThreshold: "⌛ Очікування, доки заряди досягнуть {threshold}. Зараз {current}. Наступне через {time}...", keepAspectRatio: "Зберігати співвідношення сторін", + paintOptions: "Параметри малювання", paintWhitePixels: "Малювати білі пікселі", paintTransparentPixels: "Малювати прозорі пікселі", }, @@ -3686,6 +3698,23 @@ function applyTheme() { accent-color: ${theme.highlight || '#48dbfb'}; "/> + + + + +
+ +