GL backend: Avoid run-time opengl errors with clipped zero width or height rectangles

If a Rectangle has a border-radius and clipping, we use an FBO to render
the children and then use femtovg's stencil clipping. If the Rectangle
has a zero width or height, we would end up trying to create a texture
with such dimensions, which produces run-time opengl errors.

We can detect this situation and avoid it early on. The same might happen for shadows.

Fixes #377
This commit is contained in:
Simon Hausmann 2021-08-03 11:12:06 +02:00 committed by Simon Hausmann
parent 603c5df47a
commit 25fac2bcd5
2 changed files with 20 additions and 8 deletions

View file

@ -139,7 +139,10 @@ impl CachedImage {
Self(RefCell::new(Texture { id: image_id, canvas: canvas.clone() }.into()))
}
pub fn new_empty_on_gpu(canvas: &CanvasRc, width: usize, height: usize) -> Self {
pub fn new_empty_on_gpu(canvas: &CanvasRc, width: usize, height: usize) -> Option<Self> {
if width == 0 || height == 0 {
return None;
}
let image_id = canvas
.borrow_mut()
.create_image_empty(
@ -149,7 +152,7 @@ impl CachedImage {
femtovg::ImageFlags::PREMULTIPLIED | femtovg::ImageFlags::FLIP_Y,
)
.unwrap();
Self::new_on_gpu(canvas, image_id)
Self::new_on_gpu(canvas, image_id).into()
}
#[cfg(feature = "svg")]
@ -352,6 +355,9 @@ impl CachedImage {
&canvas,
size.width.ceil() as usize,
size.height.ceil() as usize,
)
.expect(
"internal error: this can only fail if the filtered image was zero width or height",
);
let filtered_image_id = match &*filtered_image.0.borrow() {

View file

@ -934,7 +934,7 @@ impl ItemRenderer for GLItemRenderer {
return;
}
let cache_entry = self
let cache_entry = match self
.graphics_window
.graphics_cache
.borrow_mut()
@ -961,7 +961,7 @@ impl ItemRenderer for GLItemRenderer {
&self.shared_data.canvas,
shadow_image_width,
shadow_image_height,
);
)?;
{
let mut canvas = self.shared_data.canvas.borrow_mut();
@ -1033,8 +1033,10 @@ impl ItemRenderer for GLItemRenderer {
Rc::new(shadow_image)
})
.into()
})
.expect("internal error: creation of the cached shadow image must always succeed");
}) {
Some(cached_shadow_image) => cached_shadow_image,
None => return, // Zero width or height shadow
};
let shadow_image = cache_entry.as_image();
@ -1514,11 +1516,15 @@ impl GLItemRenderer {
let layer_width = path_bounds.maxx - path_bounds.minx;
let layer_height = path_bounds.maxy - path_bounds.miny;
let clip_buffer_img = CachedImage::new_empty_on_gpu(
let clip_buffer_img = match CachedImage::new_empty_on_gpu(
&self.shared_data.canvas,
layer_width as _,
layer_height as _,
);
) {
Some(clip_buffer) => clip_buffer,
None => return, // Zero width or height clip path
};
{
let mut canvas = self.shared_data.canvas.borrow_mut();