zng_view/
image_cache.rs

1use std::{fmt, mem, sync::Arc};
2
3use image::ImageDecoder;
4use webrender::api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat};
5use winit::{
6    event_loop::ActiveEventLoop,
7    window::{CustomCursor, Icon},
8};
9use zng_txt::{ToTxt, Txt, formatx};
10use zng_unit::{Px, PxPoint, PxSize};
11use zng_view_api::{
12    Event,
13    image::{ImageDataFormat, ImageDownscale, ImageId, ImageLoadedData, ImageMaskMode, ImagePpi, ImageRequest},
14    ipc::{IpcBytes, IpcBytesReceiver},
15};
16
17use crate::{AppEvent, AppEventSender};
18use rustc_hash::FxHashMap;
19
20pub(crate) const ENCODERS: &[&str] = &[
21    #[cfg(feature = "image_png")]
22    "png",
23    #[cfg(feature = "image_jpeg")]
24    "jpg",
25    #[cfg(feature = "image_jpeg")]
26    "jpeg",
27    #[cfg(feature = "image_webp")]
28    "webp",
29    #[cfg(any(feature = "image_avif", zng_view_image_has_avif))]
30    "avif",
31    #[cfg(feature = "image_gif")]
32    "gif",
33    #[cfg(feature = "image_ico")]
34    "ico",
35    #[cfg(feature = "image_bmp")]
36    "bmp",
37    #[cfg(feature = "image_jpeg")]
38    "jfif",
39    #[cfg(feature = "image_exr")]
40    "exr",
41    #[cfg(feature = "image_hdr")]
42    "hdr",
43    #[cfg(feature = "image_pnm")]
44    "pnm",
45    #[cfg(feature = "image_qoi")]
46    "qoi",
47    #[cfg(feature = "image_ff")]
48    "ff",
49    #[cfg(feature = "image_ff")]
50    "farbfeld",
51];
52pub(crate) const DECODERS: &[&str] = &[
53    #[cfg(feature = "image_png")]
54    "png",
55    #[cfg(feature = "image_jpeg")]
56    "jpg",
57    #[cfg(feature = "image_jpeg")]
58    "jpeg",
59    #[cfg(feature = "image_webp")]
60    "webp",
61    #[cfg(any(feature = "image_avif", zng_view_image_has_avif))]
62    "avif",
63    #[cfg(feature = "image_gif")]
64    "gif",
65    #[cfg(feature = "image_ico")]
66    "ico",
67    #[cfg(feature = "image_bmp")]
68    "bmp",
69    #[cfg(feature = "image_jpeg")]
70    "jfif",
71    #[cfg(feature = "image_exr")]
72    "exr",
73    #[cfg(feature = "image_pnm")]
74    "pnm",
75    #[cfg(feature = "image_qoi")]
76    "qoi",
77    #[cfg(feature = "image_ff")]
78    "ff",
79    #[cfg(feature = "image_ff")]
80    "farbfeld",
81    #[cfg(feature = "image_dds")]
82    "dds",
83];
84
85/// Decode and cache image resources.
86pub(crate) struct ImageCache {
87    app_sender: AppEventSender,
88    images: FxHashMap<ImageId, Image>,
89    image_id_gen: ImageId,
90}
91impl ImageCache {
92    pub fn new(app_sender: AppEventSender) -> Self {
93        Self {
94            app_sender,
95            images: FxHashMap::default(),
96            image_id_gen: ImageId::first(),
97        }
98    }
99
100    pub fn add(
101        &mut self,
102        ImageRequest {
103            format,
104            data,
105            max_decoded_len,
106            downscale,
107            mask,
108        }: ImageRequest<IpcBytes>,
109    ) -> ImageId {
110        let id = self.image_id_gen.incr();
111
112        let app_sender = self.app_sender.clone();
113        rayon::spawn(move || {
114            let r = match format {
115                ImageDataFormat::Bgra8 { size, ppi } => {
116                    let expected_len = size.width.0 as usize * size.height.0 as usize * 4;
117                    if data.len() != expected_len {
118                        Err(formatx!(
119                            "pixels.len() is not width * height * 4, expected {expected_len}, found {}",
120                            data.len()
121                        ))
122                    } else if mask.is_some() {
123                        let (pixels, size, _, is_opaque, _) = Self::convert_decoded(
124                            image::DynamicImage::ImageLuma8(
125                                image::ImageBuffer::from_raw(size.width.0 as _, size.height.0 as _, data.to_vec()).unwrap(),
126                            ),
127                            mask,
128                        );
129                        Ok((pixels, size, ppi, is_opaque, true))
130                    } else {
131                        let is_opaque = data.chunks_exact(4).all(|c| c[3] == 255);
132                        Ok((data, size, ppi, is_opaque, false))
133                    }
134                }
135                ImageDataFormat::A8 { size } => {
136                    let expected_len = size.width.0 as usize * size.height.0 as usize;
137                    if data.len() != expected_len {
138                        Err(formatx!(
139                            "pixels.len() is not width * height, expected {expected_len}, found {}",
140                            data.len()
141                        ))
142                    } else if mask.is_none() {
143                        let (pixels, size, _, is_opaque, _) = Self::convert_decoded(
144                            image::DynamicImage::ImageLuma8(
145                                image::ImageBuffer::from_raw(size.width.0 as _, size.height.0 as _, data.to_vec()).unwrap(),
146                            ),
147                            None,
148                        );
149                        Ok((pixels, size, None, is_opaque, false))
150                    } else {
151                        let is_opaque = data.iter().all(|&c| c == 255);
152                        Ok((data, size, None, is_opaque, true))
153                    }
154                }
155                fmt => match Self::get_format_and_size(&fmt, &data[..]) {
156                    Ok((fmt, mut size, orientation)) => {
157                        let decoded_len = size.width.0 as u64 * size.height.0 as u64 * 4;
158                        if decoded_len > max_decoded_len {
159                            Err(formatx!(
160                                "image {size:?} needs to allocate {decoded_len} bytes, but max allowed size is {max_decoded_len} bytes",
161                            ))
162                        } else {
163                            if let Some(d) = downscale {
164                                size = d.resize_dimensions(size);
165                            }
166                            let _ = app_sender.send(AppEvent::Notify(Event::ImageMetadataLoaded {
167                                image: id,
168                                size,
169                                ppi: None,
170                                is_mask: false,
171                            }));
172                            match Self::image_decode(&data[..], fmt, downscale, orientation) {
173                                Ok(img) => Ok(Self::convert_decoded(img, mask)),
174                                Err(e) => Err(e.to_txt()),
175                            }
176                        }
177                    }
178                    Err(e) => Err(e),
179                },
180            };
181
182            match r {
183                Ok((pixels, size, ppi, is_opaque, is_mask)) => {
184                    let _ = app_sender.send(AppEvent::ImageLoaded(ImageLoadedData {
185                        id,
186                        pixels,
187                        size,
188                        ppi,
189                        is_opaque,
190                        is_mask,
191                    }));
192                }
193                Err(e) => {
194                    let _ = app_sender.send(AppEvent::Notify(Event::ImageLoadError { image: id, error: e }));
195                }
196            }
197        });
198
199        id
200    }
201
202    pub fn add_pro(
203        &mut self,
204        ImageRequest {
205            format,
206            data,
207            max_decoded_len,
208            downscale,
209            mask,
210        }: ImageRequest<IpcBytesReceiver>,
211    ) -> ImageId {
212        let id = self.image_id_gen.incr();
213        let app_sender = self.app_sender.clone();
214        rayon::spawn(move || {
215            // crate `images` does not do progressive decode.
216            let mut full = vec![];
217            let mut size = None;
218            let mut ppi = None;
219            let mut is_encoded = true;
220            let mut orientation = image::metadata::Orientation::NoTransforms;
221
222            let mut format = match format {
223                ImageDataFormat::Bgra8 { size: s, ppi: p } => {
224                    is_encoded = false;
225                    size = Some(s);
226                    ppi = p;
227                    None
228                }
229                ImageDataFormat::A8 { size: s } => {
230                    is_encoded = false;
231                    size = Some(s);
232                    None
233                }
234                ImageDataFormat::FileExtension(ext) => image::ImageFormat::from_extension(ext.as_str()),
235                ImageDataFormat::MimeType(t) => t.strip_prefix("image/").and_then(image::ImageFormat::from_extension),
236                ImageDataFormat::Unknown => None,
237            };
238
239            let mut pending = true;
240            while pending {
241                match data.recv() {
242                    Ok(d) => {
243                        pending = !d.is_empty();
244
245                        full.extend(d);
246
247                        if let Some(fmt) = format {
248                            if size.is_none() {
249                                if let Ok(mut d) = image::ImageReader::with_format(std::io::Cursor::new(&full), fmt).into_decoder() {
250                                    use image::metadata::Orientation::*;
251
252                                    let (mut w, mut h) = d.dimensions();
253                                    orientation = d.orientation().unwrap_or(NoTransforms);
254
255                                    if matches!(orientation, Rotate90 | Rotate270 | Rotate90FlipH | Rotate270FlipH) {
256                                        mem::swap(&mut w, &mut h)
257                                    }
258
259                                    size = Some(PxSize::new(Px(w as i32), Px(h as i32)));
260                                }
261
262                                if let Some(s) = size {
263                                    let decoded_len = s.width.0 as u64 * s.height.0 as u64 * 4;
264                                    if decoded_len > max_decoded_len {
265                                        let error = formatx!(
266                                            "image {size:?} needs to allocate {decoded_len} bytes, but max allowed size is {max_decoded_len} bytes",
267                                        );
268                                        let _ = app_sender.send(AppEvent::Notify(Event::ImageLoadError { image: id, error }));
269                                        return;
270                                    }
271                                }
272                            }
273                        } else if is_encoded {
274                            format = image::guess_format(&full).ok();
275                        }
276                    }
277                    Err(_) => {
278                        // cancelled?
279                        return;
280                    }
281                }
282            }
283
284            if let Some(fmt) = format {
285                match Self::image_decode(&full[..], fmt, downscale, orientation) {
286                    Ok(img) => {
287                        let (pixels, size, ppi, is_opaque, is_mask) = Self::convert_decoded(img, mask);
288                        let _ = app_sender.send(AppEvent::ImageLoaded(ImageLoadedData {
289                            id,
290                            pixels,
291                            size,
292                            ppi,
293                            is_opaque,
294                            is_mask,
295                        }));
296                    }
297                    Err(e) => {
298                        let _ = app_sender.send(AppEvent::Notify(Event::ImageLoadError {
299                            image: id,
300                            error: e.to_txt(),
301                        }));
302                    }
303                }
304            } else if !is_encoded {
305                let pixels = IpcBytes::from_vec(full);
306                let is_opaque = pixels.chunks_exact(4).all(|c| c[3] == 255);
307                let _ = app_sender.send(AppEvent::ImageLoaded(ImageLoadedData {
308                    id,
309                    pixels,
310                    size: size.unwrap(),
311                    ppi,
312                    is_opaque,
313                    is_mask: false,
314                }));
315            } else {
316                let _ = app_sender.send(AppEvent::Notify(Event::ImageLoadError {
317                    image: id,
318                    error: Txt::from_static("unknown format"),
319                }));
320            }
321        });
322        id
323    }
324
325    pub fn forget(&mut self, id: ImageId) {
326        self.images.remove(&id);
327    }
328
329    pub fn get(&self, id: ImageId) -> Option<&Image> {
330        self.images.get(&id)
331    }
332
333    /// Called after receive and decode completes correctly.
334    pub(crate) fn loaded(&mut self, data: ImageLoadedData) {
335        let mut flags = ImageDescriptorFlags::empty(); //ImageDescriptorFlags::ALLOW_MIPMAPS;
336        if data.is_opaque {
337            flags |= ImageDescriptorFlags::IS_OPAQUE
338        }
339
340        self.images.insert(
341            data.id,
342            Image(Arc::new(ImageData::RawData {
343                size: data.size,
344                pixels: data.pixels.clone(),
345                descriptor: ImageDescriptor::new(
346                    data.size.width.0,
347                    data.size.height.0,
348                    if data.is_mask { ImageFormat::R8 } else { ImageFormat::BGRA8 },
349                    flags,
350                ),
351                ppi: data.ppi,
352            })),
353        );
354
355        let _ = self.app_sender.send(AppEvent::Notify(Event::ImageLoaded(data)));
356    }
357
358    fn get_format_and_size(fmt: &ImageDataFormat, data: &[u8]) -> Result<(image::ImageFormat, PxSize, image::metadata::Orientation), Txt> {
359        let fmt = match fmt {
360            ImageDataFormat::FileExtension(ext) => image::ImageFormat::from_extension(ext.as_str()),
361            ImageDataFormat::MimeType(t) => t.strip_prefix("image/").and_then(image::ImageFormat::from_extension),
362            ImageDataFormat::Unknown => None,
363            ImageDataFormat::Bgra8 { .. } => unreachable!(),
364            ImageDataFormat::A8 { .. } => unreachable!(),
365        };
366
367        let reader = match fmt {
368            Some(fmt) => image::ImageReader::with_format(std::io::Cursor::new(data), fmt),
369            None => image::ImageReader::new(std::io::Cursor::new(data))
370                .with_guessed_format()
371                .map_err(|e| e.to_txt())?,
372        };
373
374        match reader.format() {
375            Some(fmt) => {
376                use image::metadata::Orientation::*;
377
378                let mut decoder = reader.into_decoder().map_err(|e| e.to_string())?;
379                let (mut w, mut h) = decoder.dimensions();
380                let orientation = decoder.orientation().unwrap_or(NoTransforms);
381
382                if matches!(orientation, Rotate90 | Rotate270 | Rotate90FlipH | Rotate270FlipH) {
383                    mem::swap(&mut w, &mut h)
384                }
385
386                Ok((fmt, PxSize::new(Px(w as i32), Px(h as i32)), orientation))
387            }
388            None => Err(Txt::from_static("unknown format")),
389        }
390    }
391
392    fn image_decode(
393        buf: &[u8],
394        format: image::ImageFormat,
395        downscale: Option<ImageDownscale>,
396        orientation: image::metadata::Orientation,
397    ) -> image::ImageResult<image::DynamicImage> {
398        let buf = std::io::Cursor::new(buf);
399
400        let mut reader = image::ImageReader::new(buf);
401        reader.set_format(format);
402        reader.no_limits();
403        let mut image = reader.decode()?;
404
405        image.apply_orientation(orientation);
406
407        if let Some(s) = downscale {
408            let (img_w, img_h) = (image.width(), image.height());
409            match s {
410                ImageDownscale::Fit(s) => {
411                    let w = img_w.min(s.width.0 as u32);
412                    let h = img_h.min(s.height.0 as u32);
413                    if w != img_w || h != img_h {
414                        image = image.resize(w, h, image::imageops::FilterType::Triangle);
415                    }
416                }
417                ImageDownscale::Fill(s) => {
418                    let w = img_w.min(s.width.0 as u32);
419                    let h = img_h.min(s.height.0 as u32);
420                    if w != img_w && h != img_h {
421                        image = image.resize_to_fill(w, h, image::imageops::FilterType::Triangle);
422                    }
423                }
424            }
425        }
426
427        Ok(image)
428    }
429
430    fn convert_decoded(image: image::DynamicImage, mask: Option<ImageMaskMode>) -> RawLoadedImg {
431        use image::DynamicImage::*;
432
433        let mut is_opaque = true;
434
435        let (size, pixels) = match image {
436            ImageLuma8(img) => (
437                img.dimensions(),
438                if mask.is_some() {
439                    let r = img.into_raw();
440                    is_opaque = !r.iter().any(|&a| a < 255);
441                    r
442                } else {
443                    img.into_raw().into_iter().flat_map(|l| [l, l, l, 255]).collect()
444                },
445            ),
446            ImageLumaA8(img) => (
447                img.dimensions(),
448                if let Some(mask) = mask {
449                    match mask {
450                        ImageMaskMode::A => img
451                            .into_raw()
452                            .chunks(2)
453                            .map(|la| {
454                                if la[1] < 255 {
455                                    is_opaque = false;
456                                }
457                                la[1]
458                            })
459                            .collect(),
460                        ImageMaskMode::B | ImageMaskMode::G | ImageMaskMode::R | ImageMaskMode::Luminance => img
461                            .into_raw()
462                            .chunks(2)
463                            .map(|la| {
464                                if la[0] < 255 {
465                                    is_opaque = false;
466                                }
467                                la[0]
468                            })
469                            .collect(),
470                    }
471                } else {
472                    img.into_raw()
473                        .chunks(2)
474                        .flat_map(|la| {
475                            if la[1] < 255 {
476                                is_opaque = false;
477                                let l = la[0] as f32 * la[1] as f32 / 255.0;
478                                let l = l as u8;
479                                [l, l, l, la[1]]
480                            } else {
481                                let l = la[0];
482                                [l, l, l, la[1]]
483                            }
484                        })
485                        .collect()
486                },
487            ),
488            ImageRgb8(img) => (
489                img.dimensions(),
490                if let Some(mask) = mask {
491                    match mask {
492                        ImageMaskMode::Luminance | ImageMaskMode::A => img
493                            .into_raw()
494                            .chunks(3)
495                            .map(|c| {
496                                let c = luminance(c);
497                                if c < 255 {
498                                    is_opaque = false;
499                                }
500                                c
501                            })
502                            .collect(),
503                        mask => {
504                            let channel = match mask {
505                                ImageMaskMode::B => 2,
506                                ImageMaskMode::G => 1,
507                                ImageMaskMode::R => 0,
508                                _ => unreachable!(),
509                            };
510                            img.into_raw()
511                                .chunks(3)
512                                .map(|c| {
513                                    let c = c[channel];
514                                    if c < 255 {
515                                        is_opaque = false;
516                                    }
517                                    c
518                                })
519                                .collect()
520                        }
521                    }
522                } else {
523                    img.into_raw().chunks(3).flat_map(|c| [c[2], c[1], c[0], 255]).collect()
524                },
525            ),
526            ImageRgba8(img) => (
527                img.dimensions(),
528                if let Some(mask) = mask {
529                    match mask {
530                        ImageMaskMode::Luminance => img
531                            .into_raw()
532                            .chunks(4)
533                            .map(|c| {
534                                let c = luminance(&c[..3]);
535                                if c < 255 {
536                                    is_opaque = false;
537                                }
538                                c
539                            })
540                            .collect(),
541                        mask => {
542                            let channel = match mask {
543                                ImageMaskMode::A => 3,
544                                ImageMaskMode::B => 2,
545                                ImageMaskMode::G => 1,
546                                ImageMaskMode::R => 0,
547                                _ => unreachable!(),
548                            };
549                            img.into_raw()
550                                .chunks(4)
551                                .map(|c| {
552                                    let c = c[channel];
553                                    if c < 255 {
554                                        is_opaque = false;
555                                    }
556                                    c
557                                })
558                                .collect()
559                        }
560                    }
561                } else {
562                    let mut buf = img.into_raw();
563                    buf.chunks_mut(4).for_each(|c| {
564                        if c[3] < 255 {
565                            is_opaque = false;
566                            let a = c[3] as f32 / 255.0;
567                            c[0..3].iter_mut().for_each(|c| *c = (*c as f32 * a) as u8);
568                        }
569                        c.swap(0, 2);
570                    });
571                    buf
572                },
573            ),
574            ImageLuma16(img) => (
575                img.dimensions(),
576                if mask.is_some() {
577                    img.into_raw()
578                        .into_iter()
579                        .map(|l| {
580                            let l = (l as f32 / u16::MAX as f32 * 255.0) as u8;
581                            if l < 255 {
582                                is_opaque = false;
583                            }
584                            l
585                        })
586                        .collect()
587                } else {
588                    img.into_raw()
589                        .into_iter()
590                        .flat_map(|l| {
591                            let l = (l as f32 / u16::MAX as f32 * 255.0) as u8;
592                            [l, l, l, 255]
593                        })
594                        .collect()
595                },
596            ),
597            ImageLumaA16(img) => (
598                img.dimensions(),
599                if let Some(mask) = mask {
600                    match mask {
601                        ImageMaskMode::A => img
602                            .into_raw()
603                            .chunks(2)
604                            .map(|la| {
605                                if la[1] < u16::MAX {
606                                    is_opaque = false;
607                                }
608                                let max = u16::MAX as f32;
609                                let l = la[1] as f32 / max * 255.0;
610                                l as u8
611                            })
612                            .collect(),
613                        ImageMaskMode::B | ImageMaskMode::G | ImageMaskMode::R | ImageMaskMode::Luminance => img
614                            .into_raw()
615                            .chunks(2)
616                            .map(|la| {
617                                if la[0] < u16::MAX {
618                                    is_opaque = false;
619                                }
620                                let max = u16::MAX as f32;
621                                let l = la[0] as f32 / max * 255.0;
622                                l as u8
623                            })
624                            .collect(),
625                    }
626                } else {
627                    img.into_raw()
628                        .chunks(2)
629                        .flat_map(|la| {
630                            let max = u16::MAX as f32;
631                            let l = la[0] as f32 / max;
632                            let a = la[1] as f32 / max * 255.0;
633
634                            if la[1] < u16::MAX {
635                                is_opaque = false;
636                                let l = (l * a) as u8;
637                                [l, l, l, a as u8]
638                            } else {
639                                let l = (l * 255.0) as u8;
640                                [l, l, l, a as u8]
641                            }
642                        })
643                        .collect()
644                },
645            ),
646            ImageRgb16(img) => (
647                img.dimensions(),
648                if let Some(mask) = mask {
649                    match mask {
650                        ImageMaskMode::Luminance | ImageMaskMode::A => img
651                            .into_raw()
652                            .chunks(3)
653                            .map(|c| {
654                                let c = luminance_16(c);
655                                if c < 255 {
656                                    is_opaque = false;
657                                }
658                                c
659                            })
660                            .collect(),
661                        mask => {
662                            let channel = match mask {
663                                ImageMaskMode::B => 2,
664                                ImageMaskMode::G => 1,
665                                ImageMaskMode::R => 0,
666                                _ => unreachable!(),
667                            };
668                            img.into_raw()
669                                .chunks(3)
670                                .map(|c| {
671                                    let c = c[channel];
672                                    if c < u16::MAX {
673                                        is_opaque = false;
674                                    }
675
676                                    (c as f32 / u16::MAX as f32 * 255.0) as u8
677                                })
678                                .collect()
679                        }
680                    }
681                } else {
682                    img.into_raw()
683                        .chunks(3)
684                        .flat_map(|c| {
685                            let to_u8 = 255.0 / u16::MAX as f32;
686                            [
687                                (c[2] as f32 * to_u8) as u8,
688                                (c[1] as f32 * to_u8) as u8,
689                                (c[0] as f32 * to_u8) as u8,
690                                255,
691                            ]
692                        })
693                        .collect()
694                },
695            ),
696            ImageRgba16(img) => (
697                img.dimensions(),
698                if let Some(mask) = mask {
699                    match mask {
700                        ImageMaskMode::Luminance => img
701                            .into_raw()
702                            .chunks(4)
703                            .map(|c| {
704                                let c = luminance_16(&c[..3]);
705                                if c < 255 {
706                                    is_opaque = false;
707                                }
708                                c
709                            })
710                            .collect(),
711                        mask => {
712                            let channel = match mask {
713                                ImageMaskMode::A => 3,
714                                ImageMaskMode::B => 2,
715                                ImageMaskMode::G => 1,
716                                ImageMaskMode::R => 0,
717                                _ => unreachable!(),
718                            };
719                            img.into_raw()
720                                .chunks(4)
721                                .map(|c| {
722                                    let c = c[channel];
723                                    if c < 255 {
724                                        is_opaque = false;
725                                    }
726                                    (c as f32 / u16::MAX as f32 * 255.0) as u8
727                                })
728                                .collect()
729                        }
730                    }
731                } else {
732                    img.into_raw()
733                        .chunks(4)
734                        .flat_map(|c| {
735                            if c[3] < u16::MAX {
736                                is_opaque = false;
737                                let max = u16::MAX as f32;
738                                let a = c[3] as f32 / max * 255.0;
739                                [
740                                    (c[2] as f32 / max * a) as u8,
741                                    (c[1] as f32 / max * a) as u8,
742                                    (c[0] as f32 / max * a) as u8,
743                                    a as u8,
744                                ]
745                            } else {
746                                let to_u8 = 255.0 / u16::MAX as f32;
747                                [
748                                    (c[2] as f32 * to_u8) as u8,
749                                    (c[1] as f32 * to_u8) as u8,
750                                    (c[0] as f32 * to_u8) as u8,
751                                    255,
752                                ]
753                            }
754                        })
755                        .collect()
756                },
757            ),
758            ImageRgb32F(img) => (
759                img.dimensions(),
760                if let Some(mask) = mask {
761                    match mask {
762                        ImageMaskMode::Luminance | ImageMaskMode::A => img
763                            .into_raw()
764                            .chunks(3)
765                            .map(|c| {
766                                let c = luminance_f32(c);
767                                if c < 255 {
768                                    is_opaque = false;
769                                }
770                                c
771                            })
772                            .collect(),
773                        mask => {
774                            let channel = match mask {
775                                ImageMaskMode::B => 2,
776                                ImageMaskMode::G => 1,
777                                ImageMaskMode::R => 0,
778                                _ => unreachable!(),
779                            };
780                            img.into_raw()
781                                .chunks(3)
782                                .map(|c| {
783                                    let c = (c[channel] * 255.0) as u8;
784                                    if c < 255 {
785                                        is_opaque = false;
786                                    }
787                                    c
788                                })
789                                .collect()
790                        }
791                    }
792                } else {
793                    img.into_raw()
794                        .chunks(3)
795                        .flat_map(|c| [(c[2] * 255.0) as u8, (c[1] * 255.0) as u8, (c[0] * 255.0) as u8, 255])
796                        .collect()
797                },
798            ),
799            ImageRgba32F(img) => (
800                img.dimensions(),
801                if let Some(mask) = mask {
802                    match mask {
803                        ImageMaskMode::Luminance => img
804                            .into_raw()
805                            .chunks(4)
806                            .map(|c| {
807                                let c = luminance_f32(&c[..3]);
808                                if c < 255 {
809                                    is_opaque = false;
810                                }
811                                c
812                            })
813                            .collect(),
814                        mask => {
815                            let channel = match mask {
816                                ImageMaskMode::A => 3,
817                                ImageMaskMode::B => 2,
818                                ImageMaskMode::G => 1,
819                                ImageMaskMode::R => 0,
820                                _ => unreachable!(),
821                            };
822                            img.into_raw()
823                                .chunks(4)
824                                .map(|c| {
825                                    let c = (c[channel] * 255.0) as u8;
826                                    if c < 255 {
827                                        is_opaque = false;
828                                    }
829                                    c
830                                })
831                                .collect()
832                        }
833                    }
834                } else {
835                    img.into_raw()
836                        .chunks(4)
837                        .flat_map(|c| {
838                            if c[3] < 1.0 {
839                                is_opaque = false;
840                                let a = c[3] * 255.0;
841                                [(c[2] * a) as u8, (c[1] * a) as u8, (c[0] * a) as u8, a as u8]
842                            } else {
843                                [(c[2] * 255.0) as u8, (c[1] * 255.0) as u8, (c[0] * 255.0) as u8, 255]
844                            }
845                        })
846                        .collect()
847                },
848            ),
849            _ => unreachable!(),
850        };
851
852        (
853            IpcBytes::from_vec(pixels),
854            PxSize::new(Px(size.0 as i32), Px(size.1 as i32)),
855            None,
856            is_opaque,
857            mask.is_some(), // is_mask
858        )
859    }
860
861    pub fn encode(&self, id: ImageId, format: Txt) {
862        if !ENCODERS.contains(&format.as_str()) {
863            let error = formatx!("cannot encode `{id:?}` to `{format}`, unknown format");
864            let _ = self
865                .app_sender
866                .send(AppEvent::Notify(Event::ImageEncodeError { image: id, format, error }));
867            return;
868        }
869
870        if let Some(img) = self.get(id) {
871            let fmt = image::ImageFormat::from_extension(format.as_str()).unwrap();
872            debug_assert!(fmt.can_write());
873
874            let img = img.clone();
875            let sender = self.app_sender.clone();
876            rayon::spawn(move || {
877                let mut data = vec![];
878                match img.encode(fmt, &mut data) {
879                    Ok(_) => {
880                        let _ = sender.send(AppEvent::Notify(Event::ImageEncoded {
881                            image: id,
882                            format,
883                            data: IpcBytes::from_vec(data),
884                        }));
885                    }
886                    Err(e) => {
887                        let error = formatx!("failed to encode `{id:?}` to `{format}`, {e}");
888                        let _ = sender.send(AppEvent::Notify(Event::ImageEncodeError { image: id, format, error }));
889                    }
890                }
891            })
892        } else {
893            let error = formatx!("cannot encode `{id:?}` to `{format}`, image not found");
894            let _ = self
895                .app_sender
896                .send(AppEvent::Notify(Event::ImageEncodeError { image: id, format, error }));
897        }
898    }
899
900    pub(crate) fn on_low_memory(&mut self) {
901        // app-process controls this cache
902    }
903
904    pub(crate) fn clear(&mut self) {
905        self.images.clear();
906    }
907}
908
909/// (pixels, size, ppi, is_opaque, is_mask)
910type RawLoadedImg = (IpcBytes, PxSize, Option<ImagePpi>, bool, bool);
911pub(crate) enum ImageData {
912    RawData {
913        size: PxSize,
914        pixels: IpcBytes,
915        descriptor: ImageDescriptor,
916        ppi: Option<ImagePpi>,
917    },
918    NativeTexture {
919        uv: webrender::api::units::TexelRect,
920        texture: gleam::gl::GLuint,
921    },
922}
923impl ImageData {
924    pub fn is_opaque(&self) -> bool {
925        match self {
926            ImageData::RawData { descriptor, .. } => descriptor.flags.contains(ImageDescriptorFlags::IS_OPAQUE),
927            ImageData::NativeTexture { .. } => false,
928        }
929    }
930
931    pub fn is_mask(&self) -> bool {
932        match self {
933            ImageData::RawData { descriptor, .. } => descriptor.format == ImageFormat::R8,
934            ImageData::NativeTexture { .. } => false,
935        }
936    }
937}
938
939#[derive(Clone)]
940pub(crate) struct Image(Arc<ImageData>);
941impl fmt::Debug for Image {
942    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
943        match &*self.0 {
944            ImageData::RawData {
945                size,
946                pixels,
947                descriptor,
948                ppi,
949            } => f
950                .debug_struct("Image")
951                .field("size", size)
952                .field("descriptor", descriptor)
953                .field("ppi", ppi)
954                .field("pixels", &format_args!("<{} shared bytes>", pixels.len()))
955                .finish(),
956            ImageData::NativeTexture { .. } => unreachable!(),
957        }
958    }
959}
960impl Image {
961    pub fn descriptor(&self) -> ImageDescriptor {
962        match &*self.0 {
963            ImageData::RawData { descriptor, .. } => *descriptor,
964            ImageData::NativeTexture { .. } => unreachable!(),
965        }
966    }
967
968    /// Generate a window icon from the image.
969    pub fn icon(&self) -> Option<Icon> {
970        let (size, pixels) = match &*self.0 {
971            ImageData::RawData { size, pixels, .. } => (size, pixels),
972            ImageData::NativeTexture { .. } => unreachable!(),
973        };
974
975        let width = size.width.0 as u32;
976        let height = size.height.0 as u32;
977        if width == 0 || height == 0 || self.0.is_mask() {
978            None
979        } else {
980            let r = if width > 255 || height > 255 {
981                // resize to max 255
982                let mut buf = pixels[..].to_vec();
983                // BGRA to RGBA
984                buf.chunks_exact_mut(4).for_each(|c| c.swap(0, 2));
985                let img = image::ImageBuffer::from_raw(width, height, buf).unwrap();
986                let img = image::DynamicImage::ImageRgba8(img);
987                let img = img.resize(255, 255, image::imageops::FilterType::Lanczos3);
988
989                use image::GenericImageView;
990                let (width, height) = img.dimensions();
991                let buf = img.into_rgba8().into_raw();
992                winit::window::Icon::from_rgba(buf, width, height)
993            } else {
994                let mut buf = pixels[..].to_vec();
995                // BGRA to RGBA
996                buf.chunks_exact_mut(4).for_each(|c| c.swap(0, 2));
997                winit::window::Icon::from_rgba(buf, width, height)
998            };
999            match r {
1000                Ok(i) => Some(i),
1001                Err(e) => {
1002                    tracing::error!("failed to convert image to custom icon, {e}");
1003                    None
1004                }
1005            }
1006        }
1007    }
1008
1009    /// Generate a cursor from the image.
1010    pub fn cursor(&self, hotspot: PxPoint, event_loop: &ActiveEventLoop) -> Option<CustomCursor> {
1011        let (size, pixels) = match &*self.0 {
1012            ImageData::RawData { size, pixels, .. } => (size, pixels),
1013            ImageData::NativeTexture { .. } => unreachable!(),
1014        };
1015
1016        let width: u16 = size.width.0.try_into().ok()?;
1017        let height: u16 = size.height.0.try_into().ok()?;
1018        let hotspot_x: u16 = hotspot.x.0.try_into().ok()?;
1019        let hotspot_y: u16 = hotspot.y.0.try_into().ok()?;
1020
1021        if width == 0 || height == 0 || hotspot_x > width || hotspot_y > height || self.0.is_mask() {
1022            None
1023        } else {
1024            let mut buf = pixels[..].to_vec();
1025            // BGRA to RGBA
1026            buf.chunks_exact_mut(4).for_each(|c| c.swap(0, 2));
1027            match CustomCursor::from_rgba(buf, width, height, hotspot_x, hotspot_y) {
1028                Ok(c) => Some(event_loop.create_custom_cursor(c)),
1029                Err(e) => {
1030                    tracing::error!("failed to convert image to custom cursor, {e}");
1031                    None
1032                }
1033            }
1034        }
1035    }
1036
1037    pub fn encode(&self, format: image::ImageFormat, buffer: &mut Vec<u8>) -> image::ImageResult<()> {
1038        let (size, pixels, ppi) = match &*self.0 {
1039            ImageData::RawData { size, pixels, ppi, .. } => (size, pixels, ppi),
1040            ImageData::NativeTexture { .. } => unreachable!(),
1041        };
1042
1043        if size.width <= 0 || size.height <= 0 {
1044            return Err(image::ImageError::IoError(std::io::Error::new(
1045                std::io::ErrorKind::InvalidInput,
1046                "cannot encode zero sized image",
1047            )));
1048        }
1049
1050        if self.0.is_mask() {
1051            let width = size.width.0 as u32;
1052            let height = size.height.0 as u32;
1053            let is_opaque = self.0.is_opaque();
1054            let r8 = pixels[..].to_vec();
1055
1056            let mut img = image::DynamicImage::ImageLuma8(image::ImageBuffer::from_raw(width, height, r8).unwrap());
1057            if is_opaque {
1058                img = image::DynamicImage::ImageRgb8(img.to_rgb8());
1059            }
1060            img.write_to(&mut std::io::Cursor::new(buffer), format)?;
1061
1062            return Ok(());
1063        }
1064
1065        // invert rows, `image` only supports top-to-bottom buffers.
1066        let mut buf = pixels[..].to_vec();
1067        // BGRA to RGBA
1068        buf.chunks_exact_mut(4).for_each(|c| c.swap(0, 2));
1069        let rgba = buf;
1070
1071        let width = size.width.0 as u32;
1072        let height = size.height.0 as u32;
1073        let is_opaque = self.0.is_opaque();
1074
1075        match format {
1076            #[cfg(feature = "image_jpeg")]
1077            image::ImageFormat::Jpeg => {
1078                let mut jpg = image::codecs::jpeg::JpegEncoder::new(buffer);
1079                if let Some(ppi) = ppi {
1080                    jpg.set_pixel_density(image::codecs::jpeg::PixelDensity {
1081                        density: (ppi.x as u16, ppi.y as u16),
1082                        unit: image::codecs::jpeg::PixelDensityUnit::Inches,
1083                    });
1084                }
1085                jpg.encode(&rgba, width, height, image::ColorType::Rgba8.into())?;
1086            }
1087            #[cfg(feature = "image_png")]
1088            image::ImageFormat::Png => {
1089                let mut img = image::DynamicImage::ImageRgba8(image::ImageBuffer::from_raw(width, height, rgba).unwrap());
1090                if is_opaque {
1091                    img = image::DynamicImage::ImageRgb8(img.to_rgb8());
1092                }
1093                if let Some(ppi) = ppi {
1094                    let mut png_bytes = vec![];
1095
1096                    img.write_to(&mut std::io::Cursor::new(&mut png_bytes), image::ImageFormat::Png)?;
1097
1098                    let mut png = img_parts::png::Png::from_bytes(png_bytes.into()).unwrap();
1099
1100                    let chunk_kind = *b"pHYs";
1101                    debug_assert!(png.chunk_by_type(chunk_kind).is_none());
1102
1103                    use byteorder::*;
1104                    let mut chunk = Vec::with_capacity(4 * 2 + 1);
1105
1106                    // ppi / inch_to_metric
1107                    let ppm_x = (ppi.x / 0.0254) as u32;
1108                    let ppm_y = (ppi.y / 0.0254) as u32;
1109
1110                    chunk.write_u32::<BigEndian>(ppm_x).unwrap();
1111                    chunk.write_u32::<BigEndian>(ppm_y).unwrap();
1112                    chunk.write_u8(1).unwrap(); // metric
1113
1114                    let chunk = img_parts::png::PngChunk::new(chunk_kind, chunk.into());
1115                    png.chunks_mut().insert(1, chunk);
1116
1117                    png.encoder().write_to(buffer)?;
1118                } else {
1119                    img.write_to(&mut std::io::Cursor::new(buffer), image::ImageFormat::Png)?;
1120                }
1121            }
1122            _ => {
1123                // other formats that we don't with custom PPI meta.
1124
1125                let _ = ppi; // suppress warning when both png an jpeg are not enabled
1126
1127                let mut img = image::DynamicImage::ImageRgba8(image::ImageBuffer::from_raw(width, height, rgba).unwrap());
1128                if is_opaque {
1129                    img = image::DynamicImage::ImageRgb8(img.to_rgb8());
1130                }
1131                img.write_to(&mut std::io::Cursor::new(buffer), format)?;
1132            }
1133        }
1134
1135        Ok(())
1136    }
1137
1138    #[allow(unused)]
1139    pub fn size(&self) -> PxSize {
1140        match &*self.0 {
1141            ImageData::RawData { size, .. } => *size,
1142            ImageData::NativeTexture { .. } => unreachable!(),
1143        }
1144    }
1145
1146    #[allow(unused)]
1147    pub fn pixels(&self) -> &IpcBytes {
1148        match &*self.0 {
1149            ImageData::RawData { pixels, .. } => pixels,
1150            ImageData::NativeTexture { .. } => unreachable!(),
1151        }
1152    }
1153}
1154
1155// Image data is provided to webrender directly from the BGRA8 shared memory.
1156// The `ExternalImageId` is the Arc pointer to ImageData.
1157mod external {
1158    use std::{collections::hash_map::Entry, sync::Arc};
1159
1160    use rustc_hash::FxHashMap;
1161    use webrender::{
1162        RenderApi,
1163        api::{
1164            DocumentId, ExternalImage, ExternalImageData, ExternalImageHandler, ExternalImageId, ExternalImageSource, ExternalImageType,
1165            ImageKey,
1166            units::{ImageDirtyRect, TexelRect},
1167        },
1168    };
1169    use zng_view_api::image::ImageTextureId;
1170
1171    use super::{Image, ImageData};
1172
1173    /// Implements [`ExternalImageHandler`].
1174    ///
1175    /// # Safety
1176    ///
1177    /// This is only safe if use with [`ImageUseMap`].
1178    pub(crate) struct WrImageCache {
1179        locked: Vec<Arc<ImageData>>,
1180    }
1181    impl WrImageCache {
1182        pub fn new_boxed() -> Box<dyn ExternalImageHandler> {
1183            Box::new(WrImageCache { locked: vec![] })
1184        }
1185    }
1186    impl ExternalImageHandler for WrImageCache {
1187        fn lock(&mut self, key: ExternalImageId, _channel_index: u8) -> ExternalImage {
1188            // SAFETY: this is safe because the Arc is kept alive in `ImageUseMap`.
1189            let img = unsafe {
1190                let ptr = key.0 as *const ImageData;
1191                Arc::increment_strong_count(ptr);
1192                Arc::<ImageData>::from_raw(ptr)
1193            };
1194
1195            self.locked.push(img); // keep alive in case the image is removed mid-use
1196
1197            match &**self.locked.last().unwrap() {
1198                ImageData::RawData { pixels, .. } => {
1199                    ExternalImage {
1200                        uv: TexelRect::invalid(), // `RawData` does not use `uv`.
1201                        source: ExternalImageSource::RawData(&pixels[..]),
1202                    }
1203                }
1204                ImageData::NativeTexture { uv, texture: id } => ExternalImage {
1205                    uv: *uv,
1206                    source: ExternalImageSource::NativeTexture(*id),
1207                },
1208            }
1209        }
1210
1211        fn unlock(&mut self, key: ExternalImageId, _channel_index: u8) {
1212            if let Some(i) = self.locked.iter().position(|d| ExternalImageId(Arc::as_ptr(d) as _) == key) {
1213                self.locked.swap_remove(i);
1214            } else {
1215                debug_assert!(false);
1216            }
1217        }
1218    }
1219
1220    impl Image {
1221        fn external_id(&self) -> ExternalImageId {
1222            ExternalImageId(Arc::as_ptr(&self.0) as u64)
1223        }
1224
1225        fn data(&self) -> webrender::api::ImageData {
1226            webrender::api::ImageData::External(ExternalImageData {
1227                id: self.external_id(),
1228                channel_index: 0,
1229                image_type: ExternalImageType::Buffer,
1230                normalized_uvs: false,
1231            })
1232        }
1233    }
1234
1235    /// Track and manage images used in a renderer.
1236    ///
1237    /// The renderer must use [`WrImageCache`] as the external image source.
1238    #[derive(Default)]
1239    pub(crate) struct ImageUseMap {
1240        id_tex: FxHashMap<ExternalImageId, (ImageTextureId, Image)>,
1241        tex_id: FxHashMap<ImageTextureId, ExternalImageId>,
1242    }
1243    impl ImageUseMap {
1244        pub fn new_use(&mut self, image: &Image, document_id: DocumentId, api: &mut RenderApi) -> ImageTextureId {
1245            let id = image.external_id();
1246            match self.id_tex.entry(id) {
1247                Entry::Occupied(e) => e.get().0,
1248                Entry::Vacant(e) => {
1249                    let key = api.generate_image_key();
1250                    let tex_id = ImageTextureId::from_raw(key.1);
1251                    e.insert((tex_id, image.clone())); // keep the image Arc alive, we expect this in `WrImageCache`.
1252                    self.tex_id.insert(tex_id, id);
1253
1254                    let mut txn = webrender::Transaction::new();
1255                    txn.add_image(key, image.descriptor(), image.data(), None);
1256                    api.send_transaction(document_id, txn);
1257
1258                    tex_id
1259                }
1260            }
1261        }
1262
1263        /// Returns if needs to update.
1264        pub fn update_use(&mut self, texture_id: ImageTextureId, image: &Image, document_id: DocumentId, api: &mut RenderApi) {
1265            if let Entry::Occupied(mut e) = self.tex_id.entry(texture_id) {
1266                let id = image.external_id();
1267                if *e.get() != id {
1268                    let prev_id = e.insert(id);
1269                    self.id_tex.remove(&prev_id).unwrap();
1270                    self.id_tex.insert(id, (texture_id, image.clone()));
1271
1272                    let mut txn = webrender::Transaction::new();
1273                    txn.update_image(
1274                        ImageKey(api.get_namespace_id(), texture_id.get()),
1275                        image.descriptor(),
1276                        image.data(),
1277                        &ImageDirtyRect::All,
1278                    );
1279                    api.send_transaction(document_id, txn);
1280                }
1281            }
1282        }
1283
1284        pub fn delete(&mut self, texture_id: ImageTextureId, document_id: DocumentId, api: &mut RenderApi) {
1285            if let Some(id) = self.tex_id.remove(&texture_id) {
1286                let _img = self.id_tex.remove(&id); // remove but keep alive until the transaction is done.
1287                let mut txn = webrender::Transaction::new();
1288                txn.delete_image(ImageKey(api.get_namespace_id(), texture_id.get()));
1289                api.send_transaction(document_id, txn);
1290            }
1291        }
1292    }
1293}
1294pub(crate) use external::{ImageUseMap, WrImageCache};
1295
1296mod capture {
1297    use std::sync::Arc;
1298
1299    use webrender::api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat};
1300    use zng_txt::formatx;
1301    use zng_unit::{Factor, PxRect};
1302    use zng_view_api::{
1303        Event,
1304        image::{ImageDataFormat, ImageId, ImageLoadedData, ImageMaskMode, ImagePpi, ImageRequest},
1305        ipc::IpcBytes,
1306        window::{FrameId, WindowId},
1307    };
1308
1309    use crate::{
1310        AppEvent,
1311        image_cache::{Image, ImageData},
1312    };
1313
1314    use super::ImageCache;
1315
1316    impl ImageCache {
1317        /// Create frame_image for an `Api::frame_image` request.
1318        pub fn frame_image(
1319            &mut self,
1320            gl: &dyn gleam::gl::Gl,
1321            rect: PxRect,
1322            window_id: WindowId,
1323            frame_id: FrameId,
1324            scale_factor: Factor,
1325            mask: Option<ImageMaskMode>,
1326        ) -> ImageId {
1327            if frame_id == FrameId::INVALID {
1328                let id = self.image_id_gen.incr();
1329                let _ = self.app_sender.send(AppEvent::Notify(Event::ImageLoadError {
1330                    image: id,
1331                    error: formatx!("no frame rendered in window `{window_id:?}`"),
1332                }));
1333                let _ = self.app_sender.send(AppEvent::Notify(Event::FrameImageReady {
1334                    window: window_id,
1335                    frame: frame_id,
1336                    image: id,
1337                    selection: rect,
1338                }));
1339                return id;
1340            }
1341
1342            let data = self.frame_image_data(gl, rect, scale_factor, mask);
1343
1344            let id = data.id;
1345
1346            let _ = self.app_sender.send(AppEvent::ImageLoaded(data));
1347            let _ = self.app_sender.send(AppEvent::Notify(Event::FrameImageReady {
1348                window: window_id,
1349                frame: frame_id,
1350                image: id,
1351                selection: rect,
1352            }));
1353
1354            id
1355        }
1356
1357        /// Create frame_image for a capture request in the FrameRequest.
1358        pub fn frame_image_data(
1359            &mut self,
1360            gl: &dyn gleam::gl::Gl,
1361            rect: PxRect,
1362            scale_factor: Factor,
1363            mask: Option<ImageMaskMode>,
1364        ) -> ImageLoadedData {
1365            let data = self.frame_image_data_impl(gl, rect, scale_factor, mask);
1366
1367            let flags = if data.is_opaque {
1368                ImageDescriptorFlags::IS_OPAQUE
1369            } else {
1370                ImageDescriptorFlags::empty()
1371            };
1372
1373            self.images.insert(
1374                data.id,
1375                Image(Arc::new(ImageData::RawData {
1376                    size: data.size,
1377                    pixels: data.pixels.clone(),
1378                    descriptor: ImageDescriptor::new(
1379                        data.size.width.0,
1380                        data.size.height.0,
1381                        if data.is_mask { ImageFormat::R8 } else { ImageFormat::BGRA8 },
1382                        flags,
1383                    ),
1384                    ppi: data.ppi,
1385                })),
1386            );
1387
1388            data
1389        }
1390
1391        fn frame_image_data_impl(
1392            &mut self,
1393            gl: &dyn gleam::gl::Gl,
1394            rect: PxRect,
1395            scale_factor: Factor,
1396            mask: Option<ImageMaskMode>,
1397        ) -> ImageLoadedData {
1398            let format = match gl.get_type() {
1399                gleam::gl::GlType::Gl => gleam::gl::BGRA,
1400                gleam::gl::GlType::Gles => gleam::gl::RGBA,
1401            };
1402            let pixels_flipped = gl.read_pixels(
1403                rect.origin.x.0,
1404                rect.origin.y.0,
1405                rect.size.width.0,
1406                rect.size.height.0,
1407                format,
1408                gleam::gl::UNSIGNED_BYTE,
1409            );
1410            let mut buf = vec![0u8; pixels_flipped.len()];
1411            assert_eq!(rect.size.width.0 as usize * rect.size.height.0 as usize * 4, buf.len());
1412            let stride = 4 * rect.size.width.0 as usize;
1413            for (px, buf) in pixels_flipped.chunks_exact(stride).rev().zip(buf.chunks_exact_mut(stride)) {
1414                buf.copy_from_slice(px);
1415            }
1416
1417            if let Some(mask) = mask {
1418                if format == gleam::gl::BGRA {
1419                    for bgra in buf.chunks_exact_mut(4) {
1420                        bgra.swap(0, 3);
1421                    }
1422                }
1423                let (pixels, size, ppi, is_opaque, is_mask) = Self::convert_decoded(
1424                    image::DynamicImage::ImageRgba8(
1425                        image::ImageBuffer::from_raw(rect.size.width.0 as u32, rect.size.height.0 as u32, buf).unwrap(),
1426                    ),
1427                    Some(mask),
1428                );
1429
1430                let id = self.add(ImageRequest {
1431                    format: ImageDataFormat::A8 { size },
1432                    data: pixels.clone(),
1433                    max_decoded_len: u64::MAX,
1434                    downscale: None,
1435                    mask: Some(mask),
1436                });
1437
1438                ImageLoadedData {
1439                    id,
1440                    size,
1441                    ppi,
1442                    is_opaque,
1443                    is_mask,
1444                    pixels,
1445                }
1446            } else {
1447                if format == gleam::gl::RGBA {
1448                    for rgba in buf.chunks_exact_mut(4) {
1449                        rgba.swap(0, 3);
1450                    }
1451                }
1452
1453                let is_opaque = buf.chunks_exact(4).all(|bgra| bgra[3] == 255);
1454
1455                let data = IpcBytes::from_vec(buf);
1456                let ppi = 96.0 * scale_factor.0;
1457                let ppi = Some(ImagePpi::splat(ppi));
1458                let size = rect.size;
1459
1460                let id = self.add(ImageRequest {
1461                    format: ImageDataFormat::Bgra8 { size, ppi },
1462                    data: data.clone(),
1463                    max_decoded_len: u64::MAX,
1464                    downscale: None,
1465                    mask,
1466                });
1467
1468                ImageLoadedData {
1469                    id,
1470                    size,
1471                    ppi,
1472                    is_opaque,
1473                    pixels: data,
1474                    is_mask: false,
1475                }
1476            }
1477        }
1478    }
1479}
1480
1481fn luminance(rgb: &[u8]) -> u8 {
1482    let r = rgb[0] as f32 / 255.0;
1483    let g = rgb[1] as f32 / 255.0;
1484    let b = rgb[2] as f32 / 255.0;
1485
1486    let l = r * 0.2126 + g * 0.7152 + b * 0.0722;
1487    (l * 255.0) as u8
1488}
1489
1490fn luminance_16(rgb: &[u16]) -> u8 {
1491    let max = u16::MAX as f32;
1492    let r = rgb[0] as f32 / max;
1493    let g = rgb[1] as f32 / max;
1494    let b = rgb[2] as f32 / max;
1495
1496    let l = r * 0.2126 + g * 0.7152 + b * 0.0722;
1497    (l * 255.0) as u8
1498}
1499
1500fn luminance_f32(rgb: &[f32]) -> u8 {
1501    let r = rgb[0];
1502    let g = rgb[1];
1503    let b = rgb[2];
1504
1505    let l = r * 0.2126 + g * 0.7152 + b * 0.0722;
1506    (l * 255.0) as u8
1507}