elit 3.6.5 → 3.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/README.md +6 -0
  4. package/dist/build.cjs +421 -331
  5. package/dist/build.d.ts +1 -16
  6. package/dist/build.js +420 -330
  7. package/dist/build.mjs +420 -330
  8. package/dist/chokidar.cjs +219 -182
  9. package/dist/chokidar.d.ts +25 -10
  10. package/dist/chokidar.js +217 -182
  11. package/dist/chokidar.mjs +218 -183
  12. package/dist/cli.cjs +21608 -20241
  13. package/dist/cli.d.ts +19 -37
  14. package/dist/cli.mjs +21262 -19910
  15. package/dist/config.cjs +357 -350
  16. package/dist/config.d.ts +19 -240
  17. package/dist/config.js +520 -515
  18. package/dist/config.mjs +346 -341
  19. package/dist/contracts-BeW9k0yZ.d.ts +54 -0
  20. package/dist/contracts-D7KIS-TK.d.ts +36 -0
  21. package/dist/coverage.cjs +448 -485
  22. package/dist/coverage.d.ts +13 -59
  23. package/dist/coverage.js +447 -484
  24. package/dist/coverage.mjs +447 -484
  25. package/dist/database.cjs +819 -828
  26. package/dist/database.d.ts +8 -24
  27. package/dist/database.js +818 -829
  28. package/dist/database.mjs +818 -829
  29. package/dist/desktop-auto-render.cjs +1700 -1522
  30. package/dist/desktop-auto-render.d.ts +4 -9
  31. package/dist/desktop-auto-render.js +1695 -1517
  32. package/dist/desktop-auto-render.mjs +1696 -1518
  33. package/dist/desktop.cjs +3 -1
  34. package/dist/desktop.d.ts +4 -1
  35. package/dist/desktop.js +1 -1
  36. package/dist/desktop.mjs +1 -1
  37. package/dist/dev-build.cjs +830 -0
  38. package/dist/dev-build.d.ts +53 -0
  39. package/dist/dev-build.js +3318 -0
  40. package/dist/dev-build.mjs +797 -0
  41. package/dist/dom.cjs +717 -590
  42. package/dist/dom.d.ts +2 -15
  43. package/dist/dom.js +714 -587
  44. package/dist/dom.mjs +716 -589
  45. package/dist/el.cjs +62 -52
  46. package/dist/el.d.ts +5 -10
  47. package/dist/el.js +60 -52
  48. package/dist/el.mjs +60 -52
  49. package/dist/fs.cjs +72 -63
  50. package/dist/fs.d.ts +22 -19
  51. package/dist/fs.js +71 -62
  52. package/dist/fs.mjs +71 -62
  53. package/dist/hmr.cjs +40 -14
  54. package/dist/hmr.d.ts +11 -23
  55. package/dist/hmr.js +38 -14
  56. package/dist/hmr.mjs +38 -14
  57. package/dist/http.cjs +251 -99
  58. package/dist/http.d.ts +38 -104
  59. package/dist/http.js +249 -99
  60. package/dist/http.mjs +249 -99
  61. package/dist/https.cjs +524 -228
  62. package/dist/https.d.ts +44 -36
  63. package/dist/https.js +520 -226
  64. package/dist/https.mjs +522 -228
  65. package/dist/index.cjs +7502 -7690
  66. package/dist/index.d.ts +8 -3
  67. package/dist/index.js +7486 -7676
  68. package/dist/index.mjs +7497 -7686
  69. package/dist/mime-types.cjs +10 -4
  70. package/dist/mime-types.d.ts +8 -11
  71. package/dist/mime-types.js +9 -3
  72. package/dist/mime-types.mjs +9 -3
  73. package/dist/native.cjs +8616 -8869
  74. package/dist/native.d.ts +7 -8
  75. package/dist/native.js +8682 -8935
  76. package/dist/native.mjs +8615 -8868
  77. package/dist/path.cjs +83 -77
  78. package/dist/path.d.ts +29 -29
  79. package/dist/path.js +82 -76
  80. package/dist/path.mjs +82 -76
  81. package/dist/pm.cjs +3300 -0
  82. package/dist/pm.d.ts +256 -0
  83. package/dist/pm.js +5638 -0
  84. package/dist/pm.mjs +3196 -0
  85. package/dist/preview-build.cjs +712 -0
  86. package/dist/preview-build.d.ts +59 -0
  87. package/dist/preview-build.js +3194 -0
  88. package/dist/preview-build.mjs +676 -0
  89. package/dist/render-context.cjs +13 -2
  90. package/dist/render-context.d.ts +9 -31
  91. package/dist/render-context.js +11 -2
  92. package/dist/render-context.mjs +11 -2
  93. package/dist/router.cjs +787 -645
  94. package/dist/router.d.ts +8 -12
  95. package/dist/router.js +786 -644
  96. package/dist/router.mjs +786 -644
  97. package/dist/runtime.cjs +1 -1
  98. package/dist/runtime.js +1 -1
  99. package/dist/runtime.mjs +1 -1
  100. package/dist/server.cjs +3315 -2603
  101. package/dist/server.d.ts +49 -4
  102. package/dist/server.js +7611 -2834
  103. package/dist/server.mjs +3317 -2607
  104. package/dist/smtp-server.cjs +128 -0
  105. package/dist/smtp-server.d.ts +27 -0
  106. package/dist/smtp-server.js +4199 -0
  107. package/dist/smtp-server.mjs +100 -0
  108. package/dist/state-DvEkDehk.d.ts +195 -0
  109. package/dist/state.cjs +768 -658
  110. package/dist/state.d.ts +11 -69
  111. package/dist/state.js +760 -650
  112. package/dist/state.mjs +767 -657
  113. package/dist/style.cjs +1011 -968
  114. package/dist/style.d.ts +13 -127
  115. package/dist/style.js +1009 -970
  116. package/dist/style.mjs +1011 -971
  117. package/dist/test-reporter.cjs +332 -316
  118. package/dist/test-reporter.d.ts +28 -33
  119. package/dist/test-reporter.js +328 -312
  120. package/dist/test-reporter.mjs +328 -312
  121. package/dist/test-runtime.cjs +927 -968
  122. package/dist/test-runtime.d.ts +24 -99
  123. package/dist/test-runtime.js +922 -965
  124. package/dist/test-runtime.mjs +922 -965
  125. package/dist/test.cjs +4428 -4273
  126. package/dist/test.d.ts +2 -8
  127. package/dist/test.js +4307 -4154
  128. package/dist/test.mjs +4419 -4267
  129. package/dist/types-BONVzPtp.d.ts +59 -0
  130. package/dist/types-BR4wMiVx.d.ts +32 -0
  131. package/dist/types-C4gKykuG.d.ts +23 -0
  132. package/dist/types-CIhpN1-K.d.ts +64 -0
  133. package/dist/types-Ckj8md_j.d.ts +84 -0
  134. package/dist/types-CpjQTAkX.d.ts +24 -0
  135. package/dist/types-D0LjrYjS.d.ts +14 -0
  136. package/dist/types-DAisuVr5.d.ts +75 -0
  137. package/dist/types-tJn88E1N.d.ts +242 -0
  138. package/dist/types.d.ts +71 -226
  139. package/dist/universal.cjs +1 -1
  140. package/dist/universal.d.ts +1 -5
  141. package/dist/universal.js +1 -1
  142. package/dist/universal.mjs +1 -1
  143. package/dist/websocket-XfyK23zD.d.ts +119 -0
  144. package/dist/ws.cjs +129 -108
  145. package/dist/ws.d.ts +21 -131
  146. package/dist/ws.js +128 -109
  147. package/dist/ws.mjs +128 -109
  148. package/dist/wss.cjs +757 -479
  149. package/dist/wss.d.ts +31 -28
  150. package/dist/wss.js +755 -479
  151. package/dist/wss.mjs +758 -482
  152. package/package.json +16 -1
  153. package/vendor/epaint-0.31.1/src/image.rs +418 -0
  154. package/dist/server-CcBFc2F5.d.ts +0 -449
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elit",
3
- "version": "3.6.5",
3
+ "version": "3.6.7",
4
4
  "description": "Optimized lightweight library for creating DOM elements with reactive state",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -95,6 +95,12 @@
