1use std::any::Any;
5use std::sync::Arc;
6
7#[cfg(feature = "jxl")]
8use jxl_oxide::{EnumColourEncoding, JxlImage};
9#[cfg(feature = "svg")]
10use resvg::{tiny_skia, usvg};
11
12use crate::render::atlas;
13use crate::{Error, Pixel, PxDim};
14use guillotiere::euclid::Size2D;
15use std::hash::Hash;
16
17pub(crate) const MIN_AREA: i32 = 64 * 64;
18pub(crate) const MAX_VARIANCE: f32 = 0.1;
19
20pub(crate) fn within_variance(l: i32, r: i32, range: f32) -> bool {
21 let diff = l as f32 / r as f32;
22 diff > (1.0 - range) && diff < (1.0 + range)
23}
24
25#[inline]
29pub fn fill_size(size: atlas::Size, native: atlas::Size) -> atlas::Size {
30 match (size.width, size.height) {
31 (0, 0) => native,
32 (x, 0) => atlas::Size::new(
33 x,
34 (native.height as f32 * (x as f32 / native.width as f32)).round() as i32,
35 ),
36 (0, y) => atlas::Size::new(
37 (native.width as f32 * (y as f32 / native.height as f32)).round() as i32,
38 y,
39 ),
40 _ => size,
41 }
42}
43
44#[inline]
45pub fn fill_dim(size: PxDim, native: PxDim) -> PxDim {
46 match (size.width, size.height) {
47 (0.0, 0.0) => native,
48 (x, 0.0) => PxDim::new(x, native.height * (x / native.width)),
49 (0.0, y) => PxDim::new(native.width * (y / native.height), y),
50 _ => size,
51 }
52}
53
54pub trait Location: crate::DynHashEq + Any + Send + Sync {
55 fn fetch(&self) -> Result<Box<dyn Loader>, Error>;
56}
57
58dyn_clone::clone_trait_object!(Location);
59
60pub trait Loader: std::fmt::Debug + Send + Sync {
61 fn preload(&self, size: atlas::Size, dpi: f32) -> Result<(Vec<u8>, Size2D<u32, Pixel>), Error>;
64
65 fn load(
68 &self,
69 driver: &crate::graphics::Driver,
70 data: (Vec<u8>, Size2D<u32, Pixel>),
71 resize: bool,
72 ) -> Result<(atlas::Region, atlas::Size), Error>;
73}
74
75impl Hash for dyn Location {
76 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
77 self.dyn_hash(state);
78 }
79}
80
81impl PartialEq for dyn Location {
82 fn eq(&self, other: &Self) -> bool {
83 self.dyn_eq(other as &dyn Any)
84 }
85}
86
87impl Eq for dyn Location {}
88
89#[cfg(feature = "svg")]
90#[derive(Debug)]
91struct SvgXml(String);
94
95impl Location for std::path::PathBuf {
96 fn fetch(&self) -> Result<Box<dyn Loader>, Error> {
97 #[cfg(feature = "svg")]
98 if let Some(extension) = self.extension() {
99 use std::fs::File;
100 use std::io::Read;
101
102 let ext = extension.to_str().unwrap().to_ascii_lowercase();
103 if &ext == "svgz" {
104 let mut buf = Vec::new();
105 {
106 let mut f = File::open(self)?;
107 f.read_to_end(&mut buf)?;
108 }
109
110 if buf.starts_with(&[0x1f, 0x8b]) {
111 buf = usvg::decompress_svgz(&buf)
112 .map_err(|e| Error::ResourceError(Box::new(e)))?;
113 }
114
115 return Ok(Box::new(SvgXml(
116 String::from_utf8(buf).map_err(|e| Error::ResourceError(Box::new(e)))?,
117 )));
118 } else if &ext == "svg" {
119 let mut buf = String::new();
120 File::open(self)?.read_to_string(&mut buf)?;
121 return Ok(Box::new(SvgXml(buf)));
122 }
123 }
124
125 #[cfg(feature = "svg")]
126 {
127 use std::fs::File;
128 use std::io::Read;
129
130 let mut f = File::open(self)?;
131 let mut header = [0_u8; 2];
132 if f.read(&mut header)? == 2 && header == [0x1f_u8, 0x8b_u8] {
133 let mut buf: Vec<u8> = header.into();
134 f.read_to_end(&mut buf)?;
135 buf = usvg::decompress_svgz(&buf).map_err(|e| Error::ResourceError(Box::new(e)))?;
136
137 return Ok(Box::new(SvgXml(
138 String::from_utf8(buf).map_err(|e| Error::ResourceError(Box::new(e)))?,
139 )));
140 }
141 }
142
143 #[cfg(feature = "jxl")]
144 if let Ok(mut img) = JxlImage::builder().open(self) {
145 img.request_color_encoding(EnumColourEncoding::srgb(
146 jxl_oxide::RenderingIntent::Relative,
147 ));
148 return Ok(Box::new(img));
149 }
150
151 #[cfg(any(
154 feature = "avif",
155 feature = "bmp",
156 feature = "dds",
157 feature = "exr",
158 feature = "ff",
159 feature = "gif",
160 feature = "hdr",
161 feature = "ico",
162 feature = "pnm",
163 feature = "qoi",
164 feature = "tga",
165 feature = "tiff",
166 feature = "webp"
167 ))]
168 {
169 let image = image::ImageReader::open(self)?.with_guessed_format()?;
170
171 match image.format() {
172 Some(image::ImageFormat::Png) | Some(image::ImageFormat::Jpeg) => (),
173 _ => {
174 return Ok(Box::new(
175 image
176 .decode()
177 .map_err(|e| Error::ResourceError(Box::new(e)))?,
178 ));
179 }
180 }
181 }
182
183 #[cfg(any(feature = "png", feature = "jpeg"))]
187 return Ok(Box::new(load_image::load_path(self).map_err(
188 |e| match e {
189 load_image::Error::UnsupportedFileFormat => Error::UnknownResourceFormat,
190 _ => Error::ResourceError(Box::new(e)),
191 },
192 )?));
193
194 #[allow(unreachable_code)]
195 Err(Error::UnknownResourceFormat)
196 }
197}
198
199#[cfg(feature = "jxl")]
200struct JxlFrameView<'a>(&'a jxl_oxide::FrameBuffer);
201
202#[cfg(feature = "jxl")]
203impl<'a> fast_image_resize::IntoImageView for JxlFrameView<'a> {
204 fn pixel_type(&self) -> Option<fast_image_resize::PixelType> {
205 use fast_image_resize::PixelType;
206 match self.0.channels() {
207 1 => Some(PixelType::F32),
208 2 => Some(PixelType::F32x2),
209 3 => Some(PixelType::F32x3),
210 4 => Some(PixelType::F32x4),
211 _ => None,
212 }
213 }
214
215 fn width(&self) -> u32 {
216 self.0.width() as u32
217 }
218
219 fn height(&self) -> u32 {
220 self.0.height() as u32
221 }
222
223 fn image_view<P: fast_image_resize::PixelTrait>(
224 &self,
225 ) -> Option<impl fast_image_resize::ImageView<Pixel = P>> {
226 if P::pixel_type() == self.pixel_type().unwrap() {
227 return fast_image_resize::images::TypedImageRef::<P>::from_buffer(
228 self.width(),
229 self.height(),
230 bytemuck::cast_slice(self.0.buf()),
231 )
232 .ok();
233 }
234 None
235 }
236}
237
238#[cfg(feature = "jxl")]
239fn gamma_into_linear(input: f32) -> f32 {
240 input.powf(2.2)
241}
242
243#[cfg(feature = "jxl")]
244fn linear_into_gamma(input: f32) -> f32 {
245 input.powf(1.0 / 2.2)
246}
247
248#[cfg(feature = "jxl")]
249fn in_place_map<const N: usize, const FORWARD: bool>(
250 buf: &mut [[f32; N]],
251) -> fast_image_resize::PixelType {
252 let gamma: fn(f32) -> f32 = if FORWARD {
253 gamma_into_linear
254 } else {
255 linear_into_gamma
256 };
257 let color = if FORWARD {
258 crate::color::srgb_to_linear
259 } else {
260 crate::color::linear_to_srgb
261 };
262
263 match N {
264 1 => {
265 for p in buf {
266 p[0] = gamma(p[0]);
267 }
268 fast_image_resize::PixelType::F32
269 }
270 2 => {
271 for p in buf {
272 p[0] = gamma(p[0]);
273 }
274 fast_image_resize::PixelType::F32x2
275 }
276 3 => {
277 for p in buf {
278 p[0] = color(p[0]);
279 p[1] = color(p[1]);
280 p[2] = color(p[2]);
281 }
282 fast_image_resize::PixelType::F32x3
283 }
284 4 => {
285 for p in buf {
286 p[0] = color(p[0]);
287 p[1] = color(p[1]);
288 p[2] = color(p[2]);
289 }
290 fast_image_resize::PixelType::F32x4
291 }
292 _ => unreachable!(),
293 }
294}
295
296#[cfg(feature = "jxl")]
297impl Loader for JxlImage {
298 fn preload(
299 &self,
300 mut size: atlas::Size,
301 _: f32,
302 ) -> Result<(Vec<u8>, Size2D<u32, Pixel>), Error> {
303 use crate::color::sRGB;
304 use wide::f32x4;
305
306 if self.num_loaded_keyframes() == 0 {
307 return Err(Error::ResourceError(Box::new(eyre::eyre!(
308 "JpegXL had no keyframes to load???"
309 ))));
310 }
311
312 size = fill_size(
313 size,
314 atlas::Size::new(self.width() as i32, self.height() as i32),
315 );
316
317 let render = self
318 .render_frame(0)
319 .map_err(|e| Error::ResourceError(Box::new(e)))?;
320 let mut frame = render.image_all_channels();
321
322 let force_native = within_variance(size.height, self.height() as i32, 0.05)
325 && within_variance(size.width, self.width() as i32, 0.05);
326 let (raw, w, h) = if !(force_native
327 || size.width as u32 > self.width() && size.height as u32 > self.height())
328 {
329 use fast_image_resize::PixelType;
330
331 let inner_format = match frame.channels() {
332 1 => in_place_map::<1, true>(frame.buf_grouped_mut::<1>()),
333 2 => in_place_map::<2, true>(frame.buf_grouped_mut::<2>()),
334 3 => in_place_map::<3, true>(frame.buf_grouped_mut::<3>()),
335 4 => in_place_map::<4, true>(frame.buf_grouped_mut::<4>()),
336 _ => {
337 return Err(Error::ResourceError(Box::new(eyre::eyre!(
338 "Channel count not 1-4"
339 ))));
340 }
341 };
342
343 let mut dst_image = fast_image_resize::images::Image::new(
344 size.width as u32,
345 size.height as u32,
346 inner_format,
347 );
348
349 fast_image_resize::Resizer::new()
350 .resize(
351 &JxlFrameView(&frame),
352 &mut dst_image,
353 &fast_image_resize::ResizeOptions::new()
354 .use_alpha(true)
355 .resize_alg(fast_image_resize::ResizeAlg::Convolution(
356 fast_image_resize::FilterType::CatmullRom,
357 )),
358 )
359 .map_err(|e| Error::ResourceError(Box::new(e)))?;
360
361 let mut output = unsafe {
362 vec![
363 std::mem::zeroed::<u8>();
364 (dst_image.width() * dst_image.height() * 4) as usize
365 ]
366 };
367
368 use fast_image_resize::pixels;
369
370 use crate::color::linear_to_srgb;
371 match dst_image.pixel_type() {
372 PixelType::F32 => process_load_pixels(
373 output.as_mut_slice(),
374 dst_image.typed_image::<pixels::F32>().unwrap().pixels(),
375 |p| sRGB {
376 rgba: f32x4::new([
377 linear_into_gamma(p.0),
378 linear_into_gamma(p.0),
379 linear_into_gamma(p.0),
380 1.0,
381 ]),
382 },
383 ),
384 PixelType::F32x2 => process_load_pixels(
385 output.as_mut_slice(),
386 dst_image.typed_image::<pixels::F32x2>().unwrap().pixels(),
387 |p| sRGB {
388 rgba: f32x4::new([
389 linear_into_gamma(p.0[0]),
390 linear_into_gamma(p.0[0]),
391 linear_into_gamma(p.0[0]),
392 p.0[1],
393 ]),
394 },
395 ),
396 PixelType::F32x3 => process_load_pixels(
397 output.as_mut_slice(),
398 dst_image.typed_image::<pixels::F32x3>().unwrap().pixels(),
399 |p| sRGB {
400 rgba: f32x4::new([
401 linear_to_srgb(p.0[0]),
402 linear_to_srgb(p.0[1]),
403 linear_to_srgb(p.0[2]),
404 1.0,
405 ]),
406 },
407 ),
408 PixelType::F32x4 => process_load_pixels(
409 output.as_mut_slice(),
410 dst_image.typed_image::<pixels::F32x4>().unwrap().pixels(),
411 |p| sRGB {
412 rgba: f32x4::new([
413 linear_to_srgb(p.0[0]),
414 linear_to_srgb(p.0[1]),
415 linear_to_srgb(p.0[2]),
416 p.0[3],
417 ]),
418 },
419 ),
420 _ => {
421 return Err(Error::ResourceError(Box::new(eyre::eyre!(
422 "Channel count not 1-4"
423 ))));
424 }
425 }
426
427 (output, dst_image.width(), dst_image.height())
428 } else {
429 let mut output =
430 unsafe { vec![std::mem::zeroed::<u8>(); frame.width() * frame.height() * 4] };
431
432 match frame.channels() {
433 1 => process_load_pixels(output.as_mut_slice(), frame.buf(), |p| sRGB {
434 rgba: f32x4::new([*p, *p, *p, 1.0]),
435 }),
436 2 => {
437 process_load_pixels(output.as_mut_slice(), frame.buf_grouped::<2>(), |p| sRGB {
438 rgba: f32x4::new([p[0], p[0], p[0], p[1]]),
439 })
440 }
441 3 => {
442 process_load_pixels(output.as_mut_slice(), frame.buf_grouped::<3>(), |p| sRGB {
443 rgba: f32x4::new([p[0], p[1], p[2], 1.0]),
444 })
445 }
446 4 => {
447 process_load_pixels(output.as_mut_slice(), frame.buf_grouped::<4>(), |p| sRGB {
448 rgba: f32x4::new(*p),
449 })
450 }
451 _ => {
452 return Err(Error::ResourceError(Box::new(eyre::eyre!(
453 "Channel count not 1-4"
454 ))));
455 }
456 }
457
458 (output, self.width() as u32, self.height() as u32)
459 };
460
461 Ok((raw, Size2D::new(w, h)))
462 }
463 fn load(
464 &self,
465 driver: &crate::graphics::Driver,
466 data: (Vec<u8>, Size2D<u32, Pixel>),
467 resize: bool,
468 ) -> Result<(atlas::Region, atlas::Size), Error> {
469 let region = driver.atlas.write().reserve(
470 &driver.device,
471 atlas::Size::new(data.1.width as i32, data.1.height as i32),
472 if resize { Some(&driver.queue) } else { None },
473 )?;
474
475 queue_atlas_data(
476 &data.0,
477 ®ion,
478 &driver.queue,
479 data.1.width,
480 data.1.height,
481 &driver.atlas.read(),
482 );
483
484 let native = atlas::Size::new(self.width() as i32, self.height() as i32);
485 Ok((region, native))
486 }
487}
488
489#[cfg(feature = "svg")]
490impl Loader for SvgXml {
491 fn preload(&self, size: atlas::Size, dpi: f32) -> Result<(Vec<u8>, Size2D<u32, Pixel>), Error> {
492 let xml_opt = usvg::roxmltree::ParsingOptions {
493 allow_dtd: true,
494 ..Default::default()
495 };
496 let xml_tree = usvg::roxmltree::Document::parse_with_options(&self.0, xml_opt)
497 .map_err(|e| Error::ResourceError(Box::new(e)))?;
498
499 let mut svg_opt = usvg::Options {
500 dpi,
501 font_size: 12.0, ..Default::default()
503 };
504
505 if let Some(sz) = tiny_skia::Size::from_wh(size.width as f32, size.height as f32) {
506 svg_opt.default_size = sz;
507 }
508
509 let svg = usvg::Tree::from_xmltree(&xml_tree, &svg_opt)
510 .map_err(|e| Error::ResourceError(Box::new(e)))
511 .map(Box::new)?;
512
513 let svg_size = svg.size();
516 let native_size = PxDim::new(svg_size.width(), svg_size.height());
517 let sizevec = fill_dim(
518 PxDim::new(size.height as f32, size.width as f32),
519 native_size,
520 );
521
522 let t = if sizevec == native_size {
523 tiny_skia::Transform::identity()
524 } else {
525 tiny_skia::Transform::from_scale(
526 sizevec.width / native_size.width,
527 sizevec.height / native_size.height,
528 )
529 };
530
531 let mut pixmap =
532 tiny_skia::Pixmap::new(sizevec.width.ceil() as u32, sizevec.height.ceil() as u32)
533 .unwrap();
534
535 resvg::render(&svg, t, &mut pixmap.as_mut());
536
537 for c in pixmap.data_mut().as_chunks_mut::<4>().0 {
539 c.swap(0, 2);
540 }
541
542 let sz = Size2D::new(pixmap.width(), pixmap.height());
543 Ok((pixmap.take(), sz))
544 }
545
546 fn load(
547 &self,
548 driver: &crate::graphics::Driver,
549 data: (Vec<u8>, Size2D<u32, Pixel>),
550 resize: bool,
551 ) -> Result<(atlas::Region, atlas::Size), Error> {
552 let size = atlas::Size::new(data.1.width as i32, data.1.height as i32);
553 let region = driver.atlas.write().reserve(
554 &driver.device,
555 size,
556 if resize { Some(&driver.queue) } else { None },
557 None,
558 )?;
559
560 driver.atlas.read().queue_data(
561 &data.0,
562 ®ion,
563 &driver.queue,
564 data.1.width,
565 data.1.height,
566 );
567
568 Ok((region, size))
569 }
570}
571
572#[cfg(any(feature = "png", feature = "jpeg"))]
573struct LoadImageView<'a>(&'a load_image::Image);
574
575#[cfg(any(feature = "png", feature = "jpeg"))]
576fn image_data_as_bytes(data: &load_image::ImageData) -> &[u8] {
577 match data {
578 load_image::ImageData::RGB8(rgbs) => bytemuck::cast_slice(rgbs.as_slice()),
579 load_image::ImageData::RGBA8(rgbas) => bytemuck::cast_slice(rgbas.as_slice()),
580 load_image::ImageData::RGB16(rgbs) => bytemuck::cast_slice(rgbs.as_slice()),
581 load_image::ImageData::RGBA16(rgbas) => bytemuck::cast_slice(rgbas.as_slice()),
582 load_image::ImageData::GRAY8(gray_v08s) => bytemuck::cast_slice(gray_v08s.as_slice()),
583 load_image::ImageData::GRAY16(gray_v08s) => bytemuck::cast_slice(gray_v08s.as_slice()),
584 load_image::ImageData::GRAYA8(gray_alpha_v08s) => {
585 bytemuck::cast_slice(gray_alpha_v08s.as_slice())
586 }
587 load_image::ImageData::GRAYA16(gray_alpha_v08s) => {
588 bytemuck::cast_slice(gray_alpha_v08s.as_slice())
589 }
590 }
591}
592
593#[cfg(any(feature = "png", feature = "jpeg"))]
594impl<'a> fast_image_resize::IntoImageView for LoadImageView<'a> {
595 fn pixel_type(&self) -> Option<fast_image_resize::PixelType> {
596 use fast_image_resize::PixelType;
597 use load_image::ImageData;
598 Some(match self.0.bitmap {
599 ImageData::RGB8(_) => PixelType::U8x3,
600 ImageData::RGBA8(_) => PixelType::U8x4,
601 ImageData::GRAY8(_) => PixelType::U8,
602 ImageData::GRAYA8(_) => PixelType::U8x2,
603 ImageData::RGB16(_) => PixelType::U16x3,
604 ImageData::RGBA16(_) => PixelType::U16x4,
605 ImageData::GRAY16(_) => PixelType::U16,
606 ImageData::GRAYA16(_) => PixelType::U16x2,
607 })
608 }
609
610 fn width(&self) -> u32 {
611 self.0.width as u32
612 }
613
614 fn height(&self) -> u32 {
615 self.0.height as u32
616 }
617
618 fn image_view<P: fast_image_resize::PixelTrait>(
619 &self,
620 ) -> Option<impl fast_image_resize::ImageView<Pixel = P>> {
621 if P::pixel_type() == self.pixel_type().unwrap() {
622 return fast_image_resize::images::TypedImageRef::<P>::from_buffer(
623 self.width(),
624 self.height(),
625 image_data_as_bytes(&self.0.bitmap),
626 )
627 .ok();
628 }
629 None
630 }
631}
632
633#[cfg(any(
634 feature = "avif",
635 feature = "bmp",
636 feature = "dds",
637 feature = "exr",
638 feature = "ff",
639 feature = "gif",
640 feature = "hdr",
641 feature = "ico",
642 feature = "pnm",
643 feature = "png",
644 feature = "jpeg",
645 feature = "qoi",
646 feature = "tga",
647 feature = "tiff",
648 feature = "webp"
649))]
650fn process_pixels<P: fast_image_resize::pixels::InnerPixel>(
651 output: &mut [u8],
652 dst_image: &fast_image_resize::images::Image<'_>,
653 convert: fn(p: &P) -> crate::color::sRGB,
654) {
655 use crate::color::Premultiplied;
656 for (p, c) in dst_image
657 .typed_image::<P>()
658 .unwrap()
659 .pixels()
660 .iter()
661 .zip(output.chunks_exact_mut(4))
662 {
663 c.copy_from_slice(&convert(p).srgb_pre().as_bgra())
664 }
665}
666
667fn process_load_pixels<T>(
668 output: &mut [u8],
669 source: &[T],
670 convert: fn(p: &T) -> crate::color::sRGB,
671) {
672 use crate::color::Premultiplied;
673 for (p, c) in source.iter().zip(output.chunks_exact_mut(4)) {
674 c.copy_from_slice(&convert(p).srgb_pre().as_bgra());
676 }
677}
678
679#[cfg(any(
680 feature = "avif",
681 feature = "bmp",
682 feature = "dds",
683 feature = "exr",
684 feature = "ff",
685 feature = "gif",
686 feature = "hdr",
687 feature = "ico",
688 feature = "pnm",
689 feature = "png",
690 feature = "jpeg",
691 feature = "qoi",
692 feature = "tga",
693 feature = "tiff",
694 feature = "webp"
695))]
696fn image_resize_loader(
697 inner_format: fast_image_resize::PixelType,
698 srgb_map: fast_image_resize::PixelComponentMapper,
699 source: &impl fast_image_resize::IntoImageView,
700 size: atlas::Size,
701) -> Result<(Vec<u8>, u32, u32), Error> {
702 use crate::color::sRGB64;
703 use fast_image_resize::PixelType;
704
705 let mut inner_src_image =
706 fast_image_resize::images::Image::new(source.width(), source.height(), inner_format);
707
708 srgb_map
709 .forward_map(source, &mut inner_src_image)
710 .map_err(|e| Error::ResourceError(Box::new(e)))?;
711
712 let mut dst_image =
713 fast_image_resize::images::Image::new(size.width as u32, size.height as u32, inner_format);
714
715 fast_image_resize::Resizer::new()
716 .resize(
717 &inner_src_image,
718 &mut dst_image,
719 &fast_image_resize::ResizeOptions::new()
720 .use_alpha(true)
721 .resize_alg(fast_image_resize::ResizeAlg::Convolution(
722 fast_image_resize::FilterType::CatmullRom,
723 )),
724 )
725 .map_err(|e| Error::ResourceError(Box::new(e)))?;
726
727 srgb_map
728 .backward_map_inplace(&mut dst_image)
729 .map_err(|e| Error::ResourceError(Box::new(e)))?;
730
731 let mut output = unsafe {
732 vec![std::mem::zeroed::<u8>(); (dst_image.width() * dst_image.height() * 4) as usize]
733 };
734
735 match inner_format {
736 PixelType::U16 => process_pixels::<fast_image_resize::pixels::U16>(
737 output.as_mut_slice(),
738 &dst_image,
739 |p| sRGB64::new(p.0, p.0, p.0, u16::MAX).as_f32(),
740 ),
741 PixelType::U16x2 => process_pixels::<fast_image_resize::pixels::U16x2>(
742 output.as_mut_slice(),
743 &dst_image,
744 |p| sRGB64::new(p.0[0], p.0[0], p.0[0], p.0[1]).as_f32(),
745 ),
746 PixelType::U16x3 => process_pixels::<fast_image_resize::pixels::U16x3>(
747 output.as_mut_slice(),
748 &dst_image,
749 |p| sRGB64::new(p.0[0], p.0[1], p.0[2], u16::MAX).as_f32(),
750 ),
751 PixelType::U16x4 => process_pixels::<fast_image_resize::pixels::U16x4>(
752 output.as_mut_slice(),
753 &dst_image,
754 |p| sRGB64::new(p.0[0], p.0[1], p.0[2], p.0[3]).as_f32(),
755 ),
756 _ => return Err(Error::InternalFailure),
757 }
758
759 Ok((output, dst_image.width(), dst_image.height()))
760}
761
762#[cfg(any(feature = "png", feature = "jpeg"))]
784impl Loader for load_image::Image {
785 fn preload(
786 &self,
787 mut size: atlas::Size,
788 _: f32,
789 ) -> Result<(Vec<u8>, Size2D<u32, Pixel>), Error> {
790 use crate::color::{sRGB32, sRGB64};
791
792 size = fill_size(
793 size,
794 atlas::Size::new(self.width as i32, self.height as i32),
795 );
796
797 let force_native = within_variance(size.height, self.height as i32, 0.05)
800 && within_variance(size.width, self.width as i32, 0.05);
801 let (raw, w, h) = if !(force_native
802 || size.width as usize > self.width && size.height as usize > self.height)
803 {
804 use load_image::ImageData;
805
806 let srgb_map = match self.bitmap {
807 ImageData::RGB8(_)
808 | ImageData::RGBA8(_)
809 | ImageData::RGB16(_)
810 | ImageData::RGBA16(_) => fast_image_resize::create_srgb_mapper(),
811 ImageData::GRAY8(_)
812 | ImageData::GRAY16(_)
813 | ImageData::GRAYA8(_)
814 | ImageData::GRAYA16(_) => fast_image_resize::create_gamma_22_mapper(),
815 };
816
817 let inner_format = match self.bitmap {
818 ImageData::RGB8(_) | ImageData::RGB16(_) => fast_image_resize::PixelType::U16x3,
819 ImageData::RGBA8(_) | ImageData::RGBA16(_) => fast_image_resize::PixelType::U16x4,
820 ImageData::GRAY8(_) | ImageData::GRAY16(_) => fast_image_resize::PixelType::U16,
821 ImageData::GRAYA8(_) | ImageData::GRAYA16(_) => fast_image_resize::PixelType::U16x2,
822 };
823
824 image_resize_loader(inner_format, srgb_map, &LoadImageView(self), size)?
825 } else {
826 let mut output =
827 unsafe { vec![std::mem::zeroed::<u8>(); self.width * self.height * 4] };
828
829 match &self.bitmap {
830 load_image::ImageData::RGB8(rgbs) => {
831 process_load_pixels(output.as_mut_slice(), rgbs, |p| {
832 sRGB32::new(p.r, p.g, p.b, u8::MAX).as_f32()
833 })
834 }
835 load_image::ImageData::RGBA8(rgbas) => {
836 process_load_pixels(output.as_mut_slice(), rgbas, |p| {
837 sRGB32::new(p.r, p.g, p.b, p.a).as_f32()
838 })
839 }
840 load_image::ImageData::RGB16(rgbs) => {
841 process_load_pixels(output.as_mut_slice(), rgbs, |p| {
842 sRGB64::new(p.r, p.g, p.b, u16::MAX).as_f32()
843 })
844 }
845 load_image::ImageData::RGBA16(rgbas) => {
846 process_load_pixels(output.as_mut_slice(), rgbas, |p| {
847 sRGB64::new(p.r, p.g, p.b, p.a).as_f32()
848 })
849 }
850 load_image::ImageData::GRAY8(gray_v08s) => {
851 process_load_pixels(output.as_mut_slice(), gray_v08s, |p| {
852 sRGB32::new(p.value(), p.value(), p.value(), u8::MAX).as_f32()
853 })
854 }
855 load_image::ImageData::GRAY16(gray_v08s) => {
856 process_load_pixels(output.as_mut_slice(), gray_v08s, |p| {
857 sRGB64::new(p.value(), p.value(), p.value(), u16::MAX).as_f32()
858 })
859 }
860 load_image::ImageData::GRAYA8(gray_alpha_v08s) => {
861 process_load_pixels(output.as_mut_slice(), gray_alpha_v08s, |p| {
862 sRGB32::new(p.v, p.v, p.v, p.a).as_f32()
863 })
864 }
865 load_image::ImageData::GRAYA16(gray_alpha_v08s) => {
866 process_load_pixels(output.as_mut_slice(), gray_alpha_v08s, |p| {
867 sRGB64::new(p.v, p.v, p.v, p.a).as_f32()
868 })
869 }
870 };
871
872 (output, self.width as u32, self.height as u32)
873 };
874
875 Ok((raw, Size2D::new(w, h)))
876 }
877 fn load(
878 &self,
879 driver: &crate::graphics::Driver,
880 data: (Vec<u8>, Size2D<u32, Pixel>),
881 resize: bool,
882 ) -> Result<(atlas::Region, atlas::Size), Error> {
883 let region = driver.atlas.write().reserve(
884 &driver.device,
885 atlas::Size::new(data.1.width as i32, data.1.height as i32),
886 if resize { Some(&driver.queue) } else { None },
887 None,
888 )?;
889
890 driver.atlas.read().queue_data(
891 &data.0,
892 ®ion,
893 &driver.queue,
894 data.1.width,
895 data.1.height,
896 );
897
898 let native = atlas::Size::new(self.width as i32, self.height as i32);
899
900 Ok((region, native))
901 }
902}
903
904#[cfg(any(
905 feature = "avif",
906 feature = "bmp",
907 feature = "dds",
908 feature = "exr",
909 feature = "ff",
910 feature = "gif",
911 feature = "hdr",
912 feature = "ico",
913 feature = "pnm",
914 feature = "qoi",
915 feature = "tga",
916 feature = "tiff",
917 feature = "webp"
918))]
919impl Loader for image::DynamicImage {
920 fn preload(
921 &self,
922 mut size: atlas::Size,
923 _: f32,
924 ) -> Result<(Vec<u8>, Size2D<u32, Pixel>), Error> {
925 use crate::color::{Premultiplied, sRGB32};
926
927 let native = atlas::Size::new(self.width() as i32, self.height() as i32);
928 size = fill_size(size, native);
929
930 let force_native = within_variance(size.height, self.height() as i32, 0.05)
933 && within_variance(size.width, self.width() as i32, 0.05);
934
935 let (raw, w, h) = if !(force_native
936 || size.width as u32 > self.width() && size.height as u32 > self.height())
937 {
938 let srgb_map = if self.color().has_color() {
939 fast_image_resize::create_srgb_mapper()
940 } else {
941 fast_image_resize::create_gamma_22_mapper()
942 };
943
944 let inner_format = match self.color().channel_count() {
945 4 => fast_image_resize::PixelType::U16x4,
946 3 => fast_image_resize::PixelType::U16x3,
947 2 => fast_image_resize::PixelType::U16x2,
948 1 => fast_image_resize::PixelType::U16,
949 _ => return Err(Error::InternalFailure),
950 };
951
952 image_resize_loader(inner_format, srgb_map, self, size)?
953 } else {
954 let mut raw = self.to_rgba8().into_vec();
955
956 for c in raw.as_mut_slice().chunks_exact_mut(4) {
958 c.copy_from_slice(
960 &sRGB32::new(c[0], c[1], c[2], c[3])
961 .as_f32()
962 .srgb_pre()
963 .as_bgra(),
964 );
965 }
966
967 (raw, self.width(), self.height())
968 };
969
970 Ok((raw, Size2D::new(w, h)))
971 }
972
973 fn load(
974 &self,
975 driver: &crate::graphics::Driver,
976 data: (Vec<u8>, Size2D<u32, Pixel>),
977 resize: bool,
978 ) -> Result<(atlas::Region, atlas::Size), Error> {
979 let region = driver.atlas.write().reserve(
980 &driver.device,
981 atlas::Size::new(data.1.width as i32, data.1.height as i32),
982 if resize { Some(&driver.queue) } else { None },
983 None,
984 )?;
985
986 driver.atlas.read().queue_data(
987 &data.0,
988 ®ion,
989 &driver.queue,
990 data.1.width,
991 data.1.height,
992 );
993
994 let native = atlas::Size::new(self.width() as i32, self.height() as i32);
995 Ok((region, native))
996 }
997}
998
999pub fn load_icon(location: &dyn Location) -> Result<winit::window::Icon, Error> {
1000 let loader = location.fetch()?;
1001
1002 #[cfg(target_os = "windows")]
1003 use windows_sys::Win32::UI::WindowsAndMessaging::{GetSystemMetrics, SM_CXICON, SM_CYICON};
1004
1005 #[cfg(target_os = "windows")]
1006 let sz = unsafe { atlas::Size::new(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON)) };
1007
1008 #[cfg(not(target_os = "windows"))]
1009 let sz = atlas::Size::new(128, 128);
1010
1011 let (mut data, dim) = loader.preload(sz, crate::BASE_DPI.width)?;
1012
1013 for c in data.as_chunks_mut::<4>().0 {
1016 c.swap(0, 2);
1017 }
1018
1019 winit::window::Icon::from_rgba(data, dim.width, dim.height)
1020 .map_err(|e| Error::ResourceError(Box::new(e)))
1021}
1022
1023#[cfg(any(
1024 feature = "avif",
1025 feature = "bmp",
1026 feature = "dds",
1027 feature = "exr",
1028 feature = "ff",
1029 feature = "gif",
1030 feature = "hdr",
1031 feature = "ico",
1032 feature = "pnm",
1033 feature = "qoi",
1034 feature = "tga",
1035 feature = "tiff",
1036 feature = "webp"
1037))]
1038#[derive(Debug, Clone)]
1039pub struct ImageRef(pub Arc<image::DynamicImage>);
1040
1041#[cfg(any(
1042 feature = "avif",
1043 feature = "bmp",
1044 feature = "dds",
1045 feature = "exr",
1046 feature = "ff",
1047 feature = "gif",
1048 feature = "hdr",
1049 feature = "ico",
1050 feature = "pnm",
1051 feature = "qoi",
1052 feature = "tga",
1053 feature = "tiff",
1054 feature = "webp"
1055))]
1056impl std::hash::Hash for ImageRef {
1057 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1058 Arc::as_ptr(&self.0).hash(state);
1059 }
1060}
1061
1062#[cfg(any(
1063 feature = "avif",
1064 feature = "bmp",
1065 feature = "dds",
1066 feature = "exr",
1067 feature = "ff",
1068 feature = "gif",
1069 feature = "hdr",
1070 feature = "ico",
1071 feature = "pnm",
1072 feature = "qoi",
1073 feature = "tga",
1074 feature = "tiff",
1075 feature = "webp"
1076))]
1077impl PartialEq for ImageRef {
1078 fn eq(&self, other: &Self) -> bool {
1079 Arc::ptr_eq(&self.0, &other.0)
1080 }
1081}
1082
1083#[cfg(any(
1084 feature = "avif",
1085 feature = "bmp",
1086 feature = "dds",
1087 feature = "exr",
1088 feature = "ff",
1089 feature = "gif",
1090 feature = "hdr",
1091 feature = "ico",
1092 feature = "pnm",
1093 feature = "qoi",
1094 feature = "tga",
1095 feature = "tiff",
1096 feature = "webp"
1097))]
1098impl Eq for ImageRef {}
1099
1100#[cfg(any(
1101 feature = "avif",
1102 feature = "bmp",
1103 feature = "dds",
1104 feature = "exr",
1105 feature = "ff",
1106 feature = "gif",
1107 feature = "hdr",
1108 feature = "ico",
1109 feature = "pnm",
1110 feature = "qoi",
1111 feature = "tga",
1112 feature = "tiff",
1113 feature = "webp"
1114))]
1115impl Location for ImageRef {
1116 fn fetch(&self) -> Result<Box<dyn Loader>, Error> {
1117 Ok(Box::new(image::DynamicImage::clone(&self.0)))
1118 }
1119}