Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions comfy_extras/nodes_compositor.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,9 @@ def execute(cls, layers: io.Layers.Type, compositor: io.Compositor.Type = None)
raw_state = compositor
state = parse_layer_state(raw_state)
replay = bool(state is not None and tensors and state["inputs"] == fp)
canvas = None
if replay:
canvas = state["canvas"]
out = composite_from_state(tensors, state, alphas)
elif tensors:
canvas = document_canvas(layers) or canvas_extent(frames)
Expand All @@ -544,6 +546,8 @@ def execute(cls, layers: io.Layers.Type, compositor: io.Compositor.Type = None)
ui_dict["compositor_layers"] = layer_refs
ui_dict["compositor_inputs"] = fp
ui_dict["compositor_bboxes"] = layer_ui_entries(frames)
if canvas is not None:
ui_dict["compositor_canvas"] = [{"w": int(canvas[0]), "h": int(canvas[1])}]
if state_stale:
ui_dict["compositor_state_stale"] = [True]
return io.NodeOutput(out, mask, ui=ui_dict)
Expand Down
41 changes: 41 additions & 0 deletions tests-unit/comfy_extras_test/compositor_node_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import torch

from comfy_extras.nodes_compositor import (
ImageCompositor,
_layer_params,
composite_from_state,
expand_item_frames,
Expand Down Expand Up @@ -70,3 +71,43 @@ def test_uncovered_canvas_stays_transparent(self):
assert out.shape[-1] == 4
assert float(out[0, 0, 3]) == pytest.approx(1.0)
assert float(out[3, 3, 3]) == pytest.approx(0.0)


class TestCanvasEmission:
"""execute must report the document canvas so the editor sizes itself to it
rather than to the max natural size of cropped/placed layers."""

def test_explicit_document_canvas_is_emitted(self):
# A small layer placed on a large explicit canvas: the editor must learn
# the 1280x1280 canvas, not the 200x150 layer size.
doc = {
"version": 1,
"canvas": (1280, 1280),
"layers": [
{"image": _solid([1.0, 0.0, 0.0, 1.0], w=200, h=150),
"type": "raster", "x": 400, "y": 300, "z_index": 0}
],
}
ui = ImageCompositor.execute(layers=doc).ui
assert ui["compositor_canvas"] == [{"w": 1280, "h": 1280}]

def test_replay_emits_saved_canvas(self):
tensor = _solid([0.0, 1.0, 0.0, 1.0], w=4, h=4)
doc = {"version": 1, "layers": [{"image": tensor, "type": "raster"}]}
fp = ImageCompositor.execute(layers=doc).ui["compositor_inputs"]
saved = {
"version": 1,
"canvas": {"w": 640, "h": 480},
"inputs": fp,
"layers": [{
"name": "a", "visible": True, "opacity": 1.0, "blend": "normal",
"flipH": False, "flipV": False,
"transform": {"x": 0, "y": 0, "w": 4, "h": 4, "rotation": 0.0},
}],
}
ui = ImageCompositor.execute(layers=doc, compositor=saved).ui
assert ui["compositor_canvas"] == [{"w": 640, "h": 480}]

def test_no_layers_emits_no_canvas(self):
ui = ImageCompositor.execute(layers={"version": 1, "layers": []}).ui
assert "compositor_canvas" not in ui
Loading