Prepare for the ability to embed image data

The Image's source property used to be a string. Now it is a Resource
enum, which can either be None or an absolute file path to the image on
disk. This also replaces the internal Image type.

The compiler internally resolves the img bang expression to a resource
reference, which shall remain just an absolute path. For now the target
generator passes that through, but in the future the target generator
may choose a target specific way of embedding the data and thus
generating a different Resource type in the final code (through
compile_expression in the cpp and rust generator).

The C++ binding is a bit messy as cbindgen doesn't really support
exporting enums that can be constructed on the C++ side. So instead we
use cbindgen to merely export the type internally and only use the tag
from it then. The public API is then a custom Resource type that is
meant to be binary compatible.
This commit is contained in:
Simon Hausmann 2020-06-09 16:46:59 +02:00
parent a756b7fa0e
commit 5bae6e01a5
18 changed files with 196 additions and 42 deletions

View file

@ -0,0 +1,65 @@
#pragma once
#include <string_view>
#include "sixtyfps_resource_internal.h"
#include "sixtyfps_string.h"
namespace sixtyfps {
union ResourceData {
SharedString absolute_file_path;
ResourceData() { }
~ResourceData() { }
};
struct Resource
{
public:
using Tag = internal::types::Resource::Tag;
Resource() : tag(Tag::None) { }
Resource(const SharedString &file_path) : tag(Tag::AbsoluteFilePath)
{
new (&data.absolute_file_path) SharedString(file_path);
}
Resource(const Resource &other) : tag(other.tag)
{
switch (tag) {
case Tag::None:
break;
case Tag::AbsoluteFilePath:
new (&data.absolute_file_path) SharedString(other.data.absolute_file_path);
}
}
~Resource() { destroy(); }
Resource &operator=(const Resource &other)
{
if (this == &other)
return *this;
destroy();
tag = other.tag;
switch (tag) {
case Tag::None:
break;
case Tag::AbsoluteFilePath:
new (&data.absolute_file_path) SharedString(other.data.absolute_file_path);
}
return *this;
}
private:
void destroy()
{
switch (tag) {
case Tag::None:
break;
case Tag::AbsoluteFilePath:
data.absolute_file_path.~SharedString();
}
}
Tag tag;
ResourceData data;
};
}

View file

@ -1,7 +1,7 @@
use core::cell::RefCell; use core::cell::RefCell;
use neon::prelude::*; use neon::prelude::*;
use sixtyfps_compilerlib::typeregister::Type; use sixtyfps_compilerlib::typeregister::Type;
use sixtyfps_corelib::abi::datastructures::{ComponentBox, ComponentRef}; use sixtyfps_corelib::abi::datastructures::{ComponentBox, ComponentRef, Resource};
use std::rc::Rc; use std::rc::Rc;
mod persistent_context; mod persistent_context;
@ -112,7 +112,7 @@ fn to_eval_value<'cx>(
} }
Type::String => Ok(Value::String(val.to_string(cx)?.value().as_str().into())), Type::String => Ok(Value::String(val.to_string(cx)?.value().as_str().into())),
Type::Color => todo!(), Type::Color => todo!(),
Type::Image => todo!(), Type::Resource => Ok(Value::String(val.to_string(cx)?.value().as_str().into())),
Type::Bool => Ok(Value::Bool(val.downcast_or_throw::<JsBoolean, _>(cx)?.value())), Type::Bool => Ok(Value::Bool(val.downcast_or_throw::<JsBoolean, _>(cx)?.value())),
} }
} }
@ -124,6 +124,10 @@ fn to_js_value<'cx>(val: interpreter::Value, cx: &mut impl Context<'cx>) -> Hand
Value::Number(n) => JsNumber::new(cx, n).as_value(cx), Value::Number(n) => JsNumber::new(cx, n).as_value(cx),
Value::String(s) => JsString::new(cx, s.as_str()).as_value(cx), Value::String(s) => JsString::new(cx, s.as_str()).as_value(cx),
Value::Bool(b) => JsBoolean::new(cx, b).as_value(cx), Value::Bool(b) => JsBoolean::new(cx, b).as_value(cx),
Value::Resource(r) => match r {
Resource::None => JsUndefined::new().as_value(cx),
Resource::AbsoluteFilePath(path) => JsString::new(cx, path.as_str()).as_value(cx),
},
} }
} }

