Functions
Functions and constants exported by @mcut/compositor.
Constants
ROTATE_HANDLE_OFFSET
const ROTATE_HANDLE_OFFSET = 32Distance in canvas pixels from the top edge of a box to its rotate handle.
elementRenderers
const elementRenderers: { readonly [K in ElementType]: ElementRenderer<ElementByType[K]> }The closed table of element renderers keyed by ElementType, where each renderer receives the element of its own kind and the audio renderer draws nothing. The multicam renderer composes the active layout, or both layouts of an angle transition, into the compose scratch, then copies the composed pixels onto the target. The scratch shares the backend's pixelGrid, so scratch pixel (x, y) is target pixel (x, y), and each slot draws under the multicam's transform, crop, and zoom at the target pixels a direct draw would hit. The copy covers the target pixels the crop reaches, rounded out to whole pixels, and moves them one to one under any axis-aligned grid, so a composed frame resamples no more than a slot drawn straight onto the target, rotated or not. The scratch is the size of the target, so a keyframed scale reuses one scratch, a multicam magnified past the target composes only the target's pixels, a preview drawn at half size composes at half size, and a target without pixels composes nothing. An effect that reads neighboring pixels, as blur, drop shadow, and CSS filters do, draws from past the target's edge, so for one the scratch reaches past it, up to 8192 pixels a side, the default maxTextureDimension2D of WebGPU. A slot that reaches past the multicam's frame is cut at the frame. The multicam's opacity, blend mode, and effects apply once to the composed frame through the image quad a video clip draws through, and its crop, corner radius, stroke, and shadow frame the composed frame the way they frame a clip.
transitionRenderers
const transitionRenderers: { readonly [K in TransitionType]: TransitionRenderer }The closed table of transition renderers keyed by TransitionType, a dissolve, fades through black and white, slides, and wipes in both directions.
Functions
degToRad
const degToRad = (deg: number): number => (deg * Math.PI) / 180Converts degrees to radians.
fromCanvasPoint
function fromCanvasPoint(project: Project, x: number, y: number): { x: number; y: number }Converts a canvas pixel position to center-origin element coordinates.
getElementDisplaySize
function getElementDisplaySize(
project: Project,
element: TimelineElement,
helpers: SizeHelpers = {},
): ElementSize | nullReturns the element's natural size multiplied by the absolute transform scale, or null for elements without a transform or a known size.
getElementNaturalSize
function getElementNaturalSize(
project: Project,
element: TimelineElement,
helpers: SizeHelpers = {},
): ElementSize | nullReturns the unscaled size of an element, reduced to the crop region when one is set. Text and media sizes come from the helpers. A multicam is the project frame, because it composes its layout at the frame size. Audio and caption elements return null.
getElementOBB
function getElementOBB(
project: Project,
element: TimelineElement,
helpers: SizeHelpers = {},
): OBB | nullReturns the element's oriented bounding box in canvas pixels, or null for audio and caption elements and for media whose size is unknown.
getFitScale
function getFitScale(project: Project, width: number, height: number): numberReturns the scale that fits a media size inside the project frame while keeping its aspect ratio, or 1 for an empty size.
getTransformForDisplaySize
function getTransformForDisplaySize(
transform: Transform,
natural: ElementSize,
patch: DisplaySizePatch,
): TransformReturns the transform whose scale yields the requested display width or height, keeping each axis's flip sign and matching the other axis when aspect is preserved.
getHandles
function getHandles(obb: OBB): Handle[]Returns the eight resize handles and the rotate handle of a box in canvas space.
hitTestHandles
function hitTestHandles(obb: OBB, x: number, y: number, size = 12): HandleId | nullReturns the id of the handle whose square hit area of size pixels contains the point, or null.
hitTestOBB
function hitTestOBB(obb: OBB, x: number, y: number): booleanReturns true when the canvas point lies inside the rotated box.
toCanvasPoint
function toCanvasPoint(project: Project, x: number, y: number): { x: number; y: number }Converts center-origin element coordinates to canvas pixels.
getSlotBoxes
function getSlotBoxes(project: Project, element: MulticamElement, timelineMs: number): SlotBox[]Resolves the multicam at timelineMs and returns one box per shown slot of its active layout, in canvas pixels. Each box is the slot's frame carried through the placement the renderer composes with. It follows the multicam's crop, a zoom region without source, and the transform, and it is cut where the renderer cuts the slot, at the crop and at the edge of the frame. A zoom region with source moves the picture inside its slot and leaves the box in place. A slot the renderer shows none of has no box, and neither does a slot whose source is missing or audio only. During an angle transition the boxes are the active layout's at rest. The list is empty when no layout is active.
renderFrame
function renderFrame(
ctx: Canvas2D,
project: Project,
timeMs: number,
options: RenderFrameOptions = {},
): voidRenders one frame of the project at timeMs into a 2D context through the Canvas 2D backend.
renderFrameWith
function renderFrameWith(
backend: RenderBackend,
project: Project,
timeMs: number,
options: RenderFrameOptions = {},
): voidRenders one frame through the given backend, walking tracks bottom up and elements in start order and blending clips joined by an active transition.
applyChrome
function applyChrome(ctx: Canvas2D, chrome: LayerChrome, draw: () => void): voidRuns a Canvas 2D draw inside the transform, opacity, effect filter, and blend mode described by the chrome, degrading to no filter where ctx.filter is unsupported.
createElementContext
function createElementContext(
backend: RenderBackend,
project: Project,
track: Track,
timeMs: number,
options: RenderFrameOptions,
viewTimeMs: number = timeMs,
): ElementRenderContextBuilds the per-element render context for a backend, exposing ctx as a getter so the backend learns when raster drawing begins. The frame source and the compose scratch come from the render options. viewTimeMs is the time zoom views read, and each motion blur pass sets it to its own sample time.
drawImageQuad2D
function drawImageQuad2D(ctx: Canvas2D, quad: ImageQuad): voidDraws an image quad into a 2D context in local coordinates, applying its crop and corner radius.
isWebGPUSupported
function isWebGPUSupported(): booleanReturns true when the runtime exposes navigator.gpu.
curveToLut
function curveToLut(points: readonly CurvePoint[] | undefined): Float32ArrayConverts curve points with increasing x into a 256-entry lookup table by linear interpolation, or an identity table when empty.
hasUnsupportedEffects
function hasUnsupportedEffects(effects: readonly Effect[] | undefined): booleanReturns true when the effect stack contains an effect only the Canvas 2D raster path can render.
planEffects
function planEffects(effects: readonly Effect[] | undefined): EffectPlanConverts an effect stack into GPU passes, fusing consecutive color operations into one pass and flagging unsupported effects.
getImageSize
function getImageSize(source: CanvasImageSource): { width: number; height: number }Returns the pixel size of a video, image, bitmap, or canvas source.
measureWith
function measureWith(ctx: Canvas2D): MeasureFnReturns a MeasureFn that measures text width with the context's measureText after setting the font and letter spacing.
renderElementLayer
const renderElementLayer: ElementRenderer = (element, context) => renderTyped(element.type, element, context)Draws one element by dispatching on its type to the matching entry of elementRenderers.
applyTextTransform
function applyTextTransform(text: string, transform: TextStyle['textTransform']): stringReturns the text in upper or lower case according to the transform, or unchanged.
buildFont
function buildFont(style: {
fontStyle?: 'normal' | 'italic'
fontWeight: number
fontSize: number
fontFamily: string
}): stringBuilds a CSS font shorthand string from style, weight, size, and family.
layoutCaption
function layoutCaption(
measure: MeasureFn,
element: { text: string; words?: { text: string; startMs: number; endMs: number }[] },
style: CaptionStyle,
maxWidth: number,
): CaptionLayoutWraps caption words into centered lines no wider than maxWidth, keeping each word's timing, and falls back to whitespace splitting without timings.
layoutTextBlock
function layoutTextBlock(
measure: MeasureFn,
text: string,
style: TextStyle,
options: TextBlockOptions = {},
): TextBlockLayoutLays out a text element into lines, breaking only at newlines unless a text box width is provided.