Giter Site home page Giter Site logo

Comments (56)

DevCharly avatar DevCharly commented on May 29, 2024 2

The only difference between the two is starting to resize the frame horizontally by a pixel each time. Half the time the vertical lines are hairline and black, and half the time they are two pixels wide and grey.

That's because AWT does some "rounding" when converting logical coordinates to device coordinates and depending on where the window is located on screen, the results is different. To compensate this, you probably have to take the window location into account and do some "reverse rounding" ...

But there is a easy solution: change the scaling of the java.awt.Graphics to 1. Then you can paint exact pixels.

I use this in FlatLaf. Here is the related FlatLaf code (feel free to copy it):
https://github.com/JFormDesigner/FlatLaf/blob/58dbccec2d5264cd2ce0317e49f83c766bd562b7/flatlaf-core/src/main/java/com/formdev/flatlaf/util/HiDPIUtils.java#L33-L101

This is your example, modified to use HiDPIUtils.paintAtScale1x():

import com.formdev.flatlaf.util.HiDPIUtils;

class Version2 extends JComponent {
    public Version2() {
        this.setOpaque(false);
        this.setPreferredSize(new Dimension(80, 24));
    }

    @Override
    protected void paintComponent(Graphics g) {
        HiDPIUtils.paintAtScale1x( (Graphics2D) g, this, this::paintImpl );
    }

    private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
        g.setColor(Color.BLACK);
        g.drawRect(x, y, width - 1, height - 1);

        for (int i = 1; i < 5; i++) {
            int inset = 2 * i;
            g.drawRect(x + inset, y + inset, width - (inset * 2) - 1, height - (inset * 2) - 1);
        }
    }
}

Screenshot at 150%:

grafik

HiDPI tips

Some tips for painting Swing components at HiDPI screens.

Scale factor

You should not store any scale factor in the component (or somewhere else).
The scale factor may change anytime (in Java 9+).
Either the user changes it in the Windows preferences.
Or the user moves the window to secondary screen that uses different scale factor than primary screen.

Either get scale factor from the component:

double scaleFactor = comp.getGraphicsConfiguration().getDefaultTransform().getScaleX();

Or from Graphics2D:

double scaleFactor = ((Graphics2D)g).getTransform().getScaleX();

Avoid line drawing method

Because line drawing methods (g.drawLine(), g.drawRect(), g.drawRoundRect(), ...) expect coordinates specified
"in the middle" of the stroke thickness, and due to AWT scaling/rounding, the lines are not always drawn where you expect it.

To paint horizontal or vertical lines it is better to use g.fillRect().

To paint (rounded) rectangles, use paths. E.g.:

Path2D border = new Path2D.Float( Path2D.WIND_EVEN_ODD );
border.append( new Rectangle( x, y, w, h ), false );
border.append( new Rectangle( x + 1, y + 1, w - 2, h - 2 ), false );
g.fill( border );

If you always use HiDPIUtils.paintAtScale1x(), then you probably can use line drawing methods.

Fill component background

Never use antialiasing when filling complete component background with:

g.fillRect(0, 0, c.getWidth(),c.getHeight());

Paint component bounds/border

When using antialiasing and painting at the outside edge of a component (e.g. a border or a rounded background),
then use HiDPIUtils.paintAtScale1x() to do so.
Otherwise AWT would paint slightly outside of component bounds (due to antialiasing and scaling).

from radiance.

lukeu avatar lukeu commented on May 29, 2024 2

Hi folks, just to say there's a closely related issue in the JDK currently under review - about how to fix up TitledBorder to get its 2 lines of border to always draw 'pixel-perfectly' even under fractional scaling levels. In case you want to compare notes or share some pearls of wisdom: openjdk/jdk#7449 (comment)

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024 1

See the last part of https://www.pushing-pixels.org/2021/10/04/radiance-4-5-0-and-whats-next.html

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024 1

This last version looks pretty decent on my 2.5x Windows screen

image

from radiance.

DevCharly avatar DevCharly commented on May 29, 2024 1

