Skip to main content

ferroui_base/media/imaging/
writeable_bitmap.rs

1use crate::media::imaging::bitmap_memory::BitmapMemory;
2use crate::media::imaging::pixel_format_readers::PixelFormatReader;
3use crate::media::imaging::{Bitmap, BitmapEncoderOptions, BitmapInterpolationMode, IBitmap};
4use crate::media::{DrawingContext, IImage, IImageBrushSource};
5use crate::platform::{self, AlphaFormat, IBitmapImpl, ILockedFramebuffer, IWriteableBitmapImpl, PixelFormat};
6use crate::utilities::RefCounted;
7use crate::{PixelRect, PixelSize, Rect, Size, Vector};
8use std::any::Any;
9use std::cell::{Cell, RefCell};
10use std::io::{Read, Write};
11use std::ops::Deref;
12use std::rc::Rc;
13
14/// Holds a writeable bitmap image.
15pub struct WriteableBitmap {
16    base: Bitmap,
17    /// Holds a buffer with a pixel format that requires transcoding.
18    pixel_format_memory: Option<Rc<RefCell<BitmapMemory>>>,
19}
20
21impl Deref for WriteableBitmap {
22    type Target = Bitmap;
23
24    #[inline]
25    fn deref(&self) -> &Bitmap {
26        &self.base
27    }
28}
29
30impl WriteableBitmap {
31    /// Creates a bitmap of the given size, in pixels, and DPI. The formats
32    /// default to the backend's default formats.
33    ///
34    /// Panics when the size is not positive or the pixel format is not
35    /// supported.
36    pub fn new(
37        size: PixelSize,
38        dpi: Vector,
39        format: Option<PixelFormat>,
40        alpha_format: Option<AlphaFormat>,
41    ) -> WriteableBitmap {
42        let (platform_impl, pixel_format_memory) = Self::create_platform_impl(size, dpi, format, alpha_format);
43        Self::from_impl(platform_impl, pixel_format_memory)
44    }
45
46    fn from_impl(platform_impl: std::sync::Arc<dyn IWriteableBitmapImpl>, pixel_format_memory: Option<BitmapMemory>) -> Self {
47        Self {
48            base: Bitmap::from_impl(platform_impl),
49            pixel_format_memory: pixel_format_memory.map(|memory| Rc::new(RefCell::new(memory))),
50        }
51    }
52
53    /// Creates a writeable bitmap from a pixel data copy: `data` holds rows
54    /// of `format` pixels, `stride` bytes apart.
55    ///
56    /// Panics when `stride` is too small for a row.
57    pub fn from_pixels(
58        format: PixelFormat,
59        alpha_format: AlphaFormat,
60        data: &[u8],
61        size: PixelSize,
62        dpi: Vector,
63        stride: i32,
64    ) -> WriteableBitmap {
65        let result = Self::new(size, dpi, Some(format), Some(alpha_format));
66
67        let min_stride = (format.bits_per_pixel() as i32 * size.width + 7) / 8;
68        if min_stride > stride {
69            panic!("stride is out of range: {stride}");
70        }
71
72        let locked = result.lock();
73        let row_bytes = locked.row_bytes();
74        locked.with_data(&mut |dest| {
75            let min_stride = min_stride as usize;
76            for y in 0..size.height {
77                let dst = (row_bytes as i64 * y as i64) as usize;
78                let src = (stride as i64 * y as i64) as usize;
79                dest[dst..dst + min_stride].copy_from_slice(&data[src..src + min_stride]);
80            }
81        });
82        locked.dispose();
83
84        result
85    }
86
87    /// The pixel format of the bitmap.
88    pub fn format(&self) -> Option<PixelFormat> {
89        match &self.pixel_format_memory {
90            Some(memory) => Some(memory.borrow().format()),
91            None => self.base.format(),
92        }
93    }
94
95    /// Locks the pixel data of the bitmap and returns a framebuffer that can
96    /// be used to read and write the pixels. The changes are committed when
97    /// the framebuffer is disposed.
98    pub fn lock(&self) -> Rc<dyn ILockedFramebuffer> {
99        let platform_impl = self.base.platform_impl().item();
100        let Some(memory) = &self.pixel_format_memory else {
101            return platform_impl.as_readable_bitmap().expect("the platform bitmap is writeable").lock();
102        };
103
104        let (size, row_bytes, format, alpha_format) = {
105            let memory = memory.borrow();
106            (memory.size(), memory.row_bytes(), memory.format(), memory.alpha_format())
107        };
108
109        Rc::new(MemoryLockedFramebuffer {
110            memory: memory.clone(),
111            platform_impl,
112            size,
113            row_bytes,
114            dpi: self.dpi(),
115            format,
116            alpha_format,
117            disposed: Cell::new(false),
118        })
119    }
120
121    /// Copies the pixels of `source_rect` into `buffer`, whose rows are
122    /// `stride` bytes apart. An empty `source_rect` selects the whole
123    /// bitmap.
124    pub fn copy_pixels(&self, source_rect: PixelRect, buffer: &mut [u8], stride: i32) {
125        let fb = self.lock();
126        self.base.copy_pixels_core(source_rect, buffer, stride, &*fb);
127        fb.dispose();
128    }
129
130    /// Loads a writeable bitmap from a stream.
131    pub fn decode(stream: &mut dyn Read) -> std::io::Result<WriteableBitmap> {
132        let ri = platform::render_interface();
133        Ok(Self::from_impl(ri.load_writeable_bitmap(stream)?, None))
134    }
135
136    /// Loads a writeable bitmap from a stream, decoded to the specified
137    /// width maintaining aspect ratio.
138    pub fn decode_to_width(
139        stream: &mut dyn Read,
140        width: i32,
141        interpolation_mode: BitmapInterpolationMode,
142    ) -> std::io::Result<WriteableBitmap> {
143        let ri = platform::render_interface();
144        Ok(Self::from_impl(ri.load_writeable_bitmap_to_width(stream, width, interpolation_mode)?, None))
145    }
146
147    /// Loads a writeable bitmap from a stream, decoded to the specified
148    /// height maintaining aspect ratio.
149    pub fn decode_to_height(
150        stream: &mut dyn Read,
151        height: i32,
152        interpolation_mode: BitmapInterpolationMode,
153    ) -> std::io::Result<WriteableBitmap> {
154        let ri = platform::render_interface();
155        Ok(Self::from_impl(ri.load_writeable_bitmap_to_height(stream, height, interpolation_mode)?, None))
156    }
157
158    fn create_platform_impl(
159        size: PixelSize,
160        dpi: Vector,
161        format: Option<PixelFormat>,
162        alpha_format: Option<AlphaFormat>,
163    ) -> (std::sync::Arc<dyn IWriteableBitmapImpl>, Option<BitmapMemory>) {
164        if size.width <= 0 || size.height <= 0 {
165            panic!("Size should be >= (1,1)");
166        }
167
168        let ri = platform::render_interface();
169
170        let final_format = format.unwrap_or_else(|| ri.default_pixel_format());
171        let mut final_alpha_format = alpha_format.unwrap_or_else(|| ri.default_alpha_format());
172
173        if ri.is_supported_bitmap_pixel_format(final_format) {
174            return (ri.create_writeable_bitmap(size, dpi, final_format, final_alpha_format), None);
175        }
176
177        if !PixelFormatReader::supports_format(final_format) {
178            panic!("Pixel format {final_format} is not supported");
179        }
180
181        final_alpha_format = if final_format.has_alpha() { final_alpha_format } else { AlphaFormat::Opaque };
182
183        let platform_impl = ri.create_writeable_bitmap(size, dpi, PixelFormat::RGBA8888, final_alpha_format);
184        (platform_impl, Some(BitmapMemory::new(final_format, final_alpha_format, size)))
185    }
186}
187
188/// A framebuffer over the transcoding buffer of a [`WriteableBitmap`]: when
189/// it is disposed, the pixels are converted into the platform bitmap.
190struct MemoryLockedFramebuffer {
191    memory: Rc<RefCell<BitmapMemory>>,
192    platform_impl: std::sync::Arc<crate::platform::SharedBitmapImpl>,
193    size: PixelSize,
194    row_bytes: i32,
195    dpi: Vector,
196    format: PixelFormat,
197    alpha_format: AlphaFormat,
198    disposed: Cell<bool>,
199}
200
201impl ILockedFramebuffer for MemoryLockedFramebuffer {
202    fn address(&self) -> *mut u8 {
203        self.memory.borrow_mut().data_mut().as_mut_ptr()
204    }
205
206    fn with_data(&self, access: &mut dyn FnMut(&mut [u8])) {
207        access(self.memory.borrow_mut().data_mut());
208    }
209
210    fn size(&self) -> PixelSize {
211        self.size
212    }
213
214    fn row_bytes(&self) -> i32 {
215        self.row_bytes
216    }
217
218    fn dpi(&self) -> Vector {
219        self.dpi
220    }
221
222    fn format(&self) -> PixelFormat {
223        self.format
224    }
225
226    fn alpha_format(&self) -> AlphaFormat {
227        self.alpha_format
228    }
229
230    fn dispose(&self) {
231        if self.disposed.replace(true) {
232            return;
233        }
234        let inner = self.platform_impl.as_readable_bitmap().expect("the platform bitmap is writeable").lock();
235        let (alpha_format, row_bytes) = (inner.alpha_format(), inner.row_bytes());
236        let memory = self.memory.borrow();
237        inner.with_data(&mut |dest| memory.copy_to_rgba(alpha_format, dest, row_bytes));
238        inner.dispose();
239    }
240}
241
242impl IImage for WriteableBitmap {
243    fn size(&self) -> Size {
244        self.base.size()
245    }
246
247    fn draw(&self, context: &mut DrawingContext<'_>, source_rect: Rect, dest_rect: Rect) {
248        IImage::draw(&self.base, context, source_rect, dest_rect)
249    }
250
251    fn as_any(&self) -> &dyn Any {
252        self
253    }
254
255    fn as_bitmap(&self) -> Option<&dyn IBitmap> {
256        Some(self)
257    }
258}
259
260impl IBitmap for WriteableBitmap {
261    fn dpi(&self) -> Vector {
262        self.base.dpi()
263    }
264
265    fn pixel_size(&self) -> PixelSize {
266        self.base.pixel_size()
267    }
268
269    fn platform_impl(&self) -> &RefCounted<crate::platform::SharedBitmapImpl> {
270        self.base.platform_impl()
271    }
272
273    fn save(&self, stream: &mut dyn Write, options: &BitmapEncoderOptions) -> std::io::Result<()> {
274        self.base.save(stream, options)
275    }
276
277    fn dispose(&self) {
278        self.base.dispose()
279    }
280}
281
282impl IImageBrushSource for WriteableBitmap {
283    fn bitmap(&self) -> Option<&RefCounted<crate::platform::SharedBitmapImpl>> {
284        IImageBrushSource::bitmap(&self.base)
285    }
286}