ul_next/
image_source.rs

1//! User-defined image source to display custom images on a web-page.
2
3use std::sync::Arc;
4
5use crate::{bitmap::Bitmap, error::CreationError, string::UlString, Library, Rect};
6
7/// User-defined image source to display custom images on a web-page.
8///
9/// This API allows you to composite your own images into a web-page. This is useful for displaying
10/// in-game textures, external image assets, or other custom content.
11///
12/// ## ImageSource File Format
13///
14/// To use an ImageSource, you must first create an `.imgsrc` file containing a string identifying
15/// the image source. This string will be used to lookup the ImageSource from ImageSourceProvider
16/// when it is loaded on a web-page.
17///
18/// The file format is as follows:
19///
20/// ```txt
21/// IMGSRC-V1
22/// <identifier>
23/// ```
24///
25/// You can use the `.imgsrc` file anywhere in your web-page that typically accepts an image URL.
26/// For example:
27///
28/// ```html
29/// <img src="my_custom_image.imgsrc" />
30/// ```
31///
32/// ## Creating from a GPU Texture
33///
34/// To composite your own GPU texture on a web-page, you should first reserve a texture ID from
35/// [`GpuDriver::next_texture_id`][crate::gpu_driver::GpuDriver::next_texture_id] using
36/// and then create an ImageSource from that texture ID
37/// using [`ImageSource::create_from_texture`][ImageSource::create_from_texture].
38///
39/// Next, you should register the [`ImageSource`] with [`image_source_provider::add_image_source`]
40/// using the identifier from the `.imgsrc` file.
41///
42/// When the image element is drawn on the web-page, the library will draw geometry using the
43/// specified texture ID and UV coordinates. You should bind your own texture when the specified
44/// texture ID is used.
45///
46/// Note: If the GPU renderer is not enabled for the View or pixel data is needed for other
47///       purposes, the library will sample the backing bitmap instead.
48///
49/// ## Creating from a Bitmap
50///
51/// To composite your own bitmap on a web-page, you should create an [`ImageSource`] from a bitmap
52/// using [`ImageSource::create_from_bitmap`][ImageSource::create_from_bitmap].
53///
54/// Next, you should register the [`ImageSource`] with [`image_source_provider::add_image_source`]
55/// using the identifier from the `.imgsrc` file.
56///
57/// When the image element is drawn on the web-page, the library will sample this bitmap directly.
58///
59/// ## Invalidating Images
60///
61/// If you modify the texture or bitmap pixels after creating the ImageSource, you should call
62/// [`ImageSource::invalidate`] to notify the library that the image should be redrawn.
63pub struct ImageSource {
64    lib: Arc<Library>,
65    internal: ul_sys::ULImageSource,
66}
67
68impl ImageSource {
69    /// Create an image source from a GPU texture with optional backing bitmap.
70    /// # Arguments
71    /// * `lib` - The ultralight library.
72    /// * `width` - The width of the texture in pixels (used for layout).
73    /// * `height` - The height of the texture in pixels (used for layout).
74    /// * `texture_id` - The GPU texture identifier to bind when drawing the quad for this image.
75    ///                  This should be non-zero and obtained from
76    ///                  [`GpuDriver::next_texture_id`][crate::gpu_driver::GpuDriver::next_texture_id].
77    /// * `rect` - The rectangle in UV coordinates to sample from the texture.
78    /// * `bitmap` - Optional backing bitmap for the texture. This is used when drawing
79    ///              the image using the CPU renderer or when pixel data is needed for other
80    ///              purposes. You should update this bitmap when the texture changes.
81    pub fn create_from_texture(
82        lib: Arc<Library>,
83        width: u32,
84        height: u32,
85        texture_id: u32,
86        rect: Rect<f32>,
87        bitmap: Option<Bitmap>,
88    ) -> Result<ImageSource, CreationError> {
89        let internal = unsafe {
90            lib.ultralight().ulCreateImageSourceFromTexture(
91                width,
92                height,
93                texture_id,
94                ul_sys::ULRect {
95                    left: rect.left,
96                    top: rect.top,
97                    right: rect.right,
98                    bottom: rect.bottom,
99                },
100                bitmap.map(|b| b.to_ul()).unwrap_or(std::ptr::null_mut()),
101            )
102        };
103        if internal.is_null() {
104            Err(CreationError::NullReference)
105        } else {
106            Ok(Self { lib, internal })
107        }
108    }
109
110    /// Create an image source from a bitmap.
111    /// # Arguments
112    /// * `lib` - The ultralight library.
113    /// * `bitmap` - The bitmap to sample from when drawing the image.
114    pub fn create_from_bitmap(
115        lib: Arc<Library>,
116        bitmap: Bitmap,
117    ) -> Result<ImageSource, CreationError> {
118        let internal = unsafe {
119            lib.ultralight()
120                .ulCreateImageSourceFromBitmap(bitmap.to_ul())
121        };
122        if internal.is_null() {
123            Err(CreationError::NullReference)
124        } else {
125            Ok(Self { lib, internal })
126        }
127    }
128
129    /// Invalidate the image source, notifying the library that the image has changed
130    /// and should be redrawn
131    pub fn invalidate(&self) {
132        unsafe {
133            self.lib.ultralight().ulImageSourceInvalidate(self.internal);
134        }
135    }
136}
137
138impl Drop for ImageSource {
139    fn drop(&mut self) {
140        unsafe {
141            self.lib.ultralight().ulDestroyImageSource(self.internal);
142        }
143    }
144}
145
146/// Maps image sources to string identifiers.
147///
148/// This is used to lookup ImageSource instances when they are requested by a web-page.
149pub mod image_source_provider {
150    use crate::Library;
151    use std::sync::Arc;
152
153    /// Add an image source to the provider.
154    pub fn add_image_source(
155        id: &str,
156        image_source: &super::ImageSource,
157    ) -> Result<(), super::CreationError> {
158        unsafe {
159            let id_str = super::UlString::from_str(image_source.lib.clone(), id)?;
160            image_source
161                .lib
162                .ultralight()
163                .ulImageSourceProviderAddImageSource(id_str.to_ul(), image_source.internal);
164        }
165        Ok(())
166    }
167
168    /// Remove an image source from the provider.
169    pub fn remove_image_source(lib: &Arc<Library>, id: &str) -> Result<(), super::CreationError> {
170        unsafe {
171            let id_str = super::UlString::from_str(lib.clone(), id)?;
172            lib.ultralight()
173                .ulImageSourceProviderRemoveImageSource(id_str.to_ul());
174        }
175        Ok(())
176    }
177}