Trying to convert between font point sizes and pixels in C++, but getting disagreement between Qt and GLFW for scaled device resolution

Viewed 43

I have been using the following function to convert between pixel sizes and font sizes for my Qt application:

int FontFace::pointToPixelSize(float pointSize)
{
    // Points are a physical unit, where 1 point equals 1/72th of an inch in digital typography
    // Resolution is in DPI
    float resolution = QGuiApplication::primaryScreen()->logicalDotsPerInch();
    float resolution = Screen::PrimaryScreen()->logicalDotsPerInch();
    int pixelSize = int(pointSize * resolution / 72.0);
    return pixelSize;
}

float FontFace::pixelToPointSize(uint32_t pixelSize)
{
    float resolution = QGuiApplication::primaryScreen()->logicalDotsPerInch();
    float resolution = Screen::PrimaryScreen()->logicalDotsPerInch();
    float pointSize = pixelSize * 72.0 / resolution;
    return pointSize;
}

I now need to replicate this functionality using GLFW instead of Qt. However, I haven't been able to find any useful information online regarding what logicalDotsPerInch actually is beyond that "This value can be used to convert font point sizes to pixel sizes." So, I assumed that this value was the physical DPI of the monitor, divided by the content scaling of the monitor.

So, this was my best shot at getting that:

int widthMm, heightMm;
float xScale, yScale;

// Get physical monitor size
glfwGetMonitorPhysicalSize(glfwGetPrimaryMonitor(), widthMm, heightMm);

// Get current desktop resolution
const GLFWvidmode* videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
int widthRes = videoMode->width;
int heightRes = videoMode->height;

// Get monitor content scale
glfwGetMonitorContentScale(glfwGetPrimaryMonitor(), xScale, yScale);

// Get DPI
float mmToInches = 0.0393701;
float dpiX = double(widthRes) / (widthMm *  mmToInches );
float dpiY = double(heightRes) / (heightMm * mmToInches );

// Get logical DPI
float logicalDpiX = dpiX / xScale;
float logicalDpiY = dpiY / yScale;
float logicalDpi = 0.5 * (logicalDpiX + logicalDpiY);

Unfortunately, this gives me a very different answer than the Qt result. Qt gives me a logical DPI of 168, which is actually higher than the physical DPI of ~140. This makes no sense to me. The logicalDpi that I calculated using GLFW is 79.96, so ~80. These are very different numbers, with no obvious relationship between them. I did find this thread inquiring about the Qt functions, but it wasn't super insightful.

I'll note that both Qt and GLFW give me equivalent physical DPI calculations of ~140 for my monitor, so the missing piece seems to be that the concept of logicalDotsPerInch is a black box. If this is truly the correct number to use, then I'm at a total loss, since I can't think of any sensible way to obtain this result.

Edit: Digging into this more, I think I've found how Qt gets their calculation. A typical DPI for a device is 96. Dividing this value by my logical DPI gives me a factor of 1.200445. Multiplying this factor by the physical DPI of the screen gives 168, which matches the logical DPI returned by Qt.

Working back on the math from this, the value of 168 is actually just equal to the typical DPI of a monitor (96.0), multiplied by the content scale of my monitor, which is 1.75.

So, I've reverse-engineered the solution, but I can't wrap my head around what it means physically. Why is Qt's solution correct? Shouldn't the logical DPI be the ACTUAL DPI of the monitor DIVIDED by the content scaling? This doesn't make sense to me.

The only reasonable answer is that I'm misunderstanding how font size calculations work. If font sizes are always treated with a 96 DPI baseline, and then the content scaling of the monitor is applied to offset any physical size differences, then this result actually makes some sense, and most of my work is superfluous. I still don't understand why you wouldn't need to use the actual device DPI though.

0 Answers
Related