Skip to main content

ferroui_base/rendering/composition/
loaded_image_surface.rs

1use super::server::{ServerLoadedImageSurface, ServerObjectId};
2use super::{CompositionObject, Compositor, ICompositionObject, ICompositionSurface};
3use crate::media::imaging::Bitmap;
4use crate::Size;
5use std::any::Any;
6use std::io::Read;
7use std::rc::Rc;
8
9/// The outcome of loading a [`LoadedImageSurface`] (WinUI
10/// `Microsoft.UI.Xaml.Media.LoadedImageSourceLoadStatus`).
11#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
12pub enum LoadedImageSourceLoadStatus {
13    /// The image was loaded.
14    #[default]
15    Success,
16    /// The image could not be read from its source.
17    NetworkError,
18    /// The data is not an image the platform can decode.
19    InvalidFormat,
20    /// Another error.
21    Other,
22}
23
24/// A composition surface whose content is an image (WinUI
25/// `Microsoft.UI.Xaml.Media.LoadedImageSurface`), here backed by a
26/// [`Bitmap`].
27///
28/// The surface holds a reference to the platform bitmap; the server-side
29/// surface holds another one until it is disposed.
30///
31/// Deviation (winui-composition.md): WinUI loads the image asynchronously
32/// from a URI or a stream and raises `LoadCompleted`; here a surface is made
33/// from a bitmap, or loaded from a stream at once, and the outcome is
34/// [`load_status`](Self::load_status). There is no `LoadCompleted` event and
35/// no decode size.
36pub struct LoadedImageSurface {
37    object: CompositionObject,
38    bitmap: Option<Bitmap>,
39    load_status: LoadedImageSourceLoadStatus,
40}
41
42impl LoadedImageSurface {
43    fn create(compositor: &Rc<Compositor>, bitmap: Option<Bitmap>, status: LoadedImageSourceLoadStatus) -> Rc<Self> {
44        let server_bitmap = bitmap.as_ref().map(|bitmap| bitmap.platform_impl().clone_ref());
45        let server = compositor.create_server_object(move |_, _| ServerLoadedImageSurface::new(server_bitmap));
46        Rc::new(LoadedImageSurface {
47            object: CompositionObject::new(compositor, Some(server)),
48            bitmap,
49            load_status: status,
50        })
51    }
52
53    /// A surface that shows `bitmap`. The surface takes another reference to
54    /// the platform bitmap: disposing `bitmap` afterwards does not affect it.
55    pub fn from_bitmap(compositor: &Rc<Compositor>, bitmap: &Bitmap) -> Rc<LoadedImageSurface> {
56        Self::create(compositor, Some(Bitmap::from_ref(bitmap.platform_impl())), LoadedImageSourceLoadStatus::Success)
57    }
58
59    /// `StartLoadFromStream`: a surface with the image decoded from
60    /// `stream`. The image is decoded at once; a stream that cannot be read
61    /// or decoded gives an empty surface whose
62    /// [`load_status`](Self::load_status) says why.
63    pub fn start_load_from_stream(compositor: &Rc<Compositor>, stream: &mut dyn Read) -> Rc<LoadedImageSurface> {
64        match Bitmap::from_stream(stream) {
65            Ok(bitmap) => Self::create(compositor, Some(bitmap), LoadedImageSourceLoadStatus::Success),
66            Err(error) => {
67                let status = match error.kind() {
68                    std::io::ErrorKind::InvalidData | std::io::ErrorKind::Unsupported => {
69                        LoadedImageSourceLoadStatus::InvalidFormat
70                    }
71                    std::io::ErrorKind::NotFound
72                    | std::io::ErrorKind::PermissionDenied
73                    | std::io::ErrorKind::UnexpectedEof
74                    | std::io::ErrorKind::ConnectionReset
75                    | std::io::ErrorKind::TimedOut => LoadedImageSourceLoadStatus::NetworkError,
76                    _ => LoadedImageSourceLoadStatus::Other,
77                };
78                Self::create(compositor, None, status)
79            }
80        }
81    }
82
83    pub fn compositor(&self) -> &Rc<Compositor> {
84        self.object.compositor()
85    }
86
87    /// The embedded `CompositionObject`.
88    pub fn object(&self) -> &CompositionObject {
89        &self.object
90    }
91
92    /// The id of the server-side surface.
93    pub fn server(&self) -> ServerObjectId {
94        self.object.required_server()
95    }
96
97    /// The outcome of the load.
98    pub fn load_status(&self) -> LoadedImageSourceLoadStatus {
99        self.load_status
100    }
101
102    /// The bitmap the surface shows, `None` when the image did not load or
103    /// the surface is disposed.
104    pub fn bitmap(&self) -> Option<&Bitmap> {
105        self.bitmap.as_ref().filter(|bitmap| bitmap.platform_impl().is_alive())
106    }
107
108    /// The size of the image in device independent pixels (`NaturalSize`):
109    /// what a surface brush lays out. Empty when the image did not load.
110    pub fn natural_size(&self) -> Size {
111        self.bitmap().map(|bitmap| bitmap.size()).unwrap_or_default()
112    }
113
114    /// The size of the decoded image in device independent pixels
115    /// (`DecodedSize`): the natural size, as the image is decoded at its
116    /// full size.
117    pub fn decoded_size(&self) -> Size {
118        self.natural_size()
119    }
120
121    /// The size of the decoded image in pixels (`DecodedPhysicalSize`).
122    pub fn decoded_physical_size(&self) -> Size {
123        self.bitmap()
124            .map(|bitmap| {
125                let size = bitmap.pixel_size();
126                Size::new(size.width as f64, size.height as f64)
127            })
128            .unwrap_or_default()
129    }
130
131    pub fn is_disposed(&self) -> bool {
132        self.object.is_disposed()
133    }
134
135    /// Releases the surface: its server side releases the bitmap with the
136    /// next batch.
137    pub fn dispose(&self) {
138        if let Some(bitmap) = &self.bitmap {
139            bitmap.dispose();
140        }
141        self.object.dispose();
142    }
143}
144
145impl ICompositionObject for LoadedImageSurface {
146    fn server(&self) -> ServerObjectId {
147        self.object.required_server()
148    }
149
150    fn as_any(&self) -> &dyn Any {
151        self
152    }
153
154    fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
155        self
156    }
157}
158
159impl ICompositionSurface for LoadedImageSurface {}