Class HiDPIUtils

java.lang.Object
com.formdev.flatlaf.util.HiDPIUtils

public class HiDPIUtils extends Object
  • Field Details

    • useTextYCorrection

      private static Boolean useTextYCorrection
    • SCALE_FACTORS

      private static final float[] SCALE_FACTORS
    • CORRECTION_SEGOE_UI

      private static final float[] CORRECTION_SEGOE_UI
    • CORRECTION_TAHOMA

      private static final float[] CORRECTION_TAHOMA
    • CORRECTION_INTER

      private static final float[] CORRECTION_INTER
    • CORRECTION_OPEN_SANS

      private static final float[] CORRECTION_OPEN_SANS
    • useDebugScaleFactor

      private static Boolean useDebugScaleFactor
  • Constructor Details

    • HiDPIUtils

      public HiDPIUtils()
  • Method Details

    • paintAtScale1x

      public static void paintAtScale1x(Graphics2D g, JComponent c, HiDPIUtils.Painter painter)
    • paintAtScale1x

      public static void paintAtScale1x(Graphics2D g, int x, int y, int width, int height, HiDPIUtils.Painter painter)
      Paint at system scale factor 1x to avoid rounding issues at 125%, 150% and 175% scaling.

      Scales the given Graphics2D down to 100% and invokes the given painter passing scaled x, y, width and height.

      Uses the same scaling calculation as the JRE uses.

    • scale

      private static Rectangle2D.Double scale(double scaleX, double scaleY, double px, double py, int width, int height)
      Scales a rectangle in the same way as the JRE does in sun.java2d.pipe.PixelToParallelogramConverter.fillRectangle(), which is used by Graphics.fillRect().
    • normalize

      private static double normalize(double value)
    • useTextYCorrection

      private static boolean useTextYCorrection()
    • computeTextYCorrection

      public static float computeTextYCorrection(Graphics2D g)
      When painting text on HiDPI screens and the JRE scales, then the text is painted too far down on some operating systems. The higher the system scale factor is, the more.

      This method computes a correction value for the Y position.

    • correctionForScaleY

      private static float correctionForScaleY(Graphics2D g, float[] correction)
    • scaleFactor2index

      private static int scaleFactor2index(float scaleFactor)
    • useDebugScaleFactor

      private static boolean useDebugScaleFactor()
    • getUserScaleFactor

      private static float getUserScaleFactor()
    • drawStringWithYCorrection

      public static void drawStringWithYCorrection(JComponent c, Graphics2D g, String text, int x, int y)
      Applies Y correction and draws the given string at the specified location. The provided component is used to query text properties and anti-aliasing hints.

      Use this method instead of Graphics.drawString(String, int, int) for correct anti-aliasing.

      Replacement for SwingUtilities2.drawString().

    • drawStringUnderlineCharAtWithYCorrection

      public static void drawStringUnderlineCharAtWithYCorrection(JComponent c, Graphics2D g, String text, int underlinedIndex, int x, int y)
      Applies Y correction and draws the given string at the specified location underlining the specified character. The provided component is used to query text properties and anti-aliasing hints.

      Replacement for SwingUtilities2.drawStringUnderlineCharAt().

    • createGraphicsTextYCorrection

      public static Graphics2D createGraphicsTextYCorrection(Graphics2D g)
      Creates a graphics object and applies Y correction to string drawing methods. If no Y correction is necessary, the passed in graphics object is returned.
    • repaint

      public static void repaint(Component c)
      Repaints the given component.

      See repaint(Component, int, int, int, int) for more details.

      Since:
      3.5
    • repaint

      public static void repaint(Component c, Rectangle r)
      Repaints the given component area.

      See repaint(Component, int, int, int, int) for more details.

      Since:
      3.5
    • repaint

      public static void repaint(Component c, int x, int y, int width, int height)
      Repaints the given component area.

      Invokes Component.repaint(int, int, int, int) on the given component,

      Use this method, instead of Component.repaint(...), to fix a problem in Swing when using scale factors that end on .25 or .75 (e.g. 1.25, 1.75, 2.25, etc) and repainting single components, which may not repaint right and/or bottom 1px edge of component.

      The problem may occur under following conditions:

      • using Java 9 or later
      • system scale factor is 125%, 175%, 225%, ... (Windows only; Java on macOS and Linux does not support fractional scale factors)
      • repaint whole component or right/bottom area of component
      • component is opaque; or component is contained in a opaque container that has same right/bottom bounds as component
      • component has bounds that Java/Swing scales different when repainting components
      Since:
      3.5
    • needsSpecialRepaint

      private static boolean needsSpecialRepaint(Component c, int x, int y, int width, int height)
      There is a problem in Swing, when using scale factors that end on .25 or .75 (e.g. 1.25, 1.75, 2.25, etc) and repainting single components, which may not repaint right and/or bottom 1px edge of component.

      The component is first painted to an in-memory image, and then that image is copied to the screen. See javax.swing.RepaintManager.PaintManager#paintDoubleBufferedFPScales().

      There are two clipping rectangles involved when copying the image to the screen: sun.java2d.SunGraphics2D#devClip and sun.java2d.SunGraphics2D#usrClip.

      devClip is the device clipping in physical pixels. It gets the bounds of the painting component, which is either the passed component, or if it is non-opaque, then the first opaque ancestor of the passed component. It is calculated in sun.java2d.SunGraphics2D#constrain() while getting a graphics context via JComponent.getGraphics().

      usrClip is the user clipping, which is set via Graphics clipping methods. This is done in javax.swing.RepaintManager.PaintManager#paintDoubleBufferedFPScales().

      The intersection of devClip and usrClip (computed in sun.java2d.SunGraphics2D#validateCompClip()) is used to copy the image to the screen.

      Unfortunately different scaling/rounding strategies are used to calculate the two clipping rectangles, which is the reason of the issue.

      devClip (see sun.java2d.SunGraphics2D#constrain()):

      
       int devX = (int) (x * scale);
       int devWidth = Math.round( width * scale )
       
      usrClip (see javax.swing.RepaintManager.PaintManager#paintDoubleBufferedFPScales()):
      
       int usrX = (int) Math.ceil( (x * scale) - 0.5 );
       int usrWidth = ((int) Math.ceil( ((x + width) * scale) - 0.5 )) - usrX;
       
      X/Y coordinates are always rounded down for devClip, but rounded up for usrClip. Width/height calculation is also different.
    • installHiDPIRepaintManager

      public static void installHiDPIRepaintManager()
      Installs a HiDPIUtils.HiDPIRepaintManager on Windows when running in Java 9+, but only if default repaint manager is currently installed.

      Invoke once on application startup. Compatible with all/other LaFs.

      Since:
      3.5
    • addDirtyRegion

      public static void addDirtyRegion(JComponent c, int x, int y, int width, int height, HiDPIUtils.DirtyRegionCallback callback)
      Similar to repaint(Component, int, int, int, int), but invokes callback instead of invoking Component.repaint(int, int, int, int).

      For use in custom repaint managers.

      Since:
      3.5