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}