DPI Renderer Scaling Override for JAVA2D

Hello folks!

Forcing 1:1 Pixel Rendering for Different Renderers in Processing

This topic is for OS-level DPI scaling in Windows such as:

This sketch demonstrates how to force 1:1 pixel rendering by overriding DPI scaling for JAVA2D in Processing. The solution was a challenging process that required iterating through multiple approaches with both ChatGPT and Google Gemini to finally arrive at a working solution and comments.

The key piece of the solution is this static block at the beginning of the sketch, which ensures that Java2D and FX2D renderers bypass OS-level DPI scaling (like 125% scaling on Windows) and render at their raw pixel sizes. This guarantees precise 1:1 pixel mapping:

static
  {
  // JAVA2D: 
  // Forces 1:1 pixel rendering by disabling OS-level DPI scaling
  System.setProperty("sun.java2d.uiScale", "1.0");

  // This is NOT included in the code example. Give it a try with JavaFX!
  // FX2D: 
  // Forces 1:1 pixel rendering for the JavaFX-based renderer
  System.setProperty("glass.win.uiScale", "1");
  }

Code for JAVA2D:

/*
 * Multi-Renderer Scaling Debug and Override Sketch (JAVA2D);
 *
 * Author:      glv
 * Date:        2025-10-23
 * Tools used:  ChatGPT and Google Gemini
 *
 * Purpose:
 *   1. Print and analyze display scaling information:
 *        - OS DPI (using AWT Toolkit)
 *   2. Force 1:1 pixel rendering for specific renderers:
 *        - JAVA2D (Swing-based)
 *   3. Visually confirm 1:1 pixel rendering using a fixed-size ASCII string.
 *
 * Critical Overrides:
 *   - JAVA2D:
 *     System.setProperty("sun.java2d.uiScale", "1.0");
 *
 *     Forces Java2D to render at raw pixels (1:1), overriding OS DPI scaling.
 *
 * Notes:
 *   - A static block is required to set these properties before any renderer is initialized,
 *     ensuring consistent 1:1 pixel rendering.
 *   - P2D and P3D do not require scaling overrides and already renders at 1:1 pixels.
 */

// --- IMPORTS FOR ALL FUNCTIONS ---

// Required for getAWTOSDPI() and getSystemUIScaleProperties()
import java.awt.Toolkit; 

// Required for getAWTOSDPI() - specifically for the screen resolution method (less critical)
import java.awt.Dimension; 

// The 'sun.' System.getProperty call requires imports, 
// they access system state directly. 
// ---------------------------------

// This static block executes first, forcing the Java2D rendering
// pipeline to ignore Windows OS DPI scaling (125% on my system)

static
  {
  // Setting scale to 1.0 explicitly disables all
  // OS-level display scaling (example 125% DPI scaling on Windows)
  // for these renderers ensuring a 1:1 pixel mapping.

  // JAVA2D
  System.setProperty("sun.java2d.uiScale", "1.0");  // Try commenting this out to see what happens!
  }

String s; // Variable to store the string of printable ASCII characters.

void settings()
  {
  // Uncomment the renderer to test:
  size(530, 450, JAVA2D);
  //size(530, 450, P2D);
  //size(530, 450, P3D);
  }

void setup()
  {
  getAWTOSDPI();
  println();
  getSystemUIScaleProperties();
  println();

  s = ""; // Initialize the string variable otherwise it is a null.

  // Printable ASCII characters from 32 (space) to 126 (tilde).
  for (int i = 32; i <= 126; i++)
    {
    print((char) i); // Prints to console.
    s = s + char(i); // Concatenates the character to the 's' string.
    }
  println();

  println(s); // Prints to console.

  noLoop();   // Stops the draw() function from running repeatedly.
  }

void draw()
  {
  background(255); // Bacground white.

  // All content in the main sketch will now be rendered correctly and unscaled.
  fill(0);              // Sets the fill color to black.
  textSize(48);         // Sets the font size.
  textAlign(LEFT, TOP); // Sets text alignment to left-horizontal and center-vertical.

  // Displays the character string 's' within a defined rectangular area.
  int border = 20;
  text(s, border, border, width-2*border, height-2*border);
  }

/**
 * Uses the AWT Toolkit to determine the raw DPI set by the operating system.
 */
void getAWTOSDPI() {
  System.out.println("--- AWT/OS DPI Scaling Info ---");

  // AWT Toolkit DPI (96 is standard 100%)
  int osDPI = Toolkit.getDefaultToolkit().getScreenResolution();
  double scalingFactor = osDPI / 96.0;

  System.out.println("OS Detected DPI (96 = 100%): " + osDPI);
  System.out.println("Calculated Scaling Factor: " + scalingFactor);
  System.out.println("-----------------------------");
}

/**
 * Checks specific system properties sometimes used by Java for UI scaling.
 */
void getSystemUIScaleProperties() {
  System.out.println("--- System UI Scale Properties ---");

  String sunProp = System.getProperty("sun.java2d.uiScale");

  System.out.println("Property 'sun.java2d.uiScale': " + sunProp);
  System.out.println("--------------------------------");
}

Left sketch window in image is JAVA2D default with:
// System.setProperty("sun.java2d.uiScale", "1.0"); // Commented and NOT used!

Right sketch window in image is with:
System.setProperty("sun.java2d.uiScale", "1.0"); // Uncommented and used!

Output:

Finally got it working!

:)

1 Like