/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the QtOpenGL module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include <qgl.h> | |
#include <qlist.h> | |
#include <qmap.h> | |
#include <qpixmap.h> | |
#include <qevent.h> | |
#include <private/qgl_p.h> | |
#include <qcolormap.h> | |
#include <qvarlengtharray.h> | |
#include <qdebug.h> | |
#include <qcolor.h> | |
#include <qt_windows.h> | |
typedef bool (APIENTRY *PFNWGLGETPIXELFORMATATTRIBIVARB)(HDC hdc, | |
int iPixelFormat, | |
int iLayerPlane, | |
uint nAttributes, | |
const int *piAttributes, | |
int *piValues); | |
typedef bool (APIENTRY *PFNWGLCHOOSEPIXELFORMATARB)(HDC hdc, | |
const int *piAttribList, | |
const float *pfAttribFList, | |
uint nMaxFormats, | |
int *piFormats, | |
UINT *nNumFormats); | |
#ifndef WGL_ARB_multisample | |
#define WGL_SAMPLE_BUFFERS_ARB 0x2041 | |
#define WGL_SAMPLES_ARB 0x2042 | |
#endif | |
#ifndef WGL_ARB_pixel_format | |
#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 | |
#define WGL_DRAW_TO_WINDOW_ARB 0x2001 | |
#define WGL_DRAW_TO_BITMAP_ARB 0x2002 | |
#define WGL_ACCELERATION_ARB 0x2003 | |
#define WGL_NEED_PALETTE_ARB 0x2004 | |
#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005 | |
#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006 | |
#define WGL_SWAP_METHOD_ARB 0x2007 | |
#define WGL_NUMBER_OVERLAYS_ARB 0x2008 | |
#define WGL_NUMBER_UNDERLAYS_ARB 0x2009 | |
#define WGL_TRANSPARENT_ARB 0x200A | |
#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037 | |
#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038 | |
#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039 | |
#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A | |
#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B | |
#define WGL_SHARE_DEPTH_ARB 0x200C | |
#define WGL_SHARE_STENCIL_ARB 0x200D | |
#define WGL_SHARE_ACCUM_ARB 0x200E | |
#define WGL_SUPPORT_GDI_ARB 0x200F | |
#define WGL_SUPPORT_OPENGL_ARB 0x2010 | |
#define WGL_DOUBLE_BUFFER_ARB 0x2011 | |
#define WGL_STEREO_ARB 0x2012 | |
#define WGL_PIXEL_TYPE_ARB 0x2013 | |
#define WGL_COLOR_BITS_ARB 0x2014 | |
#define WGL_RED_BITS_ARB 0x2015 | |
#define WGL_RED_SHIFT_ARB 0x2016 | |
#define WGL_GREEN_BITS_ARB 0x2017 | |
#define WGL_GREEN_SHIFT_ARB 0x2018 | |
#define WGL_BLUE_BITS_ARB 0x2019 | |
#define WGL_BLUE_SHIFT_ARB 0x201A | |
#define WGL_ALPHA_BITS_ARB 0x201B | |
#define WGL_ALPHA_SHIFT_ARB 0x201C | |
#define WGL_ACCUM_BITS_ARB 0x201D | |
#define WGL_ACCUM_RED_BITS_ARB 0x201E | |
#define WGL_ACCUM_GREEN_BITS_ARB 0x201F | |
#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 | |
#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 | |
#define WGL_DEPTH_BITS_ARB 0x2022 | |
#define WGL_STENCIL_BITS_ARB 0x2023 | |
#define WGL_AUX_BUFFERS_ARB 0x2024 | |
#define WGL_NO_ACCELERATION_ARB 0x2025 | |
#define WGL_GENERIC_ACCELERATION_ARB 0x2026 | |
#define WGL_FULL_ACCELERATION_ARB 0x2027 | |
#define WGL_SWAP_EXCHANGE_ARB 0x2028 | |
#define WGL_SWAP_COPY_ARB 0x2029 | |
#define WGL_SWAP_UNDEFINED_ARB 0x202A | |
#define WGL_TYPE_RGBA_ARB 0x202B | |
#define WGL_TYPE_COLORINDEX_ARB 0x202C | |
#endif | |
#ifndef WGL_ARB_create_context | |
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 | |
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 | |
#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 | |
#define WGL_CONTEXT_FLAGS_ARB 0x2094 | |
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 | |
#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001 | |
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 | |
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x0001 | |
#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x0002 | |
// Error codes returned by GetLastError(). | |
#define ERROR_INVALID_VERSION_ARB 0x2095 | |
#define ERROR_INVALID_PROFILE_ARB 0x2096 | |
#endif | |
#ifndef GL_VERSION_3_2 | |
#define GL_CONTEXT_PROFILE_MASK 0x9126 | |
#define GL_MAJOR_VERSION 0x821B | |
#define GL_MINOR_VERSION 0x821C | |
#define GL_NUM_EXTENSIONS 0x821D | |
#define GL_CONTEXT_FLAGS 0x821E | |
#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001 | |
#endif | |
QT_BEGIN_NAMESPACE | |
class QGLCmapPrivate | |
{ | |
public: | |
QGLCmapPrivate() : count(1) { } | |
void ref() { ++count; } | |
bool deref() { return !--count; } | |
uint count; | |
enum AllocState{ UnAllocated = 0, Allocated = 0x01, Reserved = 0x02 }; | |
int maxSize; | |
QVector<uint> colorArray; | |
QVector<quint8> allocArray; | |
QVector<quint8> contextArray; | |
QMap<uint,int> colorMap; | |
}; | |
/***************************************************************************** | |
QColorMap class - temporarily here, until it is ready for prime time | |
*****************************************************************************/ | |
/**************************************************************************** | |
** | |
** Definition of QColorMap class | |
** | |
****************************************************************************/ | |
class QGLCmapPrivate; | |
class /*Q_EXPORT*/ QGLCmap | |
{ | |
public: | |
enum Flags { Reserved = 0x01 }; | |
QGLCmap(int maxSize = 256); | |
QGLCmap(const QGLCmap& map); | |
~QGLCmap(); | |
QGLCmap& operator=(const QGLCmap& map); | |
// isEmpty and/or isNull ? | |
int size() const; | |
int maxSize() const; | |
void resize(int newSize); | |
int find(QRgb color) const; | |
int findNearest(QRgb color) const; | |
int allocate(QRgb color, uint flags = 0, quint8 context = 0); | |
void setEntry(int idx, QRgb color, uint flags = 0, quint8 context = 0); | |
const QRgb* colors() const; | |
private: | |
void detach(); | |
QGLCmapPrivate* d; | |
}; | |
QGLCmap::QGLCmap(int maxSize) // add a bool prealloc? | |
{ | |
d = new QGLCmapPrivate; | |
d->maxSize = maxSize; | |
} | |
QGLCmap::QGLCmap(const QGLCmap& map) | |
{ | |
d = map.d; | |
d->ref(); | |
} | |
QGLCmap::~QGLCmap() | |
{ | |
if (d && d->deref()) | |
delete d; | |
d = 0; | |
} | |
QGLCmap& QGLCmap::operator=(const QGLCmap& map) | |
{ | |
map.d->ref(); | |
if (d->deref()) | |
delete d; | |
d = map.d; | |
return *this; | |
} | |
int QGLCmap::size() const | |
{ | |
return d->colorArray.size(); | |
} | |
int QGLCmap::maxSize() const | |
{ | |
return d->maxSize; | |
} | |
void QGLCmap::detach() | |
{ | |
if (d->count != 1) { | |
d->deref(); | |
QGLCmapPrivate* newd = new QGLCmapPrivate; | |
newd->maxSize = d->maxSize; | |
newd->colorArray = d->colorArray; | |
newd->allocArray = d->allocArray; | |
newd->contextArray = d->contextArray; | |
newd->colorArray.detach(); | |
newd->allocArray.detach(); | |
newd->contextArray.detach(); | |
newd->colorMap = d->colorMap; | |
d = newd; | |
} | |
} | |
void QGLCmap::resize(int newSize) | |
{ | |
if (newSize < 0 || newSize > d->maxSize) { | |
qWarning("QGLCmap::resize(): size out of range"); | |
return; | |
} | |
int oldSize = size(); | |
detach(); | |
//if shrinking; remove the lost elems from colorMap | |
d->colorArray.resize(newSize); | |
d->allocArray.resize(newSize); | |
d->contextArray.resize(newSize); | |
if (newSize > oldSize) { | |
memset(d->allocArray.data() + oldSize, 0, newSize - oldSize); | |
memset(d->contextArray.data() + oldSize, 0, newSize - oldSize); | |
} | |
} | |
int QGLCmap::find(QRgb color) const | |
{ | |
QMap<uint,int>::ConstIterator it = d->colorMap.find(color); | |
if (it != d->colorMap.end()) | |
return *it; | |
return -1; | |
} | |
int QGLCmap::findNearest(QRgb color) const | |
{ | |
int idx = find(color); | |
if (idx >= 0) | |
return idx; | |
int mapSize = size(); | |
int mindist = 200000; | |
int r = qRed(color); | |
int g = qGreen(color); | |
int b = qBlue(color); | |
int rx, gx, bx, dist; | |
for (int i=0; i < mapSize; i++) { | |
if (!(d->allocArray[i] & QGLCmapPrivate::Allocated)) | |
continue; | |
QRgb ci = d->colorArray[i]; | |
rx = r - qRed(ci); | |
gx = g - qGreen(ci); | |
bx = b - qBlue(ci); | |
dist = rx*rx + gx*gx + bx*bx; // calculate distance | |
if (dist < mindist) { // minimal? | |
mindist = dist; | |
idx = i; | |
} | |
} | |
return idx; | |
} | |
// Does not always allocate; returns existing c idx if found | |
int QGLCmap::allocate(QRgb color, uint flags, quint8 context) | |
{ | |
int idx = find(color); | |
if (idx >= 0) | |
return idx; | |
int mapSize = d->colorArray.size(); | |
int newIdx = d->allocArray.indexOf(QGLCmapPrivate::UnAllocated); | |
if (newIdx < 0) { // Must allocate more room | |
if (mapSize < d->maxSize) { | |
newIdx = mapSize; | |
mapSize++; | |
resize(mapSize); | |
} | |
else { | |
//# add a bool param that says what to do in case no more room - | |
// fail (-1) or return nearest? | |
return -1; | |
} | |
} | |
d->colorArray[newIdx] = color; | |
if (flags & QGLCmap::Reserved) { | |
d->allocArray[newIdx] = QGLCmapPrivate::Reserved; | |
} | |
else { | |
d->allocArray[newIdx] = QGLCmapPrivate::Allocated; | |
d->colorMap.insert(color, newIdx); | |
} | |
d->contextArray[newIdx] = context; | |
return newIdx; | |
} | |
void QGLCmap::setEntry(int idx, QRgb color, uint flags, quint8 context) | |
{ | |
if (idx < 0 || idx >= d->maxSize) { | |
qWarning("QGLCmap::set(): Index out of range"); | |
return; | |
} | |
detach(); | |
int mapSize = size(); | |
if (idx >= mapSize) { | |
mapSize = idx + 1; | |
resize(mapSize); | |
} | |
d->colorArray[idx] = color; | |
if (flags & QGLCmap::Reserved) { | |
d->allocArray[idx] = QGLCmapPrivate::Reserved; | |
} | |
else { | |
d->allocArray[idx] = QGLCmapPrivate::Allocated; | |
d->colorMap.insert(color, idx); | |
} | |
d->contextArray[idx] = context; | |
} | |
const QRgb* QGLCmap::colors() const | |
{ | |
return d->colorArray.data(); | |
} | |
/***************************************************************************** | |
QGLFormat Win32/WGL-specific code | |
*****************************************************************************/ | |
void qwglError(const char* method, const char* func) | |
{ | |
#ifndef QT_NO_DEBUG | |
char* lpMsgBuf; | |
FormatMessageA( | |
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, | |
0, GetLastError(), | |
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |
(char*) &lpMsgBuf, 0, 0); | |
qWarning("%s : %s failed: %s", method, func, lpMsgBuf); | |
LocalFree(lpMsgBuf); | |
#else | |
Q_UNUSED(method); | |
Q_UNUSED(func); | |
#endif | |
} | |
bool QGLFormat::hasOpenGL() | |
{ | |
return true; | |
} | |
static bool opengl32dll = false; | |
bool QGLFormat::hasOpenGLOverlays() | |
{ | |
// workaround for matrox driver: | |
// make a cheap call to opengl to force loading of DLL | |
if (!opengl32dll) { | |
GLint params; | |
glGetIntegerv(GL_DEPTH_BITS, ¶ms); | |
opengl32dll = true; | |
} | |
static bool checkDone = false; | |
static bool hasOl = false; | |
if (!checkDone) { | |
checkDone = true; | |
HDC display_dc = GetDC(0); | |
int pfiMax = DescribePixelFormat(display_dc, 0, 0, NULL); | |
PIXELFORMATDESCRIPTOR pfd; | |
for (int pfi = 1; pfi <= pfiMax; pfi++) { | |
DescribePixelFormat(display_dc, pfi, sizeof(PIXELFORMATDESCRIPTOR), &pfd); | |
if ((pfd.bReserved & 0x0f) && (pfd.dwFlags & PFD_SUPPORT_OPENGL)) { | |
// This format has overlays/underlays | |
LAYERPLANEDESCRIPTOR lpd; | |
wglDescribeLayerPlane(display_dc, pfi, 1, | |
sizeof(LAYERPLANEDESCRIPTOR), &lpd); | |
if (lpd.dwFlags & LPD_SUPPORT_OPENGL) { | |
hasOl = true; | |
break; | |
} | |
} | |
} | |
ReleaseDC(0, display_dc); | |
} | |
return hasOl; | |
} | |
/***************************************************************************** | |
QGLContext Win32/WGL-specific code | |
*****************************************************************************/ | |
static uchar qgl_rgb_palette_comp(int idx, uint nbits, uint shift) | |
{ | |
const uchar map_3_to_8[8] = { | |
0, 0111>>1, 0222>>1, 0333>>1, 0444>>1, 0555>>1, 0666>>1, 0377 | |
}; | |
const uchar map_2_to_8[4] = { | |
0, 0x55, 0xaa, 0xff | |
}; | |
const uchar map_1_to_8[2] = { | |
0, 255 | |
}; | |
uchar val = (uchar) (idx >> shift); | |
uchar res = 0; | |
switch (nbits) { | |
case 1: | |
val &= 0x1; | |
res = map_1_to_8[val]; | |
break; | |
case 2: | |
val &= 0x3; | |
res = map_2_to_8[val]; | |
break; | |
case 3: | |
val &= 0x7; | |
res = map_3_to_8[val]; | |
break; | |
default: | |
res = 0; | |
} | |
return res; | |
} | |
static QRgb* qgl_create_rgb_palette(const PIXELFORMATDESCRIPTOR* pfd) | |
{ | |
if ((pfd->iPixelType != PFD_TYPE_RGBA) || | |
!(pfd->dwFlags & PFD_NEED_PALETTE) || | |
(pfd->cColorBits != 8)) | |
return 0; | |
int numEntries = 1 << pfd->cColorBits; | |
QRgb* pal = new QRgb[numEntries]; | |
for (int i = 0; i < numEntries; i++) { | |
int r = qgl_rgb_palette_comp(i, pfd->cRedBits, pfd->cRedShift); | |
int g = qgl_rgb_palette_comp(i, pfd->cGreenBits, pfd->cGreenShift); | |
int b = qgl_rgb_palette_comp(i, pfd->cBlueBits, pfd->cBlueShift); | |
pal[i] = qRgb(r, g, b); | |
} | |
const int syscol_indices[12] = { | |
3, 24, 27, 64, 67, 88, 173, 181, 236, 247, 164, 91 | |
}; | |
const uint syscols[20] = { | |
0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, | |
0x008080, 0xc0c0c0, 0xc0dcc0, 0xa6caf0, 0xfffbf0, 0xa0a0a4, | |
0x808080, 0xff0000, 0x00ff00, 0xffff00, 0x0000ff, 0xff00ff, | |
0x00ffff, 0xffffff | |
}; // colors #1 - #12 are not present in pal; gets added below | |
if ((pfd->cColorBits == 8) && | |
(pfd->cRedBits == 3) && (pfd->cRedShift == 0) && | |
(pfd->cGreenBits == 3) && (pfd->cGreenShift == 3) && | |
(pfd->cBlueBits == 2) && (pfd->cBlueShift == 6)) { | |
for (int j = 0 ; j < 12 ; j++) | |
pal[syscol_indices[j]] = QRgb(syscols[j+1]); | |
} | |
return pal; | |
} | |
static QGLFormat pfdToQGLFormat(const PIXELFORMATDESCRIPTOR* pfd) | |
{ | |
QGLFormat fmt; | |
fmt.setDoubleBuffer(pfd->dwFlags & PFD_DOUBLEBUFFER); | |
fmt.setDepth(pfd->cDepthBits); | |
if (fmt.depth()) | |
fmt.setDepthBufferSize(pfd->cDepthBits); | |
fmt.setRgba(pfd->iPixelType == PFD_TYPE_RGBA); | |
fmt.setRedBufferSize(pfd->cRedBits); | |
fmt.setGreenBufferSize(pfd->cGreenBits); | |
fmt.setBlueBufferSize(pfd->cBlueBits); | |
fmt.setAlpha(pfd->cAlphaBits); | |
if (fmt.alpha()) | |
fmt.setAlphaBufferSize(pfd->cAlphaBits); | |
fmt.setAccum(pfd->cAccumBits); | |
if (fmt.accum()) | |
fmt.setAccumBufferSize(pfd->cAccumRedBits); | |
fmt.setStencil(pfd->cStencilBits); | |
if (fmt.stencil()) | |
fmt.setStencilBufferSize(pfd->cStencilBits); | |
fmt.setStereo(pfd->dwFlags & PFD_STEREO); | |
fmt.setDirectRendering((pfd->dwFlags & PFD_GENERIC_ACCELERATED) || | |
!(pfd->dwFlags & PFD_GENERIC_FORMAT)); | |
fmt.setOverlay((pfd->bReserved & 0x0f) != 0); | |
return fmt; | |
} | |
/* | |
NB! requires a current GL context to work | |
*/ | |
QGLFormat pfiToQGLFormat(HDC hdc, int pfi) | |
{ | |
QGLFormat fmt; | |
QVarLengthArray<int> iAttributes(40); | |
QVarLengthArray<int> iValues(40); | |
int i = 0; | |
bool has_sample_buffers = QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers; | |
iAttributes[i++] = WGL_DOUBLE_BUFFER_ARB; // 0 | |
iAttributes[i++] = WGL_DEPTH_BITS_ARB; // 1 | |
iAttributes[i++] = WGL_PIXEL_TYPE_ARB; // 2 | |
iAttributes[i++] = WGL_RED_BITS_ARB; // 3 | |
iAttributes[i++] = WGL_GREEN_BITS_ARB; // 4 | |
iAttributes[i++] = WGL_BLUE_BITS_ARB; // 5 | |
iAttributes[i++] = WGL_ALPHA_BITS_ARB; // 6 | |
iAttributes[i++] = WGL_ACCUM_BITS_ARB; // 7 | |
iAttributes[i++] = WGL_STENCIL_BITS_ARB; // 8 | |
iAttributes[i++] = WGL_STEREO_ARB; // 9 | |
iAttributes[i++] = WGL_ACCELERATION_ARB; // 10 | |
iAttributes[i++] = WGL_NUMBER_OVERLAYS_ARB; // 11 | |
if (has_sample_buffers) { | |
iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB; // 12 | |
iAttributes[i++] = WGL_SAMPLES_ARB; // 13 | |
} | |
PFNWGLGETPIXELFORMATATTRIBIVARB wglGetPixelFormatAttribivARB = | |
(PFNWGLGETPIXELFORMATATTRIBIVARB) wglGetProcAddress("wglGetPixelFormatAttribivARB"); | |
if (wglGetPixelFormatAttribivARB | |
&& wglGetPixelFormatAttribivARB(hdc, pfi, 0, i, | |
iAttributes.constData(), | |
iValues.data())) | |
{ | |
fmt.setDoubleBuffer(iValues[0]); | |
fmt.setDepth(iValues[1]); | |
if (fmt.depth()) | |
fmt.setDepthBufferSize(iValues[1]); | |
fmt.setRgba(iValues[2] == WGL_TYPE_RGBA_ARB); | |
fmt.setRedBufferSize(iValues[3]); | |
fmt.setGreenBufferSize(iValues[4]); | |
fmt.setBlueBufferSize(iValues[5]); | |
fmt.setAlpha(iValues[6]); | |
if (fmt.alpha()) | |
fmt.setAlphaBufferSize(iValues[6]); | |
fmt.setAccum(iValues[7]); | |
if (fmt.accum()) | |
fmt.setAccumBufferSize(iValues[7]); | |
fmt.setStencil(iValues[8]); | |
if (fmt.stencil()) | |
fmt.setStencilBufferSize(iValues[8]); | |
fmt.setStereo(iValues[9]); | |
if (iValues[10] == WGL_FULL_ACCELERATION_ARB) | |
fmt.setDirectRendering(true); | |
else | |
fmt.setDirectRendering(false); | |
fmt.setOverlay(iValues[11]); | |
if (has_sample_buffers) { | |
fmt.setSampleBuffers(iValues[12]); | |
if (fmt.sampleBuffers()) | |
fmt.setSamples(iValues[13]); | |
} | |
} | |
#if 0 | |
qDebug() << "values for pfi:" << pfi; | |
qDebug() << "doublebuffer 0:" << fmt.doubleBuffer(); | |
qDebug() << "depthbuffer 1:" << fmt.depthBufferSize(); | |
qDebug() << "rgba 2:" << fmt.rgba(); | |
qDebug() << "red size 3:" << fmt.redBufferSize(); | |
qDebug() << "green size 4:" << fmt.greenBufferSize(); | |
qDebug() << "blue size 5:" << fmt.blueBufferSize(); | |
qDebug() << "alpha size 6:" << fmt.alphaBufferSize(); | |
qDebug() << "accum size 7:" << fmt.accumBufferSize(); | |
qDebug() << "stencil size 8:" << fmt.stencilBufferSize(); | |
qDebug() << "stereo 9:" << fmt.stereo(); | |
qDebug() << "direct 10:" << fmt.directRendering(); | |
qDebug() << "has overlays 11:" << fmt.hasOverlay(); | |
qDebug() << "sample buff 12:" << fmt.sampleBuffers(); | |
qDebug() << "num samples 13:" << fmt.samples(); | |
#endif | |
return fmt; | |
} | |
/* | |
QGLTemporaryContext implementation | |
*/ | |
Q_GUI_EXPORT const QString qt_getRegisteredWndClass(); | |
class QGLTemporaryContextPrivate | |
{ | |
public: | |
HDC dmy_pdc; | |
HGLRC dmy_rc; | |
HDC old_dc; | |
HGLRC old_context; | |
WId dmy_id; | |
}; | |
QGLTemporaryContext::QGLTemporaryContext(bool directRendering, QWidget *parent) | |
: d(new QGLTemporaryContextPrivate) | |
{ | |
QString windowClassName = qt_getRegisteredWndClass(); | |
if (parent && !parent->internalWinId()) | |
parent = parent->nativeParentWidget(); | |
d->dmy_id = CreateWindow((const wchar_t *)windowClassName.utf16(), | |
0, 0, 0, 0, 1, 1, | |
parent ? parent->winId() : 0, 0, qWinAppInst(), 0); | |
d->dmy_pdc = GetDC(d->dmy_id); | |
PIXELFORMATDESCRIPTOR dmy_pfd; | |
memset(&dmy_pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); | |
dmy_pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); | |
dmy_pfd.nVersion = 1; | |
dmy_pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW; | |
dmy_pfd.iPixelType = PFD_TYPE_RGBA; | |
if (!directRendering) | |
dmy_pfd.dwFlags |= PFD_GENERIC_FORMAT; | |
int dmy_pf = ChoosePixelFormat(d->dmy_pdc, &dmy_pfd); | |
SetPixelFormat(d->dmy_pdc, dmy_pf, &dmy_pfd); | |
d->dmy_rc = wglCreateContext(d->dmy_pdc); | |
d->old_dc = wglGetCurrentDC(); | |
d->old_context = wglGetCurrentContext(); | |
wglMakeCurrent(d->dmy_pdc, d->dmy_rc); | |
} | |
QGLTemporaryContext::~QGLTemporaryContext() | |
{ | |
wglMakeCurrent(d->dmy_pdc, 0); | |
wglDeleteContext(d->dmy_rc); | |
ReleaseDC(d->dmy_id, d->dmy_pdc); | |
DestroyWindow(d->dmy_id); | |
if (d->old_dc && d->old_context) | |
wglMakeCurrent(d->old_dc, d->old_context); | |
} | |
static bool qgl_create_context(HDC hdc, QGLContextPrivate *d, QGLContextPrivate *shareContext) | |
{ | |
d->rc = 0; | |
typedef HGLRC (APIENTRYP PFNWGLCREATECONTEXTATTRIBSARB)(HDC, HGLRC, const int *); | |
PFNWGLCREATECONTEXTATTRIBSARB wglCreateContextAttribsARB = | |
(PFNWGLCREATECONTEXTATTRIBSARB) wglGetProcAddress("wglCreateContextAttribsARB"); | |
if (wglCreateContextAttribsARB) { | |
int attributes[11]; | |
int attribIndex = 0; | |
const int major = d->reqFormat.majorVersion(); | |
const int minor = d->reqFormat.minorVersion(); | |
attributes[attribIndex++] = WGL_CONTEXT_MAJOR_VERSION_ARB; | |
attributes[attribIndex++] = major; | |
attributes[attribIndex++] = WGL_CONTEXT_MINOR_VERSION_ARB; | |
attributes[attribIndex++] = minor; | |
if (major >= 3 && !d->reqFormat.testOption(QGL::DeprecatedFunctions)) { | |
attributes[attribIndex++] = WGL_CONTEXT_FLAGS_ARB; | |
attributes[attribIndex++] = WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; | |
} | |
if ((major == 3 && minor >= 2) || major > 3) { | |
switch (d->reqFormat.profile()) { | |
case QGLFormat::NoProfile: | |
break; | |
case QGLFormat::CoreProfile: | |
attributes[attribIndex++] = WGL_CONTEXT_PROFILE_MASK_ARB; | |
attributes[attribIndex++] = WGL_CONTEXT_CORE_PROFILE_BIT_ARB; | |
break; | |
case QGLFormat::CompatibilityProfile: | |
attributes[attribIndex++] = WGL_CONTEXT_PROFILE_MASK_ARB; | |
attributes[attribIndex++] = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; | |
break; | |
default: | |
qWarning("QGLContext::chooseContext(): Context profile not supported."); | |
return false; | |
} | |
} | |
if (d->reqFormat.plane() != 0) { | |
attributes[attribIndex++] = WGL_CONTEXT_LAYER_PLANE_ARB; | |
attributes[attribIndex++] = d->reqFormat.plane(); | |
} | |
attributes[attribIndex++] = 0; // Terminate list. | |
d->rc = wglCreateContextAttribsARB(hdc, shareContext && shareContext->valid | |
? shareContext->rc : 0, attributes); | |
if (d->rc) { | |
if (shareContext) | |
shareContext->sharing = d->sharing = true; | |
return true; | |
} | |
} | |
d->rc = wglCreateLayerContext(hdc, d->reqFormat.plane()); | |
if (d->rc && shareContext && shareContext->valid) | |
shareContext->sharing = d->sharing = wglShareLists(shareContext->rc, d->rc); | |
return d->rc != 0; | |
} | |
void QGLContextPrivate::updateFormatVersion() | |
{ | |
const GLubyte *s = glGetString(GL_VERSION); | |
if (!(s && s[0] >= '0' && s[0] <= '9' && s[1] == '.' && s[2] >= '0' && s[2] <= '9')) { | |
if (!s) | |
qWarning("QGLContext::chooseContext(): OpenGL version string is null."); | |
else | |
qWarning("QGLContext::chooseContext(): Unexpected OpenGL version string format."); | |
glFormat.setVersion(0, 0); | |
glFormat.setProfile(QGLFormat::NoProfile); | |
glFormat.setOption(QGL::DeprecatedFunctions); | |
return; | |
} | |
int major = s[0] - '0'; | |
int minor = s[2] - '0'; | |
glFormat.setVersion(major, minor); | |
if (major < 3) { | |
glFormat.setProfile(QGLFormat::NoProfile); | |
glFormat.setOption(QGL::DeprecatedFunctions); | |
} else { | |
GLint value = 0; | |
if (major > 3 || minor >= 2) | |
glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value); | |
switch (value) { | |
case WGL_CONTEXT_CORE_PROFILE_BIT_ARB: | |
glFormat.setProfile(QGLFormat::CoreProfile); | |
break; | |
case WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB: | |
glFormat.setProfile(QGLFormat::CompatibilityProfile); | |
break; | |
default: | |
glFormat.setProfile(QGLFormat::NoProfile); | |
break; | |
} | |
glGetIntegerv(GL_CONTEXT_FLAGS, &value); | |
if (value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) | |
glFormat.setOption(QGL::NoDeprecatedFunctions); | |
else | |
glFormat.setOption(QGL::DeprecatedFunctions); | |
} | |
} | |
bool QGLContext::chooseContext(const QGLContext* shareContext) | |
{ | |
QGLContextPrivate *share = shareContext ? const_cast<QGLContext *>(shareContext)->d_func() : 0; | |
Q_D(QGLContext); | |
// workaround for matrox driver: | |
// make a cheap call to opengl to force loading of DLL | |
if (!opengl32dll) { | |
GLint params; | |
glGetIntegerv(GL_DEPTH_BITS, ¶ms); | |
opengl32dll = true; | |
} | |
bool result = true; | |
HDC myDc; | |
QWidget *widget = 0; | |
if (deviceIsPixmap()) { | |
if (d->glFormat.plane()) | |
return false; // Pixmaps can't have overlay | |
d->win = 0; | |
HDC display_dc = GetDC(0); | |
myDc = d->hbitmap_hdc = CreateCompatibleDC(display_dc); | |
QPixmap *px = static_cast<QPixmap *>(d->paintDevice); | |
BITMAPINFO bmi; | |
memset(&bmi, 0, sizeof(bmi)); | |
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); | |
bmi.bmiHeader.biWidth = px->width(); | |
bmi.bmiHeader.biHeight = px->height(); | |
bmi.bmiHeader.biPlanes = 1; | |
bmi.bmiHeader.biBitCount = 32; | |
bmi.bmiHeader.biCompression = BI_RGB; | |
d->hbitmap = CreateDIBSection(display_dc, &bmi, DIB_RGB_COLORS, 0, 0, 0); | |
SelectObject(myDc, d->hbitmap); | |
ReleaseDC(0, display_dc); | |
} else { | |
widget = static_cast<QWidget *>(d->paintDevice); | |
d->win = widget->winId(); | |
myDc = GetDC(d->win); | |
} | |
// NB! the QGLTemporaryContext object is needed for the | |
// wglGetProcAddress() calls to succeed and are absolutely | |
// necessary - don't remove! | |
QGLTemporaryContext tmp_ctx(d->glFormat.directRendering(), widget); | |
if (!myDc) { | |
qWarning("QGLContext::chooseContext(): Paint device cannot be null"); | |
result = false; | |
goto end; | |
} | |
if (d->glFormat.plane()) { | |
d->pixelFormatId = ((QGLWidget*)d->paintDevice)->context()->d_func()->pixelFormatId; | |
if (!d->pixelFormatId) { // I.e. the glwidget is invalid | |
qWarning("QGLContext::chooseContext(): Cannot create overlay context for invalid widget"); | |
result = false; | |
goto end; | |
} | |
if (!qgl_create_context(myDc, d, share)) { | |
qwglError("QGLContext::chooseContext()", "CreateLayerContext"); | |
result = false; | |
goto end; | |
} | |
LAYERPLANEDESCRIPTOR lpfd; | |
wglDescribeLayerPlane(myDc, d->pixelFormatId, d->glFormat.plane(), sizeof(LAYERPLANEDESCRIPTOR), &lpfd); | |
d->glFormat.setDoubleBuffer(lpfd.dwFlags & LPD_DOUBLEBUFFER); | |
d->glFormat.setDepth(lpfd.cDepthBits); | |
d->glFormat.setRgba(lpfd.iPixelType == PFD_TYPE_RGBA); | |
if (d->glFormat.rgba()) { | |
if (d->glFormat.redBufferSize() != -1) | |
d->glFormat.setRedBufferSize(lpfd.cRedBits); | |
if (d->glFormat.greenBufferSize() != -1) | |
d->glFormat.setGreenBufferSize(lpfd.cGreenBits); | |
if (d->glFormat.blueBufferSize() != -1) | |
d->glFormat.setBlueBufferSize(lpfd.cBlueBits); | |
} | |
d->glFormat.setAlpha(lpfd.cAlphaBits); | |
d->glFormat.setAccum(lpfd.cAccumBits); | |
d->glFormat.setStencil(lpfd.cStencilBits); | |
d->glFormat.setStereo(lpfd.dwFlags & LPD_STEREO); | |
d->glFormat.setDirectRendering(false); | |
if (d->glFormat.depth()) | |
d->glFormat.setDepthBufferSize(lpfd.cDepthBits); | |
if (d->glFormat.alpha()) | |
d->glFormat.setAlphaBufferSize(lpfd.cAlphaBits); | |
if (d->glFormat.accum()) | |
d->glFormat.setAccumBufferSize(lpfd.cAccumRedBits); | |
if (d->glFormat.stencil()) | |
d->glFormat.setStencilBufferSize(lpfd.cStencilBits); | |
if (d->glFormat.rgba()) { | |
if (lpfd.dwFlags & LPD_TRANSPARENT) | |
d->transpColor = QColor(lpfd.crTransparent & 0xff, | |
(lpfd.crTransparent >> 8) & 0xff, | |
(lpfd.crTransparent >> 16) & 0xff); | |
else | |
d->transpColor = QColor(0, 0, 0); | |
} | |
else { | |
if (lpfd.dwFlags & LPD_TRANSPARENT) | |
d->transpColor = QColor(qRgb(1, 2, 3));//, lpfd.crTransparent); | |
else | |
d->transpColor = QColor(qRgb(1, 2, 3));//, 0); | |
d->cmap = new QGLCmap(1 << lpfd.cColorBits); | |
d->cmap->setEntry(lpfd.crTransparent, qRgb(1, 2, 3));//, QGLCmap::Reserved); | |
} | |
} else { | |
PIXELFORMATDESCRIPTOR pfd; | |
PIXELFORMATDESCRIPTOR realPfd; | |
d->pixelFormatId = choosePixelFormat(&pfd, myDc); | |
if (d->pixelFormatId == 0) { | |
qwglError("QGLContext::chooseContext()", "ChoosePixelFormat"); | |
result = false; | |
goto end; | |
} | |
bool overlayRequested = d->glFormat.hasOverlay(); | |
DescribePixelFormat(myDc, d->pixelFormatId, sizeof(PIXELFORMATDESCRIPTOR), &realPfd); | |
if (!deviceIsPixmap() && wglGetProcAddress("wglGetPixelFormatAttribivARB")) | |
d->glFormat = pfiToQGLFormat(myDc, d->pixelFormatId); | |
else | |
d->glFormat = pfdToQGLFormat(&realPfd); | |
d->glFormat.setOverlay(d->glFormat.hasOverlay() && overlayRequested); | |
if (deviceIsPixmap() && !(realPfd.dwFlags & PFD_DRAW_TO_BITMAP)) { | |
qWarning("QGLContext::chooseContext(): Failed to get pixmap rendering context."); | |
result = false; | |
goto end; | |
} | |
if (deviceIsPixmap() && | |
(((QPixmap*)d->paintDevice)->depth() != realPfd.cColorBits)) { | |
qWarning("QGLContext::chooseContext(): Failed to get pixmap rendering context of suitable depth."); | |
result = false; | |
goto end; | |
} | |
if (!SetPixelFormat(myDc, d->pixelFormatId, &realPfd)) { | |
qwglError("QGLContext::chooseContext()", "SetPixelFormat"); | |
result = false; | |
goto end; | |
} | |
if (!qgl_create_context(myDc, d, share)) { | |
qwglError("QGLContext::chooseContext()", "wglCreateContext"); | |
result = false; | |
goto end; | |
} | |
if(!deviceIsPixmap()) { | |
QRgb* pal = qgl_create_rgb_palette(&realPfd); | |
if (pal) { | |
QGLColormap cmap; | |
cmap.setEntries(256, pal); | |
((QGLWidget*)d->paintDevice)->setColormap(cmap); | |
delete[] pal; | |
} | |
} | |
} | |
end: | |
// vblanking | |
wglMakeCurrent(myDc, d->rc); | |
if (d->rc) | |
d->updateFormatVersion(); | |
typedef BOOL (APIENTRYP PFNWGLSWAPINTERVALEXT) (int interval); | |
typedef int (APIENTRYP PFNWGLGETSWAPINTERVALEXT) (void); | |
PFNWGLSWAPINTERVALEXT wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXT) wglGetProcAddress("wglSwapIntervalEXT"); | |
PFNWGLGETSWAPINTERVALEXT wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXT) wglGetProcAddress("wglGetSwapIntervalEXT"); | |
if (wglSwapIntervalEXT && wglGetSwapIntervalEXT) { | |
if (d->reqFormat.swapInterval() != -1) | |
wglSwapIntervalEXT(d->reqFormat.swapInterval()); | |
d->glFormat.setSwapInterval(wglGetSwapIntervalEXT()); | |
} | |
if (d->win) | |
ReleaseDC(d->win, myDc); | |
return result; | |
} | |
static bool qLogEq(bool a, bool b) | |
{ | |
return (((!a) && (!b)) || (a && b)); | |
} | |
/* | |
See qgl.cpp for qdoc comment. | |
*/ | |
int QGLContext::choosePixelFormat(void* dummyPfd, HDC pdc) | |
{ | |
Q_D(QGLContext); | |
// workaround for matrox driver: | |
// make a cheap call to opengl to force loading of DLL | |
if (!opengl32dll) { | |
GLint params; | |
glGetIntegerv(GL_DEPTH_BITS, ¶ms); | |
opengl32dll = true; | |
} | |
PFNWGLCHOOSEPIXELFORMATARB wglChoosePixelFormatARB = | |
(PFNWGLCHOOSEPIXELFORMATARB) wglGetProcAddress("wglChoosePixelFormatARB"); | |
int chosenPfi = 0; | |
if (!deviceIsPixmap() && wglChoosePixelFormatARB) { | |
bool valid; | |
int pixelFormat = 0; | |
uint numFormats = 0; | |
QVarLengthArray<int> iAttributes(40); | |
int i = 0; | |
iAttributes[i++] = WGL_ACCELERATION_ARB; | |
if (d->glFormat.directRendering()) | |
iAttributes[i++] = WGL_FULL_ACCELERATION_ARB; | |
else | |
iAttributes[i++] = WGL_NO_ACCELERATION_ARB; | |
iAttributes[i++] = WGL_SUPPORT_OPENGL_ARB; | |
iAttributes[i++] = TRUE; | |
iAttributes[i++] = WGL_DRAW_TO_WINDOW_ARB; | |
iAttributes[i++] = TRUE; | |
iAttributes[i++] = WGL_COLOR_BITS_ARB; | |
iAttributes[i++] = 24; | |
iAttributes[i++] = WGL_DOUBLE_BUFFER_ARB; | |
iAttributes[i++] = d->glFormat.doubleBuffer(); | |
if (d->glFormat.stereo()) { | |
iAttributes[i++] = WGL_STEREO_ARB; | |
iAttributes[i++] = TRUE; | |
} | |
if (d->glFormat.depth()) { | |
iAttributes[i++] = WGL_DEPTH_BITS_ARB; | |
iAttributes[i++] = d->glFormat.depthBufferSize() == -1 ? 24 : d->glFormat.depthBufferSize(); | |
} | |
iAttributes[i++] = WGL_PIXEL_TYPE_ARB; | |
if (d->glFormat.rgba()) { | |
iAttributes[i++] = WGL_TYPE_RGBA_ARB; | |
if (d->glFormat.redBufferSize() != -1) { | |
iAttributes[i++] = WGL_RED_BITS_ARB; | |
iAttributes[i++] = d->glFormat.redBufferSize(); | |
} | |
if (d->glFormat.greenBufferSize() != -1) { | |
iAttributes[i++] = WGL_GREEN_BITS_ARB; | |
iAttributes[i++] = d->glFormat.greenBufferSize(); | |
} | |
if (d->glFormat.blueBufferSize() != -1) { | |
iAttributes[i++] = WGL_BLUE_BITS_ARB; | |
iAttributes[i++] = d->glFormat.blueBufferSize(); | |
} | |
} else { | |
iAttributes[i++] = WGL_TYPE_COLORINDEX_ARB; | |
} | |
if (d->glFormat.alpha()) { | |
iAttributes[i++] = WGL_ALPHA_BITS_ARB; | |
iAttributes[i++] = d->glFormat.alphaBufferSize() == -1 ? 8 : d->glFormat.alphaBufferSize(); | |
} | |
if (d->glFormat.accum()) { | |
iAttributes[i++] = WGL_ACCUM_BITS_ARB; | |
iAttributes[i++] = d->glFormat.accumBufferSize() == -1 ? 16 : d->glFormat.accumBufferSize(); | |
} | |
if (d->glFormat.stencil()) { | |
iAttributes[i++] = WGL_STENCIL_BITS_ARB; | |
iAttributes[i++] = d->glFormat.stencilBufferSize() == -1 ? 8 : d->glFormat.stencilBufferSize(); | |
} | |
if (d->glFormat.hasOverlay()) { | |
iAttributes[i++] = WGL_NUMBER_OVERLAYS_ARB; | |
iAttributes[i++] = 1; | |
} | |
int si = 0; | |
bool trySampleBuffers = QGLExtensions::glExtensions() & QGLExtensions::SampleBuffers; | |
if (trySampleBuffers && d->glFormat.sampleBuffers()) { | |
iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB; | |
iAttributes[i++] = TRUE; | |
iAttributes[i++] = WGL_SAMPLES_ARB; | |
si = i; | |
iAttributes[i++] = d->glFormat.samples() == -1 ? 4 : d->glFormat.samples(); | |
} | |
iAttributes[i] = 0; | |
do { | |
valid = wglChoosePixelFormatARB(pdc, iAttributes.constData(), 0, 1, | |
&pixelFormat, &numFormats); | |
if (trySampleBuffers && (!valid || numFormats < 1) && d->glFormat.sampleBuffers()) | |
iAttributes[si] /= 2; // try different no. samples - we aim for the best one | |
else | |
break; | |
} while ((!valid || numFormats < 1) && iAttributes[si] > 1); | |
chosenPfi = pixelFormat; | |
} | |
if (!chosenPfi) { // fallback if wglChoosePixelFormatARB() failed | |
int pmDepth = deviceIsPixmap() ? ((QPixmap*)d->paintDevice)->depth() : 0; | |
PIXELFORMATDESCRIPTOR* p = (PIXELFORMATDESCRIPTOR*)dummyPfd; | |
memset(p, 0, sizeof(PIXELFORMATDESCRIPTOR)); | |
p->nSize = sizeof(PIXELFORMATDESCRIPTOR); | |
p->nVersion = 1; | |
p->dwFlags = PFD_SUPPORT_OPENGL; | |
if (deviceIsPixmap()) | |
p->dwFlags |= PFD_DRAW_TO_BITMAP; | |
else | |
p->dwFlags |= PFD_DRAW_TO_WINDOW; | |
if (!d->glFormat.directRendering()) | |
p->dwFlags |= PFD_GENERIC_FORMAT; | |
if (d->glFormat.doubleBuffer() && !deviceIsPixmap()) | |
p->dwFlags |= PFD_DOUBLEBUFFER; | |
if (d->glFormat.stereo()) | |
p->dwFlags |= PFD_STEREO; | |
if (d->glFormat.depth()) | |
p->cDepthBits = d->glFormat.depthBufferSize() == -1 ? 32 : d->glFormat.depthBufferSize(); | |
else | |
p->dwFlags |= PFD_DEPTH_DONTCARE; | |
if (d->glFormat.rgba()) { | |
p->iPixelType = PFD_TYPE_RGBA; | |
if (d->glFormat.redBufferSize() != -1) | |
p->cRedBits = d->glFormat.redBufferSize(); | |
if (d->glFormat.greenBufferSize() != -1) | |
p->cGreenBits = d->glFormat.greenBufferSize(); | |
if (d->glFormat.blueBufferSize() != -1) | |
p->cBlueBits = d->glFormat.blueBufferSize(); | |
if (deviceIsPixmap()) | |
p->cColorBits = pmDepth; | |
else | |
p->cColorBits = 32; | |
} else { | |
p->iPixelType = PFD_TYPE_COLORINDEX; | |
p->cColorBits = 8; | |
} | |
if (d->glFormat.alpha()) | |
p->cAlphaBits = d->glFormat.alphaBufferSize() == -1 ? 8 : d->glFormat.alphaBufferSize(); | |
if (d->glFormat.accum()) { | |
p->cAccumRedBits = p->cAccumGreenBits = p->cAccumBlueBits = p->cAccumAlphaBits = | |
d->glFormat.accumBufferSize() == -1 ? 16 : d->glFormat.accumBufferSize(); | |
} | |
if (d->glFormat.stencil()) | |
p->cStencilBits = d->glFormat.stencilBufferSize() == -1 ? 8 : d->glFormat.stencilBufferSize(); | |
p->iLayerType = PFD_MAIN_PLANE; | |
chosenPfi = ChoosePixelFormat(pdc, p); | |
if (!chosenPfi) | |
qErrnoWarning("QGLContext: ChoosePixelFormat failed"); | |
// Since the GDI function ChoosePixelFormat() does not handle | |
// overlay and direct-rendering requests, we must roll our own here | |
bool doSearch = chosenPfi <= 0; | |
PIXELFORMATDESCRIPTOR pfd; | |
QGLFormat fmt; | |
if (!doSearch) { | |
DescribePixelFormat(pdc, chosenPfi, sizeof(PIXELFORMATDESCRIPTOR), | |
&pfd); | |
fmt = pfdToQGLFormat(&pfd); | |
if (d->glFormat.hasOverlay() && !fmt.hasOverlay()) | |
doSearch = true; | |
else if (!qLogEq(d->glFormat.directRendering(), fmt.directRendering())) | |
doSearch = true; | |
else if (deviceIsPixmap() && (!(pfd.dwFlags & PFD_DRAW_TO_BITMAP) || | |
pfd.cColorBits != pmDepth)) | |
doSearch = true; | |
else if (!deviceIsPixmap() && !(pfd.dwFlags & PFD_DRAW_TO_WINDOW)) | |
doSearch = true; | |
else if (!qLogEq(d->glFormat.rgba(), fmt.rgba())) | |
doSearch = true; | |
} | |
if (doSearch) { | |
int pfiMax = DescribePixelFormat(pdc, 0, 0, NULL); | |
int bestScore = -1; | |
int bestPfi = -1; | |
for (int pfi = 1; pfi <= pfiMax; pfi++) { | |
DescribePixelFormat(pdc, pfi, sizeof(PIXELFORMATDESCRIPTOR), &pfd); | |
if (!(pfd.dwFlags & PFD_SUPPORT_OPENGL)) | |
continue; | |
if (deviceIsPixmap() && (!(pfd.dwFlags & PFD_DRAW_TO_BITMAP) || | |
pfd.cColorBits != pmDepth)) | |
continue; | |
if (!deviceIsPixmap() && !(pfd.dwFlags & PFD_DRAW_TO_WINDOW)) | |
continue; | |
fmt = pfdToQGLFormat(&pfd); | |
if (d->glFormat.hasOverlay() && !fmt.hasOverlay()) | |
continue; | |
int score = pfd.cColorBits; | |
if (qLogEq(d->glFormat.depth(), fmt.depth())) | |
score += pfd.cDepthBits; | |
if (qLogEq(d->glFormat.alpha(), fmt.alpha())) | |
score += pfd.cAlphaBits; | |
if (qLogEq(d->glFormat.accum(), fmt.accum())) | |
score += pfd.cAccumBits; | |
if (qLogEq(d->glFormat.stencil(), fmt.stencil())) | |
score += pfd.cStencilBits; | |
if (qLogEq(d->glFormat.doubleBuffer(), fmt.doubleBuffer())) | |
score += 1000; | |
if (qLogEq(d->glFormat.stereo(), fmt.stereo())) | |
score += 2000; | |
if (qLogEq(d->glFormat.directRendering(), fmt.directRendering())) | |
score += 4000; | |
if (qLogEq(d->glFormat.rgba(), fmt.rgba())) | |
score += 8000; | |
if (score > bestScore) { | |
bestScore = score; | |
bestPfi = pfi; | |
} | |
} | |
if (bestPfi > 0) | |
chosenPfi = bestPfi; | |
} | |
} | |
return chosenPfi; | |
} | |
void QGLContext::reset() | |
{ | |
Q_D(QGLContext); | |
// workaround for matrox driver: | |
// make a cheap call to opengl to force loading of DLL | |
if (!opengl32dll) { | |
GLint params; | |
glGetIntegerv(GL_DEPTH_BITS, ¶ms); | |
opengl32dll = true; | |
} | |
if (!d->valid) | |
return; | |
d->cleanup(); | |
doneCurrent(); | |
if (d->rc) | |
wglDeleteContext(d->rc); | |
d->rc = 0; | |
if (d->win && d->dc) | |
ReleaseDC(d->win, d->dc); | |
if (deviceIsPixmap()) { | |
DeleteDC(d->hbitmap_hdc); | |
DeleteObject(d->hbitmap); | |
d->hbitmap_hdc = 0; | |
d->hbitmap = 0; | |
} | |
d->dc = 0; | |
d->win = 0; | |
d->pixelFormatId = 0; | |
d->sharing = false; | |
d->valid = false; | |
d->transpColor = QColor(); | |
delete d->cmap; | |
d->cmap = 0; | |
d->initDone = false; | |
QGLContextGroup::removeShare(this); | |
} | |
// | |
// NOTE: In a multi-threaded environment, each thread has a current | |
// context. If we want to make this code thread-safe, we probably | |
// have to use TLS (thread local storage) for keeping current contexts. | |
// | |
void QGLContext::makeCurrent() | |
{ | |
Q_D(QGLContext); | |
if (d->rc == wglGetCurrentContext() || !d->valid) // already current | |
return; | |
if (d->win) { | |
d->dc = GetDC(d->win); | |
if (!d->dc) { | |
qwglError("QGLContext::makeCurrent()", "GetDC()"); | |
return; | |
} | |
} else if (deviceIsPixmap()) { | |
d->dc = d->hbitmap_hdc; | |
} | |
HPALETTE hpal = QColormap::hPal(); | |
if (hpal) { | |
SelectPalette(d->dc, hpal, FALSE); | |
RealizePalette(d->dc); | |
} | |
if (d->glFormat.plane()) { | |
wglRealizeLayerPalette(d->dc, d->glFormat.plane(), TRUE); | |
} | |
if (wglMakeCurrent(d->dc, d->rc)) { | |
QGLContextPrivate::setCurrentContext(this); | |
} else { | |
qwglError("QGLContext::makeCurrent()", "wglMakeCurrent"); | |
} | |
} | |
void QGLContext::doneCurrent() | |
{ | |
Q_D(QGLContext); | |
wglMakeCurrent(0, 0); | |
QGLContextPrivate::setCurrentContext(0); | |
if (deviceIsPixmap() && d->hbitmap) { | |
QPixmap *pm = static_cast<QPixmap *>(d->paintDevice); | |
*pm = QPixmap::fromWinHBITMAP(d->hbitmap); | |
} | |
if (d->win && d->dc) { | |
ReleaseDC(d->win, d->dc); | |
d->dc = 0; | |
} | |
} | |
void QGLContext::swapBuffers() const | |
{ | |
Q_D(const QGLContext); | |
if (d->dc && d->glFormat.doubleBuffer() && !deviceIsPixmap()) { | |
if (d->glFormat.plane()) | |
wglSwapLayerBuffers(d->dc, WGL_SWAP_OVERLAY1); | |
else { | |
if (d->glFormat.hasOverlay()) | |
wglSwapLayerBuffers(d->dc, WGL_SWAP_MAIN_PLANE); | |
else | |
SwapBuffers(d->dc); | |
} | |
} | |
} | |
QColor QGLContext::overlayTransparentColor() const | |
{ | |
return d_func()->transpColor; | |
} | |
uint QGLContext::colorIndex(const QColor& c) const | |
{ | |
Q_D(const QGLContext); | |
if (!isValid()) | |
return 0; | |
if (d->cmap) { | |
int idx = d->cmap->find(c.rgb()); | |
if (idx >= 0) | |
return idx; | |
if (d->dc && d->glFormat.plane()) { | |
idx = d->cmap->allocate(c.rgb()); | |
if (idx >= 0) { | |
COLORREF r = RGB(qRed(c.rgb()),qGreen(c.rgb()),qBlue(c.rgb())); | |
wglSetLayerPaletteEntries(d->dc, d->glFormat.plane(), idx, 1, &r); | |
wglRealizeLayerPalette(d->dc, d->glFormat.plane(), TRUE); | |
return idx; | |
} | |
} | |
return d->cmap->findNearest(c.rgb()); | |
} | |
QColormap cmap = QColormap::instance(); | |
return cmap.pixel(c) & 0x00ffffff; // Assumes standard palette | |
} | |
void QGLContext::generateFontDisplayLists(const QFont & fnt, int listBase) | |
{ | |
if (!isValid()) | |
return; | |
HDC display_dc = GetDC(0); | |
HDC tmp_dc = CreateCompatibleDC(display_dc); | |
HGDIOBJ old_font = SelectObject(tmp_dc, fnt.handle()); | |
ReleaseDC(0, display_dc); | |
if (!wglUseFontBitmaps(tmp_dc, 0, 256, listBase)) | |
qWarning("QGLContext::generateFontDisplayLists: Could not generate display lists for font '%s'", fnt.family().toLatin1().data()); | |
SelectObject(tmp_dc, old_font); | |
DeleteDC(tmp_dc); | |
} | |
void *QGLContext::getProcAddress(const QString &proc) const | |
{ | |
return (void *)wglGetProcAddress(proc.toLatin1()); | |
} | |
/***************************************************************************** | |
QGLWidget Win32/WGL-specific code | |
*****************************************************************************/ | |
void QGLWidgetPrivate::init(QGLContext *ctx, const QGLWidget* shareWidget) | |
{ | |
Q_Q(QGLWidget); | |
olcx = 0; | |
initContext(ctx, shareWidget); | |
if (q->isValid() && q->context()->format().hasOverlay()) { | |
olcx = new QGLContext(QGLFormat::defaultOverlayFormat(), q); | |
if (!olcx->create(shareWidget ? shareWidget->overlayContext() : 0)) { | |
delete olcx; | |
olcx = 0; | |
glcx->d_func()->glFormat.setOverlay(false); | |
} | |
} else { | |
olcx = 0; | |
} | |
} | |
/*\internal | |
Store color values in the given colormap. | |
*/ | |
static void qStoreColors(HPALETTE cmap, const QGLColormap & cols) | |
{ | |
QRgb color; | |
PALETTEENTRY pe; | |
for (int i = 0; i < cols.size(); i++) { | |
color = cols.entryRgb(i); | |
pe.peRed = qRed(color); | |
pe.peGreen = qGreen(color); | |
pe.peBlue = qBlue(color); | |
pe.peFlags = 0; | |
SetPaletteEntries(cmap, i, 1, &pe); | |
} | |
} | |
void QGLWidgetPrivate::updateColormap() | |
{ | |
Q_Q(QGLWidget); | |
if (!cmap.handle()) | |
return; | |
HDC hdc = GetDC(q->winId()); | |
SelectPalette(hdc, (HPALETTE) cmap.handle(), TRUE); | |
qStoreColors((HPALETTE) cmap.handle(), cmap); | |
RealizePalette(hdc); | |
ReleaseDC(q->winId(), hdc); | |
} | |
void QGLWidget::setMouseTracking(bool enable) | |
{ | |
QWidget::setMouseTracking(enable); | |
} | |
void QGLWidget::resizeEvent(QResizeEvent *) | |
{ | |
Q_D(QGLWidget); | |
if (!isValid()) | |
return; | |
makeCurrent(); | |
if (!d->glcx->initialized()) | |
glInit(); | |
resizeGL(width(), height()); | |
if (d->olcx) { | |
makeOverlayCurrent(); | |
resizeOverlayGL(width(), height()); | |
} | |
} | |
const QGLContext* QGLWidget::overlayContext() const | |
{ | |
return d_func()->olcx; | |
} | |
void QGLWidget::makeOverlayCurrent() | |
{ | |
Q_D(QGLWidget); | |
if (d->olcx) { | |
d->olcx->makeCurrent(); | |
if (!d->olcx->initialized()) { | |
initializeOverlayGL(); | |
d->olcx->setInitialized(true); | |
} | |
} | |
} | |
void QGLWidget::updateOverlayGL() | |
{ | |
Q_D(QGLWidget); | |
if (d->olcx) { | |
makeOverlayCurrent(); | |
paintOverlayGL(); | |
if (d->olcx->format().doubleBuffer()) { | |
if (d->autoSwap) | |
d->olcx->swapBuffers(); | |
} | |
else { | |
glFlush(); | |
} | |
} | |
} | |
void QGLWidget::setContext(QGLContext *context, | |
const QGLContext* shareContext, | |
bool deleteOldContext) | |
{ | |
Q_D(QGLWidget); | |
if (context == 0) { | |
qWarning("QGLWidget::setContext: Cannot set null context"); | |
return; | |
} | |
if (!context->deviceIsPixmap() && context->device() != this) { | |
qWarning("QGLWidget::setContext: Context must refer to this widget"); | |
return; | |
} | |
if (d->glcx) | |
d->glcx->doneCurrent(); | |
QGLContext* oldcx = d->glcx; | |
d->glcx = context; | |
bool doShow = false; | |
if (oldcx && oldcx->d_func()->win == winId() && !d->glcx->deviceIsPixmap()) { | |
// We already have a context and must therefore create a new | |
// window since Windows does not permit setting a new OpenGL | |
// context for a window that already has one set. | |
doShow = isVisible(); | |
QWidget *pW = static_cast<QWidget *>(parent()); | |
QPoint pos = geometry().topLeft(); | |
setParent(pW, windowFlags()); | |
move(pos); | |
} | |
if (!d->glcx->isValid()) { | |
bool wasSharing = shareContext || (oldcx && oldcx->isSharing()); | |
d->glcx->create(shareContext ? shareContext : oldcx); | |
// the above is a trick to keep disp lists etc when a | |
// QGLWidget has been reparented, so remove the sharing | |
// flag if we don't actually have a sharing context. | |
if (!wasSharing) | |
d->glcx->d_ptr->sharing = false; | |
} | |
if (deleteOldContext) | |
delete oldcx; | |
if (doShow) | |
show(); | |
} | |
bool QGLWidgetPrivate::renderCxPm(QPixmap*) | |
{ | |
return false; | |
} | |
void QGLWidgetPrivate::cleanupColormaps() | |
{ | |
Q_Q(QGLWidget); | |
if (cmap.handle()) { | |
HDC hdc = GetDC(q->winId()); | |
SelectPalette(hdc, (HPALETTE) GetStockObject(DEFAULT_PALETTE), FALSE); | |
DeleteObject((HPALETTE) cmap.handle()); | |
ReleaseDC(q->winId(), hdc); | |
cmap.setHandle(0); | |
} | |
return; | |
} | |
const QGLColormap & QGLWidget::colormap() const | |
{ | |
return d_func()->cmap; | |
} | |
void QGLWidget::setColormap(const QGLColormap & c) | |
{ | |
Q_D(QGLWidget); | |
d->cmap = c; | |
if (d->cmap.handle()) { // already have an allocated cmap | |
d->updateColormap(); | |
} else { | |
LOGPALETTE *lpal = (LOGPALETTE *) malloc(sizeof(LOGPALETTE) | |
+c.size()*sizeof(PALETTEENTRY)); | |
lpal->palVersion = 0x300; | |
lpal->palNumEntries = c.size(); | |
d->cmap.setHandle(CreatePalette(lpal)); | |
free(lpal); | |
d->updateColormap(); | |
} | |
} | |
QT_END_NAMESPACE |