Regarding stroke control:
For line painting, AWT actually expects coordinates specified "in the middle" of the stroke thickness.
The default stroke control normalizes coordinates to paint at full pixels.
So if you do g.drawLine( 0, 0, 100, 0 ),
then AWT actually paints a 1px thick line at 0.5, 0.5, 99.5, 0.5

If you set RenderingHints.VALUE_STROKE_PURE, then AWT does not normalize
the coordinates and paints the line "on two pixels" if antialiasing is enable, which looks blurry.

If you use HiDPIUtils.paintAtScale1x() it makes no sense to use pure stroke control.

In FlatLaf, I use pure stroke control only for painting chevron arrows.
Otherwise (small) arrows may look asymmetric when AWT normalizes coordinates...

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

Can you put a breakpoint in NeonCortex.getScaleFactor and see what it returns under Java 8 and Java 10?

from radiance.

neesjanvaneck avatar neesjanvaneck commented on May 29, 2024

NeonCortex.getScaleFactor is returning the following values.

  • Java 8 (Windows 7/10):
    • DPI 100%: 1.0
    • DPI 150%: 1.0
  • Java 9/10 (Windows 7/10):
    • DPI 100%: 1.0
    • DPI 150%: 1.5

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

Thanks. The only Windows machine I have available has a different configuration, with a higher-density display. I'll see if I can play with the DPI settings there and reproduce something similar.

from radiance.

ankaufma avatar ankaufma commented on May 29, 2024

maybe caused by http://openjdk.java.net/jeps/263 ?

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

Please try the latest 0.9-SNAPSHOT build to see how it looks on your environment. It's still not perfect, but should be better.

from radiance.

neesjanvaneck avatar neesjanvaneck commented on May 29, 2024

Thanks for the update. It seems to be improved. Please see the screenshot below. As you can see, it is indeed not yet perfect. Do you see possibilities to make more improvements? I would be happy to test it again.

image

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

There's a rather convoluted setup that I borrowed from intellij.

https://github.com/kirill-grouchnikov/radiance/blob/master/neon/src/main/java/org/pushingpixels/neon/internal/contrib/intellij/UIUtil.java queries the desktop for the current resolution to see if it should take the high-DPI path. https://github.com/kirill-grouchnikov/radiance/blob/master/neon/src/main/java/org/pushingpixels/neon/internal/contrib/intellij/JBHiDPIScaledImage.java is the high-DPI aware image that, internally, allocates a larger image if necessary to account for the desktop scale factor (examples below). And https://github.com/kirill-grouchnikov/radiance/blob/master/neon/src/main/java/org/pushingpixels/neon/internal/contrib/intellij/HiDPIScaledGraphics.java is the pass-through graphics.

So let's say you're running on a MacBook Pro with 2x scale factor. Substance caches almost all visuals as offscreen images because otherwise the performance would be terrible. For a button which is 100x30 "Swing" pixels on the screen, Substance allocates a 200x60 pixel image (JBHiDPIScaledImage).

When the time comes to draw the outlines, those are intended to be hairline, 1-pixel stroke. However, that is the screen 1-pixel and not Swing 1-pixel. You see the difference when you create a BasicStroke with with of 1 and how it looks like on a MacBook Pro screen - it's too thick.

This is what happens. During rendering in Substance UI delegates, a BasicStroke instance is created with width of 0.5. Then, as a drawPath operation is issued to the underlying graphics, that Graphics object returned by JBHiDPIScaledImage in createGraphics is scaled by 2x in both x and y. So essentially, there's a mix of Swing pixels (sizes, gradient stops), screen pixels (stroke width) and scaled image (200x60 vs 100x30).

The stroke created by the UI delegate is 0.5, which is then scaled by 2x inside JBHiDPIScaledImage when it is rendered into that 200x60 image. And then, finally, that image is drawn onto the screen on a 100x30 rectangle, essentially scaling it by 0.5x. The end result is that the 1-pixel (Swing) stroke rendered on that image is then scaled down to be 1-pixel (screen) hairline.

