summaryrefslogtreecommitdiffstats
path: root/src/gui/painting
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/painting')
-rw-r--r--src/gui/painting/painting.pri2
-rw-r--r--src/gui/painting/qbackingstore.cpp40
-rw-r--r--src/gui/painting/qblendfunctions_p.h53
-rw-r--r--src/gui/painting/qbrush.cpp173
-rw-r--r--src/gui/painting/qcolor.cpp4
-rw-r--r--src/gui/painting/qcolorspace.cpp66
-rw-r--r--src/gui/painting/qcolorspace.h4
-rw-r--r--src/gui/painting/qcolorspace_p.h4
-rw-r--r--src/gui/painting/qcolortransfertable_p.h51
-rw-r--r--src/gui/painting/qcolortrc_p.h2
-rw-r--r--src/gui/painting/qcolortrclut_p.h1
-rw-r--r--src/gui/painting/qcompositionfunctions.cpp8
-rw-r--r--src/gui/painting/qcosmeticstroker.cpp18
-rw-r--r--src/gui/painting/qdatabuffer_p.h3
-rw-r--r--src/gui/painting/qdrawhelper.cpp40
-rw-r--r--src/gui/painting/qdrawhelper_sse2.cpp4
-rw-r--r--src/gui/painting/qdrawhelper_sse4.cpp8
-rw-r--r--src/gui/painting/qicc.cpp42
-rw-r--r--src/gui/painting/qimagescale.cpp2
-rw-r--r--src/gui/painting/qimagescale_neon.cpp2
-rw-r--r--src/gui/painting/qimagescale_sse4.cpp2
-rw-r--r--src/gui/painting/qmemrotate.cpp42
-rw-r--r--src/gui/painting/qpagesize.h2
-rw-r--r--src/gui/painting/qpaintengine_raster.cpp54
-rw-r--r--src/gui/painting/qpaintengine_raster_p.h3
-rw-r--r--src/gui/painting/qpaintengineex.cpp44
-rw-r--r--src/gui/painting/qpainter.cpp68
-rw-r--r--src/gui/painting/qpainterpath.cpp7
-rw-r--r--src/gui/painting/qpainterpath_p.h1
-rw-r--r--src/gui/painting/qpathclipper_p.h6
-rw-r--r--src/gui/painting/qpdf.cpp35
-rw-r--r--src/gui/painting/qpdf_p.h1
-rw-r--r--src/gui/painting/qpdfwriter.cpp2
-rw-r--r--src/gui/painting/qpen.cpp11
-rw-r--r--src/gui/painting/qplatformbackingstore.cpp2
-rw-r--r--src/gui/painting/qregion.cpp2
-rw-r--r--src/gui/painting/qstroker.cpp56
-rw-r--r--src/gui/painting/qstroker_p.h1
-rw-r--r--src/gui/painting/qt_attribution.json2
-rw-r--r--src/gui/painting/qtextureglyphcache.cpp6
-rw-r--r--src/gui/painting/qtextureglyphcache_p.h2
-rw-r--r--src/gui/painting/qtransform.cpp23
-rw-r--r--src/gui/painting/qtriangulatingstroker.cpp1
43 files changed, 611 insertions, 289 deletions
diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri
index c68e8d6e9a..bb0fc0ffba 100644
--- a/src/gui/painting/painting.pri
+++ b/src/gui/painting/painting.pri
@@ -146,7 +146,7 @@ gcc:equals(QT_GCC_MAJOR_VERSION, 5) {
NEON_HEADERS += painting/qdrawhelper_neon_p.h
}
!uikit:!win32:contains(QT_ARCH, "arm"): CONFIG += no_clang_integrated_as
-!android:!uikit:!win32:!integrity:!contains(QT_ARCH, "arm64") {
+!macos:!android:!uikit:!win32:!integrity:!contains(QT_ARCH, "arm64") {
NEON_ASM += ../3rdparty/pixman/pixman-arm-neon-asm.S painting/qdrawhelper_neon_asm.S
DEFINES += ENABLE_PIXMAN_DRAWHELPERS
}
diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp
index 2147d9d61d..ebce163ed3 100644
--- a/src/gui/painting/qbackingstore.cpp
+++ b/src/gui/painting/qbackingstore.cpp
@@ -249,8 +249,18 @@ void QBackingStore::flush(const QRegion &region, QWindow *window, const QPoint &
Q_ASSERT(window == topLevelWindow || topLevelWindow->isAncestorOf(window, QWindow::ExcludeTransients));
- handle()->flush(window, QHighDpi::toNativeLocalRegion(region, window),
- QHighDpi::toNativeLocalPosition(offset, window));
+ QRegion nativeRegion = QHighDpi::toNativeLocalRegion(region, window);
+ QPoint nativeOffset;
+ if (!offset.isNull()) {
+ nativeOffset = QHighDpi::toNativeLocalPosition(offset, window);
+ // Under fractional DPR, rounding of region and offset may accumulate to an off-by-one
+ QPoint topLeft = region.boundingRect().topLeft() + offset;
+ QPoint nativeTopLeft = QHighDpi::toNativeLocalPosition(topLeft, window);
+ QPoint diff = nativeTopLeft - (nativeRegion.boundingRect().topLeft() + nativeOffset);
+ Q_ASSERT(qMax(qAbs(diff.x()), qAbs(diff.y())) <= 1);
+ nativeRegion.translate(diff);
+ }
+ handle()->flush(window, nativeRegion, nativeOffset);
}
/*!
@@ -320,32 +330,34 @@ bool QBackingStore::hasStaticContents() const
void Q_GUI_EXPORT qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset)
{
// make sure we don't detach
- uchar *mem = const_cast<uchar*>(const_cast<const QImage &>(img).bits());
+ uchar *mem = const_cast<uchar*>(img.constBits());
int lineskip = img.bytesPerLine();
int depth = img.depth() >> 3;
const QRect imageRect(0, 0, img.width(), img.height());
- const QRect r = rect & imageRect & imageRect.translated(-offset);
- const QPoint p = rect.topLeft() + offset;
-
- if (r.isEmpty())
+ const QRect sourceRect = rect.intersected(imageRect).intersected(imageRect.translated(-offset));
+ if (sourceRect.isEmpty())
return;
+ const QRect destRect = sourceRect.translated(offset);
+ Q_ASSERT_X(imageRect.contains(destRect), "qt_scrollRectInImage",
+ "The sourceRect should already account for clipping, both pre and post scroll");
+
const uchar *src;
uchar *dest;
- if (r.top() < p.y()) {
- src = mem + r.bottom() * lineskip + r.left() * depth;
- dest = mem + (p.y() + r.height() - 1) * lineskip + p.x() * depth;
+ if (sourceRect.top() < destRect.top()) {
+ src = mem + sourceRect.bottom() * lineskip + sourceRect.left() * depth;
+ dest = mem + (destRect.top() + sourceRect.height() - 1) * lineskip + destRect.left() * depth;
lineskip = -lineskip;
} else {
- src = mem + r.top() * lineskip + r.left() * depth;
- dest = mem + p.y() * lineskip + p.x() * depth;
+ src = mem + sourceRect.top() * lineskip + sourceRect.left() * depth;
+ dest = mem + destRect.top() * lineskip + destRect.left() * depth;
}
- const int w = r.width();
- int h = r.height();
+ const int w = sourceRect.width();
+ int h = sourceRect.height();
const int bytes = w * depth;
// overlapping segments?
diff --git a/src/gui/painting/qblendfunctions_p.h b/src/gui/painting/qblendfunctions_p.h
index 080da98ec4..6997d62b3c 100644
--- a/src/gui/painting/qblendfunctions_p.h
+++ b/src/gui/painting/qblendfunctions_p.h
@@ -246,25 +246,32 @@ void qt_transform_image_rasterize(DestT *destPixels, int dbpl,
int dudx, int dvdx, int dudy, int dvdy, int u0, int v0,
Blender blender)
{
- int fromY = qMax(qRound(topY), clip.top());
- int toY = qMin(qRound(bottomY), clip.top() + clip.height());
+ qint64 fromY = qMax(qRound(topY), clip.top());
+ qint64 toY = qMin(qRound(bottomY), clip.top() + clip.height());
if (fromY >= toY)
return;
qreal leftSlope = (bottomLeft.x - topLeft.x) / (bottomLeft.y - topLeft.y);
qreal rightSlope = (bottomRight.x - topRight.x) / (bottomRight.y - topRight.y);
- int dx_l = int(leftSlope * 0x10000);
- int dx_r = int(rightSlope * 0x10000);
- int x_l = int((topLeft.x + (qreal(0.5) + fromY - topLeft.y) * leftSlope + qreal(0.5)) * 0x10000);
- int x_r = int((topRight.x + (qreal(0.5) + fromY - topRight.y) * rightSlope + qreal(0.5)) * 0x10000);
-
- int fromX, toX, x1, x2, u, v, i, ii;
+ qint64 dx_l = qint64(leftSlope * 0x10000);
+ qint64 dx_r = qint64(rightSlope * 0x10000);
+ qint64 x_l = qint64((topLeft.x + (qreal(0.5) + fromY - topLeft.y) * leftSlope + qreal(0.5)) * 0x10000);
+ qint64 x_r = qint64((topRight.x + (qreal(0.5) + fromY - topRight.y) * rightSlope + qreal(0.5)) * 0x10000);
+
+ qint64 sourceRectTop = qint64(sourceRect.top());
+ qint64 sourceRectLeft = qint64(sourceRect.left());
+ qint64 sourceRectWidth = qint64(sourceRect.width());
+ qint64 sourceRectHeight = qint64(sourceRect.height());
+ qint64 clipLeft = qint64(clip.left());
+ qint64 clipWidth = qint64(clip.width());
+
+ qint64 fromX, toX, x1, x2, u, v, i, ii;
DestT *line;
- for (int y = fromY; y < toY; ++y) {
+ for (qint64 y = fromY; y < toY; ++y) {
line = reinterpret_cast<DestT *>(reinterpret_cast<uchar *>(destPixels) + y * dbpl);
- fromX = qMax(x_l >> 16, clip.left());
- toX = qMin(x_r >> 16, clip.left() + clip.width());
+ fromX = qMax(x_l >> 16, clipLeft);
+ toX = qMin(x_r >> 16, clipLeft + clipWidth);
if (fromX < toX) {
// Because of rounding, we can get source coordinates outside the source image.
// Clamp these coordinates to the source rect to avoid segmentation fault and
@@ -275,10 +282,10 @@ void qt_transform_image_rasterize(DestT *destPixels, int dbpl,
u = x1 * dudx + y * dudy + u0;
v = x1 * dvdx + y * dvdy + v0;
for (; x1 < toX; ++x1) {
- int uu = u >> 16;
- int vv = v >> 16;
- if (uu >= sourceRect.left() && uu < sourceRect.left() + sourceRect.width()
- && vv >= sourceRect.top() && vv < sourceRect.top() + sourceRect.height()) {
+ qint64 uu = u >> 16;
+ qint64 vv = v >> 16;
+ if (uu >= sourceRectLeft && uu < sourceRectLeft + sourceRectWidth
+ && vv >= sourceRectTop && vv < sourceRectTop + sourceRectHeight) {
break;
}
u += dudx;
@@ -290,10 +297,10 @@ void qt_transform_image_rasterize(DestT *destPixels, int dbpl,
u = (x2 - 1) * dudx + y * dudy + u0;
v = (x2 - 1) * dvdx + y * dvdy + v0;
for (; x2 > x1; --x2) {
- int uu = u >> 16;
- int vv = v >> 16;
- if (uu >= sourceRect.left() && uu < sourceRect.left() + sourceRect.width()
- && vv >= sourceRect.top() && vv < sourceRect.top() + sourceRect.height()) {
+ qint64 uu = u >> 16;
+ qint64 vv = v >> 16;
+ if (uu >= sourceRectLeft && uu < sourceRectLeft + sourceRectWidth
+ && vv >= sourceRectTop && vv < sourceRectTop + sourceRectHeight) {
break;
}
u -= dudx;
@@ -308,8 +315,8 @@ void qt_transform_image_rasterize(DestT *destPixels, int dbpl,
// Beginning of the scan line, with per-pixel checks.
i = x1 - fromX;
while (i) {
- int uu = qBound(sourceRect.left(), u >> 16, sourceRect.left() + sourceRect.width() - 1);
- int vv = qBound(sourceRect.top(), v >> 16, sourceRect.top() + sourceRect.height() - 1);
+ qint64 uu = qBound(sourceRectLeft, u >> 16, sourceRectLeft + sourceRectWidth - 1);
+ qint64 vv = qBound(sourceRectTop, v >> 16, sourceRectTop + sourceRectHeight - 1);
blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + vv * sbpl)[uu]);
u += dudx;
v += dvdx;
@@ -348,8 +355,8 @@ void qt_transform_image_rasterize(DestT *destPixels, int dbpl,
// End of the scan line, with per-pixel checks.
i = toX - x2;
while (i) {
- int uu = qBound(sourceRect.left(), u >> 16, sourceRect.left() + sourceRect.width() - 1);
- int vv = qBound(sourceRect.top(), v >> 16, sourceRect.top() + sourceRect.height() - 1);
+ qint64 uu = qBound(sourceRectLeft, u >> 16, sourceRectLeft + sourceRectWidth - 1);
+ qint64 vv = qBound(sourceRectTop, v >> 16, sourceRectTop + sourceRectHeight - 1);
blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + vv * sbpl)[uu]);
u += dudx;
v += dvdx;
diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp
index 89f3374ea1..08d7055b82 100644
--- a/src/gui/painting/qbrush.cpp
+++ b/src/gui/painting/qbrush.cpp
@@ -1354,7 +1354,176 @@ QGradient::QGradient()
\since 5.12
This enum specifies a set of predefined presets for QGradient,
- based on the gradients from https://webgradients.com/.
+ based on the gradients from \l {https://webgradients.com/}.
+
+ \value WarmFlame
+ \value NightFade
+ \value SpringWarmth
+ \value JuicyPeach
+ \value YoungPassion
+ \value LadyLips
+ \value SunnyMorning
+ \value RainyAshville
+ \value FrozenDreams
+ \value WinterNeva
+ \value DustyGrass
+ \value TemptingAzure
+ \value HeavyRain
+ \value AmyCrisp
+ \value MeanFruit
+ \value DeepBlue
+ \value RipeMalinka
+ \value CloudyKnoxville
+ \value MalibuBeach
+ \value NewLife
+ \value TrueSunset
+ \value MorpheusDen
+ \value RareWind
+ \value NearMoon
+ \value WildApple
+ \value SaintPetersburg
+ \value PlumPlate
+ \value EverlastingSky
+ \value HappyFisher
+ \value Blessing
+ \value SharpeyeEagle
+ \value LadogaBottom
+ \value LemonGate
+ \value ItmeoBranding
+ \value ZeusMiracle
+ \value OldHat
+ \value StarWine
+ \value HappyAcid
+ \value AwesomePine
+ \value NewYork
+ \value ShyRainbow
+ \value MixedHopes
+ \value FlyHigh
+ \value StrongBliss
+ \value FreshMilk
+ \value SnowAgain
+ \value FebruaryInk
+ \value KindSteel
+ \value SoftGrass
+ \value GrownEarly
+ \value SharpBlues
+ \value ShadyWater
+ \value DirtyBeauty
+ \value GreatWhale
+ \value TeenNotebook
+ \value PoliteRumors
+ \value SweetPeriod
+ \value WideMatrix
+ \value SoftCherish
+ \value RedSalvation
+ \value BurningSpring
+ \value NightParty
+ \value SkyGlider
+ \value HeavenPeach
+ \value PurpleDivision
+ \value AquaSplash
+ \value SpikyNaga
+ \value LoveKiss
+ \value CleanMirror
+ \value PremiumDark
+ \value ColdEvening
+ \value CochitiLake
+ \value SummerGames
+ \value PassionateBed
+ \value MountainRock
+ \value DesertHump
+ \value JungleDay
+ \value PhoenixStart
+ \value OctoberSilence
+ \value FarawayRiver
+ \value AlchemistLab
+ \value OverSun
+ \value PremiumWhite
+ \value MarsParty
+ \value EternalConstance
+ \value JapanBlush
+ \value SmilingRain
+ \value CloudyApple
+ \value BigMango
+ \value HealthyWater
+ \value AmourAmour
+ \value RiskyConcrete
+ \value StrongStick
+ \value ViciousStance
+ \value PaloAlto
+ \value HappyMemories
+ \value MidnightBloom
+ \value Crystalline
+ \value PartyBliss
+ \value ConfidentCloud
+ \value LeCocktail
+ \value RiverCity
+ \value FrozenBerry
+ \value ChildCare
+ \value FlyingLemon
+ \value NewRetrowave
+ \value HiddenJaguar
+ \value AboveTheSky
+ \value Nega
+ \value DenseWater
+ \value Seashore
+ \value MarbleWall
+ \value CheerfulCaramel
+ \value NightSky
+ \value MagicLake
+ \value YoungGrass
+ \value ColorfulPeach
+ \value GentleCare
+ \value PlumBath
+ \value HappyUnicorn
+ \value AfricanField
+ \value SolidStone
+ \value OrangeJuice
+ \value GlassWater
+ \value NorthMiracle
+ \value FruitBlend
+ \value MillenniumPine
+ \value HighFlight
+ \value MoleHall
+ \value SpaceShift
+ \value ForestInei
+ \value RoyalGarden
+ \value RichMetal
+ \value JuicyCake
+ \value SmartIndigo
+ \value SandStrike
+ \value NorseBeauty
+ \value AquaGuidance
+ \value SunVeggie
+ \value SeaLord
+ \value BlackSea
+ \value GrassShampoo
+ \value LandingAircraft
+ \value WitchDance
+ \value SleeplessNight
+ \value AngelCare
+ \value CrystalRiver
+ \value SoftLipstick
+ \value SaltMountain
+ \value PerfectWhite
+ \value FreshOasis
+ \value StrictNovember
+ \value MorningSalad
+ \value DeepRelief
+ \value SeaStrike
+ \value NightCall
+ \value SupremeSky
+ \value LightBlue
+ \value MindCrawl
+ \value LilyMeadow
+ \value SugarLollipop
+ \value SweetDessert
+ \value MagicRay
+ \value TeenParty
+ \value FrozenHeat
+ \value GagarinView
+ \value FabledSunset
+ \value PerfectBlue
*/
/*!
@@ -2469,3 +2638,5 @@ void QConicalGradient::setAngle(qreal angle)
#undef Q_DUMMY_ACCESSOR
QT_END_NAMESPACE
+
+#include "moc_qbrush.cpp"
diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp
index 7612f183bc..2352d6e18a 100644
--- a/src/gui/painting/qcolor.cpp
+++ b/src/gui/painting/qcolor.cpp
@@ -3203,7 +3203,7 @@ const uint qt_inv_premul_factor[256] = {
Returns the ARGB quadruplet (255, \a{r}, \a{g}, \a{b}).
- \sa qRgba(), qRed(), qGreen(), qBlue()
+ \sa qRgba(), qRed(), qGreen(), qBlue(), qAlpha()
*/
/*!
@@ -3212,7 +3212,7 @@ const uint qt_inv_premul_factor[256] = {
Returns the ARGB quadruplet (\a{a}, \a{r}, \a{g}, \a{b}).
- \sa qRgb(), qRed(), qGreen(), qBlue()
+ \sa qRgb(), qRed(), qGreen(), qBlue(), qAlpha()
*/
/*!
diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp
index 930e5aec87..dd30c64640 100644
--- a/src/gui/painting/qcolorspace.cpp
+++ b/src/gui/painting/qcolorspace.cpp
@@ -46,6 +46,7 @@
#include "qcolortransform_p.h"
#include "qicc_p.h"
+#include <qatomic.h>
#include <qmath.h>
#include <qtransform.h>
@@ -55,6 +56,18 @@ QT_BEGIN_NAMESPACE
QBasicMutex QColorSpacePrivate::s_lutWriteLock;
+static QAtomicPointer<QColorSpacePrivate> s_predefinedColorspacePrivates[QColorSpace::ProPhotoRgb] = {};
+static void cleanupPredefinedColorspaces()
+{
+ for (QAtomicPointer<QColorSpacePrivate> &ptr : s_predefinedColorspacePrivates) {
+ QColorSpacePrivate *prv = ptr.fetchAndStoreAcquire(nullptr);
+ if (prv && !prv->ref.deref())
+ delete prv;
+ }
+}
+
+Q_DESTRUCTOR_FUNCTION(cleanupPredefinedColorspaces)
+
QColorSpacePrimaries::QColorSpacePrimaries(QColorSpace::Primaries primaries)
{
switch (primaries) {
@@ -133,13 +146,17 @@ QColorMatrix QColorSpacePrimaries::toXyzMatrix() const
QColorVector srcCone = abrad.map(wXyz);
QColorVector dstCone = abrad.map(wXyzD50);
- QColorMatrix wToD50 = { { dstCone.x / srcCone.x, 0, 0 },
- { 0, dstCone.y / srcCone.y, 0 },
- { 0, 0, dstCone.z / srcCone.z } };
+ if (srcCone.x && srcCone.y && srcCone.z) {
+ QColorMatrix wToD50 = { { dstCone.x / srcCone.x, 0, 0 },
+ { 0, dstCone.y / srcCone.y, 0 },
+ { 0, 0, dstCone.z / srcCone.z } };
- QColorMatrix chromaticAdaptation = abradinv * (wToD50 * abrad);
- toXyz = chromaticAdaptation * toXyz;
+ QColorMatrix chromaticAdaptation = abradinv * (wToD50 * abrad);
+ toXyz = chromaticAdaptation * toXyz;
+ } else {
+ toXyz.r = {0, 0, 0}; // set to invalid value
+ }
}
return toXyz;
@@ -185,9 +202,9 @@ QColorSpacePrivate::QColorSpacePrivate(QColorSpace::NamedColorSpace namedColorSp
initialize();
}
-QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, QColorSpace::TransferFunction fun, float gamma)
+QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, QColorSpace::TransferFunction transferFunction, float gamma)
: primaries(primaries)
- , transferFunction(fun)
+ , transferFunction(transferFunction)
, gamma(gamma)
{
identifyColorSpace();
@@ -195,10 +212,10 @@ QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, QColorS
}
QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries,
- QColorSpace::TransferFunction fun,
+ QColorSpace::TransferFunction transferFunction,
float gamma)
: primaries(QColorSpace::Primaries::Custom)
- , transferFunction(fun)
+ , transferFunction(transferFunction)
, gamma(gamma)
{
Q_ASSERT(primaries.areValid());
@@ -318,6 +335,7 @@ void QColorSpacePrivate::setTransferFunction()
}
trc[1] = trc[0];
trc[2] = trc[0];
+ lut.generated.storeRelease(0);
}
QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpacePrivate *out) const
@@ -426,22 +444,28 @@ QColorSpace::QColorSpace(NamedColorSpace namedColorSpace)
qWarning() << "QColorSpace attempted constructed from invalid QColorSpace::NamedColorSpace: " << int(namedColorSpace);
return;
}
- static QColorSpacePrivate *predefinedColorspacePrivates[QColorSpace::ProPhotoRgb + 1];
- if (!predefinedColorspacePrivates[namedColorSpace]) {
- predefinedColorspacePrivates[namedColorSpace] = new QColorSpacePrivate(namedColorSpace);
- predefinedColorspacePrivates[namedColorSpace]->ref.ref();
+ // The defined namespaces start at 1:
+ auto &atomicRef = s_predefinedColorspacePrivates[static_cast<int>(namedColorSpace) - 1];
+ QColorSpacePrivate *cspriv = atomicRef.loadAcquire();
+ if (!cspriv) {
+ auto *tmp = new QColorSpacePrivate(namedColorSpace);
+ tmp->ref.ref();
+ if (atomicRef.testAndSetOrdered(nullptr, tmp, cspriv))
+ cspriv = tmp;
+ else
+ delete tmp;
}
- d_ptr = predefinedColorspacePrivates[namedColorSpace];
+ d_ptr = cspriv;
d_ptr->ref.ref();
Q_ASSERT(isValid());
}
/*!
- Creates a custom color space with the primaries \a primaries, using the transfer function \a fun and
+ Creates a custom color space with the primaries \a primaries, using the transfer function \a transferFunction and
optionally \a gamma.
*/
-QColorSpace::QColorSpace(QColorSpace::Primaries primaries, QColorSpace::TransferFunction fun, float gamma)
- : d_ptr(new QColorSpacePrivate(primaries, fun, gamma))
+QColorSpace::QColorSpace(QColorSpace::Primaries primaries, QColorSpace::TransferFunction transferFunction, float gamma)
+ : d_ptr(new QColorSpacePrivate(primaries, transferFunction, gamma))
{
d_ptr->ref.ref();
}
@@ -458,11 +482,11 @@ QColorSpace::QColorSpace(QColorSpace::Primaries primaries, float gamma)
/*!
Creates a custom colorspace with a primaries based on the chromaticities of the primary colors \a whitePoint,
- \a redPoint, \a greenPoint and \a bluePoint, and using the transfer function \a fun and optionally \a gamma.
+ \a redPoint, \a greenPoint and \a bluePoint, and using the transfer function \a transferFunction and optionally \a gamma.
*/
QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
const QPointF &greenPoint, const QPointF &bluePoint,
- QColorSpace::TransferFunction fun, float gamma)
+ QColorSpace::TransferFunction transferFunction, float gamma)
{
QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint);
if (!primaries.areValid()) {
@@ -470,7 +494,7 @@ QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
d_ptr = nullptr;
return;
}
- d_ptr = new QColorSpacePrivate(primaries, fun, gamma);
+ d_ptr = new QColorSpacePrivate(primaries, transferFunction, gamma);
d_ptr->ref.ref();
}
@@ -824,3 +848,5 @@ QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace)
#endif
QT_END_NAMESPACE
+
+#include "moc_qcolorspace.cpp"
diff --git a/src/gui/painting/qcolorspace.h b/src/gui/painting/qcolorspace.h
index 08c9944301..852ade9ab7 100644
--- a/src/gui/painting/qcolorspace.h
+++ b/src/gui/painting/qcolorspace.h
@@ -82,11 +82,11 @@ public:
QColorSpace();
QColorSpace(NamedColorSpace namedColorSpace);
- QColorSpace(Primaries primaries, TransferFunction fun, float gamma = 0.0f);
+ QColorSpace(Primaries primaries, TransferFunction transferFunction, float gamma = 0.0f);
QColorSpace(Primaries primaries, float gamma);
QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
const QPointF &greenPoint, const QPointF &bluePoint,
- TransferFunction fun, float gamma = 0.0f);
+ TransferFunction transferFunction, float gamma = 0.0f);
~QColorSpace();
QColorSpace(const QColorSpace &colorSpace);
diff --git a/src/gui/painting/qcolorspace_p.h b/src/gui/painting/qcolorspace_p.h
index e7add19ed3..094fdb0d37 100644
--- a/src/gui/painting/qcolorspace_p.h
+++ b/src/gui/painting/qcolorspace_p.h
@@ -91,8 +91,8 @@ class QColorSpacePrivate : public QSharedData
public:
QColorSpacePrivate();
QColorSpacePrivate(QColorSpace::NamedColorSpace namedColorSpace);
- QColorSpacePrivate(QColorSpace::Primaries primaries, QColorSpace::TransferFunction fun, float gamma);
- QColorSpacePrivate(const QColorSpacePrimaries &primaries, QColorSpace::TransferFunction fun, float gamma);
+ QColorSpacePrivate(QColorSpace::Primaries primaries, QColorSpace::TransferFunction transferFunction, float gamma);
+ QColorSpacePrivate(const QColorSpacePrimaries &primaries, QColorSpace::TransferFunction transferFunction, float gamma);
QColorSpacePrivate(const QColorSpacePrivate &other) = default;
// named different from get to avoid accidental detachs
diff --git a/src/gui/painting/qcolortransfertable_p.h b/src/gui/painting/qcolortransfertable_p.h
index c8b2f7bd92..d6b514193d 100644
--- a/src/gui/painting/qcolortransfertable_p.h
+++ b/src/gui/painting/qcolortransfertable_p.h
@@ -69,43 +69,57 @@ public:
QColorTransferTable(uint32_t size, const QVector<uint8_t> &table) noexcept
: m_tableSize(size)
, m_table8(table)
- { }
+ {
+ Q_ASSERT(size <= uint32_t(table.count()));
+ }
QColorTransferTable(uint32_t size, const QVector<uint16_t> &table) noexcept
: m_tableSize(size)
, m_table16(table)
- { }
+ {
+ Q_ASSERT(size <= uint32_t(table.count()));
+ }
- bool isValid() const
+ bool isEmpty() const
{
+ return m_tableSize == 0;
+ }
+
+ bool checkValidity() const
+ {
+ if (isEmpty())
+ return true;
+ // Only one table can be set
+ if (!m_table8.isEmpty() && !m_table16.isEmpty())
+ return false;
+ // At least 2 elements
if (m_tableSize < 2)
return false;
-
-#if !defined(QT_NO_DEBUG)
// The table must describe an injective curve:
if (!m_table8.isEmpty()) {
uint8_t val = 0;
for (uint i = 0; i < m_tableSize; ++i) {
- Q_ASSERT(m_table8[i] >= val);
+ if (m_table8[i] < val)
+ return false;
val = m_table8[i];
}
}
if (!m_table16.isEmpty()) {
uint16_t val = 0;
for (uint i = 0; i < m_tableSize; ++i) {
- Q_ASSERT(m_table16[i] >= val);
+ if (m_table16[i] < val)
+ return false;
val = m_table16[i];
}
}
-#endif
- return !m_table8.isEmpty() || !m_table16.isEmpty();
+ return true;
}
float apply(float x) const
{
x = std::min(std::max(x, 0.0f), 1.0f);
x *= m_tableSize - 1;
- uint32_t lo = (int)std::floor(x);
- uint32_t hi = std::min(lo + 1, m_tableSize);
+ uint32_t lo = static_cast<uint32_t>(std::floor(x));
+ uint32_t hi = std::min(lo + 1, m_tableSize - 1);
float frac = x - lo;
if (!m_table16.isEmpty())
return (m_table16[lo] * (1.0f - frac) + m_table16[hi] * frac) * (1.0f/65535.0f);
@@ -124,7 +138,7 @@ public:
return 1.0f;
if (!m_table16.isEmpty()) {
float v = x * 65535.0f;
- uint i = std::floor(resultLargerThan * (m_tableSize - 1)) + 1;
+ uint32_t i = std::floor(resultLargerThan * (m_tableSize - 1));
for ( ; i < m_tableSize; ++i) {
if (m_table16[i] > v)
break;
@@ -133,14 +147,14 @@ public:
return 1.0f;
float y1 = m_table16[i - 1];
float y2 = m_table16[i];
- Q_ASSERT(x >= y1 && x < y2);
+ Q_ASSERT(v >= y1 && v <= y2);
float fr = (v - y1) / (y2 - y1);
return (i + fr) * (1.0f / (m_tableSize - 1));
}
if (!m_table8.isEmpty()) {
float v = x * 255.0f;
- uint i = std::floor(resultLargerThan * (m_tableSize - 1)) + 1;
+ uint32_t i = std::floor(resultLargerThan * (m_tableSize - 1));
for ( ; i < m_tableSize; ++i) {
if (m_table8[i] > v)
break;
@@ -149,7 +163,7 @@ public:
return 1.0f;
float y1 = m_table8[i - 1];
float y2 = m_table8[i];
- Q_ASSERT(x >= y1 && x < y2);
+ Q_ASSERT(v >= y1 && v <= y2);
float fr = (v - y1) / (y2 - y1);
return (i + fr) * (1.0f / (m_tableSize - 1));
}
@@ -158,8 +172,9 @@ public:
bool asColorTransferFunction(QColorTransferFunction *transferFn)
{
- Q_ASSERT(isValid());
Q_ASSERT(transferFn);
+ if (m_tableSize < 2)
+ return false;
if (!m_table8.isEmpty() && (m_table8[0] != 0 || m_table8[m_tableSize - 1] != 255))
return false;
if (!m_table16.isEmpty() && (m_table16[0] != 0 || m_table16[m_tableSize - 1] != 65535))
@@ -221,13 +236,13 @@ inline bool operator!=(const QColorTransferTable &t1, const QColorTransferTable
if (t1.m_table16.isEmpty() != t2.m_table16.isEmpty())
return true;
if (!t1.m_table8.isEmpty()) {
- for (quint32 i = 0; i < t1.m_tableSize; ++i) {
+ for (uint32_t i = 0; i < t1.m_tableSize; ++i) {
if (t1.m_table8[i] != t2.m_table8[i])
return true;
}
}
if (!t1.m_table16.isEmpty()) {
- for (quint32 i = 0; i < t1.m_tableSize; ++i) {
+ for (uint32_t i = 0; i < t1.m_tableSize; ++i) {
if (t1.m_table16[i] != t2.m_table16[i])
return true;
}
diff --git a/src/gui/painting/qcolortrc_p.h b/src/gui/painting/qcolortrc_p.h
index 3ef9d442fc..058be3c7ce 100644
--- a/src/gui/painting/qcolortrc_p.h
+++ b/src/gui/painting/qcolortrc_p.h
@@ -114,7 +114,7 @@ public:
if (x >= 0.0f && x <= 1.0f)
return applyInverse(x);
if (m_type == Type::Function)
- return std::copysign(applyInverse(x), x);
+ return std::copysign(applyInverse(std::abs(x)), x);
if (m_type == Type::Table)
return x < 0.0f ? 0.0f : 1.0f;
return x;
diff --git a/src/gui/painting/qcolortrclut_p.h b/src/gui/painting/qcolortrclut_p.h
index 76a6a60803..24fd522e6c 100644
--- a/src/gui/painting/qcolortrclut_p.h
+++ b/src/gui/painting/qcolortrclut_p.h
@@ -118,6 +118,7 @@ public:
return QRgba64::fromRgba64(r, g, b, qAlpha(rgb32) * 257);
#endif
}
+ QRgba64 toLinear64(QRgba64) const = delete;
QRgb toLinear(QRgb rgb32) const
{
diff --git a/src/gui/painting/qcompositionfunctions.cpp b/src/gui/painting/qcompositionfunctions.cpp
index ced213e36d..aa3f148934 100644
--- a/src/gui/painting/qcompositionfunctions.cpp
+++ b/src/gui/painting/qcompositionfunctions.cpp
@@ -1045,12 +1045,12 @@ private:
static inline int mix_alpha(int da, int sa)
{
- return 255 - ((255 - sa) * (255 - da) >> 8);
+ return 255 - qt_div_255((255 - sa) * (255 - da));
}
static inline uint mix_alpha_rgb64(uint da, uint sa)
{
- return 65535 - ((65535 - sa) * (65535 - da) >> 16);
+ return 65535U - qt_div_65535((65535U - sa) * (65535U - da));
}
/*
@@ -1337,7 +1337,7 @@ static inline void comp_func_Screen_impl(uint *Q_DECL_RESTRICT dest, const uint
int da = qAlpha(d);
int sa = qAlpha(s);
-#define OP(a, b) 255 - (((255-a) * (255-b)) >> 8)
+#define OP(a, b) 255 - qt_div_255((255-a) * (255-b))
int r = OP( qRed(d), qRed(s));
int b = OP( qBlue(d), qBlue(s));
int g = OP(qGreen(d), qGreen(s));
@@ -1367,7 +1367,7 @@ static inline void comp_func_Screen_impl(QRgba64 *Q_DECL_RESTRICT dest, const QR
uint da = d.alpha();
uint sa = s.alpha();
-#define OP(a, b) 65535 - (((65535-a) * (65535-b)) >> 16)
+#define OP(a, b) 65535U - qt_div_65535((65535U-a) * (65535U-b))
uint r = OP( d.red(), s.red());
uint b = OP( d.blue(), s.blue());
uint g = OP(d.green(), s.green());
diff --git a/src/gui/painting/qcosmeticstroker.cpp b/src/gui/painting/qcosmeticstroker.cpp
index b636f0739d..8501bd6989 100644
--- a/src/gui/painting/qcosmeticstroker.cpp
+++ b/src/gui/painting/qcosmeticstroker.cpp
@@ -101,7 +101,7 @@ struct Dasher {
offset += stroker->patternLength;
dashIndex = 0;
- while (offset>= pattern[dashIndex])
+ while (dashIndex < stroker->patternSize - 1 && offset>= pattern[dashIndex])
++dashIndex;
// qDebug() << " dasher" << offset/64. << reverse << dashIndex;
@@ -250,7 +250,7 @@ void QCosmeticStroker::setup()
strokeSelection |= AntiAliased;
const QVector<qreal> &penPattern = state->lastPen.dashPattern();
- if (penPattern.isEmpty()) {
+ if (penPattern.isEmpty() || penPattern.size() > 1024) {
Q_ASSERT(!pattern && !reversePattern);
pattern = nullptr;
reversePattern = nullptr;
@@ -263,12 +263,12 @@ void QCosmeticStroker::setup()
patternLength = 0;
for (int i = 0; i < patternSize; ++i) {
- patternLength += (int) qMax(1. , penPattern.at(i)*64.);
+ patternLength += (int)qBound(1., penPattern.at(i) * 64, 65536.);
pattern[i] = patternLength;
}
patternLength = 0;
for (int i = 0; i < patternSize; ++i) {
- patternLength += (int) qMax(1., penPattern.at(patternSize - 1 - i)*64.);
+ patternLength += (int)qBound(1., penPattern.at(patternSize - 1 - i) * 64, 65536.);
reversePattern[i] = patternLength;
}
strokeSelection |= Dashed;
@@ -311,6 +311,8 @@ void QCosmeticStroker::setup()
// returns true if the whole line gets clipped away
bool QCosmeticStroker::clipLine(qreal &x1, qreal &y1, qreal &x2, qreal &y2)
{
+ if (!qIsFinite(x1) || !qIsFinite(y1) || !qIsFinite(x2) || !qIsFinite(y2))
+ return true;
// basic/rough clipping is done in floating point coordinates to avoid
// integer overflow problems.
if (x1 < xmin) {
@@ -365,14 +367,14 @@ bool QCosmeticStroker::clipLine(qreal &x1, qreal &y1, qreal &x2, qreal &y2)
void QCosmeticStroker::drawLine(const QPointF &p1, const QPointF &p2)
{
- if (p1 == p2) {
+ QPointF start = p1 * state->matrix;
+ QPointF end = p2 * state->matrix;
+
+ if (start == end) {
drawPoints(&p1, 1);
return;
}
- QPointF start = p1 * state->matrix;
- QPointF end = p2 * state->matrix;
-
patternOffset = state->lastPen.dashOffset()*64;
lastPixel.x = INT_MIN;
lastPixel.y = INT_MIN;
diff --git a/src/gui/painting/qdatabuffer_p.h b/src/gui/painting/qdatabuffer_p.h
index 181d19da0b..8e519c400c 100644
--- a/src/gui/painting/qdatabuffer_p.h
+++ b/src/gui/painting/qdatabuffer_p.h
@@ -66,7 +66,10 @@ public:
{
capacity = res;
if (res) {
+ QT_WARNING_PUSH
+ QT_WARNING_DISABLE_GCC("-Walloc-size-larger-than=")
buffer = (Type*) malloc(capacity * sizeof(Type));
+ QT_WARNING_POP
Q_CHECK_PTR(buffer);
} else {
buffer = nullptr;
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
index 7b3fffcceb..fe8ff2633b 100644
--- a/src/gui/painting/qdrawhelper.cpp
+++ b/src/gui/painting/qdrawhelper.cpp
@@ -887,9 +887,8 @@ static const uint *QT_FASTCALL fetchGrayscale16ToRGB32(uint *buffer, const uchar
static const QRgba64 *QT_FASTCALL convertGrayscale16ToRGBA64(QRgba64 *buffer, const uint *src, int count,
const QVector<QRgb> *, QDitherInfo *)
{
- const unsigned short *s = reinterpret_cast<const unsigned short *>(src);
for (int i = 0; i < count; ++i)
- buffer[i] = QRgba64::fromRgba64(s[i], s[i], s[i], 65535);
+ buffer[i] = QRgba64::fromRgba64(src[i], src[i], src[i], 65535);
return buffer;
}
@@ -1420,7 +1419,7 @@ static void QT_FASTCALL storeRGB64FromRGB32(uchar *dest, const uint *src, int in
{
QRgba64 *d = reinterpret_cast<QRgba64 *>(dest) + index;
for (int i = 0; i < count; ++i)
- d[i] = QRgba64::fromArgb32(src[i]);
+ d[i] = QRgba64::fromArgb32(src[i] | 0xff000000);
}
static const uint *QT_FASTCALL fetchRGBA64ToARGB32PM(uint *buffer, const uchar *src, int index, int count,
@@ -1432,12 +1431,24 @@ static const uint *QT_FASTCALL fetchRGBA64ToARGB32PM(uint *buffer, const uchar *
return buffer;
}
+template<bool Mask>
static void QT_FASTCALL storeRGBA64FromARGB32PM(uchar *dest, const uint *src, int index, int count,
const QVector<QRgb> *, QDitherInfo *)
{
QRgba64 *d = reinterpret_cast<QRgba64 *>(dest) + index;
- for (int i = 0; i < count; ++i)
+ for (int i = 0; i < count; ++i) {
d[i] = QRgba64::fromArgb32(src[i]).unpremultiplied();
+ if (Mask)
+ d[i].setAlpha(65535);
+ }
+}
+
+static void QT_FASTCALL storeRGBA64FromARGB32(uchar *dest, const uint *src, int index, int count,
+ const QVector<QRgb> *, QDitherInfo *)
+{
+ QRgba64 *d = reinterpret_cast<QRgba64 *>(dest) + index;
+ for (int i = 0; i < count; ++i)
+ d[i] = QRgba64::fromArgb32(src[i]);
}
// Note:
@@ -1524,15 +1535,15 @@ QPixelLayout qPixelLayouts[QImage::NImageFormats] = {
{ false, false, QPixelLayout::BPP64, nullptr,
convertPassThrough, nullptr,
fetchRGB64ToRGB32, fetchPassThrough64,
- storeRGB64FromRGB32, storeRGB64FromRGB32 }, // Format_RGBX64
+ storeRGBA64FromARGB32PM<true>, storeRGB64FromRGB32 }, // Format_RGBX64
{ true, false, QPixelLayout::BPP64, nullptr,
convertARGB32ToARGB32PM, nullptr,
fetchRGBA64ToARGB32PM, fetchRGBA64ToRGBA64PM,
- storeRGBA64FromARGB32PM, storeRGB64FromRGB32 }, // Format_RGBA64
+ storeRGBA64FromARGB32PM<false>, storeRGB64FromRGB32 }, // Format_RGBA64
{ true, true, QPixelLayout::BPP64, nullptr,
convertPassThrough, nullptr,
fetchRGB64ToRGB32, fetchPassThrough64,
- storeRGB64FromRGB32, storeRGB64FromRGB32 }, // Format_RGBA64_Premultiplied
+ storeRGBA64FromARGB32, storeRGB64FromRGB32 }, // Format_RGBA64_Premultiplied
{ false, false, QPixelLayout::BPP16, nullptr,
convertGrayscale16ToRGB32, convertGrayscale16ToRGBA64,
fetchGrayscale16ToRGB32, fetchGrayscale16ToRGBA64,
@@ -4018,6 +4029,7 @@ static inline SourceFetchProc64 getSourceFetch64(TextureBlendType blendType, QIm
#define FIXPT_BITS 8
#define FIXPT_SIZE (1<<FIXPT_BITS)
+#define FIXPT_MAX (INT_MAX >> (FIXPT_BITS + 1))
static uint qt_gradient_pixel_fixed(const QGradientData *data, int fixed_pos)
{
@@ -4114,10 +4126,12 @@ static inline const BlendType * QT_FASTCALL qt_fetch_linear_gradient_template(
const BlendType *end = buffer + length;
if (affine) {
if (inc > qreal(-1e-5) && inc < qreal(1e-5)) {
- GradientBase::memfill(buffer, GradientBase::fetchSingle(data->gradient, int(t * FIXPT_SIZE)), length);
+ if (std::abs(t) < FIXPT_MAX)
+ GradientBase::memfill(buffer, GradientBase::fetchSingle(data->gradient, int(t * FIXPT_SIZE)), length);
+ else
+ GradientBase::memfill(buffer, GradientBase::fetchSingle(data->gradient, t / GRADIENT_STOPTABLE_SIZE), length);
} else {
- if (t+inc*length < qreal(INT_MAX >> (FIXPT_BITS + 1)) &&
- t+inc*length > qreal(INT_MIN >> (FIXPT_BITS + 1))) {
+ if (std::abs(t) < FIXPT_MAX && std::abs(inc) < FIXPT_MAX && std::abs(t + inc * length) < FIXPT_MAX) {
// we can use fixed point math
int t_fixed = int(t * FIXPT_SIZE);
int inc_fixed = int(inc * FIXPT_SIZE);
@@ -6077,7 +6091,7 @@ static inline void alphargbblend_argb32(quint32 *dst, uint coverage, const QRgba
static inline void rgbBlendPixel(QRgba64 &dst, int coverage, QRgba64 slinear, const QColorTrcLut *colorProfile)
{
// Do a gammacorrected RGB alphablend...
- const QRgba64 dlinear = colorProfile ? colorProfile->toLinear64(dst) : dst;
+ const QRgba64 dlinear = colorProfile ? colorProfile->toLinear(dst) : dst;
QRgba64 blend = rgbBlend(dlinear, slinear, coverage);
@@ -6725,7 +6739,7 @@ static void qInitDrawhelperFunctions()
qInitBlendFunctions();
#ifdef __SSE2__
-# ifndef __AVX2__
+# ifndef __haswell__
qt_memfill32 = qt_memfill32_sse2;
qt_memfill64 = qt_memfill64_sse2;
# endif
@@ -6830,7 +6844,7 @@ static void qInitDrawhelperFunctions()
const QVector<QRgb> *, QDitherInfo *);
extern void QT_FASTCALL destStore64ARGB32_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length);
extern void QT_FASTCALL destStore64RGBA8888_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length);
-# ifndef __AVX2__
+# ifndef __haswell__
qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_sse4;
qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_sse4;
qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_sse4;
diff --git a/src/gui/painting/qdrawhelper_sse2.cpp b/src/gui/painting/qdrawhelper_sse2.cpp
index 77b5ab42c5..2f6b7f6bbf 100644
--- a/src/gui/painting/qdrawhelper_sse2.cpp
+++ b/src/gui/painting/qdrawhelper_sse2.cpp
@@ -233,7 +233,7 @@ void QT_FASTCALL comp_func_Source_sse2(uint *dst, const uint *src, int length, u
}
}
-#ifndef __AVX2__
+#ifndef __haswell__
static Q_NEVER_INLINE
void Q_DECL_VECTORCALL qt_memfillXX_aligned(void *dest, __m128i value128, quintptr bytecount)
{
@@ -317,7 +317,7 @@ void qt_memfill32_sse2(quint32 *dest, quint32 value, qsizetype count)
qt_memfillXX_aligned(dest, _mm_set1_epi32(value), count * sizeof(quint32));
}
-#endif // !__AVX2__
+#endif // !__haswell__
void QT_FASTCALL comp_func_solid_Source_sse2(uint *destPixels, int length, uint color, uint const_alpha)
{
diff --git a/src/gui/painting/qdrawhelper_sse4.cpp b/src/gui/painting/qdrawhelper_sse4.cpp
index 68d887ae6d..8e4cfd702d 100644
--- a/src/gui/painting/qdrawhelper_sse4.cpp
+++ b/src/gui/painting/qdrawhelper_sse4.cpp
@@ -45,7 +45,7 @@
QT_BEGIN_NAMESPACE
-#ifndef __AVX2__
+#ifndef __haswell__
template<bool RGBA>
static void convertARGBToARGB32PM_sse4(uint *buffer, const uint *src, int count)
{
@@ -141,7 +141,7 @@ static void convertARGBToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int cou
buffer[i] = QRgba64::fromArgb32(s).premultiplied();
}
}
-#endif // __AVX2__
+#endif // __haswell__
static inline __m128 Q_DECL_VECTORCALL reciprocal_mul_ps(__m128 a, float mul)
{
@@ -326,7 +326,7 @@ static inline void convertARGBFromRGBA64PM_sse4(uint *buffer, const QRgba64 *src
}
}
-#ifndef __AVX2__
+#ifndef __haswell__
void QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, int count, const QVector<QRgb> *)
{
convertARGBToARGB32PM_sse4<false>(buffer, buffer, count);
@@ -378,7 +378,7 @@ const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_sse4(QRgba64 *buffer, const u
convertARGBToRGBA64PM_sse4<true>(buffer, reinterpret_cast<const uint *>(src) + index, count);
return buffer;
}
-#endif // __AVX2__
+#endif // __haswell__
void QT_FASTCALL storeRGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
const QVector<QRgb> *, QDitherInfo *)
diff --git a/src/gui/painting/qicc.cpp b/src/gui/painting/qicc.cpp
index cb9afe3978..4651adbd2a 100644
--- a/src/gui/painting/qicc.cpp
+++ b/src/gui/painting/qicc.cpp
@@ -52,7 +52,7 @@
#include <array>
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(lcIcc, "qt.gui.icc")
+Q_LOGGING_CATEGORY(lcIcc, "qt.gui.icc", QtWarningMsg)
struct ICCProfileHeader
{
@@ -165,7 +165,7 @@ struct XYZTagData : GenericTagData {
struct CurvTagData : GenericTagData {
quint32_be valueCount;
- quint16_be value[1];
+ // followed by curv values: quint16_be[]
};
struct ParaTagData : GenericTagData {
@@ -237,18 +237,20 @@ static bool isValidIccProfile(const ICCProfileHeader &header)
}
if (header.profileClass != uint(ProfileClass::Input)
- && header.profileClass != uint(ProfileClass::Display)) {
- qCWarning(lcIcc, "Unsupported ICC profile class %x", quint32(header.profileClass));
+ && header.profileClass != uint(ProfileClass::Display)
+ && (header.profileClass != uint(ProfileClass::Output)
+ || header.inputColorSpace != uint(ColorSpaceType::Gray))) {
+ qCInfo(lcIcc, "Unsupported ICC profile class 0x%x", quint32(header.profileClass));
return false;
}
if (header.inputColorSpace != uint(ColorSpaceType::Rgb)
&& header.inputColorSpace != uint(ColorSpaceType::Gray)) {
- qCWarning(lcIcc, "Unsupported ICC input color space %x", quint32(header.inputColorSpace));
+ qCInfo(lcIcc, "Unsupported ICC input color space 0x%x", quint32(header.inputColorSpace));
return false;
}
if (header.pcs != 0x58595a20 /* 'XYZ '*/) {
// ### support PCSLAB
- qCWarning(lcIcc, "Unsupported ICC profile connection space %x", quint32(header.pcs));
+ qCInfo(lcIcc, "Unsupported ICC profile connection space 0x%x", quint32(header.pcs));
return false;
}
@@ -468,28 +470,32 @@ bool parseTRC(const QByteArray &data, const TagEntry &tagEntry, QColorTrc &gamma
const GenericTagData trcData = qFromUnaligned<GenericTagData>(data.constData()
+ tagEntry.offset);
if (trcData.type == quint32(Tag::curv)) {
+ Q_STATIC_ASSERT(sizeof(CurvTagData) == 12);
const CurvTagData curv = qFromUnaligned<CurvTagData>(data.constData() + tagEntry.offset);
if (curv.valueCount > (1 << 16))
return false;
if (tagEntry.size - 12 < 2 * curv.valueCount)
return false;
+ const auto valueOffset = tagEntry.offset + sizeof(CurvTagData);
if (curv.valueCount == 0) {
gamma.m_type = QColorTrc::Type::Function;
gamma.m_fun = QColorTransferFunction(); // Linear
} else if (curv.valueCount == 1) {
- float g = curv.value[0] * (1.0f / 256.0f);
+ const quint16 v = qFromBigEndian<quint16>(data.constData() + valueOffset);
gamma.m_type = QColorTrc::Type::Function;
- gamma.m_fun = QColorTransferFunction::fromGamma(g);
+ gamma.m_fun = QColorTransferFunction::fromGamma(v * (1.0f / 256.0f));
} else {
QVector<quint16> tabl;
tabl.resize(curv.valueCount);
static_assert(sizeof(GenericTagData) == 2 * sizeof(quint32_be),
"GenericTagData has padding. The following code is a subject to UB.");
- const auto offset = tagEntry.offset + sizeof(GenericTagData) + sizeof(quint32_be);
- qFromBigEndian<quint16>(data.constData() + offset, curv.valueCount, tabl.data());
+ qFromBigEndian<quint16>(data.constData() + valueOffset, curv.valueCount, tabl.data());
QColorTransferTable table = QColorTransferTable(curv.valueCount, std::move(tabl));
QColorTransferFunction curve;
- if (!table.asColorTransferFunction(&curve)) {
+ if (!table.checkValidity()) {
+ qCWarning(lcIcc) << "Invalid curv table";
+ return false;
+ } else if (!table.asColorTransferFunction(&curve)) {
gamma.m_type = QColorTrc::Type::Table;
gamma.m_table = table;
} else {
@@ -521,6 +527,8 @@ bool parseTRC(const QByteArray &data, const TagEntry &tagEntry, QColorTrc &gamma
return false;
std::array<quint32_be, 3> parameters =
qFromUnaligned<decltype(parameters)>(data.constData() + parametersOffset);
+ if (parameters[1] == 0)
+ return false;
float g = fromFixedS1516(parameters[0]);
float a = fromFixedS1516(parameters[1]);
float b = fromFixedS1516(parameters[2]);
@@ -534,6 +542,8 @@ bool parseTRC(const QByteArray &data, const TagEntry &tagEntry, QColorTrc &gamma
return false;
std::array<quint32_be, 4> parameters =
qFromUnaligned<decltype(parameters)>(data.constData() + parametersOffset);
+ if (parameters[1] == 0)
+ return false;
float g = fromFixedS1516(parameters[0]);
float a = fromFixedS1516(parameters[1]);
float b = fromFixedS1516(parameters[2]);
@@ -643,7 +653,7 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
const ICCProfileHeader header = qFromUnaligned<ICCProfileHeader>(data.constData());
if (!isValidIccProfile(header))
return false; // if failed we already printing a warning
- if (qsizetype(header.profileSize) > data.size()) {
+ if (qsizetype(header.profileSize) > data.size() || qsizetype(header.profileSize) < qsizetype(sizeof(ICCProfileHeader))) {
qCWarning(lcIcc) << "fromIccProfile: failed size sanity 2";
return false;
}
@@ -695,7 +705,7 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
if (!tagIndex.contains(Tag::rXYZ) || !tagIndex.contains(Tag::gXYZ) || !tagIndex.contains(Tag::bXYZ) ||
!tagIndex.contains(Tag::rTRC) || !tagIndex.contains(Tag::gTRC) || !tagIndex.contains(Tag::bTRC) ||
!tagIndex.contains(Tag::wtpt)) {
- qCWarning(lcIcc) << "fromIccProfile: Unsupported ICC profile - not three component matrix based";
+ qCInfo(lcIcc) << "fromIccProfile: Unsupported ICC profile - not three component matrix based";
return false;
}
} else {
@@ -740,7 +750,7 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
QColorVector whitePoint;
if (!parseXyzData(data, tagIndex[Tag::wtpt], whitePoint))
return false;
- if (!qFuzzyCompare(whitePoint.y, 1.0f) || (1.0f + whitePoint.z - whitePoint.x) == 0.0f) {
+ if (!qFuzzyCompare(whitePoint.y, 1.0f) || (1.0f + whitePoint.z + whitePoint.x) == 0.0f) {
qCWarning(lcIcc) << "fromIccProfile: Invalid ICC profile - gray white-point not normalized";
return false;
}
@@ -749,12 +759,12 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
} else {
colorspaceDPtr->primaries = QColorSpace::Primaries::Custom;
// Calculate chromaticity from xyz (assuming y == 1.0f).
- float y = 1.0f / (1.0f + whitePoint.z - whitePoint.x);
+ float y = 1.0f / (1.0f + whitePoint.z + whitePoint.x);
float x = whitePoint.x * y;
QColorSpacePrimaries primaries(QColorSpace::Primaries::SRgb);
primaries.whitePoint = QPointF(x,y);
if (!primaries.areValid()) {
- qCWarning(lcIcc) << "fromIccProfile: Invalid ICC profile - invalid white-point";
+ qCWarning(lcIcc, "fromIccProfile: Invalid ICC profile - invalid white-point(%f, %f)", x, y);
return false;
}
colorspaceDPtr->toXyz = primaries.toXyzMatrix();
diff --git a/src/gui/painting/qimagescale.cpp b/src/gui/painting/qimagescale.cpp
index aac1e20f7b..86057cdd33 100644
--- a/src/gui/painting/qimagescale.cpp
+++ b/src/gui/painting/qimagescale.cpp
@@ -308,7 +308,7 @@ static inline void multithread_pixels_function(QImageScaleInfo *isi, int dh, con
int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16);
segments = std::min(segments, dh);
QThreadPool *threadPool = QThreadPool::globalInstance();
- if (segments > 1 && !threadPool->contains(QThread::currentThread())) {
+ if (segments > 1 && threadPool && !threadPool->contains(QThread::currentThread())) {
QSemaphore semaphore;
int y = 0;
for (int i = 0; i < segments; ++i) {
diff --git a/src/gui/painting/qimagescale_neon.cpp b/src/gui/painting/qimagescale_neon.cpp
index 046e56b419..3c87ac8773 100644
--- a/src/gui/painting/qimagescale_neon.cpp
+++ b/src/gui/painting/qimagescale_neon.cpp
@@ -59,7 +59,7 @@ static inline void multithread_pixels_function(QImageScaleInfo *isi, int dh, con
int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16);
segments = std::min(segments, dh);
QThreadPool *threadPool = QThreadPool::globalInstance();
- if (segments > 1 && !threadPool->contains(QThread::currentThread())) {
+ if (segments > 1 && threadPool && !threadPool->contains(QThread::currentThread())) {
QSemaphore semaphore;
int y = 0;
for (int i = 0; i < segments; ++i) {
diff --git a/src/gui/painting/qimagescale_sse4.cpp b/src/gui/painting/qimagescale_sse4.cpp
index 70cfa08d95..a5c9e68fad 100644
--- a/src/gui/painting/qimagescale_sse4.cpp
+++ b/src/gui/painting/qimagescale_sse4.cpp
@@ -60,7 +60,7 @@ static inline void multithread_pixels_function(QImageScaleInfo *isi, int dh, con
int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16);
segments = std::min(segments, dh);
QThreadPool *threadPool = QThreadPool::globalInstance();
- if (segments > 1 && !threadPool->contains(QThread::currentThread())) {
+ if (segments > 1 && threadPool && !threadPool->contains(QThread::currentThread())) {
QSemaphore semaphore;
int y = 0;
for (int i = 0; i < segments; ++i) {
diff --git a/src/gui/painting/qmemrotate.cpp b/src/gui/painting/qmemrotate.cpp
index 685fbbb37a..b82adb5d35 100644
--- a/src/gui/painting/qmemrotate.cpp
+++ b/src/gui/painting/qmemrotate.cpp
@@ -43,12 +43,11 @@ QT_BEGIN_NAMESPACE
static const int tileSize = 32;
-template <class T>
-static
-inline void qt_memrotate90_tiled(const T *src, int w, int h, int sstride, T *dest, int dstride)
+template<class T>
+static inline void qt_memrotate90_tiled(const T *src, int w, int h, int isstride, T *dest, int idstride)
{
- sstride /= sizeof(T);
- dstride /= sizeof(T);
+ const qsizetype sstride = isstride / sizeof(T);
+ const qsizetype dstride = idstride / sizeof(T);
const int pack = sizeof(quint32) / sizeof(T);
const int unaligned =
@@ -102,11 +101,11 @@ inline void qt_memrotate90_tiled(const T *src, int w, int h, int sstride, T *des
}
}
-template <class T>
-static
-inline void qt_memrotate90_tiled_unpacked(const T *src, int w, int h, int sstride, T *dest,
- int dstride)
+template<class T>
+static inline void qt_memrotate90_tiled_unpacked(const T *src, int w, int h, int isstride, T *dest, int idstride)
{
+ const qsizetype sstride = isstride;
+ const qsizetype dstride = idstride;
const int numTilesX = (w + tileSize - 1) / tileSize;
const int numTilesY = (h + tileSize - 1) / tileSize;
@@ -130,12 +129,11 @@ inline void qt_memrotate90_tiled_unpacked(const T *src, int w, int h, int sstrid
}
}
-template <class T>
-static
-inline void qt_memrotate270_tiled(const T *src, int w, int h, int sstride, T *dest, int dstride)
+template<class T>
+static inline void qt_memrotate270_tiled(const T *src, int w, int h, int isstride, T *dest, int idstride)
{
- sstride /= sizeof(T);
- dstride /= sizeof(T);
+ const qsizetype sstride = isstride / sizeof(T);
+ const qsizetype dstride = idstride / sizeof(T);
const int pack = sizeof(quint32) / sizeof(T);
const int unaligned =
@@ -189,11 +187,11 @@ inline void qt_memrotate270_tiled(const T *src, int w, int h, int sstride, T *de
}
}
-template <class T>
-static
-inline void qt_memrotate270_tiled_unpacked(const T *src, int w, int h, int sstride, T *dest,
- int dstride)
+template<class T>
+static inline void qt_memrotate270_tiled_unpacked(const T *src, int w, int h, int isstride, T *dest, int idstride)
{
+ const qsizetype sstride = isstride;
+ const qsizetype dstride = idstride;
const int numTilesX = (w + tileSize - 1) / tileSize;
const int numTilesY = (h + tileSize - 1) / tileSize;
@@ -245,10 +243,12 @@ inline void qt_memrotate90_template<quint64>(const quint64 *src, int w, int h, i
qt_memrotate90_tiled_unpacked(src, w, h, sstride, dest, dstride);
}
-template <class T>
-static
-inline void qt_memrotate180_template(const T *src, int w, int h, int sstride, T *dest, int dstride)
+template<class T>
+static inline void qt_memrotate180_template(const T *src, int w, int h, int isstride, T *dest, int idstride)
{
+ const qsizetype sstride = isstride;
+ const qsizetype dstride = idstride;
+
const char *s = (const char*)(src) + (h - 1) * sstride;
for (int dy = 0; dy < h; ++dy) {
T *d = reinterpret_cast<T*>((char *)(dest) + dy * dstride);
diff --git a/src/gui/painting/qpagesize.h b/src/gui/painting/qpagesize.h
index 133274760f..5b53eae458 100644
--- a/src/gui/painting/qpagesize.h
+++ b/src/gui/painting/qpagesize.h
@@ -228,7 +228,7 @@ public:
};
QPageSize();
- explicit QPageSize(PageSizeId pageSizeId);
+ /*implicit*/ QPageSize(PageSizeId pageSizeId);
explicit QPageSize(const QSize &pointSize,
const QString &name = QString(),
SizeMatchPolicy matchPolicy = FuzzyMatch);
diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp
index a3e199cec9..38bad9a6b0 100644
--- a/src/gui/painting/qpaintengine_raster.cpp
+++ b/src/gui/painting/qpaintengine_raster.cpp
@@ -560,31 +560,6 @@ void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
QRasterPaintEngineState *s = state();
// FALCON: get rid of this line, see drawImage call below.
s->matrix = matrix;
- QTransform::TransformationType txop = s->matrix.type();
-
- switch (txop) {
-
- case QTransform::TxNone:
- s->flags.int_xform = true;
- break;
-
- case QTransform::TxTranslate:
- s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
- && qreal(int(s->matrix.dy())) == s->matrix.dy();
- break;
-
- case QTransform::TxScale:
- s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
- && qreal(int(s->matrix.dy())) == s->matrix.dy()
- && qreal(int(s->matrix.m11())) == s->matrix.m11()
- && qreal(int(s->matrix.m22())) == s->matrix.m22();
- break;
-
- default: // shear / perspective...
- s->flags.int_xform = false;
- break;
- }
-
s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
ensureOutlineMapper();
@@ -617,7 +592,6 @@ QRasterPaintEngineState::QRasterPaintEngineState()
flags.bilinear = false;
flags.legacy_rounding = false;
flags.fast_text = true;
- flags.int_xform = true;
flags.tx_noshear = true;
flags.fast_images = true;
@@ -1793,7 +1767,7 @@ void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
QRectF cpRect = path.controlPointRect();
const QRectF pathDeviceRect = s->matrix.mapRect(cpRect);
// Skip paths that by conservative estimates are completely outside the paint device.
- if (!pathDeviceRect.intersects(QRectF(d->deviceRect)))
+ if (!pathDeviceRect.intersects(QRectF(d->deviceRect)) || !pathDeviceRect.isValid())
return;
ProcessSpans blend = d->getBrushFunc(pathDeviceRect, &s->brushData);
@@ -2414,15 +2388,20 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe
QRectF targetBounds = s->matrix.mapRect(r);
bool exceedsPrecision = r.width() > 0x7fff
|| r.height() > 0x7fff
+ || targetBounds.left() < -0x7fff
+ || targetBounds.top() < -0x7fff
+ || targetBounds.right() > 0x7fff
+ || targetBounds.bottom() > 0x7fff
|| targetBounds.width() > 0x7fff
|| targetBounds.height() > 0x7fff
|| s->matrix.m11() >= 512
|| s->matrix.m22() >= 512;
-
if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
if (s->matrix.type() > QTransform::TxScale) {
SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
- if (func && (!clip || clip->hasRectClip)) {
+ // The fast transform methods doesn't really work on small targets, see QTBUG-93475
+ // And it can't antialias the edges
+ if (func && (!clip || clip->hasRectClip) && !s->flags.antialiased && targetBounds.width() >= 16 && targetBounds.height() >= 16) {
func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
s->matrix, s->intOpacity);
@@ -3090,10 +3069,10 @@ QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
static QPair<int, int> visibleGlyphRange(const QRectF &clip, QFontEngine *fontEngine,
glyph_t *glyphs, QFixedPoint *positions, int numGlyphs)
{
- QFixed clipLeft = QFixed::fromReal(clip.left());
- QFixed clipRight = QFixed::fromReal(clip.right());
- QFixed clipTop = QFixed::fromReal(clip.top());
- QFixed clipBottom = QFixed::fromReal(clip.bottom());
+ QFixed clipLeft = QFixed::fromReal(clip.left() - 1);
+ QFixed clipRight = QFixed::fromReal(clip.right() + 1);
+ QFixed clipTop = QFixed::fromReal(clip.top() - 1);
+ QFixed clipBottom = QFixed::fromReal(clip.bottom() + 1);
int first = 0;
while (first < numGlyphs) {
@@ -3309,6 +3288,11 @@ void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
qreal length = line.length();
Q_ASSERT(length > 0);
+ if (length / (patternLength * width) > QDashStroker::repetitionLimit()) {
+ rasterizer->rasterizeLine(line.p1(), line.p2(), width / length, squareCap);
+ return;
+ }
+
while (length > 0) {
const bool rasterize = *inDash;
qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
@@ -3575,7 +3559,7 @@ QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
\internal
Returns the bounding rect of the currently set clip.
*/
-QRect QRasterPaintEngine::clipBoundingRect() const
+QRectF QRasterPaintEngine::clipBoundingRect() const
{
Q_D(const QRasterPaintEngine);
@@ -3587,7 +3571,7 @@ QRect QRasterPaintEngine::clipBoundingRect() const
if (clip->hasRectClip)
return clip->clipRect;
- return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
+ return QRectF(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
}
void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
diff --git a/src/gui/painting/qpaintengine_raster_p.h b/src/gui/painting/qpaintengine_raster_p.h
index 089aadc3f7..7b15292ebb 100644
--- a/src/gui/painting/qpaintengine_raster_p.h
+++ b/src/gui/painting/qpaintengine_raster_p.h
@@ -111,7 +111,6 @@ public:
uint bilinear : 1;
uint legacy_rounding : 1;
uint fast_text : 1;
- uint int_xform : 1;
uint tx_noshear : 1;
uint fast_images : 1;
};
@@ -206,7 +205,7 @@ public:
ComplexClip
};
ClipType clipType() const;
- QRect clipBoundingRect() const;
+ QRectF clipBoundingRect() const;
#ifdef Q_OS_WIN
void setDC(HDC hdc);
diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp
index 5d8f89eadd..9d8e068a5f 100644
--- a/src/gui/painting/qpaintengineex.cpp
+++ b/src/gui/painting/qpaintengineex.cpp
@@ -385,7 +385,7 @@ QPainterState *QPaintEngineEx::createState(QPainterState *orig) const
Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
-void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen)
+void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &inPen)
{
#ifdef QT_DEBUG_DRAW
qDebug() << "QPaintEngineEx::stroke()" << pen;
@@ -403,6 +403,38 @@ void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen)
d->stroker.setCubicToHook(qpaintengineex_cubicTo);
}
+ QRectF clipRect;
+ QPen pen = inPen;
+ if (pen.style() > Qt::SolidLine) {
+ QRectF cpRect = path.controlPointRect();
+ const QTransform &xf = state()->matrix;
+ if (qt_pen_is_cosmetic(pen, state()->renderHints)) {
+ clipRect = d->exDeviceRect;
+ cpRect.translate(xf.dx(), xf.dy());
+ } else {
+ clipRect = xf.inverted().mapRect(QRectF(d->exDeviceRect));
+ }
+ // Check to avoid generating unwieldy amount of dashes that will not be visible anyway
+ qreal pw = pen.widthF() ? pen.widthF() : 1;
+ QRectF extentRect = cpRect.adjusted(-pw, -pw, pw, pw) & clipRect;
+ qreal extent = qMax(extentRect.width(), extentRect.height());
+ qreal patternLength = 0;
+ const QVector<qreal> pattern = pen.dashPattern();
+ const int patternSize = qMin(pattern.size(), 32);
+ for (int i = 0; i < patternSize; i++)
+ patternLength += qMax(pattern.at(i), qreal(0));
+ patternLength *= pw;
+ if (qFuzzyIsNull(patternLength)) {
+ pen.setStyle(Qt::NoPen);
+ } else if (extent / patternLength > QDashStroker::repetitionLimit()) {
+ // approximate stream of tiny dashes with semi-transparent solid line
+ pen.setStyle(Qt::SolidLine);
+ QColor color(pen.color());
+ color.setAlpha(color.alpha() / 2);
+ pen.setColor(color);
+ }
+ }
+
if (!qpen_fast_equals(pen, d->strokerPen)) {
d->strokerPen = pen;
d->stroker.setJoinStyle(pen.joinStyle());
@@ -430,14 +462,8 @@ void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen)
return;
}
- if (pen.style() > Qt::SolidLine) {
- if (qt_pen_is_cosmetic(pen, state()->renderHints)){
- d->activeStroker->setClipRect(d->exDeviceRect);
- } else {
- QRectF clipRect = state()->matrix.inverted().mapRect(QRectF(d->exDeviceRect));
- d->activeStroker->setClipRect(clipRect);
- }
- }
+ if (!clipRect.isNull())
+ d->activeStroker->setClipRect(clipRect);
if (d->activeStroker == &d->stroker)
d->stroker.setForceOpen(path.hasExplicitOpen());
diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp
index aaeb0e86a2..fdb7f331aa 100644
--- a/src/gui/painting/qpainter.cpp
+++ b/src/gui/painting/qpainter.cpp
@@ -175,6 +175,23 @@ static bool qt_painter_thread_test(int devType, int engineType, const char *what
}
#endif
+static bool needsEmulation(const QBrush &brush)
+{
+ bool res = false;
+
+ const QGradient *bg = brush.gradient();
+ if (bg) {
+ res = (bg->coordinateMode() > QGradient::LogicalMode);
+ } else if (brush.style() == Qt::TexturePattern) {
+ if (qHasPixmapTexture(brush))
+ res = !qFuzzyCompare(brush.texture().devicePixelRatio(), qreal(1.0));
+ else
+ res = !qFuzzyCompare(brush.textureImage().devicePixelRatio(), qreal(1.0));
+ }
+
+ return res;
+}
+
void QPainterPrivate::checkEmulation()
{
Q_ASSERT(extended);
@@ -182,21 +199,12 @@ void QPainterPrivate::checkEmulation()
if (state->bgMode == Qt::OpaqueMode)
doEmulation = true;
- const QGradient *bg = state->brush.gradient();
- if (bg && bg->coordinateMode() > QGradient::LogicalMode)
+ if (needsEmulation(state->brush))
doEmulation = true;
- const QGradient *pg = qpen_brush(state->pen).gradient();
- if (pg && pg->coordinateMode() > QGradient::LogicalMode)
+ if (needsEmulation(qpen_brush(state->pen)))
doEmulation = true;
- if (state->brush.style() == Qt::TexturePattern) {
- if (qHasPixmapTexture(state->brush))
- doEmulation |= !qFuzzyCompare(state->brush.texture().devicePixelRatioF(), 1.0);
- else
- doEmulation |= !qFuzzyCompare(state->brush.textureImage().devicePixelRatioF(), 1.0);
- }
-
if (doEmulation && extended->flags() & QPaintEngineEx::DoNotEmulate)
return;
@@ -3319,12 +3327,9 @@ void QPainter::strokePath(const QPainterPath &path, const QPen &pen)
if (path.isEmpty())
return;
- if (d->extended) {
- const QGradient *g = qpen_brush(pen).gradient();
- if (!g || g->coordinateMode() == QGradient::LogicalMode) {
- d->extended->stroke(qtVectorPathForPath(path), pen);
- return;
- }
+ if (d->extended && !needsEmulation(pen.brush())) {
+ d->extended->stroke(qtVectorPathForPath(path), pen);
+ return;
}
QBrush oldBrush = d->state->brush;
@@ -3362,12 +3367,9 @@ void QPainter::fillPath(const QPainterPath &path, const QBrush &brush)
if (path.isEmpty())
return;
- if (d->extended) {
- const QGradient *g = brush.gradient();
- if (!g || g->coordinateMode() == QGradient::LogicalMode) {
- d->extended->fill(qtVectorPathForPath(path), brush);
- return;
- }
+ if (d->extended && !needsEmulation(brush)) {
+ d->extended->fill(qtVectorPathForPath(path), brush);
+ return;
}
QBrush oldBrush = d->state->brush;
@@ -6956,12 +6958,9 @@ void QPainter::fillRect(const QRectF &r, const QBrush &brush)
if (!d->engine)
return;
- if (d->extended) {
- const QGradient *g = brush.gradient();
- if (!g || g->coordinateMode() == QGradient::LogicalMode) {
- d->extended->fillRect(r, brush);
- return;
- }
+ if (d->extended && !needsEmulation(brush)) {
+ d->extended->fillRect(r, brush);
+ return;
}
QPen oldPen = pen();
@@ -6994,12 +6993,9 @@ void QPainter::fillRect(const QRect &r, const QBrush &brush)
if (!d->engine)
return;
- if (d->extended) {
- const QGradient *g = brush.gradient();
- if (!g || g->coordinateMode() == QGradient::LogicalMode) {
- d->extended->fillRect(r, brush);
- return;
- }
+ if (d->extended && !needsEmulation(brush)) {
+ d->extended->fillRect(r, brush);
+ return;
}
QPen oldPen = pen();
@@ -8592,3 +8588,5 @@ void qt_draw_helper(QPainterPrivate *p, const QPainterPath &path, QPainterPrivat
}
QT_END_NAMESPACE
+
+#include "moc_qpainter.cpp"
diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp
index ab60afd9cd..f9544a3241 100644
--- a/src/gui/painting/qpainterpath.cpp
+++ b/src/gui/painting/qpainterpath.cpp
@@ -1222,6 +1222,11 @@ void QPainterPath::addText(const QPointF &point, const QFont &f, const QString &
QTextLayout layout(text, f);
layout.setCacheEnabled(true);
+
+ QTextOption opt = layout.textOption();
+ opt.setUseDesignMetrics(true);
+ layout.setTextOption(opt);
+
QTextEngine *eng = layout.engine();
layout.beginLayout();
QTextLine line = layout.createLine();
@@ -1367,7 +1372,7 @@ void QPainterPath::addRegion(const QRegion &region)
*/
Qt::FillRule QPainterPath::fillRule() const
{
- return isEmpty() ? Qt::OddEvenFill : d_func()->fillRule;
+ return !d_func() ? Qt::OddEvenFill : d_func()->fillRule;
}
/*!
diff --git a/src/gui/painting/qpainterpath_p.h b/src/gui/painting/qpainterpath_p.h
index a420e0b3d9..287265653a 100644
--- a/src/gui/painting/qpainterpath_p.h
+++ b/src/gui/painting/qpainterpath_p.h
@@ -313,7 +313,6 @@ inline void QPainterPathData::clear()
elements.clear();
cStart = 0;
- fillRule = Qt::OddEvenFill;
bounds = {};
controlBounds = {};
diff --git a/src/gui/painting/qpathclipper_p.h b/src/gui/painting/qpathclipper_p.h
index 9444a87b71..18f64c5e8c 100644
--- a/src/gui/painting/qpathclipper_p.h
+++ b/src/gui/painting/qpathclipper_p.h
@@ -156,7 +156,7 @@ public:
int vertex(Direction direction) const;
private:
- int m_next[2][2];
+ int m_next[2][2] = { { -1, -1 }, { -1, -1 } };
};
class QPathSegments
@@ -296,10 +296,6 @@ inline QPathEdge::QPathEdge(int a, int b)
, angle(0)
, invAngle(0)
{
- m_next[0][0] = -1;
- m_next[1][0] = -1;
- m_next[0][0] = -1;
- m_next[1][0] = -1;
}
inline int QPathEdge::next(Traversal traversal, Direction direction) const
diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp
index de9fc13331..3066744f1b 100644
--- a/src/gui/painting/qpdf.cpp
+++ b/src/gui/painting/qpdf.cpp
@@ -856,14 +856,14 @@ void QPdfEngine::drawRects (const QRectF *rects, int rectCount)
if (!d->hasPen && !d->hasBrush)
return;
- if (d->simplePen || !d->hasPen) {
- // draw strokes natively in this case for better output
- if(!d->simplePen && !d->stroker.matrix.isIdentity())
+ if ((d->simplePen && !d->needsTransform) || !d->hasPen) {
+ // draw natively in this case for better output
+ if (!d->hasPen && d->needsTransform) // i.e. this is just a fillrect
*d->currentPage << "q\n" << QPdf::generateMatrix(d->stroker.matrix);
for (int i = 0; i < rectCount; ++i)
*d->currentPage << rects[i].x() << rects[i].y() << rects[i].width() << rects[i].height() << "re\n";
*d->currentPage << (d->hasPen ? (d->hasBrush ? "B\n" : "S\n") : "f\n");
- if(!d->simplePen && !d->stroker.matrix.isIdentity())
+ if (!d->hasPen && d->needsTransform)
*d->currentPage << "Q\n";
} else {
QPainterPath p;
@@ -920,7 +920,8 @@ void QPdfEngine::drawPath (const QPainterPath &p)
if (d->simplePen) {
// draw strokes natively in this case for better output
- *d->currentPage << QPdf::generatePath(p, QTransform(), d->hasBrush ? QPdf::FillAndStrokePath : QPdf::StrokePath);
+ *d->currentPage << QPdf::generatePath(p, d->needsTransform ? d->stroker.matrix : QTransform(),
+ d->hasBrush ? QPdf::FillAndStrokePath : QPdf::StrokePath);
} else {
if (d->hasBrush)
*d->currentPage << QPdf::generatePath(p, d->stroker.matrix, QPdf::FillPath);
@@ -967,7 +968,7 @@ void QPdfEngine::drawPixmap (const QRectF &rectangle, const QPixmap &pixmap, con
*d->currentPage
<< QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(),
- rectangle.x(), rectangle.y()) * (d->simplePen ? QTransform() : d->stroker.matrix));
+ rectangle.x(), rectangle.y()) * (!d->needsTransform ? QTransform() : d->stroker.matrix));
if (bitmap) {
// set current pen as d->brush
d->brush = d->pen.brush();
@@ -1007,7 +1008,7 @@ void QPdfEngine::drawImage(const QRectF &rectangle, const QImage &image, const Q
*d->currentPage
<< QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(),
- rectangle.x(), rectangle.y()) * (d->simplePen ? QTransform() : d->stroker.matrix));
+ rectangle.x(), rectangle.y()) * (!d->needsTransform ? QTransform() : d->stroker.matrix));
setBrush();
d->currentPage->streamImage(im.width(), im.height(), object);
*d->currentPage << "Q\n";
@@ -1056,7 +1057,7 @@ void QPdfEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
}
*d->currentPage << "q\n";
- if(!d->simplePen)
+ if (d->needsTransform)
*d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
bool hp = d->hasPen;
@@ -1135,12 +1136,12 @@ void QPdfEngine::updateState(const QPaintEngineState &state)
d->pen = state.pen();
}
d->hasPen = d->pen.style() != Qt::NoPen;
+ bool oldCosmetic = d->stroker.cosmeticPen;
d->stroker.setPen(d->pen, state.renderHints());
QBrush penBrush = d->pen.brush();
- bool cosmeticPen = qt_pen_is_cosmetic(d->pen, state.renderHints());
bool oldSimple = d->simplePen;
- d->simplePen = (d->hasPen && !cosmeticPen && (penBrush.style() == Qt::SolidPattern) && penBrush.isOpaque() && d->opacity == 1.0);
- if (oldSimple != d->simplePen)
+ d->simplePen = (d->hasPen && (penBrush.style() == Qt::SolidPattern) && penBrush.isOpaque() && d->opacity == 1.0);
+ if (oldSimple != d->simplePen || oldCosmetic != d->stroker.cosmeticPen)
flags |= DirtyTransform;
} else if (flags & DirtyHints) {
d->stroker.setPen(d->pen, state.renderHints());
@@ -1224,8 +1225,13 @@ void QPdfEngine::setupGraphicsState(QPaintEngine::DirtyFlags flags)
if (flags & DirtyTransform) {
*d->currentPage << "q\n";
- if (d->simplePen && !d->stroker.matrix.isIdentity())
- *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
+ d->needsTransform = false;
+ if (!d->stroker.matrix.isIdentity()) {
+ if (d->simplePen && !d->stroker.cosmeticPen)
+ *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
+ else
+ d->needsTransform = true; // I.e. page-wide xf not set, local xf needed
+ }
}
if (flags & DirtyBrush)
setBrush();
@@ -1480,7 +1486,7 @@ int QPdfEngine::metric(QPaintDevice::PaintDeviceMetric metricType) const
QPdfEnginePrivate::QPdfEnginePrivate()
: clipEnabled(false), allClipped(false), hasPen(true), hasBrush(false), simplePen(false),
- pdfVersion(QPdfEngine::Version_1_4),
+ needsTransform(false), pdfVersion(QPdfEngine::Version_1_4),
outDevice(nullptr), ownsDevice(false),
embedFonts(true),
grayscale(false),
@@ -1539,6 +1545,7 @@ bool QPdfEngine::begin(QPaintDevice *pdev)
d->graphicsState = 0;
d->patternColorSpace = 0;
d->simplePen = false;
+ d->needsTransform = false;
d->pages.clear();
d->imageCache.clear();
diff --git a/src/gui/painting/qpdf_p.h b/src/gui/painting/qpdf_p.h
index 4ff540e67b..6964c67d93 100644
--- a/src/gui/painting/qpdf_p.h
+++ b/src/gui/painting/qpdf_p.h
@@ -271,6 +271,7 @@ public:
bool hasPen;
bool hasBrush;
bool simplePen;
+ bool needsTransform;
qreal opacity;
QPdfEngine::PdfVersion pdfVersion;
diff --git a/src/gui/painting/qpdfwriter.cpp b/src/gui/painting/qpdfwriter.cpp
index cf30ea496c..ae140f31b5 100644
--- a/src/gui/painting/qpdfwriter.cpp
+++ b/src/gui/painting/qpdfwriter.cpp
@@ -489,4 +489,6 @@ QT_WARNING_POP
QT_END_NAMESPACE
+#include "moc_qpdfwriter.cpp"
+
#endif // QT_NO_PDF
diff --git a/src/gui/painting/qpen.cpp b/src/gui/painting/qpen.cpp
index 01e581d2ed..254abf88c0 100644
--- a/src/gui/painting/qpen.cpp
+++ b/src/gui/painting/qpen.cpp
@@ -653,12 +653,15 @@ qreal QPen::widthF() const
*/
void QPen::setWidth(int width)
{
- if (width < 0)
- qWarning("QPen::setWidth: Setting a pen width with a negative value is not defined");
+ if (width < 0 || width >= (1 << 15)) {
+ qWarning("QPen::setWidth: Setting a pen width that is out of range");
+ return;
+ }
if ((qreal)width == d->width)
return;
detach();
d->width = width;
+ d->defaultWidth = false;
}
/*!
@@ -677,8 +680,8 @@ void QPen::setWidth(int width)
void QPen::setWidthF(qreal width)
{
- if (width < 0.f) {
- qWarning("QPen::setWidthF: Setting a pen width with a negative value is not defined");
+ if (width < 0.f || width >= (1 << 15)) {
+ qWarning("QPen::setWidthF: Setting a pen width that is out of range");
return;
}
if (qAbs(d->width - width) < 0.00000001f)
diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp
index c092a7153f..9c9b053b2b 100644
--- a/src/gui/painting/qplatformbackingstore.cpp
+++ b/src/gui/painting/qplatformbackingstore.cpp
@@ -751,3 +751,5 @@ bool QPlatformBackingStore::scroll(const QRegion &area, int dx, int dy)
}
QT_END_NAMESPACE
+
+#include "moc_qplatformbackingstore.cpp"
diff --git a/src/gui/painting/qregion.cpp b/src/gui/painting/qregion.cpp
index 2ecc97af04..13ffd93640 100644
--- a/src/gui/painting/qregion.cpp
+++ b/src/gui/painting/qregion.cpp
@@ -3647,6 +3647,8 @@ static QRegionPrivate *PolygonRegion(const QPoint *Pts, int Count, int rule)
POINTBLOCK *tmpPtBlock;
int numFullPtBlocks = 0;
+ Q_ASSUME(Count > 1);
+
region = new QRegionPrivate;
/* special case a rectangle */
diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp
index 22302f9790..3b6357a893 100644
--- a/src/gui/painting/qstroker.cpp
+++ b/src/gui/painting/qstroker.cpp
@@ -1179,32 +1179,42 @@ void QDashStroker::processCurrentSubpath()
bool done = pos >= estop;
- if (clipping) {
- // Check if the entire line can be clipped away.
- if (!lineIntersectsRect(prev, e, clip_tl, clip_br)) {
- // Cut away full dash sequences.
- elen -= qFloor(elen * invSumLength) * sumLength;
- // Update dash offset.
- while (!done) {
- qreal dpos = pos + dashes[idash] - doffset - estart;
-
- Q_ASSERT(dpos >= 0);
-
- if (dpos > elen) { // dash extends this line
- doffset = dashes[idash] - (dpos - elen); // subtract the part already used
- pos = estop; // move pos to next path element
- done = true;
- } else { // Dash is on this line
- pos = dpos + estart;
- done = pos >= estop;
- if (++idash >= dashCount)
- idash = 0;
- doffset = 0; // full segment so no offset on next.
- }
+ // Check if the entire line should be clipped away or simplified
+ bool clipIt = clipping && !lineIntersectsRect(prev, e, clip_tl, clip_br);
+ bool skipDashing = elen * invSumLength > repetitionLimit();
+ int maxDashes = dashCount;
+ if (skipDashing || clipIt) {
+ // Cut away full dash sequences.
+ elen -= std::floor(elen * invSumLength) * sumLength;
+ // Update dash offset.
+ while (!done) {
+ qreal dpos = pos + dashes[idash] - doffset - estart;
+
+ Q_ASSERT(dpos >= 0);
+
+ if (dpos > elen) { // dash extends this line
+ doffset = dashes[idash] - (dpos - elen); // subtract the part already used
+ pos = estop; // move pos to next path element
+ done = true;
+ } else { // Dash is on this line
+ pos = --maxDashes > 0 ? dpos + estart : estop;
+ done = pos >= estop;
+ if (++idash >= dashCount)
+ idash = 0;
+ doffset = 0; // full segment so no offset on next.
}
+ }
+ if (clipIt) {
hasMoveTo = false;
- move_to_pos = e;
+ } else {
+ // skip costly dashing, just draw solid line
+ if (!hasMoveTo) {
+ emitMoveTo(move_to_pos.x, move_to_pos.y);
+ hasMoveTo = true;
+ }
+ emitLineTo(e.x, e.y);
}
+ move_to_pos = e;
}
// Dash away...
diff --git a/src/gui/painting/qstroker_p.h b/src/gui/painting/qstroker_p.h
index 06446ae3df..72e211d046 100644
--- a/src/gui/painting/qstroker_p.h
+++ b/src/gui/painting/qstroker_p.h
@@ -268,6 +268,7 @@ public:
QStroker *stroker() const { return m_stroker; }
static QVector<qfixed> patternForStyle(Qt::PenStyle style);
+ static int repetitionLimit() { return 10000; }
void setDashPattern(const QVector<qfixed> &dashPattern) { m_dashPattern = dashPattern; }
QVector<qfixed> dashPattern() const { return m_dashPattern; }
diff --git a/src/gui/painting/qt_attribution.json b/src/gui/painting/qt_attribution.json
index 7b16e8c211..1a2b907606 100644
--- a/src/gui/painting/qt_attribution.json
+++ b/src/gui/painting/qt_attribution.json
@@ -10,7 +10,7 @@
"Homepage": "http://www.freetype.org",
"License": "Freetype Project License or GNU General Public License v2.0 only",
"LicenseId": "FTL or GPL-2.0",
- "LicenseFile": "../../3rdparty/freetype/docs/LICENSE.TXT",
+ "LicenseFile": "../../3rdparty/freetype/LICENSE.txt",
"Copyright": "Copyright 2000-2016 by David Turner, Robert Wilhelm, and Werner Lemberg."
},
{
diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp
index 141b195a42..7b2fcc2239 100644
--- a/src/gui/painting/qtextureglyphcache.cpp
+++ b/src/gui/painting/qtextureglyphcache.cpp
@@ -93,7 +93,7 @@ int QTextureGlyphCache::calculateSubPixelPositionCount(glyph_t glyph) const
}
bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs,
- const QFixedPoint *positions)
+ const QFixedPoint *positions, bool includeGlyphCacheScale)
{
#ifdef CACHE_DEBUG
printf("Populating with %d glyphs\n", numGlyphs);
@@ -120,6 +120,8 @@ bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const
m_cy = padding;
}
+ qreal glyphCacheScaleX = transform().m11();
+
QHash<GlyphAndSubPixelPosition, Coord> listItemCoordinates;
int rowHeight = 0;
@@ -130,6 +132,8 @@ bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const
QFixed subPixelPosition;
if (supportsSubPixelPositions) {
QFixed x = positions != nullptr ? positions[i].x : QFixed();
+ if (includeGlyphCacheScale)
+ x = QFixed::fromReal(x.toReal() * glyphCacheScaleX);
subPixelPosition = fontEngine->subPixelPositionForX(x);
}
diff --git a/src/gui/painting/qtextureglyphcache_p.h b/src/gui/painting/qtextureglyphcache_p.h
index b6fc7230a8..cbf5224039 100644
--- a/src/gui/painting/qtextureglyphcache_p.h
+++ b/src/gui/painting/qtextureglyphcache_p.h
@@ -110,7 +110,7 @@ public:
};
bool populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs,
- const QFixedPoint *positions);
+ const QFixedPoint *positions, bool includeGlyphCacheScale = false);
bool hasPendingGlyphs() const { return !m_pendingGlyphs.isEmpty(); };
void fillInPendingGlyphs();
diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp
index 9d8bb0c3e2..e86aff1cae 100644
--- a/src/gui/painting/qtransform.cpp
+++ b/src/gui/painting/qtransform.cpp
@@ -222,6 +222,7 @@ static void nanWarning(const char *func)
transformation is achieved by setting both the projection factors and
the scaling factors.
+ \section2 Combining Transforms
Here's the combined transformations example using basic matrix
operations:
@@ -232,6 +233,26 @@ static void nanWarning(const char *func)
\snippet transform/main.cpp 2
\endtable
+ The combined transform first scales each operand, then rotates it, and
+ finally translates it, just as in the order in which the product of its
+ factors is written. This means the point to which the transforms are
+ applied is implicitly multiplied on the left with the transform
+ to its right.
+
+ \section2 Relation to Matrix Notation
+ The matrix notation in QTransform is the transpose of a commonly-taught
+ convention which represents transforms and points as matrices and vectors.
+ That convention multiplies its matrix on the left and column vector to the
+ right. In other words, when several transforms are applied to a point, the
+ right-most matrix acts directly on the vector first. Then the next matrix
+ to the left acts on the result of the first operation - and so on. As a
+ result, that convention multiplies the matrices that make up a composite
+ transform in the reverse of the order in QTransform, as you can see in
+ \l {Combining Transforms}. Transposing the matrices, and combining them to
+ the right of a row vector that represents the point, lets the matrices of
+ transforms appear, in their product, in the order in which we think of the
+ transforms being applied to the point.
+
\sa QPainter, {Coordinate System}, {painting/affine}{Affine
Transformations Example}, {Transformations Example}
*/
@@ -2126,7 +2147,7 @@ QTransform::TransformationType QTransform::type() const
case TxShear:
case TxRotate:
if (!qFuzzyIsNull(affine._m12) || !qFuzzyIsNull(affine._m21)) {
- const qreal dot = affine._m11 * affine._m12 + affine._m21 * affine._m22;
+ const qreal dot = affine._m11 * affine._m21 + affine._m12 * affine._m22;
if (qFuzzyIsNull(dot))
m_type = TxRotate;
else
diff --git a/src/gui/painting/qtriangulatingstroker.cpp b/src/gui/painting/qtriangulatingstroker.cpp
index 9490e11dd9..bdcf49d6c8 100644
--- a/src/gui/painting/qtriangulatingstroker.cpp
+++ b/src/gui/painting/qtriangulatingstroker.cpp
@@ -50,6 +50,7 @@ QT_BEGIN_NAMESPACE
void QTriangulatingStroker::endCapOrJoinClosed(const qreal *start, const qreal *cur,
bool implicitClose, bool endsAtStart)
{
+ Q_ASSERT(start);
if (endsAtStart) {
join(start + 2);
} else if (implicitClose) {