Attempt to get the "image-in-corelib" feature compile with C++

This commit is contained in:
Olivier Goffart 2022-06-29 14:55:22 +02:00 committed by Simon Hausmann
parent 67a2f0ce3f
commit 65346c699c
7 changed files with 90 additions and 38 deletions

View file

@ -199,6 +199,7 @@ fn gen_corelib(
"slint_image_size",
"slint_image_path",
"slint_image_load_from_path",
"slint_image_load_from_embedded_data",
"Coord",
]
.iter()
@ -253,7 +254,7 @@ fn gen_corelib(
.context("Unable to generate bindings for slint_properties_internal.h")?
.write_to_file(include_dir.join("slint_properties_internal.h"));
for (rust_types, extra_excluded_types, internal_header) in [
for (rust_types, extra_excluded_types, internal_header, prelude) in [
(
vec![
"ImageInner",
@ -262,27 +263,32 @@ fn gen_corelib(
"slint_image_size",
"slint_image_path",
"slint_image_load_from_path",
"slint_image_load_from_embedded_data",
"SharedPixelBuffer",
"SharedImageBuffer",
"StaticTextures",
],
vec!["Color"],
"slint_image_internal.h",
"namespace slint::cbindgen_private { struct ParsedSVG{}; struct HTMLImage{}; using namespace vtable; }",
),
(
vec!["Color", "slint_color_brighter", "slint_color_darker"],
vec![],
"slint_color_internal.h",
"",
),
(
vec!["PathData", "PathElement", "slint_new_path_elements", "slint_new_path_events"],
vec![],
"slint_pathdata_internal.h",
"",
),
(
vec!["Brush", "LinearGradient", "GradientStop", "RadialGradient"],
vec!["Color"],
"slint_brush_internal.h",
"",
),
]
.iter()
@ -314,6 +320,7 @@ fn gen_corelib(
"slint_image_size",
"slint_image_path",
"slint_image_load_from_path",
"slint_image_load_from_embedded_data",
]
.iter()
.filter(|exclusion| !rust_types.iter().any(|inclusion| inclusion == *exclusion))
@ -338,6 +345,8 @@ fn gen_corelib(
private_exported_types.extend(special_config.export.include.iter().cloned());
special_config.after_includes = (!prelude.is_empty()).then(|| prelude.to_string());
cbindgen::Builder::new()
.with_config(special_config)
.with_src(crate_dir.join("graphics.rs"))

View file

@ -60,4 +60,13 @@ private:
Data data;
};
namespace private_api {
inline Image load_image_from_embedded_data(std::span<const uint8_t> data, std::string_view extension) {
cbindgen_private::types::Image img(cbindgen_private::types::Image::ImageInner_None());
cbindgen_private::types::slint_image_load_from_embedded_data(
slint::cbindgen_private::Slice<uint8_t>{const_cast<uint8_t *>(data.data()), data.size()}, slint::cbindgen_private::Slice<uint8_t>{const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(extension.data())), extension.size()}, &img);
return Image(img);
}
}
}

View file

@ -451,6 +451,7 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
if ident == "drop_in_place" {
vtable_ctor.push(quote!(#ident: {
#[allow(unsafe_code)]
#sig_extern {
#[allow(unused_unsafe)]
unsafe { ::core::ptr::drop_in_place((#self_call).0 as *mut T) };
@ -460,6 +461,7 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
},));
drop_impls.push(quote! {
#[allow(unsafe_code)]
unsafe impl VTableMetaDropInPlace for #vtable_name {
unsafe fn #ident(vtable: &Self::VTable, ptr: *mut u8) -> vtable::Layout {
// Safety: The vtable is valid and ptr is a type corresponding to the vtable,
@ -474,6 +476,7 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
}
if ident == "dealloc" {
vtable_ctor.push(quote!(#ident: {
#[allow(unsafe_code)]
unsafe extern "C" fn #ident(_: &#vtable_name, ptr: *mut u8, layout: vtable::Layout) {
use ::core::convert::TryInto;
vtable::internal::dealloc(ptr, layout.try_into().unwrap())
@ -762,6 +765,7 @@ and implements HasStaticVTable for it.
#(#vtable_ctor)*
}
};
#[allow(unsafe_code)]
unsafe impl vtable::HasStaticVTable<#vtable_name> for $ty {
fn static_vtable() -> &'static #vtable_name {
&$ident

View file

@ -2201,10 +2201,7 @@ fn compile_expression(expr: &llr::Expression, ctx: &EvaluationContext) -> String
crate::expression_tree::ImageReference::AbsolutePath(path) => format!(r#"slint::Image::load_from_path(slint::SharedString(u8"{}"))"#, escape_string(path.as_str())),
crate::expression_tree::ImageReference::EmbeddedData { resource_id, extension } => {
let symbol = format!("slint_embedded_resource_{}", resource_id);
format!(
r#"slint::Image(slint::cbindgen_private::types::ImageInner::ImageInner_EmbeddedData(slint::cbindgen_private::Slice<uint8_t>{{std::data({}), std::size({})}}, slint::cbindgen_private::Slice<uint8_t>{{const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(u8"{}")), {}}}))"#,
symbol, symbol, escape_string(extension), extension.as_bytes().len()
)
format!(r#"slint::private_api::load_image_from_embedded_data({symbol}, "{}")"#, escape_string(extension))
}
crate::expression_tree::ImageReference::EmbeddedTexture{..} => todo!(),
}

View file

@ -13,6 +13,16 @@ mod htmlimage;
#[cfg(feature = "svg")]
mod svg;
#[vtable::vtable]
#[repr(C)]
pub struct OpaqueRcVTable {
drop_in_place: fn(VRefMut<OpaqueRcVTable>) -> Layout,
dealloc: fn(&OpaqueRcVTable, ptr: *mut u8, layout: Layout),
}
#[cfg(feature = "svg")]
OpaqueRcVTable_static! { pub static PARSED_SVG_VT for svg::ParsedSVG }
/// SharedPixelBuffer is a container for storing image data as pixels. It is
/// internally reference counted and cheap to clone.
///
@ -246,7 +256,7 @@ pub struct StaticTextures {
/// 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.
/// cbindgen:prefix-with-name
#[derive(Clone, PartialEq, Debug)]
#[derive(Clone, Debug)]
#[repr(u8)]
#[allow(missing_docs)]
pub enum ImageInner {
@ -256,10 +266,28 @@ pub enum ImageInner {
path: SharedString, // Should be Option, but can't be because of cbindgen, so empty means none.
buffer: SharedImageBuffer,
},
Svg(svg::ParsedSVG),
#[cfg(feature = "svg")]
Svg(vtable::VRc<OpaqueRcVTable, svg::ParsedSVG>),
StaticTextures(&'static StaticTextures),
#[cfg(target_arch = "wasm32")]
HTMLImage(HTMLImage),
HTMLImage(vtable::VRc<OpaqueRcVTable, htmlimage::HTMLImage>),
}
impl PartialEq for ImageInner {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(
Self::EmbeddedImage { path: l_path, buffer: l_buffer },
Self::EmbeddedImage { path: r_path, buffer: r_buffer },
) => l_path == r_path && l_buffer == r_buffer,
#[cfg(feature = "svg")]
(Self::Svg(l0), Self::Svg(r0)) => vtable::VRc::ptr_eq(l0, r0),
(Self::StaticTextures(l0), Self::StaticTextures(r0)) => l0 == r0,
#[cfg(target_arch = "wasm32")]
(Self::HTMLImage(l0), Self::HTMLImage(r0)) => vtable::VRc::ptr_eq(l0, r0),
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
}
}
}
impl Default for ImageInner {
@ -408,6 +436,8 @@ impl Image {
ImageInner::StaticTextures(StaticTextures { original_size, .. }) => *original_size,
#[cfg(feature = "svg")]
ImageInner::Svg(svg) => svg.size(),
#[cfg(target_arch = "wasm32")]
ImageInner::HTMLImage(htmlimage) => htmlimage.size().unwrap_or_default(),
}
}
@ -437,19 +467,22 @@ impl Image {
#[cfg(feature = "image-decoders")]
impl From<(Slice<'static, u8>, Slice<'static, u8>)> for Image {
fn from((data, format): (Slice<'static, u8>, Slice<'static, u8>)) -> Self {
load_image_from_embedded_data(data, format)
}
}
#[cfg(feature = "image-decoders")]
fn load_image_from_embedded_data(data: Slice<'static, u8>, format: Slice<'static, u8>) -> Image {
self::cache::IMAGE_CACHE.with(|global_cache| {
let image_inner = global_cache
.borrow_mut()
.load_image_from_embedded_data(data, format)
.unwrap_or_else(|| {
panic!(
"internal error: embedded image data is not supported by run-time library",
)
panic!("internal error: embedded image data is not supported by run-time library",)
});
Image(image_inner)
})
}
}
#[test]
fn test_image_size_from_buffer_without_backend() {
@ -496,6 +529,15 @@ pub(crate) mod ffi {
)
}
#[no_mangle]
pub unsafe extern "C" fn slint_image_load_from_embedded_data(
data: Slice<'static, u8>,
format: Slice<'static, u8>,
image: *mut Image,
) {
std::ptr::write(image, super::load_image_from_embedded_data(data, format));
}
#[no_mangle]
pub unsafe extern "C" fn slint_image_size(image: &Image) -> IntSize {
image.size()

View file

@ -46,7 +46,7 @@ impl ImageCache {
self.lookup_image_in_cache_or_create(cache_key, || {
if cfg!(feature = "svg") {
if path.ends_with(".svg") || path.ends_with(".svgz") {
return Some(ImageInner::Svg(
return Some(ImageInner::Svg(vtable::VRc::new(
super::svg::load_from_path(std::path::Path::new(&path.as_str()))
.map_or_else(
|err| {
@ -55,7 +55,7 @@ impl ImageCache {
},
Some,
)?,
));
)));
}
}
@ -83,7 +83,7 @@ impl ImageCache {
self.lookup_image_in_cache_or_create(cache_key, || {
#[cfg(feature = "svg")]
if format.as_slice() == b"svg" || format.as_slice() == b"svgz" {
return Some(ImageInner::Svg(
return Some(ImageInner::Svg(vtable::VRc::new(
super::svg::load_from_data(data.as_slice()).map_or_else(
|svg_err| {
eprintln!("Error loading SVG: {}", svg_err);
@ -91,7 +91,7 @@ impl ImageCache {
},
Some,
)?,
));
)));
}
let format = std::str::from_utf8(format.as_slice())

View file

@ -3,12 +3,11 @@
#![cfg(feature = "svg")]
use alloc::rc::Rc;
use super::SharedPixelBuffer;
#[derive(Clone)]
pub struct ParsedSVG(Rc<usvg::Tree>);
pub struct ParsedSVG(usvg::Tree);
impl super::OpaqueRc for ParsedSVG {}
impl core::fmt::Debug for ParsedSVG {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
@ -16,12 +15,6 @@ impl core::fmt::Debug for ParsedSVG {
}
}
impl PartialEq for ParsedSVG {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
}
impl ParsedSVG {
pub fn size(&self) -> crate::graphics::IntSize {
let size = self.0.svg_node().size.to_screen_size();
@ -35,7 +28,7 @@ impl ParsedSVG {
&self,
size: euclid::default::Size2D<u32>,
) -> Result<SharedPixelBuffer<super::Rgba8Pixel>, usvg::Error> {
let tree = &*self.0;
let tree = &self.0;
// resvg doesn't support scaling to width/height, just fit to width.
// FIXME: the fit should actually depends on the image-fit property
let fit = usvg::FitTo::Width(size.width);
@ -74,13 +67,11 @@ pub fn load_from_path(path: &std::path::Path) -> Result<ParsedSVG, std::io::Erro
with_svg_options(|options| {
usvg::Tree::from_data(&svg_data, &options)
.map(|svg| ParsedSVG(svg.into()))
.map(|svg| ParsedSVG(svg))
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
})
}
pub fn load_from_data(slice: &[u8]) -> Result<ParsedSVG, usvg::Error> {
with_svg_options(|options| {
usvg::Tree::from_data(slice, &options).map(|svg| ParsedSVG(svg.into()))
})
with_svg_options(|options| usvg::Tree::from_data(slice, &options).map(|svg| ParsedSVG(svg)))
}