This works well for scale factor of 2x (and 2.5x on my Windows laptop), but does indeed result in these glitches on 1.25x and 1.5x.

An alternative approach would be to bypass the scale-down/scale-up double pass in JBHiDPIScaledImage, but rather operate directly on the underlying big image. But that big image would still need to be rendered back to a smaller area. So in this case, a 100x30 button under 1.25x is 125x37.5 - and that's where it starts getting tricky. How does this half-a-pixel get treated? The same can happen under 1.5x for controls with odd width/height.

I don't really have a good answer for now, unfortunately.

from radiance.

enwired avatar enwired commented on May 29, 2024

I'm seeing a similar problem, and this is the main issue preventing me from moving away from Java 8 and older versions of Substance at this time, though I would like to upgrade both.

When using Windows 7, magification 125% (which is the default), I am seeing extra one-pixel lines around the edge of the frame, and when I use 'JFrame.setDefaultLookAndFeelDecorated(true);' I also see that "X" for the close button looks strangely curved.

Example from Radiance-Substance 2.5.1 and Java 11.0.1 on Windows 7.
(You can ignore the details of this example. I'm just testing a ListCellRenderer.)

radianceEx1

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

How does this demo look like under other look and feels that support hi dpi, like Synthetica or Darcula?

from radiance.

enwired avatar enwired commented on May 29, 2024

I have not yet tried those other LAFs with Java 9+. We like and are accustomed to Substance and don't want to change away from it unless necessary! We don't like Synthetica. Darcula is fine for a dark LAF but we also want a lighter LAF as an option.

To be honest, the image I attached does not look horrible, and may be acceptable. When I change the title bar to a dark color, though, those little gaps become more noticeable. The curvy-looking "X" for the close button is also distracting.

I need to learn how to make some of the small Skin customizations I've done in previous versions which don't work with the current API. Probably mostly just re-named classes and methods.

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

Had some time to look at it a bit more.

For a button that is 28x28 "units" on a 2.5x scaled screen is 70x70 underlying pixels. This whole block is considered to be the visual bounds of the button, and Substance composes the overall visuals of the button into that 70x70 block. Specifically, it is the border, the background fill, the icon and the text.

The border is computed to be aligned flush with the bounding box. That is, unlike Nimbus, Darcula and a few other third-party look-and-feels that leave outer margin around the border for focus ring and drop shadow, Substance "fills" the whole bounds with the button visuals. There's a combination of factors that leads to cropped / cutoff visuals under the fractional metrics.

The first is the underlying limitation of representing fractional numbers as Java doubles/floats. This can be seen by stepping through the code in SubstanceOutlineUtilities.getBaseOutline. The simplest math such as 0.2f+16.6f-3.0f gives the result of 13.800001f. As that method computes the shape that corresponds to the outer border, these aberrations creep into the resulting GeneralPath.

Then that path is used in ButtonBackgroundDelegate.createBackgroundImage to paint that path to an offscreen image, and that image is painted on screen at the end of ButtonBackgroundDelegate.updateBackground. At this point, the code is drawing the 70x70 image back to a 28x28 unit square in the underlying Java2D pipeline. The combination of "overflowing" right and bottom coordinates of the path, along with the default rendering hints makes for the observed appearance. Here, the following rendering hints make the appearance a little bit better:

            graphics.setRenderingHint(RenderingHints.KEY_RENDERING,
                    RenderingHints.VALUE_RENDER_QUALITY);
            graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                    RenderingHints.VALUE_INTERPOLATION_BICUBIC);
            graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
                    RenderingHints.VALUE_FRACTIONALMETRICS_ON);

But it's still not what it should be in the ideal world.

Now, why is this not happening in Darcula? Darcula does two things:

  1. Uses thicker border (one unit instead of one pixel) with a more muted foreground color so that it doesn't appear visually "too" thick.
  2. The border is inset to leave outer margins for the focus ring and optional drop shadow. That ring is quite thick, and even if just a bit of it is cropped off, it's nowhere as visually noticeable as a cropped off hairline border that Substance is using.

