// ==UserScript== // @name WPlace – Contrast Tile Black or White // @namespace wplace.blackout.response // @version 1.0.0 // @description Adds contrast, black/white. To better see where to place your pixel. // @match https://wplace.live/* // @run-at document-start // @grant none // @all-frames true // @inject-into page // @downloadURL https://update.greasyfork.icu/scripts/547850/WPlace%20%E2%80%93%20Contrast%20Tile%20Black%20or%20White.user.js // @updateURL https://update.greasyfork.icu/scripts/547850/WPlace%20%E2%80%93%20Contrast%20Tile%20Black%20or%20White.meta.js // ==/UserScript== (function(){ 'use strict'; // --- réglages --- const TILE_RE = /^https:\/\/backend\.wplace\.live\/files\/.+\.png(?:\?.*)?$/i; const getMode = () => localStorage.getItem('tile_blackout') || 'off'; // 'off' | 'on' const getColor = () => localStorage.getItem('tile_blackout_color') || 'black'; // 'black' | 'white' // --- util: compose blob -> blob (fond uni + image d'origine) --- async function composePngWithBackground(origBlob){ const img = await createImageBitmap(origBlob); const cnv = document.createElement('canvas'); cnv.width = img.width; cnv.height = img.height; const ctx = cnv.getContext('2d'); ctx.fillStyle = (getColor()==='white') ? '#ffffff' : '#000000'; ctx.fillRect(0,0,cnv.width,cnv.height); ctx.drawImage(img,0,0); return new Promise(res => cnv.toBlob(b => res(b || origBlob), 'image/png')); } // --- patch Response.blob / arrayBuffer --- const processedResponses = new WeakSet(); const origBlob = Response.prototype.blob; const origArrayBuffer = Response.prototype.arrayBuffer; Response.prototype.blob = function(...args){ // si déjà traité ou pas une tuile → passe try { const url = (this && this.url) || ''; const on = getMode()==='on'; if (!on || !TILE_RE.test(url) || processedResponses.has(this)) { return origBlob.apply(this, args); } const ct = (this.headers && this.headers.get && this.headers.get('content-type'))?.toLowerCase() || ''; // on ne retouche que du PNG if (ct && !ct.includes('image/png')) { return origBlob.apply(this, args); } return origBlob.apply(this, args).then(async (b)=>{ try{ const newBlob = await composePngWithBackground(b); processedResponses.add(this); return newBlob || b; }catch(e){ return b; } }); } catch(e) { return origBlob.apply(this, args); } }; Response.prototype.arrayBuffer = function(...args){ try { const url = (this && this.url) || ''; const on = getMode()==='on'; if (!on || !TILE_RE.test(url) || processedResponses.has(this)) { return origArrayBuffer.apply(this, args); } const ct = (this.headers && this.headers.get && this.headers.get('content-type'))?.toLowerCase() || ''; if (ct && !ct.includes('image/png')) { return origArrayBuffer.apply(this, args); } // on part du blob pour recomposer puis on renvoie un ArrayBuffer return origBlob.apply(this, args).then(async (b)=>{ try{ const newBlob = await composePngWithBackground(b); processedResponses.add(this); return newBlob.arrayBuffer(); }catch(e){ return b.arrayBuffer(); } }); } catch(e){ return origArrayBuffer.apply(this, args); } }; // --- mini panneau (facultatif) --- function addPanel(){ if (document.getElementById('__tile_blackout_panel2')) return; const wrap = document.createElement('div'); wrap.id='__tile_blackout_panel2'; wrap.innerHTML = `