/**
 * Screenshot Service
 *
 * BeginFrame-based deterministic screenshot capture and video frame injection.
 */
import { type Page } from "puppeteer-core";
import { type CaptureOptions } from "../types.js";
export declare const cdpSessionCache: WeakMap<Page, import("puppeteer-core").CDPSession>;
export declare function getCdpSession(page: Page): Promise<import("puppeteer-core").CDPSession>;
export declare function shouldDefaultCaptureBeyondViewport(browserVersion: string, platform?: NodeJS.Platform): boolean;
/**
 * BeginFrame result with screenshot data and damage detection.
 */
export interface BeginFrameResult {
    buffer: Buffer;
    hasDamage: boolean;
}
/**
 * Issue a single no-output BeginFrame and race it against `timeoutMs`.
 *
 * On SwiftShader, compositions with many promoted layers (multi-group nested
 * opacity caption animations) can stall the FIRST BeginFrame indefinitely —
 * tested to 30 minutes without completion (style-7/8/10/15-prod). The
 * auto-worker calibration path catches this with its own capped protocol
 * timeout, but renders with an explicit `--workers N` skip calibration and
 * would hang for the full protocol timeout (and never succeed). This probe
 * gives the producer a cheap liveness signal right after session init:
 * `false` means route the render through screenshot capture instead.
 *
 * Healthy comps complete the probe in well under a second on GPU and within
 * a few seconds on SwiftShader. A protocol error also resolves `false` —
 * the safe direction (screenshot capture always works).
 */
export declare function probeBeginFrameLiveness(page: Page, timeoutMs: number, frameTimeTicks?: number, intervalMs?: number): Promise<boolean>;
export declare function beginFrameCapture(page: Page, options: CaptureOptions, frameTimeTicks: number, interval: number): Promise<BeginFrameResult>;
/**
 * True if the page's actual rendered content is taller than the requested
 * capture height. `captureBeyondViewport` exists for exactly one reason
 * (#1094): a native `<video>` surface whose content genuinely overflows the
 * viewport-bound capture path clips its bottom edge to black. A video that
 * fits entirely inside its composition's declared viewport doesn't have that
 * problem — ground-truth measurement beats the coarser "has a video, so
 * always request beyond-viewport" heuristic, which also unnecessarily routes
 * every video render through a CDP capture path prone to producing phantom
 * duplicate content on SwiftShader (#2550).
 *
 * Callers measure once after page settle. Hyperframes compositions have a
 * fixed-height, overflow-clipped render surface; timeline animation may move
 * pixels within that surface but must not grow document flow during capture.
 */
export declare function pageContentExceedsCaptureHeight(page: Page, requestedHeight: number): Promise<boolean>;
/**
 * Capture a screenshot using standard Page.captureScreenshot CDP call.
 * Fallback for environments where BeginFrame is unavailable (macOS, Windows).
 *
 * For `format: "png"` captures we disable Chrome's `optimizeForSpeed` fast
 * path. The fast path uses a zero-alpha-aware codec that crushes real alpha
 * values to 0 or 255 (verified empirically; CDP docs don't document this) —
 * exactly the same caveat called out on `captureScreenshotWithAlpha` /
 * `captureAlphaPng`. Keeping the fast path for opaque jpeg captures is fine.
 */
export declare function pageScreenshotCapture(page: Page, options: CaptureOptions): Promise<Buffer>;
/**
 * Capture a screenshot with transparent background (PNG + alpha channel).
 *
 * Used in the two-pass HDR compositing pipeline — captures DOM content
 * (text, graphics, SDR overlays) with transparency where the background shows,
 * so it can be overlaid on top of native HDR video frames in FFmpeg.
 *
 * Sets and restores the background color override on every call. For sessions
 * that capture many frames, prefer calling initTransparentBackground() once
 * at session init, then captureAlphaPng() per frame to avoid the 2× CDP
 * round-trip overhead.
 */
export declare function captureScreenshotWithAlpha(page: Page, width: number, height: number): Promise<Buffer>;
export declare function initTransparentBackground(page: Page): Promise<void>;
/**
 * Capture a transparent-background PNG screenshot without setting the
 * background color override. Requires initTransparentBackground() to have
 * been called once on this session.
 *
 * Faster than captureScreenshotWithAlpha() for per-frame use in the HDR
 * two-pass compositing loop.
 */
export declare function captureAlphaPng(page: Page, width: number, height: number): Promise<Buffer>;
/**
 * Stylesheet ID used by applyDomLayerMask / removeDomLayerMask. Exposed so
 * tests can assert presence/absence of the mask between captures.
 */