This combination of a thicker border + outer margins + thick focus ring is used everywhere in Darcula - buttons, checkboxes, comboboxes, etc.

While it's certainly a viable approach, I want to be careful in taking this path going forward. Diverging the optical bounds of the controls from the real bounds (that is, leaving extra outer margins for the focus ring) means far-reaching changes to application layouts. Either the relevant controls become optically smaller (as they shrink visually), or they take more space in their containers - and everything around them needs to take that into consideration to prevent content overflow or too much space between controls.

As for the rendering hint block above, I want to first check what kind of performance hit it adds, by running the Lightbeam performance suite and see if it's worth including that in the upcoming 3.0 release.

from radiance.

enwired avatar enwired commented on May 29, 2024

Thank you for continuing to work on this!

from radiance.

lukeu avatar lukeu commented on May 29, 2024

Just to say, JDK's built in Windows L&F suffers exactly the same kind of artefacts fractional scale levels - e.g. missing borders from Combo boxes (etc) taken from the Windows Theming (smoke and mirrors) stuff.

(Although there are even more jarring problems - e.g. borders that flip between 1px or 2px just as you move/resize things.)

Thanks for taking the time to post updates, it helps us non-users facing similar issues! (Although we have had calls to unify on a platform-agnostic L&F and for dark theme support, so we may become a user yet!)

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

Christoph-GEM - please file this as a separate entry in the issue tracker

from radiance.

ed-erwin-tf avatar ed-erwin-tf commented on May 29, 2024

I have only recently got a Windows machine with High DPI. My program has a bunch of 16x16 icons, and these look very tiny on that machine. (Sadly, designing resolution-independent icons is not a priority for my company.) We have found that we can get around the problem by changing one of the "compatibility settings" on the javaw.exe program itself. This can possibly also work as an imperfect solution to this issue in some cases.

from radiance.

ed-erwin-tf avatar ed-erwin-tf commented on May 29, 2024

I am starting to localize my program to multiple Locales. I noticed something very strange related to this issue. It was so unexpected that I thought I must be mistaken.

If I set the locale to Esperanto (eo) or Volapük (vo), the Substance LAF does not apply the 1.5 magnification that I specified in Windows display properties. (In the real world, I don't need to use these languages, but I use them to test for edge cases.) For all other languages I've tested, the expected 1.5 magnification is applied.

Metal LAF and Windows LAF do not show this Locale-dependent behavior.

Note that I'm still using Java 1.8 and an older version of Substance. This behavior may have changed with newer Radiance and Java 9.

from radiance.

ed-erwin-tf avatar ed-erwin-tf commented on May 29, 2024

PS: I'm the same person as "enwired" above. Just using my work account today.

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

Can't really comment on those old versions of Substance anymore. Too much time has passed since then.

from radiance.

ed-erwin-tf avatar ed-erwin-tf commented on May 29, 2024

I was not expecting you to do anything about this. That's why I put it in a comment rather than opening a new issue. It was just very surprising that changing the locale could have any effect on the fractional scaling.

I think there are some locale-related issues that are still present in the current version, so I may open "issues" for those.

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

Started converting a bit of code to direct rendering on the graphics context, but the off-pixel rendering issues happen there as well. Here's the simplest repro:

import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;

abstract class Base extends JComponent {
    protected float strokeWidth;

    public Base() {
        this.setOpaque(false);
        this.setPreferredSize(new Dimension(80, 24));
        this.strokeWidth = 1.0f / (float) getScaleFactor();
    }

    private static double getScaleFactor(GraphicsDevice device) {
        GraphicsConfiguration graphicsConfig = device.getDefaultConfiguration();

        AffineTransform tx = graphicsConfig.getDefaultTransform();
        double scaleX = tx.getScaleX();
        double scaleY = tx.getScaleY();
        return Math.max(scaleX, scaleY);
    }

    private static double getScaleFactor() {
        double result = 1.0;
        GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice[] devices = e.getScreenDevices();

        // now get the configurations for each device
        for (GraphicsDevice device : devices) {
            result = Math.max(result, getScaleFactor(device));
        }

        return result;
    }
}

class Version1 extends Base {

    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g.create();

        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
                RenderingHints.VALUE_STROKE_PURE);
        g2d.setStroke(new BasicStroke(this.strokeWidth, BasicStroke.JOIN_ROUND, BasicStroke.CAP_BUTT));
        g2d.setColor(Color.BLACK);
        g2d.draw(new Rectangle2D.Float(this.strokeWidth, this.strokeWidth, this.getWidth() - 2 * this.strokeWidth,
                this.getHeight() - 2 * this.strokeWidth));

        for (int i = 1; i < 5; i++) {
            float inset = 2 * i;
            g2d.draw(new Rectangle2D.Float(this.strokeWidth + inset, this.strokeWidth + inset,
                    this.getWidth() - 2 * this.strokeWidth - 2 * inset,
                    this.getHeight() - 2 * this.strokeWidth - 2 * inset));
        }

        g2d.dispose();
    }
}

