Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set default pixel density to the display's density. #952

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

yehiarasheed
Copy link

@yehiarasheed yehiarasheed commented Mar 2, 2025

Description

This PR closes issue #950. The aim of this PR is to change the default value of the pixelDensity variable, represented by the pixelDensity() method to the display's density (DPI). The purpose of this change is to provide sharper rendering by default to displays that support High DPI.

Implementation

This Section is regarding the implementation itself, I'll leave it here in case it is needed later for debugging. The DPI feature is computed as follows, we are making use of the Toolkit class found in the java.awt package. I've decided to implement the DPI Computation in a separate method for modularity and easier maintenance later on as could be seen below.

import java.awt.Toolkit;

  protected int getDisplayDPI() {
    int dpi = Toolkit.getDefaultToolkit().getScreenResolution();

    if (dpi == 96 && System.getProperty("os.name").toLowerCase().contains("win")) {
      return 2;
    }

    return (dpi >= 144) ? 2 : 1;  // 2x for high DPI, 1x otherwise
  }

I've used the Toolkit.getDefaultToolkit() method to acquire the Default Toolkit, on which I invoke the instance method getScreenResolution() which returns the DPI of the display if it could be computed. This introduced a tricky case, on some windows machines, whenever the DPI of the Display is high sometimes windows defaults to a DPI of 96 sometimes as could be seen in this case. Leading us to check if the DPI is equal to 96 and the system is Windows, then assume a High DPI, else just check the DPI value. We assume a High DPI value to be greater than or equal to 144 to cover Windows and Retina macOS displays effectively.

Additionally, I've updated the PImage Constructors that make use of the init method, which sets the pixelDensity variable, to use the getDisplayDPI method instead of the default value 1.

public PImage(int width, int height) {
    init(width, height, RGB, getDisplayDPI());
}

public PImage(int width, int height, int format) {
    init(width, height, format, getDisplayDPI());
}

As well as the init method itself that has three parameters to use the getDisplayDPI method too.

public void init(int width, int height, int format) {  // ignore
    init(width, height, format, getDisplayDPI());
}

Any feedback is welcomed.

This is aimed at achieving a High DPI by default for displays that support it.
@hx2A
Copy link
Collaborator

hx2A commented Mar 2, 2025

Thank you, @yehiarasheed , for this PR!

What platforms have you tested this on? I suspect platform specific behavior might add some wrinkles. I'd be happy to be wrong about this. Perhaps I have been traumatized by too many MacOS issues. :(

Can you make the visibility of the new method protected and not private? The PImage class is inherited by PGraphics and therefore all renderers. It is possible others might benefit from calling this some day.

@yehiarasheed
Copy link
Author

As it turns out, you're actually correct. After a bit more research, I've discovered that the main issue is actually with windows systems as some of them by default return a DPI of 96 instead of the actual DPI whenever the DPI is high as could be seen here. On another note, I've discovered a fault in the logic of the getDisplayDPI() method implementation that I will correct and add to the PR description so that it is visible for anyone debugging any problems later on. I am testing it on a Windows machine currently on IntelliJ as instructed. Please review and get back to me. My implementation should cover most displays although, upon research, I've discovered that using JavaFX is generally more accurate than using the Toolkit class.

- Added check for Windows systems where Toolkit.getScreenResolution()
  incorrectly reports 96 DPI on high-DPI screens.
- Force 2x scaling for Windows if DPI is 96 to ensure correct display scaling.
- Maintain normal 2x scaling for DPI >= 144, fallback to 1x for lower DPI.
@SableRaf SableRaf requested a review from Stefterv March 2, 2025 19:45
@hx2A
Copy link
Collaborator

hx2A commented Mar 3, 2025

return (dpi >= 144) ? 2 : 1; // 2x for high DPI, 1x otherwise

Is there a risk this might need to change in the future as monitors & technology evolve?

I've discovered that using JavaFX is generally more accurate than using the Toolkit class.

What other approaches are available here? JavaFX is nice but does that require extra JavaFX jars and libraries to be available? I do like JavaFX though, good for you to look into that.

Is there a way to get the pixel density info by making a call directly to the OS? Trying to infer the pixel density from the DPI feels like a less certain approach. We can write native code for each OS to do this.

@yehiarasheed
Copy link
Author

yehiarasheed commented Mar 3, 2025

Is there a risk this might need to change in the future as monitors & technology evolve?

