Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
screenshot.hpp
Go to the documentation of this file.
1#pragma once
2
4#include <pulp/view/view.hpp>
5#include <string>
6#include <vector>
7#include <cstdint>
8#include <functional>
9
10namespace pulp::view {
11
21
22// Render a view tree to a PNG image buffer (headless, no window needed).
23//
24// Platform support:
25// - macOS: native CoreGraphics-backed capture.
26// - iOS: provider-backed fallback only; no native capture backend yet.
27// - Windows/Linux: built-in Skia raster backend when Skia is available.
28// - Android or builds without a native backend: the host app registers a
29// provider via set_screenshot_provider(). Without one, returns empty
30// vector / false explicitly.
31//
32// Callers can probe has_screenshot_provider() to distinguish
33// "no backend installed" from "render failed".
34//
35// `text_raster` is the glyph preblend of the Skia surfaces the render creates
36// (skia and gpu backends; see pulp/canvas/text_raster_policy.hpp). An imported
37// panel passes select_text_raster_policy() over its captured text so the
38// capture renders the way its live surface does. The CoreGraphics backend and
39// a host-registered provider ignore it.
40std::vector<uint8_t>
41render_to_png(View& root, uint32_t width, uint32_t height,
42 float scale = 2.0f, // Retina scale factor
45
46// Render a view tree to a PNG file
47bool render_to_file(View& root, uint32_t width, uint32_t height, const std::string& output_path,
48 float scale = 2.0f,
51
52// ── Smart, always-honest capture (the "always-capturable" must-have) ─────
53//
54// One entry point so agents/CI don't pick the wrong backend. capture_view()
55// inspects the view tree and does the right thing, and NEVER returns a silent
56// blank:
57// 1. A subtree marked `contains_native_overlay()` (a WebView / native NSView
58// composited by the OS, not painted into the Pulp canvas) → ok=false with a
59// reason; such overlays are invisible to headless capture (use a real
60// window / OS screencapture).
61// 2. `requires_gpu_host()` anywhere in the tree → the `gpu` backend
62// (HeadlessSurface, Dawn+Skia) which renders GPU content correctly.
63// 3. Otherwise → CPU raster (`skia` / platform default).
64// The captured PNG is gated by the content floor (analyze_screenshot_content):
65// a blank / clear-only frame sets ok=false with a reason. `png` may still be
66// populated when !ok so callers can save it for debugging.
68 View* native_overlay = nullptr;
69 bool requires_gpu = false;
70};
71
76
78 std::vector<uint8_t> png;
79 bool ok = false;
81 std::string reason;
82};
83
85 View& root,
86 uint32_t width,
87 uint32_t height,
88 float scale = 2.0f,
89 ScreenshotBackend backend = ScreenshotBackend::auto_select // smart by default
90);
91
92// Render a view tree through the offscreen GPU surface (Dawn + Skia via
93// pulp::render::HeadlessSurface). Returns empty bytes if this build has no GPU
94// backend (PULP_HAS_SKIA off / no pulp-render). Unlike the raster backends,
95// this renders `requires_gpu_host()` views correctly.
96std::vector<uint8_t>
97render_to_png_gpu(View& root, uint32_t width, uint32_t height, float scale = 2.0f,
99
100// True when render_to_png_gpu can produce frames in this build (GPU backend
101// compiled in). Lets capture_view fall back honestly when GPU is unavailable.
103
104// True when capture_view() has an in-process producer available in this build:
105// a platform-native raster backend, portable Skia/GPU capture, or a registered
106// ScreenshotProvider whose output this build can decode for the mandatory
107// blank/content floor. This is a capability probe only; an individual capture
108// can still fail (for example, an unavailable GPU device or a blank frame).
110
115
119bool passes_capture_content_floor(const std::vector<uint8_t>& png);
120
121// Raw-RGBA sibling of render_to_png: render a view tree headlessly and hand
122// back the decoded pixel buffer instead of PNG bytes — for callers that want
123// to composite or upload the frame themselves (e.g. a foreign host that draws
124// Pulp's output into its own surface) without paying a PNG encode + decode
125// round-trip. The internal Skia raster path already holds these pixels before
126// encoding, so this exposes them directly.
127//
128// Output: tightly packed RGBA8 (R,G,B,A byte order), premultiplied alpha,
129// sRGB, top-to-bottom rows, stride == out_width * 4. The pixel dimensions are
130// the logical width/height multiplied by `scale`, returned via out_width /
131// out_height so the caller can size its buffer exactly. Returns an empty
132// vector on failure (no Skia backend, surface alloc failed, read-back failed).
133//
134// Backend: forces the Skia raster path when available (the only backend that
135// produces a stable, host-independent RGBA buffer). Without Skia this returns
136// empty (CoreGraphics capture is PNG-only here).
137std::vector<uint8_t>
138render_to_rgba(View& root, uint32_t width, uint32_t height, float scale, uint32_t* out_width,
139 uint32_t* out_height,
141
142// Whether this BUILD has a raw-RGBA producer at all — a property of which
143// backend was compiled in, not of any particular render.
144//
145// A pixel test needs to tell two situations apart that look identical from the
146// outside: a build with no raster backend, where every probe returns an empty
147// buffer no matter what the code under test does, and a build that can
148// rasterize but produced nothing, which is a real defect. Deciding from an
149// empty result cannot separate them, and skipping on an empty result would
150// turn a genuine rasterizer regression into a silently green run.
151//
152// So the question is asked of the build, and a test may skip only when the
153// answer is false. When it is true, an empty buffer stays a failure.
155
156// ── Host-registered screenshot provider (#299) ──────────────────────────
157//
158// Non-Apple platforms don't have a built-in screenshot backend in
159// core/view. A host app (or future platform module) installs a
160// provider here — e.g., a Skia-backed raster renderer on Linux/
161// Windows — and render_to_png/file will delegate to it. Apple
162// platforms' native impls ignore the provider.
163
164using ScreenshotProvider = std::function<std::vector<uint8_t>(
165 View& root,
166 uint32_t width,
167 uint32_t height,
168 float scale,
169 ScreenshotBackend backend)>;
170
174
175} // namespace pulp::view
Definition view.hpp:73
TextRasterPolicy
Glyph preblend a surface is created with.
Definition text_raster_policy.hpp:33
@ legacy
Definition text_raster_policy.hpp:34
Hover-cursor tracking for platform window hosts.
Definition fwd.hpp:27
std::vector< uint8_t > render_to_png_gpu(View &root, uint32_t width, uint32_t height, float scale=2.0f, canvas::TextRasterPolicy text_raster=canvas::TextRasterPolicy::legacy)
std::vector< uint8_t > render_to_png(View &root, uint32_t width, uint32_t height, float scale=2.0f, ScreenshotBackend backend=ScreenshotBackend::default_backend, canvas::TextRasterPolicy text_raster=canvas::TextRasterPolicy::legacy)
bool has_screenshot_backend()
ScreenshotBackend
Definition screenshot.hpp:12
@ gpu
Offscreen GPU (Dawn + Skia via pulp::render::HeadlessSurface).
Definition screenshot.hpp:16
@ skia
CPU Skia raster (SkSurfaces::Raster).
Definition screenshot.hpp:15
@ auto_select
Smart dispatch (see capture_view): native-overlay → refuse; requires_gpu_host → gpu; else → raster.
Definition screenshot.hpp:18
@ coregraphics
Apple CoreGraphics raster.
Definition screenshot.hpp:14
@ default_backend
Platform default raster (CoreGraphics on Apple).
Definition screenshot.hpp:13
bool has_screenshot_provider()
std::vector< uint8_t > render_to_rgba(View &root, uint32_t width, uint32_t height, float scale, uint32_t *out_width, uint32_t *out_height, canvas::TextRasterPolicy text_raster=canvas::TextRasterPolicy::legacy)
bool has_gpu_capture()
bool has_screenshot_decoder()
True when this build can decode PNG pixels for the mandatory capture content floor.
bool raw_rgba_render_available()
void set_screenshot_provider(ScreenshotProvider provider)
CaptureRequirements inspect_capture_requirements(View &root)
Inspect the tree once for requirements shared by capture routing and capability admission.
bool render_to_file(View &root, uint32_t width, uint32_t height, const std::string &output_path, float scale=2.0f, ScreenshotBackend backend=ScreenshotBackend::default_backend, canvas::TextRasterPolicy text_raster=canvas::TextRasterPolicy::legacy)
CaptureResult capture_view(View &root, uint32_t width, uint32_t height, float scale=2.0f, ScreenshotBackend backend=ScreenshotBackend::auto_select)
std::function< std::vector< uint8_t >( View &root, uint32_t width, uint32_t height, float scale, ScreenshotBackend backend)> ScreenshotProvider
Definition screenshot.hpp:164
@ scale
Definition css_animation.hpp:126
@ height
Definition css_animation.hpp:128
@ width
Definition css_animation.hpp:127
void clear_screenshot_provider()
bool passes_capture_content_floor(const std::vector< uint8_t > &png)
Apply the same deliberately lenient "did anything paint?" floor used by capture_view().
Definition screenshot.hpp:67
bool requires_gpu
True when any node needs a GPU host.
Definition screenshot.hpp:69
View * native_overlay
First OS-composited overlay owner, if any.
Definition screenshot.hpp:68
Definition screenshot.hpp:77
ScreenshotBackend used
Definition screenshot.hpp:80
std::vector< uint8_t > png
Definition screenshot.hpp:78
std::string reason
Empty when ok; otherwise why the capture isn't trustworthy.
Definition screenshot.hpp:81
bool ok
Definition screenshot.hpp:79