Can custom box type functions handle their own high-DPI screen scaling?

94 views
Skip to first unread message

Remy Oukaour

unread,
Feb 23, 2021, 10:54:14 AM2/23/21
to fltk.general
Some months ago I was testing FLTK 1.4.x's screen scaling support in Windows. It worked as expected, with FLTK_SCALING_FACTOR or Fl::screen_scale() affecting the whole program's scaling, without my needing to manually adjust coordinate and size values.

I didn't continue with it because it was scaling lines in an ugly way, particularly at uneven scaling factors like 1.5. What I wanted was something like this: https://i.imgur.com/SEpGcLv.png where the location and size of a widget scale appropriately, but its box features (border, background, etc) stay the same. However, what FLTK does is to scale the lines up too, which can mess up things like the alignment of an outer and inner border, or a gradient that's a series of 1-pixel lines.

Now that FLTK 1.3.6 may be released soon, I hope to use it if DPI scaling can work well. This especially matters to me because I use a lot of custom themes: https://i.imgur.com/ygCIDa6.png

Is there a way for custom boxtype functions to "opt out" of scaling, but without breaking their X/Y/W/H values? If necessary I could manually multiply those by the screen_scale.

Here are some examples of the sort of boxtype functions I need to adapt:

static void aero_button_up_frame(int x, int y, int w, int h, Fl_Color) {
// outer border
fl_color(activated_color(fl_rgb_color(0x70, 0x70, 0x70)));
fl_xyline(x+2, y, x+w-3);
fl_xyline(x+2, y+h-1, x+w-3);
fl_yxline(x, y+2, y+h-3);
fl_yxline(x+w-1, y+2, y+h-3);
// top inner borders
fl_color(activated_color(fl_rgb_color(0xFB, 0xFB, 0xFB)));
fl_xyline(x+2, y+1, x+w-3);
fl_yxline(x+1, y+2, y+h/2-1);
fl_yxline(x+w-2, y+2, y+h/2-1);
fl_point(x+2, y+2);
fl_point(x+w-3, y+2);
// bottom inner borders
fl_color(activated_color(fl_rgb_color(0xF2, 0xF2, 0xF2)));
fl_yxline(x+1, y+h/2, y+h-3);
fl_yxline(x+w-2, y+h/2, y+h-3);
fl_xyline(x+2, y+h-2, x+w-3);
fl_point(x+2, y+h-3);
fl_point(x+w-3, y+h-3);
// top corners
fl_color(activated_color(fl_rgb_color(0x90, 0x90, 0x90)));
fl_xyline(x, y+1, x+1, y);
fl_yxline(x+w-2, y, y+1, x+w-1);
// bottom corners
fl_color(activated_color(fl_rgb_color(0x88, 0x88, 0x88)));
fl_xyline(x, y+h-2, x+1, y+h-1);
fl_yxline(x+w-2, y+h-1, y+h-2, x+w-1);
}

static void metro_button_up_box(int x, int y, int w, int h, Fl_Color c) {
if (w >= h) {
vertical_gradient(x+1, y+1, x+w-2, y+h-2, activated_color(fl_rgb_color(0xF0, 0xF0, 0xF0)),
activated_color(fl_rgb_color(0xE5, 0xE5, 0xE5)));
}
else {
horizontal_gradient(x+1, y+1, x+w-2, y+h-2, activated_color(fl_rgb_color(0xF0, 0xF0, 0xF0)),
activated_color(fl_rgb_color(0xE5, 0xE5, 0xE5)));
}
metro_button_up_frame(x, y, w, h, c);
}

static void horizontal_gradient(int x1, int y1, int x2, int y2, Fl_Color c1, Fl_Color c2) {
int imax = x2 - x1;
int d = imax ? imax : 1;
if (Fl::draw_box_active()) {
for (int i = 0; i <= imax; i++) {
float w = 1.0f - (float)i / d;
fl_color(fl_color_average(c1, c2, w));
fl_yxline(x1+i, y1, y2);
}
}
else {
for (int i = 0; i <= imax; i++) {
float w = 1.0f - (float)i / d;
fl_color(fl_inactive(fl_color_average(c1, c2, w)));
fl_yxline(x1+i, y1, y2);
}
}
}

