Implement SVG rendering

This commit is contained in:
Antonio Scandurra
2021-04-06 13:46:33 +02:00
parent 2970e934da
commit e0c43dac25
9 changed files with 505 additions and 49 deletions
+26 -3
View File
@@ -296,7 +296,7 @@ impl Renderer {
drawable_size,
command_encoder,
);
self.render_glyph_sprites(scene, layer, offset, drawable_size, command_encoder);
self.render_sprites(scene, layer, offset, drawable_size, command_encoder);
}
command_encoder.end_encoding();
@@ -465,7 +465,7 @@ impl Renderer {
*offset = next_offset;
}
fn render_glyph_sprites(
fn render_sprites(
&mut self,
scene: &Scene,
layer: &Layer,
@@ -473,11 +473,12 @@ impl Renderer {
drawable_size: Vector2F,
command_encoder: &metal::RenderCommandEncoderRef,
) {
if layer.glyphs().is_empty() {
if layer.glyphs().is_empty() && layer.icons().is_empty() {
return;
}
let mut sprites_by_atlas = HashMap::new();
for glyph in layer.glyphs() {
if let Some(sprite) = self.sprite_cache.render_glyph(
glyph.font_id,
@@ -501,6 +502,28 @@ impl Renderer {
}
}
for icon in layer.icons() {
let sprite = self.sprite_cache.render_icon(
icon.bounds.size(),
icon.path.clone(),
icon.svg.clone(),
scene.scale_factor(),
);
// Snap sprite to pixel grid.
let origin = (icon.bounds.origin() * scene.scale_factor()).floor();
sprites_by_atlas
.entry(sprite.atlas_id)
.or_insert_with(Vec::new)
.push(shaders::GPUISprite {
origin: origin.to_float2(),
size: sprite.size.to_float2(),
atlas_origin: sprite.atlas_origin.to_float2(),
color: icon.color.to_uchar4(),
compute_winding: 0,
});
}
command_encoder.set_render_pipeline_state(&self.sprite_pipeline_state);
command_encoder.set_vertex_buffer(
shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexVertices as u64,
+64
View File
@@ -27,12 +27,27 @@ pub struct GlyphSprite {
pub size: Vector2I,
}
#[derive(Hash, Eq, PartialEq)]
struct IconDescriptor {
path: String,
width: i32,
height: i32,
}
#[derive(Clone)]
pub struct IconSprite {
pub atlas_id: usize,
pub atlas_origin: Vector2I,
pub size: Vector2I,
}
pub struct SpriteCache {
device: metal::Device,
atlas_size: Vector2I,
fonts: Arc<dyn platform::FontSystem>,
atlases: Vec<Atlas>,
glyphs: HashMap<GlyphDescriptor, Option<GlyphSprite>>,
icons: HashMap<IconDescriptor, IconSprite>,
}
impl SpriteCache {
@@ -48,6 +63,7 @@ impl SpriteCache {
fonts,
atlases,
glyphs: Default::default(),
icons: Default::default(),
}
}
@@ -119,6 +135,54 @@ impl SpriteCache {
.clone()
}
pub fn render_icon(
&mut self,
size: Vector2F,
path: String,
svg: usvg::Tree,
scale_factor: f32,
) -> IconSprite {
let atlases = &mut self.atlases;
let atlas_size = self.atlas_size;
let device = &self.device;
let size = (size * scale_factor).round().to_i32();
assert!(size.x() < atlas_size.x());
assert!(size.y() < atlas_size.y());
self.icons
.entry(IconDescriptor {
path,
width: size.x(),
height: size.y(),
})
.or_insert_with(|| {
let mut pixmap = tiny_skia::Pixmap::new(size.x() as u32, size.y() as u32).unwrap();
resvg::render(&svg, usvg::FitTo::Width(size.x() as u32), pixmap.as_mut());
let mask = pixmap
.pixels()
.iter()
.map(|a| a.alpha())
.collect::<Vec<_>>();
let atlas_bounds = atlases
.last_mut()
.unwrap()
.try_insert(size, &mask)
.unwrap_or_else(|| {
let mut atlas = Atlas::new(device, atlas_size);
let bounds = atlas.try_insert(size, &mask).unwrap();
atlases.push(atlas);
bounds
});
IconSprite {
atlas_id: atlases.len() - 1,
atlas_origin: atlas_bounds.origin(),
size,
}
})
.clone()
}
pub fn atlas_texture(&self, atlas_id: usize) -> Option<&metal::TextureRef> {
self.atlases.get(atlas_id).map(|a| a.texture.as_ref())
}