diff options
author | Qt Continuous Integration System <qt-info@nokia.com> | 2010-04-01 16:15:01 (GMT) |
---|---|---|
committer | Qt Continuous Integration System <qt-info@nokia.com> | 2010-04-01 16:15:01 (GMT) |
commit | c272d672fc1863f9e3a201e93ad277c734749845 (patch) | |
tree | 76676724c821878811841b817110438a52be820f | |
parent | 5fb8d729ddf56003af326ba4c9dbff56b57ed105 (diff) | |
parent | 7c939ad26e2d79a5c72ace724dfa2a3be8e398b3 (diff) | |
download | Qt-c272d672fc1863f9e3a201e93ad277c734749845.zip Qt-c272d672fc1863f9e3a201e93ad277c734749845.tar.gz Qt-c272d672fc1863f9e3a201e93ad277c734749845.tar.bz2 |
Merge branch '4.7' of scm.dev.nokia.troll.no:qt/oslo-staging-2 into 4.7-integration
* '4.7' of scm.dev.nokia.troll.no:qt/oslo-staging-2: (67 commits)
Skip tst_QGraphicsItem::updateMicroFocus test on Windows and Mac as it does not passes
QScript: more missing APIShim
Fix compilation warnings
QScript: fix compilation warning
QScript: fix APIShim usage.
QtScript: remove duplicated APIshim in QScriptValueIterator
Attempt to fix the test.
Total makeover of SVG image reader
Add a QGraphicsItem::updateMicroFocus() to QGraphicsItem.
Micro optimization after feedback from Jan-Arve.
Show only one dialog when using QFileDialog on Mac.
Re-fix compilation: re-add symbols introduced in Qt 4.6.3 to Qt 4.7
Fix bad merge from 4.6.
Doc: Updated the qdoc manual with i18n info. Fixed a code example.
Fix QStaticText test on Mac
Support the pen set on the painter in QStaticText when using rich text
Fix setting font for QStaticText on Linux and Mac
Hack .pro files on windows to define QT_NO_EGL
Fix QDate::isLeapYear() for years < 1
One more test for chinese codecs
...
100 files changed, 6672 insertions, 1387 deletions
diff --git a/doc/src/legal/3rdparty.qdoc b/doc/src/legal/3rdparty.qdoc index d608038..8d0cd2a 100644 --- a/doc/src/legal/3rdparty.qdoc +++ b/doc/src/legal/3rdparty.qdoc @@ -454,4 +454,43 @@ OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. See \c src/3rdparty/webkit/JavaScriptCore/wtf/dtoa.cpp for license details. + + \section1 Pixman (\c pixman) version 0.17.11 + + \e{pixman is a library that provides low-level pixel manipulation + features such as image compositing and trapezoid rasterization.} -- quoted + from \c src/3rdparty/pixman/README + + We are only using the pixman-arm-neon-asm.h and pixman-arm-neon-asm.S + source files which have the following copyright and license header: + + \hr + + Copyright © 2009 Nokia Corporation + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + Author: Siarhei Siamashka (siarhei.siamashka@nokia.com) + + \hr + + See \c src/3rdparty/pixman/pixman-arm-neon-asm.h and + \c src/3rdparty/pixman/pixman-arm-neon-asm.S */ diff --git a/doc/src/modules.qdoc b/doc/src/modules.qdoc index b13861f..8731d57 100644 --- a/doc/src/modules.qdoc +++ b/doc/src/modules.qdoc @@ -493,12 +493,13 @@ \snippet doc/src/snippets/code/doc_src_qtxmlpatterns.qdoc 1 - \section1 Further Links + \section1 Further Reading General overviews of XQuery and XSchema can be found in the \l{Using XML Technologies} document. - An introduction to the XQuery language can be found in \l{A Short Path to XQuery}. + An introduction to the XQuery language can be found in + \l{A Short Path to XQuery}. \section1 License Information diff --git a/examples/graphicsview/weatheranchorlayout/main.cpp b/examples/graphicsview/weatheranchorlayout/main.cpp index aa06476..391fdd8 100644 --- a/examples/graphicsview/weatheranchorlayout/main.cpp +++ b/examples/graphicsview/weatheranchorlayout/main.cpp @@ -97,6 +97,8 @@ protected: case Qt::MaximumSize: sh = QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); break; + default: + break; } return sh; } @@ -167,15 +169,6 @@ private: }; -static QGraphicsProxyWidget *createItem(const QString &name = "Unnamed") -{ - QGraphicsProxyWidget *w = new QGraphicsProxyWidget; - w->setWidget(new QPushButton(name)); - w->setData(0, name); - w->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - return w; -} - int main(int argc, char **argv) { Q_INIT_RESOURCE(weatheranchorlayout); diff --git a/examples/script/qstetrix/tetrixboard.cpp b/examples/script/qstetrix/tetrixboard.cpp index 7d44c4c..f35740d 100644 --- a/examples/script/qstetrix/tetrixboard.cpp +++ b/examples/script/qstetrix/tetrixboard.cpp @@ -54,7 +54,12 @@ TetrixBoard::TetrixBoard(QWidget *parent) void TetrixBoard::setNextPieceLabel(QWidget *label) { - nextPieceLabel = qobject_cast<QLabel*>(label); + nextPieceLbl = qobject_cast<QLabel*>(label); +} + +QLabel *TetrixBoard::nextPieceLabel() const +{ + return nextPieceLbl; } QObject *TetrixBoard::getTimer() @@ -82,16 +87,16 @@ void TetrixBoard::keyPressEvent(QKeyEvent *event) void TetrixBoard::showNextPiece(int width, int height) { - if (!nextPieceLabel) + if (!nextPieceLabel()) return; QPixmap pixmap(width * squareWidth(), height * squareHeight()); QPainter painter(&pixmap); - painter.fillRect(pixmap.rect(), nextPieceLabel->palette().background()); + painter.fillRect(pixmap.rect(), nextPieceLabel()->palette().background()); emit paintNextPieceRequested(&painter); - nextPieceLabel->setPixmap(pixmap); + nextPieceLabel()->setPixmap(pixmap); } void TetrixBoard::drawPauseScreen(QPainter *painter) diff --git a/examples/script/qstetrix/tetrixboard.h b/examples/script/qstetrix/tetrixboard.h index 7a04317..781ec39 100644 --- a/examples/script/qstetrix/tetrixboard.h +++ b/examples/script/qstetrix/tetrixboard.h @@ -45,21 +45,19 @@ #include <QTimer> #include <QFrame> #include <QPointer> - -QT_BEGIN_NAMESPACE -class QLabel; -QT_END_NAMESPACE +#include <QLabel> class TetrixBoard : public QFrame { Q_OBJECT Q_PROPERTY(QObject* timer READ getTimer) - Q_PROPERTY(QWidget* nextPieceLabel WRITE setNextPieceLabel) + Q_PROPERTY(QWidget* nextPieceLabel READ nextPieceLabel WRITE setNextPieceLabel) public: TetrixBoard(QWidget *parent = 0); void setNextPieceLabel(QWidget *label); + QLabel *nextPieceLabel() const; void setBoardWidth(int width); void setBoardHeight(int height); QSize minimumSizeHint() const; @@ -95,7 +93,7 @@ private: int squareHeight() { return contentsRect().height() / BoardHeight; } QTimer *timer; - QPointer<QLabel> nextPieceLabel; + QPointer<QLabel> nextPieceLbl; QImage image; }; diff --git a/qmake/project.cpp b/qmake/project.cpp index 9e6db10..01a3843 100644 --- a/qmake/project.cpp +++ b/qmake/project.cpp @@ -1507,6 +1507,7 @@ QMakeProject::read(uchar cmd) void QMakeProject::validateModes() { +#if !defined(QT_BUILD_QMAKE_NO_GENERATORS) if (Option::host_mode == Option::HOST_UNKNOWN_MODE || Option::target_mode == Option::TARG_UNKNOWN_MODE) { Option::HOST_MODE host_mode; @@ -1543,6 +1544,7 @@ void QMakeProject::validateModes() } } } +#endif // !defined(QT_BUILD_QMAKE_NO_GENERATORS) } bool diff --git a/src/3rdparty/harfbuzz/src/harfbuzz-greek.c b/src/3rdparty/harfbuzz/src/harfbuzz-greek.c index 59f3077..2e9b858 100644 --- a/src/3rdparty/harfbuzz/src/harfbuzz-greek.c +++ b/src/3rdparty/harfbuzz/src/harfbuzz-greek.c @@ -77,10 +77,12 @@ static HB_UChar16 compose_0x300(HB_UChar16 base) return 0x1fdd; return 0; } - const hb_greek_decomposition *d = decompose_0x300; - while (d->base && d->base != base) - ++d; - return d->composed; + { + const hb_greek_decomposition *d = decompose_0x300; + while (d->base && d->base != base) + ++d; + return d->composed; + } } static const hb_greek_decomposition decompose_0x301[] = { @@ -115,10 +117,12 @@ static HB_UChar16 compose_0x301(HB_UChar16 base) if (base == 0x1ffe) return 0x1fde; } - const hb_greek_decomposition *d = decompose_0x301; - while (d->base && d->base != base) - ++d; - return d->composed; + { + const hb_greek_decomposition *d = decompose_0x301; + while (d->base && d->base != base) + ++d; + return d->composed; + } } static const hb_greek_decomposition decompose_0x304[] = { @@ -351,8 +355,7 @@ static HB_UChar16 compose_0x345(HB_UChar16 base) */ HB_Bool HB_GreekShape(HB_ShaperItem *shaper_item) { - assert(shaper_item->item.script == HB_Script_Greek); - + const int availableGlyphs = shaper_item->num_glyphs; const HB_UChar16 *uc = shaper_item->string + shaper_item->item.pos; unsigned short *logClusters = shaper_item->log_clusters; HB_GlyphAttributes *attributes = shaper_item->attributes; @@ -363,6 +366,9 @@ HB_Bool HB_GreekShape(HB_ShaperItem *shaper_item) hb_uint32 i; HB_STACKARRAY(HB_UChar16, shapedChars, 2 * shaper_item->item.length); + + assert(shaper_item->item.script == HB_Script_Greek); + *shapedChars = *uc; logClusters[0] = 0; @@ -430,7 +436,6 @@ HB_Bool HB_GreekShape(HB_ShaperItem *shaper_item) #ifndef NO_OPENTYPE if (HB_SelectScript(shaper_item, greek_features)) { - const int availableGlyphs = shaper_item->num_glyphs; HB_OpenTypeShape(shaper_item, /*properties*/0); return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/TRUE); } diff --git a/src/3rdparty/harfbuzz/tests/shaping/main.cpp b/src/3rdparty/harfbuzz/tests/shaping/main.cpp index b48b0a9..320e8ee 100644 --- a/src/3rdparty/harfbuzz/tests/shaping/main.cpp +++ b/src/3rdparty/harfbuzz/tests/shaping/main.cpp @@ -272,7 +272,6 @@ Shaper::Shaper(FT_Face face, HB_Script script, const QString &str) } -#if defined(Q_WS_X11) static bool decomposedShaping(FT_Face face, HB_Script script, const QChar &ch) { QString uc = QString().append(ch); @@ -318,7 +317,6 @@ static bool decomposedShaping(FT_Face face, HB_Script script, const QChar &ch) qDebug(" decomposed glyph result = %s", str.toLatin1().constData()); return false; } -#endif struct ShapeTable { unsigned short unicode[16]; @@ -382,6 +380,44 @@ void tst_QScriptEngine::greek() continue; QVERIFY( decomposedShaping(face, HB_Script_Greek, QChar(uc)) ); } + FT_Done_Face(face); + } else { + QSKIP("couln't find DejaVu Sans", SkipAll); + } + + + face = loadFace("SBL_grk.ttf"); + if (face) { + for (int uc = 0x1f00; uc <= 0x1fff; ++uc) { + QString str; + str.append(uc); + if (str.normalized(QString::NormalizationForm_D).normalized(QString::NormalizationForm_C) != str) { + //qDebug() << "skipping" << hex << uc; + continue; + } + if (uc == 0x1fc1 || uc == 0x1fed) + continue; + QVERIFY( decomposedShaping(face, HB_Script_Greek, QChar(uc)) ); + + } + + const ShapeTable shape_table [] = { + { { 0x3b1, 0x300, 0x313, 0x0 }, + { 0xb8, 0x3d3, 0x3c7, 0x0 } }, + { { 0x3b1, 0x313, 0x300, 0x0 }, + { 0xd4, 0x0 } }, + + { {0}, {0} } + }; + + + const ShapeTable *s = shape_table; + while (s->unicode[0]) { + QVERIFY( shaping(face, s, HB_Script_Greek) ); + ++s; + } + + FT_Done_Face(face); } else { QSKIP("couln't find DejaVu Sans", SkipAll); } diff --git a/src/3rdparty/phonon/ds9/iodevicereader.cpp b/src/3rdparty/phonon/ds9/iodevicereader.cpp index 2dff1fe..9152712 100644 --- a/src/3rdparty/phonon/ds9/iodevicereader.cpp +++ b/src/3rdparty/phonon/ds9/iodevicereader.cpp @@ -139,10 +139,6 @@ namespace Phonon { QMutexLocker locker(&m_mutexRead); - if (m_mediaGraph->isStopping()) { - return VFW_E_WRONG_STATE; - } - if(streamSize() != 1 && pos + length > streamSize()) { //it tries to read outside of the boundaries return E_FAIL; @@ -158,9 +154,6 @@ namespace Phonon int oldSize = currentBufferSize(); while (currentBufferSize() < int(length)) { needData(); - if (m_mediaGraph->isStopping()) { - return VFW_E_WRONG_STATE; - } if (oldSize == currentBufferSize()) { break; //we didn't get any data diff --git a/src/3rdparty/pixman/README b/src/3rdparty/pixman/README new file mode 100644 index 0000000..843b069 --- /dev/null +++ b/src/3rdparty/pixman/README @@ -0,0 +1,26 @@ +pixman is a library that provides low-level pixel manipulation +features such as image compositing and trapezoid rasterization. + +Please submit bugs & patches to the libpixman bugzilla: + + https://bugs.freedesktop.org/enter_bug.cgi?product=pixman + +All questions regarding this software should be directed to either the +Xorg mailing list: + + http://lists.freedesktop.org/mailman/listinfo/xorg + +or the cairo mailing list: + + http://lists.freedesktop.org/mailman/listinfo/cairo + +The master development code repository can be found at: + + git://anongit.freedesktop.org/git/pixman + + http://gitweb.freedesktop.org/?p=pixman;a=summary + +For more information on the git code manager, see: + + http://wiki.x.org/wiki/GitPage + diff --git a/src/3rdparty/pixman/pixman-arm-neon-asm.S b/src/3rdparty/pixman/pixman-arm-neon-asm.S new file mode 100644 index 0000000..eb8cc4c --- /dev/null +++ b/src/3rdparty/pixman/pixman-arm-neon-asm.S @@ -0,0 +1,1709 @@ +/* + * Copyright © 2009 Nokia Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Siarhei Siamashka (siarhei.siamashka@nokia.com) + */ + +/* + * This file contains implementations of NEON optimized pixel processing + * functions. There is no full and detailed tutorial, but some functions + * (those which are exposing some new or interesting features) are + * extensively commented and can be used as examples. + * + * You may want to have a look at the comments for following functions: + * - pixman_composite_over_8888_0565_asm_neon + * - pixman_composite_over_n_8_0565_asm_neon + */ + +/* Prevent the stack from becoming executable for no reason... */ +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif + + .text + .fpu neon + .arch armv7a + .altmacro + +#include "pixman-arm-neon-asm.h" + +/* Global configuration options and preferences */ + +/* + * The code can optionally make use of unaligned memory accesses to improve + * performance of handling leading/trailing pixels for each scanline. + * Configuration variable RESPECT_STRICT_ALIGNMENT can be set to 0 for + * example in linux if unaligned memory accesses are not configured to + * generate.exceptions. + */ +.set RESPECT_STRICT_ALIGNMENT, 1 + +/* + * Set default prefetch type. There is a choice between the following options: + * + * PREFETCH_TYPE_NONE (may be useful for the ARM cores where PLD is set to work + * as NOP to workaround some HW bugs or for whatever other reason) + * + * PREFETCH_TYPE_SIMPLE (may be useful for simple single-issue ARM cores where + * advanced prefetch intruduces heavy overhead) + * + * PREFETCH_TYPE_ADVANCED (useful for superscalar cores such as ARM Cortex-A8 + * which can run ARM and NEON instructions simultaneously so that extra ARM + * instructions do not add (many) extra cycles, but improve prefetch efficiency) + * + * Note: some types of function can't support advanced prefetch and fallback + * to simple one (those which handle 24bpp pixels) + */ +.set PREFETCH_TYPE_DEFAULT, PREFETCH_TYPE_ADVANCED + +/* Prefetch distance in pixels for simple prefetch */ +.set PREFETCH_DISTANCE_SIMPLE, 64 + +/* + * Implementation of pixman_composite_over_8888_0565_asm_neon + * + * This function takes a8r8g8b8 source buffer, r5g6b5 destination buffer and + * performs OVER compositing operation. Function fast_composite_over_8888_0565 + * from pixman-fast-path.c does the same in C and can be used as a reference. + * + * First we need to have some NEON assembly code which can do the actual + * operation on the pixels and provide it to the template macro. + * + * Template macro quite conveniently takes care of emitting all the necessary + * code for memory reading and writing (including quite tricky cases of + * handling unaligned leading/trailing pixels), so we only need to deal with + * the data in NEON registers. + * + * NEON registers allocation in general is recommented to be the following: + * d0, d1, d2, d3 - contain loaded source pixel data + * d4, d5, d6, d7 - contain loaded destination pixels (if they are needed) + * d24, d25, d26, d27 - contain loading mask pixel data (if mask is used) + * d28, d29, d30, d31 - place for storing the result (destination pixels) + * + * As can be seen above, four 64-bit NEON registers are used for keeping + * intermediate pixel data and up to 8 pixels can be processed in one step + * for 32bpp formats (16 pixels for 16bpp, 32 pixels for 8bpp). + * + * This particular function uses the following registers allocation: + * d0, d1, d2, d3 - contain loaded source pixel data + * d4, d5 - contain loaded destination pixels (they are needed) + * d28, d29 - place for storing the result (destination pixels) + */ + +/* + * Step one. We need to have some code to do some arithmetics on pixel data. + * This is implemented as a pair of macros: '*_head' and '*_tail'. When used + * back-to-back, they take pixel data from {d0, d1, d2, d3} and {d4, d5}, + * perform all the needed calculations and write the result to {d28, d29}. + * The rationale for having two macros and not just one will be explained + * later. In practice, any single monolitic function which does the work can + * be split into two parts in any arbitrary way without affecting correctness. + * + * There is one special trick here too. Common template macro can optionally + * make our life a bit easier by doing R, G, B, A color components + * deinterleaving for 32bpp pixel formats (and this feature is used in + * 'pixman_composite_over_8888_0565_asm_neon' function). So it means that + * instead of having 8 packed pixels in {d0, d1, d2, d3} registers, we + * actually use d0 register for blue channel (a vector of eight 8-bit + * values), d1 register for green, d2 for red and d3 for alpha. This + * simple conversion can be also done with a few NEON instructions: + * + * Packed to planar conversion: + * vuzp.8 d0, d1 + * vuzp.8 d2, d3 + * vuzp.8 d1, d3 + * vuzp.8 d0, d2 + * + * Planar to packed conversion: + * vzip.8 d0, d2 + * vzip.8 d1, d3 + * vzip.8 d2, d3 + * vzip.8 d0, d1 + * + * But pixel can be loaded directly in planar format using VLD4.8 NEON + * instruction. It is 1 cycle slower than VLD1.32, so this is not always + * desirable, that's why deinterleaving is optional. + * + * But anyway, here is the code: + */ +.macro pixman_composite_over_8888_0565_process_pixblock_head + /* convert 8 r5g6b5 pixel data from {d4, d5} to planar 8-bit format + and put data into d6 - red, d7 - green, d30 - blue */ + vshrn.u16 d6, q2, #8 + vshrn.u16 d7, q2, #3 + vsli.u16 q2, q2, #5 + vsri.u8 d6, d6, #5 + vmvn.8 d3, d3 /* invert source alpha */ + vsri.u8 d7, d7, #6 + vshrn.u16 d30, q2, #2 + /* now do alpha blending, storing results in 8-bit planar format + into d16 - red, d19 - green, d18 - blue */ + vmull.u8 q10, d3, d6 + vmull.u8 q11, d3, d7 + vmull.u8 q12, d3, d30 + vrshr.u16 q13, q10, #8 + vrshr.u16 q3, q11, #8 + vrshr.u16 q15, q12, #8 + vraddhn.u16 d20, q10, q13 + vraddhn.u16 d23, q11, q3 + vraddhn.u16 d22, q12, q15 +.endm + +.macro pixman_composite_over_8888_0565_process_pixblock_tail + /* ... continue alpha blending */ + vqadd.u8 d16, d2, d20 + vqadd.u8 q9, q0, q11 + /* convert the result to r5g6b5 and store it into {d28, d29} */ + vshll.u8 q14, d16, #8 + vshll.u8 q8, d19, #8 + vshll.u8 q9, d18, #8 + vsri.u16 q14, q8, #5 + vsri.u16 q14, q9, #11 +.endm + +/* + * OK, now we got almost everything that we need. Using the above two + * macros, the work can be done right. But now we want to optimize + * it a bit. ARM Cortex-A8 is an in-order core, and benefits really + * a lot from good code scheduling and software pipelining. + * + * Let's construct some code, which will run in the core main loop. + * Some pseudo-code of the main loop will look like this: + * head + * while (...) { + * tail + * head + * } + * tail + * + * It may look a bit weird, but this setup allows to hide instruction + * latencies better and also utilize dual-issue capability more + * efficiently (make pairs of load-store and ALU instructions). + * + * So what we need now is a '*_tail_head' macro, which will be used + * in the core main loop. A trivial straightforward implementation + * of this macro would look like this: + * + * pixman_composite_over_8888_0565_process_pixblock_tail + * vst1.16 {d28, d29}, [DST_W, :128]! + * vld1.16 {d4, d5}, [DST_R, :128]! + * vld4.32 {d0, d1, d2, d3}, [SRC]! + * pixman_composite_over_8888_0565_process_pixblock_head + * cache_preload 8, 8 + * + * Now it also got some VLD/VST instructions. We simply can't move from + * processing one block of pixels to the other one with just arithmetics. + * The previously processed data needs to be written to memory and new + * data needs to be fetched. Fortunately, this main loop does not deal + * with partial leading/trailing pixels and can load/store a full block + * of pixels in a bulk. Additionally, destination buffer is already + * 16 bytes aligned here (which is good for performance). + * + * New things here are DST_R, DST_W, SRC and MASK identifiers. These + * are the aliases for ARM registers which are used as pointers for + * accessing data. We maintain separate pointers for reading and writing + * destination buffer (DST_R and DST_W). + * + * Another new thing is 'cache_preload' macro. It is used for prefetching + * data into CPU L2 cache and improve performance when dealing with large + * images which are far larger than cache size. It uses one argument + * (actually two, but they need to be the same here) - number of pixels + * in a block. Looking into 'pixman-arm-neon-asm.h' can provide some + * details about this macro. Moreover, if good performance is needed + * the code from this macro needs to be copied into '*_tail_head' macro + * and mixed with the rest of code for optimal instructions scheduling. + * We are actually doing it below. + * + * Now after all the explanations, here is the optimized code. + * Different instruction streams (originaling from '*_head', '*_tail' + * and 'cache_preload' macro) use different indentation levels for + * better readability. Actually taking the code from one of these + * indentation levels and ignoring a few VLD/VST instructions would + * result in exactly the code from '*_head', '*_tail' or 'cache_preload' + * macro! + */ + +#if 1 + +.macro pixman_composite_over_8888_0565_process_pixblock_tail_head + vqadd.u8 d16, d2, d20 + vld1.16 {d4, d5}, [DST_R, :128]! + vqadd.u8 q9, q0, q11 + vshrn.u16 d6, q2, #8 + vld4.8 {d0, d1, d2, d3}, [SRC]! + vshrn.u16 d7, q2, #3 + vsli.u16 q2, q2, #5 + vshll.u8 q14, d16, #8 + PF add PF_X, PF_X, #8 + vshll.u8 q8, d19, #8 + PF tst PF_CTL, #0xF + vsri.u8 d6, d6, #5 + PF addne PF_X, PF_X, #8 + vmvn.8 d3, d3 + PF subne PF_CTL, PF_CTL, #1 + vsri.u8 d7, d7, #6 + vshrn.u16 d30, q2, #2 + vmull.u8 q10, d3, d6 + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + vmull.u8 q11, d3, d7 + vmull.u8 q12, d3, d30 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + vsri.u16 q14, q8, #5 + PF cmp PF_X, ORIG_W + vshll.u8 q9, d18, #8 + vrshr.u16 q13, q10, #8 + PF subge PF_X, PF_X, ORIG_W + vrshr.u16 q3, q11, #8 + vrshr.u16 q15, q12, #8 + PF subges PF_CTL, PF_CTL, #0x10 + vsri.u16 q14, q9, #11 + PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + vraddhn.u16 d20, q10, q13 + vraddhn.u16 d23, q11, q3 + PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vraddhn.u16 d22, q12, q15 + vst1.16 {d28, d29}, [DST_W, :128]! +.endm + +#else + +/* If we did not care much about the performance, we would just use this... */ +.macro pixman_composite_over_8888_0565_process_pixblock_tail_head + pixman_composite_over_8888_0565_process_pixblock_tail + vst1.16 {d28, d29}, [DST_W, :128]! + vld1.16 {d4, d5}, [DST_R, :128]! + vld4.32 {d0, d1, d2, d3}, [SRC]! + pixman_composite_over_8888_0565_process_pixblock_head + cache_preload 8, 8 +.endm + +#endif + +/* + * And now the final part. We are using 'generate_composite_function' macro + * to put all the stuff together. We are specifying the name of the function + * which we want to get, number of bits per pixel for the source, mask and + * destination (0 if unused, like mask in this case). Next come some bit + * flags: + * FLAG_DST_READWRITE - tells that the destination buffer is both read + * and written, for write-only buffer we would use + * FLAG_DST_WRITEONLY flag instead + * FLAG_DEINTERLEAVE_32BPP - tells that we prefer to work with planar data + * and separate color channels for 32bpp format. + * The next things are: + * - the number of pixels processed per iteration (8 in this case, because + * that's the maximum what can fit into four 64-bit NEON registers). + * - prefetch distance, measured in pixel blocks. In this case it is 5 times + * by 8 pixels. That would be 40 pixels, or up to 160 bytes. Optimal + * prefetch distance can be selected by running some benchmarks. + * + * After that we specify some macros, these are 'default_init', + * 'default_cleanup' here which are empty (but it is possible to have custom + * init/cleanup macros to be able to save/restore some extra NEON registers + * like d8-d15 or do anything else) followed by + * 'pixman_composite_over_8888_0565_process_pixblock_head', + * 'pixman_composite_over_8888_0565_process_pixblock_tail' and + * 'pixman_composite_over_8888_0565_process_pixblock_tail_head' + * which we got implemented above. + * + * The last part is the NEON registers allocation scheme. + */ +generate_composite_function \ + pixman_composite_over_8888_0565_asm_neon, 32, 0, 16, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_over_8888_0565_process_pixblock_head, \ + pixman_composite_over_8888_0565_process_pixblock_tail, \ + pixman_composite_over_8888_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 24 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_over_n_0565_process_pixblock_head + /* convert 8 r5g6b5 pixel data from {d4, d5} to planar 8-bit format + and put data into d6 - red, d7 - green, d30 - blue */ + vshrn.u16 d6, q2, #8 + vshrn.u16 d7, q2, #3 + vsli.u16 q2, q2, #5 + vsri.u8 d6, d6, #5 + vsri.u8 d7, d7, #6 + vshrn.u16 d30, q2, #2 + /* now do alpha blending, storing results in 8-bit planar format + into d16 - red, d19 - green, d18 - blue */ + vmull.u8 q10, d3, d6 + vmull.u8 q11, d3, d7 + vmull.u8 q12, d3, d30 + vrshr.u16 q13, q10, #8 + vrshr.u16 q3, q11, #8 + vrshr.u16 q15, q12, #8 + vraddhn.u16 d20, q10, q13 + vraddhn.u16 d23, q11, q3 + vraddhn.u16 d22, q12, q15 +.endm + +.macro pixman_composite_over_n_0565_process_pixblock_tail + /* ... continue alpha blending */ + vqadd.u8 d16, d2, d20 + vqadd.u8 q9, q0, q11 + /* convert the result to r5g6b5 and store it into {d28, d29} */ + vshll.u8 q14, d16, #8 + vshll.u8 q8, d19, #8 + vshll.u8 q9, d18, #8 + vsri.u16 q14, q8, #5 + vsri.u16 q14, q9, #11 +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_n_0565_process_pixblock_tail_head + pixman_composite_over_n_0565_process_pixblock_tail + vld1.16 {d4, d5}, [DST_R, :128]! + vst1.16 {d28, d29}, [DST_W, :128]! + pixman_composite_over_n_0565_process_pixblock_head +.endm + +.macro pixman_composite_over_n_0565_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d3[0]}, [DUMMY] + vdup.8 d0, d3[0] + vdup.8 d1, d3[1] + vdup.8 d2, d3[2] + vdup.8 d3, d3[3] + vmvn.8 d3, d3 /* invert source alpha */ +.endm + +generate_composite_function \ + pixman_composite_over_n_0565_asm_neon, 0, 0, 16, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_0565_init, \ + default_cleanup, \ + pixman_composite_over_n_0565_process_pixblock_head, \ + pixman_composite_over_n_0565_process_pixblock_tail, \ + pixman_composite_over_n_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 24 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_8888_0565_process_pixblock_head + vshll.u8 q8, d1, #8 + vshll.u8 q14, d2, #8 + vshll.u8 q9, d0, #8 +.endm + +.macro pixman_composite_src_8888_0565_process_pixblock_tail + vsri.u16 q14, q8, #5 + vsri.u16 q14, q9, #11 +.endm + +.macro pixman_composite_src_8888_0565_process_pixblock_tail_head + vsri.u16 q14, q8, #5 + PF add PF_X, PF_X, #8 + PF tst PF_CTL, #0xF + vld4.8 {d0, d1, d2, d3}, [SRC]! + PF addne PF_X, PF_X, #8 + PF subne PF_CTL, PF_CTL, #1 + vsri.u16 q14, q9, #11 + PF cmp PF_X, ORIG_W + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + vshll.u8 q8, d1, #8 + vst1.16 {d28, d29}, [DST_W, :128]! + PF subge PF_X, PF_X, ORIG_W + PF subges PF_CTL, PF_CTL, #0x10 + vshll.u8 q14, d2, #8 + PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + vshll.u8 q9, d0, #8 +.endm + +generate_composite_function \ + pixman_composite_src_8888_0565_asm_neon, 32, 0, 16, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_8888_0565_process_pixblock_head, \ + pixman_composite_src_8888_0565_process_pixblock_tail, \ + pixman_composite_src_8888_0565_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_src_0565_8888_process_pixblock_head + vshrn.u16 d30, q0, #8 + vshrn.u16 d29, q0, #3 + vsli.u16 q0, q0, #5 + vmov.u8 d31, #255 + vsri.u8 d30, d30, #5 + vsri.u8 d29, d29, #6 + vshrn.u16 d28, q0, #2 +.endm + +.macro pixman_composite_src_0565_8888_process_pixblock_tail +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_src_0565_8888_process_pixblock_tail_head + pixman_composite_src_0565_8888_process_pixblock_tail + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + vld1.16 {d0, d1}, [SRC]! + pixman_composite_src_0565_8888_process_pixblock_head + cache_preload 8, 8 +.endm + +generate_composite_function \ + pixman_composite_src_0565_8888_asm_neon, 16, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_0565_8888_process_pixblock_head, \ + pixman_composite_src_0565_8888_process_pixblock_tail, \ + pixman_composite_src_0565_8888_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_add_8000_8000_process_pixblock_head + vqadd.u8 q14, q0, q2 + vqadd.u8 q15, q1, q3 +.endm + +.macro pixman_composite_add_8000_8000_process_pixblock_tail +.endm + +.macro pixman_composite_add_8000_8000_process_pixblock_tail_head + vld1.8 {d0, d1, d2, d3}, [SRC]! + PF add PF_X, PF_X, #32 + PF tst PF_CTL, #0xF + vld1.8 {d4, d5, d6, d7}, [DST_R, :128]! + PF addne PF_X, PF_X, #32 + PF subne PF_CTL, PF_CTL, #1 + vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! + PF cmp PF_X, ORIG_W + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + PF subge PF_X, PF_X, ORIG_W + PF subges PF_CTL, PF_CTL, #0x10 + vqadd.u8 q14, q0, q2 + PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vqadd.u8 q15, q1, q3 +.endm + +generate_composite_function \ + pixman_composite_add_8000_8000_asm_neon, 8, 0, 8, \ + FLAG_DST_READWRITE, \ + 32, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_add_8000_8000_process_pixblock_head, \ + pixman_composite_add_8000_8000_process_pixblock_tail, \ + pixman_composite_add_8000_8000_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_add_8888_8888_process_pixblock_tail_head + vld1.8 {d0, d1, d2, d3}, [SRC]! + PF add PF_X, PF_X, #8 + PF tst PF_CTL, #0xF + vld1.8 {d4, d5, d6, d7}, [DST_R, :128]! + PF addne PF_X, PF_X, #8 + PF subne PF_CTL, PF_CTL, #1 + vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! + PF cmp PF_X, ORIG_W + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + PF subge PF_X, PF_X, ORIG_W + PF subges PF_CTL, PF_CTL, #0x10 + vqadd.u8 q14, q0, q2 + PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vqadd.u8 q15, q1, q3 +.endm + +generate_composite_function \ + pixman_composite_add_8888_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_add_8000_8000_process_pixblock_head, \ + pixman_composite_add_8000_8000_process_pixblock_tail, \ + pixman_composite_add_8888_8888_process_pixblock_tail_head + +generate_composite_function_single_scanline \ + pixman_composite_scanline_add_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_add_8000_8000_process_pixblock_head, \ + pixman_composite_add_8000_8000_process_pixblock_tail, \ + pixman_composite_add_8888_8888_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_8888_8888_process_pixblock_head + vmvn.8 d24, d3 /* get inverted alpha */ + /* do alpha blending */ + vmull.u8 q8, d24, d4 + vmull.u8 q9, d24, d5 + vmull.u8 q10, d24, d6 + vmull.u8 q11, d24, d7 +.endm + +.macro pixman_composite_over_8888_8888_process_pixblock_tail + vrshr.u16 q14, q8, #8 + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + vraddhn.u16 d30, q12, q10 + vraddhn.u16 d31, q13, q11 + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 +.endm + +.macro pixman_composite_over_8888_8888_process_pixblock_tail_head + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + vrshr.u16 q14, q8, #8 + PF add PF_X, PF_X, #8 + PF tst PF_CTL, #0xF + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 + PF addne PF_X, PF_X, #8 + PF subne PF_CTL, PF_CTL, #1 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + PF cmp PF_X, ORIG_W + vraddhn.u16 d30, q12, q10 + vraddhn.u16 d31, q13, q11 + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 + vld4.8 {d0, d1, d2, d3}, [SRC]! + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + vmvn.8 d22, d3 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + PF subge PF_X, PF_X, ORIG_W + vmull.u8 q8, d22, d4 + PF subges PF_CTL, PF_CTL, #0x10 + vmull.u8 q9, d22, d5 + PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + vmull.u8 q10, d22, d6 + PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vmull.u8 q11, d22, d7 +.endm + +generate_composite_function \ + pixman_composite_over_8888_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_over_8888_8888_process_pixblock_head, \ + pixman_composite_over_8888_8888_process_pixblock_tail, \ + pixman_composite_over_8888_8888_process_pixblock_tail_head + +generate_composite_function_single_scanline \ + pixman_composite_scanline_over_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_over_8888_8888_process_pixblock_head, \ + pixman_composite_over_8888_8888_process_pixblock_tail, \ + pixman_composite_over_8888_8888_process_pixblock_tail_head + +/******************************************************************************/ + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_n_8888_process_pixblock_tail_head + pixman_composite_over_8888_8888_process_pixblock_tail + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + pixman_composite_over_8888_8888_process_pixblock_head +.endm + +.macro pixman_composite_over_n_8888_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d3[0]}, [DUMMY] + vdup.8 d0, d3[0] + vdup.8 d1, d3[1] + vdup.8 d2, d3[2] + vdup.8 d3, d3[3] +.endm + +generate_composite_function \ + pixman_composite_over_n_8888_asm_neon, 0, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_8888_init, \ + default_cleanup, \ + pixman_composite_over_8888_8888_process_pixblock_head, \ + pixman_composite_over_8888_8888_process_pixblock_tail, \ + pixman_composite_over_n_8888_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_reverse_n_8888_process_pixblock_tail_head + vrshr.u16 q14, q8, #8 + PF add PF_X, PF_X, #8 + PF tst PF_CTL, #0xF + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 + PF addne PF_X, PF_X, #8 + PF subne PF_CTL, PF_CTL, #1 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + PF cmp PF_X, ORIG_W + vraddhn.u16 d30, q12, q10 + vraddhn.u16 d31, q13, q11 + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 + vld4.8 {d0, d1, d2, d3}, [DST_R, :128]! + vmvn.8 d22, d3 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + PF subge PF_X, PF_X, ORIG_W + vmull.u8 q8, d22, d4 + PF subges PF_CTL, PF_CTL, #0x10 + vmull.u8 q9, d22, d5 + vmull.u8 q10, d22, d6 + PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vmull.u8 q11, d22, d7 +.endm + +.macro pixman_composite_over_reverse_n_8888_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d7[0]}, [DUMMY] + vdup.8 d4, d7[0] + vdup.8 d5, d7[1] + vdup.8 d6, d7[2] + vdup.8 d7, d7[3] +.endm + +generate_composite_function \ + pixman_composite_over_reverse_n_8888_asm_neon, 0, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_reverse_n_8888_init, \ + default_cleanup, \ + pixman_composite_over_8888_8888_process_pixblock_head, \ + pixman_composite_over_8888_8888_process_pixblock_tail, \ + pixman_composite_over_reverse_n_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 4, /* src_basereg */ \ + 24 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_over_n_8_0565_process_pixblock_head + /* in */ + vmull.u8 q0, d24, d8 + vmull.u8 q1, d24, d9 + vmull.u8 q6, d24, d10 + vmull.u8 q7, d24, d11 + vrshr.u16 q10, q0, #8 + vrshr.u16 q11, q1, #8 + vrshr.u16 q12, q6, #8 + vrshr.u16 q13, q7, #8 + vraddhn.u16 d0, q0, q10 + vraddhn.u16 d1, q1, q11 + vraddhn.u16 d2, q6, q12 + vraddhn.u16 d3, q7, q13 + + vshrn.u16 d6, q2, #8 + vshrn.u16 d7, q2, #3 + vsli.u16 q2, q2, #5 + vsri.u8 d6, d6, #5 + vmvn.8 d3, d3 + vsri.u8 d7, d7, #6 + vshrn.u16 d30, q2, #2 + /* now do alpha blending */ + vmull.u8 q10, d3, d6 + vmull.u8 q11, d3, d7 + vmull.u8 q12, d3, d30 + vrshr.u16 q13, q10, #8 + vrshr.u16 q3, q11, #8 + vrshr.u16 q15, q12, #8 + vraddhn.u16 d20, q10, q13 + vraddhn.u16 d23, q11, q3 + vraddhn.u16 d22, q12, q15 +.endm + +.macro pixman_composite_over_n_8_0565_process_pixblock_tail + vqadd.u8 d16, d2, d20 + vqadd.u8 q9, q0, q11 + /* convert to r5g6b5 */ + vshll.u8 q14, d16, #8 + vshll.u8 q8, d19, #8 + vshll.u8 q9, d18, #8 + vsri.u16 q14, q8, #5 + vsri.u16 q14, q9, #11 +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_n_8_0565_process_pixblock_tail_head + pixman_composite_over_n_8_0565_process_pixblock_tail + vst1.16 {d28, d29}, [DST_W, :128]! + vld1.16 {d4, d5}, [DST_R, :128]! + vld1.8 {d24}, [MASK]! + cache_preload 8, 8 + pixman_composite_over_n_8_0565_process_pixblock_head +.endm + +/* + * This function needs a special initialization of solid mask. + * Solid source pixel data is fetched from stack at ARGS_STACK_OFFSET + * offset, split into color components and replicated in d8-d11 + * registers. Additionally, this function needs all the NEON registers, + * so it has to save d8-d15 registers which are callee saved according + * to ABI. These registers are restored from 'cleanup' macro. All the + * other NEON registers are caller saved, so can be clobbered freely + * without introducing any problems. + */ +.macro pixman_composite_over_n_8_0565_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vpush {d8-d15} + vld1.32 {d11[0]}, [DUMMY] + vdup.8 d8, d11[0] + vdup.8 d9, d11[1] + vdup.8 d10, d11[2] + vdup.8 d11, d11[3] +.endm + +.macro pixman_composite_over_n_8_0565_cleanup + vpop {d8-d15} +.endm + +generate_composite_function \ + pixman_composite_over_n_8_0565_asm_neon, 0, 8, 16, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_8_0565_init, \ + pixman_composite_over_n_8_0565_cleanup, \ + pixman_composite_over_n_8_0565_process_pixblock_head, \ + pixman_composite_over_n_8_0565_process_pixblock_tail, \ + pixman_composite_over_n_8_0565_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_src_0565_0565_process_pixblock_head +.endm + +.macro pixman_composite_src_0565_0565_process_pixblock_tail +.endm + +.macro pixman_composite_src_0565_0565_process_pixblock_tail_head + vst1.16 {d0, d1, d2, d3}, [DST_W, :128]! + vld1.16 {d0, d1, d2, d3}, [SRC]! + cache_preload 16, 16 +.endm + +generate_composite_function \ + pixman_composite_src_0565_0565_asm_neon, 16, 0, 16, \ + FLAG_DST_WRITEONLY, \ + 16, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_0565_0565_process_pixblock_head, \ + pixman_composite_src_0565_0565_process_pixblock_tail, \ + pixman_composite_src_0565_0565_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_n_8_process_pixblock_head +.endm + +.macro pixman_composite_src_n_8_process_pixblock_tail +.endm + +.macro pixman_composite_src_n_8_process_pixblock_tail_head + vst1.8 {d0, d1, d2, d3}, [DST_W, :128]! +.endm + +.macro pixman_composite_src_n_8_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d0[0]}, [DUMMY] + vsli.u64 d0, d0, #8 + vsli.u64 d0, d0, #16 + vsli.u64 d0, d0, #32 + vmov d1, d0 + vmov q1, q0 +.endm + +.macro pixman_composite_src_n_8_cleanup +.endm + +generate_composite_function \ + pixman_composite_src_n_8_asm_neon, 0, 0, 8, \ + FLAG_DST_WRITEONLY, \ + 32, /* number of pixels, processed in a single block */ \ + 0, /* prefetch distance */ \ + pixman_composite_src_n_8_init, \ + pixman_composite_src_n_8_cleanup, \ + pixman_composite_src_n_8_process_pixblock_head, \ + pixman_composite_src_n_8_process_pixblock_tail, \ + pixman_composite_src_n_8_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_n_0565_process_pixblock_head +.endm + +.macro pixman_composite_src_n_0565_process_pixblock_tail +.endm + +.macro pixman_composite_src_n_0565_process_pixblock_tail_head + vst1.16 {d0, d1, d2, d3}, [DST_W, :128]! +.endm + +.macro pixman_composite_src_n_0565_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d0[0]}, [DUMMY] + vsli.u64 d0, d0, #16 + vsli.u64 d0, d0, #32 + vmov d1, d0 + vmov q1, q0 +.endm + +.macro pixman_composite_src_n_0565_cleanup +.endm + +generate_composite_function \ + pixman_composite_src_n_0565_asm_neon, 0, 0, 16, \ + FLAG_DST_WRITEONLY, \ + 16, /* number of pixels, processed in a single block */ \ + 0, /* prefetch distance */ \ + pixman_composite_src_n_0565_init, \ + pixman_composite_src_n_0565_cleanup, \ + pixman_composite_src_n_0565_process_pixblock_head, \ + pixman_composite_src_n_0565_process_pixblock_tail, \ + pixman_composite_src_n_0565_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_n_8888_process_pixblock_head +.endm + +.macro pixman_composite_src_n_8888_process_pixblock_tail +.endm + +.macro pixman_composite_src_n_8888_process_pixblock_tail_head + vst1.32 {d0, d1, d2, d3}, [DST_W, :128]! +.endm + +.macro pixman_composite_src_n_8888_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d0[0]}, [DUMMY] + vsli.u64 d0, d0, #32 + vmov d1, d0 + vmov q1, q0 +.endm + +.macro pixman_composite_src_n_8888_cleanup +.endm + +generate_composite_function \ + pixman_composite_src_n_8888_asm_neon, 0, 0, 32, \ + FLAG_DST_WRITEONLY, \ + 8, /* number of pixels, processed in a single block */ \ + 0, /* prefetch distance */ \ + pixman_composite_src_n_8888_init, \ + pixman_composite_src_n_8888_cleanup, \ + pixman_composite_src_n_8888_process_pixblock_head, \ + pixman_composite_src_n_8888_process_pixblock_tail, \ + pixman_composite_src_n_8888_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_8888_8888_process_pixblock_head +.endm + +.macro pixman_composite_src_8888_8888_process_pixblock_tail +.endm + +.macro pixman_composite_src_8888_8888_process_pixblock_tail_head + vst1.32 {d0, d1, d2, d3}, [DST_W, :128]! + vld1.32 {d0, d1, d2, d3}, [SRC]! + cache_preload 8, 8 +.endm + +generate_composite_function \ + pixman_composite_src_8888_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_WRITEONLY, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_8888_8888_process_pixblock_head, \ + pixman_composite_src_8888_8888_process_pixblock_tail, \ + pixman_composite_src_8888_8888_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_x888_8888_process_pixblock_head + vorr q0, q0, q2 + vorr q1, q1, q2 +.endm + +.macro pixman_composite_src_x888_8888_process_pixblock_tail +.endm + +.macro pixman_composite_src_x888_8888_process_pixblock_tail_head + vst1.32 {d0, d1, d2, d3}, [DST_W, :128]! + vld1.32 {d0, d1, d2, d3}, [SRC]! + vorr q0, q0, q2 + vorr q1, q1, q2 + cache_preload 8, 8 +.endm + +.macro pixman_composite_src_x888_8888_init + vmov.u8 q2, #0xFF + vshl.u32 q2, q2, #24 +.endm + +generate_composite_function \ + pixman_composite_src_x888_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_WRITEONLY, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + pixman_composite_src_x888_8888_init, \ + default_cleanup, \ + pixman_composite_src_x888_8888_process_pixblock_head, \ + pixman_composite_src_x888_8888_process_pixblock_tail, \ + pixman_composite_src_x888_8888_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_over_n_8_8888_process_pixblock_head + /* expecting deinterleaved source data in {d8, d9, d10, d11} */ + /* d8 - blue, d9 - green, d10 - red, d11 - alpha */ + /* and destination data in {d4, d5, d6, d7} */ + /* mask is in d24 (d25, d26, d27 are unused) */ + + /* in */ + vmull.u8 q0, d24, d8 + vmull.u8 q1, d24, d9 + vmull.u8 q6, d24, d10 + vmull.u8 q7, d24, d11 + vrshr.u16 q10, q0, #8 + vrshr.u16 q11, q1, #8 + vrshr.u16 q12, q6, #8 + vrshr.u16 q13, q7, #8 + vraddhn.u16 d0, q0, q10 + vraddhn.u16 d1, q1, q11 + vraddhn.u16 d2, q6, q12 + vraddhn.u16 d3, q7, q13 + vmvn.8 d24, d3 /* get inverted alpha */ + /* source: d0 - blue, d1 - green, d2 - red, d3 - alpha */ + /* destination: d4 - blue, d5 - green, d6 - red, d7 - alpha */ + /* now do alpha blending */ + vmull.u8 q8, d24, d4 + vmull.u8 q9, d24, d5 + vmull.u8 q10, d24, d6 + vmull.u8 q11, d24, d7 +.endm + +.macro pixman_composite_over_n_8_8888_process_pixblock_tail + vrshr.u16 q14, q8, #8 + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + vraddhn.u16 d30, q12, q10 + vraddhn.u16 d31, q13, q11 + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_n_8_8888_process_pixblock_tail_head + pixman_composite_over_n_8_8888_process_pixblock_tail + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + vld1.8 {d24}, [MASK]! + cache_preload 8, 8 + pixman_composite_over_n_8_8888_process_pixblock_head +.endm + +.macro pixman_composite_over_n_8_8888_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vpush {d8-d15} + vld1.32 {d11[0]}, [DUMMY] + vdup.8 d8, d11[0] + vdup.8 d9, d11[1] + vdup.8 d10, d11[2] + vdup.8 d11, d11[3] +.endm + +.macro pixman_composite_over_n_8_8888_cleanup + vpop {d8-d15} +.endm + +generate_composite_function \ + pixman_composite_over_n_8_8888_asm_neon, 0, 8, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_8_8888_init, \ + pixman_composite_over_n_8_8888_cleanup, \ + pixman_composite_over_n_8_8888_process_pixblock_head, \ + pixman_composite_over_n_8_8888_process_pixblock_tail, \ + pixman_composite_over_n_8_8888_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_n_8888_8888_ca_process_pixblock_head + /* + * 'combine_mask_ca' replacement + * + * input: solid src (n) in {d8, d9, d10, d11} + * dest in {d4, d5, d6, d7 } + * mask in {d24, d25, d26, d27} + * output: updated src in {d0, d1, d2, d3 } + * updated mask in {d24, d25, d26, d3 } + */ + vmull.u8 q0, d24, d8 + vmull.u8 q1, d25, d9 + vmull.u8 q6, d26, d10 + vmull.u8 q7, d27, d11 + vmull.u8 q9, d11, d25 + vmull.u8 q12, d11, d24 + vmull.u8 q13, d11, d26 + vrshr.u16 q8, q0, #8 + vrshr.u16 q10, q1, #8 + vrshr.u16 q11, q6, #8 + vraddhn.u16 d0, q0, q8 + vraddhn.u16 d1, q1, q10 + vraddhn.u16 d2, q6, q11 + vrshr.u16 q11, q12, #8 + vrshr.u16 q8, q9, #8 + vrshr.u16 q6, q13, #8 + vrshr.u16 q10, q7, #8 + vraddhn.u16 d24, q12, q11 + vraddhn.u16 d25, q9, q8 + vraddhn.u16 d26, q13, q6 + vraddhn.u16 d3, q7, q10 + /* + * 'combine_over_ca' replacement + * + * output: updated dest in {d28, d29, d30, d31} + */ + vmvn.8 d24, d24 + vmvn.8 d25, d25 + vmull.u8 q8, d24, d4 + vmull.u8 q9, d25, d5 + vmvn.8 d26, d26 + vmvn.8 d27, d3 + vmull.u8 q10, d26, d6 + vmull.u8 q11, d27, d7 +.endm + +.macro pixman_composite_over_n_8888_8888_ca_process_pixblock_tail + /* ... continue 'combine_over_ca' replacement */ + vrshr.u16 q14, q8, #8 + vrshr.u16 q15, q9, #8 + vrshr.u16 q6, q10, #8 + vrshr.u16 q7, q11, #8 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + vraddhn.u16 d30, q6, q10 + vraddhn.u16 d31, q7, q11 + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 +.endm + +.macro pixman_composite_over_n_8888_8888_ca_process_pixblock_tail_head + vrshr.u16 q14, q8, #8 + vrshr.u16 q15, q9, #8 + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + vrshr.u16 q6, q10, #8 + vrshr.u16 q7, q11, #8 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + vraddhn.u16 d30, q6, q10 + vraddhn.u16 d31, q7, q11 + vld4.8 {d24, d25, d26, d27}, [MASK]! + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 + cache_preload 8, 8 + pixman_composite_over_n_8888_8888_ca_process_pixblock_head + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! +.endm + +.macro pixman_composite_over_n_8888_8888_ca_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vpush {d8-d15} + vld1.32 {d11[0]}, [DUMMY] + vdup.8 d8, d11[0] + vdup.8 d9, d11[1] + vdup.8 d10, d11[2] + vdup.8 d11, d11[3] +.endm + +.macro pixman_composite_over_n_8888_8888_ca_cleanup + vpop {d8-d15} +.endm + +generate_composite_function \ + pixman_composite_over_n_8888_8888_ca_asm_neon, 0, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_8888_8888_ca_init, \ + pixman_composite_over_n_8888_8888_ca_cleanup, \ + pixman_composite_over_n_8888_8888_ca_process_pixblock_head, \ + pixman_composite_over_n_8888_8888_ca_process_pixblock_tail, \ + pixman_composite_over_n_8888_8888_ca_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_add_n_8_8_process_pixblock_head + /* expecting source data in {d8, d9, d10, d11} */ + /* d8 - blue, d9 - green, d10 - red, d11 - alpha */ + /* and destination data in {d4, d5, d6, d7} */ + /* mask is in d24, d25, d26, d27 */ + vmull.u8 q0, d24, d11 + vmull.u8 q1, d25, d11 + vmull.u8 q6, d26, d11 + vmull.u8 q7, d27, d11 + vrshr.u16 q10, q0, #8 + vrshr.u16 q11, q1, #8 + vrshr.u16 q12, q6, #8 + vrshr.u16 q13, q7, #8 + vraddhn.u16 d0, q0, q10 + vraddhn.u16 d1, q1, q11 + vraddhn.u16 d2, q6, q12 + vraddhn.u16 d3, q7, q13 + vqadd.u8 q14, q0, q2 + vqadd.u8 q15, q1, q3 +.endm + +.macro pixman_composite_add_n_8_8_process_pixblock_tail +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_add_n_8_8_process_pixblock_tail_head + pixman_composite_add_n_8_8_process_pixblock_tail + vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! + vld1.8 {d4, d5, d6, d7}, [DST_R, :128]! + vld1.8 {d24, d25, d26, d27}, [MASK]! + cache_preload 32, 32 + pixman_composite_add_n_8_8_process_pixblock_head +.endm + +.macro pixman_composite_add_n_8_8_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vpush {d8-d15} + vld1.32 {d11[0]}, [DUMMY] + vdup.8 d11, d11[3] +.endm + +.macro pixman_composite_add_n_8_8_cleanup + vpop {d8-d15} +.endm + +generate_composite_function \ + pixman_composite_add_n_8_8_asm_neon, 0, 8, 8, \ + FLAG_DST_READWRITE, \ + 32, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_add_n_8_8_init, \ + pixman_composite_add_n_8_8_cleanup, \ + pixman_composite_add_n_8_8_process_pixblock_head, \ + pixman_composite_add_n_8_8_process_pixblock_tail, \ + pixman_composite_add_n_8_8_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_add_8_8_8_process_pixblock_head + /* expecting source data in {d0, d1, d2, d3} */ + /* destination data in {d4, d5, d6, d7} */ + /* mask in {d24, d25, d26, d27} */ + vmull.u8 q8, d24, d0 + vmull.u8 q9, d25, d1 + vmull.u8 q10, d26, d2 + vmull.u8 q11, d27, d3 + vrshr.u16 q0, q8, #8 + vrshr.u16 q1, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 + vraddhn.u16 d0, q0, q8 + vraddhn.u16 d1, q1, q9 + vraddhn.u16 d2, q12, q10 + vraddhn.u16 d3, q13, q11 + vqadd.u8 q14, q0, q2 + vqadd.u8 q15, q1, q3 +.endm + +.macro pixman_composite_add_8_8_8_process_pixblock_tail +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_add_8_8_8_process_pixblock_tail_head + pixman_composite_add_8_8_8_process_pixblock_tail + vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! + vld1.8 {d4, d5, d6, d7}, [DST_R, :128]! + vld1.8 {d24, d25, d26, d27}, [MASK]! + vld1.8 {d0, d1, d2, d3}, [SRC]! + cache_preload 32, 32 + pixman_composite_add_8_8_8_process_pixblock_head +.endm + +.macro pixman_composite_add_8_8_8_init +.endm + +.macro pixman_composite_add_8_8_8_cleanup +.endm + +generate_composite_function \ + pixman_composite_add_8_8_8_asm_neon, 8, 8, 8, \ + FLAG_DST_READWRITE, \ + 32, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_add_8_8_8_init, \ + pixman_composite_add_8_8_8_cleanup, \ + pixman_composite_add_8_8_8_process_pixblock_head, \ + pixman_composite_add_8_8_8_process_pixblock_tail, \ + pixman_composite_add_8_8_8_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_add_8888_8888_8888_process_pixblock_head + /* expecting source data in {d0, d1, d2, d3} */ + /* destination data in {d4, d5, d6, d7} */ + /* mask in {d24, d25, d26, d27} */ + vmull.u8 q8, d27, d0 + vmull.u8 q9, d27, d1 + vmull.u8 q10, d27, d2 + vmull.u8 q11, d27, d3 + vrshr.u16 q0, q8, #8 + vrshr.u16 q1, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 + vraddhn.u16 d0, q0, q8 + vraddhn.u16 d1, q1, q9 + vraddhn.u16 d2, q12, q10 + vraddhn.u16 d3, q13, q11 + vqadd.u8 q14, q0, q2 + vqadd.u8 q15, q1, q3 +.endm + +.macro pixman_composite_add_8888_8888_8888_process_pixblock_tail +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_add_8888_8888_8888_process_pixblock_tail_head + pixman_composite_add_8888_8888_8888_process_pixblock_tail + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + vld4.8 {d24, d25, d26, d27}, [MASK]! + vld4.8 {d0, d1, d2, d3}, [SRC]! + cache_preload 8, 8 + pixman_composite_add_8888_8888_8888_process_pixblock_head +.endm + +generate_composite_function \ + pixman_composite_add_8888_8888_8888_asm_neon, 32, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_add_8888_8888_8888_process_pixblock_head, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail_head + +generate_composite_function_single_scanline \ + pixman_composite_scanline_add_mask_asm_neon, 32, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_add_8888_8888_8888_process_pixblock_head, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_8888_n_8888_process_pixblock_head + /* expecting source data in {d0, d1, d2, d3} */ + /* destination data in {d4, d5, d6, d7} */ + /* solid mask is in d15 */ + + /* 'in' */ + vmull.u8 q8, d15, d3 + vmull.u8 q6, d15, d2 + vmull.u8 q5, d15, d1 + vmull.u8 q4, d15, d0 + vrshr.u16 q13, q8, #8 + vrshr.u16 q12, q6, #8 + vrshr.u16 q11, q5, #8 + vrshr.u16 q10, q4, #8 + vraddhn.u16 d3, q8, q13 + vraddhn.u16 d2, q6, q12 + vraddhn.u16 d1, q5, q11 + vraddhn.u16 d0, q4, q10 + vmvn.8 d24, d3 /* get inverted alpha */ + /* now do alpha blending */ + vmull.u8 q8, d24, d4 + vmull.u8 q9, d24, d5 + vmull.u8 q10, d24, d6 + vmull.u8 q11, d24, d7 +.endm + +.macro pixman_composite_over_8888_n_8888_process_pixblock_tail + vrshr.u16 q14, q8, #8 + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + vraddhn.u16 d30, q12, q10 + vraddhn.u16 d31, q13, q11 + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_8888_n_8888_process_pixblock_tail_head + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + pixman_composite_over_8888_n_8888_process_pixblock_tail + vld4.8 {d0, d1, d2, d3}, [SRC]! + cache_preload 8, 8 + pixman_composite_over_8888_n_8888_process_pixblock_head + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! +.endm + +.macro pixman_composite_over_8888_n_8888_init + add DUMMY, sp, #48 + vpush {d8-d15} + vld1.32 {d15[0]}, [DUMMY] + vdup.8 d15, d15[3] +.endm + +.macro pixman_composite_over_8888_n_8888_cleanup + vpop {d8-d15} +.endm + +generate_composite_function \ + pixman_composite_over_8888_n_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_8888_n_8888_init, \ + pixman_composite_over_8888_n_8888_cleanup, \ + pixman_composite_over_8888_n_8888_process_pixblock_head, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail_head + +/******************************************************************************/ + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_8888_8888_8888_process_pixblock_tail_head + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + pixman_composite_over_8888_n_8888_process_pixblock_tail + vld4.8 {d0, d1, d2, d3}, [SRC]! + cache_preload 8, 8 + vld4.8 {d12, d13, d14, d15}, [MASK]! + pixman_composite_over_8888_n_8888_process_pixblock_head + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! +.endm + +.macro pixman_composite_over_8888_8888_8888_init + vpush {d8-d15} +.endm + +.macro pixman_composite_over_8888_8888_8888_cleanup + vpop {d8-d15} +.endm + +generate_composite_function \ + pixman_composite_over_8888_8888_8888_asm_neon, 32, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_8888_8888_8888_init, \ + pixman_composite_over_8888_8888_8888_cleanup, \ + pixman_composite_over_8888_n_8888_process_pixblock_head, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail, \ + pixman_composite_over_8888_8888_8888_process_pixblock_tail_head \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 12 /* mask_basereg */ + +generate_composite_function_single_scanline \ + pixman_composite_scanline_over_mask_asm_neon, 32, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + pixman_composite_over_8888_8888_8888_init, \ + pixman_composite_over_8888_8888_8888_cleanup, \ + pixman_composite_over_8888_n_8888_process_pixblock_head, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail, \ + pixman_composite_over_8888_8888_8888_process_pixblock_tail_head \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 12 /* mask_basereg */ + +/******************************************************************************/ + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_8888_8_8888_process_pixblock_tail_head + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + pixman_composite_over_8888_n_8888_process_pixblock_tail + vld4.8 {d0, d1, d2, d3}, [SRC]! + cache_preload 8, 8 + vld1.8 {d15}, [MASK]! + pixman_composite_over_8888_n_8888_process_pixblock_head + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! +.endm + +.macro pixman_composite_over_8888_8_8888_init + vpush {d8-d15} +.endm + +.macro pixman_composite_over_8888_8_8888_cleanup + vpop {d8-d15} +.endm + +generate_composite_function \ + pixman_composite_over_8888_8_8888_asm_neon, 32, 8, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_8888_8_8888_init, \ + pixman_composite_over_8888_8_8888_cleanup, \ + pixman_composite_over_8888_n_8888_process_pixblock_head, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail, \ + pixman_composite_over_8888_8_8888_process_pixblock_tail_head \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 15 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_0888_0888_process_pixblock_head +.endm + +.macro pixman_composite_src_0888_0888_process_pixblock_tail +.endm + +.macro pixman_composite_src_0888_0888_process_pixblock_tail_head + vst3.8 {d0, d1, d2}, [DST_W]! + vld3.8 {d0, d1, d2}, [SRC]! + cache_preload 8, 8 +.endm + +generate_composite_function \ + pixman_composite_src_0888_0888_asm_neon, 24, 0, 24, \ + FLAG_DST_WRITEONLY, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_0888_0888_process_pixblock_head, \ + pixman_composite_src_0888_0888_process_pixblock_tail, \ + pixman_composite_src_0888_0888_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_0888_8888_rev_process_pixblock_head + vswp d0, d2 +.endm + +.macro pixman_composite_src_0888_8888_rev_process_pixblock_tail +.endm + +.macro pixman_composite_src_0888_8888_rev_process_pixblock_tail_head + vst4.8 {d0, d1, d2, d3}, [DST_W]! + vld3.8 {d0, d1, d2}, [SRC]! + vswp d0, d2 + cache_preload 8, 8 +.endm + +.macro pixman_composite_src_0888_8888_rev_init + veor d3, d3, d3 +.endm + +generate_composite_function \ + pixman_composite_src_0888_8888_rev_asm_neon, 24, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + pixman_composite_src_0888_8888_rev_init, \ + default_cleanup, \ + pixman_composite_src_0888_8888_rev_process_pixblock_head, \ + pixman_composite_src_0888_8888_rev_process_pixblock_tail, \ + pixman_composite_src_0888_8888_rev_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_0888_0565_rev_process_pixblock_head + vshll.u8 q8, d1, #8 + vshll.u8 q9, d2, #8 +.endm + +.macro pixman_composite_src_0888_0565_rev_process_pixblock_tail + vshll.u8 q14, d0, #8 + vsri.u16 q14, q8, #5 + vsri.u16 q14, q9, #11 +.endm + +.macro pixman_composite_src_0888_0565_rev_process_pixblock_tail_head + vshll.u8 q14, d0, #8 + vld3.8 {d0, d1, d2}, [SRC]! + vsri.u16 q14, q8, #5 + vsri.u16 q14, q9, #11 + vshll.u8 q8, d1, #8 + vst1.16 {d28, d29}, [DST_W, :128]! + vshll.u8 q9, d2, #8 +.endm + +generate_composite_function \ + pixman_composite_src_0888_0565_rev_asm_neon, 24, 0, 16, \ + FLAG_DST_WRITEONLY, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_0888_0565_rev_process_pixblock_head, \ + pixman_composite_src_0888_0565_rev_process_pixblock_tail, \ + pixman_composite_src_0888_0565_rev_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_pixbuf_8888_process_pixblock_head + vmull.u8 q8, d3, d0 + vmull.u8 q9, d3, d1 + vmull.u8 q10, d3, d2 +.endm + +.macro pixman_composite_src_pixbuf_8888_process_pixblock_tail + vrshr.u16 q11, q8, #8 + vswp d3, d31 + vrshr.u16 q12, q9, #8 + vrshr.u16 q13, q10, #8 + vraddhn.u16 d30, q11, q8 + vraddhn.u16 d29, q12, q9 + vraddhn.u16 d28, q13, q10 +.endm + +.macro pixman_composite_src_pixbuf_8888_process_pixblock_tail_head + vrshr.u16 q11, q8, #8 + vswp d3, d31 + vrshr.u16 q12, q9, #8 + vrshr.u16 q13, q10, #8 + vld4.8 {d0, d1, d2, d3}, [SRC]! + vraddhn.u16 d30, q11, q8 + PF add PF_X, PF_X, #8 + PF tst PF_CTL, #0xF + PF addne PF_X, PF_X, #8 + PF subne PF_CTL, PF_CTL, #1 + vraddhn.u16 d29, q12, q9 + vraddhn.u16 d28, q13, q10 + vmull.u8 q8, d3, d0 + vmull.u8 q9, d3, d1 + vmull.u8 q10, d3, d2 + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + PF cmp PF_X, ORIG_W + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + PF subge PF_X, PF_X, ORIG_W + PF subges PF_CTL, PF_CTL, #0x10 + PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! +.endm + +generate_composite_function \ + pixman_composite_src_pixbuf_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_pixbuf_8888_process_pixblock_head, \ + pixman_composite_src_pixbuf_8888_process_pixblock_tail, \ + pixman_composite_src_pixbuf_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ diff --git a/src/3rdparty/pixman/pixman-arm-neon-asm.h b/src/3rdparty/pixman/pixman-arm-neon-asm.h new file mode 100644 index 0000000..56c3fae --- /dev/null +++ b/src/3rdparty/pixman/pixman-arm-neon-asm.h @@ -0,0 +1,906 @@ +/* + * Copyright © 2009 Nokia Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Siarhei Siamashka (siarhei.siamashka@nokia.com) + */ + +/* + * This file contains a macro ('generate_composite_function') which can + * construct 2D image processing functions, based on a common template. + * Any combinations of source, destination and mask images with 8bpp, + * 16bpp, 24bpp, 32bpp color formats are supported. + * + * This macro takes care of: + * - handling of leading and trailing unaligned pixels + * - doing most of the work related to L2 cache preload + * - encourages the use of software pipelining for better instructions + * scheduling + * + * The user of this macro has to provide some configuration parameters + * (bit depths for the images, prefetch distance, etc.) and a set of + * macros, which should implement basic code chunks responsible for + * pixels processing. See 'pixman-arm-neon-asm.S' file for the usage + * examples. + * + * TODO: + * - try overlapped pixel method (from Ian Rickards) when processing + * exactly two blocks of pixels + * - maybe add an option to do reverse scanline processing + */ + +/* + * Bit flags for 'generate_composite_function' macro which are used + * to tune generated functions behavior. + */ +.set FLAG_DST_WRITEONLY, 0 +.set FLAG_DST_READWRITE, 1 +.set FLAG_DEINTERLEAVE_32BPP, 2 + +/* + * Offset in stack where mask and source pointer/stride can be accessed + * from 'init' macro. This is useful for doing special handling for solid mask. + */ +.set ARGS_STACK_OFFSET, 40 + +/* + * Constants for selecting preferable prefetch type. + */ +.set PREFETCH_TYPE_NONE, 0 /* No prefetch at all */ +.set PREFETCH_TYPE_SIMPLE, 1 /* A simple, fixed-distance-ahead prefetch */ +.set PREFETCH_TYPE_ADVANCED, 2 /* Advanced fine-grained prefetch */ + +/* + * Definitions of supplementary pixld/pixst macros (for partial load/store of + * pixel data). + */ + +.macro pixldst1 op, elem_size, reg1, mem_operand, abits +.if abits > 0 + op&.&elem_size {d®1}, [&mem_operand&, :&abits&]! +.else + op&.&elem_size {d®1}, [&mem_operand&]! +.endif +.endm + +.macro pixldst2 op, elem_size, reg1, reg2, mem_operand, abits +.if abits > 0 + op&.&elem_size {d®1, d®2}, [&mem_operand&, :&abits&]! +.else + op&.&elem_size {d®1, d®2}, [&mem_operand&]! +.endif +.endm + +.macro pixldst4 op, elem_size, reg1, reg2, reg3, reg4, mem_operand, abits +.if abits > 0 + op&.&elem_size {d®1, d®2, d®3, d®4}, [&mem_operand&, :&abits&]! +.else + op&.&elem_size {d®1, d®2, d®3, d®4}, [&mem_operand&]! +.endif +.endm + +.macro pixldst0 op, elem_size, reg1, idx, mem_operand, abits + op&.&elem_size {d®1[idx]}, [&mem_operand&]! +.endm + +.macro pixldst3 op, elem_size, reg1, reg2, reg3, mem_operand + op&.&elem_size {d®1, d®2, d®3}, [&mem_operand&]! +.endm + +.macro pixldst30 op, elem_size, reg1, reg2, reg3, idx, mem_operand + op&.&elem_size {d®1[idx], d®2[idx], d®3[idx]}, [&mem_operand&]! +.endm + +.macro pixldst numbytes, op, elem_size, basereg, mem_operand, abits +.if numbytes == 32 + pixldst4 op, elem_size, %(basereg+4), %(basereg+5), \ + %(basereg+6), %(basereg+7), mem_operand, abits +.elseif numbytes == 16 + pixldst2 op, elem_size, %(basereg+2), %(basereg+3), mem_operand, abits +.elseif numbytes == 8 + pixldst1 op, elem_size, %(basereg+1), mem_operand, abits +.elseif numbytes == 4 + .if !RESPECT_STRICT_ALIGNMENT || (elem_size == 32) + pixldst0 op, 32, %(basereg+0), 1, mem_operand, abits + .elseif elem_size == 16 + pixldst0 op, 16, %(basereg+0), 2, mem_operand, abits + pixldst0 op, 16, %(basereg+0), 3, mem_operand, abits + .else + pixldst0 op, 8, %(basereg+0), 4, mem_operand, abits + pixldst0 op, 8, %(basereg+0), 5, mem_operand, abits + pixldst0 op, 8, %(basereg+0), 6, mem_operand, abits + pixldst0 op, 8, %(basereg+0), 7, mem_operand, abits + .endif +.elseif numbytes == 2 + .if !RESPECT_STRICT_ALIGNMENT || (elem_size == 16) + pixldst0 op, 16, %(basereg+0), 1, mem_operand, abits + .else + pixldst0 op, 8, %(basereg+0), 2, mem_operand, abits + pixldst0 op, 8, %(basereg+0), 3, mem_operand, abits + .endif +.elseif numbytes == 1 + pixldst0 op, 8, %(basereg+0), 1, mem_operand, abits +.else + .error "unsupported size: numbytes" +.endif +.endm + +.macro pixld numpix, bpp, basereg, mem_operand, abits=0 +.if bpp > 0 +.if (bpp == 32) && (numpix == 8) && (DEINTERLEAVE_32BPP_ENABLED != 0) + pixldst4 vld4, 8, %(basereg+4), %(basereg+5), \ + %(basereg+6), %(basereg+7), mem_operand, abits +.elseif (bpp == 24) && (numpix == 8) + pixldst3 vld3, 8, %(basereg+3), %(basereg+4), %(basereg+5), mem_operand +.elseif (bpp == 24) && (numpix == 4) + pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 4, mem_operand + pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 5, mem_operand + pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 6, mem_operand + pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 7, mem_operand +.elseif (bpp == 24) && (numpix == 2) + pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 2, mem_operand + pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 3, mem_operand +.elseif (bpp == 24) && (numpix == 1) + pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 1, mem_operand +.else + pixldst %(numpix * bpp / 8), vld1, %(bpp), basereg, mem_operand, abits +.endif +.endif +.endm + +.macro pixst numpix, bpp, basereg, mem_operand, abits=0 +.if bpp > 0 +.if (bpp == 32) && (numpix == 8) && (DEINTERLEAVE_32BPP_ENABLED != 0) + pixldst4 vst4, 8, %(basereg+4), %(basereg+5), \ + %(basereg+6), %(basereg+7), mem_operand, abits +.elseif (bpp == 24) && (numpix == 8) + pixldst3 vst3, 8, %(basereg+3), %(basereg+4), %(basereg+5), mem_operand +.elseif (bpp == 24) && (numpix == 4) + pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 4, mem_operand + pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 5, mem_operand + pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 6, mem_operand + pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 7, mem_operand +.elseif (bpp == 24) && (numpix == 2) + pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 2, mem_operand + pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 3, mem_operand +.elseif (bpp == 24) && (numpix == 1) + pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 1, mem_operand +.else + pixldst %(numpix * bpp / 8), vst1, %(bpp), basereg, mem_operand, abits +.endif +.endif +.endm + +.macro pixld_a numpix, bpp, basereg, mem_operand +.if (bpp * numpix) <= 128 + pixld numpix, bpp, basereg, mem_operand, %(bpp * numpix) +.else + pixld numpix, bpp, basereg, mem_operand, 128 +.endif +.endm + +.macro pixst_a numpix, bpp, basereg, mem_operand +.if (bpp * numpix) <= 128 + pixst numpix, bpp, basereg, mem_operand, %(bpp * numpix) +.else + pixst numpix, bpp, basereg, mem_operand, 128 +.endif +.endm + +.macro vuzp8 reg1, reg2 + vuzp.8 d®1, d®2 +.endm + +.macro vzip8 reg1, reg2 + vzip.8 d®1, d®2 +.endm + +/* deinterleave B, G, R, A channels for eight 32bpp pixels in 4 registers */ +.macro pixdeinterleave bpp, basereg +.if (bpp == 32) && (DEINTERLEAVE_32BPP_ENABLED != 0) + vuzp8 %(basereg+0), %(basereg+1) + vuzp8 %(basereg+2), %(basereg+3) + vuzp8 %(basereg+1), %(basereg+3) + vuzp8 %(basereg+0), %(basereg+2) +.endif +.endm + +/* interleave B, G, R, A channels for eight 32bpp pixels in 4 registers */ +.macro pixinterleave bpp, basereg +.if (bpp == 32) && (DEINTERLEAVE_32BPP_ENABLED != 0) + vzip8 %(basereg+0), %(basereg+2) + vzip8 %(basereg+1), %(basereg+3) + vzip8 %(basereg+2), %(basereg+3) + vzip8 %(basereg+0), %(basereg+1) +.endif +.endm + +/* + * This is a macro for implementing cache preload. The main idea is that + * cache preload logic is mostly independent from the rest of pixels + * processing code. It starts at the top left pixel and moves forward + * across pixels and can jump across scanlines. Prefetch distance is + * handled in an 'incremental' way: it starts from 0 and advances to the + * optimal distance over time. After reaching optimal prefetch distance, + * it is kept constant. There are some checks which prevent prefetching + * unneeded pixel lines below the image (but it still can prefetch a bit + * more data on the right side of the image - not a big issue and may + * be actually helpful when rendering text glyphs). Additional trick is + * the use of LDR instruction for prefetch instead of PLD when moving to + * the next line, the point is that we have a high chance of getting TLB + * miss in this case, and PLD would be useless. + * + * This sounds like it may introduce a noticeable overhead (when working with + * fully cached data). But in reality, due to having a separate pipeline and + * instruction queue for NEON unit in ARM Cortex-A8, normal ARM code can + * execute simultaneously with NEON and be completely shadowed by it. Thus + * we get no performance overhead at all (*). This looks like a very nice + * feature of Cortex-A8, if used wisely. We don't have a hardware prefetcher, + * but still can implement some rather advanced prefetch logic in sofware + * for almost zero cost! + * + * (*) The overhead of the prefetcher is visible when running some trivial + * pixels processing like simple copy. Anyway, having prefetch is a must + * when working with the graphics data. + */ +.macro PF a, x:vararg +.if (PREFETCH_TYPE_CURRENT == PREFETCH_TYPE_ADVANCED) + a x +.endif +.endm + +.macro cache_preload std_increment, boost_increment +.if (src_bpp_shift >= 0) || (dst_r_bpp != 0) || (mask_bpp_shift >= 0) +.if regs_shortage + PF ldr ORIG_W, [sp] /* If we are short on regs, ORIG_W is kept on stack */ +.endif +.if std_increment != 0 + PF add PF_X, PF_X, #std_increment +.endif + PF tst PF_CTL, #0xF + PF addne PF_X, PF_X, #boost_increment + PF subne PF_CTL, PF_CTL, #1 + PF cmp PF_X, ORIG_W +.if src_bpp_shift >= 0 + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] +.endif +.if dst_r_bpp != 0 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] +.endif +.if mask_bpp_shift >= 0 + PF pld, [PF_MASK, PF_X, lsl #mask_bpp_shift] +.endif + PF subge PF_X, PF_X, ORIG_W + PF subges PF_CTL, PF_CTL, #0x10 +.if src_bpp_shift >= 0 + PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! +.endif +.if dst_r_bpp != 0 + PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! +.endif +.if mask_bpp_shift >= 0 + PF ldrgeb DUMMY, [PF_MASK, MASK_STRIDE, lsl #mask_bpp_shift]! +.endif +.endif +.endm + +.macro cache_preload_simple +.if (PREFETCH_TYPE_CURRENT == PREFETCH_TYPE_SIMPLE) +.if src_bpp > 0 + pld [SRC, #(PREFETCH_DISTANCE_SIMPLE * src_bpp / 8)] +.endif +.if dst_r_bpp > 0 + pld [DST_R, #(PREFETCH_DISTANCE_SIMPLE * dst_r_bpp / 8)] +.endif +.if mask_bpp > 0 + pld [MASK, #(PREFETCH_DISTANCE_SIMPLE * mask_bpp / 8)] +.endif +.endif +.endm + +/* + * Macro which is used to process leading pixels until destination + * pointer is properly aligned (at 16 bytes boundary). When destination + * buffer uses 16bpp format, this is unnecessary, or even pointless. + */ +.macro ensure_destination_ptr_alignment process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head +.if dst_w_bpp != 24 + tst DST_R, #0xF + beq 2f + +.irp lowbit, 1, 2, 4, 8, 16 +local skip1 +.if (dst_w_bpp <= (lowbit * 8)) && ((lowbit * 8) < (pixblock_size * dst_w_bpp)) +.if lowbit < 16 /* we don't need more than 16-byte alignment */ + tst DST_R, #lowbit + beq 1f +.endif + pixld (lowbit * 8 / dst_w_bpp), src_bpp, src_basereg, SRC + pixld (lowbit * 8 / dst_w_bpp), mask_bpp, mask_basereg, MASK +.if dst_r_bpp > 0 + pixld_a (lowbit * 8 / dst_r_bpp), dst_r_bpp, dst_r_basereg, DST_R +.else + add DST_R, DST_R, #lowbit +.endif + PF add PF_X, PF_X, #(lowbit * 8 / dst_w_bpp) + sub W, W, #(lowbit * 8 / dst_w_bpp) +1: +.endif +.endr + pixdeinterleave src_bpp, src_basereg + pixdeinterleave mask_bpp, mask_basereg + pixdeinterleave dst_r_bpp, dst_r_basereg + + process_pixblock_head + cache_preload 0, pixblock_size + cache_preload_simple + process_pixblock_tail + + pixinterleave dst_w_bpp, dst_w_basereg +.irp lowbit, 1, 2, 4, 8, 16 +.if (dst_w_bpp <= (lowbit * 8)) && ((lowbit * 8) < (pixblock_size * dst_w_bpp)) +.if lowbit < 16 /* we don't need more than 16-byte alignment */ + tst DST_W, #lowbit + beq 1f +.endif + pixst_a (lowbit * 8 / dst_w_bpp), dst_w_bpp, dst_w_basereg, DST_W +1: +.endif +.endr +.endif +2: +.endm + +/* + * Special code for processing up to (pixblock_size - 1) remaining + * trailing pixels. As SIMD processing performs operation on + * pixblock_size pixels, anything smaller than this has to be loaded + * and stored in a special way. Loading and storing of pixel data is + * performed in such a way that we fill some 'slots' in the NEON + * registers (some slots naturally are unused), then perform compositing + * operation as usual. In the end, the data is taken from these 'slots' + * and saved to memory. + * + * cache_preload_flag - allows to suppress prefetch if + * set to 0 + * dst_aligned_flag - selects whether destination buffer + * is aligned + */ +.macro process_trailing_pixels cache_preload_flag, \ + dst_aligned_flag, \ + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head + tst W, #(pixblock_size - 1) + beq 2f +.irp chunk_size, 16, 8, 4, 2, 1 +.if pixblock_size > chunk_size + tst W, #chunk_size + beq 1f + pixld chunk_size, src_bpp, src_basereg, SRC + pixld chunk_size, mask_bpp, mask_basereg, MASK +.if dst_aligned_flag != 0 + pixld_a chunk_size, dst_r_bpp, dst_r_basereg, DST_R +.else + pixld chunk_size, dst_r_bpp, dst_r_basereg, DST_R +.endif +.if cache_preload_flag != 0 + PF add PF_X, PF_X, #chunk_size +.endif +1: +.endif +.endr + pixdeinterleave src_bpp, src_basereg + pixdeinterleave mask_bpp, mask_basereg + pixdeinterleave dst_r_bpp, dst_r_basereg + + process_pixblock_head +.if cache_preload_flag != 0 + cache_preload 0, pixblock_size + cache_preload_simple +.endif + process_pixblock_tail + pixinterleave dst_w_bpp, dst_w_basereg +.irp chunk_size, 16, 8, 4, 2, 1 +.if pixblock_size > chunk_size + tst W, #chunk_size + beq 1f +.if dst_aligned_flag != 0 + pixst_a chunk_size, dst_w_bpp, dst_w_basereg, DST_W +.else + pixst chunk_size, dst_w_bpp, dst_w_basereg, DST_W +.endif +1: +.endif +.endr +2: +.endm + +/* + * Macro, which performs all the needed operations to switch to the next + * scanline and start the next loop iteration unless all the scanlines + * are already processed. + */ +.macro advance_to_next_scanline start_of_loop_label +.if regs_shortage + ldrd W, [sp] /* load W and H (width and height) from stack */ +.else + mov W, ORIG_W +.endif + add DST_W, DST_W, DST_STRIDE, lsl #dst_bpp_shift +.if src_bpp != 0 + add SRC, SRC, SRC_STRIDE, lsl #src_bpp_shift +.endif +.if mask_bpp != 0 + add MASK, MASK, MASK_STRIDE, lsl #mask_bpp_shift +.endif +.if (dst_w_bpp != 24) + sub DST_W, DST_W, W, lsl #dst_bpp_shift +.endif +.if (src_bpp != 24) && (src_bpp != 0) + sub SRC, SRC, W, lsl #src_bpp_shift +.endif +.if (mask_bpp != 24) && (mask_bpp != 0) + sub MASK, MASK, W, lsl #mask_bpp_shift +.endif + subs H, H, #1 + mov DST_R, DST_W +.if regs_shortage + str H, [sp, #4] /* save updated height to stack */ +.endif + bge start_of_loop_label +.endm + +/* + * Registers are allocated in the following way by default: + * d0, d1, d2, d3 - reserved for loading source pixel data + * d4, d5, d6, d7 - reserved for loading destination pixel data + * d24, d25, d26, d27 - reserved for loading mask pixel data + * d28, d29, d30, d31 - final destination pixel data for writeback to memory + */ +.macro generate_composite_function fname, \ + src_bpp_, \ + mask_bpp_, \ + dst_w_bpp_, \ + flags, \ + pixblock_size_, \ + prefetch_distance, \ + init, \ + cleanup, \ + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head, \ + dst_w_basereg_ = 28, \ + dst_r_basereg_ = 4, \ + src_basereg_ = 0, \ + mask_basereg_ = 24 + + .func fname + .global fname + /* For ELF format also set function visibility to hidden */ +#ifdef __ELF__ + .hidden fname + .type fname, %function +#endif +fname: + push {r4-r12, lr} /* save all registers */ + +/* + * Select prefetch type for this function. If prefetch distance is + * set to 0 or one of the color formats is 24bpp, SIMPLE prefetch + * has to be used instead of ADVANCED. + */ + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_DEFAULT +.if prefetch_distance == 0 + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_NONE +.elseif (PREFETCH_TYPE_CURRENT > PREFETCH_TYPE_SIMPLE) && \ + ((src_bpp_ == 24) || (mask_bpp_ == 24) || (dst_w_bpp_ == 24)) + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_SIMPLE +.endif + +/* + * Make some macro arguments globally visible and accessible + * from other macros + */ + .set src_bpp, src_bpp_ + .set mask_bpp, mask_bpp_ + .set dst_w_bpp, dst_w_bpp_ + .set pixblock_size, pixblock_size_ + .set dst_w_basereg, dst_w_basereg_ + .set dst_r_basereg, dst_r_basereg_ + .set src_basereg, src_basereg_ + .set mask_basereg, mask_basereg_ + +/* + * Assign symbolic names to registers + */ + W .req r0 /* width (is updated during processing) */ + H .req r1 /* height (is updated during processing) */ + DST_W .req r2 /* destination buffer pointer for writes */ + DST_STRIDE .req r3 /* destination image stride */ + SRC .req r4 /* source buffer pointer */ + SRC_STRIDE .req r5 /* source image stride */ + DST_R .req r6 /* destination buffer pointer for reads */ + + MASK .req r7 /* mask pointer */ + MASK_STRIDE .req r8 /* mask stride */ + + PF_CTL .req r9 /* combined lines counter and prefetch */ + /* distance increment counter */ + PF_X .req r10 /* pixel index in a scanline for current */ + /* pretetch position */ + PF_SRC .req r11 /* pointer to source scanline start */ + /* for prefetch purposes */ + PF_DST .req r12 /* pointer to destination scanline start */ + /* for prefetch purposes */ + PF_MASK .req r14 /* pointer to mask scanline start */ + /* for prefetch purposes */ +/* + * Check whether we have enough registers for all the local variables. + * If we don't have enough registers, original width and height are + * kept on top of stack (and 'regs_shortage' variable is set to indicate + * this for the rest of code). Even if there are enough registers, the + * allocation scheme may be a bit different depending on whether source + * or mask is not used. + */ +.if (PREFETCH_TYPE_CURRENT < PREFETCH_TYPE_ADVANCED) + ORIG_W .req r10 /* saved original width */ + DUMMY .req r12 /* temporary register */ + .set regs_shortage, 0 +.elseif mask_bpp == 0 + ORIG_W .req r7 /* saved original width */ + DUMMY .req r8 /* temporary register */ + .set regs_shortage, 0 +.elseif src_bpp == 0 + ORIG_W .req r4 /* saved original width */ + DUMMY .req r5 /* temporary register */ + .set regs_shortage, 0 +.else + ORIG_W .req r1 /* saved original width */ + DUMMY .req r1 /* temporary register */ + .set regs_shortage, 1 +.endif + + .set mask_bpp_shift, -1 +.if src_bpp == 32 + .set src_bpp_shift, 2 +.elseif src_bpp == 24 + .set src_bpp_shift, 0 +.elseif src_bpp == 16 + .set src_bpp_shift, 1 +.elseif src_bpp == 8 + .set src_bpp_shift, 0 +.elseif src_bpp == 0 + .set src_bpp_shift, -1 +.else + .error "requested src bpp (src_bpp) is not supported" +.endif +.if mask_bpp == 32 + .set mask_bpp_shift, 2 +.elseif mask_bpp == 24 + .set mask_bpp_shift, 0 +.elseif mask_bpp == 8 + .set mask_bpp_shift, 0 +.elseif mask_bpp == 0 + .set mask_bpp_shift, -1 +.else + .error "requested mask bpp (mask_bpp) is not supported" +.endif +.if dst_w_bpp == 32 + .set dst_bpp_shift, 2 +.elseif dst_w_bpp == 24 + .set dst_bpp_shift, 0 +.elseif dst_w_bpp == 16 + .set dst_bpp_shift, 1 +.elseif dst_w_bpp == 8 + .set dst_bpp_shift, 0 +.else + .error "requested dst bpp (dst_w_bpp) is not supported" +.endif + +.if (((flags) & FLAG_DST_READWRITE) != 0) + .set dst_r_bpp, dst_w_bpp +.else + .set dst_r_bpp, 0 +.endif +.if (((flags) & FLAG_DEINTERLEAVE_32BPP) != 0) + .set DEINTERLEAVE_32BPP_ENABLED, 1 +.else + .set DEINTERLEAVE_32BPP_ENABLED, 0 +.endif + +.if prefetch_distance < 0 || prefetch_distance > 15 + .error "invalid prefetch distance (prefetch_distance)" +.endif + +.if src_bpp > 0 + ldr SRC, [sp, #40] +.endif +.if mask_bpp > 0 + ldr MASK, [sp, #48] +.endif + PF mov PF_X, #0 +.if src_bpp > 0 + ldr SRC_STRIDE, [sp, #44] +.endif +.if mask_bpp > 0 + ldr MASK_STRIDE, [sp, #52] +.endif + mov DST_R, DST_W + +.if src_bpp == 24 + sub SRC_STRIDE, SRC_STRIDE, W + sub SRC_STRIDE, SRC_STRIDE, W, lsl #1 +.endif +.if mask_bpp == 24 + sub MASK_STRIDE, MASK_STRIDE, W + sub MASK_STRIDE, MASK_STRIDE, W, lsl #1 +.endif +.if dst_w_bpp == 24 + sub DST_STRIDE, DST_STRIDE, W + sub DST_STRIDE, DST_STRIDE, W, lsl #1 +.endif + +/* + * Setup advanced prefetcher initial state + */ + PF mov PF_SRC, SRC + PF mov PF_DST, DST_R + PF mov PF_MASK, MASK + /* PF_CTL = prefetch_distance | ((h - 1) << 4) */ + PF mov PF_CTL, H, lsl #4 + PF add PF_CTL, #(prefetch_distance - 0x10) + + init +.if regs_shortage + push {r0, r1} +.endif + subs H, H, #1 +.if regs_shortage + str H, [sp, #4] /* save updated height to stack */ +.else + mov ORIG_W, W +.endif + blt 9f + cmp W, #(pixblock_size * 2) + blt 8f +/* + * This is the start of the pipelined loop, which if optimized for + * long scanlines + */ +0: + ensure_destination_ptr_alignment process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head + + /* Implement "head (tail_head) ... (tail_head) tail" loop pattern */ + pixld_a pixblock_size, dst_r_bpp, \ + (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R + pixld pixblock_size, src_bpp, \ + (src_basereg - pixblock_size * src_bpp / 64), SRC + pixld pixblock_size, mask_bpp, \ + (mask_basereg - pixblock_size * mask_bpp / 64), MASK + PF add PF_X, PF_X, #pixblock_size + process_pixblock_head + cache_preload 0, pixblock_size + cache_preload_simple + subs W, W, #(pixblock_size * 2) + blt 2f +1: + process_pixblock_tail_head + cache_preload_simple + subs W, W, #pixblock_size + bge 1b +2: + process_pixblock_tail + pixst_a pixblock_size, dst_w_bpp, \ + (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W + + /* Process the remaining trailing pixels in the scanline */ + process_trailing_pixels 1, 1, \ + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head + advance_to_next_scanline 0b + +.if regs_shortage + pop {r0, r1} +.endif + cleanup + pop {r4-r12, pc} /* exit */ +/* + * This is the start of the loop, designed to process images with small width + * (less than pixblock_size * 2 pixels). In this case neither pipelining + * nor prefetch are used. + */ +8: + /* Process exactly pixblock_size pixels if needed */ + tst W, #pixblock_size + beq 1f + pixld pixblock_size, dst_r_bpp, \ + (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R + pixld pixblock_size, src_bpp, \ + (src_basereg - pixblock_size * src_bpp / 64), SRC + pixld pixblock_size, mask_bpp, \ + (mask_basereg - pixblock_size * mask_bpp / 64), MASK + process_pixblock_head + process_pixblock_tail + pixst pixblock_size, dst_w_bpp, \ + (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W +1: + /* Process the remaining trailing pixels in the scanline */ + process_trailing_pixels 0, 0, \ + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head + advance_to_next_scanline 8b +9: +.if regs_shortage + pop {r0, r1} +.endif + cleanup + pop {r4-r12, pc} /* exit */ + + .unreq SRC + .unreq MASK + .unreq DST_R + .unreq DST_W + .unreq ORIG_W + .unreq W + .unreq H + .unreq SRC_STRIDE + .unreq DST_STRIDE + .unreq MASK_STRIDE + .unreq PF_CTL + .unreq PF_X + .unreq PF_SRC + .unreq PF_DST + .unreq PF_MASK + .unreq DUMMY + .endfunc +.endm + +/* + * A simplified variant of function generation template for a single + * scanline processing (for implementing pixman combine functions) + */ +.macro generate_composite_function_single_scanline fname, \ + src_bpp_, \ + mask_bpp_, \ + dst_w_bpp_, \ + flags, \ + pixblock_size_, \ + init, \ + cleanup, \ + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head, \ + dst_w_basereg_ = 28, \ + dst_r_basereg_ = 4, \ + src_basereg_ = 0, \ + mask_basereg_ = 24 + + .func fname + .global fname + /* For ELF format also set function visibility to hidden */ +#ifdef __ELF__ + .hidden fname + .type fname, %function +#endif +fname: + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_NONE +/* + * Make some macro arguments globally visible and accessible + * from other macros + */ + .set src_bpp, src_bpp_ + .set mask_bpp, mask_bpp_ + .set dst_w_bpp, dst_w_bpp_ + .set pixblock_size, pixblock_size_ + .set dst_w_basereg, dst_w_basereg_ + .set dst_r_basereg, dst_r_basereg_ + .set src_basereg, src_basereg_ + .set mask_basereg, mask_basereg_ +/* + * Assign symbolic names to registers + */ + W .req r0 /* width (is updated during processing) */ + DST_W .req r1 /* destination buffer pointer for writes */ + SRC .req r2 /* source buffer pointer */ + DST_R .req ip /* destination buffer pointer for reads */ + MASK .req r3 /* mask pointer */ + +.if (((flags) & FLAG_DST_READWRITE) != 0) + .set dst_r_bpp, dst_w_bpp +.else + .set dst_r_bpp, 0 +.endif +.if (((flags) & FLAG_DEINTERLEAVE_32BPP) != 0) + .set DEINTERLEAVE_32BPP_ENABLED, 1 +.else + .set DEINTERLEAVE_32BPP_ENABLED, 0 +.endif + + init + mov DST_R, DST_W + + cmp W, #pixblock_size + blt 8f + + ensure_destination_ptr_alignment process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head + + subs W, W, #pixblock_size + blt 7f + + /* Implement "head (tail_head) ... (tail_head) tail" loop pattern */ + pixld_a pixblock_size, dst_r_bpp, \ + (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R + pixld pixblock_size, src_bpp, \ + (src_basereg - pixblock_size * src_bpp / 64), SRC + pixld pixblock_size, mask_bpp, \ + (mask_basereg - pixblock_size * mask_bpp / 64), MASK + process_pixblock_head + subs W, W, #pixblock_size + blt 2f +1: + process_pixblock_tail_head + subs W, W, #pixblock_size + bge 1b +2: + process_pixblock_tail + pixst_a pixblock_size, dst_w_bpp, \ + (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W +7: + /* Process the remaining trailing pixels in the scanline (dst aligned) */ + process_trailing_pixels 0, 1, \ + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head + + cleanup + bx lr /* exit */ +8: + /* Process the remaining trailing pixels in the scanline (dst unaligned) */ + process_trailing_pixels 0, 0, \ + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head + + cleanup + bx lr /* exit */ + + .unreq SRC + .unreq MASK + .unreq DST_R + .unreq DST_W + .unreq W + .endfunc +.endm + +.macro default_init +.endm + +.macro default_cleanup +.endm diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h index a176149..b538787 100644 --- a/src/corelib/kernel/qmetaobject_p.h +++ b/src/corelib/kernel/qmetaobject_p.h @@ -196,7 +196,7 @@ static QByteArray normalizeTypeInternal(const char *t, const char *e, bool fixSc if (*(e-1) == '&') { // treat const reference as value t += 6; --e; - } else if (is_ident_char(*(e-1))) { // treat const value as value + } else if (is_ident_char(*(e-1)) || *(e-1) == '>') { // treat const value as value t += 6; } } @@ -287,13 +287,16 @@ static QByteArray normalizeTypeInternal(const char *t, const char *e, bool fixSc } // cv qualifers can appear after the type as well - if (t != e && (e - t >= 5 && strncmp("const", t, 5) == 0)) { + if (!is_ident_char(c) && t != e && (e - t >= 5 && strncmp("const", t, 5) == 0) + && (e - t == 5 || !is_ident_char(t[5]))) { t += 5; while (t != e && is_space(*t)) ++t; if (adjustConst && t != e && *t == '&') { // treat const ref as value ++t; + } else if (adjustConst && !star) { + // treat const as value } else if (!star) { // move const to the front (but not if const comes after a *) result.prepend("const "); diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index dbc6be2..330de20 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -1470,7 +1470,7 @@ void QObject::moveToThread(QThread *targetThread) } else if (d->threadData != currentData) { qWarning("QObject::moveToThread: Current thread (%p) is not the object's thread (%p).\n" "Cannot move to target thread (%p)\n", - d->threadData->thread, currentData->thread, targetData->thread); + currentData->thread, d->threadData->thread, targetData->thread); #ifdef Q_WS_MAC qWarning("On Mac OS X, you might be loading two sets of Qt binaries into the same process. " diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index dc1666a..459d76a 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -1336,7 +1336,10 @@ bool QDate::isValid(int year, int month, int day) bool QDate::isLeapYear(int y) { if (y < 1582) { - return qAbs(y) % 4 == 0; + if ( y < 1) { // No year 0 in Julian calendar, so -1, -5, -9 etc are leap years + ++y; + } + return y % 4 == 0; } else { return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0; } diff --git a/src/corelib/tools/qvarlengtharray.h b/src/corelib/tools/qvarlengtharray.h index aecb66e..9773d4b 100644 --- a/src/corelib/tools/qvarlengtharray.h +++ b/src/corelib/tools/qvarlengtharray.h @@ -107,6 +107,10 @@ public: Q_ASSERT(idx >= 0 && idx < s); return ptr[idx]; } + inline const T &at(int idx) const { return operator[](idx); } + + T value(int i) const; + T value(int i, const T &defaultValue) const; inline void append(const T &t) { if (s == a) // i.e. s != 0 @@ -248,6 +252,21 @@ Q_OUTOFLINE_TEMPLATE void QVarLengthArray<T, Prealloc>::realloc(int asize, int a } } +template <class T, int Prealloc> +Q_OUTOFLINE_TEMPLATE T QVarLengthArray<T, Prealloc>::value(int i) const +{ + if (i < 0 || i >= size()) { + return T(); + } + return at(i); +} +template <class T, int Prealloc> +Q_OUTOFLINE_TEMPLATE T QVarLengthArray<T, Prealloc>::value(int i, const T &defaultValue) const +{ + return (i < 0 || i >= size()) ? defaultValue : at(i); +} + + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/corelib/tools/qvarlengtharray.qdoc b/src/corelib/tools/qvarlengtharray.qdoc index bb7a3de..38901e5 100644 --- a/src/corelib/tools/qvarlengtharray.qdoc +++ b/src/corelib/tools/qvarlengtharray.qdoc @@ -197,7 +197,7 @@ \a i must be a valid index position in the array (i.e., 0 <= \a i < size()). - \sa data() + \sa data(), at() */ /*! \fn const T &QVarLengthArray::operator[](int i) const @@ -272,3 +272,33 @@ Constructs a copy of \a other. */ +/*! \fn const T &QVarLengthArray::at(int i) const + + Returns a reference to the item at index position \a i. + + \a i must be a valid index position in the array (i.e., 0 <= \a i + < size()). + + \sa value(), operator[]() +*/ + +/*! \fn T QVarLengthArray::value(int i) const + + Returns the value at index position \a i. + + If the index \a i is out of bounds, the function returns + a \l{default-constructed value}. If you are certain that + \a i is within bounds, you can use at() instead, which is slightly + faster. + + \sa at(), operator[]() +*/ + +/*! \fn T QVarLengthArray::value(int i, const T &defaultValue) const + + \overload + + If the index \a i is out of bounds, the function returns + \a defaultValue. +*/ + diff --git a/src/gui/dialogs/qdialog.cpp b/src/gui/dialogs/qdialog.cpp index 3f8cc72..c9129c1 100644 --- a/src/gui/dialogs/qdialog.cpp +++ b/src/gui/dialogs/qdialog.cpp @@ -258,7 +258,7 @@ QT_BEGIN_NAMESPACE QDialog::QDialog(QWidget *parent, Qt::WindowFlags f) : QWidget(*new QDialogPrivate, parent, - f | QFlag((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : 0)) + f | ((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : Qt::WindowType(0))) { #ifdef Q_WS_WINCE if (!qt_wince_is_smartphone()) @@ -295,7 +295,7 @@ QDialog::QDialog(QWidget *parent, const char *name, bool modal, Qt::WindowFlags \internal */ QDialog::QDialog(QDialogPrivate &dd, QWidget *parent, Qt::WindowFlags f) - : QWidget(dd, parent, f | QFlag((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : 0)) + : QWidget(dd, parent, f | ((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : Qt::WindowType(0))) { #ifdef Q_WS_WINCE if (!qt_wince_is_smartphone()) diff --git a/src/gui/dialogs/qfiledialog.cpp b/src/gui/dialogs/qfiledialog.cpp index ef2b223..3b5fb9f 100644 --- a/src/gui/dialogs/qfiledialog.cpp +++ b/src/gui/dialogs/qfiledialog.cpp @@ -723,9 +723,15 @@ void QFileDialog::setVisible(bool visible) // Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below // updates the state correctly, but skips showing the non-native version: setAttribute(Qt::WA_DontShowOnScreen); + //So the completer don't try to complete and therefore to show a popup + d->completer->setModel(0); } else { d->nativeDialogInUse = false; setAttribute(Qt::WA_DontShowOnScreen, false); + if (d->proxyModel != 0) + d->completer->setModel(d->proxyModel); + else + d->completer->setModel(d->model); } } diff --git a/src/gui/egl/qegl.cpp b/src/gui/egl/qegl.cpp index b870523..498245c 100644 --- a/src/gui/egl/qegl.cpp +++ b/src/gui/egl/qegl.cpp @@ -528,6 +528,11 @@ QEglProperties QEglContext::configProperties() const return QEglProperties(config()); } +#if (defined(EGL_KHR_image) || defined(EGL_KHR_image_base)) && !defined(EGL_EGLEXT_PROTOTYPES) +_eglCreateImageKHR eglCreateImageKHR = 0; +_eglDestroyImageKHR eglDestroyImageKHR = 0; +#endif + EGLDisplay QEgl::display() { static EGLDisplay dpy = EGL_NO_DISPLAY; @@ -549,6 +554,14 @@ EGLDisplay QEgl::display() qWarning() << "QEgl::display(): Cannot initialize EGL display:" << QEgl::errorString(); return EGL_NO_DISPLAY; } + + // Resolve the egl extension function pointers: +#if !defined(EGL_KHR_image) && !defined(EGL_KHR_image_base) + if (QEgl::hasExtension("EGL_KHR_image") || QEgl::hasExtension("EGL_KHR_image_base")) { + eglCreateImageKHR = (_eglCreateImageKHR) eglGetProcAddress("eglCreateImageKHR"); + eglDestroyImageKHR = (_eglDestroyImageKHR) eglGetProcAddress("eglDestroyImageKHR"); + } +#endif } return dpy; diff --git a/src/gui/egl/qegl_p.h b/src/gui/egl/qegl_p.h index 7dad9fe..540cd3d 100644 --- a/src/gui/egl/qegl_p.h +++ b/src/gui/egl/qegl_p.h @@ -100,13 +100,63 @@ typedef NativeDisplayType EGLNativeDisplayType; QT_END_INCLUDE_NAMESPACE #include <QtGui/qpaintdevice.h> - #include <QFlags> QT_BEGIN_NAMESPACE #define QEGL_NO_CONFIG ((EGLConfig)-1) +#ifndef EGLAPIENTRY +#define EGLAPIENTRY +#endif + +// Try to get some info to debug the symbian build failues: +#ifdef Q_OS_SYMBIAN + +#ifdef EGL_KHR_image +#warning "EGL_KHR_image is defined" +#else +#warning "EGL_KHR_image is NOT defined" +#endif + +#ifdef EGL_KHR_image_base +#warning "EGL_KHR_image_base is defined" +#else +#warning "EGL_KHR_image_base is NOT defined" +#endif + +#ifdef EGL_EGLEXT_PROTOTYPES +#warning "EGL_EGLEXT_PROTOTYPES is defined" +#else +#warning "EGL_EGLEXT_PROTOTYPES NOT not defined" +#endif + +#endif + + +// Declare/define the bits of EGL_KHR_image_base we need: +#if !defined(EGL_KHR_image) && !defined(EGL_KHR_image_base) +typedef void *EGLImageKHR; +#define EGL_NO_IMAGE_KHR ((EGLImageKHR)0) +#define EGL_IMAGE_PRESERVED_KHR 0x30D2 +#endif + +// It is possible that something has included eglext.h (like Symbian 10.1's broken egl.h), in +// which case, EGL_KHR_image/EGL_KHR_image_base will be defined. They may have also defined +// the actual function prototypes, but generally EGL_EGLEXT_PROTOTYPES will be defined in that +// case and we shouldn't re-define them here. +#if (defined(EGL_KHR_image) || defined(EGL_KHR_image_base)) && !defined(EGL_EGLEXT_PROTOTYPES) +typedef EGLImageKHR (EGLAPIENTRY *_eglCreateImageKHR)(EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, EGLint*); +typedef EGLBoolean (EGLAPIENTRY *_eglDestroyImageKHR)(EGLDisplay, EGLImageKHR); + +// Defined in qegl.cpp: +extern Q_GUI_EXPORT _eglCreateImageKHR eglCreateImageKHR; +extern Q_GUI_EXPORT _eglDestroyImageKHR eglDestroyImageKHR; +#endif // (defined(EGL_KHR_image) || defined(EGL_KHR_image_base)) && !defined(EGL_EGLEXT_PROTOTYPES) + +#if !defined(EGL_KHR_image) && !defined(EGL_KHR_image_pixmap) +#define EGL_NATIVE_PIXMAP_KHR 0x30B0 +#endif class QEglProperties; @@ -132,7 +182,6 @@ namespace QEgl { }; Q_DECLARE_FLAGS(ConfigOptions, ConfigOption); - // Most of the time we use the same config for things like widgets & pixmaps, so rather than // go through the eglChooseConfig loop every time, we use defaultConfig, which will return // the config for a particular device/api/option combo. This function assumes that once a diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 20c9faa..f106b3d 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -381,7 +381,9 @@ \value ItemSendsGeometryChanges The item enables itemChange() notifications for ItemPositionChange, ItemPositionHasChanged, - ItemMatrixChange, ItemTransformChange, and ItemTransformHasChanged. For + ItemMatrixChange, ItemTransformChange, ItemTransformHasChanged, + ItemRotationChange, ItemRotationHasChanged, ItemScaleChange, ItemScaleHasChanged, + ItemTransformOriginPointChange, and ItemTransformOriginPointHasChanged. For performance reasons, these notifications are disabled by default. You must enable this flag to receive notifications for position and transform changes. This flag was introduced in Qt 4.6. @@ -475,6 +477,52 @@ (same as transform()), and QGraphicsItem ignores the return value for this notification (i.e., a read-only notification). + \value ItemRotationChange The item's rotation property changes. This + notification is sent if the ItemSendsGeometryChanges flag is enabled, and + when the item's rotation property changes (i.e., as a result of calling + setRotation()). The value argument is the new rotation (i.e., a double); + to get the old rotation, call rotation(). Do not call setRotation() in + itemChange() as this notification is delivered; instead, you can return + the new rotation from itemChange(). + + \value ItemRotationHasChanged The item's rotation property has changed. + This notification is sent if the ItemSendsGeometryChanges flag is enabled, + and after the item's rotation property has changed. The value argument is + the new rotation (i.e., a double), and QGraphicsItem ignores the return + value for this notification (i.e., a read-only notification). Do not call + setRotation() in itemChange() as this notification is delivered. + + \value ItemScaleChange The item's scale property changes. This notification + is sent if the ItemSendsGeometryChanges flag is enabled, and when the item's + scale property changes (i.e., as a result of calling setScale()). The value + argument is the new scale (i.e., a double); to get the old scale, call + scale(). Do not call setScale() in itemChange() as this notification is + delivered; instead, you can return the new scale from itemChange(). + + \value ItemScaleHasChanged The item's scale property has changed. This + notification is sent if the ItemSendsGeometryChanges flag is enabled, and + after the item's scale property has changed. The value argument is the new + scale (i.e., a double), and QGraphicsItem ignores the return value for this + notification (i.e., a read-only notification). Do not call setScale() in + itemChange() as this notification is delivered. + + \value ItemTransformOriginPointChange The item's transform origin point + property changes. This notification is sent if the ItemSendsGeometryChanges + flag is enabled, and when the item's transform origin point property changes + (i.e., as a result of calling setTransformOriginPoint()). The value argument + is the new origin point (i.e., a QPointF); to get the old origin point, call + transformOriginPoint(). Do not call setTransformOriginPoint() in itemChange() + as this notification is delivered; instead, you can return the new transform + origin point from itemChange(). + + \value ItemTransformOriginPointHasChanged The item's transform origin point + property has changed. This notification is sent if the ItemSendsGeometryChanges + flag is enabled, and after the item's transform origin point property has + changed. The value argument is the new origin point (i.e., a QPointF), and + QGraphicsItem ignores the return value for this notification (i.e., a read-only + notification). Do not call setTransformOriginPoint() in itemChange() as this + notification is delivered. + \value ItemSelectedChange The item's selected state changes. If the item is presently selected, it will become unselected, and vice verca. The value argument is the new selected state (i.e., true or false). Do not call @@ -683,6 +731,9 @@ #include <QtGui/qevent.h> #include <QtGui/qinputcontext.h> #include <QtGui/qgraphicseffect.h> +#ifndef QT_NO_ACCESSIBILITY +# include "qaccessible.h" +#endif #include <private/qgraphicsitem_p.h> #include <private/qgraphicswidget_p.h> @@ -690,6 +741,7 @@ #include <private/qtextdocumentlayout_p.h> #include <private/qtextengine_p.h> #include <private/qwidget_p.h> +#include <private/qapplication_p.h> #ifdef Q_WS_X11 #include <private/qt_x11_p.h> @@ -3583,7 +3635,7 @@ void QGraphicsItem::setPos(const QPointF &pos) return; // Update and repositition. - if (!(d_ptr->flags & ItemSendsGeometryChanges) && !(d_ptr->flags & ItemSendsScenePositionChanges)) { + if (!(d_ptr->flags & (ItemSendsGeometryChanges | ItemSendsScenePositionChanges))) { d_ptr->setPosHelper(pos); return; } @@ -3728,12 +3780,28 @@ qreal QGraphicsItem::rotation() const void QGraphicsItem::setRotation(qreal angle) { prepareGeometryChange(); + qreal newRotation = angle; + + if (d_ptr->flags & ItemSendsGeometryChanges) { + // Notify the item that the rotation is changing. + const QVariant newRotationVariant(itemChange(ItemRotationChange, angle)); + newRotation = newRotationVariant.toReal(); + } + if (!d_ptr->transformData) d_ptr->transformData = new QGraphicsItemPrivate::TransformData; - d_ptr->transformData->rotation = angle; + + if (d_ptr->transformData->rotation == newRotation) + return; + + d_ptr->transformData->rotation = newRotation; d_ptr->transformData->onlyTransform = false; d_ptr->dirtySceneTransform = 1; + // Send post-notification. + if (d_ptr->flags & ItemSendsGeometryChanges) + itemChange(ItemRotationHasChanged, newRotation); + if (d_ptr->isObject) emit static_cast<QGraphicsObject *>(this)->rotationChanged(); } @@ -3776,12 +3844,28 @@ qreal QGraphicsItem::scale() const void QGraphicsItem::setScale(qreal factor) { prepareGeometryChange(); + qreal newScale = factor; + + if (d_ptr->flags & ItemSendsGeometryChanges) { + // Notify the item that the scale is changing. + const QVariant newScaleVariant(itemChange(ItemScaleChange, factor)); + newScale = newScaleVariant.toReal(); + } + if (!d_ptr->transformData) d_ptr->transformData = new QGraphicsItemPrivate::TransformData; - d_ptr->transformData->scale = factor; + + if (d_ptr->transformData->scale == newScale) + return; + + d_ptr->transformData->scale = newScale; d_ptr->transformData->onlyTransform = false; d_ptr->dirtySceneTransform = 1; + // Send post-notification. + if (d_ptr->flags & ItemSendsGeometryChanges) + itemChange(ItemScaleHasChanged, newScale); + if (d_ptr->isObject) emit static_cast<QGraphicsObject *>(this)->scaleChanged(); } @@ -3899,12 +3983,31 @@ QPointF QGraphicsItem::transformOriginPoint() const void QGraphicsItem::setTransformOriginPoint(const QPointF &origin) { prepareGeometryChange(); + QPointF newOrigin = origin; + + if (d_ptr->flags & ItemSendsGeometryChanges) { + // Notify the item that the origin point is changing. + const QVariant newOriginVariant(itemChange(ItemTransformOriginPointChange, + qVariantFromValue<QPointF>(origin))); + newOrigin = newOriginVariant.toPointF(); + } + if (!d_ptr->transformData) d_ptr->transformData = new QGraphicsItemPrivate::TransformData; - d_ptr->transformData->xOrigin = origin.x(); - d_ptr->transformData->yOrigin = origin.y(); + + if (d_ptr->transformData->xOrigin == newOrigin.x() + && d_ptr->transformData->yOrigin == newOrigin.y()) { + return; + } + + d_ptr->transformData->xOrigin = newOrigin.x(); + d_ptr->transformData->yOrigin = newOrigin.y(); d_ptr->transformData->onlyTransform = false; d_ptr->dirtySceneTransform = 1; + + // Send post-notification. + if (d_ptr->flags & ItemSendsGeometryChanges) + itemChange(ItemTransformOriginPointHasChanged, qVariantFromValue<QPointF>(newOrigin)); } /*! @@ -7206,6 +7309,31 @@ void QGraphicsItem::setInputMethodHints(Qt::InputMethodHints hints) } /*! + Updates the item's micro focus. + + \since 4.7 + + \sa QInputContext +*/ +void QGraphicsItem::updateMicroFocus() +{ +#if !defined(QT_NO_IM) && (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) + if (QWidget *fw = qApp->focusWidget()) { + if (qt_widget_private(fw)->ic || qApp->d_func()->inputContext) { + if (QInputContext *ic = fw->inputContext()) { + if (ic) + ic->update(); + } + } +#ifndef QT_NO_ACCESSIBILITY + // ##### is this correct + QAccessible::updateAccessibility(fw, 0, QAccessible::StateChanged); +#endif + } +#endif +} + +/*! This virtual function is called by QGraphicsItem to notify custom items that some part of the item's state changes. By reimplementing this function, your can react to a change, and in some cases, (depending on \a @@ -7481,6 +7609,11 @@ void QGraphicsObject::ungrabGesture(Qt::GestureType gesture) } } +void QGraphicsObject::updateMicroFocus() +{ + QGraphicsItem::updateMicroFocus(); +} + void QGraphicsItemPrivate::append(QDeclarativeListProperty<QGraphicsObject> *list, QGraphicsObject *item) { QGraphicsItemPrivate::get(item)->setParentItemHelper(static_cast<QGraphicsObject *>(list->object), /*newParentVariant=*/0, /*thisPointerVariant=*/0); @@ -11197,6 +11330,24 @@ QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemChange change) case QGraphicsItem::ItemScenePositionHasChanged: str = "ItemScenePositionHasChanged"; break; + case QGraphicsItem::ItemRotationChange: + str = "ItemRotationChange"; + break; + case QGraphicsItem::ItemRotationHasChanged: + str = "ItemRotationHasChanged"; + break; + case QGraphicsItem::ItemScaleChange: + str = "ItemScaleChange"; + break; + case QGraphicsItem::ItemScaleHasChanged: + str = "ItemScaleHasChanged"; + break; + case QGraphicsItem::ItemTransformOriginPointChange: + str = "ItemTransformOriginPointChange"; + break; + case QGraphicsItem::ItemTransformOriginPointHasChanged: + str = "ItemTransformOriginPointHasChanged"; + break; } debug << str; return debug; diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index 22be64c..9cc75af 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -139,7 +139,13 @@ public: ItemZValueHasChanged, ItemOpacityChange, ItemOpacityHasChanged, - ItemScenePositionHasChanged + ItemScenePositionHasChanged, + ItemRotationChange, + ItemRotationHasChanged, + ItemScaleChange, + ItemScaleHasChanged, + ItemTransformOriginPointChange, + ItemTransformOriginPointHasChanged }; enum CacheMode { @@ -418,6 +424,7 @@ public: void removeSceneEventFilter(QGraphicsItem *filterItem); protected: + void updateMicroFocus(); virtual bool sceneEventFilter(QGraphicsItem *watched, QEvent *event); virtual bool sceneEvent(QEvent *event); virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); @@ -565,6 +572,9 @@ public: void grabGesture(Qt::GestureType type, Qt::GestureFlags flags = Qt::GestureFlags()); void ungrabGesture(Qt::GestureType type); +protected Q_SLOTS: + void updateMicroFocus(); + Q_SIGNALS: void parentChanged(); void opacityChanged(); diff --git a/src/gui/gui.pro b/src/gui/gui.pro index cbdbb5c..b22f732 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -41,6 +41,7 @@ include(math3d/math3d.pri) include(effects/effects.pri) contains(QT_CONFIG, egl): include(egl/egl.pri) +win32:!wince*: DEFINES += QT_NO_EGL embedded: QT += network diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 94307de..233c58d 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -2988,19 +2988,19 @@ static void convert_Indexed8_to_X32(QImageData *dest, const QImageData *src, Qt: colorTable.resize(256); for (int i=0; i<256; ++i) colorTable[i] = qRgb(i, i, i); - } int w = src->width; const uchar *src_data = src->data; uchar *dest_data = dest->data; + int tableSize = colorTable.size() - 1; for (int y = 0; y < src->height; y++) { uint *p = (uint *)dest_data; const uchar *b = src_data; uint *end = p + w; while (p < end) - *p++ = colorTable.at(*b++); + *p++ = colorTable.at(qMin<int>(tableSize, *b++)); src_data += src->bytes_per_line; dest_data += dest->bytes_per_line; diff --git a/src/gui/image/qpicture.cpp b/src/gui/image/qpicture.cpp index 3220a67..45b3ed0 100644 --- a/src/gui/image/qpicture.cpp +++ b/src/gui/image/qpicture.cpp @@ -651,7 +651,12 @@ bool QPicture::exec(QPainter *painter, QDataStream &s, int nrecords) s >> dbl; QFont fnt(font, painter->device()); - qreal scale = painter->device()->logicalDpiY() / (dbl*qt_defaultDpiY()); + // Fonts that specify a pixel size should not be scaled - QPicture already + // have a matrix set to compensate for the DPI differences between the + // default Qt DPI and the actual target device DPI, and we have to take that + // into consideration in the case where the font has a pixel size set. + + qreal scale = fnt.pointSize() == -1 ? 1 : painter->device()->logicalDpiY() / (dbl*qt_defaultDpiY()); painter->save(); painter->scale(1/scale, 1/scale); @@ -660,7 +665,7 @@ bool QPicture::exec(QPainter *painter, QDataStream &s, int nrecords) int flags = Qt::TextSingleLine | Qt::TextDontClip | Qt::TextForceLeftToRight; - QSizeF size(scale, scale); + QSizeF size(1, 1); if (justificationWidth > 0) { size.setWidth(justificationWidth*scale); flags |= Qt::TextJustificationForced; diff --git a/src/gui/image/qpixmap_raster.cpp b/src/gui/image/qpixmap_raster.cpp index 0b1c18d..b183d0d 100644 --- a/src/gui/image/qpixmap_raster.cpp +++ b/src/gui/image/qpixmap_raster.cpp @@ -182,6 +182,7 @@ void QRasterPixmapData::fromImage(const QImage &sourceImage, QImage::Format opaqueFormat = QNativeImage::systemFormat(); QImage::Format alphaFormat = QImage::Format_ARGB32_Premultiplied; +#ifndef QT_HAVE_NEON switch (opaqueFormat) { case QImage::Format_RGB16: alphaFormat = QImage::Format_ARGB8565_Premultiplied; @@ -189,6 +190,7 @@ void QRasterPixmapData::fromImage(const QImage &sourceImage, default: // We don't care about the others... break; } +#endif if (!sourceImage.hasAlphaChannel() || ((flags & Qt::NoOpaqueDetection) == 0 @@ -238,6 +240,7 @@ void QRasterPixmapData::fill(const QColor &color) if (alpha != 255) { if (!image.hasAlphaChannel()) { QImage::Format toFormat; +#ifndef QT_HAVE_NEON if (image.format() == QImage::Format_RGB16) toFormat = QImage::Format_ARGB8565_Premultiplied; else if (image.format() == QImage::Format_RGB666) @@ -247,6 +250,7 @@ void QRasterPixmapData::fill(const QColor &color) else if (image.format() == QImage::Format_RGB444) toFormat = QImage::Format_ARGB4444_Premultiplied; else +#endif toFormat = QImage::Format_ARGB32_Premultiplied; image = QImage(image.width(), image.height(), toFormat); } diff --git a/src/gui/itemviews/qitemdelegate.cpp b/src/gui/itemviews/qitemdelegate.cpp index cba213b..d5f6fd2 100644 --- a/src/gui/itemviews/qitemdelegate.cpp +++ b/src/gui/itemviews/qitemdelegate.cpp @@ -69,6 +69,7 @@ #include <qdebug.h> #include <qlocale.h> #include <qdialog.h> +#include <qmath.h> #include <limits.h> @@ -1148,7 +1149,8 @@ QRect QItemDelegate::textRectangle(QPainter * /*painter*/, const QRect &rect, d->textLayout.setTextOption(d->textOption); d->textLayout.setFont(font); d->textLayout.setText(QItemDelegatePrivate::replaceNewLine(text)); - const QSize size = d->doTextLayout(rect.width()).toSize(); + QSizeF fpSize = d->doTextLayout(rect.width()); + const QSize size = QSize(qCeil(fpSize.width()), qCeil(fpSize.height())); // ###: textRectangle should take style option as argument const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; return QRect(0, 0, size.width() + 2 * textMargin, size.height()); diff --git a/src/gui/kernel/qapplication.h b/src/gui/kernel/qapplication.h index ee74350..c21b982 100644 --- a/src/gui/kernel/qapplication.h +++ b/src/gui/kernel/qapplication.h @@ -376,6 +376,7 @@ private: Q_DECLARE_PRIVATE(QApplication) friend class QGraphicsWidget; + friend class QGraphicsItem; friend class QGraphicsScene; friend class QGraphicsScenePrivate; friend class QWidget; diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri index a6cc9c7..ed8ee76 100644 --- a/src/gui/painting/painting.pri +++ b/src/gui/painting/painting.pri @@ -91,6 +91,8 @@ SOURCES += \ HEADERS += \ painting/qpaintengine_raster_p.h \ + painting/qdrawhelper_p.h \ + painting/qblendfunctions_p.h \ painting/qrasterdefs_p.h \ painting/qgrayraster_p.h @@ -379,11 +381,23 @@ symbian { QMAKE_CXXFLAGS.ARMCC *= -O3 } -neon { +neon:*-g++* { DEFINES += QT_HAVE_NEON HEADERS += painting/qdrawhelper_neon_p.h SOURCES += painting/qdrawhelper_neon.cpp QMAKE_CXXFLAGS *= -mfpu=neon + + DRAWHELPER_NEON_ASM_FILES = ../3rdparty/pixman/pixman-arm-neon-asm.S painting/qdrawhelper_neon_asm.S + + neon_compiler.commands = $$QMAKE_CXX -c + neon_compiler.commands += $(CXXFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} + neon_compiler.dependency_type = TYPE_C + neon_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)} + neon_compiler.input = DRAWHELPER_NEON_ASM_FILES + neon_compiler.variable_out = OBJECTS + neon_compiler.name = compiling[neon] ${QMAKE_FILE_IN} + silent:neon_compiler.commands = @echo compiling[neon] ${QMAKE_FILE_IN} && $$neon_compiler.commands + QMAKE_EXTRA_COMPILERS += neon_compiler } contains(QT_CONFIG, zlib) { diff --git a/src/gui/painting/qblendfunctions.cpp b/src/gui/painting/qblendfunctions.cpp index dc33896..24908ce 100644 --- a/src/gui/painting/qblendfunctions.cpp +++ b/src/gui/painting/qblendfunctions.cpp @@ -40,7 +40,7 @@ ****************************************************************************/ #include <qmath.h> -#include "qdrawhelper_p.h" +#include "qblendfunctions_p.h" QT_BEGIN_NAMESPACE @@ -88,6 +88,8 @@ static inline quint16 convert_argb32_to_rgb16(quint32 spix) struct Blend_RGB16_on_RGB16_NoAlpha { inline void write(quint16 *dst, quint16 src) { *dst = src; } + + inline void flush(void *) {} }; struct Blend_RGB16_on_RGB16_ConstAlpha { @@ -100,6 +102,8 @@ struct Blend_RGB16_on_RGB16_ConstAlpha { *dst = BYTE_MUL_RGB16(src, m_alpha) + BYTE_MUL_RGB16(*dst, m_ialpha); } + inline void flush(void *) {} + quint32 m_alpha; quint32 m_ialpha; }; @@ -114,6 +118,8 @@ struct Blend_ARGB24_on_RGB16_SourceAlpha { *dst = s; } } + + inline void flush(void *) {} }; struct Blend_ARGB24_on_RGB16_SourceAndConstAlpha { @@ -132,6 +138,8 @@ struct Blend_ARGB24_on_RGB16_SourceAndConstAlpha { } } + inline void flush(void *) {} + quint32 m_alpha; }; @@ -145,6 +153,8 @@ struct Blend_ARGB32_on_RGB16_SourceAlpha { *dst = s; } } + + inline void flush(void *) {} }; struct Blend_ARGB32_on_RGB16_SourceAndConstAlpha { @@ -163,99 +173,11 @@ struct Blend_ARGB32_on_RGB16_SourceAndConstAlpha { } } + inline void flush(void *) {} + quint32 m_alpha; }; -template <typename SRC, typename T> -void qt_scale_image_16bit(uchar *destPixels, int dbpl, - const uchar *srcPixels, int sbpl, - const QRectF &targetRect, - const QRectF &srcRect, - const QRect &clip, - T blender) -{ - qreal sx = targetRect.width() / (qreal) srcRect.width(); - qreal sy = targetRect.height() / (qreal) srcRect.height(); - - int ix = 0x00010000 / sx; - int iy = 0x00010000 / sy; - -// qDebug() << "scale:" << endl -// << " - target" << targetRect << endl -// << " - source" << srcRect << endl -// << " - clip" << clip << endl -// << " - sx=" << sx << " sy=" << sy << " ix=" << ix << " iy=" << iy; - - int cx1 = clip.x(); - int cx2 = clip.x() + clip.width(); - int cy1 = clip.top(); - int cy2 = clip.y() + clip.height(); - - int tx1 = qRound(targetRect.left()); - int tx2 = qRound(targetRect.right()); - int ty1 = qRound(targetRect.top()); - int ty2 = qRound(targetRect.bottom()); - - if (tx2 < tx1) - qSwap(tx2, tx1); - - if (ty2 < ty1) - qSwap(ty2, ty1); - - if (tx1 < cx1) - tx1 = cx1; - - if (tx2 >= cx2) - tx2 = cx2; - - if (tx1 >= tx2) - return; - - if (ty1 < cy1) - ty1 = cy1; - - if (ty2 >= cy2) - ty2 = cy2; - - if (ty1 >= ty2) - return; - - int h = ty2 - ty1; - int w = tx2 - tx1; - - - quint32 basex; - quint32 srcy; - - if (sx < 0) { - int dstx = qFloor((tx1 + qreal(0.5) - targetRect.right()) * ix) + 1; - basex = quint32(srcRect.right() * 65536) + dstx; - } else { - int dstx = qCeil((tx1 + qreal(0.5) - targetRect.left()) * ix) - 1; - basex = quint32(srcRect.left() * 65536) + dstx; - } - if (sy < 0) { - int dsty = qFloor((ty1 + qreal(0.5) - targetRect.bottom()) * iy) + 1; - srcy = quint32(srcRect.bottom() * 65536) + dsty; - } else { - int dsty = qCeil((ty1 + qreal(0.5) - targetRect.top()) * iy) - 1; - srcy = quint32(srcRect.top() * 65536) + dsty; - } - - quint16 *dst = ((quint16 *) (destPixels + ty1 * dbpl)) + tx1; - - while (h--) { - const SRC *src = (const SRC *) (srcPixels + (srcy >> 16) * sbpl); - int srcx = basex; - for (int x=0; x<w; ++x) { - blender.write(&dst[x], src[srcx >> 16]); - srcx += ix; - } - dst = (quint16 *)(((uchar *) dst) + dbpl); - srcy += iy; - } -} - void qt_scale_image_rgb16_on_rgb16(uchar *destPixels, int dbpl, const uchar *srcPixels, int sbpl, const QRectF &targetRect, @@ -447,10 +369,10 @@ static void qt_blend_argb24_on_rgb16(uchar *destPixels, int dbpl, -static void qt_blend_argb32_on_rgb16_const_alpha(uchar *destPixels, int dbpl, - const uchar *srcPixels, int sbpl, - int w, int h, - int const_alpha) +void qt_blend_argb32_on_rgb16_const_alpha(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha) { quint16 *dst = (quint16 *) destPixels; const quint32 *src = (const quint32 *) srcPixels; @@ -643,6 +565,8 @@ void qt_blend_rgb32_on_rgb32(uchar *destPixels, int dbpl, struct Blend_RGB32_on_RGB32_NoAlpha { inline void write(quint32 *dst, quint32 src) { *dst = src; } + + inline void flush(void *) {} }; struct Blend_RGB32_on_RGB32_ConstAlpha { @@ -655,6 +579,8 @@ struct Blend_RGB32_on_RGB32_ConstAlpha { *dst = BYTE_MUL(src, m_alpha) + BYTE_MUL(*dst, m_ialpha); } + inline void flush(void *) {} + quint32 m_alpha; quint32 m_ialpha; }; @@ -663,6 +589,8 @@ struct Blend_ARGB32_on_ARGB32_SourceAlpha { inline void write(quint32 *dst, quint32 src) { *dst = src + BYTE_MUL(*dst, qAlpha(~src)); } + + inline void flush(void *) {} }; struct Blend_ARGB32_on_ARGB32_SourceAndConstAlpha { @@ -676,98 +604,12 @@ struct Blend_ARGB32_on_ARGB32_SourceAndConstAlpha { *dst = src + BYTE_MUL(*dst, qAlpha(~src)); } + inline void flush(void *) {} + quint32 m_alpha; quint32 m_ialpha; }; -template <typename T> void qt_scale_image_32bit(uchar *destPixels, int dbpl, - const uchar *srcPixels, int sbpl, - const QRectF &targetRect, - const QRectF &srcRect, - const QRect &clip, - T blender) -{ - qreal sx = targetRect.width() / (qreal) srcRect.width(); - qreal sy = targetRect.height() / (qreal) srcRect.height(); - - int ix = 0x00010000 / sx; - int iy = 0x00010000 / sy; - -// qDebug() << "scale:" << endl -// << " - target" << targetRect << endl -// << " - source" << srcRect << endl -// << " - clip" << clip << endl -// << " - sx=" << sx << " sy=" << sy << " ix=" << ix << " iy=" << iy; - - int cx1 = clip.x(); - int cx2 = clip.x() + clip.width(); - int cy1 = clip.top(); - int cy2 = clip.y() + clip.height(); - - int tx1 = qRound(targetRect.left()); - int tx2 = qRound(targetRect.right()); - int ty1 = qRound(targetRect.top()); - int ty2 = qRound(targetRect.bottom()); - - if (tx2 < tx1) - qSwap(tx2, tx1); - - if (ty2 < ty1) - qSwap(ty2, ty1); - - if (tx1 < cx1) - tx1 = cx1; - - if (tx2 >= cx2) - tx2 = cx2; - - if (tx1 >= tx2) - return; - - if (ty1 < cy1) - ty1 = cy1; - - if (ty2 >= cy2) - ty2 = cy2; - - if (ty1 >= ty2) - return; - - int h = ty2 - ty1; - int w = tx2 - tx1; - - quint32 basex; - quint32 srcy; - - if (sx < 0) { - int dstx = qFloor((tx1 + qreal(0.5) - targetRect.right()) * ix) + 1; - basex = quint32(srcRect.right() * 65536) + dstx; - } else { - int dstx = qCeil((tx1 + qreal(0.5) - targetRect.left()) * ix) - 1; - basex = quint32(srcRect.left() * 65536) + dstx; - } - if (sy < 0) { - int dsty = qFloor((ty1 + qreal(0.5) - targetRect.bottom()) * iy) + 1; - srcy = quint32(srcRect.bottom() * 65536) + dsty; - } else { - int dsty = qCeil((ty1 + qreal(0.5) - targetRect.top()) * iy) - 1; - srcy = quint32(srcRect.top() * 65536) + dsty; - } - - quint32 *dst = ((quint32 *) (destPixels + ty1 * dbpl)) + tx1; - - while (h--) { - const uint *src = (const quint32 *) (srcPixels + (srcy >> 16) * sbpl); - int srcx = basex; - for (int x=0; x<w; ++x) { - blender.write(&dst[x], src[srcx >> 16]); - srcx += ix; - } - dst = (quint32 *)(((uchar *) dst) + dbpl); - srcy += iy; - } -} - void qt_scale_image_rgb32_on_rgb32(uchar *destPixels, int dbpl, const uchar *srcPixels, int sbpl, const QRectF &targetRect, @@ -818,244 +660,6 @@ void qt_scale_image_argb32_on_argb32(uchar *destPixels, int dbpl, } } -struct QTransformImageVertex -{ - qreal x, y, u, v; // destination coordinates (x, y) and source coordinates (u, v) -}; - -template <class SrcT, class DestT, class Blender> -void qt_transform_image_rasterize(DestT *destPixels, int dbpl, - const SrcT *srcPixels, int sbpl, - const QTransformImageVertex &topLeft, const QTransformImageVertex &bottomLeft, - const QTransformImageVertex &topRight, const QTransformImageVertex &bottomRight, - const QRect &sourceRect, - const QRect &clip, - qreal topY, qreal bottomY, - int dudx, int dvdx, int dudy, int dvdy, int u0, int v0, - Blender blender) -{ - int fromY = qMax(qRound(topY), clip.top()); - int toY = qMin(qRound(bottomY), clip.top() + clip.height()); - if (fromY >= toY) - return; - - qreal leftSlope = (bottomLeft.x - topLeft.x) / (bottomLeft.y - topLeft.y); - qreal rightSlope = (bottomRight.x - topRight.x) / (bottomRight.y - topRight.y); - int dx_l = int(leftSlope * 0x10000); - int dx_r = int(rightSlope * 0x10000); - int x_l = int((topLeft.x + (0.5 + fromY - topLeft.y) * leftSlope + 0.5) * 0x10000); - int x_r = int((topRight.x + (0.5 + fromY - topRight.y) * rightSlope + 0.5) * 0x10000); - - int fromX, toX, x1, x2, u, v, i, ii; - DestT *line; - for (int y = fromY; y < toY; ++y) { - line = reinterpret_cast<DestT *>(reinterpret_cast<uchar *>(destPixels) + y * dbpl); - - fromX = qMax(x_l >> 16, clip.left()); - toX = qMin(x_r >> 16, clip.left() + clip.width()); - if (fromX < toX) { - // Because of rounding, we can get source coordinates outside the source image. - // Clamp these coordinates to the source rect to avoid segmentation fault and - // garbage on the screen. - - // Find the first pixel on the current scan line where the source coordinates are within the source rect. - x1 = fromX; - u = x1 * dudx + y * dudy + u0; - v = x1 * dvdx + y * dvdy + v0; - for (; x1 < toX; ++x1) { - int uu = u >> 16; - int vv = v >> 16; - if (uu >= sourceRect.left() && uu < sourceRect.left() + sourceRect.width() - && vv >= sourceRect.top() && vv < sourceRect.top() + sourceRect.height()) { - break; - } - u += dudx; - v += dvdx; - } - - // Find the last pixel on the current scan line where the source coordinates are within the source rect. - x2 = toX; - u = (x2 - 1) * dudx + y * dudy + u0; - v = (x2 - 1) * dvdx + y * dvdy + v0; - for (; x2 > x1; --x2) { - int uu = u >> 16; - int vv = v >> 16; - if (uu >= sourceRect.left() && uu < sourceRect.left() + sourceRect.width() - && vv >= sourceRect.top() && vv < sourceRect.top() + sourceRect.height()) { - break; - } - u -= dudx; - v -= dvdx; - } - - // Set up values at the beginning of the scan line. - u = fromX * dudx + y * dudy + u0; - v = fromX * dvdx + y * dvdy + v0; - line += fromX; - - // Beginning of the scan line, with per-pixel checks. - i = x1 - fromX; - while (i) { - int uu = qBound(sourceRect.left(), u >> 16, sourceRect.left() + sourceRect.width() - 1); - int vv = qBound(sourceRect.top(), v >> 16, sourceRect.top() + sourceRect.height() - 1); - blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + vv * sbpl)[uu]); - u += dudx; - v += dvdx; - ++line; - --i; - } - - // Middle of the scan line, without checks. - // Manual loop unrolling. - i = x2 - x1; - ii = i >> 3; - while (ii) { - blender.write(&line[0], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; - blender.write(&line[1], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; - blender.write(&line[2], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; - blender.write(&line[3], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; - blender.write(&line[4], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; - blender.write(&line[5], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; - blender.write(&line[6], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; - blender.write(&line[7], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; - line += 8; - --ii; - } - switch (i & 7) { - case 7: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line; - case 6: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line; - case 5: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line; - case 4: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line; - case 3: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line; - case 2: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line; - case 1: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line; - } - - // End of the scan line, with per-pixel checks. - i = toX - x2; - while (i) { - int uu = qBound(sourceRect.left(), u >> 16, sourceRect.left() + sourceRect.width() - 1); - int vv = qBound(sourceRect.top(), v >> 16, sourceRect.top() + sourceRect.height() - 1); - blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + vv * sbpl)[uu]); - u += dudx; - v += dvdx; - ++line; - --i; - } - } - x_l += dx_l; - x_r += dx_r; - } -} - -template <class SrcT, class DestT, class Blender> -void qt_transform_image(DestT *destPixels, int dbpl, - const SrcT *srcPixels, int sbpl, - const QRectF &targetRect, - const QRectF &sourceRect, - const QRect &clip, - const QTransform &targetRectTransform, - Blender blender) -{ - enum Corner - { - TopLeft, - TopRight, - BottomRight, - BottomLeft - }; - - // map source rectangle to destination. - QTransformImageVertex v[4]; - v[TopLeft].u = v[BottomLeft].u = sourceRect.left(); - v[TopLeft].v = v[TopRight].v = sourceRect.top(); - v[TopRight].u = v[BottomRight].u = sourceRect.right(); - v[BottomLeft].v = v[BottomRight].v = sourceRect.bottom(); - targetRectTransform.map(targetRect.left(), targetRect.top(), &v[TopLeft].x, &v[TopLeft].y); - targetRectTransform.map(targetRect.right(), targetRect.top(), &v[TopRight].x, &v[TopRight].y); - targetRectTransform.map(targetRect.left(), targetRect.bottom(), &v[BottomLeft].x, &v[BottomLeft].y); - targetRectTransform.map(targetRect.right(), targetRect.bottom(), &v[BottomRight].x, &v[BottomRight].y); - - // find topmost vertex. - int topmost = 0; - for (int i = 1; i < 4; ++i) { - if (v[i].y < v[topmost].y) - topmost = i; - } - // rearrange array such that topmost vertex is at index 0. - switch (topmost) { - case 1: - { - QTransformImageVertex t = v[0]; - for (int i = 0; i < 3; ++i) - v[i] = v[i+1]; - v[3] = t; - } - break; - case 2: - qSwap(v[0], v[2]); - qSwap(v[1], v[3]); - break; - case 3: - { - QTransformImageVertex t = v[3]; - for (int i = 3; i > 0; --i) - v[i] = v[i-1]; - v[0] = t; - } - break; - } - - // if necessary, swap vertex 1 and 3 such that 1 is to the left of 3. - qreal dx1 = v[1].x - v[0].x; - qreal dy1 = v[1].y - v[0].y; - qreal dx2 = v[3].x - v[0].x; - qreal dy2 = v[3].y - v[0].y; - if (dx1 * dy2 - dx2 * dy1 > 0) - qSwap(v[1], v[3]); - - QTransformImageVertex u = {v[1].x - v[0].x, v[1].y - v[0].y, v[1].u - v[0].u, v[1].v - v[0].v}; - QTransformImageVertex w = {v[2].x - v[0].x, v[2].y - v[0].y, v[2].u - v[0].u, v[2].v - v[0].v}; - - qreal det = u.x * w.y - u.y * w.x; - if (det == 0) - return; - - qreal invDet = 1.0 / det; - qreal m11, m12, m21, m22, mdx, mdy; - - m11 = (u.u * w.y - u.y * w.u) * invDet; - m12 = (u.x * w.u - u.u * w.x) * invDet; - m21 = (u.v * w.y - u.y * w.v) * invDet; - m22 = (u.x * w.v - u.v * w.x) * invDet; - mdx = v[0].u - m11 * v[0].x - m12 * v[0].y; - mdy = v[0].v - m21 * v[0].x - m22 * v[0].y; - - int dudx = int(m11 * 0x10000); - int dvdx = int(m21 * 0x10000); - int dudy = int(m12 * 0x10000); - int dvdy = int(m22 * 0x10000); - int u0 = qCeil((0.5 * m11 + 0.5 * m12 + mdx) * 0x10000) - 1; - int v0 = qCeil((0.5 * m21 + 0.5 * m22 + mdy) * 0x10000) - 1; - - int x1 = qFloor(sourceRect.left()); - int y1 = qFloor(sourceRect.top()); - int x2 = qCeil(sourceRect.right()); - int y2 = qCeil(sourceRect.bottom()); - QRect sourceRectI(x1, y1, x2 - x1, y2 - y1); - - // rasterize trapezoids. - if (v[1].y < v[3].y) { - qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[0], v[1], v[0], v[3], sourceRectI, clip, v[0].y, v[1].y, dudx, dvdx, dudy, dvdy, u0, v0, blender); - qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[1], v[2], v[0], v[3], sourceRectI, clip, v[1].y, v[3].y, dudx, dvdx, dudy, dvdy, u0, v0, blender); - qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[1], v[2], v[3], v[2], sourceRectI, clip, v[3].y, v[2].y, dudx, dvdx, dudy, dvdy, u0, v0, blender); - } else { - qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[0], v[1], v[0], v[3], sourceRectI, clip, v[0].y, v[3].y, dudx, dvdx, dudy, dvdy, u0, v0, blender); - qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[0], v[1], v[3], v[2], sourceRectI, clip, v[3].y, v[1].y, dudx, dvdx, dudy, dvdy, u0, v0, blender); - qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[1], v[2], v[3], v[2], sourceRectI, clip, v[1].y, v[2].y, dudx, dvdx, dudy, dvdy, u0, v0, blender); - } -} - void qt_transform_image_rgb16_on_rgb16(uchar *destPixels, int dbpl, const uchar *srcPixels, int sbpl, const QRectF &targetRect, diff --git a/src/gui/painting/qblendfunctions_p.h b/src/gui/painting/qblendfunctions_p.h new file mode 100644 index 0000000..ad754b0 --- /dev/null +++ b/src/gui/painting/qblendfunctions_p.h @@ -0,0 +1,497 @@ +/**************************************************************************** +** +** Copyright (C) 2010 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 QBLENDFUNCTIONS_P_H +#define QBLENDFUNCTIONS_P_H + +#include <qmath.h> +#include "qdrawhelper_p.h" + +QT_BEGIN_NAMESPACE + +// +// 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. +// + +template <typename SRC, typename T> +void qt_scale_image_16bit(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + const QRectF &targetRect, + const QRectF &srcRect, + const QRect &clip, + T blender) +{ + qreal sx = targetRect.width() / (qreal) srcRect.width(); + qreal sy = targetRect.height() / (qreal) srcRect.height(); + + int ix = 0x00010000 / sx; + int iy = 0x00010000 / sy; + +// qDebug() << "scale:" << endl +// << " - target" << targetRect << endl +// << " - source" << srcRect << endl +// << " - clip" << clip << endl +// << " - sx=" << sx << " sy=" << sy << " ix=" << ix << " iy=" << iy; + + int cx1 = clip.x(); + int cx2 = clip.x() + clip.width(); + int cy1 = clip.top(); + int cy2 = clip.y() + clip.height(); + + int tx1 = qRound(targetRect.left()); + int tx2 = qRound(targetRect.right()); + int ty1 = qRound(targetRect.top()); + int ty2 = qRound(targetRect.bottom()); + + if (tx2 < tx1) + qSwap(tx2, tx1); + + if (ty2 < ty1) + qSwap(ty2, ty1); + + if (tx1 < cx1) + tx1 = cx1; + + if (tx2 >= cx2) + tx2 = cx2; + + if (tx1 >= tx2) + return; + + if (ty1 < cy1) + ty1 = cy1; + + if (ty2 >= cy2) + ty2 = cy2; + + if (ty1 >= ty2) + return; + + int h = ty2 - ty1; + int w = tx2 - tx1; + + + quint32 basex; + quint32 srcy; + + if (sx < 0) { + int dstx = qFloor((tx1 + qreal(0.5) - targetRect.right()) * ix) + 1; + basex = quint32(srcRect.right() * 65536) + dstx; + } else { + int dstx = qCeil((tx1 + qreal(0.5) - targetRect.left()) * ix) - 1; + basex = quint32(srcRect.left() * 65536) + dstx; + } + if (sy < 0) { + int dsty = qFloor((ty1 + qreal(0.5) - targetRect.bottom()) * iy) + 1; + srcy = quint32(srcRect.bottom() * 65536) + dsty; + } else { + int dsty = qCeil((ty1 + qreal(0.5) - targetRect.top()) * iy) - 1; + srcy = quint32(srcRect.top() * 65536) + dsty; + } + + quint16 *dst = ((quint16 *) (destPixels + ty1 * dbpl)) + tx1; + + while (h--) { + const SRC *src = (const SRC *) (srcPixels + (srcy >> 16) * sbpl); + int srcx = basex; + int x = 0; + for (; x<w-7; x+=8) { + blender.write(&dst[x], src[srcx >> 16]); srcx += ix; + blender.write(&dst[x+1], src[srcx >> 16]); srcx += ix; + blender.write(&dst[x+2], src[srcx >> 16]); srcx += ix; + blender.write(&dst[x+3], src[srcx >> 16]); srcx += ix; + blender.write(&dst[x+4], src[srcx >> 16]); srcx += ix; + blender.write(&dst[x+5], src[srcx >> 16]); srcx += ix; + blender.write(&dst[x+6], src[srcx >> 16]); srcx += ix; + blender.write(&dst[x+7], src[srcx >> 16]); srcx += ix; + } + for (; x<w; ++x) { + blender.write(&dst[x], src[srcx >> 16]); + srcx += ix; + } + blender.flush(&dst[x]); + dst = (quint16 *)(((uchar *) dst) + dbpl); + srcy += iy; + } +} + +template <typename T> void qt_scale_image_32bit(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + const QRectF &targetRect, + const QRectF &srcRect, + const QRect &clip, + T blender) +{ + qreal sx = targetRect.width() / (qreal) srcRect.width(); + qreal sy = targetRect.height() / (qreal) srcRect.height(); + + int ix = 0x00010000 / sx; + int iy = 0x00010000 / sy; + +// qDebug() << "scale:" << endl +// << " - target" << targetRect << endl +// << " - source" << srcRect << endl +// << " - clip" << clip << endl +// << " - sx=" << sx << " sy=" << sy << " ix=" << ix << " iy=" << iy; + + int cx1 = clip.x(); + int cx2 = clip.x() + clip.width(); + int cy1 = clip.top(); + int cy2 = clip.y() + clip.height(); + + int tx1 = qRound(targetRect.left()); + int tx2 = qRound(targetRect.right()); + int ty1 = qRound(targetRect.top()); + int ty2 = qRound(targetRect.bottom()); + + if (tx2 < tx1) + qSwap(tx2, tx1); + + if (ty2 < ty1) + qSwap(ty2, ty1); + + if (tx1 < cx1) + tx1 = cx1; + + if (tx2 >= cx2) + tx2 = cx2; + + if (tx1 >= tx2) + return; + + if (ty1 < cy1) + ty1 = cy1; + + if (ty2 >= cy2) + ty2 = cy2; + + if (ty1 >= ty2) + return; + + int h = ty2 - ty1; + int w = tx2 - tx1; + + quint32 basex; + quint32 srcy; + + if (sx < 0) { + int dstx = qFloor((tx1 + qreal(0.5) - targetRect.right()) * ix) + 1; + basex = quint32(srcRect.right() * 65536) + dstx; + } else { + int dstx = qCeil((tx1 + qreal(0.5) - targetRect.left()) * ix) - 1; + basex = quint32(srcRect.left() * 65536) + dstx; + } + if (sy < 0) { + int dsty = qFloor((ty1 + qreal(0.5) - targetRect.bottom()) * iy) + 1; + srcy = quint32(srcRect.bottom() * 65536) + dsty; + } else { + int dsty = qCeil((ty1 + qreal(0.5) - targetRect.top()) * iy) - 1; + srcy = quint32(srcRect.top() * 65536) + dsty; + } + + quint32 *dst = ((quint32 *) (destPixels + ty1 * dbpl)) + tx1; + + while (h--) { + const uint *src = (const quint32 *) (srcPixels + (srcy >> 16) * sbpl); + int srcx = basex; + int x = 0; + for (; x<w; ++x) { + blender.write(&dst[x], src[srcx >> 16]); + srcx += ix; + } + blender.flush(&dst[x]); + dst = (quint32 *)(((uchar *) dst) + dbpl); + srcy += iy; + } +} + +struct QTransformImageVertex +{ + qreal x, y, u, v; // destination coordinates (x, y) and source coordinates (u, v) +}; + +template <class SrcT, class DestT, class Blender> +void qt_transform_image_rasterize(DestT *destPixels, int dbpl, + const SrcT *srcPixels, int sbpl, + const QTransformImageVertex &topLeft, const QTransformImageVertex &bottomLeft, + const QTransformImageVertex &topRight, const QTransformImageVertex &bottomRight, + const QRect &sourceRect, + const QRect &clip, + qreal topY, qreal bottomY, + int dudx, int dvdx, int dudy, int dvdy, int u0, int v0, + Blender blender) +{ + int fromY = qMax(qRound(topY), clip.top()); + int toY = qMin(qRound(bottomY), clip.top() + clip.height()); + if (fromY >= toY) + return; + + qreal leftSlope = (bottomLeft.x - topLeft.x) / (bottomLeft.y - topLeft.y); + qreal rightSlope = (bottomRight.x - topRight.x) / (bottomRight.y - topRight.y); + int dx_l = int(leftSlope * 0x10000); + int dx_r = int(rightSlope * 0x10000); + int x_l = int((topLeft.x + (0.5 + fromY - topLeft.y) * leftSlope + 0.5) * 0x10000); + int x_r = int((topRight.x + (0.5 + fromY - topRight.y) * rightSlope + 0.5) * 0x10000); + + int fromX, toX, x1, x2, u, v, i, ii; + DestT *line; + for (int y = fromY; y < toY; ++y) { + line = reinterpret_cast<DestT *>(reinterpret_cast<uchar *>(destPixels) + y * dbpl); + + fromX = qMax(x_l >> 16, clip.left()); + toX = qMin(x_r >> 16, clip.left() + clip.width()); + if (fromX < toX) { + // Because of rounding, we can get source coordinates outside the source image. + // Clamp these coordinates to the source rect to avoid segmentation fault and + // garbage on the screen. + + // Find the first pixel on the current scan line where the source coordinates are within the source rect. + x1 = fromX; + u = x1 * dudx + y * dudy + u0; + v = x1 * dvdx + y * dvdy + v0; + for (; x1 < toX; ++x1) { + int uu = u >> 16; + int vv = v >> 16; + if (uu >= sourceRect.left() && uu < sourceRect.left() + sourceRect.width() + && vv >= sourceRect.top() && vv < sourceRect.top() + sourceRect.height()) { + break; + } + u += dudx; + v += dvdx; + } + + // Find the last pixel on the current scan line where the source coordinates are within the source rect. + x2 = toX; + u = (x2 - 1) * dudx + y * dudy + u0; + v = (x2 - 1) * dvdx + y * dvdy + v0; + for (; x2 > x1; --x2) { + int uu = u >> 16; + int vv = v >> 16; + if (uu >= sourceRect.left() && uu < sourceRect.left() + sourceRect.width() + && vv >= sourceRect.top() && vv < sourceRect.top() + sourceRect.height()) { + break; + } + u -= dudx; + v -= dvdx; + } + + // Set up values at the beginning of the scan line. + u = fromX * dudx + y * dudy + u0; + v = fromX * dvdx + y * dvdy + v0; + line += fromX; + + // Beginning of the scan line, with per-pixel checks. + i = x1 - fromX; + while (i) { + int uu = qBound(sourceRect.left(), u >> 16, sourceRect.left() + sourceRect.width() - 1); + int vv = qBound(sourceRect.top(), v >> 16, sourceRect.top() + sourceRect.height() - 1); + blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + vv * sbpl)[uu]); + u += dudx; + v += dvdx; + ++line; + --i; + } + + // Middle of the scan line, without checks. + // Manual loop unrolling. + i = x2 - x1; + ii = i >> 3; + while (ii) { + blender.write(&line[0], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; + blender.write(&line[1], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; + blender.write(&line[2], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; + blender.write(&line[3], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; + blender.write(&line[4], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; + blender.write(&line[5], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; + blender.write(&line[6], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; + blender.write(&line[7], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; + + line += 8; + + --ii; + } + switch (i & 7) { + case 7: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line; + case 6: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line; + case 5: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line; + case 4: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line; + case 3: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line; + case 2: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line; + case 1: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line; + } + + // End of the scan line, with per-pixel checks. + i = toX - x2; + while (i) { + int uu = qBound(sourceRect.left(), u >> 16, sourceRect.left() + sourceRect.width() - 1); + int vv = qBound(sourceRect.top(), v >> 16, sourceRect.top() + sourceRect.height() - 1); + blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + vv * sbpl)[uu]); + u += dudx; + v += dvdx; + ++line; + --i; + } + + blender.flush(line); + } + x_l += dx_l; + x_r += dx_r; + } +} + +template <class SrcT, class DestT, class Blender> +void qt_transform_image(DestT *destPixels, int dbpl, + const SrcT *srcPixels, int sbpl, + const QRectF &targetRect, + const QRectF &sourceRect, + const QRect &clip, + const QTransform &targetRectTransform, + Blender blender) +{ + enum Corner + { + TopLeft, + TopRight, + BottomRight, + BottomLeft + }; + + // map source rectangle to destination. + QTransformImageVertex v[4]; + v[TopLeft].u = v[BottomLeft].u = sourceRect.left(); + v[TopLeft].v = v[TopRight].v = sourceRect.top(); + v[TopRight].u = v[BottomRight].u = sourceRect.right(); + v[BottomLeft].v = v[BottomRight].v = sourceRect.bottom(); + targetRectTransform.map(targetRect.left(), targetRect.top(), &v[TopLeft].x, &v[TopLeft].y); + targetRectTransform.map(targetRect.right(), targetRect.top(), &v[TopRight].x, &v[TopRight].y); + targetRectTransform.map(targetRect.left(), targetRect.bottom(), &v[BottomLeft].x, &v[BottomLeft].y); + targetRectTransform.map(targetRect.right(), targetRect.bottom(), &v[BottomRight].x, &v[BottomRight].y); + + // find topmost vertex. + int topmost = 0; + for (int i = 1; i < 4; ++i) { + if (v[i].y < v[topmost].y) + topmost = i; + } + // rearrange array such that topmost vertex is at index 0. + switch (topmost) { + case 1: + { + QTransformImageVertex t = v[0]; + for (int i = 0; i < 3; ++i) + v[i] = v[i+1]; + v[3] = t; + } + break; + case 2: + qSwap(v[0], v[2]); + qSwap(v[1], v[3]); + break; + case 3: + { + QTransformImageVertex t = v[3]; + for (int i = 3; i > 0; --i) + v[i] = v[i-1]; + v[0] = t; + } + break; + } + + // if necessary, swap vertex 1 and 3 such that 1 is to the left of 3. + qreal dx1 = v[1].x - v[0].x; + qreal dy1 = v[1].y - v[0].y; + qreal dx2 = v[3].x - v[0].x; + qreal dy2 = v[3].y - v[0].y; + if (dx1 * dy2 - dx2 * dy1 > 0) + qSwap(v[1], v[3]); + + QTransformImageVertex u = {v[1].x - v[0].x, v[1].y - v[0].y, v[1].u - v[0].u, v[1].v - v[0].v}; + QTransformImageVertex w = {v[2].x - v[0].x, v[2].y - v[0].y, v[2].u - v[0].u, v[2].v - v[0].v}; + + qreal det = u.x * w.y - u.y * w.x; + if (det == 0) + return; + + qreal invDet = 1.0 / det; + qreal m11, m12, m21, m22, mdx, mdy; + + m11 = (u.u * w.y - u.y * w.u) * invDet; + m12 = (u.x * w.u - u.u * w.x) * invDet; + m21 = (u.v * w.y - u.y * w.v) * invDet; + m22 = (u.x * w.v - u.v * w.x) * invDet; + mdx = v[0].u - m11 * v[0].x - m12 * v[0].y; + mdy = v[0].v - m21 * v[0].x - m22 * v[0].y; + + int dudx = int(m11 * 0x10000); + int dvdx = int(m21 * 0x10000); + int dudy = int(m12 * 0x10000); + int dvdy = int(m22 * 0x10000); + int u0 = qCeil((0.5 * m11 + 0.5 * m12 + mdx) * 0x10000) - 1; + int v0 = qCeil((0.5 * m21 + 0.5 * m22 + mdy) * 0x10000) - 1; + + int x1 = qFloor(sourceRect.left()); + int y1 = qFloor(sourceRect.top()); + int x2 = qCeil(sourceRect.right()); + int y2 = qCeil(sourceRect.bottom()); + QRect sourceRectI(x1, y1, x2 - x1, y2 - y1); + + // rasterize trapezoids. + if (v[1].y < v[3].y) { + qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[0], v[1], v[0], v[3], sourceRectI, clip, v[0].y, v[1].y, dudx, dvdx, dudy, dvdy, u0, v0, blender); + qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[1], v[2], v[0], v[3], sourceRectI, clip, v[1].y, v[3].y, dudx, dvdx, dudy, dvdy, u0, v0, blender); + qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[1], v[2], v[3], v[2], sourceRectI, clip, v[3].y, v[2].y, dudx, dvdx, dudy, dvdy, u0, v0, blender); + } else { + qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[0], v[1], v[0], v[3], sourceRectI, clip, v[0].y, v[3].y, dudx, dvdx, dudy, dvdy, u0, v0, blender); + qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[0], v[1], v[3], v[2], sourceRectI, clip, v[3].y, v[1].y, dudx, dvdx, dudy, dvdy, u0, v0, blender); + qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[1], v[2], v[3], v[2], sourceRectI, clip, v[1].y, v[2].y, dudx, dvdx, dudy, dvdy, u0, v0, blender); + } +} + +QT_END_NAMESPACE + +#endif // QBLENDFUNCTIONS_P_H diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 1f75ec7..917b910 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -175,7 +175,7 @@ Q_STATIC_TEMPLATE_FUNCTION uint * QT_FASTCALL destFetch(uint *buffer, QRasterBuf # define SPANFUNC_POINTER_DESTFETCH(Arg) destFetch<Arg> -static const DestFetchProc destFetchProc[QImage::NImageFormats] = +static DestFetchProc destFetchProc[QImage::NImageFormats] = { 0, // Format_Invalid destFetchMono, // Format_Mono, @@ -323,7 +323,7 @@ Q_STATIC_TEMPLATE_FUNCTION void QT_FASTCALL destStore(QRasterBuffer *rasterBuffe # define SPANFUNC_POINTER_DESTSTORE(DEST) destStore<DEST> -static const DestStoreProc destStoreProc[QImage::NImageFormats] = +static DestStoreProc destStoreProc[QImage::NImageFormats] = { 0, // Format_Invalid destStoreMono, // Format_Mono, @@ -2827,7 +2827,7 @@ static void QT_FASTCALL rasterop_SourceAndNotDestination(uint *dest, } } -static const CompositionFunctionSolid functionForModeSolid_C[] = { +static CompositionFunctionSolid functionForModeSolid_C[] = { comp_func_solid_SourceOver, comp_func_solid_DestinationOver, comp_func_solid_Clear, @@ -2865,7 +2865,7 @@ static const CompositionFunctionSolid functionForModeSolid_C[] = { static const CompositionFunctionSolid *functionForModeSolid = functionForModeSolid_C; -static const CompositionFunction functionForMode_C[] = { +static CompositionFunction functionForMode_C[] = { comp_func_SourceOver, comp_func_DestinationOver, comp_func_Clear, @@ -7961,6 +7961,20 @@ void qInitDrawhelperAsm() qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon; qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon; qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon; + qBlendFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_rgb16_neon; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB16] = qt_blend_rgb16_on_argb32_neon; + + qScaleFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_rgb16_neon; + qScaleFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_scale_image_rgb16_on_rgb16_neon; + + qTransformFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_transform_image_argb32_on_rgb16_neon; + qTransformFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_transform_image_rgb16_on_rgb16_neon; + + qDrawHelper[QImage::Format_RGB16].alphamapBlit = qt_alphamapblit_quint16_neon; + + functionForMode_C[QPainter::CompositionMode_SourceOver] = qt_blend_argb32_on_argb32_scanline_neon; + destFetchProc[QImage::Format_RGB16] = qt_destFetchRGB16_neon; + destStoreProc[QImage::Format_RGB16] = qt_destStoreRGB16_neon; } #endif diff --git a/src/gui/painting/qdrawhelper_neon.cpp b/src/gui/painting/qdrawhelper_neon.cpp index 77c5202..ee5f24a 100644 --- a/src/gui/painting/qdrawhelper_neon.cpp +++ b/src/gui/painting/qdrawhelper_neon.cpp @@ -40,10 +40,13 @@ ****************************************************************************/ #include <private/qdrawhelper_p.h> +#include <private/qblendfunctions_p.h> +#include <private/qmath_p.h> #ifdef QT_HAVE_NEON #include <private/qdrawhelper_neon_p.h> +#include <private/qpaintengine_raster_p.h> #include <arm_neon.h> QT_BEGIN_NAMESPACE @@ -87,60 +90,142 @@ static inline uint16x8_t qvsource_over_u16(uint16x8_t src16, uint16x8_t dst16, u return vaddq_u16(src16, qvbyte_mul_u16(dst16, alpha16, half)); } -void qt_blend_argb32_on_argb32_neon(uchar *destPixels, int dbpl, - const uchar *srcPixels, int sbpl, - int w, int h, - int const_alpha) +extern "C" void +pixman_composite_over_8888_0565_asm_neon (int32_t w, + int32_t h, + uint16_t *dst, + int32_t dst_stride, + uint32_t *src, + int32_t src_stride); + +extern "C" void +pixman_composite_over_8888_8888_asm_neon (int32_t w, + int32_t h, + uint32_t *dst, + int32_t dst_stride, + uint32_t *src, + int32_t src_stride); + +extern "C" void +pixman_composite_src_0565_8888_asm_neon (int32_t w, + int32_t h, + uint32_t *dst, + int32_t dst_stride, + uint16_t *src, + int32_t src_stride); + +extern "C" void +pixman_composite_over_n_8_0565_asm_neon (int32_t w, + int32_t h, + uint16_t *dst, + int32_t dst_stride, + uint32_t src, + int32_t unused, + uint8_t *mask, + int32_t mask_stride); + +extern "C" void +pixman_composite_scanline_over_asm_neon (int32_t w, + const uint32_t *dst, + const uint32_t *src); + +// qblendfunctions.cpp +void qt_blend_argb32_on_rgb16_const_alpha(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + +void qt_blend_rgb16_on_argb32_neon(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha) { - const uint *src = (const uint *) srcPixels; - uint *dst = (uint *) destPixels; - uint16x8_t half = vdupq_n_u16(0x80); - uint16x8_t full = vdupq_n_u16(0xff); - if (const_alpha == 256) { - for (int y = 0; y < h; ++y) { - int x = 0; - for (; x < w-3; x += 4) { - uint32x4_t src32 = vld1q_u32((uint32_t *)&src[x]); - if ((src[x] & src[x+1] & src[x+2] & src[x+3]) >= 0xff000000) { - // all opaque - vst1q_u32((uint32_t *)&dst[x], src32); - } else if (src[x] | src[x+1] | src[x+2] | src[x+3]) { - uint32x4_t dst32 = vld1q_u32((uint32_t *)&dst[x]); + dbpl /= 4; + sbpl /= 2; - const uint8x16_t src8 = vreinterpretq_u8_u32(src32); - const uint8x16_t dst8 = vreinterpretq_u8_u32(dst32); + quint32 *dst = (quint32 *) destPixels; + quint16 *src = (quint16 *) srcPixels; - const uint8x8_t src8_low = vget_low_u8(src8); - const uint8x8_t dst8_low = vget_low_u8(dst8); + if (const_alpha != 256) { + quint8 a = (255 * const_alpha) >> 8; + quint8 ia = 255 - a; + + while (h--) { + for (int x=0; x<w; ++x) + dst[x] = INTERPOLATE_PIXEL_255(qt_colorConvert(src[x], dst[x]), a, dst[x], ia); + dst += dbpl; + src += sbpl; + } + return; + } - const uint8x8_t src8_high = vget_high_u8(src8); - const uint8x8_t dst8_high = vget_high_u8(dst8); + pixman_composite_src_0565_8888_asm_neon(w, h, dst, dbpl, src, sbpl); +} - const uint16x8_t src16_low = vmovl_u8(src8_low); - const uint16x8_t dst16_low = vmovl_u8(dst8_low); +extern "C" void blend_8_pixels_argb32_on_rgb16_neon(quint16 *dst, const quint32 *src, int const_alpha); - const uint16x8_t src16_high = vmovl_u8(src8_high); - const uint16x8_t dst16_high = vmovl_u8(dst8_high); +void qt_blend_argb32_on_rgb16_neon(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha) +{ + quint16 *dst = (quint16 *) destPixels; + quint32 *src = (quint32 *) srcPixels; - const uint16x8_t result16_low = qvsource_over_u16(src16_low, dst16_low, half, full); - const uint16x8_t result16_high = qvsource_over_u16(src16_high, dst16_high, half, full); + if (const_alpha != 256) { + for (int y=0; y<h; ++y) { + int i = 0; + for (; i < w-7; i += 8) + blend_8_pixels_argb32_on_rgb16_neon(&dst[i], &src[i], const_alpha); - const uint32x2_t result32_low = vreinterpret_u32_u8(vmovn_u16(result16_low)); - const uint32x2_t result32_high = vreinterpret_u32_u8(vmovn_u16(result16_high)); + if (i < w) { + int tail = w - i; - vst1q_u32((uint32_t *)&dst[x], vcombine_u32(result32_low, result32_high)); + quint16 dstBuffer[8]; + quint32 srcBuffer[8]; + + for (int j = 0; j < tail; ++j) { + dstBuffer[j] = dst[i + j]; + srcBuffer[j] = src[i + j]; + } + + blend_8_pixels_argb32_on_rgb16_neon(dstBuffer, srcBuffer, const_alpha); + + for (int j = 0; j < tail; ++j) { + dst[i + j] = dstBuffer[j]; + src[i + j] = srcBuffer[j]; } } - for (; x<w; ++x) { - uint s = src[x]; - if (s >= 0xff000000) - dst[x] = s; - else if (s != 0) - dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); - } - dst = (quint32 *)(((uchar *) dst) + dbpl); - src = (const quint32 *)(((const uchar *) src) + sbpl); + + dst = (quint16 *)(((uchar *) dst) + dbpl); + src = (quint32 *)(((uchar *) src) + sbpl); } + return; + } + + pixman_composite_over_8888_0565_asm_neon(w, h, dst, dbpl / 2, src, sbpl / 4); +} + +void qt_blend_argb32_on_argb32_scanline_neon(uint *dest, const uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) { + pixman_composite_scanline_over_asm_neon(length, dest, src); + } else { + qt_blend_argb32_on_argb32_neon((uchar *)dest, 4 * length, (uchar *)src, 4 * length, length, 1, (const_alpha * 256) / 255); + } +} + +void qt_blend_argb32_on_argb32_neon(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha) +{ + const uint *src = (const uint *) srcPixels; + uint *dst = (uint *) destPixels; + uint16x8_t half = vdupq_n_u16(0x80); + uint16x8_t full = vdupq_n_u16(0xff); + if (const_alpha == 256) { + pixman_composite_over_8888_8888_asm_neon(w, h, (uint32_t *)destPixels, dbpl / 4, (uint32_t *)srcPixels, sbpl / 4); } else if (const_alpha != 0) { const_alpha = (const_alpha * 255) >> 8; uint16x8_t const_alpha16 = vdupq_n_u16(const_alpha); @@ -254,6 +339,246 @@ void qt_blend_rgb32_on_rgb32_neon(uchar *destPixels, int dbpl, } } +void qt_alphamapblit_quint16_neon(QRasterBuffer *rasterBuffer, + int x, int y, quint32 color, + const uchar *bitmap, + int mapWidth, int mapHeight, int mapStride, + const QClipData *) +{ + quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y)) + x; + const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint16); + + uchar *mask = const_cast<uchar *>(bitmap); + + pixman_composite_over_n_8_0565_asm_neon(mapWidth, mapHeight, dest, destStride, color, 0, mask, mapStride); +} + +extern "C" void blend_8_pixels_rgb16_on_rgb16_neon(quint16 *dst, const quint16 *src, int const_alpha); + +template <typename SRC, typename BlendFunc> +struct Blend_on_RGB16_SourceAndConstAlpha_Neon { + Blend_on_RGB16_SourceAndConstAlpha_Neon(BlendFunc blender, int const_alpha) + : m_index(0) + , m_blender(blender) + , m_const_alpha(const_alpha) + { + } + + inline void write(quint16 *dst, quint32 src) + { + srcBuffer[m_index++] = src; + + if (m_index == 8) { + m_blender(dst - 7, srcBuffer, m_const_alpha); + m_index = 0; + } + } + + inline void flush(quint16 *dst) + { + if (m_index > 0) { + quint16 dstBuffer[8]; + for (int i = 0; i < m_index; ++i) + dstBuffer[i] = dst[i - m_index]; + + m_blender(dstBuffer, srcBuffer, m_const_alpha); + + for (int i = 0; i < m_index; ++i) + dst[i - m_index] = dstBuffer[i]; + + m_index = 0; + } + } + + SRC srcBuffer[8]; + + int m_index; + BlendFunc m_blender; + int m_const_alpha; +}; + +template <typename SRC, typename BlendFunc> +Blend_on_RGB16_SourceAndConstAlpha_Neon<SRC, BlendFunc> +Blend_on_RGB16_SourceAndConstAlpha_Neon_create(BlendFunc blender, int const_alpha) +{ + return Blend_on_RGB16_SourceAndConstAlpha_Neon<SRC, BlendFunc>(blender, const_alpha); +} + +void qt_scale_image_argb32_on_rgb16_neon(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + const QRectF &targetRect, + const QRectF &sourceRect, + const QRect &clip, + int const_alpha) +{ + if (const_alpha == 0) + return; + + qt_scale_image_16bit<quint32>(destPixels, dbpl, srcPixels, sbpl, targetRect, sourceRect, clip, + Blend_on_RGB16_SourceAndConstAlpha_Neon_create<quint32>(blend_8_pixels_argb32_on_rgb16_neon, const_alpha)); +} + +void qt_scale_image_rgb16_on_rgb16(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + const QRectF &targetRect, + const QRectF &sourceRect, + const QRect &clip, + int const_alpha); + +void qt_scale_image_rgb16_on_rgb16_neon(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + const QRectF &targetRect, + const QRectF &sourceRect, + const QRect &clip, + int const_alpha) +{ + if (const_alpha == 0) + return; + + if (const_alpha == 256) { + qt_scale_image_rgb16_on_rgb16(destPixels, dbpl, srcPixels, sbpl, targetRect, sourceRect, clip, const_alpha); + return; + } + + qt_scale_image_16bit<quint16>(destPixels, dbpl, srcPixels, sbpl, targetRect, sourceRect, clip, + Blend_on_RGB16_SourceAndConstAlpha_Neon_create<quint16>(blend_8_pixels_rgb16_on_rgb16_neon, const_alpha)); +} + +extern void qt_transform_image_rgb16_on_rgb16(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + const QRectF &targetRect, + const QRectF &sourceRect, + const QRect &clip, + const QTransform &targetRectTransform, + int const_alpha); + +void qt_transform_image_rgb16_on_rgb16_neon(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + const QRectF &targetRect, + const QRectF &sourceRect, + const QRect &clip, + const QTransform &targetRectTransform, + int const_alpha) +{ + if (const_alpha == 0) + return; + + if (const_alpha == 256) { + qt_transform_image_rgb16_on_rgb16(destPixels, dbpl, srcPixels, sbpl, targetRect, sourceRect, clip, targetRectTransform, const_alpha); + return; + } + + qt_transform_image(reinterpret_cast<quint16 *>(destPixels), dbpl, + reinterpret_cast<const quint16 *>(srcPixels), sbpl, targetRect, sourceRect, clip, targetRectTransform, + Blend_on_RGB16_SourceAndConstAlpha_Neon_create<quint16>(blend_8_pixels_rgb16_on_rgb16_neon, const_alpha)); +} + +void qt_transform_image_argb32_on_rgb16_neon(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + const QRectF &targetRect, + const QRectF &sourceRect, + const QRect &clip, + const QTransform &targetRectTransform, + int const_alpha) +{ + if (const_alpha == 0) + return; + + qt_transform_image(reinterpret_cast<quint16 *>(destPixels), dbpl, + reinterpret_cast<const quint32 *>(srcPixels), sbpl, targetRect, sourceRect, clip, targetRectTransform, + Blend_on_RGB16_SourceAndConstAlpha_Neon_create<quint32>(blend_8_pixels_argb32_on_rgb16_neon, const_alpha)); +} + +static inline void convert_8_pixels_rgb16_to_argb32(quint32 *dst, const quint16 *src) +{ + asm volatile ( + "vld1.16 { d0, d1 }, [%[SRC]]\n\t" + + /* convert 8 r5g6b5 pixel data from {d0, d1} to planar 8-bit format + and put data into d4 - red, d3 - green, d2 - blue */ + "vshrn.u16 d4, q0, #8\n\t" + "vshrn.u16 d3, q0, #3\n\t" + "vsli.u16 q0, q0, #5\n\t" + "vsri.u8 d4, d4, #5\n\t" + "vsri.u8 d3, d3, #6\n\t" + "vshrn.u16 d2, q0, #2\n\t" + + /* fill d5 - alpha with 0xff */ + "mov r2, #255\n\t" + "vdup.8 d5, r2\n\t" + + "vst4.8 { d2, d3, d4, d5 }, [%[DST]]" + : : [DST]"r" (dst), [SRC]"r" (src) + : "memory", "r2", "d0", "d1", "d2", "d3", "d4", "d5" + ); +} + +uint * QT_FASTCALL qt_destFetchRGB16_neon(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length) +{ + const ushort *data = (const ushort *)rasterBuffer->scanLine(y) + x; + + int i = 0; + for (; i < length - 7; i += 8) + convert_8_pixels_rgb16_to_argb32(&buffer[i], &data[i]); + + if (i < length) { + quint16 srcBuffer[8]; + quint32 dstBuffer[8]; + + int tail = length - i; + for (int j = 0; j < tail; ++j) + srcBuffer[j] = data[i + j]; + + convert_8_pixels_rgb16_to_argb32(dstBuffer, srcBuffer); + + for (int j = 0; j < tail; ++j) + buffer[i + j] = dstBuffer[j]; + } + + return buffer; +} + +static inline void convert_8_pixels_argb32_to_rgb16(quint16 *dst, const quint32 *src) +{ + asm volatile ( + "vld4.8 { d0, d1, d2, d3 }, [%[SRC]]\n\t" + + /* convert to r5g6b5 and store it into {d28, d29} */ + "vshll.u8 q14, d2, #8\n\t" + "vshll.u8 q8, d1, #8\n\t" + "vshll.u8 q9, d0, #8\n\t" + "vsri.u16 q14, q8, #5\n\t" + "vsri.u16 q14, q9, #11\n\t" + + "vst1.16 { d28, d29 }, [%[DST]]" + : : [DST]"r" (dst), [SRC]"r" (src) + : "memory", "d0", "d1", "d2", "d3", "d16", "d17", "d18", "d19", "d28", "d29" + ); +} + +void QT_FASTCALL qt_destStoreRGB16_neon(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length) +{ + quint16 *data = (quint16*)rasterBuffer->scanLine(y) + x; + + int i = 0; + for (; i < length - 7; i += 8) + convert_8_pixels_argb32_to_rgb16(&data[i], &buffer[i]); + + if (i < length) { + quint32 srcBuffer[8]; + quint16 dstBuffer[8]; + + int tail = length - i; + for (int j = 0; j < tail; ++j) + srcBuffer[j] = buffer[i + j]; + + convert_8_pixels_argb32_to_rgb16(dstBuffer, srcBuffer); + + for (int j = 0; j < tail; ++j) + data[i + j] = dstBuffer[j]; + } +} + QT_END_NAMESPACE #endif // QT_HAVE_NEON diff --git a/src/gui/painting/qdrawhelper_neon_asm.S b/src/gui/painting/qdrawhelper_neon_asm.S new file mode 100644 index 0000000..9992817 --- /dev/null +++ b/src/gui/painting/qdrawhelper_neon_asm.S @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2010 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$ +** +****************************************************************************/ + +/* Prevent the stack from becoming executable for no reason... */ +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif + +.text +.fpu neon +.arch armv7a +.altmacro + +/* void blend_8_pixels_argb32_on_rgb16_neon(quint16 *dst, const quint32 *src, int const_alpha) */ + + .func blend_8_pixels_argb32_on_rgb16_neon + .global blend_8_pixels_argb32_on_rgb16_neon + /* For ELF format also set function visibility to hidden */ +#ifdef __ELF__ + .hidden blend_8_pixels_argb32_on_rgb16_neon + .type blend_8_pixels_argb32_on_rgb16_neon, %function +#endif +blend_8_pixels_argb32_on_rgb16_neon: + vld4.8 { d0, d1, d2, d3 }, [r1] + vld1.16 { d4, d5 }, [r0] + + cmp r2, #256 + beq .blend_32_inner + + vdup.8 d6, r2 + + /* multiply by const_alpha */ + vmull.u8 q8, d6, d0 + vmull.u8 q9, d6, d1 + vmull.u8 q10, d6, d2 + vmull.u8 q11, d6, d3 + + vshrn.u16 d0, q8, #8 + vshrn.u16 d1, q9, #8 + vshrn.u16 d2, q10, #8 + vshrn.u16 d3, q11, #8 + +.blend_32_inner: + /* convert 8 r5g6b5 pixel data from {d4, d5} to planar 8-bit format + and put data into d6 - red, d7 - green, d30 - blue */ + vshrn.u16 d6, q2, #8 + vshrn.u16 d7, q2, #3 + vsli.u16 q2, q2, #5 + vsri.u8 d6, d6, #5 + vmvn.8 d3, d3 + vsri.u8 d7, d7, #6 + vshrn.u16 d30, q2, #2 + + pld [r0, #128] + + /* now do alpha blending, storing results in 8-bit planar format + into d16 - red, d19 - green, d18 - blue */ + vmull.u8 q10, d3, d6 + vmull.u8 q11, d3, d7 + vmull.u8 q12, d3, d30 + vrshr.u16 q13, q10, #8 + vrshr.u16 q3, q11, #8 + vrshr.u16 q15, q12, #8 + vraddhn.u16 d20, q10, q13 + vraddhn.u16 d23, q11, q3 + vraddhn.u16 d22, q12, q15 + vqadd.u8 d16, d2, d20 + vqadd.u8 q9, q0, q11 + /* convert the result to r5g6b5 and store it into {d28, d29} */ + vshll.u8 q14, d16, #8 + vshll.u8 q8, d19, #8 + vshll.u8 q9, d18, #8 + vsri.u16 q14, q8, #5 + vsri.u16 q14, q9, #11 + + vst1.16 { d28, d29 }, [r0] + + bx lr + + .endfunc + +/* void blend_8_pixels_rgb16_on_rgb16_neon(quint16 *dst, const quint16 *src, int const_alpha) */ + + .func blend_8_pixels_rgb16_on_rgb16_neon + .global blend_8_pixels_rgb16_on_rgb16_neon + /* For ELF format also set function visibility to hidden */ +#ifdef __ELF__ + .hidden blend_8_pixels_rgb16_on_rgb16_neon + .type blend_8_pixels_rgb16_on_rgb16_neon, %function +#endif +blend_8_pixels_rgb16_on_rgb16_neon: + vld1.16 { d0, d1 }, [r0] + vld1.16 { d2, d3 }, [r1] + + rsb r3, r2, #256 + vdup.8 d4, r2 + vdup.8 d5, r3 + + /* convert 8 r5g6b5 pixel data from {d0, d1} to planar 8-bit format + and put data into d6 - red, d7 - green, d30 - blue */ + vshrn.u16 d6, q0, #8 + vshrn.u16 d7, q0, #3 + vsli.u16 q0, q0, #5 + vsri.u8 d6, d6, #5 + vsri.u8 d7, d7, #6 + vshrn.u16 d30, q0, #2 + + /* same from {d2, d3} into {d26, d27, d28} */ + vshrn.u16 d26, q1, #8 + vshrn.u16 d27, q1, #3 + vsli.u16 q1, q1, #5 + vsri.u8 d26, d26, #5 + vsri.u8 d27, d27, #6 + vshrn.u16 d28, q1, #2 + + /* multiply dst by inv const_alpha */ + vmull.u8 q10, d5, d6 + vmull.u8 q11, d5, d7 + vmull.u8 q12, d5, d30 + + vshrn.u16 d6, q10, #8 + vshrn.u16 d7, q11, #8 + vshrn.u16 d30, q12, #8 + + /* multiply src by const_alpha */ + vmull.u8 q10, d4, d26 + vmull.u8 q11, d4, d27 + vmull.u8 q12, d4, d28 + + vshrn.u16 d26, q10, #8 + vshrn.u16 d27, q11, #8 + vshrn.u16 d28, q12, #8 + + /* preload dst + 128 */ + pld [r0, #128] + + /* add components, storing results in 8-bit planar format + into d16 - red, d19 - green, d18 - blue */ + vadd.u8 d16, d26, d6 + vadd.u8 d19, d27, d7 + vadd.u8 d18, d28, d30 + + /* convert the result to r5g6b5 and store it into {d28, d29} */ + vshll.u8 q14, d16, #8 + vshll.u8 q8, d19, #8 + vshll.u8 q9, d18, #8 + vsri.u16 q14, q8, #5 + vsri.u16 q14, q9, #11 + + vst1.16 { d28, d29 }, [r0] + + bx lr + + .endfunc diff --git a/src/gui/painting/qdrawhelper_neon_p.h b/src/gui/painting/qdrawhelper_neon_p.h index 1994441..d6a4509 100644 --- a/src/gui/painting/qdrawhelper_neon_p.h +++ b/src/gui/painting/qdrawhelper_neon_p.h @@ -69,6 +69,64 @@ void qt_blend_rgb32_on_rgb32_neon(uchar *destPixels, int dbpl, int w, int h, int const_alpha); +void qt_blend_argb32_on_rgb16_neon(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + +void qt_blend_argb32_on_argb32_scanline_neon(uint *dest, + const uint *src, + int length, + uint const_alpha); + +void qt_blend_rgb16_on_argb32_neon(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + +void qt_alphamapblit_quint16_neon(QRasterBuffer *rasterBuffer, + int x, int y, quint32 color, + const uchar *bitmap, + int mapWidth, int mapHeight, int mapStride, + const QClipData *clip); + +void qt_scale_image_argb32_on_rgb16_neon(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + const QRectF &targetRect, + const QRectF &sourceRect, + const QRect &clip, + int const_alpha); + +void qt_scale_image_rgb16_on_rgb16_neon(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + const QRectF &targetRect, + const QRectF &sourceRect, + const QRect &clip, + int const_alpha); + +void qt_transform_image_argb32_on_rgb16_neon(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + const QRectF &targetRect, + const QRectF &sourceRect, + const QRect &clip, + const QTransform &targetRectTransform, + int const_alpha); + +void qt_transform_image_rgb16_on_rgb16_neon(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + const QRectF &targetRect, + const QRectF &sourceRect, + const QRect &clip, + const QTransform &targetRectTransform, + int const_alpha); + +uint * QT_FASTCALL qt_destFetchRGB16_neon(uint *buffer, + QRasterBuffer *rasterBuffer, + int x, int y, int length); + +void QT_FASTCALL qt_destStoreRGB16_neon(QRasterBuffer *rasterBuffer, + int x, int y, const uint *buffer, int length); + #endif // QT_HAVE_NEON QT_END_NAMESPACE diff --git a/src/gui/painting/qpaintbuffer.cpp b/src/gui/painting/qpaintbuffer.cpp index 39b76c8..e1156dc 100644 --- a/src/gui/painting/qpaintbuffer.cpp +++ b/src/gui/painting/qpaintbuffer.cpp @@ -269,24 +269,300 @@ void QPaintBuffer::draw(QPainter *painter, int frame) const printf("\n"); #endif - if (painter && !painter->isActive()) - return; + processCommands(painter, frameStartIndex(frame), frameEndIndex(frame)); + +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBuffer::draw() -------------------------------- DONE!"; +#endif +} + +int QPaintBuffer::frameStartIndex(int frame) const +{ + return (frame == 0) ? 0 : d_ptr->frames.at(frame - 1); +} + +int QPaintBuffer::frameEndIndex(int frame) const +{ + return (frame == d_ptr->frames.size()) ? d_ptr->commands.size() : d_ptr->frames.at(frame); +} + +int QPaintBuffer::processCommands(QPainter *painter, int begin, int end) const +{ + if (!painter || !painter->isActive()) + return 0; QPaintEngineEx *xengine = painter->paintEngine()->isExtended() ? (QPaintEngineEx *) painter->paintEngine() : 0; if (xengine) { QPaintEngineExReplayer player; - player.draw(*this, painter, frame); + player.processCommands(*this, painter, begin, end); } else { QPainterReplayer player; - player.draw(*this, painter, frame); + player.processCommands(*this, painter, begin, end); } -#ifdef QPAINTBUFFER_DEBUG_DRAW - qDebug() << "QPaintBuffer::draw() -------------------------------- DONE!"; -#endif + int depth = 0; + for (int i = begin; i < end; ++i) { + const QPaintBufferCommand &cmd = d_ptr->commands.at(i); + if (cmd.id == QPaintBufferPrivate::Cmd_Save) + ++depth; + else if (cmd.id == QPaintBufferPrivate::Cmd_Restore) + --depth; + } + return depth; } +QString QPaintBuffer::commandDescription(int command) const +{ + QString desc; + QDebug debug(&desc); + + const QPaintBufferCommand &cmd = d_ptr->commands.at(command); + + switch (cmd.id) { + case QPaintBufferPrivate::Cmd_Save: { + debug << "Cmd_Save"; + break; } + + case QPaintBufferPrivate::Cmd_Restore: { + debug << "Cmd_Restore"; + break; } + + case QPaintBufferPrivate::Cmd_SetBrush: { + QBrush brush = qVariantValue<QBrush>(d_ptr->variants.at(cmd.offset)); + debug << "Cmd_SetBrush: " << brush; + break; } + + case QPaintBufferPrivate::Cmd_SetBrushOrigin: { + debug << "Cmd_SetBrushOrigin: " << d_ptr->variants.at(cmd.offset).toPointF(); + break; } + + case QPaintBufferPrivate::Cmd_SetCompositionMode: { + QPainter::CompositionMode mode = (QPainter::CompositionMode) cmd.extra; + debug << "ExCmd_SetCompositionMode, mode: " << mode; + break; } + + case QPaintBufferPrivate::Cmd_SetOpacity: { + debug << "ExCmd_SetOpacity: " << d_ptr->variants.at(cmd.offset).toDouble(); + break; } + + case QPaintBufferPrivate::Cmd_DrawVectorPath: { + debug << "ExCmd_DrawVectorPath: size: " << cmd.size +// << ", hints:" << d->ints[cmd.offset2+cmd.size] + << "pts/elms:" << cmd.offset << cmd.offset2; + break; } + + case QPaintBufferPrivate::Cmd_StrokeVectorPath: { + QPen pen = qVariantValue<QPen>(d_ptr->variants.at(cmd.extra)); + debug << "ExCmd_StrokeVectorPath: size: " << cmd.size +// << ", hints:" << d->ints[cmd.offset2+cmd.size] + << "pts/elms:" << cmd.offset << cmd.offset2 << pen; + break; } + + case QPaintBufferPrivate::Cmd_FillVectorPath: { + QBrush brush = qVariantValue<QBrush>(d_ptr->variants.at(cmd.extra)); + debug << "ExCmd_FillVectorPath: size: " << cmd.size +// << ", hints:" << d->ints[cmd.offset2+cmd.size] + << "pts/elms:" << cmd.offset << cmd.offset2 << brush; + break; } + + case QPaintBufferPrivate::Cmd_FillRectBrush: { + QBrush brush = qVariantValue<QBrush>(d_ptr->variants.at(cmd.extra)); + QRectF *rect = (QRectF *)(d_ptr->floats.constData() + cmd.offset); + debug << "ExCmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " brush: " << brush; + break; } + + case QPaintBufferPrivate::Cmd_FillRectColor: { + QColor color = qVariantValue<QColor>(d_ptr->variants.at(cmd.extra)); + QRectF *rect = (QRectF *)(d_ptr->floats.constData() + cmd.offset); + debug << "ExCmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " color: " << color; + break; } + + case QPaintBufferPrivate::Cmd_DrawPolygonF: { + debug << "ExCmd_DrawPolygonF, offset: " << cmd.offset << " size: " << cmd.size + << " mode: " << cmd.extra + << d_ptr->floats.at(cmd.offset) + << d_ptr->floats.at(cmd.offset+1); + break; } + + case QPaintBufferPrivate::Cmd_DrawPolygonI: { + debug << "ExCmd_DrawPolygonI, offset: " << cmd.offset << " size: " << cmd.size + << " mode: " << cmd.extra + << d_ptr->ints.at(cmd.offset) + << d_ptr->ints.at(cmd.offset+1); + break; } + + case QPaintBufferPrivate::Cmd_DrawEllipseF: { + debug << "ExCmd_DrawEllipseF, offset: " << cmd.offset; + break; } + + case QPaintBufferPrivate::Cmd_DrawLineF: { + debug << "ExCmd_DrawLineF, offset: " << cmd.offset << " size: " << cmd.size; + break; } + + case QPaintBufferPrivate::Cmd_DrawLineI: { + debug << "ExCmd_DrawLineI, offset: " << cmd.offset << " size: " << cmd.size; + break; } + + case QPaintBufferPrivate::Cmd_DrawPointsF: { + debug << "ExCmd_DrawPointsF, offset: " << cmd.offset << " size: " << cmd.size; + break; } + + case QPaintBufferPrivate::Cmd_DrawPointsI: { + debug << "ExCmd_DrawPointsI, offset: " << cmd.offset << " size: " << cmd.size; + break; } + + case QPaintBufferPrivate::Cmd_DrawPolylineF: { + debug << "ExCmd_DrawPolylineF, offset: " << cmd.offset << " size: " << cmd.size; + break; } + + case QPaintBufferPrivate::Cmd_DrawPolylineI: { + debug << "ExCmd_DrawPolylineI, offset: " << cmd.offset << " size: " << cmd.size; + break; } + + case QPaintBufferPrivate::Cmd_DrawRectF: { + debug << "ExCmd_DrawRectF, offset: " << cmd.offset << " size: " << cmd.size; + break; } + + case QPaintBufferPrivate::Cmd_DrawRectI: { + debug << "ExCmd_DrawRectI, offset: " << cmd.offset << " size: " << cmd.size; + break; } + + case QPaintBufferPrivate::Cmd_SetClipEnabled: { + bool clipEnabled = d_ptr->variants.at(cmd.offset).toBool(); + debug << "ExCmd_SetClipEnabled:" << clipEnabled; + break; } + + case QPaintBufferPrivate::Cmd_ClipVectorPath: { + QVectorPathCmd path(d_ptr, cmd); + debug << "ExCmd_ClipVectorPath:" << path().elementCount(); + break; } + + case QPaintBufferPrivate::Cmd_ClipRect: { + QRect rect(QPoint(d_ptr->ints.at(cmd.offset), d_ptr->ints.at(cmd.offset + 1)), + QPoint(d_ptr->ints.at(cmd.offset + 2), d_ptr->ints.at(cmd.offset + 3))); + debug << "ExCmd_ClipRect:" << rect << cmd.extra; + break; } + + case QPaintBufferPrivate::Cmd_ClipRegion: { + QRegion region(d_ptr->variants.at(cmd.offset).value<QRegion>()); + debug << "ExCmd_ClipRegion:" << region.boundingRect() << cmd.extra; + break; } + + case QPaintBufferPrivate::Cmd_SetPen: { + QPen pen = qVariantValue<QPen>(d_ptr->variants.at(cmd.offset)); + debug << "Cmd_SetPen: " << pen; + break; } + + case QPaintBufferPrivate::Cmd_SetTransform: { + QTransform xform = qVariantValue<QTransform>(d_ptr->variants.at(cmd.offset)); + debug << "Cmd_SetTransform, offset: " << cmd.offset << xform; + break; } + + case QPaintBufferPrivate::Cmd_SetRenderHints: { + debug << "Cmd_SetRenderHints, hints: " << cmd.extra; + break; } + + case QPaintBufferPrivate::Cmd_SetBackgroundMode: { + debug << "Cmd_SetBackgroundMode: " << cmd.extra; + break; } + + case QPaintBufferPrivate::Cmd_DrawConvexPolygonF: { + debug << "Cmd_DrawConvexPolygonF, offset: " << cmd.offset << " size: " << cmd.size; + break; } + + case QPaintBufferPrivate::Cmd_DrawConvexPolygonI: { + debug << "Cmd_DrawConvexPolygonI, offset: " << cmd.offset << " size: " << cmd.size; + break; } + + case QPaintBufferPrivate::Cmd_DrawEllipseI: { + debug << "Cmd_DrawEllipseI, offset: " << cmd.offset; + break; } + + case QPaintBufferPrivate::Cmd_DrawPixmapRect: { + QPixmap pm(d_ptr->variants.at(cmd.offset).value<QPixmap>()); + QRectF r(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1), + d_ptr->floats.at(cmd.extra+2), d_ptr->floats.at(cmd.extra+3)); + + QRectF sr(d_ptr->floats.at(cmd.extra+4), d_ptr->floats.at(cmd.extra+5), + d_ptr->floats.at(cmd.extra+6), d_ptr->floats.at(cmd.extra+7)); + debug << "Cmd_DrawPixmapRect:" << r << sr << pm.size(); + break; } + + case QPaintBufferPrivate::Cmd_DrawPixmapPos: { + QPixmap pm(d_ptr->variants.at(cmd.offset).value<QPixmap>()); + QPointF pos(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1)); + debug << "Cmd_DrawPixmapPos:" << pos << pm.size(); + break; } + + case QPaintBufferPrivate::Cmd_DrawTiledPixmap: { + QPixmap pm(d_ptr->variants.at(cmd.offset).value<QPixmap>()); + QRectF r(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1), + d_ptr->floats.at(cmd.extra+2), d_ptr->floats.at(cmd.extra+3)); + + QPointF offset(d_ptr->floats.at(cmd.extra+4), d_ptr->floats.at(cmd.extra+5)); + debug << "Cmd_DrawTiledPixmap:" << r << offset << pm.size(); + break; } + + case QPaintBufferPrivate::Cmd_DrawImageRect: { + QImage image(d_ptr->variants.at(cmd.offset).value<QImage>()); + QRectF r(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1), + d_ptr->floats.at(cmd.extra+2), d_ptr->floats.at(cmd.extra+3)); + QRectF sr(d_ptr->floats.at(cmd.extra+4), d_ptr->floats.at(cmd.extra+5), + d_ptr->floats.at(cmd.extra+6), d_ptr->floats.at(cmd.extra+7)); + debug << "Cmd_DrawImageRect:" << r << sr << image.size(); + break; } + + case QPaintBufferPrivate::Cmd_DrawImagePos: { + QImage image(d_ptr->variants.at(cmd.offset).value<QImage>()); + QPointF pos(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1)); + debug << "Cmd_DrawImagePos:" << pos << image.size(); + break; } + + case QPaintBufferPrivate::Cmd_DrawText: { + QPointF pos(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1)); + QList<QVariant> variants(d_ptr->variants.at(cmd.offset).value<QList<QVariant> >()); + + QFont font(variants.at(0).value<QFont>()); + QString text(variants.at(1).value<QString>()); + + debug << "Cmd_DrawText:" << pos << text << font.family(); + break; } + + case QPaintBufferPrivate::Cmd_DrawTextItem: { + QPointF pos(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1)); + QTextItemIntCopy *tiCopy = reinterpret_cast<QTextItemIntCopy *>(qVariantValue<void *>(d_ptr->variants.at(cmd.offset))); + QTextItemInt &ti = (*tiCopy)(); + QString text(ti.text()); + + QFont font(ti.font()); + font.setUnderline(false); + font.setStrikeOut(false); + font.setOverline(false); + + const QTextItemInt &si = static_cast<const QTextItemInt &>(ti); + qreal justificationWidth = 0; + if (si.justified) + justificationWidth = si.width.toReal(); + + debug << "Cmd_DrawTextItem:" << pos << " " << text; + break; } + case QPaintBufferPrivate::Cmd_SystemStateChanged: { + QRegion systemClip(d_ptr->variants.at(cmd.offset).value<QRegion>()); + + debug << "Cmd_SystemStateChanged:" << systemClip; + break; } + case QPaintBufferPrivate::Cmd_Translate: { + QPointF delta(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1)); + debug << "Cmd_Translate:" << delta; + break; } + case QPaintBufferPrivate::Cmd_DrawStaticText: { + debug << "Cmd_DrawStaticText"; + break; } + } + + return desc; +} QRectF QPaintBuffer::boundingRect() const { @@ -992,13 +1268,14 @@ void QPaintBufferEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, con void QPaintBufferEngine::drawStaticTextItem(QStaticTextItem *staticTextItem) { - QString text = QString(staticTextItem->chars, staticTextItem->numChars); + QVariantList variants; - QStaticText staticText(text); - staticText.prepare(state()->matrix, staticTextItem->font); + variants << QVariant(staticTextItem->font); + for (int i=0; i<staticTextItem->numGlyphs; ++i) { + variants.append(staticTextItem->glyphs[i]); + variants.append(staticTextItem->glyphPositions[i].toPointF()); + } - QVariantList variants; - variants << QVariant(staticTextItem->font) << QVariant::fromValue(staticText); buffer->addCommand(QPaintBufferPrivate::Cmd_DrawStaticText, QVariant(variants)); } @@ -1110,15 +1387,12 @@ void QPainterReplayer::setupTransform(QPainter *_painter) painter->setTransform(m_world_matrix); } -void QPainterReplayer::draw(const QPaintBuffer &buffer, QPainter *_painter, int frame) +void QPainterReplayer::processCommands(const QPaintBuffer &buffer, QPainter *p, int begin, int end) { d = buffer.d_ptr; - setupTransform(_painter); - - int frameStart = (frame == 0) ? 0 : d->frames.at(frame-1); - int frameEnd = (frame == d->frames.size()) ? d->commands.size() : d->frames.at(frame); + painter = p; - for (int cmdIndex=frameStart; cmdIndex<frameEnd; ++cmdIndex) { + for (int cmdIndex = begin; cmdIndex < end; ++cmdIndex) { const QPaintBufferCommand &cmd = d->commands.at(cmdIndex); process(cmd); } @@ -1484,11 +1758,19 @@ void QPainterReplayer::process(const QPaintBufferCommand &cmd) QVariantList variants(d->variants.at(cmd.offset).value<QVariantList>()); - QFont font(variants.at(0).value<QFont>()); - QStaticText text(variants.at(0).value<QStaticText>()); - + QFont font = variants.at(0).value<QFont>(); + + QVector<quint32> glyphs; + QVector<QPointF> positions; + + for (int i=0; i<(variants.size() - 1) / 2; ++i) { + glyphs.append(variants.at(i*2 + 1).toUInt()); + positions.append(variants.at(i*2 + 2).toPointF()); + } + painter->setFont(font); - painter->drawStaticText(QPointF(0, 0), text); + + qt_draw_glyphs(painter, glyphs.constData(), positions.constData(), glyphs.size()); break; } diff --git a/src/gui/painting/qpaintbuffer_p.h b/src/gui/painting/qpaintbuffer_p.h index 0fde290..4576947 100644 --- a/src/gui/painting/qpaintbuffer_p.h +++ b/src/gui/painting/qpaintbuffer_p.h @@ -78,6 +78,12 @@ public: int numFrames() const; void draw(QPainter *painter, int frame = 0) const; + + int frameStartIndex(int frame) const; + int frameEndIndex(int frame) const; + int processCommands(QPainter *painter, int begin, int end) const; + QString commandDescription(int command) const; + void setBoundingRect(const QRectF &rect); QRectF boundingRect() const; @@ -317,7 +323,7 @@ public: void setupTransform(QPainter *painter); virtual void process(const QPaintBufferCommand &cmd); - void draw(const QPaintBuffer &buffer, QPainter *painter, int frame); + void processCommands(const QPaintBuffer &buffer, QPainter *painter, int begin, int end); protected: QPaintBufferPrivate *d; diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 1c528fe..898a996 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -5720,6 +5720,16 @@ void QPainterPrivate::drawGlyphs(const quint32 *glyphArray, const QPointF *posit QFontEngine *fontEngine = state->font.d->engineForScript(QUnicodeTables::Common); + while (fontEngine->type() == QFontEngine::Multi) { + // Pick engine based on first glyph in array if we are using a multi engine. + // (all glyphs must be for same font) + int engineIdx = 0; + if (glyphCount > 0) + engineIdx = glyphArray[0] >> 24; + + fontEngine = static_cast<QFontEngineMulti *>(fontEngine)->engine(engineIdx); + } + QVarLengthArray<QFixedPoint, 128> positions; for (int i=0; i<glyphCount; ++i) { QFixedPoint fp = QFixedPoint::fromPointF(positionArray[i]); @@ -5762,19 +5772,24 @@ void QPainterPrivate::drawGlyphs(const quint32 *glyphArray, const QPointF *posit /*! - \fn void QPainter::drawStaticText(const QPoint &position, const QStaticText &staticText) + \fn void QPainter::drawStaticText(const QPoint &topLeftPosition, const QStaticText &staticText) \since 4.7 \overload - Draws the \a staticText at the \a position. + Draws the \a staticText at the \a topLeftPosition. + + \note The y-position is used as the top of the font. + */ /*! - \fn void QPainter::drawStaticText(int x, int y, const QStaticText &staticText) + \fn void QPainter::drawStaticText(int left, int top, const QStaticText &staticText) \since 4.7 \overload - Draws the \a staticText at coordinates \a x and \a y. + Draws the \a staticText at coordinates \a left and \a top. + + \note The y-position is used as the top of the font. */ /*! @@ -5802,7 +5817,7 @@ void QPainter::drawText(const QPointF &p, const QString &str) /*! \since 4.7 - Draws the given \a staticText at the given \a position. + Draws the given \a staticText at the given \a topLeftPosition. The text will be drawn using the font and the transformation set on the painter. If the font and/or transformation set on the painter are different from the ones used to initialize @@ -5810,15 +5825,17 @@ void QPainter::drawText(const QPointF &p, const QString &str) QStaticText::prepare() to initialize \a staticText with the font and transformation with which it will later be drawn. - If \a position is not the same as when \a staticText was initialized, or when it was last drawn, - then there will be a slight overhead when translating the text to its new position. + If \a topLeftPosition is not the same as when \a staticText was initialized, or when it was + last drawn, then there will be a slight overhead when translating the text to its new position. - \note If the painter's transformation is not affine, then \a staticText will be drawn using regular - calls to drawText(), losing any potential performance improvement. + \note If the painter's transformation is not affine, then \a staticText will be drawn using + regular calls to drawText(), losing any potential for performance improvement. + + \note The y-position is used as the top of the font. \sa QStaticText */ -void QPainter::drawStaticText(const QPointF &position, const QStaticText &staticText) +void QPainter::drawStaticText(const QPointF &topLeftPosition, const QStaticText &staticText) { Q_D(QPainter); if (!d->engine || staticText.text().isEmpty() || pen().style() == Qt::NoPen) @@ -5827,16 +5844,21 @@ void QPainter::drawStaticText(const QPointF &position, const QStaticText &static QStaticTextPrivate *staticText_d = const_cast<QStaticTextPrivate *>(QStaticTextPrivate::get(&staticText)); + if (font() != staticText_d->font) { + staticText_d->font = font(); + staticText_d->needsRelayout = true; + } + // If we don't have an extended paint engine, or if the painter is projected, // we go through standard code path if (d->extended == 0 || !d->state->matrix.isAffine()) { - staticText_d->paintText(position, this); + staticText_d->paintText(topLeftPosition, this); return; } // Don't recalculate entire layout because of translation, rather add the dx and dy // into the position to move each text item the correct distance. - QPointF transformedPosition = position * d->state->matrix; + QPointF transformedPosition = topLeftPosition * d->state->matrix; QTransform matrix = d->state->matrix; // The translation has been applied to transformedPosition. Remove translation @@ -5857,25 +5879,12 @@ void QPainter::drawStaticText(const QPointF &position, const QStaticText &static // If the transform is not identical to the text transform, // we have to relayout the text (for other transformations than plain translation) - bool staticTextNeedsReinit = false; + bool staticTextNeedsReinit = staticText_d->needsRelayout; if (staticText_d->matrix != d->state->matrix) { staticText_d->matrix = d->state->matrix; staticTextNeedsReinit = true; } - bool restoreWhenFinished = false; - if (staticText_d->needsClipRect) { - save(); - setClipRect(QRectF(position, staticText_d->maximumSize)); - - restoreWhenFinished = true; - } - - if (font() != staticText_d->font) { - staticText_d->font = font(); - staticTextNeedsReinit = true; - } - // Recreate the layout of the static text because the matrix or font has changed if (staticTextNeedsReinit) staticText_d->init(); @@ -5901,7 +5910,7 @@ void QPainter::drawStaticText(const QPointF &position, const QStaticText &static QColor currentColor = oldPen.color(); for (int i=0; i<staticText_d->itemCount; ++i) { QStaticTextItem *item = staticText_d->items + i; - if (currentColor != item->color) { + if (item->color.isValid() && currentColor != item->color) { setPen(item->color); currentColor = item->color; } @@ -5910,9 +5919,6 @@ void QPainter::drawStaticText(const QPointF &position, const QStaticText &static if (currentColor != oldPen.color()) setPen(oldPen); - if (restoreWhenFinished) - restore(); - if (matrix.isTranslating()) d->state->matrix = matrix; } diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h index 443925b..edfb67e 100644 --- a/src/gui/painting/qpainter.h +++ b/src/gui/painting/qpainter.h @@ -396,9 +396,9 @@ public: void setLayoutDirection(Qt::LayoutDirection direction); Qt::LayoutDirection layoutDirection() const; - void drawStaticText(const QPointF &p, const QStaticText &staticText); - inline void drawStaticText(const QPoint &p, const QStaticText &staticText); - inline void drawStaticText(int x, int y, const QStaticText &staticText); + void drawStaticText(const QPointF &topLeftPosition, const QStaticText &staticText); + inline void drawStaticText(const QPoint &topLeftPosition, const QStaticText &staticText); + inline void drawStaticText(int left, int top, const QStaticText &staticText); void drawText(const QPointF &p, const QString &s); inline void drawText(const QPoint &p, const QString &s); diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp index 4f42a58..988d678 100644 --- a/src/gui/painting/qtransform.cpp +++ b/src/gui/painting/qtransform.cpp @@ -1037,8 +1037,18 @@ QDataStream & operator>>(QDataStream &s, QTransform &t) #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QTransform &m) { - dbg.nospace() << "QTransform(" - << "11=" << m.m11() + static const char *typeStr[] = + { + "TxNone", + "TxTranslate", + "TxScale", + "TxRotate", + "TxShear", + "TxProject" + }; + + dbg.nospace() << "QTransform(type=" << typeStr[m.type()] << ',' + << " 11=" << m.m11() << " 12=" << m.m12() << " 13=" << m.m13() << " 21=" << m.m21() @@ -1048,6 +1058,7 @@ QDebug operator<<(QDebug dbg, const QTransform &m) << " 32=" << m.m32() << " 33=" << m.m33() << ')'; + return dbg.space(); } #endif diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 9b85c04..99b9f40 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -1297,6 +1297,8 @@ QFont::StyleHint QFont::styleHint() const \value PreferQuality prefer the best quality font. The font matcher will use the nearest standard point size that the font supports. + \value ForceIntegerMetrics forces the use of integer values in font engines that support fractional + font metrics. */ /*! diff --git a/src/gui/text/qfont.h b/src/gui/text/qfont.h index 5adf237..6f62424 100644 --- a/src/gui/text/qfont.h +++ b/src/gui/text/qfont.h @@ -76,17 +76,18 @@ public: }; enum StyleStrategy { - PreferDefault = 0x0001, - PreferBitmap = 0x0002, - PreferDevice = 0x0004, - PreferOutline = 0x0008, - ForceOutline = 0x0010, - PreferMatch = 0x0020, - PreferQuality = 0x0040, - PreferAntialias = 0x0080, - NoAntialias = 0x0100, - OpenGLCompatible = 0x0200, - NoFontMerging = 0x8000 + PreferDefault = 0x0001, + PreferBitmap = 0x0002, + PreferDevice = 0x0004, + PreferOutline = 0x0008, + ForceOutline = 0x0010, + PreferMatch = 0x0020, + PreferQuality = 0x0040, + PreferAntialias = 0x0080, + NoAntialias = 0x0100, + OpenGLCompatible = 0x0200, + ForceIntegerMetrics = 0x0400, + NoFontMerging = 0x8000 }; enum Weight { diff --git a/src/gui/text/qfontengine_mac.mm b/src/gui/text/qfontengine_mac.mm index 8588214..0bfdbc0 100644 --- a/src/gui/text/qfontengine_mac.mm +++ b/src/gui/text/qfontengine_mac.mm @@ -53,6 +53,7 @@ #include <qvarlengtharray.h> #include <qdebug.h> #include <qendian.h> +#include <qmath.h> #include <ApplicationServices/ApplicationServices.h> #include <AppKit/AppKit.h> @@ -303,12 +304,20 @@ bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLay outGlyphs[idx] = tmpGlyphs[i] | fontIndex; outAdvances_x[idx] = QFixed::fromReal(tmpPoints[i + 1].x - tmpPoints[i].x); outAdvances_y[idx] = QFixed::fromReal(tmpPoints[i + 1].y - tmpPoints[i].y); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + outAdvances_x[idx] = outAdvances_x[idx].round(); + outAdvances_y[idx] = outAdvances_y[idx].round(); + } } CGSize lastGlyphAdvance; CTFontGetAdvancesForGlyphs(runFont, kCTFontHorizontalOrientation, tmpGlyphs + glyphCount - 1, &lastGlyphAdvance, 1); outGlyphs[rtl ? 0 : (glyphCount - 1)] = tmpGlyphs[glyphCount - 1] | fontIndex; - outAdvances_x[rtl ? 0 : (glyphCount - 1)] = QFixed::fromReal(lastGlyphAdvance.width); + outAdvances_x[rtl ? 0 : (glyphCount - 1)] = + (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(lastGlyphAdvance.width).round() + : QFixed::fromReal(lastGlyphAdvance.width); } outGlyphs += glyphCount; outAttributes += glyphCount; @@ -378,8 +387,11 @@ bool QCoreTextFontEngine::stringToCMap(const QChar *, int, QGlyphLayout *, int * glyph_metrics_t QCoreTextFontEngine::boundingBox(const QGlyphLayout &glyphs) { QFixed w; - for (int i = 0; i < glyphs.numGlyphs; ++i) - w += glyphs.effectiveAdvance(i); + for (int i = 0; i < glyphs.numGlyphs; ++i) { + w += (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? glyphs.effectiveAdvance(i).round() + : glyphs.effectiveAdvance(i); + } return glyph_metrics_t(0, -(ascent()), w, ascent()+descent(), w, 0); } glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph) @@ -393,33 +405,51 @@ glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph) ret.y = -QFixed::fromReal(rect.origin.y) - ret.height; CGSize advances[1]; CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, advances, 1); - ret.xoff = QFixed::fromReal(advances[0].width).ceil(); - ret.yoff = QFixed::fromReal(advances[0].height).ceil(); + ret.xoff = QFixed::fromReal(advances[0].width); + ret.yoff = QFixed::fromReal(advances[0].height); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + ret.xoff = ret.xoff.round(); + ret.yoff = ret.yoff.round(); + } + return ret; } QFixed QCoreTextFontEngine::ascent() const { - return QFixed::fromReal(CTFontGetAscent(ctfont)).ceil(); + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(CTFontGetAscent(ctfont)).round() + : QFixed::fromReal(CTFontGetAscent(ctfont)); } QFixed QCoreTextFontEngine::descent() const { + QFixed d = QFixed::fromReal(CTFontGetDescent(ctfont)); + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + d = d.round(); + // subtract a pixel to even out the historical +1 in QFontMetrics::height(). // Fix in Qt 5. - return QFixed::fromReal(CTFontGetDescent(ctfont)).ceil() - 1; + return d - 1; } QFixed QCoreTextFontEngine::leading() const { - return QFixed::fromReal(CTFontGetLeading(ctfont)).ceil(); + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(CTFontGetLeading(ctfont)).round() + : QFixed::fromReal(CTFontGetLeading(ctfont)); } QFixed QCoreTextFontEngine::xHeight() const { - return QFixed::fromReal(CTFontGetXHeight(ctfont)).ceil(); + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(CTFontGetXHeight(ctfont)).round() + : QFixed::fromReal(CTFontGetXHeight(ctfont)); } QFixed QCoreTextFontEngine::averageCharWidth() const { // ### Need to implement properly and get the information from the OS/2 Table. - return QFontEngine::averageCharWidth(); + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFontEngine::averageCharWidth().round() + : QFontEngine::averageCharWidth(); } qreal QCoreTextFontEngine::maxCharWidth() const @@ -787,6 +817,7 @@ struct QGlyphLayoutInfo int *mappedFonts; QTextEngine::ShaperFlags flags; QFontEngineMacMulti::ShaperItem *shaperItem; + unsigned int styleStrategy; }; static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATSULineRef lineRef, URefCon refCon, @@ -856,6 +887,11 @@ static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATS QFixed yAdvance = FixedToQFixed(baselineDeltas[glyphIdx]); QFixed xAdvance = FixedToQFixed(layoutData[glyphIdx + 1].realPos - layoutData[glyphIdx].realPos); + if (nfo->styleStrategy & QFont::ForceIntegerMetrics) { + yAdvance = yAdvance.round(); + xAdvance = xAdvance.round(); + } + if (glyphId != 0xffff || i == 0) { if (i < nfo->glyphs->numGlyphs) { @@ -1032,6 +1068,7 @@ bool QFontEngineMacMulti::stringToCMapInternal(const QChar *str, int len, QGlyph nfo.callbackCalled = false; nfo.flags = flags; nfo.shaperItem = shaperItem; + nfo.styleStrategy = fontDef.styleStrategy; int prevNumGlyphs = *nglyphs; @@ -1061,8 +1098,6 @@ bool QFontEngineMacMulti::stringToCMapInternal(const QChar *str, int len, QGlyph | kATSLineDisableAllJustification ; - layopts |= kATSLineUseDeviceMetrics; - if (fontDef.styleStrategy & QFont::NoAntialias) layopts |= kATSLineNoAntiAliasing; @@ -1366,14 +1401,22 @@ void QFontEngineMac::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFla for (int i = 0; i < glyphs->numGlyphs; ++i) { glyphs->advances_x[i] = QFixed::fromReal(metrics[i].deviceAdvance.x); glyphs->advances_y[i] = QFixed::fromReal(metrics[i].deviceAdvance.y); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + glyphs->advances_x[i] = glyphs->advances_x[i].round(); + glyphs->advances_y[i] = glyphs->advances_y[i].round(); + } } } glyph_metrics_t QFontEngineMac::boundingBox(const QGlyphLayout &glyphs) { QFixed w; - for (int i = 0; i < glyphs.numGlyphs; ++i) - w += glyphs.effectiveAdvance(i); + for (int i = 0; i < glyphs.numGlyphs; ++i) { + w += (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? glyphs.effectiveAdvance(i).round() + : glyphs.effectiveAdvance(i); + } return glyph_metrics_t(0, -(ascent()), w, ascent()+descent(), w, 0); } @@ -1398,39 +1441,58 @@ glyph_metrics_t QFontEngineMac::boundingBox(glyph_t glyph) gm.xoff = QFixed::fromReal(metrics.deviceAdvance.x); gm.yoff = QFixed::fromReal(metrics.deviceAdvance.y); + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + gm.x = gm.x.floor(); + gm.y = gm.y.floor(); + gm.xoff = gm.xoff.round(); + gm.yoff = gm.yoff.round(); + } + return gm; } QFixed QFontEngineMac::ascent() const { - return m_ascent; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_ascent.round() + : m_ascent; } QFixed QFontEngineMac::descent() const { // subtract a pixel to even out the historical +1 in QFontMetrics::height(). // Fix in Qt 5. - return m_descent - 1; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_descent.round() - 1 + : m_descent; } QFixed QFontEngineMac::leading() const { - return m_leading; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_leading.round() + : m_leading; } qreal QFontEngineMac::maxCharWidth() const { - return m_maxCharWidth; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? qRound(m_maxCharWidth) + : m_maxCharWidth; } QFixed QFontEngineMac::xHeight() const { - return m_xHeight; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_xHeight.round() + : m_xHeight; } QFixed QFontEngineMac::averageCharWidth() const { - return m_averageCharWidth; + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_averageCharWidth.round() + : m_averageCharWidth; } static void addGlyphsToPathHelper(ATSUStyle style, glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, QPainterPath *path) diff --git a/src/gui/text/qfontmetrics.cpp b/src/gui/text/qfontmetrics.cpp index 9349f46..3a52e8e 100644 --- a/src/gui/text/qfontmetrics.cpp +++ b/src/gui/text/qfontmetrics.cpp @@ -328,7 +328,7 @@ int QFontMetrics::height() const { QFontEngine *engine = d->engineForScript(QUnicodeTables::Common); Q_ASSERT(engine != 0); - return qRound(engine->ascent() + engine->descent()) + 1; + return qRound(engine->ascent()) + qRound(engine->descent()) + 1; } /*! @@ -356,7 +356,7 @@ int QFontMetrics::lineSpacing() const { QFontEngine *engine = d->engineForScript(QUnicodeTables::Common); Q_ASSERT(engine != 0); - return qRound(engine->leading() + engine->ascent() + engine->descent()) + 1; + return qRound(engine->leading()) + qRound(engine->ascent()) + qRound(engine->descent()) + 1; } /*! diff --git a/src/gui/text/qstatictext.cpp b/src/gui/text/qstatictext.cpp index 798ae37..6c504a7 100644 --- a/src/gui/text/qstatictext.cpp +++ b/src/gui/text/qstatictext.cpp @@ -99,20 +99,27 @@ QT_BEGIN_NAMESPACE point with no boundaries, and also when QPainter::drawText() is called with a bounding rectangle. - If a bounding rectangle is not required, create a QStaticText object without setting a maximum - size. The text will then occupy a single line. + If a bounding rectangle is not required, create a QStaticText object without setting a preferred + text width. The text will then occupy a single line. - If you set a maximum size on the QStaticText object, this will bound the text. The text will - be formatted so that no line exceeds the given width. When the object is painted, it will - be clipped at the given size. The position of the text is decided by the argument - passed to QPainter::drawStaticText() and can change from call to call with a minimal impact - on performance. + If you set a text width on the QStaticText object, this will bound the text. The text will + be formatted so that no line exceeds the given width. The text width set for QStaticText will + not automatically be used for clipping. To achieve clipping in addition to line breaks, use + QPainter::setClipRect(). The position of the text is decided by the argument passed to + QPainter::drawStaticText() and can change from call to call with a minimal impact on + performance. QStaticText will attempt to guess the format of the input text using Qt::mightBeRichText(). To force QStaticText to display its contents as either plain text or rich text, use the function QStaticText::setTextFormat() and pass in, respectively, Qt::PlainText and Qt::RichText. + If it's the first time the static text is drawn, or if the static text, or the painter's font + or matrix have been altered since the last time it was drawn, the text's layout has to be + recalculated. This will impose an overhead on the QPainter::drawStaticText() call where the + relayout occurs. To avoid this overhead in the paint event, you can call prepare() ahead of + time to ensure that the layout is calculated. + \sa QPainter::drawText(), QPainter::drawStaticText(), QTextLayout, QTextDocument */ @@ -143,12 +150,11 @@ QStaticText::QStaticText() If an invalid size is passed for \a size the text will be unbounded. */ -QStaticText::QStaticText(const QString &text, const QSizeF &size) +QStaticText::QStaticText(const QString &text) : data(new QStaticTextPrivate) { data->text = text; - data->maximumSize = size; - data->init(); + data->invalidate(); } /*! @@ -177,17 +183,17 @@ void QStaticText::detach() } /*! - Prepares the QStaticText object for being painted with the given \a matrix and the given - \a font to avoid overhead when the actual drawStaticText() call is made. + Prepares the QStaticText object for being painted with the given \a matrix and the given \a font + to avoid overhead when the actual drawStaticText() call is made. - When drawStaticText() is called, the layout of the QStaticText will be recalculated if the - painter's font or matrix is different from the one used for the currently cached layout. By - default, QStaticText will use a default constructed QFont and an identity matrix to create - its layout. + When drawStaticText() is called, the layout of the QStaticText will be recalculated if any part + of the QStaticText object has changed since the last time it was drawn. It will also be + recalculated if the painter's font or matrix are not the same as when the QStaticText was last + drawn. - To avoid the overhead of creating the layout the first time you draw the QStaticText with - a painter whose matrix or font are different from the defaults, you can use the prepare() - function and pass in the matrix and font you expect to use when drawing the text. + To avoid the overhead of creating the layout the first time you draw the QStaticText after + making changes, you can use the prepare() function and pass in the \a matrix and \a font you + expect to use when drawing the text. \sa QPainter::setFont(), QPainter::setMatrix() */ @@ -209,7 +215,7 @@ QStaticText &QStaticText::operator=(const QStaticText &other) } /*! - Compares \a other to this QStaticText. Returns true if the texts, fonts and maximum sizes + Compares \a other to this QStaticText. Returns true if the texts, fonts and text widths are equal. */ bool QStaticText::operator==(const QStaticText &other) const @@ -217,7 +223,7 @@ bool QStaticText::operator==(const QStaticText &other) const return (data == other.data || (data->text == other.data->text && data->font == other.data->font - && data->maximumSize == other.data->maximumSize)); + && data->textWidth == other.data->textWidth)); } /*! @@ -232,7 +238,7 @@ bool QStaticText::operator!=(const QStaticText &other) const /*! Sets the text of the QStaticText to \a text. - \note This function will cause the layout of the text to be recalculated. + \note This function will cause the layout of the text to require recalculation. \sa text() */ @@ -240,7 +246,7 @@ void QStaticText::setText(const QString &text) { detach(); data->text = text; - data->init(); + data->invalidate(); } /*! @@ -250,7 +256,7 @@ void QStaticText::setText(const QString &text) displayed as is, whereas it will be interpreted as HTML if the format is Qt::RichText. HTML tags that alter the font of the text, its color, or its layout are supported by QStaticText. - \note This function will cause the layout of the text to be recalculated. + \note This function will cause the layout of the text to require recalculation. \sa textFormat(), setText(), text() */ @@ -258,7 +264,7 @@ void QStaticText::setTextFormat(Qt::TextFormat textFormat) { detach(); data->textFormat = textFormat; - data->init(); + data->invalidate(); } /*! @@ -289,7 +295,7 @@ QString QStaticText::text() const The default is QStaticText::ModerateCaching. - \note This function will cause the layout of the text to be recalculated. + \note This function will cause the layout of the text to require recalculation. \sa performanceHint() */ @@ -301,7 +307,7 @@ void QStaticText::setPerformanceHint(PerformanceHint performanceHint) } detach(); data->useBackendOptimizations = (performanceHint == AggressiveCaching); - data->init(); + data->invalidate(); } /*! @@ -315,48 +321,56 @@ QStaticText::PerformanceHint QStaticText::performanceHint() const } /*! - Sets the maximum size of the QStaticText to \a size. + Sets the preferred width for this QStaticText. If the text is wider than the specified width, + it will be broken into multiple lines and grow vertically. If the text cannot be split into + multiple lines, it will be larger than the specified \a textWidth. - \note This function will cause the layout of the text to be recalculated. + Setting the preferred text width to a negative number will cause the text to be unbounded. - \sa maximumSize(), size() + Use size() to get the actual size of the text. + + \note This function will cause the layout of the text to require recalculation. + + \sa textWidth(), size() */ -void QStaticText::setMaximumSize(const QSizeF &size) +void QStaticText::setTextWidth(qreal textWidth) { detach(); - data->maximumSize = size; - data->init(); + data->textWidth = textWidth; + data->invalidate(); } /*! - Returns the maximum size of the QStaticText. + Returns the preferred width for this QStaticText. - \sa setMaximumSize() + \sa setTextWidth() */ -QSizeF QStaticText::maximumSize() const +qreal QStaticText::textWidth() const { - return data->maximumSize; + return data->textWidth; } /*! Returns the size of the bounding rect for this QStaticText. - \sa maximumSize() + \sa textWidth() */ QSizeF QStaticText::size() const { + if (data->needsRelayout) + data->init(); return data->actualSize; } QStaticTextPrivate::QStaticTextPrivate() - : items(0), itemCount(0), glyphPool(0), positionPool(0), needsClipRect(false), - useBackendOptimizations(false), textFormat(Qt::AutoText) + : items(0), itemCount(0), glyphPool(0), positionPool(0), textWidth(-1.0), + needsRelayout(true), useBackendOptimizations(false), textFormat(Qt::AutoText) { } QStaticTextPrivate::QStaticTextPrivate(const QStaticTextPrivate &other) - : text(other.text), font(other.font), maximumSize(other.maximumSize), matrix(other.matrix), - items(0), itemCount(0), glyphPool(0), positionPool(0), needsClipRect(false), + : text(other.text), font(other.font), textWidth(other.textWidth), matrix(other.matrix), + items(0), itemCount(0), glyphPool(0), positionPool(0), needsRelayout(true), useBackendOptimizations(other.useBackendOptimizations), textFormat(other.textFormat) { } @@ -388,10 +402,17 @@ namespace { m_expectedItemCount(expectedItemCount), m_expectedGlyphCount(expectedGlyphCount), m_glyphPool(glyphPool), - m_positionPool(positionPool) + m_positionPool(positionPool), + m_dirtyPen(false) { } + virtual void updateState(const QPaintEngineState &newState) + { + if (newState.state() & QPaintEngine::DirtyPen) + m_dirtyPen = true; + } + virtual void drawTextItem(const QPointF &position, const QTextItem &textItem) { const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); @@ -412,7 +433,8 @@ namespace { currentItem->numGlyphs = ti.glyphs.numGlyphs; currentItem->glyphs = m_glyphPool; currentItem->glyphPositions = m_positionPool; - currentItem->color = state->pen().color(); + if (m_dirtyPen) + currentItem->color = state->pen().color(); QTransform matrix = state->transform(); matrix.translate(position.x(), position.y()); @@ -435,7 +457,6 @@ namespace { virtual bool begin(QPaintDevice *) { return true; } virtual bool end() { return true; } - virtual void updateState(const QPaintEngineState &) {} virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) {} virtual Type type() const { @@ -461,6 +482,8 @@ namespace { glyph_t *m_glyphPool; QFixedPoint *m_positionPool; + + bool m_dirtyPen; }; class DrawTextItemDevice: public QPaintDevice @@ -530,44 +553,59 @@ namespace { }; } -void QStaticTextPrivate::paintText(const QPointF &pos, QPainter *p) +void QStaticTextPrivate::paintText(const QPointF &topLeftPosition, QPainter *p) { bool preferRichText = textFormat == Qt::RichText || (textFormat == Qt::AutoText && Qt::mightBeRichText(text)); if (!preferRichText) { - if (maximumSize.isValid()) { - QRectF boundingRect; - p->drawText(QRectF(pos, maximumSize), Qt::TextWordWrap, text, &boundingRect); - - actualSize = boundingRect.size(); - needsClipRect = boundingRect.width() > maximumSize.width() - || boundingRect.height() > maximumSize.height(); - } else { - p->drawText(pos, text); - needsClipRect = false; - - QFontMetrics fm(font); - actualSize = fm.boundingRect(text).size(); + QTextLayout textLayout; + textLayout.setText(text); + textLayout.setFont(font); + + qreal leading = QFontMetricsF(font).leading(); + qreal height = -leading; + + textLayout.beginLayout(); + while (1) { + QTextLine line = textLayout.createLine(); + if (!line.isValid()) + break; + + if (textWidth >= 0.0) + line.setLineWidth(textWidth); + height += leading; + line.setPosition(QPointF(0.0, height)); + height += line.height(); } + textLayout.endLayout(); + + actualSize = textLayout.boundingRect().size(); + textLayout.draw(p, topLeftPosition); } else { QTextDocument document; + QColor color = p->pen().color(); + document.setDefaultStyleSheet(QString::fromLatin1("body { color: #%1%2%3 }") + .arg(QString::number(color.red(), 16), 2, QLatin1Char('0')) + .arg(QString::number(color.green(), 16), 2, QLatin1Char('0')) + .arg(QString::number(color.blue(), 16), 2, QLatin1Char('0'))); document.setDefaultFont(font); + document.setDocumentMargin(0.0); + if (textWidth >= 0.0) + document.setTextWidth(textWidth); #ifndef QT_NO_TEXTHTMLPARSER document.setHtml(text); #else document.setPlainText(text); #endif - QRectF rect = maximumSize.isValid() ? QRectF(pos, maximumSize) : QRectF(); + document.adjustSize(); p->save(); - p->translate(pos); - document.drawContents(p, rect); + p->translate(topLeftPosition); + document.drawContents(p); p->restore(); + actualSize = document.size(); - needsClipRect = maximumSize.isValid() - && (actualSize.width() > maximumSize.width() - || actualSize.height() > maximumSize.height()); } } @@ -614,6 +652,7 @@ void QStaticTextPrivate::init() paintText(QPointF(0, 0), &painter); } + needsRelayout = false; } QT_END_NAMESPACE diff --git a/src/gui/text/qstatictext.h b/src/gui/text/qstatictext.h index 00d42e0..f3bef93 100644 --- a/src/gui/text/qstatictext.h +++ b/src/gui/text/qstatictext.h @@ -66,7 +66,7 @@ public: }; QStaticText(); - QStaticText(const QString &text, const QSizeF &maximumSize = QSizeF()); + QStaticText(const QString &text); QStaticText(const QStaticText &other); ~QStaticText(); @@ -76,12 +76,12 @@ public: void setTextFormat(Qt::TextFormat textFormat); Qt::TextFormat textFormat() const; - void setMaximumSize(const QSizeF &maximumSize); - QSizeF maximumSize() const; + void setTextWidth(qreal textWidth); + qreal textWidth() const; QSizeF size() const; - void prepare(const QTransform &matrix, const QFont &font); + void prepare(const QTransform &matrix = QTransform(), const QFont &font = QFont()); void setPerformanceHint(PerformanceHint performanceHint); PerformanceHint performanceHint() const; diff --git a/src/gui/text/qstatictext_p.h b/src/gui/text/qstatictext_p.h index e758244..f017ed1 100644 --- a/src/gui/text/qstatictext_p.h +++ b/src/gui/text/qstatictext_p.h @@ -118,11 +118,16 @@ public: void init(); void paintText(const QPointF &pos, QPainter *p); + void invalidate() + { + needsRelayout = true; + } + QAtomicInt ref; // 4 bytes per text QString text; // 4 bytes per text QFont font; // 8 bytes per text - QSizeF maximumSize; // 16 bytes per text + qreal textWidth; // 8 bytes per text QSizeF actualSize; // 16 bytes per text QPointF position; // 16 bytes per text @@ -132,11 +137,11 @@ public: glyph_t *glyphPool; // 4 bytes per text QFixedPoint *positionPool; // 4 bytes per text - unsigned char needsClipRect : 1; // 1 byte per text - unsigned char useBackendOptimizations : 1; + unsigned char needsRelayout : 1; + unsigned char useBackendOptimizations : 1; // 1 byte per text unsigned char textFormat : 2; // ================ - // 171 bytes per text + // 163 bytes per text static QStaticTextPrivate *get(const QStaticText *q); }; diff --git a/src/gui/widgets/qlabel.cpp b/src/gui/widgets/qlabel.cpp index b81f04f..bdbd0b0 100644 --- a/src/gui/widgets/qlabel.cpp +++ b/src/gui/widgets/qlabel.cpp @@ -53,6 +53,7 @@ #include <qurl.h> #include "qlabel_p.h" #include "private/qstylesheetstyle_p.h" +#include <qmath.h> QT_BEGIN_NAMESPACE @@ -661,7 +662,9 @@ QSize QLabelPrivate::sizeForWidth(int w) const } else { control->setTextWidth(-1); } - br = QRect(QPoint(0, 0), control->size().toSize()); + + QSizeF controlSize = control->size(); + br = QRect(QPoint(0, 0), QSize(qCeil(controlSize.width()), qCeil(controlSize.height()))); // restore state control->setTextWidth(oldTextWidth); diff --git a/src/gui/widgets/qlinecontrol_p.h b/src/gui/widgets/qlinecontrol_p.h index dd82581..5da1831 100644 --- a/src/gui/widgets/qlinecontrol_p.h +++ b/src/gui/widgets/qlinecontrol_p.h @@ -94,136 +94,207 @@ public: delete [] m_maskData; } - int nextMaskBlank(int pos); - int prevMaskBlank(int pos); + int nextMaskBlank(int pos) + { + int c = findInMask(pos, true, false); + m_separator |= (c != pos); + return (c != -1 ? c : m_maxLength); + } + + int prevMaskBlank(int pos) + { + int c = findInMask(pos, false, false); + m_separator |= (c != pos); + return (c != -1 ? c : 0); + } + + bool isUndoAvailable() const { return !m_readOnly && m_undoState; } + bool isRedoAvailable() const { return !m_readOnly && m_undoState < (int)m_history.size(); } + void clearUndo() { m_history.clear(); m_modifiedState = m_undoState = 0; } - bool isUndoAvailable() const; - bool isRedoAvailable() const; - void clearUndo(); - bool isModified() const; - void setModified(bool modified); + bool isModified() const { return m_modifiedState != m_undoState; } + void setModified(bool modified) { m_modifiedState = modified ? -1 : m_undoState; } - bool allSelected() const; - bool hasSelectedText() const; + bool allSelected() const { return !m_text.isEmpty() && m_selstart == 0 && m_selend == (int)m_text.length(); } + bool hasSelectedText() const { return !m_text.isEmpty() && m_selend > m_selstart; } - int width() const; - int height() const; - int ascent() const; - qreal naturalTextWidth() const; + int width() const { return qRound(m_textLayout.lineAt(0).width()) + 1; } + int height() const { return qRound(m_textLayout.lineAt(0).height()) + 1; } + int ascent() const { return m_ascent; } + qreal naturalTextWidth() const { return m_textLayout.lineAt(0).naturalTextWidth(); } void setSelection(int start, int length); - QString selectedText() const; - QString textBeforeSelection() const; - QString textAfterSelection() const; + inline QString selectedText() const { return hasSelectedText() ? m_text.mid(m_selstart, m_selend - m_selstart) : QString(); } + QString textBeforeSelection() const { return hasSelectedText() ? m_text.left(m_selstart) : QString(); } + QString textAfterSelection() const { return hasSelectedText() ? m_text.mid(m_selend) : QString(); } - int selectionStart() const; - int selectionEnd() const; - bool inSelection(int x) const; + int selectionStart() const { return hasSelectedText() ? m_selstart : -1; } + int selectionEnd() const { return hasSelectedText() ? m_selend : -1; } + bool inSelection(int x) const + { + if (m_selstart >= m_selend) + return false; + int pos = xToPos(x, QTextLine::CursorOnCharacter); + return pos >= m_selstart && pos < m_selend; + } - void removeSelection(); + void removeSelection() + { + int priorState = m_undoState; + removeSelectedText(); + finishChange(priorState); + } - int start() const; - int end() const; + int start() const { return 0; } + int end() const { return m_text.length(); } #ifndef QT_NO_CLIPBOARD void copy(QClipboard::Mode mode = QClipboard::Clipboard) const; void paste(QClipboard::Mode mode = QClipboard::Clipboard); #endif - int cursor() const; - int preeditCursor() const; + int cursor() const{ return m_cursor; } + int preeditCursor() const { return m_preeditCursor; } + + int cursorWidth() const { return m_cursorWidth; } + void setCursorWidth(int value) { m_cursorWidth = value; } - int cursorWidth() const; - void setCursorWidth(int value); void moveCursor(int pos, bool mark = false); - void cursorForward(bool mark, int steps); - void cursorWordForward(bool mark); - void cursorWordBackward(bool mark); - void home(bool mark); - void end(bool mark); + void cursorForward(bool mark, int steps) + { + int c = m_cursor; + if (steps > 0) { + while (steps--) + c = m_textLayout.nextCursorPosition(c); + } else if (steps < 0) { + while (steps++) + c = m_textLayout.previousCursorPosition(c); + } + moveCursor(c, mark); + } + + void cursorWordForward(bool mark) { moveCursor(m_textLayout.nextCursorPosition(m_cursor, QTextLayout::SkipWords), mark); } + void cursorWordBackward(bool mark) { moveCursor(m_textLayout.previousCursorPosition(m_cursor, QTextLayout::SkipWords), mark); } + + void home(bool mark) { moveCursor(0, mark); } + void end(bool mark) { moveCursor(text().length(), mark); } int xToPos(int x, QTextLine::CursorPosition = QTextLine::CursorBetweenCharacters) const; QRect cursorRect() const; - qreal cursorToX(int cursor) const; - qreal cursorToX() const; - - bool isReadOnly() const; - void setReadOnly(bool enable); + qreal cursorToX(int cursor) const { return m_textLayout.lineAt(0).cursorToX(cursor); } + qreal cursorToX() const + { + int cursor = m_cursor; + if (m_preeditCursor != -1) + cursor += m_preeditCursor; + return cursorToX(cursor); + } - QString text() const; - void setText(const QString &txt); + bool isReadOnly() const { return m_readOnly; } + void setReadOnly(bool enable) { m_readOnly = enable; } - QString displayText() const; + QString text() const + { + QString res = m_maskData ? stripString(m_text) : m_text; + return (res.isNull() ? QString::fromLatin1("") : res); + } + void setText(const QString &txt) { internalSetText(txt, -1, false); } + QString displayText() const { return m_textLayout.text(); } void backspace(); void del(); - void deselect(); - void selectAll(); + void deselect() { internalDeselect(); finishChange(); } + void selectAll() { m_selstart = m_selend = m_cursor = 0; moveCursor(m_text.length(), true); } + void insert(const QString &); void clear(); - void undo(); - void redo(); + void undo() { internalUndo(); finishChange(-1, true); } + void redo() { internalRedo(); finishChange(); } void selectWordAtPos(int); - uint echoMode() const; - void setEchoMode(uint mode); + uint echoMode() const { return m_echoMode; } + void setEchoMode(uint mode) + { + m_echoMode = mode; + m_passwordEchoEditing = false; + updateDisplayText(); + } - void setMaxLength(int maxLength); - int maxLength() const; + int maxLength() const { return m_maxLength; } + void setMaxLength(int maxLength) + { + if (m_maskData) + return; + m_maxLength = maxLength; + setText(m_text); + } #ifndef QT_NO_VALIDATOR - const QValidator *validator() const; - void setValidator(const QValidator *); + const QValidator *validator() const { return m_validator; } + void setValidator(const QValidator *v) { m_validator = const_cast<QValidator*>(v); } #endif #ifndef QT_NO_COMPLETER - QCompleter *completer() const; - void setCompleter(const QCompleter*); + QCompleter *completer() const { return m_completer; } + /* Note that you must set the widget for the completer seperately */ + void setCompleter(const QCompleter *c) { m_completer = const_cast<QCompleter*>(c); } void complete(int key); #endif - void setCursorPosition(int pos); - int cursorPosition() const; + int cursorPosition() const { return m_cursor; } + void setCursorPosition(int pos) { if (pos <= m_text.length()) moveCursor(qMax(0, pos)); } - bool hasAcceptableInput() const; + bool hasAcceptableInput() const { return hasAcceptableInput(m_text); } bool fixup(); - QString inputMask() const; - void setInputMask(const QString &mask); + QString inputMask() const { return m_maskData ? m_inputMask + QLatin1Char(';') + m_blank : QString(); } + void setInputMask(const QString &mask) + { + parseInputMask(mask); + if (m_maskData) + moveCursor(nextMaskBlank(0)); + } // input methods #ifndef QT_NO_IM - bool composeMode() const; - void setPreeditArea(int cursor, const QString &text); + bool composeMode() const { return !m_textLayout.preeditAreaText().isEmpty(); } + void setPreeditArea(int cursor, const QString &text) { m_textLayout.setPreeditArea(cursor, text); } #endif - QString preeditAreaText() const; + QString preeditAreaText() const { return m_textLayout.preeditAreaText(); } void updatePasswordEchoEditing(bool editing); - bool passwordEchoEditing() const; + bool passwordEchoEditing() const { return m_passwordEchoEditing; } - QChar passwordCharacter() const; - void setPasswordCharacter(const QChar &character); + QChar passwordCharacter() const { return m_passwordCharacter; } + void setPasswordCharacter(const QChar &character) { m_passwordCharacter = character; updateDisplayText(); } - Qt::LayoutDirection layoutDirection() const; - void setLayoutDirection(Qt::LayoutDirection direction); - void setFont(const QFont &font); + Qt::LayoutDirection layoutDirection() const { return m_layoutDirection; } + void setLayoutDirection(Qt::LayoutDirection direction) + { + if (direction != m_layoutDirection) { + m_layoutDirection = direction; + updateDisplayText(); + } + } + + void setFont(const QFont &font) { m_textLayout.setFont(font); updateDisplayText(); } void processInputMethodEvent(QInputMethodEvent *event); void processMouseEvent(QMouseEvent* ev); void processKeyEvent(QKeyEvent* ev); - int cursorBlinkPeriod() const; + int cursorBlinkPeriod() const { return m_blinkPeriod; } void setCursorBlinkPeriod(int msec); - QString cancelText() const; - void setCancelText(const QString &text); + QString cancelText() const { return m_cancelText; } + void setCancelText(const QString &text) { m_cancelText = text; } - const QPalette &palette() const; - void setPalette(const QPalette &); + const QPalette &palette() const { return m_palette; } + void setPalette(const QPalette &p) { m_palette = p; } enum DrawFlags { DrawText = 0x01, @@ -363,406 +434,6 @@ private Q_SLOTS: }; -inline int QLineControl::nextMaskBlank(int pos) -{ - int c = findInMask(pos, true, false); - m_separator |= (c != pos); - return (c != -1 ? c : m_maxLength); -} - -inline int QLineControl::prevMaskBlank(int pos) -{ - int c = findInMask(pos, false, false); - m_separator |= (c != pos); - return (c != -1 ? c : 0); -} - -inline bool QLineControl::isUndoAvailable() const -{ - return !m_readOnly && m_undoState; -} - -inline bool QLineControl::isRedoAvailable() const -{ - return !m_readOnly && m_undoState < (int)m_history.size(); -} - -inline void QLineControl::clearUndo() -{ - m_history.clear(); - m_modifiedState = m_undoState = 0; -} - -inline bool QLineControl::isModified() const -{ - return m_modifiedState != m_undoState; -} - -inline void QLineControl::setModified(bool modified) -{ - m_modifiedState = modified ? -1 : m_undoState; -} - -inline bool QLineControl::allSelected() const -{ - return !m_text.isEmpty() && m_selstart == 0 && m_selend == (int)m_text.length(); -} - -inline bool QLineControl::hasSelectedText() const -{ - return !m_text.isEmpty() && m_selend > m_selstart; -} - -inline int QLineControl::width() const -{ - return qRound(m_textLayout.lineAt(0).width()) + 1; -} - -inline qreal QLineControl::naturalTextWidth() const -{ - return m_textLayout.lineAt(0).naturalTextWidth(); -} - -inline int QLineControl::height() const -{ - return qRound(m_textLayout.lineAt(0).height()) + 1; -} - -inline int QLineControl::ascent() const -{ - return m_ascent; -} - -inline QString QLineControl::selectedText() const -{ - if (hasSelectedText()) - return m_text.mid(m_selstart, m_selend - m_selstart); - return QString(); -} - -inline QString QLineControl::textBeforeSelection() const -{ - if (hasSelectedText()) - return m_text.left(m_selstart); - return QString(); -} - -inline QString QLineControl::textAfterSelection() const -{ - if (hasSelectedText()) - return m_text.mid(m_selend); - return QString(); -} - -inline int QLineControl::selectionStart() const -{ - return hasSelectedText() ? m_selstart : -1; -} - -inline int QLineControl::selectionEnd() const -{ - return hasSelectedText() ? m_selend : -1; -} - -inline int QLineControl::start() const -{ - return 0; -} - -inline int QLineControl::end() const -{ - return m_text.length(); -} - -inline void QLineControl::removeSelection() -{ - int priorState = m_undoState; - removeSelectedText(); - finishChange(priorState); -} - -inline bool QLineControl::inSelection(int x) const -{ - if (m_selstart >= m_selend) - return false; - int pos = xToPos(x, QTextLine::CursorOnCharacter); - return pos >= m_selstart && pos < m_selend; -} - -inline int QLineControl::cursor() const -{ - return m_cursor; -} - -inline int QLineControl::preeditCursor() const -{ - return m_preeditCursor; -} - -inline int QLineControl::cursorWidth() const -{ - return m_cursorWidth; -} - -inline void QLineControl::setCursorWidth(int value) -{ - m_cursorWidth = value; -} - -inline void QLineControl::cursorForward(bool mark, int steps) -{ - int c = m_cursor; - if (steps > 0) { - while (steps--) - c = m_textLayout.nextCursorPosition(c); - } else if (steps < 0) { - while (steps++) - c = m_textLayout.previousCursorPosition(c); - } - moveCursor(c, mark); -} - -inline void QLineControl::cursorWordForward(bool mark) -{ - moveCursor(m_textLayout.nextCursorPosition(m_cursor, QTextLayout::SkipWords), mark); -} - -inline void QLineControl::home(bool mark) -{ - moveCursor(0, mark); -} - -inline void QLineControl::end(bool mark) -{ - moveCursor(text().length(), mark); -} - -inline void QLineControl::cursorWordBackward(bool mark) -{ - moveCursor(m_textLayout.previousCursorPosition(m_cursor, QTextLayout::SkipWords), mark); -} - -inline qreal QLineControl::cursorToX(int cursor) const -{ - return m_textLayout.lineAt(0).cursorToX(cursor); -} - -inline qreal QLineControl::cursorToX() const -{ - int cursor = m_cursor; - if (m_preeditCursor != -1) - cursor += m_preeditCursor; - return cursorToX(cursor); -} - -inline bool QLineControl::isReadOnly() const -{ - return m_readOnly; -} - -inline void QLineControl::setReadOnly(bool enable) -{ - m_readOnly = enable; -} - -inline QString QLineControl::text() const -{ - QString res = m_maskData ? stripString(m_text) : m_text; - return (res.isNull() ? QString::fromLatin1("") : res); -} - -inline void QLineControl::setText(const QString &txt) -{ - internalSetText(txt, -1, false); -} - -inline QString QLineControl::displayText() const -{ - return m_textLayout.text(); -} - -inline void QLineControl::deselect() -{ - internalDeselect(); - finishChange(); -} - -inline void QLineControl::selectAll() -{ - m_selstart = m_selend = m_cursor = 0; - moveCursor(m_text.length(), true); -} - -inline void QLineControl::undo() -{ - internalUndo(); - finishChange(-1, true); -} - -inline void QLineControl::redo() -{ - internalRedo(); - finishChange(); -} - -inline uint QLineControl::echoMode() const -{ - return m_echoMode; -} - -inline void QLineControl::setEchoMode(uint mode) -{ - m_echoMode = mode; - m_passwordEchoEditing = false; - updateDisplayText(); -} - -inline void QLineControl::setMaxLength(int maxLength) -{ - if (m_maskData) - return; - m_maxLength = maxLength; - setText(m_text); -} - -inline int QLineControl::maxLength() const -{ - return m_maxLength; -} - -#ifndef QT_NO_VALIDATOR -inline const QValidator *QLineControl::validator() const -{ - return m_validator; -} - -inline void QLineControl::setValidator(const QValidator *v) -{ - m_validator = const_cast<QValidator*>(v); -} -#endif - -#ifndef QT_NO_COMPLETER -inline QCompleter *QLineControl::completer() const -{ - return m_completer; -} - -/* Note that you must set the widget for the completer seperately */ -inline void QLineControl::setCompleter(const QCompleter* c) -{ - m_completer = const_cast<QCompleter*>(c); -} -#endif - -inline void QLineControl::setCursorPosition(int pos) -{ - if (pos < 0) - pos = 0; - if (pos <= m_text.length()) - moveCursor(pos); -} - -inline int QLineControl::cursorPosition() const -{ - return m_cursor; -} - -inline bool QLineControl::hasAcceptableInput() const -{ - return hasAcceptableInput(m_text); -} - -inline QString QLineControl::inputMask() const -{ - return m_maskData ? m_inputMask + QLatin1Char(';') + m_blank : QString(); -} - -inline void QLineControl::setInputMask(const QString &mask) -{ - parseInputMask(mask); - if (m_maskData) - moveCursor(nextMaskBlank(0)); -} - -// input methods -#ifndef QT_NO_IM -inline bool QLineControl::composeMode() const -{ - return !m_textLayout.preeditAreaText().isEmpty(); -} - -inline void QLineControl::setPreeditArea(int cursor, const QString &text) -{ - m_textLayout.setPreeditArea(cursor, text); -} -#endif - -inline QString QLineControl::preeditAreaText() const -{ - return m_textLayout.preeditAreaText(); -} - -inline bool QLineControl::passwordEchoEditing() const -{ - return m_passwordEchoEditing; -} - -inline QChar QLineControl::passwordCharacter() const -{ - return m_passwordCharacter; -} - -inline void QLineControl::setPasswordCharacter(const QChar &character) -{ - m_passwordCharacter = character; - updateDisplayText(); -} - -inline Qt::LayoutDirection QLineControl::layoutDirection() const -{ - return m_layoutDirection; -} - -inline void QLineControl::setLayoutDirection(Qt::LayoutDirection direction) -{ - if (direction != m_layoutDirection) { - m_layoutDirection = direction; - updateDisplayText(); - } -} - -inline void QLineControl::setFont(const QFont &font) -{ - m_textLayout.setFont(font); - updateDisplayText(); -} - -inline int QLineControl::cursorBlinkPeriod() const -{ - return m_blinkPeriod; -} - -inline QString QLineControl::cancelText() const -{ - return m_cancelText; -} - -inline void QLineControl::setCancelText(const QString &text) -{ - m_cancelText = text; -} - -inline const QPalette & QLineControl::palette() const -{ - return m_palette; -} - -inline void QLineControl::setPalette(const QPalette &p) -{ - m_palette = p; -} - QT_END_NAMESPACE QT_END_HEADER diff --git a/src/multimedia/audio/qaudiooutput_win32_p.cpp b/src/multimedia/audio/qaudiooutput_win32_p.cpp index ab8da53..4bcbd09 100644 --- a/src/multimedia/audio/qaudiooutput_win32_p.cpp +++ b/src/multimedia/audio/qaudiooutput_win32_p.cpp @@ -88,7 +88,7 @@ QAudioOutputPrivate::~QAudioOutputPrivate() DeleteCriticalSection(&waveOutCriticalSection); } -void QT_WIN_CALLBACK QAudioOutputPrivate::waveOutProc( HWAVEOUT hWaveOut, UINT uMsg, +void CALLBACK QAudioOutputPrivate::waveOutProc( HWAVEOUT hWaveOut, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ) { Q_UNUSED(dwParam1) diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 4010710..050fb1b 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -146,7 +146,7 @@ static void locking_function(int mode, int lockNumber, const char *, int) } static unsigned long id_function() { - return (unsigned long)QThread::currentThreadId(); + return (quintptr)QThread::currentThreadId(); } } // extern "C" diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro index ff42a49..9473343 100644 --- a/src/opengl/opengl.pro +++ b/src/opengl/opengl.pro @@ -116,6 +116,7 @@ mac { LIBS_PRIVATE += -framework AppKit -framework Carbon } win32:!wince*: { + DEFINES += QT_NO_EGL SOURCES += qgl_win.cpp \ qglpixelbuffer_win.cpp } diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 7aba25a..e56b149 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -2444,7 +2444,7 @@ QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, if (pd->classId() == QPixmapData::X11Class && pd->pixelType() == QPixmapData::PixmapType && xinfo && xinfo->screen() == pixmap.x11Info().screen()) { - texture = bindTextureFromNativePixmap(pd, key, options); + texture = bindTextureFromNativePixmap(const_cast<QPixmap*>(&pixmap), key, options); if (texture) { texture->options |= QGLContext::MemoryManagedBindOption; texture->boundPixmap = pd; diff --git a/src/opengl/qgl_egl_p.h b/src/opengl/qgl_egl_p.h index 43793cd..85d7f32 100644 --- a/src/opengl/qgl_egl_p.h +++ b/src/opengl/qgl_egl_p.h @@ -53,6 +53,7 @@ // We mean it. // +#include <QtGui/private/qegl_p.h> #include <QtGui/private/qeglcontext_p.h> #include <QtGui/private/qeglproperties_p.h> diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index dc1e26e..1f28b08 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -361,7 +361,7 @@ public: quint32 gpm; int screen; QHash<QPixmapData*, QPixmap> boundPixmaps; - QGLTexture *bindTextureFromNativePixmap(QPixmapData*, const qint64 key, + QGLTexture *bindTextureFromNativePixmap(QPixmap*, const qint64 key, QGLContext::BindOptions options); static void destroyGlSurfaceForPixmap(QPixmapData*); static void unbindPixmapFromTexture(QPixmapData*); diff --git a/src/opengl/qgl_x11.cpp b/src/opengl/qgl_x11.cpp index 4fa1467..e1a202f 100644 --- a/src/opengl/qgl_x11.cpp +++ b/src/opengl/qgl_x11.cpp @@ -1671,7 +1671,7 @@ static bool qt_resolveTextureFromPixmap(QPaintDevice *paintDevice) #endif //defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX) -QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData *pmd, const qint64 key, +QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pixmap, const qint64 key, QGLContext::BindOptions options) { #if !defined(GLX_VERSION_1_3) || defined(Q_OS_HPUX) @@ -1679,12 +1679,12 @@ QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData *pmd, con #else Q_Q(QGLContext); - Q_ASSERT(pmd->classId() == QPixmapData::X11Class); + QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pixmap->data_ptr().data()); + Q_ASSERT(pixmapData->classId() == QPixmapData::X11Class); if (!qt_resolveTextureFromPixmap(paintDevice)) return 0; - QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd); const QX11Info &x11Info = pixmapData->xinfo; // Store the configs (Can be static because configs aren't dependent on current context) diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp index 0954e69..af0100b 100644 --- a/src/opengl/qgl_x11egl.cpp +++ b/src/opengl/qgl_x11egl.cpp @@ -354,7 +354,7 @@ void QGLWidgetPrivate::recreateEglSurface(bool force) } -QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData* pd, const qint64 key, +QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pixmap, const qint64 key, QGLContext::BindOptions options) { Q_Q(QGLContext); @@ -363,10 +363,33 @@ QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData* pd, cons if (!(options & QGLContext::CanFlipNativePixmapBindOption)) return 0; - Q_ASSERT(pd->classId() == QPixmapData::X11Class); static bool checkedForTFP = false; static bool haveTFP = false; + static bool checkedForEglImageTFP = false; + static bool haveEglImageTFP = false; + + + if (!checkedForEglImageTFP) { + checkedForEglImageTFP = true; + + // We need to be able to create an EGLImage from a native pixmap, which was split + // into a seperate EGL extension, EGL_KHR_image_pixmap. It is possible to have + // eglCreateImageKHR & eglDestroyImageKHR without support for pixmaps, so we must + // check we have the EGLImage from pixmap functionality. + if (QEgl::hasExtension("EGL_KHR_image") || QEgl::hasExtension("EGL_KHR_image_pixmap")) { + Q_ASSERT(eglCreateImageKHR); + Q_ASSERT(eglDestroyImageKHR); + + // Being able to create an EGLImage from a native pixmap is also pretty useless + // without the ability to bind that EGLImage as a texture, which is provided by + // the GL_OES_EGL_image extension, which we try to resolve here: + haveEglImageTFP = qt_resolve_eglimage_gl_extensions(q); + + if (haveEglImageTFP) + qDebug("Found EGL_KHR_image_pixmap & GL_OES_EGL_image extensions (preferred method)!"); + } + } if (!checkedForTFP) { // Check for texture_from_pixmap egl extension @@ -379,61 +402,111 @@ QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData* pd, cons } } - if (!haveTFP) + if (!haveTFP && !haveEglImageTFP) return 0; - QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pd); + QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pixmap->data_ptr().data()); + Q_ASSERT(pixmapData->classId() == QPixmapData::X11Class); bool hasAlpha = pixmapData->hasAlphaChannel(); + bool pixmapHasValidSurface = false; + bool textureIsBound = false; + GLuint textureId; + glGenTextures(1, &textureId); + glBindTexture(GL_TEXTURE_2D, textureId); - // Check to see if the surface is still valid - if (pixmapData->gl_surface && - hasAlpha != (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha)) + if (haveTFP && pixmapData->gl_surface && + hasAlpha == (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha)) { - // Surface is invalid! - destroyGlSurfaceForPixmap(pixmapData); + pixmapHasValidSurface = true; + } + + // If we already have a valid EGL surface for the pixmap, we should use it + if (pixmapHasValidSurface) { + EGLBoolean success; + success = eglBindTexImage(QEgl::display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER); + if (success == EGL_FALSE) { + qWarning() << "eglBindTexImage() failed:" << QEgl::errorString(); + eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface); + pixmapData->gl_surface = (void*)EGL_NO_SURFACE; + } else + textureIsBound = true; } - if (pixmapData->gl_surface == 0) { - EGLConfig config = QEgl::defaultConfig(QInternal::Pixmap, - QEgl::OpenGL, - hasAlpha ? QEgl::Translucent : QEgl::NoOptions); + // If the pixmap doesn't already have a valid surface, try binding it via EGLImage + // first, as going through EGLImage should be faster and better supported: + if (!textureIsBound && haveEglImageTFP) { + Q_ASSERT(eglCreateImageKHR); - QPixmap tmpPixmap(pixmapData); //### - pixmapData->gl_surface = (void*)QEgl::createSurface(&tmpPixmap, config); - if (pixmapData->gl_surface == (void*)EGL_NO_SURFACE) { - haveTFP = false; - return 0; - } + EGLImageKHR eglImage; + + EGLint attribs[] = { + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_NONE + }; + eglImage = eglCreateImageKHR(QEgl::display(), EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, + (EGLClientBuffer)QEgl::nativePixmap(pixmap), attribs); + + QGLContext* ctx = q; + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage); + + GLint err = glGetError(); + if (err == GL_NO_ERROR) + textureIsBound = true; + + // Once the egl image is bound, the texture becomes a new sibling image and we can safely + // destroy the EGLImage we created for the pixmap: + if (eglImage != EGL_NO_IMAGE_KHR) + eglDestroyImageKHR(QEgl::display(), eglImage); } - Q_ASSERT(pixmapData->gl_surface); + if (!textureIsBound && haveTFP) { + // Check to see if the surface is still valid + if (pixmapData->gl_surface && + hasAlpha != (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha)) + { + // Surface is invalid! + destroyGlSurfaceForPixmap(pixmapData); + } - GLuint textureId; - glGenTextures(1, &textureId); - glBindTexture(GL_TEXTURE_2D, textureId); + if (pixmapData->gl_surface == 0) { + EGLConfig config = QEgl::defaultConfig(QInternal::Pixmap, + QEgl::OpenGL, + hasAlpha ? QEgl::Translucent : QEgl::NoOptions); - // bind the egl pixmap surface to a texture - EGLBoolean success; - success = eglBindTexImage(eglContext->display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER); - if (success == EGL_FALSE) { - qWarning() << "eglBindTexImage() failed:" << QEgl::errorString(); - eglDestroySurface(eglContext->display(), (EGLSurface)pixmapData->gl_surface); - pixmapData->gl_surface = (void*)EGL_NO_SURFACE; - haveTFP = false; - return 0; + pixmapData->gl_surface = (void*)QEgl::createSurface(pixmap, config); + if (pixmapData->gl_surface == (void*)EGL_NO_SURFACE) + return false; + } + + EGLBoolean success; + success = eglBindTexImage(QEgl::display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER); + if (success == EGL_FALSE) { + qWarning() << "eglBindTexImage() failed:" << QEgl::errorString(); + eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface); + pixmapData->gl_surface = (void*)EGL_NO_SURFACE; + haveTFP = false; // If TFP isn't working, disable it's use + } else + textureIsBound = true; } - QGLTexture *texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, options); - pixmapData->flags |= QX11PixmapData::InvertedWhenBoundToTexture; + QGLTexture *texture = 0; - // We assume the cost of bound pixmaps is zero - QGLTextureCache::instance()->insert(q, key, texture, 0); + if (textureIsBound) { + texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, options); + pixmapData->flags |= QX11PixmapData::InvertedWhenBoundToTexture; + + // We assume the cost of bound pixmaps is zero + QGLTextureCache::instance()->insert(q, key, texture, 0); + + glBindTexture(GL_TEXTURE_2D, textureId); + } else + glDeleteTextures(1, &textureId); - glBindTexture(GL_TEXTURE_2D, textureId); return texture; } + void QGLContextPrivate::destroyGlSurfaceForPixmap(QPixmapData* pmd) { Q_ASSERT(pmd->classId() == QPixmapData::X11Class); diff --git a/src/opengl/qglextensions.cpp b/src/opengl/qglextensions.cpp index 02d5501..ef3c4cd 100644 --- a/src/opengl/qglextensions.cpp +++ b/src/opengl/qglextensions.cpp @@ -222,6 +222,17 @@ bool qt_resolve_buffer_extensions(QGLContext *ctx) #endif } +#ifndef QT_NO_EGL +bool qt_resolve_eglimage_gl_extensions(QGLContext *ctx) +{ + if (glEGLImageTargetTexture2DOES || glEGLImageTargetRenderbufferStorageOES) + return true; + glEGLImageTargetTexture2DOES = (_glEGLImageTargetTexture2DOES) ctx->getProcAddress(QLatin1String("glEGLImageTargetTexture2DOES")); + glEGLImageTargetRenderbufferStorageOES = (_glEGLImageTargetRenderbufferStorageOES) ctx->getProcAddress(QLatin1String("glEGLImageTargetRenderbufferStorageOES")); + return glEGLImageTargetTexture2DOES && glEGLImageTargetRenderbufferStorageOES; +} +#endif + bool qt_resolve_glsl_extensions(QGLContext *ctx) { // Geometry shaders are optional... diff --git a/src/opengl/qglextensions_p.h b/src/opengl/qglextensions_p.h index f6926f3..7597b33 100644 --- a/src/opengl/qglextensions_p.h +++ b/src/opengl/qglextensions_p.h @@ -68,6 +68,11 @@ # define APIENTRYP * #endif +#ifndef QT_NO_EGL +// Needed for EGLImageKHR definition: +#include <QtGui/private/qegl_p.h> +#endif + #include <QtCore/qglobal.h> #ifndef GL_ARB_vertex_buffer_object @@ -210,6 +215,14 @@ typedef void (APIENTRY *_glFramebufferTextureFaceEXT)(GLenum target, GLenum atta typedef void (APIENTRY *_glCompressedTexImage2DARB) (GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *); +#ifndef QT_NO_EGL +// OES_EGL_image +// Note: We define these to take EGLImage whereas spec says they take a new GLeglImageOES +// type, which the EGL image should be cast to. +typedef void (APIENTRY *_glEGLImageTargetTexture2DOES) (GLenum, EGLImageKHR); +typedef void (APIENTRY *_glEGLImageTargetRenderbufferStorageOES) (GLenum, EGLImageKHR); +#endif + QT_BEGIN_NAMESPACE struct QGLExtensionFuncs @@ -327,6 +340,12 @@ struct QGLExtensionFuncs // Texture compression qt_glCompressedTexImage2DARB = 0; #endif + +#ifndef QT_NO_EGL + // OES_EGL_image + qt_glEGLImageTargetTexture2DOES = 0; + qt_glEGLImageTargetRenderbufferStorageOES = 0; +#endif } @@ -447,6 +466,13 @@ struct QGLExtensionFuncs // Texture compression _glCompressedTexImage2DARB qt_glCompressedTexImage2DARB; #endif + +#ifndef QT_NO_EGL + // OES_EGL_image + _glEGLImageTargetTexture2DOES qt_glEGLImageTargetTexture2DOES; + _glEGLImageTargetRenderbufferStorageOES qt_glEGLImageTargetRenderbufferStorageOES; +#endif + }; @@ -839,6 +865,12 @@ struct QGLExtensionFuncs #define glCompressedTexImage2D QGLContextPrivate::extensionFuncs(ctx).qt_glCompressedTexImage2DARB #endif +#ifndef QT_NO_EGL +// OES_EGL_image +#define glEGLImageTargetTexture2DOES QGLContextPrivate::extensionFuncs(ctx).qt_glEGLImageTargetTexture2DOES +#define glEGLImageTargetRenderbufferStorageOES QGLContextPrivate::extensionFuncs(ctx).qt_glEGLImageTargetRenderbufferStorageOES +#endif + extern bool qt_resolve_framebufferobject_extensions(QGLContext *ctx); bool qt_resolve_buffer_extensions(QGLContext *ctx); @@ -849,6 +881,10 @@ bool qt_resolve_frag_program_extensions(QGLContext *ctx); bool qt_resolve_glsl_extensions(QGLContext *ctx); +#ifndef QT_NO_EGL +bool qt_resolve_eglimage_gl_extensions(QGLContext *ctx); +#endif + QT_END_NAMESPACE #endif // QGL_EXTENSIONS_P_H diff --git a/src/openvg/qpixmapdata_vg.cpp b/src/openvg/qpixmapdata_vg.cpp index ab8f26d..5c4f41b 100644 --- a/src/openvg/qpixmapdata_vg.cpp +++ b/src/openvg/qpixmapdata_vg.cpp @@ -42,6 +42,7 @@ #include "qpixmapdata_vg_p.h" #include "qpaintengine_vg_p.h" #include <QtGui/private/qdrawhelper_p.h> +#include <QtGui/private/qegl_p.h> #include "qvg_p.h" #include "qvgimagepool_p.h" @@ -51,8 +52,6 @@ #endif #ifdef QT_SYMBIAN_SUPPORTS_SGIMAGE #include <sgresource/sgimage.h> -typedef EGLImageKHR (*pfnEglCreateImageKHR)(EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, EGLint*); -typedef EGLBoolean (*pfnEglDestroyImageKHR)(EGLDisplay, EGLImageKHR); typedef VGImage (*pfnVgCreateEGLImageTargetKHR)(VGeglImageKHR); #endif // QT_SYMBIAN_SUPPORTS_SGIMAGE @@ -488,11 +487,9 @@ void QVGPixmapData::fromNativeType(void* pixmap, NativeType type) return; } - pfnEglCreateImageKHR eglCreateImageKHR = (pfnEglCreateImageKHR) eglGetProcAddress("eglCreateImageKHR"); - pfnEglDestroyImageKHR eglDestroyImageKHR = (pfnEglDestroyImageKHR) eglGetProcAddress("eglDestroyImageKHR"); pfnVgCreateEGLImageTargetKHR vgCreateEGLImageTargetKHR = (pfnVgCreateEGLImageTargetKHR) eglGetProcAddress("vgCreateEGLImageTargetKHR"); - if (eglGetError() != EGL_SUCCESS || !eglCreateImageKHR || !eglDestroyImageKHR || !vgCreateEGLImageTargetKHR) { + if (eglGetError() != EGL_SUCCESS || !(QEgl::hasExtension("EGL_KHR_image") || QEgl::hasExtension("EGL_KHR_image_pixmap")) || !vgCreateEGLImageTargetKHR) { cleanup(); driver.Close(); return; @@ -606,11 +603,9 @@ void* QVGPixmapData::toNativeType(NativeType type) return 0; } - pfnEglCreateImageKHR eglCreateImageKHR = (pfnEglCreateImageKHR) eglGetProcAddress("eglCreateImageKHR"); - pfnEglDestroyImageKHR eglDestroyImageKHR = (pfnEglDestroyImageKHR) eglGetProcAddress("eglDestroyImageKHR"); pfnVgCreateEGLImageTargetKHR vgCreateEGLImageTargetKHR = (pfnVgCreateEGLImageTargetKHR) eglGetProcAddress("vgCreateEGLImageTargetKHR"); - if (eglGetError() != EGL_SUCCESS || !eglCreateImageKHR || !eglDestroyImageKHR || !vgCreateEGLImageTargetKHR) { + if (eglGetError() != EGL_SUCCESS || !(QEgl::hasExtension("EGL_KHR_image") || QEgl::hasExtension("EGL_KHR_image_pixmap")) || !vgCreateEGLImageTargetKHR) { driver.Close(); return 0; } diff --git a/src/plugins/imageformats/svg/main.cpp b/src/plugins/imageformats/svg/main.cpp index dbbd3b7..329e9d4 100644 --- a/src/plugins/imageformats/svg/main.cpp +++ b/src/plugins/imageformats/svg/main.cpp @@ -62,26 +62,18 @@ public: QStringList QSvgPlugin::keys() const { - return QStringList() << QLatin1String("svg"); + return QStringList() << QLatin1String("svg") << QLatin1String("svgz"); } QImageIOPlugin::Capabilities QSvgPlugin::capabilities(QIODevice *device, const QByteArray &format) const { - //### canRead disabled for now because it's hard to detect - // whether the file is actually svg without parsing it - //if (device->isReadable() && QSvgIOHandler::canRead(device)) - - if (format == "svg") + if (format == "svg" || format == "svgz") return Capabilities(CanRead); - else - return 0; - - - if (!device->isOpen()) + if (!format.isEmpty()) return 0; Capabilities cap; - if (device->isReadable()) + if (device->isReadable() && QSvgIOHandler::canRead(device)) cap |= CanRead; return cap; } diff --git a/src/plugins/imageformats/svg/qsvgiohandler.cpp b/src/plugins/imageformats/svg/qsvgiohandler.cpp index f3670c6..8155569 100644 --- a/src/plugins/imageformats/svg/qsvgiohandler.cpp +++ b/src/plugins/imageformats/svg/qsvgiohandler.cpp @@ -48,6 +48,7 @@ #include "qpixmap.h" #include "qpainter.h" #include "qvariant.h" +#include "qbuffer.h" #include "qdebug.h" QT_BEGIN_NAMESPACE @@ -55,67 +56,54 @@ QT_BEGIN_NAMESPACE class QSvgIOHandlerPrivate { public: - QSvgIOHandlerPrivate() - : r(new QSvgRenderer()), loaded(false) + QSvgIOHandlerPrivate(QSvgIOHandler *qq) + : q(qq), loaded(false), readDone(false), backColor(Qt::transparent) {} - ~QSvgIOHandlerPrivate() - { - delete r; - } bool load(QIODevice *device); - static bool findSvgTag(QIODevice *device); - QSvgRenderer *r; - QSize defaultSize; - QSize currentSize; - bool loaded; + QSvgIOHandler *q; + QSvgRenderer r; + QXmlStreamReader xmlReader; + QSize defaultSize; + QRect clipRect; + QSize scaledSize; + QRect scaledClipRect; + bool loaded; + bool readDone; + QColor backColor; }; + bool QSvgIOHandlerPrivate::load(QIODevice *device) { if (loaded) return true; + if (q->format().isEmpty()) + q->canRead(); + + bool res = false; + QBuffer *buf = qobject_cast<QBuffer *>(device); + if (buf) { + res = r.load(buf->data()); + } else if (q->format() == "svgz") { + res = r.load(device->readAll()); // ### can't stream svgz + } else { + xmlReader.setDevice(device); + res = r.load(&xmlReader); //### doesn't leave pos() correctly + } - if (r->load(device->readAll())) { - defaultSize = QSize(r->viewBox().width(), r->viewBox().height()); - if (currentSize.isEmpty()) - currentSize = defaultSize; + if (res) { + defaultSize = QSize(r.viewBox().width(), r.viewBox().height()); + loaded = true; } - loaded = r->isValid(); return loaded; } -bool QSvgIOHandlerPrivate::findSvgTag(QIODevice *device) -{ - qint64 pos = device->pos(); - device->seek(0); - char buffer[256]; - const char svg_tag[] = "<svg"; - - while (1) { - int size = device->read(buffer, 256); - for (int i=0; i<size - 5; ++i) { - if (!memcmp(buffer + i, svg_tag, 4)) { - if (buffer[i+4] == ' ' || buffer[i+4] == '\t' - || buffer[i+4] == '\n' || buffer[i+4] == '\r') - { - device->seek(pos); - return true; - } - } - } - if (device->atEnd()) - break; - device->seek(device->pos()-4); - } - device->seek(pos); - return false; -} QSvgIOHandler::QSvgIOHandler() - : d(new QSvgIOHandlerPrivate()) + : d(new QSvgIOHandlerPrivate(this)) { } @@ -129,7 +117,20 @@ QSvgIOHandler::~QSvgIOHandler() bool QSvgIOHandler::canRead() const { - return QSvgIOHandlerPrivate::findSvgTag(device()); + if (!device()) + return false; + if (d->loaded && !d->readDone) + return true; // Will happen if we have been asked for the size + + QByteArray buf = device()->peek(8); + if (buf.startsWith("\x1f\x8b")) { + setFormat("svgz"); + return true; + } else if (buf.contains("<?xml") || buf.contains("<svg")) { + setFormat("svg"); + return true; + } + return false; } @@ -141,14 +142,41 @@ QByteArray QSvgIOHandler::name() const bool QSvgIOHandler::read(QImage *image) { - if (d->load(device())) { - *image = QImage(d->currentSize, QImage::Format_ARGB32_Premultiplied); - if (!d->currentSize.isEmpty()) { - image->fill(0x00000000); + if (!d->readDone && d->load(device())) { + bool xform = (d->clipRect.isValid() || d->scaledSize.isValid() || d->scaledClipRect.isValid()); + QSize finalSize = d->defaultSize; + QRectF bounds; + if (xform && !d->defaultSize.isEmpty()) { + bounds = QRectF(QPointF(0,0), QSizeF(d->defaultSize)); + QPoint tr1, tr2; + QSizeF sc(1, 1); + if (d->clipRect.isValid()) { + tr1 = -d->clipRect.topLeft(); + finalSize = d->clipRect.size(); + } + if (d->scaledSize.isValid()) { + sc = QSizeF(qreal(d->scaledSize.width()) / finalSize.width(), + qreal(d->scaledSize.height()) / finalSize.height()); + finalSize = d->scaledSize; + } + if (d->scaledClipRect.isValid()) { + tr2 = -d->scaledClipRect.topLeft(); + finalSize = d->scaledClipRect.size(); + } + QTransform t; + t.translate(tr2.x(), tr2.y()); + t.scale(sc.width(), sc.height()); + t.translate(tr1.x(), tr1.y()); + bounds = t.mapRect(bounds); + } + *image = QImage(finalSize, QImage::Format_ARGB32_Premultiplied); + if (!finalSize.isEmpty()) { + image->fill(d->backColor.rgba()); QPainter p(image); - d->r->render(&p); + d->r.render(&p, bounds); p.end(); } + d->readDone = true; return true; } @@ -166,8 +194,17 @@ QVariant QSvgIOHandler::option(ImageOption option) const d->load(device()); return d->defaultSize; break; + case ClipRect: + return d->clipRect; + break; case ScaledSize: - return d->currentSize; + return d->scaledSize; + break; + case ScaledClipRect: + return d->scaledClipRect; + break; + case BackgroundColor: + return d->backColor; break; default: break; @@ -179,12 +216,17 @@ QVariant QSvgIOHandler::option(ImageOption option) const void QSvgIOHandler::setOption(ImageOption option, const QVariant & value) { switch(option) { - case Size: - d->defaultSize = value.toSize(); - d->currentSize = value.toSize(); + case ClipRect: + d->clipRect = value.toRect(); break; case ScaledSize: - d->currentSize = value.toSize(); + d->scaledSize = value.toSize(); + break; + case ScaledClipRect: + d->scaledClipRect = value.toRect(); + break; + case BackgroundColor: + d->backColor = value.value<QColor>(); break; default: break; @@ -198,7 +240,10 @@ bool QSvgIOHandler::supportsOption(ImageOption option) const { case ImageFormat: case Size: + case ClipRect: case ScaledSize: + case ScaledClipRect: + case BackgroundColor: return true; default: break; @@ -206,9 +251,11 @@ bool QSvgIOHandler::supportsOption(ImageOption option) const return false; } + bool QSvgIOHandler::canRead(QIODevice *device) { - return QSvgIOHandlerPrivate::findSvgTag(device); + QByteArray buf = device->peek(8); + return buf.startsWith("\x1f\x8b") || buf.contains("<?xml") || buf.contains("<svg"); } QT_END_NAMESPACE diff --git a/src/plugins/imageformats/tiff/qtiffhandler.cpp b/src/plugins/imageformats/tiff/qtiffhandler.cpp index 31e0c92..619aa4e 100644 --- a/src/plugins/imageformats/tiff/qtiffhandler.cpp +++ b/src/plugins/imageformats/tiff/qtiffhandler.cpp @@ -385,8 +385,8 @@ static bool checkGrayscale(const QVector<QRgb> &colorTable) const bool increasing = (colorTable.at(0) == 0xff000000); for (int i = 0; i < 256; ++i) { - if (increasing && colorTable.at(i) != qRgb(i, i, i) - || !increasing && colorTable.at(i) != qRgb(255 - i, 255 - i, 255 - i)) + if ((increasing && colorTable.at(i) != qRgb(i, i, i)) + || (!increasing && colorTable.at(i) != qRgb(255 - i, 255 - i, 255 - i))) return false; } return true; diff --git a/src/script/api/qscriptengine.cpp b/src/script/api/qscriptengine.cpp index 3e5249a..13b8e7c 100644 --- a/src/script/api/qscriptengine.cpp +++ b/src/script/api/qscriptengine.cpp @@ -875,7 +875,7 @@ QScriptEnginePrivate::QScriptEnginePrivate() return; } JSC::initializeThreading(); - + JSC::IdentifierTable *oldTable = JSC::currentIdentifierTable(); globalData = JSC::JSGlobalData::create().releaseRef(); globalData->clientData = new QScript::GlobalClientData(this); JSC::JSGlobalObject *globalObject = new (globalData)QScript::GlobalObject(); @@ -911,6 +911,7 @@ QScriptEnginePrivate::QScriptEnginePrivate() activeAgent = 0; agentLineNumber = -1; processEventsInterval = -1; + JSC::setCurrentIdentifierTable(oldTable); } QScriptEnginePrivate::~QScriptEnginePrivate() @@ -3311,8 +3312,12 @@ bool QScriptEngine::convertV2(const QScriptValue &value, int type, void *ptr) if (vp) { switch (vp->type) { case QScriptValuePrivate::JavaScriptCore: { - JSC::ExecState *exec = vp->engine ? vp->engine->currentFrame : 0; - return QScriptEnginePrivate::convertValue(exec, vp->jscValue, type, ptr); + if (vp->engine) { + QScript::APIShim shim(vp->engine); + return QScriptEnginePrivate::convertValue(vp->engine->currentFrame, vp->jscValue, type, ptr); + } else { + return QScriptEnginePrivate::convertValue(0, vp->jscValue, type, ptr); + } } case QScriptValuePrivate::Number: return QScriptEnginePrivate::convertNumber(vp->numberValue, type, ptr); diff --git a/src/script/api/qscriptprogram.cpp b/src/script/api/qscriptprogram.cpp index d4a32f4..8d4de11 100644 --- a/src/script/api/qscriptprogram.cpp +++ b/src/script/api/qscriptprogram.cpp @@ -120,7 +120,7 @@ QScriptProgram::QScriptProgram(const QScriptProgram &other) */ QScriptProgram::~QScriptProgram() { - Q_D(QScriptProgram); + // Q_D(QScriptProgram); // if (d->engine && (d->ref == 1)) // d->engine->unregisterScriptProgram(d); } diff --git a/src/script/api/qscriptstring.cpp b/src/script/api/qscriptstring.cpp index d0b0ffd..8c7c30c 100644 --- a/src/script/api/qscriptstring.cpp +++ b/src/script/api/qscriptstring.cpp @@ -94,7 +94,7 @@ QScriptString::~QScriptString() case QScriptStringPrivate::HeapAllocated: if (d->engine && (d->ref == 1)) { // Make sure the identifier is removed from the correct engine. - QScript::APIShim(d->engine); + QScript::APIShim shim(d->engine); d->identifier = JSC::Identifier(); d->engine->unregisterScriptString(d); } diff --git a/src/script/api/qscriptvalue.cpp b/src/script/api/qscriptvalue.cpp index 4cd84a4..7469f9a 100644 --- a/src/script/api/qscriptvalue.cpp +++ b/src/script/api/qscriptvalue.cpp @@ -561,6 +561,7 @@ QScriptValue QScriptValue::scope() const Q_D(const QScriptValue); if (!d || !d->isObject()) return QScriptValue(); + QScript::APIShim shim(d->engine); // ### make hidden property JSC::JSValue result = d->property("__qt_scope__", QScriptValue::ResolveLocal); return d->engine->scriptValueFromJSCValue(result); @@ -650,11 +651,12 @@ static Type type(const QScriptValue &v) return Object; } -QScriptValue ToPrimitive(const QScriptValue &object, JSC::PreferredPrimitiveType hint = JSC::NoPreference) +static QScriptValue ToPrimitive(const QScriptValue &object, JSC::PreferredPrimitiveType hint = JSC::NoPreference) { Q_ASSERT(object.isObject()); QScriptValuePrivate *pp = QScriptValuePrivate::get(object); Q_ASSERT(pp->engine != 0); + QScript::APIShim shim(pp->engine); JSC::ExecState *exec = pp->engine->currentFrame; JSC::JSValue savedException; QScriptEnginePrivate::saveException(exec, &savedException); @@ -848,6 +850,7 @@ bool QScriptValue::equals(const QScriptValue &other) const if (!eng_p) eng_p = other.d_ptr->engine; if (eng_p) { + QScript::APIShim shim(eng_p); JSC::ExecState *exec = eng_p->currentFrame; JSC::JSValue savedException; QScriptEnginePrivate::saveException(exec, &savedException); @@ -940,9 +943,12 @@ QString QScriptValue::toString() const return QString(); switch (d->type) { case QScriptValuePrivate::JavaScriptCore: { - JSC::ExecState *exec = d->engine ? d->engine->currentFrame : 0; - return QScriptEnginePrivate::toString(exec, d->jscValue); - } + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toString(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toString(0, d->jscValue); + } } case QScriptValuePrivate::Number: return QScript::ToString(d->numberValue); case QScriptValuePrivate::String: @@ -970,8 +976,12 @@ qsreal QScriptValue::toNumber() const return 0; switch (d->type) { case QScriptValuePrivate::JavaScriptCore: { - JSC::ExecState *exec = d->engine ? d->engine->currentFrame : 0; - return QScriptEnginePrivate::toNumber(exec, d->jscValue); + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toNumber(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toNumber(0, d->jscValue); + } } case QScriptValuePrivate::Number: return d->numberValue; @@ -993,8 +1003,12 @@ bool QScriptValue::toBoolean() const return false; switch (d->type) { case QScriptValuePrivate::JavaScriptCore: { - JSC::ExecState *exec = d->engine ? d->engine->currentFrame : 0; - return QScriptEnginePrivate::toBool(exec, d->jscValue); + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toBool(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toBool(0, d->jscValue); + } } case QScriptValuePrivate::Number: return QScript::ToBool(d->numberValue); @@ -1025,8 +1039,12 @@ bool QScriptValue::toBool() const return false; switch (d->type) { case QScriptValuePrivate::JavaScriptCore: { - JSC::ExecState *exec = d->engine ? d->engine->currentFrame : 0; - return QScriptEnginePrivate::toBool(exec, d->jscValue); + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toBool(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toBool(0, d->jscValue); + } } case QScriptValuePrivate::Number: return QScript::ToBool(d->numberValue); @@ -1055,8 +1073,12 @@ qint32 QScriptValue::toInt32() const return 0; switch (d->type) { case QScriptValuePrivate::JavaScriptCore: { - JSC::ExecState *exec = d->engine ? d->engine->currentFrame : 0; - return QScriptEnginePrivate::toInt32(exec, d->jscValue); + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toInt32(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toInt32(0, d->jscValue); + } } case QScriptValuePrivate::Number: return QScript::ToInt32(d->numberValue); @@ -1085,8 +1107,12 @@ quint32 QScriptValue::toUInt32() const return 0; switch (d->type) { case QScriptValuePrivate::JavaScriptCore: { - JSC::ExecState *exec = d->engine ? d->engine->currentFrame : 0; - return QScriptEnginePrivate::toUInt32(exec, d->jscValue); + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toUInt32(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toUInt32(0, d->jscValue); + } } case QScriptValuePrivate::Number: return QScript::ToUInt32(d->numberValue); @@ -1115,8 +1141,12 @@ quint16 QScriptValue::toUInt16() const return 0; switch (d->type) { case QScriptValuePrivate::JavaScriptCore: { - JSC::ExecState *exec = d->engine ? d->engine->currentFrame : 0; - return QScriptEnginePrivate::toUInt16(exec, d->jscValue); + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toUInt16(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toUInt16(0, d->jscValue); + } } case QScriptValuePrivate::Number: return QScript::ToUInt16(d->numberValue); @@ -1145,8 +1175,12 @@ qsreal QScriptValue::toInteger() const return 0; switch (d->type) { case QScriptValuePrivate::JavaScriptCore: { - JSC::ExecState *exec = d->engine ? d->engine->currentFrame : 0; - return QScriptEnginePrivate::toInteger(exec, d->jscValue); + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toInteger(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toInteger(0, d->jscValue); + } } case QScriptValuePrivate::Number: return QScript::ToInteger(d->numberValue); @@ -1185,8 +1219,12 @@ QVariant QScriptValue::toVariant() const return QVariant(); switch (d->type) { case QScriptValuePrivate::JavaScriptCore: { - JSC::ExecState *exec = d->engine ? d->engine->currentFrame : 0; - return QScriptEnginePrivate::toVariant(exec, d->jscValue); + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toVariant(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toVariant(0, d->jscValue); + } } case QScriptValuePrivate::Number: return QVariant(d->numberValue); @@ -1409,6 +1447,7 @@ QScriptValue QScriptValue::property(const QScriptString &name, Q_D(const QScriptValue); if (!d || !d->isObject() || !QScriptStringPrivate::isValid(name)) return QScriptValue(); + QScript::APIShim shim(d->engine); return d->engine->scriptValueFromJSCValue(d->property(name.d_ptr->identifier, mode)); } @@ -1439,6 +1478,7 @@ void QScriptValue::setProperty(const QScriptString &name, qPrintable(name.toString())); return; } + QScript::APIShim shim(d->engine); JSC::JSValue jsValue = d->engine->scriptValueToJSCValue(value); d->setProperty(name.d_ptr->identifier, jsValue, flags); } diff --git a/src/script/api/qscriptvalueiterator.cpp b/src/script/api/qscriptvalueiterator.cpp index c58c046..ecda5fc 100644 --- a/src/script/api/qscriptvalueiterator.cpp +++ b/src/script/api/qscriptvalueiterator.cpp @@ -85,6 +85,17 @@ public: : initialized(false) {} + ~QScriptValueIteratorPrivate() + { + if (!initialized) + return; + QScriptEnginePrivate *eng_p = engine(); + if (!eng_p) + return; + QScript::APIShim shim(eng_p); + propertyNames.clear(); //destroying the identifiers need to be done under the APIShim guard + } + QScriptValuePrivate *object() const { return QScriptValuePrivate::get(objectValue); @@ -139,12 +150,6 @@ QScriptValueIterator::QScriptValueIterator(const QScriptValue &object) */ QScriptValueIterator::~QScriptValueIterator() { - Q_D(QScriptValueIterator); - if (d && d->engine()) { - // Make sure identifiers are removed from the correct engine. - QScript::APIShim shim(d->engine()); - d->propertyNames.clear(); - } } /*! @@ -287,6 +292,7 @@ QScriptValue QScriptValueIterator::value() const Q_D(const QScriptValueIterator); if (!d || !d->initialized || !d->engine()) return QScriptValue(); + QScript::APIShim shim(d->engine()); JSC::JSValue jsValue = d->object()->property(*d->current); return d->engine()->scriptValueFromJSCValue(jsValue); } @@ -302,6 +308,7 @@ void QScriptValueIterator::setValue(const QScriptValue &value) Q_D(QScriptValueIterator); if (!d || !d->initialized || !d->engine()) return; + QScript::APIShim shim(d->engine()); JSC::JSValue jsValue = d->engine()->scriptValueToJSCValue(value); d->object()->setProperty(*d->current, jsValue); } @@ -317,6 +324,7 @@ QScriptValue::PropertyFlags QScriptValueIterator::flags() const Q_D(const QScriptValueIterator); if (!d || !d->initialized || !d->engine()) return 0; + QScript::APIShim shim(d->engine()); return d->object()->propertyFlags(*d->current); } @@ -331,6 +339,7 @@ void QScriptValueIterator::remove() Q_D(QScriptValueIterator); if (!d || !d->initialized || !d->engine()) return; + QScript::APIShim shim(d->engine()); d->object()->setProperty(*d->current, JSC::JSValue()); d->propertyNames.erase(d->current); } diff --git a/tests/auto/moc/tst_moc.cpp b/tests/auto/moc/tst_moc.cpp index 30c2721..a56d842 100644 --- a/tests/auto/moc/tst_moc.cpp +++ b/tests/auto/moc/tst_moc.cpp @@ -1302,6 +1302,26 @@ void tst_Moc::QTBUG5590_dummyProperty() QCOMPARE(o.value2(), 82); } +class QTBUG7421_ReturnConstTemplate: public QObject +{ Q_OBJECT +public slots: + const QList<int> returnConstTemplate1() { return QList<int>(); } + QList<int> const returnConstTemplate2() { return QList<int>(); } + const int returnConstInt() { return 0; } + const QString returnConstString(const QString s) { return s; } + QString const returnConstString2( QString const s) { return s; } +}; + +class QTBUG9354_constInName: public QObject +{ Q_OBJECT +public slots: + void slotChooseScientificConst0(struct science_constant const &) {}; + void foo(struct science_const const &) {}; + void foo(struct constconst const &) {}; + void foo(struct constconst *) {}; + void foo(struct const_ *) {}; +}; + QTEST_APPLESS_MAIN(tst_Moc) #include "tst_moc.moc" diff --git a/tests/auto/qdate/tst_qdate.cpp b/tests/auto/qdate/tst_qdate.cpp index 7223291..60772eb 100644 --- a/tests/auto/qdate/tst_qdate.cpp +++ b/tests/auto/qdate/tst_qdate.cpp @@ -99,6 +99,7 @@ private slots: void standaloneShortMonthName() const; void longMonthName() const; void standaloneLongMonthName() const; + void roundtrip() const; }; Q_DECLARE_METATYPE(QDate) @@ -668,17 +669,18 @@ void tst_QDate::toString_format() void tst_QDate::isLeapYear() { - QVERIFY(QDate::isLeapYear(-4444)); - QVERIFY(!QDate::isLeapYear(-4443)); - QVERIFY(QDate::isLeapYear(-8)); - QVERIFY(!QDate::isLeapYear(-7)); - QVERIFY(QDate::isLeapYear(-4)); + QVERIFY(QDate::isLeapYear(-4445)); + QVERIFY(!QDate::isLeapYear(-4444)); + QVERIFY(!QDate::isLeapYear(-6)); + QVERIFY(QDate::isLeapYear(-5)); + QVERIFY(!QDate::isLeapYear(-4)); QVERIFY(!QDate::isLeapYear(-3)); QVERIFY(!QDate::isLeapYear(-2)); - QVERIFY(!QDate::isLeapYear(-1)); - QVERIFY(!QDate::isLeapYear(1)); - QVERIFY(!QDate::isLeapYear(1)); + QVERIFY(QDate::isLeapYear(-1)); + QVERIFY(!QDate::isLeapYear(0)); // Doesn't exist QVERIFY(!QDate::isLeapYear(1)); + QVERIFY(!QDate::isLeapYear(2)); + QVERIFY(!QDate::isLeapYear(3)); QVERIFY(QDate::isLeapYear(4)); QVERIFY(!QDate::isLeapYear(7)); QVERIFY(QDate::isLeapYear(8)); @@ -910,5 +912,37 @@ void tst_QDate::standaloneLongMonthName() const } } +void tst_QDate::roundtrip() const +{ + // Test round trip, this exercises setDate(), isValid(), isLeapYear(), + // year(), month(), day(), julianDayFromDate(), and getDateFromJulianDay() + // to ensure they are internally consistent (but doesn't guarantee correct) + + // Test Julian round trip in both BC and AD + QDate testDate; + QDate loopDate = QDate::fromJulianDay(1684899); // 1 Jan 100 BC + while ( loopDate.toJulianDay() <= 1757948 ) { // 31 Dec 100 AD + testDate.setDate( loopDate.year(), loopDate.month(), loopDate.day() ); + QCOMPARE( loopDate.toJulianDay(), testDate.toJulianDay() ); + loopDate = loopDate.addDays(1); + } + + // Test Julian and Gregorian round trip during changeover period + loopDate = QDate::fromJulianDay(2298153); // 1 Jan 1580 AD + while ( loopDate.toJulianDay() <= 2300334 ) { // 31 Dec 1585 AD + testDate.setDate( loopDate.year(), loopDate.month(), loopDate.day() ); + QCOMPARE( loopDate.toJulianDay(), testDate.toJulianDay() ); + loopDate = loopDate.addDays(1); + } + + // Test Gregorian round trip during current useful period + loopDate = QDate::fromJulianDay(2378497); // 1 Jan 1900 AD + while ( loopDate.toJulianDay() <= 2488433 ) { // 31 Dec 2100 AD + testDate.setDate( loopDate.year(), loopDate.month(), loopDate.day() ); + QCOMPARE( loopDate.toJulianDay(), testDate.toJulianDay() ); + loopDate = loopDate.addDays(1); + } +} + QTEST_APPLESS_MAIN(tst_QDate) #include "tst_qdate.moc" diff --git a/tests/auto/qfontmetrics/tst_qfontmetrics.cpp b/tests/auto/qfontmetrics/tst_qfontmetrics.cpp index 46f2b15..5d73764 100644 --- a/tests/auto/qfontmetrics/tst_qfontmetrics.cpp +++ b/tests/auto/qfontmetrics/tst_qfontmetrics.cpp @@ -259,7 +259,7 @@ void tst_QFontMetrics::bearingIncludedInBoundingRect() font.setItalic(false); QRect brectNormal = QFontMetrics(font).boundingRect("ITALIC"); - QVERIFY(brectItalic.width() > brectNormal.width()); + QVERIFY(brectItalic.width() >= brectNormal.width()); } QTEST_MAIN(tst_QFontMetrics) diff --git a/tests/auto/qgl/qgl.pro b/tests/auto/qgl/qgl.pro index 5f058f9..20f8018 100644 --- a/tests/auto/qgl/qgl.pro +++ b/tests/auto/qgl/qgl.pro @@ -7,6 +7,7 @@ requires(contains(QT_CONFIG,opengl)) QT += opengl contains(QT_CONFIG,egl):DEFINES += QGL_EGL +win32:!wince*: DEFINES += QT_NO_EGL SOURCES += tst_qgl.cpp RESOURCES = qgl.qrc diff --git a/tests/auto/qglbuffer/qglbuffer.pro b/tests/auto/qglbuffer/qglbuffer.pro index 07d05bb..5627d2d 100644 --- a/tests/auto/qglbuffer/qglbuffer.pro +++ b/tests/auto/qglbuffer/qglbuffer.pro @@ -6,4 +6,6 @@ load(qttest_p4) requires(contains(QT_CONFIG,opengl)) QT += opengl +win32:!wince*: DEFINES += QT_NO_EGL + SOURCES += tst_qglbuffer.cpp diff --git a/tests/auto/qglthreads/qglthreads.pro b/tests/auto/qglthreads/qglthreads.pro index 73b75d4..883eef2 100644 --- a/tests/auto/qglthreads/qglthreads.pro +++ b/tests/auto/qglthreads/qglthreads.pro @@ -2,6 +2,8 @@ load(qttest_p4) requires(contains(QT_CONFIG,opengl)) QT += opengl +win32:!wince*: DEFINES += QT_NO_EGL + HEADERS += tst_qglthreads.h SOURCES += tst_qglthreads.cpp diff --git a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp index 01ef9a2..89d5958 100644 --- a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp +++ b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp @@ -61,6 +61,7 @@ #include <QScrollBar> #include <QVBoxLayout> #include <QGraphicsEffect> +#include <QInputContext> #include "../../shared/util.h" @@ -424,6 +425,7 @@ private slots: void modality_keyEvents(); void itemIsInFront(); void scenePosChange(); + void updateMicroFocus(); // task specific tests below me void task141694_textItemEnsureVisible(); @@ -4318,6 +4320,21 @@ protected: break; case QGraphicsItem::ItemScenePositionHasChanged: break; + case QGraphicsItem::ItemRotationChange: + oldValues << rotation(); + break; + case QGraphicsItem::ItemRotationHasChanged: + break; + case QGraphicsItem::ItemScaleChange: + oldValues << scale(); + break; + case QGraphicsItem::ItemScaleHasChanged: + break; + case QGraphicsItem::ItemTransformOriginPointChange: + oldValues << transformOriginPoint(); + break; + case QGraphicsItem::ItemTransformOriginPointHasChanged: + break; } return itemChangeReturnValue.isValid() ? itemChangeReturnValue : value; } @@ -4414,6 +4431,48 @@ void tst_QGraphicsItem::itemChange() QCOMPARE(tester.zValue(), qreal(2.0)); } { + // ItemRotationChange / ItemRotationHasChanged + tester.itemChangeReturnValue = qreal(15.0); + tester.setRotation(10.0); + ++changeCount; // notification sent too + ++changeCount; + QCOMPARE(tester.changes.size(), changeCount); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemRotationChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemRotationHasChanged); + QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(qreal(10.0))); + QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(qreal(15.0))); + QCOMPARE(tester.oldValues.last(), QVariant(qreal(0.0))); + QCOMPARE(tester.rotation(), qreal(15.0)); + } + { + // ItemScaleChange / ItemScaleHasChanged + tester.itemChangeReturnValue = qreal(2.0); + tester.setScale(1.5); + ++changeCount; // notification sent too + ++changeCount; + QCOMPARE(tester.changes.size(), changeCount); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemScaleChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemScaleHasChanged); + QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(qreal(1.5))); + QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(qreal(2.0))); + QCOMPARE(tester.oldValues.last(), QVariant(qreal(1.0))); + QCOMPARE(tester.scale(), qreal(2.0)); + } + { + // ItemTransformOriginPointChange / ItemTransformOriginPointHasChanged + tester.itemChangeReturnValue = QPointF(2.0, 2.0); + tester.setTransformOriginPoint(1.0, 1.0); + ++changeCount; // notification sent too + ++changeCount; + QCOMPARE(tester.changes.size(), changeCount); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemTransformOriginPointChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemTransformOriginPointHasChanged); + QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(QPointF(1.0, 1.0))); + QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(QPointF(2.0, 2.0))); + QCOMPARE(tester.oldValues.last(), QVariant(QPointF(0.0, 0.0))); + QCOMPARE(tester.transformOriginPoint(), QPointF(2.0, 2.0)); + } + { // ItemFlagsChange tester.itemChangeReturnValue = QGraphicsItem::ItemIsSelectable; tester.setFlag(QGraphicsItem::ItemIsSelectable, false); @@ -7463,10 +7522,19 @@ void tst_QGraphicsItem::itemSendsGeometryChanges() QTransform x = QTransform().rotate(45); QPointF pos(10, 10); qreal o(0.5); + qreal r(10.0); + qreal s(1.5); + QPointF origin(1.0, 1.0); item.setTransform(x); item.setPos(pos); + item.setRotation(r); + item.setScale(s); + item.setTransformOriginPoint(origin); QCOMPARE(item.transform(), x); QCOMPARE(item.pos(), pos); + QCOMPARE(item.rotation(), r); + QCOMPARE(item.scale(), s); + QCOMPARE(item.transformOriginPoint(), origin); QCOMPARE(item.changes.size(), 0); item.setOpacity(o); @@ -7480,6 +7548,13 @@ void tst_QGraphicsItem::itemSendsGeometryChanges() QCOMPARE(item.transform(), QTransform()); QCOMPARE(item.pos(), QPointF()); QCOMPARE(item.opacity(), o); + item.setRotation(0.0); + item.setScale(1.0); + item.setTransformOriginPoint(0.0, 0.0); + QCOMPARE(item.changes.size(), 14); // rotation + scale + origin + QCOMPARE(item.rotation(), qreal(0.0)); + QCOMPARE(item.scale(), qreal(1.0)); + QCOMPARE(item.transformOriginPoint(), QPointF(0.0, 0.0)); QCOMPARE(item.changes, QList<QGraphicsItem::GraphicsItemChange>() << QGraphicsItem::ItemOpacityChange @@ -7489,7 +7564,13 @@ void tst_QGraphicsItem::itemSendsGeometryChanges() << QGraphicsItem::ItemTransformChange << QGraphicsItem::ItemTransformHasChanged << QGraphicsItem::ItemPositionChange - << QGraphicsItem::ItemPositionHasChanged); + << QGraphicsItem::ItemPositionHasChanged + << QGraphicsItem::ItemRotationChange + << QGraphicsItem::ItemRotationHasChanged + << QGraphicsItem::ItemScaleChange + << QGraphicsItem::ItemScaleHasChanged + << QGraphicsItem::ItemTransformOriginPointChange + << QGraphicsItem::ItemTransformOriginPointHasChanged); } // Make sure we update moved items correctly. @@ -9909,6 +9990,79 @@ void tst_QGraphicsItem::scenePosChange() QCOMPARE(child2->changes.count(QGraphicsItem::ItemScenePositionHasChanged), 0); } +class MyInputContext : public QInputContext +{ +public: + MyInputContext() : nbUpdates(0) {} + ~MyInputContext() {} + + QString identifierName() { return QString(); } + QString language() { return QString(); } + + void reset() {} + + bool isComposing() const { return false; } + + void update() { nbUpdates++; } + + bool nbUpdates; +}; + +class MyInputWidget : public QGraphicsWidget +{ +public: + MyInputWidget() + { + setFlag(QGraphicsItem::ItemIsFocusable, true); + setFlag(QGraphicsItem::ItemAcceptsInputMethod, true); + } + void mousePressEvent(QGraphicsSceneMouseEvent *event) + { + event->accept(); + } + + void doUpdateMicroFocus() + { + updateMicroFocus(); + } +}; + +void tst_QGraphicsItem::updateMicroFocus() +{ +#if defined Q_OS_WIN || defined Q_OS_MAC + QSKIP("QTBUG-9578", SkipAll); + return; +#endif + QGraphicsScene scene; + QWidget parent; + QGridLayout layout; + parent.setLayout(&layout); + QGraphicsView view(&scene); + QGraphicsView view2(&scene); + layout.addWidget(&view, 0, 0); + layout.addWidget(&view2, 0, 1); + MyInputContext ic2; + view2.setInputContext(&ic2); + MyInputContext ic; + view.setInputContext(&ic); + MyInputWidget input; + input.setPos(0, 0); + input.resize(150, 150); + scene.addItem(&input); + input.setFocus(); + parent.show(); + view.setFocus(); + qApp->setAutoSipEnabled(true); + QApplication::setActiveWindow(&parent); + QTest::qWaitForWindowShown(&parent); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&parent)); + input.doUpdateMicroFocus(); + QApplication::processEvents(); + QTRY_COMPARE(ic.nbUpdates, 1); + //No update since view2 does not have the focus. + QTRY_COMPARE(ic2.nbUpdates, 0); +} + void tst_QGraphicsItem::QTBUG_5418_textItemSetDefaultColor() { struct Item : public QGraphicsTextItem diff --git a/tests/auto/qimagereader/images/corrupt.svg b/tests/auto/qimagereader/images/corrupt.svg new file mode 100644 index 0000000..8747df0 --- /dev/null +++ b/tests/auto/qimagereader/images/corrupt.svg @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.0" + width="40px" + height="5px" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.46" + sodipodi:docname="test.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <sodipodi:namedview + inkscape:window-height="1005" +
\ No newline at end of file diff --git a/tests/auto/qimagereader/images/corrupt.svgz b/tests/auto/qimagereader/images/corrupt.svgz Binary files differnew file mode 100644 index 0000000..67fdcee --- /dev/null +++ b/tests/auto/qimagereader/images/corrupt.svgz diff --git a/tests/auto/qimagereader/images/rect.svg b/tests/auto/qimagereader/images/rect.svg new file mode 100644 index 0000000..c56549a --- /dev/null +++ b/tests/auto/qimagereader/images/rect.svg @@ -0,0 +1,462 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.0" + width="128" + height="128" + viewBox="0 0 105.427 137.439" + id="Livello_1" + xml:space="preserve" + style="overflow:visible"><defs + id="defs2727"><linearGradient + x1="26.294399" + y1="11.6704" + x2="71.901901" + y2="133.0273" + id="linearGradient3352" + xlink:href="#XMLID_34_" + gradientUnits="userSpaceOnUse" /><linearGradient + x1="36.838902" + y1="7.7075" + x2="82.446297" + y2="129.0645" + id="linearGradient3354" + xlink:href="#XMLID_34_" + gradientUnits="userSpaceOnUse" /><linearGradient + x1="33.882301" + y1="23.583" + x2="39.972198" + y2="23.583" + id="XMLID_34_" + gradientUnits="userSpaceOnUse"> + <stop + id="stop2672" + style="stop-color:#ff5d5d;stop-opacity:1" + offset="0" /> + <stop + id="stop2674" + style="stop-color:#e20800;stop-opacity:1" + offset="1" /> + </linearGradient><linearGradient + x1="33.882301" + y1="23.583" + x2="39.972198" + y2="23.583" + id="linearGradient3368" + xlink:href="#XMLID_34_" + gradientUnits="userSpaceOnUse" /><linearGradient + x1="54.356899" + y1="1.124" + x2="99.964401" + y2="122.481" + id="linearGradient3370" + xlink:href="#XMLID_34_" + gradientUnits="userSpaceOnUse" /><linearGradient + x1="15.8457" + y1="15.5972" + x2="61.453098" + y2="136.9541" + id="linearGradient3376" + xlink:href="#XMLID_34_" + gradientUnits="userSpaceOnUse" /><linearGradient + x1="43.438" + y1="5.2275" + x2="89.045403" + y2="126.5845" + id="linearGradient3382" + xlink:href="#XMLID_34_" + gradientUnits="userSpaceOnUse" /><linearGradient + x1="8.1176996" + y1="14.9019" + x2="70.759598" + y2="117.2331" + id="linearGradient3792" + xlink:href="#XMLID_30_" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9991,-4.18e-2,4.18e-2,0.9991,-2.4309,1.195)" /><linearGradient + x1="10.5708" + y1="10.1548" + x2="73.2117" + y2="112.4844" + id="linearGradient3794" + xlink:href="#XMLID_30_" + gradientUnits="userSpaceOnUse" /><linearGradient + x1="6.2178001" + y1="72.223602" + x2="79.360802" + y2="72.223602" + id="XMLID_26_" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(0,2.1512354)"> + <stop + id="stop2578" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop2580" + style="stop-color:#eeeeec;stop-opacity:1" + offset="1" /> + </linearGradient><filter + id="filter5869"><feGaussianBlur + id="feGaussianBlur5871" + stdDeviation="1.2254964" + inkscape:collect="always" /></filter><filter + id="filter5873"><feGaussianBlur + id="feGaussianBlur5875" + stdDeviation="1.3615922" + inkscape:collect="always" /></filter><filter + id="filter2854"><feGaussianBlur + id="feGaussianBlur2856" + stdDeviation="0.8944793" + inkscape:collect="always" /></filter></defs> +<filter + id="AI_Sfocatura_1"> + <feGaussianBlur + id="feGaussianBlur2545" + stdDeviation="1" /> +</filter> +<g + transform="translate(-3.2052027,3.2058836)" + id="g2547"> + <g + transform="matrix(0.9982563,0,0,0.9982563,-1.5492234e-2,0.2232388)" + id="g2549"> + <g + id="g2551"> + <linearGradient + x1="6.2178001" + y1="68.029297" + x2="79.360802" + y2="68.029297" + id="XMLID_24_" + gradientUnits="userSpaceOnUse"> + <stop + id="stop2554" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop2556" + style="stop-color:#eeeeec;stop-opacity:1" + offset="1" /> + </linearGradient> + <path + d="M 9.542,121.224 C 7.713,121.224 6.217,119.728 6.217,117.9 L 6.217,18.16 C 6.217,16.331 7.713,14.835 9.542,14.835 L 76.036,14.835 C 77.864,14.835 79.36,16.331 79.36,18.16 L 79.36,117.9 C 79.36,119.728 77.864,121.224 76.036,121.224 L 9.542,121.224 z" + id="path2558" + style="fill:url(#XMLID_24_)" /> + </g> + <g + id="g2560"> + <linearGradient + x1="10.5718" + y1="15.3989" + x2="73.212097" + y2="117.7277" + id="XMLID_25_" + gradientUnits="userSpaceOnUse"> + <stop + id="stop2563" + style="stop-color:#77b753;stop-opacity:1" + offset="0" /> + <stop + id="stop2565" + style="stop-color:#00892c;stop-opacity:1" + offset="1" /> + </linearGradient> + <path + d="M 11.204,18.159 C 10.29,18.159 9.542,18.907 9.542,19.821 L 9.542,116.237 C 9.542,117.151 10.29,117.899 11.204,117.899 L 74.375,117.899 C 75.289,117.899 76.037,117.151 76.037,116.237 L 76.037,19.821 C 76.037,18.907 75.289,18.159 74.375,18.159 L 11.204,18.159 z" + id="path2567" + style="fill:url(#XMLID_25_)" /> + </g> + </g> + <g + transform="matrix(0.9982563,0,0,0.9982563,1.05825,0.2232388)" + id="g2569"> + <path + d="M 11.639,126.468 C 9.811,126.468 8.314,124.972 8.314,123.143 L 8.314,23.403 C 8.314,21.574 9.811,20.078 11.639,20.078 L 78.134,20.078 C 79.962,20.078 81.458,21.574 81.458,23.403 L 81.458,123.143 C 81.458,124.972 79.962,126.468 78.134,126.468 L 23.696022,126.468 L 11.639,126.468 z" + transform="matrix(1.041449,0,0,1,-4.451967,3.1512354)" + id="path2575" + style="opacity:0.6;filter:url(#filter2854)" /><path + d="M 9.542,127.56924 C 7.714,127.56924 6.218,126.07324 6.218,124.24624 L 6.218,24.505236 C 6.218,22.677236 7.714,21.181236 9.542,21.181236 L 76.037,21.181236 C 77.865,21.181236 79.361,22.677236 79.361,24.505236 L 79.361,124.24624 C 79.361,126.07324 77.865,127.56924 76.037,127.56924 L 9.542,127.56924 z" + id="path2582" + style="fill:url(#XMLID_26_)" /> + <g + transform="translate(0,2.1512354)" + id="g2584"> + <g + transform="matrix(1.0276326,0,0,1,-2.2508995,0)" + id="g2586" + style="opacity:0.5;filter:url(#AI_Sfocatura_1)"> + <path + d="M 11.639,123.321 C 9.811,123.321 8.314,121.824 8.314,119.997 L 81.458,119.997 C 81.458,121.824 79.962,123.321 78.134,123.321 L 11.639,123.321 z" + id="path2588" /> + </g> + <linearGradient + x1="6.2178001" + y1="69.078102" + x2="79.360802" + y2="69.078102" + id="XMLID_27_" + gradientUnits="userSpaceOnUse"> + <stop + id="stop2591" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop2593" + style="stop-color:#eeeeec;stop-opacity:1" + offset="1" /> + </linearGradient> + <path + d="M 9.542,122.272 C 7.714,122.272 6.218,120.776 6.218,118.947 L 6.218,19.207 C 6.218,17.378 7.714,15.882 9.542,15.882 L 76.037,15.882 C 77.865,15.882 79.361,17.378 79.361,19.207 L 79.361,118.947 C 79.361,120.776 77.865,122.272 76.037,122.272 L 9.542,122.272 z" + id="path2595" + style="fill:url(#XMLID_27_)" /> + </g> + <g + transform="translate(0,3.2268531)" + id="g2597"> + <g + transform="matrix(1.0368435,0,0,1,-3.0011994,-1.0756177)" + id="g2599" + style="opacity:0.5;filter:url(#AI_Sfocatura_1)"> + <path + d="M 11.639,120.175 C 9.811,120.175 8.314,118.679 8.314,116.85 L 81.458,116.85 C 81.458,118.679 79.962,120.175 78.134,120.175 L 11.639,120.175 z" + id="path2601" /> + </g> + <linearGradient + x1="6.2178001" + y1="65.931602" + x2="79.360802" + y2="65.931602" + id="XMLID_28_" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(0,-1.0756177)"> + <stop + id="stop2604" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop2606" + style="stop-color:#eeeeec;stop-opacity:1" + offset="1" /> + </linearGradient> + <path + d="M 9.542,118.05038 C 7.714,118.05038 6.218,116.55438 6.218,114.72638 L 6.218,14.986382 C 6.218,13.157382 7.714,11.661382 9.542,11.661382 L 76.037,11.661382 C 77.865,11.661382 79.361,13.157382 79.361,14.986382 L 79.361,114.72638 C 79.361,116.55438 77.865,118.05038 76.037,118.05038 L 9.542,118.05038 z" + id="path2608" + style="fill:url(#XMLID_28_)" /> + </g> + <g + transform="translate(0,1.8317954)" + id="g2610"> + <g + transform="matrix(1.0184218,0,0,1.0158314,-1.4821779,-1.8527316)" + id="g2612" + style="opacity:0.5;filter:url(#AI_Sfocatura_1)"> + <path + d="M 10.639,117.029 C 8.811,117.029 7.314,115.532 7.314,113.704 L 7.314,13.964 C 7.314,12.135 8.811,10.639 10.639,10.639 L 77.134,10.639 C 78.962,10.639 80.458,12.135 80.458,13.964 L 80.458,113.704 C 80.458,115.532 78.962,117.029 77.134,117.029 L 10.639,117.029 z" + id="path2614" /> + </g> + <linearGradient + x1="6.2178001" + y1="62.785599" + x2="79.360802" + y2="62.785599" + id="XMLID_29_" + gradientUnits="userSpaceOnUse"> + <stop + id="stop2617" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop2619" + style="stop-color:#eeeeec;stop-opacity:1" + offset="1" /> + </linearGradient> + <path + d="M 9.542,115.98 C 7.714,115.98 6.218,114.483 6.218,112.656 L 6.218,12.916 C 6.218,11.087 7.714,9.591 9.542,9.591 L 76.037,9.591 C 77.865,9.591 79.361,11.087 79.361,12.916 L 79.361,112.657 C 79.361,114.484 77.865,115.981 76.037,115.981 L 9.542,115.981 L 9.542,115.98 z" + id="path2621" + style="fill:url(#XMLID_29_)" /> + <linearGradient + x1="10.5708" + y1="10.1548" + x2="73.2117" + y2="112.4844" + id="XMLID_30_" + gradientUnits="userSpaceOnUse"> + <stop + id="stop2624" + style="stop-color:#73bdf2;stop-opacity:1" + offset="0" /> + <stop + id="stop2626" + style="stop-color:#3592ee;stop-opacity:1" + offset="1" /> + </linearGradient> + <path + d="M 11.204,12.916 C 10.289,12.916 9.541,13.664 9.541,14.578 L 9.541,110.994 C 9.541,111.909 10.289,112.657 11.204,112.657 L 74.373,112.657 C 75.288,112.657 76.036,111.909 76.036,110.994 L 76.036,14.578 C 76.036,13.664 75.288,12.916 74.373,12.916 L 11.204,12.916 L 11.204,12.916 z" + id="path2628" + style="fill:url(#linearGradient3794)" /> + </g> + </g> + <g + transform="matrix(0.9961334,-6.5068755e-2,6.5068755e-2,0.9961334,-5.7493275,-6.3015051)" + id="g2630"> + <g + transform="matrix(1.0311837,0,0,1.0154411,-2.8218065,-1.9088007)" + id="g2632" + style="opacity:0.6;filter:url(#filter5869)"> + <path + d="M 10.744,123.615 C 8.917,123.691 7.36,122.259 7.283,120.432 L 3.118,20.779 C 3.042,18.952 4.474,17.395 6.301,17.319 L 72.737,14.542 C 74.563,14.465 76.121,15.898 76.198,17.725 L 80.363,117.377 C 80.439,119.204 79.007,120.761 77.181,120.839 L 10.744,123.615 z" + id="path2634" /> + </g> + <g + id="g2636"> + + <linearGradient + x1="3.7607" + y1="67.532204" + x2="76.909698" + y2="67.532204" + id="XMLID_31_" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9991,-4.18e-2,4.18e-2,0.9991,-2.4309,1.195)"> + <stop + id="stop2639" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop2641" + style="stop-color:#eeeeec;stop-opacity:1" + offset="1" /> + </linearGradient> + <path + d="M 9.695,121.518 C 7.868,121.595 6.311,120.163 6.234,118.335 L 2.069,18.682 C 1.993,16.855 3.425,15.298 5.252,15.222 L 71.688,12.444 C 73.514,12.368 75.072,13.8 75.149,15.627 L 79.314,115.28 C 79.391,117.106 77.959,118.663 76.131,118.741 L 9.695,121.518 z" + id="path2643" + style="fill:url(#XMLID_31_)" /> + </g> + <path + d="M 7.051,18.474 C 6.138,18.513 5.422,19.291 5.46,20.204 L 9.486,116.535 C 9.525,117.448 10.303,118.164 11.217,118.126 L 74.331,115.489 C 75.244,115.451 75.96,114.672 75.922,113.759 L 71.897,17.427 C 71.859,16.513 71.08,15.797 70.167,15.836 L 7.051,18.474 z" + id="path2652" + style="fill:url(#linearGradient3792);fill-opacity:1" /> + <path + d="M 9.5625,22.375 C 10.84375,52.927083 12.125,83.479167 13.40625,114.03125 C 32.885417,113.21875 52.364583,112.40625 71.84375,111.59375 C 70.5625,81.041667 69.28125,50.489583 68,19.9375 C 48.520833,20.75 29.041667,21.5625 9.5625,22.375 z" + id="path4189" + style="opacity:0.6;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:1.00000001, 1.00000001;stroke-dashoffset:0;stroke-opacity:1" /></g> + <g + transform="matrix(0.9982563,0,0,0.9982563,10.72193,-5.1454722)" + id="g2654"> + <g + transform="translate(-4.2156998e-8,1.0756177)" + id="g2656" + style="opacity:0.6;filter:url(#filter5873)"> + <path + d="M 10.854785,112.52047 C 9.0174891,112.09656 7.8676311,110.2731 8.2990859,108.46891 L 31.839177,9.9940152 C 32.271664,8.1888112 34.127539,7.0580233 35.964835,7.481942 L 102.78149,22.901224 C 104.61776,23.325142 105.76865,25.149615 105.33616,26.954819 L 81.79607,125.42768 C 81.364615,127.23289 79.507708,128.36368 77.671444,127.93976 L 10.854785,112.52047 z" + id="path2658" /> + </g> + <g + id="g2660"> + + <linearGradient + x1="16.688499" + y1="-8.9546003" + x2="94.108398" + y2="105.6356" + id="XMLID_33_" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9735,0.2287,-0.2287,0.9735,14.4454,7.996)"> + <stop + id="stop2663" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop2665" + style="stop-color:#eeeeec;stop-opacity:1" + offset="1" /> + </linearGradient> + <path + d="M 12.707,111.688 C 10.927,111.271 9.813,109.472 10.231,107.692 L 33.037,10.593 C 33.455,8.813 35.254,7.698 37.034,8.116 L 101.767,23.32 C 103.546,23.738 104.661,25.537 104.243,27.317 L 81.436,124.415 C 81.019,126.195 79.219,127.31 77.44,126.892 L 12.707,111.688 z" + id="path2667" + style="fill:url(#XMLID_33_)" /> + </g> + <path + d="M 33.925,25.17 L 35.435,25.3 C 35.369,25.76 35.413,26.134 35.567,26.422 C 35.721,26.71 35.941,26.887 36.226,26.954 C 36.538,27.027 36.832,26.947 37.114,26.715 C 37.396,26.483 37.594,26.116 37.712,25.615 C 37.821,25.149 37.805,24.759 37.661,24.445 C 37.517,24.132 37.298,23.939 37.004,23.87 C 36.811,23.825 36.571,23.817 36.281,23.846 L 36.797,22.386 C 37.187,22.487 37.522,22.455 37.801,22.292 C 38.076,22.127 38.26,21.847 38.353,21.451 C 38.431,21.12 38.41,20.843 38.291,20.618 C 38.172,20.392 37.984,20.25 37.729,20.19 C 37.473,20.13 37.226,20.187 36.987,20.358 C 36.749,20.531 36.562,20.825 36.427,21.24 L 35.104,20.624 C 35.455,19.78 35.886,19.208 36.401,18.909 C 36.915,18.61 37.492,18.536 38.131,18.686 C 38.85,18.855 39.36,19.244 39.663,19.853 C 39.967,20.462 40.045,21.076 39.9,21.695 C 39.802,22.113 39.618,22.468 39.35,22.758 C 39.081,23.049 38.726,23.276 38.287,23.439 C 38.699,23.661 38.996,24.004 39.18,24.471 C 39.362,24.937 39.383,25.471 39.242,26.073 C 39.036,26.949 38.604,27.613 37.948,28.064 C 37.29,28.515 36.601,28.655 35.88,28.486 C 35.189,28.323 34.661,27.944 34.297,27.347 C 33.931,26.751 33.808,26.025 33.925,25.17 z" + id="path2676" + style="fill:url(#linearGradient3368);fill-opacity:1" /> + <linearGradient + x1="26.294399" + y1="11.6704" + x2="71.901901" + y2="133.0273" + id="XMLID_35_" + gradientUnits="userSpaceOnUse"> + <stop + id="stop2679" + style="stop-color:#ff8080;stop-opacity:1" + offset="0" /> + <stop + id="stop2681" + style="stop-color:#e20800;stop-opacity:1" + offset="1" /> + </linearGradient> + <path + d="M 32.977,38.964 C 33.619,37.58 34.903,36.55 35.811,35.945 C 36.752,35.319 37.49,34.55 37.729,33.53 C 38.094,31.979 36.471,30.257 34.621,31.997 C 33.74,29.616 31.507,30.433 31.143,31.984 C 30.903,33.003 31.223,34.019 31.786,35 C 32.329,35.946 33.021,37.439 32.977,38.964 z" + id="path2683" + style="fill:url(#linearGradient3352);fill-opacity:1" /> + <path + d="M 80.223,109.559 L 78.711,109.43 C 78.779,108.969 78.734,108.595 78.58,108.308 C 78.426,108.02 78.205,107.842 77.922,107.776 C 77.61,107.703 77.315,107.782 77.033,108.014 C 76.751,108.246 76.553,108.614 76.433,109.114 C 76.324,109.581 76.341,109.97 76.484,110.284 C 76.629,110.598 76.849,110.79 77.142,110.859 C 77.335,110.904 77.576,110.913 77.865,110.883 L 77.349,112.343 C 76.958,112.242 76.624,112.274 76.345,112.439 C 76.07,112.603 75.886,112.883 75.792,113.279 C 75.714,113.609 75.735,113.887 75.854,114.112 C 75.973,114.339 76.161,114.481 76.416,114.541 C 76.672,114.602 76.918,114.545 77.156,114.372 C 77.394,114.2 77.582,113.906 77.717,113.49 L 79.039,114.106 C 78.689,114.95 78.258,115.521 77.742,115.82 C 77.228,116.119 76.652,116.193 76.013,116.043 C 75.294,115.874 74.783,115.486 74.48,114.876 C 74.175,114.268 74.097,113.653 74.244,113.034 C 74.342,112.616 74.525,112.262 74.795,111.971 C 75.063,111.681 75.418,111.453 75.857,111.289 C 75.445,111.069 75.146,110.725 74.964,110.259 C 74.78,109.793 74.761,109.259 74.902,108.657 C 75.109,107.78 75.541,107.117 76.197,106.666 C 76.855,106.216 77.543,106.075 78.265,106.244 C 78.956,106.406 79.484,106.786 79.849,107.382 C 80.217,107.978 80.34,108.704 80.223,109.559 z" + id="path2694" + style="fill:#e20800;fill-opacity:1" /><path + d="M 81.063,95.83 C 80.422,97.214 79.138,98.244 78.23,98.85 C 77.29,99.477 76.55,100.246 76.311,101.264 C 75.947,102.815 77.57,104.536 79.419,102.797 C 80.301,105.178 82.533,104.361 82.898,102.811 C 83.138,101.791 82.819,100.776 82.255,99.795 C 81.711,98.849 81.021,97.355 81.063,95.83 z" + id="path2701" + style="fill:url(#linearGradient3382);fill-opacity:1" /> + <linearGradient + x1="54.356899" + y1="1.124" + x2="99.964401" + y2="122.481" + id="XMLID_39_" + gradientUnits="userSpaceOnUse"> + <stop + id="stop2704" + style="stop-color:#ff8080;stop-opacity:1" + offset="0" /> + <stop + id="stop2706" + style="stop-color:#e20800;stop-opacity:1" + offset="1" /> + </linearGradient> + <path + d="M 63.174,42.222 C 64.361,39.656 66.742,37.748 68.427,36.625 C 70.171,35.463 71.54,34.04 71.985,32.15 C 72.66,29.274 69.651,26.081 66.22,29.307 C 64.585,24.892 60.448,26.406 59.772,29.281 C 59.329,31.172 59.921,33.055 60.965,34.873 C 61.97,36.628 63.253,39.396 63.174,42.222 z" + id="path2708" + style="fill:url(#linearGradient3370);fill-opacity:1" /> + <linearGradient + x1="36.838902" + y1="7.7075" + x2="82.446297" + y2="129.0645" + id="XMLID_40_" + xlink:href="#XMLID_39_" + gradientUnits="userSpaceOnUse"> + <stop + id="stop2711" + style="stop-color:#ff8080;stop-opacity:1" + offset="0" /> + <stop + id="stop2713" + style="stop-color:#e20800;stop-opacity:1" + offset="1" /> + </linearGradient> + <path + d="M 55.486,74.959 C 56.672,72.393 59.054,70.485 60.737,69.362 C 62.481,68.2 63.851,66.777 64.296,64.886 C 64.97,62.01 61.962,58.818 58.532,62.043 C 56.897,57.628 52.759,59.142 52.082,62.018 C 51.638,63.908 52.23,65.792 53.275,67.609 C 54.281,69.364 55.565,72.132 55.486,74.959 z" + id="path2715" + style="fill:url(#linearGradient3354);fill-opacity:1" /> + <path + d="M 51.37,92.488 C 50.182,95.054 47.801,96.961 46.117,98.084 C 44.373,99.246 43.004,100.67 42.559,102.561 C 41.884,105.436 44.893,108.627 48.323,105.404 C 49.958,109.82 54.096,108.304 54.772,105.428 C 55.217,103.538 54.623,101.655 53.579,99.836 C 52.573,98.082 51.291,95.314 51.37,92.488 z" + id="path2724" + style="fill:url(#linearGradient3376);fill-opacity:1" /> + </g> +</g> +</svg>
\ No newline at end of file diff --git a/tests/auto/qimagereader/images/rect.svgz b/tests/auto/qimagereader/images/rect.svgz Binary files differnew file mode 100644 index 0000000..c2e193b --- /dev/null +++ b/tests/auto/qimagereader/images/rect.svgz diff --git a/tests/auto/qimagereader/qimagereader.pro b/tests/auto/qimagereader/qimagereader.pro index 5b061b0..402e94b 100644 --- a/tests/auto/qimagereader/qimagereader.pro +++ b/tests/auto/qimagereader/qimagereader.pro @@ -9,6 +9,7 @@ RESOURCES += qimagereader.qrc !contains(QT_CONFIG, no-jpeg):DEFINES += QTEST_HAVE_JPEG !contains(QT_CONFIG, no-mng):DEFINES += QTEST_HAVE_MNG !contains(QT_CONFIG, no-tiff):DEFINES += QTEST_HAVE_TIFF +!contains(QT_CONFIG, no-svg):DEFINES += QTEST_HAVE_SVG win32-msvc:QMAKE_CXXFLAGS -= -Zm200 win32-msvc:QMAKE_CXXFLAGS += -Zm800 diff --git a/tests/auto/qimagereader/qimagereader.qrc b/tests/auto/qimagereader/qimagereader.qrc index bc48244..278427b 100644 --- a/tests/auto/qimagereader/qimagereader.qrc +++ b/tests/auto/qimagereader/qimagereader.qrc @@ -61,5 +61,9 @@ <file>images/four-frames.gif</file> <file>images/qt-gif-anim.gif</file> <file>images/qt-gif-noanim.gif</file> + <file>images/rect.svg</file> + <file>images/rect.svgz</file> + <file>images/corrupt.svg</file> + <file>images/corrupt.svgz</file> </qresource> </RCC> diff --git a/tests/auto/qimagereader/tst_qimagereader.cpp b/tests/auto/qimagereader/tst_qimagereader.cpp index 99244c2..1b4c502 100644 --- a/tests/auto/qimagereader/tst_qimagereader.cpp +++ b/tests/auto/qimagereader/tst_qimagereader.cpp @@ -245,6 +245,10 @@ void tst_QImageReader::readImage_data() QTest::newRow("MNG: ball") << QString("ball.mng") << true << QByteArray("mng"); QTest::newRow("MNG: fire") << QString("fire.mng") << true << QByteArray("mng"); #endif +#if defined QTEST_HAVE_SVG + QTest::newRow("SVG: rect") << QString("rect.svg") << true << QByteArray("svg"); + QTest::newRow("SVGZ: rect") << QString("rect.svgz") << true << QByteArray("svgz"); +#endif } void tst_QImageReader::readImage() @@ -294,7 +298,6 @@ void tst_QImageReader::readImage() QVERIFY(!image2Reader.format().isEmpty()); } QCOMPARE(image, image2); - do { QVERIFY2(!image.isNull(), io.errorString().toLatin1().constData()); } while (!(image = io.read()).isNull()); @@ -342,6 +345,10 @@ void tst_QImageReader::setScaledSize_data() QTest::newRow("MNG: ball") << "ball" << QSize(200, 200) << QByteArray("mng"); QTest::newRow("MNG: fire") << "fire" << QSize(200, 200) << QByteArray("mng"); #endif // QTEST_HAVE_MNG +#if defined QTEST_HAVE_SVG + QTest::newRow("SVG: rect") << "rect" << QSize(200, 200) << QByteArray("svg"); + QTest::newRow("SVGZ: rect") << "rect" << QSize(200, 200) << QByteArray("svgz"); +#endif } void tst_QImageReader::setScaledSize() @@ -409,6 +416,10 @@ void tst_QImageReader::setClipRect_data() QTest::newRow("MNG: ball") << "ball" << QRect(0, 0, 50, 50) << QByteArray("mng"); QTest::newRow("MNG: fire") << "fire" << QRect(0, 0, 50, 50) << QByteArray("mng"); #endif // QTEST_HAVE_MNG +#if defined QTEST_HAVE_SVG + QTest::newRow("SVG: rect") << "rect" << QRect(0, 0, 50, 50) << QByteArray("svg"); + QTest::newRow("SVGZ: rect") << "rect" << QRect(0, 0, 50, 50) << QByteArray("svgz"); +#endif } void tst_QImageReader::setClipRect() @@ -456,6 +467,10 @@ void tst_QImageReader::setScaledClipRect_data() QTest::newRow("MNG: ball") << "ball" << QRect(0, 0, 50, 50) << QByteArray("mng"); QTest::newRow("MNG: fire") << "fire" << QRect(0, 0, 50, 50) << QByteArray("mng"); #endif // QTEST_HAVE_MNG +#if defined QTEST_HAVE_SVG + QTest::newRow("SVG: rect") << "rect" << QRect(0, 0, 50, 50) << QByteArray("svg"); + QTest::newRow("SVGZ: rect") << "rect" << QRect(0, 0, 50, 50) << QByteArray("svgz"); +#endif } void tst_QImageReader::setScaledClipRect() @@ -509,6 +524,8 @@ void tst_QImageReader::imageFormat_data() QTest::newRow("png-2") << QString("YCbCr_cmyk.png") << QByteArray("png") << QImage::Format_RGB32; QTest::newRow("mng-1") << QString("ball.mng") << QByteArray("mng") << QImage::Format_Invalid; QTest::newRow("mng-2") << QString("fire.mng") << QByteArray("mng") << QImage::Format_Invalid; + QTest::newRow("svg") << QString("rect.svg") << QByteArray("svg") << QImage::Format_ARGB32_Premultiplied; + QTest::newRow("svgz") << QString("rect.svgz") << QByteArray("svgz") << QImage::Format_ARGB32_Premultiplied; } void tst_QImageReader::imageFormat() @@ -530,6 +547,10 @@ void tst_QImageReader::imageFormat() #ifndef QTEST_HAVE_MNG return; #endif // !QTEST_HAVE_MNG + if (QByteArray("svg") == format || QByteArray("svgz") == format) +#ifndef QTEST_HAVE_SVG + return; +#endif // !QTEST_HAVE_SVG QSKIP(("Qt does not support the " + format + " format.").constData(), SkipSingle); } else { QCOMPARE(QImageReader::imageFormat(prefix + fileName), format); @@ -604,6 +625,10 @@ void tst_QImageReader::setBackgroundColor_data() QTest::newRow("MNG: ball") << QString("ball.mng") << QColor(Qt::yellow); QTest::newRow("MNG: fire") << QString("fire.mng") << QColor(Qt::gray); #endif +#if defined QTEST_HAVE_SVG + QTest::newRow("SVG: rect") << QString("rect.svg") << QColor(Qt::darkGreen); + QTest::newRow("SVGZ: rect") << QString("rect.svgz") << QColor(Qt::darkGreen); +#endif } void tst_QImageReader::setBackgroundColor() @@ -641,6 +666,10 @@ void tst_QImageReader::supportsAnimation_data() QTest::newRow("MNG: ball") << QString("ball.mng") << true; QTest::newRow("MNG: fire") << QString("fire.mng") << true; #endif +#if defined QTEST_HAVE_SVG + QTest::newRow("SVG: rect") << QString("rect.svg") << false; + QTest::newRow("SVGZ: rect") << QString("rect.svgz") << false; +#endif } void tst_QImageReader::supportsAnimation() @@ -979,6 +1008,10 @@ void tst_QImageReader::readFromDevice_data() QTest::newRow("mng-1") << QString("ball.mng") << QByteArray("mng"); QTest::newRow("mng-2") << QString("fire.mng") << QByteArray("mng"); #endif // QTEST_HAVE_MNG +#if defined QTEST_HAVE_SVG + QTest::newRow("svg") << QString("rect.svg") << QByteArray("svg"); + QTest::newRow("svgz") << QString("rect.svgz") << QByteArray("svgz"); +#endif } void tst_QImageReader::readFromDevice() @@ -1059,6 +1092,10 @@ void tst_QImageReader::readFromFileAfterJunk_data() QTest::newRow("png") << QString("kollada.png") << QByteArray("png"); // QTest::newRow("mng-1") << QString("images/ball.mng") << QByteArray("mng"); // QTest::newRow("mng-2") << QString("images/fire.mng") << QByteArray("mng"); +#if defined QTEST_HAVE_SVG + QTest::newRow("svg") << QString("rect.svg") << QByteArray("svg"); + QTest::newRow("svgz") << QString("rect.svgz") << QByteArray("svgz"); +#endif } void tst_QImageReader::readFromFileAfterJunk() @@ -1081,7 +1118,7 @@ void tst_QImageReader::readFromFileAfterJunk() QVERIFY(!imageData.isNull()); int iterations = 10; - if (format == "ppm" || format == "pbm" || format == "pgm") + if (format == "ppm" || format == "pbm" || format == "pgm" || format == "svg" || format == "svgz") iterations = 1; if (format == "mng" || !QImageWriter::supportedImageFormats().contains(format)) { @@ -1233,6 +1270,20 @@ void tst_QImageReader::readFromResources_data() << QByteArray("mng") << QSize(32, 32) << QString(""); #endif +#ifdef QTEST_HAVE_SVG + QTest::newRow("rect.svg") << QString("rect.svg") + << QByteArray("svg") << QSize(105, 137) + << QString(""); + QTest::newRow("rect.svgz") << QString("rect.svgz") + << QByteArray("svgz") << QSize(105, 137) + << QString(""); + QTest::newRow("corrupt.svg") << QString("corrupt.svg") + << QByteArray("svg") << QSize(0, 0) + << QString(""); + QTest::newRow("corrupt.svgz") << QString("corrupt.svgz") + << QByteArray("svgz") << QSize(0, 0) + << QString(""); +#endif QTest::newRow("image.pbm") << QString("image.pbm") << QByteArray("pbm") << QSize(16, 6) << QString(""); @@ -1405,6 +1456,10 @@ void tst_QImageReader::readCorruptImage_data() #if defined QTEST_HAVE_TIFF QTest::newRow("corrupt tiff") << QString("corrupt-data.tif") << true << QString(""); #endif +#if defined QTEST_HAVE_SVG + QTest::newRow("corrupt svg") << QString("corrupt.svg") << true << QString(""); + QTest::newRow("corrupt svgz") << QString("corrupt.svgz") << true << QString(""); +#endif } void tst_QImageReader::readCorruptImage() @@ -1753,6 +1808,11 @@ void tst_QImageReader::testIgnoresFormatAndExtension_data() #if defined QTEST_HAVE_TIFF QTest::newRow("image_100dpi.tif") << "image_100dpi" << "tif" << "tiff"; #endif + +#if defined QTEST_HAVE_SVG + QTest::newRow("rect.svg") << "rect" << "svg" << "svg"; + QTest::newRow("rect.svgz") << "rect" << "svgz" << "svgz"; +#endif } diff --git a/tests/auto/qmetaobject/tst_qmetaobject.cpp b/tests/auto/qmetaobject/tst_qmetaobject.cpp index bb4a0d2..c0b1303 100644 --- a/tests/auto/qmetaobject/tst_qmetaobject.cpp +++ b/tests/auto/qmetaobject/tst_qmetaobject.cpp @@ -706,6 +706,12 @@ void tst_QMetaObject::normalizedSignature_data() QTest::newRow("const6") << "void foo(QList<const int>)" << "void foo(QList<const int>)"; QTest::newRow("const7") << "void foo(QList<const int*>)" << "void foo(QList<const int*>)"; QTest::newRow("const8") << "void foo(QList<int const*>)" << "void foo(QList<const int*>)"; + QTest::newRow("const9") << "void foo(const Foo<Bar>)" << "void foo(Foo<Bar>)"; + QTest::newRow("const10") << "void foo(Foo<Bar>const)" << "void foo(Foo<Bar>)"; + QTest::newRow("const11") << "void foo(Foo<Bar> *const)" << "void foo(Foo<Bar>*const)"; + QTest::newRow("const12") << "void foo(Foo<Bar>const*const *const)" << "void foo(Foo<Bar>*const*const)"; + QTest::newRow("const13") << "void foo(const Foo<Bar>&)" << "void foo(Foo<Bar>)"; + QTest::newRow("const14") << "void foo(Foo<Bar>const&)" << "void foo(Foo<Bar>)"; } void tst_QMetaObject::normalizedSignature() @@ -734,6 +740,14 @@ void tst_QMetaObject::normalizedType_data() QTest::newRow("template7") << "QList<QList<int> >" << "QList<QList<int> >"; QTest::newRow("value1") << "const QString &" << "QString"; QTest::newRow("value2") << "QString const &" << "QString"; + QTest::newRow("constInName1") << "constconst" << "constconst"; + QTest::newRow("constInName2") << "constconst*" << "constconst*"; + QTest::newRow("constInName3") << "const constconst&" << "constconst"; + QTest::newRow("constInName4") << "constconst const*const" << "constconst*const"; + QTest::newRow("class") << "const class foo&" << "foo"; + QTest::newRow("struct") << "const struct foo*" << "const foo*"; + QTest::newRow("struct2") << "struct foo const*" << "const foo*"; + QTest::newRow("enum") << "enum foo" << "foo"; } void tst_QMetaObject::normalizedType() diff --git a/tests/auto/qobject/tst_qobject.cpp b/tests/auto/qobject/tst_qobject.cpp index 8da3484..08b7c19 100644 --- a/tests/auto/qobject/tst_qobject.cpp +++ b/tests/auto/qobject/tst_qobject.cpp @@ -2088,6 +2088,9 @@ signals: void typePointerConstRefSignal(Class * const &); + void constTemplateSignal1( Template<int > ); + void constTemplateSignal2( Template< const int >); + public slots: void uintPointerSlot(uint *) { } void ulongPointerSlot(ulong *) { } @@ -2124,6 +2127,10 @@ public slots: void typeConstRefSlot(Template<Class const &> const &) {} void typePointerConstRefSlot(Class * const &) {} + + void constTemplateSlot1(Template<int > const) {} + void constTemplateSlot2(const Template<int > ) {} + void constTemplateSlot3(const Template< const int >) {} }; #include "oldnormalizeobject.h" @@ -2526,6 +2533,19 @@ void tst_QObject::normalize() QVERIFY(object.connect(&object, SIGNAL(typePointerConstRefSignal(Class*)), SLOT(typePointerConstRefSlot(Class*)))); + + QVERIFY( connect(&object, SIGNAL(constTemplateSignal1(Template <int>)), + &object , SLOT(constTemplateSlot1 (Template<int > ) ) )); + QVERIFY( connect(&object, SIGNAL(constTemplateSignal1(Template <int>)), + &object , SLOT(constTemplateSlot2 (Template<int > ) ) )); + QVERIFY( connect(&object, SIGNAL(constTemplateSignal2(Template <const int>)), + &object , SLOT(constTemplateSlot3(Template<int const > ) ) )); + + //type does not match + QTest::ignoreMessage(QtWarningMsg, "QObject::connect: Incompatible sender/receiver arguments\n" + " NormalizeObject::constTemplateSignal1(Template<int>) --> NormalizeObject::constTemplateSlot3(Template<const int>)"); + QVERIFY(!connect(&object, SIGNAL(constTemplateSignal1(Template <int>)), + &object , SLOT(constTemplateSlot3(Template<int const> ) ) )); } class SiblingDeleter : public QObject diff --git a/tests/auto/qscriptengine/tst_qscriptengine.cpp b/tests/auto/qscriptengine/tst_qscriptengine.cpp index 0615b63..3c6c7b2 100644 --- a/tests/auto/qscriptengine/tst_qscriptengine.cpp +++ b/tests/auto/qscriptengine/tst_qscriptengine.cpp @@ -161,6 +161,7 @@ private slots: void qRegExpInport_data(); void qRegExpInport(); + void reentrency(); }; tst_QScriptEngine::tst_QScriptEngine() @@ -4577,5 +4578,25 @@ void tst_QScriptEngine::qRegExpInport() } } +static QScriptValue createAnotherEngine(QScriptContext *, QScriptEngine *) +{ + QScriptEngine eng; + eng.evaluate("function foo(x, y) { return x + y; }" ); + eng.evaluate("hello = 5; world = 6" ); + return eng.evaluate("foo(hello,world)").toInt32(); +} + + +void tst_QScriptEngine::reentrency() +{ + QScriptEngine eng; + eng.globalObject().setProperty("foo", eng.newFunction(createAnotherEngine)); + eng.evaluate("function bar() { return foo(); } hello = 9; function getHello() { return hello; }"); + QCOMPARE(eng.evaluate("foo() + getHello() + foo()").toInt32(), 5+6 + 9 + 5+6); + QCOMPARE(eng.evaluate("foo").call().toInt32(), 5+6); + QCOMPARE(eng.evaluate("hello").toInt32(), 9); + QCOMPARE(eng.evaluate("foo() + hello").toInt32(), 5+6+9); +} + QTEST_MAIN(tst_QScriptEngine) #include "tst_qscriptengine.moc" diff --git a/tests/auto/qstatictext/tst_qstatictext.cpp b/tests/auto/qstatictext/tst_qstatictext.cpp index 16832ad..c7801ac 100644 --- a/tests/auto/qstatictext/tst_qstatictext.cpp +++ b/tests/auto/qstatictext/tst_qstatictext.cpp @@ -69,7 +69,7 @@ private slots: void drawToRect_data(); void drawToRect(); void setFont(); - void setMaximumSize(); + void setTextWidth(); void prepareToCorrectData(); void prepareToWrongData(); @@ -79,6 +79,12 @@ private slots: void projectedPainter(); void rotatedScaledAndTranslatedPainter(); void transformationChanged(); + + void plainTextVsRichText(); + + void setPenPlainText(); + void setPenRichText(); + void richTextOverridesPen(); }; void tst_QStaticText::init() @@ -121,7 +127,7 @@ void tst_QStaticText::drawToPoint() QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); text.setTextFormat(Qt::PlainText); text.setPerformanceHint(performanceHint); - p.drawStaticText(QPointF(11, 12), text); + p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text); } QCOMPARE(imageDrawStaticText, imageDrawText); @@ -150,12 +156,19 @@ void tst_QStaticText::drawToRect() imageDrawStaticText.fill(Qt::white); { QPainter p(&imageDrawStaticText); - QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.", QSizeF(10, 500)); + QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); + text.setTextWidth(10), + p.setClipRect(QRectF(11, 12, 10, 500)); text.setPerformanceHint(performanceHint); text.setTextFormat(Qt::PlainText); p.drawStaticText(QPointF(11, 12), text); } +#if defined(DEBUG_SAVE_IMAGE) + imageDrawText.save("drawToRect_imageDrawText.png"); + imageDrawStaticText.save("drawToRect_imageDrawStaticText.png"); +#endif + QCOMPARE(imageDrawStaticText, imageDrawText); } @@ -181,7 +194,7 @@ void tst_QStaticText::prepareToCorrectData() QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); text.prepare(transform, p.font()); text.setTextFormat(Qt::PlainText); - p.drawStaticText(QPointF(11, 12), text); + p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text); } if (!supportsTransformations()) @@ -209,7 +222,7 @@ void tst_QStaticText::prepareToWrongData() QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); text.prepare(transform, p.font()); text.setTextFormat(Qt::PlainText); - p.drawStaticText(QPointF(11, 12), text); + p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text); } QCOMPARE(imageDrawStaticText, imageDrawText); @@ -226,10 +239,10 @@ void tst_QStaticText::setFont() imageDrawText.fill(Qt::white); { QPainter p(&imageDrawText); - p.drawText(0, 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); + p.drawText(QRectF(0, 0, 1000, 1000), 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); p.setFont(font); - p.drawText(11, 120, "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); + p.drawText(QRectF(11, 120, 1000, 1000), 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); } QPixmap imageDrawStaticText(1000, 1000); @@ -247,10 +260,15 @@ void tst_QStaticText::setFont() p.drawStaticText(11, 120, text); } +#if defined(DEBUG_SAVE_IMAGE) + imageDrawText.save("setFont_imageDrawText.png"); + imageDrawStaticText.save("setFont_imageDrawStaticText.png"); +#endif + QCOMPARE(imageDrawStaticText, imageDrawText); } -void tst_QStaticText::setMaximumSize() +void tst_QStaticText::setTextWidth() { QPixmap imageDrawText(1000, 1000); imageDrawText.fill(Qt::white); @@ -264,7 +282,8 @@ void tst_QStaticText::setMaximumSize() { QPainter p(&imageDrawStaticText); QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); - text.setMaximumSize(QSizeF(10, 500)); + text.setTextWidth(10); + p.setClipRect(QRectF(11, 12, 10, 500)); p.drawStaticText(QPointF(11, 12), text); } @@ -291,7 +310,7 @@ void tst_QStaticText::translatedPainter() QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); text.setTextFormat(Qt::PlainText); - p.drawStaticText(QPointF(11, 12), text); + p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text); } QCOMPARE(imageDrawStaticText, imageDrawText); @@ -323,7 +342,7 @@ void tst_QStaticText::rotatedPainter() { QPainter p(&imageDrawText); p.rotate(30.0); - p.drawText(0, 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); + p.drawText(QRectF(0, 0, 1000, 100), 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); } QPixmap imageDrawStaticText(1000, 1000); @@ -367,7 +386,7 @@ void tst_QStaticText::scaledPainter() QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); text.setTextFormat(Qt::PlainText); - p.drawStaticText(QPointF(11, 12), text); + p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text); } if (!supportsTransformations()) @@ -398,7 +417,7 @@ void tst_QStaticText::projectedPainter() QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); text.setTextFormat(Qt::PlainText); - p.drawStaticText(QPointF(11, 12), text); + p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text); } QCOMPARE(imageDrawStaticText, imageDrawText); @@ -428,7 +447,7 @@ void tst_QStaticText::rotatedScaledAndTranslatedPainter() QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); text.setTextFormat(Qt::PlainText); - p.drawStaticText(QPointF(11, 12), text); + p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text); } #if defined(DEBUG_SAVE_IMAGE) @@ -450,10 +469,10 @@ void tst_QStaticText::transformationChanged() p.rotate(33.0); p.scale(0.5, 0.7); - p.drawText(0, 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); + p.drawText(QRectF(0, 0, 1000, 1000), 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); p.scale(7.0, 5.0); - p.drawText(0, 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); + p.drawText(QRectF(0, 0, 1000, 1000), 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); } QPixmap imageDrawStaticText(1000, 1000); @@ -482,5 +501,125 @@ void tst_QStaticText::transformationChanged() QCOMPARE(imageDrawStaticText, imageDrawText); } +void tst_QStaticText::plainTextVsRichText() +{ + QPixmap imagePlainText(1000, 1000); + imagePlainText.fill(Qt::white); + { + QPainter p(&imagePlainText); + + QStaticText staticText; + staticText.setText("FOObar"); + staticText.setTextFormat(Qt::PlainText); + + p.drawStaticText(10, 10, staticText); + } + + QPixmap imageRichText(1000, 1000); + imageRichText.fill(Qt::white); + { + QPainter p(&imageRichText); + + QStaticText staticText; + staticText.setText("<html><body>FOObar</body></html>"); + staticText.setTextFormat(Qt::RichText); + + p.drawStaticText(10, 10, staticText); + } + +#if defined(DEBUG_SAVE_IMAGE) + imagePlainText.save("plainTextVsRichText_imagePlainText.png"); + imageRichText.save("plainTextVsRichText_imageRichText.png"); +#endif + + QCOMPARE(imagePlainText, imageRichText); +} + +void tst_QStaticText::setPenPlainText() +{ + QFont font = QApplication::font(); + font.setStyleStrategy(QFont::NoAntialias); + + QFontMetricsF fm(font); + QPixmap image(qCeil(fm.width("XXXXX")), qCeil(fm.height())); + image.fill(Qt::white); + { + QPainter p(&image); + p.setFont(font); + p.setPen(Qt::green); + + QStaticText staticText("XXXXX"); + staticText.setTextFormat(Qt::PlainText); + p.drawStaticText(0, fm.ascent(), staticText); + } + + QImage img = image.toImage(); + for (int x=0; x<img.width(); ++x) { + for (int y=0; y<img.height(); ++y) { + QRgb pixel = img.pixel(x, y); + QVERIFY(pixel == QColor(Qt::white).rgba() + || pixel == QColor(Qt::green).rgba()); + } + } +} + +void tst_QStaticText::setPenRichText() +{ + QFont font = QApplication::font(); + font.setStyleStrategy(QFont::NoAntialias); + + QFontMetricsF fm(font); + QPixmap image(qCeil(fm.width("XXXXX")), qCeil(fm.height())); + image.fill(Qt::white); + { + QPainter p(&image); + p.setFont(font); + p.setPen(Qt::green); + + QStaticText staticText; + staticText.setText("<html><body>XXXXX</body></html>"); + staticText.setTextFormat(Qt::RichText); + p.drawStaticText(0, 0, staticText); + } + + QImage img = image.toImage(); + for (int x=0; x<img.width(); ++x) { + for (int y=0; y<img.height(); ++y) { + QRgb pixel = img.pixel(x, y); + QVERIFY(pixel == QColor(Qt::white).rgba() + || pixel == QColor(Qt::green).rgba()); + } + } +} + +void tst_QStaticText::richTextOverridesPen() +{ + QFont font = QApplication::font(); + font.setStyleStrategy(QFont::NoAntialias); + + QFontMetricsF fm(font); + QPixmap image(qCeil(fm.width("XXXXX")), qCeil(fm.height())); + image.fill(Qt::white); + { + QPainter p(&image); + p.setFont(font); + p.setPen(Qt::green); + + QStaticText staticText; + staticText.setText("<html><body><font color=\"#ff0000\">XXXXX</font></body></html>"); + staticText.setTextFormat(Qt::RichText); + p.drawStaticText(0, 0, staticText); + } + + QImage img = image.toImage(); + for (int x=0; x<img.width(); ++x) { + for (int y=0; y<img.height(); ++y) { + QRgb pixel = img.pixel(x, y); + QVERIFY(pixel == QColor(Qt::white).rgba() + || pixel == QColor(Qt::red).rgba()); + } + } +} + QTEST_MAIN(tst_QStaticText) #include "tst_qstatictext.moc" diff --git a/tests/auto/qtextcodec/tst_qtextcodec.cpp b/tests/auto/qtextcodec/tst_qtextcodec.cpp index 1c64ade..4e7123f 100644 --- a/tests/auto/qtextcodec/tst_qtextcodec.cpp +++ b/tests/auto/qtextcodec/tst_qtextcodec.cpp @@ -2178,6 +2178,45 @@ void tst_QTextCodec::moreToFromUnicode_data() { koi8_u_ba.append(x); } QTest::newRow("KOI8-U") << QByteArray("KOI8-U") << koi8_u_ba; + + + QByteArray big5_ba; + for (unsigned char u=0xa1; u<=0xf9; u++) { + if (u==0xc8) { + continue; + } + for (unsigned char v=0x40; v<=0x7e; v++) { + big5_ba.append(u); + big5_ba.append(v); + } + unsigned char v_up; + switch (u) { + case 0xa3: v_up=0xbf; break; + case 0xc7: v_up=0xfc; break; + case 0xf9: v_up=0xd5; break; + default: v_up=0xfe; + } + + for (unsigned char v=0xa1; v<=v_up; v++) { + if (u==0xa2 && (v==0xcc || v==0xce)) { + continue; + } + big5_ba.append(u); + big5_ba.append(v); + } + } + + QTest::newRow("BIG5") << QByteArray("BIG5") << big5_ba; + + QByteArray gb2312_ba; + for (unsigned char u=0xa1; u<=0xf7; u++) { + for (unsigned char v=0xa1; v<=0xfe; v++) { + gb2312_ba.append(u); + gb2312_ba.append(v); + } + } + + QTest::newRow("GB2312") << QByteArray("GB2312") << gb2312_ba; } void tst_QTextCodec::moreToFromUnicode() diff --git a/tests/auto/qtextscriptengine/tst_qtextscriptengine.cpp b/tests/auto/qtextscriptengine/tst_qtextscriptengine.cpp index 841f5b9..018c036 100644 --- a/tests/auto/qtextscriptengine/tst_qtextscriptengine.cpp +++ b/tests/auto/qtextscriptengine/tst_qtextscriptengine.cpp @@ -1068,6 +1068,42 @@ void tst_QTextScriptEngine::greek() QSKIP("couln't find DejaVu Sans", SkipAll); } } + + { + if (QFontDatabase().families(QFontDatabase::Any).contains("SBL Greek")) { + QFont f("SBL Greek"); + for (int uc = 0x1f00; uc <= 0x1fff; ++uc) { + QString str; + str.append(uc); + if (str.normalized(QString::NormalizationForm_D).normalized(QString::NormalizationForm_C) != str) { + //qDebug() << "skipping" << hex << uc; + continue; + } + if (uc == 0x1fc1 || uc == 0x1fed) + continue; + QVERIFY( decomposedShaping(f, QChar(uc) ) ); + + } + + const ShapeTable shape_table [] = { + { { 0x3b1, 0x300, 0x313, 0x0 }, + { 0xb8, 0x3d3, 0x3c7, 0x0 } }, + { { 0x3b1, 0x313, 0x300, 0x0 }, + { 0xd4, 0x0 } }, + + { {0}, {0} } + }; + + + const ShapeTable *s = shape_table; + while (s->unicode[0]) { + QVERIFY( shaping(f, s) ); + ++s; + } + } else { + QSKIP("couln't find SBL_grk", SkipAll); + } + } #else QSKIP("X11 specific test", SkipAll); #endif diff --git a/tests/auto/qtreeview/tst_qtreeview.cpp b/tests/auto/qtreeview/tst_qtreeview.cpp index bdc0a0c..2de189d 100644 --- a/tests/auto/qtreeview/tst_qtreeview.cpp +++ b/tests/auto/qtreeview/tst_qtreeview.cpp @@ -237,6 +237,7 @@ private slots: void task245654_changeModelAndExpandAll(); void doubleClickedWithSpans(); void taskQTBUG_6450_selectAllWith1stColumnHidden(); + void taskQTBUG_9216_setSizeAndUniformRowHeightsWrongRepaint(); }; class QtTestModel: public QAbstractItemModel @@ -3714,5 +3715,43 @@ void tst_QTreeView::taskQTBUG_6450_selectAllWith1stColumnHidden() QVERIFY(tree.selectionModel()->isRowSelected(i, QModelIndex())); } +class TreeViewQTBUG_9216 : public QTreeView +{ + Q_OBJECT +public: + void paintEvent(QPaintEvent *event) + { + if (doCompare) + QCOMPARE(event->rect(), viewport()->rect()); + QTreeView::paintEvent(event); + painted++; + } + int painted; + bool doCompare; +}; + +void tst_QTreeView::taskQTBUG_9216_setSizeAndUniformRowHeightsWrongRepaint() +{ + QStandardItemModel model(10, 10, this); + for (int row = 0; row < 10; row++) + for (int col = 0; col < 10; col++) + model.setItem(row, col, new QStandardItem(QString("row %0, col %1").arg(row).arg(col))); + TreeViewQTBUG_9216 view; + view.setUniformRowHeights(true); + view.setModel(&model); + view.painted = 0; + view.doCompare = false; + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(view.painted > 0); + + QTest::qWait(100); // This one is needed to make the test fail before the patch. + view.painted = 0; + view.doCompare = true; + model.setData(model.index(0, 0), QVariant(QSize(50, 50)), Qt::SizeHintRole); + QTest::qWait(100); + QTRY_VERIFY(view.painted > 0); +} + QTEST_MAIN(tst_QTreeView) #include "tst_qtreeview.moc" diff --git a/tests/auto/qvarlengtharray/tst_qvarlengtharray.cpp b/tests/auto/qvarlengtharray/tst_qvarlengtharray.cpp index 1c43069..5708726 100644 --- a/tests/auto/qvarlengtharray/tst_qvarlengtharray.cpp +++ b/tests/auto/qvarlengtharray/tst_qvarlengtharray.cpp @@ -133,6 +133,12 @@ void tst_QVarLengthArray::oldTests() QVERIFY(sa.data() == &sa[0]); QVERIFY(sa[0] == 0xfee); QVERIFY(sa[10] == 0xff); + QVERIFY(sa.at(0) == 0xfee); + QVERIFY(sa.at(10) == 0xff); + QVERIFY(sa.value(0) == 0xfee); + QVERIFY(sa.value(10) == 0xff); + QVERIFY(sa.value(1000) == 0); + QVERIFY(sa.value(1000, 12) == 12); QVERIFY(sa.size() == 512); sa.reserve(1024); QVERIFY(sa.capacity() == 1024); @@ -168,6 +174,13 @@ void tst_QVarLengthArray::oldTests() QCOMPARE(sa.size(), 12); QCOMPARE(sa[10], QString("hello")); QCOMPARE(sa[11], QString("world")); + QCOMPARE(sa.at(10), QString("hello")); + QCOMPARE(sa.at(11), QString("world")); + QCOMPARE(sa.value(10), QString("hello")); + QCOMPARE(sa.value(11), QString("world")); + QCOMPARE(sa.value(10000), QString()); + QCOMPARE(sa.value(1212112, QString("none")), QString("none")); + QCOMPARE(sa.value(-12, QString("neg")), QString("neg")); sa.append(arr, 1); QCOMPARE(sa.size(), 13); diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp index 0b14cba..dfd1196 100644 --- a/tools/configure/configureapp.cpp +++ b/tools/configure/configureapp.cpp @@ -3041,6 +3041,10 @@ void Configure::generateConfigfiles() if(dictionary["S60"] == "no") qconfigList += "QT_NO_S60"; if(dictionary["NATIVE_GESTURES"] == "no") qconfigList += "QT_NO_NATIVE_GESTURES"; + if(dictionary["OPENGL_ES_CM"] == "no" && + dictionary["OPENGL_ES_2"] == "no" && + dictionary["OPENVG"] == "no") qconfigList += "QT_NO_EGL"; + if(dictionary["OPENGL_ES_CM"] == "yes" || dictionary["OPENGL_ES_2"] == "yes") qconfigList += "QT_OPENGL_ES"; diff --git a/tools/qdoc3/doc/examples/main.cpp b/tools/qdoc3/doc/examples/main.cpp index e2cf6c5..e439d3e 100644 --- a/tools/qdoc3/doc/examples/main.cpp +++ b/tools/qdoc3/doc/examples/main.cpp @@ -46,7 +46,7 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - QPushButton *hello("Hello world!"); + QPushButton hello("Hello world!"); hello.resize(100, 30); hello.show(); diff --git a/tools/qdoc3/doc/qdoc-manual.qdoc b/tools/qdoc3/doc/qdoc-manual.qdoc index e2f670c..482af46 100644 --- a/tools/qdoc3/doc/qdoc-manual.qdoc +++ b/tools/qdoc3/doc/qdoc-manual.qdoc @@ -78,10 +78,10 @@ \endlist \o \l{QDoc Configuration} \list - \o \l{General Variables} + \o \l{General Configuration Variables} \o \l{Creating Help Project Files} - \o \l{C++ Specific Variables} - \o \l{HTML Specific Variables} + \o \l{C++ Specific Configuration Variables} + \o \l{HTML Specific Configuration Variables} \o \l{Supporting Derived Projects} \o \l{QDoc Compatibility} \o \l{qt.qdocconf} @@ -1425,6 +1425,7 @@ \quotefromfile examples/main.cpp + \skipto QApplication \printline QApplication This line includes the QApplication class @@ -1652,6 +1653,7 @@ can press and release. \quotefromfile examples/main.cpp + \skipto QApplication \skipline QApplication \printline QPushButton @@ -6974,7 +6976,7 @@ \page 21-0-qdoc-configuration.html \previouspage Title Commands \contentspage QDoc Manual - Table of Contents - \nextpage General Variables + \nextpage General Configuration Variables \title QDoc Configuration @@ -7094,9 +7096,9 @@ \section2 Categories \list - \o \l {General Variables} - \o \l {C++ Specific Variables} - \o \l {HTML Specific Variables} + \o \l {General Configuration Variables} + \o \l {C++ Specific Configuration Variables} + \o \l {HTML Specific Configuration Variables} \endlist \section1 Configuration File Examples @@ -7133,7 +7135,7 @@ \contentspage QDoc Manual - Table of Contents \nextpage Creating Help Project Files - \title General Variables + \title General Configuration Variables With the general QDoc configuration variables, you can define where QDoc will find the various source files it needs to generate @@ -7215,7 +7217,7 @@ QDoc originally used a hard-coded value of four spaces for code indentation to ensure that code snippets could be easily distinguished from surrounding text. Since we can use - \l{HTML Specific Variables#HTML.stylesheets}{stylesheets} to + \l{HTML Specific Configuration Variables#HTML.stylesheets}{stylesheets} to adjust the appearance of certain types of HTML elements, this level of indentation is not always required. @@ -7572,7 +7574,7 @@ \code header.fileextensions += *.H \endcode - + \warning The above assignment may not work as described. See also \l headerdirs. @@ -7730,6 +7732,27 @@ bold font, and that \\raisedaster renders a '*'. \row + \o \bold naturallanguage \target naturallanguage + \o \bold {The \c naturallanguage variable specifies the natural + language used for the documentation generated by qdoc.} + + For example: + + \code + naturallanguage = zh-Hans + \endcode + + By default, the natural language is \c en for compatibility + with legacy documentation. + + qdoc will add the natural language information to the HTML + it generates, using the \c lang and \c xml:lang attributes. + + See also \l sourceencoding, \l outputencoding, + \l{http://www.w3.org/TR/xhtml1/#C_7}{C.7. The lang and xml:lang Attributes} and + \l{http://www.w3.org/TR/i18n-html-tech-lang/#ri20040429.113217290}{Best Practice 13: Using Hans and Hant codes}. + + \row \o \bold outputdir \target outputdir \o \bold {The \c outputdir variable specifies the directory where QDoc will put the generated documentation.} @@ -7754,6 +7777,31 @@ directory, all files from the previous run will be lost. \row + \o \bold outputencoding \target outputencoding + \o \bold {The \c outputencoding variable specifies the encoding + used for the documentation generated by qdoc.} + + For example: + + \code + outputencoding = UTF-8 + \endcode + + By default, the output encoding is \c ISO-8859-1 (Latin1) for + compatibility with legacy documentation. When generating + documentation for some languages, particularly non-European + languages, this is not sufficient and an encoding such as UTF-8 + is required. + + qdoc will encode HTML using this encoding and generate the + correct declarations to indicate to browsers which encoding + is being used. The \l naturallanguage configuration variable + should also be specified to provide browsers with a complete + set of character encoding and language information. + + See also \l outputencoding and \l naturallanguage. + + \row \o \bold outputformats \target outputformats \o \bold {The \c outputformats variable specifies the format of the generated documentation.} @@ -7831,6 +7879,30 @@ See also \l sources and \l sources.fileextensions. \row + \o \bold sourceencoding \target sourceencoding + \o \bold {The \c sourceencoding variable specifies the encoding + used for the source code and documentation.} + + For example: + + \code + sourceencoding = UTF-8 + \endcode + + By default, the source encoding is \c ISO-8859-1 (Latin1) for + compatibility with legacy documentation. For some languages, + particularly non-European languages, this is not sufficient + and an encoding such as UTF-8 is required. + + Although qdoc will use the encoding to read source and + documentation files, limitations of C++ compilers may prevent + you from using non-ASCII characters in source code comments. + In cases like these, it is possible to write API documentation + completely in documentation files. + + See also \l naturallanguage and \l outputencoding. + + \row \o \bold sources \target sources \o \bold {The \c sources variable allows you to specify individual source files in addition to those located in the @@ -7973,9 +8045,9 @@ /*! \page 22-creating-help-project-files.html - \previouspage General Variables + \previouspage General Configuration Variables \contentspage QDoc Manual - Table of Contents - \nextpage C++ Specific Variables + \nextpage C++ Specific Configuration Variables \title Creating Help Project Files @@ -8023,9 +8095,9 @@ \page 23-qdoc-configuration-cppvariables.html \previouspage Creating Help Project Files \contentspage QDoc Manual - Table of Contents - \nextpage HTML Specific Variables + \nextpage HTML Specific Configuration Variables - \title C++ Specific Variables + \title C++ Specific Configuration Variables The C++ specific configuration variables are provided to avoid erroneous documentation due to non-standard C++ constructs. @@ -8159,11 +8231,11 @@ /*! \page 24-qdoc-configuration-htmlvariables.html - \previouspage C++ Specific Variables + \previouspage C++ Specific Configuration Variables \contentspage QDoc Manual - Table of Contents \nextpage Supporting Derived Projects - \title HTML Specific Variables + \title HTML Specific Configuration Variables The HTML specific configuration variables define the generated documentation's style, or define the contents of the @@ -8289,7 +8361,7 @@ /*! \page 25-qdoc-configuration-derivedprojects.html - \previouspage HTML Specific Variables + \previouspage HTML Specific Configuration Variables \contentspage QDoc Manual - Table of Contents \nextpage QDoc Compatibility diff --git a/tools/qdoc3/generator.cpp b/tools/qdoc3/generator.cpp index 62f191b..3f955bf 100644 --- a/tools/qdoc3/generator.cpp +++ b/tools/qdoc3/generator.cpp @@ -322,11 +322,11 @@ void Generator::generateBody(const Node *node, CodeMarker *marker) bool quiet = false; if (node->type() == Node::Function) { -#if 0 +#if 0 const FunctionNode *func = (const FunctionNode *) node; if (func->isOverload() && func->metaness() != FunctionNode::Ctor) generateOverload(node, marker); -#endif +#endif } else if (node->type() == Node::Fake) { const FakeNode *fake = static_cast<const FakeNode *>(node); @@ -347,7 +347,7 @@ void Generator::generateBody(const Node *node, CodeMarker *marker) if (func->reimplementedFrom() != 0) generateReimplementedFrom(func, marker); } - + if (!generateText(node->doc().body(), node, marker)) if (node->isReimp()) return; @@ -452,7 +452,7 @@ void Generator::generateBody(const Node *node, CodeMarker *marker) // Now we put this at the top, before the other text. if (func->reimplementedFrom() != 0) generateReimplementedFrom(func, marker); -#endif +#endif } } @@ -544,7 +544,7 @@ void Generator::generateInheritedBy(const ClassNode *classe, example is being formatted. It outputs the list of source files comprising the example, and the list of images used by the example. The images are copied into a subtree of - \c{...doc/html/images/used-in-examples/...} + \c{...doc/html/images/used-in-examples/...} */ void Generator::generateFileList(const FakeNode* fake, CodeMarker* marker, @@ -946,7 +946,7 @@ void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker) } ++c; } - if (!exceptions) + if (!exceptions) text << "."; else if (threadSafeness == Node::Reentrant) { if (nonreentrant.isEmpty()) { @@ -1033,7 +1033,7 @@ void Generator::generateOverload(const Node *node, CodeMarker *marker) text << Atom::ParaLeft << "This function overloads "; QString t = node->name() + "()"; - text << Atom::AutoLink << t + text << Atom::AutoLink << t << Atom::ParaRight; generateText(text, node, marker); } diff --git a/tools/qdoc3/node.cpp b/tools/qdoc3/node.cpp index ef75f6e..a2bd948 100644 --- a/tools/qdoc3/node.cpp +++ b/tools/qdoc3/node.cpp @@ -415,7 +415,7 @@ void InnerNode::normalizeOverloads() /*! */ -void InnerNode::removeFromRelated() +void InnerNode::removeFromRelated() { while (!related.isEmpty()) { Node *p = static_cast<Node *>(related.takeFirst()); @@ -456,7 +456,7 @@ const Node *InnerNode::findNode(const QString& name, Type type) const } /*! - Find the function node in this node that has the given \a name. + Find the function node in this node that has the given \a name. */ const FunctionNode *InnerNode::findFunctionNode(const QString& name) const { @@ -973,7 +973,7 @@ Parameter::Parameter(const Parameter& p) /*! Assigning Parameter \a p to this Parameter copies the - strings across. + strings across. */ Parameter& Parameter::operator=(const Parameter& p) { @@ -1319,7 +1319,7 @@ void QmlClassNode::clear() */ QString QmlClassNode::fileBase() const { -#if 0 +#if 0 if (Node::fileBase() == "item") qDebug() << "FILEBASE: qmlitem" << name(); return "qml_" + Node::fileBase(); @@ -1368,7 +1368,7 @@ QmlBasicTypeNode::QmlBasicTypeNode(InnerNode *parent, /*! Constructor for the Qml property group node. \a parent is - always a QmlClassNode. + always a QmlClassNode. */ QmlPropGroupNode::QmlPropGroupNode(QmlClassNode* parent, const QString& name, diff --git a/tools/qtestlib/wince/cetest/cetcpsyncconnection.cpp b/tools/qtestlib/wince/cetest/cetcpsyncconnection.cpp index 99219f3..5494194 100644 --- a/tools/qtestlib/wince/cetest/cetcpsyncconnection.cpp +++ b/tools/qtestlib/wince/cetest/cetcpsyncconnection.cpp @@ -198,3 +198,24 @@ bool CeTcpSyncConnection::fileCreationTime(const QString &fileName, FILETIME* de file.remove(); return result; } + +bool CeTcpSyncConnection::resetDevice() +{ + qWarning("CeTcpSyncConnection::resetDevice not implemented"); + return false; +} + +bool CeTcpSyncConnection::toggleDevicePower(int *returnValue) +{ + Q_UNUSED(returnValue); + qWarning("CeTcpSyncConnection::toggleDevicePower not implemented"); + return false; +} + +bool CeTcpSyncConnection::setDeviceAwake(bool activate, int *returnValue) +{ + Q_UNUSED(activate); + Q_UNUSED(returnValue); + qWarning("CeTcpSyncConnection::setDeviceAwake not implemented"); + return false; +} diff --git a/tools/qtestlib/wince/cetest/cetcpsyncconnection.h b/tools/qtestlib/wince/cetest/cetcpsyncconnection.h index 77539cc..e603efc 100644 --- a/tools/qtestlib/wince/cetest/cetcpsyncconnection.h +++ b/tools/qtestlib/wince/cetest/cetcpsyncconnection.h @@ -75,6 +75,9 @@ public: bool createDirectory(const QString&, bool deleteBefore=false); bool execute(QString program, QString arguments = QString(), int timeout = -1, int *returnValue = NULL); + bool resetDevice(); + bool toggleDevicePower(int *returnValue = NULL); + bool setDeviceAwake(bool activate, int *returnValue = NULL); private: bool connected; }; diff --git a/tools/qtestlib/wince/cetest/cetest.pro b/tools/qtestlib/wince/cetest/cetest.pro index 2773fe4..43ed18e 100644 --- a/tools/qtestlib/wince/cetest/cetest.pro +++ b/tools/qtestlib/wince/cetest/cetest.pro @@ -13,7 +13,8 @@ DEFINES += QT_BUILD_QMAKE QT_BOOTSTRAPPED QT_NO_CODECS QT_LITE_UNICODE QT QT_NO_STL QT_NO_COMPRESS QT_NO_DATASTREAM \ QT_NO_TEXTCODEC QT_NO_UNICODETABLES QT_NO_THREAD \ QT_NO_SYSTEMLOCALE QT_NO_GEOM_VARIANT \ - QT_NODLL QT_NO_QOBJECT + QT_NODLL QT_NO_QOBJECT \ + QT_BUILD_QMAKE_NO_GENERATORS INCLUDEPATH = \ $$QT_SOURCE_TREE/tools/qtestlib/ce/cetest \ @@ -36,8 +37,6 @@ HEADERS += \ SOURCES += \ remoteconnection.cpp \ deployment.cpp \ - symbian/epocroot.cpp \ - windows/registry.cpp \ main.cpp LIBS += ole32.lib advapi32.lib diff --git a/tools/qttracereplay/main.cpp b/tools/qttracereplay/main.cpp index be7906b..101d512 100644 --- a/tools/qttracereplay/main.cpp +++ b/tools/qttracereplay/main.cpp @@ -49,7 +49,7 @@ class ReplayWidget : public QWidget { Q_OBJECT public: - ReplayWidget(const QString &filename, int from, int to, bool single); + ReplayWidget(const QString &filename, int from, int to, bool single, int frame); void paintEvent(QPaintEvent *event); void resizeEvent(QResizeEvent *event); @@ -66,27 +66,96 @@ public: QTime timer; QList<uint> visibleUpdates; - QList<uint> iterationTimes; + + QVector<uint> iterationTimes; QString filename; int from; int to; bool single; + + int frame; + int currentCommand; }; void ReplayWidget::updateRect() { - if (!visibleUpdates.isEmpty()) + if (frame >= 0 && !updates.isEmpty()) + update(updates.at(frame)); + else if (!visibleUpdates.isEmpty()) update(updates.at(visibleUpdates.at(currentFrame))); } +const int singleFrameRepeatsPerCommand = 100; +const int singleFrameIterations = 4; + void ReplayWidget::paintEvent(QPaintEvent *) { QPainter p(this); + QTimer::singleShot(0, this, SLOT(updateRect())); + // p.setClipRegion(frames.at(currentFrame).updateRegion); + if (frame >= 0) { + int start = buffer.frameStartIndex(frame); + int end = buffer.frameEndIndex(frame); + + iterationTimes.resize(end - start); + + int saveRestoreStackDepth = buffer.processCommands(&p, start, start + currentCommand); + + for (int i = 0; i < saveRestoreStackDepth; ++i) + p.restore(); + + const int repeats = currentIteration >= 3 ? singleFrameRepeatsPerCommand : 1; + + ++currentFrame; + if (currentFrame == repeats) { + currentFrame = 0; + if (currentIteration >= 3) { + iterationTimes[currentCommand - 1] = qMin(iterationTimes[currentCommand - 1], uint(timer.elapsed())); + timer.restart(); + } + + if (currentIteration >= singleFrameIterations + 3) { + printf(" # | ms | description\n"); + printf("------+---------+------------------------------------------------------------\n"); + + qSort(iterationTimes); + + int sum = 0; + for (int i = 0; i < iterationTimes.size(); ++i) { + int delta = iterationTimes.at(i); + if (i > 0) + delta -= iterationTimes.at(i-1); + sum += delta; + qreal deltaF = delta / qreal(repeats); + printf("%.5d | %.5f | %s\n", i, deltaF, qPrintable(buffer.commandDescription(start + i))); + } + printf("Total | %.5f | Total frame time\n", sum / qreal(repeats)); + deleteLater(); + return; + } + + if (start + currentCommand >= end) { + currentCommand = 1; + ++currentIteration; + if (currentIteration == 3) { + timer.start(); + iterationTimes.fill(uint(-1)); + } + if (currentIteration >= 3 && currentIteration < singleFrameIterations + 3) + printf("Profiling iteration %d of %d\n", currentIteration - 2, singleFrameIterations); + } else { + ++currentCommand; + } + } + + return; + } + buffer.draw(&p, visibleUpdates.at(currentFrame)); ++currentFrame; @@ -138,11 +207,9 @@ void ReplayWidget::paintEvent(QPaintEvent *) } } } - - QTimer::singleShot(0, this, SLOT(updateRect())); } -void ReplayWidget::resizeEvent(QResizeEvent *event) +void ReplayWidget::resizeEvent(QResizeEvent *) { visibleUpdates.clear(); @@ -162,13 +229,15 @@ void ReplayWidget::resizeEvent(QResizeEvent *event) } -ReplayWidget::ReplayWidget(const QString &filename_, int from_, int to_, bool single_) +ReplayWidget::ReplayWidget(const QString &filename_, int from_, int to_, bool single_, int frame_) : currentFrame(0) , currentIteration(0) , filename(filename_) , from(from_) , to(to_) , single(single_) + , frame(frame_) + , currentCommand(1) { setWindowTitle(filename); QFile file(filename); @@ -216,7 +285,8 @@ int main(int argc, char **argv) printf("Usage:\n > %s [OPTIONS] [traceFile]\n", argv[0]); printf("OPTIONS\n" " --range=from-to to specify a frame range.\n" - " --singlerun to do only one run (without statistics)\n"); + " --singlerun to do only one run (without statistics)\n" + " --instrumentframe=frame to instrument a single frame\n"); return 1; } @@ -228,6 +298,8 @@ int main(int argc, char **argv) bool single = false; + int frame = -1; + int from = 0; int to = -1; for (int i = 1; i < app.arguments().size() - 1; ++i) { @@ -253,13 +325,22 @@ int main(int argc, char **argv) } } else if (arg == QLatin1String("--singlerun")) { single = true; + } else if (arg.startsWith(QLatin1String("--instrumentframe="))) { + QString rest = arg.mid(18); + bool ok = false; + int frameCandidate = rest.toInt(&ok); + if (ok) { + frame = frameCandidate; + } else { + printf("ERROR: malformed syntax in argument %s\n", qPrintable(arg)); + } } else { printf("Unrecognized argument: %s\n", qPrintable(arg)); return 1; } } - ReplayWidget *widget = new ReplayWidget(app.arguments().last(), from, to, single); + ReplayWidget *widget = new ReplayWidget(app.arguments().last(), from, to, single, frame); if (!widget->updates.isEmpty()) { widget->show(); |