mcut
@mcut/compositor

Functions

Functions and constants exported by @mcut/compositor.

Constants

ROTATE_HANDLE_OFFSET

const ROTATE_HANDLE_OFFSET = 32

Distance 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) / 180

Converts 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 | null

Returns 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 | null

Returns 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 | null

Returns 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): number

Returns 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,
): Transform

Returns 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 | null

Returns 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): boolean

Returns 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 = {},
): void

Renders 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 = {},
): void

Renders 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): void

Runs 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,
): ElementRenderContext

Builds 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): void

Draws an image quad into a 2D context in local coordinates, applying its crop and corner radius.

isWebGPUSupported

function isWebGPUSupported(): boolean

Returns true when the runtime exposes navigator.gpu.

curveToLut

function curveToLut(points: readonly CurvePoint[] | undefined): Float32Array

Converts 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): boolean

Returns true when the effect stack contains an effect only the Canvas 2D raster path can render.

planEffects

function planEffects(effects: readonly Effect[] | undefined): EffectPlan

Converts 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): MeasureFn

Returns 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']): string

Returns 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
}): string

Builds 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,
): CaptionLayout

Wraps 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 = {},
): TextBlockLayout

Lays out a text element into lines, breaking only at newlines unless a text box width is provided.

On this page