View file

@ -78,6 +78,7 @@ pub mod re_exports {
pub use sixtyfps_corelib::abi::signals::Signal; pub use sixtyfps_corelib::abi::signals::Signal;
pub use sixtyfps_corelib::ComponentVTable_static; pub use sixtyfps_corelib::ComponentVTable_static;
pub use sixtyfps_corelib::EvaluationContext; pub use sixtyfps_corelib::EvaluationContext;
pub use sixtyfps_corelib::Resource;
pub use sixtyfps_corelib::SharedString; pub use sixtyfps_corelib::SharedString;
pub use sixtyfps_rendering_backend_gl::sixtyfps_runtime_run_component_with_gl_renderer; pub use sixtyfps_rendering_backend_gl::sixtyfps_runtime_run_component_with_gl_renderer;
pub use vtable::{self, *}; pub use vtable::{self, *};

View file

@ -1,5 +1,5 @@
use cgmath::{Matrix4, SquareMatrix, Vector3}; use cgmath::{Matrix4, SquareMatrix, Vector3};
use sixtyfps_corelib::abi::datastructures::{Color, RenderingPrimitive}; use sixtyfps_corelib::abi::datastructures::{Color, RenderingPrimitive, Resource};
use sixtyfps_corelib::graphics::{ use sixtyfps_corelib::graphics::{
Frame, GraphicsBackend, RenderingCache, RenderingPrimitivesBuilder, Frame, GraphicsBackend, RenderingCache, RenderingPrimitivesBuilder,
}; };
@ -57,7 +57,7 @@ fn main() {
let image_primitive = rendering_primitives_builder.create(RenderingPrimitive::Image { let image_primitive = rendering_primitives_builder.create(RenderingPrimitive::Image {
x: 0., x: 0.,
y: 0., y: 0.,
source: logo_path.to_str().unwrap().into(), source: Resource::AbsoluteFilePath(logo_path.to_str().unwrap().into()),
}); });
render_cache.allocate_entry(image_primitive) render_cache.allocate_entry(image_primitive)

View file

@ -18,21 +18,34 @@ pub enum Expression {
/// Reference to the signal <name> in the <element> within the <Component> /// Reference to the signal <name> in the <element> within the <Component>
/// ///
/// Note: if we are to separate expression and statement, we probably do not need to have signal reference within expressions /// Note: if we are to separate expression and statement, we probably do not need to have signal reference within expressions
SignalReference { component: Weak<Component>, element: Weak<RefCell<Element>>, name: String }, SignalReference {
component: Weak<Component>,
element: Weak<RefCell<Element>>,
name: String,
},
/// Reference to the signal <name> in the <element> within the <Component> /// Reference to the signal <name> in the <element> within the <Component>
/// ///
/// Note: if we are to separate expression and statement, we probably do not need to have signal reference within expressions /// Note: if we are to separate expression and statement, we probably do not need to have signal reference within expressions
PropertyReference { component: Weak<Component>, element: Weak<RefCell<Element>>, name: String }, PropertyReference {
component: Weak<Component>,
element: Weak<RefCell<Element>>,
name: String,
},
/// Cast an expression to the given type /// Cast an expression to the given type
Cast { from: Box<Expression>, to: Type }, Cast {
from: Box<Expression>,
to: Type,
},
/// a code block with different expression /// a code block with different expression
CodeBlock(Vec<Expression>), CodeBlock(Vec<Expression>),
/// A function call /// A function call
FunctionCall { function: Box<Expression> }, FunctionCall {
function: Box<Expression>,
},
SelfAssignement { SelfAssignement {
lhs: Box<Expression>, lhs: Box<Expression>,
@ -40,6 +53,10 @@ pub enum Expression {
/// '+', '-', '/', or '*' /// '+', '-', '/', or '*'
op: char, op: char,
}, },
ResourceReference {
absolute_source_path: String,
},
} }
impl Expression { impl Expression {
@ -58,6 +75,7 @@ impl Expression {
Expression::CodeBlock(sub) => sub.last().map_or(Type::Invalid, |e| e.ty()), Expression::CodeBlock(sub) => sub.last().map_or(Type::Invalid, |e| e.ty()),
Expression::FunctionCall { function } => function.ty(), Expression::FunctionCall { function } => function.ty(),
Expression::SelfAssignement { .. } => Type::Invalid, Expression::SelfAssignement { .. } => Type::Invalid,
Expression::ResourceReference { .. } => Type::Resource,
} }
} }
@ -81,6 +99,7 @@ impl Expression {
visitor(&**lhs); visitor(&**lhs);
visitor(&**rhs); visitor(&**rhs);
} }
Expression::ResourceReference { .. } => {}
} }
} }
@ -103,6 +122,7 @@ impl Expression {
visitor(&mut **lhs); visitor(&mut **lhs);
visitor(&mut **rhs); visitor(&mut **rhs);
} }
Expression::ResourceReference { .. } => {}
} }
} }
@ -118,6 +138,7 @@ impl Expression {
Expression::CodeBlock(sub) => sub.len() == 1 && sub.first().unwrap().is_constant(), Expression::CodeBlock(sub) => sub.len() == 1 && sub.first().unwrap().is_constant(),
Expression::FunctionCall { .. } => false, Expression::FunctionCall { .. } => false,
Expression::SelfAssignement { .. } => false, Expression::SelfAssignement { .. } => false,
Expression::ResourceReference { .. } => true,
} }
} }
} }