Manolo

unread,
Feb 24, 2021, 1:24:55 AM2/24/21
to fltk.general
On Tuesday, February 23, 2021 at 4:54:14 PM UTC+1 remy.o...@gmail.com wrote:
Some months ago I was testing FLTK 1.4.x's screen scaling support in Windows. It worked as expected, with FLTK_SCALING_FACTOR or Fl::screen_scale() affecting the whole program's scaling, without my needing to manually adjust coordinate and size values.

I didn't continue with it because it was scaling lines in an ugly way, particularly at uneven scaling factors like 1.5. …
 
Hi Remy,
I would suggest you try again with FLTK 1.4 in its present state. The code has been much improved and
supports now all scaling factor values for color gradients. I attach an example given by the "unittests"
FLTK test program scaled at 170% on Windows. The situation is the same under Linux and macOS.

unittests@170%.png

Manolo

unread,
Feb 24, 2021, 1:27:58 AM2/24/21
to fltk.general
I add that FLTK 1.3.6 won't support DPI scaling which is present starting at 1.4

Remy Oukaour

unread,
Feb 24, 2021, 9:00:18 AM2/24/21
to fltk.general
Hi Manolo,

Thank you, but this shows the problem I was referring to. The green button's top and right borders are 4px thick, while its left and bottom borders are 2px; the blue and orange buttons have uneven borders inconsistent with orange; and so on.

Ideally for me, at 200% DPI a boxtype function could disable having its own draw calls scaled, and instead multiply its own X/Y/W/H arguments by screen_scale(), so it would be like drawing a box twice as large and with a doubled offset from (0,0). (Then it would decide whether to, for instance, draw some of its elements as thicker or not.)

Manolo

unread,
Feb 24, 2021, 9:28:23 AM2/24/21
to fltk.general
On Wednesday, February 24, 2021 at 3:00:18 PM UTC+1 remy.o...@gmail.com wrote:
Hi Manolo,

Thank you, but this shows the problem I was referring to. The green button's top and right borders are 4px thick, while its left and bottom borders are 2px; the blue and orange buttons have uneven borders inconsistent with orange; and so on.

Ideally for me, at 200% DPI a boxtype function could disable having its own draw calls scaled, and instead multiply its own X/Y/W/H arguments by screen_scale(), so it would be like drawing a box twice as large and with a doubled offset from (0,0). (Then it would decide whether to, for instance, draw some of its elements as thicker or not.)

When the scaling factor value is fractional, horizontal and vertical lines have varying width, precisely to make
sure adjacent boxes remain adjacent. But at 200% (and 300%) all lines have the same width as in the attached png.
I may add a new line style that would prevent lines from getting variable width with fractional scale factors,
then your custom boxes would just have to use that line style. But it's not possible, I believe, to simultaneously have
fixed-width lines, adjacent boxes or lines, and fractional scale factors.
unittests@200%.png

Albrecht Schlosser

unread,
Feb 24, 2021, 9:49:52 AM2/24/21
to fltkg...@googlegroups.com
What I think Remy wants is to be able to switch to a "non-scaling"
driver in his own draw() method, just like you could switch to an image
surface, draw to the surface at a higher resolution (w*scale, h*scale),
save the image, call image->scale(w,h) and then draw the image with
image->draw(). Or something like that, maybe. This would presumably work
for all scaling factors, not only integer ones.

@Remy: Maybe I missed some details but I think this would be possible
with the current Fl_Image_Surface stuff. It's a little effort but if
there's no better option...

@Manolo: Wouldn't it be great to be able to switch from the default
"scaling" drivers to "non-scaling" drivers if the user wants to do that
(as I think Remy would like to do)? As you can do with image surfaces etc.