95
95
  "import": "./dist/https.mjs",
96
96
  "require": "./dist/https.cjs"
97
97
  },
98
+ "./smtp-server": {
99
+ "types": "./dist/smtp-server.d.ts",
100
+ "browser": "./dist/smtp-server.js",
101
+ "import": "./dist/smtp-server.mjs",
102
+ "require": "./dist/smtp-server.cjs"
103
+ },
98
104
  "./ws": {
99
105
  "types": "./dist/ws.d.ts",
100
106
  "browser": "./dist/ws.js",
@@ -137,6 +143,12 @@
137
143
  "import": "./dist/runtime.mjs",
138
144
  "require": "./dist/runtime.cjs"
139
145
  },
146
+ "./pm": {
147
+ "types": "./dist/pm.d.ts",
148
+ "browser": "./dist/pm.js",
149
+ "import": "./dist/pm.mjs",
150
+ "require": "./dist/pm.cjs"
151
+ },
140
152
  "./database": {
141
153
  "types": "./dist/database.d.ts",
142
154
  "browser": "./dist/database.js",
@@ -257,12 +269,14 @@
257
269
  "esbuild": "^0.27.3",
258
270
  "esbuild-obfuscator-plugin": "^0.0.5",
259
271
  "open": "^11.0.0",
272
+ "smtp-server": "^3.18.4",
260
273
  "source-map": "^0.7.6"
261
274
  },
