diff --git a/css/style.css b/css/style.css
index 09ce6cf0..b87b20a7 100644
--- a/css/style.css
+++ b/css/style.css
@@ -94,6 +94,10 @@
overflow:inherit !important;
}
+.gradio-container{
+ overflow: visible;
+}
+
/* fullpage image viewer */
#lightboxModal{
diff --git a/fooocus_version.py b/fooocus_version.py
index 1118b486..1a708c50 100644
--- a/fooocus_version.py
+++ b/fooocus_version.py
@@ -1 +1 @@
-version = '2.1.840'
+version = '2.1.843'
diff --git a/javascript/zoom.js b/javascript/zoom.js
index e3fdcfb7..450a0347 100644
--- a/javascript/zoom.js
+++ b/javascript/zoom.js
@@ -1,18 +1,5 @@
onUiLoaded(async() => {
// Helper functions
- // Get active tab
-
- /**
- * Waits for an element to be present in the DOM.
- */
- const waitForElement = (id) => new Promise(resolve => {
- const checkForElement = () => {
- const element = document.querySelector(id);
- if (element) return resolve(element);
- setTimeout(checkForElement, 100);
- };
- checkForElement();
- });
// Detect whether the element has a horizontal scroll bar
function hasHorizontalScrollbar(element) {
@@ -33,140 +20,40 @@ onUiLoaded(async() => {
}
}
- // Check if hotkey is valid
- function isValidHotkey(value) {
- const specialKeys = ["Ctrl", "Alt", "Shift", "Disable"];
- return (
- (typeof value === "string" &&
- value.length === 1 &&
- /[a-z]/i.test(value)) ||
- specialKeys.includes(value)
- );
- }
-
- // Normalize hotkey
- function normalizeHotkey(hotkey) {
- return hotkey.length === 1 ? "Key" + hotkey.toUpperCase() : hotkey;
- }
-
- // Format hotkey for display
- function formatHotkeyForDisplay(hotkey) {
- return hotkey.startsWith("Key") ? hotkey.slice(3) : hotkey;
- }
-
// Create hotkey configuration with the provided options
function createHotkeyConfig(defaultHotkeysConfig) {
const result = {}; // Resulting hotkey configuration
-
for (const key in defaultHotkeysConfig) {
result[key] = defaultHotkeysConfig[key];
}
-
return result;
}
- // Disables functions in the config object based on the provided list of function names
- function disableFunctions(config, disabledFunctions) {
- // Bind the hasOwnProperty method to the functionMap object to avoid errors
- const hasOwnProperty =
- Object.prototype.hasOwnProperty.bind(functionMap);
-
- // Loop through the disabledFunctions array and disable the corresponding functions in the config object
- disabledFunctions.forEach(funcName => {
- if (hasOwnProperty(funcName)) {
- const key = functionMap[funcName];
- config[key] = "disable";
- }
- });
-
- // Return the updated config object
- return config;
- }
-
- /**
- * The restoreImgRedMask function displays a red mask around an image to indicate the aspect ratio.
- * If the image display property is set to 'none', the mask breaks. To fix this, the function
- * temporarily sets the display property to 'block' and then hides the mask again after 300 milliseconds
- * to avoid breaking the canvas. Additionally, the function adjusts the mask to work correctly on
- * very long images.
- */
- function restoreImgRedMask(elements) {
- const mainTabId = getTabId(elements);
-
- if (!mainTabId) return;
-
- const mainTab = gradioApp().querySelector(mainTabId);
- const img = mainTab.querySelector("img");
- const imageARPreview = gradioApp().querySelector("#imageARPreview");
-
- if (!img || !imageARPreview) return;
-
- imageARPreview.style.transform = "";
- if (parseFloat(mainTab.style.width) > 865) {
- const transformString = mainTab.style.transform;
- const scaleMatch = transformString.match(
- /scale\(([-+]?[0-9]*\.?[0-9]+)\)/
- );
- let zoom = 1; // default zoom
-
- if (scaleMatch && scaleMatch[1]) {
- zoom = Number(scaleMatch[1]);
- }
-
- imageARPreview.style.transformOrigin = "0 0";
- imageARPreview.style.transform = `scale(${zoom})`;
- }
-
- if (img.style.display !== "none") return;
-
- img.style.display = "block";
-
- setTimeout(() => {
- img.style.display = "none";
- }, 400);
- }
-
// Default config
const defaultHotkeysConfig = {
- canvas_hotkey_zoom: "Alt",
+ canvas_hotkey_zoom: "Shift",
canvas_hotkey_adjust: "Ctrl",
+ canvas_zoom_undo_extra_key: "Ctrl",
+ canvas_zoom_hotkey_undo: "KeyZ",
canvas_hotkey_reset: "KeyR",
canvas_hotkey_fullscreen: "KeyS",
canvas_hotkey_move: "KeyF",
- canvas_hotkey_overlap: "KeyO",
- canvas_disabled_functions: [],
canvas_show_tooltip: true,
canvas_auto_expand: true,
- canvas_blur_prompt: false,
- };
-
- const functionMap = {
- "Zoom": "canvas_hotkey_zoom",
- "Adjust brush size": "canvas_hotkey_adjust",
- "Moving canvas": "canvas_hotkey_move",
- "Fullscreen": "canvas_hotkey_fullscreen",
- "Reset Zoom": "canvas_hotkey_reset",
- "Overlap": "canvas_hotkey_overlap"
+ canvas_blur_prompt: true,
};
// Loading the configuration from opts
- const preHotkeysConfig = createHotkeyConfig(
+ const hotkeysConfig = createHotkeyConfig(
defaultHotkeysConfig
);
- // Disable functions that are not needed by the user
- const hotkeysConfig = disableFunctions(
- preHotkeysConfig,
- preHotkeysConfig.canvas_disabled_functions
- );
-
let isMoving = false;
- let mouseX, mouseY;
let activeElement;
const elemData = {};
- function applyZoomAndPan(elemId, isExtension = true) {
+ function applyZoomAndPan(elemId) {
const targetElement = gradioApp().querySelector(elemId);
if (!targetElement) {
@@ -181,6 +68,7 @@ onUiLoaded(async() => {
panX: 0,
panY: 0
};
+
let fullScreenMode = false;
// Create tooltip
@@ -211,44 +99,46 @@ onUiLoaded(async() => {
action: "Adjust brush size",
keySuffix: " + wheel"
},
+ {configKey: "canvas_zoom_hotkey_undo", action: "Undo last action", keyPrefix: `${hotkeysConfig.canvas_zoom_undo_extra_key} + ` },
{configKey: "canvas_hotkey_reset", action: "Reset zoom"},
{
configKey: "canvas_hotkey_fullscreen",
action: "Fullscreen mode"
},
- {configKey: "canvas_hotkey_move", action: "Move canvas"},
- {configKey: "canvas_hotkey_overlap", action: "Overlap"}
+ {configKey: "canvas_hotkey_move", action: "Move canvas"}
];
- // Create hotkeys array with disabled property based on the config values
- const hotkeys = hotkeysInfo.map(info => {
+ // Create hotkeys array based on the config values
+ const hotkeys = hotkeysInfo.map((info) => {
const configValue = hotkeysConfig[info.configKey];
- const key = info.keySuffix ?
- `${configValue}${info.keySuffix}` :
- configValue.charAt(configValue.length - 1);
- return {
- key,
- action: info.action,
- disabled: configValue === "disable"
- };
- });
-
- for (const hotkey of hotkeys) {
- if (hotkey.disabled) {
- continue;
+
+ let key = configValue.slice(-1);
+
+ if (info.keySuffix) {
+ key = `${configValue}${info.keySuffix}`;
}
+
+ if (info.keyPrefix && info.keyPrefix !== "None + ") {
+ key = `${info.keyPrefix}${configValue[3]}`;
+ }
+
+ return {
+ key,
+ action: info.action,
+ };
+ });
+
+ hotkeys
+ .forEach(hotkey => {
+ const p = document.createElement("p");
+ p.innerHTML = `${hotkey.key} - ${hotkey.action}`;
+ tooltipContent.appendChild(p);
+ });
+
+ tooltip.append(info, tooltipContent);
- const p = document.createElement("p");
- p.innerHTML = `${hotkey.key} - ${hotkey.action}`;
- tooltipContent.appendChild(p);
- }
-
- // Add information and content elements to the tooltip element
- tooltip.appendChild(info);
- tooltip.appendChild(tooltipContent);
-
- // Add a hint element to the target element
- toolTipElemnt.appendChild(tooltip);
+ // Add a hint element to the target element
+ toolTipElemnt.appendChild(tooltip);
}
//Show tool tip if setting enable
@@ -264,9 +154,7 @@ onUiLoaded(async() => {
panY: 0
};
- if (isExtension) {
- targetElement.style.overflow = "hidden";
- }
+ targetElement.style.overflow = "hidden";
targetElement.isZoomed = false;
@@ -284,7 +172,7 @@ onUiLoaded(async() => {
closeBtn.addEventListener("click", resetZoom);
}
- if (canvas && isExtension) {
+ if (canvas) {
const parentElement = targetElement.closest('[id^="component-"]');
if (
canvas &&
@@ -297,16 +185,6 @@ onUiLoaded(async() => {
}
- if (
- canvas &&
- !isExtension &&
- parseFloat(canvas.style.width) > 865 &&
- parseFloat(targetElement.style.width) > 865
- ) {
- fitToElement();
- return;
- }
-
targetElement.style.width = "";
}
@@ -372,12 +250,10 @@ onUiLoaded(async() => {
targetElement.style.transformOrigin = "0 0";
targetElement.style.transform = `translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px) scale(${newZoomLevel})`;
+ targetElement.style.overflow = "visible";
toggleOverlap("on");
- if (isExtension) {
- targetElement.style.overflow = "visible";
- }
-
+
return newZoomLevel;
}
@@ -388,6 +264,7 @@ onUiLoaded(async() => {
let zoomPosX, zoomPosY;
let delta = 0.2;
+
if (elemData[elemId].zoomLevel > 7) {
delta = 0.9;
} else if (elemData[elemId].zoomLevel > 2) {
@@ -421,12 +298,7 @@ onUiLoaded(async() => {
let parentElement;
- if (isExtension) {
- parentElement = targetElement.closest('[id^="component-"]');
- } else {
- parentElement = targetElement.parentElement;
- }
-
+ parentElement = targetElement.closest('[id^="component-"]');
// Get element and screen dimensions
const elementWidth = targetElement.offsetWidth;
@@ -455,6 +327,26 @@ onUiLoaded(async() => {
toggleOverlap("off");
}
+ // Undo last action
+ function undoLastAction(e) {
+ let isCtrlPressed = isModifierKey(e, hotkeysConfig.canvas_zoom_undo_extra_key)
+ const isAuxButton = e.button >= 3;
+
+ if (isAuxButton) {
+ isCtrlPressed = true
+ } else {
+ if (!isModifierKey(e, hotkeysConfig.canvas_zoom_undo_extra_key)) return;
+ }
+
+ // Move undoBtn query outside the if statement to avoid unnecessary queries
+ const undoBtn = document.querySelector(`${activeElement} button[aria-label="Undo"]`);
+
+ if ((isCtrlPressed) && undoBtn ) {
+ e.preventDefault();
+ undoBtn.click();
+ }
+ }
+
/**
* This function fits the target element to the screen by calculating
* the required scale and offsets. It also updates the global variables
@@ -469,13 +361,8 @@ onUiLoaded(async() => {
if (!canvas) return;
- if (canvas.offsetWidth > 862 || isExtension) {
- targetElement.style.width = (canvas.offsetWidth + 2) + "px";
- }
-
- if (isExtension) {
- targetElement.style.overflow = "visible";
- }
+ targetElement.style.width = (canvas.offsetWidth + 2) + "px";
+ targetElement.style.overflow = "visible";
if (fullScreenMode) {
resetZoom();
@@ -549,11 +436,11 @@ onUiLoaded(async() => {
}
}
-
const hotkeyActions = {
[hotkeysConfig.canvas_hotkey_reset]: resetZoom,
[hotkeysConfig.canvas_hotkey_overlap]: toggleOverlap,
- [hotkeysConfig.canvas_hotkey_fullscreen]: fitToScreen
+ [hotkeysConfig.canvas_hotkey_fullscreen]: fitToScreen,
+ [hotkeysConfig.canvas_zoom_hotkey_undo]: undoLastAction,
};
const action = hotkeyActions[event.code];
@@ -597,26 +484,27 @@ onUiLoaded(async() => {
}
targetElement.addEventListener("mousemove", getMousePosition);
+ targetElement.addEventListener("auxclick", undoLastAction);
//observers
// Creating an observer with a callback function to handle DOM changes
const observer = new MutationObserver((mutationsList, observer) => {
for (let mutation of mutationsList) {
- // If the style attribute of the canvas has changed, by observation it happens only when the picture changes
- if (mutation.type === 'attributes' && mutation.attributeName === 'style' &&
- mutation.target.tagName.toLowerCase() === 'canvas') {
- targetElement.isExpanded = false;
- setTimeout(resetZoom, 10);
- }
+ // If the style attribute of the canvas has changed, by observation it happens only when the picture changes
+ if (mutation.type === 'attributes' && mutation.attributeName === 'style' &&
+ mutation.target.tagName.toLowerCase() === 'canvas') {
+ targetElement.isExpanded = false;
+ setTimeout(resetZoom, 10);
+ }
}
- });
-
- // Apply auto expand if enabled
- if (hotkeysConfig.canvas_auto_expand) {
+ });
+
+ // Apply auto expand if enabled
+ if (hotkeysConfig.canvas_auto_expand) {
targetElement.addEventListener("mousemove", autoExpand);
// Set up an observer to track attribute changes
- observer.observe(targetElement, {attributes: true, childList: true, subtree: true});
- }
+ observer.observe(targetElement, { attributes: true, childList: true, subtree: true });
+ }
// Handle events only inside the targetElement
let isKeyDownHandlerAttached = false;
@@ -661,7 +549,7 @@ onUiLoaded(async() => {
function handleMoveKeyDown(e) {
// Disable key locks to make pasting from the buffer work correctly
- if ((e.ctrlKey && e.code === 'KeyV') || (e.ctrlKey && event.code === 'KeyC') || e.code === "F5") {
+ if ((e.ctrlKey && e.code === 'KeyV') || (e.ctrlKey && e.code === 'KeyC') || e.code === "F5") {
return;
}
@@ -713,11 +601,7 @@ onUiLoaded(async() => {
if (isMoving && elemId === activeElement) {
updatePanPosition(e.movementX, e.movementY);
targetElement.style.pointerEvents = "none";
-
- if (isExtension) {
- targetElement.style.overflow = "visible";
- }
-
+ targetElement.style.overflow = "visible";
} else {
targetElement.style.pointerEvents = "auto";
}
@@ -745,18 +629,13 @@ onUiLoaded(async() => {
}
}
- if (isExtension) {
- targetElement.addEventListener("mousemove", checkForOutBox);
- }
-
+ targetElement.addEventListener("mousemove", checkForOutBox);
window.addEventListener('resize', (e) => {
resetZoom();
- if (isExtension) {
- targetElement.isExpanded = false;
- targetElement.isZoomed = false;
- }
+ targetElement.isExpanded = false;
+ targetElement.isZoomed = false;
});
gradioApp().addEventListener("mousemove", handleMoveByKey);
diff --git a/ldm_patched/contrib/external_sag.py b/ldm_patched/contrib/external_sag.py
index 59d1890c..3505b44e 100644
--- a/ldm_patched/contrib/external_sag.py
+++ b/ldm_patched/contrib/external_sag.py
@@ -29,9 +29,7 @@ def attention_basic_with_sim(q, k, v, heads, mask=None):
# force cast to fp32 to avoid overflowing
if _ATTN_PRECISION =="fp32":
- with torch.autocast(enabled=False, device_type = 'cuda'):
- q, k = q.float(), k.float()
- sim = einsum('b i d, b j d -> b i j', q, k) * scale
+ sim = einsum('b i d, b j d -> b i j', q.float(), k.float()) * scale
else:
sim = einsum('b i d, b j d -> b i j', q, k) * scale
@@ -113,7 +111,6 @@ class SelfAttentionGuidance:
m = model.clone()
attn_scores = None
- mid_block_shape = None
# TODO: make this work properly with chunked batches
# currently, we can only save the attn from one UNet call
@@ -136,7 +133,6 @@ class SelfAttentionGuidance:
def post_cfg_function(args):
nonlocal attn_scores
- nonlocal mid_block_shape
uncond_attn = attn_scores
sag_scale = scale
diff --git a/ldm_patched/ldm/modules/attention.py b/ldm_patched/ldm/modules/attention.py
index f4579bac..49e502ed 100644
--- a/ldm_patched/ldm/modules/attention.py
+++ b/ldm_patched/ldm/modules/attention.py
@@ -104,9 +104,7 @@ def attention_basic(q, k, v, heads, mask=None):
# force cast to fp32 to avoid overflowing
if _ATTN_PRECISION =="fp32":
- with torch.autocast(enabled=False, device_type = 'cuda'):
- q, k = q.float(), k.float()
- sim = einsum('b i d, b j d -> b i j', q, k) * scale
+ sim = einsum('b i d, b j d -> b i j', q.float(), k.float()) * scale
else:
sim = einsum('b i d, b j d -> b i j', q, k) * scale
diff --git a/ldm_patched/modules/samplers.py b/ldm_patched/modules/samplers.py
index 9996e74c..4e13d727 100644
--- a/ldm_patched/modules/samplers.py
+++ b/ldm_patched/modules/samplers.py
@@ -2,6 +2,7 @@ from ldm_patched.k_diffusion import sampling as k_diffusion_sampling
from ldm_patched.unipc import uni_pc
import torch
import enum
+import collections
from ldm_patched.modules import model_management
import math
from ldm_patched.modules import model_base
@@ -61,9 +62,7 @@ def get_area_and_mult(conds, x_in, timestep_in):
for c in model_conds:
conditioning[c] = model_conds[c].process_cond(batch_size=x_in.shape[0], device=x_in.device, area=area)
- control = None
- if 'control' in conds:
- control = conds['control']
+ control = conds.get('control', None)
patches = None
if 'gligen' in conds:
@@ -78,7 +77,8 @@ def get_area_and_mult(conds, x_in, timestep_in):
patches['middle_patch'] = [gligen_patch]
- return (input_x, mult, conditioning, area, control, patches)
+ cond_obj = collections.namedtuple('cond_obj', ['input_x', 'mult', 'conditioning', 'area', 'control', 'patches'])
+ return cond_obj(input_x, mult, conditioning, area, control, patches)
def cond_equal_size(c1, c2):
if c1 is c2:
@@ -91,24 +91,24 @@ def cond_equal_size(c1, c2):
return True
def can_concat_cond(c1, c2):
- if c1[0].shape != c2[0].shape:
+ if c1.input_x.shape != c2.input_x.shape:
return False
- #control
- if (c1[4] is None) != (c2[4] is None):
- return False
- if c1[4] is not None:
- if c1[4] is not c2[4]:
+ def objects_concatable(obj1, obj2):
+ if (obj1 is None) != (obj2 is None):
return False
+ if obj1 is not None:
+ if obj1 is not obj2:
+ return False
+ return True
- #patches
- if (c1[5] is None) != (c2[5] is None):
+ if not objects_concatable(c1.control, c2.control):
return False
- if (c1[5] is not None):
- if c1[5] is not c2[5]:
- return False
- return cond_equal_size(c1[2], c2[2])
+ if not objects_concatable(c1.patches, c2.patches):
+ return False
+
+ return cond_equal_size(c1.conditioning, c2.conditioning)
def cond_cat(c_list):
c_crossattn = []
@@ -184,13 +184,13 @@ def calc_cond_uncond_batch(model, cond, uncond, x_in, timestep, model_options):
for x in to_batch:
o = to_run.pop(x)
p = o[0]
- input_x += [p[0]]
- mult += [p[1]]
- c += [p[2]]
- area += [p[3]]
- cond_or_uncond += [o[1]]
- control = p[4]
- patches = p[5]
+ input_x.append(p.input_x)
+ mult.append(p.mult)
+ c.append(p.conditioning)
+ area.append(p.area)
+ cond_or_uncond.append(o[1])
+ control = p.control
+ patches = p.patches
batch_chunks = len(cond_or_uncond)
input_x = torch.cat(input_x)
diff --git a/modules/core.py b/modules/core.py
index f23ca6c0..a72dc46a 100644
--- a/modules/core.py
+++ b/modules/core.py
@@ -191,7 +191,7 @@ def encode_vae_inpaint(vae, pixels, mask):
latent_mask = mask[:, None, :, :]
latent_mask = torch.nn.functional.interpolate(latent_mask, size=(H * 8, W * 8), mode="bilinear").round()
- latent_mask = torch.nn.functional.max_pool2d(latent_mask, (8, 8)).round()
+ latent_mask = torch.nn.functional.max_pool2d(latent_mask, (8, 8)).round().to(latent)
return latent, latent_mask
diff --git a/modules/patch.py b/modules/patch.py
index da678d00..0a04bafb 100644
--- a/modules/patch.py
+++ b/modules/patch.py
@@ -269,9 +269,10 @@ def sdxl_encode_adm_patched(self, **kwargs):
height = float(height) * positive_adm_scale
def embedder(number_list):
- h = [self.embedder(torch.Tensor([number])) for number in number_list]
- y = torch.flatten(torch.cat(h)).unsqueeze(dim=0).repeat(clip_pooled.shape[0], 1)
- return y
+ h = torch.tensor(number_list, dtype=torch.float32)
+ h = self.embedder(h)
+ h = torch.flatten(h).unsqueeze(dim=0).repeat(clip_pooled.shape[0], 1)
+ return h
width, height = round_to_64(width), round_to_64(height)
target_width, target_height = round_to_64(target_width), round_to_64(target_height)
diff --git a/readme.md b/readme.md
index 8a91c320..78682fc0 100644
--- a/readme.md
+++ b/readme.md
@@ -121,7 +121,7 @@ See also the common problems and troubleshoots [here](troubleshoot.md).
In Colab, you can modify the last line to `!python entry_with_update.py --share` or `!python entry_with_update.py --preset anime --share` or `!python entry_with_update.py --preset realistic --share` for Fooocus Default/Anime/Realistic Edition.
-Note that this Colab will disable refiner by default because Colab free's resource is relatively limited.
+Note that this Colab will disable refiner by default because Colab free's resource is relatively limited (and some "big" features like image prompt may cause free-tier Colab to disconnect). We make sure that basic text-to-image is always working on free-tier Colab.
Thanks to [camenduru](https://github.com/camenduru)!
@@ -384,7 +384,7 @@ See also [About Forking and Promotion of Forks](https://github.com/lllyasviel/Fo
## Thanks
-Special thanks to [twri](https://github.com/twri) and [3Diva](https://github.com/3Diva) for creating additional SDXL styles available in Fooocus.
+Special thanks to [twri](https://github.com/twri) and [3Diva](https://github.com/3Diva) for creating additional SDXL styles available in Fooocus. Thanks [daswer123](https://github.com/daswer123) for contributing the Canvas Zoom!
## Update Log
diff --git a/update_log.md b/update_log.md
index f889070b..640b5665 100644
--- a/update_log.md
+++ b/update_log.md
@@ -1,3 +1,15 @@
+# 2.1.843
+
+* Many improvements to Canvas. Thanks CanvasZoom author!
+
+# 2.1.841
+
+* Backend maintain.
+* Fix some potential frozen after model mismatch.
+* Fix crash when cfg=1 when using anime preset.
+* Added some guidelines for troubleshoot the "CUDA kernel errors asynchronously" problem.
+* Fix inpaint device problem in `--always-gpu` mode.
+
# 2.1.839
* Maintained some computation codes in backend for efficiency.