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:
2851HINSTANCE hInst; // current instance
2952TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
3053TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
3154unique_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.
3459void 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
136202void 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
162256int 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() {
373499LRESULT 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 );
0 commit comments