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