使用 C/C++ 和 WinApi 精确绘制 1 像素宽度的线

Posted

技术标签:

【中文标题】使用 C/C++ 和 WinApi 精确绘制 1 像素宽度的线【英文标题】:Draw exactly 1 pixel width line using C/C++ and WinApi 【发布时间】:2016-06-17 13:58:36 【问题描述】:

我正在尝试使用 C/C++ 代码和 Win API 函数来画线。出于特定原因,我需要正好有 1 个像素的线宽,但我在屏幕上看到的完全不同。根据笔/画笔设置的方式,它可以是 2 或 3 像素宽的线条,而不是 1。 只是为了测试,我尝试绘制 3 像素线,结果得到 5 像素。 其他类型的绘图原语也是如此:圆角矩形、矩形、椭圆等。 有什么方法可以禁用绘制线周围的这种阴影边界? 绘图功能的代码示例如下。

void DisplayDrawings (HDC hDC, HWND hWnd)

 LOGBRUSH       stLogBrush; 
 RECT       rRect       = 0,0,0,0;
 HBITMAP        hBitmap     = NULL;
 HBRUSH     hBrush      = NULL;
 HGDIOBJ        hOldBrush       = NULL;  
 HGDIOBJ        hOldPen     = NULL;
 HPEN       hPen            = NULL;      
 BOOL       bB_Result       = FALSE;
 UINT       bBrushByBits [8];
 int            iRightPos       = 0;
 int            iLeftPos        = 30;
 int            iLineY      = 30;
 int            iDrawStep       = 10;   
 int            i           = 0;


#ifndef RGBA
    #define RGBA(r,g,b,a)        ((COLORREF)( (((DWORD)(BYTE)(a))<<24) |     RGB(r,g,b) ))
#endif
 SetBkMode (hDC, OPAQUE);
 bB_Result              = GetClientRect(hWnd, &rRect);
 if (bB_Result == FALSE)
    
     return;
    
 iRightPos              = rRect.right - 30 - 1;

 // Line 1. Draw 1 pixel Line using standard brush/pen. 

 hPen                   = CreatePen (PS_SOLID,1, RGB(255, 0,0));
 hOldPen                    = SelectObject (hDC, hPen);
 if (hOldPen == NULL)
    
     return;
    
 MoveToEx (hDC, iLeftPos,iLineY,NULL);
 LineTo (hDC,iRightPos,iLineY);
 SelectObject (hDC, hOldPen);
 DeleteObject (hPen);
 iLineY                 += iDrawStep;
 // Result - line with 2 pixels width   
 // Line 2. Draw 1 pixel Line using standard brush/pen, but setting pen width to "0".  CreatePen API says: "If nWidth is zero, the pen is a single pixel wide, regardless of the current transformation"
 hPen                   = CreatePen (PS_SOLID,0, RGB(255, 0,0));
 hOldPen                    = SelectObject (hDC, hPen);
 if (hOldPen == NULL)
    
     return;
    
 MoveToEx (hDC, iLeftPos,iLineY,NULL);
 LineTo (hDC,iRightPos,iLineY);
 SelectObject (hDC, hOldPen);
 DeleteObject (hPen);
 iLineY                 += iDrawStep;
 // Result - line with 3 pixels width
 // Line 3. Draw 1 pixel as above, but set current brush to NULL
 hOldBrush              = SelectObject (hDC, GetStockObject(NULL_BRUSH));
 if (hOldBrush == NULL)
    
     return;
    
 hPen                   = CreatePen (PS_SOLID,0, RGB(255, 0,0));
 hOldPen                    = SelectObject (hDC, hPen);
 if (hOldPen == NULL)
    
     return;
    
 MoveToEx (hDC, iLeftPos,iLineY,NULL);
 LineTo (hDC,iRightPos,iLineY);
 SelectObject (hDC, hOldPen);
 DeleteObject (hPen);
 SelectObject (hDC, hOldBrush);
 iLineY                 += iDrawStep;
 // Result - line with 2 pixels width
 // Line 4. Draw 3 pixel Line using standard brush/pen
 hPen                   = CreatePen (PS_SOLID,3, RGB(255, 0,0));
 hOldPen                    = SelectObject (hDC, hPen);
 if (hOldPen == NULL)
    
     return;
    
 MoveToEx (hDC, iLeftPos,iLineY,NULL);
 LineTo (hDC,iRightPos,iLineY);
 SelectObject (hDC, hOldPen);
 DeleteObject (hPen);
 iLineY                 += iDrawStep;
 // Result - line with 5 pixels width
  // Line 5. Draw 3 pixel as above, but set current brush to NULL
 hOldBrush              = SelectObject (hDC, GetStockObject(NULL_BRUSH));
 if (hOldBrush == NULL)
    
     return;
    
 hPen                   = CreatePen (PS_SOLID,3, RGB(255, 0,0));
 hOldPen                    = SelectObject (hDC, hPen);
 if (hOldPen == NULL)
    
     return;
    
 MoveToEx (hDC, iLeftPos,iLineY,NULL);
 LineTo (hDC,iRightPos,iLineY);
 SelectObject (hDC, hOldPen);
 DeleteObject (hPen);
 SelectObject (hDC, hOldBrush);
 iLineY                 += iDrawStep;
 // Result - line with 5 pixels width
 // Line 6. Draw 1 pixel line by creating own pen with monochrome 8x8 brush. Result is 3  
