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.
- package/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/README.md +6 -0
- package/dist/build.cjs +421 -331
- package/dist/build.d.ts +1 -16
- package/dist/build.js +420 -330
- package/dist/build.mjs +420 -330
- package/dist/chokidar.cjs +219 -182
- package/dist/chokidar.d.ts +25 -10
- package/dist/chokidar.js +217 -182
- package/dist/chokidar.mjs +218 -183
- package/dist/cli.cjs +21608 -20241
- package/dist/cli.d.ts +19 -37
- package/dist/cli.mjs +21262 -19910
- package/dist/config.cjs +357 -350
- package/dist/config.d.ts +19 -240
- package/dist/config.js +520 -515
- package/dist/config.mjs +346 -341
- package/dist/contracts-BeW9k0yZ.d.ts +54 -0
- package/dist/contracts-D7KIS-TK.d.ts +36 -0
- package/dist/coverage.cjs +448 -485
- package/dist/coverage.d.ts +13 -59
- package/dist/coverage.js +447 -484
- package/dist/coverage.mjs +447 -484
- package/dist/database.cjs +819 -828
- package/dist/database.d.ts +8 -24
- package/dist/database.js +818 -829
- package/dist/database.mjs +818 -829
- package/dist/desktop-auto-render.cjs +1700 -1522
- package/dist/desktop-auto-render.d.ts +4 -9
- package/dist/desktop-auto-render.js +1695 -1517
- package/dist/desktop-auto-render.mjs +1696 -1518
- package/dist/desktop.cjs +3 -1
- package/dist/desktop.d.ts +4 -1
- package/dist/desktop.js +1 -1
- package/dist/desktop.mjs +1 -1
- package/dist/dev-build.cjs +830 -0
- package/dist/dev-build.d.ts +53 -0
- package/dist/dev-build.js +3318 -0
- package/dist/dev-build.mjs +797 -0
- package/dist/dom.cjs +717 -590
- package/dist/dom.d.ts +2 -15
- package/dist/dom.js +714 -587
- package/dist/dom.mjs +716 -589
- package/dist/el.cjs +62 -52
- package/dist/el.d.ts +5 -10
- package/dist/el.js +60 -52
- package/dist/el.mjs +60 -52
- package/dist/fs.cjs +72 -63
- package/dist/fs.d.ts +22 -19
- package/dist/fs.js +71 -62
- package/dist/fs.mjs +71 -62
- package/dist/hmr.cjs +40 -14
- package/dist/hmr.d.ts +11 -23
- package/dist/hmr.js +38 -14
- package/dist/hmr.mjs +38 -14
- package/dist/http.cjs +251 -99
- package/dist/http.d.ts +38 -104
- package/dist/http.js +249 -99
- package/dist/http.mjs +249 -99
- package/dist/https.cjs +524 -228
- package/dist/https.d.ts +44 -36
- package/dist/https.js +520 -226
- package/dist/https.mjs +522 -228
- package/dist/index.cjs +7502 -7690
- package/dist/index.d.ts +8 -3
- package/dist/index.js +7486 -7676
- package/dist/index.mjs +7497 -7686
- package/dist/mime-types.cjs +10 -4
- package/dist/mime-types.d.ts +8 -11
- package/dist/mime-types.js +9 -3
- package/dist/mime-types.mjs +9 -3
- package/dist/native.cjs +8616 -8869
- package/dist/native.d.ts +7 -8
- package/dist/native.js +8682 -8935
- package/dist/native.mjs +8615 -8868
- package/dist/path.cjs +83 -77
- package/dist/path.d.ts +29 -29
- package/dist/path.js +82 -76
- package/dist/path.mjs +82 -76
- package/dist/pm.cjs +3300 -0
- package/dist/pm.d.ts +256 -0
- package/dist/pm.js +5638 -0
- package/dist/pm.mjs +3196 -0
- package/dist/preview-build.cjs +712 -0
- package/dist/preview-build.d.ts +59 -0
- package/dist/preview-build.js +3194 -0
- package/dist/preview-build.mjs +676 -0
- package/dist/render-context.cjs +13 -2
- package/dist/render-context.d.ts +9 -31
- package/dist/render-context.js +11 -2
- package/dist/render-context.mjs +11 -2
- package/dist/router.cjs +787 -645
- package/dist/router.d.ts +8 -12
- package/dist/router.js +786 -644
- package/dist/router.mjs +786 -644
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.js +1 -1
- package/dist/runtime.mjs +1 -1
- package/dist/server.cjs +3315 -2603
- package/dist/server.d.ts +49 -4
- package/dist/server.js +7611 -2834
- package/dist/server.mjs +3317 -2607
- package/dist/smtp-server.cjs +128 -0
- package/dist/smtp-server.d.ts +27 -0
- package/dist/smtp-server.js +4199 -0
- package/dist/smtp-server.mjs +100 -0
- package/dist/state-DvEkDehk.d.ts +195 -0
- package/dist/state.cjs +768 -658
- package/dist/state.d.ts +11 -69
- package/dist/state.js +760 -650
- package/dist/state.mjs +767 -657
- package/dist/style.cjs +1011 -968
- package/dist/style.d.ts +13 -127
- package/dist/style.js +1009 -970
- package/dist/style.mjs +1011 -971
- package/dist/test-reporter.cjs +332 -316
- package/dist/test-reporter.d.ts +28 -33
- package/dist/test-reporter.js +328 -312
- package/dist/test-reporter.mjs +328 -312
- package/dist/test-runtime.cjs +927 -968
- package/dist/test-runtime.d.ts +24 -99
- package/dist/test-runtime.js +922 -965
- package/dist/test-runtime.mjs +922 -965
- package/dist/test.cjs +4428 -4273
- package/dist/test.d.ts +2 -8
- package/dist/test.js +4307 -4154
- package/dist/test.mjs +4419 -4267
- package/dist/types-BONVzPtp.d.ts +59 -0
- package/dist/types-BR4wMiVx.d.ts +32 -0
- package/dist/types-C4gKykuG.d.ts +23 -0
- package/dist/types-CIhpN1-K.d.ts +64 -0
- package/dist/types-Ckj8md_j.d.ts +84 -0
- package/dist/types-CpjQTAkX.d.ts +24 -0
- package/dist/types-D0LjrYjS.d.ts +14 -0
- package/dist/types-DAisuVr5.d.ts +75 -0
- package/dist/types-tJn88E1N.d.ts +242 -0
- package/dist/types.d.ts +71 -226
- package/dist/universal.cjs +1 -1
- package/dist/universal.d.ts +1 -5
- package/dist/universal.js +1 -1
- package/dist/universal.mjs +1 -1
- package/dist/websocket-XfyK23zD.d.ts +119 -0
- package/dist/ws.cjs +129 -108
- package/dist/ws.d.ts +21 -131
- package/dist/ws.js +128 -109
- package/dist/ws.mjs +128 -109
- package/dist/wss.cjs +757 -479
- package/dist/wss.d.ts +31 -28
- package/dist/wss.js +755 -479
- package/dist/wss.mjs +758 -482
- package/package.json +16 -1
- package/vendor/epaint-0.31.1/src/image.rs +418 -0
- 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.
|
|
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
|
+
}
|