diff --git a/gyp/utils.gyp b/gyp/utils.gyp index 5e6fa8548..b4b2be376 100644 --- a/gyp/utils.gyp +++ b/gyp/utils.gyp @@ -36,6 +36,7 @@ '../include/utils/SkSfntUtils.h', '../include/utils/SkTextBox.h', '../include/utils/SkUnitMappers.h', + '../include/utils/SkWGL.h', '../src/utils/SkBoundaryPatch.cpp', '../src/utils/SkCamera.cpp', @@ -77,7 +78,8 @@ '../src/utils/win/skia_win.cpp', '../src/utils/win/SkHRESULT.cpp', '../src/utils/win/SkIStream.cpp', - '../src/utils/win/SkOSWindow_Win.cpp', + '../src/utils/win/SkOSWindow_win.cpp', + '../src/utils/win/SkWGL_win.cpp', ], 'sources!': [ '../src/utils/SDL/SkOSWindow_SDL.cpp', @@ -131,16 +133,15 @@ 'include_dirs!': [ '../include/utils/win', ], + 'sources/': [ ['exclude', '_win.(h|cpp)$'],], 'sources!': [ '../include/utils/win/SkAutoCoInitialize.h', '../include/utils/win/SkHRESULT.h', '../include/utils/win/SkIStream.h', '../include/utils/win/SkTScopedComPtr.h', '../src/utils/win/SkAutoCoInitialize.cpp', - '../src/utils/win/skia_win.cpp', '../src/utils/win/SkHRESULT.cpp', '../src/utils/win/SkIStream.cpp', - '../src/utils/win/SkOSWindow_Win.cpp', ], }], ], diff --git a/include/utils/SkWGL.h b/include/utils/SkWGL.h new file mode 100644 index 000000000..35c93f68d --- /dev/null +++ b/include/utils/SkWGL.h @@ -0,0 +1,90 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkRefCnt.h" + +#ifndef SkWGL_DEFINED +#define SkWGL_DEFINED + +/** + * Working with WGL extensions can be a pain. Among the reasons is that You must + * have a GL context to get the proc addresses, but you want to use the procs to + * create a context in the first place. So you have to create a dummy GL ctx to + * get the proc addresses. + * + * This file helps by providing SkCreateWGLInterface(). It returns a struct of + * function pointers that it initializes. It also has a helper function to query + * for WGL extensions. It handles the fact that wglGetExtensionsString is itself + * an extension. + */ + +#if !defined(WIN32_LEAN_AND_MEAN) + #define WIN32_LEAN_AND_MEAN + #define SK_LOCAL_LEAN_AND_MEAN +#endif +#include +#if defined(SK_LOCAL_LEAN_AND_MEAN) + #undef WIN32_LEAN_AND_MEAN + #undef SK_LOCAL_LEAN_AND_MEAN +#endif + +#define SK_WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define SK_WGL_ACCELERATION_ARB 0x2003 +#define SK_WGL_SUPPORT_OPENGL_ARB 0x2010 +#define SK_WGL_DOUBLE_BUFFER_ARB 0x2011 +#define SK_WGL_COLOR_BITS_ARB 0x2014 +#define SK_WGL_ALPHA_BITS_ARB 0x201B +#define SK_WGL_STENCIL_BITS_ARB 0x2023 +#define SK_WGL_FULL_ACCELERATION_ARB 0x2027 +#define SK_WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define SK_WGL_SAMPLES_ARB 0x2042 +#define SK_WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define SK_WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define SK_WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 +#define SK_WGL_CONTEXT_FLAGS_ARB 0x2094 +#define SK_WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define SK_WGL_CONTEXT_DEBUG_BIT_ARB 0x0001 +#define SK_WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 +#define SK_WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define SK_WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define SK_WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 +#define SK_ERROR_INVALID_VERSION_ARB 0x2095 +#define SK_ERROR_INVALID_PROFILE_ARB 0x2096 + +class SkWGLExtensions { +public: + SkWGLExtensions(); + /** + * Determines if an extensions is available for a given DC. + * WGL_ARB_extensions_string is considered a prerequisite for all other + * extensions. It is necessary to check this before calling other class + * functions. + */ + bool hasExtension(HDC dc, const char* ext) const; + + const char* getExtensionsString(HDC hdc) const; + BOOL choosePixelFormat(HDC hdc, const int*, const FLOAT*, UINT, int*, UINT*) const; + BOOL getPixelFormatAttribiv(HDC, int, int, UINT, const int*, int*) const; + BOOL getPixelFormatAttribfv(HDC hdc, int, int, UINT, const int*, FLOAT*) const; + HGLRC createContextAttribs(HDC, HGLRC, const int *) const; + +private: + typedef const char* (WINAPI *GetExtensionsStringProc)(HDC hdc); + typedef BOOL (WINAPI *ChoosePixelFormatProc)(HDC hdc, const int *, const FLOAT *, UINT, int *, UINT *); + typedef BOOL (WINAPI *GetPixelFormatAttribivProc)(HDC, int, int, UINT, const int*, int*); + typedef BOOL (WINAPI *GetPixelFormatAttribfvProc)(HDC hdc, int, int, UINT, const int*, FLOAT*); + typedef HGLRC (WINAPI *CreateContextAttribsProc)(HDC hDC, HGLRC, const int *); + + GetExtensionsStringProc fGetExtensionsString; + ChoosePixelFormatProc fChoosePixelFormat; + GetPixelFormatAttribfvProc fGetPixelFormatAttribfv; + GetPixelFormatAttribivProc fGetPixelFormatAttribiv; + CreateContextAttribsProc fCreateContextAttribs; +}; + +#endif diff --git a/src/utils/win/SkOSWindow_win.cpp b/src/utils/win/SkOSWindow_win.cpp new file mode 100644 index 000000000..7a10823e6 --- /dev/null +++ b/src/utils/win/SkOSWindow_win.cpp @@ -0,0 +1,492 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "SkTypes.h" + +#if defined(SK_BUILD_FOR_WIN) + +#include +#include +#include +#include "SkWGL.h" +#include "SkWindow.h" +#include "SkCanvas.h" +#include "SkOSMenu.h" +#include "SkTime.h" +#include "SkUtils.h" + +#include "SkGraphics.h" + +#define INVALIDATE_DELAY_MS 200 + +static SkOSWindow* gCurrOSWin; +static HWND gEventTarget; + +#define WM_EVENT_CALLBACK (WM_USER+0) + +void post_skwinevent() +{ + PostMessage(gEventTarget, WM_EVENT_CALLBACK, 0, 0); +} + +SkOSWindow::SkOSWindow(void* hWnd) : fHWND(hWnd), + fHGLRC(NULL), + fGLAttached(false), + fD3D9Device(NULL), + fD3D9Attached(FALSE) { + gEventTarget = (HWND)hWnd; +} + +SkOSWindow::~SkOSWindow() { + if (NULL != fD3D9Device) { + ((IDirect3DDevice9*)fD3D9Device)->Release(); + } + if (NULL != fHGLRC) { + wglDeleteContext((HGLRC)fHGLRC); + } +} + +static SkKey winToskKey(WPARAM vk) { + static const struct { + WPARAM fVK; + SkKey fKey; + } gPair[] = { + { VK_BACK, kBack_SkKey }, + { VK_CLEAR, kBack_SkKey }, + { VK_RETURN, kOK_SkKey }, + { VK_UP, kUp_SkKey }, + { VK_DOWN, kDown_SkKey }, + { VK_LEFT, kLeft_SkKey }, + { VK_RIGHT, kRight_SkKey } + }; + for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { + if (gPair[i].fVK == vk) { + return gPair[i].fKey; + } + } + return kNONE_SkKey; +} + +bool SkOSWindow::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + switch (message) { + case WM_KEYDOWN: { + SkKey key = winToskKey(wParam); + if (kNONE_SkKey != key) { + this->handleKey(key); + return true; + } + } break; + case WM_KEYUP: { + SkKey key = winToskKey(wParam); + if (kNONE_SkKey != key) { + this->handleKeyUp(key); + return true; + } + } break; + case WM_UNICHAR: + this->handleChar(wParam); + return true; + case WM_CHAR: { + this->handleChar(SkUTF8_ToUnichar((char*)&wParam)); + return true; + } break; + case WM_SIZE: + this->resize(lParam & 0xFFFF, lParam >> 16); + break; + case WM_PAINT: { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + this->doPaint(hdc); + EndPaint(hWnd, &ps); + return true; + } break; + + case WM_TIMER: { + RECT* rect = (RECT*)wParam; + InvalidateRect(hWnd, rect, FALSE); + KillTimer(hWnd, (UINT_PTR)rect); + delete rect; + return true; + } break; + + case WM_LBUTTONDOWN: + this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), Click::kDown_State); + return true; + + case WM_MOUSEMOVE: + this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), Click::kMoved_State); + return true; + + case WM_LBUTTONUP: + this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), Click::kUp_State); + return true; + + case WM_EVENT_CALLBACK: + if (SkEvent::ProcessEvent()) { + post_skwinevent(); + } + return true; + } + return false; +} + +void SkOSWindow::doPaint(void* ctx) { + this->update(NULL); + + if (!fGLAttached && !fD3D9Attached) + { + HDC hdc = (HDC)ctx; + const SkBitmap& bitmap = this->getBitmap(); + + BITMAPINFO bmi; + memset(&bmi, 0, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = bitmap.width(); + bmi.bmiHeader.biHeight = -bitmap.height(); // top-down image + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = 0; + + // + // Do the SetDIBitsToDevice. + // + // TODO(wjmaclean): + // Fix this call to handle SkBitmaps that have rowBytes != width, + // i.e. may have padding at the end of lines. The SkASSERT below + // may be ignored by builds, and the only obviously safe option + // seems to be to copy the bitmap to a temporary (contiguous) + // buffer before passing to SetDIBitsToDevice(). + SkASSERT(bitmap.width() * bitmap.bytesPerPixel() == bitmap.rowBytes()); + bitmap.lockPixels(); + int iRet = SetDIBitsToDevice(hdc, + 0, 0, + bitmap.width(), bitmap.height(), + 0, 0, + 0, bitmap.height(), + bitmap.getPixels(), + &bmi, + DIB_RGB_COLORS); + bitmap.unlockPixels(); + } +} + +#if 0 +void SkOSWindow::updateSize() +{ + RECT r; + GetWindowRect((HWND)this->getHWND(), &r); + this->resize(r.right - r.left, r.bottom - r.top); +} +#endif + +void SkOSWindow::onHandleInval(const SkIRect& r) { + RECT* rect = new RECT; + rect->left = r.fLeft; + rect->top = r.fTop; + rect->right = r.fRight; + rect->bottom = r.fBottom; + SetTimer((HWND)fHWND, (UINT_PTR)rect, INVALIDATE_DELAY_MS, NULL); +} + +void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu) +{ +} + +void SkOSWindow::onSetTitle(const char title[]){ + SetWindowTextA((HWND)fHWND, title); +} + +enum { + SK_MacReturnKey = 36, + SK_MacDeleteKey = 51, + SK_MacEndKey = 119, + SK_MacLeftKey = 123, + SK_MacRightKey = 124, + SK_MacDownKey = 125, + SK_MacUpKey = 126, + + SK_Mac0Key = 0x52, + SK_Mac1Key = 0x53, + SK_Mac2Key = 0x54, + SK_Mac3Key = 0x55, + SK_Mac4Key = 0x56, + SK_Mac5Key = 0x57, + SK_Mac6Key = 0x58, + SK_Mac7Key = 0x59, + SK_Mac8Key = 0x5b, + SK_Mac9Key = 0x5c +}; + +static SkKey raw2key(uint32_t raw) +{ + static const struct { + uint32_t fRaw; + SkKey fKey; + } gKeys[] = { + { SK_MacUpKey, kUp_SkKey }, + { SK_MacDownKey, kDown_SkKey }, + { SK_MacLeftKey, kLeft_SkKey }, + { SK_MacRightKey, kRight_SkKey }, + { SK_MacReturnKey, kOK_SkKey }, + { SK_MacDeleteKey, kBack_SkKey }, + { SK_MacEndKey, kEnd_SkKey }, + { SK_Mac0Key, k0_SkKey }, + { SK_Mac1Key, k1_SkKey }, + { SK_Mac2Key, k2_SkKey }, + { SK_Mac3Key, k3_SkKey }, + { SK_Mac4Key, k4_SkKey }, + { SK_Mac5Key, k5_SkKey }, + { SK_Mac6Key, k6_SkKey }, + { SK_Mac7Key, k7_SkKey }, + { SK_Mac8Key, k8_SkKey }, + { SK_Mac9Key, k9_SkKey } + }; + + for (unsigned i = 0; i < SK_ARRAY_COUNT(gKeys); i++) + if (gKeys[i].fRaw == raw) + return gKeys[i].fKey; + return kNONE_SkKey; +} + +/////////////////////////////////////////////////////////////////////////////////////// + +void SkEvent::SignalNonEmptyQueue() +{ + post_skwinevent(); + //SkDebugf("signal nonempty\n"); +} + +static UINT_PTR gTimer; + +VOID CALLBACK sk_timer_proc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + SkEvent::ServiceQueueTimer(); + //SkDebugf("timer task fired\n"); +} + +void SkEvent::SignalQueueTimer(SkMSec delay) +{ + if (gTimer) + { + KillTimer(NULL, gTimer); + gTimer = NULL; + } + if (delay) + { + gTimer = SetTimer(NULL, 0, delay, sk_timer_proc); + //SkDebugf("SetTimer of %d returned %d\n", delay, gTimer); + } +} + + +#define USE_MSAA 0 + +HGLRC create_gl(HWND hwnd) { + HDC dc = GetDC(hwnd); + SkWGLExtensions extensions; + if (!extensions.hasExtension(dc, "WGL_ARB_pixel_format")) { + return NULL; + } + + HDC prevDC = wglGetCurrentDC(); + HGLRC prevGLRC = wglGetCurrentContext(); + PIXELFORMATDESCRIPTOR pfd; + + int format = 0; + + GLint iattrs[] = { + SK_WGL_DRAW_TO_WINDOW_ARB, TRUE, + SK_WGL_DOUBLE_BUFFER_ARB, TRUE, + SK_WGL_ACCELERATION_ARB, SK_WGL_FULL_ACCELERATION_ARB, + SK_WGL_SUPPORT_OPENGL_ARB, TRUE, + SK_WGL_COLOR_BITS_ARB, 24, + SK_WGL_ALPHA_BITS_ARB, 8, + SK_WGL_STENCIL_BITS_ARB, 8, + + // these must be kept last + SK_WGL_SAMPLE_BUFFERS_ARB, TRUE, + SK_WGL_SAMPLES_ARB, 0, + 0,0 + }; + static const int kSampleBuffersValueIdx = SK_ARRAY_COUNT(iattrs) - 5; + static const int kSamplesValueIdx = SK_ARRAY_COUNT(iattrs) - 3; + if (USE_MSAA && extensions.hasExtension(dc, "WGL_ARB_multisample")) { + for (int samples = 16; samples > 1; --samples) { + + iattrs[kSamplesValueIdx] = samples; + GLfloat fattrs[] = {0,0}; + GLuint num; + int formats[64]; + extensions.choosePixelFormat(dc, iattrs, fattrs, 64, formats, &num); + num = min(num,64); + for (GLuint i = 0; i < num; ++i) { + DescribePixelFormat(dc, formats[i], sizeof(pfd), &pfd); + if (SetPixelFormat(dc, formats[i], &pfd)) { + format = formats[i]; + break; + } + } + } + } + if (0 == format) { + iattrs[kSampleBuffersValueIdx-1] = iattrs[kSampleBuffersValueIdx] = 0; + iattrs[kSamplesValueIdx-1] = iattrs[kSamplesValueIdx] = 0; + GLfloat fattrs[] = {0,0}; + GLuint num; + extensions.choosePixelFormat(dc, iattrs, fattrs, 1, &format, &num); + DescribePixelFormat(dc, format, sizeof(pfd), &pfd); + BOOL set = SetPixelFormat(dc, format, &pfd); + SkASSERT(TRUE == set); + } + + HGLRC glrc = wglCreateContext(dc); + SkASSERT(glrc); + + wglMakeCurrent(prevDC, prevGLRC); + return glrc; +} + +bool SkOSWindow::attachGL() { + if (NULL == fHGLRC) { + fHGLRC = create_gl((HWND)fHWND); + if (NULL == fHGLRC) { + return false; + } + glClearStencil(0); + glClearColor(0, 0, 0, 0); + glStencilMask(0xffffffff); + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + } + if (wglMakeCurrent(GetDC((HWND)fHWND), (HGLRC)fHGLRC)) { + glViewport(0, 0, SkScalarRound(this->width()), + SkScalarRound(this->height())); + fGLAttached = true; + return true; + } + return false; +} + +void SkOSWindow::detachGL() { + wglMakeCurrent(GetDC((HWND)fHWND), 0); + fGLAttached = false; +} + +void SkOSWindow::presentGL() { + glFlush(); + SwapBuffers(GetDC((HWND)fHWND)); +} + +IDirect3DDevice9* create_d3d9_device(HWND hwnd) { + HRESULT hr; + + IDirect3D9* d3d9; + d3d9 = Direct3DCreate9(D3D_SDK_VERSION); + if (NULL == d3d9) { + return NULL; + } + D3DDEVTYPE devType = D3DDEVTYPE_HAL; + //D3DDEVTYPE devType = D3DDEVTYPE_REF; + DWORD qLevels; + DWORD qLevelsDepth; + D3DMULTISAMPLE_TYPE type; + for (type = D3DMULTISAMPLE_16_SAMPLES; + type >= D3DMULTISAMPLE_NONMASKABLE; --(*(DWORD*)&type)) { + hr = d3d9->CheckDeviceMultiSampleType(D3DADAPTER_DEFAULT, + devType, D3DFMT_D24S8, TRUE, + type, &qLevels); + qLevels = (hr == D3D_OK) ? qLevels : 0; + hr = d3d9->CheckDeviceMultiSampleType(D3DADAPTER_DEFAULT, + devType, D3DFMT_A8R8G8B8, TRUE, + type, &qLevelsDepth); + qLevelsDepth = (hr == D3D_OK) ? qLevelsDepth : 0; + qLevels = min(qLevels,qLevelsDepth); + if (qLevels > 0) { + break; + } + } + qLevels = 0; + IDirect3DDevice9* d3d9Device; + D3DPRESENT_PARAMETERS pres; + memset(&pres, 0, sizeof(pres)); + pres.EnableAutoDepthStencil = TRUE; + pres.AutoDepthStencilFormat = D3DFMT_D24S8; + pres.BackBufferCount = 2; + pres.BackBufferFormat = D3DFMT_A8R8G8B8; + pres.BackBufferHeight = 0; + pres.BackBufferWidth = 0; + if (qLevels > 0) { + pres.MultiSampleType = type; + pres.MultiSampleQuality = qLevels-1; + } else { + pres.MultiSampleType = D3DMULTISAMPLE_NONE; + pres.MultiSampleQuality = 0; + } + pres.SwapEffect = D3DSWAPEFFECT_DISCARD; + pres.Windowed = TRUE; + pres.hDeviceWindow = hwnd; + pres.PresentationInterval = 1; + pres.Flags = 0; + hr = d3d9->CreateDevice(D3DADAPTER_DEFAULT, + devType, + hwnd, + D3DCREATE_HARDWARE_VERTEXPROCESSING, + &pres, + &d3d9Device); + D3DERR_INVALIDCALL; + if (SUCCEEDED(hr)) { + d3d9Device->Clear(0, NULL, D3DCLEAR_TARGET, 0xFFFFFFFF, 0, 0); + return d3d9Device; + } + return NULL; +} + +// This needs some improvement. D3D doesn't have the same notion of attach/detach +// as GL. However, just allowing GDI to write to the window after creating the +// D3D device seems to work. +// We need to handle resizing. On XP and earlier Reset() will trash all our textures +// so we would need to inform the SkGpu/caches or just recreate them. On Vista+ we +// could use an IDirect3DDevice9Ex and call ResetEx() to resize without trashing +// everything. Currently we do nothing and the D3D9 image gets stretched/compressed +// when resized. + +bool SkOSWindow::attachD3D9() { + if (NULL == fD3D9Device) { + fD3D9Device = (void*) create_d3d9_device((HWND)fHWND); + } + if (NULL != fD3D9Device) { + ((IDirect3DDevice9*)fD3D9Device)->BeginScene(); + fD3D9Attached = true; + } + return fD3D9Attached; +} + +void SkOSWindow::detachD3D9() { + if (NULL != fD3D9Device) { + ((IDirect3DDevice9*)fD3D9Device)->EndScene(); + } + fD3D9Attached = false; +} + +void SkOSWindow::presentD3D9() { + if (NULL != fD3D9Device) { + HRESULT hr; + hr = ((IDirect3DDevice9*)fD3D9Device)->EndScene(); + SkASSERT(SUCCEEDED(hr)); + hr = ((IDirect3DDevice9*)d3d9Device())->Present(NULL, NULL, NULL, NULL); + SkASSERT(SUCCEEDED(hr)); + hr = ((IDirect3DDevice9*)fD3D9Device)->Clear(0,NULL,D3DCLEAR_TARGET | + D3DCLEAR_STENCIL, 0x0, 0, + 0); + SkASSERT(SUCCEEDED(hr)); + hr = ((IDirect3DDevice9*)fD3D9Device)->BeginScene(); + SkASSERT(SUCCEEDED(hr)); + } +} + + +#endif diff --git a/src/utils/win/SkWGL_win.cpp b/src/utils/win/SkWGL_win.cpp new file mode 100644 index 000000000..7cf410a00 --- /dev/null +++ b/src/utils/win/SkWGL_win.cpp @@ -0,0 +1,184 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkWGL.h" + +bool SkWGLExtensions::hasExtension(HDC dc, const char* ext) const { + if (NULL == this->fGetExtensionsString) { + return false; + } + if (!strcmp("WGL_ARB_extensions_string", ext)) { + return true; + } + const char* extensionString = this->getExtensionsString(dc); + int extLength = strlen(ext); + + while (true) { + int n = strcspn(extensionString, " "); + if (n == extLength && 0 == strncmp(ext, extensionString, n)) { + return true; + } + if (0 == extensionString[n]) { + return false; + } + extensionString += n+1; + } + + return false; +} + +const char* SkWGLExtensions::getExtensionsString(HDC hdc) const { + return fGetExtensionsString(hdc); +} + +BOOL SkWGLExtensions::choosePixelFormat(HDC hdc, + const int* piAttribIList, + const FLOAT* pfAttribFList, + UINT nMaxFormats, + int* piFormats, + UINT* nNumFormats) const { + return fChoosePixelFormat(hdc, piAttribIList, pfAttribFList, + nMaxFormats, piFormats, nNumFormats); +} + +BOOL SkWGLExtensions::getPixelFormatAttribiv(HDC hdc, + int iPixelFormat, + int iLayerPlane, + UINT nAttributes, + const int *piAttributes, + int *piValues) const { + return fGetPixelFormatAttribiv(hdc, iPixelFormat, iLayerPlane, + nAttributes, piAttributes, piValues); +} + +BOOL SkWGLExtensions::getPixelFormatAttribfv(HDC hdc, + int iPixelFormat, + int iLayerPlane, + UINT nAttributes, + const int *piAttributes, + float *pfValues) const { + return fGetPixelFormatAttribfv(hdc, iPixelFormat, iLayerPlane, + nAttributes, piAttributes, pfValues); +} +HGLRC SkWGLExtensions::createContextAttribs(HDC hDC, + HGLRC hShareContext, + const int *attribList) const { + return fCreateContextAttribs(hDC, hShareContext, attribList); +} + +namespace { + +#if defined(UNICODE) + #define STR_LIT(X) L## #X +#else + #define STR_LIT(X) #X +#endif + +#define DUMMY_CLASS STR_LIT("DummyClass") + +HWND create_dummy_window() { + HMODULE module = GetModuleHandle(NULL); + HWND dummy; + RECT windowRect; + windowRect.left = 0; + windowRect.right = 8; + windowRect.top = 0; + windowRect.bottom = 8; + + WNDCLASS wc; + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.lpfnWndProc = (WNDPROC) DefWindowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = module; + wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = DUMMY_CLASS; + + if(!RegisterClass(&wc)) { + return 0; + } + + DWORD style, exStyle; + exStyle = WS_EX_CLIENTEDGE; + style = WS_SYSMENU; + + AdjustWindowRectEx(&windowRect, style, false, exStyle); + if(!(dummy = CreateWindowEx(exStyle, + DUMMY_CLASS, + STR_LIT("DummyWindow"), + WS_CLIPSIBLINGS | WS_CLIPCHILDREN | style, + 0, 0, + windowRect.right-windowRect.left, + windowRect.bottom-windowRect.top, + NULL, NULL, + module, + NULL))) { + UnregisterClass(DUMMY_CLASS, module); + return NULL; + } + ShowWindow(dummy, SW_HIDE); + + return dummy; +} + +void destroy_dummy_window(HWND dummy) { + DestroyWindow(dummy); + HMODULE module = GetModuleHandle(NULL); + UnregisterClass(DUMMY_CLASS, module); +} +} + +#define GET_PROC(NAME, SUFFIX) f##NAME = \ + (##NAME##Proc) wglGetProcAddress("wgl" #NAME #SUFFIX) + +SkWGLExtensions::SkWGLExtensions() + : fGetExtensionsString(NULL) + , fChoosePixelFormat(NULL) + , fGetPixelFormatAttribfv(NULL) + , fGetPixelFormatAttribiv(NULL) + , fCreateContextAttribs(NULL) { + HDC prevDC = wglGetCurrentDC(); + HGLRC prevGLRC = wglGetCurrentContext(); + + PIXELFORMATDESCRIPTOR dummyPFD; + + ZeroMemory(&dummyPFD, sizeof(dummyPFD)); + dummyPFD.nSize = sizeof(dummyPFD); + dummyPFD.nVersion = 1; + dummyPFD.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; + dummyPFD.iPixelType = PFD_TYPE_RGBA; + dummyPFD.cColorBits = 32; + dummyPFD.cDepthBits = 0; + dummyPFD.cStencilBits = 8; + dummyPFD.iLayerType = PFD_MAIN_PLANE; + HWND dummyWND = create_dummy_window(); + if (dummyWND) { + HDC dummyDC = GetDC(dummyWND); + int dummyFormat = ChoosePixelFormat(dummyDC, &dummyPFD); + SetPixelFormat(dummyDC, dummyFormat, &dummyPFD); + HGLRC dummyGLRC = wglCreateContext(dummyDC); + SkASSERT(dummyGLRC); + wglMakeCurrent(dummyDC, dummyGLRC); + + GET_PROC(GetExtensionsString, ARB); + GET_PROC(ChoosePixelFormat, ARB); + GET_PROC(GetPixelFormatAttribiv, ARB); + GET_PROC(GetPixelFormatAttribfv, ARB); + GET_PROC(CreateContextAttribs, ARB); + + wglMakeCurrent(dummyDC, NULL); + wglDeleteContext(dummyGLRC); + destroy_dummy_window(dummyWND); + } + + wglMakeCurrent(prevDC, prevGLRC); +}