Files
Ewald 831b072943 Add professional compositing engines, HDR fixes, and native VIDEO support
Core (nodes/core/): alpha, masks, batch consolidation, blend, transform
matrix, bbox, parameters, validation engines shared across nodes, each with
unit tests.

Fix an unconditional RGB clamp to [0,1] that silently destroyed HDR/negative
values in Merge, Transform, Color Correct, Comp, CornerPin, Distort,
Spherize, Crop Stitch, and the shared masked-compositing helpers. Alpha
stays clamped everywhere; RGB no longer does.

Fix Grain applying `amount` twice in overlay/soft_light modes (quadratic
instead of linear falloff).

Add STMap mode to Distort (absolute UV remap vs relative displacement).
Add bbox output/input wiring between Crop and Crop Stitch. Add edge_grow
and edge_color_match to Crop Stitch. Add despill to Keyer. Add shuffle mode
to Channel (R/G/B/A routed from any source). Add size/luma_response to
Grain. Add grid mode to Constant. Add frame/layer accumulation limits to
Preview and Draw, plus a font-path containment fix and supersampling in
Text.

Make Frame Range and Append recognize a real ComfyUI VIDEO input (not only
this pack's internal media wrapper), preserving true fps/audio instead of
silently dropping them, and add a native VIDEO output alongside the
existing IMAGE output on both.

Add node_replacements.py (empty registry, wired into the extension
lifecycle) for future schema migrations.

Add a CI matrix pinning the verified requires-comfyui floor, and
.comfyignore for Registry packaging.
2026-09-27 11:55:49 +02:00

72 lines
1.9 KiB
Python

from __future__ import annotations
import torch
from nodes.core.alpha import premultiply, replace_alpha, split_rgba, unpremultiply
def test_split_rgba_with_alpha():
image = torch.rand((2, 4, 4, 4))
rgb, alpha = split_rgba(image)
assert rgb.shape == (2, 4, 4, 3)
assert alpha.shape == (2, 4, 4, 1)
assert torch.allclose(rgb, image[..., :3])
assert torch.allclose(alpha, image[..., 3:4])
def test_split_rgba_without_alpha():
image = torch.rand((2, 4, 4, 3))
rgb, alpha = split_rgba(image)
assert alpha is None
assert torch.allclose(rgb, image)
def test_premultiply_unpremultiply_round_trip():
rgb = torch.rand((1, 4, 4, 3)) * 2.0 - 0.5 # includes negative and >1 values
alpha = torch.rand((1, 4, 4, 1)).clamp(0.2, 1.0) # keep away from zero
premult = premultiply(rgb, alpha)
straight = unpremultiply(premult, alpha)
assert torch.allclose(straight, rgb, atol=1e-5)
def test_premultiply_none_alpha_is_noop():
rgb = torch.rand((1, 4, 4, 3))
assert torch.allclose(premultiply(rgb, None), rgb)
assert torch.allclose(unpremultiply(rgb, None), rgb)
def test_unpremultiply_zero_alpha_returns_zero_not_nan():
rgb = torch.full((1, 1, 1, 3), 5.0)
alpha = torch.zeros((1, 1, 1, 1))
result = unpremultiply(rgb, alpha)
assert torch.isfinite(result).all()
assert torch.allclose(result, torch.zeros_like(result))
def test_unpremultiply_preserves_hdr_values():
rgb = torch.tensor([[[[4.0, -1.0, 2.0]]]])
alpha = torch.ones((1, 1, 1, 1))
result = unpremultiply(rgb, alpha)
assert torch.allclose(result, rgb)
def test_replace_alpha_clamps_and_keeps_rgb():
image = torch.rand((1, 4, 4, 4)) * 2.0
new_alpha = torch.full((1, 4, 4, 1), 1.5)
out = replace_alpha(image, new_alpha)
assert torch.allclose(out[..., :3], image[..., :3])
assert torch.allclose(out[..., 3:4], torch.ones_like(new_alpha))