blob: 6921219504aa0043b2112e9338d8e0ab72092aca [file] [log] [blame]
robertphillips@google.com53e96a12012-03-30 14:47:53 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8#include "SkTypes.h"
9
10#if defined(SK_BUILD_FOR_WIN)
11
12#include <GL/gl.h>
13#include <d3d9.h>
14#include <WindowsX.h>
15#include "SkWGL.h"
16#include "SkWindow.h"
17#include "SkCanvas.h"
18#include "SkOSMenu.h"
19#include "SkTime.h"
20#include "SkUtils.h"
21
22#include "SkGraphics.h"
23
24#if SK_ANGLE
25#include "GLES2/gl2.h"
26#endif
27
28#define INVALIDATE_DELAY_MS 200
29
30static SkOSWindow* gCurrOSWin;
31static HWND gEventTarget;
32
33#define WM_EVENT_CALLBACK (WM_USER+0)
34
35void post_skwinevent()
36{
37 PostMessage(gEventTarget, WM_EVENT_CALLBACK, 0, 0);
38}
39
40SkOSWindow::SkOSWindow(void* hWnd)
41 : fHWND(hWnd)
42#if SK_ANGLE
43 , fDisplay(EGL_NO_DISPLAY)
44 , fContext(EGL_NO_CONTEXT)
45 , fSurface(EGL_NO_SURFACE)
46#endif
47 , fHGLRC(NULL)
48 , fD3D9Device(NULL)
49 , fAttached(kNone_BackEndType) {
50 gEventTarget = (HWND)hWnd;
51}
52
53SkOSWindow::~SkOSWindow() {
54 if (NULL != fD3D9Device) {
55 ((IDirect3DDevice9*)fD3D9Device)->Release();
56 }
57 if (NULL != fHGLRC) {
58 wglDeleteContext((HGLRC)fHGLRC);
59 }
60#if SK_ANGLE
61 if (EGL_NO_CONTEXT != fContext) {
62 angle::eglDestroyContext(fDisplay, fContext);
63 fContext = EGL_NO_CONTEXT;
64 }
65
66 if (EGL_NO_SURFACE != fSurface) {
67 angle::eglDestroySurface(fDisplay, fSurface);
68 fSurface = EGL_NO_SURFACE;
69 }
70
71 if (EGL_NO_DISPLAY != fDisplay) {
72 angle::eglTerminate(fDisplay);
73 fDisplay = EGL_NO_DISPLAY;
74 }
75#endif
76}
77
78static SkKey winToskKey(WPARAM vk) {
79 static const struct {
80 WPARAM fVK;
81 SkKey fKey;
82 } gPair[] = {
83 { VK_BACK, kBack_SkKey },
84 { VK_CLEAR, kBack_SkKey },
85 { VK_RETURN, kOK_SkKey },
86 { VK_UP, kUp_SkKey },
87 { VK_DOWN, kDown_SkKey },
88 { VK_LEFT, kLeft_SkKey },
89 { VK_RIGHT, kRight_SkKey }
90 };
91 for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) {
92 if (gPair[i].fVK == vk) {
93 return gPair[i].fKey;
94 }
95 }
96 return kNONE_SkKey;
97}
98
99bool SkOSWindow::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
100 switch (message) {
101 case WM_KEYDOWN: {
102 SkKey key = winToskKey(wParam);
103 if (kNONE_SkKey != key) {
104 this->handleKey(key);
105 return true;
106 }
107 } break;
108 case WM_KEYUP: {
109 SkKey key = winToskKey(wParam);
110 if (kNONE_SkKey != key) {
111 this->handleKeyUp(key);
112 return true;
113 }
114 } break;
115 case WM_UNICHAR:
116 this->handleChar(wParam);
117 return true;
118 case WM_CHAR: {
119 this->handleChar(SkUTF8_ToUnichar((char*)&wParam));
120 return true;
121 } break;
122 case WM_SIZE:
123 this->resize(lParam & 0xFFFF, lParam >> 16);
124 break;
125 case WM_PAINT: {
126 PAINTSTRUCT ps;
127 HDC hdc = BeginPaint(hWnd, &ps);
128 this->doPaint(hdc);
129 EndPaint(hWnd, &ps);
130 return true;
131 } break;
132
133 case WM_TIMER: {
134 RECT* rect = (RECT*)wParam;
135 InvalidateRect(hWnd, rect, FALSE);
136 KillTimer(hWnd, (UINT_PTR)rect);
137 delete rect;
138 return true;
139 } break;
140
141 case WM_LBUTTONDOWN:
142 this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), Click::kDown_State);
143 return true;
144
145 case WM_MOUSEMOVE:
146 this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), Click::kMoved_State);
147 return true;
148
149 case WM_LBUTTONUP:
150 this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), Click::kUp_State);
151 return true;
152
153 case WM_EVENT_CALLBACK:
154 if (SkEvent::ProcessEvent()) {
155 post_skwinevent();
156 }
157 return true;
158 }
159 return false;
160}
161
162void SkOSWindow::doPaint(void* ctx) {
163 this->update(NULL);
164
165 if (kNone_BackEndType == fAttached)
166 {
167 HDC hdc = (HDC)ctx;
168 const SkBitmap& bitmap = this->getBitmap();
169
170 BITMAPINFO bmi;
171 memset(&bmi, 0, sizeof(bmi));
172 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
173 bmi.bmiHeader.biWidth = bitmap.width();
174 bmi.bmiHeader.biHeight = -bitmap.height(); // top-down image
175 bmi.bmiHeader.biPlanes = 1;
176 bmi.bmiHeader.biBitCount = 32;
177 bmi.bmiHeader.biCompression = BI_RGB;
178 bmi.bmiHeader.biSizeImage = 0;
179
180 //
181 // Do the SetDIBitsToDevice.
182 //
183 // TODO(wjmaclean):
184 // Fix this call to handle SkBitmaps that have rowBytes != width,
185 // i.e. may have padding at the end of lines. The SkASSERT below
186 // may be ignored by builds, and the only obviously safe option
187 // seems to be to copy the bitmap to a temporary (contiguous)
188 // buffer before passing to SetDIBitsToDevice().
189 SkASSERT(bitmap.width() * bitmap.bytesPerPixel() == bitmap.rowBytes());
190 bitmap.lockPixels();
191 int iRet = SetDIBitsToDevice(hdc,
192 0, 0,
193 bitmap.width(), bitmap.height(),
194 0, 0,
195 0, bitmap.height(),
196 bitmap.getPixels(),
197 &bmi,
198 DIB_RGB_COLORS);
199 bitmap.unlockPixels();
200 }
201}
202
203#if 0
204void SkOSWindow::updateSize()
205{
206 RECT r;
207 GetWindowRect((HWND)this->getHWND(), &r);
208 this->resize(r.right - r.left, r.bottom - r.top);
209}
210#endif
211
212void SkOSWindow::onHandleInval(const SkIRect& r) {
213 RECT* rect = new RECT;
214 rect->left = r.fLeft;
215 rect->top = r.fTop;
216 rect->right = r.fRight;
217 rect->bottom = r.fBottom;
218 SetTimer((HWND)fHWND, (UINT_PTR)rect, INVALIDATE_DELAY_MS, NULL);
219}
220
221void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu)
222{
223}
224
225void SkOSWindow::onSetTitle(const char title[]){
226 SetWindowTextA((HWND)fHWND, title);
227}
228
229enum {
230 SK_MacReturnKey = 36,
231 SK_MacDeleteKey = 51,
232 SK_MacEndKey = 119,
233 SK_MacLeftKey = 123,
234 SK_MacRightKey = 124,
235 SK_MacDownKey = 125,
236 SK_MacUpKey = 126,
237
238 SK_Mac0Key = 0x52,
239 SK_Mac1Key = 0x53,
240 SK_Mac2Key = 0x54,
241 SK_Mac3Key = 0x55,
242 SK_Mac4Key = 0x56,
243 SK_Mac5Key = 0x57,
244 SK_Mac6Key = 0x58,
245 SK_Mac7Key = 0x59,
246 SK_Mac8Key = 0x5b,
247 SK_Mac9Key = 0x5c
248};
249
250static SkKey raw2key(uint32_t raw)
251{
252 static const struct {
253 uint32_t fRaw;
254 SkKey fKey;
255 } gKeys[] = {
256 { SK_MacUpKey, kUp_SkKey },
257 { SK_MacDownKey, kDown_SkKey },
258 { SK_MacLeftKey, kLeft_SkKey },
259 { SK_MacRightKey, kRight_SkKey },
260 { SK_MacReturnKey, kOK_SkKey },
261 { SK_MacDeleteKey, kBack_SkKey },
262 { SK_MacEndKey, kEnd_SkKey },
263 { SK_Mac0Key, k0_SkKey },
264 { SK_Mac1Key, k1_SkKey },
265 { SK_Mac2Key, k2_SkKey },
266 { SK_Mac3Key, k3_SkKey },
267 { SK_Mac4Key, k4_SkKey },
268 { SK_Mac5Key, k5_SkKey },
269 { SK_Mac6Key, k6_SkKey },
270 { SK_Mac7Key, k7_SkKey },
271 { SK_Mac8Key, k8_SkKey },
272 { SK_Mac9Key, k9_SkKey }
273 };
274
275 for (unsigned i = 0; i < SK_ARRAY_COUNT(gKeys); i++)
276 if (gKeys[i].fRaw == raw)
277 return gKeys[i].fKey;
278 return kNONE_SkKey;
279}
280
281///////////////////////////////////////////////////////////////////////////////////////
282
283void SkEvent::SignalNonEmptyQueue()
284{
285 post_skwinevent();
286 //SkDebugf("signal nonempty\n");
287}
288
289static UINT_PTR gTimer;
290
291VOID CALLBACK sk_timer_proc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
292{
293 SkEvent::ServiceQueueTimer();
294 //SkDebugf("timer task fired\n");
295}
296
297void SkEvent::SignalQueueTimer(SkMSec delay)
298{
299 if (gTimer)
300 {
301 KillTimer(NULL, gTimer);
302 gTimer = NULL;
303 }
304 if (delay)
305 {
306 gTimer = SetTimer(NULL, 0, delay, sk_timer_proc);
307 //SkDebugf("SetTimer of %d returned %d\n", delay, gTimer);
308 }
309}
310
311
312#define USE_MSAA 0
313
314HGLRC create_gl(HWND hwnd) {
315
316 HDC dc = GetDC(hwnd);
317
318 SkWGLExtensions extensions;
319 if (!extensions.hasExtension(dc, "WGL_ARB_pixel_format")) {
320 return NULL;
321 }
322
323 HDC prevDC = wglGetCurrentDC();
324 HGLRC prevGLRC = wglGetCurrentContext();
325 PIXELFORMATDESCRIPTOR pfd;
326
327 int format = 0;
328
329 GLint iattrs[] = {
330 SK_WGL_DRAW_TO_WINDOW, TRUE,
331 SK_WGL_DOUBLE_BUFFER, TRUE,
332 SK_WGL_ACCELERATION, SK_WGL_FULL_ACCELERATION,
333 SK_WGL_SUPPORT_OPENGL, TRUE,
334 SK_WGL_COLOR_BITS, 24,
335 SK_WGL_ALPHA_BITS, 8,
336 SK_WGL_STENCIL_BITS, 8,
337
338 // these must be kept last
339 SK_WGL_SAMPLE_BUFFERS, TRUE,
340 SK_WGL_SAMPLES, 0,
341 0,0
342 };
343
344 static const int kSampleBuffersValueIdx = SK_ARRAY_COUNT(iattrs) - 5;
345 static const int kSamplesValueIdx = SK_ARRAY_COUNT(iattrs) - 3;
346 if (USE_MSAA && extensions.hasExtension(dc, "WGL_ARB_multisample")) {
347 for (int samples = 16; samples > 1; --samples) {
348
349 iattrs[kSamplesValueIdx] = samples;
350 GLfloat fattrs[] = {0,0};
351 GLuint num;
352 int formats[64];
353 extensions.choosePixelFormat(dc, iattrs, fattrs, 64, formats, &num);
354 num = min(num,64);
355 for (GLuint i = 0; i < num; ++i) {
356 DescribePixelFormat(dc, formats[i], sizeof(pfd), &pfd);
357 if (SetPixelFormat(dc, formats[i], &pfd)) {
358 format = formats[i];
359 break;
360 }
361 }
362 }
363 }
364
365 if (0 == format) {
366 iattrs[kSampleBuffersValueIdx-1] = iattrs[kSampleBuffersValueIdx] = 0;
367 iattrs[kSamplesValueIdx-1] = iattrs[kSamplesValueIdx] = 0;
368 GLfloat fattrs[] = {0,0};
369 GLuint num;
370 extensions.choosePixelFormat(dc, iattrs, fattrs, 1, &format, &num);
371 DescribePixelFormat(dc, format, sizeof(pfd), &pfd);
372 BOOL set = SetPixelFormat(dc, format, &pfd);
373 SkASSERT(TRUE == set);
374 }
375
376 HGLRC glrc = wglCreateContext(dc);
377 SkASSERT(glrc);
378
379 wglMakeCurrent(prevDC, prevGLRC);
380 return glrc;
381}
382
383bool SkOSWindow::attachGL() {
384 if (NULL == fHGLRC) {
385 fHGLRC = create_gl((HWND)fHWND);
386 if (NULL == fHGLRC) {
387 return false;
388 }
389 glClearStencil(0);
390 glClearColor(0, 0, 0, 0);
391 glStencilMask(0xffffffff);
392 glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
393 }
394 if (wglMakeCurrent(GetDC((HWND)fHWND), (HGLRC)fHGLRC)) {
395 glViewport(0, 0, SkScalarRound(this->width()),
396 SkScalarRound(this->height()));
397 return true;
398 }
399 return false;
400}
401
402void SkOSWindow::detachGL() {
403 wglMakeCurrent(GetDC((HWND)fHWND), 0);
404 wglDeleteContext((HGLRC)fHGLRC);
405 fHGLRC = NULL;
406}
407
408void SkOSWindow::presentGL() {
409 glFlush();
410 SwapBuffers(GetDC((HWND)fHWND));
411}
412
413#if SK_ANGLE
414bool create_ANGLE(EGLNativeWindowType hWnd, angle::EGLDisplay* eglDisplay,
415 angle::EGLContext* eglContext, angle::EGLSurface* eglSurface) {
416 EGLint contextAttribs[] = {
417 EGL_CONTEXT_CLIENT_VERSION, 2,
418 EGL_NONE, EGL_NONE
419 };
420 EGLint configAttribList[] = {
421 EGL_RED_SIZE, 8,
422 EGL_GREEN_SIZE, 8,
423 EGL_BLUE_SIZE, 8,
424 EGL_ALPHA_SIZE, 8,
425 EGL_DEPTH_SIZE, 8,
426 EGL_STENCIL_SIZE, 8,
427 EGL_NONE
428 };
429 EGLint surfaceAttribList[] = {
430 EGL_NONE, EGL_NONE
431 };
432
433 angle::EGLDisplay display = angle::eglGetDisplay(GetDC(hWnd));
434 if (display == EGL_NO_DISPLAY ) {
435 return false;
436 }
437
438 // Initialize EGL
439 EGLint majorVersion, minorVersion;
440 if (!angle::eglInitialize(display, &majorVersion, &minorVersion)) {
441 return false;
442 }
443
444 EGLint numConfigs;
445 if (!angle::eglGetConfigs(display, NULL, 0, &numConfigs)) {
446 return false;
447 }
448
449 // Choose config
450 angle::EGLConfig config;
451 if (!angle::eglChooseConfig(display, configAttribList,
452 &config, 1, &numConfigs)) {
453 return false;
454 }
455
456 // Create a surface
457 angle::EGLSurface surface = angle::eglCreateWindowSurface(display, config,
458 (EGLNativeWindowType)hWnd,
459 surfaceAttribList);
460 if (surface == EGL_NO_SURFACE) {
461 return false;
462 }
463
464 // Create a GL context
465 angle::EGLContext context = angle::eglCreateContext(display, config,
466 EGL_NO_CONTEXT,
467 contextAttribs );
468 if (context == EGL_NO_CONTEXT ) {
469 return false;
470 }
471
472 // Make the context current
473 if (!angle::eglMakeCurrent(display, surface, surface, context)) {
474 return false;
475 }
476
477 *eglDisplay = display;
478 *eglContext = context;
479 *eglSurface = surface;
480 return true;
481}
482
483bool SkOSWindow::attachANGLE() {
484 if (EGL_NO_DISPLAY == fDisplay) {
485 bool bResult = create_ANGLE((HWND)fHWND, &fDisplay, &fContext, &fSurface);
486 if (false == bResult) {
487 return false;
488 }
489 angle::glClearStencil(0);
490 angle::glClearColor(0, 0, 0, 0);
491 angle::glStencilMask(0xffffffff);
492 angle::glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
493 }
494 if (angle::eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
495 angle::glViewport(0, 0, SkScalarRound(this->width()),
496 SkScalarRound(this->height()));
497 return true;
498 }
499 return false;
500}
501
502void SkOSWindow::detachANGLE() {
503 angle::eglMakeCurrent(fDisplay, EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT);
504
505 angle::eglDestroyContext(fDisplay, fContext);
506 fContext = EGL_NO_CONTEXT;
507
508 angle::eglDestroySurface(fDisplay, fSurface);
509 fSurface = EGL_NO_SURFACE;
510
511 angle::eglTerminate(fDisplay);
512 fDisplay = EGL_NO_DISPLAY;
513}
514
515void SkOSWindow::presentANGLE() {
516 angle::glFlush();
517 angle::eglSwapBuffers(fDisplay, fSurface);
518}
519#endif
520
521IDirect3DDevice9* create_d3d9_device(HWND hwnd) {
522 HRESULT hr;
523
524 IDirect3D9* d3d9;
525 d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
526 if (NULL == d3d9) {
527 return NULL;
528 }
529 D3DDEVTYPE devType = D3DDEVTYPE_HAL;
530 //D3DDEVTYPE devType = D3DDEVTYPE_REF;
531 DWORD qLevels;
532 DWORD qLevelsDepth;
533 D3DMULTISAMPLE_TYPE type;
534 for (type = D3DMULTISAMPLE_16_SAMPLES;
535 type >= D3DMULTISAMPLE_NONMASKABLE; --(*(DWORD*)&type)) {
536 hr = d3d9->CheckDeviceMultiSampleType(D3DADAPTER_DEFAULT,
537 devType, D3DFMT_D24S8, TRUE,
538 type, &qLevels);
539 qLevels = (hr == D3D_OK) ? qLevels : 0;
540 hr = d3d9->CheckDeviceMultiSampleType(D3DADAPTER_DEFAULT,
541 devType, D3DFMT_A8R8G8B8, TRUE,
542 type, &qLevelsDepth);
543 qLevelsDepth = (hr == D3D_OK) ? qLevelsDepth : 0;
544 qLevels = min(qLevels,qLevelsDepth);
545 if (qLevels > 0) {
546 break;
547 }
548 }
549 qLevels = 0;
550 IDirect3DDevice9* d3d9Device;
551 D3DPRESENT_PARAMETERS pres;
552 memset(&pres, 0, sizeof(pres));
553 pres.EnableAutoDepthStencil = TRUE;
554 pres.AutoDepthStencilFormat = D3DFMT_D24S8;
555 pres.BackBufferCount = 2;
556 pres.BackBufferFormat = D3DFMT_A8R8G8B8;
557 pres.BackBufferHeight = 0;
558 pres.BackBufferWidth = 0;
559 if (qLevels > 0) {
560 pres.MultiSampleType = type;
561 pres.MultiSampleQuality = qLevels-1;
562 } else {
563 pres.MultiSampleType = D3DMULTISAMPLE_NONE;
564 pres.MultiSampleQuality = 0;
565 }
566 pres.SwapEffect = D3DSWAPEFFECT_DISCARD;
567 pres.Windowed = TRUE;
568 pres.hDeviceWindow = hwnd;
569 pres.PresentationInterval = 1;
570 pres.Flags = 0;
571 hr = d3d9->CreateDevice(D3DADAPTER_DEFAULT,
572 devType,
573 hwnd,
574 D3DCREATE_HARDWARE_VERTEXPROCESSING,
575 &pres,
576 &d3d9Device);
577 D3DERR_INVALIDCALL;
578 if (SUCCEEDED(hr)) {
579 d3d9Device->Clear(0, NULL, D3DCLEAR_TARGET, 0xFFFFFFFF, 0, 0);
580 return d3d9Device;
581 }
582 return NULL;
583}
584
585// This needs some improvement. D3D doesn't have the same notion of attach/detach
586// as GL. However, just allowing GDI to write to the window after creating the
587// D3D device seems to work.
588// We need to handle resizing. On XP and earlier Reset() will trash all our textures
589// so we would need to inform the SkGpu/caches or just recreate them. On Vista+ we
590// could use an IDirect3DDevice9Ex and call ResetEx() to resize without trashing
591// everything. Currently we do nothing and the D3D9 image gets stretched/compressed
592// when resized.
593
594bool SkOSWindow::attachD3D9() {
595 if (NULL == fD3D9Device) {
596 fD3D9Device = (void*) create_d3d9_device((HWND)fHWND);
597 }
598 if (NULL != fD3D9Device) {
599 ((IDirect3DDevice9*)fD3D9Device)->BeginScene();
600 return true;
601 }
602 return false;
603}
604
605void SkOSWindow::detachD3D9() {
606 if (NULL != fD3D9Device) {
607 ((IDirect3DDevice9*)fD3D9Device)->EndScene();
608 }
609}
610
611void SkOSWindow::presentD3D9() {
612 if (NULL != fD3D9Device) {
613 HRESULT hr;
614 hr = ((IDirect3DDevice9*)fD3D9Device)->EndScene();
615 SkASSERT(SUCCEEDED(hr));
616 hr = ((IDirect3DDevice9*)d3d9Device())->Present(NULL, NULL, NULL, NULL);
617 SkASSERT(SUCCEEDED(hr));
618 hr = ((IDirect3DDevice9*)fD3D9Device)->Clear(0,NULL,D3DCLEAR_TARGET |
619 D3DCLEAR_STENCIL, 0x0, 0,
620 0);
621 SkASSERT(SUCCEEDED(hr));
622 hr = ((IDirect3DDevice9*)fD3D9Device)->BeginScene();
623 SkASSERT(SUCCEEDED(hr));
624 }
625}
626
627// return true on success
628bool SkOSWindow::attach(SkBackEndTypes attachType) {
629
630 // attach doubles as "windowResize" so we need to allo
631 // already bound states to pass through again
632 // TODO: split out the resize functionality
633// SkASSERT(kNone_BackEndType == fAttached);
634 bool result = true;
635
636 switch (attachType) {
637 case kNone_BackEndType:
638 // nothing to do
639 break;
640 case kNativeGL_BackEndType:
641 result = attachGL();
642 break;
643#if SK_ANGLE
644 case kANGLE_BackEndType:
645 result = attachANGLE();
646 break;
647#endif
648 case kD3D9_BackEndType:
649 result = attachD3D9();
650 break;
651 default:
652 SkASSERT(false);
653 result = false;
654 break;
655 }
656
657 if (result) {
658 fAttached = attachType;
659 }
660
661 return result;
662}
663
664void SkOSWindow::detach() {
665 switch (fAttached) {
666 case kNone_BackEndType:
667 // nothing to do
668 break;
669 case kNativeGL_BackEndType:
670 detachGL();
671 break;
672#if SK_ANGLE
673 case kANGLE_BackEndType:
674 detachANGLE();
675 break;
676#endif
677 case kD3D9_BackEndType:
678 detachD3D9();
679 break;
680 default:
681 SkASSERT(false);
682 break;
683 }
684 fAttached = kNone_BackEndType;
685}
686
687void SkOSWindow::present() {
688 switch (fAttached) {
689 case kNone_BackEndType:
690 // nothing to do
691 return;
692 case kNativeGL_BackEndType:
693 presentGL();
694 break;
695#if SK_ANGLE
696 case kANGLE_BackEndType:
697 presentANGLE();
698 break;
699#endif
700 case kD3D9_BackEndType:
701 presentD3D9();
702 break;
703 default:
704 SkASSERT(false);
705 break;
706 }
707}
708
709#endif
710