public class Hairlines {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Hairlines");

            frame.setLayout(new FlowLayout());
            JComponent version1 = new Version1();

            frame.add(version1);

            frame.setVisible(true);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        });
    }
}

Running on my Windows 10 laptop that has a 2.5x scale out of the box

image

Results in this:

hairlines

The only difference between the two is starting to resize the frame horizontally by a pixel each time. Half the time the vertical lines are hairline and black, and half the time they are two pixels wide and grey.

Going to send a question on the https://mail.openjdk.java.net/mailman/listinfo/client-libs-dev mailing list to ask what is the "right" way to do hairline paths under fractional screen metrics.

As mentioned earlier in this thread, this is not an issue for other active third-party look-and-feels such as flatlaf and darklaf that use thicker borders. And core look-and-feels such as Metal and Windows do display the same issue on this monitor during resizing.

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

Thanks Karl. Much appreciated!

The idea is indeed to move away from relying on the scale factor for all drawing in Radiance. I'll try the code you linked to a bit later today on my Windows box to see how it looks. Pretty much all borders, outlines and separators in Radiance are 1-pixel, so hopefully this should work out and unblock this transition that I'm planning.

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

A question on this line, @DevCharly:

private static double normalize( double value ) {
   return Math.floor( value + 0.25 ) + 0.25;
}

Where are the constants from? You reference this file but from its initialization here, here and here, looks like they're passing 0.499 as normPosition to be used in their normalize

from radiance.

DevCharly avatar DevCharly commented on May 29, 2024

No sure where it is from. Maybe from classes SurfaceData or D3DSurfaceData, which use 0.25.

Anyway, if I set a breakpoint at PixelToParallelogramConverter.normalize(),
the value for normRoundingBias is 0.25 (on Windows 11, Java 11 and 17)

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

Does it also depend on the default stroke control to get those crisp lines? If I change the paintComponent to set up the stroke control like this:

    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
                RenderingHints.VALUE_STROKE_PURE);
        HiDPIUtils.paintAtScale1x( g2d, this, this::paintImpl );
        g2d.dispose();
    }

I get this:

image

See the middle one getting all smoodged up? And the one on the right is with drawRoundRect with progressively smaller radiuses.

If I only do anti-aliasing (I want to get smooth rounded corners):

    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        HiDPIUtils.paintAtScale1x( g2d, this, this::paintImpl );
        g2d.dispose();
    }

it looks much better:

image

The full code for the component on the right in this last screenshot:

class Version3 extends Base {
    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        HiDPIUtils.paintAtScale1x( g2d, this, this::paintImpl );
        g2d.dispose();
    }

    private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
        int arcSize = 10;
        g.setColor(Color.BLACK);
        g.drawRoundRect(x, y, width - 1, height - 1, arcSize, arcSize);

        for (int i = 1; i < 5; i++) {
            int inset = 2 * i;
            g.drawRoundRect(x + inset, y + inset, width - (inset * 2) - 1, height - (inset * 2) - 1,
                    arcSize - 2, arcSize - 2);
        }
    }
}

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

