# Graphics FilmCraft's graphics work like Premiere's Essential Graphics: a **graphic clip** on a video track holds **text** and **shape** layers. Text is set by the text engine in `crates/text` ([README](../crates/text/README.md)): bundled OFL fonts plus system fonts, OpenType shaping with kerning and ligatures, bidi, line breaking, all drawn as vectors in linear light. ## Model - A graphic clip's project item is an `ItemKind::Graphic { width, height, rate }`: a transparent canvas the size of the sequence frame, with unlimited duration. These items are internal and do not appear in the Project panel. - Its **layers** are effect instances `graphic_text` / `graphic_shape` in the clip's effect list, in paint order (first = back). They are hidden from the Effects panel. Because they are effect instances they save with the project, take keyframes on every animatable property (Source Text keyframes hold), appear in Effect Controls and copy with the clip. Standard effects on a graphic clip apply to the composed graphic; Motion and Opacity apply last. - `filmcraft_project::graphic::eval_layer` evaluates a layer at a time into a `LayerSpec`. - Projects with graphics need no schema migration: everything is additive (new item kind, new effect ids). Builds older than this one refuse such files with a parse error. ### Layer properties | Group | Properties (parameter ids) | |---|---| | Text | `text` (Source Text), `font`, `font_style`, `size` (px), `align` (Left/Center/Right/Justify), `tracking` (1/1000 em), `kerning`, `ligatures`, `leading` (px added to the natural line height), `baseline_shift`, `faux_bold`, `faux_italic`, `caps` (Normal/All Caps/Small Caps), `underline`, `box_width` (0 = point text, else area text wrapped at that width) | | Shape | `shape` (Rectangle/Ellipse/Polygon/Path), `size` [w, h], `sides`, `corner_radius`, `points` (path vertices relative to the layer origin) | | Appearance | `fill`, `fill_color`; `stroke`, `stroke_color`, `stroke_width`, `stroke_type` (Outer/Center/Inner) and the same for `stroke2`; `background`, `background_color`, `background_opacity`, `background_size` (padding), `background_radius`; `shadow`, `shadow_color`, `shadow_opacity`, `shadow_angle` (135° = down-right), `shadow_distance`, `shadow_size`, `shadow_blur` | | Transform | `position` (graphic canvas px), `anchor` (layer px), `scale`, `scale_width`, `uniform_scale`, `rotation`, `opacity` | Point text's origin is its alignment point on the first baseline (the click point of the Type tool); a shape's origin is its centre. Colours are `#rrggbb` (sRGB). ## Rendering `crates/render/src/graphic_clip.rs` rasterises each layer straight to the output transform (sequence Motion × layer transform), so text stays sharp at any scale or rotation. Fill coverage comes from the text engine; strokes come from a Euclidean distance transform of that coverage (outer strokes sit under the fill, centre and inner strokes over it); the shadow is the union of all parts, spread by Size, offset and blurred. Each layer's raster is cached while it stays the same. The GPU plan receives one tight image per graphic clip and places it with a translation. The Timecode and Clip Name effects also draw with the text engine. ## Commands | Command | Menu / shortcut | Params | |---|---|---| | `graphics.newText` | Graphics and Titles ▸ New Layer ▸ Text (⌘T) | `text`, `position`, `clip` (add to this graphic), `vertical`, `size`, `font`, `fontStyle`, `seconds` (5), `track`, `time` | | `graphics.newVerticalText` | New Layer ▸ Vertical Text | as `graphics.newText`; characters stack top to bottom, paragraphs are columns right to left | | `graphics.newRectangle`, `graphics.newEllipse`, `graphics.newPolygon` | New Layer ▸ Rectangle (⌥⌘R), Ellipse (⌥⌘E), Polygon | `position`, `size`, `clip`; polygon `sides` (6) | | `graphics.newFromFile` | New Layer ▸ From file… | `path` — imports the image or video and places it above the clips at the playhead (a separate clip; graphics have no media layers yet) | | `graphics.newShape` | (agents) | `shape` (rectangle/ellipse/polygon/path), `position`, `size`, `points`, `clip` | | `graphics.setText` | typing on the monitor | `clip`, `layer`, `text`, `merge` (coalesce one typing session into one undo step) | | `graphics.set` | Properties panel | `clip`, `layer`, `props` {parameter id or camelCase alias: value; choices by index or name}, `time` | | `graphics.selectLayer` | layer list / monitor click | `clip`, `layers` | | `graphics.deleteLayer`, `graphics.arrangeLayer` | layer list | `clip`, `layer`, `to` (front/back/forward/backward/index) | | `graphics.align` | Align and Transform | `align` (left/hcenter/right/top/vcenter/bottom), `to` (frame/group/selection), `layers` | | `graphics.alignFrame.` | Align to Video Frame ▸ Left / Center Horizontally / Right / Top / Center Vertically / Bottom | each selected layer to the frame | | `graphics.alignGroup.` | Align to Video Frame as Group ▸ … | the selected layers' union to the frame (2+ layers; relative positions kept) | | `graphics.alignSelection.` | Align to Selection ▸ … | each selected layer to the selection's union (2+ layers) | | `graphics.distribute` | Align and Transform | `axis` (horizontal/vertical), `space` (equal gaps), `layers` (3+) | | `graphics.distributeVertically`, `…SpaceVertically`, `…Horizontally`, `…SpaceHorizontally` | Distribute ▸ … | the selected layers (3+): equal centre spacing or equal gaps | | `graphics.bringToFront`, `graphics.bringForward`, `graphics.sendBackward`, `graphics.sendToBack` | Arrange ▸ … (⇧⌘], ⌘], ⌘[, ⇧⌘[) | `clip`, `layer` | | `graphics.selectNextGraphic`, `graphics.selectPreviousGraphic` | Select ▸ Select Next / Previous Graphic | selects the next / previous graphic clip in timeline order (moves the playhead onto it) | | `graphics.selectNextLayer`, `graphics.selectPreviousLayer` | Select ▸ Select Next / Previous Layer (⌥⌘], ⌥⌘[) | cycles the selected layer | | `graphics.resetAllParameters` | Reset All Parameters | `layers` (default: the selected layers, else all) — appearance, text formatting and transform back to defaults; text, shape and geometry kept | | `graphics.resetDuration` | Reset Duration | `seconds` (5), limited by the next clip on the track | | `graphics.list` | (query) | `clip` — layers with names, kinds, text, position and on-canvas quads | | `fonts.list` | (query) | `system` (scan system font folders, default true) — families and styles | Without `clip`, commands use the selected graphic clip, else the topmost graphic clip under the playhead. Without `layer`, they use the selected layer, else the front one. New graphic clips are placed at the playhead on the first video track above the clips there that is free for the duration (a track is added if needed). ## Program monitor | Tool | On the Program monitor | |---|---| | Type (T) | Click empty picture: new text layer with a caret. Click a text layer: caret there. Type; ←/→ (⌥ word, ⌘ line), ↑/↓, Home/End, Shift to select, ⌘A, ⌘C/⌘X/⌘V, Return = new line, Backspace/Delete, Esc = stop editing. Drag inside the edited text to select. | | Selection (V) | Click a layer to select it (box with handles and anchor point); drag to move; drag a corner handle to scale; double-click a text layer to edit it. | | Rectangle / Ellipse | Drag to draw a shape layer. | | Pen (P) | Click to place points; click the first point (or Return) to close the path; Esc cancels. | Moving a layer snaps its edges or centre to the frame edges, the frame centre and the guides (View ▸ Snap in Program Monitor, on by default; hold ⌘/Ctrl to move freely). See [monitors.md](monitors.md) for rulers and guides. Automation ids: `program.layer..`, `program.layer...handle.`, `program.textEdit` (while editing). ## Properties / Essential Graphics panels With a graphic clip selected, the Properties panel (and Essential Graphics) shows: **Layers** (front first; new text / rectangle / ellipse, bring forward / send backward, delete, visibility), **Align and Transform** (six align buttons, two distribute buttons, position, anchor, scale, rotation, opacity), **Text** (Source Text field, font family and style, size, paragraph alignment, tracking, leading, baseline shift, box width, faux bold / faux italic / all caps / small caps / underline, kerning and ligatures), **Shape**, and **Appearance** (fill, two strokes, background, shadow). Automation ids: `graphics.layers.`, `graphics.prop.`, `graphics.align.`, `graphics.distribute.`, `graphics.sourceText`, `graphics.NewTextLayer`, `graphics.NewRectangle`, `graphics.NewEllipse`, `graphics.deleteLayer`, `graphics.section.`. ## Per-character styles Select characters with the Type tool and change font, style, size, colour, faux bold / italic, underline, tracking, baseline shift or caps in the Properties panel: the change applies to the selection only (`graphics.setCharStyle`). Styles are stored on the layer as runs of characters (`EffectInstance::layer.runs`, character offsets, sorted and non-overlapping) and follow the text when it is edited (typed characters take the style of the character before them). The text engine lays the runs out together (`filmcraft_text::layout_rich`): a run can change the font and size, lines grow to fit the largest run, and each run can have its own fill colour. | Command | Params | |---|---| | `graphics.setCharStyle` | `clip`, `layer`, `start`, `end` (characters; default the whole text), `style` {`font`, `fontStyle`, `size`, `color`, `bold`, `italic`, `underline`, `tracking`, `baselineShift`, `caps`}, `clear` (remove styles from the range) | ## Responsive design **Position (pins).** A layer's edges can be pinned to another layer of the same graphic or to the video frame (`graphics.pin {layer, to: "frame" | layer index | name | "none", edges}`). Each pinned edge keeps its distance to the *same* edge of the target, measured when the pin is made: a box pinned on all four edges to a text layer grows with the text; a shape pinned on two opposite edges is resized, a text layer only moves. Pins resolve in dependency order (cycles are ignored). Moving a pinned layer keeps the pin at its new distance. Layers get a stable `uid` for this. **Time (intro / outro).** `graphics.setResponsiveTime {introFrames, outroFrames}` protects the first and last part of a graphic: when the clip is trimmed or extended, keyframes inside the intro keep their timing from the clip start, those inside the outro from the clip end, and the middle is stretched or squeezed (`graphic_design::remap_time`). Trimming the head keeps the intro playing from the new start. **Rolls and crawls.** `graphics.setRoll {mode: off | roll | crawlLeft | crawlRight, startOffScreen, endOffScreen, prerollFrames, easeInFrames, easeOutFrames, postrollFrames}` (the Roll options of Responsive Design – Time) moves the whole graphic: up for a roll, sideways for a crawl. *Start Off Screen* starts with the content just outside the frame (below it / right of it / left of it), otherwise where it was laid out; *End Off Screen* lets it leave completely, otherwise it stops when its last edge reaches the frame edge. The speed is constant between the preroll and the postroll, with linear acceleration over Ease In and deceleration over Ease Out (`graphic_design::roll_progress`). The offset is exact maths on the union of the layers' bounds (with background padding), at the clip-relative time. These settings are `TrackItem::graphic` (`GraphicMeta`, schema v12). ## Graphics templates (`.fcgt`) FilmCraft's equivalent of motion graphics templates is its **own** format: a `.fcgt` file is documented, versioned JSON (`filmcraft_project::gtemplate`): | Field | Meaning | |---|---| | `format`, `version` | `"filmcraft.graphicsTemplate"`, `1` (newer versions are refused) | | `id`, `name`, `category`, `description`, `author`, `license`, `tags` | library information | | `canvas`, `duration` | frame size it was designed for, default clip length (ticks) | | `layers` | the graphic's layers, exactly as in a project file, each with a `uid` | | `graphic` | roll / crawl and responsive time | | `controls` | editable properties: `{id, name, kind: text\|color\|slider\|checkbox\|font\|position, layer: uid, param, min, max}`; `param: "enabled"` is the layer's visibility | | `resources.fonts` | optional embedded font files (base64) with their licence; registered on install | Placing a template on a sequence of another size scales and centres its layers (pin distances scale too). **FilmCraft never reads or imports Adobe `.mogrt` files** (or any other application's template packages): Install refuses `.mogrt` paths without opening them, and the reader rejects ZIP archives. Built-in templates are original designs made for FilmCraft in code (no image, font or template files): lower thirds (Slab, Rule, Ticker crawl), titles (Centered, Boxed), end credits (Roll) and callouts (Pointer, Tag), with the bundled Inter font. Thumbnails are rendered by the engine at runtime. | Command | Menu | Params | |---|---|---| | `graphics.template.list` | (query) | `query`, `category`, `source` (builtin / user) | | `graphics.template.apply` | Browse tab: drag or double-click | `template` (id, name or `.fcgt` path), `values` {control: value}, `time`, `track` | | `graphics.template.export` | Graphics and Titles ▸ Export As Motion Graphics Template… | `clip`, `name`, `category`, `description`, `controls` [{`layer`, `param`, `name`, `kind`, `min`, `max`}], `path` (default: the user folder), `embedFonts` | | `file.exportGraphicsTemplate` | File ▸ Export ▸ Motion Graphics Template… | as above | | `graphics.template.install` | Graphics and Titles ▸ Install Motion Graphics Template… | `path` (a `.fcgt`) | | `graphics.template.remove` | Browse tab | `template` (user templates) | | `graphics.template.set` / `graphics.template.controls` | Essential Graphics ▸ Edit | `clip`, `control`, `value` / `values` | | `graphics.template.thumbnail` | (query) | `template`, `width`, `path` (PNG) | User templates live in `/Graphics Templates/`. **Essential Graphics panel.** *Browse* lists the built-in and user templates as cards with engine-rendered thumbnails, a search field and a category filter; click selects, double-click or Apply places the template at the playhead, dragging a card onto a video track places it there, Install… picks a `.fcgt`, Remove deletes a user template. *Edit* is the graphic editor; for a graphic made from a template it starts with **Template Properties** (one editor per control). With no layer selected it shows **Responsive Design – Time** (intro / outro, Roll with its options); with a layer selected, **Responsive Design – Position** (Pin To and the pinned edges). With characters selected by the Type tool, the Text section's controls style just those characters. Export As Motion Graphics Template… opens a dialog (name, category, description and a checklist of properties to expose); Replace Fonts in Projects… lists the fonts in use (missing ones flagged) and replaces one. Automation ids: `essentialGraphics.tab.browse|edit`, `gfxTemplates.search`, `gfxTemplates.category`, `gfxTemplates.item.`, `gfxTemplates.apply|install|remove`, `gfxTemplates.control.`, `graphics.roll.*`, `graphics.time.intro|outro`, `graphics.pin.to|left|top|right|bottom`, `exportTemplate.*`, `replaceFonts.*`; `ui.set {"gfxTemplates": {...}, "gfxEdit": {...}}` sets the panel state and the Type-tool selection. ## Captions, source graphics and fonts | Command | Menu | Params | |---|---|---| | `graphics.upgradeCaption` | Graphics and Titles ▸ Upgrade Caption to Graphic | `captions` (default: the selection, else the one under the playhead): each caption becomes a graphic clip on a video track with the track style (font, size, colour, background, outline, alignment, position); ``/``/`` become character styles; one undo step | | `graphics.upgradeToSourceGraphic` | Graphics and Titles ▸ Upgrade to Source Graphic | `clip`: the graphic gets its own project item (shown in the Project panel); every clip of that item shares its layers — editing one updates the others in the same undo step (`project.source_graphics`) | | `file.replaceFonts` | Graphics and Titles ▸ Replace Fonts in Projects… | `from` (family or {family, style}), `to`, `toStyle`: graphic layers, character styles, source graphics and caption tracks | | `graphics.fonts.used` | (query) | fonts in use with counts and whether they are missing | ## Not yet Mask-with-text, gradient fills, per-layer blend modes, Bézier curves in the pen tool (paths are polygons), media layers inside graphics, and template controls grouped into folders.