This is just a thought, I don't know the scaling internals well enough,
but maybe there's a way ...

imm

unread,
Feb 24, 2021, 9:56:37 AM2/24/21
to general fltk
Can Remy not just use FL:: screen_scale() to just force fltk to treat each display as having unity scaling?

Or am I missing the point?

--
Ian
From my Fairphone FP3
   

Manolo

unread,
Feb 24, 2021, 12:59:18 PM2/24/21
to fltk.general
The possibility to draw using an unscaled context is already present in FLTK 1.4.
The key is to call Fl_Graphics_Driver::scale(float s) to change the scaling factor
and set it to 1. I attach a small example program that redraws a button label
using an unscaled setting. The result is that lines have a constant width, set to 1, 2 and 3
with growing scale factor values.
button.cxx

Ian MacArthur

unread,
Feb 24, 2021, 2:38:48 PM2/24/21
to fltkg...@googlegroups.com
Neat, didn’t know that worked.

Can you change the scale factor during a draw() method? I wonder if you could use this technique to, for example, have the text scale “normally” with the DPI, but force lines always to be scale 1.0, or etc...?



Manolo

unread,
Feb 24, 2021, 3:29:43 PM2/24/21
to fltk.general
Please, look at my example program button.cxx that does exactly this.

Albrecht Schlosser

unread,
Feb 24, 2021, 5:17:23 PM2/24/21
to fltkg...@googlegroups.com
On 2/24/21 6:59 PM Manolo wrote:
> The possibility to draw using an unscaled context is already present in
> FLTK 1.4.
> The key is to call Fl_Graphics_Driver::scale(float s) to change the
> scaling factor and set it to 1. I attach a small
> example program that redraws a button label
> using an unscaled setting. ...

Great, I think this is exactly what Remy wanted. I modified the test
program a bit drawing three rectangles:

- yellow (line width 1)
- green (line width 1)
- red (line width == scaling factor)

The attached screenshot has been taken with scaling 300% and you can see
the button borders scaled to the same width (3) as the red line and you
can also see the antialiased label. This is from Linux, BTW, and the
image has been zoomed for better visibility.

Since you can change the scaling factor while drawing inside the widget
or box(type) drawing code I believe Remy can do whatever he wants in his
own box drawing code, for instance drawing nice borders and gradients in
full (1-pixel) resolution.
draw_unscaled.png

Ian MacArthur

