Skip to content

Enabling ColorSync on Mac #351

@phraktle

Description

@phraktle

I noticed that colors in imgui-java on my Mac are displayed more saturated than they should be. After some digging I concluded that the Mac ColorSync is not applied on these windows, since it is not set by GLFW, see glfw/glfw#2748

As a workaround I have implemented the native call (see below). Until the issue above is fixed, it would be great to include this in imgui-java. There are two issues with doing it in my client code:

  1. it has to be done exactly after creating the window, but before any of the buffers are created (otherwise there will be flickering), specifically here: https://github.com/SpaiR/imgui-java/blob/v1.90.0/imgui-app/src/main/java/imgui/app/Window.java#L83 – which there's no hook method for, so a client would need to duplicate the entire class hierarchy to override
  2. the color management would need to be applied on any new native windows as well, eg. when undocking one from the root window (I couldn't yet identify where this would need to happen)

Here's a class to perform the native call:

import org.lwjgl.glfw.GLFWNativeCocoa;
import org.lwjgl.system.JNI;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.Platform;
import org.lwjgl.system.macosx.ObjCRuntime;

/**
 * Utility for enabling ColorSync color management on macOS.
 *
 * OpenGL windows bypass ColorSync by default on macOS, which causes
 * colors to appear more saturated than in native apps, especially on
 * wide-gamut displays (P3).
 *
 * See also: https://github.com/glfw/glfw/issues/2748
 */
public class ColorSync {

    /**
     * Apply sRGB color space to a GLFW window.
     *
     * @param glfwWindow The GLFW window handle to apply ColorSync to
     */
    public static void applyToWindow(long glfwWindow) {
        if (Platform.get() != Platform.MACOSX) {
            return;
        }

        try (var stack = MemoryStack.stackPush()) {
            // Get the NSWindow handle
            long nsWindow = GLFWNativeCocoa.glfwGetCocoaWindow(glfwWindow);
            if (nsWindow == 0) {
                return;
            }

            // Get Objective-C classes and selectors
            long NSColorSpace = ObjCRuntime.objc_getClass(stack.UTF8("NSColorSpace"));
            long sRGBColorSpace = ObjCRuntime.sel_registerName(stack.UTF8("sRGBColorSpace"));
            long setColorSpace = ObjCRuntime.sel_registerName(stack.UTF8("setColorSpace:"));

            // Get sRGB color space: [NSColorSpace sRGBColorSpace]
            long targetColorSpace = JNI.invokePPP(NSColorSpace, sRGBColorSpace,
                ObjCRuntime.getLibrary().getFunctionAddress("objc_msgSend"));

            // Set color space on window: [window setColorSpace:colorSpace]
            JNI.invokePPPV(nsWindow, setColorSpace, targetColorSpace,
                ObjCRuntime.getLibrary().getFunctionAddress("objc_msgSend"));

        } catch (Exception e) {
            System.err.println("Warning: Failed to enable ColorSync: " + e.getMessage());
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions