diff options
Diffstat (limited to 'src/gui/painting')
100 files changed, 7348 insertions, 4775 deletions
diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri index a6cc9c7..bd37d9f 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 @@ -116,12 +118,14 @@ embedded { } else { HEADERS += \ painting/qgraphicssystem_raster_p.h \ + painting/qgraphicssystem_runtime_p.h \ painting/qgraphicssystemfactory_p.h \ painting/qgraphicssystemplugin_p.h \ painting/qwindowsurface_raster_p.h \ SOURCES += \ painting/qgraphicssystem_raster.cpp \ + painting/qgraphicssystem_runtime.cpp \ painting/qgraphicssystemfactory.cpp \ painting/qgraphicssystemplugin.cpp \ painting/qwindowsurface_raster.cpp \ @@ -198,154 +202,18 @@ x11|embedded { DEFINES += QT_NO_CUPS QT_NO_LPR } -contains(QMAKE_MAC_XARCH, no) { - DEFINES += QT_NO_MAC_XARCH -} else:if(mmx|3dnow|sse|sse2|iwmmxt) { +if(mmx|3dnow|sse|sse2|iwmmxt) { HEADERS += painting/qdrawhelper_x86_p.h \ painting/qdrawhelper_mmx_p.h \ - painting/qdrawhelper_sse_p.h - mmx { - DEFINES += QT_HAVE_MMX - MMX_SOURCES += painting/qdrawhelper_mmx.cpp - } - 3dnow { - DEFINES += QT_HAVE_3DNOW - MMX3DNOW_SOURCES += painting/qdrawhelper_mmx3dnow.cpp - sse { - SSE3DNOW_SOURCES += painting/qdrawhelper_sse3dnow.cpp - } - } - sse { - DEFINES += QT_HAVE_SSE - SSE_SOURCES += painting/qdrawhelper_sse.cpp - - DEFINES += QT_HAVE_MMXEXT - } - sse2 { - DEFINES += QT_HAVE_SSE2 - SSE2_SOURCES += painting/qdrawhelper_sse2.cpp - } - iwmmxt { - DEFINES += QT_HAVE_IWMMXT - IWMMXT_SOURCES += painting/qdrawhelper_iwmmxt.cpp - } - - win32-g++|!win32:!*-icc* { - mmx { - mmx_compiler.commands = $$QMAKE_CXX -c -Winline - - mac { - mmx_compiler.commands += -Xarch_i386 -mmmx - mmx_compiler.commands += -Xarch_x86_64 -mmmx - } else { - mmx_compiler.commands += -mmmx - } - - mmx_compiler.commands += $(CXXFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} - mmx_compiler.dependency_type = TYPE_C - mmx_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)} - mmx_compiler.input = MMX_SOURCES - mmx_compiler.variable_out = OBJECTS - mmx_compiler.name = compiling[mmx] ${QMAKE_FILE_IN} - silent:mmx_compiler.commands = @echo compiling[mmx] ${QMAKE_FILE_IN} && $$mmx_compiler.commands - QMAKE_EXTRA_COMPILERS += mmx_compiler - } - 3dnow { - mmx3dnow_compiler.commands = $$QMAKE_CXX -c -Winline - - mac { - mmx3dnow_compiler.commands += -Xarch_i386 -m3dnow -Xarch_i386 -mmmx - mmx3dnow_compiler.commands += -Xarch_x86_64 -m3dnow -Xarch_x86_64 -mmmx - } else { - mmx3dnow_compiler.commands += -m3dnow -mmmx - } - - mmx3dnow_compiler.commands += $(CXXFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} - mmx3dnow_compiler.dependency_type = TYPE_C - mmx3dnow_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)} - mmx3dnow_compiler.input = MMX3DNOW_SOURCES - mmx3dnow_compiler.variable_out = OBJECTS - mmx3dnow_compiler.name = compiling[mmx3dnow] ${QMAKE_FILE_IN} - silent:mmx3dnow_compiler.commands = @echo compiling[mmx3dnow] ${QMAKE_FILE_IN} && $$mmx3dnow_compiler.commands - QMAKE_EXTRA_COMPILERS += mmx3dnow_compiler - sse { - sse3dnow_compiler.commands = $$QMAKE_CXX -c -Winline - - mac { - sse3dnow_compiler.commands += -Xarch_i386 -m3dnow -Xarch_i386 -msse - sse3dnow_compiler.commands += -Xarch_x86_64 -m3dnow -Xarch_x86_64 -msse - } else { - sse3dnow_compiler.commands += -m3dnow -msse - } - - sse3dnow_compiler.commands += $(CXXFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} - sse3dnow_compiler.dependency_type = TYPE_C - sse3dnow_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)} - sse3dnow_compiler.input = SSE3DNOW_SOURCES - sse3dnow_compiler.variable_out = OBJECTS - sse3dnow_compiler.name = compiling[sse3dnow] ${QMAKE_FILE_IN} - silent:sse3dnow_compiler.commands = @echo compiling[sse3dnow] ${QMAKE_FILE_IN} && $$sse3dnow_compiler.commands - QMAKE_EXTRA_COMPILERS += sse3dnow_compiler - } - } - sse { - sse_compiler.commands = $$QMAKE_CXX -c -Winline - - mac { - sse_compiler.commands += -Xarch_i386 -msse - sse_compiler.commands += -Xarch_x86_64 -msse - } else { - sse_compiler.commands += -msse - } - - sse_compiler.commands += $(CXXFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} - sse_compiler.dependency_type = TYPE_C - sse_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)} - sse_compiler.input = SSE_SOURCES - sse_compiler.variable_out = OBJECTS - sse_compiler.name = compiling[sse] ${QMAKE_FILE_IN} - silent:sse_compiler.commands = @echo compiling[sse] ${QMAKE_FILE_IN} && $$sse_compiler.commands - QMAKE_EXTRA_COMPILERS += sse_compiler - } - sse2 { - sse2_compiler.commands = $$QMAKE_CXX -c -Winline - - mac { - sse2_compiler.commands += -Xarch_i386 -msse2 - sse2_compiler.commands += -Xarch_x86_64 -msse2 - } else { - sse2_compiler.commands += -msse2 - } - - sse2_compiler.commands += $(CXXFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} - sse2_compiler.dependency_type = TYPE_C - sse2_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)} - sse2_compiler.input = SSE2_SOURCES - sse2_compiler.variable_out = OBJECTS - sse2_compiler.name = compiling[sse2] ${QMAKE_FILE_IN} - silent:sse2_compiler.commands = @echo compiling[sse2] ${QMAKE_FILE_IN} && $$sse2_compiler.commands - QMAKE_EXTRA_COMPILERS += sse2_compiler - } - iwmmxt { - iwmmxt_compiler.commands = $$QMAKE_CXX -c -Winline - iwmmxt_compiler.commands += -mcpu=iwmmxt - iwmmxt_compiler.commands += $(CXXFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} - iwmmxt_compiler.dependency_type = TYPE_C - iwmmxt_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)} - iwmmxt_compiler.input = IWMMXT_SOURCES - iwmmxt_compiler.variable_out = OBJECTS - iwmmxt_compiler.name = compiling[iwmmxt] ${QMAKE_FILE_IN} - silent:iwmmxt_compiler.commands = @echo compiling[iwmmxt] ${QMAKE_FILE_IN} && $$iwmmxt_compiler.commands - QMAKE_EXTRA_COMPILERS += iwmmxt_compiler - } - } else { - mmx: SOURCES += $$MMX_SOURCES - 3dnow: SOURCES += $$MMX3DNOW_SOURCES - 3dnow:sse: SOURCES += $$SSE3DNOW_SOURCES - sse: SOURCES += $$SSE_SOURCES - sse2: SOURCES += $$SSE2_SOURCES - iwmmxt: SOURCES += $$IWMMXT_SOURCES - } + painting/qdrawhelper_sse_p.h \ + painting/qdrawingprimitive_sse2_p.h + MMX_SOURCES += painting/qdrawhelper_mmx.cpp + MMX3DNOW_SOURCES += painting/qdrawhelper_mmx3dnow.cpp + SSE3DNOW_SOURCES += painting/qdrawhelper_sse3dnow.cpp + SSE_SOURCES += painting/qdrawhelper_sse.cpp + SSE2_SOURCES += painting/qdrawhelper_sse2.cpp + SSSE3_SOURCES += painting/qdrawhelper_ssse3.cpp + IWMMXT_SOURCES += painting/qdrawhelper_iwmmxt.cpp } x11 { @@ -366,30 +234,22 @@ embedded { symbian { - HEADERS += painting/qwindowsurface_s60_p.h + HEADERS += painting/qwindowsurface_s60_p.h \ + painting/qdrawhelper_arm_simd_p.h SOURCES += painting/qwindowsurface_s60.cpp armccIfdefBlock = \ "$${LITERAL_HASH}if defined(ARMV6)" \ + "MACRO QT_HAVE_ARM_SIMD" \ "SOURCEPATH painting" \ - "SOURCE qblendfunctions_armv6_rvct.s" \ - "SOURCE qdrawhelper_armv6_rvct.s" \ + "SOURCE qdrawhelper_arm_simd.cpp" \ "$${LITERAL_HASH}endif" MMP_RULES += armccIfdefBlock QMAKE_CXXFLAGS.ARMCC *= -O3 } -neon { - DEFINES += QT_HAVE_NEON - HEADERS += painting/qdrawhelper_neon_p.h - SOURCES += painting/qdrawhelper_neon.cpp - QMAKE_CXXFLAGS *= -mfpu=neon -} - -contains(QT_CONFIG, zlib) { - INCLUDEPATH += ../3rdparty/zlib -} else:!contains(QT_CONFIG, no-zlib) { - unix:LIBS_PRIVATE += -lz -# win32:LIBS += libz.lib -} +NEON_SOURCES += painting/qdrawhelper_neon.cpp +NEON_HEADERS += painting/qdrawhelper_neon_p.h +NEON_ASM += ../3rdparty/pixman/pixman-arm-neon-asm.S painting/qdrawhelper_neon_asm.S +include($$PWD/../../3rdparty/zlib_dependency.pri) diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp index 14f715f..ac9b994 100644 --- a/src/gui/painting/qbackingstore.cpp +++ b/src/gui/painting/qbackingstore.cpp @@ -263,7 +263,7 @@ bool QWidgetBackingStore::bltRect(const QRect &rect, int dx, int dy, QWidget *wi { const QPoint pos(tlwOffset + widget->mapTo(tlw, rect.topLeft())); const QRect tlwRect(QRect(pos, rect.size())); - if (dirty.intersects(tlwRect)) + if (fullUpdatePending || dirty.intersects(tlwRect)) return false; // We don't want to scroll junk. return windowSurface->scroll(tlwRect, dx, dy); } @@ -352,7 +352,7 @@ void QWidgetBackingStore::beginPaint(QRegion &toClean, QWidget *widget, QWindowS // Always flush repainted areas. dirtyOnScreen += toClean; -#ifdef Q_WS_QWS +#if defined(Q_WS_QWS) && !defined(Q_BACKINGSTORE_SUBSURFACES) toClean.translate(tlwOffset); #endif @@ -402,7 +402,7 @@ QRegion QWidgetBackingStore::dirtyRegion(QWidget *widget) const const bool widgetDirty = widget && widget != tlw; const QRect tlwRect(topLevelRect()); const QRect surfaceGeometry(windowSurface->geometry()); - if (surfaceGeometry != tlwRect && surfaceGeometry.size() != tlwRect.size()) { + if (fullUpdatePending || (surfaceGeometry != tlwRect && surfaceGeometry.size() != tlwRect.size())) { if (widgetDirty) { const QRect dirtyTlwRect = QRect(QPoint(), tlwRect.size()); const QPoint offset(widget->mapTo(tlw, QPoint())); @@ -555,6 +555,18 @@ void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool up return; } + if (fullUpdatePending) { + if (updateImmediately) + sendUpdateRequest(tlw, updateImmediately); + return; + } + + if (!windowSurface->hasPartialUpdateSupport()) { + fullUpdatePending = true; + sendUpdateRequest(tlw, updateImmediately); + return; + } + const QPoint offset = widget->mapTo(tlw, QPoint()); const QRect widgetRect = widget->d_func()->effectiveRectFor(widget->rect()); if (qt_region_strictContains(dirty, widgetRect.translated(offset))) { @@ -638,6 +650,18 @@ void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool upd return; } + if (fullUpdatePending) { + if (updateImmediately) + sendUpdateRequest(tlw, updateImmediately); + return; + } + + if (!windowSurface->hasPartialUpdateSupport()) { + fullUpdatePending = true; + sendUpdateRequest(tlw, updateImmediately); + return; + } + const QRect widgetRect = widget->d_func()->effectiveRectFor(rect); const QRect translatedRect(widgetRect.translated(widget->mapTo(tlw, QPoint()))); if (qt_region_strictContains(dirty, translatedRect)) { @@ -833,6 +857,7 @@ void QWidgetBackingStore::updateLists(QWidget *cur) QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel) : tlw(topLevel), dirtyOnScreenWidgets(0), hasDirtyFromPreviousSync(false) + , fullUpdatePending(0) { windowSurface = tlw->windowSurface(); if (!windowSurface) @@ -1122,6 +1147,7 @@ void QWidgetBackingStore::sync() for (int i = 0; i < dirtyWidgets.size(); ++i) resetWidget(dirtyWidgets.at(i)); dirtyWidgets.clear(); + fullUpdatePending = false; } return; } @@ -1132,28 +1158,28 @@ void QWidgetBackingStore::sync() const QRect surfaceGeometry(windowSurface->geometry()); bool repaintAllWidgets = false; - if (inTopLevelResize || surfaceGeometry != tlwRect) { - if ((inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) { - if (hasStaticContents()) { - // Repaint existing dirty area and newly visible area. - const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height()); - const QRegion staticRegion(staticContents(0, clipRect)); - QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height()); - newVisible -= staticRegion; - dirty += newVisible; - windowSurface->setStaticContents(staticRegion); - } else { - // Repaint everything. - dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height()); - for (int i = 0; i < dirtyWidgets.size(); ++i) - resetWidget(dirtyWidgets.at(i)); - dirtyWidgets.clear(); - repaintAllWidgets = true; - } + if ((fullUpdatePending || inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) { + if (hasStaticContents()) { + // Repaint existing dirty area and newly visible area. + const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height()); + const QRegion staticRegion(staticContents(0, clipRect)); + QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height()); + newVisible -= staticRegion; + dirty += newVisible; + windowSurface->setStaticContents(staticRegion); + } else { + // Repaint everything. + dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height()); + for (int i = 0; i < dirtyWidgets.size(); ++i) + resetWidget(dirtyWidgets.at(i)); + dirtyWidgets.clear(); + repaintAllWidgets = true; } - windowSurface->setGeometry(tlwRect); } + if (inTopLevelResize || surfaceGeometry != tlwRect) + windowSurface->setGeometry(tlwRect); + if (updatesDisabled) return; @@ -1212,6 +1238,8 @@ void QWidgetBackingStore::sync() } dirtyWidgets.clear(); + fullUpdatePending = false; + if (toClean.isEmpty()) { // Nothing to repaint. However, we might have newly exposed areas on the // screen if this function was called from sync(QWidget *, QRegion)), so @@ -1266,7 +1294,12 @@ void QWidgetBackingStore::sync() #ifdef Q_BACKINGSTORE_SUBSURFACES QWindowSurface *subSurface = w->windowSurface(); BeginPaintInfo beginPaintInfo; - beginPaint(toBePainted, w, subSurface, &beginPaintInfo, false); + + QPoint off = w->mapTo(tlw, QPoint()); + toBePainted.translate(off); + beginPaint(toBePainted, w, subSurface, &beginPaintInfo, true); + toBePainted.translate(-off); + if (beginPaintInfo.nothingToPaint) continue; diff --git a/src/gui/painting/qbackingstore_p.h b/src/gui/painting/qbackingstore_p.h index 02c0f7c..6510b57 100644 --- a/src/gui/painting/qbackingstore_p.h +++ b/src/gui/painting/qbackingstore_p.h @@ -91,6 +91,7 @@ public: inline bool isDirty() const { return !(dirtyWidgets.isEmpty() && dirty.isEmpty() && !hasDirtyFromPreviousSync + && !fullUpdatePending #if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER) && !hasDirtyWindowDecoration() #endif @@ -115,7 +116,8 @@ private: #ifdef Q_BACKINGSTORE_SUBSURFACES QList<QWindowSurface*> subSurfaces; #endif - bool hasDirtyFromPreviousSync; + uint hasDirtyFromPreviousSync : 1; + uint fullUpdatePending : 1; QPoint tlwOffset; diff --git a/src/gui/painting/qbezier.cpp b/src/gui/painting/qbezier.cpp index bbffda1..54c81ba 100644 --- a/src/gui/painting/qbezier.cpp +++ b/src/gui/painting/qbezier.cpp @@ -93,7 +93,7 @@ QBezier QBezier::fromPoints(const QPointF &p1, const QPointF &p2, /*! \internal */ -QPolygonF QBezier::toPolygon() const +QPolygonF QBezier::toPolygon(qreal bezier_flattening_threshold) const { // flattening is done by splitting the bezier until we can replace the segment by a straight // line. We split further until the control points are close enough to the line connecting the @@ -108,39 +108,35 @@ QPolygonF QBezier::toPolygon() const QPolygonF polygon; polygon.append(QPointF(x1, y1)); - addToPolygon(&polygon); + addToPolygon(&polygon, bezier_flattening_threshold); return polygon; } -//0.5 is really low -static const qreal flatness = 0.5; - -//based on "Fast, precise flattening of cubic Bezier path and offset curves" -// by T. F. Hain, A. L. Ahmad, S. V. R. Racherla and D. D. Langan -static inline void flattenBezierWithoutInflections(QBezier &bez, - QPolygonF *&p) +QBezier QBezier::mapBy(const QTransform &transform) const { - QBezier left; + return QBezier::fromPoints(transform.map(pt1()), transform.map(pt2()), transform.map(pt3()), transform.map(pt4())); +} - while (1) { - qreal dx = bez.x2 - bez.x1; - qreal dy = bez.y2 - bez.y1; +QBezier QBezier::getSubRange(qreal t0, qreal t1) const +{ + QBezier result; + QBezier temp; - qreal normalized = qSqrt(dx * dx + dy * dy); - if (qFuzzyIsNull(normalized)) - break; + // cut at t1 + if (qFuzzyIsNull(t1 - qreal(1.))) { + result = *this; + } else { + temp = *this; + temp.parameterSplitLeft(t1, &result); + } - qreal d = qAbs(dx * (bez.y3 - bez.y2) - dy * (bez.x3 - bez.x2)); + // cut at t0 + if (!qFuzzyIsNull(t0)) + result.parameterSplitLeft(t0 / t1, &temp); - qreal t = qSqrt(4. / 3. * normalized * flatness / d); - if (t > 1 || qFuzzyIsNull(t - (qreal)1.)) - break; - bez.parameterSplitLeft(t, &left); - p->append(bez.pt1()); - } + return result; } - static inline int quadraticRoots(qreal a, qreal b, qreal c, qreal *x1, qreal *x2) { @@ -199,7 +195,7 @@ static inline bool findInflections(qreal a, qreal b, qreal c, } -void QBezier::addToPolygon(QPolygonF *polygon) const +void QBezier::addToPolygon(QPolygonF *polygon, qreal bezier_flattening_threshold) const { QBezier beziers[32]; beziers[0] = *this; @@ -219,7 +215,7 @@ void QBezier::addToPolygon(QPolygonF *polygon) const qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3); l = 1.; } - if (d < flatness*l || b == beziers + 31) { + if (d < bezier_flattening_threshold*l || b == beziers + 31) { // good enough, we pop it off and add the endpoint polygon->append(QPointF(b->x4, b->y4)); --b; @@ -231,55 +227,6 @@ void QBezier::addToPolygon(QPolygonF *polygon) const } } -void QBezier::addToPolygonMixed(QPolygonF *polygon) const -{ - qreal ax = -x1 + 3*x2 - 3*x3 + x4; - qreal ay = -y1 + 3*y2 - 3*y3 + y4; - qreal bx = 3*x1 - 6*x2 + 3*x3; - qreal by = 3*y1 - 6*y2 + 3*y3; - qreal cx = -3*x1 + 3*x2; - qreal cy = -3*y1 + 2*y2; - qreal a = 6 * (ay * bx - ax * by); - qreal b = 6 * (ay * cx - ax * cy); - qreal c = 2 * (by * cx - bx * cy); - - if ((qFuzzyIsNull(a) && qFuzzyIsNull(b)) || - (b * b - 4 * a *c) < 0) { - QBezier bez(*this); - flattenBezierWithoutInflections(bez, polygon); - polygon->append(QPointF(x4, y4)); - } else { - QBezier beziers[32]; - beziers[0] = *this; - QBezier *b = beziers; - - while (b >= beziers) { - // check if we can pop the top bezier curve from the stack - qreal y4y1 = b->y4 - b->y1; - qreal x4x1 = b->x4 - b->x1; - qreal l = qAbs(x4x1) + qAbs(y4y1); - qreal d; - if (l > 1.) { - d = qAbs( (x4x1)*(b->y1 - b->y2) - (y4y1)*(b->x1 - b->x2) ) - + qAbs( (x4x1)*(b->y1 - b->y3) - (y4y1)*(b->x1 - b->x3) ); - } else { - d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) + - qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3); - l = 1.; - } - if (d < .5*l || b == beziers + 31) { - // good enough, we pop it off and add the endpoint - polygon->append(QPointF(b->x4, b->y4)); - --b; - } else { - // split, second half of the polygon goes lower into the stack - b->split(b+1, b); - ++b; - } - } - } -} - QRectF QBezier::bounds() const { qreal xmin = x1; @@ -346,36 +293,6 @@ static ShiftResult good_offset(const QBezier *b1, const QBezier *b2, qreal offse return Ok; } -static inline QLineF qline_shifted(const QPointF &p1, const QPointF &p2, qreal offset) -{ - QLineF l(p1, p2); - QLineF ln = l.normalVector().unitVector(); - l.translate(ln.dx() * offset, ln.dy() * offset); - return l; -} - -static bool qbezier_is_line(QPointF *points, int pointCount) -{ - Q_ASSERT(pointCount > 2); - - qreal dx13 = points[2].x() - points[0].x(); - qreal dy13 = points[2].y() - points[0].y(); - - qreal dx12 = points[1].x() - points[0].x(); - qreal dy12 = points[1].y() - points[0].y(); - - if (pointCount == 3) { - return qFuzzyCompare(dx12 * dy13, dx13 * dy12); - } else if (pointCount == 4) { - qreal dx14 = points[3].x() - points[0].x(); - qreal dy14 = points[3].y() - points[0].y(); - - return (qFuzzyCompare(dx12 * dy13, dx13 * dy12) && qFuzzyCompare(dx12 * dy14, dx14 * dy12)); - } - - return false; -} - static ShiftResult shift(const QBezier *orig, QBezier *shifted, qreal offset, qreal threshold) { int map[4]; @@ -406,17 +323,6 @@ static ShiftResult shift(const QBezier *orig, QBezier *shifted, qreal offset, qr if (np == 1) return Discard; - // We need to specialcase lines of 3 or 4 points due to numerical - // instability in intersections below - if (np > 2 && qbezier_is_line(points, np)) { - if (points[0] == points[np-1]) - return Discard; - - QLineF l = qline_shifted(points[0], points[np-1], offset); - *shifted = QBezier::fromPoints(l.p1(), l.pointAt(qreal(0.33)), l.pointAt(qreal(0.66)), l.p2()); - return Ok; - } - QRectF b = orig->bounds(); if (np == 4 && b.width() < .1*offset && b.height() < .1*offset) { qreal l = (orig->x1 - orig->x2)*(orig->x1 - orig->x2) + @@ -588,88 +494,6 @@ give_up: return o - curveSegments; } -#if 0 -static inline bool IntersectBB(const QBezier &a, const QBezier &b) -{ - return a.bounds().intersects(b.bounds()); -} -#else -static int IntersectBB(const QBezier &a, const QBezier &b) -{ - // Compute bounding box for a - qreal minax, maxax, minay, maxay; - if (a.x1 > a.x4) // These are the most likely to be extremal - minax = a.x4, maxax = a.x1; - else - minax = a.x1, maxax = a.x4; - - if (a.x3 < minax) - minax = a.x3; - else if (a.x3 > maxax) - maxax = a.x3; - - if (a.x2 < minax) - minax = a.x2; - else if (a.x2 > maxax) - maxax = a.x2; - - if (a.y1 > a.y4) - minay = a.y4, maxay = a.y1; - else - minay = a.y1, maxay = a.y4; - - if (a.y3 < minay) - minay = a.y3; - else if (a.y3 > maxay) - maxay = a.y3; - - if (a.y2 < minay) - minay = a.y2; - else if (a.y2 > maxay) - maxay = a.y2; - - // Compute bounding box for b - qreal minbx, maxbx, minby, maxby; - if (b.x1 > b.x4) - minbx = b.x4, maxbx = b.x1; - else - minbx = b.x1, maxbx = b.x4; - - if (b.x3 < minbx) - minbx = b.x3; - else if (b.x3 > maxbx) - maxbx = b.x3; - - if (b.x2 < minbx) - minbx = b.x2; - else if (b.x2 > maxbx) - maxbx = b.x2; - - if (b.y1 > b.y4) - minby = b.y4, maxby = b.y1; - else - minby = b.y1, maxby = b.y4; - - if (b.y3 < minby) - minby = b.y3; - else if (b.y3 > maxby) - maxby = b.y3; - - if (b.y2 < minby) - minby = b.y2; - else if (b.y2 > maxby) - maxby = b.y2; - - // Test bounding box of b against bounding box of a - if ((minax > maxbx) || (minay > maxby) // Not >= : need boundary case - || (minbx > maxax) || (minby > maxay)) - return 0; // they don't intersect - else - return 1; // they intersect -} -#endif - - #ifdef QDEBUG_BEZIER static QDebug operator<<(QDebug dbg, const QBezier &bz) { @@ -681,193 +505,6 @@ static QDebug operator<<(QDebug dbg, const QBezier &bz) } #endif -static bool RecursivelyIntersect(const QBezier &a, qreal t0, qreal t1, int deptha, - const QBezier &b, qreal u0, qreal u1, int depthb, - QVector<QPair<qreal, qreal> > *t) -{ -#ifdef QDEBUG_BEZIER - static int I = 0; - int currentD = I; - fprintf(stderr, "%d) t0 = %lf, t1 = %lf, deptha = %d\n" - "u0 = %lf, u1 = %lf, depthb = %d\n", I++, t0, t1, deptha, - u0, u1, depthb); -#endif - if (deptha > 0) { - QBezier A[2]; - a.split(&A[0], &A[1]); - qreal tmid = (t0+t1)*0.5; - //qDebug()<<"\t1)"<<A[0]; - //qDebug()<<"\t2)"<<A[1]; - deptha--; - if (depthb > 0) { - QBezier B[2]; - b.split(&B[0], &B[1]); - //qDebug()<<"\t3)"<<B[0]; - //qDebug()<<"\t4)"<<B[1]; - qreal umid = (u0+u1)*0.5; - depthb--; - if (IntersectBB(A[0], B[0])) { - //fprintf(stderr, "\t 1 from %d\n", currentD); - if (RecursivelyIntersect(A[0], t0, tmid, deptha, - B[0], u0, umid, depthb, - t) && !t) - return true; - } - if (IntersectBB(A[1], B[0])) { - //fprintf(stderr, "\t 2 from %d\n", currentD); - if (RecursivelyIntersect(A[1], tmid, t1, deptha, - B[0], u0, umid, depthb, - t) && !t) - return true; - } - if (IntersectBB(A[0], B[1])) { - //fprintf(stderr, "\t 3 from %d\n", currentD); - if (RecursivelyIntersect(A[0], t0, tmid, deptha, - B[1], umid, u1, depthb, - t) && !t) - return true; - } - if (IntersectBB(A[1], B[1])) { - //fprintf(stderr, "\t 4 from %d\n", currentD); - if (RecursivelyIntersect(A[1], tmid, t1, deptha, - B[1], umid, u1, depthb, - t) && !t) - return true; - } - return t ? !t->isEmpty() : false; - } else { - if (IntersectBB(A[0], b)) { - //fprintf(stderr, "\t 5 from %d\n", currentD); - if (RecursivelyIntersect(A[0], t0, tmid, deptha, - b, u0, u1, depthb, - t) && !t) - return true; - } - if (IntersectBB(A[1], b)) { - //fprintf(stderr, "\t 6 from %d\n", currentD); - if (RecursivelyIntersect(A[1], tmid, t1, deptha, - b, u0, u1, depthb, - t) && !t) - return true; - } - return t ? !t->isEmpty() : false; - } - } else { - if (depthb > 0) { - QBezier B[2]; - b.split(&B[0], &B[1]); - qreal umid = (u0 + u1)*0.5; - depthb--; - if (IntersectBB(a, B[0])) { - //fprintf(stderr, "\t 7 from %d\n", currentD); - if (RecursivelyIntersect(a, t0, t1, deptha, - B[0], u0, umid, depthb, - t) && !t) - return true; - } - if (IntersectBB(a, B[1])) { - //fprintf(stderr, "\t 8 from %d\n", currentD); - if (RecursivelyIntersect(a, t0, t1, deptha, - B[1], umid, u1, depthb, - t) && !t) - return true; - } - return t ? !t->isEmpty() : false; - } - else { - // Both segments are fully subdivided; now do line segments - qreal xlk = a.x4 - a.x1; - qreal ylk = a.y4 - a.y1; - qreal xnm = b.x4 - b.x1; - qreal ynm = b.y4 - b.y1; - qreal xmk = b.x1 - a.x1; - qreal ymk = b.y1 - a.y1; - qreal det = xnm * ylk - ynm * xlk; - if (1.0 + det == 1.0) { - return false; - } else { - qreal detinv = 1.0 / det; - qreal rs = (xnm * ymk - ynm *xmk) * detinv; - qreal rt = (xlk * ymk - ylk * xmk) * detinv; - if ((rs < 0.0) || (rs > 1.0) || (rt < 0.0) || (rt > 1.0)) - return false; - - if (t) { - const qreal alpha_a = t0 + rs * (t1 - t0); - const qreal alpha_b = u0 + rt * (u1 - u0); - - *t << qMakePair(alpha_a, alpha_b); - } - - return true; - } - } - } -} - -QVector< QPair<qreal, qreal> > QBezier::findIntersections(const QBezier &a, const QBezier &b) -{ - QVector< QPair<qreal, qreal> > v(2); - findIntersections(a, b, &v); - return v; -} - -bool QBezier::findIntersections(const QBezier &a, const QBezier &b, - QVector<QPair<qreal, qreal> > *t) -{ - if (IntersectBB(a, b)) { - QPointF la1(qFabs((a.x3 - a.x2) - (a.x2 - a.x1)), - qFabs((a.y3 - a.y2) - (a.y2 - a.y1))); - QPointF la2(qFabs((a.x4 - a.x3) - (a.x3 - a.x2)), - qFabs((a.y4 - a.y3) - (a.y3 - a.y2))); - QPointF la; - if (la1.x() > la2.x()) la.setX(la1.x()); else la.setX(la2.x()); - if (la1.y() > la2.y()) la.setY(la1.y()); else la.setY(la2.y()); - QPointF lb1(qFabs((b.x3 - b.x2) - (b.x2 - b.x1)), - qFabs((b.y3 - b.y2) - (b.y2 - b.y1))); - QPointF lb2(qFabs((b.x4 - b.x3) - (b.x3 - b.x2)), - qFabs((b.y4 - b.y3) - (b.y3 - b.y2))); - QPointF lb; - if (lb1.x() > lb2.x()) lb.setX(lb1.x()); else lb.setX(lb2.x()); - if (lb1.y() > lb2.y()) lb.setY(lb1.y()); else lb.setY(lb2.y()); - qreal l0; - if (la.x() > la.y()) - l0 = la.x(); - else - l0 = la.y(); - int ra; - if (l0 * 0.75 * M_SQRT2 + 1.0 == 1.0) - ra = 0; - else - ra = qCeil(log4(M_SQRT2 * 6.0 / 8.0 * INV_EPS * l0)); - if (lb.x() > lb.y()) - l0 = lb.x(); - else - l0 = lb.y(); - int rb; - if (l0 * 0.75 * M_SQRT2 + 1.0 == 1.0) - rb = 0; - else - rb = qCeil(log4(M_SQRT2 * 6.0 / 8.0 * INV_EPS * l0)); - - // if qreal is float then halve the number of subdivisions - if (sizeof(qreal) == 4) { - ra /= 2; - rb /= 2; - } - - return RecursivelyIntersect(a, 0., 1., ra, b, 0., 1., rb, t); - } - - //Don't sort here because it breaks the orders of corresponding - // intersections points. this way t's at the same locations correspond - // to the same intersection point. - //qSort(parameters[0].begin(), parameters[0].end(), qLess<qreal>()); - //qSort(parameters[1].begin(), parameters[1].end(), qLess<qreal>()); - - return false; -} - static inline void splitBezierAt(const QBezier &bez, qreal t, QBezier *left, QBezier *right) { @@ -896,42 +533,6 @@ static inline void splitBezierAt(const QBezier &bez, qreal t, right->y4 = bez.y4; } -QVector< QList<QBezier> > QBezier::splitAtIntersections(QBezier &b) -{ - QVector< QList<QBezier> > curves(2); - - QVector< QPair<qreal, qreal> > allInters = findIntersections(*this, b); - - QList<qreal> inters1; - QList<qreal> inters2; - - for (int i = 0; i < allInters.size(); ++i) { - inters1 << allInters[i].first; - inters2 << allInters[i].second; - } - - qSort(inters1.begin(), inters1.end(), qLess<qreal>()); - qSort(inters2.begin(), inters2.end(), qLess<qreal>()); - - Q_ASSERT(inters1.count() == inters2.count()); - - int i; - for (i = 0; i < inters1.count(); ++i) { - qreal t1 = inters1.at(i); - qreal t2 = inters2.at(i); - - QBezier curve1, curve2; - parameterSplitLeft(t1, &curve1); - b.parameterSplitLeft(t2, &curve2); - curves[0].append(curve1); - curves[0].append(curve2); - } - curves[0].append(*this); - curves[1].append(b); - - return curves; -} - qreal QBezier::length(qreal error) const { qreal length = 0.0; @@ -1018,13 +619,19 @@ int QBezier::stationaryYPoints(qreal &t0, qreal &t1) const const qreal b = 2 * y1 - 4 * y2 + 2 * y3; const qreal c = -y1 + y2; - qreal reciprocal = b * b - 4 * a * c; + if (qFuzzyIsNull(a)) { + if (qFuzzyIsNull(b)) + return 0; + + t0 = -c / b; + return t0 > 0 && t0 < 1; + } - QList<qreal> result; + qreal reciprocal = b * b - 4 * a * c; if (qFuzzyIsNull(reciprocal)) { t0 = -b / (2 * a); - return 1; + return t0 > 0 && t0 < 1; } else if (reciprocal > 0) { qreal temp = qSqrt(reciprocal); @@ -1099,147 +706,4 @@ QBezier QBezier::bezierOnInterval(qreal t0, qreal t1) const return result; } - -static inline void bindInflectionPoint(const QBezier &bez, const qreal t, - qreal *tMinus , qreal *tPlus) -{ - if (t <= 0) { - *tMinus = *tPlus = -1; - return; - } else if (t >= 1) { - *tMinus = *tPlus = 2; - return; - } - - QBezier left, right; - splitBezierAt(bez, t, &left, &right); - - qreal ax = -right.x1 + 3*right.x2 - 3*right.x3 + right.x4; - qreal ay = -right.y1 + 3*right.y2 - 3*right.y3 + right.y4; - qreal ex = 3 * (right.x2 - right.x3); - qreal ey = 3 * (right.y2 - right.y3); - - qreal s4 = qAbs(6 * (ey * ax - ex * ay) / qSqrt(ex * ex + ey * ey)) + 0.00001f; - qreal tf = qPow(qreal(9 * flatness / s4), qreal(1./3.)); - *tMinus = t - (1 - t) * tf; - *tPlus = t + (1 - t) * tf; -} - -void QBezier::addToPolygonIterative(QPolygonF *p) const -{ - qreal t1, t2, tcusp; - qreal t1min, t1plus, t2min, t2plus; - - qreal ax = -x1 + 3*x2 - 3*x3 + x4; - qreal ay = -y1 + 3*y2 - 3*y3 + y4; - qreal bx = 3*x1 - 6*x2 + 3*x3; - qreal by = 3*y1 - 6*y2 + 3*y3; - qreal cx = -3*x1 + 3*x2; - qreal cy = -3*y1 + 2*y2; - - if (findInflections(6 * (ay * bx - ax * by), - 6 * (ay * cx - ax * cy), - 2 * (by * cx - bx * cy), - &t1, &t2, &tcusp)) { - bindInflectionPoint(*this, t1, &t1min, &t1plus); - bindInflectionPoint(*this, t2, &t2min, &t2plus); - - QBezier tmpBez = *this; - QBezier left, right, bez1, bez2, bez3; - if (t1min > 0) { - if (t1min >= 1) { - flattenBezierWithoutInflections(tmpBez, p); - } else { - splitBezierAt(tmpBez, t1min, &left, &right); - flattenBezierWithoutInflections(left, p); - p->append(tmpBez.pointAt(t1min)); - - if (t2min < t1plus) { - if (tcusp < 1) { - p->append(tmpBez.pointAt(tcusp)); - } - if (t2plus < 1) { - splitBezierAt(tmpBez, t2plus, &left, &right); - flattenBezierWithoutInflections(right, p); - } - } else if (t1plus < 1) { - if (t2min < 1) { - splitBezierAt(tmpBez, t2min, &bez3, &right); - splitBezierAt(bez3, t1plus, &left, &bez2); - - flattenBezierWithoutInflections(bez2, p); - p->append(tmpBez.pointAt(t2min)); - - if (t2plus < 1) { - splitBezierAt(tmpBez, t2plus, &left, &bez2); - flattenBezierWithoutInflections(bez2, p); - } - } else { - splitBezierAt(tmpBez, t1plus, &left, &bez2); - flattenBezierWithoutInflections(bez2, p); - } - } - } - } else if (t1plus > 0) { - p->append(QPointF(x1, y1)); - if (t2min < t1plus) { - if (tcusp < 1) { - p->append(tmpBez.pointAt(tcusp)); - } - if (t2plus < 1) { - splitBezierAt(tmpBez, t2plus, &left, &bez2); - flattenBezierWithoutInflections(bez2, p); - } - } else if (t1plus < 1) { - if (t2min < 1) { - splitBezierAt(tmpBez, t2min, &bez3, &right); - splitBezierAt(bez3, t1plus, &left, &bez2); - - flattenBezierWithoutInflections(bez2, p); - - p->append(tmpBez.pointAt(t2min)); - if (t2plus < 1) { - splitBezierAt(tmpBez, t2plus, &left, &bez2); - flattenBezierWithoutInflections(bez2, p); - } - } else { - splitBezierAt(tmpBez, t1plus, &left, &bez2); - flattenBezierWithoutInflections(bez2, p); - } - } - } else if (t2min > 0) { - if (t2min < 1) { - splitBezierAt(tmpBez, t2min, &bez1, &right); - flattenBezierWithoutInflections(bez1, p); - p->append(tmpBez.pointAt(t2min)); - - if (t2plus < 1) { - splitBezierAt(tmpBez, t2plus, &left, &bez2); - flattenBezierWithoutInflections(bez2, p); - } - } else { - //### in here we should check whether the area of the - // triangle formed between pt1/pt2/pt3 is smaller - // or equal to 0 and then do iterative flattening - // if not we should fallback and do the recursive - // flattening. - flattenBezierWithoutInflections(tmpBez, p); - } - } else if (t2plus > 0) { - p->append(QPointF(x1, y1)); - if (t2plus < 1) { - splitBezierAt(tmpBez, t2plus, &left, &bez2); - flattenBezierWithoutInflections(bez2, p); - } - } else { - flattenBezierWithoutInflections(tmpBez, p); - } - } else { - QBezier bez = *this; - flattenBezierWithoutInflections(bez, p); - } - - p->append(QPointF(x4, y4)); -} - QT_END_NAMESPACE diff --git a/src/gui/painting/qbezier_p.h b/src/gui/painting/qbezier_p.h index 3409bc7..18ec116 100644 --- a/src/gui/painting/qbezier_p.h +++ b/src/gui/painting/qbezier_p.h @@ -59,6 +59,7 @@ #include "QtCore/qvector.h" #include "QtCore/qlist.h" #include "QtCore/qpair.h" +#include "QtGui/qtransform.h" QT_BEGIN_NAMESPACE @@ -78,10 +79,9 @@ public: inline QPointF derivedAt(qreal t) const; inline QPointF secondDerivedAt(qreal t) const; - QPolygonF toPolygon() const; - void addToPolygon(QPolygonF *p) const; - void addToPolygonIterative(QPolygonF *p) const; - void addToPolygonMixed(QPolygonF *p) const; + QPolygonF toPolygon(qreal bezier_flattening_threshold = 0.5) const; + void addToPolygon(QPolygonF *p, qreal bezier_flattening_threshold = 0.5) const; + QRectF bounds() const; qreal length(qreal error = 0.01) const; void addIfClose(qreal *length, qreal error) const; @@ -96,6 +96,8 @@ public: QPointF pt3() const { return QPointF(x3, y3); } QPointF pt4() const { return QPointF(x4, y4); } + QBezier mapBy(const QTransform &transform) const; + inline QPointF midPoint() const; inline QLineF midTangent() const; @@ -104,18 +106,12 @@ public: inline void parameterSplitLeft(qreal t, QBezier *left); inline void split(QBezier *firstHalf, QBezier *secondHalf) const; + int shifted(QBezier *curveSegments, int maxSegmets, qreal offset, float threshold) const; - QVector< QList<QBezier> > splitAtIntersections(QBezier &a); - QBezier bezierOnInterval(qreal t0, qreal t1) const; - - static QVector< QPair<qreal, qreal> > findIntersections(const QBezier &a, - const QBezier &b); - - static bool findIntersections(const QBezier &a, const QBezier &b, - QVector<QPair<qreal, qreal> > *t); + QBezier getSubRange(qreal t0, qreal t1) const; qreal x1, y1, x2, y2, x3, y3, x4, y4; }; diff --git a/src/gui/painting/qblendfunctions.cpp b/src/gui/painting/qblendfunctions.cpp index dc33896..0edf256 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, @@ -332,10 +254,10 @@ void qt_scale_image_argb32_on_rgb16(uchar *destPixels, int dbpl, } } -static void qt_blend_rgb16_on_rgb16(uchar *dst, int dbpl, - const uchar *src, int sbpl, - int w, int h, - int const_alpha) +void qt_blend_rgb16_on_rgb16(uchar *dst, int dbpl, + const uchar *src, int sbpl, + int w, int h, + int const_alpha) { #ifdef QT_DEBUG_DRAW printf("qt_blend_rgb16_on_rgb16: dst=(%p, %d), src=(%p, %d), dim=(%d, %d) alpha=%d\n", @@ -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_armv6_rvct.s b/src/gui/painting/qblendfunctions_armv6_rvct.s deleted file mode 100644 index 1e3faa9..0000000 --- a/src/gui/painting/qblendfunctions_armv6_rvct.s +++ /dev/null @@ -1,222 +0,0 @@ -;**************************************************************************** -;** -;** 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$ -;** -;**************************************************************************** - -; -; 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. -; - - - ARM - PRESERVE8 - - INCLUDE qdrawhelper_armv6_rvct.inc - - -;----------------------------------------------------------------------------- -; qt_blend_rgb32_on_rgb32_arm -; -; @brief -; -; @param dest Destination pixels (r0) -; @param dbpl Destination bytes per line (r1) -; @param src Source pixels (r2) -; @param sbpl Source bytes per line (r3) -; @param w Width (s0 -> r4) -; @param h Height (s1 -> r5) -; @param const_alpha Constant alpha (s2 -> r6) -; -;--------------------------------------------------------------------------- -qt_blend_rgb32_on_rgb32_armv6 Function - stmfd sp!, {r4-r12, r14} - - ; read arguments off the stack - add r8, sp, #10 * 4 - ldmia r8, {r9-r11} - - ; Reorganize registers - - mov r4, r10 - mov r5, r1 - mov r6, r3 - - mov r1, r2 - mov r2, r9 - mov r3, r11 - - ; Now we have registers - ; @param dest Destination pixels (r0) - ; @param src Source pixels (r1) - ; @param w Width (r2) - ; @param const_alpha Constant alpha (r3) - ; @param h Height (r4) - ; @param dbpl Destination bytes per line (r5) - ; @param sbpl Source bytes per line (r6) - - cmp r3, #256 ; test if we have fully opaque constant alpha value - bne rgb32_blend_const_alpha ; branch if not - -rgb32_blend_loop - - subs r4, r4, #1 - bmi rgb32_blend_exit ; while(h--) - -rgb321 PixCpySafe r0, r1, r2 - - add r0, r0, r5 ; dest = dest + dbpl - add r1, r1, r6 ; src = src + sbpl - - b rgb32_blend_loop - - -rgb32_blend_const_alpha - - ;ldr r14, =ComponentHalf ; load 0x800080 to r14 - mov r14, #0x800000 - add r14, r14, #0x80 - - sub r3, r3, #1 ; const_alpha -= 1; - -rgb32_blend_loop_const_alpha - - subs r4, r4, #1 - bmi rgb32_blend_exit ; while(h--) - -rgb322 BlendRowSafe PixelSourceOverConstAlpha - - add r0, r0, r5 ; dest = dest + dbpl - add r1, r1, r6 ; src = src + sbpl - - b rgb32_blend_loop_const_alpha - -rgb32_blend_exit - - ldmfd sp!, {r4-r12, pc} ; pop and return - - - -;----------------------------------------------------------------------------- -; qt_blend_argb32_on_argb32_arm -; -; @brief -; -; @param dest Destination pixels (r0) -; @param dbpl Destination bytes per line (r1) -; @param src Source pixels (r2) -; @param sbpl Source bytes per line (r3) -; @param w Width (s0 -> r4) -; @param h Height (s1 -> r5) -; @param const_alpha Constant alpha (s2 -> r6) -; -;--------------------------------------------------------------------------- -qt_blend_argb32_on_argb32_armv6 Function - stmfd sp!, {r4-r12, r14} - - ; read arguments off the stack - add r8, sp, #10 * 4 - ldmia r8, {r9-r11} - - ; Reorganize registers - - mov r4, r10 - mov r5, r1 - mov r6, r3 - - mov r1, r2 - mov r2, r9 - mov r3, r11 - - ; Now we have registers - ; @param dest Destination pixels (r0) - ; @param src Source pixels (r1) - ; @param w Width (r2) - ; @param const_alpha Constant alpha (r3) - ; @param h Height (r4) - ; @param dbpl Destination bytes per line (r5) - ; @param sbpl Source bytes per line (r6) - - ;ldr r14, =ComponentHalf ; load 0x800080 to r14 - mov r14, #0x800000 - add r14, r14, #0x80 - - cmp r3, #256 ; test if we have fully opaque constant alpha value - bne argb32_blend_const_alpha ; branch if not - -argb32_blend_loop - - subs r4, r4, #1 - bmi argb32_blend_exit ; while(h--) - -argb321 BlendRowSafe PixelSourceOver - - add r0, r0, r5 ; dest = dest + dbpl - add r1, r1, r6 ; src = src + sbpl - - b argb32_blend_loop - -argb32_blend_const_alpha - - sub r3, r3, #1 ; const_alpha -= 1; - -argb32_blend_loop_const_alpha - - subs r4, r4, #1 - bmi argb32_blend_exit ; while(h--) - -argb322 BlendRowSafe PixelSourceOverConstAlpha - - add r0, r0, r5 ; dest = dest + dbpl - add r1, r1, r6 ; src = src + sbpl - - b argb32_blend_loop_const_alpha - -argb32_blend_exit - - ldmfd sp!, {r4-r12, pc} ; pop and return - - - END ; File end 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/qbrush.cpp b/src/gui/painting/qbrush.cpp index c0c73a2..d0788c7 100644 --- a/src/gui/painting/qbrush.cpp +++ b/src/gui/painting/qbrush.cpp @@ -48,6 +48,7 @@ #include "qline.h" #include "qdebug.h" #include <QtCore/qcoreapplication.h> +#include "private/qstylehelper_p.h" QT_BEGIN_NAMESPACE @@ -96,9 +97,11 @@ const uchar *qt_patternForBrush(int brushStyle, bool invert) QPixmap qt_pixmapForBrush(int brushStyle, bool invert) { + QPixmap pm; - QString key = QLatin1String("$qt-brush$") + QString::number(brushStyle) - + QString::number((int)invert); + QString key = QLatin1Literal("$qt-brush$") + % HexString<uint>(brushStyle) + % QLatin1Char(invert ? '1' : '0'); if (!QPixmapCache::find(key, pm)) { pm = QBitmap::fromData(QSize(8, 8), qt_patternForBrush(brushStyle, invert), QImage::Format_MonoLSB); @@ -329,8 +332,8 @@ struct QBrushDataPointerDeleter \endtable - For more information about painting in general, see \l{The Paint - System} documentation. + For more information about painting in general, see the \l{Paint + System}. \sa Qt::BrushStyle, QPainter, QColor */ @@ -911,7 +914,7 @@ void QBrush::setTransform(const QTransform &matrix) otherwise returns false. Two brushes are different if they have different styles, colors or - pixmaps. + transforms or different pixmaps or gradients depending on the style. \sa operator==() */ @@ -923,7 +926,7 @@ void QBrush::setTransform(const QTransform &matrix) otherwise returns false. Two brushes are equal if they have equal styles, colors and - pixmaps. + transforms and equal pixmaps or gradients depending on the style. \sa operator!=() */ @@ -932,26 +935,26 @@ bool QBrush::operator==(const QBrush &b) const { if (b.d == d) return true; - if (b.d->style == d->style && b.d->color == d->color) { - switch (d->style) { - case Qt::TexturePattern: { - QPixmap &us = (static_cast<QTexturedBrushData *>(d.data()))->pixmap(); - QPixmap &them = (static_cast<QTexturedBrushData *>(b.d.data()))->pixmap(); + if (b.d->style != d->style || b.d->color != d->color || b.d->transform != d->transform) + return false; + switch (d->style) { + case Qt::TexturePattern: + { + const QPixmap &us = (static_cast<QTexturedBrushData *>(d.data()))->pixmap(); + const QPixmap &them = (static_cast<QTexturedBrushData *>(b.d.data()))->pixmap(); return ((us.isNull() && them.isNull()) || us.cacheKey() == them.cacheKey()); } - case Qt::LinearGradientPattern: - case Qt::RadialGradientPattern: - case Qt::ConicalGradientPattern: - { - QGradientBrushData *d1 = static_cast<QGradientBrushData *>(d.data()); - QGradientBrushData *d2 = static_cast<QGradientBrushData *>(b.d.data()); - return d1->gradient == d2->gradient; - } - default: - return true; + case Qt::LinearGradientPattern: + case Qt::RadialGradientPattern: + case Qt::ConicalGradientPattern: + { + const QGradientBrushData *d1 = static_cast<QGradientBrushData *>(d.data()); + const QGradientBrushData *d2 = static_cast<QGradientBrushData *>(b.d.data()); + return d1->gradient == d2->gradient; } + default: + return true; } - return false; } /*! @@ -1013,7 +1016,7 @@ QDebug operator<<(QDebug dbg, const QBrush &b) Writes the given \a brush to the given \a stream and returns a reference to the \a stream. - \sa {Format of the QDataStream Operators} + \sa {Serializing Qt Data Types} */ QDataStream &operator<<(QDataStream &s, const QBrush &b) @@ -1081,7 +1084,7 @@ QDataStream &operator<<(QDataStream &s, const QBrush &b) Reads the given \a brush from the given \a stream and returns a reference to the \a stream. - \sa {Format of the QDataStream Operators} + \sa {Serializing Qt Data Types} */ QDataStream &operator>>(QDataStream &s, QBrush &b) @@ -1215,17 +1218,16 @@ QDataStream &operator>>(QDataStream &s, QBrush &b) \o QConicalGradient \endtable - The colors in a gradient is defined using stop points of the - QGradientStop type, i.e. a position and a color. Use the - setColorAt() function to define a single stop - point. Alternatively, use the setStops() function to define - several stop points in one go. Note that the latter function \e - replaces the current set of stop points. + The colors in a gradient are defined using stop points of the + QGradientStop type; i.e., a position and a color. Use the setColorAt() + function to define a single stop point. Alternatively, use the + setStops() function to define several stop points in one go. Note that + the latter function \e replaces the current set of stop points. It is the gradient's complete set of stop points (accessible through the stops() function) that describes how the gradient area - should be filled. If no stop points have been specified, a - gradient of black at 0 to white at 1 is used. + should be filled. If no stop points have been specified, a gradient + of black at 0 to white at 1 is used. A diagonal linear gradient from black at (100, 100) to white at (200, 200) could be specified like this: diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp index d6d288e..37d7fa3 100644 --- a/src/gui/painting/qcolor.cpp +++ b/src/gui/painting/qcolor.cpp @@ -133,7 +133,7 @@ QT_BEGIN_NAMESPACE QColor is platform and device independent. The QColormap class maps the color to the hardware. - For more information about painting in general, see \l{The Paint + For more information about painting in general, see the \l{Paint System} documentation. \tableofcontents @@ -532,25 +532,49 @@ QString QColor::name() const void QColor::setNamedColor(const QString &name) { + if (!setColorFromString(name)) + qWarning("QColor::setNamedColor: Unknown color name '%s'", name.toLatin1().constData()); +} + +/*! + \since 4.7 + + Returns true if the \a name is a valid color name and can + be used to construct a valid QColor object, otherwise returns + false. + + It uses the same algorithm used in setNamedColor(). + + \sa setNamedColor() +*/ +bool QColor::isValidColor(const QString &name) +{ + return !name.isEmpty() && QColor().setColorFromString(name); +} + +bool QColor::setColorFromString(const QString &name) +{ if (name.isEmpty()) { invalidate(); - return; + return true; } if (name.startsWith(QLatin1Char('#'))) { QRgb rgb; if (qt_get_hex_rgb(name.constData(), name.length(), &rgb)) { setRgb(rgb); + return true; } else { invalidate(); + return false; } - return; } #ifndef QT_NO_COLORNAMES QRgb rgb; if (qt_get_named_rgb(name.constData(), name.length(), &rgb)) { setRgba(rgb); + return true; } else #endif { @@ -561,11 +585,12 @@ void QColor::setNamedColor(const QString &name) && QX11Info::display() && XParseColor(QX11Info::display(), QX11Info::appColormap(), name.toLatin1().constData(), &result)) { setRgb(result.red >> 8, result.green >> 8, result.blue >> 8); + return true; } else #endif { - qWarning("QColor::setNamedColor: Unknown color name '%s'", name.toLatin1().constData()); invalidate(); + return false; } } } @@ -2498,7 +2523,7 @@ QDebug operator<<(QDebug dbg, const QColor &c) Writes the \a color to the \a stream. - \sa {Format of the QDataStream Operators} + \sa {Serializing Qt Data Types} */ QDataStream &operator<<(QDataStream &stream, const QColor &color) { @@ -2534,7 +2559,7 @@ QDataStream &operator<<(QDataStream &stream, const QColor &color) Reads the \a color from the \a stream. - \sa { Format of the QDataStream Operators} + \sa {Serializing Qt Data Types} */ QDataStream &operator>>(QDataStream &stream, QColor &color) { @@ -2692,12 +2717,4 @@ QDataStream &operator>>(QDataStream &stream, QColor &color) \sa QColor::rgb(), QColor::rgba() */ -/*! \fn void QColormap::initialize() - \internal -*/ - -/*! \fn void QColormap::cleanup() - \internal -*/ - QT_END_NAMESPACE diff --git a/src/gui/painting/qcolor.h b/src/gui/painting/qcolor.h index 332dc25..0ac828d 100644 --- a/src/gui/painting/qcolor.h +++ b/src/gui/painting/qcolor.h @@ -225,6 +225,8 @@ public: QT3_SUPPORT uint pixel(int screen = -1) const; #endif + static bool isValidColor(const QString &name); + private: #ifndef QT3_SUPPORT // do not allow a spec to be used as an alpha value @@ -232,6 +234,7 @@ private: #endif void invalidate(); + bool setColorFromString(const QString &name); Spec cspec; union { diff --git a/src/gui/painting/qcolormap.qdoc b/src/gui/painting/qcolormap.qdoc index 56fabf7..96fd6a0 100644 --- a/src/gui/painting/qcolormap.qdoc +++ b/src/gui/painting/qcolormap.qdoc @@ -6,35 +6,21 @@ ** ** This file is part of the documentation of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL$ +** $QT_BEGIN_LICENSE:FDL$ ** 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. +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -150,3 +136,13 @@ Assigns the given \a colormap to \e this color map and returns a reference to \e this color map. */ + +/*! + \fn void QColormap::initialize() + \internal +*/ + +/*! + \fn void QColormap::cleanup() + \internal +*/ diff --git a/src/gui/painting/qcups.cpp b/src/gui/painting/qcups.cpp index ac41692..1ea1670 100644 --- a/src/gui/painting/qcups.cpp +++ b/src/gui/painting/qcups.cpp @@ -343,7 +343,8 @@ bool QCUPSSupport::printerHasPPD(const char *printerName) if (!isAvailable()) return false; const char *ppdFile = _cupsGetPPD(printerName); - unlink(ppdFile); + if (ppdFile) + unlink(ppdFile); return (ppdFile != 0); } diff --git a/src/gui/painting/qdatabuffer_p.h b/src/gui/painting/qdatabuffer_p.h index a7834ea..8f8544f 100644 --- a/src/gui/painting/qdatabuffer_p.h +++ b/src/gui/painting/qdatabuffer_p.h @@ -60,16 +60,20 @@ QT_BEGIN_NAMESPACE template <typename Type> class QDataBuffer { public: - QDataBuffer(int res = 64) + QDataBuffer(int res) { capacity = res; - buffer = (Type*) qMalloc(capacity * sizeof(Type)); + if (res) + buffer = (Type*) qMalloc(capacity * sizeof(Type)); + else + buffer = 0; siz = 0; } ~QDataBuffer() { - qFree(buffer); + if (buffer) + qFree(buffer); } inline void reset() { siz = 0; } @@ -81,7 +85,9 @@ public: inline Type &at(int i) { Q_ASSERT(i >= 0 && i < siz); return buffer[i]; } inline const Type &at(int i) const { Q_ASSERT(i >= 0 && i < siz); return buffer[i]; } + inline Type &last() { Q_ASSERT(!isEmpty()); return buffer[siz-1]; } inline const Type &last() const { Q_ASSERT(!isEmpty()); return buffer[siz-1]; } + inline Type &first() { Q_ASSERT(!isEmpty()); return buffer[0]; } inline const Type &first() const { Q_ASSERT(!isEmpty()); return buffer[0]; } inline void add(const Type &t) { @@ -90,6 +96,11 @@ public: ++siz; } + inline void pop_back() { + Q_ASSERT(siz > 0); + --siz; + } + inline void resize(int size) { reserve(size); siz = size; @@ -97,6 +108,8 @@ public: inline void reserve(int size) { if (size > capacity) { + if (capacity == 0) + capacity = 1; while (capacity < size) capacity *= 2; buffer = (Type*) qRealloc(buffer, capacity * sizeof(Type)); @@ -105,7 +118,12 @@ public: inline void shrink(int size) { capacity = size; - buffer = (Type*) qRealloc(buffer, capacity * sizeof(Type)); + if (size) + buffer = (Type*) qRealloc(buffer, capacity * sizeof(Type)); + else { + qFree(buffer); + buffer = 0; + } } inline void swap(QDataBuffer<Type> &other) { diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 2724d7f..a4ab278 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -43,7 +43,7 @@ #include <private/qpaintengine_raster_p.h> #include <private/qpainter_p.h> #include <private/qdrawhelper_x86_p.h> -#include <private/qdrawhelper_armv6_p.h> +#include <private/qdrawhelper_arm_simd_p.h> #include <private/qdrawhelper_neon_p.h> #include <private/qmath_p.h> #include <qmath.h> @@ -69,8 +69,10 @@ static uint gccBug(uint value) constants and structures */ -static const int fixed_scale = 1 << 16; -static const int half_point = 1 << 15; +enum { + fixed_scale = 1 << 16, + half_point = 1 << 15 +}; static const int buffer_size = 2048; struct LinearGradientValues @@ -174,7 +176,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, @@ -322,7 +324,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, @@ -653,6 +655,86 @@ const uint * QT_FASTCALL fetchTransformed(uint *buffer, const Operator *, const return buffer; } +/** \internal + interpolate 4 argb pixels with the distx and disty factor. + distx and disty bust be between 0 and 16 + */ +static inline uint interpolate_4_pixels_16(uint tl, uint tr, uint bl, uint br, int distx, int disty) +{ + uint distxy = distx * disty; + //idistx * disty = (16-distx) * disty = 16*disty - distxy + //idistx * idisty = (16-distx) * (16-disty) = 16*16 - 16*distx -16*dity + distxy + uint tlrb = (tl & 0x00ff00ff) * (16*16 - 16*distx - 16*disty + distxy); + uint tlag = ((tl & 0xff00ff00) >> 8) * (16*16 - 16*distx - 16*disty + distxy); + uint trrb = ((tr & 0x00ff00ff) * (distx*16 - distxy)); + uint trag = (((tr & 0xff00ff00) >> 8) * (distx*16 - distxy)); + uint blrb = ((bl & 0x00ff00ff) * (disty*16 - distxy)); + uint blag = (((bl & 0xff00ff00) >> 8) * (disty*16 - distxy)); + uint brrb = ((br & 0x00ff00ff) * (distxy)); + uint brag = (((br & 0xff00ff00) >> 8) * (distxy)); + return (((tlrb + trrb + blrb + brrb) >> 8) & 0x00ff00ff) | ((tlag + trag + blag + brag) & 0xff00ff00); +} + +#if defined(QT_ALWAYS_HAVE_SSE2) +#define interpolate_4_pixels_16_sse2(tl, tr, bl, br, distx, disty, colorMask, v_256, b) \ +{ \ + const __m128i dxdy = _mm_mullo_epi16 (distx, disty); \ + const __m128i distx_ = _mm_slli_epi16(distx, 4); \ + const __m128i disty_ = _mm_slli_epi16(disty, 4); \ + const __m128i idxidy = _mm_add_epi16(dxdy, _mm_sub_epi16(v_256, _mm_add_epi16(distx_, disty_))); \ + const __m128i dxidy = _mm_sub_epi16(distx_, dxdy); \ + const __m128i idxdy = _mm_sub_epi16(disty_, dxdy); \ + \ + __m128i tlAG = _mm_srli_epi16(tl, 8); \ + __m128i tlRB = _mm_and_si128(tl, colorMask); \ + __m128i trAG = _mm_srli_epi16(tr, 8); \ + __m128i trRB = _mm_and_si128(tr, colorMask); \ + __m128i blAG = _mm_srli_epi16(bl, 8); \ + __m128i blRB = _mm_and_si128(bl, colorMask); \ + __m128i brAG = _mm_srli_epi16(br, 8); \ + __m128i brRB = _mm_and_si128(br, colorMask); \ + \ + tlAG = _mm_mullo_epi16(tlAG, idxidy); \ + tlRB = _mm_mullo_epi16(tlRB, idxidy); \ + trAG = _mm_mullo_epi16(trAG, dxidy); \ + trRB = _mm_mullo_epi16(trRB, dxidy); \ + blAG = _mm_mullo_epi16(blAG, idxdy); \ + blRB = _mm_mullo_epi16(blRB, idxdy); \ + brAG = _mm_mullo_epi16(brAG, dxdy); \ + brRB = _mm_mullo_epi16(brRB, dxdy); \ + \ + /* Add the values, and shift to only keep 8 significant bits per colors */ \ + __m128i rAG =_mm_add_epi16(_mm_add_epi16(tlAG, trAG), _mm_add_epi16(blAG, brAG)); \ + __m128i rRB =_mm_add_epi16(_mm_add_epi16(tlRB, trRB), _mm_add_epi16(blRB, brRB)); \ + rAG = _mm_andnot_si128(colorMask, rAG); \ + rRB = _mm_srli_epi16(rRB, 8); \ + _mm_storeu_si128((__m128i*)(b), _mm_or_si128(rAG, rRB)); \ +} +#endif + + +template<TextureBlendType blendType> +Q_STATIC_TEMPLATE_FUNCTION inline void fetchTransformedBilinear_pixelBounds(int max, int l1, int l2, int &v1, int &v2) +{ + if (blendType == BlendTransformedBilinearTiled) { + v1 %= max; + if (v1 < 0) v1 += max; + v2 = v1 + 1; + v2 %= max; + } else { + if (v1 < l1) { + v2 = v1 = l1; + } else if (v1 >= l2) { + v2 = v1 = l2; + } else { + v2 = v1 + 1; + } + } + + Q_ASSERT(v1 >= 0 && v1 < max); + Q_ASSERT(v2 >= 0 && v2 < max); +} + template<TextureBlendType blendType, QImage::Format format> /* blendType = BlendTransformedBilinear or BlendTransformedBilinearTiled */ Q_STATIC_TEMPLATE_FUNCTION const uint * QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Operator *, const QSpanData *data, @@ -671,10 +753,15 @@ const uint * QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Operator * int image_width = data->texture.width; int image_height = data->texture.height; + int image_x1 = data->texture.x1; + int image_y1 = data->texture.y1; + int image_x2 = data->texture.x2 - 1; + int image_y2 = data->texture.y2 - 1; + const qreal cx = x + 0.5; const qreal cy = y + 0.5; - const uint *end = buffer + length; + uint *end = buffer + length; uint *b = buffer; if (data->fast_matrix) { // The increment pr x in the scanline @@ -688,54 +775,295 @@ const uint * QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Operator * fx -= half_point; fy -= half_point; - while (b < end) { - int x1 = (fx >> 16); - int x2 = x1 + 1; + + if (fdy == 0) { //simple scale, no rotation int y1 = (fy >> 16); - int y2 = y1 + 1; + int y2; + fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); + const uchar *s1 = data->texture.scanLine(y1); + const uchar *s2 = data->texture.scanLine(y2); - int distx = ((fx - (x1 << 16)) >> 8); - int disty = ((fy - (y1 << 16)) >> 8); - int idistx = 256 - distx; - int idisty = 256 - disty; + if (fdx <= fixed_scale && fdx > 0) { // scale up on X + int disty = (fy & 0x0000ffff) >> 8; + int idisty = 256 - disty; + int x = fx >> 16; + + // The idea is first to do the interpolation between the row s1 and the row s2 + // into an intermediate buffer, then we interpolate between two pixel of this buffer. + + // intermediate_buffer[0] is a buffer of red-blue component of the pixel, in the form 0x00RR00BB + // intermediate_buffer[1] is the alpha-green component of the pixel, in the form 0x00AA00GG + quint32 intermediate_buffer[2][buffer_size + 2]; + // count is the size used in the intermediate_buffer. + int count = qCeil(length * data->m11) + 2; //+1 for the last pixel to interpolate with, and +1 for rounding errors. + Q_ASSERT(count <= buffer_size + 2); //length is supposed to be <= buffer_size and data->m11 < 1 in this case + int f = 0; + int lim = count; + if (blendType == BlendTransformedBilinearTiled) { + x %= image_width; + if (x < 0) x += image_width; + } else { + lim = qMin(count, image_x2-x+1); + if (x < image_x1) { + Q_ASSERT(x <= image_x2); + uint t = fetch(s1, image_x1, data->texture.colorTable); + uint b = fetch(s2, image_x1, data->texture.colorTable); + quint32 rb = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff; + quint32 ag = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff; + do { + intermediate_buffer[0][f] = rb; + intermediate_buffer[1][f] = ag; + f++; + x++; + } while (x < image_x1 && f < lim); + } + } - if (blendType == BlendTransformedBilinearTiled) { - x1 %= image_width; - x2 %= image_width; - y1 %= image_height; - y2 %= image_height; - - if (x1 < 0) x1 += image_width; - if (x2 < 0) x2 += image_width; - if (y1 < 0) y1 += image_height; - if (y2 < 0) y2 += image_height; - - Q_ASSERT(x1 >= 0 && x1 < image_width); - Q_ASSERT(x2 >= 0 && x2 < image_width); - Q_ASSERT(y1 >= 0 && y1 < image_height); - Q_ASSERT(y2 >= 0 && y2 < image_height); - } else { - x1 = qBound(0, x1, image_width - 1); - x2 = qBound(0, x2, image_width - 1); - y1 = qBound(0, y1, image_height - 1); - y2 = qBound(0, y2, image_height - 1); +#if defined(QT_ALWAYS_HAVE_SSE2) + if (blendType != BlendTransformedBilinearTiled && + (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)) { + + const __m128i disty_ = _mm_set1_epi16(disty); + const __m128i idisty_ = _mm_set1_epi16(idisty); + const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); + + lim -= 3; + for (; f < lim; x += 4, f += 4) { + // Load 4 pixels from s1, and split the alpha-green and red-blue component + __m128i top = _mm_loadu_si128((__m128i*)((const uint *)(s1)+x)); + __m128i topAG = _mm_srli_epi16(top, 8); + __m128i topRB = _mm_and_si128(top, colorMask); + // Multiplies each colour component by idisty + topAG = _mm_mullo_epi16 (topAG, idisty_); + topRB = _mm_mullo_epi16 (topRB, idisty_); + + // Same for the s2 vector + __m128i bottom = _mm_loadu_si128((__m128i*)((const uint *)(s2)+x)); + __m128i bottomAG = _mm_srli_epi16(bottom, 8); + __m128i bottomRB = _mm_and_si128(bottom, colorMask); + bottomAG = _mm_mullo_epi16 (bottomAG, disty_); + bottomRB = _mm_mullo_epi16 (bottomRB, disty_); + + // Add the values, and shift to only keep 8 significant bits per colors + __m128i rAG =_mm_add_epi16(topAG, bottomAG); + rAG = _mm_srli_epi16(rAG, 8); + _mm_storeu_si128((__m128i*)(&intermediate_buffer[1][f]), rAG); + __m128i rRB =_mm_add_epi16(topRB, bottomRB); + rRB = _mm_srli_epi16(rRB, 8); + _mm_storeu_si128((__m128i*)(&intermediate_buffer[0][f]), rRB); + } + } +#endif + for (; f < count; f++) { // Same as above but without sse2 + if (blendType == BlendTransformedBilinearTiled) { + if (x >= image_width) x -= image_width; + } else { + x = qMin(x, image_x2); + } + + uint t = fetch(s1, x, data->texture.colorTable); + uint b = fetch(s2, x, data->texture.colorTable); + + intermediate_buffer[0][f] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff; + intermediate_buffer[1][f] = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff; + x++; + } + // Now interpolate the values from the intermediate_buffer to get the final result. + fx &= fixed_scale - 1; + Q_ASSERT((fx >> 16) == 0); + while (b < end) { + register int x1 = (fx >> 16); + register int x2 = x1 + 1; + Q_ASSERT(x1 >= 0); + Q_ASSERT(x2 < count); + + register int distx = (fx & 0x0000ffff) >> 8; + register int idistx = 256 - distx; + int rb = ((intermediate_buffer[0][x1] * idistx + intermediate_buffer[0][x2] * distx) >> 8) & 0xff00ff; + int ag = (intermediate_buffer[1][x1] * idistx + intermediate_buffer[1][x2] * distx) & 0xff00ff00; + *b = rb | ag; + b++; + fx += fdx; + } + } else if ((fdx < 0 && fdx > -(fixed_scale / 8)) || fabs(data->m22) < (1./8.)) { // scale up more than 8x + int y1 = (fy >> 16); + int y2; + fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); + const uchar *s1 = data->texture.scanLine(y1); + const uchar *s2 = data->texture.scanLine(y2); + int disty = (fy & 0x0000ffff) >> 8; + int idisty = 256 - disty; + while (b < end) { + int x1 = (fx >> 16); + int x2; + fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); + uint tl = fetch(s1, x1, data->texture.colorTable); + uint tr = fetch(s1, x2, data->texture.colorTable); + uint bl = fetch(s2, x1, data->texture.colorTable); + uint br = fetch(s2, x2, data->texture.colorTable); + + int distx = (fx & 0x0000ffff) >> 8; + int idistx = 256 - distx; + + uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx); + uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx); + *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty); + + fx += fdx; + ++b; + } + } else { //scale down + int y1 = (fy >> 16); + int y2; + fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); + const uchar *s1 = data->texture.scanLine(y1); + const uchar *s2 = data->texture.scanLine(y2); + int disty = (fy & 0x0000ffff) >> 12; + +#if defined(QT_ALWAYS_HAVE_SSE2) + if (blendType != BlendTransformedBilinearTiled && + (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)) { + + //prolog to get into the bounds + while (b < end) { + int x1 = (fx >> 16); + int x2; + fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); + if (x1 != x2) //break if we are insided the bounds. + break; + uint tl = fetch(s1, x1, data->texture.colorTable); + uint tr = fetch(s1, x2, data->texture.colorTable); + uint bl = fetch(s2, x1, data->texture.colorTable); + uint br = fetch(s2, x2, data->texture.colorTable); + int distx = (fx & 0x0000ffff) >> 12; + *b = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty); + fx += fdx; + ++b; + } + uint *boundedEnd; + if (fdx > 0) + boundedEnd = qMin(end, buffer + uint((image_x2 - (fx >> 16)) / data->m11)); + else + boundedEnd = qMin(end, buffer + uint((image_x1 - (fx >> 16)) / data->m11)); + boundedEnd -= 3; + + const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); + //const __m128i distShuffleMask = _mm_set_epi8(13, 12, 13, 12, 9, 8, 9, 8, 5, 4, 5, 4, 1, 0, 1, 0); + const __m128i v_256 = _mm_set1_epi16(256); + const __m128i v_disty = _mm_set1_epi16(disty); + __m128i v_fdx = _mm_set1_epi32(fdx*4); + + ptrdiff_t secondLine = reinterpret_cast<const uint *>(s2) - reinterpret_cast<const uint *>(s1); + + union Vect_buffer { __m128i vect; quint32 i[4]; }; + Vect_buffer v_fx; + + for (int i = 0; i < 4; i++) { + v_fx.i[i] = fx; + fx += fdx; + } + + while (b < boundedEnd) { + + Vect_buffer tl, tr, bl, br; + + for (int i = 0; i < 4; i++) { + int x1 = v_fx.i[i] >> 16; + const uint *addr_tl = reinterpret_cast<const uint *>(s1) + x1; + const uint *addr_tr = addr_tl + 1; + tl.i[i] = *addr_tl; + tr.i[i] = *addr_tr; + bl.i[i] = *(addr_tl+secondLine); + br.i[i] = *(addr_tr+secondLine); + } + __m128i v_distx = _mm_srli_epi16(v_fx.vect, 12); //distx = (fx & 0x0000ffff) >> 12; + //v_distx = _mm_shuffle_epi8(v_disty, distShuffleMask); //distx |= distx << 16; + v_distx = _mm_shufflehi_epi16(v_distx, _MM_SHUFFLE(2,2,0,0)); + v_distx = _mm_shufflelo_epi16(v_distx, _MM_SHUFFLE(2,2,0,0)); + + interpolate_4_pixels_16_sse2(tl.vect, tr.vect, bl.vect, br.vect, v_distx, v_disty, colorMask, v_256, b); + b+=4; + v_fx.vect = _mm_add_epi32(v_fx.vect, v_fdx); + } + fx = v_fx.i[0]; + } +#endif + while (b < end) { + int x1 = (fx >> 16); + int x2; + fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); + uint tl = fetch(s1, x1, data->texture.colorTable); + uint tr = fetch(s1, x2, data->texture.colorTable); + uint bl = fetch(s2, x1, data->texture.colorTable); + uint br = fetch(s2, x2, data->texture.colorTable); + int distx = (fx & 0x0000ffff) >> 12; + *b = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty); + fx += fdx; + ++b; + } } + } else { //rotation + if (fabs(data->m11) > 8 || fabs(data->m22) > 8) { + //if we are zooming more than 8 times, we use 8bit precision for the position. + while (b < end) { + int x1 = (fx >> 16); + int x2; + int y1 = (fy >> 16); + int y2; - const uchar *s1 = data->texture.scanLine(y1); - const uchar *s2 = data->texture.scanLine(y2); + fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); + fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); - uint tl = fetch(s1, x1, data->texture.colorTable); - uint tr = fetch(s1, x2, data->texture.colorTable); - uint bl = fetch(s2, x1, data->texture.colorTable); - uint br = fetch(s2, x2, data->texture.colorTable); + const uchar *s1 = data->texture.scanLine(y1); + const uchar *s2 = data->texture.scanLine(y2); - uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx); - uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx); - *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty); + uint tl = fetch(s1, x1, data->texture.colorTable); + uint tr = fetch(s1, x2, data->texture.colorTable); + uint bl = fetch(s2, x1, data->texture.colorTable); + uint br = fetch(s2, x2, data->texture.colorTable); - fx += fdx; - fy += fdy; - ++b; + int distx = (fx & 0x0000ffff) >> 8; + int disty = (fy & 0x0000ffff) >> 8; + int idistx = 256 - distx; + int idisty = 256 - disty; + + uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx); + uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx); + *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty); + + fx += fdx; + fy += fdy; + ++b; + } + } else { + //we are zooming less than 8x, use 4bit precision + while (b < end) { + int x1 = (fx >> 16); + int x2; + int y1 = (fy >> 16); + int y2; + + fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); + fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); + + const uchar *s1 = data->texture.scanLine(y1); + const uchar *s2 = data->texture.scanLine(y2); + + uint tl = fetch(s1, x1, data->texture.colorTable); + uint tr = fetch(s1, x2, data->texture.colorTable); + uint bl = fetch(s2, x1, data->texture.colorTable); + uint br = fetch(s2, x2, data->texture.colorTable); + + int distx = (fx & 0x0000ffff) >> 12; + int disty = (fy & 0x0000ffff) >> 12; + + *b = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty); + + fx += fdx; + fy += fdy; + ++b; + } + } } } else { const qreal fdx = data->m11; @@ -752,36 +1080,17 @@ const uint * QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Operator * const qreal py = fy * iw - 0.5; int x1 = int(px) - (px < 0); - int x2 = x1 + 1; + int x2; int y1 = int(py) - (py < 0); - int y2 = y1 + 1; + int y2; int distx = int((px - x1) * 256); int disty = int((py - y1) * 256); int idistx = 256 - distx; int idisty = 256 - disty; - if (blendType == BlendTransformedBilinearTiled) { - x1 %= image_width; - x2 %= image_width; - y1 %= image_height; - y2 %= image_height; - - if (x1 < 0) x1 += image_width; - if (x2 < 0) x2 += image_width; - if (y1 < 0) y1 += image_height; - if (y2 < 0) y2 += image_height; - - Q_ASSERT(x1 >= 0 && x1 < image_width); - Q_ASSERT(x2 >= 0 && x2 < image_width); - Q_ASSERT(y1 >= 0 && y1 < image_height); - Q_ASSERT(y2 >= 0 && y2 < image_height); - } else { - x1 = qBound(0, x1, image_width - 1); - x2 = qBound(0, x2, image_width - 1); - y1 = qBound(0, y1, image_height - 1); - y2 = qBound(0, y2, image_height - 1); - } + fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); + fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); const uchar *s1 = data->texture.scanLine(y1); const uchar *s2 = data->texture.scanLine(y2); @@ -1281,12 +1590,12 @@ static const uint L2CacheLineLengthInInts = L2CacheLineLength/sizeof(uint); }\ } -static void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint, uint const_alpha) +void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint, uint const_alpha) { comp_func_Clear_impl(dest, length, const_alpha); } -static void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, uint const_alpha) +void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, uint const_alpha) { comp_func_Clear_impl(dest, length, const_alpha); } @@ -1295,7 +1604,7 @@ static void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, ui result = s dest = s * ca + d * cia */ -static void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) { QT_MEMFILL_UINT(dest, length, color); @@ -1310,7 +1619,7 @@ static void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint colo } } -static void QT_FASTCALL comp_func_Source(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_Source(uint *dest, const uint *src, int length, uint const_alpha) { if (const_alpha == 255) { ::memcpy(dest, src, length * sizeof(uint)); @@ -1324,11 +1633,11 @@ static void QT_FASTCALL comp_func_Source(uint *dest, const uint *src, int length } } -static void QT_FASTCALL comp_func_solid_Destination(uint *, int, uint, uint) +void QT_FASTCALL comp_func_solid_Destination(uint *, int, uint, uint) { } -static void QT_FASTCALL comp_func_Destination(uint *, const uint *, int, uint) +void QT_FASTCALL comp_func_Destination(uint *, const uint *, int, uint) { } @@ -1338,7 +1647,7 @@ static void QT_FASTCALL comp_func_Destination(uint *, const uint *, int, uint) = s * ca + d * (sia * ca + cia) = s * ca + d * (1 - sa*ca) */ -static void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint color, uint const_alpha) { if ((const_alpha & qAlpha(color)) == 255) { QT_MEMFILL_UINT(dest, length, color); @@ -1353,7 +1662,7 @@ static void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint } } -static void QT_FASTCALL comp_func_SourceOver(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_SourceOver(uint *dest, const uint *src, int length, uint const_alpha) { PRELOAD_INIT2(dest, src) if (const_alpha == 255) { @@ -1379,7 +1688,7 @@ static void QT_FASTCALL comp_func_SourceOver(uint *dest, const uint *src, int le dest = (d + s * dia) * ca + d * cia = d + s * dia * ca */ -static void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha != 255) color = BYTE_MUL(color, const_alpha); @@ -1391,7 +1700,7 @@ static void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, } } -static void QT_FASTCALL comp_func_DestinationOver(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_DestinationOver(uint *dest, const uint *src, int length, uint const_alpha) { PRELOAD_INIT2(dest, src) if (const_alpha == 255) { @@ -1414,7 +1723,7 @@ static void QT_FASTCALL comp_func_DestinationOver(uint *dest, const uint *src, i result = s * da dest = s * da * ca + d * cia */ -static void QT_FASTCALL comp_func_solid_SourceIn(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_SourceIn(uint *dest, int length, uint color, uint const_alpha) { PRELOAD_INIT(dest) if (const_alpha == 255) { @@ -1433,7 +1742,7 @@ static void QT_FASTCALL comp_func_solid_SourceIn(uint *dest, int length, uint co } } -static void QT_FASTCALL comp_func_SourceIn(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_SourceIn(uint *dest, const uint *src, int length, uint const_alpha) { PRELOAD_INIT2(dest, src) if (const_alpha == 255) { @@ -1457,7 +1766,7 @@ static void QT_FASTCALL comp_func_SourceIn(uint *dest, const uint *src, int leng dest = d * sa * ca + d * cia = d * (sa * ca + cia) */ -static void QT_FASTCALL comp_func_solid_DestinationIn(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_DestinationIn(uint *dest, int length, uint color, uint const_alpha) { uint a = qAlpha(color); if (const_alpha != 255) { @@ -1470,7 +1779,7 @@ static void QT_FASTCALL comp_func_solid_DestinationIn(uint *dest, int length, ui } } -static void QT_FASTCALL comp_func_DestinationIn(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_DestinationIn(uint *dest, const uint *src, int length, uint const_alpha) { PRELOAD_INIT2(dest, src) if (const_alpha == 255) { @@ -1493,7 +1802,7 @@ static void QT_FASTCALL comp_func_DestinationIn(uint *dest, const uint *src, int dest = s * dia * ca + d * cia */ -static void QT_FASTCALL comp_func_solid_SourceOut(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_SourceOut(uint *dest, int length, uint color, uint const_alpha) { PRELOAD_INIT(dest) if (const_alpha == 255) { @@ -1512,7 +1821,7 @@ static void QT_FASTCALL comp_func_solid_SourceOut(uint *dest, int length, uint c } } -static void QT_FASTCALL comp_func_SourceOut(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_SourceOut(uint *dest, const uint *src, int length, uint const_alpha) { PRELOAD_INIT2(dest, src) if (const_alpha == 255) { @@ -1536,7 +1845,7 @@ static void QT_FASTCALL comp_func_SourceOut(uint *dest, const uint *src, int len dest = d * sia * ca + d * cia = d * (sia * ca + cia) */ -static void QT_FASTCALL comp_func_solid_DestinationOut(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_DestinationOut(uint *dest, int length, uint color, uint const_alpha) { uint a = qAlpha(~color); if (const_alpha != 255) @@ -1548,7 +1857,7 @@ static void QT_FASTCALL comp_func_solid_DestinationOut(uint *dest, int length, u } } -static void QT_FASTCALL comp_func_DestinationOut(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_DestinationOut(uint *dest, const uint *src, int length, uint const_alpha) { PRELOAD_INIT2(dest, src) if (const_alpha == 255) { @@ -1572,7 +1881,7 @@ static void QT_FASTCALL comp_func_DestinationOut(uint *dest, const uint *src, in = s*ca * da + d * (sia*ca + cia) = s*ca * da + d * (1 - sa*ca) */ -static void QT_FASTCALL comp_func_solid_SourceAtop(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_SourceAtop(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha != 255) { color = BYTE_MUL(color, const_alpha); @@ -1585,7 +1894,7 @@ static void QT_FASTCALL comp_func_solid_SourceAtop(uint *dest, int length, uint } } -static void QT_FASTCALL comp_func_SourceAtop(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_SourceAtop(uint *dest, const uint *src, int length, uint const_alpha) { PRELOAD_INIT2(dest, src) if (const_alpha == 255) { @@ -1610,7 +1919,7 @@ static void QT_FASTCALL comp_func_SourceAtop(uint *dest, const uint *src, int le dest = d*sa*ca + s*dia*ca + d *cia = s*ca * dia + d * (sa*ca + cia) */ -static void QT_FASTCALL comp_func_solid_DestinationAtop(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_DestinationAtop(uint *dest, int length, uint color, uint const_alpha) { uint a = qAlpha(color); if (const_alpha != 255) { @@ -1625,7 +1934,7 @@ static void QT_FASTCALL comp_func_solid_DestinationAtop(uint *dest, int length, } } -static void QT_FASTCALL comp_func_DestinationAtop(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_DestinationAtop(uint *dest, const uint *src, int length, uint const_alpha) { PRELOAD_INIT2(dest, src) if (const_alpha == 255) { @@ -1653,7 +1962,7 @@ static void QT_FASTCALL comp_func_DestinationAtop(uint *dest, const uint *src, i = s*ca * dia + d * (sia*ca + cia) = s*ca * dia + d * (1 - sa*ca) */ -static void QT_FASTCALL comp_func_solid_XOR(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_XOR(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha != 255) color = BYTE_MUL(color, const_alpha); @@ -1667,7 +1976,7 @@ static void QT_FASTCALL comp_func_solid_XOR(uint *dest, int length, uint color, } } -static void QT_FASTCALL comp_func_XOR(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_XOR(uint *dest, const uint *src, int length, uint const_alpha) { PRELOAD_INIT2(dest, src) if (const_alpha == 255) { @@ -1687,11 +1996,6 @@ static void QT_FASTCALL comp_func_XOR(uint *dest, const uint *src, int length, u } } -static const uint AMASK = 0xff000000; -static const uint RMASK = 0x00ff0000; -static const uint GMASK = 0x0000ff00; -static const uint BMASK = 0x000000ff; - struct QFullCoverage { inline void store(uint *dest, const uint src) const { @@ -1734,14 +2038,12 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Plus_impl(uint *dest, int for (int i = 0; i < length; ++i) { PRELOAD_COND(dest) uint d = dest[i]; -#define MIX(mask) (qMin(((qint64(s)&mask) + (qint64(d)&mask)), qint64(mask))) - d = (MIX(AMASK) | MIX(RMASK) | MIX(GMASK) | MIX(BMASK)); -#undef MIX + d = comp_func_Plus_one_pixel(d, s); coverage.store(&dest[i], d); } } -static void QT_FASTCALL comp_func_solid_Plus(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_Plus(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) comp_func_solid_Plus_impl(dest, length, color, QFullCoverage()); @@ -1758,15 +2060,13 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Plus_impl(uint *dest, const uin uint d = dest[i]; uint s = src[i]; -#define MIX(mask) (qMin(((qint64(s)&mask) + (qint64(d)&mask)), qint64(mask))) - d = (MIX(AMASK) | MIX(RMASK) | MIX(GMASK) | MIX(BMASK)); -#undef MIX + d = comp_func_Plus_one_pixel(d, s); coverage.store(&dest[i], d); } } -static void QT_FASTCALL comp_func_Plus(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_Plus(uint *dest, const uint *src, int length, uint const_alpha) { if (const_alpha == 255) comp_func_Plus_impl(dest, src, length, QFullCoverage()); @@ -1807,7 +2107,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Multiply_impl(uint *dest, } } -static void QT_FASTCALL comp_func_solid_Multiply(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_Multiply(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) comp_func_solid_Multiply_impl(dest, length, color, QFullCoverage()); @@ -1838,7 +2138,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Multiply_impl(uint *dest, const } } -static void QT_FASTCALL comp_func_Multiply(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_Multiply(uint *dest, const uint *src, int length, uint const_alpha) { if (const_alpha == 255) comp_func_Multiply_impl(dest, src, length, QFullCoverage()); @@ -1875,7 +2175,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Screen_impl(uint *dest, i } } -static void QT_FASTCALL comp_func_solid_Screen(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_Screen(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) comp_func_solid_Screen_impl(dest, length, color, QFullCoverage()); @@ -1906,7 +2206,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Screen_impl(uint *dest, const u } } -static void QT_FASTCALL comp_func_Screen(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_Screen(uint *dest, const uint *src, int length, uint const_alpha) { if (const_alpha == 255) comp_func_Screen_impl(dest, src, length, QFullCoverage()); @@ -1954,7 +2254,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Overlay_impl(uint *dest, } } -static void QT_FASTCALL comp_func_solid_Overlay(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_Overlay(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) comp_func_solid_Overlay_impl(dest, length, color, QFullCoverage()); @@ -1985,7 +2285,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Overlay_impl(uint *dest, const } } -static void QT_FASTCALL comp_func_Overlay(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_Overlay(uint *dest, const uint *src, int length, uint const_alpha) { if (const_alpha == 255) comp_func_Overlay_impl(dest, src, length, QFullCoverage()); @@ -2027,7 +2327,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Darken_impl(uint *dest, i } } -static void QT_FASTCALL comp_func_solid_Darken(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_Darken(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) comp_func_solid_Darken_impl(dest, length, color, QFullCoverage()); @@ -2058,7 +2358,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Darken_impl(uint *dest, const u } } -static void QT_FASTCALL comp_func_Darken(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_Darken(uint *dest, const uint *src, int length, uint const_alpha) { if (const_alpha == 255) comp_func_Darken_impl(dest, src, length, QFullCoverage()); @@ -2100,7 +2400,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Lighten_impl(uint *dest, } } -static void QT_FASTCALL comp_func_solid_Lighten(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_Lighten(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) comp_func_solid_Lighten_impl(dest, length, color, QFullCoverage()); @@ -2131,7 +2431,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Lighten_impl(uint *dest, const } } -static void QT_FASTCALL comp_func_Lighten(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_Lighten(uint *dest, const uint *src, int length, uint const_alpha) { if (const_alpha == 255) comp_func_Lighten_impl(dest, src, length, QFullCoverage()); @@ -2183,7 +2483,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorDodge_impl(uint *des } } -static void QT_FASTCALL comp_func_solid_ColorDodge(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_ColorDodge(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) comp_func_solid_ColorDodge_impl(dest, length, color, QFullCoverage()); @@ -2214,7 +2514,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorDodge_impl(uint *dest, con } } -static void QT_FASTCALL comp_func_ColorDodge(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_ColorDodge(uint *dest, const uint *src, int length, uint const_alpha) { if (const_alpha == 255) comp_func_ColorDodge_impl(dest, src, length, QFullCoverage()); @@ -2266,7 +2566,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorBurn_impl(uint *dest } } -static void QT_FASTCALL comp_func_solid_ColorBurn(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_ColorBurn(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) comp_func_solid_ColorBurn_impl(dest, length, color, QFullCoverage()); @@ -2297,7 +2597,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorBurn_impl(uint *dest, cons } } -static void QT_FASTCALL comp_func_ColorBurn(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_ColorBurn(uint *dest, const uint *src, int length, uint const_alpha) { if (const_alpha == 255) comp_func_ColorBurn_impl(dest, src, length, QFullCoverage()); @@ -2346,7 +2646,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_HardLight_impl(uint *dest } } -static void QT_FASTCALL comp_func_solid_HardLight(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_HardLight(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) comp_func_solid_HardLight_impl(dest, length, color, QFullCoverage()); @@ -2377,7 +2677,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_HardLight_impl(uint *dest, cons } } -static void QT_FASTCALL comp_func_HardLight(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_HardLight(uint *dest, const uint *src, int length, uint const_alpha) { if (const_alpha == 255) comp_func_HardLight_impl(dest, src, length, QFullCoverage()); @@ -2437,7 +2737,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_SoftLight_impl(uint *dest } } -static void QT_FASTCALL comp_func_solid_SoftLight(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_SoftLight(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) comp_func_solid_SoftLight_impl(dest, length, color, QFullCoverage()); @@ -2468,7 +2768,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_SoftLight_impl(uint *dest, cons } } -static void QT_FASTCALL comp_func_SoftLight(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_SoftLight(uint *dest, const uint *src, int length, uint const_alpha) { if (const_alpha == 255) comp_func_SoftLight_impl(dest, src, length, QFullCoverage()); @@ -2510,7 +2810,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Difference_impl(uint *des } } -static void QT_FASTCALL comp_func_solid_Difference(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_Difference(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) comp_func_solid_Difference_impl(dest, length, color, QFullCoverage()); @@ -2541,7 +2841,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Difference_impl(uint *dest, con } } -static void QT_FASTCALL comp_func_Difference(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_Difference(uint *dest, const uint *src, int length, uint const_alpha) { if (const_alpha == 255) comp_func_Difference_impl(dest, src, length, QFullCoverage()); @@ -2577,7 +2877,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void QT_FASTCALL comp_func_solid_Exclusion_imp } } -static void QT_FASTCALL comp_func_solid_Exclusion(uint *dest, int length, uint color, uint const_alpha) +void QT_FASTCALL comp_func_solid_Exclusion(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) comp_func_solid_Exclusion_impl(dest, length, color, QFullCoverage()); @@ -2608,7 +2908,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Exclusion_impl(uint *dest, cons } } -static void QT_FASTCALL comp_func_Exclusion(uint *dest, const uint *src, int length, uint const_alpha) +void QT_FASTCALL comp_func_Exclusion(uint *dest, const uint *src, int length, uint const_alpha) { if (const_alpha == 255) comp_func_Exclusion_impl(dest, src, length, QFullCoverage()); @@ -2621,30 +2921,30 @@ static void QT_FASTCALL comp_func_Exclusion(uint *dest, const uint *src, int len # pragma pop #endif -static void QT_FASTCALL rasterop_solid_SourceOrDestination(uint *dest, - int length, - uint color, - uint const_alpha) +void QT_FASTCALL rasterop_solid_SourceOrDestination(uint *dest, + int length, + uint color, + uint const_alpha) { Q_UNUSED(const_alpha); while (length--) *dest++ |= color; } -static void QT_FASTCALL rasterop_SourceOrDestination(uint *dest, - const uint *src, - int length, - uint const_alpha) +void QT_FASTCALL rasterop_SourceOrDestination(uint *dest, + const uint *src, + int length, + uint const_alpha) { Q_UNUSED(const_alpha); while (length--) *dest++ |= *src++; } -static void QT_FASTCALL rasterop_solid_SourceAndDestination(uint *dest, - int length, - uint color, - uint const_alpha) +void QT_FASTCALL rasterop_solid_SourceAndDestination(uint *dest, + int length, + uint color, + uint const_alpha) { Q_UNUSED(const_alpha); color |= 0xff000000; @@ -2652,10 +2952,10 @@ static void QT_FASTCALL rasterop_solid_SourceAndDestination(uint *dest, *dest++ &= color; } -static void QT_FASTCALL rasterop_SourceAndDestination(uint *dest, - const uint *src, - int length, - uint const_alpha) +void QT_FASTCALL rasterop_SourceAndDestination(uint *dest, + const uint *src, + int length, + uint const_alpha) { Q_UNUSED(const_alpha); while (length--) { @@ -2664,10 +2964,10 @@ static void QT_FASTCALL rasterop_SourceAndDestination(uint *dest, } } -static void QT_FASTCALL rasterop_solid_SourceXorDestination(uint *dest, - int length, - uint color, - uint const_alpha) +void QT_FASTCALL rasterop_solid_SourceXorDestination(uint *dest, + int length, + uint color, + uint const_alpha) { Q_UNUSED(const_alpha); color &= 0x00ffffff; @@ -2675,10 +2975,10 @@ static void QT_FASTCALL rasterop_solid_SourceXorDestination(uint *dest, *dest++ ^= color; } -static void QT_FASTCALL rasterop_SourceXorDestination(uint *dest, - const uint *src, - int length, - uint const_alpha) +void QT_FASTCALL rasterop_SourceXorDestination(uint *dest, + const uint *src, + int length, + uint const_alpha) { Q_UNUSED(const_alpha); while (length--) { @@ -2687,10 +2987,10 @@ static void QT_FASTCALL rasterop_SourceXorDestination(uint *dest, } } -static void QT_FASTCALL rasterop_solid_NotSourceAndNotDestination(uint *dest, - int length, - uint color, - uint const_alpha) +void QT_FASTCALL rasterop_solid_NotSourceAndNotDestination(uint *dest, + int length, + uint color, + uint const_alpha) { Q_UNUSED(const_alpha); color = ~color; @@ -2700,10 +3000,10 @@ static void QT_FASTCALL rasterop_solid_NotSourceAndNotDestination(uint *dest, } } -static void QT_FASTCALL rasterop_NotSourceAndNotDestination(uint *dest, - const uint *src, - int length, - uint const_alpha) +void QT_FASTCALL rasterop_NotSourceAndNotDestination(uint *dest, + const uint *src, + int length, + uint const_alpha) { Q_UNUSED(const_alpha); while (length--) { @@ -2712,10 +3012,10 @@ static void QT_FASTCALL rasterop_NotSourceAndNotDestination(uint *dest, } } -static void QT_FASTCALL rasterop_solid_NotSourceOrNotDestination(uint *dest, - int length, - uint color, - uint const_alpha) +void QT_FASTCALL rasterop_solid_NotSourceOrNotDestination(uint *dest, + int length, + uint color, + uint const_alpha) { Q_UNUSED(const_alpha); color = ~color | 0xff000000; @@ -2725,10 +3025,10 @@ static void QT_FASTCALL rasterop_solid_NotSourceOrNotDestination(uint *dest, } } -static void QT_FASTCALL rasterop_NotSourceOrNotDestination(uint *dest, - const uint *src, - int length, - uint const_alpha) +void QT_FASTCALL rasterop_NotSourceOrNotDestination(uint *dest, + const uint *src, + int length, + uint const_alpha) { Q_UNUSED(const_alpha); while (length--) { @@ -2737,10 +3037,10 @@ static void QT_FASTCALL rasterop_NotSourceOrNotDestination(uint *dest, } } -static void QT_FASTCALL rasterop_solid_NotSourceXorDestination(uint *dest, - int length, - uint color, - uint const_alpha) +void QT_FASTCALL rasterop_solid_NotSourceXorDestination(uint *dest, + int length, + uint color, + uint const_alpha) { Q_UNUSED(const_alpha); color = ~color & 0x00ffffff; @@ -2750,10 +3050,10 @@ static void QT_FASTCALL rasterop_solid_NotSourceXorDestination(uint *dest, } } -static void QT_FASTCALL rasterop_NotSourceXorDestination(uint *dest, - const uint *src, - int length, - uint const_alpha) +void QT_FASTCALL rasterop_NotSourceXorDestination(uint *dest, + const uint *src, + int length, + uint const_alpha) { Q_UNUSED(const_alpha); while (length--) { @@ -2762,25 +3062,25 @@ static void QT_FASTCALL rasterop_NotSourceXorDestination(uint *dest, } } -static void QT_FASTCALL rasterop_solid_NotSource(uint *dest, int length, - uint color, uint const_alpha) +void QT_FASTCALL rasterop_solid_NotSource(uint *dest, int length, + uint color, uint const_alpha) { Q_UNUSED(const_alpha); qt_memfill(dest, ~color | 0xff000000, length); } -static void QT_FASTCALL rasterop_NotSource(uint *dest, const uint *src, - int length, uint const_alpha) +void QT_FASTCALL rasterop_NotSource(uint *dest, const uint *src, + int length, uint const_alpha) { Q_UNUSED(const_alpha); while (length--) *dest++ = ~(*src++) | 0xff000000; } -static void QT_FASTCALL rasterop_solid_NotSourceAndDestination(uint *dest, - int length, - uint color, - uint const_alpha) +void QT_FASTCALL rasterop_solid_NotSourceAndDestination(uint *dest, + int length, + uint color, + uint const_alpha) { Q_UNUSED(const_alpha); color = ~color | 0xff000000; @@ -2790,10 +3090,10 @@ static void QT_FASTCALL rasterop_solid_NotSourceAndDestination(uint *dest, } } -static void QT_FASTCALL rasterop_NotSourceAndDestination(uint *dest, - const uint *src, - int length, - uint const_alpha) +void QT_FASTCALL rasterop_NotSourceAndDestination(uint *dest, + const uint *src, + int length, + uint const_alpha) { Q_UNUSED(const_alpha); while (length--) { @@ -2802,10 +3102,10 @@ static void QT_FASTCALL rasterop_NotSourceAndDestination(uint *dest, } } -static void QT_FASTCALL rasterop_solid_SourceAndNotDestination(uint *dest, - int length, - uint color, - uint const_alpha) +void QT_FASTCALL rasterop_solid_SourceAndNotDestination(uint *dest, + int length, + uint color, + uint const_alpha) { Q_UNUSED(const_alpha); while (length--) { @@ -2814,10 +3114,10 @@ static void QT_FASTCALL rasterop_solid_SourceAndNotDestination(uint *dest, } } -static void QT_FASTCALL rasterop_SourceAndNotDestination(uint *dest, - const uint *src, - int length, - uint const_alpha) +void QT_FASTCALL rasterop_SourceAndNotDestination(uint *dest, + const uint *src, + int length, + uint const_alpha) { Q_UNUSED(const_alpha); while (length--) { @@ -2826,7 +3126,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, @@ -2864,7 +3164,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, @@ -3700,7 +4000,7 @@ template <> Q_STATIC_TEMPLATE_SPECIALIZATION inline quint32 alpha_4(const qargb8555 *src) { - Q_ASSERT((long(src) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); const quint8 *src8 = reinterpret_cast<const quint8*>(src); return src8[0] << 24 | src8[3] << 16 | src8[6] << 8 | src8[9]; } @@ -3883,8 +4183,8 @@ inline void interpolate_pixel_unaligned_2(DST *dest, const SRC *src, template <class DST, class SRC> inline void interpolate_pixel_2(DST *dest, const SRC *src, quint16 alpha) { - Q_ASSERT((long(dest) & 0x3) == 0); - Q_ASSERT((long(src) & 0x3) == 0); + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); const quint16 a = eff_alpha_2(alpha, dest); const quint16 ia = eff_ialpha_2(alpha, dest); @@ -3957,8 +4257,8 @@ template <class DST, class SRC> inline void interpolate_pixel_2(DST *dest, quint8 a, const SRC *src, quint8 b) { - Q_ASSERT((long(dest) & 0x3) == 0); - Q_ASSERT((long(src) & 0x3) == 0); + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); Q_ASSERT(!SRC::hasAlpha()); @@ -4006,8 +4306,8 @@ inline void interpolate_pixel_2(qrgb444 *dest, quint8 a, template <class DST, class SRC> inline void interpolate_pixel_4(DST *dest, const SRC *src, quint32 alpha) { - Q_ASSERT((long(dest) & 0x3) == 0); - Q_ASSERT((long(src) & 0x3) == 0); + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); const quint32 a = eff_alpha_4(alpha, dest); const quint32 ia = eff_ialpha_4(alpha, dest); @@ -4026,8 +4326,8 @@ template <> inline void interpolate_pixel_4(qargb8565 *dest, const qargb8565 *src, quint32 alpha) { - Q_ASSERT((long(dest) & 0x3) == 0); - Q_ASSERT((long(src) & 0x3) == 0); + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); const quint32 a = eff_alpha_4(alpha, dest); const quint32 ia = eff_ialpha_4(alpha, dest); @@ -4122,8 +4422,8 @@ template <> inline void interpolate_pixel_4(qargb8555 *dest, const qargb8555 *src, quint32 alpha) { - Q_ASSERT((long(dest) & 0x3) == 0); - Q_ASSERT((long(src) & 0x3) == 0); + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); const quint32 a = eff_alpha_4(alpha, dest); @@ -4218,8 +4518,8 @@ template <> inline void interpolate_pixel_4(qrgb888 *dest, const qrgb888 *src, quint32 alpha) { - Q_ASSERT((long(dest) & 0x3) == 0); - Q_ASSERT((long(src) & 0x3) == 0); + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); const quint32 a = eff_alpha_4(alpha, dest); const quint32 ia = eff_ialpha_4(alpha, dest); @@ -4291,8 +4591,8 @@ template <class DST, class SRC> inline void interpolate_pixel_4(DST *dest, quint8 a, const SRC *src, quint8 b) { - Q_ASSERT((long(dest) & 0x3) == 0); - Q_ASSERT((long(src) & 0x3) == 0); + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); dest[0] = dest[0].byte_mul(a) + DST(src[0]).byte_mul(b); dest[1] = dest[1].byte_mul(a) + DST(src[1]).byte_mul(b); @@ -4303,8 +4603,8 @@ inline void interpolate_pixel_4(DST *dest, quint8 a, template <class DST, class SRC> inline void blend_sourceOver_4(DST *dest, const SRC *src) { - Q_ASSERT((long(dest) & 0x3) == 0); - Q_ASSERT((long(src) & 0x3) == 0); + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); const quint32 a = alpha_4(src); if (a == 0xffffffff) { @@ -4319,8 +4619,8 @@ inline void blend_sourceOver_4(DST *dest, const SRC *src) template <> inline void blend_sourceOver_4(qargb8565 *dest, const qargb8565 *src) { - Q_ASSERT((long(dest) & 0x3) == 0); - Q_ASSERT((long(src) & 0x3) == 0); + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); const quint32 a = alpha_4(src); if (a == 0xffffffff) { @@ -4333,8 +4633,8 @@ inline void blend_sourceOver_4(qargb8565 *dest, const qargb8565 *src) template <> inline void blend_sourceOver_4(qargb8555 *dest, const qargb8555 *src) { - Q_ASSERT((long(dest) & 0x3) == 0); - Q_ASSERT((long(src) & 0x3) == 0); + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); const quint32 a = alpha_4(src); if (a == 0xffffffff) { @@ -4347,8 +4647,8 @@ inline void blend_sourceOver_4(qargb8555 *dest, const qargb8555 *src) template <> inline void blend_sourceOver_4(qargb6666 *dest, const qargb6666 *src) { - Q_ASSERT((long(dest) & 0x3) == 0); - Q_ASSERT((long(src) & 0x3) == 0); + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); const quint32 a = alpha_4(src); if (a == 0xffffffff) { @@ -4410,7 +4710,7 @@ void QT_FASTCALL blendUntransformed_dest16(DST *dest, const SRC *src, { Q_ASSERT(sizeof(DST) == 2); Q_ASSERT(sizeof(SRC) == 2); - Q_ASSERT((long(dest) & 0x3) == (long(src) & 0x3)); + Q_ASSERT((quintptr(dest) & 0x3) == (quintptr(src) & 0x3)); Q_ASSERT(coverage > 0); const int align = quintptr(dest) & 0x3; @@ -4478,8 +4778,8 @@ void QT_FASTCALL blendUntransformed_dest16(DST *dest, const SRC *src, } while (length >= 2) { - Q_ASSERT((long(dest) & 3) == 0); - Q_ASSERT((long(src) & 3) == 0); + Q_ASSERT((quintptr(dest) & 3) == 0); + Q_ASSERT((quintptr(src) & 3) == 0); const quint16 a = alpha_2(src); if (a == 0xffff) { @@ -4510,7 +4810,7 @@ template <class DST, class SRC> void QT_FASTCALL blendUntransformed_dest24(DST *dest, const SRC *src, quint8 coverage, int length) { - Q_ASSERT((long(dest) & 0x3) == (long(src) & 0x3)); + Q_ASSERT((quintptr(dest) & 0x3) == (quintptr(src) & 0x3)); Q_ASSERT(sizeof(DST) == 3); Q_ASSERT(coverage > 0); @@ -4535,7 +4835,7 @@ void QT_FASTCALL blendUntransformed_dest24(DST *dest, const SRC *src, if (SRC::hasAlpha()) { while (length >= 4) { - const quint32 alpha = BYTE_MUL(uint(alpha_4(src)), uint(coverage)); + const quint32 alpha = QT_PREPEND_NAMESPACE(BYTE_MUL)(uint(alpha_4(src)), uint(coverage)); if (alpha) interpolate_pixel_4(dest, src, alpha); length -= 4; @@ -4733,7 +5033,7 @@ static void blend_untransformed_argb8565(int count, const QSpan *spans, static void blend_untransformed_rgb565(int count, const QSpan *spans, void *userData) { -#if defined(QT_QWS_DEPTH_16) +#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16) QSpanData *data = reinterpret_cast<QSpanData *>(userData); if (data->texture.format == QImage::Format_ARGB8565_Premultiplied) @@ -4986,7 +5286,8 @@ Q_STATIC_TEMPLATE_FUNCTION void blendTiled(int count, const QSpan *spans, void * length -= copy_image_width; copy_image_width *= 2; } - qt_memconvert(dest, src, length); + if (length > 0) + qt_memconvert(dest, src, length); } else { while (length) { int l = qMin(image_width - sx, length); @@ -5071,7 +5372,7 @@ static void blend_tiled_argb8565(int count, const QSpan *spans, void *userData) static void blend_tiled_rgb565(int count, const QSpan *spans, void *userData) { -#if defined(QT_QWS_DEPTH_16) +#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16) QSpanData *data = reinterpret_cast<QSpanData *>(userData); if (data->texture.format == QImage::Format_ARGB8565_Premultiplied) @@ -5139,183 +5440,6 @@ static void blend_tiled_rgb444(int count, const QSpan *spans, void *userData) blend_tiled_generic<RegularSpans>(count, spans, userData); } - -template <SpanMethod spanMethod> -Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_bilinear_argb(int count, const QSpan *spans, void *userData) -{ - QSpanData *data = reinterpret_cast<QSpanData *>(userData); - if (data->texture.format != QImage::Format_ARGB32_Premultiplied - && data->texture.format != QImage::Format_RGB32) { - blend_src_generic<spanMethod>(count, spans, userData); - return; - } - - CompositionFunction func = functionForMode[data->rasterBuffer->compositionMode]; - uint buffer[buffer_size]; - - int image_x1 = data->texture.x1; - int image_y1 = data->texture.y1; - int image_x2 = data->texture.x2; - int image_y2 = data->texture.y2; - const int scanline_offset = data->texture.bytesPerLine / 4; - - if (data->fast_matrix) { - // The increment pr x in the scanline - int fdx = (int)(data->m11 * fixed_scale); - int fdy = (int)(data->m12 * fixed_scale); - - while (count--) { - void *t = data->rasterBuffer->scanLine(spans->y); - - uint *target = ((uint *)t) + spans->x; - uint *image_bits = (uint *)data->texture.imageData; - - const qreal cx = spans->x + 0.5; - const qreal cy = spans->y + 0.5; - - int x = int((data->m21 * cy - + data->m11 * cx + data->dx) * fixed_scale) - half_point; - int y = int((data->m22 * cy - + data->m12 * cx + data->dy) * fixed_scale) - half_point; - - int length = spans->len; - const int coverage = (data->texture.const_alpha * spans->coverage) >> 8; - while (length) { - int l = qMin(length, buffer_size); - const uint *end = buffer + l; - uint *b = buffer; - while (b < end) { - int x1 = (x >> 16); - int x2 = x1 + 1; - int y1 = (y >> 16); - int y2 = y1 + 1; - - int distx = ((x - (x1 << 16)) >> 8); - int disty = ((y - (y1 << 16)) >> 8); - int idistx = 256 - distx; - int idisty = 256 - disty; - - x1 = qBound(image_x1, x1, image_x2 - 1); - x2 = qBound(image_x1, x2, image_x2 - 1); - y1 = qBound(image_y1, y1, image_y2 - 1); - y2 = qBound(image_y1, y2, image_y2 - 1); - - int y1_offset = y1 * scanline_offset; - int y2_offset = y2 * scanline_offset; - -#if defined(Q_IRIX_GCC3_3_WORKAROUND) - uint tl = gccBug(image_bits[y1_offset + x1]); - uint tr = gccBug(image_bits[y1_offset + x2]); - uint bl = gccBug(image_bits[y2_offset + x1]); - uint br = gccBug(image_bits[y2_offset + x2]); -#else - uint tl = image_bits[y1_offset + x1]; - uint tr = image_bits[y1_offset + x2]; - uint bl = image_bits[y2_offset + x1]; - uint br = image_bits[y2_offset + x2]; -#endif - - uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx); - uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx); - *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty); - ++b; - - x += fdx; - y += fdy; - } - if (spanMethod == RegularSpans) - func(target, buffer, l, coverage); - else - drawBufferSpan(data, buffer, buffer_size, - spans->x + spans->len - length, - spans->y, l, coverage); - target += l; - length -= l; - } - ++spans; - } - } else { - const qreal fdx = data->m11; - const qreal fdy = data->m12; - const qreal fdw = data->m13; - - while (count--) { - void *t = data->rasterBuffer->scanLine(spans->y); - - uint *target = ((uint *)t) + spans->x; - uint *image_bits = (uint *)data->texture.imageData; - - const qreal cx = spans->x + 0.5; - const qreal cy = spans->y + 0.5; - - qreal x = data->m21 * cy + data->m11 * cx + data->dx; - qreal y = data->m22 * cy + data->m12 * cx + data->dy; - qreal w = data->m23 * cy + data->m13 * cx + data->m33; - - int length = spans->len; - const int coverage = (data->texture.const_alpha * spans->coverage) >> 8; - while (length) { - int l = qMin(length, buffer_size); - const uint *end = buffer + l; - uint *b = buffer; - while (b < end) { - const qreal iw = w == 0 ? 1 : 1 / w; - const qreal px = x * iw - 0.5; - const qreal py = y * iw - 0.5; - - int x1 = int(px) - (px < 0); - int x2 = x1 + 1; - int y1 = int(py) - (py < 0); - int y2 = y1 + 1; - - int distx = int((px - x1) * 256); - int disty = int((py - y1) * 256); - int idistx = 256 - distx; - int idisty = 256 - disty; - - x1 = qBound(image_x1, x1, image_x2 - 1); - x2 = qBound(image_x1, x2, image_x2 - 1); - y1 = qBound(image_y1, y1, image_y2 - 1); - y2 = qBound(image_y1, y2, image_y2 - 1); - - int y1_offset = y1 * scanline_offset; - int y2_offset = y2 * scanline_offset; - -#if defined(Q_IRIX_GCC3_3_WORKAROUND) - uint tl = gccBug(image_bits[y1_offset + x1]); - uint tr = gccBug(image_bits[y1_offset + x2]); - uint bl = gccBug(image_bits[y2_offset + x1]); - uint br = gccBug(image_bits[y2_offset + x2]); -#else - uint tl = image_bits[y1_offset + x1]; - uint tr = image_bits[y1_offset + x2]; - uint bl = image_bits[y2_offset + x1]; - uint br = image_bits[y2_offset + x2]; -#endif - - uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx); - uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx); - *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty); - ++b; - - x += fdx; - y += fdy; - w += fdw; - } - if (spanMethod == RegularSpans) - func(target, buffer, l, coverage); - else - drawBufferSpan(data, buffer, buffer_size, - spans->x + spans->len - length, - spans->y, l, coverage); - target += l; - length -= l; - } - ++spans; - } - } -} - template <class DST, class SRC> Q_STATIC_TEMPLATE_FUNCTION void blendTransformedBilinear(int count, const QSpan *spans, void *userData) @@ -5365,17 +5489,27 @@ Q_STATIC_TEMPLATE_FUNCTION void blendTransformedBilinear(int count, const QSpan SRC *b = buffer; while (b < end) { int x1 = (x >> 16); - int x2 = x1 + 1; + int x2; int y1 = (y >> 16); - int y2 = y1 + 1; + int y2; - const int distx = ((x - (x1 << 16)) >> 8); - const int disty = ((y - (y1 << 16)) >> 8); - x1 = qBound(src_minx, x1, src_maxx); - x2 = qBound(src_minx, x2, src_maxx); - y1 = qBound(src_miny, y1, src_maxy); - y2 = qBound(src_miny, y2, src_maxy); + const int distx = (x & 0x0000ffff) >> 8; + const int disty = (y & 0x0000ffff) >> 8; + if (x1 < src_minx) { + x2 = x1 = src_minx; + } else if (x1 >= src_maxx) { + x2 = x1 = src_maxx; + } else { + x2 = x1 + 1; + } + if (y1 < src_miny) { + y2 = y1 = src_miny; + } else if (y1 >= src_maxy) { + y2 = y1 = src_maxy; + } else { + y2 = y1 + 1; + } #if 0 if (x1 == x2) { if (y1 == y2) { @@ -5466,17 +5600,27 @@ Q_STATIC_TEMPLATE_FUNCTION void blendTransformedBilinear(int count, const QSpan const qreal py = y * iw - qreal(0.5); int x1 = int(px) - (px < 0); - int x2 = x1 + 1; + int x2; int y1 = int(py) - (py < 0); - int y2 = y1 + 1; + int y2; const int distx = int((px - x1) * 256); const int disty = int((py - y1) * 256); - x1 = qBound(src_minx, x1, src_maxx); - x2 = qBound(src_minx, x2, src_maxx); - y1 = qBound(src_miny, y1, src_maxy); - y2 = qBound(src_miny, y2, src_maxy); + if (x1 < src_minx) { + x2 = x1 = src_minx; + } else if (x1 >= src_maxx) { + x2 = x1 = src_maxx; + } else { + x2 = x1 + 1; + } + if (y1 < src_miny) { + y2 = y1 = src_miny; + } else if (y1 >= src_maxy) { + y2 = y1 = src_maxy; + } else { + y2 = y1 + 1; + } const SRC *src1 = (SRC*)data->texture.scanLine(y1); const SRC *src2 = (SRC*)data->texture.scanLine(y2); @@ -5576,7 +5720,7 @@ static void blend_transformed_bilinear_argb8565(int count, const QSpan *spans, v static void blend_transformed_bilinear_rgb565(int count, const QSpan *spans, void *userData) { -#if defined(QT_QWS_DEPTH_16) +#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16) QSpanData *data = reinterpret_cast<QSpanData *>(userData); if (data->texture.format == QImage::Format_RGB16) @@ -5645,197 +5789,6 @@ static void blend_transformed_bilinear_rgb444(int count, const QSpan *spans, voi } template <SpanMethod spanMethod> -Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_bilinear_tiled_argb(int count, const QSpan *spans, void *userData) -{ - QSpanData *data = reinterpret_cast<QSpanData *>(userData); - if (data->texture.format != QImage::Format_ARGB32_Premultiplied - && data->texture.format != QImage::Format_RGB32) { - blend_src_generic<spanMethod>(count, spans, userData); - return; - } - - CompositionFunction func = functionForMode[data->rasterBuffer->compositionMode]; - uint buffer[buffer_size]; - - int image_width = data->texture.width; - int image_height = data->texture.height; - const int scanline_offset = data->texture.bytesPerLine / 4; - - if (data->fast_matrix) { - // The increment pr x in the scanline - int fdx = (int)(data->m11 * fixed_scale); - int fdy = (int)(data->m12 * fixed_scale); - - while (count--) { - void *t = data->rasterBuffer->scanLine(spans->y); - - uint *target = ((uint *)t) + spans->x; - uint *image_bits = (uint *)data->texture.imageData; - - const qreal cx = spans->x + 0.5; - const qreal cy = spans->y + 0.5; - - int x = int((data->m21 * cy - + data->m11 * cx + data->dx) * fixed_scale) - half_point; - int y = int((data->m22 * cy - + data->m12 * cx + data->dy) * fixed_scale) - half_point; - - int length = spans->len; - const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; - while (length) { - int l = qMin(length, buffer_size); - const uint *end = buffer + l; - uint *b = buffer; - while (b < end) { - int x1 = (x >> 16); - int x2 = (x1 + 1); - int y1 = (y >> 16); - int y2 = (y1 + 1); - - int distx = ((x - (x1 << 16)) >> 8); - int disty = ((y - (y1 << 16)) >> 8); - int idistx = 256 - distx; - int idisty = 256 - disty; - - x1 %= image_width; - x2 %= image_width; - y1 %= image_height; - y2 %= image_height; - - if (x1 < 0) x1 += image_width; - if (x2 < 0) x2 += image_width; - if (y1 < 0) y1 += image_height; - if (y2 < 0) y2 += image_height; - - Q_ASSERT(x1 >= 0 && x1 < image_width); - Q_ASSERT(x2 >= 0 && x2 < image_width); - Q_ASSERT(y1 >= 0 && y1 < image_height); - Q_ASSERT(y2 >= 0 && y2 < image_height); - - int y1_offset = y1 * scanline_offset; - int y2_offset = y2 * scanline_offset; - -#if defined(Q_IRIX_GCC3_3_WORKAROUND) - uint tl = gccBug(image_bits[y1_offset + x1]); - uint tr = gccBug(image_bits[y1_offset + x2]); - uint bl = gccBug(image_bits[y2_offset + x1]); - uint br = gccBug(image_bits[y2_offset + x2]); -#else - uint tl = image_bits[y1_offset + x1]; - uint tr = image_bits[y1_offset + x2]; - uint bl = image_bits[y2_offset + x1]; - uint br = image_bits[y2_offset + x2]; -#endif - - uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx); - uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx); - *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty); - ++b; - x += fdx; - y += fdy; - } - if (spanMethod == RegularSpans) - func(target, buffer, l, coverage); - else - drawBufferSpan(data, buffer, buffer_size, - spans->x + spans->len - length, - spans->y, l, coverage); - target += l; - length -= l; - } - ++spans; - } - } else { - const qreal fdx = data->m11; - const qreal fdy = data->m12; - const qreal fdw = data->m13; - while (count--) { - void *t = data->rasterBuffer->scanLine(spans->y); - - uint *target = ((uint *)t) + spans->x; - uint *image_bits = (uint *)data->texture.imageData; - - const qreal cx = spans->x + 0.5; - const qreal cy = spans->y + 0.5; - - qreal x = data->m21 * cy + data->m11 * cx + data->dx; - qreal y = data->m22 * cy + data->m12 * cx + data->dy; - qreal w = data->m23 * cy + data->m13 * cx + data->m33; - - int length = spans->len; - const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; - while (length) { - int l = qMin(length, buffer_size); - const uint *end = buffer + l; - uint *b = buffer; - while (b < end) { - const qreal iw = w == 0 ? 1 : 1 / w; - const qreal px = x * iw - 0.5; - const qreal py = y * iw - 0.5; - - int x1 = int(px) - (px < 0); - int x2 = x1 + 1; - int y1 = int(py) - (py < 0); - int y2 = y1 + 1; - - int distx = int((px - x1) * 256); - int disty = int((py - y1) * 256); - int idistx = 256 - distx; - int idisty = 256 - disty; - - x1 %= image_width; - x2 %= image_width; - y1 %= image_height; - y2 %= image_height; - - if (x1 < 0) x1 += image_width; - if (x2 < 0) x2 += image_width; - if (y1 < 0) y1 += image_height; - if (y2 < 0) y2 += image_height; - - Q_ASSERT(x1 >= 0 && x1 < image_width); - Q_ASSERT(x2 >= 0 && x2 < image_width); - Q_ASSERT(y1 >= 0 && y1 < image_height); - Q_ASSERT(y2 >= 0 && y2 < image_height); - - int y1_offset = y1 * scanline_offset; - int y2_offset = y2 * scanline_offset; - -#if defined(Q_IRIX_GCC3_3_WORKAROUND) - uint tl = gccBug(image_bits[y1_offset + x1]); - uint tr = gccBug(image_bits[y1_offset + x2]); - uint bl = gccBug(image_bits[y2_offset + x1]); - uint br = gccBug(image_bits[y2_offset + x2]); -#else - uint tl = image_bits[y1_offset + x1]; - uint tr = image_bits[y1_offset + x2]; - uint bl = image_bits[y2_offset + x1]; - uint br = image_bits[y2_offset + x2]; -#endif - - uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx); - uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx); - *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty); - ++b; - x += fdx; - y += fdy; - w += fdw; - } - if (spanMethod == RegularSpans) - func(target, buffer, l, coverage); - else - drawBufferSpan(data, buffer, buffer_size, - spans->x + spans->len - length, - spans->y, l, coverage); - target += l; - length -= l; - } - ++spans; - } - } -} - -template <SpanMethod spanMethod> Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_argb(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); @@ -6163,7 +6116,7 @@ static void blend_transformed_argb8565(int count, const QSpan *spans, static void blend_transformed_rgb565(int count, const QSpan *spans, void *userData) { -#if defined(QT_QWS_DEPTH_16) +#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16) QSpanData *data = reinterpret_cast<QSpanData *>(userData); if (data->texture.format == QImage::Format_ARGB8565_Premultiplied) @@ -6576,7 +6529,7 @@ static void blend_transformed_tiled_argb8565(int count, const QSpan *spans, static void blend_transformed_tiled_rgb565(int count, const QSpan *spans, void *userData) { -#if defined(QT_QWS_DEPTH_16) +#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16) QSpanData *data = reinterpret_cast<QSpanData *>(userData); if (data->texture.format == QImage::Format_ARGB8565_Premultiplied) @@ -6737,7 +6690,7 @@ static const ProcessSpans processTextureSpans[NBlendTypes][QImage::NImageFormats SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Indexed8 SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB32 SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32 - SPANFUNC_POINTER(blend_transformed_bilinear_argb, RegularSpans), // ARGB32_Premultiplied + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32_Premultiplied blend_transformed_bilinear_rgb565, blend_transformed_bilinear_argb8565, blend_transformed_bilinear_rgb666, @@ -6756,7 +6709,7 @@ static const ProcessSpans processTextureSpans[NBlendTypes][QImage::NImageFormats SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Indexed8 SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB32 SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32 - SPANFUNC_POINTER(blend_transformed_bilinear_tiled_argb, RegularSpans), // ARGB32_Premultiplied + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32_Premultiplied SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB16 SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB8565_Premultiplied SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB666 @@ -6855,7 +6808,7 @@ static const ProcessSpans processTextureSpansCallback[NBlendTypes][QImage::NImag blend_src_generic<CallbackSpans>, // Indexed8 blend_src_generic<CallbackSpans>, // RGB32 blend_src_generic<CallbackSpans>, // ARGB32 - blend_transformed_bilinear_argb<CallbackSpans>, // ARGB32_Premultiplied + blend_src_generic<CallbackSpans>, // ARGB32_Premultiplied blend_src_generic<CallbackSpans>, // RGB16 blend_src_generic<CallbackSpans>, // ARGB8565_Premultiplied blend_src_generic<CallbackSpans>, // RGB666 @@ -6874,7 +6827,7 @@ static const ProcessSpans processTextureSpansCallback[NBlendTypes][QImage::NImag blend_src_generic<CallbackSpans>, // Indexed8 blend_src_generic<CallbackSpans>, // RGB32 blend_src_generic<CallbackSpans>, // ARGB32 - blend_transformed_bilinear_tiled_argb<CallbackSpans>, // ARGB32_Premultiplied + blend_src_generic<CallbackSpans>, // ARGB32_Premultiplied blend_src_generic<CallbackSpans>, // RGB16 blend_src_generic<CallbackSpans>, // ARGB8565_Premultiplied blend_src_generic<CallbackSpans>, // RGB666 @@ -7134,6 +7087,11 @@ void qt_build_pow_tables() { int winSmooth; if (SystemParametersInfo(0x200C /* SPI_GETFONTSMOOTHINGCONTRAST */, 0, &winSmooth, 0)) smoothing = winSmooth / 1000.0; + + // Safeguard ourselves against corrupt registry values... + if (smoothing > 5 || smoothing < 1) + smoothing = 1.4; + #endif #ifdef Q_WS_X11 @@ -7720,295 +7678,8 @@ static void qt_memfill16_setup(quint16 *dest, quint16 value, int count); qt_memfill32_func qt_memfill32 = qt_memfill32_setup; qt_memfill16_func qt_memfill16 = qt_memfill16_setup; -enum CPUFeatures { - None = 0, - MMX = 0x1, - MMXEXT = 0x2, - MMX3DNOW = 0x4, - MMX3DNOWEXT = 0x8, - SSE = 0x10, - SSE2 = 0x20, - CMOV = 0x40, - IWMMXT = 0x80, - NEON = 0x100 -}; - -static uint detectCPUFeatures() -{ -#if defined (Q_OS_WINCE) -#if defined (ARM) - if (IsProcessorFeaturePresent(PF_ARM_INTEL_WMMX)) - return IWMMXT; -#elif defined(_X86_) - uint features = 0; -#if defined QT_HAVE_MMX - if (IsProcessorFeaturePresent(PF_MMX_INSTRUCTIONS_AVAILABLE)) - features |= MMX; -#endif -#if defined QT_HAVE_3DNOW - if (IsProcessorFeaturePresent(PF_3DNOW_INSTRUCTIONS_AVAILABLE)) - features |= MMX3DNOW; -#endif - return features; -#endif - return 0; -#elif defined(QT_HAVE_IWMMXT) - // runtime detection only available when running as a previlegied process - static const bool doIWMMXT = !qgetenv("QT_NO_IWMMXT").toInt(); - return doIWMMXT ? IWMMXT : 0; -#elif defined(QT_HAVE_NEON) - static const bool doNEON = !qgetenv("QT_NO_NEON").toInt(); - return doNEON ? NEON : 0; -#else - uint features = 0; -#if defined(__x86_64__) || defined(Q_OS_WIN64) - features = MMX|SSE|SSE2|CMOV; -#elif defined(__ia64__) - features = MMX|SSE|SSE2; -#elif defined(__i386__) || defined(_M_IX86) - unsigned int extended_result = 0; - uint result = 0; - /* see p. 118 of amd64 instruction set manual Vol3 */ -#if defined(Q_CC_GNU) - asm ("push %%ebx\n" - "pushf\n" - "pop %%eax\n" - "mov %%eax, %%ebx\n" - "xor $0x00200000, %%eax\n" - "push %%eax\n" - "popf\n" - "pushf\n" - "pop %%eax\n" - "xor %%edx, %%edx\n" - "xor %%ebx, %%eax\n" - "jz 1f\n" - - "mov $0x00000001, %%eax\n" - "cpuid\n" - "1:\n" - "pop %%ebx\n" - "mov %%edx, %0\n" - : "=r" (result) - : - : "%eax", "%ecx", "%edx" - ); - - asm ("push %%ebx\n" - "pushf\n" - "pop %%eax\n" - "mov %%eax, %%ebx\n" - "xor $0x00200000, %%eax\n" - "push %%eax\n" - "popf\n" - "pushf\n" - "pop %%eax\n" - "xor %%edx, %%edx\n" - "xor %%ebx, %%eax\n" - "jz 2f\n" - - "mov $0x80000000, %%eax\n" - "cpuid\n" - "cmp $0x80000000, %%eax\n" - "jbe 2f\n" - "mov $0x80000001, %%eax\n" - "cpuid\n" - "2:\n" - "pop %%ebx\n" - "mov %%edx, %0\n" - : "=r" (extended_result) - : - : "%eax", "%ecx", "%edx" - ); -#elif defined (Q_OS_WIN) - _asm { - push eax - push ebx - push ecx - push edx - pushfd - pop eax - mov ebx, eax - xor eax, 00200000h - push eax - popfd - pushfd - pop eax - mov edx, 0 - xor eax, ebx - jz skip - - mov eax, 1 - cpuid - mov result, edx - skip: - pop edx - pop ecx - pop ebx - pop eax - } - - _asm { - push eax - push ebx - push ecx - push edx - pushfd - pop eax - mov ebx, eax - xor eax, 00200000h - push eax - popfd - pushfd - pop eax - mov edx, 0 - xor eax, ebx - jz skip2 - - mov eax, 80000000h - cpuid - cmp eax, 80000000h - jbe skip2 - mov eax, 80000001h - cpuid - mov extended_result, edx - skip2: - pop edx - pop ecx - pop ebx - pop eax - } -#endif - - // result now contains the standard feature bits - if (result & (1u << 15)) - features |= CMOV; - if (result & (1u << 23)) - features |= MMX; - if (extended_result & (1u << 22)) - features |= MMXEXT; - if (extended_result & (1u << 31)) - features |= MMX3DNOW; - if (extended_result & (1u << 30)) - features |= MMX3DNOWEXT; - if (result & (1u << 25)) - features |= SSE; - if (result & (1u << 26)) - features |= SSE2; -#endif // i386 - - if (qgetenv("QT_NO_MMX").toInt()) - features ^= MMX; - if (qgetenv("QT_NO_MMXEXT").toInt()) - features ^= MMXEXT; - if (qgetenv("QT_NO_3DNOW").toInt()) - features ^= MMX3DNOW; - if (qgetenv("QT_NO_3DNOWEXT").toInt()) - features ^= MMX3DNOWEXT; - if (qgetenv("QT_NO_SSE").toInt()) - features ^= SSE; - if (qgetenv("QT_NO_SSE2").toInt()) - features ^= SSE2; - - return features; -#endif -} - -#if defined(Q_CC_RVCT) && defined(QT_HAVE_ARMV6) -// Move these to qdrawhelper_arm.c when all -// functions are implemented using arm assembly. -static CompositionFunctionSolid qt_functionForModeSolid_ARMv6[numCompositionFunctions] = { - comp_func_solid_SourceOver, - comp_func_solid_DestinationOver, - comp_func_solid_Clear, - comp_func_solid_Source, - comp_func_solid_Destination, - comp_func_solid_SourceIn, - comp_func_solid_DestinationIn, - comp_func_solid_SourceOut, - comp_func_solid_DestinationOut, - comp_func_solid_SourceAtop, - comp_func_solid_DestinationAtop, - comp_func_solid_XOR, - comp_func_solid_Plus, - comp_func_solid_Multiply, - comp_func_solid_Screen, - comp_func_solid_Overlay, - comp_func_solid_Darken, - comp_func_solid_Lighten, - comp_func_solid_ColorDodge, - comp_func_solid_ColorBurn, - comp_func_solid_HardLight, - comp_func_solid_SoftLight, - comp_func_solid_Difference, - comp_func_solid_Exclusion, - rasterop_solid_SourceOrDestination, - rasterop_solid_SourceAndDestination, - rasterop_solid_SourceXorDestination, - rasterop_solid_NotSourceAndNotDestination, - rasterop_solid_NotSourceOrNotDestination, - rasterop_solid_NotSourceXorDestination, - rasterop_solid_NotSource, - rasterop_solid_NotSourceAndDestination, - rasterop_solid_SourceAndNotDestination -}; - -static CompositionFunction qt_functionForMode_ARMv6[numCompositionFunctions] = { - comp_func_SourceOver_armv6, - comp_func_DestinationOver, - comp_func_Clear, - comp_func_Source_armv6, - comp_func_Destination, - comp_func_SourceIn, - comp_func_DestinationIn, - comp_func_SourceOut, - comp_func_DestinationOut, - comp_func_SourceAtop, - comp_func_DestinationAtop, - comp_func_XOR, - comp_func_Plus, - comp_func_Multiply, - comp_func_Screen, - comp_func_Overlay, - comp_func_Darken, - comp_func_Lighten, - comp_func_ColorDodge, - comp_func_ColorBurn, - comp_func_HardLight, - comp_func_SoftLight, - comp_func_Difference, - comp_func_Exclusion, - rasterop_SourceOrDestination, - rasterop_SourceAndDestination, - rasterop_SourceXorDestination, - rasterop_NotSourceAndNotDestination, - rasterop_NotSourceOrNotDestination, - rasterop_NotSourceXorDestination, - rasterop_NotSource, - rasterop_NotSourceAndDestination, - rasterop_SourceAndNotDestination -}; - -static void qt_blend_color_argb_armv6(int count, const QSpan *spans, void *userData) -{ - QSpanData *data = reinterpret_cast<QSpanData *>(userData); - - CompositionFunctionSolid func = qt_functionForModeSolid_ARMv6[data->rasterBuffer->compositionMode]; - while (count--) { - uint *target = ((uint *)data->rasterBuffer->scanLine(spans->y)) + spans->x; - func(target, spans->len, data->solid.color, spans->coverage); - ++spans; - } -} - -#endif // Q_CC_RVCT && QT_HAVE_ARMV6 - - void qInitDrawhelperAsm() { - static uint features = 0xffffffff; - if (features != 0xffffffff) - return; - features = detectCPUFeatures(); qt_memfill32 = qt_memfill_template<quint32, quint32>; qt_memfill16 = qt_memfill_quint16; //qt_memfill_template<quint16, quint16>; @@ -8016,7 +7687,7 @@ void qInitDrawhelperAsm() CompositionFunction *functionForModeAsm = 0; CompositionFunctionSolid *functionForModeSolidAsm = 0; -#ifdef QT_NO_DEBUG + const uint features = qDetectCPUFeatures(); if (false) { #ifdef QT_HAVE_SSE2 } else if (features & SSE2) { @@ -8053,6 +7724,7 @@ void qInitDrawhelperAsm() #ifdef QT_HAVE_MMX if (features & MMX) { functionForModeAsm = qt_functionForMode_MMX; + functionForModeSolidAsm = qt_functionForModeSolid_MMX; qDrawHelper[QImage::Format_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_mmx; #ifdef QT_HAVE_3DNOW @@ -8082,6 +7754,55 @@ void qInitDrawhelperAsm() #ifdef QT_HAVE_SSE if (features & SSE) { + extern void qt_blend_rgb32_on_rgb32_sse(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + extern void qt_blend_argb32_on_argb32_sse(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + + qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse; + qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse; + } +#endif // SSE + +#ifdef QT_HAVE_SSE2 + if (features & SSE2) { + extern void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + extern void qt_blend_argb32_on_argb32_sse2(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + + qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2; + qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2; + } + +#ifdef QT_HAVE_SSSE3 + if (features & SSSE3) { + extern void qt_blend_argb32_on_argb32_ssse3(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + + qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; + } +#endif // SSSE3 + +#endif // SSE2 + +#ifdef QT_HAVE_SSE + if (features & SSE) { functionForModeAsm = qt_functionForMode_SSE; functionForModeSolidAsm = qt_functionForModeSolid_SSE; qDrawHelper[QImage::Format_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_sse; @@ -8096,40 +7817,28 @@ void qInitDrawhelperAsm() #ifdef QT_HAVE_SSE2 if (features & SSE2) { - extern void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl, - const uchar *srcPixels, int sbpl, - int w, int h, - int const_alpha); - extern void qt_blend_argb32_on_argb32_sse2(uchar *destPixels, int dbpl, - const uchar *srcPixels, int sbpl, - int w, int h, - int const_alpha); - - - qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2; - qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2; - qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2; - qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2; - } else -#endif - { - extern void qt_blend_rgb32_on_rgb32_sse(uchar *destPixels, int dbpl, - const uchar *srcPixels, int sbpl, - int w, int h, - int const_alpha); - extern void qt_blend_argb32_on_argb32_sse(uchar *destPixels, int dbpl, - const uchar *srcPixels, int sbpl, - int w, int h, - int const_alpha); - - - qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse; - qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse; - qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse; - qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse; + extern void QT_FASTCALL comp_func_SourceOver_sse2(uint *destPixels, + const uint *srcPixels, + int length, + uint const_alpha); + extern void QT_FASTCALL comp_func_solid_SourceOver_sse2(uint *destPixels, int length, uint color, uint const_alpha); + extern void QT_FASTCALL comp_func_Plus_sse2(uint *dst, const uint *src, int length, uint const_alpha); + extern void QT_FASTCALL comp_func_Source_sse2(uint *dst, const uint *src, int length, uint const_alpha); + + functionForModeAsm[0] = comp_func_SourceOver_sse2; + functionForModeAsm[QPainter::CompositionMode_Source] = comp_func_Source_sse2; + functionForModeAsm[QPainter::CompositionMode_Plus] = comp_func_Plus_sse2; + functionForModeSolidAsm[0] = comp_func_solid_SourceOver_sse2; } -} -#endif // SSE +#endif + } +#elif defined(QT_HAVE_SSE2) + // this is the special case when SSE2 is usable but MMX/SSE is not usable (e.g.: Windows x64 + visual studio) + if (features & SSE2) { + functionForModeAsm = qt_functionForMode_onlySSE2; + functionForModeSolidAsm = qt_functionForModeSolid_onlySSE2; + } +#endif #ifdef QT_HAVE_IWMMXT if (features & IWMMXT) { @@ -8139,27 +7848,39 @@ void qInitDrawhelperAsm() } #endif // IWMMXT -#endif // QT_NO_DEBUG +#if defined(QT_HAVE_ARM_SIMD) + qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_arm_simd; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_arm_simd; + qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_arm_simd; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_arm_simd; +#elif defined(QT_HAVE_NEON) + if (features & NEON) { + qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon; + 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; + qBlendFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_blend_rgb16_on_rgb16_neon; -#if defined(Q_CC_RVCT) && defined(QT_HAVE_ARMV6) - functionForModeAsm = qt_functionForMode_ARMv6; - functionForModeSolidAsm = qt_functionForModeSolid_ARMv6; + 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; - qt_memfill32 = qt_memfill32_armv6; + 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_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_armv6; + qDrawHelper[QImage::Format_RGB16].alphamapBlit = qt_alphamapblit_quint16_neon; - qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_armv6; - qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_armv6; - qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_armv6; - qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_armv6; -#elif defined(QT_HAVE_NEON) - if (features & NEON) { - qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon; - 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; - } + functionForMode_C[QPainter::CompositionMode_SourceOver] = qt_blend_argb32_on_argb32_scanline_neon; + functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_neon; + functionForMode_C[QPainter::CompositionMode_Plus] = comp_func_Plus_neon; + destFetchProc[QImage::Format_RGB16] = qt_destFetchRGB16_neon; + destStoreProc[QImage::Format_RGB16] = qt_destStoreRGB16_neon; + + qMemRotateFunctions[QImage::Format_RGB16][0] = qt_memrotate90_16_neon; + qMemRotateFunctions[QImage::Format_RGB16][2] = qt_memrotate270_16_neon; + qt_memfill32 = qt_memfill32_neon; + } #endif if (functionForModeSolidAsm) { @@ -8173,17 +7894,8 @@ void qInitDrawhelperAsm() functionForModeSolid = functionForModeSolidAsm; } - if (functionForModeAsm) { - const int destinationMode = QPainter::CompositionMode_Destination; - functionForModeAsm[destinationMode] = functionForMode_C[destinationMode]; - - // use the default qdrawhelper implementation for the - // extended composition modes - for (int mode = 12; mode < numCompositionFunctions; ++mode) - functionForModeAsm[mode] = functionForMode_C[mode]; - + if (functionForModeAsm) functionForMode = functionForModeAsm; - } qt_build_pow_tables(); } diff --git a/src/gui/painting/qdrawhelper_arm_simd.cpp b/src/gui/painting/qdrawhelper_arm_simd.cpp new file mode 100644 index 0000000..2a5f5e4 --- /dev/null +++ b/src/gui/painting/qdrawhelper_arm_simd.cpp @@ -0,0 +1,332 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include "qdrawhelper_arm_simd_p.h" + +#include <private/qpaintengine_raster_p.h> +#include <private/qblendfunctions_p.h> + +#ifdef QT_HAVE_ARM_SIMD + +#if defined(Q_OS_SYMBIAN) +#if !defined(__SWITCH_TO_ARM) +#ifdef __MARM_THUMB__ +#ifndef __ARMCC__ +#define __SWITCH_TO_ARM asm("push {r0} ");\ + asm("add r0, pc, #4 ");\ + asm("bx r0 ");\ + asm("nop ");\ + asm(".align 2 ");\ + asm(".code 32 ");\ + asm("ldr r0, [sp], #4 ") +#define __END_ARM asm(".code 16 ") +#else +#define __SWITCH_TO_ARM asm(".code 32 "); +#define __END_ARM +#endif // __ARMCC__ +#else +#define __SWITCH_TO_ARM +#define __END_ARM +#endif //__MARM_THUMB__ +#endif +#endif + +#if defined(Q_OS_SYMBIAN) && defined(Q_CC_RVCT) +__asm void qt_blend_argb32_on_argb32_arm_simd(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha) +{ +#ifndef __ARMCC__ + __SWITCH_TO_ARM; +#else + CODE32 +#endif // __ARMCC__ + + stmfd sp!, {r4-r12, r14} + + // read arguments off the stack + add r8, sp, #10 * 4 + ldmia r8, {r4-r6} + + // adjust dbpl and sbpl + mov r14, #4 + mul r14, r4, r14 + sub r1, r1, r14 + sub r3, r3, r14 + + // load 0xFF00FF00 to r12 + mov r12, #0xFF000000 + add r12, r12, #0xFF00 + + // load 0x800080 to r14 + mov r14, #0x800000 + add r14, r14, #0x80 + + /* + Registers: + r0 dst + r1 dbpl + r2 src + r3 sbpl + r4 w + r5 h + r6 const_alpha + r12 0xFF0000 + r14 0x800080 + */ + + cmp r6, #256 //test if we have fully opaque constant alpha value + bne argb32constalpha // branch if not + +argb32_next_row + + mov r7, r4 + +argb32_next_pixel + + ldr r8, [r2], #4 // load src pixel + + // Negate r8 and extract src alpha + mvn r11, r8 // bitwise not + uxtb r11, r11, ror #24 + + cmp r11, #0 // test for full src opacity (negated) + beq argb32_no_blend + + cmp r11, #255 // test for full src transparency (negated) + addeq r0, #4 + beq argb32_nop + + ldr r9, [r0] // load dst pixel + + // blend + uxtb16 r10, r9 + uxtb16 r6, r9, ror #8 + mla r10, r11, r10, r14 + mla r9, r6, r11, r14 + uxtab16 r10, r10, r10, ror #8 + uxtab16 r9, r9, r9, ror #8 + and r9, r9, r12 + uxtab16 r10, r9, r10, ror #8 + + uqadd8 r8, r10, r8 + +argb32_no_blend + + str r8, [r0], #4 + +argb32_nop + + subs r7, r7, #1 + bgt argb32_next_pixel + + add r0, r0, r1 // dest = dest + dbpl + add r2, r2, r3 // src = src + sbpl + + subs r5, r5, #1 + bgt argb32_next_row + + b argb32_blend_exit + +argb32constalpha + + cmp r6, #0 + beq argb32_blend_exit + + ; const_alpha = (const_alpha * 255) >> 8; + mov r11, #255 + mul r6, r6, r11 + mov r11, r6, lsr #8 + +argb32constalpha_next_row + + mov r7, r4 + +argb32constalpha_next_pixel + + ldr r9, [r2], #4 // load src pixel + + // blend + uxtb16 r10, r9 + uxtb16 r6, r9, ror #8 + mla r10, r11, r10, r14 + mla r9, r6, r11, r14 + uxtab16 r10, r10, r10, ror #8 + uxtab16 r9, r9, r9, ror #8 + and r9, r9, r12 + uxtab16 r8, r9, r10, ror #8 + + ldr r9, [r0] // load dst pixel + + // blend + uxtb16 r10, r9 + uxtb16 r6, r9, ror #8 + + // Negate r11 and extract src alpha + mvn r9, r11 // bitwise not + uxtb r9, r9, ror #24 + + mla r10, r9, r10, r14 + mla r9, r6, r9, r14 + uxtab16 r10, r10, r10, ror #8 + uxtab16 r9, r9, r9, ror #8 + and r9, r9, r12 + uxtab16 r10, r9, r10, ror #8 + + uqadd8 r8, r10, r8 + + str r8, [r0], #4 + + subs r7, r7, #1 + bgt argb32constalpha_next_pixel + + add r0, r0, r1 // dest = dest + dbpl + add r2, r2, r3 // src = src + sbpl + + subs r5, r5, #1 + bgt argb32constalpha_next_row + +argb32_blend_exit + + // Restore registers + ldmfd sp!, {r4-r12, lr} + bx lr + + __END_ARM +} + +void qt_blend_rgb32_on_rgb32_arm_simd(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha) +{ + if (const_alpha != 256) { + qt_blend_argb32_on_argb32_arm_simd(destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha); + return; + } + + const uint *src = (const uint *) srcPixels; + uint *dst = (uint *) destPixels; + if (w <= 64) { + for (int y=0; y<h; ++y) { + qt_memconvert(dst, src, w); + dst = (quint32 *)(((uchar *) dst) + dbpl); + src = (const quint32 *)(((const uchar *) src) + sbpl); + } + } else { + int len = w * 4; + for (int y=0; y<h; ++y) { + memcpy(dst, src, len); + dst = (quint32 *)(((uchar *) dst) + dbpl); + src = (const quint32 *)(((const uchar *) src) + sbpl); + } + } +} + +#else // defined(Q_OS_SYMBIAN) && defined(Q_CC_RVCT) + +// TODO: add GNU assembler instructions and support for other platforms. +// Default to C code for now + +void qt_blend_argb32_on_argb32_arm_simd(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; + if (const_alpha == 256) { + for (int y=0; y<h; ++y) { + for (int x=0; 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); + } + } else if (const_alpha != 0) { + const_alpha = (const_alpha * 255) >> 8; + for (int y=0; y<h; ++y) { + for (int x=0; x<w; ++x) { + uint s = BYTE_MUL(src[x], const_alpha); + dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); + } + dst = (quint32 *)(((uchar *) dst) + dbpl); + src = (const quint32 *)(((const uchar *) src) + sbpl); + } + } +} + +void qt_blend_rgb32_on_rgb32_arm_simd(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha) +{ + if (const_alpha != 256) { + qt_blend_argb32_on_argb32_arm_simd(destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha); + return; + } + + const uint *src = (const uint *) srcPixels; + uint *dst = (uint *) destPixels; + if (w <= 64) { + for (int y=0; y<h; ++y) { + qt_memconvert(dst, src, w); + dst = (quint32 *)(((uchar *) dst) + dbpl); + src = (const quint32 *)(((const uchar *) src) + sbpl); + } + } else { + int len = w * 4; + for (int y=0; y<h; ++y) { + memcpy(dst, src, len); + dst = (quint32 *)(((uchar *) dst) + dbpl); + src = (const quint32 *)(((const uchar *) src) + sbpl); + } + } +} + +#endif + +#endif // QT_HAVE_ARMV_SIMD diff --git a/src/gui/painting/qdrawhelper_armv6_p.h b/src/gui/painting/qdrawhelper_arm_simd_p.h index e58f8bb..6c96a84 100644 --- a/src/gui/painting/qdrawhelper_armv6_p.h +++ b/src/gui/painting/qdrawhelper_arm_simd_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef QDRAWHELPER_ARMV6_P_H -#define QDRAWHELPER_ARMV6_P_H +#ifndef QDRAWHELPER_ARM_SIMD_P_H +#define QDRAWHELPER_ARM_SIMD_P_H // // W A R N I N G @@ -57,25 +57,20 @@ QT_BEGIN_NAMESPACE -#if defined(Q_CC_RVCT) && defined(QT_HAVE_ARMV6) +#if defined(QT_HAVE_ARM_SIMD) -extern "C" void qt_blend_rgb32_on_rgb32_armv6(uchar *destPixels, int dbpl, +void qt_blend_argb32_on_argb32_arm_simd(uchar *destPixels, int dbpl, const uchar *srcPixels, int sbpl, int w, int h, int const_alpha); -extern "C" void qt_blend_argb32_on_argb32_armv6(uchar *destPixels, int dbpl, +void qt_blend_rgb32_on_rgb32_arm_simd(uchar *destPixels, int dbpl, const uchar *srcPixels, int sbpl, int w, int h, int const_alpha); -extern "C" void qt_memfill32_armv6(quint32 *dest, quint32 value, int count); - -extern "C" void comp_func_Source_armv6(uint *dest, const uint *src, int length, uint const_alpha); -extern "C" void comp_func_SourceOver_armv6(uint *dest, const uint *src, int length, uint const_alpha); - -#endif // QT_HAVE_ARMV6 +#endif // QT_HAVE_ARM_SIMD QT_END_NAMESPACE -#endif // QDRAWHELPER_ARMV6_P_H +#endif // QDRAWHELPER_ARM_SIMD_P_H diff --git a/src/gui/painting/qdrawhelper_armv6_rvct.inc b/src/gui/painting/qdrawhelper_armv6_rvct.inc deleted file mode 100644 index 8c6d803..0000000 --- a/src/gui/painting/qdrawhelper_armv6_rvct.inc +++ /dev/null @@ -1,496 +0,0 @@ -;**************************************************************************** -;** -;** 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$ -;** -;**************************************************************************** - -; -; 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. -; - -;----------------------------------------------------------------------------- -; Globals. -; Earch marcro expects that caller has loaded 0x800080 to r14. -;----------------------------------------------------------------------------- - -ComponentHalf EQU 0x800080 - -;----------------------------------------------------------------------------- -; ARM assembly implementations of accelerated graphics operations. -; -; Conventions: -; -; - r0 = Target buffer pointer -; - r1 = Source buffer pointer -; - r2 = Length of the buffer to blend -; - r3 = Constant alpha for source buffer -; -;----------------------------------------------------------------------------- - -; A macro for transparently defining ARM functions - MACRO -$func Function - AREA Function_$func, CODE - GLOBAL $func - ALIGN 4 - CODE32 -$func - MEND - - -;----------------------------------------------------------------------------- -; Armv6 boosted implementation of BYTE_MUL(...) function found in qdrawhelper_p.h. -; -; @param dst Destination register where to store the result -; @param x Value to multiply -; @param a Multiplicator byte -; @param r14 Component half 0x800080 -; -; @note Trashes x, r8 -;----------------------------------------------------------------------------- - MACRO - ByteMul $dst, $x, $a - - ; static inline uint BYTE_MUL(uint x, uint a) - - ; uint r8 = (x & 0xff00ff) * a + 0x800080 - uxtb16 r8, $x ; r8 = r8 & 0x00FF00FF - mla r8, r8, $a, r14 - - ; x = ((r >> 8) & 0xff00ff) * a + 0x800080 - uxtb16 $x, $x, ror #8 - mla $x, $x, $a, r14 - - - ; r8 = (r8 + ((r8 >> 8) & 0xff00ff) ) >> 8 - ; r8 &= 0xff00ff - uxtab16 r8, r8, r8, ror #8 - uxtb16 r8, r8, ror #8 - - ; x = x + ((x >>8) & 0xff00ff) - uxtab16 $x, $x, $x, ror #8 - - ; x &= 0xff00ff00 - ; x |= r8 - uxtb16 $x, $x, ror #8 - orr $dst, r8, $x, lsl #8 - - MEND - -;----------------------------------------------------------------------------- -; Armv6 boosted implementation of INTERPOLATE_PIXEL_255(...) function found in -; qdrawhelper_p.h. -; -; @param dst Destination register where to store the result -; @param x First value to multiply -; @param a Multiplicator byte for first value -; @param y Second value to multiply -; @param b Multiplicator byte for second value -; @param r14 Component half 0x800080 -; -; -; @note Trashes x, r8, r14 -;----------------------------------------------------------------------------- - MACRO - InterpolatePixel255 $dst, $x, $a, $y, $b - - ; static inline uint INTERPOLATE_PIXEL_255(uint x, uint a, uint y, uint b) - - ; First calculate the parts where we need 0x800080 - - ; uint r8 = (((x & 0xff00ff) * a) + 0x800080) - uxtb16 r8, $x ; r8 = r8 & 0x00FF00FF - mla r8, r8, $a, r14 - - ; x = ((((x >> 8) & 0xff00ff) * a) + 0x800080) - uxtb16 $x, $x, ror #8 - mla $x, $x, $a, r14 - - ; Now we are trashing r14 to free it for other purposes - - ; uint r14 = (y & 0xff00ff) * b - uxtb16 r14, $y ; r14 = y & 0x00FF00FF - mul r14, r14, $b - - ; r8 = r8 + r14 - add r8, r8, r14 - - ; r8 = (r8 + ((r8 >> 8) & 0xff00ff) ) >> 8 - ; r8 &= 0xff00ff - uxtab16 r8, r8, r8, ror #8 - uxtb16 r8, r8, ror #8 - - ; r14 = ((y >> 8) & 0xff00ff) * b - uxtb16 r14, $y, ror #8 ; r14 = ((y >> 8) & 0xFF00FF) - mul r14, r14, $b - - ; x = x + r14 - add $x, $x, r14 - - ; x = x + ((x >>8) & 0xff00ff) - uxtab16 $x, $x, $x, ror #8 - - ; x &= 0xff00ff00 - ; x |= r8 - uxtb16 $x, $x, ror #8 - orr $dst, r8, $x, lsl #8 - - MEND - -;----------------------------------------------------------------------------- -; -;----------------------------------------------------------------------------- - MACRO -$label Blend4Pixels $BlendPixel - - ; Blend first 4 pixels - - ldmia r1!, {r4-r7} - ldm r0, {r9-r12} - -b4p1_$label $BlendPixel r9, r4, r3 -b4p2_$label $BlendPixel r10, r5, r3 -b4p3_$label $BlendPixel r11, r6, r3 -b4p4_$label $BlendPixel r12, r7, r3 - - stmia r0!, {r9-r12} - - MEND - -;----------------------------------------------------------------------------- -; -;----------------------------------------------------------------------------- - MACRO -$label Blend8Pixels $BlendPixel - -b8p1_$label Blend4Pixels $BlendPixel -b8p2_$label Blend4Pixels $BlendPixel - - MEND - -;----------------------------------------------------------------------------- -; -;----------------------------------------------------------------------------- - MACRO -$label Blend16Pixels $BlendPixel - -b16p1_$label Blend8Pixels $BlendPixel -b16p2_$label Blend8Pixels $BlendPixel - - MEND - -;----------------------------------------------------------------------------- -; -;----------------------------------------------------------------------------- - MACRO -$label Blend32Pixels $BlendPixel - -b32p1_$label Blend16Pixels $BlendPixel -b32p2_$label Blend16Pixels $BlendPixel - - MEND - -;----------------------------------------------------------------------------- -; A macro for source over compositing one row of pixels and saving the results -; to destination buffer. -; -; @param dest Destination buffer (r0) -; @param src Source buffer (r1) -; @param length Length (r2) -; @param const_alpha Constant alpha (r3) -; @param r14 Component Half (0x800080) (r14) -; -; @note Advances r0, r1 -; @note Trashes r2, r4-r12 -;----------------------------------------------------------------------------- - MACRO -$label BlendRow $BlendPixel - - pld [r1] - -bloop_$label - ; Blend 32 pixels per loop iteration - subs r2, r2, #32 - bmi b_remaining_$label - -brp1_$label Blend32Pixels $BlendPixel - - b bloop_$label - -b_remaining_$label - - ; Remaining 31 pixels - - addmi r2, r2, #32 - - ; Blend 16 pixels - tst r2, #16 - beq b_remaining8_$label - -brp2_$label Blend16Pixels $BlendPixel - -b_remaining8_$label - - ; Blend 8 pixels - tst r2, #8 - beq b_remaining4_$label - -brp3_$label Blend8Pixels $BlendPixel - -b_remaining4_$label - - ; Blend 4 pixels - tst r2, #4 - beq b_remaining3_$label - -brp4_$label Blend4Pixels $BlendPixel - -b_remaining3_$label - - ; Remaining 3 pixels - - tst r2, #2 - beq b_last_$label - - ldmia r1!, {r4-r5} - ldm r0, {r9-r10} - -brp5_$label $BlendPixel r9, r4, r3 -brp6_$label $BlendPixel r10, r5, r3 - - stmia r0!, {r9-r10} - -b_last_$label - - tst r2, #1 - beq bexit_$label - - ldr r4, [r1] - ldr r9, [r0] - -bpl_$label $BlendPixel r9, r4, r3 - - str r9, [r0] - -bexit_$label - - MEND - -;----------------------------------------------------------------------------- -; A macro for source over compositing one row of pixels and saving the results -; to destination buffer. Restores all registers. -; -; @param dest Destination buffer (r0) -; @param src Source buffer (r1) -; @param length Length (r2) -; @param const_alpha Constant alpha (r3) -; @param r14 Component Half (0x800080) (r14) -; -; @note Advances r0, r1 -; @note Trashes r2, r4-r12 -;----------------------------------------------------------------------------- - MACRO -$label BlendRowSafe $BlendPixel - - stmfd sp!, {r0-r6} ; Preserves registers only up to r6 - -brs_$label BlendRow $BlendPixel - - ldmfd sp!, {r0-r6} - - MEND - - -;----------------------------------------------------------------------------- -; Pix Copy. -; NOTE! Cache line size of ARM1136JF-S and ARM1136J-S is 32 bytes (8 pixels). -; -; @param dst Destination pixels (r0) -; @param src Source pixels (r1) -; @param len Length (r2) -; -; @note Trashes r3-r10 -;----------------------------------------------------------------------------- - MACRO -$label PixCpy $dst, $src, $len - - pld [$src] - -pcpy_loop_$label - ; Copy 8 pixels per loop iteration - pld [$src, #96] - subs $len, $len, #8 - ldmgeia $src!, {r3-r10} - stmgeia $dst!, {r3-r10} - bgt pcpy_loop_$label - -pcpy_remaining_$label - - ; Copy up to 7 remaining pixels - - ; Copy 4 pixels - tst $len, #4 - ldmneia $src!, {r3-r6} - stmneia $dst!, {r3-r6} - - tst $len, #2 - ldmneia $src!, {r3-r4} - stmneia $dst!, {r3-r4} - - tst $len, #1 - ldrne r3, [$src] - strne r3, [$dst] - - MEND - -;----------------------------------------------------------------------------- -; General Pix Copy. Maximum 8 pixels at time. Restores all registers. -; -; @param dst Destination pixels (r0) -; @param src Source pixels (r1) -; @param len Length (r2) -; -; @note Trashes r3-r10 -;----------------------------------------------------------------------------- - MACRO -$label PixCpySafe $dst, $src, $len - - stmfd sp!, {r0-r6} ; Preserves registers only up to r6 - -pcs_$label PixCpy $dst, $src, $len - - ldmfd sp!, {r0-r6} ; pop - - MEND - - -;----------------------------------------------------------------------------- -; A macro for source over compositing one pixel and saving the result to -; dst register. -; -; @param dst Destination register, must contain destination pixel upon entry -; @param src Source register, must contain source pixel upon entry -; @param const_alpha Constant source alpha -; @param r14 Component half 0x800080 -; -; @note Trashes const_alpha, r8 -;----------------------------------------------------------------------------- - MACRO -$label PixelSourceOver $dst, $src, $const_alpha - - ; Negate src and extract alpha - mvn $const_alpha, $src ; bitwise not - uxtb $const_alpha, $const_alpha, ror #24 ; r3 = ((r3 & 0xFF000000) >> 24); - - ;cmp $const_alpha, #255 ; test for full transparency ( negated ) - ;beq exit_$label - cmp $const_alpha, #0 ; test for full opacity ( negated ) - moveq $dst, $src - beq exit_$label - - ByteMul $dst, $dst, $const_alpha - add $dst, $src, $dst - -exit_$label - MEND - -;----------------------------------------------------------------------------- -; A macro for source over compositing one pixel and saving the result to -; dst register. -; -; @param dst Destination register, must contain destination pixel upon entry -; @param src Source register, must contain source pixel upon entry -; @param const_alpha Constant source alpha -; @param r14 Component half 0x800080 -; -; @note Trashes src, const_alpha, r8 -;----------------------------------------------------------------------------- - MACRO -$label PixelSourceOverConstAlpha $dst, $src, $const_alpha - - ; store alpha because we are going to trash it - stmfd sp!, {$const_alpha} - - ByteMul $src, $src, $const_alpha - - ; Negate src and extract alpha - mvn $const_alpha, $src ; bitwise not - uxtb $const_alpha, $const_alpha, ror #24 ; r3 = ((r3 & 0xFF000000) >> 24); - - ByteMul $dst, $dst, $const_alpha - - add $dst, $src, $dst - - ; recover alpha - ldmfd sp!, {$const_alpha} - - MEND - -;----------------------------------------------------------------------------- -; A macro for source over compositing one pixel and saving the result to -; a register. -; -; @param dst Destination register, must contain destination pixel upon entry -; @param src Source register, must contain source pixel upon entry -; @param const_alpha Constant source alpha -; @param r14 Component half 0x800080 -; -; @note Trashes src, r8 -;----------------------------------------------------------------------------- - MACRO -$label PixelSourceConstAlpha $dst, $src, $const_alpha - - ; store r2 and r14 because we are going to trash them - stmfd sp!, {r2, r14} - - rsb r2, $const_alpha, #255 - InterpolatePixel255 $dst, $src, $const_alpha, $dst, r2 - - ; recover r2 and r14 - ldmfd sp!, {r2, r14} - - MEND - - END ; File end diff --git a/src/gui/painting/qdrawhelper_armv6_rvct.s b/src/gui/painting/qdrawhelper_armv6_rvct.s deleted file mode 100644 index 180980a..0000000 --- a/src/gui/painting/qdrawhelper_armv6_rvct.s +++ /dev/null @@ -1,177 +0,0 @@ -;**************************************************************************** -;** -;** 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$ -;** -;**************************************************************************** - -; -; 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. -; - - ARM - PRESERVE8 - - INCLUDE qdrawhelper_armv6_rvct.inc - -;----------------------------------------------------------------------------- -; qt_memfill32_armv6 -; -; @brief Not yet in use! -; -; @param dest Destination buffer (r0) -; @param value Value (r1) -; @param count Count (r2) -; -;--------------------------------------------------------------------------- -qt_memfill32_armv6 Function - stmfd sp!, {r4-r12, r14} - - mov r3, r1 - mov r4, r1 - mov r5, r1 - mov r6, r1 - mov r7, r1 - mov r8, r1 - mov r9, r1 - -mfill_loop - ; Fill 32 pixels per loop iteration - subs r2, r2, #32 - stmgeia r0!, {r1, r3, r4, r5, r6, r7, r8, r9} - stmgeia r0!, {r1, r3, r4, r5, r6, r7, r8, r9} - stmgeia r0!, {r1, r3, r4, r5, r6, r7, r8, r9} - stmgeia r0!, {r1, r3, r4, r5, r6, r7, r8, r9} - bgt mfill_loop - -mfill_remaining - - ; Fill up to 31 remaining pixels - - ; Fill 16 pixels - tst r2, #16 - stmneia r0!, {r1, r3, r4, r5, r6, r7, r8, r9} - stmneia r0!, {r1, r3, r4, r5, r6, r7, r8, r9} - - ; Fill 8 pixels - tst r2, #8 - stmneia r0!, {r1, r3, r4, r5, r6, r7, r8, r9} - - ; Fill 4 pixels - tst r2, #4 - stmneia r0!, {r1, r3, r4, r5} - - ; Fill 2 pixels - tst r2, #2 - stmneia r0!, {r1, r3} - - ; Fill last one - tst r2, #1 - strne r1, [r0] - - ldmfd sp!, {r4-r12, pc} ; pop and return - -;----------------------------------------------------------------------------- -; comp_func_Source_arm -; -; @brief -; -; @param dest Destination buffer (r0) -; @param src Source buffer (r1) -; @param length Length (r2) -; @param const_alpha Constant alpha (r3) -; -;--------------------------------------------------------------------------- -comp_func_Source_armv6 Function - stmfd sp!, {r4-r12, r14} - - cmp r3, #255 ; if(r3 == 255) - bne src2 ; branch if not - -src1 PixCpy r0, r1, r2 - - ldmfd sp!, {r4-r12, pc} ; pop and return - -src2 - ;ldr r14, =ComponentHalf ; load 0x800080 to r14 - mov r14, #0x800000 - add r14, r14, #0x80 - -src22 BlendRow PixelSourceConstAlpha - - ldmfd sp!, {r4-r12, pc} ; pop and return - -;----------------------------------------------------------------------------- -; comp_func_SourceOver_arm -; -; @brief -; -; @param dest Destination buffer (r0) -; @param src Source buffer (r1) -; @param length Length (r2) -; @param const_alpha Constant alpha (r3) -; -;--------------------------------------------------------------------------- -comp_func_SourceOver_armv6 Function - stmfd sp!, {r4-r12, r14} - - ;ldr r14, =ComponentHalf ; load 0x800080 to r14 - mov r14, #0x800000 - add r14, r14, #0x80 - - cmp r3, #255 ; if(r3 == 255) - bne srcovr2 ; branch if not - -srcovr1 BlendRow PixelSourceOver - - ldmfd sp!, {r4-r12, pc} ; pop and return - -srcovr2 - -srcovr22 BlendRow PixelSourceOverConstAlpha - - ldmfd sp!, {r4-r12, pc} ; pop and return - - - END ; File end diff --git a/src/gui/painting/qdrawhelper_iwmmxt.cpp b/src/gui/painting/qdrawhelper_iwmmxt.cpp index 80b2c04..d99045d 100644 --- a/src/gui/painting/qdrawhelper_iwmmxt.cpp +++ b/src/gui/painting/qdrawhelper_iwmmxt.cpp @@ -106,14 +106,35 @@ CompositionFunction qt_functionForMode_IWMMXT[] = { comp_func_DestinationOver<QIWMMXTIntrinsics>, comp_func_Clear<QIWMMXTIntrinsics>, comp_func_Source<QIWMMXTIntrinsics>, - 0, + comp_func_Destination, comp_func_SourceIn<QIWMMXTIntrinsics>, comp_func_DestinationIn<QIWMMXTIntrinsics>, comp_func_SourceOut<QIWMMXTIntrinsics>, comp_func_DestinationOut<QIWMMXTIntrinsics>, comp_func_SourceAtop<QIWMMXTIntrinsics>, comp_func_DestinationAtop<QIWMMXTIntrinsics>, - comp_func_XOR<QIWMMXTIntrinsics> + comp_func_XOR<QIWMMXTIntrinsics>, + comp_func_Plus, + comp_func_Multiply, + comp_func_Screen, + comp_func_Overlay, + comp_func_Darken, + comp_func_Lighten, + comp_func_ColorDodge, + comp_func_ColorBurn, + comp_func_HardLight, + comp_func_SoftLight, + comp_func_Difference, + comp_func_Exclusion, + rasterop_SourceOrDestination, + rasterop_SourceAndDestination, + rasterop_SourceXorDestination, + rasterop_NotSourceAndNotDestination, + rasterop_NotSourceOrNotDestination, + rasterop_NotSourceXorDestination, + rasterop_NotSource, + rasterop_NotSourceAndDestination, + rasterop_SourceAndNotDestination }; void qt_blend_color_argb_iwmmxt(int count, const QSpan *spans, void *userData) diff --git a/src/gui/painting/qdrawhelper_mmx.cpp b/src/gui/painting/qdrawhelper_mmx.cpp index d49c5a1..ba92554 100644 --- a/src/gui/painting/qdrawhelper_mmx.cpp +++ b/src/gui/painting/qdrawhelper_mmx.cpp @@ -77,7 +77,7 @@ CompositionFunction qt_functionForMode_MMX[numCompositionFunctions] = { comp_func_DestinationOver<QMMXIntrinsics>, comp_func_Clear<QMMXIntrinsics>, comp_func_Source<QMMXIntrinsics>, - 0, + comp_func_Destination, comp_func_SourceIn<QMMXIntrinsics>, comp_func_DestinationIn<QMMXIntrinsics>, comp_func_SourceOut<QMMXIntrinsics>, @@ -85,6 +85,27 @@ CompositionFunction qt_functionForMode_MMX[numCompositionFunctions] = { comp_func_SourceAtop<QMMXIntrinsics>, comp_func_DestinationAtop<QMMXIntrinsics>, comp_func_XOR<QMMXIntrinsics>, + comp_func_Plus, + comp_func_Multiply, + comp_func_Screen, + comp_func_Overlay, + comp_func_Darken, + comp_func_Lighten, + comp_func_ColorDodge, + comp_func_ColorBurn, + comp_func_HardLight, + comp_func_SoftLight, + comp_func_Difference, + comp_func_Exclusion, + rasterop_SourceOrDestination, + rasterop_SourceAndDestination, + rasterop_SourceXorDestination, + rasterop_NotSourceAndNotDestination, + rasterop_NotSourceOrNotDestination, + rasterop_NotSourceXorDestination, + rasterop_NotSource, + rasterop_NotSourceAndDestination, + rasterop_SourceAndNotDestination }; void qt_blend_color_argb_mmx(int count, const QSpan *spans, void *userData) diff --git a/src/gui/painting/qdrawhelper_mmx3dnow.cpp b/src/gui/painting/qdrawhelper_mmx3dnow.cpp index 8bffaa7..b1e81fc 100644 --- a/src/gui/painting/qdrawhelper_mmx3dnow.cpp +++ b/src/gui/painting/qdrawhelper_mmx3dnow.cpp @@ -85,14 +85,35 @@ CompositionFunction qt_functionForMode_MMX3DNOW[numCompositionFunctions] = { comp_func_DestinationOver<QMMX3DNOWIntrinsics>, comp_func_Clear<QMMX3DNOWIntrinsics>, comp_func_Source<QMMX3DNOWIntrinsics>, - 0, + comp_func_Destination, comp_func_SourceIn<QMMX3DNOWIntrinsics>, comp_func_DestinationIn<QMMX3DNOWIntrinsics>, comp_func_SourceOut<QMMX3DNOWIntrinsics>, comp_func_DestinationOut<QMMX3DNOWIntrinsics>, comp_func_SourceAtop<QMMX3DNOWIntrinsics>, comp_func_DestinationAtop<QMMX3DNOWIntrinsics>, - comp_func_XOR<QMMX3DNOWIntrinsics> + comp_func_XOR<QMMX3DNOWIntrinsics>, + comp_func_Plus, + comp_func_Multiply, + comp_func_Screen, + comp_func_Overlay, + comp_func_Darken, + comp_func_Lighten, + comp_func_ColorDodge, + comp_func_ColorBurn, + comp_func_HardLight, + comp_func_SoftLight, + comp_func_Difference, + comp_func_Exclusion, + rasterop_SourceOrDestination, + rasterop_SourceAndDestination, + rasterop_SourceXorDestination, + rasterop_NotSourceAndNotDestination, + rasterop_NotSourceOrNotDestination, + rasterop_NotSourceXorDestination, + rasterop_NotSource, + rasterop_NotSourceAndDestination, + rasterop_SourceAndNotDestination }; void qt_blend_color_argb_mmx3dnow(int count, const QSpan *spans, void *userData) diff --git a/src/gui/painting/qdrawhelper_neon.cpp b/src/gui/painting/qdrawhelper_neon.cpp index 77c5202..0afd077 100644 --- a/src/gui/painting/qdrawhelper_neon.cpp +++ b/src/gui/painting/qdrawhelper_neon.cpp @@ -40,14 +40,55 @@ ****************************************************************************/ #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 +void qt_memfill32_neon(quint32 *dest, quint32 value, int count) +{ + const int epilogueSize = count % 16; + if (count >= 16) { + quint32 *const neonEnd = dest + count - epilogueSize; + register uint32x4_t valueVector1 asm ("q0") = vdupq_n_u32(value); + register uint32x4_t valueVector2 asm ("q1") = valueVector1; + while (dest != neonEnd) { + asm volatile ( + "vst2.32 { d0, d1, d2, d3 }, [%[DST]] !\n\t" + "vst2.32 { d0, d1, d2, d3 }, [%[DST]] !\n\t" + : [DST]"+r" (dest) + : [VALUE1]"w"(valueVector1), [VALUE2]"w"(valueVector2) + : "memory" + ); + } + } + + switch (epilogueSize) + { + case 15: *dest++ = value; + case 14: *dest++ = value; + case 13: *dest++ = value; + case 12: *dest++ = value; + case 11: *dest++ = value; + case 10: *dest++ = value; + case 9: *dest++ = value; + case 8: *dest++ = value; + case 7: *dest++ = value; + case 6: *dest++ = value; + case 5: *dest++ = value; + case 4: *dest++ = value; + case 3: *dest++ = value; + case 2: *dest++ = value; + case 1: *dest++ = value; + } +} + static inline uint16x8_t qvdiv_255_u16(uint16x8_t x, uint16x8_t half) { // result = (x + (x >> 8) + 0x80) >> 8 @@ -87,60 +128,240 @@ 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); + +extern "C" void +pixman_composite_src_0565_0565_asm_neon (int32_t w, + int32_t h, + uint16_t *dst, + int32_t dst_stride, + uint16_t *src, + int32_t src_stride); + +// 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); +// qblendfunctions.cpp +void qt_blend_rgb16_on_rgb16(uchar *dst, int dbpl, + const uchar *src, int sbpl, + int w, int h, + int const_alpha); - const uint16x8_t src16_high = vmovl_u8(src8_high); - const uint16x8_t dst16_high = vmovl_u8(dst8_high); +template <int N> +static inline void scanLineBlit16(quint16 *dst, quint16 *src, int dstride) +{ + if (N >= 2) { + ((quint32 *)dst)[0] = ((quint32 *)src)[0]; + __builtin_prefetch(dst + dstride, 1, 0); + } + for (int i = 1; i < N/2; ++i) + ((quint32 *)dst)[i] = ((quint32 *)src)[i]; + if (N & 1) + dst[N-1] = src[N-1]; +} - 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); +template <int Width> +static inline void blockBlit16(quint16 *dst, quint16 *src, int dstride, int sstride, int h) +{ + union { + quintptr address; + quint16 *pointer; + } u; + + u.pointer = dst; + + if (u.address & 2) { + while (h--) { + // align dst + dst[0] = src[0]; + if (Width > 1) + scanLineBlit16<Width-1>(dst + 1, src + 1, dstride); + dst += dstride; + src += sstride; + } + } else { + while (h--) { + scanLineBlit16<Width>(dst, src, dstride); - const uint32x2_t result32_low = vreinterpret_u32_u8(vmovn_u16(result16_low)); - const uint32x2_t result32_high = vreinterpret_u32_u8(vmovn_u16(result16_high)); + dst += dstride; + src += sstride; + } + } +} - vst1q_u32((uint32_t *)&dst[x], vcombine_u32(result32_low, result32_high)); +void qt_blend_rgb16_on_rgb16_neon(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha) +{ + // testing show that the default memcpy is faster for widths 150 and up + if (const_alpha != 256 || w >= 150) { + qt_blend_rgb16_on_rgb16(destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha); + return; + } + + int dstride = dbpl / 2; + int sstride = sbpl / 2; + + quint16 *dst = (quint16 *) destPixels; + quint16 *src = (quint16 *) srcPixels; + + switch (w) { +#define BLOCKBLIT(n) case n: blockBlit16<n>(dst, src, dstride, sstride, h); return; + BLOCKBLIT(1); + BLOCKBLIT(2); + BLOCKBLIT(3); + BLOCKBLIT(4); + BLOCKBLIT(5); + BLOCKBLIT(6); + BLOCKBLIT(7); + BLOCKBLIT(8); + BLOCKBLIT(9); + BLOCKBLIT(10); + BLOCKBLIT(11); + BLOCKBLIT(12); + BLOCKBLIT(13); + BLOCKBLIT(14); + BLOCKBLIT(15); +#undef BLOCKBLIT + default: + break; + } + + pixman_composite_src_0565_0565_asm_neon (w, h, dst, dstride, src, sstride); +} + +extern "C" void blend_8_pixels_argb32_on_rgb16_neon(quint16 *dst, const quint32 *src, 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) +{ + quint16 *dst = (quint16 *) destPixels; + quint32 *src = (quint32 *) srcPixels; + + 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); + + if (i < w) { + int tail = w - i; + + 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 +475,488 @@ 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]; + } +} + +void QT_FASTCALL comp_func_solid_SourceOver_neon(uint *destPixels, int length, uint color, uint const_alpha) +{ + if ((const_alpha & qAlpha(color)) == 255) { + QT_MEMFILL_UINT(destPixels, length, color); + } else { + if (const_alpha != 255) + color = BYTE_MUL(color, const_alpha); + + const quint32 minusAlphaOfColor = qAlpha(~color); + int x = 0; + + uint32_t *dst = (uint32_t *) destPixels; + const uint32x4_t colorVector = vdupq_n_u32(color); + uint16x8_t half = vdupq_n_u16(0x80); + const uint16x8_t minusAlphaOfColorVector = vdupq_n_u16(minusAlphaOfColor); + + for (; x < length-3; x += 4) { + uint32x4_t dstVector = vld1q_u32(&dst[x]); + + const uint8x16_t dst8 = vreinterpretq_u8_u32(dstVector); + + const uint8x8_t dst8_low = vget_low_u8(dst8); + const uint8x8_t dst8_high = vget_high_u8(dst8); + + const uint16x8_t dst16_low = vmovl_u8(dst8_low); + const uint16x8_t dst16_high = vmovl_u8(dst8_high); + + const uint16x8_t result16_low = qvbyte_mul_u16(dst16_low, minusAlphaOfColorVector, half); + const uint16x8_t result16_high = qvbyte_mul_u16(dst16_high, minusAlphaOfColorVector, half); + + const uint32x2_t result32_low = vreinterpret_u32_u8(vmovn_u16(result16_low)); + const uint32x2_t result32_high = vreinterpret_u32_u8(vmovn_u16(result16_high)); + + uint32x4_t blendedPixels = vcombine_u32(result32_low, result32_high); + uint32x4_t colorPlusBlendedPixels = vaddq_u32(colorVector, blendedPixels); + vst1q_u32(&dst[x], colorPlusBlendedPixels); + } + + for (;x < length; ++x) + destPixels[x] = color + BYTE_MUL(destPixels[x], minusAlphaOfColor); + } +} + +void QT_FASTCALL comp_func_Plus_neon(uint *dst, const uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) { + uint *const end = dst + length; + uint *const neonEnd = end - 3; + + while (dst < neonEnd) { + asm volatile ( + "vld2.8 { d0, d1 }, [%[SRC]] !\n\t" + "vld2.8 { d2, d3 }, [%[DST]]\n\t" + "vqadd.u8 q0, q0, q1\n\t" + "vst2.8 { d0, d1 }, [%[DST]] !\n\t" + : [DST]"+r" (dst), [SRC]"+r" (src) + : + : "memory", "d0", "d1", "d2", "d3", "q0", "q1" + ); + } + + while (dst != end) { + *dst = comp_func_Plus_one_pixel(*dst, *src); + ++dst; + ++src; + } + } else { + int x = 0; + const int one_minus_const_alpha = 255 - const_alpha; + const uint16x8_t constAlphaVector = vdupq_n_u16(const_alpha); + const uint16x8_t oneMinusconstAlphaVector = vdupq_n_u16(one_minus_const_alpha); + + const uint16x8_t half = vdupq_n_u16(0x80); + for (; x < length - 3; x += 4) { + const uint32x4_t src32 = vld1q_u32((uint32_t *)&src[x]); + const uint8x16_t src8 = vreinterpretq_u8_u32(src32); + uint8x16_t dst8 = vld1q_u8((uint8_t *)&dst[x]); + uint8x16_t result = vqaddq_u8(dst8, src8); + + uint16x8_t result_low = vmovl_u8(vget_low_u8(result)); + uint16x8_t result_high = vmovl_u8(vget_high_u8(result)); + + uint16x8_t dst_low = vmovl_u8(vget_low_u8(dst8)); + uint16x8_t dst_high = vmovl_u8(vget_high_u8(dst8)); + + result_low = qvinterpolate_pixel_255(result_low, constAlphaVector, dst_low, oneMinusconstAlphaVector, half); + result_high = qvinterpolate_pixel_255(result_high, constAlphaVector, dst_high, oneMinusconstAlphaVector, half); + + const uint32x2_t result32_low = vreinterpret_u32_u8(vmovn_u16(result_low)); + const uint32x2_t result32_high = vreinterpret_u32_u8(vmovn_u16(result_high)); + vst1q_u32((uint32_t *)&dst[x], vcombine_u32(result32_low, result32_high)); + } + + for (; x < length; ++x) + dst[x] = comp_func_Plus_one_pixel_const_alpha(dst[x], src[x], const_alpha, one_minus_const_alpha); + } +} + +static const int tileSize = 32; + +extern "C" void qt_rotate90_16_neon(quint16 *dst, const quint16 *src, int sstride, int dstride, int count); + +void qt_memrotate90_16_neon(const uchar *srcPixels, int w, int h, int sstride, uchar *destPixels, int dstride) +{ + const ushort *src = (const ushort *)srcPixels; + ushort *dest = (ushort *)destPixels; + + sstride /= sizeof(ushort); + dstride /= sizeof(ushort); + + const int pack = sizeof(quint32) / sizeof(ushort); + const int unaligned = + qMin(uint((quintptr(dest) & (sizeof(quint32)-1)) / sizeof(ushort)), uint(h)); + const int restX = w % tileSize; + const int restY = (h - unaligned) % tileSize; + const int unoptimizedY = restY % pack; + const int numTilesX = w / tileSize + (restX > 0); + const int numTilesY = (h - unaligned) / tileSize + (restY >= pack); + + for (int tx = 0; tx < numTilesX; ++tx) { + const int startx = w - tx * tileSize - 1; + const int stopx = qMax(startx - tileSize, 0); + + if (unaligned) { + for (int x = startx; x >= stopx; --x) { + ushort *d = dest + (w - x - 1) * dstride; + for (int y = 0; y < unaligned; ++y) { + *d++ = src[y * sstride + x]; + } + } + } + + for (int ty = 0; ty < numTilesY; ++ty) { + const int starty = ty * tileSize + unaligned; + const int stopy = qMin(starty + tileSize, h - unoptimizedY); + + int x = startx; + // qt_rotate90_16_neon writes to eight rows, four pixels at a time + for (; x >= stopx + 7; x -= 8) { + ushort *d = dest + (w - x - 1) * dstride + starty; + const ushort *s = &src[starty * sstride + x - 7]; + qt_rotate90_16_neon(d, s, sstride * 2, dstride * 2, stopy - starty); + } + + for (; x >= stopx; --x) { + quint32 *d = reinterpret_cast<quint32*>(dest + (w - x - 1) * dstride + starty); + for (int y = starty; y < stopy; y += pack) { + quint32 c = src[y * sstride + x]; + for (int i = 1; i < pack; ++i) { + const int shift = (sizeof(int) * 8 / pack * i); + const ushort color = src[(y + i) * sstride + x]; + c |= color << shift; + } + *d++ = c; + } + } + } + + if (unoptimizedY) { + const int starty = h - unoptimizedY; + for (int x = startx; x >= stopx; --x) { + ushort *d = dest + (w - x - 1) * dstride + starty; + for (int y = starty; y < h; ++y) { + *d++ = src[y * sstride + x]; + } + } + } + } +} + +extern "C" void qt_rotate270_16_neon(quint16 *dst, const quint16 *src, int sstride, int dstride, int count); + +void qt_memrotate270_16_neon(const uchar *srcPixels, int w, int h, + int sstride, + uchar *destPixels, int dstride) +{ + const ushort *src = (const ushort *)srcPixels; + ushort *dest = (ushort *)destPixels; + + sstride /= sizeof(ushort); + dstride /= sizeof(ushort); + + const int pack = sizeof(quint32) / sizeof(ushort); + const int unaligned = + qMin(uint((long(dest) & (sizeof(quint32)-1)) / sizeof(ushort)), uint(h)); + const int restX = w % tileSize; + const int restY = (h - unaligned) % tileSize; + const int unoptimizedY = restY % pack; + const int numTilesX = w / tileSize + (restX > 0); + const int numTilesY = (h - unaligned) / tileSize + (restY >= pack); + + for (int tx = 0; tx < numTilesX; ++tx) { + const int startx = tx * tileSize; + const int stopx = qMin(startx + tileSize, w); + + if (unaligned) { + for (int x = startx; x < stopx; ++x) { + ushort *d = dest + x * dstride; + for (int y = h - 1; y >= h - unaligned; --y) { + *d++ = src[y * sstride + x]; + } + } + } + + for (int ty = 0; ty < numTilesY; ++ty) { + const int starty = h - 1 - unaligned - ty * tileSize; + const int stopy = qMax(starty - tileSize, unoptimizedY); + + int x = startx; + // qt_rotate90_16_neon writes to eight rows, four pixels at a time + for (; x < stopx - 7; x += 8) { + ushort *d = dest + x * dstride + h - 1 - starty; + const ushort *s = &src[starty * sstride + x]; + qt_rotate90_16_neon(d + 7 * dstride, s, -sstride * 2, -dstride * 2, starty - stopy); + } + + for (; x < stopx; ++x) { + quint32 *d = reinterpret_cast<quint32*>(dest + x * dstride + + h - 1 - starty); + for (int y = starty; y > stopy; y -= pack) { + quint32 c = src[y * sstride + x]; + for (int i = 1; i < pack; ++i) { + const int shift = (sizeof(int) * 8 / pack * i); + const ushort color = src[(y - i) * sstride + x]; + c |= color << shift; + } + *d++ = c; + } + } + } + if (unoptimizedY) { + const int starty = unoptimizedY - 1; + for (int x = startx; x < stopx; ++x) { + ushort *d = dest + x * dstride + h - 1 - starty; + for (int y = starty; y >= 0; --y) { + *d++ = src[y * sstride + x]; + } + } + } + } +} + 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..d9cdc36 --- /dev/null +++ b/src/gui/painting/qdrawhelper_neon_asm.S @@ -0,0 +1,297 @@ +/**************************************************************************** +** +** 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 + +/* void qt_rotate90_16_neon(quint16 *dst, const quint16 *src, int sstride, int dstride, int count) */ + .func qt_rotate90_16_neon + .global qt_rotate90_16_neon + /* For ELF format also set function visibility to hidden */ +#ifdef __ELF__ + .hidden qt_rotate90_16_neon + .type qt_rotate90_16_neon, %function +#endif +qt_rotate90_16_neon: + push { r4-r11, lr } + ldr r5, [sp, #(9*4)] + + /* The preloads are the key to getting good performance */ + pld [r1] + + mov r4, r5, asr #2 + add r6, r0, r3 + add r7, r6, r3 + + add r8, r7, r3 + add r9, r8, r3 + + pld [r1, r2] + + add r10, r9, r3 + add r11, r10, r3 + + add r3, r3, r11 + and r5, r5, #3 + + pld [r1, r2, lsl #1] + + cmp r4, #0 + beq .rotate90_16_tail + +.rotate90_16_loop: + vld1.16 { q8 }, [r1], r2 + + pld [r1, r2, lsl #1] + + vld1.16 { q9 }, [r1], r2 + vld1.16 { q10 }, [r1], r2 + vld1.16 { q11 }, [r1], r2 + + pld [r1] + + /* Could have used four quad-word zips instead, + but those take three cycles as opposed to one. */ + vzip.16 d16, d20 + vzip.16 d17, d21 + + vzip.16 d18, d22 + + pld [r1, r2] + + vzip.16 d19, d23 + + vzip.16 d16, d18 + vzip.16 d17, d19 + + pld [r1, r2, lsl #1] + + vzip.16 d20, d22 + vzip.16 d21, d23 + + vst1.16 { d23 }, [r0]! + vst1.16 { d21 }, [r6]! + vst1.16 { d19 }, [r7]! + vst1.16 { d17 }, [r8]! + vst1.16 { d22 }, [r9]! + vst1.16 { d20 }, [r10]! + vst1.16 { d18 }, [r11]! + vst1.16 { d16 }, [r3]! + + sub r4, r4, #1 + cmp r4, #0 + bne .rotate90_16_loop + b .rotate90_16_tail + +.rotate90_16_tail_loop: + sub r5, r5, #2 + + vld1.16 { q8 }, [r1], r2 + vld1.16 { q9 }, [r1], r2 + + vzip.16 d16, d18 + vzip.16 d17, d19 + + vst1.32 { d19[1] }, [r0]! + vst1.32 { d19[0] }, [r6]! + vst1.32 { d17[1] }, [r7]! + vst1.32 { d17[0] }, [r8]! + vst1.32 { d18[1] }, [r9]! + vst1.32 { d18[0] }, [r10]! + vst1.32 { d16[1] }, [r11]! + vst1.32 { d16[0] }, [r3]! + +.rotate90_16_tail: + cmp r5, #0 + bgt .rotate90_16_tail_loop + + pop { r4-r11, pc } + + .endfunc diff --git a/src/gui/painting/qdrawhelper_neon_p.h b/src/gui/painting/qdrawhelper_neon_p.h index 1994441..d25b7ec 100644 --- a/src/gui/painting/qdrawhelper_neon_p.h +++ b/src/gui/painting/qdrawhelper_neon_p.h @@ -69,6 +69,76 @@ 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_blend_rgb16_on_rgb16_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); + +void qt_memfill32_neon(quint32 *dest, quint32 value, int count); +void qt_memrotate90_16_neon(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl); +void qt_memrotate270_16_neon(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl); + +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); + +void QT_FASTCALL comp_func_solid_SourceOver_neon(uint *destPixels, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_Plus_neon(uint *dst, const uint *src, int length, uint const_alpha); + #endif // QT_HAVE_NEON QT_END_NAMESPACE diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index cb0db4f..0cc2e40 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -62,20 +62,12 @@ #define QT_FT_END_HEADER #endif #include "private/qrasterdefs_p.h" +#include <private/qsimd_p.h> #ifdef Q_WS_QWS #include "QtGui/qscreen_qws.h" #endif -// Disable MMX and SSE on Mac/PPC builds, or if the compiler -// does not support -Xarch argument passing -#if defined(QT_NO_MAC_XARCH) || (defined(Q_OS_DARWIN) && (defined(__ppc__) || defined(__ppc64__))) -#undef QT_HAVE_SSE2 -#undef QT_HAVE_SSE -#undef QT_HAVE_3DNOW -#undef QT_HAVE_MMX -#endif - QT_BEGIN_NAMESPACE #if defined(Q_CC_MSVC) && _MSCVER <= 1300 && !defined(Q_CC_INTEL) @@ -93,6 +85,11 @@ QT_BEGIN_NAMESPACE # define Q_STATIC_INLINE_FUNCTION static inline #endif +static const uint AMASK = 0xff000000; +static const uint RMASK = 0x00ff0000; +static const uint GMASK = 0x0000ff00; +static const uint BMASK = 0x000000ff; + /******************************************************************************* * QSpan * @@ -154,6 +151,7 @@ typedef void (*SrcOverTransformFunc)(uchar *destPixels, int dbpl, const QTransform &targetRectTransform, int const_alpha); +typedef void (*MemRotateFunc)(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl); struct DrawHelper { ProcessSpans blendColor; @@ -167,6 +165,7 @@ struct DrawHelper { extern SrcOverBlendFunc qBlendFunctions[QImage::NImageFormats][QImage::NImageFormats]; extern SrcOverScaleFunc qScaleFunctions[QImage::NImageFormats][QImage::NImageFormats]; extern SrcOverTransformFunc qTransformFunctions[QImage::NImageFormats][QImage::NImageFormats]; +extern MemRotateFunc qMemRotateFunctions[QImage::NImageFormats][3]; extern DrawHelper qDrawHelper[QImage::NImageFormats]; @@ -309,18 +308,61 @@ struct QSpanData void adjustSpanMethods(); }; +#if defined(Q_CC_RVCT) +# pragma push +# pragma arm +#endif +Q_STATIC_INLINE_FUNCTION uint INTERPOLATE_PIXEL_255(uint x, uint a, uint y, uint b) { + uint t = (x & 0xff00ff) * a + (y & 0xff00ff) * b; + t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8; + t &= 0xff00ff; -Q_STATIC_INLINE_FUNCTION uint BYTE_MUL_RGB16(uint x, uint a) { - a += 1; - uint t = (((x & 0x07e0)*a) >> 8) & 0x07e0; - t |= (((x & 0xf81f)*(a>>2)) >> 6) & 0xf81f; - return t; + x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b; + x = (x + ((x >> 8) & 0xff00ff) + 0x800080); + x &= 0xff00ff00; + x |= t; + return x; } +#if defined(Q_CC_RVCT) +# pragma pop +#endif -Q_STATIC_INLINE_FUNCTION uint BYTE_MUL_RGB16_32(uint x, uint a) { - uint t = (((x & 0xf81f07e0) >> 5)*a) & 0xf81f07e0; - t |= (((x & 0x07e0f81f)*a) >> 5) & 0x07e0f81f; - return t; +#if QT_POINTER_SIZE == 8 // 64-bit versions + +Q_STATIC_INLINE_FUNCTION uint INTERPOLATE_PIXEL_256(uint x, uint a, uint y, uint b) { + quint64 t = (((quint64(x)) | ((quint64(x)) << 24)) & 0x00ff00ff00ff00ff) * a; + t += (((quint64(y)) | ((quint64(y)) << 24)) & 0x00ff00ff00ff00ff) * b; + t >>= 8; + t &= 0x00ff00ff00ff00ff; + return (uint(t)) | (uint(t >> 24)); +} + +Q_STATIC_INLINE_FUNCTION uint BYTE_MUL(uint x, uint a) { + quint64 t = (((quint64(x)) | ((quint64(x)) << 24)) & 0x00ff00ff00ff00ff) * a; + t = (t + ((t >> 8) & 0xff00ff00ff00ff) + 0x80008000800080) >> 8; + t &= 0x00ff00ff00ff00ff; + return (uint(t)) | (uint(t >> 24)); +} + +Q_STATIC_INLINE_FUNCTION uint PREMUL(uint x) { + uint a = x >> 24; + quint64 t = (((quint64(x)) | ((quint64(x)) << 24)) & 0x00ff00ff00ff00ff) * a; + t = (t + ((t >> 8) & 0xff00ff00ff00ff) + 0x80008000800080) >> 8; + t &= 0x000000ff00ff00ff; + return (uint(t)) | (uint(t >> 24)) | (a << 24); +} + +#else // 32-bit versions + +Q_STATIC_INLINE_FUNCTION uint INTERPOLATE_PIXEL_256(uint x, uint a, uint y, uint b) { + uint t = (x & 0xff00ff) * a + (y & 0xff00ff) * b; + t >>= 8; + t &= 0xff00ff; + + x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b; + x &= 0xff00ff00; + x |= t; + return x; } #if defined(Q_CC_RVCT) @@ -354,6 +396,21 @@ Q_STATIC_INLINE_FUNCTION uint PREMUL(uint x) { x |= t | (a << 24); return x; } +#endif + + +Q_STATIC_INLINE_FUNCTION uint BYTE_MUL_RGB16(uint x, uint a) { + a += 1; + uint t = (((x & 0x07e0)*a) >> 8) & 0x07e0; + t |= (((x & 0xf81f)*(a>>2)) >> 6) & 0xf81f; + return t; +} + +Q_STATIC_INLINE_FUNCTION uint BYTE_MUL_RGB16_32(uint x, uint a) { + uint t = (((x & 0xf81f07e0) >> 5)*a) & 0xf81f07e0; + t |= (((x & 0x07e0f81f)*a) >> 5) & 0x07e0f81f; + return t; +} #define INV_PREMUL(p) \ (qAlpha(p) == 0 ? 0 : \ @@ -1649,7 +1706,7 @@ inline void qt_memconvert(qrgb666 *dest, const quint32 *src, int count) return; } - const int align = (long(dest) & 3); + const int align = (quintptr(dest) & 3); switch (align) { case 1: *dest++ = qrgb666(*src++); --count; case 2: *dest++ = qrgb666(*src++); --count; @@ -1842,70 +1899,6 @@ inline int qBlue565(quint16 rgb) { return (b << 3) | (b >> 2); } -#if 1 -Q_STATIC_INLINE_FUNCTION uint INTERPOLATE_PIXEL_256(uint x, uint a, uint y, uint b) { - uint t = (x & 0xff00ff) * a + (y & 0xff00ff) * b; - t >>= 8; - t &= 0xff00ff; - - x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b; - x &= 0xff00ff00; - x |= t; - return x; -} - -#if defined(Q_CC_RVCT) -# pragma push -# pragma arm -#endif -Q_STATIC_INLINE_FUNCTION uint INTERPOLATE_PIXEL_255(uint x, uint a, uint y, uint b) { - uint t = (x & 0xff00ff) * a + (y & 0xff00ff) * b; - t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8; - t &= 0xff00ff; - - x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b; - x = (x + ((x >> 8) & 0xff00ff) + 0x800080); - x &= 0xff00ff00; - x |= t; - return x; -} -#if defined(Q_CC_RVCT) -# pragma pop -#endif -#else -// possible implementation for 64 bit -Q_STATIC_INLINE_FUNCTION uint INTERPOLATE_PIXEL_256(uint x, uint a, uint y, uint b) { - ulong t = (((ulong(x)) | ((ulong(x)) << 24)) & 0x00ff00ff00ff00ff) * a; - t += (((ulong(y)) | ((ulong(y)) << 24)) & 0x00ff00ff00ff00ff) * b; - t >>= 8; - t &= 0x00ff00ff00ff00ff; - return (uint(t)) | (uint(t >> 24)); -} - -Q_STATIC_INLINE_FUNCTION uint INTERPOLATE_PIXEL_255(uint x, uint a, uint y, uint b) { - ulong t = (((ulong(x)) | ((ulong(x)) << 24)) & 0x00ff00ff00ff00ff) * a; - t += (((ulong(y)) | ((ulong(y)) << 24)) & 0x00ff00ff00ff00ff) * b; - t = (t + ((t >> 8) & 0xff00ff00ff00ff) + 0x80008000800080); - t &= 0x00ff00ff00ff00ff; - return (uint(t)) | (uint(t >> 24)); -} - -Q_STATIC_INLINE_FUNCTION uint BYTE_MUL(uint x, uint a) { - ulong t = (((ulong(x)) | ((ulong(x)) << 24)) & 0x00ff00ff00ff00ff) * a; - t = (t + ((t >> 8) & 0xff00ff00ff00ff) + 0x80008000800080); - t &= 0x00ff00ff00ff00ff; - return (uint(t)) | (uint(t >> 24)); -} - -Q_STATIC_INLINE_FUNCTION uint PREMUL(uint x) { - uint a = x >> 24; - ulong t = (((ulong(x)) | ((ulong(x)) << 24)) & 0x00ff00ff00ff00ff) * a; - t = (t + ((t >> 8) & 0xff00ff00ff00ff) + 0x80008000800080); - t &= 0x00ff00ff00ff00ff; - return (uint(t)) | (uint(t >> 24)) | 0xff000000; -} -#endif - const uint qt_bayer_matrix[16][16] = { { 0x1, 0xc0, 0x30, 0xf0, 0xc, 0xcc, 0x3c, 0xfc, 0x3, 0xc3, 0x33, 0xf3, 0xf, 0xcf, 0x3f, 0xff}, @@ -1945,6 +1938,100 @@ const uint qt_bayer_matrix[16][16] = { ((((argb >> 24) * alpha) >> 8) << 24) | (argb & 0x00ffffff) +#if QT_POINTER_SIZE == 8 // 64-bit versions +#define AMIX(mask) (qMin(((qint64(s)&mask) + (qint64(d)&mask)), qint64(mask))) +#define MIX(mask) (qMin(((qint64(s)&mask) + (qint64(d)&mask)), qint64(mask))) +#else // 32 bits +// The mask for alpha can overflow over 32 bits +#define AMIX(mask) quint32(qMin(((qint64(s)&mask) + (qint64(d)&mask)), qint64(mask))) +#define MIX(mask) (qMin(((quint32(s)&mask) + (quint32(d)&mask)), quint32(mask))) +#endif + +inline int comp_func_Plus_one_pixel_const_alpha(uint d, const uint s, const uint const_alpha, const uint one_minus_const_alpha) +{ + const int result = (AMIX(AMASK) | MIX(RMASK) | MIX(GMASK) | MIX(BMASK)); + return INTERPOLATE_PIXEL_255(result, const_alpha, d, one_minus_const_alpha); +} + +inline int comp_func_Plus_one_pixel(uint d, const uint s) +{ + const int result = (AMIX(AMASK) | MIX(RMASK) | MIX(GMASK) | MIX(BMASK)); + return result; +} + +#undef MIX +#undef AMIX + +// prototypes of all the composition functions +void QT_FASTCALL comp_func_SourceOver(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_DestinationOver(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, uint const_alpha); +void QT_FASTCALL comp_func_Source(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_Destination(uint *, const uint *, int, uint); +void QT_FASTCALL comp_func_SourceIn(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_DestinationIn(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_SourceOut(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_DestinationOut(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_SourceAtop(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_DestinationAtop(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_XOR(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_Plus(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_Multiply(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_Screen(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_Overlay(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_Darken(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_Lighten(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_ColorDodge(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_ColorBurn(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_HardLight(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_SoftLight(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_Difference(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL comp_func_Exclusion(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL rasterop_SourceOrDestination(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL rasterop_SourceAndDestination(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL rasterop_SourceXorDestination(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL rasterop_NotSourceAndNotDestination(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL rasterop_NotSourceOrNotDestination(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL rasterop_NotSourceXorDestination(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL rasterop_NotSource(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL rasterop_NotSourceAndDestination(uint *dest, const uint *src, int length, uint const_alpha); +void QT_FASTCALL rasterop_SourceAndNotDestination(uint *dest, const uint *src, int length, uint const_alpha); + +// prototypes of all the solid composition functions +void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_Destination(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_SourceIn(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_DestinationIn(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_SourceOut(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_DestinationOut(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_SourceAtop(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_DestinationAtop(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_XOR(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_Plus(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_Multiply(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_Screen(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_Overlay(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_Darken(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_Lighten(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_ColorDodge(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_ColorBurn(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_HardLight(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_SoftLight(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_Difference(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL comp_func_solid_Exclusion(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL rasterop_solid_SourceOrDestination(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL rasterop_solid_SourceAndDestination(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL rasterop_solid_SourceXorDestination(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL rasterop_solid_NotSourceAndNotDestination(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL rasterop_solid_NotSourceOrNotDestination(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL rasterop_solid_NotSourceXorDestination(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL rasterop_solid_NotSource(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL rasterop_solid_NotSourceAndDestination(uint *dest, int length, uint color, uint const_alpha); +void QT_FASTCALL rasterop_solid_SourceAndNotDestination(uint *dest, int length, uint color, uint const_alpha); + QT_END_NAMESPACE #endif // QDRAWHELPER_P_H diff --git a/src/gui/painting/qdrawhelper_sse.cpp b/src/gui/painting/qdrawhelper_sse.cpp index 58a7fdd..8b17c29 100644 --- a/src/gui/painting/qdrawhelper_sse.cpp +++ b/src/gui/painting/qdrawhelper_sse.cpp @@ -77,14 +77,35 @@ CompositionFunction qt_functionForMode_SSE[numCompositionFunctions] = { comp_func_DestinationOver<QSSEIntrinsics>, comp_func_Clear<QSSEIntrinsics>, comp_func_Source<QSSEIntrinsics>, - 0, + comp_func_Destination, comp_func_SourceIn<QSSEIntrinsics>, comp_func_DestinationIn<QSSEIntrinsics>, comp_func_SourceOut<QSSEIntrinsics>, comp_func_DestinationOut<QSSEIntrinsics>, comp_func_SourceAtop<QSSEIntrinsics>, comp_func_DestinationAtop<QSSEIntrinsics>, - comp_func_XOR<QSSEIntrinsics> + comp_func_XOR<QSSEIntrinsics>, + comp_func_Plus, + comp_func_Multiply, + comp_func_Screen, + comp_func_Overlay, + comp_func_Darken, + comp_func_Lighten, + comp_func_ColorDodge, + comp_func_ColorBurn, + comp_func_HardLight, + comp_func_SoftLight, + comp_func_Difference, + comp_func_Exclusion, + rasterop_SourceOrDestination, + rasterop_SourceAndDestination, + rasterop_SourceXorDestination, + rasterop_NotSourceAndNotDestination, + rasterop_NotSourceOrNotDestination, + rasterop_NotSourceXorDestination, + rasterop_NotSource, + rasterop_NotSourceAndDestination, + rasterop_SourceAndNotDestination }; void qt_blend_color_argb_sse(int count, const QSpan *spans, void *userData) diff --git a/src/gui/painting/qdrawhelper_sse2.cpp b/src/gui/painting/qdrawhelper_sse2.cpp index 6ac64d3..f97d865 100644 --- a/src/gui/painting/qdrawhelper_sse2.cpp +++ b/src/gui/painting/qdrawhelper_sse2.cpp @@ -43,96 +43,18 @@ #ifdef QT_HAVE_SSE2 +#include <private/qdrawingprimitive_sse2_p.h> #include <private/qpaintengine_raster_p.h> -#ifdef QT_LINUXBASE -// this is an evil hack - the posix_memalign declaration in LSB -// is wrong - see http://bugs.linuxbase.org/show_bug.cgi?id=2431 -# define posix_memalign _lsb_hack_posix_memalign -# include <emmintrin.h> -# undef posix_memalign -#else -# include <emmintrin.h> -#endif - QT_BEGIN_NAMESPACE -/* - * Multiply the components of pixelVector by alphaChannel - * Each 32bits components of alphaChannel must be in the form 0x00AA00AA - * colorMask must have 0x00ff00ff on each 32 bits component - * half must have the value 128 (0x80) for each 32 bits compnent - */ -#define BYTE_MUL_SSE2(result, pixelVector, alphaChannel, colorMask, half) \ -{ \ - /* 1. separate the colors in 2 vectors so each color is on 16 bits \ - (in order to be multiplied by the alpha \ - each 32 bit of dstVectorAG are in the form 0x00AA00GG \ - each 32 bit of dstVectorRB are in the form 0x00RR00BB */\ - __m128i pixelVectorAG = _mm_srli_epi16(pixelVector, 8); \ - __m128i pixelVectorRB = _mm_and_si128(pixelVector, colorMask); \ - \ - /* 2. multiply the vectors by the alpha channel */\ - pixelVectorAG = _mm_mullo_epi16(pixelVectorAG, alphaChannel); \ - pixelVectorRB = _mm_mullo_epi16(pixelVectorRB, alphaChannel); \ - \ - /* 3. devide by 255, that's the tricky part. \ - we do it like for BYTE_MUL(), with bit shift: X/255 ~= (X + X/256 + rounding)/256 */ \ - /** so first (X + X/256 + rounding) */\ - pixelVectorRB = _mm_add_epi16(pixelVectorRB, _mm_srli_epi16(pixelVectorRB, 8)); \ - pixelVectorRB = _mm_add_epi16(pixelVectorRB, half); \ - pixelVectorAG = _mm_add_epi16(pixelVectorAG, _mm_srli_epi16(pixelVectorAG, 8)); \ - pixelVectorAG = _mm_add_epi16(pixelVectorAG, half); \ - \ - /** second devide by 256 */\ - pixelVectorRB = _mm_srli_epi16(pixelVectorRB, 8); \ - /** for AG, we could >> 8 to divide followed by << 8 to put the \ - bytes in the correct position. By masking instead, we execute \ - only one instruction */\ - pixelVectorAG = _mm_andnot_si128(colorMask, pixelVectorAG); \ - \ - /* 4. combine the 2 pairs of colors */ \ - result = _mm_or_si128(pixelVectorAG, pixelVectorRB); \ -} - -/* - * Each 32bits components of alphaChannel must be in the form 0x00AA00AA - * oneMinusAlphaChannel must be 255 - alpha for each 32 bits component - * colorMask must have 0x00ff00ff on each 32 bits component - * half must have the value 128 (0x80) for each 32 bits compnent - */ -#define INTERPOLATE_PIXEL_255_SSE2(result, srcVector, dstVector, alphaChannel, oneMinusAlphaChannel, colorMask, half) { \ - /* interpolate AG */\ - __m128i srcVectorAG = _mm_srli_epi16(srcVector, 8); \ - __m128i dstVectorAG = _mm_srli_epi16(dstVector, 8); \ - __m128i srcVectorAGalpha = _mm_mullo_epi16(srcVectorAG, alphaChannel); \ - __m128i dstVectorAGoneMinusAlphalpha = _mm_mullo_epi16(dstVectorAG, oneMinusAlphaChannel); \ - __m128i finalAG = _mm_add_epi16(srcVectorAGalpha, dstVectorAGoneMinusAlphalpha); \ - finalAG = _mm_add_epi16(finalAG, _mm_srli_epi16(finalAG, 8)); \ - finalAG = _mm_add_epi16(finalAG, half); \ - finalAG = _mm_andnot_si128(colorMask, finalAG); \ - \ - /* interpolate RB */\ - __m128i srcVectorRB = _mm_and_si128(srcVector, colorMask); \ - __m128i dstVectorRB = _mm_and_si128(dstVector, colorMask); \ - __m128i srcVectorRBalpha = _mm_mullo_epi16(srcVectorRB, alphaChannel); \ - __m128i dstVectorRBoneMinusAlphalpha = _mm_mullo_epi16(dstVectorRB, oneMinusAlphaChannel); \ - __m128i finalRB = _mm_add_epi16(srcVectorRBalpha, dstVectorRBoneMinusAlphalpha); \ - finalRB = _mm_add_epi16(finalRB, _mm_srli_epi16(finalRB, 8)); \ - finalRB = _mm_add_epi16(finalRB, half); \ - finalRB = _mm_srli_epi16(finalRB, 8); \ - \ - /* combine */\ - result = _mm_or_si128(finalAG, finalRB); \ -} - void qt_blend_argb32_on_argb32_sse2(uchar *destPixels, int dbpl, const uchar *srcPixels, int sbpl, int w, int h, int const_alpha) { const quint32 *src = (const quint32 *) srcPixels; - quint32 *dst = (uint *) destPixels; + quint32 *dst = (quint32 *) destPixels; if (const_alpha == 256) { const __m128i alphaMask = _mm_set1_epi32(0xff000000); const __m128i nullVector = _mm_set1_epi32(0); @@ -140,41 +62,7 @@ void qt_blend_argb32_on_argb32_sse2(uchar *destPixels, int dbpl, const __m128i one = _mm_set1_epi16(0xff); const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); for (int y = 0; y < h; ++y) { - int x = 0; - for (; x < w-3; x += 4) { - const __m128i srcVector = _mm_loadu_si128((__m128i *)&src[x]); - const __m128i srcVectorAlpha = _mm_and_si128(srcVector, alphaMask); - if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, alphaMask)) == 0xffff) { - // all opaque - _mm_storeu_si128((__m128i *)&dst[x], srcVector); - } else if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, nullVector)) != 0xffff) { - // not fully transparent - // result = s + d * (1-alpha) - - // extract the alpha channel on 2 x 16 bits - // so we have room for the multiplication - // each 32 bits will be in the form 0x00AA00AA - // with A being the 1 - alpha - __m128i alphaChannel = _mm_srli_epi32(srcVector, 24); - alphaChannel = _mm_or_si128(alphaChannel, _mm_slli_epi32(alphaChannel, 16)); - alphaChannel = _mm_sub_epi16(one, alphaChannel); - - const __m128i dstVector = _mm_loadu_si128((__m128i *)&dst[x]); - __m128i destMultipliedByOneMinusAlpha; - BYTE_MUL_SSE2(destMultipliedByOneMinusAlpha, dstVector, alphaChannel, colorMask, half); - - // result = s + d * (1-alpha) - const __m128i result = _mm_add_epi8(srcVector, destMultipliedByOneMinusAlpha); - _mm_storeu_si128((__m128i *)&dst[x], result); - } - } - 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)); - } + BLEND_SOURCE_OVER_ARGB32_SSE2(dst, src, w, nullVector, half, one, colorMask, alphaMask); dst = (quint32 *)(((uchar *) dst) + dbpl); src = (const quint32 *)(((const uchar *) src) + sbpl); } @@ -189,31 +77,7 @@ void qt_blend_argb32_on_argb32_sse2(uchar *destPixels, int dbpl, const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); const __m128i constAlphaVector = _mm_set1_epi16(const_alpha); for (int y = 0; y < h; ++y) { - int x = 0; - for (; x < w-3; x += 4) { - __m128i srcVector = _mm_loadu_si128((__m128i *)&src[x]); - if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVector, nullVector)) != 0xffff) { - BYTE_MUL_SSE2(srcVector, srcVector, constAlphaVector, colorMask, half); - - __m128i alphaChannel = _mm_srli_epi32(srcVector, 24); - alphaChannel = _mm_or_si128(alphaChannel, _mm_slli_epi32(alphaChannel, 16)); - alphaChannel = _mm_sub_epi16(one, alphaChannel); - - const __m128i dstVector = _mm_loadu_si128((__m128i *)&dst[x]); - __m128i destMultipliedByOneMinusAlpha; - BYTE_MUL_SSE2(destMultipliedByOneMinusAlpha, dstVector, alphaChannel, colorMask, half); - - const __m128i result = _mm_add_epi8(srcVector, destMultipliedByOneMinusAlpha); - _mm_storeu_si128((__m128i *)&dst[x], result); - } - } - for (; x<w; ++x) { - quint32 s = src[x]; - if (s != 0) { - s = BYTE_MUL(s, const_alpha); - dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); - } - } + BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_SSE2(dst, src, w, nullVector, half, one, colorMask, constAlphaVector) dst = (quint32 *)(((uchar *) dst) + dbpl); src = (const quint32 *)(((const uchar *) src) + sbpl); } @@ -232,7 +96,7 @@ void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl, int const_alpha) { const quint32 *src = (const quint32 *) srcPixels; - quint32 *dst = (uint *) destPixels; + quint32 *dst = (quint32 *) destPixels; if (const_alpha != 256) { if (const_alpha != 0) { const __m128i nullVector = _mm_set1_epi32(0); @@ -245,13 +109,21 @@ void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl, const __m128i oneMinusConstAlpha = _mm_set1_epi16(one_minus_const_alpha); for (int y = 0; y < h; ++y) { int x = 0; + + // First, align dest to 16 bytes: + ALIGNMENT_PROLOGUE_16BYTES(dst, x, w) { + quint32 s = src[x]; + s = BYTE_MUL(s, const_alpha); + dst[x] = INTERPOLATE_PIXEL_255(src[x], const_alpha, dst[x], one_minus_const_alpha); + } + for (; x < w-3; x += 4) { __m128i srcVector = _mm_loadu_si128((__m128i *)&src[x]); if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVector, nullVector)) != 0xffff) { - const __m128i dstVector = _mm_loadu_si128((__m128i *)&dst[x]); + const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); __m128i result; INTERPOLATE_PIXEL_255_SSE2(result, srcVector, dstVector, constAlphaVector, oneMinusConstAlpha, colorMask, half); - _mm_storeu_si128((__m128i *)&dst[x], result); + _mm_store_si128((__m128i *)&dst[x], result); } } for (; x<w; ++x) { @@ -268,6 +140,105 @@ void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl, } } +void QT_FASTCALL comp_func_SourceOver_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha) +{ + Q_ASSERT(const_alpha < 256); + + const quint32 *src = (const quint32 *) srcPixels; + quint32 *dst = (quint32 *) destPixels; + + const __m128i nullVector = _mm_set1_epi32(0); + const __m128i half = _mm_set1_epi16(0x80); + const __m128i one = _mm_set1_epi16(0xff); + const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); + if (const_alpha == 255) { + const __m128i alphaMask = _mm_set1_epi32(0xff000000); + BLEND_SOURCE_OVER_ARGB32_SSE2(dst, src, length, nullVector, half, one, colorMask, alphaMask); + } else { + const __m128i constAlphaVector = _mm_set1_epi16(const_alpha); + BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_SSE2(dst, src, length, nullVector, half, one, colorMask, constAlphaVector); + } +} + +void QT_FASTCALL comp_func_Plus_sse2(uint *dst, const uint *src, int length, uint const_alpha) +{ + int x = 0; + + if (const_alpha == 255) { + // 1) Prologue: align destination on 16 bytes + ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) + dst[x] = comp_func_Plus_one_pixel(dst[x], src[x]); + + // 2) composition with SSE2 + for (; x < length - 3; x += 4) { + const __m128i srcVector = _mm_loadu_si128((__m128i *)&src[x]); + const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); + + const __m128i result = _mm_adds_epu8(srcVector, dstVector); + _mm_store_si128((__m128i *)&dst[x], result); + } + + // 3) Epilogue: + for (; x < length; ++x) + dst[x] = comp_func_Plus_one_pixel(dst[x], src[x]); + } else { + const int one_minus_const_alpha = 255 - const_alpha; + const __m128i constAlphaVector = _mm_set1_epi16(const_alpha); + const __m128i oneMinusConstAlpha = _mm_set1_epi16(one_minus_const_alpha); + + // 1) Prologue: align destination on 16 bytes + ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) + dst[x] = comp_func_Plus_one_pixel_const_alpha(dst[x], src[x], const_alpha, one_minus_const_alpha); + + const __m128i half = _mm_set1_epi16(0x80); + const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); + // 2) composition with SSE2 + for (; x < length - 3; x += 4) { + const __m128i srcVector = _mm_loadu_si128((__m128i *)&src[x]); + const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); + + __m128i result = _mm_adds_epu8(srcVector, dstVector); + INTERPOLATE_PIXEL_255_SSE2(result, result, dstVector, constAlphaVector, oneMinusConstAlpha, colorMask, half) + _mm_store_si128((__m128i *)&dst[x], result); + } + + // 3) Epilogue: + for (; x < length; ++x) + dst[x] = comp_func_Plus_one_pixel_const_alpha(dst[x], src[x], const_alpha, one_minus_const_alpha); + } +} + +void QT_FASTCALL comp_func_Source_sse2(uint *dst, const uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) { + ::memcpy(dst, src, length * sizeof(uint)); + } else { + const int ialpha = 255 - const_alpha; + + int x = 0; + + // 1) prologue, align on 16 bytes + ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) + dst[x] = INTERPOLATE_PIXEL_255(src[x], const_alpha, dst[x], ialpha); + + // 2) interpolate pixels with SSE2 + const __m128i half = _mm_set1_epi16(0x80); + const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); + const __m128i constAlphaVector = _mm_set1_epi16(const_alpha); + const __m128i oneMinusConstAlpha = _mm_set1_epi16(ialpha); + for (; x < length - 3; x += 4) { + const __m128i srcVector = _mm_loadu_si128((__m128i *)&src[x]); + __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); + INTERPOLATE_PIXEL_255_SSE2(dstVector, srcVector, dstVector, constAlphaVector, oneMinusConstAlpha, colorMask, half) + _mm_store_si128((__m128i *)&dst[x], dstVector); + } + + // 3) Epilogue + for (; x < length; ++x) + dst[x] = INTERPOLATE_PIXEL_255(src[x], const_alpha, dst[x], ialpha); + } +} + void qt_memfill32_sse2(quint32 *dest, quint32 value, int count) { if (count < 7) { @@ -295,10 +266,10 @@ void qt_memfill32_sse2(quint32 *dest, quint32 value, int count) int n = (count128 + 3) / 4; switch (count128 & 0x3) { - case 0: do { _mm_store_si128(dst128++, value128); - case 3: _mm_store_si128(dst128++, value128); - case 2: _mm_store_si128(dst128++, value128); - case 1: _mm_store_si128(dst128++, value128); + case 0: do { _mm_stream_si128(dst128++, value128); + case 3: _mm_stream_si128(dst128++, value128); + case 2: _mm_stream_si128(dst128++, value128); + case 1: _mm_stream_si128(dst128++, value128); } while (--n > 0); } @@ -312,6 +283,109 @@ void qt_memfill32_sse2(quint32 *dest, quint32 value, int count) } } +void QT_FASTCALL comp_func_solid_SourceOver_sse2(uint *destPixels, int length, uint color, uint const_alpha) +{ + if ((const_alpha & qAlpha(color)) == 255) { + qt_memfill32_sse2(destPixels, color, length); + } else { + if (const_alpha != 255) + color = BYTE_MUL(color, const_alpha); + + const quint32 minusAlphaOfColor = qAlpha(~color); + int x = 0; + + quint32 *dst = (quint32 *) destPixels; + const __m128i colorVector = _mm_set1_epi32(color); + const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); + const __m128i half = _mm_set1_epi16(0x80); + const __m128i minusAlphaOfColorVector = _mm_set1_epi16(minusAlphaOfColor); + + ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) + destPixels[x] = color + BYTE_MUL(destPixels[x], minusAlphaOfColor); + + for (; x < length-3; x += 4) { + __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); + BYTE_MUL_SSE2(dstVector, dstVector, minusAlphaOfColorVector, colorMask, half); + dstVector = _mm_add_epi8(colorVector, dstVector); + _mm_store_si128((__m128i *)&dst[x], dstVector); + } + for (;x < length; ++x) + destPixels[x] = color + BYTE_MUL(destPixels[x], minusAlphaOfColor); + } +} + +CompositionFunctionSolid qt_functionForModeSolid_onlySSE2[numCompositionFunctions] = { + comp_func_solid_SourceOver_sse2, + comp_func_solid_DestinationOver, + comp_func_solid_Clear, + comp_func_solid_Source, + comp_func_solid_Destination, + comp_func_solid_SourceIn, + comp_func_solid_DestinationIn, + comp_func_solid_SourceOut, + comp_func_solid_DestinationOut, + comp_func_solid_SourceAtop, + comp_func_solid_DestinationAtop, + comp_func_solid_XOR, + comp_func_solid_Plus, + comp_func_solid_Multiply, + comp_func_solid_Screen, + comp_func_solid_Overlay, + comp_func_solid_Darken, + comp_func_solid_Lighten, + comp_func_solid_ColorDodge, + comp_func_solid_ColorBurn, + comp_func_solid_HardLight, + comp_func_solid_SoftLight, + comp_func_solid_Difference, + comp_func_solid_Exclusion, + rasterop_solid_SourceOrDestination, + rasterop_solid_SourceAndDestination, + rasterop_solid_SourceXorDestination, + rasterop_solid_NotSourceAndNotDestination, + rasterop_solid_NotSourceOrNotDestination, + rasterop_solid_NotSourceXorDestination, + rasterop_solid_NotSource, + rasterop_solid_NotSourceAndDestination, + rasterop_solid_SourceAndNotDestination +}; + +CompositionFunction qt_functionForMode_onlySSE2[numCompositionFunctions] = { + comp_func_SourceOver_sse2, + comp_func_DestinationOver, + comp_func_Clear, + comp_func_Source_sse2, + comp_func_Destination, + comp_func_SourceIn, + comp_func_DestinationIn, + comp_func_SourceOut, + comp_func_DestinationOut, + comp_func_SourceAtop, + comp_func_DestinationAtop, + comp_func_XOR, + comp_func_Plus_sse2, + comp_func_Multiply, + comp_func_Screen, + comp_func_Overlay, + comp_func_Darken, + comp_func_Lighten, + comp_func_ColorDodge, + comp_func_ColorBurn, + comp_func_HardLight, + comp_func_SoftLight, + comp_func_Difference, + comp_func_Exclusion, + rasterop_SourceOrDestination, + rasterop_SourceAndDestination, + rasterop_SourceXorDestination, + rasterop_NotSourceAndNotDestination, + rasterop_NotSourceOrNotDestination, + rasterop_NotSourceXorDestination, + rasterop_NotSource, + rasterop_NotSourceAndDestination, + rasterop_SourceAndNotDestination +}; + void qt_memfill16_sse2(quint16 *dest, quint16 value, int count) { if (count < 3) { diff --git a/src/gui/painting/qdrawhelper_sse3dnow.cpp b/src/gui/painting/qdrawhelper_sse3dnow.cpp index c58cf13..9ae0e07 100644 --- a/src/gui/painting/qdrawhelper_sse3dnow.cpp +++ b/src/gui/painting/qdrawhelper_sse3dnow.cpp @@ -85,14 +85,35 @@ CompositionFunction qt_functionForMode_SSE3DNOW[numCompositionFunctions] = { comp_func_DestinationOver<QSSE3DNOWIntrinsics>, comp_func_Clear<QSSE3DNOWIntrinsics>, comp_func_Source<QSSE3DNOWIntrinsics>, - 0, + comp_func_Destination, comp_func_SourceIn<QSSE3DNOWIntrinsics>, comp_func_DestinationIn<QSSE3DNOWIntrinsics>, comp_func_SourceOut<QSSE3DNOWIntrinsics>, comp_func_DestinationOut<QSSE3DNOWIntrinsics>, comp_func_SourceAtop<QSSE3DNOWIntrinsics>, comp_func_DestinationAtop<QSSE3DNOWIntrinsics>, - comp_func_XOR<QSSE3DNOWIntrinsics> + comp_func_XOR<QSSE3DNOWIntrinsics>, + comp_func_Plus, + comp_func_Multiply, + comp_func_Screen, + comp_func_Overlay, + comp_func_Darken, + comp_func_Lighten, + comp_func_ColorDodge, + comp_func_ColorBurn, + comp_func_HardLight, + comp_func_SoftLight, + comp_func_Difference, + comp_func_Exclusion, + rasterop_SourceOrDestination, + rasterop_SourceAndDestination, + rasterop_SourceXorDestination, + rasterop_NotSourceAndNotDestination, + rasterop_NotSourceOrNotDestination, + rasterop_NotSourceXorDestination, + rasterop_NotSource, + rasterop_NotSourceAndDestination, + rasterop_SourceAndNotDestination }; void qt_blend_color_argb_sse3dnow(int count, const QSpan *spans, void *userData) diff --git a/src/gui/painting/qdrawhelper_ssse3.cpp b/src/gui/painting/qdrawhelper_ssse3.cpp new file mode 100644 index 0000000..fb5602e --- /dev/null +++ b/src/gui/painting/qdrawhelper_ssse3.cpp @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include <private/qdrawhelper_x86_p.h> + +#ifdef QT_HAVE_SSSE3 + +#include <private/qdrawingprimitive_sse2_p.h> + +QT_BEGIN_NAMESPACE + +inline static void blend_pixel(quint32 &dst, const quint32 src) +{ + if (src >= 0xff000000) + dst = src; + else if (src != 0) + dst = src + BYTE_MUL(dst, qAlpha(~src)); +} + + +/* The instruction palignr uses direct arguments, so we have to generate the code fo the different + shift (4, 8, 12). Checking the alignment inside the loop is unfortunatelly way too slow. + */ +#define BLENDING_LOOP(palignrOffset, length)\ + for (; x < length-3; x += 4) { \ + const __m128i srcVectorLastLoaded = _mm_load_si128((__m128i *)&src[x - minusOffsetToAlignSrcOn16Bytes + 4]);\ + const __m128i srcVector = _mm_alignr_epi8(srcVectorLastLoaded, srcVectorPrevLoaded, palignrOffset); \ + const __m128i srcVectorAlpha = _mm_and_si128(srcVector, alphaMask); \ + if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, alphaMask)) == 0xffff) { \ + _mm_store_si128((__m128i *)&dst[x], srcVector); \ + } else if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, nullVector)) != 0xffff) { \ + __m128i alphaChannel = _mm_shuffle_epi8(srcVector, alphaShuffleMask); \ + alphaChannel = _mm_sub_epi16(one, alphaChannel); \ + const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); \ + __m128i destMultipliedByOneMinusAlpha; \ + BYTE_MUL_SSE2(destMultipliedByOneMinusAlpha, dstVector, alphaChannel, colorMask, half); \ + const __m128i result = _mm_add_epi8(srcVector, destMultipliedByOneMinusAlpha); \ + _mm_store_si128((__m128i *)&dst[x], result); \ + } \ + srcVectorPrevLoaded = srcVectorLastLoaded;\ + } + + +// Basically blend src over dst with the const alpha defined as constAlphaVector. +// nullVector, half, one, colorMask are constant accross the whole image/texture, and should be defined as: +//const __m128i nullVector = _mm_set1_epi32(0); +//const __m128i half = _mm_set1_epi16(0x80); +//const __m128i one = _mm_set1_epi16(0xff); +//const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); +//const __m128i alphaMask = _mm_set1_epi32(0xff000000); +// +// The computation being done is: +// result = s + d * (1-alpha) +// with shortcuts if fully opaque or fully transparent. +#define BLEND_SOURCE_OVER_ARGB32_SSSE3(dst, src, length, nullVector, half, one, colorMask, alphaMask) { \ + int x = 0; \ +\ + /* First, get dst aligned. */ \ + ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) { \ + blend_pixel(dst[x], src[x]); \ + } \ +\ + const int minusOffsetToAlignSrcOn16Bytes = (reinterpret_cast<quintptr>(&(src[x])) >> 2) & 0x3;\ +\ + if (!minusOffsetToAlignSrcOn16Bytes) {\ + /* src is aligned, usual algorithm but with aligned operations.\ + See the SSE2 version for more documentation on the algorithm itself. */\ + const __m128i alphaShuffleMask = _mm_set_epi8(0xff,15,0xff,15,0xff,11,0xff,11,0xff,7,0xff,7,0xff,3,0xff,3);\ + for (; x < length-3; x += 4) { \ + const __m128i srcVector = _mm_load_si128((__m128i *)&src[x]); \ + const __m128i srcVectorAlpha = _mm_and_si128(srcVector, alphaMask); \ + if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, alphaMask)) == 0xffff) { \ + _mm_store_si128((__m128i *)&dst[x], srcVector); \ + } else if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, nullVector)) != 0xffff) { \ + __m128i alphaChannel = _mm_shuffle_epi8(srcVector, alphaShuffleMask); \ + alphaChannel = _mm_sub_epi16(one, alphaChannel); \ + const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); \ + __m128i destMultipliedByOneMinusAlpha; \ + BYTE_MUL_SSE2(destMultipliedByOneMinusAlpha, dstVector, alphaChannel, colorMask, half); \ + const __m128i result = _mm_add_epi8(srcVector, destMultipliedByOneMinusAlpha); \ + _mm_store_si128((__m128i *)&dst[x], result); \ + } \ + } /* end for() */\ + } else if ((length - x) >= 8) {\ + /* We use two vectors to extract the src: prevLoaded for the first pixels, lastLoaded for the current pixels. */\ + __m128i srcVectorPrevLoaded = _mm_load_si128((__m128i *)&src[x - minusOffsetToAlignSrcOn16Bytes]);\ + const int palignrOffset = minusOffsetToAlignSrcOn16Bytes << 2;\ +\ + const __m128i alphaShuffleMask = _mm_set_epi8(0xff,15,0xff,15,0xff,11,0xff,11,0xff,7,0xff,7,0xff,3,0xff,3);\ + switch (palignrOffset) {\ + case 4:\ + BLENDING_LOOP(4, length)\ + break;\ + case 8:\ + BLENDING_LOOP(8, length)\ + break;\ + case 12:\ + BLENDING_LOOP(12, length)\ + break;\ + }\ + }\ + for (; x < length; ++x) \ + blend_pixel(dst[x], src[x]); \ +} + +void qt_blend_argb32_on_argb32_ssse3(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha) +{ + const quint32 *src = (const quint32 *) srcPixels; + quint32 *dst = (quint32 *) destPixels; + if (const_alpha == 256) { + const __m128i alphaMask = _mm_set1_epi32(0xff000000); + const __m128i nullVector = _mm_setzero_si128(); + const __m128i half = _mm_set1_epi16(0x80); + const __m128i one = _mm_set1_epi16(0xff); + const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); + + for (int y = 0; y < h; ++y) { + BLEND_SOURCE_OVER_ARGB32_SSSE3(dst, src, w, nullVector, half, one, colorMask, alphaMask); + dst = (quint32 *)(((uchar *) dst) + dbpl); + src = (const quint32 *)(((const uchar *) src) + sbpl); + } + } else if (const_alpha != 0) { + // dest = (s + d * sia) * ca + d * cia + // = s * ca + d * (sia * ca + cia) + // = s * ca + d * (1 - sa*ca) + const_alpha = (const_alpha * 255) >> 8; + const __m128i nullVector = _mm_setzero_si128(); + const __m128i half = _mm_set1_epi16(0x80); + const __m128i one = _mm_set1_epi16(0xff); + const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); + const __m128i constAlphaVector = _mm_set1_epi16(const_alpha); + for (int y = 0; y < h; ++y) { + BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_SSE2(dst, src, w, nullVector, half, one, colorMask, constAlphaVector) + dst = (quint32 *)(((uchar *) dst) + dbpl); + src = (const quint32 *)(((const uchar *) src) + sbpl); + } + } +} + +QT_END_NAMESPACE + +#endif // QT_HAVE_SSSE3 diff --git a/src/gui/painting/qdrawhelper_x86_p.h b/src/gui/painting/qdrawhelper_x86_p.h index d7282a7..09ccd77 100644 --- a/src/gui/painting/qdrawhelper_x86_p.h +++ b/src/gui/painting/qdrawhelper_x86_p.h @@ -122,6 +122,9 @@ void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl, const uchar *srcPixels, int sbpl, int w, int h, int const_alpha); + +extern CompositionFunction qt_functionForMode_onlySSE2[]; +extern CompositionFunctionSolid qt_functionForModeSolid_onlySSE2[]; #endif // QT_HAVE_SSE2 #ifdef QT_HAVE_IWMMXT diff --git a/src/gui/painting/qdrawingprimitive_sse2_p.h b/src/gui/painting/qdrawingprimitive_sse2_p.h new file mode 100644 index 0000000..d8f6bf5 --- /dev/null +++ b/src/gui/painting/qdrawingprimitive_sse2_p.h @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** 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 QDRAWINGPRIMITIVE_SSE2_P_H +#define QDRAWINGPRIMITIVE_SSE2_P_H + +#include <private/qsimd_p.h> + +#ifdef QT_HAVE_SSE2 + +// +// 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. +// + +QT_BEGIN_NAMESPACE + +/* + * Multiply the components of pixelVector by alphaChannel + * Each 32bits components of alphaChannel must be in the form 0x00AA00AA + * colorMask must have 0x00ff00ff on each 32 bits component + * half must have the value 128 (0x80) for each 32 bits compnent + */ +#define BYTE_MUL_SSE2(result, pixelVector, alphaChannel, colorMask, half) \ +{ \ + /* 1. separate the colors in 2 vectors so each color is on 16 bits \ + (in order to be multiplied by the alpha \ + each 32 bit of dstVectorAG are in the form 0x00AA00GG \ + each 32 bit of dstVectorRB are in the form 0x00RR00BB */\ + __m128i pixelVectorAG = _mm_srli_epi16(pixelVector, 8); \ + __m128i pixelVectorRB = _mm_and_si128(pixelVector, colorMask); \ + \ + /* 2. multiply the vectors by the alpha channel */\ + pixelVectorAG = _mm_mullo_epi16(pixelVectorAG, alphaChannel); \ + pixelVectorRB = _mm_mullo_epi16(pixelVectorRB, alphaChannel); \ + \ + /* 3. devide by 255, that's the tricky part. \ + we do it like for BYTE_MUL(), with bit shift: X/255 ~= (X + X/256 + rounding)/256 */ \ + /** so first (X + X/256 + rounding) */\ + pixelVectorRB = _mm_add_epi16(pixelVectorRB, _mm_srli_epi16(pixelVectorRB, 8)); \ + pixelVectorRB = _mm_add_epi16(pixelVectorRB, half); \ + pixelVectorAG = _mm_add_epi16(pixelVectorAG, _mm_srli_epi16(pixelVectorAG, 8)); \ + pixelVectorAG = _mm_add_epi16(pixelVectorAG, half); \ + \ + /** second devide by 256 */\ + pixelVectorRB = _mm_srli_epi16(pixelVectorRB, 8); \ + /** for AG, we could >> 8 to divide followed by << 8 to put the \ + bytes in the correct position. By masking instead, we execute \ + only one instruction */\ + pixelVectorAG = _mm_andnot_si128(colorMask, pixelVectorAG); \ + \ + /* 4. combine the 2 pairs of colors */ \ + result = _mm_or_si128(pixelVectorAG, pixelVectorRB); \ +} + +/* + * Each 32bits components of alphaChannel must be in the form 0x00AA00AA + * oneMinusAlphaChannel must be 255 - alpha for each 32 bits component + * colorMask must have 0x00ff00ff on each 32 bits component + * half must have the value 128 (0x80) for each 32 bits compnent + */ +#define INTERPOLATE_PIXEL_255_SSE2(result, srcVector, dstVector, alphaChannel, oneMinusAlphaChannel, colorMask, half) { \ + /* interpolate AG */\ + __m128i srcVectorAG = _mm_srli_epi16(srcVector, 8); \ + __m128i dstVectorAG = _mm_srli_epi16(dstVector, 8); \ + __m128i srcVectorAGalpha = _mm_mullo_epi16(srcVectorAG, alphaChannel); \ + __m128i dstVectorAGoneMinusAlphalpha = _mm_mullo_epi16(dstVectorAG, oneMinusAlphaChannel); \ + __m128i finalAG = _mm_add_epi16(srcVectorAGalpha, dstVectorAGoneMinusAlphalpha); \ + finalAG = _mm_add_epi16(finalAG, _mm_srli_epi16(finalAG, 8)); \ + finalAG = _mm_add_epi16(finalAG, half); \ + finalAG = _mm_andnot_si128(colorMask, finalAG); \ + \ + /* interpolate RB */\ + __m128i srcVectorRB = _mm_and_si128(srcVector, colorMask); \ + __m128i dstVectorRB = _mm_and_si128(dstVector, colorMask); \ + __m128i srcVectorRBalpha = _mm_mullo_epi16(srcVectorRB, alphaChannel); \ + __m128i dstVectorRBoneMinusAlphalpha = _mm_mullo_epi16(dstVectorRB, oneMinusAlphaChannel); \ + __m128i finalRB = _mm_add_epi16(srcVectorRBalpha, dstVectorRBoneMinusAlphalpha); \ + finalRB = _mm_add_epi16(finalRB, _mm_srli_epi16(finalRB, 8)); \ + finalRB = _mm_add_epi16(finalRB, half); \ + finalRB = _mm_srli_epi16(finalRB, 8); \ + \ + /* combine */\ + result = _mm_or_si128(finalAG, finalRB); \ +} + +// Basically blend src over dst with the const alpha defined as constAlphaVector. +// nullVector, half, one, colorMask are constant accross the whole image/texture, and should be defined as: +//const __m128i nullVector = _mm_set1_epi32(0); +//const __m128i half = _mm_set1_epi16(0x80); +//const __m128i one = _mm_set1_epi16(0xff); +//const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); +//const __m128i alphaMask = _mm_set1_epi32(0xff000000); +// +// The computation being done is: +// result = s + d * (1-alpha) +// with shortcuts if fully opaque or fully transparent. +#define BLEND_SOURCE_OVER_ARGB32_SSE2(dst, src, length, nullVector, half, one, colorMask, alphaMask) { \ + int x = 0; \ +\ + /* First, get dst aligned. */ \ + ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) { \ + uint s = src[x]; \ + if (s >= 0xff000000) \ + dst[x] = s; \ + else if (s != 0) \ + dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); \ + } \ +\ + for (; x < length-3; x += 4) { \ + const __m128i srcVector = _mm_loadu_si128((__m128i *)&src[x]); \ + const __m128i srcVectorAlpha = _mm_and_si128(srcVector, alphaMask); \ + if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, alphaMask)) == 0xffff) { \ + /* all opaque */ \ + _mm_store_si128((__m128i *)&dst[x], srcVector); \ + } else if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, nullVector)) != 0xffff) { \ + /* not fully transparent */ \ + /* extract the alpha channel on 2 x 16 bits */ \ + /* so we have room for the multiplication */ \ + /* each 32 bits will be in the form 0x00AA00AA */ \ + /* with A being the 1 - alpha */ \ + __m128i alphaChannel = _mm_srli_epi32(srcVector, 24); \ + alphaChannel = _mm_or_si128(alphaChannel, _mm_slli_epi32(alphaChannel, 16)); \ + alphaChannel = _mm_sub_epi16(one, alphaChannel); \ + \ + const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); \ + __m128i destMultipliedByOneMinusAlpha; \ + BYTE_MUL_SSE2(destMultipliedByOneMinusAlpha, dstVector, alphaChannel, colorMask, half); \ + \ + /* result = s + d * (1-alpha) */\ + const __m128i result = _mm_add_epi8(srcVector, destMultipliedByOneMinusAlpha); \ + _mm_store_si128((__m128i *)&dst[x], result); \ + } \ + } \ + for (; x < length; ++x) { \ + uint s = src[x]; \ + if (s >= 0xff000000) \ + dst[x] = s; \ + else if (s != 0) \ + dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); \ + } \ +} + +// Basically blend src over dst with the const alpha defined as constAlphaVector. +// nullVector, half, one, colorMask are constant accross the whole image/texture, and should be defined as: +//const __m128i nullVector = _mm_set1_epi32(0); +//const __m128i half = _mm_set1_epi16(0x80); +//const __m128i one = _mm_set1_epi16(0xff); +//const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); +// +// The computation being done is: +// dest = (s + d * sia) * ca + d * cia +// = s * ca + d * (sia * ca + cia) +// = s * ca + d * (1 - sa*ca) +#define BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_SSE2(dst, src, length, nullVector, half, one, colorMask, constAlphaVector) \ +{ \ + int x = 0; \ +\ + ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) { \ + quint32 s = src[x]; \ + if (s != 0) { \ + s = BYTE_MUL(s, const_alpha); \ + dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); \ + } \ + } \ +\ + for (; x < length-3; x += 4) { \ + __m128i srcVector = _mm_loadu_si128((__m128i *)&src[x]); \ + if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVector, nullVector)) != 0xffff) { \ + BYTE_MUL_SSE2(srcVector, srcVector, constAlphaVector, colorMask, half); \ +\ + __m128i alphaChannel = _mm_srli_epi32(srcVector, 24); \ + alphaChannel = _mm_or_si128(alphaChannel, _mm_slli_epi32(alphaChannel, 16)); \ + alphaChannel = _mm_sub_epi16(one, alphaChannel); \ + \ + const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); \ + __m128i destMultipliedByOneMinusAlpha; \ + BYTE_MUL_SSE2(destMultipliedByOneMinusAlpha, dstVector, alphaChannel, colorMask, half); \ + \ + const __m128i result = _mm_add_epi8(srcVector, destMultipliedByOneMinusAlpha); \ + _mm_store_si128((__m128i *)&dst[x], result); \ + } \ + } \ + for (; x < length; ++x) { \ + quint32 s = src[x]; \ + if (s != 0) { \ + s = BYTE_MUL(s, const_alpha); \ + dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); \ + } \ + } \ +} + +QT_END_NAMESPACE + +#endif // QT_HAVE_SSE2 + +#endif // QDRAWINGPRIMITIVE_SSE2_P_H diff --git a/src/gui/painting/qdrawutil.cpp b/src/gui/painting/qdrawutil.cpp index 35bf2bf..11ea6d5 100644 --- a/src/gui/painting/qdrawutil.cpp +++ b/src/gui/painting/qdrawutil.cpp @@ -48,6 +48,7 @@ #include <private/qpaintengineex_p.h> #include <qvarlengtharray.h> #include <qmath.h> +#include <private/qstylehelper_p.h> QT_BEGIN_NAMESPACE @@ -1018,7 +1019,9 @@ void qDrawItem(QPainter *p, Qt::GUIStyle gs, ; #ifndef QT_NO_IMAGE_HEURISTIC_MASK } else { // color pixmap, no mask - QString k = QString::fromLatin1("$qt-drawitem-%1").arg(pm.cacheKey()); + QString k = QLatin1Literal("$qt-drawitem") + % HexString<qint64>(pm.cacheKey()); + if (!QPixmapCache::find(k, pm)) { pm = pm.createHeuristicMask(); pm.setMask((QBitmap&)pm); @@ -1081,7 +1084,7 @@ void qDrawItem(QPainter *p, Qt::GUIStyle gs, according to the \a margins structure. */ -typedef QVarLengthArray<QDrawPixmaps::Data, 16> QDrawPixmapsDataArray; +typedef QVarLengthArray<QPainter::PixmapFragment, 16> QPixmapFragmentsArray; /*! \since 4.6 @@ -1102,12 +1105,12 @@ void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargin const QPixmap &pixmap, const QRect &sourceRect,const QMargins &sourceMargins, const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints) { - QDrawPixmaps::Data d; + QPainter::PixmapFragment d; d.opacity = 1.0; d.rotation = 0.0; - QDrawPixmapsDataArray opaqueData; - QDrawPixmapsDataArray translucentData; + QPixmapFragmentsArray opaqueData; + QPixmapFragmentsArray translucentData; // source center const int sourceCenterTop = sourceRect.top() + sourceMargins.top(); @@ -1137,6 +1140,13 @@ void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargin xTarget.resize(columns + 1); yTarget.resize(rows + 1); + bool oldAA = painter->testRenderHint(QPainter::Antialiasing); + if (painter->paintEngine()->type() != QPaintEngine::OpenGL + && painter->paintEngine()->type() != QPaintEngine::OpenGL2 + && oldAA && painter->combinedTransform().type() != QTransform::TxNone) { + painter->setRenderHint(QPainter::Antialiasing, false); + } + xTarget[0] = targetRect.left(); xTarget[1] = targetCenterLeft; xTarget[columns - 1] = targetCenterRight; @@ -1182,44 +1192,56 @@ void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargin // corners if (targetMargins.top() > 0 && targetMargins.left() > 0 && sourceMargins.top() > 0 && sourceMargins.left() > 0) { // top left - d.point.setX(0.5 * (xTarget[1] + xTarget[0])); - d.point.setY(0.5 * (yTarget[1] + yTarget[0])); - d.source = QRectF(sourceRect.left(), sourceRect.top(), sourceMargins.left(), sourceMargins.top()); - d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.source.width(); - d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.source.height(); + d.x = (0.5 * (xTarget[1] + xTarget[0])); + d.y = (0.5 * (yTarget[1] + yTarget[0])); + d.sourceLeft = sourceRect.left(); + d.sourceTop = sourceRect.top(); + d.width = sourceMargins.left(); + d.height = sourceMargins.top(); + d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; + d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; if (hints & QDrawBorderPixmap::OpaqueTopLeft) opaqueData.append(d); else translucentData.append(d); } if (targetMargins.top() > 0 && targetMargins.right() > 0 && sourceMargins.top() > 0 && sourceMargins.right() > 0) { // top right - d.point.setX(0.5 * (xTarget[columns] + xTarget[columns - 1])); - d.point.setY(0.5 * (yTarget[1] + yTarget[0])); - d.source = QRectF(sourceCenterRight, sourceRect.top(), sourceMargins.right(), sourceMargins.top()); - d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.source.width(); - d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.source.height(); + d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); + d.y = (0.5 * (yTarget[1] + yTarget[0])); + d.sourceLeft = sourceCenterRight; + d.sourceTop = sourceRect.top(); + d.width = sourceMargins.right(); + d.height = sourceMargins.top(); + d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; + d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; if (hints & QDrawBorderPixmap::OpaqueTopRight) opaqueData.append(d); else translucentData.append(d); } if (targetMargins.bottom() > 0 && targetMargins.left() > 0 && sourceMargins.bottom() > 0 && sourceMargins.left() > 0) { // bottom left - d.point.setX(0.5 * (xTarget[1] + xTarget[0])); - d.point.setY(0.5 * (yTarget[rows] + yTarget[rows - 1])); - d.source = QRectF(sourceRect.left(), sourceCenterBottom, sourceMargins.left(), sourceMargins.bottom()); - d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.source.width(); - d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.source.height(); + d.x = (0.5 * (xTarget[1] + xTarget[0])); + d.y =(0.5 * (yTarget[rows] + yTarget[rows - 1])); + d.sourceLeft = sourceRect.left(); + d.sourceTop = sourceCenterBottom; + d.width = sourceMargins.left(); + d.height = sourceMargins.bottom(); + d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; + d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; if (hints & QDrawBorderPixmap::OpaqueBottomLeft) opaqueData.append(d); else translucentData.append(d); } if (targetMargins.bottom() > 0 && targetMargins.right() > 0 && sourceMargins.bottom() > 0 && sourceMargins.right() > 0) { // bottom right - d.point.setX(0.5 * (xTarget[columns] + xTarget[columns - 1])); - d.point.setY(0.5 * (yTarget[rows] + yTarget[rows - 1])); - d.source = QRectF(sourceCenterRight, sourceCenterBottom, sourceMargins.right(), sourceMargins.bottom()); - d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.source.width(); - d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.source.height(); + d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); + d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); + d.sourceLeft = sourceCenterRight; + d.sourceTop = sourceCenterBottom; + d.width = sourceMargins.right(); + d.height = sourceMargins.bottom(); + d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; + d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; if (hints & QDrawBorderPixmap::OpaqueBottomRight) opaqueData.append(d); else @@ -1229,158 +1251,110 @@ void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargin // horizontal edges if (targetCenterWidth > 0 && sourceCenterWidth > 0) { if (targetMargins.top() > 0 && sourceMargins.top() > 0) { // top - QDrawPixmapsDataArray &data = hints & QDrawBorderPixmap::OpaqueTop ? opaqueData : translucentData; - d.source = QRectF(sourceCenterLeft, sourceRect.top(), sourceCenterWidth, sourceMargins.top()); - d.point.setY(0.5 * (yTarget[1] + yTarget[0])); - d.scaleX = dx / d.source.width(); - d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.source.height(); + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueTop ? opaqueData : translucentData; + d.sourceLeft = sourceCenterLeft; + d.sourceTop = sourceRect.top(); + d.width = sourceCenterWidth; + d.height = sourceMargins.top(); + d.y = (0.5 * (yTarget[1] + yTarget[0])); + d.scaleX = dx / d.width; + d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; for (int i = 1; i < columns - 1; ++i) { - d.point.setX(0.5 * (xTarget[i + 1] + xTarget[i])); + d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); data.append(d); } if (rules.horizontal == Qt::RepeatTile) - data[data.size() - 1].source.setWidth((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); + data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); } if (targetMargins.bottom() > 0 && sourceMargins.bottom() > 0) { // bottom - QDrawPixmapsDataArray &data = hints & QDrawBorderPixmap::OpaqueBottom ? opaqueData : translucentData; - d.source = QRectF(sourceCenterLeft, sourceCenterBottom, sourceCenterWidth, sourceMargins.bottom());; - d.point.setY(0.5 * (yTarget[rows] + yTarget[rows - 1])); - d.scaleX = dx / d.source.width(); - d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.source.height(); + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueBottom ? opaqueData : translucentData; + d.sourceLeft = sourceCenterLeft; + d.sourceTop = sourceCenterBottom; + d.width = sourceCenterWidth; + d.height = sourceMargins.bottom(); + d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); + d.scaleX = dx / d.width; + d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; for (int i = 1; i < columns - 1; ++i) { - d.point.setX(0.5 * (xTarget[i + 1] + xTarget[i])); + d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); data.append(d); } if (rules.horizontal == Qt::RepeatTile) - data[data.size() - 1].source.setWidth((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); + data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); } } // vertical edges if (targetCenterHeight > 0 && sourceCenterHeight > 0) { if (targetMargins.left() > 0 && sourceMargins.left() > 0) { // left - QDrawPixmapsDataArray &data = hints & QDrawBorderPixmap::OpaqueLeft ? opaqueData : translucentData; - d.source = QRectF(sourceRect.left(), sourceCenterTop, sourceMargins.left(), sourceCenterHeight); - d.point.setX(0.5 * (xTarget[1] + xTarget[0])); - d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.source.width(); - d.scaleY = dy / d.source.height(); + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueLeft ? opaqueData : translucentData; + d.sourceLeft = sourceRect.left(); + d.sourceTop = sourceCenterTop; + d.width = sourceMargins.left(); + d.height = sourceCenterHeight; + d.x = (0.5 * (xTarget[1] + xTarget[0])); + d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; + d.scaleY = dy / d.height; for (int i = 1; i < rows - 1; ++i) { - d.point.setY(0.5 * (yTarget[i + 1] + yTarget[i])); + d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); data.append(d); } if (rules.vertical == Qt::RepeatTile) - data[data.size() - 1].source.setHeight((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); + data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); } if (targetMargins.right() > 0 && sourceMargins.right() > 0) { // right - QDrawPixmapsDataArray &data = hints & QDrawBorderPixmap::OpaqueRight ? opaqueData : translucentData; - d.source = QRectF(sourceCenterRight, sourceCenterTop, sourceMargins.right(), sourceCenterHeight); - d.point.setX(0.5 * (xTarget[columns] + xTarget[columns - 1])); - d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.source.width(); - d.scaleY = dy / d.source.height(); + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueRight ? opaqueData : translucentData; + d.sourceLeft = sourceCenterRight; + d.sourceTop = sourceCenterTop; + d.width = sourceMargins.right(); + d.height = sourceCenterHeight; + d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); + d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; + d.scaleY = dy / d.height; for (int i = 1; i < rows - 1; ++i) { - d.point.setY(0.5 * (yTarget[i + 1] + yTarget[i])); + d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); data.append(d); } if (rules.vertical == Qt::RepeatTile) - data[data.size() - 1].source.setHeight((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); + data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); } } // center if (targetCenterWidth > 0 && targetCenterHeight > 0 && sourceCenterWidth > 0 && sourceCenterHeight > 0) { - QDrawPixmapsDataArray &data = hints & QDrawBorderPixmap::OpaqueCenter ? opaqueData : translucentData; - d.source = QRectF(sourceCenterLeft, sourceCenterTop, sourceCenterWidth, sourceCenterHeight); - d.scaleX = dx / d.source.width(); - d.scaleY = dy / d.source.height(); + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueCenter ? opaqueData : translucentData; + d.sourceLeft = sourceCenterLeft; + d.sourceTop = sourceCenterTop; + d.width = sourceCenterWidth; + d.height = sourceCenterHeight; + d.scaleX = dx / d.width; + d.scaleY = dy / d.height; qreal repeatWidth = (xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX; qreal repeatHeight = (yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY; for (int j = 1; j < rows - 1; ++j) { - d.point.setY(0.5 * (yTarget[j + 1] + yTarget[j])); + d.y = (0.5 * (yTarget[j + 1] + yTarget[j])); for (int i = 1; i < columns - 1; ++i) { - d.point.setX(0.5 * (xTarget[i + 1] + xTarget[i])); + d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); data.append(d); } if (rules.horizontal == Qt::RepeatTile) - data[data.size() - 1].source.setWidth(repeatWidth); + data[data.size() - 1].width = repeatWidth; } if (rules.vertical == Qt::RepeatTile) { for (int i = 1; i < columns - 1; ++i) - data[data.size() - i].source.setHeight(repeatHeight); + data[data.size() - i].height = repeatHeight; } } if (opaqueData.size()) - qDrawPixmaps(painter, opaqueData.data(), opaqueData.size(), pixmap, QDrawPixmaps::OpaqueHint); + painter->drawPixmapFragments(opaqueData.data(), opaqueData.size(), pixmap, QPainter::OpaqueHint); if (translucentData.size()) - qDrawPixmaps(painter, translucentData.data(), translucentData.size(), pixmap); -} - -/*! - \class QDrawPixmaps::Data - \since 4.6 - \internal - - This structure is used with the qDrawPixmaps() function. - - QPointF point: Specifies the center of the target rectangle. - QRectF source: Specifies the source rectangle in the pixmap passed into the qDrawPixmaps() call. - qreal scaleX: Specifies the horizontal scale of the target rectangle. - qreal scaleY: Specifies the vertical scale of the target rectangle. - qreal rotation: Specifies the rotation of the target rectangle in degrees. - The target rectangle is rotated after scaling. - qreal opacity: Specifies the opacity of the rectangle. -*/ - -/*! - \enum QDrawPixmaps::DrawingHint - \internal -*/ + painter->drawPixmapFragments(translucentData.data(), translucentData.size(), pixmap); -/*! - \internal - \since 4.6 - - This function is used to draw \a pixmap, or a sub-rectangle of \a pixmap, at multiple positions - with different scale, rotation and opacity on \a painter. \a drawingData is an array of \a - dataCount elements specifying the parameters used to draw each pixmap instance. - This can be used for example to implement a particle system. -*/ -void qDrawPixmaps(QPainter *painter, const QDrawPixmaps::Data *drawingData, int dataCount, const QPixmap &pixmap, QDrawPixmaps::DrawingHints hints) -{ - QPaintEngine *engine = painter->paintEngine(); - if (!engine) - return; - - if (engine->isExtended()) { - static_cast<QPaintEngineEx *>(engine)->drawPixmaps(drawingData, dataCount, pixmap, hints); - } else { - qreal oldOpacity = painter->opacity(); - QTransform oldTransform = painter->transform(); - - for (int i = 0; i < dataCount; ++i) { - QTransform transform = oldTransform; - qreal xOffset = 0; - qreal yOffset = 0; - if (drawingData[i].rotation == 0) { - xOffset = drawingData[i].point.x(); - yOffset = drawingData[i].point.y(); - } else { - transform.translate(drawingData[i].point.x(), drawingData[i].point.y()); - transform.rotate(drawingData[i].rotation); - } - painter->setTransform(transform); - painter->setOpacity(oldOpacity * drawingData[i].opacity); - - qreal w = drawingData[i].scaleX * drawingData[i].source.width(); - qreal h = drawingData[i].scaleY * drawingData[i].source.height(); - painter->drawPixmap(QRectF(-0.5 * w + xOffset, -0.5 * h + yOffset, w, h), pixmap, drawingData[i].source); - } - - painter->setOpacity(oldOpacity); - painter->setTransform(oldTransform); - } + if (oldAA) + painter->setRenderHint(QPainter::Antialiasing, true); } QT_END_NAMESPACE diff --git a/src/gui/painting/qdrawutil.h b/src/gui/painting/qdrawutil.h index 2801b2f..31e352f 100644 --- a/src/gui/painting/qdrawutil.h +++ b/src/gui/painting/qdrawutil.h @@ -188,31 +188,6 @@ inline void qDrawBorderPixmap(QPainter *painter, qDrawBorderPixmap(painter, target, margins, pixmap, pixmap.rect(), margins); } -// For internal use only. -namespace QDrawPixmaps -{ - struct Data - { - QPointF point; - QRectF source; - qreal scaleX; - qreal scaleY; - qreal rotation; - qreal opacity; - }; - - enum DrawingHint - { - OpaqueHint = 0x01 - }; - - Q_DECLARE_FLAGS(DrawingHints, DrawingHint) -} - -// This function is private and may change without notice. Do not use outside Qt!!! -Q_GUI_EXPORT void qDrawPixmaps(QPainter *painter, const QDrawPixmaps::Data *drawingData, - int dataCount, const QPixmap &pixmap, QDrawPixmaps::DrawingHints hints = 0); - QT_END_NAMESPACE QT_END_HEADER diff --git a/src/gui/painting/qemulationpaintengine.cpp b/src/gui/painting/qemulationpaintengine.cpp index f2f0c73..0510b10 100644 --- a/src/gui/painting/qemulationpaintengine.cpp +++ b/src/gui/painting/qemulationpaintengine.cpp @@ -172,9 +172,44 @@ void QEmulationPaintEngine::drawTextItem(const QPointF &p, const QTextItem &text QRectF rect(p.x(), p.y() - ti.ascent.toReal(), ti.width.toReal(), (ti.ascent + ti.descent + 1).toReal()); fillBGRect(rect); } + + QPainterState *s = state(); + Qt::BrushStyle style = qbrush_style(s->pen.brush()); + if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) + { + QPen savedPen = s->pen; + QGradient g = *s->pen.brush().gradient(); + + if (g.coordinateMode() > QGradient::LogicalMode) { + QTransform mat = s->pen.brush().transform(); + if (g.coordinateMode() == QGradient::StretchToDeviceMode) { + mat.scale(real_engine->painter()->device()->width(), real_engine->painter()->device()->height()); + } else if (g.coordinateMode() == QGradient::ObjectBoundingMode) { + const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); + QRectF r(p.x(), p.y() - ti.ascent.toReal(), ti.width.toReal(), (ti.ascent + ti.descent + 1).toReal()); + mat.translate(r.x(), r.y()); + mat.scale(r.width(), r.height()); + } + g.setCoordinateMode(QGradient::LogicalMode); + QBrush brush(g); + brush.setTransform(mat); + s->pen.setBrush(brush); + penChanged(); + real_engine->drawTextItem(p, textItem); + s->pen = savedPen; + penChanged(); + return; + } + } + real_engine->drawTextItem(p, textItem); } +void QEmulationPaintEngine::drawStaticTextItem(QStaticTextItem *item) +{ + real_engine->drawStaticTextItem(item); +} + void QEmulationPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s) { if (state()->bgMode == Qt::OpaqueMode && pixmap.isQBitmap()) diff --git a/src/gui/painting/qemulationpaintengine_p.h b/src/gui/painting/qemulationpaintengine_p.h index 0ed641b..5835f10 100644 --- a/src/gui/painting/qemulationpaintengine_p.h +++ b/src/gui/painting/qemulationpaintengine_p.h @@ -78,6 +78,7 @@ public: virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); virtual void drawTextItem(const QPointF &p, const QTextItem &textItem); + virtual void drawStaticTextItem(QStaticTextItem *item); virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s); virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags); diff --git a/src/gui/painting/qgraphicssystem.cpp b/src/gui/painting/qgraphicssystem.cpp index 2ea3d33..f06e309 100644 --- a/src/gui/painting/qgraphicssystem.cpp +++ b/src/gui/painting/qgraphicssystem.cpp @@ -50,7 +50,7 @@ #ifdef Q_WS_MAC # include <private/qpixmap_mac_p.h> #endif -#ifdef Q_WS_S60 +#ifdef Q_OS_SYMBIAN # include <private/qpixmap_s60_p.h> #endif @@ -71,7 +71,7 @@ QPixmapData *QGraphicsSystem::createDefaultPixmapData(QPixmapData::PixelType typ return new QRasterPixmapData(type); #elif defined(Q_WS_MAC) return new QMacPixmapData(type); -#elif defined(Q_WS_S60) +#elif defined(Q_OS_SYMBIAN) return new QS60PixmapData(type); #elif !defined(Q_WS_QWS) #error QGraphicsSystem::createDefaultPixmapData() not implemented @@ -79,5 +79,9 @@ QPixmapData *QGraphicsSystem::createDefaultPixmapData(QPixmapData::PixelType typ return 0; } +QPixmapData *QGraphicsSystem::createPixmapData(QPixmapData *origin) +{ + return createPixmapData(origin->pixelType()); +} QT_END_NAMESPACE diff --git a/src/gui/painting/qgraphicssystem_p.h b/src/gui/painting/qgraphicssystem_p.h index ddca788..1211505 100644 --- a/src/gui/painting/qgraphicssystem_p.h +++ b/src/gui/painting/qgraphicssystem_p.h @@ -64,6 +64,7 @@ class Q_GUI_EXPORT QGraphicsSystem { public: virtual QPixmapData *createPixmapData(QPixmapData::PixelType type) const = 0; + virtual QPixmapData *createPixmapData(QPixmapData *origin); virtual QWindowSurface *createWindowSurface(QWidget *widget) const = 0; virtual ~QGraphicsSystem() = 0; diff --git a/src/gui/painting/qgraphicssystem_qws.cpp b/src/gui/painting/qgraphicssystem_qws.cpp index f5dab4b..03a0d11 100644 --- a/src/gui/painting/qgraphicssystem_qws.cpp +++ b/src/gui/painting/qgraphicssystem_qws.cpp @@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE QPixmapData *QWSGraphicsSystem::createPixmapData(QPixmapData::PixelType type) const { if (screen->pixmapDataFactory()) - return screen->pixmapDataFactory()->create(type); //### For 4.4 compatability + return screen->pixmapDataFactory()->create(type); //### For 4.4 compatibility else return new QRasterPixmapData(type); } diff --git a/src/gui/painting/qgraphicssystem_runtime.cpp b/src/gui/painting/qgraphicssystem_runtime.cpp new file mode 100644 index 0000000..a9fbbee --- /dev/null +++ b/src/gui/painting/qgraphicssystem_runtime.cpp @@ -0,0 +1,416 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include <private/qgraphicssystem_runtime_p.h> +#include <private/qgraphicssystem_raster_p.h> +#include <private/qgraphicssystemfactory_p.h> +#include <private/qapplication_p.h> +#include <private/qwidget_p.h> +#include <QtCore/QDebug> +#include <QtCore/QTimer> +#include <QtGui/QBitmap> + +QT_BEGIN_NAMESPACE + +static int qt_pixmap_serial = 0; + +#define READBACK(f) \ + f \ + readBackInfo(); + + +class QDeferredGraphicsSystemChange : public QObject +{ + Q_OBJECT + +public: + QDeferredGraphicsSystemChange(QRuntimeGraphicsSystem *gs, const QString& graphicsSystemName) + : m_graphicsSystem(gs), m_graphicsSystemName(graphicsSystemName) + { + } + + void launch() + { + QTimer::singleShot(0, this, SLOT(doChange())); + } + +private slots: + + void doChange() + { + m_graphicsSystem->setGraphicsSystem(m_graphicsSystemName); + deleteLater(); + } + +private: + + QRuntimeGraphicsSystem *m_graphicsSystem; + QString m_graphicsSystemName; +}; + +QRuntimePixmapData::QRuntimePixmapData(const QRuntimeGraphicsSystem *gs, PixelType type) + : QPixmapData(type, RuntimeClass), m_graphicsSystem(gs) +{ + setSerialNumber(++qt_pixmap_serial); +} + +QRuntimePixmapData::~QRuntimePixmapData() +{ + if (QApplicationPrivate::graphics_system) + m_graphicsSystem->removePixmapData(this); + delete m_data; +} + +void QRuntimePixmapData::readBackInfo() +{ + w = m_data->width(); + h = m_data->height(); + d = m_data->depth(); + is_null = m_data->isNull(); +} + + +QPixmapData *QRuntimePixmapData::createCompatiblePixmapData() const +{ + QRuntimePixmapData *rtData = new QRuntimePixmapData(m_graphicsSystem, pixelType()); + rtData->m_data = m_data->createCompatiblePixmapData(); + return rtData; +} + + +void QRuntimePixmapData::resize(int width, int height) +{ + READBACK( + m_data->resize(width, height); + ) +} + + +void QRuntimePixmapData::fromImage(const QImage &image, + Qt::ImageConversionFlags flags) +{ + READBACK( + m_data->fromImage(image, flags); + ) +} + + +bool QRuntimePixmapData::fromFile(const QString &filename, const char *format, + Qt::ImageConversionFlags flags) +{ + bool success(false); + READBACK( + success = m_data->fromFile(filename, format, flags); + ) + return success; +} + +bool QRuntimePixmapData::fromData(const uchar *buffer, uint len, const char *format, + Qt::ImageConversionFlags flags) +{ + bool success(false); + READBACK( + success = m_data->fromData(buffer, len, format, flags); + ) + return success; +} + + +void QRuntimePixmapData::copy(const QPixmapData *data, const QRect &rect) +{ + if (data->runtimeData()) { + READBACK( + m_data->copy(data->runtimeData(), rect); + ) + } else { + READBACK( + m_data->copy(data, rect); + ) + } +} + +bool QRuntimePixmapData::scroll(int dx, int dy, const QRect &rect) +{ + return m_data->scroll(dx, dy, rect); +} + + +int QRuntimePixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const +{ + return m_data->metric(metric); +} + +void QRuntimePixmapData::fill(const QColor &color) +{ + return m_data->fill(color); +} + +QBitmap QRuntimePixmapData::mask() const +{ + return m_data->mask(); +} + +void QRuntimePixmapData::setMask(const QBitmap &mask) +{ + READBACK( + m_data->setMask(mask); + ) +} + +bool QRuntimePixmapData::hasAlphaChannel() const +{ + return m_data->hasAlphaChannel(); +} + +QPixmap QRuntimePixmapData::transformed(const QTransform &matrix, + Qt::TransformationMode mode) const +{ + return m_data->transformed(matrix, mode); +} + +void QRuntimePixmapData::setAlphaChannel(const QPixmap &alphaChannel) +{ + READBACK( + m_data->setAlphaChannel(alphaChannel); + ) +} + +QPixmap QRuntimePixmapData::alphaChannel() const +{ + return m_data->alphaChannel(); +} + +QImage QRuntimePixmapData::toImage() const +{ + return m_data->toImage(); +} + +QPaintEngine* QRuntimePixmapData::paintEngine() const +{ + return m_data->paintEngine(); +} + +QImage* QRuntimePixmapData::buffer() +{ + return m_data->buffer(); +} + +#if defined(Q_OS_SYMBIAN) +void* QRuntimePixmapData::toNativeType(NativeType type) +{ + return m_data->toNativeType(type); +} + +void QRuntimePixmapData::fromNativeType(void *pixmap, NativeType type) +{ + m_data->fromNativeType(pixmap, type); + readBackInfo(); +} +#endif + +QPixmapData* QRuntimePixmapData::runtimeData() const +{ + return m_data; +} + +QRuntimeWindowSurface::QRuntimeWindowSurface(const QRuntimeGraphicsSystem *gs, QWidget *window) + : QWindowSurface(window), m_graphicsSystem(gs) +{ + +} + +QRuntimeWindowSurface::~QRuntimeWindowSurface() +{ + if (QApplicationPrivate::graphics_system) + m_graphicsSystem->removeWindowSurface(this); +} + +QPaintDevice *QRuntimeWindowSurface::paintDevice() +{ + return m_windowSurface->paintDevice(); +} + +void QRuntimeWindowSurface::flush(QWidget *widget, const QRegion ®ion, + const QPoint &offset) +{ + m_windowSurface->flush(widget, region, offset); + + int destroyPolicy = m_graphicsSystem->windowSurfaceDestroyPolicy(); + if(m_pendingWindowSurface && + destroyPolicy == QRuntimeGraphicsSystem::DestroyAfterFirstFlush) { +#ifdef QT_DEBUG + qDebug() << "QRuntimeWindowSurface::flush() - destroy pending window surface"; +#endif + m_pendingWindowSurface.reset(); + } +} + +void QRuntimeWindowSurface::setGeometry(const QRect &rect) +{ + m_windowSurface->setGeometry(rect); +} + +bool QRuntimeWindowSurface::scroll(const QRegion &area, int dx, int dy) +{ + return m_windowSurface->scroll(area, dx, dy); +} + +void QRuntimeWindowSurface::beginPaint(const QRegion &rgn) +{ + m_windowSurface->beginPaint(rgn); +} + +void QRuntimeWindowSurface::endPaint(const QRegion &rgn) +{ + m_windowSurface->endPaint(rgn); +} + +QImage* QRuntimeWindowSurface::buffer(const QWidget *widget) +{ + return m_windowSurface->buffer(widget); +} + +QPixmap QRuntimeWindowSurface::grabWidget(const QWidget *widget, const QRect& rectangle) const +{ + return m_windowSurface->grabWidget(widget, rectangle); +} + +QPoint QRuntimeWindowSurface::offset(const QWidget *widget) const +{ + return m_windowSurface->offset(widget); +} + +QRuntimeGraphicsSystem::QRuntimeGraphicsSystem() + : m_windowSurfaceDestroyPolicy(DestroyImmediately), + m_graphicsSystem(0) +{ + QApplicationPrivate::graphics_system_name = QLatin1String("runtime"); + QApplicationPrivate::runtime_graphics_system = true; + +#ifdef QT_DEFAULT_RUNTIME_SYSTEM + m_graphicsSystemName = QLatin1String(QT_DEFAULT_RUNTIME_SYSTEM); + if (m_graphicsSystemName.isNull()) +#endif + m_graphicsSystemName = QLatin1String("raster"); + +#ifdef Q_OS_SYMBIAN + m_windowSurfaceDestroyPolicy = DestroyAfterFirstFlush; +#endif + + m_graphicsSystem = QGraphicsSystemFactory::create(m_graphicsSystemName); +} + + +QPixmapData *QRuntimeGraphicsSystem::createPixmapData(QPixmapData::PixelType type) const +{ + Q_ASSERT(m_graphicsSystem); + QPixmapData *data = m_graphicsSystem->createPixmapData(type); + + QRuntimePixmapData *rtData = new QRuntimePixmapData(this, type); + rtData->m_data = data; + m_pixmapDatas << rtData; + + return rtData; +} + +QWindowSurface *QRuntimeGraphicsSystem::createWindowSurface(QWidget *widget) const +{ + Q_ASSERT(m_graphicsSystem); + QRuntimeWindowSurface *rtSurface = new QRuntimeWindowSurface(this, widget); + rtSurface->m_windowSurface.reset(m_graphicsSystem->createWindowSurface(widget)); + widget->setWindowSurface(rtSurface); + m_windowSurfaces << rtSurface; + return rtSurface; +} + +void QRuntimeGraphicsSystem::setGraphicsSystem(const QString &name) +{ + if (m_graphicsSystemName == name) + return; +#ifdef QT_DEBUG + qDebug() << "QRuntimeGraphicsSystem::setGraphicsSystem( " << name << " )"; +#endif + QGraphicsSystem *oldSystem = m_graphicsSystem; + m_graphicsSystem = QGraphicsSystemFactory::create(name); + m_graphicsSystemName = name; + + Q_ASSERT(m_graphicsSystem); + + m_pendingGraphicsSystemName = QString(); + + for (int i = 0; i < m_pixmapDatas.size(); ++i) { + QRuntimePixmapData *proxy = m_pixmapDatas.at(i); + QPixmapData *newData = m_graphicsSystem->createPixmapData(proxy->m_data); + newData->fromImage(proxy->m_data->toImage(), Qt::NoOpaqueDetection); + delete proxy->m_data; + proxy->m_data = newData; + proxy->readBackInfo(); + } + + for (int i = 0; i < m_windowSurfaces.size(); ++i) { + QRuntimeWindowSurface *proxy = m_windowSurfaces.at(i); + QWidget *widget = proxy->m_windowSurface->window(); + + if(m_windowSurfaceDestroyPolicy == DestroyAfterFirstFlush) + proxy->m_pendingWindowSurface.reset(proxy->m_windowSurface.take()); + + proxy->m_windowSurface.reset(m_graphicsSystem->createWindowSurface(widget)); + qt_widget_private(widget)->invalidateBuffer(widget->rect()); + } + + delete oldSystem; +} + +void QRuntimeGraphicsSystem::removePixmapData(QRuntimePixmapData *pixmapData) const +{ + int index = m_pixmapDatas.lastIndexOf(pixmapData); + m_pixmapDatas.removeAt(index); +} + +void QRuntimeGraphicsSystem::removeWindowSurface(QRuntimeWindowSurface *windowSurface) const +{ + int index = m_windowSurfaces.lastIndexOf(windowSurface); + m_windowSurfaces.removeAt(index); +} + +#include "qgraphicssystem_runtime.moc" + +QT_END_NAMESPACE diff --git a/src/gui/painting/qgraphicssystem_runtime_p.h b/src/gui/painting/qgraphicssystem_runtime_p.h new file mode 100644 index 0000000..421fbeb --- /dev/null +++ b/src/gui/painting/qgraphicssystem_runtime_p.h @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** 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 plugins 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 QGRAPHICSSYSTEM_RUNTIME_P_H +#define QGRAPHICSSYSTEM_RUNTIME_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgraphicssystem_p.h" + +#include <private/qpixmapdata_p.h> + +QT_BEGIN_NAMESPACE + +class QRuntimeGraphicsSystem; + +class Q_GUI_EXPORT QRuntimePixmapData : public QPixmapData { +public: + QRuntimePixmapData(const QRuntimeGraphicsSystem *gs, PixelType type); + ~QRuntimePixmapData(); + + virtual QPixmapData *createCompatiblePixmapData() const; + virtual void resize(int width, int height); + virtual void fromImage(const QImage &image, + Qt::ImageConversionFlags flags); + + virtual bool fromFile(const QString &filename, const char *format, + Qt::ImageConversionFlags flags); + virtual bool fromData(const uchar *buffer, uint len, const char *format, + Qt::ImageConversionFlags flags); + + virtual void copy(const QPixmapData *data, const QRect &rect); + virtual bool scroll(int dx, int dy, const QRect &rect); + + virtual int metric(QPaintDevice::PaintDeviceMetric metric) const; + virtual void fill(const QColor &color); + virtual QBitmap mask() const; + virtual void setMask(const QBitmap &mask); + virtual bool hasAlphaChannel() const; + virtual QPixmap transformed(const QTransform &matrix, + Qt::TransformationMode mode) const; + virtual void setAlphaChannel(const QPixmap &alphaChannel); + virtual QPixmap alphaChannel() const; + virtual QImage toImage() const; + virtual QPaintEngine *paintEngine() const; + + virtual QImage *buffer(); + + void readBackInfo(); + + QPixmapData *m_data; + +#if defined(Q_OS_SYMBIAN) + void* toNativeType(NativeType type); + void fromNativeType(void* pixmap, NativeType type); +#endif + + virtual QPixmapData *runtimeData() const; + +private: + const QRuntimeGraphicsSystem *m_graphicsSystem; + +}; + +class QRuntimeWindowSurface : public QWindowSurface { +public: + QRuntimeWindowSurface(const QRuntimeGraphicsSystem *gs, QWidget *window); + ~QRuntimeWindowSurface(); + + virtual QPaintDevice *paintDevice(); + virtual void flush(QWidget *widget, const QRegion ®ion, + const QPoint &offset); + virtual void setGeometry(const QRect &rect); + + virtual bool scroll(const QRegion &area, int dx, int dy); + + virtual void beginPaint(const QRegion &); + virtual void endPaint(const QRegion &); + + virtual QImage* buffer(const QWidget *widget); + virtual QPixmap grabWidget(const QWidget *widget, const QRect& rectangle = QRect()) const; + + virtual QPoint offset(const QWidget *widget) const; + + QScopedPointer<QWindowSurface> m_windowSurface; + QScopedPointer<QWindowSurface> m_pendingWindowSurface; + +private: + const QRuntimeGraphicsSystem *m_graphicsSystem; +}; + +class QRuntimeGraphicsSystem : public QGraphicsSystem +{ +public: + + enum WindowSurfaceDestroyPolicy + { + DestroyImmediately, + DestroyAfterFirstFlush + }; + +public: + QRuntimeGraphicsSystem(); + + QPixmapData *createPixmapData(QPixmapData::PixelType type) const; + QWindowSurface *createWindowSurface(QWidget *widget) const; + + void removePixmapData(QRuntimePixmapData *pixmapData) const; + void removeWindowSurface(QRuntimeWindowSurface *windowSurface) const; + + void setGraphicsSystem(const QString &name); + QString graphicsSystemName() const { return m_graphicsSystemName; } + + void setWindowSurfaceDestroyPolicy(WindowSurfaceDestroyPolicy policy) + { + m_windowSurfaceDestroyPolicy = policy; + } + + int windowSurfaceDestroyPolicy() const { return m_windowSurfaceDestroyPolicy; } + + +private: + int m_windowSurfaceDestroyPolicy; + QGraphicsSystem *m_graphicsSystem; + mutable QList<QRuntimePixmapData *> m_pixmapDatas; + mutable QList<QRuntimeWindowSurface *> m_windowSurfaces; + QString m_graphicsSystemName; + + QString m_pendingGraphicsSystemName; + + friend class QRuntimePixmapData; + friend class QRuntimeWindowSurface; + friend class QMeeGoGraphicsSystem; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/painting/qgraphicssystemfactory.cpp b/src/gui/painting/qgraphicssystemfactory.cpp index 29f24a3..ee6fbd8 100644 --- a/src/gui/painting/qgraphicssystemfactory.cpp +++ b/src/gui/painting/qgraphicssystemfactory.cpp @@ -46,11 +46,12 @@ #include "qapplication.h" #include "qgraphicssystem_raster_p.h" +#include "qgraphicssystem_runtime_p.h" #include "qdebug.h" QT_BEGIN_NAMESPACE -#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) +#ifndef QT_NO_LIBRARY Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, (QGraphicsSystemFactoryInterface_iid, QLatin1String("/graphicssystems"), Qt::CaseInsensitive)) #endif @@ -68,6 +69,10 @@ QGraphicsSystem *QGraphicsSystemFactory::create(const QString& key) if (system.isEmpty()) { system = QLatin1String("openvg"); } +#elif defined (QT_GRAPHICSSYSTEM_RUNTIME) + if (system.isEmpty()) { + system = QLatin1String("runtime"); + } #elif defined (QT_GRAPHICSSYSTEM_RASTER) && !defined(Q_WS_WIN) && !defined(Q_OS_SYMBIAN) if (system.isEmpty()) { system = QLatin1String("raster"); @@ -76,10 +81,12 @@ QGraphicsSystem *QGraphicsSystemFactory::create(const QString& key) if (system == QLatin1String("raster")) return new QRasterGraphicsSystem; + else if (system == QLatin1String("runtime")) + return new QRuntimeGraphicsSystem; else if (system.isEmpty() || system == QLatin1String("native")) return 0; -#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) +#ifndef QT_NO_LIBRARY if (!ret) { if (QGraphicsSystemFactoryInterface *factory = qobject_cast<QGraphicsSystemFactoryInterface*>(loader()->instance(system))) ret = factory->create(system); @@ -100,7 +107,7 @@ QGraphicsSystem *QGraphicsSystemFactory::create(const QString& key) */ QStringList QGraphicsSystemFactory::keys() { -#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) +#ifndef QT_NO_LIBRARY QStringList list = loader()->keys(); #else QStringList list; diff --git a/src/gui/painting/qgrayraster.c b/src/gui/painting/qgrayraster.c index ff2469c..ec9ebeb 100644 --- a/src/gui/painting/qgrayraster.c +++ b/src/gui/painting/qgrayraster.c @@ -156,6 +156,7 @@ #define ErrRaster_Invalid_Outline -1 #define ErrRaster_Invalid_Argument -3 #define ErrRaster_Memory_Overflow -4 +#define ErrRaster_OutOfMemory -6 #define QT_FT_BEGIN_HEADER #define QT_FT_END_HEADER @@ -222,7 +223,6 @@ #define DOWNSCALE( x ) ( (x) << ( 6 - PIXEL_BITS ) ) #endif - /*************************************************************************/ /* */ /* TYPE DEFINITIONS */ @@ -233,7 +233,7 @@ /* new algorithms */ typedef int TCoord; /* integer scanline/pixel coordinate */ - typedef long TPos; /* sub-pixel coordinate */ + typedef int TPos; /* sub-pixel coordinate */ /* determine the type used to store cell areas. This normally takes at */ /* least PIXEL_BITS*2 + 1 bits. On 16-bit systems, we need to use */ @@ -317,6 +317,7 @@ PCell* ycells; int ycount; + int skip_spans; } TWorker, *PWorker; @@ -324,13 +325,19 @@ { void* buffer; long buffer_size; + long buffer_allocated_size; int band_size; void* memory; PWorker worker; } TRaster, *PRaster; - + int q_gray_rendered_spans(TRaster *raster) + { + if ( raster && raster->worker ) + return raster->worker->skip_spans > 0 ? 0 : -raster->worker->skip_spans; + return 0; + } /*************************************************************************/ /* */ @@ -538,7 +545,7 @@ TCoord y2 ) { TCoord ex1, ex2, fx1, fx2, delta; - long p, first, dx; + int p, first, dx; int incr, lift, mod, rem; @@ -643,7 +650,7 @@ { TCoord ey1, ey2, fy1, fy2; TPos dx, dy, x, x2; - long p, first; + int p, first; int delta, rem, mod, lift, incr; @@ -956,53 +963,49 @@ const QT_FT_Vector* control2, const QT_FT_Vector* to ) { - TPos dx, dy, da, db; int top, level; int* levels; QT_FT_Vector* arc; + int mid_x = ( DOWNSCALE( ras.x ) + to->x + + 3 * (control1->x + control2->x ) ) / 8; + int mid_y = ( DOWNSCALE( ras.y ) + to->y + + 3 * (control1->y + control2->y ) ) / 8; + TPos dx = DOWNSCALE( ras.x ) + to->x - ( mid_x << 1 ); + TPos dy = DOWNSCALE( ras.y ) + to->y - ( mid_y << 1 ); - dx = DOWNSCALE( ras.x ) + to->x - ( control1->x << 1 ); - if ( dx < 0 ) - dx = -dx; - dy = DOWNSCALE( ras.y ) + to->y - ( control1->y << 1 ); - if ( dy < 0 ) - dy = -dy; - if ( dx < dy ) - dx = dy; - da = dx; - - dx = DOWNSCALE( ras.x ) + to->x - 3 * ( control1->x + control2->x ); if ( dx < 0 ) dx = -dx; - dy = DOWNSCALE( ras.y ) + to->y - 3 * ( control1->x + control2->y ); if ( dy < 0 ) dy = -dy; if ( dx < dy ) dx = dy; - db = dx; level = 1; - da = da / ras.cubic_level; - db = db / ras.conic_level; - while ( da > 0 || db > 0 ) + dx /= ras.cubic_level; + while ( dx > 0 ) { - da >>= 2; - db >>= 3; + dx >>= 2; level++; } if ( level <= 1 ) { - TPos to_x, to_y, mid_x, mid_y; + TPos to_x, to_y; to_x = UPSCALE( to->x ); to_y = UPSCALE( to->y ); + + /* Recalculation of midpoint is needed only if */ + /* UPSCALE and DOWNSCALE have any effect. */ + +#if ( PIXEL_BITS != 6 ) mid_x = ( ras.x + to_x + 3 * UPSCALE( control1->x + control2->x ) ) / 8; mid_y = ( ras.y + to_y + 3 * UPSCALE( control1->y + control2->y ) ) / 8; +#endif gray_render_line( RAS_VAR_ mid_x, mid_y ); gray_render_line( RAS_VAR_ to_x, to_y ); @@ -1182,6 +1185,7 @@ { QT_FT_Span* span; int coverage; + int skip; /* compute the coverage line's coverage, depending on the */ @@ -1232,9 +1236,16 @@ if ( ras.num_gray_spans >= QT_FT_MAX_GRAY_SPANS ) { - if ( ras.render_span ) - ras.render_span( ras.num_gray_spans, ras.gray_spans, + if ( ras.render_span && ras.num_gray_spans > ras.skip_spans ) + { + skip = ras.skip_spans > 0 ? ras.skip_spans : 0; + ras.render_span( ras.num_gray_spans - skip, + ras.gray_spans + skip, ras.render_span_data ); + } + + ras.skip_spans -= ras.num_gray_spans; + /* ras.render_span( span->y, ras.gray_spans, count ); */ #ifdef DEBUG_GRAYS @@ -1359,10 +1370,6 @@ /* <Input> */ /* outline :: A pointer to the source target. */ /* */ - /* func_interface :: A table of `emitters', i.e,. function pointers */ - /* called during decomposition to indicate path */ - /* operations. */ - /* */ /* user :: A typeless pointer which is passed to each */ /* emitter during the decomposition. It can be */ /* used to store the state during the */ @@ -1373,15 +1380,10 @@ /* */ static int QT_FT_Outline_Decompose( const QT_FT_Outline* outline, - const QT_FT_Outline_Funcs* func_interface, void* user ) { #undef SCALED -#if 0 -#define SCALED( x ) ( ( (x) << shift ) - delta ) -#else #define SCALED( x ) (x) -#endif QT_FT_Vector v_last; QT_FT_Vector v_control; @@ -1396,12 +1398,6 @@ int error; char tag; /* current point's state */ -#if 0 - int shift = func_interface->shift; - TPos delta = func_interface->delta; -#endif - - first = 0; for ( n = 0; n < outline->n_contours; n++ ) @@ -1455,7 +1451,7 @@ tags--; } - error = func_interface->move_to( &v_start, user ); + error = gray_move_to( &v_start, user ); if ( error ) goto Exit; @@ -1475,7 +1471,7 @@ vec.x = SCALED( point->x ); vec.y = SCALED( point->y ); - error = func_interface->line_to( &vec, user ); + error = gray_line_to( &vec, user ); if ( error ) goto Exit; continue; @@ -1502,7 +1498,7 @@ if ( tag == QT_FT_CURVE_TAG_ON ) { - error = func_interface->conic_to( &v_control, &vec, + error = gray_conic_to( &v_control, &vec, user ); if ( error ) goto Exit; @@ -1515,7 +1511,7 @@ v_middle.x = ( v_control.x + vec.x ) / 2; v_middle.y = ( v_control.y + vec.y ) / 2; - error = func_interface->conic_to( &v_control, &v_middle, + error = gray_conic_to( &v_control, &v_middle, user ); if ( error ) goto Exit; @@ -1524,7 +1520,7 @@ goto Do_Conic; } - error = func_interface->conic_to( &v_control, &v_start, + error = gray_conic_to( &v_control, &v_start, user ); goto Close; } @@ -1555,20 +1551,20 @@ vec.x = SCALED( point->x ); vec.y = SCALED( point->y ); - error = func_interface->cubic_to( &vec1, &vec2, &vec, user ); + error = gray_cubic_to( &vec1, &vec2, &vec, user ); if ( error ) goto Exit; continue; } - error = func_interface->cubic_to( &vec1, &vec2, &v_start, user ); + error = gray_cubic_to( &vec1, &vec2, &v_start, user ); goto Close; } } } /* close the contour with a line segment */ - error = func_interface->line_to( &v_start, user ); + error = gray_line_to( &v_start, user ); Close: if ( error ) @@ -1596,22 +1592,11 @@ static int gray_convert_glyph_inner( RAS_ARG ) { - static - const QT_FT_Outline_Funcs func_interface = - { - (QT_FT_Outline_MoveTo_Func) gray_move_to, - (QT_FT_Outline_LineTo_Func) gray_line_to, - (QT_FT_Outline_ConicTo_Func)gray_conic_to, - (QT_FT_Outline_CubicTo_Func)gray_cubic_to, - 0, - 0 - }; - volatile int error = 0; if ( qt_ft_setjmp( ras.jump_buffer ) == 0 ) { - error = QT_FT_Outline_Decompose( &ras.outline, &func_interface, &ras ); + error = QT_FT_Outline_Decompose( &ras.outline, &ras ); gray_record_cell( RAS_VAR ); } else @@ -1630,7 +1615,8 @@ TBand* volatile band; int volatile n, num_bands; TPos volatile min, max, max_y; - QT_FT_BBox* clip; + QT_FT_BBox* clip; + int skip; ras.num_gray_spans = 0; @@ -1700,7 +1686,7 @@ { PCell cells_max; int yindex; - long cell_start, cell_end, cell_mod; + int cell_start, cell_end, cell_mod; ras.ycells = (PCell*)ras.buffer; @@ -1757,8 +1743,7 @@ #ifdef DEBUG_GRAYS fprintf( stderr, "Rotten glyph!\n" ); #endif - /* == Raster_Err_OutOfMemory in qblackraster.c */ - return -6; + return ErrRaster_OutOfMemory; } if ( bottom-top >= ras.band_size ) @@ -1772,9 +1757,15 @@ } } - if ( ras.render_span && ras.num_gray_spans > 0 ) - ras.render_span( ras.num_gray_spans, - ras.gray_spans, ras.render_span_data ); + if ( ras.render_span && ras.num_gray_spans > ras.skip_spans ) + { + skip = ras.skip_spans > 0 ? ras.skip_spans : 0; + ras.render_span( ras.num_gray_spans - skip, + ras.gray_spans + skip, + ras.render_span_data ); + } + + ras.skip_spans -= ras.num_gray_spans; if ( ras.band_shoot > 8 && ras.band_size > 16 ) ras.band_size = ras.band_size / 2; @@ -1784,7 +1775,7 @@ static int - gray_raster_render( PRaster raster, + gray_raster_render( QT_FT_Raster raster, const QT_FT_Raster_Params* params ) { const QT_FT_Outline* outline = (const QT_FT_Outline*)params->source; @@ -1795,6 +1786,15 @@ if ( !raster || !raster->buffer || !raster->buffer_size ) return ErrRaster_Invalid_Argument; + if ( raster->worker ) + raster->worker->skip_spans = params->skip_spans; + + // If raster object and raster buffer are allocated, but + // raster size isn't of the minimum size, indicate out of + // memory. + if (raster->buffer_allocated_size < MINIMUM_POOL_SIZE ) + return ErrRaster_OutOfMemory; + /* return immediately if the outline is empty */ if ( outline->n_points == 0 || outline->n_contours <= 0 ) return 0; @@ -1874,19 +1874,15 @@ /**** a static object. *****/ static int - gray_raster_new( void * memory, - QT_FT_Raster* araster ) + gray_raster_new( QT_FT_Raster* araster ) { - if (memory) - fprintf(stderr, "gray_raster_new(), memory ignored"); - memory = malloc(sizeof(TRaster)); - if (!memory) { + *araster = malloc(sizeof(TRaster)); + if (!*araster) { *araster = 0; return ErrRaster_Memory_Overflow; } - QT_FT_MEM_ZERO(memory, sizeof(TRaster)); + QT_FT_MEM_ZERO(*araster, sizeof(TRaster)); - *araster = (QT_FT_Raster) memory; return 0; } @@ -1905,10 +1901,9 @@ { PRaster rast = (PRaster)raster; - if ( raster ) { - if ( pool_base && pool_size >= (long)sizeof ( TWorker ) + 2048 ) + if ( pool_base && ( pool_size >= MINIMUM_POOL_SIZE ) ) { PWorker worker = (PWorker)pool_base; @@ -1923,12 +1918,20 @@ rast->band_size = (int)( rast->buffer_size / ( sizeof ( TCell ) * 8 ) ); } + else if ( pool_base) + { // Case when there is a raster pool allocated, but it + // doesn't have the minimum size (and so memory will be reallocated) + rast->buffer = pool_base; + rast->worker = NULL; + rast->buffer_size = pool_size; + } else { rast->buffer = NULL; rast->buffer_size = 0; rast->worker = NULL; } + rast->buffer_allocated_size = pool_size; } } diff --git a/src/gui/painting/qgrayraster_p.h b/src/gui/painting/qgrayraster_p.h index 4463fc9..ad595b8 100644 --- a/src/gui/painting/qgrayraster_p.h +++ b/src/gui/painting/qgrayraster_p.h @@ -89,6 +89,10 @@ #define QT_FT_EXPORT_VAR( x ) extern x #endif +/* Minimum buffer size for raster object, that accounts + for TWorker and TCell sizes.*/ +#define MINIMUM_POOL_SIZE 4096 + QT_FT_EXPORT_VAR( const QT_FT_Raster_Funcs ) qt_ft_grays_raster; diff --git a/src/gui/painting/qimagescale.cpp b/src/gui/painting/qimagescale.cpp index 8580775..7383308 100644 --- a/src/gui/painting/qimagescale.cpp +++ b/src/gui/painting/qimagescale.cpp @@ -304,7 +304,7 @@ QImageScaleInfo* QImageScale::qimageCalcScaleInfo(const QImage &img, return(isi); } -/* FIXME: NEED to optimise ScaleAARGBA - currently its "ok" but needs work*/ +/* FIXME: NEED to optimize ScaleAARGBA - currently its "ok" but needs work*/ /* scale by area sampling */ static void qt_qimageScaleAARGBA(QImageScaleInfo *isi, unsigned int *dest, diff --git a/src/gui/painting/qmatrix.cpp b/src/gui/painting/qmatrix.cpp index 00f32f7..7746316 100644 --- a/src/gui/painting/qmatrix.cpp +++ b/src/gui/painting/qmatrix.cpp @@ -103,7 +103,7 @@ QT_BEGIN_NAMESPACE coordinate system. The standard coordinate system of a QPaintDevice has its origin located at the top-left position. The \e x values increase to the right; \e y values increase - downward. For a complete description, see the \l {The Coordinate + downward. For a complete description, see the \l {Coordinate System}{coordinate system} documentation. QPainter has functions to translate, scale, shear and rotate the @@ -176,7 +176,7 @@ QT_BEGIN_NAMESPACE \snippet doc/src/snippets/matrix/matrix.cpp 2 \endtable - \sa QPainter, QTransform, {The Coordinate System}, + \sa QPainter, QTransform, {Coordinate System}, {demos/affine}{Affine Transformations Demo}, {Transformations Example} */ @@ -1135,7 +1135,7 @@ Q_GUI_EXPORT QPainterPath operator *(const QPainterPath &p, const QMatrix &m) Writes the given \a matrix to the given \a stream and returns a reference to the stream. - \sa {Format of the QDataStream Operators} + \sa {Serializing Qt Data Types} */ QDataStream &operator<<(QDataStream &s, const QMatrix &m) @@ -1161,7 +1161,7 @@ QDataStream &operator<<(QDataStream &s, const QMatrix &m) Reads the given \a matrix from the given \a stream and returns a reference to the stream. - \sa {Format of the QDataStream Operators} + \sa {Serializing Qt Data Types} */ QDataStream &operator>>(QDataStream &s, QMatrix &m) diff --git a/src/gui/painting/qmemrotate.cpp b/src/gui/painting/qmemrotate.cpp index c37aa51..6888bb0 100644 --- a/src/gui/painting/qmemrotate.cpp +++ b/src/gui/painting/qmemrotate.cpp @@ -594,4 +594,55 @@ void Q_GUI_EXPORT qt_memrotate90_gl(const quint32 *src, int srcWidth, int srcHei qt_memrotate90_template(src, srcWidth, srcHeight, srcStride, reinterpret_cast<qrgb_gl_rgba *>(dest), dstStride); } +void qt_memrotate90_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) +{ + qt_memrotate90((const ushort *)srcPixels, w, h, sbpl, (ushort *)destPixels, dbpl); +} + +void qt_memrotate180_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) +{ + qt_memrotate180((const ushort *)srcPixels, w, h, sbpl, (ushort *)destPixels, dbpl); +} + +void qt_memrotate270_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) +{ + qt_memrotate270((const ushort *)srcPixels, w, h, sbpl, (ushort *)destPixels, dbpl); +} + +void qt_memrotate90_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) +{ + qt_memrotate90((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl); +} + +void qt_memrotate180_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) +{ + qt_memrotate180((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl); +} + +void qt_memrotate270_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) +{ + qt_memrotate270((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl); +} + +MemRotateFunc qMemRotateFunctions[QImage::NImageFormats][3] = +// 90, 180, 270 +{ + { 0, 0, 0 }, // Format_Invalid, + { 0, 0, 0 }, // Format_Mono, + { 0, 0, 0 }, // Format_MonoLSB, + { 0, 0, 0 }, // Format_Indexed8, + { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // Format_RGB32, + { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // Format_ARGB32, + { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // Format_ARGB32_Premultiplied, + { qt_memrotate90_16, qt_memrotate180_16, qt_memrotate270_16 }, // Format_RGB16, + { 0, 0, 0 }, // Format_ARGB8565_Premultiplied, + { 0, 0, 0 }, // Format_RGB666, + { 0, 0, 0 }, // Format_ARGB6666_Premultiplied, + { 0, 0, 0 }, // Format_RGB555, + { 0, 0, 0 }, // Format_ARGB8555_Premultiplied, + { 0, 0, 0 }, // Format_RGB888, + { 0, 0, 0 }, // Format_RGB444, + { 0, 0, 0 } // Format_ARGB4444_Premultiplied, +}; + QT_END_NAMESPACE diff --git a/src/gui/painting/qmemrotate_p.h b/src/gui/painting/qmemrotate_p.h index 8df0520..2911860 100644 --- a/src/gui/painting/qmemrotate_p.h +++ b/src/gui/painting/qmemrotate_p.h @@ -82,8 +82,9 @@ QT_BEGIN_NAMESPACE void Q_GUI_QWS_EXPORT qt_memrotate270(const srctype*, int, int, int, desttype*, int) void Q_GUI_EXPORT qt_memrotate90(const quint32*, int, int, int, quint32*, int); +void Q_GUI_QWS_EXPORT qt_memrotate180(const quint32*, int, int, int, quint32*, int); +void Q_GUI_QWS_EXPORT qt_memrotate270(const quint32*, int, int, int, quint32*, int); -QT_DECL_MEMROTATE(quint32, quint32); QT_DECL_MEMROTATE(quint32, quint16); QT_DECL_MEMROTATE(quint16, quint32); QT_DECL_MEMROTATE(quint16, quint16); diff --git a/src/gui/painting/qoutlinemapper.cpp b/src/gui/painting/qoutlinemapper.cpp index ad0c2eb..bf03545 100644 --- a/src/gui/painting/qoutlinemapper.cpp +++ b/src/gui/painting/qoutlinemapper.cpp @@ -154,7 +154,8 @@ QT_FT_Outline *QOutlineMapper::convertPath(const QVectorPath &path) // ### We can kill this copying and just use the buffer straight... m_elements.resize(count); - memcpy(m_elements.data(), path.points(), count* sizeof(QPointF)); + if (count) + memcpy(m_elements.data(), path.points(), count* sizeof(QPointF)); m_element_types.resize(0); } @@ -233,12 +234,12 @@ void QOutlineMapper::endOutline() // Check for out of dev bounds... - const bool do_clip = (controlPointRect.left() < -QT_RASTER_COORD_LIMIT + const bool do_clip = !m_in_clip_elements && ((controlPointRect.left() < -QT_RASTER_COORD_LIMIT || controlPointRect.right() > QT_RASTER_COORD_LIMIT || controlPointRect.top() < -QT_RASTER_COORD_LIMIT || controlPointRect.bottom() > QT_RASTER_COORD_LIMIT || controlPointRect.width() > QT_RASTER_COORD_LIMIT - || controlPointRect.height() > QT_RASTER_COORD_LIMIT); + || controlPointRect.height() > QT_RASTER_COORD_LIMIT)); if (do_clip) { clipElements(elements, elementTypes(), element_count); @@ -352,7 +353,13 @@ void QOutlineMapper::clipElements(const QPointF *elements, // instead of going through convenience functionallity, but since // this part of code hardly every used, it shouldn't matter. + m_in_clip_elements = true; + QPainterPath path; + + if (!(m_outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL)) + path.setFillRule(Qt::WindingFill); + if (types) { for (int i=0; i<element_count; ++i) { switch (types[i]) { @@ -388,6 +395,8 @@ void QOutlineMapper::clipElements(const QPointF *elements, else convertPath(clippedPath); m_txop = old_txop; + + m_in_clip_elements = false; } QT_END_NAMESPACE diff --git a/src/gui/painting/qoutlinemapper_p.h b/src/gui/painting/qoutlinemapper_p.h index d0ce1a9..d534f76 100644 --- a/src/gui/painting/qoutlinemapper_p.h +++ b/src/gui/painting/qoutlinemapper_p.h @@ -87,8 +87,16 @@ const int QT_RASTER_COORD_LIMIT = 32767; class QOutlineMapper { public: - QOutlineMapper() - : m_round_coords(false) + QOutlineMapper() : + m_element_types(0), + m_elements(0), + m_elements_dev(0), + m_points(0), + m_tags(0), + m_contours(0), + m_polygon_dev(0), + m_in_clip_elements(false), + m_round_coords(false) { } @@ -228,6 +236,7 @@ public: qreal m_dy; bool m_valid; + bool m_in_clip_elements; private: bool m_round_coords; diff --git a/src/gui/painting/qpaintbuffer.cpp b/src/gui/painting/qpaintbuffer.cpp index 9fdb96c..3a4c94c 100644 --- a/src/gui/painting/qpaintbuffer.cpp +++ b/src/gui/painting/qpaintbuffer.cpp @@ -45,6 +45,8 @@ #include <private/qfontengine_p.h> #include <private/qemulationpaintengine_p.h> #include <private/qimage_p.h> +#include <qstatictext.h> +#include <private/qstatictext_p.h> #include <QDebug> @@ -267,24 +269,302 @@ 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; } +#ifndef QT_NO_DEBUG_STREAM +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; +} +#endif QRectF QPaintBuffer::boundingRect() const { @@ -306,6 +586,8 @@ public: Q_Q(QPaintBufferEngine); q->buffer->addCommand(QPaintBufferPrivate::Cmd_SystemStateChanged, QVariant(systemClip)); } + + QTransform last; }; @@ -492,6 +774,32 @@ void QPaintBufferEngine::renderHintsChanged() void QPaintBufferEngine::transformChanged() { + Q_D(QPaintBufferEngine); + const QTransform &transform = state()->matrix; + + QTransform delta; + + bool invertible = false; + if (transform.type() <= QTransform::TxScale && transform.type() == d->last.type()) + delta = transform * d->last.inverted(&invertible); + + d->last = transform; + + if (invertible && delta.type() == QTransform::TxNone) + return; + + if (invertible && delta.type() == QTransform::TxTranslate) { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: transformChanged (translate only) " << state()->matrix; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_Translate); + + qreal data[] = { delta.dx(), delta.dy() }; + cmd->extra = buffer->addData((qreal *) data, 2); + return; + } + // ### accumulate, like in QBrush case... if (!buffer->commands.isEmpty() && buffer->commands.last().id == QPaintBufferPrivate::Cmd_SetTransform) { @@ -960,6 +1268,19 @@ void QPaintBufferEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, con buffer->updateBoundingRect(r); } +void QPaintBufferEngine::drawStaticTextItem(QStaticTextItem *staticTextItem) +{ + QVariantList variants; + + variants << QVariant(staticTextItem->font); + for (int i=0; i<staticTextItem->numGlyphs; ++i) { + variants.append(staticTextItem->glyphs[i]); + variants.append(staticTextItem->glyphPositions[i].toPointF()); + } + + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawStaticText, QVariant(variants)); +} + void QPaintBufferEngine::drawTextItem(const QPointF &pos, const QTextItem &ti) { #ifdef QPAINTBUFFER_DEBUG_DRAW @@ -999,6 +1320,7 @@ void QPaintBufferEngine::drawTextItem(const QPointF &pos, const QTextItem &ti) void QPaintBufferEngine::setState(QPainterState *s) { + Q_D(QPaintBufferEngine); if (m_begin_detected) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: setState: begin, ignoring."; @@ -1017,6 +1339,8 @@ void QPaintBufferEngine::setState(QPainterState *s) buffer->addCommand(QPaintBufferPrivate::Cmd_Restore); } + d->last = s->matrix; + QPaintEngineEx::setState(s); } @@ -1065,15 +1389,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); + painter = p; - int frameStart = (frame == 0) ? 0 : d->frames.at(frame-1); - int frameEnd = (frame == d->frames.size()) ? d->commands.size() : d->frames.at(frame); - - for (int cmdIndex=frameStart; cmdIndex<frameEnd; ++cmdIndex) { + for (int cmdIndex = begin; cmdIndex < end; ++cmdIndex) { const QPaintBufferCommand &cmd = d->commands.at(cmdIndex); process(cmd); } @@ -1138,6 +1459,15 @@ void QPainterReplayer::process(const QPaintBufferCommand &cmd) painter->setTransform(xform * m_world_matrix); break; } + case QPaintBufferPrivate::Cmd_Translate: { + QPointF delta(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1)); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_Translate, offset: " << cmd.offset << delta; +#endif + painter->translate(delta.x(), delta.y()); + return; + } + case QPaintBufferPrivate::Cmd_SetCompositionMode: { QPainter::CompositionMode mode = (QPainter::CompositionMode) cmd.extra; #ifdef QPAINTBUFFER_DEBUG_DRAW @@ -1425,6 +1755,27 @@ void QPainterReplayer::process(const QPaintBufferCommand &cmd) #endif painter->setClipRegion(region, Qt::ClipOperation(cmd.extra)); break; } + + case QPaintBufferPrivate::Cmd_DrawStaticText: { + + QVariantList variants(d->variants.at(cmd.offset).value<QVariantList>()); + + 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); + + qt_draw_glyphs(painter, glyphs.constData(), positions.constData(), glyphs.size()); + + break; + } case QPaintBufferPrivate::Cmd_DrawText: { QPointF pos(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1)); @@ -1770,8 +2121,28 @@ struct QPaintBufferCacheEntry QVariant::Type type; quint64 cacheKey; }; + +struct QPaintBufferCacheEntryV2 +{ + enum Type { + ImageKey, + PixmapKey + }; + + struct Flags { + uint type : 8; + uint key : 24; + }; + + union { + Flags flags; + uint bits; + }; +}; + QT_END_NAMESPACE Q_DECLARE_METATYPE(QPaintBufferCacheEntry) +Q_DECLARE_METATYPE(QPaintBufferCacheEntryV2) QT_BEGIN_NAMESPACE QDataStream &operator<<(QDataStream &stream, const QPaintBufferCacheEntry &entry) @@ -1784,10 +2155,22 @@ QDataStream &operator>>(QDataStream &stream, QPaintBufferCacheEntry &entry) return stream >> entry.type >> entry.cacheKey; } +QDataStream &operator<<(QDataStream &stream, const QPaintBufferCacheEntryV2 &entry) +{ + return stream << entry.bits; +} + +QDataStream &operator>>(QDataStream &stream, QPaintBufferCacheEntryV2 &entry) +{ + return stream >> entry.bits; +} + static int qRegisterPaintBufferMetaTypes() { qRegisterMetaType<QPaintBufferCacheEntry>(); qRegisterMetaTypeStreamOperators<QPaintBufferCacheEntry>("QPaintBufferCacheEntry"); + qRegisterMetaType<QPaintBufferCacheEntryV2>(); + qRegisterMetaTypeStreamOperators<QPaintBufferCacheEntryV2>("QPaintBufferCacheEntryV2"); return 0; // something } @@ -1796,6 +2179,9 @@ Q_CONSTRUCTOR_FUNCTION(qRegisterPaintBufferMetaTypes) QDataStream &operator<<(QDataStream &stream, const QPaintBuffer &buffer) { + QHash<qint64, uint> pixmapKeys; + QHash<qint64, uint> imageKeys; + QHash<qint64, QPixmap> pixmaps; QHash<qint64, QImage> images; @@ -1804,19 +2190,33 @@ QDataStream &operator<<(QDataStream &stream, const QPaintBuffer &buffer) const QVariant &v = variants.at(i); if (v.type() == QVariant::Image) { const QImage image(v.value<QImage>()); - images[image.cacheKey()] = image; - QPaintBufferCacheEntry entry; - entry.type = QVariant::Image; - entry.cacheKey = image.cacheKey(); + QPaintBufferCacheEntryV2 entry; + entry.flags.type = QPaintBufferCacheEntryV2::ImageKey; + + QHash<qint64, uint>::iterator it = imageKeys.find(image.cacheKey()); + if (it != imageKeys.end()) { + entry.flags.key = *it; + } else { + imageKeys[image.cacheKey()] = entry.flags.key = images.size(); + images[images.size()] = image; + } + variants[i] = QVariant::fromValue(entry); } else if (v.type() == QVariant::Pixmap) { const QPixmap pixmap(v.value<QPixmap>()); - pixmaps[pixmap.cacheKey()] = pixmap; - QPaintBufferCacheEntry entry; - entry.type = QVariant::Pixmap; - entry.cacheKey = pixmap.cacheKey(); + QPaintBufferCacheEntryV2 entry; + entry.flags.type = QPaintBufferCacheEntryV2::PixmapKey; + + QHash<qint64, uint>::iterator it = pixmapKeys.find(pixmap.cacheKey()); + if (it != pixmapKeys.end()) { + entry.flags.key = *it; + } else { + pixmapKeys[pixmap.cacheKey()] = entry.flags.key = pixmaps.size(); + pixmaps[pixmaps.size()] = pixmap; + } + variants[i] = QVariant::fromValue(entry); } } @@ -1858,6 +2258,15 @@ QDataStream &operator>>(QDataStream &stream, QPaintBuffer &buffer) variants[i] = QVariant(images.value(entry.cacheKey)); else variants[i] = QVariant(pixmaps.value(entry.cacheKey)); + } else if (v.canConvert<QPaintBufferCacheEntryV2>()) { + QPaintBufferCacheEntryV2 entry = v.value<QPaintBufferCacheEntryV2>(); + + if (entry.flags.type == QPaintBufferCacheEntryV2::ImageKey) + variants[i] = QVariant(images.value(entry.flags.key)); + else if (entry.flags.type == QPaintBufferCacheEntryV2::PixmapKey) + variants[i] = QVariant(pixmaps.value(entry.flags.key)); + else + qWarning() << "operator<<(QDataStream &stream, QPaintBuffer &buffer): unrecognized cache entry type:" << entry.flags.type; } } diff --git a/src/gui/painting/qpaintbuffer_p.h b/src/gui/painting/qpaintbuffer_p.h index 79d7b35..e4fe4bf 100644 --- a/src/gui/painting/qpaintbuffer_p.h +++ b/src/gui/painting/qpaintbuffer_p.h @@ -78,6 +78,14 @@ 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; +#ifndef QT_NO_DEBUG_STREAM + QString commandDescription(int command) const; +#endif + void setBoundingRect(const QRectF &rect); QRectF boundingRect() const; @@ -183,6 +191,10 @@ public: Cmd_DrawTiledPixmap, Cmd_SystemStateChanged, + Cmd_Translate, + Cmd_DrawStaticText, + + // new commands must be added above this line Cmd_LastCommand }; @@ -313,7 +325,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; @@ -394,6 +406,7 @@ public: virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s); virtual void drawTextItem(const QPointF &pos, const QTextItem &ti); + virtual void drawStaticTextItem(QStaticTextItem *staticTextItem); virtual void setState(QPainterState *s); virtual uint flags() const {return QPaintEngineEx::DoNotEmulate;} diff --git a/src/gui/painting/qpaintdevice.qdoc b/src/gui/painting/qpaintdevice.qdoc index 8c73cc0..5290766 100644 --- a/src/gui/painting/qpaintdevice.qdoc +++ b/src/gui/painting/qpaintdevice.qdoc @@ -6,35 +6,21 @@ ** ** This file is part of the documentation of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL$ +** $QT_BEGIN_LICENSE:FDL$ ** 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. +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -87,8 +73,7 @@ function returns the number of different colors available for the paint device. - \sa QPaintEngine, QPainter, {The Coordinate System}, {The Paint - System} + \sa QPaintEngine, QPainter, {Coordinate System}, {Paint System} */ /*! diff --git a/src/gui/painting/qpaintengine.cpp b/src/gui/painting/qpaintengine.cpp index 6aabde8..a2d0337 100644 --- a/src/gui/painting/qpaintengine.cpp +++ b/src/gui/painting/qpaintengine.cpp @@ -172,7 +172,7 @@ QFont QTextItem::font() const possible to adapt to multiple technologies on each platform and take advantage of each to the fullest. - \sa QPainter, QPaintDevice::paintEngine(), {The Paint System} + \sa QPainter, QPaintDevice::paintEngine(), {Paint System} */ /*! diff --git a/src/gui/painting/qpaintengine_mac.cpp b/src/gui/painting/qpaintengine_mac.cpp index ac2fcf4..e5323d8 100644 --- a/src/gui/painting/qpaintengine_mac.cpp +++ b/src/gui/painting/qpaintengine_mac.cpp @@ -118,9 +118,10 @@ QMacCGContext::QMacCGContext(QPainter *p) QRegion clip = p->paintEngine()->systemClip(); QTransform native = p->deviceTransform(); QTransform logical = p->combinedTransform(); + if (p->hasClipping()) { QRegion r = p->clipRegion(); - r.translate(native.dx() - logical.dx(), native.dy() - logical.dy()); + r.translate(native.dx(), native.dy()); if (clip.isEmpty()) clip = r; else @@ -128,10 +129,7 @@ QMacCGContext::QMacCGContext(QPainter *p) } qt_mac_clip_cg(context, clip, 0); - QPainterState *state = static_cast<QPainterState *>(pe->state); - Q_ASSERT(state); - if (!state->redirectionMatrix.isIdentity()) - CGContextTranslateCTM(context, state->redirectionMatrix.dx(), state->redirectionMatrix.dy()); + CGContextTranslateCTM(context, native.dx(), native.dy()); } } CGContextRetain(context); @@ -1392,7 +1390,11 @@ QCoreGraphicsPaintEngine::updateRenderHints(QPainter::RenderHints hints) CGContextSetInterpolationQuality(d->hd, (hints & QPainter::SmoothPixmapTransform) ? kCGInterpolationHigh : kCGInterpolationNone); } - CGContextSetShouldSmoothFonts(d->hd, hints & QPainter::TextAntialiasing); + bool textAntialiasing = (hints & QPainter::TextAntialiasing) == QPainter::TextAntialiasing; + if (!textAntialiasing || d->disabledSmoothFonts) { + d->disabledSmoothFonts = !textAntialiasing; + CGContextSetShouldSmoothFonts(d->hd, textAntialiasing); + } } /* diff --git a/src/gui/painting/qpaintengine_mac_p.h b/src/gui/painting/qpaintengine_mac_p.h index c9ab419..940b2bc 100644 --- a/src/gui/painting/qpaintengine_mac_p.h +++ b/src/gui/painting/qpaintengine_mac_p.h @@ -148,7 +148,7 @@ class QCoreGraphicsPaintEnginePrivate : public QPaintEnginePrivate Q_DECLARE_PUBLIC(QCoreGraphicsPaintEngine) public: QCoreGraphicsPaintEnginePrivate() - : hd(0), shading(0), stackCount(0), complexXForm(false) + : hd(0), shading(0), stackCount(0), complexXForm(false), disabledSmoothFonts(false) { } @@ -168,6 +168,7 @@ public: CGShadingRef shading; int stackCount; bool complexXForm; + bool disabledSmoothFonts; enum { CosmeticNone, CosmeticTransformPath, CosmeticSetPenWidth } cosmeticPen; // pixel and cosmetic pen size in user coordinates. diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index d1c4cab..3717ed9 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -68,6 +68,8 @@ // #include <private/qpolygonclipper_p.h> // #include <private/qrasterizer_p.h> #include <private/qimage_p.h> +#include <private/qstatictext_p.h> +#include "qmemrotate_p.h" #include "qpaintengine_raster_p.h" // #include "qbezier_p.h" @@ -251,6 +253,11 @@ static void qt_debug_path(const QPainterPath &path) } #endif +QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() : + QPaintEngineExPrivate(), + cachedLines(0) +{ +} /*! @@ -336,26 +343,13 @@ void QRasterPaintEngine::init() d->hdc = 0; #endif - d->rasterPoolSize = 8192; - d->rasterPoolBase = -#if defined(Q_WS_WIN64) - // We make use of setjmp and longjmp in qgrayraster.c which requires - // 16-byte alignment, hence we hardcode this requirement here.. - (unsigned char *) _aligned_malloc(d->rasterPoolSize, sizeof(void*) * 2); -#else - (unsigned char *) malloc(d->rasterPoolSize); -#endif - Q_CHECK_PTR(d->rasterPoolBase); - // The antialiasing raster. d->grayRaster.reset(new QT_FT_Raster); Q_CHECK_PTR(d->grayRaster.data()); - if (qt_ft_grays_raster.raster_new(0, d->grayRaster.data())) + if (qt_ft_grays_raster.raster_new(d->grayRaster.data())) QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc - qt_ft_grays_raster.raster_reset(*d->grayRaster.data(), d->rasterPoolBase, d->rasterPoolSize); - d->rasterizer.reset(new QRasterizer); d->rasterBuffer.reset(new QRasterBuffer()); d->outlineMapper.reset(new QOutlineMapper); @@ -437,12 +431,6 @@ QRasterPaintEngine::~QRasterPaintEngine() { Q_D(QRasterPaintEngine); -#if defined(Q_WS_WIN64) - _aligned_free(d->rasterPoolBase); -#else - free(d->rasterPoolBase); -#endif - qt_ft_grays_raster.raster_done(*d->grayRaster.data()); } @@ -455,8 +443,9 @@ bool QRasterPaintEngine::begin(QPaintDevice *device) if (device->devType() == QInternal::Pixmap) { QPixmap *pixmap = static_cast<QPixmap *>(device); - if (pixmap->data->classId() == QPixmapData::RasterClass) - d->device = pixmap->data->buffer(); + QPixmapData *pd = pixmap->pixmapData(); + if (pd->classId() == QPixmapData::RasterClass) + d->device = pd->buffer(); } else { d->device = device; } @@ -471,13 +460,12 @@ bool QRasterPaintEngine::begin(QPaintDevice *device) QRasterPaintEngineState *s = state(); ensureOutlineMapper(); - d->outlineMapper->m_clip_rect = d->deviceRect.adjusted(-10, -10, 10, 10); - - // This is the upp - QRect bounds(-QT_RASTER_COORD_LIMIT, -QT_RASTER_COORD_LIMIT, - QT_RASTER_COORD_LIMIT*2 - 1, QT_RASTER_COORD_LIMIT * 2 - 1); - d->outlineMapper->m_clip_rect = bounds.intersected(d->outlineMapper->m_clip_rect); + d->outlineMapper->m_clip_rect = d->deviceRect; + if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT) + d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT); + if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT) + d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT); d->rasterizer->setClipRect(d->deviceRect); @@ -1115,7 +1103,7 @@ void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBru Q_Q(QRasterPaintEngine); bool bilinear = q->state()->flags.bilinear; - if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimise + if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize spanData->setupMatrix(b.transform() * m, bilinear); } else { if (m.type() <= QTransform::TxTranslate) { @@ -1725,9 +1713,10 @@ void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen) if (patternLength > 0) { int n = qFloor(dashOffset / patternLength); dashOffset -= n * patternLength; - while (dashOffset > pattern.at(dashIndex)) { + while (dashOffset >= pattern.at(dashIndex)) { dashOffset -= pattern.at(dashIndex); - dashIndex = (dashIndex + 1) % pattern.size(); + if (++dashIndex >= pattern.size()) + dashIndex = 0; inDash = !inDash; } } @@ -1738,7 +1727,6 @@ void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen) const QLineF *lines = reinterpret_cast<const QLineF *>(path.points()); for (int i = 0; i < lineCount; ++i) { - dashOffset = s->lastPen.dashOffset(); if (lines[i].p1() == lines[i].p2()) { if (s->lastPen.capStyle() != Qt::FlatCap) { QPointF p = lines[i].p1(); @@ -2372,8 +2360,9 @@ void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap) qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth(); #endif - if (pixmap.data->classId() == QPixmapData::RasterClass) { - const QImage &image = static_cast<QRasterPixmapData *>(pixmap.data.data())->image; + QPixmapData *pd = pixmap.pixmapData(); + if (pd->classId() == QPixmapData::RasterClass) { + const QImage &image = static_cast<QRasterPixmapData *>(pd)->image; if (image.depth() == 1) { Q_D(QRasterPaintEngine); QRasterPaintEngineState *s = state(); @@ -2412,8 +2401,9 @@ void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, cons qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth(); #endif - if (pixmap.data->classId() == QPixmapData::RasterClass) { - const QImage &image = static_cast<QRasterPixmapData *>(pixmap.data.data())->image; + QPixmapData* pd = pixmap.pixmapData(); + if (pd->classId() == QPixmapData::RasterClass) { + const QImage &image = static_cast<QRasterPixmapData *>(pd)->image; if (image.depth() == 1) { Q_D(QRasterPaintEngine); QRasterPaintEngineState *s = state(); @@ -2430,7 +2420,9 @@ void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, cons drawImage(r, image, sr); } } else { - const QImage image = pixmap.toImage(); + QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect()); + const QImage image = pd->toImage(clippedSource); + QRectF translatedSource = sr.translated(-clippedSource.topLeft()); if (image.depth() == 1) { Q_D(QRasterPaintEngine); QRasterPaintEngineState *s = state(); @@ -2441,10 +2433,10 @@ void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, cons drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData); return; } else { - drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr); + drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource); } } else { - drawImage(r, image, sr); + drawImage(r, image, translatedSource); } } } @@ -2532,6 +2524,41 @@ QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t) return QRectF(r.topLeft() * t, r.bottomRight() * t); } +namespace { + enum RotationType { + Rotation90, + Rotation180, + Rotation270, + NoRotation + }; + + inline RotationType qRotationType(const QTransform &transform) + { + QTransform::TransformationType type = transform.type(); + + if (type > QTransform::TxRotate) + return NoRotation; + + if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1)) + && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22())) + return Rotation90; + + if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12()) + && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1))) + return Rotation180; + + if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1)) + && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22())) + return Rotation270; + + return NoRotation; + } + + inline bool isPixelAligned(const QRectF &rect) { + return QRectF(rect.toRect()) == rect; + } +} + /*! \reimp */ @@ -2552,7 +2579,7 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe int sr_t = qFloor(sr.top()); int sr_b = qCeil(sr.bottom()) - 1; - if (!s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) { + if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) { // as fillRect will apply the aliased coordinate delta we need to // subtract it here as we don't use it for image drawing QTransform old = s->matrix; @@ -2593,9 +2620,62 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe const QClipData *clip = d->clip(); + if (s->matrix.type() > QTransform::TxTranslate + && !stretch_sr + && (!clip || clip->hasRectClip) + && s->intOpacity == 256 + && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver + || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source) + && d->rasterBuffer->format == img.format() + && (d->rasterBuffer->format == QImage::Format_RGB16 + || d->rasterBuffer->format == QImage::Format_RGB32 + || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied + && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source))) + { + RotationType rotationType = qRotationType(s->matrix); + + if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) { + QRectF transformedTargetRect = s->matrix.mapRect(r); + + if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing)) + || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr))) + { + QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect); + if (clippedTransformedTargetRect.isNull()) + return; + + QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect)); + + QRect clippedSourceRect + = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(), + clippedTargetRect.width(), clippedTargetRect.height()).toRect(); + + uint dbpl = d->rasterBuffer->bytesPerLine(); + uint sbpl = img.bytesPerLine(); + + uchar *dst = d->rasterBuffer->buffer(); + uint bpp = img.depth() >> 3; + + const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp; + uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp; + + uint cw = clippedSourceRect.width(); + uint ch = clippedSourceRect.height(); + + qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl); + + return; + } + } + } + if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) { - if (s->flags.fast_images) { + QRectF targetBounds = s->matrix.mapRect(r); + bool exceedsPrecision = targetBounds.width() > 0xffff + || targetBounds.height() > 0xffff; + + if (s->flags.fast_images && !exceedsPrecision) { if (s->matrix.type() > QTransform::TxScale) { SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()]; if (func && (!clip || clip->hasRectClip)) { @@ -2717,8 +2797,9 @@ void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, QImage image; - if (pixmap.data->classId() == QPixmapData::RasterClass) { - image = static_cast<QRasterPixmapData *>(pixmap.data.data())->image; + QPixmapData *pd = pixmap.pixmapData(); + if (pd->classId() == QPixmapData::RasterClass) { + image = static_cast<QRasterPixmapData *>(pd)->image; } else { image = pixmap.toImage(); } @@ -3003,27 +3084,22 @@ void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx blend(current, spans, &s->penData); } -void QRasterPaintEngine::drawCachedGlyphs(const QPointF &p, const QTextItemInt &ti) +void QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, + const QFixedPoint *positions, QFontEngine *fontEngine) { Q_D(QRasterPaintEngine); QRasterPaintEngineState *s = state(); - QVarLengthArray<QFixedPoint> positions; - QVarLengthArray<glyph_t> glyphs; - QTransform matrix = s->matrix; - matrix.translate(p.x(), p.y()); - ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); - - QFontEngineGlyphCache::Type glyphType = ti.fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(ti.fontEngine->glyphFormat) : d->glyphCacheType; + QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType; QImageTextureGlyphCache *cache = - (QImageTextureGlyphCache *) ti.fontEngine->glyphCache(0, glyphType, s->matrix); + static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix)); if (!cache) { cache = new QImageTextureGlyphCache(glyphType, s->matrix); - ti.fontEngine->setGlyphCache(0, cache); + fontEngine->setGlyphCache(0, cache); } - cache->populate(ti, glyphs, positions); + cache->populate(fontEngine, numGlyphs, glyphs, positions); const QImage &image = cache->image(); int bpl = image.bytesPerLine(); @@ -3041,7 +3117,7 @@ void QRasterPaintEngine::drawCachedGlyphs(const QPointF &p, const QTextItemInt & const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta); const uchar *bits = image.bits(); - for (int i=0; i<glyphs.size(); ++i) { + for (int i=0; i<numGlyphs; ++i) { const QTextureGlyphCache::Coord &c = cache->coords.value(glyphs[i]); int x = qFloor(positions[i].x + offs) + c.baseLineX - margin; int y = qFloor(positions[i].y + offs) - c.baseLineY - margin; @@ -3089,10 +3165,8 @@ void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti) const TUint8 *glyphBitmapBytes; TSize glyphBitmapSize; fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize); - const glyph_metrics_t metrics = ti.fontEngine->boundingBox(glyphs[i]); - const int x = qFloor(positions[i].x + metrics.x + aliasDelta); - const int y = qFloor(positions[i].y + metrics.y + aliasDelta); - + const int x = qFloor(positions[i].x + tmetrics.HorizBearingX() + aliasDelta); + const int y = qFloor(positions[i].y - tmetrics.HorizBearingY() + aliasDelta); alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight); } @@ -3219,6 +3293,18 @@ QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect, } /*! + \reimp +*/ +void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem) +{ + ensurePen(); + ensureState(); + + drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions, + textItem->fontEngine); +} + +/*! \reimp */ void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem) @@ -3266,7 +3352,17 @@ void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textIte drawCached = false; #endif if (drawCached) { - drawCachedGlyphs(p, ti); + QRasterPaintEngineState *s = state(); + + QVarLengthArray<QFixedPoint> positions; + QVarLengthArray<glyph_t> glyphs; + + QTransform matrix = s->matrix; + matrix.translate(p.x(), p.y()); + + ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); + + drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine); return; } @@ -3373,7 +3469,7 @@ void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textIte }; for(int i = 0; i < glyphs.size(); i++) { - QFontEngineFT::Glyph *glyph = gset->glyph_data.value(glyphs[i]); + QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i]); if (!glyph || glyph->format != neededFormat) { if (!lockedFace) @@ -3609,13 +3705,14 @@ void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line, } else { *dashOffset = 0; *inDash = !(*inDash); - *dashIndex = (*dashIndex + 1) % pattern.size(); + if (++*dashIndex >= pattern.size()) + *dashIndex = 0; length -= dash; l.setLength(dash); line.setP1(l.p2()); } - if (rasterize && dash != 0) + if (rasterize && dash > 0) rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap); } } @@ -3676,6 +3773,7 @@ void QRasterPaintEngine::drawEllipse(const QRectF &rect) if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen) || (qpen_style(s->lastPen) == Qt::NoPen && !s->flags.antialiased)) && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT + && !rect.isEmpty() && s->matrix.type() <= QTransform::TxScale) // no shear { ensureBrush(); @@ -4049,6 +4147,10 @@ void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline, rasterize(outline, callback, (void *)spanData, rasterBuffer); } +extern "C" { + int q_gray_rendered_spans(QT_FT_Raster raster); +} + void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline, ProcessSpans callback, void *userData, QRasterBuffer *) @@ -4072,6 +4174,26 @@ void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline, return; } + // Initial size for raster pool is MINIMUM_POOL_SIZE so as to + // minimize memory reallocations. However if initial size for + // raster pool is changed for lower value, reallocations will + // occur normally. + const int rasterPoolInitialSize = MINIMUM_POOL_SIZE; + int rasterPoolSize = rasterPoolInitialSize; + unsigned char *rasterPoolBase; +#if defined(Q_WS_WIN64) + rasterPoolBase = + // We make use of setjmp and longjmp in qgrayraster.c which requires + // 16-byte alignment, hence we hardcode this requirement here.. + (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2); +#else + unsigned char rasterPoolOnStack[rasterPoolInitialSize]; + rasterPoolBase = rasterPoolOnStack; +#endif + Q_CHECK_PTR(rasterPoolBase); + + qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize); + void *data = userData; QT_FT_BBox clip_box = { deviceRect.x(), @@ -4093,24 +4215,30 @@ void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline, bool done = false; int error; + int rendered_spans = 0; + while (!done) { rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT); rasterParams.gray_spans = callback; + rasterParams.skip_spans = rendered_spans; error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams); // Out of memory, reallocate some more and try again... - if (error == -6) { // -6 is Result_err_OutOfMemory + if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c int new_size = rasterPoolSize * 2; if (new_size > 1024 * 1024) { qWarning("QPainter: Rasterization of primitive failed"); - return; + break; } + rendered_spans += q_gray_rendered_spans(*grayRaster.data()); + #if defined(Q_WS_WIN64) _aligned_free(rasterPoolBase); #else - free(rasterPoolBase); + if (rasterPoolBase != rasterPoolOnStack) // initially on the stack + free(rasterPoolBase); #endif rasterPoolSize = new_size; @@ -4125,12 +4253,19 @@ void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline, Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal. qt_ft_grays_raster.raster_done(*grayRaster.data()); - qt_ft_grays_raster.raster_new(0, grayRaster.data()); + qt_ft_grays_raster.raster_new(grayRaster.data()); qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize); } else { done = true; } } + +#if defined(Q_WS_WIN64) + _aligned_free(rasterPoolBase); +#else + if (rasterPoolBase != rasterPoolOnStack) // initially on the stack + free(rasterPoolBase); +#endif } void QRasterPaintEnginePrivate::recalculateFastImages() diff --git a/src/gui/painting/qpaintengine_raster_p.h b/src/gui/painting/qpaintengine_raster_p.h index a1c73cc..1016f8d 100644 --- a/src/gui/painting/qpaintengine_raster_p.h +++ b/src/gui/painting/qpaintengine_raster_p.h @@ -203,6 +203,8 @@ public: void clip(const QRect &rect, Qt::ClipOperation op); void clip(const QRegion ®ion, Qt::ClipOperation op); + void drawStaticTextItem(QStaticTextItem *textItem); + enum ClipType { RectClip, ComplexClip @@ -257,7 +259,8 @@ private: void fillRect(const QRectF &rect, QSpanData *data); void drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fill); - void drawCachedGlyphs(const QPointF &p, const QTextItemInt &ti); + void drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions, + QFontEngine *fontEngine); #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) void drawGlyphsS60(const QPointF &p, const QTextItemInt &ti); @@ -297,6 +300,7 @@ QRasterPaintEnginePrivate : public QPaintEngineExPrivate { Q_DECLARE_PUBLIC(QRasterPaintEngine) public: + QRasterPaintEnginePrivate(); void rasterizeLine_dashed(QLineF line, qreal width, int *dashIndex, qreal *dashOffset, bool *inDash); @@ -351,8 +355,6 @@ public: QScopedPointer<QDashStroker> dashStroker; QScopedPointer<QT_FT_Raster> grayRaster; - unsigned long rasterPoolSize; - unsigned char *rasterPoolBase; QDataBuffer<QLineF> cachedLines; QSpanData image_filler; diff --git a/src/gui/painting/qpaintengine_s60.cpp b/src/gui/painting/qpaintengine_s60.cpp index 068ca0c..d7fc0af 100644 --- a/src/gui/painting/qpaintengine_s60.cpp +++ b/src/gui/painting/qpaintengine_s60.cpp @@ -59,44 +59,63 @@ bool QS60PaintEngine::begin(QPaintDevice *device) { Q_D(QS60PaintEngine); - pixmapData->beginDataAccess(); - bool ret = QRasterPaintEngine::begin(device); - // Make sure QPaintEngine::paintDevice() returns the proper device. - // QRasterPaintEngine changes pdev to QImage in case of RasterClass QPixmapData - // which is incorrect in Symbian. - d->pdev = device; - return ret; + if (pixmapData->classId() == QPixmapData::RasterClass) { + pixmapData->beginDataAccess(); + bool ret = QRasterPaintEngine::begin(device); + // Make sure QPaintEngine::paintDevice() returns the proper device. + // QRasterPaintEngine changes pdev to QImage in case of RasterClass QPixmapData + // which is incorrect in Symbian. + d->pdev = device; + return ret; + } + + return QRasterPaintEngine::begin(device); } bool QS60PaintEngine::end() { - bool ret = QRasterPaintEngine::end(); - pixmapData->endDataAccess(); - return ret; + if (pixmapData->classId() == QPixmapData::RasterClass) { + bool ret = QRasterPaintEngine::end(); + pixmapData->endDataAccess(); + return ret; + } + return QRasterPaintEngine::end(); } void QS60PaintEngine::drawPixmap(const QPointF &p, const QPixmap &pm) { - QS60PixmapData *srcData = static_cast<QS60PixmapData *>(pm.pixmapData()); - srcData->beginDataAccess(); - QRasterPaintEngine::drawPixmap(p, pm); - srcData->endDataAccess(); + if (pm.pixmapData()->classId() == QPixmapData::RasterClass) { + QS60PixmapData *srcData = static_cast<QS60PixmapData *>(pm.pixmapData()); + srcData->beginDataAccess(); + QRasterPaintEngine::drawPixmap(p, pm); + srcData->endDataAccess(); + } else { + QRasterPaintEngine::drawPixmap(p, pm); + } } void QS60PaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) { - QS60PixmapData *srcData = static_cast<QS60PixmapData *>(pm.pixmapData()); - srcData->beginDataAccess(); - QRasterPaintEngine::drawPixmap(r, pm, sr); - srcData->endDataAccess(); + if (pm.pixmapData()->classId() == QPixmapData::RasterClass) { + QS60PixmapData *srcData = static_cast<QS60PixmapData *>(pm.pixmapData()); + srcData->beginDataAccess(); + QRasterPaintEngine::drawPixmap(r, pm, sr); + srcData->endDataAccess(); + } else { + QRasterPaintEngine::drawPixmap(r, pm, sr); + } } void QS60PaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &sr) { - QS60PixmapData *srcData = static_cast<QS60PixmapData *>(pm.pixmapData()); - srcData->beginDataAccess(); - QRasterPaintEngine::drawTiledPixmap(r, pm, sr); - srcData->endDataAccess(); + if (pm.pixmapData()->classId() == QPixmapData::RasterClass) { + QS60PixmapData *srcData = static_cast<QS60PixmapData *>(pm.pixmapData()); + srcData->beginDataAccess(); + QRasterPaintEngine::drawTiledPixmap(r, pm, sr); + srcData->endDataAccess(); + } else { + QRasterPaintEngine::drawTiledPixmap(r, pm, sr); + } } void QS60PaintEngine::prepare(QImage *image) diff --git a/src/gui/painting/qpaintengine_x11.cpp b/src/gui/painting/qpaintengine_x11.cpp index da48fcb..fecf25f 100644 --- a/src/gui/painting/qpaintengine_x11.cpp +++ b/src/gui/painting/qpaintengine_x11.cpp @@ -79,6 +79,8 @@ #include <private/qtessellator_p.h> #endif +#include <private/qstylehelper_p.h> + QT_BEGIN_NAMESPACE extern Drawable qt_x11Handle(const QPaintDevice *pd); @@ -224,7 +226,10 @@ static const uchar base_dither_matrix[DITHER_SIZE][DITHER_SIZE] = { static QPixmap qt_patternForAlpha(uchar alpha, int screen) { QPixmap pm; - QString key = QLatin1String("$qt-alpha-brush$") + QString::number(alpha) + QString::number(screen); + QString key = QLatin1Literal("$qt-alpha-brush$") + % HexString<uchar>(alpha) + % HexString<int>(screen); + if (!QPixmapCache::find(key, pm)) { // #### why not use a mono image here???? QImage pattern(DITHER_SIZE, DITHER_SIZE, QImage::Format_ARGB32); @@ -315,7 +320,7 @@ static Picture getPatternFill(int screen, const QBrush &b) return X11->pattern_fills[i].picture; } // none found, replace one - int i = rand() % 16; + int i = qrand() % 16; if (X11->pattern_fills[i].screen != screen && X11->pattern_fills[i].picture) { XRenderFreePicture (X11->display, X11->pattern_fills[i].picture); @@ -1448,6 +1453,11 @@ void QX11PaintEngine::drawEllipse(const QRectF &rect) void QX11PaintEngine::drawEllipse(const QRect &rect) { + if (rect.isEmpty()) { + drawRects(&rect, 1); + return; + } + Q_D(QX11PaintEngine); QRect devclip(SHRT_MIN, SHRT_MIN, SHRT_MAX*2 - 1, SHRT_MAX*2 - 1); QRect r(rect); @@ -1906,6 +1916,8 @@ void QX11PaintEngine::drawPixmap(const QRectF &r, const QPixmap &px, const QRect int sh = qRound(sr.height()); QPixmap pixmap = qt_toX11Pixmap(px); + if(pixmap.isNull()) + return; if ((d->xinfo && d->xinfo->screen() != pixmap.x11Info().screen()) || (pixmap.x11Info().screen() != DefaultScreen(X11->display))) { diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp index d54c1ed..c1e3d66 100644 --- a/src/gui/painting/qpaintengineex.cpp +++ b/src/gui/painting/qpaintengineex.cpp @@ -149,6 +149,7 @@ QDebug Q_GUI_EXPORT &operator<<(QDebug &s, const QVectorPath &path) struct StrokeHandler { + StrokeHandler(int reserve) : pts(reserve), types(reserve) {} QDataBuffer<qreal> pts; QDataBuffer<QPainterPath::ElementType> types; }; @@ -394,7 +395,7 @@ void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) return; if (!d->strokeHandler) { - d->strokeHandler = new StrokeHandler; + d->strokeHandler = new StrokeHandler(path.elementCount()+4); d->stroker.setMoveToHook(qpaintengineex_moveTo); d->stroker.setLineToHook(qpaintengineex_lineTo); d->stroker.setCubicToHook(qpaintengineex_cubicTo); @@ -460,6 +461,7 @@ void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) // change the current transform. Normal transformed, // non-cosmetic pens will be transformed as part of fill // later, so they are also covered here.. + d->activeStroker->setCurveThresholdFromTransform(state()->matrix); d->activeStroker->begin(d->strokeHandler); if (types) { while (points < lastPoint) { @@ -492,11 +494,9 @@ void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) } else { d->activeStroker->moveTo(points[0], points[1]); points += 2; - ++types; while (points < lastPoint) { d->activeStroker->lineTo(points[0], points[1]); points += 2; - ++types; } if (path.hasImplicitClose()) d->activeStroker->lineTo(path.points()[0], path.points()[1]); @@ -517,6 +517,7 @@ void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) QPainterPath painterPath = state()->matrix.map(path.convertToPainterPath()); d->activeStroker->strokePath(painterPath, d->strokeHandler, QTransform()); } else { + d->activeStroker->setCurveThresholdFromTransform(QTransform()); d->activeStroker->begin(d->strokeHandler); if (types) { while (points < lastPoint) { @@ -558,12 +559,10 @@ void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) QPointF p = ((QPointF *)points)[0] * state()->matrix; d->activeStroker->moveTo(p.x(), p.y()); points += 2; - ++types; while (points < lastPoint) { QPointF p = ((QPointF *)points)[0] * state()->matrix; d->activeStroker->lineTo(p.x(), p.y()); points += 2; - ++types; } if (path.hasImplicitClose()) d->activeStroker->lineTo(p.x(), p.y()); @@ -607,11 +606,11 @@ void QPaintEngineEx::clip(const QRect &r, Qt::ClipOperation op) { qreal right = r.x() + r.width(); qreal bottom = r.y() + r.height(); - qreal pts[] = { r.x(), r.y(), - right, r.y(), + qreal pts[] = { qreal(r.x()), qreal(r.y()), + right, qreal(r.y()), right, bottom, - r.x(), bottom, - r.x(), r.y() }; + qreal(r.x()), bottom, + qreal(r.x()), qreal(r.y()) }; QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint); clip(vp, op); } @@ -711,11 +710,11 @@ void QPaintEngineEx::drawRects(const QRect *rects, int rectCount) // ### Is there a one off here? qreal right = r.x() + r.width(); qreal bottom = r.y() + r.height(); - qreal pts[] = { r.x(), r.y(), - right, r.y(), + qreal pts[] = { qreal(r.x()), qreal(r.y()), + right, qreal(r.y()), right, bottom, - r.x(), bottom, - r.x(), r.y() }; + qreal(r.x()), bottom, + qreal(r.x()), qreal(r.y()) }; QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint); draw(vp); } @@ -769,7 +768,7 @@ void QPaintEngineEx::drawRoundedRect(const QRectF &rect, qreal xRadius, qreal yR x1, y2 - (1 - KAPPA) * yRadius, x1, y2 - yRadius, x1, y1 + yRadius, // LineTo - x1, y1 + KAPPA * yRadius, // CurveTo + x1, y1 + (1 - KAPPA) * yRadius, // CurveTo x1 + (1 - KAPPA) * xRadius, y1, x1 + xRadius, y1 }; @@ -832,7 +831,7 @@ void QPaintEngineEx::drawEllipse(const QRectF &r) int point_count = 0; x.points[0] = qt_curves_for_arc(r, 0, -360, x.points + 1, &point_count); - QVectorPath vp((qreal *) pts, 13, qpaintengineex_ellipse_types, QVectorPath::EllipseHint); + QVectorPath vp((qreal *) pts, point_count, qpaintengineex_ellipse_types, QVectorPath::EllipseHint); draw(vp); } @@ -893,7 +892,7 @@ void QPaintEngineEx::drawPoints(const QPoint *points, int pointCount) for (int i=0; i<count; ++i) { pts[++oset] = points[i].x(); pts[++oset] = points[i].y(); - pts[++oset] = points[i].x() + 1/63; + pts[++oset] = points[i].x() + 1/63.; pts[++oset] = points[i].y(); } QVectorPath path(pts, count * 2, qpaintengineex_line_types_16, QVectorPath::LinesHint); @@ -903,7 +902,8 @@ void QPaintEngineEx::drawPoints(const QPoint *points, int pointCount) } } else { for (int i=0; i<pointCount; ++i) { - qreal pts[] = { points[i].x(), points[i].y(), points[i].x() +1/63., points[i].y() }; + qreal pts[] = { qreal(points[i].x()), qreal(points[i].y()), + qreal(points[i].x() +1/63.), qreal(points[i].y()) }; QVectorPath path(pts, 2, 0); stroke(path, pen); } @@ -970,23 +970,29 @@ void QPaintEngineEx::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, con fill(path, brush); } -void QPaintEngineEx::drawPixmaps(const QDrawPixmaps::Data *drawingData, int dataCount, const QPixmap &pixmap, QDrawPixmaps::DrawingHints /*hints*/) +void QPaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, + const QPixmap &pixmap, QPainter::PixmapFragmentHints /*hints*/) { + if (pixmap.isNull()) + return; + qreal oldOpacity = state()->opacity; QTransform oldTransform = state()->matrix; - for (int i = 0; i < dataCount; ++i) { + for (int i = 0; i < fragmentCount; ++i) { QTransform transform = oldTransform; - transform.translate(drawingData[i].point.x(), drawingData[i].point.y()); - transform.rotate(drawingData[i].rotation); - state()->opacity = oldOpacity * drawingData[i].opacity; + transform.translate(fragments[i].x, fragments[i].y); + transform.rotate(fragments[i].rotation); + state()->opacity = oldOpacity * fragments[i].opacity; state()->matrix = transform; opacityChanged(); transformChanged(); - qreal w = drawingData[i].scaleX * drawingData[i].source.width(); - qreal h = drawingData[i].scaleY * drawingData[i].source.height(); - drawPixmap(QRectF(-0.5 * w, -0.5 * h, w, h), pixmap, drawingData[i].source); + qreal w = fragments[i].scaleX * fragments[i].width; + qreal h = fragments[i].scaleY * fragments[i].height; + QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop, + fragments[i].width, fragments[i].height); + drawPixmap(QRectF(-0.5 * w, -0.5 * h, w, h), pixmap, sourceRect); } state()->opacity = oldOpacity; diff --git a/src/gui/painting/qpaintengineex_p.h b/src/gui/painting/qpaintengineex_p.h index fccd1dc..6c654bd 100644 --- a/src/gui/painting/qpaintengineex_p.h +++ b/src/gui/painting/qpaintengineex_p.h @@ -70,6 +70,7 @@ QT_MODULE(Gui) class QPainterState; class QPaintEngineExPrivate; +class QStaticTextItem; struct StrokeHandler; struct QIntRect { @@ -196,10 +197,13 @@ public: virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s); - virtual void drawPixmaps(const QDrawPixmaps::Data *drawingData, int dataCount, const QPixmap &pixmap, QFlags<QDrawPixmaps::DrawingHint> hints); + virtual void drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, + QFlags<QPainter::PixmapFragmentHint> hints); virtual void updateState(const QPaintEngineState &state); + virtual void drawStaticTextItem(QStaticTextItem *) = 0; + virtual void setState(QPainterState *s); inline QPainterState *state() { return static_cast<QPainterState *>(QPaintEngine::state); } inline const QPainterState *state() const { return static_cast<const QPainterState *>(QPaintEngine::state); } diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 6055a04..7fed7e4 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -38,6 +38,7 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ + // QtCore #include <qdebug.h> #include <qmath.h> @@ -69,6 +70,9 @@ #include <private/qwidget_p.h> #include <private/qpaintengine_raster_p.h> #include <private/qmath_p.h> +#include <qstatictext.h> +#include <private/qstatictext_p.h> +#include <private/qstylehelper_p.h> QT_BEGIN_NAMESPACE @@ -86,6 +90,15 @@ void qt_format_text(const QFont &font, const QRectF &_r, int tf, const QTextOption *option, const QString& str, QRectF *brect, int tabstops, int* tabarray, int tabarraylen, QPainter *painter); +static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QFontEngine *fe, + QTextCharFormat::UnderlineStyle underlineStyle, + const QTextItem::RenderFlags flags, qreal width, + const QTextCharFormat &charFormat); +// Helper function to calculate left most position, width and flags for decoration drawing +static void drawDecorationForGlyphs(QPainter *painter, const glyph_t *glyphArray, + const QFixedPoint *positions, int glyphCount, + QFontEngine *fontEngine, const QFont &font, + const QTextCharFormat &charFormat); static inline QGradient::CoordinateMode coordinateMode(const QBrush &brush) { @@ -683,9 +696,9 @@ void QPainterPrivate::updateEmulationSpecifier(QPainterState *s) skip = false; - QBrush penBrush = s->pen.brush(); - Qt::BrushStyle brushStyle = s->brush.style(); - Qt::BrushStyle penBrushStyle = penBrush.style(); + QBrush penBrush = (qpen_style(s->pen) == Qt::NoPen) ? QBrush(Qt::NoBrush) : qpen_brush(s->pen); + Qt::BrushStyle brushStyle = qbrush_style(s->brush); + Qt::BrushStyle penBrushStyle = qbrush_style(penBrush); alpha = (penBrushStyle != Qt::NoBrush && (penBrushStyle < Qt::LinearGradientPattern && penBrush.color().alpha() != 255) && !penBrush.isOpaque()) @@ -967,7 +980,7 @@ void QPainterPrivate::updateState(QPainterState *newState) devices. If the painter is active, device() returns the paint device on which the painter paints, and paintEngine() returns the paint engine that the painter is currently operating on. For more - information, see \l {The Paint System} documentation. + information, see the \l {Paint System}. Sometimes it is desirable to make someone else paint on an unusual QPaintDevice. QPainter supports a static function to do this, @@ -1012,7 +1025,7 @@ void QPainterPrivate::updateState(QPainterState *newState) \o viewport(), window(), worldTransform() make up the painter's coordinate transformation system. For more information, see the \l - {Coordinate Transformations} section and the \l {The Coordinate + {Coordinate Transformations} section and the \l {Coordinate System} documentation. \o hasClipping() tells whether the painter clips at all. (The paint @@ -1219,7 +1232,7 @@ void QPainterPrivate::updateState(QPainterState *newState) All the tranformation operations operate on the transformation worldTransform(). A matrix transforms a point in the plane to another point. For more information about the transformation matrix, see - the \l {The Coordinate System} and QTransform documentation. + the \l {Coordinate System} and QTransform documentation. The setWorldTransform() function can replace or add to the currently set worldTransform(). The resetTransform() function resets any @@ -1241,7 +1254,7 @@ void QPainterPrivate::updateState(QPainterState *newState) logical coordinates, and the worldTransform() is identical with the transformation matrix. - See also \l {The Coordinate System} documentation. + See also \l {Coordinate System} \section1 Clipping @@ -1560,7 +1573,6 @@ void QPainter::initFrom(const QWidget *widget) d->engine->setDirty(QPaintEngine::DirtyBrush); d->engine->setDirty(QPaintEngine::DirtyFont); } - d->state->layoutDirection = widget->layoutDirection(); } @@ -1870,7 +1882,7 @@ bool QPainter::begin(QPaintDevice *pd) QWidget *widget = static_cast<QWidget *>(d->original_device); initFrom(widget); } else { - d->state->layoutDirection = QApplication::layoutDirection(); + d->state->layoutDirection = Qt::LayoutDirectionAuto; // make sure we have a font compatible with the paintdevice d->state->deviceFont = d->state->font = QFont(d->state->deviceFont, device()); } @@ -2388,6 +2400,8 @@ void QPainter::setCompositionMode(CompositionMode mode) qWarning("QPainter::setCompositionMode: Painter not active"); return; } + if (d->state->composition_mode == mode) + return; if (d->extended) { d->state->composition_mode = mode; d->extended->compositionModeChanged(); @@ -2703,7 +2717,7 @@ void QPainter::setClipRect(const QRectF &rect, Qt::ClipOperation op) Q_D(QPainter); if (d->extended) { - if (!hasClipping() && (op == Qt::IntersectClip || op == Qt::UniteClip)) + if ((!d->state->clipEnabled && op != Qt::NoClip) || (d->state->clipOperation == Qt::NoClip && op == Qt::UniteClip)) op = Qt::ReplaceClip; if (!d->engine) { @@ -2761,7 +2775,7 @@ void QPainter::setClipRect(const QRect &rect, Qt::ClipOperation op) return; } - if (!hasClipping() && (op == Qt::IntersectClip || op == Qt::UniteClip)) + if ((!d->state->clipEnabled && op != Qt::NoClip) || (d->state->clipOperation == Qt::NoClip && op == Qt::UniteClip)) op = Qt::ReplaceClip; if (d->extended) { @@ -2816,7 +2830,7 @@ void QPainter::setClipRegion(const QRegion &r, Qt::ClipOperation op) return; } - if (!hasClipping() && (op == Qt::IntersectClip || op == Qt::UniteClip)) + if ((!d->state->clipEnabled && op != Qt::NoClip) || (d->state->clipOperation == Qt::NoClip && op == Qt::UniteClip)) op = Qt::ReplaceClip; if (d->extended) { @@ -2877,8 +2891,7 @@ void QPainter::setClipRegion(const QRegion &r, Qt::ClipOperation op) functions is safe. For more information about the coordinate system, transformations - and window-viewport conversion, see \l {The Coordinate System} - documentation. + and window-viewport conversion, see \l {Coordinate System}. \sa setWorldTransform(), QTransform */ @@ -2898,7 +2911,7 @@ void QPainter::setWorldMatrix(const QMatrix &matrix, bool combine) preserve the properties of perspective transformations. \sa {QPainter#Coordinate Transformations}{Coordinate Transformations}, - {The Coordinate System} + {Coordinate System} */ const QMatrix &QPainter::worldMatrix() const @@ -3041,7 +3054,7 @@ void QPainter::setWorldMatrixEnabled(bool enable) Returns true if world transformation is enabled; otherwise returns false. - \sa setWorldMatrixEnabled(), worldTransform(), {The Coordinate System} + \sa setWorldMatrixEnabled(), worldTransform(), {Coordinate System} */ bool QPainter::worldMatrixEnabled() const @@ -3222,7 +3235,7 @@ void QPainter::setClipPath(const QPainterPath &path, Qt::ClipOperation op) return; } - if (!hasClipping() && (op == Qt::IntersectClip || op == Qt::UniteClip)) + if ((!d->state->clipEnabled && op != Qt::NoClip) || (d->state->clipOperation == Qt::NoClip && op == Qt::UniteClip)) op = Qt::ReplaceClip; if (d->extended) { @@ -3383,7 +3396,7 @@ void QPainter::drawPath(const QPainterPath &path) \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 6 \endtable - \sa drawLines(), drawPolyline(), {The Coordinate System} + \sa drawLines(), drawPolyline(), {Coordinate System} */ /*! @@ -3430,7 +3443,7 @@ void QPainter::drawPath(const QPainterPath &path) \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 7 \endtable - \sa drawRects(), drawPolygon(), {The Coordinate System} + \sa drawRects(), drawPolygon(), {Coordinate System} */ /*! @@ -3594,7 +3607,7 @@ void QPainter::drawRects(const QRect *rects, int rectCount) Draws a single point at the given \a position using the current pen's color. - \sa {The Coordinate System} + \sa {Coordinate System} */ /*! @@ -3616,7 +3629,7 @@ void QPainter::drawRects(const QRect *rects, int rectCount) Draws the first \a pointCount points in the array \a points using the current pen's color. - \sa {The Coordinate System} + \sa {Coordinate System} */ void QPainter::drawPoints(const QPointF *points, int pointCount) { @@ -4222,7 +4235,7 @@ void QPainter::drawRoundRect(const QRectF &r, int xRnd, int yRnd) \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 9 \endtable - \sa drawPie(), {The Coordinate System} + \sa drawPie(), {Coordinate System} */ void QPainter::drawEllipse(const QRectF &r) { @@ -4236,8 +4249,6 @@ void QPainter::drawEllipse(const QRectF &r) return; QRectF rect(r.normalized()); - if (rect.isEmpty()) - return; if (d->extended) { d->extended->drawEllipse(rect); @@ -4279,8 +4290,6 @@ void QPainter::drawEllipse(const QRect &r) return; QRect rect(r.normalized()); - if (rect.isEmpty()) - return; if (d->extended) { d->extended->drawEllipse(rect); @@ -4352,7 +4361,7 @@ void QPainter::drawEllipse(const QRect &r) \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 10 \endtable - \sa drawPie(), drawChord(), {The Coordinate System} + \sa drawPie(), drawChord(), {Coordinate System} */ void QPainter::drawArc(const QRectF &r, int a, int alen) @@ -4416,7 +4425,7 @@ void QPainter::drawArc(const QRectF &r, int a, int alen) \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 11 \endtable - \sa drawEllipse(), drawChord(), {The Coordinate System} + \sa drawEllipse(), drawChord(), {Coordinate System} */ void QPainter::drawPie(const QRectF &r, int a, int alen) { @@ -4485,7 +4494,7 @@ void QPainter::drawPie(const QRectF &r, int a, int alen) \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 12 \endtable - \sa drawArc(), drawPie(), {The Coordinate System} + \sa drawArc(), drawPie(), {Coordinate System} */ void QPainter::drawChord(const QRectF &r, int a, int alen) { @@ -4776,7 +4785,7 @@ void QPainter::drawLines(const QPoint *pointPairs, int lineCount) \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 13 \endtable - \sa drawLines(), drawPolygon(), {The Coordinate System} + \sa drawLines(), drawPolygon(), {Coordinate System} */ void QPainter::drawPolyline(const QPointF *points, int pointCount) { @@ -4915,7 +4924,7 @@ void QPainter::drawPolyline(const QPoint *points, int pointCount) \l{Qt::FillRule} for a more detailed description of these fill rules. - \sa drawConvexPolygon(), drawPolyline(), {The Coordinate System} + \sa drawConvexPolygon(), drawPolyline(), {Coordinate System} */ void QPainter::drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule) { @@ -5066,7 +5075,7 @@ void QPainter::drawPolygon(const QPoint *points, int pointCount, Qt::FillRule fi On some platforms (e.g. X11), the drawConvexPolygon() function can be faster than the drawPolygon() function. - \sa drawPolygon(), drawPolyline(), {The Coordinate System} + \sa drawPolygon(), drawPolyline(), {Coordinate System} */ /*! @@ -5411,10 +5420,15 @@ void QPainter::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) scale(w / sw, h / sh); setBackgroundMode(Qt::TransparentMode); setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform); - QBrush brush(d->state->pen.color(), pm); + QBrush brush; + + if (sw == pm.width() && sh == pm.height()) + brush = QBrush(d->state->pen.color(), pm); + else + brush = QBrush(d->state->pen.color(), pm.copy(sx, sy, sw, sh)); + setBrush(brush); setPen(Qt::NoPen); - setBrushOrigin(QPointF(-sx, -sy)); drawRect(QRectF(0, 0, sw, sh)); restore(); @@ -5697,6 +5711,93 @@ void QPainter::drawImage(const QRectF &targetRect, const QImage &image, const QR d->engine->drawImage(QRectF(x, y, w, h), image, QRectF(sx, sy, sw, sh), flags); } + +void qt_draw_glyphs(QPainter *painter, const quint32 *glyphArray, const QPointF *positionArray, + int glyphCount) +{ + QPainterPrivate *painter_d = QPainterPrivate::get(painter); + painter_d->drawGlyphs(glyphArray, positionArray, glyphCount); +} + +void QPainterPrivate::drawGlyphs(const quint32 *glyphArray, const QPointF *positionArray, + int glyphCount) +{ + updateState(state); + + 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]); + positions.append(fp); + } + + if (extended != 0) { + QStaticTextItem staticTextItem; + staticTextItem.color = state->pen.color(); + staticTextItem.font = state->font; + staticTextItem.fontEngine = fontEngine; + staticTextItem.numGlyphs = glyphCount; + staticTextItem.glyphs = reinterpret_cast<glyph_t *>(const_cast<glyph_t *>(glyphArray)); + staticTextItem.glyphPositions = positions.data(); + + extended->drawStaticTextItem(&staticTextItem); + } else { + QTextItemInt textItem; + textItem.f = &state->font; + textItem.fontEngine = fontEngine; + + QVarLengthArray<QFixed, 128> advances(glyphCount); + QVarLengthArray<QGlyphJustification, 128> glyphJustifications(glyphCount); + QVarLengthArray<HB_GlyphAttributes, 128> glyphAttributes(glyphCount); + qMemSet(glyphAttributes.data(), 0, glyphAttributes.size() * sizeof(HB_GlyphAttributes)); + qMemSet(advances.data(), 0, advances.size() * sizeof(QFixed)); + qMemSet(glyphJustifications.data(), 0, glyphJustifications.size() * sizeof(QGlyphJustification)); + + textItem.glyphs.numGlyphs = glyphCount; + textItem.glyphs.glyphs = reinterpret_cast<HB_Glyph *>(const_cast<quint32 *>(glyphArray)); + textItem.glyphs.offsets = positions.data(); + textItem.glyphs.advances_x = advances.data(); + textItem.glyphs.advances_y = advances.data(); + textItem.glyphs.justifications = glyphJustifications.data(); + textItem.glyphs.attributes = glyphAttributes.data(); + + engine->drawTextItem(QPointF(0, 0), textItem); + } +} + +/*! + + \fn void QPainter::drawStaticText(const QPoint &topLeftPosition, const QStaticText &staticText) + \since 4.7 + \overload + + 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 left, int top, const QStaticText &staticText) + \since 4.7 + \overload + + Draws the \a staticText at coordinates \a left and \a top. + + \note The y-position is used as the top of the font. +*/ + /*! \fn void QPainter::drawText(const QPointF &position, const QString &text) @@ -5720,6 +5821,134 @@ void QPainter::drawText(const QPointF &p, const QString &str) } /*! + \since 4.7 + + 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 + the layout of the QStaticText, then the layout will have to be recalculated. Use + QStaticText::prepare() to initialize \a staticText with the font and transformation with which + it will later be drawn. + + 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 for performance improvement. + + \note The y-position is used as the top of the font. + + \sa QStaticText +*/ +void QPainter::drawStaticText(const QPointF &topLeftPosition, const QStaticText &staticText) +{ + Q_D(QPainter); + if (!d->engine || staticText.text().isEmpty() || pen().style() == Qt::NoPen) + return; + + 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(topLeftPosition, this); + return; + } + + bool paintEngineSupportsTransformations = d->extended->type() == QPaintEngine::OpenGL2 + || d->extended->type() == QPaintEngine::OpenVG + || d->extended->type() == QPaintEngine::OpenGL; + + if (paintEngineSupportsTransformations && !staticText_d->untransformedCoordinates) { + staticText_d->untransformedCoordinates = true; + staticText_d->needsRelayout = true; + } else if (!paintEngineSupportsTransformations && staticText_d->untransformedCoordinates) { + staticText_d->untransformedCoordinates = false; + staticText_d->needsRelayout = true; + } + + // 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 = topLeftPosition; + if (!staticText_d->untransformedCoordinates) + transformedPosition = transformedPosition * d->state->matrix; + QTransform oldMatrix; + + // The translation has been applied to transformedPosition. Remove translation + // component from matrix. + if (d->state->matrix.isTranslating() && !staticText_d->untransformedCoordinates) { + qreal m11 = d->state->matrix.m11(); + qreal m12 = d->state->matrix.m12(); + qreal m13 = d->state->matrix.m13(); + qreal m21 = d->state->matrix.m21(); + qreal m22 = d->state->matrix.m22(); + qreal m23 = d->state->matrix.m23(); + qreal m33 = d->state->matrix.m33(); + + oldMatrix = d->state->matrix; + d->state->matrix.setMatrix(m11, m12, m13, + m21, m22, m23, + 0.0, 0.0, m33); + } + + // If the transform is not identical to the text transform, + // we have to relayout the text (for other transformations than plain translation) + bool staticTextNeedsReinit = staticText_d->needsRelayout; + if (!staticText_d->untransformedCoordinates && staticText_d->matrix != d->state->matrix) { + staticText_d->matrix = d->state->matrix; + staticTextNeedsReinit = true; + } + + // Recreate the layout of the static text because the matrix or font has changed + if (staticTextNeedsReinit) + staticText_d->init(); + + if (transformedPosition != staticText_d->position) { // Translate to actual position + QFixed fx = QFixed::fromReal(transformedPosition.x()); + QFixed fy = QFixed::fromReal(transformedPosition.y()); + QFixed oldX = QFixed::fromReal(staticText_d->position.x()); + QFixed oldY = QFixed::fromReal(staticText_d->position.y()); + for (int item=0; item<staticText_d->itemCount;++item) { + QStaticTextItem *textItem = staticText_d->items + item; + for (int i=0; i<textItem->numGlyphs; ++i) { + textItem->glyphPositions[i].x += fx - oldX; + textItem->glyphPositions[i].y += fy - oldY; + } + textItem->userDataNeedsUpdate = true; + } + + staticText_d->position = transformedPosition; + } + + QPen oldPen = d->state->pen; + QColor currentColor = oldPen.color(); + for (int i=0; i<staticText_d->itemCount; ++i) { + QStaticTextItem *item = staticText_d->items + i; + if (item->color.isValid() && currentColor != item->color) { + setPen(item->color); + currentColor = item->color; + } + d->extended->drawStaticTextItem(item); + + drawDecorationForGlyphs(this, item->glyphs, item->glyphPositions, + item->numGlyphs, item->fontEngine, staticText_d->font, + QTextCharFormat()); + } + if (currentColor != oldPen.color()) + setPen(oldPen); + + if (!staticText_d->untransformedCoordinates && oldMatrix.isTranslating()) + d->state->matrix = oldMatrix; +} + +/*! \internal */ void QPainter::drawText(const QPointF &p, const QString &str, int tf, int justificationPadding) @@ -5734,6 +5963,23 @@ void QPainter::drawText(const QPointF &p, const QString &str, int tf, int justif if (!d->engine || str.isEmpty() || pen().style() == Qt::NoPen) return; + if (tf & Qt::TextBypassShaping) { + // Skip harfbuzz complex shaping, shape using glyph advances only + int len = str.length(); + int numGlyphs = len; + QVarLengthGlyphLayoutArray glyphs(len); + QFontEngine *fontEngine = d->state->font.d->engineForScript(QUnicodeTables::Common); + if (!fontEngine->stringToCMap(str.data(), len, &glyphs, &numGlyphs, 0)) { + glyphs.resize(numGlyphs); + if (!fontEngine->stringToCMap(str.data(), len, &glyphs, &numGlyphs, 0)) + Q_ASSERT_X(false, Q_FUNC_INFO, "stringToCMap shouldn't fail twice"); + } + + QTextItemInt gf(glyphs, &d->state->font, str.data(), len, fontEngine); + drawTextItem(p, gf); + return; + } + QStackTextEngine engine(str, d->state->font); engine.option.setTextDirection(d->state->layoutDirection); if (tf & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) { @@ -5939,7 +6185,7 @@ void QPainter::drawText(const QRectF &r, int flags, const QString &str, QRectF * By default, QPainter draws text anti-aliased. - \note The y-position is used as the baseline of the font. + \note The y-position is used as the top of the font. \sa Qt::AlignmentFlag, Qt::TextFlag */ @@ -6014,10 +6260,9 @@ static QPixmap generateWavyPixmap(qreal maxRadius, const QPen &pen) { const qreal radiusBase = qMax(qreal(1), maxRadius); - QString key = QLatin1String("WaveUnderline-"); - key += pen.color().name(); - key += QLatin1Char('-'); - key += QString::number(radiusBase); + QString key = QLatin1Literal("WaveUnderline-") + % pen.color().name() + % HexString<qreal>(radiusBase); QPixmap pixmap; if (QPixmapCache::find(key, pixmap)) @@ -6062,15 +6307,15 @@ static QPixmap generateWavyPixmap(qreal maxRadius, const QPen &pen) return pixmap; } -static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QTextItemInt &ti) +static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QFontEngine *fe, + QTextCharFormat::UnderlineStyle underlineStyle, + const QTextItem::RenderFlags flags, qreal width, + const QTextCharFormat &charFormat) { - QTextCharFormat::UnderlineStyle underlineStyle = ti.underlineStyle; if (underlineStyle == QTextCharFormat::NoUnderline - && !(ti.flags & (QTextItem::StrikeOut | QTextItem::Overline))) + && !(flags & (QTextItem::StrikeOut | QTextItem::Overline))) return; - QFontEngine *fe = ti.fontEngine; - const QPen oldPen = painter->pen(); const QBrush oldBrush = painter->brush(); painter->setBrush(Qt::NoBrush); @@ -6079,7 +6324,7 @@ static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const pen.setWidthF(fe->lineThickness().toReal()); pen.setCapStyle(Qt::FlatCap); - QLineF line(pos.x(), pos.y(), pos.x() + ti.width.toReal(), pos.y()); + QLineF line(pos.x(), pos.y(), pos.x() + width, pos.y()); const qreal underlineOffset = fe->underlinePosition().toReal(); // deliberately ceil the offset to avoid the underline coming too close to @@ -6094,21 +6339,21 @@ static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const painter->save(); painter->translate(0, pos.y() + 1); - QColor uc = ti.charFormat.underlineColor(); + QColor uc = charFormat.underlineColor(); if (uc.isValid()) pen.setColor(uc); // Adapt wave to underlineOffset or pen width, whatever is larger, to make it work on all platforms const QPixmap wave = generateWavyPixmap(qMax(underlineOffset, pen.widthF()), pen); - const int descent = (int) ti.descent.toReal(); + const int descent = (int) fe->descent().toReal(); painter->setBrushOrigin(painter->brushOrigin().x(), 0); - painter->fillRect(pos.x(), 0, qCeil(ti.width.toReal()), qMin(wave.height(), descent), wave); + painter->fillRect(pos.x(), 0, qCeil(width), qMin(wave.height(), descent), wave); painter->restore(); } else if (underlineStyle != QTextCharFormat::NoUnderline) { QLineF underLine(line.x1(), underlinePos, line.x2(), underlinePos); - QColor uc = ti.charFormat.underlineColor(); + QColor uc = charFormat.underlineColor(); if (uc.isValid()) pen.setColor(uc); @@ -6120,14 +6365,14 @@ static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const pen.setStyle(Qt::SolidLine); pen.setColor(oldPen.color()); - if (ti.flags & QTextItem::StrikeOut) { + if (flags & QTextItem::StrikeOut) { QLineF strikeOutLine = line; strikeOutLine.translate(0., - fe->ascent().toReal() / 3.); painter->setPen(pen); painter->drawLine(strikeOutLine); } - if (ti.flags & QTextItem::Overline) { + if (flags & QTextItem::Overline) { QLineF overLine = line; overLine.translate(0., - fe->ascent().toReal()); painter->setPen(pen); @@ -6138,6 +6383,50 @@ static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const painter->setBrush(oldBrush); } +static void drawDecorationForGlyphs(QPainter *painter, const glyph_t *glyphArray, + const QFixedPoint *positions, int glyphCount, + QFontEngine *fontEngine, const QFont &font, + const QTextCharFormat &charFormat) +{ + if (!(font.underline() || font.strikeOut() || font.overline())) + return; + + QFixed leftMost; + QFixed rightMost; + QFixed baseLine; + for (int i=0; i<glyphCount; ++i) { + glyph_metrics_t gm = fontEngine->boundingBox(glyphArray[i]); + if (i == 0 || leftMost > positions[i].x) + leftMost = positions[i].x; + + // We don't support glyphs that do not share a common baseline. If this turns out to + // be a relevant use case, then we need to find clusters of glyphs that share a baseline + // and do a drawTextItemDecorations call per cluster. + if (i == 0 || baseLine < positions[i].y) + baseLine = positions[i].y; + + // We use the advance rather than the actual bounds to match the algorithm in drawText() + if (i == 0 || rightMost < positions[i].x + gm.xoff) + rightMost = positions[i].x + gm.xoff; + } + + QFixed width = rightMost - leftMost; + QTextItem::RenderFlags flags = 0; + + if (font.underline()) + flags |= QTextItem::Underline; + if (font.overline()) + flags |= QTextItem::Overline; + if (font.strikeOut()) + flags |= QTextItem::StrikeOut; + + drawTextItemDecoration(painter, QPointF(leftMost.toReal(), baseLine.toReal()), + fontEngine, + font.underline() ? QTextCharFormat::SingleUnderline + : QTextCharFormat::NoUnderline, flags, + width.toReal(), charFormat); +} + void QPainter::drawTextItem(const QPointF &p, const QTextItem &_ti) { #ifdef QT_DEBUG_DRAW @@ -6268,7 +6557,8 @@ void QPainter::drawTextItem(const QPointF &p, const QTextItem &_ti) else d->engine->drawTextItem(p, ti); } - drawTextItemDecoration(this, p, ti); + drawTextItemDecoration(this, p, ti.fontEngine, ti.underlineStyle, ti.flags, ti.width.toReal(), + ti.charFormat); if (d->state->renderHints != oldRenderHints) { d->state->renderHints = oldRenderHints; @@ -6908,7 +7198,7 @@ bool QPainter::viewTransformEnabled() const The default window rectangle is the same as the device's rectangle. - \sa window(), viewTransformEnabled(), {The Coordinate + \sa window(), viewTransformEnabled(), {Coordinate System#Window-Viewport Conversion}{Window-Viewport Conversion} */ @@ -6972,7 +7262,7 @@ QRect QPainter::window() const The default viewport rectangle is the same as the device's rectangle. - \sa viewport(), viewTransformEnabled() {The Coordinate + \sa viewport(), viewTransformEnabled() {Coordinate System#Window-Viewport Conversion}{Window-Viewport Conversion} */ @@ -7056,7 +7346,7 @@ QRect QPainter::viewport() const Enables view transformations if \a enable is true, or disables view transformations if \a enable is false. - \sa viewTransformEnabled(), {The Coordinate System#Window-Viewport + \sa viewTransformEnabled(), {Coordinate System#Window-Viewport Conversion}{Window-Viewport Conversion} */ @@ -7806,7 +8096,7 @@ start_lengthVariant: for (int i = 0; i < textLayout.lineCount(); i++) { QTextLine line = textLayout.lineAt(i); - qreal advance = textLayout.engine()->lines[i].textAdvance.toReal(); + qreal advance = line.horizontalAdvance(); if (tf & Qt::AlignRight) xoff = r.width() - advance; else if (tf & Qt::AlignHCenter) @@ -7825,7 +8115,10 @@ start_lengthVariant: Sets the layout direction used by the painter when drawing text, to the specified \a direction. - \sa layoutDirection(), drawText(), {QPainter#Settings}{Settings} + The default is Qt::LayoutDirectionAuto, which will implicitly determine the + direction from the text drawn. + + \sa QTextOption::setTextDirection(), layoutDirection(), drawText(), {QPainter#Settings}{Settings} */ void QPainter::setLayoutDirection(Qt::LayoutDirection direction) { @@ -7837,12 +8130,12 @@ void QPainter::setLayoutDirection(Qt::LayoutDirection direction) /*! Returns the layout direction used by the painter when drawing text. - \sa setLayoutDirection(), drawText(), {QPainter#Settings}{Settings} + \sa QTextOption::textDirection(), setLayoutDirection(), drawText(), {QPainter#Settings}{Settings} */ Qt::LayoutDirection QPainter::layoutDirection() const { Q_D(const QPainter); - return d->state ? d->state->layoutDirection : Qt::LeftToRight; + return d->state ? d->state->layoutDirection : Qt::LayoutDirectionAuto; } QPainterState::QPainterState(const QPainterState *s) @@ -8505,7 +8798,7 @@ QPainterPath QPaintEngineState::clipPath() const } /*! - Returns wether clipping is enabled or not in the current paint + Returns whether clipping is enabled or not in the current paint engine state. This variable should only be used when the state() returns a @@ -8715,6 +9008,176 @@ QTransform QPainter::combinedTransform() const return d->state->worldMatrix * d->viewTransform(); } +/*! + \since 4.7 + + This function is used to draw \a pixmap, or a sub-rectangle of \a pixmap, + at multiple positions with different scale, rotation and opacity. \a + fragments is an array of \a fragmentCount elements specifying the + parameters used to draw each pixmap fragment. The \a hints + parameter can be used to pass in drawing hints. + + This function is potentially faster than multiple calls to drawPixmap(), + since the backend can optimize state changes. + + \sa QPainter::PixmapFragment, QPainter::PixmapFragmentHint +*/ + +void QPainter::drawPixmapFragments(const PixmapFragment *fragments, int fragmentCount, + const QPixmap &pixmap, PixmapFragmentHints hints) +{ + Q_D(QPainter); + + if (!d->engine || pixmap.isNull()) + return; + +#ifndef QT_NO_DEBUG + for (int i = 0; i < fragmentCount; ++i) { + QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop, + fragments[i].width, fragments[i].height); + if (!(QRectF(pixmap.rect()).contains(sourceRect))) + qWarning("QPainter::drawPixmapFragments - the source rect is not contained by the pixmap's rectangle"); + } +#endif + + if (d->engine->isExtended()) { + d->extended->drawPixmapFragments(fragments, fragmentCount, pixmap, hints); + } else { + qreal oldOpacity = opacity(); + QTransform oldTransform = transform(); + + for (int i = 0; i < fragmentCount; ++i) { + QTransform transform = oldTransform; + qreal xOffset = 0; + qreal yOffset = 0; + if (fragments[i].rotation == 0) { + xOffset = fragments[i].x; + yOffset = fragments[i].y; + } else { + transform.translate(fragments[i].x, fragments[i].y); + transform.rotate(fragments[i].rotation); + } + setOpacity(oldOpacity * fragments[i].opacity); + setTransform(transform); + + qreal w = fragments[i].scaleX * fragments[i].width; + qreal h = fragments[i].scaleY * fragments[i].height; + QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop, + fragments[i].width, fragments[i].height); + drawPixmap(QRectF(-0.5 * w + xOffset, -0.5 * h + yOffset, w, h), pixmap, sourceRect); + } + + setOpacity(oldOpacity); + setTransform(oldTransform); + } +} + +/*! + \since 4.7 + \class QPainter::PixmapFragment + + \brief This class is used in conjunction with the + QPainter::drawPixmapFragments() function to specify how a pixmap, or + sub-rect of a pixmap, is drawn. + + The \a sourceLeft, \a sourceTop, \a width and \a height variables are used + as a source rectangle within the pixmap passed into the + QPainter::drawPixmapFragments() function. The variables \a x, \a y, \a + width and \a height are used to calculate the target rectangle that is + drawn. \a x and \a y denotes the center of the target rectangle. The \a + width and \a heigth in the target rectangle is scaled by the \a scaleX and + \a scaleY values. The resulting target rectangle is then rotated \a + rotation degrees around the \a x, \a y center point. + + \sa QPainter::drawPixmapFragments() +*/ + +/*! + \since 4.7 + + This is a convenience function that returns a QPainter::PixmapFragment that is + initialized with the \a pos, \a sourceRect, \a scaleX, \a scaleY, \a + rotation, \a opacity parameters. +*/ + +QPainter::PixmapFragment QPainter::PixmapFragment::create(const QPointF &pos, const QRectF &sourceRect, + qreal scaleX, qreal scaleY, qreal rotation, + qreal opacity) +{ + PixmapFragment fragment = {pos.x(), pos.y(), sourceRect.x(), sourceRect.y(), sourceRect.width(), + sourceRect.height(), scaleX, scaleY, rotation, opacity}; + return fragment; +} + +/*! + \variable QPainter::PixmapFragment::x + \brief the x coordinate of center point in the target rectangle. +*/ + +/*! + \variable QPainter::PixmapFragment::y + \brief the y coordinate of the center point in the target rectangle. +*/ + +/*! + \variable QPainter::PixmapFragment::sourceLeft + \brief the left coordinate of the source rectangle. +*/ + +/*! + \variable QPainter::PixmapFragment::sourceTop + \brief the top coordinate of the source rectangle. +*/ + +/*! + \variable QPainter::PixmapFragment::width + + \brief the width of the source rectangle and is used to calculate the width + of the target rectangle. +*/ + +/*! + \variable QPainter::PixmapFragment::height + + \brief the height of the source rectangle and is used to calculate the + height of the target rectangle. +*/ + +/*! + \variable QPainter::PixmapFragment::scaleX + \brief the horizontal scale of the target rectangle. +*/ + +/*! + \variable QPainter::PixmapFragment::scaleY + \brief the vertical scale of the target rectangle. +*/ + +/*! + \variable QPainter::PixmapFragment::rotation + + \brief the rotation of the target rectangle in degrees. The target + rectangle is rotated after it has been scaled. +*/ + +/*! + \variable QPainter::PixmapFragment::opacity + + \brief the opacity of the target rectangle, where 0.0 is fully transparent + and 1.0 is fully opaque. +*/ + +/*! + \since 4.7 + + \enum QPainter::PixmapFragmentHint + + \value OpaqueHint Indicates that the pixmap fragments to be drawn are + opaque. Opaque fragments are potentially faster to draw. + + \sa QPainter::drawPixmapFragments(), QPainter::PixmapFragment +*/ + void qt_draw_helper(QPainterPrivate *p, const QPainterPath &path, QPainterPrivate::DrawOperation operation) { p->draw_helper(path, operation); diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h index ffddcba..edfb67e 100644 --- a/src/gui/painting/qpainter.h +++ b/src/gui/painting/qpainter.h @@ -78,6 +78,7 @@ class QPolygon; class QTextItem; class QMatrix; class QTransform; +class QStaticText; class QPainterPrivateDeleter; @@ -98,6 +99,29 @@ public: Q_DECLARE_FLAGS(RenderHints, RenderHint) + class PixmapFragment { + public: + qreal x; + qreal y; + qreal sourceLeft; + qreal sourceTop; + qreal width; + qreal height; + qreal scaleX; + qreal scaleY; + qreal rotation; + qreal opacity; + static PixmapFragment Q_GUI_EXPORT create(const QPointF &pos, const QRectF &sourceRect, + qreal scaleX = 1, qreal scaleY = 1, + qreal rotation = 0, qreal opacity = 1); + }; + + enum PixmapFragmentHint { + OpaqueHint = 0x01 + }; + + Q_DECLARE_FLAGS(PixmapFragmentHints, PixmapFragmentHint) + QPainter(); explicit QPainter(QPaintDevice *); ~QPainter(); @@ -351,6 +375,9 @@ public: inline void drawPixmap(const QRect &r, const QPixmap &pm); inline void drawPixmap(int x, int y, int w, int h, const QPixmap &pm); + void drawPixmapFragments(const PixmapFragment *fragments, int fragmentCount, + const QPixmap &pixmap, PixmapFragmentHints hints = 0); + void drawImage(const QRectF &targetRect, const QImage &image, const QRectF &sourceRect, Qt::ImageConversionFlags flags = Qt::AutoColor); inline void drawImage(const QRect &targetRect, const QImage &image, const QRect &sourceRect, @@ -369,6 +396,10 @@ public: void setLayoutDirection(Qt::LayoutDirection direction); Qt::LayoutDirection layoutDirection() const; + 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); inline void drawText(int x, int y, const QString &s); @@ -896,6 +927,16 @@ inline void QPainter::drawImage(int x, int y, const QImage &image, int sx, int s drawImage(QRectF(x, y, -1, -1), image, QRectF(sx, sy, sw, sh), flags); } +inline void QPainter::drawStaticText(const QPoint &p, const QStaticText &staticText) +{ + drawStaticText(QPointF(p), staticText); +} + +inline void QPainter::drawStaticText(int x, int y, const QStaticText &staticText) +{ + drawStaticText(QPointF(x, y), staticText); +} + inline void QPainter::drawTextItem(const QPoint &p, const QTextItem &ti) { drawTextItem(QPointF(p), ti); diff --git a/src/gui/painting/qpainter_p.h b/src/gui/painting/qpainter_p.h index 02a91aa..9362dbe 100644 --- a/src/gui/painting/qpainter_p.h +++ b/src/gui/painting/qpainter_p.h @@ -228,6 +228,7 @@ public: void draw_helper(const QPainterPath &path, DrawOperation operation = StrokeAndFillDraw); void drawStretchedGradient(const QPainterPath &path, DrawOperation operation); void drawOpaqueBackground(const QPainterPath &path, DrawOperation operation); + void drawGlyphs(const quint32 *glyphArray, const QPointF *positionArray, int glyphCount); void updateMatrix(); void updateInvMatrix(); @@ -238,6 +239,11 @@ public: void checkEmulation(); + static QPainterPrivate *get(QPainter *painter) + { + return painter->d_ptr.data(); + } + QTransform viewTransform() const; static bool attachPainterPrivate(QPainter *q, QPaintDevice *pdev); void detachPainterPrivate(QPainter *q); @@ -252,6 +258,8 @@ public: }; Q_GUI_EXPORT void qt_draw_helper(QPainterPrivate *p, const QPainterPath &path, QPainterPrivate::DrawOperation operation); +Q_GUI_EXPORT void qt_draw_glyphs(QPainter *painter, const quint32 *glyphArray, + const QPointF *positionArray, int glyphCount); QString qt_generate_brush_key(const QBrush &brush); diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index fba3595..ffd0d5c 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -1257,6 +1257,8 @@ Qt::FillRule QPainterPath::fillRule() const void QPainterPath::setFillRule(Qt::FillRule fillRule) { ensureData(); + if (d_func()->fillRule == fillRule) + return; detach(); d_func()->fillRule = fillRule; @@ -1912,7 +1914,7 @@ static bool qt_painterpath_check_crossing(const QPainterPath *path, const QRectF case QPainterPath::MoveToElement: if (i > 0 - && qFuzzyCompare(last_pt.x(), last_start.y()) + && qFuzzyCompare(last_pt.x(), last_start.x()) && qFuzzyCompare(last_pt.y(), last_start.y()) && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(), last_start.x(), last_start.y(), rect)) @@ -2305,7 +2307,7 @@ QPainterPath &QPainterPath::operator-=(const QPainterPath &other) Writes the given painter \a path to the given \a stream, and returns a reference to the \a stream. - \sa {Format of the QDataStream Operators} + \sa {Serializing Qt Data Types} */ QDataStream &operator<<(QDataStream &s, const QPainterPath &p) { @@ -2332,7 +2334,7 @@ QDataStream &operator<<(QDataStream &s, const QPainterPath &p) Reads a painter path from the given \a stream into the specified \a path, and returns a reference to the \a stream. - \sa {Format of the QDataStream Operators} + \sa {Serializing Qt Data Types} */ QDataStream &operator>>(QDataStream &s, QPainterPath &p) { @@ -3165,6 +3167,8 @@ void QPainterPath::addRoundRect(const QRectF &r, int xRnd, int yRnd) Set operations on paths will treat the paths as areas. Non-closed paths will be treated as implicitly closed. + Bezier curves may be flattened to line segments due to numerical instability of + doing bezier curve intersections. \sa intersected(), subtracted() */ @@ -3180,6 +3184,8 @@ QPainterPath QPainterPath::united(const QPainterPath &p) const \since 4.3 Returns a path which is the intersection of this path's fill area and \a p's fill area. + Bezier curves may be flattened to line segments due to numerical instability of + doing bezier curve intersections. */ QPainterPath QPainterPath::intersected(const QPainterPath &p) const { @@ -3196,7 +3202,8 @@ QPainterPath QPainterPath::intersected(const QPainterPath &p) const Set operations on paths will treat the paths as areas. Non-closed paths will be treated as implicitly closed. - + Bezier curves may be flattened to line segments due to numerical instability of + doing bezier curve intersections. */ QPainterPath QPainterPath::subtracted(const QPainterPath &p) const { @@ -3225,6 +3232,8 @@ QPainterPath QPainterPath::subtractedInverted(const QPainterPath &p) const Returns a simplified version of this path. This implies merging all subpaths that intersect, and returning a path containing no intersecting edges. Consecutive parallel lines will also be merged. The simplified path will always use the default fill rule, Qt::OddEvenFill. + Bezier curves may be flattened to line segments due to numerical instability of + doing bezier curve intersections. */ QPainterPath QPainterPath::simplified() const { diff --git a/src/gui/painting/qpainterpath.h b/src/gui/painting/qpainterpath.h index 296ce33..15d83b8 100644 --- a/src/gui/painting/qpainterpath.h +++ b/src/gui/painting/qpainterpath.h @@ -288,6 +288,8 @@ public: QPainterPath createStroke(const QPainterPath &path) const; private: + Q_DISABLE_COPY(QPainterPathStroker) + friend class QX11PaintEngine; QScopedPointer<QPainterPathStrokerPrivate> d_ptr; diff --git a/src/gui/painting/qpathclipper.cpp b/src/gui/painting/qpathclipper.cpp index bc81514..a17b7c1 100644 --- a/src/gui/painting/qpathclipper.cpp +++ b/src/gui/painting/qpathclipper.cpp @@ -43,6 +43,7 @@ #include <private/qbezier_p.h> #include <private/qdatabuffer_p.h> +#include <private/qnumeric_p.h> #include <qmath.h> /** @@ -85,9 +86,11 @@ static qreal dot(const QPointF &a, const QPointF &b) return a.x() * b.x() + a.y() * b.y(); } -static QPointF normalize(const QPointF &p) +static void normalize(double &x, double &y) { - return p / qSqrt(p.x() * p.x() + p.y() * p.y()); + double reciprocal = 1 / qSqrt(x * x + y * y); + x *= reciprocal; + y *= reciprocal; } struct QIntersection @@ -105,22 +108,9 @@ public: bool hasIntersections(const QPathSegments &a, const QPathSegments &b) const; private: - void intersectBeziers(const QBezier &one, const QBezier &two, QVector<QPair<qreal, qreal> > &t, QDataBuffer<QIntersection> &intersections); - void intersectLines(const QLineF &a, const QLineF &b, QDataBuffer<QIntersection> &intersections); - - bool beziersIntersect(const QBezier &one, const QBezier &two) const; bool linesIntersect(const QLineF &a, const QLineF &b) const; }; -bool QIntersectionFinder::beziersIntersect(const QBezier &one, const QBezier &two) const -{ - return (comparePoints(one.pt1(), two.pt1()) && comparePoints(one.pt2(), two.pt2()) - && comparePoints(one.pt3(), two.pt3()) && comparePoints(one.pt4(), two.pt4())) - || (comparePoints(one.pt1(), two.pt4()) && comparePoints(one.pt2(), two.pt3()) - && comparePoints(one.pt3(), two.pt2()) && comparePoints(one.pt4(), two.pt1())) - || QBezier::findIntersections(one, two, 0); -} - bool QIntersectionFinder::linesIntersect(const QLineF &a, const QLineF &b) const { const QPointF p1 = a.p1(); @@ -174,11 +164,6 @@ bool QIntersectionFinder::linesIntersect(const QLineF &a, const QLineF &b) const return false; } - // if the lines are not parallel and share a common end point, then they - // don't intersect - if (p1_equals_q1 || p1_equals_q2 || p2_equals_q1 || p2_equals_q2) - return false; - const qreal invPar = 1 / par; const qreal tp = (qDelta.y() * (q1.x() - p1.x()) - @@ -193,49 +178,224 @@ bool QIntersectionFinder::linesIntersect(const QLineF &a, const QLineF &b) const return tq >= 0 && tq <= 1; } -void QIntersectionFinder::intersectBeziers(const QBezier &one, const QBezier &two, QVector<QPair<qreal, qreal> > &t, QDataBuffer<QIntersection> &intersections) +bool QIntersectionFinder::hasIntersections(const QPathSegments &a, const QPathSegments &b) const { - if ((comparePoints(one.pt1(), two.pt1()) && comparePoints(one.pt2(), two.pt2()) - && comparePoints(one.pt3(), two.pt3()) && comparePoints(one.pt4(), two.pt4())) - || (comparePoints(one.pt1(), two.pt4()) && comparePoints(one.pt2(), two.pt3()) - && comparePoints(one.pt3(), two.pt2()) && comparePoints(one.pt4(), two.pt1()))) { + if (a.segments() == 0 || b.segments() == 0) + return false; - return; + const QRectF &rb0 = b.elementBounds(0); + + qreal minX = rb0.left(); + qreal minY = rb0.top(); + qreal maxX = rb0.right(); + qreal maxY = rb0.bottom(); + + for (int i = 1; i < b.segments(); ++i) { + const QRectF &r = b.elementBounds(i); + minX = qMin(minX, r.left()); + minY = qMin(minY, r.top()); + maxX = qMax(maxX, r.right()); + maxY = qMax(maxY, r.bottom()); } - t.clear(); + QRectF rb(minX, minY, maxX - minX, maxY - minY); - if (!QBezier::findIntersections(one, two, &t)) - return; + for (int i = 0; i < a.segments(); ++i) { + const QRectF &r1 = a.elementBounds(i); - int count = t.size(); - - for (int i = 0; i < count; ++i) { - qreal alpha_p = t.at(i).first; - qreal alpha_q = t.at(i).second; - - QPointF pt; - if (qFuzzyIsNull(alpha_p)) { - pt = one.pt1(); - } else if (qFuzzyIsNull(alpha_p - 1)) { - pt = one.pt4(); - } else if (qFuzzyIsNull(alpha_q)) { - pt = two.pt1(); - } else if (qFuzzyIsNull(alpha_q - 1)) { - pt = two.pt4(); - } else { - pt = one.pointAt(alpha_p); + if (r1.left() > rb.right() || rb.left() > r1.right()) + continue; + if (r1.top() > rb.bottom() || rb.top() > r1.bottom()) + continue; + + for (int j = 0; j < b.segments(); ++j) { + const QRectF &r2 = b.elementBounds(j); + + if (r1.left() > r2.right() || r2.left() > r1.right()) + continue; + if (r1.top() > r2.bottom() || r2.top() > r1.bottom()) + continue; + + if (linesIntersect(a.lineAt(i), b.lineAt(j))) + return true; } + } + + return false; +} - QIntersection intersection; - intersection.alphaA = alpha_p; - intersection.alphaB = alpha_q; - intersection.pos = pt; - intersections.add(intersection); +namespace { +struct TreeNode +{ + qreal splitLeft; + qreal splitRight; + bool leaf; + + int lowestLeftIndex; + int lowestRightIndex; + + union { + struct { + int first; + int last; + } interval; + struct { + int left; + int right; + } children; + } index; +}; + +struct RectF +{ + qreal x1; + qreal y1; + qreal x2; + qreal y2; +}; + +class SegmentTree +{ +public: + SegmentTree(QPathSegments &segments); + + QRectF boundingRect() const; + + void produceIntersections(int segment); + +private: + TreeNode buildTree(int first, int last, int depth, const RectF &bounds); + + void produceIntersectionsLeaf(const TreeNode &node, int segment); + void produceIntersections(const TreeNode &node, int segment, const RectF &segmentBounds, const RectF &nodeBounds, int axis); + void intersectLines(const QLineF &a, const QLineF &b, QDataBuffer<QIntersection> &intersections); + + QPathSegments &m_segments; + QVector<int> m_index; + + RectF m_bounds; + + QVector<TreeNode> m_tree; + QDataBuffer<QIntersection> m_intersections; +}; + +SegmentTree::SegmentTree(QPathSegments &segments) + : m_segments(segments), + m_intersections(0) +{ + m_bounds.x1 = qt_inf(); + m_bounds.y1 = qt_inf(); + m_bounds.x2 = -qt_inf(); + m_bounds.y2 = -qt_inf(); + + m_index.resize(m_segments.segments()); + + for (int i = 0; i < m_index.size(); ++i) { + m_index[i] = i; + + const QRectF &segmentBounds = m_segments.elementBounds(i); + + if (segmentBounds.left() < m_bounds.x1) + m_bounds.x1 = segmentBounds.left(); + if (segmentBounds.top() < m_bounds.y1) + m_bounds.y1 = segmentBounds.top(); + if (segmentBounds.right() > m_bounds.x2) + m_bounds.x2 = segmentBounds.right(); + if (segmentBounds.bottom() > m_bounds.y2) + m_bounds.y2 = segmentBounds.bottom(); + } + + m_tree.resize(1); + + TreeNode root = buildTree(0, m_index.size(), 0, m_bounds); + m_tree[0] = root; +} + +QRectF SegmentTree::boundingRect() const +{ + return QRectF(QPointF(m_bounds.x1, m_bounds.y1), + QPointF(m_bounds.x2, m_bounds.y2)); +} + +static inline qreal coordinate(const QPointF &pos, int axis) +{ + return axis == 0 ? pos.x() : pos.y(); +} + +TreeNode SegmentTree::buildTree(int first, int last, int depth, const RectF &bounds) +{ + if (depth >= 24 || (last - first) <= 10) { + TreeNode node; + node.leaf = true; + node.index.interval.first = first; + node.index.interval.last = last; + + return node; } + + int splitAxis = (depth & 1); + + TreeNode node; + node.leaf = false; + + qreal split = 0.5f * ((&bounds.x1)[splitAxis] + (&bounds.x2)[splitAxis]); + + node.splitLeft = (&bounds.x1)[splitAxis]; + node.splitRight = (&bounds.x2)[splitAxis]; + + node.lowestLeftIndex = INT_MAX; + node.lowestRightIndex = INT_MAX; + + const int treeSize = m_tree.size(); + + node.index.children.left = treeSize; + node.index.children.right = treeSize + 1; + + m_tree.resize(treeSize + 2); + + int l = first; + int r = last - 1; + + // partition into left and right sets + while (l <= r) { + const int index = m_index.at(l); + const QRectF &segmentBounds = m_segments.elementBounds(index); + + qreal lowCoordinate = coordinate(segmentBounds.topLeft(), splitAxis); + + if (coordinate(segmentBounds.center(), splitAxis) < split) { + qreal highCoordinate = coordinate(segmentBounds.bottomRight(), splitAxis); + if (highCoordinate > node.splitLeft) + node.splitLeft = highCoordinate; + if (index < node.lowestLeftIndex) + node.lowestLeftIndex = index; + ++l; + } else { + if (lowCoordinate < node.splitRight) + node.splitRight = lowCoordinate; + if (index < node.lowestRightIndex) + node.lowestRightIndex = index; + qSwap(m_index[l], m_index[r]); + --r; + } + } + + RectF lbounds = bounds; + (&lbounds.x2)[splitAxis] = node.splitLeft; + + RectF rbounds = bounds; + (&rbounds.x1)[splitAxis] = node.splitRight; + + TreeNode left = buildTree(first, l, depth + 1, lbounds); + m_tree[node.index.children.left] = left; + + TreeNode right = buildTree(l, last, depth + 1, rbounds); + m_tree[node.index.children.right] = right; + + return node; } -void QIntersectionFinder::intersectLines(const QLineF &a, const QLineF &b, QDataBuffer<QIntersection> &intersections) +void SegmentTree::intersectLines(const QLineF &a, const QLineF &b, QDataBuffer<QIntersection> &intersections) { const QPointF p1 = a.p1(); const QPointF p2 = a.p2(); @@ -357,149 +517,86 @@ void QIntersectionFinder::intersectLines(const QLineF &a, const QLineF &b, QData intersections.add(intersection); } -static const QBezier bezierFromLine(const QLineF &line) +void SegmentTree::produceIntersections(int segment) { - const QPointF p1 = line.p1(); - const QPointF p2 = line.p2(); - const QPointF delta = (p2 - p1) / 3; - return QBezier::fromPoints(p1, p1 + delta, p1 + 2 * delta, p2); -} + const QRectF &segmentBounds = m_segments.elementBounds(segment); -bool QIntersectionFinder::hasIntersections(const QPathSegments &a, const QPathSegments &b) const -{ - QBezier tempA; - QBezier tempB; - - if (a.segments() == 0 || b.segments() == 0) - return false; - - const QRectF &rb0 = b.elementBounds(0); - - qreal minX = rb0.left(); - qreal minY = rb0.top(); - qreal maxX = rb0.right(); - qreal maxY = rb0.bottom(); + RectF sbounds; + sbounds.x1 = segmentBounds.left(); + sbounds.y1 = segmentBounds.top(); + sbounds.x2 = segmentBounds.right(); + sbounds.y2 = segmentBounds.bottom(); - for (int i = 1; i < b.segments(); ++i) { - const QRectF &r = b.elementBounds(i); - minX = qMin(minX, r.left()); - minY = qMin(minY, r.top()); - maxX = qMax(maxX, r.right()); - maxY = qMax(maxY, r.bottom()); - } + produceIntersections(m_tree.at(0), segment, sbounds, m_bounds, 0); +} - QRectF rb(minX, minY, maxX - minX, maxY - minY); +void SegmentTree::produceIntersectionsLeaf(const TreeNode &node, int segment) +{ + const QRectF &r1 = m_segments.elementBounds(segment); + const QLineF lineA = m_segments.lineAt(segment); - for (int i = 0; i < a.segments(); ++i) { - const QBezier *bezierA = a.bezierAt(i); - bool isBezierA = bezierA != 0; + for (int i = node.index.interval.first; i < node.index.interval.last; ++i) { + const int other = m_index.at(i); + if (other >= segment) + continue; - const QRectF &r1 = a.elementBounds(i); + const QRectF &r2 = m_segments.elementBounds(other); - if (r1.left() > rb.right() || rb.left() > r1.right()) + if (r1.left() > r2.right() || r2.left() > r1.right()) continue; - if (r1.top() > rb.bottom() || rb.top() > r1.bottom()) + if (r1.top() > r2.bottom() || r2.top() > r1.bottom()) continue; - for (int j = 0; j < b.segments(); ++j) { - const QRectF &r2 = b.elementBounds(j); + m_intersections.reset(); - if (r1.left() > r2.right() || r2.left() > r1.right()) - continue; - if (r1.top() > r2.bottom() || r2.top() > r1.bottom()) - continue; + const QLineF lineB = m_segments.lineAt(other); - bool isBezierB = b.bezierAt(j) != 0; + intersectLines(lineA, lineB, m_intersections); - if (isBezierA || isBezierB) { - const QBezier *bezierB; - if (isBezierB) { - bezierB = b.bezierAt(j); - } else { - tempB = bezierFromLine(b.lineAt(j)); - bezierB = &tempB; - } + for (int k = 0; k < m_intersections.size(); ++k) { + QPathSegments::Intersection i_isect, j_isect; + i_isect.vertex = j_isect.vertex = m_segments.addPoint(m_intersections.at(k).pos); - if (!bezierA) { - tempA = bezierFromLine(a.lineAt(i)); - bezierA = &tempA; - } + i_isect.t = m_intersections.at(k).alphaA; + j_isect.t = m_intersections.at(k).alphaB; - if (beziersIntersect(*bezierA, *bezierB)) - return true; - } else { - if (linesIntersect(a.lineAt(i), b.lineAt(j))) - return true; - } + i_isect.next = 0; + j_isect.next = 0; + + m_segments.addIntersection(segment, i_isect); + m_segments.addIntersection(other, j_isect); } } - - return false; } -void QIntersectionFinder::produceIntersections(QPathSegments &segments) +void SegmentTree::produceIntersections(const TreeNode &node, int segment, const RectF &segmentBounds, const RectF &nodeBounds, int axis) { - QBezier tempA; - QBezier tempB; - - QVector<QPair<qreal, qreal> > t; - QDataBuffer<QIntersection> intersections; - - for (int i = 0; i < segments.segments(); ++i) { - const QBezier *bezierA = segments.bezierAt(i); - bool isBezierA = bezierA != 0; - - const QRectF &r1 = segments.elementBounds(i); - - for (int j = 0; j < i; ++j) { - const QRectF &r2 = segments.elementBounds(j); - - if (r1.left() > r2.right() || r2.left() > r1.right()) - continue; - if (r1.top() > r2.bottom() || r2.top() > r1.bottom()) - continue; - - intersections.reset(); - - bool isBezierB = segments.bezierAt(j) != 0; - - if (isBezierA || isBezierB) { - const QBezier *bezierB; - if (isBezierB) { - bezierB = segments.bezierAt(j); - } else { - tempB = bezierFromLine(segments.lineAt(j)); - bezierB = &tempB; - } + if (node.leaf) { + produceIntersectionsLeaf(node, segment); + return; + } - if (!bezierA) { - tempA = bezierFromLine(segments.lineAt(i)); - bezierA = &tempA; - } + RectF lbounds = nodeBounds; + (&lbounds.x2)[axis] = node.splitLeft; - intersectBeziers(*bezierA, *bezierB, t, intersections); - } else { - const QLineF lineA = segments.lineAt(i); - const QLineF lineB = segments.lineAt(j); + RectF rbounds = nodeBounds; + (&rbounds.x1)[axis] = node.splitRight; - intersectLines(lineA, lineB, intersections); - } + if (segment > node.lowestLeftIndex && (&segmentBounds.x1)[axis] <= node.splitLeft) + produceIntersections(m_tree.at(node.index.children.left), segment, segmentBounds, lbounds, !axis); - for (int k = 0; k < intersections.size(); ++k) { - QPathSegments::Intersection i_isect, j_isect; - i_isect.vertex = j_isect.vertex = segments.addPoint(intersections.at(k).pos); + if (segment > node.lowestRightIndex && (&segmentBounds.x2)[axis] >= node.splitRight) + produceIntersections(m_tree.at(node.index.children.right), segment, segmentBounds, rbounds, !axis); +} - i_isect.t = intersections.at(k).alphaA; - j_isect.t = intersections.at(k).alphaB; +} - i_isect.next = 0; - j_isect.next = 0; +void QIntersectionFinder::produceIntersections(QPathSegments &segments) +{ + SegmentTree tree(segments); - segments.addIntersection(i, i_isect); - segments.addIntersection(j, j_isect); - } - } - } + for (int i = 0; i < segments.segments(); ++i) + tree.produceIntersections(i); } class QKdPointTree @@ -712,7 +809,7 @@ void QWingedEdge::intersectAndAdd() for (int i = 0; i < m_segments.points(); ++i) addVertex(m_segments.pointAt(i)); - QDataBuffer<QPathSegments::Intersection> intersections; + QDataBuffer<QPathSegments::Intersection> intersections(m_segments.segments()); for (int i = 0; i < m_segments.segments(); ++i) { intersections.reset(); @@ -731,62 +828,49 @@ void QWingedEdge::intersectAndAdd() qSort(intersections.data(), intersections.data() + intersections.size()); - const QBezier *bezier = m_segments.bezierAt(i); - if (bezier) { - int first = m_segments.segmentAt(i).va; - int second = m_segments.segmentAt(i).vb; - - qreal alpha = 0.0; - int last = first; - for (int j = 0; j < intersections.size(); ++j) { - const QPathSegments::Intersection &isect = intersections.at(j); - - addBezierEdge(bezier, last, isect.vertex, alpha, isect.t, pathId); - - alpha = isect.t; - last = isect.vertex; - } - - addBezierEdge(bezier, last, second, alpha, 1.0, pathId); - } else { - int first = m_segments.segmentAt(i).va; - int second = m_segments.segmentAt(i).vb; - - int last = first; - for (int j = 0; j < intersections.size(); ++j) { - const QPathSegments::Intersection &isect = intersections.at(j); + int first = m_segments.segmentAt(i).va; + int second = m_segments.segmentAt(i).vb; - QPathEdge *ep = edge(addEdge(last, isect.vertex)); + int last = first; + for (int j = 0; j < intersections.size(); ++j) { + const QPathSegments::Intersection &isect = intersections.at(j); - if (ep) { - const int dir = m_segments.pointAt(last).y() < m_segments.pointAt(isect.vertex).y() ? 1 : -1; - if (pathId == 0) - ep->windingA += dir; - else - ep->windingB += dir; - } - - last = isect.vertex; - } - - QPathEdge *ep = edge(addEdge(last, second)); + QPathEdge *ep = edge(addEdge(last, isect.vertex)); if (ep) { - const int dir = m_segments.pointAt(last).y() < m_segments.pointAt(second).y() ? 1 : -1; + const int dir = m_segments.pointAt(last).y() < m_segments.pointAt(isect.vertex).y() ? 1 : -1; if (pathId == 0) ep->windingA += dir; else ep->windingB += dir; } + + last = isect.vertex; + } + + QPathEdge *ep = edge(addEdge(last, second)); + + if (ep) { + const int dir = m_segments.pointAt(last).y() < m_segments.pointAt(second).y() ? 1 : -1; + if (pathId == 0) + ep->windingA += dir; + else + ep->windingB += dir; } } } -QWingedEdge::QWingedEdge() +QWingedEdge::QWingedEdge() : + m_edges(0), + m_vertices(0), + m_segments(0) { } -QWingedEdge::QWingedEdge(const QPainterPath &subject, const QPainterPath &clip) +QWingedEdge::QWingedEdge(const QPainterPath &subject, const QPainterPath &clip) : + m_edges(subject.length()), + m_vertices(subject.length()), + m_segments(subject.length()) { m_segments.setPath(subject); m_segments.addPath(clip); @@ -832,7 +916,6 @@ static bool isLine(const QBezier &bezier) void QPathSegments::setPath(const QPainterPath &path) { m_points.reset(); - m_beziers.reset(); m_intersections.reset(); m_segments.reset(); @@ -879,8 +962,25 @@ void QPathSegments::addPath(const QPainterPath &path) if (isLine(bezier)) { m_segments << Segment(m_pathId, last, current); } else { - m_segments << Segment(m_pathId, last, current, m_beziers.size()); - m_beziers << bezier; + QRectF bounds = bezier.bounds(); + + // threshold based on similar algorithm as in qtriangulatingstroker.cpp + int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * (2 * qreal(3.14) / 6)); + + if (threshold < 3) threshold = 3; + qreal one_over_threshold_minus_1 = qreal(1) / (threshold - 1); + + for (int t = 1; t < threshold - 1; ++t) { + currentPoint = bezier.pointAt(t * one_over_threshold_minus_1); + + int index = m_points.size(); + m_segments << Segment(m_pathId, last, index); + last = index; + + m_points << currentPoint; + } + + m_segments << Segment(m_pathId, last, current); } } last = current; @@ -896,24 +996,19 @@ void QPathSegments::addPath(const QPainterPath &path) m_segments << Segment(m_pathId, last, lastMoveTo); for (int i = firstSegment; i < m_segments.size(); ++i) { - const QBezier *bezier = bezierAt(i); - if (bezier) { - m_segments.at(i).bounds = bezier->bounds(); - } else { - const QLineF line = lineAt(i); + const QLineF line = lineAt(i); - qreal x1 = line.p1().x(); - qreal y1 = line.p1().y(); - qreal x2 = line.p2().x(); - qreal y2 = line.p2().y(); + qreal x1 = line.p1().x(); + qreal y1 = line.p1().y(); + qreal x2 = line.p2().x(); + qreal y2 = line.p2().y(); - if (x2 < x1) - qSwap(x1, x2); - if (y2 < y1) - qSwap(y1, y2); + if (x2 < x1) + qSwap(x1, x2); + if (y2 < y1) + qSwap(y1, y2); - m_segments.at(i).bounds = QRectF(x1, y1, x2 - x1, y2 - y1); - } + m_segments.at(i).bounds = QRectF(x1, y1, x2 - x1, y2 - y1); } ++m_pathId; @@ -924,8 +1019,8 @@ qreal QWingedEdge::delta(int vertex, int a, int b) const const QPathEdge *ap = edge(a); const QPathEdge *bp = edge(b); - qreal a_angle = ap->angle; - qreal b_angle = bp->angle; + double a_angle = ap->angle; + double b_angle = bp->angle; if (vertex == ap->second) a_angle = ap->invAngle; @@ -933,130 +1028,24 @@ qreal QWingedEdge::delta(int vertex, int a, int b) const if (vertex == bp->second) b_angle = bp->invAngle; - qreal result = b_angle - a_angle; - - if (qFuzzyIsNull(result) || qFuzzyCompare(result, 128)) - return 0; + double result = b_angle - a_angle; - if (result < 0) + if (result >= 128.) + return result - 128.; + else if (result < 0) return result + 128.; else return result; } -static inline QPointF tangentAt(const QWingedEdge &list, int vi, int ei) -{ - const QPathEdge *ep = list.edge(ei); - Q_ASSERT(ep); - - qreal t; - qreal sign; - - if (ep->first == vi) { - t = ep->t0; - sign = 1; - } else { - t = ep->t1; - sign = -1; - } - - QPointF normal; - if (ep->bezier) { - normal = ep->bezier->derivedAt(t); - - if (qFuzzyIsNull(normal.x()) && qFuzzyIsNull(normal.y())) - normal = ep->bezier->secondDerivedAt(t); - } else { - const QPointF a = *list.vertex(ep->first); - const QPointF b = *list.vertex(ep->second); - normal = b - a; - } - - return normalize(sign * normal); -} - static inline QPointF midPoint(const QWingedEdge &list, int ei) { const QPathEdge *ep = list.edge(ei); Q_ASSERT(ep); - if (ep->bezier) { - return ep->bezier->pointAt(0.5 * (ep->t0 + ep->t1)); - } else { - const QPointF a = *list.vertex(ep->first); - const QPointF b = *list.vertex(ep->second); - return a + 0.5 * (b - a); - } -} - -static QBezier transform(const QBezier &bezier, const QPointF &xAxis, const QPointF &yAxis, const QPointF &origin) -{ - QPointF points[4] = { - bezier.pt1(), - bezier.pt2(), - bezier.pt3(), - bezier.pt4() - }; - - for (int i = 0; i < 4; ++i) { - const QPointF p = points[i] - origin; - - points[i].rx() = dot(xAxis, p); - points[i].ry() = dot(yAxis, p); - } - - return QBezier::fromPoints(points[0], points[1], points[2], points[3]); -} - -static bool isLeftOf(const QWingedEdge &list, int vi, int ai, int bi) -{ - const QPathEdge *ap = list.edge(ai); - const QPathEdge *bp = list.edge(bi); - - Q_ASSERT(ap); - Q_ASSERT(bp); - - if (!(ap->bezier || bp->bezier)) - return false; - - const QPointF tangent = tangentAt(list, vi, ai); - const QPointF normal(tangent.y(), -tangent.x()); - - const QPointF origin = *list.vertex(vi); - - const QPointF dpA = midPoint(list, ai) - origin; - const QPointF dpB = midPoint(list, bi) - origin; - - qreal xA = dot(normal, dpA); - qreal xB = dot(normal, dpB); - - if (xA <= 0 && xB >= 0) - return true; - - if (xA >= 0 && xB <= 0) - return false; - - if (!ap->bezier) - return xB > 0; - - if (!bp->bezier) - return xA < 0; - - // both are beziers on the same side of the tangent - - // transform the beziers into the local coordinate system - // such that positive y is along the tangent, and positive x is along the normal - - QBezier bezierA = transform(*ap->bezier, normal, tangent, origin); - QBezier bezierB = transform(*bp->bezier, normal, tangent, origin); - - qreal y = qMin(bezierA.pointAt(0.5 * (ap->t0 + ap->t1)).y(), - bezierB.pointAt(0.5 * (bp->t0 + bp->t1)).y()); - - xA = bezierA.pointAt(bezierA.tForY(ap->t0, ap->t1, y)).x(); - xB = bezierB.pointAt(bezierB.tForY(bp->t0, bp->t1, y)).x(); - - return xA < xB; + const QPointF a = *list.vertex(ep->first); + const QPointF b = *list.vertex(ep->second); + return a + 0.5 * (b - a); } QWingedEdge::TraversalStatus QWingedEdge::findInsertStatus(int vi, int ei) const @@ -1085,7 +1074,6 @@ QWingedEdge::TraversalStatus QWingedEdge::findInsertStatus(int vi, int ei) const status.flip(); Q_ASSERT(edge(status.edge)->vertex(status.direction) == vi); - qreal d2 = delta(vi, ei, status.edge); #ifdef QDEBUG_CLIPPER @@ -1093,8 +1081,7 @@ QWingedEdge::TraversalStatus QWingedEdge::findInsertStatus(int vi, int ei) const qDebug() << "Delta to edge" << status.edge << d2 << ", angles: " << op->angle << op->invAngle; #endif - if (!(qFuzzyIsNull(d2) && isLeftOf(*this, vi, status.edge, ei)) - && (d2 < d || (qFuzzyCompare(d2, d) && isLeftOf(*this, vi, status.edge, position)))) { + if (d2 < d) { position = status.edge; d = d2; } @@ -1186,7 +1173,7 @@ static int commonEdge(const QWingedEdge &list, int a, int b) return -1; } -static qreal computeAngle(const QPointF &v) +static double computeAngle(const QPointF &v) { #if 1 if (v.x() == 0) { @@ -1195,15 +1182,17 @@ static qreal computeAngle(const QPointF &v) return v.x() <= 0 ? 32. : 96.; } - QPointF nv = normalize(v); - if (nv.y() < 0) { - if (nv.x() < 0) { // 0 - 32 - return -32. * nv.x(); + double vx = v.x(); + double vy = v.y(); + normalize(vx, vy); + if (vy < 0) { + if (vx < 0) { // 0 - 32 + return -32. * vx; } else { // 96 - 128 - return 128. - 32. * nv.x(); + return 128. - 32. * vx; } } else { // 32 - 96 - return 64. + 32 * nv.x(); + return 64. + 32. * vx; } #else // doesn't seem to be robust enough @@ -1211,15 +1200,15 @@ static qreal computeAngle(const QPointF &v) #endif } -int QWingedEdge::addEdge(const QPointF &a, const QPointF &b, const QBezier *bezier, qreal t0, qreal t1) +int QWingedEdge::addEdge(const QPointF &a, const QPointF &b) { int fi = insert(a); int si = insert(b); - return addEdge(fi, si, bezier, t0, t1); + return addEdge(fi, si); } -int QWingedEdge::addEdge(int fi, int si, const QBezier *bezier, qreal t0, qreal t1) +int QWingedEdge::addEdge(int fi, int si) { if (fi == si) return -1; @@ -1237,29 +1226,11 @@ int QWingedEdge::addEdge(int fi, int si, const QBezier *bezier, qreal t0, qreal QPathEdge *ep = edge(ei); - ep->bezier = bezier; - ep->t0 = t0; - ep->t1 = t1; - - if (bezier) { - QPointF aTangent = bezier->derivedAt(t0); - QPointF bTangent = -bezier->derivedAt(t1); - - if (qFuzzyIsNull(aTangent.x()) && qFuzzyIsNull(aTangent.y())) - aTangent = bezier->secondDerivedAt(t0); - - if (qFuzzyIsNull(bTangent.x()) && qFuzzyIsNull(bTangent.y())) - bTangent = bezier->secondDerivedAt(t1); - - ep->angle = computeAngle(aTangent); - ep->invAngle = computeAngle(bTangent); - } else { - const QPointF tangent = QPointF(*sp) - QPointF(*fp); - ep->angle = computeAngle(tangent); - ep->invAngle = ep->angle + 64; - if (ep->invAngle >= 128) - ep->invAngle -= 128; - } + const QPointF tangent = QPointF(*sp) - QPointF(*fp); + ep->angle = computeAngle(tangent); + ep->invAngle = ep->angle + 64; + if (ep->invAngle >= 128) + ep->invAngle -= 128; QPathVertex *vertices[2] = { fp, sp }; QPathEdge::Direction dirs[2] = { QPathEdge::Backward, QPathEdge::Forward }; @@ -1314,74 +1285,6 @@ int QWingedEdge::addEdge(int fi, int si, const QBezier *bezier, qreal t0, qreal return ei; } -void QWingedEdge::addBezierEdge(const QBezier *bezier, int vertexA, int vertexB, qreal alphaA, qreal alphaB, int path) -{ - if (qFuzzyCompare(alphaA, alphaB)) - return; - - qreal alphaMid = (alphaA + alphaB) * 0.5; - - qreal s0 = 0; - qreal s1 = 1; - int count = bezier->stationaryYPoints(s0, s1); - - m_splitPoints.clear(); - m_splitPoints << alphaA; - m_splitPoints << alphaMid; - m_splitPoints << alphaB; - - if (count > 0 && !qFuzzyCompare(s0, alphaA) && !qFuzzyCompare(s0, alphaMid) && !qFuzzyCompare(s0, alphaB) && s0 > alphaA && s0 < alphaB) - m_splitPoints << s0; - - if (count > 1 && !qFuzzyCompare(s1, alphaA) && !qFuzzyCompare(s1, alphaMid) && !qFuzzyCompare(s1, alphaB) && s1 > alphaA && s1 < alphaB) - m_splitPoints << s1; - - if (count > 0) - qSort(m_splitPoints.begin(), m_splitPoints.end()); - - int last = vertexA; - for (int i = 0; i < m_splitPoints.size() - 1; ++i) { - const qreal t0 = m_splitPoints[i]; - const qreal t1 = m_splitPoints[i+1]; - - int current; - if ((i + 1) == (m_splitPoints.size() - 1)) { - current = vertexB; - } else { - current = insert(bezier->pointAt(t1)); - } - - QPathEdge *ep = edge(addEdge(last, current, bezier, t0, t1)); - - if (ep) { - const int dir = m_vertices.at(last).y < m_vertices.at(current).y ? 1 : -1; - if (path == 0) - ep->windingA += dir; - else - ep->windingB += dir; - } - - last = current; - } -} - -void QWingedEdge::addBezierEdge(const QBezier *bezier, const QPointF &a, const QPointF &b, qreal alphaA, qreal alphaB, int path) -{ - if (qFuzzyCompare(alphaA, alphaB)) - return; - - if (comparePoints(a, b)) { - int v = insert(a); - - addBezierEdge(bezier, v, v, alphaA, alphaB, path); - } else { - int va = insert(a); - int vb = insert(b); - - addBezierEdge(bezier, va, vb, alphaA, alphaB, path); - } -} - int QWingedEdge::insert(const QPathVertex &vertex) { if (!m_vertices.isEmpty()) { @@ -1430,37 +1333,12 @@ static void add(QPainterPath &path, const QWingedEdge &list, int edge, QPathEdge status.traversal = traversal; status.direction = QPathEdge::Forward; - const QBezier *bezier = 0; - qreal t0 = 1; - qreal t1 = 0; - bool forward = true; - path.moveTo(*list.vertex(list.edge(edge)->first)); do { const QPathEdge *ep = list.edge(status.edge); - if (ep->bezier != bezier || (bezier && t0 != ep->t1 && t1 != ep->t0)) { - if (bezier) { - QBezier sub = bezier->bezierOnInterval(t0, t1); - - if (forward) - path.cubicTo(sub.pt2(), sub.pt3(), sub.pt4()); - else - path.cubicTo(sub.pt3(), sub.pt2(), sub.pt1()); - } - - bezier = ep->bezier; - t0 = 1; - t1 = 0; - forward = status.direction == QPathEdge::Forward; - } - - if (ep->bezier) { - t0 = qMin(t0, ep->t0); - t1 = qMax(t1, ep->t1); - } else - addLineTo(path, *list.vertex(ep->vertex(status.direction))); + addLineTo(path, *list.vertex(ep->vertex(status.direction))); if (status.traversal == QPathEdge::LeftTraversal) ep->flag &= ~16; @@ -1469,14 +1347,6 @@ static void add(QPainterPath &path, const QWingedEdge &list, int edge, QPathEdge status = list.next(status); } while (status.edge != edge); - - if (bezier) { - QBezier sub = bezier->bezierOnInterval(t0, t1); - if (forward) - path.cubicTo(sub.pt2(), sub.pt3(), sub.pt4()); - else - path.cubicTo(sub.pt3(), sub.pt2(), sub.pt1()); - } } void QWingedEdge::simplify() @@ -1535,9 +1405,9 @@ bool QPathClipper::intersect() else if (clipIsRect) return subjectPath.intersects(r2); - QPathSegments a; + QPathSegments a(subjectPath.length()); a.setPath(subjectPath); - QPathSegments b; + QPathSegments b(clipPath.length()); b.setPath(clipPath); QIntersectionFinder finder; @@ -1580,9 +1450,9 @@ bool QPathClipper::contains() if (clipIsRect) return subjectPath.contains(r2); - QPathSegments a; + QPathSegments a(subjectPath.length()); a.setPath(subjectPath); - QPathSegments b; + QPathSegments b(clipPath.length()); b.setPath(clipPath); QIntersectionFinder finder; @@ -1705,6 +1575,9 @@ QPainterPath QPathClipper::clip(Operation operation) if (subjectPath == clipPath) return op == BoolSub ? QPainterPath() : subjectPath; + bool subjectIsRect = pathToRect(subjectPath, 0); + bool clipIsRect = pathToRect(clipPath, 0); + const QRectF clipBounds = clipPath.boundingRect(); const QRectF subjectBounds = subjectPath.boundingRect(); @@ -1732,8 +1605,7 @@ QPainterPath QPathClipper::clip(Operation operation) } if (clipBounds.contains(subjectBounds)) { - QRectF clipRect; - if (pathToRect(clipPath, &clipRect) && clipRect.contains(subjectBounds)) { + if (clipIsRect) { switch (op) { case BoolSub: return QPainterPath(); @@ -1746,17 +1618,16 @@ QPainterPath QPathClipper::clip(Operation operation) } } } else if (subjectBounds.contains(clipBounds)) { - QRectF subjectRect; - if (pathToRect(subjectPath, &subjectRect) && subjectRect.contains(clipBounds)) { + if (subjectIsRect) { switch (op) { case BoolSub: if (clipPath.fillRule() == Qt::OddEvenFill) { QPainterPath result = clipPath; - result.addRect(subjectRect); + result.addRect(subjectBounds); return result; } else { QPainterPath result = clipPath.simplified(); - result.addRect(subjectRect); + result.addRect(subjectBounds); return result; } break; @@ -1769,6 +1640,13 @@ QPainterPath QPathClipper::clip(Operation operation) } } } + + if (op == BoolAnd) { + if (subjectIsRect) + return intersect(clipPath, subjectBounds); + else if (clipIsRect) + return intersect(subjectPath, clipBounds); + } } QWingedEdge list(subjectPath, clipPath); @@ -1930,25 +1808,10 @@ bool QWingedEdge::isInside(qreal x, qreal y) const QPointF b = *vertex(ep->second); if ((a.y() < y && b.y() > y) || (a.y() > y && b.y() < y)) { - if (ep->bezier) { - qreal maxX = qMax(a.x(), qMax(b.x(), qMax(ep->bezier->x2, ep->bezier->x3))); - qreal minX = qMin(a.x(), qMin(b.x(), qMin(ep->bezier->x2, ep->bezier->x3))); - - if (minX > x) { - winding += w; - } else if (maxX > x) { - const qreal t = ep->bezier->tForY(ep->t0, ep->t1, y); - const qreal intersection = ep->bezier->pointAt(t).x(); - - if (intersection > x) - winding += w; - } - } else { - qreal intersectionX = a.x() + (b.x() - a.x()) * (y - a.y()) / (b.y() - a.y()); + qreal intersectionX = a.x() + (b.x() - a.x()) * (y - a.y()) / (b.y() - a.y()); - if (intersectionX > x) - winding += w; - } + if (intersectionX > x) + winding += w; } } @@ -1964,17 +1827,9 @@ static QVector<QCrossingEdge> findCrossings(const QWingedEdge &list, qreal y) QPointF b = *list.vertex(edge->second); if ((a.y() < y && b.y() > y) || (a.y() > y && b.y() < y)) { - if (edge->bezier) { - const qreal t = edge->bezier->tForY(edge->t0, edge->t1, y); - const qreal intersection = edge->bezier->pointAt(t).x(); - - const QCrossingEdge edge = { i, intersection }; - crossings << edge; - } else { - const qreal intersection = a.x() + (b.x() - a.x()) * (y - a.y()) / (b.y() - a.y()); - const QCrossingEdge edge = { i, intersection }; - crossings << edge; - } + const qreal intersection = a.x() + (b.x() - a.x()) * (y - a.y()) / (b.y() - a.y()); + const QCrossingEdge edge = { i, intersection }; + crossings << edge; } } return crossings; @@ -2052,4 +1907,243 @@ bool QPathClipper::handleCrossingEdges(QWingedEdge &list, qreal y, ClipperMode m return false; } +namespace { + +QList<QPainterPath> toSubpaths(const QPainterPath &path) +{ + + QList<QPainterPath> subpaths; + if (path.isEmpty()) + return subpaths; + + QPainterPath current; + for (int i = 0; i < path.elementCount(); ++i) { + const QPainterPath::Element &e = path.elementAt(i); + switch (e.type) { + case QPainterPath::MoveToElement: + if (current.elementCount() > 1) + subpaths += current; + current = QPainterPath(); + current.moveTo(e); + break; + case QPainterPath::LineToElement: + current.lineTo(e); + break; + case QPainterPath::CurveToElement: { + current.cubicTo(e, path.elementAt(i + 1), path.elementAt(i + 2)); + i+=2; + break; + } + case QPainterPath::CurveToDataElement: + Q_ASSERT(!"toSubpaths(), bad element type"); + break; + } + } + + if (current.elementCount() > 1) + subpaths << current; + + return subpaths; +} + +enum Edge +{ + Left, Top, Right, Bottom +}; + +static bool isVertical(Edge edge) +{ + return edge == Left || edge == Right; +} + +template <Edge edge> +bool compare(const QPointF &p, qreal t) +{ + switch (edge) + { + case Left: + return p.x() < t; + case Right: + return p.x() > t; + case Top: + return p.y() < t; + default: + return p.y() > t; + } +} + +template <Edge edge> +QPointF intersectLine(const QPointF &a, const QPointF &b, qreal t) +{ + QLineF line(a, b); + switch (edge) { + case Left: // fall-through + case Right: + return line.pointAt((t - a.x()) / (b.x() - a.x())); + default: + return line.pointAt((t - a.y()) / (b.y() - a.y())); + } +} + +void addLine(QPainterPath &path, const QLineF &line) +{ + if (path.elementCount() > 0) + path.lineTo(line.p1()); + else + path.moveTo(line.p1()); + + path.lineTo(line.p2()); +} + +template <Edge edge> +void clipLine(const QPointF &a, const QPointF &b, qreal t, QPainterPath &result) +{ + bool outA = compare<edge>(a, t); + bool outB = compare<edge>(b, t); + if (outA && outB) + return; + + if (outA) + addLine(result, QLineF(intersectLine<edge>(a, b, t), b)); + else if(outB) + addLine(result, QLineF(a, intersectLine<edge>(a, b, t))); + else + addLine(result, QLineF(a, b)); +} + +void addBezier(QPainterPath &path, const QBezier &bezier) +{ + if (path.elementCount() > 0) + path.lineTo(bezier.pt1()); + else + path.moveTo(bezier.pt1()); + + path.cubicTo(bezier.pt2(), bezier.pt3(), bezier.pt4()); +} + +template <Edge edge> +void clipBezier(const QPointF &a, const QPointF &b, const QPointF &c, const QPointF &d, qreal t, QPainterPath &result) +{ + QBezier bezier = QBezier::fromPoints(a, b, c, d); + + bool outA = compare<edge>(a, t); + bool outB = compare<edge>(b, t); + bool outC = compare<edge>(c, t); + bool outD = compare<edge>(d, t); + + int outCount = int(outA) + int(outB) + int(outC) + int(outD); + + if (outCount == 4) + return; + + if (outCount == 0) { + addBezier(result, bezier); + return; + } + + QTransform flip = isVertical(edge) ? QTransform(0, 1, 1, 0, 0, 0) : QTransform(); + QBezier unflipped = bezier; + QBezier flipped = bezier.mapBy(flip); + + qreal t0 = 0, t1 = 1; + int stationary = flipped.stationaryYPoints(t0, t1); + + qreal segments[4]; + QPointF points[4]; + points[0] = unflipped.pt1(); + segments[0] = 0; + + int segmentCount = 0; + if (stationary > 0) { + ++segmentCount; + segments[segmentCount] = t0; + points[segmentCount] = unflipped.pointAt(t0); + } + if (stationary > 1) { + ++segmentCount; + segments[segmentCount] = t1; + points[segmentCount] = unflipped.pointAt(t1); + } + ++segmentCount; + segments[segmentCount] = 1; + points[segmentCount] = unflipped.pt4(); + + qreal lastIntersection = 0; + for (int i = 0; i < segmentCount; ++i) { + outA = compare<edge>(points[i], t); + outB = compare<edge>(points[i+1], t); + + if (outA != outB) { + qreal intersection = flipped.tForY(segments[i], segments[i+1], t); + + if (outB) + addBezier(result, unflipped.getSubRange(lastIntersection, intersection)); + + lastIntersection = intersection; + } + } + + if (!outB) + addBezier(result, unflipped.getSubRange(lastIntersection, 1)); +} + +// clips a single subpath against a single edge +template <Edge edge> +QPainterPath clip(const QPainterPath &path, qreal t) +{ + QPainterPath result; + for (int i = 1; i < path.elementCount(); ++i) { + const QPainterPath::Element &element = path.elementAt(i); + Q_ASSERT(!element.isMoveTo()); + if (element.isLineTo()) { + clipLine<edge>(path.elementAt(i-1), path.elementAt(i), t, result); + } else { + clipBezier<edge>(path.elementAt(i-1), path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2), t, result); + i += 2; + } + } + + int last = path.elementCount() - 1; + if (QPointF(path.elementAt(last)) != QPointF(path.elementAt(0))) + clipLine<edge>(path.elementAt(last), path.elementAt(0), t, result); + + return result; +} + +QPainterPath intersectPath(const QPainterPath &path, const QRectF &rect) +{ + QList<QPainterPath> subpaths = toSubpaths(path); + + QPainterPath result; + result.setFillRule(path.fillRule()); + for (int i = 0; i < subpaths.size(); ++i) { + QPainterPath subPath = subpaths.at(i); + QRectF bounds = subPath.boundingRect(); + if (bounds.intersects(rect)) { + if (bounds.left() < rect.left()) + subPath = clip<Left>(subPath, rect.left()); + if (bounds.right() > rect.right()) + subPath = clip<Right>(subPath, rect.right()); + + bounds = subPath.boundingRect(); + + if (bounds.top() < rect.top()) + subPath = clip<Top>(subPath, rect.top()); + if (bounds.bottom() > rect.bottom()) + subPath = clip<Bottom>(subPath, rect.bottom()); + + if (subPath.elementCount() > 1) + result.addPath(subPath); + } + } + return result; +} + +} + +QPainterPath QPathClipper::intersect(const QPainterPath &path, const QRectF &rect) +{ + return intersectPath(path, rect); +} + QT_END_NAMESPACE diff --git a/src/gui/painting/qpathclipper_p.h b/src/gui/painting/qpathclipper_p.h index b900862..bdad4e1 100644 --- a/src/gui/painting/qpathclipper_p.h +++ b/src/gui/painting/qpathclipper_p.h @@ -87,6 +87,7 @@ public: bool contains(); static bool pathToRect(const QPainterPath &path, QRectF *rect = 0); + static QPainterPath intersect(const QPainterPath &path, const QRectF &rect); private: Q_DISABLE_COPY(QPathClipper) @@ -147,12 +148,8 @@ public: int first; int second; - qreal angle; - qreal invAngle; - - const QBezier *bezier; - qreal t0; - qreal t1; + double angle; + double invAngle; int next(Traversal traversal, Direction direction) const; @@ -181,9 +178,8 @@ public: }; struct Segment { - Segment(int pathId, int vertexA, int vertexB, int bezierIndex = -1) + Segment(int pathId, int vertexA, int vertexB) : path(pathId) - , bezier(bezierIndex) , va(vertexA) , vb(vertexB) , intersection(-1) @@ -191,7 +187,6 @@ public: } int path; - int bezier; // vertices int va; @@ -204,7 +199,7 @@ public: }; - QPathSegments(); + QPathSegments(int reserve); void setPath(const QPainterPath &path); void addPath(const QPainterPath &path); @@ -215,7 +210,6 @@ public: const Segment &segmentAt(int index) const; const QLineF lineAt(int index) const; - const QBezier *bezierAt(int index) const; const QRectF &elementBounds(int index) const; int pathId(int index) const; @@ -230,7 +224,6 @@ public: private: QDataBuffer<QPointF> m_points; QDataBuffer<Segment> m_segments; - QDataBuffer<QBezier> m_beziers; QDataBuffer<Intersection> m_intersections; int m_pathId; @@ -271,8 +264,8 @@ public: TraversalStatus next(const TraversalStatus &status) const; - int addEdge(const QPointF &a, const QPointF &b, const QBezier *bezier = 0, qreal t0 = 0, qreal t1 = 1); - int addEdge(int vertexA, int vertexB, const QBezier *bezier = 0, qreal t0 = 0, qreal t1 = 1); + int addEdge(const QPointF &a, const QPointF &b); + int addEdge(int vertexA, int vertexB); bool isInside(qreal x, qreal y) const; @@ -284,11 +277,7 @@ private: void printNode(int i, FILE *handle); - QBezier bezierFromIndex(int index) const; - void removeEdge(int ei); - void addBezierEdge(const QBezier *bezier, const QPointF &a, const QPointF &b, qreal alphaA, qreal alphaB, int path); - void addBezierEdge(const QBezier *bezier, int vertexA, int vertexB, qreal alphaA, qreal alphaB, int path); int insert(const QPathVertex &vertex); TraversalStatus findInsertStatus(int vertex, int edge) const; @@ -311,9 +300,6 @@ inline QPathEdge::QPathEdge(int a, int b) , second(b) , angle(0) , invAngle(0) - , bezier(0) - , t0(0) - , t1(0) { m_next[0][0] = -1; m_next[1][0] = -1; @@ -359,7 +345,10 @@ inline QPathVertex::operator QPointF() const return QPointF(x, y); } -inline QPathSegments::QPathSegments() +inline QPathSegments::QPathSegments(int reserve) : + m_points(reserve), + m_segments(reserve), + m_intersections(reserve) { } @@ -395,15 +384,6 @@ inline const QLineF QPathSegments::lineAt(int index) const return QLineF(m_points.at(segment.va), m_points.at(segment.vb)); } -inline const QBezier *QPathSegments::bezierAt(int index) const -{ - const Segment &segment = m_segments.at(index); - if (segment.bezier >= 0) - return &m_beziers.at(segment.bezier); - else - return 0; -} - inline const QRectF &QPathSegments::elementBounds(int index) const { return m_segments.at(index).bounds; diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp index 05341e3..ba5d164 100644 --- a/src/gui/painting/qpdf.cpp +++ b/src/gui/painting/qpdf.cpp @@ -916,24 +916,6 @@ const char *QPdf::paperSizeToString(QPrinter::PaperSize paperSize) } -QByteArray QPdf::stripSpecialCharacters(const QByteArray &string) -{ - QByteArray s = string; - s.replace(' ', ""); - s.replace('(', ""); - s.replace(')', ""); - s.replace('<', ""); - s.replace('>', ""); - s.replace('[', ""); - s.replace(']', ""); - s.replace('{', ""); - s.replace('}', ""); - s.replace('/', ""); - s.replace('%', ""); - return s; -} - - // -------------------------- base engine, shared code between PS and PDF ----------------------- QPdfBaseEngine::QPdfBaseEngine(QPdfBaseEnginePrivate &dd, PaintEngineFeatures f) @@ -1424,6 +1406,7 @@ void QPdfBaseEngine::setProperty(PrintEnginePropertyKey key, const QVariant &val case PPK_FullPage: d->fullPage = value.toBool(); break; + case PPK_CopyCount: // fallthrough case PPK_NumberOfCopies: d->copies = value.toInt(); break; @@ -1513,6 +1496,17 @@ QVariant QPdfBaseEngine::property(PrintEnginePropertyKey key) const case PPK_FullPage: ret = d->fullPage; break; + case PPK_CopyCount: + ret = d->copies; + break; + case PPK_SupportsMultipleCopies: +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + if (QCUPSSupport::isAvailable()) + ret = true; + else +#endif + ret = false; + break; case PPK_NumberOfCopies: #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) if (QCUPSSupport::isAvailable()) diff --git a/src/gui/painting/qpdf_p.h b/src/gui/painting/qpdf_p.h index f79c5cc..5c5ceb4 100644 --- a/src/gui/painting/qpdf_p.h +++ b/src/gui/painting/qpdf_p.h @@ -156,8 +156,6 @@ namespace QPdf { PaperSize paperSize(QPrinter::PaperSize paperSize); const char *paperSizeToString(QPrinter::PaperSize paperSize); - - QByteArray stripSpecialCharacters(const QByteArray &string); } @@ -216,8 +214,6 @@ public: private: void updateClipPath(const QPainterPath & path, Qt::ClipOperation op); - - friend int qt_printerRealNumCopies(QPaintEngine *); }; class QPdfBaseEnginePrivate : public QAlphaPaintEnginePrivate diff --git a/src/gui/painting/qpen.cpp b/src/gui/painting/qpen.cpp index e290cbe..2e43984 100644 --- a/src/gui/painting/qpen.cpp +++ b/src/gui/painting/qpen.cpp @@ -92,7 +92,7 @@ typedef QPenPrivate QPenData; convenience functions to extract and set the color of the pen's brush, respectively. Pens may also be compared and streamed. - For more information about painting in general, see \l{The Paint + For more information about painting in general, see the \l{Paint System} documentation. \tableofcontents @@ -872,7 +872,7 @@ bool QPen::isDetached() Writes the given \a pen to the given \a stream and returns a reference to the \a stream. - \sa {Format of the QDataStream Operators} + \sa {Serializing Qt Data Types} */ QDataStream &operator<<(QDataStream &s, const QPen &p) @@ -918,7 +918,7 @@ QDataStream &operator<<(QDataStream &s, const QPen &p) Reads a pen from the given \a stream into the given \a pen and returns a reference to the \a stream. - \sa {Format of the QDataStream Operators} + \sa {Serializing Qt Data Types} */ QDataStream &operator>>(QDataStream &s, QPen &p) diff --git a/src/gui/painting/qpolygon.cpp b/src/gui/painting/qpolygon.cpp index b68314f..2fb52b5 100644 --- a/src/gui/painting/qpolygon.cpp +++ b/src/gui/painting/qpolygon.cpp @@ -719,7 +719,7 @@ QPolygon::operator QVariant() const Writes the given \a polygon to the given \a stream, and returns a reference to the stream. - \sa {Format of the QDataStream Operators} + \sa {Serializing Qt Data Types} */ QDataStream &operator<<(QDataStream &s, const QPolygon &a) { @@ -735,7 +735,7 @@ QDataStream &operator<<(QDataStream &s, const QPolygon &a) Reads a polygon from the given \a stream into the given \a polygon, and returns a reference to the stream. - \sa {Format of the QDataStream Operators} + \sa {Serializing Qt Data Types} */ QDataStream &operator>>(QDataStream &s, QPolygon &a) { @@ -755,7 +755,7 @@ QDataStream &operator>>(QDataStream &s, QPolygon &a) Writes the given \a polygon to the given \a stream, and returns a reference to the stream. - \sa {Format of the QDataStream Operators} + \sa {Serializing Qt Data Types} */ QDataStream &operator<<(QDataStream &s, const QPolygonF &a) @@ -776,7 +776,7 @@ QDataStream &operator<<(QDataStream &s, const QPolygonF &a) Reads a polygon from the given \a stream into the given \a polygon, and returns a reference to the stream. - \sa {Format of the QDataStream Operators} + \sa {Serializing Qt Data Types} */ QDataStream &operator>>(QDataStream &s, QPolygonF &a) diff --git a/src/gui/painting/qpolygonclipper_p.h b/src/gui/painting/qpolygonclipper_p.h index 1b4cbb3..cdaac1c 100644 --- a/src/gui/painting/qpolygonclipper_p.h +++ b/src/gui/painting/qpolygonclipper_p.h @@ -62,7 +62,8 @@ QT_BEGIN_NAMESPACE template <typename InType, typename OutType, typename CastType> class QPolygonClipper { public: - QPolygonClipper() + QPolygonClipper() : + buffer1(0), buffer2(0) { x1 = y1 = x2 = y2 = 0; } diff --git a/src/gui/painting/qprintengine.h b/src/gui/painting/qprintengine.h index 35715fb..71ff954 100644 --- a/src/gui/painting/qprintengine.h +++ b/src/gui/painting/qprintengine.h @@ -86,6 +86,8 @@ public: PPK_PaperSources, PPK_CustomPaperSize, PPK_PageMargins, + PPK_CopyCount, + PPK_SupportsMultipleCopies, PPK_PaperSize = PPK_PageSize, PPK_CustomBase = 0xff00 diff --git a/src/gui/painting/qprintengine_mac.mm b/src/gui/painting/qprintengine_mac.mm index 7195c63..a548225 100644 --- a/src/gui/painting/qprintengine_mac.mm +++ b/src/gui/painting/qprintengine_mac.mm @@ -114,8 +114,11 @@ bool QMacPrintEngine::end() Q_D(QMacPrintEngine); if (d->state == QPrinter::Aborted) return true; // I was just here a function call ago :) - if(d->paintEngine->type() == QPaintEngine::CoreGraphics) + if(d->paintEngine->type() == QPaintEngine::CoreGraphics) { + // We dont need the paint engine to call restoreGraphicsState() + static_cast<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->stackCount = 0; static_cast<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->hd = 0; + } d->paintEngine->end(); if (d->state != QPrinter::Idle) d->releaseSession(); @@ -685,6 +688,7 @@ void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &va case PPK_FullPage: d->fullPage = value.toBool(); break; + case PPK_CopyCount: // fallthrough case PPK_NumberOfCopies: PMSetCopies(d->settings, value.toInt(), false); break; @@ -787,6 +791,15 @@ QVariant QMacPrintEngine::property(PrintEnginePropertyKey key) const case PPK_NumberOfCopies: ret = 1; break; + case PPK_CopyCount: { + UInt32 copies = 1; + PMGetCopies(d->settings, &copies); + ret = (uint) copies; + break; + } + case PPK_SupportsMultipleCopies: + ret = true; + break; case PPK_Orientation: PMOrientation orientation; PMGetOrientation(d->format, &orientation); diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp index b8bf15e..2955e39 100644 --- a/src/gui/painting/qprintengine_pdf.cpp +++ b/src/gui/painting/qprintengine_pdf.cpp @@ -931,29 +931,16 @@ void QPdfEnginePrivate::writeHeader() void QPdfEnginePrivate::writeInfo() { info = addXrefEntry(-1); - - // The 'text string' type in PDF is encoded either as PDFDocEncoding, or - // Unicode UTF-16 with a Unicode byte order mark as the first character - // (0xfeff), with the high-order byte first. - QByteArray array("<<\n/Title (\xfe\xff"); - const ushort *utf16Title = title.utf16(); - for (int i=0; i < title.size(); ++i) { - array.append((*(utf16Title + i)) >> 8); - array.append((*(utf16Title + i)) & 0xff); - } - array.append(")\n/Creator (\xfe\xff"); - const ushort *utf16Creator = creator.utf16(); - for (int i=0; i < creator.size(); ++i) { - array.append((*(utf16Creator + i)) >> 8); - array.append((*(utf16Creator + i)) & 0xff); - } - array.append(")\n/Producer (Qt " QT_VERSION_STR " (C) 2010 Nokia Corporation and/or its subsidiary(-ies))\n"); - write(array); - + xprintf("<<\n/Title "); + printString(title); + xprintf("\n/Creator "); + printString(creator); + xprintf("\n/Producer "); + printString(QString::fromLatin1("Qt " QT_VERSION_STR " (C) 2010 Nokia Corporation and/or its subsidiary(-ies)")); QDateTime now = QDateTime::currentDateTime().toUTC(); QTime t = now.time(); QDate d = now.date(); - xprintf("/CreationDate (D:%d%02d%02d%02d%02d%02d)\n", + xprintf("\n/CreationDate (D:%d%02d%02d%02d%02d%02d)\n", d.year(), d.month(), d.day(), @@ -1230,6 +1217,25 @@ int QPdfEnginePrivate::addXrefEntry(int object, bool printostr) return object; } +void QPdfEnginePrivate::printString(const QString &string) { + // The 'text string' type in PDF is encoded either as PDFDocEncoding, or + // Unicode UTF-16 with a Unicode byte order mark as the first character + // (0xfeff), with the high-order byte first. + QByteArray array("(\xfe\xff"); + const ushort *utf16 = string.utf16(); + + for (int i=0; i < string.size(); ++i) { + char part[2] = {char((*(utf16 + i)) >> 8), char((*(utf16 + i)) & 0xff)}; + for(int j=0; j < 2; ++j) { + if (part[j] == '(' || part[j] == ')' || part[j] == '\\') + array.append('\\'); + array.append(part[j]); + } + } + array.append(")"); + write(array); +} + QT_END_NAMESPACE #endif // QT_NO_PRINTER diff --git a/src/gui/painting/qprintengine_pdf_p.h b/src/gui/painting/qprintengine_pdf_p.h index cb6c59d..e0ca56f 100644 --- a/src/gui/painting/qprintengine_pdf_p.h +++ b/src/gui/painting/qprintengine_pdf_p.h @@ -170,6 +170,7 @@ private: void writePage(); int addXrefEntry(int object, bool printostr = true); + void printString(const QString &string); void xprintf(const char* fmt, ...); inline void write(const QByteArray &data) { stream->writeRawData(data.constData(), data.size()); diff --git a/src/gui/painting/qprintengine_ps.cpp b/src/gui/painting/qprintengine_ps.cpp index 28e9a7a..ca694ae 100644 --- a/src/gui/painting/qprintengine_ps.cpp +++ b/src/gui/painting/qprintengine_ps.cpp @@ -64,7 +64,6 @@ #include "qbitmap.h" #include "qregion.h" #include "qimagewriter.h" -#include <private/qunicodetables_p.h> #include <private/qpainterpath_p.h> #include <qdebug.h> #include <private/qdrawhelper_p.h> diff --git a/src/gui/painting/qprintengine_qws.cpp b/src/gui/painting/qprintengine_qws.cpp index 2d355b8..396d712 100644 --- a/src/gui/painting/qprintengine_qws.cpp +++ b/src/gui/painting/qprintengine_qws.cpp @@ -268,9 +268,13 @@ QVariant QtopiaPrintEngine::property(PrintEnginePropertyKey key) const case PPK_FullPage: ret = d->fullPage; break; + case PPK_CopyCount: // fallthrough case PPK_NumberOfCopies: ret = d->numCopies; break; + case PPK_SupportsMultipleCopies: + ret = false; + break; case PPK_Orientation: ret = d->orientation; break; @@ -329,6 +333,7 @@ void QtopiaPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant & case PPK_FullPage: d->fullPage = value.toBool(); break; + case PPK_CopyCount: // fallthrough case PPK_NumberOfCopies: d->numCopies = value.toInt(); break; diff --git a/src/gui/painting/qprintengine_win.cpp b/src/gui/painting/qprintengine_win.cpp index 76dad72..afae0a5 100644 --- a/src/gui/painting/qprintengine_win.cpp +++ b/src/gui/painting/qprintengine_win.cpp @@ -368,7 +368,8 @@ void QWin32PrintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem } // We only want to convert the glyphs to text if the entire string is compatible with ASCII - bool convertToText = true; + // and if we actually have access to the chars. + bool convertToText = ti.chars != 0; for (int i=0; i < ti.num_chars; ++i) { if (ti.chars[i].unicode() >= 0x80) { convertToText = false; @@ -850,7 +851,7 @@ void QWin32PrintEnginePrivate::strokePath_dev(const QPainterPath &path, const QC HPEN pen = ExtCreatePen(((penWidth == 0) ? PS_COSMETIC : PS_GEOMETRIC) | PS_SOLID | capStyle | joinStyle, - penWidth, &brush, 0, 0); + (penWidth == 0) ? 1 : penWidth, &brush, 0, 0); HGDIOBJ old_pen = SelectObject(hdc, pen); StrokePath(hdc); @@ -1241,6 +1242,7 @@ void QWin32PrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant & d->updateOrigin(); break; + case PPK_CopyCount: // fallthrough case PPK_NumberOfCopies: if (!d->devMode) break; @@ -1407,6 +1409,14 @@ QVariant QWin32PrintEngine::property(PrintEnginePropertyKey key) const value = d->fullPage; break; + case PPK_CopyCount: + value = d->num_copies; + break; + + case PPK_SupportsMultipleCopies: + value = true; + break; + case PPK_NumberOfCopies: value = 1; break; diff --git a/src/gui/painting/qprintengine_win_p.h b/src/gui/painting/qprintengine_win_p.h index a3271cb..d435831 100644 --- a/src/gui/painting/qprintengine_win_p.h +++ b/src/gui/painting/qprintengine_win_p.h @@ -108,7 +108,6 @@ public: private: friend class QPrintDialog; friend class QPageSetupDialog; - friend int qt_printerRealNumCopies(QPaintEngine *); }; class QWin32PrintEnginePrivate : public QAlphaPaintEnginePrivate diff --git a/src/gui/painting/qprinter.cpp b/src/gui/painting/qprinter.cpp index 4d2b50a..ae21416 100644 --- a/src/gui/painting/qprinter.cpp +++ b/src/gui/painting/qprinter.cpp @@ -158,27 +158,6 @@ QSizeF qt_printerPaperSize(QPrinter::Orientation orientation, (qt_paperSizes[paperSize][height_index] * 72 / 25.4) / multiplier); } - -// returns the actual num copies set on a printer, not -// the number that is documented in QPrinter::numCopies() -int qt_printerRealNumCopies(QPaintEngine *engine) -{ - int numCopies = 1; - if (engine->type() == QPaintEngine::PostScript - || engine->type() == QPaintEngine::Pdf) - { - QPdfBaseEngine *base = static_cast<QPdfBaseEngine *>(engine); - numCopies = base->d_func()->copies; - } -#ifdef Q_WS_WIN - else if (engine->type() == QPaintEngine::Windows) { - QWin32PrintEngine *base = static_cast<QWin32PrintEngine *>(engine); - numCopies = base->d_func()->num_copies; - } -#endif - return numCopies; -} - void QPrinterPrivate::createDefaultEngines() { QPrinter::OutputFormat realOutputFormat = outputFormat; @@ -302,7 +281,7 @@ void QPrinterPrivate::addToManualSetList(QPrintEngine::PrintEnginePropertyKey ke printer to provide, in dots per inch (DPI). \i setFullPage() tells QPrinter whether you want to deal with the full page or just with the part the printer can draw on. - \i setNumCopies() tells QPrinter how many copies of the document + \i setCopyCount() tells QPrinter how many copies of the document it should print. \endlist @@ -403,6 +382,7 @@ void QPrinterPrivate::addToManualSetList(QPrintEngine::PrintEnginePropertyKey ke \value AllPages All pages should be printed. \value Selection Only the selection should be printed. \value PageRange The specified page range should be printed. + \value CurrentPage Only the current page should be printed. \sa QAbstractPrintDialog::PrintRange */ @@ -592,6 +572,7 @@ void QPrinterPrivate::addToManualSetList(QPrintEngine::PrintEnginePropertyKey ke \value AllPages All the pages should be printed. \value Selection Only the selection should be printed. \value PageRange Print according to the from page and to page options. + \value CurrentPage Only the current page should be printed. \sa setPrintRange(), printRange() */ @@ -607,6 +588,7 @@ void QPrinterPrivate::addToManualSetList(QPrintEngine::PrintEnginePropertyKey ke \value PrintSelection Describes if printing selections should be enabled. \value PrintPageRange Describes if printing page ranges (from, to) should be enabled + \value PrintCurrentPage if Print Current Page option should be enabled \sa setOptionEnabled(), isOptionEnabled() */ @@ -748,7 +730,6 @@ void QPrinter::setOutputFormat(OutputFormat format) d->outputFormat = format; QPrintEngine *oldPrintEngine = d->printEngine; - QPaintEngine *oldPaintEngine = d->paintEngine; // same as the above - shouldn't be deleted const bool def_engine = d->use_default_engine; d->printEngine = 0; @@ -761,7 +742,7 @@ void QPrinter::setOutputFormat(OutputFormat format) // PPK_NumberOfCopies need special treatmeant since it in most cases // will return 1, disregarding the actual value that was set if (key == QPrintEngine::PPK_NumberOfCopies) - prop = QVariant(qt_printerRealNumCopies(oldPaintEngine)); + prop = QVariant(copyCount()); else prop = oldPrintEngine->property(key); if (prop.isValid()) @@ -1263,6 +1244,7 @@ QPrinter::ColorMode QPrinter::colorMode() const /*! + \obsolete Returns the number of copies to be printed. The default value is 1. On Windows, Mac OS X and X11 systems that support CUPS, this will always @@ -1275,6 +1257,8 @@ QPrinter::ColorMode QPrinter::colorMode() const buffering up the copies and in those cases the application must make an explicit call to the print code for each copy. + Use copyCount() in conjunction with supportsMultipleCopies() instead. + \sa setNumCopies(), actualNumCopies() */ @@ -1286,6 +1270,7 @@ int QPrinter::numCopies() const /*! + \obsolete \since 4.6 Returns the number of copies that will be printed. The default @@ -1294,22 +1279,26 @@ int QPrinter::numCopies() const This function always returns the actual value specified in the print dialog or using setNumCopies(). + Use copyCount() instead. + \sa setNumCopies(), numCopies() */ int QPrinter::actualNumCopies() const { - Q_D(const QPrinter); - return qt_printerRealNumCopies(d->paintEngine); + return copyCount(); } /*! + \obsolete Sets the number of copies to be printed to \a numCopies. The printer driver reads this setting and prints the specified number of copies. + Use setCopyCount() instead. + \sa numCopies() */ @@ -1321,6 +1310,58 @@ void QPrinter::setNumCopies(int numCopies) d->addToManualSetList(QPrintEngine::PPK_NumberOfCopies); } +/*! + \since 4.7 + + Sets the number of copies to be printed to \a count. + + The printer driver reads this setting and prints the specified number of + copies. + + \sa copyCount(), supportsMultipleCopies() +*/ + +void QPrinter::setCopyCount(int count) +{ + Q_D(QPrinter); + ABORT_IF_ACTIVE("QPrinter::setCopyCount;"); + d->printEngine->setProperty(QPrintEngine::PPK_CopyCount, count); + d->addToManualSetList(QPrintEngine::PPK_CopyCount); +} + +/*! + \since 4.7 + + Returns the number of copies that will be printed. The default value is 1. + + \sa setCopyCount(), supportsMultipleCopies() +*/ + +int QPrinter::copyCount() const +{ + Q_D(const QPrinter); + return d->printEngine->property(QPrintEngine::PPK_CopyCount).toInt(); +} + +/*! + \since 4.7 + + Returns true if the printer supports printing multiple copies of the same + document in one job; otherwise false is returned. + + On most systems this function will return true. However, on X11 systems + that do not support CUPS, this function will return false. That means the + application has to handle the number of copies by printing the same + document the required number of times. + + \sa setCopyCount(), copyCount() +*/ + +bool QPrinter::supportsMultipleCopies() const +{ + Q_D(const QPrinter); + return d->printEngine->property(QPrintEngine::PPK_SupportsMultipleCopies).toBool(); +} /*! \since 4.1 @@ -2262,8 +2303,8 @@ bool QPrinter::isOptionEnabled( PrinterOption option ) const \value PPK_FullPage A boolean describing if the printer should be full page or not. - \value PPK_NumberOfCopies An integer specifying the number of - copies + \value PPK_NumberOfCopies Obsolete. An integer specifying the number of + copies. Use PPK_CopyCount instead. \value PPK_Orientation Specifies a QPrinter::Orientation value. @@ -2310,6 +2351,11 @@ bool QPrinter::isOptionEnabled( PrinterOption option ) const \value PPK_PageMargins A QList<QVariant> containing the left, top, right and bottom margin values. + \value PPK_CopyCount An integer specifying the number of copies to print. + + \value PPK_SupportsMultipleCopies A boolean value indicating whether or not + the printer supports printing multiple copies in one job. + \value PPK_CustomBase Basis for extension. */ diff --git a/src/gui/painting/qprinter.h b/src/gui/painting/qprinter.h index 5aa891e..996a954 100644 --- a/src/gui/painting/qprinter.h +++ b/src/gui/painting/qprinter.h @@ -124,7 +124,7 @@ public: enum OutputFormat { NativeFormat, PdfFormat, PostScriptFormat }; // ### Qt 5: Merge with QAbstractPrintDialog::PrintRange - enum PrintRange { AllPages, Selection, PageRange }; + enum PrintRange { AllPages, Selection, PageRange, CurrentPage }; enum Unit { Millimeter, @@ -199,6 +199,10 @@ public: int actualNumCopies() const; + void setCopyCount(int); + int copyCount() const; + bool supportsMultipleCopies() const; + void setPaperSource(PaperSource); PaperSource paperSource() const; diff --git a/src/gui/painting/qprinterinfo.qdoc b/src/gui/painting/qprinterinfo.qdoc index 7e1a097..9193213 100644 --- a/src/gui/painting/qprinterinfo.qdoc +++ b/src/gui/painting/qprinterinfo.qdoc @@ -6,35 +6,21 @@ ** ** This file is part of the documentation of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL$ +** $QT_BEGIN_LICENSE:FDL$ ** 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. +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/painting/qrasterdefs_p.h b/src/gui/painting/qrasterdefs_p.h index c33fa57..f6339ed 100644 --- a/src/gui/painting/qrasterdefs_p.h +++ b/src/gui/painting/qrasterdefs_p.h @@ -81,7 +81,6 @@ QT_FT_BEGIN_HEADER - /*************************************************************************/ /* */ /* <Section> */ @@ -101,7 +100,7 @@ QT_FT_BEGIN_HEADER /* distances in integer font units, or 16,16, or 26.6 fixed float */ /* pixel coordinates. */ /* */ - typedef signed long QT_FT_Pos; + typedef signed int QT_FT_Pos; /*************************************************************************/ @@ -837,7 +836,7 @@ QT_FT_BEGIN_HEADER /* A handle (pointer) to a raster object. Each object can be used */ /* independently to convert an outline into a bitmap or pixmap. */ /* */ - typedef struct QT_FT_RasterRec_* QT_FT_Raster; + typedef struct TRaster_ *QT_FT_Raster; /*************************************************************************/ @@ -1089,6 +1088,7 @@ QT_FT_BEGIN_HEADER QT_FT_Raster_BitSet_Func bit_set; /* doesn't work! */ void* user; QT_FT_BBox clip_box; + int skip_spans; } QT_FT_Raster_Params; @@ -1118,8 +1118,7 @@ QT_FT_BEGIN_HEADER /* ignored by a given raster implementation. */ /* */ typedef int - (*QT_FT_Raster_NewFunc)( void* memory, - QT_FT_Raster* raster ); + (*QT_FT_Raster_NewFunc)( QT_FT_Raster* raster ); #define QT_FT_Raster_New_Func QT_FT_Raster_NewFunc diff --git a/src/gui/painting/qrasterizer.cpp b/src/gui/painting/qrasterizer.cpp index 51d01c9..4fded1f 100644 --- a/src/gui/painting/qrasterizer.cpp +++ b/src/gui/painting/qrasterizer.cpp @@ -65,6 +65,12 @@ typedef int Q16Dot16; #define COORD_ROUNDING 1 // 0: round up, 1: round down #define COORD_OFFSET 32 // 26.6, 32 is half a pixel +static inline QT_FT_Vector PointToVector(const QPointF &p) +{ + QT_FT_Vector result = { QT_FT_Pos(p.x() * 64), QT_FT_Pos(p.y() * 64) }; + return result; +} + class QSpanBuffer { public: QSpanBuffer(ProcessSpans blend, void *data, const QRect &clipRect) @@ -198,9 +204,11 @@ public: }; QScanConverter::QScanConverter() - : m_alloc(0) + : m_lines(0) + , m_alloc(0) , m_size(0) , m_intersections(0) + , m_active(0) { } @@ -310,6 +318,10 @@ struct QBoolToType template <typename T> void qScanConvert(QScanConverter &d, T allVertical) { + if (!d.m_lines.size()) { + d.m_active.reset(); + return; + } qSort(d.m_lines.data(), d.m_lines.data() + d.m_lines.size(), QT_PREPEND_NAMESPACE(topOrder)); int line = 0; for (int y = d.m_lines.first().top; y <= d.m_bottom; ++y) { @@ -687,9 +699,9 @@ static Q16Dot16 intersectPixelFP(int x, Q16Dot16 top, Q16Dot16 bottom, Q16Dot16 } } -static inline bool q16Dot16Compare(qreal p1, qreal p2) +static inline bool q26Dot6Compare(qreal p1, qreal p2) { - return FloatToQ16Dot16(p2 - p1) == 0; + return int((p2 - p1) * 64.) == 0; } static inline qreal qFloorF(qreal v) @@ -702,6 +714,12 @@ static inline qreal qFloorF(qreal v) return floor(v); } +static inline QPointF snapTo26Dot6Grid(const QPointF &p) +{ + return QPointF(qFloorF(p.x() * 64) * (1 / qreal(64)), + qFloorF(p.y() * 64) * (1 / qreal(64))); +} + void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, bool squareCap) { if (a == b || width == 0 || d->clipRect.isEmpty()) @@ -712,17 +730,21 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, QPointF pa = a; QPointF pb = b; - QPointF offs = QPointF(qAbs(b.y() - a.y()), qAbs(b.x() - a.x())) * width * 0.5; - if (squareCap) - offs += QPointF(offs.y(), offs.x()); + if (squareCap) { + QPointF delta = pb - pa; + pa -= (0.5f * width) * delta; + pb += (0.5f * width) * delta; + } + + QPointF offs = QPointF(qAbs(b.y() - a.y()), qAbs(b.x() - a.x())) * width * 0.5; const QRectF clip(d->clipRect.topLeft() - offs, d->clipRect.bottomRight() + QPoint(1, 1) + offs); - if (!clip.contains(a) || !clip.contains(b)) { + if (!clip.contains(pa) || !clip.contains(pb)) { qreal t1 = 0; qreal t2 = 1; - const qreal o[2] = { a.x(), a.y() }; - const qreal d[2] = { b.x() - a.x(), b.y() - a.y() }; + const qreal o[2] = { pa.x(), pa.y() }; + const qreal d[2] = { pb.x() - pa.x(), pb.y() - pa.y() }; const qreal low[2] = { clip.left(), clip.top() }; const qreal high[2] = { clip.right(), clip.bottom() }; @@ -745,8 +767,12 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, if (t1 >= t2) return; } - pa = a + (b - a) * t1; - pb = a + (b - a) * t2; + + QPointF npa = pa + (pb - pa) * t1; + QPointF npb = pa + (pb - pa) * t2; + + pa = npa; + pb = npb; } if (!d->antialiased) { @@ -757,15 +783,6 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, } { - const qreal gridResolution = 64; - const qreal reciprocal = 1 / gridResolution; - - // snap to grid to prevent large slopes - pa.rx() = qFloorF(pa.rx() * gridResolution) * reciprocal; - pa.ry() = qFloorF(pa.ry() * gridResolution) * reciprocal; - pb.rx() = qFloorF(pb.rx() * gridResolution) * reciprocal; - pb.ry() = qFloorF(pb.ry() * gridResolution) * reciprocal; - // old delta const QPointF d0 = a - b; const qreal w0 = d0.x() * d0.x() + d0.y() * d0.y(); @@ -783,7 +800,7 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, QSpanBuffer buffer(d->blend, d->data, d->clipRect); - if (q16Dot16Compare(pa.y(), pb.y())) { + if (q26Dot6Compare(pa.y(), pb.y())) { const qreal x = (pa.x() + pb.x()) * 0.5f; const qreal dx = qAbs(pb.x() - pa.x()) * 0.5f; @@ -793,26 +810,16 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, pa = QPointF(x, y - dy); pb = QPointF(x, y + dy); - if (squareCap) - width = 1 / width + 1.0f; - else - width = 1 / width; - - squareCap = false; + width = 1 / width; } - if (q16Dot16Compare(pa.x(), pb.x())) { + if (q26Dot6Compare(pa.x(), pb.x())) { if (pa.y() > pb.y()) qSwap(pa, pb); const qreal dy = pb.y() - pa.y(); const qreal halfWidth = 0.5f * width * dy; - if (squareCap) { - pa.ry() -= halfWidth; - pb.ry() += halfWidth; - } - qreal left = pa.x() - halfWidth; qreal right = pa.x() + halfWidth; @@ -822,7 +829,7 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, pa.ry() = qBound(qreal(d->clipRect.top()), pa.y(), qreal(d->clipRect.bottom() + 1)); pb.ry() = qBound(qreal(d->clipRect.top()), pb.y(), qreal(d->clipRect.bottom() + 1)); - if (q16Dot16Compare(left, right) || q16Dot16Compare(pa.y(), pb.y())) + if (q26Dot6Compare(left, right) || q26Dot6Compare(pa.y(), pb.y())) return; if (d->antialiased) { @@ -893,11 +900,6 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, delta *= 0.5f * width; const QPointF perp(delta.y(), -delta.x()); - if (squareCap) { - pa -= delta; - pb += delta; - } - QPointF top; QPointF left; QPointF right; @@ -915,14 +917,36 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, bottom = pb + perp; } + top = snapTo26Dot6Grid(top); + bottom = snapTo26Dot6Grid(bottom); + left = snapTo26Dot6Grid(left); + right = snapTo26Dot6Grid(right); + const qreal topBound = qBound(qreal(d->clipRect.top()), top.y(), qreal(d->clipRect.bottom())); const qreal bottomBound = qBound(qreal(d->clipRect.top()), bottom.y(), qreal(d->clipRect.bottom())); - const qreal leftSlope = (left.x() - top.x()) / (left.y() - top.y()); - const qreal rightSlope = -1.0f / leftSlope; + const QPointF topLeftEdge = left - top; + const QPointF topRightEdge = right - top; + const QPointF bottomLeftEdge = bottom - left; + const QPointF bottomRightEdge = bottom - right; + + const qreal topLeftSlope = topLeftEdge.x() / topLeftEdge.y(); + const qreal bottomLeftSlope = bottomLeftEdge.x() / bottomLeftEdge.y(); + + const qreal topRightSlope = topRightEdge.x() / topRightEdge.y(); + const qreal bottomRightSlope = bottomRightEdge.x() / bottomRightEdge.y(); - const Q16Dot16 leftSlopeFP = FloatToQ16Dot16(leftSlope); - const Q16Dot16 rightSlopeFP = FloatToQ16Dot16(rightSlope); + const Q16Dot16 topLeftSlopeFP = FloatToQ16Dot16(topLeftSlope); + const Q16Dot16 topRightSlopeFP = FloatToQ16Dot16(topRightSlope); + + const Q16Dot16 bottomLeftSlopeFP = FloatToQ16Dot16(bottomLeftSlope); + const Q16Dot16 bottomRightSlopeFP = FloatToQ16Dot16(bottomRightSlope); + + const Q16Dot16 invTopLeftSlopeFP = FloatToQ16Dot16(1 / topLeftSlope); + const Q16Dot16 invTopRightSlopeFP = FloatToQ16Dot16(1 / topRightSlope); + + const Q16Dot16 invBottomLeftSlopeFP = FloatToQ16Dot16(1 / bottomLeftSlope); + const Q16Dot16 invBottomRightSlopeFP = FloatToQ16Dot16(1 / bottomRightSlope); if (d->antialiased) { const Q16Dot16 iTopFP = IntToQ16Dot16(int(topBound)); @@ -930,16 +954,16 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, const Q16Dot16 iRightFP = IntToQ16Dot16(int(right.y())); const Q16Dot16 iBottomFP = IntToQ16Dot16(int(bottomBound)); - Q16Dot16 leftIntersectAf = FloatToQ16Dot16(top.x() + (int(topBound) - top.y()) * leftSlope); - Q16Dot16 rightIntersectAf = FloatToQ16Dot16(top.x() + (int(topBound) - top.y()) * rightSlope); + Q16Dot16 leftIntersectAf = FloatToQ16Dot16(top.x() + (int(topBound) - top.y()) * topLeftSlope); + Q16Dot16 rightIntersectAf = FloatToQ16Dot16(top.x() + (int(topBound) - top.y()) * topRightSlope); Q16Dot16 leftIntersectBf = 0; Q16Dot16 rightIntersectBf = 0; if (iLeftFP < iTopFP) - leftIntersectBf = FloatToQ16Dot16(left.x() + (int(topBound) - left.y()) * rightSlope); + leftIntersectBf = FloatToQ16Dot16(left.x() + (int(topBound) - left.y()) * bottomLeftSlope); if (iRightFP < iTopFP) - rightIntersectBf = FloatToQ16Dot16(right.x() + (int(topBound) - right.y()) * leftSlope); + rightIntersectBf = FloatToQ16Dot16(right.x() + (int(topBound) - right.y()) * bottomRightSlope); Q16Dot16 rowTop, rowBottomLeft, rowBottomRight, rowTopLeft, rowTopRight, rowBottom; Q16Dot16 topLeftIntersectAf, topLeftIntersectBf, topRightIntersectAf, topRightIntersectBf; @@ -954,9 +978,9 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, rowTop = qMax(iTopFP, yTopFP); topLeftIntersectAf = leftIntersectAf + - Q16Dot16Multiply(leftSlopeFP, rowTop - iTopFP); + Q16Dot16Multiply(topLeftSlopeFP, rowTop - iTopFP); topRightIntersectAf = rightIntersectAf + - Q16Dot16Multiply(rightSlopeFP, rowTop - iTopFP); + Q16Dot16Multiply(topRightSlopeFP, rowTop - iTopFP); Q16Dot16 yFP = iTopFP; while (yFP <= iBottomFP) { @@ -968,30 +992,30 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, if (yFP == iLeftFP) { const int y = Q16Dot16ToInt(yFP); - leftIntersectBf = FloatToQ16Dot16(left.x() + (y - left.y()) * rightSlope); - topLeftIntersectBf = leftIntersectBf + Q16Dot16Multiply(rightSlopeFP, rowTopLeft - yFP); - bottomLeftIntersectAf = leftIntersectAf + Q16Dot16Multiply(leftSlopeFP, rowBottomLeft - yFP); + leftIntersectBf = FloatToQ16Dot16(left.x() + (y - left.y()) * bottomLeftSlope); + topLeftIntersectBf = leftIntersectBf + Q16Dot16Multiply(bottomLeftSlopeFP, rowTopLeft - yFP); + bottomLeftIntersectAf = leftIntersectAf + Q16Dot16Multiply(topLeftSlopeFP, rowBottomLeft - yFP); } else { topLeftIntersectBf = leftIntersectBf; - bottomLeftIntersectAf = leftIntersectAf + leftSlopeFP; + bottomLeftIntersectAf = leftIntersectAf + topLeftSlopeFP; } if (yFP == iRightFP) { const int y = Q16Dot16ToInt(yFP); - rightIntersectBf = FloatToQ16Dot16(right.x() + (y - right.y()) * leftSlope); - topRightIntersectBf = rightIntersectBf + Q16Dot16Multiply(leftSlopeFP, rowTopRight - yFP); - bottomRightIntersectAf = rightIntersectAf + Q16Dot16Multiply(rightSlopeFP, rowBottomRight - yFP); + rightIntersectBf = FloatToQ16Dot16(right.x() + (y - right.y()) * bottomRightSlope); + topRightIntersectBf = rightIntersectBf + Q16Dot16Multiply(bottomRightSlopeFP, rowTopRight - yFP); + bottomRightIntersectAf = rightIntersectAf + Q16Dot16Multiply(topRightSlopeFP, rowBottomRight - yFP); } else { topRightIntersectBf = rightIntersectBf; - bottomRightIntersectAf = rightIntersectAf + rightSlopeFP; + bottomRightIntersectAf = rightIntersectAf + topRightSlopeFP; } if (yFP == iBottomFP) { - bottomLeftIntersectBf = leftIntersectBf + Q16Dot16Multiply(rightSlopeFP, rowBottom - yFP); - bottomRightIntersectBf = rightIntersectBf + Q16Dot16Multiply(leftSlopeFP, rowBottom - yFP); + bottomLeftIntersectBf = leftIntersectBf + Q16Dot16Multiply(bottomLeftSlopeFP, rowBottom - yFP); + bottomRightIntersectBf = rightIntersectBf + Q16Dot16Multiply(bottomRightSlopeFP, rowBottom - yFP); } else { - bottomLeftIntersectBf = leftIntersectBf + rightSlopeFP; - bottomRightIntersectBf = rightIntersectBf + leftSlopeFP; + bottomLeftIntersectBf = leftIntersectBf + bottomLeftSlopeFP; + bottomRightIntersectBf = rightIntersectBf + bottomRightSlopeFP; } if (yFP < iLeftFP) { @@ -1036,21 +1060,21 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, if (yFP <= iLeftFP) excluded += intersectPixelFP(x, rowTop, rowBottomLeft, bottomLeftIntersectAf, topLeftIntersectAf, - leftSlopeFP, -rightSlopeFP); + topLeftSlopeFP, invTopLeftSlopeFP); if (yFP >= iLeftFP) excluded += intersectPixelFP(x, rowTopLeft, rowBottom, topLeftIntersectBf, bottomLeftIntersectBf, - rightSlopeFP, -leftSlopeFP); + bottomLeftSlopeFP, invBottomLeftSlopeFP); if (x >= rightMin) { if (yFP <= iRightFP) excluded += (rowBottomRight - rowTop) - intersectPixelFP(x, rowTop, rowBottomRight, topRightIntersectAf, bottomRightIntersectAf, - rightSlopeFP, -leftSlopeFP); + topRightSlopeFP, invTopRightSlopeFP); if (yFP >= iRightFP) excluded += (rowBottom - rowTopRight) - intersectPixelFP(x, rowTopRight, rowBottom, bottomRightIntersectBf, topRightIntersectBf, - leftSlopeFP, -rightSlopeFP); + bottomRightSlopeFP, invBottomRightSlopeFP); } Q16Dot16 coverage = rowHeight - excluded; @@ -1068,11 +1092,11 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, if (yFP <= iRightFP) excluded += (rowBottomRight - rowTop) - intersectPixelFP(x, rowTop, rowBottomRight, topRightIntersectAf, bottomRightIntersectAf, - rightSlopeFP, -leftSlopeFP); + topRightSlopeFP, invTopRightSlopeFP); if (yFP >= iRightFP) excluded += (rowBottom - rowTopRight) - intersectPixelFP(x, rowTopRight, rowBottom, bottomRightIntersectBf, topRightIntersectBf, - leftSlopeFP, -rightSlopeFP); + bottomRightSlopeFP, invBottomRightSlopeFP); Q16Dot16 coverage = rowHeight - excluded; buffer.addSpan(x, 1, Q16Dot16ToInt(yFP), @@ -1080,10 +1104,10 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, ++x; } - leftIntersectAf += leftSlopeFP; - leftIntersectBf += rightSlopeFP; - rightIntersectAf += rightSlopeFP; - rightIntersectBf += leftSlopeFP; + leftIntersectAf += topLeftSlopeFP; + leftIntersectBf += bottomLeftSlopeFP; + rightIntersectAf += topRightSlopeFP; + rightIntersectBf += bottomRightSlopeFP; topLeftIntersectAf = leftIntersectAf; topRightIntersectAf = rightIntersectAf; @@ -1097,10 +1121,10 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, int iBottom = bottom.y() < 0.5f? -1 : int(bottom.y() - 0.5f); int iMiddle = qMin(iLeft, iRight); - Q16Dot16 leftIntersectAf = FloatToQ16Dot16(top.x() + 0.5f + (iTop + 0.5f - top.y()) * leftSlope); - Q16Dot16 leftIntersectBf = FloatToQ16Dot16(left.x() + 0.5f + (iLeft + 1.5f - left.y()) * rightSlope); - Q16Dot16 rightIntersectAf = FloatToQ16Dot16(top.x() - 0.5f + (iTop + 0.5f - top.y()) * rightSlope); - Q16Dot16 rightIntersectBf = FloatToQ16Dot16(right.x() - 0.5f + (iRight + 1.5f - right.y()) * leftSlope); + Q16Dot16 leftIntersectAf = FloatToQ16Dot16(top.x() + 0.5f + (iTop + 0.5f - top.y()) * topLeftSlope); + Q16Dot16 leftIntersectBf = FloatToQ16Dot16(left.x() + 0.5f + (iLeft + 1.5f - left.y()) * bottomLeftSlope); + Q16Dot16 rightIntersectAf = FloatToQ16Dot16(top.x() - 0.5f + (iTop + 0.5f - top.y()) * topRightSlope); + Q16Dot16 rightIntersectBf = FloatToQ16Dot16(right.x() - 0.5f + (iRight + 1.5f - right.y()) * bottomRightSlope); int ny; int y = iTop; @@ -1122,10 +1146,10 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, ri += rs; \ } - DO_SEGMENT(iMiddle, leftIntersectAf, rightIntersectAf, leftSlopeFP, rightSlopeFP) - DO_SEGMENT(iRight, leftIntersectBf, rightIntersectAf, rightSlopeFP, rightSlopeFP) - DO_SEGMENT(iLeft, leftIntersectAf, rightIntersectBf, leftSlopeFP, leftSlopeFP) - DO_SEGMENT(iBottom, leftIntersectBf, rightIntersectBf, rightSlopeFP, leftSlopeFP) + DO_SEGMENT(iMiddle, leftIntersectAf, rightIntersectAf, topLeftSlopeFP, topRightSlopeFP) + DO_SEGMENT(iRight, leftIntersectBf, rightIntersectAf, bottomLeftSlopeFP, topRightSlopeFP) + DO_SEGMENT(iLeft, leftIntersectAf, rightIntersectBf, topLeftSlopeFP, bottomRightSlopeFP); + DO_SEGMENT(iBottom, leftIntersectBf, rightIntersectBf, bottomLeftSlopeFP, bottomRightSlopeFP); #undef DO_SEGMENT } } @@ -1177,12 +1201,6 @@ void QRasterizer::rasterize(const QT_FT_Outline *outline, Qt::FillRule fillRule) d->scanConverter.end(); } -static inline QT_FT_Vector PointToVector(const QPointF &p) -{ - QT_FT_Vector result = { QT_FT_Pos(p.x() * 64), QT_FT_Pos(p.y() * 64) }; - return result; -} - void QRasterizer::rasterize(const QPainterPath &path, Qt::FillRule fillRule) { if (path.isEmpty()) diff --git a/src/gui/painting/qregion.cpp b/src/gui/painting/qregion.cpp index bea2b6e..3412f9b 100644 --- a/src/gui/painting/qregion.cpp +++ b/src/gui/painting/qregion.cpp @@ -704,28 +704,13 @@ bool QRegion::intersects(const QRegion ®ion) const } /*! + \fn bool QRegion::intersects(const QRect &rect) const \since 4.2 Returns true if this region intersects with \a rect, otherwise returns false. */ -bool QRegion::intersects(const QRect &rect) const -{ - if (isEmpty() || rect.isNull()) - return false; - const QRect r = rect.normalized(); - if (!rect_intersects(boundingRect(), r)) - return false; - if (rectCount() == 1) - return true; - - const QVector<QRect> myRects = rects(); - for (QVector<QRect>::const_iterator it = myRects.constBegin(); it < myRects.constEnd(); ++it) - if (rect_intersects(r, *it)) - return true; - return false; -} #if !defined (Q_OS_UNIX) && !defined (Q_WS_WIN) /*! @@ -1714,8 +1699,8 @@ QT_BEGIN_INCLUDE_NAMESPACE #include <limits.h> QT_END_INCLUDE_NAMESPACE -/* 1 if two BOXs overlap. - * 0 if two BOXs do not overlap. +/* 1 if two BOXes overlap. + * 0 if two BOXes do not overlap. * Remember, x2 and y2 are not in the region */ #define EXTENTCHECK(r1, r2) \ @@ -4349,5 +4334,24 @@ bool QRegion::operator==(const QRegion &r) const return EqualRegion(d->qt_rgn, r.d->qt_rgn); } +bool QRegion::intersects(const QRect &rect) const +{ + if (isEmptyHelper(d->qt_rgn) || rect.isNull()) + return false; + + const QRect r = rect.normalized(); + if (!rect_intersects(d->qt_rgn->extents, r)) + return false; + if (d->qt_rgn->numRects == 1) + return true; + + const QVector<QRect> myRects = rects(); + for (QVector<QRect>::const_iterator it = myRects.constBegin(); it < myRects.constEnd(); ++it) + if (rect_intersects(r, *it)) + return true; + return false; +} + + #endif QT_END_NAMESPACE diff --git a/src/gui/painting/qregion.h b/src/gui/painting/qregion.h index bc4da28..acb20c1 100644 --- a/src/gui/painting/qregion.h +++ b/src/gui/painting/qregion.h @@ -201,7 +201,7 @@ private: Region rgn; void *xrectangles; #elif defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA) - mutable RgnHandle unused; // Here for binary compatability reasons. ### Qt 5 remove. + mutable RgnHandle unused; // Here for binary compatibility reasons. ### Qt 5 remove. #endif #if defined(Q_WS_QWS) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN) QRegionPrivate *qt_rgn; diff --git a/src/gui/painting/qrgb.h b/src/gui/painting/qrgb.h index 8e635a8..ea5f353 100644 --- a/src/gui/painting/qrgb.h +++ b/src/gui/painting/qrgb.h @@ -64,7 +64,7 @@ Q_GUI_EXPORT_INLINE int qBlue(QRgb rgb) // get blue part of RGB { return (rgb & 0xff); } Q_GUI_EXPORT_INLINE int qAlpha(QRgb rgb) // get alpha part of RGBA -{ return ((rgb >> 24) & 0xff); } +{ return rgb >> 24; } Q_GUI_EXPORT_INLINE QRgb qRgb(int r, int g, int b)// set RGB value { return (0xffu << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); } diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp index 094d42e..9decf41 100644 --- a/src/gui/painting/qstroker.cpp +++ b/src/gui/painting/qstroker.cpp @@ -120,8 +120,8 @@ private: class QSubpathFlatIterator { public: - QSubpathFlatIterator(const QDataBuffer<QStrokerOps::Element> *path) - : m_path(path), m_pos(0), m_curve_index(-1) { } + QSubpathFlatIterator(const QDataBuffer<QStrokerOps::Element> *path, qreal threshold) + : m_path(path), m_pos(0), m_curve_index(-1), m_curve_threshold(threshold) { } inline bool hasNext() const { return m_curve_index >= 0 || m_pos < m_path->size(); } @@ -152,7 +152,7 @@ public: QPointF(qt_fixed_to_real(m_path->at(m_pos+1).x), qt_fixed_to_real(m_path->at(m_pos+1).y)), QPointF(qt_fixed_to_real(m_path->at(m_pos+2).x), - qt_fixed_to_real(m_path->at(m_pos+2).y))).toPolygon(); + qt_fixed_to_real(m_path->at(m_pos+2).y))).toPolygon(m_curve_threshold); m_curve_index = 1; e.type = QPainterPath::LineToElement; e.x = m_curve.at(0).x(); @@ -169,6 +169,7 @@ private: int m_pos; QPolygonF m_curve; int m_curve_index; + qreal m_curve_threshold; }; template <class Iterator> bool qt_stroke_side(Iterator *it, QStroker *stroker, @@ -187,7 +188,13 @@ static inline qreal adapted_angle_on_x(const QLineF &line) } QStrokerOps::QStrokerOps() - : m_customData(0), m_moveTo(0), m_lineTo(0), m_cubicTo(0) + : m_elements(0) + , m_curveThreshold(qt_real_to_fixed(0.25)) + , m_dashThreshold(qt_real_to_fixed(0.25)) + , m_customData(0) + , m_moveTo(0) + , m_lineTo(0) + , m_cubicTo(0) { } @@ -195,7 +202,6 @@ QStrokerOps::~QStrokerOps() { } - /*! Prepares the stroker. Call this function once before starting a stroke by calling moveTo, lineTo or cubicTo. @@ -238,6 +244,7 @@ void QStrokerOps::strokePath(const QPainterPath &path, void *customData, const Q if (path.isEmpty()) return; + setCurveThresholdFromTransform(QTransform()); begin(customData); int count = path.elementCount(); if (matrix.isIdentity()) { @@ -308,6 +315,8 @@ void QStrokerOps::strokePolygon(const QPointF *points, int pointCount, bool impl { if (!pointCount) return; + + setCurveThresholdFromTransform(QTransform()); begin(data); if (matrix.isIdentity()) { moveTo(qt_real_to_fixed(points[0].x()), qt_real_to_fixed(points[0].y())); @@ -348,6 +357,7 @@ void QStrokerOps::strokeEllipse(const QRectF &rect, void *data, const QTransform } } + setCurveThresholdFromTransform(QTransform()); begin(data); moveTo(qt_real_to_fixed(start.x()), qt_real_to_fixed(start.y())); for (int i=0; i<12; i+=3) { @@ -366,12 +376,10 @@ QStroker::QStroker() { m_strokeWidth = qt_real_to_fixed(1); m_miterLimit = qt_real_to_fixed(2); - m_curveThreshold = qt_real_to_fixed(0.25); } QStroker::~QStroker() { - } Qt::PenCapStyle QStroker::capForJoinMode(LineJoinMode mode) @@ -602,7 +610,7 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine } QLineF miterLine(QPointF(qt_fixed_to_real(focal_x), qt_fixed_to_real(focal_y)), isect); - if (miterLine.length() > qt_fixed_to_real(m_strokeWidth * m_miterLimit) / 2) { + if (type == QLineF::NoIntersection || miterLine.length() > qt_fixed_to_real(m_strokeWidth * m_miterLimit) / 2) { emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1())); } else { @@ -1043,6 +1051,47 @@ QVector<qfixed> QDashStroker::patternForStyle(Qt::PenStyle style) return pattern; } +static inline bool lineRectIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br) +{ + return ((p1.x > tl.x || p2.x > tl.x) && (p1.x < br.x || p2.x < br.x) + && (p1.y > tl.y || p2.y > tl.y) && (p1.y < br.y || p2.y < br.y)); +} + +// If the line intersects the rectangle, this function will return true. +static bool lineIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br) +{ + if (!lineRectIntersectsRect(p1, p2, tl, br)) + return false; + if (p1.x == p2.x || p1.y == p2.y) + return true; + + if (p1.y > p2.y) + qSwap(p1, p2); // make p1 above p2 + qfixed2d u; + qfixed2d v; + qfixed2d w = {p2.x - p1.x, p2.y - p1.y}; + if (p1.x < p2.x) { + // backslash + u.x = tl.x - p1.x; u.y = br.y - p1.y; + v.x = br.x - p1.x; v.y = tl.y - p1.y; + } else { + // slash + u.x = tl.x - p1.x; u.y = tl.y - p1.y; + v.x = br.x - p1.x; v.y = br.y - p1.y; + } +#if defined(QFIXED_IS_26_6) || defined(QFIXED_IS_16_16) + qint64 val1 = qint64(u.x) * qint64(w.y) - qint64(u.y) * qint64(w.x); + qint64 val2 = qint64(v.x) * qint64(w.y) - qint64(v.y) * qint64(w.x); + return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0); +#elif defined(QFIXED_IS_32_32) + // Cannot do proper test because it may overflow. + return true; +#else + qreal val1 = u.x * w.y - u.y * w.x; + qreal val2 = v.x * w.y - v.y * w.x; + return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0); +#endif +} void QDashStroker::processCurrentSubpath() { @@ -1067,9 +1116,11 @@ void QDashStroker::processCurrentSubpath() if (qFuzzyIsNull(sumLength)) return; + qreal invSumLength = qreal(1) / sumLength; + Q_ASSERT(dashCount > 0); - dashCount = (dashCount / 2) * 2; // Round down to even number + dashCount = dashCount & -2; // Round down to even number int idash = 0; // Index to current dash qreal pos = 0; // The position on the curve, 0 <= pos <= path.length @@ -1077,11 +1128,12 @@ void QDashStroker::processCurrentSubpath() qreal doffset = m_dashOffset * m_stroke_width; // make sure doffset is in range [0..sumLength) - doffset -= qFloor(doffset / sumLength) * sumLength; + doffset -= qFloor(doffset * invSumLength) * sumLength; while (doffset >= dashes[idash]) { doffset -= dashes[idash]; - idash = (idash + 1) % dashCount; + if (++idash >= dashCount) + idash = 0; } qreal estart = 0; // The elements starting position @@ -1091,7 +1143,7 @@ void QDashStroker::processCurrentSubpath() QPainterPath dashPath; - QSubpathFlatIterator it(&m_elements); + QSubpathFlatIterator it(&m_elements, m_dashThreshold); qfixed2d prev = it.next(); bool clipping = !m_clip_rect.isEmpty(); @@ -1119,12 +1171,41 @@ void QDashStroker::processCurrentSubpath() estop = estart + elen; bool done = pos >= estop; + + if (clipping) { + // Check if the entire line can be clipped away. + if (!lineIntersectsRect(prev, e, clip_tl, clip_br)) { + // Cut away full dash sequences. + elen -= qFloor(elen * invSumLength) * sumLength; + // Update dash offset. + while (!done) { + qreal dpos = pos + dashes[idash] - doffset - estart; + + Q_ASSERT(dpos >= 0); + + if (dpos > elen) { // dash extends this line + doffset = dashes[idash] - (dpos - elen); // subtract the part already used + pos = estop; // move pos to next path element + done = true; + } else { // Dash is on this line + pos = dpos + estart; + done = pos >= estop; + if (++idash >= dashCount) + idash = 0; + doffset = 0; // full segment so no offset on next. + } + } + hasMoveTo = false; + move_to_pos = e; + } + } + // Dash away... while (!done) { QPointF p2; - int idash_incr = 0; bool has_offset = doffset > 0; + bool evenDash = (idash & 1) == 0; qreal dpos = pos + dashes[idash] - doffset - estart; Q_ASSERT(dpos >= 0); @@ -1138,39 +1219,36 @@ void QDashStroker::processCurrentSubpath() p2 = cline.pointAt(dpos/elen); pos = dpos + estart; done = pos >= estop; - idash_incr = 1; + if (++idash >= dashCount) + idash = 0; doffset = 0; // full segment so no offset on next. } - if (idash % 2 == 0) { + if (evenDash) { line_to_pos.x = qt_real_to_fixed(p2.x()); line_to_pos.y = qt_real_to_fixed(p2.y()); - // If we have an offset, we're continuing a dash - // from a previous element and should only - // continue the current dash, without starting a - // new subpath. - if (!has_offset || !hasMoveTo) { - emitMoveTo(move_to_pos.x, move_to_pos.y); - hasMoveTo = true; - } - if (!clipping - // if move_to is inside... - || (move_to_pos.x > clip_tl.x && move_to_pos.x < clip_br.x - && move_to_pos.y > clip_tl.y && move_to_pos.y < clip_br.y) - // Or if line_to is inside... - || (line_to_pos.x > clip_tl.x && line_to_pos.x < clip_br.x - && line_to_pos.y > clip_tl.y && line_to_pos.y < clip_br.y)) + || lineRectIntersectsRect(move_to_pos, line_to_pos, clip_tl, clip_br)) { + // If we have an offset, we're continuing a dash + // from a previous element and should only + // continue the current dash, without starting a + // new subpath. + if (!has_offset || !hasMoveTo) { + emitMoveTo(move_to_pos.x, move_to_pos.y); + hasMoveTo = true; + } + emitLineTo(line_to_pos.x, line_to_pos.y); + } else { + hasMoveTo = false; } + move_to_pos = line_to_pos; } else { move_to_pos.x = qt_real_to_fixed(p2.x()); move_to_pos.y = qt_real_to_fixed(p2.y()); } - - idash = (idash + idash_incr) % dashCount; } // Shuffle to the next cycle... diff --git a/src/gui/painting/qstroker_p.h b/src/gui/painting/qstroker_p.h index 3e622a8..5607a8e 100644 --- a/src/gui/painting/qstroker_p.h +++ b/src/gui/painting/qstroker_p.h @@ -124,6 +124,9 @@ typedef void (*qStrokerCubicToHook)(qfixed c1x, qfixed c1y, qfixed ex, qfixed ey, void *data); +// qtransform.cpp +Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale); + class Q_GUI_EXPORT QStrokerOps { public: @@ -161,6 +164,16 @@ public: QRectF clipRect() const { return m_clip_rect; } void setClipRect(const QRectF &clip) { m_clip_rect = clip; } + void setCurveThresholdFromTransform(const QTransform &transform) + { + qreal scale; + qt_scaleForTransform(transform, &scale); + m_dashThreshold = scale == 0 ? qreal(0.5) : (qreal(0.5) / scale); + } + + void setCurveThreshold(qfixed threshold) { m_curveThreshold = threshold; } + qfixed curveThreshold() const { return m_curveThreshold; } + protected: inline void emitMoveTo(qfixed x, qfixed y); inline void emitLineTo(qfixed x, qfixed y); @@ -170,6 +183,8 @@ protected: QDataBuffer<Element> m_elements; QRectF m_clip_rect; + qfixed m_curveThreshold; + qfixed m_dashThreshold; void *m_customData; qStrokerMoveToHook m_moveTo; @@ -208,9 +223,6 @@ public: void setMiterLimit(qfixed length) { m_miterLimit = length; } qfixed miterLimit() const { return m_miterLimit; } - void setCurveThreshold(qfixed threshold) { m_curveThreshold = threshold; } - qfixed curveThreshold() const { return m_curveThreshold; } - void joinPoints(qfixed x, qfixed y, const QLineF &nextLine, LineJoinMode join); inline void emitMoveTo(qfixed x, qfixed y); inline void emitLineTo(qfixed x, qfixed y); @@ -227,7 +239,6 @@ protected: qfixed m_strokeWidth; qfixed m_miterLimit; - qfixed m_curveThreshold; LineJoinMode m_capStyle; LineJoinMode m_joinStyle; diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index b65323f..b609f7b 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -47,24 +47,33 @@ #include "private/qnativeimage_p.h" #include "private/qfontengine_ft_p.h" -#ifndef QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH -#define QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH 256 -#endif - QT_BEGIN_NAMESPACE // #define CACHE_DEBUG -void QTextureGlyphCache::populate(const QTextItemInt &ti, - const QVarLengthArray<glyph_t> &glyphs, - const QVarLengthArray<QFixedPoint> &) +// returns the highest number closest to v, which is a power of 2 +// NB! assumes 32 bit ints +static inline int qt_next_power_of_two(int v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + ++v; + return v; +} + +void QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs, + const QFixedPoint *) { #ifdef CACHE_DEBUG - printf("Populating with '%s'\n", QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data()); + printf("Populating with %d glyphs\n", numGlyphs); qDebug() << " -> current transformation: " << m_transform; #endif - m_current_textitem = &ti; + m_current_fontengine = fontEngine; const int margin = glyphMargin(); const int paddingDoubled = glyphPadding() * 2; @@ -72,26 +81,23 @@ void QTextureGlyphCache::populate(const QTextItemInt &ti, int rowHeight = 0; // check each glyph for its metrics and get the required rowHeight. - for (int i=0; i < glyphs.size(); ++i) { + for (int i=0; i < numGlyphs; ++i) { const glyph_t glyph = glyphs[i]; if (coords.contains(glyph)) continue; if (listItemCoordinates.contains(glyph)) continue; - glyph_metrics_t metrics = ti.fontEngine->boundingBox(glyph, m_transform); + glyph_metrics_t metrics = fontEngine->boundingBox(glyph, m_transform); #ifdef CACHE_DEBUG - printf("'%c' (%4x): w=%.2f, h=%.2f, xoff=%.2f, yoff=%.2f, x=%.2f, y=%.2f, ti.ascent=%.2f, ti.descent=%.2f\n", - ti.chars[i].toLatin1(), + printf("(%4x): w=%.2f, h=%.2f, xoff=%.2f, yoff=%.2f, x=%.2f, y=%.2f\n", glyph, metrics.width.toReal(), metrics.height.toReal(), metrics.xoff.toReal(), metrics.yoff.toReal(), metrics.x.toReal(), - metrics.y.toReal(), - ti.ascent.toReal(), - ti.descent.toReal()); + metrics.y.toReal()); #endif int glyph_width = metrics.width.ceil().toInt(); int glyph_height = metrics.height.ceil().toInt(); @@ -117,26 +123,33 @@ void QTextureGlyphCache::populate(const QTextItemInt &ti, rowHeight += margin * 2 + paddingDoubled; if (isNull()) - createCache(QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH, rowHeight); + createCache(QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH, qt_next_power_of_two(rowHeight)); // now actually use the coords and paint the wanted glyps into cache. QHash<glyph_t, Coord>::iterator iter = listItemCoordinates.begin(); while (iter != listItemCoordinates.end()) { Coord c = iter.value(); + m_currentRowHeight = qMax(m_currentRowHeight, c.h + margin * 2); + if (m_cx + c.w > m_w) { - // no room on the current line, start new glyph strip - m_cx = 0; - m_cy = m_h + paddingDoubled; + int new_width = m_w*2; + while (new_width < m_cx + c.w) + new_width *= 2; + if (new_width <= maxTextureWidth()) { + resizeTextureData(new_width, m_h); + m_w = new_width; + } else { + // no room on the current line, start new glyph strip + m_cx = 0; + m_cy += m_currentRowHeight + paddingDoubled; + m_currentRowHeight = 0; // New row + } } if (m_cy + c.h > m_h) { - int new_height; - if (m_cx == 0) { // add a whole row - new_height = m_h + rowHeight; - m_cy = m_h; - } else { // just extend row - new_height = m_cy + rowHeight; - } + int new_height = m_h*2; + while (new_height < m_cy + c.h) + new_height *= 2; // if no room in the current texture - realloc a larger texture resizeTextureData(m_w, new_height); m_h = new_height; @@ -148,14 +161,7 @@ void QTextureGlyphCache::populate(const QTextItemInt &ti, fillTexture(c, iter.key()); coords.insert(iter.key(), c); - if (m_cx + c.w > m_w) { - m_cx = 0; - m_cy += rowHeight; - } else { - // for the Mono case, glyph_width is 8-bit aligned, - // and therefore so will m_cx - m_cx += c.w + paddingDoubled; - } + m_cx += c.w + paddingDoubled; ++iter; } @@ -183,11 +189,11 @@ QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g) const break; }; - QFontEngineFT *ft = static_cast<QFontEngineFT*> (m_current_textitem->fontEngine); + QFontEngineFT *ft = static_cast<QFontEngineFT*> (m_current_fontengine); QFontEngineFT::QGlyphSet *gset = ft->loadTransformedGlyphSet(m_transform); if (gset && ft->loadGlyphs(gset, &g, 1, format)) { - QFontEngineFT::Glyph *glyph = gset->glyph_data.value(g); + QFontEngineFT::Glyph *glyph = gset->getGlyph(g); const int bytesPerLine = (format == QFontEngineFT::Format_Mono ? ((glyph->width + 31) & ~31) >> 3 : (glyph->width + 3) & ~3); return QImage(glyph->data, glyph->width, glyph->height, bytesPerLine, imageFormat); @@ -195,9 +201,9 @@ QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g) const } else #endif if (m_type == QFontEngineGlyphCache::Raster_RGBMask) - return m_current_textitem->fontEngine->alphaRGBMapForGlyph(g, glyphMargin(), m_transform); + return m_current_fontengine->alphaRGBMapForGlyph(g, glyphMargin(), m_transform); else - return m_current_textitem->fontEngine->alphaMapForGlyph(g, m_transform); + return m_current_fontengine->alphaMapForGlyph(g, m_transform); return QImage(); } @@ -325,10 +331,7 @@ void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g) QPoint base(c.x + glyphMargin(), c.y + glyphMargin() + c.baseLineY-1); if (m_image.rect().contains(base)) m_image.setPixel(base, 255); - m_image.save(QString::fromLatin1("cache-%1-%2-%3.png") - .arg(m_current_textitem->font().family()) - .arg(m_current_textitem->font().pointSize()) - .arg(m_transform.type())); + m_image.save(QString::fromLatin1("cache-%1.png").arg(int(this))); #endif } diff --git a/src/gui/painting/qtextureglyphcache_p.h b/src/gui/painting/qtextureglyphcache_p.h index ebb7d6b..e6d2b22 100644 --- a/src/gui/painting/qtextureglyphcache_p.h +++ b/src/gui/painting/qtextureglyphcache_p.h @@ -64,6 +64,10 @@ # undef m_type #endif +#ifndef QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH +#define QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH 256 +#endif + struct glyph_metrics_t; typedef unsigned int glyph_t; @@ -76,7 +80,9 @@ class Q_GUI_EXPORT QTextureGlyphCache : public QFontEngineGlyphCache { public: QTextureGlyphCache(QFontEngineGlyphCache::Type type, const QTransform &matrix) - : QFontEngineGlyphCache(matrix, type), m_w(0), m_h(0), m_cx(0), m_cy(0) { } + : QFontEngineGlyphCache(matrix, type), m_current_fontengine(0), + m_w(0), m_h(0), m_cx(0), m_cy(0), m_currentRowHeight(0) + { } virtual ~QTextureGlyphCache() { } @@ -90,9 +96,8 @@ public: int baseLineY; }; - void populate(const QTextItemInt &ti, - const QVarLengthArray<glyph_t> &glyphs, - const QVarLengthArray<QFixedPoint> &positions); + void populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs, + const QFixedPoint *positions); virtual void createTextureData(int width, int height) = 0; virtual void resizeTextureData(int width, int height) = 0; @@ -112,18 +117,21 @@ public: QHash<glyph_t, Coord> coords; QImage textureMapForGlyph(glyph_t g) const; + virtual int maxTextureWidth() const { return QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH; } + virtual int maxTextureHeight() const { return 32768; } protected: - const QTextItemInt *m_current_textitem; + QFontEngine *m_current_fontengine; int m_w; // image width int m_h; // image height int m_cx; // current x int m_cy; // current y + int m_currentRowHeight; // Height of last row }; -class QImageTextureGlyphCache : public QTextureGlyphCache +class Q_GUI_EXPORT QImageTextureGlyphCache : public QTextureGlyphCache { public: QImageTextureGlyphCache(QFontEngineGlyphCache::Type type, const QTransform &matrix) diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp index 4f42a58..47b7758 100644 --- a/src/gui/painting/qtransform.cpp +++ b/src/gui/painting/qtransform.cpp @@ -47,6 +47,7 @@ #include "qpainterpath.h" #include "qvariant.h" #include <qmath.h> +#include <qnumeric.h> #include <private/qbezier_p.h> @@ -147,8 +148,8 @@ QT_BEGIN_NAMESPACE coordinate system. The standard coordinate system of a QPaintDevice has its origin located at the top-left position. The \e x values increase to the right; \e y values increase - downward. For a complete description, see the \l {The Coordinate - System}{coordinate system} documentation. + downward. For a complete description, see the \l {Coordinate + System} {coordinate system} documentation. QPainter has functions to translate, scale, shear and rotate the coordinate system without using a QTransform. For example: @@ -222,7 +223,7 @@ QT_BEGIN_NAMESPACE \snippet doc/src/snippets/transform/main.cpp 2 \endtable - \sa QPainter, {The Coordinate System}, {demos/affine}{Affine + \sa QPainter, {Coordinate System}, {demos/affine}{Affine Transformations Demo}, {Transformations Example} */ @@ -410,6 +411,12 @@ QTransform &QTransform::translate(qreal dx, qreal dy) { if (dx == 0 && dy == 0) return *this; +#ifndef QT_NO_DEBUG + if (qIsNaN(dx) | qIsNaN(dy)) { + qWarning() << "QTransform::translate with NaN called"; + return *this; + } +#endif switch(inline_type()) { case TxNone: @@ -447,6 +454,12 @@ QTransform &QTransform::translate(qreal dx, qreal dy) */ QTransform QTransform::fromTranslate(qreal dx, qreal dy) { +#ifndef QT_NO_DEBUG + if (qIsNaN(dx) | qIsNaN(dy)) { + qWarning() << "QTransform::fromTranslate with NaN called"; + return QTransform(); +} +#endif QTransform transform(1, 0, 0, 0, 1, 0, dx, dy, 1, true); if (dx == 0 && dy == 0) transform.m_type = TxNone; @@ -466,6 +479,12 @@ QTransform & QTransform::scale(qreal sx, qreal sy) { if (sx == 1 && sy == 1) return *this; +#ifndef QT_NO_DEBUG + if (qIsNaN(sx) | qIsNaN(sy)) { + qWarning() << "QTransform::scale with NaN called"; + return *this; + } +#endif switch(inline_type()) { case TxNone: @@ -501,6 +520,12 @@ QTransform & QTransform::scale(qreal sx, qreal sy) */ QTransform QTransform::fromScale(qreal sx, qreal sy) { +#ifndef QT_NO_DEBUG + if (qIsNaN(sx) | qIsNaN(sy)) { + qWarning() << "QTransform::fromScale with NaN called"; + return QTransform(); +} +#endif QTransform transform(sx, 0, 0, 0, sy, 0, 0, 0, 1, true); if (sx == 1. && sy == 1.) transform.m_type = TxNone; @@ -520,6 +545,12 @@ QTransform & QTransform::shear(qreal sh, qreal sv) { if (sh == 0 && sv == 0) return *this; +#ifndef QT_NO_DEBUG + if (qIsNaN(sh) | qIsNaN(sv)) { + qWarning() << "QTransform::shear with NaN called"; + return *this; + } +#endif switch(inline_type()) { case TxNone: @@ -575,6 +606,12 @@ QTransform & QTransform::rotate(qreal a, Qt::Axis axis) { if (a == 0) return *this; +#ifndef QT_NO_DEBUG + if (qIsNaN(a)) { + qWarning() << "QTransform::rotate with NaN called"; + return *this; + } +#endif qreal sina = 0; qreal cosa = 0; @@ -660,6 +697,12 @@ QTransform & QTransform::rotate(qreal a, Qt::Axis axis) */ QTransform & QTransform::rotateRadians(qreal a, Qt::Axis axis) { +#ifndef QT_NO_DEBUG + if (qIsNaN(a)) { + qWarning() << "QTransform::rotateRadians with NaN called"; + return *this; + } +#endif qreal sina = qSin(a); qreal cosa = qCos(a); @@ -985,7 +1028,7 @@ void QTransform::reset() Writes the given \a matrix to the given \a stream and returns a reference to the stream. - \sa {Format of the QDataStream Operators} + \sa {Serializing Qt Data Types} */ QDataStream & operator<<(QDataStream &s, const QTransform &m) { @@ -1009,7 +1052,7 @@ QDataStream & operator<<(QDataStream &s, const QTransform &m) Reads the given \a matrix from the given \a stream and returns a reference to the stream. - \sa {Format of the QDataStream Operators} + \sa {Serializing Qt Data Types} */ QDataStream & operator>>(QDataStream &s, QTransform &t) { @@ -1037,8 +1080,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 +1101,7 @@ QDebug operator<<(QDebug dbg, const QTransform &m) << " 32=" << m.m32() << " 33=" << m.m33() << ')'; + return dbg.space(); } #endif @@ -1491,12 +1545,19 @@ static inline bool lineTo_clipped(QPainterPath &path, const QTransform &transfor return true; } +Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale); static inline bool cubicTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b, const QPointF &c, const QPointF &d, bool needsMoveTo) { // Convert projective xformed curves to line // segments so they can be transformed more accurately - QPolygonF segment = QBezier::fromPoints(a, b, c, d).toPolygon(); + + qreal scale; + qt_scaleForTransform(transform, &scale); + + qreal curveThreshold = scale == 0 ? qreal(0.25) : (qreal(0.25) / scale); + + QPolygonF segment = QBezier::fromPoints(a, b, c, d).toPolygon(curveThreshold); for (int i = 0; i < segment.size() - 1; ++i) if (lineTo_clipped(path, transform, segment.at(i), segment.at(i+1), needsMoveTo)) @@ -1565,7 +1626,7 @@ static QPainterPath mapProjective(const QTransform &transform, const QPainterPat QPainterPath QTransform::map(const QPainterPath &path) const { TransformationType t = inline_type(); - if (t == TxNone || path.isEmpty()) + if (t == TxNone || path.elementCount() == 0) return path; if (t >= TxProject) diff --git a/src/gui/painting/qtransform.h b/src/gui/painting/qtransform.h index 212a582..11f2673 100644 --- a/src/gui/painting/qtransform.h +++ b/src/gui/painting/qtransform.h @@ -64,7 +64,6 @@ class QVariant; class Q_GUI_EXPORT QTransform { - Q_ENUMS(TransformationType) public: enum TransformationType { TxNone = 0x00, diff --git a/src/gui/painting/qwindowsurface.cpp b/src/gui/painting/qwindowsurface.cpp index 8bd6344..02a8b80 100644 --- a/src/gui/painting/qwindowsurface.cpp +++ b/src/gui/painting/qwindowsurface.cpp @@ -43,19 +43,26 @@ #include <qwidget.h> #include <private/qwidget_p.h> #include <private/qbackingstore_p.h> +#include <private/qapplication_p.h> QT_BEGIN_NAMESPACE class QWindowSurfacePrivate { public: - QWindowSurfacePrivate(QWidget *w) : window(w), staticContentsSupport(false) {} + QWindowSurfacePrivate(QWidget *w) + : window(w) + , staticContentsSupport(0) + , partialUpdateSupport(1) + { + } QWidget *window; QRect geometry; QRegion staticContents; QList<QImage*> bufferImages; - bool staticContentsSupport; + uint staticContentsSupport : 1; + uint partialUpdateSupport : 1; }; /*! @@ -110,8 +117,10 @@ public: QWindowSurface::QWindowSurface(QWidget *window) : d_ptr(new QWindowSurfacePrivate(window)) { - if (window) - window->setWindowSurface(this); + if (!QApplicationPrivate::runtime_graphics_system) { + if(window) + window->setWindowSurface(this); + } } /*! @@ -284,6 +293,10 @@ bool QWindowSurface::hasStaticContentsSupport() const void QWindowSurface::setStaticContentsSupport(bool enable) { + if (enable && !d_ptr->partialUpdateSupport) { + qWarning("QWindowSurface::setStaticContentsSupport: static contents support requires partial update support"); + return; + } d_ptr->staticContentsSupport = enable; } @@ -302,6 +315,20 @@ bool QWindowSurface::hasStaticContents() const return d_ptr->staticContentsSupport && !d_ptr->staticContents.isEmpty(); } +bool QWindowSurface::hasPartialUpdateSupport() const +{ + return d_ptr->partialUpdateSupport; +} + +void QWindowSurface::setPartialUpdateSupport(bool enable) +{ + if (!enable && d_ptr->staticContentsSupport) { + qWarning("QWindowSurface::setPartialUpdateSupport: static contents support requires partial update support"); + return; + } + d_ptr->partialUpdateSupport = enable; +} + void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset) { // make sure we don't detach diff --git a/src/gui/painting/qwindowsurface_p.h b/src/gui/painting/qwindowsurface_p.h index 0a453af..6171ae8 100644 --- a/src/gui/painting/qwindowsurface_p.h +++ b/src/gui/painting/qwindowsurface_p.h @@ -73,8 +73,12 @@ public: QWidget *window() const; virtual QPaintDevice *paintDevice() = 0; - virtual void flush(QWidget *widget, const QRegion ®ion, - const QPoint &offset) = 0; + + // 'widget' can be a child widget, in which case 'region' is in child widget coordinates and + // offset is the (child) widget's offset in relation to the window surface. On QWS, 'offset' + // can be larger than just the offset from the top-level widget as there may also be window + // decorations which are painted into the window surface. + virtual void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset) = 0; virtual void setGeometry(const QRect &rect); QRect geometry() const; @@ -90,6 +94,7 @@ public: inline QRect rect(const QWidget *widget) const; bool hasStaticContentsSupport() const; + bool hasPartialUpdateSupport() const; void setStaticContents(const QRegion ®ion); QRegion staticContents() const; @@ -97,6 +102,7 @@ public: protected: bool hasStaticContents() const; void setStaticContentsSupport(bool enable); + void setPartialUpdateSupport(bool enable); private: QWindowSurfacePrivate *d_ptr; diff --git a/src/gui/painting/qwindowsurface_qws.cpp b/src/gui/painting/qwindowsurface_qws.cpp index a816ed2..170668b 100644 --- a/src/gui/painting/qwindowsurface_qws.cpp +++ b/src/gui/painting/qwindowsurface_qws.cpp @@ -882,7 +882,11 @@ void QWSMemorySurface::beginPaint(const QRegion &rgn) const QVector<QRect> rects = rgn.rects(); const QColor blank = Qt::transparent; for (QVector<QRect>::const_iterator it = rects.begin(); it != rects.end(); ++it) { - p.fillRect(*it, blank); + QRect r = *it; +#ifdef Q_BACKINGSTORE_SUBSURFACES + r.translate(painterOffset()); +#endif + p.fillRect(r, blank); } } QWSWindowSurface::beginPaint(rgn); diff --git a/src/gui/painting/qwindowsurface_s60.cpp b/src/gui/painting/qwindowsurface_s60.cpp index d05c7e4..ea19fcd 100644 --- a/src/gui/painting/qwindowsurface_s60.cpp +++ b/src/gui/painting/qwindowsurface_s60.cpp @@ -43,10 +43,15 @@ #include <QtGui/qpaintdevice.h> #include <private/qwidget_p.h> -#include "qwindowsurface_s60_p.h" -#include "qpixmap_s60_p.h" -#include "qt_s60_p.h" -#include "private/qdrawhelper_p.h" +#include <private/qwindowsurface_s60_p.h> +#include <private/qpixmap_s60_p.h> +#include <private/qt_s60_p.h> +#include <private/qapplication_p.h> +#include <private/qdrawhelper_p.h> + +#ifdef QT_GRAPHICSSYSTEM_RUNTIME +#include <private/qgraphicssystem_runtime_p.h> +#endif QT_BEGIN_NAMESPACE @@ -62,10 +67,14 @@ QS60WindowSurface::QS60WindowSurface(QWidget* widget) TDisplayMode mode = S60->screenDevice()->DisplayMode(); bool isOpaque = qt_widget_private(widget)->isOpaque; - if (mode == EColor16MA && isOpaque) - mode = EColor16MU; // Faster since 16MU -> 16MA is typically accelerated - else if (mode == EColor16MU && !isOpaque) - mode = EColor16MA; // Try for transparency anyway + if (isOpaque) { + mode = EColor16MU; + } else { + if (QSysInfo::symbianVersion() >= QSysInfo::SV_SF_3) + mode = Q_SYMBIAN_ECOLOR16MAP; // Symbian^3 WServ has support for ARGB32_PRE + else + mode = EColor16MA; // Symbian prior to Symbian^3 sw accelerates EColor16MA + } // We create empty CFbsBitmap here -> it will be resized in setGeometry CFbsBitmap *bitmap = q_check_ptr(new CFbsBitmap); // CBase derived object needs check on new @@ -79,13 +88,37 @@ QS60WindowSurface::QS60WindowSurface(QWidget* widget) setStaticContentsSupport(true); } + QS60WindowSurface::~QS60WindowSurface() { +#if defined(QT_GRAPHICSSYSTEM_RUNTIME) && defined(Q_SYMBIAN_SUPPORTS_SURFACES) + if(QApplicationPrivate::runtime_graphics_system) { + QRuntimeGraphicsSystem *runtimeGraphicsSystem = + static_cast<QRuntimeGraphicsSystem*>(QApplicationPrivate::graphics_system); + if(runtimeGraphicsSystem->graphicsSystemName() == QLatin1String("openvg")) { + + // Graphics system has been switched from raster to openvg. + // Issue empty redraw to clear the UI surface + + QWidget *w = window(); + if (w->testAttribute(Qt::WA_WState_Created)) { + RWindow *const window = static_cast<RWindow *>(w->winId()->DrawableWindow()); + window->BeginRedraw(); + window->EndRedraw(); + } + } + } +#endif + delete d_ptr; } void QS60WindowSurface::beginPaint(const QRegion &rgn) { +#ifdef Q_SYMBIAN_SUPPORTS_SURFACES + S60->wsSession().Finish(); +#endif + if (!qt_widget_private(window())->isOpaque) { QS60PixmapData *pixmapData = static_cast<QS60PixmapData *>(d_ptr->device.data_ptr().data()); pixmapData->beginDataAccess(); |