Skip to content

[4k Display] AWT fonts within SWT are badly scaled on 4k displays #1411

@wolfgang-ch

Description

@wolfgang-ch

Describe the bug

Mainly small fonts are looking really bad compared with SWT fonts.

Why do I use AWT fonts? SWT has a limit with antialiasing when drawing into an image, see here mytourbook/mytourbook#1376 (comment)

With autoScale=100 (for none 4k displays) the AWT font rendering is as good as SWT but autoScale > 100 has this issue.

Is there any AWT/SWT parameter to disable AWT font/image scaling when swt.autoScale > 100 ?

To Reproduce

package test4k;

import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class SWTvsAWT_FontsAndImages {

   private static int                  AUTO_SCALE;

   private static int                  IMAGE_WIDTH;
   private static int                  IMAGE_HEIGHT;

   private static final String         FONT_NAME            = "Segoe UI";
   private static int[]                FONT_SIZE;

   private static final int            MARGIN               = 2;

   private static final int            SHELL_X              = 1500;
   private static final int            SHELL_Y              = 100;

   private static final int            FOREGROUND_COLOR_SWT = SWT.COLOR_BLACK;
   private static final int            BACKGROUND_COLOR_SWT = SWT.COLOR_WHITE;

   private static final java.awt.Color FOREGROUND_COLOR_AWT = java.awt.Color.black;
   private static final java.awt.Color BACKGROUND_COLOR_AWT = java.awt.Color.white;

   private static Display              _swtDisplay;
   private static Font[]               _swtFont;
   private static int[]                _swtFontHeight;

   static {

      AUTO_SCALE = 200;
      AUTO_SCALE = 100;
      AUTO_SCALE = 150;

      FONT_SIZE = new int[] {
            10,
            12,
            16,
            20,
            24,
//            28,
            36
      };

      System.setProperty("swt.autoScale", Integer.toString(AUTO_SCALE));

      _swtDisplay = new Display();

      int numFonts = FONT_SIZE.length;
      int allFontHeights = 0;

      _swtFont = new Font[numFonts];
      _swtFontHeight = new int[numFonts];

      for (int fontIndex = 0; fontIndex < numFonts; fontIndex++) {

         Font swtFont = new Font(_swtDisplay, FONT_NAME, FONT_SIZE[fontIndex], SWT.NORMAL);
         FontData swtFontData = swtFont.getFontData()[0];

         int fontHeight = swtFontData.getHeight();

         _swtFont[fontIndex] = swtFont;
         _swtFontHeight[fontIndex] = fontHeight;

         allFontHeights += fontHeight;
      }

      IMAGE_WIDTH = 1500;
      IMAGE_HEIGHT = (int) (allFontHeights * 1.8);
   }

   private static String getTestText(int fontIndex) {

      return "  autoScale=%d  FONT SIZE=%d".formatted(AUTO_SCALE, FONT_SIZE[fontIndex]);
   }

   private static Image convertAWTtoSWT(final BufferedImage awtImage) {

      final int imageWidth = awtImage.getWidth();
      final int imageHeight = awtImage.getHeight();

      final ImageData swtImageData = new ImageData(imageWidth, imageHeight, 24, new PaletteData(0xFF0000, 0xFF00, 0xFF));

      final int scansize = (((imageWidth * 3) + 3) * 4) / 4;

      final WritableRaster alphaRaster = awtImage.getAlphaRaster();
      final byte[] alphaBytes = new byte[imageWidth];

      for (int y = 0; y < imageHeight; y++) {

         final int[] buff = awtImage.getRGB(0, y, imageWidth, 1, null, 0, scansize);

         swtImageData.setPixels(0, y, imageWidth, buff, 0);

         if (alphaRaster != null) {

            final int[] alpha = alphaRaster.getPixels(0, y, imageWidth, 1, (int[]) null);

            for (int i = 0; i < imageWidth; i++) {
               alphaBytes[i] = (byte) alpha[i];
            }

            swtImageData.setAlphas(0, y, imageWidth, alphaBytes, 0);
         }
      }

      return new Image(_swtDisplay, swtImageData);
   }

   private static BufferedImage createAWTImage() {

      final BufferedImage awtImage = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_4BYTE_ABGR);

      final Graphics2D g2d = awtImage.createGraphics();
      try {

         g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

         g2d.setBackground(BACKGROUND_COLOR_AWT);
         g2d.clearRect(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);

         int devY = 0;

         for (int fontIndex = 0; fontIndex < FONT_SIZE.length; fontIndex++) {

            java.awt.Font font = new java.awt.Font(FONT_NAME, java.awt.Font.PLAIN, FONT_SIZE[fontIndex]);
            g2d.setFont(font);

            final FontMetrics fontMetrics = g2d.getFontMetrics();
            final int textHeight = fontMetrics.getHeight();
            devY += textHeight;

            g2d.setColor(FOREGROUND_COLOR_AWT);
            g2d.drawString(" AWT" + getTestText(fontIndex), MARGIN, devY);
         }

      } finally {

         g2d.dispose();
      }

      return awtImage;
   }

   private static Image createSWTImage() {

      final Image swtImage = new Image(_swtDisplay, IMAGE_WIDTH, IMAGE_HEIGHT);

      final GC gc = new GC(swtImage);
      {
         gc.setAntialias(SWT.ON);

         gc.setBackground(_swtDisplay.getSystemColor(BACKGROUND_COLOR_SWT));
         gc.fillRectangle(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);

         int devY = 0;

         for (int fontIndex = 0; fontIndex < FONT_SIZE.length; fontIndex++) {

            gc.setFont(_swtFont[fontIndex]);
            gc.setForeground(_swtDisplay.getSystemColor(FOREGROUND_COLOR_SWT));
            gc.drawString(" SWT" + getTestText(fontIndex), MARGIN, devY, true);

            devY += _swtFontHeight[fontIndex] + 10;
         }
      }
      gc.dispose();

      return swtImage;
   }

   public static void main(final String[] args) {

      final Image swtImage = createSWTImage();

      final BufferedImage awtImage = createAWTImage();
      final Image swtImageFromAwt = convertAWTtoSWT(awtImage);

      final Shell shell = new Shell(_swtDisplay);
      shell.setText("SWT vs AWT scaling");
      shell.setLocation(SHELL_X, SHELL_Y);
      shell.setSize(IMAGE_WIDTH + 50, IMAGE_HEIGHT * 2 + 30);

      shell.addListener(SWT.Paint, event -> {

         int devY = MARGIN;

         GC gc = event.gc;

         gc.drawImage(swtImage, MARGIN, devY);
         devY += IMAGE_HEIGHT + MARGIN;

         gc.drawImage(swtImageFromAwt, MARGIN, devY);
      });

      shell.open();

      while (!shell.isDisposed()) {
         if (!_swtDisplay.readAndDispatch()) {
            _swtDisplay.sleep();
         }
      }

      swtImage.dispose();
      swtImageFromAwt.dispose();

      for (Font font : _swtFont) {
         font.dispose();
      }

      _swtDisplay.dispose();
   }
}


Expected behavior
AWT within SWT should scale fonts with the same quality as SWT

Screenshots
This screenshot should be viewed without github scaling !

SwtVsAwtScaling

Environment:

  1. Select the platform(s) on which the behavior is seen:
    • All OS
    • [x ] Windows
    • [?] Linux, not tested
    • [?] macOS, not tested
  1. Additional OS info (e.g. OS version, Linux Desktop, etc)
    Windows 10

  2. JRE/JDK version
    java.vendor.version=Temurin-21.0.4+7

Version since
4.23 and older

Workaround (or) Additional context
Have not yet found

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions