Skip to content

Commit 1a59dea

Browse files
committed
fix(engine): supersample screenshot capture to honor deviceScaleFactor
1 parent 994d013 commit 1a59dea

2 files changed

Lines changed: 15 additions & 1 deletion

File tree

packages/engine/src/services/frameCapture.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,14 @@ export async function createCaptureSession(
114114
const headlessShell = resolveHeadlessShellPath(config);
115115
const isLinux = process.platform === "linux";
116116
const forceScreenshot = config?.forceScreenshot ?? DEFAULT_CONFIG.forceScreenshot;
117+
// BeginFrame's screenshot does not honor a viewport `deviceScaleFactor`
118+
// (the captured surface is sized by the OS window in CSS pixels regardless
119+
// of `Emulation.setDeviceMetricsOverride`'s DPR). When supersampling we
120+
// need explicit clip+scale on `Page.captureScreenshot`, so fall back to
121+
// the screenshot path for any DPR > 1.
122+
const supersampling = (options.deviceScaleFactor ?? 1) > 1;
117123
const preMode: CaptureMode =
118-
headlessShell && isLinux && !forceScreenshot ? "beginframe" : "screenshot";
124+
headlessShell && isLinux && !forceScreenshot && !supersampling ? "beginframe" : "screenshot";
119125
const requestedGpuMode = config?.browserGpuMode ?? DEFAULT_CONFIG.browserGpuMode;
120126
const resolvedGpuMode = await resolveBrowserGpuMode(requestedGpuMode, {
121127
chromePath: headlessShell ?? undefined,

packages/engine/src/services/screenshotService.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,20 @@ export async function beginFrameCapture(
129129
export async function pageScreenshotCapture(page: Page, options: CaptureOptions): Promise<Buffer> {
130130
const client = await getCdpSession(page);
131131
const isPng = options.format === "png";
132+
const dpr = options.deviceScaleFactor ?? 1;
133+
// When supersampling, pass an explicit clip with `scale` so Chrome emits a
134+
// screenshot at device-pixel dimensions (`width × height × dpr`). Without
135+
// this, `Page.captureScreenshot` returns at CSS dimensions regardless of
136+
// the viewport's deviceScaleFactor.
137+
const clip =
138+
dpr > 1 ? { x: 0, y: 0, width: options.width, height: options.height, scale: dpr } : undefined;
132139
const result = await client.send("Page.captureScreenshot", {
133140
format: isPng ? "png" : "jpeg",
134141
quality: isPng ? undefined : (options.quality ?? 80),
135142
fromSurface: true,
136143
captureBeyondViewport: false,
137144
optimizeForSpeed: !isPng,
145+
...(clip ? { clip } : {}),
138146
});
139147
return Buffer.from(result.data, "base64");
140148
}

0 commit comments

Comments
 (0)