diff options
Diffstat (limited to 'src/gui/painting')
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 ®ion, 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 ®ion) */ 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) { |