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() {
@@ -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'};
"/>
+
+
+
+
+