image.rs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use async_trait::async_trait;
  19. use image::ImageReader;
  20. use miniquad::TextureFormat;
  21. use parking_lot::Mutex as SyncMutex;
  22. use rand::{rngs::OsRng, Rng};
  23. use std::{io::Cursor, sync::Arc};
  24. use tracing::instrument;
  25. use crate::{
  26. gfx::{
  27. gfxtag, DrawCall, DrawInstruction, DrawMesh, ManagedTexturePtr, Rectangle, RenderApi,
  28. Renderer,
  29. },
  30. mesh::{MeshBuilder, MeshInfo, COLOR_WHITE},
  31. prop::{BatchGuardPtr, PropertyAtomicGuard, PropertyRect, PropertyStr, PropertyUint32, Role},
  32. scene::{Pimpl, SceneNodeWeak},
  33. ExecutorPtr,
  34. };
  35. use super::{DrawUpdate, OnModify, RedrawTrigger, UIObject};
  36. pub type ImagePtr = Arc<Image>;
  37. pub struct Image {
  38. node: SceneNodeWeak,
  39. renderer: Renderer,
  40. redraw: RedrawTrigger,
  41. tasks: SyncMutex<Vec<smol::Task<()>>>,
  42. texture: SyncMutex<Option<ManagedTexturePtr>>,
  43. dc_key: u64,
  44. rect: PropertyRect,
  45. uv: PropertyRect,
  46. z_index: PropertyUint32,
  47. priority: PropertyUint32,
  48. path: PropertyStr,
  49. /// Cached draw instructions. `None` means stale.
  50. draw_cache: SyncMutex<Option<Vec<DrawInstruction>>>,
  51. }
  52. impl Image {
  53. pub async fn new(node: SceneNodeWeak, renderer: Renderer, redraw: RedrawTrigger) -> Pimpl {
  54. let node_ref = &node.upgrade().unwrap();
  55. let rect = PropertyRect::wrap(node_ref, Role::Internal, "rect").unwrap();
  56. let uv = PropertyRect::wrap(node_ref, Role::Internal, "uv").unwrap();
  57. let z_index = PropertyUint32::wrap(node_ref, Role::Internal, "z_index", 0).unwrap();
  58. let priority = PropertyUint32::wrap(node_ref, Role::Internal, "priority", 0).unwrap();
  59. let path = PropertyStr::wrap(node_ref, Role::Internal, "path", 0).unwrap();
  60. let self_ = Arc::new(Self {
  61. node,
  62. renderer,
  63. redraw,
  64. tasks: SyncMutex::new(vec![]),
  65. texture: SyncMutex::new(None),
  66. dc_key: OsRng.gen(),
  67. rect,
  68. uv,
  69. z_index,
  70. priority,
  71. path,
  72. draw_cache: SyncMutex::new(None),
  73. });
  74. Pimpl::Image(self_)
  75. }
  76. async fn reload(self_: Arc<Self>, _batch: BatchGuardPtr) {
  77. let texture = self_.load_texture();
  78. *self_.texture.lock() = Some(texture);
  79. *self_.draw_cache.lock() = None;
  80. self_.redraw.trigger();
  81. }
  82. fn load_texture(&self) -> ManagedTexturePtr {
  83. let path = self.path.get();
  84. // TODO we should NOT use panic here
  85. let data = Arc::new(SyncMutex::new(vec![]));
  86. let data2 = data.clone();
  87. miniquad::fs::load_file(&path.clone(), move |res| match res {
  88. Ok(res) => *data2.lock() = res,
  89. Err(e) => {
  90. error!(target: "ui::image", "Unable to open image: {path}: {e}");
  91. panic!("Resource not found! {e}");
  92. }
  93. });
  94. let data = std::mem::take(&mut *data.lock());
  95. let img =
  96. ImageReader::new(Cursor::new(data)).with_guessed_format().unwrap().decode().unwrap();
  97. let img = img.to_rgba8();
  98. //let img = image::ImageReader::open(path).unwrap().decode().unwrap().to_rgba8();
  99. let width = img.width() as u16;
  100. let height = img.height() as u16;
  101. let bmp = img.into_raw();
  102. self.renderer.new_texture(width, height, bmp, TextureFormat::RGBA8, gfxtag!("img"))
  103. }
  104. /// Called whenever any property changes.
  105. fn regen_mesh(&self) -> MeshInfo {
  106. let rect = self.rect.get();
  107. let uv = self.uv.get();
  108. let mesh_rect = Rectangle::from([0., 0., rect.w, rect.h]);
  109. let mut mesh = MeshBuilder::new(gfxtag!("img"));
  110. mesh.draw_box(&mesh_rect, COLOR_WHITE, &uv);
  111. mesh.alloc(&self.renderer)
  112. }
  113. fn get_draw_calls(
  114. &self,
  115. atom: &mut PropertyAtomicGuard,
  116. parent_rect: Rectangle,
  117. ) -> Option<DrawUpdate> {
  118. // Rect property is its own memo: compare before/after eval.
  119. let prev_rect = self.rect.get();
  120. self.rect.eval(atom, &parent_rect).ok()?;
  121. let rect = self.rect.get();
  122. let rect_changed = rect != prev_rect;
  123. self.uv.eval(atom, &rect).ok()?;
  124. // Mesh geometry depends on the rect; compute under the lock so a
  125. // concurrent invalidation lands before or after, never between.
  126. let mut cache = self.draw_cache.lock();
  127. if cache.is_none() || rect_changed {
  128. let mesh = self.regen_mesh();
  129. let texture = self.texture.lock().clone().expect("Node missing texture_id!");
  130. let mesh = DrawMesh {
  131. vertex_buffer: mesh.vertex_buffer,
  132. index_buffer: mesh.index_buffer,
  133. textures: Some(vec![texture]),
  134. num_elements: mesh.num_elements,
  135. };
  136. *cache = Some(vec![DrawInstruction::Move(rect.pos()), DrawInstruction::Draw(mesh)]);
  137. }
  138. let instrs = cache.clone().unwrap();
  139. drop(cache);
  140. Some(DrawUpdate {
  141. key: self.dc_key,
  142. draw_calls: vec![(
  143. self.dc_key,
  144. DrawCall::new(instrs, vec![], self.z_index.get(), "img"),
  145. )],
  146. })
  147. }
  148. }
  149. #[async_trait]
  150. impl UIObject for Image {
  151. fn priority(&self) -> u32 {
  152. self.priority.get()
  153. }
  154. fn init(&self) {
  155. *self.texture.lock() = Some(self.load_texture());
  156. }
  157. async fn start(self: Arc<Self>, ex: ExecutorPtr) {
  158. let me = Arc::downgrade(&self);
  159. let mut on_modify = OnModify::new(ex, self.node.clone(), me.clone());
  160. // Invalidate the cache, then request a pass. Internal-role echoes
  161. // (the pass's own evals) are skipped.
  162. on_modify.when_change_external(self.rect.prop(), |self_, _| async move {
  163. *self_.draw_cache.lock() = None;
  164. self_.redraw.trigger();
  165. });
  166. on_modify.when_change_external(self.uv.prop(), |self_, _| async move {
  167. *self_.draw_cache.lock() = None;
  168. self_.redraw.trigger();
  169. });
  170. on_modify.when_change_external(self.z_index.prop(), |self_, _| async move {
  171. *self_.draw_cache.lock() = None;
  172. self_.redraw.trigger();
  173. });
  174. on_modify.when_change(self.path.prop(), Self::reload);
  175. *self.tasks.lock() = on_modify.tasks;
  176. }
  177. fn stop(&self) {
  178. self.tasks.lock().clear();
  179. *self.draw_cache.lock() = None;
  180. *self.texture.lock() = None;
  181. }
  182. #[instrument(target = "ui::button")]
  183. async fn draw(
  184. &self,
  185. parent_rect: Rectangle,
  186. atom: &mut PropertyAtomicGuard,
  187. ) -> Option<DrawUpdate> {
  188. self.get_draw_calls(atom, parent_rect)
  189. }
  190. }
  191. impl Drop for Image {
  192. fn drop(&mut self) {
  193. let atom = self.renderer.make_guard(gfxtag!("Image::drop"));
  194. self.renderer.replace_draw_calls(vec![(self.dc_key, Default::default())]);
  195. }
  196. }
  197. impl std::fmt::Debug for Image {
  198. fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
  199. write!(f, "{:?}", self.node.upgrade().unwrap())
  200. }
  201. }