Skip to content

Commit

Permalink
WARNING: Support high DPI displays
Browse files Browse the repository at this point in the history
This change could break things. So, I created SUPPORT_HIGH_DPI flag to enable it (disabled by default).

Basically, it detects HighDPI display and scales all drawing (and mouse input) appropiately to match the equivalent "standardDPI" screen size on highDPI. It uses screenScaling matrix to do that.

This scaling comes with some undesired effects, like aliasing on default font text (keep in mind that font is pixel-perfect, not intended for any non-rounded scale factor).

The only solution for this aliasing would be some AA postpro filter or implementing the highDPI scaling in a different way: rendering to a texture and scaling it with FILTER_BILINEAR, check `core_window_scale_letterbox.c` example for reference.

Use at your own risk.
  • Loading branch information
raysan5 committed May 1, 2019
1 parent 270f563 commit bb2841a
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 75 deletions.
2 changes: 2 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
#define SUPPORT_SCREEN_CAPTURE 1
// Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()
#define SUPPORT_GIF_RECORDING 1
// Allow scale all the drawn content to match the high-DPI equivalent size (only PLATFORM_DESKTOP)
//#define SUPPORT_HIGH_DPI 1


//------------------------------------------------------------------------------------
Expand Down
132 changes: 57 additions & 75 deletions src/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
* #define SUPPORT_GIF_RECORDING
* Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()
*
* #define SUPPORT_HIGH_DPI
* Allow scale all the drawn content to match the high-DPI equivalent size (only PLATFORM_DESKTOP)
*
* DEPENDENCIES:
* rglfw - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX. FreeBSD, OpenBSD, NetBSD, DragonFly)
* raymath - 3D math functionality (Vector2, Vector3, Matrix, Quaternion)
Expand Down Expand Up @@ -437,7 +440,7 @@ extern void UnloadDefaultFont(void); // [Module: text] Unloads default fo
//----------------------------------------------------------------------------------
static bool InitGraphicsDevice(int width, int height); // Initialize graphics device
static void SetupFramebuffer(int width, int height); // Setup main framebuffer
static void SetupViewport(void); // Set viewport parameters
static void SetupViewport(int width, int height); // Set viewport for a provided width and height
static void SwapBuffers(void); // Copy back buffer to front buffers

static void InitTimer(void); // Initialize timer
Expand Down Expand Up @@ -1175,6 +1178,7 @@ void BeginMode2D(Camera2D camera)
rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2)

rlLoadIdentity(); // Reset current matrix (MODELVIEW)
rlMultMatrixf(MatrixToFloat(screenScaling)); // Apply screen scaling if required

// Camera rotation and scaling is always relative to target
Matrix matOrigin = MatrixTranslate(-camera.target.x, -camera.target.y, 0.0f);
Expand All @@ -1193,6 +1197,7 @@ void EndMode2D(void)
rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2)

rlLoadIdentity(); // Reset current matrix (MODELVIEW)
rlMultMatrixf(MatrixToFloat(screenScaling)); // Apply screen scaling if required
}

// Initializes 3D mode with custom camera (3D)
Expand Down Expand Up @@ -1246,6 +1251,8 @@ void EndMode3D(void)
rlMatrixMode(RL_MODELVIEW); // Get back to modelview matrix
rlLoadIdentity(); // Reset current matrix (MODELVIEW)

rlMultMatrixf(MatrixToFloat(screenScaling)); // Apply screen scaling if required

rlDisableDepthTest(); // Disable DEPTH_TEST for 2D
}

Expand Down Expand Up @@ -1284,22 +1291,8 @@ void EndTextureMode(void)

rlDisableRenderTexture(); // Disable render target

// Set viewport to default framebuffer size (screen size)
SetupViewport();

rlMatrixMode(RL_PROJECTION); // Switch to PROJECTION matrix
rlLoadIdentity(); // Reset current matrix (PROJECTION)

// Set orthographic projection to current framebuffer size
// NOTE: Configured top-left corner as (0, 0)
rlOrtho(0, GetScreenWidth(), GetScreenHeight(), 0, 0.0f, 1.0f);

rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix
rlLoadIdentity(); // Reset current matrix (MODELVIEW)