There is definitely a risk that this might need to change in the future since we’re relying on a specific number. A better approach would be us checking if the DPI is just greater than 96, so that we don’t specifically check for numbers greater than 144 (hardcoding) and we can include any display with a DPI higher than the standard 96 as high-DPI. Although, most OSes do tend to standardize scaling factors so the number 144 should work fine in most cases.

What other approaches are available here? JavaFX is nice but does that require extra JavaFX jars and libraries to be available? I do like JavaFX though, good for you to look into that.

As for the JavaFX approach, it’s generally more accurate than using the Toolkit class and would definitely need some setup and adding dependencies and JARs, still I do like JavaFX too. However, this doesn’t really matter since the best approach is actually querying the OS itself using native OS functions and getting the actual numbers, this is as good as it gets. It actually came up to me while looking through the getDisplayDPI implementation but I didn’t think we’d want to go through with such implementation. This way we would get the most accurate numbers available, directly from the OS. If we all agree on this implementation I can go ahead and change the implementation to be platform-specific and use native OS functions.

Note

Upon revision, if we decided to go with implementing platform-specific methods to get the correct pixel density, we could reuse the methods already implemented in the Platform.java file/class (Platform.isWindows(), Platform.isMacOS(), Platform.isLinux()) for maintainability.

@yehiarasheed yehiarasheed marked this pull request as draft March 6, 2025 22:02
@yehiarasheed
Copy link
Author

Hi @hx2A , I’m adding support for detecting pixel density across platforms (Windows, macOS, Linux, Wayland). Since NativeInterface.java is in the io folder, should I create a new class for this functionality (e.g., DisplayUtils) in the core folder, or is there a better location for it?

Thanks!

@hx2A
Copy link
Collaborator

hx2A commented Mar 7, 2025

However, this doesn’t really matter since the best approach is actually querying the OS itself using native OS functions and getting the actual numbers, this is as good as it gets

Let's pursue this avenue and see where we can go with it.

Hi @hx2A , I’m adding support for detecting pixel density across platforms (Windows, macOS, Linux, Wayland). Since NativeInterface.java is in the io folder, should I create a new class for this functionality (e.g., DisplayUtils) in the core folder, or is there a better location for it?

Thanks!

I'm not familiar with NativeInterface.java but that doesn't seem to be a part of core.jar. Isn't that in this library?

https://processing.org/reference/libraries/io/index.html

Not everyone will be using that library though.

In core.jar you will find a ThinkDifferent.java class that already makes several native MacOS calls through a small compiled library. If there is an Objective C call that provides this information, it would be easy for you to add the code to what is already there.

For Windows there is a executable called fenster.exe that seems to be there to get the display scaling. Is that helpful for this problem? Is something about that not working?

I'm not sure about what to do for Linux. Generally Linux is better behaved about these kinds of things though.

yehiarasheed and others added 7 commits March 8, 2025 11:31
- Added the getScaleFactor method to different.m to retrieve the display scale factor on macOS.
- Updated ThinkDifferent.java to include the native method declaration for getScaleFactor.
- Ensured the Makefile compiles different.m correctly for both x86_64 and ARM architectures and creates a universal library.
- Implemented getScaleFactor in PImage to call native OS methods based on the platform.

This commit prepares the code for compilation and testing on a macOS machine.
- Enhanced `getScaleFactor` to include native OS calls for Windows DPI using `Fenster.exe`.
- Adjusted scale factor rounding to use 1.5 as the threshold for high DPI detection.
- Added debug print statements to assist with troubleshooting, which can be easily enabled if needed.
- Included error handling for potential issues with executing `Fenster.exe`.
- Implement `getScaleFactor()` and `getScaleFactor(String method)` to determine the scale factor based on the operating system.
- Add `roundScaleFactor(String roundingMethod, float scaleFactor)` to apply different rounding methods to the scale factor.
- Introduce `setDefaultDPIRoundingMethod(String method)` and `getDefaultDPIRoundingMethod()` to manage the default DPI rounding method.
- Update error message for running the required executable to be more user-friendly.
- Implemented DPI scaling for Linux using `xdpyinfo` for X11 and `wlr-randr` for Wayland.
- Added error handling for potential IOExceptions.
- Added logging to indicate the DPI scaling method used.

Note: This implementation is pending testing on an AWS instance.
- Added handling for NumberFormatException in the Linux X11 DPI scaling logic.
- Tested the changes on a Linux X11 machine.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants