Skip to content

Commit

Permalink
Don't apply the layered opacity of the target item has no children
Browse files Browse the repository at this point in the history
  • Loading branch information
tronical committed Mar 18, 2022
1 parent f667c8c commit beaf5e4
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 57 deletions.
65 changes: 41 additions & 24 deletions internal/backends/gl/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,31 +684,16 @@ impl ItemRenderer for GLItemRenderer {
}

fn visit_opacity(&mut self, opacity_item: Pin<&Opacity>, self_rc: &ItemRc) -> RenderingResult {
let current_clip = self.get_current_clip();
if let Some(layer_image) =
self.render_layer(&opacity_item.cached_rendering_data, self_rc, &|| {
// We don't need to include the size of the opacity item itself, since it has no content.
let children_rect = i_slint_core::properties::evaluate_no_tracking(|| {
opacity_item.as_ref().geometry().union(
&i_slint_core::item_rendering::item_children_bounding_rect(
&self_rc.component(),
self_rc.index() as isize,
&current_clip,
),
)
});
children_rect.size
})
{
let layer_image_paint = layer_image.as_paint_with_alpha(opacity_item.opacity());

let mut layer_path = femtovg::Path::new();
if let Some(layer_size) = layer_image.size() {
layer_path.rect(0., 0., layer_size.width as _, layer_size.height as _);
self.canvas.borrow_mut().fill_path(&mut layer_path, layer_image_paint);
}
if Opacity::opacity_needs_layer(self_rc) {
self.render_and_blend_layer(
&opacity_item.cached_rendering_data,
opacity_item.opacity(),
self_rc,
)
} else {
self.apply_opacity(opacity_item.opacity());
RenderingResult::ContinueRenderingChildren
}
RenderingResult::ContinueRenderingWithoutChildren
}

fn visit_clip(&mut self, clip_item: Pin<&Clip>, self_rc: &ItemRc) -> RenderingResult {
Expand Down Expand Up @@ -959,6 +944,38 @@ impl GLItemRenderer {
cache_entry.map(|item_cache_entry| item_cache_entry.as_image().clone())
}

fn render_and_blend_layer(
&mut self,
item_cache: &CachedRenderingData,
alpha_tint: f32,
self_rc: &ItemRc,
) -> RenderingResult {
let current_clip = self.get_current_clip();
if let Some(layer_image) = self.render_layer(&item_cache, &self_rc.clone(), &|| {
// We don't need to include the size of the opacity item itself, since it has no content.
let children_rect = i_slint_core::properties::evaluate_no_tracking(|| {
let self_ref = self_rc.borrow();
self_ref.as_ref().geometry().union(
&i_slint_core::item_rendering::item_children_bounding_rect(
&self_rc.component(),
self_rc.index() as isize,
&current_clip,
),
)
});
children_rect.size
}) {
let layer_image_paint = layer_image.as_paint_with_alpha(alpha_tint);

let mut layer_path = femtovg::Path::new();
if let Some(layer_size) = layer_image.size() {
layer_path.rect(0., 0., layer_size.width as _, layer_size.height as _);
self.canvas.borrow_mut().fill_path(&mut layer_path, layer_image_paint);
}
}
RenderingResult::ContinueRenderingWithoutChildren
}

fn colorize_image(
&self,
original_cache_entry: ItemGraphicsCacheEntry,
Expand Down
83 changes: 50 additions & 33 deletions internal/backends/qt/qt_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use i_slint_core::graphics::{
use i_slint_core::input::{KeyEvent, KeyEventType, MouseEvent};
use i_slint_core::item_rendering::{CachedRenderingData, ItemRenderer};
use i_slint_core::items::{
self, FillRule, ImageRendering, InputType, Item, ItemRc, ItemRef, MouseCursor, Opacity,
self, FillRule, ImageRendering, InputType, ItemRc, ItemRef, MouseCursor, Opacity,
PointerEventButton, RenderingResult, TextOverflow, TextWrap,
};
use i_slint_core::layout::Orientation;
Expand Down Expand Up @@ -777,40 +777,16 @@ impl ItemRenderer for QtItemRenderer<'_> {
}

fn visit_opacity(&mut self, opacity_item: Pin<&Opacity>, self_rc: &ItemRc) -> RenderingResult {
let current_clip = self.get_current_clip();
if let Some(mut layer_image) =
self.render_layer(&opacity_item.cached_rendering_data, self_rc, &|| {
// We don't need to include the size of the opacity item itself, since it has no content.
let children_rect = i_slint_core::properties::evaluate_no_tracking(|| {
opacity_item.as_ref().geometry().union(
&i_slint_core::item_rendering::item_children_bounding_rect(
&self_rc.component(),
self_rc.index() as isize,
&current_clip,
),
)
});
qttypes::QSize {
width: children_rect.size.width as _,
height: children_rect.size.height as _,
}
})
{
self.save_state();
if Opacity::opacity_needs_layer(self_rc) {
self.render_and_blend_layer(
&opacity_item.cached_rendering_data,
opacity_item.opacity(),
self_rc,
)
} else {
self.apply_opacity(opacity_item.opacity());
{
let painter: &mut QPainter = &mut *self.painter;
let layer_image_ref: &mut qttypes::QPixmap = &mut layer_image;
cpp! { unsafe [
painter as "QPainter*",
layer_image_ref as "QPixmap*"
] {
painter->drawPixmap(0, 0, *layer_image_ref);
}}
}
self.restore_state();
RenderingResult::ContinueRenderingChildren
}
RenderingResult::ContinueRenderingWithoutChildren
}

fn combine_clip(&mut self, rect: Rect, radius: f32, mut border_width: f32) {
Expand Down Expand Up @@ -1197,6 +1173,47 @@ impl QtItemRenderer<'_> {
_ => None,
}
}

fn render_and_blend_layer(
&mut self,
item_cache: &CachedRenderingData,
alpha_tint: f32,
self_rc: &ItemRc,
) -> RenderingResult {
let current_clip = self.get_current_clip();
if let Some(mut layer_image) = self.render_layer(&item_cache, self_rc, &|| {
// We don't need to include the size of the opacity item itself, since it has no content.
let children_rect = i_slint_core::properties::evaluate_no_tracking(|| {
let self_ref = self_rc.borrow();
self_ref.as_ref().geometry().union(
&i_slint_core::item_rendering::item_children_bounding_rect(
&self_rc.component(),
self_rc.index() as isize,
&current_clip,
),
)
});
qttypes::QSize {
width: children_rect.size.width as _,
height: children_rect.size.height as _,
}
}) {
self.save_state();
self.apply_opacity(alpha_tint);
{
let painter: &mut QPainter = &mut *self.painter;
let layer_image_ref: &mut qttypes::QPixmap = &mut layer_image;
cpp! { unsafe [
painter as "QPainter*",
layer_image_ref as "QPixmap*"
] {
painter->drawPixmap(0, 0, *layer_image_ref);
}}
}
self.restore_state();
}
RenderingResult::ContinueRenderingWithoutChildren
}
}

cpp_class!(unsafe struct QWidgetPtr as "std::unique_ptr<QWidget>");
Expand Down
45 changes: 45 additions & 0 deletions internal/core/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,18 @@ impl ItemRc {
pub fn component(&self) -> vtable::VRc<ComponentVTable> {
self.component.clone()
}

/// Returns the number of child items for this item. Returns None if
/// the number is dynamically determined.
/// TODO: Remove the option when the Subtree trait exists and allows querying
pub fn children_count(&self) -> Option<u32> {
let comp_ref_pin = vtable::VRc::borrow_pin(&self.component);
let item_tree = comp_ref_pin.as_ref().get_item_tree();
match item_tree.as_slice()[self.index] {
crate::item_tree::ItemTreeNode::Item { children_count, .. } => Some(children_count),
crate::item_tree::ItemTreeNode::DynamicTree { .. } => None,
}
}
}

/// A Weak reference to an item that can be constructed from an ItemRc.
Expand Down Expand Up @@ -837,6 +849,39 @@ impl Item for Opacity {
}
}

impl Opacity {
// The opacity item has only one child, as per the compiler's output. However if that item has
// no children, then we can skip the layer and apply the opacity directly. This is not perfect though,
// for example if the compiler inserts another synthetic element between the `Opacity` and the actual child,
// then this check will apply a layer even though it might not actually be necessary.
pub fn opacity_needs_layer(self_rc: &ItemRc) -> bool {
let target_has_children = {
let component_rc = self_rc.component();
let component_ref = vtable::VRc::borrow_pin(&component_rc);
let self_index = self_rc.index();
// TODO: use first_child() once it exists
let item_tree = component_ref.as_ref().get_item_tree();
let target_item_index = match item_tree.as_slice()[self_index] {
crate::item_tree::ItemTreeNode::Item {
children_count: _children_count,
children_index,
..
} => {
debug_assert_eq!(_children_count, 1);
children_index as usize
}
crate::item_tree::ItemTreeNode::DynamicTree { .. } => panic!(
"internal error: compiler placed Opacity element as a parent for a repeater!"
),
};
let target_item = ItemRc::new(component_rc.clone(), target_item_index);
target_item.children_count() != Some(0)
};

target_has_children
}
}

impl ItemConsts for Opacity {
const cached_rendering_data_offset: const_field_offset::FieldOffset<
Opacity,
Expand Down

0 comments on commit beaf5e4

Please sign in to comment.