Skip to content

Commit 2e29dd4

Browse files
committed
Merge remote-tracking branch 'origin/ISSUE-26' into ISSUE-21
2 parents 953795f + 1b436e0 commit 2e29dd4

File tree

3 files changed

+187
-40
lines changed

3 files changed

+187
-40
lines changed

win32/N3888_RefImpl/drawing.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,7 @@ namespace std {
877877

878878
int format_stride_for_width(format format, int width);
879879
surface make_surface(surface::native_handle_type nh);
880+
surface make_surface(format format, int width, int height);
880881
path _Make_path(path::native_handle_type nh);
881882
}
882883
}

win32/N3888_RefImpl/entrypoint-win32.cpp

Lines changed: 182 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <memory>
1010
#include <functional>
11+
#include <atomic>
1112
#include <wrl.h>
1213
#include "throw_helpers.h"
1314
#include "drawing.h"
@@ -24,40 +25,70 @@ using namespace std::experimental::drawing;
2425

2526
#define MAX_LOADSTRING 100
2627

28+
class ref_counted_bool {
29+
::std::atomic<int> m_refCount;
30+
public:
31+
ref_counted_bool() : m_refCount(0) {}
32+
ref_counted_bool& operator=(bool value) {
33+
if (value) {
34+
m_refCount++;
35+
}
36+
else {
37+
auto count = m_refCount.load(memory_order_acquire);
38+
if (--count < 0) {
39+
count = 0;
40+
}
41+
m_refCount.store(count, memory_order_release);
42+
}
43+
return *this;
44+
}
45+
operator bool() {
46+
return m_refCount.load() != 0;
47+
}
48+
};
49+
2750
// Global Variables:
2851
HINSTANCE hInst; // current instance
2952
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
3053
TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
3154
unique_ptr<surface> g_psurface;
55+
RECT g_previousClientRect;
56+
ref_counted_bool g_doNotPaint;
3257

3358
// Everything in the Draw function should be portable C++ code.
3459
void Draw(surface& surface) {
3560
// Create a context that will draw to our surface.
3661
auto context = drawing::context(surface);
3762

3863
// The is a demonstration of how a raster_source_pattern works. We create one that is 100px x 100px
39-
auto pattern = raster_source_pattern(nullptr, content::color_alpha, 100, 100);
40-
pattern.set_acquire(
41-
[pattern](void*, experimental::drawing::surface& target, const rectangle_int& extents) -> experimental::drawing::surface
42-
{
43-
auto result = experimental::drawing::image_surface(target, format::rgb24, extents.width - extents.x, extents.height - extents.y);
44-
vector<unsigned char> data;
45-
const auto dataSize = result.get_stride() * result.get_height();
46-
data.resize(dataSize);
47-
for (auto i = 0; i < dataSize; i += 4) {
48-
data[i + 0] = 255ui8;
49-
data[i + 1] = 0ui8;
50-
data[i + 2] = 0ui8;
51-
data[i + 3] = 0ui8;
52-
}
53-
result.set_data(data);
54-
return result;
55-
},
56-
nullptr
57-
);
58-
pattern.set_extend(extend::repeat);
59-
context.set_source(pattern);
64+
//auto pattern = raster_source_pattern(nullptr, content::color_alpha, 100, 100);
65+
//pattern.set_acquire(
66+
// [pattern](void*, experimental::drawing::surface& target, const rectangle_int& extents) -> experimental::drawing::surface
67+
//{
68+
// auto result = experimental::drawing::image_surface(target, format::rgb24, extents.width - extents.x, extents.height - extents.y);
69+
// vector<unsigned char> data;
70+
// const auto dataSize = result.get_stride() * result.get_height();
71+
// data.resize(dataSize);
72+
// for (auto i = 0; i < dataSize; i += 4) {
73+
// data[i + 0] = 255ui8;
74+
// data[i + 1] = 0ui8;
75+
// data[i + 2] = 0ui8;
76+
// data[i + 3] = 0ui8;
77+
// }
78+
// result.set_data(data);
79+
// return result;
80+
//},
81+
// nullptr
82+
// );
83+
//pattern.set_extend(extend::repeat);
84+
//context.set_source(pattern);
85+
//context.paint();
86+
87+
context.save();
88+
auto scp = solid_color_pattern(0.0, 0.0, 1.0);
89+
context.set_source(scp);
6090
context.paint();
91+
context.restore();
6192

6293
context.save();
6394
const int width = 100;
@@ -98,6 +129,43 @@ void Draw(surface& surface) {
98129
imageSurfaceFromData.finish();
99130
context.restore();
100131

132+
context.save();
133+
auto surfaceForContext2 = image_surface(format::argb32, 100, 100);
134+
auto context2 = drawing::context(surfaceForContext2);
135+
context2.set_source_rgba(0.5, 0.5, 0.5, 0.5);
136+
context2.set_compositing_operator(compositing_operator::clear);
137+
context2.paint_with_alpha(0.0);
138+
context2.set_source_rgba(0.5, 0.5, 0.5, 0.5);
139+
context2.set_compositing_operator(compositing_operator::add);
140+
context2.paint();
141+
surfaceForContext2.flush();
142+
context.set_source_surface(surfaceForContext2, 600.0, 400.0);
143+
context.move_to(600.0, 400.0);
144+
context.rel_line_to(100.0, 0.0);
145+
context.rel_line_to(0.0, 100.0);
146+
context.rel_line_to(-100.0, 0.0);
147+
context.close_path();
148+
context.fill();
149+
context.set_source_surface(surfaceForContext2, 650.0, 400.0);
150+
context.move_to(650.0, 400.0);
151+
context.rel_line_to(100.0, 0.0);
152+
context.rel_line_to(0.0, 100.0);
153+
context.rel_line_to(-100.0, 0.0);
154+
context.close_path();
155+
context.fill();
156+
context.restore();
157+
158+
context.save();
159+
auto subsurface = drawing::surface(surface, 10.5, 11.0, 50.0, 50.0);
160+
auto subcontext = drawing::context(subsurface);
161+
subcontext.set_source_rgb(0.0, 0.0, 0.0);
162+
subcontext.move_to(2.0, 2.0);
163+
subcontext.rel_line_to(48.0, 0.0);
164+
subcontext.set_line_width(3.0);
165+
subcontext.set_line_cap(line_cap::butt);
166+
subcontext.stroke();
167+
context.restore();
168+
101169
context.save();
102170
matrix m;
103171
m.init_translate(300.0, 400.0);
@@ -129,8 +197,6 @@ void Draw(surface& surface) {
129197
context.select_font_face("Segoe UI", font_slant::normal, font_weight::normal);
130198
context.set_font_size(30.0);
131199
context.show_text("Hello C++!");
132-
133-
//surface.finish();
134200
}
135201

136202
void OnPaint(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
@@ -140,23 +206,51 @@ void OnPaint(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
140206

141207
PAINTSTRUCT ps;
142208
HDC hdc;
209+
RECT updateRect{ };
210+
auto getUpdateRectResult = GetUpdateRect(hWnd, &updateRect, FALSE);
211+
212+
// There's a bug somewhere (possibly cairo?) where if you run this code without the check to make sure that
213+
// updateRect.left and .top are both 0, it crashes and does so in the cairo DLL such that it's immune to
214+
// being caught. Specifically in a Win32/Debug config it throws well inside cairo on the ctxt.paint() call
215+
// on an illegal memory access (actually in pixman-sse2.c in a call to "void save_128_aligned(__m128i*, __m128i);").
216+
if (getUpdateRectResult != FALSE && updateRect.left == 0 && updateRect.top == 0) {
217+
hdc = BeginPaint(hWnd, &ps);
218+
219+
RECT clientRect;
220+
if (!GetClientRect(hWnd, &clientRect)) {
221+
throw_get_last_error<logic_error>("Failed GetClientRect call.");
222+
}
223+
auto width = clientRect.right - clientRect.left;
224+
auto height = clientRect.bottom - clientRect.top;
225+
auto previousWidth = g_previousClientRect.right - g_previousClientRect.left;
226+
auto previousHeight = g_previousClientRect.bottom - g_previousClientRect.top;
227+
228+
// To enable screenshot saving, we are using a global unique_ptr surface. I did not rewrite the boilerplate
229+
// Win32 code so that it'd be a class, hence the globals.
230+
if ((g_psurface == nullptr) || (width != previousWidth) || (height != previousHeight)) {
231+
g_psurface = unique_ptr<surface>(new surface(move(make_surface(format::argb32, width, height))));
232+
g_previousClientRect = clientRect;
233+
}
143234

144-
hdc = BeginPaint(hWnd, &ps);
145-
146-
// To enable screenshot saving, we are using a global unique_ptr surface. I did not rewrite the boilerplate
147-
// Win32 code so that it'd be a class, hence the globals. Note that this would not work without using CS_OWNDC
148-
// when registering the window class since we could get a different HDC each time without that flag.
149-
if (g_psurface == nullptr) {
150-
g_psurface = unique_ptr<surface>(new surface(move(make_surface(cairo_win32_surface_create(hdc)))));
151-
}
152-
153-
// Draw to the off-screen buffer.
154-
Draw(*g_psurface);
235+
// Draw to the off-screen buffer.
236+
Draw(*g_psurface);
155237

156-
// Flush to ensure that it is drawn to the window.
157-
g_psurface->flush();
238+
// Flush to ensure that it is drawn to the window.
239+
g_psurface->flush();
158240

159-
EndPaint(hWnd, &ps);
241+
auto surface = make_surface(cairo_win32_surface_create(hdc));
242+
auto ctxt = context(surface);
243+
ctxt.set_source_surface(*g_psurface, 0.0, 0.0);
244+
ctxt.paint();
245+
surface.flush();
246+
EndPaint(hWnd, &ps);
247+
}
248+
else {
249+
if (getUpdateRectResult != FALSE) {
250+
hdc = BeginPaint(hWnd, &ps);
251+
EndPaint(hWnd, &ps);
252+
}
253+
}
160254
}
161255

162256
int WINAPI wWinMain(
@@ -167,6 +261,7 @@ int WINAPI wWinMain(
167261
) {
168262
UNREFERENCED_PARAMETER(hPrevInstance);
169263
UNREFERENCED_PARAMETER(lpCmdLine);
264+
g_previousClientRect = { }; // Zero out previous client rect.
170265
throw_if_failed_hresult<runtime_error>(
171266
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED), "Failed call to CoInitializeEx."
172267
);
@@ -195,7 +290,38 @@ int WINAPI wWinMain(
195290
while (msg.message != WM_QUIT) {
196291
if (!PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
197292
// No message so redraw the window.
198-
RedrawWindow(hWnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
293+
if (g_psurface != nullptr && !g_doNotPaint) {
294+
RECT clientRect;
295+
if (!GetClientRect(hWnd, &clientRect)) {
296+
throw_get_last_error<logic_error>("Failed GetClientRect call.");
297+
}
298+
auto width = clientRect.right - clientRect.left;
299+
auto height = clientRect.bottom - clientRect.top;
300+
auto previousWidth = g_previousClientRect.right - g_previousClientRect.left;
301+
auto previousHeight = g_previousClientRect.bottom - g_previousClientRect.top;
302+
303+
// To enable screenshot saving, we are using a global unique_ptr surface. I did not rewrite the boilerplate
304+
// Win32 code so that it'd be a class, hence the globals.
305+
if ((width != previousWidth) || (height != previousHeight)) {
306+
g_psurface = unique_ptr<surface>(new surface(move(make_surface(format::argb32, width, height))));
307+
g_previousClientRect = clientRect;
308+
}
309+
310+
// Draw to the off-screen buffer.
311+
Draw(*g_psurface);
312+
313+
// Flush to ensure that it is drawn to the window.
314+
g_psurface->flush();
315+
auto hdc = GetDC(hWnd);
316+
{
317+
auto surface = make_surface(cairo_win32_surface_create(hdc));
318+
auto ctxt = context(surface);
319+
ctxt.set_source_surface(*g_psurface, 0.0, 0.0);
320+
ctxt.paint();
321+
surface.flush();
322+
}
323+
ReleaseDC(hWnd, hdc);
324+
}
199325
}
200326
else {
201327
if (msg.message != WM_QUIT) {
@@ -220,7 +346,7 @@ ATOM MyRegisterClass(HINSTANCE hInstance) {
220346

221347
wcex.cbSize = sizeof(WNDCLASSEX);
222348

223-
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
349+
wcex.style = 0;
224350
wcex.lpfnWndProc = WndProc;
225351
wcex.cbClsExtra = 0;
226352
wcex.cbWndExtra = 0;
@@ -373,6 +499,14 @@ void ShowSaveAsPNGDialog() {
373499
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
374500
int wmId, wmEvent;
375501

502+
#if defined(DEBUG_WNDPROC)
503+
wstringstream str;
504+
str << L"Message: 0x" << hex << uppercase << message << nouppercase
505+
<< L". WPARAM: 0x" << hex << uppercase << static_cast<UINT>(wParam) << nouppercase
506+
<< L". LPARAM: 0x" << hex << uppercase << static_cast<UINT>(lParam) << endl;
507+
OutputDebugStringW(str.str().c_str());
508+
#endif
509+
376510
switch (message) {
377511
case WM_COMMAND:
378512
wmId = LOWORD(wParam);
@@ -397,8 +531,16 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
397531
return DefWindowProc(hWnd, message, wParam, lParam);
398532
}
399533
break;
534+
case WM_ENTERSIZEMOVE:
535+
g_doNotPaint = true; // Don't paint while resizing to avoid flicker.
536+
return DefWindowProc(hWnd, message, wParam, lParam);
537+
case WM_EXITSIZEMOVE:
538+
g_doNotPaint = false;
539+
return DefWindowProc(hWnd, message, wParam, lParam);
400540
case WM_PAINT:
401-
OnPaint(hWnd, message, wParam, lParam);
541+
if (!g_doNotPaint) {
542+
OnPaint(hWnd, message, wParam, lParam);
543+
}
402544
break;
403545
case WM_DESTROY:
404546
PostQuitMessage(0);

win32/N3888_RefImpl/standalone_functions.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ namespace std {
3030
return surface(nh);
3131
}
3232

33+
surface make_surface(format fmt, int width, int height) {
34+
return image_surface(fmt, width, height);
35+
}
36+
3337
int format_stride_for_width(format format, int width) {
3438
return cairo_format_stride_for_width(_Format_to_cairo_format_t(format), width);
3539
}

0 commit comments

Comments
 (0)