Fix canvas visibility and regional conditioning bugs
Fixed three critical issues reported from user testing: 1. Canvas not visible (JavaScript) - Set explicit canvas width/height attributes (not just CSS) - Added fallback dimensions (200px minimum) - Force initial canvas size computation with setTimeout - Added safeguards for empty/undefined values array - Show helpful message when no regions defined 2. Regions not showing (only background appeared) - Added default template boxes to both nodes - RegionalPrompterSimple: 2 boxes at 512x512 - RegionalPrompterFlux: 2 boxes at 1024x1024 - Boxes only used if workflow not saved yet - Matches JavaScript defaults perfectly 3. Correct CLIP encoder detection - Detect Flux via hasattr(clip, 'encode_from_tokens_scheduled') - Flux: dual encoder (CLIP-L + T5-XXL) with guidance - Chroma/Qwen/SD3: single encoder (standard encoding) - Added guidance parameter to RegionalPrompterFlux User can now: - See canvas drawing interface immediately - Get working regional prompts without saving workflow first - Use correct encoding for each model type
This commit is contained in:
+68
-14
@@ -78,30 +78,38 @@ Compatible: SD1.5, SD2.x, SDXL"""
|
||||
region2_prompt="", region3_prompt="", region4_prompt=""):
|
||||
"""Encode all prompts and apply regional conditioning."""
|
||||
|
||||
values = []
|
||||
# Default template boxes (if workflow not saved yet)
|
||||
# Region 1 (red sports car): left side, 200x250px starting at (50, 150)
|
||||
# Region 2 (street vendor): right side, 180x250px starting at (280, 150)
|
||||
values = [
|
||||
[50, 150, 200, 250, 1.0], # Region 1
|
||||
[280, 150, 180, 250, 1.0] # Region 2
|
||||
]
|
||||
resolutionX = 512
|
||||
resolutionY = 512
|
||||
|
||||
# Get canvas dimensions and region data from UI
|
||||
# Get region data from UI (overrides defaults if workflow saved)
|
||||
try:
|
||||
if extra_pnginfo and "workflow" in extra_pnginfo and "nodes" in extra_pnginfo["workflow"]:
|
||||
for node in extra_pnginfo["workflow"]["nodes"]:
|
||||
if node["id"] == int(unique_id):
|
||||
values = node["properties"].get("values", [])
|
||||
saved_values = node["properties"].get("values", [])
|
||||
if saved_values: # Only override if we actually have saved values
|
||||
values = saved_values
|
||||
resolutionX = node["properties"].get("width", 512)
|
||||
resolutionY = node["properties"].get("height", 512)
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"ℹ️ Using default canvas size 512x512")
|
||||
print(f"ℹ️ Using default template boxes and canvas size 512x512")
|
||||
|
||||
# Collect all non-empty prompts
|
||||
prompts = [background_prompt, region1_prompt, region2_prompt, region3_prompt, region4_prompt]
|
||||
|
||||
# Encode each prompt using CLIP
|
||||
# Encode each prompt using CLIP (standard SD/SDXL encoding)
|
||||
encoded_conditionings = []
|
||||
for prompt in prompts:
|
||||
if prompt and prompt.strip(): # Only encode non-empty prompts
|
||||
# Use CLIPTextEncode functionality
|
||||
# Standard CLIP encoding for SD/SDXL
|
||||
tokens = clip.tokenize(prompt)
|
||||
cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)
|
||||
encoded_conditionings.append([[cond, {"pooled_output": pooled}]])
|
||||
@@ -199,6 +207,13 @@ class RegionalPrompterFlux:
|
||||
"default": True,
|
||||
"tooltip": "✅ RECOMMENDED: Softer masks (0.8 strength + gentle edge blend) work better than harsh full-strength (1.0) masks. Confirmed better for Flux, works well with other mask-based models. Disable if you prefer sharper region edges."
|
||||
}),
|
||||
"guidance": ("FLOAT", {
|
||||
"default": 3.5,
|
||||
"min": 0.0,
|
||||
"max": 100.0,
|
||||
"step": 0.1,
|
||||
"tooltip": "Guidance strength for Flux (default 3.5). Higher values = stronger prompt adherence. Try 5-7 for better regional control."
|
||||
}),
|
||||
},
|
||||
"optional": {
|
||||
"region2_prompt": ("STRING", {
|
||||
@@ -260,24 +275,32 @@ Model-Specific Tips:
|
||||
Compatible: Flux (all variants), Chroma, SD3, SD3.5, and other mask-based models"""
|
||||
|
||||
def encode_regions_flux(self, clip, width, height, background_prompt, region1_prompt,
|
||||
soften_masks, extra_pnginfo, unique_id,
|
||||
soften_masks, guidance, extra_pnginfo, unique_id,
|
||||
region2_prompt="", region3_prompt="", region4_prompt=""):
|
||||
"""Encode all prompts and apply mask-based regional conditioning for Flux."""
|
||||
"""Encode all prompts and apply mask-based regional conditioning for Flux/Chroma/SD3."""
|
||||
|
||||
values = []
|
||||
# Default template boxes (if workflow not saved yet)
|
||||
# Region 1 (red sports car): left side, 400x500px starting at (100, 300)
|
||||
# Region 2 (street vendor): right side, 350x500px starting at (560, 300)
|
||||
values = [
|
||||
[100, 300, 400, 500, 1.0], # Region 1
|
||||
[560, 300, 350, 500, 1.0] # Region 2
|
||||
]
|
||||
|
||||
# Get region data from UI
|
||||
# Get region data from UI (overrides defaults if workflow saved)
|
||||
try:
|
||||
if extra_pnginfo and "workflow" in extra_pnginfo and "nodes" in extra_pnginfo["workflow"]:
|
||||
for node in extra_pnginfo["workflow"]["nodes"]:
|
||||
if node["id"] == int(unique_id):
|
||||
values = node["properties"].get("values", [])
|
||||
saved_values = node["properties"].get("values", [])
|
||||
if saved_values: # Only override if we actually have saved values
|
||||
values = saved_values
|
||||
# Override with property dimensions if available
|
||||
prop_width = node["properties"].get("width", width)
|
||||
prop_height = node["properties"].get("height", height)
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"ℹ️ Using input dimensions {width}x{height}")
|
||||
print(f"ℹ️ Using default template boxes and dimensions {width}x{height}")
|
||||
prop_width = width
|
||||
prop_height = height
|
||||
|
||||
@@ -291,12 +314,43 @@ Compatible: Flux (all variants), Chroma, SD3, SD3.5, and other mask-based models
|
||||
print(f"⚠️ Warning: {num_regions} regions detected. Flux works best with 3-4 regions maximum.")
|
||||
|
||||
# Encode each prompt using CLIP
|
||||
# Flux uses dual-encoder (CLIP-L + T5-XXL), Chroma/Qwen use single encoder
|
||||
encoded_conditionings = []
|
||||
|
||||
# Detect if this is Flux by checking for encode_from_tokens_scheduled method
|
||||
is_flux = hasattr(clip, 'encode_from_tokens_scheduled')
|
||||
|
||||
for prompt in prompts:
|
||||
if prompt and prompt.strip():
|
||||
tokens = clip.tokenize(prompt)
|
||||
cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)
|
||||
encoded_conditionings.append([[cond, {"pooled_output": pooled}]])
|
||||
|
||||
if is_flux:
|
||||
# Flux-specific: Dual encoder (CLIP-L + T5-XXL) with guidance
|
||||
# Check if t5xxl tokens are available
|
||||
if "t5xxl" in tokens:
|
||||
# Already has both encoders in single tokenize call
|
||||
pass
|
||||
else:
|
||||
# Need to merge t5xxl tokens manually
|
||||
try:
|
||||
t5_result = clip.tokenize(prompt)
|
||||
if "t5xxl" in t5_result:
|
||||
tokens["t5xxl"] = t5_result["t5xxl"]
|
||||
except:
|
||||
pass
|
||||
|
||||
# Use Flux's scheduled encoding with guidance
|
||||
try:
|
||||
cond = clip.encode_from_tokens_scheduled(tokens, add_dict={"guidance": guidance})
|
||||
encoded_conditionings.append(cond)
|
||||
except Exception as e:
|
||||
print(f"⚠️ Flux encoding failed: {e}, using fallback")
|
||||
cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)
|
||||
encoded_conditionings.append([[cond, {"pooled_output": pooled}]])
|
||||
else:
|
||||
# Standard encoding for Chroma, Qwen, SD3, etc. (single encoder)
|
||||
cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)
|
||||
encoded_conditionings.append([[cond, {"pooled_output": pooled}]])
|
||||
else:
|
||||
encoded_conditionings.append(None)
|
||||
|
||||
|
||||
+42
-17
@@ -28,10 +28,10 @@ function addRegionalPrompterCanvas(node, app) {
|
||||
const margin = 10
|
||||
const border = 2
|
||||
|
||||
const widgetHeight = node.canvasHeight
|
||||
const values = node.properties["values"]
|
||||
const width = Math.round(node.properties["width"])
|
||||
const height = Math.round(node.properties["height"])
|
||||
const widgetHeight = node.canvasHeight || 200
|
||||
const values = node.properties["values"] || []
|
||||
const width = Math.round(node.properties["width"] || 512)
|
||||
const height = Math.round(node.properties["height"] || 512)
|
||||
|
||||
const scale = Math.min((widgetWidth-margin*2)/width, (widgetHeight-margin*2)/height)
|
||||
|
||||
@@ -45,11 +45,19 @@ function addRegionalPrompterCanvas(node, app) {
|
||||
}
|
||||
const index = indexWidget ? Math.round(indexWidget.value) : 0;
|
||||
|
||||
// Ensure canvas element has proper internal dimensions
|
||||
const canvasDisplayWidth = Math.max(200, widgetWidth * t.a);
|
||||
const canvasDisplayHeight = Math.max(200, widgetHeight * t.d);
|
||||
|
||||
// Set canvas internal resolution (for drawing)
|
||||
this.canvas.width = widgetWidth;
|
||||
this.canvas.height = widgetHeight;
|
||||
|
||||
Object.assign(this.canvas.style, {
|
||||
left: `${t.e}px`,
|
||||
top: `${t.f + (widgetY*t.d)}px`,
|
||||
width: `${widgetWidth * t.a}px`,
|
||||
height: `${widgetHeight * t.d}px`,
|
||||
width: `${canvasDisplayWidth}px`,
|
||||
height: `${canvasDisplayHeight}px`,
|
||||
position: "absolute",
|
||||
zIndex: 1,
|
||||
fontSize: `${t.d * 10.0}px`,
|
||||
@@ -128,21 +136,29 @@ function addRegionalPrompterCanvas(node, app) {
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
|
||||
// Draw currently selected zone
|
||||
let [x, y, w, h] = getDrawArea(values[index])
|
||||
// Draw currently selected zone (if values exist)
|
||||
if (values.length > 0 && index < values.length) {
|
||||
let [x, y, w, h] = getDrawArea(values[index])
|
||||
|
||||
w = Math.max(32*scale, w)
|
||||
h = Math.max(32*scale, h)
|
||||
w = Math.max(32*scale, w)
|
||||
h = Math.max(32*scale, h)
|
||||
|
||||
ctx.fillStyle = "#ffffff"
|
||||
ctx.fillRect(widgetX+x, widgetY+y, w, h)
|
||||
ctx.fillStyle = "#ffffff"
|
||||
ctx.fillRect(widgetX+x, widgetY+y, w, h)
|
||||
|
||||
const selectedColor = getDrawColor(index/values.length, "FF")
|
||||
ctx.fillStyle = selectedColor
|
||||
ctx.fillRect(widgetX+x+border, widgetY+y+border, w-border*2, h-border*2)
|
||||
const selectedColor = getDrawColor(index/values.length, "FF")
|
||||
ctx.fillStyle = selectedColor
|
||||
ctx.fillRect(widgetX+x+border, widgetY+y+border, w-border*2, h-border*2)
|
||||
|
||||
ctx.lineWidth = 1;
|
||||
ctx.closePath();
|
||||
ctx.lineWidth = 1;
|
||||
ctx.closePath();
|
||||
} else {
|
||||
// No regions defined yet - show helpful message
|
||||
ctx.fillStyle = "#ffffff80";
|
||||
ctx.font = "14px Arial";
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillText("Add regions by adjusting x/y/width/height below", widgetX + backgroundWidth/2, widgetY + backgroundHeight/2);
|
||||
}
|
||||
|
||||
},
|
||||
};
|
||||
@@ -150,6 +166,10 @@ function addRegionalPrompterCanvas(node, app) {
|
||||
widget.canvas = document.createElement("canvas");
|
||||
widget.canvas.className = "dave-custom-canvas";
|
||||
|
||||
// Set initial canvas dimensions (will be updated by draw function)
|
||||
widget.canvas.width = 512;
|
||||
widget.canvas.height = 512;
|
||||
|
||||
widget.parent = node;
|
||||
document.body.appendChild(widget.canvas);
|
||||
|
||||
@@ -172,6 +192,11 @@ function addRegionalPrompterCanvas(node, app) {
|
||||
computeCanvasSize(node, size);
|
||||
}
|
||||
|
||||
// Force initial canvas size computation
|
||||
setTimeout(() => {
|
||||
computeCanvasSize(node, node.size);
|
||||
}, 100);
|
||||
|
||||
return { minWidth: 200, minHeight: 200, widget }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user