diff options
Diffstat (limited to 'src/gui')
51 files changed, 2153 insertions, 1729 deletions
diff --git a/src/gui/configure.json b/src/gui/configure.json index f4171a8e9f..7efaaff505 100644 --- a/src/gui/configure.json +++ b/src/gui/configure.json @@ -32,6 +32,7 @@ "opengl": { "type": "optionalString", "values": [ "no", "yes", "desktop", "es2", "dynamic" ] }, "opengl-es-2": { "type": "void", "name": "opengl", "value": "es2" }, "opengles3": "boolean", + "openvg": "boolean", "qpa": { "type": "string", "name": "qpa_default_platform" }, "qpa-platform-guard": "boolean", "sm": { "type": "boolean", "name": "sessionmanager" }, @@ -193,6 +194,14 @@ { "type": "makeSpec", "spec": "OPENGL_ES2" } ] }, + "openvg": { + "label": "OpenVG", + "test": "unix/openvg", + "sources": [ + { "type": "pkgConfig", "args": "vg" }, + { "type": "makeSpec", "spec": "OPENVG" } + ] + }, "tslib": { "label": "tslib", "test": "unix/tslib", @@ -499,7 +508,7 @@ "kms": { "label": "KMS", "condition": "libs.drm", - "output": [ "publicQtConfig" ] + "output": [ "publicQtConfig", "privateFeature" ] }, "libinput": { "label": "libinput", @@ -582,9 +591,14 @@ "condition": "features.opengl-desktop || features.opengl-dynamic || features.opengles2", "output": [ "publicFeature", "feature" ] }, + "openvg": { + "label": "OpenVG", + "condition": "libs.openvg", + "output": [ "publicFeature" ] + }, "egl": { "label": "EGL", - "condition": "features.opengl && (features.angle || libs.egl)", + "condition": "(features.opengl || features.openvg) && (features.angle || libs.egl)", "output": [ "privateFeature", "feature" ] }, "egl_x11": { @@ -1037,6 +1051,8 @@ QMAKE_LIBDIR_OPENGL[_ES2] and QMAKE_LIBS_OPENGL[_ES2] in the mkspec for your pla "gif", "ico", "jpeg", "system-jpeg", "png", "system-png" ] }, + "egl", + "openvg", { "section": "OpenGL", "entries": [ @@ -1045,7 +1061,6 @@ QMAKE_LIBDIR_OPENGL[_ES2] and QMAKE_LIBS_OPENGL[_ES2] in the mkspec for your pla "args": "angle", "condition": "config.win32" }, - "egl", "opengl-desktop", { "type": "feature", diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp index 0d2f55b1c2..e8f2c878c8 100644 --- a/src/gui/image/qicon.cpp +++ b/src/gui/image/qicon.cpp @@ -137,8 +137,8 @@ static qreal qt_effective_device_pixel_ratio(QWindow *window = 0) return qApp->devicePixelRatio(); // Don't know which window to target. } -QIconPrivate::QIconPrivate() - : engine(0), ref(1), +QIconPrivate::QIconPrivate(QIconEngine *e) + : engine(e), ref(1), serialNum(serialNumCounter.fetchAndAddRelaxed(1)), detach_no(0), is_mask(false) @@ -673,9 +673,8 @@ QIcon::QIcon(const QString &fileName) ownership of the engine. */ QIcon::QIcon(QIconEngine *engine) - :d(new QIconPrivate) + :d(new QIconPrivate(engine)) { - d->engine = engine; } /*! @@ -950,8 +949,7 @@ void QIcon::detach() d = 0; return; } else if (d->ref.load() != 1) { - QIconPrivate *x = new QIconPrivate; - x->engine = d->engine->clone(); + QIconPrivate *x = new QIconPrivate(d->engine->clone()); if (!d->ref.deref()) delete d; d = x; @@ -974,10 +972,8 @@ void QIcon::addPixmap(const QPixmap &pixmap, Mode mode, State state) if (pixmap.isNull()) return; detach(); - if (!d) { - d = new QIconPrivate; - d->engine = new QPixmapIconEngine; - } + if (!d) + d = new QIconPrivate(new QPixmapIconEngine); d->engine->addPixmap(pixmap, mode, state); } @@ -1037,8 +1033,7 @@ void QIcon::addFile(const QString &fileName, const QSize &size, Mode mode, State if (!engine) engine = iconEngineFromSuffix(fileName, QMimeDatabase().mimeTypeForFile(info).preferredSuffix()); #endif // !QT_NO_MIMETYPE - d = new QIconPrivate; - d->engine = engine ? engine : new QPixmapIconEngine; + d = new QIconPrivate(engine ? engine : new QPixmapIconEngine); } d->engine->addFile(fileName, size, mode, state); @@ -1240,12 +1235,10 @@ bool QIcon::hasThemeIcon(const QString &name) */ void QIcon::setIsMask(bool isMask) { - if (!d) { - d = new QIconPrivate; - d->engine = new QPixmapIconEngine; - } else { + if (!d) + d = new QIconPrivate(new QPixmapIconEngine); + else detach(); - } d->is_mask = isMask; } @@ -1326,22 +1319,17 @@ QDataStream &operator>>(QDataStream &s, QIcon &icon) QString key; s >> key; if (key == QLatin1String("QPixmapIconEngine")) { - icon.d = new QIconPrivate; - QIconEngine *engine = new QPixmapIconEngine; - icon.d->engine = engine; - engine->read(s); + icon.d = new QIconPrivate(new QPixmapIconEngine); + icon.d->engine->read(s); } else if (key == QLatin1String("QIconLoaderEngine")) { - icon.d = new QIconPrivate; - QIconEngine *engine = new QIconLoaderEngine(); - icon.d->engine = engine; - engine->read(s); + icon.d = new QIconPrivate(new QIconLoaderEngine()); + icon.d->engine->read(s); } else { const int index = loader()->indexOf(key); if (index != -1) { if (QIconEnginePlugin *factory = qobject_cast<QIconEnginePlugin*>(loader()->instance(index))) { if (QIconEngine *engine= factory->create()) { - icon.d = new QIconPrivate; - icon.d->engine = engine; + icon.d = new QIconPrivate(engine); engine->read(s); } // factory } // instance diff --git a/src/gui/image/qicon_p.h b/src/gui/image/qicon_p.h index a978a72192..aa358e88af 100644 --- a/src/gui/image/qicon_p.h +++ b/src/gui/image/qicon_p.h @@ -64,7 +64,7 @@ QT_BEGIN_NAMESPACE class QIconPrivate { public: - QIconPrivate(); + explicit QIconPrivate(QIconEngine *e); ~QIconPrivate() { delete engine; diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 9e911bdcea..7e0922a90a 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -4489,7 +4489,8 @@ QImage QImage::smoothScaled(int w, int h) const { return src; } -static QImage rotated90(const QImage &image) { +static QImage rotated90(const QImage &image) +{ QImage out(image.height(), image.width(), image.format()); out.setDotsPerMeterX(image.dotsPerMeterY()); out.setDotsPerMeterY(image.dotsPerMeterX()); @@ -4497,49 +4498,10 @@ static QImage rotated90(const QImage &image) { out.setColorTable(image.colorTable()); int w = image.width(); int h = image.height(); - switch (image.format()) { - case QImage::Format_RGB32: - case QImage::Format_ARGB32: - case QImage::Format_ARGB32_Premultiplied: - case QImage::Format_RGBX8888: - case QImage::Format_RGBA8888: - case QImage::Format_RGBA8888_Premultiplied: - case QImage::Format_BGR30: - case QImage::Format_A2BGR30_Premultiplied: - case QImage::Format_RGB30: - case QImage::Format_A2RGB30_Premultiplied: - qt_memrotate270(reinterpret_cast<const quint32*>(image.bits()), - w, h, image.bytesPerLine(), - reinterpret_cast<quint32*>(out.bits()), - out.bytesPerLine()); - break; - case QImage::Format_RGB666: - case QImage::Format_ARGB6666_Premultiplied: - case QImage::Format_ARGB8565_Premultiplied: - case QImage::Format_ARGB8555_Premultiplied: - case QImage::Format_RGB888: - qt_memrotate270(reinterpret_cast<const quint24*>(image.bits()), - w, h, image.bytesPerLine(), - reinterpret_cast<quint24*>(out.bits()), - out.bytesPerLine()); - break; - case QImage::Format_RGB555: - case QImage::Format_RGB16: - case QImage::Format_ARGB4444_Premultiplied: - qt_memrotate270(reinterpret_cast<const quint16*>(image.bits()), - w, h, image.bytesPerLine(), - reinterpret_cast<quint16*>(out.bits()), - out.bytesPerLine()); - break; - case QImage::Format_Alpha8: - case QImage::Format_Grayscale8: - case QImage::Format_Indexed8: - qt_memrotate270(reinterpret_cast<const quint8*>(image.bits()), - w, h, image.bytesPerLine(), - reinterpret_cast<quint8*>(out.bits()), - out.bytesPerLine()); - break; - default: + const MemRotateFunc memrotate = qMemRotateFunctions[qPixelLayouts[image.format()].bpp][2]; + if (memrotate) { + memrotate(image.constBits(), w, h, image.bytesPerLine(), out.bits(), out.bytesPerLine()); + } else { for (int y=0; y<h; ++y) { if (image.colorCount()) for (int x=0; x<w; ++x) @@ -4548,18 +4510,29 @@ static QImage rotated90(const QImage &image) { for (int x=0; x<w; ++x) out.setPixel(h-y-1, x, image.pixel(x, y)); } - break; } return out; } +static QImage rotated180(const QImage &image) +{ + const MemRotateFunc memrotate = qMemRotateFunctions[qPixelLayouts[image.format()].bpp][1]; + if (!memrotate) + return image.mirrored(true, true); -static QImage rotated180(const QImage &image) { - return image.mirrored(true, true); + QImage out(image.width(), image.height(), image.format()); + out.setDotsPerMeterX(image.dotsPerMeterY()); + out.setDotsPerMeterY(image.dotsPerMeterX()); + if (image.colorCount() > 0) + out.setColorTable(image.colorTable()); + int w = image.width(); + int h = image.height(); + memrotate(image.constBits(), w, h, image.bytesPerLine(), out.bits(), out.bytesPerLine()); + return out; } - -static QImage rotated270(const QImage &image) { +static QImage rotated270(const QImage &image) +{ QImage out(image.height(), image.width(), image.format()); out.setDotsPerMeterX(image.dotsPerMeterY()); out.setDotsPerMeterY(image.dotsPerMeterX()); @@ -4567,49 +4540,10 @@ static QImage rotated270(const QImage &image) { out.setColorTable(image.colorTable()); int w = image.width(); int h = image.height(); - switch (image.format()) { - case QImage::Format_RGB32: - case QImage::Format_ARGB32: - case QImage::Format_ARGB32_Premultiplied: - case QImage::Format_RGBX8888: - case QImage::Format_RGBA8888: - case QImage::Format_RGBA8888_Premultiplied: - case QImage::Format_BGR30: - case QImage::Format_A2BGR30_Premultiplied: - case QImage::Format_RGB30: - case QImage::Format_A2RGB30_Premultiplied: - qt_memrotate90(reinterpret_cast<const quint32*>(image.bits()), - w, h, image.bytesPerLine(), - reinterpret_cast<quint32*>(out.bits()), - out.bytesPerLine()); - break; - case QImage::Format_RGB666: - case QImage::Format_ARGB6666_Premultiplied: - case QImage::Format_ARGB8565_Premultiplied: - case QImage::Format_ARGB8555_Premultiplied: - case QImage::Format_RGB888: - qt_memrotate90(reinterpret_cast<const quint24*>(image.bits()), - w, h, image.bytesPerLine(), - reinterpret_cast<quint24*>(out.bits()), - out.bytesPerLine()); - break; - case QImage::Format_RGB555: - case QImage::Format_RGB16: - case QImage::Format_ARGB4444_Premultiplied: - qt_memrotate90(reinterpret_cast<const quint16*>(image.bits()), - w, h, image.bytesPerLine(), - reinterpret_cast<quint16*>(out.bits()), - out.bytesPerLine()); - break; - case QImage::Format_Alpha8: - case QImage::Format_Grayscale8: - case QImage::Format_Indexed8: - qt_memrotate90(reinterpret_cast<const quint8*>(image.bits()), - w, h, image.bytesPerLine(), - reinterpret_cast<quint8*>(out.bits()), - out.bytesPerLine()); - break; - default: + const MemRotateFunc memrotate = qMemRotateFunctions[qPixelLayouts[image.format()].bpp][0]; + if (memrotate) { + memrotate(image.constBits(), w, h, image.bytesPerLine(), out.bits(), out.bytesPerLine()); + } else { for (int y=0; y<h; ++y) { if (image.colorCount()) for (int x=0; x<w; ++x) @@ -4618,7 +4552,6 @@ static QImage rotated270(const QImage &image) { for (int x=0; x<w; ++x) out.setPixel(y, w-x-1, image.pixel(x, y)); } - break; } return out; } diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp index c646ee96b4..d685d50d49 100644 --- a/src/gui/image/qimage_conversions.cpp +++ b/src/gui/image/qimage_conversions.cpp @@ -39,6 +39,7 @@ #include <private/qdrawhelper_p.h> #include <private/qguiapplication_p.h> +#include <private/qcolorprofile_p.h> #include <private/qsimd_p.h> #include <private/qimage_p.h> #include <qendian.h> @@ -82,23 +83,17 @@ const uchar *qt_get_bitflip_array() void qGamma_correct_back_to_linear_cs(QImage *image) { - const QDrawHelperGammaTables *tables = QGuiApplicationPrivate::instance()->gammaTables(); - if (!tables) + const QColorProfile *cp = QGuiApplicationPrivate::instance()->colorProfileForA32Text(); + if (!cp) return; - const uchar *gamma = tables->qt_pow_rgb_gamma; // gamma correct the pixels back to linear color space... int h = image->height(); int w = image->width(); for (int y=0; y<h; ++y) { - uint *pixels = (uint *) image->scanLine(y); - for (int x=0; x<w; ++x) { - uint p = pixels[x]; - uint r = gamma[qRed(p)]; - uint g = gamma[qGreen(p)]; - uint b = gamma[qBlue(p)]; - pixels[x] = (r << 16) | (g << 8) | b | 0xff000000; - } + QRgb *pixels = reinterpret_cast<QRgb *>(image->scanLine(y)); + for (int x=0; x<w; ++x) + pixels[x] = cp->toLinear(pixels[x]); } } diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 254b8926c8..468523ab9c 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3887,7 +3887,7 @@ QDebug operator<<(QDebug dbg, const QTouchEvent::TouchPoint &tp) { QDebugStateSaver saver(dbg); dbg.nospace(); - dbg << "TouchPoint(" << tp.id() << " ("; + dbg << "TouchPoint(" << hex << tp.id() << dec << " ("; QtDebugUtils::formatQRect(dbg, tp.rect()); dbg << ") "; QtDebugUtils::formatQEnum(dbg, tp.state()); @@ -4417,6 +4417,8 @@ QTouchEvent::~QTouchEvent() \brief The TouchPoint class provides information about a touch point in a QTouchEvent. \since 4.6 \inmodule QtGui + + \image touchpoint-metrics.png */ /*! \enum TouchPoint::InfoFlag @@ -4502,7 +4504,7 @@ Qt::TouchPointState QTouchEvent::TouchPoint::state() const */ QPointF QTouchEvent::TouchPoint::pos() const { - return d->rect.center(); + return d->pos; } /*! @@ -4517,7 +4519,7 @@ QPointF QTouchEvent::TouchPoint::pos() const */ QPointF QTouchEvent::TouchPoint::scenePos() const { - return d->sceneRect.center(); + return d->scenePos; } /*! @@ -4527,7 +4529,7 @@ QPointF QTouchEvent::TouchPoint::scenePos() const */ QPointF QTouchEvent::TouchPoint::screenPos() const { - return d->screenRect.center(); + return d->screenPos; } /*! @@ -4650,10 +4652,19 @@ QPointF QTouchEvent::TouchPoint::lastNormalizedPos() const around the point returned by pos(). \note This function returns an empty rect if the device does not report touch point sizes. + + \obsolete This function is deprecated in 5.9 because it returns the outer bounds + of the touchpoint regardless of rotation, whereas a touchpoint is more correctly + modeled as an ellipse at position pos() with ellipseDiameters() + which are independent of rotation(). + + \sa scenePos(), ellipseDiameters() */ QRectF QTouchEvent::TouchPoint::rect() const { - return d->rect; + QRectF ret(QPointF(), d->ellipseDiameters); + ret.moveCenter(d->pos); + return ret; } /*! @@ -4661,11 +4672,18 @@ QRectF QTouchEvent::TouchPoint::rect() const \note This function returns an empty rect if the device does not report touch point sizes. - \sa scenePos(), rect() + \obsolete This function is deprecated in 5.9 because it returns the outer bounds + of the touchpoint regardless of rotation, whereas a touchpoint is more correctly + modeled as an ellipse at position scenePos() with ellipseDiameters() + which are independent of rotation(). + + \sa scenePos(), ellipseDiameters() */ QRectF QTouchEvent::TouchPoint::sceneRect() const { - return d->sceneRect; + QRectF ret(QPointF(), d->ellipseDiameters); + ret.moveCenter(d->scenePos); + return ret; } /*! @@ -4673,11 +4691,18 @@ QRectF QTouchEvent::TouchPoint::sceneRect() const \note This function returns an empty rect if the device does not report touch point sizes. - \sa screenPos(), rect() + \obsolete This function is deprecated because it returns the outer bounds of the + touchpoint regardless of rotation, whereas a touchpoint is more correctly + modeled as an ellipse at position screenPos() with ellipseDiameters() + which are independent of rotation(). + + \sa screenPos(), ellipseDiameters() */ QRectF QTouchEvent::TouchPoint::screenRect() const { - return d->screenRect; + QRectF ret(QPointF(), d->ellipseDiameters); + ret.moveCenter(d->screenPos); + return ret; } /*! @@ -4703,6 +4728,19 @@ qreal QTouchEvent::TouchPoint::rotation() const } /*! + \since 5.9 + Returns the width and height of the bounding ellipse of this touch point. + The return value is in logical pixels. Most touchscreens do not detect the + shape of the contact point, so a null size is the most common value. + In other cases the diameters may be nonzero and equal (the ellipse is + approximated as a circle). +*/ +QSizeF QTouchEvent::TouchPoint::ellipseDiameters() const +{ + return d->ellipseDiameters; +} + +/*! Returns a velocity vector for this touch point. The vector is in the screen's coordinate system, using pixels per seconds for the magnitude. @@ -4773,7 +4811,7 @@ void QTouchEvent::TouchPoint::setPos(const QPointF &pos) { if (d->ref.load() != 1) d = d->detach(); - d->rect.moveCenter(pos); + d->pos = pos; } /*! \internal */ @@ -4781,7 +4819,7 @@ void QTouchEvent::TouchPoint::setScenePos(const QPointF &scenePos) { if (d->ref.load() != 1) d = d->detach(); - d->sceneRect.moveCenter(scenePos); + d->scenePos = scenePos; } /*! \internal */ @@ -4789,7 +4827,7 @@ void QTouchEvent::TouchPoint::setScreenPos(const QPointF &screenPos) { if (d->ref.load() != 1) d = d->detach(); - d->screenRect.moveCenter(screenPos); + d->screenPos = screenPos; } /*! \internal */ @@ -4864,28 +4902,32 @@ void QTouchEvent::TouchPoint::setLastNormalizedPos(const QPointF &lastNormalized d->lastNormalizedPos = lastNormalizedPos; } -/*! \internal */ +// ### remove the following 3 setRect functions and their usages soon +/*! \internal \obsolete */ void QTouchEvent::TouchPoint::setRect(const QRectF &rect) { if (d->ref.load() != 1) d = d->detach(); - d->rect = rect; + d->pos = rect.center(); + d->ellipseDiameters = rect.size(); } -/*! \internal */ +/*! \internal \obsolete */ void QTouchEvent::TouchPoint::setSceneRect(const QRectF &sceneRect) { if (d->ref.load() != 1) d = d->detach(); - d->sceneRect = sceneRect; + d->scenePos = sceneRect.center(); + d->ellipseDiameters = sceneRect.size(); } -/*! \internal */ +/*! \internal \obsolete */ void QTouchEvent::TouchPoint::setScreenRect(const QRectF &screenRect) { if (d->ref.load() != 1) d = d->detach(); - d->screenRect = screenRect; + d->screenPos = screenRect.center(); + d->ellipseDiameters = screenRect.size(); } /*! \internal */ @@ -4905,6 +4947,14 @@ void QTouchEvent::TouchPoint::setRotation(qreal angle) } /*! \internal */ +void QTouchEvent::TouchPoint::setEllipseDiameters(const QSizeF &dia) +{ + if (d->ref.load() != 1) + d = d->detach(); + d->ellipseDiameters = dia; +} + +/*! \internal */ void QTouchEvent::TouchPoint::setVelocity(const QVector2D &v) { if (d->ref.load() != 1) diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index a062331bd8..d7b7b636b2 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -870,6 +870,8 @@ public: qreal pressure() const; qreal rotation() const; + QSizeF ellipseDiameters() const; + QVector2D velocity() const; InfoFlags flags() const; QVector<QPointF> rawScreenPositions() const; @@ -890,11 +892,12 @@ public: void setLastScenePos(const QPointF &lastScenePos); void setLastScreenPos(const QPointF &lastScreenPos); void setLastNormalizedPos(const QPointF &lastNormalizedPos); - void setRect(const QRectF &rect); - void setSceneRect(const QRectF &sceneRect); - void setScreenRect(const QRectF &screenRect); + void setRect(const QRectF &rect); // deprecated + void setSceneRect(const QRectF &sceneRect); // deprecated + void setScreenRect(const QRectF &screenRect); // deprecated void setPressure(qreal pressure); void setRotation(qreal angle); + void setEllipseDiameters(const QSizeF &dia); void setVelocity(const QVector2D &v); void setFlags(InfoFlags flags); void setRawScreenPositions(const QVector<QPointF> &positions); diff --git a/src/gui/kernel/qevent_p.h b/src/gui/kernel/qevent_p.h index 7e82b9c654..f67284eebb 100644 --- a/src/gui/kernel/qevent_p.h +++ b/src/gui/kernel/qevent_p.h @@ -65,8 +65,9 @@ public: : ref(1), id(id), state(Qt::TouchPointReleased), - pressure(qreal(-1.)), - rotation(qreal(0.)) + pressure(-1), + rotation(0), + ellipseDiameters(0, 0) { } inline QTouchEventTouchPointPrivate *detach() @@ -82,12 +83,12 @@ public: int id; QPointerUniqueId uniqueId; Qt::TouchPointStates state; - QRectF rect, sceneRect, screenRect; - QPointF normalizedPos, + QPointF pos, scenePos, screenPos, normalizedPos, startPos, startScenePos, startScreenPos, startNormalizedPos, lastPos, lastScenePos, lastScreenPos, lastNormalizedPos; qreal pressure; qreal rotation; + QSizeF ellipseDiameters; QVector2D velocity; QTouchEvent::TouchPoint::InfoFlags flags; QVector<QPointF> rawScreenPositions; diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index c189c5b068..d3f3827d6e 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -68,8 +68,8 @@ #include <qpalette.h> #include <qscreen.h> #include "qsessionmanager.h" +#include <private/qcolorprofile_p.h> #include <private/qscreen_p.h> -#include <private/qdrawhelper_p.h> #include <QtGui/qgenericpluginfactory.h> #include <QtGui/qstylehints.h> @@ -84,6 +84,7 @@ #include "private/qcursor_p.h" #include "private/qopenglcontext_p.h" #include "private/qinputdevicemanager_p.h" +#include "private/qtouchdevice_p.h" #include "private/qdnd_p.h" #include <qpa/qplatformthemefactory_p.h> @@ -817,19 +818,11 @@ bool QGuiApplicationPrivate::isWindowBlocked(QWindow *window, QWindow **blocking for (int i = 0; i < modalWindowList.count(); ++i) { QWindow *modalWindow = modalWindowList.at(i); - { - // check if the modal window is our window or a (transient) parent of our window - QWindow *w = window; - while (w) { - if (w == modalWindow) { - *blockingWindow = 0; - return false; - } - QWindow *p = w->parent(); - if (!p) - p = w->transientParent(); - w = p; - } + // A window is not blocked by another modal window if the two are + // the same, or if the window is a child of the modal window. + if (window == modalWindow || modalWindow->isAncestorOf(window, QWindow::IncludeTransients)) { + *blockingWindow = 0; + return false; } Qt::WindowModality windowModality = modalWindow->modality(); @@ -936,15 +929,25 @@ QWindowList QGuiApplication::topLevelWindows() { const QWindowList &list = QGuiApplicationPrivate::window_list; QWindowList topLevelWindows; - for (int i = 0; i < list.size(); i++) { - if (!list.at(i)->parent() && list.at(i)->type() != Qt::Desktop) { - // Top windows of embedded QAxServers do not have QWindow parents, - // but they are not true top level windows, so do not include them. - const bool embedded = list.at(i)->handle() && list.at(i)->handle()->isEmbedded(); - if (!embedded) - topLevelWindows.prepend(list.at(i)); - } + for (int i = 0; i < list.size(); ++i) { + QWindow *window = list.at(i); + if (!window->isTopLevel()) + continue; + + // Desktop windows are special, as each individual desktop window + // will report that it's a top level window, but we don't want to + // include them in the application wide list of top level windows. + if (window->type() == Qt::Desktop) + continue; + + // Windows embedded in native windows do not have QWindow parents, + // but they are not true top level windows, so do not include them. + if (window->handle() && window->handle()->isEmbedded()) + continue; + + topLevelWindows.prepend(window); } + return topLevelWindows; } @@ -1519,7 +1522,8 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate() platform_theme = 0; delete platform_integration; platform_integration = 0; - delete m_gammaTables.load(); + delete m_a8ColorProfile.load(); + delete m_a32ColorProfile.load(); window_list.clear(); } @@ -1954,7 +1958,8 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo points << point; QEvent::Type type; - QList<QTouchEvent::TouchPoint> touchPoints = QWindowSystemInterfacePrivate::fromNativeTouchPoints(points, window, &type); + QList<QTouchEvent::TouchPoint> touchPoints = + QWindowSystemInterfacePrivate::fromNativeTouchPoints(points, window, QTouchDevicePrivate::get(m_fakeTouchDevice)->id, &type); QWindowSystemInterfacePrivate::TouchEvent fake(window, e->timestamp, type, m_fakeTouchDevice, touchPoints, e->modifiers); fake.flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic; @@ -2191,10 +2196,10 @@ void QGuiApplicationPrivate::processThemeChanged(QWindowSystemInterfacePrivate:: void QGuiApplicationPrivate::processGeometryChangeEvent(QWindowSystemInterfacePrivate::GeometryChangeEvent *e) { - if (e->tlw.isNull()) + if (e->window.isNull()) return; - QWindow *window = e->tlw.data(); + QWindow *window = e->window.data(); if (!window) return; @@ -2559,7 +2564,10 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To Q_ASSERT(w.data() != 0); // make the *scene* functions return the same as the *screen* functions - touchPoint.d->sceneRect = touchPoint.screenRect(); + // Note: touchPoint is a reference to the one from activeTouchPoints, + // so we can modify it as long as we're careful NOT to call setters and + // otherwise NOT to cause the d-pointer to be detached. + touchPoint.d->scenePos = touchPoint.screenPos(); touchPoint.d->startScenePos = touchPoint.startScreenPos(); touchPoint.d->lastScenePos = touchPoint.lastScreenPos(); @@ -2615,8 +2623,8 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To QTouchEvent touchEvent(eventType, e->device, e->modifiers, - it.value().first, - it.value().second); + it.value().first, // state flags + it.value().second); // list of touchpoints touchEvent.setTimestamp(e->timestamp); touchEvent.setWindow(w); @@ -2625,13 +2633,14 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To QTouchEvent::TouchPoint &touchPoint = touchEvent._touchPoints[i]; // preserve the sub-pixel resolution - QRectF rect = touchPoint.screenRect(); - const QPointF screenPos = rect.center(); + const QPointF screenPos = touchPoint.screenPos(); const QPointF delta = screenPos - screenPos.toPoint(); - rect.moveCenter(w->mapFromGlobal(screenPos.toPoint()) + delta); - touchPoint.d->rect = rect; + touchPoint.d->pos = w->mapFromGlobal(screenPos.toPoint()) + delta; if (touchPoint.state() == Qt::TouchPointPressed) { + // touchPoint is actually a reference to one that is stored in activeTouchPoints, + // and we are now going to store the startPos and lastPos there, for the benefit + // of future moves and releases. It's important that the d-pointer is NOT detached. touchPoint.d->startPos = w->mapFromGlobal(touchPoint.startScreenPos().toPoint()) + delta; touchPoint.d->lastPos = w->mapFromGlobal(touchPoint.lastScreenPos().toPoint()) + delta; } @@ -3671,14 +3680,30 @@ void QGuiApplicationPrivate::notifyDragStarted(const QDrag *drag) } #endif -const QDrawHelperGammaTables *QGuiApplicationPrivate::gammaTables() +const QColorProfile *QGuiApplicationPrivate::colorProfileForA8Text() +{ +#ifdef Q_OS_WIN + QColorProfile *result = m_a8ColorProfile.load(); + if (!result){ + QColorProfile *cs = QColorProfile::fromGamma(2.31); // This is a hard-coded thing for Windows text rendering + if (!m_a8ColorProfile.testAndSetRelease(0, cs)) + delete cs; + result = m_a8ColorProfile.load(); + } + return result; +#else + return colorProfileForA32Text(); +#endif +} + +const QColorProfile *QGuiApplicationPrivate::colorProfileForA32Text() { - QDrawHelperGammaTables *result = m_gammaTables.load(); + QColorProfile *result = m_a32ColorProfile.load(); if (!result){ - QDrawHelperGammaTables *tables = new QDrawHelperGammaTables(fontSmoothingGamma); - if (!m_gammaTables.testAndSetRelease(0, tables)) - delete tables; - result = m_gammaTables.load(); + QColorProfile *cs = QColorProfile::fromGamma(fontSmoothingGamma); + if (!m_a32ColorProfile.testAndSetRelease(0, cs)) + delete cs; + result = m_a32ColorProfile.load(); } return result; } diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h index 0d62490c75..3804667ef3 100644 --- a/src/gui/kernel/qguiapplication_p.h +++ b/src/gui/kernel/qguiapplication_p.h @@ -66,10 +66,10 @@ QT_BEGIN_NAMESPACE +class QColorProfile; class QPlatformIntegration; class QPlatformTheme; class QPlatformDragQtResponse; -struct QDrawHelperGammaTables; #ifndef QT_NO_DRAGANDDROP class QDrag; #endif // QT_NO_DRAGANDDROP @@ -293,7 +293,8 @@ public: static QInputDeviceManager *inputDeviceManager(); - const QDrawHelperGammaTables *gammaTables(); + const QColorProfile *colorProfileForA8Text(); + const QColorProfile *colorProfileForA32Text(); // hook reimplemented in QApplication to apply the QStyle function on the QIcon virtual QPixmap applyQIconStyleHelper(QIcon::Mode, const QPixmap &basePixmap) const { return basePixmap; } @@ -317,7 +318,8 @@ private: static QGuiApplicationPrivate *self; static QTouchDevice *m_fakeTouchDevice; static int m_fakeMouseSourcePointId; - QAtomicPointer<QDrawHelperGammaTables> m_gammaTables; + QAtomicPointer<QColorProfile> m_a8ColorProfile; + QAtomicPointer<QColorProfile> m_a32ColorProfile; bool ownGlobalShareContext; diff --git a/src/gui/kernel/qplatformscreen.h b/src/gui/kernel/qplatformscreen.h index 030edea880..2d3cbaf170 100644 --- a/src/gui/kernel/qplatformscreen.h +++ b/src/gui/kernel/qplatformscreen.h @@ -66,7 +66,6 @@ QT_BEGIN_NAMESPACE class QPlatformBackingStore; -class QPlatformOpenGLContext; class QPlatformScreenPrivate; class QPlatformWindow; class QPlatformCursor; diff --git a/src/gui/kernel/qplatformtheme.cpp b/src/gui/kernel/qplatformtheme.cpp index 9c140f1d68..eef8b817fd 100644 --- a/src/gui/kernel/qplatformtheme.cpp +++ b/src/gui/kernel/qplatformtheme.cpp @@ -149,6 +149,11 @@ QT_BEGIN_NAMESPACE \value ContextMenuOnMouseRelease (bool) Whether the context menu should be shown on mouse release. + \value TouchDoubleTapDistance (int) The maximum distance in logical pixels which a touchpoint can travel + between taps in order for the tap sequence to be handled as a double tap. + The default value is double the MouseDoubleClickDistance, or 10 logical pixels + if that is not specified. + \sa themeHint(), QStyle::pixelMetric() */ @@ -533,6 +538,14 @@ QVariant QPlatformTheme::defaultThemeHint(ThemeHint hint) } case WheelScrollLines: return QVariant(3); + case TouchDoubleTapDistance: + { + bool ok = false; + int dist = qEnvironmentVariableIntValue("QT_DBL_TAP_DIST", &ok); + if (!ok) + dist = defaultThemeHint(MouseDoubleClickDistance).toInt(&ok) * 2; + return QVariant(ok ? dist : 10); + } } return QVariant(); } diff --git a/src/gui/kernel/qplatformtheme.h b/src/gui/kernel/qplatformtheme.h index 686dbed4b1..4135e31517 100644 --- a/src/gui/kernel/qplatformtheme.h +++ b/src/gui/kernel/qplatformtheme.h @@ -114,7 +114,8 @@ public: ContextMenuOnMouseRelease, MousePressAndHoldInterval, MouseDoubleClickDistance, - WheelScrollLines + WheelScrollLines, + TouchDoubleTapDistance }; enum DialogType { diff --git a/src/gui/kernel/qsurface.cpp b/src/gui/kernel/qsurface.cpp index afe4cf93e1..3cdd11de8c 100644 --- a/src/gui/kernel/qsurface.cpp +++ b/src/gui/kernel/qsurface.cpp @@ -76,6 +76,8 @@ QT_BEGIN_NAMESPACE \value RasterGLSurface The surface can be rendered to using a software rasterizer, and also supports OpenGL. This surface type is intended for internal Qt use, and requires the use of private API. + \value OpenVGSurface The surface is an OpenVG compatible surface and can be used + in conjunction with OpenVG contexts. */ diff --git a/src/gui/kernel/qsurface.h b/src/gui/kernel/qsurface.h index d9ccdc096d..a96b7a6422 100644 --- a/src/gui/kernel/qsurface.h +++ b/src/gui/kernel/qsurface.h @@ -64,7 +64,8 @@ public: enum SurfaceType { RasterSurface, OpenGLSurface, - RasterGLSurface + RasterGLSurface, + OpenVGSurface, }; virtual ~QSurface(); diff --git a/src/gui/kernel/qtouchdevice.h b/src/gui/kernel/qtouchdevice.h index 0fb24e47bf..c98aa69236 100644 --- a/src/gui/kernel/qtouchdevice.h +++ b/src/gui/kernel/qtouchdevice.h @@ -87,6 +87,7 @@ public: private: QTouchDevicePrivate *d; + friend class QTouchDevicePrivate; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QTouchDevice::Capabilities) diff --git a/src/gui/kernel/qtouchdevice_p.h b/src/gui/kernel/qtouchdevice_p.h index 203d9fca74..18d2af46a7 100644 --- a/src/gui/kernel/qtouchdevice_p.h +++ b/src/gui/kernel/qtouchdevice_p.h @@ -64,16 +64,21 @@ public: : type(QTouchDevice::TouchScreen), caps(QTouchDevice::Position), maxTouchPoints(1) - { } + { + static quint8 nextId = 2; // device 0 is not used, device 1 is for mouse device + id = nextId++; + } QTouchDevice::DeviceType type; QTouchDevice::Capabilities caps; QString name; int maxTouchPoints; + quint8 id; static void registerDevice(const QTouchDevice *dev); static void unregisterDevice(const QTouchDevice *dev); static bool isRegistered(const QTouchDevice *dev); + static QTouchDevicePrivate *get(QTouchDevice *q) { return q->d; } }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index 94a34b5c27..93edfe6331 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -343,6 +343,30 @@ void QWindowPrivate::updateVisibility() emit q->visibilityChanged(visibility); } +void QWindowPrivate::updateSiblingPosition(SiblingPosition position) +{ + Q_Q(QWindow); + + if (!q->parent()) + return; + + QObjectList &siblings = q->parent()->d_ptr->children; + + const int siblingCount = siblings.size() - 1; + if (siblingCount == 0) + return; + + const int currentPosition = siblings.indexOf(q); + Q_ASSERT(currentPosition >= 0); + + const int targetPosition = position == PositionTop ? siblingCount : 0; + + if (currentPosition == targetPosition) + return; + + siblings.move(currentPosition, targetPosition); +} + inline bool QWindowPrivate::windowRecreationRequired(QScreen *newScreen) const { Q_Q(const QWindow); @@ -605,6 +629,22 @@ WId QWindow::winId() const return d->platformWindow->winId(); } + /*! + Returns the parent window, if any. + + If \a mode is IncludeTransients, then the transient parent is returned + if there is no parent. + + A window without a parent is known as a top level window. + + \since 5.9 +*/ +QWindow *QWindow::parent(AncestorMode mode) const +{ + Q_D(const QWindow); + return d->parentWindow ? d->parentWindow : (mode == IncludeTransients ? transientParent() : nullptr); +} + /*! Returns the parent window, if any. @@ -797,10 +837,15 @@ QSurfaceFormat QWindow::format() const The actual window flags might differ from the flags set with setFlags() if the requested flags could not be fulfilled. + + \sa setFlag() */ void QWindow::setFlags(Qt::WindowFlags flags) { Q_D(QWindow); + if (d->windowFlags == flags) + return; + if (d->platformWindow) d->platformWindow->setWindowFlags(flags); d->windowFlags = flags; @@ -813,6 +858,23 @@ Qt::WindowFlags QWindow::flags() const } /*! + \since 5.9 + + Sets the window flag \a flag on this window if \a on is true; + otherwise clears the flag. + + \sa setFlags(), flags(), type() +*/ +void QWindow::setFlag(Qt::WindowType flag, bool on) +{ + Q_D(QWindow); + if (on) + setFlags(d->windowFlags | flag); + else + setFlags(d->windowFlags & ~flag); +} + +/*! Returns the type of the window. This returns the part of the window flags that represents @@ -920,6 +982,9 @@ QIcon QWindow::icon() const void QWindow::raise() { Q_D(QWindow); + + d->updateSiblingPosition(QWindowPrivate::PositionTop); + if (d->platformWindow) d->platformWindow->raise(); } @@ -932,6 +997,9 @@ void QWindow::raise() void QWindow::lower() { Q_D(QWindow); + + d->updateSiblingPosition(QWindowPrivate::PositionBottom); + if (d->platformWindow) d->platformWindow->lower(); } @@ -1065,11 +1133,10 @@ bool QWindow::isActive() const if (focus == this) return true; - if (!parent() && !transientParent()) { + if (QWindow *p = parent(IncludeTransients)) + return p->isActive(); + else return isAncestorOf(focus); - } else { - return (parent() && parent()->isActive()) || (transientParent() && transientParent()->isActive()); - } } /*! @@ -1233,8 +1300,10 @@ bool QWindow::isAncestorOf(const QWindow *child, AncestorMode mode) const if (child->parent() == this || (mode == IncludeTransients && child->transientParent() == this)) return true; - return (child->parent() && isAncestorOf(child->parent(), mode)) - || (mode == IncludeTransients && child->transientParent() && isAncestorOf(child->transientParent(), mode)); + if (child->parent(mode) && isAncestorOf(child->parent(mode), mode)) + return true; + + return false; } /*! @@ -1455,6 +1524,8 @@ void QWindow::setSizeIncrement(const QSize &size) Sets the geometry of the window, excluding its window frame, to a rectangle constructed from \a posx, \a posy, \a w and \a h. + The geometry is in relation to the virtualGeometry() of its screen. + \sa geometry() */ void QWindow::setGeometry(int posx, int posy, int w, int h) @@ -1465,6 +1536,8 @@ void QWindow::setGeometry(int posx, int posy, int w, int h) /*! \brief Sets the geometry of the window, excluding its window frame, to \a rect. + The geometry is in relation to the virtualGeometry() of its screen. + \sa geometry() */ void QWindow::setGeometry(const QRect &rect) @@ -1526,6 +1599,8 @@ QScreen *QWindowPrivate::screenForGeometry(const QRect &newGeometry) /*! Returns the geometry of the window, excluding its window frame. + The geometry is in relation to the virtualGeometry() of its screen. + \sa frameMargins(), frameGeometry() */ QRect QWindow::geometry() const @@ -1552,6 +1627,8 @@ QMargins QWindow::frameMargins() const /*! Returns the geometry of the window, including its window frame. + The geometry is in relation to the virtualGeometry() of its screen. + \sa geometry(), frameMargins() */ QRect QWindow::frameGeometry() const @@ -1584,6 +1661,8 @@ QPoint QWindow::framePosition() const /*! Sets the upper left position of the window (\a point) including its window frame. + The position is in relation to the virtualGeometry() of its screen. + \sa setGeometry(), frameGeometry() */ void QWindow::setFramePosition(const QPoint &point) @@ -1601,6 +1680,8 @@ void QWindow::setFramePosition(const QPoint &point) /*! \brief set the position of the window on the desktop to \a pt + The position is in relation to the virtualGeometry() of its screen. + \sa position() */ void QWindow::setPosition(const QPoint &pt) @@ -1611,6 +1692,8 @@ void QWindow::setPosition(const QPoint &pt) /*! \brief set the position of the window on the desktop to \a posx, \a posy + The position is in relation to the virtualGeometry() of its screen. + \sa position() */ void QWindow::setPosition(int posx, int posy) @@ -1787,8 +1870,9 @@ QScreen *QWindow::screen() const If the window has been created, it will be recreated on the \a newScreen. - Note that if the screen is part of a virtual desktop of multiple screens, - the window can appear on any of the screens returned by QScreen::virtualSiblings(). + \note If the screen is part of a virtual desktop of multiple screens, + the window will not move automatically to \a newScreen. To place the + window relative to the screen, use the screen's topLeft() position. This function only works for top level windows. @@ -2377,6 +2461,20 @@ QPoint QWindow::mapFromGlobal(const QPoint &pos) const return pos - d->globalPosition(); } +QPoint QWindowPrivate::globalPosition() const +{ + Q_Q(const QWindow); + QPoint offset = q->position(); + for (const QWindow *p = q->parent(); p; p = p->parent()) { + if (p->type() != Qt::ForeignWindow) { + offset += p->position(); + } else { // Use mapToGlobal() for foreign windows + offset += p->mapToGlobal(QPoint(0, 0)); + break; + } + } + return offset; +} Q_GUI_EXPORT QWindowPrivate *qt_window_private(QWindow *window) { @@ -2416,10 +2514,7 @@ QWindow *QWindowPrivate::topLevelWindow() const QWindow *window = const_cast<QWindow *>(q); while (window) { - QWindow *parent = window->parent(); - if (!parent) - parent = window->transientParent(); - + QWindow *parent = window->parent(QWindow::IncludeTransients); if (!parent) break; diff --git a/src/gui/kernel/qwindow.h b/src/gui/kernel/qwindow.h index bf25cf64c9..2883749d2e 100644 --- a/src/gui/kernel/qwindow.h +++ b/src/gui/kernel/qwindow.h @@ -132,6 +132,12 @@ public: }; Q_ENUM(Visibility) + enum AncestorMode { + ExcludeTransients, + IncludeTransients + }; + Q_ENUM(AncestorMode) + explicit QWindow(QScreen *screen = Q_NULLPTR); explicit QWindow(QWindow *parent); virtual ~QWindow(); @@ -148,7 +154,8 @@ public: WId winId() const; - QWindow *parent() const; + QWindow *parent(AncestorMode mode) const; + QWindow *parent() const; // ### Qt6: Merge with above void setParent(QWindow *parent); bool isTopLevel() const; @@ -163,6 +170,7 @@ public: void setFlags(Qt::WindowFlags flags); Qt::WindowFlags flags() const; + void setFlag(Qt::WindowType, bool on = true); Qt::WindowType type() const; QString title() const; @@ -186,11 +194,6 @@ public: void setTransientParent(QWindow *parent); QWindow *transientParent() const; - enum AncestorMode { - ExcludeTransients, - IncludeTransients - }; - bool isAncestorOf(const QWindow *child, AncestorMode mode = IncludeTransients) const; bool isExposed() const; diff --git a/src/gui/kernel/qwindow_p.h b/src/gui/kernel/qwindow_p.h index b8a9f5d3de..95c6abf428 100644 --- a/src/gui/kernel/qwindow_p.h +++ b/src/gui/kernel/qwindow_p.h @@ -123,19 +123,7 @@ public: void deliverUpdateRequest(); - QPoint globalPosition() const { - Q_Q(const QWindow); - QPoint offset = q->position(); - for (const QWindow *p = q->parent(); p; p = p->parent()) { - if (p->type() != Qt::ForeignWindow) { - offset += p->position(); - } else { // QTBUG-43252, mapToGlobal() for foreign children. - offset += p->mapToGlobal(QPoint(0, 0)); - break; - } - } - return offset; - } + QPoint globalPosition() const; QWindow *topLevelWindow() const; @@ -144,6 +132,9 @@ public: void updateVisibility(); void _q_clearAlert(); + enum SiblingPosition { PositionTop, PositionBottom }; + void updateSiblingPosition(SiblingPosition); + bool windowRecreationRequired(QScreen *newScreen) const; void create(bool recursive); void setTopLevelScreen(QScreen *newScreen, bool recreate); diff --git a/src/gui/kernel/qwindowsysteminterface.cpp b/src/gui/kernel/qwindowsysteminterface.cpp index 667304859e..813b0f72bb 100644 --- a/src/gui/kernel/qwindowsysteminterface.cpp +++ b/src/gui/kernel/qwindowsysteminterface.cpp @@ -59,41 +59,57 @@ QWaitCondition QWindowSystemInterfacePrivate::eventsFlushed; QMutex QWindowSystemInterfacePrivate::flushEventMutex; QAtomicInt QWindowSystemInterfacePrivate::eventAccepted; QWindowSystemEventHandler *QWindowSystemInterfacePrivate::eventHandler; - -//------------------------------------------------------------ -// -// Callback functions for plugins: -// - QWindowSystemInterfacePrivate::WindowSystemEventList QWindowSystemInterfacePrivate::windowSystemEventQueue; extern QPointer<QWindow> qt_last_mouse_receiver; + +// ------------------- QWindowSystemInterfacePrivate ------------------- + /*! Handles a window system event asynchronously by posting the event to Qt Gui. - \sa postWindowSystemEvent() + This function posts the event on the window system event queue and wakes the + Gui event dispatcher. Qt Gui will then handle the event asynchonously at a + later point. */ template<> bool QWindowSystemInterfacePrivate::handleWindowSystemEvent<QWindowSystemInterface::AsynchronousDelivery>(WindowSystemEvent *ev) { - QWindowSystemInterfacePrivate::postWindowSystemEvent(ev); + windowSystemEventQueue.append(ev); + if (QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::qt_qpa_core_dispatcher()) + dispatcher->wakeUp(); return true; } /*! Handles a window system event synchronously. + Qt Gui will process the event immediately. The return value indicates if Qt + accepted the event. + If the event is delivered from another thread than the Qt main thread the window system event queue is flushed, which may deliver other events as well. - - \sa processWindowSystemEvent() */ template<> bool QWindowSystemInterfacePrivate::handleWindowSystemEvent<QWindowSystemInterface::SynchronousDelivery>(WindowSystemEvent *ev) { - return QWindowSystemInterfacePrivate::processWindowSystemEvent(ev); + bool accepted = true; + if (QThread::currentThread() == QGuiApplication::instance()->thread()) { + // Process the event immediately on the current thread and return the accepted state. + QGuiApplicationPrivate::processWindowSystemEvent(ev); + accepted = ev->eventAccepted; + delete ev; + } else { + // Post the event on the Qt main thread queue and flush the queue. + // This will wake up the Gui thread which will process the event. + // Return the accepted state for the last event on the queue, + // which is the event posted by this function. + handleWindowSystemEvent<QWindowSystemInterface::AsynchronousDelivery>(ev); + accepted = QWindowSystemInterface::flushWindowSystemEvents(); + } + return accepted; } /*! @@ -109,7 +125,7 @@ bool QWindowSystemInterfacePrivate::handleWindowSystemEvent<QWindowSystemInterfa than the Qt main thread the window system event queue is flushed, which may deliver other events as well. - \sa flushWindowSystemEvents(), processWindowSystemEvent(), setSynchronousWindowSystemEvents() + \sa flushWindowSystemEvents(), setSynchronousWindowSystemEvents() */ template<> bool QWindowSystemInterfacePrivate::handleWindowSystemEvent<QWindowSystemInterface::DefaultDelivery>(QWindowSystemInterfacePrivate::WindowSystemEvent *ev) @@ -120,6 +136,59 @@ bool QWindowSystemInterfacePrivate::handleWindowSystemEvent<QWindowSystemInterfa return handleWindowSystemEvent<QWindowSystemInterface::AsynchronousDelivery>(ev); } +int QWindowSystemInterfacePrivate::windowSystemEventsQueued() +{ + return windowSystemEventQueue.count(); +} + +QWindowSystemInterfacePrivate::WindowSystemEvent * QWindowSystemInterfacePrivate::getWindowSystemEvent() +{ + return windowSystemEventQueue.takeFirstOrReturnNull(); +} + +QWindowSystemInterfacePrivate::WindowSystemEvent *QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent() +{ + return windowSystemEventQueue.takeFirstNonUserInputOrReturnNull(); +} + +QWindowSystemInterfacePrivate::WindowSystemEvent *QWindowSystemInterfacePrivate::peekWindowSystemEvent(EventType t) +{ + return windowSystemEventQueue.peekAtFirstOfType(t); +} + +void QWindowSystemInterfacePrivate::removeWindowSystemEvent(WindowSystemEvent *event) +{ + windowSystemEventQueue.remove(event); +} + +void QWindowSystemInterfacePrivate::installWindowSystemEventHandler(QWindowSystemEventHandler *handler) +{ + if (!eventHandler) + eventHandler = handler; +} + +void QWindowSystemInterfacePrivate::removeWindowSystemEventhandler(QWindowSystemEventHandler *handler) +{ + if (eventHandler == handler) + eventHandler = 0; +} + +QWindowSystemEventHandler::~QWindowSystemEventHandler() +{ + QWindowSystemInterfacePrivate::removeWindowSystemEventhandler(this); +} + +bool QWindowSystemEventHandler::sendEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e) +{ + QGuiApplicationPrivate::processWindowSystemEvent(e); + return true; +} + +//------------------------------------------------------------ +// +// Callback functions for plugins: +// + #define QT_DEFINE_QPA_EVENT_HANDLER(ReturnType, HandlerName, ...) \ template Q_GUI_EXPORT ReturnType QWindowSystemInterface::HandlerName<QWindowSystemInterface::DefaultDelivery>(__VA_ARGS__); \ template Q_GUI_EXPORT ReturnType QWindowSystemInterface::HandlerName<QWindowSystemInterface::SynchronousDelivery>(__VA_ARGS__); \ @@ -138,17 +207,17 @@ bool QWindowSystemInterfacePrivate::handleWindowSystemEvent<QWindowSystemInterfa until sendWindowSystemEvents() is called by the event dispatcher. */ -QT_DEFINE_QPA_EVENT_HANDLER(void, handleEnterEvent, QWindow *tlw, const QPointF &local, const QPointF &global) +QT_DEFINE_QPA_EVENT_HANDLER(void, handleEnterEvent, QWindow *window, const QPointF &local, const QPointF &global) { - if (tlw) { - QWindowSystemInterfacePrivate::EnterEvent *e = new QWindowSystemInterfacePrivate::EnterEvent(tlw, local, global); + if (window) { + QWindowSystemInterfacePrivate::EnterEvent *e = new QWindowSystemInterfacePrivate::EnterEvent(window, local, global); QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e); } } -QT_DEFINE_QPA_EVENT_HANDLER(void, handleLeaveEvent, QWindow *tlw) +QT_DEFINE_QPA_EVENT_HANDLER(void, handleLeaveEvent, QWindow *window) { - QWindowSystemInterfacePrivate::LeaveEvent *e = new QWindowSystemInterfacePrivate::LeaveEvent(tlw); + QWindowSystemInterfacePrivate::LeaveEvent *e = new QWindowSystemInterfacePrivate::LeaveEvent(window); QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e); } @@ -165,24 +234,24 @@ void QWindowSystemInterface::handleEnterLeaveEvent(QWindow *enter, QWindow *leav handleEnterEvent(enter, local, global); } -QT_DEFINE_QPA_EVENT_HANDLER(void, handleWindowActivated, QWindow *tlw, Qt::FocusReason r) +QT_DEFINE_QPA_EVENT_HANDLER(void, handleWindowActivated, QWindow *window, Qt::FocusReason r) { QWindowSystemInterfacePrivate::ActivatedWindowEvent *e = - new QWindowSystemInterfacePrivate::ActivatedWindowEvent(tlw, r); + new QWindowSystemInterfacePrivate::ActivatedWindowEvent(window, r); QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e); } -void QWindowSystemInterface::handleWindowStateChanged(QWindow *tlw, Qt::WindowState newState) +void QWindowSystemInterface::handleWindowStateChanged(QWindow *window, Qt::WindowState newState) { QWindowSystemInterfacePrivate::WindowStateChangedEvent *e = - new QWindowSystemInterfacePrivate::WindowStateChangedEvent(tlw, newState); + new QWindowSystemInterfacePrivate::WindowStateChangedEvent(window, newState); QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); } -void QWindowSystemInterface::handleWindowScreenChanged(QWindow *tlw, QScreen *screen) +void QWindowSystemInterface::handleWindowScreenChanged(QWindow *window, QScreen *screen) { QWindowSystemInterfacePrivate::WindowScreenChangedEvent *e = - new QWindowSystemInterfacePrivate::WindowScreenChangedEvent(tlw, screen); + new QWindowSystemInterfacePrivate::WindowScreenChangedEvent(window, screen); QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); } @@ -197,9 +266,9 @@ void QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState /*! If \a oldRect is null, Qt will use the previously reported geometry instead. */ -QT_DEFINE_QPA_EVENT_HANDLER(void, handleGeometryChange, QWindow *tlw, const QRect &newRect, const QRect &oldRect) +QT_DEFINE_QPA_EVENT_HANDLER(void, handleGeometryChange, QWindow *window, const QRect &newRect, const QRect &oldRect) { - QWindowSystemInterfacePrivate::GeometryChangeEvent *e = new QWindowSystemInterfacePrivate::GeometryChangeEvent(tlw, QHighDpi::fromNativePixels(newRect, tlw), QHighDpi::fromNativePixels(oldRect, tlw)); + QWindowSystemInterfacePrivate::GeometryChangeEvent *e = new QWindowSystemInterfacePrivate::GeometryChangeEvent(window, QHighDpi::fromNativePixels(newRect, window), QHighDpi::fromNativePixels(oldRect, window)); QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e); } @@ -211,18 +280,18 @@ QWindowSystemInterfacePrivate::ExposeEvent::ExposeEvent(QWindow *window, const Q { } -QT_DEFINE_QPA_EVENT_HANDLER(void, handleExposeEvent, QWindow *tlw, const QRegion ®ion) +QT_DEFINE_QPA_EVENT_HANDLER(void, handleExposeEvent, QWindow *window, const QRegion ®ion) { QWindowSystemInterfacePrivate::ExposeEvent *e = - new QWindowSystemInterfacePrivate::ExposeEvent(tlw, QHighDpi::fromNativeLocalExposedRegion(region, tlw)); + new QWindowSystemInterfacePrivate::ExposeEvent(window, QHighDpi::fromNativeLocalExposedRegion(region, window)); QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e); } -void QWindowSystemInterface::handleCloseEvent(QWindow *tlw, bool *accepted) +void QWindowSystemInterface::handleCloseEvent(QWindow *window, bool *accepted) { - if (tlw) { + if (window) { QWindowSystemInterfacePrivate::CloseEvent *e = - new QWindowSystemInterfacePrivate::CloseEvent(tlw, accepted); + new QWindowSystemInterfacePrivate::CloseEvent(window, accepted); QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); } } @@ -232,35 +301,35 @@ void QWindowSystemInterface::handleCloseEvent(QWindow *tlw, bool *accepted) \a w == 0 means that the event is in global coords only, \a local will be ignored in this case */ -QT_DEFINE_QPA_EVENT_HANDLER(void, handleMouseEvent, QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, +QT_DEFINE_QPA_EVENT_HANDLER(void, handleMouseEvent, QWindow *window, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods, Qt::MouseEventSource source) { unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); - handleMouseEvent<Delivery>(w, time, local, global, b, mods, source); + handleMouseEvent<Delivery>(window, time, local, global, b, mods, source); } -QT_DEFINE_QPA_EVENT_HANDLER(void, handleMouseEvent, QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b, +QT_DEFINE_QPA_EVENT_HANDLER(void, handleMouseEvent, QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods, Qt::MouseEventSource source) { QWindowSystemInterfacePrivate::MouseEvent * e = - new QWindowSystemInterfacePrivate::MouseEvent(w, timestamp, QHighDpi::fromNativeLocalPosition(local, w), QHighDpi::fromNativePixels(global, w), b, mods, source); + new QWindowSystemInterfacePrivate::MouseEvent(window, timestamp, QHighDpi::fromNativeLocalPosition(local, window), QHighDpi::fromNativePixels(global, window), b, mods, source); QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e); } -void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, +void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *window, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods, Qt::MouseEventSource source) { const unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); - handleFrameStrutMouseEvent(w, time, local, global, b, mods, source); + handleFrameStrutMouseEvent(window, time, local, global, b, mods, source); } -void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b, +void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods, Qt::MouseEventSource source) { QWindowSystemInterfacePrivate::MouseEvent * e = - new QWindowSystemInterfacePrivate::MouseEvent(w, timestamp, + new QWindowSystemInterfacePrivate::MouseEvent(window, timestamp, QWindowSystemInterfacePrivate::FrameStrutMouse, - QHighDpi::fromNativeLocalPosition(local, w), QHighDpi::fromNativePixels(global, w), b, mods, source); + QHighDpi::fromNativeLocalPosition(local, window), QHighDpi::fromNativePixels(global, window), b, mods, source); QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); } @@ -309,35 +378,35 @@ bool QWindowSystemInterface::handleShortcutEvent(QWindow *window, ulong timestam #endif } -QT_DEFINE_QPA_EVENT_HANDLER(bool, handleKeyEvent, QWindow *w, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text, bool autorep, ushort count) { +QT_DEFINE_QPA_EVENT_HANDLER(bool, handleKeyEvent, QWindow *window, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text, bool autorep, ushort count) { unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); - return handleKeyEvent<Delivery>(w, time, t, k, mods, text, autorep, count); + return handleKeyEvent<Delivery>(window, time, t, k, mods, text, autorep, count); } -QT_DEFINE_QPA_EVENT_HANDLER(bool, handleKeyEvent, QWindow *tlw, ulong timestamp, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text, bool autorep, ushort count) +QT_DEFINE_QPA_EVENT_HANDLER(bool, handleKeyEvent, QWindow *window, ulong timestamp, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text, bool autorep, ushort count) { #if defined(Q_OS_OSX) - if (t == QEvent::KeyPress && QWindowSystemInterface::handleShortcutEvent(tlw, timestamp, k, mods, 0, 0, 0, text, autorep, count)) + if (t == QEvent::KeyPress && QWindowSystemInterface::handleShortcutEvent(window, timestamp, k, mods, 0, 0, 0, text, autorep, count)) return true; #endif QWindowSystemInterfacePrivate::KeyEvent * e = - new QWindowSystemInterfacePrivate::KeyEvent(tlw, timestamp, t, k, mods, text, autorep, count); + new QWindowSystemInterfacePrivate::KeyEvent(window, timestamp, t, k, mods, text, autorep, count); return QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e); } -bool QWindowSystemInterface::handleExtendedKeyEvent(QWindow *w, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, +bool QWindowSystemInterface::handleExtendedKeyEvent(QWindow *window, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, const QString& text, bool autorep, ushort count, bool tryShortcutOverride) { unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); - return handleExtendedKeyEvent(w, time, type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, + return handleExtendedKeyEvent(window, time, type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count, tryShortcutOverride); } -bool QWindowSystemInterface::handleExtendedKeyEvent(QWindow *tlw, ulong timestamp, QEvent::Type type, int key, +bool QWindowSystemInterface::handleExtendedKeyEvent(QWindow *window, ulong timestamp, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, @@ -345,7 +414,7 @@ bool QWindowSystemInterface::handleExtendedKeyEvent(QWindow *tlw, ulong timestam ushort count, bool tryShortcutOverride) { #if defined(Q_OS_OSX) - if (tryShortcutOverride && type == QEvent::KeyPress && QWindowSystemInterface::handleShortcutEvent(tlw, + if (tryShortcutOverride && type == QEvent::KeyPress && QWindowSystemInterface::handleShortcutEvent(window, timestamp, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count)) { return true; } @@ -354,29 +423,36 @@ bool QWindowSystemInterface::handleExtendedKeyEvent(QWindow *tlw, ulong timestam #endif QWindowSystemInterfacePrivate::KeyEvent * e = - new QWindowSystemInterfacePrivate::KeyEvent(tlw, timestamp, type, key, modifiers, + new QWindowSystemInterfacePrivate::KeyEvent(window, timestamp, type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count); return QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); } -void QWindowSystemInterface::handleWheelEvent(QWindow *w, const QPointF & local, const QPointF & global, int d, Qt::Orientation o, Qt::KeyboardModifiers mods) { +QWindowSystemInterfacePrivate::WheelEvent::WheelEvent(QWindow *window, ulong time, const QPointF &local, const QPointF &global, QPoint pixelD, + QPoint angleD, int qt4D, Qt::Orientation qt4O, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase, Qt::MouseEventSource src, bool inverted) + : InputEvent(window, time, Wheel, mods), pixelDelta(pixelD), angleDelta(angleD), qt4Delta(qt4D), + qt4Orientation(qt4O), localPos(local), globalPos(global), phase(phase), source(src), inverted(inverted) +{ +} + +void QWindowSystemInterface::handleWheelEvent(QWindow *window, const QPointF &local, const QPointF &global, int d, Qt::Orientation o, Qt::KeyboardModifiers mods) { unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); - handleWheelEvent(w, time, local, global, d, o, mods); + handleWheelEvent(window, time, local, global, d, o, mods); } -void QWindowSystemInterface::handleWheelEvent(QWindow *tlw, ulong timestamp, const QPointF & local, const QPointF & global, int d, Qt::Orientation o, Qt::KeyboardModifiers mods) +void QWindowSystemInterface::handleWheelEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, int d, Qt::Orientation o, Qt::KeyboardModifiers mods) { QPoint point = (o == Qt::Vertical) ? QPoint(0, d) : QPoint(d, 0); - handleWheelEvent(tlw, timestamp, local, global, QPoint(), point, mods); + handleWheelEvent(window, timestamp, local, global, QPoint(), point, mods); } -void QWindowSystemInterface::handleWheelEvent(QWindow *w, const QPointF & local, const QPointF & global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase, Qt::MouseEventSource source) +void QWindowSystemInterface::handleWheelEvent(QWindow *window, const QPointF &local, const QPointF &global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase, Qt::MouseEventSource source) { unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); - handleWheelEvent(w, time, local, global, pixelDelta, angleDelta, mods, phase, source); + handleWheelEvent(window, time, local, global, pixelDelta, angleDelta, mods, phase, source); } -void QWindowSystemInterface::handleWheelEvent(QWindow *tlw, ulong timestamp, const QPointF & local, const QPointF & global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase, +void QWindowSystemInterface::handleWheelEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase, Qt::MouseEventSource source, bool invertedScrolling) { // Qt 4 sends two separate wheel events for horizontal and vertical @@ -395,7 +471,7 @@ void QWindowSystemInterface::handleWheelEvent(QWindow *tlw, ulong timestamp, con // Simple case: vertical deltas only: if (angleDelta.y() != 0 && angleDelta.x() == 0) { - e = new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, QHighDpi::fromNativeLocalPosition(local, tlw), QHighDpi::fromNativePixels(global, tlw), pixelDelta, angleDelta, angleDelta.y(), Qt::Vertical, + e = new QWindowSystemInterfacePrivate::WheelEvent(window, timestamp, QHighDpi::fromNativeLocalPosition(local, window), QHighDpi::fromNativePixels(global, window), pixelDelta, angleDelta, angleDelta.y(), Qt::Vertical, mods, phase, source, invertedScrolling); QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); return; @@ -403,7 +479,7 @@ void QWindowSystemInterface::handleWheelEvent(QWindow *tlw, ulong timestamp, con // Simple case: horizontal deltas only: if (angleDelta.y() == 0 && angleDelta.x() != 0) { - e = new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, QHighDpi::fromNativeLocalPosition(local, tlw), QHighDpi::fromNativePixels(global, tlw), pixelDelta, angleDelta, angleDelta.x(), Qt::Horizontal, mods, phase, source, invertedScrolling); + e = new QWindowSystemInterfacePrivate::WheelEvent(window, timestamp, QHighDpi::fromNativeLocalPosition(local, window), QHighDpi::fromNativePixels(global, window), pixelDelta, angleDelta, angleDelta.x(), Qt::Horizontal, mods, phase, source, invertedScrolling); QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); return; } @@ -411,106 +487,65 @@ void QWindowSystemInterface::handleWheelEvent(QWindow *tlw, ulong timestamp, con // Both horizontal and vertical deltas: Send two wheel events. // The first event contains the Qt 5 pixel and angle delta as points, // and in addition the Qt 4 compatibility vertical angle delta. - e = new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, QHighDpi::fromNativeLocalPosition(local, tlw), QHighDpi::fromNativePixels(global, tlw), pixelDelta, angleDelta, angleDelta.y(), Qt::Vertical, mods, phase, source, invertedScrolling); + e = new QWindowSystemInterfacePrivate::WheelEvent(window, timestamp, QHighDpi::fromNativeLocalPosition(local, window), QHighDpi::fromNativePixels(global, window), pixelDelta, angleDelta, angleDelta.y(), Qt::Vertical, mods, phase, source, invertedScrolling); QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); // The second event contains null pixel and angle points and the // Qt 4 compatibility horizontal angle delta. - e = new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, QHighDpi::fromNativeLocalPosition(local, tlw), QHighDpi::fromNativePixels(global, tlw), QPoint(), QPoint(), angleDelta.x(), Qt::Horizontal, mods, phase, source, invertedScrolling); + e = new QWindowSystemInterfacePrivate::WheelEvent(window, timestamp, QHighDpi::fromNativeLocalPosition(local, window), QHighDpi::fromNativePixels(global, window), QPoint(), QPoint(), angleDelta.x(), Qt::Horizontal, mods, phase, source, invertedScrolling); QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); } -int QWindowSystemInterfacePrivate::windowSystemEventsQueued() -{ - return windowSystemEventQueue.count(); -} - -QWindowSystemInterfacePrivate::WindowSystemEvent * QWindowSystemInterfacePrivate::getWindowSystemEvent() +void QWindowSystemInterface::registerTouchDevice(const QTouchDevice *device) { - return windowSystemEventQueue.takeFirstOrReturnNull(); + QTouchDevicePrivate::registerDevice(device); } -QWindowSystemInterfacePrivate::WindowSystemEvent *QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent() +void QWindowSystemInterface::unregisterTouchDevice(const QTouchDevice *device) { - return windowSystemEventQueue.takeFirstNonUserInputOrReturnNull(); + QTouchDevicePrivate::unregisterDevice(device); } -QWindowSystemInterfacePrivate::WindowSystemEvent *QWindowSystemInterfacePrivate::peekWindowSystemEvent(EventType t) +bool QWindowSystemInterface::isTouchDeviceRegistered(const QTouchDevice *device) { - return windowSystemEventQueue.peekAtFirstOfType(t); + return QTouchDevicePrivate::isRegistered(device); } -void QWindowSystemInterfacePrivate::removeWindowSystemEvent(WindowSystemEvent *event) -{ - windowSystemEventQueue.remove(event); -} +static int g_nextPointId = 1; -/*! - Posts a window system event to be handled asynchronously by Qt Gui. - - This function posts the event on the window system event queue and wakes the - Gui event dispatcher. Qt Gui will then handle the event asynchonously at a - later point. - - \sa flushWindowSystemEvents(), processWindowSystemEvent(), handleWindowSystemEvent() -*/ -void QWindowSystemInterfacePrivate::postWindowSystemEvent(WindowSystemEvent *ev) -{ - windowSystemEventQueue.append(ev); - QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::qt_qpa_core_dispatcher(); - if (dispatcher) - dispatcher->wakeUp(); -} +// map from device-independent point id (arbitrary) to "Qt point" ids +typedef QMap<quint64, int> PointIdMap; +Q_GLOBAL_STATIC(PointIdMap, g_pointIdMap) /*! - Processes a window system event synchronously. - - Qt Gui will process the event immediately. The return value indicates if Qt - accepted the event. - - If the event is delivered from another thread than the Qt main thread the - window system event queue is flushed, which may deliver other events as - well. - - \sa flushWindowSystemEvents(), postWindowSystemEvent(), handleWindowSystemEvent() + \internal + This function maps potentially arbitrary point ids \a pointId in the 32 bit + value space to start from 1 and increase incrementally for each touch point + held down. If all touch points are released it will reset the id back to 1 + for the following touch point. + + We can then assume that the touch points ids will never become too large, + and it will then put the device identifier \a deviceId in the upper 8 bits. + This leaves us with max 255 devices, and 16.7M taps without full release + before we run out of value space. */ -bool QWindowSystemInterfacePrivate::processWindowSystemEvent(WindowSystemEvent *ev) -{ - bool accepted = true; - if (QThread::currentThread() == QGuiApplication::instance()->thread()) { - // Process the event immediately on the current thread and return the accepted state. - QGuiApplicationPrivate::processWindowSystemEvent(ev); - accepted = ev->eventAccepted; - delete ev; +static int acquireCombinedPointId(quint8 deviceId, int pointId) +{ + quint64 combinedId64 = (quint64(deviceId) << 32) + pointId; + auto it = g_pointIdMap->constFind(combinedId64); + int uid; + if (it == g_pointIdMap->constEnd()) { + uid = g_nextPointId++; + g_pointIdMap->insert(combinedId64, uid); } else { - // Post the event on the Qt main thread queue and flush the queue. - // This will wake up the Gui thread which will process the event. - // Return the accepted state for the last event on the queue, - // which is the event posted by this function. - postWindowSystemEvent(ev); - accepted = QWindowSystemInterface::flushWindowSystemEvents(); + uid = *it; } - return accepted; -} - -void QWindowSystemInterface::registerTouchDevice(const QTouchDevice *device) -{ - QTouchDevicePrivate::registerDevice(device); -} - -void QWindowSystemInterface::unregisterTouchDevice(const QTouchDevice *device) -{ - QTouchDevicePrivate::unregisterDevice(device); -} - -bool QWindowSystemInterface::isTouchDeviceRegistered(const QTouchDevice *device) -{ - return QTouchDevicePrivate::isRegistered(device); + return (deviceId << 24) + uid; } QList<QTouchEvent::TouchPoint> QWindowSystemInterfacePrivate::fromNativeTouchPoints(const QList<QWindowSystemInterface::TouchPoint> &points, - const QWindow *window, + const QWindow *window, quint8 deviceId, QEvent::Type *type) { QList<QTouchEvent::TouchPoint> touchPoints; @@ -521,7 +556,7 @@ QList<QTouchEvent::TouchPoint> QList<QWindowSystemInterface::TouchPoint>::const_iterator point = points.constBegin(); QList<QWindowSystemInterface::TouchPoint>::const_iterator end = points.constEnd(); while (point != end) { - p.setId(point->id); + p.setId(acquireCombinedPointId(deviceId, point->id)); if (point->uniqueId >= 0) p.setUniqueId(point->uniqueId); p.setPressure(point->pressure); @@ -554,6 +589,11 @@ QList<QTouchEvent::TouchPoint> *type = QEvent::TouchEnd; } + if (states == Qt::TouchPointReleased) { + g_nextPointId = 1; + g_pointIdMap->clear(); + } + return touchPoints; } @@ -578,14 +618,14 @@ QList<QWindowSystemInterface::TouchPoint> return newList; } -QT_DEFINE_QPA_EVENT_HANDLER(void, handleTouchEvent, QWindow *w, QTouchDevice *device, +QT_DEFINE_QPA_EVENT_HANDLER(void, handleTouchEvent, QWindow *window, QTouchDevice *device, const QList<TouchPoint> &points, Qt::KeyboardModifiers mods) { unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); - handleTouchEvent<Delivery>(w, time, device, points, mods); + handleTouchEvent<Delivery>(window, time, device, points, mods); } -QT_DEFINE_QPA_EVENT_HANDLER(void, handleTouchEvent, QWindow *tlw, ulong timestamp, QTouchDevice *device, +QT_DEFINE_QPA_EVENT_HANDLER(void, handleTouchEvent, QWindow *window, ulong timestamp, QTouchDevice *device, const QList<TouchPoint> &points, Qt::KeyboardModifiers mods) { if (!points.size()) // Touch events must have at least one point @@ -595,25 +635,26 @@ QT_DEFINE_QPA_EVENT_HANDLER(void, handleTouchEvent, QWindow *tlw, ulong timestam return; QEvent::Type type; - QList<QTouchEvent::TouchPoint> touchPoints = QWindowSystemInterfacePrivate::fromNativeTouchPoints(points, tlw, &type); + QList<QTouchEvent::TouchPoint> touchPoints = + QWindowSystemInterfacePrivate::fromNativeTouchPoints(points, window, QTouchDevicePrivate::get(device)->id, &type); QWindowSystemInterfacePrivate::TouchEvent *e = - new QWindowSystemInterfacePrivate::TouchEvent(tlw, timestamp, type, device, touchPoints, mods); + new QWindowSystemInterfacePrivate::TouchEvent(window, timestamp, type, device, touchPoints, mods); QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e); } -QT_DEFINE_QPA_EVENT_HANDLER(void, handleTouchCancelEvent, QWindow *w, QTouchDevice *device, +QT_DEFINE_QPA_EVENT_HANDLER(void, handleTouchCancelEvent, QWindow *window, QTouchDevice *device, Qt::KeyboardModifiers mods) { unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); - handleTouchCancelEvent<Delivery>(w, time, device, mods); + handleTouchCancelEvent<Delivery>(window, time, device, mods); } -QT_DEFINE_QPA_EVENT_HANDLER(void, handleTouchCancelEvent, QWindow *w, ulong timestamp, QTouchDevice *device, +QT_DEFINE_QPA_EVENT_HANDLER(void, handleTouchCancelEvent, QWindow *window, ulong timestamp, QTouchDevice *device, Qt::KeyboardModifiers mods) { QWindowSystemInterfacePrivate::TouchEvent *e = - new QWindowSystemInterfacePrivate::TouchEvent(w, timestamp, QEvent::TouchCancel, device, + new QWindowSystemInterfacePrivate::TouchEvent(window, timestamp, QEvent::TouchCancel, device, QList<QTouchEvent::TouchPoint>(), mods); QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e); } @@ -646,113 +687,21 @@ void QWindowSystemInterface::handleScreenRefreshRateChange(QScreen *screen, qrea QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); } -void QWindowSystemInterface::handleThemeChange(QWindow *tlw) +void QWindowSystemInterface::handleThemeChange(QWindow *window) { - QWindowSystemInterfacePrivate::ThemeChangeEvent *e = new QWindowSystemInterfacePrivate::ThemeChangeEvent(tlw); + QWindowSystemInterfacePrivate::ThemeChangeEvent *e = new QWindowSystemInterfacePrivate::ThemeChangeEvent(window); QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); } -void QWindowSystemInterface::deferredFlushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags) -{ - Q_ASSERT(QThread::currentThread() == QGuiApplication::instance()->thread()); - - QMutexLocker locker(&QWindowSystemInterfacePrivate::flushEventMutex); - sendWindowSystemEvents(flags); - QWindowSystemInterfacePrivate::eventsFlushed.wakeOne(); -} - -/*! - Make Qt Gui process all events on the event queue immediately. Return the - accepted state for the last event on the queue. -*/ -bool QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags) -{ - const int count = QWindowSystemInterfacePrivate::windowSystemEventQueue.count(); - if (!count) - return false; - if (!QGuiApplication::instance()) { - qWarning().nospace() - << "QWindowSystemInterface::flushWindowSystemEvents() invoked after " - "QGuiApplication destruction, discarding " << count << " events."; - QWindowSystemInterfacePrivate::windowSystemEventQueue.clear(); - return false; - } - if (QThread::currentThread() != QGuiApplication::instance()->thread()) { - // Post a FlushEvents event which will trigger a call back to - // deferredFlushWindowSystemEvents from the Gui thread. - QMutexLocker locker(&QWindowSystemInterfacePrivate::flushEventMutex); - QWindowSystemInterfacePrivate::FlushEventsEvent *e = new QWindowSystemInterfacePrivate::FlushEventsEvent(flags); - QWindowSystemInterfacePrivate::handleWindowSystemEvent<AsynchronousDelivery>(e); - QWindowSystemInterfacePrivate::eventsFlushed.wait(&QWindowSystemInterfacePrivate::flushEventMutex); - } else { - sendWindowSystemEvents(flags); - } - return QWindowSystemInterfacePrivate::eventAccepted.load() > 0; -} - -bool QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::ProcessEventsFlags flags) -{ - int nevents = 0; - - while (QWindowSystemInterfacePrivate::windowSystemEventsQueued()) { - QWindowSystemInterfacePrivate::WindowSystemEvent *event = - (flags & QEventLoop::ExcludeUserInputEvents) ? - QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent() : - QWindowSystemInterfacePrivate::getWindowSystemEvent(); - if (!event) - break; - - if (QWindowSystemInterfacePrivate::eventHandler) { - if (QWindowSystemInterfacePrivate::eventHandler->sendEvent(event)) - nevents++; - } else { - nevents++; - QGuiApplicationPrivate::processWindowSystemEvent(event); - } - - // Record the accepted state for the processed event - // (excluding flush events). This state can then be - // returned by flushWindowSystemEvents(). - if (event->type != QWindowSystemInterfacePrivate::FlushEvents) - QWindowSystemInterfacePrivate::eventAccepted.store(event->eventAccepted); - - delete event; - } - - return (nevents > 0); -} - -void QWindowSystemInterfacePrivate::installWindowSystemEventHandler(QWindowSystemEventHandler *handler) -{ - if (!eventHandler) - eventHandler = handler; -} - -void QWindowSystemInterfacePrivate::removeWindowSystemEventhandler(QWindowSystemEventHandler *handler) -{ - if (eventHandler == handler) - eventHandler = 0; -} - -void QWindowSystemInterface::setSynchronousWindowSystemEvents(bool enable) -{ - QWindowSystemInterfacePrivate::synchronousWindowSystemEvents = enable; -} - -int QWindowSystemInterface::windowSystemEventsQueued() -{ - return QWindowSystemInterfacePrivate::windowSystemEventsQueued(); -} - #ifndef QT_NO_DRAGANDDROP -QPlatformDragQtResponse QWindowSystemInterface::handleDrag(QWindow *w, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions) +QPlatformDragQtResponse QWindowSystemInterface::handleDrag(QWindow *window, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions) { - return QGuiApplicationPrivate::processDrag(w, dropData, QHighDpi::fromNativeLocalPosition(p, w) ,supportedActions); + return QGuiApplicationPrivate::processDrag(window, dropData, QHighDpi::fromNativeLocalPosition(p, window) ,supportedActions); } -QPlatformDropQtResponse QWindowSystemInterface::handleDrop(QWindow *w, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions) +QPlatformDropQtResponse QWindowSystemInterface::handleDrop(QWindow *window, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions) { - return QGuiApplicationPrivate::processDrop(w, dropData, QHighDpi::fromNativeLocalPosition(p, w),supportedActions); + return QGuiApplicationPrivate::processDrop(window, dropData, QHighDpi::fromNativeLocalPosition(p, window),supportedActions); } #endif // QT_NO_DRAGANDDROP @@ -780,45 +729,50 @@ void QWindowSystemInterface::handleFileOpenEvent(const QUrl &url) QGuiApplicationPrivate::processWindowSystemEvent(&e); } -void QWindowSystemInterface::handleTabletEvent(QWindow *w, ulong timestamp, const QPointF &local, const QPointF &global, +void QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(bool v) +{ + platformSynthesizesMouse = v; +} + +void QWindowSystemInterface::handleTabletEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, qint64 uid, Qt::KeyboardModifiers modifiers) { QWindowSystemInterfacePrivate::TabletEvent *e = - new QWindowSystemInterfacePrivate::TabletEvent(w,timestamp, - QHighDpi::fromNativeLocalPosition(local, w), - QHighDpi::fromNativePixels(global, w), + new QWindowSystemInterfacePrivate::TabletEvent(window, timestamp, + QHighDpi::fromNativeLocalPosition(local, window), + QHighDpi::fromNativePixels(global, window), device, pointerType, buttons, pressure, xTilt, yTilt, tangentialPressure, rotation, z, uid, modifiers); QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); } -void QWindowSystemInterface::handleTabletEvent(QWindow *w, const QPointF &local, const QPointF &global, +void QWindowSystemInterface::handleTabletEvent(QWindow *window, const QPointF &local, const QPointF &global, int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, qint64 uid, Qt::KeyboardModifiers modifiers) { ulong time = QWindowSystemInterfacePrivate::eventTime.elapsed(); - handleTabletEvent(w, time, local, global, device, pointerType, buttons, pressure, + handleTabletEvent(window, time, local, global, device, pointerType, buttons, pressure, xTilt, yTilt, tangentialPressure, rotation, z, uid, modifiers); } -void QWindowSystemInterface::handleTabletEvent(QWindow *w, ulong timestamp, bool down, const QPointF &local, const QPointF &global, +void QWindowSystemInterface::handleTabletEvent(QWindow *window, ulong timestamp, bool down, const QPointF &local, const QPointF &global, int device, int pointerType, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, qint64 uid, Qt::KeyboardModifiers modifiers) { - handleTabletEvent(w, timestamp, local, global, device, pointerType, (down ? Qt::LeftButton : Qt::NoButton), pressure, + handleTabletEvent(window, timestamp, local, global, device, pointerType, (down ? Qt::LeftButton : Qt::NoButton), pressure, xTilt, yTilt, tangentialPressure, rotation, z, uid, modifiers); } -void QWindowSystemInterface::handleTabletEvent(QWindow *w, bool down, const QPointF &local, const QPointF &global, +void QWindowSystemInterface::handleTabletEvent(QWindow *window, bool down, const QPointF &local, const QPointF &global, int device, int pointerType, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, qint64 uid, Qt::KeyboardModifiers modifiers) { - handleTabletEvent(w, local, global, device, pointerType, (down ? Qt::LeftButton : Qt::NoButton), pressure, + handleTabletEvent(window, local, global, device, pointerType, (down ? Qt::LeftButton : Qt::NoButton), pressure, xTilt, yTilt, tangentialPressure, rotation, z, uid, modifiers); } @@ -885,12 +839,12 @@ void QWindowSystemInterface::handlePlatformPanelEvent(QWindow *w) } #ifndef QT_NO_CONTEXTMENU -void QWindowSystemInterface::handleContextMenuEvent(QWindow *w, bool mouseTriggered, +void QWindowSystemInterface::handleContextMenuEvent(QWindow *window, bool mouseTriggered, const QPoint &pos, const QPoint &globalPos, Qt::KeyboardModifiers modifiers) { QWindowSystemInterfacePrivate::ContextMenuEvent *e = - new QWindowSystemInterfacePrivate::ContextMenuEvent(w, mouseTriggered, pos, + new QWindowSystemInterfacePrivate::ContextMenuEvent(window, mouseTriggered, pos, globalPos, modifiers); QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); } @@ -915,18 +869,102 @@ Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QWindowSystemInterface::TouchPo } #endif +// ------------------ Event dispatcher functionality ------------------ + +/*! + Make Qt Gui process all events on the event queue immediately. Return the + accepted state for the last event on the queue. +*/ +bool QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags) +{ + const int count = QWindowSystemInterfacePrivate::windowSystemEventQueue.count(); + if (!count) + return false; + if (!QGuiApplication::instance()) { + qWarning().nospace() + << "QWindowSystemInterface::flushWindowSystemEvents() invoked after " + "QGuiApplication destruction, discarding " << count << " events."; + QWindowSystemInterfacePrivate::windowSystemEventQueue.clear(); + return false; + } + if (QThread::currentThread() != QGuiApplication::instance()->thread()) { + // Post a FlushEvents event which will trigger a call back to + // deferredFlushWindowSystemEvents from the Gui thread. + QMutexLocker locker(&QWindowSystemInterfacePrivate::flushEventMutex); + QWindowSystemInterfacePrivate::FlushEventsEvent *e = new QWindowSystemInterfacePrivate::FlushEventsEvent(flags); + QWindowSystemInterfacePrivate::handleWindowSystemEvent<AsynchronousDelivery>(e); + QWindowSystemInterfacePrivate::eventsFlushed.wait(&QWindowSystemInterfacePrivate::flushEventMutex); + } else { + sendWindowSystemEvents(flags); + } + return QWindowSystemInterfacePrivate::eventAccepted.load() > 0; +} + +void QWindowSystemInterface::deferredFlushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags) +{ + Q_ASSERT(QThread::currentThread() == QGuiApplication::instance()->thread()); + + QMutexLocker locker(&QWindowSystemInterfacePrivate::flushEventMutex); + sendWindowSystemEvents(flags); + QWindowSystemInterfacePrivate::eventsFlushed.wakeOne(); +} + +bool QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::ProcessEventsFlags flags) +{ + int nevents = 0; + + while (QWindowSystemInterfacePrivate::windowSystemEventsQueued()) { + QWindowSystemInterfacePrivate::WindowSystemEvent *event = + (flags & QEventLoop::ExcludeUserInputEvents) ? + QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent() : + QWindowSystemInterfacePrivate::getWindowSystemEvent(); + if (!event) + break; + + if (QWindowSystemInterfacePrivate::eventHandler) { + if (QWindowSystemInterfacePrivate::eventHandler->sendEvent(event)) + nevents++; + } else { + nevents++; + QGuiApplicationPrivate::processWindowSystemEvent(event); + } + + // Record the accepted state for the processed event + // (excluding flush events). This state can then be + // returned by flushWindowSystemEvents(). + if (event->type != QWindowSystemInterfacePrivate::FlushEvents) + QWindowSystemInterfacePrivate::eventAccepted.store(event->eventAccepted); + + delete event; + } + + return (nevents > 0); +} + +void QWindowSystemInterface::setSynchronousWindowSystemEvents(bool enable) +{ + QWindowSystemInterfacePrivate::synchronousWindowSystemEvents = enable; +} + +int QWindowSystemInterface::windowSystemEventsQueued() +{ + return QWindowSystemInterfacePrivate::windowSystemEventsQueued(); +} + +// --------------------- QtTestLib support --------------------- + // The following functions are used by testlib, and need to be synchronous to avoid // race conditions with plugins delivering native events from secondary threads. -Q_GUI_EXPORT void qt_handleMouseEvent(QWindow *w, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods, int timestamp) +Q_GUI_EXPORT void qt_handleMouseEvent(QWindow *window, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods, int timestamp) { - const qreal factor = QHighDpiScaling::factor(w); - QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(w, timestamp, local * factor, global * factor, b, mods); + const qreal factor = QHighDpiScaling::factor(window); + QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(window, timestamp, local * factor, global * factor, b, mods); } -Q_GUI_EXPORT void qt_handleKeyEvent(QWindow *w, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1) +Q_GUI_EXPORT void qt_handleKeyEvent(QWindow *window, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1) { - QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(w, t, k, mods, text, autorep, count); + QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(window, t, k, mods, text, autorep, count); } Q_GUI_EXPORT bool qt_sendShortcutOverrideEvent(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, const QString &text = QString(), bool autorep = false, ushort count = 1) @@ -977,35 +1015,13 @@ namespace QTest } } -Q_GUI_EXPORT void qt_handleTouchEvent(QWindow *w, QTouchDevice *device, +Q_GUI_EXPORT void qt_handleTouchEvent(QWindow *window, QTouchDevice *device, const QList<QTouchEvent::TouchPoint> &points, Qt::KeyboardModifiers mods = Qt::NoModifier) { - QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(w, device, - QWindowSystemInterfacePrivate::toNativeTouchPoints(points, w), mods); -} - -QWindowSystemEventHandler::~QWindowSystemEventHandler() -{ - QWindowSystemInterfacePrivate::removeWindowSystemEventhandler(this); -} - -bool QWindowSystemEventHandler::sendEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e) -{ - QGuiApplicationPrivate::processWindowSystemEvent(e); - return true; + QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(window, device, + QWindowSystemInterfacePrivate::toNativeTouchPoints(points, window), mods); } -QWindowSystemInterfacePrivate::WheelEvent::WheelEvent(QWindow *w, ulong time, const QPointF &local, const QPointF &global, QPoint pixelD, - QPoint angleD, int qt4D, Qt::Orientation qt4O, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase, Qt::MouseEventSource src, bool inverted) - : InputEvent(w, time, Wheel, mods), pixelDelta(pixelD), angleDelta(angleD), qt4Delta(qt4D), - qt4Orientation(qt4O), localPos(local), globalPos(global), phase(phase), source(src), inverted(inverted) -{ -} - -void QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(bool v) -{ - platformSynthesizesMouse = v; -} QT_END_NAMESPACE diff --git a/src/gui/kernel/qwindowsysteminterface.h b/src/gui/kernel/qwindowsysteminterface.h index 3be3c3188c..b6b57e01f5 100644 --- a/src/gui/kernel/qwindowsysteminterface.h +++ b/src/gui/kernel/qwindowsysteminterface.h @@ -77,44 +77,44 @@ public: struct DefaultDelivery {}; template<typename Delivery = QWindowSystemInterface::DefaultDelivery> - static void handleMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, + static void handleMouseEvent(QWindow *window, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods = Qt::NoModifier, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); template<typename Delivery = QWindowSystemInterface::DefaultDelivery> - static void handleMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b, + static void handleMouseEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods = Qt::NoModifier, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); - static void handleFrameStrutMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, + static void handleFrameStrutMouseEvent(QWindow *window, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods = Qt::NoModifier, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); - static void handleFrameStrutMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b, + static void handleFrameStrutMouseEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods = Qt::NoModifier, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); - static bool handleShortcutEvent(QWindow *w, ulong timestamp, int k, Qt::KeyboardModifiers mods, quint32 nativeScanCode, + static bool handleShortcutEvent(QWindow *window, ulong timestamp, int k, Qt::KeyboardModifiers mods, quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, const QString & text = QString(), bool autorep = false, ushort count = 1); template<typename Delivery = QWindowSystemInterface::DefaultDelivery> - static bool handleKeyEvent(QWindow *w, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1); + static bool handleKeyEvent(QWindow *window, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1); template<typename Delivery = QWindowSystemInterface::DefaultDelivery> - static bool handleKeyEvent(QWindow *w, ulong timestamp, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1); + static bool handleKeyEvent(QWindow *window, ulong timestamp, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1); - static bool handleExtendedKeyEvent(QWindow *w, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, + static bool handleExtendedKeyEvent(QWindow *window, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, const QString& text = QString(), bool autorep = false, ushort count = 1, bool tryShortcutOverride = true); - static bool handleExtendedKeyEvent(QWindow *w, ulong timestamp, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, + static bool handleExtendedKeyEvent(QWindow *window, ulong timestamp, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, const QString& text = QString(), bool autorep = false, ushort count = 1, bool tryShortcutOverride = true); - static void handleWheelEvent(QWindow *w, const QPointF & local, const QPointF & global, + static void handleWheelEvent(QWindow *window, const QPointF &local, const QPointF &global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods = Qt::NoModifier, Qt::ScrollPhase phase = Qt::NoScrollPhase, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); - static void handleWheelEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, + static void handleWheelEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods = Qt::NoModifier, Qt::ScrollPhase phase = Qt::NoScrollPhase, @@ -122,8 +122,8 @@ public: bool inverted = false); // Wheel event compatibility functions. Will be removed: do not use. - static void handleWheelEvent(QWindow *w, const QPointF & local, const QPointF & global, int d, Qt::Orientation o, Qt::KeyboardModifiers mods = Qt::NoModifier); - static void handleWheelEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, int d, Qt::Orientation o, Qt::KeyboardModifiers mods = Qt::NoModifier); + static void handleWheelEvent(QWindow *window, const QPointF &local, const QPointF &global, int d, Qt::Orientation o, Qt::KeyboardModifiers mods = Qt::NoModifier); + static void handleWheelEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, int d, Qt::Orientation o, Qt::KeyboardModifiers mods = Qt::NoModifier); struct TouchPoint { TouchPoint() : id(0), uniqueId(-1), pressure(0), rotation(0), state(Qt::TouchPointStationary) { } @@ -131,9 +131,11 @@ public: qint64 uniqueId; // for TUIO: object/token ID; otherwise empty // TODO for TUIO 2.0: add registerPointerUniqueID(QPointerUniqueId) QPointF normalPosition; // touch device coordinates, (0 to 1, 0 to 1) - QRectF area; // the touched area, centered at position in screen coordinates + QRectF area; // dimensions of the elliptical contact patch, unrotated, and centered at position in screen coordinates + // width is the horizontal diameter, height is the vertical diameter qreal pressure; // 0 to 1 - qreal rotation; // 0 means pointing straight up; 0 if unknown (like QTabletEvent::rotation) + qreal rotation; // rotation applied to the elliptical contact patch + // 0 means pointing straight up; 0 if unknown (like QTabletEvent::rotation) Qt::TouchPointState state; //Qt::TouchPoint{Pressed|Moved|Stationary|Released} QVector2D velocity; // in screen coordinate system, pixels / seconds QTouchEvent::TouchPoint::InfoFlags flags; @@ -145,43 +147,43 @@ public: static bool isTouchDeviceRegistered(const QTouchDevice *device); template<typename Delivery = QWindowSystemInterface::DefaultDelivery> - static void handleTouchEvent(QWindow *w, QTouchDevice *device, + static void handleTouchEvent(QWindow *window, QTouchDevice *device, const QList<struct TouchPoint> &points, Qt::KeyboardModifiers mods = Qt::NoModifier); template<typename Delivery = QWindowSystemInterface::DefaultDelivery> - static void handleTouchEvent(QWindow *w, ulong timestamp, QTouchDevice *device, + static void handleTouchEvent(QWindow *window, ulong timestamp, QTouchDevice *device, const QList<struct TouchPoint> &points, Qt::KeyboardModifiers mods = Qt::NoModifier); template<typename Delivery = QWindowSystemInterface::DefaultDelivery> - static void handleTouchCancelEvent(QWindow *w, QTouchDevice *device, Qt::KeyboardModifiers mods = Qt::NoModifier); + static void handleTouchCancelEvent(QWindow *window, QTouchDevice *device, Qt::KeyboardModifiers mods = Qt::NoModifier); template<typename Delivery = QWindowSystemInterface::DefaultDelivery> - static void handleTouchCancelEvent(QWindow *w, ulong timestamp, QTouchDevice *device, Qt::KeyboardModifiers mods = Qt::NoModifier); + static void handleTouchCancelEvent(QWindow *window, ulong timestamp, QTouchDevice *device, Qt::KeyboardModifiers mods = Qt::NoModifier); // rect is relative to parent template<typename Delivery = QWindowSystemInterface::DefaultDelivery> - static void handleGeometryChange(QWindow *w, const QRect &newRect, const QRect &oldRect = QRect()); + static void handleGeometryChange(QWindow *window, const QRect &newRect, const QRect &oldRect = QRect()); // region is in local coordinates, do not confuse with geometry which is parent-relative template<typename Delivery = QWindowSystemInterface::DefaultDelivery> - static void handleExposeEvent(QWindow *tlw, const QRegion ®ion); + static void handleExposeEvent(QWindow *window, const QRegion ®ion); - static void handleCloseEvent(QWindow *w, bool *accepted = Q_NULLPTR); + static void handleCloseEvent(QWindow *window, bool *accepted = Q_NULLPTR); template<typename Delivery = QWindowSystemInterface::DefaultDelivery> - static void handleEnterEvent(QWindow *w, const QPointF &local = QPointF(), const QPointF& global = QPointF()); + static void handleEnterEvent(QWindow *window, const QPointF &local = QPointF(), const QPointF& global = QPointF()); template<typename Delivery = QWindowSystemInterface::DefaultDelivery> - static void handleLeaveEvent(QWindow *w); + static void handleLeaveEvent(QWindow *window); static void handleEnterLeaveEvent(QWindow *enter, QWindow *leave, const QPointF &local = QPointF(), const QPointF& global = QPointF()); template<typename Delivery = QWindowSystemInterface::DefaultDelivery> - static void handleWindowActivated(QWindow *w, Qt::FocusReason r = Qt::OtherFocusReason); + static void handleWindowActivated(QWindow *window, Qt::FocusReason r = Qt::OtherFocusReason); - static void handleWindowStateChanged(QWindow *w, Qt::WindowState newState); - static void handleWindowScreenChanged(QWindow *w, QScreen *newScreen); + static void handleWindowStateChanged(QWindow *window, Qt::WindowState newState); + static void handleWindowScreenChanged(QWindow *window, QScreen *newScreen); static void handleApplicationStateChanged(Qt::ApplicationState newState, bool forcePropagate = false); #ifndef QT_NO_DRAGANDDROP // Drag and drop. These events are sent immediately. - static QPlatformDragQtResponse handleDrag(QWindow *w, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions); - static QPlatformDropQtResponse handleDrop(QWindow *w, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions); + static QPlatformDragQtResponse handleDrag(QWindow *window, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions); + static QPlatformDropQtResponse handleDrop(QWindow *window, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions); #endif static bool handleNativeEvent(QWindow *window, const QByteArray &eventType, void *message, long *result); @@ -192,24 +194,24 @@ public: static void handleScreenLogicalDotsPerInchChange(QScreen *screen, qreal newDpiX, qreal newDpiY); static void handleScreenRefreshRateChange(QScreen *screen, qreal newRefreshRate); - static void handleThemeChange(QWindow *tlw); + static void handleThemeChange(QWindow *window); static void handleFileOpenEvent(const QString& fileName); static void handleFileOpenEvent(const QUrl &url); - static void handleTabletEvent(QWindow *w, ulong timestamp, const QPointF &local, const QPointF &global, + static void handleTabletEvent(QWindow *window, ulong timestamp, const QPointF &local, const QPointF &global, int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, qint64 uid, Qt::KeyboardModifiers modifiers = Qt::NoModifier); - static void handleTabletEvent(QWindow *w, const QPointF &local, const QPointF &global, + static void handleTabletEvent(QWindow *window, const QPointF &local, const QPointF &global, int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, qint64 uid, Qt::KeyboardModifiers modifiers = Qt::NoModifier); - static void handleTabletEvent(QWindow *w, ulong timestamp, bool down, const QPointF &local, const QPointF &global, + static void handleTabletEvent(QWindow *window, ulong timestamp, bool down, const QPointF &local, const QPointF &global, int device, int pointerType, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, qint64 uid, Qt::KeyboardModifiers modifiers = Qt::NoModifier); // ### remove in Qt 6 - static void handleTabletEvent(QWindow *w, bool down, const QPointF &local, const QPointF &global, + static void handleTabletEvent(QWindow *window, bool down, const QPointF &local, const QPointF &global, int device, int pointerType, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, qint64 uid, Qt::KeyboardModifiers modifiers = Qt::NoModifier); // ### remove in Qt 6 @@ -227,9 +229,9 @@ public: ulong sequenceId, quint64 value, QPointF &local, QPointF &global); #endif // QT_NO_GESTURES - static void handlePlatformPanelEvent(QWindow *w); + static void handlePlatformPanelEvent(QWindow *window); #ifndef QT_NO_CONTEXTMENU - static void handleContextMenuEvent(QWindow *w, bool mouseTriggered, + static void handleContextMenuEvent(QWindow *window, bool mouseTriggered, const QPoint &pos, const QPoint &globalPos, Qt::KeyboardModifiers modifiers); #endif diff --git a/src/gui/kernel/qwindowsysteminterface_p.h b/src/gui/kernel/qwindowsysteminterface_p.h index 7c9b1f2852..1db60e8cb8 100644 --- a/src/gui/kernel/qwindowsysteminterface_p.h +++ b/src/gui/kernel/qwindowsysteminterface_p.h @@ -131,10 +131,10 @@ public: class GeometryChangeEvent : public WindowSystemEvent { public: - GeometryChangeEvent(QWindow *tlw, const QRect &newGeometry, const QRect &oldGeometry) - : WindowSystemEvent(GeometryChange), tlw(tlw), newGeometry(newGeometry), oldGeometry(oldGeometry) + GeometryChangeEvent(QWindow *window, const QRect &newGeometry, const QRect &oldGeometry) + : WindowSystemEvent(GeometryChange), window(window), newGeometry(newGeometry), oldGeometry(oldGeometry) { } - QPointer<QWindow> tlw; + QPointer<QWindow> window; QRect newGeometry; QRect oldGeometry; }; @@ -226,11 +226,11 @@ public: class MouseEvent : public InputEvent { public: - MouseEvent(QWindow * w, ulong time, const QPointF & local, const QPointF & global, + MouseEvent(QWindow * w, ulong time, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods, Qt::MouseEventSource src = Qt::MouseEventNotSynthesized) : InputEvent(w, time, Mouse, mods), localPos(local), globalPos(global), buttons(b), source(src) { } - MouseEvent(QWindow * w, ulong time, EventType t, const QPointF & local, const QPointF & global, + MouseEvent(QWindow * w, ulong time, EventType t, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods, Qt::MouseEventSource src = Qt::MouseEventNotSynthesized) : InputEvent(w, time, t, mods), localPos(local), globalPos(global), buttons(b), source(src) { } @@ -242,7 +242,7 @@ public: class WheelEvent : public InputEvent { public: - WheelEvent(QWindow *w, ulong time, const QPointF & local, const QPointF & global, QPoint pixelD, QPoint angleD, int qt4D, Qt::Orientation qt4O, + WheelEvent(QWindow *w, ulong time, const QPointF &local, const QPointF &global, QPoint pixelD, QPoint angleD, int qt4D, Qt::Orientation qt4O, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase = Qt::NoScrollPhase, Qt::MouseEventSource src = Qt::MouseEventNotSynthesized, bool inverted = false); QPoint pixelDelta; QPoint angleDelta; @@ -494,10 +494,6 @@ public: template<typename Delivery = QWindowSystemInterface::DefaultDelivery> static bool handleWindowSystemEvent(WindowSystemEvent *ev); -private: - static void postWindowSystemEvent(WindowSystemEvent *ev); - static bool processWindowSystemEvent(WindowSystemEvent *ev); - public: static QElapsedTimer eventTime; static bool synchronousWindowSystemEvents; @@ -508,7 +504,7 @@ public: static QList<QTouchEvent::TouchPoint> fromNativeTouchPoints(const QList<QWindowSystemInterface::TouchPoint> &points, - const QWindow *window, QEvent::Type *type = Q_NULLPTR); + const QWindow *window, quint8 deviceId, QEvent::Type *type = Q_NULLPTR); static QList<QWindowSystemInterface::TouchPoint> toNativeTouchPoints(const QList<QTouchEvent::TouchPoint>& pointList, const QWindow *window); diff --git a/src/gui/opengl/opengl.pri b/src/gui/opengl/opengl.pri index 1a1022b3a7..4c778b184e 100644 --- a/src/gui/opengl/opengl.pri +++ b/src/gui/opengl/opengl.pri @@ -33,7 +33,8 @@ qtConfig(opengl) { opengl/qopengltexture_p.h \ opengl/qopengltexturehelper_p.h \ opengl/qopenglpixeltransferoptions.h \ - opengl/qopenglextrafunctions.h + opengl/qopenglextrafunctions.h \ + opengl/qopenglprogrambinarycache_p.h SOURCES += opengl/qopengl.cpp \ opengl/qopenglfunctions.cpp \ @@ -55,7 +56,8 @@ qtConfig(opengl) { opengl/qopengltextureblitter.cpp \ opengl/qopengltexture.cpp \ opengl/qopengltexturehelper.cpp \ - opengl/qopenglpixeltransferoptions.cpp + opengl/qopenglpixeltransferoptions.cpp \ + opengl/qopenglprogrambinarycache.cpp !qtConfig(opengles2) { HEADERS += opengl/qopenglfunctions_1_0.h \ diff --git a/src/gui/opengl/qopengl.cpp b/src/gui/opengl/qopengl.cpp index eb08492254..e61473cb7b 100644 --- a/src/gui/opengl/qopengl.cpp +++ b/src/gui/opengl/qopengl.cpp @@ -42,6 +42,7 @@ #include "qopenglcontext.h" #include "qopenglfunctions.h" +#include "qoperatingsystemversion.h" #include "qoffscreensurface.h" #include <QtCore/QDebug> @@ -221,29 +222,25 @@ struct OsTypeTerm static QString hostOsRelease() { QString ver; #ifdef Q_OS_WIN - switch (QSysInfo::windowsVersion()) { - case QSysInfo::WV_XP: - case QSysInfo::WV_2003: - ver = QStringLiteral("xp"); - break; - case QSysInfo::WV_VISTA: - ver = QStringLiteral("vista"); - break; - case QSysInfo::WV_WINDOWS7: + const auto osver = QOperatingSystemVersion::current(); +#define Q_WINVER(major, minor) (major << 8 | minor) + switch (Q_WINVER(osver.majorVersion(), osver.minorVersion())) { + case Q_WINVER(6, 1): ver = QStringLiteral("7"); break; - case QSysInfo::WV_WINDOWS8: + case Q_WINVER(6, 2): ver = QStringLiteral("8"); break; - case QSysInfo::WV_WINDOWS8_1: + case Q_WINVER(6, 3): ver = QStringLiteral("8.1"); break; - case QSysInfo::WV_WINDOWS10: + case Q_WINVER(10, 0): ver = QStringLiteral("10"); break; default: break; } +#undef Q_WINVER #endif return ver; } diff --git a/src/gui/opengl/qopengldebug.h b/src/gui/opengl/qopengldebug.h index 74b2731f7e..6b10c36291 100644 --- a/src/gui/opengl/qopengldebug.h +++ b/src/gui/opengl/qopengldebug.h @@ -52,6 +52,11 @@ #include <QtCore/qdebug.h> #include <QtGui/qopenglcontext.h> +#if defined(Q_CLANG_QDOC) +#undef GLuint +typedef unsigned int GLuint; +#endif + QT_BEGIN_NAMESPACE class QOpenGLDebugLogger; diff --git a/src/gui/opengl/qopenglengineshadermanager.cpp b/src/gui/opengl/qopenglengineshadermanager.cpp index 1d3e47f93b..c7e457b364 100644 --- a/src/gui/opengl/qopenglengineshadermanager.cpp +++ b/src/gui/opengl/qopenglengineshadermanager.cpp @@ -208,8 +208,6 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context) snippetsPopulated = true; } - QOpenGLShader* fragShader; - QOpenGLShader* vertexShader; QByteArray vertexSource; QByteArray fragSource; @@ -227,19 +225,11 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context) bool inCache = simpleShaderCache.load(simpleShaderProg, context); if (!inCache) { - vertexShader = new QOpenGLShader(QOpenGLShader::Vertex); - shaders.append(vertexShader); - if (!vertexShader->compileSourceCode(vertexSource)) + if (!simpleShaderProg->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource)) qWarning("Vertex shader for simpleShaderProg (MainVertexShader & PositionOnlyVertexShader) failed to compile"); - - fragShader = new QOpenGLShader(QOpenGLShader::Fragment); - shaders.append(fragShader); - if (!fragShader->compileSourceCode(fragSource)) + if (!simpleShaderProg->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragSource)) qWarning("Fragment shader for simpleShaderProg (MainFragmentShader & ShockingPinkSrcFragmentShader) failed to compile"); - simpleShaderProg->addShader(vertexShader); - simpleShaderProg->addShader(fragShader); - simpleShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); simpleShaderProg->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR); simpleShaderProg->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR); @@ -271,19 +261,11 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context) inCache = blitShaderCache.load(blitShaderProg, context); if (!inCache) { - vertexShader = new QOpenGLShader(QOpenGLShader::Vertex); - shaders.append(vertexShader); - if (!vertexShader->compileSourceCode(vertexSource)) + if (!blitShaderProg->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource)) qWarning("Vertex shader for blitShaderProg (MainWithTexCoordsVertexShader & UntransformedPositionVertexShader) failed to compile"); - - fragShader = new QOpenGLShader(QOpenGLShader::Fragment); - shaders.append(fragShader); - if (!fragShader->compileSourceCode(fragSource)) + if (!blitShaderProg->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragSource)) qWarning("Fragment shader for blitShaderProg (MainFragmentShader & ImageSrcFragmentShader) failed to compile"); - blitShaderProg->addShader(vertexShader); - blitShaderProg->addShader(fragShader); - blitShaderProg->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); blitShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); } @@ -306,9 +288,6 @@ QOpenGLEngineSharedShaders::~QOpenGLEngineSharedShaders() #ifdef QT_GL_SHARED_SHADER_DEBUG qDebug(" -> ~QOpenGLEngineSharedShaders() %p for thread %p.", this, QThread::currentThread()); #endif - qDeleteAll(shaders); - shaders.clear(); - qDeleteAll(cachedPrograms); cachedPrograms.clear(); @@ -370,50 +349,37 @@ QOpenGLEngineShaderProg *QOpenGLEngineSharedShaders::findProgramInCache(const QO bool inCache = shaderCache.load(shaderProgram.data(), QOpenGLContext::currentContext()); if (!inCache) { - - QScopedPointer<QOpenGLShader> fragShader(new QOpenGLShader(QOpenGLShader::Fragment)); - QByteArray description; + if (!shaderProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource)) { + QByteArray description; #if defined(QT_DEBUG) - // Name the shader for easier debugging - description.append("Fragment shader: main="); - description.append(snippetNameStr(prog.mainFragShader)); - description.append(", srcPixel="); - description.append(snippetNameStr(prog.srcPixelFragShader)); - if (prog.compositionFragShader) { - description.append(", composition="); - description.append(snippetNameStr(prog.compositionFragShader)); - } - if (prog.maskFragShader) { - description.append(", mask="); - description.append(snippetNameStr(prog.maskFragShader)); - } - fragShader->setObjectName(QString::fromLatin1(description)); + description.append("Vertex shader: main="); + description.append(snippetNameStr(prog.mainVertexShader)); + description.append(", position="); + description.append(snippetNameStr(prog.positionVertexShader)); #endif - if (!fragShader->compileSourceCode(fragSource)) { qWarning("Warning: \"%s\" failed to compile!", description.constData()); break; } - - QScopedPointer<QOpenGLShader> vertexShader(new QOpenGLShader(QOpenGLShader::Vertex)); + if (!shaderProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragSource)) { + QByteArray description; #if defined(QT_DEBUG) - // Name the shader for easier debugging - description.clear(); - description.append("Vertex shader: main="); - description.append(snippetNameStr(prog.mainVertexShader)); - description.append(", position="); - description.append(snippetNameStr(prog.positionVertexShader)); - vertexShader->setObjectName(QString::fromLatin1(description)); + description.append("Fragment shader: main="); + description.append(snippetNameStr(prog.mainFragShader)); + description.append(", srcPixel="); + description.append(snippetNameStr(prog.srcPixelFragShader)); + if (prog.compositionFragShader) { + description.append(", composition="); + description.append(snippetNameStr(prog.compositionFragShader)); + } + if (prog.maskFragShader) { + description.append(", mask="); + description.append(snippetNameStr(prog.maskFragShader)); + } #endif - if (!vertexShader->compileSourceCode(vertexSource)) { qWarning("Warning: \"%s\" failed to compile!", description.constData()); break; } - shaders.append(vertexShader.data()); - shaders.append(fragShader.data()); - shaderProgram->addShader(vertexShader.take()); - shaderProgram->addShader(fragShader.take()); - // We have to bind the vertex attribute names before the program is linked: shaderProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); if (prog.useTextureCoords) @@ -436,18 +402,9 @@ QOpenGLEngineShaderProg *QOpenGLEngineSharedShaders::findProgramInCache(const QO shaderCache.store(newProg->program, QOpenGLContext::currentContext()); } else { QString error; - error = QLatin1String("Shader program failed to link,"); -#if defined(QT_DEBUG) - QLatin1String br("\n"); - error += QLatin1String("\n Shaders Used:\n"); - for (int i = 0; i < newProg->program->shaders().count(); ++i) { - QOpenGLShader *shader = newProg->program->shaders().at(i); - error += QLatin1String(" ") + shader->objectName() + QLatin1String(": \n") - + QLatin1String(shader->sourceCode()) + br; - } -#endif - error += QLatin1String(" Error Log:\n") - + QLatin1String(" ") + newProg->program->log(); + error = QLatin1String("Shader program failed to link") + + QLatin1String(" Error Log:\n") + + QLatin1String(" ") + newProg->program->log(); qWarning() << error; break; } diff --git a/src/gui/opengl/qopenglengineshadermanager_p.h b/src/gui/opengl/qopenglengineshadermanager_p.h index 23b2ccf486..377501457d 100644 --- a/src/gui/opengl/qopenglengineshadermanager_p.h +++ b/src/gui/opengl/qopenglengineshadermanager_p.h @@ -366,7 +366,6 @@ private: QOpenGLShaderProgram *blitShaderProg; QOpenGLShaderProgram *simpleShaderProg; QList<QOpenGLEngineShaderProg*> cachedPrograms; - QList<QOpenGLShader *> shaders; static const char* qShaderSnippets[TotalSnippetCount]; }; diff --git a/src/gui/opengl/qopenglfunctions.h b/src/gui/opengl/qopenglfunctions.h index aad48571b3..f1a717f659 100644 --- a/src/gui/opengl/qopenglfunctions.h +++ b/src/gui/opengl/qopenglfunctions.h @@ -591,499 +591,319 @@ struct QOpenGLFunctionsPrivate inline void QOpenGLFunctions::glBindTexture(GLenum target, GLuint texture) { -#ifdef QT_OPENGL_ES_2 - ::glBindTexture(target, texture); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.BindTexture(target, texture); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glBlendFunc(GLenum sfactor, GLenum dfactor) { -#ifdef QT_OPENGL_ES_2 - ::glBlendFunc(sfactor, dfactor); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.BlendFunc(sfactor, dfactor); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glClear(GLbitfield mask) { -#ifdef QT_OPENGL_ES_2 - ::glClear(mask); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Clear(mask); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) { -#ifdef QT_OPENGL_ES_2 - ::glClearColor(red, green, blue, alpha); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.ClearColor(red, green, blue, alpha); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glClearStencil(GLint s) { -#ifdef QT_OPENGL_ES_2 - ::glClearStencil(s); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.ClearStencil(s); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) { -#ifdef QT_OPENGL_ES_2 - ::glColorMask(red, green, blue, alpha); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.ColorMask(red, green, blue, alpha); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) { -#ifdef QT_OPENGL_ES_2 - ::glCopyTexImage2D(target, level, internalformat, x, y, width,height, border); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.CopyTexImage2D(target, level, internalformat, x, y, width,height, border); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) { -#ifdef QT_OPENGL_ES_2 - ::glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glCullFace(GLenum mode) { -#ifdef QT_OPENGL_ES_2 - ::glCullFace(mode); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.CullFace(mode); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glDeleteTextures(GLsizei n, const GLuint* textures) { -#ifdef QT_OPENGL_ES_2 - ::glDeleteTextures(n, textures); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.DeleteTextures(n, textures); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glDepthFunc(GLenum func) { -#ifdef QT_OPENGL_ES_2 - ::glDepthFunc(func); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.DepthFunc(func); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glDepthMask(GLboolean flag) { -#ifdef QT_OPENGL_ES_2 - ::glDepthMask(flag); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.DepthMask(flag); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glDisable(GLenum cap) { -#ifdef QT_OPENGL_ES_2 - ::glDisable(cap); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Disable(cap); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glDrawArrays(GLenum mode, GLint first, GLsizei count) { -#ifdef QT_OPENGL_ES_2 - ::glDrawArrays(mode, first, count); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.DrawArrays(mode, first, count); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) { -#ifdef QT_OPENGL_ES_2 - ::glDrawElements(mode, count, type, indices); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.DrawElements(mode, count, type, indices); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glEnable(GLenum cap) { -#ifdef QT_OPENGL_ES_2 - ::glEnable(cap); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Enable(cap); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glFinish() { -#ifdef QT_OPENGL_ES_2 - ::glFinish(); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Finish(); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glFlush() { -#ifdef QT_OPENGL_ES_2 - ::glFlush(); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Flush(); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glFrontFace(GLenum mode) { -#ifdef QT_OPENGL_ES_2 - ::glFrontFace(mode); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.FrontFace(mode); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGenTextures(GLsizei n, GLuint* textures) { -#ifdef QT_OPENGL_ES_2 - ::glGenTextures(n, textures); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GenTextures(n, textures); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetBooleanv(GLenum pname, GLboolean* params) { -#ifdef QT_OPENGL_ES_2 - ::glGetBooleanv(pname, params); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetBooleanv(pname, params); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline GLenum QOpenGLFunctions::glGetError() { -#ifdef QT_OPENGL_ES_2 - GLenum result = ::glGetError(); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); GLenum result = d_ptr->f.GetError(); -#endif return result; } inline void QOpenGLFunctions::glGetFloatv(GLenum pname, GLfloat* params) { -#ifdef QT_OPENGL_ES_2 - ::glGetFloatv(pname, params); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetFloatv(pname, params); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetIntegerv(GLenum pname, GLint* params) { -#ifdef QT_OPENGL_ES_2 - ::glGetIntegerv(pname, params); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetIntegerv(pname, params); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline const GLubyte *QOpenGLFunctions::glGetString(GLenum name) { -#ifdef QT_OPENGL_ES_2 - const GLubyte *result = ::glGetString(name); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); const GLubyte *result = d_ptr->f.GetString(name); -#endif Q_OPENGL_FUNCTIONS_DEBUG return result; } inline void QOpenGLFunctions::glGetTexParameterfv(GLenum target, GLenum pname, GLfloat* params) { -#ifdef QT_OPENGL_ES_2 - ::glGetTexParameterfv(target, pname, params); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetTexParameterfv(target, pname, params); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetTexParameteriv(GLenum target, GLenum pname, GLint* params) { -#ifdef QT_OPENGL_ES_2 - ::glGetTexParameteriv(target, pname, params); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetTexParameteriv(target, pname, params); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glHint(GLenum target, GLenum mode) { -#ifdef QT_OPENGL_ES_2 - ::glHint(target, mode); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Hint(target, mode); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline GLboolean QOpenGLFunctions::glIsEnabled(GLenum cap) { -#ifdef QT_OPENGL_ES_2 - GLboolean result = ::glIsEnabled(cap); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); GLboolean result = d_ptr->f.IsEnabled(cap); -#endif Q_OPENGL_FUNCTIONS_DEBUG return result; } inline GLboolean QOpenGLFunctions::glIsTexture(GLuint texture) { -#ifdef QT_OPENGL_ES_2 - GLboolean result = ::glIsTexture(texture); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); GLboolean result = d_ptr->f.IsTexture(texture); -#endif Q_OPENGL_FUNCTIONS_DEBUG return result; } inline void QOpenGLFunctions::glLineWidth(GLfloat width) { -#ifdef QT_OPENGL_ES_2 - ::glLineWidth(width); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.LineWidth(width); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glPixelStorei(GLenum pname, GLint param) { -#ifdef QT_OPENGL_ES_2 - ::glPixelStorei(pname, param); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.PixelStorei(pname, param); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glPolygonOffset(GLfloat factor, GLfloat units) { -#ifdef QT_OPENGL_ES_2 - ::glPolygonOffset(factor, units); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.PolygonOffset(factor, units); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels) { -#ifdef QT_OPENGL_ES_2 - ::glReadPixels(x, y, width, height, format, type, pixels); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.ReadPixels(x, y, width, height, format, type, pixels); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glScissor(GLint x, GLint y, GLsizei width, GLsizei height) { -#ifdef QT_OPENGL_ES_2 - ::glScissor(x, y, width, height); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Scissor(x, y, width, height); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glStencilFunc(GLenum func, GLint ref, GLuint mask) { -#ifdef QT_OPENGL_ES_2 - ::glStencilFunc(func, ref, mask); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.StencilFunc(func, ref, mask); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glStencilMask(GLuint mask) { -#ifdef QT_OPENGL_ES_2 - ::glStencilMask(mask); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.StencilMask(mask); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glStencilOp(GLenum fail, GLenum zfail, GLenum zpass) { -#ifdef QT_OPENGL_ES_2 - ::glStencilOp(fail, zfail, zpass); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.StencilOp(fail, zfail, zpass); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) { -#ifdef QT_OPENGL_ES_2 - ::glTexImage2D(target, level, internalformat, width,height, border, format, type, pixels); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.TexImage2D(target, level, internalformat, width,height, border, format, type, pixels); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glTexParameterf(GLenum target, GLenum pname, GLfloat param) { -#ifdef QT_OPENGL_ES_2 - ::glTexParameterf(target, pname, param); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.TexParameterf(target, pname, param); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glTexParameterfv(GLenum target, GLenum pname, const GLfloat* params) { -#ifdef QT_OPENGL_ES_2 - ::glTexParameterfv(target, pname, params); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.TexParameterfv(target, pname, params); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glTexParameteri(GLenum target, GLenum pname, GLint param) { -#ifdef QT_OPENGL_ES_2 - ::glTexParameteri(target, pname, param); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.TexParameteri(target, pname, param); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glTexParameteriv(GLenum target, GLenum pname, const GLint* params) { -#ifdef QT_OPENGL_ES_2 - ::glTexParameteriv(target, pname, params); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.TexParameteriv(target, pname, params); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels) { -#ifdef QT_OPENGL_ES_2 - ::glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.TexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glViewport(GLint x, GLint y, GLsizei width, GLsizei height) { -#ifdef QT_OPENGL_ES_2 - ::glViewport(x, y, width, height); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Viewport(x, y, width, height); -#endif Q_OPENGL_FUNCTIONS_DEBUG } @@ -1091,45 +911,29 @@ inline void QOpenGLFunctions::glViewport(GLint x, GLint y, GLsizei width, GLsize inline void QOpenGLFunctions::glActiveTexture(GLenum texture) { -#ifdef QT_OPENGL_ES_2 - ::glActiveTexture(texture); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.ActiveTexture(texture); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glAttachShader(GLuint program, GLuint shader) { -#ifdef QT_OPENGL_ES_2 - ::glAttachShader(program, shader); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.AttachShader(program, shader); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glBindAttribLocation(GLuint program, GLuint index, const char* name) { -#ifdef QT_OPENGL_ES_2 - ::glBindAttribLocation(program, index, name); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.BindAttribLocation(program, index, name); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glBindBuffer(GLenum target, GLuint buffer) { -#ifdef QT_OPENGL_ES_2 - ::glBindBuffer(target, buffer); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.BindBuffer(target, buffer); -#endif Q_OPENGL_FUNCTIONS_DEBUG } @@ -1137,1034 +941,662 @@ inline void QOpenGLFunctions::glBindFramebuffer(GLenum target, GLuint framebuffe { if (framebuffer == 0) framebuffer = QOpenGLContext::currentContext()->defaultFramebufferObject(); -#ifdef QT_OPENGL_ES_2 - ::glBindFramebuffer(target, framebuffer); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.BindFramebuffer(target, framebuffer); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glBindRenderbuffer(GLenum target, GLuint renderbuffer) { -#ifdef QT_OPENGL_ES_2 - ::glBindRenderbuffer(target, renderbuffer); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.BindRenderbuffer(target, renderbuffer); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) { -#ifdef QT_OPENGL_ES_2 - ::glBlendColor(red, green, blue, alpha); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.BlendColor(red, green, blue, alpha); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glBlendEquation(GLenum mode) { -#ifdef QT_OPENGL_ES_2 - ::glBlendEquation(mode); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.BlendEquation(mode); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) { -#ifdef QT_OPENGL_ES_2 - ::glBlendEquationSeparate(modeRGB, modeAlpha); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.BlendEquationSeparate(modeRGB, modeAlpha); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) { -#ifdef QT_OPENGL_ES_2 - ::glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.BlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glBufferData(GLenum target, qopengl_GLsizeiptr size, const void* data, GLenum usage) { -#ifdef QT_OPENGL_ES_2 - ::glBufferData(target, size, data, usage); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.BufferData(target, size, data, usage); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glBufferSubData(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr size, const void* data) { -#ifdef QT_OPENGL_ES_2 - ::glBufferSubData(target, offset, size, data); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.BufferSubData(target, offset, size, data); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline GLenum QOpenGLFunctions::glCheckFramebufferStatus(GLenum target) { -#ifdef QT_OPENGL_ES_2 - GLenum result = ::glCheckFramebufferStatus(target); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); GLenum result = d_ptr->f.CheckFramebufferStatus(target); -#endif Q_OPENGL_FUNCTIONS_DEBUG return result; } inline void QOpenGLFunctions::glClearDepthf(GLclampf depth) { -#ifndef QT_OPENGL_ES Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.ClearDepthf(depth); -#else - ::glClearDepthf(depth); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glCompileShader(GLuint shader) { -#ifdef QT_OPENGL_ES_2 - ::glCompileShader(shader); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.CompileShader(shader); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data) { -#ifdef QT_OPENGL_ES_2 - ::glCompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.CompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) { -#ifdef QT_OPENGL_ES_2 - ::glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.CompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline GLuint QOpenGLFunctions::glCreateProgram() { -#ifdef QT_OPENGL_ES_2 - GLuint result = ::glCreateProgram(); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); GLuint result = d_ptr->f.CreateProgram(); -#endif Q_OPENGL_FUNCTIONS_DEBUG return result; } inline GLuint QOpenGLFunctions::glCreateShader(GLenum type) { -#ifdef QT_OPENGL_ES_2 - GLuint result = ::glCreateShader(type); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); GLuint result = d_ptr->f.CreateShader(type); -#endif Q_OPENGL_FUNCTIONS_DEBUG return result; } inline void QOpenGLFunctions::glDeleteBuffers(GLsizei n, const GLuint* buffers) { -#ifdef QT_OPENGL_ES_2 - ::glDeleteBuffers(n, buffers); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.DeleteBuffers(n, buffers); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers) { -#ifdef QT_OPENGL_ES_2 - ::glDeleteFramebuffers(n, framebuffers); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.DeleteFramebuffers(n, framebuffers); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glDeleteProgram(GLuint program) { -#ifdef QT_OPENGL_ES_2 - ::glDeleteProgram(program); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.DeleteProgram(program); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) { -#ifdef QT_OPENGL_ES_2 - ::glDeleteRenderbuffers(n, renderbuffers); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.DeleteRenderbuffers(n, renderbuffers); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glDeleteShader(GLuint shader) { -#ifdef QT_OPENGL_ES_2 - ::glDeleteShader(shader); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.DeleteShader(shader); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glDepthRangef(GLclampf zNear, GLclampf zFar) { -#ifndef QT_OPENGL_ES Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.DepthRangef(zNear, zFar); -#else - ::glDepthRangef(zNear, zFar); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glDetachShader(GLuint program, GLuint shader) { -#ifdef QT_OPENGL_ES_2 - ::glDetachShader(program, shader); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.DetachShader(program, shader); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glDisableVertexAttribArray(GLuint index) { -#ifdef QT_OPENGL_ES_2 - ::glDisableVertexAttribArray(index); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.DisableVertexAttribArray(index); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glEnableVertexAttribArray(GLuint index) { -#ifdef QT_OPENGL_ES_2 - ::glEnableVertexAttribArray(index); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.EnableVertexAttribArray(index); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) { -#ifdef QT_OPENGL_ES_2 - ::glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.FramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) { -#ifdef QT_OPENGL_ES_2 - ::glFramebufferTexture2D(target, attachment, textarget, texture, level); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.FramebufferTexture2D(target, attachment, textarget, texture, level); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGenBuffers(GLsizei n, GLuint* buffers) { -#ifdef QT_OPENGL_ES_2 - ::glGenBuffers(n, buffers); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GenBuffers(n, buffers); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGenerateMipmap(GLenum target) { -#ifdef QT_OPENGL_ES_2 - ::glGenerateMipmap(target); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GenerateMipmap(target); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGenFramebuffers(GLsizei n, GLuint* framebuffers) { -#ifdef QT_OPENGL_ES_2 - ::glGenFramebuffers(n, framebuffers); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GenFramebuffers(n, framebuffers); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGenRenderbuffers(GLsizei n, GLuint* renderbuffers) { -#ifdef QT_OPENGL_ES_2 - ::glGenRenderbuffers(n, renderbuffers); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GenRenderbuffers(n, renderbuffers); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) { -#ifdef QT_OPENGL_ES_2 - ::glGetActiveAttrib(program, index, bufsize, length, size, type, name); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetActiveAttrib(program, index, bufsize, length, size, type, name); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) { -#ifdef QT_OPENGL_ES_2 - ::glGetActiveUniform(program, index, bufsize, length, size, type, name); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetActiveUniform(program, index, bufsize, length, size, type, name); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) { -#ifdef QT_OPENGL_ES_2 - ::glGetAttachedShaders(program, maxcount, count, shaders); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetAttachedShaders(program, maxcount, count, shaders); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline GLint QOpenGLFunctions::glGetAttribLocation(GLuint program, const char* name) { -#ifdef QT_OPENGL_ES_2 - GLint result = ::glGetAttribLocation(program, name); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); GLint result = d_ptr->f.GetAttribLocation(program, name); -#endif Q_OPENGL_FUNCTIONS_DEBUG return result; } inline void QOpenGLFunctions::glGetBufferParameteriv(GLenum target, GLenum pname, GLint* params) { -#ifdef QT_OPENGL_ES_2 - ::glGetBufferParameteriv(target, pname, params); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetBufferParameteriv(target, pname, params); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params) { -#ifdef QT_OPENGL_ES_2 - ::glGetFramebufferAttachmentParameteriv(target, attachment, pname, params); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetFramebufferAttachmentParameteriv(target, attachment, pname, params); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetProgramiv(GLuint program, GLenum pname, GLint* params) { -#ifdef QT_OPENGL_ES_2 - ::glGetProgramiv(program, pname, params); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetProgramiv(program, pname, params); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog) { -#ifdef QT_OPENGL_ES_2 - ::glGetProgramInfoLog(program, bufsize, length, infolog); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetProgramInfoLog(program, bufsize, length, infolog); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params) { -#ifdef QT_OPENGL_ES_2 - ::glGetRenderbufferParameteriv(target, pname, params); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetRenderbufferParameteriv(target, pname, params); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetShaderiv(GLuint shader, GLenum pname, GLint* params) { -#ifdef QT_OPENGL_ES_2 - ::glGetShaderiv(shader, pname, params); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetShaderiv(shader, pname, params); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog) { -#ifdef QT_OPENGL_ES_2 - ::glGetShaderInfoLog(shader, bufsize, length, infolog); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetShaderInfoLog(shader, bufsize, length, infolog); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) { -#ifdef QT_OPENGL_ES_2 - ::glGetShaderPrecisionFormat(shadertype, precisiontype, range, precision); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetShaderPrecisionFormat(shadertype, precisiontype, range, precision); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, char* source) { -#ifdef QT_OPENGL_ES_2 - ::glGetShaderSource(shader, bufsize, length, source); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetShaderSource(shader, bufsize, length, source); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetUniformfv(GLuint program, GLint location, GLfloat* params) { -#ifdef QT_OPENGL_ES_2 - ::glGetUniformfv(program, location, params); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetUniformfv(program, location, params); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetUniformiv(GLuint program, GLint location, GLint* params) { -#ifdef QT_OPENGL_ES_2 - ::glGetUniformiv(program, location, params); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetUniformiv(program, location, params); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline GLint QOpenGLFunctions::glGetUniformLocation(GLuint program, const char* name) { -#ifdef QT_OPENGL_ES_2 - GLint result = ::glGetUniformLocation(program, name); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); GLint result = d_ptr->f.GetUniformLocation(program, name); -#endif Q_OPENGL_FUNCTIONS_DEBUG return result; } inline void QOpenGLFunctions::glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params) { -#ifdef QT_OPENGL_ES_2 - ::glGetVertexAttribfv(index, pname, params); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetVertexAttribfv(index, pname, params); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params) { -#ifdef QT_OPENGL_ES_2 - ::glGetVertexAttribiv(index, pname, params); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetVertexAttribiv(index, pname, params); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glGetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer) { -#ifdef QT_OPENGL_ES_2 - ::glGetVertexAttribPointerv(index, pname, pointer); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.GetVertexAttribPointerv(index, pname, pointer); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline GLboolean QOpenGLFunctions::glIsBuffer(GLuint buffer) { -#ifdef QT_OPENGL_ES_2 - GLboolean result = ::glIsBuffer(buffer); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); GLboolean result = d_ptr->f.IsBuffer(buffer); -#endif Q_OPENGL_FUNCTIONS_DEBUG return result; } inline GLboolean QOpenGLFunctions::glIsFramebuffer(GLuint framebuffer) { -#ifdef QT_OPENGL_ES_2 - GLboolean result = ::glIsFramebuffer(framebuffer); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); GLboolean result = d_ptr->f.IsFramebuffer(framebuffer); -#endif Q_OPENGL_FUNCTIONS_DEBUG return result; } inline GLboolean QOpenGLFunctions::glIsProgram(GLuint program) { -#ifdef QT_OPENGL_ES_2 - GLboolean result = ::glIsProgram(program); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); GLboolean result = d_ptr->f.IsProgram(program); -#endif Q_OPENGL_FUNCTIONS_DEBUG return result; } inline GLboolean QOpenGLFunctions::glIsRenderbuffer(GLuint renderbuffer) { -#ifdef QT_OPENGL_ES_2 - GLboolean result = ::glIsRenderbuffer(renderbuffer); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); GLboolean result = d_ptr->f.IsRenderbuffer(renderbuffer); -#endif Q_OPENGL_FUNCTIONS_DEBUG return result; } inline GLboolean QOpenGLFunctions::glIsShader(GLuint shader) { -#ifdef QT_OPENGL_ES_2 - GLboolean result = ::glIsShader(shader); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); GLboolean result = d_ptr->f.IsShader(shader); -#endif Q_OPENGL_FUNCTIONS_DEBUG return result; } inline void QOpenGLFunctions::glLinkProgram(GLuint program) { -#ifdef QT_OPENGL_ES_2 - ::glLinkProgram(program); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.LinkProgram(program); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glReleaseShaderCompiler() { -#ifdef QT_OPENGL_ES_2 - ::glReleaseShaderCompiler(); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.ReleaseShaderCompiler(); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) { -#ifdef QT_OPENGL_ES_2 - ::glRenderbufferStorage(target, internalformat, width, height); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.RenderbufferStorage(target, internalformat, width, height); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glSampleCoverage(GLclampf value, GLboolean invert) { -#ifdef QT_OPENGL_ES_2 - ::glSampleCoverage(value, invert); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.SampleCoverage(value, invert); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glShaderBinary(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length) { -#ifdef QT_OPENGL_ES_2 - ::glShaderBinary(n, shaders, binaryformat, binary, length); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.ShaderBinary(n, shaders, binaryformat, binary, length); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glShaderSource(GLuint shader, GLsizei count, const char** string, const GLint* length) { -#ifdef QT_OPENGL_ES_2 - ::glShaderSource(shader, count, string, length); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.ShaderSource(shader, count, string, length); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) { -#ifdef QT_OPENGL_ES_2 - ::glStencilFuncSeparate(face, func, ref, mask); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.StencilFuncSeparate(face, func, ref, mask); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glStencilMaskSeparate(GLenum face, GLuint mask) { -#ifdef QT_OPENGL_ES_2 - ::glStencilMaskSeparate(face, mask); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.StencilMaskSeparate(face, mask); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass) { -#ifdef QT_OPENGL_ES_2 - ::glStencilOpSeparate(face, fail, zfail, zpass); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.StencilOpSeparate(face, fail, zfail, zpass); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniform1f(GLint location, GLfloat x) { -#ifdef QT_OPENGL_ES_2 - ::glUniform1f(location, x); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Uniform1f(location, x); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniform1fv(GLint location, GLsizei count, const GLfloat* v) { -#ifdef QT_OPENGL_ES_2 - ::glUniform1fv(location, count, v); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Uniform1fv(location, count, v); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniform1i(GLint location, GLint x) { -#ifdef QT_OPENGL_ES_2 - ::glUniform1i(location, x); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Uniform1i(location, x); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniform1iv(GLint location, GLsizei count, const GLint* v) { -#ifdef QT_OPENGL_ES_2 - ::glUniform1iv(location, count, v); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Uniform1iv(location, count, v); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniform2f(GLint location, GLfloat x, GLfloat y) { -#ifdef QT_OPENGL_ES_2 - ::glUniform2f(location, x, y); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Uniform2f(location, x, y); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniform2fv(GLint location, GLsizei count, const GLfloat* v) { -#ifdef QT_OPENGL_ES_2 - ::glUniform2fv(location, count, v); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Uniform2fv(location, count, v); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniform2i(GLint location, GLint x, GLint y) { -#ifdef QT_OPENGL_ES_2 - ::glUniform2i(location, x, y); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Uniform2i(location, x, y); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniform2iv(GLint location, GLsizei count, const GLint* v) { -#ifdef QT_OPENGL_ES_2 - ::glUniform2iv(location, count, v); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Uniform2iv(location, count, v); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z) { -#ifdef QT_OPENGL_ES_2 - ::glUniform3f(location, x, y, z); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Uniform3f(location, x, y, z); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniform3fv(GLint location, GLsizei count, const GLfloat* v) { -#ifdef QT_OPENGL_ES_2 - ::glUniform3fv(location, count, v); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Uniform3fv(location, count, v); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniform3i(GLint location, GLint x, GLint y, GLint z) { -#ifdef QT_OPENGL_ES_2 - ::glUniform3i(location, x, y, z); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Uniform3i(location, x, y, z); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniform3iv(GLint location, GLsizei count, const GLint* v) { -#ifdef QT_OPENGL_ES_2 - ::glUniform3iv(location, count, v); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Uniform3iv(location, count, v); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) { -#ifdef QT_OPENGL_ES_2 - ::glUniform4f(location, x, y, z, w); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Uniform4f(location, x, y, z, w); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniform4fv(GLint location, GLsizei count, const GLfloat* v) { -#ifdef QT_OPENGL_ES_2 - ::glUniform4fv(location, count, v); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Uniform4fv(location, count, v); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w) { -#ifdef QT_OPENGL_ES_2 - ::glUniform4i(location, x, y, z, w); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Uniform4i(location, x, y, z, w); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniform4iv(GLint location, GLsizei count, const GLint* v) { -#ifdef QT_OPENGL_ES_2 - ::glUniform4iv(location, count, v); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.Uniform4iv(location, count, v); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) { -#ifdef QT_OPENGL_ES_2 - ::glUniformMatrix2fv(location, count, transpose, value); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.UniformMatrix2fv(location, count, transpose, value); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) { -#ifdef QT_OPENGL_ES_2 - ::glUniformMatrix3fv(location, count, transpose, value); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.UniformMatrix3fv(location, count, transpose, value); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) { -#ifdef QT_OPENGL_ES_2 - ::glUniformMatrix4fv(location, count, transpose, value); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.UniformMatrix4fv(location, count, transpose, value); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glUseProgram(GLuint program) { -#ifdef QT_OPENGL_ES_2 - ::glUseProgram(program); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.UseProgram(program); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glValidateProgram(GLuint program) { -#ifdef QT_OPENGL_ES_2 - ::glValidateProgram(program); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.ValidateProgram(program); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glVertexAttrib1f(GLuint indx, GLfloat x) { -#ifdef QT_OPENGL_ES_2 - ::glVertexAttrib1f(indx, x); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.VertexAttrib1f(indx, x); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glVertexAttrib1fv(GLuint indx, const GLfloat* values) { -#ifdef QT_OPENGL_ES_2 - ::glVertexAttrib1fv(indx, values); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.VertexAttrib1fv(indx, values); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) { -#ifdef QT_OPENGL_ES_2 - ::glVertexAttrib2f(indx, x, y); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.VertexAttrib2f(indx, x, y); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glVertexAttrib2fv(GLuint indx, const GLfloat* values) { -#ifdef QT_OPENGL_ES_2 - ::glVertexAttrib2fv(indx, values); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.VertexAttrib2fv(indx, values); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z) { -#ifdef QT_OPENGL_ES_2 - ::glVertexAttrib3f(indx, x, y, z); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.VertexAttrib3f(indx, x, y, z); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glVertexAttrib3fv(GLuint indx, const GLfloat* values) { -#ifdef QT_OPENGL_ES_2 - ::glVertexAttrib3fv(indx, values); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.VertexAttrib3fv(indx, values); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glVertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w) { -#ifdef QT_OPENGL_ES_2 - ::glVertexAttrib4f(indx, x, y, z, w); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.VertexAttrib4f(indx, x, y, z, w); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glVertexAttrib4fv(GLuint indx, const GLfloat* values) { -#ifdef QT_OPENGL_ES_2 - ::glVertexAttrib4fv(indx, values); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.VertexAttrib4fv(indx, values); -#endif Q_OPENGL_FUNCTIONS_DEBUG } inline void QOpenGLFunctions::glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr) { -#ifdef QT_OPENGL_ES_2 - ::glVertexAttribPointer(indx, size, type, normalized, stride, ptr); -#else Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); d_ptr->f.VertexAttribPointer(indx, size, type, normalized, stride, ptr); -#endif Q_OPENGL_FUNCTIONS_DEBUG } diff --git a/src/gui/opengl/qopenglprogrambinarycache.cpp b/src/gui/opengl/qopenglprogrambinarycache.cpp new file mode 100644 index 0000000000..5c8e425e29 --- /dev/null +++ b/src/gui/opengl/qopenglprogrambinarycache.cpp @@ -0,0 +1,336 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopenglprogrambinarycache_p.h" +#include <QOpenGLContext> +#include <QOpenGLExtraFunctions> +#include <QStandardPaths> +#include <QDir> +#include <QSaveFile> +#include <QLoggingCategory> + +#ifdef Q_OS_UNIX +#include <sys/mman.h> +#include <private/qcore_unix_p.h> +#endif + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(DBG_SHADER_CACHE) + +#ifndef GL_PROGRAM_BINARY_LENGTH +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#endif + +const quint32 BINSHADER_MAGIC = 0x5174; +const quint32 BINSHADER_VERSION = 0x1; +const quint32 BINSHADER_QTVERSION = QT_VERSION; + +struct GLEnvInfo +{ + GLEnvInfo(); + + QByteArray glvendor; + QByteArray glrenderer; + QByteArray glversion; +}; + +GLEnvInfo::GLEnvInfo() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + Q_ASSERT(ctx); + QOpenGLFunctions *f = ctx->functions(); + const char *vendor = reinterpret_cast<const char *>(f->glGetString(GL_VENDOR)); + const char *renderer = reinterpret_cast<const char *>(f->glGetString(GL_RENDERER)); + const char *version = reinterpret_cast<const char *>(f->glGetString(GL_VERSION)); + if (vendor) + glvendor = QByteArray(vendor); + if (renderer) + glrenderer = QByteArray(renderer); + if (version) + glversion = QByteArray(version); +} + +static inline bool qt_ensureWritableDir(const QString &name) +{ + QDir::root().mkpath(name); + return QFileInfo(name).isWritable(); +} + +QOpenGLProgramBinaryCache::QOpenGLProgramBinaryCache() + : m_cacheWritable(false) +{ + const QString subPath = QLatin1String("/qtshadercache/"); + const QString sharedCachePath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation); + if (!sharedCachePath.isEmpty()) { + m_cacheDir = sharedCachePath + subPath; + m_cacheWritable = qt_ensureWritableDir(m_cacheDir); + } + if (!m_cacheWritable) { + m_cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + subPath; + m_cacheWritable = qt_ensureWritableDir(m_cacheDir); + } + qCDebug(DBG_SHADER_CACHE, "Cache location '%s' writable = %d", qPrintable(m_cacheDir), m_cacheWritable); +} + +QString QOpenGLProgramBinaryCache::cacheFileName(const QByteArray &cacheKey) const +{ + return m_cacheDir + QString::fromUtf8(cacheKey); +} + +static const int HEADER_SIZE = 3 * sizeof(quint32); + +bool QOpenGLProgramBinaryCache::verifyHeader(const QByteArray &buf) const +{ + if (buf.size() < HEADER_SIZE) { + qCDebug(DBG_SHADER_CACHE, "Cached size too small"); + return false; + } + const quint32 *p = reinterpret_cast<const quint32 *>(buf.constData()); + if (*p++ != BINSHADER_MAGIC) { + qCDebug(DBG_SHADER_CACHE, "Magic does not match"); + return false; + } + if (*p++ != BINSHADER_VERSION) { + qCDebug(DBG_SHADER_CACHE, "Version does not match"); + return false; + } + if (*p++ != BINSHADER_QTVERSION) { + qCDebug(DBG_SHADER_CACHE, "Qt version does not match"); + return false; + } + return true; +} + +bool QOpenGLProgramBinaryCache::setProgramBinary(uint programId, uint blobFormat, const void *p, uint blobSize) +{ + QOpenGLExtraFunctions *funcs = QOpenGLContext::currentContext()->extraFunctions(); + while (funcs->glGetError() != GL_NO_ERROR) { } + funcs->glProgramBinary(programId, blobFormat, p, blobSize); + int err = funcs->glGetError(); + qCDebug(DBG_SHADER_CACHE, "Program binary set for program %u, size %d, format 0x%x, err = 0x%x", + programId, blobSize, blobFormat, err); + return err == 0; +} + +#ifdef Q_OS_UNIX +class FdWrapper +{ +public: + FdWrapper(const QString &fn) + : ptr(MAP_FAILED) + { + fd = qt_safe_open(QFile::encodeName(fn).constData(), O_RDONLY); + } + ~FdWrapper() + { + if (ptr != MAP_FAILED) + munmap(ptr, mapSize); + if (fd != -1) + qt_safe_close(fd); + } + bool map() + { + off_t offs = lseek(fd, 0, SEEK_END); + if (offs == (off_t) -1) { + qErrnoWarning(errno, "lseek failed for program binary"); + return false; + } + mapSize = static_cast<size_t>(offs); + ptr = mmap(nullptr, mapSize, PROT_READ, MAP_SHARED, fd, 0); + return ptr != MAP_FAILED; + } + + int fd; + void *ptr; + size_t mapSize; +}; +#endif + +class DeferredFileRemove +{ +public: + DeferredFileRemove(const QString &fn) + : fn(fn), + active(false) + { + } + ~DeferredFileRemove() + { + if (active) + QFile(fn).remove(); + } + void setActive() + { + active = true; + } + + QString fn; + bool active; +}; + +bool QOpenGLProgramBinaryCache::load(const QByteArray &cacheKey, uint programId) +{ + if (m_memCache.contains(cacheKey)) { + const MemCacheEntry *e = m_memCache[cacheKey]; + return setProgramBinary(programId, e->format, e->blob.constData(), e->blob.size()); + } + + QByteArray buf; + const QString fn = cacheFileName(cacheKey); + DeferredFileRemove undertaker(fn); +#ifdef Q_OS_UNIX + FdWrapper fdw(fn); + if (fdw.fd == -1) + return false; + char header[HEADER_SIZE]; + qint64 bytesRead = qt_safe_read(fdw.fd, header, HEADER_SIZE); + if (bytesRead == HEADER_SIZE) + buf = QByteArray::fromRawData(header, HEADER_SIZE); +#else + QFile f(fn); + if (!f.open(QIODevice::ReadOnly)) + return false; + buf = f.read(HEADER_SIZE); +#endif + + if (!verifyHeader(buf)) { + undertaker.setActive(); + return false; + } + + const quint32 *p; +#ifdef Q_OS_UNIX + if (!fdw.map()) { + undertaker.setActive(); + return false; + } + p = reinterpret_cast<const quint32 *>(static_cast<const char *>(fdw.ptr) + HEADER_SIZE); +#else + buf = f.readAll(); + p = reinterpret_cast<const quint32 *>(buf.constData()); +#endif + + GLEnvInfo info; + + quint32 v = *p++; + QByteArray vendor = QByteArray::fromRawData(reinterpret_cast<const char *>(p), v); + if (vendor != info.glvendor) { + qCDebug(DBG_SHADER_CACHE, "GL_VENDOR does not match (%s, %s)", vendor.constData(), info.glvendor.constData()); + undertaker.setActive(); + return false; + } + p = reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(p) + v); + v = *p++; + QByteArray renderer = QByteArray::fromRawData(reinterpret_cast<const char *>(p), v); + if (renderer != info.glrenderer) { + qCDebug(DBG_SHADER_CACHE, "GL_RENDERER does not match (%s, %s)", renderer.constData(), info.glrenderer.constData()); + undertaker.setActive(); + return false; + } + p = reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(p) + v); + v = *p++; + QByteArray version = QByteArray::fromRawData(reinterpret_cast<const char *>(p), v); + if (version != info.glversion) { + qCDebug(DBG_SHADER_CACHE, "GL_VERSION does not match (%s, %s)", version.constData(), info.glversion.constData()); + undertaker.setActive(); + return false; + } + p = reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(p) + v); + + quint32 blobFormat = *p++; + quint32 blobSize = *p++; + + return setProgramBinary(programId, blobFormat, p, blobSize) + && m_memCache.insert(cacheKey, new MemCacheEntry(p, blobSize, blobFormat)); +} + +void QOpenGLProgramBinaryCache::save(const QByteArray &cacheKey, uint programId) +{ + if (!m_cacheWritable) + return; + + GLEnvInfo info; + + QOpenGLExtraFunctions *funcs = QOpenGLContext::currentContext()->extraFunctions(); + GLint blobSize = 0; + while (funcs->glGetError() != GL_NO_ERROR) { } + funcs->glGetProgramiv(programId, GL_PROGRAM_BINARY_LENGTH, &blobSize); + int totalSize = blobSize + 8 + 12 + 12 + info.glvendor.size() + info.glrenderer.size() + info.glversion.size(); + qCDebug(DBG_SHADER_CACHE, "Program binary is %d bytes, err = 0x%x, total %d", blobSize, funcs->glGetError(), totalSize); + if (!blobSize) + return; + + QByteArray blob(totalSize, Qt::Uninitialized); + quint32 *p = reinterpret_cast<quint32 *>(blob.data()); + + *p++ = BINSHADER_MAGIC; + *p++ = BINSHADER_VERSION; + *p++ = BINSHADER_QTVERSION; + + *p++ = info.glvendor.size(); + memcpy(p, info.glvendor.constData(), info.glvendor.size()); + p = reinterpret_cast<quint32 *>(reinterpret_cast<char *>(p) + info.glvendor.size()); + *p++ = info.glrenderer.size(); + memcpy(p, info.glrenderer.constData(), info.glrenderer.size()); + p = reinterpret_cast<quint32 *>(reinterpret_cast<char *>(p) + info.glrenderer.size()); + *p++ = info.glversion.size(); + memcpy(p, info.glversion.constData(), info.glversion.size()); + p = reinterpret_cast<quint32 *>(reinterpret_cast<char *>(p) + info.glversion.size()); + + GLint outSize = 0; + quint32 *blobFormat = p++; + *p++ = blobSize; + funcs->glGetProgramBinary(programId, blobSize, &outSize, blobFormat, p); + if (blobSize != outSize) { + qCDebug(DBG_SHADER_CACHE, "glGetProgramBinary returned size %d instead of %d", outSize, blobSize); + return; + } + + QSaveFile f(cacheFileName(cacheKey)); + if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + f.write(blob); + if (!f.commit()) + qCDebug(DBG_SHADER_CACHE, "Failed to write %s to shader cache", qPrintable(f.fileName())); + } else { + qCDebug(DBG_SHADER_CACHE, "Failed to create %s in shader cache", qPrintable(f.fileName())); + } +} + +QT_END_NAMESPACE diff --git a/src/gui/opengl/qopenglprogrambinarycache_p.h b/src/gui/opengl/qopenglprogrambinarycache_p.h new file mode 100644 index 0000000000..a0e1f91e25 --- /dev/null +++ b/src/gui/opengl/qopenglprogrambinarycache_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENGLPROGRAMBINARYCACHE_P_H +#define QOPENGLPROGRAMBINARYCACHE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qtguiglobal.h> +#include <QtGui/qopenglshaderprogram.h> +#include <QtCore/qcache.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLProgramBinaryCache +{ +public: + struct ShaderDesc { + ShaderDesc() { } + ShaderDesc(QOpenGLShader::ShaderType type, const QByteArray &source = QByteArray()) + : type(type), source(source) + { } + QOpenGLShader::ShaderType type; + QByteArray source; + }; + struct ProgramDesc { + QVector<ShaderDesc> shaders; + }; + + QOpenGLProgramBinaryCache(); + + bool load(const QByteArray &cacheKey, uint programId); + void save(const QByteArray &cacheKey, uint programId); + +private: + QString cacheFileName(const QByteArray &cacheKey) const; + bool verifyHeader(const QByteArray &buf) const; + bool setProgramBinary(uint programId, uint blobFormat, const void *p, uint blobSize); + + QString m_cacheDir; + bool m_cacheWritable; + struct MemCacheEntry { + MemCacheEntry(const void *p, int size, uint format) + : blob(reinterpret_cast<const char *>(p), size), + format(format) + { } + QByteArray blob; + uint format; + }; + QCache<QByteArray, MemCacheEntry> m_memCache; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/opengl/qopenglshaderprogram.cpp b/src/gui/opengl/qopenglshaderprogram.cpp index cd582c5285..d296f327c8 100644 --- a/src/gui/opengl/qopenglshaderprogram.cpp +++ b/src/gui/opengl/qopenglshaderprogram.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qopenglshaderprogram.h" +#include "qopenglprogrambinarycache_p.h" #include "qopenglfunctions.h" #include "private/qopenglcontext_p.h" #include <QtCore/private/qobject_p.h> @@ -46,6 +47,9 @@ #include <QtCore/qvarlengtharray.h> #include <QtCore/qvector.h> #include <QtCore/qregularexpression.h> +#include <QtCore/qloggingcategory.h> +#include <QtCore/qcryptographichash.h> +#include <QtCore/qcoreapplication.h> #include <QtGui/qtransform.h> #include <QtGui/QColor> #include <QtGui/QSurfaceFormat> @@ -127,6 +131,20 @@ QT_BEGIN_NAMESPACE on the shader program. The shader program's id can be explicitly created using the create() function. + \section2 Caching Program Binaries + + As of Qt 5.9, support for caching program binaries on disk is built in. To + enable this, switch to using addCacheableShaderFromSourceCode() and + addCacheableShaderFromSourceFile(). With an OpenGL ES 3.x context or support + for \c{GL_ARB_get_program_binary}, this will transparently cache program + binaries under QStandardPaths::GenericCacheLocation or + QStandardPaths::CacheLocation. When support is not available, calling the + cacheable function variants is equivalent to the normal ones. + + \note Some drivers do not have any binary formats available, even though + they advertise the extension or offer OpenGL ES 3.0. In this case program + binary support will be disabled. + \sa QOpenGLShader */ @@ -162,6 +180,84 @@ QT_BEGIN_NAMESPACE based on the core feature (requires OpenGL >= 4.3). */ +Q_LOGGING_CATEGORY(DBG_SHADER_CACHE, "qt.opengl.diskcache") + +// For GLES 3.1/3.2 +#ifndef GL_GEOMETRY_SHADER +#define GL_GEOMETRY_SHADER 0x8DD9 +#endif +#ifndef GL_TESS_CONTROL_SHADER +#define GL_TESS_CONTROL_SHADER 0x8E88 +#endif +#ifndef GL_TESS_EVALUATION_SHADER +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#endif +#ifndef GL_COMPUTE_SHADER +#define GL_COMPUTE_SHADER 0x91B9 +#endif +#ifndef GL_MAX_GEOMETRY_OUTPUT_VERTICES +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#endif +#ifndef GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#endif +#ifndef GL_PATCH_VERTICES +#define GL_PATCH_VERTICES 0x8E72 +#endif +#ifndef GL_PATCH_DEFAULT_OUTER_LEVEL +#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 +#endif +#ifndef GL_PATCH_DEFAULT_INNER_LEVEL +#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 +#endif + +#ifndef GL_NUM_PROGRAM_BINARY_FORMATS +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#endif + +static inline bool isFormatGLES(const QSurfaceFormat &f) +{ + return (f.renderableType() == QSurfaceFormat::OpenGLES); +} + +static inline bool supportsGeometry(const QSurfaceFormat &f) +{ +#ifndef QT_OPENGL_ES_2 + if (!isFormatGLES(f)) + return (f.version() >= qMakePair<int, int>(3, 2)); + else + return false; +#else + Q_UNUSED(f); + return false; +#endif +} + +static inline bool supportsCompute(const QSurfaceFormat &f) +{ +#ifndef QT_OPENGL_ES_2 + if (!isFormatGLES(f)) + return (f.version() >= qMakePair<int, int>(4, 3)); + else + return (f.version() >= qMakePair<int, int>(3, 1)); +#else + return (f.version() >= qMakePair<int, int>(3, 1)); +#endif +} + +static inline bool supportsTessellation(const QSurfaceFormat &f) +{ +#ifndef QT_OPENGL_ES_2 + if (!isFormatGLES(f)) + return (f.version() >= qMakePair<int, int>(4, 0)); + else + return false; +#else + Q_UNUSED(f); + return false; +#endif +} + class QOpenGLShaderPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QOpenGLShader) @@ -171,22 +267,16 @@ public: , shaderType(type) , compiled(false) , glfuncs(new QOpenGLFunctions(ctx)) -#ifndef QT_OPENGL_ES_2 , supportsGeometryShaders(false) , supportsTessellationShaders(false) -#endif + , supportsComputeShaders(false) { -#ifndef QT_OPENGL_ES_2 - if (!ctx->isOpenGLES()) { - QSurfaceFormat f = ctx->format(); - - // Geometry shaders require OpenGL >= 3.2 - if (shaderType & QOpenGLShader::Geometry) - supportsGeometryShaders = (f.version() >= qMakePair<int, int>(3, 2)); - else if (shaderType & (QOpenGLShader::TessellationControl | QOpenGLShader::TessellationEvaluation)) - supportsTessellationShaders = (f.version() >= qMakePair<int, int>(4, 0)); - } -#endif + if (shaderType & QOpenGLShader::Geometry) + supportsGeometryShaders = supportsGeometry(ctx->format()); + else if (shaderType & (QOpenGLShader::TessellationControl | QOpenGLShader::TessellationEvaluation)) + supportsTessellationShaders = supportsTessellation(ctx->format()); + else if (shaderType & QOpenGLShader::Compute) + supportsComputeShaders = supportsCompute(ctx->format()); } ~QOpenGLShaderPrivate(); @@ -197,13 +287,13 @@ public: QOpenGLFunctions *glfuncs; -#ifndef QT_OPENGL_ES_2 // Support for geometry shaders bool supportsGeometryShaders; - // Support for tessellation shaders bool supportsTessellationShaders; -#endif + // Support for compute shaders + bool supportsComputeShaders; + bool create(); bool compile(QOpenGLShader *q); @@ -229,24 +319,18 @@ bool QOpenGLShaderPrivate::create() QOpenGLContext *context = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); if (!context) return false; - GLuint shader; + GLuint shader = 0; if (shaderType == QOpenGLShader::Vertex) { shader = glfuncs->glCreateShader(GL_VERTEX_SHADER); -#if defined(QT_OPENGL_3_2) } else if (shaderType == QOpenGLShader::Geometry && supportsGeometryShaders) { shader = glfuncs->glCreateShader(GL_GEOMETRY_SHADER); -#endif -#if defined(QT_OPENGL_4) } else if (shaderType == QOpenGLShader::TessellationControl && supportsTessellationShaders) { shader = glfuncs->glCreateShader(GL_TESS_CONTROL_SHADER); } else if (shaderType == QOpenGLShader::TessellationEvaluation && supportsTessellationShaders) { shader = glfuncs->glCreateShader(GL_TESS_EVALUATION_SHADER); -#endif -#if defined(QT_OPENGL_4_3) - } else if (shaderType == QOpenGLShader::Compute) { + } else if (shaderType == QOpenGLShader::Compute && supportsComputeShaders) { shader = glfuncs->glCreateShader(GL_COMPUTE_SHADER); -#endif - } else { + } else if (shaderType == QOpenGLShader::Fragment) { shader = glfuncs->glCreateShader(GL_FRAGMENT_SHADER); } if (!shader) { @@ -710,6 +794,7 @@ public: #ifndef QT_OPENGL_ES_2 , tessellationFuncs(0) #endif + , linkBinaryRecursion(false) { } ~QOpenGLShaderProgramPrivate(); @@ -731,6 +816,13 @@ public: #endif bool hasShader(QOpenGLShader::ShaderType type) const; + + QOpenGLProgramBinaryCache::ProgramDesc binaryProgram; + bool isCacheDisabled() const; + bool compileCacheable(); + bool linkBinary(); + + bool linkBinaryRecursion; }; namespace { @@ -962,6 +1054,139 @@ bool QOpenGLShaderProgram::addShaderFromSourceFile } /*! + Registers the shader of the specified \a type and \a source to this + program. Unlike addShaderFromSourceCode(), this function does not perform + compilation. Compilation is deferred to link(), and may not happen at all, + because link() may potentially use a program binary from Qt's shader disk + cache. This will typically lead to a significant increase in performance. + + \return true if the shader has been registered or, in the non-cached case, + compiled successfully; false if there was an error. The compilation error + messages can be retrieved via log(). + + When the disk cache is disabled, via Qt::AA_DisableShaderDiskCache for + example, or the OpenGL context has no support for context binaries, calling + this function is equivalent to addShaderFromSourceCode(). + + \since 5.9 + \sa addShaderFromSourceCode(), addCacheableShaderFromSourceFile() + */ +bool QOpenGLShaderProgram::addCacheableShaderFromSourceCode(QOpenGLShader::ShaderType type, const char *source) +{ + Q_D(QOpenGLShaderProgram); + if (!init()) + return false; + if (d->isCacheDisabled()) + return addShaderFromSourceCode(type, source); + + return addCacheableShaderFromSourceCode(type, QByteArray(source)); +} + +/*! + \overload + + Registers the shader of the specified \a type and \a source to this + program. Unlike addShaderFromSourceCode(), this function does not perform + compilation. Compilation is deferred to link(), and may not happen at all, + because link() may potentially use a program binary from Qt's shader disk + cache. This will typically lead to a significant increase in performance. + + \return true if the shader has been registered or, in the non-cached case, + compiled successfully; false if there was an error. The compilation error + messages can be retrieved via log(). + + When the disk cache is disabled, via Qt::AA_DisableShaderDiskCache for + example, or the OpenGL context has no support for context binaries, calling + this function is equivalent to addShaderFromSourceCode(). + + \since 5.9 + \sa addShaderFromSourceCode(), addCacheableShaderFromSourceFile() + */ +bool QOpenGLShaderProgram::addCacheableShaderFromSourceCode(QOpenGLShader::ShaderType type, const QByteArray &source) +{ + Q_D(QOpenGLShaderProgram); + if (!init()) + return false; + if (d->isCacheDisabled()) + return addShaderFromSourceCode(type, source); + + d->binaryProgram.shaders.append(QOpenGLProgramBinaryCache::ShaderDesc(type, source)); + return true; +} + +/*! + \overload + + Registers the shader of the specified \a type and \a source to this + program. Unlike addShaderFromSourceCode(), this function does not perform + compilation. Compilation is deferred to link(), and may not happen at all, + because link() may potentially use a program binary from Qt's shader disk + cache. This will typically lead to a significant increase in performance. + + When the disk cache is disabled, via Qt::AA_DisableShaderDiskCache for + example, or the OpenGL context has no support for context binaries, calling + this function is equivalent to addShaderFromSourceCode(). + + \since 5.9 + \sa addShaderFromSourceCode(), addCacheableShaderFromSourceFile() + */ +bool QOpenGLShaderProgram::addCacheableShaderFromSourceCode(QOpenGLShader::ShaderType type, const QString &source) +{ + Q_D(QOpenGLShaderProgram); + if (!init()) + return false; + if (d->isCacheDisabled()) + return addShaderFromSourceCode(type, source); + + return addCacheableShaderFromSourceCode(type, source.toUtf8().constData()); +} + +/*! + Registers the shader of the specified \a type and \a fileName to this + program. Unlike addShaderFromSourceFile(), this function does not perform + compilation. Compilation is deferred to link(), and may not happen at all, + because link() may potentially use a program binary from Qt's shader disk + cache. This will typically lead to a significant increase in performance. + + \return true if the file has been read successfully, false if the file could + not be opened or the normal, non-cached compilation of the shader has + failed. The compilation error messages can be retrieved via log(). + + When the disk cache is disabled, via Qt::AA_DisableShaderDiskCache for + example, or the OpenGL context has no support for context binaries, calling + this function is equivalent to addShaderFromSourceFile(). + + \since 5.9 + \sa addShaderFromSourceFile(), addCacheableShaderFromSourceCode() + */ +bool QOpenGLShaderProgram::addCacheableShaderFromSourceFile(QOpenGLShader::ShaderType type, const QString &fileName) +{ + Q_D(QOpenGLShaderProgram); + if (!init()) + return false; + if (d->isCacheDisabled()) + return addShaderFromSourceFile(type, fileName); + + QOpenGLProgramBinaryCache::ShaderDesc shader(type); + // NB! It could be tempting to defer reading the file contents and just + // hash the filename as the cache key, perhaps combined with last-modified + // timestamp checks. However, this would raise a number of issues (no + // timestamps for files in the resource system; preference for global, not + // per-application cache items (where filenames may clash); resource-based + // shaders from libraries like Qt Quick; etc.), so just avoid it. + QFile f(fileName); + if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { + shader.source = f.readAll(); + f.close(); + } else { + qWarning("QOpenGLShaderProgram: Unable to open file %s", qPrintable(fileName)); + return false; + } + d->binaryProgram.shaders.append(shader); + return true; +} + +/*! Removes \a shader from this shader program. The object is not deleted. The shader program must be valid in the current QOpenGLContext. @@ -1019,6 +1244,7 @@ void QOpenGLShaderProgram::removeAllShaders() qDeleteAll(d->anonShaders); d->shaders.clear(); d->anonShaders.clear(); + d->binaryProgram = QOpenGLProgramBinaryCache::ProgramDesc(); d->linked = false; // Program needs to be relinked. d->removingShaders = false; } @@ -1035,6 +1261,16 @@ void QOpenGLShaderProgram::removeAllShaders() If the shader program was already linked, calling this function again will force it to be re-linked. + When shaders were added to this program via + addCacheableShaderFromSourceCode() or addCacheableShaderFromSourceFile(), + program binaries are supported, and a cached binary is available on disk, + actual compilation and linking are skipped. Instead, link() will initialize + the program with the binary blob via glProgramBinary(). If there is no + cached version of the program or it was generated with a different driver + version, the shaders will be compiled from source and the program will get + linked normally. This allows seamless upgrading of the graphics drivers, + without having to worry about potentially incompatible binary formats. + \sa addShader(), log() */ bool QOpenGLShaderProgram::link() @@ -1044,12 +1280,17 @@ bool QOpenGLShaderProgram::link() if (!program) return false; + if (!d->linkBinaryRecursion && d->shaders.isEmpty() && !d->binaryProgram.shaders.isEmpty()) + return d->linkBinary(); + GLint value; if (d->shaders.isEmpty()) { // If there are no explicit shaders, then it is possible that the - // application added a program binary with glProgramBinaryOES(), - // or otherwise populated the shaders itself. Check to see if the - // program is already linked and bail out if so. + // application added a program binary with glProgramBinaryOES(), or + // otherwise populated the shaders itself. This is also the case when + // we are recursively called back from linkBinary() after a successful + // glProgramBinary(). Check to see if the program is already linked and + // bail out if so. value = 0; d->glfuncs->glGetProgramiv(program, GL_LINK_STATUS, &value); d->linked = (value != 0); @@ -3230,10 +3471,8 @@ void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix4 int QOpenGLShaderProgram::maxGeometryOutputVertices() const { GLint n = 0; -#if defined(QT_OPENGL_3_2) Q_D(const QOpenGLShaderProgram); d->glfuncs->glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES, &n); -#endif return n; } @@ -3257,7 +3496,7 @@ int QOpenGLShaderProgram::maxGeometryOutputVertices() const */ void QOpenGLShaderProgram::setPatchVertexCount(int count) { -#if defined(QT_OPENGL_4) +#ifndef QT_OPENGL_ES_2 Q_D(QOpenGLShaderProgram); if (d->tessellationFuncs) d->tessellationFuncs->glPatchParameteri(GL_PATCH_VERTICES, count); @@ -3276,13 +3515,15 @@ void QOpenGLShaderProgram::setPatchVertexCount(int count) */ int QOpenGLShaderProgram::patchVertexCount() const { +#ifndef QT_OPENGL_ES_2 int patchVertices = 0; -#if defined(QT_OPENGL_4) Q_D(const QOpenGLShaderProgram); if (d->tessellationFuncs) d->tessellationFuncs->glGetIntegerv(GL_PATCH_VERTICES, &patchVertices); -#endif return patchVertices; +#else + return 0; +#endif } /*! @@ -3304,21 +3545,21 @@ int QOpenGLShaderProgram::patchVertexCount() const */ void QOpenGLShaderProgram::setDefaultOuterTessellationLevels(const QVector<float> &levels) { -#if defined(QT_OPENGL_4) - QVector<float> tessLevels = levels; - - // Ensure we have the required 4 outer tessellation levels - // Use default of 1 for missing entries (same as spec) - const int argCount = 4; - if (tessLevels.size() < argCount) { - tessLevels.reserve(argCount); - for (int i = tessLevels.size(); i < argCount; ++i) - tessLevels.append(1.0f); - } - +#ifndef QT_OPENGL_ES_2 Q_D(QOpenGLShaderProgram); - if (d->tessellationFuncs) + if (d->tessellationFuncs) { + QVector<float> tessLevels = levels; + + // Ensure we have the required 4 outer tessellation levels + // Use default of 1 for missing entries (same as spec) + const int argCount = 4; + if (tessLevels.size() < argCount) { + tessLevels.reserve(argCount); + for (int i = tessLevels.size(); i < argCount; ++i) + tessLevels.append(1.0f); + } d->tessellationFuncs->glPatchParameterfv(GL_PATCH_DEFAULT_OUTER_LEVEL, tessLevels.data()); + } #else Q_UNUSED(levels); #endif @@ -3341,13 +3582,15 @@ void QOpenGLShaderProgram::setDefaultOuterTessellationLevels(const QVector<float */ QVector<float> QOpenGLShaderProgram::defaultOuterTessellationLevels() const { +#ifndef QT_OPENGL_ES_2 QVector<float> tessLevels(4, 1.0f); -#if defined(QT_OPENGL_4) Q_D(const QOpenGLShaderProgram); if (d->tessellationFuncs) d->tessellationFuncs->glGetFloatv(GL_PATCH_DEFAULT_OUTER_LEVEL, tessLevels.data()); -#endif return tessLevels; +#else + return QVector<float>(); +#endif } /*! @@ -3369,21 +3612,21 @@ QVector<float> QOpenGLShaderProgram::defaultOuterTessellationLevels() const */ void QOpenGLShaderProgram::setDefaultInnerTessellationLevels(const QVector<float> &levels) { -#if defined(QT_OPENGL_4) - QVector<float> tessLevels = levels; - - // Ensure we have the required 2 inner tessellation levels - // Use default of 1 for missing entries (same as spec) - const int argCount = 2; - if (tessLevels.size() < argCount) { - tessLevels.reserve(argCount); - for (int i = tessLevels.size(); i < argCount; ++i) - tessLevels.append(1.0f); - } - +#ifndef QT_OPENGL_ES_2 Q_D(QOpenGLShaderProgram); - if (d->tessellationFuncs) + if (d->tessellationFuncs) { + QVector<float> tessLevels = levels; + + // Ensure we have the required 2 inner tessellation levels + // Use default of 1 for missing entries (same as spec) + const int argCount = 2; + if (tessLevels.size() < argCount) { + tessLevels.reserve(argCount); + for (int i = tessLevels.size(); i < argCount; ++i) + tessLevels.append(1.0f); + } d->tessellationFuncs->glPatchParameterfv(GL_PATCH_DEFAULT_INNER_LEVEL, tessLevels.data()); + } #else Q_UNUSED(levels); #endif @@ -3406,13 +3649,15 @@ void QOpenGLShaderProgram::setDefaultInnerTessellationLevels(const QVector<float */ QVector<float> QOpenGLShaderProgram::defaultInnerTessellationLevels() const { +#ifndef QT_OPENGL_ES_2 QVector<float> tessLevels(2, 1.0f); -#if defined(QT_OPENGL_4) Q_D(const QOpenGLShaderProgram); if (d->tessellationFuncs) d->tessellationFuncs->glGetFloatv(GL_PATCH_DEFAULT_INNER_LEVEL, tessLevels.data()); -#endif return tessLevels; +#else + return QVector<float>(); +#endif } @@ -3425,16 +3670,11 @@ QVector<float> QOpenGLShaderProgram::defaultInnerTessellationLevels() const */ bool QOpenGLShaderProgram::hasOpenGLShaderPrograms(QOpenGLContext *context) { -#if !defined(QT_OPENGL_ES_2) if (!context) context = QOpenGLContext::currentContext(); if (!context) return false; return QOpenGLFunctions(context).hasOpenGLFeature(QOpenGLFunctions::Shaders); -#else - Q_UNUSED(context); - return true; -#endif } /*! @@ -3465,37 +3705,148 @@ bool QOpenGLShader::hasOpenGLShaders(ShaderType type, QOpenGLContext *context) if ((type & ~(Geometry | Vertex | Fragment | TessellationControl | TessellationEvaluation | Compute)) || type == 0) return false; - QSurfaceFormat format = context->format(); - if (type == Geometry) { -#ifndef QT_OPENGL_ES_2 - // Geometry shaders require OpenGL 3.2 or newer - QSurfaceFormat format = context->format(); - return (!context->isOpenGLES()) - && (format.version() >= qMakePair<int, int>(3, 2)); -#else - // No geometry shader support in OpenGL ES2 - return false; -#endif - } else if (type == TessellationControl || type == TessellationEvaluation) { -#if !defined(QT_OPENGL_ES_2) - return (!context->isOpenGLES()) - && (format.version() >= qMakePair<int, int>(4, 0)); -#else - // No tessellation shader support in OpenGL ES2 - return false; -#endif - } else if (type == Compute) { -#if defined(QT_OPENGL_4_3) - return (format.version() >= qMakePair<int, int>(4, 3)); -#else - // No compute shader support without OpenGL 4.3 or newer - return false; -#endif - } + if (type & QOpenGLShader::Geometry) + return supportsGeometry(context->format()); + else if (type & (QOpenGLShader::TessellationControl | QOpenGLShader::TessellationEvaluation)) + return supportsTessellation(context->format()); + else if (type & QOpenGLShader::Compute) + return supportsCompute(context->format()); // Unconditional support of vertex and fragment shaders implicitly assumes // a minimum OpenGL version of 2.0 return true; } +// While unlikely, one application can in theory use contexts with different versions +// or profiles. Therefore any version- or extension-specific checks must be done on a +// per-context basis, not just once per process. QOpenGLSharedResource enables this, +// although it's once-per-sharing-context-group, not per-context. Still, this should +// be good enough in practice. +class QOpenGLProgramBinarySupportCheck : public QOpenGLSharedResource +{ +public: + QOpenGLProgramBinarySupportCheck(QOpenGLContext *context); + void invalidateResource() override { } + void freeResource(QOpenGLContext *) override { } + + bool isSupported() const { return m_supported; } + +private: + bool m_supported; +}; + +QOpenGLProgramBinarySupportCheck::QOpenGLProgramBinarySupportCheck(QOpenGLContext *context) + : QOpenGLSharedResource(context->shareGroup()), + m_supported(false) +{ + if (QCoreApplication::testAttribute(Qt::AA_DisableShaderDiskCache)) { + qCDebug(DBG_SHADER_CACHE, "Shader cache disabled via app attribute"); + return; + } + if (qEnvironmentVariableIntValue("QT_DISABLE_SHADER_DISK_CACHE")) { + qCDebug(DBG_SHADER_CACHE, "Shader cache disabled via env var"); + return; + } + + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (ctx) { + if (ctx->isOpenGLES()) { + qCDebug(DBG_SHADER_CACHE, "OpenGL ES v%d context", ctx->format().majorVersion()); + if (ctx->format().majorVersion() >= 3) + m_supported = true; + } else { + const bool hasExt = ctx->hasExtension("GL_ARB_get_program_binary"); + qCDebug(DBG_SHADER_CACHE, "GL_ARB_get_program_binary support = %d", hasExt); + if (hasExt) + m_supported = true; + } + if (m_supported) { + GLint fmtCount = 0; + ctx->functions()->glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &fmtCount); + qCDebug(DBG_SHADER_CACHE, "Supported binary format count = %d", fmtCount); + m_supported = fmtCount > 0; + } + } + qCDebug(DBG_SHADER_CACHE, "Shader cache supported = %d", m_supported); +} + +class QOpenGLProgramBinarySupportCheckWrapper +{ +public: + QOpenGLProgramBinarySupportCheck *get(QOpenGLContext *context) + { + return m_resource.value<QOpenGLProgramBinarySupportCheck>(context); + } + +private: + QOpenGLMultiGroupSharedResource m_resource; +}; + +bool QOpenGLShaderProgramPrivate::isCacheDisabled() const +{ + static QOpenGLProgramBinarySupportCheckWrapper binSupportCheck; + return !binSupportCheck.get(QOpenGLContext::currentContext())->isSupported(); +} + +bool QOpenGLShaderProgramPrivate::compileCacheable() +{ + Q_Q(QOpenGLShaderProgram); + for (const QOpenGLProgramBinaryCache::ShaderDesc &shader : qAsConst(binaryProgram.shaders)) { + QScopedPointer<QOpenGLShader> s(new QOpenGLShader(shader.type, q)); + if (!s->compileSourceCode(shader.source)) { + log = s->log(); + return false; + } + anonShaders.append(s.take()); + if (!q->addShader(anonShaders.last())) + return false; + } + return true; +} + +bool QOpenGLShaderProgramPrivate::linkBinary() +{ + static QOpenGLProgramBinaryCache binCache; + + Q_Q(QOpenGLShaderProgram); + + QCryptographicHash keyBuilder(QCryptographicHash::Sha1); + for (const QOpenGLProgramBinaryCache::ShaderDesc &shader : qAsConst(binaryProgram.shaders)) + keyBuilder.addData(shader.source); + + const QByteArray cacheKey = keyBuilder.result().toHex(); + if (DBG_SHADER_CACHE().isEnabled(QtDebugMsg)) + qCDebug(DBG_SHADER_CACHE, "program with %d shaders, cache key %s", + binaryProgram.shaders.count(), cacheKey.constData()); + + bool needsCompile = true; + if (binCache.load(cacheKey, q->programId())) { + qCDebug(DBG_SHADER_CACHE, "Program binary received from cache"); + linkBinaryRecursion = true; + bool ok = q->link(); + linkBinaryRecursion = false; + if (ok) + needsCompile = false; + else + qCDebug(DBG_SHADER_CACHE, "Link failed after glProgramBinary"); + } + + bool needsSave = false; + if (needsCompile) { + qCDebug(DBG_SHADER_CACHE, "Program binary not in cache, compiling"); + if (compileCacheable()) + needsSave = true; + else + return false; + } + + linkBinaryRecursion = true; + bool ok = q->link(); + linkBinaryRecursion = false; + if (ok && needsSave) + binCache.save(cacheKey, q->programId()); + + return ok; +} + QT_END_NAMESPACE diff --git a/src/gui/opengl/qopenglshaderprogram.h b/src/gui/opengl/qopenglshaderprogram.h index 2da359c535..fd4d82ecf9 100644 --- a/src/gui/opengl/qopenglshaderprogram.h +++ b/src/gui/opengl/qopenglshaderprogram.h @@ -50,6 +50,13 @@ #include <QtGui/qvector4d.h> #include <QtGui/qmatrix4x4.h> +#if defined(Q_CLANG_QDOC) +#undef GLint +typedef int GLint; +#undef GLfloat +typedef double GLfloat; +#endif + QT_BEGIN_NAMESPACE @@ -119,6 +126,11 @@ public: bool addShaderFromSourceCode(QOpenGLShader::ShaderType type, const QString& source); bool addShaderFromSourceFile(QOpenGLShader::ShaderType type, const QString& fileName); + bool addCacheableShaderFromSourceCode(QOpenGLShader::ShaderType type, const char *source); + bool addCacheableShaderFromSourceCode(QOpenGLShader::ShaderType type, const QByteArray &source); + bool addCacheableShaderFromSourceCode(QOpenGLShader::ShaderType type, const QString &source); + bool addCacheableShaderFromSourceFile(QOpenGLShader::ShaderType type, const QString &fileName); + void removeAllShaders(); virtual bool link(); diff --git a/src/gui/opengl/qopengltextureblitter.cpp b/src/gui/opengl/qopengltextureblitter.cpp index 858fc0d857..b65df9dc82 100644 --- a/src/gui/opengl/qopengltextureblitter.cpp +++ b/src/gui/opengl/qopengltextureblitter.cpp @@ -330,8 +330,8 @@ bool QOpenGLTextureBlitterPrivate::buildProgram(ProgramIndex idx, const char *vs p->glProgram.reset(new QOpenGLShaderProgram); - p->glProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, vs); - p->glProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, fs); + p->glProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vs); + p->glProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fs); p->glProgram->link(); if (!p->glProgram->isLinked()) { qWarning() << "Could not link shader program:\n" << p->glProgram->log(); diff --git a/src/gui/opengl/qopengltextureglyphcache.cpp b/src/gui/opengl/qopengltextureglyphcache.cpp index 9a7b1eb21d..afd5004cec 100644 --- a/src/gui/opengl/qopengltextureglyphcache.cpp +++ b/src/gui/opengl/qopengltextureglyphcache.cpp @@ -342,22 +342,14 @@ void QOpenGLTextureGlyphCache::resizeTextureData(int width, int height) QString source; source.append(QLatin1String(isCoreProfile ? qopenglslMainWithTexCoordsVertexShader_core : qopenglslMainWithTexCoordsVertexShader)); source.append(QLatin1String(isCoreProfile ? qopenglslUntransformedPositionVertexShader_core : qopenglslUntransformedPositionVertexShader)); - - QOpenGLShader *vertexShader = new QOpenGLShader(QOpenGLShader::Vertex, m_blitProgram); - vertexShader->compileSourceCode(source); - - m_blitProgram->addShader(vertexShader); + m_blitProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, source); } { QString source; source.append(QLatin1String(isCoreProfile ? qopenglslMainFragmentShader_core : qopenglslMainFragmentShader)); source.append(QLatin1String(isCoreProfile ? qopenglslImageSrcFragmentShader_core : qopenglslImageSrcFragmentShader)); - - QOpenGLShader *fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, m_blitProgram); - fragmentShader->compileSourceCode(source); - - m_blitProgram->addShader(fragmentShader); + m_blitProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, source); } m_blitProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri index 86e35c39f8..63e345545c 100644 --- a/src/gui/painting/painting.pri +++ b/src/gui/painting/painting.pri @@ -8,6 +8,7 @@ HEADERS += \ painting/qbrush.h \ painting/qcolor.h \ painting/qcolor_p.h \ + painting/qcolorprofile_p.h \ painting/qcosmeticstroker_p.h \ painting/qdatabuffer_p.h \ painting/qdrawhelper_p.h \ @@ -63,11 +64,11 @@ SOURCES += \ painting/qblittable.cpp \ painting/qbrush.cpp \ painting/qcolor.cpp \ + painting/qcolorprofile.cpp \ painting/qcompositionfunctions.cpp \ painting/qcosmeticstroker.cpp \ painting/qdrawhelper.cpp \ painting/qemulationpaintengine.cpp \ - painting/qgammatables.cpp \ painting/qgrayraster.c \ painting/qimagescale.cpp \ painting/qmatrix.cpp \ diff --git a/src/gui/painting/qgammatables.cpp b/src/gui/painting/qcolorprofile.cpp index 1d76f7ee3c..3b7b0a248b 100644 --- a/src/gui/painting/qgammatables.cpp +++ b/src/gui/painting/qcolorprofile.cpp @@ -37,28 +37,51 @@ ** ****************************************************************************/ -#include <private/qdrawhelper_p.h> +#include "qcolorprofile_p.h" +#include <qmath.h> QT_BEGIN_NAMESPACE +QColorProfile *QColorProfile::fromGamma(qreal gamma) +{ + QColorProfile *cp = new QColorProfile; + + for (int i = 0; i <= (255 * 16); ++i) { + cp->m_toLinear[i] = ushort(qRound(qPow(i / qreal(255 * 16), gamma) * (255 * 256))); + cp->m_fromLinear[i] = ushort(qRound(qPow(i / qreal(255 * 16), qreal(1) / gamma) * (255 * 256))); + } + + return cp; +} -QDrawHelperGammaTables::QDrawHelperGammaTables(qreal smoothing) +static qreal srgbToLinear(qreal v) { - const qreal gray_gamma = 2.31; - for (int i=0; i<256; ++i) - qt_pow_gamma[i] = uint(qRound(qPow(i / qreal(255.), gray_gamma) * 2047)); - for (int i=0; i<2048; ++i) - qt_pow_invgamma[i] = uchar(qRound(qPow(i / qreal(2047.0), 1 / gray_gamma) * 255)); + const qreal a = 0.055; + if (v <= qreal(0.04045)) + return v / qreal(12.92); + else + return qPow((v + a) / (qreal(1) + a), qreal(2.4)); +} - refresh(smoothing); +static qreal linearToSrgb(qreal v) +{ + const qreal a = 0.055; + if (v <= qreal(0.0031308)) + return v * qreal(12.92); + else + return (qreal(1) + a) * qPow(v, qreal(1.0 / 2.4)) - a; } -void QDrawHelperGammaTables::refresh(qreal smoothing) +QColorProfile *QColorProfile::fromSRgb() { - for (int i=0; i<256; ++i) { - qt_pow_rgb_gamma[i] = uchar(qRound(qPow(i / qreal(255.0), smoothing) * 255)); - qt_pow_rgb_invgamma[i] = uchar(qRound(qPow(i / qreal(255.), 1 / smoothing) * 255)); + QColorProfile *cp = new QColorProfile; + + for (int i = 0; i <= (255 * 16); ++i) { + cp->m_toLinear[i] = ushort(qRound(srgbToLinear(i / qreal(255 * 16)) * (255 * 256))); + cp->m_fromLinear[i] = ushort(qRound(linearToSrgb(i / qreal(255 * 16)) * (255 * 256))); } + + return cp; } QT_END_NAMESPACE diff --git a/src/gui/painting/qcolorprofile_p.h b/src/gui/painting/qcolorprofile_p.h new file mode 100644 index 0000000000..ca1786ee6d --- /dev/null +++ b/src/gui/painting/qcolorprofile_p.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOLORPROFILE_P_H +#define QCOLORPROFILE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/private/qtguiglobal_p.h> +#include <QtGui/qrgb.h> +#include <QtGui/qrgba64.h> + +QT_BEGIN_NAMESPACE + +class Q_GUI_EXPORT QColorProfile +{ +public: + static QColorProfile *fromGamma(qreal gamma); + static QColorProfile *fromSRgb(); + + // The following methods all convert opaque or unpremultiplied colors: + + QRgba64 toLinear64(QRgb rgb32) const + { + ushort r = m_toLinear[qRed(rgb32) << 4]; + ushort g = m_toLinear[qGreen(rgb32) << 4]; + ushort b = m_toLinear[qBlue(rgb32) << 4]; + r = r + (r >> 8); + g = g + (g >> 8); + b = b + (b >> 8); + return QRgba64::fromRgba64(r, g, b, qAlpha(rgb32) * 257); + } + + QRgb toLinear(QRgb rgb32) const + { + uchar r = (m_toLinear[qRed(rgb32) << 4] + 0x80) >> 8; + uchar g = (m_toLinear[qGreen(rgb32) << 4] + 0x80) >> 8; + uchar b = (m_toLinear[qBlue(rgb32) << 4] + 0x80) >> 8; + return qRgba(r, g, b, qAlpha(rgb32)); + } + + QRgba64 toLinear(QRgba64 rgb64) const + { + ushort r = rgb64.red(); + ushort g = rgb64.green(); + ushort b = rgb64.blue(); + r = r - (r >> 8); + g = g - (g >> 8); + b = b - (b >> 8); + r = m_toLinear[r >> 4]; + g = m_toLinear[g >> 4]; + b = m_toLinear[b >> 4]; + r = r + (r >> 8); + g = g + (g >> 8); + b = b + (b >> 8); + return QRgba64::fromRgba64(r, g, b, rgb64.alpha()); + } + + QRgb fromLinear64(QRgba64 rgb64) const + { + ushort r = rgb64.red(); + ushort g = rgb64.green(); + ushort b = rgb64.blue(); + r = r - (r >> 8); + g = g - (g >> 8); + b = b - (b >> 8); + r = (m_fromLinear[r >> 4] + 0x80) >> 8; + g = (m_fromLinear[g >> 4] + 0x80) >> 8; + b = (m_fromLinear[b >> 4] + 0x80) >> 8; + return qRgba(r, g, b, rgb64.alpha8()); + } + + QRgb fromLinear(QRgb rgb32) const + { + uchar r = (m_fromLinear[qRed(rgb32) << 4] + 0x80) >> 8; + uchar g = (m_fromLinear[qGreen(rgb32) << 4] + 0x80) >> 8; + uchar b = (m_fromLinear[qBlue(rgb32) << 4] + 0x80) >> 8; + return qRgba(r, g, b, qAlpha(rgb32)); + } + + QRgba64 fromLinear(QRgba64 rgb64) const + { + ushort r = rgb64.red(); + ushort g = rgb64.green(); + ushort b = rgb64.blue(); + r = r - (r >> 8); + g = g - (g >> 8); + b = b - (b >> 8); + r = m_fromLinear[r >> 4]; + g = m_fromLinear[g >> 4]; + b = m_fromLinear[b >> 4]; + r = r + (r >> 8); + g = g + (g >> 8); + b = b + (b >> 8); + return QRgba64::fromRgba64(r, g, b, rgb64.alpha()); + } + +private: + QColorProfile() { } + + // We translate to 0-65280 (255*256) instead to 0-65535 to make simple + // shifting an accurate conversion. + // We translate from 0-4080 (255*16) for the same speed up, and to keep + // the tables small enough to fit in most inner caches. + ushort m_toLinear[(255 * 16) + 1]; // [0-4080] -> [0-65280] + ushort m_fromLinear[(255 * 16) + 1]; // [0-4080] -> [0-65280] + +}; + +QT_END_NAMESPACE + +#endif // QCOLORPROFILE_P_H diff --git a/src/gui/painting/qcoregraphics.mm b/src/gui/painting/qcoregraphics.mm index 466b18d59b..bf0be7f3d3 100644 --- a/src/gui/painting/qcoregraphics.mm +++ b/src/gui/painting/qcoregraphics.mm @@ -39,6 +39,7 @@ #include <QtGui/private/qpaintengine_p.h> #include <QtCore/qdebug.h> #include <QtCore/qcoreapplication.h> +#include <QtCore/qoperatingsystemversion.h> QT_BEGIN_NAMESPACE @@ -119,7 +120,7 @@ QT_END_NAMESPACE + (NSGraphicsContext *)qt_graphicsContextWithCGContext:(CGContextRef)graphicsPort flipped:(BOOL)initialFlippedState { #if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_10, __IPHONE_NA) - if (QT_PREPEND_NAMESPACE(QSysInfo::MacintoshVersion) >= QT_PREPEND_NAMESPACE(QSysInfo::MV_10_10)) + if (QT_PREPEND_NAMESPACE(QOperatingSystemVersion::current()) >= QT_PREPEND_NAMESPACE(QOperatingSystemVersion::OSXYosemite)) return [self graphicsContextWithCGContext:graphicsPort flipped:initialFlippedState]; #endif return [self graphicsContextWithGraphicsPort:graphicsPort flipped:initialFlippedState]; diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index ff7c8dfc28..08f96bd654 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -43,6 +43,7 @@ #include <qstylehints.h> #include <qguiapplication.h> #include <qatomic.h> +#include <private/qcolorprofile_p.h> #include <private/qdrawhelper_p.h> #include <private/qpaintengine_raster_p.h> #include <private/qpainter_p.h> @@ -1319,7 +1320,7 @@ static void QT_FASTCALL destStore(QRasterBuffer *rasterBuffer, int x, int y, con static void QT_FASTCALL convertFromRgb64(uint *dest, const QRgba64 *src, int length) { for (int i = 0; i < length; ++i) { - dest[i] = src[i].toArgb32(); + dest[i] = toArgb32(src[i]); } } @@ -1410,7 +1411,7 @@ static void QT_FASTCALL destStore64ARGB32(QRasterBuffer *rasterBuffer, int x, in { uint *dest = (uint*)rasterBuffer->scanLine(y) + x; for (int i = 0; i < length; ++i) { - dest[i] = buffer[i].unpremultiplied().toArgb32(); + dest[i] = toArgb32(buffer[i].unpremultiplied()); } } @@ -1418,7 +1419,7 @@ static void QT_FASTCALL destStore64RGBA8888(QRasterBuffer *rasterBuffer, int x, { uint *dest = (uint*)rasterBuffer->scanLine(y) + x; for (int i = 0; i < length; ++i) { - dest[i] = ARGB2RGBA(buffer[i].unpremultiplied().toArgb32()); + dest[i] = toRgba8888(buffer[i].unpremultiplied()); } } @@ -5194,6 +5195,8 @@ void qBlendTexture(int count, const QSpan *spans, void *userData) case QImage::Format_RGB16: proc = processTextureSpansRGB16[blendType]; break; + case QImage::Format_ARGB32: + case QImage::Format_RGBA8888: case QImage::Format_BGR30: case QImage::Format_A2BGR30_Premultiplied: case QImage::Format_RGB30: @@ -5435,80 +5438,27 @@ static void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer, } } -static inline void rgbBlendPixel(quint32 *dst, int coverage, int sr, int sg, int sb, const uchar *gamma, const uchar *invgamma) +static inline void rgbBlendPixel(quint32 *dst, int coverage, QRgba64 slinear, const QColorProfile *colorProfile) { - // Do a gray alphablend... - int da = qAlpha(*dst); - int dr = qRed(*dst); - int dg = qGreen(*dst); - int db = qBlue(*dst); + // Do a gammacorrected RGB alphablend... + const QRgba64 dlinear = colorProfile->toLinear64(*dst); - if (da != 255 - ) { + QRgba64 blend = rgbBlend(dlinear, slinear, coverage); - int a = qGray(coverage); - sr = qt_div_255(invgamma[sr] * a); - sg = qt_div_255(invgamma[sg] * a); - sb = qt_div_255(invgamma[sb] * a); - - int ia = 255 - a; - dr = qt_div_255(dr * ia); - dg = qt_div_255(dg * ia); - db = qt_div_255(db * ia); - - *dst = ((a + qt_div_255((255 - a) * da)) << 24) - | ((sr + dr) << 16) - | ((sg + dg) << 8) - | ((sb + db)); - return; - } - - int mr = qRed(coverage); - int mg = qGreen(coverage); - int mb = qBlue(coverage); - - dr = gamma[dr]; - dg = gamma[dg]; - db = gamma[db]; - - int nr = qt_div_255(sr * mr + dr * (255 - mr)); - int ng = qt_div_255(sg * mg + dg * (255 - mg)); - int nb = qt_div_255(sb * mb + db * (255 - mb)); - - nr = invgamma[nr]; - ng = invgamma[ng]; - nb = invgamma[nb]; - - *dst = qRgb(nr, ng, nb); + *dst = colorProfile->fromLinear64(blend); } -#if defined(Q_OS_WIN) Q_GUI_EXPORT bool qt_needs_a8_gamma_correction = false; -static inline void grayBlendPixel(quint32 *dst, int coverage, int sr, int sg, int sb, const uint *gamma, const uchar *invgamma) +static inline void grayBlendPixel(quint32 *dst, int coverage, QRgba64 slinear, const QColorProfile *colorProfile) { // Do a gammacorrected gray alphablend... - int dr = qRed(*dst); - int dg = qGreen(*dst); - int db = qBlue(*dst); - - dr = gamma[dr]; - dg = gamma[dg]; - db = gamma[db]; + const QRgba64 dlinear = colorProfile->toLinear64(*dst); - int alpha = coverage; - int ialpha = 255 - alpha; - int nr = qt_div_255(sr * alpha + dr * ialpha); - int ng = qt_div_255(sg * alpha + dg * ialpha); - int nb = qt_div_255(sb * alpha + db * ialpha); + QRgba64 blend = interpolate255(slinear, coverage, dlinear, 255 - coverage); - nr = invgamma[nr]; - ng = invgamma[ng]; - nb = invgamma[nb]; - - *dst = qRgb(nr, ng, nb); + *dst = colorProfile->fromLinear64(blend); } -#endif static void qt_alphamapblit_uint32(QRasterBuffer *rasterBuffer, int x, int y, quint32 color, @@ -5519,21 +5469,14 @@ static void qt_alphamapblit_uint32(QRasterBuffer *rasterBuffer, const quint32 c = color; const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint32); -#if defined(Q_OS_WIN) - const QDrawHelperGammaTables *tables = QGuiApplicationPrivate::instance()->gammaTables(); - if (!tables) + const QColorProfile *colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text(); + if (!colorProfile) return; - const uint *gamma = tables->qt_pow_gamma; - const uchar *invgamma = tables->qt_pow_invgamma; - - int sr = gamma[qRed(color)]; - int sg = gamma[qGreen(color)]; - int sb = gamma[qBlue(color)]; + const QRgba64 slinear = colorProfile->toLinear64(c); bool opaque_src = (qAlpha(color) == 255); bool doGrayBlendPixel = opaque_src && qt_needs_a8_gamma_correction; -#endif if (!clip) { quint32 *dest = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x; @@ -5546,13 +5489,9 @@ static void qt_alphamapblit_uint32(QRasterBuffer *rasterBuffer, } else if (coverage == 255) { dest[i] = c; } else { -#if defined(Q_OS_WIN) - if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP && doGrayBlendPixel - && qAlpha(dest[i]) == 255) { - grayBlendPixel(dest+i, coverage, sr, sg, sb, gamma, invgamma); - } else -#endif - { + if (doGrayBlendPixel && qAlpha(dest[i]) == 255) { + grayBlendPixel(dest+i, coverage, slinear, colorProfile); + } else { int ialpha = 255 - coverage; dest[i] = INTERPOLATE_PIXEL_255(c, coverage, dest[i], ialpha); } @@ -5587,13 +5526,9 @@ static void qt_alphamapblit_uint32(QRasterBuffer *rasterBuffer, } else if (coverage == 255) { dest[xp] = c; } else { -#if defined(Q_OS_WIN) - if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP && doGrayBlendPixel - && qAlpha(dest[xp]) == 255) { - grayBlendPixel(dest+xp, coverage, sr, sg, sb, gamma, invgamma); - } else -#endif - { + if (doGrayBlendPixel && qAlpha(dest[xp]) == 255) { + grayBlendPixel(dest+xp, coverage, slinear, colorProfile); + } else { int ialpha = 255 - coverage; dest[xp] = INTERPOLATE_PIXEL_255(c, coverage, dest[xp], ialpha); } @@ -5627,6 +5562,11 @@ static void qt_alphamapblit_rgba8888(QRasterBuffer *rasterBuffer, } #endif +inline static int qRgbAvg(QRgb rgb) +{ + return (qRed(rgb) * 5 + qGreen(rgb) * 6 + qBlue(rgb) * 5) / 16; +} + static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 &color, const uint *src, int mapWidth, int mapHeight, int srcStride, @@ -5634,21 +5574,13 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer, { const quint32 c = color.toArgb32(); - int sr = qRed(c); - int sg = qGreen(c); - int sb = qBlue(c); int sa = qAlpha(c); - const QDrawHelperGammaTables *tables = QGuiApplicationPrivate::instance()->gammaTables(); - if (!tables) + const QColorProfile *colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text(); + if (!colorProfile) return; - const uchar *gamma = tables->qt_pow_rgb_gamma; - const uchar *invgamma = tables->qt_pow_rgb_invgamma; - - sr = gamma[sr]; - sg = gamma[sg]; - sb = gamma[sb]; + const QRgba64 slinear = colorProfile->toLinear64(c); if (sa == 0) return; @@ -5662,7 +5594,13 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer, if (coverage == 0xffffffff) { dst[i] = c; } else if (coverage != 0xff000000) { - rgbBlendPixel(dst+i, coverage, sr, sg, sb, gamma, invgamma); + if (dst[i] >= 0xff000000) { + rgbBlendPixel(dst+i, coverage, slinear, colorProfile); + } else { + // Give up and do a naive blend. + const int a = qRgbAvg(coverage); + dst[i] = INTERPOLATE_PIXEL_255(c, a, dst[i], 255 - a); + } } } @@ -5692,7 +5630,13 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer, if (coverage == 0xffffffff) { dst[xp] = c; } else if (coverage != 0xff000000) { - rgbBlendPixel(dst+xp, coverage, sr, sg, sb, gamma, invgamma); + if (dst[xp] >= 0xff000000) { + rgbBlendPixel(dst+xp, coverage, slinear, colorProfile); + } else { + // Give up and do a naive blend. + const int a = qRgbAvg(coverage); + dst[xp] = INTERPOLATE_PIXEL_255(c, a, dst[xp], 255 - coverage); + } } } } // for (i -> line.count) diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index e537c343bb..933da56095 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -157,7 +157,6 @@ struct DrawHelper { extern SrcOverBlendFunc qBlendFunctions[QImage::NImageFormats][QImage::NImageFormats]; extern SrcOverScaleFunc qScaleFunctions[QImage::NImageFormats][QImage::NImageFormats]; extern SrcOverTransformFunc qTransformFunctions[QImage::NImageFormats][QImage::NImageFormats]; -extern MemRotateFunc qMemRotateFunctions[QImage::NImageFormats][3]; extern DrawHelper qDrawHelper[QImage::NImageFormats]; @@ -345,18 +344,6 @@ struct QSpanData void adjustSpanMethods(); }; -struct QDrawHelperGammaTables -{ - explicit QDrawHelperGammaTables(qreal smoothing); - - void refresh(qreal smoothing); - - uchar qt_pow_rgb_gamma[256]; - uchar qt_pow_rgb_invgamma[256]; - uint qt_pow_gamma[256]; - uchar qt_pow_invgamma[2048]; -}; - static inline uint qt_gradient_clamp(const QGradientData *data, int ipos) { if (ipos < 0 || ipos >= GRADIENT_STOPTABLE_SIZE) { @@ -1238,6 +1225,7 @@ extern QPixelLayout qPixelLayouts[QImage::NImageFormats]; extern const FetchPixelsFunc qFetchPixels[QPixelLayout::BPPCount]; extern StorePixelsFunc qStorePixels[QPixelLayout::BPPCount]; +extern MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3]; QT_END_NAMESPACE diff --git a/src/gui/painting/qmemrotate.cpp b/src/gui/painting/qmemrotate.cpp index 3fbae76de5..25aa6a3122 100644 --- a/src/gui/painting/qmemrotate.cpp +++ b/src/gui/painting/qmemrotate.cpp @@ -41,164 +41,10 @@ QT_BEGIN_NAMESPACE -#if QT_ROTATION_ALGORITHM == QT_ROTATION_TILED static const int tileSize = 32; -#endif - -#if Q_BYTE_ORDER == Q_BIG_ENDIAN -#if QT_ROTATION_ALGORITHM == QT_ROTATION_PACKED || QT_ROTATION_ALGORITHM == QT_ROTATION_TILED -#error Big endian version not implemented for the transformed driver! -#endif -#endif - -template <class T> -Q_STATIC_TEMPLATE_FUNCTION -inline void qt_memrotate90_cachedRead(const T *src, int w, int h, int sstride, T *dest, - int dstride) -{ - const char *s = reinterpret_cast<const char*>(src); - char *d = reinterpret_cast<char*>(dest); - for (int y = 0; y < h; ++y) { - for (int x = w - 1; x >= 0; --x) { - T *destline = reinterpret_cast<T *>(d + (w - x - 1) * dstride); - destline[y] = src[x]; - } - s += sstride; - src = reinterpret_cast<const T*>(s); - } -} template <class T> Q_STATIC_TEMPLATE_FUNCTION -inline void qt_memrotate270_cachedRead(const T *src, int w, int h, int sstride, T *dest, - int dstride) -{ - const char *s = reinterpret_cast<const char*>(src); - char *d = reinterpret_cast<char*>(dest); - s += (h - 1) * sstride; - for (int y = h - 1; y >= 0; --y) { - src = reinterpret_cast<const T*>(s); - for (int x = 0; x < w; ++x) { - T *destline = reinterpret_cast<T *>(d + x * dstride); - destline[h - y - 1] = src[x]; - } - s -= sstride; - } -} - -#if QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDWRITE - -template <class T> -Q_STATIC_TEMPLATE_FUNCTION -inline void qt_memrotate90_cachedWrite(const T *src, int w, int h, int sstride, T *dest, - int dstride) -{ - for (int x = w - 1; x >= 0; --x) { - T *d = dest + (w - x - 1) * dstride; - for (int y = 0; y < h; ++y) { - *d++ = src[y * sstride + x]; - } - } - -} - -template <class T> -Q_STATIC_TEMPLATE_FUNCTION -inline void qt_memrotate270_cachedWrite(const T *src, int w, int h, int sstride, T *dest, - int dstride) -{ - for (int x = 0; x < w; ++x) { - T *d = dest + x * dstride; - for (int y = h - 1; y >= 0; --y) { - *d++ = src[y * sstride + x]; - } - } -} - -#endif // QT_ROTATION_CACHEDWRITE - -#if QT_ROTATION_ALGORITHM == QT_ROTATION_PACKING - -// TODO: packing algorithms should probably be modified on 64-bit architectures - -template <class T> -Q_STATIC_TEMPLATE_FUNCTION -inline void qt_memrotate90_packing(const T *src, int w, int h, int sstride, T *dest, int dstride) -{ - sstride /= sizeof(T); - dstride /= sizeof(T); - - const int pack = sizeof(quint32) / sizeof(T); - const int unaligned = int((long(dest) & (sizeof(quint32)-1))) / sizeof(T); - - for (int x = w - 1; x >= 0; --x) { - int y = 0; - - for (int i = 0; i < unaligned; ++i) { - dest[(w - x - 1) * dstride + y] = src[y * sstride + x]; - ++y; - } - - quint32 *d = reinterpret_cast<quint32*>(dest + (w - x - 1) * dstride - + unaligned); - const int rest = (h - unaligned) % pack; - while (y < h - rest) { - quint32 c = src[y * sstride + x]; - for (int i = 1; i < pack; ++i) { - c |= src[(y + i) * sstride + x] << (sizeof(int) * 8 / pack * i); - } - *d++ = c; - y += pack; - } - - while (y < h) { - dest[(w - x - 1) * dstride + y] = src[y * sstride + x]; - ++y; - } - } -} - -template <class T> -Q_STATIC_TEMPLATE_FUNCTION -inline void qt_memrotate270_packing(const T *src, int w, int h, int sstride, T *dest, int dstride) -{ - sstride /= sizeof(T); - dstride /= sizeof(T); - - const int pack = sizeof(quint32) / sizeof(T); - const int unaligned = int((long(dest) & (sizeof(quint32)-1))) / sizeof(T); - - for (int x = 0; x < w; ++x) { - int y = h - 1; - - for (int i = 0; i < unaligned; ++i) { - dest[x * dstride + h - y - 1] = src[y * sstride + x]; - --y; - } - - quint32 *d = reinterpret_cast<quint32*>(dest + x * dstride - + unaligned); - const int rest = (h - unaligned) % pack; - while (y > rest) { - quint32 c = src[y * sstride + x]; - for (int i = 1; i < pack; ++i) { - c |= src[(y - i) * sstride + x] << (sizeof(int) * 8 / pack * i); - } - *d++ = c; - y -= pack; - } - while (y >= 0) { - dest[x * dstride + h - y - 1] = src[y * sstride + x]; - --y; - } - } -} - -#endif // QT_ROTATION_PACKING - -#if QT_ROTATION_ALGORITHM == QT_ROTATION_TILED -template <class T> -Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate90_tiled(const T *src, int w, int h, int sstride, T *dest, int dstride) { sstride /= sizeof(T); @@ -235,7 +81,7 @@ inline void qt_memrotate90_tiled(const T *src, int w, int h, int sstride, T *des for (int y = starty; y < stopy; y += pack) { quint32 c = src[y * sstride + x]; for (int i = 1; i < pack; ++i) { - const int shift = (sizeof(int) * 8 / pack * i); + const int shift = (sizeof(T) * 8 * i); const T color = src[(y + i) * sstride + x]; c |= color << shift; } @@ -293,7 +139,7 @@ inline void qt_memrotate270_tiled(const T *src, int w, int h, int sstride, T *de const int pack = sizeof(quint32) / sizeof(T); const int unaligned = - qMin(uint((long(dest) & (sizeof(quint32)-1)) / sizeof(T)), uint(h)); + qMin(uint((quintptr(dest) & (sizeof(quint32)-1)) / sizeof(T)), uint(h)); const int restX = w % tileSize; const int restY = (h - unaligned) % tileSize; const int unoptimizedY = restY % pack; @@ -320,10 +166,10 @@ inline void qt_memrotate270_tiled(const T *src, int w, int h, int sstride, T *de for (int x = startx; x < stopx; ++x) { quint32 *d = reinterpret_cast<quint32*>(dest + x * dstride + h - 1 - starty); - for (int y = starty; y > stopy; y -= pack) { + for (int y = starty; y >= stopy; y -= pack) { quint32 c = src[y * sstride + x]; for (int i = 1; i < pack; ++i) { - const int shift = (sizeof(int) * 8 / pack * i); + const int shift = (sizeof(T) * 8 * i); const T color = src[(y - i) * sstride + x]; c |= color << shift; } @@ -371,22 +217,26 @@ inline void qt_memrotate270_tiled_unpacked(const T *src, int w, int h, int sstri } } -#endif // QT_ROTATION_ALGORITHM template <class T> Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate90_template(const T *src, int srcWidth, int srcHeight, int srcStride, T *dest, int dstStride) { -#if QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDREAD - qt_memrotate90_cachedRead<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); -#elif QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDWRITE - qt_memrotate90_cachedWrite<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); -#elif QT_ROTATION_ALGORITHM == QT_ROTATION_PACKING - qt_memrotate90_packing<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); -#elif QT_ROTATION_ALGORITHM == QT_ROTATION_TILED - qt_memrotate90_tiled<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + // packed algorithm assumes little endian and that sizeof(quint32)/sizeof(T) is an integer + if (sizeof(quint32) % sizeof(T) == 0) + qt_memrotate90_tiled<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); + else #endif + qt_memrotate90_tiled_unpacked<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); +} + +template <> +inline void qt_memrotate90_template<quint32>(const quint32 *src, int w, int h, int sstride, quint32 *dest, int dstride) +{ + // packed algorithm doesn't have any benefit for quint32 + qt_memrotate90_tiled_unpacked(src, w, h, sstride, dest, dstride); } template <class T> @@ -394,11 +244,11 @@ Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate180_template(const T *src, int w, int h, int sstride, T *dest, int dstride) { const char *s = (const char*)(src) + (h - 1) * sstride; - for (int y = h - 1; y >= 0; --y) { - T *d = reinterpret_cast<T*>((char *)(dest) + (h - y - 1) * dstride); + for (int dy = 0; dy < h; ++dy) { + T *d = reinterpret_cast<T*>((char *)(dest) + dy * dstride); src = reinterpret_cast<const T*>(s); - for (int x = w - 1; x >= 0; --x) { - d[w - x - 1] = src[x]; + for (int dx = 0; dx < w; ++dx) { + d[dx] = src[w - 1 - dx]; } s -= sstride; } @@ -409,32 +259,20 @@ Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate270_template(const T *src, int srcWidth, int srcHeight, int srcStride, T *dest, int dstStride) { -#if QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDREAD - qt_memrotate270_cachedRead<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); -#elif QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDWRITE - qt_memrotate270_cachedWrite<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); -#elif QT_ROTATION_ALGORITHM == QT_ROTATION_PACKING - qt_memrotate270_packing<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); -#elif QT_ROTATION_ALGORITHM == QT_ROTATION_TILED - qt_memrotate270_tiled_unpacked<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + // packed algorithm assumes little endian and that sizeof(quint32)/sizeof(T) is an integer + if (sizeof(quint32) % sizeof(T) == 0) + qt_memrotate270_tiled<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); + else #endif + qt_memrotate270_tiled_unpacked<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); } template <> -inline void qt_memrotate90_template<quint24>(const quint24 *src, int srcWidth, int srcHeight, - int srcStride, quint24 *dest, int dstStride) +inline void qt_memrotate270_template<quint32>(const quint32 *src, int w, int h, int sstride, quint32 *dest, int dstride) { -#if QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDREAD - qt_memrotate90_cachedRead<quint24>(src, srcWidth, srcHeight, srcStride, dest, dstStride); -#elif QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDWRITE - qt_memrotate90_cachedWrite<quint24>(src, srcWidth, srcHeight, srcStride, dest, dstStride); -#elif QT_ROTATION_ALGORITHM == QT_ROTATION_PACKING - // packed algorithm not implemented - qt_memrotate90_cachedRead<quint24>(src, srcWidth, srcHeight, srcStride, dest, dstStride); -#elif QT_ROTATION_ALGORITHM == QT_ROTATION_TILED - // packed algorithm not implemented - qt_memrotate90_tiled_unpacked<quint24>(src, srcWidth, srcHeight, srcStride, dest, dstStride); -#endif + // packed algorithm doesn't have any benefit for quint32 + qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride); } #define QT_IMPL_MEMROTATE(type) \ @@ -458,7 +296,7 @@ Q_GUI_EXPORT void qt_memrotate270(const type *src, int w, int h, int sstride, \ Q_GUI_EXPORT void qt_memrotate90(const type *src, int w, int h, int sstride, \ type *dest, int dstride) \ { \ - qt_memrotate90_tiled_unpacked<type>(src, w, h, sstride, dest, dstride); \ + qt_memrotate90_tiled_unpacked(src, w, h, sstride, dest, dstride); \ } \ Q_GUI_EXPORT void qt_memrotate180(const type *src, int w, int h, int sstride, \ type *dest, int dstride) \ @@ -468,7 +306,7 @@ Q_GUI_EXPORT void qt_memrotate180(const type *src, int w, int h, int sstride, \ Q_GUI_EXPORT void qt_memrotate270(const type *src, int w, int h, int sstride, \ type *dest, int dstride) \ { \ - qt_memrotate270_tiled_unpacked<type>(src, w, h, sstride, dest, dstride); \ + qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride); \ } @@ -509,6 +347,21 @@ void qt_memrotate270_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *d qt_memrotate270((const ushort *)srcPixels, w, h, sbpl, (ushort *)destPixels, dbpl); } +void qt_memrotate90_24(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) +{ + qt_memrotate90((const quint24 *)srcPixels, w, h, sbpl, (quint24 *)destPixels, dbpl); +} + +void qt_memrotate180_24(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) +{ + qt_memrotate180((const quint24 *)srcPixels, w, h, sbpl, (quint24 *)destPixels, dbpl); +} + +void qt_memrotate270_24(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) +{ + qt_memrotate270((const quint24 *)srcPixels, w, h, sbpl, (quint24 *)destPixels, dbpl); +} + void qt_memrotate90_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) { qt_memrotate90((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl); @@ -524,34 +377,16 @@ void qt_memrotate270_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *d qt_memrotate270((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl); } -MemRotateFunc qMemRotateFunctions[QImage::NImageFormats][3] = +MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3] = // 90, 180, 270 { - { 0, 0, 0 }, // Format_Invalid, - { 0, 0, 0 }, // Format_Mono, - { 0, 0, 0 }, // Format_MonoLSB, - { 0, 0, 0 }, // Format_Indexed8, - { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // Format_RGB32, - { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // Format_ARGB32, - { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // Format_ARGB32_Premultiplied, - { qt_memrotate90_16, qt_memrotate180_16, qt_memrotate270_16 }, // Format_RGB16, - { 0, 0, 0 }, // Format_ARGB8565_Premultiplied, - { 0, 0, 0 }, // Format_RGB666, - { 0, 0, 0 }, // Format_ARGB6666_Premultiplied, - { 0, 0, 0 }, // Format_RGB555, - { 0, 0, 0 }, // Format_ARGB8555_Premultiplied, - { 0, 0, 0 }, // Format_RGB888, - { 0, 0, 0 }, // Format_RGB444, - { 0, 0, 0 }, // Format_ARGB4444_Premultiplied, - { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // Format_RGBX8888, - { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // Format_RGBA8888, - { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // Format_RGBA8888_Premultiplied, - { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // Format_BGB30, - { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // Format_A2BGR30_Premultiplied, - { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // Format_RGB30, - { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // Format_A2RGB30_Premultiplied, - { qt_memrotate90_8, qt_memrotate180_8, qt_memrotate270_8 }, // Format_Alpha8, - { qt_memrotate90_8, qt_memrotate180_8, qt_memrotate270_8 }, // Format_Grayscale8, + { 0, 0, 0 }, // BPPNone, + { 0, 0, 0 }, // BPP1MSB, + { 0, 0, 0 }, // BPP1LSB, + { qt_memrotate90_8, qt_memrotate180_8, qt_memrotate270_8 }, // BPP8, + { qt_memrotate90_16, qt_memrotate180_16, qt_memrotate270_16 }, // BPP16, + { qt_memrotate90_24, qt_memrotate180_24, qt_memrotate270_24 }, // BPP24 + { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // BPP32 }; QT_END_NAMESPACE diff --git a/src/gui/painting/qmemrotate_p.h b/src/gui/painting/qmemrotate_p.h index 62613d301a..9bc3fd1010 100644 --- a/src/gui/painting/qmemrotate_p.h +++ b/src/gui/painting/qmemrotate_p.h @@ -56,19 +56,6 @@ QT_BEGIN_NAMESPACE -#define QT_ROTATION_CACHEDREAD 1 -#define QT_ROTATION_CACHEDWRITE 2 -#define QT_ROTATION_PACKING 3 -#define QT_ROTATION_TILED 4 - -#ifndef QT_ROTATION_ALGORITHM -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN -#define QT_ROTATION_ALGORITHM QT_ROTATION_TILED -#else -#define QT_ROTATION_ALGORITHM QT_ROTATION_CACHEDREAD -#endif -#endif - #define QT_DECL_MEMROTATE(type) \ void Q_GUI_EXPORT qt_memrotate90(const type*, int, int, int, type*, int); \ void Q_GUI_EXPORT qt_memrotate180(const type*, int, int, int, type*, int); \ diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index df96a993e3..647bd5cb2c 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -272,6 +272,35 @@ static void qt_debug_path(const QPainterPath &path) } #endif +// QRect::normalized() will change the width/height of the rectangle due to +// its incusive-integer definition of left/right vs width. This is not +// something we want to change in QRect as that would potentially introduce +// regressions all over the place, so we implement a straightforward +// normalized here. QRectF already does this, so QRectF::normalized() is ok to +// use. +static QRect qrect_normalized(const QRect &rect) +{ + int x, y, w, h; + if (Q_UNLIKELY(rect.width() < 0)) { + x = rect.x() + rect.width(); + w = -rect.width(); + } else { + x = rect.x(); + w = rect.width(); + } + + if (Q_UNLIKELY(rect.height() < 0)) { + y = rect.y() + rect.height(); + h = -rect.height(); + } else { + y = rect.y(); + h = rect.height(); + } + + return QRect(x, y, w, h); +} + + QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() : QPaintEngineExPrivate(), cachedLines(0) @@ -1236,7 +1265,9 @@ void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op) bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op) { Q_D(QRasterPaintEngine); - QRect clipRect = r & d->deviceRect; + // normalize before using the & operator which uses QRect::normalize() + // internally which will give us the wrong values. + QRect clipRect = qrect_normalized(r) & d->deviceRect; QRasterPaintEngineState *s = state(); if (op == Qt::ReplaceClip || s->clip == 0) { @@ -1471,7 +1502,7 @@ void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount) int offset_x = int(s->matrix.dx()); int offset_y = int(s->matrix.dy()); while (r < lastRect) { - QRect rect = r->normalized(); + QRect rect = qrect_normalized(*r); QRect rr = rect.translated(offset_x, offset_y); fillRect_normalized(rr, &s->brushData, d); ++r; @@ -2266,8 +2297,9 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source))) { RotationType rotationType = qRotationType(s->matrix); + const QPixelLayout::BPP plBpp = qPixelLayouts[d->rasterBuffer->format].bpp; - if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) { + if (rotationType != NoRotation && qMemRotateFunctions[plBpp][rotationType] && img.rect().contains(sr.toAlignedRect())) { QRectF transformedTargetRect = s->matrix.mapRect(r); if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing)) @@ -2297,7 +2329,7 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe uint cw = clippedSourceRect.width(); uint ch = clippedSourceRect.height(); - qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl); + qMemRotateFunctions[plBpp][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl); return; } @@ -2500,7 +2532,7 @@ void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, QRectF rr = r; rr.translate(s->matrix.dx(), s->matrix.dy()); - fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d); + fillRect_normalized(rr.normalized().toRect(), &d->image_filler, d); } } @@ -2880,7 +2912,7 @@ bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect, const QRasterPaintEngineState *s = q->state(); const QClipData *cl = clip(); if (!cl) { - QRect r = rect.normalized(); + QRect r = qrect_normalized(rect); // inline contains() for performance (we know the rects are normalized) const QRect &r1 = deviceRect; return (r.left() >= r1.left() && r.right() <= r1.right() @@ -2895,7 +2927,7 @@ bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect, if (s->flags.antialiased) ++penWidth; - QRect r = rect.normalized(); + QRect r = qrect_normalized(rect); if (penWidth > 0) { r.setX(r.x() - penWidth); r.setY(r.y() - penWidth); diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp index 84e18a64dd..7b8bae1642 100644 --- a/src/gui/painting/qpdf.cpp +++ b/src/gui/painting/qpdf.cpp @@ -1504,16 +1504,25 @@ void QPdfEnginePrivate::writeInfo() printString(creator); xprintf("\n/Producer "); printString(QString::fromLatin1("Qt " QT_VERSION_STR)); - QDateTime now = QDateTime::currentDateTimeUtc(); + QDateTime now = QDateTime::currentDateTime(); QTime t = now.time(); QDate d = now.date(); - xprintf("\n/CreationDate (D:%d%02d%02d%02d%02d%02d)\n", + xprintf("\n/CreationDate (D:%d%02d%02d%02d%02d%02d", d.year(), d.month(), d.day(), t.hour(), t.minute(), t.second()); + int offset = now.offsetFromUtc(); + int hours = (offset / 60) / 60; + int mins = (offset / 60) % 60; + if (offset < 0) + xprintf("-%02d'%02d')\n", -hours, -mins); + else if (offset > 0) + xprintf("+%02d'%02d')\n", hours , mins); + else + xprintf("Z)\n"); xprintf(">>\n" "endobj\n"); } diff --git a/src/gui/painting/qrgba64_p.h b/src/gui/painting/qrgba64_p.h index 0dadc038fa..91b5926e43 100644 --- a/src/gui/painting/qrgba64_p.h +++ b/src/gui/painting/qrgba64_p.h @@ -185,6 +185,55 @@ inline QRgba64 addWithSaturation(QRgba64 a, QRgba64 b) qMin(a.alpha() + b.alpha(), 65535)); } +#if defined __SSE2__ +Q_ALWAYS_INLINE uint toArgb32(__m128i v) +{ + v = _mm_unpacklo_epi16(v, _mm_setzero_si128()); + v = _mm_add_epi32(v, _mm_set1_epi32(128)); + v = _mm_sub_epi32(v, _mm_srli_epi32(v, 8)); + v = _mm_srli_epi32(v, 8); + v = _mm_packs_epi32(v, v); + v = _mm_packus_epi16(v, v); + return _mm_cvtsi128_si32(v); +} +#elif defined __ARM_NEON__ +Q_ALWAYS_INLINE uint toArgb32(uint16x4_t v) +{ + v = vsub_u16(v, vrshr_n_u16(v, 8)); + v = vrshr_n_u16(v, 8); + uint8x8_t v8 = vmovn_u16(vcombine_u16(v, v)); + return vget_lane_u32(vreinterpret_u32_u8(v8), 0); +} +#endif + +inline uint toArgb32(QRgba64 rgba64) +{ +#if defined __SSE2__ + __m128i v = _mm_loadl_epi64((const __m128i *)&rgba64); + v = _mm_shufflelo_epi16(v, _MM_SHUFFLE(3, 0, 1, 2)); + return toArgb32(v); +#elif defined __ARM_NEON__ + uint16x4_t v = vreinterpret_u16_u64(vld1_u64(reinterpret_cast<const uint64_t *>(&rgba64))); + v = vext_u16(v, v, 1); + return toArgb32(v); +#else + return rgba64.toArgb32(); +#endif +} + +inline uint toRgba8888(QRgba64 rgba64) +{ +#if defined __SSE2__ + __m128i v = _mm_loadl_epi64((const __m128i *)&rgba64); + return toArgb32(v); +#elif defined __ARM_NEON__ + uint16x4_t v = vreinterpret_u16_u64(vld1_u64(reinterpret_cast<const uint64_t *>(&rgba64))); + return toArgb32(v); +#else + return ARGB2RGBA(toArgb32(rgba64)); +#endif +} + #if defined(__SSE2__) Q_ALWAYS_INLINE __m128i addWithSaturation(__m128i a, __m128i b) { @@ -199,6 +248,52 @@ Q_ALWAYS_INLINE uint16x4_t addWithSaturation(uint16x4_t a, uint16x4_t b) } #endif +inline QRgba64 rgbBlend(QRgba64 d, QRgba64 s, uint rgbAlpha) +{ + QRgba64 blend; +#ifdef __SSE2__ + __m128i vd = _mm_loadl_epi64((const __m128i *)&d); + __m128i vs = _mm_loadl_epi64((const __m128i *)&s); + __m128i va = _mm_cvtsi32_si128(rgbAlpha); + va = _mm_unpacklo_epi8(va, va); + __m128i vb = _mm_xor_si128(_mm_set1_epi16(-1), va); + + vs = _mm_unpacklo_epi16(_mm_mullo_epi16(vs, va), _mm_mulhi_epu16(vs, va)); + vd = _mm_unpacklo_epi16(_mm_mullo_epi16(vd, vb), _mm_mulhi_epu16(vd, vb)); + vd = _mm_add_epi32(vd, vs); + vd = _mm_add_epi32(vd, _mm_srli_epi32(vd, 16)); + vd = _mm_add_epi32(vd, _mm_set1_epi32(0x8000)); + vd = _mm_srai_epi32(vd, 16); + vd = _mm_packs_epi32(vd, _mm_setzero_si128()); + + _mm_storel_epi64((__m128i *)&blend, vd); +#elif defined(__ARM_NEON__) + uint16x4_t vd = vreinterpret_u16_u64(vmov_n_u64(d)); + uint16x4_t vs = vreinterpret_u16_u64(vmov_n_u64(s)); + uint8x8_t va8 = vreinterpret_u8_u32(vmov_n_u32(rgbAlpha)); + uint16x4_t va = vreinterpret_u16_u8(vzip_u8(va8, va8).val[0]); + uint16x4_t vb = vdup_n_u16(0xffff); + vb = vsub_u16(vb, va); + + uint32x4_t vs32 = vmull_u16(vs, va); + uint32x4_t vd32 = vmull_u16(vd, vb); + vd32 = vaddq_u32(vd32, vs32); + vd32 = vsraq_n_u32(vd32, vd32, 16); + vd = vrshrn_n_u32(vd32, 16); + vst1_u64(reinterpret_cast<uint64_t *>(&blend), vreinterpret_u64_u16(vd)); +#else + const int mr = qRed(rgbAlpha); + const int mg = qGreen(rgbAlpha); + const int mb = qBlue(rgbAlpha); + blend.setRed (qt_div_255(s.red() * mr + d.red() * (255 - mr))); + blend.setGreen(qt_div_255(s.green() * mg + d.green() * (255 - mg))); + blend.setBlue (qt_div_255(s.blue() * mb + d.blue() * (255 - mb))); + blend.setAlpha(s.alpha()); +#endif + return blend; +} + + QT_END_NAMESPACE #endif // QRGBA64_P_H diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 0d05fee6ef..5e9fac5f86 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -1145,9 +1145,28 @@ void QTextDocument::setMetaInformation(MetaInformation info, const QString &stri } /*! + Returns the raw text contained in the document without any + formatting information. If you want formatting information + use a QTextCursor instead. + + \since 5.9 + \sa toPlainText() +*/ +QString QTextDocument::toRawText() const +{ + Q_D(const QTextDocument); + return d->plainText(); +} + +/*! Returns the plain text contained in the document. If you want formatting information use a QTextCursor instead. + This function returns the same as toRawText(), but will replace + some unicode characters, such as line separators and non-breaking + spaces, with ASCII alternatives. If you need the precise contents + of the document, use toRawText() instead. + \note Embedded objects, such as images, are represented by a Unicode value U+FFFC (OBJECT REPLACEMENT CHARACTER). diff --git a/src/gui/text/qtextdocument.h b/src/gui/text/qtextdocument.h index 1888088f0d..c2761a39b9 100644 --- a/src/gui/text/qtextdocument.h +++ b/src/gui/text/qtextdocument.h @@ -151,6 +151,7 @@ public: void setHtml(const QString &html); #endif + QString toRawText() const; QString toPlainText() const; void setPlainText(const QString &text); diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index d4c43b3069..269e505a56 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -329,17 +329,17 @@ bool operator<(const QTextHtmlEntity &entity1, const QTextHtmlEntity &entity2) } #endif -static bool operator<(const QString &entityStr, const QTextHtmlEntity &entity) +static bool operator<(const QStringRef &entityStr, const QTextHtmlEntity &entity) { return entityStr < QLatin1String(entity.name); } -static bool operator<(const QTextHtmlEntity &entity, const QString &entityStr) +static bool operator<(const QTextHtmlEntity &entity, const QStringRef &entityStr) { return QLatin1String(entity.name) < entityStr; } -static QChar resolveEntity(const QString &entity) +static QChar resolveEntity(const QStringRef &entity) { const QTextHtmlEntity *start = &entities[0]; const QTextHtmlEntity *end = &entities[MAX_ENTITY]; @@ -801,8 +801,9 @@ void QTextHtmlParser::parseExclamationTag() // parses an entity after "&", and returns it QString QTextHtmlParser::parseEntity() { - int recover = pos; - QString entity; + const int recover = pos; + int entityLen = 0; + QStringRef entity; while (pos < len) { QChar c = txt.at(pos++); if (c.isSpace() || pos - recover > 9) { @@ -810,36 +811,38 @@ QString QTextHtmlParser::parseEntity() } if (c == QLatin1Char(';')) break; - entity += c; + ++entityLen; } - { + if (entityLen) { + entity = QStringRef(&txt, recover, entityLen); QChar resolved = resolveEntity(entity); if (!resolved.isNull()) return QString(resolved); - } - if (entity.length() > 1 && entity.at(0) == QLatin1Char('#')) { - entity.remove(0, 1); // removing leading # - int base = 10; - bool ok = false; + if (entityLen > 1 && entity.at(0) == QLatin1Char('#')) { + entity = entity.mid(1); // removing leading # - if (entity.at(0).toLower() == QLatin1Char('x')) { // hex entity? - entity.remove(0, 1); - base = 16; - } + int base = 10; + bool ok = false; - uint uc = entity.toUInt(&ok, base); - if (ok) { - if (uc >= 0x80 && uc < 0x80 + (sizeof(windowsLatin1ExtendedCharacters)/sizeof(windowsLatin1ExtendedCharacters[0]))) - uc = windowsLatin1ExtendedCharacters[uc - 0x80]; - QString str; - if (QChar::requiresSurrogates(uc)) { - str += QChar(QChar::highSurrogate(uc)); - str += QChar(QChar::lowSurrogate(uc)); - } else { - str = QChar(uc); + if (entity.at(0).toLower() == QLatin1Char('x')) { // hex entity? + entity = entity.mid(1); + base = 16; + } + + uint uc = entity.toUInt(&ok, base); + if (ok) { + if (uc >= 0x80 && uc < 0x80 + (sizeof(windowsLatin1ExtendedCharacters)/sizeof(windowsLatin1ExtendedCharacters[0]))) + uc = windowsLatin1ExtendedCharacters[uc - 0x80]; + QString str; + if (QChar::requiresSurrogates(uc)) { + str += QChar(QChar::highSurrogate(uc)); + str += QChar(QChar::lowSurrogate(uc)); + } else { + str = QChar(uc); + } + return str; } - return str; } } error: diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp index 540bbf5d54..023a1b7f52 100644 --- a/src/gui/text/qtextlayout.cpp +++ b/src/gui/text/qtextlayout.cpp @@ -1653,6 +1653,7 @@ namespace { int maxGlyphs; int currentPosition; glyph_t previousGlyph; + QFontEngine *previousGlyphFontEngine; QFixed minw; QFixed softHyphenWidth; @@ -1686,13 +1687,14 @@ namespace { if (currentPosition > 0 && logClusters[currentPosition - 1] < glyphs.numGlyphs) { previousGlyph = currentGlyph(); // needed to calculate right bearing later + previousGlyphFontEngine = fontEngine; } } - inline void calculateRightBearing(glyph_t glyph) + inline void calculateRightBearing(QFontEngine *engine, glyph_t glyph) { qreal rb; - fontEngine->getGlyphBearings(glyph, 0, &rb); + engine->getGlyphBearings(glyph, 0, &rb); // We only care about negative right bearings, so we limit the range // of the bearing here so that we can assume it's negative in the rest @@ -1705,13 +1707,13 @@ namespace { { if (currentPosition <= 0) return; - calculateRightBearing(currentGlyph()); + calculateRightBearing(fontEngine, currentGlyph()); } inline void calculateRightBearingForPreviousGlyph() { if (previousGlyph > 0) - calculateRightBearing(previousGlyph); + calculateRightBearing(previousGlyphFontEngine, previousGlyph); } static const QFixed RightBearingNotCalculated; diff --git a/src/gui/util/qdesktopservices.cpp b/src/gui/util/qdesktopservices.cpp index 085c073bb1..c9747877f7 100644 --- a/src/gui/util/qdesktopservices.cpp +++ b/src/gui/util/qdesktopservices.cpp @@ -198,8 +198,15 @@ bool QDesktopServices::openUrl(const QUrl &url) return false; QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration(); - if (!platformIntegration) + if (Q_UNLIKELY(!platformIntegration)) { + QCoreApplication *application = QCoreApplication::instance(); + if (Q_UNLIKELY(!application)) + qWarning("QDesktopServices::openUrl: Please instantiate the QGuiApplication object " + "first"); + else if (Q_UNLIKELY(!qobject_cast<QGuiApplication *>(application))) + qWarning("QDesktopServices::openUrl: Application is not a GUI application"); return false; + } QPlatformServices *platformServices = platformIntegration->services(); if (!platformServices) { |