// Reset current screen size
currentWidth = GetScreenWidth();
currentHeight = GetScreenHeight();
// Set viewport to default framebuffer size
SetupViewport(renderWidth, renderHeight);
}

// Returns a ray trace from mouse position
Expand Down Expand Up @@ -2336,12 +2329,11 @@ static bool InitGraphicsDevice(int width, int height)
currentWidth = width;
currentHeight = height;

screenScaling = MatrixIdentity(); // No draw scaling required by default

// NOTE: Framebuffer (render area - renderWidth, renderHeight) could include black bars...
// ...in top-down or left-right to match display aspect ratio (no weird scalings)

// Screen scaling matrix is required in case desired screen area is different than display area
screenScaling = MatrixIdentity();

#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
glfwSetErrorCallback(ErrorCallback);

Expand Down Expand Up @@ -2388,7 +2380,7 @@ static bool InitGraphicsDevice(int width, int height)
//glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window
//glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API
//glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers
#if defined(PLATFORM_DESKTOP)
#if defined(PLATFORM_DESKTOP) && defined(SUPPORT_HIGH_DPI)
// NOTE: If using external GLFW, it requires latest GLFW 3.3 for this functionality
glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); // Scale content area based on the monitor content scale where window is placed on
#endif
Expand Down Expand Up @@ -2468,9 +2460,11 @@ static bool InitGraphicsDevice(int width, int height)
// framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched
// by the sides to fit all monitor space...

// At this point we need to manage render size vs screen size
// NOTE: This function uses and modifies global module variables:
// screenWidth/screenHeight - renderWidth/renderHeight - screenScaling
// Try to setup the most appropiate fullscreen framebuffer for the requested screenWidth/screenHeight
// It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset)
// Modified global variables: screenWidth/screenHeight - renderWidth/renderHeight - renderOffsetX/renderOffsetY - screenScaling
// TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed...
// HighDPI monitors are properly considered in a following similar function: SetupViewport()
SetupFramebuffer(displayWidth, displayHeight);

window = glfwCreateWindow(displayWidth, displayHeight, windowTitle, glfwGetPrimaryMonitor(), NULL);
Expand All @@ -2494,12 +2488,6 @@ static bool InitGraphicsDevice(int width, int height)
if (windowPosY < 0) windowPosY = 0;

glfwSetWindowPos(window, windowPosX, windowPosY);

// Get window HiDPI scaling factor
float scaleRatio = 0.0f;
glfwGetWindowContentScale(window, &scaleRatio, NULL);
scaleRatio = roundf(scaleRatio);
//screenScaling = MatrixScale(scaleRatio, scaleRatio, scaleRatio);
#endif
renderWidth = screenWidth;
renderHeight = screenHeight;
Expand Down Expand Up @@ -2886,23 +2874,23 @@ static bool InitGraphicsDevice(int width, int height)
}
#endif // PLATFORM_ANDROID || PLATFORM_RPI

renderWidth = screenWidth;
renderHeight = screenHeight;

// Initialize OpenGL context (states and resources)
// NOTE: screenWidth and screenHeight not used, just stored as globals
// NOTE: screenWidth and screenHeight not used, just stored as globals in rlgl
rlglInit(screenWidth, screenHeight);

// Setup default viewport
SetupViewport();
int fbWidth = renderWidth;
int fbHeight = renderHeight;

// Initialize internal projection and modelview matrices
// NOTE: Default to orthographic projection mode with top-left corner at (0,0)
rlMatrixMode(RL_PROJECTION); // Switch to PROJECTION matrix
rlLoadIdentity(); // Reset current matrix (PROJECTION)
rlOrtho(0, renderWidth - renderOffsetX, renderHeight - renderOffsetY, 0, 0.0f, 1.0f);
rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix
rlLoadIdentity(); // Reset current matrix (MODELVIEW)
#if defined(PLATFORM_DESKTOP) && defined(SUPPORT_HIGH_DPI)
glfwGetFramebufferSize(window, &fbWidth, &fbHeight);

// Screen scaling matrix is required in case desired screen area is different than display area
screenScaling = MatrixScale((float)fbWidth/screenWidth, (float)fbHeight/screenHeight, 1.0f);
SetMouseScale((float)screenWidth/fbWidth, (float)screenHeight/fbHeight);
#endif // PLATFORM_DESKTOP && SUPPORT_HIGH_DPI

