From c39d62c2a4cd5e1017d29073e097a84b026c7050 Mon Sep 17 00:00:00 2001 From: Yehia Rasheed <157399068+yehiarasheed@users.noreply.github.com> Date: Sun, 2 Mar 2025 14:54:15 +0200 Subject: [PATCH 1/8] Set default pixel density to the display's density. This is aimed at achieving a High DPI by default for displays that support it. --- core/src/processing/core/PImage.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/core/src/processing/core/PImage.java b/core/src/processing/core/PImage.java index d2ac4e3b99..765d7f7b86 100644 --- a/core/src/processing/core/PImage.java +++ b/core/src/processing/core/PImage.java @@ -25,6 +25,7 @@ package processing.core; import java.awt.Image; +import java.awt.Toolkit; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; @@ -200,7 +201,7 @@ public PImage() { * @param height image height */ public PImage(int width, int height) { - init(width, height, RGB, 1); + init(width, height, RGB, getDisplayDPI()); // toxi: is it maybe better to init the image with max alpha enabled? //for(int i=0; i 0 ? dpi : 1; // Fallback to 1 if DPI cannot be determined + } /** * Check the alpha on an image, using a really primitive loop. From 4ee403f4607bdeca5b188d3cc0d50ae46f7dfbe1 Mon Sep 17 00:00:00 2001 From: Yehia Rasheed <157399068+yehiarasheed@users.noreply.github.com> Date: Sun, 2 Mar 2025 21:33:32 +0200 Subject: [PATCH 2/8] fix: modify getDisplayDPI() to handle Windows DPI misreporting - 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. --- core/src/processing/core/PImage.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/core/src/processing/core/PImage.java b/core/src/processing/core/PImage.java index 765d7f7b86..e915c80333 100644 --- a/core/src/processing/core/PImage.java +++ b/core/src/processing/core/PImage.java @@ -281,11 +281,18 @@ private void init(int width, int height, int format, int factor, (ACHIEVE HIGH-DPI BY DEFAULT) Change access modifier as needed. */ - private int getDisplayDPI() { - int dpi = Toolkit.getDefaultToolkit().getScreenResolution(); - return dpi > 0 ? dpi : 1; // Fallback to 1 if DPI cannot be determined + protected int getDisplayDPI() { + int dpi = Toolkit.getDefaultToolkit().getScreenResolution(); + + // On Windows, always assume 2x if Toolkit misreports 96 DPI + if (dpi == 96 && System.getProperty("os.name").toLowerCase().contains("win")) { + return 2; + } + + return (dpi >= 144) ? 2 : 1; // 2x for high DPI, 1x otherwise } + /** * Check the alpha on an image, using a really primitive loop. */ From b5573f3d50eaafd2d229bcd4b4449645c60d87b8 Mon Sep 17 00:00:00 2001 From: Yehia Rasheed <157399068+yehiarasheed@users.noreply.github.com> Date: Sat, 8 Mar 2025 13:52:44 +0200 Subject: [PATCH 3/8] Add getScaleFactor method to ThinkDifferent and update Makefile - 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. --- core/different/different.m | 19 ++++++++++++----- core/src/processing/core/PImage.java | 22 ++++++++++++-------- core/src/processing/core/ThinkDifferent.java | 4 ++++ 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/core/different/different.m b/core/different/different.m index 5edfbe0f69..ddd8b42b57 100644 --- a/core/different/different.m +++ b/core/different/different.m @@ -7,27 +7,36 @@ (JNIEnv *env, jclass clazz, jboolean visible, jboolean kioskMode) { NSApplicationPresentationOptions options = - NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar; - [NSApp setPresentationOptions:options]; + NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar; + [NSApp setPresentationOptions:options]; } - JNIEXPORT void JNICALL Java_processing_core_ThinkDifferent_showMenuBar (JNIEnv *env, jclass clazz, jboolean visible, jboolean kioskMode) { [NSApp setPresentationOptions:0]; } - JNIEXPORT void JNICALL Java_processing_core_ThinkDifferent_activateIgnoringOtherApps (JNIEnv *env, jclass klass) { [NSApp activateIgnoringOtherApps:true]; } - JNIEXPORT void JNICALL Java_processing_core_ThinkDifferent_activate (JNIEnv *env, jclass klass) { [NSApp activate]; } + +JNIEXPORT jfloat JNICALL Java_processing_core_ThinkDifferent_getScaleFactor +(JNIEnv *env, jclass cls) { + NSArray *screens = [NSScreen screens]; + if ([screens count] > 0) { + NSScreen *mainScreen = [screens objectAtIndex:0]; + NSDictionary *description = [mainScreen deviceDescription]; + NSNumber *scaleFactor = [description objectForKey:NSDeviceResolution]; + return [scaleFactor floatValue]; + } + return 1.0; +} \ No newline at end of file diff --git a/core/src/processing/core/PImage.java b/core/src/processing/core/PImage.java index e915c80333..176a799320 100644 --- a/core/src/processing/core/PImage.java +++ b/core/src/processing/core/PImage.java @@ -201,7 +201,7 @@ public PImage() { * @param height image height */ public PImage(int width, int height) { - init(width, height, RGB, getDisplayDPI()); + init(width, height, RGB, getScaleFactor()); // toxi: is it maybe better to init the image with max alpha enabled? //for(int i=0; i= 144) ? 2 : 1; // 2x for high DPI, 1x otherwise + + return (scaleFactor >= 1.75) ? 2 : 1; // 2x for high DPI, 1x otherwise } diff --git a/core/src/processing/core/ThinkDifferent.java b/core/src/processing/core/ThinkDifferent.java index bfa6768bee..1929898489 100644 --- a/core/src/processing/core/ThinkDifferent.java +++ b/core/src/processing/core/ThinkDifferent.java @@ -132,6 +132,10 @@ static private Desktop getDesktop() { // https://developer.apple.com/documentation/appkit/nsapplication/activate() static native public void activate(); + // for platform-specific (native) scale Scale Factor getter + // https://developer.apple.com/documentation/appkit/nsscreen/backingscalefactor + public static native float getScaleFactor(); + // Used by py5 to bring Sketch to the front static public boolean activateSketchWindow() { try { From d113f7af99712e8bac9579cef3d7c5c121beca52 Mon Sep 17 00:00:00 2001 From: Yehia Rasheed <157399068+yehiarasheed@users.noreply.github.com> Date: Sun, 9 Mar 2025 01:42:51 +0200 Subject: [PATCH 4/8] Improve DPI Scaling Detection and Debugging on Windows - 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`. --- core/src/processing/core/PImage.java | 58 +++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/core/src/processing/core/PImage.java b/core/src/processing/core/PImage.java index 176a799320..aef0dea6a4 100644 --- a/core/src/processing/core/PImage.java +++ b/core/src/processing/core/PImage.java @@ -30,8 +30,11 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.io.InputStream; import java.io.OutputStream; +import java.io.BufferedReader; +import java.io.InputStream; import java.util.Arrays; import processing.awt.ShimAWT; @@ -282,21 +285,58 @@ private void init(int width, int height, int format, int factor, Change access modifier as needed. */ protected int getScaleFactor() { - float scaleFactor = 1; + float scaleFactor = 1.0f; + + String osName = System.getProperty("os.name").toLowerCase(); +// System.out.println("Operating System: " + osName); - if(Platform.isMacOS()) { + if (osName.contains("mac")) { + // macOS DPI scaling scaleFactor = ThinkDifferent.getScaleFactor(); - } - else if(Platform.isWindows()) - //scaleFactor = Fenster.getScaleFactor(); - else if(Platform.isLinux()) - System.out.println("Linux"); + System.out.println("This is macOS DPI"); + } else if (osName.contains("windows")) { + try { + // Path to Fenster.exe + String basePath = new File("").getAbsolutePath(); + basePath = basePath.substring(0, basePath.length() - 4); + basePath = basePath.replace("\\", "/"); + String fensterExePath = basePath + "/build/windows/fenster/Fenster.exe"; + +// for debugging purposes +// System.out.println("Fenster.exe Path: " + fensterExePath); + + // Create a ProcessBuilder to launch Fenster.exe + ProcessBuilder processBuilder = new ProcessBuilder(fensterExePath); + processBuilder.redirectErrorStream(true); + + // Start the process + Process process = processBuilder.start(); + + // Read the output of the process (getting the Scale Factor) + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + try { + scaleFactor = Float.parseFloat(line) / 96.0f; // Convert DPI to scale factor + } catch (NumberFormatException e) { + System.err.println("Failed to parse scale factor from line: " + line); + } + } + } + } catch (IOException e) { + System.err.println("Error running Fenster.exe: " + e.getMessage()); + } +// System.out.println("This is Windows DPI"); + } else if (osName.contains("linux")) { + // Linux DPI scaling + System.out.println("This is Linux DPI"); + } - return (scaleFactor >= 1.75) ? 2 : 1; // 2x for high DPI, 1x otherwise +// System.out.println("Scale Factor: " + scaleFactor); + return (scaleFactor >= 1.5) ? 2 : 1; // 2x for high DPI, 1x otherwise Threshold is a 1.5 Scale Factor (144 DPI) } - /** * Check the alpha on an image, using a really primitive loop. */ From 2d7e97f66a358f0d7d65c452e66c7801a6c200a7 Mon Sep 17 00:00:00 2001 From: yehiarasheed Date: Sun, 9 Mar 2025 04:42:13 +0200 Subject: [PATCH 5/8] Add DPI scaling and rounding methods to PImage class - 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. --- core/src/processing/core/PImage.java | 37 +++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/core/src/processing/core/PImage.java b/core/src/processing/core/PImage.java index aef0dea6a4..d07f876e87 100644 --- a/core/src/processing/core/PImage.java +++ b/core/src/processing/core/PImage.java @@ -25,7 +25,6 @@ package processing.core; import java.awt.Image; -import java.awt.Toolkit; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; @@ -34,7 +33,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.BufferedReader; -import java.io.InputStream; import java.util.Arrays; import processing.awt.ShimAWT; @@ -166,6 +164,10 @@ public class PImage implements PConstants, Cloneable { public static final int BLUE_MASK = 0x000000ff; + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + private static String defaultDPIRoundingMethod = "round"; + ////////////////////////////////////////////////////////////// @@ -285,6 +287,10 @@ private void init(int width, int height, int format, int factor, Change access modifier as needed. */ protected int getScaleFactor() { + return getScaleFactor(defaultDPIRoundingMethod); + } + + protected int getScaleFactor(String roundingMethod) { float scaleFactor = 1.0f; String osName = System.getProperty("os.name").toLowerCase(); @@ -325,7 +331,7 @@ protected int getScaleFactor() { } } catch (IOException e) { - System.err.println("Error running Fenster.exe: " + e.getMessage()); + System.err.println("An error occurred while trying to run the required executable (Fenster.exe). Please ensure the file path is correct and try again: " + e.getMessage()); } // System.out.println("This is Windows DPI"); } else if (osName.contains("linux")) { @@ -334,9 +340,32 @@ protected int getScaleFactor() { } // System.out.println("Scale Factor: " + scaleFactor); - return (scaleFactor >= 1.5) ? 2 : 1; // 2x for high DPI, 1x otherwise Threshold is a 1.5 Scale Factor (144 DPI) + return roundScaleFactor(roundingMethod,scaleFactor); // 2x for high DPI, 1x otherwise Threshold is a 1.5 Scale Factor (144 DPI) } + protected static int roundScaleFactor(String roundingMethod, float scaleFactor) { + roundingMethod = roundingMethod.toLowerCase(); + + if(roundingMethod.equals("round") || roundingMethod.equals("rnd") || roundingMethod.equals("nearestint") || roundingMethod.equals("nearest int") || roundingMethod.equals("nearest integer") || roundingMethod.equals("nearestinteger")) + return (int) Math.round(scaleFactor); + else if(roundingMethod.equals("ceil") || roundingMethod.equals("roundup") || roundingMethod.equals("round up") || roundingMethod.equals("rndup") || roundingMethod.equals("rnd up") || roundingMethod.equals("up")) + return (int) Math.ceil(scaleFactor); + else if(roundingMethod.equals("floor") || roundingMethod.equals("rounddown") || roundingMethod.equals("round down") || roundingMethod.equals("rnddown") || roundingMethod.equals("rnd down") || roundingMethod.equals("down")) + return (int) Math.floor(scaleFactor); + else if(roundingMethod.equals("roundpreferfloor") || roundingMethod.equals("round prefer floor") || roundingMethod.equals("roundpreferdown") || roundingMethod.equals("round prefer down") || roundingMethod.equals("rndpreferfloor") || roundingMethod.equals("rnd prefer floor") || roundingMethod.equals("rndpreferdown") || roundingMethod.equals("rnd prefer down") || roundingMethod.equals("preferfloor") || roundingMethod.equals("prefer floor") || roundingMethod.equals("preferdown") || roundingMethod.equals("prefer down")) + return (scaleFactor >= 1.75) ? 2 : 1; + + // Throw an Exception Whenever an invalid Rounding method is used + throw new RuntimeException("Invalid Rounding Method: " + roundingMethod); + } + + public static String getDefaultDPIRoundingMethod() { + return defaultDPIRoundingMethod; + } + + public static void setDefaultDPIRoundingMethod(String method) { + defaultDPIRoundingMethod = method; + } /** * Check the alpha on an image, using a really primitive loop. */ From 36e11616f7da420d685ff727ba142b6deb9d6298 Mon Sep 17 00:00:00 2001 From: yehiarasheed Date: Sun, 9 Mar 2025 04:52:32 +0200 Subject: [PATCH 6/8] Add JavaDoc comments to the static variable and methods. --- core/src/processing/core/PImage.java | 35 ++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/core/src/processing/core/PImage.java b/core/src/processing/core/PImage.java index d07f876e87..9da87000b6 100644 --- a/core/src/processing/core/PImage.java +++ b/core/src/processing/core/PImage.java @@ -166,6 +166,9 @@ public class PImage implements PConstants, Cloneable { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + /** + * The default DPI rounding method to be used. + */ private static String defaultDPIRoundingMethod = "round"; ////////////////////////////////////////////////////////////// @@ -281,11 +284,11 @@ private void init(int width, int height, int format, int factor, this.pixels = pixels; } - /* - This method computes the current display's DPI, in order to set the default pixel density to the display's density - (ACHIEVE HIGH-DPI BY DEFAULT) - Change access modifier as needed. - */ + /** + * Computes the current display's DPI to set the default pixel density to the display's density. + * + * @return The scale factor based on the default DPI rounding method. + */ protected int getScaleFactor() { return getScaleFactor(defaultDPIRoundingMethod); } @@ -343,6 +346,14 @@ protected int getScaleFactor(String roundingMethod) { return roundScaleFactor(roundingMethod,scaleFactor); // 2x for high DPI, 1x otherwise Threshold is a 1.5 Scale Factor (144 DPI) } + /** + * Rounds the scale factor based on the specified rounding method. + * + * @param roundingMethod The method to use for rounding the scale factor. + * @param scaleFactor The scale factor to be rounded. + * @return The rounded scale factor. + * @throws RuntimeException If an invalid rounding method is used. + */ protected static int roundScaleFactor(String roundingMethod, float scaleFactor) { roundingMethod = roundingMethod.toLowerCase(); @@ -359,12 +370,22 @@ else if(roundingMethod.equals("roundpreferfloor") || roundingMethod.equals("roun throw new RuntimeException("Invalid Rounding Method: " + roundingMethod); } + /** + * Gets the default DPI rounding method. + * + * @return The default DPI rounding method. + */ public static String getDefaultDPIRoundingMethod() { return defaultDPIRoundingMethod; } - public static void setDefaultDPIRoundingMethod(String method) { - defaultDPIRoundingMethod = method; + /** + * Sets the default DPI rounding method. + * + * @param method The method to set as the default DPI rounding method. + */ + public static void setDefaultDPIRoundingMethod(String roundingMethod) { + defaultDPIRoundingMethod = roundingMethod; } /** * Check the alpha on an image, using a really primitive loop. From 040309fa59b5091a0f1ed9c65a0e7e71860d8757 Mon Sep 17 00:00:00 2001 From: yehiarasheed Date: Sun, 9 Mar 2025 05:07:14 +0200 Subject: [PATCH 7/8] Add Linux DPI scaling implementation - 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. --- core/src/processing/core/PImage.java | 40 ++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/core/src/processing/core/PImage.java b/core/src/processing/core/PImage.java index 9da87000b6..ed409fb014 100644 --- a/core/src/processing/core/PImage.java +++ b/core/src/processing/core/PImage.java @@ -339,6 +339,46 @@ protected int getScaleFactor(String roundingMethod) { // System.out.println("This is Windows DPI"); } else if (osName.contains("linux")) { // Linux DPI scaling + try { + String desktopSession = System.getenv("XDG_SESSION_TYPE"); + if ("wayland".equalsIgnoreCase(desktopSession)) { + // Wayland DPI scaling using wlr-randr + ProcessBuilder processBuilder = new ProcessBuilder("wlr-randr"); + processBuilder.redirectErrorStream(true); + Process process = processBuilder.start(); + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + if (line.contains("Scale:")) { + String[] parts = line.split("\\s+"); + if (parts.length >= 2) { + scaleFactor = Float.parseFloat(parts[1]); + } + } + } + } + } else { + // X11 DPI scaling using xdpyinfo + ProcessBuilder processBuilder = new ProcessBuilder("xdpyinfo"); + processBuilder.redirectErrorStream(true); + Process process = processBuilder.start(); + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + if (line.contains("resolution:")) { + String[] parts = line.split("\\s+"); + if (parts.length >= 3) { + scaleFactor = Float.parseFloat(parts[2]) / 96.0f; // Convert DPI to scale factor + } + } + } + } + } + } catch (IOException e) { + System.err.println("An error occurred while trying to run the DPI scaling command: " + e.getMessage()); + } System.out.println("This is Linux DPI"); } From e41ef6322dd0cafa080c8a9f6bc7961eb6bce2a6 Mon Sep 17 00:00:00 2001 From: Yehia Rasheed Date: Sun, 9 Mar 2025 20:29:02 +0200 Subject: [PATCH 8/8] Handled NumberFormatException for Linux X11 DPI scaling - Added handling for NumberFormatException in the Linux X11 DPI scaling logic. - Tested the changes on a Linux X11 machine. --- core/src/processing/core/PImage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/processing/core/PImage.java b/core/src/processing/core/PImage.java index ed409fb014..4c6b5bc423 100644 --- a/core/src/processing/core/PImage.java +++ b/core/src/processing/core/PImage.java @@ -301,7 +301,7 @@ protected int getScaleFactor(String roundingMethod) { if (osName.contains("mac")) { // macOS DPI scaling - scaleFactor = ThinkDifferent.getScaleFactor(); + //scaleFactor = ThinkDifferent.getScaleFactor(); System.out.println("This is macOS DPI"); } else if (osName.contains("windows")) { try {