Skip to main content

ferroui_skia/
writeable_bitmap_impl.rs

1use crate::gpu::{drawable_image, needs_mipmaps, ISkiaGrContext};
2use crate::helpers::image_saving_helper;
3use crate::i_drawable_bitmap_impl::IDrawableBitmapImpl;
4use crate::immutable_bitmap::{decode_bitmap, decode_bitmap_to_size, SendBitmap};
5use crate::skia_platform::SkiaPlatform;
6use crate::skia_sharp_extensions::{
7    to_alpha_format, to_pixel_format, to_pixel_format_opt, to_sk_alpha_type, to_sk_color_type_or_panic,
8};
9use ferroui_base::media::imaging::{BitmapEncoderOptions, BitmapInterpolationMode};
10use ferroui_base::platform::{
11    AlphaFormat, IBitmapImpl, ILockedFramebuffer, IReadableBitmapImpl, IWriteableBitmapImpl, PixelFormat,
12};
13use ferroui_base::{PixelSize, Vector};
14use skia_safe::canvas::SrcRectConstraint;
15use skia_safe::{images, Bitmap, Canvas, Color, Data, Image, ImageInfo, Paint, Rect, SamplingOptions};
16use std::any::Any;
17use std::cell::RefCell;
18use std::io::{self, Read, Write};
19use std::rc::Rc;
20use std::sync::atomic::{AtomicI32, Ordering};
21use std::sync::{Arc, Mutex, Weak};
22
23/// Skia based writeable bitmap.
24///
25/// The UI thread writes the pixels and the render thread draws them: the
26/// pixels and the image drawn from them are under one lock.
27pub struct WriteableBitmapImpl {
28    weak_self: Weak<WriteableBitmapImpl>,
29    pixels: Mutex<Pixels>,
30    dpi: Vector,
31    pixel_size: PixelSize,
32    version: AtomicI32,
33}
34
35struct Pixels {
36    bitmap: Option<SendBitmap>,
37    /// The snapshot the bitmap is drawn from, until the pixels change.
38    image: Option<Image>,
39}
40
41impl WriteableBitmapImpl {
42    fn from_bitmap(bitmap: Bitmap, pixel_size: PixelSize, dpi: Vector) -> Arc<Self> {
43        Arc::new_cyclic(|weak_self| Self {
44            weak_self: weak_self.clone(),
45            pixels: Mutex::new(Pixels { bitmap: Some(SendBitmap(bitmap)), image: None }),
46            dpi,
47            pixel_size,
48            version: AtomicI32::new(1),
49        })
50    }
51
52    /// Creates a writeable bitmap from the given stream.
53    pub fn from_stream(stream: &mut dyn Read) -> io::Result<Arc<Self>> {
54        let bitmap = decode_bitmap(stream)?;
55        let pixel_size = PixelSize::new(bitmap.width(), bitmap.height());
56
57        Ok(Self::from_bitmap(bitmap, pixel_size, SkiaPlatform::default_dpi()))
58    }
59
60    /// Creates a writeable bitmap from a stream, decoded so that its width
61    /// (`horizontal`) or height is `decode_size`.
62    pub fn from_stream_to_size(
63        stream: &mut dyn Read,
64        decode_size: i32,
65        horizontal: bool,
66        interpolation_mode: BitmapInterpolationMode,
67    ) -> io::Result<Arc<Self>> {
68        let bitmap = decode_bitmap_to_size(stream, decode_size, horizontal, interpolation_mode)?;
69        let pixel_size = PixelSize::new(bitmap.width(), bitmap.height());
70
71        Ok(Self::from_bitmap(bitmap, pixel_size, SkiaPlatform::default_dpi()))
72    }
73
74    /// Creates a writeable bitmap with the given size, DPI and formats. The
75    /// pixels start out transparent.
76    ///
77    /// # Panics
78    /// Panics when the pixel format is unknown to Skia or the pixels cannot
79    /// be allocated.
80    pub fn new(size: PixelSize, dpi: Vector, format: PixelFormat, alpha_format: AlphaFormat) -> Arc<Self> {
81        let color_type = to_sk_color_type_or_panic(format);
82        let alpha_type = to_sk_alpha_type(alpha_format);
83
84        let nfo = ImageInfo::new((size.width, size.height), color_type, alpha_type, None);
85
86        let mut bitmap = Bitmap::new();
87        if !bitmap.try_alloc_pixels_info(&nfo, None) {
88            panic!("Unable to allocate a {}x{} bitmap", size.width, size.height);
89        }
90        bitmap.erase_color(Color::TRANSPARENT);
91
92        Self::from_bitmap(bitmap, size, dpi)
93    }
94
95    fn with_bitmap<R>(&self, f: impl FnOnce(&Bitmap) -> R) -> R {
96        let pixels = self.pixels.lock().unwrap();
97        f(pixels.bitmap.as_ref().expect("WriteableBitmapImpl has been disposed"))
98    }
99
100    fn snapshot_of(bitmap: &Bitmap) -> Image {
101        let pixmap = bitmap.pixmap();
102        let bytes = pixmap.bytes().unwrap_or(&[]);
103        images::raster_from_data(bitmap.info(), Data::new_copy(bytes), bitmap.row_bytes())
104            .unwrap_or_else(|| panic!("Unable to create an image from the bitmap pixels"))
105    }
106
107    /// Gets a snapshot of the bitmap: an image holding a copy of the current
108    /// pixels.
109    pub fn get_snapshot(&self) -> Image {
110        self.with_bitmap(Self::snapshot_of)
111    }
112}
113
114impl IBitmapImpl for WriteableBitmapImpl {
115    fn dpi(&self) -> Vector {
116        self.dpi
117    }
118
119    fn pixel_size(&self) -> PixelSize {
120        self.pixel_size
121    }
122
123    fn version(&self) -> i32 {
124        self.version.load(Ordering::SeqCst)
125    }
126
127    fn save(&self, stream: &mut dyn Write, options: &BitmapEncoderOptions) -> io::Result<()> {
128        let image = self.get_snapshot();
129        image_saving_helper::save_image(&image, stream, options)
130    }
131
132    fn dispose(&self) {
133        let mut pixels = self.pixels.lock().unwrap();
134        pixels.image.take();
135        pixels.bitmap.take();
136    }
137
138    fn as_any(&self) -> &dyn Any {
139        self
140    }
141
142    fn as_readable_bitmap(&self) -> Option<&dyn IReadableBitmapImpl> {
143        Some(self)
144    }
145}
146
147impl IDrawableBitmapImpl for WriteableBitmapImpl {
148    fn draw(
149        &self,
150        gr_context: Option<&dyn ISkiaGrContext>,
151        canvas: &Canvas,
152        source_rect: &Rect,
153        dest_rect: &Rect,
154        sampling_options: SamplingOptions,
155        paint: &Paint,
156    ) {
157        let image = {
158            let mut pixels = self.pixels.lock().unwrap();
159            if pixels.image.is_none() {
160                // NOTE: this does a snapshot of the bitmap. If the canvas is
161                // not GPU-backed we might want to avoid that by force-sharing
162                // the pixel data with the bitmap, but that would require
163                // manual pixel buffer management.
164                let snapshot =
165                    Self::snapshot_of(pixels.bitmap.as_ref().expect("WriteableBitmapImpl has been disposed"));
166                pixels.image = Some(snapshot);
167            }
168            pixels.image.clone()
169        };
170
171        if let Some(image) = image {
172            canvas.draw_image_rect_with_sampling_options(
173                drawable_image(gr_context, image, needs_mipmaps(&sampling_options)),
174                Some((source_rect, SrcRectConstraint::Fast)),
175                dest_rect,
176                sampling_options,
177                paint,
178            );
179        }
180    }
181}
182
183impl IReadableBitmapImpl for WriteableBitmapImpl {
184    fn format(&self) -> Option<PixelFormat> {
185        self.with_bitmap(|bitmap| to_pixel_format_opt(bitmap.color_type()))
186    }
187
188    fn alpha_format(&self) -> Option<AlphaFormat> {
189        self.with_bitmap(|bitmap| Some(to_alpha_format(bitmap.alpha_type())))
190    }
191
192    fn lock(&self) -> Rc<dyn ILockedFramebuffer> {
193        let parent = self.weak_self.upgrade().expect("WriteableBitmapImpl is alive while it is used");
194        Rc::new(BitmapFramebuffer { parent: RefCell::new(Some(parent)) })
195    }
196}
197
198impl IWriteableBitmapImpl for WriteableBitmapImpl {}
199
200/// Framebuffer for a bitmap.
201struct BitmapFramebuffer {
202    parent: RefCell<Option<Arc<WriteableBitmapImpl>>>,
203}
204
205impl BitmapFramebuffer {
206    fn with_bitmap<R>(&self, f: impl FnOnce(&WriteableBitmapImpl, &Bitmap) -> R) -> R {
207        let parent = self.parent.borrow();
208        let parent = parent.as_ref().expect("the framebuffer has been unlocked");
209        parent.with_bitmap(|bitmap| f(parent, bitmap))
210    }
211}
212
213impl ILockedFramebuffer for BitmapFramebuffer {
214    fn address(&self) -> *mut u8 {
215        self.with_bitmap(|_, bitmap| bitmap.pixmap().addr() as *mut u8)
216    }
217
218    fn with_data(&self, access: &mut dyn FnMut(&mut [u8])) {
219        self.with_bitmap(|_, bitmap| match bitmap.peek_pixels().as_mut().and_then(|pixmap| pixmap.bytes_mut()) {
220            Some(bytes) => access(bytes),
221            None => access(&mut []),
222        });
223    }
224
225    fn size(&self) -> PixelSize {
226        self.with_bitmap(|_, bitmap| PixelSize::new(bitmap.width(), bitmap.height()))
227    }
228
229    fn row_bytes(&self) -> i32 {
230        self.with_bitmap(|_, bitmap| bitmap.row_bytes() as i32)
231    }
232
233    fn dpi(&self) -> Vector {
234        self.with_bitmap(|parent, _| parent.dpi)
235    }
236
237    fn format(&self) -> PixelFormat {
238        self.with_bitmap(|_, bitmap| to_pixel_format(bitmap.color_type()))
239    }
240
241    fn alpha_format(&self) -> AlphaFormat {
242        self.with_bitmap(|_, bitmap| to_alpha_format(bitmap.alpha_type()))
243    }
244
245    fn dispose(&self) {
246        if let Some(parent) = self.parent.borrow_mut().take() {
247            let mut pixels = parent.pixels.lock().unwrap();
248            if let Some(bitmap) = pixels.bitmap.as_ref() {
249                bitmap.notify_pixels_changed();
250            }
251            parent.version.fetch_add(1, Ordering::SeqCst);
252            // The image is taken again from the new pixels when drawn.
253            pixels.image = None;
254        }
255    }
256}