mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 14:21:16 +00:00
Add support for embedding data in generated C++ code
This allows compiling with SIXTYFPS_EMBED_RESOURCES=true and images/fonts are embedded as inline variables. Generated data is emitted into the header file as inline uint8_t sfps_embedded_resources_123[789] = { 0x1, 0x2, 0x3, };
This commit is contained in:
parent
30e79d3ea2
commit
c91a38cdce
4 changed files with 101 additions and 5 deletions
|
@ -863,7 +863,7 @@ auto blocking_invoke_from_event_loop(Functor f) -> std::invoke_result_t<Functor>
|
||||||
namespace private_api {
|
namespace private_api {
|
||||||
|
|
||||||
/// Registers a font by the specified path. The path must refer to an existing
|
/// Registers a font by the specified path. The path must refer to an existing
|
||||||
/// TrueType font font.
|
/// TrueType font.
|
||||||
/// \returns an empty optional on success, otherwise an error string
|
/// \returns an empty optional on success, otherwise an error string
|
||||||
inline std::optional<SharedString> register_font_from_path(const SharedString &path)
|
inline std::optional<SharedString> register_font_from_path(const SharedString &path)
|
||||||
{
|
{
|
||||||
|
@ -876,6 +876,20 @@ inline std::optional<SharedString> register_font_from_path(const SharedString &p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Registers a font by the data. The data must be valid TrueType font data.
|
||||||
|
/// \returns an empty optional on success, otherwise an error string
|
||||||
|
inline std::optional<SharedString> register_font_from_data(const uint8_t *data, std::size_t len)
|
||||||
|
{
|
||||||
|
SharedString maybe_err;
|
||||||
|
cbindgen_private::sixtyfps_register_font_from_data({ const_cast<uint8_t *>(data), len },
|
||||||
|
&maybe_err);
|
||||||
|
if (!maybe_err.empty()) {
|
||||||
|
return maybe_err;
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace sixtyfps
|
} // namespace sixtyfps
|
||||||
|
|
|
@ -69,6 +69,9 @@ public:
|
||||||
/// Returns false if \a a refers to the same image as \a b; true otherwise.
|
/// Returns false if \a a refers to the same image as \a b; true otherwise.
|
||||||
friend bool operator!=(const Image &a, const Image &b) { return a.data != b.data; }
|
friend bool operator!=(const Image &a, const Image &b) { return a.data != b.data; }
|
||||||
|
|
||||||
|
/// \private
|
||||||
|
explicit Image(cbindgen_private::types::Image inner) : data(inner) { }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using Tag = cbindgen_private::types::ImageInner::Tag;
|
using Tag = cbindgen_private::types::ImageInner::Tag;
|
||||||
using Data = cbindgen_private::types::Image;
|
using Data = cbindgen_private::types::Image;
|
||||||
|
|
|
@ -81,6 +81,20 @@ pub unsafe extern "C" fn sixtyfps_register_font_from_path(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sixtyfps_register_font_from_data(
|
||||||
|
data: sixtyfps_corelib::slice::Slice<'static, u8>,
|
||||||
|
error_str: *mut sixtyfps_corelib::SharedString,
|
||||||
|
) {
|
||||||
|
core::ptr::write(
|
||||||
|
error_str,
|
||||||
|
match crate::backend().register_font_from_memory(data.as_slice()) {
|
||||||
|
Ok(()) => Default::default(),
|
||||||
|
Err(err) => err.to_string().into(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "testing")]
|
#[cfg(feature = "testing")]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn sixtyfps_testing_init_backend() {
|
pub unsafe extern "C" fn sixtyfps_testing_init_backend() {
|
||||||
|
|
|
@ -181,6 +181,7 @@ mod cpp_ast {
|
||||||
pub struct Var {
|
pub struct Var {
|
||||||
pub ty: String,
|
pub ty: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub array_size: Option<usize>,
|
||||||
pub init: Option<String>,
|
pub init: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,6 +189,9 @@ mod cpp_ast {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
||||||
indent(f)?;
|
indent(f)?;
|
||||||
write!(f, "{} {}", self.ty, self.name)?;
|
write!(f, "{} {}", self.ty, self.name)?;
|
||||||
|
if let Some(size) = self.array_size {
|
||||||
|
write!(f, "[{}]", size)?;
|
||||||
|
}
|
||||||
if let Some(i) = &self.init {
|
if let Some(i) = &self.init {
|
||||||
write!(f, " = {}", i)?;
|
write!(f, " = {}", i)?;
|
||||||
}
|
}
|
||||||
|
@ -493,6 +497,7 @@ fn handle_item(elem: &ElementRc, main_struct: &mut Struct) {
|
||||||
),
|
),
|
||||||
name: ident(&item.id).into_owned(),
|
name: ident(&item.id).into_owned(),
|
||||||
init: Some("{}".to_owned()),
|
init: Some("{}".to_owned()),
|
||||||
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -578,6 +583,7 @@ fn handle_repeater(
|
||||||
),
|
),
|
||||||
name: repeater_id,
|
name: repeater_id,
|
||||||
init: None,
|
init: None,
|
||||||
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -592,6 +598,34 @@ pub fn generate(doc: &Document, diag: &mut BuildDiagnostics) -> Option<impl std:
|
||||||
file.includes.push("<cmath>".into()); // TODO: ideally only include this if needed (by floor/ceil/round)
|
file.includes.push("<cmath>".into()); // TODO: ideally only include this if needed (by floor/ceil/round)
|
||||||
file.includes.push("<sixtyfps.h>".into());
|
file.includes.push("<sixtyfps.h>".into());
|
||||||
|
|
||||||
|
file.declarations.extend(doc.root_component.embedded_file_resources.borrow().iter().map(
|
||||||
|
|(path, id)| {
|
||||||
|
let data =
|
||||||
|
std::fs::read(path).expect(&format!("unable to read file for embedding: {}", path));
|
||||||
|
|
||||||
|
let mut init = "{ ".to_string();
|
||||||
|
|
||||||
|
for (index, byte) in data.iter().enumerate() {
|
||||||
|
if index > 0 {
|
||||||
|
init.push(',');
|
||||||
|
}
|
||||||
|
init.push_str(&format!("0x{:x}", byte));
|
||||||
|
if index % 16 == 0 {
|
||||||
|
init.push('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init.push_str("}");
|
||||||
|
|
||||||
|
Declaration::Var(Var {
|
||||||
|
ty: "inline uint8_t".into(),
|
||||||
|
name: format!("sfps_embedded_resource_{}", id),
|
||||||
|
array_size: Some(data.len()),
|
||||||
|
init: Some(init),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
for ty in doc.root_component.used_types.borrow().structs.iter() {
|
for ty in doc.root_component.used_types.borrow().structs.iter() {
|
||||||
if let Type::Struct { fields, name: Some(name), node: Some(_) } = ty {
|
if let Type::Struct { fields, name: Some(name), node: Some(_) } = ty {
|
||||||
generate_struct(&mut file, name, fields, diag);
|
generate_struct(&mut file, name, fields, diag);
|
||||||
|
@ -626,7 +660,8 @@ pub fn generate(doc: &Document, diag: &mut BuildDiagnostics) -> Option<impl std:
|
||||||
env!("CARGO_PKG_VERSION_MINOR"),
|
env!("CARGO_PKG_VERSION_MINOR"),
|
||||||
env!("CARGO_PKG_VERSION_PATCH")),
|
env!("CARGO_PKG_VERSION_PATCH")),
|
||||||
name: "THE_SAME_VERSION_MUST_BE_USED_FOR_THE_COMPILER_AND_THE_RUNTIME".into(),
|
name: "THE_SAME_VERSION_MUST_BE_USED_FOR_THE_COMPILER_AND_THE_RUNTIME".into(),
|
||||||
init: Some("sixtyfps::private_api::VersionCheckHelper<int(sixtyfps::private_api::VersionCheck::Major), int(sixtyfps::private_api::VersionCheck::Minor), int(sixtyfps::private_api::VersionCheck::Patch)>()".into())
|
init: Some("sixtyfps::private_api::VersionCheckHelper<int(sixtyfps::private_api::VersionCheck::Major), int(sixtyfps::private_api::VersionCheck::Minor), int(sixtyfps::private_api::VersionCheck::Patch)>()".into()),
|
||||||
|
..Default::default()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if diag.has_error() {
|
if diag.has_error() {
|
||||||
|
@ -812,7 +847,12 @@ fn generate_component(
|
||||||
if property_decl.is_alias.is_none() {
|
if property_decl.is_alias.is_none() {
|
||||||
component_struct.members.push((
|
component_struct.members.push((
|
||||||
if component.is_global() { Access::Public } else { Access::Private },
|
if component.is_global() { Access::Public } else { Access::Private },
|
||||||
Declaration::Var(Var { ty, name: cpp_name.into_owned(), init: None }),
|
Declaration::Var(Var {
|
||||||
|
ty,
|
||||||
|
name: cpp_name.into_owned(),
|
||||||
|
init: None,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -830,6 +870,7 @@ fn generate_component(
|
||||||
ty: "sixtyfps::private_api::Property<int>".into(),
|
ty: "sixtyfps::private_api::Property<int>".into(),
|
||||||
name: "index".into(),
|
name: "index".into(),
|
||||||
init: None,
|
init: None,
|
||||||
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -839,6 +880,7 @@ fn generate_component(
|
||||||
ty: format!("sixtyfps::private_api::Property<{}>", cpp_model_data_type),
|
ty: format!("sixtyfps::private_api::Property<{}>", cpp_model_data_type),
|
||||||
name: "model_data".into(),
|
name: "model_data".into(),
|
||||||
init: None,
|
init: None,
|
||||||
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -885,6 +927,7 @@ fn generate_component(
|
||||||
ty: parent_type,
|
ty: parent_type,
|
||||||
name: "parent".into(),
|
name: "parent".into(),
|
||||||
init: Some("nullptr".to_owned()),
|
init: Some("nullptr".to_owned()),
|
||||||
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
component_struct.friends.push(parent_component_id);
|
component_struct.friends.push(parent_component_id);
|
||||||
|
@ -942,6 +985,7 @@ fn generate_component(
|
||||||
ty: "sixtyfps::Window".into(),
|
ty: "sixtyfps::Window".into(),
|
||||||
name: "m_window".into(),
|
name: "m_window".into(),
|
||||||
init: Some("sixtyfps::Window{sixtyfps::private_api::WindowRc()}".into()),
|
init: Some("sixtyfps::Window{sixtyfps::private_api::WindowRc()}".into()),
|
||||||
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
} else if !component.is_global() {
|
} else if !component.is_global() {
|
||||||
|
@ -951,6 +995,7 @@ fn generate_component(
|
||||||
ty: "sixtyfps::Window".into(),
|
ty: "sixtyfps::Window".into(),
|
||||||
name: "m_window".into(),
|
name: "m_window".into(),
|
||||||
init: Some("sixtyfps::Window{sixtyfps::private_api::WindowRc()}".into()),
|
init: Some("sixtyfps::Window{sixtyfps::private_api::WindowRc()}".into()),
|
||||||
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -1283,6 +1328,7 @@ fn generate_component(
|
||||||
ty: "static const sixtyfps::private_api::ComponentVTable".to_owned(),
|
ty: "static const sixtyfps::private_api::ComponentVTable".to_owned(),
|
||||||
name: "static_vtable".to_owned(),
|
name: "static_vtable".to_owned(),
|
||||||
init: None,
|
init: None,
|
||||||
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -1321,6 +1367,7 @@ fn generate_component(
|
||||||
ty: format!("std::shared_ptr<{}>", ty),
|
ty: format!("std::shared_ptr<{}>", ty),
|
||||||
name: global_field_name(glob),
|
name: global_field_name(glob),
|
||||||
init: Some(format!("std::make_shared<{}>()", ty)),
|
init: Some(format!("std::make_shared<{}>()", ty)),
|
||||||
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -1374,6 +1421,7 @@ fn generate_component(
|
||||||
"{{ visit_children, get_item_ref, parent_item, layout_info, sixtyfps::private_api::drop_in_place<{}>, sixtyfps::private_api::dealloc }}",
|
"{{ visit_children, get_item_ref, parent_item, layout_info, sixtyfps::private_api::drop_in_place<{}>, sixtyfps::private_api::dealloc }}",
|
||||||
component_id)
|
component_id)
|
||||||
),
|
),
|
||||||
|
..Default::default()
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1593,7 +1641,7 @@ fn compile_expression(
|
||||||
panic!("internal error: RegisterCustomFontByPath can only be evaluated from within a FunctionCall expression")
|
panic!("internal error: RegisterCustomFontByPath can only be evaluated from within a FunctionCall expression")
|
||||||
}
|
}
|
||||||
BuiltinFunction::RegisterCustomFontByMemory => {
|
BuiltinFunction::RegisterCustomFontByMemory => {
|
||||||
panic!("embedding fonts is not supported in C++ yet")
|
panic!("internal error: RegisterCustomFontByMemory can only be evaluated from within a FunctionCall expression")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Expression::ElementReference(_) => todo!("Element references are only supported in the context of built-in function calls at the moment"),
|
Expression::ElementReference(_) => todo!("Element references are only supported in the context of built-in function calls at the moment"),
|
||||||
|
@ -1742,6 +1790,18 @@ fn compile_expression(
|
||||||
panic!("internal error: argument to RegisterCustomFontByPath must be a string literal")
|
panic!("internal error: argument to RegisterCustomFontByPath must be a string literal")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Expression::BuiltinFunctionReference(BuiltinFunction::RegisterCustomFontByMemory, _) => {
|
||||||
|
if arguments.len() != 1 {
|
||||||
|
panic!("internal error: incorrect argument count to RegisterCustomFontByMemory call");
|
||||||
|
}
|
||||||
|
if let Expression::NumberLiteral(resource_id, _) = &arguments[0] {
|
||||||
|
let resource_id: usize = *resource_id as _;
|
||||||
|
let symbol = format!("sfps_embedded_resource_{}", resource_id);
|
||||||
|
format!("sixtyfps::private_api::register_font_from_data({}, std::size({}));", symbol, symbol)
|
||||||
|
} else {
|
||||||
|
panic!("internal error: argument to RegisterCustomFontByMemory must be a number")
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let mut args = arguments.iter().map(|e| compile_expression(e, component));
|
let mut args = arguments.iter().map(|e| compile_expression(e, component));
|
||||||
|
|
||||||
|
@ -1777,7 +1837,12 @@ fn compile_expression(
|
||||||
match resource_ref {
|
match resource_ref {
|
||||||
crate::expression_tree::ImageReference::None => r#"sixtyfps::Image()"#.to_string(),
|
crate::expression_tree::ImageReference::None => r#"sixtyfps::Image()"#.to_string(),
|
||||||
crate::expression_tree::ImageReference::AbsolutePath(path) => format!(r#"sixtyfps::Image::load_from_path(sixtyfps::SharedString(u8"{}"))"#, escape_string(path.as_str())),
|
crate::expression_tree::ImageReference::AbsolutePath(path) => format!(r#"sixtyfps::Image::load_from_path(sixtyfps::SharedString(u8"{}"))"#, escape_string(path.as_str())),
|
||||||
crate::expression_tree::ImageReference::EmbeddedData { .. } => unimplemented!("The C++ generator does not support resource embedding yet")
|
crate::expression_tree::ImageReference::EmbeddedData { resource_id, extension } => {
|
||||||
|
let symbol = format!("sfps_embedded_resource_{}", resource_id);
|
||||||
|
let data_slice = format!("sixtyfps::Slice<uint8_t>{{std::data({}), std::size({})}}", symbol, symbol);
|
||||||
|
let extension_slice = format!("sixtyfps::Slice<uint8_t>{{const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(\"{}\")), {}}}", extension, extension.as_bytes().len());
|
||||||
|
format!("sixtyfps::Image(sixtyfps::cbindgen_private::types::ImageInner::EmbeddedData({}, {}))", data_slice, extension_slice)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::Condition { condition, true_expr, false_expr } => {
|
Expression::Condition { condition, true_expr, false_expr } => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue