Rounded Frames/Draw Rounded Rectangle

247 views
Skip to first unread message

Webb Hinton

unread,
Jul 16, 2021, 11:08:08 PM7/16/21
to fltk.general
Why not expose fl_rounded_box's rbox(int fill, int x, int y, int h) function in the draw module? Seems like a handy function to have, though I think it would be improved by adding parameter for setting the radius of the corners. 

Additionally, what are options does FLTK provide for clipping contents to a rounded frame?  I know we can clip to a rectangular area, but could we use fl_clip_region() for this as well? The later seems like overkill. 



Webb Hinton

unread,
Jul 20, 2021, 4:47:08 AM7/20/21
to fltk.general
Am I right that the only kind of non-rectangular clipping available in FLTK is FL:Window's set_shape() method?

Manolo

unread,
Jul 20, 2021, 6:05:30 AM7/20/21
to fltk.general
FLTK provides only ways to clip to a rectangle. It's also possible to clip to a region, but FLTK contains no
means to create a region. The region is expected to have been created for FLTK by the OS.

Fl_Window::shape() method controls the shape of the window itself, thus it prevents any drawing
outside the shape region.

imm

unread,
Jul 20, 2021, 6:08:38 AM7/20/21
to General FLTK
On Tue, 20 Jul 2021, 09:47 Webb Hinton wrote:
Am I right that the only kind of non-rectangular clipping available in FLTK is FL:Window's set_shape() method?


Well, I guess that somewhat depends on what you're trying to achieve (and possibly on what you mean by rectangular too!)

For clipping within a window it may be possible to composite a series of overlapping rectangles to produce more complex outlines, for example (though not exactly smoothed curves!)

Also, many surfaces will allow you to define a stencil to draw through, which will clip to an arbitrary shape.

What is it that you are trying to achieve?
-- 
Ian
From my Fairphone FP3

imm

unread,
Jul 20, 2021, 9:54:09 AM7/20/21
to General FLTK
And, just to tie up with Manolo's comment, the approaches I suggest don't really have any "direct" support via fltk API, so will need some surface-specific coding, be that X11, Win32, Quartz, GL, etc...

Webb Hinton

unread,
Jul 20, 2021, 1:15:51 PM7/20/21
to fltk.general
Yes I'm a little confused about the "Region" API. Does anyone have an example of how to use this?

Webb Hinton

unread,
Jul 27, 2021, 12:47:22 PM7/27/21
to fltk.general
Perhaps if no examples of using a Fl_Region exist and people are not using Fl_Region/don't know how to use FL_Region, it should be deprecated or removed? 

Additionality, requiring OS-specific code to generate an FL_Region seems to clash with FLTK's cross-platform nature. 

That being said, I think having a means of providing an arbitrary clipping shape in FLTK's drawing module is still highly desirable.

 I know that we can set an arbitrary shape for a Window,  would it be impossible to translate this design into a method that would work in the context of the draw module? 

Bill Spitzak

unread,
Jul 27, 2021, 1:26:55 PM7/27/21
to fltkg...@googlegroups.com
Any region clipping probably should be part of the drawing api. Fl_Region is just left over from an attempt to provide some access to X clipping, and exposes how it works far too much.
IMHO there is so little use of clipping to anything other than a rectangle that there is no need to support it. Rectangles are relatively easy, as intersecting two of them is still a rectangle.


--
You received this message because you are subscribed to the Google Groups "fltk.general" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkgeneral...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/fltkgeneral/9da638fa-4e62-43f9-bf19-9422e7f50ad1n%40googlegroups.com.

Webb Hinton

unread,
Jul 27, 2021, 2:12:47 PM7/27/21
to fltk.general

> IMHO there is so little use of clipping to anything other than a rectangle that there is no need to support it. 

While it might be a slightly more niche drawing feature, I think it's general usefulness is demonstrated by the large number of drawing apis that support such a feature. CSS allows for an "overflow: hidden" attribute, which clips to the bounds of the element. Qt has a setClipPath() and setClipRegion() method.  

However, it doesn't look like GTK or imgui support clipping to a path/non rectangular region. 

I also found this, which seems to explain some of the difficulties of clipping to a shape:

Webb Hinton

unread,
Jul 27, 2021, 4:27:25 PM7/27/21
to fltk.general
Or consider the following UI designs:

roundedavatars.PNG

Rounded images in a grid:
masonary-images.jpg

As far as I can tell there's no way to clip to rounded edges in FLTK in a way that would (with reasonable effort) create such effects. 

I'm sure you could find some way to edit the pixel data of the image themselves to get these rounded edges, but that sounds very complicated...

It doesn't seem like there's much appetite for rounded edges here, but I think saying that the use cases are too far and few between ignores the fact that rounded frames are both useful and popular tool for designing GUIs. Just look at the design of Google Groups (or any google product), you'll find a plethora of rounded edges. 