int k = 0;
 for(i = 0; i < 8; i++)
    
     bBrushByBits [i]       = 0x00;
    
 hBitmap                    = CreateBitmap(8, 8, 1, 1, (LPBYTE)bBrushByBits); 
 if (hBitmap == NULL)
    
     return;
    
 stLogBrush.lbColor         = RGBA(255,0,0,0);
 stLogBrush.lbHatch         = (ULONG_PTR) hBitmap; 
 stLogBrush.lbStyle         = BS_PATTERN;   

 hPen                   = ExtCreatePen (PS_GEOMETRIC, 3, &stLogBrush, 0, NULL); 
 hOldPen                    = SelectObject (hDC, hPen);
 if (hOldPen == NULL)
    
     return;
    
 MoveToEx (hDC, iLeftPos,iLineY,NULL);
 LineTo (hDC,iRightPos,iLineY);
 SelectObject (hDC, hOldPen);
 DeleteObject (hPen);
 DeleteObject (hBitmap);
 // Result - line with 2 pixels width


提前致谢

【问题讨论】:

CreatePen: "nWidth [in]: 笔的宽度,以逻辑单位为单位。如果 nWidth 为零,则无论当前的转变。” 在上面的代码中也是这样做的,但是宽度仍然是 3 或 2(如果画笔设置为 NULL_BRUSH)像素宽 如果一切都失败了,你可以把它弄成贫民窟,直接使用“SetPixel”。 msdn.microsoft.com/en-us/library/windows/desktop/… 【参考方案1】:

tl;dr:DPI 意识。

Windows 会为高 DPI 显示器放大图形,除非您明确告诉它您的程序了解如何使用实际 DPI。

您可以在应用程序的早期通过清单或系统调用来执行此操作。 API 的原始版本是 SetProcessDPIAware。较新的版本是 SetProcessDPIAwareness,即使在显示器具有不同 DPI 的多显示器系统上,它也可以让您告诉 Windows 您知道自己在做什么。

【讨论】:

以上是关于使用 C/C++ 和 WinApi 精确绘制 1 像素宽度的线的主要内容,如果未能解决你的问题,请参考以下文章

winapi - 如何正确使用 LayeredWindows

如何在 WinAPI 中将 TrueType 字体绘制到像素数组中?

如何在 C/C++ 中获得大数的精确二进制表示?

C++ Winapi Owner 绘制的列表框动画

Ownerdrawn托盘图标(winapi)?

C/C++实现生产者消费者模式