262
275
  "devDependencies": {
263
276
  "@types/bun": "^1.3.6",
264
277
  "@types/mime-types": "^2.1.4",
265
278
  "@types/node": "^25.0.10",
279
+ "@types/smtp-server": "^3.5.13",
266
280
  "@types/ws": "^8.5.13",
267
281
  "copyfiles": "^2.4.1",
268
282
  "elysia": "^1.4.19",
@@ -277,6 +291,7 @@
277
291
  "dist/*.d.ts",
278
292
  "dist/*.js",
279
293
  "desktop",
294
+ "vendor",
280
295
  "Cargo.toml",
281
296
  "Cargo.lock",
282
297
  "README.md",
@@ -0,0 +1,418 @@
1
+ use crate::{textures::TextureOptions, Color32};
2
+ use std::sync::Arc;
3
+
4
+ /// An image stored in RAM.
5
+ ///
6
+ /// To load an image file, see [`ColorImage::from_rgba_unmultiplied`].
7
+ ///
8
+ /// In order to paint the image on screen, you first need to convert it to
9
+ ///
10
+ /// See also: [`ColorImage`], [`FontImage`].
11
+ #[derive(Clone, PartialEq)]
12
+ #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
13
+ pub enum ImageData {
14
+ /// RGBA image.
15
+ Color(Arc<ColorImage>),
16
+
17
+ /// Used for the font texture.
18
+ Font(FontImage),
19
+ }
20
+
21
+ impl ImageData {
22
+ pub fn size(&self) -> [usize; 2] {
23
+ match self {
24
+ Self::Color(image) => image.size,
25
+ Self::Font(image) => image.size,
26
+ }
27
+ }
28
+
29
+ pub fn width(&self) -> usize {
30
+ self.size()[0]
31
+ }
32
+
33
+ pub fn height(&self) -> usize {
34
+ self.size()[1]
35
+ }
36
+
37
+ pub fn bytes_per_pixel(&self) -> usize {
38
+ match self {
39
+ Self::Color(_) | Self::Font(_) => 4,
40
+ }
41
+ }
42
+ }
43
+
44
+ // ----------------------------------------------------------------------------
45
+
46
+ /// A 2D RGBA color image in RAM.
47
+ #[derive(Clone, Default, PartialEq, Eq)]
48
+ #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
49
+ pub struct ColorImage {
50
+ /// width, height.
51
+ pub size: [usize; 2],
52
+
53
+ /// The pixels, row by row, from top to bottom.
54
+ pub pixels: Vec<Color32>,
55
+ }
56
+
57
+ impl ColorImage {
58
+ /// Create an image filled with the given color.
59
+ pub fn new(size: [usize; 2], color: Color32) -> Self {
60
+ Self {
61
+ size,
62
+ pixels: vec![color; size[0] * size[1]],
63
+ }
64
+ }
65
+
66
+ /// Create a [`ColorImage`] from flat un-multiplied RGBA data.
67
+ ///
68
+ /// This is usually what you want to use after having loaded an image file.
69
+ ///
70
+ /// Panics if `size[0] * size[1] * 4 != rgba.len()`.
71
+ ///
72
+ /// ## Example using the [`image`](crates.io/crates/image) crate:
73
+ /// ``` ignore
74
+ /// fn load_image_from_path(path: &std::path::Path) -> Result<egui::ColorImage, image::ImageError> {
75
+ /// let image = image::io::Reader::open(path)?.decode()?;
76
+ /// let size = [image.width() as _, image.height() as _];
77
+ /// let image_buffer = image.to_rgba8();
78
+ /// let pixels = image_buffer.as_flat_samples();
79
+ /// Ok(egui::ColorImage::from_rgba_unmultiplied(
80
+ /// size,
81
+ /// pixels.as_slice(),
82
+ /// ))
83
+ /// }
84
+ ///
85
+ /// fn load_image_from_memory(image_data: &[u8]) -> Result<ColorImage, image::ImageError> {
86
+ /// let image = image::load_from_memory(image_data)?;
87
+ /// let size = [image.width() as _, image.height() as _];
88
+ /// let image_buffer = image.to_rgba8();
89
+ /// let pixels = image_buffer.as_flat_samples();
90
+ /// Ok(ColorImage::from_rgba_unmultiplied(
91
+ /// size,
92
+ /// pixels.as_slice(),
93
+ /// ))
94
+ /// }
95
+ /// ```
96
+ pub fn from_rgba_unmultiplied(size: [usize; 2], rgba: &[u8]) -> Self {
97
+ assert_eq!(size[0] * size[1] * 4, rgba.len());
98
+ let pixels = rgba
99
+ .chunks_exact(4)
100
+ .map(|p| Color32::from_rgba_unmultiplied(p[0], p[1], p[2], p[3]))
101
+ .collect();
102
+ Self { size, pixels }
103
+ }
104
+
105
+ pub fn from_rgba_premultiplied(size: [usize; 2], rgba: &[u8]) -> Self {
106
+ assert_eq!(size[0] * size[1] * 4, rgba.len());
107
+ let pixels = rgba
108
+ .chunks_exact(4)
109
+ .map(|p| Color32::from_rgba_premultiplied(p[0], p[1], p[2], p[3]))
110
+ .collect();
111
+ Self { size, pixels }
112
+ }
113
+
114
+ /// Create a [`ColorImage`] from flat opaque gray data.
115
+ ///
116
+ /// Panics if `size[0] * size[1] != gray.len()`.
117
+ pub fn from_gray(size: [usize; 2], gray: &[u8]) -> Self {
118
+ assert_eq!(size[0] * size[1], gray.len());
119
+ let pixels = gray.iter().map(|p| Color32::from_gray(*p)).collect();
120
+ Self { size, pixels }
121
+ }
122
+
123
+ /// Alternative method to `from_gray`.
124
+ /// Create a [`ColorImage`] from iterator over flat opaque gray data.
125
+ ///
126
+ /// Panics if `size[0] * size[1] != gray_iter.len()`.
127
+ #[doc(alias = "from_grey_iter")]
128
+ pub fn from_gray_iter(size: [usize; 2], gray_iter: impl Iterator<Item = u8>) -> Self {
129
+ let pixels: Vec<_> = gray_iter.map(Color32::from_gray).collect();
130
+ assert_eq!(size[0] * size[1], pixels.len());
131
+ Self { size, pixels }
132
+ }
133
+
134
+ /// A view of the underlying data as `&[u8]`
135
+ #[cfg(feature = "bytemuck")]
136
+ pub fn as_raw(&self) -> &[u8] {
137
+ bytemuck::cast_slice(&self.pixels)
138
+ }
139
+
140
+ /// A view of the underlying data as `&mut [u8]`
141
+ #[cfg(feature = "bytemuck")]
142
+ pub fn as_raw_mut(&mut self) -> &mut [u8] {
143
+ bytemuck::cast_slice_mut(&mut self.pixels)
144
+ }
145
+
146
+ /// Create a [`ColorImage`] from flat RGB data.
147
+ ///
148
+ /// This is what you want to use after having loaded an image file (and if
149
+ /// you are ignoring the alpha channel - considering it to always be 0xff)
150
+ ///
151
+ /// Panics if `size[0] * size[1] * 3 != rgb.len()`.
152
+ pub fn from_rgb(size: [usize; 2], rgb: &[u8]) -> Self {
153
+ assert_eq!(size[0] * size[1] * 3, rgb.len());
154
+ let pixels = rgb
155
+ .chunks_exact(3)
156
+ .map(|p| Color32::from_rgb(p[0], p[1], p[2]))
157
+ .collect();
158
+ Self { size, pixels }
159
+ }
160
+
161
+ /// An example color image, useful for tests.
162
+ pub fn example() -> Self {
163
+ let width = 128;
164
+ let height = 64;
165
+ let mut img = Self::new([width, height], Color32::TRANSPARENT);
166
+ for y in 0..height {
167
+ for x in 0..width {
168
+ let h = x as f32 / width as f32;
169
+ let s = 1.0;
170
+ let v = 1.0;
171
+ let a = y as f32 / height as f32;
172
+ img[(x, y)] = crate::Hsva { h, s, v, a }.into();
173
+ }
174
+ }
175
+ img
176
+ }
177
+
178
+ #[inline]
179
+ pub fn width(&self) -> usize {
180
+ self.size[0]
181
+ }
182
+
183
+ #[inline]
184
+ pub fn height(&self) -> usize {
185
+ self.size[1]
186
+ }
187
+
188
+ /// Create a new image from a patch of the current image.
189
+ ///
190
+ /// This method is especially convenient for screenshotting a part of the app
191
+ /// since `region` can be interpreted as screen coordinates of the entire screenshot if `pixels_per_point` is provided for the native application.
192
+ /// The floats of [`emath::Rect`] are cast to usize, rounding them down in order to interpret them as indices to the image data.
193
+ ///
194
+ /// Panics if `region.min.x > region.max.x || region.min.y > region.max.y`, or if a region larger than the image is passed.
195
+ pub fn region(&self, region: &emath::Rect, pixels_per_point: Option<f32>) -> Self {
196
+ let pixels_per_point = pixels_per_point.unwrap_or(1.0);
197
+ let min_x = (region.min.x * pixels_per_point) as usize;
198
+ let max_x = (region.max.x * pixels_per_point) as usize;
199
+ let min_y = (region.min.y * pixels_per_point) as usize;
200
+ let max_y = (region.max.y * pixels_per_point) as usize;
201
+ assert!(
202
+ min_x <= max_x && min_y <= max_y,
203
+ "Screenshot region is invalid: {region:?}"
204
+ );
205
+ let width = max_x - min_x;
206
+ let height = max_y - min_y;
207
+ let mut output = Vec::with_capacity(width * height);
208
+ let row_stride = self.size[0];
209
+
210
+ for row in min_y..max_y {
211
+ output.extend_from_slice(
212
+ &self.pixels[row * row_stride + min_x..row * row_stride + max_x],
213
+ );
214
+ }
215
+ Self {
216
+ size: [width, height],
217
+ pixels: output,
218
+ }
219
+ }
220
+ }
221
+
222
+ impl std::ops::Index<(usize, usize)> for ColorImage {
223
+ type Output = Color32;
224
+
225
+ #[inline]
226
+ fn index(&self, (x, y): (usize, usize)) -> &Color32 {
227
+ let [w, h] = self.size;
228
+ assert!(x < w && y < h);
229
+ &self.pixels[y * w + x]
230
+ }
231
+ }
232
+
233
+ impl std::ops::IndexMut<(usize, usize)> for ColorImage {
234
+ #[inline]
235
+ fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut Color32 {
236
+ let [w, h] = self.size;
237
+ assert!(x < w && y < h);
238
+ &mut self.pixels[y * w + x]
239
+ }
240
+ }
241
+
242
+ impl From<ColorImage> for ImageData {
243
+ #[inline(always)]
244
+ fn from(image: ColorImage) -> Self {
245
+ Self::Color(Arc::new(image))
246
+ }
247
+ }
248
+
249
+ impl From<Arc<ColorImage>> for ImageData {
250
+ #[inline]
251
+ fn from(image: Arc<ColorImage>) -> Self {
252
+ Self::Color(image)
253
+ }
254
+ }
255
+
256
+ impl std::fmt::Debug for ColorImage {
257
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
258
+ f.debug_struct("ColorImage")
259
+ .field("size", &self.size)
260
+ .field("pixel-count", &self.pixels.len())
261
+ .finish_non_exhaustive()
262
+ }
263
+ }
264
+
265
+ // ----------------------------------------------------------------------------
266
+
267
+ /// A single-channel image designed for the font texture.
268
+ ///
269
+ /// Each value represents "coverage", i.e. how much a texel is covered by a character.
270
+ ///
271
+ /// This is roughly interpreted as the opacity of a white image.
272
+ #[derive(Clone, Default, PartialEq)]
273
+ #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
274
+ pub struct FontImage {
275
+ /// width, height
276
+ pub size: [usize; 2],
277
+
278
+ /// The coverage value.
279
+ ///
280
+ /// Often you want to use [`Self::srgba_pixels`] instead.
281
+ pub pixels: Vec<f32>,
282
+ }
283
+
284
+ impl FontImage {
285
+ pub fn new(size: [usize; 2]) -> Self {
286
+ Self {
287
+ size,
288
+ pixels: vec![0.0; size[0] * size[1]],
289
+ }
290
+ }
291
+
292
+ #[inline]
293
+ pub fn width(&self) -> usize {
294
+ self.size[0]
295
+ }
296
+
297
+ #[inline]
298
+ pub fn height(&self) -> usize {
299
+ self.size[1]
300
+ }
301
+
302
+ /// Returns the textures as `sRGBA` premultiplied pixels, row by row, top to bottom.
303
+ ///
304
+ /// `gamma` should normally be set to `None`.
305
+ ///
306
+ /// If you are having problems with text looking skinny and pixelated, try using a low gamma, e.g. `0.4`.
307
+ #[inline]
308
+ pub fn srgba_pixels(&self, gamma: Option<f32>) -> impl ExactSizeIterator<Item = Color32> + '_ {
309
+ // Tuned slightly lower than upstream's 0.55 default so text coverage lands a bit closer
310
+ // to browser-rendered output in Elit's desktop-native parity checks.
311
+ let gamma = gamma.unwrap_or(0.50);
312
+ self.pixels.iter().map(move |coverage| {
313
+ let alpha = coverage.powf(gamma);
314
+ // We want to multiply with `vec4(alpha)` in the fragment shader:
315
+ let a = fast_round(alpha * 255.0);
316
+ Color32::from_rgba_premultiplied(a, a, a, a)
317
+ })
318
+ }
319
+
320
+ /// Clone a sub-region as a new image.
321
+ pub fn region(&self, [x, y]: [usize; 2], [w, h]: [usize; 2]) -> Self {
322
+ assert!(x + w <= self.width());
323
+ assert!(y + h <= self.height());
324
+
325
+ let mut pixels = Vec::with_capacity(w * h);
326
+ for y in y..y + h {
327
+ let offset = y * self.width() + x;
328
+ pixels.extend(&self.pixels[offset..(offset + w)]);
329
+ }
330
+ assert_eq!(pixels.len(), w * h);
331
+ Self {
332
+ size: [w, h],
333
+ pixels,
334
+ }
335
+ }
336
+ }
337
+
338
+ impl std::ops::Index<(usize, usize)> for FontImage {
339
+ type Output = f32;
340
+
341
+ #[inline]
342
+ fn index(&self, (x, y): (usize, usize)) -> &f32 {
343
+ let [w, h] = self.size;
344
+ assert!(x < w && y < h);
345
+ &self.pixels[y * w + x]
346
+ }
347
+ }
348
+
349
+ impl std::ops::IndexMut<(usize, usize)> for FontImage {
350
+ #[inline]
351
+ fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut f32 {
352
+ let [w, h] = self.size;
353
+ assert!(x < w && y < h);
354
+ &mut self.pixels[y * w + x]
355
+ }
356
+ }
357
+
358
+ impl From<FontImage> for ImageData {
359
+ #[inline(always)]
360
+ fn from(image: FontImage) -> Self {
361
+ Self::Font(image)
362
+ }
363
+ }
364
+
365
+ #[inline]
366
+ fn fast_round(r: f32) -> u8 {
367
+ (r + 0.5) as _ // rust does a saturating cast since 1.45
368
+ }
369
+
370
+ // ----------------------------------------------------------------------------
371
+
372
+ /// A change to an image.
373
+ ///
374
+ /// Either a whole new image, or an update to a rectangular region of it.
375
+ #[derive(Clone, PartialEq)]
376
+ #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
377
+ #[must_use = "The painter must take care of this"]
378
+ pub struct ImageDelta {
379
+ /// What to set the texture to.
380
+ ///
381
+ /// If [`Self::pos`] is `None`, this describes the whole texture.
382
+ ///
383
+ /// If [`Self::pos`] is `Some`, this describes a patch of the whole image starting at [`Self::pos`].
384
+ pub image: ImageData,
385
+
386
+ pub options: TextureOptions,
387
+
388
+ /// If `None`, set the whole texture to [`Self::image`].
389
+ ///
390
+ /// If `Some(pos)`, update a sub-region of an already allocated texture with the patch in [`Self::image`].
391
+ pub pos: Option<[usize; 2]>,
392
+ }
393
+
394
+ impl ImageDelta {
395
+ /// Update the whole texture.
396
+ pub fn full(image: impl Into<ImageData>, options: TextureOptions) -> Self {
397
+ Self {
398
+ image: image.into(),
399
+ options,
400
+ pos: None,
401
+ }
402
+ }
403
+
404
+ /// Update a sub-region of an existing texture.
405
+ pub fn partial(pos: [usize; 2], image: impl Into<ImageData>, options: TextureOptions) -> Self {
406
+ Self {
407
+ image: image.into(),
408
+ options,
409
+ pos: Some(pos),
410
+ }
411
+ }
412
+
413
+ /// Is this affecting the whole texture?
414
+ /// If `false`, this is a partial (sub-region) update.
415
+ pub fn is_whole(&self) -> bool {
416
+ self.pos.is_none()
417
+ }
418
+ }