Skip to main content

ferroui_vello/
immutable_bitmap.rs

1use crate::helpers::image_decoding_helper::{jpeg_scaled_dimensions, reduce_by_area, DecodedImage, EncodedImageFormat};
2use crate::helpers::image_saving_helper::{self, decode_image, load_error};
3use crate::helpers::pixel_format_helper::{scene_size, to_image, to_premul_rgba, try_bytes_per_pixel};
4use crate::i_drawable_bitmap_impl::IDrawableBitmapImpl;
5use crate::scene::{IVelloSceneSink, VelloCpuSceneSink, VelloSceneBrush, VelloSceneImage, VelloScenePaint};
6use crate::helpers::mipmap_helper;
7use crate::vello_extensions::{rect_path, to_sampling};
8use ferroui_base::media::imaging::{BitmapEncoderOptions, BitmapInterpolationMode};
9use ferroui_base::platform::{AlphaFormat, IBitmapImpl, ILockedFramebuffer, IReadableBitmapImpl, PixelFormat};
10use ferroui_base::{PixelSize, Rect, Vector};
11use kurbo::Affine;
12use peniko::{BlendMode, Extend, Fill, ImageData};
13use std::any::Any;
14use std::cell::RefCell;
15use std::io::{self, Read, Write};
16use std::rc::Rc;
17use std::sync::Mutex;
18
19/// The pixels of a decoded image as an image.
20fn decoded_to_image(rgba: Vec<u8>, width: u32, height: u32) -> io::Result<ImageData> {
21    let size = PixelSize::new(i32::try_from(width).map_err(|_| load_error())?, i32::try_from(height).map_err(|_| load_error())?);
22
23    Ok(to_image(rgba, size))
24}
25
26/// Decodes an encoded image at its natural size, and tells whether it has
27/// no alpha.
28pub(crate) fn decode_bitmap(stream: &mut dyn Read) -> io::Result<(ImageData, bool)> {
29    let DecodedImage { rgba, width, height, opaque, .. } = decode_image(stream)?;
30
31    Ok((decoded_to_image(rgba, width, height)?, opaque))
32}
33
34/// Decodes an encoded image so that its width (`horizontal`) or height is
35/// `decode_size`, keeping the aspect ratio.
36pub(crate) fn decode_bitmap_to_size(
37    stream: &mut dyn Read,
38    decode_size: i32,
39    horizontal: bool,
40    interpolation_mode: BitmapInterpolationMode,
41) -> io::Result<ImageData> {
42    let DecodedImage { mut rgba, mut width, mut height, format, .. } = decode_image(stream)?;
43    if decode_size < 1 {
44        return Err(load_error());
45    }
46    let (natural_width, natural_height) = (width, height);
47
48    // Get the scale that is nearest to what we want: the codec of Skia
49    // decodes a JPEG at a number of eighths of its size (and every other
50    // format at its size), which is the mean of the pixels that fall into
51    // one.
52    if format == EncodedImageFormat::Jpeg {
53        let scale = decode_size as f64 / if horizontal { width } else { height } as f64;
54        let (scaled_width, scaled_height) = jpeg_scaled_dimensions(width, height, scale);
55        if (scaled_width, scaled_height) != (width, height) {
56            rgba = reduce_by_area(&rgba, width as usize, height as usize, scaled_width as usize, scaled_height as usize);
57            (width, height) = (scaled_width, scaled_height);
58        }
59    }
60    let image = decoded_to_image(rgba, width, height)?;
61
62    // Now scale that to the size that we want.
63    let real_scale = if horizontal {
64        natural_height as f64 / natural_width as f64
65    } else {
66        natural_width as f64 / natural_height as f64
67    };
68
69    let desired = if horizontal {
70        PixelSize::new(decode_size, (real_scale * decode_size as f64) as i32)
71    } else {
72        PixelSize::new((real_scale * decode_size as f64) as i32, decode_size)
73    };
74
75    if desired.width as u32 != image.width || desired.height as u32 != image.height {
76        return scale_image(&image, desired, interpolation_mode);
77    }
78
79    Ok(image)
80}
81
82/// Scales an image to another size.
83///
84/// The image is drawn scaled by the CPU renderer, which every build of the
85/// backend has: a bitmap is resized the same way in every rendering mode,
86/// and on the thread that asks.
87pub(crate) fn scale_image(
88    image: &ImageData,
89    destination_size: PixelSize,
90    interpolation_mode: BitmapInterpolationMode,
91) -> io::Result<ImageData> {
92    if destination_size.width < 1
93        || destination_size.height < 1
94        || destination_size.width > u16::MAX as i32
95        || destination_size.height > u16::MAX as i32
96        || image.width == 0
97        || image.height == 0
98    {
99        return Err(load_error());
100    }
101
102    let (width, height) = scene_size(destination_size);
103    let mut sink = VelloCpuSceneSink::new(width, height);
104
105    let destination = Rect::new(0.0, 0.0, destination_size.width as f64, destination_size.height as f64);
106    let is_upscaling =
107        destination_size.width as u32 > image.width || destination_size.height as u32 > image.height;
108    let (quality, mipmaps) = to_sampling(interpolation_mode, is_upscaling);
109
110    let transform =
111        Affine::scale_non_uniform(destination.width / image.width as f64, destination.height / image.height as f64);
112
113    // An image that is reduced in a mode with mipmaps is sampled from the
114    // two levels of its mipmap that are nearest to the new size.
115    match mipmaps.then(|| mipmap_helper::levels(image, transform)).flatten() {
116        Some(levels) => mipmap_helper::fill_with_levels(
117            &mut sink,
118            &rect_path(destination),
119            Affine::IDENTITY,
120            transform,
121            &levels,
122            1.0,
123            false,
124        ),
125        None => {
126            let paint = VelloScenePaint {
127                brush: VelloSceneBrush::Image(VelloSceneImage {
128                    image: image.clone(),
129                    x_extend: Extend::Pad,
130                    y_extend: Extend::Pad,
131                    quality,
132                    alpha: 1.0,
133                }),
134                transform,
135            };
136            sink.fill(&rect_path(destination), Fill::NonZero, Affine::IDENTITY, &paint, BlendMode::default(), false);
137        }
138    }
139
140    let mut rgba = vec![0u8; width as usize * height as usize * 4];
141    sink.render_to_pixels(&mut rgba);
142
143    Ok(to_image(rgba, destination_size))
144}
145
146/// The pixels a bitmap was created from, in their own format.
147struct SourcePixels {
148    /// `row_bytes` bytes a row, without padding.
149    data: Vec<u8>,
150    row_bytes: usize,
151    format: PixelFormat,
152    alpha_format: AlphaFormat,
153}
154
155/// Immutable bitmap: pixels that never change.
156///
157/// The bitmap is drawn from an image of premultiplied RGBA pixels, the one
158/// format the renderers draw. A bitmap that was created from pixels in
159/// another format keeps those as well: it reports their format and is read
160/// in it, as a bitmap of the Skia backend, which draws every format it has
161/// a color type for.
162pub struct ImmutableBitmap {
163    image: Mutex<Option<ImageData>>,
164    source: Mutex<Option<SourcePixels>>,
165    /// Whether the bitmap was decoded from an image without alpha.
166    opaque: bool,
167    dpi: Vector,
168    pixel_size: PixelSize,
169}
170
171impl ImmutableBitmap {
172    /// Wraps an image of premultiplied RGBA pixels.
173    pub fn from_image(image: ImageData) -> Self {
174        let pixel_size = PixelSize::new(image.width as i32, image.height as i32);
175
176        Self {
177            image: Mutex::new(Some(image)),
178            source: Mutex::new(None),
179            opaque: false,
180            dpi: Vector::new(96.0, 96.0),
181            pixel_size,
182        }
183    }
184
185    /// Creates an immutable bitmap from the given stream.
186    pub fn from_stream(stream: &mut dyn Read) -> io::Result<Self> {
187        // The decoders have no API for DPI.
188        let (image, opaque) = decode_bitmap(stream)?;
189
190        Ok(Self { opaque, ..Self::from_image(image) })
191    }
192
193    /// Creates an immutable bitmap from a stream, decoded so that its width
194    /// (`horizontal`) or height is `decode_size`.
195    pub fn from_stream_to_size(
196        stream: &mut dyn Read,
197        decode_size: i32,
198        horizontal: bool,
199        interpolation_mode: BitmapInterpolationMode,
200    ) -> io::Result<Self> {
201        Ok(Self::from_image(decode_bitmap_to_size(stream, decode_size, horizontal, interpolation_mode)?))
202    }
203
204    /// Creates a resized copy of `src`.
205    pub fn resized(
206        src: &ImmutableBitmap,
207        destination_size: PixelSize,
208        interpolation_mode: BitmapInterpolationMode,
209    ) -> io::Result<Self> {
210        let image = src.image().ok_or_else(load_error)?;
211
212        Ok(Self::from_image(scale_image(&image, destination_size, interpolation_mode)?))
213    }
214
215    /// Creates an immutable bitmap from raw pixel data, which is copied.
216    ///
217    /// `data` holds `|stride| * size.height` bytes. A negative stride means
218    /// the rows are stored bottom-up: the first row of the image is the last
219    /// row of `data`.
220    ///
221    /// # Panics
222    /// Panics when the pixel format is unknown to the backend.
223    pub fn from_pixels(
224        size: PixelSize,
225        dpi: Vector,
226        stride: i32,
227        format: PixelFormat,
228        alpha_format: AlphaFormat,
229        data: &[u8],
230    ) -> io::Result<Self> {
231        let create_error =
232            || io::Error::new(io::ErrorKind::InvalidInput, "Unable to create bitmap from provided data");
233
234        let pixel_bytes =
235            try_bytes_per_pixel(format).unwrap_or_else(|| panic!("Unknown pixel format: {format}"));
236        if size.width < 1 || size.height < 1 || size.width > u16::MAX as i32 || size.height > u16::MAX as i32 {
237            return Err(create_error());
238        }
239
240        let (width, height) = (size.width as usize, size.height as usize);
241        let row_bytes = width * pixel_bytes;
242        let abs_stride = stride.unsigned_abs() as usize;
243        let copy_bytes = row_bytes.min(abs_stride);
244
245        if data.len() < abs_stride * (height - 1) + copy_bytes {
246            return Err(create_error());
247        }
248
249        // The rows from the top down, without their padding; what a row of
250        // the data lacks is zero.
251        let mut pixels = vec![0u8; row_bytes * height];
252        for row in 0..height {
253            let source_row = if stride < 0 { height - 1 - row } else { row };
254            let source = &data[source_row * abs_stride..source_row * abs_stride + copy_bytes];
255            pixels[row * row_bytes..row * row_bytes + copy_bytes].copy_from_slice(source);
256        }
257
258        let rgba = to_premul_rgba(&pixels, row_bytes, size, format, alpha_format);
259
260        // Pixels in the format the renderers draw are the image itself.
261        let source = (format != PixelFormat::RGBA8888 || alpha_format != AlphaFormat::Premul)
262            .then_some(SourcePixels { data: pixels, row_bytes, format, alpha_format });
263
264        Ok(Self {
265            image: Mutex::new(Some(to_image(rgba, size))),
266            source: Mutex::new(source),
267            opaque: false,
268            dpi,
269            pixel_size: size,
270        })
271    }
272}
273
274impl IBitmapImpl for ImmutableBitmap {
275    fn dpi(&self) -> Vector {
276        self.dpi
277    }
278
279    fn pixel_size(&self) -> PixelSize {
280        self.pixel_size
281    }
282
283    fn version(&self) -> i32 {
284        1
285    }
286
287    fn save(&self, stream: &mut dyn Write, options: &BitmapEncoderOptions) -> io::Result<()> {
288        let image = self.image().ok_or_else(|| io::Error::other("ImmutableBitmap has been disposed"))?;
289        image_saving_helper::save_image(&image, stream, options)
290    }
291
292    fn dispose(&self) {
293        self.image.lock().unwrap().take();
294        self.source.lock().unwrap().take();
295    }
296
297    fn as_any(&self) -> &dyn Any {
298        self
299    }
300
301    fn as_readable_bitmap(&self) -> Option<&dyn IReadableBitmapImpl> {
302        Some(self)
303    }
304}
305
306impl IDrawableBitmapImpl for ImmutableBitmap {
307    fn image(&self) -> Option<ImageData> {
308        self.image.lock().unwrap().clone()
309    }
310}
311
312impl IReadableBitmapImpl for ImmutableBitmap {
313    fn format(&self) -> Option<PixelFormat> {
314        Some(self.source.lock().unwrap().as_ref().map_or(PixelFormat::RGBA8888, |source| source.format))
315    }
316
317    fn alpha_format(&self) -> Option<AlphaFormat> {
318        Some(self.source.lock().unwrap().as_ref().map_or(self.decoded_alpha_format(), |source| source.alpha_format))
319    }
320
321    fn lock(&self) -> Rc<dyn ILockedFramebuffer> {
322        // The pixels of the bitmap do not change: the framebuffer is over a
323        // copy of them, and what is written to it is dropped with it.
324        if let Some(source) = self.source.lock().unwrap().as_ref() {
325            return Rc::new(PixelsFramebuffer {
326                pixels: RefCell::new(source.data.clone()),
327                size: self.pixel_size,
328                row_bytes: source.row_bytes,
329                dpi: self.dpi,
330                format: source.format,
331                alpha_format: source.alpha_format,
332            });
333        }
334
335        let image = self.image().unwrap_or_else(|| panic!("ImmutableBitmap has been disposed"));
336
337        Rc::new(PixelsFramebuffer {
338            pixels: RefCell::new(image.data.data().to_vec()),
339            size: self.pixel_size,
340            row_bytes: self.pixel_size.width.max(0) as usize * 4,
341            dpi: self.dpi,
342            format: PixelFormat::RGBA8888,
343            alpha_format: self.decoded_alpha_format(),
344        })
345    }
346}
347
348impl ImmutableBitmap {
349    /// The alpha format of the image of the bitmap: opaque for one that was
350    /// decoded from an image without alpha, as the codec of Skia reports
351    /// it.
352    fn decoded_alpha_format(&self) -> AlphaFormat {
353        if self.opaque {
354            AlphaFormat::Opaque
355        } else {
356            AlphaFormat::Premul
357        }
358    }
359}
360
361/// A framebuffer over pixels of its own, without padding.
362pub(crate) struct PixelsFramebuffer {
363    pub pixels: RefCell<Vec<u8>>,
364    pub size: PixelSize,
365    pub row_bytes: usize,
366    pub dpi: Vector,
367    pub format: PixelFormat,
368    pub alpha_format: AlphaFormat,
369}
370
371impl ILockedFramebuffer for PixelsFramebuffer {
372    fn address(&self) -> *mut u8 {
373        self.pixels.borrow_mut().as_mut_ptr()
374    }
375
376    fn with_data(&self, access: &mut dyn FnMut(&mut [u8])) {
377        access(&mut self.pixels.borrow_mut());
378    }
379
380    fn size(&self) -> PixelSize {
381        self.size
382    }
383
384    fn row_bytes(&self) -> i32 {
385        self.row_bytes as i32
386    }
387
388    fn dpi(&self) -> Vector {
389        self.dpi
390    }
391
392    fn format(&self) -> PixelFormat {
393        self.format
394    }
395
396    fn alpha_format(&self) -> AlphaFormat {
397        self.alpha_format
398    }
399
400    fn dispose(&self) {}
401}