diff options
author | Oswald Buddenhagen <oswald.buddenhagen@nokia.com> | 2011-03-17 13:21:42 (GMT) |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@nokia.com> | 2011-03-17 13:21:42 (GMT) |
commit | 2e7a049ee698698842cd269f91d04044667c0487 (patch) | |
tree | c530637dbb144ec94f0a75f5ab7defb2f9d4f2c4 /src | |
parent | 65ae717b2c8fc979ccb30c967335999a6d90eb4f (diff) | |
parent | cbf6c5b810316efba3ccfb27f05576b8dbfe3890 (diff) | |
download | Qt-2e7a049ee698698842cd269f91d04044667c0487.zip Qt-2e7a049ee698698842cd269f91d04044667c0487.tar.gz Qt-2e7a049ee698698842cd269f91d04044667c0487.tar.bz2 |
Merge remote-tracking branch 'mainline/master'
Diffstat (limited to 'src')
32 files changed, 1834 insertions, 115 deletions
diff --git a/src/declarative/graphicsitems/qdeclarativeflickable.cpp b/src/declarative/graphicsitems/qdeclarativeflickable.cpp index 1e2b70c..bfebff6 100644 --- a/src/declarative/graphicsitems/qdeclarativeflickable.cpp +++ b/src/declarative/graphicsitems/qdeclarativeflickable.cpp @@ -1409,8 +1409,10 @@ bool QDeclarativeFlickable::sendMouseEvent(QGraphicsSceneMouseEvent *event) QGraphicsScene *s = scene(); QDeclarativeItem *grabber = s ? qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem()) : 0; + QGraphicsItem *grabberItem = s ? s->mouseGrabberItem() : 0; + bool disabledItem = grabberItem && !grabberItem->isEnabled(); bool stealThisEvent = d->stealMouse; - if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) { + if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) { mouseEvent.setAccepted(false); for (int i = 0x1; i <= 0x10; i <<= 1) { if (event->buttons() & i) { @@ -1457,12 +1459,12 @@ bool QDeclarativeFlickable::sendMouseEvent(QGraphicsSceneMouseEvent *event) break; } grabber = qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem()); - if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) { + if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || disabledItem) { d->clearDelayedPress(); grabMouse(); } - return stealThisEvent || d->delayedPressEvent; + return stealThisEvent || d->delayedPressEvent || disabledItem; } else if (d->lastPosTime.isValid()) { d->lastPosTime.invalidate(); } diff --git a/src/declarative/graphicsitems/qdeclarativegridview.cpp b/src/declarative/graphicsitems/qdeclarativegridview.cpp index 5c2f781..c0cbed0 100644 --- a/src/declarative/graphicsitems/qdeclarativegridview.cpp +++ b/src/declarative/graphicsitems/qdeclarativegridview.cpp @@ -2834,11 +2834,9 @@ void QDeclarativeGridView::itemsInserted(int modelIndex, int count) if (d->currentItem) { d->currentItem->index = d->currentIndex; d->currentItem->setPosition(d->colPosAt(d->currentIndex), d->rowPosAt(d->currentIndex)); - } else if (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared)) { - d->updateCurrent(0); } emit currentIndexChanged(); - } else if (d->itemCount == 0 && d->currentIndex == -1) { + } else if (d->itemCount == 0 && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) { setCurrentIndex(0); } @@ -2906,6 +2904,8 @@ void QDeclarativeGridView::itemsRemoved(int modelIndex, int count) d->currentIndex = -1; if (d->itemCount) d->updateCurrent(qMin(modelIndex, d->itemCount-1)); + else + emit currentIndexChanged(); } // update visibleIndex diff --git a/src/declarative/graphicsitems/qdeclarativelistview.cpp b/src/declarative/graphicsitems/qdeclarativelistview.cpp index 2c23a1b..6ae1ddc 100644 --- a/src/declarative/graphicsitems/qdeclarativelistview.cpp +++ b/src/declarative/graphicsitems/qdeclarativelistview.cpp @@ -3272,10 +3272,10 @@ void QDeclarativeListView::itemsInserted(int modelIndex, int count) if (d->currentItem) { d->currentItem->index = d->currentIndex; d->currentItem->setPosition(d->currentItem->position() + diff); - } else if (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared)) { - d->updateCurrent(0); } emit currentIndexChanged(); + } else if (!d->itemCount && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) { + d->updateCurrent(0); } // Update the indexes of the following visible items. for (; index < d->visibleItems.count(); ++index) { @@ -3356,6 +3356,8 @@ void QDeclarativeListView::itemsRemoved(int modelIndex, int count) d->currentIndex = -1; if (d->itemCount) d->updateCurrent(qMin(modelIndex, d->itemCount-1)); + else + emit currentIndexChanged(); } // update visibleIndex diff --git a/src/declarative/graphicsitems/qdeclarativerectangle.cpp b/src/declarative/graphicsitems/qdeclarativerectangle.cpp index d962919..8f59073 100644 --- a/src/declarative/graphicsitems/qdeclarativerectangle.cpp +++ b/src/declarative/graphicsitems/qdeclarativerectangle.cpp @@ -70,7 +70,7 @@ QT_BEGIN_NAMESPACE void QDeclarativePen::setColor(const QColor &c) { _color = c; - _valid = _color.alpha() ? true : false; + _valid = (_color.alpha() && _width >= 1) ? true : false; emit penChanged(); } @@ -80,7 +80,7 @@ void QDeclarativePen::setWidth(int w) return; _width = w; - _valid = (_width < 1) ? false : true; + _valid = (_color.alpha() && _width >= 1) ? true : false; emit penChanged(); } diff --git a/src/declarative/qml/qdeclarativevme.cpp b/src/declarative/qml/qdeclarativevme.cpp index 2d551f2..781e1b8 100644 --- a/src/declarative/qml/qdeclarativevme.cpp +++ b/src/declarative/qml/qdeclarativevme.cpp @@ -127,6 +127,22 @@ void QDeclarativeVME::runDeferred(QObject *object) run(stack, ctxt, comp, start, count, QBitField()); } +inline bool fastHasBinding(QObject *o, int index) +{ + QDeclarativeData *ddata = static_cast<QDeclarativeData *>(QObjectPrivate::get(o)->declarativeData); + + return ddata && (ddata->bindingBitsSize > index) && + (ddata->bindingBits[index / 32] & (1 << (index % 32))); +} + +static void removeBindingOnProperty(QObject *o, int index) +{ + QDeclarativeAbstractBinding *binding = QDeclarativePropertyPrivate::setBinding(o, index, -1, 0); + if (binding) binding->destroy(); +} + +#define CLEAN_PROPERTY(o, index) if (fastHasBinding(o, index)) removeBindingOnProperty(o, index) + QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, QDeclarativeContextData *ctxt, QDeclarativeCompiledData *comp, @@ -336,6 +352,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreVariant: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeString.propertyIndex); + // XXX - can be more efficient QVariant v = QDeclarativeStringConverters::variantFromString(primitives.at(instr.storeString.value)); void *a[] = { &v, 0, &status, &flags }; @@ -347,6 +365,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreVariantInteger: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeString.propertyIndex); + QVariant v(instr.storeInteger.value); void *a[] = { &v, 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, @@ -357,6 +377,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreVariantDouble: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeString.propertyIndex); + QVariant v(instr.storeDouble.value); void *a[] = { &v, 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, @@ -367,6 +389,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreVariantBool: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeString.propertyIndex); + QVariant v(instr.storeBool.value); void *a[] = { &v, 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, @@ -377,6 +401,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreString: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeString.propertyIndex); + void *a[] = { (void *)&primitives.at(instr.storeString.value), 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.storeString.propertyIndex, a); @@ -386,6 +412,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreUrl: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeUrl.propertyIndex); + void *a[] = { (void *)&urls.at(instr.storeUrl.value), 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.storeUrl.propertyIndex, a); @@ -395,6 +423,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreFloat: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeFloat.propertyIndex); + float f = instr.storeFloat.value; void *a[] = { &f, 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, @@ -405,6 +435,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreDouble: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeDouble.propertyIndex); + double d = instr.storeDouble.value; void *a[] = { &d, 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, @@ -415,6 +447,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreBool: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeBool.propertyIndex); + void *a[] = { (void *)&instr.storeBool.value, 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.storeBool.propertyIndex, a); @@ -424,6 +458,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreInteger: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeInteger.propertyIndex); + void *a[] = { (void *)&instr.storeInteger.value, 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.storeInteger.propertyIndex, a); @@ -433,6 +469,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreColor: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeColor.propertyIndex); + QColor c = QColor::fromRgba(instr.storeColor.value); void *a[] = { &c, 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, @@ -443,6 +481,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreDate: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeDate.propertyIndex); + QDate d = QDate::fromJulianDay(instr.storeDate.value); void *a[] = { &d, 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, @@ -453,6 +493,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreTime: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeTime.propertyIndex); + QTime t; t.setHMS(intData.at(instr.storeTime.valueIndex), intData.at(instr.storeTime.valueIndex+1), @@ -467,6 +509,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreDateTime: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeDateTime.propertyIndex); + QTime t; t.setHMS(intData.at(instr.storeDateTime.valueIndex+1), intData.at(instr.storeDateTime.valueIndex+2), @@ -482,6 +526,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StorePoint: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeRealPair.propertyIndex); + QPoint p = QPointF(floatData.at(instr.storeRealPair.valueIndex), floatData.at(instr.storeRealPair.valueIndex+1)).toPoint(); void *a[] = { &p, 0, &status, &flags }; @@ -493,6 +539,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StorePointF: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeRealPair.propertyIndex); + QPointF p(floatData.at(instr.storeRealPair.valueIndex), floatData.at(instr.storeRealPair.valueIndex+1)); void *a[] = { &p, 0, &status, &flags }; @@ -504,6 +552,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreSize: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeRealPair.propertyIndex); + QSize p = QSizeF(floatData.at(instr.storeRealPair.valueIndex), floatData.at(instr.storeRealPair.valueIndex+1)).toSize(); void *a[] = { &p, 0, &status, &flags }; @@ -515,6 +565,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreSizeF: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeRealPair.propertyIndex); + QSizeF s(floatData.at(instr.storeRealPair.valueIndex), floatData.at(instr.storeRealPair.valueIndex+1)); void *a[] = { &s, 0, &status, &flags }; @@ -526,6 +578,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreRect: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeRect.propertyIndex); + QRect r = QRectF(floatData.at(instr.storeRect.valueIndex), floatData.at(instr.storeRect.valueIndex+1), floatData.at(instr.storeRect.valueIndex+2), @@ -539,6 +593,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreRectF: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeRect.propertyIndex); + QRectF r(floatData.at(instr.storeRect.valueIndex), floatData.at(instr.storeRect.valueIndex+1), floatData.at(instr.storeRect.valueIndex+2), @@ -552,6 +608,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::StoreVector3D: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeVector3D.propertyIndex); + QVector3D p(floatData.at(instr.storeVector3D.valueIndex), floatData.at(instr.storeVector3D.valueIndex+1), floatData.at(instr.storeVector3D.valueIndex+2)); @@ -565,6 +623,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, { QObject *assignObj = stack.pop(); QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeObject.propertyIndex); void *a[] = { (void *)&assignObj, 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, @@ -576,6 +635,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::AssignCustomType: { QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.assignCustomType.propertyIndex); + QDeclarativeCompiledData::CustomTypeData data = customTypeData.at(instr.assignCustomType.valueIndex); const QString &primitive = primitives.at(data.index); QDeclarativeMetaType::StringConverter converter = @@ -780,6 +841,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, { QObject *assign = stack.pop(); QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeObject.propertyIndex); QVariant v = QVariant::fromValue(assign); void *a[] = { &v, 0, &status, &flags }; @@ -792,6 +854,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, { QObject *assign = stack.pop(); QObject *target = stack.top(); + CLEAN_PROPERTY(target, instr.storeObject.propertyIndex); int coreIdx = instr.storeObject.propertyIndex; QMetaProperty prop = target->metaObject()->property(coreIdx); diff --git a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp index 73aa982..9d8dd41 100644 --- a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp +++ b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp @@ -394,7 +394,8 @@ void QCoeFepInputContext::resetSplitViewWidget(bool keepInputWidget) if (!alwaysResize) { if (gv->scene()) { - disconnect(gv->scene()->focusItem()->toGraphicsObject(), SIGNAL(cursorPositionChanged()), this, SLOT(translateInputWidget())); + if (gv->scene()->focusItem()) + disconnect(gv->scene()->focusItem()->toGraphicsObject(), SIGNAL(cursorPositionChanged()), this, SLOT(translateInputWidget())); QGraphicsItem *rootItem; foreach (QGraphicsItem *item, gv->scene()->items()) { if (!item->parentItem()) { diff --git a/src/gui/kernel/qdesktopwidget_s60.cpp b/src/gui/kernel/qdesktopwidget_s60.cpp index c3963f4..62a4d40 100644 --- a/src/gui/kernel/qdesktopwidget_s60.cpp +++ b/src/gui/kernel/qdesktopwidget_s60.cpp @@ -188,12 +188,14 @@ void QDesktopWidgetPrivate::cleanup() void QDesktopWidgetPrivate::init_sys() { #if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) - CWsScreenDevice *dev = S60->screenDevice(1); - if (dev) { - displayControl = static_cast<MDisplayControl *>( - dev->GetInterface(MDisplayControl::ETypeId)); - if (displayControl) { - displayControl->EnableDisplayChangeEvents(ETrue); + if (S60->screenCount() > 1) { + CWsScreenDevice *dev = S60->screenDevice(1); + if (dev) { + displayControl = static_cast<MDisplayControl *>( + dev->GetInterface(MDisplayControl::ETypeId)); + if (displayControl) { + displayControl->EnableDisplayChangeEvents(ETrue); + } } } #endif diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 37d7147..adb5fe1 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -1342,8 +1342,8 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) //give potential windows a bigger "pre-initial" size; create_sys() will give them a new size later #ifdef Q_OS_SYMBIAN if (isGLWidget) { - // Don't waste GPU mem for unnecessary large egl surface - data.crect = QRect(0,0,2,2); + // Don't waste GPU mem for unnecessary large egl surface until resized by application + data.crect = QRect(0,0,1,1); } else { data.crect = parentWidget ? QRect(0,0,100,30) : QRect(0,0,360,640); } diff --git a/src/gui/painting/qgraphicssystem_runtime.cpp b/src/gui/painting/qgraphicssystem_runtime.cpp index 5841d40..33652ee 100644 --- a/src/gui/painting/qgraphicssystem_runtime.cpp +++ b/src/gui/painting/qgraphicssystem_runtime.cpp @@ -394,7 +394,10 @@ void QRuntimeGraphicsSystem::setGraphicsSystem(const QString &name) if(m_windowSurfaceDestroyPolicy == DestroyAfterFirstFlush) proxy->m_pendingWindowSurface.reset(proxy->m_windowSurface.take()); - proxy->m_windowSurface.reset(m_graphicsSystem->createWindowSurface(widget)); + QWindowSurface *newWindowSurface = m_graphicsSystem->createWindowSurface(widget); + newWindowSurface->setGeometry(proxy->geometry()); + + proxy->m_windowSurface.reset(newWindowSurface); qt_widget_private(widget)->invalidateBuffer(widget->rect()); } diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index 8bff021..49f1a5f 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -1724,7 +1724,7 @@ static void qt_painterpath_isect_line(const QPointF &p1, } static void qt_painterpath_isect_curve(const QBezier &bezier, const QPointF &pt, - int *winding) + int *winding, int depth = 0) { qreal y = pt.y(); qreal x = pt.x(); @@ -1739,7 +1739,7 @@ static void qt_painterpath_isect_curve(const QBezier &bezier, const QPointF &pt, // hit lower limit... This is a rough threshold, but its a // tradeoff between speed and precision. const qreal lower_bound = qreal(.001); - if (bounds.width() < lower_bound && bounds.height() < lower_bound) { + if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound)) { // We make the assumption here that the curve starts to // approximate a line after while (i.e. that it doesn't // change direction drastically during its slope) @@ -1752,8 +1752,8 @@ static void qt_painterpath_isect_curve(const QBezier &bezier, const QPointF &pt, // split curve and try again... QBezier first_half, second_half; bezier.split(&first_half, &second_half); - qt_painterpath_isect_curve(first_half, pt, winding); - qt_painterpath_isect_curve(second_half, pt, winding); + qt_painterpath_isect_curve(first_half, pt, winding, depth + 1); + qt_painterpath_isect_curve(second_half, pt, winding, depth + 1); } } diff --git a/src/gui/styles/qs60style.cpp b/src/gui/styles/qs60style.cpp index c107511..6a35027 100644 --- a/src/gui/styles/qs60style.cpp +++ b/src/gui/styles/qs60style.cpp @@ -118,6 +118,7 @@ const short *QS60StylePrivate::m_pmPointer = QS60StylePrivate::data[0]; // theme background texture QPixmap *QS60StylePrivate::m_background = 0; +QPixmap *QS60StylePrivate::m_placeHolderTexture = 0; // theme palette QPalette *QS60StylePrivate::m_themePalette = 0; @@ -155,6 +156,10 @@ const double KTabFontMul = 0.72; QS60StylePrivate::~QS60StylePrivate() { clearCaches(); //deletes also background image + if (m_placeHolderTexture) { + delete m_placeHolderTexture; + m_placeHolderTexture = 0; + } deleteThemePalette(); #ifdef Q_WS_S60 removeAnimations(); @@ -505,7 +510,10 @@ void QS60StylePrivate::setBackgroundTexture(QApplication *app) const { Q_UNUSED(app) QPalette applicationPalette = QApplication::palette(); - applicationPalette.setBrush(QPalette::Window, backgroundTexture()); + // The initial QPalette::Window is just a placeHolder QPixmap to save RAM + // if the actual texture is not needed. The real texture is created just before + // painting it in qt_s60_fill_background(). + applicationPalette.setBrush(QPalette::Window, placeHolderTexture()); setThemePalette(&applicationPalette); } @@ -630,25 +638,6 @@ QPixmap QS60StylePrivate::cachedFrame(SkinFrameElements frame, const QSize &size return result; } -void QS60StylePrivate::refreshUI() -{ - QList<QWidget *> widgets = QApplication::allWidgets(); - - for (int i = 0; i < widgets.size(); ++i) { - QWidget *widget = widgets.at(i); - if (widget == 0) - continue; - - if (widget->style()) { - widget->style()->polish(widget); - QEvent event(QEvent::StyleChange); - qApp->sendEvent(widget, &event); - } - widget->update(); - widget->updateGeometry(); - } -} - void QS60StylePrivate::setFont(QWidget *widget) const { QS60StyleEnums::FontCategories fontCategory = QS60StyleEnums::FC_Undefined; @@ -719,8 +708,10 @@ void QS60StylePrivate::setThemePalette(QPalette *palette) const palette->setColor(QPalette::LinkVisited, palette->color(QPalette::Link).darker()); palette->setColor(QPalette::Highlight, s60Color(QS60StyleEnums::CL_QsnHighlightColors, 2, 0)); - // set background image as a texture brush - palette->setBrush(QPalette::Window, backgroundTexture()); + // The initial QPalette::Window is just a placeHolder QPixmap to save RAM + // if the actual texture is not needed. The real texture is created just before + // painting it in qt_s60_fill_background(). + palette->setBrush(QPalette::Window, placeHolderTexture()); // set as transparent so that styled full screen theme background is visible palette->setBrush(QPalette::Base, Qt::transparent); // set button color based on pixel colors @@ -3548,10 +3539,12 @@ extern QPoint qt_s60_fill_background_offset(const QWidget *targetWidget); bool qt_s60_fill_background(QPainter *painter, const QRegion &rgn, const QBrush &brush) { - const QPixmap backgroundTexture(QS60StylePrivate::backgroundTexture()); - if (backgroundTexture.cacheKey() != brush.texture().cacheKey()) + const QPixmap placeHolder(QS60StylePrivate::placeHolderTexture()); + if (placeHolder.cacheKey() != brush.texture().cacheKey()) return false; + const QPixmap backgroundTexture(QS60StylePrivate::backgroundTexture()); + const QPaintDevice *target = painter->device(); if (target->devType() == QInternal::Widget) { const QWidget *widget = static_cast<const QWidget *>(target); diff --git a/src/gui/styles/qs60style_p.h b/src/gui/styles/qs60style_p.h index 242c451..8c023bf 100644 --- a/src/gui/styles/qs60style_p.h +++ b/src/gui/styles/qs60style_p.h @@ -555,6 +555,7 @@ public: static QPixmap frame(SkinFrameElements frame, const QSize &size, SkinElementFlags flags = KDefaultSkinElementFlags); static QPixmap backgroundTexture(); + static QPixmap placeHolderTexture(); #ifdef Q_WS_S60 void handleDynamicLayoutVariantSwitch(); @@ -592,8 +593,6 @@ private: static QPixmap cachedFrame(SkinFrameElements frame, const QSize &size, SkinElementFlags flags = KDefaultSkinElementFlags); - static void refreshUI(); - // set S60 font for widget void setFont(QWidget *widget) const; void setThemePalette(QWidget *widget) const; @@ -616,6 +615,9 @@ private: // Contains background texture. static QPixmap *m_background; + // Placeholder pixmap for the real background texture. + static QPixmap *m_placeHolderTexture; + const static SkinElementFlags KDefaultSkinElementFlags; // defined theme palette static QPalette *m_themePalette; diff --git a/src/gui/styles/qs60style_s60.cpp b/src/gui/styles/qs60style_s60.cpp index 600c631..1ff195d 100644 --- a/src/gui/styles/qs60style_s60.cpp +++ b/src/gui/styles/qs60style_s60.cpp @@ -66,7 +66,6 @@ #include <aknnavi.h> #include <gulicon.h> #include <AknBitmapAnimation.h> - #include <centralrepository.h> #if !defined(QT_NO_STYLE_S60) || defined(QT_PLUGIN) @@ -1408,12 +1407,23 @@ QPixmap QS60StylePrivate::backgroundTexture() if (createNewBackground) { QPixmap background = part(QS60StyleEnums::SP_QsnBgScreen, - QSize(applicationRect.Width(), applicationRect.Height()), 0, SkinElementFlags()); + QSize(applicationRect.Width(), applicationRect.Height()), 0, SkinElementFlags()); m_background = new QPixmap(background); } return *m_background; } +// Generates 1*1 red pixmap as a placeholder for real texture. +// The actual theme texture is drawn in qt_s60_fill_background(). +QPixmap QS60StylePrivate::placeHolderTexture() +{ + if (!m_placeHolderTexture) { + m_placeHolderTexture = new QPixmap(1,1); + m_placeHolderTexture->fill(Qt::red); + } + return *m_placeHolderTexture; +} + QSize QS60StylePrivate::screenSize() { return QSize(S60->screenWidthInPixels, S60->screenHeightInPixels); @@ -1428,8 +1438,8 @@ QS60Style::QS60Style() void QS60StylePrivate::handleDynamicLayoutVariantSwitch() { clearCaches(QS60StylePrivate::CC_LayoutChange); + setBackgroundTexture(qApp); setActiveLayout(); - refreshUI(); foreach (QWidget *widget, QApplication::allWidgets()) widget->ensurePolished(); } diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp index 354e8a9..8eff7d2 100644 --- a/src/gui/text/qtextlayout.cpp +++ b/src/gui/text/qtextlayout.cpp @@ -1754,6 +1754,7 @@ namespace { int glyphCount; int maxGlyphs; int currentPosition; + glyph_t previousGlyph; QFixed minw; QFixed softHyphenWidth; @@ -1781,6 +1782,15 @@ namespace { return glyphs.glyphs[logClusters[currentPosition - 1]]; } + inline void saveCurrentGlyph() + { + previousGlyph = 0; + if (currentPosition > 0 && + logClusters[currentPosition - 1] < glyphs.numGlyphs) { + previousGlyph = currentGlyph(); // needed to calculate right bearing later + } + } + inline void adjustRightBearing(glyph_t glyph) { qreal rb; @@ -1795,6 +1805,12 @@ namespace { adjustRightBearing(currentGlyph()); } + inline void adjustPreviousRightBearing() + { + if (previousGlyph > 0) + adjustRightBearing(previousGlyph); + } + inline void resetRightBearing() { rightBearing = QFixed(1); // Any positive number is defined as invalid since only @@ -1871,22 +1887,7 @@ void QTextLine::layout_helper(int maxGlyphs) lbh.manualWrap = (wrapMode == QTextOption::ManualWrap || wrapMode == QTextOption::NoWrap); int item = -1; - int newItem = -1; - int left = 0; - int right = eng->layoutData->items.size()-1; - while(left <= right) { - int middle = ((right-left)/2)+left; - if (line.from > eng->layoutData->items[middle].position) - left = middle+1; - else if(line.from < eng->layoutData->items[middle].position) - right = middle-1; - else { - newItem = middle; - break; - } - } - if (newItem == -1) - newItem = right; + int newItem = eng->findItem(line.from); LB_DEBUG("from: %d: item=%d, total %d, width available %f", line.from, newItem, eng->layoutData->items.size(), line.width.toReal()); @@ -1898,6 +1899,7 @@ void QTextLine::layout_helper(int maxGlyphs) lbh.currentPosition = line.from; int end = 0; lbh.logClusters = eng->layoutData->logClustersPtr; + lbh.previousGlyph = 0; while (newItem < eng->layoutData->items.size()) { lbh.resetRightBearing(); @@ -1958,6 +1960,7 @@ void QTextLine::layout_helper(int maxGlyphs) current, lbh.logClusters, lbh.glyphs); } else { lbh.tmpData.length++; + lbh.adjustPreviousRightBearing(); } line += lbh.tmpData; goto found; @@ -1988,9 +1991,7 @@ void QTextLine::layout_helper(int maxGlyphs) } else { lbh.whiteSpaceOrObject = false; bool sb_or_ws = false; - glyph_t previousGlyph = 0; - if (lbh.currentPosition > 0 && lbh.logClusters[lbh.currentPosition - 1] <lbh.glyphs.numGlyphs) - previousGlyph = lbh.currentGlyph(); // needed to calculate right bearing later + lbh.saveCurrentGlyph(); do { addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount, current, lbh.logClusters, lbh.glyphs); @@ -2015,7 +2016,7 @@ void QTextLine::layout_helper(int maxGlyphs) // b) if we are so short of available width that the // soft hyphen is the first breakable position, then // we don't want to show it. However we initially - // have to take the width for it into accoun so that + // have to take the width for it into account so that // the text document layout sees the overflow and // switch to break-anywhere mode, in which we // want the soft-hyphen to slip into the next line @@ -2043,8 +2044,9 @@ void QTextLine::layout_helper(int maxGlyphs) // we are too wide, fix right bearing if (rightBearing <= 0) lbh.rightBearing = rightBearing; // take from cache - else if (previousGlyph > 0) - lbh.adjustRightBearing(previousGlyph); + else + lbh.adjustPreviousRightBearing(); + if (!breakany) { line.textWidth += lbh.softHyphenWidth; } @@ -2052,6 +2054,7 @@ void QTextLine::layout_helper(int maxGlyphs) goto found; } } + lbh.saveCurrentGlyph(); } if (lbh.currentPosition == end) newItem = item + 1; diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 23dd518..62c6fab 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -915,15 +915,18 @@ bool QHttpNetworkConnectionChannel::isSocketReading() const //private slots void QHttpNetworkConnectionChannel::_q_readyRead() { - // We got a readyRead but no bytes are available.. - // This happens for the Unbuffered QTcpSocket - // Also check if socket is in ConnectedState since - // this function may also be invoked via the event loop. if (socket->state() == QAbstractSocket::ConnectedState && socket->bytesAvailable() == 0) { + // We got a readyRead but no bytes are available.. + // This happens for the Unbuffered QTcpSocket + // Also check if socket is in ConnectedState since + // this function may also be invoked via the event loop. char c; qint64 ret = socket->peek(&c, 1); if (ret < 0) { - socket->disconnectFromHost(); + _q_error(socket->error()); + // We still need to handle the reply so it emits its signals etc. + if (reply) + _q_receiveReply(); return; } } @@ -1020,8 +1023,20 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket } else { errorCode = QNetworkReply::RemoteHostClosedError; } + } else if (state == QHttpNetworkConnectionChannel::ReadingState) { + if (!reply->d_func()->expectContent()) { + // No content expected, this is a valid way to have the connection closed by the server + return; + } + if (reply->contentLength() == -1 && !reply->d_func()->isChunked()) { + // There was no content-length header and it's not chunked encoding, + // so this is a valid way to have the connection closed by the server + return; + } + // ok, we got a disconnect even though we did not expect it + errorCode = QNetworkReply::RemoteHostClosedError; } else { - return; + errorCode = QNetworkReply::RemoteHostClosedError; } break; case QAbstractSocket::SocketTimeoutError: @@ -1052,6 +1067,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket if (reply) { reply->d_func()->errorString = errorString; emit reply->finishedWithError(errorCode, errorString); + reply = 0; } // send the next request QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection); diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index 2a942cc..c7c2e82 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -2210,6 +2210,8 @@ qint64 QAbstractSocket::readData(char *data, qint64 maxSize) } else if (readBytes < 0) { d->socketError = d->socketEngine->error(); setErrorString(d->socketEngine->errorString()); + d->resetSocketLayer(); + d->state = QAbstractSocket::UnconnectedState; } else if (!d->socketEngine->isReadNotificationEnabled()) { // Only do this when there was no error d->socketEngine->setReadNotificationEnabled(true); diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 3d7612a..c1b1712 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -325,9 +325,18 @@ init_context: q_X509_STORE_add_cert(ctx->cert_store, (X509 *)caCertificate.handle()); } } + + bool addExpiredCerts = true; +#if defined(Q_OS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5) + //On Leopard SSL does not work if we add the expired certificates. + if (QSysInfo::MacintoshVersion == QSysInfo::MV_10_5) + addExpiredCerts = false; +#endif // now add the expired certs - foreach (const QSslCertificate &caCertificate, expiredCerts) { - q_X509_STORE_add_cert(ctx->cert_store, (X509 *)caCertificate.handle()); + if (addExpiredCerts) { + foreach (const QSslCertificate &caCertificate, expiredCerts) { + q_X509_STORE_add_cert(ctx->cert_store, (X509 *)caCertificate.handle()); + } } if (s_loadRootCertsOnDemand && allowRootCertOnDemandLoading) { diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index fa38b5d..a134078 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -1349,11 +1349,14 @@ void QGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, c ensureActive(); d->transferMode(ImageDrawingMode); + QGLContext::BindOptions bindOptions = QGLContext::InternalBindOption|QGLContext::CanFlipNativePixmapBindOption; +#ifdef QGL_USE_TEXTURE_POOL + bindOptions |= QGLContext::TemporarilyCachedBindOption; +#endif + glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); QGLTexture *texture = - ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, - QGLContext::InternalBindOption - | QGLContext::CanFlipNativePixmapBindOption); + ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, bindOptions); GLfloat top = texture->options & QGLContext::InvertedYBindOption ? (pixmap.height() - src.top()) : src.top(); GLfloat bottom = texture->options & QGLContext::InvertedYBindOption ? (pixmap.height() - src.bottom()) : src.bottom(); @@ -1365,6 +1368,12 @@ void QGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, c d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, state()->renderHints & QPainter::SmoothPixmapTransform, texture->id); d->drawTexture(dest, srcRect, pixmap.size(), isOpaque, isBitmap); + + if (texture->options&QGLContext::TemporarilyCachedBindOption) { + // pixmap was temporarily cached as a QImage texture by pooling system + // and should be destroyed immediately + QGLTextureCache::instance()->remove(ctx, texture->id); + } } void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const QRectF& src, @@ -1389,12 +1398,23 @@ void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); - QGLTexture *texture = ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, QGLContext::InternalBindOption); + QGLContext::BindOptions bindOptions = QGLContext::InternalBindOption; +#ifdef QGL_USE_TEXTURE_POOL + bindOptions |= QGLContext::TemporarilyCachedBindOption; +#endif + + QGLTexture *texture = ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, bindOptions); GLuint id = texture->id; d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, state()->renderHints & QPainter::SmoothPixmapTransform, id); d->drawTexture(dest, src, image.size(), !image.hasAlphaChannel()); + + if (texture->options&QGLContext::TemporarilyCachedBindOption) { + // image was temporarily cached by texture pooling system + // and should be destroyed immediately + QGLTextureCache::instance()->remove(ctx, texture->id); + } } void QGL2PaintEngineEx::drawStaticTextItem(QStaticTextItem *textItem) diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro index 08ae774..b512146 100644 --- a/src/opengl/opengl.pro +++ b/src/opengl/opengl.pro @@ -157,11 +157,16 @@ embedded { } symbian { + DEFINES += QGL_USE_TEXTURE_POOL + SOURCES -= qpixmapdata_gl.cpp SOURCES += qgl_symbian.cpp \ + qpixmapdata_poolgl.cpp \ qglpixelbuffer_egl.cpp \ - qgl_egl.cpp + qgl_egl.cpp \ + qgltexturepool.cpp - HEADERS += qgl_egl_p.h + HEADERS += qgl_egl_p.h \ + qgltexturepool_p.h contains(QT_CONFIG, freetype) { DEFINES += QT_NO_FONTCONFIG diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 76621e9..a769f2c 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -100,6 +100,9 @@ #ifdef QT_OPENGL_ES #include <EGL/egl.h> #endif +#ifdef QGL_USE_TEXTURE_POOL +#include <private/qgltexturepool_p.h> +#endif // #define QT_GL_CONTEXT_RESOURCE_DEBUG @@ -2033,6 +2036,10 @@ struct DDSFormat { the pixmap/image that it stems from, e.g. installing destruction hooks in them. + \omitvalue TemporarilyCachedBindOption Used by paint engines on some + platforms to indicate that the pixmap or image texture is possibly + cached only temporarily and must be destroyed immediately after the use. + \omitvalue InternalBindOption */ @@ -2537,8 +2544,18 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G #endif const QImage &constRef = img; // to avoid detach in bits()... +#ifdef QGL_USE_TEXTURE_POOL + QGLTexturePool::instance()->createPermanentTexture(tx_id, + target, + 0, internalFormat, + img.width(), img.height(), + externalFormat, + pixel_type, + constRef.bits()); +#else glTexImage2D(target, 0, internalFormat, img.width(), img.height(), 0, externalFormat, pixel_type, constRef.bits()); +#endif #if defined(QT_OPENGL_ES_2) if (genMipmap) glGenerateMipmap(target); @@ -2577,7 +2594,6 @@ QGLTexture *QGLContextPrivate::textureCacheLookup(const qint64 key, GLenum targe return 0; } - /*! \internal */ QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, QGLContext::BindOptions options) { diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h index 9a51a77..ff73d88 100644 --- a/src/opengl/qgl.h +++ b/src/opengl/qgl.h @@ -333,6 +333,7 @@ public: MemoryManagedBindOption = 0x0010, // internal flag CanFlipNativePixmapBindOption = 0x0020, // internal flag + TemporarilyCachedBindOption = 0x0040, // internal flag DefaultBindOption = LinearFilteringBindOption | InvertedYBindOption diff --git a/src/opengl/qgl_symbian.cpp b/src/opengl/qgl_symbian.cpp index a9e2248..2978514 100644 --- a/src/opengl/qgl_symbian.cpp +++ b/src/opengl/qgl_symbian.cpp @@ -52,6 +52,7 @@ #include <private/qwidget_p.h> // to access QWExtra #include "qgl_egl_p.h" #include "qpixmapdata_gl_p.h" +#include "qgltexturepool_p.h" #include "qcolormap.h" #include <QDebug> diff --git a/src/opengl/qgltexturepool.cpp b/src/opengl/qgltexturepool.cpp new file mode 100644 index 0000000..61a88c3 --- /dev/null +++ b/src/opengl/qgltexturepool.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG 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 "qgltexturepool_p.h" +#include "qpixmapdata_gl_p.h" + +QT_BEGIN_NAMESPACE + +Q_OPENGL_EXPORT extern QGLWidget* qt_gl_share_widget(); + +static QGLTexturePool *qt_gl_texture_pool = 0; + +class QGLTexturePoolPrivate +{ +public: + QGLTexturePoolPrivate() : lruFirst(0), lruLast(0) {} + + QGLPixmapData *lruFirst; + QGLPixmapData *lruLast; +}; + +QGLTexturePool::QGLTexturePool() + : d_ptr(new QGLTexturePoolPrivate()) +{ +} + +QGLTexturePool::~QGLTexturePool() +{ +} + +QGLTexturePool *QGLTexturePool::instance() +{ + if (!qt_gl_texture_pool) + qt_gl_texture_pool = new QGLTexturePool(); + return qt_gl_texture_pool; +} + +GLuint QGLTexturePool::createTextureForPixmap(GLenum target, + GLint level, + GLint internalformat, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + QGLPixmapData *data) +{ + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(target, texture); + do { + glTexImage2D(target, level, internalformat, width, height, 0, format, type, 0); + GLenum error = glGetError(); + if (error == GL_NO_ERROR) { + if (data) + moveToHeadOfLRU(data); + return texture; + } else if (error != GL_OUT_OF_MEMORY) { + qWarning("QGLTexturePool: cannot create temporary texture because of invalid params"); + return 0; + } + } while (reclaimSpace(internalformat, width, height, format, type, data)); + qWarning("QGLTexturePool: cannot reclaim sufficient space for a %dx%d pixmap", + width, height); + return 0; +} + +bool QGLTexturePool::createPermanentTexture(GLuint texture, + GLenum target, + GLint level, + GLint internalformat, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + const GLvoid *data) +{ + glBindTexture(target, texture); + do { + glTexImage2D(target, level, internalformat, width, height, 0, format, type, data); + + GLenum error = glGetError(); + if (error == GL_NO_ERROR) { + return true; + } else if (error != GL_OUT_OF_MEMORY) { + qWarning("QGLTexturePool: cannot create permanent texture because of invalid params"); + return false; + } + } while (reclaimSpace(internalformat, width, height, format, type, 0)); + qWarning("QGLTexturePool: cannot reclaim sufficient space for a %dx%d pixmap", + width, height); + return 0; +} + +void QGLTexturePool::releaseTexture(QGLPixmapData *data, GLuint texture) +{ + // Very simple strategy at the moment: just destroy the texture. + if (data) + removeFromLRU(data); + + QGLShareContextScope ctx(qt_gl_share_widget()->context()); + glDeleteTextures(1, &texture); +} + +void QGLTexturePool::useTexture(QGLPixmapData *data) +{ + moveToHeadOfLRU(data); +} + +void QGLTexturePool::detachTexture(QGLPixmapData *data) +{ + removeFromLRU(data); +} + +bool QGLTexturePool::reclaimSpace(GLint internalformat, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + QGLPixmapData *data) +{ + Q_UNUSED(internalformat); // For future use in picking the best texture to eject. + Q_UNUSED(width); + Q_UNUSED(height); + Q_UNUSED(format); + Q_UNUSED(type); + + bool succeeded = false; + bool wasInLRU = false; + if (data) { + wasInLRU = data->inLRU; + moveToHeadOfLRU(data); + } + + QGLPixmapData *lrudata = pixmapLRU(); + if (lrudata && lrudata != data) { + lrudata->reclaimTexture(); + succeeded = true; + } + + if (data && !wasInLRU) + removeFromLRU(data); + + return succeeded; +} + +void QGLTexturePool::hibernate() +{ + Q_D(QGLTexturePool); + QGLPixmapData *pd = d->lruLast; + while (pd) { + QGLPixmapData *prevLRU = pd->prevLRU; + pd->inTexturePool = false; + pd->inLRU = false; + pd->nextLRU = 0; + pd->prevLRU = 0; + pd->hibernate(); + pd = prevLRU; + } + d->lruFirst = 0; + d->lruLast = 0; +} + +void QGLTexturePool::moveToHeadOfLRU(QGLPixmapData *data) +{ + Q_D(QGLTexturePool); + if (data->inLRU) { + if (!data->prevLRU) + return; // Already at the head of the list. + removeFromLRU(data); + } + data->inLRU = true; + data->nextLRU = d->lruFirst; + data->prevLRU = 0; + if (d->lruFirst) + d->lruFirst->prevLRU = data; + else + d->lruLast = data; + d->lruFirst = data; +} + +void QGLTexturePool::removeFromLRU(QGLPixmapData *data) +{ + Q_D(QGLTexturePool); + if (!data->inLRU) + return; + if (data->nextLRU) + data->nextLRU->prevLRU = data->prevLRU; + else + d->lruLast = data->prevLRU; + if (data->prevLRU) + data->prevLRU->nextLRU = data->nextLRU; + else + d->lruFirst = data->nextLRU; + data->inLRU = false; +} + +QGLPixmapData *QGLTexturePool::pixmapLRU() +{ + Q_D(QGLTexturePool); + return d->lruLast; +} + +QT_END_NAMESPACE diff --git a/src/opengl/qgltexturepool_p.h b/src/opengl/qgltexturepool_p.h new file mode 100644 index 0000000..8b6f726 --- /dev/null +++ b/src/opengl/qgltexturepool_p.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG 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 QGLTEXTUREPOOL_P_H +#define QGLTEXTUREPOOL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgl.h" +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_NAMESPACE + +class QGLPixmapData; +class QGLTexturePoolPrivate; + +class QGLTexturePool +{ +public: + QGLTexturePool(); + virtual ~QGLTexturePool(); + + static QGLTexturePool *instance(); + + // Create a new texture with the specified parameters and associate + // it with "data". The QGLPixmapData will be notified when the + // texture needs to be reclaimed by the pool. + // + // This function will call reclaimSpace() when texture creation fails. + GLuint createTextureForPixmap(GLenum target, + GLint level, + GLint internalformat, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + QGLPixmapData *data); + + // Create a permanent texture with the specified parameters. + // If there is insufficient space for the texture, + // then this function will call reclaimSpace() and try again. + // + // The caller is responsible for calling glDeleteTextures() + // when it no longer needs the texture, as the texture is not + // recorded in the texture pool. + bool createPermanentTexture(GLuint texture, + GLenum target, + GLint level, + GLint internalformat, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + const GLvoid *data); + + // Release a texture that is no longer required. + void releaseTexture(QGLPixmapData *data, GLuint texture); + + // Notify the pool that a QGLPixmapData object is using + // an texture again. This allows the pool to move the texture + // within a least-recently-used list of QGLPixmapData objects. + void useTexture(QGLPixmapData *data); + + // Notify the pool that the texture associated with a + // QGLPixmapData is being detached from the pool. The caller + // will become responsible for calling glDeleteTextures(). + void detachTexture(QGLPixmapData *data); + + // Reclaim space for an image allocation with the specified parameters. + // Returns true if space was reclaimed, or false if there is no + // further space that can be reclaimed. The "data" parameter + // indicates the pixmap that is trying to obtain space which should + // not itself be reclaimed. + bool reclaimSpace(GLint internalformat, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + QGLPixmapData *data); + + // Hibernate the image pool because the context is about to be + // destroyed. All textures left in the pool should be released. + void hibernate(); + +protected: + // Helper functions for managing the LRU list of QGLPixmapData objects. + void moveToHeadOfLRU(QGLPixmapData *data); + void removeFromLRU(QGLPixmapData *data); + QGLPixmapData *pixmapLRU(); + +private: + QScopedPointer<QGLTexturePoolPrivate> d_ptr; + + Q_DECLARE_PRIVATE(QGLTexturePool) + Q_DISABLE_COPY(QGLTexturePool) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/opengl/qgraphicssystem_gl.cpp b/src/opengl/qgraphicssystem_gl.cpp index 3574756..0aa3c2e 100644 --- a/src/opengl/qgraphicssystem_gl.cpp +++ b/src/opengl/qgraphicssystem_gl.cpp @@ -57,6 +57,10 @@ #include <QtGui/private/qapplication_p.h> #endif +#ifdef QGL_USE_TEXTURE_POOL +#include "private/qgltexturepool_p.h" +#endif + QT_BEGIN_NAMESPACE extern QGLWidget *qt_gl_getShareWidget(); @@ -100,6 +104,11 @@ QWindowSurface *QGLGraphicsSystem::createWindowSurface(QWidget *widget) const return new QGLWindowSurface(widget); } - +#ifdef QGL_USE_TEXTURE_POOL +void QGLGraphicsSystem::releaseCachedResources() +{ + QGLTexturePool::instance()->hibernate(); +} +#endif QT_END_NAMESPACE diff --git a/src/opengl/qgraphicssystem_gl_p.h b/src/opengl/qgraphicssystem_gl_p.h index 4630da1..5829dcc 100644 --- a/src/opengl/qgraphicssystem_gl_p.h +++ b/src/opengl/qgraphicssystem_gl_p.h @@ -66,6 +66,10 @@ public: QPixmapData *createPixmapData(QPixmapData::PixelType type) const; QWindowSurface *createWindowSurface(QWidget *widget) const; + +#ifdef QGL_USE_TEXTURE_POOL + void releaseCachedResources(); +#endif private: bool m_useX11GL; }; diff --git a/src/opengl/qpixmapdata_gl_p.h b/src/opengl/qpixmapdata_gl_p.h index a4066fd..55cc29d 100644 --- a/src/opengl/qpixmapdata_gl_p.h +++ b/src/opengl/qpixmapdata_gl_p.h @@ -66,6 +66,12 @@ class QGLFramebufferObject; class QGLFramebufferObjectFormat; class QGLPixmapData; +#ifdef QGL_USE_TEXTURE_POOL +void qt_gl_register_pixmap(QGLPixmapData *pd); +void qt_gl_unregister_pixmap(QGLPixmapData *pd); +void qt_gl_hibernate_pixmaps(); +#endif + class QGLFramebufferObjectPool { public: @@ -129,7 +135,24 @@ public: GLuint bind(bool copyBack = true) const; QGLTexture *texture() const; -#if defined(Q_OS_SYMBIAN) +#ifdef QGL_USE_TEXTURE_POOL + void destroyTexture(); + // Detach this image from the image pool. + void detachTextureFromPool(); + // Release the GL resources associated with this pixmap and copy + // the pixmap's contents out of the GPU back into main memory. + // The GL resource will be automatically recreated the next time + // ensureCreated() is called. Does nothing if the pixmap cannot be + // hibernated for some reason (e.g. texture is shared with another + // process via a SgImage). + void hibernate(); + // Called when the QGLTexturePool wants to reclaim this pixmap's + // texture objects to reuse storage. + void reclaimTexture(); + void forceToImage(); +#endif + +#ifdef Q_OS_SYMBIAN void* toNativeType(NativeType type); void fromNativeType(void* pixmap, NativeType type); #endif @@ -176,6 +199,23 @@ private: mutable QGLPixmapGLPaintDevice m_glDevice; +#ifdef QGL_USE_TEXTURE_POOL + QGLPixmapData *nextLRU; + QGLPixmapData *prevLRU; + mutable bool inLRU; + mutable bool failedToAlloc; + mutable bool inTexturePool; + + QGLPixmapData *next; + QGLPixmapData *prev; + + friend class QGLTexturePool; + + friend void qt_gl_register_pixmap(QGLPixmapData *pd); + friend void qt_gl_unregister_pixmap(QGLPixmapData *pd); + friend void qt_gl_hibernate_pixmaps(); +#endif + friend class QGLPixmapGLPaintDevice; friend class QMeeGoPixmapData; friend class QMeeGoLivePixmapData; diff --git a/src/opengl/qpixmapdata_poolgl.cpp b/src/opengl/qpixmapdata_poolgl.cpp new file mode 100644 index 0000000..f1220b1 --- /dev/null +++ b/src/opengl/qpixmapdata_poolgl.cpp @@ -0,0 +1,902 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 "qpixmap.h" +#include "qglframebufferobject.h" + +#include <private/qpaintengine_raster_p.h> + +#include "qpixmapdata_gl_p.h" + +#include <private/qgl_p.h> +#include <private/qdrawhelper_p.h> +#include <private/qimage_p.h> + +#include <private/qpaintengineex_opengl2_p.h> + +#include <qdesktopwidget.h> +#include <qfile.h> +#include <qimagereader.h> +#include <qbuffer.h> + +#include "qgltexturepool_p.h" + +QT_BEGIN_NAMESPACE + +Q_OPENGL_EXPORT extern QGLWidget* qt_gl_share_widget(); + +/*! + \class QGLFramebufferObjectPool + \since 4.6 + + \brief The QGLFramebufferObject class provides a pool of framebuffer + objects for offscreen rendering purposes. + + When requesting an FBO of a given size and format, an FBO of the same + format and a size at least as big as the requested size will be returned. + + \internal +*/ + +static inline int areaDiff(const QSize &size, const QGLFramebufferObject *fbo) +{ + return qAbs(size.width() * size.height() - fbo->width() * fbo->height()); +} + +extern int qt_next_power_of_two(int v); + +static inline QSize maybeRoundToNextPowerOfTwo(const QSize &sz) +{ +#ifdef QT_OPENGL_ES_2 + QSize rounded(qt_next_power_of_two(sz.width()), qt_next_power_of_two(sz.height())); + if (rounded.width() * rounded.height() < 1.20 * sz.width() * sz.height()) + return rounded; +#endif + return sz; +} + + +QGLFramebufferObject *QGLFramebufferObjectPool::acquire(const QSize &requestSize, const QGLFramebufferObjectFormat &requestFormat, bool strictSize) +{ + QGLFramebufferObject *chosen = 0; + QGLFramebufferObject *candidate = 0; + for (int i = 0; !chosen && i < m_fbos.size(); ++i) { + QGLFramebufferObject *fbo = m_fbos.at(i); + + if (strictSize) { + if (fbo->size() == requestSize && fbo->format() == requestFormat) { + chosen = fbo; + break; + } else { + continue; + } + } + + if (fbo->format() == requestFormat) { + // choose the fbo with a matching format and the closest size + if (!candidate || areaDiff(requestSize, candidate) > areaDiff(requestSize, fbo)) + candidate = fbo; + } + + if (candidate) { + m_fbos.removeOne(candidate); + + const QSize fboSize = candidate->size(); + QSize sz = fboSize; + + if (sz.width() < requestSize.width()) + sz.setWidth(qMax(requestSize.width(), qRound(sz.width() * 1.5))); + if (sz.height() < requestSize.height()) + sz.setHeight(qMax(requestSize.height(), qRound(sz.height() * 1.5))); + + // wasting too much space? + if (sz.width() * sz.height() > requestSize.width() * requestSize.height() * 4) + sz = requestSize; + + if (sz != fboSize) { + delete candidate; + candidate = new QGLFramebufferObject(maybeRoundToNextPowerOfTwo(sz), requestFormat); + } + + chosen = candidate; + } + } + + if (!chosen) { + if (strictSize) + chosen = new QGLFramebufferObject(requestSize, requestFormat); + else + chosen = new QGLFramebufferObject(maybeRoundToNextPowerOfTwo(requestSize), requestFormat); + } + + if (!chosen->isValid()) { + delete chosen; + chosen = 0; + } + + return chosen; +} + +void QGLFramebufferObjectPool::release(QGLFramebufferObject *fbo) +{ + if (fbo) + m_fbos << fbo; +} + + +QPaintEngine* QGLPixmapGLPaintDevice::paintEngine() const +{ + return data->paintEngine(); +} + +void QGLPixmapGLPaintDevice::beginPaint() +{ + if (!data->isValid()) + return; + + // QGLPaintDevice::beginPaint will store the current binding and replace + // it with m_thisFBO: + m_thisFBO = data->m_renderFbo->handle(); + QGLPaintDevice::beginPaint(); + + Q_ASSERT(data->paintEngine()->type() == QPaintEngine::OpenGL2); + + // QPixmap::fill() is deferred until now, where we actually need to do the fill: + if (data->needsFill()) { + const QColor &c = data->fillColor(); + float alpha = c.alphaF(); + glDisable(GL_SCISSOR_TEST); + glClearColor(c.redF() * alpha, c.greenF() * alpha, c.blueF() * alpha, alpha); + glClear(GL_COLOR_BUFFER_BIT); + } + else if (!data->isUninitialized()) { + // If the pixmap (GL Texture) has valid content (it has been + // uploaded from an image or rendered into before), we need to + // copy it from the texture to the render FBO. + + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + +#if !defined(QT_OPENGL_ES_2) + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, data->width(), data->height(), 0, -999999, 999999); +#endif + + glViewport(0, 0, data->width(), data->height()); + + // Pass false to bind so it doesn't copy the FBO into the texture! + context()->drawTexture(QRect(0, 0, data->width(), data->height()), data->bind(false)); + } +} + +void QGLPixmapGLPaintDevice::endPaint() +{ + if (!data->isValid()) + return; + + data->copyBackFromRenderFbo(false); + + // Base's endPaint will restore the previous FBO binding + QGLPaintDevice::endPaint(); + + qgl_fbo_pool()->release(data->m_renderFbo); + data->m_renderFbo = 0; +} + +QGLContext* QGLPixmapGLPaintDevice::context() const +{ + data->ensureCreated(); + return data->m_ctx; +} + +QSize QGLPixmapGLPaintDevice::size() const +{ + return data->size(); +} + +bool QGLPixmapGLPaintDevice::alphaRequested() const +{ + return data->m_hasAlpha; +} + +void QGLPixmapGLPaintDevice::setPixmapData(QGLPixmapData* d) +{ + data = d; +} + +static int qt_gl_pixmap_serial = 0; + +QGLPixmapData::QGLPixmapData(PixelType type) + : QPixmapData(type, OpenGLClass) + , m_renderFbo(0) + , m_engine(0) + , m_ctx(0) + , m_dirty(false) + , m_hasFillColor(false) + , m_hasAlpha(false) + , inLRU(false) + , failedToAlloc(false) + , inTexturePool(false) +{ + setSerialNumber(++qt_gl_pixmap_serial); + m_glDevice.setPixmapData(this); + + qt_gl_register_pixmap(this); +} + +QGLPixmapData::~QGLPixmapData() +{ + delete m_engine; + + destroyTexture(); + qt_gl_unregister_pixmap(this); +} + +void QGLPixmapData::destroyTexture() +{ + if (inTexturePool) { + QGLTexturePool *pool = QGLTexturePool::instance(); + if (m_texture.id) + pool->releaseTexture(this, m_texture.id); + } else { + if (m_texture.id) { + QGLWidget *shareWidget = qt_gl_share_widget(); + if (shareWidget) { + QGLShareContextScope ctx(shareWidget->context()); + glDeleteTextures(1, &m_texture.id); + } + } + } + m_texture.id = 0; + inTexturePool = false; +} + +QPixmapData *QGLPixmapData::createCompatiblePixmapData() const +{ + return new QGLPixmapData(pixelType()); +} + +bool QGLPixmapData::isValid() const +{ + return w > 0 && h > 0; +} + +bool QGLPixmapData::isValidContext(const QGLContext *ctx) const +{ + if (ctx == m_ctx) + return true; + + const QGLContext *share_ctx = qt_gl_share_widget()->context(); + return ctx == share_ctx || QGLContext::areSharing(ctx, share_ctx); +} + +void QGLPixmapData::resize(int width, int height) +{ + if (width == w && height == h) + return; + + if (width <= 0 || height <= 0) { + width = 0; + height = 0; + } + + w = width; + h = height; + is_null = (w <= 0 || h <= 0); + d = pixelType() == QPixmapData::PixmapType ? 32 : 1; + + destroyTexture(); + + m_source = QImage(); + m_dirty = isValid(); + setSerialNumber(++qt_gl_pixmap_serial); +} + +void QGLPixmapData::ensureCreated() const +{ + if (!m_dirty) + return; + + m_dirty = false; + + QGLShareContextScope ctx(qt_gl_share_widget()->context()); + m_ctx = ctx; + + const GLenum internal_format = m_hasAlpha ? GL_RGBA : GL_RGB; +#ifdef QT_OPENGL_ES_2 + const GLenum external_format = internal_format; +#else + const GLenum external_format = qt_gl_preferredTextureFormat(); +#endif + const GLenum target = GL_TEXTURE_2D; + + m_texture.options &= ~QGLContext::MemoryManagedBindOption; + + if (!m_texture.id) { + m_texture.id = QGLTexturePool::instance()->createTextureForPixmap( + target, + 0, internal_format, + w, h, + external_format, + GL_UNSIGNED_BYTE, + const_cast<QGLPixmapData*>(this)); + if (!m_texture.id) { + failedToAlloc = true; + return; + } + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + inTexturePool = true; + } else if (inTexturePool) { + glBindTexture(target, m_texture.id); + QGLTexturePool::instance()->useTexture(const_cast<QGLPixmapData*>(this)); + } + + if (!m_source.isNull() && m_texture.id) { + if (external_format == GL_RGB) { + const QImage tx = m_source.convertToFormat(QImage::Format_RGB888).mirrored(false, true); + + glBindTexture(target, m_texture.id); + glTexSubImage2D(target, 0, 0, 0, w, h, external_format, + GL_UNSIGNED_BYTE, tx.bits()); + } else { + // do byte swizzling ARGB -> RGBA + const QImage tx = ctx->d_func()->convertToGLFormat(m_source, true, external_format); + glBindTexture(target, m_texture.id); + glTexSubImage2D(target, 0, 0, 0, w, h, external_format, + GL_UNSIGNED_BYTE, tx.bits()); + } + + if (useFramebufferObjects()) + m_source = QImage(); + } +} + + +void QGLPixmapData::fromImage(const QImage &image, + Qt::ImageConversionFlags flags) +{ + QImage img = image; + createPixmapForImage(img, flags, false); +} + +void QGLPixmapData::fromImageReader(QImageReader *imageReader, + Qt::ImageConversionFlags flags) +{ + QImage image = imageReader->read(); + if (image.isNull()) + return; + + createPixmapForImage(image, flags, true); +} + +bool QGLPixmapData::fromFile(const QString &filename, const char *format, + Qt::ImageConversionFlags flags) +{ + if (pixelType() == QPixmapData::BitmapType) + return QPixmapData::fromFile(filename, format, flags); + QFile file(filename); + if (file.open(QIODevice::ReadOnly)) { + QByteArray data = file.peek(64); + bool alpha; + if (m_texture.canBindCompressedTexture + (data.constData(), data.size(), format, &alpha)) { + resize(0, 0); + data = file.readAll(); + file.close(); + QGLShareContextScope ctx(qt_gl_share_widget()->context()); + QSize size = m_texture.bindCompressedTexture + (data.constData(), data.size(), format); + if (!size.isEmpty()) { + w = size.width(); + h = size.height(); + is_null = false; + d = 32; + m_hasAlpha = alpha; + m_source = QImage(); + m_dirty = isValid(); + return true; + } + return false; + } + } + + QImage image = QImageReader(filename, format).read(); + if (image.isNull()) + return false; + + createPixmapForImage(image, flags, true); + + return !isNull(); +} + +bool QGLPixmapData::fromData(const uchar *buffer, uint len, const char *format, + Qt::ImageConversionFlags flags) +{ + bool alpha; + const char *buf = reinterpret_cast<const char *>(buffer); + if (m_texture.canBindCompressedTexture(buf, int(len), format, &alpha)) { + resize(0, 0); + QGLShareContextScope ctx(qt_gl_share_widget()->context()); + QSize size = m_texture.bindCompressedTexture(buf, int(len), format); + if (!size.isEmpty()) { + w = size.width(); + h = size.height(); + is_null = false; + d = 32; + m_hasAlpha = alpha; + m_source = QImage(); + m_dirty = isValid(); + return true; + } + } + + QByteArray a = QByteArray::fromRawData(reinterpret_cast<const char *>(buffer), len); + QBuffer b(&a); + b.open(QIODevice::ReadOnly); + QImage image = QImageReader(&b, format).read(); + if (image.isNull()) + return false; + + createPixmapForImage(image, flags, true); + + return !isNull(); +} + +/*! + out-of-place conversion (inPlace == false) will always detach() + */ +void QGLPixmapData::createPixmapForImage(QImage &image, Qt::ImageConversionFlags flags, bool inPlace) +{ + if (image.size() == QSize(w, h)) + setSerialNumber(++qt_gl_pixmap_serial); + + resize(image.width(), image.height()); + + if (pixelType() == BitmapType) { + m_source = image.convertToFormat(QImage::Format_MonoLSB); + + } else { + QImage::Format format = QImage::Format_RGB32; + if (qApp->desktop()->depth() == 16) + format = QImage::Format_RGB16; + + if (image.hasAlphaChannel() + && ((flags & Qt::NoOpaqueDetection) + || const_cast<QImage &>(image).data_ptr()->checkForAlphaPixels())) + format = QImage::Format_ARGB32_Premultiplied; + + if (inPlace && image.data_ptr()->convertInPlace(format, flags)) { + m_source = image; + } else { + m_source = image.convertToFormat(format); + + // convertToFormat won't detach the image if format stays the same. + if (image.format() == format) + m_source.detach(); + } + } + + m_dirty = true; + m_hasFillColor = false; + + m_hasAlpha = m_source.hasAlphaChannel(); + w = image.width(); + h = image.height(); + is_null = (w <= 0 || h <= 0); + d = m_source.depth(); + + destroyTexture(); +} + +bool QGLPixmapData::scroll(int dx, int dy, const QRect &rect) +{ + Q_UNUSED(dx); + Q_UNUSED(dy); + Q_UNUSED(rect); + return false; +} + +void QGLPixmapData::copy(const QPixmapData *data, const QRect &rect) +{ + if (data->classId() != QPixmapData::OpenGLClass || !static_cast<const QGLPixmapData *>(data)->useFramebufferObjects()) { + QPixmapData::copy(data, rect); + return; + } + + const QGLPixmapData *other = static_cast<const QGLPixmapData *>(data); + if (other->m_renderFbo) { + QGLShareContextScope ctx(qt_gl_share_widget()->context()); + + resize(rect.width(), rect.height()); + m_hasAlpha = other->m_hasAlpha; + ensureCreated(); + + if (!ctx->d_ptr->fbo) + glGenFramebuffers(1, &ctx->d_ptr->fbo); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, ctx->d_ptr->fbo); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, m_texture.id, 0); + + if (!other->m_renderFbo->isBound()) + glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, other->m_renderFbo->handle()); + + glDisable(GL_SCISSOR_TEST); + if (ctx->d_ptr->active_engine && ctx->d_ptr->active_engine->type() == QPaintEngine::OpenGL2) + static_cast<QGL2PaintEngineEx *>(ctx->d_ptr->active_engine)->invalidateState(); + + glBlitFramebufferEXT(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height(), + 0, 0, w, h, + GL_COLOR_BUFFER_BIT, + GL_NEAREST); + + glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo); + } else { + QPixmapData::copy(data, rect); + } +} + +void QGLPixmapData::fill(const QColor &color) +{ + if (!isValid()) + return; + + bool hasAlpha = color.alpha() != 255; + if (hasAlpha && !m_hasAlpha) { + if (m_texture.id) { + destroyTexture(); + m_dirty = true; + } + m_hasAlpha = color.alpha() != 255; + } + + if (useFramebufferObjects()) { + m_source = QImage(); + m_hasFillColor = true; + m_fillColor = color; + } else { + + if (m_source.isNull()) { + m_fillColor = color; + m_hasFillColor = true; + + } else if (m_source.depth() == 32) { + m_source.fill(PREMUL(color.rgba())); + + } else if (m_source.depth() == 1) { + if (color == Qt::color1) + m_source.fill(1); + else + m_source.fill(0); + } + } +} + +bool QGLPixmapData::hasAlphaChannel() const +{ + return m_hasAlpha; +} + +QImage QGLPixmapData::fillImage(const QColor &color) const +{ + QImage img; + if (pixelType() == BitmapType) { + img = QImage(w, h, QImage::Format_MonoLSB); + + img.setColorCount(2); + img.setColor(0, QColor(Qt::color0).rgba()); + img.setColor(1, QColor(Qt::color1).rgba()); + + if (color == Qt::color1) + img.fill(1); + else + img.fill(0); + } else { + img = QImage(w, h, + m_hasAlpha + ? QImage::Format_ARGB32_Premultiplied + : QImage::Format_RGB32); + img.fill(PREMUL(color.rgba())); + } + return img; +} + +extern QImage qt_gl_read_texture(const QSize &size, bool alpha_format, bool include_alpha); + +QImage QGLPixmapData::toImage() const +{ + if (!isValid()) + return QImage(); + + if (m_renderFbo) { + copyBackFromRenderFbo(true); + } else if (!m_source.isNull()) { + QImageData *data = const_cast<QImage &>(m_source).data_ptr(); + if (data->paintEngine && data->paintEngine->isActive() + && data->paintEngine->paintDevice() == &m_source) + { + return m_source.copy(); + } + return m_source; + } else if (m_dirty || m_hasFillColor) { + return fillImage(m_fillColor); + } else { + ensureCreated(); + } + + QGLShareContextScope ctx(qt_gl_share_widget()->context()); + glBindTexture(GL_TEXTURE_2D, m_texture.id); + return qt_gl_read_texture(QSize(w, h), true, true); +} + +struct TextureBuffer +{ + QGLFramebufferObject *fbo; + QGL2PaintEngineEx *engine; +}; + +Q_GLOBAL_STATIC(QGLFramebufferObjectPool, _qgl_fbo_pool) +QGLFramebufferObjectPool* qgl_fbo_pool() +{ + return _qgl_fbo_pool(); +} + +void QGLPixmapData::copyBackFromRenderFbo(bool keepCurrentFboBound) const +{ + if (!isValid()) + return; + + m_hasFillColor = false; + + const QGLContext *share_ctx = qt_gl_share_widget()->context(); + QGLShareContextScope ctx(share_ctx); + + ensureCreated(); + + if (!ctx->d_ptr->fbo) + glGenFramebuffers(1, &ctx->d_ptr->fbo); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, ctx->d_ptr->fbo); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, m_texture.id, 0); + + const int x0 = 0; + const int x1 = w; + const int y0 = 0; + const int y1 = h; + + if (!m_renderFbo->isBound()) + glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, m_renderFbo->handle()); + + glDisable(GL_SCISSOR_TEST); + + glBlitFramebufferEXT(x0, y0, x1, y1, + x0, y0, x1, y1, + GL_COLOR_BUFFER_BIT, + GL_NEAREST); + + if (keepCurrentFboBound) { + glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo); + } else { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, m_renderFbo->handle()); + ctx->d_ptr->current_fbo = m_renderFbo->handle(); + } +} + +bool QGLPixmapData::useFramebufferObjects() const +{ +#ifdef Q_OS_SYMBIAN + // We don't want to use FBOs on Symbian + return false; +#else + return QGLFramebufferObject::hasOpenGLFramebufferObjects() + && QGLFramebufferObject::hasOpenGLFramebufferBlit() + && qt_gl_preferGL2Engine() + && (w * h > 32*32); // avoid overhead of FBOs for small pixmaps +#endif +} + +QPaintEngine* QGLPixmapData::paintEngine() const +{ + if (!isValid()) + return 0; + + if (m_renderFbo) + return m_engine; + + if (useFramebufferObjects()) { + extern QGLWidget* qt_gl_share_widget(); + + if (!QGLContext::currentContext()) + qt_gl_share_widget()->makeCurrent(); + QGLShareContextScope ctx(qt_gl_share_widget()->context()); + + QGLFramebufferObjectFormat format; + format.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + format.setSamples(4); + format.setInternalTextureFormat(GLenum(m_hasAlpha ? GL_RGBA : GL_RGB)); + + m_renderFbo = qgl_fbo_pool()->acquire(size(), format); + + if (m_renderFbo) { + if (!m_engine) + m_engine = new QGL2PaintEngineEx; + return m_engine; + } + + qWarning() << "Failed to create pixmap texture buffer of size " << size() << ", falling back to raster paint engine"; + } + + // If the application wants to paint into the QPixmap, we first + // force it to QImage format and then paint into that. + // This is simpler than juggling multiple GL contexts. + const_cast<QGLPixmapData *>(this)->forceToImage(); + + if (m_hasFillColor) { + m_source.fill(PREMUL(m_fillColor.rgba())); + m_hasFillColor = false; + } + return m_source.paintEngine(); +} + +extern QRgb qt_gl_convertToGLFormat(QRgb src_pixel, GLenum texture_format); + +// If copyBack is true, bind will copy the contents of the render +// FBO to the texture (which is not bound to the texture, as it's +// a multisample FBO). +GLuint QGLPixmapData::bind(bool copyBack) const +{ + if (m_renderFbo && copyBack) { + copyBackFromRenderFbo(true); + } else { + ensureCreated(); + } + + GLuint id = m_texture.id; + glBindTexture(GL_TEXTURE_2D, id); + + if (m_hasFillColor) { + if (!useFramebufferObjects()) { + m_source = QImage(w, h, QImage::Format_ARGB32_Premultiplied); + m_source.fill(PREMUL(m_fillColor.rgba())); + } + + m_hasFillColor = false; + + GLenum format = qt_gl_preferredTextureFormat(); + QImage tx(w, h, QImage::Format_ARGB32_Premultiplied); + tx.fill(qt_gl_convertToGLFormat(m_fillColor.rgba(), format)); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, format, GL_UNSIGNED_BYTE, tx.bits()); + } + + return id; +} + +QGLTexture* QGLPixmapData::texture() const +{ + return &m_texture; +} + +void QGLPixmapData::detachTextureFromPool() +{ + if (inTexturePool) { + QGLTexturePool::instance()->detachTexture(this); + inTexturePool = false; + } +} + +void QGLPixmapData::hibernate() +{ + // If the texture was imported (e.g, from an SgImage under Symbian), + // then we cannot copy it back to main memory for storage. + if (m_texture.id && m_source.isNull()) + return; + + forceToImage(); + destroyTexture(); +} + +void QGLPixmapData::reclaimTexture() +{ + if (!inTexturePool) + return; + forceToImage(); + destroyTexture(); +} + +Q_GUI_EXPORT int qt_defaultDpiX(); +Q_GUI_EXPORT int qt_defaultDpiY(); + +int QGLPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const +{ + if (w == 0) + return 0; + + switch (metric) { + case QPaintDevice::PdmWidth: + return w; + case QPaintDevice::PdmHeight: + return h; + case QPaintDevice::PdmNumColors: + return 0; + case QPaintDevice::PdmDepth: + return d; + case QPaintDevice::PdmWidthMM: + return qRound(w * 25.4 / qt_defaultDpiX()); + case QPaintDevice::PdmHeightMM: + return qRound(h * 25.4 / qt_defaultDpiY()); + case QPaintDevice::PdmDpiX: + case QPaintDevice::PdmPhysicalDpiX: + return qt_defaultDpiX(); + case QPaintDevice::PdmDpiY: + case QPaintDevice::PdmPhysicalDpiY: + return qt_defaultDpiY(); + default: + qWarning("QGLPixmapData::metric(): Invalid metric"); + return 0; + } +} + +// Force the pixmap data to be backed by some valid data. +void QGLPixmapData::forceToImage() +{ + if (!isValid()) + return; + + if (m_source.isNull()) + m_source = QImage(w, h, QImage::Format_ARGB32_Premultiplied); + + m_dirty = true; +} + +QGLPaintDevice *QGLPixmapData::glDevice() const +{ + return &m_glDevice; +} + +QT_END_NAMESPACE diff --git a/src/opengl/qwindowsurface_gl.cpp b/src/opengl/qwindowsurface_gl.cpp index 21b2f09..93e720c 100644 --- a/src/opengl/qwindowsurface_gl.cpp +++ b/src/opengl/qwindowsurface_gl.cpp @@ -181,17 +181,15 @@ QGLGraphicsSystem::QGLGraphicsSystem(bool useX11GL) // // QGLWindowSurface // - #ifndef Q_WS_QPA class QGLGlobalShareWidget { public: - QGLGlobalShareWidget() : widget(0), initializing(false) {} + QGLGlobalShareWidget() : firstPixmap(0), widgetRefCount(0), widget(0), initializing(false) {} QGLWidget *shareWidget() { if (!initializing && !widget && !cleanedUp) { initializing = true; - widget = new QGLWidget(QGLFormat(QGL::SingleBuffer | QGL::NoDepthBuffer | QGL::NoStencilBuffer)); widget->resize(1, 1); @@ -227,6 +225,9 @@ public: static bool cleanedUp; + QGLPixmapData *firstPixmap; + int widgetRefCount; + private: QGLWidget *widget; bool initializing; @@ -274,6 +275,43 @@ const QGLContext *qt_gl_share_context() #endif } +#ifdef QGL_USE_TEXTURE_POOL +void qt_gl_register_pixmap(QGLPixmapData *pd) +{ + QGLGlobalShareWidget *shared = _qt_gl_share_widget(); + pd->next = shared->firstPixmap; + pd->prev = 0; + if (shared->firstPixmap) + shared->firstPixmap->prev = pd; + shared->firstPixmap = pd; +} + +void qt_gl_unregister_pixmap(QGLPixmapData *pd) +{ + if (pd->next) + pd->next->prev = pd->prev; + if (pd->prev) { + pd->prev->next = pd->next; + } else { + QGLGlobalShareWidget *shared = _qt_gl_share_widget(); + if (shared) + shared->firstPixmap = pd->next; + } +} + +void qt_gl_hibernate_pixmaps() +{ + QGLGlobalShareWidget *shared = _qt_gl_share_widget(); + + // Scan all QGLPixmapData objects in the system and hibernate them. + QGLPixmapData *pd = shared->firstPixmap; + while (pd != 0) { + pd->hibernate(); + pd = pd->next; + } +} +#endif + struct QGLWindowSurfacePrivate { QGLFramebufferObject *fbo; @@ -366,6 +404,27 @@ QGLWindowSurface::~QGLWindowSurface() delete d_ptr->pb; delete d_ptr->fbo; delete d_ptr; + + if (QGLGlobalShareWidget::cleanedUp) + return; + + --(_qt_gl_share_widget()->widgetRefCount); + +#ifdef QGL_USE_TEXTURE_POOL + if (_qt_gl_share_widget()->widgetRefCount <= 0) { + // All of the widget window surfaces have been destroyed + // but we still have GL pixmaps active. Ask them to hibernate + // to free up GPU resources until a widget is shown again. + // This may eventually cause the EGLContext to be destroyed + // because nothing in the system needs a context, which will + // free up even more GPU resources. + qt_gl_hibernate_pixmaps(); + + // Destroy the context if necessary. + if (!qt_gl_share_widget()->context()->isSharing()) + qt_destroy_gl_share_widget(); + } +#endif } void QGLWindowSurface::deleted(QObject *object) @@ -415,6 +474,9 @@ void QGLWindowSurface::hijackWindow(QWidget *widget) ctx->create(qt_gl_share_context()); + if (widget != qt_gl_share_widget()) + ++(_qt_gl_share_widget()->widgetRefCount); + #ifndef QT_NO_EGL static bool checkedForNOKSwapRegion = false; static bool haveNOKSwapRegion = false; @@ -426,9 +488,9 @@ void QGLWindowSurface::hijackWindow(QWidget *widget) if (haveNOKSwapRegion) qDebug() << "Found EGL_NOK_swap_region2 extension. Using partial updates."; } - - if (ctx->d_func()->eglContext->configAttrib(EGL_SWAP_BEHAVIOR) != EGL_BUFFER_PRESERVED && - ! haveNOKSwapRegion) + bool swapBehaviourPreserved = (ctx->d_func()->eglContext->configAttrib(EGL_SWAP_BEHAVIOR) + || (ctx->d_func()->eglContext->configAttrib(EGL_SURFACE_TYPE)&EGL_SWAP_BEHAVIOR_PRESERVED_BIT)); + if (!swapBehaviourPreserved && !haveNOKSwapRegion) setPartialUpdateSupport(false); // Force full-screen updates else setPartialUpdateSupport(true); @@ -444,7 +506,9 @@ void QGLWindowSurface::hijackWindow(QWidget *widget) voidPtrPtr = &widgetPrivate->extraData()->glContext; d_ptr->contexts << ctxPtrPtr; +#ifndef Q_OS_SYMBIAN qDebug() << "hijackWindow() context created for" << widget << d_ptr->contexts.size(); +#endif } QGLContext *QGLWindowSurface::context() const @@ -943,8 +1007,9 @@ void QGLWindowSurface::updateGeometry() { if (d_ptr->destructive_swap_buffers) initializeOffscreenTexture(surfSize); #endif - - qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this;; +#ifndef Q_OS_SYMBIAN + qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this; +#endif d_ptr->ctx = ctx; d_ptr->ctx->d_ptr->internal_context = true; } diff --git a/src/plugins/graphicssystems/meego/qmeegographicssystem.cpp b/src/plugins/graphicssystems/meego/qmeegographicssystem.cpp index 13eab7f..18a0944 100644 --- a/src/plugins/graphicssystems/meego/qmeegographicssystem.cpp +++ b/src/plugins/graphicssystems/meego/qmeegographicssystem.cpp @@ -63,6 +63,8 @@ bool QMeeGoGraphicsSystem::surfaceWasCreated = false; QHash <Qt::HANDLE, QPixmap*> QMeeGoGraphicsSystem::liveTexturePixmaps; +QList<QMeeGoSwitchCallback> QMeeGoGraphicsSystem::switchCallbacks; + QMeeGoGraphicsSystem::QMeeGoGraphicsSystem() { qDebug("Using the meego graphics system"); @@ -74,6 +76,78 @@ QMeeGoGraphicsSystem::~QMeeGoGraphicsSystem() qt_destroy_gl_share_widget(); } +class QMeeGoGraphicsSystemSwitchHandler : public QObject +{ + Q_OBJECT +public: + QMeeGoGraphicsSystemSwitchHandler(); + + void addWidget(QWidget *widget); + bool eventFilter(QObject *, QEvent *); + +private slots: + void removeWidget(QObject *object); + +private: + int visibleWidgets() const; + +private: + QList<QWidget *> m_widgets; +}; + +QMeeGoGraphicsSystemSwitchHandler::QMeeGoGraphicsSystemSwitchHandler() +{ +} + +void QMeeGoGraphicsSystemSwitchHandler::addWidget(QWidget *widget) +{ + if (!m_widgets.contains(widget)) { + widget->installEventFilter(this); + connect(widget, SIGNAL(destroyed(QObject *)), this, SLOT(removeWidget(QObject *))); + m_widgets << widget; + } +} + +void QMeeGoGraphicsSystemSwitchHandler::removeWidget(QObject *object) +{ + m_widgets.removeOne(static_cast<QWidget *>(object)); +} + +int QMeeGoGraphicsSystemSwitchHandler::visibleWidgets() const +{ + int count = 0; + for (int i = 0; i < m_widgets.size(); ++i) + count += m_widgets.at(i)->isVisible() && !(m_widgets.at(i)->windowState() & Qt::WindowMinimized); + return count; +} + +bool QMeeGoGraphicsSystemSwitchHandler::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::WindowStateChange) { + QWindowStateChangeEvent *change = static_cast<QWindowStateChangeEvent *>(event); + QWidget *widget = static_cast<QWidget *>(object); + + Qt::WindowStates current = widget->windowState(); + Qt::WindowStates old = change->oldState(); + + // did minimized flag change? + if ((current ^ old) & Qt::WindowMinimized) { + if (current & Qt::WindowMinimized) { + if (visibleWidgets() == 0) + QMeeGoGraphicsSystem::switchToRaster(); + } else { + if (visibleWidgets() == 1) + QMeeGoGraphicsSystem::switchToMeeGo(); + } + } + } + + // resume processing of event + return false; +} + +Q_GLOBAL_STATIC(QMeeGoGraphicsSystemSwitchHandler, switch_handler) + QWindowSurface* QMeeGoGraphicsSystem::createWindowSurface(QWidget *widget) const { QGLWidget *shareWidget = qt_gl_share_widget(); @@ -83,6 +157,9 @@ QWindowSurface* QMeeGoGraphicsSystem::createWindowSurface(QWidget *widget) const QGLShareContextScope ctx(shareWidget->context()); + if (QApplicationPrivate::instance()->graphics_system_name == QLatin1String("runtime")) + switch_handler()->addWidget(widget); + QMeeGoGraphicsSystem::surfaceWasCreated = true; QWindowSurface *surface = new QGLWindowSurface(widget); return surface; @@ -203,18 +280,7 @@ QPixmapData *QMeeGoGraphicsSystem::pixmapDataWithGLTexture(int w, int h) bool QMeeGoGraphicsSystem::meeGoRunning() { - if (! QApplicationPrivate::instance()) { - qWarning("Application not running just yet... hard to know what system running!"); - return false; - } - - QString name = QApplicationPrivate::instance()->graphics_system_name; - if (name == "runtime") { - QRuntimeGraphicsSystem *rsystem = (QRuntimeGraphicsSystem *) QApplicationPrivate::instance()->graphics_system; - name = rsystem->graphicsSystemName(); - } - - return (name == "meego"); + return runningGraphicsSystemName() == "meego"; } QPixmapData* QMeeGoGraphicsSystem::pixmapDataWithNewLiveTexture(int w, int h, QImage::Format format) @@ -259,6 +325,69 @@ void QMeeGoGraphicsSystem::destroyFenceSync(void *fenceSync) QMeeGoExtensions::eglDestroySyncKHR(QEgl::display(), fenceSync); } +QString QMeeGoGraphicsSystem::runningGraphicsSystemName() +{ + if (!QApplicationPrivate::instance()) { + qWarning("Querying graphics system but application not running yet!"); + return QString(); + } + + QString name = QApplicationPrivate::instance()->graphics_system_name; + if (name == QLatin1String("runtime")) { + QRuntimeGraphicsSystem *rsystem = (QRuntimeGraphicsSystem *) QApplicationPrivate::instance()->graphics_system; + name = rsystem->graphicsSystemName(); + } + + return name; +} + +void QMeeGoGraphicsSystem::switchToMeeGo() +{ + if (meeGoRunning()) + return; + + if (QApplicationPrivate::instance()->graphics_system_name != QLatin1String("runtime")) + qWarning("Can't switch to meego - switching only supported with 'runtime' graphics system."); + else { + triggerSwitchCallbacks(0, "meego"); + + QApplication *app = static_cast<QApplication *>(QCoreApplication::instance()); + app->setGraphicsSystem(QLatin1String("meego")); + + triggerSwitchCallbacks(1, "meego"); + } +} + +void QMeeGoGraphicsSystem::switchToRaster() +{ + if (runningGraphicsSystemName() == QLatin1String("raster")) + return; + + if (QApplicationPrivate::instance()->graphics_system_name != QLatin1String("runtime")) + qWarning("Can't switch to raster - switching only supported with 'runtime' graphics system."); + else { + triggerSwitchCallbacks(0, "raster"); + + QApplication *app = static_cast<QApplication *>(QCoreApplication::instance()); + app->setGraphicsSystem(QLatin1String("raster")); + + QMeeGoLivePixmapData::invalidateSurfaces(); + + triggerSwitchCallbacks(1, "raster"); + } +} + +void QMeeGoGraphicsSystem::registerSwitchCallback(QMeeGoSwitchCallback callback) +{ + switchCallbacks << callback; +} + +void QMeeGoGraphicsSystem::triggerSwitchCallbacks(int type, const char *name) +{ + for (int i = 0; i < switchCallbacks.size(); ++i) + switchCallbacks.at(i)(type, name); +} + /* C API */ int qt_meego_image_to_egl_shared_image(const QImage &image) @@ -340,3 +469,20 @@ void qt_meego_invalidate_live_surfaces(void) { return QMeeGoLivePixmapData::invalidateSurfaces(); } + +void qt_meego_switch_to_raster(void) +{ + QMeeGoGraphicsSystem::switchToRaster(); +} + +void qt_meego_switch_to_meego(void) +{ + QMeeGoGraphicsSystem::switchToMeeGo(); +} + +void qt_meego_register_switch_callback(QMeeGoSwitchCallback callback) +{ + QMeeGoGraphicsSystem::registerSwitchCallback(callback); +} + +#include "qmeegographicssystem.moc" diff --git a/src/plugins/graphicssystems/meego/qmeegographicssystem.h b/src/plugins/graphicssystems/meego/qmeegographicssystem.h index 27a4e7a..ecc85b2 100644 --- a/src/plugins/graphicssystems/meego/qmeegographicssystem.h +++ b/src/plugins/graphicssystems/meego/qmeegographicssystem.h @@ -47,6 +47,8 @@ #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> +extern "C" typedef void (*QMeeGoSwitchCallback)(int type, const char *name); + class QMeeGoGraphicsSystem : public QGraphicsSystem { public: @@ -76,13 +78,22 @@ public: static void* createFenceSync(); static void destroyFenceSync(void* fenceSync); + static void switchToRaster(); + static void switchToMeeGo(); + static QString runningGraphicsSystemName(); + + static void registerSwitchCallback(QMeeGoSwitchCallback callback); + private: static bool meeGoRunning(); static EGLSurface getSurfaceForLiveTexturePixmap(QPixmap *pixmap); static void destroySurfaceForLiveTexturePixmap(QPixmapData* pmd); + static void triggerSwitchCallbacks(int type, const char *name); static bool surfaceWasCreated; - static QHash <Qt::HANDLE, QPixmap*> liveTexturePixmaps; + static QHash<Qt::HANDLE, QPixmap*> liveTexturePixmaps; + static QList<QMeeGoSwitchCallback> switchCallbacks; + }; /* C api */ @@ -95,7 +106,7 @@ extern "C" { Q_DECL_EXPORT bool qt_meego_destroy_egl_shared_image(Qt::HANDLE handle); Q_DECL_EXPORT void qt_meego_set_surface_fixed_size(int width, int height); Q_DECL_EXPORT void qt_meego_set_surface_scaling(int x, int y, int width, int height); - Q_DECL_EXPORT void qt_meego_set_translucent(bool translucent); + Q_DECL_EXPORT void qt_meego_set_translucent(bool translucent); Q_DECL_EXPORT QPixmapData* qt_meego_pixmapdata_with_new_live_texture(int w, int h, QImage::Format format); Q_DECL_EXPORT QPixmapData* qt_meego_pixmapdata_from_live_texture_handle(Qt::HANDLE handle); Q_DECL_EXPORT QImage* qt_meego_live_texture_lock(QPixmap *pixmap, void *fenceSync); @@ -104,6 +115,9 @@ extern "C" { Q_DECL_EXPORT void* qt_meego_create_fence_sync(void); Q_DECL_EXPORT void qt_meego_destroy_fence_sync(void* fs); Q_DECL_EXPORT void qt_meego_invalidate_live_surfaces(void); + Q_DECL_EXPORT void qt_meego_switch_to_raster(void); + Q_DECL_EXPORT void qt_meego_switch_to_meego(void); + Q_DECL_EXPORT void qt_meego_register_switch_callback(QMeeGoSwitchCallback callback); } #endif diff --git a/src/plugins/graphicssystems/meego/qmeegolivepixmapdata.cpp b/src/plugins/graphicssystems/meego/qmeegolivepixmapdata.cpp index 2a2a098..0970b89 100644 --- a/src/plugins/graphicssystems/meego/qmeegolivepixmapdata.cpp +++ b/src/plugins/graphicssystems/meego/qmeegolivepixmapdata.cpp @@ -219,7 +219,7 @@ QImage* QMeeGoLivePixmapData::lock(EGLSyncKHR fenceSync) return &lockedImage; } - lockedImage = QImage((uchar *) data, width(), height(), format); + lockedImage = QImage((uchar *) data, width(), height(), pitch, format); return &lockedImage; } |