No sure where it is from. Maybe from classes SurfaceData or D3DSurfaceData, which use 0.25.

Anyway, if I set a breakpoint at PixelToParallelogramConverter.normalize(), the value for normRoundingBias is 0.25 (on Windows 11, Java 11 and 17)

Interesting. Here they create it with 0.25 but in another place in the same class they do 0.499.

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

Thanks for the link @lukeu. After reading through comments, I found https://github.com/apache/netbeans/pull/1284/files and https://github.com/apache/netbeans/pull/1777/files to be most relevant in terms of the approach.

https://github.com/apache/netbeans/blob/master/platform/o.n.swing.plaf/src/org/netbeans/swing/plaf/windows8/DPIUnscaledBorder.java is the latest source code for the scale-aware border.

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

There was an interesting suggestion in that thread about very high density displays, that at some point a hairline border is just too thin in terms of its physical size, and it becomes washed out and almost invisible.

I don't think there's a lot of such displays out there at 4x+ resolution, but worth keeping it in mind. That maybe in that future, Radiance borders and separators will be 2px instead of 1px after a certain scale threshold.

from radiance.

lukeu avatar lukeu commented on May 29, 2024

FYI: my interest in HiDPI started way back in 2015 I got my first Dell XPS with 4k on a relatively small 15" screen. Hacking around the lack of support in Java 7/8 from outside the L&F was, um, "fun". (In hindsight, probably shouldn't have tried to keep supporting Swing's in-built L&Fs. Whoops.)

I don't think there's a lot of such displays out there at 4x+ resolution, but worth keeping it in mind. That maybe in that future, Radiance borders and separators will be 2px instead of 1px after a certain scale threshold.

Yeah whether to upscale a normally-1px line is probably context dependent, but I would suggest that doing so may be the safer default stance. My screen is also 250% and while 1px lines are generally visible (assuming a reasonable contrast) the majority of times I notice them (in any application) it's usually because scaling wasn't taken into account properly, and they look too fine and unbalanced.

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

An update on this issue. The work being done for #388 to convert all rendering in Radiance to direct rendering instead of via cached offscreen images is in a good shape. The majority of core Swing component rendering has been converted, with the remainder being arrow icons, double arrow icons and title pane icons (which are not hairline strokes in any case).

Once these icons have been converted to direct rendering, the second large chunk of work will be to convert all custom Radiance components to direct rendering (command buttons, strips, popups, panels, the entire ribbon).

It would be great if people who have filed and commented on the original visual issues of Radiance under fractional scaling take the latest 6.0-SNAPSHOT bits for a ride and let me know how it looks like. I've been looking at it on my 2.5x scale Windows 10 box, but of course it will be super helpful to have more testing on it.

from radiance.

ed-erwin-tf avatar ed-erwin-tf commented on May 29, 2024

Thanks for working on this. I am finally in a good place to update past java 8, so I can now start using recent builds of Radiance. So I am willing to try 6.0-SNAPSHOT. Where can I find a copy? I would prefer not to attempt to build the jars locally since I have no experience with Gradle.

I'm still seeing a few issues with fractional scaling in 5.0. I will let you know if they still appear in 6.0.

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

https://oss.sonatype.org/content/repositories/snapshots/org/pushing-pixels/ for the snapshot builds if you're using Maven or Gradle. If not, this document for building it locally.

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

All of the changes went into 6.0-SNAPSHOT, so 5.0 isn't going to be of much help / progress. Speaking of which, version 5.0 was a pure renaming / refactoring to bring everything under the Radiance umbrella.

from radiance.

ed-erwin-tf avatar ed-erwin-tf commented on May 29, 2024

Upgrading to 5.0 does help me because it allows me to have some bug fixes that you have made over the years since the very old version I'm using. Also, I can now file bugs about 5.0 and have some hope of them being fixed.

The re-naming in 5.0, and a few other changes, threw me for a bit of a loop. I've found how to do most things I need in the new code, but would like to ask some questions about how to do a few things.

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

Please open a new conversation so that this one stays focused on this particular issue

from radiance.

