Skip to content

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

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

19 changes: 14 additions & 5 deletions core/different/different.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
157 changes: 154 additions & 3 deletions core/src/processing/core/PImage.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@
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.util.Arrays;

import processing.awt.ShimAWT;
Expand Down Expand Up @@ -162,6 +164,13 @@ public class PImage implements PConstants, Cloneable {
public static final int BLUE_MASK = 0x000000ff;


// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

/**
* The default DPI rounding method to be used.
*/
private static String defaultDPIRoundingMethod = "round";

//////////////////////////////////////////////////////////////


Expand Down Expand Up @@ -200,7 +209,7 @@ public PImage() {
* @param height image height
*/
public PImage(int width, int height) {
init(width, height, RGB, 1);
init(width, height, RGB, getScaleFactor());

// toxi: is it maybe better to init the image with max alpha enabled?
//for(int i=0; i<pixels.length; i++) pixels[i]=0xffffffff;
Expand All @@ -218,7 +227,7 @@ public PImage(int width, int height) {
* @param format Either RGB, ARGB, ALPHA (grayscale alpha channel)
*/
public PImage(int width, int height, int format) {
init(width, height, format, 1);
init(width, height, format, getScaleFactor());
}


Expand All @@ -231,7 +240,7 @@ public PImage(int width, int height, int format, int factor) {
* Do not remove, see notes in the other variant.
*/
public void init(int width, int height, int format) { // ignore
init(width, height, format, 1);
init(width, height, format, getScaleFactor());
}


Expand Down Expand Up @@ -275,7 +284,149 @@ private void init(int width, int height, int format, int factor,
this.pixels = pixels;
}

/**
* 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);
}

protected int getScaleFactor(String roundingMethod) {
float scaleFactor = 1.0f;

String osName = System.getProperty("os.name").toLowerCase();
// System.out.println("Operating System: " + osName);

if (osName.contains("mac")) {
// macOS DPI scaling
//scaleFactor = ThinkDifferent.getScaleFactor();
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("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")) {
// 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");
}

// System.out.println("Scale Factor: " + scaleFactor);
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();

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);
}

/**
* Gets the default DPI rounding method.
*
* @return The default DPI rounding method.
*/
public static String getDefaultDPIRoundingMethod() {
return defaultDPIRoundingMethod;
}

/**
* 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.
*/
Expand Down
4 changes: 4 additions & 0 deletions core/src/processing/core/ThinkDifferent.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down