LC Outpaint, Rotate, & Pad π
Rotating a Picture Is Easy β Getting the Mask for the Corners Is the Hard Part
- image
- control_image
- control_mask
- width
- height
Straightening a photo, turning a landscape into a portrait, or cropping tighter and then padding back out are all two-part problems. The geometry is the easy half. The other half is that every pixel that didn't exist before has to be painted by a model, which means somebody has to tell the model exactly which pixels those are. This node does both on the node's own canvas and hands you the pair: the turned picture, and a mask that is white only where the model needs to work.
That makes it the front half of an outpaint or reframe workflow. Wire control_image into LC Krea2 AnyPaint or LC Z-Image AnyPaint, and control_mask into that node's mask input, and the corners get painted while the rest of your photo comes back untouched. Both of those nodes name their inputs "control_image" and "control_mask" precisely so this connection reads correctly on the canvas. If you already use LC Outpaint for canvas extension, this is the same idea with a rotation handle bolted on - plausible since they ship in the same pack.
How it works
Drag the round handle to turn the picture, hold Shift for 15Β° steps, or just type an angle (clockwise, -180 to 180). The frame around it is your output box: drag a handle outward to pad, inward to crop, and drag inside the frame to move the picture within it. The left, top, right, bottom widgets are the same thing as numbers, in percent of the source size, where positive pads and negative crops.
The interesting part is what happens to your pixels. Quarter turns - 0/90/180/270 - are done with an exact pixel copy, so a straight photo comes back bit-identical, no resampling, no softening. Anything in between goes through a bicubic grid sample, and the node marks a pixel as "keep" only if it sits fully inside the turned picture; edge pixels and the newly opened corners land in the mask. fill decides what sits in that masked area before the model sees it: gray is the default and is what Krea 2 and AnyPaint expect, edge stretches the outer pixels into the gap for models that just copy what they see, and white/black are there for when you want a hard visual cue.
fit is the choice you'll actually think about. expand starts the frame around the whole turned picture, which means the empty corners are masked and painted. crop starts at the largest box with no empty corners, so nothing needs inventing - use that when you're straightening a crooked shot and don't want the model touching it.
Inputs and outputs
Beyond image and angle, the ones worth setting: fit, fill, the four edge percents, aspect to lock the output shape (original matches the source), and snap_to, which rounds the output width and height up to a multiple (16 by default, 1 to turn it off). Rounding up means your output is a few pixels larger than the frame you dragged, with the extra going right and bottom - harmless for most models, slightly annoying if you're matching exact dimensions.
block defaults on and means "until turned or framed": while the picture is untouched, the run stops after this node. That's deliberate. Run once to get the preview and place the picture, set up the turn and frame, then run again to execute. keep_frame decides what happens next: keep (same image) holds your turn and frame run after run, which is what you want when you're re-processing the same image; snap back after run resets it once the run finishes so the next picture starts clean. Switching modes snaps the box back immediately.
Outputs are control_image (the turned, cropped, padded picture), control_mask (white = paint), and width/height as INTs, which are handy for sizing a latent or an empty image elsewhere in the graph. The node is also an output node with a preview, so you can run it on its own to see what you're doing.
Installing it
ComfyUI Manager: search "LC MaskMaker" (the pack is ComfyUI_LC_MaskMaker_nodes, part of ComfyUI LC MaskMaker Nodes by lonecatone23). Or:
cd ComfyUI/custom_nodes
git clone https://github.com/lonecatone23/ComfyUI_LC_MaskMaker_nodes
# restart ComfyUI
No model downloads and no heavy dependencies for this one - it's pure torch. The pack's install.py only checks that transformers, huggingface_hub and timm are importable, and only installs what's missing (the model-backed nodes need them; this node doesn't). Pack version 0.17.1, MIT.
Where people get burned
If the run seems to end early and nothing downstream fires, that's block doing its job, not a bug. Same for a first run that only produces a preview.
Two more to watch. snap_to at 16 changes your output dimensions, so a "why is my output 1040 instead of 1024" question usually ends here. And rotating by a non-quarter angle resamples the whole frame - for detail-critical work, don't rotate; crop and pad instead and let the model only fill the new area.
Inputs (13)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | Picture to turn, crop and pad. | |
| keep_frame | BOOLEAN | true | keep: the turn and frame stay as you set them, run after run. Use it when you rerun the same picture (post processing, same seed). snap back after run: once a run finishes, the turn and frame go back to the plain picture, so the next image starts clean. |
| angle | FLOAT | 0.0-180β180 | Turn in degrees, clockwise. Or drag the round handle on the node (hold Shift for 15Β° steps). |
| fit | COMBO | expand | expand: the frame starts around the whole turned picture, and the empty corners are masked for the model to paint. crop: the frame starts at the largest box with no empty corners. |
| fill | COMBO | gray | What goes in the new area before the model paints it. gray is what Krea 2 and AnyPaint expect. edge stretches the outer pixels out (for models that copy what they see). |
| left | FLOAT | 0.00-100β400 | Frame left edge, % of source width. Positive pads, negative crops. |
| top | FLOAT | 0.00-100β400 | Frame top edge, % of source height. Positive pads, negative crops. |
| right | FLOAT | 0.00-100β400 | Frame right edge, % of source width. Positive pads, negative crops. |
| bottom | FLOAT | 0.00-100β400 | Frame bottom edge, % of source height. Positive pads, negative crops. |
| aspect | COMBO | free | Lock the output shape. 'original' matches the source picture. |
| snap_to | COMBO | 16 | Round the output width and height up to a multiple of this (extra goes right and bottom). 1 = off. |
| block | BOOLEAN | true | until turned or framed: while the picture is untouched (no turn, no crop, no pad), the run stops after this node. Run once to see the picture here, set it up, then run again. never: always passes the picture on, even untouched. |
| max_megapixels | FLOAT | 2.00β64 | Stops the edges from being dragged past this canvas size, so the canvas never grows bigger than the model handles well (Krea 2 and AnyPaint like about 2). Only limits dragging: the run itself is never resized. 0 = no limit. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| control_image | IMAGE | The turned, cropped and padded picture. |
| control_mask | MASK | White where the model paints: the padding and the corners a turn opens up. |
| width | INT | Output width. |
| height | INT | Output height. |