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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
//! Per-view code-built imaging bridge: drive a named `<Image>`'s pixels from a
//! Rust-provided bitmap (RGBA8 + size), no image file on disk required. The
//! imaging counterpart of the [`crate::brushes`] / [`crate::geometry`] write
//! bridges.
//!
//! Add a [`NoesisImaging`] component to the view's camera entity. Its `images`
//! map is the desired bitmap per `x:Name`: each entry carries the raw RGBA8
//! pixels, the bitmap's pixel size, and the `uri` the element's XAML `Source`
//! references. On change the bridge stages those pixels into the shared
//! [`crate::ImageRegistry`] under `uri`, so the live `Noesis::TextureProvider`
//! resolves `<Image Source="uri"/>` to the Rust bytes (the same path a `.png`
//! asset takes, but fed from memory).
//!
//! ```ignore
//! // XAML: <Image x:Name="Pic" Source="dm-bitmap://logo" Stretch="None"/>
//! let rgba = Arc::new(vec![255u8; 13 * 7 * 4]); // 13x7 opaque white
//! commands.entity(view).insert(
//! NoesisImaging::new().set("Pic", "dm-bitmap://logo", 13, 7, rgba),
//! );
//! ```
//!
//! # Timing
//!
//! Noesis resolves a `<Image>`'s `BitmapImage` source from the texture provider
//! **once, when the scene is first laid out**, and does not retry a miss. So the
//! bytes must be staged *before* the view's scene is built. Attach
//! [`NoesisImaging`] (populated) at spawn time, alongside the
//! [`NoesisView`](crate::NoesisView), rather than filling it in a later frame.
//! The staging system runs before the per-frame registry→provider sync precisely
//! so a same-frame spawn lands the bitmap ahead of scene build.
//!
//! # Why URI registration rather than `Image.Source = bitmap`
//!
//! Assigning a Rust-built `ImageSource` straight onto an element
//! (`Image::SetSource`) is an `unsafe` raw-pointer call in `noesis_runtime`, and
//! this crate is `unsafe_code = forbid` with no safe typed setter for it in the
//! runtime (unlike brushes' `set_background` / transforms' `set_render_transform`).
//! The texture-provider URI path is the safe route, and it is the canonical one
//! for tiled / streamed bitmaps anyway.
//!
//! # Observable
//!
//! The bridge *polls back* the live element and emits [`NoesisImageChanged`] when
//! a watched `<Image>`'s resolved size changes. Noesis sizes an `Image` from its
//! source's pixel dimensions, which it obtains from our texture provider's
//! `GetTextureInfo` during the layout pass, so a Rust-registered `13x7` bitmap
//! drives the element's `ActualWidth`/`ActualHeight` to `13`/`7` (with the
//! element authored `Stretch="None"`), with **no GPU render pass required**. An
//! unresolvable `Source` (nothing registered for its `uri`) measures to `0`, the
//! built-in negative control: a no-op apply, a wrong `uri`, or a wrong size all
//! read back differently from the requested dimensions.
//!
//! Everything runs on the main thread (Noesis is thread-affine and lives there):
//! the reconcile system stages each view's bytes into the registry, polls the
//! element read-back, and emits messages directly. No cross-world queues.
use HashMap;
use Arc;
use *;
use crateImageRegistry;
use crate;
/// A Rust-provided bitmap, declarative side: tightly-packed RGBA8 pixels plus
/// the `uri` the target element's `Source` references. Staged into the shared
/// [`ImageRegistry`] at apply time so the live texture provider resolves it.
/// Per-view imaging bridge. Attach to a [`NoesisView`](crate::NoesisView) entity.
/// What was read back from a watched `<Image>` element after layout: the
/// observable proof a Rust-provided bitmap actually reached the live element. A
/// `Source` that resolved to our registered bytes carries the bitmap's pixel
/// dimensions in [`actual_size`](Self::actual_size); an unresolvable source
/// measures to `[0.0, 0.0]`.
/// Emitted when a watched `<Image>`'s read-back changes from the previous frame's
/// snapshot. Proves a [`ImageBitmap`] reached the element: once the texture
/// provider resolves the staged bytes, `readback.actual_size` becomes the
/// bitmap's pixel size. Read with `MessageReader<NoesisImageChanged>`.
/// Stage every changed [`NoesisImaging`]'s bitmaps into the [`ImageRegistry`].
/// Runs before the registry→provider sync (and thus before scene build) so a
/// same-frame spawn lands the bytes ahead of Noesis's one-shot source
/// resolution. Independent of [`NoesisRenderState`]: a plain resource write.
pub
/// Poll each view's watched `<Image>` elements and emit [`NoesisImageChanged`]
/// when a resolved size / source presence changes. Runs in
/// [`NoesisSet::Apply`], reading the layout the previous frame's drive produced.
pub
/// Wires the per-view imaging bridge. Added transitively by [`crate::NoesisPlugin`].
;