3 * Copyright 2011 Google Inc.
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
10 #if defined(SK_BUILD_FOR_WIN)
21 #include "SkGraphics.h"
24 #include "gl/GrGLInterface.h"
26 #include "GLES2/gl2.h"
28 #define ANGLE_GL_CALL(IFACE, X) \
30 (IFACE)->fFunctions.f##X; \
35 #define INVALIDATE_DELAY_MS 200
37 static SkOSWindow* gCurrOSWin;
38 static HWND gEventTarget;
40 #define WM_EVENT_CALLBACK (WM_USER+0)
42 void post_skwinevent()
44 PostMessage(gEventTarget, WM_EVENT_CALLBACK, 0, 0);
47 SkOSWindow::SkOSWindow(void* hWnd) {
51 fDisplay = EGL_NO_DISPLAY;
52 fContext = EGL_NO_CONTEXT;
53 fSurface = EGL_NO_SURFACE;
57 fAttached = kNone_BackEndType;
58 gEventTarget = (HWND)hWnd;
61 SkOSWindow::~SkOSWindow() {
64 wglDeleteContext((HGLRC)fHGLRC);
67 if (EGL_NO_CONTEXT != fContext) {
68 eglDestroyContext(fDisplay, fContext);
69 fContext = EGL_NO_CONTEXT;
72 if (EGL_NO_SURFACE != fSurface) {
73 eglDestroySurface(fDisplay, fSurface);
74 fSurface = EGL_NO_SURFACE;
77 if (EGL_NO_DISPLAY != fDisplay) {
78 eglTerminate(fDisplay);
79 fDisplay = EGL_NO_DISPLAY;
82 #endif // SK_SUPPORT_GPU
85 static SkKey winToskKey(WPARAM vk) {
90 { VK_BACK, kBack_SkKey },
91 { VK_CLEAR, kBack_SkKey },
92 { VK_RETURN, kOK_SkKey },
94 { VK_DOWN, kDown_SkKey },
95 { VK_LEFT, kLeft_SkKey },
96 { VK_RIGHT, kRight_SkKey }
98 for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) {
99 if (gPair[i].fVK == vk) {
100 return gPair[i].fKey;
106 static unsigned getModifiers(UINT message) {
110 bool SkOSWindow::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
113 SkKey key = winToskKey(wParam);
114 if (kNONE_SkKey != key) {
115 this->handleKey(key);
120 SkKey key = winToskKey(wParam);
121 if (kNONE_SkKey != key) {
122 this->handleKeyUp(key);
127 this->handleChar((SkUnichar) wParam);
130 this->handleChar(SkUTF8_ToUnichar((char*)&wParam));
134 INT width = LOWORD(lParam);
135 INT height = HIWORD(lParam);
136 this->resize(width, height);
141 HDC hdc = BeginPaint(hWnd, &ps);
148 RECT* rect = (RECT*)wParam;
149 InvalidateRect(hWnd, rect, FALSE);
150 KillTimer(hWnd, (UINT_PTR)rect);
156 this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
157 Click::kDown_State, NULL, getModifiers(message));
161 this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
162 Click::kMoved_State, NULL, getModifiers(message));
166 this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
167 Click::kUp_State, NULL, getModifiers(message));
170 case WM_EVENT_CALLBACK:
171 if (SkEvent::ProcessEvent()) {
179 void SkOSWindow::doPaint(void* ctx) {
182 if (kNone_BackEndType == fAttached)
185 const SkBitmap& bitmap = this->getBitmap();
188 memset(&bmi, 0, sizeof(bmi));
189 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
190 bmi.bmiHeader.biWidth = bitmap.width();
191 bmi.bmiHeader.biHeight = -bitmap.height(); // top-down image
192 bmi.bmiHeader.biPlanes = 1;
193 bmi.bmiHeader.biBitCount = 32;
194 bmi.bmiHeader.biCompression = BI_RGB;
195 bmi.bmiHeader.biSizeImage = 0;
198 // Do the SetDIBitsToDevice.
201 // Fix this call to handle SkBitmaps that have rowBytes != width,
202 // i.e. may have padding at the end of lines. The SkASSERT below
203 // may be ignored by builds, and the only obviously safe option
204 // seems to be to copy the bitmap to a temporary (contiguous)
205 // buffer before passing to SetDIBitsToDevice().
206 SkASSERT(bitmap.width() * bitmap.bytesPerPixel() == bitmap.rowBytes());
208 int ret = SetDIBitsToDevice(hdc,
210 bitmap.width(), bitmap.height(),
216 (void)ret; // we're ignoring potential failures for now.
217 bitmap.unlockPixels();
222 void SkOSWindow::updateSize()
225 GetWindowRect((HWND)this->getHWND(), &r);
226 this->resize(r.right - r.left, r.bottom - r.top);
230 void SkOSWindow::onHandleInval(const SkIRect& r) {
231 RECT* rect = new RECT;
232 rect->left = r.fLeft;
234 rect->right = r.fRight;
235 rect->bottom = r.fBottom;
236 SetTimer((HWND)fHWND, (UINT_PTR)rect, INVALIDATE_DELAY_MS, NULL);
239 void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu)
243 void SkOSWindow::onSetTitle(const char title[]){
244 SetWindowTextA((HWND)fHWND, title);
248 SK_MacReturnKey = 36,
249 SK_MacDeleteKey = 51,
252 SK_MacRightKey = 124,
268 static SkKey raw2key(uint32_t raw)
270 static const struct {
274 { SK_MacUpKey, kUp_SkKey },
275 { SK_MacDownKey, kDown_SkKey },
276 { SK_MacLeftKey, kLeft_SkKey },
277 { SK_MacRightKey, kRight_SkKey },
278 { SK_MacReturnKey, kOK_SkKey },
279 { SK_MacDeleteKey, kBack_SkKey },
280 { SK_MacEndKey, kEnd_SkKey },
281 { SK_Mac0Key, k0_SkKey },
282 { SK_Mac1Key, k1_SkKey },
283 { SK_Mac2Key, k2_SkKey },
284 { SK_Mac3Key, k3_SkKey },
285 { SK_Mac4Key, k4_SkKey },
286 { SK_Mac5Key, k5_SkKey },
287 { SK_Mac6Key, k6_SkKey },
288 { SK_Mac7Key, k7_SkKey },
289 { SK_Mac8Key, k8_SkKey },
290 { SK_Mac9Key, k9_SkKey }
293 for (unsigned i = 0; i < SK_ARRAY_COUNT(gKeys); i++)
294 if (gKeys[i].fRaw == raw)
295 return gKeys[i].fKey;
299 ///////////////////////////////////////////////////////////////////////////////////////
301 void SkEvent::SignalNonEmptyQueue()
304 //SkDebugf("signal nonempty\n");
307 static UINT_PTR gTimer;
309 VOID CALLBACK sk_timer_proc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
311 SkEvent::ServiceQueueTimer();
312 //SkDebugf("timer task fired\n");
315 void SkEvent::SignalQueueTimer(SkMSec delay)
319 KillTimer(NULL, gTimer);
324 gTimer = SetTimer(NULL, 0, delay, sk_timer_proc);
325 //SkDebugf("SetTimer of %d returned %d\n", delay, gTimer);
331 bool SkOSWindow::attachGL(int msaaSampleCount, AttachmentInfo* info) {
332 HDC dc = GetDC((HWND)fHWND);
333 if (NULL == fHGLRC) {
334 fHGLRC = SkCreateWGLContext(dc, msaaSampleCount,
335 kGLPreferCompatibilityProfile_SkWGLContextRequest);
336 if (NULL == fHGLRC) {
340 glClearColor(0, 0, 0, 0);
341 glStencilMask(0xffffffff);
342 glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
344 if (wglMakeCurrent(dc, (HGLRC)fHGLRC)) {
345 // use DescribePixelFormat to get the stencil bit depth.
346 int pixelFormat = GetPixelFormat(dc);
347 PIXELFORMATDESCRIPTOR pfd;
348 DescribePixelFormat(dc, pixelFormat, sizeof(pfd), &pfd);
349 info->fStencilBits = pfd.cStencilBits;
351 // Get sample count if the MSAA WGL extension is present
352 SkWGLExtensions extensions;
353 if (extensions.hasExtension(dc, "WGL_ARB_multisample")) {
354 static const int kSampleCountAttr = SK_WGL_SAMPLES;
355 extensions.getPixelFormatAttribiv(dc,
360 &info->fSampleCount);
362 info->fSampleCount = 0;
366 SkScalarRoundToInt(this->width()),
367 SkScalarRoundToInt(this->height()));
373 void SkOSWindow::detachGL() {
374 wglMakeCurrent(GetDC((HWND)fHWND), 0);
375 wglDeleteContext((HGLRC)fHGLRC);
379 void SkOSWindow::presentGL() {
381 HDC dc = GetDC((HWND)fHWND);
383 ReleaseDC((HWND)fHWND, dc);
387 bool create_ANGLE(EGLNativeWindowType hWnd,
389 EGLDisplay* eglDisplay,
390 EGLContext* eglContext,
391 EGLSurface* eglSurface,
392 EGLConfig* eglConfig) {
393 static const EGLint contextAttribs[] = {
394 EGL_CONTEXT_CLIENT_VERSION, 2,
397 static const EGLint configAttribList[] = {
406 static const EGLint surfaceAttribList[] = {
410 EGLDisplay display = eglGetDisplay(GetDC(hWnd));
411 if (display == EGL_NO_DISPLAY ) {
416 EGLint majorVersion, minorVersion;
417 if (!eglInitialize(display, &majorVersion, &minorVersion)) {
422 if (!eglGetConfigs(display, NULL, 0, &numConfigs)) {
427 bool foundConfig = false;
428 if (msaaSampleCount) {
429 static const int kConfigAttribListCnt =
430 SK_ARRAY_COUNT(configAttribList);
431 EGLint msaaConfigAttribList[kConfigAttribListCnt + 4];
432 memcpy(msaaConfigAttribList,
434 sizeof(configAttribList));
435 SkASSERT(EGL_NONE == msaaConfigAttribList[kConfigAttribListCnt - 1]);
436 msaaConfigAttribList[kConfigAttribListCnt - 1] = EGL_SAMPLE_BUFFERS;
437 msaaConfigAttribList[kConfigAttribListCnt + 0] = 1;
438 msaaConfigAttribList[kConfigAttribListCnt + 1] = EGL_SAMPLES;
439 msaaConfigAttribList[kConfigAttribListCnt + 2] = msaaSampleCount;
440 msaaConfigAttribList[kConfigAttribListCnt + 3] = EGL_NONE;
441 if (eglChooseConfig(display, configAttribList, eglConfig, 1, &numConfigs)) {
442 SkASSERT(numConfigs > 0);
447 if (!eglChooseConfig(display, configAttribList, eglConfig, 1, &numConfigs)) {
453 EGLSurface surface = eglCreateWindowSurface(display, *eglConfig,
454 (EGLNativeWindowType)hWnd,
456 if (surface == EGL_NO_SURFACE) {
460 // Create a GL context
461 EGLContext context = eglCreateContext(display, *eglConfig,
464 if (context == EGL_NO_CONTEXT ) {
468 // Make the context current
469 if (!eglMakeCurrent(display, surface, surface, context)) {
473 *eglDisplay = display;
474 *eglContext = context;
475 *eglSurface = surface;
479 bool SkOSWindow::attachANGLE(int msaaSampleCount, AttachmentInfo* info) {
480 if (EGL_NO_DISPLAY == fDisplay) {
481 bool bResult = create_ANGLE((HWND)fHWND,
487 if (false == bResult) {
490 SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface());
493 ANGLE_GL_CALL(intf, ClearStencil(0));
494 ANGLE_GL_CALL(intf, ClearColor(0, 0, 0, 0));
495 ANGLE_GL_CALL(intf, StencilMask(0xffffffff));
496 ANGLE_GL_CALL(intf, Clear(GL_STENCIL_BUFFER_BIT |GL_COLOR_BUFFER_BIT));
499 if (eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
500 eglGetConfigAttrib(fDisplay, fConfig, EGL_STENCIL_SIZE, &info->fStencilBits);
501 eglGetConfigAttrib(fDisplay, fConfig, EGL_SAMPLES, &info->fSampleCount);
503 SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface());
506 ANGLE_GL_CALL(intf, Viewport(0, 0,
507 SkScalarRoundToInt(this->width()),
508 SkScalarRoundToInt(this->height())));
515 void SkOSWindow::detachANGLE() {
516 eglMakeCurrent(fDisplay, EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT);
518 eglDestroyContext(fDisplay, fContext);
519 fContext = EGL_NO_CONTEXT;
521 eglDestroySurface(fDisplay, fSurface);
522 fSurface = EGL_NO_SURFACE;
524 eglTerminate(fDisplay);
525 fDisplay = EGL_NO_DISPLAY;
528 void SkOSWindow::presentANGLE() {
529 SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface());
532 ANGLE_GL_CALL(intf, Flush());
535 eglSwapBuffers(fDisplay, fSurface);
538 #endif // SK_SUPPORT_GPU
540 // return true on success
541 bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo* info) {
543 // attach doubles as "windowResize" so we need to allo
544 // already bound states to pass through again
545 // TODO: split out the resize functionality
546 // SkASSERT(kNone_BackEndType == fAttached);
549 switch (attachType) {
550 case kNone_BackEndType:
554 case kNativeGL_BackEndType:
555 result = attachGL(msaaSampleCount, info);
558 case kANGLE_BackEndType:
559 result = attachANGLE(msaaSampleCount, info);
562 #endif // SK_SUPPORT_GPU
570 fAttached = attachType;
576 void SkOSWindow::detach() {
578 case kNone_BackEndType:
582 case kNativeGL_BackEndType:
586 case kANGLE_BackEndType:
590 #endif // SK_SUPPORT_GPU
595 fAttached = kNone_BackEndType;
598 void SkOSWindow::present() {
600 case kNone_BackEndType:
604 case kNativeGL_BackEndType:
608 case kANGLE_BackEndType:
612 #endif // SK_SUPPORT_GPU