summaryrefslogtreecommitdiffstats
path: root/src/gui/painting
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/painting')
-rw-r--r--src/gui/painting/painting.pri15
-rw-r--r--src/gui/painting/qbackingstore.cpp3
-rw-r--r--src/gui/painting/qbackingstore.h1
-rw-r--r--src/gui/painting/qbezier_p.h1
-rw-r--r--src/gui/painting/qblendfunctions_p.h1
-rw-r--r--src/gui/painting/qblittable_p.h1
-rw-r--r--src/gui/painting/qbrush.cpp59
-rw-r--r--src/gui/painting/qbrush.h1
-rw-r--r--src/gui/painting/qcolor.cpp367
-rw-r--r--src/gui/painting/qcolor.h15
-rw-r--r--src/gui/painting/qcolor_p.cpp381
-rw-r--r--src/gui/painting/qcolor_p.h8
-rw-r--r--src/gui/painting/qcompositionfunctions.cpp104
-rw-r--r--src/gui/painting/qcoregraphics.mm565
-rw-r--r--src/gui/painting/qcoregraphics_p.h121
-rw-r--r--src/gui/painting/qcosmeticstroker_p.h1
-rw-r--r--src/gui/painting/qcssutil.cpp2
-rw-r--r--src/gui/painting/qdatabuffer_p.h1
-rw-r--r--src/gui/painting/qdrawhelper.cpp1420
-rw-r--r--src/gui/painting/qdrawhelper_avx2.cpp309
-rw-r--r--src/gui/painting/qdrawhelper_p.h28
-rw-r--r--src/gui/painting/qdrawhelper_sse4.cpp16
-rw-r--r--src/gui/painting/qdrawhelper_ssse3.cpp9
-rw-r--r--src/gui/painting/qdrawhelper_x86_p.h1
-rw-r--r--src/gui/painting/qdrawingprimitive_sse2_p.h25
-rw-r--r--src/gui/painting/qemulationpaintengine_p.h1
-rw-r--r--src/gui/painting/qfixed_p.h1
-rw-r--r--src/gui/painting/qmatrix.h1
-rw-r--r--src/gui/painting/qmemrotate_p.h1
-rw-r--r--src/gui/painting/qoutlinemapper_p.h1
-rw-r--r--src/gui/painting/qpagedpaintdevice.h1
-rw-r--r--src/gui/painting/qpagedpaintdevice_p.h1
-rw-r--r--src/gui/painting/qpagelayout.h1
-rw-r--r--src/gui/painting/qpagesize.cpp2
-rw-r--r--src/gui/painting/qpagesize.h1
-rw-r--r--src/gui/painting/qpaintdevice.h1
-rw-r--r--src/gui/painting/qpaintengine.h1
-rw-r--r--src/gui/painting/qpaintengine_blitter.cpp18
-rw-r--r--src/gui/painting/qpaintengine_blitter_p.h1
-rw-r--r--src/gui/painting/qpaintengine_p.h6
-rw-r--r--src/gui/painting/qpaintengine_raster_p.h1
-rw-r--r--src/gui/painting/qpaintengineex_p.h1
-rw-r--r--src/gui/painting/qpainter.cpp10
-rw-r--r--src/gui/painting/qpainter.h1
-rw-r--r--src/gui/painting/qpainter_p.h1
-rw-r--r--src/gui/painting/qpainterpath.cpp7
-rw-r--r--src/gui/painting/qpainterpath.h1
-rw-r--r--src/gui/painting/qpainterpath_p.h1
-rw-r--r--src/gui/painting/qpathclipper.cpp4
-rw-r--r--src/gui/painting/qpathclipper_p.h1
-rw-r--r--src/gui/painting/qpathsimplifier_p.h1
-rw-r--r--src/gui/painting/qpdf.cpp9
-rw-r--r--src/gui/painting/qpdf_p.h2
-rw-r--r--src/gui/painting/qpdfwriter.h2
-rw-r--r--src/gui/painting/qpen.h1
-rw-r--r--src/gui/painting/qplatformbackingstore.cpp38
-rw-r--r--src/gui/painting/qplatformbackingstore.h7
-rw-r--r--src/gui/painting/qpolygon.cpp15
-rw-r--r--src/gui/painting/qpolygon.h1
-rw-r--r--src/gui/painting/qpolygonclipper_p.h1
-rw-r--r--src/gui/painting/qrasterizer_p.h2
-rw-r--r--src/gui/painting/qrbtree_p.h571
-rw-r--r--src/gui/painting/qregion.cpp160
-rw-r--r--src/gui/painting/qregion.h18
-rw-r--r--src/gui/painting/qrgb.h2
-rw-r--r--src/gui/painting/qrgba64.h2
-rw-r--r--src/gui/painting/qrgba64_p.h101
-rw-r--r--src/gui/painting/qstroker_p.h1
-rw-r--r--src/gui/painting/qtextureglyphcache.cpp3
-rw-r--r--src/gui/painting/qtextureglyphcache_p.h3
-rw-r--r--src/gui/painting/qtransform.cpp12
-rw-r--r--src/gui/painting/qtransform.h1
-rw-r--r--src/gui/painting/qtriangulatingstroker.cpp613
-rw-r--r--src/gui/painting/qtriangulatingstroker_p.h171
-rw-r--r--src/gui/painting/qtriangulator.cpp2382
-rw-r--r--src/gui/painting/qtriangulator_p.h148
-rw-r--r--src/gui/painting/qvectorpath_p.h1
77 files changed, 6308 insertions, 1481 deletions
diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri
index 283b6643b9..a61865a0b6 100644
--- a/src/gui/painting/painting.pri
+++ b/src/gui/painting/painting.pri
@@ -42,6 +42,7 @@ HEADERS += \
painting/qpolygonclipper_p.h \
painting/qrasterdefs_p.h \
painting/qrasterizer_p.h \
+ painting/qrbtree_p.h \
painting/qregion.h \
painting/qrgb.h \
painting/qrgba64.h \
@@ -49,6 +50,8 @@ HEADERS += \
painting/qstroker_p.h \
painting/qtextureglyphcache_p.h \
painting/qtransform.h \
+ painting/qtriangulatingstroker_p.h \
+ painting/qtriangulator_p.h \
painting/qplatformbackingstore.h \
painting/qpathsimplifier_p.h
@@ -60,7 +63,6 @@ SOURCES += \
painting/qblittable.cpp \
painting/qbrush.cpp \
painting/qcolor.cpp \
- painting/qcolor_p.cpp \
painting/qcompositionfunctions.cpp \
painting/qcosmeticstroker.cpp \
painting/qcssutil.cpp \
@@ -92,9 +94,16 @@ SOURCES += \
painting/qstroker.cpp \
painting/qtextureglyphcache.cpp \
painting/qtransform.cpp \
+ painting/qtriangulatingstroker.cpp \
+ painting/qtriangulator.cpp \
painting/qplatformbackingstore.cpp \
painting/qpathsimplifier.cpp
+darwin {
+ HEADERS += painting/qcoregraphics_p.h
+ SOURCES += painting/qcoregraphics.mm
+}
+
SSE2_SOURCES += painting/qdrawhelper_sse2.cpp
SSSE3_SOURCES += painting/qdrawhelper_ssse3.cpp
SSE4_1_SOURCES += painting/qdrawhelper_sse4.cpp \
@@ -104,8 +113,8 @@ AVX2_SOURCES += painting/qdrawhelper_avx2.cpp
NEON_SOURCES += painting/qdrawhelper_neon.cpp painting/qimagescale_neon.cpp
NEON_HEADERS += painting/qdrawhelper_neon_p.h
NEON_ASM += ../3rdparty/pixman/pixman-arm-neon-asm.S painting/qdrawhelper_neon_asm.S
-!ios:contains(QT_ARCH, "arm"): CONFIG+=no_clang_integrated_as
-!ios:!contains(QT_ARCH, "arm64"): DEFINES += ENABLE_PIXMAN_DRAWHELPERS
+!uikit:contains(QT_ARCH, "arm"): CONFIG += no_clang_integrated_as
+!uikit:!contains(QT_ARCH, "arm64"): DEFINES += ENABLE_PIXMAN_DRAWHELPERS
MIPS_DSP_SOURCES += painting/qdrawhelper_mips_dsp.cpp
MIPS_DSP_HEADERS += painting/qdrawhelper_mips_dsp_p.h painting/qt_mips_asm_dsp_p.h
diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp
index e3d18512dd..53349dcef4 100644
--- a/src/gui/painting/qbackingstore.cpp
+++ b/src/gui/painting/qbackingstore.cpp
@@ -112,6 +112,8 @@ void QBackingStore::flush(const QRegion &region, QWindow *win, const QPoint &off
}
#endif
+ Q_ASSERT(win == this->window() || this->window()->isAncestorOf(win, QWindow::ExcludeTransients));
+
d_ptr->platformBackingStore->flush(win, QHighDpi::toNativeLocalRegion(region, win),
QHighDpi::toNativeLocalPosition(offset, win));
}
@@ -138,6 +140,7 @@ QBackingStore::QBackingStore(QWindow *window)
: d_ptr(new QBackingStorePrivate(window))
{
d_ptr->platformBackingStore = QGuiApplicationPrivate::platformIntegration()->createPlatformBackingStore(window);
+ d_ptr->platformBackingStore->setBackingStore(this);
}
/*!
diff --git a/src/gui/painting/qbackingstore.h b/src/gui/painting/qbackingstore.h
index 886cf29f0e..2ba6e1c906 100644
--- a/src/gui/painting/qbackingstore.h
+++ b/src/gui/painting/qbackingstore.h
@@ -40,6 +40,7 @@
#ifndef QBACKINGSTORE_H
#define QBACKINGSTORE_H
+#include <QtGui/qtguiglobal.h>
#include <QtCore/qrect.h>
#include <QtGui/qwindow.h>
diff --git a/src/gui/painting/qbezier_p.h b/src/gui/painting/qbezier_p.h
index c4e14a18cb..f8a91e9ef3 100644
--- a/src/gui/painting/qbezier_p.h
+++ b/src/gui/painting/qbezier_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include "QtCore/qpoint.h"
#include "QtCore/qline.h"
#include "QtCore/qrect.h"
diff --git a/src/gui/painting/qblendfunctions_p.h b/src/gui/painting/qblendfunctions_p.h
index d21765bcde..7ee04987fe 100644
--- a/src/gui/painting/qblendfunctions_p.h
+++ b/src/gui/painting/qblendfunctions_p.h
@@ -40,6 +40,7 @@
#ifndef QBLENDFUNCTIONS_P_H
#define QBLENDFUNCTIONS_P_H
+#include <QtGui/private/qtguiglobal_p.h>
#include <qmath.h>
#include "qdrawhelper_p.h"
diff --git a/src/gui/painting/qblittable_p.h b/src/gui/painting/qblittable_p.h
index 8df56f6d0c..24440c3c61 100644
--- a/src/gui/painting/qblittable_p.h
+++ b/src/gui/painting/qblittable_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include <QtCore/qsize.h>
#include <QtGui/private/qpixmap_blitter_p.h>
diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp
index 0d26457c15..ebb035a2c1 100644
--- a/src/gui/painting/qbrush.cpp
+++ b/src/gui/painting/qbrush.cpp
@@ -458,13 +458,8 @@ QBrush::QBrush(const QImage &image)
*/
QBrush::QBrush(Qt::BrushStyle style)
+ : QBrush(QColor(Qt::black), style)
{
- if (qbrush_check_type(style))
- init(Qt::black, style);
- else {
- d.reset(nullBrushInstance());
- d->ref.ref();
- }
}
/*!
@@ -491,13 +486,8 @@ QBrush::QBrush(const QColor &color, Qt::BrushStyle style)
\sa setColor(), setStyle()
*/
QBrush::QBrush(Qt::GlobalColor color, Qt::BrushStyle style)
+ : QBrush(QColor(color), style)
{
- if (qbrush_check_type(style))
- init(color, style);
- else {
- d.reset(nullBrushInstance());
- d->ref.ref();
- }
}
/*!
@@ -579,11 +569,20 @@ void QBrush::cleanUp(QBrushData *x)
QBrushDataPointerDeleter::deleteData(x);
}
+static Q_DECL_CONSTEXPR inline bool use_same_brushdata(Qt::BrushStyle lhs, Qt::BrushStyle rhs)
+{
+ return lhs == rhs // includes Qt::TexturePattern
+ || (lhs >= Qt::NoBrush && lhs <= Qt::DiagCrossPattern && rhs >= Qt::NoBrush && rhs <= Qt::DiagCrossPattern)
+ || (lhs >= Qt::LinearGradientPattern && lhs <= Qt::ConicalGradientPattern && rhs >= Qt::LinearGradientPattern && rhs <= Qt::ConicalGradientPattern)
+ ;
+}
void QBrush::detach(Qt::BrushStyle newStyle)
{
- if (newStyle == d->style && d->ref.load() == 1)
+ if (use_same_brushdata(newStyle, d->style) && d->ref.load() == 1) {
+ d->style = newStyle;
return;
+ }
QScopedPointer<QBrushData, QBrushDataPointerDeleter> x;
switch(newStyle) {
@@ -1675,13 +1674,8 @@ QLinearGradient::QLinearGradient(const QPointF &start, const QPointF &finalStop)
\sa QGradient::setColorAt(), QGradient::setStops()
*/
QLinearGradient::QLinearGradient(qreal xStart, qreal yStart, qreal xFinalStop, qreal yFinalStop)
+ : QLinearGradient(QPointF(xStart, yStart), QPointF(xFinalStop, yFinalStop))
{
- m_type = LinearGradient;
- m_spread = PadSpread;
- m_data.linear.x1 = xStart;
- m_data.linear.y1 = yStart;
- m_data.linear.x2 = xFinalStop;
- m_data.linear.y2 = yFinalStop;
}
@@ -1884,19 +1878,8 @@ QRadialGradient::QRadialGradient(const QPointF &center, qreal radius)
*/
QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal radius, qreal fx, qreal fy)
+ : QRadialGradient(QPointF(cx, cy), radius, QPointF(fx, fy))
{
- m_type = RadialGradient;
- m_spread = PadSpread;
- m_data.radial.cx = cx;
- m_data.radial.cy = cy;
- m_data.radial.cradius = radius;
-
- QPointF adapted_focal = qt_radial_gradient_adapt_focal_point(QPointF(cx, cy),
- radius,
- QPointF(fx, fy));
-
- m_data.radial.fx = adapted_focal.x();
- m_data.radial.fy = adapted_focal.y();
}
/*!
@@ -1906,14 +1889,8 @@ QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal radius, qreal fx, qre
\sa QGradient::setColorAt(), QGradient::setStops()
*/
QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal radius)
+ : QRadialGradient(QPointF(cx, cy), radius)
{
- m_type = RadialGradient;
- m_spread = PadSpread;
- m_data.radial.cx = cx;
- m_data.radial.cy = cy;
- m_data.radial.cradius = radius;
- m_data.radial.fx = cx;
- m_data.radial.fy = cy;
}
@@ -2213,12 +2190,8 @@ QConicalGradient::QConicalGradient(const QPointF &center, qreal angle)
*/
QConicalGradient::QConicalGradient(qreal cx, qreal cy, qreal angle)
+ : QConicalGradient(QPointF(cx, cy), angle)
{
- m_type = ConicalGradient;
- m_spread = PadSpread;
- m_data.conical.cx = cx;
- m_data.conical.cy = cy;
- m_data.conical.angle = angle;
}
diff --git a/src/gui/painting/qbrush.h b/src/gui/painting/qbrush.h
index 568c06ef84..e5cff9cc9b 100644
--- a/src/gui/painting/qbrush.h
+++ b/src/gui/painting/qbrush.h
@@ -40,6 +40,7 @@
#ifndef QBRUSH_H
#define QBRUSH_H
+#include <QtGui/qtguiglobal.h>
#include <QtCore/qpair.h>
#include <QtCore/qpoint.h>
#include <QtCore/qvector.h>
diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp
index 0d24fd94f5..e3dbf663e1 100644
--- a/src/gui/painting/qcolor.cpp
+++ b/src/gui/painting/qcolor.cpp
@@ -43,6 +43,9 @@
#include "qdatastream.h"
#include "qvariant.h"
#include "qdebug.h"
+#include "private/qtools_p.h"
+
+#include <algorithm>
#include <stdio.h>
#include <limits.h>
@@ -50,6 +53,320 @@
QT_BEGIN_NAMESPACE
/*!
+ \internal
+ If s[0..1] is a valid hex number, returns its integer value,
+ otherwise returns -1.
+ */
+static inline int hex2int(const char *s)
+{
+ const int hi = QtMiscUtils::fromHex(s[0]);
+ if (hi < 0)
+ return -1;
+ const int lo = QtMiscUtils::fromHex(s[1]);
+ if (lo < 0)
+ return -1;
+ return (hi << 4) | lo;
+}
+
+/*!
+ \internal
+ If s is a valid hex digit, returns its integer value,
+ multiplied by 0x11, otherwise returns -1.
+ */
+static inline int hex2int(char s)
+{
+ const int h = QtMiscUtils::fromHex(s);
+ return h < 0 ? h : (h << 4) | h;
+}
+
+static bool get_hex_rgb(const char *name, int len, QRgb *rgb)
+{
+ if (name[0] != '#')
+ return false;
+ name++;
+ --len;
+ int a, r, g, b;
+ a = 255;
+ if (len == 12) {
+ r = hex2int(name);
+ g = hex2int(name + 4);
+ b = hex2int(name + 8);
+ } else if (len == 9) {
+ r = hex2int(name);
+ g = hex2int(name + 3);
+ b = hex2int(name + 6);
+ } else if (len == 8) {
+ a = hex2int(name);
+ r = hex2int(name + 2);
+ g = hex2int(name + 4);
+ b = hex2int(name + 6);
+ } else if (len == 6) {
+ r = hex2int(name);
+ g = hex2int(name + 2);
+ b = hex2int(name + 4);
+ } else if (len == 3) {
+ r = hex2int(name[0]);
+ g = hex2int(name[1]);
+ b = hex2int(name[2]);
+ } else {
+ r = g = b = -1;
+ }
+ if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || (uint)a > 255) {
+ *rgb = 0;
+ return false;
+ }
+ *rgb = qRgba(r, g ,b, a);
+ return true;
+}
+
+bool qt_get_hex_rgb(const char *name, QRgb *rgb)
+{
+ return get_hex_rgb(name, qstrlen(name), rgb);
+}
+
+static bool get_hex_rgb(const QChar *str, int len, QRgb *rgb)
+{
+ if (len > 13)
+ return false;
+ char tmp[16];
+ for (int i = 0; i < len; ++i)
+ tmp[i] = str[i].toLatin1();
+ tmp[len] = 0;
+ return get_hex_rgb(tmp, len, rgb);
+}
+
+#ifndef QT_NO_COLORNAMES
+
+/*
+ CSS color names = SVG 1.0 color names + transparent (rgba(0,0,0,0))
+*/
+
+#ifdef rgb
+# undef rgb
+#endif
+#define rgb(r,g,b) (0xff000000 | (r << 16) | (g << 8) | b)
+
+static const struct RGBData {
+ const char name[21];
+ uint value;
+} rgbTbl[] = {
+ { "aliceblue", rgb(240, 248, 255) },
+ { "antiquewhite", rgb(250, 235, 215) },
+ { "aqua", rgb( 0, 255, 255) },
+ { "aquamarine", rgb(127, 255, 212) },
+ { "azure", rgb(240, 255, 255) },
+ { "beige", rgb(245, 245, 220) },
+ { "bisque", rgb(255, 228, 196) },
+ { "black", rgb( 0, 0, 0) },
+ { "blanchedalmond", rgb(255, 235, 205) },
+ { "blue", rgb( 0, 0, 255) },
+ { "blueviolet", rgb(138, 43, 226) },
+ { "brown", rgb(165, 42, 42) },
+ { "burlywood", rgb(222, 184, 135) },
+ { "cadetblue", rgb( 95, 158, 160) },
+ { "chartreuse", rgb(127, 255, 0) },
+ { "chocolate", rgb(210, 105, 30) },
+ { "coral", rgb(255, 127, 80) },
+ { "cornflowerblue", rgb(100, 149, 237) },
+ { "cornsilk", rgb(255, 248, 220) },
+ { "crimson", rgb(220, 20, 60) },
+ { "cyan", rgb( 0, 255, 255) },
+ { "darkblue", rgb( 0, 0, 139) },
+ { "darkcyan", rgb( 0, 139, 139) },
+ { "darkgoldenrod", rgb(184, 134, 11) },
+ { "darkgray", rgb(169, 169, 169) },
+ { "darkgreen", rgb( 0, 100, 0) },
+ { "darkgrey", rgb(169, 169, 169) },
+ { "darkkhaki", rgb(189, 183, 107) },
+ { "darkmagenta", rgb(139, 0, 139) },
+ { "darkolivegreen", rgb( 85, 107, 47) },
+ { "darkorange", rgb(255, 140, 0) },
+ { "darkorchid", rgb(153, 50, 204) },
+ { "darkred", rgb(139, 0, 0) },
+ { "darksalmon", rgb(233, 150, 122) },
+ { "darkseagreen", rgb(143, 188, 143) },
+ { "darkslateblue", rgb( 72, 61, 139) },
+ { "darkslategray", rgb( 47, 79, 79) },
+ { "darkslategrey", rgb( 47, 79, 79) },
+ { "darkturquoise", rgb( 0, 206, 209) },
+ { "darkviolet", rgb(148, 0, 211) },
+ { "deeppink", rgb(255, 20, 147) },
+ { "deepskyblue", rgb( 0, 191, 255) },
+ { "dimgray", rgb(105, 105, 105) },
+ { "dimgrey", rgb(105, 105, 105) },
+ { "dodgerblue", rgb( 30, 144, 255) },
+ { "firebrick", rgb(178, 34, 34) },
+ { "floralwhite", rgb(255, 250, 240) },
+ { "forestgreen", rgb( 34, 139, 34) },
+ { "fuchsia", rgb(255, 0, 255) },
+ { "gainsboro", rgb(220, 220, 220) },
+ { "ghostwhite", rgb(248, 248, 255) },
+ { "gold", rgb(255, 215, 0) },
+ { "goldenrod", rgb(218, 165, 32) },
+ { "gray", rgb(128, 128, 128) },
+ { "green", rgb( 0, 128, 0) },
+ { "greenyellow", rgb(173, 255, 47) },
+ { "grey", rgb(128, 128, 128) },
+ { "honeydew", rgb(240, 255, 240) },
+ { "hotpink", rgb(255, 105, 180) },
+ { "indianred", rgb(205, 92, 92) },
+ { "indigo", rgb( 75, 0, 130) },
+ { "ivory", rgb(255, 255, 240) },
+ { "khaki", rgb(240, 230, 140) },
+ { "lavender", rgb(230, 230, 250) },
+ { "lavenderblush", rgb(255, 240, 245) },
+ { "lawngreen", rgb(124, 252, 0) },
+ { "lemonchiffon", rgb(255, 250, 205) },
+ { "lightblue", rgb(173, 216, 230) },
+ { "lightcoral", rgb(240, 128, 128) },
+ { "lightcyan", rgb(224, 255, 255) },
+ { "lightgoldenrodyellow", rgb(250, 250, 210) },
+ { "lightgray", rgb(211, 211, 211) },
+ { "lightgreen", rgb(144, 238, 144) },
+ { "lightgrey", rgb(211, 211, 211) },
+ { "lightpink", rgb(255, 182, 193) },
+ { "lightsalmon", rgb(255, 160, 122) },
+ { "lightseagreen", rgb( 32, 178, 170) },
+ { "lightskyblue", rgb(135, 206, 250) },
+ { "lightslategray", rgb(119, 136, 153) },
+ { "lightslategrey", rgb(119, 136, 153) },
+ { "lightsteelblue", rgb(176, 196, 222) },
+ { "lightyellow", rgb(255, 255, 224) },
+ { "lime", rgb( 0, 255, 0) },
+ { "limegreen", rgb( 50, 205, 50) },
+ { "linen", rgb(250, 240, 230) },
+ { "magenta", rgb(255, 0, 255) },
+ { "maroon", rgb(128, 0, 0) },
+ { "mediumaquamarine", rgb(102, 205, 170) },
+ { "mediumblue", rgb( 0, 0, 205) },
+ { "mediumorchid", rgb(186, 85, 211) },
+ { "mediumpurple", rgb(147, 112, 219) },
+ { "mediumseagreen", rgb( 60, 179, 113) },
+ { "mediumslateblue", rgb(123, 104, 238) },
+ { "mediumspringgreen", rgb( 0, 250, 154) },
+ { "mediumturquoise", rgb( 72, 209, 204) },
+ { "mediumvioletred", rgb(199, 21, 133) },
+ { "midnightblue", rgb( 25, 25, 112) },
+ { "mintcream", rgb(245, 255, 250) },
+ { "mistyrose", rgb(255, 228, 225) },
+ { "moccasin", rgb(255, 228, 181) },
+ { "navajowhite", rgb(255, 222, 173) },
+ { "navy", rgb( 0, 0, 128) },
+ { "oldlace", rgb(253, 245, 230) },
+ { "olive", rgb(128, 128, 0) },
+ { "olivedrab", rgb(107, 142, 35) },
+ { "orange", rgb(255, 165, 0) },
+ { "orangered", rgb(255, 69, 0) },
+ { "orchid", rgb(218, 112, 214) },
+ { "palegoldenrod", rgb(238, 232, 170) },
+ { "palegreen", rgb(152, 251, 152) },
+ { "paleturquoise", rgb(175, 238, 238) },
+ { "palevioletred", rgb(219, 112, 147) },
+ { "papayawhip", rgb(255, 239, 213) },
+ { "peachpuff", rgb(255, 218, 185) },
+ { "peru", rgb(205, 133, 63) },
+ { "pink", rgb(255, 192, 203) },
+ { "plum", rgb(221, 160, 221) },
+ { "powderblue", rgb(176, 224, 230) },
+ { "purple", rgb(128, 0, 128) },
+ { "red", rgb(255, 0, 0) },
+ { "rosybrown", rgb(188, 143, 143) },
+ { "royalblue", rgb( 65, 105, 225) },
+ { "saddlebrown", rgb(139, 69, 19) },
+ { "salmon", rgb(250, 128, 114) },
+ { "sandybrown", rgb(244, 164, 96) },
+ { "seagreen", rgb( 46, 139, 87) },
+ { "seashell", rgb(255, 245, 238) },
+ { "sienna", rgb(160, 82, 45) },
+ { "silver", rgb(192, 192, 192) },
+ { "skyblue", rgb(135, 206, 235) },
+ { "slateblue", rgb(106, 90, 205) },
+ { "slategray", rgb(112, 128, 144) },
+ { "slategrey", rgb(112, 128, 144) },
+ { "snow", rgb(255, 250, 250) },
+ { "springgreen", rgb( 0, 255, 127) },
+ { "steelblue", rgb( 70, 130, 180) },
+ { "tan", rgb(210, 180, 140) },
+ { "teal", rgb( 0, 128, 128) },
+ { "thistle", rgb(216, 191, 216) },
+ { "tomato", rgb(255, 99, 71) },
+ { "transparent", 0 },
+ { "turquoise", rgb( 64, 224, 208) },
+ { "violet", rgb(238, 130, 238) },
+ { "wheat", rgb(245, 222, 179) },
+ { "white", rgb(255, 255, 255) },
+ { "whitesmoke", rgb(245, 245, 245) },
+ { "yellow", rgb(255, 255, 0) },
+ { "yellowgreen", rgb(154, 205, 50) }
+};
+
+static const int rgbTblSize = sizeof(rgbTbl) / sizeof(RGBData);
+
+#undef rgb
+
+#if defined(Q_CC_MSVC) && _MSC_VER < 1600
+inline bool operator<(const RGBData &data1, const RGBData &data2)
+{ return qstrcmp(data1.name, data2.name) < 0; }
+#endif
+
+inline bool operator<(const char *name, const RGBData &data)
+{ return qstrcmp(name, data.name) < 0; }
+inline bool operator<(const RGBData &data, const char *name)
+{ return qstrcmp(data.name, name) < 0; }
+
+static bool get_named_rgb_no_space(const char *name_no_space, QRgb *rgb)
+{
+ const RGBData *r = std::lower_bound(rgbTbl, rgbTbl + rgbTblSize, name_no_space);
+ if ((r != rgbTbl + rgbTblSize) && !(name_no_space < *r)) {
+ *rgb = r->value;
+ return true;
+ }
+ return false;
+}
+
+static bool get_named_rgb(const char *name, int len, QRgb* rgb)
+{
+ if (len > 255)
+ return false;
+ char name_no_space[256];
+ int pos = 0;
+ for (int i = 0; i < len; i++) {
+ if (name[i] != '\t' && name[i] != ' ')
+ name_no_space[pos++] = QChar::toLower(name[i]);
+ }
+ name_no_space[pos] = 0;
+
+ return get_named_rgb_no_space(name_no_space, rgb);
+}
+
+static bool get_named_rgb(const QChar *name, int len, QRgb *rgb)
+{
+ if (len > 255)
+ return false;
+ char name_no_space[256];
+ int pos = 0;
+ for (int i = 0; i < len; i++) {
+ if (name[i] != QLatin1Char('\t') && name[i] != QLatin1Char(' '))
+ name_no_space[pos++] = name[i].toLower().toLatin1();
+ }
+ name_no_space[pos] = 0;
+ return get_named_rgb_no_space(name_no_space, rgb);
+}
+
+#endif // QT_NO_COLORNAMES
+
+static QStringList get_colornames()
+{
+ QStringList lst;
+#ifndef QT_NO_COLORNAMES
+ lst.reserve(rgbTblSize);
+ for (int i = 0; i < rgbTblSize; i++)
+ lst << QLatin1String(rgbTbl[i].name);
+#endif
+ return lst;
+}
+
+/*!
\class QColor
\brief The QColor class provides colors based on RGB, HSV or CMYK values.
@@ -261,7 +578,7 @@ QT_BEGIN_NAMESPACE
alpha-channel to feature \l {QColor#Alpha-Blended
Drawing}{alpha-blended drawing}.
- \sa QPalette, QBrush, QApplication::setColorSpec()
+ \sa QPalette, QBrush
*/
#define QCOLOR_INT_RANGE_CHECK(fn, var) \
@@ -478,12 +795,14 @@ QColor::QColor(Spec spec) Q_DECL_NOTHROW
/*!
\fn QColor::QColor(const char *name)
+ \overload
+ \sa setNamedColor(), name(), isValid()
+*/
- Constructs a named color in the same way as setNamedColor() using
- the given \a name.
-
- The color is left invalid if the \a name cannot be parsed.
-
+/*!
+ \fn QColor::QColor(QLatin1String name)
+ \overload
+ \since 5.8
\sa setNamedColor(), name(), isValid()
*/
@@ -564,6 +883,16 @@ void QColor::setNamedColor(const QString &name)
}
/*!
+ \overload
+ \since 5.8
+*/
+
+void QColor::setNamedColor(QLatin1String name)
+{
+ setColorFromString(name);
+}
+
+/*!
\since 4.7
Returns \c true if the \a name is a valid color name and can
@@ -579,16 +908,26 @@ bool QColor::isValidColor(const QString &name)
return !name.isEmpty() && QColor().setColorFromString(name);
}
-bool QColor::setColorFromString(const QString &name)
+/*!
+ \overload
+ \since 5.8
+*/
+bool QColor::isValidColor(QLatin1String name) Q_DECL_NOTHROW
{
- if (name.isEmpty()) {
+ return name.size() && QColor().setColorFromString(name);
+}
+
+template <typename String>
+bool QColor::setColorFromString(const String &name)
+{
+ if (!name.size()) {
invalidate();
return true;
}
- if (name.startsWith(QLatin1Char('#'))) {
+ if (name[0] == QLatin1Char('#')) {
QRgb rgba;
- if (qt_get_hex_rgb(name.constData(), name.length(), &rgba)) {
+ if (get_hex_rgb(name.data(), name.size(), &rgba)) {
setRgba(rgba);
return true;
} else {
@@ -599,7 +938,7 @@ bool QColor::setColorFromString(const QString &name)
#ifndef QT_NO_COLORNAMES
QRgb rgb;
- if (qt_get_named_rgb(name.constData(), name.length(), &rgb)) {
+ if (get_named_rgb(name.data(), name.size(), &rgb)) {
setRgba(rgb);
return true;
} else
@@ -617,11 +956,7 @@ bool QColor::setColorFromString(const QString &name)
*/
QStringList QColor::colorNames()
{
-#ifndef QT_NO_COLORNAMES
- return qt_get_colornames();
-#else
- return QStringList();
-#endif
+ return get_colornames();
}
/*!
diff --git a/src/gui/painting/qcolor.h b/src/gui/painting/qcolor.h
index 6338eedd22..db6eb92916 100644
--- a/src/gui/painting/qcolor.h
+++ b/src/gui/painting/qcolor.h
@@ -40,6 +40,7 @@
#ifndef QCOLOR_H
#define QCOLOR_H
+#include <QtGui/qtguiglobal.h>
#include <QtGui/qrgb.h>
#include <QtCore/qnamespace.h>
#include <QtCore/qstringlist.h>
@@ -72,7 +73,8 @@ public:
QColor(QRgb rgb) Q_DECL_NOTHROW;
QColor(QRgba64 rgba64) Q_DECL_NOTHROW;
QColor(const QString& name);
- QColor(const char *name);
+ QColor(const char *aname) : QColor(QLatin1String(aname)) {}
+ QColor(QLatin1String name);
QColor(Spec spec) Q_DECL_NOTHROW;
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
@@ -92,7 +94,9 @@ public:
// ### Qt 6: merge overloads
QString name() const;
QString name(NameFormat format) const;
+
void setNamedColor(const QString& name);
+ void setNamedColor(QLatin1String name);
static QStringList colorNames();
@@ -218,11 +222,13 @@ public:
operator QVariant() const;
static bool isValidColor(const QString &name);
+ static bool isValidColor(QLatin1String) Q_DECL_NOTHROW;
private:
void invalidate() Q_DECL_NOTHROW;
- bool setColorFromString(const QString &name);
+ template <typename String>
+ bool setColorFromString(const String &name);
Spec cspec;
union {
@@ -263,6 +269,7 @@ private:
friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QColor &);
#endif
};
+Q_DECLARE_TYPEINFO(QColor, QT_VERSION >= QT_VERSION_CHECK(6,0,0) ? Q_MOVABLE_TYPE : Q_RELOCATABLE_TYPE);
inline QColor::QColor() Q_DECL_NOTHROW
{ invalidate(); }
@@ -270,8 +277,8 @@ inline QColor::QColor() Q_DECL_NOTHROW
inline QColor::QColor(int r, int g, int b, int a)
{ setRgb(r, g, b, a); }
-inline QColor::QColor(const char *aname)
-{ setNamedColor(QLatin1String(aname)); }
+inline QColor::QColor(QLatin1String aname)
+{ setNamedColor(aname); }
inline QColor::QColor(const QString& aname)
{ setNamedColor(aname); }
diff --git a/src/gui/painting/qcolor_p.cpp b/src/gui/painting/qcolor_p.cpp
deleted file mode 100644
index 773e4096ee..0000000000
--- a/src/gui/painting/qcolor_p.cpp
+++ /dev/null
@@ -1,381 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtGui module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qglobal.h"
-#include "qrgb.h"
-#include "qstringlist.h"
-#include "private/qtools_p.h"
-
-#include <algorithm>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \internal
- If s[0..1] is a valid hex number, returns its integer value,
- otherwise returns -1.
- */
-static inline int hex2int(const char *s)
-{
- const int hi = QtMiscUtils::fromHex(s[0]);
- if (hi < 0)
- return -1;
- const int lo = QtMiscUtils::fromHex(s[1]);
- if (lo < 0)
- return -1;
- return (hi << 4) | lo;
-}
-
-/*!
- \internal
- If s is a valid hex digit, returns its integer value,
- multiplied by 0x11, otherwise returns -1.
- */
-static inline int hex2int(char s)
-{
- const int h = QtMiscUtils::fromHex(s);
- return h < 0 ? h : (h << 4) | h;
-}
-
-bool qt_get_hex_rgb(const char *name, QRgb *rgb)
-{
- if(name[0] != '#')
- return false;
- name++;
- int len = qstrlen(name);
- int a, r, g, b;
- a = 255;
- if (len == 12) {
- r = hex2int(name);
- g = hex2int(name + 4);
- b = hex2int(name + 8);
- } else if (len == 9) {
- r = hex2int(name);
- g = hex2int(name + 3);
- b = hex2int(name + 6);
- } else if (len == 8) {
- a = hex2int(name);
- r = hex2int(name + 2);
- g = hex2int(name + 4);
- b = hex2int(name + 6);
- } else if (len == 6) {
- r = hex2int(name);
- g = hex2int(name + 2);
- b = hex2int(name + 4);
- } else if (len == 3) {
- r = hex2int(name[0]);
- g = hex2int(name[1]);
- b = hex2int(name[2]);
- } else {
- r = g = b = -1;
- }
- if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || (uint)a > 255) {
- *rgb = 0;
- return false;
- }
- *rgb = qRgba(r, g ,b, a);
- return true;
-}
-
-bool qt_get_hex_rgb(const QChar *str, int len, QRgb *rgb)
-{
- if (len > 13)
- return false;
- char tmp[16];
- for(int i = 0; i < len; ++i)
- tmp[i] = str[i].toLatin1();
- tmp[len] = 0;
- return qt_get_hex_rgb(tmp, rgb);
-}
-
-#ifndef QT_NO_COLORNAMES
-
-/*
- CSS color names = SVG 1.0 color names + transparent (rgba(0,0,0,0))
-*/
-
-#ifdef rgb
-# undef rgb
-#endif
-#define rgb(r,g,b) (0xff000000 | (r << 16) | (g << 8) | b)
-
-static const struct RGBData {
- const char name[21];
- uint value;
-} rgbTbl[] = {
- { "aliceblue", rgb(240, 248, 255) },
- { "antiquewhite", rgb(250, 235, 215) },
- { "aqua", rgb( 0, 255, 255) },
- { "aquamarine", rgb(127, 255, 212) },
- { "azure", rgb(240, 255, 255) },
- { "beige", rgb(245, 245, 220) },
- { "bisque", rgb(255, 228, 196) },
- { "black", rgb( 0, 0, 0) },
- { "blanchedalmond", rgb(255, 235, 205) },
- { "blue", rgb( 0, 0, 255) },
- { "blueviolet", rgb(138, 43, 226) },
- { "brown", rgb(165, 42, 42) },
- { "burlywood", rgb(222, 184, 135) },
- { "cadetblue", rgb( 95, 158, 160) },
- { "chartreuse", rgb(127, 255, 0) },
- { "chocolate", rgb(210, 105, 30) },
- { "coral", rgb(255, 127, 80) },
- { "cornflowerblue", rgb(100, 149, 237) },
- { "cornsilk", rgb(255, 248, 220) },
- { "crimson", rgb(220, 20, 60) },
- { "cyan", rgb( 0, 255, 255) },
- { "darkblue", rgb( 0, 0, 139) },
- { "darkcyan", rgb( 0, 139, 139) },
- { "darkgoldenrod", rgb(184, 134, 11) },
- { "darkgray", rgb(169, 169, 169) },
- { "darkgreen", rgb( 0, 100, 0) },
- { "darkgrey", rgb(169, 169, 169) },
- { "darkkhaki", rgb(189, 183, 107) },
- { "darkmagenta", rgb(139, 0, 139) },
- { "darkolivegreen", rgb( 85, 107, 47) },
- { "darkorange", rgb(255, 140, 0) },
- { "darkorchid", rgb(153, 50, 204) },
- { "darkred", rgb(139, 0, 0) },
- { "darksalmon", rgb(233, 150, 122) },
- { "darkseagreen", rgb(143, 188, 143) },
- { "darkslateblue", rgb( 72, 61, 139) },
- { "darkslategray", rgb( 47, 79, 79) },
- { "darkslategrey", rgb( 47, 79, 79) },
- { "darkturquoise", rgb( 0, 206, 209) },
- { "darkviolet", rgb(148, 0, 211) },
- { "deeppink", rgb(255, 20, 147) },
- { "deepskyblue", rgb( 0, 191, 255) },
- { "dimgray", rgb(105, 105, 105) },
- { "dimgrey", rgb(105, 105, 105) },
- { "dodgerblue", rgb( 30, 144, 255) },
- { "firebrick", rgb(178, 34, 34) },
- { "floralwhite", rgb(255, 250, 240) },
- { "forestgreen", rgb( 34, 139, 34) },
- { "fuchsia", rgb(255, 0, 255) },
- { "gainsboro", rgb(220, 220, 220) },
- { "ghostwhite", rgb(248, 248, 255) },
- { "gold", rgb(255, 215, 0) },
- { "goldenrod", rgb(218, 165, 32) },
- { "gray", rgb(128, 128, 128) },
- { "green", rgb( 0, 128, 0) },
- { "greenyellow", rgb(173, 255, 47) },
- { "grey", rgb(128, 128, 128) },
- { "honeydew", rgb(240, 255, 240) },
- { "hotpink", rgb(255, 105, 180) },
- { "indianred", rgb(205, 92, 92) },
- { "indigo", rgb( 75, 0, 130) },
- { "ivory", rgb(255, 255, 240) },
- { "khaki", rgb(240, 230, 140) },
- { "lavender", rgb(230, 230, 250) },
- { "lavenderblush", rgb(255, 240, 245) },
- { "lawngreen", rgb(124, 252, 0) },
- { "lemonchiffon", rgb(255, 250, 205) },
- { "lightblue", rgb(173, 216, 230) },
- { "lightcoral", rgb(240, 128, 128) },
- { "lightcyan", rgb(224, 255, 255) },
- { "lightgoldenrodyellow", rgb(250, 250, 210) },
- { "lightgray", rgb(211, 211, 211) },
- { "lightgreen", rgb(144, 238, 144) },
- { "lightgrey", rgb(211, 211, 211) },
- { "lightpink", rgb(255, 182, 193) },
- { "lightsalmon", rgb(255, 160, 122) },
- { "lightseagreen", rgb( 32, 178, 170) },
- { "lightskyblue", rgb(135, 206, 250) },
- { "lightslategray", rgb(119, 136, 153) },
- { "lightslategrey", rgb(119, 136, 153) },
- { "lightsteelblue", rgb(176, 196, 222) },
- { "lightyellow", rgb(255, 255, 224) },
- { "lime", rgb( 0, 255, 0) },
- { "limegreen", rgb( 50, 205, 50) },
- { "linen", rgb(250, 240, 230) },
- { "magenta", rgb(255, 0, 255) },
- { "maroon", rgb(128, 0, 0) },
- { "mediumaquamarine", rgb(102, 205, 170) },
- { "mediumblue", rgb( 0, 0, 205) },
- { "mediumorchid", rgb(186, 85, 211) },
- { "mediumpurple", rgb(147, 112, 219) },
- { "mediumseagreen", rgb( 60, 179, 113) },
- { "mediumslateblue", rgb(123, 104, 238) },
- { "mediumspringgreen", rgb( 0, 250, 154) },
- { "mediumturquoise", rgb( 72, 209, 204) },
- { "mediumvioletred", rgb(199, 21, 133) },
- { "midnightblue", rgb( 25, 25, 112) },
- { "mintcream", rgb(245, 255, 250) },
- { "mistyrose", rgb(255, 228, 225) },
- { "moccasin", rgb(255, 228, 181) },
- { "navajowhite", rgb(255, 222, 173) },
- { "navy", rgb( 0, 0, 128) },
- { "oldlace", rgb(253, 245, 230) },
- { "olive", rgb(128, 128, 0) },
- { "olivedrab", rgb(107, 142, 35) },
- { "orange", rgb(255, 165, 0) },
- { "orangered", rgb(255, 69, 0) },
- { "orchid", rgb(218, 112, 214) },
- { "palegoldenrod", rgb(238, 232, 170) },
- { "palegreen", rgb(152, 251, 152) },
- { "paleturquoise", rgb(175, 238, 238) },
- { "palevioletred", rgb(219, 112, 147) },
- { "papayawhip", rgb(255, 239, 213) },
- { "peachpuff", rgb(255, 218, 185) },
- { "peru", rgb(205, 133, 63) },
- { "pink", rgb(255, 192, 203) },
- { "plum", rgb(221, 160, 221) },
- { "powderblue", rgb(176, 224, 230) },
- { "purple", rgb(128, 0, 128) },
- { "red", rgb(255, 0, 0) },
- { "rosybrown", rgb(188, 143, 143) },
- { "royalblue", rgb( 65, 105, 225) },
- { "saddlebrown", rgb(139, 69, 19) },
- { "salmon", rgb(250, 128, 114) },
- { "sandybrown", rgb(244, 164, 96) },
- { "seagreen", rgb( 46, 139, 87) },
- { "seashell", rgb(255, 245, 238) },
- { "sienna", rgb(160, 82, 45) },
- { "silver", rgb(192, 192, 192) },
- { "skyblue", rgb(135, 206, 235) },
- { "slateblue", rgb(106, 90, 205) },
- { "slategray", rgb(112, 128, 144) },
- { "slategrey", rgb(112, 128, 144) },
- { "snow", rgb(255, 250, 250) },
- { "springgreen", rgb( 0, 255, 127) },
- { "steelblue", rgb( 70, 130, 180) },
- { "tan", rgb(210, 180, 140) },
- { "teal", rgb( 0, 128, 128) },
- { "thistle", rgb(216, 191, 216) },
- { "tomato", rgb(255, 99, 71) },
- { "transparent", 0 },
- { "turquoise", rgb( 64, 224, 208) },
- { "violet", rgb(238, 130, 238) },
- { "wheat", rgb(245, 222, 179) },
- { "white", rgb(255, 255, 255) },
- { "whitesmoke", rgb(245, 245, 245) },
- { "yellow", rgb(255, 255, 0) },
- { "yellowgreen", rgb(154, 205, 50) }
-};
-
-static const int rgbTblSize = sizeof(rgbTbl) / sizeof(RGBData);
-
-#undef rgb
-
-#if defined(Q_CC_MSVC) && _MSC_VER < 1600
-inline bool operator<(const RGBData &data1, const RGBData &data2)
-{ return qstrcmp(data1.name, data2.name) < 0; }
-#endif
-
-inline bool operator<(const char *name, const RGBData &data)
-{ return qstrcmp(name, data.name) < 0; }
-inline bool operator<(const RGBData &data, const char *name)
-{ return qstrcmp(data.name, name) < 0; }
-
-static bool get_named_rgb(const char *name_no_space, QRgb *rgb)
-{
- const RGBData *r = std::lower_bound(rgbTbl, rgbTbl + rgbTblSize, name_no_space);
- if ((r != rgbTbl + rgbTblSize) && !(name_no_space < *r)) {
- *rgb = r->value;
- return true;
- }
- return false;
-}
-
-bool qt_get_named_rgb(const char *name, QRgb* rgb)
-{
- int len = int(strlen(name));
- if(len > 255)
- return false;
- char name_no_space[256];
- int pos = 0;
- for(int i = 0; i < len; i++) {
- if(name[i] != '\t' && name[i] != ' ')
- name_no_space[pos++] = QChar::toLower(name[i]);
- }
- name_no_space[pos] = 0;
-
- return get_named_rgb(name_no_space, rgb);
-}
-
-bool qt_get_named_rgb(const QChar *name, int len, QRgb *rgb)
-{
- if(len > 255)
- return false;
- char name_no_space[256];
- int pos = 0;
- for(int i = 0; i < len; i++) {
- if(name[i] != QLatin1Char('\t') && name[i] != QLatin1Char(' '))
- name_no_space[pos++] = name[i].toLower().toLatin1();
- }
- name_no_space[pos] = 0;
- return get_named_rgb(name_no_space, rgb);
-}
-
-
-uint qt_get_rgb_val(const char *name)
-{
- QRgb r = 0;
- qt_get_named_rgb(name,&r);
- return r;
-}
-
-QStringList qt_get_colornames()
-{
- int i = 0;
- QStringList lst;
- lst.reserve(rgbTblSize);
- for (i = 0; i < rgbTblSize; i++)
- lst << QLatin1String(rgbTbl[i].name);
- return lst;
-}
-
-#else
-
-bool qt_get_named_rgb(const char *, QRgb*)
-{
- return false;
-}
-
-uint qt_get_rgb_val(const char *)
-{
- return 0;
-}
-QStringList qt_get_colornames()
-{
- return QStringList();
-}
-#endif // QT_NO_COLORNAMES
-
-QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolor_p.h b/src/gui/painting/qcolor_p.h
index 75bc888952..b44f2b163a 100644
--- a/src/gui/painting/qcolor_p.h
+++ b/src/gui/painting/qcolor_p.h
@@ -51,18 +51,12 @@
// We mean it.
//
-#include "QtCore/qglobal.h"
+#include <QtGui/private/qtguiglobal_p.h>
#include "QtGui/qrgb.h"
-#include "QtCore/qstringlist.h"
QT_BEGIN_NAMESPACE
-uint qt_get_rgb_val(const char *name);
-bool qt_get_named_rgb(const char *, QRgb*);
-bool qt_get_named_rgb(const QChar *, int len, QRgb*);
bool qt_get_hex_rgb(const char *, QRgb *);
-bool qt_get_hex_rgb(const QChar *, int len, QRgb *);
-QStringList qt_get_colornames();
QT_END_NAMESPACE
diff --git a/src/gui/painting/qcompositionfunctions.cpp b/src/gui/painting/qcompositionfunctions.cpp
index 9312ee9540..6d2cb9aadb 100644
--- a/src/gui/painting/qcompositionfunctions.cpp
+++ b/src/gui/painting/qcompositionfunctions.cpp
@@ -87,6 +87,36 @@ QT_BEGIN_NAMESPACE
}\
}
+#if defined __SSE2__
+# define LOAD(ptr) _mm_loadl_epi64(reinterpret_cast<const __m128i *>(ptr))
+#ifdef Q_PROCESSOR_X86_64
+# define CONVERT(value) _mm_cvtsi64_si128(value)
+#else
+# define CONVERT(value) LOAD(&value)
+#endif
+# define STORE(ptr, value) _mm_storel_epi64(reinterpret_cast<__m128i *>(ptr), value)
+# define ADD(p, q) _mm_add_epi32(p, q)
+# define ALPHA(c) _mm_shufflelo_epi16(c, _MM_SHUFFLE(3, 3, 3, 3))
+# define CONST(n) _mm_shufflelo_epi16(_mm_cvtsi32_si128(n), _MM_SHUFFLE(0, 0, 0, 0))
+# define INVALPHA(c) _mm_sub_epi32(CONST(65535), ALPHA(c))
+#elif defined __ARM_NEON__
+# define LOAD(ptr) vreinterpret_u16_u64(vld1_u64(reinterpret_cast<const uint64_t *>(ptr)))
+# define CONVERT(value) vreinterpret_u16_u64(vmov_n_u64(value))
+# define STORE(ptr, value) vst1_u64(reinterpret_cast<uint64_t *>(ptr), vreinterpret_u64_u16(value))
+# define ADD(p, q) vadd_u16(p, q)
+# define ALPHA(c) vdup_lane_u16(c, 3)
+# define CONST(n) vdup_n_u16(n)
+# define INVALPHA(c) vmvn_u16(ALPHA(c))
+#else
+# define LOAD(ptr) *ptr
+# define CONVERT(value) value
+# define STORE(ptr, value) *ptr = value
+# define ADD(p, q) (p + q)
+# define ALPHA(c) (c).alpha()
+# define CONST(n) n
+# define INVALPHA(c) (65535 - ALPHA(c))
+#endif
+
void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint, uint const_alpha)
{
comp_func_Clear_impl(dest, length, const_alpha);
@@ -99,7 +129,7 @@ void QT_FASTCALL comp_func_solid_Clear_rgb64(QRgba64 *dest, int length, QRgba64,
else {
int ialpha = 255 - const_alpha;
for (int i = 0; i < length; ++i) {
- dest[i] = multiplyAlpha255(dest[i], ialpha);
+ STORE(&dest[i], multiplyAlpha255(LOAD(&dest[i]), ialpha));
}
}
}
@@ -116,7 +146,7 @@ void QT_FASTCALL comp_func_Clear_rgb64(QRgba64 *dest, const QRgba64 *, int lengt
else {
int ialpha = 255 - const_alpha;
for (int i = 0; i < length; ++i) {
- dest[i] = multiplyAlpha255(dest[i], ialpha);
+ STORE(&dest[i], multiplyAlpha255(LOAD(&dest[i]), ialpha));
}
}
}
@@ -146,9 +176,9 @@ void QT_FASTCALL comp_func_solid_Source_rgb64(QRgba64 *dest, int length, QRgba64
qt_memfill64((quint64*)dest, color, length);
else {
int ialpha = 255 - const_alpha;
- color = multiplyAlpha255(color, const_alpha);
+ auto c = multiplyAlpha255(CONVERT(color), const_alpha);
for (int i = 0; i < length; ++i) {
- dest[i] = color + multiplyAlpha255(dest[i], ialpha);
+ STORE(&dest[i], ADD(c, multiplyAlpha255(LOAD(&dest[i]), ialpha)));
}
}
}
@@ -174,7 +204,7 @@ void QT_FASTCALL comp_func_Source_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRg
else {
int ialpha = 255 - const_alpha;
for (int i = 0; i < length; ++i) {
- dest[i] = interpolate255(src[i], const_alpha, dest[i], ialpha);
+ STORE(&dest[i], interpolate255(LOAD(&src[i]), const_alpha, LOAD(&dest[i]), ialpha));
}
}
}
@@ -221,10 +251,12 @@ void QT_FASTCALL comp_func_solid_SourceOver_rgb64(QRgba64 *dest, int length, QRg
if (const_alpha == 255 && color.isOpaque()) {
qt_memfill64((quint64*)dest, color, length);
} else {
+ auto c = CONVERT(color);
if (const_alpha != 255)
- color = multiplyAlpha255(color, const_alpha);
+ c = multiplyAlpha255(c, const_alpha);
+ auto cAlpha = INVALPHA(c);
for (int i = 0; i < length; ++i) {
- dest[i] = color + multiplyAlpha65535(dest[i], 65535 - color.alpha());
+ STORE(&dest[i], ADD(c, multiplyAlpha65535(LOAD(&dest[i]), cAlpha)));
}
}
}
@@ -258,12 +290,12 @@ void QT_FASTCALL comp_func_SourceOver_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const
if (s.isOpaque())
dest[i] = s;
else if (!s.isTransparent())
- dest[i] = s + multiplyAlpha65535(dest[i], 65535 - s.alpha());
+ STORE(&dest[i], ADD(CONVERT(s), multiplyAlpha65535(LOAD(&dest[i]), 65535 - s.alpha())));
}
} else {
for (int i = 0; i < length; ++i) {
- QRgba64 s = multiplyAlpha255(src[i], const_alpha);
- dest[i] = s + multiplyAlpha65535(dest[i], 65535 - s.alpha());
+ auto s = multiplyAlpha255(LOAD(&src[i]), const_alpha);
+ STORE(&dest[i], ADD(s, multiplyAlpha65535(LOAD(&dest[i]), INVALPHA(s))));
}
}
}
@@ -287,11 +319,12 @@ void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, uint co
void QT_FASTCALL comp_func_solid_DestinationOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
+ auto c = CONVERT(color);
if (const_alpha != 255)
- color = multiplyAlpha255(color, const_alpha);
+ c = multiplyAlpha255(c, const_alpha);
for (int i = 0; i < length; ++i) {
- QRgba64 d = dest[i];
- dest[i] = d + multiplyAlpha65535(color, 65535 - d.alpha());
+ auto d = LOAD(&dest[i]);
+ STORE(&dest[i], ADD(d, multiplyAlpha65535(c, INVALPHA(d))));
}
}
@@ -318,14 +351,14 @@ void QT_FASTCALL comp_func_DestinationOver_rgb64(QRgba64 *Q_DECL_RESTRICT dest,
{
if (const_alpha == 255) {
for (int i = 0; i < length; ++i) {
- QRgba64 d = dest[i];
- dest[i] = d + multiplyAlpha65535(src[i], 65535 - d.alpha());
+ auto d = LOAD(&dest[i]);
+ STORE(&dest[i], ADD(d, multiplyAlpha65535(LOAD(&src[i]), INVALPHA(d))));
}
} else {
for (int i = 0; i < length; ++i) {
- QRgba64 d = dest[i];
- QRgba64 s = multiplyAlpha255(src[i], const_alpha);
- dest[i] = d + multiplyAlpha65535(s, 65535 - d.alpha());
+ auto d = LOAD(&dest[i]);
+ auto s = multiplyAlpha255(LOAD(&src[i]), const_alpha);
+ STORE(&dest[i], ADD(d, multiplyAlpha65535(s, INVALPHA(d))));
}
}
}
@@ -393,15 +426,15 @@ void QT_FASTCALL comp_func_SourceIn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const Q
{
if (const_alpha == 255) {
for (int i = 0; i < length; ++i) {
- dest[i] = multiplyAlpha65535(src[i], dest[i].alpha());
+ STORE(&dest[i], multiplyAlpha65535(LOAD(&src[i]), dest[i].alpha()));
}
} else {
uint ca = const_alpha * 257;
- uint cia = 65535 - ca;
+ auto cia = CONST(65535 - ca);
for (int i = 0; i < length; ++i) {
- QRgba64 d = dest[i];
- QRgba64 s = multiplyAlpha65535(src[i], ca);
- dest[i] = interpolate65535(s, d.alpha(), d, cia);
+ auto d = LOAD(&dest[i]);
+ auto s = multiplyAlpha65535(LOAD(&src[i]), ca);
+ STORE(&dest[i], interpolate65535(s, ALPHA(d), d, cia));
}
}
}
@@ -431,7 +464,7 @@ void QT_FASTCALL comp_func_solid_DestinationIn_rgb64(QRgba64 *dest, int length,
if (const_alpha != 255)
a = qt_div_65535(a * ca64k) + 65535 - ca64k;
for (int i = 0; i < length; ++i) {
- dest[i] = multiplyAlpha65535(dest[i], a);
+ STORE(&dest[i], multiplyAlpha65535(LOAD(&dest[i]), a));
}
}
@@ -885,14 +918,19 @@ void QT_FASTCALL comp_func_solid_Plus(uint *dest, int length, uint color, uint c
void QT_FASTCALL comp_func_solid_Plus_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
+ auto b = CONVERT(color);
if (const_alpha == 255) {
for (int i = 0; i < length; ++i) {
- dest[i] = addWithSaturation(dest[i], color);
+ auto a = LOAD(&dest[i]);
+ a = addWithSaturation(a, b);
+ STORE(&dest[i], a);
}
} else {
for (int i = 0; i < length; ++i) {
- QRgba64 d = addWithSaturation(dest[i], color);
- dest[i] = interpolate255(d, const_alpha, dest[i], 255 - const_alpha);
+ auto a = LOAD(&dest[i]);
+ auto d = addWithSaturation(a, b);
+ a = interpolate255(d, const_alpha, a, 255 - const_alpha);
+ STORE(&dest[i], a);
}
}
}
@@ -924,12 +962,18 @@ void QT_FASTCALL comp_func_Plus_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba
{
if (const_alpha == 255) {
for (int i = 0; i < length; ++i) {
- dest[i] = addWithSaturation(dest[i], src[i]);
+ auto a = LOAD(&dest[i]);
+ auto b = LOAD(&src[i]);
+ a = addWithSaturation(a, b);
+ STORE(&dest[i], a);
}
} else {
for (int i = 0; i < length; ++i) {
- QRgba64 d = addWithSaturation(dest[i], src[i]);
- dest[i] = interpolate255(d, const_alpha, dest[i], 255 - const_alpha);
+ auto a = LOAD(&dest[i]);
+ auto b = LOAD(&src[i]);
+ auto d = addWithSaturation(a, b);
+ a = interpolate255(d, const_alpha, a, 255 - const_alpha);
+ STORE(&dest[i], a);
}
}
}
diff --git a/src/gui/painting/qcoregraphics.mm b/src/gui/painting/qcoregraphics.mm
new file mode 100644
index 0000000000..6af12c19d8
--- /dev/null
+++ b/src/gui/painting/qcoregraphics.mm
@@ -0,0 +1,565 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcoregraphics_p.h"
+
+#include <private/qcore_mac_p.h>
+#include <qpa/qplatformpixmap.h>
+#include <QtGui/qicon.h>
+#include <QtGui/private/qpaintengine_p.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qcoreapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+// ---------------------- Images ----------------------
+
+CGImageRef qt_mac_toCGImage(const QImage &inImage)
+{
+ CGImageRef cgImage = inImage.toCGImage();
+ if (cgImage)
+ return cgImage;
+
+ // Convert image data to a known-good format if the fast conversion fails.
+ return inImage.convertToFormat(QImage::Format_ARGB32_Premultiplied).toCGImage();
+}
+
+CGImageRef qt_mac_toCGImageMask(const QImage &image)
+{
+ static const auto deleter = [](void *image, const void *, size_t) { delete static_cast<QImage *>(image); };
+ QCFType<CGDataProviderRef> dataProvider =
+ CGDataProviderCreateWithData(new QImage(image), image.bits(),
+ image.byteCount(), deleter);
+
+ return CGImageMaskCreate(image.width(), image.height(), 8, image.depth(),
+ image.bytesPerLine(), dataProvider, NULL, false);
+}
+
+OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage)
+{
+ // Verbatim copy if HIViewDrawCGImage (as shown on Carbon-Dev)
+ OSStatus err = noErr;
+
+#ifdef Q_OS_MACOS
+ require_action(inContext != NULL, InvalidContext, err = paramErr);
+ require_action(inBounds != NULL, InvalidBounds, err = paramErr);
+ require_action(inImage != NULL, InvalidImage, err = paramErr);
+#endif
+
+ CGContextSaveGState( inContext );
+ CGContextTranslateCTM (inContext, 0, inBounds->origin.y + CGRectGetMaxY(*inBounds));
+ CGContextScaleCTM(inContext, 1, -1);
+
+ CGContextDrawImage(inContext, *inBounds, inImage);
+
+ CGContextRestoreGState(inContext);
+
+#ifdef Q_OS_MACOS
+InvalidImage:
+InvalidBounds:
+InvalidContext:
+#endif
+ return err;
+}
+
+QImage qt_mac_toQImage(CGImageRef image)
+{
+ const size_t w = CGImageGetWidth(image),
+ h = CGImageGetHeight(image);
+ QImage ret(w, h, QImage::Format_ARGB32_Premultiplied);
+ ret.fill(Qt::transparent);
+ CGRect rect = CGRectMake(0, 0, w, h);
+ QMacCGContext ctx(&ret);
+ qt_mac_drawCGImage(ctx, &rect, image);
+ return ret;
+}
+
+#ifdef Q_OS_MACOS
+
+QT_END_NAMESPACE
+
+@interface NSGraphicsContext (QtAdditions)
+
++ (NSGraphicsContext *)qt_graphicsContextWithCGContext:(CGContextRef)graphicsPort flipped:(BOOL)initialFlippedState;
+
+@end
+
+@implementation NSGraphicsContext (QtAdditions)
+
++ (NSGraphicsContext *)qt_graphicsContextWithCGContext:(CGContextRef)graphicsPort flipped:(BOOL)initialFlippedState
+{
+#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_10, __IPHONE_NA)
+ if (QT_PREPEND_NAMESPACE(QSysInfo::MacintoshVersion) >= QT_PREPEND_NAMESPACE(QSysInfo::MV_10_10))
+ return [self graphicsContextWithCGContext:graphicsPort flipped:initialFlippedState];
+#endif
+ return [self graphicsContextWithGraphicsPort:graphicsPort flipped:initialFlippedState];
+}
+
+@end
+
+QT_BEGIN_NAMESPACE
+
+static NSImage *qt_mac_cgimage_to_nsimage(CGImageRef image)
+{
+ NSImage *newImage = [[NSImage alloc] initWithCGImage:image size:NSZeroSize];
+ return newImage;
+}
+
+NSImage *qt_mac_create_nsimage(const QPixmap &pm)
+{
+ if (pm.isNull())
+ return 0;
+ QImage image = pm.toImage();
+ CGImageRef cgImage = qt_mac_toCGImage(image);
+ NSImage *nsImage = qt_mac_cgimage_to_nsimage(cgImage);
+ CGImageRelease(cgImage);
+ return nsImage;
+}
+
+NSImage *qt_mac_create_nsimage(const QIcon &icon)
+{
+ if (icon.isNull())
+ return nil;
+
+ NSImage *nsImage = [[NSImage alloc] init];
+ foreach (QSize size, icon.availableSizes()) {
+ QPixmap pm = icon.pixmap(size);
+ QImage image = pm.toImage();
+ CGImageRef cgImage = qt_mac_toCGImage(image);
+ NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage];
+ [nsImage addRepresentation:imageRep];
+ [imageRep release];
+ CGImageRelease(cgImage);
+ }
+ return nsImage;
+}
+
+QPixmap qt_mac_toQPixmap(const NSImage *image, const QSizeF &size)
+{
+ const NSSize pixmapSize = NSMakeSize(size.width(), size.height());
+ QPixmap pixmap(pixmapSize.width, pixmapSize.height);
+ pixmap.fill(Qt::transparent);
+ [image setSize:pixmapSize];
+ const NSRect iconRect = NSMakeRect(0, 0, pixmapSize.width, pixmapSize.height);
+ QMacCGContext ctx(&pixmap);
+ if (!ctx)
+ return QPixmap();
+ NSGraphicsContext *gc = [NSGraphicsContext qt_graphicsContextWithCGContext:ctx flipped:YES];
+ if (!gc)
+ return QPixmap();
+ [NSGraphicsContext saveGraphicsState];
+ [NSGraphicsContext setCurrentContext:gc];
+ [image drawInRect:iconRect fromRect:iconRect operation:NSCompositeSourceOver fraction:1.0 respectFlipped:YES hints:nil];
+ [NSGraphicsContext restoreGraphicsState];
+ return pixmap;
+}
+
+#endif // Q_OS_MACOS
+
+// ---------------------- Colors and Brushes ----------------------
+
+QColor qt_mac_toQColor(CGColorRef color)
+{
+ QColor qtColor;
+ CGColorSpaceModel model = CGColorSpaceGetModel(CGColorGetColorSpace(color));
+ const CGFloat *components = CGColorGetComponents(color);
+ if (model == kCGColorSpaceModelRGB) {
+ qtColor.setRgbF(components[0], components[1], components[2], components[3]);
+ } else if (model == kCGColorSpaceModelCMYK) {
+ qtColor.setCmykF(components[0], components[1], components[2], components[3]);
+ } else if (model == kCGColorSpaceModelMonochrome) {
+ qtColor.setRgbF(components[0], components[0], components[0], components[1]);
+ } else {
+ // Colorspace we can't deal with.
+ qWarning("Qt: qt_mac_toQColor: cannot convert from colorspace model: %d", model);
+ Q_ASSERT(false);
+ }
+ return qtColor;
+}
+
+#ifdef Q_OS_MACOS
+QColor qt_mac_toQColor(const NSColor *color)
+{
+ QColor qtColor;
+ NSString *colorSpace = [color colorSpaceName];
+ if (colorSpace == NSDeviceCMYKColorSpace) {
+ CGFloat cyan, magenta, yellow, black, alpha;
+ [color getCyan:&cyan magenta:&magenta yellow:&yellow black:&black alpha:&alpha];
+ qtColor.setCmykF(cyan, magenta, yellow, black, alpha);
+ } else {
+ NSColor *tmpColor;
+ tmpColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+ CGFloat red, green, blue, alpha;
+ [tmpColor getRed:&red green:&green blue:&blue alpha:&alpha];
+ qtColor.setRgbF(red, green, blue, alpha);
+ }
+ return qtColor;
+}
+#endif
+
+QBrush qt_mac_toQBrush(CGColorRef color)
+{
+ QBrush qtBrush;
+ CGColorSpaceModel model = CGColorSpaceGetModel(CGColorGetColorSpace(color));
+ if (model == kCGColorSpaceModelPattern) {
+ // Colorspace we can't deal with; the color is drawn directly using a callback.
+ qWarning("Qt: qt_mac_toQBrush: cannot convert from colorspace model: %d", model);
+ Q_ASSERT(false);
+ } else {
+ qtBrush.setStyle(Qt::SolidPattern);
+ qtBrush.setColor(qt_mac_toQColor(color));
+ }
+ return qtBrush;
+}
+
+#ifdef Q_OS_MACOS
+static bool qt_mac_isSystemColorOrInstance(const NSColor *color, NSString *colorNameComponent, NSString *className)
+{
+ // We specifically do not want isKindOfClass: here
+ if ([color.className isEqualToString:className]) // NSPatternColorSpace
+ return true;
+ if ([color.catalogNameComponent isEqualToString:@"System"] &&
+ [color.colorNameComponent isEqualToString:colorNameComponent] &&
+ [color.colorSpaceName isEqualToString:NSNamedColorSpace])
+ return true;
+ return false;
+}
+
+QBrush qt_mac_toQBrush(const NSColor *color, QPalette::ColorGroup colorGroup)
+{
+ QBrush qtBrush;
+
+ // QTBUG-49773: This calls NSDrawMenuItemBackground to render a 1 by n gradient; could use HITheme
+ if ([color.className isEqualToString:@"NSMenuItemHighlightColor"]) {
+ qWarning("Qt: qt_mac_toQBrush: cannot convert from NSMenuItemHighlightColor");
+ return qtBrush;
+ }
+
+ // Not a catalog color or a manifestation of System.windowBackgroundColor;
+ // only retrieved from NSWindow.backgroundColor directly
+ if ([color.className isEqualToString:@"NSMetalPatternColor"]) {
+ // NSTexturedBackgroundWindowMask, could theoretically handle this without private API by
+ // creating a window with the appropriate properties and then calling NSWindow.backgroundColor.patternImage,
+ // which returns a texture sized 1 by (window height, including frame), backed by a CGPattern
+ // which follows the window key state... probably need to allow QBrush to store a function pointer
+ // like CGPattern does
+ qWarning("Qt: qt_mac_toQBrush: cannot convert from NSMetalPatternColor");
+ return qtBrush;
+ }
+
+ // No public API to get these colors/stops;
+ // both accurately obtained through runtime object inspection on OS X 10.11
+ // (the NSColor object has NSGradient i-vars for both color groups)
+ if (qt_mac_isSystemColorOrInstance(color, @"_sourceListBackgroundColor", @"NSSourceListBackgroundColor")) {
+ QLinearGradient gradient;
+ if (colorGroup == QPalette::Active) {
+ gradient.setColorAt(0, QColor(233, 237, 242));
+ gradient.setColorAt(0.5, QColor(225, 229, 235));
+ gradient.setColorAt(1, QColor(209, 216, 224));
+ } else {
+ gradient.setColorAt(0, QColor(248, 248, 248));
+ gradient.setColorAt(0.5, QColor(240, 240, 240));
+ gradient.setColorAt(1, QColor(235, 235, 235));
+ }
+ return QBrush(gradient);
+ }
+
+ // A couple colors are special... they are actually instances of NSGradientPatternColor, which
+ // override set/setFill/setStroke to instead initialize an internal color
+ // ([NSColor colorWithCalibratedWhite:0.909804 alpha:1.000000]) while still returning the
+ // ruled lines pattern image (from OS X 10.4) to the user from -[NSColor patternImage]
+ // (and providing no public API to get the underlying color without this insanity)
+ if (qt_mac_isSystemColorOrInstance(color, @"controlColor", @"NSGradientPatternColor") ||
+ qt_mac_isSystemColorOrInstance(color, @"windowBackgroundColor", @"NSGradientPatternColor")) {
+ qtBrush.setStyle(Qt::SolidPattern);
+ qtBrush.setColor(qt_mac_toQColor(color.CGColor));
+ return qtBrush;
+ }
+
+ if (NSColor *patternColor = [color colorUsingColorSpaceName:NSPatternColorSpace]) {
+ NSImage *patternImage = patternColor.patternImage;
+ const QSizeF sz(patternImage.size.width, patternImage.size.height);
+ // FIXME: QBrush is not resolution independent (QTBUG-49774)
+ qtBrush.setTexture(qt_mac_toQPixmap(patternImage, sz));
+ } else {
+ qtBrush.setStyle(Qt::SolidPattern);
+ qtBrush.setColor(qt_mac_toQColor(color));
+ }
+ return qtBrush;
+}
+#endif
+
+// ---------------------- Color Management ----------------------
+
+static CGColorSpaceRef m_genericColorSpace = 0;
+static QHash<uint32_t, CGColorSpaceRef> m_displayColorSpaceHash;
+static bool m_postRoutineRegistered = false;
+
+static void qt_mac_cleanUpMacColorSpaces()
+{
+ if (m_genericColorSpace) {
+ CFRelease(m_genericColorSpace);
+ m_genericColorSpace = 0;
+ }
+ QHash<uint32_t, CGColorSpaceRef>::const_iterator it = m_displayColorSpaceHash.constBegin();
+ while (it != m_displayColorSpaceHash.constEnd()) {
+ if (it.value())
+ CFRelease(it.value());
+ ++it;
+ }
+ m_displayColorSpaceHash.clear();
+}
+
+static CGColorSpaceRef qt_mac_displayColorSpace(const QWindow *window)
+{
+ CGColorSpaceRef colorSpace = 0;
+ uint32_t displayID = 0;
+
+#ifdef Q_OS_MACOS
+ if (window == 0) {
+ displayID = CGMainDisplayID();
+ } else {
+ displayID = CGMainDisplayID();
+ /*
+ ### get correct display
+ const QRect &qrect = window->geometry();
+ CGRect rect = CGRectMake(qrect.x(), qrect.y(), qrect.width(), qrect.height());
+ CGDisplayCount throwAway;
+ CGDisplayErr dErr = CGGetDisplaysWithRect(rect, 1, &displayID, &throwAway);
+ if (dErr != kCGErrorSuccess)
+ return macDisplayColorSpace(0); // fall back on main display
+ */
+ }
+ if ((colorSpace = m_displayColorSpaceHash.value(displayID)))
+ return colorSpace;
+
+ colorSpace = CGDisplayCopyColorSpace(displayID);
+#else
+ Q_UNUSED(window);
+#endif
+
+ if (colorSpace == 0)
+ colorSpace = CGColorSpaceCreateDeviceRGB();
+
+ m_displayColorSpaceHash.insert(displayID, colorSpace);
+ if (!m_postRoutineRegistered) {
+ m_postRoutineRegistered = true;
+ qAddPostRoutine(qt_mac_cleanUpMacColorSpaces);
+ }
+ return colorSpace;
+}
+
+CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *paintDevice)
+{
+ Q_UNUSED(paintDevice);
+
+ // FIXME: Move logic into each paint device once Qt has support for color spaces
+ return qt_mac_displayColorSpace(0);
+
+ // The following code seems to take care of QWidget, but in reality doesn't, as
+ // qt_mac_displayColorSpace ignores the argument and always uses the main display.
+#if 0
+ bool isWidget = (paintDevice->devType() == QInternal::Widget);
+ return qt_mac_displayColorSpace(isWidget ? static_cast<const QWidget *>(paintDevice)->window() : 0);
+#endif
+}
+
+CGColorSpaceRef qt_mac_genericColorSpace()
+{
+#if 0
+ if (!m_genericColorSpace) {
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ m_genericColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+ } else
+ {
+ m_genericColorSpace = CGColorSpaceCreateDeviceRGB();
+ }
+ if (!m_postRoutineRegistered) {
+ m_postRoutineRegistered = true;
+ qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
+ }
+ }
+ return m_genericColorSpace;
+#else
+ // Just return the main display colorspace for the moment.
+ return qt_mac_displayColorSpace(0);
+#endif
+}
+
+// ---------------------- Geometry Helpers ----------------------
+
+void qt_mac_clip_cg(CGContextRef hd, const QRegion &rgn, CGAffineTransform *orig_xform)
+{
+ CGAffineTransform old_xform = CGAffineTransformIdentity;
+ if (orig_xform) { //setup xforms
+ old_xform = CGContextGetCTM(hd);
+ CGContextConcatCTM(hd, CGAffineTransformInvert(old_xform));
+ CGContextConcatCTM(hd, *orig_xform);
+ }
+
+ //do the clipping
+ CGContextBeginPath(hd);
+ if (rgn.isEmpty()) {
+ CGContextAddRect(hd, CGRectMake(0, 0, 0, 0));
+ } else {
+ for (const QRect &r : rgn) {
+ CGRect mac_r = CGRectMake(r.x(), r.y(), r.width(), r.height());
+ CGContextAddRect(hd, mac_r);
+ }
+ }
+ CGContextClip(hd);
+
+ if (orig_xform) {//reset xforms
+ CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd)));
+ CGContextConcatCTM(hd, old_xform);
+ }
+}
+
+// move to QRegion?
+void qt_mac_scale_region(QRegion *region, qreal scaleFactor)
+{
+ if (!region || !region->rectCount())
+ return;
+
+ QVector<QRect> scaledRects;
+ scaledRects.reserve(region->rectCount());
+
+ for (const QRect &rect : *region)
+ scaledRects.append(QRect(rect.topLeft() * scaleFactor, rect.size() * scaleFactor));
+
+ region->setRects(&scaledRects[0], scaledRects.count());
+}
+
+// ---------------------- QMacCGContext ----------------------
+
+QMacCGContext::QMacCGContext(QPaintDevice *paintDevice) : context(0)
+{
+ // In Qt 5, QWidget and QPixmap (and QImage) paint devices are all QImages under the hood.
+ QImage *image = 0;
+ if (paintDevice->devType() == QInternal::Image) {
+ image = static_cast<QImage *>(paintDevice);
+ } else if (paintDevice->devType() == QInternal::Pixmap) {
+
+ const QPixmap *pm = static_cast<const QPixmap*>(paintDevice);
+ QPlatformPixmap *data = const_cast<QPixmap *>(pm)->data_ptr().data();
+ if (data && data->classId() == QPlatformPixmap::RasterClass) {
+ image = data->buffer();
+ } else {
+ qDebug("QMacCGContext: Unsupported pixmap class");
+ }
+ } else if (paintDevice->devType() == QInternal::Widget) {
+ // TODO test: image = static_cast<QImage *>(static_cast<const QWidget *>(paintDevice)->backingStore()->paintDevice());
+ qDebug("QMacCGContext: not implemented: Widget class");
+ }
+
+ if (!image)
+ return; // Context type not supported.
+
+ CGColorSpaceRef colorspace = qt_mac_colorSpaceForDeviceType(paintDevice);
+ uint flags = kCGImageAlphaPremultipliedFirst;
+ flags |= kCGBitmapByteOrder32Host;
+
+ context = CGBitmapContextCreate(image->bits(), image->width(), image->height(),
+ 8, image->bytesPerLine(), colorspace, flags);
+ CGContextTranslateCTM(context, 0, image->height());
+ CGContextScaleCTM(context, 1, -1);
+}
+
+QMacCGContext::QMacCGContext(QPainter *painter) : context(0)
+{
+ QPaintEngine *paintEngine = painter->paintEngine();
+
+ // Handle the case of QMacPrintEngine, which has an internal QCoreGraphicsPaintEngine
+ while (QPaintEngine *aggregateEngine = QPaintEnginePrivate::get(paintEngine)->aggregateEngine())
+ paintEngine = aggregateEngine;
+
+ paintEngine->syncState();
+
+ if (Qt::HANDLE handle = QPaintEnginePrivate::get(paintEngine)->nativeHandle()) {
+ context = static_cast<CGContextRef>(handle);
+ return;
+ }
+
+ int devType = painter->device()->devType();
+ if (paintEngine->type() == QPaintEngine::Raster
+ && (devType == QInternal::Widget ||
+ devType == QInternal::Pixmap ||
+ devType == QInternal::Image)) {
+
+ CGColorSpaceRef colorspace = qt_mac_colorSpaceForDeviceType(paintEngine->paintDevice());
+ uint flags = kCGImageAlphaPremultipliedFirst;
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ flags |= kCGBitmapByteOrder32Host;
+#endif
+ const QImage *image = static_cast<const QImage *>(paintEngine->paintDevice());
+
+ context = CGBitmapContextCreate((void *)image->bits(), image->width(), image->height(),
+ 8, image->bytesPerLine(), colorspace, flags);
+
+ // Invert y axis
+ CGContextTranslateCTM(context, 0, image->height());
+ CGContextScaleCTM(context, 1, -1);
+
+ const qreal devicePixelRatio = image->devicePixelRatio();
+
+ if (devType == QInternal::Widget) {
+ // Set the clip rect which is an intersection of the system clip
+ // and the painter clip. To make matters more interesting these
+ // are in device pixels and device-independent pixels, respectively.
+ QRegion clip = painter->paintEngine()->systemClip(); // get system clip in device pixels
+ QTransform native = painter->deviceTransform(); // get device transform. dx/dy is in device pixels
+
+ if (painter->hasClipping()) {
+ QRegion r = painter->clipRegion(); // get painter clip, which is in device-independent pixels
+ qt_mac_scale_region(&r, devicePixelRatio); // scale painter clip to device pixels
+ r.translate(native.dx(), native.dy());
+ if (clip.isEmpty())
+ clip = r;
+ else
+ clip &= r;
+ }
+ qt_mac_clip_cg(context, clip, 0); // clip in device pixels
+
+ // Scale the context so that painting happens in device-independent pixels
+ CGContextScaleCTM(context, devicePixelRatio, devicePixelRatio);
+ CGContextTranslateCTM(context, native.dx() / devicePixelRatio, native.dy() / devicePixelRatio);
+ } else {
+ // Scale to paint in device-independent pixels
+ CGContextScaleCTM(context, devicePixelRatio, devicePixelRatio);
+ }
+ } else {
+ qDebug() << "QMacCGContext:: Unsupported painter devtype type" << devType;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcoregraphics_p.h b/src/gui/painting/qcoregraphics_p.h
new file mode 100644
index 0000000000..ab2579387e
--- /dev/null
+++ b/src/gui/painting/qcoregraphics_p.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOREGRAPHICS_P_H
+#define QCOREGRAPHICS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qtguiglobal_p.h>
+#include <QtGui/qregion.h>
+#include <QtGui/qpalette.h>
+
+#include <CoreGraphics/CoreGraphics.h>
+#ifdef Q_OS_MACOS
+#include <AppKit/AppKit.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_OS_MACOS
+Q_GUI_EXPORT NSImage *qt_mac_create_nsimage(const QPixmap &pm);
+Q_GUI_EXPORT NSImage *qt_mac_create_nsimage(const QIcon &icon);
+Q_GUI_EXPORT QPixmap qt_mac_toQPixmap(const NSImage *image, const QSizeF &size);
+#endif
+Q_GUI_EXPORT CGImageRef qt_mac_toCGImage(const QImage &qImage);
+Q_GUI_EXPORT CGImageRef qt_mac_toCGImageMask(const QImage &qImage);
+Q_GUI_EXPORT QImage qt_mac_toQImage(CGImageRef image);
+
+Q_GUI_EXPORT OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage);
+
+Q_GUI_EXPORT CGColorSpaceRef qt_mac_genericColorSpace();
+Q_GUI_EXPORT CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *paintDevice);
+
+Q_GUI_EXPORT void qt_mac_clip_cg(CGContextRef hd, const QRegion &rgn, CGAffineTransform *orig_xform);
+
+#ifdef Q_OS_MACOS
+Q_GUI_EXPORT QColor qt_mac_toQColor(const NSColor *color);
+Q_GUI_EXPORT QBrush qt_mac_toQBrush(const NSColor *color, QPalette::ColorGroup colorGroup = QPalette::Normal);
+#endif
+Q_GUI_EXPORT QColor qt_mac_toQColor(CGColorRef color);
+Q_GUI_EXPORT QBrush qt_mac_toQBrush(CGColorRef color);
+
+class Q_GUI_EXPORT QMacCGContext
+{
+public:
+ inline QMacCGContext() { context = 0; }
+ QMacCGContext(QPaintDevice *pdev);
+ QMacCGContext(QPainter *p);
+ inline QMacCGContext(CGContextRef cg, bool takeOwnership = false) {
+ context = cg;
+ if (!takeOwnership)
+ CGContextRetain(context);
+ }
+ inline QMacCGContext(const QMacCGContext &copy) : context(0) { *this = copy; }
+ inline ~QMacCGContext() {
+ if (context)
+ CGContextRelease(context);
+ }
+ inline bool isNull() const { return context; }
+ inline operator CGContextRef() { return context; }
+ inline QMacCGContext &operator=(const QMacCGContext &copy) {
+ if (context)
+ CGContextRelease(context);
+ context = copy.context;
+ CGContextRetain(context);
+ return *this;
+ }
+ inline QMacCGContext &operator=(CGContextRef cg) {
+ if (context)
+ CGContextRelease(context);
+ context = cg;
+ CGContextRetain(context); //we do not take ownership
+ return *this;
+ }
+
+private:
+ CGContextRef context;
+};
+
+QT_END_NAMESPACE
+
+#endif // QCOREGRAPHICS_P_H
diff --git a/src/gui/painting/qcosmeticstroker_p.h b/src/gui/painting/qcosmeticstroker_p.h
index b22e2c2cf5..68f4e00cdc 100644
--- a/src/gui/painting/qcosmeticstroker_p.h
+++ b/src/gui/painting/qcosmeticstroker_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include <private/qdrawhelper_p.h>
#include <private/qvectorpath_p.h>
#include <private/qpaintengine_raster_p.h>
diff --git a/src/gui/painting/qcssutil.cpp b/src/gui/painting/qcssutil.cpp
index a826532b43..2d514e14e0 100644
--- a/src/gui/painting/qcssutil.cpp
+++ b/src/gui/painting/qcssutil.cpp
@@ -197,7 +197,7 @@ void qDrawEdge(QPainter *p, qreal x1, qreal y1, qreal x2, qreal y2, qreal dw1, q
if ((style == BorderStyle_Outset && (edge == TopEdge || edge == LeftEdge))
|| (style == BorderStyle_Inset && (edge == BottomEdge || edge == RightEdge)))
c = c.color().lighter();
- // fall through!
+ Q_FALLTHROUGH();
case BorderStyle_Solid: {
p->setPen(Qt::NoPen);
p->setBrush(c);
diff --git a/src/gui/painting/qdatabuffer_p.h b/src/gui/painting/qdatabuffer_p.h
index f5a3fa1432..77b5be0c4c 100644
--- a/src/gui/painting/qdatabuffer_p.h
+++ b/src/gui/painting/qdatabuffer_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include "QtCore/qbytearray.h"
#include <stdlib.h>
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
index 1c772ef232..3e01d34cb2 100644
--- a/src/gui/painting/qdrawhelper.cpp
+++ b/src/gui/painting/qdrawhelper.cpp
@@ -168,7 +168,7 @@ template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB66
template<QImage::Format Format>
static const uint *QT_FASTCALL convertToRGB32(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
Q_CONSTEXPR uint redMask = ((1 << redWidth<Format>()) - 1);
Q_CONSTEXPR uint greenMask = ((1 << greenWidth<Format>()) - 1);
@@ -198,7 +198,7 @@ static const uint *QT_FASTCALL convertToRGB32(uint *buffer, const uint *src, int
template<QImage::Format Format>
static const QRgba64 *QT_FASTCALL convertToRGB64(QRgba64 *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
Q_CONSTEXPR uint redMask = ((1 << redWidth<Format>()) - 1);
Q_CONSTEXPR uint greenMask = ((1 << greenWidth<Format>()) - 1);
@@ -228,7 +228,7 @@ static const QRgba64 *QT_FASTCALL convertToRGB64(QRgba64 *buffer, const uint *sr
template<QImage::Format Format>
static const uint *QT_FASTCALL convertARGBPMToARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
Q_CONSTEXPR uint alphaMask = ((1 << alphaWidth<Format>()) - 1);
Q_CONSTEXPR uint redMask = ((1 << redWidth<Format>()) - 1);
@@ -282,7 +282,7 @@ static const uint *QT_FASTCALL convertARGBPMToARGB32PM(uint *buffer, const uint
template<QImage::Format Format>
static const QRgba64 *QT_FASTCALL convertARGBPMToARGB64PM(QRgba64 *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
Q_CONSTEXPR uint alphaMask = ((1 << alphaWidth<Format>()) - 1);
Q_CONSTEXPR uint redMask = ((1 << redWidth<Format>()) - 1);
@@ -336,48 +336,110 @@ static const QRgba64 *QT_FASTCALL convertARGBPMToARGB64PM(QRgba64 *buffer, const
template<QImage::Format Format, bool fromRGB>
static const uint *QT_FASTCALL convertRGBFromARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *dither)
{
- Q_CONSTEXPR uint rMask = ((1 << redWidth<Format>()) - 1);
- Q_CONSTEXPR uint gMask = ((1 << greenWidth<Format>()) - 1);
- Q_CONSTEXPR uint bMask = ((1 << blueWidth<Format>()) - 1);
+ Q_CONSTEXPR uchar rWidth = redWidth<Format>();
+ Q_CONSTEXPR uchar gWidth = greenWidth<Format>();
+ Q_CONSTEXPR uchar bWidth = blueWidth<Format>();
- Q_CONSTEXPR uchar rRightShift = 24 - redWidth<Format>();
- Q_CONSTEXPR uchar gRightShift = 16 - greenWidth<Format>();
- Q_CONSTEXPR uchar bRightShift = 8 - blueWidth<Format>();
+ // RGB32 -> RGB888 is not a precision loss.
+ if (!dither || (rWidth == 8 && gWidth == 8 && bWidth == 8)) {
+ Q_CONSTEXPR uint rMask = (1 << rWidth) - 1;
+ Q_CONSTEXPR uint gMask = (1 << gWidth) - 1;
+ Q_CONSTEXPR uint bMask = (1 << bWidth) - 1;
- for (int i = 0; i < count; ++i) {
- const uint c = fromRGB ? src[i] : qUnpremultiply(src[i]);
- const uint r = ((c >> rRightShift) & rMask) << redShift<Format>();
- const uint g = ((c >> gRightShift) & gMask) << greenShift<Format>();
- const uint b = ((c >> bRightShift) & bMask) << blueShift<Format>();
- buffer[i] = r | g | b;
+ Q_CONSTEXPR uchar rRightShift = 24 - rWidth;
+ Q_CONSTEXPR uchar gRightShift = 16 - gWidth;
+ Q_CONSTEXPR uchar bRightShift = 8 - bWidth;
+
+ for (int i = 0; i < count; ++i) {
+ const uint c = fromRGB ? src[i] : qUnpremultiply(src[i]);
+ const uint r = ((c >> rRightShift) & rMask) << redShift<Format>();
+ const uint g = ((c >> gRightShift) & gMask) << greenShift<Format>();
+ const uint b = ((c >> bRightShift) & bMask) << blueShift<Format>();
+ buffer[i] = r | g | b;
+ }
+ } else {
+ // We do ordered dither by using a rounding conversion, but instead of
+ // adding half of input precision, we add the adjusted result from the
+ // bayer matrix before narrowing.
+ // Note: Rounding conversion in itself is different from the naive
+ // conversion we do above for non-dithering.
+ const uint *bayer_line = qt_bayer_matrix[dither->y & 15];
+ for (int i = 0; i < count; ++i) {
+ const uint c = fromRGB ? src[i] : qUnpremultiply(src[i]);
+ const int d = bayer_line[(dither->x + i) & 15];
+ const int dr = d - ((d + 1) >> rWidth);
+ const int dg = d - ((d + 1) >> gWidth);
+ const int db = d - ((d + 1) >> bWidth);
+ int r = qRed(c);
+ int g = qGreen(c);
+ int b = qBlue(c);
+ r = (r + ((dr - r) >> rWidth) + 1) >> (8 - rWidth);
+ g = (g + ((dg - g) >> gWidth) + 1) >> (8 - gWidth);
+ b = (b + ((db - b) >> bWidth) + 1) >> (8 - bWidth);
+ buffer[i] = (r << redShift<Format>())
+ | (g << greenShift<Format>())
+ | (b << blueShift<Format>());
+ }
}
return buffer;
}
template<QImage::Format Format, bool fromRGB>
static const uint *QT_FASTCALL convertARGBPMFromARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *dither)
{
- Q_CONSTEXPR uint aMask = ((1 << alphaWidth<Format>()) - 1);
- Q_CONSTEXPR uint rMask = ((1 << redWidth<Format>()) - 1);
- Q_CONSTEXPR uint gMask = ((1 << greenWidth<Format>()) - 1);
- Q_CONSTEXPR uint bMask = ((1 << blueWidth<Format>()) - 1);
+ Q_CONSTEXPR uchar aWidth = alphaWidth<Format>();
+ Q_CONSTEXPR uchar rWidth = redWidth<Format>();
+ Q_CONSTEXPR uchar gWidth = greenWidth<Format>();
+ Q_CONSTEXPR uchar bWidth = blueWidth<Format>();
- Q_CONSTEXPR uchar aRightShift = 32 - alphaWidth<Format>();
- Q_CONSTEXPR uchar rRightShift = 24 - redWidth<Format>();
- Q_CONSTEXPR uchar gRightShift = 16 - greenWidth<Format>();
- Q_CONSTEXPR uchar bRightShift = 8 - blueWidth<Format>();
+ if (!dither) {
+ Q_CONSTEXPR uint aMask = (1 << aWidth) - 1;
+ Q_CONSTEXPR uint rMask = (1 << rWidth) - 1;
+ Q_CONSTEXPR uint gMask = (1 << gWidth) - 1;
+ Q_CONSTEXPR uint bMask = (1 << bWidth) - 1;
- Q_CONSTEXPR uint aOpaque = (0xff & aMask) << alphaShift<Format>();
- for (int i = 0; i < count; ++i) {
- const uint c = src[i];
- const uint a = fromRGB ? aOpaque : (((c >> aRightShift) & aMask) << alphaShift<Format>());
- const uint r = ((c >> rRightShift) & rMask) << redShift<Format>();
- const uint g = ((c >> gRightShift) & gMask) << greenShift<Format>();
- const uint b = ((c >> bRightShift) & bMask) << blueShift<Format>();
- buffer[i] = a | r | g | b;
+ Q_CONSTEXPR uchar aRightShift = 32 - aWidth;
+ Q_CONSTEXPR uchar rRightShift = 24 - rWidth;
+ Q_CONSTEXPR uchar gRightShift = 16 - gWidth;
+ Q_CONSTEXPR uchar bRightShift = 8 - bWidth;
+
+ Q_CONSTEXPR uint aOpaque = aMask << alphaShift<Format>();
+ for (int i = 0; i < count; ++i) {
+ const uint c = src[i];
+ const uint a = fromRGB ? aOpaque : (((c >> aRightShift) & aMask) << alphaShift<Format>());
+ const uint r = ((c >> rRightShift) & rMask) << redShift<Format>();
+ const uint g = ((c >> gRightShift) & gMask) << greenShift<Format>();
+ const uint b = ((c >> bRightShift) & bMask) << blueShift<Format>();
+ buffer[i] = a | r | g | b;
+ }
+ } else {
+ const uint *bayer_line = qt_bayer_matrix[dither->y & 15];
+ for (int i = 0; i < count; ++i) {
+ const uint c = src[i];
+ const int d = bayer_line[(dither->x + i) & 15];
+ const int da = d - ((d + 1) >> aWidth);
+ const int dr = d - ((d + 1) >> rWidth);
+ const int dg = d - ((d + 1) >> gWidth);
+ const int db = d - ((d + 1) >> bWidth);
+ int a = qAlpha(c);
+ int r = qRed(c);
+ int g = qGreen(c);
+ int b = qBlue(c);
+ if (fromRGB)
+ a = (1 << aWidth) - 1;
+ else
+ a = (a + ((da - a) >> aWidth) + 1) >> (8 - aWidth);
+ r = (r + ((dr - r) >> rWidth) + 1) >> (8 - rWidth);
+ g = (g + ((dg - g) >> gWidth) + 1) >> (8 - gWidth);
+ b = (b + ((db - b) >> bWidth) + 1) >> (8 - bWidth);
+ buffer[i] = (a << alphaShift<Format>())
+ | (r << redShift<Format>())
+ | (g << greenShift<Format>())
+ | (b << blueShift<Format>());
+ }
}
return buffer;
}
@@ -418,35 +480,35 @@ template<QImage::Format Format> Q_DECL_CONSTEXPR static inline QPixelLayout pixe
// To convert in place, let 'dest' and 'src' be the same.
static const uint *QT_FASTCALL convertIndexedToARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *clut)
+ const QVector<QRgb> *clut, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
- buffer[i] = qPremultiply(clut[src[i]]);
+ buffer[i] = qPremultiply(clut->at(src[i]));
return buffer;
}
static const QRgba64 *QT_FASTCALL convertIndexedToARGB64PM(QRgba64 *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *clut)
+ const QVector<QRgb> *clut, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
- buffer[i] = QRgba64::fromArgb32(clut[src[i]]).premultiplied();
+ buffer[i] = QRgba64::fromArgb32(clut->at(src[i])).premultiplied();
return buffer;
}
static const uint *QT_FASTCALL convertPassThrough(uint *, const uint *src, int,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
return src;
}
static const uint *QT_FASTCALL convertARGB32ToARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
return qt_convertARGB32ToARGB32PM(buffer, src, count);
}
static const uint *QT_FASTCALL convertRGBA8888PMToARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = RGBA2ARGB(src[i]);
@@ -454,13 +516,13 @@ static const uint *QT_FASTCALL convertRGBA8888PMToARGB32PM(uint *buffer, const u
}
static const uint *QT_FASTCALL convertRGBA8888ToARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
return qt_convertRGBA8888ToARGB32PM(buffer, src, count);
}
static const uint *QT_FASTCALL convertAlpha8ToRGB32(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = qRgba(0, 0, 0, src[i]);
@@ -468,7 +530,7 @@ static const uint *QT_FASTCALL convertAlpha8ToRGB32(uint *buffer, const uint *sr
}
static const uint *QT_FASTCALL convertGrayscale8ToRGB32(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = qRgb(src[i], src[i], src[i]);
@@ -476,7 +538,7 @@ static const uint *QT_FASTCALL convertGrayscale8ToRGB32(uint *buffer, const uint
}
static const QRgba64 *QT_FASTCALL convertAlpha8ToRGB64(QRgba64 *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = QRgba64::fromRgba(0, 0, 0, src[i]);
@@ -484,7 +546,7 @@ static const QRgba64 *QT_FASTCALL convertAlpha8ToRGB64(QRgba64 *buffer, const ui
}
static const QRgba64 *QT_FASTCALL convertGrayscale8ToRGB64(QRgba64 *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = QRgba64::fromRgba(src[i], src[i], src[i], 255);
@@ -492,7 +554,7 @@ static const QRgba64 *QT_FASTCALL convertGrayscale8ToRGB64(QRgba64 *buffer, cons
}
static const uint *QT_FASTCALL convertARGB32FromARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = qUnpremultiply(src[i]);
@@ -500,7 +562,7 @@ static const uint *QT_FASTCALL convertARGB32FromARGB32PM(uint *buffer, const uin
}
static const uint *QT_FASTCALL convertRGBA8888PMFromARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = ARGB2RGBA(src[i]);
@@ -551,7 +613,7 @@ static inline void qConvertARGB32PMToARGB64PM_sse2(QRgba64 *buffer, const uint *
#endif
static const QRgba64 *QT_FASTCALL convertRGB32ToRGB64(QRgba64 *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
#ifdef __SSE2__
qConvertARGB32PMToARGB64PM_sse2<false, true>(buffer, src, count);
@@ -563,7 +625,7 @@ static const QRgba64 *QT_FASTCALL convertRGB32ToRGB64(QRgba64 *buffer, const uin
}
static const QRgba64 *QT_FASTCALL convertARGB32ToARGB64PM(QRgba64 *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
#ifdef __SSE2__
qConvertARGB32PMToARGB64PM_sse2<false, false>(buffer, src, count);
@@ -577,7 +639,7 @@ static const QRgba64 *QT_FASTCALL convertARGB32ToARGB64PM(QRgba64 *buffer, const
}
static const QRgba64 *QT_FASTCALL convertARGB32PMToARGB64PM(QRgba64 *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
#ifdef __SSE2__
qConvertARGB32PMToARGB64PM_sse2<false, false>(buffer, src, count);
@@ -589,7 +651,7 @@ static const QRgba64 *QT_FASTCALL convertARGB32PMToARGB64PM(QRgba64 *buffer, con
}
static const QRgba64 *QT_FASTCALL convertRGBA8888ToARGB64PM(QRgba64 *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
#ifdef __SSE2__
qConvertARGB32PMToARGB64PM_sse2<true, false>(buffer, src, count);
@@ -603,7 +665,7 @@ static const QRgba64 *QT_FASTCALL convertRGBA8888ToARGB64PM(QRgba64 *buffer, con
}
static const QRgba64 *QT_FASTCALL convertRGBA8888PMToARGB64PM(QRgba64 *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
#ifdef __SSE2__
qConvertARGB32PMToARGB64PM_sse2<true, false>(buffer, src, count);
@@ -615,7 +677,7 @@ static const QRgba64 *QT_FASTCALL convertRGBA8888PMToARGB64PM(QRgba64 *buffer, c
}
static const uint *QT_FASTCALL convertRGBA8888FromARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = ARGB2RGBA(qUnpremultiply(src[i]));
@@ -623,7 +685,7 @@ static const uint *QT_FASTCALL convertRGBA8888FromARGB32PM(uint *buffer, const u
}
static const uint *QT_FASTCALL convertRGBXFromRGB32(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = ARGB2RGBA(0xff000000 | src[i]);
@@ -631,7 +693,7 @@ static const uint *QT_FASTCALL convertRGBXFromRGB32(uint *buffer, const uint *sr
}
static const uint *QT_FASTCALL convertRGBXFromARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = ARGB2RGBA(0xff000000 | qUnpremultiply(src[i]));
@@ -640,10 +702,28 @@ static const uint *QT_FASTCALL convertRGBXFromARGB32PM(uint *buffer, const uint
template<QtPixelOrder PixelOrder>
static const uint *QT_FASTCALL convertA2RGB30PMToARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *dither)
{
- for (int i = 0; i < count; ++i)
- buffer[i] = qConvertA2rgb30ToArgb32<PixelOrder>(src[i]);
+ if (!dither) {
+ for (int i = 0; i < count; ++i)
+ buffer[i] = qConvertA2rgb30ToArgb32<PixelOrder>(src[i]);
+ } else {
+ for (int i = 0; i < count; ++i) {
+ const uint c = src[i];
+ short d10 = (qt_bayer_matrix[dither->y & 15][(dither->x + i) & 15] << 2);
+ short a10 = (c >> 30) * 0x155;
+ short r10 = ((c >> 20) & 0x3ff);
+ short g10 = ((c >> 10) & 0x3ff);
+ short b10 = (c & 0x3ff);
+ if (PixelOrder == PixelOrderBGR)
+ std::swap(r10, b10);
+ short a8 = (a10 + ((d10 - a10) >> 8)) >> 2;
+ short r8 = (r10 + ((d10 - r10) >> 8)) >> 2;
+ short g8 = (g10 + ((d10 - g10) >> 8)) >> 2;
+ short b8 = (b10 + ((d10 - b10) >> 8)) >> 2;
+ buffer[i] = qRgba(r8, g8, b8, a8);
+ }
+ }
return buffer;
}
@@ -693,7 +773,7 @@ static inline void qConvertA2RGB30PMToARGB64PM_sse2(QRgba64 *buffer, const uint
template<QtPixelOrder PixelOrder>
static const QRgba64 *QT_FASTCALL convertA2RGB30PMToARGB64PM(QRgba64 *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
#ifdef __SSE2__
qConvertA2RGB30PMToARGB64PM_sse2<PixelOrder>(buffer, src, count);
@@ -706,7 +786,7 @@ static const QRgba64 *QT_FASTCALL convertA2RGB30PMToARGB64PM(QRgba64 *buffer, co
template<QtPixelOrder PixelOrder>
static const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = qConvertArgb32ToA2rgb30<PixelOrder>(src[i]);
@@ -715,7 +795,7 @@ static const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM(uint *buffer, const
template<QtPixelOrder PixelOrder>
static const uint *QT_FASTCALL convertRGB30FromRGB32(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = qConvertRgb32ToRgb30<PixelOrder>(src[i]);
@@ -724,7 +804,7 @@ static const uint *QT_FASTCALL convertRGB30FromRGB32(uint *buffer, const uint *s
template<QtPixelOrder PixelOrder>
static const uint *QT_FASTCALL convertRGB30FromARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = qConvertRgb32ToRgb30<PixelOrder>(qUnpremultiply(src[i]));
@@ -732,7 +812,7 @@ static const uint *QT_FASTCALL convertRGB30FromARGB32PM(uint *buffer, const uint
}
static const uint *QT_FASTCALL convertAlpha8FromARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = qAlpha(src[i]);
@@ -740,7 +820,7 @@ static const uint *QT_FASTCALL convertAlpha8FromARGB32PM(uint *buffer, const uin
}
static const uint *QT_FASTCALL convertGrayscale8FromRGB32(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = qGray(src[i]);
@@ -748,7 +828,7 @@ static const uint *QT_FASTCALL convertGrayscale8FromRGB32(uint *buffer, const ui
}
static const uint *QT_FASTCALL convertGrayscale8FromARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = qGray(qUnpremultiply(src[i]));
@@ -756,7 +836,10 @@ static const uint *QT_FASTCALL convertGrayscale8FromARGB32PM(uint *buffer, const
}
template <QPixelLayout::BPP bpp> static
-uint QT_FASTCALL fetchPixel(const uchar *src, int index);
+uint QT_FASTCALL fetchPixel(const uchar *, int)
+{
+ Q_UNREACHABLE();
+}
template <>
inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP1LSB>(const uchar *src, int index)
@@ -1037,7 +1120,7 @@ static uint *QT_FASTCALL destFetch(uint *buffer, QRasterBuffer *rasterBuffer, in
{
const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format];
const uint *ptr = qFetchPixels[layout->bpp](buffer, rasterBuffer->scanLine(y), x, length);
- return const_cast<uint *>(layout->convertToARGB32PM(buffer, ptr, length, layout, 0));
+ return const_cast<uint *>(layout->convertToARGB32PM(buffer, ptr, length, 0, 0));
}
static QRgba64 *QT_FASTCALL destFetch64(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
@@ -1045,14 +1128,14 @@ static QRgba64 *QT_FASTCALL destFetch64(QRgba64 *buffer, QRasterBuffer *rasterBu
const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format];
uint buffer32[buffer_size];
const uint *ptr = qFetchPixels[layout->bpp](buffer32, rasterBuffer->scanLine(y), x, length);
- return const_cast<QRgba64 *>(layout->convertToARGB64PM(buffer, ptr, length, layout, 0));
+ return const_cast<QRgba64 *>(layout->convertToARGB64PM(buffer, ptr, length, 0, 0));
}
static QRgba64 *QT_FASTCALL destFetch64uint32(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
{
const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format];
const uint *src = ((const uint *)rasterBuffer->scanLine(y)) + x;
- return const_cast<QRgba64 *>(layout->convertToARGB64PM(buffer, src, length, layout, 0));
+ return const_cast<QRgba64 *>(layout->convertToARGB64PM(buffer, src, length, 0, 0));
}
static DestFetchProc destFetchProc[QImage::NImageFormats] =
@@ -1219,9 +1302,9 @@ static void QT_FASTCALL destStore(QRasterBuffer *rasterBuffer, int x, int y, con
int l = qMin(length, buffer_size);
const uint *ptr = 0;
if (!layout->premultiplied && !layout->alphaWidth)
- ptr = layout->convertFromRGB32(buf, buffer, l, layout, 0);
+ ptr = layout->convertFromRGB32(buf, buffer, l, 0, 0);
else
- ptr = layout->convertFromARGB32PM(buf, buffer, l, layout, 0);
+ ptr = layout->convertFromARGB32PM(buf, buffer, l, 0, 0);
store(dest, ptr, x, l);
length -= l;
buffer += l;
@@ -1247,9 +1330,9 @@ static void QT_FASTCALL destStore64(QRasterBuffer *rasterBuffer, int x, int y, c
const uint *ptr = 0;
convertFromRgb64(buf, buffer, l);
if (!layout->premultiplied && !layout->alphaWidth)
- ptr = layout->convertFromRGB32(buf, buf, l, layout, 0);
+ ptr = layout->convertFromRGB32(buf, buf, l, 0, 0);
else
- ptr = layout->convertFromARGB32PM(buf, buf, l, layout, 0);
+ ptr = layout->convertFromARGB32PM(buf, buf, l, 0, 0);
store(dest, ptr, x, l);
length -= l;
buffer += l;
@@ -1436,8 +1519,7 @@ static const uint *QT_FASTCALL fetchUntransformed(uint *buffer, const Operator *
{
const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
const uint *ptr = qFetchPixels[layout->bpp](buffer, data->texture.scanLine(y), x, length);
- const QRgb *clut = data->texture.colorTable ? data->texture.colorTable->constData() : 0;
- return layout->convertToARGB32PM(buffer, ptr, length, layout, clut);
+ return layout->convertToARGB32PM(buffer, ptr, length, data->texture.colorTable, 0);
}
static const uint *QT_FASTCALL fetchUntransformedARGB32PM(uint *, const Operator *,
@@ -1465,103 +1547,21 @@ static const QRgba64 *QT_FASTCALL fetchUntransformed64(QRgba64 *buffer, const Op
const QSpanData *data, int y, int x, int length)
{
const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
- const QRgb *clut = data->texture.colorTable ? data->texture.colorTable->constData() : 0;
if (layout->bpp != QPixelLayout::BPP32) {
uint buffer32[buffer_size];
const uint *ptr = qFetchPixels[layout->bpp](buffer32, data->texture.scanLine(y), x, length);
- return layout->convertToARGB64PM(buffer, ptr, length, layout, clut);
+ return layout->convertToARGB64PM(buffer, ptr, length, data->texture.colorTable, 0);
} else {
const uint *src = (const uint *)data->texture.scanLine(y) + x;
- return layout->convertToARGB64PM(buffer, src, length, layout, clut);
- }
-}
-
-// blendType is either BlendTransformed or BlendTransformedTiled
-template<TextureBlendType blendType>
-static const uint *QT_FASTCALL fetchTransformedARGB32PM(uint *buffer, const Operator *, const QSpanData *data,
- int y, int x, int length)
-{
- int image_width = data->texture.width;
- int image_height = data->texture.height;
-
- const qreal cx = x + qreal(0.5);
- const qreal cy = y + qreal(0.5);
-
- const uint *end = buffer + length;
- uint *b = buffer;
- if (data->fast_matrix) {
- // The increment pr x in the scanline
- int fdx = (int)(data->m11 * fixed_scale);
- int fdy = (int)(data->m12 * fixed_scale);
-
- int fx = int((data->m21 * cy
- + data->m11 * cx + data->dx) * fixed_scale);
- int fy = int((data->m22 * cy
- + data->m12 * cx + data->dy) * fixed_scale);
-
- while (b < end) {
- int px = fx >> 16;
- int py = fy >> 16;
-
- if (blendType == BlendTransformedTiled) {
- px %= image_width;
- py %= image_height;
- if (px < 0) px += image_width;
- if (py < 0) py += image_height;
- } else {
- px = qBound(0, px, image_width - 1);
- py = qBound(0, py, image_height - 1);
- }
- *b = reinterpret_cast<const uint *>(data->texture.scanLine(py))[px];
-
- fx += fdx;
- fy += fdy;
- ++b;
- }
- } else {
- const qreal fdx = data->m11;
- const qreal fdy = data->m12;
- const qreal fdw = data->m13;
-
- qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
- qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
- qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
-
- while (b < end) {
- const qreal iw = fw == 0 ? 1 : 1 / fw;
- const qreal tx = fx * iw;
- const qreal ty = fy * iw;
- int px = int(tx) - (tx < 0);
- int py = int(ty) - (ty < 0);
-
- if (blendType == BlendTransformedTiled) {
- px %= image_width;
- py %= image_height;
- if (px < 0) px += image_width;
- if (py < 0) py += image_height;
- } else {
- px = qBound(0, px, image_width - 1);
- py = qBound(0, py, image_height - 1);
- }
- *b = reinterpret_cast<const uint *>(data->texture.scanLine(py))[px];
-
- fx += fdx;
- fy += fdy;
- fw += fdw;
- //force increment to avoid /0
- if (!fw) {
- fw += fdw;
- }
- ++b;
- }
+ return layout->convertToARGB64PM(buffer, src, length, data->texture.colorTable, 0);
}
- return buffer;
}
-template<TextureBlendType blendType> /* either BlendTransformed or BlendTransformedTiled */
+template<TextureBlendType blendType, QPixelLayout::BPP bpp>
static const uint *QT_FASTCALL fetchTransformed(uint *buffer, const Operator *, const QSpanData *data,
int y, int x, int length)
{
+ Q_STATIC_ASSERT(blendType == BlendTransformed || blendType == BlendTransformedTiled);
int image_width = data->texture.width;
int image_height = data->texture.height;
@@ -1569,9 +1569,12 @@ static const uint *QT_FASTCALL fetchTransformed(uint *buffer, const Operator *,
const qreal cy = y + qreal(0.5);
const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
- FetchPixelFunc fetch = qFetchPixel[layout->bpp];
+ if (bpp != QPixelLayout::BPPNone) // Like this to not ICE on GCC 5.3.1
+ Q_ASSERT(layout->bpp == bpp);
+ // When templated 'fetch' should be inlined at compile time:
+ const FetchPixelFunc fetch = (bpp == QPixelLayout::BPPNone) ? qFetchPixel[layout->bpp] : fetchPixel<bpp>;
- const uint *end = buffer + length;
+ uint *const end = buffer + length;
uint *b = buffer;
if (data->fast_matrix) {
// The increment pr x in the scanline
@@ -1639,8 +1642,7 @@ static const uint *QT_FASTCALL fetchTransformed(uint *buffer, const Operator *,
++b;
}
}
- const QRgb *clut = data->texture.colorTable ? data->texture.colorTable->constData() : 0;
- return layout->convertToARGB32PM(buffer, buffer, length, layout, clut);
+ return layout->convertToARGB32PM(buffer, buffer, length, data->texture.colorTable, 0);
}
template<TextureBlendType blendType> /* either BlendTransformed or BlendTransformedTiled */
@@ -1655,7 +1657,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Oper
const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
FetchPixelFunc fetch = qFetchPixel[layout->bpp];
- const QRgb *clut = data->texture.colorTable ? data->texture.colorTable->constData() : 0;
+ const QVector<QRgb> *clut = data->texture.colorTable;
uint buffer32[buffer_size];
QRgba64 *b = buffer;
@@ -1672,7 +1674,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Oper
int i = 0, j = 0;
while (i < length) {
if (j == buffer_size) {
- layout->convertToARGB64PM(b, buffer32, buffer_size, layout, clut);
+ layout->convertToARGB64PM(b, buffer32, buffer_size, clut, 0);
b += buffer_size;
j = 0;
}
@@ -1695,7 +1697,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Oper
++i; ++j;
}
if (j > 0) {
- layout->convertToARGB64PM(b, buffer32, j, layout, clut);
+ layout->convertToARGB64PM(b, buffer32, j, clut, 0);
b += j;
}
} else {
@@ -1710,7 +1712,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Oper
int i = 0, j = 0;
while (i < length) {
if (j == buffer_size) {
- layout->convertToARGB64PM(b, buffer32, buffer_size, layout, clut);
+ layout->convertToARGB64PM(b, buffer32, buffer_size, clut, 0);
b += buffer_size;
j = 0;
}
@@ -1741,7 +1743,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Oper
++i; ++j;
}
if (j > 0) {
- layout->convertToARGB64PM(b, buffer32, j, layout, clut);
+ layout->convertToARGB64PM(b, buffer32, j, clut, 0);
b += j;
}
}
@@ -1750,9 +1752,9 @@ static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Oper
/** \internal
interpolate 4 argb pixels with the distx and disty factor.
- distx and disty bust be between 0 and 16
+ distx and disty must be between 0 and 16
*/
-static inline uint interpolate_4_pixels_16(uint tl, uint tr, uint bl, uint br, int distx, int disty)
+static inline uint interpolate_4_pixels_16(uint tl, uint tr, uint bl, uint br, uint distx, uint disty)
{
uint distxy = distx * disty;
//idistx * disty = (16-distx) * disty = 16*disty - distxy
@@ -2099,30 +2101,30 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2);
const uint *s1 = (const uint *)data->texture.scanLine(y1);
const uint *s2 = (const uint *)data->texture.scanLine(y2);
- int disty = (fy & 0x0000ffff) >> 12;
+ const int disty8 = (fy & 0x0000ffff) >> 8;
+ const int disty4 = (disty8 + 0x08) >> 4;
if (blendType != BlendTransformedBilinearTiled) {
#define BILINEAR_DOWNSCALE_BOUNDS_PROLOG \
+ const qint64 min_fx = qint64(image_x1) * fixed_scale; \
+ const qint64 max_fx = qint64(image_x2) * fixed_scale; \
while (b < end) { \
int x1 = (fx >> 16); \
int x2; \
fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); \
if (x1 != x2) \
break; \
- uint tl = s1[x1]; \
- uint tr = s1[x2]; \
- uint bl = s2[x1]; \
- uint br = s2[x2]; \
- int distx = (fx & 0x0000ffff) >> 12; \
- *b = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty); \
+ uint top = s1[x1]; \
+ uint bot = s2[x1]; \
+ *b = INTERPOLATE_PIXEL_256(top, 256 - disty8, bot, disty8); \
fx += fdx; \
++b; \
} \
- uint *boundedEnd; \
+ uint *boundedEnd = end; \
if (fdx > 0) \
- boundedEnd = qMin(end, buffer + uint((image_x2 - (fx >> 16)) / data->m11)); \
- else \
- boundedEnd = qMin(end, buffer + uint((image_x1 - (fx >> 16)) / data->m11)); \
+ boundedEnd = qMin(boundedEnd, b + (max_fx - fx) / fdx); \
+ else if (fdx < 0) \
+ boundedEnd = qMin(boundedEnd, b + (min_fx - fx) / fdx); \
boundedEnd -= 3;
#if defined(__SSE2__)
@@ -2130,8 +2132,9 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
const __m128i v_256 = _mm_set1_epi16(256);
- const __m128i v_disty = _mm_set1_epi16(disty);
+ const __m128i v_disty = _mm_set1_epi16(disty4);
const __m128i v_fdx = _mm_set1_epi32(fdx*4);
+ const __m128i v_fx_r = _mm_set1_epi32(0x8);
__m128i v_fx = _mm_setr_epi32(fx, fx + fdx, fx + fdx + fdx, fx + fdx + fdx + fdx);
while (b < boundedEnd) {
@@ -2145,7 +2148,8 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
const __m128i bl = _mm_setr_epi32(s2[offset0], s2[offset1], s2[offset2], s2[offset3]);
const __m128i br = _mm_setr_epi32(s2[offset0 + 1], s2[offset1 + 1], s2[offset2 + 1], s2[offset3 + 1]);
- __m128i v_distx = _mm_srli_epi16(v_fx, 12);
+ __m128i v_distx = _mm_srli_epi16(v_fx, 8);
+ v_distx = _mm_srli_epi16(_mm_add_epi32(v_distx, v_fx_r), 4);
v_distx = _mm_shufflehi_epi16(v_distx, _MM_SHUFFLE(2,2,0,0));
v_distx = _mm_shufflelo_epi16(v_distx, _MM_SHUFFLE(2,2,0,0));
@@ -2160,48 +2164,49 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
const int16x8_t colorMask = vdupq_n_s16(0x00ff);
const int16x8_t invColorMask = vmvnq_s16(colorMask);
const int16x8_t v_256 = vdupq_n_s16(256);
- const int16x8_t v_disty = vdupq_n_s16(disty);
+ const int16x8_t v_disty = vdupq_n_s16(disty4);
const int16x8_t v_disty_ = vshlq_n_s16(v_disty, 4);
int32x4_t v_fdx = vdupq_n_s32(fdx*4);
- ptrdiff_t secondLine = reinterpret_cast<const uint *>(s2) - reinterpret_cast<const uint *>(s1);
-
- union Vect_buffer { int32x4_t vect; quint32 i[4]; };
- Vect_buffer v_fx;
-
- for (int i = 0; i < 4; i++) {
- v_fx.i[i] = fx;
- fx += fdx;
- }
+ int32x4_t v_fx = vmovq_n_s32(fx);
+ v_fx = vsetq_lane_s32(fx + fdx, v_fx, 1);
+ v_fx = vsetq_lane_s32(fx + fdx * 2, v_fx, 2);
+ v_fx = vsetq_lane_s32(fx + fdx * 3, v_fx, 3);
const int32x4_t v_ffff_mask = vdupq_n_s32(0x0000ffff);
+ const int32x4_t v_fx_r = vdupq_n_s32(0x0800);
while (b < boundedEnd) {
+ uint32x4x2_t v_top, v_bot;
- Vect_buffer tl, tr, bl, br;
-
- Vect_buffer v_fx_shifted;
- v_fx_shifted.vect = vshrq_n_s32(v_fx.vect, 16);
-
- int32x4_t v_distx = vshrq_n_s32(vandq_s32(v_fx.vect, v_ffff_mask), 12);
-
- for (int i = 0; i < 4; i++) {
- int x1 = v_fx_shifted.i[i];
- const uint *addr_tl = reinterpret_cast<const uint *>(s1) + x1;
- const uint *addr_tr = addr_tl + 1;
- tl.i[i] = *addr_tl;
- tr.i[i] = *addr_tr;
- bl.i[i] = *(addr_tl+secondLine);
- br.i[i] = *(addr_tr+secondLine);
- }
+ int x1 = (fx >> 16);
+ fx += fdx;
+ v_top = vld2q_lane_u32(s1 + x1, v_top, 0);
+ v_bot = vld2q_lane_u32(s2 + x1, v_bot, 0);
+ x1 = (fx >> 16);
+ fx += fdx;
+ v_top = vld2q_lane_u32(s1 + x1, v_top, 1);
+ v_bot = vld2q_lane_u32(s2 + x1, v_bot, 1);
+ x1 = (fx >> 16);
+ fx += fdx;
+ v_top = vld2q_lane_u32(s1 + x1, v_top, 2);
+ v_bot = vld2q_lane_u32(s2 + x1, v_bot, 2);
+ x1 = (fx >> 16);
+ fx += fdx;
+ v_top = vld2q_lane_u32(s1 + x1, v_top, 3);
+ v_bot = vld2q_lane_u32(s2 + x1, v_bot, 3);
+ int32x4_t v_distx = vshrq_n_s32(vaddq_s32(vandq_s32(v_fx, v_ffff_mask), v_fx_r), 12);
v_distx = vorrq_s32(v_distx, vshlq_n_s32(v_distx, 16));
- interpolate_4_pixels_16_neon(vreinterpretq_s16_s32(tl.vect), vreinterpretq_s16_s32(tr.vect), vreinterpretq_s16_s32(bl.vect), vreinterpretq_s16_s32(br.vect), vreinterpretq_s16_s32(v_distx), v_disty, v_disty_, colorMask, invColorMask, v_256, b);
+ interpolate_4_pixels_16_neon(
+ vreinterpretq_s16_u32(v_top.val[0]), vreinterpretq_s16_u32(v_top.val[1]),
+ vreinterpretq_s16_u32(v_bot.val[0]), vreinterpretq_s16_u32(v_bot.val[1]),
+ vreinterpretq_s16_s32(v_distx), v_disty, v_disty_,
+ colorMask, invColorMask, v_256, b);
b+=4;
- v_fx.vect = vaddq_s32(v_fx.vect, v_fdx);
+ v_fx = vaddq_s32(v_fx, v_fdx);
}
- fx = v_fx.i[0];
#endif
}
@@ -2213,8 +2218,14 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
uint tr = s1[x2];
uint bl = s2[x1];
uint br = s2[x2];
- int distx = (fx & 0x0000ffff) >> 12;
- *b = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty);
+#if defined(__SSE2__) || defined(__ARM_NEON__)
+ // The optimized interpolate_4_pixels are faster than interpolate_4_pixels_16.
+ int distx8 = (fx & 0x0000ffff) >> 8;
+ *b = interpolate_4_pixels(tl, tr, bl, br, distx8, disty8);
+#else
+ int distx4 = ((fx & 0x0000ffff) + 0x0800) >> 12;
+ *b = interpolate_4_pixels_16(tl, tr, bl, br, distx4, disty4);
+#endif
fx += fdx;
++b;
}
@@ -2253,6 +2264,10 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
if (blendType != BlendTransformedBilinearTiled) {
#define BILINEAR_ROTATE_BOUNDS_PROLOG \
+ const qint64 min_fx = qint64(image_x1) * fixed_scale; \
+ const qint64 max_fx = qint64(image_x2) * fixed_scale; \
+ const qint64 min_fy = qint64(image_y1) * fixed_scale; \
+ const qint64 max_fy = qint64(image_y2) * fixed_scale; \
while (b < end) { \
int x1 = (fx >> 16); \
int x2; \
@@ -2275,7 +2290,15 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
fy += fdy; \
++b; \
} \
- uint *boundedEnd = end - 3; \
+ uint *boundedEnd = end; \
+ if (fdx > 0) \
+ boundedEnd = qMin(boundedEnd, b + (max_fx - fx) / fdx); \
+ else if (fdx < 0) \
+ boundedEnd = qMin(boundedEnd, b + (min_fx - fx) / fdx); \
+ if (fdy > 0) \
+ boundedEnd = qMin(boundedEnd, b + (max_fy - fy) / fdy); \
+ else if (fdy < 0) \
+ boundedEnd = qMin(boundedEnd, b + (min_fy - fy) / fdy); \
boundedEnd -= 3;
#if defined(__SSE2__)
@@ -2285,6 +2308,7 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
const __m128i v_256 = _mm_set1_epi16(256);
const __m128i v_fdx = _mm_set1_epi32(fdx*4);
const __m128i v_fdy = _mm_set1_epi32(fdy*4);
+ const __m128i v_fxy_r = _mm_set1_epi32(0x8);
__m128i v_fx = _mm_setr_epi32(fx, fx + fdx, fx + fdx + fdx, fx + fdx + fdx + fdx);
__m128i v_fy = _mm_setr_epi32(fy, fy + fdy, fy + fdy + fdy, fy + fdy + fdy + fdy);
@@ -2293,15 +2317,6 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
const __m128i vbpl = _mm_shufflelo_epi16(_mm_cvtsi32_si128(bytesPerLine/4), _MM_SHUFFLE(0, 0, 0, 0));
while (b < boundedEnd) {
- if (fdx > 0 && (short)_mm_extract_epi16(v_fx, 7) >= image_x2)
- break;
- if (fdx < 0 && (short)_mm_extract_epi16(v_fx, 7) < image_x1)
- break;
- if (fdy > 0 && (short)_mm_extract_epi16(v_fy, 7) >= image_y2)
- break;
- if (fdy < 0 && (short)_mm_extract_epi16(v_fy, 7) < image_y1)
- break;
-
const __m128i vy = _mm_packs_epi32(_mm_srli_epi32(v_fy, 16), _mm_setzero_si128());
// 4x16bit * 4x16bit -> 4x32bit
__m128i offset = _mm_unpacklo_epi16(_mm_mullo_epi16(vy, vbpl), _mm_mulhi_epi16(vy, vbpl));
@@ -2317,8 +2332,10 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
const __m128i bl = _mm_setr_epi32(bottomData[offset0], bottomData[offset1], bottomData[offset2], bottomData[offset3]);
const __m128i br = _mm_setr_epi32(bottomData[offset0 + 1], bottomData[offset1 + 1], bottomData[offset2 + 1], bottomData[offset3 + 1]);
- __m128i v_distx = _mm_srli_epi16(v_fx, 12);
- __m128i v_disty = _mm_srli_epi16(v_fy, 12);
+ __m128i v_distx = _mm_srli_epi16(v_fx, 8);
+ __m128i v_disty = _mm_srli_epi16(v_fy, 8);
+ v_distx = _mm_srli_epi16(_mm_add_epi32(v_distx, v_fxy_r), 4);
+ v_disty = _mm_srli_epi16(_mm_add_epi32(v_disty, v_fxy_r), 4);
v_distx = _mm_shufflehi_epi16(v_distx, _MM_SHUFFLE(2,2,0,0));
v_distx = _mm_shufflelo_epi16(v_distx, _MM_SHUFFLE(2,2,0,0));
v_disty = _mm_shufflehi_epi16(v_disty, _MM_SHUFFLE(2,2,0,0));
@@ -2331,6 +2348,81 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
}
fx = _mm_cvtsi128_si32(v_fx);
fy = _mm_cvtsi128_si32(v_fy);
+#elif defined(__ARM_NEON__)
+ BILINEAR_ROTATE_BOUNDS_PROLOG
+
+ const int16x8_t colorMask = vdupq_n_s16(0x00ff);
+ const int16x8_t invColorMask = vmvnq_s16(colorMask);
+ const int16x8_t v_256 = vdupq_n_s16(256);
+ int32x4_t v_fdx = vdupq_n_s32(fdx * 4);
+ int32x4_t v_fdy = vdupq_n_s32(fdy * 4);
+
+ const uchar *textureData = data->texture.imageData;
+ const int bytesPerLine = data->texture.bytesPerLine;
+
+ int32x4_t v_fx = vmovq_n_s32(fx);
+ int32x4_t v_fy = vmovq_n_s32(fy);
+ v_fx = vsetq_lane_s32(fx + fdx, v_fx, 1);
+ v_fy = vsetq_lane_s32(fy + fdy, v_fy, 1);
+ v_fx = vsetq_lane_s32(fx + fdx * 2, v_fx, 2);
+ v_fy = vsetq_lane_s32(fy + fdy * 2, v_fy, 2);
+ v_fx = vsetq_lane_s32(fx + fdx * 3, v_fx, 3);
+ v_fy = vsetq_lane_s32(fy + fdy * 3, v_fy, 3);
+
+ const int32x4_t v_ffff_mask = vdupq_n_s32(0x0000ffff);
+ const int32x4_t v_round = vdupq_n_s32(0x0800);
+
+ while (b < boundedEnd) {
+ uint32x4x2_t v_top, v_bot;
+
+ int x1 = (fx >> 16);
+ int y1 = (fy >> 16);
+ fx += fdx; fy += fdy;
+ const uchar *sl = textureData + bytesPerLine * y1;
+ const uint *s1 = reinterpret_cast<const uint *>(sl);
+ const uint *s2 = reinterpret_cast<const uint *>(sl + bytesPerLine);
+ v_top = vld2q_lane_u32(s1 + x1, v_top, 0);
+ v_bot = vld2q_lane_u32(s2 + x1, v_bot, 0);
+ x1 = (fx >> 16);
+ y1 = (fy >> 16);
+ fx += fdx; fy += fdy;
+ sl = textureData + bytesPerLine * y1;
+ s1 = reinterpret_cast<const uint *>(sl);
+ s2 = reinterpret_cast<const uint *>(sl + bytesPerLine);
+ v_top = vld2q_lane_u32(s1 + x1, v_top, 1);
+ v_bot = vld2q_lane_u32(s2 + x1, v_bot, 1);
+ x1 = (fx >> 16);
+ y1 = (fy >> 16);
+ fx += fdx; fy += fdy;
+ sl = textureData + bytesPerLine * y1;
+ s1 = reinterpret_cast<const uint *>(sl);
+ s2 = reinterpret_cast<const uint *>(sl + bytesPerLine);
+ v_top = vld2q_lane_u32(s1 + x1, v_top, 2);
+ v_bot = vld2q_lane_u32(s2 + x1, v_bot, 2);
+ x1 = (fx >> 16);
+ y1 = (fy >> 16);
+ fx += fdx; fy += fdy;
+ sl = textureData + bytesPerLine * y1;
+ s1 = reinterpret_cast<const uint *>(sl);
+ s2 = reinterpret_cast<const uint *>(sl + bytesPerLine);
+ v_top = vld2q_lane_u32(s1 + x1, v_top, 3);
+ v_bot = vld2q_lane_u32(s2 + x1, v_bot, 3);
+
+ int32x4_t v_distx = vshrq_n_s32(vaddq_s32(vandq_s32(v_fx, v_ffff_mask), v_round), 12);
+ int32x4_t v_disty = vshrq_n_s32(vaddq_s32(vandq_s32(v_fy, v_ffff_mask), v_round), 12);
+ v_distx = vorrq_s32(v_distx, vshlq_n_s32(v_distx, 16));
+ v_disty = vorrq_s32(v_disty, vshlq_n_s32(v_disty, 16));
+ int16x8_t v_disty_ = vshlq_n_s16(vreinterpretq_s16_s32(v_disty), 4);
+
+ interpolate_4_pixels_16_neon(
+ vreinterpretq_s16_u32(v_top.val[0]), vreinterpretq_s16_u32(v_top.val[1]),
+ vreinterpretq_s16_u32(v_bot.val[0]), vreinterpretq_s16_u32(v_bot.val[1]),
+ vreinterpretq_s16_s32(v_distx), vreinterpretq_s16_s32(v_disty),
+ v_disty_, colorMask, invColorMask, v_256, b);
+ b += 4;
+ v_fx = vaddq_s32(v_fx, v_fdx);
+ v_fy = vaddq_s32(v_fy, v_fdy);
+ }
#endif
}
@@ -2357,8 +2449,8 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
int disty = (fy & 0x0000ffff) >> 8;
*b = interpolate_4_pixels(tl, tr, bl, br, distx, disty);
#else
- int distx = (fx & 0x0000ffff) >> 12;
- int disty = (fy & 0x0000ffff) >> 12;
+ int distx = ((fx & 0x0000ffff) + 0x0800) >> 12;
+ int disty = ((fy & 0x0000ffff) + 0x0800) >> 12;
*b = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty);
#endif
@@ -2418,12 +2510,17 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
}
// blendType = BlendTransformedBilinear or BlendTransformedBilinearTiled
-template<TextureBlendType blendType>
+template<TextureBlendType blendType, QPixelLayout::BPP bpp>
static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Operator *,
const QSpanData *data, int y, int x, int length)
{
const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
- const QRgb *clut = data->texture.colorTable ? data->texture.colorTable->constData() : 0;
+ const QVector<QRgb> *clut = data->texture.colorTable;
+ if (bpp != QPixelLayout::BPPNone) // Like this to not ICE on GCC 5.3.1
+ Q_ASSERT(layout->bpp == bpp);
+ // When templated 'fetch' should be inlined at compile time:
+ const FetchPixelsFunc fetch = (bpp == QPixelLayout::BPPNone) ? qFetchPixels[layout->bpp] : fetchPixels<bpp>;
+ const FetchPixelFunc fetch1 = (bpp == QPixelLayout::BPPNone) ? qFetchPixel[layout->bpp] : fetchPixel<bpp>;
int image_width = data->texture.width;
int image_height = data->texture.height;
@@ -2461,7 +2558,6 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
// The idea is first to do the interpolation between the row s1 and the row s2
// into an intermediate buffer, then we interpolate between two pixel of this buffer.
- FetchPixelsFunc fetch = qFetchPixels[layout->bpp];
// +1 for the last pixel to interpolate with, and +1 for rounding errors.
uint buf1[buffer_size + 2];
uint buf2[buffer_size + 2];
@@ -2479,9 +2575,9 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
int len2 = qMin(x, count - len1);
ptr1 = fetch(buf1, s1, x, len1);
- ptr1 = layout->convertToARGB32PM(buf1, ptr1, len1, layout, clut);
+ ptr1 = layout->convertToARGB32PM(buf1, ptr1, len1, clut, 0);
ptr2 = fetch(buf2, s2, x, len1);
- ptr2 = layout->convertToARGB32PM(buf2, ptr2, len1, layout, clut);
+ ptr2 = layout->convertToARGB32PM(buf2, ptr2, len1, clut, 0);
for (int i = 0; i < len1; ++i) {
uint t = ptr1[i];
uint b = ptr2[i];
@@ -2491,9 +2587,9 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
if (len2) {
ptr1 = fetch(buf1 + len1, s1, 0, len2);
- ptr1 = layout->convertToARGB32PM(buf1 + len1, ptr1, len2, layout, clut);
+ ptr1 = layout->convertToARGB32PM(buf1 + len1, ptr1, len2, clut, 0);
ptr2 = fetch(buf2 + len1, s2, 0, len2);
- ptr2 = layout->convertToARGB32PM(buf2 + len1, ptr2, len2, layout, clut);
+ ptr2 = layout->convertToARGB32PM(buf2 + len1, ptr2, len2, clut, 0);
for (int i = 0; i < len2; ++i) {
uint t = ptr1[i];
uint b = ptr2[i];
@@ -2512,9 +2608,9 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
int leading = start - x;
ptr1 = fetch(buf1 + leading, s1, start, len);
- ptr1 = layout->convertToARGB32PM(buf1 + leading, ptr1, len, layout, clut);
+ ptr1 = layout->convertToARGB32PM(buf1 + leading, ptr1, len, clut, 0);
ptr2 = fetch(buf2 + leading, s2, start, len);
- ptr2 = layout->convertToARGB32PM(buf2 + leading, ptr2, len, layout, clut);
+ ptr2 = layout->convertToARGB32PM(buf2 + leading, ptr2, len, clut, 0);
for (int i = 0; i < len; ++i) {
uint t = ptr1[i];
@@ -2550,7 +2646,6 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
fx += fdx;
}
} else {
- FetchPixelFunc fetch = qFetchPixel[layout->bpp];
uint buf1[buffer_size];
uint buf2[buffer_size];
uint *b = buffer;
@@ -2561,23 +2656,14 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
int x1 = (fx >> 16);
int x2;
fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2);
-
- if (layout->bpp == QPixelLayout::BPP32) {
- buf1[i * 2 + 0] = ((const uint*)s1)[x1];
- buf1[i * 2 + 1] = ((const uint*)s1)[x2];
- buf2[i * 2 + 0] = ((const uint*)s2)[x1];
- buf2[i * 2 + 1] = ((const uint*)s2)[x2];
- } else {
- buf1[i * 2 + 0] = fetch(s1, x1);
- buf1[i * 2 + 1] = fetch(s1, x2);
- buf2[i * 2 + 0] = fetch(s2, x1);
- buf2[i * 2 + 1] = fetch(s2, x2);
- }
-
+ buf1[i * 2 + 0] = fetch1(s1, x1);
+ buf1[i * 2 + 1] = fetch1(s1, x2);
+ buf2[i * 2 + 0] = fetch1(s2, x1);
+ buf2[i * 2 + 1] = fetch1(s2, x2);
fx += fdx;
}
- layout->convertToARGB32PM(buf1, buf1, len * 2, layout, clut);
- layout->convertToARGB32PM(buf2, buf2, len * 2, layout, clut);
+ layout->convertToARGB32PM(buf1, buf1, len * 2, clut, 0);
+ layout->convertToARGB32PM(buf2, buf2, len * 2, clut, 0);
if ((fdx < 0 && fdx > -(fixed_scale / 8)) || std::abs(data->m22) < (1./8.)) { // scale up more than 8x
int disty = (fy & 0x0000ffff) >> 8;
@@ -2587,13 +2673,13 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
fracX += fdx;
}
} else { //scale down
- int disty = (fy & 0x0000ffff) >> 12;
+ int disty = ((fy & 0x0000ffff) + 0x0800) >> 12;
for (int i = 0; i < len; ++i) {
uint tl = buf1[i * 2 + 0];
uint tr = buf1[i * 2 + 1];
uint bl = buf2[i * 2 + 0];
uint br = buf2[i * 2 + 1];
- int distx = (fracX & 0x0000ffff) >> 12;
+ int distx = ((fracX & 0x0000ffff) + 0x0800) >> 12;
b[i] = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty);
fracX += fdx;
}
@@ -2603,7 +2689,6 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
}
}
} else { //rotation
- FetchPixelFunc fetch = qFetchPixel[layout->bpp];
uint buf1[buffer_size];
uint buf2[buffer_size];
uint *b = buffer;
@@ -2622,24 +2707,15 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
const uchar *s1 = data->texture.scanLine(y1);
const uchar *s2 = data->texture.scanLine(y2);
-
- if (layout->bpp == QPixelLayout::BPP32) {
- buf1[i * 2 + 0] = ((const uint*)s1)[x1];
- buf1[i * 2 + 1] = ((const uint*)s1)[x2];
- buf2[i * 2 + 0] = ((const uint*)s2)[x1];
- buf2[i * 2 + 1] = ((const uint*)s2)[x2];
- } else {
- buf1[i * 2 + 0] = fetch(s1, x1);
- buf1[i * 2 + 1] = fetch(s1, x2);
- buf2[i * 2 + 0] = fetch(s2, x1);
- buf2[i * 2 + 1] = fetch(s2, x2);
- }
-
+ buf1[i * 2 + 0] = fetch1(s1, x1);
+ buf1[i * 2 + 1] = fetch1(s1, x2);
+ buf2[i * 2 + 0] = fetch1(s2, x1);
+ buf2[i * 2 + 1] = fetch1(s2, x2);
fx += fdx;
fy += fdy;
}
- layout->convertToARGB32PM(buf1, buf1, len * 2, layout, clut);
- layout->convertToARGB32PM(buf2, buf2, len * 2, layout, clut);
+ layout->convertToARGB32PM(buf1, buf1, len * 2, clut, 0);
+ layout->convertToARGB32PM(buf2, buf2, len * 2, clut, 0);
if (std::abs(data->m11) > 8 || std::abs(data->m22) > 8) {
//if we are zooming more than 8 times, we use 8bit precision for the position.
@@ -2659,8 +2735,8 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
uint bl = buf2[i * 2 + 0];
uint br = buf2[i * 2 + 1];
- int distx = (fracX & 0x0000ffff) >> 12;
- int disty = (fracY & 0x0000ffff) >> 12;
+ int distx = ((fracX & 0x0000ffff) + 0x0800) >> 12;
+ int disty = ((fracY & 0x0000ffff) + 0x0800) >> 12;
b[i] = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty);
fracX += fdx;
@@ -2681,7 +2757,6 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
- FetchPixelFunc fetch = qFetchPixel[layout->bpp];
uint buf1[buffer_size];
uint buf2[buffer_size];
uint *b = buffer;
@@ -2709,18 +2784,10 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
const uchar *s1 = data->texture.scanLine(y1);
const uchar *s2 = data->texture.scanLine(y2);
-
- if (layout->bpp == QPixelLayout::BPP32) {
- buf1[i * 2 + 0] = ((const uint*)s1)[x1];
- buf1[i * 2 + 1] = ((const uint*)s1)[x2];
- buf2[i * 2 + 0] = ((const uint*)s2)[x1];
- buf2[i * 2 + 1] = ((const uint*)s2)[x2];
- } else {
- buf1[i * 2 + 0] = fetch(s1, x1);
- buf1[i * 2 + 1] = fetch(s1, x2);
- buf2[i * 2 + 0] = fetch(s2, x1);
- buf2[i * 2 + 1] = fetch(s2, x2);
- }
+ buf1[i * 2 + 0] = fetch1(s1, x1);
+ buf1[i * 2 + 1] = fetch1(s1, x2);
+ buf2[i * 2 + 0] = fetch1(s2, x1);
+ buf2[i * 2 + 1] = fetch1(s2, x2);
fx += fdx;
fy += fdy;
@@ -2730,8 +2797,8 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
fw += fdw;
}
- layout->convertToARGB32PM(buf1, buf1, len * 2, layout, clut);
- layout->convertToARGB32PM(buf2, buf2, len * 2, layout, clut);
+ layout->convertToARGB32PM(buf1, buf1, len * 2, clut, 0);
+ layout->convertToARGB32PM(buf2, buf2, len * 2, clut, 0);
for (int i = 0; i < len; ++i) {
int distx = distxs[i];
@@ -2752,7 +2819,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co
const QSpanData *data, int y, int x, int length)
{
const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
- const QRgb *clut = data->texture.colorTable ? data->texture.colorTable->constData() : 0;
+ const QVector<QRgb> *clut = data->texture.colorTable;
int image_width = data->texture.width;
int image_height = data->texture.height;
@@ -2808,10 +2875,8 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co
fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2);
if (x1 != x2)
break;
- sbuf1[i * 2 + 0] = ((const uint*)s1)[x1];
- sbuf1[i * 2 + 1] = ((const uint*)s1)[x2];
- sbuf2[i * 2 + 0] = ((const uint*)s2)[x1];
- sbuf2[i * 2 + 1] = ((const uint*)s2)[x2];
+ sbuf1[i * 2 + 0] = sbuf1[i * 2 + 1] = ((const uint*)s1)[x1];
+ sbuf2[i * 2 + 0] = sbuf2[i * 2 + 1] = ((const uint*)s2)[x1];
fx += fdx;
}
int fastLen;
@@ -2869,9 +2934,9 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co
fx += fdx;
}
- layout->convertToARGB64PM(buf1, sbuf1, len * 2, layout, clut);
+ layout->convertToARGB64PM(buf1, sbuf1, len * 2, clut, 0);
if (disty)
- layout->convertToARGB64PM(buf2, sbuf2, len * 2, layout, clut);
+ layout->convertToARGB64PM(buf2, sbuf2, len * 2, clut, 0);
for (int i = 0; i < len; ++i) {
int distx = (fracX & 0x0000ffff);
@@ -2930,6 +2995,16 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co
fx += fdx;
fy += fdy;
}
+ int fastLen = len;
+ if (fdx > 0)
+ fastLen = qMin(fastLen, int((qint64(image_x2) * fixed_scale - fx) / fdx));
+ else if (fdx < 0)
+ fastLen = qMin(fastLen, int((qint64(image_x1) * fixed_scale - fx) / fdx));
+ if (fdy > 0)
+ fastLen = qMin(fastLen, int((qint64(image_y2) * fixed_scale - fy) / fdy));
+ else if (fdy < 0)
+ fastLen = qMin(fastLen, int((qint64(image_y1) * fixed_scale - fy) / fdy));
+ fastLen -= 3;
const __m128i v_fdx = _mm_set1_epi32(fdx*4);
const __m128i v_fdy = _mm_set1_epi32(fdy*4);
@@ -2939,15 +3014,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co
const uchar *s1 = data->texture.imageData;
const uchar *s2 = s1 + bytesPerLine;
const __m128i vbpl = _mm_shufflelo_epi16(_mm_cvtsi32_si128(bytesPerLine/4), _MM_SHUFFLE(0, 0, 0, 0));
- for (; i < len-3; i+=4) {
- if (fdx > 0 && (short)_mm_extract_epi16(v_fx, 7) >= image_x2)
- break;
- if (fdx < 0 && (short)_mm_extract_epi16(v_fx, 7) < image_x1)
- break;
- if (fdy > 0 && (short)_mm_extract_epi16(v_fy, 7) >= image_y2)
- break;
- if (fdy < 0 && (short)_mm_extract_epi16(v_fy, 7) < image_y1)
- break;
+ for (; i < fastLen; i += 4) {
const __m128i vy = _mm_packs_epi32(_mm_srai_epi32(v_fy, 16), _mm_setzero_si128());
__m128i voffset = _mm_unpacklo_epi16(_mm_mullo_epi16(vy, vbpl), _mm_mulhi_epu16(vy, vbpl));
voffset = _mm_add_epi32(voffset, _mm_srli_epi32(v_fx, 16));
@@ -3007,8 +3074,8 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co
fx += fdx;
fy += fdy;
}
- layout->convertToARGB64PM(buf1, sbuf1, len * 2, layout, clut);
- layout->convertToARGB64PM(buf2, sbuf2, len * 2, layout, clut);
+ layout->convertToARGB64PM(buf1, sbuf1, len * 2, clut, 0);
+ layout->convertToARGB64PM(buf2, sbuf2, len * 2, clut, 0);
for (int i = 0; i < len; ++i) {
int distx = (fracX & 0x0000ffff);
@@ -3079,8 +3146,8 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co
fw += fdw;
}
- layout->convertToARGB64PM(buf1, sbuf1, len * 2, layout, clut);
- layout->convertToARGB64PM(buf2, sbuf2, len * 2, layout, clut);
+ layout->convertToARGB64PM(buf1, sbuf1, len * 2, clut, 0);
+ layout->convertToARGB64PM(buf2, sbuf2, len * 2, clut, 0);
for (int i = 0; i < len; ++i) {
int distx = distxs[i];
@@ -3096,342 +3163,83 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co
return buffer;
}
-static SourceFetchProc sourceFetch[NBlendTypes][QImage::NImageFormats] = {
- // Untransformed
- {
- 0, // Invalid
- fetchUntransformed, // Mono
- fetchUntransformed, // MonoLsb
- fetchUntransformed, // Indexed8
- fetchUntransformedARGB32PM, // RGB32
- fetchUntransformed, // ARGB32
- fetchUntransformedARGB32PM, // ARGB32_Premultiplied
- fetchUntransformedRGB16, // RGB16
- fetchUntransformed, // ARGB8565_Premultiplied
- fetchUntransformed, // RGB666
- fetchUntransformed, // ARGB6666_Premultiplied
- fetchUntransformed, // RGB555
- fetchUntransformed, // ARGB8555_Premultiplied
- fetchUntransformed, // RGB888
- fetchUntransformed, // RGB444
- fetchUntransformed, // ARGB4444_Premultiplied
- fetchUntransformed, // RGBX8888
- fetchUntransformed, // RGBA8888
- fetchUntransformed, // RGBA8888_Premultiplied
- fetchUntransformed, // Format_BGR30
- fetchUntransformed, // Format_A2BGR30_Premultiplied
- fetchUntransformed, // Format_RGB30
- fetchUntransformed, // Format_A2RGB30_Premultiplied
- fetchUntransformed, // Alpha8
- fetchUntransformed, // Grayscale8
- },
- // Tiled
- {
- 0, // Invalid
- fetchUntransformed, // Mono
- fetchUntransformed, // MonoLsb
- fetchUntransformed, // Indexed8
- fetchUntransformedARGB32PM, // RGB32
- fetchUntransformed, // ARGB32
- fetchUntransformedARGB32PM, // ARGB32_Premultiplied
- fetchUntransformedRGB16, // RGB16
- fetchUntransformed, // ARGB8565_Premultiplied
- fetchUntransformed, // RGB666
- fetchUntransformed, // ARGB6666_Premultiplied
- fetchUntransformed, // RGB555
- fetchUntransformed, // ARGB8555_Premultiplied
- fetchUntransformed, // RGB888
- fetchUntransformed, // RGB444
- fetchUntransformed, // ARGB4444_Premultiplied
- fetchUntransformed, // RGBX8888
- fetchUntransformed, // RGBA8888
- fetchUntransformed, // RGBA8888_Premultiplied
- fetchUntransformed, // BGR30
- fetchUntransformed, // A2BGR30_Premultiplied
- fetchUntransformed, // RGB30
- fetchUntransformed, // A2RGB30_Premultiplied
- fetchUntransformed, // Alpha8
- fetchUntransformed, // Grayscale8
- },
- // Transformed
- {
- 0, // Invalid
- fetchTransformed<BlendTransformed>, // Mono
- fetchTransformed<BlendTransformed>, // MonoLsb
- fetchTransformed<BlendTransformed>, // Indexed8
- fetchTransformedARGB32PM<BlendTransformed>, // RGB32
- fetchTransformed<BlendTransformed>, // ARGB32
- fetchTransformedARGB32PM<BlendTransformed>, // ARGB32_Premultiplied
- fetchTransformed<BlendTransformed>, // RGB16
- fetchTransformed<BlendTransformed>, // ARGB8565_Premultiplied
- fetchTransformed<BlendTransformed>, // RGB666
- fetchTransformed<BlendTransformed>, // ARGB6666_Premultiplied
- fetchTransformed<BlendTransformed>, // RGB555
- fetchTransformed<BlendTransformed>, // ARGB8555_Premultiplied
- fetchTransformed<BlendTransformed>, // RGB888
- fetchTransformed<BlendTransformed>, // RGB444
- fetchTransformed<BlendTransformed>, // ARGB4444_Premultiplied
- fetchTransformed<BlendTransformed>, // RGBX8888
- fetchTransformed<BlendTransformed>, // RGBA8888
- fetchTransformed<BlendTransformed>, // RGBA8888_Premultiplied
- fetchTransformed<BlendTransformed>, // BGR30
- fetchTransformed<BlendTransformed>, // A2BGR30_Premultiplied
- fetchTransformed<BlendTransformed>, // RGB30
- fetchTransformed<BlendTransformed>, // A2RGB30_Premultiplied
- fetchTransformed<BlendTransformed>, // Alpah8
- fetchTransformed<BlendTransformed>, // Grayscale8
- },
- {
- 0, // TransformedTiled
- fetchTransformed<BlendTransformedTiled>, // Mono
- fetchTransformed<BlendTransformedTiled>, // MonoLsb
- fetchTransformed<BlendTransformedTiled>, // Indexed8
- fetchTransformedARGB32PM<BlendTransformedTiled>, // RGB32
- fetchTransformed<BlendTransformedTiled>, // ARGB32
- fetchTransformedARGB32PM<BlendTransformedTiled>, // ARGB32_Premultiplied
- fetchTransformed<BlendTransformedTiled>, // RGB16
- fetchTransformed<BlendTransformedTiled>, // ARGB8565_Premultiplied
- fetchTransformed<BlendTransformedTiled>, // RGB666
- fetchTransformed<BlendTransformedTiled>, // ARGB6666_Premultiplied
- fetchTransformed<BlendTransformedTiled>, // RGB555
- fetchTransformed<BlendTransformedTiled>, // ARGB8555_Premultiplied
- fetchTransformed<BlendTransformedTiled>, // RGB888
- fetchTransformed<BlendTransformedTiled>, // RGB444
- fetchTransformed<BlendTransformedTiled>, // ARGB4444_Premultiplied
- fetchTransformed<BlendTransformedTiled>, // RGBX8888
- fetchTransformed<BlendTransformedTiled>, // RGBA8888
- fetchTransformed<BlendTransformedTiled>, // RGBA8888_Premultiplied
- fetchTransformed<BlendTransformedTiled>, // BGR30
- fetchTransformed<BlendTransformedTiled>, // A2BGR30_Premultiplied
- fetchTransformed<BlendTransformedTiled>, // RGB30
- fetchTransformed<BlendTransformedTiled>, // A2RGB30_Premultiplied
- fetchTransformed<BlendTransformedTiled>, // Alpha8
- fetchTransformed<BlendTransformedTiled>, // Grayscale8
- },
- {
- 0, // Bilinear
- fetchTransformedBilinear<BlendTransformedBilinear>, // Mono
- fetchTransformedBilinear<BlendTransformedBilinear>, // MonoLsb
- fetchTransformedBilinear<BlendTransformedBilinear>, // Indexed8
- fetchTransformedBilinearARGB32PM<BlendTransformedBilinear>, // RGB32
- fetchTransformedBilinear<BlendTransformedBilinear>, // ARGB32
- fetchTransformedBilinearARGB32PM<BlendTransformedBilinear>, // ARGB32_Premultiplied
- fetchTransformedBilinear<BlendTransformedBilinear>, // RGB16
- fetchTransformedBilinear<BlendTransformedBilinear>, // ARGB8565_Premultiplied
- fetchTransformedBilinear<BlendTransformedBilinear>, // RGB666
- fetchTransformedBilinear<BlendTransformedBilinear>, // ARGB6666_Premultiplied
- fetchTransformedBilinear<BlendTransformedBilinear>, // RGB555
- fetchTransformedBilinear<BlendTransformedBilinear>, // ARGB8555_Premultiplied
- fetchTransformedBilinear<BlendTransformedBilinear>, // RGB888
- fetchTransformedBilinear<BlendTransformedBilinear>, // RGB444
- fetchTransformedBilinear<BlendTransformedBilinear>, // ARGB4444_Premultiplied
- fetchTransformedBilinear<BlendTransformedBilinear>, // RGBX8888
- fetchTransformedBilinear<BlendTransformedBilinear>, // RGBA8888
- fetchTransformedBilinear<BlendTransformedBilinear>, // RGBA8888_Premultiplied
- fetchTransformedBilinear<BlendTransformedBilinear>, // BGR30
- fetchTransformedBilinear<BlendTransformedBilinear>, // A2BGR30_Premultiplied
- fetchTransformedBilinear<BlendTransformedBilinear>, // RGB30
- fetchTransformedBilinear<BlendTransformedBilinear>, // A2RGB30_Premultiplied
- fetchTransformedBilinear<BlendTransformedBilinear>, // Alpha8
- fetchTransformedBilinear<BlendTransformedBilinear>, // Grayscale8
- },
- {
- 0, // BilinearTiled
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // Mono
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // MonoLsb
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // Indexed8
- fetchTransformedBilinearARGB32PM<BlendTransformedBilinearTiled>, // RGB32
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // ARGB32
- fetchTransformedBilinearARGB32PM<BlendTransformedBilinearTiled>, // ARGB32_Premultiplied
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // RGB16
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // ARGB8565_Premultiplied
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // RGB666
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // ARGB6666_Premultiplied
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // RGB555
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // ARGB8555_Premultiplied
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // RGB888
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // RGB444
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // ARGB4444_Premultiplied
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // RGBX8888
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // RGBA8888
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // RGBA8888_Premultiplied
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // BGR30
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // A2BGR30_Premultiplied
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // RGB30
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // A2RGB30_Premultiplied
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // Alpha8
- fetchTransformedBilinear<BlendTransformedBilinearTiled>, // Grayscale8
- },
+// FetchUntransformed can have more specialized methods added depending on SIMD features.
+static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = {
+ 0, // Invalid
+ fetchUntransformed, // Mono
+ fetchUntransformed, // MonoLsb
+ fetchUntransformed, // Indexed8
+ fetchUntransformedARGB32PM, // RGB32
+ fetchUntransformed, // ARGB32
+ fetchUntransformedARGB32PM, // ARGB32_Premultiplied
+ fetchUntransformedRGB16, // RGB16
+ fetchUntransformed, // ARGB8565_Premultiplied
+ fetchUntransformed, // RGB666
+ fetchUntransformed, // ARGB6666_Premultiplied
+ fetchUntransformed, // RGB555
+ fetchUntransformed, // ARGB8555_Premultiplied
+ fetchUntransformed, // RGB888
+ fetchUntransformed, // RGB444
+ fetchUntransformed, // ARGB4444_Premultiplied
+ fetchUntransformed, // RGBX8888
+ fetchUntransformed, // RGBA8888
+ fetchUntransformed, // RGBA8888_Premultiplied
+ fetchUntransformed, // Format_BGR30
+ fetchUntransformed, // Format_A2BGR30_Premultiplied
+ fetchUntransformed, // Format_RGB30
+ fetchUntransformed, // Format_A2RGB30_Premultiplied
+ fetchUntransformed, // Alpha8
+ fetchUntransformed, // Grayscale8
};
-static SourceFetchProc64 sourceFetch64[NBlendTypes][QImage::NImageFormats] = {
- // Untransformed
- {
- 0, // Invalid
- fetchUntransformed64, // Mono
- fetchUntransformed64, // MonoLsb
- fetchUntransformed64, // Indexed8
- fetchUntransformed64, // RGB32
- fetchUntransformed64, // ARGB32
- fetchUntransformed64, // ARGB32_Premultiplied
- fetchUntransformed64, // RGB16
- fetchUntransformed64, // ARGB8565_Premultiplied
- fetchUntransformed64, // RGB666
- fetchUntransformed64, // ARGB6666_Premultiplied
- fetchUntransformed64, // RGB555
- fetchUntransformed64, // ARGB8555_Premultiplied
- fetchUntransformed64, // RGB888
- fetchUntransformed64, // RGB444
- fetchUntransformed64, // ARGB4444_Premultiplied
- fetchUntransformed64, // RGBX8888
- fetchUntransformed64, // RGBA8888
- fetchUntransformed64, // RGBA8888_Premultiplied
- fetchUntransformed64, // Format_BGR30
- fetchUntransformed64, // Format_A2BGR30_Premultiplied
- fetchUntransformed64, // Format_RGB30
- fetchUntransformed64, // Format_A2RGB30_Premultiplied
- fetchUntransformed64, // Alpha8
- fetchUntransformed64, // Grayscale8
- },
- // Tiled
- {
- 0, // Invalid
- fetchUntransformed64, // Mono
- fetchUntransformed64, // MonoLsb
- fetchUntransformed64, // Indexed8
- fetchUntransformed64, // RGB32
- fetchUntransformed64, // ARGB32
- fetchUntransformed64, // ARGB32_Premultiplied
- fetchUntransformed64, // RGB16
- fetchUntransformed64, // ARGB8565_Premultiplied
- fetchUntransformed64, // RGB666
- fetchUntransformed64, // ARGB6666_Premultiplied
- fetchUntransformed64, // RGB555
- fetchUntransformed64, // ARGB8555_Premultiplied
- fetchUntransformed64, // RGB888
- fetchUntransformed64, // RGB444
- fetchUntransformed64, // ARGB4444_Premultiplied
- fetchUntransformed64, // RGBX8888
- fetchUntransformed64, // RGBA8888
- fetchUntransformed64, // RGBA8888_Premultiplied
- fetchUntransformed64, // BGR30
- fetchUntransformed64, // A2BGR30_Premultiplied
- fetchUntransformed64, // RGB30
- fetchUntransformed64, // A2RGB30_Premultiplied
- fetchUntransformed64, // Alpha8
- fetchUntransformed64, // Grayscale8
- },
- // Transformed
- {
- 0, // Invalid
- fetchTransformed64<BlendTransformed>, // Mono
- fetchTransformed64<BlendTransformed>, // MonoLsb
- fetchTransformed64<BlendTransformed>, // Indexed8
- fetchTransformed64<BlendTransformed>, // RGB32
- fetchTransformed64<BlendTransformed>, // ARGB32
- fetchTransformed64<BlendTransformed>, // ARGB32_Premultiplied
- fetchTransformed64<BlendTransformed>, // RGB16
- fetchTransformed64<BlendTransformed>, // ARGB8565_Premultiplied
- fetchTransformed64<BlendTransformed>, // RGB666
- fetchTransformed64<BlendTransformed>, // ARGB6666_Premultiplied
- fetchTransformed64<BlendTransformed>, // RGB555
- fetchTransformed64<BlendTransformed>, // ARGB8555_Premultiplied
- fetchTransformed64<BlendTransformed>, // RGB888
- fetchTransformed64<BlendTransformed>, // RGB444
- fetchTransformed64<BlendTransformed>, // ARGB4444_Premultiplied
- fetchTransformed64<BlendTransformed>, // RGBX8888
- fetchTransformed64<BlendTransformed>, // RGBA8888
- fetchTransformed64<BlendTransformed>, // RGBA8888_Premultiplied
- fetchTransformed64<BlendTransformed>, // BGR30
- fetchTransformed64<BlendTransformed>, // A2BGR30_Premultiplied
- fetchTransformed64<BlendTransformed>, // RGB30
- fetchTransformed64<BlendTransformed>, // A2RGB30_Premultiplied
- fetchTransformed64<BlendTransformed>, // Alpah8
- fetchTransformed64<BlendTransformed>, // Grayscale8
- },
- {
- 0, // TransformedTiled
- fetchTransformed64<BlendTransformedTiled>, // Mono
- fetchTransformed64<BlendTransformedTiled>, // MonoLsb
- fetchTransformed64<BlendTransformedTiled>, // Indexed8
- fetchTransformed64<BlendTransformedTiled>, // RGB32
- fetchTransformed64<BlendTransformedTiled>, // ARGB32
- fetchTransformed64<BlendTransformedTiled>, // ARGB32_Premultiplied
- fetchTransformed64<BlendTransformedTiled>, // RGB16
- fetchTransformed64<BlendTransformedTiled>, // ARGB8565_Premultiplied
- fetchTransformed64<BlendTransformedTiled>, // RGB666
- fetchTransformed64<BlendTransformedTiled>, // ARGB6666_Premultiplied
- fetchTransformed64<BlendTransformedTiled>, // RGB555
- fetchTransformed64<BlendTransformedTiled>, // ARGB8555_Premultiplied
- fetchTransformed64<BlendTransformedTiled>, // RGB888
- fetchTransformed64<BlendTransformedTiled>, // RGB444
- fetchTransformed64<BlendTransformedTiled>, // ARGB4444_Premultiplied
- fetchTransformed64<BlendTransformedTiled>, // RGBX8888
- fetchTransformed64<BlendTransformedTiled>, // RGBA8888
- fetchTransformed64<BlendTransformedTiled>, // RGBA8888_Premultiplied
- fetchTransformed64<BlendTransformedTiled>, // BGR30
- fetchTransformed64<BlendTransformedTiled>, // A2BGR30_Premultiplied
- fetchTransformed64<BlendTransformedTiled>, // RGB30
- fetchTransformed64<BlendTransformedTiled>, // A2RGB30_Premultiplied
- fetchTransformed64<BlendTransformedTiled>, // Alpha8
- fetchTransformed64<BlendTransformedTiled>, // Grayscale8
- },
- {
- 0, // Bilinear
- fetchTransformedBilinear64<BlendTransformedBilinear>, // Mono
- fetchTransformedBilinear64<BlendTransformedBilinear>, // MonoLsb
- fetchTransformedBilinear64<BlendTransformedBilinear>, // Indexed8
- fetchTransformedBilinear64<BlendTransformedBilinear>, // RGB32
- fetchTransformedBilinear64<BlendTransformedBilinear>, // ARGB32
- fetchTransformedBilinear64<BlendTransformedBilinear>, // ARGB32_Premultiplied
- fetchTransformedBilinear64<BlendTransformedBilinear>, // RGB16
- fetchTransformedBilinear64<BlendTransformedBilinear>, // ARGB8565_Premultiplied
- fetchTransformedBilinear64<BlendTransformedBilinear>, // RGB666
- fetchTransformedBilinear64<BlendTransformedBilinear>, // ARGB6666_Premultiplied
- fetchTransformedBilinear64<BlendTransformedBilinear>, // RGB555
- fetchTransformedBilinear64<BlendTransformedBilinear>, // ARGB8555_Premultiplied
- fetchTransformedBilinear64<BlendTransformedBilinear>, // RGB888
- fetchTransformedBilinear64<BlendTransformedBilinear>, // RGB444
- fetchTransformedBilinear64<BlendTransformedBilinear>, // ARGB4444_Premultiplied
- fetchTransformedBilinear64<BlendTransformedBilinear>, // RGBX8888
- fetchTransformedBilinear64<BlendTransformedBilinear>, // RGBA8888
- fetchTransformedBilinear64<BlendTransformedBilinear>, // RGBA8888_Premultiplied
- fetchTransformedBilinear64<BlendTransformedBilinear>, // BGR30
- fetchTransformedBilinear64<BlendTransformedBilinear>, // A2BGR30_Premultiplied
- fetchTransformedBilinear64<BlendTransformedBilinear>, // RGB30
- fetchTransformedBilinear64<BlendTransformedBilinear>, // A2RGB30_Premultiplied
- fetchTransformedBilinear64<BlendTransformedBilinear>, // Alpha8
- fetchTransformedBilinear64<BlendTransformedBilinear>, // Grayscale8
- },
- {
- 0, // BilinearTiled
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // Mono
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // MonoLsb
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // Indexed8
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // RGB32
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // ARGB32
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // ARGB32_Premultiplied
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // RGB16
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // ARGB8565_Premultiplied
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // RGB666
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // ARGB6666_Premultiplied
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // RGB555
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // ARGB8555_Premultiplied
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // RGB888
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // RGB444
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // ARGB4444_Premultiplied
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // RGBX8888
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // RGBA8888
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // RGBA8888_Premultiplied
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // BGR30
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // A2BGR30_Premultiplied
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // RGB30
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // A2RGB30_Premultiplied
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // Alpha8
- fetchTransformedBilinear64<BlendTransformedBilinearTiled>, // Grayscale8
- },
+static const SourceFetchProc sourceFetchGeneric[NBlendTypes] = {
+ fetchUntransformed, // Untransformed
+ fetchUntransformed, // Tiled
+ fetchTransformed<BlendTransformed, QPixelLayout::BPPNone>, // Transformed
+ fetchTransformed<BlendTransformedTiled, QPixelLayout::BPPNone>, // TransformedTiled
+ fetchTransformedBilinear<BlendTransformedBilinear, QPixelLayout::BPPNone>, // TransformedBilinear
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPPNone> // TransformedBilinearTiled
};
+static SourceFetchProc sourceFetchARGB32PM[NBlendTypes] = {
+ fetchUntransformedARGB32PM, // Untransformed
+ fetchUntransformedARGB32PM, // Tiled
+ fetchTransformed<BlendTransformed, QPixelLayout::BPP32>, // Transformed
+ fetchTransformed<BlendTransformedTiled, QPixelLayout::BPP32>, // TransformedTiled
+ fetchTransformedBilinearARGB32PM<BlendTransformedBilinear>, // Bilinear
+ fetchTransformedBilinearARGB32PM<BlendTransformedBilinearTiled> // BilinearTiled
+};
+
+static SourceFetchProc sourceFetchAny32[NBlendTypes] = {
+ fetchUntransformed, // Untransformed
+ fetchUntransformed, // Tiled
+ fetchTransformed<BlendTransformed, QPixelLayout::BPP32>, // Transformed
+ fetchTransformed<BlendTransformedTiled, QPixelLayout::BPP32>, // TransformedTiled
+ fetchTransformedBilinear<BlendTransformedBilinear, QPixelLayout::BPP32>, // TransformedBilinear
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP32> // TransformedBilinearTiled
+};
+
+static const SourceFetchProc64 sourceFetchGeneric64[NBlendTypes] = {
+ fetchUntransformed64, // Untransformed
+ fetchUntransformed64, // Tiled
+ fetchTransformed64<BlendTransformed>, // Transformed
+ fetchTransformed64<BlendTransformedTiled>, // TransformedTiled
+ fetchTransformedBilinear64<BlendTransformedBilinear>, // Bilinear
+ fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled
+};
+
+static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage::Format format)
+{
+ if (format == QImage::Format_RGB32 || format == QImage::Format_ARGB32_Premultiplied)
+ return sourceFetchARGB32PM[blendType];
+ if (blendType == BlendUntransformed || blendType == BlendTiled)
+ return sourceFetchUntransformed[format];
+ if (qPixelLayouts[format].bpp == QPixelLayout::BPP32)
+ return sourceFetchAny32[blendType];
+ return sourceFetchGeneric[blendType];
+}
+
+
#define FIXPT_BITS 8
#define FIXPT_SIZE (1<<FIXPT_BITS)
@@ -3772,8 +3580,8 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in
break;
case QSpanData::Texture:
solidSource = !data->texture.hasAlpha;
- op.srcFetch = sourceFetch[getBlendType(data)][data->texture.format];
- op.srcFetch64 = sourceFetch64[getBlendType(data)][data->texture.format];
+ op.srcFetch = getSourceFetch(getBlendType(data), data->texture.format);
+ op.srcFetch64 = sourceFetchGeneric64[getBlendType(data)];
break;
default:
Q_UNREACHABLE();
@@ -4038,7 +3846,7 @@ template<typename T>
struct QBlendBase
{
typedef T BlendType;
- QBlendBase(QSpanData *d, Operator o)
+ QBlendBase(QSpanData *d, const Operator &o)
: data(d)
, op(o)
, dest(0)
@@ -4057,7 +3865,7 @@ struct QBlendBase
class BlendSrcGeneric : public QBlendBase<uint>
{
public:
- BlendSrcGeneric(QSpanData *d, Operator o)
+ BlendSrcGeneric(QSpanData *d, const Operator &o)
: QBlendBase<uint>(d, o)
{
}
@@ -4083,7 +3891,7 @@ public:
class BlendSrcGenericRGB64 : public QBlendBase<QRgba64>
{
public:
- BlendSrcGenericRGB64(QSpanData *d, Operator o)
+ BlendSrcGenericRGB64(QSpanData *d, const Operator &o)
: QBlendBase<QRgba64>(d, o)
{
}
@@ -5316,181 +5124,67 @@ static void blend_transformed_tiled_rgb565(int count, const QSpan *spans, void *
/* Image formats here are target formats */
-static const ProcessSpans processTextureSpans[NBlendTypes][QImage::NImageFormats] = {
- // Untransformed
- {
- 0, // Invalid
- blend_untransformed_generic, // Mono
- blend_untransformed_generic, // MonoLsb
- blend_untransformed_generic, // Indexed8
- blend_untransformed_generic, // RGB32
- blend_untransformed_generic, // ARGB32
- blend_untransformed_argb, // ARGB32_Premultiplied
- blend_untransformed_rgb565,
- blend_untransformed_generic,
- blend_untransformed_generic,
- blend_untransformed_generic,
- blend_untransformed_generic,
- blend_untransformed_generic,
- blend_untransformed_generic,
- blend_untransformed_generic,
- blend_untransformed_generic,
- blend_untransformed_generic,
- blend_untransformed_generic,
- blend_untransformed_generic,
- blend_untransformed_generic_rgb64,
- blend_untransformed_generic_rgb64,
- blend_untransformed_generic_rgb64,
- blend_untransformed_generic_rgb64,
- blend_untransformed_generic,
- blend_untransformed_generic,
- },
- // Tiled
- {
- 0, // Invalid
- blend_tiled_generic, // Mono
- blend_tiled_generic, // MonoLsb
- blend_tiled_generic, // Indexed8
- blend_tiled_generic, // RGB32
- blend_tiled_generic, // ARGB32
- blend_tiled_argb, // ARGB32_Premultiplied
- blend_tiled_rgb565,
- blend_tiled_generic,
- blend_tiled_generic,
- blend_tiled_generic,
- blend_tiled_generic,
- blend_tiled_generic,
- blend_tiled_generic,
- blend_tiled_generic,
- blend_tiled_generic,
- blend_tiled_generic,
- blend_tiled_generic,
- blend_tiled_generic,
- blend_tiled_generic_rgb64,
- blend_tiled_generic_rgb64,
- blend_tiled_generic_rgb64,
- blend_tiled_generic_rgb64,
- blend_tiled_generic,
- blend_tiled_generic,
- },
- // Transformed
- {
- 0, // Invalid
- blend_src_generic, // Mono
- blend_src_generic, // MonoLsb
- blend_src_generic, // Indexed8
- blend_src_generic, // RGB32
- blend_src_generic, // ARGB32
- blend_transformed_argb, // ARGB32_Premultiplied
- blend_transformed_rgb565,
- blend_src_generic, // ARGB8565_Premultiplied
- blend_src_generic, // RGB666
- blend_src_generic, // ARGB6666_Premultiplied
- blend_src_generic, // RGB555
- blend_src_generic, // ARGB8555_Premultiplied
- blend_src_generic, // RGB888
- blend_src_generic, // RGB444
- blend_src_generic, // ARGB4444_Premultiplied
- blend_src_generic, // RGBX8888
- blend_src_generic, // RGBA8888
- blend_src_generic, // RGBA8888_Premultiplied
- blend_src_generic_rgb64,
- blend_src_generic_rgb64,
- blend_src_generic_rgb64,
- blend_src_generic_rgb64,
- blend_src_generic,
- blend_src_generic,
- },
- // TransformedTiled
- {
- 0,
- blend_src_generic, // Mono
- blend_src_generic, // MonoLsb
- blend_src_generic, // Indexed8
- blend_src_generic, // RGB32
- blend_src_generic, // ARGB32
- blend_transformed_tiled_argb, // ARGB32_Premultiplied
- blend_transformed_tiled_rgb565,
- blend_src_generic, // ARGB8565_Premultiplied
- blend_src_generic, // RGB666
- blend_src_generic, // ARGB6666_Premultiplied
- blend_src_generic, // RGB555
- blend_src_generic, // ARGB8555_Premultiplied
- blend_src_generic, // RGB888
- blend_src_generic, // RGB444
- blend_src_generic, // ARGB4444_Premultiplied
- blend_src_generic, // RGBX8888
- blend_src_generic, // RGBA8888
- blend_src_generic, // RGBA8888_Premultiplied
- blend_src_generic_rgb64,
- blend_src_generic_rgb64,
- blend_src_generic_rgb64,
- blend_src_generic_rgb64,
- blend_src_generic,
- blend_src_generic,
- },
- // Bilinear
- {
- 0,
- blend_src_generic, // Mono
- blend_src_generic, // MonoLsb
- blend_src_generic, // Indexed8
- blend_src_generic, // RGB32
- blend_src_generic, // ARGB32
- blend_src_generic, // ARGB32_Premultiplied
- blend_transformed_bilinear_rgb565,
- blend_src_generic, // ARGB8565_Premultiplied
- blend_src_generic, // RGB666
- blend_src_generic, // ARGB6666_Premultiplied
- blend_src_generic, // RGB555
- blend_src_generic, // ARGB8555_Premultiplied
- blend_src_generic, // RGB888
- blend_src_generic, // RGB444
- blend_src_generic, // ARGB4444_Premultiplied
- blend_src_generic, // RGBX8888
- blend_src_generic, // RGBA8888
- blend_src_generic, // RGBA8888_Premultiplied
- blend_src_generic_rgb64,
- blend_src_generic_rgb64,
- blend_src_generic_rgb64,
- blend_src_generic_rgb64,
- blend_src_generic,
- blend_src_generic,
- },
- // BilinearTiled
- {
- 0,
- blend_src_generic, // Mono
- blend_src_generic, // MonoLsb
- blend_src_generic, // Indexed8
- blend_src_generic, // RGB32
- blend_src_generic, // ARGB32
- blend_src_generic, // ARGB32_Premultiplied
- blend_src_generic, // RGB16
- blend_src_generic, // ARGB8565_Premultiplied
- blend_src_generic, // RGB666
- blend_src_generic, // ARGB6666_Premultiplied
- blend_src_generic, // RGB555
- blend_src_generic, // ARGB8555_Premultiplied
- blend_src_generic, // RGB888
- blend_src_generic, // RGB444
- blend_src_generic, // ARGB4444_Premultiplied
- blend_src_generic, // RGBX8888
- blend_src_generic, // RGBA8888
- blend_src_generic, // RGBA8888_Premultiplied
- blend_src_generic_rgb64, // BGR30
- blend_src_generic_rgb64, // A2BGR30_Premultiplied
- blend_src_generic_rgb64, // RGB30
- blend_src_generic_rgb64, // A2RGB30_Premultiplied
- blend_src_generic, // Alpha8
- blend_src_generic, // Grayscale8
- }
+static const ProcessSpans processTextureSpansARGB32PM[NBlendTypes] = {
+ blend_untransformed_argb, // Untransformed
+ blend_tiled_argb, // Tiled
+ blend_transformed_argb, // Transformed
+ blend_transformed_tiled_argb, // TransformedTiled
+ blend_src_generic, // TransformedBilinear
+ blend_src_generic // TransformedBilinearTiled
+};
+
+static const ProcessSpans processTextureSpansRGB16[NBlendTypes] = {
+ blend_untransformed_rgb565, // Untransformed
+ blend_tiled_rgb565, // Tiled
+ blend_transformed_rgb565, // Transformed
+ blend_transformed_tiled_rgb565, // TransformedTiled
+ blend_transformed_bilinear_rgb565, // TransformedBilinear
+ blend_src_generic // TransformedBilinearTiled
+};
+
+static const ProcessSpans processTextureSpansGeneric[NBlendTypes] = {
+ blend_untransformed_generic, // Untransformed
+ blend_tiled_generic, // Tiled
+ blend_src_generic, // Transformed
+ blend_src_generic, // TransformedTiled
+ blend_src_generic, // TransformedBilinear
+ blend_src_generic // TransformedBilinearTiled
+};
+
+static const ProcessSpans processTextureSpansGeneric64[NBlendTypes] = {
+ blend_untransformed_generic_rgb64, // Untransformed
+ blend_tiled_generic_rgb64, // Tiled
+ blend_src_generic_rgb64, // Transformed
+ blend_src_generic_rgb64, // TransformedTiled
+ blend_src_generic_rgb64, // TransformedBilinear
+ blend_src_generic_rgb64 // TransformedBilinearTiled
};
void qBlendTexture(int count, const QSpan *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
- ProcessSpans proc = processTextureSpans[getBlendType(data)][data->rasterBuffer->format];
+ TextureBlendType blendType = getBlendType(data);
+ ProcessSpans proc;
+ switch (data->rasterBuffer->format) {
+ case QImage::Format_ARGB32_Premultiplied:
+ proc = processTextureSpansARGB32PM[blendType];
+ break;
+ case QImage::Format_RGB16:
+ proc = processTextureSpansRGB16[blendType];
+ break;
+ case QImage::Format_BGR30:
+ case QImage::Format_A2BGR30_Premultiplied:
+ case QImage::Format_RGB30:
+ case QImage::Format_A2RGB30_Premultiplied:
+ proc = processTextureSpansGeneric64[blendType];
+ break;
+ case QImage::Format_Invalid:
+ Q_UNREACHABLE();
+ return;
+ default:
+ proc = processTextureSpansGeneric[blendType];
+ break;
+ }
proc(count, spans, userData);
}
@@ -5766,7 +5460,7 @@ static inline void rgbBlendPixel(quint32 *dst, int coverage, int sr, int sg, int
*dst = qRgb(nr, ng, nb);
}
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN)
Q_GUI_EXPORT bool qt_needs_a8_gamma_correction = false;
static inline void grayBlendPixel(quint32 *dst, int coverage, int sr, int sg, int sb, const uint *gamma, const uchar *invgamma)
@@ -5803,7 +5497,7 @@ static void qt_alphamapblit_uint32(QRasterBuffer *rasterBuffer,
const quint32 c = color;
const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint32);
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN)
const QDrawHelperGammaTables *tables = QGuiApplicationPrivate::instance()->gammaTables();
if (!tables)
return;
@@ -5830,7 +5524,7 @@ static void qt_alphamapblit_uint32(QRasterBuffer *rasterBuffer,
} else if (coverage == 255) {
dest[i] = c;
} else {
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN)
if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP && doGrayBlendPixel
&& qAlpha(dest[i]) == 255) {
grayBlendPixel(dest+i, coverage, sr, sg, sb, gamma, invgamma);
@@ -5871,7 +5565,7 @@ static void qt_alphamapblit_uint32(QRasterBuffer *rasterBuffer,
} else if (coverage == 255) {
dest[xp] = c;
} else {
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN)
if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP && doGrayBlendPixel
&& qAlpha(dest[xp]) == 255) {
grayBlendPixel(dest+xp, coverage, sr, sg, sb, gamma, invgamma);
@@ -6329,7 +6023,7 @@ void qt_memfill32(quint32 *dest, quint32 color, int count)
#endif
#ifdef QT_COMPILER_SUPPORTS_SSE4_1
-template<QtPixelOrder> const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4(uint *buffer, const uint *src, int count, const QPixelLayout *, const QRgb *);
+template<QtPixelOrder> const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4(uint *buffer, const uint *src, int count, const QVector<QRgb> *, QDitherInfo *);
#endif
extern void qInitBlendFunctions();
@@ -6382,6 +6076,15 @@ static void qInitDrawhelperFunctions()
qt_fetch_radial_gradient = qt_fetch_radial_gradient_sse2;
+ extern void QT_FASTCALL comp_func_SourceOver_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
+ extern void QT_FASTCALL comp_func_solid_SourceOver_sse2(uint *destPixels, int length, uint color, uint const_alpha);
+ extern void QT_FASTCALL comp_func_Source_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
+ extern void QT_FASTCALL comp_func_Plus_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
+ qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_sse2;
+ qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_sse2;
+ qt_functionForMode_C[QPainter::CompositionMode_Source] = comp_func_Source_sse2;
+ qt_functionForMode_C[QPainter::CompositionMode_Plus] = comp_func_Plus_sse2;
+
#ifdef QT_COMPILER_SUPPORTS_SSSE3
if (qCpuHasFeature(SSSE3)) {
extern void qt_blend_argb32_on_argb32_ssse3(uchar *destPixels, int dbpl,
@@ -6397,22 +6100,26 @@ static void qInitDrawhelperFunctions()
qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3;
qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3;
qStorePixels[QPixelLayout::BPP24] = storePixelsBPP24_ssse3;
- sourceFetch[BlendUntransformed][QImage::Format_RGB888] = qt_fetchUntransformed_888_ssse3;
- sourceFetch[BlendTiled][QImage::Format_RGB888] = qt_fetchUntransformed_888_ssse3;
+ sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_ssse3;
}
#endif // SSSE3
#if defined(QT_COMPILER_SUPPORTS_SSE4_1)
if (qCpuHasFeature(SSE4_1)) {
#if !defined(__SSE4_1__)
- extern const uint *QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, const uint *src, int count, const QPixelLayout *, const QRgb *);
- extern const uint *QT_FASTCALL convertRGBA8888ToARGB32PM_sse4(uint *buffer, const uint *src, int count, const QPixelLayout *, const QRgb *);
+ extern const uint *QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, const uint *src, int count,
+ const QVector<QRgb> *, QDitherInfo *);
+ extern const uint *QT_FASTCALL convertRGBA8888ToARGB32PM_sse4(uint *buffer, const uint *src, int count,
+ const QVector<QRgb> *, QDitherInfo *);
qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_sse4;
qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_sse4;
#endif
- extern const uint *QT_FASTCALL convertARGB32FromARGB32PM_sse4(uint *buffer, const uint *src, int count, const QPixelLayout *, const QRgb *);
- extern const uint *QT_FASTCALL convertRGBA8888FromARGB32PM_sse4(uint *buffer, const uint *src, int count, const QPixelLayout *, const QRgb *);
- extern const uint *QT_FASTCALL convertRGBXFromARGB32PM_sse4(uint *buffer, const uint *src, int count, const QPixelLayout *, const QRgb *);
+ extern const uint *QT_FASTCALL convertARGB32FromARGB32PM_sse4(uint *buffer, const uint *src, int count,
+ const QVector<QRgb> *, QDitherInfo *);
+ extern const uint *QT_FASTCALL convertRGBA8888FromARGB32PM_sse4(uint *buffer, const uint *src, int count,
+ const QVector<QRgb> *, QDitherInfo *);
+ extern const uint *QT_FASTCALL convertRGBXFromARGB32PM_sse4(uint *buffer, const uint *src, int count,
+ const QVector<QRgb> *, QDitherInfo *);
qPixelLayouts[QImage::Format_ARGB32].convertFromARGB32PM = convertARGB32FromARGB32PM_sse4;
qPixelLayouts[QImage::Format_RGBA8888].convertFromARGB32PM = convertRGBA8888FromARGB32PM_sse4;
qPixelLayouts[QImage::Format_RGBX8888].convertFromARGB32PM = convertRGBXFromARGB32PM_sse4;
@@ -6421,22 +6128,39 @@ static void qInitDrawhelperFunctions()
}
#endif
-#if defined(QT_COMPILER_SUPPORTS_AVX2) && !defined(__AVX2__)
+#if defined(QT_COMPILER_SUPPORTS_AVX2)
if (qCpuHasFeature(AVX2)) {
- extern const uint *QT_FASTCALL convertARGB32ToARGB32PM_avx2(uint *buffer, const uint *src, int count, const QPixelLayout *, const QRgb *);
- extern const uint *QT_FASTCALL convertRGBA8888ToARGB32PM_avx2(uint *buffer, const uint *src, int count, const QPixelLayout *, const QRgb *);
+#if !defined(__AVX2__)
+ extern const uint *QT_FASTCALL convertARGB32ToARGB32PM_avx2(uint *buffer, const uint *src, int count,
+ const QVector<QRgb> *, QDitherInfo *);
+ extern const uint *QT_FASTCALL convertRGBA8888ToARGB32PM_avx2(uint *buffer, const uint *src, int count,
+ const QVector<QRgb> *, QDitherInfo *);
qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_avx2;
qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_avx2;
+#endif
+ extern void qt_blend_rgb32_on_rgb32_avx2(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h, int const_alpha);
+ extern void qt_blend_argb32_on_argb32_avx2(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h, int const_alpha);
+ qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_avx2;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_avx2;
+ qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_avx2;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_avx2;
+ qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_avx2;
+ qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_avx2;
+ qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_avx2;
+ qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_avx2;
+
+ extern void QT_FASTCALL comp_func_SourceOver_avx2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
+ extern void QT_FASTCALL comp_func_solid_SourceOver_avx2(uint *destPixels, int length, uint color, uint const_alpha);
+ extern void QT_FASTCALL comp_func_Source_avx2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
+ qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_avx2;
+ qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_avx2;
+ qt_functionForMode_C[QPainter::CompositionMode_Source] = comp_func_Source_avx2;
}
#endif
- extern void QT_FASTCALL comp_func_SourceOver_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
- extern void QT_FASTCALL comp_func_solid_SourceOver_sse2(uint *destPixels, int length, uint color, uint const_alpha);
- extern void QT_FASTCALL comp_func_Source_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
- extern void QT_FASTCALL comp_func_Plus_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
- qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_sse2;
- qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_sse2;
- qt_functionForMode_C[QPainter::CompositionMode_Source] = comp_func_Source_sse2;
- qt_functionForMode_C[QPainter::CompositionMode_Plus] = comp_func_Plus_sse2;
#endif // SSE2
@@ -6461,8 +6185,7 @@ static void qInitDrawhelperFunctions()
qt_fetch_radial_gradient = qt_fetch_radial_gradient_neon;
- sourceFetch[BlendUntransformed][QImage::Format_RGB888] = qt_fetchUntransformed_888_neon;
- sourceFetch[BlendTiled][QImage::Format_RGB888] = qt_fetchUntransformed_888_neon;
+ sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_neon;
#if defined(ENABLE_PIXMAN_DRAWHELPERS)
// The RGB16 helpers are using Arm32 assemblythat has not been ported to AArch64
@@ -6523,14 +6246,9 @@ static void qInitDrawhelperFunctions()
destStoreProc[QImage::Format_ARGB32] = qt_destStoreARGB32_mips_dsp;
- sourceFetch[BlendUntransformed][QImage::Format_RGB888] = qt_fetchUntransformed_888_mips_dsp;
- sourceFetch[BlendTiled][QImage::Format_RGB888] = qt_fetchUntransformed_888_mips_dsp;
-
- sourceFetch[BlendUntransformed][QImage::Format_RGB444] = qt_fetchUntransformed_444_mips_dsp;
- sourceFetch[BlendTiled][QImage::Format_RGB444] = qt_fetchUntransformed_444_mips_dsp;
-
- sourceFetch[BlendUntransformed][QImage::Format_ARGB8565_Premultiplied] = qt_fetchUntransformed_argb8565_premultiplied_mips_dsp;
- sourceFetch[BlendTiled][QImage::Format_ARGB8565_Premultiplied] = qt_fetchUntransformed_argb8565_premultiplied_mips_dsp;
+ sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_mips_dsp;
+ sourceFetchUntransformed[QImage::Format_RGB444] = qt_fetchUntransformed_444_mips_dsp;
+ sourceFetchUntransformed[QImage::Format_ARGB8565_Premultiplied] = qt_fetchUntransformed_argb8565_premultiplied_mips_dsp;
#if defined(QT_COMPILER_SUPPORTS_MIPS_DSPR2)
qBlendFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_blend_rgb16_on_rgb16_mips_dspr2;
diff --git a/src/gui/painting/qdrawhelper_avx2.cpp b/src/gui/painting/qdrawhelper_avx2.cpp
index e11536ebd0..b3fa380dc0 100644
--- a/src/gui/painting/qdrawhelper_avx2.cpp
+++ b/src/gui/painting/qdrawhelper_avx2.cpp
@@ -37,24 +37,327 @@
**
****************************************************************************/
-#include <private/qdrawhelper_p.h>
+#include "qdrawhelper_p.h"
+#include "qdrawingprimitive_sse2_p.h"
#if defined(QT_COMPILER_SUPPORTS_AVX2)
QT_BEGIN_NAMESPACE
+// Autovectorized premultiply functions:
const uint *QT_FASTCALL convertARGB32ToARGB32PM_avx2(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
return qt_convertARGB32ToARGB32PM(buffer, src, count);
}
const uint *QT_FASTCALL convertRGBA8888ToARGB32PM_avx2(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
return qt_convertRGBA8888ToARGB32PM(buffer, src, count);
}
+// Vectorized blend functions:
+
+// See BYTE_MUL_SSE2 for details.
+inline static void BYTE_MUL_AVX2(__m256i &pixelVector, const __m256i &alphaChannel, const __m256i &colorMask, const __m256i &half)
+{
+ __m256i pixelVectorAG = _mm256_srli_epi16(pixelVector, 8);
+ __m256i pixelVectorRB = _mm256_and_si256(pixelVector, colorMask);
+
+ pixelVectorAG = _mm256_mullo_epi16(pixelVectorAG, alphaChannel);
+ pixelVectorRB = _mm256_mullo_epi16(pixelVectorRB, alphaChannel);
+
+ pixelVectorRB = _mm256_add_epi16(pixelVectorRB, _mm256_srli_epi16(pixelVectorRB, 8));
+ pixelVectorAG = _mm256_add_epi16(pixelVectorAG, _mm256_srli_epi16(pixelVectorAG, 8));
+ pixelVectorRB = _mm256_add_epi16(pixelVectorRB, half);
+ pixelVectorAG = _mm256_add_epi16(pixelVectorAG, half);
+
+ pixelVectorRB = _mm256_srli_epi16(pixelVectorRB, 8);
+ pixelVectorAG = _mm256_andnot_si256(colorMask, pixelVectorAG);
+
+ pixelVector = _mm256_or_si256(pixelVectorAG, pixelVectorRB);
+}
+
+// See INTERPOLATE_PIXEL_255_SSE2 for details.
+inline static void INTERPOLATE_PIXEL_255_AVX2(const __m256i &srcVector, __m256i &dstVector, const __m256i &alphaChannel, const __m256i &oneMinusAlphaChannel, const __m256i &colorMask, const __m256i &half)
+{
+ const __m256i srcVectorAG = _mm256_srli_epi16(srcVector, 8);
+ const __m256i dstVectorAG = _mm256_srli_epi16(dstVector, 8);
+ const __m256i srcVectorRB = _mm256_and_si256(srcVector, colorMask);
+ const __m256i dstVectorRB = _mm256_and_si256(dstVector, colorMask);
+ const __m256i srcVectorAGalpha = _mm256_mullo_epi16(srcVectorAG, alphaChannel);
+ const __m256i srcVectorRBalpha = _mm256_mullo_epi16(srcVectorRB, alphaChannel);
+ const __m256i dstVectorAGoneMinusAlpha = _mm256_mullo_epi16(dstVectorAG, oneMinusAlphaChannel);
+ const __m256i dstVectorRBoneMinusAlpha = _mm256_mullo_epi16(dstVectorRB, oneMinusAlphaChannel);
+ __m256i finalAG = _mm256_add_epi16(srcVectorAGalpha, dstVectorAGoneMinusAlpha);
+ __m256i finalRB = _mm256_add_epi16(srcVectorRBalpha, dstVectorRBoneMinusAlpha);
+ finalAG = _mm256_add_epi16(finalAG, _mm256_srli_epi16(finalAG, 8));
+ finalRB = _mm256_add_epi16(finalRB, _mm256_srli_epi16(finalRB, 8));
+ finalAG = _mm256_add_epi16(finalAG, half);
+ finalRB = _mm256_add_epi16(finalRB, half);
+ finalAG = _mm256_andnot_si256(colorMask, finalAG);
+ finalRB = _mm256_srli_epi16(finalRB, 8);
+
+ dstVector = _mm256_or_si256(finalAG, finalRB);
+}
+
+// See BLEND_SOURCE_OVER_ARGB32_SSE2 for details.
+inline static void BLEND_SOURCE_OVER_ARGB32_AVX2(quint32 *dst, const quint32 *src, const int length)
+{
+ const __m256i half = _mm256_set1_epi16(0x80);
+ const __m256i one = _mm256_set1_epi16(0xff);
+ const __m256i colorMask = _mm256_set1_epi32(0x00ff00ff);
+ const __m256i alphaMask = _mm256_set1_epi32(0xff000000);
+ const __m256i offsetMask = _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7);
+ const __m256i alphaShuffleMask = _mm256_set_epi8(char(0xff),15,char(0xff),15,char(0xff),11,char(0xff),11,char(0xff),7,char(0xff),7,char(0xff),3,char(0xff),3,
+ char(0xff),15,char(0xff),15,char(0xff),11,char(0xff),11,char(0xff),7,char(0xff),7,char(0xff),3,char(0xff),3);
+
+ const int minusOffsetToAlignDstOn32Bytes = (reinterpret_cast<quintptr>(dst) >> 2) & 0x7;
+
+ int x = 0;
+ // Prologue to handle all pixels until dst is 32-byte aligned in one step.
+ if (minusOffsetToAlignDstOn32Bytes != 0 && x < (length - 7)) {
+ const __m256i prologueMask = _mm256_sub_epi32(_mm256_set1_epi32(minusOffsetToAlignDstOn32Bytes - 1), offsetMask);
+ const __m256i srcVector = _mm256_maskload_epi32((const int *)&src[x - minusOffsetToAlignDstOn32Bytes], prologueMask);
+ const __m256i prologueAlphaMask = _mm256_blendv_epi8(_mm256_setzero_si256(), alphaMask, prologueMask);
+ if (!_mm256_testz_si256(srcVector, prologueAlphaMask)) {
+ if (_mm256_testc_si256(srcVector, prologueAlphaMask)) {
+ _mm256_maskstore_epi32((int *)&dst[x - minusOffsetToAlignDstOn32Bytes], prologueMask, srcVector);
+ } else {
+ __m256i alphaChannel = _mm256_shuffle_epi8(srcVector, alphaShuffleMask);
+ alphaChannel = _mm256_sub_epi16(one, alphaChannel);
+ __m256i dstVector = _mm256_maskload_epi32((int *)&dst[x - minusOffsetToAlignDstOn32Bytes], prologueMask);
+ BYTE_MUL_AVX2(dstVector, alphaChannel, colorMask, half);
+ dstVector = _mm256_add_epi8(dstVector, srcVector);
+ _mm256_maskstore_epi32((int *)&dst[x - minusOffsetToAlignDstOn32Bytes], prologueMask, dstVector);
+ }
+ }
+ x += (8 - minusOffsetToAlignDstOn32Bytes);
+ }
+
+ for (; x < (length - 7); x += 8) {
+ const __m256i srcVector = _mm256_lddqu_si256((const __m256i *)&src[x]);
+ if (!_mm256_testz_si256(srcVector, alphaMask)) {
+ if (_mm256_testc_si256(srcVector, alphaMask)) {
+ _mm256_store_si256((__m256i *)&dst[x], srcVector);
+ } else {
+ __m256i alphaChannel = _mm256_shuffle_epi8(srcVector, alphaShuffleMask);
+ alphaChannel = _mm256_sub_epi16(one, alphaChannel);
+ __m256i dstVector = _mm256_load_si256((__m256i *)&dst[x]);
+ BYTE_MUL_AVX2(dstVector, alphaChannel, colorMask, half);
+ dstVector = _mm256_add_epi8(dstVector, srcVector);
+ _mm256_store_si256((__m256i *)&dst[x], dstVector);
+ }
+ }
+ }
+
+ // Epilogue to handle all remaining pixels in one step.
+ if (x < length) {
+ const __m256i epilogueMask = _mm256_add_epi32(offsetMask, _mm256_set1_epi32(x - length));
+ const __m256i srcVector = _mm256_maskload_epi32((const int *)&src[x], epilogueMask);
+ const __m256i epilogueAlphaMask = _mm256_blendv_epi8(_mm256_setzero_si256(), alphaMask, epilogueMask);
+ if (!_mm256_testz_si256(srcVector, epilogueAlphaMask)) {
+ if (_mm256_testc_si256(srcVector, epilogueAlphaMask)) {
+ _mm256_maskstore_epi32((int *)&dst[x], epilogueMask, srcVector);
+ } else {
+ __m256i alphaChannel = _mm256_shuffle_epi8(srcVector, alphaShuffleMask);
+ alphaChannel = _mm256_sub_epi16(one, alphaChannel);
+ __m256i dstVector = _mm256_maskload_epi32((int *)&dst[x], epilogueMask);
+ BYTE_MUL_AVX2(dstVector, alphaChannel, colorMask, half);
+ dstVector = _mm256_add_epi8(dstVector, srcVector);
+ _mm256_maskstore_epi32((int *)&dst[x], epilogueMask, dstVector);
+ }
+ }
+ }
+}
+
+
+// See BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_SSE2 for details.
+inline static void BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_AVX2(quint32 *dst, const quint32 *src, const int length, const int const_alpha)
+{
+ int x = 0;
+
+ ALIGNMENT_PROLOGUE_32BYTES(dst, x, length)
+ blend_pixel(dst[x], src[x], const_alpha);
+
+ const __m256i half = _mm256_set1_epi16(0x80);
+ const __m256i one = _mm256_set1_epi16(0xff);
+ const __m256i colorMask = _mm256_set1_epi32(0x00ff00ff);
+ const __m256i alphaMask = _mm256_set1_epi32(0xff000000);
+ const __m256i alphaShuffleMask = _mm256_set_epi8(char(0xff),15,char(0xff),15,char(0xff),11,char(0xff),11,char(0xff),7,char(0xff),7,char(0xff),3,char(0xff),3,
+ char(0xff),15,char(0xff),15,char(0xff),11,char(0xff),11,char(0xff),7,char(0xff),7,char(0xff),3,char(0xff),3);
+ const __m256i constAlphaVector = _mm256_set1_epi16(const_alpha);
+ for (; x < (length - 7); x += 8) {
+ __m256i srcVector = _mm256_lddqu_si256((const __m256i *)&src[x]);
+ if (!_mm256_testz_si256(srcVector, alphaMask)) {
+ BYTE_MUL_AVX2(srcVector, constAlphaVector, colorMask, half);
+
+ __m256i alphaChannel = _mm256_shuffle_epi8(srcVector, alphaShuffleMask);
+ alphaChannel = _mm256_sub_epi16(one, alphaChannel);
+ __m256i dstVector = _mm256_load_si256((__m256i *)&dst[x]);
+ BYTE_MUL_AVX2(dstVector, alphaChannel, colorMask, half);
+ dstVector = _mm256_add_epi8(dstVector, srcVector);
+ _mm256_store_si256((__m256i *)&dst[x], dstVector);
+ }
+ }
+ for (; x < length; ++x)
+ blend_pixel(dst[x], src[x], const_alpha);
+}
+
+void qt_blend_argb32_on_argb32_avx2(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ if (const_alpha == 256) {
+ for (int y = 0; y < h; ++y) {
+ const quint32 *src = reinterpret_cast<const quint32 *>(srcPixels);
+ quint32 *dst = reinterpret_cast<quint32 *>(destPixels);
+ BLEND_SOURCE_OVER_ARGB32_AVX2(dst, src, w);
+ destPixels += dbpl;
+ srcPixels += sbpl;
+ }
+ } else if (const_alpha != 0) {
+ const_alpha = (const_alpha * 255) >> 8;
+ for (int y = 0; y < h; ++y) {
+ const quint32 *src = reinterpret_cast<const quint32 *>(srcPixels);
+ quint32 *dst = reinterpret_cast<quint32 *>(destPixels);
+ BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_AVX2(dst, src, w, const_alpha);
+ destPixels += dbpl;
+ srcPixels += sbpl;
+ }
+ }
+}
+
+void qt_blend_rgb32_on_rgb32_avx2(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ if (const_alpha == 256) {
+ for (int y = 0; y < h; ++y) {
+ const quint32 *src = reinterpret_cast<const quint32 *>(srcPixels);
+ quint32 *dst = reinterpret_cast<quint32 *>(destPixels);
+ ::memcpy(dst, src, w * sizeof(uint));
+ srcPixels += sbpl;
+ destPixels += dbpl;
+ }
+ return;
+ }
+ if (const_alpha == 0)
+ return;
+
+ const __m256i half = _mm256_set1_epi16(0x80);
+ const __m256i colorMask = _mm256_set1_epi32(0x00ff00ff);
+
+ const_alpha = (const_alpha * 255) >> 8;
+ int one_minus_const_alpha = 255 - const_alpha;
+ const __m256i constAlphaVector = _mm256_set1_epi16(const_alpha);
+ const __m256i oneMinusConstAlpha = _mm256_set1_epi16(one_minus_const_alpha);
+ for (int y = 0; y < h; ++y) {
+ const quint32 *src = reinterpret_cast<const quint32 *>(srcPixels);
+ quint32 *dst = reinterpret_cast<quint32 *>(destPixels);
+ int x = 0;
+
+ // First, align dest to 32 bytes:
+ ALIGNMENT_PROLOGUE_32BYTES(dst, x, w)
+ dst[x] = INTERPOLATE_PIXEL_255(src[x], const_alpha, dst[x], one_minus_const_alpha);
+
+ // 2) interpolate pixels with AVX2
+ for (; x < (w - 7); x += 8) {
+ const __m256i srcVector = _mm256_lddqu_si256((const __m256i *)&src[x]);
+ if (!_mm256_testz_si256(srcVector, srcVector)) {
+ __m256i dstVector = _mm256_load_si256((__m256i *)&dst[x]);
+ INTERPOLATE_PIXEL_255_AVX2(srcVector, dstVector, constAlphaVector, oneMinusConstAlpha, colorMask, half);
+ _mm256_store_si256((__m256i *)&dst[x], dstVector);
+ }
+ }
+
+ // 3) Epilogue
+ for (; x < w; ++x)
+ dst[x] = INTERPOLATE_PIXEL_255(src[x], const_alpha, dst[x], one_minus_const_alpha);
+
+ srcPixels += sbpl;
+ destPixels += dbpl;
+ }
+}
+
+void QT_FASTCALL comp_func_SourceOver_avx2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha)
+{
+ Q_ASSERT(const_alpha < 256);
+
+ const quint32 *src = (const quint32 *) srcPixels;
+ quint32 *dst = (quint32 *) destPixels;
+
+ if (const_alpha == 255)
+ BLEND_SOURCE_OVER_ARGB32_AVX2(dst, src, length);
+ else
+ BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_AVX2(dst, src, length, const_alpha);
+}
+
+void QT_FASTCALL comp_func_Source_avx2(uint *dst, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ ::memcpy(dst, src, length * sizeof(uint));
+ } else {
+ const int ialpha = 255 - const_alpha;
+
+ int x = 0;
+
+ // 1) prologue, align on 32 bytes
+ ALIGNMENT_PROLOGUE_32BYTES(dst, x, length)
+ dst[x] = INTERPOLATE_PIXEL_255(src[x], const_alpha, dst[x], ialpha);
+
+ // 2) interpolate pixels with AVX2
+ const __m256i half = _mm256_set1_epi16(0x80);
+ const __m256i colorMask = _mm256_set1_epi32(0x00ff00ff);
+ const __m256i constAlphaVector = _mm256_set1_epi16(const_alpha);
+ const __m256i oneMinusConstAlpha = _mm256_set1_epi16(ialpha);
+ for (; x < length - 7; x += 8) {
+ const __m256i srcVector = _mm256_lddqu_si256((const __m256i *)&src[x]);
+ __m256i dstVector = _mm256_load_si256((__m256i *)&dst[x]);
+ INTERPOLATE_PIXEL_255_AVX2(srcVector, dstVector, constAlphaVector, oneMinusConstAlpha, colorMask, half);
+ _mm256_store_si256((__m256i *)&dst[x], dstVector);
+ }
+
+ // 3) Epilogue
+ for (; x < length; ++x)
+ dst[x] = INTERPOLATE_PIXEL_255(src[x], const_alpha, dst[x], ialpha);
+ }
+}
+
+void QT_FASTCALL comp_func_solid_SourceOver_avx2(uint *destPixels, int length, uint color, uint const_alpha)
+{
+ if ((const_alpha & qAlpha(color)) == 255) {
+ qt_memfill32(destPixels, color, length);
+ } else {
+ if (const_alpha != 255)
+ color = BYTE_MUL(color, const_alpha);
+
+ const quint32 minusAlphaOfColor = qAlpha(~color);
+ int x = 0;
+
+ quint32 *dst = (quint32 *) destPixels;
+ const __m256i colorVector = _mm256_set1_epi32(color);
+ const __m256i colorMask = _mm256_set1_epi32(0x00ff00ff);
+ const __m256i half = _mm256_set1_epi16(0x80);
+ const __m256i minusAlphaOfColorVector = _mm256_set1_epi16(minusAlphaOfColor);
+
+ ALIGNMENT_PROLOGUE_32BYTES(dst, x, length)
+ destPixels[x] = color + BYTE_MUL(destPixels[x], minusAlphaOfColor);
+
+ for (; x < length - 7; x += 8) {
+ __m256i dstVector = _mm256_load_si256((__m256i *)&dst[x]);
+ BYTE_MUL_AVX2(dstVector, minusAlphaOfColorVector, colorMask, half);
+ dstVector = _mm256_add_epi8(colorVector, dstVector);
+ _mm256_store_si256((__m256i *)&dst[x], dstVector);
+ }
+ for (; x < length; ++x)
+ destPixels[x] = color + BYTE_MUL(destPixels[x], minusAlphaOfColor);
+ }
+}
+
QT_END_NAMESPACE
#endif
diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h
index fa3e6396ec..45a3174734 100644
--- a/src/gui/painting/qdrawhelper_p.h
+++ b/src/gui/painting/qdrawhelper_p.h
@@ -51,7 +51,7 @@
// We mean it.
//
-#include "QtCore/qglobal.h"
+#include <QtGui/private/qtguiglobal_p.h>
#include "QtCore/qmath.h"
#include "QtGui/qcolor.h"
#include "QtGui/qpainter.h"
@@ -636,6 +636,22 @@ static Q_ALWAYS_INLINE uint BYTE_MUL(uint x, uint a) {
}
#endif
+static Q_ALWAYS_INLINE void blend_pixel(quint32 &dst, const quint32 src)
+{
+ if (src >= 0xff000000)
+ dst = src;
+ else if (src != 0)
+ dst = src + BYTE_MUL(dst, qAlpha(~src));
+}
+
+static Q_ALWAYS_INLINE void blend_pixel(quint32 &dst, const quint32 src, const int const_alpha)
+{
+ if (src != 0) {
+ const quint32 s = BYTE_MUL(src, const_alpha);
+ dst = s + BYTE_MUL(dst, qAlpha(~s));
+ }
+}
+
#if defined(__SSE2__)
static Q_ALWAYS_INLINE uint interpolate_4_pixels_sse2(__m128i vt, __m128i vb, uint distx, uint disty)
{
@@ -1172,11 +1188,15 @@ inline int comp_func_Plus_one_pixel(uint d, const uint s)
#undef MIX
#undef AMIX
-struct QPixelLayout;
+struct QDitherInfo {
+ int x;
+ int y;
+};
+
typedef const uint *(QT_FASTCALL *ConvertFunc)(uint *buffer, const uint *src, int count,
- const QPixelLayout *layout, const QRgb *clut);
+ const QVector<QRgb> *clut, QDitherInfo *dither);
typedef const QRgba64 *(QT_FASTCALL *ConvertFunc64)(QRgba64 *buffer, const uint *src, int count,
- const QPixelLayout *layout, const QRgb *clut);
+ const QVector<QRgb> *clut, QDitherInfo *dither);
struct QPixelLayout
{
diff --git a/src/gui/painting/qdrawhelper_sse4.cpp b/src/gui/painting/qdrawhelper_sse4.cpp
index a39cdb3127..257bad9eca 100644
--- a/src/gui/painting/qdrawhelper_sse4.cpp
+++ b/src/gui/painting/qdrawhelper_sse4.cpp
@@ -45,19 +45,19 @@
QT_BEGIN_NAMESPACE
const uint *QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
return qt_convertARGB32ToARGB32PM(buffer, src, count);
}
const uint *QT_FASTCALL convertRGBA8888ToARGB32PM_sse4(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
return qt_convertRGBA8888ToARGB32PM(buffer, src, count);
}
const uint *QT_FASTCALL convertARGB32FromARGB32PM_sse4(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = qUnpremultiply_sse4(src[i]);
@@ -65,7 +65,7 @@ const uint *QT_FASTCALL convertARGB32FromARGB32PM_sse4(uint *buffer, const uint
}
const uint *QT_FASTCALL convertRGBA8888FromARGB32PM_sse4(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = ARGB2RGBA(qUnpremultiply_sse4(src[i]));
@@ -73,7 +73,7 @@ const uint *QT_FASTCALL convertRGBA8888FromARGB32PM_sse4(uint *buffer, const uin
}
const uint *QT_FASTCALL convertRGBXFromARGB32PM_sse4(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = ARGB2RGBA(0xff000000 | qUnpremultiply_sse4(src[i]));
@@ -82,7 +82,7 @@ const uint *QT_FASTCALL convertRGBXFromARGB32PM_sse4(uint *buffer, const uint *s
template<QtPixelOrder PixelOrder>
const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+ const QVector<QRgb> *, QDitherInfo *)
{
for (int i = 0; i < count; ++i)
buffer[i] = qConvertArgb32ToA2rgb30_sse4<PixelOrder>(src[i]);
@@ -91,10 +91,10 @@ const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4(uint *buffer, const ui
template
const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *);
+ const QVector<QRgb> *, QDitherInfo *);
template
const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *);
+ const QVector<QRgb> *, QDitherInfo *);
QT_END_NAMESPACE
diff --git a/src/gui/painting/qdrawhelper_ssse3.cpp b/src/gui/painting/qdrawhelper_ssse3.cpp
index 7cd3e9ca1b..2026a4e656 100644
--- a/src/gui/painting/qdrawhelper_ssse3.cpp
+++ b/src/gui/painting/qdrawhelper_ssse3.cpp
@@ -45,15 +45,6 @@
QT_BEGIN_NAMESPACE
-inline static void blend_pixel(quint32 &dst, const quint32 src)
-{
- if (src >= 0xff000000)
- dst = src;
- else if (src != 0)
- dst = src + BYTE_MUL(dst, qAlpha(~src));
-}
-
-
/* The instruction palignr uses direct arguments, so we have to generate the code fo the different
shift (4, 8, 12). Checking the alignment inside the loop is unfortunatelly way too slow.
*/
diff --git a/src/gui/painting/qdrawhelper_x86_p.h b/src/gui/painting/qdrawhelper_x86_p.h
index 37be8b89a9..cefc213999 100644
--- a/src/gui/painting/qdrawhelper_x86_p.h
+++ b/src/gui/painting/qdrawhelper_x86_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include <private/qdrawhelper_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/gui/painting/qdrawingprimitive_sse2_p.h b/src/gui/painting/qdrawingprimitive_sse2_p.h
index e16529e2d0..7affc63b32 100644
--- a/src/gui/painting/qdrawingprimitive_sse2_p.h
+++ b/src/gui/painting/qdrawingprimitive_sse2_p.h
@@ -40,6 +40,7 @@
#ifndef QDRAWINGPRIMITIVE_SSE2_P_H
#define QDRAWINGPRIMITIVE_SSE2_P_H
+#include <QtGui/private/qtguiglobal_p.h>
#include <private/qsimd_p.h>
#include "qdrawhelper_p.h"
@@ -170,11 +171,7 @@ QT_BEGIN_NAMESPACE
\
/* First, get dst aligned. */ \
ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) { \
- uint s = src[x]; \
- if (s >= 0xff000000) \
- dst[x] = s; \
- else if (s != 0) \
- dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); \
+ blend_pixel(dst[x], src[x]); \
} \
\
for (; x < length-3; x += 4) { \
@@ -182,11 +179,7 @@ QT_BEGIN_NAMESPACE
BLEND_SOURCE_OVER_ARGB32_SSE2_helper(dst, srcVector, nullVector, half, one, colorMask, alphaMask) \
} \
for (; x < length; ++x) { \
- uint s = src[x]; \
- if (s >= 0xff000000) \
- dst[x] = s; \
- else if (s != 0) \
- dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); \
+ blend_pixel(dst[x], src[x]); \
} \
}
@@ -206,11 +199,7 @@ QT_BEGIN_NAMESPACE
int x = 0; \
\
ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) { \
- quint32 s = src[x]; \
- if (s != 0) { \
- s = BYTE_MUL(s, const_alpha); \
- dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); \
- } \
+ blend_pixel(dst[x], src[x], const_alpha); \
} \
\
for (; x < length-3; x += 4) { \
@@ -231,11 +220,7 @@ QT_BEGIN_NAMESPACE
} \
} \
for (; x < length; ++x) { \
- quint32 s = src[x]; \
- if (s != 0) { \
- s = BYTE_MUL(s, const_alpha); \
- dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); \
- } \
+ blend_pixel(dst[x], src[x], const_alpha); \
} \
}
diff --git a/src/gui/painting/qemulationpaintengine_p.h b/src/gui/painting/qemulationpaintengine_p.h
index f3cf88af17..457cc06d63 100644
--- a/src/gui/painting/qemulationpaintengine_p.h
+++ b/src/gui/painting/qemulationpaintengine_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include <private/qpaintengineex_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/gui/painting/qfixed_p.h b/src/gui/painting/qfixed_p.h
index d2b6e51ab1..846592881c 100644
--- a/src/gui/painting/qfixed_p.h
+++ b/src/gui/painting/qfixed_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include "QtCore/qdebug.h"
#include "QtCore/qpoint.h"
#include "QtCore/qsize.h"
diff --git a/src/gui/painting/qmatrix.h b/src/gui/painting/qmatrix.h
index b28c8433cd..76bf61671c 100644
--- a/src/gui/painting/qmatrix.h
+++ b/src/gui/painting/qmatrix.h
@@ -40,6 +40,7 @@
#ifndef QMATRIX_H
#define QMATRIX_H
+#include <QtGui/qtguiglobal.h>
#include <QtGui/qpolygon.h>
#include <QtGui/qregion.h>
#include <QtGui/qwindowdefs.h>
diff --git a/src/gui/painting/qmemrotate_p.h b/src/gui/painting/qmemrotate_p.h
index 64cfb22517..62613d301a 100644
--- a/src/gui/painting/qmemrotate_p.h
+++ b/src/gui/painting/qmemrotate_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include "private/qdrawhelper_p.h"
QT_BEGIN_NAMESPACE
diff --git a/src/gui/painting/qoutlinemapper_p.h b/src/gui/painting/qoutlinemapper_p.h
index 47413d920c..71999fbdee 100644
--- a/src/gui/painting/qoutlinemapper_p.h
+++ b/src/gui/painting/qoutlinemapper_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include <QtCore/qrect.h>
#include <QtGui/qtransform.h>
diff --git a/src/gui/painting/qpagedpaintdevice.h b/src/gui/painting/qpagedpaintdevice.h
index 3aebbb7551..c516f6a963 100644
--- a/src/gui/painting/qpagedpaintdevice.h
+++ b/src/gui/painting/qpagedpaintdevice.h
@@ -40,6 +40,7 @@
#ifndef QPAGEDPAINTDEVICE_H
#define QPAGEDPAINTDEVICE_H
+#include <QtGui/qtguiglobal.h>
#include <QtGui/qpaintdevice.h>
#include <QtGui/qpagelayout.h>
diff --git a/src/gui/painting/qpagedpaintdevice_p.h b/src/gui/painting/qpagedpaintdevice_p.h
index 028ab3b5af..a993ea4cac 100644
--- a/src/gui/painting/qpagedpaintdevice_p.h
+++ b/src/gui/painting/qpagedpaintdevice_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include <qpagedpaintdevice.h>
QT_BEGIN_NAMESPACE
diff --git a/src/gui/painting/qpagelayout.h b/src/gui/painting/qpagelayout.h
index bd29165777..b41689d33b 100644
--- a/src/gui/painting/qpagelayout.h
+++ b/src/gui/painting/qpagelayout.h
@@ -40,6 +40,7 @@
#ifndef QPAGELAYOUT_H
#define QPAGELAYOUT_H
+#include <QtGui/qtguiglobal.h>
#include <QtCore/qsharedpointer.h>
#include <QtCore/qstring.h>
#include <QtCore/qmargins.h>
diff --git a/src/gui/painting/qpagesize.cpp b/src/gui/painting/qpagesize.cpp
index f53285d9cb..8831d60d48 100644
--- a/src/gui/painting/qpagesize.cpp
+++ b/src/gui/painting/qpagesize.cpp
@@ -400,7 +400,7 @@ static QPageSize::PageSizeId qt_idForPpdKey(const QString &ppdKey, QSize *match
{
if (ppdKey.isEmpty())
return QPageSize::Custom;
- QString key = ppdKey;
+ QStringRef key(&ppdKey);
// Remove any Rotated or Tranverse modifiers
if (key.endsWith(QLatin1String("Rotated")))
key.chop(7);
diff --git a/src/gui/painting/qpagesize.h b/src/gui/painting/qpagesize.h
index 4916ddd4b5..82054824b4 100644
--- a/src/gui/painting/qpagesize.h
+++ b/src/gui/painting/qpagesize.h
@@ -40,6 +40,7 @@
#ifndef QPAGESIZE_H
#define QPAGESIZE_H
+#include <QtGui/qtguiglobal.h>
#include <QtCore/qsharedpointer.h>
QT_BEGIN_NAMESPACE
diff --git a/src/gui/painting/qpaintdevice.h b/src/gui/painting/qpaintdevice.h
index 5c9b966029..9458b4ba9a 100644
--- a/src/gui/painting/qpaintdevice.h
+++ b/src/gui/painting/qpaintdevice.h
@@ -40,6 +40,7 @@
#ifndef QPAINTDEVICE_H
#define QPAINTDEVICE_H
+#include <QtGui/qtguiglobal.h>
#include <QtGui/qwindowdefs.h>
#include <QtCore/qrect.h>
diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h
index a9da37b354..9fb67e253e 100644
--- a/src/gui/painting/qpaintengine.h
+++ b/src/gui/painting/qpaintengine.h
@@ -40,6 +40,7 @@
#ifndef QPAINTENGINE_H
#define QPAINTENGINE_H
+#include <QtGui/qtguiglobal.h>
#include <QtCore/qnamespace.h>
#include <QtCore/qobjectdefs.h>
#include <QtCore/qscopedpointer.h>
diff --git a/src/gui/painting/qpaintengine_blitter.cpp b/src/gui/painting/qpaintengine_blitter.cpp
index 81191d07b8..a50d1dfd73 100644
--- a/src/gui/painting/qpaintengine_blitter.cpp
+++ b/src/gui/painting/qpaintengine_blitter.cpp
@@ -312,7 +312,7 @@ void QBlitterPaintEnginePrivate::updateBrushState(QPainterState *s)
{
Qt::BrushStyle style = qbrush_style(s->brush);
- caps.updateState(STATE_BRUSH_PATTERN, style > Qt::SolidPattern);
+ caps.updateState(STATE_BRUSH_PATTERN, style != Qt::SolidPattern);
caps.updateState(STATE_BRUSH_ALPHA,
qbrush_color(s->brush).alpha() < 255);
}
@@ -374,9 +374,8 @@ void QBlitterPaintEnginePrivate::fillRect(const QRectF &rect, const QColor &colo
else
pmData->blittable()->fillRect(targetRect & clipData->clipRect, color);
} else if (clipData->hasRegionClip) {
- QVector<QRect> rects = clipData->clipRegion.rects();
- for (int i = 0; i < rects.size(); ++i) {
- QRect intersectRect = rects.at(i).intersected(targetRect.toRect());
+ for (const QRect &rect : clipData->clipRegion) {
+ QRect intersectRect = rect.intersected(targetRect.toRect());
if (!intersectRect.isEmpty()) {
unlock();
if (alpha)
@@ -609,10 +608,8 @@ void QBlitterPaintEngine::fillRect(const QRectF &rect, const QBrush &brush)
}
} else if (clipData->hasRegionClip) {
QRect unclippedTargetRect(x, y, blitWidth, blitHeight);
- const QVector<QRect> intersectedRects = clipData->clipRegion.intersected(unclippedTargetRect).rects();
- const int intersectedSize = intersectedRects.size();
- for (int i = 0; i < intersectedSize; ++i) {
- const QRect &targetRect = intersectedRects.at(i);
+ const QRegion targetRegion = clipData->clipRegion.intersected(unclippedTargetRect);
+ for (const QRect &targetRect : targetRegion) {
if (!targetRect.isValid() || targetRect.isEmpty())
continue;
int tmpSrcX = srcX + (targetRect.x() - x);
@@ -686,9 +683,8 @@ void QBlitterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const Q
if (clipData->hasRectClip) {
d->clipAndDrawPixmap(clipData->clipRect, targetRect, pm, sr, canDrawOpacity);
} else if (clipData->hasRegionClip) {
- QVector<QRect>rects = clipData->clipRegion.rects();
- for (int i = 0; i<rects.size(); ++i)
- d->clipAndDrawPixmap(rects.at(i), targetRect, pm, sr, canDrawOpacity);
+ for (const QRect &rect : clipData->clipRegion)
+ d->clipAndDrawPixmap(rect, targetRect, pm, sr, canDrawOpacity);
}
} else {
QRectF deviceRect(0, 0, paintDevice()->width(), paintDevice()->height());
diff --git a/src/gui/painting/qpaintengine_blitter_p.h b/src/gui/painting/qpaintengine_blitter_p.h
index 3feabcf6fc..40f5347b26 100644
--- a/src/gui/painting/qpaintengine_blitter_p.h
+++ b/src/gui/painting/qpaintengine_blitter_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include "private/qpaintengine_raster_p.h"
#ifndef QT_NO_BLITTABLE
diff --git a/src/gui/painting/qpaintengine_p.h b/src/gui/painting/qpaintengine_p.h
index c14f085721..9d511f9bad 100644
--- a/src/gui/painting/qpaintengine_p.h
+++ b/src/gui/painting/qpaintengine_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include "QtGui/qpainter.h"
#include "QtGui/qpaintengine.h"
#include "QtGui/qregion.h"
@@ -117,6 +118,11 @@ public:
virtual void systemStateChanged() { }
void drawBoxTextItem(const QPointF &p, const QTextItemInt &ti);
+
+ static QPaintEnginePrivate *get(QPaintEngine *paintEngine) { return paintEngine->d_func(); }
+
+ virtual QPaintEngine *aggregateEngine() { return 0; }
+ virtual Qt::HANDLE nativeHandle() { return 0; }
};
QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintengine_raster_p.h b/src/gui/painting/qpaintengine_raster_p.h
index 1afb119535..8cde88fa82 100644
--- a/src/gui/painting/qpaintengine_raster_p.h
+++ b/src/gui/painting/qpaintengine_raster_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include "private/qpaintengineex_p.h"
#include "QtGui/qpainterpath.h"
#include "private/qdatabuffer_p.h"
diff --git a/src/gui/painting/qpaintengineex_p.h b/src/gui/painting/qpaintengineex_p.h
index 643c39c80f..7c2c98140f 100644
--- a/src/gui/painting/qpaintengineex_p.h
+++ b/src/gui/painting/qpaintengineex_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include <QtGui/qpaintengine.h>
#include <private/qpaintengine_p.h>
diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp
index 2c5e0672b1..6472481e7a 100644
--- a/src/gui/painting/qpainter.cpp
+++ b/src/gui/painting/qpainter.cpp
@@ -2834,7 +2834,7 @@ void QPainter::setClipRegion(const QRegion &r, Qt::ClipOperation op)
QRect rect = r.boundingRect();
if (qt_show_painter_debug_output)
printf("QPainter::setClipRegion(), size=%d, [%d,%d,%d,%d]\n",
- r.rects().size(), rect.x(), rect.y(), rect.width(), rect.height());
+ r.rectCount(), rect.x(), rect.y(), rect.width(), rect.height());
#endif
if (!d->engine) {
qWarning("QPainter::setClipRegion: Painter not active");
@@ -6477,6 +6477,8 @@ void QPainterPrivate::drawTextItem(const QPointF &p, const QTextItem &_ti, QText
extended->drawTextItem(QPointF(x, y), ti2);
else
engine->drawTextItem(QPointF(x, y), ti2);
+ drawTextItemDecoration(q, p, ti2.fontEngine, textEngine, ti2.underlineStyle,
+ ti2.flags, ti2.width.toReal(), ti2.charFormat);
if (!rtl)
x += ti2.width.toReal();
@@ -6508,6 +6510,8 @@ void QPainterPrivate::drawTextItem(const QPointF &p, const QTextItem &_ti, QText
extended->drawTextItem(QPointF(x, y), ti2);
else
engine->drawTextItem(QPointF(x,y), ti2);
+ drawTextItemDecoration(q, p, ti2.fontEngine, textEngine, ti2.underlineStyle,
+ ti2.flags, ti2.width.toReal(), ti2.charFormat);
// reset the high byte for all glyphs
const int hi = which << 24;
@@ -6519,9 +6523,9 @@ void QPainterPrivate::drawTextItem(const QPointF &p, const QTextItem &_ti, QText
extended->drawTextItem(p, ti);
else
engine->drawTextItem(p, ti);
+ drawTextItemDecoration(q, p, ti.fontEngine, textEngine, ti.underlineStyle,
+ ti.flags, ti.width.toReal(), ti.charFormat);
}
- drawTextItemDecoration(q, p, ti.fontEngine, textEngine, ti.underlineStyle,
- ti.flags, ti.width.toReal(), ti.charFormat);
if (state->renderHints != oldRenderHints) {
state->renderHints = oldRenderHints;
diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h
index 5743d97405..46817b9c73 100644
--- a/src/gui/painting/qpainter.h
+++ b/src/gui/painting/qpainter.h
@@ -40,6 +40,7 @@
#ifndef QPAINTER_H
#define QPAINTER_H
+#include <QtGui/qtguiglobal.h>
#include <QtCore/qnamespace.h>
#include <QtCore/qrect.h>
#include <QtCore/qpoint.h>
diff --git a/src/gui/painting/qpainter_p.h b/src/gui/painting/qpainter_p.h
index 23ab5db7b4..2d44577310 100644
--- a/src/gui/painting/qpainter_p.h
+++ b/src/gui/painting/qpainter_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include "QtGui/qbrush.h"
#include "QtGui/qfont.h"
#include "QtGui/qpen.h"
diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp
index 5b91266b64..7dbc83b338 100644
--- a/src/gui/painting/qpainterpath.cpp
+++ b/src/gui/painting/qpainterpath.cpp
@@ -1294,10 +1294,9 @@ void QPainterPath::addRegion(const QRegion &region)
ensureData();
detach();
- QVector<QRect> rects = region.rects();
- d_func()->elements.reserve(rects.size() * 5);
- for (int i=0; i<rects.size(); ++i)
- addRect(rects.at(i));
+ d_func()->elements.reserve(region.rectCount() * 5);
+ for (const QRect &rect : region)
+ addRect(rect);
}
diff --git a/src/gui/painting/qpainterpath.h b/src/gui/painting/qpainterpath.h
index 882918fc87..131fcde8cc 100644
--- a/src/gui/painting/qpainterpath.h
+++ b/src/gui/painting/qpainterpath.h
@@ -40,6 +40,7 @@
#ifndef QPAINTERPATH_H
#define QPAINTERPATH_H
+#include <QtGui/qtguiglobal.h>
#include <QtGui/qmatrix.h>
#include <QtCore/qglobal.h>
#include <QtCore/qrect.h>
diff --git a/src/gui/painting/qpainterpath_p.h b/src/gui/painting/qpainterpath_p.h
index e90cdb0f43..92d9a4ea66 100644
--- a/src/gui/painting/qpainterpath_p.h
+++ b/src/gui/painting/qpainterpath_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include "QtGui/qpainterpath.h"
#include "QtGui/qregion.h"
#include "QtCore/qlist.h"
diff --git a/src/gui/painting/qpathclipper.cpp b/src/gui/painting/qpathclipper.cpp
index 4f2b59c775..f92a681eca 100644
--- a/src/gui/painting/qpathclipper.cpp
+++ b/src/gui/painting/qpathclipper.cpp
@@ -1760,7 +1760,7 @@ static bool bool_op(bool a, bool b, QPathClipper::Operation op)
switch (op) {
case QPathClipper::BoolAnd:
return a && b;
- case QPathClipper::BoolOr: // fall-through
+ case QPathClipper::BoolOr:
case QPathClipper::Simplify:
return a || b;
case QPathClipper::BoolSub:
@@ -1956,7 +1956,7 @@ QPointF intersectLine(const QPointF &a, const QPointF &b, qreal t)
{
QLineF line(a, b);
switch (edge) {
- case Left: // fall-through
+ case Left:
case Right:
return line.pointAt((t - a.x()) / (b.x() - a.x()));
default:
diff --git a/src/gui/painting/qpathclipper_p.h b/src/gui/painting/qpathclipper_p.h
index fe3ce84e4a..64e684e1ad 100644
--- a/src/gui/painting/qpathclipper_p.h
+++ b/src/gui/painting/qpathclipper_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include <QtGui/qpainterpath.h>
#include <QtCore/qlist.h>
diff --git a/src/gui/painting/qpathsimplifier_p.h b/src/gui/painting/qpathsimplifier_p.h
index 47b1c3c623..6ef298f6bf 100644
--- a/src/gui/painting/qpathsimplifier_p.h
+++ b/src/gui/painting/qpathsimplifier_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include <QtGui/qpainterpath.h>
#include <QtGui/private/qdatabuffer_p.h>
#include <QtGui/private/qvectorpath_p.h>
diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp
index d568dca6b9..d246c96da6 100644
--- a/src/gui/painting/qpdf.cpp
+++ b/src/gui/painting/qpdf.cpp
@@ -1078,9 +1078,8 @@ void QPdfEngine::updateState(const QPaintEngineState &state)
} else if (flags & DirtyClipRegion) {
d->clipEnabled = true;
QPainterPath path;
- QVector<QRect> rects = state.clipRegion().rects();
- for (int i = 0; i < rects.size(); ++i)
- path.addRect(rects.at(i));
+ for (const QRect &rect : state.clipRegion())
+ path.addRect(rect);
updateClipPath(path, state.clipOperation());
flags |= DirtyClipPath;
} else if (flags & DirtyClipEnabled) {
@@ -2092,7 +2091,7 @@ int QPdfEnginePrivate::generateLinearGradientShader(const QLinearGradient *gradi
break;
case QGradient::ReflectSpread:
reflect = true;
- // fall through
+ Q_FALLTHROUGH();
case QGradient::RepeatSpread: {
// calculate required bounds
QRectF pageRect = m_pageLayout.fullRectPixels(resolution);
@@ -2155,7 +2154,7 @@ int QPdfEnginePrivate::generateRadialGradientShader(const QRadialGradient *gradi
break;
case QGradient::ReflectSpread:
reflect = true;
- // fall through
+ Q_FALLTHROUGH();
case QGradient::RepeatSpread: {
Q_ASSERT(qFuzzyIsNull(r0)); // QPainter emulates if this is not 0
diff --git a/src/gui/painting/qpdf_p.h b/src/gui/painting/qpdf_p.h
index ef41f1efc1..cb1a91e09f 100644
--- a/src/gui/painting/qpdf_p.h
+++ b/src/gui/painting/qpdf_p.h
@@ -51,7 +51,7 @@
// We mean it.
//
-#include <QtCore/qglobal.h>
+#include <QtGui/private/qtguiglobal_p.h>
#ifndef QT_NO_PDF
diff --git a/src/gui/painting/qpdfwriter.h b/src/gui/painting/qpdfwriter.h
index cf1da95bf1..baad274818 100644
--- a/src/gui/painting/qpdfwriter.h
+++ b/src/gui/painting/qpdfwriter.h
@@ -40,7 +40,7 @@
#ifndef QPDFWRITER_H
#define QPDFWRITER_H
-#include <QtCore/qglobal.h>
+#include <QtGui/qtguiglobal.h>
#ifndef QT_NO_PDF
diff --git a/src/gui/painting/qpen.h b/src/gui/painting/qpen.h
index 7469931a48..d8d99ba800 100644
--- a/src/gui/painting/qpen.h
+++ b/src/gui/painting/qpen.h
@@ -40,6 +40,7 @@
#ifndef QPEN_H
#define QPEN_H
+#include <QtGui/qtguiglobal.h>
#include <QtGui/qcolor.h>
#include <QtGui/qbrush.h>
diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp
index 35cf1dae86..b8bbdefa37 100644
--- a/src/gui/painting/qplatformbackingstore.cpp
+++ b/src/gui/painting/qplatformbackingstore.cpp
@@ -49,7 +49,7 @@
#include <QtGui/QOpenGLContext>
#include <QtGui/QOpenGLFunctions>
#ifndef QT_NO_OPENGL
-#include <QtGui/private/qopengltextureblitter_p.h>
+#include <QtGui/qopengltextureblitter.h>
#endif
#include <qpa/qplatformgraphicsbuffer.h>
#include <qpa/qplatformgraphicsbufferhelper.h>
@@ -77,6 +77,7 @@ class QPlatformBackingStorePrivate
public:
QPlatformBackingStorePrivate(QWindow *w)
: window(w)
+ , backingStore(0)
#ifndef QT_NO_OPENGL
, textureId(0)
, blitter(0)
@@ -100,6 +101,7 @@ public:
#endif
}
QWindow *window;
+ QBackingStore *backingStore;
#ifndef QT_NO_OPENGL
mutable GLuint textureId;
mutable QSize textureSize;
@@ -251,9 +253,8 @@ static QRegion deviceRegion(const QRegion &region, QWindow *window, const QPoint
return region;
QVector<QRect> rects;
- const QVector<QRect> regionRects = region.rects();
- rects.reserve(regionRects.count());
- for (const QRect &rect : regionRects)
+ rects.reserve(region.rectCount());
+ for (const QRect &rect : region)
rects.append(deviceRect(rect.translated(offset), window));
QRegion deviceRegion;
@@ -390,7 +391,7 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion &regi
if (textureId) {
if (d_ptr->needsSwizzle)
- d_ptr->blitter->setSwizzleRB(true);
+ d_ptr->blitter->setRedBlueSwizzle(true);
// The backingstore is for the entire tlw.
// In case of native children offset tells the position relative to the tlw.
const QRect srcRect = toBottomLeftRect(deviceWindowRect.translated(offset), d_ptr->textureSize.height());
@@ -399,7 +400,7 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion &regi
origin);
d_ptr->blitter->blit(textureId, QMatrix4x4(), source);
if (d_ptr->needsSwizzle)
- d_ptr->blitter->setSwizzleRB(false);
+ d_ptr->blitter->setRedBlueSwizzle(false);
}
// Textures for renderToTexture widgets that have WA_AlwaysStackOnTop set.
@@ -413,7 +414,7 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion &regi
context->swapBuffers(window);
}
-
+#endif
/*!
Implemented in subclasses to return the content of the backingstore as a QImage.
@@ -426,7 +427,7 @@ QImage QPlatformBackingStore::toImage() const
{
return QImage();
}
-
+#ifndef QT_NO_OPENGL
/*!
May be reimplemented in subclasses to return the content of the
backingstore as an OpenGL texture. \a dirtyRegion is the part of the
@@ -472,14 +473,14 @@ GLuint QPlatformBackingStore::toTexture(const QRegion &dirtyRegion, QSize *textu
switch (image.format()) {
case QImage::Format_ARGB32_Premultiplied:
*flags |= TexturePremultiplied;
- // no break
+ Q_FALLTHROUGH();
case QImage::Format_RGB32:
case QImage::Format_ARGB32:
*flags |= TextureSwizzle;
break;
case QImage::Format_RGBA8888_Premultiplied:
*flags |= TexturePremultiplied;
- // no break
+ Q_FALLTHROUGH();
case QImage::Format_RGBX8888:
case QImage::Format_RGBA8888:
break;
@@ -624,6 +625,23 @@ QWindow* QPlatformBackingStore::window() const
}
/*!
+ Sets the backing store associated with this surface.
+*/
+void QPlatformBackingStore::setBackingStore(QBackingStore *backingStore)
+{
+ d_ptr->backingStore = backingStore;
+}
+
+/*!
+ Returns a pointer to the backing store associated with this
+ surface.
+*/
+QBackingStore *QPlatformBackingStore::backingStore() const
+{
+ return d_ptr->backingStore;
+}
+
+/*!
This function is called before painting onto the surface begins,
with the \a region in which the painting will occur.
diff --git a/src/gui/painting/qplatformbackingstore.h b/src/gui/painting/qplatformbackingstore.h
index 9b09620cce..ec56aaa002 100644
--- a/src/gui/painting/qplatformbackingstore.h
+++ b/src/gui/painting/qplatformbackingstore.h
@@ -49,6 +49,7 @@
// source and binary incompatible with future versions of Qt.
//
+#include <QtGui/qtguiglobal.h>
#include <QtCore/qrect.h>
#include <QtCore/qobject.h>
@@ -111,6 +112,7 @@ public:
virtual ~QPlatformBackingStore();
QWindow *window() const;
+ QBackingStore *backingStore() const;
virtual QPaintDevice *paintDevice() = 0;
@@ -121,7 +123,9 @@ public:
virtual void composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
QPlatformTextureList *textures, QOpenGLContext *context,
bool translucentBackground);
+#endif
virtual QImage toImage() const;
+#ifndef QT_NO_OPENGL
enum TextureFlag {
TextureSwizzle = 0x01,
TextureFlip = 0x02,
@@ -142,6 +146,9 @@ public:
private:
QPlatformBackingStorePrivate *d_ptr;
+
+ void setBackingStore(QBackingStore *);
+ friend class QBackingStore;
};
#ifndef QT_NO_OPENGL
diff --git a/src/gui/painting/qpolygon.cpp b/src/gui/painting/qpolygon.cpp
index d2203595c8..126b752811 100644
--- a/src/gui/painting/qpolygon.cpp
+++ b/src/gui/painting/qpolygon.cpp
@@ -443,14 +443,15 @@ void QPolygon::putPoints(int index, int nPoints, const QPolygon & from, int from
QRect QPolygon::boundingRect() const
{
- if (isEmpty())
- return QRect(0, 0, 0, 0);
const QPoint *pd = constData();
+ const QPoint *pe = pd + size();
+ if (pd == pe)
+ return QRect(0, 0, 0, 0);
int minx, maxx, miny, maxy;
minx = maxx = pd->x();
miny = maxy = pd->y();
++pd;
- for (int i = 1; i < size(); ++i) {
+ for (; pd != pe; ++pd) {
if (pd->x() < minx)
minx = pd->x();
else if (pd->x() > maxx)
@@ -459,7 +460,6 @@ QRect QPolygon::boundingRect() const
miny = pd->y();
else if (pd->y() > maxy)
maxy = pd->y();
- ++pd;
}
return QRect(QPoint(minx,miny), QPoint(maxx,maxy));
}
@@ -657,14 +657,15 @@ QPolygonF QPolygonF::translated(const QPointF &offset) const
QRectF QPolygonF::boundingRect() const
{
- if (isEmpty())
- return QRectF(0, 0, 0, 0);
const QPointF *pd = constData();
+ const QPointF *pe = pd + size();
+ if (pd == pe)
+ return QRectF(0, 0, 0, 0);
qreal minx, maxx, miny, maxy;
minx = maxx = pd->x();
miny = maxy = pd->y();
++pd;
- for (int i = 1; i < size(); ++i) {
+ while (pd != pe) {
if (pd->x() < minx)
minx = pd->x();
else if (pd->x() > maxx)
diff --git a/src/gui/painting/qpolygon.h b/src/gui/painting/qpolygon.h
index 23bdd1933f..c57802e84e 100644
--- a/src/gui/painting/qpolygon.h
+++ b/src/gui/painting/qpolygon.h
@@ -40,6 +40,7 @@
#ifndef QPOLYGON_H
#define QPOLYGON_H
+#include <QtGui/qtguiglobal.h>
#include <QtCore/qvector.h>
#include <QtCore/qpoint.h>
#include <QtCore/qrect.h>
diff --git a/src/gui/painting/qpolygonclipper_p.h b/src/gui/painting/qpolygonclipper_p.h
index c67a692b52..04a31111c9 100644
--- a/src/gui/painting/qpolygonclipper_p.h
+++ b/src/gui/painting/qpolygonclipper_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include "private/qdatabuffer_p.h"
QT_BEGIN_NAMESPACE
diff --git a/src/gui/painting/qrasterizer_p.h b/src/gui/painting/qrasterizer_p.h
index a3e12c076d..955577f4a3 100644
--- a/src/gui/painting/qrasterizer_p.h
+++ b/src/gui/painting/qrasterizer_p.h
@@ -51,7 +51,7 @@
// We mean it.
//
-#include "QtCore/qglobal.h"
+#include <QtGui/private/qtguiglobal_p.h>
#include "QtGui/qpainter.h"
#include <private/qdrawhelper_p.h>
diff --git a/src/gui/painting/qrbtree_p.h b/src/gui/painting/qrbtree_p.h
new file mode 100644
index 0000000000..d3ee23a91c
--- /dev/null
+++ b/src/gui/painting/qrbtree_p.h
@@ -0,0 +1,571 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRBTREE_P_H
+#define QRBTREE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qtguiglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+template <class T>
+struct QRBTree
+{
+ struct Node
+ {
+ inline Node() : parent(0), left(0), right(0), red(true) { }
+ inline ~Node() {if (left) delete left; if (right) delete right;}
+ T data;
+ Node *parent;
+ Node *left;
+ Node *right;
+ bool red;
+ };
+
+ inline QRBTree() : root(0), freeList(0) { }
+ inline ~QRBTree();
+
+ inline void clear();
+
+ void attachBefore(Node *parent, Node *child);
+ void attachAfter(Node *parent, Node *child);
+
+ inline Node *front(Node *node) const;
+ inline Node *back(Node *node) const;
+ Node *next(Node *node) const;
+ Node *previous(Node *node) const;
+
+ inline void deleteNode(Node *&node);
+ inline Node *newNode();
+
+ // Return 1 if 'left' comes after 'right', 0 if equal, and -1 otherwise.
+ // 'left' and 'right' cannot be null.
+ int order(Node *left, Node *right);
+ inline bool validate() const;
+
+private:
+ void rotateLeft(Node *node);
+ void rotateRight(Node *node);
+ void update(Node *node);
+
+ inline void attachLeft(Node *parent, Node *child);
+ inline void attachRight(Node *parent, Node *child);
+
+ int blackDepth(Node *top) const;
+ bool checkRedBlackProperty(Node *top) const;
+
+ void swapNodes(Node *n1, Node *n2);
+ void detach(Node *node);
+
+ // 'node' must be black. rebalance will reduce the depth of black nodes by one in the sibling tree.
+ void rebalance(Node *node);
+
+public:
+ Node *root;
+private:
+ Node *freeList;
+};
+
+template <class T>
+inline QRBTree<T>::~QRBTree()
+{
+ clear();
+ while (freeList) {
+ // Avoid recursively calling the destructor, as this list may become large.
+ Node *next = freeList->right;
+ freeList->right = 0;
+ delete freeList;
+ freeList = next;
+ }
+}
+
+template <class T>
+inline void QRBTree<T>::clear()
+{
+ if (root)
+ delete root;
+ root = 0;
+}
+
+template <class T>
+void QRBTree<T>::rotateLeft(Node *node)
+{
+ // | | //
+ // N B //
+ // / \ / \ //
+ // A B ---> N D //
+ // / \ / \ //
+ // C D A C //
+
+ Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root);
+ ref = node->right;
+ node->right->parent = node->parent;
+
+ // : //
+ // N //
+ // / :| //
+ // A B //
+ // / \ //
+ // C D //
+
+ node->right = ref->left;
+ if (ref->left)
+ ref->left->parent = node;
+
+ // : | //
+ // N B //
+ // / \ : \ //
+ // A C D //
+
+ ref->left = node;
+ node->parent = ref;
+
+ // | //
+ // B //
+ // / \ //
+ // N D //
+ // / \ //
+ // A C //
+}
+
+template <class T>
+void QRBTree<T>::rotateRight(Node *node)
+{
+ // | | //
+ // N A //
+ // / \ / \ //
+ // A B ---> C N //
+ // / \ / \ //
+ // C D D B //
+
+ Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root);
+ ref = node->left;
+ node->left->parent = node->parent;
+
+ node->left = ref->right;
+ if (ref->right)
+ ref->right->parent = node;
+
+ ref->right = node;
+ node->parent = ref;
+}
+
+template <class T>
+void QRBTree<T>::update(Node *node) // call this after inserting a node
+{
+ for (;;) {
+ Node *parent = node->parent;
+
+ // if the node is the root, color it black
+ if (!parent) {
+ node->red = false;
+ return;
+ }
+
+ // if the parent is black, the node can be left red
+ if (!parent->red)
+ return;
+
+ // at this point, the parent is red and cannot be the root
+ Node *grandpa = parent->parent;
+ Q_ASSERT(grandpa);
+
+ Node *uncle = (parent == grandpa->left ? grandpa->right : grandpa->left);
+ if (uncle && uncle->red) {
+ // grandpa's black, parent and uncle are red.
+ // let parent and uncle be black, grandpa red and recursively update grandpa.
+ Q_ASSERT(!grandpa->red);
+ parent->red = false;
+ uncle->red = false;
+ grandpa->red = true;
+ node = grandpa;
+ continue;
+ }
+
+ // at this point, uncle is black
+ if (node == parent->right && parent == grandpa->left)
+ rotateLeft(node = parent);
+ else if (node == parent->left && parent == grandpa->right)
+ rotateRight(node = parent);
+ parent = node->parent;
+
+ if (parent == grandpa->left) {
+ rotateRight(grandpa);
+ parent->red = false;
+ grandpa->red = true;
+ } else {
+ rotateLeft(grandpa);
+ parent->red = false;
+ grandpa->red = true;
+ }
+ return;
+ }
+}
+
+template <class T>
+inline void QRBTree<T>::attachLeft(Node *parent, Node *child)
+{
+ Q_ASSERT(!parent->left);
+ parent->left = child;
+ child->parent = parent;
+ update(child);
+}
+
+template <class T>
+inline void QRBTree<T>::attachRight(Node *parent, Node *child)
+{
+ Q_ASSERT(!parent->right);
+ parent->right = child;
+ child->parent = parent;
+ update(child);
+}
+
+template <class T>
+void QRBTree<T>::attachBefore(Node *parent, Node *child)
+{
+ if (!root)
+ update(root = child);
+ else if (!parent)
+ attachRight(back(root), child);
+ else if (parent->left)
+ attachRight(back(parent->left), child);
+ else
+ attachLeft(parent, child);
+}
+
+template <class T>
+void QRBTree<T>::attachAfter(Node *parent, Node *child)
+{
+ if (!root)
+ update(root = child);
+ else if (!parent)
+ attachLeft(front(root), child);
+ else if (parent->right)
+ attachLeft(front(parent->right), child);
+ else
+ attachRight(parent, child);
+}
+
+template <class T>
+void QRBTree<T>::swapNodes(Node *n1, Node *n2)
+{
+ // Since iterators must not be invalidated, it is not sufficient to only swap the data.
+ if (n1->parent == n2) {
+ n1->parent = n2->parent;
+ n2->parent = n1;
+ } else if (n2->parent == n1) {
+ n2->parent = n1->parent;
+ n1->parent = n2;
+ } else {
+ qSwap(n1->parent, n2->parent);
+ }
+
+ qSwap(n1->left, n2->left);
+ qSwap(n1->right, n2->right);
+ qSwap(n1->red, n2->red);
+
+ if (n1->parent) {
+ if (n1->parent->left == n2)
+ n1->parent->left = n1;
+ else
+ n1->parent->right = n1;
+ } else {
+ root = n1;
+ }
+
+ if (n2->parent) {
+ if (n2->parent->left == n1)
+ n2->parent->left = n2;
+ else
+ n2->parent->right = n2;
+ } else {
+ root = n2;
+ }
+
+ if (n1->left)
+ n1->left->parent = n1;
+ if (n1->right)
+ n1->right->parent = n1;
+
+ if (n2->left)
+ n2->left->parent = n2;
+ if (n2->right)
+ n2->right->parent = n2;
+}
+
+template <class T>
+void QRBTree<T>::detach(Node *node) // call this before removing a node.
+{
+ if (node->right)
+ swapNodes(node, front(node->right));
+
+ Node *child = (node->left ? node->left : node->right);
+
+ if (!node->red) {
+ if (child && child->red)
+ child->red = false;
+ else
+ rebalance(node);
+ }
+
+ Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root);
+ ref = child;
+ if (child)
+ child->parent = node->parent;
+ node->left = node->right = node->parent = 0;
+}
+
+// 'node' must be black. rebalance will reduce the depth of black nodes by one in the sibling tree.
+template <class T>
+void QRBTree<T>::rebalance(Node *node)
+{
+ Q_ASSERT(!node->red);
+ for (;;) {
+ if (!node->parent)
+ return;
+
+ // at this point, node is not a parent, it is black, thus it must have a sibling.
+ Node *sibling = (node == node->parent->left ? node->parent->right : node->parent->left);
+ Q_ASSERT(sibling);
+
+ if (sibling->red) {
+ sibling->red = false;
+ node->parent->red = true;
+ if (node == node->parent->left)
+ rotateLeft(node->parent);
+ else
+ rotateRight(node->parent);
+ sibling = (node == node->parent->left ? node->parent->right : node->parent->left);
+ Q_ASSERT(sibling);
+ }
+
+ // at this point, the sibling is black.
+ Q_ASSERT(!sibling->red);
+
+ if ((!sibling->left || !sibling->left->red) && (!sibling->right || !sibling->right->red)) {
+ bool parentWasRed = node->parent->red;
+ sibling->red = true;
+ node->parent->red = false;
+ if (parentWasRed)
+ return;
+ node = node->parent;
+ continue;
+ }
+
+ // at this point, at least one of the sibling's children is red.
+
+ if (node == node->parent->left) {
+ if (!sibling->right || !sibling->right->red) {
+ Q_ASSERT(sibling->left);
+ sibling->red = true;
+ sibling->left->red = false;
+ rotateRight(sibling);
+
+ sibling = sibling->parent;
+ Q_ASSERT(sibling);
+ }
+ sibling->red = node->parent->red;
+ node->parent->red = false;
+
+ Q_ASSERT(sibling->right->red);
+ sibling->right->red = false;
+ rotateLeft(node->parent);
+ } else {
+ if (!sibling->left || !sibling->left->red) {
+ Q_ASSERT(sibling->right);
+ sibling->red = true;
+ sibling->right->red = false;
+ rotateLeft(sibling);
+
+ sibling = sibling->parent;
+ Q_ASSERT(sibling);
+ }
+ sibling->red = node->parent->red;
+ node->parent->red = false;
+
+ Q_ASSERT(sibling->left->red);
+ sibling->left->red = false;
+ rotateRight(node->parent);
+ }
+ return;
+ }
+}
+
+template <class T>
+inline typename QRBTree<T>::Node *QRBTree<T>::front(Node *node) const
+{
+ while (node->left)
+ node = node->left;
+ return node;
+}
+
+template <class T>
+inline typename QRBTree<T>::Node *QRBTree<T>::back(Node *node) const
+{
+ while (node->right)
+ node = node->right;
+ return node;
+}
+
+template <class T>
+typename QRBTree<T>::Node *QRBTree<T>::next(Node *node) const
+{
+ if (node->right)
+ return front(node->right);
+ while (node->parent && node == node->parent->right)
+ node = node->parent;
+ return node->parent;
+}
+
+template <class T>
+typename QRBTree<T>::Node *QRBTree<T>::previous(Node *node) const
+{
+ if (node->left)
+ return back(node->left);
+ while (node->parent && node == node->parent->left)
+ node = node->parent;
+ return node->parent;
+}
+
+template <class T>
+int QRBTree<T>::blackDepth(Node *top) const
+{
+ if (!top)
+ return 0;
+ int leftDepth = blackDepth(top->left);
+ int rightDepth = blackDepth(top->right);
+ if (leftDepth != rightDepth)
+ return -1;
+ if (!top->red)
+ ++leftDepth;
+ return leftDepth;
+}
+
+template <class T>
+bool QRBTree<T>::checkRedBlackProperty(Node *top) const
+{
+ if (!top)
+ return true;
+ if (top->left && !checkRedBlackProperty(top->left))
+ return false;
+ if (top->right && !checkRedBlackProperty(top->right))
+ return false;
+ return !(top->red && ((top->left && top->left->red) || (top->right && top->right->red)));
+}
+
+template <class T>
+inline bool QRBTree<T>::validate() const
+{
+ return checkRedBlackProperty(root) && blackDepth(root) != -1;
+}
+
+template <class T>
+inline void QRBTree<T>::deleteNode(Node *&node)
+{
+ Q_ASSERT(node);
+ detach(node);
+ node->right = freeList;
+ freeList = node;
+ node = 0;
+}
+
+template <class T>
+inline typename QRBTree<T>::Node *QRBTree<T>::newNode()
+{
+ if (freeList) {
+ Node *node = freeList;
+ freeList = freeList->right;
+ node->parent = node->left = node->right = 0;
+ node->red = true;
+ return node;
+ }
+ return new Node;
+}
+
+// Return 1 if 'left' comes after 'right', 0 if equal, and -1 otherwise.
+// 'left' and 'right' cannot be null.
+template <class T>
+int QRBTree<T>::order(Node *left, Node *right)
+{
+ Q_ASSERT(left && right);
+ if (left == right)
+ return 0;
+
+ QVector<Node *> leftAncestors;
+ QVector<Node *> rightAncestors;
+ while (left) {
+ leftAncestors.push_back(left);
+ left = left->parent;
+ }
+ while (right) {
+ rightAncestors.push_back(right);
+ right = right->parent;
+ }
+ Q_ASSERT(leftAncestors.back() == root && rightAncestors.back() == root);
+
+ while (!leftAncestors.empty() && !rightAncestors.empty() && leftAncestors.back() == rightAncestors.back()) {
+ leftAncestors.pop_back();
+ rightAncestors.pop_back();
+ }
+
+ if (!leftAncestors.empty())
+ return (leftAncestors.back() == leftAncestors.back()->parent->left ? -1 : 1);
+
+ if (!rightAncestors.empty())
+ return (rightAncestors.back() == rightAncestors.back()->parent->right ? -1 : 1);
+
+ // The code should never reach this point.
+ Q_ASSERT(!leftAncestors.empty() || !rightAncestors.empty());
+ return 0;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qregion.cpp b/src/gui/painting/qregion.cpp
index 7571b5c3e8..0571e1a328 100644
--- a/src/gui/painting/qregion.cpp
+++ b/src/gui/painting/qregion.cpp
@@ -81,8 +81,8 @@ QT_BEGIN_NAMESPACE
contains() a QPoint or QRect. The bounding rectangle can be found
with boundingRect().
- The function rects() gives a decomposition of the region into
- rectangles.
+ Iteration over the region (with begin(), end()) gives a decomposition of
+ the region into rectangles. The same sequence of rectangles is returned by rects().
Example of using complex regions:
\snippet code/src_gui_painting_qregion.cpp 0
@@ -395,23 +395,24 @@ void QRegion::exec(const QByteArray &buffer, int ver, QDataStream::ByteOrder byt
QDataStream &operator<<(QDataStream &s, const QRegion &r)
{
- QVector<QRect> a = r.rects();
- if (a.isEmpty()) {
+ auto b = r.begin(), e = r.end();
+ if (b == e) {
s << (quint32)0;
} else {
+ const auto size = e - b;
if (s.version() == 1) {
- int i;
- for (i = a.size() - 1; i > 0; --i) {
+ for (auto i = size - 1; i > 0; --i) {
s << (quint32)(12 + i * 24);
s << (int)QRGN_OR;
}
- for (i = 0; i < a.size(); ++i) {
- s << (quint32)(4+8) << (int)QRGN_SETRECT << a[i];
- }
+ for (auto it = b; it != e; ++it)
+ s << (quint32)(4+8) << (int)QRGN_SETRECT << *it;
} else {
- s << (quint32)(4 + 4 + 16 * a.size()); // 16: storage size of QRect
+ s << quint32(4 + 4 + 16 * size); // 16: storage size of QRect
s << (qint32)QRGN_RECTS;
- s << a;
+ s << quint32(size);
+ for (auto it = b; it != e; ++it)
+ s << *it;
}
}
return s;
@@ -722,12 +723,9 @@ bool QRegion::intersects(const QRegion &region) const
if (rectCount() == 1 && region.rectCount() == 1)
return true;
- const QVector<QRect> myRects = rects();
- const QVector<QRect> otherRects = region.rects();
-
- for (QVector<QRect>::const_iterator i1 = myRects.constBegin(); i1 < myRects.constEnd(); ++i1)
- for (QVector<QRect>::const_iterator i2 = otherRects.constBegin(); i2 < otherRects.constEnd(); ++i2)
- if (rect_intersects(*i1, *i2))
+ for (const QRect &myRect : *this)
+ for (const QRect &otherRect : region)
+ if (rect_intersects(myRect, otherRect))
return true;
return false;
}
@@ -928,6 +926,100 @@ QRegion QRegion::intersect(const QRect &r) const
*/
/*!
+ \typedef QRegion::const_iterator
+ \since 5.8
+
+ An iterator over the QRects that make up the region.
+
+ QRegion does not offer mutable iterators.
+
+ \sa begin(), end()
+*/
+
+/*!
+ \typedef QRegion::const_reverse_iterator
+ \since 5.8
+
+ A reverse iterator over the QRects that make up the region.
+
+ QRegion does not offer mutable iterators.
+
+ \sa rbegin(), rend()
+*/
+
+/*!
+ \fn QRegion::begin() const
+ \since 5.8
+
+ Returns a const_iterator pointing to the beginning of the range of
+ rectangles that make up this range, in the order in which rects()
+ returns them.
+
+ \sa rbegin(), cbegin(), end()
+*/
+
+/*!
+ \fn QRegion::cbegin() const
+ \since 5.8
+
+ Same as begin().
+*/
+
+/*!
+ \fn QRegion::end() const
+ \since 5.8
+
+ Returns a const_iterator pointing to one past the end of the range of
+ rectangles that make up this range, in the order in which rects()
+ returns them.
+
+ \sa rend(), cend(), begin()
+*/
+
+/*!
+ \fn QRegion::cend() const
+ \since 5.8
+
+ Same as end().
+*/
+
+/*!
+ \fn QRegion::rbegin() const
+ \since 5.8
+
+ Returns a const_reverse_iterator pointing to the beginning of the range of
+ rectangles that make up this range, in the reverse order in which rects()
+ returns them.
+
+ \sa begin(), crbegin(), rend()
+*/
+
+/*!
+ \fn QRegion::crbegin() const
+ \since 5.8
+
+ Same as rbegin().
+*/
+
+/*!
+ \fn QRegion::rend() const
+ \since 5.8
+
+ Returns a const_reverse_iterator pointing to one past the end of the range of
+ rectangles that make up this range, in the reverse order in which rects()
+ returns them.
+
+ \sa end(), crend(), rbegin()
+*/
+
+/*!
+ \fn QRegion::crend() const
+ \since 5.8
+
+ Same as rend().
+*/
+
+/*!
\fn void QRegion::setRects(const QRect *rects, int number)
Sets the region using the array of rectangles specified by \a rects and
@@ -1065,13 +1157,11 @@ Q_GUI_EXPORT QPainterPath qt_regionToPath(const QRegion &region)
return result;
}
- const QVector<QRect> rects = region.rects();
+ auto rect = region.begin();
+ const auto end = region.end();
QVarLengthArray<Segment> segments;
- segments.resize(4 * rects.size());
-
- const QRect *rect = rects.constData();
- const QRect *end = rect + rects.size();
+ segments.resize(4 * (end - rect));
int lastRowSegmentCount = 0;
Segment *lastRowSegments = 0;
@@ -1179,6 +1269,12 @@ struct QRegionPrivate {
}
}
+ const QRect *begin() const Q_DECL_NOTHROW
+ { return numRects == 1 ? &extents : rects.data(); } // avoid vectorize()
+
+ const QRect *end() const Q_DECL_NOTHROW
+ { return begin() + numRects; }
+
inline void append(const QRect *r);
void append(const QRegionPrivate *r);
void prepend(const QRect *r);
@@ -4200,7 +4296,7 @@ QRegion QRegion::xored(const QRegion &r) const
}
}
-QRect QRegion::boundingRect() const
+QRect QRegion::boundingRect() const Q_DECL_NOTHROW
{
if (isEmpty())
return QRect();
@@ -4256,6 +4352,16 @@ QVector<QRect> QRegion::rects() const
}
}
+QRegion::const_iterator QRegion::begin() const Q_DECL_NOTHROW
+{
+ return d->qt_rgn ? d->qt_rgn->begin() : nullptr;
+}
+
+QRegion::const_iterator QRegion::end() const Q_DECL_NOTHROW
+{
+ return d->qt_rgn ? d->qt_rgn->end() : nullptr;
+}
+
void QRegion::setRects(const QRect *rects, int num)
{
*this = QRegion();
@@ -4288,7 +4394,7 @@ void QRegion::setRects(const QRect *rects, int num)
}
}
-int QRegion::rectCount() const
+int QRegion::rectCount() const Q_DECL_NOTHROW
{
return (d->qt_rgn ? d->qt_rgn->numRects : 0);
}
@@ -4318,10 +4424,10 @@ bool QRegion::intersects(const QRect &rect) const
if (d->qt_rgn->numRects == 1)
return true;
- const QVector<QRect> myRects = rects();
- for (QVector<QRect>::const_iterator it = myRects.constBegin(); it < myRects.constEnd(); ++it)
- if (rect_intersects(r, *it))
+ for (const QRect &rect : *this) {
+ if (rect_intersects(r, rect))
return true;
+ }
return false;
}
diff --git a/src/gui/painting/qregion.h b/src/gui/painting/qregion.h
index d66f80fcde..4f0a071da8 100644
--- a/src/gui/painting/qregion.h
+++ b/src/gui/painting/qregion.h
@@ -40,6 +40,7 @@
#ifndef QREGION_H
#define QREGION_H
+#include <QtGui/qtguiglobal.h>
#include <QtCore/qatomic.h>
#include <QtCore/qrect.h>
#include <QtGui/qwindowdefs.h>
@@ -81,6 +82,18 @@ public:
bool isEmpty() const;
bool isNull() const;
+ typedef const QRect *const_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ const_iterator begin() const Q_DECL_NOTHROW;
+ const_iterator cbegin() const Q_DECL_NOTHROW { return begin(); }
+ const_iterator end() const Q_DECL_NOTHROW;
+ const_iterator cend() const Q_DECL_NOTHROW { return end(); }
+ const_reverse_iterator rbegin() const Q_DECL_NOTHROW { return const_reverse_iterator(end()); }
+ const_reverse_iterator crbegin() const Q_DECL_NOTHROW { return rbegin(); }
+ const_reverse_iterator rend() const Q_DECL_NOTHROW { return const_reverse_iterator(begin()); }
+ const_reverse_iterator crend() const Q_DECL_NOTHROW { return rend(); }
+
bool contains(const QPoint &p) const;
bool contains(const QRect &r) const;
@@ -108,10 +121,10 @@ public:
bool intersects(const QRegion &r) const;
bool intersects(const QRect &r) const;
- QRect boundingRect() const;
+ QRect boundingRect() const Q_DECL_NOTHROW;
QVector<QRect> rects() const;
void setRects(const QRect *rect, int num);
- int rectCount() const;
+ int rectCount() const Q_DECL_NOTHROW;
#ifdef Q_COMPILER_MANGLES_RETURN_TYPE
// ### Qt 6: remove these, they're kept for MSVC compat
const QRegion operator|(const QRegion &r) const;
@@ -165,6 +178,7 @@ Q_GUI_EXPORT
static const struct QRegionData shared_empty;
static void cleanUp(QRegionData *x);
};
+Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QRegion)
/*****************************************************************************
QRegion stream functions
diff --git a/src/gui/painting/qrgb.h b/src/gui/painting/qrgb.h
index 6e666025ac..fe8306109b 100644
--- a/src/gui/painting/qrgb.h
+++ b/src/gui/painting/qrgb.h
@@ -40,7 +40,7 @@
#ifndef QRGB_H
#define QRGB_H
-#include <QtCore/qglobal.h>
+#include <QtGui/qtguiglobal.h>
#include <QtCore/qprocessordetection.h>
QT_BEGIN_NAMESPACE
diff --git a/src/gui/painting/qrgba64.h b/src/gui/painting/qrgba64.h
index 27e1e6e06d..a3ae0c9b14 100644
--- a/src/gui/painting/qrgba64.h
+++ b/src/gui/painting/qrgba64.h
@@ -40,7 +40,7 @@
#ifndef QRGBA64_H
#define QRGBA64_H
-#include <QtCore/qglobal.h>
+#include <QtGui/qtguiglobal.h>
#include <QtCore/qprocessordetection.h>
QT_BEGIN_NAMESPACE
diff --git a/src/gui/painting/qrgba64_p.h b/src/gui/painting/qrgba64_p.h
index cf3dad5f90..0dadc038fa 100644
--- a/src/gui/painting/qrgba64_p.h
+++ b/src/gui/painting/qrgba64_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include <QtGui/qrgba64.h>
#include <QtGui/private/qdrawhelper_p.h>
#include <private/qsimd_p.h>
@@ -72,27 +73,49 @@ inline QRgba64 multiplyAlpha256(QRgba64 rgba64, uint alpha256)
inline QRgba64 multiplyAlpha65535(QRgba64 rgba64, uint alpha65535)
{
+ return QRgba64::fromRgba64(qt_div_65535(rgba64.red() * alpha65535),
+ qt_div_65535(rgba64.green() * alpha65535),
+ qt_div_65535(rgba64.blue() * alpha65535),
+ qt_div_65535(rgba64.alpha() * alpha65535));
+}
+
#ifdef __SSE2__
- const __m128i va = _mm_shufflelo_epi16(_mm_cvtsi32_si128(alpha65535), _MM_SHUFFLE(0, 0, 0, 0));
- __m128i vs = _mm_loadl_epi64((__m128i*)&rgba64);
+Q_ALWAYS_INLINE __m128i multiplyAlpha65535(__m128i rgba64, __m128i va)
+{
+ __m128i vs = rgba64;
vs = _mm_unpacklo_epi16(_mm_mullo_epi16(vs, va), _mm_mulhi_epu16(vs, va));
vs = _mm_add_epi32(vs, _mm_srli_epi32(vs, 16));
vs = _mm_add_epi32(vs, _mm_set1_epi32(0x8000));
vs = _mm_srai_epi32(vs, 16);
vs = _mm_packs_epi32(vs, _mm_setzero_si128());
- _mm_storel_epi64((__m128i*)&rgba64, vs);
- return rgba64;
-#else
- return QRgba64::fromRgba64(qt_div_65535(rgba64.red() * alpha65535),
- qt_div_65535(rgba64.green() * alpha65535),
- qt_div_65535(rgba64.blue() * alpha65535),
- qt_div_65535(rgba64.alpha() * alpha65535));
+ return vs;
+}
+Q_ALWAYS_INLINE __m128i multiplyAlpha65535(__m128i rgba64, uint alpha65535)
+{
+ const __m128i va = _mm_shufflelo_epi16(_mm_cvtsi32_si128(alpha65535), _MM_SHUFFLE(0, 0, 0, 0));
+ return multiplyAlpha65535(rgba64, va);
+}
#endif
+
+#if defined(__ARM_NEON__)
+Q_ALWAYS_INLINE uint16x4_t multiplyAlpha65535(uint16x4_t rgba64, uint16x4_t alpha65535)
+{
+ uint32x4_t vs32 = vmull_u16(rgba64, alpha65535); // vs = vs * alpha
+ vs32 = vsraq_n_u32(vs32, vs32, 16); // vs = vs + (vs >> 16)
+ return vrshrn_n_u32(vs32, 16); // vs = (vs + 0x8000) >> 16
}
+Q_ALWAYS_INLINE uint16x4_t multiplyAlpha65535(uint16x4_t rgba64, uint alpha65535)
+{
+ uint32x4_t vs32 = vmull_n_u16(rgba64, alpha65535); // vs = vs * alpha
+ vs32 = vsraq_n_u32(vs32, vs32, 16); // vs = vs + (vs >> 16)
+ return vrshrn_n_u32(vs32, 16); // vs = (vs + 0x8000) >> 16
+}
+#endif
-inline QRgba64 multiplyAlpha255(QRgba64 rgba64, uint alpha255)
+template<typename T>
+inline T multiplyAlpha255(T rgba64, uint alpha255)
{
-#ifdef __SSE2__
+#if defined(__SSE2__) || defined(__ARM_NEON__)
return multiplyAlpha65535(rgba64, alpha255 * 257);
#else
return QRgba64::fromRgba64(qt_div_255(rgba64.red() * alpha255),
@@ -112,25 +135,69 @@ inline QRgba64 interpolate255(QRgba64 x, uint alpha1, QRgba64 y, uint alpha2)
return QRgba64::fromRgba64(multiplyAlpha255(x, alpha1) + multiplyAlpha255(y, alpha2));
}
+#if defined __SSE2__
+Q_ALWAYS_INLINE __m128i interpolate255(__m128i x, uint alpha1, __m128i y, uint alpha2)
+{
+ return _mm_add_epi32(multiplyAlpha255(x, alpha1), multiplyAlpha255(y, alpha2));
+}
+#endif
+
+#if defined __ARM_NEON__
+Q_ALWAYS_INLINE uint16x4_t interpolate255(uint16x4_t x, uint alpha1, uint16x4_t y, uint alpha2)
+{
+ return vadd_u16(multiplyAlpha255(x, alpha1), multiplyAlpha255(y, alpha2));
+}
+#endif
+
inline QRgba64 interpolate65535(QRgba64 x, uint alpha1, QRgba64 y, uint alpha2)
{
return QRgba64::fromRgba64(multiplyAlpha65535(x, alpha1) + multiplyAlpha65535(y, alpha2));
}
+#if defined __SSE2__
+Q_ALWAYS_INLINE __m128i interpolate65535(__m128i x, uint alpha1, __m128i y, uint alpha2)
+{
+ return _mm_add_epi32(multiplyAlpha65535(x, alpha1), multiplyAlpha65535(y, alpha2));
+}
+// alpha2 below is const-ref because otherwise MSVC2013 complains that it can't 16-byte align the argument.
+Q_ALWAYS_INLINE __m128i interpolate65535(__m128i x, __m128i alpha1, __m128i y, const __m128i &alpha2)
+{
+ return _mm_add_epi32(multiplyAlpha65535(x, alpha1), multiplyAlpha65535(y, alpha2));
+}
+#endif
+
+#if defined __ARM_NEON__
+Q_ALWAYS_INLINE uint16x4_t interpolate65535(uint16x4_t x, uint alpha1, uint16x4_t y, uint alpha2)
+{
+ return vadd_u16(multiplyAlpha65535(x, alpha1), multiplyAlpha65535(y, alpha2));
+}
+Q_ALWAYS_INLINE uint16x4_t interpolate65535(uint16x4_t x, uint16x4_t alpha1, uint16x4_t y, uint16x4_t alpha2)
+{
+ return vadd_u16(multiplyAlpha65535(x, alpha1), multiplyAlpha65535(y, alpha2));
+}
+#endif
+
inline QRgba64 addWithSaturation(QRgba64 a, QRgba64 b)
{
-#if defined(__SSE2__) && defined(Q_PROCESSOR_X86_64)
- __m128i va = _mm_cvtsi64_si128((quint64)a);
- __m128i vb = _mm_cvtsi64_si128((quint64)b);
- va = _mm_adds_epu16(va, vb);
- return QRgba64::fromRgba64(_mm_cvtsi128_si64(va));
-#else
return QRgba64::fromRgba64(qMin(a.red() + b.red(), 65535),
qMin(a.green() + b.green(), 65535),
qMin(a.blue() + b.blue(), 65535),
qMin(a.alpha() + b.alpha(), 65535));
+}
+
+#if defined(__SSE2__)
+Q_ALWAYS_INLINE __m128i addWithSaturation(__m128i a, __m128i b)
+{
+ return _mm_adds_epu16(a, b);
+}
#endif
+
+#if defined(__ARM_NEON__)
+Q_ALWAYS_INLINE uint16x4_t addWithSaturation(uint16x4_t a, uint16x4_t b)
+{
+ return vqmovn_u32(vaddl_u16(a, b));
}
+#endif
QT_END_NAMESPACE
diff --git a/src/gui/painting/qstroker_p.h b/src/gui/painting/qstroker_p.h
index e17e68b237..ededb5d80b 100644
--- a/src/gui/painting/qstroker_p.h
+++ b/src/gui/painting/qstroker_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include "QtGui/qpainterpath.h"
#include "private/qdatabuffer_p.h"
#include "private/qnumeric_p.h"
diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp
index d2c3eceeef..86a53c21a3 100644
--- a/src/gui/painting/qtextureglyphcache.cpp
+++ b/src/gui/painting/qtextureglyphcache.cpp
@@ -42,7 +42,6 @@
#include "qtextureglyphcache_p.h"
#include "private/qfontengine_p.h"
#include "private/qnumeric_p.h"
-#include "private/qnativeimage_p.h"
QT_BEGIN_NAMESPACE
@@ -247,6 +246,7 @@ void QTextureGlyphCache::fillInPendingGlyphs()
resizeCache(qNextPowerOfTwo(requiredWidth - 1), qNextPowerOfTwo(requiredHeight - 1));
}
+ beginFillTexture();
{
QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = m_pendingGlyphs.begin();
while (iter != m_pendingGlyphs.end()) {
@@ -256,6 +256,7 @@ void QTextureGlyphCache::fillInPendingGlyphs()
++iter;
}
}
+ endFillTexture();
m_pendingGlyphs.clear();
}
diff --git a/src/gui/painting/qtextureglyphcache_p.h b/src/gui/painting/qtextureglyphcache_p.h
index 14271ccc65..25253b496a 100644
--- a/src/gui/painting/qtextureglyphcache_p.h
+++ b/src/gui/painting/qtextureglyphcache_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include <qhash.h>
#include <qimage.h>
#include <qobject.h>
@@ -117,7 +118,9 @@ public:
virtual void resizeTextureData(int width, int height) = 0;
virtual int glyphPadding() const { return 0; }
+ virtual void beginFillTexture() { }
virtual void fillTexture(const Coord &coord, glyph_t glyph, QFixed subPixelPosition) = 0;
+ virtual void endFillTexture() { }
inline void createCache(int width, int height) {
m_w = width;
diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp
index 6058811176..2d841b2953 100644
--- a/src/gui/painting/qtransform.cpp
+++ b/src/gui/painting/qtransform.cpp
@@ -446,7 +446,7 @@ QTransform &QTransform::translate(qreal dx, qreal dy)
break;
case TxProject:
m_33 += dx*m_13 + dy*m_23;
- // Fall through
+ Q_FALLTHROUGH();
case TxShear:
case TxRotate:
affine._dx += dx*affine._m11 + dy*affine._m21;
@@ -508,12 +508,12 @@ QTransform & QTransform::scale(qreal sx, qreal sy)
case TxProject:
m_13 *= sx;
m_23 *= sy;
- // fall through
+ Q_FALLTHROUGH();
case TxRotate:
case TxShear:
affine._m12 *= sx;
affine._m21 *= sy;
- // fall through
+ Q_FALLTHROUGH();
case TxScale:
affine._m11 *= sx;
affine._m22 *= sy;
@@ -581,7 +581,7 @@ QTransform & QTransform::shear(qreal sh, qreal sv)
m_13 += tm13;
m_23 += tm23;
}
- // fall through
+ Q_FALLTHROUGH();
case TxRotate:
case TxShear: {
qreal tm11 = sv*affine._m21;
@@ -663,7 +663,7 @@ QTransform & QTransform::rotate(qreal a, Qt::Axis axis)
qreal tm23 = -sina*m_13 + cosa*m_23;
m_13 = tm13;
m_23 = tm23;
- // fall through
+ Q_FALLTHROUGH();
}
case TxRotate:
case TxShear: {
@@ -742,7 +742,7 @@ QTransform & QTransform::rotateRadians(qreal a, Qt::Axis axis)
qreal tm23 = -sina*m_13 + cosa*m_23;
m_13 = tm13;
m_23 = tm23;
- // fall through
+ Q_FALLTHROUGH();
}
case TxRotate:
case TxShear: {
diff --git a/src/gui/painting/qtransform.h b/src/gui/painting/qtransform.h
index 470a5565e1..2aea19eca0 100644
--- a/src/gui/painting/qtransform.h
+++ b/src/gui/painting/qtransform.h
@@ -39,6 +39,7 @@
#ifndef QTRANSFORM_H
#define QTRANSFORM_H
+#include <QtGui/qtguiglobal.h>
#include <QtGui/qmatrix.h>
#include <QtGui/qpainterpath.h>
#include <QtGui/qpolygon.h>
diff --git a/src/gui/painting/qtriangulatingstroker.cpp b/src/gui/painting/qtriangulatingstroker.cpp
new file mode 100644
index 0000000000..d9a3231165
--- /dev/null
+++ b/src/gui/painting/qtriangulatingstroker.cpp
@@ -0,0 +1,613 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtriangulatingstroker_p.h"
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+#define CURVE_FLATNESS Q_PI / 8
+
+
+
+
+void QTriangulatingStroker::endCapOrJoinClosed(const qreal *start, const qreal *cur,
+ bool implicitClose, bool endsAtStart)
+{
+ if (endsAtStart) {
+ join(start + 2);
+ } else if (implicitClose) {
+ join(start);
+ lineTo(start);
+ join(start+2);
+ } else {
+ endCap(cur);
+ }
+ int count = m_vertices.size();
+
+ // Copy the (x, y) values because QDataBuffer::add(const float& t)
+ // may resize the buffer, which will leave t pointing at the
+ // previous buffer's memory region if we don't copy first.
+ float x = m_vertices.at(count-2);
+ float y = m_vertices.at(count-1);
+ m_vertices.add(x);
+ m_vertices.add(y);
+}
+
+static inline void skipDuplicatePoints(const qreal **pts, const qreal *endPts)
+{
+ while ((*pts + 2) < endPts && float((*pts)[0]) == float((*pts)[2])
+ && float((*pts)[1]) == float((*pts)[3]))
+ {
+ *pts += 2;
+ }
+}
+
+void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen, const QRectF &, QPainter::RenderHints hints)
+{
+ const qreal *pts = path.points();
+ const QPainterPath::ElementType *types = path.elements();
+ int count = path.elementCount();
+ if (count < 2)
+ return;
+
+ float realWidth = qpen_widthf(pen);
+ if (realWidth == 0)
+ realWidth = 1;
+
+ m_width = realWidth / 2;
+
+ bool cosmetic = qt_pen_is_cosmetic(pen, hints);
+ if (cosmetic) {
+ m_width = m_width * m_inv_scale;
+ }
+
+ m_join_style = qpen_joinStyle(pen);
+ m_cap_style = qpen_capStyle(pen);
+ m_vertices.reset();
+ m_miter_limit = pen.miterLimit() * qpen_widthf(pen);
+
+ // The curvyness is based on the notion that I originally wanted
+ // roughly one line segment pr 4 pixels. This may seem little, but
+ // because we sample at constantly incrementing B(t) E [0<t<1], we
+ // will get longer segments where the curvature is small and smaller
+ // segments when the curvature is high.
+ //
+ // To get a rough idea of the length of each curve, I pretend that
+ // the curve is a 90 degree arc, whose radius is
+ // qMax(curveBounds.width, curveBounds.height). Based on this
+ // logic we can estimate the length of the outline edges based on
+ // the radius + a pen width and adjusting for scale factors
+ // depending on if the pen is cosmetic or not.
+ //
+ // The curvyness value of PI/14 was based on,
+ // arcLength = 2*PI*r/4 = PI*r/2 and splitting length into somewhere
+ // between 3 and 8 where 5 seemed to be give pretty good results
+ // hence: Q_PI/14. Lower divisors will give more detail at the
+ // direct cost of performance.
+
+ // simplfy pens that are thin in device size (2px wide or less)
+ if (realWidth < 2.5 && (cosmetic || m_inv_scale == 1)) {
+ if (m_cap_style == Qt::RoundCap)
+ m_cap_style = Qt::SquareCap;
+ if (m_join_style == Qt::RoundJoin)
+ m_join_style = Qt::MiterJoin;
+ m_curvyness_add = 0.5;
+ m_curvyness_mul = CURVE_FLATNESS / m_inv_scale;
+ m_roundness = 1;
+ } else if (cosmetic) {
+ m_curvyness_add = realWidth / 2;
+ m_curvyness_mul = float(CURVE_FLATNESS);
+ m_roundness = qMax<int>(4, realWidth * CURVE_FLATNESS);
+ } else {
+ m_curvyness_add = m_width;
+ m_curvyness_mul = CURVE_FLATNESS / m_inv_scale;
+ m_roundness = qMax<int>(4, realWidth * m_curvyness_mul);
+ }
+
+ // Over this level of segmentation, there doesn't seem to be any
+ // benefit, even for huge penWidth
+ if (m_roundness > 24)
+ m_roundness = 24;
+
+ m_sin_theta = qFastSin(Q_PI / m_roundness);
+ m_cos_theta = qFastCos(Q_PI / m_roundness);
+
+ const qreal *endPts = pts + (count<<1);
+ const qreal *startPts = 0;
+
+ Qt::PenCapStyle cap = m_cap_style;
+
+ if (!types) {
+ skipDuplicatePoints(&pts, endPts);
+ if ((pts + 2) == endPts)
+ return;
+
+ startPts = pts;
+
+ bool endsAtStart = float(startPts[0]) == float(endPts[-2])
+ && float(startPts[1]) == float(endPts[-1]);
+
+ if (endsAtStart || path.hasImplicitClose())
+ m_cap_style = Qt::FlatCap;
+ moveTo(pts);
+ m_cap_style = cap;
+ pts += 2;
+ skipDuplicatePoints(&pts, endPts);
+ lineTo(pts);
+ pts += 2;
+ skipDuplicatePoints(&pts, endPts);
+ while (pts < endPts) {
+ join(pts);
+ lineTo(pts);
+ pts += 2;
+ skipDuplicatePoints(&pts, endPts);
+ }
+ endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);
+
+ } else {
+ bool endsAtStart = false;
+ QPainterPath::ElementType previousType = QPainterPath::MoveToElement;
+ const qreal *previousPts = pts;
+ while (pts < endPts) {
+ switch (*types) {
+ case QPainterPath::MoveToElement: {
+ if (previousType != QPainterPath::MoveToElement)
+ endCapOrJoinClosed(startPts, previousPts, path.hasImplicitClose(), endsAtStart);
+
+ startPts = pts;
+ skipDuplicatePoints(&startPts, endPts); // Skip duplicates to find correct normal.
+ if (startPts + 2 >= endPts)
+ return; // Nothing to see here...
+
+ int end = (endPts - pts) / 2;
+ int i = 2; // Start looking to ahead since we never have two moveto's in a row
+ while (i<end && types[i] != QPainterPath::MoveToElement) {
+ ++i;
+ }
+ endsAtStart = float(startPts[0]) == float(pts[i*2 - 2])
+ && float(startPts[1]) == float(pts[i*2 - 1]);
+ if (endsAtStart || path.hasImplicitClose())
+ m_cap_style = Qt::FlatCap;
+
+ moveTo(startPts);
+ m_cap_style = cap;
+ previousType = QPainterPath::MoveToElement;
+ previousPts = pts;
+ pts+=2;
+ ++types;
+ break; }
+ case QPainterPath::LineToElement:
+ if (float(m_cx) != float(pts[0]) || float(m_cy) != float(pts[1])) {
+ if (previousType != QPainterPath::MoveToElement)
+ join(pts);
+ lineTo(pts);
+ previousType = QPainterPath::LineToElement;
+ previousPts = pts;
+ }
+ pts+=2;
+ ++types;
+ break;
+ case QPainterPath::CurveToElement:
+ if (float(m_cx) != float(pts[0]) || float(m_cy) != float(pts[1])
+ || float(pts[0]) != float(pts[2]) || float(pts[1]) != float(pts[3])
+ || float(pts[2]) != float(pts[4]) || float(pts[3]) != float(pts[5]))
+ {
+ if (float(m_cx) != float(pts[0]) || float(m_cy) != float(pts[1])) {
+ if (previousType != QPainterPath::MoveToElement)
+ join(pts);
+ }
+ cubicTo(pts);
+ previousType = QPainterPath::CurveToElement;
+ previousPts = pts + 4;
+ }
+ pts+=6;
+ types+=3;
+ break;
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+ }
+
+ if (previousType != QPainterPath::MoveToElement)
+ endCapOrJoinClosed(startPts, previousPts, path.hasImplicitClose(), endsAtStart);
+ }
+}
+
+void QTriangulatingStroker::moveTo(const qreal *pts)
+{
+ m_cx = pts[0];
+ m_cy = pts[1];
+
+ float x2 = pts[2];
+ float y2 = pts[3];
+ normalVector(m_cx, m_cy, x2, y2, &m_nvx, &m_nvy);
+
+
+ // To acheive jumps we insert zero-area tringles. This is done by
+ // adding two identical points in both the end of previous strip
+ // and beginning of next strip
+ bool invisibleJump = m_vertices.size();
+
+ switch (m_cap_style) {
+ case Qt::FlatCap:
+ if (invisibleJump) {
+ m_vertices.add(m_cx + m_nvx);
+ m_vertices.add(m_cy + m_nvy);
+ }
+ break;
+ case Qt::SquareCap: {
+ float sx = m_cx - m_nvy;
+ float sy = m_cy + m_nvx;
+ if (invisibleJump) {
+ m_vertices.add(sx + m_nvx);
+ m_vertices.add(sy + m_nvy);
+ }
+ emitLineSegment(sx, sy, m_nvx, m_nvy);
+ break; }
+ case Qt::RoundCap: {
+ QVarLengthArray<float> points;
+ arcPoints(m_cx, m_cy, m_cx + m_nvx, m_cy + m_nvy, m_cx - m_nvx, m_cy - m_nvy, points);
+ m_vertices.resize(m_vertices.size() + points.size() + 2 * int(invisibleJump));
+ int count = m_vertices.size();
+ int front = 0;
+ int end = points.size() / 2;
+ while (front != end) {
+ m_vertices.at(--count) = points[2 * end - 1];
+ m_vertices.at(--count) = points[2 * end - 2];
+ --end;
+ if (front == end)
+ break;
+ m_vertices.at(--count) = points[2 * front + 1];
+ m_vertices.at(--count) = points[2 * front + 0];
+ ++front;
+ }
+
+ if (invisibleJump) {
+ m_vertices.at(count - 1) = m_vertices.at(count + 1);
+ m_vertices.at(count - 2) = m_vertices.at(count + 0);
+ }
+ break; }
+ default: break; // ssssh gcc...
+ }
+ emitLineSegment(m_cx, m_cy, m_nvx, m_nvy);
+}
+
+void QTriangulatingStroker::cubicTo(const qreal *pts)
+{
+ const QPointF *p = (const QPointF *) pts;
+ QBezier bezier = QBezier::fromPoints(*(p - 1), p[0], p[1], p[2]);
+
+ QRectF bounds = bezier.bounds();
+ float rad = qMax(bounds.width(), bounds.height());
+ int threshold = qMin<float>(64, (rad + m_curvyness_add) * m_curvyness_mul);
+ if (threshold < 4)
+ threshold = 4;
+ qreal threshold_minus_1 = threshold - 1;
+ float vx, vy;
+
+ float cx = m_cx, cy = m_cy;
+ float x, y;
+
+ for (int i=1; i<threshold; ++i) {
+ qreal t = qreal(i) / threshold_minus_1;
+ QPointF p = bezier.pointAt(t);
+ x = p.x();
+ y = p.y();
+
+ normalVector(cx, cy, x, y, &vx, &vy);
+
+ emitLineSegment(x, y, vx, vy);
+
+ cx = x;
+ cy = y;
+ }
+
+ m_cx = cx;
+ m_cy = cy;
+
+ m_nvx = vx;
+ m_nvy = vy;
+}
+
+void QTriangulatingStroker::join(const qreal *pts)
+{
+ // Creates a join to the next segment (m_cx, m_cy) -> (pts[0], pts[1])
+ normalVector(m_cx, m_cy, pts[0], pts[1], &m_nvx, &m_nvy);
+
+ switch (m_join_style) {
+ case Qt::BevelJoin:
+ break;
+ case Qt::SvgMiterJoin:
+ case Qt::MiterJoin: {
+ // Find out on which side the join should be.
+ int count = m_vertices.size();
+ float prevNvx = m_vertices.at(count - 2) - m_cx;
+ float prevNvy = m_vertices.at(count - 1) - m_cy;
+ float xprod = prevNvx * m_nvy - prevNvy * m_nvx;
+ float px, py, qx, qy;
+
+ // If the segments are parallel, use bevel join.
+ if (qFuzzyIsNull(xprod))
+ break;
+
+ // Find the corners of the previous and next segment to join.
+ if (xprod < 0) {
+ px = m_vertices.at(count - 2);
+ py = m_vertices.at(count - 1);
+ qx = m_cx - m_nvx;
+ qy = m_cy - m_nvy;
+ } else {
+ px = m_vertices.at(count - 4);
+ py = m_vertices.at(count - 3);
+ qx = m_cx + m_nvx;
+ qy = m_cy + m_nvy;
+ }
+
+ // Find intersection point.
+ float pu = px * prevNvx + py * prevNvy;
+ float qv = qx * m_nvx + qy * m_nvy;
+ float ix = (m_nvy * pu - prevNvy * qv) / xprod;
+ float iy = (prevNvx * qv - m_nvx * pu) / xprod;
+
+ // Check that the distance to the intersection point is less than the miter limit.
+ if ((ix - px) * (ix - px) + (iy - py) * (iy - py) <= m_miter_limit * m_miter_limit) {
+ m_vertices.add(ix);
+ m_vertices.add(iy);
+ m_vertices.add(ix);
+ m_vertices.add(iy);
+ }
+ // else
+ // Do a plain bevel join if the miter limit is exceeded or if
+ // the lines are parallel. This is not what the raster
+ // engine's stroker does, but it is both faster and similar to
+ // what some other graphics API's do.
+
+ break; }
+ case Qt::RoundJoin: {
+ QVarLengthArray<float> points;
+ int count = m_vertices.size();
+ float prevNvx = m_vertices.at(count - 2) - m_cx;
+ float prevNvy = m_vertices.at(count - 1) - m_cy;
+ if (m_nvx * prevNvy - m_nvy * prevNvx < 0) {
+ arcPoints(0, 0, m_nvx, m_nvy, -prevNvx, -prevNvy, points);
+ for (int i = points.size() / 2; i > 0; --i)
+ emitLineSegment(m_cx, m_cy, points[2 * i - 2], points[2 * i - 1]);
+ } else {
+ arcPoints(0, 0, -prevNvx, -prevNvy, m_nvx, m_nvy, points);
+ for (int i = 0; i < points.size() / 2; ++i)
+ emitLineSegment(m_cx, m_cy, points[2 * i + 0], points[2 * i + 1]);
+ }
+ break; }
+ default: break; // gcc warn--
+ }
+
+ emitLineSegment(m_cx, m_cy, m_nvx, m_nvy);
+}
+
+void QTriangulatingStroker::endCap(const qreal *)
+{
+ switch (m_cap_style) {
+ case Qt::FlatCap:
+ break;
+ case Qt::SquareCap:
+ emitLineSegment(m_cx + m_nvy, m_cy - m_nvx, m_nvx, m_nvy);
+ break;
+ case Qt::RoundCap: {
+ QVarLengthArray<float> points;
+ int count = m_vertices.size();
+ arcPoints(m_cx, m_cy, m_vertices.at(count - 2), m_vertices.at(count - 1), m_vertices.at(count - 4), m_vertices.at(count - 3), points);
+ int front = 0;
+ int end = points.size() / 2;
+ while (front != end) {
+ m_vertices.add(points[2 * end - 2]);
+ m_vertices.add(points[2 * end - 1]);
+ --end;
+ if (front == end)
+ break;
+ m_vertices.add(points[2 * front + 0]);
+ m_vertices.add(points[2 * front + 1]);
+ ++front;
+ }
+ break; }
+ default: break; // to shut gcc up...
+ }
+}
+
+void QTriangulatingStroker::arcPoints(float cx, float cy, float fromX, float fromY, float toX, float toY, QVarLengthArray<float> &points)
+{
+ float dx1 = fromX - cx;
+ float dy1 = fromY - cy;
+ float dx2 = toX - cx;
+ float dy2 = toY - cy;
+
+ // while more than 180 degrees left:
+ while (dx1 * dy2 - dx2 * dy1 < 0) {
+ float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta;
+ float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta;
+ dx1 = tmpx;
+ dy1 = tmpy;
+ points.append(cx + dx1);
+ points.append(cy + dy1);
+ }
+
+ // while more than 90 degrees left:
+ while (dx1 * dx2 + dy1 * dy2 < 0) {
+ float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta;
+ float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta;
+ dx1 = tmpx;
+ dy1 = tmpy;
+ points.append(cx + dx1);
+ points.append(cy + dy1);
+ }
+
+ // while more than 0 degrees left:
+ while (dx1 * dy2 - dx2 * dy1 > 0) {
+ float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta;
+ float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta;
+ dx1 = tmpx;
+ dy1 = tmpy;
+ points.append(cx + dx1);
+ points.append(cy + dy1);
+ }
+
+ // remove last point which was rotated beyond [toX, toY].
+ if (!points.isEmpty())
+ points.resize(points.size() - 2);
+}
+
+static void qdashprocessor_moveTo(qreal x, qreal y, void *data)
+{
+ ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::MoveToElement, x, y);
+}
+
+static void qdashprocessor_lineTo(qreal x, qreal y, void *data)
+{
+ ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::LineToElement, x, y);
+}
+
+static void qdashprocessor_cubicTo(qreal, qreal, qreal, qreal, qreal, qreal, void *)
+{
+ Q_ASSERT(0); // The dasher should not produce curves...
+}
+
+QDashedStrokeProcessor::QDashedStrokeProcessor()
+ : m_points(0), m_types(0),
+ m_dash_stroker(0), m_inv_scale(1)
+{
+ m_dash_stroker.setMoveToHook(qdashprocessor_moveTo);
+ m_dash_stroker.setLineToHook(qdashprocessor_lineTo);
+ m_dash_stroker.setCubicToHook(qdashprocessor_cubicTo);
+}
+
+void QDashedStrokeProcessor::process(const QVectorPath &path, const QPen &pen, const QRectF &clip, QPainter::RenderHints hints)
+{
+
+ const qreal *pts = path.points();
+ const QPainterPath::ElementType *types = path.elements();
+ int count = path.elementCount();
+
+ bool cosmetic = qt_pen_is_cosmetic(pen, hints);
+
+ m_points.reset();
+ m_types.reset();
+ m_points.reserve(path.elementCount());
+ m_types.reserve(path.elementCount());
+
+ qreal width = qpen_widthf(pen);
+ if (width == 0)
+ width = 1;
+
+ m_dash_stroker.setDashPattern(pen.dashPattern());
+ m_dash_stroker.setStrokeWidth(cosmetic ? width * m_inv_scale : width);
+ m_dash_stroker.setDashOffset(pen.dashOffset());
+ m_dash_stroker.setMiterLimit(pen.miterLimit());
+ m_dash_stroker.setClipRect(clip);
+
+ float curvynessAdd, curvynessMul;
+
+ // simplify pens that are thin in device size (2px wide or less)
+ if (width < 2.5 && (cosmetic || m_inv_scale == 1)) {
+ curvynessAdd = 0.5;
+ curvynessMul = CURVE_FLATNESS / m_inv_scale;
+ } else if (cosmetic) {
+ curvynessAdd= width / 2;
+ curvynessMul= float(CURVE_FLATNESS);
+ } else {
+ curvynessAdd = width * m_inv_scale;
+ curvynessMul = CURVE_FLATNESS / m_inv_scale;
+ }
+
+ if (count < 2)
+ return;
+
+ const qreal *endPts = pts + (count<<1);
+
+ m_dash_stroker.begin(this);
+
+ if (!types) {
+ m_dash_stroker.moveTo(pts[0], pts[1]);
+ pts += 2;
+ while (pts < endPts) {
+ m_dash_stroker.lineTo(pts[0], pts[1]);
+ pts += 2;
+ }
+ } else {
+ while (pts < endPts) {
+ switch (*types) {
+ case QPainterPath::MoveToElement:
+ m_dash_stroker.moveTo(pts[0], pts[1]);
+ pts += 2;
+ ++types;
+ break;
+ case QPainterPath::LineToElement:
+ m_dash_stroker.lineTo(pts[0], pts[1]);
+ pts += 2;
+ ++types;
+ break;
+ case QPainterPath::CurveToElement: {
+ QBezier b = QBezier::fromPoints(*(((const QPointF *) pts) - 1),
+ *(((const QPointF *) pts)),
+ *(((const QPointF *) pts) + 1),
+ *(((const QPointF *) pts) + 2));
+ QRectF bounds = b.bounds();
+ float rad = qMax(bounds.width(), bounds.height());
+ int threshold = qMin<float>(64, (rad + curvynessAdd) * curvynessMul);
+ if (threshold < 4)
+ threshold = 4;
+
+ qreal threshold_minus_1 = threshold - 1;
+ for (int i=0; i<threshold; ++i) {
+ QPointF pt = b.pointAt(i / threshold_minus_1);
+ m_dash_stroker.lineTo(pt.x(), pt.y());
+ }
+ pts += 6;
+ types += 3;
+ break; }
+ default: break;
+ }
+ }
+ }
+
+ m_dash_stroker.end();
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/gui/painting/qtriangulatingstroker_p.h b/src/gui/painting/qtriangulatingstroker_p.h
new file mode 100644
index 0000000000..2b0f08972b
--- /dev/null
+++ b/src/gui/painting/qtriangulatingstroker_p.h
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTRIANGULATINGSTROKER_P_H
+#define QTRIANGULATINGSTROKER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qtguiglobal_p.h>
+#include <private/qdatabuffer_p.h>
+#include <qvarlengtharray.h>
+#include <private/qvectorpath_p.h>
+#include <private/qbezier_p.h>
+#include <private/qnumeric_p.h>
+#include <private/qmath_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QTriangulatingStroker
+{
+public:
+ QTriangulatingStroker() : m_vertices(0), m_cx(0), m_cy(0), m_nvx(0), m_nvy(0), m_width(1), m_miter_limit(2),
+ m_roundness(0), m_sin_theta(0), m_cos_theta(0), m_inv_scale(1), m_curvyness_mul(1), m_curvyness_add(0),
+ m_join_style(Qt::BevelJoin), m_cap_style(Qt::SquareCap) {}
+
+ void process(const QVectorPath &path, const QPen &pen, const QRectF &clip, QPainter::RenderHints hints);
+
+ inline int vertexCount() const { return m_vertices.size(); }
+ inline const float *vertices() const { return m_vertices.data(); }
+
+ inline void setInvScale(qreal invScale) { m_inv_scale = invScale; }
+
+private:
+ inline void emitLineSegment(float x, float y, float nx, float ny);
+ void moveTo(const qreal *pts);
+ inline void lineTo(const qreal *pts);
+ void cubicTo(const qreal *pts);
+ void join(const qreal *pts);
+ inline void normalVector(float x1, float y1, float x2, float y2, float *nx, float *ny);
+ void endCap(const qreal *pts);
+ void arcPoints(float cx, float cy, float fromX, float fromY, float toX, float toY, QVarLengthArray<float> &points);
+ void endCapOrJoinClosed(const qreal *start, const qreal *cur, bool implicitClose, bool endsAtStart);
+
+
+ QDataBuffer<float> m_vertices;
+
+ float m_cx, m_cy; // current points
+ float m_nvx, m_nvy; // normal vector...
+ float m_width;
+ qreal m_miter_limit;
+
+ int m_roundness; // Number of line segments in a round join
+ qreal m_sin_theta; // sin(m_roundness / 360);
+ qreal m_cos_theta; // cos(m_roundness / 360);
+ qreal m_inv_scale;
+ float m_curvyness_mul;
+ float m_curvyness_add;
+
+ Qt::PenJoinStyle m_join_style;
+ Qt::PenCapStyle m_cap_style;
+};
+
+class Q_GUI_EXPORT QDashedStrokeProcessor
+{
+public:
+ QDashedStrokeProcessor();
+
+ void process(const QVectorPath &path, const QPen &pen, const QRectF &clip, QPainter::RenderHints hints);
+
+ inline void addElement(QPainterPath::ElementType type, qreal x, qreal y) {
+ m_points.add(x);
+ m_points.add(y);
+ m_types.add(type);
+ }
+
+ inline int elementCount() const { return m_types.size(); }
+ inline qreal *points() const { return m_points.data(); }
+ inline QPainterPath::ElementType *elementTypes() const { return m_types.data(); }
+
+ inline void setInvScale(qreal invScale) { m_inv_scale = invScale; }
+
+private:
+ QDataBuffer<qreal> m_points;
+ QDataBuffer<QPainterPath::ElementType> m_types;
+ QDashStroker m_dash_stroker;
+ qreal m_inv_scale;
+};
+
+inline void QTriangulatingStroker::normalVector(float x1, float y1, float x2, float y2,
+ float *nx, float *ny)
+{
+ float dx = x2 - x1;
+ float dy = y2 - y1;
+ Q_ASSERT(dx != 0 || dy != 0);
+
+ float pw;
+
+ if (dx == 0)
+ pw = m_width / std::abs(dy);
+ else if (dy == 0)
+ pw = m_width / std::abs(dx);
+ else
+ pw = m_width / std::sqrt(dx*dx + dy*dy);
+
+ *nx = -dy * pw;
+ *ny = dx * pw;
+}
+
+inline void QTriangulatingStroker::emitLineSegment(float x, float y, float vx, float vy)
+{
+ m_vertices.add(x + vx);
+ m_vertices.add(y + vy);
+ m_vertices.add(x - vx);
+ m_vertices.add(y - vy);
+}
+
+void QTriangulatingStroker::lineTo(const qreal *pts)
+{
+ emitLineSegment(pts[0], pts[1], m_nvx, m_nvy);
+ m_cx = pts[0];
+ m_cy = pts[1];
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qtriangulator.cpp b/src/gui/painting/qtriangulator.cpp
new file mode 100644
index 0000000000..6604d407f0
--- /dev/null
+++ b/src/gui/painting/qtriangulator.cpp
@@ -0,0 +1,2382 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtriangulator_p.h"
+
+#include <QtGui/qevent.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qpainterpath.h>
+#include <QtGui/private/qbezier_p.h>
+#include <QtGui/private/qdatabuffer_p.h>
+#include <QtCore/qbitarray.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qqueue.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qalgorithms.h>
+#ifndef QT_NO_OPENGL
+# include <private/qopenglcontext_p.h>
+# include <private/qopenglextensions_p.h>
+#endif
+#include <private/qrbtree_p.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define Q_TRIANGULATOR_DEBUG
+
+#define Q_FIXED_POINT_SCALE 32
+
+template<typename T>
+struct QVertexSet
+{
+ inline QVertexSet() { }
+ inline QVertexSet(const QVertexSet<T> &other) : vertices(other.vertices), indices(other.indices) { }
+ QVertexSet<T> &operator = (const QVertexSet<T> &other) {vertices = other.vertices; indices = other.indices; return *this;}
+
+ // The vertices of a triangle are given by: (x[i[n]], y[i[n]]), (x[j[n]], y[j[n]]), (x[k[n]], y[k[n]]), n = 0, 1, ...
+ QVector<qreal> vertices; // [x[0], y[0], x[1], y[1], x[2], ...]
+ QVector<T> indices; // [i[0], j[0], k[0], i[1], j[1], k[1], i[2], ...]
+};
+
+//============================================================================//
+// QFraction //
+//============================================================================//
+
+// Fraction must be in the range [0, 1)
+struct QFraction
+{
+ // Comparison operators must not be called on invalid fractions.
+ inline bool operator < (const QFraction &other) const;
+ inline bool operator == (const QFraction &other) const;
+ inline bool operator != (const QFraction &other) const {return !(*this == other);}
+ inline bool operator > (const QFraction &other) const {return other < *this;}
+ inline bool operator >= (const QFraction &other) const {return !(*this < other);}
+ inline bool operator <= (const QFraction &other) const {return !(*this > other);}
+
+ inline bool isValid() const {return denominator != 0;}
+
+ // numerator and denominator must not have common denominators.
+ quint64 numerator, denominator;
+};
+
+static inline quint64 gcd(quint64 x, quint64 y)
+{
+ while (y != 0) {
+ quint64 z = y;
+ y = x % y;
+ x = z;
+ }
+ return x;
+}
+
+static inline int compare(quint64 a, quint64 b)
+{
+ return (a > b) - (a < b);
+}
+
+// Compare a/b with c/d.
+// Return negative if less, 0 if equal, positive if greater.
+// a < b, c < d
+static int qCompareFractions(quint64 a, quint64 b, quint64 c, quint64 d)
+{
+ const quint64 LIMIT = Q_UINT64_C(0x100000000);
+ for (;;) {
+ // If the products 'ad' and 'bc' fit into 64 bits, they can be directly compared.
+ if (b < LIMIT && d < LIMIT)
+ return compare(a * d, b * c);
+
+ if (a == 0 || c == 0)
+ return compare(a, c);
+
+ // a/b < c/d <=> d/c < b/a
+ quint64 b_div_a = b / a;
+ quint64 d_div_c = d / c;
+ if (b_div_a != d_div_c)
+ return compare(d_div_c, b_div_a);
+
+ // floor(d/c) == floor(b/a)
+ // frac(d/c) < frac(b/a) ?
+ // frac(x/y) = (x%y)/y
+ d -= d_div_c * c; //d %= c;
+ b -= b_div_a * a; //b %= a;
+ qSwap(a, d);
+ qSwap(b, c);
+ }
+}
+
+// Fraction must be in the range [0, 1)
+// Assume input is valid.
+static QFraction qFraction(quint64 n, quint64 d) {
+ QFraction result;
+ if (n == 0) {
+ result.numerator = 0;
+ result.denominator = 1;
+ } else {
+ quint64 g = gcd(n, d);
+ result.numerator = n / g;
+ result.denominator = d / g;
+ }
+ return result;
+}
+
+inline bool QFraction::operator < (const QFraction &other) const
+{
+ return qCompareFractions(numerator, denominator, other.numerator, other.denominator) < 0;
+}
+
+inline bool QFraction::operator == (const QFraction &other) const
+{
+ return numerator == other.numerator && denominator == other.denominator;
+}
+
+//============================================================================//
+// QPodPoint //
+//============================================================================//
+
+struct QPodPoint
+{
+ inline bool operator < (const QPodPoint &other) const
+ {
+ if (y != other.y)
+ return y < other.y;
+ return x < other.x;
+ }
+
+ inline bool operator > (const QPodPoint &other) const {return other < *this;}
+ inline bool operator <= (const QPodPoint &other) const {return !(*this > other);}
+ inline bool operator >= (const QPodPoint &other) const {return !(*this < other);}
+ inline bool operator == (const QPodPoint &other) const {return x == other.x && y == other.y;}
+ inline bool operator != (const QPodPoint &other) const {return x != other.x || y != other.y;}
+
+ inline QPodPoint &operator += (const QPodPoint &other) {x += other.x; y += other.y; return *this;}
+ inline QPodPoint &operator -= (const QPodPoint &other) {x -= other.x; y -= other.y; return *this;}
+ inline QPodPoint operator + (const QPodPoint &other) const {QPodPoint result = {x + other.x, y + other.y}; return result;}
+ inline QPodPoint operator - (const QPodPoint &other) const {QPodPoint result = {x - other.x, y - other.y}; return result;}
+
+ int x;
+ int y;
+};
+
+static inline qint64 qCross(const QPodPoint &u, const QPodPoint &v)
+{
+ return qint64(u.x) * qint64(v.y) - qint64(u.y) * qint64(v.x);
+}
+
+#ifdef Q_TRIANGULATOR_DEBUG
+static inline qint64 qDot(const QPodPoint &u, const QPodPoint &v)
+{
+ return qint64(u.x) * qint64(v.x) + qint64(u.y) * qint64(v.y);
+}
+#endif
+
+// Return positive value if 'p' is to the right of the line 'v1'->'v2', negative if left of the
+// line and zero if exactly on the line.
+// The returned value is the z-component of the qCross product between 'v2-v1' and 'p-v1',
+// which is twice the signed area of the triangle 'p'->'v1'->'v2' (positive for CW order).
+static inline qint64 qPointDistanceFromLine(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2)
+{
+ return qCross(v2 - v1, p - v1);
+}
+
+static inline bool qPointIsLeftOfLine(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2)
+{
+ return QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(p, v1, v2) < 0;
+}
+
+//============================================================================//
+// QIntersectionPoint //
+//============================================================================//
+
+struct QIntersectionPoint
+{
+ inline bool isValid() const {return xOffset.isValid() && yOffset.isValid();}
+ QPodPoint round() const;
+ inline bool isAccurate() const {return xOffset.numerator == 0 && yOffset.numerator == 0;}
+ bool operator < (const QIntersectionPoint &other) const;
+ bool operator == (const QIntersectionPoint &other) const;
+ inline bool operator != (const QIntersectionPoint &other) const {return !(*this == other);}
+ inline bool operator > (const QIntersectionPoint &other) const {return other < *this;}
+ inline bool operator >= (const QIntersectionPoint &other) const {return !(*this < other);}
+ inline bool operator <= (const QIntersectionPoint &other) const {return !(*this > other);}
+ bool isOnLine(const QPodPoint &u, const QPodPoint &v) const;
+
+ QPodPoint upperLeft;
+ QFraction xOffset;
+ QFraction yOffset;
+};
+
+static inline QIntersectionPoint qIntersectionPoint(const QPodPoint &point)
+{
+ // upperLeft = point, xOffset = 0/1, yOffset = 0/1.
+ QIntersectionPoint p = {{point.x, point.y}, {0, 1}, {0, 1}};
+ return p;
+}
+
+static QIntersectionPoint qIntersectionPoint(const QPodPoint &u1, const QPodPoint &u2, const QPodPoint &v1, const QPodPoint &v2)
+{
+ QIntersectionPoint result = {{0, 0}, {0, 0}, {0, 0}};
+
+ QPodPoint u = u2 - u1;
+ QPodPoint v = v2 - v1;
+ qint64 d1 = qCross(u, v1 - u1);
+ qint64 d2 = qCross(u, v2 - u1);
+ qint64 det = d2 - d1;
+ qint64 d3 = qCross(v, u1 - v1);
+ qint64 d4 = d3 - det; //qCross(v, u2 - v1);
+
+ // Check that the math is correct.
+ Q_ASSERT(d4 == qCross(v, u2 - v1));
+
+ // The intersection point can be expressed as:
+ // v1 - v * d1/det
+ // v2 - v * d2/det
+ // u1 + u * d3/det
+ // u2 + u * d4/det
+
+ // I'm only interested in lines that are crossing, so ignore parallel lines even if they overlap.
+ if (det == 0)
+ return result;
+
+ if (det < 0) {
+ det = -det;
+ d1 = -d1;
+ d2 = -d2;
+ d3 = -d3;
+ d4 = -d4;
+ }
+
+ // I'm only interested in lines intersecting at their interior, not at their end points.
+ // The lines intersect at their interior if and only if 'd1 < 0', 'd2 > 0', 'd3 < 0' and 'd4 > 0'.
+ if (d1 >= 0 || d2 <= 0 || d3 <= 0 || d4 >= 0)
+ return result;
+
+ // Calculate the intersection point as follows:
+ // v1 - v * d1/det | v1 <= v2 (component-wise)
+ // v2 - v * d2/det | v2 < v1 (component-wise)
+
+ // Assuming 21 bits per vector component.
+ // TODO: Make code path for 31 bits per vector component.
+ if (v.x >= 0) {
+ result.upperLeft.x = v1.x + (-v.x * d1) / det;
+ result.xOffset = qFraction(quint64(-v.x * d1) % quint64(det), quint64(det));
+ } else {
+ result.upperLeft.x = v2.x + (-v.x * d2) / det;
+ result.xOffset = qFraction(quint64(-v.x * d2) % quint64(det), quint64(det));
+ }
+
+ if (v.y >= 0) {
+ result.upperLeft.y = v1.y + (-v.y * d1) / det;
+ result.yOffset = qFraction(quint64(-v.y * d1) % quint64(det), quint64(det));
+ } else {
+ result.upperLeft.y = v2.y + (-v.y * d2) / det;
+ result.yOffset = qFraction(quint64(-v.y * d2) % quint64(det), quint64(det));
+ }
+
+ Q_ASSERT(result.xOffset.isValid());
+ Q_ASSERT(result.yOffset.isValid());
+ return result;
+}
+
+QPodPoint QIntersectionPoint::round() const
+{
+ QPodPoint result = upperLeft;
+ if (2 * xOffset.numerator >= xOffset.denominator)
+ ++result.x;
+ if (2 * yOffset.numerator >= yOffset.denominator)
+ ++result.y;
+ return result;
+}
+
+bool QIntersectionPoint::operator < (const QIntersectionPoint &other) const
+{
+ if (upperLeft.y != other.upperLeft.y)
+ return upperLeft.y < other.upperLeft.y;
+ if (yOffset != other.yOffset)
+ return yOffset < other.yOffset;
+ if (upperLeft.x != other.upperLeft.x)
+ return upperLeft.x < other.upperLeft.x;
+ return xOffset < other.xOffset;
+}
+
+bool QIntersectionPoint::operator == (const QIntersectionPoint &other) const
+{
+ return upperLeft == other.upperLeft && xOffset == other.xOffset && yOffset == other.yOffset;
+}
+
+// Returns \c true if this point is on the infinite line passing through 'u' and 'v'.
+bool QIntersectionPoint::isOnLine(const QPodPoint &u, const QPodPoint &v) const
+{
+ // TODO: Make code path for coordinates with more than 21 bits.
+ const QPodPoint p = upperLeft - u;
+ const QPodPoint q = v - u;
+ bool isHorizontal = p.y == 0 && yOffset.numerator == 0;
+ bool isVertical = p.x == 0 && xOffset.numerator == 0;
+ if (isHorizontal && isVertical)
+ return true;
+ if (isHorizontal)
+ return q.y == 0;
+ if (q.y == 0)
+ return false;
+ if (isVertical)
+ return q.x == 0;
+ if (q.x == 0)
+ return false;
+
+ // At this point, 'p+offset' and 'q' cannot lie on the x or y axis.
+
+ if (((q.x < 0) == (q.y < 0)) != ((p.x < 0) == (p.y < 0)))
+ return false; // 'p + offset' and 'q' pass through different quadrants.
+
+ // Move all coordinates into the first quadrant.
+ quint64 nx, ny;
+ if (p.x < 0)
+ nx = quint64(-p.x) * xOffset.denominator - xOffset.numerator;
+ else
+ nx = quint64(p.x) * xOffset.denominator + xOffset.numerator;
+ if (p.y < 0)
+ ny = quint64(-p.y) * yOffset.denominator - yOffset.numerator;
+ else
+ ny = quint64(p.y) * yOffset.denominator + yOffset.numerator;
+
+ return qFraction(quint64(qAbs(q.x)) * xOffset.denominator, quint64(qAbs(q.y)) * yOffset.denominator) == qFraction(nx, ny);
+}
+
+//============================================================================//
+// QMaxHeap //
+//============================================================================//
+
+template <class T>
+class QMaxHeap
+{
+public:
+ QMaxHeap() : m_data(0) {}
+ inline int size() const {return m_data.size();}
+ inline bool empty() const {return m_data.isEmpty();}
+ inline bool isEmpty() const {return m_data.isEmpty();}
+ void push(const T &x);
+ T pop();
+ inline const T &top() const {return m_data.first();}
+private:
+ static inline int parent(int i) {return (i - 1) / 2;}
+ static inline int left(int i) {return 2 * i + 1;}
+ static inline int right(int i) {return 2 * i + 2;}
+
+ QDataBuffer<T> m_data;
+};
+
+template <class T>
+void QMaxHeap<T>::push(const T &x)
+{
+ int current = m_data.size();
+ int parent = QMaxHeap::parent(current);
+ m_data.add(x);
+ while (current != 0 && m_data.at(parent) < x) {
+ m_data.at(current) = m_data.at(parent);
+ current = parent;
+ parent = QMaxHeap::parent(current);
+ }
+ m_data.at(current) = x;
+}
+
+template <class T>
+T QMaxHeap<T>::pop()
+{
+ T result = m_data.first();
+ T back = m_data.last();
+ m_data.pop_back();
+ if (!m_data.isEmpty()) {
+ int current = 0;
+ for (;;) {
+ int left = QMaxHeap::left(current);
+ int right = QMaxHeap::right(current);
+ if (left >= m_data.size())
+ break;
+ int greater = left;
+ if (right < m_data.size() && m_data.at(left) < m_data.at(right))
+ greater = right;
+ if (m_data.at(greater) < back)
+ break;
+ m_data.at(current) = m_data.at(greater);
+ current = greater;
+ }
+ m_data.at(current) = back;
+ }
+ return result;
+}
+
+//============================================================================//
+// QInt64Hash //
+//============================================================================//
+
+// Copied from qhash.cpp
+static const uchar prime_deltas[] = {
+ 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 17, 27, 3,
+ 1, 29, 3, 21, 7, 17, 15, 9, 43, 35, 15, 0, 0, 0, 0, 0
+};
+
+// Copied from qhash.cpp
+static inline int primeForNumBits(int numBits)
+{
+ return (1 << numBits) + prime_deltas[numBits];
+}
+
+static inline int primeForCount(int count)
+{
+ int low = 0;
+ int high = 32;
+ for (int i = 0; i < 5; ++i) {
+ int mid = (high + low) / 2;
+ if (uint(count) >= (1u << mid))
+ low = mid;
+ else
+ high = mid;
+ }
+ return primeForNumBits(high);
+}
+
+// Hash set of quint64s. Elements cannot be removed without clearing the
+// entire set. A value of -1 is used to mark unused entries.
+class QInt64Set
+{
+public:
+ inline QInt64Set(int capacity = 64);
+ inline ~QInt64Set() {if (m_array) delete[] m_array;}
+ inline bool isValid() const {return m_array;}
+ void insert(quint64 key);
+ bool contains(quint64 key) const;
+ inline void clear();
+private:
+ bool rehash(int capacity);
+
+ static const quint64 UNUSED;
+
+ quint64 *m_array;
+ int m_capacity;
+ int m_count;
+};
+
+const quint64 QInt64Set::UNUSED = quint64(-1);
+
+inline QInt64Set::QInt64Set(int capacity)
+{
+ m_capacity = primeForCount(capacity);
+ m_array = new quint64[m_capacity];
+ if (m_array)
+ clear();
+ else
+ m_capacity = 0;
+}
+
+bool QInt64Set::rehash(int capacity)
+{
+ quint64 *oldArray = m_array;
+ int oldCapacity = m_capacity;
+
+ m_capacity = capacity;
+ m_array = new quint64[m_capacity];
+ if (m_array) {
+ clear();
+ if (oldArray) {
+ for (int i = 0; i < oldCapacity; ++i) {
+ if (oldArray[i] != UNUSED)
+ insert(oldArray[i]);
+ }
+ delete[] oldArray;
+ }
+ return true;
+ } else {
+ m_capacity = oldCapacity;
+ m_array = oldArray;
+ return false;
+ }
+}
+
+void QInt64Set::insert(quint64 key)
+{
+ if (m_count > 3 * m_capacity / 4)
+ rehash(primeForCount(2 * m_capacity));
+ Q_ASSERT_X(m_array, "QInt64Hash<T>::insert", "Hash set not allocated.");
+ int index = int(key % m_capacity);
+ for (int i = 0; i < m_capacity; ++i) {
+ index += i;
+ if (index >= m_capacity)
+ index -= m_capacity;
+ if (m_array[index] == key)
+ return;
+ if (m_array[index] == UNUSED) {
+ ++m_count;
+ m_array[index] = key;
+ return;
+ }
+ }
+ Q_ASSERT_X(0, "QInt64Hash<T>::insert", "Hash set full.");
+}
+
+bool QInt64Set::contains(quint64 key) const
+{
+ Q_ASSERT_X(m_array, "QInt64Hash<T>::contains", "Hash set not allocated.");
+ int index = int(key % m_capacity);
+ for (int i = 0; i < m_capacity; ++i) {
+ index += i;
+ if (index >= m_capacity)
+ index -= m_capacity;
+ if (m_array[index] == key)
+ return true;
+ if (m_array[index] == UNUSED)
+ return false;
+ }
+ return false;
+}
+
+inline void QInt64Set::clear()
+{
+ Q_ASSERT_X(m_array, "QInt64Hash<T>::clear", "Hash set not allocated.");
+ for (int i = 0; i < m_capacity; ++i)
+ m_array[i] = UNUSED;
+ m_count = 0;
+}
+
+//============================================================================//
+// QTriangulator //
+//============================================================================//
+template<typename T>
+class QTriangulator
+{
+public:
+ typedef QVarLengthArray<int, 6> ShortArray;
+
+ //================================//
+ // QTriangulator::ComplexToSimple //
+ //================================//
+ friend class ComplexToSimple;
+ class ComplexToSimple
+ {
+ public:
+ inline ComplexToSimple(QTriangulator<T> *parent) : m_parent(parent),
+ m_edges(0), m_events(0), m_splits(0) { }
+ void decompose();
+ private:
+ struct Edge
+ {
+ inline int &upper() {return pointingUp ? to : from;}
+ inline int &lower() {return pointingUp ? from : to;}
+ inline int upper() const {return pointingUp ? to : from;}
+ inline int lower() const {return pointingUp ? from : to;}
+
+ QRBTree<int>::Node *node;
+ int from, to; // vertex
+ int next, previous; // edge
+ int winding;
+ bool mayIntersect;
+ bool pointingUp, originallyPointingUp;
+ };
+
+ struct Intersection
+ {
+ bool operator < (const Intersection &other) const {return other.intersectionPoint < intersectionPoint;}
+
+ QIntersectionPoint intersectionPoint;
+ int vertex;
+ int leftEdge;
+ int rightEdge;
+ };
+
+ struct Split
+ {
+ int vertex;
+ int edge;
+ bool accurate;
+ };
+
+ struct Event
+ {
+ enum Type {Upper, Lower};
+ inline bool operator < (const Event &other) const;
+
+ QPodPoint point;
+ Type type;
+ int edge;
+ };
+
+#ifdef Q_TRIANGULATOR_DEBUG
+ friend class DebugDialog;
+ friend class QTriangulator;
+ class DebugDialog : public QDialog
+ {
+ public:
+ DebugDialog(ComplexToSimple *parent, int currentVertex);
+ protected:
+ void paintEvent(QPaintEvent *);
+ void wheelEvent(QWheelEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void mousePressEvent(QMouseEvent *);
+ private:
+ ComplexToSimple *m_parent;
+ QRectF m_window;
+ QPoint m_lastMousePos;
+ int m_vertex;
+ };
+#endif
+
+ void initEdges();
+ bool calculateIntersection(int left, int right);
+ bool edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const;
+ QRBTree<int>::Node *searchEdgeLeftOf(int edgeIndex) const;
+ QRBTree<int>::Node *searchEdgeLeftOf(int edgeIndex, QRBTree<int>::Node *after) const;
+ QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> bounds(const QPodPoint &point) const;
+ QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> outerBounds(const QPodPoint &point) const;
+ void splitEdgeListRange(QRBTree<int>::Node *leftmost, QRBTree<int>::Node *rightmost, int vertex, const QIntersectionPoint &intersectionPoint);
+ void reorderEdgeListRange(QRBTree<int>::Node *leftmost, QRBTree<int>::Node *rightmost);
+ void sortEdgeList(const QPodPoint eventPoint);
+ void fillPriorityQueue();
+ void calculateIntersections();
+ int splitEdge(int splitIndex);
+ bool splitEdgesAtIntersections();
+ void insertEdgeIntoVectorIfWanted(ShortArray &orderedEdges, int i);
+ void removeUnwantedEdgesAndConnect();
+ void removeUnusedPoints();
+
+ QTriangulator *m_parent;
+ QDataBuffer<Edge> m_edges;
+ QRBTree<int> m_edgeList;
+ QDataBuffer<Event> m_events;
+ QDataBuffer<Split> m_splits;
+ QMaxHeap<Intersection> m_topIntersection;
+ QInt64Set m_processedEdgePairs;
+ int m_initialPointCount;
+ };
+#ifdef Q_TRIANGULATOR_DEBUG
+ friend class ComplexToSimple::DebugDialog;
+#endif
+
+ //=================================//
+ // QTriangulator::SimpleToMonotone //
+ //=================================//
+ friend class SimpleToMonotone;
+ class SimpleToMonotone
+ {
+ public:
+ inline SimpleToMonotone(QTriangulator<T> *parent) : m_parent(parent), m_edges(0), m_upperVertex(0) { }
+ void decompose();
+ private:
+ enum VertexType {MergeVertex, EndVertex, RegularVertex, StartVertex, SplitVertex};
+
+ struct Edge
+ {
+ QRBTree<int>::Node *node;
+ int helper, twin, next, previous;
+ T from, to;
+ VertexType type;
+ bool pointingUp;
+ int upper() const {return (pointingUp ? to : from);}
+ int lower() const {return (pointingUp ? from : to);}
+ };
+
+ friend class CompareVertices;
+ class CompareVertices
+ {
+ public:
+ CompareVertices(SimpleToMonotone *parent) : m_parent(parent) { }
+ bool operator () (int i, int j) const;
+ private:
+ SimpleToMonotone *m_parent;
+ };
+
+ void setupDataStructures();
+ void removeZeroLengthEdges();
+ void fillPriorityQueue();
+ bool edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const;
+ // Returns the rightmost edge not to the right of the given edge.
+ QRBTree<int>::Node *searchEdgeLeftOfEdge(int edgeIndex) const;
+ // Returns the rightmost edge left of the given point.
+ QRBTree<int>::Node *searchEdgeLeftOfPoint(int pointIndex) const;
+ void classifyVertex(int i);
+ void classifyVertices();
+ bool pointIsInSector(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2, const QPodPoint &v3);
+ bool pointIsInSector(int vertex, int sector);
+ int findSector(int edge, int vertex);
+ void createDiagonal(int lower, int upper);
+ void monotoneDecomposition();
+
+ QTriangulator *m_parent;
+ QRBTree<int> m_edgeList;
+ QDataBuffer<Edge> m_edges;
+ QDataBuffer<int> m_upperVertex;
+ bool m_clockwiseOrder;
+ };
+
+ //====================================//
+ // QTriangulator::MonotoneToTriangles //
+ //====================================//
+ friend class MonotoneToTriangles;
+ class MonotoneToTriangles
+ {
+ public:
+ inline MonotoneToTriangles(QTriangulator<T> *parent) : m_parent(parent) { }
+ void decompose();
+ private:
+ inline T indices(int index) const {return m_parent->m_indices.at(index + m_first);}
+ inline int next(int index) const {return (index + 1) % m_length;}
+ inline int previous(int index) const {return (index + m_length - 1) % m_length;}
+ inline bool less(int i, int j) const {return m_parent->m_vertices.at((qint32)indices(i)) < m_parent->m_vertices.at(indices(j));}
+ inline bool leftOfEdge(int i, int j, int k) const
+ {
+ return qPointIsLeftOfLine(m_parent->m_vertices.at((qint32)indices(i)),
+ m_parent->m_vertices.at((qint32)indices(j)), m_parent->m_vertices.at((qint32)indices(k)));
+ }
+
+ QTriangulator<T> *m_parent;
+ int m_first;
+ int m_length;
+ };
+
+ inline QTriangulator() : m_vertices(0) { }
+
+ // Call this only once.
+ void initialize(const qreal *polygon, int count, uint hint, const QTransform &matrix);
+ // Call this only once.
+ void initialize(const QVectorPath &path, const QTransform &matrix, qreal lod);
+ // Call this only once.
+ void initialize(const QPainterPath &path, const QTransform &matrix, qreal lod);
+ // Call either triangulate() or polyline() only once.
+ QVertexSet<T> triangulate();
+ QVertexSet<T> polyline();
+private:
+ QDataBuffer<QPodPoint> m_vertices;
+ QVector<T> m_indices;
+ uint m_hint;
+};
+
+//============================================================================//
+// QTriangulator //
+//============================================================================//
+
+template <typename T>
+QVertexSet<T> QTriangulator<T>::triangulate()
+{
+ for (int i = 0; i < m_vertices.size(); ++i) {
+ Q_ASSERT(qAbs(m_vertices.at(i).x) < (1 << 21));
+ Q_ASSERT(qAbs(m_vertices.at(i).y) < (1 << 21));
+ }
+
+ if (!(m_hint & (QVectorPath::OddEvenFill | QVectorPath::WindingFill)))
+ m_hint |= QVectorPath::OddEvenFill;
+
+ if (m_hint & QVectorPath::NonConvexShapeMask) {
+ ComplexToSimple c2s(this);
+ c2s.decompose();
+ SimpleToMonotone s2m(this);
+ s2m.decompose();
+ }
+ MonotoneToTriangles m2t(this);
+ m2t.decompose();
+
+ QVertexSet<T> result;
+ result.indices = m_indices;
+ result.vertices.resize(2 * m_vertices.size());
+ for (int i = 0; i < m_vertices.size(); ++i) {
+ result.vertices[2 * i + 0] = qreal(m_vertices.at(i).x) / Q_FIXED_POINT_SCALE;
+ result.vertices[2 * i + 1] = qreal(m_vertices.at(i).y) / Q_FIXED_POINT_SCALE;
+ }
+ return result;
+}
+
+template <typename T>
+QVertexSet<T> QTriangulator<T>::polyline()
+{
+ for (int i = 0; i < m_vertices.size(); ++i) {
+ Q_ASSERT(qAbs(m_vertices.at(i).x) < (1 << 21));
+ Q_ASSERT(qAbs(m_vertices.at(i).y) < (1 << 21));
+ }
+
+ if (!(m_hint & (QVectorPath::OddEvenFill | QVectorPath::WindingFill)))
+ m_hint |= QVectorPath::OddEvenFill;
+
+ if (m_hint & QVectorPath::NonConvexShapeMask) {
+ ComplexToSimple c2s(this);
+ c2s.decompose();
+ }
+
+ QVertexSet<T> result;
+ result.indices = m_indices;
+ result.vertices.resize(2 * m_vertices.size());
+ for (int i = 0; i < m_vertices.size(); ++i) {
+ result.vertices[2 * i + 0] = qreal(m_vertices.at(i).x) / Q_FIXED_POINT_SCALE;
+ result.vertices[2 * i + 1] = qreal(m_vertices.at(i).y) / Q_FIXED_POINT_SCALE;
+ }
+ return result;
+}
+
+template <typename T>
+void QTriangulator<T>::initialize(const qreal *polygon, int count, uint hint, const QTransform &matrix)
+{
+ m_hint = hint;
+ m_vertices.resize(count);
+ m_indices.resize(count + 1);
+ for (int i = 0; i < count; ++i) {
+ qreal x, y;
+ matrix.map(polygon[2 * i + 0], polygon[2 * i + 1], &x, &y);
+ m_vertices.at(i).x = qRound(x * Q_FIXED_POINT_SCALE);
+ m_vertices.at(i).y = qRound(y * Q_FIXED_POINT_SCALE);
+ m_indices[i] = i;
+ }
+ m_indices[count] = T(-1); //Q_TRIANGULATE_END_OF_POLYGON
+}
+
+template <typename T>
+void QTriangulator<T>::initialize(const QVectorPath &path, const QTransform &matrix, qreal lod)
+{
+ m_hint = path.hints();
+ // Curved paths will be converted to complex polygons.
+ m_hint &= ~QVectorPath::CurvedShapeMask;
+
+ const qreal *p = path.points();
+ const QPainterPath::ElementType *e = path.elements();
+ if (e) {
+ for (int i = 0; i < path.elementCount(); ++i, ++e, p += 2) {
+ switch (*e) {
+ case QPainterPath::MoveToElement:
+ if (!m_indices.isEmpty())
+ m_indices.push_back(T(-1)); // Q_TRIANGULATE_END_OF_POLYGON
+ Q_FALLTHROUGH();
+ case QPainterPath::LineToElement:
+ m_indices.push_back(T(m_vertices.size()));
+ m_vertices.resize(m_vertices.size() + 1);
+ qreal x, y;
+ matrix.map(p[0], p[1], &x, &y);
+ m_vertices.last().x = qRound(x * Q_FIXED_POINT_SCALE);
+ m_vertices.last().y = qRound(y * Q_FIXED_POINT_SCALE);
+ break;
+ case QPainterPath::CurveToElement:
+ {
+ qreal pts[8];
+ for (int i = 0; i < 4; ++i)
+ matrix.map(p[2 * i - 2], p[2 * i - 1], &pts[2 * i + 0], &pts[2 * i + 1]);
+ for (int i = 0; i < 8; ++i)
+ pts[i] *= lod;
+ QBezier bezier = QBezier::fromPoints(QPointF(pts[0], pts[1]), QPointF(pts[2], pts[3]), QPointF(pts[4], pts[5]), QPointF(pts[6], pts[7]));
+ QPolygonF poly = bezier.toPolygon();
+ // Skip first point, it already exists in 'm_vertices'.
+ for (int j = 1; j < poly.size(); ++j) {
+ m_indices.push_back(T(m_vertices.size()));
+ m_vertices.resize(m_vertices.size() + 1);
+ m_vertices.last().x = qRound(poly.at(j).x() * Q_FIXED_POINT_SCALE / lod);
+ m_vertices.last().y = qRound(poly.at(j).y() * Q_FIXED_POINT_SCALE / lod);
+ }
+ }
+ i += 2;
+ e += 2;
+ p += 4;
+ break;
+ default:
+ Q_ASSERT_X(0, "QTriangulator::triangulate", "Unexpected element type.");
+ break;
+ }
+ }
+ } else {
+ for (int i = 0; i < path.elementCount(); ++i, p += 2) {
+ m_indices.push_back(T(m_vertices.size()));
+ m_vertices.resize(m_vertices.size() + 1);
+ qreal x, y;
+ matrix.map(p[0], p[1], &x, &y);
+ m_vertices.last().x = qRound(x * Q_FIXED_POINT_SCALE);
+ m_vertices.last().y = qRound(y * Q_FIXED_POINT_SCALE);
+ }
+ }
+ m_indices.push_back(T(-1)); // Q_TRIANGULATE_END_OF_POLYGON
+}
+
+template <typename T>
+void QTriangulator<T>::initialize(const QPainterPath &path, const QTransform &matrix, qreal lod)
+{
+ initialize(qtVectorPathForPath(path), matrix, lod);
+}
+
+//============================================================================//
+// QTriangulator::ComplexToSimple //
+//============================================================================//
+template <typename T>
+void QTriangulator<T>::ComplexToSimple::decompose()
+{
+ m_initialPointCount = m_parent->m_vertices.size();
+ initEdges();
+ do {
+ calculateIntersections();
+ } while (splitEdgesAtIntersections());
+
+ removeUnwantedEdgesAndConnect();
+ removeUnusedPoints();
+
+ m_parent->m_indices.clear();
+ QBitArray processed(m_edges.size(), false);
+ for (int first = 0; first < m_edges.size(); ++first) {
+ // If already processed, or if unused path, skip.
+ if (processed.at(first) || m_edges.at(first).next == -1)
+ continue;
+
+ int i = first;
+ do {
+ Q_ASSERT(!processed.at(i));
+ Q_ASSERT(m_edges.at(m_edges.at(i).next).previous == i);
+ m_parent->m_indices.push_back(m_edges.at(i).from);
+ processed.setBit(i);
+ i = m_edges.at(i).next; // CCW order
+ } while (i != first);
+ m_parent->m_indices.push_back(T(-1)); // Q_TRIANGULATE_END_OF_POLYGON
+ }
+}
+
+template <typename T>
+void QTriangulator<T>::ComplexToSimple::initEdges()
+{
+ // Initialize edge structure.
+ // 'next' and 'previous' are not being initialized at this point.
+ int first = 0;
+ for (int i = 0; i < m_parent->m_indices.size(); ++i) {
+ if (m_parent->m_indices.at(i) == T(-1)) { // Q_TRIANGULATE_END_OF_POLYGON
+ if (m_edges.size() != first)
+ m_edges.last().to = m_edges.at(first).from;
+ first = m_edges.size();
+ } else {
+ Q_ASSERT(i + 1 < m_parent->m_indices.size());
+ // {node, from, to, next, previous, winding, mayIntersect, pointingUp, originallyPointingUp}
+ Edge edge = {0, int(m_parent->m_indices.at(i)), int(m_parent->m_indices.at(i + 1)), -1, -1, 0, true, false, false};
+ m_edges.add(edge);
+ }
+ }
+ if (first != m_edges.size())
+ m_edges.last().to = m_edges.at(first).from;
+ for (int i = 0; i < m_edges.size(); ++i) {
+ m_edges.at(i).originallyPointingUp = m_edges.at(i).pointingUp =
+ m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from);
+ }
+}
+
+// Return true if new intersection was found
+template <typename T>
+bool QTriangulator<T>::ComplexToSimple::calculateIntersection(int left, int right)
+{
+ const Edge &e1 = m_edges.at(left);
+ const Edge &e2 = m_edges.at(right);
+
+ const QPodPoint &u1 = m_parent->m_vertices.at((qint32)e1.from);
+ const QPodPoint &u2 = m_parent->m_vertices.at((qint32)e1.to);
+ const QPodPoint &v1 = m_parent->m_vertices.at((qint32)e2.from);
+ const QPodPoint &v2 = m_parent->m_vertices.at((qint32)e2.to);
+ if (qMax(u1.x, u2.x) <= qMin(v1.x, v2.x))
+ return false;
+
+ quint64 key = (left > right ? (quint64(right) << 32) | quint64(left) : (quint64(left) << 32) | quint64(right));
+ if (m_processedEdgePairs.contains(key))
+ return false;
+ m_processedEdgePairs.insert(key);
+
+ Intersection intersection;
+ intersection.leftEdge = left;
+ intersection.rightEdge = right;
+ intersection.intersectionPoint = QT_PREPEND_NAMESPACE(qIntersectionPoint)(u1, u2, v1, v2);
+
+ if (!intersection.intersectionPoint.isValid())
+ return false;
+
+ Q_ASSERT(intersection.intersectionPoint.isOnLine(u1, u2));
+ Q_ASSERT(intersection.intersectionPoint.isOnLine(v1, v2));
+
+ intersection.vertex = m_parent->m_vertices.size();
+ m_topIntersection.push(intersection);
+ m_parent->m_vertices.add(intersection.intersectionPoint.round());
+ return true;
+}
+
+template <typename T>
+bool QTriangulator<T>::ComplexToSimple::edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const
+{
+ const Edge &leftEdge = m_edges.at(leftEdgeIndex);
+ const Edge &rightEdge = m_edges.at(rightEdgeIndex);
+ const QPodPoint &u = m_parent->m_vertices.at(rightEdge.upper());
+ const QPodPoint &l = m_parent->m_vertices.at(rightEdge.lower());
+ const QPodPoint &upper = m_parent->m_vertices.at(leftEdge.upper());
+ if (upper.x < qMin(l.x, u.x))
+ return true;
+ if (upper.x > qMax(l.x, u.x))
+ return false;
+ qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(upper, l, u);
+ // d < 0: left, d > 0: right, d == 0: on top
+ if (d == 0)
+ d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(m_parent->m_vertices.at(leftEdge.lower()), l, u);
+ return d < 0;
+}
+
+template <typename T>
+QRBTree<int>::Node *QTriangulator<T>::ComplexToSimple::searchEdgeLeftOf(int edgeIndex) const
+{
+ QRBTree<int>::Node *current = m_edgeList.root;
+ QRBTree<int>::Node *result = 0;
+ while (current) {
+ if (edgeIsLeftOfEdge(edgeIndex, current->data)) {
+ current = current->left;
+ } else {
+ result = current;
+ current = current->right;
+ }
+ }
+ return result;
+}
+
+template <typename T>
+QRBTree<int>::Node *QTriangulator<T>::ComplexToSimple::searchEdgeLeftOf(int edgeIndex, QRBTree<int>::Node *after) const
+{
+ if (!m_edgeList.root)
+ return after;
+ QRBTree<int>::Node *result = after;
+ QRBTree<int>::Node *current = (after ? m_edgeList.next(after) : m_edgeList.front(m_edgeList.root));
+ while (current) {
+ if (edgeIsLeftOfEdge(edgeIndex, current->data))
+ return result;
+ result = current;
+ current = m_edgeList.next(current);
+ }
+ return result;
+}
+
+template <typename T>
+QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> QTriangulator<T>::ComplexToSimple::bounds(const QPodPoint &point) const
+{
+ QRBTree<int>::Node *current = m_edgeList.root;
+ QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> result(0, 0);
+ while (current) {
+ const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower());
+ const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper());
+ qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2);
+ if (d == 0) {
+ result.first = result.second = current;
+ break;
+ }
+ current = (d < 0 ? current->left : current->right);
+ }
+ if (current == 0)
+ return result;
+
+ current = result.first->left;
+ while (current) {
+ const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower());
+ const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper());
+ qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2);
+ Q_ASSERT(d >= 0);
+ if (d == 0) {
+ result.first = current;
+ current = current->left;
+ } else {
+ current = current->right;
+ }
+ }
+
+ current = result.second->right;
+ while (current) {
+ const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower());
+ const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper());
+ qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2);
+ Q_ASSERT(d <= 0);
+ if (d == 0) {
+ result.second = current;
+ current = current->right;
+ } else {
+ current = current->left;
+ }
+ }
+
+ return result;
+}
+
+template <typename T>
+QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> QTriangulator<T>::ComplexToSimple::outerBounds(const QPodPoint &point) const
+{
+ QRBTree<int>::Node *current = m_edgeList.root;
+ QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> result(0, 0);
+
+ while (current) {
+ const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower());
+ const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper());
+ qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2);
+ if (d == 0)
+ break;
+ if (d < 0) {
+ result.second = current;
+ current = current->left;
+ } else {
+ result.first = current;
+ current = current->right;
+ }
+ }
+
+ if (!current)
+ return result;
+
+ QRBTree<int>::Node *mid = current;
+
+ current = mid->left;
+ while (current) {
+ const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower());
+ const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper());
+ qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2);
+ Q_ASSERT(d >= 0);
+ if (d == 0) {
+ current = current->left;
+ } else {
+ result.first = current;
+ current = current->right;
+ }
+ }
+
+ current = mid->right;
+ while (current) {
+ const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower());
+ const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper());
+ qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(point, v1, v2);
+ Q_ASSERT(d <= 0);
+ if (d == 0) {
+ current = current->right;
+ } else {
+ result.second = current;
+ current = current->left;
+ }
+ }
+
+ return result;
+}
+
+template <typename T>
+void QTriangulator<T>::ComplexToSimple::splitEdgeListRange(QRBTree<int>::Node *leftmost, QRBTree<int>::Node *rightmost, int vertex, const QIntersectionPoint &intersectionPoint)
+{
+ Q_ASSERT(leftmost && rightmost);
+
+ // Split.
+ for (;;) {
+ const QPodPoint &u = m_parent->m_vertices.at(m_edges.at(leftmost->data).from);
+ const QPodPoint &v = m_parent->m_vertices.at(m_edges.at(leftmost->data).to);
+ Q_ASSERT(intersectionPoint.isOnLine(u, v));
+ const Split split = {vertex, leftmost->data, intersectionPoint.isAccurate()};
+ if (intersectionPoint.xOffset.numerator != 0 || intersectionPoint.yOffset.numerator != 0 || (intersectionPoint.upperLeft != u && intersectionPoint.upperLeft != v))
+ m_splits.add(split);
+ if (leftmost == rightmost)
+ break;
+ leftmost = m_edgeList.next(leftmost);
+ }
+}
+
+template <typename T>
+void QTriangulator<T>::ComplexToSimple::reorderEdgeListRange(QRBTree<int>::Node *leftmost, QRBTree<int>::Node *rightmost)
+{
+ Q_ASSERT(leftmost && rightmost);
+
+ QRBTree<int>::Node *storeLeftmost = leftmost;
+ QRBTree<int>::Node *storeRightmost = rightmost;
+
+ // Reorder.
+ while (leftmost != rightmost) {
+ Edge &left = m_edges.at(leftmost->data);
+ Edge &right = m_edges.at(rightmost->data);
+ qSwap(left.node, right.node);
+ qSwap(leftmost->data, rightmost->data);
+ leftmost = m_edgeList.next(leftmost);
+ if (leftmost == rightmost)
+ break;
+ rightmost = m_edgeList.previous(rightmost);
+ }
+
+ rightmost = m_edgeList.next(storeRightmost);
+ leftmost = m_edgeList.previous(storeLeftmost);
+ if (leftmost)
+ calculateIntersection(leftmost->data, storeLeftmost->data);
+ if (rightmost)
+ calculateIntersection(storeRightmost->data, rightmost->data);
+}
+
+template <typename T>
+void QTriangulator<T>::ComplexToSimple::sortEdgeList(const QPodPoint eventPoint)
+{
+ QIntersectionPoint eventPoint2 = QT_PREPEND_NAMESPACE(qIntersectionPoint)(eventPoint);
+ while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint < eventPoint2) {
+ Intersection intersection = m_topIntersection.pop();
+
+ QIntersectionPoint currentIntersectionPoint = intersection.intersectionPoint;
+ int currentVertex = intersection.vertex;
+
+ QRBTree<int>::Node *leftmost = m_edges.at(intersection.leftEdge).node;
+ QRBTree<int>::Node *rightmost = m_edges.at(intersection.rightEdge).node;
+
+ for (;;) {
+ QRBTree<int>::Node *previous = m_edgeList.previous(leftmost);
+ if (!previous)
+ break;
+ const Edge &edge = m_edges.at(previous->data);
+ const QPodPoint &u = m_parent->m_vertices.at((qint32)edge.from);
+ const QPodPoint &v = m_parent->m_vertices.at((qint32)edge.to);
+ if (!currentIntersectionPoint.isOnLine(u, v)) {
+ Q_ASSERT(!currentIntersectionPoint.isAccurate() || qCross(currentIntersectionPoint.upperLeft - u, v - u) != 0);
+ break;
+ }
+ leftmost = previous;
+ }
+
+ for (;;) {
+ QRBTree<int>::Node *next = m_edgeList.next(rightmost);
+ if (!next)
+ break;
+ const Edge &edge = m_edges.at(next->data);
+ const QPodPoint &u = m_parent->m_vertices.at((qint32)edge.from);
+ const QPodPoint &v = m_parent->m_vertices.at((qint32)edge.to);
+ if (!currentIntersectionPoint.isOnLine(u, v)) {
+ Q_ASSERT(!currentIntersectionPoint.isAccurate() || qCross(currentIntersectionPoint.upperLeft - u, v - u) != 0);
+ break;
+ }
+ rightmost = next;
+ }
+
+ Q_ASSERT(leftmost && rightmost);
+ splitEdgeListRange(leftmost, rightmost, currentVertex, currentIntersectionPoint);
+ reorderEdgeListRange(leftmost, rightmost);
+
+ while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint <= currentIntersectionPoint)
+ m_topIntersection.pop();
+
+#ifdef Q_TRIANGULATOR_DEBUG
+ DebugDialog dialog(this, intersection.vertex);
+ dialog.exec();
+#endif
+
+ }
+}
+
+template <typename T>
+void QTriangulator<T>::ComplexToSimple::fillPriorityQueue()
+{
+ m_events.reset();
+ m_events.reserve(m_edges.size() * 2);
+ for (int i = 0; i < m_edges.size(); ++i) {
+ Q_ASSERT(m_edges.at(i).previous == -1 && m_edges.at(i).next == -1);
+ Q_ASSERT(m_edges.at(i).node == 0);
+ Q_ASSERT(m_edges.at(i).pointingUp == m_edges.at(i).originallyPointingUp);
+ Q_ASSERT(m_edges.at(i).pointingUp == (m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from)));
+ // Ignore zero-length edges.
+ if (m_parent->m_vertices.at(m_edges.at(i).to) != m_parent->m_vertices.at(m_edges.at(i).from)) {
+ QPodPoint upper = m_parent->m_vertices.at(m_edges.at(i).upper());
+ QPodPoint lower = m_parent->m_vertices.at(m_edges.at(i).lower());
+ Event upperEvent = {{upper.x, upper.y}, Event::Upper, i};
+ Event lowerEvent = {{lower.x, lower.y}, Event::Lower, i};
+ m_events.add(upperEvent);
+ m_events.add(lowerEvent);
+ }
+ }
+
+ std::sort(m_events.data(), m_events.data() + m_events.size());
+}
+
+template <typename T>
+void QTriangulator<T>::ComplexToSimple::calculateIntersections()
+{
+ fillPriorityQueue();
+
+ Q_ASSERT(m_topIntersection.empty());
+ Q_ASSERT(m_edgeList.root == 0);
+
+ // Find all intersection points.
+ while (!m_events.isEmpty()) {
+ Event event = m_events.last();
+ sortEdgeList(event.point);
+
+ // Find all edges in the edge list that contain the current vertex and mark them to be split later.
+ QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> range = bounds(event.point);
+ QRBTree<int>::Node *leftNode = range.first ? m_edgeList.previous(range.first) : 0;
+ int vertex = (event.type == Event::Upper ? m_edges.at(event.edge).upper() : m_edges.at(event.edge).lower());
+ QIntersectionPoint eventPoint = QT_PREPEND_NAMESPACE(qIntersectionPoint)(event.point);
+
+ if (range.first != 0) {
+ splitEdgeListRange(range.first, range.second, vertex, eventPoint);
+ reorderEdgeListRange(range.first, range.second);
+ }
+
+ // Handle the edges with start or end point in the current vertex.
+ while (!m_events.isEmpty() && m_events.last().point == event.point) {
+ event = m_events.last();
+ m_events.pop_back();
+ int i = event.edge;
+
+ if (m_edges.at(i).node) {
+ // Remove edge from edge list.
+ Q_ASSERT(event.type == Event::Lower);
+ QRBTree<int>::Node *left = m_edgeList.previous(m_edges.at(i).node);
+ QRBTree<int>::Node *right = m_edgeList.next(m_edges.at(i).node);
+ m_edgeList.deleteNode(m_edges.at(i).node);
+ if (!left || !right)
+ continue;
+ calculateIntersection(left->data, right->data);
+ } else {
+ // Insert edge into edge list.
+ Q_ASSERT(event.type == Event::Upper);
+ QRBTree<int>::Node *left = searchEdgeLeftOf(i, leftNode);
+ m_edgeList.attachAfter(left, m_edges.at(i).node = m_edgeList.newNode());
+ m_edges.at(i).node->data = i;
+ QRBTree<int>::Node *right = m_edgeList.next(m_edges.at(i).node);
+ if (left)
+ calculateIntersection(left->data, i);
+ if (right)
+ calculateIntersection(i, right->data);
+ }
+ }
+ while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint <= eventPoint)
+ m_topIntersection.pop();
+#ifdef Q_TRIANGULATOR_DEBUG
+ DebugDialog dialog(this, vertex);
+ dialog.exec();
+#endif
+ }
+ m_processedEdgePairs.clear();
+}
+
+// Split an edge into two pieces at the given point.
+// The upper piece is pushed to the end of the 'm_edges' vector.
+// The lower piece replaces the old edge.
+// Return the edge whose 'from' is 'pointIndex'.
+template <typename T>
+int QTriangulator<T>::ComplexToSimple::splitEdge(int splitIndex)
+{
+ const Split &split = m_splits.at(splitIndex);
+ Edge &lowerEdge = m_edges.at(split.edge);
+ Q_ASSERT(lowerEdge.node == 0);
+ Q_ASSERT(lowerEdge.previous == -1 && lowerEdge.next == -1);
+
+ if (lowerEdge.from == split.vertex)
+ return split.edge;
+ if (lowerEdge.to == split.vertex)
+ return lowerEdge.next;
+
+ // Check that angle >= 90 degrees.
+ //Q_ASSERT(qDot(m_points.at(m_edges.at(edgeIndex).from) - m_points.at(pointIndex),
+ // m_points.at(m_edges.at(edgeIndex).to) - m_points.at(pointIndex)) <= 0);
+
+ Edge upperEdge = lowerEdge;
+ upperEdge.mayIntersect |= !split.accurate; // The edge may have been split before at an inaccurate split point.
+ lowerEdge.mayIntersect = !split.accurate;
+ if (lowerEdge.pointingUp) {
+ lowerEdge.to = upperEdge.from = split.vertex;
+ m_edges.add(upperEdge);
+ return m_edges.size() - 1;
+ } else {
+ lowerEdge.from = upperEdge.to = split.vertex;
+ m_edges.add(upperEdge);
+ return split.edge;
+ }
+}
+
+template <typename T>
+bool QTriangulator<T>::ComplexToSimple::splitEdgesAtIntersections()
+{
+ for (int i = 0; i < m_edges.size(); ++i)
+ m_edges.at(i).mayIntersect = false;
+ bool checkForNewIntersections = false;
+ for (int i = 0; i < m_splits.size(); ++i) {
+ splitEdge(i);
+ checkForNewIntersections |= !m_splits.at(i).accurate;
+ }
+ for (int i = 0; i < m_edges.size(); ++i) {
+ m_edges.at(i).originallyPointingUp = m_edges.at(i).pointingUp =
+ m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from);
+ }
+ m_splits.reset();
+ return checkForNewIntersections;
+}
+
+template <typename T>
+void QTriangulator<T>::ComplexToSimple::insertEdgeIntoVectorIfWanted(ShortArray &orderedEdges, int i)
+{
+ // Edges with zero length should not reach this part.
+ Q_ASSERT(m_parent->m_vertices.at(m_edges.at(i).from) != m_parent->m_vertices.at(m_edges.at(i).to));
+
+ // Skip edges with unwanted winding number.
+ int windingNumber = m_edges.at(i).winding;
+ if (m_edges.at(i).originallyPointingUp)
+ ++windingNumber;
+
+ // Make sure exactly one fill rule is specified.
+ Q_ASSERT(((m_parent->m_hint & QVectorPath::WindingFill) != 0) != ((m_parent->m_hint & QVectorPath::OddEvenFill) != 0));
+
+ if ((m_parent->m_hint & QVectorPath::WindingFill) && windingNumber != 0 && windingNumber != 1)
+ return;
+
+ // Skip cancelling edges.
+ if (!orderedEdges.isEmpty()) {
+ int j = orderedEdges[orderedEdges.size() - 1];
+ // If the last edge is already connected in one end, it should not be cancelled.
+ if (m_edges.at(j).next == -1 && m_edges.at(j).previous == -1
+ && (m_parent->m_vertices.at(m_edges.at(i).from) == m_parent->m_vertices.at(m_edges.at(j).to))
+ && (m_parent->m_vertices.at(m_edges.at(i).to) == m_parent->m_vertices.at(m_edges.at(j).from))) {
+ orderedEdges.removeLast();
+ return;
+ }
+ }
+ orderedEdges.append(i);
+}
+
+template <typename T>
+void QTriangulator<T>::ComplexToSimple::removeUnwantedEdgesAndConnect()
+{
+ Q_ASSERT(m_edgeList.root == 0);
+ // Initialize priority queue.
+ fillPriorityQueue();
+
+ ShortArray orderedEdges;
+
+ while (!m_events.isEmpty()) {
+ Event event = m_events.last();
+ int edgeIndex = event.edge;
+
+ // Check that all the edges in the list crosses the current scanline
+ //if (m_edgeList.root) {
+ // for (QRBTree<int>::Node *node = m_edgeList.front(m_edgeList.root); node; node = m_edgeList.next(node)) {
+ // Q_ASSERT(event.point <= m_points.at(m_edges.at(node->data).lower()));
+ // }
+ //}
+
+ orderedEdges.clear();
+ QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> b = outerBounds(event.point);
+ if (m_edgeList.root) {
+ QRBTree<int>::Node *current = (b.first ? m_edgeList.next(b.first) : m_edgeList.front(m_edgeList.root));
+ // Process edges that are going to be removed from the edge list at the current event point.
+ while (current != b.second) {
+ Q_ASSERT(current);
+ Q_ASSERT(m_edges.at(current->data).node == current);
+ Q_ASSERT(QT_PREPEND_NAMESPACE(qIntersectionPoint)(event.point).isOnLine(m_parent->m_vertices.at(m_edges.at(current->data).from), m_parent->m_vertices.at(m_edges.at(current->data).to)));
+ Q_ASSERT(m_parent->m_vertices.at(m_edges.at(current->data).from) == event.point || m_parent->m_vertices.at(m_edges.at(current->data).to) == event.point);
+ insertEdgeIntoVectorIfWanted(orderedEdges, current->data);
+ current = m_edgeList.next(current);
+ }
+ }
+
+ // Remove edges above the event point, insert edges below the event point.
+ do {
+ event = m_events.last();
+ m_events.pop_back();
+ edgeIndex = event.edge;
+
+ // Edges with zero length should not reach this part.
+ Q_ASSERT(m_parent->m_vertices.at(m_edges.at(edgeIndex).from) != m_parent->m_vertices.at(m_edges.at(edgeIndex).to));
+
+ if (m_edges.at(edgeIndex).node) {
+ Q_ASSERT(event.type == Event::Lower);
+ Q_ASSERT(event.point == m_parent->m_vertices.at(m_edges.at(event.edge).lower()));
+ m_edgeList.deleteNode(m_edges.at(edgeIndex).node);
+ } else {
+ Q_ASSERT(event.type == Event::Upper);
+ Q_ASSERT(event.point == m_parent->m_vertices.at(m_edges.at(event.edge).upper()));
+ QRBTree<int>::Node *left = searchEdgeLeftOf(edgeIndex, b.first);
+ m_edgeList.attachAfter(left, m_edges.at(edgeIndex).node = m_edgeList.newNode());
+ m_edges.at(edgeIndex).node->data = edgeIndex;
+ }
+ } while (!m_events.isEmpty() && m_events.last().point == event.point);
+
+ if (m_edgeList.root) {
+ QRBTree<int>::Node *current = (b.first ? m_edgeList.next(b.first) : m_edgeList.front(m_edgeList.root));
+
+ // Calculate winding number and turn counter-clockwise.
+ int currentWindingNumber = (b.first ? m_edges.at(b.first->data).winding : 0);
+ while (current != b.second) {
+ Q_ASSERT(current);
+ //Q_ASSERT(b.second == 0 || m_edgeList.order(current, b.second) < 0);
+ int i = current->data;
+ Q_ASSERT(m_edges.at(i).node == current);
+
+ // Winding number.
+ int ccwWindingNumber = m_edges.at(i).winding = currentWindingNumber;
+ if (m_edges.at(i).originallyPointingUp) {
+ --m_edges.at(i).winding;
+ } else {
+ ++m_edges.at(i).winding;
+ ++ccwWindingNumber;
+ }
+ currentWindingNumber = m_edges.at(i).winding;
+
+ // Turn counter-clockwise.
+ if ((ccwWindingNumber & 1) == 0) {
+ Q_ASSERT(m_edges.at(i).previous == -1 && m_edges.at(i).next == -1);
+ qSwap(m_edges.at(i).from, m_edges.at(i).to);
+ m_edges.at(i).pointingUp = !m_edges.at(i).pointingUp;
+ }
+
+ current = m_edgeList.next(current);
+ }
+
+ // Process edges that were inserted into the edge list at the current event point.
+ current = (b.second ? m_edgeList.previous(b.second) : m_edgeList.back(m_edgeList.root));
+ while (current != b.first) {
+ Q_ASSERT(current);
+ Q_ASSERT(m_edges.at(current->data).node == current);
+ insertEdgeIntoVectorIfWanted(orderedEdges, current->data);
+ current = m_edgeList.previous(current);
+ }
+ }
+ if (orderedEdges.isEmpty())
+ continue;
+
+ Q_ASSERT((orderedEdges.size() & 1) == 0);
+
+ // Connect edges.
+ // First make sure the first edge point towards the current point.
+ int i;
+ if (m_parent->m_vertices.at(m_edges.at(orderedEdges[0]).from) == event.point) {
+ i = 1;
+ int copy = orderedEdges[0]; // Make copy in case the append() will cause a reallocation.
+ orderedEdges.append(copy);
+ } else {
+ Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[0]).to) == event.point);
+ i = 0;
+ }
+
+ // Remove references to duplicate points. First find the point with lowest index.
+ int pointIndex = INT_MAX;
+ for (int j = i; j < orderedEdges.size(); j += 2) {
+ Q_ASSERT(j + 1 < orderedEdges.size());
+ Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[j]).to) == event.point);
+ Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[j + 1]).from) == event.point);
+ if (m_edges.at(orderedEdges[j]).to < pointIndex)
+ pointIndex = m_edges.at(orderedEdges[j]).to;
+ if (m_edges.at(orderedEdges[j + 1]).from < pointIndex)
+ pointIndex = m_edges.at(orderedEdges[j + 1]).from;
+ }
+
+ for (; i < orderedEdges.size(); i += 2) {
+ // Remove references to duplicate points by making all edges reference one common point.
+ m_edges.at(orderedEdges[i]).to = m_edges.at(orderedEdges[i + 1]).from = pointIndex;
+
+ Q_ASSERT(m_edges.at(orderedEdges[i]).pointingUp || m_edges.at(orderedEdges[i]).previous != -1);
+ Q_ASSERT(!m_edges.at(orderedEdges[i + 1]).pointingUp || m_edges.at(orderedEdges[i + 1]).next != -1);
+
+ m_edges.at(orderedEdges[i]).next = orderedEdges[i + 1];
+ m_edges.at(orderedEdges[i + 1]).previous = orderedEdges[i];
+ }
+ } // end while
+}
+
+template <typename T>
+void QTriangulator<T>::ComplexToSimple::removeUnusedPoints() {
+ QBitArray used(m_parent->m_vertices.size(), false);
+ for (int i = 0; i < m_edges.size(); ++i) {
+ Q_ASSERT((m_edges.at(i).previous == -1) == (m_edges.at(i).next == -1));
+ if (m_edges.at(i).next != -1)
+ used.setBit(m_edges.at(i).from);
+ }
+ QDataBuffer<quint32> newMapping(m_parent->m_vertices.size());
+ newMapping.resize(m_parent->m_vertices.size());
+ int count = 0;
+ for (int i = 0; i < m_parent->m_vertices.size(); ++i) {
+ if (used.at(i)) {
+ m_parent->m_vertices.at(count) = m_parent->m_vertices.at(i);
+ newMapping.at(i) = count;
+ ++count;
+ }
+ }
+ m_parent->m_vertices.resize(count);
+ for (int i = 0; i < m_edges.size(); ++i) {
+ m_edges.at(i).from = newMapping.at(m_edges.at(i).from);
+ m_edges.at(i).to = newMapping.at(m_edges.at(i).to);
+ }
+}
+
+template <typename T>
+inline bool QTriangulator<T>::ComplexToSimple::Event::operator < (const Event &other) const
+{
+ if (point == other.point)
+ return type < other.type; // 'Lower' has higher priority than 'Upper'.
+ return other.point < point;
+}
+
+//============================================================================//
+// QTriangulator::ComplexToSimple::DebugDialog //
+//============================================================================//
+
+#ifdef Q_TRIANGULATOR_DEBUG
+template <typename T>
+QTriangulator<T>::ComplexToSimple::DebugDialog::DebugDialog(ComplexToSimple *parent, int currentVertex)
+ : m_parent(parent), m_vertex(currentVertex)
+{
+ QDataBuffer<QPodPoint> &vertices = m_parent->m_parent->m_vertices;
+ if (vertices.isEmpty())
+ return;
+
+ int minX, maxX, minY, maxY;
+ minX = maxX = vertices.at(0).x;
+ minY = maxY = vertices.at(0).y;
+ for (int i = 1; i < vertices.size(); ++i) {
+ minX = qMin(minX, vertices.at(i).x);
+ maxX = qMax(maxX, vertices.at(i).x);
+ minY = qMin(minY, vertices.at(i).y);
+ maxY = qMax(maxY, vertices.at(i).y);
+ }
+ int w = maxX - minX;
+ int h = maxY - minY;
+ qreal border = qMin(w, h) / 10.0;
+ m_window = QRectF(minX - border, minY - border, (maxX - minX + 2 * border), (maxY - minY + 2 * border));
+}
+
+template <typename T>
+void QTriangulator<T>::ComplexToSimple::DebugDialog::paintEvent(QPaintEvent *)
+{
+ QPainter p(this);
+ p.setRenderHint(QPainter::Antialiasing, true);
+ p.fillRect(rect(), Qt::black);
+ QDataBuffer<QPodPoint> &vertices = m_parent->m_parent->m_vertices;
+ if (vertices.isEmpty())
+ return;
+
+ qreal halfPointSize = qMin(m_window.width(), m_window.height()) / 300.0;
+ p.setWindow(m_window.toRect());
+
+ p.setPen(Qt::white);
+
+ QDataBuffer<Edge> &edges = m_parent->m_edges;
+ for (int i = 0; i < edges.size(); ++i) {
+ QPodPoint u = vertices.at(edges.at(i).from);
+ QPodPoint v = vertices.at(edges.at(i).to);
+ p.drawLine(u.x, u.y, v.x, v.y);
+ }
+
+ for (int i = 0; i < vertices.size(); ++i) {
+ QPodPoint q = vertices.at(i);
+ p.fillRect(QRectF(q.x - halfPointSize, q.y - halfPointSize, 2 * halfPointSize, 2 * halfPointSize), Qt::red);
+ }
+
+ Qt::GlobalColor colors[6] = {Qt::red, Qt::green, Qt::blue, Qt::cyan, Qt::magenta, Qt::yellow};
+ p.setOpacity(0.5);
+ int count = 0;
+ if (m_parent->m_edgeList.root) {
+ QRBTree<int>::Node *current = m_parent->m_edgeList.front(m_parent->m_edgeList.root);
+ while (current) {
+ p.setPen(colors[count++ % 6]);
+ QPodPoint u = vertices.at(edges.at(current->data).from);
+ QPodPoint v = vertices.at(edges.at(current->data).to);
+ p.drawLine(u.x, u.y, v.x, v.y);
+ current = m_parent->m_edgeList.next(current);
+ }
+ }
+
+ p.setOpacity(1.0);
+ QPodPoint q = vertices.at(m_vertex);
+ p.fillRect(QRectF(q.x - halfPointSize, q.y - halfPointSize, 2 * halfPointSize, 2 * halfPointSize), Qt::green);
+
+ p.setPen(Qt::gray);
+ QDataBuffer<Split> &splits = m_parent->m_splits;
+ for (int i = 0; i < splits.size(); ++i) {
+ QPodPoint q = vertices.at(splits.at(i).vertex);
+ QPodPoint u = vertices.at(edges.at(splits.at(i).edge).from) - q;
+ QPodPoint v = vertices.at(edges.at(splits.at(i).edge).to) - q;
+ qreal uLen = qSqrt(qDot(u, u));
+ qreal vLen = qSqrt(qDot(v, v));
+ if (uLen) {
+ u.x *= 2 * halfPointSize / uLen;
+ u.y *= 2 * halfPointSize / uLen;
+ }
+ if (vLen) {
+ v.x *= 2 * halfPointSize / vLen;
+ v.y *= 2 * halfPointSize / vLen;
+ }
+ u += q;
+ v += q;
+ p.drawLine(u.x, u.y, v.x, v.y);
+ }
+}
+
+template <typename T>
+void QTriangulator<T>::ComplexToSimple::DebugDialog::wheelEvent(QWheelEvent *event)
+{
+ qreal scale = qExp(-0.001 * event->delta());
+ QPointF center = m_window.center();
+ QPointF delta = scale * (m_window.bottomRight() - center);
+ m_window = QRectF(center - delta, center + delta);
+ event->accept();
+ update();
+}
+
+template <typename T>
+void QTriangulator<T>::ComplexToSimple::DebugDialog::mouseMoveEvent(QMouseEvent *event)
+{
+ if (event->buttons() & Qt::LeftButton) {
+ QPointF delta = event->pos() - m_lastMousePos;
+ delta.setX(delta.x() * m_window.width() / width());
+ delta.setY(delta.y() * m_window.height() / height());
+ m_window.translate(-delta.x(), -delta.y());
+ m_lastMousePos = event->pos();
+ event->accept();
+ update();
+ }
+}
+
+template <typename T>
+void QTriangulator<T>::ComplexToSimple::DebugDialog::mousePressEvent(QMouseEvent *event)
+{
+ if (event->button() == Qt::LeftButton)
+ m_lastMousePos = event->pos();
+ event->accept();
+}
+
+
+#endif
+
+//============================================================================//
+// QTriangulator::SimpleToMonotone //
+//============================================================================//
+template <typename T>
+void QTriangulator<T>::SimpleToMonotone::decompose()
+{
+ setupDataStructures();
+ removeZeroLengthEdges();
+ monotoneDecomposition();
+
+ m_parent->m_indices.clear();
+ QBitArray processed(m_edges.size(), false);
+ for (int first = 0; first < m_edges.size(); ++first) {
+ if (processed.at(first))
+ continue;
+ int i = first;
+ do {
+ Q_ASSERT(!processed.at(i));
+ Q_ASSERT(m_edges.at(m_edges.at(i).next).previous == i);
+ m_parent->m_indices.push_back(m_edges.at(i).from);
+ processed.setBit(i);
+ i = m_edges.at(i).next;
+ } while (i != first);
+ if (m_parent->m_indices.size() > 0 && m_parent->m_indices.back() != T(-1)) // Q_TRIANGULATE_END_OF_POLYGON
+ m_parent->m_indices.push_back(T(-1)); // Q_TRIANGULATE_END_OF_POLYGON
+ }
+}
+
+template <typename T>
+void QTriangulator<T>::SimpleToMonotone::setupDataStructures()
+{
+ int i = 0;
+ Edge e;
+ e.node = 0;
+ e.twin = -1;
+
+ while (i + 3 <= m_parent->m_indices.size()) {
+ int start = m_edges.size();
+
+ do {
+ e.from = m_parent->m_indices.at(i);
+ e.type = RegularVertex;
+ e.next = m_edges.size() + 1;
+ e.previous = m_edges.size() - 1;
+ m_edges.add(e);
+ ++i;
+ Q_ASSERT(i < m_parent->m_indices.size());
+ } while (m_parent->m_indices.at(i) != T(-1)); // Q_TRIANGULATE_END_OF_POLYGON
+
+ m_edges.last().next = start;
+ m_edges.at(start).previous = m_edges.size() - 1;
+ ++i; // Skip Q_TRIANGULATE_END_OF_POLYGON.
+ }
+
+ for (i = 0; i < m_edges.size(); ++i) {
+ m_edges.at(i).to = m_edges.at(m_edges.at(i).next).from;
+ m_edges.at(i).pointingUp = m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from);
+ m_edges.at(i).helper = -1; // Not initialized here.
+ }
+}
+
+template <typename T>
+void QTriangulator<T>::SimpleToMonotone::removeZeroLengthEdges()
+{
+ for (int i = 0; i < m_edges.size(); ++i) {
+ if (m_parent->m_vertices.at(m_edges.at(i).from) == m_parent->m_vertices.at(m_edges.at(i).to)) {
+ m_edges.at(m_edges.at(i).previous).next = m_edges.at(i).next;
+ m_edges.at(m_edges.at(i).next).previous = m_edges.at(i).previous;
+ m_edges.at(m_edges.at(i).next).from = m_edges.at(i).from;
+ m_edges.at(i).next = -1; // Mark as removed.
+ }
+ }
+
+ QDataBuffer<int> newMapping(m_edges.size());
+ newMapping.resize(m_edges.size());
+ int count = 0;
+ for (int i = 0; i < m_edges.size(); ++i) {
+ if (m_edges.at(i).next != -1) {
+ m_edges.at(count) = m_edges.at(i);
+ newMapping.at(i) = count;
+ ++count;
+ }
+ }
+ m_edges.resize(count);
+ for (int i = 0; i < m_edges.size(); ++i) {
+ m_edges.at(i).next = newMapping.at(m_edges.at(i).next);
+ m_edges.at(i).previous = newMapping.at(m_edges.at(i).previous);
+ }
+}
+
+template <typename T>
+void QTriangulator<T>::SimpleToMonotone::fillPriorityQueue()
+{
+ m_upperVertex.reset();
+ m_upperVertex.reserve(m_edges.size());
+ for (int i = 0; i < m_edges.size(); ++i)
+ m_upperVertex.add(i);
+ CompareVertices cmp(this);
+ std::sort(m_upperVertex.data(), m_upperVertex.data() + m_upperVertex.size(), cmp);
+ //for (int i = 1; i < m_upperVertex.size(); ++i) {
+ // Q_ASSERT(!cmp(m_upperVertex.at(i), m_upperVertex.at(i - 1)));
+ //}
+}
+
+template <typename T>
+bool QTriangulator<T>::SimpleToMonotone::edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const
+{
+ const Edge &leftEdge = m_edges.at(leftEdgeIndex);
+ const Edge &rightEdge = m_edges.at(rightEdgeIndex);
+ const QPodPoint &u = m_parent->m_vertices.at(rightEdge.upper());
+ const QPodPoint &l = m_parent->m_vertices.at(rightEdge.lower());
+ qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(m_parent->m_vertices.at(leftEdge.upper()), l, u);
+ // d < 0: left, d > 0: right, d == 0: on top
+ if (d == 0)
+ d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(m_parent->m_vertices.at(leftEdge.lower()), l, u);
+ return d < 0;
+}
+
+// Returns the rightmost edge not to the right of the given edge.
+template <typename T>
+QRBTree<int>::Node *QTriangulator<T>::SimpleToMonotone::searchEdgeLeftOfEdge(int edgeIndex) const
+{
+ QRBTree<int>::Node *current = m_edgeList.root;
+ QRBTree<int>::Node *result = 0;
+ while (current) {
+ if (edgeIsLeftOfEdge(edgeIndex, current->data)) {
+ current = current->left;
+ } else {
+ result = current;
+ current = current->right;
+ }
+ }
+ return result;
+}
+
+// Returns the rightmost edge left of the given point.
+template <typename T>
+QRBTree<int>::Node *QTriangulator<T>::SimpleToMonotone::searchEdgeLeftOfPoint(int pointIndex) const
+{
+ QRBTree<int>::Node *current = m_edgeList.root;
+ QRBTree<int>::Node *result = 0;
+ while (current) {
+ const QPodPoint &p1 = m_parent->m_vertices.at(m_edges.at(current->data).lower());
+ const QPodPoint &p2 = m_parent->m_vertices.at(m_edges.at(current->data).upper());
+ qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(m_parent->m_vertices.at(pointIndex), p1, p2);
+ if (d <= 0) {
+ current = current->left;
+ } else {
+ result = current;
+ current = current->right;
+ }
+ }
+ return result;
+}
+
+template <typename T>
+void QTriangulator<T>::SimpleToMonotone::classifyVertex(int i)
+{
+ Edge &e2 = m_edges.at(i);
+ const Edge &e1 = m_edges.at(e2.previous);
+
+ bool startOrSplit = (e1.pointingUp && !e2.pointingUp);
+ bool endOrMerge = (!e1.pointingUp && e2.pointingUp);
+
+ const QPodPoint &p1 = m_parent->m_vertices.at(e1.from);
+ const QPodPoint &p2 = m_parent->m_vertices.at(e2.from);
+ const QPodPoint &p3 = m_parent->m_vertices.at(e2.to);
+ qint64 d = QT_PREPEND_NAMESPACE(qPointDistanceFromLine)(p1, p2, p3);
+ Q_ASSERT(d != 0 || (!startOrSplit && !endOrMerge));
+
+ e2.type = RegularVertex;
+
+ if (m_clockwiseOrder) {
+ if (startOrSplit)
+ e2.type = (d < 0 ? SplitVertex : StartVertex);
+ else if (endOrMerge)
+ e2.type = (d < 0 ? MergeVertex : EndVertex);
+ } else {
+ if (startOrSplit)
+ e2.type = (d > 0 ? SplitVertex : StartVertex);
+ else if (endOrMerge)
+ e2.type = (d > 0 ? MergeVertex : EndVertex);
+ }
+}
+
+template <typename T>
+void QTriangulator<T>::SimpleToMonotone::classifyVertices()
+{
+ for (int i = 0; i < m_edges.size(); ++i)
+ classifyVertex(i);
+}
+
+template <typename T>
+bool QTriangulator<T>::SimpleToMonotone::pointIsInSector(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2, const QPodPoint &v3)
+{
+ bool leftOfPreviousEdge = !qPointIsLeftOfLine(p, v2, v1);
+ bool leftOfNextEdge = !qPointIsLeftOfLine(p, v3, v2);
+
+ if (qPointIsLeftOfLine(v1, v2, v3))
+ return leftOfPreviousEdge && leftOfNextEdge;
+ else
+ return leftOfPreviousEdge || leftOfNextEdge;
+}
+
+template <typename T>
+bool QTriangulator<T>::SimpleToMonotone::pointIsInSector(int vertex, int sector)
+{
+ const QPodPoint &center = m_parent->m_vertices.at(m_edges.at(sector).from);
+ // Handle degenerate edges.
+ while (m_parent->m_vertices.at(m_edges.at(vertex).from) == center)
+ vertex = m_edges.at(vertex).next;
+ int next = m_edges.at(sector).next;
+ while (m_parent->m_vertices.at(m_edges.at(next).from) == center)
+ next = m_edges.at(next).next;
+ int previous = m_edges.at(sector).previous;
+ while (m_parent->m_vertices.at(m_edges.at(previous).from) == center)
+ previous = m_edges.at(previous).previous;
+
+ const QPodPoint &p = m_parent->m_vertices.at(m_edges.at(vertex).from);
+ const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(previous).from);
+ const QPodPoint &v3 = m_parent->m_vertices.at(m_edges.at(next).from);
+ if (m_clockwiseOrder)
+ return pointIsInSector(p, v3, center, v1);
+ else
+ return pointIsInSector(p, v1, center, v3);
+}
+
+template <typename T>
+int QTriangulator<T>::SimpleToMonotone::findSector(int edge, int vertex)
+{
+ while (!pointIsInSector(vertex, edge)) {
+ edge = m_edges.at(m_edges.at(edge).previous).twin;
+ Q_ASSERT(edge != -1);
+ }
+ return edge;
+}
+
+template <typename T>
+void QTriangulator<T>::SimpleToMonotone::createDiagonal(int lower, int upper)
+{
+ lower = findSector(lower, upper);
+ upper = findSector(upper, lower);
+
+ int prevLower = m_edges.at(lower).previous;
+ int prevUpper = m_edges.at(upper).previous;
+
+ Edge e;
+
+ e.twin = m_edges.size() + 1;
+ e.next = upper;
+ e.previous = prevLower;
+ e.from = m_edges.at(lower).from;
+ e.to = m_edges.at(upper).from;
+ m_edges.at(upper).previous = m_edges.at(prevLower).next = int(m_edges.size());
+ m_edges.add(e);
+
+ e.twin = m_edges.size() - 1;
+ e.next = lower;
+ e.previous = prevUpper;
+ e.from = m_edges.at(upper).from;
+ e.to = m_edges.at(lower).from;
+ m_edges.at(lower).previous = m_edges.at(prevUpper).next = int(m_edges.size());
+ m_edges.add(e);
+}
+
+template <typename T>
+void QTriangulator<T>::SimpleToMonotone::monotoneDecomposition()
+{
+ if (m_edges.isEmpty())
+ return;
+
+ Q_ASSERT(!m_edgeList.root);
+ QDataBuffer<QPair<int, int> > diagonals(m_upperVertex.size());
+
+ int i = 0;
+ for (int index = 1; index < m_edges.size(); ++index) {
+ if (m_parent->m_vertices.at(m_edges.at(index).from) < m_parent->m_vertices.at(m_edges.at(i).from))
+ i = index;
+ }
+ Q_ASSERT(i < m_edges.size());
+ int j = m_edges.at(i).previous;
+ Q_ASSERT(j < m_edges.size());
+ m_clockwiseOrder = qPointIsLeftOfLine(m_parent->m_vertices.at((quint32)m_edges.at(i).from),
+ m_parent->m_vertices.at((quint32)m_edges.at(j).from), m_parent->m_vertices.at((quint32)m_edges.at(i).to));
+
+ classifyVertices();
+ fillPriorityQueue();
+
+ // debug: set helpers explicitly (shouldn't be necessary)
+ //for (int i = 0; i < m_edges.size(); ++i)
+ // m_edges.at(i).helper = m_edges.at(i).upper();
+
+ while (!m_upperVertex.isEmpty()) {
+ i = m_upperVertex.last();
+ Q_ASSERT(i < m_edges.size());
+ m_upperVertex.pop_back();
+ j = m_edges.at(i).previous;
+ Q_ASSERT(j < m_edges.size());
+
+ QRBTree<int>::Node *leftEdgeNode = 0;
+
+ switch (m_edges.at(i).type) {
+ case RegularVertex:
+ // If polygon interior is to the right of the vertex...
+ if (m_edges.at(i).pointingUp == m_clockwiseOrder) {
+ if (m_edges.at(i).node) {
+ Q_ASSERT(!m_edges.at(j).node);
+ if (m_edges.at(m_edges.at(i).helper).type == MergeVertex)
+ diagonals.add(QPair<int, int>(i, m_edges.at(i).helper));
+ m_edges.at(j).node = m_edges.at(i).node;
+ m_edges.at(i).node = 0;
+ m_edges.at(j).node->data = j;
+ m_edges.at(j).helper = i;
+ } else if (m_edges.at(j).node) {
+ Q_ASSERT(!m_edges.at(i).node);
+ if (m_edges.at(m_edges.at(j).helper).type == MergeVertex)
+ diagonals.add(QPair<int, int>(i, m_edges.at(j).helper));
+ m_edges.at(i).node = m_edges.at(j).node;
+ m_edges.at(j).node = 0;
+ m_edges.at(i).node->data = i;
+ m_edges.at(i).helper = i;
+ } else {
+ qWarning("Inconsistent polygon. (#1)");
+ }
+ } else {
+ leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from);
+ if (leftEdgeNode) {
+ if (m_edges.at(m_edges.at(leftEdgeNode->data).helper).type == MergeVertex)
+ diagonals.add(QPair<int, int>(i, m_edges.at(leftEdgeNode->data).helper));
+ m_edges.at(leftEdgeNode->data).helper = i;
+ } else {
+ qWarning("Inconsistent polygon. (#2)");
+ }
+ }
+ break;
+ case SplitVertex:
+ leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from);
+ if (leftEdgeNode) {
+ diagonals.add(QPair<int, int>(i, m_edges.at(leftEdgeNode->data).helper));
+ m_edges.at(leftEdgeNode->data).helper = i;
+ } else {
+ qWarning("Inconsistent polygon. (#3)");
+ }
+ Q_FALLTHROUGH();
+ case StartVertex:
+ if (m_clockwiseOrder) {
+ leftEdgeNode = searchEdgeLeftOfEdge(j);
+ QRBTree<int>::Node *node = m_edgeList.newNode();
+ node->data = j;
+ m_edges.at(j).node = node;
+ m_edges.at(j).helper = i;
+ m_edgeList.attachAfter(leftEdgeNode, node);
+ Q_ASSERT(m_edgeList.validate());
+ } else {
+ leftEdgeNode = searchEdgeLeftOfEdge(i);
+ QRBTree<int>::Node *node = m_edgeList.newNode();
+ node->data = i;
+ m_edges.at(i).node = node;
+ m_edges.at(i).helper = i;
+ m_edgeList.attachAfter(leftEdgeNode, node);
+ Q_ASSERT(m_edgeList.validate());
+ }
+ break;
+ case MergeVertex:
+ leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from);
+ if (leftEdgeNode) {
+ if (m_edges.at(m_edges.at(leftEdgeNode->data).helper).type == MergeVertex)
+ diagonals.add(QPair<int, int>(i, m_edges.at(leftEdgeNode->data).helper));
+ m_edges.at(leftEdgeNode->data).helper = i;
+ } else {
+ qWarning("Inconsistent polygon. (#4)");
+ }
+ Q_FALLTHROUGH();
+ case EndVertex:
+ if (m_clockwiseOrder) {
+ if (m_edges.at(m_edges.at(i).helper).type == MergeVertex)
+ diagonals.add(QPair<int, int>(i, m_edges.at(i).helper));
+ if (m_edges.at(i).node) {
+ m_edgeList.deleteNode(m_edges.at(i).node);
+ Q_ASSERT(m_edgeList.validate());
+ } else {
+ qWarning("Inconsistent polygon. (#5)");
+ }
+ } else {
+ if (m_edges.at(m_edges.at(j).helper).type == MergeVertex)
+ diagonals.add(QPair<int, int>(i, m_edges.at(j).helper));
+ if (m_edges.at(j).node) {
+ m_edgeList.deleteNode(m_edges.at(j).node);
+ Q_ASSERT(m_edgeList.validate());
+ } else {
+ qWarning("Inconsistent polygon. (#6)");
+ }
+ }
+ break;
+ }
+ }
+
+ for (int i = 0; i < diagonals.size(); ++i)
+ createDiagonal(diagonals.at(i).first, diagonals.at(i).second);
+}
+
+template <typename T>
+bool QTriangulator<T>::SimpleToMonotone::CompareVertices::operator () (int i, int j) const
+{
+ if (m_parent->m_edges.at(i).from == m_parent->m_edges.at(j).from)
+ return m_parent->m_edges.at(i).type > m_parent->m_edges.at(j).type;
+ return m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(i).from) >
+ m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(j).from);
+}
+
+//============================================================================//
+// QTriangulator::MonotoneToTriangles //
+//============================================================================//
+template <typename T>
+void QTriangulator<T>::MonotoneToTriangles::decompose()
+{
+ QVector<T> result;
+ QDataBuffer<int> stack(m_parent->m_indices.size());
+ m_first = 0;
+ // Require at least three more indices.
+ while (m_first + 3 <= m_parent->m_indices.size()) {
+ m_length = 0;
+ while (m_parent->m_indices.at(m_first + m_length) != T(-1)) { // Q_TRIANGULATE_END_OF_POLYGON
+ ++m_length;
+ Q_ASSERT(m_first + m_length < m_parent->m_indices.size());
+ }
+ if (m_length < 3) {
+ m_first += m_length + 1;
+ continue;
+ }
+
+ int minimum = 0;
+ while (less(next(minimum), minimum))
+ minimum = next(minimum);
+ while (less(previous(minimum), minimum))
+ minimum = previous(minimum);
+
+ stack.reset();
+ stack.add(minimum);
+ int left = previous(minimum);
+ int right = next(minimum);
+ bool stackIsOnLeftSide;
+ bool clockwiseOrder = leftOfEdge(minimum, left, right);
+
+ if (less(left, right)) {
+ stack.add(left);
+ left = previous(left);
+ stackIsOnLeftSide = true;
+ } else {
+ stack.add(right);
+ right = next(right);
+ stackIsOnLeftSide = false;
+ }
+
+ for (int count = 0; count + 2 < m_length; ++count)
+ {
+ Q_ASSERT(stack.size() >= 2);
+ if (less(left, right)) {
+ if (stackIsOnLeftSide == false) {
+ for (int i = 0; i + 1 < stack.size(); ++i) {
+ result.push_back(indices(stack.at(i + 1)));
+ result.push_back(indices(left));
+ result.push_back(indices(stack.at(i)));
+ }
+ stack.first() = stack.last();
+ stack.resize(1);
+ } else {
+ while (stack.size() >= 2 && (clockwiseOrder ^ !leftOfEdge(left, stack.at(stack.size() - 2), stack.last()))) {
+ result.push_back(indices(stack.at(stack.size() - 2)));
+ result.push_back(indices(left));
+ result.push_back(indices(stack.last()));
+ stack.pop_back();
+ }
+ }
+ stack.add(left);
+ left = previous(left);
+ stackIsOnLeftSide = true;
+ } else {
+ if (stackIsOnLeftSide == true) {
+ for (int i = 0; i + 1 < stack.size(); ++i) {
+ result.push_back(indices(stack.at(i)));
+ result.push_back(indices(right));
+ result.push_back(indices(stack.at(i + 1)));
+ }
+ stack.first() = stack.last();
+ stack.resize(1);
+ } else {
+ while (stack.size() >= 2 && (clockwiseOrder ^ !leftOfEdge(right, stack.last(), stack.at(stack.size() - 2)))) {
+ result.push_back(indices(stack.last()));
+ result.push_back(indices(right));
+ result.push_back(indices(stack.at(stack.size() - 2)));
+ stack.pop_back();
+ }
+ }
+ stack.add(right);
+ right = next(right);
+ stackIsOnLeftSide = false;
+ }
+ }
+
+ m_first += m_length + 1;
+ }
+ m_parent->m_indices = result;
+}
+
+//============================================================================//
+// qTriangulate //
+//============================================================================//
+
+static bool hasElementIndexUint()
+{
+#ifndef QT_NO_OPENGL
+ QOpenGLContext *context = QOpenGLContext::currentContext();
+ if (!context)
+ return false;
+ return static_cast<QOpenGLExtensions *>(context->functions())->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint);
+#else
+ return false;
+#endif
+}
+
+Q_GUI_EXPORT QTriangleSet qTriangulate(const qreal *polygon,
+ int count, uint hint, const QTransform &matrix)
+{
+ QTriangleSet triangleSet;
+ if (hasElementIndexUint()) {
+ QTriangulator<quint32> triangulator;
+ triangulator.initialize(polygon, count, hint, matrix);
+ QVertexSet<quint32> vertexSet = triangulator.triangulate();
+ triangleSet.vertices = vertexSet.vertices;
+ triangleSet.indices.setDataUint(vertexSet.indices);
+
+ } else {
+ QTriangulator<quint16> triangulator;
+ triangulator.initialize(polygon, count, hint, matrix);
+ QVertexSet<quint16> vertexSet = triangulator.triangulate();
+ triangleSet.vertices = vertexSet.vertices;
+ triangleSet.indices.setDataUshort(vertexSet.indices);
+ }
+ return triangleSet;
+}
+
+Q_GUI_EXPORT QTriangleSet qTriangulate(const QVectorPath &path,
+ const QTransform &matrix, qreal lod)
+{
+ QTriangleSet triangleSet;
+ if (hasElementIndexUint()) {
+ QTriangulator<quint32> triangulator;
+ triangulator.initialize(path, matrix, lod);
+ QVertexSet<quint32> vertexSet = triangulator.triangulate();
+ triangleSet.vertices = vertexSet.vertices;
+ triangleSet.indices.setDataUint(vertexSet.indices);
+ } else {
+ QTriangulator<quint16> triangulator;
+ triangulator.initialize(path, matrix, lod);
+ QVertexSet<quint16> vertexSet = triangulator.triangulate();
+ triangleSet.vertices = vertexSet.vertices;
+ triangleSet.indices.setDataUshort(vertexSet.indices);
+ }
+ return triangleSet;
+}
+
+QTriangleSet qTriangulate(const QPainterPath &path,
+ const QTransform &matrix, qreal lod)
+{
+ QTriangleSet triangleSet;
+ if (hasElementIndexUint()) {
+ QTriangulator<quint32> triangulator;
+ triangulator.initialize(path, matrix, lod);
+ QVertexSet<quint32> vertexSet = triangulator.triangulate();
+ triangleSet.vertices = vertexSet.vertices;
+ triangleSet.indices.setDataUint(vertexSet.indices);
+ } else {
+ QTriangulator<quint16> triangulator;
+ triangulator.initialize(path, matrix, lod);
+ QVertexSet<quint16> vertexSet = triangulator.triangulate();
+ triangleSet.vertices = vertexSet.vertices;
+ triangleSet.indices.setDataUshort(vertexSet.indices);
+ }
+ return triangleSet;
+}
+
+QPolylineSet qPolyline(const QVectorPath &path,
+ const QTransform &matrix, qreal lod)
+{
+ QPolylineSet polyLineSet;
+ if (hasElementIndexUint()) {
+ QTriangulator<quint32> triangulator;
+ triangulator.initialize(path, matrix, lod);
+ QVertexSet<quint32> vertexSet = triangulator.polyline();
+ polyLineSet.vertices = vertexSet.vertices;
+ polyLineSet.indices.setDataUint(vertexSet.indices);
+ } else {
+ QTriangulator<quint16> triangulator;
+ triangulator.initialize(path, matrix, lod);
+ QVertexSet<quint16> vertexSet = triangulator.polyline();
+ polyLineSet.vertices = vertexSet.vertices;
+ polyLineSet.indices.setDataUshort(vertexSet.indices);
+ }
+ return polyLineSet;
+}
+
+QPolylineSet qPolyline(const QPainterPath &path,
+ const QTransform &matrix, qreal lod)
+{
+ QPolylineSet polyLineSet;
+ if (hasElementIndexUint()) {
+ QTriangulator<quint32> triangulator;
+ triangulator.initialize(path, matrix, lod);
+ QVertexSet<quint32> vertexSet = triangulator.polyline();
+ polyLineSet.vertices = vertexSet.vertices;
+ polyLineSet.indices.setDataUint(vertexSet.indices);
+ } else {
+ QTriangulator<quint16> triangulator;
+ triangulator.initialize(path, matrix, lod);
+ QVertexSet<quint16> vertexSet = triangulator.polyline();
+ polyLineSet.vertices = vertexSet.vertices;
+ polyLineSet.indices.setDataUshort(vertexSet.indices);
+ }
+ return polyLineSet;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qtriangulator_p.h b/src/gui/painting/qtriangulator_p.h
new file mode 100644
index 0000000000..4d1aba099c
--- /dev/null
+++ b/src/gui/painting/qtriangulator_p.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTRIANGULATOR_P_H
+#define QTRIANGULATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qtguiglobal_p.h>
+#include <QtCore/qvector.h>
+#include <QtGui/private/qvectorpath_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QVertexIndexVector
+{
+public:
+ enum Type {
+ UnsignedInt,
+ UnsignedShort
+ };
+
+ inline Type type() const { return t; }
+
+ inline void setDataUint(const QVector<quint32> &data)
+ {
+ t = UnsignedInt;
+ indices32 = data;
+ }
+
+ inline void setDataUshort(const QVector<quint16> &data)
+ {
+ t = UnsignedShort;
+ indices16 = data;
+ }
+
+ inline const void* data() const
+ {
+ if (t == UnsignedInt)
+ return indices32.data();
+ return indices16.data();
+ }
+
+ inline int size() const
+ {
+ if (t == UnsignedInt)
+ return indices32.size();
+ return indices16.size();
+ }
+
+ inline QVertexIndexVector &operator = (const QVertexIndexVector &other)
+ {
+ if (t == UnsignedInt)
+ indices32 = other.indices32;
+ else
+ indices16 = other.indices16;
+
+ t = other.t;
+ return *this;
+ }
+
+private:
+
+ Type t;
+ QVector<quint32> indices32;
+ QVector<quint16> indices16;
+};
+
+struct Q_GUI_EXPORT QTriangleSet
+{
+ inline QTriangleSet() { }
+ inline QTriangleSet(const QTriangleSet &other) : vertices(other.vertices), indices(other.indices) { }
+ QTriangleSet &operator = (const QTriangleSet &other) {vertices = other.vertices; indices = other.indices; return *this;}
+
+ // The vertices of a triangle are given by: (x[i[n]], y[i[n]]), (x[j[n]], y[j[n]]), (x[k[n]], y[k[n]]), n = 0, 1, ...
+ QVector<qreal> vertices; // [x[0], y[0], x[1], y[1], x[2], ...]
+ QVertexIndexVector indices; // [i[0], j[0], k[0], i[1], j[1], k[1], i[2], ...]
+};
+
+struct Q_GUI_EXPORT QPolylineSet
+{
+ inline QPolylineSet() { }
+ inline QPolylineSet(const QPolylineSet &other) : vertices(other.vertices), indices(other.indices) { }
+ QPolylineSet &operator = (const QPolylineSet &other) {vertices = other.vertices; indices = other.indices; return *this;}
+
+ QVector<qreal> vertices; // [x[0], y[0], x[1], y[1], x[2], ...]
+ QVertexIndexVector indices; // End of polyline is marked with -1.
+};
+
+// The vertex coordinates of the returned triangle set will be rounded to a grid with a mesh size
+// of 1/32. The polygon is first transformed, then scaled by 32, the coordinates are rounded to
+// integers, the polygon is triangulated, and then scaled back by 1/32.
+// 'hint' should be a combination of QVectorPath::Hints.
+// 'lod' is the level of detail. Default is 1. Curves are split into more lines when 'lod' is higher.
+QTriangleSet Q_GUI_EXPORT qTriangulate(const qreal *polygon, int count, uint hint = QVectorPath::PolygonHint | QVectorPath::OddEvenFill, const QTransform &matrix = QTransform());
+QTriangleSet Q_GUI_EXPORT qTriangulate(const QVectorPath &path, const QTransform &matrix = QTransform(), qreal lod = 1);
+QTriangleSet Q_GUI_EXPORT qTriangulate(const QPainterPath &path, const QTransform &matrix = QTransform(), qreal lod = 1);
+QPolylineSet qPolyline(const QVectorPath &path, const QTransform &matrix = QTransform(), qreal lod = 1);
+QPolylineSet Q_GUI_EXPORT qPolyline(const QPainterPath &path, const QTransform &matrix = QTransform(), qreal lod = 1);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qvectorpath_p.h b/src/gui/painting/qvectorpath_p.h
index 01f7418930..d1b08ed423 100644
--- a/src/gui/painting/qvectorpath_p.h
+++ b/src/gui/painting/qvectorpath_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtGui/private/qtguiglobal_p.h>
#include <QtGui/qpaintengine.h>
#include <private/qpaintengine_p.h>