1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//! Screenshot capture handle for agg-gui apps.
//!
//! The GL rendering harness (`GlGfxCtx::read_screenshot` on the desktop GL
//! path + the equivalent WebGL2 read-back in the WASM harness) produces a
//! top-down RGBA8 buffer of the current back buffer. This module supplies
//! the small shared-state handle that a button or hotkey uses to
//! **request** a capture and that a widget uses to **display** the result.
//!
//! # Threading / ownership
//!
//! All fields are `Rc<...>` — single-threaded, cheap to clone. Never
//! transfer a [`ScreenshotHandle`] across threads.
//!
//! # Wiring on native (winit + glow)
//!
//! ```ignore
//! let shot = agg_gui::ScreenshotHandle::new();
//!
//! // In a button's on_click:
//! let req = shot.request.clone();
//! Button::new("📷 Capture", font).on_click(move || req.set(true))
//!
//! // In the event loop, AFTER render_frame but BEFORE swap_buffers:
//! if shot.request.get() {
//! let (rgba, w, h) = gl_ctx.read_screenshot();
//! *shot.image.borrow_mut() = Some((rgba, w, h));
//! shot.request.set(false);
//! }
//!
//! // Display: pass `shot.image` to `ImageView`.
//! ```
//!
//! # Wiring on WASM
//!
//! Same Rust-side flow — the browser's WebGL2 context still provides
//! `glReadPixels`, so `GlGfxCtx::read_screenshot()` works unchanged. The
//! JS side needs no special code beyond driving the animation loop:
//!
//! ```ignore
//! // In the WASM render export (called from JS requestAnimationFrame):
//! if shot.request.get() {
//! let (rgba, w, h) = gl_ctx.read_screenshot(); // must be BEFORE presenting
//! *shot.image.borrow_mut() = Some((rgba, w, h));
//! shot.request.set(false);
//! }
//! ```
//!
//! Note for the LLM / future dev: on WASM, `read_screenshot` MUST be called
//! before the browser composites the canvas (i.e. within the same rAF
//! tick, before yielding). Because WebGL uses a preserved-drawing-buffer
//! only when explicitly requested, calling it outside that window yields
//! a blank image. The natural "after paint, before yield" position in the
//! render function is correct.
//!
//! If the app wants to TRIGGER a browser download instead of displaying
//! in-canvas, export a WASM function that calls `read_screenshot`, encode
//! with the `png` crate via `agg_gui::encode_png_rgba` (if available in
//! the surrounding app), and pass the bytes to a JS helper that creates a
//! `Blob` + `URL.createObjectURL` + synthetic `<a download>` click.
use ;
use Rc;
/// Shared capture state. Clone freely; all inner fields are `Rc<...>`.
// ─── Capture-aware render orchestration ─────────────────────────────────
//
// Both the native (winit/glutin) and wasm (rAF/WebGL2) harnesses need the
// same "screenshot capture" flow around their per-frame render:
//
// 1. If a capture was requested:
// a. Flip `capturing` to true so the Screenshot preview pane
// paints empty (so captured pixels don't include last frame's
// preview — the hall-of-mirrors bug).
// b. Render the frame (platform-specific: clear + paint widgets).
// c. `glReadPixels` the back buffer (platform-specific).
// d. Publish the bytes into `image` and clear both flags.
// e. Render again — this time the preview pane reveals the
// freshly-captured image.
// 2. Otherwise: render once.
//
// The orchestration (flag flipping, double-render, Arc wrap) is
// platform-agnostic and belongs here; each host supplies two closures:
// - `render_fn()` : clear the framebuffer and paint the widget
// tree once (the host's existing frame path).
// - `read_back_buffer()` : glReadPixels the current framebuffer and
// return `(rgba, width, height)`.
/// Run one frame through the screenshot capture flow.
///
/// Call this instead of invoking the per-frame render directly. It runs
/// the single-render path in the common case and the double-render
/// capture path when `request` is set.
///
/// `ctx` is the host's rendering context (e.g. the GL `GlGfxCtx`) —
/// passed in once and handed through to each closure so the two
/// closures don't both borrow it from their capture environment (which
/// the borrow checker can't reconcile statically even though the
/// closures are invoked sequentially).
///
/// The `image` field uses the `Arc<Vec<u8>>` form so the GL back-end's
/// texture cache can key on the Arc's pointer identity — see
/// `gfx_ctx::draw_image_rgba_arc`.