unread,
Feb 24, 2021, 6:22:06 PM2/24/21
to fltkg...@googlegroups.com
Yeah, sorry Manolo, I didn’t ask the question well.
What I mean is whether we can draw the text at labelsize() (when “s" is not unity) and then once the text has drawn set “s" to 1 to draw the lines.

What the example does is set “s” to 1, then draw the text at (lablesize() * s) with the lines, but all with unit “s” - which has exactly the same effect I imagine, unless for some weird reason (lablesize() * s) is not the same as labelsize() at scale “s”...


Manolo

unread,
Feb 25, 2021, 4:55:14 AM2/25/21
to fltk.general
On Thursday, February 25, 2021 at 12:22:06 AM UTC+1 Ian MacArthur wrote:
On 24 Feb 2021, at 20:29, Manolo wrote:
>
> On Wednesday, February 24, 2021 at 8:38:48 PM UTC+1 Ian MacArthur wrote:
>
> Neat, didn’t know that worked.
>
> Can you change the scale factor during a draw() method? I wonder if you could use this technique to, for example, have the text scale “normally” with the DPI, but force lines always to be scale 1.0, or etc...?
>
> Please, look at my example program button.cxx that does exactly this.


Yeah, sorry Manolo, I didn’t ask the question well.
What I mean is whether we can draw the text at labelsize() (when “s" is not unity) and then once the text has drawn set “s" to 1 to draw the lines.
You could remove and restore the scaling factor several times in the draw() method and thus
draw some parts with and other without scaling.


What the example does is set “s” to 1, then draw the text at (lablesize() * s) with the lines, but all with unit “s” - which has exactly the same effect I imagine, unless for some weird reason (lablesize() * s) is not the same as labelsize() at scale “s”...
Text drawing in presence of a scaling factor s is nothing but multiplying the fontsize by s and drawing text with it.
Thus, both approaches give exactly the same text, antialiasing included.

Manolo

unread,
Feb 25, 2021, 5:16:33 AM2/25/21
to fltk.general
There's now a new public API in FLTK 1.4 to support "drawing without scaling factor",
in the form of a pair of functions that remove and then restore the scaling factor:
   float fl_remove_scale();
  void fl_restore_scale(float s);

I attach the same example program modified to use this API.
button.cxx

Albrecht Schlosser

unread,
Feb 25, 2021, 7:57:34 AM2/25/21
to fltkg...@googlegroups.com
On 2/25/21 11:16 AM Manolo wrote:
> There's now a new public API in FLTK 1.4 to support "drawing without
> scaling factor",
> in the form of a pair of functions that remove and then restore the
> scaling factor:
>    float fl_remove_scale();
>   void fl_restore_scale(float s);

Thanks, that's great. The "naive" implementation to reset the scale
didn't work well on my macOS system (I think you know why) but with the
new API it works fine.

Almost. See below.

May I request to change fl_remove_scale() to fl_reset_scale() ? "Remove"
sounds strange in this context.

> I attach the same example program modified to use this API.

Okay, works fine on Linux (not tested elsewhere). Your program doesn't
show any issues in my tests.

My own modified test program that demonstrates switching the scaling
factor back and forth after drawing the box and before drawing the label
fails in some situations. I attach my demo 'draw_unscaled.cxx' here. I
also attach 'draw_unscaled.png' which shows a failure situation.

If there's anything wrong with my test program, please tell me.
Otherwise this seems to be a bug.

What I found out with my demo: it's somehow interfering with the
transient scaling factor display window when the scaling is changed
(ctrl/+ or ctrl/-). I see very similar effects on macOS with cmd/+ or
cmd/- but I didn't test on Windows.

Note: I used fluid/Edit/Global FLTK Settings... to enable/disable the
scaling factor display. You need to restart the demo program for the
change to take effect.

There are different effects depending on the appearance of the transient
scaling factor display and which button has the focus:

(1) Transient scaling factor display OFF: works fine.

(2) Scaling factor display on and focus on "LABEL" button: fails almost
always.

(3) scaling factor display on and focus on "no op" button: fails
sumetimes but not always.

What I see is that there are two draw operations: one before the zoom
factor window is shown and one after it was removed (or at least it
appears so).

If I resize the window or click on another button and back on the LABEL
button the redraw works fine.

Can anybody else confirm this?
draw_unscaled.cxx
draw_unscaled.png

Manolo

unread,
Feb 25, 2021, 9:54:19 AM2/25/21
to fltk.general
On Thursday, February 25, 2021 at 1:57:34 PM UTC+1 Albrecht Schlosser wrote:
On 2/25/21 11:16 AM Manolo wrote:
> There's now a new public API in FLTK 1.4 to support "drawing without
> scaling factor",
> in the form of a pair of functions that remove and then restore the
> scaling factor:
>    float fl_remove_scale();
>   void fl_restore_scale(float s);

Thanks, that's great. The "naive" implementation to reset the scale
didn't work well on my macOS system (I think you know why) but with the
new API it works fine.

Almost. See below.

May I request to change fl_remove_scale() to fl_reset_scale() ? "Remove"
sounds strange in this context.

> I attach the same example program modified to use this API.

Okay, works fine on Linux (not tested elsewhere). Your program doesn't
show any issues in my tests.

My own modified test program that demonstrates switching the scaling
factor back and forth after drawing the box and before drawing the label
fails in some situations. I attach my demo 'draw_unscaled.cxx' here. I
also attach 'draw_unscaled.png' which shows a failure situation.

If there's anything wrong with my test program, please tell me.
Yes. There is. That program assumes the font face and size are correctly set
when fl_text_extents() is called. But that's not the case at the very beginning
of the draw() method.

If you add
    fl_font(labelfont(), labelsize());
just before
    fl_text_extents(label(), dx, dy, ll, lh);
all runs smoothly.

My simpler version added custom code after Fl_Button::draw() which
does set the font face and size adequately.

Otherwise this seems to be a bug.

What I found out with my demo: it's somehow interfering with the
transient scaling factor display window when the scaling is changed
(ctrl/+ or ctrl/-). I see very similar effects on macOS with cmd/+ or
cmd/- but I didn't test on Windows.
The transient window display lets the font size to large values which produce
erroneously large colored boxes later.

Manolo

unread,
Feb 25, 2021, 10:01:00 AM2/25/21
to fltk.general
On Thursday, February 25, 2021 at 1:57:34 PM UTC+1 Albrecht Schlosser wrote:

May I request to change fl_remove_scale() to fl_reset_scale() ? "Remove"
sounds strange in this context.

I'm  satisfied by neither proposal.

What about
fl_undo_scale() / fl_redo_scale()
or
fl_scale_undo() / fl_scale_redo()

Could Greg help us?

Manolo

unread,
Feb 25, 2021, 10:01:41 AM2/25/21
to fltk.general
Please read "Could Greg or Ian help us?"

Albrecht Schlosser

unread,
Feb 25, 2021, 12:40:57 PM2/25/21
to fltkg...@googlegroups.com
On 2/25/21 3:54 PM Manolo wrote:
>
> On Thursday, February 25, 2021 at 1:57:34 PM UTC+1 Albrecht Schlosser wrote:
>
> If there's anything wrong with my test program, please tell me.
>
> Yes. There is. That program assumes the font face and size are correctly
> set when fl_text_extents() is called. But that's not the case at
> the very beginning of the draw() method.
>
> If you add
>     fl_font(labelfont(), labelsize());
> just before
>     fl_text_extents(label(), dx, dy, ll, lh);
> all runs smoothly.

Ooh, what a silly mistake, sorry for the noise. I had copied things
around and obviously deleted that line.

Works now, as you say. Thanks for the correction.

> The transient window display lets the font size to large values which
> produce erroneously large colored boxes later.

Yep, that explains it very well.

Bill Spitzak

unread,
Feb 25, 2021, 2:47:49 PM2/25/21
to fltkg...@googlegroups.com
I recommend the existing fl_push_matrix and fl_pop_matrix be used, and one new function that sets the scale to 1:

fl_push_matrix();
fl_reset_scale();
draw();
fl_pop_matrix();

Setting the scale to 1 probably should preserve translation and rotation, and maybe even mirror images. This is scale(1/abs(det)) where det is the determinant of the current matrix. It might make sense for this to round the translation to the nearest integer, that may be expected by code using this.


--
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/5d22c61e-f4b3-4ec2-ea84-bc1270afdc14%40online.de.

Remy Oukaour

unread,
Feb 26, 2021, 1:19:56 PM2/26/21
to fltk.general
Thank you, this does just what I needed! The fl_remove_scale()/fl_restore_scale() API is a convenient wrapper around what was already possible.

Manolo

unread,
Feb 27, 2021, 3:11:26 AM2/27/21
to fltk.general
On Friday, February 26, 2021 at 7:19:56 PM UTC+1 remy.o...@gmail.com wrote:
Thank you, this does just what I needed! The fl_remove_scale()/fl_restore_scale() API is a convenient wrapper around what was already possible.
Good. It's now fixed in the source code, but with these improved names:
    fl_override_scale()  /  fl_restore_scale()
Reply all
Reply to author
Forward
0 new messages