manganis_core/images.rs
1use const_serialize::SerializeConst;
2
3use crate::AssetOptions;
4
5/// The type of an image. You can read more about the tradeoffs between image formats [here](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types)
6#[derive(
7 Debug,
8 PartialEq,
9 PartialOrd,
10 Clone,
11 Copy,
12 Hash,
13 SerializeConst,
14 serde::Serialize,
15 serde::Deserialize,
16)]
17#[repr(u8)]
18pub enum ImageFormat {
19 /// A png image. Png images cannot contain transparency and tend to compress worse than other formats
20 Png,
21 /// A jpg image. Jpg images can contain transparency and tend to compress better than png images
22 Jpg,
23 /// A webp image. Webp images can contain transparency and tend to compress better than jpg images
24 Webp,
25 /// An avif image. Avif images can compress slightly better than webp images but are not supported by all browsers
26 Avif,
27 /// An unknown image type
28 Unknown,
29}
30
31/// The size of an image asset
32#[derive(
33 Debug,
34 PartialEq,
35 PartialOrd,
36 Clone,
37 Copy,
38 Hash,
39 SerializeConst,
40 serde::Serialize,
41 serde::Deserialize,
42)]
43#[repr(C, u8)]
44pub enum ImageSize {
45 /// A manual size in pixels
46 Manual {
47 /// The width of the image in pixels
48 width: u32,
49 /// The height of the image in pixels
50 height: u32,
51 },
52 /// The size will be automatically determined from the image source
53 Automatic,
54}
55
56/// Options for an image asset
57#[derive(
58 Debug,
59 PartialEq,
60 PartialOrd,
61 Clone,
62 Copy,
63 Hash,
64 SerializeConst,
65 serde::Serialize,
66 serde::Deserialize,
67)]
68pub struct ImageAssetOptions {
69 ty: ImageFormat,
70 low_quality_preview: bool,
71 size: ImageSize,
72 preload: bool,
73}
74
75impl Default for ImageAssetOptions {
76 fn default() -> Self {
77 Self::new()
78 }
79}
80
81impl ImageAssetOptions {
82 /// Create a new image asset options
83 pub const fn new() -> Self {
84 Self {
85 ty: ImageFormat::Unknown,
86 low_quality_preview: false,
87 size: ImageSize::Automatic,
88 preload: false,
89 }
90 }
91
92 /// Make the asset preloaded
93 ///
94 /// Preloading an image will make the image start to load as soon as possible. This is useful for images that will be displayed soon after the page loads or images that may not be visible immediately, but should start loading sooner
95 ///
96 /// ```rust
97 /// # use manganis::{asset, Asset, ImageAssetOptions};
98 /// const _: Asset = asset!("/assets/image.png", ImageAssetOptions::new().with_preload(true));
99 /// ```
100 pub const fn with_preload(self, preload: bool) -> Self {
101 Self { preload, ..self }
102 }
103
104 /// Check if the asset is preloaded
105 pub const fn preloaded(&self) -> bool {
106 self.preload
107 }
108
109 /// Sets the format of the image
110 ///
111 /// Choosing the right format can make your site load much faster. Webp and avif images tend to be a good default for most images
112 ///
113 /// ```rust
114 /// # use manganis::{asset, Asset, ImageAssetOptions, ImageFormat};
115 /// const _: Asset = asset!("/assets/image.png", ImageAssetOptions::new().with_format(ImageFormat::Webp));
116 /// ```
117 pub const fn with_format(self, format: ImageFormat) -> Self {
118 Self { ty: format, ..self }
119 }
120
121 /// Sets the format of the image to [`ImageFormat::Avif`]
122 ///
123 /// Avif images tend to be a good default for most images rendered in browser because
124 /// they compress images well
125 ///
126 /// ```rust
127 /// # use manganis::{asset, Asset, ImageAssetOptions, ImageFormat};
128 /// const _: Asset = asset!("/assets/image.png", ImageAssetOptions::new().with_avif());
129 /// ```
130 pub const fn with_avif(self) -> Self {
131 self.with_format(ImageFormat::Avif)
132 }
133
134 /// Sets the format of the image to [`ImageFormat::Webp`]
135 ///
136 /// Webp images tend to be a good default for most images rendered in browser because
137 /// they compress images well
138 ///
139 /// ```rust
140 /// # use manganis::{asset, Asset, ImageAssetOptions, ImageFormat};
141 /// const _: Asset = asset!("/assets/image.png", ImageAssetOptions::new().with_webp());
142 /// ```
143 pub const fn with_webp(self) -> Self {
144 self.with_format(ImageFormat::Webp)
145 }
146
147 /// Sets the format of the image to [`ImageFormat::Jpg`]
148 ///
149 /// Jpeg images compress much better than [`ImageFormat::Png`], but worse than [`ImageFormat::Webp`] or [`ImageFormat::Avif`]
150 ///
151 /// ```rust
152 /// # use manganis::{asset, Asset, ImageAssetOptions, ImageFormat};
153 /// const _: Asset = asset!("/assets/image.png", ImageAssetOptions::new().with_jpg());
154 /// ```
155 pub const fn with_jpg(self) -> Self {
156 self.with_format(ImageFormat::Jpg)
157 }
158
159 /// Sets the format of the image to [`ImageFormat::Png`]
160 ///
161 /// Png images don't compress very well, so they are not recommended for large images
162 ///
163 /// ```rust
164 /// # use manganis::{asset, Asset, ImageAssetOptions, ImageFormat};
165 /// const _: Asset = asset!("/assets/image.png", ImageAssetOptions::new().with_png());
166 /// ```
167 pub const fn with_png(self) -> Self {
168 self.with_format(ImageFormat::Png)
169 }
170
171 /// Get the format of the image
172 pub const fn format(&self) -> ImageFormat {
173 self.ty
174 }
175
176 /// Sets the size of the image
177 ///
178 /// If you only use the image in one place, you can set the size of the image to the size it will be displayed at. This will make the image load faster
179 ///
180 /// ```rust
181 /// # use manganis::{asset, Asset, ImageAssetOptions, ImageSize};
182 /// const _: Asset = asset!("/assets/image.png", ImageAssetOptions::new().with_size(ImageSize::Manual { width: 512, height: 512 }));
183 /// ```
184 pub const fn with_size(self, size: ImageSize) -> Self {
185 Self { size, ..self }
186 }
187
188 /// Get the size of the image
189 pub const fn size(&self) -> ImageSize {
190 self.size
191 }
192
193 // LQIP is currently disabled until we have the CLI set up to inject the low quality image preview after the crate is built through the linker
194 // /// Make the image use a low quality preview
195 // ///
196 // /// A low quality preview is a small version of the image that will load faster. This is useful for large images on mobile devices that may take longer to load
197 // ///
198 // /// ```rust
199 // /// # use manganis::{asset, Asset, ImageAssetOptions};
200 // /// const _: Asset = manganis::asset!("/assets/image.png", ImageAssetOptions::new().with_low_quality_image_preview());
201 // /// ```
202 //
203 // pub const fn with_low_quality_image_preview(self, low_quality_preview: bool) -> Self {
204 // Self {
205 // low_quality_preview,
206 // ..self
207 // }
208 // }
209
210 /// Convert the options into options for a generic asset
211 pub const fn into_asset_options(self) -> AssetOptions {
212 AssetOptions::Image(self)
213 }
214
215 pub(crate) const fn extension(&self) -> Option<&'static str> {
216 match self.ty {
217 ImageFormat::Png => Some("png"),
218 ImageFormat::Jpg => Some("jpg"),
219 ImageFormat::Webp => Some("webp"),
220 ImageFormat::Avif => Some("avif"),
221 ImageFormat::Unknown => None,
222 }
223 }
224}