export declare const DOM_LAYER_MASK_STYLE_ID = "__hf_dom_layer_mask__";
/**
 * Mask the DOM so a single layer screenshot captures ONLY the layer's pixels.
 *
 * The HDR layered compositor walks z-ordered layers and blits each one over a
 * shared canvas. DOM layers are full-page screenshots — a naive screenshot
 * captures every painted pixel on the page, which means root background +
 * static overlays + sibling-scene content all overwrite previously composited
 * HDR content beneath. The mask narrows each screenshot to the elements that
 * actually belong to this layer.
 *
 * Strategy:
 *
 * 1. Inject a stylesheet that hides every body descendant
 *    (`body * { visibility: hidden !important }`) and re-shows the layer's
 *    elements (and their descendants, injected `__render_frame_*` siblings,
 *    and media color-grading canvases) via `visibility: visible !important`. CSS `visibility: visible`
 *    on a descendant overrides an ancestor's `visibility: hidden`, so deep
 *    layer elements remain visible even though intermediate parents are
 *    hidden by the mass-hide rule.
 * 2. Inline-hide each `extraHideId` (and its render-frame/color-grading siblings) with
 *    `visibility: hidden !important`, while first recording its previous
 *    inline visibility. Inline `!important` beats stylesheet `!important`,
 *    so this overrides the show rule for elements that fall under a show
 *    selector but should NOT paint — typically other-layer elements that are
 *    descendants of a container layer (for example HDR videos and other-layer
 *    SDR videos are descendants of `#root` when we capture the root DOM layer).
 * 3. Inline-hide timed descendants of shown elements that were hidden before
 *    the mask was installed. This covers idless child clips and same-layer
 *    descendants that the `extraHideIds` id list cannot represent.
 *
 * Only `visibility` is set on extraHideIds — never `opacity`. CSS opacity is
 * multiplicative through the descendant chain and a descendant cannot escape
 * an ancestor's `opacity: 0`. If `#root` is in `extraHideIds` and we set
 * `opacity: 0` on it, every descendant — including `#vid-5-b` and its
 * `__render_frame_vid-5-b__` IMG — becomes invisible even with
 * `visibility: visible !important`. `visibility` does NOT have this problem:
 * a descendant with `visibility: visible` overrides an ancestor's
 * `visibility: hidden`.
 *
 * Layout is preserved (visibility doesn't trigger reflow), so border-radius
 * clipping, overflow:hidden, and absolute positioning continue to apply to
 * the visible layer elements. Opacity is also preserved — an ancestor at
 * `opacity: 0` (e.g. an inactive scene during a transition) still
 * propagates to its descendants, which is the desired behavior during
 * cross-scene blends.
 *
 * Idempotent across calls: an existing mask stylesheet is removed before a
 * new one is installed, so consecutive `applyDomLayerMask` invocations leave
 * exactly one stylesheet attached.
 */
export declare function applyDomLayerMask(page: Page, showIds: string[], extraHideIds: string[]): Promise<void>;
/**
 * Tear down the mask installed by applyDomLayerMask.
 *
 * Removes the mask stylesheet and restores the inline `visibility` values
 * temporarily overwritten for hidden timed descendants, `extraHideIds`, and
 * their render-frame/color-grading siblings.
 *
 * IMPORTANT: We do NOT strip inline `opacity` here. applyDomLayerMask only
 * ever sets `visibility` (never `opacity`), so any inline opacity present on
 * a wrapper was put there by user animation code (typically GSAP) and must
 * survive across per-layer captures. GSAP's seek with suppress-events does
 * not re-apply tweens when the timeline is already at the target time, so if
 * we strip opacity here and then seek to the same time for the next layer,
 * GSAP won't put it back and the wrapper will render fully opaque.
 */
export declare function removeDomLayerMask(page: Page, _extraHideIds: string[]): Promise<void>;
/**
 * Pre-create hidden `__render_frame__` sibling `<img>`s for every
 * `video[data-start]` in the page. Idempotent — videos that already
 * have a sibling are skipped.
 *
 * `injectVideoFramesBatch` creates the sibling on the fly the first time
 * it paints a given videoId (the `isNewImage = !hasImg` branch below).
 * Under chrome-headless-shell's deterministic + `HeadlessExperimental.
 * BeginFrame` mode, the immediately-next BeginFrame captures before the
 * freshly-inserted `<img>` layer lands in the compositor's layer tree;
 * the layer arrives a frame later. That single frame paints only the
 * body background + previously-composed overlays.
 *
 * Called from `initializeSession`: in the screenshot path at the end (that
 * capture path flushes paint, so timing doesn't matter), and in the BeginFrame
 * path followed by one explicit visual `HeadlessExperimental.beginFrame`
 * (`noDisplayUpdates: false`) that composites the new layers before the first
 * capture — the warmup ticks are `noDisplayUpdates: true` and don't paint.
 * Every subsequent `injectVideoFramesBatch` then takes the `hasImg = true` path
 * (just an `img.src` update). The `isNewImage` branch stays as a fallback for
 * callers that don't run through `initializeSession`.
 */
export declare function ensureRenderFrameSiblings(page: Page): Promise<void>;
/**
 * Returns the subset of `updates.videoId`s that were actually painted in
 * this call. Videos skipped because of a hidden visual ancestor are NOT
 * included — the caller relies on this to avoid recording a `lastInjected`
 * cache entry for a frame that never reached the page, which would otherwise
 * short-circuit the next inject at the same frameIndex and leave the host's
 * first visible frame blank.
 */
export declare function injectVideoFramesBatch(page: Page, updates: Array<{
    videoId: string;
    dataUri: string;
}>): Promise<string[]>;
export declare function syncVideoFrameVisibility(page: Page, activeVideoIds: string[]): Promise<void>;
//# sourceMappingURL=screenshotService.d.ts.map