diff options
Diffstat (limited to 'src/gui/painting')
32 files changed, 2234 insertions, 965 deletions
diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri index 63e345545c..9ff0b5f5e5 100644 --- a/src/gui/painting/painting.pri +++ b/src/gui/painting/painting.pri @@ -99,6 +99,9 @@ SOURCES += \ painting/qplatformbackingstore.cpp \ painting/qpathsimplifier.cpp +RESOURCES += \ + painting/qpdf.qrc + darwin { HEADERS += painting/qcoregraphics_p.h SOURCES += painting/qcoregraphics.mm diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp index 53349dcef4..d2c0912878 100644 --- a/src/gui/painting/qbackingstore.cpp +++ b/src/gui/painting/qbackingstore.cpp @@ -90,50 +90,6 @@ public: */ /*! - Flushes the given \a region from the specified window \a win onto the - screen. - - Note that the \a offset parameter is currently unused. -*/ -void QBackingStore::flush(const QRegion ®ion, QWindow *win, const QPoint &offset) -{ - if (!win) - win = window(); - if (!win->handle()) { - qWarning() << "QBackingStore::flush() called for " - << win << " which does not have a handle."; - return; - } - -#ifdef QBACKINGSTORE_DEBUG - if (win && win->isTopLevel() && !qt_window_private(win)->receivedExpose) { - qWarning().nospace() << "QBackingStore::flush() called with non-exposed window " - << win << ", behavior is undefined"; - } -#endif - - Q_ASSERT(win == this->window() || this->window()->isAncestorOf(win, QWindow::ExcludeTransients)); - - d_ptr->platformBackingStore->flush(win, QHighDpi::toNativeLocalRegion(region, win), - QHighDpi::toNativeLocalPosition(offset, win)); -} - -/*! - \fn QPaintDevice* QBackingStore::paintDevice() - - Implement this function to return the appropriate paint device. -*/ -QPaintDevice *QBackingStore::paintDevice() -{ - QPaintDevice *device = d_ptr->platformBackingStore->paintDevice(); - - if (QHighDpiScaling::isActive() && device->devType() == QInternal::Image) - return d_ptr->highDpiBackingstore.data(); - - return device; -} - -/*! Constructs an empty surface for the given top-level \a window. */ QBackingStore::QBackingStore(QWindow *window) @@ -161,8 +117,10 @@ QWindow* QBackingStore::window() const } /*! - This function is called before painting onto the surface begins, - with the \a region in which the painting will occur. + Begins painting on the backing store surface in the given \a region. + + You should call this function before using the paintDevice() to + paint. \sa endPaint(), paintDevice() */ @@ -203,7 +161,26 @@ void QBackingStore::beginPaint(const QRegion ®ion) } /*! - This function is called after painting onto the surface has ended. + Returns the paint device for this surface. + + \warning The device is only valid between calls to beginPaint() and + endPaint(). You should not cache the returned value. +*/ +QPaintDevice *QBackingStore::paintDevice() +{ + QPaintDevice *device = d_ptr->platformBackingStore->paintDevice(); + + if (QHighDpiScaling::isActive() && device->devType() == QInternal::Image) + return d_ptr->highDpiBackingstore.data(); + + return device; +} + +/*! + Ends painting. + + You should call this function after painting with the paintDevice() + has ended. \sa beginPaint(), paintDevice() */ @@ -213,9 +190,50 @@ void QBackingStore::endPaint() } /*! - Sets the size of the windowsurface to be \a size. + Flushes the given \a region from the specified \a window onto the + screen. + + The \a window must either be the top level window represented by + this backingstore, or a non-transient child of that window. Passing + \c nullptr falls back to using the backingstore's top level window. + + If the \a window is a child window, the \a region should be in child window + coordinates, and the \a offset should be the child window's offset in relation + to the backingstore's top level window. + + You should call this function after ending painting with endPaint(). + + \sa QWindow::transientParent() +*/ +void QBackingStore::flush(const QRegion ®ion, QWindow *window, const QPoint &offset) +{ + QWindow *topLevelWindow = this->window(); + + if (!window) + window = topLevelWindow; + if (!window->handle()) { + qWarning() << "QBackingStore::flush() called for " + << window << " which does not have a handle."; + return; + } + +#ifdef QBACKINGSTORE_DEBUG + if (window && window->isTopLevel() && !qt_window_private(window)->receivedExpose) { + qWarning().nospace() << "QBackingStore::flush() called with non-exposed window " + << window << ", behavior is undefined"; + } +#endif + + Q_ASSERT(window == topLevelWindow || topLevelWindow->isAncestorOf(window, QWindow::ExcludeTransients)); + + d_ptr->platformBackingStore->flush(window, QHighDpi::toNativeLocalRegion(region, window), + QHighDpi::toNativeLocalPosition(offset, window)); +} + +/*! + Sets the size of the window surface to \a size. - \sa size() + \sa size() */ void QBackingStore::resize(const QSize &size) { @@ -224,7 +242,7 @@ void QBackingStore::resize(const QSize &size) } /*! - Returns the current size of the windowsurface. + Returns the current size of the window surface. */ QSize QBackingStore::size() const { @@ -252,7 +270,7 @@ bool QBackingStore::scroll(const QRegion &area, int dx, int dy) } /*! - Set \a region as the static contents of this window. + Set \a region as the static contents of this window. */ void QBackingStore::setStaticContents(const QRegion ®ion) { @@ -260,8 +278,8 @@ void QBackingStore::setStaticContents(const QRegion ®ion) } /*! - Returns a pointer to the QRegion that has the static contents - of this window. + Returns a QRegion representing the area of the window that + has static contents. */ QRegion QBackingStore::staticContents() const { @@ -269,8 +287,7 @@ QRegion QBackingStore::staticContents() const } /*! - Returns a boolean indicating if this window - has static contents or not. + Returns a boolean indicating if this window has static contents or not. */ bool QBackingStore::hasStaticContents() const { @@ -325,7 +342,7 @@ void Q_GUI_EXPORT qt_scrollRectInImage(QImage &img, const QRect &rect, const QPo } /*! - Returns a pointer to the QPlatformBackingStore implementation + Returns a pointer to the QPlatformBackingStore implementation */ QPlatformBackingStore *QBackingStore::handle() const { diff --git a/src/gui/painting/qbackingstore.h b/src/gui/painting/qbackingstore.h index 2ba6e1c906..ed37e11a5b 100644 --- a/src/gui/painting/qbackingstore.h +++ b/src/gui/painting/qbackingstore.h @@ -66,9 +66,7 @@ public: QPaintDevice *paintDevice(); - // 'window' can be a child window, in which case 'region' is in child window coordinates and - // offset is the (child) window's offset in relation to the window surface. - void flush(const QRegion ®ion, QWindow *window = Q_NULLPTR, const QPoint &offset = QPoint()); + void flush(const QRegion ®ion, QWindow *window = nullptr, const QPoint &offset = QPoint()); void resize(const QSize &size); QSize size() const; diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp index cc3ee76f0d..5c13308d94 100644 --- a/src/gui/painting/qbrush.cpp +++ b/src/gui/painting/qbrush.cpp @@ -100,7 +100,7 @@ const uchar *qt_patternForBrush(int brushStyle, bool invert) return pat_tbl[brushStyle - Qt::Dense1Pattern][invert]; } -QPixmap qt_pixmapForBrush(int brushStyle, bool invert) +Q_GUI_EXPORT QPixmap qt_pixmapForBrush(int brushStyle, bool invert) { QPixmap pm; diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp index 9e1785c11d..855f245396 100644 --- a/src/gui/painting/qcolor.cpp +++ b/src/gui/painting/qcolor.cpp @@ -79,7 +79,7 @@ static inline int hex2int(char s) return h < 0 ? h : (h << 4) | h; } -static bool get_hex_rgb(const char *name, int len, QRgb *rgb) +static bool get_hex_rgb(const char *name, size_t len, QRgb *rgb) { if (name[0] != '#') return false; @@ -124,12 +124,12 @@ bool qt_get_hex_rgb(const char *name, QRgb *rgb) return get_hex_rgb(name, qstrlen(name), rgb); } -static bool get_hex_rgb(const QChar *str, int len, QRgb *rgb) +static bool get_hex_rgb(const QChar *str, size_t len, QRgb *rgb) { if (len > 13) return false; char tmp[16]; - for (int i = 0; i < len; ++i) + for (size_t i = 0; i < len; ++i) tmp[i] = str[i].toLatin1(); tmp[len] = 0; return get_hex_rgb(tmp, len, rgb); @@ -858,6 +858,7 @@ QString QColor::name(NameFormat format) const return QString(); } +#if QT_STRINGVIEW_LEVEL < 2 /*! Sets the RGB value of this QColor to \a name, which may be in one of these formats: @@ -883,6 +884,17 @@ QString QColor::name(NameFormat format) const void QColor::setNamedColor(const QString &name) { + setColorFromString(qToStringViewIgnoringNull(name)); +} +#endif + +/*! + \overload + \since 5.10 +*/ + +void QColor::setNamedColor(QStringView name) +{ setColorFromString(name); } @@ -896,6 +908,7 @@ void QColor::setNamedColor(QLatin1String name) setColorFromString(name); } +#if QT_STRINGVIEW_LEVEL < 2 /*! \since 4.7 @@ -909,7 +922,17 @@ void QColor::setNamedColor(QLatin1String name) */ bool QColor::isValidColor(const QString &name) { - return !name.isEmpty() && QColor().setColorFromString(name); + return isValidColor(qToStringViewIgnoringNull(name)); +} +#endif + +/*! + \overload + \since 5.10 +*/ +bool QColor::isValidColor(QStringView name) Q_DECL_NOTHROW +{ + return name.size() && QColor().setColorFromString(name); } /*! @@ -922,7 +945,7 @@ bool QColor::isValidColor(QLatin1String name) Q_DECL_NOTHROW } template <typename String> -bool QColor::setColorFromString(const String &name) +bool QColor::setColorFromString(String name) { if (!name.size()) { invalidate(); diff --git a/src/gui/painting/qcolor.h b/src/gui/painting/qcolor.h index 83a93e25ea..0c5ebcbda9 100644 --- a/src/gui/painting/qcolor.h +++ b/src/gui/painting/qcolor.h @@ -72,7 +72,10 @@ public: inline QColor(int r, int g, int b, int a = 255); QColor(QRgb rgb) Q_DECL_NOTHROW; QColor(QRgba64 rgba64) Q_DECL_NOTHROW; +#if QT_STRINGVIEW_LEVEL < 2 inline QColor(const QString& name); +#endif + explicit inline QColor(QStringView name); inline QColor(const char *aname) : QColor(QLatin1String(aname)) {} inline QColor(QLatin1String name); QColor(Spec spec) Q_DECL_NOTHROW; @@ -95,7 +98,10 @@ public: QString name() const; QString name(NameFormat format) const; +#if QT_STRINGVIEW_LEVEL < 2 void setNamedColor(const QString& name); +#endif + void setNamedColor(QStringView name); void setNamedColor(QLatin1String name); static QStringList colorNames(); @@ -221,14 +227,17 @@ public: operator QVariant() const; +#if QT_STRINGVIEW_LEVEL < 2 static bool isValidColor(const QString &name); +#endif + static bool isValidColor(QStringView) Q_DECL_NOTHROW; static bool isValidColor(QLatin1String) Q_DECL_NOTHROW; private: void invalidate() Q_DECL_NOTHROW; template <typename String> - bool setColorFromString(const String &name); + bool setColorFromString(String name); Spec cspec; union { @@ -280,8 +289,13 @@ inline QColor::QColor(int r, int g, int b, int a) inline QColor::QColor(QLatin1String aname) { setNamedColor(aname); } +inline QColor::QColor(QStringView aname) +{ setNamedColor(aname); } + +#if QT_STRINGVIEW_LEVEL < 2 inline QColor::QColor(const QString& aname) { setNamedColor(aname); } +#endif #if QT_VERSION < QT_VERSION_CHECK(6,0,0) inline QColor::QColor(const QColor &acolor) Q_DECL_NOTHROW diff --git a/src/gui/painting/qcompositionfunctions.cpp b/src/gui/painting/qcompositionfunctions.cpp index 6d2cb9aadb..ee05f810f1 100644 --- a/src/gui/painting/qcompositionfunctions.cpp +++ b/src/gui/painting/qcompositionfunctions.cpp @@ -855,6 +855,10 @@ struct QFullCoverage { { *dest = src; } + inline void store(QRgba64 *dest, const QRgba64 src) const + { + *dest = src; + } }; struct QPartialCoverage { @@ -868,6 +872,10 @@ struct QPartialCoverage { { *dest = INTERPOLATE_PIXEL_255(src, ca, *dest, ica); } + inline void store(QRgba64 *dest, const QRgba64 src) const + { + *dest = interpolate255(src, ca, *dest, ica); + } private: const uint ca; @@ -879,6 +887,11 @@ static inline int mix_alpha(int da, int sa) return 255 - ((255 - sa) * (255 - da) >> 8); } +static inline uint mix_alpha_rgb64(uint da, uint sa) +{ + return 65535 - ((65535 - sa) * (65535 - da) >> 16); +} + /* Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa) = Sca + Dca @@ -897,17 +910,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Plus_impl(uint *dest, int } } -template <typename T> -Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Plus_impl_rgb64(QRgba64 *dest, int length, QRgba64 color, const T &coverage) -{ - QRgba64 s = color; - for (int i = 0; i < length; ++i) { - QRgba64 d = dest[i]; - d = comp_func_Plus_one_pixel(d, s); - coverage.store(&dest[i], d); - } -} - void QT_FASTCALL comp_func_solid_Plus(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) @@ -986,6 +988,11 @@ static inline int multiply_op(int dst, int src, int da, int sa) return qt_div_255(src * dst + src * (255 - da) + dst * (255 - sa)); } +static inline uint multiply_op_rgb64(uint dst, uint src, uint da, uint sa) +{ + return qt_div_65535(src * dst + src * (65535 - da) + dst * (65535 - sa)); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Multiply_impl(uint *dest, int length, uint color, const T &coverage) { @@ -1011,6 +1018,29 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Multiply_impl(uint *dest, } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Multiply_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +{ + uint sa = color.alpha(); + uint sr = color.red(); + uint sg = color.green(); + uint sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + uint da = d.alpha(); + +#define OP(a, b) multiply_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), sr); + uint b = OP( d.blue(), sb); + uint g = OP(d.green(), sg); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_solid_Multiply(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) @@ -1019,6 +1049,14 @@ void QT_FASTCALL comp_func_solid_Multiply(uint *dest, int length, uint color, ui comp_func_solid_Multiply_impl(dest, length, color, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_solid_Multiply_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Multiply_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Multiply_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Multiply_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1042,6 +1080,27 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Multiply_impl(uint *Q_DECL_REST } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Multiply_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + QRgba64 s = src[i]; + + uint da = d.alpha(); + uint sa = s.alpha(); + +#define OP(a, b) multiply_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), s.red()); + uint b = OP( d.blue(), s.blue()); + uint g = OP(d.green(), s.green()); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_Multiply(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1050,6 +1109,14 @@ void QT_FASTCALL comp_func_Multiply(uint *Q_DECL_RESTRICT dest, const uint *Q_DE comp_func_Multiply_impl(dest, src, length, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_Multiply_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Multiply_impl(dest, src, length, QFullCoverage()); + else + comp_func_Multiply_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + /* Dca' = (Sca.Da + Dca.Sa - Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa) = Sca + Dca - Sca.Dca @@ -1079,6 +1146,29 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Screen_impl(uint *dest, i } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Screen_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +{ + uint sa = color.alpha(); + uint sr = color.red(); + uint sg = color.green(); + uint sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + uint da = d.alpha(); + +#define OP(a, b) 65535 - qt_div_65535((65535-a) * (65535-b)) + uint r = OP( d.red(), sr); + uint b = OP( d.blue(), sb); + uint g = OP(d.green(), sg); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_solid_Screen(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) @@ -1087,6 +1177,14 @@ void QT_FASTCALL comp_func_solid_Screen(uint *dest, int length, uint color, uint comp_func_solid_Screen_impl(dest, length, color, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_solid_Screen_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Screen_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Screen_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Screen_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1110,6 +1208,27 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Screen_impl(uint *Q_DECL_RESTRI } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Screen_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + QRgba64 s = src[i]; + + uint da = d.alpha(); + uint sa = s.alpha(); + +#define OP(a, b) 65535 - (((65535-a) * (65535-b)) >> 16) + uint r = OP( d.red(), s.red()); + uint b = OP( d.blue(), s.blue()); + uint g = OP(d.green(), s.green()); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_Screen(uint *dest, const uint *src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1118,6 +1237,14 @@ void QT_FASTCALL comp_func_Screen(uint *dest, const uint *src, int length, uint comp_func_Screen_impl(dest, src, length, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_Screen_rgb64(QRgba64 *dest, const QRgba64 *src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Screen_impl(dest, src, length, QFullCoverage()); + else + comp_func_Screen_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + /* if 2.Dca < Da Dca' = 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa) @@ -1133,6 +1260,15 @@ static inline int overlay_op(int dst, int src, int da, int sa) return qt_div_255(sa * da - 2 * (da - dst) * (sa - src) + temp); } +static inline uint overlay_op_rgb64(uint dst, uint src, uint da, uint sa) +{ + const uint temp = src * (65535 - da) + dst * (65535 - sa); + if (2 * dst < da) + return qt_div_65535(2 * src * dst + temp); + else + return qt_div_65535(sa * da - 2 * (da - dst) * (sa - src) + temp); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Overlay_impl(uint *dest, int length, uint color, const T &coverage) { @@ -1158,6 +1294,29 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Overlay_impl(uint *dest, } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Overlay_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +{ + uint sa = color.alpha(); + uint sr = color.red(); + uint sg = color.green(); + uint sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + uint da = d.alpha(); + +#define OP(a, b) overlay_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), sr); + uint b = OP( d.blue(), sb); + uint g = OP(d.green(), sg); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_solid_Overlay(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) @@ -1166,6 +1325,14 @@ void QT_FASTCALL comp_func_solid_Overlay(uint *dest, int length, uint color, uin comp_func_solid_Overlay_impl(dest, length, color, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_solid_Overlay_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Overlay_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Overlay_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Overlay_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1189,6 +1356,27 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Overlay_impl(uint *Q_DECL_RESTR } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Overlay_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + QRgba64 s = src[i]; + + uint da = d.alpha(); + uint sa = s.alpha(); + +#define OP(a, b) overlay_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), s.red()); + uint b = OP( d.blue(), s.blue()); + uint g = OP(d.green(), s.green()); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_Overlay(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1197,6 +1385,14 @@ void QT_FASTCALL comp_func_Overlay(uint *Q_DECL_RESTRICT dest, const uint *Q_DEC comp_func_Overlay_impl(dest, src, length, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_Overlay_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Overlay_impl(dest, src, length, QFullCoverage()); + else + comp_func_Overlay_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + /* Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa) Da' = Sa + Da - Sa.Da @@ -1206,6 +1402,11 @@ static inline int darken_op(int dst, int src, int da, int sa) return qt_div_255(qMin(src * da, dst * sa) + src * (255 - da) + dst * (255 - sa)); } +static inline uint darken_op_rgb64(uint dst, uint src, uint da, uint sa) +{ + return qt_div_65535(qMin(src * da, dst * sa) + src * (65535 - da) + dst * (65535 - sa)); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Darken_impl(uint *dest, int length, uint color, const T &coverage) { @@ -1231,6 +1432,29 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Darken_impl(uint *dest, i } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Darken_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +{ + uint sa = color.alpha(); + uint sr = color.red(); + uint sg = color.green(); + uint sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + uint da = d.alpha(); + +#define OP(a, b) darken_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), sr); + uint b = OP( d.blue(), sb); + uint g = OP(d.green(), sg); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_solid_Darken(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) @@ -1239,6 +1463,14 @@ void QT_FASTCALL comp_func_solid_Darken(uint *dest, int length, uint color, uint comp_func_solid_Darken_impl(dest, length, color, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_solid_Darken_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Darken_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Darken_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Darken_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1262,6 +1494,27 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Darken_impl(uint *Q_DECL_RESTRI } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Darken_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + QRgba64 s = src[i]; + + uint da = d.alpha(); + uint sa = s.alpha(); + +#define OP(a, b) darken_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), s.red()); + uint b = OP( d.blue(), s.blue()); + uint g = OP(d.green(), s.green()); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_Darken(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1270,6 +1523,14 @@ void QT_FASTCALL comp_func_Darken(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL comp_func_Darken_impl(dest, src, length, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_Darken_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Darken_impl(dest, src, length, QFullCoverage()); + else + comp_func_Darken_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + /* Dca' = max(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa) Da' = Sa + Da - Sa.Da @@ -1279,6 +1540,11 @@ static inline int lighten_op(int dst, int src, int da, int sa) return qt_div_255(qMax(src * da, dst * sa) + src * (255 - da) + dst * (255 - sa)); } +static inline uint lighten_op_rgb64(uint dst, uint src, uint da, uint sa) +{ + return qt_div_65535(qMax(src * da, dst * sa) + src * (65535 - da) + dst * (65535 - sa)); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Lighten_impl(uint *dest, int length, uint color, const T &coverage) { @@ -1304,6 +1570,29 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Lighten_impl(uint *dest, } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Lighten_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +{ + uint sa = color.alpha(); + uint sr = color.red(); + uint sg = color.green(); + uint sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + uint da = d.alpha(); + +#define OP(a, b) lighten_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), sr); + uint b = OP( d.blue(), sb); + uint g = OP(d.green(), sg); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_solid_Lighten(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) @@ -1312,6 +1601,14 @@ void QT_FASTCALL comp_func_solid_Lighten(uint *dest, int length, uint color, uin comp_func_solid_Lighten_impl(dest, length, color, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_solid_Lighten_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Lighten_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Lighten_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Lighten_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1335,6 +1632,27 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Lighten_impl(uint *Q_DECL_RESTR } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Lighten_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + QRgba64 s = src[i]; + + uint da = d.alpha(); + uint sa = s.alpha(); + +#define OP(a, b) lighten_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), s.red()); + uint b = OP( d.blue(), s.blue()); + uint g = OP(d.green(), s.green()); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_Lighten(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1343,6 +1661,14 @@ void QT_FASTCALL comp_func_Lighten(uint *Q_DECL_RESTRICT dest, const uint *Q_DEC comp_func_Lighten_impl(dest, src, length, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_Lighten_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Lighten_impl(dest, src, length, QFullCoverage()); + else + comp_func_Lighten_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + /* if Sca.Da + Dca.Sa >= Sa.Da Dca' = Sa.Da + Sca.(1 - Da) + Dca.(1 - Sa) @@ -1362,6 +1688,19 @@ static inline int color_dodge_op(int dst, int src, int da, int sa) return qt_div_255(255 * dst_sa / (255 - 255 * src / sa) + temp); } +static inline uint color_dodge_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa) +{ + const qint64 sa_da = sa * da; + const qint64 dst_sa = dst * sa; + const qint64 src_da = src * da; + + const qint64 temp = src * (65535 - da) + dst * (65535 - sa); + if (src_da + dst_sa >= sa_da) + return qt_div_65535(sa_da + temp); + else + return qt_div_65535(65535 * dst_sa / (65535 - 65535 * src / sa) + temp); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorDodge_impl(uint *dest, int length, uint color, const T &coverage) { @@ -1387,6 +1726,29 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorDodge_impl(uint *des } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorDodge_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +{ + uint sa = color.alpha(); + uint sr = color.red(); + uint sg = color.green(); + uint sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + uint da = d.alpha(); + +#define OP(a,b) color_dodge_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), sr); + uint b = OP( d.blue(), sb); + uint g = OP(d.green(), sg); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_solid_ColorDodge(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) @@ -1395,6 +1757,14 @@ void QT_FASTCALL comp_func_solid_ColorDodge(uint *dest, int length, uint color, comp_func_solid_ColorDodge_impl(dest, length, color, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_solid_ColorDodge_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_ColorDodge_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_ColorDodge_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorDodge_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1418,6 +1788,27 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorDodge_impl(uint *Q_DECL_RE } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorDodge_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + QRgba64 s = src[i]; + + uint da = d.alpha(); + uint sa = s.alpha(); + +#define OP(a, b) color_dodge_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), s.red()); + uint b = OP( d.blue(), s.blue()); + uint g = OP(d.green(), s.green()); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_ColorDodge(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1426,6 +1817,14 @@ void QT_FASTCALL comp_func_ColorDodge(uint *Q_DECL_RESTRICT dest, const uint *Q_ comp_func_ColorDodge_impl(dest, src, length, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_ColorDodge_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_ColorDodge_impl(dest, src, length, QFullCoverage()); + else + comp_func_ColorDodge_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + /* if Sca.Da + Dca.Sa <= Sa.Da Dca' = Sca.(1 - Da) + Dca.(1 - Sa) @@ -1445,6 +1844,19 @@ static inline int color_burn_op(int dst, int src, int da, int sa) return qt_div_255(sa * (src_da + dst_sa - sa_da) / src + temp); } +static inline uint color_burn_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa) +{ + const qint64 src_da = src * da; + const qint64 dst_sa = dst * sa; + const qint64 sa_da = sa * da; + + const qint64 temp = src * (65535 - da) + dst * (65535 - sa); + + if (src == 0 || src_da + dst_sa <= sa_da) + return qt_div_65535(temp); + return qt_div_65535(sa * (src_da + dst_sa - sa_da) / src + temp); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorBurn_impl(uint *dest, int length, uint color, const T &coverage) { @@ -1470,6 +1882,29 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorBurn_impl(uint *dest } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorBurn_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +{ + uint sa = color.alpha(); + uint sr = color.red(); + uint sg = color.green(); + uint sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + uint da = d.alpha(); + +#define OP(a, b) color_burn_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), sr); + uint b = OP( d.blue(), sb); + uint g = OP(d.green(), sg); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_solid_ColorBurn(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) @@ -1478,6 +1913,14 @@ void QT_FASTCALL comp_func_solid_ColorBurn(uint *dest, int length, uint color, u comp_func_solid_ColorBurn_impl(dest, length, color, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_solid_ColorBurn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_ColorBurn_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_ColorBurn_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorBurn_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1501,6 +1944,27 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorBurn_impl(uint *Q_DECL_RES } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorBurn_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + QRgba64 s = src[i]; + + uint da = d.alpha(); + uint sa = s.alpha(); + +#define OP(a, b) color_burn_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), s.red()); + uint b = OP( d.blue(), s.blue()); + uint g = OP(d.green(), s.green()); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_ColorBurn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1509,6 +1973,14 @@ void QT_FASTCALL comp_func_ColorBurn(uint *Q_DECL_RESTRICT dest, const uint *Q_D comp_func_ColorBurn_impl(dest, src, length, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_ColorBurn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_ColorBurn_impl(dest, src, length, QFullCoverage()); + else + comp_func_ColorBurn_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + /* if 2.Sca < Sa Dca' = 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa) @@ -1525,6 +1997,16 @@ static inline uint hardlight_op(int dst, int src, int da, int sa) return qt_div_255(sa * da - 2 * (da - dst) * (sa - src) + temp); } +static inline uint hardlight_op_rgb64(uint dst, uint src, uint da, uint sa) +{ + const uint temp = src * (65535 - da) + dst * (65535 - sa); + + if (2 * src < sa) + return qt_div_65535(2 * src * dst + temp); + else + return qt_div_65535(sa * da - 2 * (da - dst) * (sa - src) + temp); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_HardLight_impl(uint *dest, int length, uint color, const T &coverage) { @@ -1550,6 +2032,29 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_HardLight_impl(uint *dest } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_HardLight_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +{ + uint sa = color.alpha(); + uint sr = color.red(); + uint sg = color.green(); + uint sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + uint da = d.alpha(); + +#define OP(a, b) hardlight_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), sr); + uint b = OP( d.blue(), sb); + uint g = OP(d.green(), sg); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_solid_HardLight(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) @@ -1558,6 +2063,14 @@ void QT_FASTCALL comp_func_solid_HardLight(uint *dest, int length, uint color, u comp_func_solid_HardLight_impl(dest, length, color, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_solid_HardLight_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_HardLight_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_HardLight_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_HardLight_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1581,6 +2094,27 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_HardLight_impl(uint *Q_DECL_RES } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_HardLight_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + QRgba64 s = src[i]; + + uint da = d.alpha(); + uint sa = s.alpha(); + +#define OP(a, b) hardlight_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), s.red()); + uint b = OP( d.blue(), s.blue()); + uint g = OP(d.green(), s.green()); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_HardLight(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1589,6 +2123,14 @@ void QT_FASTCALL comp_func_HardLight(uint *Q_DECL_RESTRICT dest, const uint *Q_D comp_func_HardLight_impl(dest, src, length, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_HardLight_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_HardLight_impl(dest, src, length, QFullCoverage()); + else + comp_func_HardLight_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + /* if 2.Sca <= Sa Dca' = Dca.(Sa + (2.Sca - Sa).(1 - Dca/Da)) + Sca.(1 - Da) + Dca.(1 - Sa) @@ -1612,6 +2154,22 @@ static inline int soft_light_op(int dst, int src, int da, int sa) } } +static inline uint soft_light_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa) +{ + const qint64 src2 = src << 1; + const qint64 dst_np = da != 0 ? (65535 * dst) / da : 0; + const qint64 temp = (src * (65535 - da) + dst * (65535 - sa)) * 65535; + const qint64 factor = qint64(65535) * 65535; + + if (src2 < sa) + return (dst * (sa * 65535 + (src2 - sa) * (65535 - dst_np)) + temp) / factor; + else if (4 * dst <= da) + return (dst * sa * 65535 + da * (src2 - sa) * ((((16 * dst_np - 12 * 65535) * dst_np + 3 * factor) * dst_np) / factor) + temp) / factor; + else { + return (dst * sa * 65535 + da * (src2 - sa) * (int(qSqrt(qreal(dst_np * 65535))) - dst_np) + temp) / factor; + } +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_SoftLight_impl(uint *dest, int length, uint color, const T &coverage) { @@ -1637,6 +2195,29 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_SoftLight_impl(uint *dest } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_SoftLight_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +{ + uint sa = color.alpha(); + uint sr = color.red(); + uint sg = color.green(); + uint sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + uint da = d.alpha(); + +#define OP(a, b) soft_light_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), sr); + uint b = OP( d.blue(), sb); + uint g = OP(d.green(), sg); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_solid_SoftLight(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) @@ -1645,6 +2226,14 @@ void QT_FASTCALL comp_func_solid_SoftLight(uint *dest, int length, uint color, u comp_func_solid_SoftLight_impl(dest, length, color, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_solid_SoftLight_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_SoftLight_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_SoftLight_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_SoftLight_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1668,6 +2257,27 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_SoftLight_impl(uint *Q_DECL_RES } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_SoftLight_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + QRgba64 s = src[i]; + + uint da = d.alpha(); + uint sa = s.alpha(); + +#define OP(a, b) soft_light_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), s.red()); + uint b = OP( d.blue(), s.blue()); + uint g = OP(d.green(), s.green()); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_SoftLight(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1676,6 +2286,14 @@ void QT_FASTCALL comp_func_SoftLight(uint *Q_DECL_RESTRICT dest, const uint *Q_D comp_func_SoftLight_impl(dest, src, length, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_SoftLight_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_SoftLight_impl(dest, src, length, QFullCoverage()); + else + comp_func_SoftLight_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + /* Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa) = Sca + Dca - 2.min(Sca.Da, Dca.Sa) @@ -1685,6 +2303,11 @@ static inline int difference_op(int dst, int src, int da, int sa) return src + dst - qt_div_255(2 * qMin(src * da, dst * sa)); } +static inline uint difference_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa) +{ + return src + dst - qt_div_65535(2 * qMin(src * da, dst * sa)); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Difference_impl(uint *dest, int length, uint color, const T &coverage) { @@ -1710,6 +2333,29 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Difference_impl(uint *des } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Difference_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +{ + uint sa = color.alpha(); + uint sr = color.red(); + uint sg = color.green(); + uint sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + uint da = d.alpha(); + +#define OP(a, b) difference_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), sr); + uint b = OP( d.blue(), sb); + uint g = OP(d.green(), sg); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_solid_Difference(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) @@ -1718,6 +2364,14 @@ void QT_FASTCALL comp_func_solid_Difference(uint *dest, int length, uint color, comp_func_solid_Difference_impl(dest, length, color, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_solid_Difference_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Difference_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Difference_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Difference_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1741,6 +2395,27 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Difference_impl(uint *Q_DECL_RE } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Difference_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + QRgba64 s = src[i]; + + uint da = d.alpha(); + uint sa = s.alpha(); + +#define OP(a, b) difference_op_rgb64(a, b, da, sa) + uint r = OP( d.red(), s.red()); + uint b = OP( d.blue(), s.blue()); + uint g = OP(d.green(), s.green()); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_Difference(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1749,6 +2424,14 @@ void QT_FASTCALL comp_func_Difference(uint *Q_DECL_RESTRICT dest, const uint *Q_ comp_func_Difference_impl(dest, src, length, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_Difference_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Difference_impl(dest, src, length, QFullCoverage()); + else + comp_func_Difference_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + /* Dca' = (Sca.Da + Dca.Sa - 2.Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa) */ @@ -1777,6 +2460,30 @@ Q_STATIC_TEMPLATE_FUNCTION inline void QT_FASTCALL comp_func_solid_Exclusion_imp } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void QT_FASTCALL comp_func_solid_Exclusion_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +{ + uint sa = color.alpha(); + uint sr = color.red(); + uint sg = color.green(); + uint sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + uint da = d.alpha(); + +#define OP(a, b) (a + b - qt_div_65535(2*(qint64(a)*b))) + uint r = OP( d.red(), sr); + uint b = OP( d.blue(), sb); + uint g = OP(d.green(), sg); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + + void QT_FASTCALL comp_func_solid_Exclusion(uint *dest, int length, uint color, uint const_alpha) { if (const_alpha == 255) @@ -1785,6 +2492,14 @@ void QT_FASTCALL comp_func_solid_Exclusion(uint *dest, int length, uint color, u comp_func_solid_Exclusion_impl(dest, length, color, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_solid_Exclusion_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Exclusion_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Exclusion_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + template <typename T> Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Exclusion_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1808,6 +2523,27 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Exclusion_impl(uint *Q_DECL_RES } } +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Exclusion_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba64 d = dest[i]; + QRgba64 s = src[i]; + + uint da = d.alpha(); + uint sa = s.alpha(); + +#define OP(a, b) (a + b - ((qint64(a)*b) >> 15)) + uint r = OP( d.red(), s.red()); + uint b = OP( d.blue(), s.blue()); + uint g = OP(d.green(), s.green()); + uint a = mix_alpha_rgb64(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba64(r, g, b, a)); + } +} + void QT_FASTCALL comp_func_Exclusion(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1816,6 +2552,14 @@ void QT_FASTCALL comp_func_Exclusion(uint *Q_DECL_RESTRICT dest, const uint *Q_D comp_func_Exclusion_impl(dest, src, length, QPartialCoverage(const_alpha)); } +void QT_FASTCALL comp_func_Exclusion_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Exclusion_impl(dest, src, length, QFullCoverage()); + else + comp_func_Exclusion_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + void QT_FASTCALL rasterop_solid_SourceOrDestination(uint *dest, int length, uint color, @@ -2177,8 +2921,17 @@ CompositionFunctionSolid64 qt_functionForModeSolid64_C[] = { comp_func_solid_DestinationAtop_rgb64, comp_func_solid_XOR_rgb64, comp_func_solid_Plus_rgb64, - 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, + comp_func_solid_Multiply_rgb64, + comp_func_solid_Screen_rgb64, + comp_func_solid_Overlay_rgb64, + comp_func_solid_Darken_rgb64, + comp_func_solid_Lighten_rgb64, + comp_func_solid_ColorDodge_rgb64, + comp_func_solid_ColorBurn_rgb64, + comp_func_solid_HardLight_rgb64, + comp_func_solid_SoftLight_rgb64, + comp_func_solid_Difference_rgb64, + comp_func_solid_Exclusion_rgb64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -2238,8 +2991,17 @@ CompositionFunction64 qt_functionForMode64_C[] = { comp_func_DestinationAtop_rgb64, comp_func_XOR_rgb64, comp_func_Plus_rgb64, - 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, + comp_func_Multiply_rgb64, + comp_func_Screen_rgb64, + comp_func_Overlay_rgb64, + comp_func_Darken_rgb64, + comp_func_Lighten_rgb64, + comp_func_ColorDodge_rgb64, + comp_func_ColorBurn_rgb64, + comp_func_HardLight_rgb64, + comp_func_SoftLight_rgb64, + comp_func_Difference_rgb64, + comp_func_Exclusion_rgb64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; diff --git a/src/gui/painting/qcoregraphics.mm b/src/gui/painting/qcoregraphics.mm index c4fb8afc64..e1601d87dc 100644 --- a/src/gui/painting/qcoregraphics.mm +++ b/src/gui/painting/qcoregraphics.mm @@ -66,7 +66,7 @@ CGImageRef qt_mac_toCGImageMask(const QImage &image) static const auto deleter = [](void *image, const void *, size_t) { delete static_cast<QImage *>(image); }; QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(new QImage(image), image.bits(), - image.byteCount(), deleter); + image.sizeInBytes(), deleter); return CGImageMaskCreate(image.width(), image.height(), 8, image.depth(), image.bytesPerLine(), dataProvider, NULL, false); @@ -110,6 +110,7 @@ NSImage *qt_mac_create_nsimage(const QPixmap &pm) QImage image = pm.toImage(); CGImageRef cgImage = qt_mac_toCGImage(image); NSImage *nsImage = qt_mac_cgimage_to_nsimage(cgImage); + nsImage.size = (pm.size() / pm.devicePixelRatioF()).toCGSize(); CGImageRelease(cgImage); return nsImage; } diff --git a/src/gui/painting/qcosmeticstroker.cpp b/src/gui/painting/qcosmeticstroker.cpp index bcb243db6a..436a62d486 100644 --- a/src/gui/painting/qcosmeticstroker.cpp +++ b/src/gui/painting/qcosmeticstroker.cpp @@ -292,7 +292,7 @@ void QCosmeticStroker::setup() color = multiplyAlpha256(state->penData.solid.color, opacity).toArgb32(); QRasterBuffer *buffer = state->penData.rasterBuffer; pixels = (uint *)buffer->buffer(); - ppl = buffer->bytesPerLine()>>2; + ppl = buffer->stride<quint32>(); } // line drawing produces different results with different clips, so diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 6c25710271..5ec570a5db 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -55,10 +55,13 @@ #endif #include <private/qguiapplication_p.h> #include <private/qrgba64_p.h> +#include <qloggingcategory.h> #include <qmath.h> QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcQtGuiDrawHelper, "qt.gui.drawhelper") + #define MASK(src, a) src = BYTE_MUL(src, a) /* @@ -1042,7 +1045,7 @@ QPixelLayout qPixelLayouts[QImage::NImageFormats] = { { 10, 20, 10, 10, 10, 0, 2, 30, true, QPixelLayout::BPP32, convertA2RGB30PMToARGB32PM<PixelOrderBGR>, convertA2RGB30PMFromARGB32PM<PixelOrderBGR>, convertRGB30FromRGB32<PixelOrderBGR>, convertA2RGB30PMToARGB64PM<PixelOrderBGR> }, // Format_A2BGR30_Premultiplied { 10, 0, 10, 10, 10, 20, 0, 30, false, QPixelLayout::BPP32, convertA2RGB30PMToARGB32PM<PixelOrderRGB>, convertRGB30FromARGB32PM<PixelOrderRGB>, convertRGB30FromRGB32<PixelOrderRGB>, convertA2RGB30PMToARGB64PM<PixelOrderRGB> }, // Format_RGB30 { 10, 0, 10, 10, 10, 20, 2, 30, true, QPixelLayout::BPP32, convertA2RGB30PMToARGB32PM<PixelOrderRGB>, convertA2RGB30PMFromARGB32PM<PixelOrderRGB>, convertRGB30FromRGB32<PixelOrderRGB>, convertA2RGB30PMToARGB64PM<PixelOrderRGB> }, // Format_A2RGB30_Premultiplied - { 0, 0, 0, 0, 0, 0, 8, 0, false, QPixelLayout::BPP8, convertAlpha8ToRGB32, convertAlpha8FromARGB32PM, 0, convertAlpha8ToRGB64 }, // Format_Alpha8 + { 0, 0, 0, 0, 0, 0, 8, 0, true, QPixelLayout::BPP8, convertAlpha8ToRGB32, convertAlpha8FromARGB32PM, 0, convertAlpha8ToRGB64 }, // Format_Alpha8 { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPP8, convertGrayscale8ToRGB32, convertGrayscale8FromARGB32PM, convertGrayscale8FromRGB32, convertGrayscale8ToRGB64 } // Format_Grayscale8 }; @@ -1942,10 +1945,14 @@ static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper(u const uint *s1 = (const uint *)image.scanLine(y1); const uint *s2 = (const uint *)image.scanLine(y2); - int disty = (fy & 0x0000ffff) >> 8; - int idisty = 256 - disty; - int x = fx >> 16; - int length = end - b; + const int disty = (fy & 0x0000ffff) >> 8; + const int idisty = 256 - disty; + const int length = end - b; + + // The intermediate buffer is generated in the positive direction + const int adjust = (fdx < 0) ? fdx * length : 0; + const int offset = (fx + adjust) >> 16; + int x = offset; // The idea is first to do the interpolation between the row s1 and the row s2 // into an intermediate buffer, then we interpolate between two pixel of this buffer. @@ -1955,7 +1962,7 @@ static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper(u // +1 for the last pixel to interpolate with, and +1 for rounding errors. quint32 intermediate_buffer[2][buffer_size + 2]; // count is the size used in the intermediate_buffer. - int count = (qint64(length) * fdx + fixed_scale - 1) / fixed_scale + 2; + int count = (qint64(length) * qAbs(fdx) + fixed_scale - 1) / fixed_scale + 2; Q_ASSERT(count <= buffer_size + 2); //length is supposed to be <= buffer_size and data->m11 < 1 in this case int f = 0; int lim = count; @@ -2056,9 +2063,10 @@ static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper(u intermediate_buffer[1][f] = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff; x++; } + // Now interpolate the values from the intermediate_buffer to get the final result. - fx &= fixed_scale - 1; - Q_ASSERT((fx >> 16) == 0); + fx -= offset * fixed_scale; // Switch to intermediate buffer coordinates + while (b < end) { int x1 = (fx >> 16); int x2 = x1 + 1; @@ -2375,7 +2383,7 @@ static void QT_FASTCALL fetchTransformedBilinearARGB32PM_fast_rotate_helper(uint __m128i v_fy = _mm_setr_epi32(fy, fy + fdy, fy + fdy + fdy, fy + fdy + fdy + fdy); const uchar *textureData = image.imageData; - const int bytesPerLine = image.bytesPerLine; + const qssize_t bytesPerLine = image.bytesPerLine; const __m128i vbpl = _mm_shufflelo_epi16(_mm_cvtsi32_si128(bytesPerLine/4), _MM_SHUFFLE(0, 0, 0, 0)); while (b < boundedEnd - 3) { @@ -2588,14 +2596,14 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c fy -= half_point; if (fdy == 0) { // simple scale, no rotation or shear - if (fdx <= fixed_scale && fdx > 0) { - // simple scale up on X without mirroring + if (qAbs(fdx) <= fixed_scale) { + // simple scale up on X bilinearFastTransformHelperARGB32PM[tiled][SimpleUpscaleTransform](b, end, data->texture, fx, fy, fdx, fdy); - } else if ((fdx < 0 && fdx > -(fixed_scale / 8)) || qAbs(data->m22) < qreal(1./8.)) { - // scale up more than 8x (on either Y or on X mirrored) + } else if (qAbs(data->m22) < qreal(1./8.)) { + // scale up more than 8x (on Y) bilinearFastTransformHelperARGB32PM[tiled][UpscaleTransform](b, end, data->texture, fx, fy, fdx, fdy); } else { - // scale down on X (or up on X mirrored less than 8x) + // scale down on X bilinearFastTransformHelperARGB32PM[tiled][DownscaleTransform](b, end, data->texture, fx, fy, fdx, fdy); } } else { // rotation or shear @@ -2658,6 +2666,245 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c return buffer; } +template<TextureBlendType blendType, QPixelLayout::BPP bpp> +static void QT_FASTCALL fetchTransformedBilinear_simple_upscale_helper(uint *b, uint *end, const QTextureData &image, + int &fx, int &fy, int fdx, int /*fdy*/) +{ + const QPixelLayout *layout = &qPixelLayouts[image.format]; + const QVector<QRgb> *clut = image.colorTable; + Q_ASSERT(bpp == QPixelLayout::BPPNone || bpp == layout->bpp); + // When templated 'fetch' should be inlined at compile time: + const FetchPixelsFunc fetch = (bpp == QPixelLayout::BPPNone) ? qFetchPixels[layout->bpp] : fetchPixels<bpp>; + const ConvertFunc convertToARGB32PM = layout->convertToARGB32PM; + + int y1 = (fy >> 16); + int y2; + fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2); + const uchar *s1 = image.scanLine(y1); + const uchar *s2 = image.scanLine(y2); + + const int disty = (fy & 0x0000ffff) >> 8; + const int idisty = 256 - disty; + const int length = end - b; + + // The intermediate buffer is generated in the positive direction + const int adjust = (fdx < 0) ? fdx * length : 0; + const int offset = (fx + adjust) >> 16; + int x = offset; + + // The idea is first to do the interpolation between the row s1 and the row s2 + // into an intermediate buffer, then we interpolate between two pixel of this buffer. + // +1 for the last pixel to interpolate with, and +1 for rounding errors. + uint buf1[buffer_size + 2]; + uint buf2[buffer_size + 2]; + const uint *ptr1; + const uint *ptr2; + + int count = (qint64(length) * qAbs(fdx) + fixed_scale - 1) / fixed_scale + 2; + Q_ASSERT(count <= buffer_size + 2); //length is supposed to be <= buffer_size and data->m11 < 1 in this case + + if (blendType == BlendTransformedBilinearTiled) { + x %= image.width; + if (x < 0) + x += image.width; + int len1 = qMin(count, image.width - x); + int len2 = qMin(x, count - len1); + + ptr1 = fetch(buf1, s1, x, len1); + ptr1 = convertToARGB32PM(buf1, ptr1, len1, clut, 0); + ptr2 = fetch(buf2, s2, x, len1); + ptr2 = convertToARGB32PM(buf2, ptr2, len1, clut, 0); + for (int i = 0; i < len1; ++i) { + uint t = ptr1[i]; + uint b = ptr2[i]; + buf1[i] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff; + buf2[i] = ((((t >> 8) & 0xff00ff) * idisty + ((b >> 8) & 0xff00ff) * disty) >> 8) & 0xff00ff; + } + + if (len2) { + ptr1 = fetch(buf1 + len1, s1, 0, len2); + ptr1 = convertToARGB32PM(buf1 + len1, ptr1, len2, clut, 0); + ptr2 = fetch(buf2 + len1, s2, 0, len2); + ptr2 = convertToARGB32PM(buf2 + len1, ptr2, len2, clut, 0); + for (int i = 0; i < len2; ++i) { + uint t = ptr1[i]; + uint b = ptr2[i]; + buf1[i + len1] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff; + buf2[i + len1] = ((((t >> 8) & 0xff00ff) * idisty + ((b >> 8) & 0xff00ff) * disty) >> 8) & 0xff00ff; + } + } + for (int i = image.width; i < count; ++i) { + buf1[i] = buf1[i - image.width]; + buf2[i] = buf2[i - image.width]; + } + } else { + int start = qMax(x, image.x1); + int end = qMin(x + count, image.x2); + int len = qMax(1, end - start); + int leading = start - x; + + ptr1 = fetch(buf1 + leading, s1, start, len); + ptr1 = convertToARGB32PM(buf1 + leading, ptr1, len, clut, 0); + ptr2 = fetch(buf2 + leading, s2, start, len); + ptr2 = convertToARGB32PM(buf2 + leading, ptr2, len, clut, 0); + + for (int i = 0; i < len; ++i) { + uint t = ptr1[i]; + uint b = ptr2[i]; + buf1[i + leading] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff; + buf2[i + leading] = ((((t >> 8) & 0xff00ff) * idisty + ((b >> 8) & 0xff00ff) * disty) >> 8) & 0xff00ff; + } + + for (int i = 0; i < leading; ++i) { + buf1[i] = buf1[leading]; + buf2[i] = buf2[leading]; + } + for (int i = leading + len; i < count; ++i) { + buf1[i] = buf1[i - 1]; + buf2[i] = buf2[i - 1]; + } + } + + // Now interpolate the values from the intermediate_buffer to get the final result. + fx -= offset * fixed_scale; // Switch to intermediate buffer coordinates + + while (b < end) { + int x1 = (fx >> 16); + int x2 = x1 + 1; + Q_ASSERT(x1 >= 0); + Q_ASSERT(x2 < count); + + int distx = (fx & 0x0000ffff) >> 8; + int idistx = 256 - distx; + int rb = ((buf1[x1] * idistx + buf1[x2] * distx) >> 8) & 0xff00ff; + int ag = (buf2[x1] * idistx + buf2[x2] * distx) & 0xff00ff00; + *b++ = rb | ag; + fx += fdx; + } +} + + +typedef void (QT_FASTCALL *BilinearFastTransformFetcher)(uint *buf1, uint *buf2, const int len, const QTextureData &image, + int fx, int fy, const int fdx, const int fdy); + +template<TextureBlendType blendType, QPixelLayout::BPP bpp> +static void QT_FASTCALL fetchTransformedBilinear_fetcher(uint *buf1, uint *buf2, const int len, const QTextureData &image, + int fx, int fy, const int fdx, const int fdy) +{ + const QPixelLayout &layout = qPixelLayouts[image.format]; + Q_ASSERT(bpp == QPixelLayout::BPPNone || bpp == layout.bpp); + // When templated 'fetch1' should be inlined at compile time: + const FetchPixelFunc fetch1 = (bpp == QPixelLayout::BPPNone) ? qFetchPixel[layout.bpp] : fetchPixel<bpp>; + if (fdy == 0) { + int y1 = (fy >> 16); + int y2; + fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2); + const uchar *s1 = image.scanLine(y1); + const uchar *s2 = image.scanLine(y2); + + int i = 0; + if (blendType == BlendTransformedBilinear) { + for (; i < len; ++i) { + int x1 = (fx >> 16); + int x2; + fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2); + if (x1 != x2) + break; + buf1[i * 2 + 0] = buf1[i * 2 + 1] = fetch1(s1, x1); + buf2[i * 2 + 0] = buf2[i * 2 + 1] = fetch1(s2, x1); + fx += fdx; + } + int fastLen = len; + if (fdx > 0) + fastLen = qMin(fastLen, int((qint64(image.x2 - 1) * fixed_scale - fx) / fdx)); + else if (fdx < 0) + fastLen = qMin(fastLen, int((qint64(image.x1) * fixed_scale - fx) / fdx)); + + for (; i < fastLen; ++i) { + int x = (fx >> 16); + buf1[i * 2 + 0] = fetch1(s1, x); + buf1[i * 2 + 1] = fetch1(s1, x + 1); + buf2[i * 2 + 0] = fetch1(s2, x); + buf2[i * 2 + 1] = fetch1(s2, x + 1); + fx += fdx; + } + } + + for (; i < len; ++i) { + int x1 = (fx >> 16); + int x2; + fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2); + buf1[i * 2 + 0] = fetch1(s1, x1); + buf1[i * 2 + 1] = fetch1(s1, x2); + buf2[i * 2 + 0] = fetch1(s2, x1); + buf2[i * 2 + 1] = fetch1(s2, x2); + fx += fdx; + } + } else { + int i = 0; + if (blendType == BlendTransformedBilinear) { + for (; i < len; ++i) { + int x1 = (fx >> 16); + int x2; + int y1 = (fy >> 16); + int y2; + fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2); + fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2); + if (x1 != x2 && y1 != y2) + break; + const uchar *s1 = image.scanLine(y1); + const uchar *s2 = image.scanLine(y2); + buf1[i * 2 + 0] = fetch1(s1, x1); + buf1[i * 2 + 1] = fetch1(s1, x2); + buf2[i * 2 + 0] = fetch1(s2, x1); + buf2[i * 2 + 1] = fetch1(s2, x2); + fx += fdx; + fy += fdy; + } + int fastLen = len; + if (fdx > 0) + fastLen = qMin(fastLen, int((qint64(image.x2 - 1) * fixed_scale - fx) / fdx)); + else if (fdx < 0) + fastLen = qMin(fastLen, int((qint64(image.x1) * fixed_scale - fx) / fdx)); + if (fdy > 0) + fastLen = qMin(fastLen, int((qint64(image.y2 - 1) * fixed_scale - fy) / fdy)); + else if (fdy < 0) + fastLen = qMin(fastLen, int((qint64(image.y1) * fixed_scale - fy) / fdy)); + + for (; i < fastLen; ++i) { + int x = (fx >> 16); + int y = (fy >> 16); + const uchar *s1 = image.scanLine(y); + const uchar *s2 = s1 + image.bytesPerLine; + buf1[i * 2 + 0] = fetch1(s1, x); + buf1[i * 2 + 1] = fetch1(s1, x + 1); + buf2[i * 2 + 0] = fetch1(s2, x); + buf2[i * 2 + 1] = fetch1(s2, x + 1); + fx += fdx; + fy += fdy; + } + } + + for (; i < len; ++i) { + int x1 = (fx >> 16); + int x2; + int y1 = (fy >> 16); + int y2; + fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2); + fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2); + + const uchar *s1 = image.scanLine(y1); + const uchar *s2 = image.scanLine(y2); + buf1[i * 2 + 0] = fetch1(s1, x1); + buf1[i * 2 + 1] = fetch1(s1, x2); + buf2[i * 2 + 0] = fetch1(s2, x1); + buf2[i * 2 + 1] = fetch1(s2, x2); + fx += fdx; + fy += fdy; + } + } +} + // blendType = BlendTransformedBilinear or BlendTransformedBilinearTiled template<TextureBlendType blendType, QPixelLayout::BPP bpp> static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Operator *, @@ -2665,19 +2912,7 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper { const QPixelLayout *layout = &qPixelLayouts[data->texture.format]; const QVector<QRgb> *clut = data->texture.colorTable; - if (bpp != QPixelLayout::BPPNone) // Like this to not ICE on GCC 5.3.1 - Q_ASSERT(layout->bpp == bpp); - // When templated 'fetch' should be inlined at compile time: - const FetchPixelsFunc fetch = (bpp == QPixelLayout::BPPNone) ? qFetchPixels[layout->bpp] : FetchPixelsFunc(fetchPixels<bpp>); - const FetchPixelFunc fetch1 = (bpp == QPixelLayout::BPPNone) ? qFetchPixel[layout->bpp] : FetchPixelFunc(fetchPixel<bpp>); - - int image_width = data->texture.width; - int image_height = data->texture.height; - - int image_x1 = data->texture.x1; - int image_y1 = data->texture.y1; - int image_x2 = data->texture.x2 - 1; - int image_y2 = data->texture.y2 - 1; + Q_ASSERT(bpp == QPixelLayout::BPPNone || layout->bpp == bpp); const qreal cx = x + qreal(0.5); const qreal cy = y + qreal(0.5); @@ -2693,203 +2928,80 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper fx -= half_point; fy -= half_point; - if (fdy == 0) { //simple scale, no rotation - int y1 = (fy >> 16); - int y2; - fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); - const uchar *s1 = data->texture.scanLine(y1); - const uchar *s2 = data->texture.scanLine(y2); - - if (fdx <= fixed_scale && fdx > 0) { // scale up on X - int disty = (fy & 0x0000ffff) >> 8; - int idisty = 256 - disty; - int x = fx >> 16; - - // The idea is first to do the interpolation between the row s1 and the row s2 - // into an intermediate buffer, then we interpolate between two pixel of this buffer. - // +1 for the last pixel to interpolate with, and +1 for rounding errors. - uint buf1[buffer_size + 2]; - uint buf2[buffer_size + 2]; - const uint *ptr1; - const uint *ptr2; - - int count = (qint64(length) * fdx + fixed_scale - 1) / fixed_scale + 2; - Q_ASSERT(count <= buffer_size + 2); //length is supposed to be <= buffer_size and data->m11 < 1 in this case - - if (blendType == BlendTransformedBilinearTiled) { - x %= image_width; - if (x < 0) - x += image_width; - int len1 = qMin(count, image_width - x); - int len2 = qMin(x, count - len1); - - ptr1 = fetch(buf1, s1, x, len1); - ptr1 = layout->convertToARGB32PM(buf1, ptr1, len1, clut, 0); - ptr2 = fetch(buf2, s2, x, len1); - ptr2 = layout->convertToARGB32PM(buf2, ptr2, len1, clut, 0); - for (int i = 0; i < len1; ++i) { - uint t = ptr1[i]; - uint b = ptr2[i]; - buf1[i] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff; - buf2[i] = ((((t >> 8) & 0xff00ff) * idisty + ((b >> 8) & 0xff00ff) * disty) >> 8) & 0xff00ff; - } - - if (len2) { - ptr1 = fetch(buf1 + len1, s1, 0, len2); - ptr1 = layout->convertToARGB32PM(buf1 + len1, ptr1, len2, clut, 0); - ptr2 = fetch(buf2 + len1, s2, 0, len2); - ptr2 = layout->convertToARGB32PM(buf2 + len1, ptr2, len2, clut, 0); - for (int i = 0; i < len2; ++i) { - uint t = ptr1[i]; - uint b = ptr2[i]; - buf1[i + len1] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff; - buf2[i + len1] = ((((t >> 8) & 0xff00ff) * idisty + ((b >> 8) & 0xff00ff) * disty) >> 8) & 0xff00ff; - } - } - for (int i = image_width; i < count; ++i) { - buf1[i] = buf1[i - image_width]; - buf2[i] = buf2[i - image_width]; - } - } else { - int start = qMax(x, image_x1); - int end = qMin(x + count, image_x2 + 1); - int len = qMax(1, end - start); - int leading = start - x; - - ptr1 = fetch(buf1 + leading, s1, start, len); - ptr1 = layout->convertToARGB32PM(buf1 + leading, ptr1, len, clut, 0); - ptr2 = fetch(buf2 + leading, s2, start, len); - ptr2 = layout->convertToARGB32PM(buf2 + leading, ptr2, len, clut, 0); - - for (int i = 0; i < len; ++i) { - uint t = ptr1[i]; - uint b = ptr2[i]; - buf1[i + leading] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff; - buf2[i + leading] = ((((t >> 8) & 0xff00ff) * idisty + ((b >> 8) & 0xff00ff) * disty) >> 8) & 0xff00ff; - } - - for (int i = 0; i < leading; ++i) { - buf1[i] = buf1[leading]; - buf2[i] = buf2[leading]; - } - for (int i = leading + len; i < count; ++i) { - buf1[i] = buf1[i - 1]; - buf2[i] = buf2[i - 1]; - } - } - - // Now interpolate the values from the intermediate_buffer to get the final result. - fx &= fixed_scale - 1; - Q_ASSERT((fx >> 16) == 0); - for (int i = 0; i < length; ++i) { - int x1 = (fx >> 16); - int x2 = x1 + 1; - Q_ASSERT(x1 >= 0); - Q_ASSERT(x2 < count); - - int distx = (fx & 0x0000ffff) >> 8; - int idistx = 256 - distx; - int rb = ((buf1[x1] * idistx + buf1[x2] * distx) >> 8) & 0xff00ff; - int ag = (buf2[x1] * idistx + buf2[x2] * distx) & 0xff00ff00; - buffer[i] = rb | ag; - fx += fdx; - } + if (fdy == 0) { // simple scale, no rotation or shear + if (qAbs(fdx) <= fixed_scale) { // scale up on X + fetchTransformedBilinear_simple_upscale_helper<blendType, bpp>(buffer, buffer + length, data->texture, fx, fy, fdx, fdy); } else { + const BilinearFastTransformFetcher fetcher = fetchTransformedBilinear_fetcher<blendType,bpp>; + uint buf1[buffer_size]; uint buf2[buffer_size]; uint *b = buffer; while (length) { int len = qMin(length, buffer_size / 2); - int fracX = fx; - for (int i = 0; i < len; ++i) { - int x1 = (fx >> 16); - int x2; - fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); - buf1[i * 2 + 0] = fetch1(s1, x1); - buf1[i * 2 + 1] = fetch1(s1, x2); - buf2[i * 2 + 0] = fetch1(s2, x1); - buf2[i * 2 + 1] = fetch1(s2, x2); - fx += fdx; - } + fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, 0); layout->convertToARGB32PM(buf1, buf1, len * 2, clut, 0); layout->convertToARGB32PM(buf2, buf2, len * 2, clut, 0); - if ((fdx < 0 && fdx > -(fixed_scale / 8)) || qAbs(data->m22) < qreal(1./8.)) { // scale up more than 8x + if (qAbs(data->m22) < qreal(1./8.)) { // scale up more than 8x (on Y) int disty = (fy & 0x0000ffff) >> 8; for (int i = 0; i < len; ++i) { - int distx = (fracX & 0x0000ffff) >> 8; + int distx = (fx & 0x0000ffff) >> 8; b[i] = interpolate_4_pixels(buf1 + i * 2, buf2 + i * 2, distx, disty); - fracX += fdx; + fx += fdx; } - } else { //scale down + } else { int disty = ((fy & 0x0000ffff) + 0x0800) >> 12; for (int i = 0; i < len; ++i) { uint tl = buf1[i * 2 + 0]; uint tr = buf1[i * 2 + 1]; uint bl = buf2[i * 2 + 0]; uint br = buf2[i * 2 + 1]; - int distx = ((fracX & 0x0000ffff) + 0x0800) >> 12; + int distx = ((fx & 0x0000ffff) + 0x0800) >> 12; b[i] = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty); - fracX += fdx; + fx += fdx; } } length -= len; b += len; } } - } else { //rotation + } else { // rotation or shear + const BilinearFastTransformFetcher fetcher = fetchTransformedBilinear_fetcher<blendType,bpp>; + uint buf1[buffer_size]; uint buf2[buffer_size]; uint *b = buffer; - while (length) { int len = qMin(length, buffer_size / 2); - int fracX = fx; - int fracY = fy; - for (int i = 0; i < len; ++i) { - int x1 = (fx >> 16); - int x2; - int y1 = (fy >> 16); - int y2; - fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); - fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); - - const uchar *s1 = data->texture.scanLine(y1); - const uchar *s2 = data->texture.scanLine(y2); - buf1[i * 2 + 0] = fetch1(s1, x1); - buf1[i * 2 + 1] = fetch1(s1, x2); - buf2[i * 2 + 0] = fetch1(s2, x1); - buf2[i * 2 + 1] = fetch1(s2, x2); - fx += fdx; - fy += fdy; - } + fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, fdy); layout->convertToARGB32PM(buf1, buf1, len * 2, clut, 0); layout->convertToARGB32PM(buf2, buf2, len * 2, clut, 0); - if (qAbs(data->m11) < qreal(1./8.) || qAbs(data->m22) < qreal(1./8.) ) { - //if we are zooming more than 8 times, we use 8bit precision for the position. + if (qAbs(data->m11) < qreal(1./8.)|| qAbs(data->m22) < qreal(1./8.)) { + // If we are zooming more than 8 times, we use 8bit precision for the position. for (int i = 0; i < len; ++i) { - int distx = (fracX & 0x0000ffff) >> 8; - int disty = (fracY & 0x0000ffff) >> 8; + int distx = (fx & 0x0000ffff) >> 8; + int disty = (fy & 0x0000ffff) >> 8; b[i] = interpolate_4_pixels(buf1 + i * 2, buf2 + i * 2, distx, disty); - fracX += fdx; - fracY += fdy; + fx += fdx; + fy += fdy; } } else { - //we are zooming less than 8x, use 4bit precision + // We are zooming less than 8x, use 4bit precision for (int i = 0; i < len; ++i) { uint tl = buf1[i * 2 + 0]; uint tr = buf1[i * 2 + 1]; uint bl = buf2[i * 2 + 0]; uint br = buf2[i * 2 + 1]; - int distx = ((fracX & 0x0000ffff) + 0x0800) >> 12; - int disty = ((fracY & 0x0000ffff) + 0x0800) >> 12; + int distx = ((fx & 0x0000ffff) + 0x0800) >> 12; + int disty = ((fy & 0x0000ffff) + 0x0800) >> 12; b[i] = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty); - fracX += fdx; - fracY += fdy; + fx += fdx; + fy += fdy; } } @@ -2898,6 +3010,11 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper } } } else { + // When templated 'fetch' should be inlined at compile time: + const FetchPixelFunc fetch1 = (bpp == QPixelLayout::BPPNone) ? qFetchPixel[layout->bpp] : fetchPixel<bpp>; + + const QTextureData &image = data->texture; + const qreal fdx = data->m11; const qreal fdy = data->m12; const qreal fdw = data->m13; @@ -2928,8 +3045,8 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper distxs[i] = int((px - x1) * 256); distys[i] = int((py - y1) * 256); - fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); - fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); + fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2); + fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2); const uchar *s1 = data->texture.scanLine(y1); const uchar *s2 = data->texture.scanLine(y2); @@ -2970,21 +3087,9 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co const QPixelLayout *layout = &qPixelLayouts[data->texture.format]; const QVector<QRgb> *clut = data->texture.colorTable; - int image_width = data->texture.width; - int image_height = data->texture.height; - - int image_x1 = data->texture.x1; - int image_y1 = data->texture.y1; - int image_x2 = data->texture.x2 - 1; - int image_y2 = data->texture.y2 - 1; - const qreal cx = x + qreal(0.5); const qreal cy = y + qreal(0.5); - const qreal fdx = data->m11; - const qreal fdy = data->m12; - const qreal fdw = data->m13; - if (data->fast_matrix) { // The increment pr x in the scanline int fdx = (int)(data->m11 * fixed_scale); @@ -2996,14 +3101,13 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co fx -= half_point; fy -= half_point; + const BilinearFastTransformFetcher fetcher = + (layout->bpp == QPixelLayout::BPP32) + ? fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP32> + : fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPPNone>; + if (fdy == 0) { //simple scale, no rotation - int y1 = (fy >> 16); - int y2; - fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); - const uchar *s1 = data->texture.scanLine(y1); - const uchar *s2 = data->texture.scanLine(y2); - FetchPixelFunc fetch = qFetchPixel[layout->bpp]; uint sbuf1[buffer_size]; uint sbuf2[buffer_size]; quint64 buf1[buffer_size]; @@ -3011,84 +3115,19 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co QRgba64 *b = buffer; while (length) { int len = qMin(length, buffer_size / 2); - int fracX = fx; - int i = 0; int disty = (fy & 0x0000ffff); #if defined(__SSE2__) const __m128i vdy = _mm_set1_epi16(disty); const __m128i vidy = _mm_set1_epi16(0x10000 - disty); - if (blendType != BlendTransformedBilinearTiled && layout->bpp == QPixelLayout::BPP32) { - for (; i < len; ++i) { - int x1 = (fx >> 16); - int x2; - fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); - if (x1 != x2) - break; - sbuf1[i * 2 + 0] = sbuf1[i * 2 + 1] = ((const uint*)s1)[x1]; - sbuf2[i * 2 + 0] = sbuf2[i * 2 + 1] = ((const uint*)s2)[x1]; - fx += fdx; - } - int fastLen; - if (fdx > 0) - fastLen = qMin(len, int((image_x2 - (fx >> 16)) / data->m11)); - else - fastLen = qMin(len, int((image_x1 - (fx >> 16)) / data->m11)); - fastLen -= 3; - - const __m128i v_fdx = _mm_set1_epi32(fdx*4); - __m128i v_fx = _mm_setr_epi32(fx, fx + fdx, fx + fdx + fdx, fx + fdx + fdx + fdx); - for (; i < fastLen; i += 4) { - int offset = _mm_extract_epi16(v_fx, 1); - sbuf1[i * 2 + 0] = ((const uint*)s1)[offset]; - sbuf1[i * 2 + 1] = ((const uint*)s1)[offset + 1]; - sbuf2[i * 2 + 0] = ((const uint*)s2)[offset]; - sbuf2[i * 2 + 1] = ((const uint*)s2)[offset + 1]; - offset = _mm_extract_epi16(v_fx, 3); - sbuf1[i * 2 + 2] = ((const uint*)s1)[offset]; - sbuf1[i * 2 + 3] = ((const uint*)s1)[offset + 1]; - sbuf2[i * 2 + 2] = ((const uint*)s2)[offset]; - sbuf2[i * 2 + 3] = ((const uint*)s2)[offset + 1]; - offset = _mm_extract_epi16(v_fx, 5); - sbuf1[i * 2 + 4] = ((const uint*)s1)[offset]; - sbuf1[i * 2 + 5] = ((const uint*)s1)[offset + 1]; - sbuf2[i * 2 + 4] = ((const uint*)s2)[offset]; - sbuf2[i * 2 + 5] = ((const uint*)s2)[offset + 1]; - offset = _mm_extract_epi16(v_fx, 7); - sbuf1[i * 2 + 6] = ((const uint*)s1)[offset]; - sbuf1[i * 2 + 7] = ((const uint*)s1)[offset + 1]; - sbuf2[i * 2 + 6] = ((const uint*)s2)[offset]; - sbuf2[i * 2 + 7] = ((const uint*)s2)[offset + 1]; - v_fx = _mm_add_epi32(v_fx, v_fdx); - } - fx = _mm_cvtsi128_si32(v_fx); - } #endif - for (; i < len; ++i) { - int x1 = (fx >> 16); - int x2; - fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); - - if (layout->bpp == QPixelLayout::BPP32) { - sbuf1[i * 2 + 0] = ((const uint*)s1)[x1]; - sbuf1[i * 2 + 1] = ((const uint*)s1)[x2]; - sbuf2[i * 2 + 0] = ((const uint*)s2)[x1]; - sbuf2[i * 2 + 1] = ((const uint*)s2)[x2]; - - } else { - sbuf1[i * 2 + 0] = fetch(s1, x1); - sbuf1[i * 2 + 1] = fetch(s1, x2); - sbuf2[i * 2 + 0] = fetch(s2, x1); - sbuf2[i * 2 + 1] = fetch(s2, x2); - } + fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy); - fx += fdx; - } layout->convertToARGB64PM((QRgba64 *)buf1, sbuf1, len * 2, clut, 0); if (disty) layout->convertToARGB64PM((QRgba64 *)buf2, sbuf2, len * 2, clut, 0); for (int i = 0; i < len; ++i) { - int distx = (fracX & 0x0000ffff); + int distx = (fx & 0x0000ffff); #if defined(__SSE2__) __m128i vt = _mm_loadu_si128((const __m128i*)(buf1 + i*2)); if (disty) { @@ -3107,13 +3146,12 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co #else b[i] = interpolate_4_pixels_rgb64((QRgba64 *)buf1 + i*2, (QRgba64 *)buf2 + i*2, distx, disty); #endif - fracX += fdx; + fx += fdx; } length -= len; b += len; } } else { //rotation - FetchPixelFunc fetch = qFetchPixel[layout->bpp]; uint sbuf1[buffer_size]; uint sbuf2[buffer_size]; quint64 buf1[buffer_size]; @@ -3123,117 +3161,18 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co while (b < end) { int len = qMin(length, buffer_size / 2); - int fracX = fx; - int fracY = fy; - int i = 0; -#if defined(__SSE2__) - if (blendType != BlendTransformedBilinearTiled && layout->bpp == QPixelLayout::BPP32) { - for (; i < len; ++i) { - int x1 = (fx >> 16); - int x2; - int y1 = (fy >> 16); - int y2; - fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); - fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); - if (x1 != x2 && y1 != y2) - break; - const uchar *s1 = data->texture.scanLine(y1); - const uchar *s2 = data->texture.scanLine(y2); - sbuf1[i * 2 + 0] = ((const uint*)s1)[x1]; - sbuf1[i * 2 + 1] = ((const uint*)s1)[x2]; - sbuf2[i * 2 + 0] = ((const uint*)s2)[x1]; - sbuf2[i * 2 + 1] = ((const uint*)s2)[x2]; - fx += fdx; - fy += fdy; - } - int fastLen = len; - if (fdx > 0) - fastLen = qMin(fastLen, int((qint64(image_x2) * fixed_scale - fx) / fdx)); - else if (fdx < 0) - fastLen = qMin(fastLen, int((qint64(image_x1) * fixed_scale - fx) / fdx)); - if (fdy > 0) - fastLen = qMin(fastLen, int((qint64(image_y2) * fixed_scale - fy) / fdy)); - else if (fdy < 0) - fastLen = qMin(fastLen, int((qint64(image_y1) * fixed_scale - fy) / fdy)); - fastLen -= 3; - - const __m128i v_fdx = _mm_set1_epi32(fdx*4); - const __m128i v_fdy = _mm_set1_epi32(fdy*4); - __m128i v_fx = _mm_setr_epi32(fx, fx + fdx, fx + fdx + fdx, fx + fdx + fdx + fdx); - __m128i v_fy = _mm_setr_epi32(fy, fy + fdy, fy + fdy + fdy, fy + fdy + fdy + fdy); - const int bytesPerLine = data->texture.bytesPerLine; - const uchar *s1 = data->texture.imageData; - const uchar *s2 = s1 + bytesPerLine; - const __m128i vbpl = _mm_shufflelo_epi16(_mm_cvtsi32_si128(bytesPerLine/4), _MM_SHUFFLE(0, 0, 0, 0)); - for (; i < fastLen; i += 4) { - const __m128i vy = _mm_packs_epi32(_mm_srai_epi32(v_fy, 16), _mm_setzero_si128()); - __m128i voffset = _mm_unpacklo_epi16(_mm_mullo_epi16(vy, vbpl), _mm_mulhi_epu16(vy, vbpl)); - voffset = _mm_add_epi32(voffset, _mm_srli_epi32(v_fx, 16)); - - int offset = _mm_cvtsi128_si32(voffset); voffset = _mm_srli_si128(voffset, 4); - sbuf1[i * 2 + 0] = ((const uint*)s1)[offset]; - sbuf1[i * 2 + 1] = ((const uint*)s1)[offset + 1]; - sbuf2[i * 2 + 0] = ((const uint*)s2)[offset]; - sbuf2[i * 2 + 1] = ((const uint*)s2)[offset + 1]; - offset = _mm_cvtsi128_si32(voffset); voffset = _mm_srli_si128(voffset, 4); - sbuf1[i * 2 + 2] = ((const uint*)s1)[offset]; - sbuf1[i * 2 + 3] = ((const uint*)s1)[offset + 1]; - sbuf2[i * 2 + 2] = ((const uint*)s2)[offset]; - sbuf2[i * 2 + 3] = ((const uint*)s2)[offset + 1]; - offset = _mm_cvtsi128_si32(voffset); voffset = _mm_srli_si128(voffset, 4); - sbuf1[i * 2 + 4] = ((const uint*)s1)[offset]; - sbuf1[i * 2 + 5] = ((const uint*)s1)[offset + 1]; - sbuf2[i * 2 + 4] = ((const uint*)s2)[offset]; - sbuf2[i * 2 + 5] = ((const uint*)s2)[offset + 1]; - offset = _mm_cvtsi128_si32(voffset); - sbuf1[i * 2 + 6] = ((const uint*)s1)[offset]; - sbuf1[i * 2 + 7] = ((const uint*)s1)[offset + 1]; - sbuf2[i * 2 + 6] = ((const uint*)s2)[offset]; - sbuf2[i * 2 + 7] = ((const uint*)s2)[offset + 1]; - - v_fx = _mm_add_epi32(v_fx, v_fdx); - v_fy = _mm_add_epi32(v_fy, v_fdy); - } - fx = _mm_cvtsi128_si32(v_fx); - fy = _mm_cvtsi128_si32(v_fy); - } -#endif - for (; i < len; ++i) { - int x1 = (fx >> 16); - int x2; - int y1 = (fy >> 16); - int y2; - fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); - fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); - const uchar *s1 = data->texture.scanLine(y1); - const uchar *s2 = data->texture.scanLine(y2); - - if (layout->bpp == QPixelLayout::BPP32) { - sbuf1[i * 2 + 0] = ((const uint*)s1)[x1]; - sbuf1[i * 2 + 1] = ((const uint*)s1)[x2]; - sbuf2[i * 2 + 0] = ((const uint*)s2)[x1]; - sbuf2[i * 2 + 1] = ((const uint*)s2)[x2]; - - } else { - sbuf1[i * 2 + 0] = fetch(s1, x1); - sbuf1[i * 2 + 1] = fetch(s1, x2); - sbuf2[i * 2 + 0] = fetch(s2, x1); - sbuf2[i * 2 + 1] = fetch(s2, x2); - } + fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy); - fx += fdx; - fy += fdy; - } layout->convertToARGB64PM((QRgba64 *)buf1, sbuf1, len * 2, clut, 0); layout->convertToARGB64PM((QRgba64 *)buf2, sbuf2, len * 2, clut, 0); for (int i = 0; i < len; ++i) { - int distx = (fracX & 0x0000ffff); - int disty = (fracY & 0x0000ffff); + int distx = (fx & 0x0000ffff); + int disty = (fy & 0x0000ffff); b[i] = interpolate_4_pixels_rgb64((QRgba64 *)buf1 + i*2, (QRgba64 *)buf2 + i*2, distx, disty); - fracX += fdx; - fracY += fdy; + fx += fdx; + fy += fdy; } length -= len; @@ -3241,6 +3180,12 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co } } } else { + const QTextureData &image = data->texture; + + const qreal fdx = data->m11; + const qreal fdy = data->m12; + const qreal fdw = data->m13; + qreal fx = data->m21 * cy + data->m11 * cx + data->dx; qreal fy = data->m22 * cy + data->m12 * cx + data->dy; qreal fw = data->m23 * cy + data->m13 * cx + data->m33; @@ -3270,8 +3215,8 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co distxs[i] = int((px - x1) * (1<<16)); distys[i] = int((py - y1) * (1<<16)); - fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); - fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); + fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2); + fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2); const uchar *s1 = data->texture.scanLine(y1); const uchar *s2 = data->texture.scanLine(y2); @@ -3842,7 +3787,7 @@ void blend_color_generic_rgb64(int count, const QSpan *spans, void *userData) QSpanData *data = reinterpret_cast<QSpanData *>(userData); Operator op = getOperator(data, spans, count); if (!op.funcSolid64) { - qDebug("unsupported 64bit blend attempted"); + qCDebug(lcQtGuiDrawHelper, "blend_color_generic_rgb64: unsupported 64bit blend attempted, falling back to 32-bit"); return blend_color_generic(count, spans, userData); } @@ -4080,7 +4025,7 @@ static void blend_src_generic_rgb64(int count, const QSpan *spans, void *userDat if (blend64.isSupported()) handleSpans(count, spans, data, blend64); else { - qDebug("blend_src_generic_rgb64: unsupported 64-bit blend attempted"); + qCDebug(lcQtGuiDrawHelper, "blend_src_generic_rgb64: unsupported 64-bit blend attempted, falling back to 32-bit"); BlendSrcGeneric blend32(data, op); handleSpans(count, spans, data, blend32); } @@ -4137,7 +4082,7 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi Operator op = getOperator(data, spans, count); if (!op.func64) { - qWarning("Unsupported blend"); + qCDebug(lcQtGuiDrawHelper, "blend_untransformed_generic_rgb64: unsupported 64-bit blend attempted, falling back to 32-bit"); return blend_untransformed_generic(count, spans, userData); } quint64 buffer[buffer_size]; @@ -4378,7 +4323,7 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD Operator op = getOperator(data, spans, count); if (!op.func64) { - qDebug("unsupported rgb64 blend"); + qCDebug(lcQtGuiDrawHelper, "blend_tiled_generic_rgb64: unsupported 64-bit blend attempted, falling back to 32-bit"); return blend_tiled_generic(count, spans, userData); } quint64 buffer[buffer_size]; @@ -5014,7 +4959,7 @@ static void blend_transformed_tiled_argb(int count, const QSpan *spans, void *us int image_width = data->texture.width; int image_height = data->texture.height; - const int scanline_offset = data->texture.bytesPerLine / 4; + const qssize_t scanline_offset = data->texture.bytesPerLine / 4; if (data->fast_matrix) { // The increment pr x in the scanline @@ -5354,7 +5299,7 @@ inline void qt_bitmapblit_template(QRasterBuffer *rasterBuffer, int mapWidth, int mapHeight, int mapStride) { DST *dest = reinterpret_cast<DST *>(rasterBuffer->scanLine(y)) + x; - const int destStride = rasterBuffer->bytesPerLine() / sizeof(DST); + const int destStride = rasterBuffer->stride<DST>(); if (mapWidth > 8) { while (mapHeight--) { @@ -5670,7 +5615,7 @@ void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer, if (!clip) { quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y)) + x; - const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint16); + const int destStride = rasterBuffer->stride<quint16>(); while (mapHeight--) { for (int i = 0; i < mapWidth; ++i) alphamapblend_quint16(map[i], dest, i, c); @@ -5745,7 +5690,7 @@ static void qt_alphamapblit_argb32(QRasterBuffer *rasterBuffer, const QClipData *clip, bool useGammaCorrection) { const quint32 c = color.toArgb32(); - const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint32); + const int destStride = rasterBuffer->stride<quint32>(); if (color.isTransparent()) return; @@ -5943,7 +5888,7 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer, if (!clip) { quint32 *dst = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x; - const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint32); + const int destStride = rasterBuffer->stride<quint32>(); while (mapHeight--) { for (int i = 0; i < mapWidth; ++i) { const uint coverage = src[i]; diff --git a/src/gui/painting/qdrawhelper_avx2.cpp b/src/gui/painting/qdrawhelper_avx2.cpp index a7e03a7bb3..d6c3319c76 100644 --- a/src/gui/painting/qdrawhelper_avx2.cpp +++ b/src/gui/painting/qdrawhelper_avx2.cpp @@ -420,10 +420,14 @@ void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2(uin const uint *s1 = (const uint *)image.scanLine(y1); const uint *s2 = (const uint *)image.scanLine(y2); - int disty = (fy & 0x0000ffff) >> 8; - int idisty = 256 - disty; - int x = fx >> 16; - int length = end - b; + const int disty = (fy & 0x0000ffff) >> 8; + const int idisty = 256 - disty; + const int length = end - b; + + // The intermediate buffer is generated in the positive direction + const int adjust = (fdx < 0) ? fdx * length : 0; + const int offset = (fx + adjust) >> 16; + int x = offset; // The idea is first to do the interpolation between the row s1 and the row s2 // into an intermediate buffer, then we interpolate between two pixel of this buffer. @@ -433,7 +437,7 @@ void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2(uin // +1 for the last pixel to interpolate with, and +1 for rounding errors. quint32 intermediate_buffer[2][BufferSize + 2]; // count is the size used in the intermediate_buffer. - int count = (qint64(length) * fdx + FixedScale - 1) / FixedScale + 2; + int count = (qint64(length) * qAbs(fdx) + FixedScale - 1) / FixedScale + 2; Q_ASSERT(count <= BufferSize + 2); //length is supposed to be <= buffer_size and data->m11 < 1 in this case int f = 0; int lim = qMin(count, image.x2 - x); @@ -492,8 +496,7 @@ void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2(uin x++; } // Now interpolate the values from the intermediate_buffer to get the final result. - fx &= FixedScale - 1; - Q_ASSERT((fx >> 16) == 0); + fx -= offset * FixedScale; const __m128i v_fdx = _mm_set1_epi32(fdx * 4); const __m128i v_blend = _mm_set1_epi32(0x00800080); @@ -682,7 +685,7 @@ void QT_FASTCALL fetchTransformedBilinearARGB32PM_fast_rotate_helper_avx2(uint * v_fy = _mm256_add_epi32(v_fy, _mm256_mullo_epi32(_mm256_set1_epi32(fdy), v_index)); const uchar *textureData = image.imageData; - const int bytesPerLine = image.bytesPerLine; + const qssize_t bytesPerLine = image.bytesPerLine; const __m256i vbpl = _mm256_set1_epi16(bytesPerLine/4); while (b < boundedEnd - 7) { diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index ccfc9a2889..2be10d2cfb 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -293,7 +293,7 @@ struct QTextureData int y1; int x2; int y2; - int bytesPerLine; + qssize_t bytesPerLine; QImage::Format format; const QVector<QRgb> *colorTable; bool hasAlpha; @@ -847,7 +847,7 @@ inline void qt_memfill(T *dest, T value, int count) template <class T> Q_STATIC_TEMPLATE_FUNCTION inline void qt_rectfill(T *dest, T value, - int x, int y, int width, int height, int stride) + int x, int y, int width, int height, qssize_t stride) { char *d = reinterpret_cast<char*>(dest + x) + y * stride; if (uint(stride) == (width * sizeof(T))) { diff --git a/src/gui/painting/qdrawhelper_sse2.cpp b/src/gui/painting/qdrawhelper_sse2.cpp index 3013d2cf3e..bfe2080298 100644 --- a/src/gui/painting/qdrawhelper_sse2.cpp +++ b/src/gui/painting/qdrawhelper_sse2.cpp @@ -341,7 +341,7 @@ void qt_bitmapblit32_sse2_base(QRasterBuffer *rasterBuffer, int x, int y, const uchar *src, int width, int height, int stride) { quint32 *dest = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x; - const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint32); + const int destStride = rasterBuffer->stride<quint32>(); const __m128i c128 = _mm_set1_epi32(color); const __m128i maskmask1 = _mm_set_epi32(0x10101010, 0x20202020, @@ -407,7 +407,7 @@ void qt_bitmapblit16_sse2(QRasterBuffer *rasterBuffer, int x, int y, { const quint16 c = qConvertRgb32To16(color.toArgb32()); quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y)) + x; - const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint16); + const int destStride = rasterBuffer->stride<quint32>(); const __m128i c128 = _mm_set1_epi16(c); QT_WARNING_DISABLE_MSVC(4309) // truncation of constant value diff --git a/src/gui/painting/qemulationpaintengine.cpp b/src/gui/painting/qemulationpaintengine.cpp index 586b71557e..e6686e3721 100644 --- a/src/gui/painting/qemulationpaintengine.cpp +++ b/src/gui/painting/qemulationpaintengine.cpp @@ -72,6 +72,14 @@ QPainterState *QEmulationPaintEngine::createState(QPainterState *orig) const return real_engine->createState(orig); } +static inline void combineXForm(QBrush *brush, const QRectF &r) +{ + QTransform t = brush->transform(); + t.translate(r.x(), r.y()); + t.scale(r.width(), r.height()); + brush->setTransform(t); +} + void QEmulationPaintEngine::fill(const QVectorPath &path, const QBrush &brush) { QPainterState *s = state(); @@ -84,26 +92,14 @@ void QEmulationPaintEngine::fill(const QVectorPath &path, const QBrush &brush) Qt::BrushStyle style = qbrush_style(brush); if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) { - const QGradient *g = brush.gradient(); - - if (g->coordinateMode() > QGradient::LogicalMode) { - if (g->coordinateMode() == QGradient::StretchToDeviceMode) { - QBrush copy = brush; - QTransform mat = copy.transform(); - mat.scale(real_engine->painter()->device()->width(), real_engine->painter()->device()->height()); - copy.setTransform(mat); - real_engine->fill(path, copy); - return; - } else if (g->coordinateMode() == QGradient::ObjectBoundingMode) { - QBrush copy = brush; - QTransform mat = copy.transform(); - QRectF r = path.controlPointRect(); - mat.translate(r.x(), r.y()); - mat.scale(r.width(), r.height()); - copy.setTransform(mat); - real_engine->fill(path, copy); - return; - } + QGradient::CoordinateMode coMode = brush.gradient()->coordinateMode(); + if (coMode > QGradient::LogicalMode) { + QBrush copy = brush; + const QPaintDevice *d = real_engine->painter()->device(); + QRectF r = (coMode == QGradient::ObjectBoundingMode) ? path.controlPointRect() : QRectF(0, 0, d->width(), d->height()); + combineXForm(©, r); + real_engine->fill(path, copy); + return; } } @@ -124,27 +120,15 @@ void QEmulationPaintEngine::stroke(const QVectorPath &path, const QPen &pen) QBrush brush = pen.brush(); QPen copy = pen; Qt::BrushStyle style = qbrush_style(brush); - if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) { - const QGradient *g = brush.gradient(); - - if (g->coordinateMode() > QGradient::LogicalMode) { - if (g->coordinateMode() == QGradient::StretchToDeviceMode) { - QTransform mat = brush.transform(); - mat.scale(real_engine->painter()->device()->width(), real_engine->painter()->device()->height()); - brush.setTransform(mat); - copy.setBrush(brush); - real_engine->stroke(path, copy); - return; - } else if (g->coordinateMode() == QGradient::ObjectBoundingMode) { - QTransform mat = brush.transform(); - QRectF r = path.controlPointRect(); - mat.translate(r.x(), r.y()); - mat.scale(r.width(), r.height()); - brush.setTransform(mat); - copy.setBrush(brush); - real_engine->stroke(path, copy); - return; - } + if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern ) { + QGradient::CoordinateMode coMode = brush.gradient()->coordinateMode(); + if (coMode > QGradient::LogicalMode) { + const QPaintDevice *d = real_engine->painter()->device(); + QRectF r = (coMode == QGradient::ObjectBoundingMode) ? path.controlPointRect() : QRectF(0, 0, d->width(), d->height()); + combineXForm(&brush, r); + copy.setBrush(brush); + real_engine->stroke(path, copy); + return; } } @@ -179,18 +163,16 @@ void QEmulationPaintEngine::drawTextItem(const QPointF &p, const QTextItem &text QGradient g = *s->pen.brush().gradient(); if (g.coordinateMode() > QGradient::LogicalMode) { - QTransform mat = s->pen.brush().transform(); - if (g.coordinateMode() == QGradient::StretchToDeviceMode) { - mat.scale(real_engine->painter()->device()->width(), real_engine->painter()->device()->height()); - } else if (g.coordinateMode() == QGradient::ObjectBoundingMode) { - const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); - QRectF r(p.x(), p.y() - ti.ascent.toReal(), ti.width.toReal(), (ti.ascent + ti.descent + 1).toReal()); - mat.translate(r.x(), r.y()); - mat.scale(r.width(), r.height()); - } + QBrush copy = s->pen.brush(); + const QPaintDevice *d = real_engine->painter()->device(); + const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); + QRectF r = (g.coordinateMode() == QGradient::ObjectBoundingMode) ? + QRectF(p.x(), p.y() - ti.ascent.toReal(), ti.width.toReal(), (ti.ascent + ti.descent + 1).toReal()) : + QRectF(0, 0, d->width(), d->height()); + combineXForm(©, r); g.setCoordinateMode(QGradient::LogicalMode); QBrush brush(g); - brush.setTransform(mat); + brush.setTransform(copy.transform()); s->pen.setBrush(brush); penChanged(); real_engine->drawTextItem(p, textItem); diff --git a/src/gui/painting/qgrayraster.c b/src/gui/painting/qgrayraster.c index 4a135e25d8..0143e9b602 100644 --- a/src/gui/painting/qgrayraster.c +++ b/src/gui/painting/qgrayraster.c @@ -43,7 +43,7 @@ /* */ /* A new `perfect' anti-aliasing renderer (body). */ /* */ -/* Copyright 2000-2001, 2002, 2003 by */ +/* Copyright 2000-2016 by */ /* David Turner, Robert Wilhelm, and Werner Lemberg. */ /* */ /* This file is part of the FreeType project, and may only be used, */ @@ -119,10 +119,6 @@ /* */ /*************************************************************************/ -/* experimental support for gamma correction within the rasterizer */ -#define xxxGRAYS_USE_GAMMA - - /*************************************************************************/ /* */ /* The macro QT_FT_COMPONENT is used in trace mode. It is an implicit */ @@ -133,6 +129,28 @@ #define QT_FT_COMPONENT trace_smooth +/* Auxiliary macros for token concatenation. */ +#define QT_FT_ERR_XCAT( x, y ) x ## y +#define QT_FT_ERR_CAT( x, y ) QT_FT_ERR_XCAT( x, y ) + +#define QT_FT_BEGIN_STMNT do { +#define QT_FT_END_STMNT } while ( 0 ) + +#define QT_FT_MAX( a, b ) ( (a) > (b) ? (a) : (b) ) +#define QT_FT_ABS( a ) ( (a) < 0 ? -(a) : (a) ) + + +/* + * Approximate sqrt(x*x+y*y) using the `alpha max plus beta min' + * algorithm. We use alpha = 1, beta = 3/8, giving us results with a + * largest error less than 7% compared to the exact value. + */ +#define QT_FT_HYPOT( x, y ) \ + ( x = QT_FT_ABS( x ), \ + y = QT_FT_ABS( y ), \ + x > y ? x + ( 3 * y >> 3 ) \ + : y + ( 3 * x >> 3 ) ) + #define ErrRaster_MemoryOverflow -4 #if defined(VXWORKS) @@ -150,6 +168,9 @@ #define qt_ft_longjmp longjmp #define qt_ft_jmp_buf jmp_buf +#include <stddef.h> +typedef ptrdiff_t QT_FT_PtrDist; + #define ErrRaster_Invalid_Mode -2 #define ErrRaster_Invalid_Outline -1 #define ErrRaster_Invalid_Argument -3 @@ -169,15 +190,10 @@ #define QT_FT_UNUSED( x ) (void) x - /* Disable the tracing mechanism for simplicity -- developers can */ - /* activate it easily by redefining these two macros. */ -#ifndef QT_FT_ERROR -#define QT_FT_ERROR( x ) do ; while ( 0 ) /* nothing */ -#endif - -#ifndef QT_FT_TRACE -#define QT_FT_TRACE( x ) do ; while ( 0 ) /* nothing */ -#endif +#define QT_FT_TRACE5( x ) do { } while ( 0 ) /* nothing */ +#define QT_FT_TRACE7( x ) do { } while ( 0 ) /* nothing */ +#define QT_FT_ERROR( x ) do { } while ( 0 ) /* nothing */ +#define QT_FT_THROW( e ) QT_FT_ERR_CAT( ErrRaster_, e ) #ifndef QT_FT_MEM_SET #define QT_FT_MEM_SET( d, s, c ) qt_ft_memset( d, s, c ) @@ -187,9 +203,6 @@ #define QT_FT_MEM_ZERO( dest, count ) QT_FT_MEM_SET( dest, 0, count ) #endif - /* define this to dump debugging information */ -#define xxxDEBUG_GRAYS - #define RAS_ARG PWorker worker #define RAS_ARG_ PWorker worker, @@ -199,26 +212,47 @@ #define ras (*worker) - /* must be at least 6 bits! */ #define PIXEL_BITS 8 #define ONE_PIXEL ( 1L << PIXEL_BITS ) -#define PIXEL_MASK ( -1L << PIXEL_BITS ) #define TRUNC( x ) ( (TCoord)( (x) >> PIXEL_BITS ) ) -#define SUBPIXELS( x ) ( (TPos)(x) * ( 1 << PIXEL_BITS ) ) +#define SUBPIXELS( x ) ( (TPos)(x) * ONE_PIXEL ) #define FLOOR( x ) ( (x) & -ONE_PIXEL ) #define CEILING( x ) ( ( (x) + ONE_PIXEL - 1 ) & -ONE_PIXEL ) #define ROUND( x ) ( ( (x) + ONE_PIXEL / 2 ) & -ONE_PIXEL ) #if PIXEL_BITS >= 6 -#define UPSCALE( x ) ( (x) * ( 1 << ( PIXEL_BITS - 6 ) ) ) +#define UPSCALE( x ) ( (x) * ( ONE_PIXEL >> 6 ) ) #define DOWNSCALE( x ) ( (x) >> ( PIXEL_BITS - 6 ) ) #else #define UPSCALE( x ) ( (x) >> ( 6 - PIXEL_BITS ) ) -#define DOWNSCALE( x ) ( (x) << ( 6 - PIXEL_BITS ) ) +#define DOWNSCALE( x ) ( (x) * ( 64 >> PIXEL_BITS ) ) #endif +/* Compute `dividend / divisor' and return both its quotient and */ +/* remainder, cast to a specific type. This macro also ensures that */ +/* the remainder is always positive. */ +#define QT_FT_DIV_MOD( type, dividend, divisor, quotient, remainder ) \ +QT_FT_BEGIN_STMNT \ + (quotient) = (type)( (dividend) / (divisor) ); \ + (remainder) = (type)( (dividend) % (divisor) ); \ + if ( (remainder) < 0 ) \ + { \ + (quotient)--; \ + (remainder) += (type)(divisor); \ + } \ +QT_FT_END_STMNT + + /* These macros speed up repetitive divisions by replacing them */ + /* with multiplications and right shifts. */ +#define QT_FT_UDIVPREP( b ) \ + long b ## _r = (long)( ULONG_MAX >> PIXEL_BITS ) / ( b ) +#define QT_FT_UDIV( a, b ) \ + ( ( (unsigned long)( a ) * (unsigned long)( b ## _r ) ) >> \ + ( sizeof( long ) * CHAR_BIT - PIXEL_BITS ) ) + + /*************************************************************************/ /* */ /* TYPE DEFINITIONS */ @@ -228,28 +262,9 @@ /* need to define them to "float" or "double" when experimenting with */ /* new algorithms */ - typedef int TCoord; /* integer scanline/pixel coordinate */ - typedef int TPos; /* sub-pixel coordinate */ - - /* determine the type used to store cell areas. This normally takes at */ - /* least PIXEL_BITS*2 + 1 bits. On 16-bit systems, we need to use */ - /* `long' instead of `int', otherwise bad things happen */ - -#if PIXEL_BITS <= 7 - - typedef int TArea; - -#else /* PIXEL_BITS >= 8 */ - - /* approximately determine the size of integers using an ANSI-C header */ -#if QT_FT_UINT_MAX == 0xFFFFU - typedef long TArea; -#else - typedef int TArea; -#endif - -#endif /* PIXEL_BITS >= 8 */ - + typedef long TCoord; /* integer scanline/pixel coordinate */ + typedef long TPos; /* sub-pixel coordinate */ + typedef long TArea ; /* cell areas, coordinate products */ /* maximal number of gray spans in a call to the span callback */ #define QT_FT_MAX_GRAY_SPANS 256 @@ -279,17 +294,11 @@ int invalid; PCell cells; - int max_cells; - int num_cells; + QT_FT_PtrDist max_cells; + QT_FT_PtrDist num_cells; - TCoord cx, cy; TPos x, y; - TPos last_ey; - - QT_FT_Vector bez_stack[32 * 3 + 1]; - int lev_stack[32]; - QT_FT_Outline outline; QT_FT_Bitmap target; QT_FT_BBox clip_box; @@ -302,8 +311,6 @@ int band_size; int band_shoot; - int conic_level; - int cubic_level; qt_ft_jmp_buf jump_buffer; @@ -311,7 +318,7 @@ long buffer_size; PCell* ycells; - int ycount; + TPos ycount; int skip_spans; } TWorker, *PWorker; @@ -341,7 +348,7 @@ /* */ static void gray_init_cells( RAS_ARG_ void* buffer, - long byte_size ) + long byte_size ) { ras.buffer = buffer; ras.buffer_size = byte_size; @@ -404,31 +411,25 @@ /* */ /* Record the current cell in the table. */ /* */ - static void - gray_record_cell( RAS_ARG ) + static PCell + gray_find_cell( RAS_ARG ) { PCell *pcell, cell; - int x = ras.ex; + TPos x = ras.ex; - if ( ras.invalid || !( ras.area | ras.cover ) ) - return; - if ( x > ras.max_ex ) - x = ras.max_ex; + if ( x > ras.count_ex ) + x = ras.count_ex; pcell = &ras.ycells[ras.ey]; - for (;;) { cell = *pcell; if ( cell == NULL || cell->x > x ) break; - if ( cell->x == x ) { - cell->area += ras.area; - cell->cover += ras.cover; - return; - } + if ( cell->x == x ) + goto Exit; pcell = &cell->next; } @@ -438,11 +439,28 @@ cell = ras.cells + ras.num_cells++; cell->x = x; - cell->area = ras.area; - cell->cover = ras.cover; + cell->area = 0; + cell->cover = 0; cell->next = *pcell; *pcell = cell; + + Exit: + return cell; + } + + + static void + gray_record_cell( RAS_ARG ) + { + if ( ras.area | ras.cover ) + { + PCell cell = gray_find_cell( RAS_VAR ); + + + cell->area += ras.area; + cell->cover += ras.cover; + } } @@ -488,8 +506,8 @@ ras.ey = ey; } - ras.invalid = ( (unsigned)ey >= (unsigned)ras.count_ey || - ex >= ras.count_ex ); + ras.invalid = ( (unsigned int)ey >= (unsigned int)ras.count_ey || + ex >= ras.count_ex ); } @@ -511,12 +529,13 @@ ras.cover = 0; ras.ex = ex - ras.min_ex; ras.ey = ey - ras.min_ey; - ras.last_ey = SUBPIXELS( ey ); ras.invalid = 0; gray_set_cell( RAS_VAR_ ex, ey ); } +// The new render-line implementation is not yet used +#if 1 /*************************************************************************/ /* */ @@ -529,9 +548,9 @@ TPos x2, TCoord y2 ) { - TCoord ex1, ex2, fx1, fx2, delta; + TCoord ex1, ex2, fx1, fx2, delta, mod; int p, first, dx; - int incr, lift, mod, rem; + int incr; dx = x2 - x1; @@ -573,13 +592,7 @@ dx = -dx; } - delta = (TCoord)( p / dx ); - mod = (TCoord)( p % dx ); - if ( mod < 0 ) - { - delta--; - mod += (TCoord)dx; - } + QT_FT_DIV_MOD( TCoord, p, dx, delta, mod ); ras.area += (TArea)( fx1 + first ) * delta; ras.cover += delta; @@ -590,14 +603,11 @@ if ( ex1 != ex2 ) { - p = ONE_PIXEL * ( y2 - y1 + delta ); - lift = (TCoord)( p / dx ); - rem = (TCoord)( p % dx ); - if ( rem < 0 ) - { - lift--; - rem += (TCoord)dx; - } + TCoord lift, rem; + + + p = ONE_PIXEL * ( y2 - y1 + delta ); + QT_FT_DIV_MOD( TCoord, p, dx, lift, rem ); mod -= (int)dx; @@ -633,38 +643,24 @@ gray_render_line( RAS_ARG_ TPos to_x, TPos to_y ) { - TCoord ey1, ey2, fy1, fy2; + TCoord ey1, ey2, fy1, fy2, mod; TPos dx, dy, x, x2; int p, first; - int delta, rem, mod, lift, incr; + int delta, rem, lift, incr; - ey1 = TRUNC( ras.last_ey ); + ey1 = TRUNC( ras.y ); ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */ - fy1 = (TCoord)( ras.y - ras.last_ey ); + fy1 = (TCoord)( ras.y - SUBPIXELS( ey1 ) ); fy2 = (TCoord)( to_y - SUBPIXELS( ey2 ) ); dx = to_x - ras.x; dy = to_y - ras.y; - /* XXX: we should do something about the trivial case where dx == 0, */ - /* as it happens very often! */ - /* perform vertical clipping */ - { - TCoord min, max; - - - min = ey1; - max = ey2; - if ( ey1 > ey2 ) - { - min = ey2; - max = ey1; - } - if ( min >= ras.max_ey || max < ras.min_ey ) - goto End; - } + if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) || + ( ey1 < ras.min_ey && ey2 < ras.min_ey ) ) + goto End; /* everything is on a single scanline */ if ( ey1 == ey2 ) @@ -771,13 +767,7 @@ if ( ey1 != ey2 ) { p = ONE_PIXEL * dx; - lift = (int)( p / dy ); - rem = (int)( p % dy ); - if ( rem < 0 ) - { - lift--; - rem += (int)dy; - } + QT_FT_DIV_MOD( int, p, dy, lift, rem ); mod -= (int)dy; while ( ey1 != ey2 ) @@ -808,10 +798,147 @@ End: ras.x = to_x; ras.y = to_y; - ras.last_ey = SUBPIXELS( ey2 ); } +#else + + /*************************************************************************/ + /* */ + /* Render a straight line across multiple cells in any direction. */ + /* */ + static void + gray_render_line( RAS_ARG_ TPos to_x, + TPos to_y ) + { + TPos dx, dy, fx1, fy1, fx2, fy2; + TCoord ex1, ex2, ey1, ey2; + + + ex1 = TRUNC( ras.x ); + ex2 = TRUNC( to_x ); + ey1 = TRUNC( ras.y ); + ey2 = TRUNC( to_y ); + + /* perform vertical clipping */ + if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) || + ( ey1 < ras.min_ey && ey2 < ras.min_ey ) ) + goto End; + + dx = to_x - ras.x; + dy = to_y - ras.y; + + fx1 = ras.x - SUBPIXELS( ex1 ); + fy1 = ras.y - SUBPIXELS( ey1 ); + + if ( ex1 == ex2 && ey1 == ey2 ) /* inside one cell */ + ; + else if ( dy == 0 ) /* ex1 != ex2 */ /* any horizontal line */ + { + ex1 = ex2; + gray_set_cell( RAS_VAR_ ex1, ey1 ); + } + else if ( dx == 0 ) + { + if ( dy > 0 ) /* vertical line up */ + do + { + fy2 = ONE_PIXEL; + ras.cover += ( fy2 - fy1 ); + ras.area += ( fy2 - fy1 ) * fx1 * 2; + fy1 = 0; + ey1++; + gray_set_cell( RAS_VAR_ ex1, ey1 ); + } while ( ey1 != ey2 ); + else /* vertical line down */ + do + { + fy2 = 0; + ras.cover += ( fy2 - fy1 ); + ras.area += ( fy2 - fy1 ) * fx1 * 2; + fy1 = ONE_PIXEL; + ey1--; + gray_set_cell( RAS_VAR_ ex1, ey1 ); + } while ( ey1 != ey2 ); + } + else /* any other line */ + { + TArea prod = dx * fy1 - dy * fx1; + QT_FT_UDIVPREP( dx ); + QT_FT_UDIVPREP( dy ); + + + /* The fundamental value `prod' determines which side and the */ + /* exact coordinate where the line exits current cell. It is */ + /* also easily updated when moving from one cell to the next. */ + do + { + if ( prod <= 0 && + prod - dx * ONE_PIXEL > 0 ) /* left */ + { + fx2 = 0; + fy2 = (TPos)QT_FT_UDIV( -prod, -dx ); + prod -= dy * ONE_PIXEL; + ras.cover += ( fy2 - fy1 ); + ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); + fx1 = ONE_PIXEL; + fy1 = fy2; + ex1--; + } + else if ( prod - dx * ONE_PIXEL <= 0 && + prod - dx * ONE_PIXEL + dy * ONE_PIXEL > 0 ) /* up */ + { + prod -= dx * ONE_PIXEL; + fx2 = (TPos)QT_FT_UDIV( -prod, dy ); + fy2 = ONE_PIXEL; + ras.cover += ( fy2 - fy1 ); + ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); + fx1 = fx2; + fy1 = 0; + ey1++; + } + else if ( prod - dx * ONE_PIXEL + dy * ONE_PIXEL <= 0 && + prod + dy * ONE_PIXEL >= 0 ) /* right */ + { + prod += dy * ONE_PIXEL; + fx2 = ONE_PIXEL; + fy2 = (TPos)QT_FT_UDIV( prod, dx ); + ras.cover += ( fy2 - fy1 ); + ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); + fx1 = 0; + fy1 = fy2; + ex1++; + } + else /* ( prod + dy * ONE_PIXEL < 0 && + prod > 0 ) down */ + { + fx2 = (TPos)QT_FT_UDIV( prod, -dy ); + fy2 = 0; + prod += dx * ONE_PIXEL; + ras.cover += ( fy2 - fy1 ); + ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); + fx1 = fx2; + fy1 = ONE_PIXEL; + ey1--; + } + + gray_set_cell( RAS_VAR_ ex1, ey1 ); + } while ( ex1 != ex2 || ey1 != ey2 ); + } + + fx2 = to_x - SUBPIXELS( ex2 ); + fy2 = to_y - SUBPIXELS( ey2 ); + + ras.cover += ( fy2 - fy1 ); + ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); + + End: + ras.x = to_x; + ras.y = to_y; + } + +#endif + static void gray_split_conic( QT_FT_Vector* base ) { @@ -836,52 +963,11 @@ gray_render_conic( RAS_ARG_ const QT_FT_Vector* control, const QT_FT_Vector* to ) { + QT_FT_Vector bez_stack[16 * 2 + 1]; /* enough to accommodate bisections */ + QT_FT_Vector* arc = bez_stack; TPos dx, dy; - int top, level; - int* levels; - QT_FT_Vector* arc; - - - dx = DOWNSCALE( ras.x ) + to->x - ( control->x << 1 ); - if ( dx < 0 ) - dx = -dx; - dy = DOWNSCALE( ras.y ) + to->y - ( control->y << 1 ); - if ( dy < 0 ) - dy = -dy; - if ( dx < dy ) - dx = dy; - - level = 1; - dx = dx / ras.conic_level; - while ( dx > 0 ) - { - dx >>= 2; - level++; - } + int draw, split; - /* a shortcut to speed things up */ - if ( level <= 1 ) - { - /* we compute the mid-point directly in order to avoid */ - /* calling gray_split_conic() */ - TPos to_x, to_y, mid_x, mid_y; - - - to_x = UPSCALE( to->x ); - to_y = UPSCALE( to->y ); - mid_x = ( ras.x + to_x + 2 * UPSCALE( control->x ) ) / 4; - mid_y = ( ras.y + to_y + 2 * UPSCALE( control->y ) ) / 4; - - gray_render_line( RAS_VAR_ mid_x, mid_y ); - gray_render_line( RAS_VAR_ to_x, to_y ); - - return; - } - - arc = ras.bez_stack; - levels = ras.lev_stack; - top = 0; - levels[0] = level; arc[0].x = UPSCALE( to->x ); arc[0].y = UPSCALE( to->y ); @@ -890,54 +976,51 @@ arc[2].x = ras.x; arc[2].y = ras.y; - while ( top >= 0 ) + /* short-cut the arc that crosses the current band */ + if ( ( TRUNC( arc[0].y ) >= ras.max_ey && + TRUNC( arc[1].y ) >= ras.max_ey && + TRUNC( arc[2].y ) >= ras.max_ey ) || + ( TRUNC( arc[0].y ) < ras.min_ey && + TRUNC( arc[1].y ) < ras.min_ey && + TRUNC( arc[2].y ) < ras.min_ey ) ) { - level = levels[top]; - if ( level > 1 ) - { - /* check that the arc crosses the current band */ - TPos min, max, y; - - - min = max = arc[0].y; - - y = arc[1].y; - if ( y < min ) min = y; - if ( y > max ) max = y; + ras.x = arc[0].x; + ras.y = arc[0].y; + return; + } - y = arc[2].y; - if ( y < min ) min = y; - if ( y > max ) max = y; + dx = QT_FT_ABS( arc[2].x + arc[0].x - 2 * arc[1].x ); + dy = QT_FT_ABS( arc[2].y + arc[0].y - 2 * arc[1].y ); + if ( dx < dy ) + dx = dy; - if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < ras.min_ey ) - goto Draw; + /* We can calculate the number of necessary bisections because */ + /* each bisection predictably reduces deviation exactly 4-fold. */ + /* Even 32-bit deviation would vanish after 16 bisections. */ + draw = 1; + while ( dx > ONE_PIXEL / 4 ) + { + dx >>= 2; + draw <<= 1; + } + /* We use decrement counter to count the total number of segments */ + /* to draw starting from 2^level. Before each draw we split as */ + /* many times as there are trailing zeros in the counter. */ + do + { + split = 1; + while ( ( draw & split ) == 0 ) + { gray_split_conic( arc ); arc += 2; - top++; - levels[top] = levels[top - 1] = level - 1; - continue; + split <<= 1; } - Draw: - { - TPos to_x, to_y, mid_x, mid_y; - - - to_x = arc[0].x; - to_y = arc[0].y; - mid_x = ( ras.x + to_x + 2 * arc[1].x ) / 4; - mid_y = ( ras.y + to_y + 2 * arc[1].y ) / 4; - - gray_render_line( RAS_VAR_ mid_x, mid_y ); - gray_render_line( RAS_VAR_ to_x, to_y ); - - top--; - arc -= 2; - } - } + gray_render_line( RAS_VAR_ arc[0].x, arc[0].y ); + arc -= 2; - return; + } while ( --draw ); } @@ -974,60 +1057,13 @@ const QT_FT_Vector* control2, const QT_FT_Vector* to ) { - TPos dx, dy, da, db; - int top, level; - int* levels; - QT_FT_Vector* arc; + QT_FT_Vector bez_stack[16 * 3 + 1]; /* enough to accommodate bisections */ + QT_FT_Vector* arc = bez_stack; + TPos dx, dy, dx_, dy_; + TPos dx1, dy1, dx2, dy2; + TPos L, s, s_limit; - dx = DOWNSCALE( ras.x ) + to->x - ( control1->x << 1 ); - if ( dx < 0 ) - dx = -dx; - dy = DOWNSCALE( ras.y ) + to->y - ( control1->y << 1 ); - if ( dy < 0 ) - dy = -dy; - if ( dx < dy ) - dx = dy; - da = dx; - - dx = DOWNSCALE( ras.x ) + to->x - 3 * ( control1->x + control2->x ); - if ( dx < 0 ) - dx = -dx; - dy = DOWNSCALE( ras.y ) + to->y - 3 * ( control1->y + control2->y ); - if ( dy < 0 ) - dy = -dy; - if ( dx < dy ) - dx = dy; - db = dx; - - level = 1; - da = da / ras.cubic_level; - db = db / ras.conic_level; - while ( da > 0 || db > 0 ) - { - da >>= 2; - db >>= 3; - level++; - } - - if ( level <= 1 ) - { - TPos to_x, to_y, mid_x, mid_y; - - - to_x = UPSCALE( to->x ); - to_y = UPSCALE( to->y ); - mid_x = ( ras.x + to_x + - 3 * UPSCALE( control1->x + control2->x ) ) / 8; - mid_y = ( ras.y + to_y + - 3 * UPSCALE( control1->y + control2->y ) ) / 8; - - gray_render_line( RAS_VAR_ mid_x, mid_y ); - gray_render_line( RAS_VAR_ to_x, to_y ); - return; - } - - arc = ras.bez_stack; arc[0].x = UPSCALE( to->x ); arc[0].y = UPSCALE( to->y ); arc[1].x = UPSCALE( control2->x ); @@ -1037,56 +1073,77 @@ arc[3].x = ras.x; arc[3].y = ras.y; - levels = ras.lev_stack; - top = 0; - levels[0] = level; + /* short-cut the arc that crosses the current band */ + if ( ( TRUNC( arc[0].y ) >= ras.max_ey && + TRUNC( arc[1].y ) >= ras.max_ey && + TRUNC( arc[2].y ) >= ras.max_ey && + TRUNC( arc[3].y ) >= ras.max_ey ) || + ( TRUNC( arc[0].y ) < ras.min_ey && + TRUNC( arc[1].y ) < ras.min_ey && + TRUNC( arc[2].y ) < ras.min_ey && + TRUNC( arc[3].y ) < ras.min_ey ) ) + { + ras.x = arc[0].x; + ras.y = arc[0].y; + return; + } - while ( top >= 0 ) + for (;;) { - level = levels[top]; - if ( level > 1 ) - { - /* check that the arc crosses the current band */ - TPos min, max, y; - - - min = max = arc[0].y; - y = arc[1].y; - if ( y < min ) min = y; - if ( y > max ) max = y; - y = arc[2].y; - if ( y < min ) min = y; - if ( y > max ) max = y; - y = arc[3].y; - if ( y < min ) min = y; - if ( y > max ) max = y; - if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < 0 ) - goto Draw; - gray_split_cubic( arc ); - arc += 3; - top ++; - levels[top] = levels[top - 1] = level - 1; - continue; - } + /* Decide whether to split or draw. See `Rapid Termination */ + /* Evaluation for Recursive Subdivision of Bezier Curves' by Thomas */ + /* F. Hain, at */ + /* http://www.cis.southalabama.edu/~hain/general/Publications/Bezier/Camera-ready%20CISST02%202.pdf */ - Draw: - { - TPos to_x, to_y, mid_x, mid_y; + /* dx and dy are x and y components of the P0-P3 chord vector. */ + dx = dx_ = arc[3].x - arc[0].x; + dy = dy_ = arc[3].y - arc[0].y; - to_x = arc[0].x; - to_y = arc[0].y; - mid_x = ( ras.x + to_x + 3 * ( arc[1].x + arc[2].x ) ) / 8; - mid_y = ( ras.y + to_y + 3 * ( arc[1].y + arc[2].y ) ) / 8; + L = QT_FT_HYPOT( dx_, dy_ ); - gray_render_line( RAS_VAR_ mid_x, mid_y ); - gray_render_line( RAS_VAR_ to_x, to_y ); - top --; - arc -= 3; - } - } + /* Avoid possible arithmetic overflow below by splitting. */ + if ( L > 32767 ) + goto Split; + + /* Max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1). */ + s_limit = L * (TPos)( ONE_PIXEL / 6 ); + + /* s is L * the perpendicular distance from P1 to the line P0-P3. */ + dx1 = arc[1].x - arc[0].x; + dy1 = arc[1].y - arc[0].y; + s = QT_FT_ABS( dy * dx1 - dx * dy1 ); + + if ( s > s_limit ) + goto Split; + + /* s is L * the perpendicular distance from P2 to the line P0-P3. */ + dx2 = arc[2].x - arc[0].x; + dy2 = arc[2].y - arc[0].y; + s = QT_FT_ABS( dy * dx2 - dx * dy2 ); + + if ( s > s_limit ) + goto Split; + + /* Split super curvy segments where the off points are so far + from the chord that the angles P0-P1-P3 or P0-P2-P3 become + acute as detected by appropriate dot products. */ + if ( dx1 * ( dx1 - dx ) + dy1 * ( dy1 - dy ) > 0 || + dx2 * ( dx2 - dx ) + dy2 * ( dy2 - dy ) > 0 ) + goto Split; - return; + gray_render_line( RAS_VAR_ arc[0].x, arc[0].y ); + + if ( arc == bez_stack ) + return; + + arc -= 3; + continue; + + Split: + gray_split_cubic( arc ); + arc += 3; + } } @@ -1099,7 +1156,8 @@ /* record current cell, if any */ - gray_record_cell( worker ); + if ( !ras.invalid ) + gray_record_cell( worker ); /* start to a new position */ x = UPSCALE( to->x ); @@ -1107,15 +1165,15 @@ gray_start_cell( worker, TRUNC( x ), TRUNC( y ) ); - worker->x = x; - worker->y = y; + ras.x = x; + ras.y = y; return 0; } static void - gray_render_span( int count, + gray_render_span( int count, const QT_FT_Span* spans, - PWorker worker ) + PWorker worker ) { unsigned char* p; QT_FT_Bitmap* map = &worker->target; @@ -1127,34 +1185,30 @@ /* first of all, compute the scanline offset */ p = (unsigned char*)map->buffer - spans->y * map->pitch; if ( map->pitch >= 0 ) - p += ( map->rows - 1 ) * map->pitch; + p += ( map->rows - 1 ) * (unsigned int)map->pitch; if ( coverage ) { + unsigned char* q = p + spans->x; + + /* For small-spans it is faster to do it by ourselves than * calling `memset'. This is mainly due to the cost of the * function call. */ - if ( spans->len >= 8 ) - QT_FT_MEM_SET( p + spans->x, (unsigned char)coverage, spans->len ); - else + switch ( spans->len ) { - unsigned char* q = p + spans->x; - - - switch ( spans->len ) - { - case 7: *q++ = (unsigned char)coverage; Q_FALLTHROUGH(); - case 6: *q++ = (unsigned char)coverage; Q_FALLTHROUGH(); - case 5: *q++ = (unsigned char)coverage; Q_FALLTHROUGH(); - case 4: *q++ = (unsigned char)coverage; Q_FALLTHROUGH(); - case 3: *q++ = (unsigned char)coverage; Q_FALLTHROUGH(); - case 2: *q++ = (unsigned char)coverage; Q_FALLTHROUGH(); - case 1: *q = (unsigned char)coverage; - default: - ; - } + case 7: *q++ = coverage; Q_FALLTHROUGH(); + case 6: *q++ = coverage; Q_FALLTHROUGH(); + case 5: *q++ = coverage; Q_FALLTHROUGH(); + case 4: *q++ = coverage; Q_FALLTHROUGH(); + case 3: *q++ = coverage; Q_FALLTHROUGH(); + case 2: *q++ = coverage; Q_FALLTHROUGH(); + case 1: *q = coverage; Q_FALLTHROUGH(); + case 0: break; + default: + QT_FT_MEM_SET( q, coverage, spans->len ); } } } @@ -1167,9 +1221,7 @@ TPos area, int acount ) { - QT_FT_Span* span; - int coverage; - int skip; + int coverage; /* compute the coverage line's coverage, depending on the */ @@ -1202,14 +1254,24 @@ x += (TCoord)ras.min_ex; /* QT_FT_Span.x is a 16-bit short, so limit our coordinates appropriately */ - if ( x >= 32768 ) + if ( x >= 32767 ) x = 32767; + /* QT_FT_Span.y is a 16-bit short, so limit our coordinates appropriately */ + if ( y >= 32767 ) + y = 32767; + if ( coverage ) { + QT_FT_Span* span; + int count; + int skip; + + /* see whether we can add this span to the current list */ - span = ras.gray_spans + ras.num_gray_spans - 1; - if ( ras.num_gray_spans > 0 && + count = ras.num_gray_spans; + span = ras.gray_spans + count - 1; + if ( count > 0 && span->y == y && (int)span->x + span->len == (int)x && span->coverage == coverage ) @@ -1218,9 +1280,9 @@ return; } - if ( ras.num_gray_spans >= QT_FT_MAX_GRAY_SPANS ) + if ( count >= QT_FT_MAX_GRAY_SPANS ) { - if ( ras.render_span && ras.num_gray_spans > ras.skip_spans ) + if ( ras.render_span && count > ras.skip_spans ) { skip = ras.skip_spans > 0 ? ras.skip_spans : 0; ras.render_span( ras.num_gray_spans - skip, @@ -1302,6 +1364,8 @@ if ( ras.num_cells == 0 ) return; + QT_FT_TRACE7(( "gray_sweep: start\n" )); + for ( yindex = 0; yindex < ras.ycount; yindex++ ) { PCell cell = ras.ycells[yindex]; @@ -1331,6 +1395,8 @@ gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ), ras.count_ex - x ); } + + QT_FT_TRACE7(( "gray_sweep: end\n" )); } /*************************************************************************/ @@ -1364,7 +1430,7 @@ /* */ static int QT_FT_Outline_Decompose( const QT_FT_Outline* outline, - void* user ) + void* user ) { #undef SCALED #define SCALED( x ) (x) @@ -1382,6 +1448,9 @@ int error; char tag; /* current point's state */ + if ( !outline ) + return ErrRaster_Invalid_Outline; + first = 0; for ( n = 0; n < outline->n_contours; n++ ) @@ -1390,16 +1459,17 @@ last = outline->contours[n]; + if ( last < 0 ) + goto Invalid_Outline; limit = outline->points + last; - v_start = outline->points[first]; - v_last = outline->points[last]; - + v_start = outline->points[first]; v_start.x = SCALED( v_start.x ); v_start.y = SCALED( v_start.y ); - v_last.x = SCALED( v_last.x ); - v_last.y = SCALED( v_last.y ); + v_last = outline->points[last]; + v_last.x = SCALED( v_last.x ); + v_last.y = SCALED( v_last.y ); v_control = v_start; @@ -1435,6 +1505,8 @@ tags--; } + QT_FT_TRACE5(( " move to (%.2f, %.2f)\n", + v_start.x / 64.0, v_start.y / 64.0 )); error = gray_move_to( &v_start, user ); if ( error ) goto Exit; @@ -1455,6 +1527,8 @@ vec.x = SCALED( point->x ); vec.y = SCALED( point->y ); + QT_FT_TRACE5(( " line to (%.2f, %.2f)\n", + vec.x / 64.0, vec.y / 64.0 )); gray_render_line(user, UPSCALE(vec.x), UPSCALE(vec.y)); continue; } @@ -1480,6 +1554,10 @@ if ( tag == QT_FT_CURVE_TAG_ON ) { + QT_FT_TRACE5(( " conic to (%.2f, %.2f)" + " with control (%.2f, %.2f)\n", + vec.x / 64.0, vec.y / 64.0, + v_control.x / 64.0, v_control.y / 64.0 )); gray_render_conic(user, &v_control, &vec); continue; } @@ -1490,11 +1568,20 @@ v_middle.x = ( v_control.x + vec.x ) / 2; v_middle.y = ( v_control.y + vec.y ) / 2; + QT_FT_TRACE5(( " conic to (%.2f, %.2f)" + " with control (%.2f, %.2f)\n", + v_middle.x / 64.0, v_middle.y / 64.0, + v_control.x / 64.0, v_control.y / 64.0 )); gray_render_conic(user, &v_control, &v_middle); + v_control = vec; goto Do_Conic; } + QT_FT_TRACE5(( " conic to (%.2f, %.2f)" + " with control (%.2f, %.2f)\n", + v_start.x / 64.0, v_start.y / 64.0, + v_control.x / 64.0, v_control.y / 64.0 )); gray_render_conic(user, &v_control, &v_start); goto Close; } @@ -1525,10 +1612,20 @@ vec.x = SCALED( point->x ); vec.y = SCALED( point->y ); + QT_FT_TRACE5(( " cubic to (%.2f, %.2f)" + " with controls (%.2f, %.2f) and (%.2f, %.2f)\n", + vec.x / 64.0, vec.y / 64.0, + vec1.x / 64.0, vec1.y / 64.0, + vec2.x / 64.0, vec2.y / 64.0 )); gray_render_cubic(user, &vec1, &vec2, &vec); continue; } + QT_FT_TRACE5(( " cubic to (%.2f, %.2f)" + " with controls (%.2f, %.2f) and (%.2f, %.2f)\n", + v_start.x / 64.0, v_start.y / 64.0, + vec1.x / 64.0, vec1.y / 64.0, + vec2.x / 64.0, vec2.y / 64.0 )); gray_render_cubic(user, &vec1, &vec2, &v_start); goto Close; } @@ -1536,15 +1633,19 @@ } /* close the contour with a line segment */ + QT_FT_TRACE5(( " line to (%.2f, %.2f)\n", + v_start.x / 64.0, v_start.y / 64.0 )); gray_render_line(user, UPSCALE(v_start.x), UPSCALE(v_start.y)); Close: first = last + 1; } + QT_FT_TRACE5(( "FT_Outline_Decompose: Done\n", n )); return 0; Exit: + QT_FT_TRACE5(( "FT_Outline_Decompose: Error %d\n", error )); return error; Invalid_Outline: @@ -1557,7 +1658,6 @@ } TBand; - static int gray_convert_glyph_inner( RAS_ARG ) { @@ -1566,7 +1666,8 @@ if ( qt_ft_setjmp( ras.jump_buffer ) == 0 ) { error = QT_FT_Outline_Decompose( &ras.outline, &ras ); - gray_record_cell( RAS_VAR ); + if ( !ras.invalid ) + gray_record_cell( RAS_VAR ); } else { @@ -1608,29 +1709,12 @@ ras.count_ex = ras.max_ex - ras.min_ex; ras.count_ey = ras.max_ey - ras.min_ey; - /* simple heuristic used to speed-up the bezier decomposition -- see */ - /* the code in gray_render_conic() and gray_render_cubic() for more */ - /* details */ - ras.conic_level = 32; - ras.cubic_level = 16; - - { - int level = 0; - - - if ( ras.count_ex > 24 || ras.count_ey > 24 ) - level++; - if ( ras.count_ex > 120 || ras.count_ey > 120 ) - level++; - - ras.conic_level <<= level; - ras.cubic_level <<= level; - } - - /* setup vertical bands */ + /* set up vertical bands */ num_bands = (int)( ( ras.max_ey - ras.min_ey ) / ras.band_size ); - if ( num_bands == 0 ) num_bands = 1; - if ( num_bands >= 39 ) num_bands = 39; + if ( num_bands == 0 ) + num_bands = 1; + if ( num_bands >= 39 ) + num_bands = 39; ras.band_shoot = 0; @@ -1764,11 +1848,14 @@ if (raster->buffer_allocated_size < MINIMUM_POOL_SIZE ) return ErrRaster_OutOfMemory; + if ( !outline ) + return ErrRaster_Invalid_Outline; + /* return immediately if the outline is empty */ if ( outline->n_points == 0 || outline->n_contours <= 0 ) return 0; - if ( !outline || !outline->contours || !outline->points ) + if ( !outline->contours || !outline->points ) return ErrRaster_Invalid_Outline; if ( outline->n_points != diff --git a/src/gui/painting/qpagedpaintdevice.cpp b/src/gui/painting/qpagedpaintdevice.cpp index 22ec981134..1c7d6471b6 100644 --- a/src/gui/painting/qpagedpaintdevice.cpp +++ b/src/gui/painting/qpagedpaintdevice.cpp @@ -243,6 +243,18 @@ QPagedPaintDevicePrivate *QPagedPaintDevice::dd() Starts a new page. Returns \c true on success. */ +/*! + \enum QPagedPaintDevice::PdfVersion + + The PdfVersion enum describes the version of the PDF file that + is produced by QPrinter or QPdfWriter. + + \value PdfVersion_1_4 A PDF 1.4 compatible document is produced. + + \value PdfVersion_A1b A PDF/A-1b compatible document is produced. + + \since 5.10 +*/ /*! Sets the size of the a page to \a size. diff --git a/src/gui/painting/qpagedpaintdevice.h b/src/gui/painting/qpagedpaintdevice.h index c516f6a963..66dd6fa8cf 100644 --- a/src/gui/painting/qpagedpaintdevice.h +++ b/src/gui/painting/qpagedpaintdevice.h @@ -213,6 +213,8 @@ public: Envelope10 = Comm10E }; + enum PdfVersion { PdfVersion_1_4, PdfVersion_A1b }; + // ### Qt6 Make these virtual bool setPageLayout(const QPageLayout &pageLayout); bool setPageSize(const QPageSize &pageSize); diff --git a/src/gui/painting/qpaintengine.cpp b/src/gui/painting/qpaintengine.cpp index ddea168e72..f42fd4ff87 100644 --- a/src/gui/painting/qpaintengine.cpp +++ b/src/gui/painting/qpaintengine.cpp @@ -547,8 +547,8 @@ void qt_fill_tile(QPixmap *tile, const QPixmap &pixmap) } } -void qt_draw_tile(QPaintEngine *gc, qreal x, qreal y, qreal w, qreal h, - const QPixmap &pixmap, qreal xOffset, qreal yOffset) +Q_GUI_EXPORT void qt_draw_tile(QPaintEngine *gc, qreal x, qreal y, qreal w, qreal h, + const QPixmap &pixmap, qreal xOffset, qreal yOffset) { qreal yPos, xPos, drawH, drawW, yOff, xOff; yPos = y; @@ -929,11 +929,11 @@ QPoint QPaintEngine::coordinateOffset() const void QPaintEngine::setSystemClip(const QRegion ®ion) { Q_D(QPaintEngine); - d->systemClip = region; + d->baseSystemClip = region; // Be backward compatible and only call d->systemStateChanged() // if we currently have a system transform/viewport set. + d->updateSystemClip(); if (d->hasSystemTransform || d->hasSystemViewport) { - d->transformSystemClip(); d->systemStateChanged(); } } diff --git a/src/gui/painting/qpaintengine_p.h b/src/gui/painting/qpaintengine_p.h index 9d511f9bad..8ac3fcff5c 100644 --- a/src/gui/painting/qpaintengine_p.h +++ b/src/gui/painting/qpaintengine_p.h @@ -71,6 +71,7 @@ public: QPaintDevice *pdev; QPaintEngine *q_ptr; + QRegion baseSystemClip; QRegion systemClip; QRect systemRect; QRegion systemViewport; @@ -79,8 +80,9 @@ public: uint hasSystemTransform : 1; uint hasSystemViewport : 1; - inline void transformSystemClip() + inline void updateSystemClip() { + systemClip = baseSystemClip; if (systemClip.isEmpty()) return; @@ -104,15 +106,30 @@ public: inline void setSystemTransform(const QTransform &xform) { systemTransform = xform; - if ((hasSystemTransform = !xform.isIdentity()) || hasSystemViewport) - transformSystemClip(); - systemStateChanged(); + hasSystemTransform = !xform.isIdentity(); + updateSystemClip(); + if (q_ptr->state) + systemStateChanged(); } inline void setSystemViewport(const QRegion ®ion) { systemViewport = region; hasSystemViewport = !systemViewport.isEmpty(); + updateSystemClip(); + if (q_ptr->state) + systemStateChanged(); + } + + inline void setSystemTransformAndViewport(const QTransform &xform, const QRegion ®ion) + { + systemTransform = xform; + hasSystemTransform = !xform.isIdentity(); + systemViewport = region; + hasSystemViewport = !systemViewport.isEmpty(); + updateSystemClip(); + if (q_ptr->state) + systemStateChanged(); } virtual void systemStateChanged() { } diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 92ab6e8375..d0d948bbb7 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -50,6 +50,7 @@ #include <qdebug.h> #include <qbitmap.h> #include <qmath.h> +#include <qrandom.h> // #include <private/qdatabuffer_p.h> // #include <private/qpainter_p.h> @@ -993,7 +994,7 @@ void QRasterPaintEnginePrivate::drawImage(const QPointF &pt, Q_ASSERT(img.depth() >= 8); - int srcBPL = img.bytesPerLine(); + qssize_t srcBPL = img.bytesPerLine(); const uchar *srcBits = img.bits(); int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit.. int iw = img.width(); @@ -1042,7 +1043,7 @@ void QRasterPaintEnginePrivate::drawImage(const QPointF &pt, // call the blend function... int dstSize = rasterBuffer->bytesPerPixel(); - int dstBPL = rasterBuffer->bytesPerLine(); + qssize_t dstBPL = rasterBuffer->bytesPerLine(); func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL, srcBits, srcBPL, iw, ih, @@ -2317,8 +2318,8 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe clippedSourceRect = clippedSourceRect.intersected(img.rect()); - uint dbpl = d->rasterBuffer->bytesPerLine(); - uint sbpl = img.bytesPerLine(); + const qssize_t dbpl = d->rasterBuffer->bytesPerLine(); + const qssize_t sbpl = img.bytesPerLine(); uchar *dst = d->rasterBuffer->buffer(); uint bpp = img.depth() >> 3; @@ -2827,7 +2828,7 @@ bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, cache->fillInPendingGlyphs(); const QImage &image = cache->image(); - int bpl = image.bytesPerLine(); + qssize_t bpl = image.bytesPerLine(); int depth = image.depth(); int rightShift = 0; @@ -4229,7 +4230,7 @@ protected: QSharedPointer<const CacheInfo> addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) { if (cache.size() == maxCacheSize()) { // may remove more than 1, but OK - cache.erase(cache.begin() + (qrand() % maxCacheSize())); + cache.erase(cache.begin() + QRandomGenerator::global()->bounded(maxCacheSize())); } auto cache_entry = QSharedPointer<CacheInfo>::create(gradient.stops(), opacity, gradient.interpolationMode()); generateGradientColorTable(gradient, cache_entry->buffer64, paletteSize(), opacity); diff --git a/src/gui/painting/qpaintengine_raster_p.h b/src/gui/painting/qpaintengine_raster_p.h index d0b82b3a93..e2e0f821e3 100644 --- a/src/gui/painting/qpaintengine_raster_p.h +++ b/src/gui/painting/qpaintengine_raster_p.h @@ -457,6 +457,8 @@ public: int height() const { return m_height; } int bytesPerLine() const { return bytes_per_line; } int bytesPerPixel() const { return bytes_per_pixel; } + template<typename T> + int stride() { return bytes_per_line / sizeof(T); } uchar *buffer() const { return m_buffer; } diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 49d8fd2846..b95f9b18aa 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -5850,6 +5850,7 @@ void QPainter::drawText(const QPointF &p, const QString &str, int tf, int justif if (!d->engine || str.isEmpty() || pen().style() == Qt::NoPen) return; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (tf & Qt::TextBypassShaping) { // Skip complex shaping, shape using glyph advances only int len = str.length(); @@ -5863,6 +5864,7 @@ void QPainter::drawText(const QPointF &p, const QString &str, int tf, int justif drawTextItem(p, gf); return; } +#endif QStackTextEngine engine(str, d->state->font); engine.option.setTextDirection(d->state->layoutDirection); @@ -7419,7 +7421,7 @@ void qt_format_text(const QFont &fnt, const QRectF &_r, if (option->flags() & QTextOption::IncludeTrailingSpaces) tf |= Qt::TextIncludeTrailingSpaces; - if (option->tabStop() >= 0 || !option->tabArray().isEmpty()) + if (option->tabStopDistance() >= 0 || !option->tabArray().isEmpty()) tf |= Qt::TextExpandTabs; } @@ -7536,8 +7538,8 @@ start_lengthVariant: engine.option = *option; } - if (engine.option.tabStop() < 0 && tabstops > 0) - engine.option.setTabStop(tabstops); + if (engine.option.tabStopDistance() < 0 && tabstops > 0) + engine.option.setTabStopDistance(tabstops); if (engine.option.tabs().isEmpty() && ta) { QList<qreal> tabs; diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp index afeb198953..e421055ef3 100644 --- a/src/gui/painting/qpdf.cpp +++ b/src/gui/painting/qpdf.cpp @@ -42,16 +42,20 @@ #ifndef QT_NO_PDF #include "qplatformdefs.h" -#include <qdebug.h> -#include <qfile.h> -#include <qtemporaryfile.h> + +#include <private/qfont_p.h> #include <private/qmath_p.h> #include <private/qpainter_p.h> -#include <qnumeric.h> -#include "private/qfont_p.h" + +#include <qbuffer.h> +#include <qcryptographichash.h> +#include <qdatetime.h> +#include <qdebug.h> +#include <qfile.h> #include <qimagewriter.h> -#include "qbuffer.h" -#include "QtCore/qdatetime.h" +#include <qnumeric.h> +#include <qtemporaryfile.h> +#include <quuid.h> #ifndef QT_NO_COMPRESS #include <zlib.h> @@ -79,6 +83,45 @@ inline QPaintEngine::PaintEngineFeatures qt_pdf_decide_features() return f; } +extern bool qt_isExtendedRadialGradient(const QBrush &brush); + +// helper function to remove transparency from brush in PDF/A-1b mode +static void removeTransparencyFromBrush(QBrush &brush) +{ + if (brush.style() == Qt::SolidPattern) { + QColor color = brush.color(); + if (color.alpha() != 255) { + color.setAlpha(255); + brush.setColor(color); + } + + return; + } + + if (qt_isExtendedRadialGradient(brush)) { + brush = QBrush(Qt::black); // the safest we can do so far... + return; + } + + if (brush.style() == Qt::LinearGradientPattern + || brush.style() == Qt::RadialGradientPattern + || brush.style() == Qt::ConicalGradientPattern) { + + QGradientStops stops = brush.gradient()->stops(); + for (int i = 0; i < stops.size(); ++i) { + if (stops[i].second.alpha() != 255) + stops[i].second.setAlpha(255); + } + + const_cast<QGradient*>(brush.gradient())->setStops(stops); + return; + } + + if (brush.style() == Qt::TexturePattern) { + // handled inside QPdfEnginePrivate::addImage() already + return; + } +} /* also adds a space at the end of the number */ @@ -1020,7 +1063,12 @@ void QPdfEngine::drawHyperlink(const QRectF &r, const QUrl &url) char buf[256]; const QRectF rr = d->pageMatrix().mapRect(r); - d->xprintf("<<\n/Type /Annot\n/Subtype /Link\n/Rect ["); + d->xprintf("<<\n/Type /Annot\n/Subtype /Link\n"); + + if (d->pdfVersion == QPdfEngine::Version_A1b) + d->xprintf("/F 4\n"); // enable print flag, disable all other + + d->xprintf("/Rect ["); d->xprintf("%s ", qt_real_to_string(rr.left(), buf)); d->xprintf("%s ", qt_real_to_string(rr.top(), buf)); d->xprintf("%s ", qt_real_to_string(rr.right(), buf)); @@ -1042,7 +1090,22 @@ void QPdfEngine::updateState(const QPaintEngineState &state) d->stroker.matrix = state.transform(); if (flags & DirtyPen) { - d->pen = state.pen(); + if (d->pdfVersion == QPdfEngine::Version_A1b) { + QPen pen = state.pen(); + + QColor penColor = pen.color(); + if (penColor.alpha() != 255) + penColor.setAlpha(255); + pen.setColor(penColor); + + QBrush penBrush = pen.brush(); + removeTransparencyFromBrush(penBrush); + pen.setBrush(penBrush); + + d->pen = pen; + } else { + d->pen = state.pen(); + } d->hasPen = d->pen.style() != Qt::NoPen; d->stroker.setPen(d->pen, state.renderHints()); QBrush penBrush = d->pen.brush(); @@ -1054,7 +1117,13 @@ void QPdfEngine::updateState(const QPaintEngineState &state) d->stroker.setPen(d->pen, state.renderHints()); } if (flags & DirtyBrush) { - d->brush = state.brush(); + if (d->pdfVersion == QPdfEngine::Version_A1b) { + QBrush brush = state.brush(); + removeTransparencyFromBrush(brush); + d->brush = brush; + } else { + d->brush = state.brush(); + } if (d->brush.color().alpha() == 0 && d->brush.style() == Qt::SolidPattern) d->brush.setStyle(Qt::NoBrush); d->hasBrush = d->brush.style() != Qt::NoBrush; @@ -1286,6 +1355,12 @@ int QPdfEngine::resolution() const return d->resolution; } +void QPdfEngine::setPdfVersion(PdfVersion version) +{ + Q_D(QPdfEngine); + d->pdfVersion = version; +} + void QPdfEngine::setPageLayout(const QPageLayout &pageLayout) { Q_D(QPdfEngine); @@ -1364,6 +1439,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), outDevice(0), ownsDevice(false), embedFonts(true), grayscale(false), @@ -1465,16 +1541,38 @@ void QPdfEnginePrivate::writeHeader() addXrefEntry(0,false); xprintf("%%PDF-1.4\n"); + xprintf("%%\303\242\303\243\n"); writeInfo(); + int metaDataObj = -1; + int outputIntentObj = -1; + if (pdfVersion == QPdfEngine::Version_A1b) { + metaDataObj = writeXmpMetaData(); + outputIntentObj = writeOutputIntent(); + } + catalog = addXrefEntry(-1); pageRoot = requestObject(); - xprintf("<<\n" - "/Type /Catalog\n" - "/Pages %d 0 R\n" - ">>\n" - "endobj\n", pageRoot); + + // catalog + { + QByteArray catalog; + QPdf::ByteStream s(&catalog); + s << "<<\n" + << "/Type /Catalog\n" + << "/Pages " << pageRoot << "0 R\n"; + + if (pdfVersion == QPdfEngine::Version_A1b) { + s << "/OutputIntents [" << outputIntentObj << "0 R]\n"; + s << "/Metadata " << metaDataObj << "0 R\n"; + } + + s << ">>\n" + << "endobj\n"; + + write(catalog); + } // graphics state graphicsState = addXrefEntry(-1); @@ -1527,6 +1625,95 @@ void QPdfEnginePrivate::writeInfo() "endobj\n"); } +int QPdfEnginePrivate::writeXmpMetaData() +{ + const int metaDataObj = addXrefEntry(-1); + + const QString producer(QString::fromLatin1("Qt " QT_VERSION_STR)); + + const QDateTime now = QDateTime::currentDateTime(); + const QDate date = now.date(); + const QTime time = now.time(); + + QString timeStr; + timeStr.sprintf("%d-%02d-%02dT%02d:%02d:%02d", date.year(), date.month(), date.day(), + time.hour(), time.minute(), time.second()); + + const int offset = now.offsetFromUtc(); + const int hours = (offset / 60) / 60; + const int mins = (offset / 60) % 60; + QString tzStr; + if (offset < 0) + tzStr.sprintf("-%02d:%02d", -hours, -mins); + else if (offset > 0) + tzStr.sprintf("+%02d:%02d", hours , mins); + else + tzStr = QLatin1String("Z"); + + const QString metaDataDate = timeStr + tzStr; + + QFile metaDataFile(QLatin1String(":/qpdf/qpdfa_metadata.xml")); + metaDataFile.open(QIODevice::ReadOnly); + const QByteArray metaDataContent = QString::fromUtf8(metaDataFile.readAll()).arg(producer.toHtmlEscaped(), + title.toHtmlEscaped(), + creator.toHtmlEscaped(), + metaDataDate).toUtf8(); + xprintf("<<\n" + "/Type /Metadata /Subtype /XML\n" + "/Length %d\n" + ">>\n" + "stream\n", metaDataContent.size()); + write(metaDataContent); + xprintf("\nendstream\n" + "endobj\n"); + + return metaDataObj; +} + +int QPdfEnginePrivate::writeOutputIntent() +{ + const int colorProfile = addXrefEntry(-1); + { + QFile colorProfileFile(QLatin1String(":/qpdf/sRGB2014.icc")); + colorProfileFile.open(QIODevice::ReadOnly); + const QByteArray colorProfileData = colorProfileFile.readAll(); + + QByteArray data; + QPdf::ByteStream s(&data); + int length_object = requestObject(); + + s << "<<\n"; + s << "/N 3\n"; + s << "/Alternate /DeviceRGB\n"; + s << "/Length " << length_object << "0 R\n"; + s << "/Filter /FlateDecode\n"; + s << ">>\n"; + s << "stream\n"; + write(data); + const int len = writeCompressed(colorProfileData); + write("\nendstream\n" + "endobj\n"); + addXrefEntry(length_object); + xprintf("%d\n" + "endobj\n", len); + } + + const int outputIntent = addXrefEntry(-1); + { + xprintf("<<\n"); + xprintf("/Type /OutputIntent\n"); + xprintf("/S/GTS_PDFA1\n"); + xprintf("/OutputConditionIdentifier (sRGB_IEC61966-2-1_black_scaled)\n"); + xprintf("/DestOutputProfile %d 0 R\n", colorProfile); + xprintf("/Info(sRGB IEC61966 v2.1 with black scaling)\n"); + xprintf("/RegistryName(http://www.color.org)\n"); + xprintf(">>\n"); + xprintf("endobj\n"); + } + + return outputIntent; +} + void QPdfEnginePrivate::writePageRoot() { addXrefEntry(pageRoot); @@ -1568,6 +1755,7 @@ void QPdfEnginePrivate::embedFont(QFontSubset *font) int fontstream = requestObject(); int cidfont = requestObject(); int toUnicode = requestObject(); + int cidset = requestObject(); QFontEngine::Properties properties = font->fontEngine->properties(); QByteArray postscriptName = properties.postscriptName.replace(' ', '_'); @@ -1597,7 +1785,8 @@ void QPdfEnginePrivate::embedFont(QFontSubset *font) "/CapHeight " << properties.capHeight.toReal()*scale << "\n" "/StemV " << properties.lineWidth.toReal()*scale << "\n" "/FontFile2 " << fontstream << "0 R\n" - ">> endobj\n"; + "/CIDSet " << cidset << "0 R\n" + ">>\nendobj\n"; write(descriptor); } { @@ -1615,7 +1804,7 @@ void QPdfEnginePrivate::embedFont(QFontSubset *font) "stream\n"; write(header); int len = writeCompressed(fontData); - write("endstream\n" + write("\nendstream\n" "endobj\n"); addXrefEntry(length_object); xprintf("%d\n" @@ -1642,7 +1831,7 @@ void QPdfEnginePrivate::embedFont(QFontSubset *font) xprintf("<< /Length %d >>\n" "stream\n", touc.length()); write(touc); - write("endstream\n" + write("\nendstream\n" "endobj\n"); } { @@ -1659,6 +1848,29 @@ void QPdfEnginePrivate::embedFont(QFontSubset *font) "endobj\n"; write(font); } + { + QByteArray cidSetStream(font->nGlyphs() / 8 + 1, 0); + int byteCounter = 0; + int bitCounter = 0; + for (int i = 0; i < font->nGlyphs(); ++i) { + cidSetStream.data()[byteCounter] |= (1 << (7 - bitCounter)); + + bitCounter++; + if (bitCounter == 8) { + bitCounter = 0; + byteCounter++; + } + } + + addXrefEntry(cidset); + xprintf("<<\n"); + xprintf("/Length %d\n", cidSetStream.size()); + xprintf(">>\n"); + xprintf("stream\n"); + write(cidSetStream); + xprintf("\nendstream\n"); + xprintf("endobj\n"); + } } @@ -1748,7 +1960,7 @@ void QPdfEnginePrivate::writePage() xprintf("stream\n"); QIODevice *content = currentPage->stream(); int len = writeCompressed(content); - xprintf("endstream\n" + xprintf("\nendstream\n" "endobj\n"); addXrefEntry(pageStreamLength); @@ -1768,15 +1980,28 @@ void QPdfEnginePrivate::writeTail() for (int i = 1; i < xrefPositions.size()-1; ++i) xprintf("%010d 00000 n \n", xrefPositions[i]); - xprintf("trailer\n" - "<<\n" - "/Size %d\n" - "/Info %d 0 R\n" - "/Root %d 0 R\n" - ">>\n" - "startxref\n%d\n" - "%%%%EOF\n", - xrefPositions.size()-1, info, catalog, xrefPositions.constLast()); + { + QByteArray trailer; + QPdf::ByteStream s(&trailer); + + s << "trailer\n" + << "<<\n" + << "/Size " << xrefPositions.size() - 1 << "\n" + << "/Info " << info << "0 R\n" + << "/Root " << catalog << "0 R\n"; + + if (pdfVersion == QPdfEngine::Version_A1b) { + const QString uniqueId = QUuid::createUuid().toString(); + const QByteArray fileIdentifier = QCryptographicHash::hash(uniqueId.toLatin1(), QCryptographicHash::Md5).toHex(); + s << "/ID [ <" << fileIdentifier << "> <" << fileIdentifier << "> ]\n"; + } + + s << ">>\n" + << "startxref\n" << xrefPositions.constLast() << "\n" + << "%%EOF\n"; + + write(trailer); + } } int QPdfEnginePrivate::addXrefEntry(int object, bool printostr) @@ -1794,7 +2019,13 @@ int QPdfEnginePrivate::addXrefEntry(int object, bool printostr) return object; } -void QPdfEnginePrivate::printString(const QString &string) { +void QPdfEnginePrivate::printString(const QString &string) +{ + if (string.isEmpty()) { + write("()"); + return; + } + // The 'text string' type in PDF is encoded either as PDFDocEncoding, or // Unicode UTF-16 with a Unicode byte order mark as the first character // (0xfeff), with the high-order byte first. @@ -1976,7 +2207,7 @@ int QPdfEnginePrivate::writeImage(const QByteArray &data, int width, int height, xprintf(">>\nstream\n"); len = writeCompressed(data); } - xprintf("endstream\n" + xprintf("\nendstream\n" "endobj\n"); addXrefEntry(lenobj); xprintf("%d\n" @@ -2303,7 +2534,7 @@ int QPdfEnginePrivate::gradientBrush(const QBrush &b, const QTransform &matrix, ">>\n" "stream\n" << content - << "endstream\n" + << "\nendstream\n" "endobj\n"; int softMaskFormObject = addXrefEntry(-1); @@ -2412,7 +2643,7 @@ int QPdfEnginePrivate::addBrushPattern(const QTransform &m, bool *specifyColor, ">>\n" "stream\n" << pattern - << "endstream\n" + << "\nendstream\n" "endobj\n"; int patternObj = addXrefEntry(-1); @@ -2443,6 +2674,23 @@ int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, qint64 serial_n QImage image = img; QImage::Format format = image.format(); + + if (pdfVersion == QPdfEngine::Version_A1b) { + if (image.hasAlphaChannel()) { + // transparent images are not allowed in PDF/A-1b, so we convert it to + // a format without alpha channel first + + QImage alphaLessImage(image.width(), image.height(), QImage::Format_RGB32); + alphaLessImage.fill(Qt::white); + + QPainter p(&alphaLessImage); + p.drawImage(0, 0, image); + + image = alphaLessImage; + format = image.format(); + } + } + if (image.depth() == 1 && *bitmap && is_monochrome(img.colorTable())) { if (format == QImage::Format_MonoLSB) image = image.convertToFormat(QImage::Format_Mono); @@ -2588,7 +2836,12 @@ void QPdfEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti) x2s.setNum(static_cast<double>(x2), 'f'); y2s.setNum(static_cast<double>(y2), 'f'); QByteArray rectData = x1s + ' ' + y1s + ' ' + x2s + ' ' + y2s; - xprintf("<<\n/Type /Annot\n/Subtype /Link\n/Rect ["); + xprintf("<<\n/Type /Annot\n/Subtype /Link\n"); + + if (pdfVersion == QPdfEngine::Version_A1b) + xprintf("/F 4\n"); // enable print flag, disable all other + + xprintf("/Rect ["); xprintf(rectData.constData()); #ifdef Q_DEBUG_PDF_LINKS xprintf("]\n/Border [16 16 1]\n/A <<\n"); diff --git a/src/gui/painting/qpdf.qrc b/src/gui/painting/qpdf.qrc new file mode 100644 index 0000000000..56359c775b --- /dev/null +++ b/src/gui/painting/qpdf.qrc @@ -0,0 +1,7 @@ +<!DOCTYPE RCC> +<RCC version="1.0"> + <qresource prefix="qpdf/"> + <file>qpdfa_metadata.xml</file> + <file alias="sRGB2014.icc">../../3rdparty/icc/sRGB2014.icc</file> + </qresource> +</RCC> diff --git a/src/gui/painting/qpdf_p.h b/src/gui/painting/qpdf_p.h index a6aa2940c8..f5bb4e17a8 100644 --- a/src/gui/painting/qpdf_p.h +++ b/src/gui/painting/qpdf_p.h @@ -168,6 +168,12 @@ class Q_GUI_EXPORT QPdfEngine : public QPaintEngine Q_DECLARE_PRIVATE(QPdfEngine) friend class QPdfWriter; public: + enum PdfVersion + { + Version_1_4, + Version_A1b + }; + QPdfEngine(); QPdfEngine(QPdfEnginePrivate &d); ~QPdfEngine() {} @@ -177,6 +183,8 @@ public: void setResolution(int resolution); int resolution() const; + void setPdfVersion(PdfVersion version); + // reimplementations QPaintEngine bool begin(QPaintDevice *pdev) Q_DECL_OVERRIDE; bool end() Q_DECL_OVERRIDE; @@ -258,6 +266,7 @@ public: bool hasBrush; bool simplePen; qreal opacity; + QPdfEngine::PdfVersion pdfVersion; QHash<QFontEngine::FaceId, QFontSubset *> fonts; @@ -286,6 +295,8 @@ private: int createShadingFunction(const QGradient *gradient, int from, int to, bool reflect, bool alpha); void writeInfo(); + int writeXmpMetaData(); + int writeOutputIntent(); void writePageRoot(); void writeFonts(); void embedFont(QFontSubset *font); diff --git a/src/gui/painting/qpdfa_metadata.xml b/src/gui/painting/qpdfa_metadata.xml new file mode 100644 index 0000000000..5e5c57f1c6 --- /dev/null +++ b/src/gui/painting/qpdfa_metadata.xml @@ -0,0 +1,16 @@ +<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?> +<x:xmpmeta xmlns:x="adobe:ns:meta/"> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> + <rdf:Description xmlns:dc="http://purl.org/dc/elements/1.1/" rdf:about=""> + <dc:title> + <rdf:Alt> + <rdf:li xml:lang="x-default">%2</rdf:li> + </rdf:Alt> + </dc:title> + </rdf:Description> + <rdf:Description xmlns:xmp="http://ns.adobe.com/xap/1.0/" rdf:about="" xmp:CreatorTool="%3" xmp:CreateDate="%4" xmp:ModifyDate="%4"/> + <rdf:Description xmlns:pdf="http://ns.adobe.com/pdf/1.3/" rdf:about="" pdf:Producer="%1"/> + <rdf:Description xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/" rdf:about="" pdfaid:part="1" pdfaid:conformance="B"/> + </rdf:RDF> +</x:xmpmeta> +<?xpacket end='w'?> diff --git a/src/gui/painting/qpdfwriter.cpp b/src/gui/painting/qpdfwriter.cpp index edf2950a67..5af465edeb 100644 --- a/src/gui/painting/qpdfwriter.cpp +++ b/src/gui/painting/qpdfwriter.cpp @@ -56,6 +56,7 @@ public: { engine = new QPdfEngine(); output = 0; + pdfVersion = QPdfWriter::PdfVersion_1_4; } ~QPdfWriterPrivate() { @@ -65,6 +66,7 @@ public: QPdfEngine *engine; QFile *output; + QPdfWriter::PdfVersion pdfVersion; }; class QPdfPagedPaintDevicePrivate : public QPagedPaintDevicePrivate @@ -177,6 +179,35 @@ QPdfWriter::~QPdfWriter() } /*! + \since 5.10 + + Sets the PDF version for this writer to \a version. + + If \a version is the same value as currently set then no change will be made. +*/ +void QPdfWriter::setPdfVersion(PdfVersion version) +{ + Q_D(QPdfWriter); + + if (d->pdfVersion == version) + return; + + d->pdfVersion = version; + d->engine->setPdfVersion(d->pdfVersion == QPdfWriter::PdfVersion_1_4 ? QPdfEngine::Version_1_4 : QPdfEngine::Version_A1b); +} + +/*! + \since 5.10 + + Returns the PDF version for this writer. The default is \c PdfVersion_1_4. +*/ +QPdfWriter::PdfVersion QPdfWriter::pdfVersion() const +{ + Q_D(const QPdfWriter); + return d->pdfVersion; +} + +/*! Returns the title of the document. */ QString QPdfWriter::title() const diff --git a/src/gui/painting/qpdfwriter.h b/src/gui/painting/qpdfwriter.h index 17c73dd480..b260805b2b 100644 --- a/src/gui/painting/qpdfwriter.h +++ b/src/gui/painting/qpdfwriter.h @@ -61,6 +61,9 @@ public: explicit QPdfWriter(QIODevice *device); ~QPdfWriter(); + void setPdfVersion(PdfVersion version); + PdfVersion pdfVersion() const; + QString title() const; void setTitle(const QString &title); diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp index 8215255cf5..8ab22beb31 100644 --- a/src/gui/painting/qplatformbackingstore.cpp +++ b/src/gui/painting/qplatformbackingstore.cpp @@ -50,6 +50,7 @@ #include <QtGui/QOpenGLFunctions> #ifndef QT_NO_OPENGL #include <QtGui/qopengltextureblitter.h> +#include <QtGui/qoffscreensurface.h> #endif #include <qpa/qplatformgraphicsbuffer.h> #include <qpa/qplatformgraphicsbufferhelper.h> @@ -70,6 +71,13 @@ #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 #endif +#ifndef GL_FRAMEBUFFER_SRB +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#endif +#ifndef GL_FRAMEBUFFER_SRGB_CAPABLE +#define GL_FRAMEBUFFER_SRGB_CAPABLE 0x8DBA +#endif + QT_BEGIN_NAMESPACE class QPlatformBackingStorePrivate @@ -88,14 +96,15 @@ public: ~QPlatformBackingStorePrivate() { #ifndef QT_NO_OPENGL - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - if (ctx) { + if (context) { + QOffscreenSurface offscreenSurface; + offscreenSurface.setFormat(context->format()); + offscreenSurface.create(); + context->makeCurrent(&offscreenSurface); if (textureId) - ctx->functions()->glDeleteTextures(1, &textureId); + context->functions()->glDeleteTextures(1, &textureId); if (blitter) blitter->destroy(); - } else if (textureId || blitter) { - qWarning("No context current during QPlatformBackingStore destruction, OpenGL resources not released"); } delete blitter; #endif @@ -103,6 +112,7 @@ public: QWindow *window; QBackingStore *backingStore; #ifndef QT_NO_OPENGL + QScopedPointer<QOpenGLContext> context; mutable GLuint textureId; mutable QSize textureSize; mutable bool needsSwizzle; @@ -227,17 +237,6 @@ void QPlatformTextureList::clear() windows. */ -/*! - \fn void QPlatformBackingStore::flush(QWindow *window, const QRegion ®ion, - const QPoint &offset) - - Flushes the given \a region from the specified \a window onto the - screen. - - The \a offset parameter is relative to the origin of the backing - store image. -*/ - #ifndef QT_NO_OPENGL static inline QRect deviceRect(const QRect &rect, QWindow *window) @@ -274,7 +273,7 @@ static inline QRect toBottomLeftRect(const QRect &topLeftRect, int windowHeight) } static void blitTextureForWidget(const QPlatformTextureList *textures, int idx, QWindow *window, const QRect &deviceWindowRect, - QOpenGLTextureBlitter *blitter, const QPoint &offset) + QOpenGLTextureBlitter *blitter, const QPoint &offset, bool canUseSrgb) { const QRect clipRect = textures->clipRect(idx); if (clipRect.isEmpty()) @@ -294,7 +293,15 @@ static void blitTextureForWidget(const QPlatformTextureList *textures, int idx, deviceRect(rectInWindow, window).size(), QOpenGLTextureBlitter::OriginBottomLeft); + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + const bool srgb = textures->flags(idx).testFlag(QPlatformTextureList::TextureIsSrgb); + if (srgb && canUseSrgb) + funcs->glEnable(GL_FRAMEBUFFER_SRGB); + blitter->blit(textures->textureId(idx), target, source); + + if (srgb && canUseSrgb) + funcs->glDisable(GL_FRAMEBUFFER_SRGB); } /*! @@ -312,20 +319,31 @@ static void blitTextureForWidget(const QPlatformTextureList *textures, int idx, void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®ion, const QPoint &offset, - QPlatformTextureList *textures, QOpenGLContext *context, + QPlatformTextureList *textures, bool translucentBackground) { if (!qt_window_private(window)->receivedExpose) return; - if (!context->makeCurrent(window)) { + if (!d_ptr->context) { + d_ptr->context.reset(new QOpenGLContext); + d_ptr->context->setFormat(d_ptr->window->requestedFormat()); + d_ptr->context->setScreen(d_ptr->window->screen()); + d_ptr->context->setShareContext(qt_window_private(d_ptr->window)->shareContext()); + if (!d_ptr->context->create()) { + qWarning("composeAndFlush: QOpenGLContext creation failed"); + return; + } + } + + if (!d_ptr->context->makeCurrent(window)) { qWarning("composeAndFlush: makeCurrent() failed"); return; } QWindowPrivate::get(window)->lastComposeTime.start(); - QOpenGLFunctions *funcs = context->functions(); + QOpenGLFunctions *funcs = d_ptr->context->functions(); funcs->glViewport(0, 0, window->width() * window->devicePixelRatio(), window->height() * window->devicePixelRatio()); funcs->glClearColor(0, 0, 0, translucentBackground ? 0 : 1); funcs->glClear(GL_COLOR_BUFFER_BIT); @@ -340,10 +358,23 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i const QRect deviceWindowRect = deviceRect(QRect(QPoint(), window->size()), window); const QPoint deviceWindowOffset = deviceOffset(offset, window); + bool canUseSrgb = false; + // If there are any sRGB textures in the list, check if the destination + // framebuffer is sRGB capable. + for (int i = 0; i < textures->count(); ++i) { + if (textures->flags(i).testFlag(QPlatformTextureList::TextureIsSrgb)) { + GLint cap = 0; + funcs->glGetIntegerv(GL_FRAMEBUFFER_SRGB_CAPABLE, &cap); + if (cap) + canUseSrgb = true; + break; + } + } + // Textures for renderToTexture widgets. for (int i = 0; i < textures->count(); ++i) { if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) - blitTextureForWidget(textures, i, window, deviceWindowRect, d_ptr->blitter, offset); + blitTextureForWidget(textures, i, window, deviceWindowRect, d_ptr->blitter, offset, canUseSrgb); } // Backingstore texture with the normal widgets. @@ -412,13 +443,13 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i // Textures for renderToTexture widgets that have WA_AlwaysStackOnTop set. for (int i = 0; i < textures->count(); ++i) { if (textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) - blitTextureForWidget(textures, i, window, deviceWindowRect, d_ptr->blitter, offset); + blitTextureForWidget(textures, i, window, deviceWindowRect, d_ptr->blitter, offset, canUseSrgb); } funcs->glDisable(GL_BLEND); d_ptr->blitter->release(); - context->swapBuffers(window); + d_ptr->context->swapBuffers(window); } #endif /*! diff --git a/src/gui/painting/qplatformbackingstore.h b/src/gui/painting/qplatformbackingstore.h index ec56aaa002..381c564079 100644 --- a/src/gui/painting/qplatformbackingstore.h +++ b/src/gui/painting/qplatformbackingstore.h @@ -78,7 +78,8 @@ class Q_GUI_EXPORT QPlatformTextureList : public QObject Q_DECLARE_PRIVATE(QPlatformTextureList) public: enum Flag { - StacksOnTop = 0x01 + StacksOnTop = 0x01, + TextureIsSrgb = 0x02 }; Q_DECLARE_FLAGS(Flags, Flag) @@ -116,12 +117,10 @@ public: virtual QPaintDevice *paintDevice() = 0; - // 'window' can be a child window, in which case 'region' is in child window coordinates and - // offset is the (child) window's offset in relation to the window surface. virtual void flush(QWindow *window, const QRegion ®ion, const QPoint &offset) = 0; #ifndef QT_NO_OPENGL virtual void composeAndFlush(QWindow *window, const QRegion ®ion, const QPoint &offset, - QPlatformTextureList *textures, QOpenGLContext *context, + QPlatformTextureList *textures, bool translucentBackground); #endif virtual QImage toImage() const; diff --git a/src/gui/painting/qpolygon.cpp b/src/gui/painting/qpolygon.cpp index 126b752811..3bf6004fcc 100644 --- a/src/gui/painting/qpolygon.cpp +++ b/src/gui/painting/qpolygon.cpp @@ -909,6 +909,8 @@ QPolygon QPolygon::united(const QPolygon &r) const Set operations on polygons will treat the polygons as areas. Non-closed polygons will be treated as implicitly closed. + + \sa intersects() */ QPolygon QPolygon::intersected(const QPolygon &r) const @@ -938,6 +940,26 @@ QPolygon QPolygon::subtracted(const QPolygon &r) const } /*! + \since 5.10 + + Returns \c true if the current polygon intersects at any point the given polygon \a p. + Also returns \c true if the current polygon contains or is contained by any part of \a p. + + Set operations on polygons will treat the polygons as + areas. Non-closed polygons will be treated as implicitly closed. + + \sa intersected() +*/ + +bool QPolygon::intersects(const QPolygon &p) const +{ + QPainterPath subject; subject.addPolygon(*this); + QPainterPath clip; clip.addPolygon(p); + + return subject.intersects(clip); +} + +/*! \since 4.3 Returns a polygon which is the union of this polygon and \a r. @@ -964,6 +986,7 @@ QPolygonF QPolygonF::united(const QPolygonF &r) const Set operations on polygons will treat the polygons as areas. Non-closed polygons will be treated as implicitly closed. + \sa intersects() */ QPolygonF QPolygonF::intersected(const QPolygonF &r) const @@ -992,6 +1015,26 @@ QPolygonF QPolygonF::subtracted(const QPolygonF &r) const } /*! + \since 5.10 + + Returns \c true if the current polygon intersects at any point the given polygon \a p. + Also returns \c true if the current polygon contains or is contained by any part of \a p. + + Set operations on polygons will treat the polygons as + areas. Non-closed polygons will be treated as implicitly closed. + + \sa intersected() +*/ + +bool QPolygonF::intersects(const QPolygonF &p) const +{ + QPainterPath subject; subject.addPolygon(*this); + QPainterPath clip; clip.addPolygon(p); + + return subject.intersects(clip); +} + +/*! Returns the polygon as a QVariant. */ diff --git a/src/gui/painting/qpolygon.h b/src/gui/painting/qpolygon.h index f192e8d10b..8e74a499fd 100644 --- a/src/gui/painting/qpolygon.h +++ b/src/gui/painting/qpolygon.h @@ -98,6 +98,8 @@ public: Q_REQUIRED_RESULT QPolygon united(const QPolygon &r) const; Q_REQUIRED_RESULT QPolygon intersected(const QPolygon &r) const; Q_REQUIRED_RESULT QPolygon subtracted(const QPolygon &r) const; + + bool intersects(const QPolygon &r) const; }; Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QPolygon) @@ -175,6 +177,8 @@ public: Q_REQUIRED_RESULT QPolygonF united(const QPolygonF &r) const; Q_REQUIRED_RESULT QPolygonF intersected(const QPolygonF &r) const; Q_REQUIRED_RESULT QPolygonF subtracted(const QPolygonF &r) const; + + bool intersects(const QPolygonF &r) const; }; Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QPolygonF) |