Search code examples
c++winapigdidpi

gdiScaling : issues with lines drawn with GDI


I have enabled the "gdiScaling" option in the manifest to enable the enhanced GDI scaling feature on my MFC C++ application.

I have however some issues with drawing lines with MoveTo/LineTo : drawing a simple rectangle have some "glitches" on the edges, that do not match anymore, like an off-by-one pixel because of the scaling.

Anyone have experienced this? How to fix it? (apart from changing API, for example Rectangle())

PS: gdiScalign is enabled as described in https://blogs.windows.com/windowsdeveloper/2017/05/19/improving-high-dpi-experience-gdi-based-desktop-apps/

SelectObject(hdc, CreatePen(PS_SOLID, 1, 0));

int x1 = 100;
int y1 = 100;
int x2 = 199;
int y2 = 199;
int ofs = 1;  // LineTo does not include last pixel!

MoveToEx(hdc, x1, y1, nullptr);
LineTo(hdc, x2 + ofs, y1);

MoveToEx(hdc, x1, y1, nullptr);
LineTo(hdc, x1, y2 + ofs);

MoveToEx(hdc, x2, y1, nullptr);
LineTo(hdc, x2, y2 + ofs);

MoveToEx(hdc, x1, y2, nullptr);
LineTo(hdc, x2 + ofs, y2);

wrong rectangle


Solution

  • Forget the manifest thing I suggested in my previous answer.

    Thinking like how Windows deals with the "1 past" thing in its drawing APIs and how its intended so that developers shouldn't have to explicitly think about it. My hypothesis is that if you wrote the code to explicitly have each "MoveTo" coordinate pick up explicitly where it was drawn too for the subsequent line, it becomes a lot easier. That is, draw it like your pencil never gets picked up until the entire shape is drawn

    So instead of this:

    MoveToEx(hdc, x1, y1, nullptr);
    LineTo(hdc, x2 + ofs, y1);
    
    MoveToEx(hdc, x1, y1, nullptr);
    LineTo(hdc, x1, y2 + ofs);
    
    MoveToEx(hdc, x2, y1, nullptr);
    LineTo(hdc, x2, y2 + ofs);
    
    MoveToEx(hdc, x1, y2, nullptr);
    LineTo(hdc, x2 + ofs, y2);
    
    

    This:

    POINT points[] = { POINT{x1,y1}, POINT{x2,y1}, POINT{x2,y2}, POINT{x1,y2}, POINT{x1,y1} };
    
    for (size_t i = 1; i < sizeof(points)/sizeof(points[0]); i++)
    {
        MoveToEx(hdc, points[i - 1].x, points[i - 1].y, nullptr);
        LineTo(hdc, points[i].x, points[i].y);
    }
    

    And that might be a correct hypothesis. This is the end result with gdiscaling enabled.

    enter image description here

    There might be an aliasing issue where the loop closes, but it's technically complete and far better than the original version you had.

    enter image description here