Free now2sdk Core is completely free — no license key required   See what's included →
SDK Modules Docs Release Notes Pricing GitHub
Get the SDK →

LCharacter Reference Manual & Broadcasting CG Overlay Guide

The LCharacter class is the high-performance vector graphics and Character Generator (CG) overlay engine of the now2sdk framework, derived from the base LObject class. It composites broadcasting overlays (lower-thirds, scoreboards, scrolling tickers, logos, lens flares, animated image sequences, live and transparent video elements) directly onto transparent RGBA frames using the Cairo 2D graphics API, with optional CPU or GPU rasterization.

It supports static and dynamic text wrapping, shape rendering with borders, gradients, and alpha transparency, horizontal/vertical scrolling crawls, vertical rolling tickers, custom images, image sequences, loopable transparent videos, dynamic live sources (CGo1), lens flares, declarative grouping, and complete XML template serialization for show playback.


1. Architectural Concept & Core Compositing Pipeline

graph TD
    A[Item Creation: addItem] --> B[Cairo ARGB32 Surface - 1920x1080]
    B --> C{Item Type Dispatch}
    
    C -->|LCGTextItem| D[Static / Wrapped Text + Outline + Shadow]
    C -->|LCGCrawlItem| E[Cached Text Surface + Scrolling Animation]
    C -->|LCGTickerItem| F[Vertical Roll with Smooth Transitions]
    C -->|LCGImageItem| G[PNG / JPG with Crop + Stretch Modes]
    C -->|LCGImageSeqItem| H[FPS-Locked Image Sequence Playback]
    C -->|LCGVideoItem| I[FFmpeg Decode + Loop - ProRes 4444 / WebM alpha]
    C -->|LCGLiveItem| J[Live Source Registry - LLive Sink]
    C -->|LCGRectItem| K[Vector Rectangle - Solid / Gradient]
    C -->|LCGCircleItem| L[Vector Circle / Ellipse Shape]
    C -->|LCGFlareItem| M[Animated Multi-Ray Lens Flare]
    
    D & E & F & G & H & I & J & K & L & M --> N[Dynamic Tag Processor]
    N -->|dateTime countdown countup| O[Refreshed Text]
    O --> P[Cairo Compositor]
    P --> Q[Group Transforms - x, y, show]
    Q --> R[Item Transform - rotation, anchorX, anchorY]
    R --> S[BGRA -> YUVA420P Conversion]
    S --> T[distributeVideoFrame -> LPreview / LMixer / LOutput / LRecorder]

Key Subsystems:


2. API Reference & Key Methods

📌 Ingestion & Item Addition

LCGTextItem* addItem(std::string id, int x, int y, int w, int h, LCGTextProps props);
LCGCrawlItem* addItem(std::string id, int x, int y, int w, int h, LCGCrawlProps props);
LCGTickerItem* addItem(std::string id, int x, int y, int w, int h, LCGTickerProps props);
LCGImageItem* addItem(std::string id, int x, int y, int w, int h, LCGImageProps props);
LCGImageSeqItem* addItem(std::string id, int x, int y, int w, int h, LCGImageSeqProps props);
LCGVideoItem* addItem(std::string id, int x, int y, int w, int h, LCGVideoProps props);
LCGRectItem* addItem(std::string id, int x, int y, int w, int h, LCGRectProps props);
LCGCircleItem* addItem(std::string id, int x, int y, int w, int h, LCGCircleProps props);
LCGFlareItem* addItem(std::string id, int x, int y, int w, int h, LCGFlareProps props);
LCGLiveItem* addItem(std::string id, int x, int y, int w, int h, LCGLiveProps props);

