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