bake-in widgetry assets: image_path -> image_source::{Path, Bytes}

This commit is contained in:
Michael Kirk 2021-01-21 11:38:53 -06:00 committed by Dustin Carlino
parent 2505d64e8b
commit 4ee6564032
6 changed files with 85 additions and 20 deletions

View File

@ -271,7 +271,7 @@ pub fn bio(
let mut svg_data = Vec::new();
svg_face::generate_face(&mut svg_data, &mut rng).unwrap();
let batch = GeomBatch::from_svg_contents(svg_data).autocrop();
let batch = GeomBatch::from_svg_contents(&svg_data).autocrop();
let dims = batch.get_dims();
let batch = batch.scale((200.0 / dims.width).min(200.0 / dims.height));
rows.push(Widget::draw_batch(ctx, batch).centered_horiz());

View File

@ -142,7 +142,7 @@ impl<'a> EventCtx<'a> {
Widget::custom_col(vec![
Widget::draw_batch(
self,
GeomBatch::from_svg_contents(include_bytes!("../icons/loading.svg").to_vec())
GeomBatch::from_svg_contents(&include_bytes!("../icons/loading.svg").to_vec())
.scale(5.0),
)
.container()

View File

@ -136,9 +136,9 @@ impl GeomBatch {
}
/// Returns a batch containing a parsed SVG string.
pub fn from_svg_contents(raw: Vec<u8>) -> GeomBatch {
pub fn from_svg_contents(raw: &[u8]) -> GeomBatch {
let mut batch = GeomBatch::new();
let svg_tree = usvg::Tree::from_data(&raw, &usvg::Options::default()).unwrap();
let svg_tree = usvg::Tree::from_data(raw, &usvg::Options::default()).unwrap();
svg::add_svg_inner(&mut batch, svg_tree, svg::HIGH_QUALITY).unwrap();
batch
}

View File

@ -15,21 +15,46 @@ pub const LOW_QUALITY: f32 = 1.0;
// https://github.com/nical/lyon/blob/0d0ee771180fb317b986d9cf30266722e0773e01/examples/wgpu_svg/src/main.rs
pub fn load_svg(prerender: &Prerender, filename: &str) -> (GeomBatch, Bounds) {
if let Some(pair) = prerender.assets.get_cached_svg(filename) {
let cache_key = format!("file://{}", filename);
if let Some(pair) = prerender.assets.get_cached_svg(&cache_key) {
return pair;
}
let raw = (prerender.assets.read_svg)(filename);
let svg_tree = usvg::Tree::from_data(&raw, &usvg::Options::default()).unwrap();
let mut batch = GeomBatch::new();
match add_svg_inner(&mut batch, svg_tree, HIGH_QUALITY) {
Ok(bounds) => {
let bytes = (prerender.assets.read_svg)(filename);
load_svg_from_bytes(&bytes)
.map(|(batch, bounds)| {
prerender
.assets
.cache_svg(filename.to_string(), batch.clone(), bounds.clone());
.cache_svg(cache_key, batch.clone(), bounds.clone());
(batch, bounds)
}
Err(err) => panic!("{}: {}", filename, err),
})
.expect(&format!("error loading svg: {}", filename))
}
pub fn load_svg_bytes(
prerender: &Prerender,
bytes: &[u8],
cache_key: &str,
) -> anyhow::Result<(GeomBatch, Bounds)> {
let cache_key = format!("bytes://{}", cache_key);
if let Some(pair) = prerender.assets.get_cached_svg(&cache_key) {
return Ok(pair);
}
load_svg_from_bytes(&bytes).map(|(batch, bounds)| {
prerender
.assets
.cache_svg(cache_key, batch.clone(), bounds.clone());
(batch, bounds)
})
}
fn load_svg_from_bytes(bytes: &[u8]) -> anyhow::Result<(GeomBatch, Bounds)> {
let svg_tree = usvg::Tree::from_data(&bytes, &usvg::Options::default()).unwrap();
let mut batch = GeomBatch::new();
match add_svg_inner(&mut batch, svg_tree, HIGH_QUALITY) {
Ok(bounds) => Ok((batch, bounds)),
Err(err) => Err(anyhow!(err)),
}
}

View File

@ -254,11 +254,27 @@ impl<'b, 'a: 'b> ButtonBuilder<'a> {
}
/// Set the image for the button. If not set, the button will have no image.
///
/// This will replace any image previously set by [`image_bytes`]
pub fn image_path(mut self, path: &'a str) -> Self {
// Currently we don't support setting image for other states like "hover", we easily
// could, but the API gets more verbose for a thing we don't currently need.
let mut image = self.default_style.image.take().unwrap_or_default();
image.path = Some(path);
image.source = Some(ImageSource::Path(path));
self.default_style.image = Some(image);
self
}
/// Set the image for the button. If not set, the button will have no image.
///
/// This will replace any image previously set by [`image_path`].
/// `bytes`: utf-8 encoded bytes of the svg
/// `name`: a label to describe the bytes for debugging purposes
pub fn image_bytes(mut self, bytes: &'a [u8], cache_key: &'a str) -> Self {
// Currently we don't support setting image for other states like "hover", we easily
// could, but the API gets more verbose for a thing we don't currently need.
let mut image = self.default_style.image.take().unwrap_or_default();
image.source = Some(ImageSource::Bytes { bytes, cache_key });
self.default_style.image = Some(image);
self
}
@ -496,12 +512,16 @@ impl<'b, 'a: 'b> ButtonBuilder<'a> {
.or(default_style.image.as_ref())
.and_then(|image| {
let default = default_style.image.as_ref();
let image_path = image.path.or(default.and_then(|d| d.path));
if image_path.is_none() {
let image_source = image
.source
.as_ref()
.or(default.and_then(|d| d.source.as_ref()));
if image_source.is_none() {
return None;
}
let image_path = image_path.unwrap();
let (mut svg_batch, svg_bounds) = svg::load_svg(ctx.prerender, image_path);
let image_source = image_source.unwrap();
let (mut svg_batch, svg_bounds) = image_source.load(ctx.prerender);
if let Some(color) = image.color.or(default.and_then(|d| d.color)) {
svg_batch = svg_batch.color(color);
}
@ -623,9 +643,29 @@ impl<'b, 'a: 'b> ButtonBuilder<'a> {
}
}
#[derive(Clone, Debug)]
enum ImageSource<'a> {
Path(&'a str),
Bytes { bytes: &'a [u8], cache_key: &'a str },
}
impl ImageSource<'_> {
fn load(&self, prerender: &crate::Prerender) -> (GeomBatch, geom::Bounds) {
match self {
ImageSource::Path(image_path) => svg::load_svg(prerender, image_path),
ImageSource::Bytes { bytes, cache_key } => {
svg::load_svg_bytes(prerender, bytes, cache_key).expect(&format!(
"Failed to load svg from bytes. cache_key: {}",
cache_key
))
}
}
}
}
#[derive(Clone, Debug, Default)]
pub struct Image<'a> {
path: Option<&'a str>,
source: Option<ImageSource<'a>>,
color: Option<RewriteColor>,
dims: Option<ScreenDims>,
content_mode: ContentMode,

View File

@ -289,7 +289,7 @@ fn setup_scrollable_canvas(ctx: &mut EventCtx) -> Drawable {
for i in 0..10 {
let mut svg_data = Vec::new();
svg_face::generate_face(&mut svg_data, &mut rng).unwrap();
let face = GeomBatch::from_svg_contents(svg_data).autocrop();
let face = GeomBatch::from_svg_contents(&svg_data).autocrop();
let dims = face.get_dims();
batch.append(
face.scale((200.0 / dims.width).min(200.0 / dims.height))