Windows DPI 렌더링 이해하기

이 문서에서는 Windows 환경에서의 DPI 렌더링 과정을 상세하게 설명합니다. 다음과 같은 구조로 내용을 전달하겠습니다: "프로세스 흐름 + 개념 분석 + WinForms 예제 코드".

1. 핵심 개념 이해

===============

Windows 환경에서는 세 가지 종류의 좌표가 존재합니다:

물리 픽셀 (Physical Pixel)
논리 픽셀 (Logical Pixel)
장치 독립 픽셀 (DIP, Device Independent Pixel)

기본 표준은 다음과 같습니다:

96 DPI = 100% 확대
1 DIP = 1 논리 픽셀 = 1/96 인치

화면 확대율이 150%일 경우:

DPI = 144
1 DIP = 1.5 물리 픽셀

2. DPI 렌더링 프로세스

===============

시나리오 설정


컴퓨터 A:

  • 해상도: 1920×1080
  • 확대율: 100%
  • DPI: 96

컴퓨터 B:

  • 해상도: 3840×2160
  • 확대율: 150%
  • DPI: 144

DPI 렌더링 흐름도 (전체 경로)

================

┌───────────────────────────────────────┐
│         1️⃣ 애플리케이션 시작          │
└───────────────────────────────────────┘
                 │
                 ▼
┌───────────────────────────────────────┐
│ 2️⃣ Windows는 DPI 인식 수준을 조회     │
│    - Unaware                         │
│    - System Aware                    │
│    - PerMonitor                      │
│    - PerMonitorV2                    │
└───────────────────────────────────────┘
                 │
                 ▼
┌───────────────────────────────────────┐
│ 3️⃣ 현재 모니터의 DPI를 가져옴        │
│    예:                              │
│    100% → 96 DPI                     │
│    150% → 144 DPI                    │
└───────────────────────────────────────┘
                 │
                 ▼
┌───────────────────────────────────────┐
│ 4️⃣ UI 배치 단계                      │
│                                       │
│ 사용하는 레이아웃에 따라:              │
│  - 픽셀 기반 → 96 기준 계산           │
│  - 논리 기반 → DPI 기준 계산          │
└───────────────────────────────────────┘
                 │
                 ▼
┌───────────────────────────────────────┐
│ 5️⃣ GDI / Direct2D / WPF 렌더링       │
│                                       │
│ 논리 좌표를 물리 픽셀로 변환           │
│                                       │
│ 공식:                               │
│ physical = logical × (DPI / 96)      │
└───────────────────────────────────────┘
                 │
                 ▼
┌───────────────────────────────────────┐
│ 6️⃣ Windows는 가상화 스케일링 필요 여부 확인 │
│                                       │
│ DPI Unaware인 경우:                  │
│ → 전체 창을 bitmap으로 확대           │
│                                       │
│ DPI Aware인 경우:                   │
│ → 확대 없이 실제 렌더링               │
└───────────────────────────────────────┘
                 │
                 ▼
┌───────────────────────────────────────┐
│ 7️⃣ 최종 출력: 그래픽 카드             │
└───────────────────────────────────────┘

3. 가장 중요한 분기점: DPI 인식 수준

=================

이 부분은 인터페이스가 왜곡되는지 결정하는 핵심 요소입니다.

DPI Unaware (기존 WinForms 기본값)


프로세스:

프로그램은 96 DPI 기준으로 계산
↓
Windows는 전체 창을 캡처하고
↓
전체적으로 150% 확대
↓
출력

결과:

  • 흐릿한 이미지
  • 글씨가 불명확
  • 레이아웃 오류
  • 잘림 현상

이것이 문제의 원인입니다.

System DPI Aware


프로그램 시작 시 주 모니터의 DPI를 읽음
↓
해당 DPI로 렌더링
↓
다른 모니터로 이동 시 재계산하지 않음

문제:

  • 멀티 모니터 환경에서 오류 발생

PerMonitorV2 (권장)


창이 새 모니터로 이동
↓
WM_DPICHANGED 메시지 수신
↓
레이아웃 재설정
↓
재렌더링

이것이 현대적인 올바른 방법입니다.

4. WinForms의 내부 렌더링 경로

==================

Control
  ↓
GDI+
  ↓
USER32
  ↓
DWM (Desktop Window Manager)
  ↓
GPU

DPI Unaware인 경우:

DWM이 비트맵 스케일링을 수행합니다.

5. WinForms에서 올바른 DPI 설정 방법

======================

Program.cs

using System;
using System.Windows.Forms;

internal static class Program
{
    [STAThread]
    static void Main()
    {
        // PerMonitorV2 DPI 지원 활성화
        Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        Application.Run(new MainForm());
    }
}

Form 생성자

public MainForm()
{
    InitializeComponent();

    // DPI를 기준으로 스케일링
    this.AutoScaleMode = AutoScaleMode.Dpi;
}

6. 실제 시스템 메시지 흐름 (PerMonitorV2)

========================

창을 4K 모니터로 드래그할 때:

WM_DPICHANGED
    │
    ├─ 새로운 DPI 값
    ├─ 제안된 새 창 크기
    │
    ▼
WinForms에서 Layout 트리거
    │
    ▼
Measure 다시 실행
    │
    ▼
Arrange 다시 실행
    │
    ▼
Paint 다시 실행

태그: Windows DPI WinForms GDI Direct2D

5월 22일 13:49에 게시됨