ed-erwin-tf avatar ed-erwin-tf commented on May 29, 2024

I've tried both 5.0 and 6.0-SNAPSHOT. Windows 10, 250% scaling. Corretto JRE 11.

5.0 fixes the problems I mentioned on [Nov 7, 2019]. (A one-pixel line around some borders, and a strange-looking window close button.) I found only one area in my GUI where there still seems to be a problem with an unwanted one-pixel line with random contents. (That is quite possibly a mistake with my component, not the LAF.)

5.0 will probably work fine for me.

6.0: I do not notice any difference in appearance, but there is a huge negative impact on performance.

My program uses many self-drawn graphics. (Graphs and such.) When I hide those panels, the regular Swing component panels show only a modest negative performance impact.

The current performance with my self-drawn graphics would not be acceptable. Perhaps, though, there are some rendering hints that I need to change?

PS: Regarding my comment on Oct 25, 2021 about components changing size when the locale is changed: this is because the system changes some fonts when using certain locales, and the different font metrics cause changes to some component sizes. Not really so strange after all.

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

6.0: I do not notice any difference in appearance, but there is a huge negative impact on performance.

My program uses many self-drawn graphics. (Graphs and such.) When I hide those panels, the regular Swing component panels show only a modest negative performance impact.

The current performance with my self-drawn graphics would not be acceptable. Perhaps, though, there are some rendering hints that I need to change?

What kind of components / drawing are we talking about?

The change in 6.0 is to switch from rendering to offscreen buffered images to direct rendering to the passed graphics content in all the UI delegates. For example, in pre-6.0 world, a button would track its state. Let's say that button starts in enabled state. Then that button would render itself to an offscreen buffered image, cache that image, and then render that image on screen. Subsequent requests to draw the same exact button in the same exact state would reuse that cached image and render it on the screen without rendering the button first to a new offscreen image. So essentially, the more you used your app, the more the different parts of it would be rendered onto dozens, hundreds, thousands of these offscreen images, and the faster the rendering would become.

In the new world, everything is rendered directly to the screen with no intermediate and cached offscreen bitmaps.

Do your components extend some core Swing component like buttons or checkboxes? Do they perhaps use them as "renderers" a-la lists, tables and trees approach in core Swing? Your code might have explicitly relied on Substance / Radiance doing that offscreen rendering and caching, which does not exist anymore.

from radiance.

ed-erwin-tf avatar ed-erwin-tf commented on May 29, 2024

We have a few custom Swing components. For example a JTree where the nodes have checkboxes. But such things are a minor part of the program.

We also use many customized TableCellRenderer objects which derive from Swing renderers, not Radiance renderers. But those don't seem to be causing a problem.

The slowdown comes from customized JPanel classes which draw graphs and charts. Everything inside those is drawn directly onto the Graphics object, using no Swing UI components. I have a suspicion of what the problem might be. Essentially, our graphics contain 100s of thousands of objects. Those draw themselves with more or less details depending on the current zoom level. If an object will only be a few pixels wide on the screen, we don't draw the inner details of the object. It is possible that something has changed about how the objects are determining their size on screen. Maybe they now think they are 2.5 times as large as they really are because the magnification factor on Windows is set to 250%.

I need a few days to investigate.

from radiance.

ed-erwin-tf avatar ed-erwin-tf commented on May 29, 2024

About performance of 6.0-SNAPSHOT:
I investigated a bunch of stuff in my program. I may find some problems on my end. But right now it looks like there is something wrong in performance of the UI for JSlider. I use Sliders in multiple panels and they interact with each other (via some shared background models). Even if you have only one JSlider, connected to nothing else, it performs slowly sometimes. It often, though not always, responds slowly to being dragged, especially near the end of the range. Problem is absent in 5.0.

Again, Windows 10 at 250%, Java 11 Corretto.

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

Yes, thanks for finding that. I can see it in the Radiance main demo app.

After switching to some of the skins like Autumn or Nebula, the sliders are taking enormous amount of time to paint themselves. First there's this logged to console:

