88
99#include < memory>
1010#include < functional>
11+ #include < atomic>
1112#include < wrl.h>
1213#include " throw_helpers.h"
1314#include " drawing.h"
@@ -23,40 +24,70 @@ using namespace std::experimental::drawing;
2324
2425#define MAX_LOADSTRING 100
2526
27+ class ref_counted_bool {
28+ ::std::atomic<int > m_refCount;
29+ public:
30+ ref_counted_bool () : m_refCount(0 ) {}
31+ ref_counted_bool& operator =(bool value) {
32+ if (value) {
33+ m_refCount++;
34+ }
35+ else {
36+ auto count = m_refCount.load (memory_order_acquire);
37+ if (--count < 0 ) {
38+ count = 0 ;
39+ }
40+ m_refCount.store (count, memory_order_release);
41+ }
42+ return *this ;
43+ }
44+ operator bool () {
45+ return m_refCount.load () != 0 ;
46+ }
47+ };
48+
2649// Global Variables:
2750HINSTANCE hInst; // current instance
2851TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
2952TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
3053unique_ptr<surface> g_psurface;
54+ RECT g_previousClientRect;
55+ ref_counted_bool g_doNotPaint;
3156
3257// Everything in the Draw function should be portable C++ code.
3358void Draw (surface& surface) {
3459 // Create a context that will draw to our surface.
3560 auto context = drawing::context (surface);
3661
3762 // The is a demonstration of how a raster_source_pattern works. We create one that is 100px x 100px
38- auto pattern = raster_source_pattern (nullptr , content::color_alpha, 100 , 100 );
39- pattern.set_acquire (
40- [pattern](void *, experimental::drawing::surface& target, const rectangle_int& extents) -> experimental::drawing::surface
41- {
42- auto result = experimental::drawing::image_surface (target, format::rgb24, extents.width - extents.x , extents.height - extents.y );
43- vector<unsigned char > data;
44- const auto dataSize = result.get_stride () * result.get_height ();
45- data.resize (dataSize);
46- for (auto i = 0 ; i < dataSize; i += 4 ) {
47- data[i + 0 ] = 255ui8;
48- data[i + 1 ] = 0ui8;
49- data[i + 2 ] = 0ui8;
50- data[i + 3 ] = 0ui8;
51- }
52- result.set_data (data);
53- return result;
54- },
55- nullptr
56- );
57- pattern.set_extend (extend::repeat);
58- context.set_source (pattern);
63+ // auto pattern = raster_source_pattern(nullptr, content::color_alpha, 100, 100);
64+ // pattern.set_acquire(
65+ // [pattern](void*, experimental::drawing::surface& target, const rectangle_int& extents) -> experimental::drawing::surface
66+ // {
67+ // auto result = experimental::drawing::image_surface(target, format::rgb24, extents.width - extents.x, extents.height - extents.y);
68+ // vector<unsigned char> data;
69+ // const auto dataSize = result.get_stride() * result.get_height();
70+ // data.resize(dataSize);
71+ // for (auto i = 0; i < dataSize; i += 4) {
72+ // data[i + 0] = 255ui8;
73+ // data[i + 1] = 0ui8;
74+ // data[i + 2] = 0ui8;
75+ // data[i + 3] = 0ui8;
76+ // }
77+ // result.set_data(data);
78+ // return result;
79+ // },
80+ // nullptr
81+ // );
82+ // pattern.set_extend(extend::repeat);
83+ // context.set_source(pattern);
84+ // context.paint();
85+
86+ context.save ();
87+ auto scp = solid_color_pattern (0.0 , 0.0 , 1.0 );
88+ context.set_source (scp);
5989 context.paint ();
90+ context.restore ();
6091
6192 context.save ();
6293 const int width = 100 ;
@@ -97,6 +128,43 @@ void Draw(surface& surface) {
97128 imageSurfaceFromData.finish ();
98129 context.restore ();
99130
131+ context.save ();
132+ auto surfaceForContext2 = image_surface (format::argb32, 100 , 100 );
133+ auto context2 = drawing::context (surfaceForContext2);
134+ context2.set_source_rgba (0.5 , 0.5 , 0.5 , 0.5 );
135+ context2.set_compositing_operator (compositing_operator::clear);
136+ context2.paint_with_alpha (0.0 );
137+ context2.set_source_rgba (0.5 , 0.5 , 0.5 , 0.5 );
138+ context2.set_compositing_operator (compositing_operator::add);
139+ context2.paint ();
140+ surfaceForContext2.flush ();
141+ context.set_source_surface (surfaceForContext2, 600.0 , 400.0 );
142+ context.move_to (600.0 , 400.0 );
143+ context.rel_line_to (100.0 , 0.0 );
144+ context.rel_line_to (0.0 , 100.0 );
145+ context.rel_line_to (-100.0 , 0.0 );
146+ context.close_path ();
147+ context.fill ();
148+ context.set_source_surface (surfaceForContext2, 650.0 , 400.0 );
149+ context.move_to (650.0 , 400.0 );
150+ context.rel_line_to (100.0 , 0.0 );
151+ context.rel_line_to (0.0 , 100.0 );
152+ context.rel_line_to (-100.0 , 0.0 );
153+ context.close_path ();
154+ context.fill ();
155+ context.restore ();
156+
157+ context.save ();
158+ auto subsurface = drawing::surface (surface, 10.5 , 11.0 , 50.0 , 50.0 );
159+ auto subcontext = drawing::context (subsurface);
160+ subcontext.set_source_rgb (0.0 , 0.0 , 0.0 );
161+ subcontext.move_to (2.0 , 2.0 );
162+ subcontext.rel_line_to (48.0 , 0.0 );
163+ subcontext.set_line_width (3.0 );
164+ subcontext.set_line_cap (line_cap::butt);
165+ subcontext.stroke ();
166+ context.restore ();
167+
100168 context.save ();
101169 matrix m;
102170 m.init_translate (300.0 , 400.0 );
@@ -128,8 +196,6 @@ void Draw(surface& surface) {
128196 context.select_font_face (" Segoe UI" , font_slant::normal, font_weight::normal);
129197 context.set_font_size (30.0 );
130198 context.show_text (" Hello C++!" );
131-
132- // surface.finish();
133199}
134200
135201void OnPaint (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
@@ -139,23 +205,51 @@ void OnPaint(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
139205
140206 PAINTSTRUCT ps;
141207 HDC hdc;
208+ RECT updateRect{ };
209+ auto getUpdateRectResult = GetUpdateRect (hWnd, &updateRect, FALSE );
210+
211+ // There's a bug somewhere (possibly cairo?) where if you run this code without the check to make sure that
212+ // updateRect.left and .top are both 0, it crashes and does so in the cairo DLL such that it's immune to
213+ // being caught. Specifically in a Win32/Debug config it throws well inside cairo on the ctxt.paint() call
214+ // on an illegal memory access (actually in pixman-sse2.c in a call to "void save_128_aligned(__m128i*, __m128i);").
215+ if (getUpdateRectResult != FALSE && updateRect.left == 0 && updateRect.top == 0 ) {
216+ hdc = BeginPaint (hWnd, &ps);
217+
218+ RECT clientRect;
219+ if (!GetClientRect (hWnd, &clientRect)) {
220+ throw_get_last_error<logic_error>(" Failed GetClientRect call." );
221+ }
222+ auto width = clientRect.right - clientRect.left ;
223+ auto height = clientRect.bottom - clientRect.top ;
224+ auto previousWidth = g_previousClientRect.right - g_previousClientRect.left ;
225+ auto previousHeight = g_previousClientRect.bottom - g_previousClientRect.top ;
226+
227+ // To enable screenshot saving, we are using a global unique_ptr surface. I did not rewrite the boilerplate
228+ // Win32 code so that it'd be a class, hence the globals.
229+ if ((g_psurface == nullptr ) || (width != previousWidth) || (height != previousHeight)) {
230+ g_psurface = unique_ptr<surface>(new surface (move (make_surface (format::argb32, width, height))));
231+ g_previousClientRect = clientRect;
232+ }
142233
143- hdc = BeginPaint (hWnd, &ps);
144-
145- // To enable screenshot saving, we are using a global unique_ptr surface. I did not rewrite the boilerplate
146- // Win32 code so that it'd be a class, hence the globals. Note that this would not work without using CS_OWNDC
147- // when registering the window class since we could get a different HDC each time without that flag.
148- if (g_psurface == nullptr ) {
149- g_psurface = unique_ptr<surface>(new surface (move (make_surface (cairo_win32_surface_create (hdc)))));
150- }
151-
152- // Draw to the off-screen buffer.
153- Draw (*g_psurface);
234+ // Draw to the off-screen buffer.
235+ Draw (*g_psurface);
154236
155- // Flush to ensure that it is drawn to the window.
156- g_psurface->flush ();
237+ // Flush to ensure that it is drawn to the window.
238+ g_psurface->flush ();
157239
158- EndPaint (hWnd, &ps);
240+ auto surface = make_surface (cairo_win32_surface_create (hdc));
241+ auto ctxt = context (surface);
242+ ctxt.set_source_surface (*g_psurface, 0.0 , 0.0 );
243+ ctxt.paint ();
244+ surface.flush ();
245+ EndPaint (hWnd, &ps);
246+ }
247+ else {
248+ if (getUpdateRectResult != FALSE ) {
249+ hdc = BeginPaint (hWnd, &ps);
250+ EndPaint (hWnd, &ps);
251+ }
252+ }
159253}
160254
161255int WINAPI wWinMain (
@@ -166,6 +260,7 @@ int WINAPI wWinMain(
166260 ) {
167261 UNREFERENCED_PARAMETER (hPrevInstance);
168262 UNREFERENCED_PARAMETER (lpCmdLine);
263+ g_previousClientRect = { }; // Zero out previous client rect.
169264 throw_if_failed_hresult<runtime_error>(
170265 CoInitializeEx (nullptr , COINIT_APARTMENTTHREADED), " Failed call to CoInitializeEx."
171266 );
@@ -191,7 +286,38 @@ int WINAPI wWinMain(
191286 while (msg.message != WM_QUIT) {
192287 if (!PeekMessageW (&msg, NULL , 0 , 0 , PM_REMOVE)) {
193288 // No message so redraw the window.
194- RedrawWindow (hWnd, nullptr , nullptr , RDW_INVALIDATE | RDW_UPDATENOW);
289+ if (g_psurface != nullptr && !g_doNotPaint) {
290+ RECT clientRect;
291+ if (!GetClientRect (hWnd, &clientRect)) {
292+ throw_get_last_error<logic_error>(" Failed GetClientRect call." );
293+ }
294+ auto width = clientRect.right - clientRect.left ;
295+ auto height = clientRect.bottom - clientRect.top ;
296+ auto previousWidth = g_previousClientRect.right - g_previousClientRect.left ;
297+ auto previousHeight = g_previousClientRect.bottom - g_previousClientRect.top ;
298+
299+ // To enable screenshot saving, we are using a global unique_ptr surface. I did not rewrite the boilerplate
300+ // Win32 code so that it'd be a class, hence the globals.
301+ if ((width != previousWidth) || (height != previousHeight)) {
302+ g_psurface = unique_ptr<surface>(new surface (move (make_surface (format::argb32, width, height))));
303+ g_previousClientRect = clientRect;
304+ }
305+
306+ // Draw to the off-screen buffer.
307+ Draw (*g_psurface);
308+
309+ // Flush to ensure that it is drawn to the window.
310+ g_psurface->flush ();
311+ auto hdc = GetDC (hWnd);
312+ {
313+ auto surface = make_surface (cairo_win32_surface_create (hdc));
314+ auto ctxt = context (surface);
315+ ctxt.set_source_surface (*g_psurface, 0.0 , 0.0 );
316+ ctxt.paint ();
317+ surface.flush ();
318+ }
319+ ReleaseDC (hWnd, hdc);
320+ }
195321 }
196322 else {
197323 if (msg.message != WM_QUIT) {
@@ -216,7 +342,7 @@ ATOM MyRegisterClass(HINSTANCE hInstance) {
216342
217343 wcex.cbSize = sizeof (WNDCLASSEX);
218344
219- wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC ;
345+ wcex.style = 0 ;
220346 wcex.lpfnWndProc = WndProc;
221347 wcex.cbClsExtra = 0 ;
222348 wcex.cbWndExtra = 0 ;
@@ -369,6 +495,14 @@ void ShowSaveAsPNGDialog() {
369495LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
370496 int wmId, wmEvent;
371497
498+ #if defined(DEBUG_WNDPROC)
499+ wstringstream str;
500+ str << L" Message: 0x" << hex << uppercase << message << nouppercase
501+ << L" . WPARAM: 0x" << hex << uppercase << static_cast <UINT>(wParam) << nouppercase
502+ << L" . LPARAM: 0x" << hex << uppercase << static_cast <UINT>(lParam) << endl;
503+ OutputDebugStringW (str.str ().c_str ());
504+ #endif
505+
372506 switch (message) {
373507 case WM_COMMAND:
374508 wmId = LOWORD (wParam);
@@ -393,8 +527,16 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
393527 return DefWindowProc (hWnd, message, wParam, lParam);
394528 }
395529 break ;
530+ case WM_ENTERSIZEMOVE:
531+ g_doNotPaint = true ; // Don't paint while resizing to avoid flicker.
532+ return DefWindowProc (hWnd, message, wParam, lParam);
533+ case WM_EXITSIZEMOVE:
534+ g_doNotPaint = false ;
535+ return DefWindowProc (hWnd, message, wParam, lParam);
396536 case WM_PAINT:
397- OnPaint (hWnd, message, wParam, lParam);
537+ if (!g_doNotPaint) {
538+ OnPaint (hWnd, message, wParam, lParam);
539+ }
398540 break ;
399541 case WM_DESTROY:
400542 PostQuitMessage (0 );
0 commit comments