CropOverlay

fun CropOverlay(rect: NormalizedRect, onRectChange: (NormalizedRect) -> Unit, output: Size, modifier: Modifier = Modifier, contentScale: VideoContentScale = VideoContentScale.Fit, constraint: CropConstraint = CropOverlayDefaults.Constraint, colors: CropColors = CropOverlayDefaults.Palette, handleLength: Dp = CropOverlayDefaults.HandleLength, touchSize: Dp = CropOverlayDefaults.TouchSize, showGrid: Boolean = true)(source)

A drag-to-crop rectangle over a video frame.

Controlled, so a drag reports the rectangle it wants through onRectChange and draws whatever rect is next passed back in. A caller that drops the change draws a rectangle that does not move.

The overlay measures itself and builds its own CropFrame from output and contentScale, so a host cannot hand it a rectangle sized against a mismatched letterbox. Inside a dev.jordond.filmstrip.compose.ui.VideoStage the stage has measured that already, and the overload on VideoStageScope reads its letterbox rather than taking an output of its own.

A constraint that arrives after the rectangle was drawn corrects it once, through onRectChange, so a lock picked from a chip reaches the rectangle without waiting for the next drag.

val surface = rememberVideoSurfaceState(player)
var rect by remember(source) { mutableStateOf(NormalizedRect.Full) }

Box(Modifier.videoAspect(surface, VideoContentScale.Fit)) {
VideoSurface(player, Modifier.fillMaxSize())

CropOverlay(
rect = rect,
onRectChange = { rect = it },
output = surface.outputSize,
constraint = CropConstraint.FixedAspect(ratio = 1f, minWidth = CropOverlayDefaults.MinWidth),
)
}

// The rectangle addresses the frame the overlay is drawn over, so the preview under it is the
// uncropped one and the crop joins the edit on the way out of the tool.
val cropped = remember(source, rect) {
compositionOf {
clip(source)
effects { crop(rect) }
}
}

Parameters

rect

Where the crop currently sits, in the frame's own normalized space.

onRectChange

Called with the rectangle a gesture asks for, already constrained.

output

The frame size the composition outputs, which the video is letterboxed against.

modifier

Modifier for the overlay, which fills whatever it is placed in.

contentScale

How the video fills the space the overlay measures.

constraint

What the gesture is allowed to produce.

colors

What the overlay paints with.

handleLength

How long a corner bracket's arms and an edge grip are drawn.

touchSize

How wide a handle answers to a finger, which is wider than it is drawn.

showGrid

Whether rule-of-thirds guides are drawn while a drag is in flight.


fun VideoStageScope.CropOverlay(rect: NormalizedRect, onRectChange: (NormalizedRect) -> Unit, modifier: Modifier = Modifier, constraint: CropConstraint = CropOverlayDefaults.Constraint, colors: CropColors = CropOverlayDefaults.Palette, handleLength: Dp = CropOverlayDefaults.HandleLength, touchSize: Dp = CropOverlayDefaults.TouchSize, showGrid: Boolean = true)(source)

A drag-to-crop rectangle over the video a dev.jordond.filmstrip.compose.ui.VideoStage is showing.

Everything the Size form does, reading the stage's own letterbox instead of measuring for one. A host that has already been letterboxed has no honest output size left to hand over, only the shape it was letterboxed to, and the rectangle this authors addresses the frame the stage is drawing.

var rect by remember(source) { mutableStateOf(NormalizedRect.Full) }
var lock: AspectRatio? by remember { mutableStateOf(null) }

VideoStage(player = player, outputAspect = outputAspect, modifier = Modifier.padding(16.dp)) {
CropOverlay(
rect = rect,
onRectChange = { rect = it },
constraint = lock?.let { CropConstraint.lockedTo(it, aspect) } ?: CropOverlayDefaults.Constraint,
)
}

// The rectangle addresses the frame the overlay is drawn over, so the preview under it is the
// uncropped one and the crop joins the edit on the way out of the tool.
compositionOf {
clip(source)
effects { crop(rect) }
}

Parameters

rect

Where the crop currently sits, in the frame's own normalized space.

onRectChange

Called with the rectangle a gesture asks for, already constrained.

modifier

Modifier for the overlay, which fills the stage.

constraint

What the gesture is allowed to produce.

colors

What the overlay paints with.

handleLength

How long a corner bracket's arms and an edge grip are drawn.

touchSize

How wide a handle answers to a finger, which is wider than it is drawn.

showGrid

Whether rule-of-thirds guides are drawn while a drag is in flight.