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}