// Setup default viewport
SetupViewport(fbWidth, fbHeight);

ClearBackground(RAYWHITE); // Default background color for raylib games :P

Expand All @@ -2912,21 +2900,32 @@ static bool InitGraphicsDevice(int width, int height)
return true;
}

// Set viewport parameters
static void SetupViewport(void)
// Set viewport for a provided width and height
static void SetupViewport(int width, int height)
{
#if defined(__APPLE__)
// Get framebuffer size of current window
// NOTE: Required to handle HighDPI display correctly on OSX because framebuffer is automatically reasized to adapt to new DPI.
// When OS does that, it can be detected using GLFW3 callback: glfwSetFramebufferSizeCallback()
int fbWidth, fbHeight;
glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
rlViewport(renderOffsetX/2, renderOffsetY/2, fbWidth - renderOffsetX, fbHeight - renderOffsetY);
#else
// Initialize screen viewport (area of the screen that you will actually draw to)
// NOTE: Viewport must be recalculated if screen is resized
renderWidth = width;
renderHeight = height;

// Set viewport width and height
// NOTE: We consider render size and offset in case black bars are required and
// render area does not match full display area (this situation is only applicable on fullscreen mode)
rlViewport(renderOffsetX/2, renderOffsetY/2, renderWidth - renderOffsetX, renderHeight - renderOffsetY);
#endif

rlMatrixMode(RL_PROJECTION); // Switch to PROJECTION matrix
rlLoadIdentity(); // Reset current matrix (PROJECTION)

// Set orthographic projection to current framebuffer size
// NOTE: Configured top-left corner as (0, 0)
rlOrtho(0, renderWidth, renderHeight, 0, 0.0f, 1.0f);

rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix
rlLoadIdentity(); // Reset current matrix (MODELVIEW)

// Window size must be updated to be used on 3D mode to get new aspect ratio (BeginMode3D())
// NOTE: Be careful! GLFW3 will choose the closest fullscreen resolution supported by current monitor,
// for example, if reescaling back to 800x450 (desired), it could set 720x480 (closest fullscreen supported)
currentWidth = screenWidth;
currentHeight = screenHeight;
}

// Compute framebuffer size relative to screen size and display size
Expand Down Expand Up @@ -2959,7 +2958,7 @@ static void SetupFramebuffer(int width, int height)

// Screen scaling required
float scaleRatio = (float)renderWidth/(float)screenWidth;
screenScaling = MatrixScale(scaleRatio, scaleRatio, scaleRatio);
screenScaling = MatrixScale(scaleRatio, scaleRatio, 1.0f);

// NOTE: We render to full display resolution!
// We just need to calculate above parameters for downscale matrix and offsets
Expand Down Expand Up @@ -3705,24 +3704,7 @@ static void CursorEnterCallback(GLFWwindow *window, int enter)
// NOTE: Window resizing not allowed by default
static void WindowSizeCallback(GLFWwindow *window, int width, int height)
{
// If window is resized, viewport and projection matrix needs to be re-calculated
rlViewport(0, 0, width, height); // Set viewport width and height
rlMatrixMode(RL_PROJECTION); // Switch to PROJECTION matrix
rlLoadIdentity(); // Reset current matrix (PROJECTION)
rlOrtho(0, width, height, 0, 0.0f, 1.0f); // Orthographic projection mode with top-left corner at (0,0)
rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix
rlLoadIdentity(); // Reset current matrix (MODELVIEW)
rlClearScreenBuffers(); // Clear screen buffers (color and depth)

// Window size must be updated to be used on 3D mode to get new aspect ratio (BeginMode3D())
// NOTE: Be careful! GLFW3 will choose the closest fullscreen resolution supported by current monitor,
// for example, if reescaling back to 800x450 (desired), it could set 720x480 (closest fullscreen supported)
screenWidth = width;
screenHeight = height;
renderWidth = width;
renderHeight = height;
currentWidth = width;
currentHeight = height;
SetupViewport(width, height); // Reset viewport and projection matrix for new size

// NOTE: Postprocessing texture is not scaled to new size

Expand Down

0 comments on commit bb2841a

Please sign in to comment.