class ImageComparator { static IMG_COMP_WIDTH; static img_A; static img_B; static alpha_slider; static bar; static direction_checkbox; static cached_image = undefined; static translateX = 0.0; static translateY = 0.0; static scale = 1.0; static isHorizontal() { return this.direction_checkbox.checked; } static switch_to_comparison() { const tabs = gradioApp().querySelector('#tabs').querySelector('.tab-nav').querySelectorAll('button'); for (let i = 0; i < tabs.length; i++) { if (tabs[i].textContent.trim() === "Comparison") { tabs[i].click(); break; } } } static reset() { if (this.isHorizontal()) { this.img_B.parentNode.style.left = `calc(50% + ${this.IMG_COMP_WIDTH / 2}px)`; this.img_B.style.left = `${this.IMG_COMP_WIDTH}px`; this.bar.style.left = `calc(50% + ${this.IMG_COMP_WIDTH / 2}px)`; this.img_B.parentNode.style.top = '0px'; this.img_B.style.top = '0px'; this.bar.style.top = '0px'; this.bar.style.height = `${this.IMG_COMP_WIDTH}px`; this.bar.style.width = '2px'; this.img_A.classList.add('hor'); this.img_A.classList.remove('ver'); } else { this.img_B.parentNode.style.left = `calc(50% - ${this.IMG_COMP_WIDTH / 2}px)`; this.img_B.style.left = '0px'; this.bar.style.left = `calc(50% - ${this.IMG_COMP_WIDTH / 2}px)`; this.img_B.parentNode.style.top = `calc(50% + ${this.IMG_COMP_WIDTH / 2}px)`; this.img_B.style.top = `${this.IMG_COMP_WIDTH}px`; this.bar.style.top = `${this.IMG_COMP_WIDTH}px`; this.bar.style.width = `${this.IMG_COMP_WIDTH}px`; this.bar.style.height = '2px'; this.img_A.classList.remove('hor'); this.img_A.classList.add('ver'); } this.img_B.style.opacity = 1.0; this.alpha_slider.querySelector('input').value = 1.0; updateInput(this.alpha_slider.querySelector('input')); } static addButtons() { // 0: Off ; 1: Text ; 2: Icon const config = gradioApp().getElementById('setting_comp_send_btn').querySelectorAll('label'); var option = 0; for (let i = 1; i < 3; i++) { if (config[i].classList.contains('selected')) { option = i; break; } } if (option === 0) return; ['img2img', 'extras'].forEach((mode) => { const row = gradioApp().getElementById(`image_buttons_${mode}`).querySelector('.form'); const btn = row.lastElementChild.cloneNode(); btn.id = `${mode}_send_to_comp`; btn.title = "Send images to comparison tab."; if (option === 1) btn.textContent = "Send to Comparison"; else btn.textContent = "🆚"; if (mode === "extras") { btn.addEventListener('click', () => { ImgCompLoader.loadImage("extras"); this.switch_to_comparison(); }); } else { const tabs = gradioApp().getElementById('img2img_settings').querySelector('.tabs').querySelector('.tab-nav'); btn.addEventListener('click', () => { [...tabs.querySelectorAll('button')].forEach((tab) => { if (tab.classList.contains('selected')) { const t = tab.textContent.trim(); if (t === "img2img") { ImgCompLoader.loadImage("i2i"); this.switch_to_comparison(); } else if (t === "Inpaint") { ImgCompLoader.loadImage("inpaint"); this.switch_to_comparison(); } else { alert("Only img2img and Inpaint are supported in Comparison!"); return; } } }); }); } row.appendChild(btn); }); } static addTxt2ImgButton() { // 0: Off ; 1: Text ; 2: Icon const config = gradioApp().getElementById('setting_comp_send_btn_t2i').querySelectorAll('label'); var option = 0; for (let i = 1; i < 3; i++) { if (config[i].classList.contains('selected')) { option = i; break; } } if (option === 0) return; ["txt2img_generate", "txt2img_upscale"].forEach((btn) => { const generate = gradioApp().getElementById(btn); if (generate != null) generate.addEventListener("click", () => { this.cached_image = gradioApp().getElementById('txt2img_gallery').querySelector('img')?.src; }); }); const row = gradioApp().getElementById("image_buttons_txt2img").querySelector('.form'); const btn = row.lastElementChild.cloneNode(); btn.id = "txt2img_send_to_comp"; btn.title = "Send images to comparison tab."; if (option === 1) btn.textContent = "Send to Comparison"; else btn.textContent = "🆚"; btn.addEventListener('click', () => { if (this.cached_image == null) { alert("No cached result exists!"); return; } ImageComparator.img_A.src = this.cached_image; ImageComparator.img_B.src = gradioApp().getElementById('txt2img_gallery').querySelector('img').src; ImageComparator.reset(); this.switch_to_comparison(); }); row.appendChild(btn); } static init() { const block_A = gradioApp().getElementById('img_comp_A'); this.img_A = block_A.querySelector('img'); const block_B = gradioApp().getElementById('img_comp_B'); this.img_B = block_B.querySelector('img'); const tab = gradioApp().getElementById('tab_sd-webui-image-comparison'); this.IMG_COMP_WIDTH = parseFloat(getComputedStyle(tab).getPropertyValue('--img-comp-width').split('px')[0]); const row = gradioApp().getElementById('img_comp_row'); row.setAttribute("tabindex", 0); row.style.display = 'block'; block_A.insertBefore(this.img_A, block_A.firstChild); while (block_A.children.length > 1) block_A.lastChild.remove(); block_B.insertBefore(this.img_B, block_B.firstChild); while (block_B.children.length > 1) block_B.lastChild.remove(); block_A.classList.add('comp-block'); block_B.classList.add('comp-block'); this.img_A.ondragstart = (event) => { event.preventDefault; return false; }; this.img_B.ondragstart = (event) => { event.preventDefault; return false; }; block_B.style.pointerEvents = 'none'; block_B.style.left = `calc(50% + ${this.IMG_COMP_WIDTH / 2}px)`; this.img_B.style.left = `${this.IMG_COMP_WIDTH}px`; this.alpha_slider = gradioApp().getElementById('img_comp_alpha'); ['mousemove', 'touchmove'].forEach((ev) => { this.alpha_slider.addEventListener(ev, () => { this.img_B.style.opacity = this.alpha_slider.querySelector('input').value; }); }); this.direction_checkbox = gradioApp().getElementById('img_comp_horizontal').querySelector('input[type=checkbox]'); this.bar = row.querySelector('.bar'); this.bar = document.createElement('div'); this.bar.classList.add('bar'); row.appendChild(this.bar); ['click', 'mousemove', 'touchmove'].forEach((ev) => { row.addEventListener(ev, (e) => { e.preventDefault(); if (ev.startsWith('touch')) e = e.changedTouches[0]; else if (e.buttons != 1) return; const rect = e.target.getBoundingClientRect(); var ratio = 0.5; if (this.isHorizontal()) { if (e.target.id === 'img_comp_row') ratio = (e.clientX > (rect.left + rect.right) / 2) ? 1.0 : 0.0; else ratio = ((e.clientX - rect.left) / (rect.right - rect.left)); const SLIDE_VALUE = this.IMG_COMP_WIDTH * (1.0 - ratio); this.bar.style.left = `calc(50% + ${this.IMG_COMP_WIDTH / 2}px - ${SLIDE_VALUE}px)`; block_B.style.left = `calc(50% + ${this.IMG_COMP_WIDTH / 2}px - ${SLIDE_VALUE}px)`; this.img_B.style.left = `calc(${-this.IMG_COMP_WIDTH}px + ${SLIDE_VALUE}px)`; } else { if (e.target.id === 'img_comp_row') ratio = (e.clientX > (rect.left + rect.right) / 2) ? 1.0 : 0.0; else ratio = ((e.clientY - rect.top) / (rect.bottom - rect.top)); const SLIDE_VALUE = this.IMG_COMP_WIDTH * (1.0 - ratio); this.bar.style.top = `calc(${this.IMG_COMP_WIDTH}px - ${SLIDE_VALUE}px)`; block_B.style.top = `calc(${this.IMG_COMP_WIDTH}px - ${SLIDE_VALUE}px)`; this.img_B.style.top = `calc(${-this.IMG_COMP_WIDTH}px + ${SLIDE_VALUE}px)`; } }); }); row.addEventListener("keydown", (e) => { var flag = false; if (e.key == "=" || e.key == "+") { this.scale = Math.min(this.scale + 0.25, 8.0); flag = true; } if (e.key == "-") { this.scale = Math.max(this.scale - 0.25, 0.25) flag = true; } if (e.key == "ArrowUp") { this.translateY += (50 / this.scale); flag = true; } if (e.key == "ArrowDown") { this.translateY -= (50 / this.scale); flag = true; } if (e.key == "ArrowRight") { this.translateX -= (50 / this.scale); flag = true; } if (e.key == "ArrowLeft") { this.translateX += (50 / this.scale); flag = true; } if (e.key == "0") { this.translateX = 0.0; this.translateY = 0.0; this.scale = 1.0; flag = true; } if (flag) { e.preventDefault(); row.style.transform = `scale(${this.scale}) translate(${this.translateX}px, ${this.translateY}px)`; return false; } }); ImageComparator.reset(); this.addButtons(); this.addTxt2ImgButton(); const container = document.createElement("div"); container.id = "img_comp_row_container"; row.parentNode.insertBefore(container, row); container.appendChild(row); } } onUiLoaded(async () => { ImageComparator.init(); });