有没有办法找到 CGDisplay 的缩放系数?

Is there any way to find the scaling coefficient for a CGDisplay?

我目前正在做一些测试,看看我的应用程序是否可以在 Retina Mac 上正确运行。为此,我安装了 Quartz Debug,目前我处于 运行 缩放模式。我的屏幕模式现在是 960x540,但当然显示器的物理尺寸仍然是全高清,即 1920x1080 像素。

当使用 CGGetActiveDisplayList() 查询监视器数据库,然后在列表中的各个监视器上使用 CGDisplayBounds() 时,returned 监视器大小为 960x540。这是我所期望的,因为据说 CGDisplayBounds() 使用全局显示坐标 space,而不是像素。

然而,令我惊讶的是,CGDisplayPixelsWide()CGDisplayPixelsHigh() 也 return 960x540,尽管它们明确表示为 return 像素,所以我希望它们return 改为 1920x1080。但他们没有。

这让我想知道如何使用 CGDisplay API 检索显示器的真实物理分辨率而不是缩放模式,即在我的例子中是 1920x1080 而不是 960x540?有什么办法可以得到 CGDisplay 的比例系数,这样我就可以自己计算出真实的物理分辨率了吗?

我知道我可以使用backingScaleFactor方法得到这个比例系数,但这只适用于NSScreen,我如何得到CGDisplay的比例系数?

您需要检查显示器的模式,而不仅仅是显示器本身。使用 CGDisplayCopyDisplayMode(),然后使用 CGDisplayModeGetPixelWidth()CGDisplayModeGetPixelHeight()。最后两个是相对较新的函数,文档主要存在于 headers.

当然,不要忘记 CGDisplayModeRelease() 模式 object。

从 Ken 的回答来看,您如何找到本机模式并不明显。为此,请调用 CGDisplayModeGetIOFlags 并从设置了 kDisplayModeNativeFlag 的模式中进行选择(参见 IOKit/IOGraphicsTypes.h,值为 0x02000000)。

const int kFlagNativeMode = 0x2000000;  // see IOGraphicsTypes.h
const CGFloat kNoSize = 100000.0;

NSScreen *screen = NSScreen.mainScreen;    
NSDictionary *desc = screen.deviceDescription;
unsigned int displayID = [[desc objectForKey:@"NSScreenNumber"] unsignedIntValue];
CGSize displaySizeMM = CGDisplayScreenSize(displayID);
CGSize nativeSize = CGSizeMake(kNoSize, kNoSize);
CFStringRef keys[1] = { kCGDisplayShowDuplicateLowResolutionModes };
CFBooleanRef values[1] = { kCFBooleanTrue };
CFDictionaryRef options = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys, (const void**)values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
CFArrayRef modes = CGDisplayCopyAllDisplayModes(displayID, options);
int n = CFArrayGetCount(modes);
for (int i = 0;  i < n;  i++) {
    CGDisplayModeRef mode = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
    if (CGDisplayModeGetIOFlags(mode) & kFlagNativeMode) {
        int w = CGDisplayModeGetWidth(mode);
        // We get both high resolution (screen.backingScaleFactor > 1)
        // and the "low" resolution, in CGFloat units. Since screen.frame
        // is CGFloat units, we want the lowest native resolution.
        if (w < nativeSize.width) {
            nativeSize.width = w;
            nativeSize.height = CGDisplayModeGetHeight(mode);
        }
    }
    // printf("mode: %dx%d %f dpi 0x%x\n", (int)CGDisplayModeGetWidth(mode), (int)CGDisplayModeGetHeight(mode), CGDisplayModeGetWidth(mode) / displaySizeMM.width * 25.4, CGDisplayModeGetIOFlags(mode));
}
if (nativeSize.width == kNoSize) {
    nativeSize = screen.frame.size;
}
CFRelease(modes);
CFRelease(options);

float scaleFactor = screen.frame.size.width / nativeSize.width;