티스토리 뷰

프로그래밍/MFC

GDI, GDI+, Direct2D 비교

에어버스 2022. 9. 18. 19:20

GDI+ 기본 : https://petra.tistory.com/1729

 

GDI, GDI+ 는 더블버퍼링 적용했을때 이 코드에서는 속도차이를 모르겠고
다만, 원의 갯수를 200 에서 2000 으로 늘려서 해보니 Direct2D는 GDI, GDI+ 보다 CPU사용률이 적다.

1. DGI와 GDI plus 에서 화면 DC에 직접 그리므로 깜박임이 발생한다.
DGI 보다 GDI+ 가 빠르게 그려준다.

gdi_gdiPlus비교1.mp4
9.93MB

2. GDI plus 에서 Bitmap  메모리에 그린 다음 화면 DC에 Bitmap 을 직접 그리므로 깜박임이 없다.

gdi_gdiPlus비교2.mp4
16.88MB

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
int CGDI_Plus_View::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CWnd::OnCreate(lpCreateStruct) == -1)
        return -1;
 
    // TODO:  여기에 특수화된 작성 코드를 추가합니다.
    GdiplusStartupInput gpsi;
    if (GdiplusStartup(&m_token, &gpsi, NULL!= Ok) return 0;
    GetClientRect(m_rect);
    int w = m_rect.Width(), h = m_rect.Height();
    srand((unsigned int)time(NULL));
    mp_mem_image = new Bitmap(w, h);
    mp_mem_graphics = new Graphics(mp_mem_image);
    mp_brush = new SolidBrush(Color(255255255255));
    mp_mem_graphics->SetSmoothingMode(SmoothingModeAntiAlias);
    CircleDataPlus* p = m_list;
    for (int i = 0; i < MAX_GDI_PLUS_COUNT; i++, p++)
    {
        p->= rand() % w;
        p->= rand() % h;
        p->radius = rand() % 40 + 10;
        p->= 255;
        p->= rand() % 256;
        p->= rand() % 256;
        p->= rand() % 256;
    }
    SetTimer(110NULL);
    return 0;
}
 
void CGDI_Plus_View::ImageDarw(CDC* ap_dc)
{
    Graphics* p_graphics = new Graphics(ap_dc->m_hDC);
    mp_brush->SetColor(Color(255255255255));
    mp_mem_graphics->FillRectangle(mp_brush, m_rect.left, m_rect.top, m_rect.right, m_rect.bottom); // Bitmap 메모리에 그린다
    CircleDataPlus* p = m_list;
    for (int i = 0; i < MAX_GDI_PLUS_COUNT; i++, p++)
    {
        mp_brush->SetColor(Color(p->a, p->r, p->g, p->b));
        mp_mem_graphics->FillEllipse(mp_brush, p->- p->radius, p->- p->radius, p->radius << 1, p->radius << 1); // Bitmap 메모리에 그림
    }
    p_graphics->DrawImage(mp_mem_image, 00); // 이미지를 화면에 그림
    delete p_graphics;
}
 
void CGDI_Plus_View::NormalDraw(CDC* ap_dc)
{
    Graphics* p_graphics = new Graphics(ap_dc->m_hDC);
    p_graphics->SetSmoothingMode(SmoothingModeAntiAlias);
    mp_brush->SetColor(Color(255255255255));
    p_graphics->FillRectangle(mp_brush, m_rect.left, m_rect.top, m_rect.right, m_rect.bottom); // 화면에 직접 그려 깜박임
    CircleDataPlus* p = m_list;
    for (int i = 0; i < MAX_GDI_PLUS_COUNT; i++, p++)
    {
        mp_brush->SetColor(Color(p->a, p->r, p->g, p->b));        
        p_graphics->FillEllipse(mp_brush, p->- p->radius, p->- p->radius, p->radius << 1, p->radius << 1); // 화면에 직접 그려 깜박임
    }
    delete p_graphics;
}
 
void CGDI_Plus_View::OnPaint()
{
    CPaintDC dc(this); // device context for painting
    // TODO: 여기에 메시지 처리기 코드를 추가합니다.
    // 그리기 메시지에 대해서는 CWnd::OnPaint()을(를) 호출하지 마십시오.
    
    //NormalDraw(&dc); // DC에 직접 그려 깜박임 발생한다.
    ImageDarw(&dc); // Bitmap 에 그린 후 Bitmap 을 화면에 보여주고 있어 깜박임이 없다.
}
 
void CGDI_Plus_View::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
    if (1 == nIDEvent)
    {
        int w = m_rect.Width(), h = m_rect.Height();
        CircleDataPlus* p = m_list;
        for (int i = 0; i < MAX_GDI_PLUS_COUNT; i++, p++)
        {
            p->radius--;
            if (p->radius == 0)
            {
                p->= rand() % w;
                p->= rand() % h;
                p->radius = rand() % 40 + 10;
                p->= rand() % 256;
                p->= rand() % 256;
                p->= rand() % 256;
            }
        }
        Invalidate(0);
    } else
    CWnd::OnTimer(nIDEvent);
}
cs

 