View file

@ -354,6 +354,9 @@ fn compile_expression(e: &crate::expression_tree::Expression) -> String {
), ),
_ => panic!("typechecking should make sure this was a PropertyReference"), _ => panic!("typechecking should make sure this was a PropertyReference"),
}, },
ResourceReference { absolute_source_path } => {
format!(r#"sixtyfps::Resource(sixtyfps::SharedString("{}"))"#, absolute_source_path)
}
Uncompiled(_) => panic!(), Uncompiled(_) => panic!(),
Invalid => format!("\n#error invalid expression\n"), Invalid => format!("\n#error invalid expression\n"),
} }

View file

@ -212,6 +212,9 @@ fn compile_expression(e: &Expression) -> TokenStream {
} }
_ => panic!("typechecking should make sure this was a PropertyReference"), _ => panic!("typechecking should make sure this was a PropertyReference"),
}, },
Expression::ResourceReference { absolute_source_path } => {
quote!(sixtyfps::re_exports::Resource::AbsoluteFilePath(sixtyfps::re_exports::SharedString::from(#absolute_source_path)))
}
_ => { _ => {
let error = format!("unsupported expression {:?}", e); let error = format!("unsupported expression {:?}", e);
quote!(compile_error! {#error}) quote!(compile_error! {#error})

View file

@ -163,18 +163,27 @@ impl Expression {
return Self::Invalid; return Self::Invalid;
} }
}; };
let path = std::path::Path::new(&s);
if path.is_absolute() { let absolute_source_path = {
return Expression::StringLiteral(s); let path = std::path::Path::new(&s);
}
let path = ctx.diag.path(node.span()).parent().unwrap().join(path); if path.is_absolute() {
if path.is_absolute() { s
return Expression::StringLiteral(path.to_string_lossy().to_string()); } else {
} let path = ctx.diag.path(node.span()).parent().unwrap().join(path);
Expression::StringLiteral( if path.is_absolute() {
std::env::current_dir().unwrap().join(path).to_string_lossy().to_string(), path.to_string_lossy().to_string()
) } else {
std::env::current_dir()
.unwrap()
.join(path)
.to_string_lossy()
.to_string()
}
}
};
Expression::ResourceReference { absolute_source_path }
} }
Some(x) => { Some(x) => {
ctx.diag.push_error(format!("Unknown bang keyword `{}`", x), node.span()); ctx.diag.push_error(format!("Unknown bang keyword `{}`", x), node.span());

View file

@ -5,7 +5,7 @@ SubElements := Rectangle {
color: blue; color: blue;
Image { Image {
source: "foo.png"; source: img!"foo.png";
} }

View file

@ -15,7 +15,7 @@ pub enum Type {
Int32, Int32,
String, String,
Color, Color,
Image, Resource,
Bool, Bool,
} }
@ -30,7 +30,7 @@ impl core::cmp::PartialEq for Type {
(Type::Int32, Type::Int32) => true, (Type::Int32, Type::Int32) => true,
(Type::String, Type::String) => true, (Type::String, Type::String) => true,
(Type::Color, Type::Color) => true, (Type::Color, Type::Color) => true,
(Type::Image, Type::Image) => true, (Type::Resource, Type::Resource) => true,
(Type::Bool, Type::Bool) => true, (Type::Bool, Type::Bool) => true,
_ => false, _ => false,
} }
@ -48,7 +48,7 @@ impl Display for Type {
Type::Int32 => write!(f, "int32"), Type::Int32 => write!(f, "int32"),
Type::String => write!(f, "string"), Type::String => write!(f, "string"),
Type::Color => write!(f, "color"), Type::Color => write!(f, "color"),
Type::Image => write!(f, "image"), Type::Resource => write!(f, "resource"),
Type::Bool => write!(f, "bool"), Type::Bool => write!(f, "bool"),
} }
} }
@ -63,7 +63,7 @@ impl Type {
pub fn is_property_type(&self) -> bool { pub fn is_property_type(&self) -> bool {
matches!( matches!(
self, self,
Self::Float32 | Self::Int32 | Self::String | Self::Color | Self::Image | Self::Bool Self::Float32 | Self::Int32 | Self::String | Self::Color | Self::Resource | Self::Bool
) )
} }
@ -95,7 +95,6 @@ impl Type {
// FIXME: REMOVE // FIXME: REMOVE
| (Type::Float32, Type::Color) | (Type::Float32, Type::Color)
| (Type::Int32, Type::Color) | (Type::Int32, Type::Color)
| (Type::String, Type::Image)
) )
} }
} }
@ -135,7 +134,7 @@ impl TypeRegister {
instert_type(Type::Int32); instert_type(Type::Int32);
instert_type(Type::String); instert_type(Type::String);
instert_type(Type::Color); instert_type(Type::Color);
instert_type(Type::Image); instert_type(Type::Resource);
instert_type(Type::Bool); instert_type(Type::Bool);
let mut rectangle = BuiltinElement::new("Rectangle"); let mut rectangle = BuiltinElement::new("Rectangle");
@ -147,7 +146,7 @@ impl TypeRegister {
r.types.insert("Rectangle".to_owned(), Type::Builtin(Rc::new(rectangle))); r.types.insert("Rectangle".to_owned(), Type::Builtin(Rc::new(rectangle)));
let mut image = BuiltinElement::new("Image"); let mut image = BuiltinElement::new("Image");
image.properties.insert("source".to_owned(), Type::Image); image.properties.insert("source".to_owned(), Type::Resource);
image.properties.insert("x".to_owned(), Type::Float32); image.properties.insert("x".to_owned(), Type::Float32);
image.properties.insert("y".to_owned(), Type::Float32); image.properties.insert("y".to_owned(), Type::Float32);
image.properties.insert("width".to_owned(), Type::Float32); image.properties.insert("width".to_owned(), Type::Float32);

View file

@ -204,6 +204,24 @@ impl Color {
pub const WHITE: Color = Color::from_rgb(255, 255, 255); pub const WHITE: Color = Color::from_rgb(255, 255, 255);
} }
/// A resource is a reference to binary data, for example images. They can be accessible on the file
/// system or embedded in the resulting binary. Or they might be URLs to a web server and a downloaded
/// is necessary before they can be used.
#[derive(Clone, PartialEq, Debug)]
#[repr(C)]
pub enum Resource {
/// A resource that does not represent any data.
None,
/// A resource that points to a file in the file system
AbsoluteFilePath(crate::SharedString),
}
impl Default for Resource {
fn default() -> Self {
Resource::None
}
}
/// Each item return a RenderingPrimitive to the backend with information about what to draw. /// Each item return a RenderingPrimitive to the backend with information about what to draw.
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
#[repr(C)] #[repr(C)]
@ -221,7 +239,7 @@ pub enum RenderingPrimitive {
Image { Image {
x: f32, x: f32,
y: f32, y: f32,
source: crate::SharedString, source: crate::Resource,
}, },
Text { Text {
x: f32, x: f32,

View file

@ -16,7 +16,7 @@ When adding an item or a property, it needs to be kept in sync with different pl
#![allow(missing_docs)] // because documenting each property of items is redundent #![allow(missing_docs)] // because documenting each property of items is redundent
use super::datastructures::{ use super::datastructures::{
CachedRenderingData, Color, Item, ItemConsts, LayoutInfo, Rect, RenderingPrimitive, CachedRenderingData, Color, Item, ItemConsts, LayoutInfo, Rect, RenderingPrimitive, Resource,
}; };
use crate::rtti::*; use crate::rtti::*;
use crate::{EvaluationContext, Property, SharedString, Signal}; use crate::{EvaluationContext, Property, SharedString, Signal};
@ -81,8 +81,7 @@ pub use crate::abi::datastructures::RectangleVTable;
#[derive(FieldOffsets, Default, BuiltinItem)] #[derive(FieldOffsets, Default, BuiltinItem)]
/// The implementation of the `Image` element /// The implementation of the `Image` element
pub struct Image { pub struct Image {
/// FIXME: make it a image source pub source: Property<Resource>,
pub source: Property<SharedString>,
pub x: Property<f32>, pub x: Property<f32>,
pub y: Property<f32>, pub y: Property<f32>,
pub width: Property<f32>, pub width: Property<f32>,

View file

@ -9,7 +9,8 @@ fn main() {
.map(|x| x.to_string()) .map(|x| x.to_string())
.collect::<Vec<String>>(); .collect::<Vec<String>>();
let exclude = ["SharedString"].iter().map(|x| x.to_string()).collect::<Vec<String>>(); let exclude =
["SharedString", "Resource"].iter().map(|x| x.to_string()).collect::<Vec<String>>();
let config = cbindgen::Config { let config = cbindgen::Config {
pragma_once: true, pragma_once: true,
@ -56,6 +57,23 @@ fn main() {
.expect("Unable to generate bindings") .expect("Unable to generate bindings")
.write_to_file(include_dir.join("sixtyfps_signals_internal.h")); .write_to_file(include_dir.join("sixtyfps_signals_internal.h"));
let mut resource_config = config.clone();
resource_config.export.include = ["Resource"].iter().map(|s| s.to_string()).collect();
resource_config.enumeration = cbindgen::EnumConfig {
derive_tagged_enum_copy_assignment: true,
derive_tagged_enum_copy_constructor: true,
derive_tagged_enum_destructor: true,
..Default::default()
};
resource_config.namespaces = Some(vec!["sixtyfps".into(), "internal".into(), "types".into()]);
resource_config.export.exclude.clear();
cbindgen::Builder::new()
.with_config(resource_config)
.with_src(crate_dir.join("abi/datastructures.rs"))
.generate()
.expect("Unable to generate bindings")
.write_to_file(include_dir.join("sixtyfps_resource_internal.h"));
cbindgen::Builder::new() cbindgen::Builder::new()
.with_config(config) .with_config(config)
.with_src(crate_dir.join("abi/datastructures.rs")) .with_src(crate_dir.join("abi/datastructures.rs"))
@ -65,6 +83,7 @@ fn main() {
.with_include("sixtyfps_string.h") .with_include("sixtyfps_string.h")
.with_include("sixtyfps_properties.h") .with_include("sixtyfps_properties.h")
.with_include("sixtyfps_signals.h") .with_include("sixtyfps_signals.h")
.with_include("sixtyfps_resource.h")
.generate() .generate()
.expect("Unable to generate bindings") .expect("Unable to generate bindings")
.write_to_file(include_dir.join("sixtyfps_internal.h")); .write_to_file(include_dir.join("sixtyfps_internal.h"));

View file

@ -28,6 +28,9 @@ pub mod rtti;
#[doc(inline)] #[doc(inline)]
pub use abi::string::SharedString; pub use abi::string::SharedString;
#[doc(inline)]
pub use abi::datastructures::Resource;
#[doc(inline)] #[doc(inline)]
pub use abi::properties::{EvaluationContext, Property}; pub use abi::properties::{EvaluationContext, Property};

View file

@ -11,7 +11,7 @@ macro_rules! declare_ValueType {
pub trait ValueType: 'static $(+ TryInto<$ty> + TryFrom<$ty>)* {} pub trait ValueType: 'static $(+ TryInto<$ty> + TryFrom<$ty>)* {}
}; };
} }
declare_ValueType![bool, u32, u64, i32, i64, f32, f64, crate::SharedString]; declare_ValueType![bool, u32, u64, i32, i64, f32, f64, crate::SharedString, crate::Resource];
pub trait PropertyInfo<Item, Value> { pub trait PropertyInfo<Item, Value> {
fn get(&self, item: &Item, context: &crate::EvaluationContext) -> Result<Value, ()>; fn get(&self, item: &Item, context: &crate::EvaluationContext) -> Result<Value, ()>;

View file

@ -6,7 +6,7 @@ use object_tree::Element;
use sixtyfps_compilerlib::typeregister::Type; use sixtyfps_compilerlib::typeregister::Type;
use sixtyfps_compilerlib::*; use sixtyfps_compilerlib::*;
use sixtyfps_corelib::abi::datastructures::{ use sixtyfps_corelib::abi::datastructures::{
ComponentBox, ComponentRef, ComponentVTable, ItemTreeNode, ItemVTable, ComponentBox, ComponentRef, ComponentVTable, ItemTreeNode, ItemVTable, Resource,
}; };
use sixtyfps_corelib::rtti::PropertyInfo; use sixtyfps_corelib::rtti::PropertyInfo;
use sixtyfps_corelib::{rtti, EvaluationContext, Property, SharedString, Signal}; use sixtyfps_corelib::{rtti, EvaluationContext, Property, SharedString, Signal};
@ -153,7 +153,7 @@ pub fn load(
Type::Int32 => property_info::<u32>(), Type::Int32 => property_info::<u32>(),
Type::String => property_info::<SharedString>(), Type::String => property_info::<SharedString>(),
Type::Color => property_info::<u32>(), Type::Color => property_info::<u32>(),
Type::Image => property_info::<SharedString>(), Type::Resource => property_info::<Resource>(),
Type::Bool => property_info::<bool>(), Type::Bool => property_info::<bool>(),
Type::Signal => { Type::Signal => {
custom_signals.insert(name.clone(), builder.add_field_type::<Signal<()>>()); custom_signals.insert(name.clone(), builder.add_field_type::<Signal<()>>());

View file

@ -1,7 +1,7 @@
use sixtyfps_compilerlib::expression_tree::Expression; use sixtyfps_compilerlib::expression_tree::Expression;
use sixtyfps_compilerlib::typeregister::Type; use sixtyfps_compilerlib::typeregister::Type;
use sixtyfps_corelib as corelib; use sixtyfps_corelib as corelib;
use sixtyfps_corelib::{abi::datastructures::ItemRef, EvaluationContext, SharedString}; use sixtyfps_corelib::{abi::datastructures::ItemRef, EvaluationContext, Resource, SharedString};
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
pub trait ErasedPropertyInfo { pub trait ErasedPropertyInfo {
@ -30,6 +30,7 @@ pub enum Value {
Number(f64), Number(f64),
String(SharedString), String(SharedString),
Bool(bool), Bool(bool),
Resource(Resource),
} }
impl corelib::rtti::ValueType for Value {} impl corelib::rtti::ValueType for Value {}
@ -59,6 +60,7 @@ macro_rules! declare_value_conversion {
declare_value_conversion!(Number => [u32, u64, i32, i64, f32, f64] ); declare_value_conversion!(Number => [u32, u64, i32, i64, f32, f64] );
declare_value_conversion!(String => [SharedString] ); declare_value_conversion!(String => [SharedString] );
declare_value_conversion!(Bool => [bool] ); declare_value_conversion!(Bool => [bool] );
declare_value_conversion!(Resource => [Resource] );
pub fn eval_expression( pub fn eval_expression(
e: &Expression, e: &Expression,
@ -160,5 +162,8 @@ pub fn eval_expression(
} }
_ => panic!("typechecking should make sure this was a PropertyReference"), _ => panic!("typechecking should make sure this was a PropertyReference"),
}, },
Expression::ResourceReference { absolute_source_path } => {
Value::Resource(Resource::AbsoluteFilePath(absolute_source_path.as_str().into()))
}
} }
} }

View file

@ -5,7 +5,7 @@ use itertools::Itertools;
use lyon::tessellation::geometry_builder::{BuffersBuilder, VertexBuffers}; use lyon::tessellation::geometry_builder::{BuffersBuilder, VertexBuffers};
use lyon::tessellation::{FillAttributes, FillOptions, FillTessellator}; use lyon::tessellation::{FillAttributes, FillOptions, FillTessellator};
use sixtyfps_corelib::abi::datastructures::{ use sixtyfps_corelib::abi::datastructures::{
Color, ComponentVTable, Point, Rect, RenderingPrimitive, Size, Color, ComponentVTable, Point, Rect, RenderingPrimitive, Resource, Size,
}; };
use sixtyfps_corelib::graphics::{ use sixtyfps_corelib::graphics::{
FillStyle, Frame as GraphicsFrame, GraphicsBackend, HasRenderingPrimitive, FillStyle, Frame as GraphicsFrame, GraphicsBackend, HasRenderingPrimitive,
@ -306,11 +306,16 @@ impl RenderingPrimitivesBuilder for GLRenderingPrimitivesBuilder {
Some(self.create_path(&rect_path.build(), FillStyle::SolidColor(*color))) Some(self.create_path(&rect_path.build(), FillStyle::SolidColor(*color)))
} }
RenderingPrimitive::Image { x: _, y: _, source } => { RenderingPrimitive::Image { x: _, y: _, source } => {
let mut image_path = std::env::current_exe().unwrap(); match source {
image_path.pop(); // pop of executable name Resource::AbsoluteFilePath(path) => {
image_path.push(&*source.clone()); let mut image_path = std::env::current_exe().unwrap();
let image = image::open(image_path.as_path()).unwrap().into_rgba(); image_path.pop(); // pop of executable name
Some(self.create_image(image)) image_path.push(&*path.clone());
let image = image::open(image_path.as_path()).unwrap().into_rgba();
Some(self.create_image(image))
}
Resource::None => None,
}
} }
RenderingPrimitive::Text { RenderingPrimitive::Text {
x: _, x: _,