UNSUPPORTED (log once): POSSIBLE ISSUE: unit 1 GLD_TEXTURE_INDEX_2D is unloadable and bound to sampler type (Float) - using zero texture because texture unloadable

And then sliders that take multiple seconds to paint:

Drawing a slider took 453ms
Drawing a slider took 454ms
Drawing a slider took 1674ms
Drawing a slider took 1785ms
Drawing a slider took 1966ms
Drawing a slider took 1962ms
Drawing a slider took 2042ms
Drawing a slider took 1956ms
Drawing a slider took 6418ms
Drawing a slider took 6332ms

This will absolutely need to be addressed as it makes the whole thing unshippable

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

Should be addressed now with this latest commit. I'll upload to Sonatype snapshot repository later in the day, but it can be built and tested from sources.

from radiance.

ed-erwin-tf avatar ed-erwin-tf commented on May 29, 2024

Thanks. I will wait for the snapshot build, and use 5.0 in the meantime. But at least I know a fix will be coming.

This statement that I made above was incorrect: "My program uses many self-drawn graphics. (Graphs and such.) When I hide those panels, the regular Swing component panels show only a modest negative performance impact."

I don't understand the code you checked-in, but it looks like this problem maybe also affected checkboxes?

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

It's some sort of a combination of applying an affine transform on graphics context (for 1x rendering) and then also applying a custom clipping area before drawing gradients. No idea why would that go into multiple seconds, though.

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

Latest snapshot of 6.0 has just been published

from radiance.

ed-erwin-tf avatar ed-erwin-tf commented on May 29, 2024

I can verify that the fix restores the proper performance.
I am seeing a separate bug. No time to investigate today, but this stack trace should help;

java.util.NoSuchElementException: null
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:1000)
at java.base/java.util.Collections.min(Collections.java:601)
at org.pushingpixels.radiance.theming.internal.painter.SeparatorPainterUtils.paintVerticalLines(SeparatorPainterUtils.java:405)
at org.pushingpixels.radiance.theming.internal.ui.RadianceSliderUI.paintTicks(RadianceSliderUI.java:587)
at org.pushingpixels.radiance.theming.internal.ui.RadianceSliderUI.paint(RadianceSliderUI.java:429)
at java.desktop/javax.swing.plaf.ComponentUI.update(ComponentUI.java:161)

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

Thanks, should be fixed now.

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

All relevant UI delegates and painters have been converted to direct rendering world as part of #388.

I'm closing this now, as these umbrella-type trackers can linger around for a long time if they are to include every single follow-up bug fix or improvement. The rest of the work should be tracked as separate new entries for specific components.

from radiance.

ed-erwin-tf avatar ed-erwin-tf commented on May 29, 2024

I have found a strange issue with 6.0-SNAPSHOT today.

My large program works fine with 6.0, mostly. And it fixes some single-pixel details. But there is one panel which simply does not display at all. In 5.0, the panel shows some text boxes and sliders and everything is fine. In 6.0, the panel is simply blank.

When I am able to create a small example, or can give more specific details, I will open a new issue. This is just FYI that 6.0 may not be ready to ship.

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

Thanks, let me know. With #396 fixed, I don't have any more fixes / features planned for 6.0.

from radiance.

ed-erwin-tf avatar ed-erwin-tf commented on May 29, 2024

I have submitted issue 398.

from radiance.

ed-erwin-tf avatar ed-erwin-tf commented on May 29, 2024

"I don't have any more fixes / features planned for 6.0."

I may need to beg for one small feature. I am required to make the toolbar of a window look a special way. It must have

  • an icon on the left top,
  • regular control buttons on the top right,
  • centered text in between.

The slight trick is that the icon is not square. It is wider than it is tall.

I was able to do this in older versions by creating custom sub-classes of your RootPaneUI and RadianceTitlePane. In the modern version, methods that I need to override are marked as private or final.

I would hope you could help me with this since I've found two bugs for you.

from radiance.

kirill-grouchnikov avatar kirill-grouchnikov commented on May 29, 2024

This should be discussed in a separate tracker entry

from radiance.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.