Adds an overlay element to the canvas. Each addItem is an overloaded method that returns a pointer to the specific item subclass (LCGTextItem, LCGCrawlItem, etc.). The pointer can then be modified directly to mutate x, y, w, h, alpha, show, or its props/*Props struct, without calling updateItem.

For LCGImageItem, if w or h is <= 0, the item adopts the source image's intrinsic dimensions. For LCGVideoItem, the same rule is applied using the first decoded video frame's dimensions. For all other items, w<=0 or h<=0 falls back to the canvas size (1920 × 1080).

For LCGVideoItem, the decoder opens synchronously inside addItem. If the file cannot be opened, has no video stream, or fails to decode the first frame, the function returns nullptr.

For LCGLiveItem, a hidden internal LCGLiveSink is created and stored under the item's id. The render thread attaches the sink to whatever LLive* is currently stored in LCGLiveProps::source — pass the LLive* directly when adding the item, no separate registration is required. The item will surface frames only when that source produces them.


📌 Modification, Transform & Lifecycle

void updateItem(std::string id, int x, int y, int w, int h, float alpha, bool show, LCGTextProps props);
void updateItem(std::string id, int x, int y, int w, int h, float alpha, bool show, LCGCrawlProps props);
void updateItem(std::string id, int x, int y, int w, int h, float alpha, bool show, LCGTickerProps props);
void updateItem(std::string id, int x, int y, int w, int h, float alpha, bool show, LCGImageProps props);
void updateItem(std::string id, int x, int y, int w, int h, float alpha, bool show, LCGImageSeqProps props);
void updateItem(std::string id, int x, int y, int w, int h, float alpha, bool show, LCGVideoProps props);
void updateItem(std::string id, int x, int y, int w, int h, float alpha, bool show, LCGRectProps props);
void updateItem(std::string id, int x, int y, int w, int h, float alpha, bool show, LCGCircleProps props);
void updateItem(std::string id, int x, int y, int w, int h, float alpha, bool show, LCGFlareProps props);
void updateItem(std::string id, int x, int y, int w, int h, float alpha, bool show, LCGLiveProps props);

Updates an existing item's coordinates, bounding box, alpha opacity (0.0f to 1.0f), visibility, and style props in a single call. Type-specific overloads exist for all 10 element types.

void setItemTransform(std::string id, float rotation, float anchorX, float anchorY);

Sets per-item rotation in degrees (clockwise) and an anchor point (anchorX, anchorY from 0.0f to 1.0f, relative to the item's w × h box) around which the rotation is applied.

LCGItem* getItem(std::string id);
int getItemCount() const;
LCGItem* getItemByIndex(int index);

Resolves items: getItem(id) retrieves by ID (must cast to the proper subclass to access props); getItemCount() returns total items in the canvas; getItemByIndex(i) returns the i-th item by z-order.

void showItem(std::string id_or_group, bool show);
void remove(const std::string& id);
void clear();
void forceUpdate();
void setFPS(double fps);

📌 Video Format Configuration

void setVideoFormat(const videoFormatProps& props);
void getVideoFormat(videoFormatProps& props) const;

Mirrors LMixer::setVideoFormat. Sets or queries the canvas resolution, frame rate, and pixel format used for compositing. Call this before adding items if you need a non-default canvas size — the first addItem call lazily allocates the Cairo surface to match.


📌 Frame-Based Playback Timing & Markers

void setTotalFrames(int n);
int  getTotalFrames() const;
double getDurationSeconds() const;

void setInStopFrame(int f);
int  getInStopFrame() const;
void setOutStartFrame(int f);
int  getOutStartFrame() const;
void setLoopFrame(int f);
int  getLoopFrame() const;

void forceUpdate();

📌 Keyframe Animation & Pre-Baked Frames

void setItemBakedFrames(std::string id,
                       const std::vector<LCGFrameState>& states,
                       const std::vector<LCGTextProps>& props);
void setItemBakedFrames(std::string id,
                       const std::vector<LCGFrameState>& states,
                       const std::vector<LCGCrawlProps>& props);
void setItemBakedFrames(std::string id,
                       const std::vector<LCGFrameState>& states,
                       const std::vector<LCGTickerProps>& props);
void setItemBakedFrames(std::string id,
                       const std::vector<LCGFrameState>& states,
                       const std::vector<LCGImageProps>& props);
void setItemBakedFrames(std::string id,
                       const std::vector<LCGFrameState>& states,
                       const std::vector<LCGImageSeqProps>& props);
void setItemBakedFrames(std::string id,
                       const std::vector<LCGFrameState>& states,
                       const std::vector<LCGVideoProps>& props);
void setItemBakedFrames(std::string id,
                       const std::vector<LCGFrameState>& states,
                       const std::vector<LCGRectProps>& props);
void setItemBakedFrames(std::string id,
                       const std::vector<LCGFrameState>& states,
                       const std::vector<LCGCircleProps>& props);
void setItemBakedFrames(std::string id,
                       const std::vector<LCGFrameState>& states,
                       const std::vector<LCGFlareProps>& props);
void setItemBakedFrames(std::string id,
                       const std::vector<LCGFrameState>& states,
                       const std::vector<LCGLiveProps>& props);

Loads a fully pre-baked animation timeline into the item. Each entry in states describes the transform (x, y, w, h, alpha, show, rotation, anchorX, anchorY) at a specific frame index; the matching props entry carries the type-specific style for that frame. The playback thread samples these arrays directly without recomputing keyframe easing.

The companion types are:

struct LCGFrameState {
    int x = 0, y = 0, w = 0, h = 0;
    float alpha = 1.0f;
    bool show = true;
    float rotation = 0.0f;
    float anchorX = 0.5f;
    float anchorY = 0.5f;
};

enum class LCGAnimProp { PosX, PosY, Width, Height, Alpha, Rotation, AnchorX, AnchorY };
enum class LCGEasing  { Linear, EaseIn, EaseOut, EaseInOut };
using LCGValue = std::variant<float, int, std::string, LCGColor>;

struct LCGKeyframe {
    int frame = 0;
    LCGAnimProp prop = LCGAnimProp::PosX;
    LCGValue value{};
    LCGEasing easing = LCGEasing::Linear;
    float cp1x = -1.f, cp1y = -1.f, cp2x = -1.f, cp2y = -1.f;
};

Per-item keyframe API on every LCGItem subclass:

void addKeyframe(int frame, LCGAnimProp prop, LCGValue value,
                 LCGEasing easing = LCGEasing::Linear);
void updateKeyframe(int frame, LCGAnimProp prop, LCGValue newValue);
void removeKeyframe(int frame, LCGAnimProp prop);
void removeKeyframesForProp(LCGAnimProp prop);
void clearKeyframes();
const std::vector<LCGKeyframe>& getKeyframes() const;

Initializer-list setter proxies for compact timeline authoring:

item->addkeyframes    = { kf(0,   PosX, 50),
                          kf(50,  PosX, 1920, EaseInOut) };
item->updatekeyframes = { kf(50,  PosX, 1980) };
item->removekeyframes = { kf(50,  PosX) };

📌 Playback Sequence Control

void setPlaybackSequence(int totalFrames, int inStopFrame,
                         int outStartFrame, int loopFrame,
                         bool hasLoop);
void startPlayback();
void stopPlayback();
void shutdownPlayback();   // hard stop: joins the playback thread
void seekPlayback(int frame);
int  getPlaybackFrame() const;
bool isPlaying() const;
bool isPlaybackFinished() const;

📌 XML Configuration Save & Load

void saveToXMLFile(const std::string& filePath);
void loadFromXMLFile(const std::string& filePath);
void insertFromXMLFile(const std::string& filePath);

XML persistence makes it trivial to author complex on-air branding templates in a desktop tool and reload them at runtime during a show playback.


📌 Grouping Operations

std::string group(const std::vector<std::string>& itemIDs);
void unGroup(const std::string& groupID);
void deleteGroup(const std::string& groupID);
int groupItemCount(const std::string& groupID);
LCGItem* getGroupItemByIndex(const std::string& groupID, int index);
std::string getGroup(const std::string& itemID);
LCGGroup* getGroupObject(const std::string& groupID);
int getGroupCount() const;
LCGGroup* getGroupByIndex(int index);

3. Base Element Properties: LCGItem & Group Class: LCGGroup

LCGItem Base Class

All items added to LCharacter (LCGTextItem, LCGCrawlItem, etc.) inherit the base properties of LCGItem. These parameters can be read or modified directly on the item pointer:

Field Type Default Value Description
id std::string "" Unique identifier for the element.
x float 0.0f Current horizontal coordinate in pixels.
y float 0.0f Current vertical coordinate in pixels.
startX float 0.0f Default starting horizontal coordinate (reference / reset position).
startY float 0.0f Default starting vertical coordinate (reference / reset position).
w int 0 Width of the bounding box in pixels.
h int 0 Height of the bounding box in pixels.
show bool true Visibility switch (true to render, false to hide).
alpha float 1.0f Opacity scale factor (0.0f to 1.0f).
pGroup LCGGroup* nullptr Pointer to the parent group object (or nullptr if independent).
move std::string "None" Motion style key: "None", "Horizontal", "Vertical".
speed float 0.0f Animation/motion velocity factor (pixels per frame).
loop bool true If true, items entering the off-canvas area wrap around (used for crawls/loops).
rotation float 0.0f Item rotation in degrees, applied via setItemTransform.
anchorX float 0.5f Horizontal rotation pivot (normalized 0.0f to 1.0f).
anchorY float 0.5f Vertical rotation pivot (normalized 0.0f to 1.0f).
inTrack int -1 Input track index that routes this item's video into the global mixer bus. -1 = unassigned.
outTrack int -1 Output track index that consumes this item's video from the bus. -1 = unassigned.
bakedStates std::vector<LCGFrameState> {} Pre-baked per-frame transforms loaded by setItemBakedFrames.
keyframes std::vector<LCGKeyframe> {} Live keyframe timeline sampled every render tick. Mutate via the keyframe methods below.

LCGGroup Class

Defines a collection of elements that can be moved or toggled as a single unit:

Field Type Default Value Description
id std::string "" Unique group identifier.
show bool true Group visibility switch. When false, all member items are skipped during rendering.
x float 0.0f Group horizontal offset in pixels (added on top of each item's local x).
y float 0.0f Group vertical offset in pixels (added on top of each item's local y).
w float 0.0f Group bounding box width in pixels (informational; not enforced during rendering).
h float 0.0f Group bounding box height in pixels (informational; not enforced during rendering).
items std::vector<LCGItem*> {} Vector of items inside the group.

4. Subclass Items & Property Structs

To mutate a specific item, modify its props / *Props struct on the pointer returned by addItem or getItem (cast the base LCGItem* to the appropriate subclass first):

📌 LCGTextItem (Static / Dynamic Text)

Defines a text block layout:

class NOW2SDK_EXPORT LCGTextItem : public LCGItem {
public:
    LCGTextProps props; // Text properties struct
};

LCGTextProps Struct

Field Type Default Value Description
text std::string "" Text contents. Supports dynamic tags: %dateTime::...%, %countdown::SEC::FORMAT%, %countup::SEC::FORMAT%.
font std::string "Arial" Font family name (must be available to the system font loader).
fontSize int 24 Font size in pixels.
color LCGColor "#FFFFFF" Font hex color.
textAlpha int 255 Font opacity (0 to 255).
letterSpacing float 0.0f Spacing between characters (in pixels).
autoShrink bool false Shrinks font size automatically if the text exceeds bounding width.
padding int 0 Inner padding in pixels.
outlineSize int 0 Text outline stroke size in pixels (0 disables).
outlineColor LCGColor "#000000" Text outline stroke color.
hAlign std::string "Left" Horizontal alignment: "Left", "Center", "Right".
vAlign std::string "Center" Vertical alignment: "Top", "Center", "Bottom".
bgType std::string "None" Background type: "None", "Color", "Gradient".
bgColor LCGColor "#000000" Solid background fill color.
bgGradientStartColor LCGColor "#FF0000" Gradient starting color (top).
bgGradientEndColor LCGColor "#000000" Gradient ending color (bottom).
bgAlpha int 255 Background block opacity (0 to 255).
bgRadius int 0 Corner rounding radius for the background box in pixels (0 = square).
bgShadowOffsetX float 0.0f Background drop shadow horizontal offset.
bgShadowOffsetY float 0.0f Background drop shadow vertical offset.
bgShadowAlpha int 0 Background drop shadow opacity (0 to 255).
bgShadowColor LCGColor "#000000" Background drop shadow color.
borderColor LCGColor "#FFFFFF" Background box border outline color.
borderSize int 0 Background box border outline thickness in pixels (0 disables).
borderAlpha int 255 Background box border outline opacity.
textShadowOffsetX float 0.0f Text character drop shadow horizontal offset.
textShadowOffsetY float 0.0f Text character drop shadow vertical offset.
textShadowAlpha int 0 Text character shadow opacity (0 to 255).
textShadowColor LCGColor "#000000" Text character shadow color.

📌 LCGCrawlItem (Scrolling News Tickers)

Defines a continuous text crawl marquee. The text content, fonts, colors, alignment, outline, shadow, padding, background and panel border are all carried by LCGCrawlProps, which inherits directly from LCGTextProps. Crawl-only motion and separator fields are appended at the bottom of the same struct.

class NOW2SDK_EXPORT LCGCrawlItem : public LCGItem { 
public:
    LCGCrawlProps crawlProps; // Crawl ticker properties
};

struct NOW2SDK_EXPORT LCGCrawlProps : public LCGTextProps {
    // crawl-specific fields appended below — see table
};

Text Source — crawlProps.text

LCGCrawlProps — Complete Property Reference

The table below lists every field you can set on a crawl. Inherited fields from LCGTextProps are marked [Inherited], crawl-only fields are marked [Crawl-only]:

Field Type Default Group Description
text std::string "" [Inherited] Crawler text. Multi-line content via \n or |. Supports %dateTime::%, %countdown::%, %countup::% tags.
font std::string "Arial" [Inherited] Font family name (must be available to the system font loader).
fontSize int 24 [Inherited] Font size in pixels.
color LCGColor "#FFFFFF" [Inherited] Font hex color.
textAlpha int 255 [Inherited] Font opacity (0 to 255).
letterSpacing float 0.0f [Inherited] Spacing between characters (in pixels).
autoShrink bool false [Inherited] Shrinks font size automatically if the text exceeds bounding width.
padding int 0 [Inherited] Inner padding (in pixels) applied to the bounding box.
outlineSize int 0 [Inherited] Text outline stroke size in pixels (0 disables).
outlineColor LCGColor "#000000" [Inherited] Text outline stroke color.
hAlign std::string "Left" [Inherited] Horizontal alignment of the text segment inside the bounding box: "Left", "Center", "Right".
vAlign std::string "Center" [Inherited] Vertical alignment inside the bounding box: "Top", "Center", "Bottom".
bgType std::string "None" [Inherited] Background type behind the crawl: "None", "Color", "Gradient".
bgColor LCGColor "#000000" [Inherited] Solid background fill color.
bgGradientStartColor LCGColor "#FF0000" [Inherited] Gradient starting color (top edge).
bgGradientEndColor LCGColor "#000000" [Inherited] Gradient ending color (bottom edge).
bgAlpha int 255 [Inherited] Background block opacity (0 to 255).
bgRadius int 0 [Inherited] Corner rounding radius for the background box (0 = square).
bgShadowOffsetX float 0.0f [Inherited] Background drop shadow horizontal offset.
bgShadowOffsetY float 0.0f [Inherited] Background drop shadow vertical offset.
bgShadowAlpha int 0 [Inherited] Background drop shadow opacity (0 to 255).
bgShadowColor LCGColor "#000000" [Inherited] Background drop shadow color.
borderColor LCGColor "#FFFFFF" [Inherited] Background box border outline color.
borderSize int 0 [Inherited] Background box border outline thickness in pixels (0 disables).
borderAlpha int 255 [Inherited] Background box border outline opacity.
textShadowOffsetX float 0.0f [Inherited] Text character drop shadow horizontal offset.
textShadowOffsetY float 0.0f [Inherited] Text character drop shadow vertical offset.
textShadowAlpha int 0 [Inherited] Text character shadow opacity (0 to 255).
textShadowColor LCGColor "#000000" [Inherited] Text character shadow color.
textMove std::string "Horizontal" [Crawl-only] Scroll direction: "Horizontal" or "Vertical".
textSpeed float 50.0f [Crawl-only] Pixels-per-frame offset. Positive scrolls right / down, negative scrolls left / up.
separatorImage std::string "" [Crawl-only] Path to image file used as separator between text repetitions.
textSeparator std::string " *** " [Crawl-only] Text used as separator if separatorImage is empty.
separatorPadding int 10 [Crawl-only] Padding space (in pixels) added around the separator image or text.

loop (inherited from LCGItem, bool, default true): when true the text chain is appended with the separator indefinitely so the crawl has no visible end.

Format Example

LCGCrawlProps crawl;
crawl.text        = "::: NOW2SDK CAIRO GRAPHICS ENGINE ::: %dateTime::HH:mm:ss% ::: "
                     "BREAKING %countdown::120::mm:ss% :::";  // multi-segment + dynamic tags
crawl.textMove    = "Horizontal";
crawl.textSpeed   = -4.0f;
crawl.textSeparator = "  ★  ";
crawl.separatorPadding = 12;
crawl.font        = "Arial";
crawl.fontSize    = 30;
crawl.color       = "#FFFF00";
crawl.bgType      = "Solid";
crawl.bgColor     = "#111111";
crawl.bgAlpha     = 220;

The crawl pre-renders the full text + separator chain into an internal Cairo surface on the first frame, then blits-and-translates this cached surface every frame for high-performance rendering.


📌 LCGTickerItem (Vertical Rolling Page Tickers)

Defines a multi-line vertical slide-roll ticker. Every text appearance field lives in LCGTickerProps, which inherits directly from LCGTextProps. Ticker-only motion/transition fields are appended at the bottom of the same struct.

class NOW2SDK_EXPORT LCGTickerItem : public LCGItem { 
public:
    LCGTickerProps tickerProps; // Rolling ticker properties
};

struct NOW2SDK_EXPORT LCGTickerProps : public LCGTextProps {
    // ticker-specific fields appended below — see table
};

Text Source — tickerProps.text

LCGTickerProps — Complete Property Reference

The table below lists every field you can set on a ticker. Inherited fields from LCGTextProps are marked [Inherited], ticker-only fields are marked [Ticker-only]:

Field Type Default Group Description
text std::string "" [Inherited] Pages to display, separated by \n. Supports %dateTime::%, %countdown::%, %countup::% tags.
font std::string "Arial" [Inherited] Font family name (must be available to the system font loader).
fontSize int 24 [Inherited] Font size in pixels.
color LCGColor "#FFFFFF" [Inherited] Font hex color.
textAlpha int 255 [Inherited] Font opacity (0 to 255).
letterSpacing float 0.0f [Inherited] Spacing between characters (in pixels).
autoShrink bool false [Inherited] Shrinks font size automatically if the text exceeds bounding width.
padding int 0 [Inherited] Inner padding (in pixels) applied to the bounding box.
outlineSize int 0 [Inherited] Text outline stroke size in pixels (0 disables).
outlineColor LCGColor "#000000" [Inherited] Text outline stroke color.
hAlign std::string "Left" [Inherited] Horizontal alignment of the page text inside the bounding box: "Left", "Center", "Right".
vAlign std::string "Center" [Inherited] Vertical alignment inside the bounding box: "Top", "Center", "Bottom".
bgType std::string "None" [Inherited] Background type behind the ticker: "None", "Color", "Gradient".
bgColor LCGColor "#000000" [Inherited] Solid background fill color.
bgGradientStartColor LCGColor "#FF0000" [Inherited] Gradient starting color (top edge).
bgGradientEndColor LCGColor "#000000" [Inherited] Gradient ending color (bottom edge).
bgAlpha int 255 [Inherited] Background block opacity (0 to 255).
bgRadius int 0 [Inherited] Corner rounding radius for the background box (0 = square).
bgShadowOffsetX float 0.0f [Inherited] Background drop shadow horizontal offset.
bgShadowOffsetY float 0.0f [Inherited] Background drop shadow vertical offset.
bgShadowAlpha int 0 [Inherited] Background drop shadow opacity (0 to 255).
bgShadowColor LCGColor "#000000" [Inherited] Background drop shadow color.
borderColor LCGColor "#FFFFFF" [Inherited] Background box border outline color.
borderSize int 0 [Inherited] Background box border outline thickness in pixels (0 disables).
borderAlpha int 255 [Inherited] Background box border outline opacity.
textShadowOffsetX float 0.0f [Inherited] Text character drop shadow horizontal offset.
textShadowOffsetY float 0.0f [Inherited] Text character drop shadow vertical offset.
textShadowAlpha int 0 [Inherited] Text character shadow opacity (0 to 255).
textShadowColor LCGColor "#000000" [Inherited] Text character shadow color.
textMove std::string "Up" [Ticker-only] Rolling direction between pages: "Up" (default) or "Down".
textMoveDuration float 5.0f [Ticker-only] Dwell time, in seconds, each line stays visible before triggering the transition. The transition itself is a fixed 0.5-second smooth slide.

Format Example

LCGTickerProps ticker;
ticker.text = "Headline One\n"
              "Headline Two\n"
              "BREAKING  %dateTime::HH:mm:ss%\n"
              "Round %countup::600::mm:ss% elapsed";        // multi-page with dynamic tags
ticker.font = "Arial";
ticker.fontSize = 32;
ticker.color = "#FFFFFF";
ticker.bgType = "Color";
ticker.bgColor = "#001133";
ticker.bgAlpha = 230;
ticker.textMove = "Up";
ticker.textMoveDuration = 4.0f;     // each page visible 4 s

Empty lines are skipped during rendering; if all lines evaluate to empty, a single empty placeholder line is drawn so the ticker box is never fully blank.


📌 LCGImageItem (Static Raster Image Overlay)

Defines a static raster image overlay with cropping, aspect-lock, and stretch support:

class NOW2SDK_EXPORT LCGImageItem : public LCGItem {
public:
    LCGImageProps imageProps; // Image path and fit settings
};

LCGImageProps Struct

Field Type Default Value Description
path std::string "" Path to local image file (.png, .jpg, .bmp, .webp, .tga) that will be loaded and scaled into the item bounds. PNGs are decoded natively via Cairo; other formats use FFmpeg.
cropLeft int 0 Pixels cropped from the left edge of the source image before scaling.
cropTop int 0 Pixels cropped from the top edge of the source image.
cropRight int 0 Pixels cropped from the right edge of the source image.
cropBottom int 0 Pixels cropped from the bottom edge of the source image.
stretchMode int 0 Layout mode: 0 Stretch, 1 Fit (letter-box), 2 Fill (crop-to-fit), 3 Original (no scale, draw 1:1).
aspectLock bool true If true, the source aspect ratio is preserved when scaling into the item box.
bgType std::string "None" Background panel behind the image: "None", "Color", "Gradient".
bgColor LCGColor "#FFFFFF" Solid panel fill color.
bgGradientStartColor LCGColor "#FFFFFF" Gradient starting color (top edge).
bgGradientEndColor LCGColor "#000000" Gradient ending color (bottom edge).
bgAlpha int 255 Background panel opacity (0 to 255).
bgRadius int 0 Panel corner rounding radius (and image clip radius) in pixels.
borderColor LCGColor "#FFFFFF" Border outline color.
borderSize int 0 Border outline thickness in pixels (0 disables).
borderAlpha int 255 Border outline opacity.
bgShadowOffsetX float 0.0f Background drop shadow horizontal offset.
bgShadowOffsetY float 0.0f Background drop shadow vertical offset.
bgShadowAlpha int 0 Background drop shadow opacity (0 to 255).
bgShadowColor LCGColor "#000000" Background drop shadow color.

📌 LCGImageSeqItem (Image Sequence / Stop-Motion Animation)

Loads all image files from a directory (*.png, *.jpg, *.jpeg, *.bmp, *.webp) and plays them back as a frame-locked sequence:

class NOW2SDK_EXPORT LCGImageSeqItem : public LCGItem {
public:
    LCGImageSeqProps seqProps;
};

LCGImageSeqProps Struct

Field Type Default Value Description
folder std::string "" Directory containing the sorted image sequence frames. Files are sorted alphabetically before playback. If the folder changes between updateItem calls, the frame list is rebuilt from scratch.
fps double 25.0 Playback frame rate of the sequence. Independent of the canvas m_fps.
loop bool true If true, playback wraps back to the first frame after the last. If false, the last frame is held when the sequence ends.
cropLeft int 0 Pixels cropped from the left edge of each frame before scaling.
cropTop int 0 Pixels cropped from the top edge of each frame.
cropRight int 0 Pixels cropped from the right edge of each frame.
cropBottom int 0 Pixels cropped from the bottom edge of each frame.
stretchMode int 0 Layout mode: 0 Stretch, 1 Fit (letter-box), 2 Fill (crop-to-fit), 3 Original.
aspectLock bool true If true, source aspect ratio is preserved when scaling into the item box.
bgType std::string "None" Background panel behind the sequence: "None", "Color", "Gradient".
bgColor LCGColor "#FFFFFF" Solid panel fill color.
bgGradientStartColor LCGColor "#FFFFFF" Gradient starting color (top).
bgGradientEndColor LCGColor "#000000" Gradient ending color (bottom).
bgAlpha int 255 Background panel opacity (0 to 255).
bgRadius int 0 Panel corner rounding radius (and image clip radius) in pixels.
borderColor LCGColor "#FFFFFF" Border outline color.
borderSize int 0 Border outline thickness in pixels (0 disables).
borderAlpha int 255 Border outline opacity.
bgShadowOffsetX float 0.0f Background drop shadow horizontal offset.
bgShadowOffsetY float 0.0f Background drop shadow vertical offset.
bgShadowAlpha int 0 Background drop shadow opacity (0 to 255).
bgShadowColor LCGColor "#000000" Background drop shadow color.

📌 LCGVideoItem (Loopable Transparent Video)

Defines a loopable transparent video overlay:

class NOW2SDK_EXPORT LCGVideoItem : public LCGItem {
public:
    LCGVideoProps videoProps;
};

LCGVideoProps Struct

Field Type Default Value Description
path std::string "" Path to local transparent video file (.mov with alpha, .webm with alpha, ProRes 4444, etc.) that loops inside the item bounds. The decoder is opened synchronously inside addItem and returns nullptr if the file or video stream cannot be opened.
cropLeft int 0 Pixels cropped from the left edge of each decoded frame before scaling.
cropTop int 0 Pixels cropped from the top edge of each decoded frame.
cropRight int 0 Pixels cropped from the right edge of each decoded frame.
cropBottom int 0 Pixels cropped from the bottom edge of each decoded frame.
stretchMode int 0 Layout mode: 0 Stretch, 1 Fit (letter-box), 2 Fill (crop-to-fit), 3 Original.
aspectLock bool true If true, decoded frame aspect ratio is preserved when scaling into the item box.
bgType std::string "None" Background panel behind the video: "None", "Color", "Gradient".
bgColor LCGColor "#FFFFFF" Solid panel fill color.
bgGradientStartColor LCGColor "#FFFFFF" Gradient starting color (top).
bgGradientEndColor LCGColor "#000000" Gradient ending color (bottom).
bgAlpha int 255 Background panel opacity (0 to 255).
bgRadius int 0 Panel corner rounding radius in pixels.
borderColor LCGColor "#FFFFFF" Border outline color.
borderSize int 0 Border outline thickness in pixels (0 disables).
borderAlpha int 255 Border outline opacity.
bgShadowOffsetX float 0.0f Background drop shadow horizontal offset.
bgShadowOffsetY float 0.0f Background drop shadow vertical offset.
bgShadowAlpha int 0 Background drop shadow opacity (0 to 255).
bgShadowColor LCGColor "#000000" Background drop shadow color.

The decoder reads packets in the playback loop using the active m_fps value, scales each frame into BGRA, premultiplies alpha, and blits into the Cairo surface.


📌 LCGLiveItem (Live Source Overlay)

Binds to a registered LLive* source by name and displays its most recently decoded frame:

class NOW2SDK_EXPORT LCGLiveItem : public LCGItem {
public:
    LCGLiveProps liveProps;
};

LCGLiveProps Struct

Field Type Default Value Description
source LLive* nullptr Direct pointer to the LLive source this overlay should display. The render thread attaches the item's internal sink to source automatically on the next frame. If nullptr, the item renders empty. Pass the LLive* directly (object-based binding) — no separate registry call is needed.
cropLeft int 0 Pixels cropped from the left edge of each cached live frame before scaling.
cropTop int 0 Pixels cropped from the top edge of each cached live frame.
cropRight int 0 Pixels cropped from the right edge of each cached live frame.
cropBottom int 0 Pixels cropped from the bottom edge of each cached live frame.
stretchMode int 0 Layout mode: 0 Stretch, 1 Fit (letter-box), 2 Fill (crop-to-fit), 3 Original.
aspectLock bool true If true, source aspect ratio is preserved when scaling into the item box.
bgType std::string "None" Background panel behind the live frame: "None", "Color", "Gradient".
bgColor LCGColor "#FFFFFF" Solid panel fill color.
bgGradientStartColor LCGColor "#FFFFFF" Gradient starting color (top).
bgGradientEndColor LCGColor "#000000" Gradient ending color (bottom).
bgAlpha int 255 Background panel opacity (0 to 255).
bgRadius int 0 Panel corner rounding radius in pixels.
borderColor LCGColor "#FFFFFF" Border outline color.
borderSize int 0 Border outline thickness in pixels (0 disables).
borderAlpha int 255 Border outline opacity.
bgShadowOffsetX float 0.0f Background drop shadow horizontal offset.
bgShadowOffsetY float 0.0f Background drop shadow vertical offset.
bgShadowAlpha int 0 Background drop shadow opacity (0 to 255).
bgShadowColor LCGColor "#000000" Background drop shadow color.

XML persistence note: saveToXMLFile writes the bound LLive* address as a hex <sourcePtr> tag for in-process round-trip reference. After loadFromXMLFile, every LCGLiveItem's source field is reset to nullptr because runtime pointers cannot be reconstructed from disk. Re-bind by calling addItem or updateItem with the new LLive* after the load.

Each LCGLiveItem internally allocates an LCGLiveSink (LSink derivative) which is attached to the named LLive. The sink caches the latest decoded frame in a thread-safe manner; LCGLiveItem blits the cached frame each render tick.


📌 LCGRectItem (Vector Rectangle Shape)

Defines a vector rectangle block:

class NOW2SDK_EXPORT LCGRectItem : public LCGItem {
public:
    LCGRectProps rectProps; // Rectangle styling properties
};

LCGRectProps Struct

Field Type Default Value Description
bgType std::string "Solid" Fill type: "Solid" or "Gradient".
bgColor LCGColor "#FFFFFF" Solid color fill.
bgGradientStartColor LCGColor "#FFFFFF" Gradient starting color.
bgGradientEndColor LCGColor "#000000" Gradient ending color.
bgAlpha int 255 Opacity (0 to 255).
bgRadius int 0 Corner rounding radius in pixels.
borderColor LCGColor "#FFFFFF" Border outline color.
borderSize int 0 Border outline thickness in pixels.
borderAlpha int 255 Border outline opacity.
bgShadowOffsetX float 0.0f Background drop shadow horizontal offset.
bgShadowOffsetY float 0.0f Background drop shadow vertical offset.
bgShadowAlpha int 0 Background drop shadow opacity.
bgShadowColor LCGColor "#000000" Background drop shadow color.

📌 LCGCircleItem (Vector Circle / Ellipse Shape)

Defines a vector circle or ellipse. Same property surface as LCGRectItem with bgRadius reinterpreted as a percentage (0100) of the shortest side for elliptical curvature:

class NOW2SDK_EXPORT LCGCircleItem : public LCGItem {
public:
    LCGCircleProps circleProps;
};

LCGCircleProps Struct

Field Type Default Value Description
bgType std::string "Solid" Fill type: "Solid" or "Gradient".
bgColor LCGColor "#FFFFFF" Solid color fill.
bgGradientStartColor LCGColor "#FFFFFF" Gradient starting color.
bgGradientEndColor LCGColor "#000000" Gradient ending color.
bgAlpha int 255 Opacity (0 to 255).
borderColor LCGColor "#FFFFFF" Border outline color.
borderSize int 0 Border outline thickness in pixels.
borderAlpha int 255 Border outline opacity.
bgShadowOffsetX float 0.0f Background drop shadow horizontal offset.
bgShadowOffsetY float 0.0f Background drop shadow vertical offset.
bgShadowAlpha int 0 Background drop shadow opacity.
bgShadowColor LCGColor "#000000" Background drop shadow color.

📌 LCGFlareItem (Animated Multi-Ray Lens Flare)

Generates an animated light-flare effect with a configurable number of rays, hotspot, fade, and sweep direction:

class NOW2SDK_EXPORT LCGFlareItem : public LCGItem {
public:
    LCGFlareProps flareProps;
};

LCGFlareProps Struct

Field Type Default Value Description
rayLength float 200.0f Half-width of the flare's bounding box along the ray axis (pixels).
wingStrength float 20.0f Half-height of the rays perpendicular to the ray axis (pixels).
centerHotspot float 0.35f Center hotspot size as a fraction of rayLength (0.0 to 1.0).
centerStretch float 1.0f Horizontal stretch factor of the hotspot (0.1 to 10.0; 1.0 = perfect circle).
cropLeft int 0 Pixels hidden on the left edge of the rendered flare.
cropTop int 0 Pixels hidden on the top edge of the rendered flare.
cropRight int 0 Pixels hidden on the right edge of the rendered flare.
cropBottom int 0 Pixels hidden on the bottom edge of the rendered flare.
brightness int 220 Peak brightness (0 to 255).
rotationSpeed float 30.0f Rotation speed in degrees per second.
angle int 1 Number of duplicated rays spaced equally around 360° (1 to 12).
horizontalFade float 0.2f Per-edge horizontal fade (0.0 to 0.5).
verticalFade float 0.1f Per-edge vertical fade (0.0 to 0.5).
color LCGColor "#FFEE88" Flare tint color.
flowDirection int 0 Sweep direction: 0 L→R, 1 R→L, 2 T→B, 3 B→T. Active when directionEnable = true.
directionSpeed int 2000 Time to traverse from one end to the other (milliseconds).
directionLoopDelay float 1.0f Wait time before the next sweep, in seconds.
directionEnable bool false Toggles the flow animation around the canvas.

5. Dynamic Tag Engine (Live Timers, Clocks & Counters)

Any text field on LCGTextItem, LCGCrawlItem, or LCGTickerItem (props.text / crawlProps.text / tickerProps.text) supports runtime interpolation through three tag families. The engine's processDynamicTags(itemId, rawText) is invoked once per render frame on every text-bearing item — there is no need to call updateItem to refresh the displayed value.

Reset rule: changing any of (itemId, type, targetSeconds, formatString) for a given tag restarts the timer from zero.

📌 dateTime:: — Live Clock & Calendar

Insert a literal dateTime::FORMAT token (surrounded by % on both sides) anywhere in the text field. The engine replaces it on every frame.

LCGTextProps lowerThirdLine;
lowerThirdLine.text = std::string("ON AIR  ") + "%dateTime::HH:mm:ss%";   // live broadcast clock
lowerThirdLine.text = std::string("Recorded: ") + "%dateTime::yyyy:MM:dd HH:mm:ss%";  // full stamp
LCGTextItem* liveClock = m_lCG->addItem("live_clock", 1500, 50, 380, 50, lowerThirdLine);

📌 countdown:: — Sports / Show Timer

Format: %countdown::INITIAL_SECONDS::FORMAT%

The engine starts a wall-clock timer the first time it sees this tag for a given (itemId, type, target, format) tuple. As soon as the timer hits zero it stays at 00:00.

Supported output formats: HH:mm:ss, mm:ss:ii, mm:ss, ss:ii, ss. Anything else falls back to mm:ss.

LCGTextProps gameClock;
gameClock.text = "Time Left: %countdown::90::mm:ss%";   // 90-second countdown, mm:ss
gameClock.text = "Round 2: %countdown::180::HH:mm:ss%"; // 3-minute round, HH:mm:ss
LCGTextItem* clock = m_lCG->addItem("game_clock", 100, 100, 400, 60, gameClock);

📌 countup:: — Elapsed Timer

Format: %countup::TARGET_SECONDS::FORMAT%

Counts up from zero and clamps at the target value (then freezes). Useful for on-air elapsed-time readouts.

LCGTextProps elapsed;
elapsed.text = "Elapsed: %countup::600::mm:ss%";        // counts up to 10 minutes, then holds
elapsed.font = "Arial"; elapsed.fontSize = 32; elapsed.color = "#FFFFFF";
LCGTextItem* showTimer = m_lCG->addItem("show_timer", 100, 200, 400, 60, elapsed);

6. Mixer & Live Source Integration Example

The following C++ example demonstrates how to integrate LCharacter inside LMixer to overlay branding elements (logo, scrolling crawl, lower-third with live countdown, animated image sequence, transparent video bumper, and live camera inset) on top of a video playback.

mainwindow.cpp

#include "mainwindow.h"
#include "now2sdk.h"
#include "LFile.h"
#include "LMixer.h"
#include "LPreview.h"
#include "LCharacter.h"
#include "LLive.h"
#include <QTimer>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),
      m_lFile(new LFile()),
      m_lMixer(new LMixer()),
      m_lCG(new LCharacter()),
      m_lLive(new LLive()),
      m_lPreview(new LPreview())
{
    // 1. Configure Mixer Resolution (1080p50)
    videoFormatProps format;
    format.setVideoFormat = vF::HD1080_50p;
    m_lMixer->setVideoFormat(format);

    // 2. Add Background Video Layer (z-index: 0)
    m_lMixer->addLayer("video_bg", m_lFile, 0, 0, 0, 255, 1920, 1080);

    // 4. Add CG Character Generator Overlay Layer (z-index: 1)
    m_lMixer->addLayer("cg_overlay", m_lCG, 1, 0, 0, 255, 1920, 1080);

    // 5. Configure Preview Widget
    m_lPreview->setProps("ui_framework", "qt");
    m_lPreview->previewEnable(ui->programView, true, true);
    m_lPreview->previewObject(m_lMixer);

    // 6. Lock the composite FPS (50p for PAL, 60p for NTSC)
    m_lCG->setFPS(50.0);

    // 7. Load file and play
    m_lFile->fileNameSet("/media/background.mp4");
    m_lFile->play();
}

MainWindow::~MainWindow() {
    m_lFile->stop();
    delete m_lPreview;
    delete m_lLive;
    delete m_lFile;
    delete m_lMixer;
    delete m_lCG;
}

// ─── 1. Lower-Third with Live Countdown Clock ─────────────────────────────
void MainWindow::showLowerThird() {
    m_lCG->remove("lt_bg");
    m_lCG->remove("lt_title");
    m_lCG->remove("lt_desc");

    // Gradient background block
    LCGRectProps bg;
    bg.bgType = "Gradient";
    bg.bgGradientStartColor = "#0055ff";
    bg.bgGradientEndColor = "#020215";
    bg.bgAlpha = 240;
    bg.bgRadius = 12;
    bg.bgShadowOffsetX = 4.0f;
    bg.bgShadowOffsetY = 6.0f;
    bg.bgShadowAlpha = 160;
    bg.bgShadowColor = "#000000";
    m_lCG->addItem("lt_bg", 150, 800, 700, 140, bg);

    // Header
    LCGTextItem* title = m_lCG->addItem("lt_title", 150, 800, 700, 70, LCGTextProps{});
    title->props.text = "ALİ SABER";
    title->props.font = "Courier New";
    title->props.fontSize = 36;
    title->props.color = "#FFFFFF";
    title->props.outlineSize = 1;
    title->props.outlineColor = "#000000";
    title->props.hAlign = "Left";
    title->props.padding = 20;

    // Subtitle — interpolates the live timer on each frame
    LCGTextItem* desc = m_lCG->addItem("lt_desc", 150, 870, 700, 60, LCGTextProps{});
    desc->props.text = "Lead Broadcast Engineer  |  %countdown::90::mm:ss%";
    desc->props.font = "Courier New";
    desc->props.fontSize = 22;
    desc->props.color = "#55AAFF";
    desc->props.hAlign = "Left";
    desc->props.padding = 20;
    desc->props.autoShrink = true;

    m_lCG->forceUpdate();
}

// ─── 2. Scrolling Crawl Ticker ─────────────────────────────────────────────
void MainWindow::showNewsTicker() {
    m_lCG->remove("ticker");

    LCGCrawlProps crawl;
    crawl.text = "::: NOW2SDK CAIRO GRAPHICS ENGINE DEMO ::: EXTREMELY LOW LATENCY RASTERIZATION :::";
    crawl.font = "Arial";
    crawl.fontSize = 30;
    crawl.color = "#FFFF00";
    crawl.bgType = "Solid";
    crawl.bgColor = "#111111";
    crawl.bgAlpha = 220;
    crawl.textMove = "Horizontal";
    crawl.textSpeed = -4.0f; // negative = scroll left

    m_lCG->addItem("ticker", 0, 980, 1920, 80, crawl);
    m_lCG->forceUpdate();
}

// ─── 3. Vertical Rolling Ticker with Multiple Pages ────────────────────────
void MainWindow::showVerticalTicker() {
    m_lCG->remove("vticker");

    LCGTickerProps ticker;
    ticker.text = "Headline One\nHeadline Two\nHeadline Three\nBreaking News";
    ticker.font = "Arial";
    ticker.fontSize = 32;
    ticker.color = "#FFFFFF";
    ticker.bgType = "Color";
    ticker.bgColor = "#001133";
    ticker.bgAlpha = 230;
    ticker.textMove = "Up";
    ticker.textMoveDuration = 4.0f; // each page visible for 4 s

    m_lCG->addItem("vticker", 1620, 100, 280, 800, ticker);
    m_lCG->forceUpdate();
}

// ─── 4. Stop-Motion Image Sequence Loader ─────────────────────────────────
void MainWindow::showImageSequence() {
    m_lCG->remove("seq");

    LCGImageSeqProps seq;
    seq.folder = "/media/frames/intro";
    seq.fps = 30.0;
    seq.loop = true;
    seq.aspectLock = true;
    seq.stretchMode = 1; // Fit (letter-box)

    m_lCG->addItem("seq", 760, 380, 400, 320, seq);
    m_lCG->forceUpdate();
}

// ─── 5. Transparent Video Bumper (ProRes 4444 / WebM alpha) ───────────────
void MainWindow::showTransparentBumper() {
    m_lCG->remove("bumper");

    LCGVideoProps vid;
    vid.path = "/media/bumper_alpha.mov";
    vid.aspectLock = true;
    vid.stretchMode = 0; // Stretch
    vid.loop = true;

    m_lCG->addItem("bumper", 0, 0, 1920, 1080, vid);
    m_lCG->forceUpdate();
}

// ─── 5b. Static Raster Image (logo PNG with rounded clip + border) ──────
void MainWindow::showLogo() {
    m_lCG->remove("logo");

    LCGImageProps logo;
    logo.path     = "/media/brand/logo.png";
    logo.stretchMode  = 0;        // Stretch
    logo.aspectLock   = true;
    logo.bgRadius     = 16;       // rounded clip mask
    logo.borderSize   = 2;
    logo.borderColor  = "#FFFFFF";
    logo.borderAlpha  = 255;
    logo.bgShadowOffsetX = 3.0f;
    logo.bgShadowOffsetY = 3.0f;
    logo.bgShadowAlpha   = 160;
    logo.bgShadowColor   = "#000000";

    m_lCG->addItem("logo", 50, 50, 220, 80, logo);   // 50,50 = top-left coords
    m_lCG->forceUpdate();
}

// ─── 5c. Vector Circle Element (round badge with gradient) ──────────────
void MainWindow::showCircleBadge() {
    m_lCG->remove("badge");

    LCGCircleProps circle;
    circle.bgType      = "Gradient";
    circle.bgColor     = "#FFFFFF";
    circle.bgGradientStartColor = "#FF5500";
    circle.bgGradientEndColor   = "#880000";
    circle.bgAlpha     = 230;
    circle.borderSize  = 3;
    circle.borderColor = "#FFFFFF";
    circle.borderAlpha = 255;

    m_lCG->addItem("badge", 1820, 60, 80, 80, circle);
    m_lCG->forceUpdate();
}

// ─── 5d. Lens Flare (animated 6-ray flare with hotspot) ─────────────────
void MainWindow::showLensFlare() {
    m_lCG->remove("flare");

    LCGFlareProps flare;
    flare.rayLength      = 240.0f;
    flare.wingStrength   = 28.0f;
    flare.centerHotspot  = 0.40f;
    flare.centerStretch  = 1.0f;
    flare.brightness     = 230;
    flare.rotationSpeed  = 40.0f;   // degrees / second
    flare.angle          = 6;       // 6 rays around 360°
    flare.horizontalFade = 0.2f;
    flare.verticalFade   = 0.1f;
    flare.color          = "#FFEE88";
    flare.directionEnable     = true;
    flare.flowDirection       = 0;   // L -> R
    flare.directionSpeed      = 2500;
    flare.directionLoopDelay  = 0.5f;

    m_lCG->addItem("flare", 760, 420, 400, 240, flare);
    m_lCG->forceUpdate();
}

// ─── 6. Live Camera Inset (LLive source pulled live) ──────────────────────
void MainWindow::showLiveInset() {
    // 1. Pick a real device / channel / format reported by the SDK, then Start() capture.
    int devCount = 0; m_lLive->DeviceGetCount(devCount);
    if (devCount > 0) {
        std::string devName, devDesc;
        m_lLive->DeviceGetByIndex(0, devName, devDesc);
        m_lLive->DeviceSet(0);                       // select device 0

        int chCount = 0; m_lLive->DeviceChannelGetCount(0, chCount);
        if (chCount > 0) m_lLive->DeviceChannelSet(0);  // select channel 0

        videoFormatProps vidFormat; std::string fmtName;
        int fmtCount = 0; m_lLive->DeviceFormatVideoGetCount(0, 0, fmtCount);
        if (fmtCount > 0) m_lLive->DeviceFormatVideoSet(0); // first available mode

        m_lLive->Start();                            // start the capture thread
    }

    m_lCG->remove("livecam");

    // 2. Object-based binding: just hand the LLive* to LCGLiveProps.source.
    //    No registry call required — the engine attaches the sink on the next
    //    render frame and detaches it on remove() / clear() / destruction.
    LCGLiveProps live;
    live.source = m_lLive;       // direct LLive* binding
    live.aspectLock = true;
    live.stretchMode = 1;        // Fit
    live.borderSize = 4;
    live.borderColor = "#FFCC00";
    live.borderAlpha = 255;
    live.bgRadius = 24;

    m_lCG->addItem("livecam", 1480, 700, 360, 240, live);
    m_lCG->forceUpdate();
}

// ─── 7. Group Multiple Items for Bulk Translation ─────────────────────────
void MainWindow::groupLowerThird() {
    std::vector<std::string> ids = { "lt_bg", "lt_title", "lt_desc" };
    std::string gid = m_lCG->group(ids);

    LCGGroup* g = m_lCG->getGroupObject(gid);
    if (g) { g->x = 0; g->y = 0; } // anchor to current position

    m_lCG->forceUpdate();
}

// ─── 8. Save / Load CG Templates to XML ───────────────────────────────────
void MainWindow::exportTemplate() {
    m_lCG->saveToXMLFile("/templates/lower_third_branding.xml");
}

void MainWindow::importTemplate() {
    m_lCG->loadFromXMLFile("/templates/lower_third_branding.xml");
    m_lCG->forceUpdate();
}

// ─── 9. Clear CG overlays ─────────────────────────────────────────────────
void MainWindow::clearOverlays() {
    m_lCG->remove("lt_bg");
    m_lCG->remove("lt_title");
    m_lCG->remove("lt_desc");
    m_lCG->remove("ticker");
    m_lCG->remove("vticker");
    m_lCG->remove("seq");
    m_lCG->remove("bumper");
    m_lCG->remove("logo");
    m_lCG->remove("badge");
    m_lCG->remove("flare");
    m_lCG->remove("livecam");
    m_lCG->forceUpdate();
}