|
|
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() { |
|
|
|
|
|
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() { |
|
|
|
|
|
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(); |
|
|
}); |
|
|
|