JDK-8240568 : HiDPI custom cursor is twice as large on regular secondary screen
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 9,10,11,12,13,14,15
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: windows_10
  • CPU: x86_64
  • Submitted: 2020-02-27
  • Updated: 2021-07-13
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
Windows 10, Java 15 EA Build 6

A DESCRIPTION OF THE PROBLEM :
The described problems occur in the following multi-screen setup (and probably on similar ones):
The primary screen is a HiDPI screen and has Windows scaling set to 200%. (In principle, also a screen with regular physical resolution will reproduce the problem, just imagine the results scaled reciprocally.)
The secondary screen has Windows scaling set to 100%.


This bug was found while developing JOSM, the Java OpenStreetMap editor.
Please add the "josm-found" label.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the Java application provided as source code below.  Drag the window that it opens to either screen and hover the mouse over it.

The application passes a BaseMultiResolutionImage to Toolkit.createCustomCursor, with the first image having 32x32 pixels (low-res), and the second image having 64x64 pixels (hi-res).  Passing a MultiResolutionImage is suggested in bug  JDK-8139152, and ordering the images by size increasing is suggested in https://docs.oracle.com/javase/9/docs/api/java/awt/image/BaseMultiResolutionImage.html

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
On both screens, the cursor has about the same (real-world) size, consisting of 64x64 pixels on the HiDPI screen, and 32x32 pixels on the regular screen.

The alpha blending of the background and the lines, in the transparent right half of the cursor, is smooth.
ACTUAL -
On the secondary screen, the cursor is about twice as large as on the primary screen, consisting of 64x64 pixels on both.

The lines in the transparent right half of the cursor are jaggy, black pixels surround the blue lines.  So alpha blending is not smooth.

The erroneous alpha blending might be caused by this source code:
https://github.com/openjdk/jdk/blob/6bab0f539fba8fb441697846347597b4a0ade428/src/java.desktop/share/classes/sun/awt/CustomCursor.java
It constructs the alpha mask from variable "cursor", for which getScaledInstance will default to the first, low-res image.  However, it passes on variable "image" to createNativeCursor, which reference the original "cursor", i.e. the BaseMultiResolutionImage in this case.  createNativeCursor then seems to choose the HiDPI image (which is good at least for the primary screen).
Generally, the code mentioned above is hard to read.  Initially, "image" and "cursor" reference the same object, but depending on conditions, they reference different objects later.  One is scaled, the other is not.  The hotspot is never scaled accordingly, causing bug JDK-8238734.


---------- BEGIN SOURCE ----------
import java.awt.*;
import java.awt.image.BaseMultiResolutionImage;
import java.awt.image.BufferedImage;

public class CustomCursorResolution {
    public static void main(String[] args)
    {
        Dimension bestCursorSize = Toolkit.getDefaultToolkit().getBestCursorSize(32, 32);
        assert bestCursorSize.width == bestCursorSize.height; // effectively 64 for systems with (also) a HiDPI screen
        final int cursorSize = bestCursorSize.width;

        // construct blue diagonal cross with a frame, in two resolutions
        BufferedImage bufferedImageLowRes = createCursorImage(cursorSize / 2);
        BufferedImage bufferedImageHighRes = createCursorImage(cursorSize);

        Image image;
        image = new BaseMultiResolutionImage(bufferedImageLowRes, bufferedImageHighRes); // triggers bug
        //image = bufferedImageHighRes; // workaround avoiding low res alpha mask

        Frame frame = new Frame();
        Cursor cursor;
        cursor = Toolkit.getDefaultToolkit().createCustomCursor(
                image, new Point(cursorSize / 2, cursorSize / 2), null);
        frame.setCursor(cursor);
        frame.setSize(new Dimension(500, 500));
        frame.setBackground(Color.LIGHT_GRAY);
        frame.setVisible(true);
    }

    private static BufferedImage createCursorImage(int cursorSize) {
        BufferedImage bufferedImage = new BufferedImage(cursorSize, cursorSize, BufferedImage.TYPE_INT_ARGB);
        Graphics2D bg = bufferedImage.createGraphics();
        bg.setBackground(new Color(1.0f, 1.0f, 1.0f, 0.0f));
        bg.clearRect(0, 0, cursorSize, cursorSize);
        bg.setColor(Color.WHITE);
        bg.fillRect(0, 0, cursorSize / 2, cursorSize); // white background in the left half, transparent background in right half
        bg.setColor(Color.BLUE);
        bg.drawRect(0, 0, cursorSize - 1, cursorSize - 1);
        bg.setStroke(new BasicStroke(2));
        bg.drawLine(0, 0, cursorSize, cursorSize);
        bg.drawLine(0, cursorSize, cursorSize, 0);
        return bufferedImage;
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Passing only a 64x64 pixel simple Image (uncomment respective line in the sample source code), or making the HiDPI Image the first one of the BaseMultiResolutionImage, fixes the alpha blending problem.
However, the problem remains that the cursor is double size on the regular screen.

Predefined cursors as return by e.g. Cursor.getPredefinedCursor(Cursor.HAND_CURSOR) work perfectly.

FREQUENCY : always



Comments
A HiDPI cursor on the HiDPI resolution when compared to a normal DPI cursor on the normal DPI screen, the latter is displayed with twice the size. Checked this for reported versions and could confirm the issue in JDK 9 and above. See attached screenshot as reference. Result: ========= 8u241: Ok (No HiDPI support) 9: Fail 11.0.6: Fail 13.0.2: Fail 14 ea b 36: Fail 15 ea b11: Fail To verify, run the attached test case with respective JDK versions.
05-03-2020