On Tuesday, July 27, 2021 at 1:26:55 PM UTC-4 spitzak wrote:

Ian MacArthur

unread,
Jul 27, 2021, 4:46:14 PM7/27/21
to Fltk General
On 27 Jul 2021, at 21:27, Webb Hinton wrote:
>
> Or consider the following UI designs:
>
> Rounded avatar pictures:
> <roundedavatars.PNG>
>
> Rounded images in a grid:
> <masonary-images.jpg>
>
> As far as I can tell there's no way to clip to rounded edges in FLTK in a way that would (with reasonable effort) create such effects.
>
> I'm sure you could find some way to edit the pixel data of the image themselves to get these rounded edges, but that sounds very complicated...
>
> It doesn't seem like there's much appetite for rounded edges here, but I think saying that the use cases are too far and few between ignores the fact that rounded frames are both useful and popular tool for designing GUIs. Just look at the design of Google Groups (or any google product), you'll find a plethora of rounded edges.

It is, I think, very much a “horses for courses” thing, though.
Clipping to non-rectilinear bounds is potentially complex in a lot of cases, so does not represent a good fit for the fltk “fast and light” ethos.
It may be that, if it is a style you need, then some heavier-weight toolkit might be a better fit for that task.

You can of course create those effects in fltk, but there are no “default” widgets that will do that.
Typically, you’d do this by rendering to a (rectangular) offscreen surface, then mask that with a “barn door” (usually only two states, transparent and opaque) alpha channel mask to show the appropriate final shape. You need to derive your own widget for this though, from Fl_Box say, as it is not an “out of the box” feature.


Greg Ercolano

unread,
Jul 27, 2021, 4:54:19 PM7/27/21
to fltkg...@googlegroups.com

On 7/27/21 1:27 PM, Webb Hinton wrote:

Or consider the following UI designs: [images with rounded corners]


    Our nanoSVG support should allow this sort of thing easily I'd think,
    utilizing its alpha support, anti-aliased rendering, and ability to draw
    graphics at runtime.

    Consider this clock example which draws the hand shapes in realtime
    over the clock face image:
    http://seriss.com/people/erco/fltk/#FLTK-Simplex-Clock

    One should also be able to render a mask with curved corners and apply it
    over the images similar to the clock I would think. Then you can make that
    a custom widget to display images of arbitrary size with a curved border.

Albrecht Schlosser

unread,
Jul 27, 2021, 9:02:30 PM7/27/21
to fltkg...@googlegroups.com
On 7/27/21 10:46 PM Ian MacArthur wrote:
> On 27 Jul 2021, at 21:27, Webb Hinton wrote:
>> Or consider the following UI designs:
>>
>> Rounded avatar pictures:
>> <roundedavatars.PNG>
>>
>> Rounded images in a grid:
>> <masonary-images.jpg>
>>
>> As far as I can tell there's no way to clip to rounded edges in FLTK in a way that would (with reasonable effort) create such effects.
>>
>> I'm sure you could find some way to edit the pixel data of the image themselves to get these rounded edges, but that sounds very complicated...
>
> You can of course create those effects in fltk, but there are no “default” widgets that will do that.
> Typically, you’d do this by rendering to a (rectangular) offscreen surface, then mask that with a “barn door” (usually only two states, transparent and opaque) alpha channel mask to show the appropriate final shape. You need to derive your own widget for this though, from Fl_Box say, as it is not an “out of the box” feature.

You could also draw the image first in a rectangular shape and the
rounded corners (in the background color) "over" the image.

Greg Ercolano

unread,
Jul 27, 2021, 9:08:32 PM7/27/21
to fltkg...@googlegroups.com

On 7/27/21 6:02 PM, Albrecht Schlosser wrote:

On 7/27/21 10:46 PM Ian MacArthur wrote:
On 27 Jul 2021, at 21:27, Webb Hinton wrote:
Or consider the following UI designs:

Rounded avatar pictures:
<roundedavatars.PNG>

Rounded images in a grid:
<masonary-images.jpg>
You could also draw the image first in a rectangular shape and the rounded corners (in the background color) "over" the image.

Yep, that's actually what I just ended up implementing as a widget called RoundedImageDisplay.
Added it to my cheat page just now:
http://seriss.com/people/erco/fltk/#RoundedCorners

The result is antialiased corners, and in the demo you can interactively change the corner radius
with a slider, the result being:


Webb Hinton

unread,
Jul 27, 2021, 9:37:47 PM7/27/21
to fltk.general
Incredible! Erco to the rescue yet again. Thanks!

Mo_Al_

