From e99602e5abfe503bad59784787265ea4f6e01482 Mon Sep 17 00:00:00 2001 From: drozbay <17261091+drozbay@users.noreply.github.com> Date: Sun, 28 Jun 2026 12:35:42 -0600 Subject: [PATCH] Condense input parameters --- comfy_extras/nodes_text_overlay.py | 49 ++++++++++++++++++------------ 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/comfy_extras/nodes_text_overlay.py b/comfy_extras/nodes_text_overlay.py index dfdf8e9f7..6e5fcbf8b 100644 --- a/comfy_extras/nodes_text_overlay.py +++ b/comfy_extras/nodes_text_overlay.py @@ -5,9 +5,6 @@ from typing_extensions import override from comfy_api.latest import ComfyExtension, IO -LINE_SPACING = 1.2 -BANNER_OPACITY = 0.45 - class TextOverlay(IO.ComfyNode): @classmethod @@ -21,33 +18,45 @@ class TextOverlay(IO.ComfyNode): inputs=[ IO.Image.Input("image"), IO.String.Input("text", multiline=True, default=""), + IO.Float.Input("font_size_percent", default=5.0, min=0.5, max=50.0, step=0.5, tooltip="Font size as a percentage of the image height.", advanced=True), + #IO.Combo.Input("text_color", options=["white", "black", "red", "green", "blue", "yellow", "cyan", "magenta", "gray"], default="white"), + #IO.Combo.Input("outline_color", options=["auto", "none", "black", "white", "red", "green", "blue", "yellow"], default="auto"), ], outputs=[IO.Image.Output()], ) @classmethod - def execute(cls, image, text, font_size_percent=5.0, text_color="white", outline=True, background=False, background_color="auto", margin_percent=1.0) -> IO.NodeOutput: + def execute(cls, image, text, font_size_percent=5.0, text_color="white", outline_color="auto", margin_percent=1.0, + line_spacing=1.2, background_opacity=0.0, min_font_percent=2.0, outline_thickness_factor=0.04) -> IO.NodeOutput: if text.strip() == "": return IO.NodeOutput(image) text = text.replace("\\n", "\n").replace("\\t", "\t") try: - fill_color = ImageColor.getrgb(text_color)[:3] + text_color = ImageColor.getrgb(text_color)[:3] except ValueError: - fill_color = (255, 255, 255) - if background_color.lower() == "auto": - luminance = 0.299 * fill_color[0] + 0.587 * fill_color[1] + 0.114 * fill_color[2] - contrast_color = (0, 0, 0) if luminance > 140 else (255, 255, 255) - else: - contrast_color = ImageColor.getrgb(background_color)[:3] + text_color = (255, 255, 255) - frames = [cls.render_text_on_frame(frame, text, font_size_percent, margin_percent, fill_color, contrast_color, outline, background) + luminance = 0.299 * text_color[0] + 0.587 * text_color[1] + 0.114 * text_color[2] + contrast_color = (0, 0, 0) if luminance > 40 else (255, 255, 255) + choice = outline_color.lower() + if choice == "none": + outline_color = None + elif choice == "auto": + outline_color = contrast_color + else: + outline_color = ImageColor.getrgb(outline_color)[:3] + background_color = contrast_color if outline_color is None else outline_color + + frames = [cls.render_text_on_frame(frame, text, font_size_percent, margin_percent, text_color, outline_color, background_color, + line_spacing, background_opacity, min_font_percent, outline_thickness_factor) for frame in image] return IO.NodeOutput(torch.stack(frames, dim=0)) @classmethod - def render_text_on_frame(cls, frame, text, font_size_percent, margin_percent, fill_color, contrast_color, outline, background): + def render_text_on_frame(cls, frame, text, font_size_percent, margin_percent, text_color, outline_color, background_color, + line_spacing=1.2, background_opacity=0.0, min_font_percent=2.0, outline_thickness_factor=0.04, min_font_px=10): pil_image = PILImage.fromarray((frame.clamp(0.0, 1.0).cpu().numpy() * 255.0).astype(np.uint8), mode="RGB") width, height = pil_image.width, pil_image.height @@ -57,26 +66,26 @@ class TextOverlay(IO.ComfyNode): # Font scales with resolution, then shrinks to fit the height. size = max(1, int(round(font_size_percent / 100.0 * height))) - floor = min(size, max(10, int(round(0.02 * height)))) + floor = min(size, max(min_font_px, int(round(min_font_percent / 100.0 * height)))) while True: font = ImageFont.load_default(size=size) lines = cls.wrap_text(text, font, max_width) - line_height = size * LINE_SPACING + line_height = size * line_spacing if line_height * len(lines) <= max_height or size <= floor: break size = max(floor, int(size * 0.9)) - if background: - banner_bottom = 2 * margin + line_height * len(lines) + if background_opacity > 0: + background_bottom = 2 * margin + line_height * len(lines) overlay = PILImage.new("RGBA", pil_image.size, (0, 0, 0, 0)) - ImageDraw.Draw(overlay).rectangle([0, 0, width, banner_bottom], fill=(*contrast_color, int(round(BANNER_OPACITY * 255)))) + ImageDraw.Draw(overlay).rectangle([0, 0, width, background_bottom], fill=(*background_color, int(round(background_opacity * 255)))) pil_image = PILImage.alpha_composite(pil_image.convert("RGBA"), overlay).convert("RGB") draw = ImageDraw.Draw(pil_image) - stroke = max(1, int(round(size / 24))) if outline else 0 + stroke = max(1, int(round(size * outline_thickness_factor))) if outline_color is not None else 0 for index, line in enumerate(lines): draw.text((margin, margin + index * line_height), line, font=font, - fill=fill_color, stroke_width=stroke, stroke_fill=contrast_color) + fill=text_color, stroke_width=stroke, stroke_fill=outline_color) return torch.from_numpy(np.array(pil_image).astype(np.float32) / 255.0)