aboutsummaryrefslogtreecommitdiff
path: root/src/libui_sdl/libui/windows/areadraw.cpp
blob: a9ad477256b959b0ce452c7f474a82d3eeee3f8a (plain)
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// 8 september 2015
#include "uipriv_windows.hpp"
#include "area.hpp"

static HRESULT doPaint(uiArea *a, ID2D1RenderTarget *rt, RECT *clip)
{
	uiAreaHandler *ah = a->ah;
	uiAreaDrawParams dp;
	COLORREF bgcolorref;
	D2D1_COLOR_F bgcolor;
	D2D1_MATRIX_3X2_F scrollTransform;

	// no need to save or restore the graphics state to reset transformations;  it's handled by resetTarget() in draw.c, called during the following
	dp.Context = newContext(rt);

	loadAreaSize(a, rt, &(dp.AreaWidth), &(dp.AreaHeight));

	dp.ClipX = clip->left;
	dp.ClipY = clip->top;
	dp.ClipWidth = clip->right - clip->left;
	dp.ClipHeight = clip->bottom - clip->top;
	if (a->scrolling) {
		dp.ClipX += a->hscrollpos;
		dp.ClipY += a->vscrollpos;
	}

	rt->BeginDraw();

	{
        float dpi_x, dpi_y;
        D2D1_MATRIX_3X2_F dm;
        rt->GetDpi(&dpi_x, &dpi_y);
        ZeroMemory(&dm, sizeof (D2D1_MATRIX_3X2_F));
        dm._11 = 96.f/dpi_x;
        dm._22 = 96.f/dpi_y;
        rt->SetTransform(&dm);
	}

	if (a->scrolling) {
		ZeroMemory(&scrollTransform, sizeof (D2D1_MATRIX_3X2_F));
		scrollTransform._11 = 1;
		scrollTransform._22 = 1;
		// negative because we want nonzero scroll positions to move the drawing area up/left
		scrollTransform._31 = -a->hscrollpos;
		scrollTransform._32 = -a->vscrollpos;
		rt->SetTransform(&scrollTransform);
	}

	// TODO push axis aligned clip

	// TODO only clear the clip area
	// TODO clear with actual background brush
	if (a->bgR != -1)
    {
        bgcolor.r = ((float) a->bgR) / 255.0;
        bgcolor.g = ((float) a->bgG) / 255.0;
        bgcolor.b = ((float) a->bgB) / 255.0;
        bgcolor.a = 1.0;
    }
    else
    {
        bgcolorref = GetSysColor(COLOR_BTNFACE);
        bgcolor.r = ((float) GetRValue(bgcolorref)) / 255.0;
        // due to utter apathy on Microsoft's part, GetGValue() does not work with MSVC's Run-Time Error Checks
        // it has not worked since 2008 and they have *never* fixed it
        // TODO now that -RTCc has just been deprecated entirely, should we switch back?
        bgcolor.g = ((float) ((BYTE) ((bgcolorref & 0xFF00) >> 8))) / 255.0;
        bgcolor.b = ((float) GetBValue(bgcolorref)) / 255.0;
        bgcolor.a = 1.0;
    }
	rt->Clear(&bgcolor);

	(*(ah->Draw))(ah, a, &dp);

	freeContext(dp.Context);

	// TODO pop axis aligned clip

	return rt->EndDraw(NULL, NULL);
}

static void onWM_PAINT(uiArea *a)
{
	RECT clip;
	HRESULT hr;

	// do not clear the update rect; we do that ourselves in doPaint()
	if (GetUpdateRect(a->hwnd, &clip, FALSE) == 0) {
		// set a zero clip rect just in case GetUpdateRect() didn't change clip
		clip.left = 0;
		clip.top = 0;
		clip.right = 0;
		clip.bottom = 0;
	}
	hr = doPaint(a, a->rt, &clip);
	switch (hr) {
	case S_OK:
		if (ValidateRect(a->hwnd, NULL) == 0)
			logLastError(L"error validating rect");
		break;
	case D2DERR_RECREATE_TARGET:
		// DON'T validate the rect
		// instead, simply drop the render target
		// we'll get another WM_PAINT and make the render target again
		// TODO would this require us to invalidate the entire client area?
		a->rt->Release();;
		a->rt = NULL;
		break;
	default:
		logHRESULT(L"error painting", hr);
	}
}

static void onWM_PRINTCLIENT(uiArea *a, HDC dc)
{
	ID2D1DCRenderTarget *rt;
	RECT client;
	HRESULT hr;

	uiWindowsEnsureGetClientRect(a->hwnd, &client);
	rt = makeHDCRenderTarget(dc, &client);
	hr = doPaint(a, rt, &client);
	if (hr != S_OK)
		logHRESULT(L"error printing uiArea client area", hr);
	rt->Release();
}

BOOL areaDoDraw(uiArea *a, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
{
	switch (uMsg) {
	case WM_PAINT:
		onWM_PAINT(a);
		*lResult = 0;
		return TRUE;
	case WM_PRINTCLIENT:
		onWM_PRINTCLIENT(a, (HDC) wParam);
		*lResult = 0;
		return TRUE;
	}
	return FALSE;
}

// TODO only if the render target wasn't just created?
void areaDrawOnResize(uiArea *a, RECT *newClient)
{
	D2D1_SIZE_U size;

	size.width = newClient->right - newClient->left;
	size.height = newClient->bottom - newClient->top;
	// don't track the error; we'll get that in EndDraw()
	// see https://msdn.microsoft.com/en-us/library/windows/desktop/dd370994%28v=vs.85%29.aspx
	a->rt->Resize(&size);

	// according to Rick Brewster, we must always redraw the entire client area after calling ID2D1RenderTarget::Resize() (see http://stackoverflow.com/a/33222983/3408572)
	// we used to have a uiAreaHandler.RedrawOnResize() method to decide this; now you know why we don't anymore
	invalidateRect(a->hwnd, NULL, TRUE);
}