summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xconfigure16
-rw-r--r--src/gui/kernel/qwidget.cpp1
-rw-r--r--src/gui/painting/qbrush.cpp191
-rw-r--r--src/gui/painting/qbrush.h12
-rw-r--r--src/gui/painting/qdrawhelper.cpp238
-rw-r--r--src/gui/painting/qdrawhelper_neon.cpp40
-rw-r--r--src/gui/painting/qdrawhelper_p.h258
-rw-r--r--src/gui/painting/qdrawhelper_sse2.cpp52
-rw-r--r--src/gui/painting/qpaintengine_mac.cpp3
-rw-r--r--src/gui/painting/qpaintengine_raster.cpp81
-rw-r--r--src/gui/painting/qpaintengineex.cpp46
-rw-r--r--src/gui/painting/qpainter.cpp19
-rw-r--r--src/gui/painting/qpainter.h1
-rw-r--r--src/gui/painting/qunifiedtoolbarsurface_mac.cpp3
-rw-r--r--src/gui/text/qglyphs.cpp6
-rw-r--r--src/gui/text/qtextcursor.cpp42
-rw-r--r--src/gui/text/qtextcursor.h4
-rw-r--r--src/gui/text/qtextdocument.cpp23
-rw-r--r--src/gui/text/qtextdocument.h5
-rw-r--r--src/gui/text/qtextdocument_p.cpp15
-rw-r--r--src/gui/text/qtextdocument_p.h4
-rw-r--r--src/gui/text/qtextengine.cpp331
-rw-r--r--src/gui/text/qtextengine_p.h60
-rw-r--r--src/gui/text/qtextlayout.cpp394
-rw-r--r--src/gui/text/qtextlayout.h6
-rw-r--r--src/gui/text/text.pri3
-rw-r--r--src/gui/widgets/qlinecontrol.cpp9
-rw-r--r--src/gui/widgets/qlinecontrol_p.h8
-rw-r--r--src/gui/widgets/qlineedit.cpp28
-rw-r--r--src/gui/widgets/qlineedit.h4
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager.cpp2
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager_p.h2
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadersource_p.h18
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp8
-rw-r--r--src/opengl/qpaintengine_opengl.cpp60
-rw-r--r--src/openvg/qpaintengine_vg.cpp111
-rw-r--r--src/qt3support/text/q3richtext.cpp1
-rw-r--r--tests/arthur/common/paintcommands.cpp34
-rw-r--r--tests/arthur/common/paintcommands.h1
-rw-r--r--tests/arthur/data/qps/radial_gradients_extended.qps95
-rw-r--r--tests/arthur/data/qps/radial_gradients_extended_qps.pngbin0 -> 107978 bytes
-rw-r--r--tests/auto/qcomplextext/tst_qcomplextext.cpp87
-rw-r--r--tests/auto/qlineedit/tst_qlineedit.cpp136
-rw-r--r--tests/auto/qpainter/tst_qpainter.cpp43
-rw-r--r--tests/auto/qpixmap/tst_qpixmap.cpp4
-rw-r--r--tests/auto/qtextedit/tst_qtextedit.cpp149
-rw-r--r--tests/auto/qwizard/tst_qwizard.cpp13
47 files changed, 2111 insertions, 556 deletions
diff --git a/configure b/configure
index 99b79fc..9b923ab 100755
--- a/configure
+++ b/configure
@@ -804,6 +804,7 @@ CFG_MAC_XARCH=auto
CFG_MAC_CARBON=no
CFG_MAC_COCOA=yes
COMMANDLINE_MAC_CARBON=no
+CFG_MAC_HARFBUZZ=no
CFG_SXE=no
CFG_PREFIX_INSTALL=yes
CFG_SDK=
@@ -1040,7 +1041,7 @@ while [ "$#" -gt 0 ]; do
VAL=no
;;
#Qt style yes options
- -incremental|-qvfb|-profile|-shared|-static|-sm|-xinerama|-xshape|-xsync|-xinput|-egl|-reduce-exports|-pch|-separate-debug-info|-stl|-freetype|-xcursor|-xfixes|-xrandr|-xrender|-mitshm|-fontconfig|-xkb|-nis|-qdbus|-dbus|-dbus-linked|-glib|-gstreamer|-gtkstyle|-cups|-iconv|-largefile|-h|-help|-v|-verbose|-debug|-release|-fast|-accessibility|-confirm-license|-gnumake|-framework|-qt3support|-debug-and-release|-exceptions|-cocoa|-carbon|-universal|-prefix-install|-silent|-armfpa|-optimized-qmake|-dwarf2|-reduce-relocations|-sse|-openssl|-openssl-linked|-ptmalloc|-xmlpatterns|-phonon|-phonon-backend|-multimedia|-audio-backend|-svg|-declarative|-declarative-debug|-javascript-jit|-script|-scripttools|-rpath|-force-pkg-config|-s60|-usedeffiles)
+ -incremental|-qvfb|-profile|-shared|-static|-sm|-xinerama|-xshape|-xsync|-xinput|-egl|-reduce-exports|-pch|-separate-debug-info|-stl|-freetype|-xcursor|-xfixes|-xrandr|-xrender|-mitshm|-fontconfig|-xkb|-nis|-qdbus|-dbus|-dbus-linked|-glib|-gstreamer|-gtkstyle|-cups|-iconv|-largefile|-h|-help|-v|-verbose|-debug|-release|-fast|-accessibility|-confirm-license|-gnumake|-framework|-qt3support|-debug-and-release|-exceptions|-cocoa|-carbon|-universal|-harfbuzz|-prefix-install|-silent|-armfpa|-optimized-qmake|-dwarf2|-reduce-relocations|-sse|-openssl|-openssl-linked|-ptmalloc|-xmlpatterns|-phonon|-phonon-backend|-multimedia|-audio-backend|-svg|-declarative|-declarative-debug|-javascript-jit|-script|-scripttools|-rpath|-force-pkg-config|-s60|-usedeffiles)
VAR=`echo $1 | sed "s,^-\(.*\),\1,"`
VAL=yes
;;
@@ -1498,6 +1499,13 @@ while [ "$#" -gt 0 ]; do
UNKNOWN_OPT=yes
fi
;;
+ harfbuzz)
+ if [ "$PLATFORM_MAC" = "yes" ] && [ "$CFG_MAC_CARBON" != "yes" ] && [ "$VAL" = "yes" ]; then
+ CFG_MAC_HARFBUZZ="$VAL"
+ else
+ UNKNOWN_OPT=yes
+ fi
+ ;;
framework)
if [ "$PLATFORM_MAC" = "yes" ] || [ "$PLATFORM_QPA" = "yes" ]; then
@@ -4245,6 +4253,11 @@ Qt/Mac only:
-sdk <sdk> ......... Build Qt using Apple provided SDK <sdk>. This option requires gcc 4.
To use a different SDK with gcc 3.3, set the SDKROOT environment variable.
+ -harfbuzz .......... Use HarfBuzz to do text layout instead of Core Text when possible.
+ It is only available to Cocoa builds.
+ * -no-harfbuzz ....... Disable HarfBuzz on Mac. It can still be enabled by setting
+ QT_ENABLE_HARFBUZZ environment variable.
+
EOF
fi
@@ -7209,6 +7222,7 @@ fi
[ "$CFG_NAS" = "system" ] && QT_CONFIG="$QT_CONFIG nas"
[ "$CFG_OPENSSL" = "yes" ] && QT_CONFIG="$QT_CONFIG openssl"
[ "$CFG_OPENSSL" = "linked" ] && QT_CONFIG="$QT_CONFIG openssl-linked"
+[ "$CFG_MAC_HARFBUZZ" = "yes" ] && QT_CONFIG="$QT_CONFIG harfbuzz"
if [ "$PLATFORM_X11" = "yes" ]; then
[ "$CFG_SM" = "yes" ] && QT_CONFIG="$QT_CONFIG x11sm"
diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp
index 5705214..758ccce 100644
--- a/src/gui/kernel/qwidget.cpp
+++ b/src/gui/kernel/qwidget.cpp
@@ -1588,6 +1588,7 @@ QWidget::~QWidget()
// delete layout while we still are a valid widget
delete d->layout;
+ d->layout = 0;
// Remove myself from focus list
Q_ASSERT(d->focus_next->d_func()->focus_prev == this);
diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp
index bf5764a..3ff7eb8 100644
--- a/src/gui/painting/qbrush.cpp
+++ b/src/gui/painting/qbrush.cpp
@@ -840,6 +840,22 @@ const QGradient *QBrush::gradient() const
return 0;
}
+Q_GUI_EXPORT bool qt_isExtendedRadialGradient(const QBrush &brush)
+{
+ if (brush.style() == Qt::RadialGradientPattern) {
+ const QGradient *g = brush.gradient();
+ const QRadialGradient *rg = static_cast<const QRadialGradient *>(g);
+
+ if (!qFuzzyIsNull(rg->focalRadius()))
+ return true;
+
+ QPointF delta = rg->focalPoint() - rg->center();
+ if (delta.x() * delta.x() + delta.y() * delta.y() > rg->radius() * rg->radius())
+ return true;
+ }
+
+ return false;
+}
/*!
Returns true if the brush is fully opaque otherwise false. A brush
@@ -849,6 +865,7 @@ const QGradient *QBrush::gradient() const
\i The alpha component of the color() is 255.
\i Its texture() does not have an alpha channel and is not a QBitmap.
\i The colors in the gradient() all have an alpha component that is 255.
+ \i It is an extended radial gradient.
\endlist
*/
@@ -860,6 +877,9 @@ bool QBrush::isOpaque() const
if (d->style == Qt::SolidPattern)
return opaqueColor;
+ if (qt_isExtendedRadialGradient(*this))
+ return false;
+
if (d->style == Qt::LinearGradientPattern
|| d->style == Qt::RadialGradientPattern
|| d->style == Qt::ConicalGradientPattern) {
@@ -1209,8 +1229,10 @@ QDataStream &operator>>(QDataStream &s, QBrush &b)
\list
\o \e Linear gradients interpolate colors between start and end points.
- \o \e Radial gradients interpolate colors between a focal point and end
- points on a circle surrounding it.
+ \o \e Simple radial gradients interpolate colors between a focal point
+ and end points on a circle surrounding it.
+ \o \e Extended radial gradients interpolate colors between a center and
+ a focal circle.
\o \e Conical gradients interpolate colors around a center point.
\endlist
@@ -1506,8 +1528,6 @@ void QGradient::setInterpolationMode(InterpolationMode mode)
dummy = p;
}
-#undef Q_DUMMY_ACCESSOR
-
/*!
\fn bool QGradient::operator!=(const QGradient &gradient) const
\since 4.2
@@ -1541,7 +1561,7 @@ bool QGradient::operator==(const QGradient &gradient) const
|| m_data.radial.cy != gradient.m_data.radial.cy
|| m_data.radial.fx != gradient.m_data.radial.fx
|| m_data.radial.fy != gradient.m_data.radial.fy
- || m_data.radial.radius != gradient.m_data.radial.radius)
+ || m_data.radial.cradius != gradient.m_data.radial.cradius)
return false;
} else { // m_type == ConicalGradient
if (m_data.conical.cx != gradient.m_data.conical.cx
@@ -1747,10 +1767,17 @@ void QLinearGradient::setFinalStop(const QPointF &stop)
\brief The QRadialGradient class is used in combination with QBrush to
specify a radial gradient brush.
- Radial gradients interpolate colors between a focal point and end
- points on a circle surrounding it. Outside the end points the
- gradient is either padded, reflected or repeated depending on the
- currently set \l {QGradient::Spread}{spread} method:
+ Qt supports both simple and extended radial gradients.
+
+ Simple radial gradients interpolate colors between a focal point and end
+ points on a circle surrounding it. Extended radial gradients interpolate
+ colors between a focal circle and a center circle. Points outside the cone
+ defined by the two circles will be transparent. For simple radial gradients
+ the focal point is adjusted to lie inside the center circle, whereas the
+ focal point can have any position in an extended radial gradient.
+
+ Outside the end points the gradient is either padded, reflected or repeated
+ depending on the currently set \l {QGradient::Spread}{spread} method:
\table
\row
@@ -1795,9 +1822,14 @@ static QPointF qt_radial_gradient_adapt_focal_point(const QPointF &center,
}
/*!
- Constructs a radial gradient with the given \a center, \a
+ Constructs a simple radial gradient with the given \a center, \a
radius and \a focalPoint.
+ \note If the given focal point is outside the circle defined by the
+ center (\a cx, \a cy) and the \a radius it will be re-adjusted to
+ the intersection between the line from the center to the focal point
+ and the circle.
+
\sa QGradient::setColorAt(), QGradient::setStops()
*/
@@ -1807,7 +1839,7 @@ QRadialGradient::QRadialGradient(const QPointF &center, qreal radius, const QPoi
m_spread = PadSpread;
m_data.radial.cx = center.x();
m_data.radial.cy = center.y();
- m_data.radial.radius = radius;
+ m_data.radial.cradius = radius;
QPointF adapted_focal = qt_radial_gradient_adapt_focal_point(center, radius, focalPoint);
m_data.radial.fx = adapted_focal.x();
@@ -1815,7 +1847,7 @@ QRadialGradient::QRadialGradient(const QPointF &center, qreal radius, const QPoi
}
/*!
- Constructs a radial gradient with the given \a center, \a
+ Constructs a simple radial gradient with the given \a center, \a
radius and the focal point in the circle center.
\sa QGradient::setColorAt(), QGradient::setStops()
@@ -1826,16 +1858,21 @@ QRadialGradient::QRadialGradient(const QPointF &center, qreal radius)
m_spread = PadSpread;
m_data.radial.cx = center.x();
m_data.radial.cy = center.y();
- m_data.radial.radius = radius;
+ m_data.radial.cradius = radius;
m_data.radial.fx = center.x();
m_data.radial.fy = center.y();
}
/*!
- Constructs a radial gradient with the given center (\a cx, \a cy),
+ Constructs a simple radial gradient with the given center (\a cx, \a cy),
\a radius and focal point (\a fx, \a fy).
+ \note If the given focal point is outside the circle defined by the
+ center (\a cx, \a cy) and the \a radius it will be re-adjusted to
+ the intersection between the line from the center to the focal point
+ and the circle.
+
\sa QGradient::setColorAt(), QGradient::setStops()
*/
@@ -1845,7 +1882,7 @@ QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal radius, qreal fx, qre
m_spread = PadSpread;
m_data.radial.cx = cx;
m_data.radial.cy = cy;
- m_data.radial.radius = radius;
+ m_data.radial.cradius = radius;
QPointF adapted_focal = qt_radial_gradient_adapt_focal_point(QPointF(cx, cy),
radius,
@@ -1856,7 +1893,7 @@ QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal radius, qreal fx, qre
}
/*!
- Constructs a radial gradient with the center at (\a cx, \a cy) and the
+ Constructs a simple radial gradient with the center at (\a cx, \a cy) and the
specified \a radius. The focal point lies at the center of the circle.
\sa QGradient::setColorAt(), QGradient::setStops()
@@ -1867,14 +1904,14 @@ QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal radius)
m_spread = PadSpread;
m_data.radial.cx = cx;
m_data.radial.cy = cy;
- m_data.radial.radius = radius;
+ m_data.radial.cradius = radius;
m_data.radial.fx = cx;
m_data.radial.fy = cy;
}
/*!
- Constructs a radial gradient with the center and focal point at
+ Constructs a simple radial gradient with the center and focal point at
(0, 0) with a radius of 1.
*/
QRadialGradient::QRadialGradient()
@@ -1883,11 +1920,51 @@ QRadialGradient::QRadialGradient()
m_spread = PadSpread;
m_data.radial.cx = 0;
m_data.radial.cy = 0;
- m_data.radial.radius = 1;
+ m_data.radial.cradius = 1;
m_data.radial.fx = 0;
m_data.radial.fy = 0;
}
+/*!
+ \since 4.8
+
+ Constructs an extended radial gradient with the given \a center, \a
+ centerRadius, \a focalPoint, and \a focalRadius.
+*/
+QRadialGradient::QRadialGradient(const QPointF &center, qreal centerRadius, const QPointF &focalPoint, qreal focalRadius)
+{
+ m_type = RadialGradient;
+ m_spread = PadSpread;
+ m_data.radial.cx = center.x();
+ m_data.radial.cy = center.y();
+ m_data.radial.cradius = centerRadius;
+
+ m_data.radial.fx = focalPoint.x();
+ m_data.radial.fy = focalPoint.y();
+ setFocalRadius(focalRadius);
+}
+
+/*!
+ \since 4.8
+
+ Constructs an extended radial gradient with the given \a center, \a
+ centerRadius, \a focalPoint, and \a focalRadius.
+ Constructs a radial gradient with the given center (\a cx, \a cy),
+ center radius \a centerRadius, focal point (\a fx, \a fy), and
+ focal radius \a focalRadius.
+*/
+QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal centerRadius, qreal fx, qreal fy, qreal focalRadius)
+{
+ m_type = RadialGradient;
+ m_spread = PadSpread;
+ m_data.radial.cx = cx;
+ m_data.radial.cy = cy;
+ m_data.radial.cradius = centerRadius;
+
+ m_data.radial.fx = fx;
+ m_data.radial.fy = fy;
+ setFocalRadius(focalRadius);
+}
/*!
Returns the center of this radial gradient in logical coordinates.
@@ -1932,13 +2009,15 @@ void QRadialGradient::setCenter(const QPointF &center)
/*!
Returns the radius of this radial gradient in logical coordinates.
+ Equivalent to centerRadius()
+
\sa QGradient::stops()
*/
qreal QRadialGradient::radius() const
{
Q_ASSERT(m_type == RadialGradient);
- return m_data.radial.radius;
+ return m_data.radial.cradius;
}
@@ -1947,13 +2026,81 @@ qreal QRadialGradient::radius() const
Sets the radius of this radial gradient in logical coordinates
to \a radius
+
+ Equivalent to setCenterRadius()
*/
void QRadialGradient::setRadius(qreal radius)
{
Q_ASSERT(m_type == RadialGradient);
- m_data.radial.radius = radius;
+ m_data.radial.cradius = radius;
+}
+
+/*!
+ \since 4.8
+
+ Returns the center radius of this radial gradient in logical
+ coordinates.
+
+ \sa QGradient::stops()
+*/
+qreal QRadialGradient::centerRadius() const
+{
+ Q_ASSERT(m_type == RadialGradient);
+ return m_data.radial.cradius;
+}
+
+/*
+ \since 4.8
+
+ Sets the center radius of this radial gradient in logical coordinates
+ to \a radius
+*/
+void QRadialGradient::setCenterRadius(qreal radius)
+{
+ Q_ASSERT(m_type == RadialGradient);
+ m_data.radial.cradius = radius;
+}
+
+/*!
+ \since 4.8
+
+ Returns the focal radius of this radial gradient in logical
+ coordinates.
+
+ \sa QGradient::stops()
+*/
+qreal QRadialGradient::focalRadius() const
+{
+ Q_ASSERT(m_type == RadialGradient);
+ Q_DUMMY_ACCESSOR
+
+ // mask away low three bits
+ union { float f; quint32 i; } u;
+ u.i = i & ~0x07;
+ return u.f;
}
+/*
+ \since 4.8
+
+ Sets the focal radius of this radial gradient in logical coordinates
+ to \a radius
+*/
+void QRadialGradient::setFocalRadius(qreal radius)
+{
+ Q_ASSERT(m_type == RadialGradient);
+ Q_DUMMY_ACCESSOR
+
+ // Since there's no QGradientData, we only have the dummy void * to
+ // store additional data in. The three lowest bits are already
+ // taken, thus we cut the three lowest bits from the significand
+ // and store the radius as a float.
+ union { float f; quint32 i; } u;
+ u.f = float(radius);
+ // add 0x04 to round up when we drop the three lowest bits
+ i |= (u.i + 0x04) & ~0x07;
+ dummy = p;
+}
/*!
Returns the focal point of this radial gradient in logical
@@ -2193,4 +2340,6 @@ void QConicalGradient::setAngle(qreal angle)
\sa setTransform()
*/
+#undef Q_DUMMY_ACCESSOR
+
QT_END_NAMESPACE
diff --git a/src/gui/painting/qbrush.h b/src/gui/painting/qbrush.h
index 8b31319..d914c8c 100644
--- a/src/gui/painting/qbrush.h
+++ b/src/gui/painting/qbrush.h
@@ -255,6 +255,7 @@ private:
friend class QLinearGradient;
friend class QRadialGradient;
friend class QConicalGradient;
+ friend class QBrush;
Type m_type;
Spread m_spread;
@@ -264,7 +265,7 @@ private:
qreal x1, y1, x2, y2;
} linear;
struct {
- qreal cx, cy, fx, fy, radius;
+ qreal cx, cy, fx, fy, cradius;
} radial;
struct {
qreal cx, cy, angle;
@@ -303,6 +304,9 @@ public:
QRadialGradient(const QPointF &center, qreal radius);
QRadialGradient(qreal cx, qreal cy, qreal radius);
+ QRadialGradient(const QPointF &center, qreal centerRadius, const QPointF &focalPoint, qreal focalRadius);
+ QRadialGradient(qreal cx, qreal cy, qreal centerRadius, qreal fx, qreal fy, qreal focalRadius);
+
QPointF center() const;
void setCenter(const QPointF &center);
inline void setCenter(qreal x, qreal y) { setCenter(QPointF(x, y)); }
@@ -313,6 +317,12 @@ public:
qreal radius() const;
void setRadius(qreal radius);
+
+ qreal centerRadius() const;
+ void setCenterRadius(qreal radius);
+
+ qreal focalRadius() const;
+ void setFocalRadius(qreal radius);
};
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
index 5e75e4a..ae9993e 100644
--- a/src/gui/painting/qdrawhelper.cpp
+++ b/src/gui/painting/qdrawhelper.cpp
@@ -75,43 +75,9 @@ enum {
fixed_scale = 1 << 16,
half_point = 1 << 15
};
-static const int buffer_size = 2048;
-
-struct LinearGradientValues
-{
- qreal dx;
- qreal dy;
- qreal l;
- qreal off;
-};
-
-struct RadialGradientValues
-{
- qreal dx;
- qreal dy;
- qreal a;
-};
-
-struct Operator;
-typedef uint* (QT_FASTCALL *DestFetchProc)(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length);
-typedef void (QT_FASTCALL *DestStoreProc)(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length);
-typedef const uint* (QT_FASTCALL *SourceFetchProc)(uint *buffer, const Operator *o, const QSpanData *data, int y, int x, int length);
-
-struct Operator
-{
- QPainter::CompositionMode mode;
- DestFetchProc dest_fetch;
- DestStoreProc dest_store;
- SourceFetchProc src_fetch;
- CompositionFunctionSolid funcSolid;
- CompositionFunction func;
- union {
- LinearGradientValues linear;
- RadialGradientValues radial;
-// TextureValues texture;
- };
-};
+// must be multiple of 4 for easier SIMD implementations
+static const int buffer_size = 2048;
/*
Destination fetch. This is simple as we don't have to do bounds checks or
@@ -1346,64 +1312,13 @@ static const SourceFetchProc sourceFetch[NBlendTypes][QImage::NImageFormats] = {
},
};
-
-static inline uint qt_gradient_pixel(const QGradientData *data, qreal pos)
-{
- int ipos = int(pos * (GRADIENT_STOPTABLE_SIZE - 1) + qreal(0.5));
-
- // calculate the actual offset.
- if (ipos < 0 || ipos >= GRADIENT_STOPTABLE_SIZE) {
- if (data->spread == QGradient::RepeatSpread) {
- ipos = ipos % GRADIENT_STOPTABLE_SIZE;
- ipos = ipos < 0 ? GRADIENT_STOPTABLE_SIZE + ipos : ipos;
-
- } else if (data->spread == QGradient::ReflectSpread) {
- const int limit = GRADIENT_STOPTABLE_SIZE * 2 - 1;
- ipos = ipos % limit;
- ipos = ipos < 0 ? limit + ipos : ipos;
- ipos = ipos >= GRADIENT_STOPTABLE_SIZE ? limit - ipos : ipos;
-
- } else {
- if (ipos < 0) ipos = 0;
- else if (ipos >= GRADIENT_STOPTABLE_SIZE) ipos = GRADIENT_STOPTABLE_SIZE-1;
- }
- }
-
- Q_ASSERT(ipos >= 0);
- Q_ASSERT(ipos < GRADIENT_STOPTABLE_SIZE);
-
- return data->colorTable[ipos];
-}
-
#define FIXPT_BITS 8
#define FIXPT_SIZE (1<<FIXPT_BITS)
static uint qt_gradient_pixel_fixed(const QGradientData *data, int fixed_pos)
{
int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
-
- // calculate the actual offset.
- if (ipos < 0 || ipos >= GRADIENT_STOPTABLE_SIZE) {
- if (data->spread == QGradient::RepeatSpread) {
- ipos = ipos % GRADIENT_STOPTABLE_SIZE;
- ipos = ipos < 0 ? GRADIENT_STOPTABLE_SIZE + ipos : ipos;
-
- } else if (data->spread == QGradient::ReflectSpread) {
- const int limit = GRADIENT_STOPTABLE_SIZE * 2 - 1;
- ipos = ipos % limit;
- ipos = ipos < 0 ? limit + ipos : ipos;
- ipos = ipos >= GRADIENT_STOPTABLE_SIZE ? limit - ipos : ipos;
-
- } else {
- if (ipos < 0) ipos = 0;
- else if (ipos >= GRADIENT_STOPTABLE_SIZE) ipos = GRADIENT_STOPTABLE_SIZE-1;
- }
- }
-
- Q_ASSERT(ipos >= 0);
- Q_ASSERT(ipos < GRADIENT_STOPTABLE_SIZE);
-
- return data->colorTable[ipos];
+ return data->colorTable[qt_gradient_clamp(data, ipos)];
}
static void QT_FASTCALL getLinearGradientValues(LinearGradientValues *v, const QSpanData *data)
@@ -1419,8 +1334,8 @@ static void QT_FASTCALL getLinearGradientValues(LinearGradientValues *v, const Q
}
}
-static const uint * QT_FASTCALL fetchLinearGradient(uint *buffer, const Operator *op, const QSpanData *data,
- int y, int x, int length)
+static const uint * QT_FASTCALL qt_fetch_linear_gradient(uint *buffer, const Operator *op, const QSpanData *data,
+ int y, int x, int length)
{
const uint *b = buffer;
qreal t, inc;
@@ -1487,110 +1402,65 @@ static const uint * QT_FASTCALL fetchLinearGradient(uint *buffer, const Operator
return b;
}
-static inline qreal determinant(qreal a, qreal b, qreal c)
-{
- return (b * b) - (4 * a * c);
-}
-
-// function to evaluate real roots
-static inline qreal realRoots(qreal a, qreal b, qreal detSqrt)
-{
- return (-b + detSqrt)/(2 * a);
-}
-
-static inline qreal qSafeSqrt(qreal x)
-{
- return x > 0 ? qSqrt(x) : 0;
-}
-
static void QT_FASTCALL getRadialGradientValues(RadialGradientValues *v, const QSpanData *data)
{
v->dx = data->gradient.radial.center.x - data->gradient.radial.focal.x;
v->dy = data->gradient.radial.center.y - data->gradient.radial.focal.y;
- v->a = data->gradient.radial.radius*data->gradient.radial.radius - v->dx*v->dx - v->dy*v->dy;
-}
-
-static const uint * QT_FASTCALL fetchRadialGradient(uint *buffer, const Operator *op, const QSpanData *data,
- int y, int x, int length)
-{
- const uint *b = buffer;
- qreal rx = data->m21 * (y + qreal(0.5))
- + data->dx + data->m11 * (x + qreal(0.5));
- qreal ry = data->m22 * (y + qreal(0.5))
- + data->dy + data->m12 * (x + qreal(0.5));
- bool affine = !data->m13 && !data->m23;
- //qreal r = data->gradient.radial.radius;
-
- const uint *end = buffer + length;
- if (affine) {
- rx -= data->gradient.radial.focal.x;
- ry -= data->gradient.radial.focal.y;
-
- qreal inv_a = 1 / qreal(2 * op->radial.a);
-
- const qreal delta_rx = data->m11;
- const qreal delta_ry = data->m12;
- qreal b = 2*(rx * op->radial.dx + ry * op->radial.dy);
- qreal delta_b = 2*(delta_rx * op->radial.dx + delta_ry * op->radial.dy);
- const qreal b_delta_b = 2 * b * delta_b;
- const qreal delta_b_delta_b = 2 * delta_b * delta_b;
+ v->dr = data->gradient.radial.center.radius - data->gradient.radial.focal.radius;
+ v->sqrfr = data->gradient.radial.focal.radius * data->gradient.radial.focal.radius;
- const qreal bb = b * b;
- const qreal delta_bb = delta_b * delta_b;
+ v->a = v->dr * v->dr - v->dx*v->dx - v->dy*v->dy;
+ v->inv2a = 1 / (2 * v->a);
- b *= inv_a;
- delta_b *= inv_a;
-
- const qreal rxrxryry = rx * rx + ry * ry;
- const qreal delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry;
- const qreal rx_plus_ry = 2*(rx * delta_rx + ry * delta_ry);
- const qreal delta_rx_plus_ry = 2 * delta_rxrxryry;
-
- inv_a *= inv_a;
+ v->extended = !qFuzzyIsNull(data->gradient.radial.focal.radius) || v->a <= 0;
+}
- qreal det = (bb + 4 * op->radial.a * rxrxryry) * inv_a;
- qreal delta_det = (b_delta_b + delta_bb + 4 * op->radial.a * (rx_plus_ry + delta_rxrxryry)) * inv_a;
- const qreal delta_delta_det = (delta_b_delta_b + 4 * op->radial.a * delta_rx_plus_ry) * inv_a;
+class RadialFetchPlain
+{
+public:
+ static inline void fetch(uint *buffer, uint *end, const Operator *op, const QSpanData *data, qreal det,
+ qreal delta_det, qreal delta_delta_det, qreal b, qreal delta_b)
+ {
+ if (op->radial.extended) {
+ while (buffer < end) {
+ quint32 result = 0;
+ if (det >= 0) {
+ qreal w = qSqrt(det) - b;
+ if (data->gradient.radial.focal.radius + op->radial.dr * w >= 0)
+ result = qt_gradient_pixel(&data->gradient, w);
+ }
- while (buffer < end) {
- *buffer = qt_gradient_pixel(&data->gradient, qSafeSqrt(det) - b);
+ *buffer = result;
- det += delta_det;
- delta_det += delta_delta_det;
- b += delta_b;
+ det += delta_det;
+ delta_det += delta_delta_det;
+ b += delta_b;
- ++buffer;
- }
- } else {
- qreal rw = data->m23 * (y + qreal(0.5))
- + data->m33 + data->m13 * (x + qreal(0.5));
- if (!rw)
- rw = 1;
- while (buffer < end) {
- qreal gx = rx/rw - data->gradient.radial.focal.x;
- qreal gy = ry/rw - data->gradient.radial.focal.y;
- qreal b = 2*(gx*op->radial.dx + gy*op->radial.dy);
- qreal det = determinant(op->radial.a, b , -(gx*gx + gy*gy));
- qreal s = realRoots(op->radial.a, b, qSafeSqrt(det));
-
- *buffer = qt_gradient_pixel(&data->gradient, s);
+ ++buffer;
+ }
+ } else {
+ while (buffer < end) {
+ *buffer++ = qt_gradient_pixel(&data->gradient, qSqrt(det) - b);
- rx += data->m11;
- ry += data->m12;
- rw += data->m13;
- if (!rw) {
- rw += data->m13;
+ det += delta_det;
+ delta_det += delta_delta_det;
+ b += delta_b;
}
- ++buffer;
}
}
+};
- return b;
+const uint * QT_FASTCALL qt_fetch_radial_gradient_plain(uint *buffer, const Operator *op, const QSpanData *data,
+ int y, int x, int length)
+{
+ return qt_fetch_radial_gradient_template<RadialFetchPlain>(buffer, op, data, y, x, length);
}
-static const uint * QT_FASTCALL fetchConicalGradient(uint *buffer, const Operator *, const QSpanData *data,
- int y, int x, int length)
+static SourceFetchProc qt_fetch_radial_gradient = qt_fetch_radial_gradient_plain;
+
+static const uint * QT_FASTCALL qt_fetch_conical_gradient(uint *buffer, const Operator *, const QSpanData *data,
+ int y, int x, int length)
{
const uint *b = buffer;
qreal rx = data->m21 * (y + qreal(0.5))
@@ -3347,16 +3217,16 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in
case QSpanData::LinearGradient:
solidSource = !data->gradient.alphaColor;
getLinearGradientValues(&op.linear, data);
- op.src_fetch = fetchLinearGradient;
+ op.src_fetch = qt_fetch_linear_gradient;
break;
case QSpanData::RadialGradient:
solidSource = !data->gradient.alphaColor;
getRadialGradientValues(&op.radial, data);
- op.src_fetch = fetchRadialGradient;
+ op.src_fetch = qt_fetch_radial_gradient;
break;
case QSpanData::ConicalGradient:
solidSource = !data->gradient.alphaColor;
- op.src_fetch = fetchConicalGradient;
+ op.src_fetch = qt_fetch_conical_gradient;
break;
case QSpanData::Texture:
op.src_fetch = sourceFetch[getBlendType(data)][data->texture.format];
@@ -7888,6 +7758,11 @@ void qInitDrawhelperAsm()
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;
+
+ extern const uint * QT_FASTCALL qt_fetch_radial_gradient_sse2(uint *buffer, const Operator *op, const QSpanData *data,
+ int y, int x, int length);
+
+ qt_fetch_radial_gradient = qt_fetch_radial_gradient_sse2;
}
#ifdef QT_HAVE_SSSE3
@@ -7983,6 +7858,11 @@ void qInitDrawhelperAsm()
qMemRotateFunctions[QImage::Format_RGB16][0] = qt_memrotate90_16_neon;
qMemRotateFunctions[QImage::Format_RGB16][2] = qt_memrotate270_16_neon;
qt_memfill32 = qt_memfill32_neon;
+
+ extern const uint * QT_FASTCALL qt_fetch_radial_gradient_neon(uint *buffer, const Operator *op, const QSpanData *data,
+ int y, int x, int length);
+
+ qt_fetch_radial_gradient = qt_fetch_radial_gradient_neon;
}
#endif
diff --git a/src/gui/painting/qdrawhelper_neon.cpp b/src/gui/painting/qdrawhelper_neon.cpp
index debca37..e673dd9 100644
--- a/src/gui/painting/qdrawhelper_neon.cpp
+++ b/src/gui/painting/qdrawhelper_neon.cpp
@@ -955,6 +955,46 @@ void qt_memrotate270_16_neon(const uchar *srcPixels, int w, int h,
}
}
+class QSimdNeon
+{
+public:
+ typedef int32x4_t Int32x4;
+ typedef float32x4_t Float32x4;
+
+ union Vect_buffer_i { Int32x4 v; int i[4]; };
+ union Vect_buffer_f { Float32x4 v; float f[4]; };
+
+ static inline Float32x4 v_dup(float x) { return vdupq_n_f32(x); }
+ static inline Int32x4 v_dup(int x) { return vdupq_n_s32(x); }
+ static inline Int32x4 v_dup(uint x) { return vdupq_n_s32(x); }
+
+ static inline Float32x4 v_add(Float32x4 a, Float32x4 b) { return vaddq_f32(a, b); }
+ static inline Int32x4 v_add(Int32x4 a, Int32x4 b) { return vaddq_s32(a, b); }
+
+ static inline Float32x4 v_max(Float32x4 a, Float32x4 b) { return vmaxq_f32(a, b); }
+ static inline Float32x4 v_min(Float32x4 a, Float32x4 b) { return vminq_f32(a, b); }
+ static inline Int32x4 v_min_16(Int32x4 a, Int32x4 b) { return vminq_s32(a, b); }
+
+ static inline Int32x4 v_and(Int32x4 a, Int32x4 b) { return vandq_s32(a, b); }
+
+ static inline Float32x4 v_sub(Float32x4 a, Float32x4 b) { return vsubq_f32(a, b); }
+ static inline Int32x4 v_sub(Int32x4 a, Int32x4 b) { return vsubq_s32(a, b); }
+
+ static inline Float32x4 v_mul(Float32x4 a, Float32x4 b) { return vmulq_f32(a, b); }
+
+ static inline Float32x4 v_sqrt(Float32x4 x) { Float32x4 y = vrsqrteq_f32(x); y = vmulq_f32(y, vrsqrtsq_f32(x, vmulq_f32(y, y))); return vmulq_f32(x, y); }
+
+ static inline Int32x4 v_toInt(Float32x4 x) { return vcvtq_s32_f32(x); }
+
+ static inline Int32x4 v_greaterOrEqual(Float32x4 a, Float32x4 b) { return vcge_f32(a, b); }
+};
+
+const uint * QT_FASTCALL qt_fetch_radial_gradient_neon(uint *buffer, const Operator *op, const QSpanData *data,
+ int y, int x, int length)
+{
+ return qt_fetch_radial_gradient_template<QRadialFetchSimd<QSimdNeon> >(buffer, op, data, y, x, length);
+}
+
QT_END_NAMESPACE
#endif // QT_HAVE_NEON
diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h
index d4e731b..e93d736 100644
--- a/src/gui/painting/qdrawhelper_p.h
+++ b/src/gui/painting/qdrawhelper_p.h
@@ -63,6 +63,7 @@
#endif
#include "private/qrasterdefs_p.h"
#include <private/qsimd_p.h>
+#include <private/qmath_p.h>
#ifdef Q_WS_QWS
#include "QtGui/qscreen_qws.h"
@@ -178,6 +179,44 @@ void qBlendTextureCallback(int count, const QSpan *spans, void *userData);
typedef void (QT_FASTCALL *CompositionFunction)(uint *dest, const uint *src, int length, uint const_alpha);
typedef void (QT_FASTCALL *CompositionFunctionSolid)(uint *dest, int length, uint color, uint const_alpha);
+struct LinearGradientValues
+{
+ qreal dx;
+ qreal dy;
+ qreal l;
+ qreal off;
+};
+
+struct RadialGradientValues
+{
+ qreal dx;
+ qreal dy;
+ qreal dr;
+ qreal sqrfr;
+ qreal a;
+ qreal inv2a;
+ bool extended;
+};
+
+struct Operator;
+typedef uint* (QT_FASTCALL *DestFetchProc)(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length);
+typedef void (QT_FASTCALL *DestStoreProc)(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length);
+typedef const uint* (QT_FASTCALL *SourceFetchProc)(uint *buffer, const Operator *o, const QSpanData *data, int y, int x, int length);
+
+struct Operator
+{
+ QPainter::CompositionMode mode;
+ DestFetchProc dest_fetch;
+ DestStoreProc dest_store;
+ SourceFetchProc src_fetch;
+ CompositionFunctionSolid funcSolid;
+ CompositionFunction func;
+ union {
+ LinearGradientValues linear;
+ RadialGradientValues radial;
+ };
+};
+
void qInitDrawhelperAsm();
class QRasterPaintEngine;
@@ -204,12 +243,13 @@ struct QRadialGradientData
struct {
qreal x;
qreal y;
+ qreal radius;
} center;
struct {
qreal x;
qreal y;
+ qreal radius;
} focal;
- qreal radius;
};
struct QConicalGradientData
@@ -233,8 +273,10 @@ struct QGradientData
#ifdef Q_WS_QWS
#define GRADIENT_STOPTABLE_SIZE 256
+#define GRADIENT_STOPTABLE_SIZE_SHIFT 8
#else
#define GRADIENT_STOPTABLE_SIZE 1024
+#define GRADIENT_STOPTABLE_SIZE_SHIFT 10
#endif
uint* colorTable; //[GRADIENT_STOPTABLE_SIZE];
@@ -308,6 +350,220 @@ struct QSpanData
void adjustSpanMethods();
};
+static inline uint qt_gradient_clamp(const QGradientData *data, int ipos)
+{
+ if (ipos < 0 || ipos >= GRADIENT_STOPTABLE_SIZE) {
+ if (data->spread == QGradient::RepeatSpread) {
+ ipos = ipos % GRADIENT_STOPTABLE_SIZE;
+ ipos = ipos < 0 ? GRADIENT_STOPTABLE_SIZE + ipos : ipos;
+ } else if (data->spread == QGradient::ReflectSpread) {
+ const int limit = GRADIENT_STOPTABLE_SIZE * 2;
+ ipos = ipos % limit;
+ ipos = ipos < 0 ? limit + ipos : ipos;
+ ipos = ipos >= GRADIENT_STOPTABLE_SIZE ? limit - 1 - ipos : ipos;
+ } else {
+ if (ipos < 0)
+ ipos = 0;
+ else if (ipos >= GRADIENT_STOPTABLE_SIZE)
+ ipos = GRADIENT_STOPTABLE_SIZE-1;
+ }
+ }
+
+ Q_ASSERT(ipos >= 0);
+ Q_ASSERT(ipos < GRADIENT_STOPTABLE_SIZE);
+
+ return ipos;
+}
+
+static inline uint qt_gradient_pixel(const QGradientData *data, qreal pos)
+{
+ int ipos = int(pos * (GRADIENT_STOPTABLE_SIZE - 1) + qreal(0.5));
+ return data->colorTable[qt_gradient_clamp(data, ipos)];
+}
+
+static inline qreal qRadialDeterminant(qreal a, qreal b, qreal c)
+{
+ return (b * b) - (4 * a * c);
+}
+
+template <class RadialFetchFunc>
+const uint * QT_FASTCALL qt_fetch_radial_gradient_template(uint *buffer, const Operator *op, const QSpanData *data,
+ int y, int x, int length)
+{
+ // avoid division by zero
+ if (qFuzzyIsNull(op->radial.a)) {
+ extern void (*qt_memfill32)(quint32 *dest, quint32 value, int count);
+ qt_memfill32(buffer, 0, length);
+ return buffer;
+ }
+
+ const uint *b = buffer;
+ qreal rx = data->m21 * (y + qreal(0.5))
+ + data->dx + data->m11 * (x + qreal(0.5));
+ qreal ry = data->m22 * (y + qreal(0.5))
+ + data->dy + data->m12 * (x + qreal(0.5));
+ bool affine = !data->m13 && !data->m23;
+
+ uint *end = buffer + length;
+ if (affine) {
+ rx -= data->gradient.radial.focal.x;
+ ry -= data->gradient.radial.focal.y;
+
+ qreal inv_a = 1 / qreal(2 * op->radial.a);
+
+ const qreal delta_rx = data->m11;
+ const qreal delta_ry = data->m12;
+
+ qreal b = 2*(op->radial.dr*data->gradient.radial.focal.radius + rx * op->radial.dx + ry * op->radial.dy);
+ qreal delta_b = 2*(delta_rx * op->radial.dx + delta_ry * op->radial.dy);
+ const qreal b_delta_b = 2 * b * delta_b;
+ const qreal delta_b_delta_b = 2 * delta_b * delta_b;
+
+ const qreal bb = b * b;
+ const qreal delta_bb = delta_b * delta_b;
+
+ b *= inv_a;
+ delta_b *= inv_a;
+
+ const qreal rxrxryry = rx * rx + ry * ry;
+ const qreal delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry;
+ const qreal rx_plus_ry = 2*(rx * delta_rx + ry * delta_ry);
+ const qreal delta_rx_plus_ry = 2 * delta_rxrxryry;
+
+ inv_a *= inv_a;
+
+ qreal det = (bb - 4 * op->radial.a * (op->radial.sqrfr - rxrxryry)) * inv_a;
+ qreal delta_det = (b_delta_b + delta_bb + 4 * op->radial.a * (rx_plus_ry + delta_rxrxryry)) * inv_a;
+ const qreal delta_delta_det = (delta_b_delta_b + 4 * op->radial.a * delta_rx_plus_ry) * inv_a;
+
+ RadialFetchFunc::fetch(buffer, end, op, data, det, delta_det, delta_delta_det, b, delta_b);
+ } else {
+ qreal rw = data->m23 * (y + qreal(0.5))
+ + data->m33 + data->m13 * (x + qreal(0.5));
+
+ while (buffer < end) {
+ if (rw == 0) {
+ *buffer = 0;
+ } else {
+ qreal invRw = 1 / rw;
+ qreal gx = rx * invRw - data->gradient.radial.focal.x;
+ qreal gy = ry * invRw - data->gradient.radial.focal.y;
+ qreal b = 2*(op->radial.dr*data->gradient.radial.focal.radius + gx*op->radial.dx + gy*op->radial.dy);
+ qreal det = qRadialDeterminant(op->radial.a, b, op->radial.sqrfr - (gx*gx + gy*gy));
+
+ quint32 result = 0;
+ if (det >= 0) {
+ qreal detSqrt = qSqrt(det);
+
+ qreal s0 = (-b - detSqrt) * op->radial.inv2a;
+ qreal s1 = (-b + detSqrt) * op->radial.inv2a;
+
+ qreal s = qMax(s0, s1);
+
+ if (data->gradient.radial.focal.radius + op->radial.dr * s >= 0)
+ result = qt_gradient_pixel(&data->gradient, s);
+ }
+
+ *buffer = result;
+ }
+
+ rx += data->m11;
+ ry += data->m12;
+ rw += data->m13;
+
+ ++buffer;
+ }
+ }
+
+ return b;
+}
+
+template <class Simd>
+class QRadialFetchSimd
+{
+public:
+ static void fetch(uint *buffer, uint *end, const Operator *op, const QSpanData *data, qreal det,
+ qreal delta_det, qreal delta_delta_det, qreal b, qreal delta_b)
+ {
+ typename Simd::Vect_buffer_f det_vec;
+ typename Simd::Vect_buffer_f delta_det4_vec;
+ typename Simd::Vect_buffer_f b_vec;
+
+ for (int i = 0; i < 4; ++i) {
+ det_vec.f[i] = det;
+ delta_det4_vec.f[i] = 4 * delta_det;
+ b_vec.f[i] = b;
+
+ det += delta_det;
+ delta_det += delta_delta_det;
+ b += delta_b;
+ }
+
+ const typename Simd::Float32x4 v_delta_delta_det16 = Simd::v_dup(16 * delta_delta_det);
+ const typename Simd::Float32x4 v_delta_delta_det6 = Simd::v_dup(6 * delta_delta_det);
+ const typename Simd::Float32x4 v_delta_b4 = Simd::v_dup(4 * delta_b);
+
+ const typename Simd::Float32x4 v_r0 = Simd::v_dup(data->gradient.radial.focal.radius);
+ const typename Simd::Float32x4 v_dr = Simd::v_dup(op->radial.dr);
+
+ const typename Simd::Float32x4 v_min = Simd::v_dup(0.0f);
+ const typename Simd::Float32x4 v_max = Simd::v_dup(GRADIENT_STOPTABLE_SIZE-1.5f);
+ const typename Simd::Float32x4 v_half = Simd::v_dup(0.5f);
+
+ const typename Simd::Float32x4 v_table_size_minus_one = Simd::v_dup(float(GRADIENT_STOPTABLE_SIZE-1));
+
+ const typename Simd::Int32x4 v_repeat_mask = Simd::v_dup(~(uint(0xffffff) << GRADIENT_STOPTABLE_SIZE_SHIFT));
+ const typename Simd::Int32x4 v_reflect_mask = Simd::v_dup(~(uint(0xffffff) << (GRADIENT_STOPTABLE_SIZE_SHIFT+1)));
+
+ const typename Simd::Int32x4 v_reflect_limit = Simd::v_dup(2 * GRADIENT_STOPTABLE_SIZE - 1);
+
+ const int extended_mask = op->radial.extended ? 0x0 : ~0x0;
+
+#define FETCH_RADIAL_LOOP_PROLOGUE \
+ while (buffer < end) { \
+ typename Simd::Vect_buffer_i v_buffer_mask; \
+ v_buffer_mask.v = Simd::v_greaterOrEqual(det_vec.v, v_min); \
+ const typename Simd::Float32x4 v_index_local = Simd::v_sub(Simd::v_sqrt(Simd::v_max(v_min, det_vec.v)), b_vec.v); \
+ const typename Simd::Float32x4 v_index = Simd::v_add(Simd::v_mul(v_index_local, v_table_size_minus_one), v_half); \
+ v_buffer_mask.v = Simd::v_and(v_buffer_mask.v, Simd::v_greaterOrEqual(Simd::v_add(v_r0, Simd::v_mul(v_dr, v_index_local)), v_min)); \
+ typename Simd::Vect_buffer_i index_vec;
+#define FETCH_RADIAL_LOOP_CLAMP_REPEAT \
+ index_vec.v = Simd::v_and(v_repeat_mask, Simd::v_toInt(v_index));
+#define FETCH_RADIAL_LOOP_CLAMP_REFLECT \
+ const typename Simd::Int32x4 v_index_i = Simd::v_and(v_reflect_mask, Simd::v_toInt(v_index)); \
+ const typename Simd::Int32x4 v_index_i_inv = Simd::v_sub(v_reflect_limit, v_index_i); \
+ index_vec.v = Simd::v_min_16(v_index_i, v_index_i_inv);
+#define FETCH_RADIAL_LOOP_CLAMP_PAD \
+ index_vec.v = Simd::v_toInt(Simd::v_min(v_max, Simd::v_max(v_min, v_index)));
+#define FETCH_RADIAL_LOOP_EPILOGUE \
+ det_vec.v = Simd::v_add(Simd::v_add(det_vec.v, delta_det4_vec.v), v_delta_delta_det6); \
+ delta_det4_vec.v = Simd::v_add(delta_det4_vec.v, v_delta_delta_det16); \
+ b_vec.v = Simd::v_add(b_vec.v, v_delta_b4); \
+ for (int i = 0; i < 4; ++i) \
+ *buffer++ = (extended_mask | v_buffer_mask.i[i]) & data->gradient.colorTable[index_vec.i[i]]; \
+ }
+
+#define FETCH_RADIAL_LOOP(FETCH_RADIAL_LOOP_CLAMP) \
+ FETCH_RADIAL_LOOP_PROLOGUE \
+ FETCH_RADIAL_LOOP_CLAMP \
+ FETCH_RADIAL_LOOP_EPILOGUE
+
+ switch (data->gradient.spread) {
+ case QGradient::RepeatSpread:
+ FETCH_RADIAL_LOOP(FETCH_RADIAL_LOOP_CLAMP_REPEAT)
+ break;
+ case QGradient::ReflectSpread:
+ FETCH_RADIAL_LOOP(FETCH_RADIAL_LOOP_CLAMP_REFLECT)
+ break;
+ case QGradient::PadSpread:
+ FETCH_RADIAL_LOOP(FETCH_RADIAL_LOOP_CLAMP_PAD)
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+ }
+};
+
#if defined(Q_CC_RVCT)
# pragma push
# pragma arm
diff --git a/src/gui/painting/qdrawhelper_sse2.cpp b/src/gui/painting/qdrawhelper_sse2.cpp
index aad6bc9..be6dc91 100644
--- a/src/gui/painting/qdrawhelper_sse2.cpp
+++ b/src/gui/painting/qdrawhelper_sse2.cpp
@@ -491,6 +491,58 @@ void qt_bitmapblit16_sse2(QRasterBuffer *rasterBuffer, int x, int y,
}
}
+class QSimdSse2
+{
+public:
+ typedef __m128i Int32x4;
+ typedef __m128 Float32x4;
+
+ union Vect_buffer_i { Int32x4 v; int i[4]; };
+ union Vect_buffer_f { Float32x4 v; float f[4]; };
+
+ static inline Float32x4 v_dup(float x) { return _mm_set1_ps(x); }
+ static inline Float32x4 v_dup(double x) { return _mm_set1_ps(x); }
+ static inline Int32x4 v_dup(int x) { return _mm_set1_epi32(x); }
+ static inline Int32x4 v_dup(uint x) { return _mm_set1_epi32(x); }
+
+ static inline Float32x4 v_add(Float32x4 a, Float32x4 b) { return _mm_add_ps(a, b); }
+ static inline Int32x4 v_add(Int32x4 a, Int32x4 b) { return _mm_add_epi32(a, b); }
+
+ static inline Float32x4 v_max(Float32x4 a, Float32x4 b) { return _mm_max_ps(a, b); }
+ static inline Float32x4 v_min(Float32x4 a, Float32x4 b) { return _mm_min_ps(a, b); }
+ static inline Int32x4 v_min_16(Int32x4 a, Int32x4 b) { return _mm_min_epi16(a, b); }
+
+ static inline Int32x4 v_and(Int32x4 a, Int32x4 b) { return _mm_and_si128(a, b); }
+
+ static inline Float32x4 v_sub(Float32x4 a, Float32x4 b) { return _mm_sub_ps(a, b); }
+ static inline Int32x4 v_sub(Int32x4 a, Int32x4 b) { return _mm_sub_epi32(a, b); }
+
+ static inline Float32x4 v_mul(Float32x4 a, Float32x4 b) { return _mm_mul_ps(a, b); }
+
+ static inline Float32x4 v_sqrt(Float32x4 x) { return _mm_sqrt_ps(x); }
+
+ static inline Int32x4 v_toInt(Float32x4 x) { return _mm_cvttps_epi32(x); }
+
+ // pre-VS 2008 doesn't have cast intrinsics, whereas 2008 and later requires it
+#if defined(Q_CC_MSVC) && _MSC_VER < 1500
+ static inline Int32x4 v_greaterOrEqual(Float32x4 a, Float32x4 b)
+ {
+ union Convert { Int32x4 vi; Float32x4 vf; } convert;
+ convert.vf = _mm_cmpgt_ps(a, b);
+ return convert.vi;
+ }
+#else
+ static inline Int32x4 v_greaterOrEqual(Float32x4 a, Float32x4 b) { return _mm_castps_si128(_mm_cmpgt_ps(a, b)); }
+#endif
+};
+
+const uint * QT_FASTCALL qt_fetch_radial_gradient_sse2(uint *buffer, const Operator *op, const QSpanData *data,
+ int y, int x, int length)
+{
+ return qt_fetch_radial_gradient_template<QRadialFetchSimd<QSimdSse2> >(buffer, op, data, y, x, length);
+}
+
+
QT_END_NAMESPACE
#endif // QT_HAVE_SSE2
diff --git a/src/gui/painting/qpaintengine_mac.cpp b/src/gui/painting/qpaintengine_mac.cpp
index 8aab7c7..cc75b86 100644
--- a/src/gui/painting/qpaintengine_mac.cpp
+++ b/src/gui/painting/qpaintengine_mac.cpp
@@ -1544,8 +1544,9 @@ void QCoreGraphicsPaintEnginePrivate::setFillBrush(const QPointF &offset)
QPointF center(radialGrad->center());
QPointF focal(radialGrad->focalPoint());
qreal radius = radialGrad->radius();
+ qreal focalRadius = radialGrad->focalRadius();
shading = CGShadingCreateRadial(colorspace, CGPointMake(focal.x(), focal.y()),
- 0.0, CGPointMake(center.x(), center.y()), radius, fill_func, false, true);
+ focalRadius, CGPointMake(center.x(), center.y()), radius, fill_func, false, true);
}
CGFunctionRelease(fill_func);
diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp
index 6902543..2119e30 100644
--- a/src/gui/painting/qpaintengine_raster.cpp
+++ b/src/gui/painting/qpaintengine_raster.cpp
@@ -5033,6 +5033,84 @@ void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint
bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
+ if (stopCount == 2) {
+ uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
+ uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity);
+
+ qreal first_stop = stops[0].first;
+ qreal second_stop = stops[1].first;
+
+ if (second_stop < first_stop) {
+ qSwap(first_color, second_color);
+ qSwap(first_stop, second_stop);
+ }
+
+ if (colorInterpolation) {
+ first_color = PREMUL(first_color);
+ second_color = PREMUL(second_color);
+ }
+
+ int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1));
+ int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1));
+
+ uint red_first = qRed(first_color) << 16;
+ uint green_first = qGreen(first_color) << 16;
+ uint blue_first = qBlue(first_color) << 16;
+ uint alpha_first = qAlpha(first_color) << 16;
+
+ uint red_second = qRed(second_color) << 16;
+ uint green_second = qGreen(second_color) << 16;
+ uint blue_second = qBlue(second_color) << 16;
+ uint alpha_second = qAlpha(second_color) << 16;
+
+ int i = 0;
+ for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) {
+ if (colorInterpolation)
+ colorTable[i] = first_color;
+ else
+ colorTable[i] = PREMUL(first_color);
+ }
+
+ if (i < second_index) {
+ qreal reciprocal = qreal(1) / (second_index - first_index);
+
+ int red_delta = qRound(int(red_second - red_first) * reciprocal);
+ int green_delta = qRound(int(green_second - green_first) * reciprocal);
+ int blue_delta = qRound(int(blue_second - blue_first) * reciprocal);
+ int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal);
+
+ // rounding
+ red_first += 1 << 15;
+ green_first += 1 << 15;
+ blue_first += 1 << 15;
+ alpha_first += 1 << 15;
+
+ for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) {
+ red_first += red_delta;
+ green_first += green_delta;
+ blue_first += blue_delta;
+ alpha_first += alpha_delta;
+
+ const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000)
+ | ((green_first >> 8) & 0xff00) | (blue_first >> 16);
+
+ if (colorInterpolation)
+ colorTable[i] = color;
+ else
+ colorTable[i] = PREMUL(color);
+ }
+ }
+
+ for (; i < GRADIENT_STOPTABLE_SIZE; ++i) {
+ if (colorInterpolation)
+ colorTable[i] = second_color;
+ else
+ colorTable[i] = PREMUL(second_color);
+ }
+
+ return;
+ }
+
uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
if (stopCount == 1) {
current_color = PREMUL(current_color);
@@ -5204,10 +5282,11 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode
QPointF center = g->center();
radialData.center.x = center.x();
radialData.center.y = center.y();
+ radialData.center.radius = g->centerRadius();
QPointF focal = g->focalPoint();
radialData.focal.x = focal.x();
radialData.focal.y = focal.y();
- radialData.radius = g->radius();
+ radialData.focal.radius = g->focalRadius();
}
break;
diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp
index 509fb77..7f601eb 100644
--- a/src/gui/painting/qpaintengineex.cpp
+++ b/src/gui/painting/qpaintengineex.cpp
@@ -1012,4 +1012,50 @@ void QPaintEngineEx::updateState(const QPaintEngineState &)
// do nothing...
}
+Q_GUI_EXPORT QPainterPath qt_painterPathFromVectorPath(const QVectorPath &path)
+{
+ const qreal *points = path.points();
+ const QPainterPath::ElementType *types = path.elements();
+
+ QPainterPath p;
+ if (types) {
+ int id = 0;
+ for (int i=0; i<path.elementCount(); ++i) {
+ switch(types[i]) {
+ case QPainterPath::MoveToElement:
+ p.moveTo(QPointF(points[id], points[id+1]));
+ id+=2;
+ break;
+ case QPainterPath::LineToElement:
+ p.lineTo(QPointF(points[id], points[id+1]));
+ id+=2;
+ break;
+ case QPainterPath::CurveToElement: {
+ QPointF p1(points[id], points[id+1]);
+ QPointF p2(points[id+2], points[id+3]);
+ QPointF p3(points[id+4], points[id+5]);
+ p.cubicTo(p1, p2, p3);
+ id+=6;
+ break;
+ }
+ case QPainterPath::CurveToDataElement:
+ ;
+ break;
+ }
+ }
+ } else {
+ p.moveTo(QPointF(points[0], points[1]));
+ int id = 2;
+ for (int i=1; i<path.elementCount(); ++i) {
+ p.lineTo(QPointF(points[id], points[id+1]));
+ id+=2;
+ }
+ }
+ if (path.hints() & QVectorPath::WindingFill)
+ p.setFillRule(Qt::WindingFill);
+
+ return p;
+}
+
+
QT_END_NAMESPACE
diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp
index bef6b7d..25bf6be 100644
--- a/src/gui/painting/qpainter.cpp
+++ b/src/gui/painting/qpainter.cpp
@@ -156,7 +156,8 @@ static bool qt_paintengine_supports_transformations(QPaintEngine::Type type)
{
return type == QPaintEngine::OpenGL2
|| type == QPaintEngine::OpenVG
- || type == QPaintEngine::OpenGL;
+ || type == QPaintEngine::OpenGL
+ || type == QPaintEngine::CoreGraphics;
}
#ifndef QT_NO_DEBUG
@@ -503,8 +504,12 @@ void QPainterPrivate::draw_helper(const QPainterPath &originalPath, DrawOperatio
q->save();
state->matrix = QTransform();
- state->dirtyFlags |= QPaintEngine::DirtyTransform;
- updateState(state);
+ if (extended) {
+ extended->transformChanged();
+ } else {
+ state->dirtyFlags |= QPaintEngine::DirtyTransform;
+ updateState(state);
+ }
engine->drawImage(absPathRect,
image,
QRectF(0, 0, absPathRect.width(), absPathRect.height()),
@@ -687,11 +692,14 @@ void QPainterPrivate::updateInvMatrix()
invMatrix = state->matrix.inverted();
}
+extern bool qt_isExtendedRadialGradient(const QBrush &brush);
+
void QPainterPrivate::updateEmulationSpecifier(QPainterState *s)
{
bool alpha = false;
bool linearGradient = false;
bool radialGradient = false;
+ bool extendedRadialGradient = false;
bool conicalGradient = false;
bool patternBrush = false;
bool xform = false;
@@ -723,6 +731,7 @@ void QPainterPrivate::updateEmulationSpecifier(QPainterState *s)
(brushStyle == Qt::LinearGradientPattern));
radialGradient = ((penBrushStyle == Qt::RadialGradientPattern) ||
(brushStyle == Qt::RadialGradientPattern));
+ extendedRadialGradient = radialGradient && (qt_isExtendedRadialGradient(penBrush) || qt_isExtendedRadialGradient(s->brush));
conicalGradient = ((penBrushStyle == Qt::ConicalGradientPattern) ||
(brushStyle == Qt::ConicalGradientPattern));
patternBrush = (((penBrushStyle > Qt::SolidPattern
@@ -806,7 +815,7 @@ void QPainterPrivate::updateEmulationSpecifier(QPainterState *s)
s->emulationSpecifier &= ~QPaintEngine::LinearGradientFill;
// Radial gradient emulation
- if (radialGradient && !engine->hasFeature(QPaintEngine::RadialGradientFill))
+ if (extendedRadialGradient || (radialGradient && !engine->hasFeature(QPaintEngine::RadialGradientFill)))
s->emulationSpecifier |= QPaintEngine::RadialGradientFill;
else
s->emulationSpecifier &= ~QPaintEngine::RadialGradientFill;
@@ -5809,7 +5818,7 @@ void QPainter::drawGlyphs(const QPointF &position, const QGlyphs &glyphs)
bool paintEngineSupportsTransformations =
d->extended != 0
? qt_paintengine_supports_transformations(d->extended->type())
- : false;
+ : qt_paintengine_supports_transformations(d->engine->type());
for (int i=0; i<count; ++i) {
QPointF processedPosition = position + glyphPositions.at(i);
if (!paintEngineSupportsTransformations)
diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h
index 4b2c447..1a432e6 100644
--- a/src/gui/painting/qpainter.h
+++ b/src/gui/painting/qpainter.h
@@ -553,6 +553,7 @@ private:
friend class QPaintEngine;
friend class QPaintEngineExPrivate;
friend class QOpenGLPaintEngine;
+ friend class QVGPaintEngine;
friend class QX11PaintEngine;
friend class QX11PaintEnginePrivate;
friend class QWin32PaintEngine;
diff --git a/src/gui/painting/qunifiedtoolbarsurface_mac.cpp b/src/gui/painting/qunifiedtoolbarsurface_mac.cpp
index 3876c3d..2fda6b9 100644
--- a/src/gui/painting/qunifiedtoolbarsurface_mac.cpp
+++ b/src/gui/painting/qunifiedtoolbarsurface_mac.cpp
@@ -152,7 +152,8 @@ void QUnifiedToolbarSurface::beginPaint(const QRegion &rgn)
void QUnifiedToolbarSurface::updateToolbarOffset(QWidget *widget)
{
QMainWindowLayout *mlayout = qobject_cast<QMainWindowLayout*> (widget->window()->layout());
- mlayout->updateUnifiedToolbarOffset();
+ if (mlayout)
+ mlayout->updateUnifiedToolbarOffset();
}
void QUnifiedToolbarSurface::flush(QWidget *widget, const QRegion &region, const QPoint &offset)
diff --git a/src/gui/text/qglyphs.cpp b/src/gui/text/qglyphs.cpp
index b8a418d..cfea6ec 100644
--- a/src/gui/text/qglyphs.cpp
+++ b/src/gui/text/qglyphs.cpp
@@ -50,7 +50,7 @@ QT_BEGIN_NAMESPACE
/*!
\class QGlyphs
- \brief the QGlyphs class provides direct access to the internal glyphs in a font
+ \brief The QGlyphs class provides direct access to the internal glyphs in a font.
\since 4.8
\ingroup text
@@ -76,8 +76,8 @@ QT_BEGIN_NAMESPACE
QTextLayout::glyphs() or QTextFragment::glyphs() can be used to convert unicode encoded text
into a list of QGlyphs objects, and QPainter::drawGlyphs() can be used to draw the glyphs.
- \note Please note that QRawFont is considered local to the thread in which it is constructed,
- which in turn means that a new QRawFont will have to be created and set on the QGlyphs if it is
+ \note Please note that QRawFont is considered local to the thread in which it is constructed.
+ This in turn means that a new QRawFont will have to be created and set on the QGlyphs if it is
moved to a different thread. If the QGlyphs contains a reference to a QRawFont from a different
thread than the current, it will not be possible to draw the glyphs using a QPainter, as the
QRawFont is considered invalid and inaccessible in this case.
diff --git a/src/gui/text/qtextcursor.cpp b/src/gui/text/qtextcursor.cpp
index 6ddfdb0..4f6857a 100644
--- a/src/gui/text/qtextcursor.cpp
+++ b/src/gui/text/qtextcursor.cpp
@@ -362,20 +362,23 @@ bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor
currentCharFormat = -1;
bool adjustX = true;
QTextBlock blockIt = block();
+ bool visualMovement = priv->defaultCursorMoveStyle == QTextCursor::Visual;
if (!blockIt.isValid())
return false;
- if (op >= QTextCursor::Left && op <= QTextCursor::WordRight
- && blockIt.textDirection() == Qt::RightToLeft) {
- if (op == QTextCursor::Left)
- op = QTextCursor::NextCharacter;
- else if (op == QTextCursor::Right)
- op = QTextCursor::PreviousCharacter;
- else if (op == QTextCursor::WordLeft)
+ if (blockIt.textDirection() == Qt::RightToLeft) {
+ if (op == QTextCursor::WordLeft)
op = QTextCursor::NextWord;
else if (op == QTextCursor::WordRight)
op = QTextCursor::PreviousWord;
+
+ if (!visualMovement) {
+ if (op == QTextCursor::Left)
+ op = QTextCursor::NextCharacter;
+ else if (op == QTextCursor::Right)
+ op = QTextCursor::PreviousCharacter;
+ }
}
const QTextLayout *layout = blockLayout(blockIt);
@@ -418,9 +421,12 @@ bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor
break;
}
case QTextCursor::PreviousCharacter:
- case QTextCursor::Left:
newPosition = priv->previousCursorPosition(position, QTextLayout::SkipCharacters);
break;
+ case QTextCursor::Left:
+ newPosition = visualMovement ? priv->leftCursorPosition(position)
+ : priv->previousCursorPosition(position, QTextLayout::SkipCharacters);
+ break;
case QTextCursor::StartOfWord: {
if (relativePos == 0)
break;
@@ -529,9 +535,12 @@ bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor
break;
}
case QTextCursor::NextCharacter:
- case QTextCursor::Right:
newPosition = priv->nextCursorPosition(position, QTextLayout::SkipCharacters);
break;
+ case QTextCursor::Right:
+ newPosition = visualMovement ? priv->rightCursorPosition(position)
+ : priv->nextCursorPosition(position, QTextLayout::SkipCharacters);
+ break;
case QTextCursor::NextWord:
case QTextCursor::WordRight:
newPosition = priv->nextCursorPosition(position, QTextLayout::SkipWords);
@@ -2558,4 +2567,19 @@ QTextDocument *QTextCursor::document() const
return 0; // document went away
}
+/*!
+ \enum QTextCursor::MoveStyle
+
+ This enum describes the movement style available to QTextCursor. The options
+ are:
+
+ \value Logical Within a left-to-right text block, increase cursor position
+ when pressing left arrow key, decrease cursor position when pressing the
+ right arrow key. If the text block is right-to-left, the opposite behavior
+ applies.
+ \value Visual Pressing the left arrow key will always cause the cursor to move
+ left, regardless of the text's writing direction. The same behavior applies to
+ right arrow key.
+*/
+
QT_END_NAMESPACE
diff --git a/src/gui/text/qtextcursor.h b/src/gui/text/qtextcursor.h
index 4eaeb41..9e4c0b8 100644
--- a/src/gui/text/qtextcursor.h
+++ b/src/gui/text/qtextcursor.h
@@ -86,6 +86,10 @@ public:
MoveAnchor,
KeepAnchor
};
+ enum MoveStyle {
+ Logical,
+ Visual
+ };
void setPosition(int pos, MoveMode mode = MoveAnchor);
int position() const;
diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp
index 6dbd755..36f3c6c 100644
--- a/src/gui/text/qtextdocument.cpp
+++ b/src/gui/text/qtextdocument.cpp
@@ -586,6 +586,29 @@ void QTextDocument::setDefaultTextOption(const QTextOption &option)
}
/*!
+ \since 4.8
+
+ The default cursor movement style is used by all QTextCursor objects
+ created from the document. The default is QTextCursor::Logical.
+*/
+QTextCursor::MoveStyle QTextDocument::defaultCursorMoveStyle() const
+{
+ Q_D(const QTextDocument);
+ return d->defaultCursorMoveStyle;
+}
+
+/*!
+ \since 4.8
+
+ Set the default cursor movement style.
+*/
+void QTextDocument::setDefaultCursorMoveStyle(QTextCursor::MoveStyle style)
+{
+ Q_D(QTextDocument);
+ d->defaultCursorMoveStyle = style;
+}
+
+/*!
\fn void QTextDocument::markContentsDirty(int position, int length)
Marks the contents specified by the given \a position and \a length
diff --git a/src/gui/text/qtextdocument.h b/src/gui/text/qtextdocument.h
index f87ccc9..e515b36 100644
--- a/src/gui/text/qtextdocument.h
+++ b/src/gui/text/qtextdocument.h
@@ -46,6 +46,7 @@
#include <QtCore/qsize.h>
#include <QtCore/qrect.h>
#include <QtGui/qfont.h>
+#include <QtGui/qtextcursor.h>
QT_BEGIN_HEADER
@@ -60,7 +61,6 @@ class QPainter;
class QPrinter;
class QAbstractTextDocumentLayout;
class QPoint;
-class QTextCursor;
class QTextObject;
class QTextFormat;
class QTextFrame;
@@ -269,6 +269,9 @@ public:
QTextOption defaultTextOption() const;
void setDefaultTextOption(const QTextOption &option);
+ QTextCursor::MoveStyle defaultCursorMoveStyle() const;
+ void setDefaultCursorMoveStyle(QTextCursor::MoveStyle style);
+
Q_SIGNALS:
void contentsChange(int from, int charsRemoves, int charsAdded);
void contentsChanged();
diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp
index 2172f74..71af89f 100644
--- a/src/gui/text/qtextdocument_p.cpp
+++ b/src/gui/text/qtextdocument_p.cpp
@@ -209,6 +209,7 @@ QTextDocumentPrivate::QTextDocumentPrivate()
defaultTextOption.setTabStop(80); // same as in qtextengine.cpp
defaultTextOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+ defaultCursorMoveStyle = QTextCursor::Logical;
indentWidth = 40;
documentMargin = 4;
@@ -1382,6 +1383,20 @@ int QTextDocumentPrivate::previousCursorPosition(int position, QTextLayout::Curs
return it.layout()->previousCursorPosition(position-start, mode) + start;
}
+int QTextDocumentPrivate::leftCursorPosition(int position) const
+{
+ QTextBlock it = blocksFind(position);
+ int start = it.position();
+ return it.layout()->leftCursorPosition(position-start) + start;
+}
+
+int QTextDocumentPrivate::rightCursorPosition(int position) const
+{
+ QTextBlock it = blocksFind(position);
+ int start = it.position();
+ return it.layout()->rightCursorPosition(position-start) + start;
+}
+
void QTextDocumentPrivate::changeObjectFormat(QTextObject *obj, int format)
{
beginEditBlock();
diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h
index b464f2e..6563920 100644
--- a/src/gui/text/qtextdocument_p.h
+++ b/src/gui/text/qtextdocument_p.h
@@ -64,6 +64,7 @@
#include "private/qtextformat_p.h"
#include "QtGui/qtextdocument.h"
#include "QtGui/qtextobject.h"
+#include "QtGui/qtextcursor.h"
#include "QtCore/qmap.h"
#include "QtCore/qvariant.h"
#include "QtCore/qurl.h"
@@ -244,6 +245,8 @@ public:
int nextCursorPosition(int position, QTextLayout::CursorMode mode) const;
int previousCursorPosition(int position, QTextLayout::CursorMode mode) const;
+ int leftCursorPosition(int position) const;
+ int rightCursorPosition(int position) const;
void changeObjectFormat(QTextObject *group, int format);
@@ -339,6 +342,7 @@ private:
public:
QTextOption defaultTextOption;
+ QTextCursor::MoveStyle defaultCursorMoveStyle;
#ifndef QT_NO_CSSPARSER
QCss::StyleSheet parsedDefaultStyleSheet;
#endif
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
index 08d0eca..dab4bb7 100644
--- a/src/gui/text/qtextengine.cpp
+++ b/src/gui/text/qtextengine.cpp
@@ -856,6 +856,21 @@ void QTextEngine::shapeLine(const QScriptLine &line)
}
}
+#if !defined(QT_ENABLE_HARFBUZZ_FOR_MAC)
+static bool enableHarfBuzz()
+{
+ static enum { Yes, No, Unknown } status = Unknown;
+
+ if (status == Unknown) {
+ QByteArray v = qgetenv("QT_ENABLE_HARFBUZZ");
+ bool value = !v.isEmpty() && v != "0" && v != "false";
+ if (value) status = Yes;
+ else status = No;
+ }
+ return status == Yes;
+}
+#endif
+
void QTextEngine::shapeText(int item) const
{
Q_ASSERT(item < layoutData->items.size());
@@ -865,7 +880,24 @@ void QTextEngine::shapeText(int item) const
return;
#if defined(Q_WS_MAC)
- shapeTextMac(item);
+#if !defined(QT_ENABLE_HARFBUZZ_FOR_MAC)
+ if (enableHarfBuzz()) {
+#endif
+ QFontEngine *actualFontEngine = fontEngine(si, &si.ascent, &si.descent, &si.leading);
+ if (actualFontEngine->type() == QFontEngine::Multi)
+ actualFontEngine = static_cast<QFontEngineMulti *>(actualFontEngine)->engine(0);
+
+ HB_Face face = actualFontEngine->harfbuzzFace();
+ HB_Script script = (HB_Script) si.analysis.script;
+ if (face->supported_scripts[script])
+ shapeTextWithHarfbuzz(item);
+ else
+ shapeTextMac(item);
+#if !defined(QT_ENABLE_HARFBUZZ_FOR_MAC)
+ } else {
+ shapeTextMac(item);
+ }
+#endif
#elif defined(Q_WS_WINCE)
shapeTextWithCE(item);
#else
@@ -1304,6 +1336,7 @@ static void init(QTextEngine *e)
e->ignoreBidi = false;
e->cacheGlyphs = false;
e->forceJustification = false;
+ e->visualMovement = false;
e->layoutData = 0;
@@ -2737,6 +2770,180 @@ QFixed QTextEngine::leadingSpaceWidth(const QScriptLine &line)
return width(line.from + pos, line.length - pos);
}
+QFixed QTextEngine::alignLine(const QScriptLine &line)
+{
+ QFixed x = 0;
+ justify(line);
+ // if width is QFIXED_MAX that means we used setNumColumns() and that implicitly makes this line left aligned.
+ if (!line.justified && line.width != QFIXED_MAX) {
+ int align = option.alignment();
+ if (align & Qt::AlignJustify && isRightToLeft())
+ align = Qt::AlignRight;
+ if (align & Qt::AlignRight)
+ x = line.width - (line.textAdvance + leadingSpaceWidth(line));
+ else if (align & Qt::AlignHCenter)
+ x = (line.width - line.textAdvance)/2;
+ }
+ return x;
+}
+
+QFixed QTextEngine::offsetInLigature(const QScriptItem *si, int pos, int max, int glyph_pos)
+{
+ unsigned short *logClusters = this->logClusters(si);
+ const QGlyphLayout &glyphs = shapedGlyphs(si);
+
+ int offsetInCluster = 0;
+ for (int i = pos - 1; i >= 0; i--) {
+ if (logClusters[i] == glyph_pos)
+ offsetInCluster++;
+ else
+ break;
+ }
+
+ // in the case that the offset is inside a (multi-character) glyph,
+ // interpolate the position.
+ if (offsetInCluster > 0) {
+ int clusterLength = 0;
+ for (int i = pos - offsetInCluster; i < max; i++) {
+ if (logClusters[i] == glyph_pos)
+ clusterLength++;
+ else
+ break;
+ }
+ if (clusterLength)
+ return glyphs.advances_x[glyph_pos] * offsetInCluster / clusterLength;
+ }
+
+ return 0;
+}
+
+int QTextEngine::previousLogicalPosition(int oldPos) const
+{
+ const HB_CharAttributes *attrs = attributes();
+ if (!attrs || oldPos < 0)
+ return oldPos;
+
+ if (oldPos <= 0)
+ return 0;
+ oldPos--;
+ while (oldPos && !attrs[oldPos].charStop)
+ oldPos--;
+ return oldPos;
+}
+
+int QTextEngine::nextLogicalPosition(int oldPos) const
+{
+ const HB_CharAttributes *attrs = attributes();
+ int len = block.isValid() ? block.length() - 1
+ : layoutData->string.length();
+ Q_ASSERT(len <= layoutData->string.length());
+ if (!attrs || oldPos < 0 || oldPos >= len)
+ return oldPos;
+
+ oldPos++;
+ while (oldPos < len && !attrs[oldPos].charStop)
+ oldPos++;
+ return oldPos;
+}
+
+int QTextEngine::lineNumberForTextPosition(int pos)
+{
+ if (!layoutData)
+ itemize();
+ if (pos == layoutData->string.length() && lines.size())
+ return lines.size() - 1;
+ for (int i = 0; i < lines.size(); ++i) {
+ const QScriptLine& line = lines[i];
+ if (line.from + line.length > pos)
+ return i;
+ }
+ return -1;
+}
+
+void QTextEngine::insertionPointsForLine(int lineNum, QVector<int> &insertionPoints)
+{
+ QTextLineItemIterator iterator(this, lineNum);
+ bool rtl = isRightToLeft();
+ bool lastLine = lineNum >= lines.size() - 1;
+
+ while (!iterator.atEnd()) {
+ iterator.next();
+ const QScriptItem *si = &layoutData->items[iterator.item];
+ if (si->analysis.bidiLevel % 2) {
+ int i = iterator.itemEnd - 1, min = iterator.itemStart;
+ if (lastLine && (rtl ? iterator.atBeginning() : iterator.atEnd()))
+ i++;
+ for (; i >= min; i--)
+ insertionPoints.push_back(i);
+ } else {
+ int i = iterator.itemStart, max = iterator.itemEnd;
+ if (lastLine && (rtl ? iterator.atBeginning() : iterator.atEnd()))
+ max++;
+ for (; i < max; i++)
+ insertionPoints.push_back(i);
+ }
+ }
+}
+
+int QTextEngine::endOfLine(int lineNum)
+{
+ QVector<int> insertionPoints;
+ insertionPointsForLine(lineNum, insertionPoints);
+
+ if (insertionPoints.size() > 0)
+ return insertionPoints.last();
+ return 0;
+}
+
+int QTextEngine::beginningOfLine(int lineNum)
+{
+ QVector<int> insertionPoints;
+ insertionPointsForLine(lineNum, insertionPoints);
+
+ if (insertionPoints.size() > 0)
+ return insertionPoints.first();
+ return 0;
+}
+
+int QTextEngine::positionAfterVisualMovement(int pos, QTextCursor::MoveOperation op)
+{
+ if (!layoutData)
+ itemize();
+
+ bool moveRight = (op == QTextCursor::Right);
+ bool alignRight = isRightToLeft();
+ if (!layoutData->hasBidi)
+ return moveRight ^ alignRight ? nextLogicalPosition(pos) : previousLogicalPosition(pos);
+
+ int lineNum = lineNumberForTextPosition(pos);
+ Q_ASSERT(lineNum >= 0);
+
+ QVector<int> insertionPoints;
+ insertionPointsForLine(lineNum, insertionPoints);
+ int i, max = insertionPoints.size();
+ for (i = 0; i < max; i++)
+ if (pos == insertionPoints[i]) {
+ if (moveRight) {
+ if (i + 1 < max)
+ return insertionPoints[i + 1];
+ } else {
+ if (i > 0)
+ return insertionPoints[i - 1];
+ }
+
+ if (moveRight ^ alignRight) {
+ if (lineNum + 1 < lines.size())
+ return alignRight ? endOfLine(lineNum + 1) : beginningOfLine(lineNum + 1);
+ }
+ else {
+ if (lineNum > 0)
+ return alignRight ? beginningOfLine(lineNum - 1) : endOfLine(lineNum - 1);
+ }
+ }
+
+ return pos;
+}
+
QStackTextEngine::QStackTextEngine(const QString &string, const QFont &f)
: QTextEngine(string, f),
_layoutData(string, _memory, MemSize)
@@ -2841,5 +3048,127 @@ glyph_metrics_t glyph_metrics_t::transformed(const QTransform &matrix) const
return m;
}
+QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int _lineNum, const QPointF &pos,
+ const QTextLayout::FormatRange *_selection)
+ : eng(_eng),
+ line(eng->lines[_lineNum]),
+ si(0),
+ lineNum(_lineNum),
+ lineEnd(line.from + line.length),
+ firstItem(eng->findItem(line.from)),
+ lastItem(eng->findItem(lineEnd - 1)),
+ nItems((firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0),
+ logicalItem(-1),
+ item(-1),
+ visualOrder(nItems),
+ levels(nItems),
+ selection(_selection)
+{
+ pos_x = x = QFixed::fromReal(pos.x());
+
+ x += line.x;
+
+ x += eng->alignLine(line);
+
+ for (int i = 0; i < nItems; ++i)
+ levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
+ QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+
+ eng->shapeLine(line);
+}
+
+QScriptItem &QTextLineItemIterator::next()
+{
+ x += itemWidth;
+
+ ++logicalItem;
+ item = visualOrder[logicalItem] + firstItem;
+ itemLength = eng->length(item);
+ si = &eng->layoutData->items[item];
+ if (!si->num_glyphs)
+ eng->shape(item);
+
+ if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
+ itemWidth = si->width;
+ return *si;
+ }
+
+ unsigned short *logClusters = eng->logClusters(si);
+ QGlyphLayout glyphs = eng->shapedGlyphs(si);
+
+ itemStart = qMax(line.from, si->position);
+ glyphsStart = logClusters[itemStart - si->position];
+ if (lineEnd < si->position + itemLength) {
+ itemEnd = lineEnd;
+ glyphsEnd = logClusters[itemEnd-si->position];
+ } else {
+ itemEnd = si->position + itemLength;
+ glyphsEnd = si->num_glyphs;
+ }
+ // show soft-hyphen at line-break
+ if (si->position + itemLength >= lineEnd
+ && eng->layoutData->string.at(lineEnd - 1) == 0x00ad)
+ glyphs.attributes[glyphsEnd - 1].dontPrint = false;
+
+ itemWidth = 0;
+ for (int g = glyphsStart; g < glyphsEnd; ++g)
+ itemWidth += glyphs.effectiveAdvance(g);
+
+ return *si;
+}
+
+bool QTextLineItemIterator::getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const
+{
+ *selectionX = *selectionWidth = 0;
+
+ if (!selection)
+ return false;
+
+ if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
+ if (si->position >= selection->start + selection->length
+ || si->position + itemLength <= selection->start)
+ return false;
+
+ *selectionX = x;
+ *selectionWidth = itemWidth;
+ } else {
+ unsigned short *logClusters = eng->logClusters(si);
+ QGlyphLayout glyphs = eng->shapedGlyphs(si);
+
+ int from = qMax(itemStart, selection->start) - si->position;
+ int to = qMin(itemEnd, selection->start + selection->length) - si->position;
+ if (from >= to)
+ return false;
+
+ int start_glyph = logClusters[from];
+ int end_glyph = (to == eng->length(item)) ? si->num_glyphs : logClusters[to];
+ QFixed soff;
+ QFixed swidth;
+ if (si->analysis.bidiLevel %2) {
+ for (int g = glyphsEnd - 1; g >= end_glyph; --g)
+ soff += glyphs.effectiveAdvance(g);
+ for (int g = end_glyph - 1; g >= start_glyph; --g)
+ swidth += glyphs.effectiveAdvance(g);
+ } else {
+ for (int g = glyphsStart; g < start_glyph; ++g)
+ soff += glyphs.effectiveAdvance(g);
+ for (int g = start_glyph; g < end_glyph; ++g)
+ swidth += glyphs.effectiveAdvance(g);
+ }
+
+ // If the starting character is in the middle of a ligature,
+ // selection should only contain the right part of that ligature
+ // glyph, so we need to get the width of the left part here and
+ // add it to *selectionX
+ QFixed leftOffsetInLigature = eng->offsetInLigature(si, from, to, start_glyph);
+ *selectionX = x + soff + leftOffsetInLigature;
+ *selectionWidth = swidth - leftOffsetInLigature;
+ // If the ending character is also part of a ligature, swidth does
+ // not contain that part yet, we also need to find out the width of
+ // that left part
+ *selectionWidth += eng->offsetInLigature(si, to, eng->length(item), end_glyph);
+ }
+ return true;
+}
QT_END_NAMESPACE
diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h
index 366c5c3..c476485 100644
--- a/src/gui/text/qtextengine_p.h
+++ b/src/gui/text/qtextengine_p.h
@@ -64,6 +64,7 @@
#include "QtGui/qpaintengine.h"
#include "QtGui/qtextobject.h"
#include "QtGui/qtextoption.h"
+#include "QtGui/qtextcursor.h"
#include "QtCore/qset.h"
#include "QtCore/qdebug.h"
#ifndef QT_BUILD_COMPAT_LIB
@@ -471,6 +472,7 @@ public:
void shape(int item) const;
void justify(const QScriptLine &si);
+ QFixed alignLine(const QScriptLine &line);
QFixed width(int charFrom, int numChars) const;
glyph_metrics_t boundingBox(int from, int len) const;
@@ -586,12 +588,18 @@ public:
uint cacheGlyphs : 1;
uint stackEngine : 1;
uint forceJustification : 1;
+ uint visualMovement : 1;
int *underlinePositions;
mutable LayoutData *layoutData;
inline bool hasFormats() const { return (block.docHandle() || specialData); }
+ inline bool visualCursorMovement() const
+ {
+ return (visualMovement ||
+ (block.docHandle() ? block.docHandle()->defaultCursorMoveStyle == QTextCursor::Visual : false));
+ }
struct SpecialData {
int preeditPosition;
@@ -611,6 +619,13 @@ public:
void shapeLine(const QScriptLine &line);
QFixed leadingSpaceWidth(const QScriptLine &line);
+ QFixed offsetInLigature(const QScriptItem *si, int pos, int max, int glyph_pos);
+ int previousLogicalPosition(int oldPos) const;
+ int nextLogicalPosition(int oldPos) const;
+ int lineNumberForTextPosition(int pos);
+ int positionAfterVisualMovement(int oldPos, QTextCursor::MoveOperation op);
+ void insertionPointsForLine(int lineNum, QVector<int> &insertionPoints);
+
private:
void setBoundary(int strPos) const;
void addRequiredBoundaries() const;
@@ -625,6 +640,8 @@ private:
void splitItem(int item, int pos) const;
void resolveAdditionalFormats() const;
+ int endOfLine(int lineNum);
+ int beginningOfLine(int lineNum);
};
class QStackTextEngine : public QTextEngine {
@@ -635,6 +652,49 @@ public:
void *_memory[MemSize];
};
+struct QTextLineItemIterator
+{
+ QTextLineItemIterator(QTextEngine *eng, int lineNum, const QPointF &pos = QPointF(),
+ const QTextLayout::FormatRange *_selection = 0);
+
+ inline bool atEnd() const { return logicalItem >= nItems - 1; }
+ inline bool atBeginning() const { return logicalItem <= 0; }
+ QScriptItem &next();
+
+ bool getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const;
+ inline bool isOutsideSelection() const {
+ QFixed tmp1, tmp2;
+ return !getSelectionBounds(&tmp1, &tmp2);
+ }
+
+ QTextEngine *eng;
+
+ QFixed x;
+ QFixed pos_x;
+ const QScriptLine &line;
+ QScriptItem *si;
+
+ int lineNum;
+ int lineEnd;
+ int firstItem;
+ int lastItem;
+ int nItems;
+ int logicalItem;
+ int item;
+ int itemLength;
+
+ int glyphsStart;
+ int glyphsEnd;
+ int itemStart;
+ int itemEnd;
+
+ QFixed itemWidth;
+
+ QVarLengthArray<int> visualOrder;
+ QVarLengthArray<uchar> levels;
+
+ const QTextLayout::FormatRange *selection;
+};
Q_DECLARE_OPERATORS_FOR_FLAGS(QTextEngine::ShaperFlags)
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
index 93f71d3..bd224c6 100644
--- a/src/gui/text/qtextlayout.cpp
+++ b/src/gui/text/qtextlayout.cpp
@@ -72,23 +72,6 @@ QT_BEGIN_NAMESPACE
#define SuppressText 0x5012
#define SuppressBackground 0x513
-static QFixed alignLine(QTextEngine *eng, const QScriptLine &line)
-{
- QFixed x = 0;
- eng->justify(line);
- // if width is QFIXED_MAX that means we used setNumColumns() and that implicitly makes this line left aligned.
- if (!line.justified && line.width != QFIXED_MAX) {
- int align = eng->option.alignment();
- if (align & Qt::AlignJustify && eng->isRightToLeft())
- align = Qt::AlignRight;
- if (align & Qt::AlignRight)
- x = line.width - (line.textAdvance + eng->leadingSpaceWidth(line));
- else if (align & Qt::AlignHCenter)
- x = (line.width - line.textAdvance)/2;
- }
- return x;
-}
-
/*!
\class QTextLayout::FormatRange
\reentrant
@@ -596,6 +579,30 @@ bool QTextLayout::cacheEnabled() const
}
/*!
+ Set the visual cursor movement style. If the QTextLayout is backed by
+ a document, you can ignore this and use the option in QTextDocument,
+ this option is for widgets like QLineEdit or custom widgets without
+ a QTextDocument. Default value is QTextCursor::Logical.
+
+ \sa setCursorMoveStyle()
+*/
+void QTextLayout::setCursorMoveStyle(QTextCursor::MoveStyle style)
+{
+ d->visualMovement = style == QTextCursor::Visual ? true : false;
+}
+
+/*!
+ The cursor movement style of this QTextLayout. The default is
+ QTextCursor::Logical.
+
+ \sa setCursorMoveStyle()
+*/
+QTextCursor::MoveStyle QTextLayout::cursorMoveStyle() const
+{
+ return d->visualMovement ? QTextCursor::Visual : QTextCursor::Logical;
+}
+
+/*!
Begins the layout process.
\sa endLayout()
@@ -718,6 +725,34 @@ int QTextLayout::previousCursorPosition(int oldPos, CursorMode mode) const
}
/*!
+ Returns the cursor position to the right of \a oldPos, next to it.
+ It's dependent on the visual position of characters, after bi-directional
+ reordering.
+
+ \sa leftCursorPosition(), nextCursorPosition()
+*/
+int QTextLayout::rightCursorPosition(int oldPos) const
+{
+ int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Right);
+// qDebug("%d -> %d", oldPos, newPos);
+ return newPos;
+}
+
+/*!
+ Returns the cursor position to the left of \a oldPos, next to it.
+ It's dependent on the visual position of characters, after bi-directional
+ reordering.
+
+ \sa rightCursorPosition(), previousCursorPosition()
+*/
+int QTextLayout::leftCursorPosition(int oldPos) const
+{
+ int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Left);
+// qDebug("%d -> %d", oldPos, newPos);
+ return newPos;
+}
+
+/*!/
Returns true if position \a pos is a valid cursor position.
In a Unicode context some positions in the text are not valid
@@ -815,16 +850,8 @@ QTextLine QTextLayout::lineAt(int i) const
*/
QTextLine QTextLayout::lineForTextPosition(int pos) const
{
- for (int i = 0; i < d->lines.size(); ++i) {
- const QScriptLine& line = d->lines[i];
- if (line.from + (int)line.length > pos)
- return QTextLine(i, d);
- }
- if (!d->layoutData)
- d->itemize();
- if (pos == d->layoutData->string.length() && d->lines.size())
- return QTextLine(d->lines.size()-1, d);
- return QTextLine();
+ int lineNum = d->lineNumberForTextPosition(pos);
+ return lineNum >= 0 ? lineAt(lineNum) : QTextLine();
}
/*!
@@ -919,201 +946,6 @@ void QTextLayout::setFlags(int flags)
}
}
-struct QTextLineItemIterator
-{
- QTextLineItemIterator(QTextEngine *eng, int lineNum, const QPointF &pos = QPointF(),
- const QTextLayout::FormatRange *_selection = 0);
-
- inline bool atEnd() const { return logicalItem >= nItems - 1; }
- QScriptItem &next();
-
- bool getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const;
- inline bool isOutsideSelection() const {
- QFixed tmp1, tmp2;
- return !getSelectionBounds(&tmp1, &tmp2);
- }
-
- QTextEngine *eng;
-
- QFixed x;
- QFixed pos_x;
- const QScriptLine &line;
- QScriptItem *si;
-
- int lineEnd;
- int firstItem;
- int lastItem;
- int nItems;
- int logicalItem;
- int item;
- int itemLength;
-
- int glyphsStart;
- int glyphsEnd;
- int itemStart;
- int itemEnd;
-
- QFixed itemWidth;
-
- QVarLengthArray<int> visualOrder;
- QVarLengthArray<uchar> levels;
-
- const QTextLayout::FormatRange *selection;
-};
-
-QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int lineNum, const QPointF &pos,
- const QTextLayout::FormatRange *_selection)
- : eng(_eng),
- line(eng->lines[lineNum]),
- si(0),
- lineEnd(line.from + line.length),
- firstItem(eng->findItem(line.from)),
- lastItem(eng->findItem(lineEnd - 1)),
- nItems((firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0),
- logicalItem(-1),
- item(-1),
- visualOrder(nItems),
- levels(nItems),
- selection(_selection)
-{
- pos_x = x = QFixed::fromReal(pos.x());
-
- x += line.x;
-
- x += alignLine(eng, line);
-
- for (int i = 0; i < nItems; ++i)
- levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
- QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
-
- eng->shapeLine(line);
-}
-
-QScriptItem &QTextLineItemIterator::next()
-{
- x += itemWidth;
-
- ++logicalItem;
- item = visualOrder[logicalItem] + firstItem;
- itemLength = eng->length(item);
- si = &eng->layoutData->items[item];
- if (!si->num_glyphs)
- eng->shape(item);
-
- if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
- itemWidth = si->width;
- return *si;
- }
-
- unsigned short *logClusters = eng->logClusters(si);
- QGlyphLayout glyphs = eng->shapedGlyphs(si);
-
- itemStart = qMax(line.from, si->position);
- glyphsStart = logClusters[itemStart - si->position];
- if (lineEnd < si->position + itemLength) {
- itemEnd = lineEnd;
- glyphsEnd = logClusters[itemEnd-si->position];
- } else {
- itemEnd = si->position + itemLength;
- glyphsEnd = si->num_glyphs;
- }
- // show soft-hyphen at line-break
- if (si->position + itemLength >= lineEnd
- && eng->layoutData->string.at(lineEnd - 1) == 0x00ad)
- glyphs.attributes[glyphsEnd - 1].dontPrint = false;
-
- itemWidth = 0;
- for (int g = glyphsStart; g < glyphsEnd; ++g)
- itemWidth += glyphs.effectiveAdvance(g);
-
- return *si;
-}
-
-static QFixed offsetInLigature(const unsigned short *logClusters,
- const QGlyphLayout &glyphs,
- int pos, int max, int glyph_pos)
-{
- int offsetInCluster = 0;
- for (int i = pos - 1; i >= 0; i--) {
- if (logClusters[i] == glyph_pos)
- offsetInCluster++;
- else
- break;
- }
-
- // in the case that the offset is inside a (multi-character) glyph,
- // interpolate the position.
- if (offsetInCluster > 0) {
- int clusterLength = 0;
- for (int i = pos - offsetInCluster; i < max; i++) {
- if (logClusters[i] == glyph_pos)
- clusterLength++;
- else
- break;
- }
- if (clusterLength)
- return glyphs.advances_x[glyph_pos] * offsetInCluster / clusterLength;
- }
-
- return 0;
-}
-
-bool QTextLineItemIterator::getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const
-{
- *selectionX = *selectionWidth = 0;
-
- if (!selection)
- return false;
-
- if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
- if (si->position >= selection->start + selection->length
- || si->position + itemLength <= selection->start)
- return false;
-
- *selectionX = x;
- *selectionWidth = itemWidth;
- } else {
- unsigned short *logClusters = eng->logClusters(si);
- QGlyphLayout glyphs = eng->shapedGlyphs(si);
-
- int from = qMax(itemStart, selection->start) - si->position;
- int to = qMin(itemEnd, selection->start + selection->length) - si->position;
- if (from >= to)
- return false;
-
- int start_glyph = logClusters[from];
- int end_glyph = (to == eng->length(item)) ? si->num_glyphs : logClusters[to];
- QFixed soff;
- QFixed swidth;
- if (si->analysis.bidiLevel %2) {
- for (int g = glyphsEnd - 1; g >= end_glyph; --g)
- soff += glyphs.effectiveAdvance(g);
- for (int g = end_glyph - 1; g >= start_glyph; --g)
- swidth += glyphs.effectiveAdvance(g);
- } else {
- for (int g = glyphsStart; g < start_glyph; ++g)
- soff += glyphs.effectiveAdvance(g);
- for (int g = start_glyph; g < end_glyph; ++g)
- swidth += glyphs.effectiveAdvance(g);
- }
-
- // If the starting character is in the middle of a ligature,
- // selection should only contain the right part of that ligature
- // glyph, so we need to get the width of the left part here and
- // add it to *selectionX
- QFixed leftOffsetInLigature = offsetInLigature(logClusters, glyphs, from,
- to, start_glyph);
- *selectionX = x + soff + leftOffsetInLigature;
- *selectionWidth = swidth - leftOffsetInLigature;
- // If the ending character is also part of a ligature, swidth does
- // not contain that part yet, we also need to find out the width of
- // that left part
- *selectionWidth += offsetInLigature(logClusters, glyphs, to,
- eng->length(item), end_glyph);
- }
- return true;
-}
-
static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPointF &pos, QTextLayout::FormatRange *selection,
QPainterPath *region, QRectF boundingRect)
{
@@ -1382,18 +1214,9 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
QFixed pos_y = QFixed::fromReal(position.y());
cursorPosition = qBound(0, cursorPosition, d->layoutData->string.length());
- int line = 0;
- if (cursorPosition == d->layoutData->string.length()) {
- line = d->lines.size() - 1;
- } else {
- // ### binary search
- for (line = 0; line < d->lines.size(); line++) {
- const QScriptLine &sl = d->lines[line];
- if (sl.from <= cursorPosition && sl.from + (int)sl.length > cursorPosition)
- break;
- }
- }
-
+ int line = d->lineNumberForTextPosition(cursorPosition);
+ if (line < 0)
+ line = 0;
if (line >= d->lines.size())
return;
@@ -1402,7 +1225,15 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
qreal x = position.x() + l.cursorToX(cursorPosition);
- int itm = d->findItem(cursorPosition - 1);
+ int itm;
+
+ if (d->visualCursorMovement()) {
+ if (cursorPosition == sl.from + sl.length)
+ cursorPosition--;
+ itm = d->findItem(cursorPosition);
+ } else
+ itm = d->findItem(cursorPosition - 1);
+
QFixed base = sl.base();
QFixed descent = sl.descent;
bool rightToLeft = d->isRightToLeft();
@@ -1512,7 +1343,7 @@ QRectF QTextLine::rect() const
QRectF QTextLine::naturalTextRect() const
{
const QScriptLine& sl = eng->lines[i];
- QFixed x = sl.x + alignLine(eng, sl);
+ QFixed x = sl.x + eng->alignLine(sl);
QFixed width = sl.textWidth;
if (sl.justified)
@@ -2632,9 +2463,10 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
eng->itemize();
const QScriptLine &line = eng->lines[i];
+ bool lastLine = i >= eng->lines.size() - 1;
QFixed x = line.x;
- x += alignLine(eng, line);
+ x += eng->alignLine(line);
if (!i && !eng->layoutData->items.size()) {
*cursorPos = 0;
@@ -2720,21 +2552,29 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
logClusters = eng->logClusters(si);
glyphs = eng->shapedGlyphs(si);
if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
- if(pos == l)
+ if (pos == (reverse ? 0 : l))
x += si->width;
} else {
+ bool rtl = eng->isRightToLeft();
+ bool visual = eng->visualCursorMovement();
if (reverse) {
int end = qMin(lineEnd, si->position + l) - si->position;
int glyph_end = end == l ? si->num_glyphs : logClusters[end];
- for (int i = glyph_end - 1; i >= glyph_pos; i--)
+ int glyph_start = glyph_pos;
+ if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem)))
+ glyph_start++;
+ for (int i = glyph_end - 1; i >= glyph_start; i--)
x += glyphs.effectiveAdvance(i);
} else {
int start = qMax(line.from - si->position, 0);
int glyph_start = logClusters[start];
- for (int i = glyph_start; i < glyph_pos; i++)
+ int glyph_end = glyph_pos;
+ if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
+ glyph_end--;
+ for (int i = glyph_start; i <= glyph_end; i++)
x += glyphs.effectiveAdvance(i);
}
- x += offsetInLigature(logClusters, glyphs, pos, line.length, glyph_pos);
+ x += eng->offsetInLigature(si, pos, line.length, glyph_pos);
}
*cursorPos = pos + si->position;
@@ -2753,6 +2593,8 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
{
QFixed x = QFixed::fromReal(_x);
const QScriptLine &line = eng->lines[i];
+ bool lastLine = i >= eng->lines.size() - 1;
+ int lineNum = i;
if (!eng->layoutData)
eng->itemize();
@@ -2770,7 +2612,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
return 0;
x -= line.x;
- x -= alignLine(eng, line);
+ x -= eng->alignLine(line);
// qDebug("xToCursor: x=%f, cpos=%d", x.toReal(), cpos);
QVarLengthArray<int> visualOrder(nItems);
@@ -2779,6 +2621,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+ bool visual = eng->visualCursorMovement();
if (x <= 0) {
// left of first item
int item = visualOrder[0]+firstItem;
@@ -2795,8 +2638,13 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
|| (line.justified && x < line.width)) {
// has to be in one of the runs
QFixed pos;
+ bool rtl = eng->isRightToLeft();
eng->shapeLine(line);
+ QVector<int> insertionPoints;
+ if (visual && rtl)
+ eng->insertionPointsForLine(lineNum, insertionPoints);
+ int nchars = 0;
for (int i = 0; i < nItems; ++i) {
int item = visualOrder[i]+firstItem;
QScriptItem &si = eng->layoutData->items[item];
@@ -2826,6 +2674,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
if (pos + item_width < x) {
pos += item_width;
+ nchars += end;
continue;
}
// qDebug(" inside run");
@@ -2870,27 +2719,60 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
} else {
QFixed dist = INT_MAX/256;
if (si.analysis.bidiLevel % 2) {
- pos += item_width;
- while (gs <= ge) {
- if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
- glyph_pos = gs;
- dist = qAbs(x-pos);
+ if (!visual || rtl || (lastLine && i == nItems - 1)) {
+ pos += item_width;
+ while (gs <= ge) {
+ if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
+ glyph_pos = gs;
+ dist = qAbs(x-pos);
+ }
+ pos -= glyphs.effectiveAdvance(gs);
+ ++gs;
+ }
+ } else {
+ while (ge >= gs) {
+ if (glyphs.attributes[ge].clusterStart && qAbs(x-pos) < dist) {
+ glyph_pos = ge;
+ dist = qAbs(x-pos);
+ }
+ pos += glyphs.effectiveAdvance(ge);
+ --ge;
}
- pos -= glyphs.effectiveAdvance(gs);
- ++gs;
}
} else {
- while (gs <= ge) {
- if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
- glyph_pos = gs;
- dist = qAbs(x-pos);
+ if (!visual || !rtl || (lastLine && i == 0)) {
+ while (gs <= ge) {
+ if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
+ glyph_pos = gs;
+ dist = qAbs(x-pos);
+ }
+ pos += glyphs.effectiveAdvance(gs);
+ ++gs;
}
- pos += glyphs.effectiveAdvance(gs);
- ++gs;
+ } else {
+ QFixed oldPos = pos;
+ while (gs <= ge) {
+ pos += glyphs.effectiveAdvance(gs);
+ if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
+ glyph_pos = gs;
+ dist = qAbs(x-pos);
+ }
+ ++gs;
+ }
+ pos = oldPos;
}
}
- if (qAbs(x-pos) < dist)
+ if (qAbs(x-pos) < dist) {
+ if (visual) {
+ if (!rtl && i < nItems - 1) {
+ nchars += end;
+ continue;
+ }
+ if (rtl && nchars > 0)
+ return insertionPoints[lastLine ? nchars : nchars - 1];
+ }
return si.position + end;
+ }
}
Q_ASSERT(glyph_pos != -1);
int j;
diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h
index 9dd8ebd..6aa81f9 100644
--- a/src/gui/text/qtextlayout.h
+++ b/src/gui/text/qtextlayout.h
@@ -50,6 +50,7 @@
#include <QtGui/qevent.h>
#include <QtGui/qtextformat.h>
#include <QtGui/qglyphs.h>
+#include <QtGui/qtextcursor.h>
QT_BEGIN_HEADER
@@ -136,6 +137,9 @@ public:
void setCacheEnabled(bool enable);
bool cacheEnabled() const;
+ void setCursorMoveStyle(QTextCursor::MoveStyle style);
+ QTextCursor::MoveStyle cursorMoveStyle() const;
+
void beginLayout();
void endLayout();
void clearLayout();
@@ -153,6 +157,8 @@ public:
bool isValidCursorPosition(int pos) const;
int nextCursorPosition(int oldPos, CursorMode mode = SkipCharacters) const;
int previousCursorPosition(int oldPos, CursorMode mode = SkipCharacters) const;
+ int leftCursorPosition(int oldPos) const;
+ int rightCursorPosition(int oldPos) const;
void draw(QPainter *p, const QPointF &pos, const QVector<FormatRange> &selections = QVector<FormatRange>(),
const QRectF &clip = QRectF()) const;
diff --git a/src/gui/text/text.pri b/src/gui/text/text.pri
index df9398c..83be5b4 100644
--- a/src/gui/text/text.pri
+++ b/src/gui/text/text.pri
@@ -114,6 +114,9 @@ unix:x11 {
OBJECTIVE_SOURCES += \
text/qfontengine_coretext.mm \
text/qfontengine_mac.mm
+ contains(QT_CONFIG, harfbuzz) {
+ DEFINES += QT_ENABLE_HARFBUZZ_FOR_MAC
+ }
}
embedded {
diff --git a/src/gui/widgets/qlinecontrol.cpp b/src/gui/widgets/qlinecontrol.cpp
index 3eac64a..a1b3ff8 100644
--- a/src/gui/widgets/qlinecontrol.cpp
+++ b/src/gui/widgets/qlinecontrol.cpp
@@ -1577,6 +1577,7 @@ void QLineControl::processKeyEvent(QKeyEvent* event)
}
bool unknown = false;
+ bool visual = cursorMoveStyle() == QTextCursor::Visual;
if (false) {
}
@@ -1641,11 +1642,11 @@ void QLineControl::processKeyEvent(QKeyEvent* event)
#endif
moveCursor(selectionEnd(), false);
} else {
- cursorForward(0, layoutDirection() == Qt::LeftToRight ? 1 : -1);
+ cursorForward(0, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
}
}
else if (event == QKeySequence::SelectNextChar) {
- cursorForward(1, layoutDirection() == Qt::LeftToRight ? 1 : -1);
+ cursorForward(1, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
}
else if (event == QKeySequence::MoveToPreviousChar) {
#if !defined(Q_WS_WIN) || defined(QT_NO_COMPLETER)
@@ -1656,11 +1657,11 @@ void QLineControl::processKeyEvent(QKeyEvent* event)
#endif
moveCursor(selectionStart(), false);
} else {
- cursorForward(0, layoutDirection() == Qt::LeftToRight ? -1 : 1);
+ cursorForward(0, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
}
}
else if (event == QKeySequence::SelectPreviousChar) {
- cursorForward(1, layoutDirection() == Qt::LeftToRight ? -1 : 1);
+ cursorForward(1, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
}
else if (event == QKeySequence::MoveToNextWord) {
if (echoMode() == QLineEdit::Normal)
diff --git a/src/gui/widgets/qlinecontrol_p.h b/src/gui/widgets/qlinecontrol_p.h
index 3c505c8..0042f17 100644
--- a/src/gui/widgets/qlinecontrol_p.h
+++ b/src/gui/widgets/qlinecontrol_p.h
@@ -160,6 +160,8 @@ public:
int cursorWidth() const { return m_cursorWidth; }
void setCursorWidth(int value) { m_cursorWidth = value; }
+ QTextCursor::MoveStyle cursorMoveStyle() const { return m_textLayout.cursorMoveStyle(); }
+ void setCursorMoveStyle(QTextCursor::MoveStyle style) { m_textLayout.setCursorMoveStyle(style); }
void moveCursor(int pos, bool mark = false);
void cursorForward(bool mark, int steps)
@@ -167,10 +169,12 @@ public:
int c = m_cursor;
if (steps > 0) {
while (steps--)
- c = m_textLayout.nextCursorPosition(c);
+ c = cursorMoveStyle() == QTextCursor::Visual ? m_textLayout.rightCursorPosition(c)
+ : m_textLayout.nextCursorPosition(c);
} else if (steps < 0) {
while (steps++)
- c = m_textLayout.previousCursorPosition(c);
+ c = cursorMoveStyle() == QTextCursor::Visual ? m_textLayout.leftCursorPosition(c)
+ : m_textLayout.previousCursorPosition(c);
}
moveCursor(c, mark);
}
diff --git a/src/gui/widgets/qlineedit.cpp b/src/gui/widgets/qlineedit.cpp
index 07bd273..43c3f52 100644
--- a/src/gui/widgets/qlineedit.cpp
+++ b/src/gui/widgets/qlineedit.cpp
@@ -1112,6 +1112,34 @@ void QLineEdit::setDragEnabled(bool b)
/*!
+ \property QLineEdit::cursorMoveStyle
+ \brief the movement style of cursor in this line edit
+ \since 4.8
+
+ When this property is set to QTextCursor::Visual, the line edit will use visual
+ movement style. Pressing the left arrow key will always cause the cursor to move
+ left, regardless of the text's writing direction. The same behavior applies to
+ right arrow key.
+
+ When the property is QTextCursor::Logical (the default), within a LTR text block,
+ increase cursor position when pressing left arrow key, decrease cursor position
+ when pressing the right arrow key. If the text block is right to left, the opposite
+ behavior applies.
+*/
+
+QTextCursor::MoveStyle QLineEdit::cursorMoveStyle() const
+{
+ Q_D(const QLineEdit);
+ return d->control->cursorMoveStyle();
+}
+
+void QLineEdit::setCursorMoveStyle(QTextCursor::MoveStyle style)
+{
+ Q_D(QLineEdit);
+ d->control->setCursorMoveStyle(style);
+}
+
+/*!
\property QLineEdit::acceptableInput
\brief whether the input satisfies the inputMask and the
validator.
diff --git a/src/gui/widgets/qlineedit.h b/src/gui/widgets/qlineedit.h
index 636cee7..73a736c 100644
--- a/src/gui/widgets/qlineedit.h
+++ b/src/gui/widgets/qlineedit.h
@@ -43,6 +43,7 @@
#define QLINEEDIT_H
#include <QtGui/qframe.h>
+#include <QtGui/qtextcursor.h>
#include <QtCore/qstring.h>
#include <QtCore/qmargins.h>
@@ -158,6 +159,9 @@ public:
void setDragEnabled(bool b);
bool dragEnabled() const;
+ void setCursorMoveStyle(QTextCursor::MoveStyle style);
+ QTextCursor::MoveStyle cursorMoveStyle() const;
+
QString inputMask() const;
void setInputMask(const QString &inputMask);
bool hasAcceptableInput() const;
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
index 8068aa8..207ab3d 100644
--- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
+++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
@@ -495,6 +495,8 @@ GLuint QGLEngineShaderManager::getUniformLocation(Uniform id)
"fmp",
"fmp2_m_radius2",
"inverse_2_fmp2_m_radius2",
+ "sqrfr",
+ "bradius",
"invertedTextureSize",
"brushTransform",
"brushTexture",
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
index 7cc9dc3..bf2fe42 100644
--- a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
+++ b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
@@ -442,6 +442,8 @@ public:
Fmp,
Fmp2MRadius2,
Inverse2Fmp2MRadius2,
+ SqrFr,
+ BRadius,
InvertedTextureSize,
BrushTransform,
BrushTexture,
diff --git a/src/opengl/gl2paintengineex/qglengineshadersource_p.h b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
index fc8b9ef..9362c58 100644
--- a/src/opengl/gl2paintengineex/qglengineshadersource_p.h
+++ b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
@@ -241,6 +241,7 @@ static const char* const qglslPositionWithRadialGradientBrushVertexShader = "\n\
uniform mediump vec2 halfViewportSize; \n\
uniform highp mat3 brushTransform; \n\
uniform highp vec2 fmp; \n\
+ uniform highp vec3 bradius; \n\
varying highp float b; \n\
varying highp vec2 A; \n\
void setPosition(void) \n\
@@ -253,7 +254,7 @@ static const char* const qglslPositionWithRadialGradientBrushVertexShader = "\n\
mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\
gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\
A = hTexCoords.xy * invertedHTexCoordsZ; \n\
- b = 2.0 * dot(A, fmp); \n\
+ b = bradius.x + 2.0 * dot(A, fmp); \n\
}\n";
static const char* const qglslAffinePositionWithRadialGradientBrushVertexShader
@@ -263,13 +264,22 @@ static const char* const qglslRadialGradientBrushSrcFragmentShader = "\n\
uniform sampler2D brushTexture; \n\
uniform highp float fmp2_m_radius2; \n\
uniform highp float inverse_2_fmp2_m_radius2; \n\
+ uniform highp float sqrfr; \n\
varying highp float b; \n\
varying highp vec2 A; \n\
+ uniform highp vec3 bradius; \n\
lowp vec4 srcPixel() \n\
{ \n\
- highp float c = -dot(A, A); \n\
- highp vec2 val = vec2((-b + sqrt(b*b - 4.0*fmp2_m_radius2*c)) * inverse_2_fmp2_m_radius2, 0.5); \n\
- return texture2D(brushTexture, val); \n\
+ highp float c = sqrfr-dot(A, A); \n\
+ highp float det = b*b - 4.0*fmp2_m_radius2*c; \n\
+ lowp vec4 result = vec4(0.0); \n\
+ if (det >= 0.0) { \n\
+ highp float detSqrt = sqrt(det); \n\
+ highp float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2); \n\
+ if (bradius.y + w * bradius.z >= 0.0) \n\
+ result = texture2D(brushTexture, vec2(w, 0.5)); \n\
+ } \n\
+ return result; \n\
}\n";
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
index 18c684f..a2707e0 100644
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
@@ -301,7 +301,7 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms()
const QRadialGradient *g = static_cast<const QRadialGradient *>(currentBrush.gradient());
QPointF realCenter = g->center();
QPointF realFocal = g->focalPoint();
- qreal realRadius = g->radius();
+ qreal realRadius = g->centerRadius() - g->focalRadius();
translationPoint = realFocal;
QPointF fmp = realCenter - realFocal;
@@ -311,6 +311,12 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms()
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Fmp2MRadius2), fmp2_m_radius2);
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Inverse2Fmp2MRadius2),
GLfloat(1.0 / (2.0*fmp2_m_radius2)));
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::SqrFr),
+ GLfloat(g->focalRadius() * g->focalRadius()));
+ shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::BRadius),
+ GLfloat(2 * (g->centerRadius() - g->focalRadius()) * g->focalRadius()),
+ g->focalRadius(),
+ g->centerRadius() - g->focalRadius());
QVector2D halfViewportSize(width*0.5, height*0.5);
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize);
diff --git a/src/opengl/qpaintengine_opengl.cpp b/src/opengl/qpaintengine_opengl.cpp
index 2c01ac4..efa050d 100644
--- a/src/opengl/qpaintengine_opengl.cpp
+++ b/src/opengl/qpaintengine_opengl.cpp
@@ -2119,6 +2119,7 @@ void QOpenGLPaintEnginePrivate::fillPath(const QPainterPath &path)
updateGLMatrix();
}
+extern bool qt_isExtendedRadialGradient(const QBrush &brush);
static inline bool needsEmulation(Qt::BrushStyle style)
{
@@ -2129,9 +2130,11 @@ static inline bool needsEmulation(Qt::BrushStyle style)
void QOpenGLPaintEnginePrivate::updateUseEmulation()
{
- use_emulation = !use_fragment_programs
- && ((has_pen && needsEmulation(pen_brush_style))
- || (has_brush && needsEmulation(brush_style)));
+ use_emulation = (!use_fragment_programs
+ && ((has_pen && needsEmulation(pen_brush_style))
+ || (has_brush && needsEmulation(brush_style))))
+ || (has_pen && qt_isExtendedRadialGradient(cpen.brush()))
+ || (has_brush && qt_isExtendedRadialGradient(cbrush));
}
void QOpenGLPaintEngine::updatePen(const QPen &pen)
@@ -5442,50 +5445,7 @@ void QOpenGLPaintEngine::transformChanged()
updateMatrix(state()->matrix);
}
-static QPainterPath painterPathFromVectorPath(const QVectorPath &path)
-{
- const qreal *points = path.points();
- const QPainterPath::ElementType *types = path.elements();
-
- QPainterPath p;
- if (types) {
- int id = 0;
- for (int i=0; i<path.elementCount(); ++i) {
- switch(types[i]) {
- case QPainterPath::MoveToElement:
- p.moveTo(QPointF(points[id], points[id+1]));
- id+=2;
- break;
- case QPainterPath::LineToElement:
- p.lineTo(QPointF(points[id], points[id+1]));
- id+=2;
- break;
- case QPainterPath::CurveToElement: {
- QPointF p1(points[id], points[id+1]);
- QPointF p2(points[id+2], points[id+3]);
- QPointF p3(points[id+4], points[id+5]);
- p.cubicTo(p1, p2, p3);
- id+=6;
- break;
- }
- case QPainterPath::CurveToDataElement:
- ;
- break;
- }
- }
- } else {
- p.moveTo(QPointF(points[0], points[1]));
- int id = 2;
- for (int i=1; i<path.elementCount(); ++i) {
- p.lineTo(QPointF(points[id], points[id+1]));
- id+=2;
- }
- }
- if (path.hints() & QVectorPath::WindingFill)
- p.setFillRule(Qt::WindingFill);
-
- return p;
-}
+extern QPainterPath qt_painterPathFromVectorPath(const QVectorPath &path);
void QOpenGLPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
{
@@ -5494,11 +5454,11 @@ void QOpenGLPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
if (brush.style() == Qt::NoBrush)
return;
- if (!d->use_fragment_programs && needsEmulation(brush.style())) {
+ if ((!d->use_fragment_programs && needsEmulation(brush.style())) || qt_isExtendedRadialGradient(brush)) {
QPainter *p = painter();
QBrush oldBrush = p->brush();
p->setBrush(brush);
- qt_draw_helper(p->d_ptr.data(), painterPathFromVectorPath(path), QPainterPrivate::FillDraw);
+ qt_draw_helper(p->d_ptr.data(), qt_painterPathFromVectorPath(path), QPainterPrivate::FillDraw);
p->setBrush(oldBrush);
return;
}
@@ -5515,7 +5475,7 @@ void QOpenGLPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
drawRects(&r, 1);
updatePen(old_pen);
} else {
- d->fillPath(painterPathFromVectorPath(path));
+ d->fillPath(qt_painterPathFromVectorPath(path));
}
updateBrush(old_brush, state()->brushOrigin);
diff --git a/src/openvg/qpaintengine_vg.cpp b/src/openvg/qpaintengine_vg.cpp
index 3c2b5fd..7c9d102 100644
--- a/src/openvg/qpaintengine_vg.cpp
+++ b/src/openvg/qpaintengine_vg.cpp
@@ -173,6 +173,9 @@ public:
bool forcePenChange; // Force a pen change, even if the same.
bool forceBrushChange; // Force a brush change, even if the same.
+ bool hasExtendedRadialGradientPen; // Current pen's brush is extended radial gradient.
+ bool hasExtendedRadialGradientBrush; // Current brush is extended radial gradient.
+
VGPaintType penType; // Type of the last pen that was set.
VGPaintType brushType; // Type of the last brush that was set.
@@ -275,6 +278,27 @@ public:
}
}
+ inline bool needsEmulation(const QBrush &brush) const
+ {
+ extern bool qt_isExtendedRadialGradient(const QBrush &brush);
+ return qt_isExtendedRadialGradient(brush);
+ }
+
+ inline bool needsEmulation() const
+ {
+ return hasExtendedRadialGradientPen || hasExtendedRadialGradientBrush;
+ }
+
+ inline bool needsPenEmulation() const
+ {
+ return hasExtendedRadialGradientPen;
+ }
+
+ inline bool needsBrushEmulation() const
+ {
+ return hasExtendedRadialGradientBrush;
+ }
+
// Set various modes, but only if different.
inline void setImageMode(VGImageMode mode);
inline void setRenderingQuality(VGRenderingQuality mode);
@@ -355,6 +379,10 @@ void QVGPaintEnginePrivate::init()
forcePenChange = true;
forceBrushChange = true;
+
+ hasExtendedRadialGradientPen = false;
+ hasExtendedRadialGradientBrush = false;
+
penType = (VGPaintType)0;
brushType = (VGPaintType)0;
@@ -1534,6 +1562,10 @@ bool QVGPaintEngine::end()
void QVGPaintEngine::draw(const QVectorPath &path)
{
Q_D(QVGPaintEngine);
+ if (d->needsEmulation()) {
+ QPaintEngineEx::draw(path);
+ return;
+ }
QVGPainterState *s = state();
VGPath vgpath = d->vectorPathToVGPath(path);
if (!path.hasWindingFill())
@@ -1543,9 +1575,19 @@ void QVGPaintEngine::draw(const QVectorPath &path)
vgDestroyPath(vgpath);
}
+extern QPainterPath qt_painterPathFromVectorPath(const QVectorPath &path);
+
void QVGPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
{
Q_D(QVGPaintEngine);
+ if (d->needsEmulation(brush)) {
+ QPainter *p = painter();
+ QBrush oldBrush = p->brush();
+ p->setBrush(brush);
+ qt_draw_helper(p->d_ptr.data(), qt_painterPathFromVectorPath(path), QPainterPrivate::FillDraw);
+ p->setBrush(oldBrush);
+ return;
+ }
VGPath vgpath = d->vectorPathToVGPath(path);
if (!path.hasWindingFill())
d->fill(vgpath, brush, VG_EVEN_ODD);
@@ -1557,6 +1599,10 @@ void QVGPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
void QVGPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
{
Q_D(QVGPaintEngine);
+ if (d->needsEmulation(pen.brush())) {
+ QPaintEngineEx::stroke(path, pen);
+ return;
+ }
VGPath vgpath = d->vectorPathToVGPath(path);
d->stroke(vgpath, pen);
vgDestroyPath(vgpath);
@@ -2360,12 +2406,17 @@ void QVGPaintEngine::penChanged()
{
Q_D(QVGPaintEngine);
d->dirty |= QPaintEngine::DirtyPen;
+
+ d->hasExtendedRadialGradientPen =
+ state()->pen.style() != Qt::NoPen && d->needsEmulation(state()->pen.brush());
}
void QVGPaintEngine::brushChanged()
{
Q_D(QVGPaintEngine);
d->dirty |= QPaintEngine::DirtyBrush;
+
+ d->hasExtendedRadialGradientPen = d->needsEmulation(state()->brush);
}
void QVGPaintEngine::brushOriginChanged()
@@ -2544,6 +2595,11 @@ void QVGPaintEngine::fillRect(const QRectF &rect, const QBrush &brush)
return;
}
+ if (d->needsEmulation(brush)) {
+ QPaintEngineEx::fillRect(rect, brush);
+ return;
+ }
+
#if !defined(QVG_NO_MODIFY_PATH)
VGfloat coords[8];
if (d->simpleTransform) {
@@ -2621,6 +2677,10 @@ void QVGPaintEngine::fillRect(const QRectF &rect, const QColor &color)
void QVGPaintEngine::drawRoundedRect(const QRectF &rect, qreal xrad, qreal yrad, Qt::SizeMode mode)
{
Q_D(QVGPaintEngine);
+ if (d->needsEmulation()) {
+ QPaintEngineEx::drawRoundedRect(rect, xrad, yrad, mode);
+ return;
+ }
if (d->simpleTransform) {
QVGPainterState *s = state();
VGPath vgpath = d->roundedRectPath(rect, xrad, yrad, mode);
@@ -2637,6 +2697,10 @@ void QVGPaintEngine::drawRects(const QRect *rects, int rectCount)
{
#if !defined(QVG_NO_MODIFY_PATH)
Q_D(QVGPaintEngine);
+ if (d->needsEmulation()) {
+ QPaintEngineEx::drawRects(rects, rectCount);
+ return;
+ }
QVGPainterState *s = state();
for (int i = 0; i < rectCount; ++i, ++rects) {
VGfloat coords[8];
@@ -2678,6 +2742,10 @@ void QVGPaintEngine::drawRects(const QRectF *rects, int rectCount)
{
#if !defined(QVG_NO_MODIFY_PATH)
Q_D(QVGPaintEngine);
+ if (d->needsEmulation()) {
+ QPaintEngineEx::drawRects(rects, rectCount);
+ return;
+ }
QVGPainterState *s = state();
for (int i = 0; i < rectCount; ++i, ++rects) {
VGfloat coords[8];
@@ -2716,6 +2784,10 @@ void QVGPaintEngine::drawLines(const QLine *lines, int lineCount)
{
#if !defined(QVG_NO_MODIFY_PATH)
Q_D(QVGPaintEngine);
+ if (d->needsEmulation()) {
+ QPaintEngineEx::drawLines(lines, lineCount);
+ return;
+ }
QVGPainterState *s = state();
for (int i = 0; i < lineCount; ++i, ++lines) {
VGfloat coords[4];
@@ -2744,6 +2816,10 @@ void QVGPaintEngine::drawLines(const QLineF *lines, int lineCount)
{
#if !defined(QVG_NO_MODIFY_PATH)
Q_D(QVGPaintEngine);
+ if (d->needsEmulation()) {
+ QPaintEngineEx::drawLines(lines, lineCount);
+ return;
+ }
QVGPainterState *s = state();
for (int i = 0; i < lineCount; ++i, ++lines) {
VGfloat coords[4];
@@ -2773,6 +2849,10 @@ void QVGPaintEngine::drawEllipse(const QRectF &r)
// Based on the description of vguEllipse() in the OpenVG specification.
// We don't use vguEllipse(), to avoid unnecessary library dependencies.
Q_D(QVGPaintEngine);
+ if (d->needsEmulation()) {
+ QPaintEngineEx::drawEllipse(r);
+ return;
+ }
if (d->simpleTransform) {
QVGPainterState *s = state();
VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
@@ -2823,6 +2903,10 @@ void QVGPaintEngine::drawPath(const QPainterPath &path)
// Shortcut past the QPainterPath -> QVectorPath conversion,
// converting the QPainterPath directly into a VGPath.
Q_D(QVGPaintEngine);
+ if (d->needsEmulation()) {
+ QPaintEngineEx::drawPath(path);
+ return;
+ }
QVGPainterState *s = state();
VGPath vgpath = d->painterPathToVGPath(path);
if (path.fillRule() == Qt::OddEvenFill)
@@ -2837,6 +2921,11 @@ void QVGPaintEngine::drawPoints(const QPointF *points, int pointCount)
#if !defined(QVG_NO_MODIFY_PATH)
Q_D(QVGPaintEngine);
+ if (d->needsPenEmulation()) {
+ QPaintEngineEx::drawPoints(points, pointCount);
+ return;
+ }
+
// Set up a new pen if necessary.
QPen pen = state()->pen;
if (pen.style() == Qt::NoPen)
@@ -2871,6 +2960,11 @@ void QVGPaintEngine::drawPoints(const QPoint *points, int pointCount)
#if !defined(QVG_NO_MODIFY_PATH)
Q_D(QVGPaintEngine);
+ if (d->needsEmulation()) {
+ QPaintEngineEx::drawPoints(points, pointCount);
+ return;
+ }
+
// Set up a new pen if necessary.
QPen pen = state()->pen;
if (pen.style() == Qt::NoPen)
@@ -2903,6 +2997,12 @@ void QVGPaintEngine::drawPoints(const QPoint *points, int pointCount)
void QVGPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
{
Q_D(QVGPaintEngine);
+
+ if (d->needsEmulation()) {
+ QPaintEngineEx::drawPolygon(points, pointCount, mode);
+ return;
+ }
+
QVGPainterState *s = state();
VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
VG_PATH_DATATYPE_F,
@@ -2950,6 +3050,12 @@ void QVGPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonD
void QVGPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
{
Q_D(QVGPaintEngine);
+
+ if (d->needsEmulation()) {
+ QPaintEngineEx::drawPolygon(points, pointCount, mode);
+ return;
+ }
+
QVGPainterState *s = state();
VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
VG_PATH_DATATYPE_F,
@@ -3596,6 +3702,11 @@ void QVGPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
return;
}
+ if (d->needsPenEmulation()) {
+ QPaintEngineEx::drawTextItem(p, textItem);
+ return;
+ }
+
// Get the glyphs and positions associated with the text item.
QVarLengthArray<QFixedPoint> positions;
QVarLengthArray<glyph_t> glyphs;
diff --git a/src/qt3support/text/q3richtext.cpp b/src/qt3support/text/q3richtext.cpp
index 668a322..6c77405 100644
--- a/src/qt3support/text/q3richtext.cpp
+++ b/src/qt3support/text/q3richtext.cpp
@@ -63,6 +63,7 @@
#include "qstyleoption.h"
#include "q3stylesheet.h"
#include "qtextstream.h"
+#include <private/qtextdocument_p.h>
#include <private/qtextengine_p.h>
#include <stdlib.h>
diff --git a/tests/arthur/common/paintcommands.cpp b/tests/arthur/common/paintcommands.cpp
index 7a018e3..9273142 100644
--- a/tests/arthur/common/paintcommands.cpp
+++ b/tests/arthur/common/paintcommands.cpp
@@ -346,8 +346,12 @@ void PaintCommands::staticInit()
"gradient_setLinear 1.0 1.0 2.0 2.0");
DECL_PAINTCOMMAND("gradient_setRadial", command_gradient_setRadial,
"^gradient_setRadial\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)\\s?([\\w.]*)\\s?([\\w.]*)$",
- "gradient_setRadial <cx> <cy> <rad> <fx> <fy>\n - C is the center\n - rad is the angle in degrees\n - F is the focal point",
+ "gradient_setRadial <cx> <cy> <rad> <fx> <fy>\n - C is the center\n - rad is the radius\n - F is the focal point",
"gradient_setRadial 1.0 1.0 45.0 2.0 2.0");
+ DECL_PAINTCOMMAND("gradient_setRadialExtended", command_gradient_setRadialExtended,
+ "^gradient_setRadialExtended\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)\\s?([\\w.]*)\\s?([\\w.]*)\\s?([\\w.]*)$",
+ "gradient_setRadialExtended <cx> <cy> <rad> <fx> <fy> <frad>\n - C is the center\n - rad is the center radius\n - F is the focal point\n - frad is the focal radius",
+ "gradient_setRadialExtended 1.0 1.0 45.0 2.0 2.0 45.0");
DECL_PAINTCOMMAND("gradient_setLinearPen", command_gradient_setLinearPen,
"^gradient_setLinearPen\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)\\s+([\\w.]*)$",
"gradient_setLinearPen <x1> <y1> <x2> <y2>",
@@ -2400,7 +2404,7 @@ void PaintCommands::command_gradient_setRadial(QRegExp re)
double fy = convertToDouble(caps.at(5));
if (m_verboseMode)
- printf(" -(lance) gradient_setRadial center=(%.2f, %.2f), radius=%.2f focal=(%.2f, %.2f), "
+ printf(" -(lance) gradient_setRadial center=(%.2f, %.2f), radius=%.2f, focal=(%.2f, %.2f), "
"spread=%d\n",
cx, cy, rad, fx, fy, m_gradientSpread);
@@ -2415,6 +2419,32 @@ void PaintCommands::command_gradient_setRadial(QRegExp re)
}
/***************************************************************************************************/
+void PaintCommands::command_gradient_setRadialExtended(QRegExp re)
+{
+ QStringList caps = re.capturedTexts();
+ double cx = convertToDouble(caps.at(1));
+ double cy = convertToDouble(caps.at(2));
+ double rad = convertToDouble(caps.at(3));
+ double fx = convertToDouble(caps.at(4));
+ double fy = convertToDouble(caps.at(5));
+ double frad = convertToDouble(caps.at(6));
+
+ if (m_verboseMode)
+ printf(" -(lance) gradient_setRadialExtended center=(%.2f, %.2f), radius=%.2f, focal=(%.2f, %.2f), "
+ "focal radius=%.2f, spread=%d\n",
+ cx, cy, rad, fx, fy, frad, m_gradientSpread);
+
+ QRadialGradient rg(QPointF(cx, cy), rad, QPointF(fx, fy), frad);
+ rg.setStops(m_gradientStops);
+ rg.setSpread(m_gradientSpread);
+ rg.setCoordinateMode(m_gradientCoordinate);
+ QBrush brush(rg);
+ QTransform brush_matrix = m_painter->brush().transform();
+ brush.setTransform(brush_matrix);
+ m_painter->setBrush(brush);
+}
+
+/***************************************************************************************************/
void PaintCommands::command_gradient_setConical(QRegExp re)
{
QStringList caps = re.capturedTexts();
diff --git a/tests/arthur/common/paintcommands.h b/tests/arthur/common/paintcommands.h
index 2740412..08c0e25 100644
--- a/tests/arthur/common/paintcommands.h
+++ b/tests/arthur/common/paintcommands.h
@@ -179,6 +179,7 @@ private:
void command_gradient_setConical(QRegExp re);
void command_gradient_setLinear(QRegExp re);
void command_gradient_setRadial(QRegExp re);
+ void command_gradient_setRadialExtended(QRegExp re);
void command_gradient_setLinearPen(QRegExp re);
void command_gradient_setSpread(QRegExp re);
void command_gradient_setCoordinateMode(QRegExp re);
diff --git a/tests/arthur/data/qps/radial_gradients_extended.qps b/tests/arthur/data/qps/radial_gradients_extended.qps
new file mode 100644
index 0000000..d80a149
--- /dev/null
+++ b/tests/arthur/data/qps/radial_gradients_extended.qps
@@ -0,0 +1,95 @@
+path_addRect path 400 0 80 80
+path_addEllipse path 440 40 60 60
+
+setRenderHint Antialiasing
+
+setPen black
+
+begin_block gradients
+gradient_clearStops
+gradient_appendStop 0 red
+gradient_appendStop 0.25 orange
+gradient_appendStop 0.5 yellow
+gradient_appendStop 0.8 green
+gradient_appendStop 1 cyan
+
+gradient_setSpread PadSpread
+gradient_setRadialExtended 0 0 20 40 40 10
+drawRect 0 0 100 100
+
+gradient_setSpread ReflectSpread
+gradient_setRadialExtended 120 20 20 140 40 10
+drawEllipse 100 0 100 100
+
+gradient_setSpread RepeatSpread
+gradient_setRadialExtended 240 20 20 260 40 10
+drawRoundRect 200 0 100 100
+
+gradient_clearStops
+gradient_appendStop 0 3f7f7fff
+gradient_appendStop 0.5 dfdfffff
+gradient_appendStop 1 7f00007f
+
+gradient_setSpread PadSpread
+gradient_setRadialExtended 320 20 20 340 40 10
+drawPolygon [300 0 390 0 350 99]
+
+gradient_setSpread ReflectSpread
+gradient_setRadialExtended 420 20 20 440 40 10
+drawPath path
+
+gradient_setSpread RepeatSpread
+gradient_setRadialExtended 520 20 20 540 40 10
+drawPie 500 0 100 100 720 4320
+end_block
+
+translate 0 100
+scale 1 2
+repeat_block gradients
+
+resetMatrix
+translate 0 300
+brushTranslate 30 0
+brushScale 0.9 0.9
+repeat_block gradients
+
+# Some helpful info perhaps?
+resetMatrix
+setPen black
+
+drawText 610 50 "No XForm"
+drawText 610 200 "scale 1x2"
+drawText 610 300 "brush transform"
+drawText 10 450 "Pad"
+drawText 110 450 "Reflect"
+drawText 210 450 "Repeat"
+drawText 310 450 "Pad w/alpha"
+drawText 410 450 "Reflect w/alpha"
+drawText 510 450 "Repeat w/alpha"
+
+# Radius and focal indicators
+setPen 3f000000
+setBrush nobrush
+
+begin_block ellipse_draw
+setClipRect 0 0 100 100
+drawEllipse -30 -30 100 100
+drawEllipse 35 35 11 11
+translate 100 0
+end_block
+
+repeat_block ellipse_draw
+repeat_block ellipse_draw
+repeat_block ellipse_draw
+repeat_block ellipse_draw
+repeat_block ellipse_draw
+
+resetMatrix
+translate 0 100
+scale 1 2
+repeat_block ellipse_draw
+repeat_block ellipse_draw
+repeat_block ellipse_draw
+repeat_block ellipse_draw
+repeat_block ellipse_draw
+repeat_block ellipse_draw
diff --git a/tests/arthur/data/qps/radial_gradients_extended_qps.png b/tests/arthur/data/qps/radial_gradients_extended_qps.png
new file mode 100644
index 0000000..45a3e60
--- /dev/null
+++ b/tests/arthur/data/qps/radial_gradients_extended_qps.png
Binary files differ
diff --git a/tests/auto/qcomplextext/tst_qcomplextext.cpp b/tests/auto/qcomplextext/tst_qcomplextext.cpp
index 3d8e290..04943c5 100644
--- a/tests/auto/qcomplextext/tst_qcomplextext.cpp
+++ b/tests/auto/qcomplextext/tst_qcomplextext.cpp
@@ -71,6 +71,10 @@ private slots:
void bidiReorderString();
void bidiCursor_qtbug2795();
void bidiCursor_PDF();
+ void bidiCursorMovement_data();
+ void bidiCursorMovement();
+ void bidiCursorLogicalMovement_data();
+ void bidiCursorLogicalMovement();
};
tst_QComplexText::tst_QComplexText()
@@ -185,6 +189,89 @@ void tst_QComplexText::bidiCursor_qtbug2795()
QVERIFY(x1 == x2);
}
+void tst_QComplexText::bidiCursorMovement_data()
+{
+ QTest::addColumn<QString>("logical");
+ QTest::addColumn<int>("basicDir");
+
+ const LV *data = logical_visual;
+ while ( data->name ) {
+ //next we fill it with data
+ QTest::newRow( data->name )
+ << QString::fromUtf8( data->logical )
+ << (int) data->basicDir;
+ data++;
+ }
+}
+
+void tst_QComplexText::bidiCursorMovement()
+{
+ QFETCH(QString, logical);
+ QFETCH(int, basicDir);
+
+ QTextLayout layout(logical);
+
+ QTextOption option = layout.textOption();
+ option.setTextDirection(basicDir == QChar::DirL ? Qt::LeftToRight : Qt::RightToLeft);
+ layout.setTextOption(option);
+ bool moved;
+ int oldPos, newPos = 0;
+ qreal x, newX;
+
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+
+ newX = line.cursorToX(0);
+ do {
+ oldPos = newPos;
+ x = newX;
+ newX = line.cursorToX(oldPos);
+ if (basicDir == QChar::DirL) {
+ QVERIFY(newX >= x);
+ newPos = layout.rightCursorPosition(oldPos);
+ } else
+ {
+ QVERIFY(newX <= x);
+ newPos = layout.leftCursorPosition(oldPos);
+ }
+ moved = (oldPos != newPos);
+ } while (moved);
+}
+
+void tst_QComplexText::bidiCursorLogicalMovement_data()
+{
+ bidiCursorMovement_data();
+}
+
+void tst_QComplexText::bidiCursorLogicalMovement()
+{
+ QFETCH(QString, logical);
+ QFETCH(int, basicDir);
+
+ QTextLayout layout(logical);
+
+ QTextOption option = layout.textOption();
+ option.setTextDirection(basicDir == QChar::DirL ? Qt::LeftToRight : Qt::RightToLeft);
+ layout.setTextOption(option);
+ bool moved;
+ int oldPos, newPos = 0;
+
+ do {
+ oldPos = newPos;
+ newPos = layout.nextCursorPosition(oldPos);
+ QVERIFY(newPos >= oldPos);
+ moved = (oldPos != newPos);
+ } while (moved);
+
+ do {
+ oldPos = newPos;
+ newPos = layout.previousCursorPosition(oldPos);
+ QVERIFY(newPos <= oldPos);
+ moved = (oldPos != newPos);
+ } while (moved);
+}
+
void tst_QComplexText::bidiCursor_PDF()
{
QString str = QString::fromUtf8("\342\200\252hello\342\200\254");
diff --git a/tests/auto/qlineedit/tst_qlineedit.cpp b/tests/auto/qlineedit/tst_qlineedit.cpp
index 9176174..f45481c 100644
--- a/tests/auto/qlineedit/tst_qlineedit.cpp
+++ b/tests/auto/qlineedit/tst_qlineedit.cpp
@@ -282,6 +282,12 @@ private slots:
void validateAndSet();
#endif
+ void bidiVisualMovement_data();
+ void bidiVisualMovement();
+
+ void bidiLogicalMovement_data();
+ void bidiLogicalMovement();
+
protected slots:
#ifdef QT3_SUPPORT
void lostFocus();
@@ -3760,5 +3766,135 @@ void tst_QLineEdit::QTBUG13520_textNotVisible()
}
+void tst_QLineEdit::bidiVisualMovement_data()
+{
+ QTest::addColumn<QString>("logical");
+ QTest::addColumn<int>("basicDir");
+ QTest::addColumn<IntList>("positionList");
+
+ QTest::newRow("Latin text")
+ << QString::fromUtf8("abc")
+ << (int) QChar::DirL
+ << (IntList() << 0 << 1 << 2 << 3);
+ QTest::newRow("Hebrew text, one item")
+ << QString::fromUtf8("\327\220\327\221\327\222")
+ << (int) QChar::DirR
+ << (QList<int>() << 0 << 1 << 2 << 3);
+ QTest::newRow("Hebrew text after Latin text")
+ << QString::fromUtf8("abc\327\220\327\221\327\222")
+ << (int) QChar::DirL
+ << (QList<int>() << 0 << 1 << 2 << 6 << 5 << 4 << 3);
+ QTest::newRow("Latin text after Hebrew text")
+ << QString::fromUtf8("\327\220\327\221\327\222abc")
+ << (int) QChar::DirR
+ << (QList<int>() << 0 << 1 << 2 << 6 << 5 << 4 << 3);
+ QTest::newRow("LTR, 3 items")
+ << QString::fromUtf8("abc\327\220\327\221\327\222abc")
+ << (int) QChar::DirL
+ << (QList<int>() << 0 << 1 << 2 << 5 << 4 << 3 << 6 << 7 << 8 << 9);
+ QTest::newRow("RTL, 3 items")
+ << QString::fromUtf8("\327\220\327\221\327\222abc\327\220\327\221\327\222")
+ << (int) QChar::DirR
+ << (QList<int>() << 0 << 1 << 2 << 5 << 4 << 3 << 6 << 7 << 8 << 9);
+ QTest::newRow("LTR, 4 items")
+ << QString::fromUtf8("abc\327\220\327\221\327\222abc\327\220\327\221\327\222")
+ << (int) QChar::DirL
+ << (QList<int>() << 0 << 1 << 2 << 5 << 4 << 3 << 6 << 7 << 8 << 12 << 11 << 10 << 9);
+ QTest::newRow("RTL, 4 items")
+ << QString::fromUtf8("\327\220\327\221\327\222abc\327\220\327\221\327\222abc")
+ << (int) QChar::DirR
+ << (QList<int>() << 0 << 1 << 2 << 5 << 4 << 3 << 6 << 7 << 8 << 12 << 11 << 10 << 9);
+}
+
+void tst_QLineEdit::bidiVisualMovement()
+{
+ QFETCH(QString, logical);
+ QFETCH(int, basicDir);
+ QFETCH(IntList, positionList);
+
+ QLineEdit le;
+ le.setText(logical);
+
+ le.setCursorMoveStyle(QTextCursor::Visual);
+ le.setCursorPosition(0);
+
+ bool moved;
+ int i = 0, oldPos, newPos = 0;
+
+ do {
+ oldPos = newPos;
+ QVERIFY(oldPos == positionList[i]);
+ if (basicDir == QChar::DirL) {
+ QTest::keyClick(&le, Qt::Key_Right);
+ } else
+ QTest::keyClick(&le, Qt::Key_Left);
+ newPos = le.cursorPosition();
+ moved = (oldPos != newPos);
+ i++;
+ } while (moved);
+
+ QVERIFY(i == positionList.size());
+
+ do {
+ i--;
+ oldPos = newPos;
+ QVERIFY(oldPos == positionList[i]);
+ if (basicDir == QChar::DirL) {
+ QTest::keyClick(&le, Qt::Key_Left);
+ } else
+ {
+ QTest::keyClick(&le, Qt::Key_Right);
+ }
+ newPos = le.cursorPosition();
+ moved = (oldPos != newPos);
+ } while (moved && i >= 0);
+}
+
+void tst_QLineEdit::bidiLogicalMovement_data()
+{
+ bidiVisualMovement_data();
+}
+
+void tst_QLineEdit::bidiLogicalMovement()
+{
+ QFETCH(QString, logical);
+ QFETCH(int, basicDir);
+
+ QLineEdit le;
+ le.setText(logical);
+
+ le.setCursorMoveStyle(QTextCursor::Logical);
+ le.setCursorPosition(0);
+
+ bool moved;
+ int i = 0, oldPos, newPos = 0;
+
+ do {
+ oldPos = newPos;
+ QVERIFY(oldPos == i);
+ if (basicDir == QChar::DirL) {
+ QTest::keyClick(&le, Qt::Key_Right);
+ } else
+ QTest::keyClick(&le, Qt::Key_Left);
+ newPos = le.cursorPosition();
+ moved = (oldPos != newPos);
+ i++;
+ } while (moved);
+
+ do {
+ i--;
+ oldPos = newPos;
+ QVERIFY(oldPos == i);
+ if (basicDir == QChar::DirL) {
+ QTest::keyClick(&le, Qt::Key_Left);
+ } else
+ {
+ QTest::keyClick(&le, Qt::Key_Right);
+ }
+ newPos = le.cursorPosition();
+ moved = (oldPos != newPos);
+ } while (moved && i >= 0);
+}
+
QTEST_MAIN(tst_QLineEdit)
#include "tst_qlineedit.moc"
diff --git a/tests/auto/qpainter/tst_qpainter.cpp b/tests/auto/qpainter/tst_qpainter.cpp
index c21514b..76bc5d63 100644
--- a/tests/auto/qpainter/tst_qpainter.cpp
+++ b/tests/auto/qpainter/tst_qpainter.cpp
@@ -77,6 +77,7 @@
# define SRCDIR "."
#endif
+Q_DECLARE_METATYPE(QGradientStops)
Q_DECLARE_METATYPE(QLine)
Q_DECLARE_METATYPE(QRect)
Q_DECLARE_METATYPE(QSize)
@@ -189,6 +190,7 @@ private slots:
void fillRect_stretchToDeviceMode();
void monoImages();
+ void linearGradientSymmetry_data();
void linearGradientSymmetry();
void gradientInterpolation();
@@ -3983,8 +3985,42 @@ static QLinearGradient inverseGradient(QLinearGradient g)
return g2;
}
+void tst_QPainter::linearGradientSymmetry_data()
+{
+ QTest::addColumn<QGradientStops>("stops");
+
+ if (sizeof(qreal) != sizeof(float)) {
+ QGradientStops stops;
+ stops << qMakePair(qreal(0.0), QColor(Qt::blue));
+ stops << qMakePair(qreal(0.2), QColor(220, 220, 220, 0));
+ stops << qMakePair(qreal(0.6), QColor(Qt::red));
+ stops << qMakePair(qreal(0.9), QColor(220, 220, 220, 255));
+ stops << qMakePair(qreal(1.0), QColor(Qt::black));
+ QTest::newRow("multiple stops") << stops;
+ }
+
+ {
+ QGradientStops stops;
+ stops << qMakePair(qreal(0.0), QColor(Qt::blue));
+ stops << qMakePair(qreal(1.0), QColor(Qt::black));
+ QTest::newRow("two stops") << stops;
+ }
+
+ if (sizeof(qreal) != sizeof(float)) {
+ QGradientStops stops;
+ stops << qMakePair(qreal(0.3), QColor(Qt::blue));
+ stops << qMakePair(qreal(0.6), QColor(Qt::black));
+ QTest::newRow("two stops 2") << stops;
+ }
+}
+
void tst_QPainter::linearGradientSymmetry()
{
+#ifdef Q_WS_QWS
+ QSKIP("QWS has limited resolution in the gradient color table", SkipAll);
+#else
+ QFETCH(QGradientStops, stops);
+
QImage a(64, 8, QImage::Format_ARGB32_Premultiplied);
QImage b(64, 8, QImage::Format_ARGB32_Premultiplied);
@@ -3992,11 +4028,7 @@ void tst_QPainter::linearGradientSymmetry()
b.fill(0);
QLinearGradient gradient(QRectF(b.rect()).topLeft(), QRectF(b.rect()).topRight());
- gradient.setColorAt(0.0, Qt::blue);
- gradient.setColorAt(0.2, QColor(220, 220, 220, 0));
- gradient.setColorAt(0.6, Qt::red);
- gradient.setColorAt(0.9, QColor(220, 220, 220, 255));
- gradient.setColorAt(1.0, Qt::black);
+ gradient.setStops(stops);
QPainter pa(&a);
pa.fillRect(a.rect(), gradient);
@@ -4008,6 +4040,7 @@ void tst_QPainter::linearGradientSymmetry()
b = b.mirrored(true);
QCOMPARE(a, b);
+#endif
}
void tst_QPainter::gradientInterpolation()
diff --git a/tests/auto/qpixmap/tst_qpixmap.cpp b/tests/auto/qpixmap/tst_qpixmap.cpp
index 4d032e8..4ba51de 100644
--- a/tests/auto/qpixmap/tst_qpixmap.cpp
+++ b/tests/auto/qpixmap/tst_qpixmap.cpp
@@ -1337,7 +1337,7 @@ void tst_QPixmap::toSymbianCFbsBitmap()
void tst_QPixmap::onlyNullPixmapsOutsideGuiThread()
{
-#if !defined(Q_WS_WIN)
+#if !defined(Q_WS_WIN) && !defined(Q_WS_MAC)
class Thread : public QThread
{
public:
@@ -1370,7 +1370,7 @@ void tst_QPixmap::onlyNullPixmapsOutsideGuiThread()
thread.wait();
#endif
-#endif // !defined(Q_WS_WIN)
+#endif // !defined(Q_WS_WIN) && !defined(Q_WS_MAC)
}
void tst_QPixmap::refUnref()
diff --git a/tests/auto/qtextedit/tst_qtextedit.cpp b/tests/auto/qtextedit/tst_qtextedit.cpp
index 9ca17b9..5a64593 100644
--- a/tests/auto/qtextedit/tst_qtextedit.cpp
+++ b/tests/auto/qtextedit/tst_qtextedit.cpp
@@ -42,7 +42,6 @@
#include <QtTest/QtTest>
-
#include <qtextedit.h>
#include <qtextcursor.h>
#include <qtextlist.h>
@@ -69,6 +68,7 @@ typedef QList<keyPairType> pairListType;
Q_DECLARE_METATYPE(pairListType);
Q_DECLARE_METATYPE(keyPairType);
Q_DECLARE_METATYPE(QList<bool>);
+Q_DECLARE_METATYPE(QList<int>);
#ifdef Q_WS_MAC
#include <Carbon/Carbon.h>
@@ -205,6 +205,11 @@ private slots:
#ifndef QT_NO_CONTEXTMENU
void taskQTBUG_7902_contextMenuCrash();
#endif
+ void bidiVisualMovement_data();
+ void bidiVisualMovement();
+
+ void bidiLogicalMovement_data();
+ void bidiLogicalMovement();
private:
void createSelection();
@@ -2235,5 +2240,147 @@ void tst_QTextEdit::taskQTBUG_7902_contextMenuCrash()
}
#endif
+void tst_QTextEdit::bidiVisualMovement_data()
+{
+ QTest::addColumn<QString>("logical");
+ QTest::addColumn<int>("basicDir");
+ QTest::addColumn<QList<int> >("positionList");
+
+ QTest::newRow("Latin text")
+ << QString::fromUtf8("abc")
+ << (int) QChar::DirL
+ << (QList<int>() << 0 << 1 << 2 << 3);
+ QTest::newRow("Hebrew text, one item")
+ << QString::fromUtf8("\327\220\327\221\327\222")
+ << (int) QChar::DirR
+ << (QList<int>() << 0 << 1 << 2 << 3);
+ QTest::newRow("Hebrew text after Latin text")
+ << QString::fromUtf8("abc\327\220\327\221\327\222")
+ << (int) QChar::DirL
+ << (QList<int>() << 0 << 1 << 2 << 6 << 5 << 4 << 3);
+ QTest::newRow("Latin text after Hebrew text")
+ << QString::fromUtf8("\327\220\327\221\327\222abc")
+ << (int) QChar::DirR
+ << (QList<int>() << 0 << 1 << 2 << 6 << 5 << 4 << 3);
+ QTest::newRow("LTR, 3 items")
+ << QString::fromUtf8("abc\327\220\327\221\327\222abc")
+ << (int) QChar::DirL
+ << (QList<int>() << 0 << 1 << 2 << 5 << 4 << 3 << 6 << 7 << 8 << 9);
+ QTest::newRow("RTL, 3 items")
+ << QString::fromUtf8("\327\220\327\221\327\222abc\327\220\327\221\327\222")
+ << (int) QChar::DirR
+ << (QList<int>() << 0 << 1 << 2 << 5 << 4 << 3 << 6 << 7 << 8 << 9);
+ QTest::newRow("LTR, 4 items")
+ << QString::fromUtf8("abc\327\220\327\221\327\222abc\327\220\327\221\327\222")
+ << (int) QChar::DirL
+ << (QList<int>() << 0 << 1 << 2 << 5 << 4 << 3 << 6 << 7 << 8 << 12 << 11 << 10 << 9);
+ QTest::newRow("RTL, 4 items")
+ << QString::fromUtf8("\327\220\327\221\327\222abc\327\220\327\221\327\222abc")
+ << (int) QChar::DirR
+ << (QList<int>() << 0 << 1 << 2 << 5 << 4 << 3 << 6 << 7 << 8 << 12 << 11 << 10 << 9);
+}
+
+void tst_QTextEdit::bidiVisualMovement()
+{
+ QFETCH(QString, logical);
+ QFETCH(int, basicDir);
+ QFETCH(QList<int>, positionList);
+
+ ed->setText(logical);
+
+ QTextOption option = ed->document()->defaultTextOption();
+ option.setTextDirection(basicDir == QChar::DirL ? Qt::LeftToRight : Qt::RightToLeft);
+ ed->document()->setDefaultTextOption(option);
+
+ ed->document()->setDefaultCursorMoveStyle(QTextCursor::Visual);
+ ed->moveCursor(QTextCursor::Start);
+ ed->show();
+
+ bool moved;
+ int i = 0, oldPos, newPos = 0;
+
+ do {
+ oldPos = newPos;
+ QVERIFY(oldPos == positionList[i]);
+ if (basicDir == QChar::DirL) {
+ ed->moveCursor(QTextCursor::Right);
+ } else
+ {
+ ed->moveCursor(QTextCursor::Left);
+ }
+ newPos = ed->textCursor().position();
+ moved = (oldPos != newPos);
+ i++;
+ } while (moved);
+
+ QVERIFY(i == positionList.size());
+
+ do {
+ i--;
+ oldPos = newPos;
+ QVERIFY(oldPos == positionList[i]);
+ if (basicDir == QChar::DirL) {
+ ed->moveCursor(QTextCursor::Left);
+ } else
+ {
+ ed->moveCursor(QTextCursor::Right);
+ }
+ newPos = ed->textCursor().position();
+ moved = (oldPos != newPos);
+ } while (moved && i >= 0);
+}
+
+void tst_QTextEdit::bidiLogicalMovement_data()
+{
+ bidiVisualMovement_data();
+}
+
+void tst_QTextEdit::bidiLogicalMovement()
+{
+ QFETCH(QString, logical);
+ QFETCH(int, basicDir);
+
+ ed->setText(logical);
+
+ QTextOption option = ed->document()->defaultTextOption();
+ option.setTextDirection(basicDir == QChar::DirL ? Qt::LeftToRight : Qt::RightToLeft);
+ ed->document()->setDefaultTextOption(option);
+
+ ed->document()->setDefaultCursorMoveStyle(QTextCursor::Logical);
+ ed->moveCursor(QTextCursor::Start);
+ ed->show();
+
+ bool moved;
+ int i = 0, oldPos, newPos = 0;
+
+ do {
+ oldPos = newPos;
+ QVERIFY(oldPos == i);
+ if (basicDir == QChar::DirL) {
+ ed->moveCursor(QTextCursor::Right);
+ } else
+ {
+ ed->moveCursor(QTextCursor::Left);
+ }
+ newPos = ed->textCursor().position();
+ moved = (oldPos != newPos);
+ i++;
+ } while (moved);
+
+ do {
+ i--;
+ oldPos = newPos;
+ QVERIFY(oldPos == i);
+ if (basicDir == QChar::DirL) {
+ ed->moveCursor(QTextCursor::Left);
+ } else
+ {
+ ed->moveCursor(QTextCursor::Right);
+ }
+ newPos = ed->textCursor().position();
+ moved = (oldPos != newPos);
+ } while (moved && i >= 0);
+}
+
QTEST_MAIN(tst_QTextEdit)
#include "tst_qtextedit.moc"
diff --git a/tests/auto/qwizard/tst_qwizard.cpp b/tests/auto/qwizard/tst_qwizard.cpp
index a813727..5667d40 100644
--- a/tests/auto/qwizard/tst_qwizard.cpp
+++ b/tests/auto/qwizard/tst_qwizard.cpp
@@ -1770,8 +1770,11 @@ public:
~TestWizard()
{
- foreach (int id, pageIds)
- delete page(id);
+ foreach (int id, pageIds) {
+ QWizardPage *page_to_delete = page(id);
+ removePage(id);
+ delete page_to_delete;
+ }
}
void applyOperations(const QList<Operation *> &operations)
@@ -2548,8 +2551,8 @@ void tst_QWizard::task177022_setFixedSize()
QWizard wiz;
QWizardPage page1;
QWizardPage page2;
- wiz.addPage(&page1);
- wiz.addPage(&page2);
+ int page1_id = wiz.addPage(&page1);
+ int page2_id = wiz.addPage(&page2);
wiz.setFixedSize(width, height);
if (wiz.wizardStyle() == QWizard::AeroStyle)
QEXPECT_FAIL("", "this probably relates to non-client area hack for AeroStyle titlebar "
@@ -2576,6 +2579,8 @@ void tst_QWizard::task177022_setFixedSize()
QCOMPARE(wiz.maximumWidth(), width);
QCOMPARE(wiz.maximumHeight(), height);
+ wiz.removePage(page1_id);
+ wiz.removePage(page2_id);
}
void tst_QWizard::task248107_backButton()