1use crate::{
  2    AssetSource, DevicePixels, IsZero, RenderImage, Result, SharedString, Size,
  3    swap_rgba_pa_to_bgra,
  4};
  5use image::Frame;
  6use resvg::tiny_skia::Pixmap;
  7use smallvec::SmallVec;
  8use std::{
  9    hash::Hash,
 10    sync::{Arc, LazyLock},
 11};
 12
 13/// When rendering SVGs, we render them at twice the size to get a higher-quality result.
 14pub const SMOOTH_SVG_SCALE_FACTOR: f32 = 2.;
 15
 16#[derive(Clone, PartialEq, Hash, Eq)]
 17pub(crate) struct RenderSvgParams {
 18    pub(crate) path: SharedString,
 19    pub(crate) size: Size<DevicePixels>,
 20}
 21
 22#[derive(Clone)]
 23/// A struct holding everything necessary to render SVGs.
 24pub struct SvgRenderer {
 25    asset_source: Arc<dyn AssetSource>,
 26    usvg_options: Arc<usvg::Options<'static>>,
 27}
 28
 29/// The size in which to render the SVG.
 30pub enum SvgSize {
 31    /// An absolute size in device pixels.
 32    Size(Size<DevicePixels>),
 33    /// A scaling factor to apply to the size provided by the SVG.
 34    ScaleFactor(f32),
 35}
 36
 37impl SvgRenderer {
 38    /// Creates a new SVG renderer with the provided asset source.
 39    pub fn new(asset_source: Arc<dyn AssetSource>) -> Self {
 40        static FONT_DB: LazyLock<Arc<usvg::fontdb::Database>> = LazyLock::new(|| {
 41            let mut db = usvg::fontdb::Database::new();
 42            db.load_system_fonts();
 43            Arc::new(db)
 44        });
 45        let default_font_resolver = usvg::FontResolver::default_font_selector();
 46        let font_resolver = Box::new(
 47            move |font: &usvg::Font, db: &mut Arc<usvg::fontdb::Database>| {
 48                if db.is_empty() {
 49                    *db = FONT_DB.clone();
 50                }
 51                default_font_resolver(font, db)
 52            },
 53        );
 54        let options = usvg::Options {
 55            font_resolver: usvg::FontResolver {
 56                select_font: font_resolver,
 57                select_fallback: usvg::FontResolver::default_fallback_selector(),
 58            },
 59            ..Default::default()
 60        };
 61        Self {
 62            asset_source,
 63            usvg_options: Arc::new(options),
 64        }
 65    }
 66
 67    /// Renders the given bytes into an image buffer.
 68    pub fn render_single_frame(
 69        &self,
 70        bytes: &[u8],
 71        scale_factor: f32,
 72        to_brga: bool,
 73    ) -> Result<Arc<RenderImage>, usvg::Error> {
 74        self.render_pixmap(
 75            bytes,
 76            SvgSize::ScaleFactor(scale_factor * SMOOTH_SVG_SCALE_FACTOR),
 77        )
 78        .map(|pixmap| {
 79            let mut buffer =
 80                image::ImageBuffer::from_raw(pixmap.width(), pixmap.height(), pixmap.take())
 81                    .unwrap();
 82
 83            if to_brga {
 84                for pixel in buffer.chunks_exact_mut(4) {
 85                    swap_rgba_pa_to_bgra(pixel);
 86                }
 87            }
 88
 89            let mut image = RenderImage::new(SmallVec::from_const([Frame::new(buffer)]));
 90            image.scale_factor = SMOOTH_SVG_SCALE_FACTOR;
 91            Arc::new(image)
 92        })
 93    }
 94
 95    pub(crate) fn render_alpha_mask(
 96        &self,
 97        params: &RenderSvgParams,
 98    ) -> Result<Option<(Size<DevicePixels>, Vec<u8>)>> {
 99        anyhow::ensure!(!params.size.is_zero(), "can't render at a zero size");
100
101        // Load the tree.
102        let Some(bytes) = self.asset_source.load(¶ms.path)? else {
103            return Ok(None);
104        };
105
106        let pixmap = self.render_pixmap(&bytes, SvgSize::Size(params.size))?;
107
108        // Convert the pixmap's pixels into an alpha mask.
109        let size = Size::new(
110            DevicePixels(pixmap.width() as i32),
111            DevicePixels(pixmap.height() as i32),
112        );
113        let alpha_mask = pixmap
114            .pixels()
115            .iter()
116            .map(|p| p.alpha())
117            .collect::<Vec<_>>();
118        Ok(Some((size, alpha_mask)))
119    }
120
121    fn render_pixmap(&self, bytes: &[u8], size: SvgSize) -> Result<Pixmap, usvg::Error> {
122        let tree = usvg::Tree::from_data(bytes, &self.usvg_options)?;
123        let svg_size = tree.size();
124        let scale = match size {
125            SvgSize::Size(size) => size.width.0 as f32 / svg_size.width(),
126            SvgSize::ScaleFactor(scale) => scale,
127        };
128
129        // Render the SVG to a pixmap with the specified width and height.
130        let mut pixmap = resvg::tiny_skia::Pixmap::new(
131            (svg_size.width() * scale) as u32,
132            (svg_size.height() * scale) as u32,
133        )
134        .ok_or(usvg::Error::InvalidSize)?;
135
136        let transform = resvg::tiny_skia::Transform::from_scale(scale, scale);
137
138        resvg::render(&tree, transform, &mut pixmap.as_mut());
139
140        Ok(pixmap)
141    }
142}