diff options
author | Qt Continuous Integration System <qt-info@nokia.com> | 2011-03-03 18:47:14 (GMT) |
---|---|---|
committer | Qt Continuous Integration System <qt-info@nokia.com> | 2011-03-03 18:47:14 (GMT) |
commit | c968e6602aef04aba4b68af205cd29ad18e0a823 (patch) | |
tree | 5dee68c5bd2e691218bd45c7b74da7c7c16b22cd | |
parent | 5414957f0feebe24a2aa0ac7c5ec32e53e49b57a (diff) | |
parent | 8ddf96a15f707001755d5b934f3f900b825b5887 (diff) | |
download | Qt-c968e6602aef04aba4b68af205cd29ad18e0a823.zip Qt-c968e6602aef04aba4b68af205cd29ad18e0a823.tar.gz Qt-c968e6602aef04aba4b68af205cd29ad18e0a823.tar.bz2 |
Merge branch 'qt-master-from-4.7' of scm.dev.nokia.troll.no:qt/qt-integration into master-integration
* 'qt-master-from-4.7' of scm.dev.nokia.troll.no:qt/qt-integration: (133 commits)
Use the generic QVoltileImage implementation on S60 3.1 and 3.2.
Position the TextInput cursor correctly after IM text is commited.
fix DLLDESTDIR handling in nmake generator when QMAKE_POST_LINK is used
Make the TextInput cursorRectangle relative to the item.
Don't assert if font engine creates new glyphs
Doc: remove experimental GestureArea from contents and add PinchArea
Add QML file for TextEdit positionAt test.
un-document semicolon-splitting of INCLUDEPATH
Changed some parts of QVolatileImage autotest to be Symbian specific.
Have QVolatileImage backed by a bitmap in all cases.
Removing extra tabs from def file.
Don't return positions within preedit text from positionAt().
Add an is input method composing property to TextEdit and TextInput.
Fix style issue in qvgimagepool.cpp
Add internal function for releasing cached GPU resources.
Changed QPixmap VG backend to use CFbsBitmap on Symbian.
Fix typo in qglthreads auto test.
Add selected P1 tasks to changes file.
My 4.7.2 changes (cherry picked from commit ab38731fe5dcfaa1a7a70bc290a8856b5b01524d)
4.7.2 changes
...
39 files changed, 2070 insertions, 181 deletions
diff --git a/doc/src/declarative/elements.qdoc b/doc/src/declarative/elements.qdoc index 466b940..e2d9350 100644 --- a/doc/src/declarative/elements.qdoc +++ b/doc/src/declarative/elements.qdoc @@ -76,7 +76,7 @@ To see the QML elements listed by functional area, see the \o \l {FocusScope} - Element that mediate keyboard focus changes \o \l {Flickable} - Provides a surface that can be "flicked" \o \l {Flipable} - Provides a surface that produces "flipping" effects -\o \l {GestureArea} - Enables simple gesture handling +\o \l {PinchArea} - Enables simple pinch gesture handling \endlist \section1 Positioners and Repeater diff --git a/doc/src/declarative/qmlinuse.qdoc b/doc/src/declarative/qmlinuse.qdoc index 7380ef5..131be1b 100644 --- a/doc/src/declarative/qmlinuse.qdoc +++ b/doc/src/declarative/qmlinuse.qdoc @@ -207,7 +207,7 @@ <li><a href="qml-flipable.html">Flipable Element</a> - The Flipable item provides a surface that can be flipped or reflected.</li> <li><a href="qml-focuspanel.html">FocusPanel Element</a> - The FocusPanel item explicitly creates a focus panel.</li> <li><a href="qml-focusscope.html">FocusScope Element</a> - The FocusScope object explicitly creates a focus scope for focus management.</li> - <li><a href="qml-gesturearea.html">GestureArea Element</a> - The GestureArea item enables simple gesture handling.</li> + <li><a href="qml-pincharea.html">PinchArea Element</a> - The PinchArea item enables simple pinch gesture handling.</li> <li><a href="qml-keynavigation.html">KeyNavigation Element</a> - The KeyNavigation attached property supports key navigation by arrow keys.</li> <li><a href="qml-keys.html">Keys Element</a> - The Keys attached property provides key handling to Items.</li> <li><a href="qml-mousearea.html">MouseArea Element</a> - The MouseArea item enables simple mouse handling.</li> diff --git a/doc/src/development/qmake-manual.qdoc b/doc/src/development/qmake-manual.qdoc index f2ca21e..1c5d903 100644 --- a/doc/src/development/qmake-manual.qdoc +++ b/doc/src/development/qmake-manual.qdoc @@ -1647,8 +1647,7 @@ \section1 INCLUDEPATH This variable specifies the #include directories which should be - searched when compiling the project. Use ';' or a space as the - directory separator. + searched when compiling the project. For example: diff --git a/qmake/generators/win32/winmakefile.cpp b/qmake/generators/win32/winmakefile.cpp index 603ffb5..c85533b 100644 --- a/qmake/generators/win32/winmakefile.cpp +++ b/qmake/generators/win32/winmakefile.cpp @@ -701,10 +701,10 @@ void Win32MakefileGenerator::writeStandardParts(QTextStream &t) if(project->isActiveConfig("shared") && !project->values("DLLDESTDIR").isEmpty()) { QStringList dlldirs = project->values("DLLDESTDIR"); for (QStringList::Iterator dlldir = dlldirs.begin(); dlldir != dlldirs.end(); ++dlldir) { - t << "\n\t" << "-$(COPY_FILE) \"$(DESTDIR_TARGET)\" " << Option::fixPathToTargetOS(*dlldir, false); + t << "\t" << "-$(COPY_FILE) \"$(DESTDIR_TARGET)\" " << Option::fixPathToTargetOS(*dlldir, false) << endl; } } - t << endl << endl; + t << endl; writeRcFilePart(t); diff --git a/src/declarative/graphicsitems/qdeclarativetextedit.cpp b/src/declarative/graphicsitems/qdeclarativetextedit.cpp index 59921d5..c28d7f4 100644 --- a/src/declarative/graphicsitems/qdeclarativetextedit.cpp +++ b/src/declarative/graphicsitems/qdeclarativetextedit.cpp @@ -606,6 +606,22 @@ int QDeclarativeTextEdit::positionAt(int x, int y) const { Q_D(const QDeclarativeTextEdit); int r = d->document->documentLayout()->hitTest(QPoint(x,y-d->yoff), Qt::FuzzyHit); + QTextCursor cursor = d->control->textCursor(); + if (r > cursor.position()) { + // The cursor position includes positions within the preedit text, but only positions in the + // same text block are offset so it is possible to get a position that is either part of the + // preedit or the next text block. + QTextLayout *layout = cursor.block().layout(); + const int preeditLength = layout + ? layout->preeditAreaText().length() + : 0; + if (preeditLength > 0 + && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x,y-d->yoff)) { + r = r > cursor.position() + preeditLength + ? r - preeditLength + : cursor.position(); + } + } return r; } @@ -1318,7 +1334,10 @@ Handles the given input method \a event. void QDeclarativeTextEdit::inputMethodEvent(QInputMethodEvent *event) { Q_D(QDeclarativeTextEdit); + const bool wasComposing = isInputMethodComposing(); d->control->processEvent(event, QPointF(0, -d->yoff)); + if (wasComposing != isInputMethodComposing()) + emit inputMethodComposingChanged(); } /*! @@ -1394,6 +1413,27 @@ bool QDeclarativeTextEdit::canPaste() const return d->canPaste; } +/*! + \qmlproperty bool TextEdit::isInputMethodComposing() + + \since QtQuick 1.1 + + This property holds whether the TextEdit has partial text input from an + input method. + + While it is composing an input method may rely on mouse or key events from + the TextEdit to edit or commit the partial text. This property can be used + to determine when to disable events handlers that may interfere with the + correct operation of an input method. +*/ +bool QDeclarativeTextEdit::isInputMethodComposing() const +{ + Q_D(const QDeclarativeTextEdit); + if (QTextLayout *layout = d->control->textCursor().block().layout()) + return layout->preeditAreaText().length() > 0; + return false; +} + void QDeclarativeTextEditPrivate::init() { Q_Q(QDeclarativeTextEdit); diff --git a/src/declarative/graphicsitems/qdeclarativetextedit_p.h b/src/declarative/graphicsitems/qdeclarativetextedit_p.h index 7785a7a..612f9a9 100644 --- a/src/declarative/graphicsitems/qdeclarativetextedit_p.h +++ b/src/declarative/graphicsitems/qdeclarativetextedit_p.h @@ -94,6 +94,7 @@ class Q_AUTOTEST_EXPORT QDeclarativeTextEdit : public QDeclarativeImplicitSizePa Q_PROPERTY(bool selectByMouse READ selectByMouse WRITE setSelectByMouse NOTIFY selectByMouseChanged) Q_PROPERTY(SelectionMode mouseSelectionMode READ mouseSelectionMode WRITE setMouseSelectionMode NOTIFY mouseSelectionModeChanged REVISION 1) Q_PROPERTY(bool canPaste READ canPaste NOTIFY canPasteChanged REVISION 1) + Q_PROPERTY(bool inputMethodComposing READ isInputMethodComposing NOTIFY inputMethodComposingChanged REVISION 1) public: QDeclarativeTextEdit(QDeclarativeItem *parent=0); @@ -215,6 +216,8 @@ public: QRectF boundingRect() const; + bool isInputMethodComposing() const; + Q_SIGNALS: void textChanged(const QString &); void paintedSizeChanged(); @@ -242,6 +245,7 @@ Q_SIGNALS: Q_REVISION(1) void mouseSelectionModeChanged(SelectionMode mode); Q_REVISION(1) void linkActivated(const QString &link); Q_REVISION(1) void canPasteChanged(); + Q_REVISION(1) void inputMethodComposingChanged(); public Q_SLOTS: void selectAll(); diff --git a/src/declarative/graphicsitems/qdeclarativetextinput.cpp b/src/declarative/graphicsitems/qdeclarativetextinput.cpp index 65ed5c6..dc44bfe 100644 --- a/src/declarative/graphicsitems/qdeclarativetextinput.cpp +++ b/src/declarative/graphicsitems/qdeclarativetextinput.cpp @@ -482,6 +482,7 @@ QRect QDeclarativeTextInput::cursorRectangle() const Q_D(const QDeclarativeTextInput); QRect r = d->control->cursorRect(); r.setHeight(r.height()-1); // Make consistent with TextEdit (QLineControl inexplicably adds 1) + r.moveLeft(r.x() - d->hscroll); return r; } @@ -921,6 +922,8 @@ void QDeclarativeTextInput::moveCursor() QRectF QDeclarativeTextInput::positionToRectangle(int pos) const { Q_D(const QDeclarativeTextInput); + if (pos > d->control->cursorPosition()) + pos += d->control->preeditAreaText().length(); return QRectF(d->control->cursorToX(pos)-d->hscroll, 0.0, d->control->cursorWidth(), @@ -929,8 +932,7 @@ QRectF QDeclarativeTextInput::positionToRectangle(int pos) const int QDeclarativeTextInput::positionAt(int x) const { - Q_D(const QDeclarativeTextInput); - return d->control->xToPos(x + d->hscroll); + return positionAt(x, CursorBetweenCharacters); } /*! @@ -952,10 +954,18 @@ int QDeclarativeTextInput::positionAt(int x) const \o TextInput.CursorOnCharacter - Returns the position before the character that is nearest x. \endlist */ -int QDeclarativeTextInput::positionAt(int x, CursorPosition position) +int QDeclarativeTextInput::positionAt(int x, CursorPosition position) const { Q_D(const QDeclarativeTextInput); - return d->control->xToPos(x + d->hscroll, QTextLine::CursorPosition(position)); + int pos = d->control->xToPos(x + d->hscroll, QTextLine::CursorPosition(position)); + const int cursor = d->control->cursor(); + if (pos > cursor) { + const int preeditLength = d->control->preeditAreaText().length(); + pos = pos > cursor + preeditLength + ? pos - preeditLength + : cursor; + } + return pos; } void QDeclarativeTextInputPrivate::focusChanged(bool hasFocus) @@ -996,18 +1006,22 @@ void QDeclarativeTextInput::inputMethodEvent(QInputMethodEvent *ev) { Q_D(QDeclarativeTextInput); ev->ignore(); + const bool wasComposing = d->control->preeditAreaText().length() > 0; inputMethodPreHandler(ev); - if (ev->isAccepted()) - return; - if (d->control->isReadOnly()) { - ev->ignore(); - } else { - d->control->processInputMethodEvent(ev); - updateSize(); - d->updateHorizontalScroll(); + if (!ev->isAccepted()) { + if (d->control->isReadOnly()) { + ev->ignore(); + } else { + d->control->processInputMethodEvent(ev); + updateSize(); + d->updateHorizontalScroll(); + } } if (!ev->isAccepted()) QDeclarativePaintedItem::inputMethodEvent(ev); + + if (wasComposing != (d->control->preeditAreaText().length() > 0)) + emit inputMethodComposingChanged(); } /*! @@ -1061,7 +1075,7 @@ void QDeclarativeTextInput::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { Q_D(QDeclarativeTextInput); if (d->sendMouseEventToInputContext(event, QEvent::MouseMove)) - event->setAccepted(true); + return; if (d->selectByMouse) { if (qAbs(int(event->pos().x() - d->pressPos.x())) > QApplication::startDragDistance()) setKeepMouseGrab(true); @@ -1706,6 +1720,25 @@ void QDeclarativeTextInput::focusInEvent(QFocusEvent *event) QDeclarativePaintedItem::focusInEvent(event); } +/*! + \qmlproperty bool TextInput::isInputMethodComposing() + + \since QtQuick 1.1 + + This property holds whether the TextInput has partial text input from an + input method. + + While it is composing an input method may rely on mouse or key events from + the TextInput to edit or commit the partial text. This property can be + used to determine when to disable events handlers that may interfere with + the correct operation of an input method. +*/ +bool QDeclarativeTextInput::isInputMethodComposing() const +{ + Q_D(const QDeclarativeTextInput); + return d->control->preeditAreaText().length() > 0; +} + void QDeclarativeTextInputPrivate::init() { Q_Q(QDeclarativeTextInput); diff --git a/src/declarative/graphicsitems/qdeclarativetextinput_p.h b/src/declarative/graphicsitems/qdeclarativetextinput_p.h index c29be26..822d5e2 100644 --- a/src/declarative/graphicsitems/qdeclarativetextinput_p.h +++ b/src/declarative/graphicsitems/qdeclarativetextinput_p.h @@ -97,6 +97,7 @@ class Q_AUTOTEST_EXPORT QDeclarativeTextInput : public QDeclarativeImplicitSizeP Q_PROPERTY(bool selectByMouse READ selectByMouse WRITE setSelectByMouse NOTIFY selectByMouseChanged) Q_PROPERTY(SelectionMode mouseSelectionMode READ mouseSelectionMode WRITE setMouseSelectionMode NOTIFY mouseSelectionModeChanged REVISION 1) Q_PROPERTY(bool canPaste READ canPaste NOTIFY canPasteChanged REVISION 1) + Q_PROPERTY(bool inputMethodComposing READ isInputMethodComposing NOTIFY inputMethodComposingChanged REVISION 1) public: QDeclarativeTextInput(QDeclarativeItem* parent=0); @@ -127,7 +128,7 @@ public: //Auxilliary functions needed to control the TextInput from QML Q_INVOKABLE int positionAt(int x) const; - Q_INVOKABLE Q_REVISION(1) int positionAt(int x, CursorPosition position); + Q_INVOKABLE Q_REVISION(1) int positionAt(int x, CursorPosition position) const; Q_INVOKABLE QRectF positionToRectangle(int pos) const; Q_INVOKABLE void moveCursorSelection(int pos); Q_INVOKABLE Q_REVISION(1) void moveCursorSelection(int pos, SelectionMode mode); @@ -210,6 +211,8 @@ public: QRectF boundingRect() const; bool canPaste() const; + bool isInputMethodComposing() const; + Q_SIGNALS: void textChanged(); void cursorPositionChanged(); @@ -237,6 +240,7 @@ Q_SIGNALS: void selectByMouseChanged(bool selectByMouse); Q_REVISION(1) void mouseSelectionModeChanged(SelectionMode mode); Q_REVISION(1) void canPasteChanged(); + Q_REVISION(1) void inputMethodComposingChanged(); protected: virtual void geometryChanged(const QRectF &newGeometry, diff --git a/src/declarative/graphicsitems/qdeclarativetextlayout.cpp b/src/declarative/graphicsitems/qdeclarativetextlayout.cpp index b24dd2c..31819f5 100644 --- a/src/declarative/graphicsitems/qdeclarativetextlayout.cpp +++ b/src/declarative/graphicsitems/qdeclarativetextlayout.cpp @@ -99,7 +99,6 @@ class DrawTextItemRecorder: public QPaintEngine needFreshCurrentItem = false; last.numChars += ti.num_chars; - last.numGlyphs += ti.glyphs.numGlyphs; } } @@ -111,7 +110,7 @@ class DrawTextItemRecorder: public QPaintEngine currentItem.font = ti.font(); currentItem.charOffset = charOffset; currentItem.numChars = ti.num_chars; - currentItem.numGlyphs = ti.glyphs.numGlyphs; + currentItem.numGlyphs = 0; currentItem.glyphOffset = glyphOffset; currentItem.positionOffset = positionOffset; currentItem.useBackendOptimizations = m_useBackendOptimizations; @@ -121,6 +120,8 @@ class DrawTextItemRecorder: public QPaintEngine m_inertText->items.append(currentItem); } + QStaticTextItem ¤tItem = m_inertText->items.last(); + QTransform matrix = m_untransformedCoordinates ? QTransform() : state->transform(); matrix.translate(position.x(), position.y()); @@ -129,18 +130,18 @@ class DrawTextItemRecorder: public QPaintEngine ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); int size = glyphs.size(); - Q_ASSERT(size == ti.glyphs.numGlyphs); Q_ASSERT(size == positions.size()); + currentItem.numGlyphs += size; m_inertText->glyphs.resize(m_inertText->glyphs.size() + size); m_inertText->positions.resize(m_inertText->glyphs.size()); m_inertText->chars.resize(m_inertText->chars.size() + ti.num_chars); glyph_t *glyphsDestination = m_inertText->glyphs.data() + glyphOffset; - qMemCopy(glyphsDestination, glyphs.constData(), sizeof(glyph_t) * ti.glyphs.numGlyphs); + qMemCopy(glyphsDestination, glyphs.constData(), sizeof(glyph_t) * size); QFixedPoint *positionsDestination = m_inertText->positions.data() + positionOffset; - qMemCopy(positionsDestination, positions.constData(), sizeof(QFixedPoint) * ti.glyphs.numGlyphs); + qMemCopy(positionsDestination, positions.constData(), sizeof(QFixedPoint) * size); QChar *charsDestination = m_inertText->chars.data() + charOffset; qMemCopy(charsDestination, ti.chars, sizeof(QChar) * ti.num_chars); diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri index 92ea397..a5e37f3 100644 --- a/src/gui/image/image.pri +++ b/src/gui/image/image.pri @@ -29,7 +29,9 @@ HEADERS += \ image/qpixmapdata_p.h \ image/qpixmapdatafactory_p.h \ image/qpixmapfilter_p.h \ - image/qimagepixmapcleanuphooks_p.h + image/qimagepixmapcleanuphooks_p.h \ + image/qvolatileimage_p.h \ + image/qvolatileimagedata_p.h SOURCES += \ image/qbitmap.cpp \ @@ -53,7 +55,8 @@ SOURCES += \ image/qpixmap_raster.cpp \ image/qpixmap_blitter.cpp \ image/qnativeimage.cpp \ - image/qimagepixmapcleanuphooks.cpp + image/qimagepixmapcleanuphooks.cpp \ + image/qvolatileimage.cpp win32 { SOURCES += image/qpixmap_win.cpp @@ -77,6 +80,13 @@ else:symbian { SOURCES += image/qpixmap_s60.cpp } +!symbian|contains(S60_VERSION, 3.1)|contains(S60_VERSION, 3.2) { + SOURCES += image/qvolatileimagedata.cpp +} +else { + SOURCES += image/qvolatileimagedata_symbian.cpp +} + # Built-in image format support HEADERS += \ image/qbmphandler_p.h \ diff --git a/src/gui/image/qpixmapdata_p.h b/src/gui/image/qpixmapdata_p.h index 5c05c28..db5ff4a 100644 --- a/src/gui/image/qpixmapdata_p.h +++ b/src/gui/image/qpixmapdata_p.h @@ -71,7 +71,8 @@ public: #if defined(Q_OS_SYMBIAN) enum NativeType { FbsBitmap, - SgImage + SgImage, + VolatileImage }; #endif enum ClassId { RasterClass, X11Class, MacClass, DirectFBClass, diff --git a/src/gui/image/qvolatileimage.cpp b/src/gui/image/qvolatileimage.cpp new file mode 100644 index 0000000..098e9a1 --- /dev/null +++ b/src/gui/image/qvolatileimage.cpp @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** 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 QtGui 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 "qvolatileimage_p.h" +#include "qvolatileimagedata_p.h" +#include <QtGui/private/qpaintengine_raster_p.h> +#include <QtGui/private/qpixmapdata_p.h> + +QT_BEGIN_NAMESPACE + +class QVolatileImagePaintEnginePrivate : public QRasterPaintEnginePrivate +{ +public: + QVolatileImagePaintEnginePrivate() { } + QVolatileImage *img; +}; + +class QVolatileImagePaintEngine : public QRasterPaintEngine +{ + Q_DECLARE_PRIVATE(QVolatileImagePaintEngine) + +public: + QVolatileImagePaintEngine(QPaintDevice *device, QVolatileImage *img); + bool begin(QPaintDevice *device); + bool end(); + void drawPixmap(const QPointF &p, const QPixmap &pm); + void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); +}; + +QVolatileImage::QVolatileImage() + : d(new QVolatileImageData) +{ +} + +QVolatileImage::QVolatileImage(int w, int h, QImage::Format format) + : d(new QVolatileImageData(w, h, format)) +{ +} + +QVolatileImage::QVolatileImage(const QImage &sourceImage) + : d(new QVolatileImageData(sourceImage)) +{ +} + +QVolatileImage::QVolatileImage(void *nativeImage, void *nativeMask) + : d(new QVolatileImageData(nativeImage, nativeMask)) +{ +} + +// Need non-inline, non-autogenerated copy ctor, dtor, op= to keep the +// fwd declared QSharedData working. + +QVolatileImage::QVolatileImage(const QVolatileImage &other) + : d(other.d) +{ +} + +QVolatileImage::~QVolatileImage() +{ +} + +QVolatileImage &QVolatileImage::operator=(const QVolatileImage &rhs) +{ + d = rhs.d; + return *this; +} + +bool QVolatileImage::isNull() const +{ + return d->image.isNull(); +} + +QImage::Format QVolatileImage::format() const +{ + return d->image.format(); +} + +int QVolatileImage::width() const +{ + return d->image.width(); +} + +int QVolatileImage::height() const +{ + return d->image.height(); +} + +int QVolatileImage::bytesPerLine() const +{ + return d->image.bytesPerLine(); +} + +int QVolatileImage::byteCount() const +{ + return d->image.byteCount(); +} + +int QVolatileImage::depth() const +{ + return d->image.depth(); +} + +bool QVolatileImage::hasAlphaChannel() const +{ + return d->image.hasAlphaChannel(); +} + +void QVolatileImage::beginDataAccess() const +{ + d->beginDataAccess(); +} + +void QVolatileImage::endDataAccess(bool readOnly) const +{ + d->endDataAccess(readOnly); +} + +/*! + Access to pixel data via bits() or constBits() should be guarded by + begin/endDataAccess(). + */ +uchar *QVolatileImage::bits() +{ + return d->image.bits(); +} + +const uchar *QVolatileImage::constBits() const +{ + return d->image.constBits(); +} + +bool QVolatileImage::ensureFormat(QImage::Format format) +{ + return d->ensureFormat(format); +} + +/*! + This will always perform a copy of the pixel data. + */ +QImage QVolatileImage::toImage() const +{ + d->beginDataAccess(); + QImage newImage = d->image.copy(); // no sharing allowed + d->endDataAccess(true); + return newImage; +} + +/*! + Returns a reference to the image that is potentially using some native + buffer internally. Access to the image's pixel data should be guarded by + begin/endDataAccess(). Use it when there is a need for QImage APIs not provided + by this class. The returned QImage must never be shared or assigned to. + */ +QImage &QVolatileImage::imageRef() // non-const, in order to cause a detach +{ + d->ensureImage(); + return d->image; +} + +void *QVolatileImage::duplicateNativeImage() const +{ + return d->duplicateNativeImage(); +} + +void QVolatileImage::setAlphaChannel(const QPixmap &alphaChannel) +{ + ensureFormat(QImage::Format_ARGB32_Premultiplied); + beginDataAccess(); + imageRef().setAlphaChannel(alphaChannel.toImage()); + endDataAccess(); + d->ensureImage(); +} + +void QVolatileImage::fill(uint pixelValue) +{ + beginDataAccess(); + imageRef().fill(pixelValue); + endDataAccess(); + d->ensureImage(); +} + +void QVolatileImage::copyFrom(QVolatileImage *source, const QRect &rect) +{ + if (source->isNull()) { + return; + } + QRect r = rect; + if (rect.isNull()) { + r = QRect(0, 0, source->width(), source->height()); + } + source->beginDataAccess(); + QImage &srcImgRef(source->imageRef()); + int srcbpl = srcImgRef.bytesPerLine(); + int srcbpp = srcImgRef.depth() / 8; + const uchar *sptr = srcImgRef.constBits() + r.y() * srcbpl; + beginDataAccess(); + QImage &dstImgRef(imageRef()); + int dstbpl = dstImgRef.bytesPerLine(); + uchar *dptr = dstImgRef.bits(); + for (int y = 0; y < r.height(); ++y) { + qMemCopy(dptr, sptr + r.x() * srcbpp, r.width() * srcbpp); + sptr += srcbpl; + dptr += dstbpl; + } + endDataAccess(); + source->endDataAccess(true); +} + +/*! + To be called from the PixmapData's paintEngine(). + */ +QPaintEngine *QVolatileImage::paintEngine() +{ + if (!d->pengine) { + d->pengine = new QVolatileImagePaintEngine(&imageRef(), this); + } + return d->pengine; +} + +QVolatileImagePaintEngine::QVolatileImagePaintEngine(QPaintDevice *device, + QVolatileImage *img) + : QRasterPaintEngine(*(new QVolatileImagePaintEnginePrivate), device) +{ + Q_D(QVolatileImagePaintEngine); + d->img = img; +} + +bool QVolatileImagePaintEngine::begin(QPaintDevice *device) +{ + Q_D(QVolatileImagePaintEngine); + d->img->beginDataAccess(); + return QRasterPaintEngine::begin(device); +} + +bool QVolatileImagePaintEngine::end() +{ + Q_D(QVolatileImagePaintEngine); + bool ret = QRasterPaintEngine::end(); + d->img->endDataAccess(); + return ret; +} + +// For non-RasterClass pixmaps drawPixmap() would call toImage() which is slow in +// our case. Therefore drawPixmap() is rerouted to drawImage(). + +void QVolatileImagePaintEngine::drawPixmap(const QPointF &p, const QPixmap &pm) +{ +#ifdef Q_OS_SYMBIAN + void *nativeData = pm.pixmapData()->toNativeType(QPixmapData::VolatileImage); + if (nativeData) { + QVolatileImage *img = static_cast<QVolatileImage *>(nativeData); + img->beginDataAccess(); + QRasterPaintEngine::drawImage(p, img->imageRef()); + img->endDataAccess(true); + } else { + QRasterPaintEngine::drawPixmap(p, pm); + } +#else + QRasterPaintEngine::drawPixmap(p, pm); +#endif +} + +void QVolatileImagePaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) +{ +#ifdef Q_OS_SYMBIAN + void *nativeData = pm.pixmapData()->toNativeType(QPixmapData::VolatileImage); + if (nativeData) { + QVolatileImage *img = static_cast<QVolatileImage *>(nativeData); + img->beginDataAccess(); + QRasterPaintEngine::drawImage(r, img->imageRef(), sr); + img->endDataAccess(true); + } else { + QRasterPaintEngine::drawPixmap(r, pm, sr); + } +#else + QRasterPaintEngine::drawPixmap(r, pm, sr); +#endif +} + +QT_END_NAMESPACE diff --git a/src/gui/image/qvolatileimage_p.h b/src/gui/image/qvolatileimage_p.h new file mode 100644 index 0000000..fc5d6b1 --- /dev/null +++ b/src/gui/image/qvolatileimage_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** 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 QtGui 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 QVOLATILEIMAGE_P_H +#define QVOLATILEIMAGE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qimage.h> +#include <QtCore/qshareddata.h> + +QT_BEGIN_NAMESPACE + +class QVolatileImageData; + +class Q_GUI_EXPORT QVolatileImage +{ +public: + QVolatileImage(); + QVolatileImage(int w, int h, QImage::Format format); + explicit QVolatileImage(const QImage &sourceImage); + explicit QVolatileImage(void *nativeImage, void *nativeMask = 0); + QVolatileImage(const QVolatileImage &other); + ~QVolatileImage(); + QVolatileImage &operator=(const QVolatileImage &rhs); + + bool isNull() const; + QImage::Format format() const; + int width() const; + int height() const; + int bytesPerLine() const; + int byteCount() const; + int depth() const; + bool hasAlphaChannel() const; + void beginDataAccess() const; + void endDataAccess(bool readOnly = false) const; + uchar *bits(); + const uchar *constBits() const; + bool ensureFormat(QImage::Format format); + QImage toImage() const; + QImage &imageRef(); + QPaintEngine *paintEngine(); + void setAlphaChannel(const QPixmap &alphaChannel); + void fill(uint pixelValue); + void *duplicateNativeImage() const; + void copyFrom(QVolatileImage *source, const QRect &rect); + +private: + QSharedDataPointer<QVolatileImageData> d; +}; + +QT_END_NAMESPACE + +#endif // QVOLATILEIMAGE_P_H diff --git a/src/gui/image/qvolatileimagedata.cpp b/src/gui/image/qvolatileimagedata.cpp new file mode 100644 index 0000000..51b5995 --- /dev/null +++ b/src/gui/image/qvolatileimagedata.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** 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 QtGui 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 "qvolatileimagedata_p.h" +#include <QtGui/qpaintengine.h> + +QT_BEGIN_NAMESPACE + +QVolatileImageData::QVolatileImageData() + : pengine(0) +{ +} + +QVolatileImageData::QVolatileImageData(int w, int h, QImage::Format format) + : pengine(0) +{ + image = QImage(w, h, format); +} + +QVolatileImageData::QVolatileImageData(const QImage &sourceImage) + : pengine(0) +{ + image = sourceImage; +} + +QVolatileImageData::QVolatileImageData(void *, void *) + : pengine(0) +{ + // Not supported. +} + +QVolatileImageData::QVolatileImageData(const QVolatileImageData &other) +{ + image = other.image; + // The detach is not mandatory here but we do it nonetheless in order to + // keep the behavior consistent with other platforms. + image.detach(); + pengine = 0; +} + +QVolatileImageData::~QVolatileImageData() +{ + delete pengine; +} + +void QVolatileImageData::beginDataAccess() const +{ + // nothing to do here +} + +void QVolatileImageData::endDataAccess(bool readOnly) const +{ + Q_UNUSED(readOnly); + // nothing to do here +} + +bool QVolatileImageData::ensureFormat(QImage::Format format) +{ + if (image.format() != format) { + image = image.convertToFormat(format); + } + return true; +} + +void *QVolatileImageData::duplicateNativeImage() const +{ + return 0; +} + +void QVolatileImageData::ensureImage() +{ + // nothing to do here +} + +QT_END_NAMESPACE diff --git a/src/gui/image/qvolatileimagedata_p.h b/src/gui/image/qvolatileimagedata_p.h new file mode 100644 index 0000000..dab1685 --- /dev/null +++ b/src/gui/image/qvolatileimagedata_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** 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 QtGui 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 QVOLATILEIMAGEDATA_P_H +#define QVOLATILEIMAGEDATA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qimage.h> +#include <QtCore/qshareddata.h> + +#ifdef Q_OS_SYMBIAN +class CFbsBitmap; +#endif + +QT_BEGIN_NAMESPACE + +class QVolatileImageData : public QSharedData +{ +public: + QVolatileImageData(); + QVolatileImageData(int w, int h, QImage::Format format); + QVolatileImageData(const QImage &sourceImage); + QVolatileImageData(void *nativeImage, void *nativeMask); + QVolatileImageData(const QVolatileImageData &other); + ~QVolatileImageData(); + + void beginDataAccess() const; + void endDataAccess(bool readOnly = false) const; + bool ensureFormat(QImage::Format format); + void *duplicateNativeImage() const; + void ensureImage(); + +#ifdef Q_OS_SYMBIAN + void updateImage(); + void initWithBitmap(CFbsBitmap *source); + void applyMask(CFbsBitmap *mask); + void ensureBitmap(); + void release(); + QVolatileImageData *next; + QVolatileImageData *prev; + CFbsBitmap *bitmap; +#endif + QImage image; + QPaintEngine *pengine; +}; + +QT_END_NAMESPACE + +#endif // QVOLATILEIMAGEDATA_P_H diff --git a/src/gui/image/qvolatileimagedata_symbian.cpp b/src/gui/image/qvolatileimagedata_symbian.cpp new file mode 100644 index 0000000..474d0ef --- /dev/null +++ b/src/gui/image/qvolatileimagedata_symbian.cpp @@ -0,0 +1,471 @@ +/**************************************************************************** +** +** 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 QtGui 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 "qvolatileimagedata_p.h" +#include <fbs.h> +#include <QtGui/private/qt_s60_p.h> +#include <QtGui/qpaintengine.h> +#include <QtGui/private/qimage_p.h> + +QT_BEGIN_NAMESPACE + +static CFbsBitmap *rasterizeBitmap(CFbsBitmap *bitmap, TDisplayMode newMode) +{ + if (!bitmap) { + return 0; + } + QScopedPointer<CFbsBitmap> newBitmap(new CFbsBitmap); + if (newBitmap->Create(bitmap->SizeInPixels(), newMode) != KErrNone) { + qWarning("QVolatileImage: Failed to create new bitmap"); + return 0; + } + CFbsBitmapDevice *bitmapDevice = 0; + CFbsBitGc *bitmapGc = 0; + QT_TRAP_THROWING(bitmapDevice = CFbsBitmapDevice::NewL(newBitmap.data())); + QScopedPointer<CFbsBitmapDevice> bitmapDevicePtr(bitmapDevice); + QT_TRAP_THROWING(bitmapGc = CFbsBitGc::NewL()); + bitmapGc->Activate(bitmapDevice); + bitmapGc->BitBlt(TPoint(), bitmap); + delete bitmapGc; + return newBitmap.take(); +} + +static inline TDisplayMode format2TDisplayMode(QImage::Format format) +{ + TDisplayMode mode; + switch (format) { + case QImage::Format_MonoLSB: + mode = EGray2; + break; + case QImage::Format_Indexed8: + mode = EColor256; + break; + case QImage::Format_RGB444: + mode = EColor4K; + break; + case QImage::Format_RGB16: + mode = EColor64K; + break; + case QImage::Format_RGB888: + mode = EColor16M; + break; + case QImage::Format_RGB32: + mode = EColor16MU; + break; + case QImage::Format_ARGB32: + mode = EColor16MA; + break; + case QImage::Format_ARGB32_Premultiplied: + mode = Q_SYMBIAN_ECOLOR16MAP; + break; + default: + mode = ENone; + break; + } + return mode; +} + +static CFbsBitmap *imageToBitmap(const QImage &image) +{ + if (image.isNull()) { + return 0; + } + CFbsBitmap *bitmap = new CFbsBitmap; + if (bitmap->Create(TSize(image.width(), image.height()), + format2TDisplayMode(image.format())) == KErrNone) { + bitmap->BeginDataAccess(); + uchar *dptr = reinterpret_cast<uchar *>(bitmap->DataAddress()); + int bmpLineLen = bitmap->DataStride(); + int imgLineLen = image.bytesPerLine(); + if (bmpLineLen == imgLineLen) { + qMemCopy(dptr, image.constBits(), image.byteCount()); + } else { + int len = qMin(bmpLineLen, imgLineLen); + const uchar *sptr = image.constBits(); + for (int y = 0; y < image.height(); ++y) { + qMemCopy(dptr, sptr, len); + dptr += bmpLineLen; + sptr += imgLineLen; + } + } + bitmap->EndDataAccess(); + } else { + qWarning("QVolatileImage: Failed to create source bitmap"); + delete bitmap; + bitmap = 0; + } + return bitmap; +} + +static CFbsBitmap *copyData(const QVolatileImageData &source) +{ + source.beginDataAccess(); + CFbsBitmap *bmp = imageToBitmap(source.image); + source.endDataAccess(); + return bmp; +} + +static CFbsBitmap *convertData(const QVolatileImageData &source, QImage::Format newFormat) +{ + source.beginDataAccess(); + QImage img = source.image.convertToFormat(newFormat); + CFbsBitmap *bmp = imageToBitmap(img); + source.endDataAccess(); + return bmp; +} + +static CFbsBitmap *duplicateBitmap(const CFbsBitmap &sourceBitmap) +{ + CFbsBitmap *bitmap = new CFbsBitmap; + if (bitmap->Duplicate(sourceBitmap.Handle()) != KErrNone) { + qWarning("QVolatileImage: Failed to duplicate source bitmap"); + delete bitmap; + bitmap = 0; + } + return bitmap; +} + +static CFbsBitmap *createBitmap(int w, int h, QImage::Format format) +{ + CFbsBitmap *bitmap = new CFbsBitmap; + if (bitmap->Create(TSize(w, h), format2TDisplayMode(format)) != KErrNone) { + qWarning("QVolatileImage: Failed to create source bitmap %d,%d (%d)", w, h, format); + delete bitmap; + bitmap = 0; + } + return bitmap; +} + +static inline bool bitmapNeedsCopy(CFbsBitmap *bitmap) +{ + bool needsCopy = bitmap->IsCompressedInRAM(); +#ifdef Q_SYMBIAN_HAS_EXTENDED_BITMAP_TYPE + needsCopy |= (bitmap->ExtendedBitmapType() != KNullUid); +#endif + return needsCopy; +} + +static bool cleanup_function_registered = false; +static QVolatileImageData *firstImageData = 0; + +static void cleanup() +{ + if (RFbsSession::GetSession()) { + QVolatileImageData *imageData = firstImageData; + while (imageData) { + imageData->release(); + imageData = imageData->next; + } + } +} + +static void ensureCleanup() +{ + // Destroy all underlying bitmaps in a post routine to prevent panics. + // This is a must because CFbsBitmap destructor needs the fbs session, + // that was used to create the bitmap, to be open still. + if (!cleanup_function_registered) { + qAddPostRoutine(cleanup); + cleanup_function_registered = true; + } +} + +static void registerImageData(QVolatileImageData *imageData) +{ + ensureCleanup(); + imageData->next = firstImageData; + if (firstImageData) { + firstImageData->prev = imageData; + } + firstImageData = imageData; +} + +static void unregisterImageData(QVolatileImageData *imageData) +{ + if (imageData->prev) { + imageData->prev->next = imageData->next; + } else { + firstImageData = imageData->next; + } + if (imageData->next) { + imageData->next->prev = imageData->prev; + } +} + +QVolatileImageData::QVolatileImageData() + : next(0), prev(0), bitmap(0), pengine(0) +{ + registerImageData(this); +} + +QVolatileImageData::QVolatileImageData(int w, int h, QImage::Format format) + : next(0), prev(0), bitmap(0), pengine(0) +{ + registerImageData(this); + bitmap = createBitmap(w, h, format); + updateImage(); +} + +QVolatileImageData::QVolatileImageData(const QImage &sourceImage) + : next(0), prev(0), bitmap(0), pengine(0) +{ + registerImageData(this); + image = sourceImage; + // The following is not mandatory, but we do it here to have a bitmap + // created always in order to reduce local heap usage. + ensureBitmap(); +} + +QVolatileImageData::QVolatileImageData(void *nativeImage, void *nativeMask) + : next(0), prev(0), bitmap(0), pengine(0) +{ + registerImageData(this); + if (nativeImage) { + CFbsBitmap *source = static_cast<CFbsBitmap *>(nativeImage); + CFbsBitmap *mask = static_cast<CFbsBitmap *>(nativeMask); + initWithBitmap(source); + if (mask) { + applyMask(mask); + } + } +} + +QVolatileImageData::QVolatileImageData(const QVolatileImageData &other) +{ + bitmap = 0; + pengine = 0; + next = prev = 0; + registerImageData(this); + if (!other.image.isNull()) { + bitmap = copyData(other); + updateImage(); + } +} + +QVolatileImageData::~QVolatileImageData() +{ + release(); + unregisterImageData(this); +} + +void QVolatileImageData::release() +{ + delete bitmap; + bitmap = 0; + delete pengine; + pengine = 0; +} + +void QVolatileImageData::beginDataAccess() const +{ + if (bitmap) { + bitmap->BeginDataAccess(); + } +} + +void QVolatileImageData::endDataAccess(bool readOnly) const +{ + if (bitmap) { + bitmap->EndDataAccess(readOnly); + } +} + +bool QVolatileImageData::ensureFormat(QImage::Format format) +{ + if (image.isNull()) { + return false; + } + if (image.format() != format) { + CFbsBitmap *newBitmap = convertData(*this, format); + if (newBitmap && newBitmap != bitmap) { + delete bitmap; + bitmap = newBitmap; + updateImage(); + } else { + return false; + } + } + return true; +} + +void *QVolatileImageData::duplicateNativeImage() const +{ + const_cast<QVolatileImageData *>(this)->ensureBitmap(); + if (bitmap) { + if (bitmap->DisplayMode() == EColor16M) { + // slow path: needs rgb swapping + beginDataAccess(); + QImage tmp = image.rgbSwapped(); + endDataAccess(true); + return imageToBitmap(tmp); + } else if (bitmap->DisplayMode() == EGray2) { + // slow path: needs inverting pixels + beginDataAccess(); + QImage tmp = image.copy(); + endDataAccess(true); + tmp.invertPixels(); + return imageToBitmap(tmp); + } else { + // fast path: just duplicate the bitmap + return duplicateBitmap(*bitmap); + } + } + return 0; +} + +void QVolatileImageData::updateImage() +{ + if (bitmap) { + TSize size = bitmap->SizeInPixels(); + beginDataAccess(); + // Use existing buffer, no copy. The data address never changes so it + // is enough to do this whenever we have a new CFbsBitmap. N.B. never + // use const uchar* here, that would create a read-only image data which + // would make a copy in detach() even when refcount is 1. + image = QImage(reinterpret_cast<uchar *>(bitmap->DataAddress()), + size.iWidth, size.iHeight, bitmap->DataStride(), + qt_TDisplayMode2Format(bitmap->DisplayMode())); + endDataAccess(true); + } else { + image = QImage(); + } +} + +void QVolatileImageData::initWithBitmap(CFbsBitmap *source) +{ + bool needsCopy = bitmapNeedsCopy(source); + if (source->DisplayMode() == EColor16M) { + // EColor16M is BGR + CFbsBitmap *unswappedBmp = source; + if (needsCopy) { + unswappedBmp = rasterizeBitmap(source, source->DisplayMode()); + } + unswappedBmp->BeginDataAccess(); + TSize sourceSize = unswappedBmp->SizeInPixels(); + QImage img((uchar *) unswappedBmp->DataAddress(), + sourceSize.iWidth, sourceSize.iHeight, unswappedBmp->DataStride(), + qt_TDisplayMode2Format(unswappedBmp->DisplayMode())); + img = img.rgbSwapped(); + unswappedBmp->EndDataAccess(true); + bitmap = imageToBitmap(img); + if (needsCopy) { + delete unswappedBmp; + } + } else if (needsCopy) { + // Rasterize extended and compressed bitmaps. + bitmap = rasterizeBitmap(source, EColor16MAP); + } else { + // Efficient path: no pixel data copying. Just duplicate. This of course + // means the original bitmap's data may get modified, but that's fine + // and is in accordance with the QPixmap::fromSymbianCFbsBitmap() docs. + bitmap = duplicateBitmap(*source); + } + updateImage(); + if (bitmap && bitmap->DisplayMode() == EGray2) { + // Symbian thinks set pixels are white/transparent, Qt thinks they are + // foreground/solid. Invert mono bitmaps so that masks work correctly. + beginDataAccess(); + image.invertPixels(); + endDataAccess(); + } +} + +void QVolatileImageData::applyMask(CFbsBitmap *mask) +{ + ensureFormat(QImage::Format_ARGB32_Premultiplied); + bool destroyMask = false; + if (bitmapNeedsCopy(mask)) { + mask = rasterizeBitmap(mask, EColor16MU); + if (!mask) { + return; + } + destroyMask = true; + } + mask->BeginDataAccess(); + TSize maskSize = mask->SizeInPixels(); + QImage maskImg((const uchar *) mask->DataAddress(), maskSize.iWidth, maskSize.iHeight, + mask->DataStride(), qt_TDisplayMode2Format(mask->DisplayMode())); + if (mask->DisplayMode() == EGray2) { + maskImg = maskImg.copy(); + maskImg.invertPixels(); + } + beginDataAccess(); + image.setAlphaChannel(maskImg); + endDataAccess(); + mask->EndDataAccess(true); + ensureImage(); + if (destroyMask) { + delete mask; + } +} + +void QVolatileImageData::ensureImage() +{ + if (bitmap && !image.isNull()) { + QImageData *imaged = image.data_ptr(); + if (imaged->ref != 1 || imaged->ro_data) { + // This is bad, the imagedata got shared somehow. Detach, in order to + // have the next check fail and thus have 'image' recreated. + beginDataAccess(); + image.detach(); + endDataAccess(true); + } + } + if (bitmap && image.constBits() != reinterpret_cast<const uchar *>(bitmap->DataAddress())) { + // Should not ever get here. If we do it means that either 'image' has + // been replaced with a copy (e.g. because some QImage API assigned a + // new, regular QImage to *this) or the bitmap's data address changed + // unexpectedly. + qWarning("QVolatileImageData: Ptr mismatch"); + // Recover by recreating the image so that it uses the bitmap as its buffer. + updateImage(); + } +} + +void QVolatileImageData::ensureBitmap() +{ + if (!bitmap && !image.isNull()) { + bitmap = imageToBitmap(image); + updateImage(); + } +} + +QT_END_NAMESPACE diff --git a/src/gui/painting/qgraphicssystem.cpp b/src/gui/painting/qgraphicssystem.cpp index 8d39b59..171ef46 100644 --- a/src/gui/painting/qgraphicssystem.cpp +++ b/src/gui/painting/qgraphicssystem.cpp @@ -89,4 +89,9 @@ QPixmapData *QGraphicsSystem::createPixmapData(QPixmapData *origin) return createPixmapData(origin->pixelType()); } +void QGraphicsSystem::releaseCachedResources() +{ + // Do nothing here +} + QT_END_NAMESPACE diff --git a/src/gui/painting/qgraphicssystem_p.h b/src/gui/painting/qgraphicssystem_p.h index 1aa3aa0..0f99a31 100644 --- a/src/gui/painting/qgraphicssystem_p.h +++ b/src/gui/painting/qgraphicssystem_p.h @@ -76,6 +76,8 @@ public: //### Remove this & change qpixmap.cpp & qbitmap.cpp once every platform is gaurenteed // to have a graphics system. static QPixmapData *createDefaultPixmapData(QPixmapData::PixelType type); + + virtual void releaseCachedResources(); }; QT_END_NAMESPACE diff --git a/src/gui/painting/qpaintengine_s60.cpp b/src/gui/painting/qpaintengine_s60.cpp index 1bc7799..ca303be 100644 --- a/src/gui/painting/qpaintengine_s60.cpp +++ b/src/gui/painting/qpaintengine_s60.cpp @@ -41,6 +41,7 @@ #include <private/qpaintengine_s60_p.h> #include <private/qpixmap_s60_p.h> #include <private/qt_s60_p.h> +#include <private/qvolatileimage_p.h> QT_BEGIN_NAMESPACE @@ -90,7 +91,15 @@ void QS60PaintEngine::drawPixmap(const QPointF &p, const QPixmap &pm) QRasterPaintEngine::drawPixmap(p, pm); srcData->endDataAccess(); } else { - QRasterPaintEngine::drawPixmap(p, pm); + void *nativeData = pm.pixmapData()->toNativeType(QPixmapData::VolatileImage); + if (nativeData) { + QVolatileImage *img = static_cast<QVolatileImage *>(nativeData); + img->beginDataAccess(); + QRasterPaintEngine::drawImage(p, img->imageRef()); + img->endDataAccess(true); + } else { + QRasterPaintEngine::drawPixmap(p, pm); + } } } @@ -102,7 +111,15 @@ void QS60PaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRect QRasterPaintEngine::drawPixmap(r, pm, sr); srcData->endDataAccess(); } else { - QRasterPaintEngine::drawPixmap(r, pm, sr); + void *nativeData = pm.pixmapData()->toNativeType(QPixmapData::VolatileImage); + if (nativeData) { + QVolatileImage *img = static_cast<QVolatileImage *>(nativeData); + img->beginDataAccess(); + QRasterPaintEngine::drawImage(r, img->imageRef(), sr); + img->endDataAccess(true); + } else { + QRasterPaintEngine::drawPixmap(r, pm, sr); + } } } diff --git a/src/gui/styles/qs60style_s60.cpp b/src/gui/styles/qs60style_s60.cpp index 605872e..600c631 100644 --- a/src/gui/styles/qs60style_s60.cpp +++ b/src/gui/styles/qs60style_s60.cpp @@ -47,6 +47,7 @@ #include "private/qt_s60_p.h" #include "private/qpixmap_s60_p.h" #include "private/qcore_symbian_p.h" +#include "private/qvolatileimage_p.h" #include "qapplication.h" #include "qsettings.h" @@ -637,9 +638,22 @@ QPixmap QS60StyleModeSpecifics::fromFbsBitmap(CFbsBitmap *icon, CFbsBitmap *mask if (error) return QPixmap(); - QPixmap pixmap = QPixmap::fromSymbianCFbsBitmap(icon); - if (mask) - pixmap.setAlphaChannel(QPixmap::fromSymbianCFbsBitmap(mask)); + QPixmap pixmap; + QScopedPointer<QPixmapData> pd(QPixmapData::create(0, 0, QPixmapData::PixmapType)); + bool nativeMaskSupported = (pd->toNativeType(QPixmapData::VolatileImage) != 0); + if (mask && nativeMaskSupported) { + // Efficient path, less copying and conversion. + QVolatileImage img(icon, mask); + pd->fromNativeType(&img, QPixmapData::VolatileImage); + pixmap = QPixmap(pd.take()); + } else { + // Potentially more expensive path. + pd->fromNativeType(icon, QPixmapData::FbsBitmap); + pixmap = QPixmap(pd.take()); + if (mask) { + pixmap.setAlphaChannel(QPixmap::fromSymbianCFbsBitmap(mask)); + } + } if ((flags & QS60StylePrivate::SF_PointEast) || (flags & QS60StylePrivate::SF_PointSouth) || @@ -795,6 +809,8 @@ QPixmap QS60StyleModeSpecifics::createSkinnedGraphicsLX( rotatedBy90or270 ? TSize(size.height(), size.width()) : qt_QSize2TSize(size); MAknsSkinInstance* skinInstance = AknsUtils::SkinInstance(); + static const TDisplayMode displayMode = S60->supportsPremultipliedAlpha ? Q_SYMBIAN_ECOLOR16MAP : EColor16MA; + static const TInt drawParam = S60->supportsPremultipliedAlpha ? KAknsDrawParamDefault : KAknsDrawParamRGBOnly; QPixmap result; @@ -833,7 +849,7 @@ QPixmap QS60StyleModeSpecifics::createSkinnedGraphicsLX( // QS60WindowSurface::unlockBitmapHeap(); CFbsBitmap *background = new (ELeave) CFbsBitmap(); //offscreen CleanupStack::PushL(background); - User::LeaveIfError(background->Create(targetSize, EColor16MA)); + User::LeaveIfError(background->Create(targetSize, displayMode)); CFbsBitmapDevice *dev = CFbsBitmapDevice::NewL(background); CleanupStack::PushL(dev); @@ -854,7 +870,7 @@ QPixmap QS60StyleModeSpecifics::createSkinnedGraphicsLX( *gc, TPoint(), targetSize, - KAknsDrawParamDefault | KAknsDrawParamRGBOnly); + drawParam); if (drawn) result = fromFbsBitmap(background, NULL, flags, targetSize); diff --git a/src/gui/widgets/qlinecontrol.cpp b/src/gui/widgets/qlinecontrol.cpp index 023b9ee..f20e018 100644 --- a/src/gui/widgets/qlinecontrol.cpp +++ b/src/gui/widgets/qlinecontrol.cpp @@ -420,7 +420,7 @@ void QLineControl::processInputMethodEvent(QInputMethodEvent *event) int c = m_cursor; // cursor position after insertion of commit string if (event->replacementStart() <= 0) - c += event->commitString().length() + qMin(-event->replacementStart(), event->replacementLength()); + c += event->commitString().length() - qMin(-event->replacementStart(), event->replacementLength()); m_cursor += event->replacementStart(); diff --git a/src/imports/gestures/qdeclarativegesturearea.cpp b/src/imports/gestures/qdeclarativegesturearea.cpp index 1b6e723..c2a2e98 100644 --- a/src/imports/gestures/qdeclarativegesturearea.cpp +++ b/src/imports/gestures/qdeclarativegesturearea.cpp @@ -89,6 +89,10 @@ public: \warning Elements in the Qt.labs module are not guaranteed to remain compatible in future versions. + \warning GestureArea is an experimental element whose development has + been discontinued. PinchArea is available in QtQuick 1.1 and handles + two finger gesture input. + \note This element is only functional on devices with touch input. \qml diff --git a/src/openvg/qpaintengine_vg.cpp b/src/openvg/qpaintengine_vg.cpp index 22cbbf5..f0f198f 100644 --- a/src/openvg/qpaintengine_vg.cpp +++ b/src/openvg/qpaintengine_vg.cpp @@ -3134,9 +3134,13 @@ void QVGPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF if (pm.size().width() <= screenSize.width() && pm.size().height() <= screenSize.height()) vgpd->failedToAlloc = false; - } - drawImage(r, *(pd->buffer()), sr, Qt::AutoColor); + vgpd->source.beginDataAccess(); + drawImage(r, vgpd->source.imageRef(), sr, Qt::AutoColor); + vgpd->source.endDataAccess(true); + } else { + drawImage(r, *(pd->buffer()), sr, Qt::AutoColor); + } } void QVGPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pm) @@ -3162,9 +3166,13 @@ void QVGPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pm) if (pm.size().width() <= screenSize.width() && pm.size().height() <= screenSize.height()) vgpd->failedToAlloc = false; - } - drawImage(pos, *(pd->buffer())); + vgpd->source.beginDataAccess(); + drawImage(pos, vgpd->source.imageRef()); + vgpd->source.endDataAccess(); + } else { + drawImage(pos, *(pd->buffer())); + } } void QVGPaintEngine::drawImage diff --git a/src/openvg/qpixmapdata_vg.cpp b/src/openvg/qpixmapdata_vg.cpp index 732b484..c5da115 100644 --- a/src/openvg/qpixmapdata_vg.cpp +++ b/src/openvg/qpixmapdata_vg.cpp @@ -70,7 +70,7 @@ QVGPixmapData::QVGPixmapData(PixelType type) context = 0; qt_vg_register_pixmap(this); #endif - setSerialNumber(++qt_vg_pixmap_serial); + updateSerial(); } QVGPixmapData::~QVGPixmapData() @@ -138,25 +138,31 @@ bool QVGPixmapData::isValid() const return (w > 0 && h > 0); } +void QVGPixmapData::updateSerial() +{ + setSerialNumber(++qt_vg_pixmap_serial); +} + void QVGPixmapData::resize(int wid, int ht) { - if (w == wid && h == ht) + if (w == wid && h == ht) { + updateSerial(); return; + } w = wid; h = ht; d = 32; // We always use ARGB_Premultiplied for VG pixmaps. is_null = (w <= 0 || h <= 0); - source = QImage(); + source = QVolatileImage(); recreate = true; - setSerialNumber(++qt_vg_pixmap_serial); + updateSerial(); } -void QVGPixmapData::fromImage - (const QImage &image, Qt::ImageConversionFlags flags) +void QVGPixmapData::fromImage(const QImage &image, Qt::ImageConversionFlags flags) { - if(image.isNull()) + if (image.isNull()) return; QImage img = image; @@ -200,33 +206,34 @@ bool QVGPixmapData::fromData(const uchar *buffer, uint len, const char *format, return !isNull(); } -/*! - out-of-place conversion (inPlace == false) will always detach() - */ -void QVGPixmapData::createPixmapForImage(QImage &image, Qt::ImageConversionFlags flags, bool inPlace) +QImage::Format QVGPixmapData::idealFormat(QImage *image, Qt::ImageConversionFlags flags) const { - if (image.size() == QSize(w, h)) - setSerialNumber(++qt_vg_pixmap_serial); - else - resize(image.width(), image.height()); - QImage::Format format = sourceFormat(); - int d = image.depth(); - if (d == 1 || d == 16 || d == 24 || (d == 32 && !image.hasAlphaChannel())) + int d = image->depth(); + if (d == 1 || d == 16 || d == 24 || (d == 32 && !image->hasAlphaChannel())) format = QImage::Format_RGB32; - else if (!(flags & Qt::NoOpaqueDetection) && const_cast<QImage &>(image).data_ptr()->checkForAlphaPixels()) + else if (!(flags & Qt::NoOpaqueDetection) && image->data_ptr()->checkForAlphaPixels()) format = sourceFormat(); else - format = image.hasAlphaChannel() ? sourceFormat() : QImage::Format_RGB32; + format = image->hasAlphaChannel() ? sourceFormat() : QImage::Format_RGB32; + return format; +} + +void QVGPixmapData::createPixmapForImage(QImage &image, Qt::ImageConversionFlags flags, bool inPlace) +{ + resize(image.width(), image.height()); + + QImage::Format format = idealFormat(&image, flags); if (inPlace && image.data_ptr()->convertInPlace(format, flags)) { - source = image; + source = QVolatileImage(image); } else { - source = image.convertToFormat(format); - - // convertToFormat won't detach the image if format stays the same. - if (image.format() == format) - source.detach(); + QImage convertedImage = image.convertToFormat(format); + // convertToFormat won't detach the image if format stays the + // same. Detaching is needed to prevent issues with painting + // onto this QPixmap later on. + convertedImage.detach(); + source = QVolatileImage(convertedImage); } recreate = true; @@ -238,12 +245,13 @@ void QVGPixmapData::fill(const QColor &color) return; if (source.isNull()) - source = QImage(w, h, sourceFormat()); + source = QVolatileImage(w, h, sourceFormat()); if (source.depth() == 1) { // Pick the best approximate color in the image's colortable. int gray = qGray(color.rgba()); - if (qAbs(qGray(source.color(0)) - gray) < qAbs(qGray(source.color(1)) - gray)) + if (qAbs(qGray(source.imageRef().color(0)) - gray) + < qAbs(qGray(source.imageRef().color(1)) - gray)) source.fill(0); else source.fill(1); @@ -266,7 +274,7 @@ bool QVGPixmapData::hasAlphaChannel() const void QVGPixmapData::setAlphaChannel(const QPixmap &alphaChannel) { forceToImage(); - source.setAlphaChannel(alphaChannel.toImage()); + source.setAlphaChannel(alphaChannel); } QImage QVGPixmapData::toImage() const @@ -275,17 +283,41 @@ QImage QVGPixmapData::toImage() const return QImage(); if (source.isNull()) { - source = QImage(w, h, sourceFormat()); + source = QVolatileImage(w, h, sourceFormat()); recreate = true; } - return source; + return source.toImage(); +} + +void QVGPixmapData::copy(const QPixmapData *data, const QRect &rect) +{ + // toImage() is potentially expensive with QVolatileImage so provide a + // more efficient implementation of copy() that does not rely on it. + if (!data) { + return; + } + if (data->classId() != OpenVGClass) { + fromImage(data->toImage(rect), Qt::NoOpaqueDetection); + return; + } + const QVGPixmapData *pd = static_cast<const QVGPixmapData *>(data); + QRect r = rect; + if (r.isNull() || r.contains(QRect(0, 0, pd->w, pd->h))) { + r = QRect(0, 0, pd->w, pd->h); + } + resize(r.width(), r.height()); + recreate = true; + if (!pd->source.isNull()) { + source = QVolatileImage(r.width(), r.height(), pd->source.format()); + source.copyFrom(&pd->source, r); + } } QImage *QVGPixmapData::buffer() { - forceToImage(); - return &source; + // Cannot be safely implemented and QVGPixmapData is not (must not be) RasterClass anyway. + return 0; } QPaintEngine* QVGPixmapData::paintEngine() const @@ -329,10 +361,12 @@ VGImage QVGPixmapData::toVGImage() } if (!source.isNull() && recreate) { + source.beginDataAccess(); vgImageSubData (vgImage, source.constBits(), source.bytesPerLine(), qt_vg_image_to_vg_format(source.format()), 0, 0, w, h); + source.endDataAccess(true); } recreate = false; @@ -442,14 +476,14 @@ int QVGPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const } } -// Force the pixmap data to be in QImage format. +// Force the pixmap data to be backed by some valid data. void QVGPixmapData::forceToImage() { if (!isValid()) return; if (source.isNull()) - source = QImage(w, h, sourceFormat()); + source = QVolatileImage(w, h, sourceFormat()); recreate = true; } diff --git a/src/openvg/qpixmapdata_vg_p.h b/src/openvg/qpixmapdata_vg_p.h index 58e69db..c4fd47f 100644 --- a/src/openvg/qpixmapdata_vg_p.h +++ b/src/openvg/qpixmapdata_vg_p.h @@ -54,6 +54,7 @@ // #include <QtGui/private/qpixmap_raster_p.h> +#include <QtGui/private/qvolatileimage_p.h> #include <private/qvg_p.h> #if defined(Q_OS_SYMBIAN) @@ -99,6 +100,7 @@ public: bool hasAlphaChannel() const; void setAlphaChannel(const QPixmap &alphaChannel); QImage toImage() const; + void copy(const QPixmapData *data, const QRect &rect); QImage *buffer(); QPaintEngine* paintEngine() const; @@ -161,7 +163,7 @@ protected: VGImage vgImage; VGImage vgImageOpacity; qreal cachedOpacity; - mutable QImage source; + mutable QVolatileImage source; mutable bool recreate; bool inImagePool; #if !defined(QT_NO_EGL) @@ -170,6 +172,8 @@ protected: void forceToImage(); QImage::Format sourceFormat() const; + QImage::Format idealFormat(QImage *image, Qt::ImageConversionFlags flags) const; + void updateSerial(); void destroyImageAndContext(); void destroyImages(); diff --git a/src/openvg/qvg_symbian.cpp b/src/openvg/qvg_symbian.cpp index ff0ab13..22cbb3c 100644 --- a/src/openvg/qvg_symbian.cpp +++ b/src/openvg/qvg_symbian.cpp @@ -44,7 +44,6 @@ #include <private/qt_s60_p.h> #include <fbs.h> -#include <bitdev.h> #ifdef QT_SYMBIAN_SUPPORTS_SGIMAGE # include <sgresource/sgimage.h> @@ -75,34 +74,6 @@ VGImage QVG::vgCreateEGLImageTargetKHR(VGeglImageKHR eglImage) extern int qt_vg_pixmap_serial; -static CFbsBitmap* createBlitCopy(CFbsBitmap* bitmap) -{ - CFbsBitmap *copy = new CFbsBitmap; - Q_CHECK_PTR(copy); - if(!copy) - return 0; - - if (copy->Create(bitmap->SizeInPixels(), bitmap->DisplayMode()) != KErrNone) { - delete copy; - copy = 0; - - return 0; - } - - CFbsBitmapDevice* bitmapDevice = 0; - CFbsBitGc *bitmapGc = 0; - QT_TRAP_THROWING(bitmapDevice = CFbsBitmapDevice::NewL(copy)); - QT_TRAP_THROWING(bitmapGc = CFbsBitGc::NewL()); - bitmapGc->Activate(bitmapDevice); - - bitmapGc->BitBlt(TPoint(), bitmap); - - delete bitmapGc; - delete bitmapDevice; - - return copy; -} - #ifdef QT_SYMBIAN_SUPPORTS_SGIMAGE static VGImage sgImageToVGImage(QEglContext *context, const RSgImage &sgImage) { @@ -137,7 +108,7 @@ void QVGPixmapData::cleanup() { is_null = w = h = 0; recreate = false; - source = QImage(); + source = QVolatileImage(); } void QVGPixmapData::fromNativeType(void* pixmap, NativeType type) @@ -156,55 +127,28 @@ void QVGPixmapData::fromNativeType(void* pixmap, NativeType type) } is_null = (w <= 0 || h <= 0); - source = QImage(); // vgGetImageSubData() some day? + source = QVolatileImage(); // vgGetImageSubData() some day? recreate = false; prevSize = QSize(w, h); - //setSerialNumber(++qt_vg_pixmap_serial); + updateSerial(); #endif - } else if (type == QPixmapData::FbsBitmap) { - CFbsBitmap *bitmap = reinterpret_cast<CFbsBitmap*>(pixmap); - - bool deleteSourceBitmap = false; -#ifdef Q_SYMBIAN_HAS_EXTENDED_BITMAP_TYPE - - // Rasterize extended bitmaps - - TUid extendedBitmapType = bitmap->ExtendedBitmapType(); - if (extendedBitmapType != KNullUid) { - bitmap = createBlitCopy(bitmap); - deleteSourceBitmap = true; - } -#endif - - if (bitmap->IsCompressedInRAM()) { - bitmap = createBlitCopy(bitmap); - deleteSourceBitmap = true; - } - - TDisplayMode displayMode = bitmap->DisplayMode(); - QImage::Format format = qt_TDisplayMode2Format(displayMode); - - TSize size = bitmap->SizeInPixels(); - int bytesPerLine = bitmap->ScanLineLength(size.iWidth, displayMode); - - bitmap->BeginDataAccess(); - uchar *bytes = (uchar*)bitmap->DataAddress(); - QImage img = QImage(bytes, size.iWidth, size.iHeight, bytesPerLine, format); - img = img.copy(); - bitmap->EndDataAccess(); - - if(displayMode == EGray2) { - //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid - //So invert mono bitmaps so that masks work correctly. - img.invertPixels(); - } else if(displayMode == EColor16M) { - img = img.rgbSwapped(); // EColor16M is BGR - } - - fromImage(img, Qt::AutoColor); - - if(deleteSourceBitmap) - delete bitmap; + } else if (type == QPixmapData::FbsBitmap && pixmap) { + CFbsBitmap *bitmap = reinterpret_cast<CFbsBitmap *>(pixmap); + QSize size(bitmap->SizeInPixels().iWidth, bitmap->SizeInPixels().iHeight); + resize(size.width(), size.height()); + source = QVolatileImage(bitmap); // duplicates only, if possible + if (source.isNull()) + return; + // Here we may need to copy if the formats do not match. + // (e.g. for display modes other than EColor16MAP and EColor16MU) + source.ensureFormat(idealFormat(&source.imageRef(), Qt::AutoColor)); + recreate = true; + } else if (type == QPixmapData::VolatileImage && pixmap) { + QVolatileImage *img = static_cast<QVolatileImage *>(pixmap); + resize(img->width(), img->height()); + source = *img; + source.ensureFormat(idealFormat(&source.imageRef(), Qt::AutoColor)); + recreate = true; } } @@ -271,27 +215,14 @@ void* QVGPixmapData::toNativeType(NativeType type) driver.Close(); return reinterpret_cast<void*>(sgImage.take()); #endif - } else if (type == QPixmapData::FbsBitmap) { - CFbsBitmap *bitmap = new CFbsBitmap; - Q_CHECK_PTR(bitmap); - - if (bitmap) { - if (bitmap->Create(TSize(source.width(), source.height()), - EColor16MAP) == KErrNone) { - const uchar *sptr = const_cast<const QImage&>(source).bits(); - bitmap->BeginDataAccess(); - - uchar *dptr = (uchar*)bitmap->DataAddress(); - Mem::Copy(dptr, sptr, source.byteCount()); - - bitmap->EndDataAccess(); - } else { - delete bitmap; - bitmap = 0; - } + } else if (type == QPixmapData::FbsBitmap && isValid()) { + if (source.isNull()) { + source = QVolatileImage(w, h, sourceFormat()); } - - return reinterpret_cast<void*>(bitmap); + // Just duplicate the bitmap handle, no data copying happens. + return source.duplicateNativeImage(); + } else if (type == QPixmapData::VolatileImage) { + return &source; } return 0; } diff --git a/src/openvg/qvgimagepool.cpp b/src/openvg/qvgimagepool.cpp index a2b1c4e..7a7ec78 100644 --- a/src/openvg/qvgimagepool.cpp +++ b/src/openvg/qvgimagepool.cpp @@ -175,8 +175,19 @@ bool QVGImagePool::reclaimSpace(VGImageFormat format, void QVGImagePool::hibernate() { - // Nothing to do here at the moment since the pool does not - // retain VGImage's after they have been released. + Q_D(QVGImagePool); + QVGPixmapData *pd = d->lruLast; + while (pd) { + QVGPixmapData *prevLRU = pd->prevLRU; + pd->inImagePool = false; + pd->inLRU = false; + pd->nextLRU = 0; + pd->prevLRU = 0; + pd->hibernate(); + pd = prevLRU; + } + d->lruFirst = 0; + d->lruLast = 0; } void QVGImagePool::moveToHeadOfLRU(QVGPixmapData *data) diff --git a/src/plugins/graphicssystems/openvg/qgraphicssystem_vg.cpp b/src/plugins/graphicssystems/openvg/qgraphicssystem_vg.cpp index 0c107b5..1da58e1 100644 --- a/src/plugins/graphicssystems/openvg/qgraphicssystem_vg.cpp +++ b/src/plugins/graphicssystems/openvg/qgraphicssystem_vg.cpp @@ -42,6 +42,7 @@ #include "qgraphicssystem_vg_p.h" #include <QtOpenVG/private/qpixmapdata_vg_p.h> #include <QtOpenVG/private/qwindowsurface_vg_p.h> +#include <QtOpenVG/private/qvgimagepool_p.h> #if defined(Q_OS_SYMBIAN) && !defined(Q_SYMBIAN_SEMITRANSPARENT_BG_SURFACE) #include <QtGui/private/qwidget_p.h> #endif @@ -77,4 +78,9 @@ QWindowSurface *QVGGraphicsSystem::createWindowSurface(QWidget *widget) const return new QVGWindowSurface(widget); } +void QVGGraphicsSystem::releaseCachedResources() +{ + QVGImagePool::instance()->hibernate(); +} + QT_END_NAMESPACE diff --git a/src/plugins/graphicssystems/openvg/qgraphicssystem_vg_p.h b/src/plugins/graphicssystems/openvg/qgraphicssystem_vg_p.h index d1bce28..9c9b3e2 100644 --- a/src/plugins/graphicssystems/openvg/qgraphicssystem_vg_p.h +++ b/src/plugins/graphicssystems/openvg/qgraphicssystem_vg_p.h @@ -64,6 +64,8 @@ public: QPixmapData *createPixmapData(QPixmapData::PixelType type) const; QWindowSurface *createWindowSurface(QWidget *widget) const; + + void releaseCachedResources(); }; QT_END_NAMESPACE diff --git a/src/s60installs/bwins/QtOpenVGu.def b/src/s60installs/bwins/QtOpenVGu.def index 87b9c7f..4767d93 100644 --- a/src/s60installs/bwins/QtOpenVGu.def +++ b/src/s60installs/bwins/QtOpenVGu.def @@ -176,4 +176,7 @@ EXPORTS ?fromData@QVGPixmapData@@UAE_NPBEIPBDV?$QFlags@W4ImageConversionFlag@Qt@@@@@Z @ 175 NONAME ; bool QVGPixmapData::fromData(unsigned char const *, unsigned int, char const *, class QFlags<enum Qt::ImageConversionFlag>) ?fromImageReader@QVGPixmapData@@UAEXPAVQImageReader@@V?$QFlags@W4ImageConversionFlag@Qt@@@@@Z @ 176 NONAME ; void QVGPixmapData::fromImageReader(class QImageReader *, class QFlags<enum Qt::ImageConversionFlag>) ?canVgWritePixels@QVGPaintEngine@@ABE_NABVQImage@@@Z @ 177 NONAME ; bool QVGPaintEngine::canVgWritePixels(class QImage const &) const + ?updateSerial@QVGPixmapData@@IAEXXZ @ 178 NONAME ; void QVGPixmapData::updateSerial(void) + ?copy@QVGPixmapData@@UAEXPBVQPixmapData@@ABVQRect@@@Z @ 179 NONAME ; void QVGPixmapData::copy(class QPixmapData const *, class QRect const &) + ?idealFormat@QVGPixmapData@@IBE?AW4Format@QImage@@PAV3@V?$QFlags@W4ImageConversionFlag@Qt@@@@@Z @ 180 NONAME ; enum QImage::Format QVGPixmapData::idealFormat(class QImage *, class QFlags<enum Qt::ImageConversionFlag>) const diff --git a/src/s60installs/eabi/QtOpenVGu.def b/src/s60installs/eabi/QtOpenVGu.def index e1828c1..40aac8a 100644 --- a/src/s60installs/eabi/QtOpenVGu.def +++ b/src/s60installs/eabi/QtOpenVGu.def @@ -206,4 +206,7 @@ EXPORTS _ZN13QVGPixmapData8fromDataEPKhjPKc6QFlagsIN2Qt19ImageConversionFlagEE @ 205 NONAME _ZN13QVGPixmapData8fromFileERK7QStringPKc6QFlagsIN2Qt19ImageConversionFlagEE @ 206 NONAME _ZNK14QVGPaintEngine16canVgWritePixelsERK6QImage @ 207 NONAME + _ZN13QVGPixmapData12updateSerialEv @ 208 NONAME + _ZN13QVGPixmapData4copyEPK11QPixmapDataRK5QRect @ 209 NONAME + _ZNK13QVGPixmapData11idealFormatEP6QImage6QFlagsIN2Qt19ImageConversionFlagEE @ 210 NONAME diff --git a/tests/auto/declarative/qdeclarativetext/data/qtbug_14734.qml b/tests/auto/declarative/qdeclarativetext/data/qtbug_14734.qml new file mode 100644 index 0000000..bd07d66 --- /dev/null +++ b/tests/auto/declarative/qdeclarativetext/data/qtbug_14734.qml @@ -0,0 +1,10 @@ +import QtQuick 1.0 + +Rectangle { + width: 640 + height: 480 + + Text { + text: "Ã " + } +} diff --git a/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp b/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp index a3bba3b..dbb822b 100644 --- a/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp +++ b/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp @@ -109,6 +109,7 @@ private slots: void testQtQuick11Attributes(); void testQtQuick11Attributes_data(); + void qtbug_14734(); private: QStringList standard; QStringList richText; @@ -1204,6 +1205,19 @@ void tst_qdeclarativetext::testQtQuick11Attributes_data() << ""; } +void tst_qdeclarativetext::qtbug_14734() +{ + QDeclarativeView *canvas = createView(SRCDIR "/data/qtbug_14734.qml"); + QVERIFY(canvas); + + canvas->show(); + QApplication::setActiveWindow(canvas); + QTest::qWaitForWindowShown(canvas); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas)); + + delete canvas; +} + QTEST_MAIN(tst_qdeclarativetext) #include "tst_qdeclarativetext.moc" diff --git a/tests/auto/declarative/qdeclarativetextedit/data/positionAt.qml b/tests/auto/declarative/qdeclarativetextedit/data/positionAt.qml new file mode 100644 index 0000000..e010135 --- /dev/null +++ b/tests/auto/declarative/qdeclarativetextedit/data/positionAt.qml @@ -0,0 +1,9 @@ +import QtQuick 1.0 + +TextEdit { + focus: true + objectName: "myInput" + width: 50 + height: 25 + text: "This is\n a long piece of text" +} diff --git a/tests/auto/declarative/qdeclarativetextedit/tst_qdeclarativetextedit.cpp b/tests/auto/declarative/qdeclarativetextedit/tst_qdeclarativetextedit.cpp index 2c3ec7c..dc1063f 100644 --- a/tests/auto/declarative/qdeclarativetextedit/tst_qdeclarativetextedit.cpp +++ b/tests/auto/declarative/qdeclarativetextedit/tst_qdeclarativetextedit.cpp @@ -121,6 +121,8 @@ private slots: void dragMouseSelection(); void inputMethodHints(); + void positionAt(); + void cursorDelegate(); void cursorVisible(); void delegateLoading_data(); @@ -140,6 +142,7 @@ private slots: void preeditMicroFocus(); void inputContextMouseHandler(); + void inputMethodComposing(); private: void simulateKey(QDeclarativeView *, int key); @@ -1250,6 +1253,56 @@ void tst_qdeclarativetextedit::inputMethodHints() delete canvas; } +void tst_qdeclarativetextedit::positionAt() +{ + QDeclarativeView *canvas = createView(SRCDIR "/data/positionAt.qml"); + QVERIFY(canvas->rootObject() != 0); + canvas->show(); + canvas->setFocus(); + QApplication::setActiveWindow(canvas); + QTest::qWaitForWindowShown(canvas); + + QDeclarativeTextEdit *texteditObject = qobject_cast<QDeclarativeTextEdit *>(canvas->rootObject()); + QVERIFY(texteditObject != 0); + + QFontMetrics fm(texteditObject->font()); + const int y0 = fm.height() / 2; + const int y1 = fm.height() * 3 / 2; + + int pos = texteditObject->positionAt(texteditObject->width()/2, y0); + int diff = abs(int(fm.width(texteditObject->text().left(pos))-texteditObject->width()/2)); + + // some tollerance for different fonts. +#ifdef Q_OS_LINUX + QVERIFY(diff < 2); +#else + QVERIFY(diff < 5); +#endif + + const qreal x0 = texteditObject->positionToRectangle(pos).x(); + const qreal x1 = texteditObject->positionToRectangle(pos + 1).x(); + + QString preeditText = texteditObject->text().mid(0, pos); + texteditObject->setText(texteditObject->text().mid(pos)); + texteditObject->setCursorPosition(0); + + QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>()); + QApplication::sendEvent(canvas, &inputEvent); + + // Check all points within the preedit text return the same position. + QCOMPARE(texteditObject->positionAt(0, y0), 0); + QCOMPARE(texteditObject->positionAt(x0 / 2, y0), 0); + QCOMPARE(texteditObject->positionAt(x0, y0), 0); + + // Verify positioning returns to normal after the preedit text. + QCOMPARE(texteditObject->positionAt(x1, y0), 1); + QCOMPARE(texteditObject->positionToRectangle(1).x(), x1); + + QVERIFY(texteditObject->positionAt(x0 / 2, y1) > 0); + + delete canvas; +} + void tst_qdeclarativetextedit::cursorDelegate() { QDeclarativeView* view = createView(SRCDIR "/data/cursorTest.qml"); @@ -2079,6 +2132,43 @@ void tst_qdeclarativetextedit::inputContextMouseHandler() ic.eventType = QEvent::None; } +void tst_qdeclarativetextedit::inputMethodComposing() +{ + QString text = "supercalifragisiticexpialidocious!"; + + QGraphicsScene scene; + QGraphicsView view(&scene); + MyInputContext ic; + view.setInputContext(&ic); + QDeclarativeTextEdit edit; + edit.setWidth(200); + edit.setText(text.mid(0, 12)); + edit.setCursorPosition(12); + edit.setPos(0, 0); + edit.setFocus(true); + scene.addItem(&edit); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); + + QSignalSpy spy(&edit, SIGNAL(inputMethodComposingChanged())); + + QCOMPARE(edit.isInputMethodComposing(), false); + + ic.sendEvent(QInputMethodEvent(text.mid(3), QList<QInputMethodEvent::Attribute>())); + QCOMPARE(edit.isInputMethodComposing(), true); + QCOMPARE(spy.count(), 1); + + ic.sendEvent(QInputMethodEvent(text.mid(12), QList<QInputMethodEvent::Attribute>())); + QCOMPARE(edit.isInputMethodComposing(), true); + QCOMPARE(spy.count(), 1); + + ic.sendEvent(QInputMethodEvent()); + QCOMPARE(edit.isInputMethodComposing(), false); + QCOMPARE(spy.count(), 2); +} + QTEST_MAIN(tst_qdeclarativetextedit) #include "tst_qdeclarativetextedit.moc" diff --git a/tests/auto/declarative/qdeclarativetextinput/tst_qdeclarativetextinput.cpp b/tests/auto/declarative/qdeclarativetextinput/tst_qdeclarativetextinput.cpp index 6cb57a2..24ce0e4 100644 --- a/tests/auto/declarative/qdeclarativetextinput/tst_qdeclarativetextinput.cpp +++ b/tests/auto/declarative/qdeclarativetextinput/tst_qdeclarativetextinput.cpp @@ -111,6 +111,7 @@ private slots: void passwordCharacter(); void cursorDelegate(); void cursorVisible(); + void cursorRectangle(); void navigation(); void copyAndPaste(); void readOnly(); @@ -128,6 +129,7 @@ private slots: void preeditAutoScroll(); void preeditMicroFocus(); void inputContextMouseHandler(); + void inputMethodComposing(); private: void simulateKey(QDeclarativeView *, int key); @@ -975,6 +977,25 @@ void tst_qdeclarativetextinput::positionAt() QCOMPARE(textinputObject->positionAt(x, QDeclarativeTextInput::CursorBetweenCharacters), pos + 1); QCOMPARE(textinputObject->positionAt(x, QDeclarativeTextInput::CursorOnCharacter), pos); + const qreal x0 = textinputObject->positionToRectangle(pos).x(); + const qreal x1 = textinputObject->positionToRectangle(pos + 1).x(); + + QString preeditText = textinputObject->text().mid(0, pos); + textinputObject->setText(textinputObject->text().mid(pos)); + textinputObject->setCursorPosition(0); + + QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>()); + QApplication::sendEvent(canvas, &inputEvent); + + // Check all points within the preedit text return the same position. + QCOMPARE(textinputObject->positionAt(0), 0); + QCOMPARE(textinputObject->positionAt(x0 / 2), 0); + QCOMPARE(textinputObject->positionAt(x0), 0); + + // Verify positioning returns to normal after the preedit text. + QCOMPARE(textinputObject->positionAt(x1), 1); + QCOMPARE(textinputObject->positionToRectangle(1).x(), x1); + delete canvas; } @@ -1153,6 +1174,24 @@ void tst_qdeclarativetextinput::inputMethods() QApplication::sendEvent(canvas, &event); QCOMPARE(input->text(), QString("My Hello world!")); + input->setCursorPosition(2); + event.setCommitString("Your", -2, 2); + QApplication::sendEvent(canvas, &event); + QCOMPARE(input->text(), QString("Your Hello world!")); + QCOMPARE(input->cursorPosition(), 4); + + input->setCursorPosition(7); + event.setCommitString("Goodbye", -2, 5); + QApplication::sendEvent(canvas, &event); + QCOMPARE(input->text(), QString("Your Goodbye world!")); + QCOMPARE(input->cursorPosition(), 12); + + input->setCursorPosition(8); + event.setCommitString("Our", -8, 4); + QApplication::sendEvent(canvas, &event); + QCOMPARE(input->text(), QString("Our Goodbye world!")); + QCOMPARE(input->cursorPosition(), 7); + delete canvas; } @@ -1394,6 +1433,41 @@ void tst_qdeclarativetextinput::cursorVisible() #endif } +void tst_qdeclarativetextinput::cursorRectangle() +{ + QString text = "Hello World!"; + + QDeclarativeTextInput input; + input.setText(text); + QFontMetricsF fm(input.font()); + input.setWidth(fm.width(text.mid(0, 5))); + + QRect r; + + for (int i = 0; i <= 5; ++i) { + input.setCursorPosition(i); + r = input.cursorRectangle(); + int textWidth = fm.width(text.mid(0, i)); + + QVERIFY(r.left() < textWidth); + QVERIFY(r.right() > textWidth); + } + + // Check the cursor rectangle remains within the input bounding rect when auto scrolling. + QVERIFY(r.left() < input.boundingRect().width()); + QVERIFY(r.right() >= input.width()); + + for (int i = 6; i < text.length(); ++i) { + input.setCursorPosition(i); + QCOMPARE(r, input.cursorRectangle()); + } + + for (int i = text.length() - 2; i >= 0; --i) { + input.setCursorPosition(i); + QVERIFY(r.right() >= 0); + } +} + void tst_qdeclarativetextinput::readOnly() { QDeclarativeView *canvas = createView(SRCDIR "/data/readOnly.qml"); @@ -1848,7 +1922,8 @@ void tst_qdeclarativetextinput::preeditAutoScroll() MyInputContext ic; view.setInputContext(&ic); QDeclarativeTextInput input; - input.setWidth(QFontMetricsF(input.font()).width(committedText)); + QFontMetricsF fm(input.font()); + input.setWidth(fm.width(committedText)); input.setText(committedText); input.setPos(0, 0); input.setFocus(true); @@ -1861,7 +1936,7 @@ void tst_qdeclarativetextinput::preeditAutoScroll() // test the text is scrolled so the preedit is visible. ic.sendPreeditText(preeditText.mid(0, 3), 1); QVERIFY(input.positionAt(0) != 0); - QCOMPARE(input.positionAt(input.width()), 8); + QVERIFY(input.cursorRectangle().left() < input.boundingRect().width()); // test the text is scrolled back when the preedit is removed. ic.sendEvent(QInputMethodEvent()); @@ -1870,26 +1945,31 @@ void tst_qdeclarativetextinput::preeditAutoScroll() // test if the preedit is larger than the text input that the // character preceding the cursor is still visible. + qreal x = input.positionToRectangle(0).x(); for (int i = 0; i < 3; ++i) { ic.sendPreeditText(preeditText, i + 1); - QCOMPARE(input.positionAt(0), 5 + i); + QVERIFY(input.cursorRectangle().right() >= fm.width(preeditText.at(i))); + QVERIFY(input.positionToRectangle(0).x() < x); + x = input.positionToRectangle(0).x(); } for (int i = 1; i >= 0; --i) { ic.sendPreeditText(preeditText, i + 1); - QCOMPARE(input.positionAt(0), 5 + i); + QVERIFY(input.cursorRectangle().right() >= fm.width(preeditText.at(i))); + QVERIFY(input.positionToRectangle(0).x() > x); + x = input.positionToRectangle(0).x(); } // Test incrementing the preedit cursor doesn't cause further // scrolling when right most text is visible. ic.sendPreeditText(preeditText, preeditText.length() - 3); - int position = input.positionAt(0); + x = input.positionToRectangle(0).x(); for (int i = 2; i >= 0; --i) { ic.sendPreeditText(preeditText, preeditText.length() - i); - QCOMPARE(input.positionAt(0), position); + QCOMPARE(input.positionToRectangle(0).x(), x); } for (int i = 1; i < 3; ++i) { ic.sendPreeditText(preeditText, preeditText.length() - i); - QCOMPARE(input.positionAt(0), position); + QCOMPARE(input.positionToRectangle(0).x(), x); } // Test disabling auto scroll. @@ -2074,6 +2154,43 @@ void tst_qdeclarativetextinput::inputContextMouseHandler() ic.eventType = QEvent::None; } +void tst_qdeclarativetextinput::inputMethodComposing() +{ + QString text = "supercalifragisiticexpialidocious!"; + + QGraphicsScene scene; + QGraphicsView view(&scene); + MyInputContext ic; + view.setInputContext(&ic); + QDeclarativeTextInput input; + input.setWidth(200); + input.setText(text.mid(0, 12)); + input.setCursorPosition(12); + input.setPos(0, 0); + input.setFocus(true); + scene.addItem(&input); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); + + QSignalSpy spy(&input, SIGNAL(inputMethodComposingChanged())); + + QCOMPARE(input.isInputMethodComposing(), false); + + ic.sendEvent(QInputMethodEvent(text.mid(3), QList<QInputMethodEvent::Attribute>())); + QCOMPARE(input.isInputMethodComposing(), true); + QCOMPARE(spy.count(), 1); + + ic.sendEvent(QInputMethodEvent(text.mid(12), QList<QInputMethodEvent::Attribute>())); + QCOMPARE(input.isInputMethodComposing(), true); + QCOMPARE(spy.count(), 1); + + ic.sendEvent(QInputMethodEvent()); + QCOMPARE(input.isInputMethodComposing(), false); + QCOMPARE(spy.count(), 2); +} + QTEST_MAIN(tst_qdeclarativetextinput) #include "tst_qdeclarativetextinput.moc" diff --git a/tests/auto/gui.pro b/tests/auto/gui.pro index 07dc5ef..186f00c 100644 --- a/tests/auto/gui.pro +++ b/tests/auto/gui.pro @@ -112,6 +112,7 @@ SUBDIRS=\ qmimedata \ qmouseevent_modal \ qmovie \ + qvolatileimage \ qnetworkaccessmanager_and_qprogressdialog \ qnetworkcachemetadata \ qnetworkdiskcache \ diff --git a/tests/auto/qvolatileimage/qvolatileimage.pro b/tests/auto/qvolatileimage/qvolatileimage.pro new file mode 100644 index 0000000..5a0a613 --- /dev/null +++ b/tests/auto/qvolatileimage/qvolatileimage.pro @@ -0,0 +1,7 @@ +load(qttest_p4) +SOURCES += tst_qvolatileimage.cpp + +symbian { + TARGET.EPOCHEAPSIZE = 0x200000 0x800000 + LIBS += -lfbscli +} diff --git a/tests/auto/qvolatileimage/tst_qvolatileimage.cpp b/tests/auto/qvolatileimage/tst_qvolatileimage.cpp new file mode 100644 index 0000000..b91a150 --- /dev/null +++ b/tests/auto/qvolatileimage/tst_qvolatileimage.cpp @@ -0,0 +1,403 @@ +/**************************************************************************** +** +** 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 test suite 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 <QtTest/QtTest> +#include <QtGui/qpainter.h> +#include <QtGui/qpaintengine.h> +#include <QtGui/private/qvolatileimage_p.h> +#ifdef Q_OS_SYMBIAN +#include <fbs.h> +#endif + +class tst_QVolatileImage : public QObject +{ + Q_OBJECT + +public: + tst_QVolatileImage() { } + +private slots: + void create(); + void ensureFormat(); + void dataAccess(); + void sharing(); + void paint(); + void fill(); + void copy(); + void bitmap(); +}; + +void tst_QVolatileImage::create() +{ + QVolatileImage nullImg; + QVERIFY(nullImg.isNull()); + + QVolatileImage img(100, 200, QImage::Format_ARGB32); + QVERIFY(!img.isNull()); + QCOMPARE(img.width(), 100); + QCOMPARE(img.height(), 200); + QCOMPARE(img.format(), QImage::Format_ARGB32); + QCOMPARE(img.byteCount(), img.bytesPerLine() * img.height()); + QCOMPARE(img.hasAlphaChannel(), true); + QCOMPARE(img.depth(), 32); + + QImage source(12, 23, QImage::Format_ARGB32_Premultiplied); + img = QVolatileImage(source); + QVERIFY(!img.isNull()); + QCOMPARE(img.width(), 12); + QCOMPARE(img.height(), 23); + QCOMPARE(img.format(), source.format()); + QCOMPARE(img.byteCount(), img.bytesPerLine() * img.height()); + QVERIFY(img.imageRef() == source); + QVERIFY(img.toImage() == source); + QCOMPARE(img.hasAlphaChannel(), true); + QCOMPARE(img.hasAlphaChannel(), img.imageRef().hasAlphaChannel()); + QCOMPARE(img.hasAlphaChannel(), img.toImage().hasAlphaChannel()); + QCOMPARE(img.depth(), 32); + +#ifdef Q_OS_SYMBIAN + CFbsBitmap *bmp = new CFbsBitmap; + QVERIFY(bmp->Create(TSize(100, 50), EColor16MAP) == KErrNone); + QVolatileImage bmpimg(bmp); + QVERIFY(!bmpimg.isNull()); + QCOMPARE(bmpimg.width(), 100); + QCOMPARE(bmpimg.height(), 50); + // Verify that we only did handle duplication, not pixel data copying. + QCOMPARE(bmpimg.constBits(), (const uchar *) bmp->DataAddress()); + delete bmp; + // Check if content is still valid. + QImage copyimg = bmpimg.toImage(); + QCOMPARE(copyimg.format(), QImage::Format_ARGB32_Premultiplied); +#endif +} + +void tst_QVolatileImage::ensureFormat() +{ + QImage source(12, 23, QImage::Format_ARGB32_Premultiplied); + QVolatileImage img(source); + QVERIFY(!img.isNull()); + QVERIFY(img.imageRef() == source); + QVERIFY(img.toImage() == source); + + QVERIFY(img.ensureFormat(QImage::Format_ARGB32_Premultiplied)); // no-op + QVERIFY(img.imageRef() == source); + QVERIFY(img.toImage() == source); + QVERIFY(img.format() == QImage::Format_ARGB32_Premultiplied); + + QVERIFY(img.ensureFormat(QImage::Format_RGB32)); // new data under-the-hood + QVERIFY(img.imageRef() != source); + QVERIFY(img.toImage() != source); + QVERIFY(img.format() == QImage::Format_RGB32); + +#ifdef Q_OS_SYMBIAN + CFbsBitmap *bmp = new CFbsBitmap; + QVERIFY(bmp->Create(TSize(100, 50), EColor16MAP) == KErrNone); + QVolatileImage bmpimg(bmp); + QVERIFY(bmpimg.ensureFormat(QImage::Format_ARGB32_Premultiplied)); // no-op + QCOMPARE(bmpimg.constBits(), (const uchar *) bmp->DataAddress()); + + // A different format should cause data copying. + QVERIFY(bmpimg.ensureFormat(QImage::Format_RGB32)); + QVERIFY(bmpimg.constBits() != (const uchar *) bmp->DataAddress()); + const uchar *prevBits = bmpimg.constBits(); + + QVERIFY(bmpimg.ensureFormat(QImage::Format_RGB16)); + QVERIFY(bmpimg.constBits() != (const uchar *) bmp->DataAddress()); + QVERIFY(bmpimg.constBits() != prevBits); + prevBits = bmpimg.constBits(); + + QVERIFY(bmpimg.ensureFormat(QImage::Format_MonoLSB)); + QVERIFY(bmpimg.constBits() != (const uchar *) bmp->DataAddress()); + QVERIFY(bmpimg.constBits() != prevBits); + + delete bmp; +#endif +} + +void tst_QVolatileImage::dataAccess() +{ + QImage source(12, 23, QImage::Format_ARGB32_Premultiplied); + QVolatileImage img(source); + QVERIFY(!img.isNull()); + img.beginDataAccess(); + QVERIFY(img.constBits()); + QVERIFY(img.imageRef().constBits()); + QVERIFY(img.bits()); + QVERIFY(img.imageRef().bits()); + img.endDataAccess(); + + img = QVolatileImage(12, 23, QImage::Format_ARGB32); + img.beginDataAccess(); + QVERIFY(img.constBits() && img.bits()); + img.endDataAccess(); +} + +void tst_QVolatileImage::sharing() +{ + QVolatileImage img1(100, 100, QImage::Format_ARGB32); + QVolatileImage img2 = img1; + img1.beginDataAccess(); + img2.beginDataAccess(); + QVERIFY(img1.constBits() == img2.constBits()); + img2.endDataAccess(); + img1.endDataAccess(); + img1.imageRef(); // non-const call, should detach + img1.beginDataAccess(); + img2.beginDataAccess(); + QVERIFY(img1.constBits() != img2.constBits()); + img2.endDataAccess(); + img1.endDataAccess(); + + // toImage() should return a copy of the internal QImage. + // imageRef() is a reference to the internal QImage. + QVERIFY(img1.imageRef().constBits() != img1.toImage().constBits()); + +#ifdef Q_OS_SYMBIAN + CFbsBitmap *bmp = new CFbsBitmap; + QVERIFY(bmp->Create(TSize(100, 50), EColor16MAP) == KErrNone); + QVolatileImage bmpimg(bmp); + QVolatileImage bmpimg2; + bmpimg2 = bmpimg; + QCOMPARE(bmpimg.constBits(), (const uchar *) bmp->DataAddress()); + QCOMPARE(bmpimg2.constBits(), (const uchar *) bmp->DataAddress()); + // Now force a detach, which should copy the pixel data under-the-hood. + bmpimg.imageRef(); + QVERIFY(bmpimg.constBits() != (const uchar *) bmp->DataAddress()); + QCOMPARE(bmpimg2.constBits(), (const uchar *) bmp->DataAddress()); + delete bmp; +#endif +} + +bool fuzzyCompareImages(const QImage &image1, const QImage &image2, int tolerance) +{ + if (image1.bytesPerLine() != image2.bytesPerLine() + || image1.width() != image2.width() + || image1.height() != image2.height()) { + return false; + } + for (int i = 0; i < image1.height(); i++) { + const uchar *line1 = image1.scanLine(i); + const uchar *line2 = image2.scanLine(i); + int bytes = image1.bytesPerLine(); + for (int j = 0; j < bytes; j++) { + int delta = line1[j] - line2[j]; + if (qAbs(delta) > tolerance) + return false; + } + } + return true; +} + +void tst_QVolatileImage::paint() +{ +#ifdef Q_OS_SYMBIAN + QVolatileImage img(100, 100, QImage::Format_ARGB32); + img.beginDataAccess(); + img.imageRef().fill(QColor(Qt::green).rgba()); + QPainter p(&img.imageRef()); + p.drawRect(10, 10, 50, 50); + p.end(); + img.endDataAccess(); + QImage imgA = img.toImage(); + + // The following assumes that on openvg the pixmapdata is backed by QVolatileImage) + // (and that openvg is in use) + // It should pass with any engine nonetheless. + // See if painting into the underlying QImage succeeds. + QPixmap pm(100, 100); + if (pm.paintEngine()->type() == QPaintEngine::Raster) { + pm.fill(Qt::green); + QPainter pmp(&pm); + pmp.drawRect(10, 10, 50, 50); + pmp.end(); + QImage imgB = pm.toImage(); + QVERIFY(fuzzyCompareImages(imgA, imgB, 0)); + // Exercise the accelerated QVolatileImagePaintEngine::drawPixmap() a bit. + QPixmap targetPm(pm.size()); + targetPm.fill(Qt::black); + pmp.begin(&targetPm); + pmp.drawPixmap(QPointF(0, 0), pm); + pmp.end(); + imgB = targetPm.toImage(); + QVERIFY(fuzzyCompareImages(imgA, imgB, 0)); + // Now the overload taking rects. + targetPm.fill(Qt::black); + pmp.begin(&targetPm); + QRectF rect(QPointF(0, 0), pm.size()); + pmp.drawPixmap(rect, pm, rect); + pmp.end(); + imgB = targetPm.toImage(); + QVERIFY(fuzzyCompareImages(imgA, imgB, 0)); + } else { + QSKIP("Pixmaps not painted via raster, skipping paint test", SkipSingle); + } +#endif +} + +void tst_QVolatileImage::fill() +{ + QVolatileImage img(100, 100, QImage::Format_ARGB32_Premultiplied); + QColor col = QColor(10, 20, 30); + img.fill(col.rgba()); + QVERIFY(img.imageRef().pixel(1, 1) == col.rgba()); + QVERIFY(img.toImage().pixel(1, 1) == col.rgba()); + +#ifdef Q_OS_SYMBIAN + CFbsBitmap *bmp = static_cast<CFbsBitmap *>(img.duplicateNativeImage()); + QVERIFY(bmp); + TRgb pix; + bmp->GetPixel(pix, TPoint(1, 1)); + QCOMPARE(pix.Red(), col.red()); + QCOMPARE(pix.Green(), col.green()); + QCOMPARE(pix.Blue(), col.blue()); + delete bmp; +#endif +} + +void tst_QVolatileImage::copy() +{ + QVolatileImage img(100, 100, QImage::Format_RGB32); + img.beginDataAccess(); + img.imageRef().fill(QColor(Qt::green).rgba()); + QPainter p(&img.imageRef()); + p.drawRect(10, 10, 50, 50); + p.end(); + img.endDataAccess(); + + QVolatileImage img2(100, 100, QImage::Format_RGB32); + img2.copyFrom(&img, QRect()); + QImage imgA = img.toImage(); + QImage imgB = img2.toImage(); + QCOMPARE(imgA.size(), imgB.size()); + QVERIFY(fuzzyCompareImages(imgA, imgB, 0)); + + img2 = QVolatileImage(20, 20, QImage::Format_RGB32); + img2.copyFrom(&img, QRect(5, 5, 20, 20)); + imgA = img.toImage().copy(5, 5, 20, 20); + imgB = img2.toImage(); + QCOMPARE(imgA.size(), imgB.size()); + QVERIFY(fuzzyCompareImages(imgA, imgB, 0)); +} + +void tst_QVolatileImage::bitmap() +{ +#ifdef Q_OS_SYMBIAN + CFbsBitmap *bmp = new CFbsBitmap; + QVERIFY(bmp->Create(TSize(100, 50), EColor64K) == KErrNone); + QVolatileImage bmpimg(bmp); + CFbsBitmap *dupbmp = static_cast<CFbsBitmap *>(bmpimg.duplicateNativeImage()); + QVERIFY(dupbmp); + QVERIFY(dupbmp != bmp); + QCOMPARE(dupbmp->DataAddress(), bmp->DataAddress()); + delete dupbmp; + delete bmp; + bmpimg.beginDataAccess(); + qMemSet(bmpimg.bits(), 0, bmpimg.byteCount()); + qMemSet(bmpimg.bits(), 1, bmpimg.bytesPerLine() * bmpimg.height()); + bmpimg.endDataAccess(); + + // Test bgr->rgb conversion in case of EColor16M. + bmp = new CFbsBitmap; + QVERIFY(bmp->Create(TSize(101, 89), EColor16M) == KErrNone); + bmp->BeginDataAccess(); + TUint32 *addr = bmp->DataAddress(); + uint rgb = QColor(10, 20, 30).rgb(); + qMemCopy(bmp->DataAddress(), &rgb, 3); + bmp->EndDataAccess(); + TRgb symrgb; + bmp->GetPixel(symrgb, TPoint(0, 0)); + QVERIFY(symrgb.Red() == 10 && symrgb.Green() == 20 && symrgb.Blue() == 30); + bmpimg = QVolatileImage(bmp); + QVERIFY(bmpimg.toImage().pixel(0, 0) == rgb); + // check if there really was a conversion + bmp->BeginDataAccess(); + bmpimg.beginDataAccess(); + qMemCopy(&rgb, bmpimg.constBits(), 3); + uint rgb2 = rgb; + qMemCopy(&rgb2, bmp->DataAddress(), 3); + QVERIFY(rgb != rgb2); + bmpimg.endDataAccess(true); + bmp->EndDataAccess(true); + delete bmp; + + bmp = new CFbsBitmap; + QVERIFY(bmp->Create(TSize(101, 89), EGray2) == KErrNone); + bmpimg = QVolatileImage(bmp); // inverts pixels, but should do it in place + QCOMPARE(bmpimg.constBits(), (const uchar *) bmp->DataAddress()); + QCOMPARE(bmpimg.format(), QImage::Format_MonoLSB); + bmpimg.ensureFormat(QImage::Format_ARGB32_Premultiplied); + QVERIFY(bmpimg.constBits() != (const uchar *) bmp->DataAddress()); + QCOMPARE(bmpimg.format(), QImage::Format_ARGB32_Premultiplied); + delete bmp; + + // The following two formats must be optimal always. + bmp = new CFbsBitmap; + QVERIFY(bmp->Create(TSize(101, 89), EColor16MAP) == KErrNone); + bmpimg = QVolatileImage(bmp); + QCOMPARE(bmpimg.format(), QImage::Format_ARGB32_Premultiplied); + QCOMPARE(bmpimg.constBits(), (const uchar *) bmp->DataAddress()); + bmpimg.ensureFormat(QImage::Format_ARGB32_Premultiplied); + QCOMPARE(bmpimg.constBits(), (const uchar *) bmp->DataAddress()); + delete bmp; + bmp = new CFbsBitmap; + QVERIFY(bmp->Create(TSize(101, 89), EColor16MU) == KErrNone); + bmpimg = QVolatileImage(bmp); + QCOMPARE(bmpimg.format(), QImage::Format_RGB32); + QCOMPARE(bmpimg.constBits(), (const uchar *) bmp->DataAddress()); + bmpimg.ensureFormat(QImage::Format_RGB32); + QCOMPARE(bmpimg.constBits(), (const uchar *) bmp->DataAddress()); + delete bmp; + +#else + QSKIP("CFbsBitmap is only available on Symbian, skipping bitmap test", SkipSingle); +#endif +} + +int main(int argc, char *argv[]) +{ + QApplication::setGraphicsSystem("openvg"); + QApplication app(argc, argv); + tst_QVolatileImage tc; + return QTest::qExec(&tc, argc, argv); +} + +#include "tst_qvolatileimage.moc" |