img2img_input_display.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /**
  2. * Display the currently active img2img tab's input image as an uninteractable
  3. * background image behind ControlNet's image input. This change will hint user
  4. * that if no ControlNet input image is uploaded, by default ControlNet will
  5. * fallback onto img2img input image.
  6. */
  7. (function () {
  8. function getActiveImg2ImgTabImgSrc(img2imgTabs) {
  9. const tabs = img2imgTabs.querySelectorAll('.tabitem');
  10. const activeTabs = [...tabs].filter(tab => tab.style.display !== 'none');
  11. if (!activeTabs) return;
  12. const image = activeTabs[0].querySelector('.image-container img')
  13. return image ? image.src : undefined;
  14. }
  15. function updateControlNetInputFallbackPreview(cnetInputContainers, imgDataURL) {
  16. for (const container of cnetInputContainers) {
  17. const badge = container.querySelector('.cnet-badge');
  18. if (badge) badge.remove();
  19. if (imgDataURL) {
  20. // Do not add fallback image if controlnet input already exists.
  21. if (container.querySelector('img')) {
  22. continue;
  23. }
  24. // Set the background image
  25. container.style.backgroundImage = `url('${imgDataURL}')`;
  26. // Set other background properties
  27. container.style.backgroundPosition = 'center';
  28. container.style.backgroundRepeat = 'no-repeat';
  29. container.style.backgroundSize = 'contain';
  30. container.title = "Img2Img input will be used if no ControlNet input is specified.";
  31. const div = document.createElement('div');
  32. div.classList.add('cnet-badge', 'primary', 'cnet-a1111-badge');
  33. div.innerHTML = 'A1111';
  34. container.appendChild(div);
  35. } else {
  36. container.style.backgroundImage = 'none';
  37. }
  38. }
  39. }
  40. const ImgChangeType = {
  41. NO_CHANGE: 0,
  42. REMOVE: 1,
  43. ADD: 2,
  44. SRC_CHANGE: 3,
  45. };
  46. function imgChangeObserved(mutationsList) {
  47. // Iterate over all mutations that just occured
  48. for (let mutation of mutationsList) {
  49. // Check if the mutation is an addition or removal of a node
  50. if (mutation.type === 'childList') {
  51. // Check if nodes were added
  52. if (mutation.addedNodes.length > 0) {
  53. for (const node of mutation.addedNodes) {
  54. if (node.tagName === 'IMG') {
  55. return ImgChangeType.ADD;
  56. }
  57. }
  58. }
  59. // Check if nodes were removed
  60. if (mutation.removedNodes.length > 0) {
  61. for (const node of mutation.removedNodes) {
  62. if (node.tagName === 'IMG') {
  63. return ImgChangeType.REMOVE;
  64. }
  65. }
  66. }
  67. }
  68. // Check if the mutation is a change of an attribute
  69. else if (mutation.type === 'attributes') {
  70. if (mutation.target.tagName === 'IMG' && mutation.attributeName === 'src') {
  71. return ImgChangeType.SRC_CHANGE;
  72. }
  73. }
  74. }
  75. return ImgChangeType.NO_CHANGE;
  76. }
  77. let callback_registered = false;
  78. onUiUpdate(() => {
  79. if (callback_registered) return;
  80. const cnetInputContainers = gradioApp().querySelectorAll(
  81. "#img2img_controlnet_tabs .cnet-input-image-group .cnet-image");
  82. if (!cnetInputContainers) return;
  83. const img2imgTabs = gradioApp().querySelector("#mode_img2img");
  84. if (!img2imgTabs) return;
  85. // Every time img2img input updates, update fallback preview.
  86. const img2imgContainers = img2imgTabs.querySelectorAll('.tabitem .image-container');
  87. for (const container of img2imgContainers) {
  88. new MutationObserver((mutationsList) => {
  89. if (imgChangeObserved(mutationsList) !== ImgChangeType.NO_CHANGE) {
  90. updateControlNetInputFallbackPreview(
  91. cnetInputContainers,
  92. getActiveImg2ImgTabImgSrc(img2imgTabs)
  93. );
  94. return;
  95. }
  96. }).observe(container, {
  97. childList: true,
  98. attributes: true,
  99. attributeFilter: ['src'],
  100. subtree: true,
  101. });
  102. }
  103. // Every time controlnet input updates, update fallback preview.
  104. for (const container of cnetInputContainers) {
  105. new MutationObserver((mutationsList) => {
  106. const changeObserved = imgChangeObserved(mutationsList);
  107. if (changeObserved === ImgChangeType.REMOVE) {
  108. updateControlNetInputFallbackPreview(
  109. [container],
  110. getActiveImg2ImgTabImgSrc(img2imgTabs)
  111. );
  112. return;
  113. }
  114. if (changeObserved === ImgChangeType.ADD ||
  115. changeObserved === ImgChangeType.SRC_CHANGE) {
  116. updateControlNetInputFallbackPreview(
  117. [container],
  118. undefined
  119. );
  120. return;
  121. }
  122. }).observe(container, {
  123. childList: true,
  124. attributes: true,
  125. attributeFilter: ['src'],
  126. subtree: true,
  127. });
  128. }
  129. // Every time the img2img tab is switched, update fallback preview.
  130. new MutationObserver((mutationsList) => {
  131. for (const mutation of mutationsList) {
  132. if (mutation.type === 'childList') {
  133. updateControlNetInputFallbackPreview(
  134. cnetInputContainers,
  135. getActiveImg2ImgTabImgSrc(img2imgTabs)
  136. );
  137. return;
  138. }
  139. }
  140. }).observe(img2imgTabs.querySelector('.tab-nav'), { childList: true });
  141. callback_registered = true;
  142. });
  143. })();