GDI_PLUS2.zip
3.88MB

 

3. Direct2D 는 COM 객체를 이용하여 GPU에서 화면을 그리므로 속도가 빠르다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
using namespace D2D1;
 
#pragma comment(lib, "D2D1.lib"// Direct2D
 
int CD2D_View::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    ...
    // TODO:  여기에 특수화된 작성 코드를 추가합니다.
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); // COM 초기화
    D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &mp_factory); // COM 인터페이스 주소를 얻는다.
 
    CRect r;
    ::GetClientRect(m_hWnd, &r);
    m_rect = r;
    ID2D1HwndRenderTarget* p_hwnd_target; // Direct2D에서 윈도우의 클라이언트 영역에 그림을 그리는 객체
    // 지정한 윈도우의 클라이언트 영역에 그림을 그리기 위한 랜더 타겟을 생성한다.
    mp_factory->CreateHwndRenderTarget(RenderTargetProperties(),
        HwndRenderTargetProperties(m_hWnd, SizeU(r.right - r.left, r.bottom - r.top)), &p_hwnd_target);
    ...
}
 
void CD2D_View::OnPaint()
{
    CPaintDC dc(this); // device context for painting
    // TODO: 여기에 메시지 처리기 코드를 추가합니다.
    // 그리기 메시지에 대해서는 CDialogEx::OnPaint()을(를) 호출하지 마십시오.
 
    ::ValidateRect(m_hWnd, NULL); // WM_PAINT 플래그를 무효화 한다.
 
    Draw();
}
 
void CD2D_View::Draw()
{
    mp_target->BeginDraw();
    mp_target->Clear(ColorF(1.0f, 1.0f, 1.0f));
    CircleDataD2D* p = m_list;
 
    //D2D1_ELLIPSE pos;
    if (NULL != mp_brush)
    {
        for (int i = 0; i < MAX_D2D_COUNT; i++, p++)
        {
            //mp_brush->SetColor(D2D1_COLOR_F({ m_list[i].a, m_list[i].r, m_list[i].g, m_list[i].b })); // 반투명게 나온다.
            mp_brush->SetColor(ColorF(m_list[i].r, m_list[i].g, m_list[i].b)); // 브러시 색상을 바꾸고 불투명하게 나온다.
 
            //pos = D2D1_ELLIPSE({ { m_list[i].x, m_list[i].y }, m_list[i].radius, m_list[i].radius });            
            //mp_target->FillEllipse(pos, mp_brush);            
            mp_target->FillEllipse(D2D1_ELLIPSE({ { m_list[i].x, m_list[i].y }, m_list[i].radius, m_list[i].radius }), mp_brush);
 
            m_cnt++;
        }
    }
    mp_target->EndDraw(); // 그리기 작업을 종료하고 화면에 출력한다.
}
 
void CGDI_Plus_View::OnDestroy()
{
    ...
    // TODO: 여기에 메시지 처리기 코드를 추가합니다.
    KillTimer(1); KillTimer(2);
 
    delete mp_mem_graphics;
    delete mp_mem_image;
    delete mp_brush;
 
    GdiplusShutdown(m_token);
}
cs

 

D2D.zip
3.99MB

 

D2D.mp4
8.59MB

 

 

1. #pragma comment(lib, "D2D1.lib") 필요하다.
lib 없으면 링크 에러 난다.

2. Direct2D를 구성하는 각종 객체를 생성하는 Factory객체
ID2D1Factory* mp_factory; 

3. Direct2D에서 윈도우의 클라이언트 영역에 그리을 그릴 객체
ID2D1RenderTarget* mp_target; 

4. COM을 사용하기 위해 초기화 함수 호출
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

5. D2D를 사용하기 위한 팩토리를 생선한다.
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &mp_factory);

6. 윈도우 기반 렌터 타겟을 생성해서 주소를 가져올 포인터 선언
ID2D1HwndRenderTarget* p_hwnd_target;

7. 지정한 윈도우의 클라이언트 영역에 그림을 그리기 위한 Render Target을 생성한다.
RECT r;
GetClientRect(&r);
mp_factory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(m_hWnd, D2D1::SizeU(r.right - r.left, r.bottom - r.top)), &p_hwnd_target);

8. 부모 인터페이스로 주소를 저장한다.
mp_target = p_hwnd_target;

9. 사용하던 D2D 객체를 해제한다.
IRelease(&mp_target);
IRelease(&mp_factory);
CoUninitialize();

10. WM_PAINT 처리기인 OnPaint() 에서 그리기

void CDataListWnd::OnPaint()
{
   CPaintDC dc(this); // device context for painting

  ::ValidateRect(m_hWnd, NULL); // // WM_PAINT 플래그를 무효화한다.
   mp_target->BeginDraw();
   mp_target->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f)); // 바탕색을 검정색으로 지정
   mp_target->EndDraw(); // 그리기 작업을 종료하고 화면에 출력한다.
}

그러면 클라이언트 영역의 배경색을 검정색으로 그린다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28