diff options
41 files changed, 1581 insertions, 138 deletions
diff --git a/examples/multimedia/audioinput/audioinput.cpp b/examples/multimedia/audioinput/audioinput.cpp index 05723ae..3d537a2 100644 --- a/examples/multimedia/audioinput/audioinput.cpp +++ b/examples/multimedia/audioinput/audioinput.cpp @@ -216,7 +216,7 @@ InputTest::~InputTest() {} void InputTest::status() { - qWarning()<<"bytesReady = "<<audioInput->bytesReady()<<" bytes, clock = "<<audioInput->clock()<<"ms, totalTime = "<<audioInput->totalTime()/1000<<"ms"; + qWarning()<<"bytesReady = "<<audioInput->bytesReady()<<" bytes, clock = "<<audioInput->clock()/1000<<"ms, totalTime = "<<audioInput->totalTime()/1000<<"ms"; } void InputTest::readMore() diff --git a/examples/multimedia/audiooutput/audiooutput.cpp b/examples/multimedia/audiooutput/audiooutput.cpp index 9e532cd..c92bbaf 100644 --- a/examples/multimedia/audiooutput/audiooutput.cpp +++ b/examples/multimedia/audiooutput/audiooutput.cpp @@ -200,7 +200,7 @@ void AudioTest::deviceChanged(int idx) void AudioTest::status() { - qWarning()<<"byteFree = "<<audioOutput->bytesFree()<<" bytes, clock = "<<audioOutput->clock()<<"ms, totalTime = "<<audioOutput->totalTime()/1000<<"ms"; + qWarning()<<"byteFree = "<<audioOutput->bytesFree()<<" bytes, clock = "<<audioOutput->clock()/1000<<"ms, totalTime = "<<audioOutput->totalTime()/1000<<"ms"; } void AudioTest::writeMore() diff --git a/src/gui/egl/qegl.cpp b/src/gui/egl/qegl.cpp index 840b9d6..39291d3 100644 --- a/src/gui/egl/qegl.cpp +++ b/src/gui/egl/qegl.cpp @@ -61,6 +61,7 @@ QEglContext::QEglContext() , cfg(0) , currentSurface(EGL_NO_SURFACE) , current(false) + , ownsContext(true) { } @@ -206,7 +207,7 @@ void QEglContext::destroySurface(EGLSurface surface) // Destroy the context. Note: this does not destroy the surface. void QEglContext::destroy() { - if (ctx != EGL_NO_CONTEXT) + if (ctx != EGL_NO_CONTEXT && ownsContext) eglDestroyContext(dpy, ctx); dpy = EGL_NO_DISPLAY; ctx = EGL_NO_CONTEXT; diff --git a/src/gui/egl/qegl_p.h b/src/gui/egl/qegl_p.h index dc399da..16b5b16 100644 --- a/src/gui/egl/qegl_p.h +++ b/src/gui/egl/qegl_p.h @@ -110,7 +110,7 @@ public: EGLDisplay display() const { return dpy; } EGLContext context() const { return ctx; } - void setContext(EGLContext context) { ctx = context; } + void setContext(EGLContext context) { ctx = context; ownsContext = false;} EGLConfig config() const { return cfg; } void setConfig(EGLConfig config) { cfg = config; } @@ -131,6 +131,7 @@ private: EGLConfig cfg; EGLSurface currentSurface; bool current; + bool ownsContext; static EGLDisplay getDisplay(QPaintDevice *device); diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 6550362..8696324 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -539,7 +539,7 @@ struct QGraphicsItemPrivate::TransformData QMatrix4x4 m; for (int i = 0; i < graphicsTransforms.size(); ++i) graphicsTransforms.at(i)->applyTo(&m); - x *= m.toTransform(); + x *= m.toTransform(0); } x.translate(xOrigin, yOrigin); x.rotate(rotation); diff --git a/src/gui/graphicsview/qgraphicstransform.cpp b/src/gui/graphicsview/qgraphicstransform.cpp index ec1a2f5..49d8999 100644 --- a/src/gui/graphicsview/qgraphicstransform.cpp +++ b/src/gui/graphicsview/qgraphicstransform.cpp @@ -547,7 +547,9 @@ void QGraphicsRotation::applyTo(QMatrix4x4 *matrix) const return; matrix->translate(d->origin); - matrix->rotate(d->angle, d->axis.x(), d->axis.y(), d->axis.z()); + QMatrix4x4 m; + m.rotate(d->angle, d->axis.x(), d->axis.y(), d->axis.z()); + *matrix *= m.toTransform(); matrix->translate(-d->origin); } diff --git a/src/gui/image/qimagepixmapcleanuphooks.cpp b/src/gui/image/qimagepixmapcleanuphooks.cpp index d08d3ef..ac30646 100644 --- a/src/gui/image/qimagepixmapcleanuphooks.cpp +++ b/src/gui/image/qimagepixmapcleanuphooks.cpp @@ -70,19 +70,30 @@ QImagePixmapCleanupHooks *QImagePixmapCleanupHooks::instance() return qt_image_and_pixmap_cleanup_hooks; } -void QImagePixmapCleanupHooks::addPixmapHook(_qt_pixmap_cleanup_hook_pm hook) +void QImagePixmapCleanupHooks::addPixmapModificationHook(_qt_pixmap_cleanup_hook_pm hook) { - pixmapHooks.append(hook); + pixmapModificationHooks.append(hook); } +void QImagePixmapCleanupHooks::addPixmapDestructionHook(_qt_pixmap_cleanup_hook_pm hook) +{ + pixmapDestructionHooks.append(hook); +} + + void QImagePixmapCleanupHooks::addImageHook(_qt_image_cleanup_hook_64 hook) { imageHooks.append(hook); } -void QImagePixmapCleanupHooks::removePixmapHook(_qt_pixmap_cleanup_hook_pm hook) +void QImagePixmapCleanupHooks::removePixmapModificationHook(_qt_pixmap_cleanup_hook_pm hook) +{ + pixmapModificationHooks.removeAll(hook); +} + +void QImagePixmapCleanupHooks::removePixmapDestructionHook(_qt_pixmap_cleanup_hook_pm hook) { - pixmapHooks.removeAll(hook); + pixmapDestructionHooks.removeAll(hook); } void QImagePixmapCleanupHooks::removeImageHook(_qt_image_cleanup_hook_64 hook) @@ -91,18 +102,29 @@ void QImagePixmapCleanupHooks::removeImageHook(_qt_image_cleanup_hook_64 hook) } -void QImagePixmapCleanupHooks::executePixmapHooks(QPixmap* pm) +void QImagePixmapCleanupHooks::executePixmapModificationHooks(QPixmap* pm) { - for (int i = 0; i < qt_image_and_pixmap_cleanup_hooks->pixmapHooks.count(); ++i) - qt_image_and_pixmap_cleanup_hooks->pixmapHooks[i](pm); + Q_ASSERT(qt_image_and_pixmap_cleanup_hooks); + for (int i = 0; i < qt_image_and_pixmap_cleanup_hooks->pixmapModificationHooks.count(); ++i) + qt_image_and_pixmap_cleanup_hooks->pixmapModificationHooks[i](pm); if (qt_pixmap_cleanup_hook_64) qt_pixmap_cleanup_hook_64(pm->cacheKey()); } +void QImagePixmapCleanupHooks::executePixmapDestructionHooks(QPixmap* pm) +{ + Q_ASSERT(qt_image_and_pixmap_cleanup_hooks); + for (int i = 0; i < qt_image_and_pixmap_cleanup_hooks->pixmapDestructionHooks.count(); ++i) + qt_image_and_pixmap_cleanup_hooks->pixmapDestructionHooks[i](pm); + + if (qt_pixmap_cleanup_hook_64) + qt_pixmap_cleanup_hook_64(pm->cacheKey()); +} void QImagePixmapCleanupHooks::executeImageHooks(qint64 key) { + Q_ASSERT(qt_image_and_pixmap_cleanup_hooks); for (int i = 0; i < qt_image_and_pixmap_cleanup_hooks->imageHooks.count(); ++i) qt_image_and_pixmap_cleanup_hooks->imageHooks[i](key); diff --git a/src/gui/image/qimagepixmapcleanuphooks_p.h b/src/gui/image/qimagepixmapcleanuphooks_p.h index dd2d0f7..16c8974 100644 --- a/src/gui/image/qimagepixmapcleanuphooks_p.h +++ b/src/gui/image/qimagepixmapcleanuphooks_p.h @@ -70,18 +70,27 @@ public: static QImagePixmapCleanupHooks *instance(); - void addPixmapHook(_qt_pixmap_cleanup_hook_pm); + // Gets called when a pixmap is about to be modified: + void addPixmapModificationHook(_qt_pixmap_cleanup_hook_pm); + + // Gets called when a pixmap is about to be destroyed: + void addPixmapDestructionHook(_qt_pixmap_cleanup_hook_pm); + + // Gets called when an image is about to be modified or destroyed: void addImageHook(_qt_image_cleanup_hook_64); - void removePixmapHook(_qt_pixmap_cleanup_hook_pm); + void removePixmapModificationHook(_qt_pixmap_cleanup_hook_pm); + void removePixmapDestructionHook(_qt_pixmap_cleanup_hook_pm); void removeImageHook(_qt_image_cleanup_hook_64); - static void executePixmapHooks(QPixmap*); + static void executePixmapModificationHooks(QPixmap*); + static void executePixmapDestructionHooks(QPixmap*); static void executeImageHooks(qint64 key); private: QList<_qt_image_cleanup_hook_64> imageHooks; - QList<_qt_pixmap_cleanup_hook_pm> pixmapHooks; + QList<_qt_pixmap_cleanup_hook_pm> pixmapModificationHooks; + QList<_qt_pixmap_cleanup_hook_pm> pixmapDestructionHooks; }; QT_END_NAMESPACE diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index c03a364..f94552d 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -322,8 +322,9 @@ QPixmap::QPixmap(const char * const xpm[]) QPixmap::~QPixmap() { - if (data->is_cached && data->ref == 1) - QImagePixmapCleanupHooks::executePixmapHooks(this); + Q_ASSERT(data->ref >= 1); // Catch if ref-counting changes again + if (data->is_cached && data->ref == 1) // ref will be decrememnted after destructor returns + QImagePixmapCleanupHooks::executePixmapDestructionHooks(this); } /*! @@ -959,7 +960,17 @@ void QPixmap::fill(const QColor &color) return; } - detach(); + if (data->ref == 1) { + // detach() will also remove this pixmap from caches, so + // it has to be called even when ref == 1. + detach(); + } else { + // Don't bother to make a copy of the data object, since + // it will be filled with new pixel data anyway. + QPixmapData *d = data->createCompatiblePixmapData(); + d->resize(data->width(), data->height()); + data = d; + } data->fill(color); } @@ -1907,7 +1918,7 @@ void QPixmap::detach() } if (data->is_cached && data->ref == 1) - QImagePixmapCleanupHooks::executePixmapHooks(this); + QImagePixmapCleanupHooks::executePixmapModificationHooks(this); #if defined(Q_WS_MAC) QMacPixmapData *macData = id == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(data.data()) : 0; diff --git a/src/gui/image/qpixmap_x11_p.h b/src/gui/image/qpixmap_x11_p.h index 2d6672d..8ce7c0d 100644 --- a/src/gui/image/qpixmap_x11_p.h +++ b/src/gui/image/qpixmap_x11_p.h @@ -103,6 +103,7 @@ private: friend class QRasterWindowSurface; friend class QGLContextPrivate; // Needs to access xinfo, gl_surface & flags friend class QEglContext; // Needs gl_surface + friend class QX11GLPixmapData; // Needs gl_surface friend bool qt_createEGLSurfaceForPixmap(QPixmapData*, bool); // Needs gl_surface void release(); diff --git a/src/gui/kernel/qapplication_mac.mm b/src/gui/kernel/qapplication_mac.mm index f9c8aa3..771cddc 100644 --- a/src/gui/kernel/qapplication_mac.mm +++ b/src/gui/kernel/qapplication_mac.mm @@ -203,6 +203,8 @@ static EventHandlerRef tablet_proximity_handler = 0; static EventHandlerUPP tablet_proximity_UPP = 0; bool QApplicationPrivate::native_modal_dialog_active; +Q_GUI_EXPORT bool qt_applefontsmoothing_enabled; + /***************************************************************************** External functions *****************************************************************************/ @@ -222,6 +224,12 @@ extern bool qt_sendSpontaneousEvent(QObject *obj, QEvent *event); // qapplicatio void onApplicationWindowChangedActivation( QWidget*widget, bool activated ); void onApplicationChangedActivation( bool activated ); +static void qt_mac_read_fontsmoothing_settings() +{ + NSInteger appleFontSmoothing = [[NSUserDefaults standardUserDefaults] integerForKey:@"AppleFontSmoothing"]; + qt_applefontsmoothing_enabled = (appleFontSmoothing > 0); +} + Q_GUI_EXPORT bool qt_mac_execute_apple_script(const char *script, long script_len, AEDesc *ret) { OSStatus err; AEDesc scriptTextDesc; @@ -1203,6 +1211,9 @@ void qt_init(QApplicationPrivate *priv, int) } if (QApplication::desktopSettingsAware()) QApplicationPrivate::qt_mac_apply_settings(); + + qt_mac_read_fontsmoothing_settings(); + // Cocoa application delegate #ifdef QT_MAC_USE_COCOA NSApplication *cocoaApp = [NSApplication sharedApplication]; diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h index 5b82e7b..bf4b4ea 100644 --- a/src/gui/painting/qpaintengine.h +++ b/src/gui/painting/qpaintengine.h @@ -278,6 +278,7 @@ private: friend class QWin32PaintEnginePrivate; friend class QMacCGContext; friend class QPreviewPaintEngine; + friend class QX11GLPixmapData; }; diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index fab2d8d..fd0e810 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -132,6 +132,10 @@ static const qreal aliasedCoordinateDelta = 0.5 - 0.015625; extern bool qt_cleartype_enabled; #endif +#ifdef Q_WS_MAC +extern bool qt_applefontsmoothing_enabled; +#endif + /******************************************************************************** * Span functions @@ -508,7 +512,7 @@ bool QRasterPaintEngine::begin(QPaintDevice *device) #if defined(Q_WS_WIN) else if (qt_cleartype_enabled) #elif defined (Q_WS_MAC) - else if (true) + else if (qt_applefontsmoothing_enabled) #else else if (false) #endif diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index 9e5707d..a192e87 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -229,16 +229,9 @@ void QImageTextureGlyphCache::createTextureData(int width, int height) int QImageTextureGlyphCache::glyphMargin() const { -#ifdef Q_WS_MAC - -#ifdef QT_MAC_USE_COCOA - // For cocoa the margin is built into the glyph it seems.. +#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA) return 0; #else - return 2; -#endif - -#else return m_type == QFontEngineGlyphCache::Raster_RGBMask ? 2 : 0; #endif } diff --git a/src/multimedia/audio/qaudioinput_alsa_p.cpp b/src/multimedia/audio/qaudioinput_alsa_p.cpp index 6786657..9eb8cfb 100644 --- a/src/multimedia/audio/qaudioinput_alsa_p.cpp +++ b/src/multimedia/audio/qaudioinput_alsa_p.cpp @@ -630,7 +630,7 @@ qint64 QAudioInputPrivate::clock() const l = -l; l %= 1000000; } - return ((t1.tv_sec * 1000)+l/1000); + return ((t1.tv_sec * 1000000)+l); } else return 0; #else diff --git a/src/multimedia/audio/qaudioinput_win32_p.cpp b/src/multimedia/audio/qaudioinput_win32_p.cpp index b6b1efe..a059e76 100644 --- a/src/multimedia/audio/qaudioinput_win32_p.cpp +++ b/src/multimedia/audio/qaudioinput_win32_p.cpp @@ -544,7 +544,7 @@ qint64 QAudioInputPrivate::clock() const if (deviceState == QAudio::StopState) return 0; - return timeStampOpened.elapsed(); + return timeStampOpened.elapsed()*1000; } void QAudioInputPrivate::reset() diff --git a/src/multimedia/audio/qaudiooutput_alsa_p.cpp b/src/multimedia/audio/qaudiooutput_alsa_p.cpp index efa7b27..689da89 100644 --- a/src/multimedia/audio/qaudiooutput_alsa_p.cpp +++ b/src/multimedia/audio/qaudiooutput_alsa_p.cpp @@ -682,7 +682,7 @@ qint64 QAudioOutputPrivate::clock() const l = -l; l %= 1000000; } - return ((t1.tv_sec * 1000)+l/1000); + return ((t1.tv_sec * 1000000)+l); } else return 0; #else diff --git a/src/multimedia/audio/qaudiooutput_win32_p.cpp b/src/multimedia/audio/qaudiooutput_win32_p.cpp index 2c4a1c2..1810ed2 100644 --- a/src/multimedia/audio/qaudiooutput_win32_p.cpp +++ b/src/multimedia/audio/qaudiooutput_win32_p.cpp @@ -496,7 +496,7 @@ qint64 QAudioOutputPrivate::clock() const if (deviceState == QAudio::StopState) return 0; - return timeStampOpened.elapsed(); + return timeStampOpened.elapsed()*1000; } QAudio::Error QAudioOutputPrivate::error() const diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index 13efbda..bcc6bdb 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -81,6 +81,8 @@ #include "qglengineshadermanager_p.h" #include "qgl2pexvertexarray_p.h" +#include "qtriangulatingstroker_p.h" + #include <QDebug> QT_BEGIN_NAMESPACE @@ -572,7 +574,6 @@ void QGL2PaintEngineExPrivate::updateMatrix() // // We expand out the multiplication to save the cost of a full 4x4 // matrix multiplication as most of the components are trivial. - const QTransform& transform = q->state()->matrix; if (mode == TextDrawingMode) { @@ -628,6 +629,9 @@ void QGL2PaintEngineExPrivate::updateMatrix() // The actual data has been updated so both shader program's uniforms need updating simpleShaderMatrixUniformDirty = true; shaderMatrixUniformDirty = true; + + dasher.setInvScale(inverseScale); + stroker.setInvScale(inverseScale); } @@ -838,28 +842,6 @@ void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode) mode = newMode; } -void QGL2PaintEngineExPrivate::drawOutline(const QVectorPath& path) -{ - transferMode(BrushDrawingMode); - - // Might need to call updateMatrix to re-calculate inverseScale - if (matrixDirty) - updateMatrix(); - - vertexCoordinateArray.clear(); - vertexCoordinateArray.addPath(path, inverseScale); - - if (path.hasImplicitClose()) { - // Close the path's outline - vertexCoordinateArray.lineToArray(path.points()[0], path.points()[1]); - vertexCoordinateArray.stops().last() += 1; - } - - prepareForDraw(currentBrush->isOpaque()); - drawVertexArrays(vertexCoordinateArray, GL_LINE_STRIP); -} - - // Assumes everything is configured for the brush you want to use void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) { @@ -922,8 +904,14 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) } -void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(QGL2PEXVertexArray& vertexArray, bool useWindingFill) +void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(const float *data, + int count, + const QVector<int> *stops, + const QGLRect &bounds, + StencilFillMode mode) { + Q_ASSERT(count || stops); + // qDebug("QGL2PaintEngineExPrivate::fillStencilWithVertexArray()"); glStencilMask(0xff); // Enable stencil writes @@ -955,19 +943,20 @@ void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(QGL2PEXVertexArray& ve } #endif - if (useWindingFill) { + if (mode == WindingFillMode) { + Q_ASSERT(stops && !count); if (q->state()->clipTestEnabled) { // Flatten clip values higher than current clip, and set high bit to match current clip glStencilFunc(GL_LEQUAL, GL_STENCIL_HIGH_BIT | q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); - composite(vertexArray.boundingRect()); + composite(bounds); glStencilFunc(GL_EQUAL, GL_STENCIL_HIGH_BIT, GL_STENCIL_HIGH_BIT); } else if (!stencilClean) { // Clear stencil buffer within bounding rect glStencilFunc(GL_ALWAYS, 0, 0xff); glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); - composite(vertexArray.boundingRect()); + composite(bounds); } // Inc. for front-facing triangle @@ -975,19 +964,43 @@ void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(QGL2PEXVertexArray& ve // Dec. for back-facing "holes" glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_DECR_WRAP); glStencilMask(~GL_STENCIL_HIGH_BIT); - drawVertexArrays(vertexArray, GL_TRIANGLE_FAN); + drawVertexArrays(data, stops, GL_TRIANGLE_FAN); if (q->state()->clipTestEnabled) { // Clear high bit of stencil outside of path glStencilFunc(GL_EQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); glStencilMask(GL_STENCIL_HIGH_BIT); - composite(vertexArray.boundingRect()); + composite(bounds); } - } else { + } else if (mode == OddEvenFillMode) { + glStencilMask(GL_STENCIL_HIGH_BIT); + glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit + drawVertexArrays(data, stops, GL_TRIANGLE_FAN); + + } else { // TriStripStrokeFillMode + Q_ASSERT(count && !stops); // tristrips generated directly, so no vertexArray or stops glStencilMask(GL_STENCIL_HIGH_BIT); +#if 0 glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit - drawVertexArrays(vertexArray, GL_TRIANGLE_FAN); + glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR); + glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, data); + glDrawArrays(GL_TRIANGLE_STRIP, 0, count); + glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR); +#else + + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + if (q->state()->clipTestEnabled) { + glStencilFunc(GL_LEQUAL, q->state()->currentClip | GL_STENCIL_HIGH_BIT, + ~GL_STENCIL_HIGH_BIT); + } else { + glStencilFunc(GL_ALWAYS, GL_STENCIL_HIGH_BIT, 0xff); + } + glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR); + glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, data); + glDrawArrays(GL_TRIANGLE_STRIP, 0, count); + glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR); +#endif } // Enable color writes & disable stencil writes @@ -1122,14 +1135,15 @@ void QGL2PaintEngineExPrivate::composite(const QGLRect& boundingRect) } // Draws the vertex array as a set of <vertexArrayStops.size()> triangle fans. -void QGL2PaintEngineExPrivate::drawVertexArrays(QGL2PEXVertexArray& vertexArray, GLenum primitive) +void QGL2PaintEngineExPrivate::drawVertexArrays(const float *data, const QVector<int> *stops, + GLenum primitive) { // Now setup the pointer to the vertex array: glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR); - glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexArray.data()); + glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, data); int previousStop = 0; - foreach(int stop, vertexArray.stops()) { + foreach(int stop, *stops) { /* qDebug("Drawing triangle fan for vertecies %d -> %d:", previousStop, stop-1); for (int i=previousStop; i<stop; ++i) @@ -1180,12 +1194,29 @@ void QGL2PaintEngineEx::fill(const QVectorPath &path, const QBrush &brush) { Q_D(QGL2PaintEngineEx); - if (qbrush_style(brush) == Qt::NoBrush) + Qt::BrushStyle style = qbrush_style(brush); + if (style == Qt::NoBrush) return; if (!d->inRenderText) ensureActive(); + + QOpenGL2PaintEngineState *s = state(); + bool doOffset = !(s->renderHints & QPainter::Antialiasing) && style == Qt::SolidPattern; + + if (doOffset) { + d->temporaryTransform = s->matrix; + QTransform tx = QTransform::fromTranslate(.49, .49); + s->matrix = s->matrix * tx; + d->matrixDirty = true; + } + d->setBrush(&brush); d->fill(path); + + if (doOffset) { + s->matrix = d->temporaryTransform; + d->matrixDirty = true; + } } void QGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) @@ -1197,23 +1228,89 @@ void QGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) if (penStyle == Qt::NoPen || qbrush_style(penBrush) == Qt::NoBrush) return; + QOpenGL2PaintEngineState *s = state(); + ensureActive(); - qreal penWidth = qpen_widthf(pen); - if ( (pen.isCosmetic() && (penStyle == Qt::SolidLine)) && (penWidth < 2.5f) ) - { - // We only handle solid, cosmetic pens with a width of 1 pixel - const QBrush& brush = pen.brush(); - d->setBrush(&brush); + bool doOffset = !(s->renderHints & QPainter::Antialiasing); + if (doOffset) { + d->temporaryTransform = s->matrix; + QTransform tx = QTransform::fromTranslate(0.49, .49); + s->matrix = s->matrix * tx; + d->matrixDirty = true; + } - if (penWidth < 0.01f) - glLineWidth(1.0); - else - glLineWidth(penWidth); + bool opaque = penBrush.isOpaque() && s->opacity > 0.99; + d->setBrush(&penBrush); + d->transferMode(BrushDrawingMode); + + // updateMatrix() is responsible for setting the inverse scale on + // the strokers, so we need to call it here and not wait for + // prepareForDraw() down below. + d->updateMatrix(); + + if (penStyle == Qt::SolidLine) { + d->stroker.process(path, pen); + + } else { // Some sort of dash + d->dasher.process(path, pen); + + QVectorPath dashStroke(d->dasher.points(), + d->dasher.elementCount(), + d->dasher.elementTypes()); + d->stroker.process(dashStroke, pen); + } + + + QGLContext *ctx = d->ctx; + + if (opaque) { + d->prepareForDraw(opaque); + glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR); + glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, false, 0, d->stroker.vertices()); + glDrawArrays(GL_TRIANGLE_STRIP, 0, d->stroker.vertexCount() / 2); + +// QBrush b(Qt::green); +// d->setBrush(&b); +// d->prepareForDraw(true); +// glDrawArrays(GL_LINE_STRIP, 0, d->stroker.vertexCount() / 2); - d->drawOutline(path); - } else - return QPaintEngineEx::stroke(path, pen); + glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR); + + } else { + qreal width = qpen_widthf(pen) / 2; + if (width == 0) + width = 0.5; + qreal extra = pen.joinStyle() == Qt::MiterJoin + ? qMax(pen.miterLimit() * width, width) + : width; + + if (pen.isCosmetic()) + extra = extra * d->inverseScale; + + QRectF bounds = path.controlPointRect().adjusted(-extra, -extra, extra, extra); + + d->fillStencilWithVertexArray(d->stroker.vertices(), d->stroker.vertexCount() / 2, + 0, bounds, QGL2PaintEngineExPrivate::TriStripStrokeFillMode); + + glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + + // Pass when any bit is set, replace stencil value with 0 + glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT); + d->prepareForDraw(false); + + // Stencil the brush onto the dest buffer + d->composite(bounds); + + glStencilMask(0); + + d->updateClipScissorTest(); + } + + if (doOffset) { + s->matrix = d->temporaryTransform; + d->matrixDirty = true; + } } void QGL2PaintEngineEx::penChanged() { } @@ -1542,7 +1639,7 @@ void QGL2PaintEngineEx::drawPixmaps(const QDrawPixmaps::Data *drawingData, int d s = qFastSin(drawingData[i].rotation * Q_PI / 180); c = qFastCos(drawingData[i].rotation * Q_PI / 180); } - + qreal right = 0.5 * drawingData[i].scaleX * drawingData[i].source.width(); qreal bottom = 0.5 * drawingData[i].scaleY * drawingData[i].source.height(); QGLPoint bottomRight(right * c - bottom * s, right * s + bottom * c); diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h index 5704a04..209cd36 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h @@ -62,6 +62,7 @@ #include <private/qglpixmapfilter_p.h> #include <private/qfontengine_p.h> #include <private/qdatabuffer_p.h> +#include <private/qtriangulatingstroker_p.h> enum EngineMode { ImageDrawingMode, @@ -160,6 +161,12 @@ class QGL2PaintEngineExPrivate : public QPaintEngineExPrivate { Q_DECLARE_PUBLIC(QGL2PaintEngineEx) public: + enum StencilFillMode { + OddEvenFillMode, + WindingFillMode, + TriStripStrokeFillMode + }; + QGL2PaintEngineExPrivate(QGL2PaintEngineEx *q_ptr) : q(q_ptr), width(0), height(0), @@ -185,15 +192,24 @@ public: // fill, drawOutline, drawTexture & drawCachedGlyphs are the rendering entry points: void fill(const QVectorPath &path); - void drawOutline(const QVectorPath& path); void drawTexture(const QGLRect& dest, const QGLRect& src, const QSize &textureSize, bool opaque, bool pattern = false); void drawCachedGlyphs(const QPointF &p, QFontEngineGlyphCache::Type glyphType, const QTextItemInt &ti); - void drawVertexArrays(QGL2PEXVertexArray& vertexArray, GLenum primitive); + void drawVertexArrays(const float *data, const QVector<int> *stops, GLenum primitive); + void drawVertexArrays(QGL2PEXVertexArray &vertexArray, GLenum primitive) { + drawVertexArrays((const float *) vertexArray.data(), &vertexArray.stops(), primitive); + } + // ^ draws whatever is in the vertex array void composite(const QGLRect& boundingRect); // ^ Composites the bounding rect onto dest buffer - void fillStencilWithVertexArray(QGL2PEXVertexArray& vertexArray, bool useWindingFill); + + void fillStencilWithVertexArray(const float *data, int count, const QVector<int> *stops, const QGLRect &bounds, StencilFillMode mode); + void fillStencilWithVertexArray(QGL2PEXVertexArray& vertexArray, bool useWindingFill) { + fillStencilWithVertexArray((const float *) vertexArray.data(), 0, &vertexArray.stops(), + vertexArray.boundingRect(), + useWindingFill ? WindingFillMode : OddEvenFillMode); + } // ^ Calls drawVertexArrays to render into stencil buffer bool prepareForDraw(bool srcPixelsAreOpaque); @@ -266,6 +282,10 @@ public: float textureInvertedY; + QTriangulatingStroker stroker; + QDashedStrokeProcessor dasher; + QTransform temporaryTransform; + QScopedPointer<QPixmapFilter> convolutionFilter; QScopedPointer<QPixmapFilter> colorizeFilter; QScopedPointer<QPixmapFilter> blurFilter; diff --git a/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp b/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp new file mode 100644 index 0000000..250dab6 --- /dev/null +++ b/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp @@ -0,0 +1,299 @@ +#include "qtriangulatingstroker_p.h" +#include <qmath.h> + + +#define CURVE_FLATNESS Q_PI / 8 + + + + +void QTriangulatingStroker::endCapOrJoinClosed(const qreal *start, const qreal *cur, + bool implicitClose, bool endsAtStart) +{ + if (endsAtStart) { + join(start + 2); + } else if (implicitClose) { + join(start); + lineTo(start); + join(start+2); + } else { + endCap(cur); + } +} + + +void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen) +{ + const qreal *pts = path.points(); + const QPainterPath::ElementType *types = path.elements(); + int count = path.elementCount(); + if (count < 2) + return; + + float realWidth = qpen_widthf(pen); + if (realWidth == 0) + realWidth = 1; + + m_width = realWidth / 2; + + bool cosmetic = pen.isCosmetic(); + if (cosmetic) { + m_width = m_width * m_inv_scale; + } + + m_join_style = qpen_joinStyle(pen); + m_cap_style = qpen_capStyle(pen); + m_vertices.reset(); + m_miter_limit = pen.miterLimit() * qpen_widthf(pen); + + // The curvyness is based on the notion that I originally wanted + // roughly one line segment pr 4 pixels. This may seem little, but + // because we sample at constantly incrementing B(t) E [0<t<1], we + // will get longer segments where the curvature is small and smaller + // segments when the curvature is high. + // + // To get a rough idea of the length of each curve, I pretend that + // the curve is a 90 degree arc, whose radius is + // qMax(curveBounds.width, curveBounds.height). Based on this + // logic we can estimate the length of the outline edges based on + // the radius + a pen width and adjusting for scale factors + // depending on if the pen is cosmetic or not. + // + // The curvyness value of PI/14 was based on, + // arcLength=2*PI*r/4=PI/2 and splitting length into somewhere + // between 3 and 8 where 5 seemed to be give pretty good results + // hence: Q_PI/14. Lower divisors will give more detail at the + // direct cost of performance. + + // simplfy pens that are thin in device size (2px wide or less) + if (realWidth < 2.5 && (cosmetic || m_inv_scale == 1)) { + if (m_cap_style == Qt::RoundCap) + m_cap_style = Qt::SquareCap; + if (m_join_style == Qt::RoundJoin) + m_join_style = Qt::MiterJoin; + m_curvyness_add = 0.5; + m_curvyness_mul = CURVE_FLATNESS; + m_roundness = 1; + } else if (cosmetic) { + m_curvyness_add = realWidth / 2; + m_curvyness_mul = CURVE_FLATNESS; + m_roundness = qMax<int>(4, realWidth * CURVE_FLATNESS); + } else { + m_curvyness_add = m_width; + m_curvyness_mul = CURVE_FLATNESS / m_inv_scale; + m_roundness = qMax<int>(4, realWidth * m_curvyness_mul); + } + + // Over this level of segmentation, there doesn't seem to be any + // benefit, even for huge penWidth + if (m_roundness > 24) + m_roundness = 24; + + m_sin_theta = qSin(Q_PI / m_roundness); // ### Use qFastSin + m_cos_theta = qCos(Q_PI / m_roundness); + + const qreal *endPts = pts + (count<<1); + const qreal *startPts; + + Qt::PenCapStyle cap = m_cap_style; + + if (!types) { + startPts = pts; + + bool endsAtStart = startPts[0] == *(endPts-2) && startPts[1] == *(endPts-1); + + Qt::PenCapStyle cap = m_cap_style; + if (endsAtStart || path.hasImplicitClose()) + m_cap_style = Qt::FlatCap; + moveTo(pts); + m_cap_style = cap; + pts += 2; + lineTo(pts); + pts += 2; + while (pts < endPts) { + join(pts); + lineTo(pts); + pts += 2; + } + + endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart); + + } else { + bool endsAtStart; + while (pts < endPts) { + switch (*types) { + case QPainterPath::MoveToElement: { + if (pts != path.points()) + endCapOrJoinClosed(startPts, pts, path.hasImplicitClose(), endsAtStart); + + startPts = pts; + int end = (endPts - pts) / 2; + int i = 2; // Start looking to ahead since we never have two moveto's in a row + while (i<end && types[i] != QPainterPath::MoveToElement) { + ++i; + } + endsAtStart = startPts[0] == pts[i*2 - 2] && startPts[1] == pts[i*2 - 1]; + if (endsAtStart || path.hasImplicitClose()) + m_cap_style = Qt::FlatCap; + + moveTo(pts); + m_cap_style = cap; + pts+=2; + ++types; + break; } + case QPainterPath::LineToElement: + if (*(types - 1) != QPainterPath::MoveToElement) + join(pts); + lineTo(pts); + pts+=2; + ++types; + break; + case QPainterPath::CurveToElement: + if (*(types - 1) != QPainterPath::MoveToElement) + join(pts); + cubicTo(pts); + pts+=6; + types+=3; + break; + default: + Q_ASSERT(false); + break; + } + } + + endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart); + } +} + +void QTriangulatingStroker::cubicTo(const qreal *pts) +{ + const QPointF *p = (const QPointF *) pts; + QBezier bezier = QBezier::fromPoints(*(p - 1), p[0], p[1], p[2]); + + QRectF bounds = bezier.bounds(); + float rad = qMax(bounds.width(), bounds.height()); + int threshold = qMin<float>(64, (rad + m_curvyness_add) * m_curvyness_mul); + if (threshold < 4) + threshold = 4; + qreal threshold_minus_1 = threshold - 1; + float vx, vy; + + float cx = m_cx, cy = m_cy; + float x, y; + + for (int i=1; i<threshold; ++i) { + qreal t = qreal(i) / threshold_minus_1; + QPointF p = bezier.pointAt(t); + x = p.x(); + y = p.y(); + + normalVector(cx, cy, x, y, &vx, &vy); + + emitLineSegment(x, y, vx, vy); + + cx = x; + cy = y; + } + + m_cx = cx; + m_cy = cy; + + m_nvx = vx; + m_nvy = vy; +} + + + +static void qdashprocessor_moveTo(qreal x, qreal y, void *data) +{ + ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::MoveToElement, x, y); +} + +static void qdashprocessor_lineTo(qreal x, qreal y, void *data) +{ + ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::LineToElement, x, y); +} + +static void qdashprocessor_cubicTo(qreal, qreal, qreal, qreal, qreal, qreal, void *) +{ + Q_ASSERT(0); // The dasher should not produce curves... +} + +QDashedStrokeProcessor::QDashedStrokeProcessor() + : m_dash_stroker(0), m_inv_scale(1) +{ + m_dash_stroker.setMoveToHook(qdashprocessor_moveTo); + m_dash_stroker.setLineToHook(qdashprocessor_lineTo); + m_dash_stroker.setCubicToHook(qdashprocessor_cubicTo); +} + +void QDashedStrokeProcessor::process(const QVectorPath &path, const QPen &pen) +{ + + const qreal *pts = path.points(); + const QPainterPath::ElementType *types = path.elements(); + int count = path.elementCount(); + + m_points.reset(); + m_types.reset(); + + qreal width = pen.width(); + if (width == 0) + width = 1; + + m_dash_stroker.setDashPattern(pen.dashPattern()); + m_dash_stroker.setStrokeWidth(width); + m_dash_stroker.setMiterLimit(pen.miterLimit()); + qreal curvyness = sqrt(width) * m_inv_scale / 8; + + if (count < 2) + return; + + const qreal *endPts = pts + (count<<1); + + m_dash_stroker.begin(this); + + if (!types) { + m_dash_stroker.moveTo(pts[0], pts[1]); + pts += 2; + while (pts < endPts) { + m_dash_stroker.lineTo(pts[0], pts[1]); + pts += 2; + } + } else { + while (pts < endPts) { + switch (*types) { + case QPainterPath::MoveToElement: + m_dash_stroker.moveTo(pts[0], pts[1]); + pts += 2; + ++types; + break; + case QPainterPath::LineToElement: + m_dash_stroker.lineTo(pts[0], pts[1]); + pts += 2; + ++types; + break; + case QPainterPath::CurveToElement: { + QBezier b = QBezier::fromPoints(*(((const QPointF *) pts) - 1), + *(((const QPointF *) pts)), + *(((const QPointF *) pts) + 1), + *(((const QPointF *) pts) + 2)); + QRectF bounds = b.bounds(); + int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * curvyness); + if (threshold < 4) + threshold = 4; + qreal threshold_minus_1 = threshold - 1; + for (int i=0; i<threshold; ++i) { + QPointF pt = b.pointAt(i / threshold_minus_1); + m_dash_stroker.lineTo(pt.x(), pt.y()); + } + pts += 6; + types += 3; + break; } + default: break; + } + } + } + + m_dash_stroker.end(); +} diff --git a/src/opengl/gl2paintengineex/qtriangulatingstroker_p.h b/src/opengl/gl2paintengineex/qtriangulatingstroker_p.h new file mode 100644 index 0000000..a28fc45 --- /dev/null +++ b/src/opengl/gl2paintengineex/qtriangulatingstroker_p.h @@ -0,0 +1,258 @@ +#ifndef QTRIANGULATINGSTROKER_P_H +#define QTRIANGULATINGSTROKER_P_H + +#include <private/qdatabuffer_p.h> +#include <private/qvectorpath_p.h> +#include <private/qbezier_p.h> +#include <private/qnumeric_p.h> +#include <private/qmath_p.h> + + +class QTriangulatingStroker +{ +public: + void process(const QVectorPath &path, const QPen &pen); + + inline int vertexCount() const { return m_vertices.size(); } + inline const float *vertices() const { return m_vertices.data(); } + + inline void setInvScale(qreal invScale) { m_inv_scale = invScale; } + +private: + inline void emitLineSegment(float x, float y, float nx, float ny); + inline void moveTo(const qreal *pts); + inline void lineTo(const qreal *pts); + void cubicTo(const qreal *pts); + inline void join(const qreal *pts); + inline void normalVector(float x1, float y1, float x2, float y2, float *nx, float *ny); + inline void endCap(const qreal *pts); + inline void arc(float x, float y); + void endCapOrJoinClosed(const qreal *start, const qreal *cur, bool implicitClose, bool endsAtStart); + + + QDataBuffer<float> m_vertices; + + float m_cx, m_cy; // current points + float m_nvx, m_nvy; // normal vector... + float m_width; + qreal m_miter_limit; + + int m_roundness; // Number of line segments in a round join + qreal m_sin_theta; // sin(m_roundness / 360); + qreal m_cos_theta; // cos(m_roundness / 360); + qreal m_inv_scale; + float m_curvyness_mul; + float m_curvyness_add; + + Qt::PenJoinStyle m_join_style; + Qt::PenCapStyle m_cap_style; +}; + +class QDashedStrokeProcessor +{ +public: + QDashedStrokeProcessor(); + + void process(const QVectorPath &path, const QPen &pen); + + inline void addElement(QPainterPath::ElementType type, qreal x, qreal y) { + m_points.add(x); + m_points.add(y); + m_types.add(type); + } + + inline int elementCount() const { return m_types.size(); } + inline qreal *points() const { return m_points.data(); } + inline QPainterPath::ElementType *elementTypes() const { return m_types.data(); } + + inline void setInvScale(qreal invScale) { m_inv_scale = invScale; } + +private: + QDataBuffer<qreal> m_points; + QDataBuffer<QPainterPath::ElementType> m_types; + QDashStroker m_dash_stroker; + qreal m_inv_scale; +}; + + + + + +inline void QTriangulatingStroker::normalVector(float x1, float y1, float x2, float y2, + float *nx, float *ny) +{ + float dx = x2 - x1; + float dy = y2 - y1; + float pw = m_width / sqrt(dx*dx + dy*dy); + *nx = -dy * pw; + *ny = dx * pw; +} + + + +inline void QTriangulatingStroker::emitLineSegment(float x, float y, float vx, float vy) +{ + m_vertices.add(x + vx); + m_vertices.add(y + vy); + m_vertices.add(x - vx); + m_vertices.add(y - vy); +} + + + +// We draw a full circle for any round join or round cap which is a +// bit of overkill... +inline void QTriangulatingStroker::arc(float x, float y) +{ + float dx = m_width; + float dy = 0; + for (int i=0; i<=m_roundness; ++i) { + float tmpx = dx * m_cos_theta - dy * m_sin_theta; + float tmpy = dx * m_sin_theta + dy * m_cos_theta; + dx = tmpx; + dy = tmpy; + emitLineSegment(x, y, dx, dy); + } +} + + + +inline void QTriangulatingStroker::endCap(const qreal *pts) +{ + switch (m_cap_style) { + case Qt::FlatCap: + break; + case Qt::SquareCap: { + float dx = m_cx - *(pts - 2); + float dy = m_cy - *(pts - 1); + + float len = m_width / sqrt(dx * dx + dy * dy); + dx = dx * len; + dy = dy * len; + + emitLineSegment(m_cx + dx, m_cy + dy, m_nvx, m_nvy); + break; } + case Qt::RoundCap: + arc(m_cx, m_cy); + break; + default: break; // to shut gcc up... + } + + int count = m_vertices.size(); + m_vertices.add(m_vertices.at(count-2)); + m_vertices.add(m_vertices.at(count-1)); +} + + +void QTriangulatingStroker::moveTo(const qreal *pts) +{ + m_cx = pts[0]; + m_cy = pts[1]; + + float x2 = pts[2]; + float y2 = pts[3]; + normalVector(m_cx, m_cy, x2, y2, &m_nvx, &m_nvy); + + + // To acheive jumps we insert zero-area tringles. This is done by + // adding two identical points in both the end of previous strip + // and beginning of next strip + bool invisibleJump = m_vertices.size(); + + switch (m_cap_style) { + case Qt::FlatCap: + if (invisibleJump) { + m_vertices.add(m_cx + m_nvx); + m_vertices.add(m_cy + m_nvy); + } + break; + case Qt::SquareCap: { + float dx = x2 - m_cx; + float dy = y2 - m_cy; + float len = m_width / sqrt(dx * dx + dy * dy); + dx = dx * len; + dy = dy * len; + float sx = m_cx - dx; + float sy = m_cy - dy; + if (invisibleJump) { + m_vertices.add(sx + m_nvx); + m_vertices.add(sy + m_nvy); + } + emitLineSegment(sx, sy, m_nvx, m_nvy); + break; } + case Qt::RoundCap: + if (invisibleJump) { + m_vertices.add(m_cx + m_nvx); + m_vertices.add(m_cy + m_nvy); + } + + // This emitLineSegment is not needed for the arc, but we need + // to start where we put the invisibleJump vertex, otherwise + // we'll have visible triangles between subpaths. + emitLineSegment(m_cx, m_cy, m_nvx, m_nvy); + arc(m_cx, m_cy); + break; + default: break; // ssssh gcc... + } + emitLineSegment(m_cx, m_cy, m_nvx, m_nvy); +} + + + +void QTriangulatingStroker::lineTo(const qreal *pts) +{ + emitLineSegment(pts[0], pts[1], m_nvx, m_nvy); + m_cx = pts[0]; + m_cy = pts[1]; +} + + + + + +void QTriangulatingStroker::join(const qreal *pts) +{ + // Creates a join to the next segment (m_cx, m_cy) -> (pts[0], pts[1]) + normalVector(m_cx, m_cy, pts[0], pts[1], &m_nvx, &m_nvy); + + switch (m_join_style) { + case Qt::BevelJoin: + break; + case Qt::MiterJoin: { + int p1 = m_vertices.size() - 6; + int p2 = m_vertices.size() - 2; + QLineF line(m_vertices.at(p1), m_vertices.at(p1+1), + m_vertices.at(p2), m_vertices.at(p2+1)); + QLineF nextLine(m_cx - m_nvx, m_cy - m_nvy, + pts[0] - m_nvx, pts[1] - m_nvy); + + QPointF isect; + if (line.intersect(nextLine, &isect) != QLineF::NoIntersection + && QLineF(line.p2(), isect).length() <= m_miter_limit) { + // The intersection point mirrored over the m_cx, m_cy point + m_vertices.add(m_cx - (isect.x() - m_cx)); + m_vertices.add(m_cy - (isect.y() - m_cy)); + + // The intersection point + m_vertices.add(isect.x()); + m_vertices.add(isect.y()); + } + // else + // Do a plain bevel join if the miter limit is exceeded or if + // the lines are parallel. This is not what the raster + // engine's stroker does, but it is both faster and similar to + // what some other graphics API's do. + + break; } + case Qt::RoundJoin: + arc(m_cx, m_cy); + break; + + default: break; // gcc warn-- + } + + emitLineSegment(m_cx, m_cy, m_nvx, m_nvy); +} + + +#endif diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro index e561932..a212675 100644 --- a/src/opengl/opengl.pro +++ b/src/opengl/opengl.pro @@ -60,7 +60,8 @@ SOURCES += qgl.cpp \ gl2paintengineex/qgl2pexvertexarray_p.h \ gl2paintengineex/qpaintengineex_opengl2_p.h \ gl2paintengineex/qglengineshadersource_p.h \ - gl2paintengineex/qglcustomshaderstage_p.h + gl2paintengineex/qglcustomshaderstage_p.h \ + gl2paintengineex/qtriangulatingstroker_p.h SOURCES += qglshaderprogram.cpp \ qglpixmapfilter.cpp \ @@ -72,7 +73,8 @@ SOURCES += qgl.cpp \ gl2paintengineex/qglengineshadermanager.cpp \ gl2paintengineex/qgl2pexvertexarray.cpp \ gl2paintengineex/qpaintengineex_opengl2.cpp \ - gl2paintengineex/qglcustomshaderstage.cpp + gl2paintengineex/qglcustomshaderstage.cpp \ + gl2paintengineex/qtriangulatingstroker.cpp } @@ -80,9 +82,13 @@ x11 { contains(QT_CONFIG, opengles1)|contains(QT_CONFIG, opengles1cl)|contains(QT_CONFIG, opengles2) { SOURCES += qgl_x11egl.cpp \ qglpixelbuffer_egl.cpp \ - qgl_egl.cpp + qgl_egl.cpp \ + qpixmapdata_x11gl_egl.cpp \ + qwindowsurface_x11gl.cpp - HEADERS += qgl_egl_p.h + HEADERS += qgl_egl_p.h \ + qpixmapdata_x11gl_p.h \ + qwindowsurface_x11gl_p.h } else { SOURCES += qgl_x11.cpp \ diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 39f04d4..6720ae7 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -151,22 +151,6 @@ class QGLEngineSelector public: QGLEngineSelector() : engineType(QPaintEngine::MaxUser) { -#ifdef Q_WS_MAC - // The ATI X1600 driver for Mac OS X does not support return - // values from functions in GLSL. Since working around this in - // the GL2 engine would require a big, ugly rewrite, we're - // falling back to the GL 1 engine.. - QGLWidget *tmp = 0; - if (!QGLContext::currentContext()) { - tmp = new QGLWidget(); - tmp->makeCurrent(); - } - if (strstr((char *) glGetString(GL_RENDERER), "X1600")) - setPreferredPaintEngine(QPaintEngine::OpenGL); - if (tmp) - delete tmp; -#endif - } void setPreferredPaintEngine(QPaintEngine::Type type) { @@ -175,6 +159,25 @@ public: } QPaintEngine::Type preferredPaintEngine() { +#ifdef Q_WS_MAC + // The ATI X1600 driver for Mac OS X does not support return + // values from functions in GLSL. Since working around this in + // the GL2 engine would require a big, ugly rewrite, we're + // falling back to the GL 1 engine.. + static bool mac_x1600_check_done = false; + if (!mac_x1600_check_done) { + QGLWidget *tmp = 0; + if (!QGLContext::currentContext()) { + tmp = new QGLWidget(); + tmp->makeCurrent(); + } + if (strstr((char *) glGetString(GL_RENDERER), "X1600")) + engineType = QPaintEngine::OpenGL; + if (tmp) + delete tmp; + mac_x1600_check_done = true; + } +#endif if (engineType == QPaintEngine::MaxUser) { // No user-set engine - use the defaults #if defined(QT_OPENGL_ES_2) @@ -1584,7 +1587,10 @@ QGLTextureCache::QGLTextureCache() Q_ASSERT(qt_gl_texture_cache == 0); qt_gl_texture_cache = this; - QImagePixmapCleanupHooks::instance()->addPixmapHook(pixmapCleanupHook); + QImagePixmapCleanupHooks::instance()->addPixmapModificationHook(cleanupTextures); +#ifdef Q_WS_X11 + QImagePixmapCleanupHooks::instance()->addPixmapDestructionHook(cleanupPixmapSurfaces); +#endif QImagePixmapCleanupHooks::instance()->addImageHook(imageCleanupHook); } @@ -1592,7 +1598,10 @@ QGLTextureCache::~QGLTextureCache() { qt_gl_texture_cache = 0; - QImagePixmapCleanupHooks::instance()->removePixmapHook(pixmapCleanupHook); + QImagePixmapCleanupHooks::instance()->removePixmapModificationHook(cleanupTextures); +#ifdef Q_WS_X11 + QImagePixmapCleanupHooks::instance()->removePixmapDestructionHook(cleanupPixmapSurfaces); +#endif QImagePixmapCleanupHooks::instance()->removeImageHook(imageCleanupHook); } @@ -1660,7 +1669,7 @@ void QGLTextureCache::imageCleanupHook(qint64 cacheKey) } -void QGLTextureCache::pixmapCleanupHook(QPixmap* pixmap) +void QGLTextureCache::cleanupTextures(QPixmap* pixmap) { // ### remove when the GL texture cache becomes thread-safe if (qApp->thread() == QThread::currentThread()) { @@ -1669,14 +1678,21 @@ void QGLTextureCache::pixmapCleanupHook(QPixmap* pixmap) if (texture && texture->options & QGLContext::MemoryManagedBindOption) instance()->remove(cacheKey); } +} + #if defined(Q_WS_X11) +void QGLTextureCache::cleanupPixmapSurfaces(QPixmap* pixmap) +{ + // Remove any bound textures first: + cleanupTextures(pixmap); + QPixmapData *pd = pixmap->data_ptr().data(); if (pd->classId() == QPixmapData::X11Class) { Q_ASSERT(pd->ref == 1); // Make sure reference counting isn't broken QGLContextPrivate::destroyGlSurfaceForPixmap(pd); } -#endif } +#endif void QGLTextureCache::deleteIfEmpty() { diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h index b1c1317..e14e7fb 100644 --- a/src/opengl/qgl.h +++ b/src/opengl/qgl.h @@ -411,6 +411,7 @@ private: friend class QGLFramebufferObjectPrivate; friend class QGLFBOGLPaintDevice; friend class QGLPaintDevice; + friend class QX11GLPixmapData; private: Q_DISABLE_COPY(QGLContext) }; diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index 129e7f7..9a17c67 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -498,7 +498,11 @@ public: static QGLTextureCache *instance(); static void deleteIfEmpty(); static void imageCleanupHook(qint64 cacheKey); - static void pixmapCleanupHook(QPixmap* pixmap); + static void cleanupTextures(QPixmap* pixmap); +#ifdef Q_WS_X11 + // X11 needs to catch pixmap data destruction to delete EGL/GLX pixmap surfaces + static void cleanupPixmapSurfaces(QPixmap* pixmap); +#endif private: QCache<qint64, QGLTexture> m_cache; diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp index 971a660..3894ed1 100644 --- a/src/opengl/qgl_x11egl.cpp +++ b/src/opengl/qgl_x11egl.cpp @@ -513,11 +513,22 @@ bool Q_OPENGL_EXPORT qt_createEGLSurfaceForPixmap(QPixmapData* pmd, bool readOnl pixmapConfig, (EGLNativePixmapType) pixmapData->handle(), pixmapAttribs.properties()); +// qDebug("qt_createEGLSurfaceForPixmap() created surface 0x%x for pixmap 0x%x", +// pixmapSurface, pixmapData->handle()); if (pixmapSurface == EGL_NO_SURFACE) { - qWarning("Failed to create a pixmap surface using config %d", (int)pixmapConfig); + qWarning() << "Failed to create a pixmap surface using config" << (int)pixmapConfig + << ":" << QEglContext::errorString(eglGetError()); return false; } + static bool doneOnce = false; + if (!doneOnce) { + // Make sure QGLTextureCache is instanciated so it can install cleanup hooks + // which cleanup the EGL surface. + QGLTextureCache::instance(); + doneOnce = true; + } + Q_ASSERT(sizeof(Qt::HANDLE) >= sizeof(EGLSurface)); // Just to make totally sure! pixmapData->gl_surface = (Qt::HANDLE)pixmapSurface; pixmapData->is_cached = true; // Make sure the cleanup hook gets called diff --git a/src/opengl/qglpaintdevice.cpp b/src/opengl/qglpaintdevice.cpp index e68a4b9..2867de5 100644 --- a/src/opengl/qglpaintdevice.cpp +++ b/src/opengl/qglpaintdevice.cpp @@ -44,6 +44,9 @@ #include <private/qglpixelbuffer_p.h> #include <private/qglframebufferobject_p.h> #include <private/qwindowsurface_gl_p.h> +#ifdef Q_WS_X11 +#include <private/qpixmapdata_x11gl_p.h> +#endif #if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) #include <private/qpixmapdata_gl_p.h> @@ -81,6 +84,7 @@ void QGLPaintDevice::beginPaint() // explicitly unbind. Otherwise the painting will go into // the previous FBO instead of to the window. m_previousFBO = ctx->d_func()->current_fbo; + if (m_previousFBO != m_thisFBO) { ctx->d_ptr->current_fbo = m_thisFBO; glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_thisFBO); @@ -186,8 +190,14 @@ QGLPaintDevice* QGLPaintDevice::getDevice(QPaintDevice* pd) case QInternal::Pixmap: { #if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) QPixmapData* pmd = static_cast<QPixmap*>(pd)->pixmapData(); - Q_ASSERT(pmd->classId() == QPixmapData::OpenGLClass); - glpd = static_cast<QGLPixmapData*>(pmd)->glDevice(); + if (pmd->classId() == QPixmapData::OpenGLClass) + glpd = static_cast<QGLPixmapData*>(pmd)->glDevice(); +#ifdef Q_WS_X11 + else if (pmd->classId() == QPixmapData::X11Class) + glpd = static_cast<QX11GLPixmapData*>(pmd); +#endif + else + qWarning("Pixmap type not supported for GL rendering"); #else qWarning("Pixmap render targets not supported on OpenGL ES 1.x"); #endif diff --git a/src/opengl/qgraphicssystem_gl.cpp b/src/opengl/qgraphicssystem_gl.cpp index 3e7fece..c0d9233 100644 --- a/src/opengl/qgraphicssystem_gl.cpp +++ b/src/opengl/qgraphicssystem_gl.cpp @@ -47,12 +47,22 @@ #include "private/qgl_p.h" #include <private/qwindowsurface_raster_p.h> +#if defined(Q_WS_X11) && defined(QT_OPENGL_ES) +#include "private/qpixmapdata_x11gl_p.h" +#include "private/qwindowsurface_x11gl_p.h" +#endif + QT_BEGIN_NAMESPACE extern QGLWidget *qt_gl_getShareWidget(); QPixmapData *QGLGraphicsSystem::createPixmapData(QPixmapData::PixelType type) const { +#if defined(Q_WS_X11) && defined(QT_OPENGL_ES) + if (type == QPixmapData::PixmapType && QX11GLPixmapData::hasX11GLPixmaps()) + return new QX11GLPixmapData(); +#endif + return new QGLPixmapData(type); } @@ -66,6 +76,11 @@ QWindowSurface *QGLGraphicsSystem::createWindowSurface(QWidget *widget) const return new QRasterWindowSurface(widget); #endif +#if defined(Q_WS_X11) && defined(QT_OPENGL_ES) + if (QX11GLPixmapData::hasX11GLPixmaps()) + return new QX11GLWindowSurface(widget); +#endif + return new QGLWindowSurface(widget); } diff --git a/src/opengl/qpaintengine_opengl.cpp b/src/opengl/qpaintengine_opengl.cpp index 80628a2..aa6b6c9 100644 --- a/src/opengl/qpaintengine_opengl.cpp +++ b/src/opengl/qpaintengine_opengl.cpp @@ -746,7 +746,6 @@ public: uint has_brush : 1; uint has_fast_pen : 1; uint use_stencil_method : 1; - uint dirty_stencil : 1; uint dirty_drawable_texture : 1; uint has_stencil_face_ext : 1; uint use_fragment_programs : 1; @@ -757,6 +756,8 @@ public: uint use_system_clip : 1; uint use_emulation : 1; + QRegion dirty_stencil; + void updateUseEmulation(); QTransform matrix; @@ -1259,7 +1260,9 @@ bool QOpenGLPaintEngine::begin(QPaintDevice *pdev) d->matrix = QTransform(); d->has_antialiasing = false; d->high_quality_antialiasing = false; - d->dirty_stencil = true; + + QSize sz(d->device->size()); + d->dirty_stencil = QRect(0, 0, sz.width(), sz.height()); d->use_emulation = false; @@ -1347,7 +1350,6 @@ bool QOpenGLPaintEngine::begin(QPaintDevice *pdev) d->offscreen.begin(); - QSize sz(d->device->size()); glViewport(0, 0, sz.width(), sz.height()); // XXX (Embedded): We need a solution for GLWidgets that draw in a part or a bigger surface... glMatrixMode(GL_PROJECTION); glLoadIdentity(); @@ -1949,34 +1951,34 @@ void QOpenGLPaintEnginePrivate::fillVertexArray(Qt::FillRule fillRule) { Q_Q(QOpenGLPaintEngine); - if (dirty_stencil) { - disableClipping(); + QRect rect = dirty_stencil.boundingRect(); - if (use_system_clip) { - glEnable(GL_SCISSOR_TEST); + if (use_system_clip) + rect = q->systemClip().intersected(dirty_stencil).boundingRect(); - QRect rect = q->systemClip().boundingRect(); + glStencilMask(~0); - const int left = rect.left(); - const int width = rect.width(); - const int bottom = device->size().height() - (rect.bottom() + 1); - const int height = rect.height(); + if (!rect.isEmpty()) { + disableClipping(); - glScissor(left, bottom, width, height); - } + glEnable(GL_SCISSOR_TEST); + + const int left = rect.left(); + const int width = rect.width(); + const int bottom = device->size().height() - (rect.bottom() + 1); + const int height = rect.height(); + + glScissor(left, bottom, width, height); glClearStencil(0); glClear(GL_STENCIL_BUFFER_BIT); - dirty_stencil = false; + dirty_stencil -= rect; - if (use_system_clip) - glDisable(GL_SCISSOR_TEST); + glDisable(GL_SCISSOR_TEST); enableClipping(); } - glStencilMask(~0); - // Enable stencil. glEnable(GL_STENCIL_TEST); diff --git a/src/opengl/qpixmapdata_x11gl_egl.cpp b/src/opengl/qpixmapdata_x11gl_egl.cpp new file mode 100644 index 0000000..813e6c8 --- /dev/null +++ b/src/opengl/qpixmapdata_x11gl_egl.cpp @@ -0,0 +1,252 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qgl_p.h> +#include <private/qegl_p.h> +#include <private/qeglproperties_p.h> +#include <private/qpaintengineex_opengl2_p.h> + +#include "qpixmapdata_x11gl_p.h" + +QT_BEGIN_NAMESPACE + +extern EGLConfig qt_chooseEGLConfigForPixmap(bool hasAlpha, bool readOnly); // in qgl_x11egl.cpp +extern bool qt_createEGLSurfaceForPixmap(QPixmapData* pmd, bool readOnly); // in qgl_x11egl.cpp + +// On 16bpp systems, RGB & ARGB pixmaps are different bit-depths and therefore need +// different contexts: +static EGLContext qPixmapARGBSharedEglContext = EGL_NO_CONTEXT; +static EGLContext qPixmapRGBSharedEglContext = EGL_NO_CONTEXT; + +bool QX11GLPixmapData::hasX11GLPixmaps() +{ + static bool checkedForX11Pixmaps = false; + static bool haveX11Pixmaps = false; + + if (checkedForX11Pixmaps) + return haveX11Pixmaps; + + checkedForX11Pixmaps = true; + + QX11PixmapData *argbPixmapData = 0; + QX11PixmapData *rgbPixmapData = 0; + do { + if (qgetenv("QT_USE_X11GL_PIXMAPS").isEmpty()) + break; + + // Check we actually have EGL configs which support pixmaps + EGLConfig argbConfig = qt_chooseEGLConfigForPixmap(true, false); + EGLConfig rgbConfig = qt_chooseEGLConfigForPixmap(false, false); + + if (argbConfig == 0 || rgbConfig == 0) + break; + + // Create the shared contexts: + eglBindAPI(EGL_OPENGL_ES_API); + EGLint contextAttribs[] = { +#if defined(QT_OPENGL_ES_2) + EGL_CONTEXT_CLIENT_VERSION, 2, +#endif + EGL_NONE + }; + qPixmapARGBSharedEglContext = eglCreateContext(QEglContext::defaultDisplay(0), + argbConfig, 0, contextAttribs); + + if (argbConfig == rgbConfig) { + // If the configs are the same, we can re-use the same context. + qPixmapRGBSharedEglContext = qPixmapARGBSharedEglContext; + } else { + qPixmapRGBSharedEglContext = eglCreateContext(QEglContext::defaultDisplay(0), + rgbConfig, 0, contextAttribs); + } + + argbPixmapData = new QX11PixmapData(QPixmapData::PixmapType); + argbPixmapData->resize(100, 100); + argbPixmapData->fill(Qt::transparent); // Force ARGB + + if (!qt_createEGLSurfaceForPixmap(argbPixmapData, false)) + break; + + haveX11Pixmaps = eglMakeCurrent(QEglContext::defaultDisplay(0), + (EGLSurface)argbPixmapData->gl_surface, + (EGLSurface)argbPixmapData->gl_surface, + qPixmapARGBSharedEglContext); + if (!haveX11Pixmaps) { + EGLint err = eglGetError(); + qWarning() << "Unable to make pixmap config current:" << err << QEglContext::errorString(err); + break; + } + + // If the ARGB & RGB configs are the same, we don't need to check RGB too + if (haveX11Pixmaps && (argbConfig != rgbConfig)) { + rgbPixmapData = new QX11PixmapData(QPixmapData::PixmapType); + rgbPixmapData->resize(100, 100); + rgbPixmapData->fill(Qt::red); + + // Try to actually create an EGL pixmap surface + if (!qt_createEGLSurfaceForPixmap(rgbPixmapData, false)) + break; + + haveX11Pixmaps = eglMakeCurrent(QEglContext::defaultDisplay(0), + (EGLSurface)rgbPixmapData->gl_surface, + (EGLSurface)rgbPixmapData->gl_surface, + qPixmapRGBSharedEglContext); + if (!haveX11Pixmaps) { + EGLint err = eglGetError(); + qWarning() << "Unable to make pixmap config current:" << err << QEglContext::errorString(err); + break; + } + } + } while (0); + + if (qPixmapARGBSharedEglContext || qPixmapRGBSharedEglContext) { + eglMakeCurrent(QEglContext::defaultDisplay(0), + EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } + + if (argbPixmapData) { + if (argbPixmapData->gl_surface) + QGLContextPrivate::destroyGlSurfaceForPixmap(argbPixmapData); + delete argbPixmapData; + argbPixmapData = 0; + } + if (rgbPixmapData) { + if (rgbPixmapData->gl_surface) + QGLContextPrivate::destroyGlSurfaceForPixmap(rgbPixmapData); + delete rgbPixmapData; + rgbPixmapData = 0; + } + + if (!haveX11Pixmaps) { + // Clean up the context(s) if we can't use X11GL pixmaps + if (qPixmapARGBSharedEglContext != EGL_NO_CONTEXT) + eglDestroyContext(QEglContext::defaultDisplay(0), qPixmapARGBSharedEglContext); + + if (qPixmapRGBSharedEglContext != qPixmapARGBSharedEglContext && + qPixmapRGBSharedEglContext != EGL_NO_CONTEXT) + { + eglDestroyContext(QEglContext::defaultDisplay(0), qPixmapRGBSharedEglContext); + } + qPixmapRGBSharedEglContext = EGL_NO_CONTEXT; + qPixmapARGBSharedEglContext = EGL_NO_CONTEXT; + } + + if (haveX11Pixmaps) + qDebug("QX11GLPixmapData is supported"); + else + qDebug("QX11GLPixmapData is *NOT* being used"); + + return haveX11Pixmaps; +} + +QX11GLPixmapData::QX11GLPixmapData() + : QX11PixmapData(QPixmapData::PixmapType), + ctx(0) +{ +} + +QX11GLPixmapData::~QX11GLPixmapData() +{ +} + +static QGL2PaintEngineEx* qt_gl2_engine_for_pixmaps = 0; + +QPaintEngine* QX11GLPixmapData::paintEngine() const +{ + // We need to create the context before beginPaint - do it here: + if (!ctx) { + ctx = new QGLContext(glFormat()); + if (ctx->d_func()->eglContext == 0) + ctx->d_func()->eglContext = new QEglContext(); + ctx->d_func()->eglContext->openDisplay(0); // ;-) + ctx->d_func()->eglContext->setApi(QEgl::OpenGL); + ctx->d_func()->eglContext->setContext(hasAlphaChannel() ? qPixmapARGBSharedEglContext + : qPixmapRGBSharedEglContext); + } + + if (!qt_gl2_engine_for_pixmaps) + qt_gl2_engine_for_pixmaps = new QGL2PaintEngineEx(); + + // Support multiple painters on multiple pixmaps simultaniously + if (qt_gl2_engine_for_pixmaps->isActive()) { + qWarning("Pixmap paint engine already active"); + QPaintEngine* engine = new QGL2PaintEngineEx(); + engine->setAutoDestruct(true); + return engine; + } + + return qt_gl2_engine_for_pixmaps; +} + +void QX11GLPixmapData::beginPaint() +{ +// qDebug("QX11GLPixmapData::beginPaint()"); + if ((EGLSurface)gl_surface == EGL_NO_SURFACE) { + qt_createEGLSurfaceForPixmap(this, false); + ctx->d_func()->eglSurface = (EGLSurface)gl_surface; + ctx->d_func()->valid = true; // ;-) + } + QGLPaintDevice::beginPaint(); +} + +void QX11GLPixmapData::endPaint() +{ + glFinish(); + QGLPaintDevice::endPaint(); +} + +QGLContext* QX11GLPixmapData::context() const +{ + return ctx; +} + +QSize QX11GLPixmapData::size() const +{ + return QSize(w, h); +} + + +QGLFormat QX11GLPixmapData::glFormat() +{ + return QGLFormat::defaultFormat(); //### +} + +QT_END_NAMESPACE diff --git a/src/opengl/qpixmapdata_x11gl_p.h b/src/opengl/qpixmapdata_x11gl_p.h new file mode 100644 index 0000000..bba9bb3 --- /dev/null +++ b/src/opengl/qpixmapdata_x11gl_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPIXMAPDATA_X11GL_P_H +#define QPIXMAPDATA_X11GL_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 <private/qpixmapdata_p.h> +#include <private/qpixmap_x11_p.h> +#include <private/qglpaintdevice_p.h> + +#include <qgl.h> + +QT_BEGIN_NAMESPACE + +class QX11GLPixmapData : public QX11PixmapData, public QGLPaintDevice +{ +public: + QX11GLPixmapData(); + virtual ~QX11GLPixmapData(); + + // Re-implemented from QGLPaintDevice + QPaintEngine* paintEngine() const; // Also re-implements QX11PixmapData::paintEngine + void beginPaint(); + void endPaint(); + QGLContext* context() const; + QSize size() const; + + static bool hasX11GLPixmaps(); + static QGLFormat glFormat(); +private: + mutable QGLContext* ctx; +}; + + +QT_END_NAMESPACE + +#endif // QPIXMAPDATA_X11GL_P_H diff --git a/src/opengl/qwindowsurface_gl.cpp b/src/opengl/qwindowsurface_gl.cpp index 2816eca..4547416 100644 --- a/src/opengl/qwindowsurface_gl.cpp +++ b/src/opengl/qwindowsurface_gl.cpp @@ -364,6 +364,7 @@ void QGLWindowSurface::hijackWindow(QWidget *widget) if (ctxpriv->eglSurface == EGL_NO_SURFACE) { qWarning() << "hijackWindow() could not create EGL surface"; } + qDebug("QGLWindowSurface - using EGLConfig %d", ctxpriv->eglContext->config()); #endif widgetPrivate->extraData()->glContext = ctx; diff --git a/src/opengl/qwindowsurface_x11gl.cpp b/src/opengl/qwindowsurface_x11gl.cpp new file mode 100644 index 0000000..8ef239d --- /dev/null +++ b/src/opengl/qwindowsurface_x11gl.cpp @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QTime> +#include <QDebug> + +#include <private/qt_x11_p.h> +#include <private/qimagepixmapcleanuphooks_p.h> + +#include "qwindowsurface_x11gl_p.h" +#include "qpixmapdata_x11gl_p.h" + +QT_BEGIN_NAMESPACE + +QX11GLWindowSurface::QX11GLWindowSurface(QWidget* window) + : QWindowSurface(window), m_GC(0), m_window(window) +{ +} + +QX11GLWindowSurface::~QX11GLWindowSurface() +{ + if (m_GC) + XFree(m_GC); +} + +QPaintDevice *QX11GLWindowSurface::paintDevice() +{ + return &m_backBuffer; +} + +extern void *qt_getClipRects(const QRegion &r, int &num); // in qpaintengine_x11.cpp + +void QX11GLWindowSurface::flush(QWidget *widget, const QRegion &widgetRegion, const QPoint &offset) +{ +// qDebug("QX11GLWindowSurface::flush()"); + QTime startTime = QTime::currentTime(); + if (m_backBuffer.isNull()) { + qDebug("QHarmattanWindowSurface::flush() - backBuffer is null, not flushing anything"); + return; + } + + QPoint widgetOffset = qt_qwidget_data(widget)->wrect.topLeft(); + QRegion windowRegion(widgetRegion); + QRect boundingRect = widgetRegion.boundingRect(); + if (!widgetOffset.isNull()) + windowRegion.translate(-widgetOffset); + QRect windowBoundingRect = windowRegion.boundingRect(); + + int rectCount; + XRectangle *rects = (XRectangle *)qt_getClipRects(windowRegion, rectCount); + if (rectCount <= 0) + return; +// qDebug() << "XSetClipRectangles"; +// for (int i = 0; i < num; ++i) +// qDebug() << ' ' << i << rects[i].x << rects[i].x << rects[i].y << rects[i].width << rects[i].height; + + if (m_GC == 0) { + m_GC = XCreateGC(X11->display, m_window->handle(), 0, 0); + XSetGraphicsExposures(X11->display, m_GC, False); + } + + XSetClipRectangles(X11->display, m_GC, 0, 0, rects, rectCount, YXBanded); + XCopyArea(X11->display, m_backBuffer.handle(), m_window->handle(), m_GC, + boundingRect.x() + offset.x(), boundingRect.y() + offset.y(), + boundingRect.width(), boundingRect.height(), + windowBoundingRect.x(), windowBoundingRect.y()); +} + +void QX11GLWindowSurface::setGeometry(const QRect &rect) +{ + if (rect.width() > m_backBuffer.size().width() || rect.height() > m_backBuffer.size().height()) { + QSize newSize = rect.size(); +// QSize newSize(1024,512); + qDebug() << "QX11GLWindowSurface::setGeometry() - creating a pixmap of size" << newSize; + QX11GLPixmapData *pd = new QX11GLPixmapData; + pd->resize(newSize.width(), newSize.height()); + m_backBuffer = QPixmap(pd); + } + +// if (gc) +// XFreeGC(X11->display, gc); +// gc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0); +// XSetGraphicsExposures(X11->display, gc, False); + QWindowSurface::setGeometry(rect); +} + +bool QX11GLWindowSurface::scroll(const QRegion &area, int dx, int dy) +{ + return false; +} + +/* +void QX11GLWindowSurface::beginPaint(const QRegion ®ion) +{ +} + +void QX11GLWindowSurface::endPaint(const QRegion ®ion) +{ +} + +QImage *QX11GLWindowSurface::buffer(const QWidget *widget) +{ +} +*/ + +QT_END_NAMESPACE diff --git a/src/opengl/qwindowsurface_x11gl_p.h b/src/opengl/qwindowsurface_x11gl_p.h new file mode 100644 index 0000000..5ba4dc5 --- /dev/null +++ b/src/opengl/qwindowsurface_x11gl_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSURFACE_X11GL_P_H +#define QWINDOWSURFACE_X11GL_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 <private/qwindowsurface_p.h> + +QT_BEGIN_NAMESPACE + +class QX11GLWindowSurface : public QWindowSurface +{ +public: + QX11GLWindowSurface(QWidget* window); + virtual ~QX11GLWindowSurface(); + + // Inherreted from QWindowSurface + QPaintDevice *paintDevice(); + void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset); + void setGeometry(const QRect &rect); + bool scroll(const QRegion &area, int dx, int dy); + +private: + GC m_GC; + QPixmap m_backBuffer; + QWidget *m_window; +}; + + +QT_END_NAMESPACE + +#endif // QWINDOWSURFACE_X11GL_P_H diff --git a/src/svg/qsvggenerator.cpp b/src/svg/qsvggenerator.cpp index da3123f..2f80a92 100644 --- a/src/svg/qsvggenerator.cpp +++ b/src/svg/qsvggenerator.cpp @@ -1063,6 +1063,7 @@ void QSvgPaintEngine::drawTextItem(const QPointF &pt, const QTextItem &textItem) "fill=\"" << d->attributes.stroke << "\" " "fill-opacity=\"" << d->attributes.strokeOpacity << "\" " "stroke=\"none\" " + "xml:space=\"preserve\" " "x=\"" << pt.x() << "\" y=\"" << pt.y() << "\" "; qfontToSvg(textItem.font()); *d->stream << " >" diff --git a/tests/auto/qgraphicstransform/tst_qgraphicstransform.cpp b/tests/auto/qgraphicstransform/tst_qgraphicstransform.cpp index b407fef..eb5c099 100644 --- a/tests/auto/qgraphicstransform/tst_qgraphicstransform.cpp +++ b/tests/auto/qgraphicstransform/tst_qgraphicstransform.cpp @@ -88,7 +88,7 @@ static QTransform transform2D(const QGraphicsTransform& t) { QMatrix4x4 m; t.applyTo(&m); - return m.toTransform(); + return m.toTransform(0); } void tst_QGraphicsTransform::scale() diff --git a/tests/auto/qsslsocket/tst_qsslsocket.cpp b/tests/auto/qsslsocket/tst_qsslsocket.cpp index 6efe440..d576201 100644 --- a/tests/auto/qsslsocket/tst_qsslsocket.cpp +++ b/tests/auto/qsslsocket/tst_qsslsocket.cpp @@ -182,6 +182,7 @@ private slots: void ignoreSslErrorsListWithSlot_data(); void ignoreSslErrorsListWithSlot(); void readFromClosedSocket(); + void writeBigChunk(); static void exitLoop() { @@ -1698,6 +1699,50 @@ void tst_QSslSocket::readFromClosedSocket() QVERIFY(!socket->bytesToWrite()); } +void tst_QSslSocket::writeBigChunk() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslSocketPtr socket = newSocket(); + this->socket = socket; + + connect(socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(ignoreErrorSlot())); + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443); + + QByteArray data; + data.resize(1024*1024*10); // 10 MB + // init with garbage. needed so ssl cannot compress it in an efficient way. + for (int i = 0; i < data.size() / sizeof(int); i++) { + int r = qrand(); + data.data()[i*sizeof(int)] = r; + } + + QVERIFY(socket->waitForEncrypted(10000)); + QString errorBefore = socket->errorString(); + + int ret = socket->write(data.constData(), data.size()); + QVERIFY(data.size() == ret); + + // spin the event loop once so QSslSocket::transmit() gets called + QCoreApplication::processEvents(); + QString errorAfter = socket->errorString(); + + // no better way to do this right now since the error is the same as the default error. + if (socket->errorString().startsWith(QLatin1String("Unable to write data"))) + { + qWarning() << socket->error() << socket->errorString(); + QFAIL("Error while writing! Check if the OpenSSL BIO size is limited?!"); + } + // also check the error string. If another error (than UnknownError) occured, it should be different than before + QVERIFY(errorBefore == errorAfter); + + // check that everything has been written to OpenSSL + QVERIFY(socket->bytesToWrite() == 0); + + socket->close(); +} + #endif // QT_NO_OPENSSL QTEST_MAIN(tst_QSslSocket) diff --git a/tests/benchmarks/benchmarks.pro b/tests/benchmarks/benchmarks.pro index ad1920b..a63fb41 100644 --- a/tests/benchmarks/benchmarks.pro +++ b/tests/benchmarks/benchmarks.pro @@ -9,8 +9,8 @@ SUBDIRS = containers-associative \ qiodevice \ qpixmap \ blendbench \ - qstring \ - qstringlist \ + qstring \ + qstringlist \ qmatrix4x4 \ qnetworkreply \ qobject \ diff --git a/tests/benchmarks/qnetworkreply/qnetworkreply.pro b/tests/benchmarks/qnetworkreply/qnetworkreply.pro index 060acf5..1e67d81 100644 --- a/tests/benchmarks/qnetworkreply/qnetworkreply.pro +++ b/tests/benchmarks/qnetworkreply/qnetworkreply.pro @@ -10,4 +10,4 @@ QT += network CONFIG += release # Input -SOURCES += main.cpp +SOURCES += tst_qnetworkreply.cpp diff --git a/tests/benchmarks/qnetworkreply/main.cpp b/tests/benchmarks/qnetworkreply/tst_qnetworkreply.cpp index 666e4f1..993db52 100644 --- a/tests/benchmarks/qnetworkreply/main.cpp +++ b/tests/benchmarks/qnetworkreply/tst_qnetworkreply.cpp @@ -54,6 +54,10 @@ class tst_qnetworkreply : public QObject private slots: void httpLatency(); +#ifndef QT_NO_OPENSSL + void echoPerformance_data(); + void echoPerformance(); +#endif }; void tst_qnetworkreply::httpLatency() @@ -69,6 +73,40 @@ void tst_qnetworkreply::httpLatency() } } +#ifndef QT_NO_OPENSSL +void tst_qnetworkreply::echoPerformance_data() +{ + QTest::addColumn<bool>("ssl"); + QTest::newRow("no_ssl") << false; + QTest::newRow("ssl") << true; +} + +void tst_qnetworkreply::echoPerformance() +{ + QFETCH(bool, ssl); + QNetworkAccessManager manager; + QNetworkRequest request(QUrl((ssl ? "https://" : "http://") + QtNetworkSettings::serverName() + "/qtest/cgi-bin/echo.cgi")); + + QByteArray data; + data.resize(1024*1024*10); // 10 MB + // init with garbage. needed so ssl cannot compress it in an efficient way. + for (int i = 0; i < data.size() / sizeof(int); i++) { + int r = qrand(); + data.data()[i*sizeof(int)] = r; + } + + QBENCHMARK{ + QNetworkReply* reply = manager.post(request, data); + connect(reply, SIGNAL(sslErrors( const QList<QSslError> &)), reply, SLOT(ignoreSslErrors())); + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(reply->error() == QNetworkReply::NoError); + delete reply; + } +} +#endif + QTEST_MAIN(tst_qnetworkreply) -#include "main.moc" +#include "tst_qnetworkreply.moc" |