unread,
Jul 28, 2021, 12:05:55 AM7/28/21
to fltk.general
A translation into Rust:
```
use fltk::{enums::*, prelude::*, *};
use std::cell::RefCell;
use std::ops::{Deref, DerefMut};
use std::rc::Rc;

struct RoundedImageDisplay {
    frame_: frame::Frame,
    // wrap the following in an Rc RefCell since we need to mutate them after passing into callbacks
    bordercolor_: Rc<RefCell<[u8; 3]>>,
    radius_: Rc<RefCell<i32>>,
}

impl RoundedImageDisplay {
    pub fn new(x: i32, y: i32, w: i32, h: i32, title: Option<&'static str>) -> Self {
        let mut frame_ = frame::Frame::new(xywhtitle);
        let radius_ = 20;
        let bordercolor_ = [0x80, 0x80, 0x80];
        frame_.set_frame(FrameType::BorderBox);
        let radius_ = Rc::from(RefCell::from(radius_));
        let bordercolor_ = Rc::from(RefCell::from(bordercolor_));
        frame_.draw({
            let radius_ = radius_.clone();
            let bordercolor_ = bordercolor_.clone();
            move |f| {
                let radius_ = radius_.borrow();
                let bordercolor_ = bordercolor_.borrow();
                let svg = format!(
                    "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n
                    <svg width='{}' height='{}'>\n
                    <rect x='{}' y='{}' rx='{}' ry='{}' width='{}' height='{}' 
                    fill='none' stroke='rgb({},{},{})' stroke-width='{}'/>\n
                    </svg>\n",
                    f.w(),
                    f.h(),
                    (0 - *radius_ / 2),
                    (0 - *radius_ / 2),
                    *radius_,
                    *radius_,
                    (f.w() + *radius_),
                    (f.h() + *radius_),
                    bordercolor_[0],
                    bordercolor_[1],
                    bordercolor_[2],
                    *radius_
                );
                let mut svg = image::SvgImage::from_data(&svg).unwrap();
                svg.draw(f.x(), f.y(), f.w(), f.h())
            }
        });
        Self {
            frame_,
            radius_,
            bordercolor_,
        }
    }

    pub fn bordercolor(&mut selfr: u8, g: u8, b: u8) {
        let mut bordercolor = self.bordercolor_.borrow_mut();
        bordercolor[0] = r;
        bordercolor[1] = g;
        bordercolor[2] = b;
        self.frame_.parent().unwrap().redraw();
    }

    pub fn radius(&mut selfval: i32) {
        *self.radius_.borrow_mut() = val;
        self.frame_.parent().unwrap().redraw();
    }
}

// impl Deref and DerefMut to emulate inheritance of RoundedImageDisplay from Frame (Fl_Box)
impl Deref for RoundedImageDisplay {
    type Target = frame::Frame;

    fn deref(&self) -> &Self::Target {
        &self.frame_
    }
}

impl DerefMut for RoundedImageDisplay {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.frame_
    }
}

fn main() {
    let a = app::App::default().with_scheme(app::Scheme::Gtk);

    let border = [0x80, 0xa0, 0x80]; // gray green
    let mut win = window::Window::default()
        .with_size(1000, 800)
        .with_label("Rounded Corners");

    win.set_color(Color::from_rgb(border[0], border[1], border[2]));

    let jpg = image::JpegImage::load("screenshots/calc.jpg").expect("Failed to open jpg file");

    let mut rimage = RoundedImageDisplay::new(10, 10, jpg.w(), jpg.h(), None);
    rimage.bordercolor(border[0], border[1], border[2]);
    rimage.radius(50);
    rimage.set_image(Some(jpg));

    let mut slider = valuator::Slider::new(1000 - 50, 10, 20, 200, "border\nradius");
    slider.set_align(Align::Bottom);
    slider.set_bounds(0., 200.);
    slider.set_value(20.);
    slider.do_callback();
    slider.set_color(Color::from_rgb(
        (border[0] as f64 / 1.5) as u8,
        (border[1] as f64 / 1.5) as u8,
        (border[2] as f64 / 1.5) as u8,
    ));
    slider.set_callback(move |s| {
        rimage.radius(s.value() as i32);
    });

    win.end();
    win.show();
    a.run().unwrap();
}

```

duncan

unread,
Jul 28, 2021, 4:11:47 AM7/28/21
to fltk.general
Yep, that's actually what I just ended up implementing as a widget called RoundedImageDisplay.
Added it to my cheat page just now:
http://seriss.com/people/erco/fltk/#RoundedCorners

Just curious why you used an FL_BORDER_BOX and not an FL_FLAT_BOX because
you don't actually use the border in the end. Or was it to have a definite artefact on
screen so that you knew when you had the size and location correct?

Greg Ercolano

unread,
Jul 28, 2021, 9:25:22 AM7/28/21
to fltkg...@googlegroups.com
    Ah, that's left over from debugging/development so I could see the edge.
    I'll tweak the example, thanks for reminding me.
Reply all
Reply to author
Forward
0 new messages