diff options
Diffstat (limited to 'src/gui/painting')
93 files changed, 6330 insertions, 1811 deletions
diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri index c3585a4647..972cf387ff 100644 --- a/src/gui/painting/painting.pri +++ b/src/gui/painting/painting.pri @@ -8,7 +8,15 @@ HEADERS += \ painting/qbrush.h \ painting/qcolor.h \ painting/qcolor_p.h \ - painting/qcolorprofile_p.h \ + painting/qcolormatrix_p.h \ + painting/qcolorspace.h \ + painting/qcolorspace_p.h \ + painting/qcolortransferfunction_p.h \ + painting/qcolortransfertable_p.h \ + painting/qcolortransform.h \ + painting/qcolortransform_p.h \ + painting/qcolortrc_p.h \ + painting/qcolortrclut_p.h \ painting/qcosmeticstroker_p.h \ painting/qdatabuffer_p.h \ painting/qdrawhelper_p.h \ @@ -17,6 +25,7 @@ HEADERS += \ painting/qemulationpaintengine_p.h \ painting/qfixed_p.h \ painting/qgrayraster_p.h \ + painting/qicc_p.h \ painting/qmatrix.h \ painting/qmemrotate_p.h \ painting/qoutlinemapper_p.h \ @@ -64,12 +73,15 @@ SOURCES += \ painting/qblittable.cpp \ painting/qbrush.cpp \ painting/qcolor.cpp \ - painting/qcolorprofile.cpp \ + painting/qcolorspace.cpp \ + painting/qcolortransform.cpp \ + painting/qcolortrclut.cpp \ painting/qcompositionfunctions.cpp \ painting/qcosmeticstroker.cpp \ painting/qdrawhelper.cpp \ painting/qemulationpaintengine.cpp \ painting/qgrayraster.c \ + painting/qicc.cpp \ painting/qimagescale.cpp \ painting/qmatrix.cpp \ painting/qmemrotate.cpp \ @@ -131,9 +143,11 @@ ARCH_HASWELL_SOURCES += painting/qdrawhelper_avx2.cpp NEON_SOURCES += painting/qdrawhelper_neon.cpp painting/qimagescale_neon.cpp NEON_HEADERS += painting/qdrawhelper_neon_p.h -NEON_ASM += ../3rdparty/pixman/pixman-arm-neon-asm.S painting/qdrawhelper_neon_asm.S !uikit:!win32:contains(QT_ARCH, "arm"): CONFIG += no_clang_integrated_as -!uikit:!win32:!contains(QT_ARCH, "arm64"): DEFINES += ENABLE_PIXMAN_DRAWHELPERS +!uikit:!win32:!integrity:!contains(QT_ARCH, "arm64") { + NEON_ASM += ../3rdparty/pixman/pixman-arm-neon-asm.S painting/qdrawhelper_neon_asm.S + DEFINES += ENABLE_PIXMAN_DRAWHELPERS +} MIPS_DSP_SOURCES += painting/qdrawhelper_mips_dsp.cpp MIPS_DSP_HEADERS += painting/qdrawhelper_mips_dsp_p.h painting/qt_mips_asm_dsp_p.h diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp index 8d71d1c3a9..3fab903c4d 100644 --- a/src/gui/painting/qbackingstore.cpp +++ b/src/gui/painting/qbackingstore.cpp @@ -62,7 +62,7 @@ public: } QWindow *window; - QPlatformBackingStore *platformBackingStore; + QPlatformBackingStore *platformBackingStore = nullptr; QScopedPointer<QImage> highDpiBackingstore; QRegion staticContents; QSize size; @@ -95,8 +95,6 @@ public: QBackingStore::QBackingStore(QWindow *window) : d_ptr(new QBackingStorePrivate(window)) { - d_ptr->platformBackingStore = QGuiApplicationPrivate::platformIntegration()->createPlatformBackingStore(window); - d_ptr->platformBackingStore->setBackingStore(this); } /*! @@ -131,7 +129,8 @@ void QBackingStore::beginPaint(const QRegion ®ion) d_ptr->highDpiBackingstore->devicePixelRatio() != d_ptr->window->devicePixelRatio()) resize(size()); - d_ptr->platformBackingStore->beginPaint(QHighDpi::toNativeLocalRegion(region, d_ptr->window)); + QPlatformBackingStore *platformBackingStore = handle(); + platformBackingStore->beginPaint(QHighDpi::toNativeLocalRegion(region, d_ptr->window)); // When QtGui is applying a high-dpi scale factor the backing store // creates a "large" backing store image. This image needs to be @@ -139,7 +138,7 @@ void QBackingStore::beginPaint(const QRegion ®ion) // devicePixelRatio. Do this on a separate image instance that shares // the image data to avoid having the new devicePixelRatio be propagated // back to the platform plugin. - QPaintDevice *device = d_ptr->platformBackingStore->paintDevice(); + QPaintDevice *device = platformBackingStore->paintDevice(); if (QHighDpiScaling::isActive() && device->devType() == QInternal::Image) { QImage *source = static_cast<QImage *>(device); const bool needsNewImage = d_ptr->highDpiBackingstore.isNull() @@ -168,7 +167,7 @@ void QBackingStore::beginPaint(const QRegion ®ion) */ QPaintDevice *QBackingStore::paintDevice() { - QPaintDevice *device = d_ptr->platformBackingStore->paintDevice(); + QPaintDevice *device = handle()->paintDevice(); if (QHighDpiScaling::isActive() && device->devType() == QInternal::Image) return d_ptr->highDpiBackingstore.data(); @@ -189,7 +188,18 @@ void QBackingStore::endPaint() if (paintDevice()->paintingActive()) qWarning() << "QBackingStore::endPaint() called with active painter on backingstore paint device"; - d_ptr->platformBackingStore->endPaint(); + handle()->endPaint(); +} + +static bool isRasterSurface(QWindow *window) +{ + switch (window->surfaceType()) { + case QSurface::RasterSurface: + case QSurface::RasterGLSurface: + return true; + default: + return false; + }; } /*! @@ -198,7 +208,7 @@ void QBackingStore::endPaint() 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. + \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 @@ -220,6 +230,13 @@ void QBackingStore::flush(const QRegion ®ion, QWindow *window, const QPoint & return; } + if (!isRasterSurface(window)) { + qWarning() << "Attempted flush to non-raster surface" << window << "of type" << window->surfaceType() + << (window->inherits("QWidgetWindow") ? "(consider using Qt::WA_PaintOnScreen to exclude " + "from backingstore sync)" : ""); + return; + } + #ifdef QBACKINGSTORE_DEBUG if (window && window->isTopLevel() && !qt_window_private(window)->receivedExpose) { qWarning().nospace() << "QBackingStore::flush() called with non-exposed window " @@ -229,7 +246,7 @@ void QBackingStore::flush(const QRegion ®ion, QWindow *window, const QPoint & Q_ASSERT(window == topLevelWindow || topLevelWindow->isAncestorOf(window, QWindow::ExcludeTransients)); - d_ptr->platformBackingStore->flush(window, QHighDpi::toNativeLocalRegion(region, window), + handle()->flush(window, QHighDpi::toNativeLocalRegion(region, window), QHighDpi::toNativeLocalPosition(offset, window)); } @@ -241,7 +258,7 @@ void QBackingStore::flush(const QRegion ®ion, QWindow *window, const QPoint & void QBackingStore::resize(const QSize &size) { d_ptr->size = size; - d_ptr->platformBackingStore->resize(QHighDpi::toNativePixels(size, d_ptr->window), d_ptr->staticContents); + handle()->resize(QHighDpi::toNativePixels(size, d_ptr->window), d_ptr->staticContents); } /*! @@ -268,7 +285,7 @@ bool QBackingStore::scroll(const QRegion &area, int dx, int dy) if (qFloor(nativeDx) != nativeDx || qFloor(nativeDy) != nativeDy) return false; - return d_ptr->platformBackingStore->scroll(QHighDpi::toNativeLocalRegion(area, d_ptr->window), + return handle()->scroll(QHighDpi::toNativeLocalRegion(area, d_ptr->window), nativeDx, nativeDy); } @@ -349,6 +366,10 @@ void Q_GUI_EXPORT qt_scrollRectInImage(QImage &img, const QRect &rect, const QPo */ QPlatformBackingStore *QBackingStore::handle() const { + if (!d_ptr->platformBackingStore) { + d_ptr->platformBackingStore = QGuiApplicationPrivate::platformIntegration()->createPlatformBackingStore(d_ptr->window); + d_ptr->platformBackingStore->setBackingStore(const_cast<QBackingStore*>(this)); + } return d_ptr->platformBackingStore; } diff --git a/src/gui/painting/qbezier.cpp b/src/gui/painting/qbezier.cpp index a3dcb02e07..ddd1d997f2 100644 --- a/src/gui/painting/qbezier.cpp +++ b/src/gui/painting/qbezier.cpp @@ -333,7 +333,9 @@ static ShiftResult shift(const QBezier *orig, QBezier *shifted, qreal offset, qr *shifted = QBezier::fromPoints(points_shifted[map[0]], points_shifted[map[1]], points_shifted[map[2]], points_shifted[map[3]]); - return good_offset(orig, shifted, offset, threshold); + if (np > 2) + return good_offset(orig, shifted, offset, threshold); + return Ok; } // This value is used to determine the length of control point vectors @@ -432,7 +434,6 @@ redo: } else if (res == Ok) { ++o; --b; - continue; } else if (res == Circle && maxSegments - (o - curveSegments) >= 2) { // add semi circle if (addCircle(b, offset, o)) diff --git a/src/gui/painting/qblendfunctions.cpp b/src/gui/painting/qblendfunctions.cpp index 2dd5144e40..348eceb47f 100644 --- a/src/gui/painting/qblendfunctions.cpp +++ b/src/gui/painting/qblendfunctions.cpp @@ -187,19 +187,11 @@ void qt_blend_rgb16_on_rgb16(uchar *dst, int dbpl, #endif if (const_alpha == 256) { - if (w <= 64) { - while (h--) { - QT_MEMCPY_USHORT(dst, src, w); - dst += dbpl; - src += sbpl; - } - } else { - int length = w << 1; - while (h--) { - memcpy(dst, src, length); - dst += dbpl; - src += sbpl; - } + int length = w << 1; + while (h--) { + memcpy(dst, src, length); + dst += dbpl; + src += sbpl; } } else if (const_alpha != 0) { quint16 *d = (quint16 *) dst; diff --git a/src/gui/painting/qblendfunctions_p.h b/src/gui/painting/qblendfunctions_p.h index dc7a4dfe8c..5ea78cdde2 100644 --- a/src/gui/painting/qblendfunctions_p.h +++ b/src/gui/painting/qblendfunctions_p.h @@ -65,11 +65,11 @@ void qt_scale_image_16bit(uchar *destPixels, int dbpl, const QRect &clip, T blender) { - qreal sx = targetRect.width() / (qreal) srcRect.width(); - qreal sy = targetRect.height() / (qreal) srcRect.height(); + qreal sx = srcRect.width() / (qreal) targetRect.width(); + qreal sy = srcRect.height() / (qreal) targetRect.height(); - int ix = 0x00010000 / sx; - int iy = 0x00010000 / sy; + const int ix = 0x00010000 * sx; + const int iy = 0x00010000 * sy; // qDebug() << "scale:" << endl // << " - target" << targetRect << endl @@ -77,59 +77,30 @@ void qt_scale_image_16bit(uchar *destPixels, int dbpl, // << " - clip" << clip << endl // << " - sx=" << sx << " sy=" << sy << " ix=" << ix << " iy=" << iy; - int cx1 = clip.x(); - int cx2 = clip.x() + clip.width(); - int cy1 = clip.top(); - int cy2 = clip.y() + clip.height(); - - int tx1 = qRound(targetRect.left()); - int tx2 = qRound(targetRect.right()); - int ty1 = qRound(targetRect.top()); - int ty2 = qRound(targetRect.bottom()); - - if (tx2 < tx1) - qSwap(tx2, tx1); - - if (ty2 < ty1) - qSwap(ty2, ty1); - - if (tx1 < cx1) - tx1 = cx1; - - if (tx2 >= cx2) - tx2 = cx2; - - if (tx1 >= tx2) + QRect tr = targetRect.normalized().toRect(); + tr = tr.intersected(clip); + if (tr.isEmpty()) return; - - if (ty1 < cy1) - ty1 = cy1; - - if (ty2 >= cy2) - ty2 = cy2; - - if (ty1 >= ty2) - return; - - int h = ty2 - ty1; - int w = tx2 - tx1; - + const int tx1 = tr.left(); + const int ty1 = tr.top(); + int h = tr.height(); + int w = tr.width(); quint32 basex; quint32 srcy; if (sx < 0) { - int dstx = qFloor((tx1 + qreal(0.5) - targetRect.right()) * ix) + 1; + int dstx = qFloor((tx1 + qreal(0.5) - targetRect.right()) * sx * 65536) + 1; basex = quint32(srcRect.right() * 65536) + dstx; } else { - int dstx = qCeil((tx1 + qreal(0.5) - targetRect.left()) * ix) - 1; + int dstx = qCeil((tx1 + qreal(0.5) - targetRect.left()) * sx * 65536) - 1; basex = quint32(srcRect.left() * 65536) + dstx; } if (sy < 0) { - int dsty = qFloor((ty1 + qreal(0.5) - targetRect.bottom()) * iy) + 1; + int dsty = qFloor((ty1 + qreal(0.5) - targetRect.bottom()) * sy * 65536) + 1; srcy = quint32(srcRect.bottom() * 65536) + dsty; } else { - int dsty = qCeil((ty1 + qreal(0.5) - targetRect.top()) * iy) - 1; + int dsty = qCeil((ty1 + qreal(0.5) - targetRect.top()) * sy * 65536) - 1; srcy = quint32(srcRect.top() * 65536) + dsty; } @@ -185,11 +156,11 @@ template <typename T> void qt_scale_image_32bit(uchar *destPixels, int dbpl, const QRect &clip, T blender) { - qreal sx = targetRect.width() / (qreal) srcRect.width(); - qreal sy = targetRect.height() / (qreal) srcRect.height(); + qreal sx = srcRect.width() / (qreal) targetRect.width(); + qreal sy = srcRect.height() / (qreal) targetRect.height(); - int ix = 0x00010000 / sx; - int iy = 0x00010000 / sy; + const int ix = 0x00010000 * sx; + const int iy = 0x00010000 * sy; // qDebug() << "scale:" << endl // << " - target" << targetRect << endl @@ -197,60 +168,30 @@ template <typename T> void qt_scale_image_32bit(uchar *destPixels, int dbpl, // << " - clip" << clip << endl // << " - sx=" << sx << " sy=" << sy << " ix=" << ix << " iy=" << iy; - int cx1 = clip.x(); - int cx2 = clip.x() + clip.width(); - int cy1 = clip.top(); - int cy2 = clip.y() + clip.height(); - - int tx1 = qRound(targetRect.left()); - int tx2 = qRound(targetRect.right()); - int ty1 = qRound(targetRect.top()); - int ty2 = qRound(targetRect.bottom()); - - if (tx2 < tx1) - qSwap(tx2, tx1); - - if (ty2 < ty1) - qSwap(ty2, ty1); - - if (tx1 < cx1) - tx1 = cx1; - - if (tx2 >= cx2) - tx2 = cx2; - - if (tx1 >= tx2) - return; - - if (ty1 < cy1) - ty1 = cy1; - - if (ty2 >= cy2) - ty2 = cy2; - - if (ty1 >= ty2) - return; - - int h = ty2 - ty1; - int w = tx2 - tx1; - if (!w || !h) + QRect tr = targetRect.normalized().toRect(); + tr = tr.intersected(clip); + if (tr.isEmpty()) return; + const int tx1 = tr.left(); + const int ty1 = tr.top(); + int h = tr.height(); + int w = tr.width(); quint32 basex; quint32 srcy; if (sx < 0) { - int dstx = qFloor((tx1 + qreal(0.5) - targetRect.right()) * ix) + 1; + int dstx = qFloor((tx1 + qreal(0.5) - targetRect.right()) * sx * 65536) + 1; basex = quint32(srcRect.right() * 65536) + dstx; } else { - int dstx = qCeil((tx1 + qreal(0.5) - targetRect.left()) * ix) - 1; + int dstx = qCeil((tx1 + qreal(0.5) - targetRect.left()) * sx * 65536) - 1; basex = quint32(srcRect.left() * 65536) + dstx; } if (sy < 0) { - int dsty = qFloor((ty1 + qreal(0.5) - targetRect.bottom()) * iy) + 1; + int dsty = qFloor((ty1 + qreal(0.5) - targetRect.bottom()) * sy * 65536) + 1; srcy = quint32(srcRect.bottom() * 65536) + dsty; } else { - int dsty = qCeil((ty1 + qreal(0.5) - targetRect.top()) * iy) - 1; + int dsty = qCeil((ty1 + qreal(0.5) - targetRect.top()) * sy * 65536) - 1; srcy = quint32(srcRect.top() * 65536) + dsty; } diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp index 860653cc4c..49b40aa756 100644 --- a/src/gui/painting/qbrush.cpp +++ b/src/gui/painting/qbrush.cpp @@ -111,7 +111,7 @@ Q_GUI_EXPORT QPixmap qt_pixmapForBrush(int brushStyle, bool invert) QString key = QLatin1String("$qt-brush$") % HexString<uint>(brushStyle) % QLatin1Char(invert ? '1' : '0'); - if (!QPixmapCache::find(key, pm)) { + if (!QPixmapCache::find(key, &pm)) { pm = QBitmap::fromData(QSize(8, 8), qt_patternForBrush(brushStyle, invert), QImage::Format_MonoLSB); QPixmapCache::insert(key, pm); @@ -545,9 +545,11 @@ QBrush::QBrush(const QBrush &other) */ QBrush::QBrush(const QGradient &gradient) { - Q_ASSERT_X(gradient.type() != QGradient::NoGradient, "QBrush::QBrush", - "QGradient should not be used directly, use the linear, radial\n" - "or conical gradients instead"); + if (Q_UNLIKELY(gradient.type() == QGradient::NoGradient)) { + d.reset(nullBrushInstance()); + d->ref.ref(); + return; + } const Qt::BrushStyle enum_table[] = { Qt::LinearGradientPattern, @@ -1138,11 +1140,11 @@ QDataStream &operator>>(QDataStream &s, QBrush &b) if (s.version() >= QDataStream::Qt_5_5) { QImage img; s >> img; - b.setTextureImage(qMove(img)); + b.setTextureImage(std::move(img)); } else { QPixmap pm; s >> pm; - b.setTexture(qMove(pm)); + b.setTexture(std::move(pm)); } } else if (style == Qt::LinearGradientPattern || style == Qt::RadialGradientPattern diff --git a/src/gui/painting/qbrush.h b/src/gui/painting/qbrush.h index aee51c526d..27d710eca6 100644 --- a/src/gui/painting/qbrush.h +++ b/src/gui/painting/qbrush.h @@ -80,10 +80,10 @@ public: ~QBrush(); QBrush &operator=(const QBrush &brush); #ifdef Q_COMPILER_RVALUE_REFS - inline QBrush &operator=(QBrush &&other) Q_DECL_NOEXCEPT + inline QBrush &operator=(QBrush &&other) noexcept { qSwap(d, other.d); return *this; } #endif - inline void swap(QBrush &other) Q_DECL_NOEXCEPT + inline void swap(QBrush &other) noexcept { qSwap(d, other.d); } operator QVariant() const; diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp index ef3296a6d4..bb42660cf2 100644 --- a/src/gui/painting/qcolor.cpp +++ b/src/gui/painting/qcolor.cpp @@ -54,77 +54,77 @@ QT_BEGIN_NAMESPACE /*! \internal - If s[0..1] is a valid hex number, returns its integer value, + If s[0..n] is a valid hex number, returns its integer value, otherwise returns -1. */ -static inline int hex2int(const char *s) +static inline int hex2int(const char *s, int n) { - const int hi = QtMiscUtils::fromHex(s[0]); - if (hi < 0) + if (n < 0) return -1; - const int lo = QtMiscUtils::fromHex(s[1]); - if (lo < 0) - return -1; - return (hi << 4) | lo; -} - -/*! - \internal - If s is a valid hex digit, returns its integer value, - multiplied by 0x11, otherwise returns -1. - */ -static inline int hex2int(char s) -{ - const int h = QtMiscUtils::fromHex(s); - return h < 0 ? h : (h << 4) | h; + int result = 0; + for (; n > 0; --n) { + result = result * 16; + const int h = QtMiscUtils::fromHex(*s++); + if (h < 0) + return -1; + result += h; + } + return result; } -static bool get_hex_rgb(const char *name, size_t len, QRgb *rgb) +static bool get_hex_rgb(const char *name, size_t len, QRgba64 *rgb) { if (name[0] != '#') return false; name++; --len; int a, r, g, b; - a = 255; + a = 65535; if (len == 12) { - r = hex2int(name); - g = hex2int(name + 4); - b = hex2int(name + 8); + r = hex2int(name + 0, 4); + g = hex2int(name + 4, 4); + b = hex2int(name + 8, 4); } else if (len == 9) { - r = hex2int(name); - g = hex2int(name + 3); - b = hex2int(name + 6); + r = hex2int(name + 0, 3); + g = hex2int(name + 3, 3); + b = hex2int(name + 6, 3); + r = (r << 4) | (r >> 8); + g = (g << 4) | (g >> 8); + b = (b << 4) | (b >> 8); } else if (len == 8) { - a = hex2int(name); - r = hex2int(name + 2); - g = hex2int(name + 4); - b = hex2int(name + 6); + a = hex2int(name + 0, 2) * 0x101; + r = hex2int(name + 2, 2) * 0x101; + g = hex2int(name + 4, 2) * 0x101; + b = hex2int(name + 6, 2) * 0x101; } else if (len == 6) { - r = hex2int(name); - g = hex2int(name + 2); - b = hex2int(name + 4); + r = hex2int(name + 0, 2) * 0x101; + g = hex2int(name + 2, 2) * 0x101; + b = hex2int(name + 4, 2) * 0x101; } else if (len == 3) { - r = hex2int(name[0]); - g = hex2int(name[1]); - b = hex2int(name[2]); + r = hex2int(name + 0, 1) * 0x1111; + g = hex2int(name + 1, 1) * 0x1111; + b = hex2int(name + 2, 1) * 0x1111; } else { r = g = b = -1; } - if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || (uint)a > 255) { + if ((uint)r > 65535 || (uint)g > 65535 || (uint)b > 65535 || (uint)a > 65535) { *rgb = 0; return false; } - *rgb = qRgba(r, g ,b, a); + *rgb = qRgba64(r, g ,b, a); return true; } bool qt_get_hex_rgb(const char *name, QRgb *rgb) { - return get_hex_rgb(name, qstrlen(name), rgb); + QRgba64 rgba64; + if (!get_hex_rgb(name, qstrlen(name), &rgba64)) + return false; + *rgb = rgba64.toArgb32(); + return true; } -static bool get_hex_rgb(const QChar *str, size_t len, QRgb *rgb) +static bool get_hex_rgb(const QChar *str, size_t len, QRgba64 *rgb) { if (len > 13) return false; @@ -541,7 +541,15 @@ static QStringList get_colornames() \section1 The HSL Color Model HSL is similar to HSV, however instead of the Value parameter, HSL - specifies a Lightness parameter. + specifies a Lightness parameter which maps somewhat differently to the + brightness of the color. + + Similarly, the HSL saturation value is not in general the same as the HSV + saturation value for the same color. hslSaturation() provides the color's + HSL saturation value, while saturation() and hsvSaturation() provides the + HSV saturation value. + + The hue value is defined to be the same in HSL and HSV. \section1 The CMYK Color Model @@ -645,7 +653,7 @@ static QStringList get_colornames() \sa isValid(), {QColor#Predefined Colors}{Predefined Colors} */ -QColor::QColor(Qt::GlobalColor color) Q_DECL_NOTHROW +QColor::QColor(Qt::GlobalColor color) noexcept { #define QRGB(r, g, b) \ QRgb(((0xffu << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff))) @@ -720,7 +728,7 @@ QColor::QColor(Qt::GlobalColor color) Q_DECL_NOTHROW \sa fromRgb(), isValid() */ -QColor::QColor(QRgb color) Q_DECL_NOTHROW +QColor::QColor(QRgb color) noexcept { cspec = Rgb; ct.argb.alpha = 0xffff; @@ -738,7 +746,7 @@ QColor::QColor(QRgb color) Q_DECL_NOTHROW \sa fromRgba64() */ -QColor::QColor(QRgba64 rgba64) Q_DECL_NOTHROW +QColor::QColor(QRgba64 rgba64) noexcept { setRgba64(rgba64); } @@ -752,7 +760,7 @@ QColor::QColor(QRgba64 rgba64) Q_DECL_NOTHROW becomes a valid color by accident. */ -QColor::QColor(Spec spec) Q_DECL_NOTHROW +QColor::QColor(Spec spec) noexcept { switch (spec) { case Invalid: @@ -925,7 +933,7 @@ bool QColor::isValidColor(const QString &name) \overload \since 5.10 */ -bool QColor::isValidColor(QStringView name) Q_DECL_NOTHROW +bool QColor::isValidColor(QStringView name) noexcept { return name.size() && QColor().setColorFromString(name); } @@ -934,7 +942,7 @@ bool QColor::isValidColor(QStringView name) Q_DECL_NOTHROW \overload \since 5.8 */ -bool QColor::isValidColor(QLatin1String name) Q_DECL_NOTHROW +bool QColor::isValidColor(QLatin1String name) noexcept { return name.size() && QColor().setColorFromString(name); } @@ -948,9 +956,9 @@ bool QColor::setColorFromString(String name) } if (name[0] == QLatin1Char('#')) { - QRgb rgba; + QRgba64 rgba; if (get_hex_rgb(name.data(), name.size(), &rgba)) { - setRgba(rgba); + setRgba64(rgba); return true; } else { invalidate(); @@ -1098,7 +1106,7 @@ void QColor::setHsv(int h, int s, int v, int a) These components can be retrieved individually using the hslHueF(), hslSaturationF(), lightnessF() and alphaF() functions. - \sa setHsl() + \sa getHsl(), setHslF(), {QColor#The HSL Color Model}{The HSL Color Model} */ void QColor::getHslF(qreal *h, qreal *s, qreal *l, qreal *a) const { @@ -1128,7 +1136,7 @@ void QColor::getHslF(qreal *h, qreal *s, qreal *l, qreal *a) const These components can be retrieved individually using the hslHue(), hslSaturation(), lightness() and alpha() functions. - \sa setHsl() + \sa getHslF(), setHsl(), {QColor#The HSL Color Model}{The HSL Color Model} */ void QColor::getHsl(int *h, int *s, int *l, int *a) const { @@ -1322,7 +1330,7 @@ void QColor::setRgb(int r, int g, int b, int a) \sa setRgba(), rgb(), rgba64() */ -QRgb QColor::rgba() const Q_DECL_NOTHROW +QRgb QColor::rgba() const noexcept { if (cspec != Invalid && cspec != Rgb) return toRgb().rgba(); @@ -1334,7 +1342,7 @@ QRgb QColor::rgba() const Q_DECL_NOTHROW \sa rgba(), rgb(), setRgba64() */ -void QColor::setRgba(QRgb rgba) Q_DECL_NOTHROW +void QColor::setRgba(QRgb rgba) noexcept { cspec = Rgb; ct.argb.alpha = qAlpha(rgba) * 0x101; @@ -1354,7 +1362,7 @@ void QColor::setRgba(QRgb rgba) Q_DECL_NOTHROW \sa setRgba64(), rgba(), rgb() */ -QRgba64 QColor::rgba64() const Q_DECL_NOTHROW +QRgba64 QColor::rgba64() const noexcept { if (cspec != Invalid && cspec != Rgb) return toRgb().rgba64(); @@ -1366,9 +1374,9 @@ QRgba64 QColor::rgba64() const Q_DECL_NOTHROW Sets the RGB64 value to \a rgba, including its alpha. - \sa \setRgba(), rgba64() + \sa setRgba(), rgba64() */ -void QColor::setRgba64(QRgba64 rgba) Q_DECL_NOTHROW +void QColor::setRgba64(QRgba64 rgba) noexcept { cspec = Rgb; ct.argb.alpha = rgba.alpha(); @@ -1385,7 +1393,7 @@ void QColor::setRgba64(QRgba64 rgba) Q_DECL_NOTHROW \sa getRgb(), rgba() */ -QRgb QColor::rgb() const Q_DECL_NOTHROW +QRgb QColor::rgb() const noexcept { if (cspec != Invalid && cspec != Rgb) return toRgb().rgb(); @@ -1397,7 +1405,7 @@ QRgb QColor::rgb() const Q_DECL_NOTHROW Sets the RGB value to \a rgb. The alpha value is set to opaque. */ -void QColor::setRgb(QRgb rgb) Q_DECL_NOTHROW +void QColor::setRgb(QRgb rgb) noexcept { cspec = Rgb; ct.argb.alpha = 0xffff; @@ -1412,7 +1420,7 @@ void QColor::setRgb(QRgb rgb) Q_DECL_NOTHROW \sa setAlpha(), alphaF(), {QColor#Alpha-Blended Drawing}{Alpha-Blended Drawing} */ -int QColor::alpha() const Q_DECL_NOTHROW +int QColor::alpha() const noexcept { return ct.argb.alpha >> 8; } @@ -1434,7 +1442,7 @@ void QColor::setAlpha(int alpha) \sa setAlphaF(), alpha(), {QColor#Alpha-Blended Drawing}{Alpha-Blended Drawing} */ -qreal QColor::alphaF() const Q_DECL_NOTHROW +qreal QColor::alphaF() const noexcept { return ct.argb.alpha / qreal(USHRT_MAX); } /*! @@ -1457,7 +1465,7 @@ void QColor::setAlphaF(qreal alpha) \sa setRed(), redF(), getRgb() */ -int QColor::red() const Q_DECL_NOTHROW +int QColor::red() const noexcept { if (cspec != Invalid && cspec != Rgb) return toRgb().red(); @@ -1484,7 +1492,7 @@ void QColor::setRed(int red) \sa setGreen(), greenF(), getRgb() */ -int QColor::green() const Q_DECL_NOTHROW +int QColor::green() const noexcept { if (cspec != Invalid && cspec != Rgb) return toRgb().green(); @@ -1512,7 +1520,7 @@ void QColor::setGreen(int green) \sa setBlue(), blueF(), getRgb() */ -int QColor::blue() const Q_DECL_NOTHROW +int QColor::blue() const noexcept { if (cspec != Invalid && cspec != Rgb) return toRgb().blue(); @@ -1540,7 +1548,7 @@ void QColor::setBlue(int blue) \sa setRedF(), red(), getRgbF() */ -qreal QColor::redF() const Q_DECL_NOTHROW +qreal QColor::redF() const noexcept { if (cspec != Invalid && cspec != Rgb) return toRgb().redF(); @@ -1568,7 +1576,7 @@ void QColor::setRedF(qreal red) \sa setGreenF(), green(), getRgbF() */ -qreal QColor::greenF() const Q_DECL_NOTHROW +qreal QColor::greenF() const noexcept { if (cspec != Invalid && cspec != Rgb) return toRgb().greenF(); @@ -1596,7 +1604,7 @@ void QColor::setGreenF(qreal green) \sa setBlueF(), blue(), getRgbF() */ -qreal QColor::blueF() const Q_DECL_NOTHROW +qreal QColor::blueF() const noexcept { if (cspec != Invalid && cspec != Rgb) return toRgb().blueF(); @@ -1619,24 +1627,24 @@ void QColor::setBlueF(qreal blue) } /*! - Returns the hue color component of this color. + Returns the HSV hue color component of this color. The color is implicitly converted to HSV. - \sa hsvHue(), hueF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color Model} + \sa hsvHue(), hslHue(), hueF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color Model} */ -int QColor::hue() const Q_DECL_NOTHROW +int QColor::hue() const noexcept { return hsvHue(); } /*! - Returns the hue color component of this color. + Returns the HSV hue color component of this color. - \sa hueF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color Model} + \sa hueF(), hslHue(), getHsv(), {QColor#The HSV Color Model}{The HSV Color Model} */ -int QColor::hsvHue() const Q_DECL_NOTHROW +int QColor::hsvHue() const noexcept { if (cspec != Invalid && cspec != Hsv) return toHsv().hue(); @@ -1644,25 +1652,25 @@ int QColor::hsvHue() const Q_DECL_NOTHROW } /*! - Returns the saturation color component of this color. + Returns the HSV saturation color component of this color. The color is implicitly converted to HSV. - \sa hsvSaturation(), saturationF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color + \sa hsvSaturation(), hslSaturation(), saturationF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color Model} */ -int QColor::saturation() const Q_DECL_NOTHROW +int QColor::saturation() const noexcept { return hsvSaturation(); } /*! - Returns the saturation color component of this color. + Returns the HSV saturation color component of this color. - \sa saturationF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color Model} + \sa saturationF(), hslSaturation(), getHsv(), {QColor#The HSV Color Model}{The HSV Color Model} */ -int QColor::hsvSaturation() const Q_DECL_NOTHROW +int QColor::hsvSaturation() const noexcept { if (cspec != Invalid && cspec != Hsv) return toHsv().saturation(); @@ -1674,7 +1682,7 @@ int QColor::hsvSaturation() const Q_DECL_NOTHROW \sa valueF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color Model} */ -int QColor::value() const Q_DECL_NOTHROW +int QColor::value() const noexcept { if (cspec != Invalid && cspec != Hsv) return toHsv().value(); @@ -1682,13 +1690,13 @@ int QColor::value() const Q_DECL_NOTHROW } /*! - Returns the hue color component of this color. + Returns the HSV hue color component of this color. The color is implicitly converted to HSV. - \sa hsvHueF(), hue(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color Model} + \sa hsvHueF(), hslHueF(), hue(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color Model} */ -qreal QColor::hueF() const Q_DECL_NOTHROW +qreal QColor::hueF() const noexcept { return hsvHueF(); } @@ -1696,10 +1704,10 @@ qreal QColor::hueF() const Q_DECL_NOTHROW /*! Returns the hue color component of this color. - \sa hue(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color + \sa hue(), hslHueF(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color Model} */ -qreal QColor::hsvHueF() const Q_DECL_NOTHROW +qreal QColor::hsvHueF() const noexcept { if (cspec != Invalid && cspec != Hsv) return toHsv().hueF(); @@ -1707,24 +1715,24 @@ qreal QColor::hsvHueF() const Q_DECL_NOTHROW } /*! - Returns the saturation color component of this color. + Returns the HSV saturation color component of this color. The color is implicitly converted to HSV. - \sa hsvSaturationF(), saturation(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color + \sa hsvSaturationF(), hslSaturationF(), saturation(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color Model} */ -qreal QColor::saturationF() const Q_DECL_NOTHROW +qreal QColor::saturationF() const noexcept { return hsvSaturationF(); } /*! - Returns the saturation color component of this color. + Returns the HSV saturation color component of this color. - \sa saturation(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color Model} + \sa saturation(), hslSaturationF(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color Model} */ -qreal QColor::hsvSaturationF() const Q_DECL_NOTHROW +qreal QColor::hsvSaturationF() const noexcept { if (cspec != Invalid && cspec != Hsv) return toHsv().saturationF(); @@ -1736,7 +1744,7 @@ qreal QColor::hsvSaturationF() const Q_DECL_NOTHROW \sa value(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color Model} */ -qreal QColor::valueF() const Q_DECL_NOTHROW +qreal QColor::valueF() const noexcept { if (cspec != Invalid && cspec != Hsv) return toHsv().valueF(); @@ -1746,11 +1754,11 @@ qreal QColor::valueF() const Q_DECL_NOTHROW /*! \since 4.6 - Returns the hue color component of this color. + Returns the HSL hue color component of this color. - \sa getHslF(), getHsl() + \sa hslHueF(), hsvHue(), getHsl(), {QColor#The HSL Color Model}{The HSL Color Model} */ -int QColor::hslHue() const Q_DECL_NOTHROW +int QColor::hslHue() const noexcept { if (cspec != Invalid && cspec != Hsl) return toHsl().hslHue(); @@ -1760,11 +1768,11 @@ int QColor::hslHue() const Q_DECL_NOTHROW /*! \since 4.6 - Returns the saturation color component of this color. + Returns the HSL saturation color component of this color. - \sa saturationF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color Model} + \sa hslSaturationF(), hsvSaturation(), getHsl(), {QColor#The HSL Color Model}{The HSL Color Model} */ -int QColor::hslSaturation() const Q_DECL_NOTHROW +int QColor::hslSaturation() const noexcept { if (cspec != Invalid && cspec != Hsl) return toHsl().hslSaturation(); @@ -1778,7 +1786,7 @@ int QColor::hslSaturation() const Q_DECL_NOTHROW \sa lightnessF(), getHsl() */ -int QColor::lightness() const Q_DECL_NOTHROW +int QColor::lightness() const noexcept { if (cspec != Invalid && cspec != Hsl) return toHsl().lightness(); @@ -1788,11 +1796,11 @@ int QColor::lightness() const Q_DECL_NOTHROW /*! \since 4.6 - Returns the hue color component of this color. + Returns the HSL hue color component of this color. - \sa hue(), getHslF() + \sa hslHue(), hsvHueF(), getHslF() */ -qreal QColor::hslHueF() const Q_DECL_NOTHROW +qreal QColor::hslHueF() const noexcept { if (cspec != Invalid && cspec != Hsl) return toHsl().hslHueF(); @@ -1802,11 +1810,11 @@ qreal QColor::hslHueF() const Q_DECL_NOTHROW /*! \since 4.6 - Returns the saturation color component of this color. + Returns the HSL saturation color component of this color. - \sa saturationF(), getHslF() + \sa hslSaturation(), hsvSaturationF(), getHslF(), {QColor#The HSL Color Model}{The HSL Color Model} */ -qreal QColor::hslSaturationF() const Q_DECL_NOTHROW +qreal QColor::hslSaturationF() const noexcept { if (cspec != Invalid && cspec != Hsl) return toHsl().hslSaturationF(); @@ -1820,7 +1828,7 @@ qreal QColor::hslSaturationF() const Q_DECL_NOTHROW \sa value(), getHslF() */ -qreal QColor::lightnessF() const Q_DECL_NOTHROW +qreal QColor::lightnessF() const noexcept { if (cspec != Invalid && cspec != Hsl) return toHsl().lightnessF(); @@ -1832,7 +1840,7 @@ qreal QColor::lightnessF() const Q_DECL_NOTHROW \sa cyanF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -int QColor::cyan() const Q_DECL_NOTHROW +int QColor::cyan() const noexcept { if (cspec != Invalid && cspec != Cmyk) return toCmyk().cyan(); @@ -1844,7 +1852,7 @@ int QColor::cyan() const Q_DECL_NOTHROW \sa magentaF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -int QColor::magenta() const Q_DECL_NOTHROW +int QColor::magenta() const noexcept { if (cspec != Invalid && cspec != Cmyk) return toCmyk().magenta(); @@ -1856,7 +1864,7 @@ int QColor::magenta() const Q_DECL_NOTHROW \sa yellowF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -int QColor::yellow() const Q_DECL_NOTHROW +int QColor::yellow() const noexcept { if (cspec != Invalid && cspec != Cmyk) return toCmyk().yellow(); @@ -1869,7 +1877,7 @@ int QColor::yellow() const Q_DECL_NOTHROW \sa blackF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -int QColor::black() const Q_DECL_NOTHROW +int QColor::black() const noexcept { if (cspec != Invalid && cspec != Cmyk) return toCmyk().black(); @@ -1881,7 +1889,7 @@ int QColor::black() const Q_DECL_NOTHROW \sa cyan(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -qreal QColor::cyanF() const Q_DECL_NOTHROW +qreal QColor::cyanF() const noexcept { if (cspec != Invalid && cspec != Cmyk) return toCmyk().cyanF(); @@ -1893,7 +1901,7 @@ qreal QColor::cyanF() const Q_DECL_NOTHROW \sa magenta(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -qreal QColor::magentaF() const Q_DECL_NOTHROW +qreal QColor::magentaF() const noexcept { if (cspec != Invalid && cspec != Cmyk) return toCmyk().magentaF(); @@ -1905,7 +1913,7 @@ qreal QColor::magentaF() const Q_DECL_NOTHROW \sa yellow(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -qreal QColor::yellowF() const Q_DECL_NOTHROW +qreal QColor::yellowF() const noexcept { if (cspec != Invalid && cspec != Cmyk) return toCmyk().yellowF(); @@ -1917,7 +1925,7 @@ qreal QColor::yellowF() const Q_DECL_NOTHROW \sa black(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -qreal QColor::blackF() const Q_DECL_NOTHROW +qreal QColor::blackF() const noexcept { if (cspec != Invalid && cspec != Cmyk) return toCmyk().blackF(); @@ -1929,7 +1937,7 @@ qreal QColor::blackF() const Q_DECL_NOTHROW \sa fromRgb(), convertTo(), isValid() */ -QColor QColor::toRgb() const Q_DECL_NOTHROW +QColor QColor::toRgb() const noexcept { if (!isValid() || cspec == Rgb) return *this; @@ -2075,7 +2083,7 @@ QColor QColor::toRgb() const Q_DECL_NOTHROW \sa fromHsv(), convertTo(), isValid(), {QColor#The HSV Color Model}{The HSV Color Model} */ -QColor QColor::toHsv() const Q_DECL_NOTHROW +QColor QColor::toHsv() const noexcept { if (!isValid() || cspec == Hsv) return *this; @@ -2124,9 +2132,9 @@ QColor QColor::toHsv() const Q_DECL_NOTHROW /*! Creates and returns an HSL QColor based on this color. - \sa fromHsl(), convertTo(), isValid() + \sa fromHsl(), convertTo(), isValid(), {QColor#The HSL Color Model}{The HSL Color Model} */ -QColor QColor::toHsl() const Q_DECL_NOTHROW +QColor QColor::toHsl() const noexcept { if (!isValid() || cspec == Hsl) return *this; @@ -2182,7 +2190,7 @@ QColor QColor::toHsl() const Q_DECL_NOTHROW \sa fromCmyk(), convertTo(), isValid(), {QColor#The CMYK Color Model}{The CMYK Color Model} */ -QColor QColor::toCmyk() const Q_DECL_NOTHROW +QColor QColor::toCmyk() const noexcept { if (!isValid() || cspec == Cmyk) return *this; @@ -2193,32 +2201,37 @@ QColor QColor::toCmyk() const Q_DECL_NOTHROW color.cspec = Cmyk; color.ct.acmyk.alpha = ct.argb.alpha; - // rgb -> cmy - const qreal r = ct.argb.red / qreal(USHRT_MAX); - const qreal g = ct.argb.green / qreal(USHRT_MAX); - const qreal b = ct.argb.blue / qreal(USHRT_MAX); - qreal c = qreal(1.0) - r; - qreal m = qreal(1.0) - g; - qreal y = qreal(1.0) - b; - - // cmy -> cmyk - const qreal k = qMin(c, qMin(m, y)); - - if (!qFuzzyIsNull(k - 1)) { + if (!ct.argb.red && !ct.argb.green && !ct.argb.blue) { + // Avoid div-by-0 below + color.ct.acmyk.cyan = 0; + color.ct.acmyk.magenta = 0; + color.ct.acmyk.yellow = 0; + color.ct.acmyk.black = USHRT_MAX; + } else { + // rgb -> cmy + const qreal r = ct.argb.red / qreal(USHRT_MAX); + const qreal g = ct.argb.green / qreal(USHRT_MAX); + const qreal b = ct.argb.blue / qreal(USHRT_MAX); + qreal c = qreal(1.0) - r; + qreal m = qreal(1.0) - g; + qreal y = qreal(1.0) - b; + + // cmy -> cmyk + const qreal k = qMin(c, qMin(m, y)); c = (c - k) / (qreal(1.0) - k); m = (m - k) / (qreal(1.0) - k); y = (y - k) / (qreal(1.0) - k); - } - color.ct.acmyk.cyan = qRound(c * USHRT_MAX); - color.ct.acmyk.magenta = qRound(m * USHRT_MAX); - color.ct.acmyk.yellow = qRound(y * USHRT_MAX); - color.ct.acmyk.black = qRound(k * USHRT_MAX); + color.ct.acmyk.cyan = qRound(c * USHRT_MAX); + color.ct.acmyk.magenta = qRound(m * USHRT_MAX); + color.ct.acmyk.yellow = qRound(y * USHRT_MAX); + color.ct.acmyk.black = qRound(k * USHRT_MAX); + } return color; } -QColor QColor::convertTo(QColor::Spec colorSpec) const Q_DECL_NOTHROW +QColor QColor::convertTo(QColor::Spec colorSpec) const noexcept { if (colorSpec == cspec) return *this; @@ -2249,7 +2262,7 @@ QColor QColor::convertTo(QColor::Spec colorSpec) const Q_DECL_NOTHROW \sa fromRgba(), fromRgbF(), toRgb(), isValid() */ -QColor QColor::fromRgb(QRgb rgb) Q_DECL_NOTHROW +QColor QColor::fromRgb(QRgb rgb) noexcept { return fromRgb(qRed(rgb), qGreen(rgb), qBlue(rgb)); } @@ -2265,7 +2278,7 @@ QColor QColor::fromRgb(QRgb rgb) Q_DECL_NOTHROW \sa fromRgb(), fromRgba64(), isValid() */ -QColor QColor::fromRgba(QRgb rgba) Q_DECL_NOTHROW +QColor QColor::fromRgba(QRgb rgba) noexcept { return fromRgb(qRed(rgba), qGreen(rgba), qBlue(rgba), qAlpha(rgba)); } @@ -2338,7 +2351,7 @@ QColor QColor::fromRgbF(qreal r, qreal g, qreal b, qreal a) \sa fromRgb(), fromRgbF(), toRgb(), isValid() */ -QColor QColor::fromRgba64(ushort r, ushort g, ushort b, ushort a) Q_DECL_NOTHROW +QColor QColor::fromRgba64(ushort r, ushort g, ushort b, ushort a) noexcept { QColor color; color.setRgba64(qRgba64(r, g, b, a)); @@ -2353,7 +2366,7 @@ QColor QColor::fromRgba64(ushort r, ushort g, ushort b, ushort a) Q_DECL_NOTHROW \sa fromRgb(), fromRgbF(), toRgb(), isValid() */ -QColor QColor::fromRgba64(QRgba64 rgba64) Q_DECL_NOTHROW +QColor QColor::fromRgba64(QRgba64 rgba64) noexcept { QColor color; color.setRgba64(rgba64); @@ -2431,7 +2444,7 @@ QColor QColor::fromHsvF(qreal h, qreal s, qreal v, qreal a) The value of \a s, \a l, and \a a must all be in the range 0-255; the value of \a h must be in the range 0-359. - \sa toHsl(), fromHslF(), isValid() + \sa toHsl(), fromHslF(), isValid(), {QColor#The HSL Color Model}{The HSL Color Model} */ QColor QColor::fromHsl(int h, int s, int l, int a) { @@ -2463,7 +2476,7 @@ QColor QColor::fromHsl(int h, int s, int l, int a) All the values must be in the range 0.0-1.0. - \sa toHsl(), fromHsl(), isValid() + \sa toHsl(), fromHsl(), isValid(), {QColor#The HSL Color Model}{The HSL Color Model} */ QColor QColor::fromHslF(qreal h, qreal s, qreal l, qreal a) { @@ -2695,18 +2708,13 @@ QColor QColor::fromCmykF(qreal c, qreal m, qreal y, qreal k, qreal a) recommend using the darker() function for this purpose. If the \a factor is 0 or negative, the return value is unspecified. - The function converts the current RGB color to HSV, multiplies the value - (V) component by \a factor and converts the color back to RGB. + The function converts the current color to HSV, multiplies the value + (V) component by \a factor and converts the color back to it's original + color spec. \sa darker(), isValid() */ - -/*! - \obsolete - - Use lighter(\a factor) instead. -*/ -QColor QColor::light(int factor) const Q_DECL_NOTHROW +QColor QColor::lighter(int factor) const noexcept { if (factor <= 0) // invalid lightness factor return *this; @@ -2745,18 +2753,13 @@ QColor QColor::light(int factor) const Q_DECL_NOTHROW but we recommend using the lighter() function for this purpose. If the \a factor is 0 or negative, the return value is unspecified. - The function converts the current RGB color to HSV, divides the value (V) - component by \a factor and converts the color back to RGB. + The function converts the current color to HSV, divides the value (V) + component by \a factor and converts the color back to it's original + color spec. \sa lighter(), isValid() */ - -/*! - \obsolete - - Use darker(\a factor) instead. -*/ -QColor QColor::dark(int factor) const Q_DECL_NOTHROW +QColor QColor::darker(int factor) const noexcept { if (factor <= 0) // invalid darkness factor return *this; @@ -2770,11 +2773,33 @@ QColor QColor::dark(int factor) const Q_DECL_NOTHROW return hsv.convertTo(cspec); } +#if QT_DEPRECATED_SINCE(5, 13) +/*! + \obsolete + + Use lighter(\a factor) instead. +*/ +QColor QColor::light(int factor) const noexcept +{ + return lighter(factor); +} + +/*! + \obsolete + + Use darker(\a factor) instead. +*/ +QColor QColor::dark(int factor) const noexcept +{ + return darker(factor); +} +#endif + #if QT_VERSION < QT_VERSION_CHECK(6,0,0) /*! Assigns a copy of \a color to this color, and returns a reference to it. */ -QColor &QColor::operator=(const QColor &color) Q_DECL_NOTHROW +QColor &QColor::operator=(const QColor &color) noexcept { cspec = color.cspec; ct.argb = color.ct.argb; @@ -2785,7 +2810,7 @@ QColor &QColor::operator=(const QColor &color) Q_DECL_NOTHROW /*! \overload Assigns a copy of \a color and returns a reference to this color. */ -QColor &QColor::operator=(Qt::GlobalColor color) Q_DECL_NOTHROW +QColor &QColor::operator=(Qt::GlobalColor color) noexcept { return operator=(QColor(color)); } @@ -2794,7 +2819,7 @@ QColor &QColor::operator=(Qt::GlobalColor color) Q_DECL_NOTHROW Returns \c true if this color has the same RGB and alpha values as \a color; otherwise returns \c false. */ -bool QColor::operator==(const QColor &color) const Q_DECL_NOTHROW +bool QColor::operator==(const QColor &color) const noexcept { if (cspec == Hsl && cspec == color.cspec) { return (ct.argb.alpha == color.ct.argb.alpha @@ -2821,7 +2846,7 @@ bool QColor::operator==(const QColor &color) const Q_DECL_NOTHROW Returns \c true if this color has a different RGB and alpha values from \a color; otherwise returns \c false. */ -bool QColor::operator!=(const QColor &color) const Q_DECL_NOTHROW +bool QColor::operator!=(const QColor &color) const noexcept { return !operator==(color); } @@ -2838,7 +2863,7 @@ QColor::operator QVariant() const Marks the color as invalid and sets all components to zero (alpha is set to fully opaque for compatibility with Qt 3). */ -void QColor::invalidate() Q_DECL_NOTHROW +void QColor::invalidate() noexcept { cspec = Invalid; ct.argb.alpha = USHRT_MAX; diff --git a/src/gui/painting/qcolor.h b/src/gui/painting/qcolor.h index a9b05ae7e3..cbc8b98f9c 100644 --- a/src/gui/painting/qcolor.h +++ b/src/gui/painting/qcolor.h @@ -67,32 +67,32 @@ public: enum Spec { Invalid, Rgb, Hsv, Cmyk, Hsl }; enum NameFormat { HexRgb, HexArgb }; - inline QColor() Q_DECL_NOTHROW; - QColor(Qt::GlobalColor color) Q_DECL_NOTHROW; + inline QColor() noexcept; + QColor(Qt::GlobalColor color) noexcept; inline QColor(int r, int g, int b, int a = 255); - QColor(QRgb rgb) Q_DECL_NOTHROW; - QColor(QRgba64 rgba64) Q_DECL_NOTHROW; + QColor(QRgb rgb) noexcept; + QColor(QRgba64 rgba64) noexcept; #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; + QColor(Spec spec) noexcept; #if QT_VERSION < QT_VERSION_CHECK(6,0,0) - inline QColor(const QColor &color) Q_DECL_NOTHROW; // ### Qt 6: remove all of these, the trivial ones are fine. + inline QColor(const QColor &color) noexcept; // ### Qt 6: remove all of these, the trivial ones are fine. # ifdef Q_COMPILER_RVALUE_REFS - QColor(QColor &&other) Q_DECL_NOTHROW : cspec(other.cspec), ct(other.ct) {} - QColor &operator=(QColor &&other) Q_DECL_NOTHROW + QColor(QColor &&other) noexcept : cspec(other.cspec), ct(other.ct) {} + QColor &operator=(QColor &&other) noexcept { cspec = other.cspec; ct = other.ct; return *this; } # endif - QColor &operator=(const QColor &) Q_DECL_NOTHROW; + QColor &operator=(const QColor &) noexcept; #endif // Qt < 6 - QColor &operator=(Qt::GlobalColor color) Q_DECL_NOTHROW; + QColor &operator=(Qt::GlobalColor color) noexcept; - bool isValid() const Q_DECL_NOTHROW; + bool isValid() const noexcept; // ### Qt 6: merge overloads QString name() const; @@ -106,25 +106,25 @@ public: static QStringList colorNames(); - inline Spec spec() const Q_DECL_NOTHROW + inline Spec spec() const noexcept { return cspec; } - int alpha() const Q_DECL_NOTHROW; + int alpha() const noexcept; void setAlpha(int alpha); - qreal alphaF() const Q_DECL_NOTHROW; + qreal alphaF() const noexcept; void setAlphaF(qreal alpha); - int red() const Q_DECL_NOTHROW; - int green() const Q_DECL_NOTHROW; - int blue() const Q_DECL_NOTHROW; + int red() const noexcept; + int green() const noexcept; + int blue() const noexcept; void setRed(int red); void setGreen(int green); void setBlue(int blue); - qreal redF() const Q_DECL_NOTHROW; - qreal greenF() const Q_DECL_NOTHROW; - qreal blueF() const Q_DECL_NOTHROW; + qreal redF() const noexcept; + qreal greenF() const noexcept; + qreal blueF() const noexcept; void setRedF(qreal red); void setGreenF(qreal green); void setBlueF(qreal blue); @@ -135,26 +135,26 @@ public: void getRgbF(qreal *r, qreal *g, qreal *b, qreal *a = nullptr) const; void setRgbF(qreal r, qreal g, qreal b, qreal a = 1.0); - QRgba64 rgba64() const Q_DECL_NOTHROW; - void setRgba64(QRgba64 rgba) Q_DECL_NOTHROW; + QRgba64 rgba64() const noexcept; + void setRgba64(QRgba64 rgba) noexcept; - QRgb rgba() const Q_DECL_NOTHROW; - void setRgba(QRgb rgba) Q_DECL_NOTHROW; + QRgb rgba() const noexcept; + void setRgba(QRgb rgba) noexcept; - QRgb rgb() const Q_DECL_NOTHROW; - void setRgb(QRgb rgb) Q_DECL_NOTHROW; + QRgb rgb() const noexcept; + void setRgb(QRgb rgb) noexcept; - int hue() const Q_DECL_NOTHROW; // 0 <= hue < 360 - int saturation() const Q_DECL_NOTHROW; - int hsvHue() const Q_DECL_NOTHROW; // 0 <= hue < 360 - int hsvSaturation() const Q_DECL_NOTHROW; - int value() const Q_DECL_NOTHROW; + int hue() const noexcept; // 0 <= hue < 360 + int saturation() const noexcept; + int hsvHue() const noexcept; // 0 <= hue < 360 + int hsvSaturation() const noexcept; + int value() const noexcept; - qreal hueF() const Q_DECL_NOTHROW; // 0.0 <= hueF < 360.0 - qreal saturationF() const Q_DECL_NOTHROW; - qreal hsvHueF() const Q_DECL_NOTHROW; // 0.0 <= hueF < 360.0 - qreal hsvSaturationF() const Q_DECL_NOTHROW; - qreal valueF() const Q_DECL_NOTHROW; + qreal hueF() const noexcept; // 0.0 <= hueF < 360.0 + qreal saturationF() const noexcept; + qreal hsvHueF() const noexcept; // 0.0 <= hueF < 360.0 + qreal hsvSaturationF() const noexcept; + qreal valueF() const noexcept; void getHsv(int *h, int *s, int *v, int *a = nullptr) const; void setHsv(int h, int s, int v, int a = 255); @@ -162,15 +162,15 @@ public: void getHsvF(qreal *h, qreal *s, qreal *v, qreal *a = nullptr) const; void setHsvF(qreal h, qreal s, qreal v, qreal a = 1.0); - int cyan() const Q_DECL_NOTHROW; - int magenta() const Q_DECL_NOTHROW; - int yellow() const Q_DECL_NOTHROW; - int black() const Q_DECL_NOTHROW; + int cyan() const noexcept; + int magenta() const noexcept; + int yellow() const noexcept; + int black() const noexcept; - qreal cyanF() const Q_DECL_NOTHROW; - qreal magentaF() const Q_DECL_NOTHROW; - qreal yellowF() const Q_DECL_NOTHROW; - qreal blackF() const Q_DECL_NOTHROW; + qreal cyanF() const noexcept; + qreal magentaF() const noexcept; + qreal yellowF() const noexcept; + qreal blackF() const noexcept; void getCmyk(int *c, int *m, int *y, int *k, int *a = nullptr); // ### Qt 6: remove void getCmyk(int *c, int *m, int *y, int *k, int *a = nullptr) const; @@ -180,13 +180,13 @@ public: void getCmykF(qreal *c, qreal *m, qreal *y, qreal *k, qreal *a = nullptr) const; void setCmykF(qreal c, qreal m, qreal y, qreal k, qreal a = 1.0); - int hslHue() const Q_DECL_NOTHROW; // 0 <= hue < 360 - int hslSaturation() const Q_DECL_NOTHROW; - int lightness() const Q_DECL_NOTHROW; + int hslHue() const noexcept; // 0 <= hue < 360 + int hslSaturation() const noexcept; + int lightness() const noexcept; - qreal hslHueF() const Q_DECL_NOTHROW; // 0.0 <= hueF < 360.0 - qreal hslSaturationF() const Q_DECL_NOTHROW; - qreal lightnessF() const Q_DECL_NOTHROW; + qreal hslHueF() const noexcept; // 0.0 <= hueF < 360.0 + qreal hslSaturationF() const noexcept; + qreal lightnessF() const noexcept; void getHsl(int *h, int *s, int *l, int *a = nullptr) const; void setHsl(int h, int s, int l, int a = 255); @@ -194,21 +194,21 @@ public: void getHslF(qreal *h, qreal *s, qreal *l, qreal *a = nullptr) const; void setHslF(qreal h, qreal s, qreal l, qreal a = 1.0); - QColor toRgb() const Q_DECL_NOTHROW; - QColor toHsv() const Q_DECL_NOTHROW; - QColor toCmyk() const Q_DECL_NOTHROW; - QColor toHsl() const Q_DECL_NOTHROW; + QColor toRgb() const noexcept; + QColor toHsv() const noexcept; + QColor toCmyk() const noexcept; + QColor toHsl() const noexcept; - Q_REQUIRED_RESULT QColor convertTo(Spec colorSpec) const Q_DECL_NOTHROW; + Q_REQUIRED_RESULT QColor convertTo(Spec colorSpec) const noexcept; - static QColor fromRgb(QRgb rgb) Q_DECL_NOTHROW; - static QColor fromRgba(QRgb rgba) Q_DECL_NOTHROW; + static QColor fromRgb(QRgb rgb) noexcept; + static QColor fromRgba(QRgb rgba) noexcept; static QColor fromRgb(int r, int g, int b, int a = 255); static QColor fromRgbF(qreal r, qreal g, qreal b, qreal a = 1.0); - static QColor fromRgba64(ushort r, ushort g, ushort b, ushort a = USHRT_MAX) Q_DECL_NOTHROW; - static QColor fromRgba64(QRgba64 rgba) Q_DECL_NOTHROW; + static QColor fromRgba64(ushort r, ushort g, ushort b, ushort a = USHRT_MAX) noexcept; + static QColor fromRgba64(QRgba64 rgba) noexcept; static QColor fromHsv(int h, int s, int v, int a = 255); static QColor fromHsvF(qreal h, qreal s, qreal v, qreal a = 1.0); @@ -219,25 +219,29 @@ public: static QColor fromHsl(int h, int s, int l, int a = 255); static QColor fromHslF(qreal h, qreal s, qreal l, qreal a = 1.0); - Q_REQUIRED_RESULT QColor light(int f = 150) const Q_DECL_NOTHROW; - Q_REQUIRED_RESULT QColor lighter(int f = 150) const Q_DECL_NOTHROW; - Q_REQUIRED_RESULT QColor dark(int f = 200) const Q_DECL_NOTHROW; - Q_REQUIRED_RESULT QColor darker(int f = 200) const Q_DECL_NOTHROW; +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED_X("Use QColor::lighter() instead") + Q_REQUIRED_RESULT QColor light(int f = 150) const noexcept; + QT_DEPRECATED_X("Use QColor::darker() instead") + Q_REQUIRED_RESULT QColor dark(int f = 200) const noexcept; +#endif + Q_REQUIRED_RESULT QColor lighter(int f = 150) const noexcept; + Q_REQUIRED_RESULT QColor darker(int f = 200) const noexcept; - bool operator==(const QColor &c) const Q_DECL_NOTHROW; - bool operator!=(const QColor &c) const Q_DECL_NOTHROW; + bool operator==(const QColor &c) const noexcept; + bool operator!=(const QColor &c) const noexcept; 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; + static bool isValidColor(QStringView) noexcept; + static bool isValidColor(QLatin1String) noexcept; private: - void invalidate() Q_DECL_NOTHROW; + void invalidate() noexcept; template <typename String> bool setColorFromString(String name); @@ -282,7 +286,7 @@ private: }; Q_DECLARE_TYPEINFO(QColor, QT_VERSION >= QT_VERSION_CHECK(6,0,0) ? Q_MOVABLE_TYPE : Q_RELOCATABLE_TYPE); -inline QColor::QColor() Q_DECL_NOTHROW +inline QColor::QColor() noexcept { invalidate(); } inline QColor::QColor(int r, int g, int b, int a) @@ -300,20 +304,14 @@ inline QColor::QColor(const QString& aname) #endif #if QT_VERSION < QT_VERSION_CHECK(6,0,0) -inline QColor::QColor(const QColor &acolor) Q_DECL_NOTHROW +inline QColor::QColor(const QColor &acolor) noexcept : cspec(acolor.cspec) { ct.argb = acolor.ct.argb; } #endif -inline bool QColor::isValid() const Q_DECL_NOTHROW +inline bool QColor::isValid() const noexcept { return cspec != Invalid; } -inline QColor QColor::lighter(int f) const Q_DECL_NOTHROW -{ return light(f); } - -inline QColor QColor::darker(int f) const Q_DECL_NOTHROW -{ return dark(f); } - QT_END_NAMESPACE #endif // QCOLOR_H diff --git a/src/gui/painting/qcolormatrix_p.h b/src/gui/painting/qcolormatrix_p.h new file mode 100644 index 0000000000..3d1dca6222 --- /dev/null +++ b/src/gui/painting/qcolormatrix_p.h @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOLORMATRIX_H +#define QCOLORMATRIX_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qtguiglobal.h> +#include <cmath> + +QT_BEGIN_NAMESPACE + +// An abstract 3 value color +class QColorVector +{ +public: + QColorVector() = default; + constexpr QColorVector(float x, float y, float z) : x(x), y(y), z(z), _unused(0.0f) { } + float x; // X, x or red + float y; // Y, y or green + float z; // Z, Y or blue + float _unused; + + friend inline bool operator==(const QColorVector &v1, const QColorVector &v2); + friend inline bool operator!=(const QColorVector &v1, const QColorVector &v2); + + static constexpr QColorVector null() { return QColorVector(0.0f, 0.0f, 0.0f); } + // Common whitepoints on normalized XYZ form: + static constexpr QColorVector D50() { return QColorVector(0.96421f, 1.0f, 0.82519f); } + static constexpr QColorVector D65() { return QColorVector(0.95043f, 1.0f, 1.08890f); } +}; + +inline bool operator==(const QColorVector &v1, const QColorVector &v2) +{ + return (std::abs(v1.x - v2.x) < (1.0f / 2048.0f)) + && (std::abs(v1.y - v2.y) < (1.0f / 2048.0f)) + && (std::abs(v1.z - v2.z) < (1.0f / 2048.0f)); +} + +inline bool operator!=(const QColorVector &v1, const QColorVector &v2) +{ + return !(v1 == v2); +} + + +// A matrix mapping 3 value colors. +// Not using QMatrix because only floats are needed and performance is critical. +class QColorMatrix +{ +public: + // We are storing the matrix transposed as that is more convenient: + QColorVector r; + QColorVector g; + QColorVector b; + + friend inline bool operator==(const QColorMatrix &m1, const QColorMatrix &m2); + friend inline bool operator!=(const QColorMatrix &m1, const QColorMatrix &m2); + + bool isValid() const + { + // A color matrix must be invertible + float det = r.x * (b.z * g.y - g.z * b.y) - + r.y * (b.z * g.x - g.z * b.x) + + r.z * (b.y * g.x - g.y * b.x); + return !qFuzzyIsNull(det); + } + + QColorMatrix inverted() const + { + float det = r.x * (b.z * g.y - g.z * b.y) - + r.y * (b.z * g.x - g.z * b.x) + + r.z * (b.y * g.x - g.y * b.x); + det = 1.0f / det; + QColorMatrix inv; + inv.r.x = (g.y * b.z - b.y * g.z) * det; + inv.r.y = (b.y * r.z - r.y * b.z) * det; + inv.r.z = (r.y * g.z - g.y * r.z) * det; + inv.g.x = (b.x * g.z - g.x * b.z) * det; + inv.g.y = (r.x * b.z - b.x * r.z) * det; + inv.g.z = (g.x * r.z - r.x * g.z) * det; + inv.b.x = (g.x * b.y - b.x * g.y) * det; + inv.b.y = (b.x * r.y - r.x * b.y) * det; + inv.b.z = (r.x * g.y - g.x * r.y) * det; + return inv; + } + QColorMatrix operator*(const QColorMatrix &o) const + { + QColorMatrix comb; + comb.r.x = r.x * o.r.x + g.x * o.r.y + b.x * o.r.z; + comb.g.x = r.x * o.g.x + g.x * o.g.y + b.x * o.g.z; + comb.b.x = r.x * o.b.x + g.x * o.b.y + b.x * o.b.z; + + comb.r.y = r.y * o.r.x + g.y * o.r.y + b.y * o.r.z; + comb.g.y = r.y * o.g.x + g.y * o.g.y + b.y * o.g.z; + comb.b.y = r.y * o.b.x + g.y * o.b.y + b.y * o.b.z; + + comb.r.z = r.z * o.r.x + g.z * o.r.y + b.z * o.r.z; + comb.g.z = r.z * o.g.x + g.z * o.g.y + b.z * o.g.z; + comb.b.z = r.z * o.b.x + g.z * o.b.y + b.z * o.b.z; + return comb; + + } + QColorVector map(const QColorVector &c) const + { + return QColorVector { c.x * r.x + c.y * g.x + c.z * b.x, + c.x * r.y + c.y * g.y + c.z * b.y, + c.x * r.z + c.y * g.z + c.z * b.z }; + } + QColorMatrix transposed() const + { + return QColorMatrix { { r.x, g.x, b.x }, + { r.y, g.y, b.y }, + { r.z, g.z, b.z } }; + } + + static QColorMatrix null() + { + return { QColorVector::null(), QColorVector::null(), QColorVector::null() }; + } + static QColorMatrix identity() + { + return { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }; + } + static QColorMatrix toXyzFromSRgb() + { + return QColorMatrix { { 0.4360217452f, 0.2224751115f, 0.0139281144f }, + { 0.3851087987f, 0.7169067264f, 0.0971015394f }, + { 0.1430812478f, 0.0606181994f, 0.7141585946f } }; + } + static QColorMatrix toXyzFromAdobeRgb() + { + return QColorMatrix { { 0.6097189188f, 0.3111021519f, 0.0194766335f }, + { 0.2052682191f, 0.6256770492f, 0.0608891509f }, + { 0.1492247432f, 0.0632209629f, 0.7448224425f } }; + } + static QColorMatrix toXyzFromDciP3D65() + { + return QColorMatrix { { 0.5150973201f, 0.2411795557f, -0.0010491034f }, + { 0.2919696569f, 0.6922441125f, 0.0418830328f }, + { 0.1571449190f, 0.0665764511f, 0.7843542695f } }; + } + static QColorMatrix toXyzFromProPhotoRgb() + { + return QColorMatrix { { 0.7976672649f, 0.2880374491f, 0.0000000000f }, + { 0.1351922452f, 0.7118769884f, 0.0000000000f }, + { 0.0313525312f, 0.0000856627f, 0.8251883388f } }; + } + static QColorMatrix toXyzFromBt2020() + { + return QColorMatrix { { 0.6506130099f, 0.2695676684f, -0.0018652577f }, + { 0.1865101457f, 0.6840794086f, 0.0172256753f }, + { 0.1270887405f, 0.0463530831f, 0.8098278046f } }; + } +}; + +inline bool operator==(const QColorMatrix &m1, const QColorMatrix &m2) +{ + return (m1.r == m2.r) && (m1.g == m2.g) && (m1.b == m2.b); +} + +inline bool operator!=(const QColorMatrix &m1, const QColorMatrix &m2) +{ + return !(m1 == m2); +} + +QT_END_NAMESPACE + +#endif // QCOLORMATRIX_P_H diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp new file mode 100644 index 0000000000..24785f7b61 --- /dev/null +++ b/src/gui/painting/qcolorspace.cpp @@ -0,0 +1,633 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcolorspace.h" +#include "qcolorspace_p.h" + +#include "qcolortransform.h" +#include "qcolormatrix_p.h" +#include "qcolortransferfunction_p.h" +#include "qcolortransform_p.h" +#include "qicc_p.h" + +#include <qmath.h> +#include <qtransform.h> + +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +QColorSpacePrivate::QColorSpacePrivate() + : id(QColorSpace::Unknown) + , gamut(QColorSpace::Gamut::Custom) + , transferFunction(QColorSpace::TransferFunction::Custom) + , gamma(0.0f) + , whitePoint(QColorVector::null()) + , toXyz(QColorMatrix::null()) +{ +} + +QColorSpacePrivate::QColorSpacePrivate(QColorSpace::ColorSpaceId colorSpaceId) + : id(colorSpaceId) +{ + switch (colorSpaceId) { + case QColorSpace::Undefined: + gamut = QColorSpace::Gamut::Custom; + transferFunction = QColorSpace::TransferFunction::Custom; + gamma = 0.0f; + description = QStringLiteral("Undefined"); + break; + case QColorSpace::SRgb: + gamut = QColorSpace::Gamut::SRgb; + transferFunction = QColorSpace::TransferFunction::SRgb; + gamma = 2.31f; // ? + description = QStringLiteral("sRGB"); + break; + case QColorSpace::SRgbLinear: + gamut = QColorSpace::Gamut::SRgb; + transferFunction = QColorSpace::TransferFunction::Linear; + gamma = 1.0f; + description = QStringLiteral("Linear sRGB"); + break; + case QColorSpace::AdobeRgb: + gamut = QColorSpace::Gamut::AdobeRgb; + transferFunction = QColorSpace::TransferFunction::Gamma; + gamma = 2.19921875f; // Not quite 2.2, see https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf + description = QStringLiteral("Adobe RGB"); + break; + case QColorSpace::DisplayP3: + gamut = QColorSpace::Gamut::DciP3D65; + transferFunction = QColorSpace::TransferFunction::SRgb; + gamma = 2.31f; // ? + description = QStringLiteral("Display P3"); + break; + case QColorSpace::ProPhotoRgb: + gamut = QColorSpace::Gamut::ProPhotoRgb; + transferFunction = QColorSpace::TransferFunction::ProPhotoRgb; + gamma = 1.8f; + description = QStringLiteral("ProPhoto RGB"); + break; + case QColorSpace::Bt2020: + gamut = QColorSpace::Gamut::Bt2020; + transferFunction = QColorSpace::TransferFunction::Bt2020; + gamma = 2.1f; // ? + description = QStringLiteral("BT.2020"); + break; + case QColorSpace::Unknown: + qWarning("Can not create an unknown color space"); + Q_FALLTHROUGH(); + default: + Q_UNREACHABLE(); + } + initialize(); +} + +QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Gamut gamut, QColorSpace::TransferFunction fun, float gamma) + : gamut(gamut) + , transferFunction(fun) + , gamma(gamma) +{ + if (!identifyColorSpace()) + id = QColorSpace::Unknown; + initialize(); +} + +bool QColorSpacePrivate::identifyColorSpace() +{ + switch (gamut) { + case QColorSpace::Gamut::SRgb: + if (transferFunction == QColorSpace::TransferFunction::SRgb) { + id = QColorSpace::SRgb; + description = QStringLiteral("sRGB"); + return true; + } + if (transferFunction == QColorSpace::TransferFunction::Linear) { + id = QColorSpace::SRgbLinear; + description = QStringLiteral("Linear sRGB"); + return true; + } + break; + case QColorSpace::Gamut::AdobeRgb: + if (transferFunction == QColorSpace::TransferFunction::Gamma) { + if (qAbs(gamma - 2.19921875f) < (1/1024.0f)) { + id = QColorSpace::AdobeRgb; + description = QStringLiteral("Adobe RGB"); + return true; + } + } + break; + case QColorSpace::Gamut::DciP3D65: + if (transferFunction == QColorSpace::TransferFunction::SRgb) { + id = QColorSpace::DisplayP3; + description = QStringLiteral("Display P3"); + return true; + } + break; + case QColorSpace::Gamut::ProPhotoRgb: + if (transferFunction == QColorSpace::TransferFunction::ProPhotoRgb) { + id = QColorSpace::ProPhotoRgb; + description = QStringLiteral("ProPhoto RGB"); + return true; + } + if (transferFunction == QColorSpace::TransferFunction::Gamma) { + // ProPhoto RGB's curve is effectively gamma 1.8 for 8bit precision. + if (qAbs(gamma - 1.8f) < (1/1024.0f)) { + id = QColorSpace::ProPhotoRgb; + description = QStringLiteral("ProPhoto RGB"); + return true; + } + } + break; + case QColorSpace::Gamut::Bt2020: + if (transferFunction == QColorSpace::TransferFunction::Bt2020) { + id = QColorSpace::Bt2020; + description = QStringLiteral("BT.2020"); + return true; + } + break; + default: + break; + } + return false; +} + +void QColorSpacePrivate::initialize() +{ + setToXyzMatrix(); + setTransferFunction(); +} + +void QColorSpacePrivate::setToXyzMatrix() +{ + switch (gamut) { + case QColorSpace::Gamut::SRgb: + toXyz = QColorMatrix::toXyzFromSRgb(); + whitePoint = QColorVector::D65(); + return; + case QColorSpace::Gamut::AdobeRgb: + toXyz = QColorMatrix::toXyzFromAdobeRgb(); + whitePoint = QColorVector::D65(); + return; + case QColorSpace::Gamut::DciP3D65: + toXyz = QColorMatrix::toXyzFromDciP3D65(); + whitePoint = QColorVector::D65(); + return; + case QColorSpace::Gamut::ProPhotoRgb: + toXyz = QColorMatrix::toXyzFromProPhotoRgb(); + whitePoint = QColorVector::D50(); + return; + case QColorSpace::Gamut::Bt2020: + toXyz = QColorMatrix::toXyzFromBt2020(); + whitePoint = QColorVector::D65(); + return; + case QColorSpace::Gamut::Custom: + toXyz = QColorMatrix::null(); + whitePoint = QColorVector::D50(); + return; + } + Q_UNREACHABLE(); +} + +void QColorSpacePrivate::setTransferFunction() +{ + switch (transferFunction) { + case QColorSpace::TransferFunction::Linear: + trc[0].m_type = QColorTrc::Type::Function; + trc[0].m_fun = QColorTransferFunction(); + break; + case QColorSpace::TransferFunction::Gamma: + trc[0].m_type = QColorTrc::Type::Function; + trc[0].m_fun = QColorTransferFunction::fromGamma(gamma); + break; + case QColorSpace::TransferFunction::SRgb: + trc[0].m_type = QColorTrc::Type::Function; + trc[0].m_fun = QColorTransferFunction::fromSRgb(); + break; + case QColorSpace::TransferFunction::ProPhotoRgb: + trc[0].m_type = QColorTrc::Type::Function; + trc[0].m_fun = QColorTransferFunction::fromProPhotoRgb(); + break; + case QColorSpace::TransferFunction::Bt2020: + trc[0].m_type = QColorTrc::Type::Function; + trc[0].m_fun = QColorTransferFunction::fromBt2020(); + break; + case QColorSpace::TransferFunction::Custom: + break; + default: + Q_UNREACHABLE(); + break; + } + trc[1] = trc[0]; + trc[2] = trc[0]; +} + +QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpacePrivate *out) const +{ + Q_ASSERT(out); + QColorTransform combined; + combined.d_ptr.reset(new QColorTransformPrivate); + combined.d_ptr->colorSpaceIn = this; + combined.d_ptr->colorSpaceOut = out; + combined.d_ptr->colorMatrix = out->toXyz.inverted() * toXyz; + return combined; +} + +/*! + \class QColorSpace + \brief The QColorSpace class provides a color space abstraction. + \since 5.14 + + \ingroup painting + \ingroup appearance + \inmodule QtGui + + Color values can be interpreted in different ways, and based on the interpretation + can live in different spaces. We call this \e {color spaces}. + + QColorSpace provides access to creating several predefined color spaces and + can generate QColorTransforms for converting colors from one color space to + another. + + QColorSpace can also represent color spaces defined by ICC profiles or embedded + in images, that do not otherwise fit the predefined color spaces. + + A color space can generally speaking be conceived as a combination of a transfer + function and a gamut. The gamut defines which colors the color space can represent. + A color space that can represent a wider range of colors is also known as a + wide-gamut color space. The gamut is defined by three primary colors that represent + exactly how red, green, and blue look in this particular color space, and a white + color that represents where and how bright pure white is. + + The transfer function or gamma curve determines how each component in the + color space is encoded. These are used because human perception does not operate + linearly, and the transfer functions try to ensure that colors will seem evenly + spaced to human eyes. +*/ + + +/*! + \enum QColorSpace::ColorSpaceId + + Predefined color spaces. + + \value Undefined An empty, invalid or unsupported color space. + \value Unknown A valid color space that doesn't match any of the predefined color spaces. + \value SRgb The sRGB color space, which Qt operates in by default. It is a close approximation + of how most classic monitors operate, and a mode most software and hardware support. + \l{http://www.color.org/chardata/rgb/srgb.xalter}{ICC registration of sRGB}. + \value SRgbLinear The sRGB color space with linear gamma. Useful for gamma-corrected blending. + \value AdobeRgb The Adobe RGB color space is a classic wide-gamut color space, using a gamma of 2.2. + \l{http://www.color.org/chardata/rgb/adobergb.xalter}{ICC registration of Adobe RGB (1998)} + \value DisplayP3 A color-space using the primaries of DCI-P3, but with the whitepoint and transfer + function of sRGB. Common in modern wide-gamut screens. + \l{http://www.color.org/chardata/rgb/DCIP3.xalter}{ICC registration of DCI-P3} + \value ProPhotoRgb The Pro Photo RGB color space, also known as ROMM RGB is a very wide gamut color space. + \l{http://www.color.org/chardata/rgb/rommrgb.xalter}{ICC registration of ROMM RGB} + \value Bt2020 BT.2020 also known as Rec.2020 is the color space of HDR TVs. + \l{http://www.color.org/chardata/rgb/BT2020.xalter}{ICC registration of BT.2020} +*/ + +/*! + \enum QColorSpace::Gamut + + Predefined gamuts, or sets of primary colors. + + \value Custom The gamut is undefined or does not match any predefined sets. + \value SRgb The sRGB gamut + \value AdobeRgb The Adobe RGB gamut + \value DciP3D65 The DCI-P3 gamut with the D65 whitepoint + \value ProPhotoRgb The ProPhoto RGB gamut with the D50 whitepoint + \value Bt2020 The BT.2020 gamut +*/ + +/*! + \enum QColorSpace::TransferFunction + + Predefined transfer functions or gamma curves. + + \value Custom The custom or null transfer function + \value Linear The linear transfer functions + \value Gamma A transfer function that is a real gamma curve based on the value of gamma() + \value SRgb The sRGB transfer function, composed of linear and gamma parts + \value ProPhotoRgb The ProPhoto RGB transfer function, composed of linear and gamma parts + \value Bt2020 The BT.2020 transfer function, composed of linear and gamma parts +*/ + +/*! + Creates a new colorspace object that represents \a colorSpaceId. + */ +QColorSpace::QColorSpace(QColorSpace::ColorSpaceId colorSpaceId) +{ + static QExplicitlySharedDataPointer<QColorSpacePrivate> predefinedColorspacePrivates[QColorSpace::Bt2020]; + if (colorSpaceId <= QColorSpace::Unknown) { + if (!predefinedColorspacePrivates[0]) + predefinedColorspacePrivates[0] = new QColorSpacePrivate(QColorSpace::Undefined); + d_ptr = predefinedColorspacePrivates[0]; // unknown and undefined both returns the static undefined colorspace. + } else { + if (!predefinedColorspacePrivates[colorSpaceId - 1]) + predefinedColorspacePrivates[colorSpaceId - 1] = new QColorSpacePrivate(colorSpaceId); + d_ptr = predefinedColorspacePrivates[colorSpaceId - 1]; + } + + Q_ASSERT(colorSpaceId == QColorSpace::Undefined || isValid()); +} + +/*! + Creates a custom color space with the gamut \a gamut, using the transfer function \a fun and + optionally \a gamma. + */ +QColorSpace::QColorSpace(QColorSpace::Gamut gamut, QColorSpace::TransferFunction fun, float gamma) + : d_ptr(new QColorSpacePrivate(gamut, fun, gamma)) +{ +} + +/*! + Creates a custom color space with the gamut \a gamut, using a gamma transfer function of + \a gamma. + */ +QColorSpace::QColorSpace(QColorSpace::Gamut gamut, float gamma) + : d_ptr(new QColorSpacePrivate(gamut, TransferFunction::Gamma, gamma)) +{ +} + +QColorSpace::~QColorSpace() +{ +} + +QColorSpace::QColorSpace(QColorSpace &&colorSpace) + : d_ptr(std::move(colorSpace.d_ptr)) +{ +} + +QColorSpace::QColorSpace(const QColorSpace &colorSpace) + : d_ptr(colorSpace.d_ptr) +{ +} + +QColorSpace &QColorSpace::operator=(QColorSpace &&colorSpace) +{ + d_ptr = std::move(colorSpace.d_ptr); + return *this; +} + +QColorSpace &QColorSpace::operator=(const QColorSpace &colorSpace) +{ + d_ptr = colorSpace.d_ptr; + return *this; +} + +/*! + Returns the id of the predefined color space this object + represents or \c Unknown if it doesn't match any of them. +*/ +QColorSpace::ColorSpaceId QColorSpace::colorSpaceId() const noexcept +{ + return d_ptr->id; +} + +/*! + Returns the predefined gamut of the color space + or \c Gamut::Custom if it doesn't match any of them. +*/ +QColorSpace::Gamut QColorSpace::gamut() const noexcept +{ + return d_ptr->gamut; +} + +/*! + Returns the predefined transfer function of the color space + or \c TransferFunction::Custom if it doesn't match any of them. + + \sa gamma() +*/ +QColorSpace::TransferFunction QColorSpace::transferFunction() const noexcept +{ + return d_ptr->transferFunction; +} + +/*! + Returns the gamma value of color spaces with \c TransferFunction::Gamma, + an approximate gamma value for other predefined color spaces, or + 0.0 if no approximate gamma is known. + + \sa transferFunction() +*/ +float QColorSpace::gamma() const noexcept +{ + return d_ptr->gamma; +} + +/*! + Returns an ICC profile representing the color space. + + If the color space was generated from an ICC profile, that profile + is returned, otherwise one is generated. + + \note Even invalid color spaces may return the ICC profile if they + were generated from one, to allow applications to implement wider + support themselves. + + \sa fromIccProfile() +*/ +QByteArray QColorSpace::iccProfile() const +{ + if (!d_ptr->iccProfile.isEmpty()) + return d_ptr->iccProfile; + if (!isValid()) + return QByteArray(); + return QIcc::toIccProfile(*this); +} + +/*! + Creates a QColorSpace from ICC profile \a iccProfile. + + \note Not all ICC profiles are supported. QColorSpace only supports + RGB-XYZ ICC profiles that are three-component matrix-based. + + If the ICC profile is not supported an invalid QColorSpace is returned + where you can still read the original ICC profile using iccProfile(). + + \sa iccProfile() +*/ +QColorSpace QColorSpace::fromIccProfile(const QByteArray &iccProfile) +{ + QColorSpace colorSpace; + if (QIcc::fromIccProfile(iccProfile, &colorSpace)) + return colorSpace; + colorSpace.d_ptr->id = QColorSpace::Undefined; + colorSpace.d_ptr->iccProfile = iccProfile; + return colorSpace; +} + +/*! + Returns \c true if the color space is valid. +*/ +bool QColorSpace::isValid() const noexcept +{ + return d_ptr->id != QColorSpace::Undefined && d_ptr->toXyz.isValid() + && d_ptr->trc[0].isValid() && d_ptr->trc[1].isValid() && d_ptr->trc[2].isValid(); +} + +/*! + \relates QColorSpace + Returns \c true if colorspace \a colorSpace1 is equal to colorspace \a colorSpace2; + otherwise returns \c false +*/ +bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2) +{ + if (colorSpace1.d_ptr == colorSpace2.d_ptr) + return true; + + if (colorSpace1.colorSpaceId() == QColorSpace::Undefined && colorSpace2.colorSpaceId() == QColorSpace::Undefined) + return colorSpace1.d_ptr->iccProfile == colorSpace2.d_ptr->iccProfile; + + if (colorSpace1.colorSpaceId() != QColorSpace::Unknown && colorSpace2.colorSpaceId() != QColorSpace::Unknown) + return colorSpace1.colorSpaceId() == colorSpace2.colorSpaceId(); + + if (colorSpace1.gamut() != QColorSpace::Gamut::Custom && colorSpace2.gamut() != QColorSpace::Gamut::Custom) { + if (colorSpace1.gamut() != colorSpace2.gamut()) + return false; + } else { + if (colorSpace1.d_ptr->toXyz != colorSpace2.d_ptr->toXyz) + return false; + } + + if (colorSpace1.transferFunction() != QColorSpace::TransferFunction::Custom && + colorSpace2.transferFunction() != QColorSpace::TransferFunction::Custom) { + if (colorSpace1.transferFunction() != colorSpace2.transferFunction()) + return false; + if (colorSpace1.transferFunction() == QColorSpace::TransferFunction::Gamma) + return colorSpace1.gamma() == colorSpace2.gamma(); + return true; + } + + if (colorSpace1.d_ptr->trc[0] != colorSpace2.d_ptr->trc[0] || + colorSpace1.d_ptr->trc[1] != colorSpace2.d_ptr->trc[1] || + colorSpace1.d_ptr->trc[2] != colorSpace2.d_ptr->trc[2]) + return false; + + return true; +} + +/*! + \fn bool operator!=(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2) + \relates QColorSpace + + Returns \c true if colorspace \a colorspace1 is not equal to colorspace \a colorspace2; + otherwise returns \c false +*/ + +/*! + Generates and returns a color space transformation from this color space to + \a colorspace. +*/ +QColorTransform QColorSpace::transformationToColorSpace(const QColorSpace &colorspace) const +{ + if (!isValid() || !colorspace.isValid()) + return QColorTransform(); + + return d_ptr->transformationToColorSpace(colorspace.d_ptr.constData()); +} + +/*! + \internal +*/ +QColorSpacePrivate *QColorSpace::d_func() +{ + d_ptr.detach(); + return d_ptr.data(); +} + +/*! + \fn const QColorSpacePrivate* QColorSpacePrivate::d_func() const + \internal +*/ + +/***************************************************************************** + QColorSpace stream functions + *****************************************************************************/ +#if !defined(QT_NO_DATASTREAM) +/*! + \fn QDataStream &operator<<(QDataStream &stream, const QColorSpace &colorSpace) + \relates QColorSpace + + Writes the given \a colorSpace to the given \a stream as an ICC profile. + + \sa QColorSpace::iccProfile(), {Serializing Qt Data Types} +*/ + +QDataStream &operator<<(QDataStream &s, const QColorSpace &image) +{ + s << image.iccProfile(); + return s; +} + +/*! + \fn QDataStream &operator>>(QDataStream &stream, QColorSpace &colorSpace) + \relates QColorSpace + + Reads a color space from the given \a stream and stores it in the given + \a colorSpace. + + \sa QColorSpace::fromIccProfile(), {Serializing Qt Data Types} +*/ + +QDataStream &operator>>(QDataStream &s, QColorSpace &colorSpace) +{ + QByteArray iccProfile; + s >> iccProfile; + colorSpace = QColorSpace::fromIccProfile(iccProfile); + return s; +} +#endif // QT_NO_DATASTREAM + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace) +{ + QDebugStateSaver saver(dbg); + dbg.nospace(); + dbg << "QColorSpace("; + dbg << colorSpace.colorSpaceId() << ", " << colorSpace.gamut() << ", " << colorSpace.transferFunction(); + dbg << ", gamma=" << colorSpace.gamma(); + dbg << ')'; + return dbg; +} +#endif + +QT_END_NAMESPACE diff --git a/src/gui/painting/qcolorspace.h b/src/gui/painting/qcolorspace.h new file mode 100644 index 0000000000..923546ec6f --- /dev/null +++ b/src/gui/painting/qcolorspace.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOLORSPACE_H +#define QCOLORSPACE_H + +#include <QtGui/qtguiglobal.h> +#include <QtGui/qcolortransform.h> +#include <QtCore/qshareddata.h> + +QT_BEGIN_NAMESPACE + +class QColorSpacePrivate; + +class Q_GUI_EXPORT QColorSpace +{ + Q_GADGET +public: + enum ColorSpaceId { + Undefined = 0, + Unknown = 1, + SRgb, + SRgbLinear, + AdobeRgb, + DisplayP3, + ProPhotoRgb, + Bt2020, + }; + Q_ENUM(ColorSpaceId) + enum class Gamut { + Custom = 0, + SRgb, + AdobeRgb, + DciP3D65, + ProPhotoRgb, + Bt2020, + }; + Q_ENUM(Gamut) + enum class TransferFunction { + Custom = 0, + Linear, + Gamma, + SRgb, + ProPhotoRgb, + Bt2020, + }; + Q_ENUM(TransferFunction) + + QColorSpace(ColorSpaceId colorSpaceId = Undefined); + QColorSpace(Gamut gamut, TransferFunction fun, float gamma = 0.0f); + QColorSpace(Gamut gamut, float gamma); + ~QColorSpace(); + + QColorSpace(QColorSpace &&colorSpace); + QColorSpace(const QColorSpace &colorSpace); + QColorSpace &operator=(QColorSpace &&colorSpace); + QColorSpace &operator=(const QColorSpace &colorSpace); + + ColorSpaceId colorSpaceId() const noexcept; + Gamut gamut() const noexcept; + TransferFunction transferFunction() const noexcept; + float gamma() const noexcept; + + bool isValid() const noexcept; + + friend Q_GUI_EXPORT bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2); + friend inline bool operator!=(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2); + + static QColorSpace fromIccProfile(const QByteArray &iccProfile); + QByteArray iccProfile() const; + + QColorTransform transformationToColorSpace(const QColorSpace &colorspace) const; + + QColorSpacePrivate *d_func(); + inline const QColorSpacePrivate *d_func() const { return d_ptr.constData(); } + +private: + friend class QColorSpacePrivate; + QExplicitlySharedDataPointer<QColorSpacePrivate> d_ptr; +}; + +bool Q_GUI_EXPORT operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2); +inline bool operator!=(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2) +{ + return !(colorSpace1 == colorSpace2); +} + +// QColorSpace stream functions +#if !defined(QT_NO_DATASTREAM) +Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QColorSpace &); +Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QColorSpace &); +#endif + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug, const QColorSpace &); +#endif + +QT_END_NAMESPACE + +#endif // QCOLORSPACE_P_H diff --git a/src/gui/painting/qcolorspace_p.h b/src/gui/painting/qcolorspace_p.h new file mode 100644 index 0000000000..91107a9a89 --- /dev/null +++ b/src/gui/painting/qcolorspace_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOLORSPACE_P_H +#define QCOLORSPACE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qcolorspace.h" +#include "qcolormatrix_p.h" +#include "qcolortrc_p.h" +#include "qcolortrclut_p.h" + +#include <QtCore/qshareddata.h> + +QT_BEGIN_NAMESPACE + +class QColorSpacePrivate : public QSharedData +{ +public: + QColorSpacePrivate(); + QColorSpacePrivate(QColorSpace::ColorSpaceId colorSpaceId); + QColorSpacePrivate(QColorSpace::Gamut gamut, QColorSpace::TransferFunction fun, float gamma); + QColorSpacePrivate(const QColorSpacePrivate &other) = default; + QColorSpacePrivate &operator=(const QColorSpacePrivate &other) = default; + + void initialize(); + void setToXyzMatrix(); + void setTransferFunction(); + bool identifyColorSpace(); + QColorTransform transformationToColorSpace(const QColorSpacePrivate *out) const; + + QColorSpace::ColorSpaceId id; + QColorSpace::Gamut gamut; + QColorSpace::TransferFunction transferFunction; + float gamma; + QColorVector whitePoint; + + QColorTrc trc[3]; + QColorMatrix toXyz; + + QString description; + QByteArray iccProfile; + + mutable QSharedPointer<QColorTrcLut> lut[3]; + mutable QAtomicInt lutsGenerated; +}; + +QT_END_NAMESPACE + +#endif // QCOLORSPACE_P_H diff --git a/src/gui/painting/qcolortransferfunction_p.h b/src/gui/painting/qcolortransferfunction_p.h new file mode 100644 index 0000000000..fd7cfa2b2b --- /dev/null +++ b/src/gui/painting/qcolortransferfunction_p.h @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOLORTRANSFERFUNCTION_P_H +#define QCOLORTRANSFERFUNCTION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/private/qtguiglobal_p.h> + +#include <cmath> + +QT_BEGIN_NAMESPACE + +// Defines a ICC parametric curve type 4 +class Q_GUI_EXPORT QColorTransferFunction +{ +public: + QColorTransferFunction() noexcept + : m_a(1.0f), m_b(0.0f), m_c(1.0f), m_d(0.0f), m_e(0.0f), m_f(0.0f), m_g(1.0f), m_flags(0) + { } + QColorTransferFunction(float a, float b, float c, float d, float e, float f, float g) noexcept + : m_a(a), m_b(b), m_c(c), m_d(d), m_e(e), m_f(f), m_g(g), m_flags(0) + { } + + bool isGamma() const + { + updateHints(); + return m_flags & quint32(Hints::IsGamma); + } + bool isLinear() const + { + updateHints(); + return m_flags & quint32(Hints::IsLinear); + } + bool isSRgb() const + { + updateHints(); + return m_flags & quint32(Hints::IsSRgb); + } + + float apply(float x) const + { + if (x < m_d) + return m_c * x + m_f; + else + return std::pow(m_a * x + m_b, m_g) + m_e; + } + + QColorTransferFunction inverted() const + { + float a, b, c, d, e, f, g; + + d = m_c * m_d + m_f; + + if (!qFuzzyIsNull(m_c)) { + c = 1.0f / m_c; + f = -m_f / m_c; + } else { + c = 0.0f; + f = 0.0f; + } + + if (!qFuzzyIsNull(m_a) && !qFuzzyIsNull(m_g)) { + a = std::pow(1.0f / m_a, m_g); + b = -a * m_e; + e = -m_b / m_a; + g = 1.0f / m_g; + } else { + a = 0.0f; + b = 0.0f; + e = 1.0f; + g = 1.0f; + } + + return QColorTransferFunction(a, b, c, d, e, f, g); + } + + // A few predefined curves: + static QColorTransferFunction fromGamma(float gamma) + { + return QColorTransferFunction(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, gamma); + } + static QColorTransferFunction fromSRgb() + { + return QColorTransferFunction(1.0f / 1.055f, 0.055f / 1.055f, 1.0f / 12.92f, 0.04045f, 0.0f, 0.0f, 2.4f); + } + static QColorTransferFunction fromBt2020() + { + return QColorTransferFunction(1.0f / 1.0993f, 0.0993f / 1.0993f, 1.0f / 4.5f, 0.08145f, 0.0f, 0.0f, 2.2f); + } + static QColorTransferFunction fromProPhotoRgb() + { + return QColorTransferFunction(1.0f, 0.0f, 1.0f / 16.0f, 16.0f / 512.0f, 0.0f, 0.0f, 1.8f); + } + bool matches(const QColorTransferFunction &o) const + { + return paramCompare(m_a, o.m_a) && paramCompare(m_b, o.m_b) + && paramCompare(m_c, o.m_c) && paramCompare(m_d, o.m_d) + && paramCompare(m_e, o.m_e) && paramCompare(m_f, o.m_f) + && paramCompare(m_g, o.m_g); + } + friend inline bool operator==(const QColorTransferFunction &f1, const QColorTransferFunction &f2); + friend inline bool operator!=(const QColorTransferFunction &f1, const QColorTransferFunction &f2); + + float m_a; + float m_b; + float m_c; + float m_d; + float m_e; + float m_f; + float m_g; + +private: + static inline bool paramCompare(float p1, float p2) + { + // Much fuzzier than fuzzy compare. + // It tries match parameters that has been passed through a 8.8 + // fixed point form. + return (qAbs(p1 - p2) <= (1.0f / 512.0f)); + } + + void updateHints() const + { + if (m_flags & quint32(Hints::Calculated)) + return; + // We do not consider the case with m_d = 1.0f linear or simple, + // since it wouldn't be linear for applyExtended(). + bool simple = paramCompare(m_a, 1.0f) && paramCompare(m_b, 0.0f) + && paramCompare(m_d, 0.0f) + && paramCompare(m_e, 0.0f); + if (simple) { + m_flags |= quint32(Hints::IsGamma); + if (qFuzzyCompare(m_g, 1.0f)) + m_flags |= quint32(Hints::IsLinear); + } else { + if (*this == fromSRgb()) + m_flags |= quint32(Hints::IsSRgb); + } + m_flags |= quint32(Hints::Calculated); + } + enum class Hints : quint32 { + Calculated = 1, + IsGamma = 2, + IsLinear = 4, + IsSRgb = 8 + }; + mutable quint32 m_flags; +}; + +inline bool operator==(const QColorTransferFunction &f1, const QColorTransferFunction &f2) +{ + return f1.matches(f2); +} +inline bool operator!=(const QColorTransferFunction &f1, const QColorTransferFunction &f2) +{ + return !f1.matches(f2); +} + +QT_END_NAMESPACE + +#endif // QCOLORTRANSFERFUNCTION_P_H diff --git a/src/gui/painting/qcolortransfertable_p.h b/src/gui/painting/qcolortransfertable_p.h new file mode 100644 index 0000000000..c8b2f7bd92 --- /dev/null +++ b/src/gui/painting/qcolortransfertable_p.h @@ -0,0 +1,245 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOLORTRANSFERTABLE_P_H +#define QCOLORTRANSFERTABLE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/private/qtguiglobal_p.h> +#include "qcolortransferfunction_p.h" + +#include <QVector> +#include <cmath> + +QT_BEGIN_NAMESPACE + +// Defines either an ICC TRC 'curve' or a lut8/lut16 A or B table +class Q_GUI_EXPORT QColorTransferTable +{ +public: + QColorTransferTable() noexcept + : m_tableSize(0) + { } + QColorTransferTable(uint32_t size, const QVector<uint8_t> &table) noexcept + : m_tableSize(size) + , m_table8(table) + { } + QColorTransferTable(uint32_t size, const QVector<uint16_t> &table) noexcept + : m_tableSize(size) + , m_table16(table) + { } + + bool isValid() const + { + if (m_tableSize < 2) + return false; + +#if !defined(QT_NO_DEBUG) + // The table must describe an injective curve: + if (!m_table8.isEmpty()) { + uint8_t val = 0; + for (uint i = 0; i < m_tableSize; ++i) { + Q_ASSERT(m_table8[i] >= val); + val = m_table8[i]; + } + } + if (!m_table16.isEmpty()) { + uint16_t val = 0; + for (uint i = 0; i < m_tableSize; ++i) { + Q_ASSERT(m_table16[i] >= val); + val = m_table16[i]; + } + } +#endif + return !m_table8.isEmpty() || !m_table16.isEmpty(); + } + + float apply(float x) const + { + x = std::min(std::max(x, 0.0f), 1.0f); + x *= m_tableSize - 1; + uint32_t lo = (int)std::floor(x); + uint32_t hi = std::min(lo + 1, m_tableSize); + float frac = x - lo; + if (!m_table16.isEmpty()) + return (m_table16[lo] * (1.0f - frac) + m_table16[hi] * frac) * (1.0f/65535.0f); + if (!m_table8.isEmpty()) + return (m_table8[lo] * (1.0f - frac) + m_table8[hi] * frac) * (1.0f/255.0f); + return x; + } + + // Apply inverse, optimized by giving a previous result a value < x. + float applyInverse(float x, float resultLargerThan = 0.0f) const + { + Q_ASSERT(resultLargerThan >= 0.0f && resultLargerThan <= 1.0f); + if (x <= 0.0f) + return 0.0f; + if (x >= 1.0f) + return 1.0f; + if (!m_table16.isEmpty()) { + float v = x * 65535.0f; + uint i = std::floor(resultLargerThan * (m_tableSize - 1)) + 1; + for ( ; i < m_tableSize; ++i) { + if (m_table16[i] > v) + break; + } + if (i >= m_tableSize - 1) + return 1.0f; + float y1 = m_table16[i - 1]; + float y2 = m_table16[i]; + Q_ASSERT(x >= y1 && x < y2); + float fr = (v - y1) / (y2 - y1); + return (i + fr) * (1.0f / (m_tableSize - 1)); + + } + if (!m_table8.isEmpty()) { + float v = x * 255.0f; + uint i = std::floor(resultLargerThan * (m_tableSize - 1)) + 1; + for ( ; i < m_tableSize; ++i) { + if (m_table8[i] > v) + break; + } + if (i >= m_tableSize - 1) + return 1.0f; + float y1 = m_table8[i - 1]; + float y2 = m_table8[i]; + Q_ASSERT(x >= y1 && x < y2); + float fr = (v - y1) / (y2 - y1); + return (i + fr) * (1.0f / (m_tableSize - 1)); + } + return x; + } + + bool asColorTransferFunction(QColorTransferFunction *transferFn) + { + Q_ASSERT(isValid()); + Q_ASSERT(transferFn); + if (!m_table8.isEmpty() && (m_table8[0] != 0 || m_table8[m_tableSize - 1] != 255)) + return false; + if (!m_table16.isEmpty() && (m_table16[0] != 0 || m_table16[m_tableSize - 1] != 65535)) + return false; + if (m_tableSize == 2) { + *transferFn = QColorTransferFunction(); // Linear + return true; + } + // The following heuristics are based on those from Skia: + if (m_tableSize == 26 && !m_table16.isEmpty()) { + // code.facebook.com/posts/411525055626587/under-the-hood-improving-facebook-photos + if (m_table16[6] != 3062) + return false; + if (m_table16[12] != 12824) + return false; + if (m_table16[18] != 31237) + return false; + *transferFn = QColorTransferFunction::fromSRgb(); + return true; + } + if (m_tableSize == 1024 && !m_table16.isEmpty()) { + // HP and Canon sRGB gamma tables: + if (m_table16[257] != 3366) + return false; + if (m_table16[513] != 14116) + return false; + if (m_table16[768] != 34318) + return false; + *transferFn = QColorTransferFunction::fromSRgb(); + return true; + } + if (m_tableSize == 4096 && !m_table16.isEmpty()) { + // Nikon, Epson, and lcms2 sRGB gamma tables: + if (m_table16[515] != 960) + return false; + if (m_table16[1025] != 3342) + return false; + if (m_table16[2051] != 14079) + return false; + *transferFn = QColorTransferFunction::fromSRgb(); + return true; + } + return false; + } + friend inline bool operator!=(const QColorTransferTable &t1, const QColorTransferTable &t2); + friend inline bool operator==(const QColorTransferTable &t1, const QColorTransferTable &t2); + + uint32_t m_tableSize; + QVector<uint8_t> m_table8; + QVector<uint16_t> m_table16; +}; + +inline bool operator!=(const QColorTransferTable &t1, const QColorTransferTable &t2) +{ + if (t1.m_tableSize != t2.m_tableSize) + return true; + if (t1.m_table8.isEmpty() != t2.m_table8.isEmpty()) + return true; + if (t1.m_table16.isEmpty() != t2.m_table16.isEmpty()) + return true; + if (!t1.m_table8.isEmpty()) { + for (quint32 i = 0; i < t1.m_tableSize; ++i) { + if (t1.m_table8[i] != t2.m_table8[i]) + return true; + } + } + if (!t1.m_table16.isEmpty()) { + for (quint32 i = 0; i < t1.m_tableSize; ++i) { + if (t1.m_table16[i] != t2.m_table16[i]) + return true; + } + } + return false; +} + +inline bool operator==(const QColorTransferTable &t1, const QColorTransferTable &t2) +{ + return !(t1 != t2); +} + +QT_END_NAMESPACE + +#endif // QCOLORTRANSFERTABLE_P_H diff --git a/src/gui/painting/qcolortransform.cpp b/src/gui/painting/qcolortransform.cpp new file mode 100644 index 0000000000..b677c4b36b --- /dev/null +++ b/src/gui/painting/qcolortransform.cpp @@ -0,0 +1,679 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qcolortransform.h" +#include "qcolortransform_p.h" + +#include "qcolormatrix_p.h" +#include "qcolorspace_p.h" +#include "qcolortrc_p.h" +#include "qcolortrclut_p.h" + +#include <QtCore/qatomic.h> +#include <QtCore/qmath.h> +#include <QtGui/qcolor.h> +#include <QtGui/qtransform.h> +#include <QtCore/private/qsimd_p.h> + +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +QColorTrcLut *lutFromTrc(const QColorTrc &trc) +{ + if (trc.m_type == QColorTrc::Type::Table) + return QColorTrcLut::fromTransferTable(trc.m_table); + if (trc.m_type == QColorTrc::Type::Function) + return QColorTrcLut::fromTransferFunction(trc.m_fun); + qWarning() << "TRC uninitialized"; + return nullptr; +} + +void QColorTransformPrivate::updateLutsIn() const +{ + if (colorSpaceIn->lutsGenerated.loadAcquire()) + return; + for (int i = 0; i < 3; ++i) { + if (!colorSpaceIn->trc[i].isValid()) + return; + } + + if (colorSpaceIn->trc[0] == colorSpaceIn->trc[1] && colorSpaceIn->trc[0] == colorSpaceIn->trc[2]) { + colorSpaceIn->lut[0].reset(lutFromTrc(colorSpaceIn->trc[0])); + colorSpaceIn->lut[1] = colorSpaceIn->lut[0]; + colorSpaceIn->lut[2] = colorSpaceIn->lut[0]; + } else { + for (int i = 0; i < 3; ++i) + colorSpaceIn->lut[i].reset(lutFromTrc(colorSpaceIn->trc[i])); + } + + colorSpaceIn->lutsGenerated.storeRelease(1); +} + +void QColorTransformPrivate::updateLutsOut() const +{ + if (colorSpaceOut->lutsGenerated.loadAcquire()) + return; + for (int i = 0; i < 3; ++i) { + if (!colorSpaceOut->trc[i].isValid()) + return; + } + + if (colorSpaceOut->trc[0] == colorSpaceOut->trc[1] && colorSpaceOut->trc[0] == colorSpaceOut->trc[2]) { + colorSpaceOut->lut[0].reset(lutFromTrc(colorSpaceOut->trc[0])); + colorSpaceOut->lut[1] = colorSpaceOut->lut[0]; + colorSpaceOut->lut[2] = colorSpaceOut->lut[0]; + } else { + for (int i = 0; i < 3; ++i) + colorSpaceOut->lut[i].reset(lutFromTrc(colorSpaceOut->trc[i])); + } + + colorSpaceOut->lutsGenerated.storeRelease(1); +} + +/*! + \class QColorTransform + \brief The QColorTransform class is a transformation between color spaces. + \since 5.14 + + \ingroup painting + \ingroup appearance + \inmodule QtGui + + QColorTransform is an instantiation of a transformation between color spaces. + It can be applied on color and pixels to convert them from one color space to + another. + + Setting up a QColorTransform takes some preprocessing, so keeping around + QColorTransforms that you need often is recommended, instead of generating + them on the fly. +*/ + + +QColorTransform::~QColorTransform() noexcept +{ +} + +/*! + Applies the color transformation on the QRgb value \a argb. + + The input should be opaque or unpremultiplied. +*/ +QRgb QColorTransform::map(const QRgb &argb) const +{ + if (!d_ptr) + return argb; + Q_D(const QColorTransform); + constexpr float f = 1.0f / 255.0f; + QColorVector c = { qRed(argb) * f, qGreen(argb) * f, qBlue(argb) * f }; + c.x = d->colorSpaceIn->trc[0].apply(c.x); + c.y = d->colorSpaceIn->trc[1].apply(c.y); + c.z = d->colorSpaceIn->trc[2].apply(c.z); + c = d->colorMatrix.map(c); + c.x = std::max(0.0f, std::min(1.0f, c.x)); + c.y = std::max(0.0f, std::min(1.0f, c.y)); + c.z = std::max(0.0f, std::min(1.0f, c.z)); + if (d->colorSpaceOut->lutsGenerated.loadAcquire()) { + c.x = d->colorSpaceOut->lut[0]->fromLinear(c.x); + c.y = d->colorSpaceOut->lut[1]->fromLinear(c.y); + c.z = d->colorSpaceOut->lut[2]->fromLinear(c.z); + } else { + c.x = d->colorSpaceOut->trc[0].applyInverse(c.x); + c.y = d->colorSpaceOut->trc[1].applyInverse(c.y); + c.z = d->colorSpaceOut->trc[2].applyInverse(c.z); + } + + return qRgba(c.x * 255 + 0.5f, c.y * 255 + 0.5f, c.z * 255 + 0.5f, qAlpha(argb)); +} + +/*! + Applies the color transformation on the QRgba64 value \a rgba64. + + The input should be opaque or unpremultiplied. +*/ +QRgba64 QColorTransform::map(const QRgba64 &rgba64) const +{ + if (!d_ptr) + return rgba64; + Q_D(const QColorTransform); + constexpr float f = 1.0f / 65535.0f; + QColorVector c = { rgba64.red() * f, rgba64.green() * f, rgba64.blue() * f }; + c.x = d->colorSpaceIn->trc[0].apply(c.x); + c.y = d->colorSpaceIn->trc[1].apply(c.y); + c.z = d->colorSpaceIn->trc[2].apply(c.z); + c = d->colorMatrix.map(c); + c.x = std::max(0.0f, std::min(1.0f, c.x)); + c.y = std::max(0.0f, std::min(1.0f, c.y)); + c.z = std::max(0.0f, std::min(1.0f, c.z)); + if (d->colorSpaceOut->lutsGenerated.loadAcquire()) { + c.x = d->colorSpaceOut->lut[0]->fromLinear(c.x); + c.y = d->colorSpaceOut->lut[1]->fromLinear(c.y); + c.z = d->colorSpaceOut->lut[2]->fromLinear(c.z); + } else { + c.x = d->colorSpaceOut->trc[0].applyInverse(c.x); + c.y = d->colorSpaceOut->trc[1].applyInverse(c.y); + c.z = d->colorSpaceOut->trc[2].applyInverse(c.z); + } + + return QRgba64::fromRgba64(c.x * 65535, c.y * 65535, c.z * 65535, rgba64.alpha()); +} + +/*! + Applies the color transformation on the QColor value \a color. + +*/ +QColor QColorTransform::map(const QColor &color) const +{ + if (!d_ptr) + return color; + Q_D(const QColorTransform); + QColorVector c = { (float)color.redF(), (float)color.greenF(), (float)color.blueF() }; + c.x = d->colorSpaceIn->trc[0].apply(c.x); + c.y = d->colorSpaceIn->trc[1].apply(c.y); + c.z = d->colorSpaceIn->trc[2].apply(c.z); + c = d->colorMatrix.map(c); + if (d_ptr->colorSpaceOut->lutsGenerated.loadAcquire()) { + c.x = d->colorSpaceOut->lut[0]->fromLinear(c.x); + c.y = d->colorSpaceOut->lut[1]->fromLinear(c.y); + c.z = d->colorSpaceOut->lut[2]->fromLinear(c.z); + } else { + c.x = d->colorSpaceOut->trc[0].applyInverse(c.x); + c.y = d->colorSpaceOut->trc[1].applyInverse(c.y); + c.z = d->colorSpaceOut->trc[2].applyInverse(c.z); + } + QColor out; + out.setRgbF(c.x, c.y, c.z, color.alphaF()); + return out; +} + +// Optimized sub-routines for fast block based conversion: + +static void applyMatrix(QColorVector *buffer, const qsizetype len, const QColorMatrix &colorMatrix) +{ +#if defined(__SSE2__) + const __m128 minV = _mm_set1_ps(0.0f); + const __m128 maxV = _mm_set1_ps(1.0f); + const __m128 xMat = _mm_loadu_ps(&colorMatrix.r.x); + const __m128 yMat = _mm_loadu_ps(&colorMatrix.g.x); + const __m128 zMat = _mm_loadu_ps(&colorMatrix.b.x); + for (qsizetype j = 0; j < len; ++j) { + __m128 c = _mm_loadu_ps(&buffer[j].x); + __m128 cx = _mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 cy = _mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)); + __m128 cz = _mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)); + cx = _mm_mul_ps(cx, xMat); + cy = _mm_mul_ps(cy, yMat); + cz = _mm_mul_ps(cz, zMat); + cx = _mm_add_ps(cx, cy); + cx = _mm_add_ps(cx, cz); + // Clamp: + cx = _mm_min_ps(cx, maxV); + cx = _mm_max_ps(cx, minV); + _mm_storeu_ps(&buffer[j].x, cx); + } +#else + for (int j = 0; j < len; ++j) { + const QColorVector cv = colorMatrix.map(buffer[j]); + buffer[j].x = std::max(0.0f, std::min(1.0f, cv.x)); + buffer[j].y = std::max(0.0f, std::min(1.0f, cv.y)); + buffer[j].z = std::max(0.0f, std::min(1.0f, cv.z)); + } +#endif +} + +template<typename T> +static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr); +template<typename T> +static void loadUnpremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr); + +#if defined(__SSE2__) +// Load to [0-alpha] in 4x32 SIMD +template<typename T> +static inline void loadP(const T &p, __m128i &v); + +template<> +inline void loadP<QRgb>(const QRgb &p, __m128i &v) +{ + v = _mm_cvtsi32_si128(p); +#if defined(__SSE4_1__) + v = _mm_cvtepu8_epi32(v); +#else + v = _mm_unpacklo_epi8(v, _mm_setzero_si128()); + v = _mm_unpacklo_epi16(v, _mm_setzero_si128()); +#endif +} + +template<> +inline void loadP<QRgba64>(const QRgba64 &p, __m128i &v) +{ + v = _mm_loadl_epi64((const __m128i *)&p); +#if defined(__SSE4_1__) + v = _mm_cvtepu16_epi32(v); +#else + v = _mm_unpacklo_epi16(v, _mm_setzero_si128()); +#endif + // Shuffle to ARGB as the template below expects it + v = _mm_shuffle_epi32(v, _MM_SHUFFLE(3, 0, 1, 2)); +} + +template<typename T> +static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr) +{ + const __m128 v4080 = _mm_set1_ps(4080.f); + const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256)); + for (qsizetype i = 0; i < len; ++i) { + __m128i v; + loadP<T>(src[i], v); + __m128 vf = _mm_cvtepi32_ps(v); + // Approximate 1/a: + __m128 va = _mm_shuffle_ps(vf, vf, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 via = _mm_rcp_ps(va); + via = _mm_sub_ps(_mm_add_ps(via, via), _mm_mul_ps(via, _mm_mul_ps(via, va))); + // v * (1/a) + vf = _mm_mul_ps(vf, via); + + // Handle zero alpha + __m128 vAlphaMask = _mm_cmpeq_ps(va, _mm_set1_ps(0.0f)); + vf = _mm_andnot_ps(vAlphaMask, vf); + + // LUT + v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080)); + const int ridx = _mm_extract_epi16(v, 4); + const int gidx = _mm_extract_epi16(v, 2); + const int bidx = _mm_extract_epi16(v, 0); + v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx], 0); + v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx], 2); + v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx], 4); + vf = _mm_mul_ps(_mm_cvtepi32_ps(v), iFF00); + + _mm_storeu_ps(&buffer[i].x, vf); + } +} + +// Load to [0-4080] in 4x32 SIMD +template<typename T> +static inline void loadPU(const T &p, __m128i &v); + +template<> +inline void loadPU<QRgb>(const QRgb &p, __m128i &v) +{ + v = _mm_cvtsi32_si128(p); +#if defined(__SSE4_1__) + v = _mm_cvtepu8_epi32(v); +#else + v = _mm_unpacklo_epi8(v, _mm_setzero_si128()); + v = _mm_unpacklo_epi16(v, _mm_setzero_si128()); +#endif + v = _mm_slli_epi32(v, 4); +} + +template<> +inline void loadPU<QRgba64>(const QRgba64 &p, __m128i &v) +{ + v = _mm_loadl_epi64((const __m128i *)&p); + v = _mm_sub_epi16(v, _mm_srli_epi16(v, 8)); +#if defined(__SSE4_1__) + v = _mm_cvtepu16_epi32(v); +#else + v = _mm_unpacklo_epi16(v, _mm_setzero_si128()); +#endif + v = _mm_srli_epi32(v, 4); + // Shuffle to ARGB as the template below expects it + v = _mm_shuffle_epi32(v, _MM_SHUFFLE(3, 0, 1, 2)); +} + +template<typename T> +void loadUnpremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr) +{ + const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256)); + for (qsizetype i = 0; i < len; ++i) { + __m128i v; + loadPU<T>(src[i], v); + const int ridx = _mm_extract_epi16(v, 4); + const int gidx = _mm_extract_epi16(v, 2); + const int bidx = _mm_extract_epi16(v, 0); + v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx], 0); + v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx], 2); + v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx], 4); + __m128 vf = _mm_mul_ps(_mm_cvtepi32_ps(v), iFF00); + _mm_storeu_ps(&buffer[i].x, vf); + } +} + +#else +template<> +void loadPremultiplied<QRgb>(QColorVector *buffer, const QRgb *src, const qsizetype len, const QColorTransformPrivate *d_ptr) +{ + for (qsizetype i = 0; i < len; ++i) { + const uint p = src[i]; + const int a = qAlpha(p); + if (a) { + const float ia = 4080.0f / a; + const int ridx = int(qRed(p) * ia + 0.5f); + const int gidx = int(qGreen(p) * ia + 0.5f); + const int bidx = int(qBlue(p) * ia + 0.5f); + buffer[i].x = d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx] * (1.0f / (255 * 256)); + buffer[i].y = d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx] * (1.0f / (255 * 256)); + buffer[i].z = d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx] * (1.0f / (255 * 256)); + } else { + buffer[i].x = buffer[i].y = buffer[i].z = 0.0f; + } + } +} + +template<> +void loadPremultiplied<QRgba64>(QColorVector *buffer, const QRgba64 *src, const qsizetype len, const QColorTransformPrivate *d_ptr) +{ + for (qsizetype i = 0; i < len; ++i) { + const QRgba64 &p = src[i]; + const int a = p.alpha(); + if (a) { + const float ia = 4080.0f / a; + const int ridx = int(p.red() * ia + 0.5f); + const int gidx = int(p.green() * ia + 0.5f); + const int bidx = int(p.blue() * ia + 0.5f); + buffer[i].x = d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx] * (1.0f / (255 * 256)); + buffer[i].y = d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx] * (1.0f / (255 * 256)); + buffer[i].z = d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx] * (1.0f / (255 * 256)); + } else { + buffer[i].x = buffer[i].y = buffer[i].z = 0.0f; + } + } +} + +template<> +void loadUnpremultiplied<QRgb>(QColorVector *buffer, const QRgb *src, const qsizetype len, const QColorTransformPrivate *d_ptr) +{ + for (qsizetype i = 0; i < len; ++i) { + const uint p = src[i]; + buffer[i].x = d_ptr->colorSpaceIn->lut[0]->u8ToLinearF32(qRed(p)); + buffer[i].y = d_ptr->colorSpaceIn->lut[1]->u8ToLinearF32(qGreen(p)); + buffer[i].z = d_ptr->colorSpaceIn->lut[2]->u8ToLinearF32(qBlue(p)); + } +} + +template<> +void loadUnpremultiplied<QRgba64>(QColorVector *buffer, const QRgba64 *src, const qsizetype len, const QColorTransformPrivate *d_ptr) +{ + for (qsizetype i = 0; i < len; ++i) { + const QRgba64 &p = src[i]; + buffer[i].x = d_ptr->colorSpaceIn->lut[0]->u16ToLinearF32(p.red()); + buffer[i].y = d_ptr->colorSpaceIn->lut[1]->u16ToLinearF32(p.green()); + buffer[i].z = d_ptr->colorSpaceIn->lut[2]->u16ToLinearF32(p.blue()); + } +} +#endif + +static void storePremultiplied(QRgb *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len, + const QColorTransformPrivate *d_ptr) +{ +#if defined(__SSE2__) + const __m128 v4080 = _mm_set1_ps(4080.f); + const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256)); + for (qsizetype i = 0; i < len; ++i) { + const int a = qAlpha(src[i]); + __m128 vf = _mm_loadu_ps(&buffer[i].x); + __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080)); + __m128 va = _mm_set1_ps(a); + va = _mm_mul_ps(va, iFF00); + const int ridx = _mm_extract_epi16(v, 0); + const int gidx = _mm_extract_epi16(v, 2); + const int bidx = _mm_extract_epi16(v, 4); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], 4); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], 2); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], 0); + vf = _mm_cvtepi32_ps(v); + vf = _mm_mul_ps(vf, va); + v = _mm_cvtps_epi32(vf); + v = _mm_packs_epi32(v, v); + v = _mm_insert_epi16(v, a, 3); + v = _mm_packus_epi16(v, v); + dst[i] = _mm_cvtsi128_si32(v); + } +#else + for (qsizetype i = 0; i < len; ++i) { + const int a = qAlpha(src[i]); + const float fa = a / (255.0f * 256.0f); + const float r = d_ptr->colorSpaceOut->lut[0]->m_fromLinear[int(buffer[i].x * 4080.0f + 0.5f)]; + const float g = d_ptr->colorSpaceOut->lut[1]->m_fromLinear[int(buffer[i].y * 4080.0f + 0.5f)]; + const float b = d_ptr->colorSpaceOut->lut[2]->m_fromLinear[int(buffer[i].z * 4080.0f + 0.5f)]; + dst[i] = qRgba(r * fa + 0.5f, g * fa + 0.5f, b * fa + 0.5f, a); + } +#endif +} + +static void storeUnpremultiplied(QRgb *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len, + const QColorTransformPrivate *d_ptr) +{ +#if defined(__SSE2__) + const __m128 v4080 = _mm_set1_ps(4080.f); + for (qsizetype i = 0; i < len; ++i) { + const int a = qAlpha(src[i]); + __m128 vf = _mm_loadu_ps(&buffer[i].x); + __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080)); + const int ridx = _mm_extract_epi16(v, 0); + const int gidx = _mm_extract_epi16(v, 2); + const int bidx = _mm_extract_epi16(v, 4); + v = _mm_setzero_si128(); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], 2); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], 1); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], 0); + v = _mm_add_epi16(v, _mm_set1_epi16(0x80)); + v = _mm_srli_epi16(v, 8); + v = _mm_insert_epi16(v, a, 3); + v = _mm_packus_epi16(v, v); + dst[i] = _mm_cvtsi128_si32(v); + } +#else + for (qsizetype i = 0; i < len; ++i) { + const int r = d_ptr->colorSpaceOut->lut[0]->u8FromLinearF32(buffer[i].x); + const int g = d_ptr->colorSpaceOut->lut[1]->u8FromLinearF32(buffer[i].y); + const int b = d_ptr->colorSpaceOut->lut[2]->u8FromLinearF32(buffer[i].z); + dst[i] = (src[i] & 0xff000000) | (r << 16) | (g << 8) | (b << 0); + } +#endif +} + +static void storeOpaque(QRgb *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len, + const QColorTransformPrivate *d_ptr) +{ + Q_UNUSED(src); +#if defined(__SSE2__) + const __m128 v4080 = _mm_set1_ps(4080.f); + for (qsizetype i = 0; i < len; ++i) { + __m128 vf = _mm_loadu_ps(&buffer[i].x); + __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080)); + const int ridx = _mm_extract_epi16(v, 0); + const int gidx = _mm_extract_epi16(v, 2); + const int bidx = _mm_extract_epi16(v, 4); + v = _mm_setzero_si128(); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], 2); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], 1); + v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], 0); + v = _mm_add_epi16(v, _mm_set1_epi16(0x80)); + v = _mm_srli_epi16(v, 8); + v = _mm_insert_epi16(v, 255, 3); + v = _mm_packus_epi16(v, v); + dst[i] = _mm_cvtsi128_si32(v); + } +#else + for (qsizetype i = 0; i < len; ++i) { + const int r = d_ptr->colorSpaceOut->lut[0]->u8FromLinearF32(buffer[i].x); + const int g = d_ptr->colorSpaceOut->lut[1]->u8FromLinearF32(buffer[i].y); + const int b = d_ptr->colorSpaceOut->lut[2]->u8FromLinearF32(buffer[i].z); + dst[i] = 0xff000000 | (r << 16) | (g << 8) | (b << 0); + } +#endif +} + +static void storePremultiplied(QRgba64 *dst, const QRgba64 *src, const QColorVector *buffer, const qsizetype len, + const QColorTransformPrivate *d_ptr) +{ + for (qsizetype i = 0; i < len; ++i) { + const int a = src[i].alpha(); + const float fa = a / (255.0f * 256.0f); + const float r = d_ptr->colorSpaceOut->lut[0]->m_fromLinear[int(buffer[i].x * 4080.0f + 0.5f)]; + const float g = d_ptr->colorSpaceOut->lut[1]->m_fromLinear[int(buffer[i].y * 4080.0f + 0.5f)]; + const float b = d_ptr->colorSpaceOut->lut[2]->m_fromLinear[int(buffer[i].z * 4080.0f + 0.5f)]; + dst[i] = qRgba64(r * fa + 0.5f, g * fa + 0.5f, b * fa + 0.5f, a); + } +} + +static void storeUnpremultiplied(QRgba64 *dst, const QRgba64 *src, const QColorVector *buffer, const qsizetype len, + const QColorTransformPrivate *d_ptr) +{ + for (qsizetype i = 0; i < len; ++i) { + const int r = d_ptr->colorSpaceOut->lut[0]->u16FromLinearF32(buffer[i].x); + const int g = d_ptr->colorSpaceOut->lut[1]->u16FromLinearF32(buffer[i].y); + const int b = d_ptr->colorSpaceOut->lut[2]->u16FromLinearF32(buffer[i].z); + dst[i] = qRgba64(r, g, b, src[i].alpha()); + } +} + +static void storeOpaque(QRgba64 *dst, const QRgba64 *src, const QColorVector *buffer, const qsizetype len, + const QColorTransformPrivate *d_ptr) +{ + Q_UNUSED(src); + for (qsizetype i = 0; i < len; ++i) { + const int r = d_ptr->colorSpaceOut->lut[0]->u16FromLinearF32(buffer[i].x); + const int g = d_ptr->colorSpaceOut->lut[1]->u16FromLinearF32(buffer[i].y); + const int b = d_ptr->colorSpaceOut->lut[2]->u16FromLinearF32(buffer[i].z); + dst[i] = qRgba64(r, g, b, 0xFFFF); + } +} + +static constexpr qsizetype WorkBlockSize = 256; + +template<typename T> +void QColorTransformPrivate::apply(T *dst, const T *src, qsizetype count, TransformFlags flags) const +{ + if (!colorMatrix.isValid()) + return; + + updateLutsIn(); + updateLutsOut(); + + bool doApplyMatrix = (colorMatrix != QColorMatrix::identity()); + + QColorVector buffer[WorkBlockSize]; + qsizetype i = 0; + while (i < count) { + const qsizetype len = qMin(count - i, WorkBlockSize); + if (flags & InputPremultiplied) + loadPremultiplied(buffer, src + i, len, this); + else + loadUnpremultiplied(buffer, src + i, len, this); + + if (doApplyMatrix) + applyMatrix(buffer, len, colorMatrix); + + if (flags & InputOpaque) + storeOpaque(dst + i, src + i, buffer, len, this); + else if (flags & OutputPremultiplied) + storePremultiplied(dst + i, src + i, buffer, len, this); + else + storeUnpremultiplied(dst + i, src + i, buffer, len, this); + + i += len; + } +} + +/*! + \internal + \enum QColorTransformPrivate::TransformFlag + + Defines how the transform is to be applied. + + \value Unpremultiplied The input and output should both be unpremultiplied. + \value InputOpaque The input is guaranteed to be opaque. + \value InputPremultiplied The input is premultiplied. + \value OutputPremultiplied The output should be premultiplied. + \value Premultiplied Both input and output should both be premultiplied. +*/ + +/*! + \internal + Prepares a color transformation for fast application. You do not need to + call this explicitly as it will be called implicitly on the first transforms, but + if you want predictable performance on the first transforms, you can perform it + in advance. + + \sa QColorTransform::map(), apply() +*/ +void QColorTransformPrivate::prepare() +{ + updateLutsIn(); + updateLutsOut(); +} + +/*! + \internal + Applies the color transformation on \a count QRgb pixels starting from + \a src and stores the result in \a dst. + + Thread-safe if prepare() has been called first. + + Assumes unpremultiplied data by default. Set \a flags to change defaults. + + \sa prepare() +*/ +void QColorTransformPrivate::apply(QRgb *dst, const QRgb *src, qsizetype count, TransformFlags flags) const +{ + apply<QRgb>(dst, src, count, flags); +} + +/*! + \internal + Applies the color transformation on \a count QRgba64 pixels starting from + \a src and stores the result in \a dst. + + Thread-safe if prepare() has been called first. + + Assumes unpremultiplied data by default. Set \a flags to change defaults. + + \sa prepare() +*/ +void QColorTransformPrivate::apply(QRgba64 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const +{ + apply<QRgba64>(dst, src, count, flags); +} + + +QT_END_NAMESPACE diff --git a/src/gui/painting/qcolortransform.h b/src/gui/painting/qcolortransform.h new file mode 100644 index 0000000000..9274387b97 --- /dev/null +++ b/src/gui/painting/qcolortransform.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOLORTRANSFORM_H +#define QCOLORTRANSFORM_H + +#include <QtGui/qtguiglobal.h> +#include <QtCore/qsharedpointer.h> +#include <QtGui/qrgb.h> + +QT_BEGIN_NAMESPACE + +class QColor; +class QRgba64; +class QColorSpacePrivate; +class QColorTransformPrivate; + +class Q_GUI_EXPORT QColorTransform +{ +public: + QColorTransform() noexcept : d_ptr(nullptr) { } + ~QColorTransform() noexcept; + QColorTransform(const QColorTransform &colorTransform) noexcept + : d_ptr(colorTransform.d_ptr) + { } + QColorTransform(QColorTransform &&colorTransform) noexcept + : d_ptr(std::move(colorTransform.d_ptr)) + { } + QColorTransform &operator=(const QColorTransform &other) noexcept + { + d_ptr = other.d_ptr; + return *this; + } + QColorTransform &operator=(QColorTransform &&other) noexcept + { + d_ptr = std::move(other.d_ptr); + return *this; + } + + bool isNull() const { return d_ptr.isNull(); } + + QRgb map(const QRgb &argb) const; + QRgba64 map(const QRgba64 &rgba64) const; + QColor map(const QColor &color) const; + +private: + friend class QColorSpace; + friend class QColorSpacePrivate; + friend class QImage; + + Q_DECLARE_PRIVATE(QColorTransform) + QSharedPointer<QColorTransformPrivate> d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QCOLORTRANSFORM_H diff --git a/src/gui/painting/qcolortransform_p.h b/src/gui/painting/qcolortransform_p.h new file mode 100644 index 0000000000..74a1e7fe0a --- /dev/null +++ b/src/gui/painting/qcolortransform_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOLORTRANSFORM_P_H +#define QCOLORTRANSFORM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qcolormatrix_p.h" +#include "qcolorspace_p.h" + +QT_BEGIN_NAMESPACE + +class QColorTransformPrivate +{ +public: + QColorMatrix colorMatrix; + QExplicitlySharedDataPointer<const QColorSpacePrivate> colorSpaceIn; + QExplicitlySharedDataPointer<const QColorSpacePrivate> colorSpaceOut; + + void updateLutsIn() const; + void updateLutsOut() const; + bool simpleGammaCorrection() const; + + void prepare(); + enum TransformFlag { + Unpremultiplied = 0, + InputOpaque = 1, + InputPremultiplied = 2, + OutputPremultiplied = 4, + Premultiplied = (InputPremultiplied | OutputPremultiplied) + }; + Q_DECLARE_FLAGS(TransformFlags, TransformFlag) + + void apply(QRgb *dst, const QRgb *src, qsizetype count, TransformFlags flags = Unpremultiplied) const; + void apply(QRgba64 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags = Unpremultiplied) const; + + template<typename T> + void apply(T *dst, const T *src, qsizetype count, TransformFlags flags) const; +}; + +QT_END_NAMESPACE + +#endif // QCOLORTRANSFORM_P_H diff --git a/src/gui/painting/qcolortrc_p.h b/src/gui/painting/qcolortrc_p.h new file mode 100644 index 0000000000..3a649f3756 --- /dev/null +++ b/src/gui/painting/qcolortrc_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOLORTRC_P_H +#define QCOLORTRC_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/private/qtguiglobal_p.h> +#include "qcolortransferfunction_p.h" +#include "qcolortransfertable_p.h" + +QT_BEGIN_NAMESPACE + + +// Defines an ICC TRC (Tone Reproduction Curve) +class Q_GUI_EXPORT QColorTrc +{ +public: + QColorTrc() noexcept : m_type(Type::Uninitialized) + { } + QColorTrc(const QColorTransferFunction &fun) : m_type(Type::Function), m_fun(fun) + { } + QColorTrc(const QColorTransferTable &table) : m_type(Type::Table), m_table(table) + { } + + enum class Type { + Uninitialized, + Function, + Table + }; + + bool isLinear() const + { + return m_type == Type::Uninitialized || (m_type == Type::Function && m_fun.isLinear()); + } + bool isValid() const + { + return m_type != Type::Uninitialized; + } + float apply(float x) const + { + if (m_type == Type::Table) + return m_table.apply(x); + if (m_type == Type::Function) + return m_fun.apply(x); + return x; + } + + float applyInverse(float x) const + { + if (m_type == Type::Table) + return m_table.applyInverse(x); + if (m_type == Type::Function) + return m_fun.inverted().apply(x); + return x; + } + + friend inline bool operator!=(const QColorTrc &o1, const QColorTrc &o2); + friend inline bool operator==(const QColorTrc &o1, const QColorTrc &o2); + + Type m_type; + QColorTransferFunction m_fun; + QColorTransferTable m_table; +}; + +inline bool operator!=(const QColorTrc &o1, const QColorTrc &o2) +{ + if (o1.m_type != o2.m_type) + return true; + if (o1.m_type == QColorTrc::Type::Function) + return o1.m_fun != o2.m_fun; + if (o1.m_type == QColorTrc::Type::Table) + return o1.m_table != o2.m_table; + return false; +} +inline bool operator==(const QColorTrc &o1, const QColorTrc &o2) +{ + return !(o1 != o2); +} + +QT_END_NAMESPACE + +#endif // QCOLORTRC diff --git a/src/gui/painting/qcolorprofile.cpp b/src/gui/painting/qcolortrclut.cpp index 3b7b0a248b..268d7252b4 100644 --- a/src/gui/painting/qcolorprofile.cpp +++ b/src/gui/painting/qcolortrclut.cpp @@ -37,14 +37,16 @@ ** ****************************************************************************/ -#include "qcolorprofile_p.h" +#include "qcolortrclut_p.h" +#include "qcolortransferfunction_p.h" +#include "qcolortransfertable_p.h" #include <qmath.h> QT_BEGIN_NAMESPACE -QColorProfile *QColorProfile::fromGamma(qreal gamma) +QColorTrcLut *QColorTrcLut::fromGamma(qreal gamma) { - QColorProfile *cp = new QColorProfile; + QColorTrcLut *cp = new QColorTrcLut; for (int i = 0; i <= (255 * 16); ++i) { cp->m_toLinear[i] = ushort(qRound(qPow(i / qreal(255 * 16), gamma) * (255 * 256))); @@ -54,31 +56,28 @@ QColorProfile *QColorProfile::fromGamma(qreal gamma) return cp; } -static qreal srgbToLinear(qreal v) +QColorTrcLut *QColorTrcLut::fromTransferFunction(const QColorTransferFunction &fun) { - const qreal a = 0.055; - if (v <= qreal(0.04045)) - return v / qreal(12.92); - else - return qPow((v + a) / (qreal(1) + a), qreal(2.4)); -} + QColorTrcLut *cp = new QColorTrcLut; + QColorTransferFunction inv = fun.inverted(); -static qreal linearToSrgb(qreal v) -{ - const qreal a = 0.055; - if (v <= qreal(0.0031308)) - return v * qreal(12.92); - else - return (qreal(1) + a) * qPow(v, qreal(1.0 / 2.4)) - a; + for (int i = 0; i <= (255 * 16); ++i) { + cp->m_toLinear[i] = ushort(qRound(fun.apply(i / qreal(255 * 16)) * (255 * 256))); + cp->m_fromLinear[i] = ushort(qRound(inv.apply(i / qreal(255 * 16)) * (255 * 256))); + } + + return cp; } -QColorProfile *QColorProfile::fromSRgb() +QColorTrcLut *QColorTrcLut::fromTransferTable(const QColorTransferTable &table) { - QColorProfile *cp = new QColorProfile; + QColorTrcLut *cp = new QColorTrcLut; + float minInverse = 0.0f; for (int i = 0; i <= (255 * 16); ++i) { - cp->m_toLinear[i] = ushort(qRound(srgbToLinear(i / qreal(255 * 16)) * (255 * 256))); - cp->m_fromLinear[i] = ushort(qRound(linearToSrgb(i / qreal(255 * 16)) * (255 * 256))); + cp->m_toLinear[i] = ushort(qBound(0, qRound(table.apply(i / qreal(255 * 16)) * (255 * 256)), 65280)); + minInverse = table.applyInverse(i / qreal(255 * 16), minInverse); + cp->m_fromLinear[i] = ushort(qBound(0, qRound(minInverse * (255 * 256)), 65280)); } return cp; diff --git a/src/gui/painting/qcolorprofile_p.h b/src/gui/painting/qcolortrclut_p.h index 425e9abace..76a6a60803 100644 --- a/src/gui/painting/qcolorprofile_p.h +++ b/src/gui/painting/qcolortrclut_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QCOLORPROFILE_P_H -#define QCOLORPROFILE_P_H +#ifndef QCOLORTRCLUT_P_H +#define QCOLORTRCLUT_P_H // // W A R N I N G @@ -52,21 +52,29 @@ // #include <QtGui/private/qtguiglobal_p.h> +#include <QtCore/qsharedpointer.h> #include <QtGui/qrgb.h> #include <QtGui/qrgba64.h> +#include <cmath> + #if defined(__SSE2__) #include <emmintrin.h> #elif defined(__ARM_NEON__) || defined(__ARM_NEON) #include <arm_neon.h> #endif + QT_BEGIN_NAMESPACE -class Q_GUI_EXPORT QColorProfile +class QColorTransferFunction; +class QColorTransferTable; + +class Q_GUI_EXPORT QColorTrcLut : public QEnableSharedFromThis<QColorTrcLut> { public: - static QColorProfile *fromGamma(qreal gamma); - static QColorProfile *fromSRgb(); + static QColorTrcLut *fromGamma(qreal gamma); + static QColorTrcLut *fromTransferFunction(const QColorTransferFunction &transfn); + static QColorTrcLut *fromTransferTable(const QColorTransferTable &transTable); // The following methods all convert opaque or unpremultiplied colors: @@ -121,6 +129,25 @@ public: return convertWithTable(rgb64, m_toLinear); } + float u8ToLinearF32(int c) const + { + ushort v = m_toLinear[c << 4]; + return v * (1.0f / (255*256)); + } + + float u16ToLinearF32(int c) const + { + c -= (c >> 8); + ushort v = m_toLinear[c >> 4]; + return v * (1.0f / (255*256)); + } + + float toLinear(float f) const + { + ushort v = m_toLinear[(int)(f * (255 * 16) + 0.5f)]; + return v * (1.0f / (255*256)); + } + QRgb fromLinear64(QRgba64 rgb64) const { #if defined(__SSE2__) @@ -176,8 +203,31 @@ public: return convertWithTable(rgb64, m_fromLinear); } + int u8FromLinearF32(float f) const + { + ushort v = m_fromLinear[(int)(f * (255 * 16) + 0.5f)]; + return (v + 0x80) >> 8; + } + int u16FromLinearF32(float f) const + { + ushort v = m_fromLinear[(int)(f * (255 * 16) + 0.5f)]; + return v + (v >> 8); + } + float fromLinear(float f) const + { + ushort v = m_fromLinear[(int)(f * (255 * 16) + 0.5f)]; + return v * (1.0f / (255*256)); + } + + // We translate to 0-65280 (255*256) instead to 0-65535 to make simple + // shifting an accurate conversion. + // We translate from 0-4080 (255*16) for the same speed up, and to keep + // the tables small enough to fit in most inner caches. + ushort m_toLinear[(255 * 16) + 1]; // [0-4080] -> [0-65280] + ushort m_fromLinear[(255 * 16) + 1]; // [0-4080] -> [0-65280] + private: - QColorProfile() { } + QColorTrcLut() { } Q_ALWAYS_INLINE static QRgb convertWithTable(QRgb rgb32, const ushort *table) { @@ -230,16 +280,8 @@ private: return QRgba64::fromRgba64(r, g, b, rgb64.alpha()); #endif } - - // We translate to 0-65280 (255*256) instead to 0-65535 to make simple - // shifting an accurate conversion. - // We translate from 0-4080 (255*16) for the same speed up, and to keep - // the tables small enough to fit in most inner caches. - ushort m_toLinear[(255 * 16) + 1]; // [0-4080] -> [0-65280] - ushort m_fromLinear[(255 * 16) + 1]; // [0-4080] -> [0-65280] - }; QT_END_NAMESPACE -#endif // QCOLORPROFILE_P_H +#endif // QCOLORTRCLUT_P_H diff --git a/src/gui/painting/qcompositionfunctions.cpp b/src/gui/painting/qcompositionfunctions.cpp index 027bf23115..06a849e790 100644 --- a/src/gui/painting/qcompositionfunctions.cpp +++ b/src/gui/painting/qcompositionfunctions.cpp @@ -137,6 +137,7 @@ struct Rgba64OperationsBase { ::memcpy(dest, src, len * sizeof(Type)); } }; +#if QT_CONFIG(raster_64bit) const Rgba64OperationsBase::Type Rgba64OperationsBase::clear = QRgba64::fromRgba64(0); struct Rgba64OperationsC : public Rgba64OperationsBase @@ -309,10 +310,8 @@ struct Rgba64OperationsNEON : public Rgba64OperationsBase return interpolate65535(x, a1, y, a2); } }; - #endif -typedef Argb32OperationsC Argb32Operations; #if defined(__SSE2__) typedef Rgba64OperationsSSE2 Rgba64Operations; #elif defined(__ARM_NEON__) @@ -320,6 +319,9 @@ typedef Rgba64OperationsNEON Rgba64Operations; #else typedef Rgba64OperationsC Rgba64Operations; #endif +#endif // QT_CONFIG(raster_64bit) + +typedef Argb32OperationsC Argb32Operations; /* result = 0 @@ -343,20 +345,23 @@ void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint, uint const_ comp_func_Clear_template<Argb32Operations>(dest, length, const_alpha); } -void QT_FASTCALL comp_func_solid_Clear_rgb64(QRgba64 *dest, int length, QRgba64, uint const_alpha) +void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, uint const_alpha) { - comp_func_Clear_template<Rgba64Operations>(dest, length, const_alpha); + comp_func_Clear_template<Argb32Operations>(dest, length, const_alpha); } -void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_Clear_rgb64(QRgba64 *dest, int length, QRgba64, uint const_alpha) { - comp_func_Clear_template<Argb32Operations>(dest, length, const_alpha); + comp_func_Clear_template<Rgba64Operations>(dest, length, const_alpha); } void QT_FASTCALL comp_func_Clear_rgb64(QRgba64 *dest, const QRgba64 *, int length, uint const_alpha) { comp_func_Clear_template<Rgba64Operations>(dest, length, const_alpha); } +#endif + /* result = s @@ -399,36 +404,40 @@ void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint color, uint comp_func_solid_Source_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_Source_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_Source(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_Source_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_Source_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_Source(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_Source_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_Source_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_Source_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_Source_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_Source_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif void QT_FASTCALL comp_func_solid_Destination(uint *, int, uint, uint) { } -void QT_FASTCALL comp_func_solid_Destination_rgb64(QRgba64 *, int, QRgba64, uint) +void QT_FASTCALL comp_func_Destination(uint *, const uint *, int, uint) { } -void QT_FASTCALL comp_func_Destination(uint *, const uint *, int, uint) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_Destination_rgb64(QRgba64 *, int, QRgba64, uint) { } void QT_FASTCALL comp_func_Destination_rgb64(QRgba64 *, const QRgba64 *, int, uint) { } +#endif /* result = s + d * sia @@ -483,20 +492,22 @@ void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint color, comp_func_solid_SourceOver_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_SourceOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_SourceOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_SourceOver_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_SourceOver_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_SourceOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_SourceOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_SourceOver_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_SourceOver_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_SourceOver_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_SourceOver_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* result = d + s * dia @@ -542,20 +553,22 @@ void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, uint co comp_func_solid_DestinationOver_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_DestinationOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_DestinationOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_DestinationOver_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_DestinationOver_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_DestinationOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_DestinationOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_DestinationOver_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_DestinationOver_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_DestinationOver_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_DestinationOver_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* result = s * da @@ -606,20 +619,22 @@ void QT_FASTCALL comp_func_solid_SourceIn(uint *dest, int length, uint color, ui comp_func_solid_SourceIn_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_SourceIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_SourceIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_SourceIn_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_SourceIn_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_SourceIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_SourceIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_SourceIn_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_SourceIn_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_SourceIn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_SourceIn_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* result = d * sa @@ -665,20 +680,22 @@ void QT_FASTCALL comp_func_solid_DestinationIn(uint *dest, int length, uint colo comp_func_solid_DestinationIn_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_DestinationIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_DestinationIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_DestinationIn_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_DestinationIn_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_DestinationIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_DestinationIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_DestinationIn_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_DestinationIn_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_DestinationIn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_DestinationIn_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* result = s * dia @@ -727,20 +744,22 @@ void QT_FASTCALL comp_func_solid_SourceOut(uint *dest, int length, uint color, u comp_func_solid_SourceOut_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_SourceOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_SourceOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_SourceOut_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_SourceOut_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_SourceOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_SourceOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_SourceOut_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_SourceOut_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_SourceOut_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_SourceOut_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* result = d * sia @@ -786,20 +805,22 @@ void QT_FASTCALL comp_func_solid_DestinationOut(uint *dest, int length, uint col comp_func_solid_DestinationOut_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_DestinationOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_DestinationOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_DestinationOut_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_DestinationOut_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_DestinationOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_DestinationOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_DestinationOut_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_DestinationOut_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_DestinationOut_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_DestinationOut_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* result = s*da + d*sia @@ -845,20 +866,22 @@ void QT_FASTCALL comp_func_solid_SourceAtop(uint *dest, int length, uint color, comp_func_solid_SourceAtop_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_SourceAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_SourceAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_SourceAtop_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_SourceAtop_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_SourceAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_SourceAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_SourceAtop_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_SourceAtop_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_SourceAtop_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_SourceAtop_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* result = d*sa + s*dia @@ -909,20 +932,22 @@ void QT_FASTCALL comp_func_solid_DestinationAtop(uint *dest, int length, uint co comp_func_solid_DestinationAtop_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_DestinationAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_DestinationAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_DestinationAtop_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_DestinationAtop_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_DestinationAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_DestinationAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_DestinationAtop_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_DestinationAtop_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_DestinationAtop_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_DestinationAtop_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* result = d*sia + s*dia @@ -969,20 +994,22 @@ void QT_FASTCALL comp_func_solid_XOR(uint *dest, int length, uint color, uint co comp_func_solid_XOR_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_XOR_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_XOR(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_XOR_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_XOR_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_XOR(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_XOR_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_XOR_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_XOR_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_XOR_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_XOR_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif struct QFullCoverage { inline void store(uint *dest, const uint src) const @@ -1078,20 +1105,22 @@ void QT_FASTCALL comp_func_solid_Plus(uint *dest, int length, uint color, uint c comp_func_solid_Plus_template<Argb32Operations>(dest, length, color, const_alpha); } -void QT_FASTCALL comp_func_solid_Plus_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +void QT_FASTCALL comp_func_Plus(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - comp_func_solid_Plus_template<Rgba64Operations>(dest, length, color, const_alpha); + comp_func_Plus_template<Argb32Operations>(dest, src, length, const_alpha); } -void QT_FASTCALL comp_func_Plus(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +#if QT_CONFIG(raster_64bit) +void QT_FASTCALL comp_func_solid_Plus_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { - comp_func_Plus_template<Argb32Operations>(dest, src, length, const_alpha); + comp_func_solid_Plus_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_Plus_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { comp_func_Plus_template<Rgba64Operations>(dest, src, length, const_alpha); } +#endif /* Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa) @@ -1101,13 +1130,8 @@ 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) +static inline void comp_func_solid_Multiply_impl(uint *dest, int length, uint color, const T &coverage) { int sa = qAlpha(color); int sr = qRed(color); @@ -1129,8 +1153,22 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Multiply_impl(uint *dest, } } +void QT_FASTCALL comp_func_solid_Multiply(uint *dest, int length, uint 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)); +} + +#if QT_CONFIG(raster_64bit) +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(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +static inline void comp_func_solid_Multiply_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { uint sa = color.alpha(); uint sr = color.red(); @@ -1152,14 +1190,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Multiply_impl(QRgba64 *de } } -void QT_FASTCALL comp_func_solid_Multiply(uint *dest, int length, uint 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)); -} - void QT_FASTCALL comp_func_solid_Multiply_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -1167,9 +1197,10 @@ void QT_FASTCALL comp_func_solid_Multiply_rgb64(QRgba64 *dest, int length, QRgba else comp_func_solid_Multiply_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif 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) +static inline void comp_func_Multiply_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { for (int i = 0; i < length; ++i) { uint d = dest[i]; @@ -1189,8 +1220,17 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Multiply_impl(uint *Q_DECL_REST } } +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) + comp_func_Multiply_impl(dest, src, length, QFullCoverage()); + else + comp_func_Multiply_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) 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) +static 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]; @@ -1210,14 +1250,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Multiply_impl(QRgba64 *Q_DECL_R } } -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) - comp_func_Multiply_impl(dest, src, length, QFullCoverage()); - else - 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) @@ -1225,13 +1257,14 @@ void QT_FASTCALL comp_func_Multiply_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const Q else comp_func_Multiply_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* Dca' = (Sca.Da + Dca.Sa - Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa) = Sca + Dca - Sca.Dca */ template <typename T> -Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Screen_impl(uint *dest, int length, uint color, const T &coverage) +static inline void comp_func_solid_Screen_impl(uint *dest, int length, uint color, const T &coverage) { int sa = qAlpha(color); int sr = qRed(color); @@ -1253,8 +1286,17 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Screen_impl(uint *dest, i } } +void QT_FASTCALL comp_func_solid_Screen(uint *dest, int length, uint 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)); +} + +#if QT_CONFIG(raster_64bit) template <typename T> -Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Screen_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +static inline void comp_func_solid_Screen_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { uint sa = color.alpha(); uint sr = color.red(); @@ -1276,14 +1318,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Screen_impl(QRgba64 *dest } } -void QT_FASTCALL comp_func_solid_Screen(uint *dest, int length, uint 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)); -} - void QT_FASTCALL comp_func_solid_Screen_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -1291,9 +1325,10 @@ void QT_FASTCALL comp_func_solid_Screen_rgb64(QRgba64 *dest, int length, QRgba64 else comp_func_solid_Screen_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif 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) +static inline void comp_func_Screen_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { for (int i = 0; i < length; ++i) { uint d = dest[i]; @@ -1313,8 +1348,17 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Screen_impl(uint *Q_DECL_RESTRI } } +void QT_FASTCALL comp_func_Screen(uint *dest, const uint *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 QT_CONFIG(raster_64bit) 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) +static 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]; @@ -1334,14 +1378,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Screen_impl(QRgba64 *Q_DECL_RES } } -void QT_FASTCALL comp_func_Screen(uint *dest, const uint *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)); -} - void QT_FASTCALL comp_func_Screen_rgb64(QRgba64 *dest, const QRgba64 *src, int length, uint const_alpha) { if (const_alpha == 255) @@ -1349,6 +1385,7 @@ void QT_FASTCALL comp_func_Screen_rgb64(QRgba64 *dest, const QRgba64 *src, int l else comp_func_Screen_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* if 2.Dca < Da @@ -1365,17 +1402,8 @@ 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) +static inline void comp_func_solid_Overlay_impl(uint *dest, int length, uint color, const T &coverage) { int sa = qAlpha(color); int sr = qRed(color); @@ -1397,8 +1425,26 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Overlay_impl(uint *dest, } } +void QT_FASTCALL comp_func_solid_Overlay(uint *dest, int length, uint 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)); +} + +#if QT_CONFIG(raster_64bit) +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(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +static inline void comp_func_solid_Overlay_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { uint sa = color.alpha(); uint sr = color.red(); @@ -1420,14 +1466,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Overlay_impl(QRgba64 *des } } -void QT_FASTCALL comp_func_solid_Overlay(uint *dest, int length, uint 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)); -} - void QT_FASTCALL comp_func_solid_Overlay_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -1435,9 +1473,10 @@ void QT_FASTCALL comp_func_solid_Overlay_rgb64(QRgba64 *dest, int length, QRgba6 else comp_func_solid_Overlay_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif 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) +static inline void comp_func_Overlay_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { for (int i = 0; i < length; ++i) { uint d = dest[i]; @@ -1457,8 +1496,17 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Overlay_impl(uint *Q_DECL_RESTR } } +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) + comp_func_Overlay_impl(dest, src, length, QFullCoverage()); + else + comp_func_Overlay_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) 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) +static 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]; @@ -1478,14 +1526,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Overlay_impl(QRgba64 *Q_DECL_RE } } -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) - comp_func_Overlay_impl(dest, src, length, QFullCoverage()); - else - 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) @@ -1493,6 +1533,7 @@ void QT_FASTCALL comp_func_Overlay_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QR else comp_func_Overlay_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa) @@ -1503,13 +1544,8 @@ 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) +static inline void comp_func_solid_Darken_impl(uint *dest, int length, uint color, const T &coverage) { int sa = qAlpha(color); int sr = qRed(color); @@ -1531,8 +1567,22 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Darken_impl(uint *dest, i } } +void QT_FASTCALL comp_func_solid_Darken(uint *dest, int length, uint 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)); +} + +#if QT_CONFIG(raster_64bit) +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(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +static inline void comp_func_solid_Darken_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { uint sa = color.alpha(); uint sr = color.red(); @@ -1554,14 +1604,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Darken_impl(QRgba64 *dest } } -void QT_FASTCALL comp_func_solid_Darken(uint *dest, int length, uint 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)); -} - void QT_FASTCALL comp_func_solid_Darken_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -1569,9 +1611,10 @@ void QT_FASTCALL comp_func_solid_Darken_rgb64(QRgba64 *dest, int length, QRgba64 else comp_func_solid_Darken_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif 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) +static inline void comp_func_Darken_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { for (int i = 0; i < length; ++i) { uint d = dest[i]; @@ -1591,8 +1634,17 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Darken_impl(uint *Q_DECL_RESTRI } } +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) + comp_func_Darken_impl(dest, src, length, QFullCoverage()); + else + comp_func_Darken_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) 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) +static 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]; @@ -1612,14 +1664,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Darken_impl(QRgba64 *Q_DECL_RES } } -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) - comp_func_Darken_impl(dest, src, length, QFullCoverage()); - else - 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) @@ -1627,6 +1671,7 @@ void QT_FASTCALL comp_func_Darken_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRg else comp_func_Darken_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* Dca' = max(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa) @@ -1637,13 +1682,8 @@ 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) +static inline void comp_func_solid_Lighten_impl(uint *dest, int length, uint color, const T &coverage) { int sa = qAlpha(color); int sr = qRed(color); @@ -1665,8 +1705,23 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Lighten_impl(uint *dest, } } +void QT_FASTCALL comp_func_solid_Lighten(uint *dest, int length, uint 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)); +} + + +#if QT_CONFIG(raster_64bit) +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(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +static inline void comp_func_solid_Lighten_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { uint sa = color.alpha(); uint sr = color.red(); @@ -1688,14 +1743,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Lighten_impl(QRgba64 *des } } -void QT_FASTCALL comp_func_solid_Lighten(uint *dest, int length, uint 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)); -} - void QT_FASTCALL comp_func_solid_Lighten_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -1703,9 +1750,10 @@ void QT_FASTCALL comp_func_solid_Lighten_rgb64(QRgba64 *dest, int length, QRgba6 else comp_func_solid_Lighten_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif 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) +static inline void comp_func_Lighten_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { for (int i = 0; i < length; ++i) { uint d = dest[i]; @@ -1725,8 +1773,17 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Lighten_impl(uint *Q_DECL_RESTR } } +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) + comp_func_Lighten_impl(dest, src, length, QFullCoverage()); + else + comp_func_Lighten_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) 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) +static 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]; @@ -1746,14 +1803,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Lighten_impl(QRgba64 *Q_DECL_RE } } -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) - comp_func_Lighten_impl(dest, src, length, QFullCoverage()); - else - 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) @@ -1761,6 +1810,7 @@ void QT_FASTCALL comp_func_Lighten_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QR else comp_func_Lighten_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* if Sca.Da + Dca.Sa >= Sa.Da @@ -1781,21 +1831,8 @@ 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) +static inline void comp_func_solid_ColorDodge_impl(uint *dest, int length, uint color, const T &coverage) { int sa = qAlpha(color); int sr = qRed(color); @@ -1817,8 +1854,30 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorDodge_impl(uint *des } } +void QT_FASTCALL comp_func_solid_ColorDodge(uint *dest, int length, uint 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)); +} + +#if QT_CONFIG(raster_64bit) +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(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +static inline void comp_func_solid_ColorDodge_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { uint sa = color.alpha(); uint sr = color.red(); @@ -1840,14 +1899,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorDodge_impl(QRgba64 * } } -void QT_FASTCALL comp_func_solid_ColorDodge(uint *dest, int length, uint 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)); -} - void QT_FASTCALL comp_func_solid_ColorDodge_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -1855,9 +1906,10 @@ void QT_FASTCALL comp_func_solid_ColorDodge_rgb64(QRgba64 *dest, int length, QRg else comp_func_solid_ColorDodge_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif 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) +static inline void comp_func_ColorDodge_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { for (int i = 0; i < length; ++i) { uint d = dest[i]; @@ -1877,8 +1929,17 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorDodge_impl(uint *Q_DECL_RE } } +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) + comp_func_ColorDodge_impl(dest, src, length, QFullCoverage()); + else + comp_func_ColorDodge_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) 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) +static 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]; @@ -1898,14 +1959,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorDodge_impl(QRgba64 *Q_DECL } } -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) - comp_func_ColorDodge_impl(dest, src, length, QFullCoverage()); - else - 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) @@ -1913,6 +1966,7 @@ void QT_FASTCALL comp_func_ColorDodge_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const else comp_func_ColorDodge_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* if Sca.Da + Dca.Sa <= Sa.Da @@ -1933,21 +1987,8 @@ 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) +static inline void comp_func_solid_ColorBurn_impl(uint *dest, int length, uint color, const T &coverage) { int sa = qAlpha(color); int sr = qRed(color); @@ -1969,8 +2010,30 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorBurn_impl(uint *dest } } +void QT_FASTCALL comp_func_solid_ColorBurn(uint *dest, int length, uint 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)); +} + +#if QT_CONFIG(raster_64bit) +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(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +static inline void comp_func_solid_ColorBurn_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { uint sa = color.alpha(); uint sr = color.red(); @@ -1992,14 +2055,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorBurn_impl(QRgba64 *d } } -void QT_FASTCALL comp_func_solid_ColorBurn(uint *dest, int length, uint 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)); -} - void QT_FASTCALL comp_func_solid_ColorBurn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -2007,9 +2062,10 @@ void QT_FASTCALL comp_func_solid_ColorBurn_rgb64(QRgba64 *dest, int length, QRgb else comp_func_solid_ColorBurn_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif 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) +static inline void comp_func_ColorBurn_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { for (int i = 0; i < length; ++i) { uint d = dest[i]; @@ -2029,8 +2085,17 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorBurn_impl(uint *Q_DECL_RES } } +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) + comp_func_ColorBurn_impl(dest, src, length, QFullCoverage()); + else + comp_func_ColorBurn_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) 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) +static 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]; @@ -2050,14 +2115,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorBurn_impl(QRgba64 *Q_DECL_ } } -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) - comp_func_ColorBurn_impl(dest, src, length, QFullCoverage()); - else - 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) @@ -2065,6 +2122,7 @@ void QT_FASTCALL comp_func_ColorBurn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const else comp_func_ColorBurn_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* if 2.Sca < Sa @@ -2082,18 +2140,8 @@ 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) +static inline void comp_func_solid_HardLight_impl(uint *dest, int length, uint color, const T &coverage) { int sa = qAlpha(color); int sr = qRed(color); @@ -2115,8 +2163,27 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_HardLight_impl(uint *dest } } +void QT_FASTCALL comp_func_solid_HardLight(uint *dest, int length, uint 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)); +} + +#if QT_CONFIG(raster_64bit) +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(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +static inline void comp_func_solid_HardLight_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { uint sa = color.alpha(); uint sr = color.red(); @@ -2138,14 +2205,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_HardLight_impl(QRgba64 *d } } -void QT_FASTCALL comp_func_solid_HardLight(uint *dest, int length, uint 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)); -} - void QT_FASTCALL comp_func_solid_HardLight_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -2153,9 +2212,10 @@ void QT_FASTCALL comp_func_solid_HardLight_rgb64(QRgba64 *dest, int length, QRgb else comp_func_solid_HardLight_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif 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) +static inline void comp_func_HardLight_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { for (int i = 0; i < length; ++i) { uint d = dest[i]; @@ -2175,8 +2235,17 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_HardLight_impl(uint *Q_DECL_RES } } +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) + comp_func_HardLight_impl(dest, src, length, QFullCoverage()); + else + comp_func_HardLight_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) 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) +static 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]; @@ -2196,14 +2265,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_HardLight_impl(QRgba64 *Q_DECL_ } } -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) - comp_func_HardLight_impl(dest, src, length, QFullCoverage()); - else - 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) @@ -2211,6 +2272,7 @@ void QT_FASTCALL comp_func_HardLight_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const else comp_func_HardLight_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* if 2.Sca <= Sa @@ -2235,24 +2297,8 @@ 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) +static inline void comp_func_solid_SoftLight_impl(uint *dest, int length, uint color, const T &coverage) { int sa = qAlpha(color); int sr = qRed(color); @@ -2274,8 +2320,25 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_SoftLight_impl(uint *dest } } +#if QT_CONFIG(raster_64bit) +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(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +static inline void comp_func_solid_SoftLight_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { uint sa = color.alpha(); uint sr = color.red(); @@ -2296,6 +2359,7 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_SoftLight_impl(QRgba64 *d coverage.store(&dest[i], qRgba64(r, g, b, a)); } } +#endif void QT_FASTCALL comp_func_solid_SoftLight(uint *dest, int length, uint color, uint const_alpha) { @@ -2305,16 +2369,8 @@ 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) +static inline void comp_func_SoftLight_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { for (int i = 0; i < length; ++i) { uint d = dest[i]; @@ -2334,8 +2390,25 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_SoftLight_impl(uint *Q_DECL_RES } } +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) + comp_func_SoftLight_impl(dest, src, length, QFullCoverage()); + else + comp_func_SoftLight_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) +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(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) +static 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]; @@ -2355,14 +2428,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_SoftLight_impl(QRgba64 *Q_DECL_ } } -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) - comp_func_SoftLight_impl(dest, src, length, QFullCoverage()); - else - 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) @@ -2370,6 +2435,7 @@ void QT_FASTCALL comp_func_SoftLight_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const else comp_func_SoftLight_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa) @@ -2380,13 +2446,8 @@ 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) +static inline void comp_func_solid_Difference_impl(uint *dest, int length, uint color, const T &coverage) { int sa = qAlpha(color); int sr = qRed(color); @@ -2408,8 +2469,22 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Difference_impl(uint *des } } +void QT_FASTCALL comp_func_solid_Difference(uint *dest, int length, uint 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)); +} + +#if QT_CONFIG(raster_64bit) +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(QRgba64 *dest, int length, QRgba64 color, const T &coverage) +static inline void comp_func_solid_Difference_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage) { uint sa = color.alpha(); uint sr = color.red(); @@ -2431,14 +2506,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Difference_impl(QRgba64 * } } -void QT_FASTCALL comp_func_solid_Difference(uint *dest, int length, uint 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)); -} - void QT_FASTCALL comp_func_solid_Difference_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -2446,9 +2513,10 @@ void QT_FASTCALL comp_func_solid_Difference_rgb64(QRgba64 *dest, int length, QRg else comp_func_solid_Difference_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif 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) +static inline void comp_func_Difference_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { for (int i = 0; i < length; ++i) { uint d = dest[i]; @@ -2468,8 +2536,17 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Difference_impl(uint *Q_DECL_RE } } +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) + comp_func_Difference_impl(dest, src, length, QFullCoverage()); + else + comp_func_Difference_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) 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) +static 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]; @@ -2489,14 +2566,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Difference_impl(QRgba64 *Q_DECL } } -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) - comp_func_Difference_impl(dest, src, length, QFullCoverage()); - else - 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) @@ -2504,12 +2573,13 @@ void QT_FASTCALL comp_func_Difference_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const else comp_func_Difference_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif /* Dca' = (Sca.Da + Dca.Sa - 2.Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa) */ template <typename T> -Q_STATIC_TEMPLATE_FUNCTION inline void QT_FASTCALL comp_func_solid_Exclusion_impl(uint *dest, int length, uint color, const T &coverage) +static inline void QT_FASTCALL comp_func_solid_Exclusion_impl(uint *dest, int length, uint color, const T &coverage) { int sa = qAlpha(color); int sr = qRed(color); @@ -2531,8 +2601,17 @@ Q_STATIC_TEMPLATE_FUNCTION inline void QT_FASTCALL comp_func_solid_Exclusion_imp } } +void QT_FASTCALL comp_func_solid_Exclusion(uint *dest, int length, uint 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)); +} + +#if QT_CONFIG(raster_64bit) 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) +static 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(); @@ -2555,14 +2634,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void QT_FASTCALL comp_func_solid_Exclusion_imp } -void QT_FASTCALL comp_func_solid_Exclusion(uint *dest, int length, uint 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)); -} - void QT_FASTCALL comp_func_solid_Exclusion_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -2570,9 +2641,10 @@ void QT_FASTCALL comp_func_solid_Exclusion_rgb64(QRgba64 *dest, int length, QRgb else comp_func_solid_Exclusion_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif 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) +static inline void comp_func_Exclusion_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { for (int i = 0; i < length; ++i) { uint d = dest[i]; @@ -2592,8 +2664,17 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Exclusion_impl(uint *Q_DECL_RES } } +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) + comp_func_Exclusion_impl(dest, src, length, QFullCoverage()); + else + comp_func_Exclusion_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if QT_CONFIG(raster_64bit) 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) +static 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]; @@ -2613,14 +2694,6 @@ Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Exclusion_impl(QRgba64 *Q_DECL_ } } -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) - comp_func_Exclusion_impl(dest, src, length, QFullCoverage()); - else - 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) @@ -2628,6 +2701,7 @@ void QT_FASTCALL comp_func_Exclusion_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const else comp_func_Exclusion_impl(dest, src, length, QPartialCoverage(const_alpha)); } +#endif void QT_FASTCALL rasterop_solid_SourceOrDestination(uint *dest, int length, @@ -2977,6 +3051,7 @@ CompositionFunctionSolid qt_functionForModeSolid_C[] = { }; CompositionFunctionSolid64 qt_functionForModeSolid64_C[] = { +#if QT_CONFIG(raster_64bit) comp_func_solid_SourceOver_rgb64, comp_func_solid_DestinationOver_rgb64, comp_func_solid_Clear_rgb64, @@ -3001,6 +3076,10 @@ CompositionFunctionSolid64 qt_functionForModeSolid64_C[] = { comp_func_solid_SoftLight_rgb64, comp_func_solid_Difference_rgb64, comp_func_solid_Exclusion_rgb64, +#else + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -3047,6 +3126,7 @@ CompositionFunction qt_functionForMode_C[] = { }; CompositionFunction64 qt_functionForMode64_C[] = { +#if QT_CONFIG(raster_64bit) comp_func_SourceOver_rgb64, comp_func_DestinationOver_rgb64, comp_func_Clear_rgb64, @@ -3071,6 +3151,10 @@ CompositionFunction64 qt_functionForMode64_C[] = { comp_func_SoftLight_rgb64, comp_func_Difference_rgb64, comp_func_Exclusion_rgb64, +#else + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif 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 d45da14767..53066687d3 100644 --- a/src/gui/painting/qcoregraphics.mm +++ b/src/gui/painting/qcoregraphics.mm @@ -51,6 +51,33 @@ QT_BEGIN_NAMESPACE // ---------------------- Images ---------------------- +CGBitmapInfo qt_mac_bitmapInfoForImage(const QImage &image) +{ + CGBitmapInfo bitmapInfo = kCGImageAlphaNone; + switch (image.format()) { + case QImage::Format_ARGB32: + bitmapInfo = kCGImageAlphaFirst | kCGBitmapByteOrder32Host; + break; + case QImage::Format_RGB32: + bitmapInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; + break; + case QImage::Format_RGBA8888_Premultiplied: + bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big; + break; + case QImage::Format_RGBA8888: + bitmapInfo = kCGImageAlphaLast | kCGBitmapByteOrder32Big; + break; + case QImage::Format_RGBX8888: + bitmapInfo = kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big; + break; + case QImage::Format_ARGB32_Premultiplied: + bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; + break; + default: break; + } + return bitmapInfo; +} + CGImageRef qt_mac_toCGImage(const QImage &inImage) { CGImageRef cgImage = inImage.toCGImage(); @@ -362,13 +389,10 @@ QMacCGContext::QMacCGContext(QPaintDevice *paintDevice) : context(0) if (!image) return; // Context type not supported. - CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); - uint flags = kCGImageAlphaPremultipliedFirst; - flags |= kCGBitmapByteOrder32Host; + QCFType<CGColorSpaceRef> colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + context = CGBitmapContextCreate(image->bits(), image->width(), image->height(), 8, + image->bytesPerLine(), colorSpace, qt_mac_bitmapInfoForImage(*image)); - context = CGBitmapContextCreate(image->bits(), image->width(), image->height(), - 8, image->bytesPerLine(), colorSpace, flags); - CFRelease(colorSpace); CGContextTranslateCTM(context, 0, image->height()); const qreal devicePixelRatio = paintDevice->devicePixelRatioF(); CGContextScaleCTM(context, devicePixelRatio, devicePixelRatio); @@ -396,16 +420,10 @@ QMacCGContext::QMacCGContext(QPainter *painter) : context(0) devType == QInternal::Pixmap || devType == QInternal::Image)) { - CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); - uint flags = kCGImageAlphaPremultipliedFirst; -#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version - flags |= kCGBitmapByteOrder32Host; -#endif const QImage *image = static_cast<const QImage *>(paintEngine->paintDevice()); - - context = CGBitmapContextCreate((void *)image->bits(), image->width(), image->height(), - 8, image->bytesPerLine(), colorSpace, flags); - CFRelease(colorSpace); + QCFType<CGColorSpaceRef> colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + context = CGBitmapContextCreate((void *)image->bits(), image->width(), image->height(), 8, + image->bytesPerLine(), colorSpace, qt_mac_bitmapInfoForImage(*image)); // Invert y axis CGContextTranslateCTM(context, 0, image->height()); diff --git a/src/gui/painting/qcoregraphics_p.h b/src/gui/painting/qcoregraphics_p.h index de721c94aa..868c2b08b5 100644 --- a/src/gui/painting/qcoregraphics_p.h +++ b/src/gui/painting/qcoregraphics_p.h @@ -64,6 +64,8 @@ QT_BEGIN_NAMESPACE +Q_GUI_EXPORT CGBitmapInfo qt_mac_bitmapInfoForImage(const QImage &image); + #ifdef HAVE_APPKIT Q_GUI_EXPORT NSImage *qt_mac_create_nsimage(const QPixmap &pm); Q_GUI_EXPORT NSImage *qt_mac_create_nsimage(const QIcon &icon, int defaultSize = 0); diff --git a/src/gui/painting/qcosmeticstroker.cpp b/src/gui/painting/qcosmeticstroker.cpp index 436a62d486..0fb89a75b5 100644 --- a/src/gui/painting/qcosmeticstroker.cpp +++ b/src/gui/painting/qcosmeticstroker.cpp @@ -289,7 +289,7 @@ void QCosmeticStroker::setup() drawCaps = state->lastPen.capStyle() != Qt::FlatCap; if (strokeSelection & FastDraw) { - color = multiplyAlpha256(state->penData.solid.color, opacity).toArgb32(); + color = multiplyAlpha256(state->penData.solidColor, opacity).toArgb32(); QRasterBuffer *buffer = state->penData.rasterBuffer; pixels = (uint *)buffer->buffer(); ppl = buffer->stride<quint32>(); diff --git a/src/gui/painting/qcosmeticstroker_p.h b/src/gui/painting/qcosmeticstroker_p.h index 082ddee30f..8571b0476a 100644 --- a/src/gui/painting/qcosmeticstroker_p.h +++ b/src/gui/painting/qcosmeticstroker_p.h @@ -98,8 +98,8 @@ public: : state(s), deviceRect(dr_unclipped), clip(dr), - pattern(0), - reversePattern(0), + pattern(nullptr), + reversePattern(nullptr), patternSize(0), patternLength(0), patternOffset(0), diff --git a/src/gui/painting/qdatabuffer_p.h b/src/gui/painting/qdatabuffer_p.h index 7cac2ac358..181d19da0b 100644 --- a/src/gui/painting/qdatabuffer_p.h +++ b/src/gui/painting/qdatabuffer_p.h @@ -60,7 +60,7 @@ QT_BEGIN_NAMESPACE template <typename Type> class QDataBuffer { - Q_DISABLE_COPY(QDataBuffer) + Q_DISABLE_COPY_MOVE(QDataBuffer) public: QDataBuffer(int res) { @@ -69,7 +69,7 @@ public: buffer = (Type*) malloc(capacity * sizeof(Type)); Q_CHECK_PTR(buffer); } else { - buffer = 0; + buffer = nullptr; } siz = 0; } @@ -128,7 +128,7 @@ public: Q_CHECK_PTR(buffer); } else { free(buffer); - buffer = 0; + buffer = nullptr; } } diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index bbeb9fd9ea..ac15d8f4f4 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -43,7 +43,7 @@ #include <qstylehints.h> #include <qguiapplication.h> #include <qatomic.h> -#include <private/qcolorprofile_p.h> +#include <private/qcolortrclut_p.h> #include <private/qdrawhelper_p.h> #include <private/qpaintengine_raster_p.h> #include <private/qpainter_p.h> @@ -55,6 +55,7 @@ #endif #include <private/qguiapplication_p.h> #include <private/qrgba64_p.h> +#include <qendian.h> #include <qloggingcategory.h> #include <qmath.h> @@ -856,6 +857,44 @@ static const QRgba64 *QT_FASTCALL fetchGrayscale8ToRGB64(QRgba64 *buffer, const return buffer; } +static void QT_FASTCALL convertGrayscale16ToRGB32(uint *buffer, int count, const QVector<QRgb> *) +{ + for (int i = 0; i < count; ++i) { + const uint x = qt_div_257(buffer[i]); + buffer[i] = qRgb(x, x, x); + } +} + +static const uint *QT_FASTCALL fetchGrayscale16ToRGB32(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + const unsigned short *s = reinterpret_cast<const unsigned short *>(src) + index; + for (int i = 0; i < count; ++i) { + const uint x = qt_div_257(s[i]); + buffer[i] = qRgb(x, x, x); + } + return buffer; +} + +static const QRgba64 *QT_FASTCALL convertGrayscale16ToRGBA64(QRgba64 *buffer, const uint *src, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + const unsigned short *s = reinterpret_cast<const unsigned short *>(src); + for (int i = 0; i < count; ++i) + buffer[i] = QRgba64::fromRgba64(s[i], s[i], s[i], 65535); + return buffer; +} + +static const QRgba64 *QT_FASTCALL fetchGrayscale16ToRGBA64(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + const unsigned short *s = reinterpret_cast<const unsigned short *>(src) + index; + for (int i = 0; i < count; ++i) { + buffer[i] = QRgba64::fromRgba64(s[i], s[i], s[i], 65535); + } + return buffer; +} + static void QT_FASTCALL storeARGB32FromARGB32PM(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *) { @@ -1047,18 +1086,8 @@ static const QRgba64 *QT_FASTCALL fetchRGB32ToRGB64(QRgba64 *buffer, const uchar static const QRgba64 *QT_FASTCALL convertARGB32ToRGBA64PM(QRgba64 *buffer, const uint *src, int count, const QVector<QRgb> *, QDitherInfo *) { -#ifdef __SSE2__ - qConvertARGB32PMToRGBA64PM_sse2<false, false>(buffer, src, count); - for (int i = 0; i < count; ++i) - buffer[i] = buffer[i].premultiplied(); -#elif defined(__ARM_NEON__) - qConvertARGB32PMToRGBA64PM_neon<false, false>(buffer, src, count); - for (int i = 0; i < count; ++i) - buffer[i] = buffer[i].premultiplied(); -#else for (int i = 0; i < count; ++i) buffer[i] = QRgba64::fromArgb32(src[i]).premultiplied(); -#endif return buffer; } @@ -1088,6 +1117,7 @@ static const QRgba64 *QT_FASTCALL fetchARGB32PMToRGBA64PM(QRgba64 *buffer, const return convertARGB32PMToRGBA64PM(buffer, reinterpret_cast<const uint *>(src) + index, count, nullptr, nullptr); } +#if QT_CONFIG(raster_64bit) static void convertRGBA64ToRGBA64PM(QRgba64 *buffer, int count) { for (int i = 0; i < count; ++i) @@ -1097,6 +1127,7 @@ static void convertRGBA64ToRGBA64PM(QRgba64 *buffer, int count) static void convertRGBA64PMToRGBA64PM(QRgba64 *, int) { } +#endif static const QRgba64 *QT_FASTCALL fetchRGBA64ToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count, const QVector<QRgb> *, QDitherInfo *) @@ -1110,18 +1141,8 @@ static const QRgba64 *QT_FASTCALL fetchRGBA64ToRGBA64PM(QRgba64 *buffer, const u static const QRgba64 *QT_FASTCALL convertRGBA8888ToRGBA64PM(QRgba64 *buffer, const uint *src, int count, const QVector<QRgb> *, QDitherInfo *) { -#ifdef __SSE2__ - qConvertARGB32PMToRGBA64PM_sse2<true, false>(buffer, src, count); - for (int i = 0; i < count; ++i) - buffer[i] = buffer[i].premultiplied(); -#elif defined(__ARM_NEON__) - qConvertARGB32PMToRGBA64PM_neon<true, false>(buffer, src, count); - for (int i = 0; i < count; ++i) - buffer[i] = buffer[i].premultiplied(); -#else for (int i = 0; i < count; ++i) buffer[i] = QRgba64::fromArgb32(RGBA2ARGB(src[i])).premultiplied(); -#endif return buffer; } @@ -1361,6 +1382,22 @@ static void QT_FASTCALL storeGrayscale8FromARGB32PM(uchar *dest, const uint *src dest[index + i] = qGray(qUnpremultiply(src[i])); } +static void QT_FASTCALL storeGrayscale16FromRGB32(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + unsigned short *d = reinterpret_cast<unsigned short *>(dest) + index; + for (int i = 0; i < count; ++i) + d[i] = qGray(src[i]) * 257; +} + +static void QT_FASTCALL storeGrayscale16FromARGB32PM(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + unsigned short *d = reinterpret_cast<unsigned short *>(dest) + index; + for (int i = 0; i < count; ++i) + d[i] = qGray(qUnpremultiply(src[i])) * 257; +} + static const uint *QT_FASTCALL fetchRGB64ToRGB32(uint *buffer, const uchar *src, int index, int count, const QVector<QRgb> *, QDitherInfo *) { @@ -1487,7 +1524,11 @@ QPixelLayout qPixelLayouts[QImage::NImageFormats] = { { true, true, QPixelLayout::BPP64, nullptr, convertPassThrough, nullptr, fetchRGB64ToRGB32, fetchPassThrough64, - storeRGB64FromRGB32, storeRGB64FromRGB32 } // Format_RGBA64_Premultiplied + storeRGB64FromRGB32, storeRGB64FromRGB32 }, // Format_RGBA64_Premultiplied + { false, false, QPixelLayout::BPP16, nullptr, + convertGrayscale16ToRGB32, convertGrayscale16ToRGBA64, + fetchGrayscale16ToRGB32, fetchGrayscale16ToRGBA64, + storeGrayscale16FromARGB32PM, storeGrayscale16FromRGB32 } // Format_Grayscale16 }; Q_STATIC_ASSERT(sizeof(qPixelLayouts) / sizeof(*qPixelLayouts) == QImage::NImageFormats); @@ -1563,6 +1604,16 @@ static void QT_FASTCALL storeRGBA64PMFromRGBA64PM(uchar *dest, const QRgba64 *sr memcpy(d, src, count * sizeof(QRgba64)); } +static void QT_FASTCALL storeGray16FromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + quint16 *d = reinterpret_cast<quint16*>(dest) + index; + for (int i = 0; i < count; ++i) { + QRgba64 s = src[i].unpremultiplied(); + d[i] = qGray(s.red(), s.green(), s.blue()); + } +} + ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[QImage::NImageFormats] = { nullptr, nullptr, @@ -1591,7 +1642,8 @@ ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[QImage::NImageFormats] = { storeGenericFromRGBA64PM<QImage::Format_Grayscale8>, storeRGBX64FromRGBA64PM, storeRGBA64FromRGBA64PM, - storeRGBA64PMFromRGBA64PM + storeRGBA64PMFromRGBA64PM, + storeGray16FromRGBA64PM }; /* @@ -1649,22 +1701,6 @@ static uint *QT_FASTCALL destFetchUndefined(uint *buffer, QRasterBuffer *, int, return buffer; } -static QRgba64 *QT_FASTCALL destFetch64(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length) -{ - const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format]; - return const_cast<QRgba64 *>(layout->fetchToRGBA64PM(buffer, rasterBuffer->scanLine(y), x, length, nullptr, nullptr)); -} - -static QRgba64 * QT_FASTCALL destFetchRGB64(QRgba64 *, QRasterBuffer *rasterBuffer, int x, int y, int) -{ - return (QRgba64 *)rasterBuffer->scanLine(y) + x; -} - -static QRgba64 * QT_FASTCALL destFetch64Undefined(QRgba64 *buffer, QRasterBuffer *, int, int, int) -{ - return buffer; -} - static DestFetchProc destFetchProc[QImage::NImageFormats] = { 0, // Format_Invalid @@ -1695,8 +1731,26 @@ static DestFetchProc destFetchProc[QImage::NImageFormats] = destFetch, // Format_RGBX64 destFetch, // Format_RGBA64 destFetch, // Format_RGBA64_Premultiplied + destFetch, // Format_Grayscale16 }; +#if QT_CONFIG(raster_64bit) +static QRgba64 *QT_FASTCALL destFetch64(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length) +{ + const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format]; + return const_cast<QRgba64 *>(layout->fetchToRGBA64PM(buffer, rasterBuffer->scanLine(y), x, length, nullptr, nullptr)); +} + +static QRgba64 * QT_FASTCALL destFetchRGB64(QRgba64 *, QRasterBuffer *rasterBuffer, int x, int y, int) +{ + return (QRgba64 *)rasterBuffer->scanLine(y) + x; +} + +static QRgba64 * QT_FASTCALL destFetch64Undefined(QRgba64 *buffer, QRasterBuffer *, int, int, int) +{ + return buffer; +} + static DestFetchProc64 destFetchProc64[QImage::NImageFormats] = { 0, // Format_Invalid @@ -1727,7 +1781,9 @@ static DestFetchProc64 destFetchProc64[QImage::NImageFormats] = destFetchRGB64, // Format_RGBX64 destFetch64, // Format_RGBA64 destFetchRGB64, // Format_RGBA64_Premultiplied + destFetch64, // Format_Grayscale16 }; +#endif /* Returns the color in the mono destination color table @@ -1835,21 +1891,6 @@ static void QT_FASTCALL destStore(QRasterBuffer *rasterBuffer, int x, int y, con store(dest, buffer, x, length, nullptr, nullptr); } -static void QT_FASTCALL destStore64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) -{ - auto store = qStoreFromRGBA64PM[rasterBuffer->format]; - uchar *dest = rasterBuffer->scanLine(y); - store(dest, buffer, x, length, nullptr, nullptr); -} - -static void QT_FASTCALL destStore64RGBA64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) -{ - QRgba64 *dest = reinterpret_cast<QRgba64*>(rasterBuffer->scanLine(y)) + x; - for (int i = 0; i < length; ++i) { - dest[i] = buffer[i].unpremultiplied(); - } -} - static DestStoreProc destStoreProc[QImage::NImageFormats] = { 0, // Format_Invalid @@ -1880,8 +1921,25 @@ static DestStoreProc destStoreProc[QImage::NImageFormats] = destStore, // Format_RGBX64 destStore, // Format_RGBA64 destStore, // Format_RGBA64_Premultiplied + destStore, // Format_Grayscale16 }; +#if QT_CONFIG(raster_64bit) +static void QT_FASTCALL destStore64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) +{ + auto store = qStoreFromRGBA64PM[rasterBuffer->format]; + uchar *dest = rasterBuffer->scanLine(y); + store(dest, buffer, x, length, nullptr, nullptr); +} + +static void QT_FASTCALL destStore64RGBA64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) +{ + QRgba64 *dest = reinterpret_cast<QRgba64*>(rasterBuffer->scanLine(y)) + x; + for (int i = 0; i < length; ++i) { + dest[i] = buffer[i].unpremultiplied(); + } +} + static DestStoreProc64 destStoreProc64[QImage::NImageFormats] = { 0, // Format_Invalid @@ -1911,8 +1969,10 @@ static DestStoreProc64 destStoreProc64[QImage::NImageFormats] = destStore64, // Format_Grayscale8 0, // Format_RGBX64 destStore64RGBA64, // Format_RGBA64 - 0 // Format_RGBA64_Premultiplied + 0, // Format_RGBA64_Premultiplied + destStore64, // Format_Grayscale16 }; +#endif /* Source fetches @@ -1963,6 +2023,7 @@ static const uint *QT_FASTCALL fetchUntransformedRGB16(uint *buffer, const Opera return buffer; } +#if QT_CONFIG(raster_64bit) static const QRgba64 *QT_FASTCALL fetchUntransformed64(QRgba64 *buffer, const Operator *, const QSpanData *data, int y, int x, int length) { @@ -1976,6 +2037,7 @@ static const QRgba64 *QT_FASTCALL fetchUntransformedRGBA64PM(QRgba64 *, const Op const uchar *scanLine = data->texture.scanLine(y); return reinterpret_cast<const QRgba64 *>(scanLine) + x; } +#endif template<TextureBlendType blendType> inline void fetchTransformed_pixelBounds(int max, int l1, int l2, int &v) @@ -2166,6 +2228,7 @@ static const uint *QT_FASTCALL fetchTransformed(uint *buffer, const Operator *, return buffer; } +#if QT_CONFIG(raster_64bit) template<TextureBlendType blendType> /* either BlendTransformed or BlendTransformedTiled */ static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Operator *, const QSpanData *data, int y, int x, int length) @@ -2186,6 +2249,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Oper convertRGBA64ToRGBA64PM(buffer, length); return buffer; } +#endif /** \internal interpolate 4 argb pixels with the distx and disty factor. @@ -3487,6 +3551,7 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper return buffer; } +#if QT_CONFIG(raster_64bit) template<TextureBlendType blendType> static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint32(QRgba64 *buffer, const QSpanData *data, int y, int x, int length) @@ -3500,8 +3565,8 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint32(QRgba64 *buf uint sbuf1[BufferSize]; uint sbuf2[BufferSize]; - QRgba64 buf1[BufferSize]; - QRgba64 buf2[BufferSize]; + alignas(8) QRgba64 buf1[BufferSize]; + alignas(8) QRgba64 buf2[BufferSize]; QRgba64 *end = buffer + length; QRgba64 *b = buffer; @@ -3658,8 +3723,8 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint64(QRgba64 *buf const qreal cx = x + qreal(0.5); const qreal cy = y + qreal(0.5); - QRgba64 buf1[BufferSize]; - QRgba64 buf2[BufferSize]; + alignas(8) QRgba64 buf1[BufferSize]; + alignas(8) QRgba64 buf2[BufferSize]; QRgba64 *end = buffer + length; QRgba64 *b = buffer; @@ -3807,6 +3872,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co return fetchTransformedBilinear64_uint64<blendType>(buffer, data, y, x, length); return fetchTransformedBilinear64_uint32<blendType>(buffer, data, y, x, length); } +#endif // FetchUntransformed can have more specialized methods added depending on SIMD features. static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = { @@ -3838,6 +3904,7 @@ static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = { fetchUntransformed, // RGBX64 fetchUntransformed, // RGBA64 fetchUntransformed, // RGBA64_Premultiplied + fetchUntransformed, // Grayscale16 }; static const SourceFetchProc sourceFetchGeneric[NBlendTypes] = { @@ -3876,6 +3943,20 @@ static SourceFetchProc sourceFetchAny32[NBlendTypes] = { fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP32> // TransformedBilinearTiled }; +static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage::Format format) +{ + if (format == QImage::Format_RGB32 || format == QImage::Format_ARGB32_Premultiplied) + return sourceFetchARGB32PM[blendType]; + if (blendType == BlendUntransformed || blendType == BlendTiled) + return sourceFetchUntransformed[format]; + if (qPixelLayouts[format].bpp == QPixelLayout::BPP16) + return sourceFetchAny16[blendType]; + if (qPixelLayouts[format].bpp == QPixelLayout::BPP32) + return sourceFetchAny32[blendType]; + return sourceFetchGeneric[blendType]; +} + +#if QT_CONFIG(raster_64bit) static const SourceFetchProc64 sourceFetchGeneric64[NBlendTypes] = { fetchUntransformed64, // Untransformed fetchUntransformed64, // Tiled @@ -3894,25 +3975,13 @@ static const SourceFetchProc64 sourceFetchRGBA64PM[NBlendTypes] = { fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled }; -static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage::Format format) -{ - if (format == QImage::Format_RGB32 || format == QImage::Format_ARGB32_Premultiplied) - return sourceFetchARGB32PM[blendType]; - if (blendType == BlendUntransformed || blendType == BlendTiled) - return sourceFetchUntransformed[format]; - if (qPixelLayouts[format].bpp == QPixelLayout::BPP16) - return sourceFetchAny16[blendType]; - if (qPixelLayouts[format].bpp == QPixelLayout::BPP32) - return sourceFetchAny32[blendType]; - return sourceFetchGeneric[blendType]; -} - static inline SourceFetchProc64 getSourceFetch64(TextureBlendType blendType, QImage::Format format) { if (format == QImage::Format_RGBX64 || format == QImage::Format_RGBA64_Premultiplied) return sourceFetchRGBA64PM[blendType]; return sourceFetchGeneric64[blendType]; } +#endif #define FIXPT_BITS 8 @@ -3924,11 +3993,13 @@ static uint qt_gradient_pixel_fixed(const QGradientData *data, int fixed_pos) return data->colorTable32[qt_gradient_clamp(data, ipos)]; } +#if QT_CONFIG(raster_64bit) static const QRgba64& qt_gradient_pixel64_fixed(const QGradientData *data, int fixed_pos) { int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS; return data->colorTable64[qt_gradient_clamp(data, ipos)]; } +#endif static void QT_FASTCALL getLinearGradientValues(LinearGradientValues *v, const QSpanData *data) { @@ -3962,6 +4033,7 @@ public: } }; +#if QT_CONFIG(raster_64bit) class GradientBase64 { public: @@ -3980,6 +4052,7 @@ public: qt_memfill64((quint64*)buffer, fill, length); } }; +#endif template<class GradientBase, typename BlendType> static inline const BlendType * QT_FASTCALL qt_fetch_linear_gradient_template( @@ -4057,11 +4130,13 @@ static const uint * QT_FASTCALL qt_fetch_linear_gradient(uint *buffer, const Ope return qt_fetch_linear_gradient_template<GradientBase32, uint>(buffer, op, data, y, x, length); } +#if QT_CONFIG(raster_64bit) static const QRgba64 * QT_FASTCALL qt_fetch_linear_gradient_rgb64(QRgba64 *buffer, const Operator *op, const QSpanData *data, int y, int x, int length) { return qt_fetch_linear_gradient_template<GradientBase64, QRgba64>(buffer, op, data, y, x, length); } +#endif static void QT_FASTCALL getRadialGradientValues(RadialGradientValues *v, const QSpanData *data) { @@ -4123,11 +4198,13 @@ const uint * QT_FASTCALL qt_fetch_radial_gradient_plain(uint *buffer, const Oper static SourceFetchProc qt_fetch_radial_gradient = qt_fetch_radial_gradient_plain; +#if QT_CONFIG(raster_64bit) const QRgba64 * QT_FASTCALL qt_fetch_radial_gradient_rgb64(QRgba64 *buffer, const Operator *op, const QSpanData *data, int y, int x, int length) { return qt_fetch_radial_gradient_template<RadialFetchPlain<GradientBase64>, QRgba64>(buffer, op, data, y, x, length); } +#endif template <class GradientBase, typename BlendType> static inline const BlendType * QT_FASTCALL qt_fetch_conical_gradient_template( @@ -4186,23 +4263,29 @@ static const uint * QT_FASTCALL qt_fetch_conical_gradient(uint *buffer, const Op return qt_fetch_conical_gradient_template<GradientBase32, uint>(buffer, data, y, x, length); } +#if QT_CONFIG(raster_64bit) static const QRgba64 * QT_FASTCALL qt_fetch_conical_gradient_rgb64(QRgba64 *buffer, const Operator *, const QSpanData *data, int y, int x, int length) { return qt_fetch_conical_gradient_template<GradientBase64, QRgba64>(buffer, data, y, x, length); } +#endif extern CompositionFunctionSolid qt_functionForModeSolid_C[]; extern CompositionFunctionSolid64 qt_functionForModeSolid64_C[]; static const CompositionFunctionSolid *functionForModeSolid = qt_functionForModeSolid_C; +#if QT_CONFIG(raster_64bit) static const CompositionFunctionSolid64 *functionForModeSolid64 = qt_functionForModeSolid64_C; +#endif extern CompositionFunction qt_functionForMode_C[]; extern CompositionFunction64 qt_functionForMode64_C[]; static const CompositionFunction *functionForMode = qt_functionForMode_C; +#if QT_CONFIG(raster_64bit) static const CompositionFunction64 *functionForMode64 = qt_functionForMode64_C; +#endif static TextureBlendType getBlendType(const QSpanData *data) { @@ -4232,43 +4315,60 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in switch(data->type) { case QSpanData::Solid: - solidSource = data->solid.color.isOpaque(); + solidSource = data->solidColor.isOpaque(); op.srcFetch = 0; +#if QT_CONFIG(raster_64bit) op.srcFetch64 = 0; +#endif break; case QSpanData::LinearGradient: solidSource = !data->gradient.alphaColor; getLinearGradientValues(&op.linear, data); op.srcFetch = qt_fetch_linear_gradient; +#if QT_CONFIG(raster_64bit) op.srcFetch64 = qt_fetch_linear_gradient_rgb64; +#endif break; case QSpanData::RadialGradient: solidSource = !data->gradient.alphaColor; getRadialGradientValues(&op.radial, data); op.srcFetch = qt_fetch_radial_gradient; +#if QT_CONFIG(raster_64bit) op.srcFetch64 = qt_fetch_radial_gradient_rgb64; +#endif break; case QSpanData::ConicalGradient: solidSource = !data->gradient.alphaColor; op.srcFetch = qt_fetch_conical_gradient; +#if QT_CONFIG(raster_64bit) op.srcFetch64 = qt_fetch_conical_gradient_rgb64; +#endif break; case QSpanData::Texture: solidSource = !data->texture.hasAlpha; op.srcFetch = getSourceFetch(getBlendType(data), data->texture.format); +#if QT_CONFIG(raster_64bit) op.srcFetch64 = getSourceFetch64(getBlendType(data), data->texture.format);; +#endif break; default: Q_UNREACHABLE(); break; } +#if !QT_CONFIG(raster_64bit) + op.srcFetch64 = 0; +#endif op.mode = data->rasterBuffer->compositionMode; if (op.mode == QPainter::CompositionMode_SourceOver && solidSource) op.mode = QPainter::CompositionMode_Source; op.destFetch = destFetchProc[data->rasterBuffer->format]; +#if QT_CONFIG(raster_64bit) op.destFetch64 = destFetchProc64[data->rasterBuffer->format]; +#else + op.destFetch64 = 0; +#endif if (op.mode == QPainter::CompositionMode_Source && (data->type != QSpanData::Texture || data->texture.const_alpha == 256)) { const QSpan *lastSpan = spans + spanCount; @@ -4280,44 +4380,90 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in } ++spans; } - if (!alphaSpans) { + if (!alphaSpans && spanCount > 0) { // If all spans are opaque we do not need to fetch dest. // But don't clear passthrough destFetch as they are just as fast and save destStore. if (op.destFetch != destFetchARGB32P) op.destFetch = destFetchUndefined; +#if QT_CONFIG(raster_64bit) if (op.destFetch64 != destFetchRGB64) op.destFetch64 = destFetch64Undefined; +#endif } } op.destStore = destStoreProc[data->rasterBuffer->format]; - op.destStore64 = destStoreProc64[data->rasterBuffer->format]; - op.funcSolid = functionForModeSolid[op.mode]; - op.funcSolid64 = functionForModeSolid64[op.mode]; op.func = functionForMode[op.mode]; +#if QT_CONFIG(raster_64bit) + op.destStore64 = destStoreProc64[data->rasterBuffer->format]; + op.funcSolid64 = functionForModeSolid64[op.mode]; op.func64 = functionForMode64[op.mode]; +#else + op.destStore64 = 0; + op.funcSolid64 = 0; + op.func64 = 0; +#endif return op; } +static void spanfill_from_first(QRasterBuffer *rasterBuffer, QPixelLayout::BPP bpp, int x, int y, int length) +{ + switch (bpp) { + case QPixelLayout::BPP64: { + quint64 *dest = reinterpret_cast<quint64 *>(rasterBuffer->scanLine(y)) + x; + qt_memfill_template(dest + 1, dest[0], length - 1); + break; + } + case QPixelLayout::BPP32: { + quint32 *dest = reinterpret_cast<quint32 *>(rasterBuffer->scanLine(y)) + x; + qt_memfill_template(dest + 1, dest[0], length - 1); + break; + } + case QPixelLayout::BPP24: { + quint24 *dest = reinterpret_cast<quint24 *>(rasterBuffer->scanLine(y)) + x; + qt_memfill_template(dest + 1, dest[0], length - 1); + break; + } + case QPixelLayout::BPP16: { + quint16 *dest = reinterpret_cast<quint16 *>(rasterBuffer->scanLine(y)) + x; + qt_memfill_template(dest + 1, dest[0], length - 1); + break; + } + case QPixelLayout::BPP8: { + uchar *dest = rasterBuffer->scanLine(y) + x; + memset(dest + 1, dest[0], length - 1); + break; + } + default: + Q_UNREACHABLE(); + } +} // -------------------- blend methods --------------------- -#if !defined(Q_CC_SUN) -static -#endif -void blend_color_generic(int count, const QSpan *spans, void *userData) +static void blend_color_generic(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); uint buffer[BufferSize]; - Operator op = getOperator(data, spans, count); - const uint color = data->solid.color.toArgb32(); + Operator op = getOperator(data, nullptr, 0); + const uint color = data->solidColor.toArgb32(); + bool solidFill = data->rasterBuffer->compositionMode == QPainter::CompositionMode_Source + || (data->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver && qAlpha(color) == 255); + QPixelLayout::BPP bpp = qPixelLayouts[data->rasterBuffer->format].bpp; while (count--) { int x = spans->x; int length = spans->len; + if (solidFill && bpp >= QPixelLayout::BPP8 && spans->coverage == 255 && length) { + // If dest doesn't matter we don't need to bother with blending or converting all the identical pixels + op.destStore(data->rasterBuffer, x, spans->y, &color, 1); + spanfill_from_first(data->rasterBuffer, bpp, x, spans->y, length); + length = 0; + } + while (length) { int l = qMin(BufferSize, length); uint *dest = op.destFetch(buffer, data->rasterBuffer, x, spans->y, l); @@ -4335,15 +4481,15 @@ static void blend_color_argb(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); - const Operator op = getOperator(data, spans, count); - const uint color = data->solid.color.toArgb32(); + const Operator op = getOperator(data, nullptr, 0); + const uint color = data->solidColor.toArgb32(); if (op.mode == QPainter::CompositionMode_Source) { // inline for performance while (count--) { uint *target = ((uint *)data->rasterBuffer->scanLine(spans->y)) + spans->x; if (spans->coverage == 255) { - QT_MEMFILL_UINT(target, spans->len, color); + qt_memfill(target, color, spans->len); } else { uint c = BYTE_MUL(color, spans->coverage); int ialpha = 255 - spans->coverage; @@ -4364,35 +4510,33 @@ static void blend_color_argb(int count, const QSpan *spans, void *userData) void blend_color_generic_rgb64(int count, const QSpan *spans, void *userData) { +#if QT_CONFIG(raster_64bit) QSpanData *data = reinterpret_cast<QSpanData *>(userData); - Operator op = getOperator(data, spans, count); + Operator op = getOperator(data, nullptr, 0); if (!op.funcSolid64) { qCDebug(lcQtGuiDrawHelper, "blend_color_generic_rgb64: unsupported 64bit blend attempted, falling back to 32-bit"); return blend_color_generic(count, spans, userData); } - quint64 buffer[BufferSize]; - const QRgba64 color = data->solid.color; + alignas(8) QRgba64 buffer[BufferSize]; + const QRgba64 color = data->solidColor; bool solidFill = data->rasterBuffer->compositionMode == QPainter::CompositionMode_Source || (data->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver && color.isOpaque()); - bool isBpp32 = qPixelLayouts[data->rasterBuffer->format].bpp == QPixelLayout::BPP32; + QPixelLayout::BPP bpp = qPixelLayouts[data->rasterBuffer->format].bpp; while (count--) { int x = spans->x; int length = spans->len; - if (solidFill && isBpp32 && spans->coverage == 255) { + if (solidFill && bpp >= QPixelLayout::BPP8 && spans->coverage == 255 && length) { // If dest doesn't matter we don't need to bother with blending or converting all the identical pixels - if (length > 0) { - op.destStore64(data->rasterBuffer, x, spans->y, &color, 1); - uint *dest = (uint*)data->rasterBuffer->scanLine(spans->y) + x; - qt_memfill32(dest + 1, dest[0], length - 1); - length = 0; - } + op.destStore64(data->rasterBuffer, x, spans->y, &color, 1); + spanfill_from_first(data->rasterBuffer, bpp, x, spans->y, length); + length = 0; } while (length) { int l = qMin(BufferSize, length); - QRgba64 *dest = op.destFetch64((QRgba64 *)buffer, data->rasterBuffer, x, spans->y, l); + QRgba64 *dest = op.destFetch64(buffer, data->rasterBuffer, x, spans->y, l); op.funcSolid64(dest, l, color, spans->coverage); if (op.destStore64) op.destStore64(data->rasterBuffer, x, spans->y, dest, l); @@ -4401,6 +4545,9 @@ void blend_color_generic_rgb64(int count, const QSpan *spans, void *userData) } ++spans; } +#else + blend_color_generic(count, spans, userData); +#endif } static void blend_color_rgb16(int count, const QSpan *spans, void *userData) @@ -4413,16 +4560,16 @@ static void blend_color_rgb16(int count, const QSpan *spans, void *userData) from qt_gradient_quint16 with minimal overhead. */ QPainter::CompositionMode mode = data->rasterBuffer->compositionMode; - if (mode == QPainter::CompositionMode_SourceOver && data->solid.color.isOpaque()) + if (mode == QPainter::CompositionMode_SourceOver && data->solidColor.isOpaque()) mode = QPainter::CompositionMode_Source; if (mode == QPainter::CompositionMode_Source) { // inline for performance - ushort c = data->solid.color.toRgb16(); + ushort c = data->solidColor.toRgb16(); while (count--) { ushort *target = ((ushort *)data->rasterBuffer->scanLine(spans->y)) + spans->x; if (spans->coverage == 255) { - QT_MEMFILL_USHORT(target, spans->len, c); + qt_memfill(target, c, spans->len); } else { ushort color = BYTE_MUL_RGB16(c, spans->coverage); int ialpha = 255 - spans->coverage; @@ -4439,7 +4586,7 @@ static void blend_color_rgb16(int count, const QSpan *spans, void *userData) if (mode == QPainter::CompositionMode_SourceOver) { while (count--) { - uint color = BYTE_MUL(data->solid.color.toArgb32(), spans->coverage); + uint color = BYTE_MUL(data->solidColor.toArgb32(), spans->coverage); int ialpha = qAlpha(~color); ushort c = qConvertRgb32To16(color); ushort *target = ((ushort *)data->rasterBuffer->scanLine(spans->y)) + spans->x; @@ -4544,8 +4691,8 @@ struct QBlendBase BlendType *dest; - BlendType buffer[BufferSize]; - BlendType src_buffer[BufferSize]; + alignas(8) BlendType buffer[BufferSize]; + alignas(8) BlendType src_buffer[BufferSize]; }; class BlendSrcGeneric : public QBlendBase<uint> @@ -4574,11 +4721,12 @@ public: } }; -class BlendSrcGenericRGB64 : public QBlendBase<quint64> +#if QT_CONFIG(raster_64bit) +class BlendSrcGenericRGB64 : public QBlendBase<QRgba64> { public: BlendSrcGenericRGB64(QSpanData *d, const Operator &o) - : QBlendBase<quint64>(d, o) + : QBlendBase<QRgba64>(d, o) { } @@ -4587,23 +4735,24 @@ public: return op.func64 && op.destFetch64; } - const quint64 *fetch(int x, int y, int len) + const QRgba64 *fetch(int x, int y, int len) { - dest = (quint64 *)op.destFetch64((QRgba64 *)buffer, data->rasterBuffer, x, y, len); - return (const quint64 *)op.srcFetch64((QRgba64 *)src_buffer, &op, data, y, x, len); + dest = op.destFetch64(buffer, data->rasterBuffer, x, y, len); + return op.srcFetch64(src_buffer, &op, data, y, x, len); } - void process(int, int, int len, int coverage, const quint64 *src, int offset) + void process(int, int, int len, int coverage, const QRgba64 *src, int offset) { - op.func64((QRgba64 *)dest + offset, (const QRgba64 *)src + offset, len, coverage); + op.func64(dest + offset, src + offset, len, coverage); } void store(int x, int y, int len) { if (op.destStore64) - op.destStore64(data->rasterBuffer, x, y, (QRgba64 *)dest, len); + op.destStore64(data->rasterBuffer, x, y, dest, len); } }; +#endif static void blend_src_generic(int count, const QSpan *spans, void *userData) { @@ -4612,6 +4761,7 @@ static void blend_src_generic(int count, const QSpan *spans, void *userData) handleSpans(count, spans, data, blend); } +#if QT_CONFIG(raster_64bit) static void blend_src_generic_rgb64(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); @@ -4625,6 +4775,7 @@ static void blend_src_generic_rgb64(int count, const QSpan *spans, void *userDat handleSpans(count, spans, data, blend32); } } +#endif static void blend_untransformed_generic(int count, const QSpan *spans, void *userData) { @@ -4671,6 +4822,7 @@ static void blend_untransformed_generic(int count, const QSpan *spans, void *use } } +#if QT_CONFIG(raster_64bit) static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); @@ -4680,8 +4832,8 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi 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[BufferSize]; - quint64 src_buffer[BufferSize]; + alignas(8) QRgba64 buffer[BufferSize]; + alignas(8) QRgba64 src_buffer[BufferSize]; const int image_width = data->texture.width; const int image_height = data->texture.height; @@ -4705,8 +4857,8 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; while (length) { int l = qMin(BufferSize, length); - const QRgba64 *src = op.srcFetch64((QRgba64 *)src_buffer, &op, data, sy, sx, l); - QRgba64 *dest = op.destFetch64((QRgba64 *)buffer, data->rasterBuffer, x, spans->y, l); + const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, l); + QRgba64 *dest = op.destFetch64(buffer, data->rasterBuffer, x, spans->y, l); op.func64(dest, src, l, coverage); if (op.destStore64) op.destStore64(data->rasterBuffer, x, spans->y, dest, l); @@ -4719,6 +4871,7 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi ++spans; } } +#endif static void blend_untransformed_argb(int count, const QSpan *spans, void *userData) { @@ -4913,6 +5066,7 @@ static void blend_tiled_generic(int count, const QSpan *spans, void *userData) } } +#if QT_CONFIG(raster_64bit) static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); @@ -4922,8 +5076,8 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD 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[BufferSize]; - quint64 src_buffer[BufferSize]; + alignas(8) QRgba64 buffer[BufferSize]; + alignas(8) QRgba64 src_buffer[BufferSize]; const int image_width = data->texture.width; const int image_height = data->texture.height; @@ -4952,7 +5106,7 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD int sl = qMin(image_width, length); if (sx > 0 && sl > 0) { int l = qMin(image_width - sx, sl); - const QRgba64 *src = op.srcFetch64((QRgba64 *)src_buffer, &op, data, sy, sx, l); + const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, l); op.destStore64(data->rasterBuffer, x, y, src, l); x += l; sx += l; @@ -4962,7 +5116,7 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD } if (sl > 0) { Q_ASSERT(sx == 0); - const QRgba64 *src = op.srcFetch64((QRgba64 *)src_buffer, &op, data, sy, sx, sl); + const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, sl); op.destStore64(data->rasterBuffer, x, y, src, sl); x += sl; sx += sl; @@ -4994,8 +5148,8 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD int l = qMin(image_width - sx, length); if (BufferSize < l) l = BufferSize; - const QRgba64 *src = op.srcFetch64((QRgba64 *)src_buffer, &op, data, sy, sx, l); - QRgba64 *dest = op.destFetch64((QRgba64 *)buffer, data->rasterBuffer, x, spans->y, l); + const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, l); + QRgba64 *dest = op.destFetch64(buffer, data->rasterBuffer, x, spans->y, l); op.func64(dest, src, l, coverage); if (op.destStore64) op.destStore64(data->rasterBuffer, x, spans->y, dest, l); @@ -5008,6 +5162,7 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD ++spans; } } +#endif static void blend_tiled_argb(int count, const QSpan *spans, void *userData) { @@ -5184,6 +5339,7 @@ static const ProcessSpans processTextureSpansGeneric[NBlendTypes] = { blend_src_generic // TransformedBilinearTiled }; +#if QT_CONFIG(raster_64bit) static const ProcessSpans processTextureSpansGeneric64[NBlendTypes] = { blend_untransformed_generic_rgb64, // Untransformed blend_tiled_generic_rgb64, // Tiled @@ -5192,6 +5348,7 @@ static const ProcessSpans processTextureSpansGeneric64[NBlendTypes] = { blend_src_generic_rgb64, // TransformedBilinear blend_src_generic_rgb64 // TransformedBilinearTiled }; +#endif void qBlendTexture(int count, const QSpan *spans, void *userData) { @@ -5205,6 +5362,7 @@ void qBlendTexture(int count, const QSpan *spans, void *userData) case QImage::Format_RGB16: proc = processTextureSpansRGB16[blendType]; break; +#if QT_CONFIG(raster_64bit) #if defined(__SSE2__) || defined(__ARM_NEON__) || (Q_PROCESSOR_WORDSIZE == 8) case QImage::Format_ARGB32: case QImage::Format_RGBA8888: @@ -5216,8 +5374,10 @@ void qBlendTexture(int count, const QSpan *spans, void *userData) case QImage::Format_RGBX64: case QImage::Format_RGBA64: case QImage::Format_RGBA64_Premultiplied: + case QImage::Format_Grayscale16: proc = processTextureSpansGeneric64[blendType]; break; +#endif // QT_CONFIG(raster_64bit) case QImage::Format_Invalid: Q_UNREACHABLE(); return; @@ -5228,7 +5388,117 @@ void qBlendTexture(int count, const QSpan *spans, void *userData) proc(count, spans, userData); } -template <class DST> Q_STATIC_TEMPLATE_FUNCTION +static void blend_vertical_gradient_argb(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + LinearGradientValues linear; + getLinearGradientValues(&linear, data); + + CompositionFunctionSolid funcSolid = + functionForModeSolid[data->rasterBuffer->compositionMode]; + + /* + The logic for vertical gradient calculations is a mathematically + reduced copy of that in fetchLinearGradient() - which is basically: + + qreal ry = data->m22 * (y + 0.5) + data->dy; + qreal t = linear.dy*ry + linear.off; + t *= (GRADIENT_STOPTABLE_SIZE - 1); + quint32 color = + qt_gradient_pixel_fixed(&data->gradient, + int(t * FIXPT_SIZE)); + + This has then been converted to fixed point to improve performance. + */ + const int gss = GRADIENT_STOPTABLE_SIZE - 1; + int yinc = int((linear.dy * data->m22 * gss) * FIXPT_SIZE); + int off = int((((linear.dy * (data->m22 * qreal(0.5) + data->dy) + linear.off) * gss) * FIXPT_SIZE)); + + while (count--) { + int y = spans->y; + int x = spans->x; + + quint32 *dst = (quint32 *)(data->rasterBuffer->scanLine(y)) + x; + quint32 color = + qt_gradient_pixel_fixed(&data->gradient, yinc * y + off); + + funcSolid(dst, spans->len, color, spans->coverage); + ++spans; + } +} + +template<ProcessSpans blend_color> +static void blend_vertical_gradient(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + LinearGradientValues linear; + getLinearGradientValues(&linear, data); + + // Based on the same logic as blend_vertical_gradient_argb. + + const int gss = GRADIENT_STOPTABLE_SIZE - 1; + int yinc = int((linear.dy * data->m22 * gss) * FIXPT_SIZE); + int off = int((((linear.dy * (data->m22 * qreal(0.5) + data->dy) + linear.off) * gss) * FIXPT_SIZE)); + + while (count--) { + int y = spans->y; + +#if QT_CONFIG(raster_64bit) + data->solidColor = qt_gradient_pixel64_fixed(&data->gradient, yinc * y + off); +#else + data->solidColor = QRgba64::fromArgb32(qt_gradient_pixel_fixed(&data->gradient, yinc * y + off)); +#endif + blend_color(1, spans, userData); + ++spans; + } +} + +void qBlendGradient(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + bool isVerticalGradient = + data->txop <= QTransform::TxScale && + data->type == QSpanData::LinearGradient && + data->gradient.linear.end.x == data->gradient.linear.origin.x; + switch (data->rasterBuffer->format) { + case QImage::Format_RGB16: + if (isVerticalGradient) + return blend_vertical_gradient<blend_color_rgb16>(count, spans, userData); + return blend_src_generic(count, spans, userData); + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + if (isVerticalGradient) + return blend_vertical_gradient_argb(count, spans, userData); + return blend_src_generic(count, spans, userData); +#if QT_CONFIG(raster_64bit) +#if defined(__SSE2__) || defined(__ARM_NEON__) || (Q_PROCESSOR_WORDSIZE == 8) + case QImage::Format_ARGB32: + case QImage::Format_RGBA8888: +#endif + case QImage::Format_BGR30: + case QImage::Format_A2BGR30_Premultiplied: + case QImage::Format_RGB30: + case QImage::Format_A2RGB30_Premultiplied: + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + if (isVerticalGradient) + return blend_vertical_gradient<blend_color_generic_rgb64>(count, spans, userData); + return blend_src_generic_rgb64(count, spans, userData); +#endif // QT_CONFIG(raster_64bit) + case QImage::Format_Invalid: + break; + default: + if (isVerticalGradient) + return blend_vertical_gradient<blend_color_generic>(count, spans, userData); + return blend_src_generic(count, spans, userData); + } + Q_UNREACHABLE(); +} + +template <class DST> static inline void qt_bitmapblit_template(QRasterBuffer *rasterBuffer, int x, int y, DST color, const uchar *map, @@ -5290,103 +5560,6 @@ inline void qt_bitmapblit_template(QRasterBuffer *rasterBuffer, } } -static void qt_gradient_argb32(int count, const QSpan *spans, void *userData) -{ - QSpanData *data = reinterpret_cast<QSpanData *>(userData); - - bool isVerticalGradient = - data->txop <= QTransform::TxScale && - data->type == QSpanData::LinearGradient && - data->gradient.linear.end.x == data->gradient.linear.origin.x; - - if (isVerticalGradient) { - LinearGradientValues linear; - getLinearGradientValues(&linear, data); - - CompositionFunctionSolid funcSolid = - functionForModeSolid[data->rasterBuffer->compositionMode]; - - /* - The logic for vertical gradient calculations is a mathematically - reduced copy of that in fetchLinearGradient() - which is basically: - - qreal ry = data->m22 * (y + 0.5) + data->dy; - qreal t = linear.dy*ry + linear.off; - t *= (GRADIENT_STOPTABLE_SIZE - 1); - quint32 color = - qt_gradient_pixel_fixed(&data->gradient, - int(t * FIXPT_SIZE)); - - This has then been converted to fixed point to improve performance. - */ - const int gss = GRADIENT_STOPTABLE_SIZE - 1; - int yinc = int((linear.dy * data->m22 * gss) * FIXPT_SIZE); - int off = int((((linear.dy * (data->m22 * qreal(0.5) + data->dy) + linear.off) * gss) * FIXPT_SIZE)); - - while (count--) { - int y = spans->y; - int x = spans->x; - - quint32 *dst = (quint32 *)(data->rasterBuffer->scanLine(y)) + x; - quint32 color = - qt_gradient_pixel_fixed(&data->gradient, yinc * y + off); - - funcSolid(dst, spans->len, color, spans->coverage); - ++spans; - } - - } else { - blend_src_generic(count, spans, userData); - } -} - -static void qt_gradient_quint16(int count, const QSpan *spans, void *userData) -{ - QSpanData *data = reinterpret_cast<QSpanData *>(userData); - - bool isVerticalGradient = - data->txop <= QTransform::TxScale && - data->type == QSpanData::LinearGradient && - data->gradient.linear.end.x == data->gradient.linear.origin.x; - - if (isVerticalGradient) { - - LinearGradientValues linear; - getLinearGradientValues(&linear, data); - - /* - The logic for vertical gradient calculations is a mathematically - reduced copy of that in fetchLinearGradient() - which is basically: - - qreal ry = data->m22 * (y + 0.5) + data->dy; - qreal t = linear.dy*ry + linear.off; - t *= (GRADIENT_STOPTABLE_SIZE - 1); - quint32 color = - qt_gradient_pixel_fixed(&data->gradient, - int(t * FIXPT_SIZE)); - - This has then been converted to fixed point to improve performance. - */ - const int gss = GRADIENT_STOPTABLE_SIZE - 1; - int yinc = int((linear.dy * data->m22 * gss) * FIXPT_SIZE); - int off = int((((linear.dy * (data->m22 * qreal(0.5) + data->dy) + linear.off) * gss) * FIXPT_SIZE)); - - // Save the fillData since we overwrite it when setting solid.color. - QGradientData gradient = data->gradient; - while (count--) { - int y = spans->y; - - data->solid.color = QRgba64::fromArgb32(qt_gradient_pixel_fixed(&gradient, yinc * y + off)); - blend_color_rgb16(1, spans, userData); - ++spans; - } - data->gradient = gradient; - - } else { - blend_src_generic(count, spans, userData); - } -} - inline static void qt_bitmapblit_argb32(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 &color, const uchar *map, @@ -5424,7 +5597,36 @@ inline static void qt_bitmapblit_quint16(QRasterBuffer *rasterBuffer, map, mapWidth, mapHeight, mapStride); } -static inline void alphamapblend_generic(int coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorProfile *colorProfile) +static inline void grayBlendPixel(quint32 *dst, int coverage, QRgba64 srcLinear, const QColorTrcLut *colorProfile) +{ + // Do a gammacorrected gray alphablend... + const QRgba64 dstLinear = colorProfile ? colorProfile->toLinear64(*dst) : QRgba64::fromArgb32(*dst); + + QRgba64 blend = interpolate255(srcLinear, coverage, dstLinear, 255 - coverage); + + *dst = colorProfile ? colorProfile->fromLinear64(blend) : toArgb32(blend); +} + +static inline void alphamapblend_argb32(quint32 *dst, int coverage, QRgba64 srcLinear, quint32 src, const QColorTrcLut *colorProfile) +{ + if (coverage == 0) { + // nothing + } else if (coverage == 255) { + *dst = src; + } else if (!colorProfile) { + *dst = INTERPOLATE_PIXEL_255(src, coverage, *dst, 255 - coverage); + } else { + if (*dst >= 0xff000000) { + grayBlendPixel(dst, coverage, srcLinear, colorProfile); + } else { + // Give up and do a naive gray alphablend. Needed to deal with ARGB32 and invalid ARGB32_premultiplied, see QTBUG-60571 + *dst = INTERPOLATE_PIXEL_255(src, coverage, *dst, 255 - coverage); + } + } +} + +#if QT_CONFIG(raster_64bit) +static inline void alphamapblend_generic(int coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorTrcLut *colorProfile) { if (coverage == 0) { // nothing @@ -5459,7 +5661,7 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer, if (color.isTransparent()) return; - const QColorProfile *colorProfile = nullptr; + const QColorTrcLut *colorProfile = nullptr; if (useGammaCorrection) colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text(); @@ -5472,7 +5674,7 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer, srcColor = colorProfile->toLinear(srcColor.unpremultiplied()).premultiplied(); } - quint64 buffer[BufferSize]; + alignas(8) QRgba64 buffer[BufferSize]; const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format]; const DestStoreProc64 destStore64 = destStoreProc64[rasterBuffer->format]; @@ -5482,7 +5684,7 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer, int length = mapWidth; while (length > 0) { int l = qMin(BufferSize, length); - QRgba64 *dest = destFetch64((QRgba64*)buffer, rasterBuffer, i, y + ly, l); + QRgba64 *dest = destFetch64(buffer, rasterBuffer, i, y + ly, l); for (int j=0; j < l; ++j) { const int coverage = map[j + (i - x)]; alphamapblend_generic(coverage, dest, j, srcColor, color, colorProfile); @@ -5512,7 +5714,7 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer, if (end <= start) continue; Q_ASSERT(end - start <= BufferSize); - QRgba64 *dest = destFetch64((QRgba64*)buffer, rasterBuffer, start, clip.y, end - start); + QRgba64 *dest = destFetch64(buffer, rasterBuffer, start, clip.y, end - start); for (int xp=start; xp<end; ++xp) { const int coverage = map[xp - x]; @@ -5525,6 +5727,85 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer, } // for (yp -> bottom) } } +#else +static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer, + int x, int y, const QRgba64 &color, + const uchar *map, + int mapWidth, int mapHeight, int mapStride, + const QClipData *clip, bool useGammaCorrection) +{ + if (color.isTransparent()) + return; + + const quint32 c = color.toArgb32(); + + const QColorTrcLut *colorProfile = nullptr; + + if (useGammaCorrection) + colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text(); + + QRgba64 srcColor = color; + if (colorProfile) { + if (color.isOpaque()) + srcColor = colorProfile->toLinear(srcColor); + else + srcColor = colorProfile->toLinear(srcColor.unpremultiplied()).premultiplied(); + } + + quint32 buffer[BufferSize]; + const DestFetchProc destFetch = destFetchProc[rasterBuffer->format]; + const DestStoreProc destStore = destStoreProc[rasterBuffer->format]; + + if (!clip) { + for (int ly = 0; ly < mapHeight; ++ly) { + int i = x; + int length = mapWidth; + while (length > 0) { + int l = qMin(BufferSize, length); + quint32 *dest = destFetch(buffer, rasterBuffer, i, y + ly, l); + for (int j=0; j < l; ++j) { + const int coverage = map[j + (i - x)]; + alphamapblend_argb32(dest + j, coverage, srcColor, c, colorProfile); + } + if (destStore) + destStore(rasterBuffer, i, y + ly, dest, l); + length -= l; + i += l; + } + map += mapStride; + } + } else { + int bottom = qMin(y + mapHeight, rasterBuffer->height()); + + int top = qMax(y, 0); + map += (top - y) * mapStride; + + const_cast<QClipData *>(clip)->initialize(); + for (int yp = top; yp<bottom; ++yp) { + const QClipData::ClipLine &line = clip->m_clipLines[yp]; + + for (int i=0; i<line.count; ++i) { + const QSpan &clip = line.spans[i]; + + int start = qMax<int>(x, clip.x); + int end = qMin<int>(x + mapWidth, clip.x + clip.len); + if (end <= start) + continue; + Q_ASSERT(end - start <= BufferSize); + quint32 *dest = destFetch(buffer, rasterBuffer, start, clip.y, end - start); + + for (int xp=start; xp<end; ++xp) { + const int coverage = map[xp - x]; + alphamapblend_argb32(dest + xp - x, coverage, srcColor, color, colorProfile); + } + if (destStore) + destStore(rasterBuffer, start, clip.y, dest, end - start); + } // for (i -> line.count) + map += mapStride; + } // for (yp -> bottom) + } +} +#endif static inline void alphamapblend_quint16(int coverage, quint16 *dest, int x, const quint16 srcColor) { @@ -5585,44 +5866,6 @@ void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer, } } -static inline void rgbBlendPixel(quint32 *dst, int coverage, QRgba64 slinear, const QColorProfile *colorProfile) -{ - // Do a gammacorrected RGB alphablend... - const QRgba64 dlinear = colorProfile ? colorProfile->toLinear64(*dst) : QRgba64::fromArgb32(*dst); - - QRgba64 blend = rgbBlend(dlinear, slinear, coverage); - - *dst = colorProfile ? colorProfile->fromLinear64(blend) : toArgb32(blend); -} - -static inline void grayBlendPixel(quint32 *dst, int coverage, QRgba64 srcLinear, const QColorProfile *colorProfile) -{ - // Do a gammacorrected gray alphablend... - const QRgba64 dstLinear = colorProfile ? colorProfile->toLinear64(*dst) : QRgba64::fromArgb32(*dst); - - QRgba64 blend = interpolate255(srcLinear, coverage, dstLinear, 255 - coverage); - - *dst = colorProfile ? colorProfile->fromLinear64(blend) : toArgb32(blend); -} - -static inline void alphamapblend_argb32(quint32 *dst, int coverage, QRgba64 srcLinear, quint32 src, const QColorProfile *colorProfile) -{ - if (coverage == 0) { - // nothing - } else if (coverage == 255) { - *dst = src; - } else if (!colorProfile) { - *dst = INTERPOLATE_PIXEL_255(src, coverage, *dst, 255 - coverage); - } else { - if (*dst >= 0xff000000) { - grayBlendPixel(dst, coverage, srcLinear, colorProfile); - } else { - // Give up and do a naive gray alphablend. Needed to deal with ARGB32 and invalid ARGB32_premultiplied, see QTBUG-60571 - *dst = INTERPOLATE_PIXEL_255(src, coverage, *dst, 255 - coverage); - } - } -} - static void qt_alphamapblit_argb32(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 &color, const uchar *map, @@ -5635,7 +5878,7 @@ static void qt_alphamapblit_argb32(QRasterBuffer *rasterBuffer, if (color.isTransparent()) return; - const QColorProfile *colorProfile = nullptr; + const QColorTrcLut *colorProfile = nullptr; if (useGammaCorrection) colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text(); @@ -5691,6 +5934,16 @@ static inline int qRgbAvg(QRgb rgb) return (qRed(rgb) * 5 + qGreen(rgb) * 6 + qBlue(rgb) * 5) / 16; } +static inline void rgbBlendPixel(quint32 *dst, int coverage, QRgba64 slinear, const QColorTrcLut *colorProfile) +{ + // Do a gammacorrected RGB alphablend... + const QRgba64 dlinear = colorProfile ? colorProfile->toLinear64(*dst) : QRgba64::fromArgb32(*dst); + + QRgba64 blend = rgbBlend(dlinear, slinear, coverage); + + *dst = colorProfile ? colorProfile->fromLinear64(blend) : toArgb32(blend); +} + static inline QRgb rgbBlend(QRgb d, QRgb s, uint rgbAlpha) { #if defined(__SSE2__) @@ -5731,7 +5984,8 @@ static inline QRgb rgbBlend(QRgb d, QRgb s, uint rgbAlpha) #endif } -static inline void alphargbblend_generic(uint coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorProfile *colorProfile) +#if QT_CONFIG(raster_64bit) +static inline void alphargbblend_generic(uint coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorTrcLut *colorProfile) { if (coverage == 0xff000000) { // nothing @@ -5752,8 +6006,9 @@ static inline void alphargbblend_generic(uint coverage, QRgba64 *dest, int x, co } } } +#endif -static inline void alphargbblend_argb32(quint32 *dst, uint coverage, const QRgba64 &srcLinear, quint32 src, const QColorProfile *colorProfile) +static inline void alphargbblend_argb32(quint32 *dst, uint coverage, const QRgba64 &srcLinear, quint32 src, const QColorTrcLut *colorProfile) { if (coverage == 0xff000000) { // nothing @@ -5770,6 +6025,7 @@ static inline void alphargbblend_argb32(quint32 *dst, uint coverage, const QRgba } } +#if QT_CONFIG(raster_64bit) static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 &color, const uint *src, int mapWidth, int mapHeight, int srcStride, @@ -5778,7 +6034,7 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer, if (color.isTransparent()) return; - const QColorProfile *colorProfile = nullptr; + const QColorTrcLut *colorProfile = nullptr; if (useGammaCorrection) colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text(); @@ -5791,7 +6047,7 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer, srcColor = colorProfile->toLinear(srcColor.unpremultiplied()).premultiplied(); } - quint64 buffer[BufferSize]; + alignas(8) QRgba64 buffer[BufferSize]; const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format]; const DestStoreProc64 destStore64 = destStoreProc64[rasterBuffer->format]; @@ -5801,7 +6057,7 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer, int length = mapWidth; while (length > 0) { int l = qMin(BufferSize, length); - QRgba64 *dest = destFetch64((QRgba64*)buffer, rasterBuffer, i, y + ly, l); + QRgba64 *dest = destFetch64(buffer, rasterBuffer, i, y + ly, l); for (int j=0; j < l; ++j) { const uint coverage = src[j + (i - x)]; alphargbblend_generic(coverage, dest, j, srcColor, color, colorProfile); @@ -5831,7 +6087,7 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer, if (end <= start) continue; Q_ASSERT(end - start <= BufferSize); - QRgba64 *dest = destFetch64((QRgba64*)buffer, rasterBuffer, start, clip.y, end - start); + QRgba64 *dest = destFetch64(buffer, rasterBuffer, start, clip.y, end - start); for (int xp=start; xp<end; ++xp) { const uint coverage = src[xp - x]; @@ -5844,6 +6100,84 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer, } // for (yp -> bottom) } } +#else +static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer, + int x, int y, const QRgba64 &color, + const uint *src, int mapWidth, int mapHeight, int srcStride, + const QClipData *clip, bool useGammaCorrection) +{ + if (color.isTransparent()) + return; + + const quint32 c = color.toArgb32(); + + const QColorTrcLut *colorProfile = nullptr; + + if (useGammaCorrection) + colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text(); + + QRgba64 srcColor = color; + if (colorProfile) { + if (color.isOpaque()) + srcColor = colorProfile->toLinear(srcColor); + else + srcColor = colorProfile->toLinear(srcColor.unpremultiplied()).premultiplied(); + } + + quint32 buffer[BufferSize]; + const DestFetchProc destFetch = destFetchProc[rasterBuffer->format]; + const DestStoreProc destStore = destStoreProc[rasterBuffer->format]; + + if (!clip) { + for (int ly = 0; ly < mapHeight; ++ly) { + int i = x; + int length = mapWidth; + while (length > 0) { + int l = qMin(BufferSize, length); + quint32 *dest = destFetch(buffer, rasterBuffer, i, y + ly, l); + for (int j=0; j < l; ++j) { + const uint coverage = src[j + (i - x)]; + alphargbblend_argb32(dest + j, coverage, srcColor, c, colorProfile); + } + if (destStore) + destStore(rasterBuffer, i, y + ly, dest, l); + length -= l; + i += l; + } + src += srcStride; + } + } else { + int bottom = qMin(y + mapHeight, rasterBuffer->height()); + + int top = qMax(y, 0); + src += (top - y) * srcStride; + + const_cast<QClipData *>(clip)->initialize(); + for (int yp = top; yp<bottom; ++yp) { + const QClipData::ClipLine &line = clip->m_clipLines[yp]; + + for (int i=0; i<line.count; ++i) { + const QSpan &clip = line.spans[i]; + + int start = qMax<int>(x, clip.x); + int end = qMin<int>(x + mapWidth, clip.x + clip.len); + if (end <= start) + continue; + Q_ASSERT(end - start <= BufferSize); + quint32 *dest = destFetch(buffer, rasterBuffer, start, clip.y, end - start); + + for (int xp=start; xp<end; ++xp) { + const uint coverage = src[xp - x]; + alphargbblend_argb32(dest + xp - start, coverage, srcColor, c, colorProfile); + } + if (destStore) + destStore(rasterBuffer, start, clip.y, dest, end - start); + } // for (i -> line.count) + src += srcStride; + } // for (yp -> bottom) + } +} +#endif static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 &color, @@ -5855,7 +6189,7 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer, const quint32 c = color.toArgb32(); - const QColorProfile *colorProfile = nullptr; + const QColorTrcLut *colorProfile = nullptr; if (useGammaCorrection) colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text(); @@ -6007,29 +6341,25 @@ static void qt_rectfill_quint64(QRasterBuffer *rasterBuffer, DrawHelper qDrawHelper[QImage::NImageFormats] = { // Format_Invalid, - { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, // Format_Mono, { blend_color_generic, - blend_src_generic, 0, 0, 0, 0 }, // Format_MonoLSB, { blend_color_generic, - blend_src_generic, 0, 0, 0, 0 }, // Format_Indexed8, { blend_color_generic, - blend_src_generic, 0, 0, 0, 0 }, // Format_RGB32, { blend_color_argb, - qt_gradient_argb32, qt_bitmapblit_argb32, qt_alphamapblit_argb32, qt_alphargbblit_argb32, @@ -6038,7 +6368,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_ARGB32, { blend_color_generic, - blend_src_generic, qt_bitmapblit_argb32, qt_alphamapblit_argb32, qt_alphargbblit_argb32, @@ -6047,7 +6376,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_ARGB32_Premultiplied { blend_color_argb, - qt_gradient_argb32, qt_bitmapblit_argb32, qt_alphamapblit_argb32, qt_alphargbblit_argb32, @@ -6056,7 +6384,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_RGB16 { blend_color_rgb16, - qt_gradient_quint16, qt_bitmapblit_quint16, qt_alphamapblit_quint16, qt_alphargbblit_generic, @@ -6065,7 +6392,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_ARGB8565_Premultiplied { blend_color_generic, - blend_src_generic, 0, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6074,7 +6400,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_RGB666 { blend_color_generic, - blend_src_generic, 0, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6083,7 +6408,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_ARGB6666_Premultiplied { blend_color_generic, - blend_src_generic, 0, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6092,7 +6416,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_RGB555 { blend_color_generic, - blend_src_generic, 0, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6101,7 +6424,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_ARGB8555_Premultiplied { blend_color_generic, - blend_src_generic, 0, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6110,7 +6432,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_RGB888 { blend_color_generic, - blend_src_generic, 0, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6119,7 +6440,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_RGB444 { blend_color_generic, - blend_src_generic, 0, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6128,7 +6448,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_ARGB4444_Premultiplied { blend_color_generic, - blend_src_generic, 0, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6137,7 +6456,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_RGBX8888 { blend_color_generic, - blend_src_generic, qt_bitmapblit_rgba8888, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6146,7 +6464,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_RGBA8888 { blend_color_generic, - blend_src_generic, qt_bitmapblit_rgba8888, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6155,7 +6472,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_RGB8888_Premultiplied { blend_color_generic, - blend_src_generic, qt_bitmapblit_rgba8888, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6164,7 +6480,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_BGR30 { blend_color_generic_rgb64, - blend_src_generic_rgb64, qt_bitmapblit_rgb30<PixelOrderBGR>, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6173,7 +6488,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_A2BGR30_Premultiplied { blend_color_generic_rgb64, - blend_src_generic_rgb64, qt_bitmapblit_rgb30<PixelOrderBGR>, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6182,7 +6496,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_RGB30 { blend_color_generic_rgb64, - blend_src_generic_rgb64, qt_bitmapblit_rgb30<PixelOrderRGB>, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6191,7 +6504,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_A2RGB30_Premultiplied { blend_color_generic_rgb64, - blend_src_generic_rgb64, qt_bitmapblit_rgb30<PixelOrderRGB>, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6200,7 +6512,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_Alpha8 { blend_color_generic, - blend_src_generic, 0, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6209,7 +6520,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_Grayscale8 { blend_color_generic, - blend_src_generic, 0, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6218,7 +6528,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_RGBX64 { blend_color_generic_rgb64, - blend_src_generic_rgb64, 0, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6227,7 +6536,6 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_RGBA64 { blend_color_generic_rgb64, - blend_src_generic_rgb64, 0, qt_alphamapblit_generic, qt_alphargbblit_generic, @@ -6236,82 +6544,98 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = // Format_RGBA64_Premultiplied { blend_color_generic_rgb64, - blend_src_generic_rgb64, 0, qt_alphamapblit_generic, qt_alphargbblit_generic, qt_rectfill_quint64 }, + // Format_Grayscale16 + { + blend_color_generic_rgb64, + 0, + qt_alphamapblit_generic, + qt_alphargbblit_generic, + qt_rectfill_quint16 + }, }; -#if defined(Q_CC_MSVC) && !defined(_MIPS_) -template <class T> -inline void qt_memfill_template(T *dest, T color, int count) +#if !defined(__SSE2__) +void qt_memfill64(quint64 *dest, quint64 color, qsizetype count) { - while (count--) - *dest++ = color; + qt_memfill_template<quint64>(dest, color, count); } +#endif -#else - -template <class T> -inline void qt_memfill_template(T *dest, T color, int count) +#if defined(QT_COMPILER_SUPPORTS_SSSE3) && defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && !defined(Q_CC_CLANG) +__attribute__((optimize("no-tree-vectorize"))) +#endif +void qt_memfill24(quint24 *dest, quint24 color, qsizetype count) { - int n = (count + 7) / 8; - switch (count & 0x07) - { - case 0: do { *dest++ = color; Q_FALLTHROUGH(); - case 7: *dest++ = color; Q_FALLTHROUGH(); - case 6: *dest++ = color; Q_FALLTHROUGH(); - case 5: *dest++ = color; Q_FALLTHROUGH(); - case 4: *dest++ = color; Q_FALLTHROUGH(); - case 3: *dest++ = color; Q_FALLTHROUGH(); - case 2: *dest++ = color; Q_FALLTHROUGH(); - case 1: *dest++ = color; - } while (--n > 0); - } -} +# ifdef QT_COMPILER_SUPPORTS_SSSE3 + extern void qt_memfill24_ssse3(quint24 *, quint24, qsizetype); + if (qCpuHasFeature(SSSE3)) + return qt_memfill24_ssse3(dest, color, count); +# endif -template <> -inline void qt_memfill_template(quint16 *dest, quint16 value, int count) -{ - if (count < 3) { - switch (count) { - case 2: *dest++ = value; Q_FALLTHROUGH(); - case 1: *dest = value; - } + const quint32 v = color; + quint24 *end = dest + count; + + // prolog: align dest to 32bit + while ((quintptr(dest) & 0x3) && dest < end) { + *dest++ = v; + } + if (dest >= end) return; + + const uint val1 = qFromBigEndian((v << 8) | (v >> 16)); + const uint val2 = qFromBigEndian((v << 16) | (v >> 8)); + const uint val3 = qFromBigEndian((v << 24) | (v >> 0)); + + for ( ; dest <= (end - 4); dest += 4) { + quint32 *dst = reinterpret_cast<quint32 *>(dest); + dst[0] = val1; + dst[1] = val2; + dst[2] = val3; } - const int align = (quintptr)(dest) & 0x3; - switch (align) { - case 2: *dest++ = value; --count; + // less than 4px left + switch (end - dest) { + case 3: + *dest++ = v; + Q_FALLTHROUGH(); + case 2: + *dest++ = v; + Q_FALLTHROUGH(); + case 1: + *dest++ = v; + } +} + +void qt_memfill16(quint16 *dest, quint16 value, qsizetype count) +{ + const int align = quintptr(dest) & 0x3; + if (align) { + *dest++ = value; + --count; } - const quint32 value32 = (value << 16) | value; - qt_memfill(reinterpret_cast<quint32*>(dest), value32, count / 2); if (count & 0x1) dest[count - 1] = value; -} -#endif -void qt_memfill64(quint64 *dest, quint64 color, int count) -{ - qt_memfill_template<quint64>(dest, color, count); + const quint32 value32 = (value << 16) | value; + qt_memfill32(reinterpret_cast<quint32*>(dest), value32, count / 2); } -#if !defined(__SSE2__) -void qt_memfill16(quint16 *dest, quint16 color, int count) -{ - qt_memfill_template<quint16>(dest, color, count); -} -#endif #if !defined(__SSE2__) && !defined(__ARM_NEON__) && !defined(__MIPS_DSP__) -void qt_memfill32(quint32 *dest, quint32 color, int count) +void qt_memfill32(quint32 *dest, quint32 color, qsizetype count) { qt_memfill_template<quint32>(dest, color, count); } #endif +#ifdef __SSE2__ +decltype(qt_memfill32_sse2) *qt_memfill32 = nullptr; +decltype(qt_memfill64_sse2) *qt_memfill64 = nullptr; +#endif #ifdef QT_COMPILER_SUPPORTS_SSE4_1 template<QtPixelOrder> void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *); @@ -6325,6 +6649,10 @@ static void qInitDrawhelperFunctions() qInitBlendFunctions(); #ifdef __SSE2__ +# ifndef __AVX2__ + qt_memfill32 = qt_memfill32_sse2; + qt_memfill64 = qt_memfill64_sse2; +# endif qDrawHelper[QImage::Format_RGB32].bitmapBlit = qt_bitmapblit32_sse2; qDrawHelper[QImage::Format_ARGB32].bitmapBlit = qt_bitmapblit32_sse2; qDrawHelper[QImage::Format_ARGB32_Premultiplied].bitmapBlit = qt_bitmapblit32_sse2; @@ -6401,6 +6729,14 @@ static void qInitDrawhelperFunctions() const QVector<QRgb> *, QDitherInfo *); extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count, const QVector<QRgb> *, QDitherInfo *); + extern const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int count, + const QVector<QRgb> *, QDitherInfo *); + extern const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int count, + const QVector<QRgb> *, QDitherInfo *); + extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_sse4(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *); + extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_sse4(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *); extern void QT_FASTCALL storeARGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *); extern void QT_FASTCALL storeRGBA8888FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, @@ -6413,10 +6749,18 @@ static void qInitDrawhelperFunctions() const QVector<QRgb> *, QDitherInfo *); extern void QT_FASTCALL destStore64ARGB32_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length); extern void QT_FASTCALL destStore64RGBA8888_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length); +# ifndef __AVX2__ qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_sse4; qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_sse4; qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_sse4; qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_sse4; + qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_sse4; + qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_sse4; + qPixelLayouts[QImage::Format_RGBA8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_sse4; + qPixelLayouts[QImage::Format_RGBA8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_sse4; + qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_sse4; + qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_sse4; +# endif qPixelLayouts[QImage::Format_ARGB32].storeFromARGB32PM = storeARGB32FromARGB32PM_sse4; qPixelLayouts[QImage::Format_RGBA8888].storeFromARGB32PM = storeRGBA8888FromARGB32PM_sse4; qPixelLayouts[QImage::Format_RGBX8888].storeFromARGB32PM = storeRGBXFromARGB32PM_sse4; @@ -6424,13 +6768,17 @@ static void qInitDrawhelperFunctions() qPixelLayouts[QImage::Format_A2RGB30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>; qStoreFromRGBA64PM[QImage::Format_ARGB32] = storeARGB32FromRGBA64PM_sse4; qStoreFromRGBA64PM[QImage::Format_RGBA8888] = storeRGBA8888FromRGBA64PM_sse4; +#if QT_CONFIG(raster_64bit) destStoreProc64[QImage::Format_ARGB32] = destStore64ARGB32_sse4; destStoreProc64[QImage::Format_RGBA8888] = destStore64RGBA8888_sse4; +#endif } #endif #if defined(QT_COMPILER_SUPPORTS_AVX2) if (qCpuHasFeature(ArchHaswell)) { + qt_memfill32 = qt_memfill32_avx2; + qt_memfill64 = qt_memfill64_avx2; extern void qt_blend_rgb32_on_rgb32_avx2(uchar *destPixels, int dbpl, const uchar *srcPixels, int sbpl, int w, int h, int const_alpha); @@ -6447,18 +6795,19 @@ static void qInitDrawhelperFunctions() qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_avx2; extern void QT_FASTCALL comp_func_Source_avx2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha); - extern void QT_FASTCALL comp_func_Source_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha); extern void QT_FASTCALL comp_func_SourceOver_avx2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha); - extern void QT_FASTCALL comp_func_SourceOver_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha); extern void QT_FASTCALL comp_func_solid_SourceOver_avx2(uint *destPixels, int length, uint color, uint const_alpha); - extern void QT_FASTCALL comp_func_solid_SourceOver_rgb64_avx2(QRgba64 *destPixels, int length, QRgba64 color, uint const_alpha); - qt_functionForMode_C[QPainter::CompositionMode_Source] = comp_func_Source_avx2; - qt_functionForMode64_C[QPainter::CompositionMode_Source] = comp_func_Source_rgb64_avx2; qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_avx2; - qt_functionForMode64_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_rgb64_avx2; qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_avx2; +#if QT_CONFIG(raster_64bit) + extern void QT_FASTCALL comp_func_Source_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha); + extern void QT_FASTCALL comp_func_SourceOver_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha); + extern void QT_FASTCALL comp_func_solid_SourceOver_rgb64_avx2(QRgba64 *destPixels, int length, QRgba64 color, uint const_alpha); + qt_functionForMode64_C[QPainter::CompositionMode_Source] = comp_func_Source_rgb64_avx2; + qt_functionForMode64_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_rgb64_avx2; qt_functionForModeSolid64_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_rgb64_avx2; +#endif extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_scale_helper_avx2(uint *b, uint *end, const QTextureData &image, int &fx, int &fy, int fdx, int /*fdy*/); @@ -6470,6 +6819,28 @@ static void qInitDrawhelperFunctions() bilinearFastTransformHelperARGB32PM[0][SimpleScaleTransform] = fetchTransformedBilinearARGB32PM_simple_scale_helper_avx2; bilinearFastTransformHelperARGB32PM[0][DownscaleTransform] = fetchTransformedBilinearARGB32PM_downscale_helper_avx2; bilinearFastTransformHelperARGB32PM[0][FastRotateTransform] = fetchTransformedBilinearARGB32PM_fast_rotate_helper_avx2; + + extern void QT_FASTCALL convertARGB32ToARGB32PM_avx2(uint *buffer, int count, const QVector<QRgb> *); + extern void QT_FASTCALL convertRGBA8888ToARGB32PM_avx2(uint *buffer, int count, const QVector<QRgb> *); + extern const uint *QT_FASTCALL fetchARGB32ToARGB32PM_avx2(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *); + extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_avx2(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *); + qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_avx2; + qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_avx2; + qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_avx2; + qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_avx2; + +#if QT_CONFIG(raster_64bit) + extern const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_avx2(QRgba64 *, const uint *, int, const QVector<QRgb> *, QDitherInfo *); + extern const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_avx2(QRgba64 *, const uint *, int count, const QVector<QRgb> *, QDitherInfo *); + extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_avx2(QRgba64 *, const uchar *, int, int, const QVector<QRgb> *, QDitherInfo *); + extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_avx2(QRgba64 *, const uchar *, int, int, const QVector<QRgb> *, QDitherInfo *); + qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_avx2; + qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_avx2; + qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_avx2; + qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_avx2; +#endif } #endif @@ -6505,6 +6876,14 @@ static void qInitDrawhelperFunctions() const QVector<QRgb> *, QDitherInfo *); extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_neon(uint *buffer, const uchar *src, int index, int count, const QVector<QRgb> *, QDitherInfo *); + extern const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count, + const QVector<QRgb> *, QDitherInfo *); + extern const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count, + const QVector<QRgb> *, QDitherInfo *); + extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_neon(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *); + extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_neon(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *); extern void QT_FASTCALL storeARGB32FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *); extern void QT_FASTCALL storeRGBA8888FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count, @@ -6514,10 +6893,16 @@ static void qInitDrawhelperFunctions() qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_neon; qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_neon; qPixelLayouts[QImage::Format_ARGB32].storeFromARGB32PM = storeARGB32FromARGB32PM_neon; + qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_neon; + qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_neon; qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_neon; qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_neon; qPixelLayouts[QImage::Format_RGBA8888].storeFromARGB32PM = storeRGBA8888FromARGB32PM_neon; + qPixelLayouts[QImage::Format_RGBA8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_neon; + qPixelLayouts[QImage::Format_RGBA8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_neon; qPixelLayouts[QImage::Format_RGBX8888].storeFromARGB32PM = storeRGBXFromARGB32PM_neon; + qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_neon; + qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_neon; #endif #if defined(ENABLE_PIXMAN_DRAWHELPERS) diff --git a/src/gui/painting/qdrawhelper_avx2.cpp b/src/gui/painting/qdrawhelper_avx2.cpp index ec6643deed..fb50cb6a50 100644 --- a/src/gui/painting/qdrawhelper_avx2.cpp +++ b/src/gui/painting/qdrawhelper_avx2.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -38,6 +39,7 @@ ****************************************************************************/ #include "qdrawhelper_p.h" +#include "qdrawhelper_x86_p.h" #include "qdrawingprimitive_sse2_p.h" #include "qrgba64_p.h" @@ -53,7 +55,8 @@ enum { // Vectorized blend functions: // See BYTE_MUL_SSE2 for details. -inline static void BYTE_MUL_AVX2(__m256i &pixelVector, const __m256i &alphaChannel, const __m256i &colorMask, const __m256i &half) +inline static void Q_DECL_VECTORCALL +BYTE_MUL_AVX2(__m256i &pixelVector, __m256i alphaChannel, __m256i colorMask, __m256i half) { __m256i pixelVectorAG = _mm256_srli_epi16(pixelVector, 8); __m256i pixelVectorRB = _mm256_and_si256(pixelVector, colorMask); @@ -72,7 +75,8 @@ inline static void BYTE_MUL_AVX2(__m256i &pixelVector, const __m256i &alphaChann pixelVector = _mm256_or_si256(pixelVectorAG, pixelVectorRB); } -inline static void BYTE_MUL_RGB64_AVX2(__m256i &pixelVector, const __m256i &alphaChannel, const __m256i &colorMask, const __m256i &half) +inline static void Q_DECL_VECTORCALL +BYTE_MUL_RGB64_AVX2(__m256i &pixelVector, __m256i alphaChannel, __m256i colorMask, __m256i half) { __m256i pixelVectorAG = _mm256_srli_epi32(pixelVector, 16); __m256i pixelVectorRB = _mm256_and_si256(pixelVector, colorMask); @@ -92,7 +96,8 @@ inline static void BYTE_MUL_RGB64_AVX2(__m256i &pixelVector, const __m256i &alph } // See INTERPOLATE_PIXEL_255_SSE2 for details. -inline static void INTERPOLATE_PIXEL_255_AVX2(const __m256i &srcVector, __m256i &dstVector, const __m256i &alphaChannel, const __m256i &oneMinusAlphaChannel, const __m256i &colorMask, const __m256i &half) +inline static void Q_DECL_VECTORCALL +INTERPOLATE_PIXEL_255_AVX2(__m256i srcVector, __m256i &dstVector, __m256i alphaChannel, __m256i oneMinusAlphaChannel, __m256i colorMask, __m256i half) { const __m256i srcVectorAG = _mm256_srli_epi16(srcVector, 8); const __m256i dstVectorAG = _mm256_srli_epi16(dstVector, 8); @@ -114,7 +119,8 @@ inline static void INTERPOLATE_PIXEL_255_AVX2(const __m256i &srcVector, __m256i dstVector = _mm256_or_si256(finalAG, finalRB); } -inline static void INTERPOLATE_PIXEL_RGB64_AVX2(const __m256i &srcVector, __m256i &dstVector, const __m256i &alphaChannel, const __m256i &oneMinusAlphaChannel, const __m256i &colorMask, const __m256i &half) +inline static void Q_DECL_VECTORCALL +INTERPOLATE_PIXEL_RGB64_AVX2(__m256i srcVector, __m256i &dstVector, __m256i alphaChannel, __m256i oneMinusAlphaChannel, __m256i colorMask, __m256i half) { const __m256i srcVectorAG = _mm256_srli_epi32(srcVector, 16); const __m256i dstVectorAG = _mm256_srli_epi32(dstVector, 16); @@ -138,7 +144,7 @@ inline static void INTERPOLATE_PIXEL_RGB64_AVX2(const __m256i &srcVector, __m256 // See BLEND_SOURCE_OVER_ARGB32_SSE2 for details. -inline static void BLEND_SOURCE_OVER_ARGB32_AVX2(quint32 *dst, const quint32 *src, const int length) +inline static void Q_DECL_VECTORCALL BLEND_SOURCE_OVER_ARGB32_AVX2(quint32 *dst, const quint32 *src, const int length) { const __m256i half = _mm256_set1_epi16(0x80); const __m256i one = _mm256_set1_epi16(0xff); @@ -209,7 +215,8 @@ inline static void BLEND_SOURCE_OVER_ARGB32_AVX2(quint32 *dst, const quint32 *sr // See BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_SSE2 for details. -inline static void BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_AVX2(quint32 *dst, const quint32 *src, const int length, const int const_alpha) +inline static void Q_DECL_VECTORCALL +BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_AVX2(quint32 *dst, const quint32 *src, const int length, const int const_alpha) { int x = 0; @@ -316,6 +323,66 @@ void qt_blend_rgb32_on_rgb32_avx2(uchar *destPixels, int dbpl, } } +static Q_NEVER_INLINE +void Q_DECL_VECTORCALL qt_memfillXX_avx2(uchar *dest, __m256i value256, qsizetype bytes) +{ + __m128i value128 = _mm256_castsi256_si128(value256); + + // main body + __m256i *dst256 = reinterpret_cast<__m256i *>(dest); + uchar *end = dest + bytes; + while (reinterpret_cast<uchar *>(dst256 + 4) <= end) { + _mm256_storeu_si256(dst256 + 0, value256); + _mm256_storeu_si256(dst256 + 1, value256); + _mm256_storeu_si256(dst256 + 2, value256); + _mm256_storeu_si256(dst256 + 3, value256); + dst256 += 4; + } + + // first epilogue: fewer than 128 bytes / 32 entries + bytes = end - reinterpret_cast<uchar *>(dst256); + switch (bytes / sizeof(value256)) { + case 3: _mm256_storeu_si256(dst256++, value256); Q_FALLTHROUGH(); + case 2: _mm256_storeu_si256(dst256++, value256); Q_FALLTHROUGH(); + case 1: _mm256_storeu_si256(dst256++, value256); + } + + // second epilogue: fewer than 32 bytes + __m128i *dst128 = reinterpret_cast<__m128i *>(dst256); + if (bytes & sizeof(value128)) + _mm_storeu_si128(dst128++, value128); + + // third epilogue: fewer than 16 bytes + if (bytes & 8) + _mm_storel_epi64(reinterpret_cast<__m128i *>(end - 8), value128); +} + +void qt_memfill64_avx2(quint64 *dest, quint64 value, qsizetype count) +{ +#if defined(Q_CC_GNU) && !defined(Q_CC_CLANG) && !defined(Q_CC_INTEL) + // work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80820 + __m128i value64 = _mm_set_epi64x(0, value); // _mm_cvtsi64_si128(value); +# ifdef Q_PROCESSOR_X86_64 + asm ("" : "+x" (value64)); +# endif + __m256i value256 = _mm256_broadcastq_epi64(value64); +#else + __m256i value256 = _mm256_set1_epi64x(value); +#endif + + qt_memfillXX_avx2(reinterpret_cast<uchar *>(dest), value256, count * sizeof(quint64)); +} + +void qt_memfill32_avx2(quint32 *dest, quint32 value, qsizetype count) +{ + if (count % 2) { + // odd number of pixels, round to even + *dest++ = value; + --count; + } + qt_memfillXX_avx2(reinterpret_cast<uchar *>(dest), _mm256_set1_epi32(value), count * sizeof(quint32)); +} + void QT_FASTCALL comp_func_SourceOver_avx2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha) { Q_ASSERT(const_alpha < 256); @@ -329,6 +396,7 @@ void QT_FASTCALL comp_func_SourceOver_avx2(uint *destPixels, const uint *srcPixe BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_AVX2(dst, src, length, const_alpha); } +#if QT_CONFIG(raster_64bit) void QT_FASTCALL comp_func_SourceOver_rgb64_avx2(QRgba64 *dst, const QRgba64 *src, int length, uint const_alpha) { Q_ASSERT(const_alpha < 256); // const_alpha is in [0-255] @@ -386,6 +454,7 @@ void QT_FASTCALL comp_func_SourceOver_rgb64_avx2(QRgba64 *dst, const QRgba64 *sr blend_pixel(dst[x], src[x], const_alpha); } } +#endif void QT_FASTCALL comp_func_Source_avx2(uint *dst, const uint *src, int length, uint const_alpha) { @@ -418,6 +487,7 @@ void QT_FASTCALL comp_func_Source_avx2(uint *dst, const uint *src, int length, u } } +#if QT_CONFIG(raster_64bit) void QT_FASTCALL comp_func_Source_rgb64_avx2(QRgba64 *dst, const QRgba64 *src, int length, uint const_alpha) { Q_ASSERT(const_alpha < 256); // const_alpha is in [0-255] @@ -450,6 +520,7 @@ void QT_FASTCALL comp_func_Source_rgb64_avx2(QRgba64 *dst, const QRgba64 *src, i dst[x] = interpolate65535(src[x], ca, dst[x], cia); } } +#endif void QT_FASTCALL comp_func_solid_SourceOver_avx2(uint *destPixels, int length, uint color, uint const_alpha) { @@ -482,6 +553,7 @@ void QT_FASTCALL comp_func_solid_SourceOver_avx2(uint *destPixels, int length, u } } +#if QT_CONFIG(raster_64bit) void QT_FASTCALL comp_func_solid_SourceOver_rgb64_avx2(QRgba64 *destPixels, int length, QRgba64 color, uint const_alpha) { Q_ASSERT(const_alpha < 256); // const_alpha is in [0-255] @@ -512,6 +584,7 @@ void QT_FASTCALL comp_func_solid_SourceOver_rgb64_avx2(QRgba64 *destPixels, int destPixels[x] = color + multiplyAlpha65535(destPixels[x], minusAlphaOfColor); } } +#endif #define interpolate_4_pixels_16_avx2(tlr1, tlr2, blr1, blr2, distx, disty, colorMask, v_256, b) \ { \ @@ -928,6 +1001,233 @@ void QT_FASTCALL fetchTransformedBilinearARGB32PM_fast_rotate_helper_avx2(uint * } } +static inline __m256i epilogueMaskFromCount(qsizetype count) +{ + Q_ASSERT(count > 0); + static const __m256i offsetMask = _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7); + return _mm256_add_epi32(offsetMask, _mm256_set1_epi32(-count)); +} + +template<bool RGBA> +static void convertARGBToARGB32PM_avx2(uint *buffer, const uint *src, qsizetype count) +{ + qsizetype i = 0; + const __m256i alphaMask = _mm256_set1_epi32(0xff000000); + const __m256i rgbaMask = _mm256_broadcastsi128_si256(_mm_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15)); + const __m256i shuffleMask = _mm256_broadcastsi128_si256(_mm_setr_epi8(6, 7, 6, 7, 6, 7, 6, 7, 14, 15, 14, 15, 14, 15, 14, 15)); + const __m256i half = _mm256_set1_epi16(0x0080); + const __m256i zero = _mm256_setzero_si256(); + + for (; i < count - 7; i += 8) { + __m256i srcVector = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + i)); + if (!_mm256_testz_si256(srcVector, alphaMask)) { + // keep the two _mm_test[zc]_siXXX next to each other + bool cf = _mm256_testc_si256(srcVector, alphaMask); + if (RGBA) + srcVector = _mm256_shuffle_epi8(srcVector, rgbaMask); + if (!cf) { + __m256i src1 = _mm256_unpacklo_epi8(srcVector, zero); + __m256i src2 = _mm256_unpackhi_epi8(srcVector, zero); + __m256i alpha1 = _mm256_shuffle_epi8(src1, shuffleMask); + __m256i alpha2 = _mm256_shuffle_epi8(src2, shuffleMask); + src1 = _mm256_mullo_epi16(src1, alpha1); + src2 = _mm256_mullo_epi16(src2, alpha2); + src1 = _mm256_add_epi16(src1, _mm256_srli_epi16(src1, 8)); + src2 = _mm256_add_epi16(src2, _mm256_srli_epi16(src2, 8)); + src1 = _mm256_add_epi16(src1, half); + src2 = _mm256_add_epi16(src2, half); + src1 = _mm256_srli_epi16(src1, 8); + src2 = _mm256_srli_epi16(src2, 8); + src1 = _mm256_blend_epi16(src1, alpha1, 0x88); + src2 = _mm256_blend_epi16(src2, alpha2, 0x88); + srcVector = _mm256_packus_epi16(src1, src2); + _mm256_storeu_si256(reinterpret_cast<__m256i *>(buffer + i), srcVector); + } else { + if (buffer != src || RGBA) + _mm256_storeu_si256(reinterpret_cast<__m256i *>(buffer + i), srcVector); + } + } else { + _mm256_storeu_si256(reinterpret_cast<__m256i *>(buffer + i), zero); + } + } + + if (i < count) { + const __m256i epilogueMask = epilogueMaskFromCount(count - i); + __m256i srcVector = _mm256_maskload_epi32(reinterpret_cast<const int *>(src + i), epilogueMask); + const __m256i epilogueAlphaMask = _mm256_blendv_epi8(_mm256_setzero_si256(), alphaMask, epilogueMask); + + if (!_mm256_testz_si256(srcVector, epilogueAlphaMask)) { + // keep the two _mm_test[zc]_siXXX next to each other + bool cf = _mm256_testc_si256(srcVector, epilogueAlphaMask); + if (RGBA) + srcVector = _mm256_shuffle_epi8(srcVector, rgbaMask); + if (!cf) { + __m256i src1 = _mm256_unpacklo_epi8(srcVector, zero); + __m256i src2 = _mm256_unpackhi_epi8(srcVector, zero); + __m256i alpha1 = _mm256_shuffle_epi8(src1, shuffleMask); + __m256i alpha2 = _mm256_shuffle_epi8(src2, shuffleMask); + src1 = _mm256_mullo_epi16(src1, alpha1); + src2 = _mm256_mullo_epi16(src2, alpha2); + src1 = _mm256_add_epi16(src1, _mm256_srli_epi16(src1, 8)); + src2 = _mm256_add_epi16(src2, _mm256_srli_epi16(src2, 8)); + src1 = _mm256_add_epi16(src1, half); + src2 = _mm256_add_epi16(src2, half); + src1 = _mm256_srli_epi16(src1, 8); + src2 = _mm256_srli_epi16(src2, 8); + src1 = _mm256_blend_epi16(src1, alpha1, 0x88); + src2 = _mm256_blend_epi16(src2, alpha2, 0x88); + srcVector = _mm256_packus_epi16(src1, src2); + _mm256_maskstore_epi32(reinterpret_cast<int *>(buffer + i), epilogueMask, srcVector); + } else { + if (buffer != src || RGBA) + _mm256_maskstore_epi32(reinterpret_cast<int *>(buffer + i), epilogueMask, srcVector); + } + } else { + _mm256_maskstore_epi32(reinterpret_cast<int *>(buffer + i), epilogueMask, zero); + } + } +} + +void QT_FASTCALL convertARGB32ToARGB32PM_avx2(uint *buffer, int count, const QVector<QRgb> *) +{ + convertARGBToARGB32PM_avx2<false>(buffer, buffer, count); +} + +void QT_FASTCALL convertRGBA8888ToARGB32PM_avx2(uint *buffer, int count, const QVector<QRgb> *) +{ + convertARGBToARGB32PM_avx2<true>(buffer, buffer, count); +} + +const uint *QT_FASTCALL fetchARGB32ToARGB32PM_avx2(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + convertARGBToARGB32PM_avx2<false>(buffer, reinterpret_cast<const uint *>(src) + index, count); + return buffer; +} + +const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_avx2(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + convertARGBToARGB32PM_avx2<true>(buffer, reinterpret_cast<const uint *>(src) + index, count); + return buffer; +} + +template<bool RGBA> +static void convertARGBToRGBA64PM_avx2(QRgba64 *buffer, const uint *src, qsizetype count) +{ + qsizetype i = 0; + const __m256i alphaMask = _mm256_set1_epi32(0xff000000); + const __m256i rgbaMask = _mm256_broadcastsi128_si256(_mm_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15)); + const __m256i shuffleMask = _mm256_broadcastsi128_si256(_mm_setr_epi8(6, 7, 6, 7, 6, 7, 6, 7, 14, 15, 14, 15, 14, 15, 14, 15)); + const __m256i zero = _mm256_setzero_si256(); + + for (; i < count - 7; i += 8) { + __m256i dst1, dst2; + __m256i srcVector = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + i)); + if (!_mm256_testz_si256(srcVector, alphaMask)) { + // keep the two _mm_test[zc]_siXXX next to each other + bool cf = _mm256_testc_si256(srcVector, alphaMask); + if (!RGBA) + srcVector = _mm256_shuffle_epi8(srcVector, rgbaMask); + + // The two unpack instructions unpack the low and upper halves of + // each 128-bit half of the 256-bit register. Here's the tracking + // of what's where: (p is 32-bit, P is 64-bit) + // as loaded: [ p1, p2, p3, p4; p5, p6, p7, p8 ] + // after permute4x64 [ p1, p2, p5, p6; p3, p4, p7, p8 ] + // after unpacklo/hi [ P1, P2; P3, P4 ] [ P5, P6; P7, P8 ] + srcVector = _mm256_permute4x64_epi64(srcVector, _MM_SHUFFLE(3, 1, 2, 0)); + + const __m256i src1 = _mm256_unpacklo_epi8(srcVector, srcVector); + const __m256i src2 = _mm256_unpackhi_epi8(srcVector, srcVector); + if (!cf) { + const __m256i alpha1 = _mm256_shuffle_epi8(src1, shuffleMask); + const __m256i alpha2 = _mm256_shuffle_epi8(src2, shuffleMask); + dst1 = _mm256_mulhi_epu16(src1, alpha1); + dst2 = _mm256_mulhi_epu16(src2, alpha2); + dst1 = _mm256_add_epi16(dst1, _mm256_srli_epi16(dst1, 15)); + dst2 = _mm256_add_epi16(dst2, _mm256_srli_epi16(dst2, 15)); + dst1 = _mm256_blend_epi16(dst1, src1, 0x88); + dst2 = _mm256_blend_epi16(dst2, src2, 0x88); + } else { + dst1 = src1; + dst2 = src2; + } + } else { + dst1 = dst2 = zero; + } + _mm256_storeu_si256(reinterpret_cast<__m256i *>(buffer + i), dst1); + _mm256_storeu_si256(reinterpret_cast<__m256i *>(buffer + i) + 1, dst2); + } + + if (i < count) { + __m256i epilogueMask = epilogueMaskFromCount(count - i); + const __m256i epilogueAlphaMask = _mm256_blendv_epi8(_mm256_setzero_si256(), alphaMask, epilogueMask); + __m256i dst1, dst2; + __m256i srcVector = _mm256_maskload_epi32(reinterpret_cast<const int *>(src + i), epilogueMask); + + if (!_mm256_testz_si256(srcVector, epilogueAlphaMask)) { + // keep the two _mm_test[zc]_siXXX next to each other + bool cf = _mm256_testc_si256(srcVector, epilogueAlphaMask); + if (!RGBA) + srcVector = _mm256_shuffle_epi8(srcVector, rgbaMask); + srcVector = _mm256_permute4x64_epi64(srcVector, _MM_SHUFFLE(3, 1, 2, 0)); + const __m256i src1 = _mm256_unpacklo_epi8(srcVector, srcVector); + const __m256i src2 = _mm256_unpackhi_epi8(srcVector, srcVector); + if (!cf) { + const __m256i alpha1 = _mm256_shuffle_epi8(src1, shuffleMask); + const __m256i alpha2 = _mm256_shuffle_epi8(src2, shuffleMask); + dst1 = _mm256_mulhi_epu16(src1, alpha1); + dst2 = _mm256_mulhi_epu16(src2, alpha2); + dst1 = _mm256_add_epi16(dst1, _mm256_srli_epi16(dst1, 15)); + dst2 = _mm256_add_epi16(dst2, _mm256_srli_epi16(dst2, 15)); + dst1 = _mm256_blend_epi16(dst1, src1, 0x88); + dst2 = _mm256_blend_epi16(dst2, src2, 0x88); + } else { + dst1 = src1; + dst2 = src2; + } + } else { + dst1 = dst2 = zero; + } + epilogueMask = _mm256_permute4x64_epi64(epilogueMask, _MM_SHUFFLE(3, 1, 2, 0)); + _mm256_maskstore_epi64(reinterpret_cast<qint64 *>(buffer + i), + _mm256_unpacklo_epi32(epilogueMask, epilogueMask), + dst1); + _mm256_maskstore_epi64(reinterpret_cast<qint64 *>(buffer + i + 4), + _mm256_unpackhi_epi32(epilogueMask, epilogueMask), + dst2); + } +} + +const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_avx2(QRgba64 *buffer, const uint *src, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + convertARGBToRGBA64PM_avx2<false>(buffer, src, count); + return buffer; +} + +const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_avx2(QRgba64 *buffer, const uint *src, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + convertARGBToRGBA64PM_avx2<true>(buffer, src, count); + return buffer; +} + +const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_avx2(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + convertARGBToRGBA64PM_avx2<false>(buffer, reinterpret_cast<const uint *>(src) + index, count); + return buffer; +} + +const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_avx2(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + convertARGBToRGBA64PM_avx2<true>(buffer, reinterpret_cast<const uint *>(src) + index, count); + return buffer; +} + QT_END_NAMESPACE #endif diff --git a/src/gui/painting/qdrawhelper_mips_dsp.cpp b/src/gui/painting/qdrawhelper_mips_dsp.cpp index e92a6606de..17597deb1d 100644 --- a/src/gui/painting/qdrawhelper_mips_dsp.cpp +++ b/src/gui/painting/qdrawhelper_mips_dsp.cpp @@ -43,7 +43,7 @@ QT_BEGIN_NAMESPACE -void qt_memfill32(quint32 *dest, quint32 color, int count) +void qt_memfill32(quint32 *dest, quint32 color, qsizetype count) { qt_memfill32_asm_mips_dsp(dest, color, count); } diff --git a/src/gui/painting/qdrawhelper_mips_dsp_p.h b/src/gui/painting/qdrawhelper_mips_dsp_p.h index 36c4af2732..a3d0410274 100644 --- a/src/gui/painting/qdrawhelper_mips_dsp_p.h +++ b/src/gui/painting/qdrawhelper_mips_dsp_p.h @@ -57,7 +57,7 @@ QT_BEGIN_NAMESPACE #if defined(QT_COMPILER_SUPPORTS_MIPS_DSP) -extern "C" void qt_memfill32_asm_mips_dsp(quint32 *dest, quint32 value, int count); +extern "C" void qt_memfill32_asm_mips_dsp(quint32 *dest, quint32 value, qsizetype count); extern "C" void comp_func_SourceOver_asm_mips_dsp(uint *dest, const uint *src, int length, uint const_alpha); diff --git a/src/gui/painting/qdrawhelper_neon.cpp b/src/gui/painting/qdrawhelper_neon.cpp index 629dfe2358..8196a87b24 100644 --- a/src/gui/painting/qdrawhelper_neon.cpp +++ b/src/gui/painting/qdrawhelper_neon.cpp @@ -47,10 +47,21 @@ QT_BEGIN_NAMESPACE -void qt_memfill32(quint32 *dest, quint32 value, int count) +void qt_memfill32(quint32 *dest, quint32 value, qsizetype count) { const int epilogueSize = count % 16; -#if !defined(Q_PROCESSOR_ARM_64) +#if defined(Q_CC_GHS) || defined(Q_CC_MSVC) + // inline assembler free version: + if (count >= 16) { + quint32 *const neonEnd = dest + count - epilogueSize; + const uint32x4_t valueVector1 = vdupq_n_u32(value); + const uint32x4x4_t valueVector4 = { valueVector1, valueVector1, valueVector1, valueVector1 }; + do { + vst4q_u32(dest, valueVector4); + dest += 16; + } while (dest != neonEnd); + } +#elif !defined(Q_PROCESSOR_ARM_64) if (count >= 16) { quint32 *const neonEnd = dest + count - epilogueSize; register uint32x4_t valueVector1 asm ("q0") = vdupq_n_u32(value); @@ -84,20 +95,20 @@ void qt_memfill32(quint32 *dest, quint32 value, int count) switch (epilogueSize) { - case 15: *dest++ = value; - case 14: *dest++ = value; - case 13: *dest++ = value; - case 12: *dest++ = value; - case 11: *dest++ = value; - case 10: *dest++ = value; - case 9: *dest++ = value; - case 8: *dest++ = value; - case 7: *dest++ = value; - case 6: *dest++ = value; - case 5: *dest++ = value; - case 4: *dest++ = value; - case 3: *dest++ = value; - case 2: *dest++ = value; + case 15: *dest++ = value; Q_FALLTHROUGH(); + case 14: *dest++ = value; Q_FALLTHROUGH(); + case 13: *dest++ = value; Q_FALLTHROUGH(); + case 12: *dest++ = value; Q_FALLTHROUGH(); + case 11: *dest++ = value; Q_FALLTHROUGH(); + case 10: *dest++ = value; Q_FALLTHROUGH(); + case 9: *dest++ = value; Q_FALLTHROUGH(); + case 8: *dest++ = value; Q_FALLTHROUGH(); + case 7: *dest++ = value; Q_FALLTHROUGH(); + case 6: *dest++ = value; Q_FALLTHROUGH(); + case 5: *dest++ = value; Q_FALLTHROUGH(); + case 4: *dest++ = value; Q_FALLTHROUGH(); + case 3: *dest++ = value; Q_FALLTHROUGH(); + case 2: *dest++ = value; Q_FALLTHROUGH(); case 1: *dest++ = value; } } @@ -791,7 +802,7 @@ void QT_FASTCALL qt_destStoreRGB16_neon(QRasterBuffer *rasterBuffer, int x, int void QT_FASTCALL comp_func_solid_SourceOver_neon(uint *destPixels, int length, uint color, uint const_alpha) { if ((const_alpha & qAlpha(color)) == 255) { - QT_MEMFILL_UINT(destPixels, length, color); + qt_memfill32(destPixels, color, length); } else { if (const_alpha != 255) color = BYTE_MUL(color, const_alpha); @@ -1149,6 +1160,72 @@ static inline void convertARGBToARGB32PM_neon(uint *buffer, const uint *src, int } } +template<bool RGBA> +static inline void convertARGB32ToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count) +{ + if (count <= 0) + return; + + const uint8x8_t shuffleMask = { 3, 3, 3, 3, 7, 7, 7, 7}; + const uint64x2_t blendMask = vdupq_n_u64(Q_UINT64_C(0xffff000000000000)); + + int i = 0; + for (; i < count-3; i += 4) { + uint32x4_t vs32 = vld1q_u32(src + i); + uint32x4_t alphaVector = vshrq_n_u32(vs32, 24); +#if defined(Q_PROCESSOR_ARM_64) + uint32_t alphaSum = vaddvq_u32(alphaVector); +#else + // no vaddvq_u32 + uint32x2_t tmp = vpadd_u32(vget_low_u32(alphaVector), vget_high_u32(alphaVector)); + uint32_t alphaSum = vget_lane_u32(vpadd_u32(tmp, tmp), 0); +#endif + if (alphaSum) { + if (!RGBA) + vs32 = vrgba2argb(vs32); + const uint8x16_t vs8 = vreinterpretq_u8_u32(vs32); + const uint8x16x2_t v = vzipq_u8(vs8, vs8); + if (alphaSum != 255 * 4) { + const uint8x8_t s1 = vreinterpret_u8_u32(vget_low_u32(vs32)); + const uint8x8_t s2 = vreinterpret_u8_u32(vget_high_u32(vs32)); + const uint8x8_t alpha1 = vtbl1_u8(s1, shuffleMask); + const uint8x8_t alpha2 = vtbl1_u8(s2, shuffleMask); + uint16x8_t src1 = vmull_u8(s1, alpha1); + uint16x8_t src2 = vmull_u8(s2, alpha2); + // convert from 0->(255x255) to 0->(255x257) + src1 = vsraq_n_u16(src1, src1, 7); + src2 = vsraq_n_u16(src2, src2, 7); + + // now restore alpha from the trivial conversion + const uint64x2_t d1 = vbslq_u64(blendMask, vreinterpretq_u64_u8(v.val[0]), vreinterpretq_u64_u16(src1)); + const uint64x2_t d2 = vbslq_u64(blendMask, vreinterpretq_u64_u8(v.val[1]), vreinterpretq_u64_u16(src2)); + + vst1q_u16((uint16_t *)buffer, vreinterpretq_u16_u64(d1)); + buffer += 2; + vst1q_u16((uint16_t *)buffer, vreinterpretq_u16_u64(d2)); + buffer += 2; + } else { + vst1q_u16((uint16_t *)buffer, vreinterpretq_u16_u8(v.val[0])); + buffer += 2; + vst1q_u16((uint16_t *)buffer, vreinterpretq_u16_u8(v.val[1])); + buffer += 2; + } + } else { + vst1q_u16((uint16_t *)buffer, vdupq_n_u16(0)); + buffer += 2; + vst1q_u16((uint16_t *)buffer, vdupq_n_u16(0)); + buffer += 2; + } + } + + SIMD_EPILOGUE(i, count, 3) { + uint s = src[i]; + if (RGBA) + s = RGBA2ARGB(s); + *buffer++ = QRgba64::fromArgb32(s).premultiplied(); + } +} + static inline float32x4_t reciprocal_mul_ps(float32x4_t a, float mul) { float32x4_t ia = vrecpeq_f32(a); // estimate 1/a @@ -1258,6 +1335,34 @@ const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_neon(uint *buffer, const uchar * return buffer; } +const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + convertARGB32ToRGBA64PM_neon<false>(buffer, src, count); + return buffer; +} + +const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + convertARGB32ToRGBA64PM_neon<true>(buffer, src, count); + return buffer; +} + +const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_neon(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + convertARGB32ToRGBA64PM_neon<false>(buffer, reinterpret_cast<const uint *>(src) + index, count); + return buffer; +} + +const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_neon(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + convertARGB32ToRGBA64PM_neon<true>(buffer, reinterpret_cast<const uint *>(src) + index, count); + return buffer; +} + void QT_FASTCALL storeRGB32FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *) { diff --git a/src/gui/painting/qdrawhelper_neon_p.h b/src/gui/painting/qdrawhelper_neon_p.h index 40475a9bde..19e1f21a3b 100644 --- a/src/gui/painting/qdrawhelper_neon_p.h +++ b/src/gui/painting/qdrawhelper_neon_p.h @@ -123,7 +123,7 @@ void qt_transform_image_rgb16_on_rgb16_neon(uchar *destPixels, int dbpl, const QTransform &targetRectTransform, int const_alpha); -void qt_memfill32_neon(quint32 *dest, quint32 value, int count); +void qt_memfill32_neon(quint32 *dest, quint32 value, qsizetype count); void qt_memrotate90_16_neon(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl); void qt_memrotate270_16_neon(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl); diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index fb08261205..9c5d525722 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -69,14 +69,18 @@ QT_BEGIN_NAMESPACE #if defined(Q_CC_GNU) -# define Q_STATIC_TEMPLATE_FUNCTION static # define Q_DECL_RESTRICT __restrict__ +# if defined(Q_PROCESSOR_X86_32) && defined(Q_CC_GNU) && !defined(Q_CC_CLANG) && !defined(Q_CC_INTEL) +# define Q_DECL_VECTORCALL __attribute__((sseregparm,regparm(3))) +# else +# define Q_DECL_VECTORCALL +# endif #elif defined(Q_CC_MSVC) -# define Q_STATIC_TEMPLATE_FUNCTION static # define Q_DECL_RESTRICT __restrict +# define Q_DECL_VECTORCALL __vectorcall #else -# define Q_STATIC_TEMPLATE_FUNCTION static # define Q_DECL_RESTRICT +# define Q_DECL_VECTORCALL #endif static const uint AMASK = 0xff000000; @@ -149,7 +153,6 @@ typedef void (*MemRotateFunc)(const uchar *srcPixels, int w, int h, int sbpl, uc struct DrawHelper { ProcessSpans blendColor; - ProcessSpans blendGradient; BitmapBlitFunc bitmapBlit; AlphamapBlitFunc alphamapBlit; AlphaRGBBlitFunc alphaRGBBlit; @@ -162,10 +165,33 @@ extern SrcOverTransformFunc qTransformFunctions[QImage::NImageFormats][QImage::N extern DrawHelper qDrawHelper[QImage::NImageFormats]; +struct quint24 { + quint24() = default; + quint24(uint value) + { + data[0] = uchar(value >> 16); + data[1] = uchar(value >> 8); + data[2] = uchar(value); + } + operator uint() const + { + return data[2] | (data[1] << 8) | (data[0] << 16); + } + + uchar data[3]; +}; + +void qBlendGradient(int count, const QSpan *spans, void *userData); void qBlendTexture(int count, const QSpan *spans, void *userData); -extern void qt_memfill64(quint64 *dest, quint64 value, int count); -extern void qt_memfill32(quint32 *dest, quint32 value, int count); -extern void qt_memfill16(quint16 *dest, quint16 value, int count); +#ifdef __SSE2__ +extern void (*qt_memfill64)(quint64 *dest, quint64 value, qsizetype count); +extern void (*qt_memfill32)(quint32 *dest, quint32 value, qsizetype count); +#else +extern void qt_memfill64(quint64 *dest, quint64 value, qsizetype count); +extern void qt_memfill32(quint32 *dest, quint32 value, qsizetype count); +#endif +extern void qt_memfill24(quint24 *dest, quint24 value, qsizetype count); +extern void qt_memfill16(quint16 *dest, quint16 value, qsizetype count); typedef void (QT_FASTCALL *CompositionFunction)(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha); typedef void (QT_FASTCALL *CompositionFunction64)(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha); @@ -222,11 +248,6 @@ struct Operator class QRasterPaintEngine; -struct QSolidData -{ - QRgba64 color; -}; - struct QLinearGradientData { struct { @@ -275,7 +296,9 @@ struct QGradientData #define GRADIENT_STOPTABLE_SIZE 1024 #define GRADIENT_STOPTABLE_SIZE_SHIFT 10 +#if QT_CONFIG(raster_64bit) const QRgba64 *colorTable64; //[GRADIENT_STOPTABLE_SIZE]; +#endif const QRgb *colorTable32; //[GRADIENT_STOPTABLE_SIZE]; uint alphaColor : 1; @@ -307,7 +330,7 @@ struct QTextureData struct QSpanData { - QSpanData() : tempImage(0) {} + QSpanData() : tempImage(nullptr) {} ~QSpanData() { delete tempImage; } QRasterBuffer *rasterBuffer; @@ -331,8 +354,8 @@ struct QSpanData int fast_matrix : 1; bool bilinear; QImage *tempImage; + QRgba64 solidColor; union { - QSolidData solid; QGradientData gradient; QTextureData texture; }; @@ -381,11 +404,13 @@ static inline uint qt_gradient_pixel(const QGradientData *data, qreal pos) return data->colorTable32[qt_gradient_clamp(data, ipos)]; } +#if QT_CONFIG(raster_64bit) static inline const QRgba64& qt_gradient_pixel64(const QGradientData *data, qreal pos) { int ipos = int(pos * (GRADIENT_STOPTABLE_SIZE - 1) + qreal(0.5)); return data->colorTable64[qt_gradient_clamp(data, ipos)]; } +#endif static inline qreal qRadialDeterminant(qreal a, qreal b, qreal c) { @@ -858,6 +883,8 @@ static Q_ALWAYS_INLINE uint BYTE_MUL_RGB16_32(uint x, uint a) { // qt_div_255 is a fast rounded division by 255 using an approximation that is accurate for all positive 16-bit integers static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE int qt_div_255(int x) { return (x + (x>>8) + 0x80) >> 8; } +static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE uint qt_div_257_floor(uint x) { return (x - (x >> 8)) >> 8; } +static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE uint qt_div_257(uint x) { return qt_div_257_floor(x + 128); } static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE uint qt_div_65535(uint x) { return (x + (x>>16) + 0x8000U) >> 16; } static Q_ALWAYS_INLINE uint qAlphaRgb30(uint c) @@ -868,76 +895,63 @@ static Q_ALWAYS_INLINE uint qAlphaRgb30(uint c) return a; } -struct quint24 { - quint24() = default; - quint24(uint value); - operator uint() const; - uchar data[3]; -}; - -inline quint24::quint24(uint value) +template <class T> inline void qt_memfill_template(T *dest, T color, qsizetype count) { - data[0] = uchar(value >> 16); - data[1] = uchar(value >> 8); - data[2] = uchar(value); + if (!count) + return; + + qsizetype n = (count + 7) / 8; + switch (count & 0x07) + { + case 0: do { *dest++ = color; Q_FALLTHROUGH(); + case 7: *dest++ = color; Q_FALLTHROUGH(); + case 6: *dest++ = color; Q_FALLTHROUGH(); + case 5: *dest++ = color; Q_FALLTHROUGH(); + case 4: *dest++ = color; Q_FALLTHROUGH(); + case 3: *dest++ = color; Q_FALLTHROUGH(); + case 2: *dest++ = color; Q_FALLTHROUGH(); + case 1: *dest++ = color; + } while (--n > 0); + } } -inline quint24::operator uint() const +template <class T> inline void qt_memfill(T *dest, T value, qsizetype count) { - return data[2] | (data[1] << 8) | (data[0] << 16); + qt_memfill_template(dest, value, count); } -template <class T> Q_STATIC_TEMPLATE_FUNCTION -void qt_memfill(T *dest, T value, int count); - -template<> inline void qt_memfill(quint64 *dest, quint64 color, int count) +template<> inline void qt_memfill(quint64 *dest, quint64 color, qsizetype count) { qt_memfill64(dest, color, count); } -template<> inline void qt_memfill(quint32 *dest, quint32 color, int count) +template<> inline void qt_memfill(quint32 *dest, quint32 color, qsizetype count) { qt_memfill32(dest, color, count); } -template<> inline void qt_memfill(quint16 *dest, quint16 color, int count) +template<> inline void qt_memfill(quint24 *dest, quint24 color, qsizetype count) { - qt_memfill16(dest, color, count); + qt_memfill24(dest, color, count); } -template<> inline void qt_memfill(quint8 *dest, quint8 color, int count) +template<> inline void qt_memfill(quint16 *dest, quint16 color, qsizetype count) { - memset(dest, color, count); + qt_memfill16(dest, color, count); } -template <class T> -inline void qt_memfill(T *dest, T value, int count) +template<> inline void qt_memfill(quint8 *dest, quint8 color, qsizetype count) { - if (!count) - return; - - int n = (count + 7) / 8; - switch (count & 0x07) - { - case 0: do { *dest++ = value; Q_FALLTHROUGH(); - case 7: *dest++ = value; Q_FALLTHROUGH(); - case 6: *dest++ = value; Q_FALLTHROUGH(); - case 5: *dest++ = value; Q_FALLTHROUGH(); - case 4: *dest++ = value; Q_FALLTHROUGH(); - case 3: *dest++ = value; Q_FALLTHROUGH(); - case 2: *dest++ = value; Q_FALLTHROUGH(); - case 1: *dest++ = value; - } while (--n > 0); - } + memset(dest, color, count); } -template <class T> Q_STATIC_TEMPLATE_FUNCTION +template <class T> static inline void qt_rectfill(T *dest, T value, int x, int y, int width, int height, qsizetype stride) { char *d = reinterpret_cast<char*>(dest + x) + y * stride; if (uint(stride) == (width * sizeof(T))) { - qt_memfill(reinterpret_cast<T*>(d), value, width * height); + qt_memfill(reinterpret_cast<T*>(d), value, qsizetype(width) * height); } else { for (int j = 0; j < height; ++j) { dest = reinterpret_cast<T*>(d); @@ -947,52 +961,6 @@ inline void qt_rectfill(T *dest, T value, } } -#define QT_MEMFILL_UINT(dest, length, color) \ - qt_memfill<quint32>(dest, color, length); - -#define QT_MEMFILL_USHORT(dest, length, color) \ - qt_memfill<quint16>(dest, color, length); - -#define QT_MEMCPY_REV_UINT(dest, src, length) \ -do { \ - /* Duff's device */ \ - uint *_d = (uint*)(dest) + length; \ - const uint *_s = (uint*)(src) + length; \ - int n = ((length) + 7) / 8; \ - switch ((length) & 0x07) \ - { \ - case 0: do { *--_d = *--_s; Q_FALLTHROUGH(); \ - case 7: *--_d = *--_s; Q_FALLTHROUGH(); \ - case 6: *--_d = *--_s; Q_FALLTHROUGH(); \ - case 5: *--_d = *--_s; Q_FALLTHROUGH(); \ - case 4: *--_d = *--_s; Q_FALLTHROUGH(); \ - case 3: *--_d = *--_s; Q_FALLTHROUGH(); \ - case 2: *--_d = *--_s; Q_FALLTHROUGH(); \ - case 1: *--_d = *--_s; \ - } while (--n > 0); \ - } \ -} while (false) - -#define QT_MEMCPY_USHORT(dest, src, length) \ -do { \ - /* Duff's device */ \ - ushort *_d = (ushort*)(dest); \ - const ushort *_s = (const ushort*)(src); \ - int n = ((length) + 7) / 8; \ - switch ((length) & 0x07) \ - { \ - case 0: do { *_d++ = *_s++; Q_FALLTHROUGH(); \ - case 7: *_d++ = *_s++; Q_FALLTHROUGH(); \ - case 6: *_d++ = *_s++; Q_FALLTHROUGH(); \ - case 5: *_d++ = *_s++; Q_FALLTHROUGH(); \ - case 4: *_d++ = *_s++; Q_FALLTHROUGH(); \ - case 3: *_d++ = *_s++; Q_FALLTHROUGH(); \ - case 2: *_d++ = *_s++; Q_FALLTHROUGH(); \ - case 1: *_d++ = *_s++; \ - } while (--n > 0); \ - } \ -} while (false) - inline ushort qConvertRgb32To16(uint c) { return (((c) >> 3) & 0x001f) diff --git a/src/gui/painting/qdrawhelper_sse2.cpp b/src/gui/painting/qdrawhelper_sse2.cpp index 3212ffdd2d..c82f41ec88 100644 --- a/src/gui/painting/qdrawhelper_sse2.cpp +++ b/src/gui/painting/qdrawhelper_sse2.cpp @@ -233,19 +233,71 @@ void QT_FASTCALL comp_func_Source_sse2(uint *dst, const uint *src, int length, u } } -void qt_memfill32(quint32 *dest, quint32 value, int count) +#ifndef __AVX2__ +static Q_NEVER_INLINE +void Q_DECL_VECTORCALL qt_memfillXX_aligned(void *dest, __m128i value128, quintptr bytecount) { - if (count < 7) { + __m128i *dst128 = reinterpret_cast<__m128i *>(dest); + __m128i *end128 = reinterpret_cast<__m128i *>(static_cast<uchar *>(dest) + bytecount); + + while (dst128 + 4 <= end128) { + _mm_store_si128(dst128 + 0, value128); + _mm_store_si128(dst128 + 1, value128); + _mm_store_si128(dst128 + 2, value128); + _mm_store_si128(dst128 + 3, value128); + dst128 += 4; + } + + bytecount %= 4 * sizeof(__m128i); + switch (bytecount / sizeof(__m128i)) { + case 3: _mm_store_si128(dst128++, value128); Q_FALLTHROUGH(); + case 2: _mm_store_si128(dst128++, value128); Q_FALLTHROUGH(); + case 1: _mm_store_si128(dst128++, value128); + } +} + +void qt_memfill64_sse2(quint64 *dest, quint64 value, qsizetype count) +{ + quintptr misaligned = quintptr(dest) % sizeof(__m128i); + if (misaligned && count) { +#if defined(Q_PROCESSOR_X86_32) + // Before SSE came out, the alignment of the stack used to be only 4 + // bytes and some OS/ABIs (notably, code generated by MSVC) still only + // align to that. In any case, we cannot count on the alignment of + // quint64 to be 8 -- see QtPrivate::AlignOf_WorkaroundForI386Abi in + // qglobal.h. + // + // If the pointer is not aligned to at least 8 bytes, then we'll never + // in turn hit a multiple of 16 for the qt_memfillXX_aligned call + // below. + if (Q_UNLIKELY(misaligned % sizeof(quint64))) + return qt_memfill_template(dest, value, count); +#endif + + *dest++ = value; + --count; + } + + if (count % 2) { + dest[count - 1] = value; + --count; + } + + qt_memfillXX_aligned(dest, _mm_set1_epi64x(value), count * sizeof(quint64)); +} + +void qt_memfill32_sse2(quint32 *dest, quint32 value, qsizetype count) +{ + if (count < 4) { + // this simplifies the code below: the first switch can fall through + // without checking the value of count switch (count) { - case 6: *dest++ = value; Q_FALLTHROUGH(); - case 5: *dest++ = value; Q_FALLTHROUGH(); - case 4: *dest++ = value; Q_FALLTHROUGH(); case 3: *dest++ = value; Q_FALLTHROUGH(); case 2: *dest++ = value; Q_FALLTHROUGH(); case 1: *dest = value; } return; - }; + } const int align = (quintptr)(dest) & 0xf; switch (align) { @@ -263,25 +315,9 @@ void qt_memfill32(quint32 *dest, quint32 value, int count) } } - int count128 = count / 4; - __m128i *dst128 = reinterpret_cast<__m128i*>(dest); - __m128i *end128 = dst128 + count128; - const __m128i value128 = _mm_set_epi32(value, value, value, value); - - while (dst128 + 3 < end128) { - _mm_stream_si128(dst128 + 0, value128); - _mm_stream_si128(dst128 + 1, value128); - _mm_stream_si128(dst128 + 2, value128); - _mm_stream_si128(dst128 + 3, value128); - dst128 += 4; - } - - switch (count128 & 0x3) { - case 3: _mm_stream_si128(dst128++, value128); Q_FALLTHROUGH(); - case 2: _mm_stream_si128(dst128++, value128); Q_FALLTHROUGH(); - case 1: _mm_stream_si128(dst128++, value128); - } + qt_memfillXX_aligned(dest, _mm_set1_epi32(value), count * sizeof(quint32)); } +#endif // !__AVX2__ void QT_FASTCALL comp_func_solid_SourceOver_sse2(uint *destPixels, int length, uint color, uint const_alpha) { @@ -314,28 +350,6 @@ void QT_FASTCALL comp_func_solid_SourceOver_sse2(uint *destPixels, int length, u } } -void qt_memfill16(quint16 *dest, quint16 value, int count) -{ - if (count < 3) { - switch (count) { - case 2: *dest++ = value; Q_FALLTHROUGH(); - case 1: *dest = value; - } - return; - } - - const int align = (quintptr)(dest) & 0x3; - switch (align) { - case 2: *dest++ = value; --count; - } - - const quint32 value32 = (value << 16) | value; - qt_memfill32(reinterpret_cast<quint32*>(dest), value32, count / 2); - - if (count & 0x1) - dest[count - 1] = value; -} - void qt_bitmapblit32_sse2_base(QRasterBuffer *rasterBuffer, int x, int y, quint32 color, const uchar *src, int width, int height, int stride) @@ -440,30 +454,30 @@ public: union Vect_buffer_i { Int32x4 v; int i[4]; }; union Vect_buffer_f { Float32x4 v; float f[4]; }; - static inline Float32x4 v_dup(float x) { return _mm_set1_ps(x); } - static inline Float32x4 v_dup(double x) { return _mm_set1_ps(x); } - static inline Int32x4 v_dup(int x) { return _mm_set1_epi32(x); } - static inline Int32x4 v_dup(uint x) { return _mm_set1_epi32(x); } + static inline Float32x4 Q_DECL_VECTORCALL v_dup(float x) { return _mm_set1_ps(x); } + static inline Float32x4 Q_DECL_VECTORCALL v_dup(double x) { return _mm_set1_ps(x); } + static inline Int32x4 Q_DECL_VECTORCALL v_dup(int x) { return _mm_set1_epi32(x); } + static inline Int32x4 Q_DECL_VECTORCALL v_dup(uint x) { return _mm_set1_epi32(x); } - static inline Float32x4 v_add(Float32x4 a, Float32x4 b) { return _mm_add_ps(a, b); } - static inline Int32x4 v_add(Int32x4 a, Int32x4 b) { return _mm_add_epi32(a, b); } + static inline Float32x4 Q_DECL_VECTORCALL v_add(Float32x4 a, Float32x4 b) { return _mm_add_ps(a, b); } + static inline Int32x4 Q_DECL_VECTORCALL v_add(Int32x4 a, Int32x4 b) { return _mm_add_epi32(a, b); } - static inline Float32x4 v_max(Float32x4 a, Float32x4 b) { return _mm_max_ps(a, b); } - static inline Float32x4 v_min(Float32x4 a, Float32x4 b) { return _mm_min_ps(a, b); } - static inline Int32x4 v_min_16(Int32x4 a, Int32x4 b) { return _mm_min_epi16(a, b); } + static inline Float32x4 Q_DECL_VECTORCALL v_max(Float32x4 a, Float32x4 b) { return _mm_max_ps(a, b); } + static inline Float32x4 Q_DECL_VECTORCALL v_min(Float32x4 a, Float32x4 b) { return _mm_min_ps(a, b); } + static inline Int32x4 Q_DECL_VECTORCALL v_min_16(Int32x4 a, Int32x4 b) { return _mm_min_epi16(a, b); } - static inline Int32x4 v_and(Int32x4 a, Int32x4 b) { return _mm_and_si128(a, b); } + static inline Int32x4 Q_DECL_VECTORCALL v_and(Int32x4 a, Int32x4 b) { return _mm_and_si128(a, b); } - static inline Float32x4 v_sub(Float32x4 a, Float32x4 b) { return _mm_sub_ps(a, b); } - static inline Int32x4 v_sub(Int32x4 a, Int32x4 b) { return _mm_sub_epi32(a, b); } + static inline Float32x4 Q_DECL_VECTORCALL v_sub(Float32x4 a, Float32x4 b) { return _mm_sub_ps(a, b); } + static inline Int32x4 Q_DECL_VECTORCALL v_sub(Int32x4 a, Int32x4 b) { return _mm_sub_epi32(a, b); } - static inline Float32x4 v_mul(Float32x4 a, Float32x4 b) { return _mm_mul_ps(a, b); } + static inline Float32x4 Q_DECL_VECTORCALL v_mul(Float32x4 a, Float32x4 b) { return _mm_mul_ps(a, b); } - static inline Float32x4 v_sqrt(Float32x4 x) { return _mm_sqrt_ps(x); } + static inline Float32x4 Q_DECL_VECTORCALL v_sqrt(Float32x4 x) { return _mm_sqrt_ps(x); } - static inline Int32x4 v_toInt(Float32x4 x) { return _mm_cvttps_epi32(x); } + static inline Int32x4 Q_DECL_VECTORCALL v_toInt(Float32x4 x) { return _mm_cvttps_epi32(x); } - static inline Int32x4 v_greaterOrEqual(Float32x4 a, Float32x4 b) { return _mm_castps_si128(_mm_cmpgt_ps(a, b)); } + static inline Int32x4 Q_DECL_VECTORCALL v_greaterOrEqual(Float32x4 a, Float32x4 b) { return _mm_castps_si128(_mm_cmpgt_ps(a, b)); } }; const uint * QT_FASTCALL qt_fetch_radial_gradient_sse2(uint *buffer, const Operator *op, const QSpanData *data, diff --git a/src/gui/painting/qdrawhelper_sse4.cpp b/src/gui/painting/qdrawhelper_sse4.cpp index e3cc1dd43e..5e8acc332d 100644 --- a/src/gui/painting/qdrawhelper_sse4.cpp +++ b/src/gui/painting/qdrawhelper_sse4.cpp @@ -45,6 +45,7 @@ QT_BEGIN_NAMESPACE +#ifndef __AVX2__ template<bool RGBA> static void convertARGBToARGB32PM_sse4(uint *buffer, const uint *src, int count) { @@ -94,7 +95,55 @@ static void convertARGBToARGB32PM_sse4(uint *buffer, const uint *src, int count) } } -static inline __m128 reciprocal_mul_ps(__m128 a, float mul) +template<bool RGBA> +static void convertARGBToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int count) +{ + int i = 0; + const __m128i alphaMask = _mm_set1_epi32(0xff000000); + const __m128i rgbaMask = _mm_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15); + const __m128i shuffleMask = _mm_setr_epi8(6, 7, 6, 7, 6, 7, 6, 7, 14, 15, 14, 15, 14, 15, 14, 15); + const __m128i zero = _mm_setzero_si128(); + + for (; i < count - 3; i += 4) { + __m128i srcVector = _mm_loadu_si128((const __m128i *)&src[i]); + if (!_mm_testz_si128(srcVector, alphaMask)) { + bool cf = _mm_testc_si128(srcVector, alphaMask); + + if (!RGBA) + srcVector = _mm_shuffle_epi8(srcVector, rgbaMask); + const __m128i src1 = _mm_unpacklo_epi8(srcVector, srcVector); + const __m128i src2 = _mm_unpackhi_epi8(srcVector, srcVector); + if (!cf) { + __m128i alpha1 = _mm_shuffle_epi8(src1, shuffleMask); + __m128i alpha2 = _mm_shuffle_epi8(src2, shuffleMask); + __m128i dst1 = _mm_mulhi_epu16(src1, alpha1); + __m128i dst2 = _mm_mulhi_epu16(src2, alpha2); + // Map 0->0xfffe to 0->0xffff + dst1 = _mm_add_epi16(dst1, _mm_srli_epi16(dst1, 15)); + dst2 = _mm_add_epi16(dst2, _mm_srli_epi16(dst2, 15)); + // correct alpha value: + dst1 = _mm_blend_epi16(dst1, src1, 0x88); + dst2 = _mm_blend_epi16(dst2, src2, 0x88); + _mm_storeu_si128((__m128i *)&buffer[i], dst1); + _mm_storeu_si128((__m128i *)&buffer[i + 2], dst2); + } else { + _mm_storeu_si128((__m128i *)&buffer[i], src1); + _mm_storeu_si128((__m128i *)&buffer[i + 2], src2); + } + } else { + _mm_storeu_si128((__m128i *)&buffer[i], zero); + _mm_storeu_si128((__m128i *)&buffer[i + 2], zero); + } + } + + SIMD_EPILOGUE(i, count, 3) { + const uint s = RGBA ? RGBA2ARGB(src[i]) : src[i]; + buffer[i] = QRgba64::fromArgb32(s).premultiplied(); + } +} +#endif // __AVX2__ + +static inline __m128 Q_DECL_VECTORCALL reciprocal_mul_ps(__m128 a, float mul) { __m128 ia = _mm_rcp_ps(a); // Approximate 1/a // Improve precision of ia using Newton-Raphson @@ -174,7 +223,7 @@ template<bool RGBA> static inline void convertARGBFromRGBA64PM_sse4(uint *buffer, const QRgba64 *src, int count) { int i = 0; - const __m128i alphaMask = _mm_set1_epi64x(Q_UINT64_C(0xffff) << 48); + const __m128i alphaMask = _mm_set1_epi64x(qint64(Q_UINT64_C(0xffff) << 48)); const __m128i alphaMask32 = _mm_set1_epi32(0xff000000); const __m128i rgbaMask = _mm_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15); const __m128i zero = _mm_setzero_si128(); @@ -259,6 +308,7 @@ static inline void convertARGBFromRGBA64PM_sse4(uint *buffer, const QRgba64 *src } } +#ifndef __AVX2__ void QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, int count, const QVector<QRgb> *) { convertARGBToARGB32PM_sse4<false>(buffer, buffer, count); @@ -269,6 +319,20 @@ void QT_FASTCALL convertRGBA8888ToARGB32PM_sse4(uint *buffer, int count, const Q convertARGBToARGB32PM_sse4<true>(buffer, buffer, count); } +const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + convertARGBToRGBA64PM_sse4<false>(buffer, src, count); + return buffer; +} + +const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + convertARGBToRGBA64PM_sse4<true>(buffer, src, count); + return buffer; +} + const uint *QT_FASTCALL fetchARGB32ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count, const QVector<QRgb> *, QDitherInfo *) { @@ -283,6 +347,21 @@ const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_sse4(uint *buffer, const uchar * return buffer; } +const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_sse4(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + convertARGBToRGBA64PM_sse4<false>(buffer, reinterpret_cast<const uint *>(src) + index, count); + return buffer; +} + +const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_sse4(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + convertARGBToRGBA64PM_sse4<true>(buffer, reinterpret_cast<const uint *>(src) + index, count); + return buffer; +} +#endif // __AVX2__ + void QT_FASTCALL storeRGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *) { @@ -320,6 +399,7 @@ void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4(uchar *dest, const uint *src, i d[i] = qConvertArgb32ToA2rgb30_sse4<PixelOrder>(src[i]); } +#if QT_CONFIG(raster_64bit) void QT_FASTCALL destStore64ARGB32_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) { uint *dest = (uint*)rasterBuffer->scanLine(y) + x; @@ -331,6 +411,7 @@ void QT_FASTCALL destStore64RGBA8888_sse4(QRasterBuffer *rasterBuffer, int x, in uint *dest = (uint*)rasterBuffer->scanLine(y) + x; convertARGBFromRGBA64PM_sse4<true>(dest, buffer, length); } +#endif void QT_FASTCALL storeARGB32FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count, const QVector<QRgb> *, QDitherInfo *) diff --git a/src/gui/painting/qdrawhelper_ssse3.cpp b/src/gui/painting/qdrawhelper_ssse3.cpp index 42d760d5cc..35d61c3e6c 100644 --- a/src/gui/painting/qdrawhelper_ssse3.cpp +++ b/src/gui/painting/qdrawhelper_ssse3.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -79,55 +80,58 @@ QT_BEGIN_NAMESPACE // The computation being done is: // result = s + d * (1-alpha) // with shortcuts if fully opaque or fully transparent. -#define BLEND_SOURCE_OVER_ARGB32_SSSE3(dst, src, length, nullVector, half, one, colorMask, alphaMask) { \ - int x = 0; \ -\ - /* First, get dst aligned. */ \ - ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) { \ - blend_pixel(dst[x], src[x]); \ - } \ -\ - const int minusOffsetToAlignSrcOn16Bytes = (reinterpret_cast<quintptr>(&(src[x])) >> 2) & 0x3;\ -\ - if (!minusOffsetToAlignSrcOn16Bytes) {\ - /* src is aligned, usual algorithm but with aligned operations.\ - See the SSE2 version for more documentation on the algorithm itself. */\ - const __m128i alphaShuffleMask = _mm_set_epi8(char(0xff),15,char(0xff),15,char(0xff),11,char(0xff),11,char(0xff),7,char(0xff),7,char(0xff),3,char(0xff),3);\ - for (; x < length-3; x += 4) { \ - const __m128i srcVector = _mm_load_si128((const __m128i *)&src[x]); \ - const __m128i srcVectorAlpha = _mm_and_si128(srcVector, alphaMask); \ - if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, alphaMask)) == 0xffff) { \ - _mm_store_si128((__m128i *)&dst[x], srcVector); \ - } else if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, nullVector)) != 0xffff) { \ - __m128i alphaChannel = _mm_shuffle_epi8(srcVector, alphaShuffleMask); \ - alphaChannel = _mm_sub_epi16(one, alphaChannel); \ - const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); \ - __m128i destMultipliedByOneMinusAlpha; \ - BYTE_MUL_SSE2(destMultipliedByOneMinusAlpha, dstVector, alphaChannel, colorMask, half); \ - const __m128i result = _mm_add_epi8(srcVector, destMultipliedByOneMinusAlpha); \ - _mm_store_si128((__m128i *)&dst[x], result); \ - } \ - } /* end for() */\ - } else if ((length - x) >= 8) {\ - /* We use two vectors to extract the src: prevLoaded for the first pixels, lastLoaded for the current pixels. */\ - __m128i srcVectorPrevLoaded = _mm_load_si128((const __m128i *)&src[x - minusOffsetToAlignSrcOn16Bytes]);\ - const int palignrOffset = minusOffsetToAlignSrcOn16Bytes << 2;\ -\ - const __m128i alphaShuffleMask = _mm_set_epi8(char(0xff),15,char(0xff),15,char(0xff),11,char(0xff),11,char(0xff),7,char(0xff),7,char(0xff),3,char(0xff),3);\ - switch (palignrOffset) {\ - case 4:\ - BLENDING_LOOP(4, length)\ - break;\ - case 8:\ - BLENDING_LOOP(8, length)\ - break;\ - case 12:\ - BLENDING_LOOP(12, length)\ - break;\ - }\ - }\ - for (; x < length; ++x) \ - blend_pixel(dst[x], src[x]); \ +static inline void Q_DECL_VECTORCALL +BLEND_SOURCE_OVER_ARGB32_SSSE3(quint32 *dst, const quint32 *src, int length, + __m128i nullVector, __m128i half, __m128i one, __m128i colorMask, __m128i alphaMask) +{ + int x = 0; + + /* First, get dst aligned. */ + ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) { + blend_pixel(dst[x], src[x]); + } + + const int minusOffsetToAlignSrcOn16Bytes = (reinterpret_cast<quintptr>(&(src[x])) >> 2) & 0x3; + + if (!minusOffsetToAlignSrcOn16Bytes) { + /* src is aligned, usual algorithm but with aligned operations. + See the SSE2 version for more documentation on the algorithm itself. */ + const __m128i alphaShuffleMask = _mm_set_epi8(char(0xff),15,char(0xff),15,char(0xff),11,char(0xff),11,char(0xff),7,char(0xff),7,char(0xff),3,char(0xff),3); + for (; x < length-3; x += 4) { + const __m128i srcVector = _mm_load_si128((const __m128i *)&src[x]); + const __m128i srcVectorAlpha = _mm_and_si128(srcVector, alphaMask); + if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, alphaMask)) == 0xffff) { + _mm_store_si128((__m128i *)&dst[x], srcVector); + } else if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, nullVector)) != 0xffff) { + __m128i alphaChannel = _mm_shuffle_epi8(srcVector, alphaShuffleMask); + alphaChannel = _mm_sub_epi16(one, alphaChannel); + const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); + __m128i destMultipliedByOneMinusAlpha; + BYTE_MUL_SSE2(destMultipliedByOneMinusAlpha, dstVector, alphaChannel, colorMask, half); + const __m128i result = _mm_add_epi8(srcVector, destMultipliedByOneMinusAlpha); + _mm_store_si128((__m128i *)&dst[x], result); + } + } /* end for() */ + } else if ((length - x) >= 8) { + /* We use two vectors to extract the src: prevLoaded for the first pixels, lastLoaded for the current pixels. */ + __m128i srcVectorPrevLoaded = _mm_load_si128((const __m128i *)&src[x - minusOffsetToAlignSrcOn16Bytes]); + const int palignrOffset = minusOffsetToAlignSrcOn16Bytes << 2; + + const __m128i alphaShuffleMask = _mm_set_epi8(char(0xff),15,char(0xff),15,char(0xff),11,char(0xff),11,char(0xff),7,char(0xff),7,char(0xff),3,char(0xff),3); + switch (palignrOffset) { + case 4: + BLENDING_LOOP(4, length) + break; + case 8: + BLENDING_LOOP(8, length) + break; + case 12: + BLENDING_LOOP(12, length) + break; + } + } + for (; x < length; ++x) + blend_pixel(dst[x], src[x]); } void qt_blend_argb32_on_argb32_ssse3(uchar *destPixels, int dbpl, @@ -185,6 +189,71 @@ const uint * QT_FASTCALL qt_fetchUntransformed_888_ssse3(uint *buffer, const Ope return buffer; } +void qt_memfill24_ssse3(quint24 *dest, quint24 color, qsizetype count) +{ + // LCM of 12 and 16 bytes is 48 bytes (16 px) + quint32 v = color; + __m128i m = _mm_cvtsi32_si128(v); + quint24 *end = dest + count; + + constexpr uchar x = 2, y = 1, z = 0; + Q_DECL_ALIGN(__m128i) static const uchar + shuffleMask[16 + 1] = { x, y, z, x, y, z, x, y, z, x, y, z, x, y, z, x, y }; + + __m128i mval1 = _mm_shuffle_epi8(m, _mm_load_si128(reinterpret_cast<const __m128i *>(shuffleMask))); + __m128i mval2 = _mm_shuffle_epi8(m, _mm_loadu_si128(reinterpret_cast<const __m128i *>(shuffleMask + 1))); + __m128i mval3 = _mm_alignr_epi8(mval2, mval1, 2); + + for ( ; dest + 16 <= end; dest += 16) { +#ifdef __AVX__ + // Store using 32-byte AVX instruction + __m256 mval12 = _mm256_castps128_ps256(_mm_castsi128_ps(mval1)); + mval12 = _mm256_insertf128_ps(mval12, _mm_castsi128_ps(mval2), 1); + _mm256_storeu_ps(reinterpret_cast<float *>(dest), mval12); +#else + _mm_storeu_si128(reinterpret_cast<__m128i *>(dest) + 0, mval1); + _mm_storeu_si128(reinterpret_cast<__m128i *>(dest) + 1, mval2); +#endif + _mm_storeu_si128(reinterpret_cast<__m128i *>(dest) + 2, mval3); + } + + if (count < 3) { + if (count > 1) + end[-2] = v; + if (count) + end[-1] = v; + return; + } + + // less than 16px/48B left + uchar *ptr = reinterpret_cast<uchar *>(dest); + uchar *ptr_end = reinterpret_cast<uchar *>(end); + qptrdiff left = ptr_end - ptr; + if (left >= 24) { + // 8px/24B or more left + _mm_storeu_si128(reinterpret_cast<__m128i *>(ptr) + 0, mval1); + _mm_storel_epi64(reinterpret_cast<__m128i *>(ptr) + 1, mval2); + ptr += 24; + left -= 24; + } + + // less than 8px/24B left + + if (left >= 16) { + // but more than 5px/15B left + _mm_storeu_si128(reinterpret_cast<__m128i *>(ptr) , mval1); + } else if (left >= 8) { + // but more than 2px/6B left + _mm_storel_epi64(reinterpret_cast<__m128i *>(ptr), mval1); + } + + if (left) { + // 1 or 2px left + // store 8 bytes ending with the right values (will overwrite a bit) + _mm_storel_epi64(reinterpret_cast<__m128i *>(ptr_end - 8), mval2); + } +} + QT_END_NAMESPACE #endif // QT_COMPILER_SUPPORTS_SSSE3 diff --git a/src/gui/painting/qdrawhelper_x86_p.h b/src/gui/painting/qdrawhelper_x86_p.h index cefc213999..5749d8c9fb 100644 --- a/src/gui/painting/qdrawhelper_x86_p.h +++ b/src/gui/painting/qdrawhelper_x86_p.h @@ -57,8 +57,8 @@ QT_BEGIN_NAMESPACE #ifdef __SSE2__ -void qt_memfill32(quint32 *dest, quint32 value, int count); -void qt_memfill16(quint16 *dest, quint16 value, int count); +void qt_memfill64_sse2(quint64 *dest, quint64 value, qsizetype count); +void qt_memfill32_sse2(quint32 *dest, quint32 value, qsizetype count); void qt_bitmapblit32_sse2(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 &color, const uchar *src, int width, int height, int stride); @@ -79,6 +79,9 @@ void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl, extern CompositionFunction qt_functionForMode_SSE2[]; extern CompositionFunctionSolid qt_functionForModeSolid_SSE2[]; + +void qt_memfill64_avx2(quint64 *dest, quint64 value, qsizetype count); +void qt_memfill32_avx2(quint32 *dest, quint32 value, qsizetype count); #endif // __SSE2__ static const int numCompositionFunctions = 38; diff --git a/src/gui/painting/qdrawingprimitive_sse2_p.h b/src/gui/painting/qdrawingprimitive_sse2_p.h index b237ea1611..cc8d230fa8 100644 --- a/src/gui/painting/qdrawingprimitive_sse2_p.h +++ b/src/gui/painting/qdrawingprimitive_sse2_p.h @@ -42,7 +42,7 @@ #include <QtGui/private/qtguiglobal_p.h> #include <private/qsimd_p.h> -#include "qdrawhelper_p.h" +#include "qdrawhelper_x86_p.h" #include "qrgba64_p.h" #ifdef __SSE2__ @@ -232,7 +232,7 @@ QT_END_NAMESPACE QT_BEGIN_NAMESPACE #if QT_COMPILER_SUPPORTS_HERE(SSE4_1) QT_FUNCTION_TARGET(SSE2) -Q_ALWAYS_INLINE void reciprocal_mul_ss(__m128 &ia, const __m128 a, float mul) +Q_ALWAYS_INLINE void Q_DECL_VECTORCALL reciprocal_mul_ss(__m128 &ia, const __m128 a, float mul) { ia = _mm_rcp_ss(a); // Approximate 1/a // Improve precision of ia using Newton-Raphson diff --git a/src/gui/painting/qicc.cpp b/src/gui/painting/qicc.cpp new file mode 100644 index 0000000000..d88b005782 --- /dev/null +++ b/src/gui/painting/qicc.cpp @@ -0,0 +1,669 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qicc_p.h" + +#include <qbuffer.h> +#include <qbytearray.h> +#include <qdatastream.h> +#include <qloggingcategory.h> +#include <qendian.h> + +#include "qcolorspace_p.h" +#include "qcolortrc_p.h" + +QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcIcc, "qt.gui.icc") + +struct ICCProfileHeader +{ + quint32_be profileSize; + + quint32_be preferredCmmType; + + quint32_be profileVersion; + quint32_be profileClass; + quint32_be inputColorSpace; + quint32_be pcs; + quint32_be datetime[3]; + quint32_be signature; + quint32_be platformSignature; + quint32_be flags; + quint32_be deviceManufacturer; + quint32_be deviceModel; + quint32_be deviceAttributes[2]; + + quint32_be renderingIntent; + qint32_be illuminantXyz[3]; + + quint32_be creatorSignature; + quint32_be profileId[4]; + + quint32_be reserved[7]; + +// Technically after the header, but easier to include here: + quint32_be tagCount; +}; + +constexpr quint32 IccTag(uchar a, uchar b, uchar c, uchar d) +{ + return (a << 24) | (b << 16) | (c << 8) | d; +} + +enum class ProfileClass : quint32 { + Input = IccTag('s', 'c', 'r', 'n'), + Display = IccTag('m', 'n', 't', 'r'), + // Not supported: + Output = IccTag('p', 'r', 't', 'r'), + ColorSpace = IccTag('s', 'p', 'a', 'c'), +}; + +enum class Tag : quint32 { + acsp = IccTag('a', 'c', 's', 'p'), + RGB_ = IccTag('R', 'G', 'B', ' '), + XYZ_ = IccTag('X', 'Y', 'Z', ' '), + rXYZ = IccTag('r', 'X', 'Y', 'Z'), + gXYZ = IccTag('g', 'X', 'Y', 'Z'), + bXYZ = IccTag('b', 'X', 'Y', 'Z'), + rTRC = IccTag('r', 'T', 'R', 'C'), + gTRC = IccTag('g', 'T', 'R', 'C'), + bTRC = IccTag('b', 'T', 'R', 'C'), + A2B0 = IccTag('A', '2', 'B', '0'), + A2B1 = IccTag('A', '2', 'B', '1'), + B2A0 = IccTag('B', '2', 'A', '0'), + B2A1 = IccTag('B', '2', 'A', '1'), + desc = IccTag('d', 'e', 's', 'c'), + text = IccTag('t', 'e', 'x', 't'), + cprt = IccTag('c', 'p', 'r', 't'), + curv = IccTag('c', 'u', 'r', 'v'), + para = IccTag('p', 'a', 'r', 'a'), + wtpt = IccTag('w', 't', 'p', 't'), + bkpt = IccTag('b', 'k', 'p', 't'), + mft1 = IccTag('m', 'f', 't', '1'), + mft2 = IccTag('m', 'f', 't', '2'), + mAB_ = IccTag('m', 'A', 'B', ' '), + mBA_ = IccTag('m', 'B', 'A', ' '), + chad = IccTag('c', 'h', 'a', 'd'), + sf32 = IccTag('s', 'f', '3', '2'), + + // Apple extensions for ICCv2: + aarg = IccTag('a', 'a', 'r', 'g'), + aagg = IccTag('a', 'a', 'g', 'g'), + aabg = IccTag('a', 'a', 'b', 'g'), +}; + +inline uint qHash(const Tag &key, uint seed = 0) +{ + return qHash(quint32(key), seed); +} + +namespace QIcc { + +struct TagTableEntry +{ + quint32_be signature; + quint32_be offset; + quint32_be size; +}; + +struct GenericTagData { + quint32_be type; + quint32_be null; +}; + +struct XYZTagData : GenericTagData { + qint32_be fixedX; + qint32_be fixedY; + qint32_be fixedZ; +}; + +struct CurvTagData : GenericTagData { + quint32_be valueCount; + quint16_be value[1]; +}; + +struct ParaTagData : GenericTagData { + quint16_be curveType; + quint16_be null2; + quint32_be parameter[1]; +}; + +// For both mAB and mBA +struct mABTagData : GenericTagData { + quint8 inputChannels; + quint8 outputChannels; + quint8 padding[2]; + quint32_be bCurvesOffset; + quint32_be matrixOffset; + quint32_be mCurvesOffset; + quint32_be clutOffset; + quint32_be aCurvesOffset; +}; + +struct Sf32TagData : GenericTagData { + quint32_be value[1]; +}; + +static int toFixedS1516(float x) +{ + return int(x * 65536.0f + 0.5f); +} + +static float fromFixedS1516(int x) +{ + return x * (1.0f / 65536.0f); +} + +QColorVector fromXyzData(const XYZTagData *xyz) +{ + const float x = fromFixedS1516(xyz->fixedX); + const float y = fromFixedS1516(xyz->fixedY); + const float z = fromFixedS1516(xyz->fixedZ); + qCDebug(lcIcc) << "XYZ_ " << x << y << z; + + return QColorVector(x, y, z); +} + +static bool isValidIccProfile(const ICCProfileHeader &header) +{ + if (header.signature != uint(Tag::acsp)) { + qCWarning(lcIcc, "Failed ICC signature test"); + return false; + } + if (header.profileSize < (sizeof(ICCProfileHeader) + header.tagCount * sizeof(TagTableEntry))) { + qCWarning(lcIcc, "Failed basic size sanity"); + return false; + } + + if (header.profileClass != uint(ProfileClass::Input) + && header.profileClass != uint(ProfileClass::Display)) { + qCWarning(lcIcc, "Unsupported ICC profile class %x", quint32(header.profileClass)); + return false; + } + if (header.inputColorSpace != 0x52474220 /* 'RGB '*/) { + qCWarning(lcIcc, "Unsupported ICC input color space %x", quint32(header.inputColorSpace)); + return false; + } + if (header.pcs != 0x58595a20 /* 'XYZ '*/) { + // ### support PCSLAB + qCWarning(lcIcc, "Unsupported ICC profile connection space %x", quint32(header.pcs)); + return false; + } + + QColorVector illuminant; + illuminant.x = fromFixedS1516(header.illuminantXyz[0]); + illuminant.y = fromFixedS1516(header.illuminantXyz[1]); + illuminant.z = fromFixedS1516(header.illuminantXyz[2]); + if (illuminant != QColorVector::D50()) { + qCWarning(lcIcc, "Invalid ICC illuminant"); + return false; + } + + return true; +} + +static int writeColorTrc(QDataStream &stream, const QColorTrc &trc) +{ + if (trc.isLinear()) { + stream << uint(Tag::curv) << uint(0); + stream << uint(0); + return 12; + } + + if (trc.m_type == QColorTrc::Type::Function) { + const QColorTransferFunction &fun = trc.m_fun; + stream << uint(Tag::para) << uint(0); + if (fun.isGamma()) { + stream << ushort(0) << ushort(0); + stream << toFixedS1516(fun.m_g); + return 12 + 4; + } + bool type3 = qFuzzyIsNull(fun.m_e) && qFuzzyIsNull(fun.m_f); + stream << ushort(type3 ? 3 : 4) << ushort(0); + stream << toFixedS1516(fun.m_g); + stream << toFixedS1516(fun.m_a); + stream << toFixedS1516(fun.m_b); + stream << toFixedS1516(fun.m_c); + stream << toFixedS1516(fun.m_d); + if (type3) + return 12 + 5 * 4; + stream << toFixedS1516(fun.m_e); + stream << toFixedS1516(fun.m_f); + return 12 + 7 * 4; + } + + Q_ASSERT(trc.m_type == QColorTrc::Type::Table); + stream << uint(Tag::curv) << uint(0); + stream << uint(trc.m_table.m_tableSize); + if (!trc.m_table.m_table16.isEmpty()) { + for (uint i = 0; i < trc.m_table.m_tableSize; ++i) { + stream << ushort(trc.m_table.m_table16[i]); + } + } else { + for (uint i = 0; i < trc.m_table.m_tableSize; ++i) { + stream << ushort(trc.m_table.m_table8[i] * 257U); + } + } + return 12 + 2 * trc.m_table.m_tableSize; +} + +QByteArray toIccProfile(const QColorSpace &space) +{ + if (!space.isValid()) + return QByteArray(); + + const QColorSpacePrivate *spaceDPtr = space.d_func(); + + constexpr int tagCount = 9; + constexpr uint profileDataOffset = 128 + 4 + 12 * tagCount; + constexpr uint variableTagTableOffsets = 128 + 4 + 12 * 5; + uint currentOffset = 0; + uint rTrcOffset, gTrcOffset, bTrcOffset; + uint rTrcSize, gTrcSize, bTrcSize; + uint descOffset, descSize; + + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + QDataStream stream(&buffer); + + // Profile header: + stream << uint(0); // Size, we will update this later + stream << uint(0); + stream << uint(0x02400000); // Version 2.4 (note we use 'para' from version 4) + stream << uint(ProfileClass::Display); + stream << uint(Tag::RGB_); + stream << uint(Tag::XYZ_); + stream << uint(0) << uint(0) << uint(0); + stream << uint(Tag::acsp); + stream << uint(0) << uint(0) << uint(0); + stream << uint(0) << uint(0) << uint(0); + stream << uint(1); // Rendering intent + stream << uint(0x0000f6d6); // D50 X + stream << uint(0x00010000); // D50 Y + stream << uint(0x0000d32d); // D50 Z + stream << IccTag('Q','t', QT_VERSION_MAJOR, QT_VERSION_MINOR); + stream << uint(0) << uint(0) << uint(0) << uint(0); + stream << uint(0) << uint(0) << uint(0) << uint(0) << uint(0) << uint(0) << uint(0); + + // Tag table: + stream << uint(tagCount); + stream << uint(Tag::rXYZ) << uint(profileDataOffset + 00) << uint(20); + stream << uint(Tag::gXYZ) << uint(profileDataOffset + 20) << uint(20); + stream << uint(Tag::bXYZ) << uint(profileDataOffset + 40) << uint(20); + stream << uint(Tag::wtpt) << uint(profileDataOffset + 60) << uint(20); + stream << uint(Tag::cprt) << uint(profileDataOffset + 80) << uint(12); + // From here the offset and size will be updated later: + stream << uint(Tag::rTRC) << uint(0) << uint(0); + stream << uint(Tag::gTRC) << uint(0) << uint(0); + stream << uint(Tag::bTRC) << uint(0) << uint(0); + stream << uint(Tag::desc) << uint(0) << uint(0); + // TODO: consider adding 'chad' tag (required in ICC >=4 when we have non-D50 whitepoint) + currentOffset = profileDataOffset; + + // Tag data: + stream << uint(Tag::XYZ_) << uint(0); + stream << toFixedS1516(spaceDPtr->toXyz.r.x); + stream << toFixedS1516(spaceDPtr->toXyz.r.y); + stream << toFixedS1516(spaceDPtr->toXyz.r.z); + stream << uint(Tag::XYZ_) << uint(0); + stream << toFixedS1516(spaceDPtr->toXyz.g.x); + stream << toFixedS1516(spaceDPtr->toXyz.g.y); + stream << toFixedS1516(spaceDPtr->toXyz.g.z); + stream << uint(Tag::XYZ_) << uint(0); + stream << toFixedS1516(spaceDPtr->toXyz.b.x); + stream << toFixedS1516(spaceDPtr->toXyz.b.y); + stream << toFixedS1516(spaceDPtr->toXyz.b.z); + stream << uint(Tag::XYZ_) << uint(0); + stream << toFixedS1516(spaceDPtr->whitePoint.x); + stream << toFixedS1516(spaceDPtr->whitePoint.y); + stream << toFixedS1516(spaceDPtr->whitePoint.z); + stream << uint(Tag::text) << uint(0); + stream << uint(IccTag('N', '/', 'A', '\0')); + currentOffset += 92; + + // From now on the data is variable sized: + rTrcOffset = currentOffset; + rTrcSize = writeColorTrc(stream, spaceDPtr->trc[0]); + currentOffset += rTrcSize; + if (spaceDPtr->trc[0] == spaceDPtr->trc[1]) { + gTrcOffset = rTrcOffset; + gTrcSize = rTrcSize; + } else { + gTrcOffset = currentOffset; + gTrcSize = writeColorTrc(stream, spaceDPtr->trc[1]); + currentOffset += gTrcSize; + } + if (spaceDPtr->trc[0] == spaceDPtr->trc[2]) { + bTrcOffset = rTrcOffset; + bTrcSize = rTrcSize; + } else { + bTrcOffset = currentOffset; + bTrcSize = writeColorTrc(stream, spaceDPtr->trc[2]); + currentOffset += bTrcSize; + } + + descOffset = currentOffset; + QByteArray description = spaceDPtr->description.toUtf8(); + stream << uint(Tag::desc) << uint(0); + stream << uint(description.size() + 1); + stream.writeRawData(description.constData(), description.size() + 1); + stream << uint(0) << uint(0); + stream << ushort(0) << uchar(0); + QByteArray macdesc(67, '\0'); + stream.writeRawData(macdesc.constData(), 67); + descSize = 90 + description.size() + 1; + currentOffset += descSize; + + buffer.close(); + QByteArray iccProfile = buffer.buffer(); + // Now write final size + *(quint32_be *)iccProfile.data() = iccProfile.size(); + // And the final indices and sizes of variable size tags: + *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 4) = rTrcOffset; + *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 8) = rTrcSize; + *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 12 + 4) = gTrcOffset; + *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 12 + 8) = gTrcSize; + *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 2 * 12 + 4) = bTrcOffset; + *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 2 * 12 + 8) = bTrcSize; + *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 3 * 12 + 4) = descOffset; + *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 3 * 12 + 8) = descSize; + +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + const ICCProfileHeader *iccHeader = (const ICCProfileHeader *)iccProfile.constData(); + Q_ASSERT(qsizetype(iccHeader->profileSize) == qsizetype(iccProfile.size())); + Q_ASSERT(isValidIccProfile(*iccHeader)); +#endif + + return iccProfile; +} + +bool parseTRC(const GenericTagData *trcData, QColorTrc &gamma) +{ + if (trcData->type == quint32(Tag::curv)) { + const CurvTagData *curv = reinterpret_cast<const CurvTagData *>(trcData); + qCDebug(lcIcc) << "curv" << uint(curv->valueCount); + if (curv->valueCount == 0) { + gamma.m_type = QColorTrc::Type::Function; + gamma.m_fun = QColorTransferFunction(); // Linear + } else if (curv->valueCount == 1) { + float g = curv->value[0] * (1.0f / 256.0f); + qCDebug(lcIcc) << g; + gamma.m_type = QColorTrc::Type::Function; + gamma.m_fun = QColorTransferFunction::fromGamma(g); + } else { + QVector<quint16> tabl; + tabl.resize(curv->valueCount); + for (uint i = 0; i < curv->valueCount; ++i) + tabl[i] = curv->value[i]; + QColorTransferTable table = QColorTransferTable(curv->valueCount, std::move(tabl)); + QColorTransferFunction curve; + if (!table.asColorTransferFunction(&curve)) { + gamma.m_type = QColorTrc::Type::Table; + gamma.m_table = table; + } else { + qCDebug(lcIcc) << "Detected curv table as function"; + gamma.m_type = QColorTrc::Type::Function; + gamma.m_fun = curve; + } + } + return true; + } + if (trcData->type == quint32(Tag::para)) { + const ParaTagData *para = reinterpret_cast<const ParaTagData *>(trcData); + qCDebug(lcIcc) << "para" << uint(para->curveType); + switch (para->curveType) { + case 0: { + float g = fromFixedS1516(para->parameter[0]); + qCDebug(lcIcc) << g; + gamma.m_type = QColorTrc::Type::Function; + gamma.m_fun = QColorTransferFunction::fromGamma(g); + break; + } + case 1: { + float g = fromFixedS1516(para->parameter[0]); + float a = fromFixedS1516(para->parameter[1]); + float b = fromFixedS1516(para->parameter[2]); + float d = -b / a; + qCDebug(lcIcc) << g << a << b; + gamma.m_type = QColorTrc::Type::Function; + gamma.m_fun = QColorTransferFunction(a, b, 0.0f, d, 0.0f, 0.0f, g); + break; + } + case 2: { + float g = fromFixedS1516(para->parameter[0]); + float a = fromFixedS1516(para->parameter[1]); + float b = fromFixedS1516(para->parameter[2]); + float c = fromFixedS1516(para->parameter[3]); + float d = -b / a; + qCDebug(lcIcc) << g << a << b << c; + gamma.m_type = QColorTrc::Type::Function; + gamma.m_fun = QColorTransferFunction(a, b, 0.0f, d, c, c, g); + break; + } + case 3: { + float g = fromFixedS1516(para->parameter[0]); + float a = fromFixedS1516(para->parameter[1]); + float b = fromFixedS1516(para->parameter[2]); + float c = fromFixedS1516(para->parameter[3]); + float d = fromFixedS1516(para->parameter[4]); + qCDebug(lcIcc) << g << a << b << c << d; + gamma.m_type = QColorTrc::Type::Function; + gamma.m_fun = QColorTransferFunction(a, b, c, d, 0.0f, 0.0f, g); + break; + } + case 4: { + float g = fromFixedS1516(para->parameter[0]); + float a = fromFixedS1516(para->parameter[1]); + float b = fromFixedS1516(para->parameter[2]); + float c = fromFixedS1516(para->parameter[3]); + float d = fromFixedS1516(para->parameter[4]); + float e = fromFixedS1516(para->parameter[5]); + float f = fromFixedS1516(para->parameter[6]); + qCDebug(lcIcc) << g << a << b << c << d << e << f; + gamma.m_type = QColorTrc::Type::Function; + gamma.m_fun = QColorTransferFunction(a, b, c, d, e, f, g); + break; + } + default: + qCWarning(lcIcc) << "Unknown para type" << uint(para->curveType); + return false; + } + return true; + } + qCWarning(lcIcc) << "Invalid TRC data type"; + return false; +} + +bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace) +{ + if (data.size() < qsizetype(sizeof(ICCProfileHeader))) { + qCWarning(lcIcc) << "fromIccProfile: failed size sanity 1"; + return false; + } + const ICCProfileHeader *header = (const ICCProfileHeader *)data.constData(); + if (!isValidIccProfile(*header)) { + qCWarning(lcIcc) << "fromIccProfile: failed general sanity check"; + return false; + } + if (qsizetype(header->profileSize) > data.size()) { + qCWarning(lcIcc) << "fromIccProfile: failed size sanity 2"; + return false; + } + + // Read tag index + const TagTableEntry *tagTable = (const TagTableEntry *)(data.constData() + sizeof(ICCProfileHeader)); + const qsizetype offsetToData = sizeof(ICCProfileHeader) + header->tagCount * sizeof(TagTableEntry); + + QHash<Tag, quint32> tagIndex; + for (uint i = 0; i < header->tagCount; ++i) { + // Sanity check tag sizes and offsets: + if (qsizetype(tagTable[i].offset) < offsetToData) { + qCWarning(lcIcc) << "fromIccProfile: failed tag offset sanity 1"; + return false; + } + // Checked separately from (+ size) to handle overflow. + if (tagTable[i].offset > header->profileSize) { + qCWarning(lcIcc) << "fromIccProfile: failed tag offset sanity 2"; + return false; + } + if ((tagTable[i].offset + tagTable[i].size) > header->profileSize) { + qCWarning(lcIcc) << "fromIccProfile: failed tag offset + size sanity"; + return false; + } +// printf("'%4s' %d %d\n", (const char *)&tagTable[i].signature, +// quint32(tagTable[i].offset), +// quint32(tagTable[i].size)); + tagIndex.insert(Tag(quint32(tagTable[i].signature)), tagTable[i].offset); + } + // Check the profile is three-component matrix based (what we currently support): + if (!tagIndex.contains(Tag::rXYZ) || !tagIndex.contains(Tag::gXYZ) || !tagIndex.contains(Tag::bXYZ) || + !tagIndex.contains(Tag::rTRC) || !tagIndex.contains(Tag::gTRC) || !tagIndex.contains(Tag::bTRC) || + !tagIndex.contains(Tag::wtpt)) { + qCWarning(lcIcc) << "fromIccProfile: Unsupported ICC profile - not three component matrix based"; + return false; + } + + // Parse XYZ tags + const XYZTagData *rXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::rXYZ]); + const XYZTagData *gXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::gXYZ]); + const XYZTagData *bXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::bXYZ]); + const XYZTagData *wXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::wtpt]); + if (rXyz->type != quint32(Tag::XYZ_) || gXyz->type != quint32(Tag::XYZ_) || + wXyz->type != quint32(Tag::XYZ_) || wXyz->type != quint32(Tag::XYZ_)) { + qCWarning(lcIcc) << "fromIccProfile: Bad XYZ data type"; + return false; + } + QColorSpacePrivate *colorspaceDPtr = colorSpace->d_func(); + + colorspaceDPtr->toXyz.r = fromXyzData(rXyz); + colorspaceDPtr->toXyz.g = fromXyzData(gXyz); + colorspaceDPtr->toXyz.b = fromXyzData(bXyz); + QColorVector whitePoint = fromXyzData(wXyz); + colorspaceDPtr->whitePoint = whitePoint; + + colorspaceDPtr->gamut = QColorSpace::Gamut::Custom; + if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromSRgb()) { + qCDebug(lcIcc) << "fromIccProfile: sRGB gamut detected"; + colorspaceDPtr->gamut = QColorSpace::Gamut::SRgb; + } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromAdobeRgb()) { + qCDebug(lcIcc) << "fromIccProfile: Adobe RGB gamut detected"; + colorspaceDPtr->gamut = QColorSpace::Gamut::AdobeRgb; + } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromDciP3D65()) { + qCDebug(lcIcc) << "fromIccProfile: DCI-P3 D65 gamut detected"; + colorspaceDPtr->gamut = QColorSpace::Gamut::DciP3D65; + } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromBt2020()) { + qCDebug(lcIcc) << "fromIccProfile: BT.2020 gamut detected"; + colorspaceDPtr->gamut = QColorSpace::Gamut::Bt2020; + } + if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromProPhotoRgb()) { + qCDebug(lcIcc) << "fromIccProfile: ProPhoto RGB gamut detected"; + colorspaceDPtr->gamut = QColorSpace::Gamut::ProPhotoRgb; + } + // Reset the matrix to our canonical values: + if (colorspaceDPtr->gamut != QColorSpace::Gamut::Custom) + colorspaceDPtr->setToXyzMatrix(); + + // Parse TRC tags + const GenericTagData *rTrc; + const GenericTagData *gTrc; + const GenericTagData *bTrc; + if (tagIndex.contains(Tag::aarg) && tagIndex.contains(Tag::aagg) && tagIndex.contains(Tag::aabg)) { + // Apple extension for parametric version of TRCs in ICCv2: + rTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::aarg]); + gTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::aagg]); + bTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::aabg]); + } else { + rTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::rTRC]); + gTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::gTRC]); + bTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::bTRC]); + } + + QColorTrc rCurve; + QColorTrc gCurve; + QColorTrc bCurve; + if (!parseTRC(rTrc, rCurve)) + return false; + if (!parseTRC(gTrc, gCurve)) + return false; + if (!parseTRC(bTrc, bCurve)) + return false; + if (rCurve == gCurve && gCurve == bCurve && rCurve.m_type == QColorTrc::Type::Function) { + if (rCurve.m_fun.isLinear()) { + qCDebug(lcIcc) << "fromIccProfile: Linear gamma detected"; + colorspaceDPtr->trc[0] = QColorTransferFunction(); + colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Linear; + colorspaceDPtr->gamma = 1.0f; + } else if (rCurve.m_fun.isGamma()) { + qCDebug(lcIcc) << "fromIccProfile: Simple gamma detected"; + colorspaceDPtr->trc[0] = QColorTransferFunction::fromGamma(rCurve.m_fun.m_g); + colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Gamma; + colorspaceDPtr->gamma = rCurve.m_fun.m_g; + } else if (rCurve.m_fun.isSRgb()) { + qCDebug(lcIcc) << "fromIccProfile: sRGB gamma detected"; + colorspaceDPtr->trc[0] = QColorTransferFunction::fromSRgb(); + colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::SRgb; + } else { + colorspaceDPtr->trc[0] = rCurve; + colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Custom; + } + + colorspaceDPtr->trc[1] = colorspaceDPtr->trc[0]; + colorspaceDPtr->trc[2] = colorspaceDPtr->trc[0]; + } else { + colorspaceDPtr->trc[0] = rCurve; + colorspaceDPtr->trc[1] = gCurve; + colorspaceDPtr->trc[2] = bCurve; + colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Custom; + } + + // FIXME: try to parse the description.. + + if (!colorspaceDPtr->identifyColorSpace()) + colorspaceDPtr->id = QColorSpace::Unknown; + else + qCDebug(lcIcc) << "fromIccProfile: Named colorspace detected: " << colorSpace->colorSpaceId(); + + colorspaceDPtr->iccProfile = data; + + return true; +} + +} // namespace QIcc + +QT_END_NAMESPACE diff --git a/src/gui/painting/qicc_p.h b/src/gui/painting/qicc_p.h new file mode 100644 index 0000000000..c3220391f4 --- /dev/null +++ b/src/gui/painting/qicc_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QICC_P_H +#define QICC_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qbytearray.h> +#include <QtGui/qtguiglobal.h> + +QT_BEGIN_NAMESPACE + +class QColorSpace; + +namespace QIcc { + +Q_GUI_EXPORT bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace); +Q_GUI_EXPORT QByteArray toIccProfile(const QColorSpace &space); + +} + +QT_END_NAMESPACE + +#endif // QICC_P_H diff --git a/src/gui/painting/qimagescale.cpp b/src/gui/painting/qimagescale.cpp index 96da5e029c..0d7205b483 100644 --- a/src/gui/painting/qimagescale.cpp +++ b/src/gui/painting/qimagescale.cpp @@ -239,7 +239,6 @@ static QImageScaleInfo* QImageScale::qimageCalcScaleInfo(const QImage &img, isi = new QImageScaleInfo; if (!isi) return 0; - memset(isi, 0, sizeof(QImageScaleInfo)); isi->xup_yup = (qAbs(dw) >= sw) + ((qAbs(dh) >= sh) << 1); @@ -529,6 +528,7 @@ static void qt_qimageScaleAARGBA_down_xy(QImageScaleInfo *isi, unsigned int *des } } +#if QT_CONFIG(raster_64bit) static void qt_qimageScaleRgba64_up_x_down_y(QImageScaleInfo *isi, QRgba64 *dest, int dw, int dh, int dow, int sow); @@ -729,6 +729,7 @@ static void qt_qimageScaleRgba64_down_xy(QImageScaleInfo *isi, QRgba64 *dest, } } } +#endif static void qt_qimageScaleAARGB_up_x_down_y(QImageScaleInfo *isi, unsigned int *dest, int dw, int dh, int dow, int sow); @@ -946,10 +947,13 @@ QImage qSmoothScaleImage(const QImage &src, int dw, int dh) return QImage(); } +#if QT_CONFIG(raster_64bit) if (src.depth() > 32) qt_qimageScaleRgba64(scaleinfo, (QRgba64 *)buffer.scanLine(0), dw, dh, dw, src.bytesPerLine() / 8); - else if (src.hasAlphaChannel()) + else +#endif + if (src.hasAlphaChannel()) qt_qimageScaleAARGBA(scaleinfo, (unsigned int *)buffer.scanLine(0), dw, dh, dw, src.bytesPerLine() / 4); else diff --git a/src/gui/painting/qimagescale_p.h b/src/gui/painting/qimagescale_p.h index 415623a575..244d681718 100644 --- a/src/gui/painting/qimagescale_p.h +++ b/src/gui/painting/qimagescale_p.h @@ -61,10 +61,11 @@ QImage qSmoothScaleImage(const QImage &img, int w, int h); namespace QImageScale { struct QImageScaleInfo { - int *xpoints; - const unsigned int **ypoints; - int *xapoints, *yapoints; - int xup_yup; + int *xpoints{nullptr}; + const unsigned int **ypoints{nullptr}; + int *xapoints{nullptr}; + int *yapoints{nullptr}; + int xup_yup{0}; }; } diff --git a/src/gui/painting/qimagescale_sse4.cpp b/src/gui/painting/qimagescale_sse4.cpp index 34d6b3882e..5861a2e2ff 100644 --- a/src/gui/painting/qimagescale_sse4.cpp +++ b/src/gui/painting/qimagescale_sse4.cpp @@ -39,6 +39,7 @@ #include "qimagescale_p.h" #include "qimage.h" +#include <private/qdrawhelper_x86_p.h> #include <private/qsimd_p.h> #if defined(QT_COMPILER_SUPPORTS_SSE4_1) @@ -47,7 +48,8 @@ QT_BEGIN_NAMESPACE using namespace QImageScale; -inline static __m128i qt_qimageScaleAARGBA_helper(const unsigned int *pix, int xyap, int Cxy, int step, const __m128i vxyap, const __m128i vCxy) +inline static __m128i Q_DECL_VECTORCALL +qt_qimageScaleAARGBA_helper(const unsigned int *pix, int xyap, int Cxy, int step, const __m128i vxyap, const __m128i vCxy) { __m128i vpix = _mm_cvtepu8_epi32(_mm_cvtsi32_si128(*pix)); __m128i vx = _mm_mullo_epi32(vpix, vxyap); diff --git a/src/gui/painting/qmatrix.cpp b/src/gui/painting/qmatrix.cpp index 24b33243da..b1f01332b6 100644 --- a/src/gui/painting/qmatrix.cpp +++ b/src/gui/painting/qmatrix.cpp @@ -248,7 +248,7 @@ QMatrix::QMatrix(qreal m11, qreal m12, qreal m21, qreal m22, qreal dx, qreal dy) /*! Constructs a matrix that is a copy of the given \a matrix. */ -QMatrix::QMatrix(const QMatrix &matrix) Q_DECL_NOTHROW +QMatrix::QMatrix(const QMatrix &matrix) noexcept : _m11(matrix._m11) , _m12(matrix._m12) , _m21(matrix._m21) @@ -989,7 +989,7 @@ bool QMatrix::operator==(const QMatrix &m) const Returns the hash value for \a key, using \a seed to seed the calculation. */ -uint qHash(const QMatrix &key, uint seed) Q_DECL_NOTHROW +uint qHash(const QMatrix &key, uint seed) noexcept { QtPrivate::QHashCombine hash; seed = hash(seed, key.m11()); @@ -1068,7 +1068,7 @@ QMatrix QMatrix::operator *(const QMatrix &m) const /*! Assigns the given \a matrix's values to this matrix. */ -QMatrix &QMatrix::operator=(const QMatrix &matrix) Q_DECL_NOTHROW +QMatrix &QMatrix::operator=(const QMatrix &matrix) noexcept { _m11 = matrix._m11; _m12 = matrix._m12; diff --git a/src/gui/painting/qmatrix.h b/src/gui/painting/qmatrix.h index d8a4fcfb1c..a167260ade 100644 --- a/src/gui/painting/qmatrix.h +++ b/src/gui/painting/qmatrix.h @@ -64,12 +64,12 @@ public: #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove; the compiler-generated ones are fine! - QMatrix &operator=(QMatrix &&other) Q_DECL_NOTHROW // = default + QMatrix &operator=(QMatrix &&other) noexcept // = default { memcpy(static_cast<void *>(this), static_cast<void *>(&other), sizeof(QMatrix)); return *this; } - QMatrix &operator=(const QMatrix &) Q_DECL_NOTHROW; // = default - QMatrix(QMatrix &&other) Q_DECL_NOTHROW // = default + QMatrix &operator=(const QMatrix &) noexcept; // = default + QMatrix(QMatrix &&other) noexcept // = default { memcpy(static_cast<void *>(this), static_cast<void *>(&other), sizeof(QMatrix)); } - QMatrix(const QMatrix &other) Q_DECL_NOTHROW; // = default + QMatrix(const QMatrix &other) noexcept; // = default #endif void setMatrix(qreal m11, qreal m12, qreal m21, qreal m22, @@ -140,7 +140,7 @@ private: }; Q_DECLARE_TYPEINFO(QMatrix, Q_MOVABLE_TYPE); -Q_GUI_EXPORT Q_DECL_CONST_FUNCTION uint qHash(const QMatrix &key, uint seed = 0) Q_DECL_NOTHROW; +Q_GUI_EXPORT Q_DECL_CONST_FUNCTION uint qHash(const QMatrix &key, uint seed = 0) noexcept; // mathematical semantics inline QPoint operator*(const QPoint &p, const QMatrix &m) diff --git a/src/gui/painting/qmemrotate.cpp b/src/gui/painting/qmemrotate.cpp index 43aeff3268..9cb787fb2c 100644 --- a/src/gui/painting/qmemrotate.cpp +++ b/src/gui/painting/qmemrotate.cpp @@ -44,7 +44,7 @@ QT_BEGIN_NAMESPACE static const int tileSize = 32; template <class T> -Q_STATIC_TEMPLATE_FUNCTION +static inline void qt_memrotate90_tiled(const T *src, int w, int h, int sstride, T *dest, int dstride) { sstride /= sizeof(T); @@ -103,7 +103,7 @@ inline void qt_memrotate90_tiled(const T *src, int w, int h, int sstride, T *des } template <class T> -Q_STATIC_TEMPLATE_FUNCTION +static inline void qt_memrotate90_tiled_unpacked(const T *src, int w, int h, int sstride, T *dest, int dstride) { @@ -131,7 +131,7 @@ inline void qt_memrotate90_tiled_unpacked(const T *src, int w, int h, int sstrid } template <class T> -Q_STATIC_TEMPLATE_FUNCTION +static inline void qt_memrotate270_tiled(const T *src, int w, int h, int sstride, T *dest, int dstride) { sstride /= sizeof(T); @@ -190,7 +190,7 @@ inline void qt_memrotate270_tiled(const T *src, int w, int h, int sstride, T *de } template <class T> -Q_STATIC_TEMPLATE_FUNCTION +static inline void qt_memrotate270_tiled_unpacked(const T *src, int w, int h, int sstride, T *dest, int dstride) { @@ -219,7 +219,7 @@ inline void qt_memrotate270_tiled_unpacked(const T *src, int w, int h, int sstri template <class T> -Q_STATIC_TEMPLATE_FUNCTION +static inline void qt_memrotate90_template(const T *src, int srcWidth, int srcHeight, int srcStride, T *dest, int dstStride) { @@ -246,7 +246,7 @@ inline void qt_memrotate90_template<quint64>(const quint64 *src, int w, int h, i } template <class T> -Q_STATIC_TEMPLATE_FUNCTION +static inline void qt_memrotate180_template(const T *src, int w, int h, int sstride, T *dest, int dstride) { const char *s = (const char*)(src) + (h - 1) * sstride; @@ -261,7 +261,7 @@ inline void qt_memrotate180_template(const T *src, int w, int h, int sstride, T } template <class T> -Q_STATIC_TEMPLATE_FUNCTION +static inline void qt_memrotate270_template(const T *src, int srcWidth, int srcHeight, int srcStride, T *dest, int dstStride) { diff --git a/src/gui/painting/qoutlinemapper.cpp b/src/gui/painting/qoutlinemapper.cpp index b2d02182c3..2074f98069 100644 --- a/src/gui/painting/qoutlinemapper.cpp +++ b/src/gui/painting/qoutlinemapper.cpp @@ -42,6 +42,7 @@ #include "qbezier_p.h" #include "qmath.h" #include "qpainterpath_p.h" +#include "qscopedvaluerollback.h" #include <stdlib.h> @@ -354,7 +355,7 @@ void QOutlineMapper::clipElements(const QPointF *elements, // instead of going through convenience functionallity, but since // this part of code hardly every used, it shouldn't matter. - m_in_clip_elements = true; + QScopedValueRollback<bool> in_clip_elements(m_in_clip_elements, true); QPainterPath path; @@ -397,8 +398,6 @@ void QOutlineMapper::clipElements(const QPointF *elements, convertPath(clippedPath); m_transform = oldTransform; } - - m_in_clip_elements = false; } QT_END_NAMESPACE diff --git a/src/gui/painting/qoutlinemapper_p.h b/src/gui/painting/qoutlinemapper_p.h index 71999fbdee..04a68797c2 100644 --- a/src/gui/painting/qoutlinemapper_p.h +++ b/src/gui/painting/qoutlinemapper_p.h @@ -180,13 +180,13 @@ public: QT_FT_Outline *outline() { if (m_valid) return &m_outline; - return 0; + return nullptr; } QT_FT_Outline *convertPath(const QPainterPath &path); QT_FT_Outline *convertPath(const QVectorPath &path); - inline QPainterPath::ElementType *elementTypes() const { return m_element_types.size() == 0 ? 0 : m_element_types.data(); } + inline QPainterPath::ElementType *elementTypes() const { return m_element_types.size() == 0 ? nullptr : m_element_types.data(); } public: QDataBuffer<QPainterPath::ElementType> m_element_types; diff --git a/src/gui/painting/qpagedpaintdevice.cpp b/src/gui/painting/qpagedpaintdevice.cpp index 613a686848..72b2834470 100644 --- a/src/gui/painting/qpagedpaintdevice.cpp +++ b/src/gui/painting/qpagedpaintdevice.cpp @@ -290,7 +290,8 @@ QPagedPaintDevicePrivate *QPagedPaintDevice::dd() \value PdfVersion_A1b A PDF/A-1b compatible document is produced. - \since 5.10 + \value PdfVersion_1_6 A PDF 1.6 compatible document is produced. + This value was added in Qt 5.12. */ /*! diff --git a/src/gui/painting/qpagedpaintdevice.h b/src/gui/painting/qpagedpaintdevice.h index c8957edab8..1c37c17fa3 100644 --- a/src/gui/painting/qpagedpaintdevice.h +++ b/src/gui/painting/qpagedpaintdevice.h @@ -213,7 +213,7 @@ public: Envelope10 = Comm10E }; - enum PdfVersion { PdfVersion_1_4, PdfVersion_A1b }; + enum PdfVersion { PdfVersion_1_4, PdfVersion_A1b, PdfVersion_1_6 }; // ### Qt6 Make these virtual bool setPageLayout(const QPageLayout &pageLayout); diff --git a/src/gui/painting/qpagelayout.h b/src/gui/painting/qpagelayout.h index b41689d33b..faf0827c1a 100644 --- a/src/gui/painting/qpagelayout.h +++ b/src/gui/painting/qpagelayout.h @@ -83,12 +83,12 @@ public: const QMarginsF &minMargins = QMarginsF(0, 0, 0, 0)); QPageLayout(const QPageLayout &other); #ifdef Q_COMPILER_RVALUE_REFS - QPageLayout &operator=(QPageLayout &&other) Q_DECL_NOTHROW { swap(other); return *this; } + QPageLayout &operator=(QPageLayout &&other) noexcept { swap(other); return *this; } #endif QPageLayout &operator=(const QPageLayout &other); ~QPageLayout(); - void swap(QPageLayout &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QPageLayout &other) noexcept { qSwap(d, other.d); } friend Q_GUI_EXPORT bool operator==(const QPageLayout &lhs, const QPageLayout &rhs); bool isEquivalentTo(const QPageLayout &other) const; diff --git a/src/gui/painting/qpagesize.h b/src/gui/painting/qpagesize.h index 82054824b4..a2ea691677 100644 --- a/src/gui/painting/qpagesize.h +++ b/src/gui/painting/qpagesize.h @@ -237,13 +237,13 @@ public: SizeMatchPolicy matchPolicy = FuzzyMatch); QPageSize(const QPageSize &other); #ifdef Q_COMPILER_RVALUE_REFS - QPageSize &operator=(QPageSize &&other) Q_DECL_NOTHROW { swap(other); return *this; } + QPageSize &operator=(QPageSize &&other) noexcept { swap(other); return *this; } #endif QPageSize &operator=(const QPageSize &other); ~QPageSize(); - void swap(QPageSize &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QPageSize &other) noexcept { qSwap(d, other.d); } friend Q_GUI_EXPORT bool operator==(const QPageSize &lhs, const QPageSize &rhs); bool isEquivalentTo(const QPageSize &other) const; diff --git a/src/gui/painting/qpaintdevice.cpp b/src/gui/painting/qpaintdevice.cpp index faaa316085..0ddfba6ee9 100644 --- a/src/gui/painting/qpaintdevice.cpp +++ b/src/gui/painting/qpaintdevice.cpp @@ -41,7 +41,7 @@ QT_BEGIN_NAMESPACE -QPaintDevice::QPaintDevice() Q_DECL_NOEXCEPT +QPaintDevice::QPaintDevice() noexcept { reserved = 0; painters = 0; diff --git a/src/gui/painting/qpaintdevice.h b/src/gui/painting/qpaintdevice.h index 9458b4ba9a..5f8dad205d 100644 --- a/src/gui/painting/qpaintdevice.h +++ b/src/gui/painting/qpaintdevice.h @@ -90,7 +90,7 @@ public: static inline qreal devicePixelRatioFScale() { return 0x10000; } protected: - QPaintDevice() Q_DECL_NOEXCEPT; + QPaintDevice() noexcept; virtual int metric(PaintDeviceMetric metric) const; virtual void initPainter(QPainter *painter) const; virtual QPaintDevice *redirected(QPoint *offset) const; diff --git a/src/gui/painting/qpaintengine.cpp b/src/gui/painting/qpaintengine.cpp index 1aee7d5f74..bfe1c9cadf 100644 --- a/src/gui/painting/qpaintengine.cpp +++ b/src/gui/painting/qpaintengine.cpp @@ -913,7 +913,7 @@ void QPaintEngine::setPaintDevice(QPaintDevice *device) /*! Returns the device that this engine is painting on, if painting is - active; otherwise returns 0. + active; otherwise returns \nullptr. */ QPaintDevice *QPaintEngine::paintDevice() const { diff --git a/src/gui/painting/qpaintengine_p.h b/src/gui/painting/qpaintengine_p.h index 8ac3fcff5c..40b9474165 100644 --- a/src/gui/painting/qpaintengine_p.h +++ b/src/gui/painting/qpaintengine_p.h @@ -65,7 +65,7 @@ class Q_GUI_EXPORT QPaintEnginePrivate { Q_DECLARE_PUBLIC(QPaintEngine) public: - QPaintEnginePrivate() : pdev(0), q_ptr(0), currentClipDevice(0), hasSystemTransform(0), + QPaintEnginePrivate() : pdev(nullptr), q_ptr(nullptr), currentClipDevice(nullptr), hasSystemTransform(0), hasSystemViewport(0) {} virtual ~QPaintEnginePrivate(); @@ -138,8 +138,8 @@ public: static QPaintEnginePrivate *get(QPaintEngine *paintEngine) { return paintEngine->d_func(); } - virtual QPaintEngine *aggregateEngine() { return 0; } - virtual Qt::HANDLE nativeHandle() { return 0; } + virtual QPaintEngine *aggregateEngine() { return nullptr; } + virtual Qt::HANDLE nativeHandle() { return nullptr; } }; QT_END_NAMESPACE diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 7caaf3a8fa..b0dec5cf78 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -555,35 +555,6 @@ bool QRasterPaintEngine::end() /*! \internal */ -void QRasterPaintEngine::releaseBuffer() -{ - Q_D(QRasterPaintEngine); - d->rasterBuffer.reset(new QRasterBuffer); -} - -/*! - \internal -*/ -QSize QRasterPaintEngine::size() const -{ - Q_D(const QRasterPaintEngine); - return QSize(d->rasterBuffer->width(), d->rasterBuffer->height()); -} - -/*! - \internal -*/ -#ifndef QT_NO_DEBUG -void QRasterPaintEngine::saveBuffer(const QString &s) const -{ - Q_D(const QRasterPaintEngine); - d->rasterBuffer->bufferImage().save(s, "PNG"); -} -#endif - -/*! - \internal -*/ void QRasterPaintEngine::updateMatrix(const QTransform &matrix) { QRasterPaintEngineState *s = state(); @@ -873,7 +844,7 @@ void QRasterPaintEngine::updateRasterState() && s->intOpacity == 256 && (mode == QPainter::CompositionMode_Source || (mode == QPainter::CompositionMode_SourceOver - && s->penData.solid.color.isOpaque())); + && s->penData.solidColor.isOpaque())); } s->dirty = 0; @@ -997,6 +968,10 @@ void QRasterPaintEnginePrivate::drawImage(const QPointF &pt, { if (alpha == 0 || !clip.isValid()) return; + if (pt.x() > qreal(clip.right()) || pt.y() > qreal(clip.bottom())) + return; + if ((pt.x() + img.width()) < qreal(clip.left()) || (pt.y() + img.height()) < qreal(clip.top())) + return; Q_ASSERT(img.depth() >= 8); @@ -1063,6 +1038,10 @@ void QRasterPaintEnginePrivate::blitImage(const QPointF &pt, { if (!clip.isValid()) return; + if (pt.x() > qreal(clip.right()) || pt.y() > qreal(clip.bottom())) + return; + if ((pt.x() + img.width()) < qreal(clip.left()) || (pt.y() + img.height()) < qreal(clip.top())) + return; Q_ASSERT(img.depth() >= 8); @@ -1528,9 +1507,9 @@ static void fillRect_normalized(const QRect &r, QSpanData *data, if (data->fillRect && (mode == QPainter::CompositionMode_Source || (mode == QPainter::CompositionMode_SourceOver - && data->solid.color.isOpaque()))) + && data->solidColor.isOpaque()))) { - data->fillRect(data->rasterBuffer, x1, y1, width, height, data->solid.color); + data->fillRect(data->rasterBuffer, x1, y1, width, height, data->solidColor); return; } } @@ -1892,9 +1871,9 @@ void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color) Q_D(QRasterPaintEngine); QRasterPaintEngineState *s = state(); - d->solid_color_filler.solid.color = qPremultiply(combineAlpha256(color.rgba64(), s->intOpacity)); + d->solid_color_filler.solidColor = qPremultiply(combineAlpha256(color.rgba64(), s->intOpacity)); - if (d->solid_color_filler.solid.color.isTransparent() + if (d->solid_color_filler.solidColor.isTransparent() && s->composition_mode == QPainter::CompositionMode_SourceOver) { return; } @@ -2348,14 +2327,14 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe case QImage::Format_A2BGR30_Premultiplied: case QImage::Format_A2RGB30_Premultiplied: // Combine premultiplied color with the opacity set on the painter. - d->solid_color_filler.solid.color = multiplyAlpha256(QRgba64::fromArgb32(color), s->intOpacity); + d->solid_color_filler.solidColor = multiplyAlpha256(QRgba64::fromArgb32(color), s->intOpacity); break; default: - d->solid_color_filler.solid.color = qPremultiply(combineAlpha256(QRgba64::fromArgb32(color), s->intOpacity)); + d->solid_color_filler.solidColor = qPremultiply(combineAlpha256(QRgba64::fromArgb32(color), s->intOpacity)); break; } - if (d->solid_color_filler.solid.color.isTransparent() && s->composition_mode == QPainter::CompositionMode_SourceOver) + if (d->solid_color_filler.solidColor.isTransparent() && s->composition_mode == QPainter::CompositionMode_SourceOver) return; d->solid_color_filler.clip = d->clip(); @@ -2705,20 +2684,20 @@ void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx if (unclipped) { if (depth == 1) { if (s->penData.bitmapBlit) { - s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color, + s->penData.bitmapBlit(rb, rx, ry, s->penData.solidColor, scanline, w, h, bpl); return; } } else if (depth == 8) { if (s->penData.alphamapBlit) { - s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color, + s->penData.alphamapBlit(rb, rx, ry, s->penData.solidColor, scanline, w, h, bpl, 0, useGammaCorrection); return; } } else if (depth == 32) { // (A)RGB Alpha mask where the alpha component is not used. if (s->penData.alphaRGBBlit) { - s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color, + s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solidColor, (const uint *) scanline, w, h, bpl / 4, 0, useGammaCorrection); return; } @@ -2747,10 +2726,10 @@ void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx ry = ny; } if (depth == 8) - s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color, + s->penData.alphamapBlit(rb, rx, ry, s->penData.solidColor, scanline, w, h, bpl, clip, useGammaCorrection); else if (depth == 32) - s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color, + s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solidColor, (const uint *) scanline, w, h, bpl / 4, clip, useGammaCorrection); return; } @@ -3428,7 +3407,7 @@ bool QRasterPaintEngine::requiresPretransformedGlyphPositions(QFontEngine *fontE } /*! - Indicates whether glyph caching is supported by the font engine + Returns whether glyph caching is supported by the font engine \a fontEngine with the given transform \a m applied. */ bool QRasterPaintEngine::shouldDrawCachedGlyphs(QFontEngine *fontEngine, const QTransform &m) const @@ -3827,7 +3806,6 @@ QImage::Format QRasterBuffer::prepare(QImage *image) bytes_per_line = image->bytesPerLine(); format = image->format(); - drawHelper = qDrawHelper + format; if (image->depth() == 1 && image->colorTable().size() == 2) { monoDestinationWithClut = true; const QVector<QRgb> colorTable = image->colorTable(); @@ -3838,11 +3816,6 @@ QImage::Format QRasterBuffer::prepare(QImage *image) return format; } -void QRasterBuffer::resetBuffer(int val) -{ - memset(m_buffer, val, m_height*bytes_per_line); -} - QClipData::QClipData(int height) { clipSpanHeight = height; @@ -3875,51 +3848,15 @@ void QClipData::initialize() Q_CHECK_PTR(m_clipLines); QT_TRY { - m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan)); allocated = clipSpanHeight; - Q_CHECK_PTR(m_spans); - QT_TRY { - if (hasRectClip) { - int y = 0; - while (y < ymin) { - m_clipLines[y].spans = 0; - m_clipLines[y].count = 0; - ++y; - } - - const int len = clipRect.width(); - count = 0; - while (y < ymax) { - QSpan *span = m_spans + count; - span->x = xmin; - span->len = len; - span->y = y; - span->coverage = 255; - ++count; - - m_clipLines[y].spans = span; - m_clipLines[y].count = 1; - ++y; - } - - while (y < clipSpanHeight) { - m_clipLines[y].spans = 0; - m_clipLines[y].count = 0; - ++y; - } - } else if (hasRegionClip) { - + if (hasRegionClip) { const auto rects = clipRegion.begin(); const int numRects = clipRegion.rectCount(); - - { // resize - const int maxSpans = (ymax - ymin) * numRects; - if (maxSpans > allocated) { - m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan))); - allocated = maxSpans; - } - } + const int maxSpans = (ymax - ymin) * numRects; + allocated = qMax(allocated, maxSpans); + m_spans = (QSpan *)malloc(allocated * sizeof(QSpan)); + Q_CHECK_PTR(m_spans); int y = 0; int firstInBand = 0; @@ -3966,6 +3903,40 @@ void QClipData::initialize() ++y; } + return; + } + + m_spans = (QSpan *)malloc(allocated * sizeof(QSpan)); + Q_CHECK_PTR(m_spans); + + if (hasRectClip) { + int y = 0; + while (y < ymin) { + m_clipLines[y].spans = 0; + m_clipLines[y].count = 0; + ++y; + } + + const int len = clipRect.width(); + count = 0; + while (y < ymax) { + QSpan *span = m_spans + count; + span->x = xmin; + span->len = len; + span->y = y; + span->coverage = 255; + ++count; + + m_clipLines[y].spans = span; + m_clipLines[y].count = 1; + ++y; + } + + while (y < clipSpanHeight) { + m_clipLines[y].spans = 0; + m_clipLines[y].count = 0; + ++y; + } } } QT_CATCH(...) { free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized. @@ -4267,55 +4238,13 @@ static void qt_span_clip(int count, const QSpan *spans, void *userData) } } -#ifndef QT_NO_DEBUG -QImage QRasterBuffer::bufferImage() const -{ - QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied); - - for (int y = 0; y < m_height; ++y) { - uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y); - - for (int x=0; x<m_width; ++x) { - uint argb = span[x]; - image.setPixel(x, y, argb); - } - } - return image; -} -#endif - - -void QRasterBuffer::flushToARGBImage(QImage *target) const -{ - int w = qMin(m_width, target->width()); - int h = qMin(m_height, target->height()); - - for (int y=0; y<h; ++y) { - uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y); - QRgb *dest = (QRgb *) target->scanLine(y); - for (int x=0; x<w; ++x) { - QRgb pixel = sourceLine[x]; - int alpha = qAlpha(pixel); - if (!alpha) { - dest[x] = 0; - } else { - dest[x] = (alpha << 24) - | ((255*qRed(pixel)/alpha) << 16) - | ((255*qGreen(pixel)/alpha) << 8) - | ((255*qBlue(pixel)/alpha) << 0); - } - } - } -} - - class QGradientCache { public: struct CacheInfo : QSpanData::Pinnable { inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) : - stops(qMove(s)), opacity(op), interpolationMode(mode) {} + stops(std::move(s)), opacity(op), interpolationMode(mode) {} QRgba64 buffer64[GRADIENT_STOPTABLE_SIZE]; QRgb buffer32[GRADIENT_STOPTABLE_SIZE]; QGradientStops stops; @@ -4588,8 +4517,8 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode case Qt::SolidPattern: { type = Solid; QColor c = qbrush_color(brush); - solid.color = qPremultiply(combineAlpha256(c.rgba64(), alpha)); - if (solid.color.isTransparent() && compositionMode == QPainter::CompositionMode_SourceOver) + solidColor = qPremultiply(combineAlpha256(c.rgba64(), alpha)); + if (solidColor.isTransparent() && compositionMode == QPainter::CompositionMode_SourceOver) type = None; break; } @@ -4602,7 +4531,9 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha); gradient.colorTable32 = cacheInfo->buffer32; +#if QT_CONFIG(raster_64bit) gradient.colorTable64 = cacheInfo->buffer64; +#endif cachedGradient = std::move(cacheInfo); gradient.spread = g->spread(); @@ -4624,7 +4555,9 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha); gradient.colorTable32 = cacheInfo->buffer32; +#if QT_CONFIG(raster_64bit) gradient.colorTable64 = cacheInfo->buffer64; +#endif cachedGradient = std::move(cacheInfo); gradient.spread = g->spread(); @@ -4650,7 +4583,9 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha); gradient.colorTable32 = cacheInfo->buffer32; +#if QT_CONFIG(raster_64bit) gradient.colorTable64 = cacheInfo->buffer64; +#endif cachedGradient = std::move(cacheInfo); gradient.spread = QGradient::RepeatSpread; @@ -4715,17 +4650,19 @@ void QSpanData::adjustSpanMethods() case None: unclipped_blend = 0; break; - case Solid: - unclipped_blend = rasterBuffer->drawHelper->blendColor; - bitmapBlit = rasterBuffer->drawHelper->bitmapBlit; - alphamapBlit = rasterBuffer->drawHelper->alphamapBlit; - alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit; - fillRect = rasterBuffer->drawHelper->fillRect; + case Solid: { + const DrawHelper &drawHelper = qDrawHelper[rasterBuffer->format]; + unclipped_blend = drawHelper.blendColor; + bitmapBlit = drawHelper.bitmapBlit; + alphamapBlit = drawHelper.alphamapBlit; + alphaRGBBlit = drawHelper.alphaRGBBlit; + fillRect = drawHelper.fillRect; break; + } case LinearGradient: case RadialGradient: case ConicalGradient: - unclipped_blend = rasterBuffer->drawHelper->blendGradient; + unclipped_blend = qBlendGradient; break; case Texture: unclipped_blend = qBlendTexture; diff --git a/src/gui/painting/qpaintengine_raster_p.h b/src/gui/painting/qpaintengine_raster_p.h index 14eddf07b1..500e0fae54 100644 --- a/src/gui/painting/qpaintengine_raster_p.h +++ b/src/gui/painting/qpaintengine_raster_p.h @@ -208,15 +208,6 @@ public: ClipType clipType() const; QRect clipBoundingRect() const; - void releaseBuffer(); - - QSize size() const; - -#ifndef QT_NO_DEBUG - void saveBuffer(const QString &s) const; -#endif - - #ifdef Q_OS_WIN void setDC(HDC hdc); HDC getDC() const; @@ -435,27 +426,16 @@ inline void QClipData::appendSpans(const QSpan *s, int num) class QRasterBuffer { public: - QRasterBuffer() : m_width(0), m_height(0), m_buffer(0) { init(); } + QRasterBuffer() : m_width(0), m_height(0), m_buffer(nullptr) { init(); } ~QRasterBuffer(); void init(); QImage::Format prepare(QImage *image); - QImage::Format prepare(QPixmap *pix); - void prepare(int w, int h); - void prepareBuffer(int w, int h); - - void resetBuffer(int val=0); uchar *scanLine(int y) { Q_ASSERT(y>=0); Q_ASSERT(y<m_height); return m_buffer + y * bytes_per_line; } -#ifndef QT_NO_DEBUG - QImage bufferImage() const; -#endif - - void flushToARGBImage(QImage *image) const; - int width() const { return m_width; } int height() const { return m_height; } int bytesPerLine() const { return bytes_per_line; } @@ -471,7 +451,6 @@ public: QPainter::CompositionMode compositionMode; QImage::Format format; - DrawHelper *drawHelper; QImage colorizeBitmap(const QImage &image, const QColor &color); private: diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp index 9f07af92e4..22d3fb3001 100644 --- a/src/gui/painting/qpaintengineex.cpp +++ b/src/gui/painting/qpaintengineex.cpp @@ -439,6 +439,9 @@ void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) } } + if (d->activeStroker == &d->stroker) + d->stroker.setForceOpen(path.hasExplicitOpen()); + const QPainterPath::ElementType *types = path.elements(); const qreal *points = path.points(); int pointCount = path.elementCount(); @@ -1090,9 +1093,14 @@ bool QPaintEngineEx::shouldDrawCachedGlyphs(QFontEngine *fontEngine, const QTran if (fontEngine->glyphFormat == QFontEngine::Format_ARGB) return true; + static const int maxCachedGlyphSizeSquared = std::pow([]{ + if (int env = qEnvironmentVariableIntValue("QT_MAX_CACHED_GLYPH_SIZE")) + return env; + return QT_MAX_CACHED_GLYPH_SIZE; + }(), 2); + qreal pixelSize = fontEngine->fontDef.pixelSize; - return (pixelSize * pixelSize * qAbs(m.determinant())) < - QT_MAX_CACHED_GLYPH_SIZE * QT_MAX_CACHED_GLYPH_SIZE; + return (pixelSize * pixelSize * qAbs(m.determinant())) <= maxCachedGlyphSizeSquared; } QT_END_NAMESPACE diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index b70b29e54e..95e6bda78b 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -1437,6 +1437,13 @@ void QPainterPrivate::updateState(QPainterState *newState) by slightly less than half a pixel. Also will treat default constructed pens as cosmetic. Potentially useful when porting a Qt 4 application to Qt 5. + \value LosslessImageRendering Use a lossless image rendering, whenever possible. + Currently, this hint is only used when QPainter is employed to output a PDF + file through QPrinter or QPdfWriter, where drawImage()/drawPixmap() calls + will encode images using a lossless compression algorithm instead of lossy + JPEG compression. + This value was added in Qt 5.13. + \sa renderHints(), setRenderHint(), {QPainter#Rendering Quality}{Rendering Quality}, {Concentric Circles Example} @@ -1514,7 +1521,7 @@ QPainter::~QPainter() /*! Returns the paint device on which this painter is currently - painting, or 0 if the painter is not active. + painting, or \nullptr if the painter is not active. \sa isActive() */ @@ -1540,6 +1547,7 @@ bool QPainter::isActive() const return d->engine; } +#if QT_DEPRECATED_SINCE(5, 13) /*! Initializes the painters pen, background and font to the same as the given \a device. @@ -1567,7 +1575,7 @@ void QPainter::initFrom(const QPaintDevice *device) d->engine->setDirty(QPaintEngine::DirtyFont); } } - +#endif /*! Saves the current painter state (pushes the state onto a stack). A @@ -2878,6 +2886,7 @@ void QPainter::setClipRegion(const QRegion &r, Qt::ClipOperation op) d->updateState(d->state); } +#if QT_DEPRECATED_SINCE(5, 13) /*! \since 4.2 \obsolete @@ -2972,7 +2981,10 @@ void QPainter::setMatrix(const QMatrix &matrix, bool combine) const QMatrix &QPainter::matrix() const { +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED return worldMatrix(); +QT_WARNING_POP } @@ -3041,7 +3053,7 @@ void QPainter::resetMatrix() { resetTransform(); } - +#endif /*! \since 4.2 @@ -3092,6 +3104,7 @@ bool QPainter::worldMatrixEnabled() const return d->state->WxF; } +#if QT_DEPRECATED_SINCE(5, 13) /*! \obsolete @@ -3117,6 +3130,7 @@ bool QPainter::matrixEnabled() const { return worldMatrixEnabled(); } +#endif /*! Scales the coordinate system by (\a{sx}, \a{sy}). @@ -4175,6 +4189,7 @@ void QPainter::drawRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius, Draws the given rectangle \a x, \a y, \a w, \a h with rounded corners. */ +#if QT_DEPRECATED_SINCE(5, 13) /*! \obsolete @@ -4202,6 +4217,10 @@ void QPainter::drawRoundRect(const QRectF &r, int xRnd, int yRnd) Draws the rectangle \a r with rounded corners. */ +void QPainter::drawRoundRect(const QRect &rect, int xRnd, int yRnd) +{ + drawRoundedRect(QRectF(rect), xRnd, yRnd, Qt::RelativeSize); +} /*! \obsolete @@ -4212,6 +4231,11 @@ void QPainter::drawRoundRect(const QRectF &r, int xRnd, int yRnd) Draws the rectangle \a x, \a y, \a w, \a h with rounded corners. */ +void QPainter::drawRoundRect(int x, int y, int w, int h, int xRnd, int yRnd) +{ + drawRoundedRect(QRectF(x, y, w, h), xRnd, yRnd, Qt::RelativeSize); +} +#endif /*! \fn void QPainter::drawEllipse(const QRectF &rectangle) @@ -6203,7 +6227,7 @@ static QPixmap generateWavyPixmap(qreal maxRadius, const QPen &pen) % HexString<qreal>(pen.widthF()); QPixmap pixmap; - if (QPixmapCache::find(key, pixmap)) + if (QPixmapCache::find(key, &pixmap)) return pixmap; const qreal halfPeriod = qMax(qreal(2), qreal(radiusBase * 1.61803399)); // the golden ratio @@ -7371,6 +7395,7 @@ void QPainter::setViewTransformEnabled(bool enable) d->updateMatrix(); } +#if QT_DEPRECATED_SINCE(5, 13) /*! \threadsafe @@ -7451,6 +7476,7 @@ QPaintDevice *QPainter::redirected(const QPaintDevice *device, QPoint *offset) Q_UNUSED(offset) return 0; } +#endif void qt_format_text(const QFont &fnt, const QRectF &_r, int tf, const QString& str, QRectF *brect, @@ -8060,6 +8086,7 @@ QFont QPaintEngineState::font() const return static_cast<const QPainterState *>(this)->font; } +#if QT_DEPRECATED_SINCE(5, 13) /*! \since 4.2 \obsolete @@ -8082,6 +8109,7 @@ QMatrix QPaintEngineState::matrix() const return st->matrix.toAffine(); } +#endif /*! \since 4.3 @@ -8321,7 +8349,7 @@ void QPainter::resetTransform() d->state->ww = d->state->vw = d->device->metric(QPaintDevice::PdmWidth); d->state->wh = d->state->vh = d->device->metric(QPaintDevice::PdmHeight); d->state->worldMatrix = QTransform(); - setMatrixEnabled(false); + setWorldMatrixEnabled(false); setViewTransformEnabled(false); if (d->extended) d->extended->transformChanged(); diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h index 482f5fb63d..843f24e3e1 100644 --- a/src/gui/painting/qpainter.h +++ b/src/gui/painting/qpainter.h @@ -91,7 +91,8 @@ public: SmoothPixmapTransform = 0x04, HighQualityAntialiasing = 0x08, NonCosmeticDefaultPen = 0x10, - Qt4CompatiblePainting = 0x20 + Qt4CompatiblePainting = 0x20, + LosslessImageRendering = 0x40, }; Q_FLAG(RenderHint) @@ -131,7 +132,10 @@ public: bool end(); bool isActive() const; +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED_X("Use begin(QPaintDevice*) instead") void initFrom(const QPaintDevice *device); +#endif enum CompositionMode { CompositionMode_SourceOver, @@ -231,28 +235,40 @@ public: void restore(); // XForm functions +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED_X("Use setTransform() instead") void setMatrix(const QMatrix &matrix, bool combine = false); + QT_DEPRECATED_X("Use transform() instead") const QMatrix &matrix() const; + QT_DEPRECATED_X("Use deviceTransform() instead") const QMatrix &deviceMatrix() const; + QT_DEPRECATED_X("Use resetTransform() instead") void resetMatrix(); +#endif void setTransform(const QTransform &transform, bool combine = false); const QTransform &transform() const; const QTransform &deviceTransform() const; void resetTransform(); +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED_X("Use setWorldTransform() instead") void setWorldMatrix(const QMatrix &matrix, bool combine = false); + QT_DEPRECATED_X("Use worldTransform() instead") const QMatrix &worldMatrix() const; + QT_DEPRECATED_X("Use combinedTransform() instead") + QMatrix combinedMatrix() const; + QT_DEPRECATED_X("Use setWorldMatrixEnabled() instead") + void setMatrixEnabled(bool enabled); + QT_DEPRECATED_X("Use worldMatrixEnabled() instead") + bool matrixEnabled() const; +#endif void setWorldTransform(const QTransform &matrix, bool combine = false); const QTransform &worldTransform() const; - QMatrix combinedMatrix() const; QTransform combinedTransform() const; - void setMatrixEnabled(bool enabled); - bool matrixEnabled() const; - void setWorldMatrixEnabled(bool enabled); bool worldMatrixEnabled() const; @@ -354,9 +370,14 @@ public: inline void drawRoundedRect(const QRect &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode = Qt::AbsoluteSize); +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED_X("Use drawRoundedRect(..., Qt::RelativeSize) instead") void drawRoundRect(const QRectF &r, int xround = 25, int yround = 25); - inline void drawRoundRect(int x, int y, int w, int h, int = 25, int = 25); - inline void drawRoundRect(const QRect &r, int xround = 25, int yround = 25); + QT_DEPRECATED_X("Use drawRoundedRect(..., Qt::RelativeSize) instead") + void drawRoundRect(int x, int y, int w, int h, int = 25, int = 25); + QT_DEPRECATED_X("Use drawRoundedRect(..., Qt::RelativeSize) instead") + void drawRoundRect(const QRect &r, int xround = 25, int yround = 25); +#endif void drawTiledPixmap(const QRectF &rect, const QPixmap &pm, const QPointF &offset = QPointF()); inline void drawTiledPixmap(int x, int y, int w, int h, const QPixmap &, int sx=0, int sy=0); @@ -463,10 +484,15 @@ public: QPaintEngine *paintEngine() const; +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED_X("Use QWidget::render() instead") static void setRedirected(const QPaintDevice *device, QPaintDevice *replacement, const QPoint& offset = QPoint()); + QT_DEPRECATED_X("Use QWidget::render() instead") static QPaintDevice *redirected(const QPaintDevice *device, QPoint *offset = nullptr); + QT_DEPRECATED_X("Use QWidget::render() instead") static void restoreRedirected(const QPaintDevice *device); +#endif void beginNativePainting(); void endNativePainting(); @@ -628,16 +654,6 @@ inline void QPainter::drawPoints(const QPolygon &points) drawPoints(points.constData(), points.size()); } -inline void QPainter::drawRoundRect(int x, int y, int w, int h, int xRnd, int yRnd) -{ - drawRoundRect(QRectF(x, y, w, h), xRnd, yRnd); -} - -inline void QPainter::drawRoundRect(const QRect &rect, int xRnd, int yRnd) -{ - drawRoundRect(QRectF(rect), xRnd, yRnd); -} - inline void QPainter::drawRoundedRect(int x, int y, int w, int h, qreal xRadius, qreal yRadius, Qt::SizeMode mode) { diff --git a/src/gui/painting/qpainter_p.h b/src/gui/painting/qpainter_p.h index 2d44577310..bd2bc4c9b3 100644 --- a/src/gui/painting/qpainter_p.h +++ b/src/gui/painting/qpainter_p.h @@ -51,8 +51,11 @@ // We mean it. // +#include <QtCore/qvarlengtharray.h> #include <QtGui/private/qtguiglobal_p.h> #include "QtGui/qbrush.h" +#include "QtGui/qcolorspace.h" +#include "QtGui/qcolortransform.h" #include "QtGui/qfont.h" #include "QtGui/qpen.h" #include "QtGui/qregion.h" @@ -190,9 +193,9 @@ class QPainterPrivate Q_DECLARE_PUBLIC(QPainter) public: QPainterPrivate(QPainter *painter) - : q_ptr(painter), d_ptrs(0), state(0), dummyState(0), txinv(0), inDestructor(false), d_ptrs_size(0), - refcount(1), device(0), original_device(0), helper_device(0), engine(0), emulationEngine(0), - extended(0) + : q_ptr(painter), d_ptrs(nullptr), state(nullptr), dummyState(nullptr), txinv(0), inDestructor(false), d_ptrs_size(0), + refcount(1), device(nullptr), original_device(nullptr), helper_device(nullptr), engine(nullptr), emulationEngine(nullptr), + extended(nullptr) { } @@ -202,7 +205,7 @@ public: QPainterPrivate **d_ptrs; QPainterState *state; - QVector<QPainterState*> states; + QVarLengthArray<QPainterState *, 8> states; mutable QPainterDummyState *dummyState; diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index 2574a00838..649cfd554b 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -527,7 +527,7 @@ void QPainterPath::setElementPositionAt(int i, qreal x, qreal y) /*! Constructs an empty QPainterPath object. */ -QPainterPath::QPainterPath() Q_DECL_NOEXCEPT +QPainterPath::QPainterPath() noexcept : d_ptr(0) { } @@ -629,6 +629,55 @@ QPainterPath::~QPainterPath() } /*! + Clears the path elements stored. + + This allows the path to reuse previous memory allocations. + + \sa reserve(), capacity() + \since 5.13 +*/ +void QPainterPath::clear() +{ + if (!d_ptr) + return; + + detach(); + d_func()->clear(); +} + +/*! + Reserves a given amount of elements in QPainterPath's internal memory. + + Attempts to allocate memory for at least \a size elements. + + \sa clear(), capacity(), QVector::reserve() + \since 5.13 +*/ +void QPainterPath::reserve(int size) +{ + Q_D(QPainterPath); + if ((!d && size > 0) || (d && d->elements.capacity() < size)) { + detach(); + d->elements.reserve(size); + } +} + +/*! + Returns the number of elements allocated by the QPainterPath. + + \sa clear(), reserve() + \since 5.13 +*/ +int QPainterPath::capacity() const +{ + Q_D(QPainterPath); + if (d) + return d->elements.capacity(); + + return 0; +} + +/*! Closes the current subpath by drawing a line to the beginning of the subpath, automatically starting a new path. The current point of the new path is (0, 0). @@ -2271,13 +2320,19 @@ static inline bool epsilonCompare(const QPointF &a, const QPointF &b, const QSiz bool QPainterPath::operator==(const QPainterPath &path) const { QPainterPathData *d = reinterpret_cast<QPainterPathData *>(d_func()); - if (path.d_func() == d) + QPainterPathData *other_d = path.d_func(); + if (other_d == d) return true; - else if (!d || !path.d_func()) + else if (!d || !other_d) { + if (!d && other_d->elements.empty() && other_d->fillRule == Qt::OddEvenFill) + return true; + if (!other_d && d && d->elements.empty() && d->fillRule == Qt::OddEvenFill) + return true; return false; - else if (d->fillRule != path.d_func()->fillRule) + } + else if (d->fillRule != other_d->fillRule) return false; - else if (d->elements.size() != path.d_func()->elements.size()) + else if (d->elements.size() != other_d->elements.size()) return false; const qreal qt_epsilon = sizeof(qreal) == sizeof(double) ? 1e-12 : qreal(1e-5); @@ -2287,8 +2342,8 @@ bool QPainterPath::operator==(const QPainterPath &path) const epsilon.rheight() *= qt_epsilon; for (int i = 0; i < d->elements.size(); ++i) - if (d->elements.at(i).type != path.d_func()->elements.at(i).type - || !epsilonCompare(d->elements.at(i), path.d_func()->elements.at(i), epsilon)) + if (d->elements.at(i).type != other_d->elements.at(i).type + || !epsilonCompare(d->elements.at(i), other_d->elements.at(i), epsilon)) return false; return true; @@ -2476,7 +2531,7 @@ QDataStream &operator>>(QDataStream &s, QPainterPath &p) s >> p.d_func()->cStart; int fillRule; s >> fillRule; - Q_ASSERT(fillRule == Qt::OddEvenFill || Qt::WindingFill); + Q_ASSERT(fillRule == Qt::OddEvenFill || fillRule == Qt::WindingFill); p.d_func()->fillRule = Qt::FillRule(fillRule); p.d_func()->dirtyBounds = true; p.d_func()->dirtyControlBounds = true; @@ -3187,6 +3242,7 @@ void QPainterPath::addRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadi Adds the given rectangle \a x, \a y, \a w, \a h with rounded corners to the path. */ +#if QT_DEPRECATED_SINCE(5, 13) /*! \obsolete @@ -3253,6 +3309,17 @@ void QPainterPath::addRoundRect(const QRectF &r, int xRnd, int yRnd) \sa addRoundedRect() */ +void QPainterPath::addRoundRect(const QRectF &rect, + int roundness) +{ + int xRnd = roundness; + int yRnd = roundness; + if (rect.width() > rect.height()) + xRnd = int(roundness * rect.height()/rect.width()); + else + yRnd = int(roundness * rect.width()/rect.height()); + addRoundedRect(rect, xRnd, yRnd, Qt::RelativeSize); +} /*! \obsolete @@ -3269,6 +3336,11 @@ void QPainterPath::addRoundRect(const QRectF &r, int xRnd, int yRnd) \sa addRoundedRect() */ +void QPainterPath::addRoundRect(qreal x, qreal y, qreal w, qreal h, + int xRnd, int yRnd) +{ + addRoundedRect(QRectF(x, y, w, h), xRnd, yRnd, Qt::RelativeSize); +} /*! \obsolete @@ -3288,6 +3360,12 @@ void QPainterPath::addRoundRect(const QRectF &r, int xRnd, int yRnd) \sa addRoundedRect() */ +void QPainterPath::addRoundRect(qreal x, qreal y, qreal w, qreal h, + int roundness) +{ + addRoundedRect(QRectF(x, y, w, h), roundness, Qt::RelativeSize); +} +#endif /*! \since 4.3 @@ -3342,6 +3420,7 @@ QPainterPath QPainterPath::subtracted(const QPainterPath &p) const return clipper.clip(QPathClipper::BoolSub); } +#if QT_DEPRECATED_SINCE(5, 13) /*! \since 4.3 \obsolete @@ -3354,6 +3433,7 @@ QPainterPath QPainterPath::subtractedInverted(const QPainterPath &p) const { return p.subtracted(*this); } +#endif /*! \since 4.4 diff --git a/src/gui/painting/qpainterpath.h b/src/gui/painting/qpainterpath.h index db39c1c5a0..2785669260 100644 --- a/src/gui/painting/qpainterpath.h +++ b/src/gui/painting/qpainterpath.h @@ -88,16 +88,21 @@ public: inline bool operator!=(const Element &e) const { return !operator==(e); } }; - QPainterPath() Q_DECL_NOEXCEPT; + QPainterPath() noexcept; explicit QPainterPath(const QPointF &startPoint); QPainterPath(const QPainterPath &other); QPainterPath &operator=(const QPainterPath &other); #ifdef Q_COMPILER_RVALUE_REFS - inline QPainterPath &operator=(QPainterPath &&other) Q_DECL_NOEXCEPT + inline QPainterPath &operator=(QPainterPath &&other) noexcept { qSwap(d_ptr, other.d_ptr); return *this; } #endif ~QPainterPath(); - inline void swap(QPainterPath &other) Q_DECL_NOEXCEPT { d_ptr.swap(other.d_ptr); } + + inline void swap(QPainterPath &other) noexcept { d_ptr.swap(other.d_ptr); } + + void clear(); + void reserve(int size); + int capacity() const; void closeSubpath(); @@ -138,12 +143,18 @@ public: qreal xRadius, qreal yRadius, Qt::SizeMode mode = Qt::AbsoluteSize); +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED_X("Use addRoundedRect(..., Qt::RelativeSize) instead") void addRoundRect(const QRectF &rect, int xRnd, int yRnd); - inline void addRoundRect(qreal x, qreal y, qreal w, qreal h, - int xRnd, int yRnd); - inline void addRoundRect(const QRectF &rect, int roundness); - inline void addRoundRect(qreal x, qreal y, qreal w, qreal h, - int roundness); + QT_DEPRECATED_X("Use addRoundedRect(..., Qt::RelativeSize) instead") + void addRoundRect(qreal x, qreal y, qreal w, qreal h, + int xRnd, int yRnd); + QT_DEPRECATED_X("Use addRoundedRect(..., Qt::RelativeSize) instead") + void addRoundRect(const QRectF &rect, int roundness); + QT_DEPRECATED_X("Use addRoundedRect(..., Qt::RelativeSize) instead") + void addRoundRect(qreal x, qreal y, qreal w, qreal h, + int roundness); +#endif void connectPath(const QPainterPath &path); @@ -188,7 +199,10 @@ public: Q_REQUIRED_RESULT QPainterPath united(const QPainterPath &r) const; Q_REQUIRED_RESULT QPainterPath intersected(const QPainterPath &r) const; Q_REQUIRED_RESULT QPainterPath subtracted(const QPainterPath &r) const; +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED_X("Use r.subtracted() instead") Q_REQUIRED_RESULT QPainterPath subtractedInverted(const QPainterPath &r) const; +#endif Q_REQUIRED_RESULT QPainterPath simplified() const; @@ -333,30 +347,6 @@ inline void QPainterPath::addRoundedRect(qreal x, qreal y, qreal w, qreal h, addRoundedRect(QRectF(x, y, w, h), xRadius, yRadius, mode); } -inline void QPainterPath::addRoundRect(qreal x, qreal y, qreal w, qreal h, - int xRnd, int yRnd) -{ - addRoundRect(QRectF(x, y, w, h), xRnd, yRnd); -} - -inline void QPainterPath::addRoundRect(const QRectF &rect, - int roundness) -{ - int xRnd = roundness; - int yRnd = roundness; - if (rect.width() > rect.height()) - xRnd = int(roundness * rect.height()/rect.width()); - else - yRnd = int(roundness * rect.width()/rect.height()); - addRoundRect(rect, xRnd, yRnd); -} - -inline void QPainterPath::addRoundRect(qreal x, qreal y, qreal w, qreal h, - int roundness) -{ - addRoundRect(QRectF(x, y, w, h), roundness); -} - inline void QPainterPath::addText(qreal x, qreal y, const QFont &f, const QString &text) { addText(QPointF(x, y), f, text); diff --git a/src/gui/painting/qpainterpath_p.h b/src/gui/painting/qpainterpath_p.h index 92d9a4ea66..98056483bc 100644 --- a/src/gui/painting/qpainterpath_p.h +++ b/src/gui/painting/qpainterpath_p.h @@ -157,7 +157,7 @@ public: QVectorPath path; private: - Q_DISABLE_COPY(QVectorPathConverter) + Q_DISABLE_COPY_MOVE(QVectorPathConverter) }; class QPainterPathData : public QPainterPathPrivate @@ -168,7 +168,7 @@ public: fillRule(Qt::OddEvenFill), dirtyBounds(false), dirtyControlBounds(false), - pathConverter(0) + pathConverter(nullptr) { require_moveTo = false; convex = false; @@ -181,7 +181,7 @@ public: dirtyBounds(other.dirtyBounds), dirtyControlBounds(other.dirtyControlBounds), convex(other.convex), - pathConverter(0) + pathConverter(nullptr) { require_moveTo = false; elements = other.elements; @@ -194,6 +194,7 @@ public: inline bool isClosed() const; inline void close(); inline void maybeMoveTo(); + inline void clear(); const QVectorPath &vectorPath() { if (!pathConverter) @@ -290,6 +291,25 @@ inline void QPainterPathData::maybeMoveTo() } } +inline void QPainterPathData::clear() +{ + Q_ASSERT(ref.load() == 1); + + elements.clear(); + + cStart = 0; + + bounds = {}; + controlBounds = {}; + + require_moveTo = false; + dirtyBounds = false; + dirtyControlBounds = false; + convex = false; + + delete pathConverter; + pathConverter = nullptr; +} #define KAPPA qreal(0.5522847498) diff --git a/src/gui/painting/qpathclipper_p.h b/src/gui/painting/qpathclipper_p.h index 64e684e1ad..9444a87b71 100644 --- a/src/gui/painting/qpathclipper_p.h +++ b/src/gui/painting/qpathclipper_p.h @@ -82,11 +82,11 @@ public: bool intersect(); bool contains(); - static bool pathToRect(const QPainterPath &path, QRectF *rect = 0); + static bool pathToRect(const QPainterPath &path, QRectF *rect = nullptr); static QPainterPath intersect(const QPainterPath &path, const QRectF &rect); private: - Q_DISABLE_COPY(QPathClipper) + Q_DISABLE_COPY_MOVE(QPathClipper) enum ClipperMode { ClipMode, // do the full clip @@ -394,7 +394,7 @@ inline const QPathSegments::Intersection *QPathSegments::intersectionAt(int inde { const int intersection = m_segments.at(index).intersection; if (intersection < 0) - return 0; + return nullptr; else return &m_intersections.at(intersection); } @@ -428,12 +428,12 @@ inline int QWingedEdge::edgeCount() const inline QPathEdge *QWingedEdge::edge(int edge) { - return edge < 0 ? 0 : &m_edges.at(edge); + return edge < 0 ? nullptr : &m_edges.at(edge); } inline const QPathEdge *QWingedEdge::edge(int edge) const { - return edge < 0 ? 0 : &m_edges.at(edge); + return edge < 0 ? nullptr : &m_edges.at(edge); } inline int QWingedEdge::vertexCount() const @@ -449,12 +449,12 @@ inline int QWingedEdge::addVertex(const QPointF &p) inline QPathVertex *QWingedEdge::vertex(int vertex) { - return vertex < 0 ? 0 : &m_vertices.at(vertex); + return vertex < 0 ? nullptr : &m_vertices.at(vertex); } inline const QPathVertex *QWingedEdge::vertex(int vertex) const { - return vertex < 0 ? 0 : &m_vertices.at(vertex); + return vertex < 0 ? nullptr : &m_vertices.at(vertex); } inline QPathEdge::Traversal QWingedEdge::flip(QPathEdge::Traversal traversal) diff --git a/src/gui/painting/qpathsimplifier.cpp b/src/gui/painting/qpathsimplifier.cpp index 40585ec502..4251840bbc 100644 --- a/src/gui/painting/qpathsimplifier.cpp +++ b/src/gui/painting/qpathsimplifier.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** -** This file is part of the QtDeclarative module of the Qt Toolkit. +** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage diff --git a/src/gui/painting/qpathsimplifier_p.h b/src/gui/painting/qpathsimplifier_p.h index 6ef298f6bf..6c0062c592 100644 --- a/src/gui/painting/qpathsimplifier_p.h +++ b/src/gui/painting/qpathsimplifier_p.h @@ -3,7 +3,7 @@ ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** -** This file is part of the QtDeclarative module of the Qt Toolkit. +** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp index 4fd0d3c8fe..25d488961c 100644 --- a/src/gui/painting/qpdf.cpp +++ b/src/gui/painting/qpdf.cpp @@ -71,6 +71,11 @@ static const bool do_compress = true; // Can't use it though, as gs generates completely wrong images if this is true. static const bool interpolateImages = false; +static void initResources() +{ + Q_INIT_RESOURCE(qpdf); +} + QT_BEGIN_NAMESPACE inline QPaintEngine::PaintEngineFeatures qt_pdf_decide_features() @@ -851,7 +856,6 @@ void QPdfEngine::drawRects (const QRectF *rects, int rectCount) if (!d->hasPen && !d->hasBrush) return; - QBrush penBrush = d->pen.brush(); if (d->simplePen || !d->hasPen) { // draw strokes natively in this case for better output if(!d->simplePen && !d->stroker.matrix.isIdentity()) @@ -944,11 +948,23 @@ void QPdfEngine::drawPixmap (const QRectF &rectangle, const QPixmap &pixmap, con QPixmap pm = sourceRect != pixmap.rect() ? pixmap.copy(sourceRect) : pixmap; QImage image = pm.toImage(); bool bitmap = true; - const int object = d->addImage(image, &bitmap, pm.cacheKey()); + const bool lossless = painter()->testRenderHint(QPainter::LosslessImageRendering); + const int object = d->addImage(image, &bitmap, lossless, pm.cacheKey()); if (object < 0) return; - *d->currentPage << "q\n/GSa gs\n"; + *d->currentPage << "q\n"; + + if ((d->pdfVersion != QPdfEngine::Version_A1b) && (d->opacity != 1.0)) { + int stateObject = d->addConstantAlphaObject(qRound(255 * d->opacity), qRound(255 * d->opacity)); + if (stateObject) + *d->currentPage << "/GState" << stateObject << "gs\n"; + else + *d->currentPage << "/GSa gs\n"; + } else { + *d->currentPage << "/GSa gs\n"; + } + *d->currentPage << QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(), rectangle.x(), rectangle.y()) * (d->simplePen ? QTransform() : d->stroker.matrix)); @@ -972,11 +988,23 @@ void QPdfEngine::drawImage(const QRectF &rectangle, const QImage &image, const Q QRect sourceRect = sr.toRect(); QImage im = sourceRect != image.rect() ? image.copy(sourceRect) : image; bool bitmap = true; - const int object = d->addImage(im, &bitmap, im.cacheKey()); + const bool lossless = painter()->testRenderHint(QPainter::LosslessImageRendering); + const int object = d->addImage(im, &bitmap, lossless, im.cacheKey()); if (object < 0) return; - *d->currentPage << "q\n/GSa gs\n"; + *d->currentPage << "q\n"; + + if ((d->pdfVersion != QPdfEngine::Version_A1b) && (d->opacity != 1.0)) { + int stateObject = d->addConstantAlphaObject(qRound(255 * d->opacity), qRound(255 * d->opacity)); + if (stateObject) + *d->currentPage << "/GState" << stateObject << "gs\n"; + else + *d->currentPage << "/GSa gs\n"; + } else { + *d->currentPage << "/GSa gs\n"; + } + *d->currentPage << QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(), rectangle.x(), rectangle.y()) * (d->simplePen ? QTransform() : d->stroker.matrix)); @@ -1446,6 +1474,7 @@ QPdfEnginePrivate::QPdfEnginePrivate() grayscale(false), m_pageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(10, 10, 10, 10)) { + initResources(); resolution = 1200; currentObject = 1; currentPage = 0; @@ -1513,7 +1542,7 @@ bool QPdfEngine::end() Q_D(QPdfEngine); d->writeTail(); - d->stream->unsetDevice(); + d->stream->setDevice(nullptr); qDeleteAll(d->fonts); d->fonts.clear(); @@ -1541,7 +1570,14 @@ void QPdfEnginePrivate::writeHeader() { addXrefEntry(0,false); - xprintf("%%PDF-1.4\n"); + static const QHash<QPdfEngine::PdfVersion, const char *> mapping { + {QPdfEngine::Version_1_4, "1.4"}, + {QPdfEngine::Version_A1b, "1.4"}, + {QPdfEngine::Version_1_6, "1.6"} + }; + const char *verStr = mapping.value(pdfVersion, "1.4"); + + xprintf("%%PDF-%s\n", verStr); xprintf("%%\303\242\303\243\n"); writeInfo(); @@ -1635,19 +1671,19 @@ int QPdfEnginePrivate::writeXmpMetaData() 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 QString timeStr = + QString::asprintf("%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); + tzStr = QString::asprintf("-%02d:%02d", -hours, -mins); else if (offset > 0) - tzStr.sprintf("+%02d:%02d", hours , mins); + tzStr = QString::asprintf("+%02d:%02d", hours , mins); else tzStr = QLatin1String("Z"); @@ -1874,6 +1910,19 @@ void QPdfEnginePrivate::embedFont(QFontSubset *font) } } +qreal QPdfEnginePrivate::calcUserUnit() const +{ + // PDF standards < 1.6 support max 200x200in pages (no UserUnit) + if (pdfVersion < QPdfEngine::Version_1_6) + return 1.0; + + const int maxLen = qMax(currentPage->pageSize.width(), currentPage->pageSize.height()); + if (maxLen <= 14400) + return 1.0; // for pages up to 200x200in (14400x14400 units) use default scaling + + // for larger pages, rescale units so we can have up to 381x381km + return qMin(maxLen / 14400.0, 75000.0); +} void QPdfEnginePrivate::writeFonts() { @@ -1896,6 +1945,8 @@ void QPdfEnginePrivate::writePage() uint resources = requestObject(); uint annots = requestObject(); + qreal userUnit = calcUserUnit(); + addXrefEntry(pages.constLast()); xprintf("<<\n" "/Type /Page\n" @@ -1903,12 +1954,17 @@ void QPdfEnginePrivate::writePage() "/Contents %d 0 R\n" "/Resources %d 0 R\n" "/Annots %d 0 R\n" - "/MediaBox [0 0 %d %d]\n" - ">>\n" - "endobj\n", + "/MediaBox [0 0 %s %s]\n", pageRoot, pageStream, resources, annots, // make sure we use the pagesize from when we started the page, since the user may have changed it - currentPage->pageSize.width(), currentPage->pageSize.height()); + QByteArray::number(currentPage->pageSize.width() / userUnit, 'f').constData(), + QByteArray::number(currentPage->pageSize.height() / userUnit, 'f').constData()); + + if (pdfVersion >= QPdfEngine::Version_1_6) + xprintf("/UserUnit %s\n", QByteArray::number(userUnit, 'f').constData()); + + xprintf(">>\n" + "endobj\n"); addXrefEntry(resources); xprintf("<<\n" @@ -2578,6 +2634,8 @@ int QPdfEnginePrivate::addConstantAlphaObject(int brushAlpha, int penAlpha) int QPdfEnginePrivate::addBrushPattern(const QTransform &m, bool *specifyColor, int *gStateObject) { + Q_Q(QPdfEngine); + int paintType = 2; // Uncolored tiling int w = 8; int h = 8; @@ -2607,7 +2665,8 @@ int QPdfEnginePrivate::addBrushPattern(const QTransform &m, bool *specifyColor, return 0; QImage image = brush.textureImage(); bool bitmap = true; - imageObject = addImage(image, &bitmap, image.cacheKey()); + const bool lossless = q->painter()->testRenderHint(QPainter::LosslessImageRendering); + imageObject = addImage(image, &bitmap, lossless, image.cacheKey()); if (imageObject != -1) { QImage::Format f = image.format(); if (f != QImage::Format_MonoLSB && f != QImage::Format_Mono) { @@ -2669,7 +2728,7 @@ static inline bool is_monochrome(const QVector<QRgb> &colorTable) /*! * Adds an image to the pdf and return the pdf-object id. Returns -1 if adding the image failed. */ -int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, qint64 serial_no) +int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, bool lossless, qint64 serial_no) { if (img.isNull()) return -1; @@ -2730,7 +2789,7 @@ int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, qint64 serial_n bool hasAlpha = false; bool hasMask = false; - if (QImageWriter::supportedImageFormats().contains("jpeg") && !grayscale) { + if (QImageWriter::supportedImageFormats().contains("jpeg") && !grayscale && !lossless) { QBuffer buffer(&imageData); QImageWriter writer(&buffer, "jpeg"); writer.setQuality(94); @@ -2977,8 +3036,9 @@ void QPdfEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti) QTransform QPdfEnginePrivate::pageMatrix() const { - qreal scale = 72./resolution; - QTransform tmp(scale, 0.0, 0.0, -scale, 0.0, m_pageLayout.fullRectPoints().height()); + qreal userUnit = calcUserUnit(); + qreal scale = 72. / userUnit / resolution; + QTransform tmp(scale, 0.0, 0.0, -scale, 0.0, m_pageLayout.fullRectPoints().height() / userUnit); if (m_pageLayout.mode() != QPageLayout::FullPageMode) { QRect r = m_pageLayout.paintRectPixels(resolution); tmp.translate(r.left(), r.top()); diff --git a/src/gui/painting/qpdf_p.h b/src/gui/painting/qpdf_p.h index 5a909f2ede..e337c61f64 100644 --- a/src/gui/painting/qpdf_p.h +++ b/src/gui/painting/qpdf_p.h @@ -171,7 +171,8 @@ public: enum PdfVersion { Version_1_4, - Version_A1b + Version_A1b, + Version_1_6 }; QPdfEngine(); @@ -241,7 +242,7 @@ public: void writeHeader(); void writeTail(); - int addImage(const QImage &image, bool *bitmap, qint64 serial_no); + int addImage(const QImage &image, bool *bitmap, bool lossless, qint64 serial_no); int addConstantAlphaObject(int brushAlpha, int penAlpha = 255); int addBrushPattern(const QTransform &matrix, bool *specifyColor, int *gStateObject); @@ -300,6 +301,7 @@ private: void writePageRoot(); void writeFonts(); void embedFont(QFontSubset *font); + qreal calcUserUnit() const; QVector<int> xrefPositions; QDataStream* stream; diff --git a/src/gui/painting/qpdfwriter.cpp b/src/gui/painting/qpdfwriter.cpp index 5f0fad2a4e..7f18ce42be 100644 --- a/src/gui/painting/qpdfwriter.cpp +++ b/src/gui/painting/qpdfwriter.cpp @@ -170,11 +170,17 @@ void QPdfWriter::setPdfVersion(PdfVersion version) { Q_D(QPdfWriter); + static const QHash<QPdfWriter::PdfVersion, QPdfEngine::PdfVersion> engineMapping { + {QPdfWriter::PdfVersion_1_4, QPdfEngine::Version_1_4}, + {QPdfWriter::PdfVersion_A1b, QPdfEngine::Version_A1b}, + {QPdfWriter::PdfVersion_1_6, QPdfEngine::Version_1_6} + }; + if (d->pdfVersion == version) return; d->pdfVersion = version; - d->engine->setPdfVersion(d->pdfVersion == QPdfWriter::PdfVersion_1_4 ? QPdfEngine::Version_1_4 : QPdfEngine::Version_A1b); + d->engine->setPdfVersion(engineMapping.value(version, QPdfEngine::Version_1_4)); } /*! @@ -373,6 +379,9 @@ int QPdfWriter::resolution() const */ #endif +#if QT_DEPRECATED_SINCE(5, 14) +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED /*! \reimp @@ -398,6 +407,8 @@ void QPdfWriter::setPageSizeMM(const QSizeF &size) { setPageSize(QPageSize(size, QPageSize::Millimeter)); } +QT_WARNING_POP +#endif /*! \internal @@ -421,6 +432,9 @@ bool QPdfWriter::newPage() } +#if QT_DEPRECATED_SINCE(5, 14) +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED /*! \reimp @@ -432,6 +446,8 @@ void QPdfWriter::setMargins(const Margins &m) { setPageMargins(QMarginsF(m.left, m.top, m.right, m.bottom), QPageLayout::Millimeter); } +QT_WARNING_POP +#endif QT_END_NAMESPACE diff --git a/src/gui/painting/qpdfwriter.h b/src/gui/painting/qpdfwriter.h index b260805b2b..668081e008 100644 --- a/src/gui/painting/qpdfwriter.h +++ b/src/gui/painting/qpdfwriter.h @@ -86,10 +86,14 @@ public: using QPagedPaintDevice::setPageSize; #endif +#if QT_DEPRECATED_SINCE(5, 14) + QT_DEPRECATED_X("Use setPageSize(QPageSize(id)) instead") void setPageSize(PageSize size) override; + QT_DEPRECATED_X("Use setPageSize(QPageSize(size, QPageSize::Millimeter)) instead") void setPageSizeMM(const QSizeF &size) override; - + QT_DEPRECATED_X("Use setPageMargins(QMarginsF(l, t, r, b), QPageLayout::Millimeter) instead") void setMargins(const Margins &m) override; +#endif protected: QPaintEngine *paintEngine() const override; diff --git a/src/gui/painting/qpen.cpp b/src/gui/painting/qpen.cpp index 778c770b82..58a1716037 100644 --- a/src/gui/painting/qpen.cpp +++ b/src/gui/painting/qpen.cpp @@ -322,7 +322,7 @@ QPen::QPen(const QBrush &brush, qreal width, Qt::PenStyle s, Qt::PenCapStyle c, Constructs a pen that is a copy of the given \a pen. */ -QPen::QPen(const QPen &p) Q_DECL_NOTHROW +QPen::QPen(const QPen &p) noexcept { d = p.d; if (d) @@ -381,7 +381,7 @@ void QPen::detach() this pen. */ -QPen &QPen::operator=(const QPen &p) Q_DECL_NOTHROW +QPen &QPen::operator=(const QPen &p) noexcept { QPen(p).swap(*this); return *this; diff --git a/src/gui/painting/qpen.h b/src/gui/painting/qpen.h index 03abfb3d7d..884ec8cdfa 100644 --- a/src/gui/painting/qpen.h +++ b/src/gui/painting/qpen.h @@ -65,18 +65,18 @@ public: QPen(const QColor &color); QPen(const QBrush &brush, qreal width, Qt::PenStyle s = Qt::SolidLine, Qt::PenCapStyle c = Qt::SquareCap, Qt::PenJoinStyle j = Qt::BevelJoin); - QPen(const QPen &pen) Q_DECL_NOTHROW; + QPen(const QPen &pen) noexcept; ~QPen(); - QPen &operator=(const QPen &pen) Q_DECL_NOTHROW; + QPen &operator=(const QPen &pen) noexcept; #ifdef Q_COMPILER_RVALUE_REFS - QPen(QPen &&other) Q_DECL_NOTHROW + QPen(QPen &&other) noexcept : d(other.d) { other.d = nullptr; } - QPen &operator=(QPen &&other) Q_DECL_NOTHROW + QPen &operator=(QPen &&other) noexcept { qSwap(d, other.d); return *this; } #endif - void swap(QPen &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QPen &other) noexcept { qSwap(d, other.d); } Qt::PenStyle style() const; void setStyle(Qt::PenStyle); diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp index 373722cdb4..c71d82546a 100644 --- a/src/gui/painting/qplatformbackingstore.cpp +++ b/src/gui/painting/qplatformbackingstore.cpp @@ -71,7 +71,7 @@ #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 #endif -#ifndef GL_FRAMEBUFFER_SRB +#ifndef GL_FRAMEBUFFER_SRGB #define GL_FRAMEBUFFER_SRGB 0x8DB9 #endif #ifndef GL_FRAMEBUFFER_SRGB_CAPABLE @@ -411,6 +411,7 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i } else if (!region.isEmpty()){ funcs->glBindTexture(GL_TEXTURE_2D, d_ptr->textureId); QPlatformGraphicsBufferHelper::lockAndBindToTexture(graphicsBuffer, &d_ptr->needsSwizzle, &d_ptr->premultiplied); + graphicsBuffer->unlock(); } if (graphicsBuffer->origin() == QPlatformGraphicsBuffer::OriginBottomLeft) @@ -445,6 +446,11 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i d_ptr->blitter->setRedBlueSwizzle(false); } + // There is no way to tell if the OpenGL-rendered content is premultiplied or not. + // For compatibility, assume that it is not, and use normal alpha blend always. + if (d_ptr->premultiplied) + funcs->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); + // Textures for renderToTexture widgets that have WA_AlwaysStackOnTop set. for (int i = 0; i < textures->count(); ++i) { if (textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) diff --git a/src/gui/painting/qplatformbackingstore.h b/src/gui/painting/qplatformbackingstore.h index de5ba964dc..414d2bf0de 100644 --- a/src/gui/painting/qplatformbackingstore.h +++ b/src/gui/painting/qplatformbackingstore.h @@ -85,7 +85,7 @@ public: }; Q_DECLARE_FLAGS(Flags, Flag) - explicit QPlatformTextureList(QObject *parent = 0); + explicit QPlatformTextureList(QObject *parent = nullptr); ~QPlatformTextureList(); int count() const; @@ -99,7 +99,7 @@ public: bool isLocked() const; void appendTexture(void *source, GLuint textureId, const QRect &geometry, - const QRect &clipRect = QRect(), Flags flags = 0); + const QRect &clipRect = QRect(), Flags flags = nullptr); void clear(); Q_SIGNALS: diff --git a/src/gui/painting/qpolygon.h b/src/gui/painting/qpolygon.h index 8e74a499fd..118861c0f2 100644 --- a/src/gui/painting/qpolygon.h +++ b/src/gui/painting/qpolygon.h @@ -61,17 +61,17 @@ public: inline explicit QPolygon(int size); inline /*implicit*/ QPolygon(const QVector<QPoint> &v) : QVector<QPoint>(v) {} #ifdef Q_COMPILER_RVALUE_REFS - /*implicit*/ QPolygon(QVector<QPoint> &&v) Q_DECL_NOTHROW : QVector<QPoint>(std::move(v)) {} + /*implicit*/ QPolygon(QVector<QPoint> &&v) noexcept : QVector<QPoint>(std::move(v)) {} #endif QPolygon(const QRect &r, bool closed=false); QPolygon(int nPoints, const int *points); QPolygon(const QPolygon &other) : QVector<QPoint>(other) {} #ifdef Q_COMPILER_RVALUE_REFS - QPolygon(QPolygon &&other) Q_DECL_NOTHROW : QVector<QPoint>(std::move(other)) {} - QPolygon &operator=(QPolygon &&other) Q_DECL_NOTHROW { swap(other); return *this; } + QPolygon(QPolygon &&other) noexcept : QVector<QPoint>(std::move(other)) {} + QPolygon &operator=(QPolygon &&other) noexcept { swap(other); return *this; } #endif QPolygon &operator=(const QPolygon &other) { QVector<QPoint>::operator=(other); return *this; } - void swap(QPolygon &other) Q_DECL_NOTHROW { QVector<QPoint>::swap(other); } // prevent QVector<QPoint><->QPolygon swaps + void swap(QPolygon &other) noexcept { QVector<QPoint>::swap(other); } // prevent QVector<QPoint><->QPolygon swaps operator QVariant() const; @@ -146,14 +146,14 @@ public: inline explicit QPolygonF(int size); inline /*implicit*/ QPolygonF(const QVector<QPointF> &v) : QVector<QPointF>(v) {} #ifdef Q_COMPILER_RVALUE_REFS - /* implicit */ QPolygonF(QVector<QPointF> &&v) Q_DECL_NOTHROW : QVector<QPointF>(std::move(v)) {} + /* implicit */ QPolygonF(QVector<QPointF> &&v) noexcept : QVector<QPointF>(std::move(v)) {} #endif QPolygonF(const QRectF &r); /*implicit*/ QPolygonF(const QPolygon &a); inline QPolygonF(const QPolygonF &a) : QVector<QPointF>(a) {} #ifdef Q_COMPILER_RVALUE_REFS - QPolygonF(QPolygonF &&other) Q_DECL_NOTHROW : QVector<QPointF>(std::move(other)) {} - QPolygonF &operator=(QPolygonF &&other) Q_DECL_NOTHROW { swap(other); return *this; } + QPolygonF(QPolygonF &&other) noexcept : QVector<QPointF>(std::move(other)) {} + QPolygonF &operator=(QPolygonF &&other) noexcept { swap(other); return *this; } #endif QPolygonF &operator=(const QPolygonF &other) { QVector<QPointF>::operator=(other); return *this; } inline void swap(QPolygonF &other) { QVector<QPointF>::swap(other); } // prevent QVector<QPointF><->QPolygonF swaps diff --git a/src/gui/painting/qrasterizer.cpp b/src/gui/painting/qrasterizer.cpp index 52501880e4..b4014272f4 100644 --- a/src/gui/painting/qrasterizer.cpp +++ b/src/gui/painting/qrasterizer.cpp @@ -755,11 +755,9 @@ static inline int qSafeFloatToQ16Dot16(qreal x) void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, bool squareCap) { - if (a == b || width == 0 || d->clipRect.isEmpty()) + if (a == b || !(width > 0.0) || d->clipRect.isEmpty()) return; - Q_ASSERT(width > 0.0); - QPointF pa = a; QPointF pb = b; diff --git a/src/gui/painting/qrbtree_p.h b/src/gui/painting/qrbtree_p.h index d3ee23a91c..42e88889a1 100644 --- a/src/gui/painting/qrbtree_p.h +++ b/src/gui/painting/qrbtree_p.h @@ -60,7 +60,7 @@ struct QRBTree { struct Node { - inline Node() : parent(0), left(0), right(0), red(true) { } + inline Node() : parent(nullptr), left(nullptr), right(nullptr), red(true) { } inline ~Node() {if (left) delete left; if (right) delete right;} T data; Node *parent; @@ -69,7 +69,7 @@ struct QRBTree bool red; }; - inline QRBTree() : root(0), freeList(0) { } + inline QRBTree() : root(nullptr), freeList(nullptr) { } inline ~QRBTree(); inline void clear(); @@ -120,7 +120,7 @@ inline QRBTree<T>::~QRBTree() while (freeList) { // Avoid recursively calling the destructor, as this list may become large. Node *next = freeList->right; - freeList->right = 0; + freeList->right = nullptr; delete freeList; freeList = next; } @@ -131,7 +131,7 @@ inline void QRBTree<T>::clear() { if (root) delete root; - root = 0; + root = nullptr; } template <class T> @@ -359,7 +359,7 @@ void QRBTree<T>::detach(Node *node) // call this before removing a node. ref = child; if (child) child->parent = node->parent; - node->left = node->right = node->parent = 0; + node->left = node->right = node->parent = nullptr; } // 'node' must be black. rebalance will reduce the depth of black nodes by one in the sibling tree. @@ -513,7 +513,7 @@ inline void QRBTree<T>::deleteNode(Node *&node) detach(node); node->right = freeList; freeList = node; - node = 0; + node = nullptr; } template <class T> @@ -522,7 +522,7 @@ inline typename QRBTree<T>::Node *QRBTree<T>::newNode() if (freeList) { Node *node = freeList; freeList = freeList->right; - node->parent = node->left = node->right = 0; + node->parent = node->left = node->right = nullptr; node->red = true; return node; } diff --git a/src/gui/painting/qregion.cpp b/src/gui/painting/qregion.cpp index 77718ce747..51eed962f0 100644 --- a/src/gui/painting/qregion.cpp +++ b/src/gui/painting/qregion.cpp @@ -1287,10 +1287,10 @@ struct QRegionPrivate { } } - const QRect *begin() const Q_DECL_NOTHROW + const QRect *begin() const noexcept { return numRects == 1 ? &extents : rects.data(); } // avoid vectorize() - const QRect *end() const Q_DECL_NOTHROW + const QRect *end() const noexcept { return begin() + numRects; } inline void append(const QRect *r); @@ -4321,7 +4321,7 @@ QRegion QRegion::xored(const QRegion &r) const } } -QRect QRegion::boundingRect() const Q_DECL_NOTHROW +QRect QRegion::boundingRect() const noexcept { if (isEmpty()) return QRect(); @@ -4379,12 +4379,12 @@ QVector<QRect> QRegion::rects() const } #endif -QRegion::const_iterator QRegion::begin() const Q_DECL_NOTHROW +QRegion::const_iterator QRegion::begin() const noexcept { return d->qt_rgn ? d->qt_rgn->begin() : nullptr; } -QRegion::const_iterator QRegion::end() const Q_DECL_NOTHROW +QRegion::const_iterator QRegion::end() const noexcept { return d->qt_rgn ? d->qt_rgn->end() : nullptr; } @@ -4421,7 +4421,7 @@ void QRegion::setRects(const QRect *rects, int num) } } -int QRegion::rectCount() const Q_DECL_NOTHROW +int QRegion::rectCount() const noexcept { return (d->qt_rgn ? d->qt_rgn->numRects : 0); } diff --git a/src/gui/painting/qregion.h b/src/gui/painting/qregion.h index 9fe6ed5675..9b6b25d743 100644 --- a/src/gui/painting/qregion.h +++ b/src/gui/painting/qregion.h @@ -69,30 +69,30 @@ public: QRegion(const QRect &r, RegionType t = Rectangle); QRegion(const QPolygon &pa, Qt::FillRule fillRule = Qt::OddEvenFill); QRegion(const QRegion ®ion); - QRegion(QRegion &&other) Q_DECL_NOTHROW + QRegion(QRegion &&other) noexcept : d(other.d) { other.d = const_cast<QRegionData*>(&shared_empty); } QRegion(const QBitmap &bitmap); ~QRegion(); QRegion &operator=(const QRegion &); #ifdef Q_COMPILER_RVALUE_REFS - inline QRegion &operator=(QRegion &&other) Q_DECL_NOEXCEPT + inline QRegion &operator=(QRegion &&other) noexcept { qSwap(d, other.d); return *this; } #endif - inline void swap(QRegion &other) Q_DECL_NOEXCEPT { qSwap(d, other.d); } + inline void swap(QRegion &other) noexcept { qSwap(d, other.d); } bool isEmpty() const; bool isNull() const; typedef const QRect *const_iterator; typedef std::reverse_iterator<const_iterator> const_reverse_iterator; - const_iterator begin() const Q_DECL_NOTHROW; - const_iterator cbegin() const Q_DECL_NOTHROW { return begin(); } - const_iterator end() const Q_DECL_NOTHROW; - const_iterator cend() const Q_DECL_NOTHROW { return end(); } - const_reverse_iterator rbegin() const Q_DECL_NOTHROW { return const_reverse_iterator(end()); } - const_reverse_iterator crbegin() const Q_DECL_NOTHROW { return rbegin(); } - const_reverse_iterator rend() const Q_DECL_NOTHROW { return const_reverse_iterator(begin()); } - const_reverse_iterator crend() const Q_DECL_NOTHROW { return rend(); } + const_iterator begin() const noexcept; + const_iterator cbegin() const noexcept { return begin(); } + const_iterator end() const noexcept; + const_iterator cend() const noexcept { return end(); } + const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } + const_reverse_iterator crbegin() const noexcept { return rbegin(); } + const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } + const_reverse_iterator crend() const noexcept { return rend(); } bool contains(const QPoint &p) const; bool contains(const QRect &r) const; @@ -121,13 +121,13 @@ public: bool intersects(const QRegion &r) const; bool intersects(const QRect &r) const; - QRect boundingRect() const Q_DECL_NOTHROW; + QRect boundingRect() const noexcept; #if QT_DEPRECATED_SINCE(5, 11) QT_DEPRECATED_X("Use begin()/end() instead") QVector<QRect> rects() const; #endif void setRects(const QRect *rect, int num); - int rectCount() const Q_DECL_NOTHROW; + int rectCount() const noexcept; #ifdef Q_COMPILER_MANGLES_RETURN_TYPE // ### Qt 6: remove these, they're kept for MSVC compat const QRegion operator|(const QRegion &r) const; diff --git a/src/gui/painting/qrgba64.h b/src/gui/painting/qrgba64.h index 0e5344cacb..0e0b567890 100644 --- a/src/gui/painting/qrgba64.h +++ b/src/gui/painting/qrgba64.h @@ -118,7 +118,27 @@ public: Q_DECL_CONSTEXPR quint8 alpha8() const { return div_257(alpha()); } Q_DECL_CONSTEXPR uint toArgb32() const { +#if defined(__cpp_constexpr) && __cpp_constexpr-0 >= 201304 + quint64 br = rgba & Q_UINT64_C(0xffff0000ffff); + quint64 ag = (rgba >> 16) & Q_UINT64_C(0xffff0000ffff); + br += Q_UINT64_C(0x8000000080); + ag += Q_UINT64_C(0x8000000080); + br = (br - ((br >> 8) & Q_UINT64_C(0xffff0000ffff))) >> 8; + ag = (ag - ((ag >> 8) & Q_UINT64_C(0xffff0000ffff))); +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + return ((br << 24) & 0xff000000) + | ((ag >> 24) & 0xff0000) + | ((br >> 24) & 0xff00) + | ((ag >> 8) & 0xff); +#else + return ((ag >> 16) & 0xff000000) + | ((br << 16) & 0xff0000) + | (ag & 0xff00) + | ((br >> 32) & 0xff); +#endif +#else return uint((alpha8() << 24) | (red8() << 16) | (green8() << 8) | blue8()); +#endif } Q_DECL_CONSTEXPR ushort toRgb16() const { @@ -131,11 +151,20 @@ public: return *this; if (isTransparent()) return QRgba64::fromRgba64(0); - const quint32 a = alpha(); - const quint16 r = div_65535(red() * a); - const quint16 g = div_65535(green() * a); - const quint16 b = div_65535(blue() * a); - return fromRgba64(r, g, b, quint16(a)); + const quint64 a = alpha(); + quint64 br = (rgba & Q_UINT64_C(0xffff0000ffff)) * a; + quint64 ag = ((rgba >> 16) & Q_UINT64_C(0xffff0000ffff)) * a; + br = (br + ((br >> 16) & Q_UINT64_C(0xffff0000ffff)) + Q_UINT64_C(0x800000008000)); + ag = (ag + ((ag >> 16) & Q_UINT64_C(0xffff0000ffff)) + Q_UINT64_C(0x800000008000)); +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + ag = ag & Q_UINT64_C(0xffff0000ffff0000); + br = (br >> 16) & Q_UINT64_C(0xffff00000000); + return fromRgba64(a | br | ag); +#else + br = (br >> 16) & Q_UINT64_C(0xffff0000ffff); + ag = ag & Q_UINT64_C(0xffff0000); + return fromRgba64((a << 48) | br | ag); +#endif } Q_DECL_RELAXED_CONSTEXPR QRgba64 unpremultiplied() const @@ -163,7 +192,6 @@ private: static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE quint8 div_257_floor(uint x) { return quint8((x - (x >> 8)) >> 8); } static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE quint8 div_257(quint16 x) { return div_257_floor(x + 128U); } - static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE quint16 div_65535(uint x) { return quint16((x + (x>>16) + 0x8000U) >> 16); } Q_DECL_RELAXED_CONSTEXPR Q_ALWAYS_INLINE QRgba64 unpremultiplied_32bit() const { if (isOpaque() || isTransparent()) diff --git a/src/gui/painting/qrgba64.qdoc b/src/gui/painting/qrgba64.qdoc index 26c78a5dcd..3ee8a355e6 100644 --- a/src/gui/painting/qrgba64.qdoc +++ b/src/gui/painting/qrgba64.qdoc @@ -35,7 +35,7 @@ QRgba64 is a 64-bit data-structure containing four 16-bit color channels: Red, green, blue and alpha. - QRgba64 can be used a replacement for QRgb when higher precision is needed. In particular a + QRgba64 can be used as a replacement for QRgb when higher precision is needed. In particular a premultiplied QRgba64 can operate on unpremultiplied QRgb without loss of precision except for alpha 0. diff --git a/src/gui/painting/qrgba64_p.h b/src/gui/painting/qrgba64_p.h index b7e4d4d905..ca879de27c 100644 --- a/src/gui/painting/qrgba64_p.h +++ b/src/gui/painting/qrgba64_p.h @@ -266,10 +266,10 @@ inline QRgba64 rgbBlend(QRgba64 d, QRgba64 s, uint rgbAlpha) const int mr = qRed(rgbAlpha); const int mg = qGreen(rgbAlpha); const int mb = qBlue(rgbAlpha); - blend.setRed (qt_div_255(s.red() * mr + d.red() * (255 - mr))); - blend.setGreen(qt_div_255(s.green() * mg + d.green() * (255 - mg))); - blend.setBlue (qt_div_255(s.blue() * mb + d.blue() * (255 - mb))); - blend.setAlpha(s.alpha()); + blend = qRgba64(qt_div_255(s.red() * mr + d.red() * (255 - mr)), + qt_div_255(s.green() * mg + d.green() * (255 - mg)), + qt_div_255(s.blue() * mb + d.blue() * (255 - mb)), + s.alpha()); #endif return blend; } diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp index 4776545be6..f8f8d72d14 100644 --- a/src/gui/painting/qstroker.cpp +++ b/src/gui/painting/qstroker.cpp @@ -173,15 +173,12 @@ template <class Iterator> bool qt_stroke_side(Iterator *it, QStroker *stroker, bool capFirst, QLineF *startTangent); /******************************************************************************* - * QLineF::angle gives us the smalles angle between two lines. Here we - * want to identify the line's angle direction on the unit circle. + * QLineF::angleTo gives us the angle between two lines with respecting the direction. + * Here we want to identify the line's angle direction on the unit circle. */ static inline qreal adapted_angle_on_x(const QLineF &line) { - qreal angle = line.angle(QLineF(0, 0, 1, 0)); - if (line.dy() > 0) - angle = 360 - angle; - return angle; + return QLineF(0, 0, 1, 0).angleTo(line); } QStrokerOps::QStrokerOps() @@ -369,7 +366,8 @@ void QStrokerOps::strokeEllipse(const QRectF &rect, void *data, const QTransform QStroker::QStroker() : m_capStyle(SquareJoin), m_joinStyle(FlatJoin), m_back1X(0), m_back1Y(0), - m_back2X(0), m_back2Y(0) + m_back2X(0), m_back2Y(0), + m_forceOpen(false) { m_strokeWidth = qt_real_to_fixed(1); m_miterLimit = qt_real_to_fixed(2); @@ -455,12 +453,12 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine return; } #endif + QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y), + qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y)); + QPointF isect; + QLineF::IntersectType type = prevLine.intersect(nextLine, &isect); if (join == FlatJoin) { - QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y), - qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y)); - QPointF isect; - QLineF::IntersectType type = prevLine.intersect(nextLine, &isect); QLineF shortCut(prevLine.p2(), nextLine.p1()); qreal angle = shortCut.angleTo(prevLine); if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) { @@ -472,12 +470,6 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine qt_real_to_fixed(nextLine.y1())); } else { - QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y), - qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y)); - - QPointF isect; - QLineF::IntersectType type = prevLine.intersect(nextLine, &isect); - if (join == MiterJoin) { qreal appliedMiterLimit = qt_fixed_to_real(m_strokeWidth * m_miterLimit); @@ -512,7 +504,11 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine qfixed offset = m_strokeWidth / 2; QLineF l1(prevLine); - l1.translate(l1.dx(), l1.dy()); + qreal dp = QPointF::dotProduct(QPointF(prevLine.dx(), prevLine.dy()), QPointF(nextLine.dx(), nextLine.dy())); + if (dp > 0) // same direction, means that prevLine is from a bezier that has been "reversed" by shifting + l1 = QLineF(prevLine.p2(), prevLine.p1()); + else + l1.translate(l1.dx(), l1.dy()); l1.setLength(qt_fixed_to_real(offset)); QLineF l2(nextLine.p2(), nextLine.p1()); l2.translate(l2.dx(), l2.dy()); @@ -570,7 +566,11 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine // first control line QLineF l1 = prevLine; - l1.translate(l1.dx(), l1.dy()); + qreal dp = QPointF::dotProduct(QPointF(prevLine.dx(), prevLine.dy()), QPointF(nextLine.dx(), nextLine.dy())); + if (dp > 0) // same direction, means that prevLine is from a bezier that has been "reversed" by shifting + l1 = QLineF(prevLine.p2(), prevLine.p1()); + else + l1.translate(l1.dx(), l1.dy()); l1.setLength(QT_PATH_KAPPA * offset); // second control line, find through normal between prevLine and focal. @@ -705,7 +705,6 @@ template <class Iterator> bool qt_stroke_side(Iterator *it, QPointF(qt_fixed_to_real(e.x), qt_fixed_to_real(e.y)), QPointF(qt_fixed_to_real(cp2.x), qt_fixed_to_real(cp2.y)), QPointF(qt_fixed_to_real(ep.x), qt_fixed_to_real(ep.y))); - int count = bezier.shifted(offsetCurves, MAX_OFFSET, offset, @@ -748,7 +747,7 @@ template <class Iterator> bool qt_stroke_side(Iterator *it, } } - if (start == prev) { + if (start == prev && !stroker->forceOpen()) { // closed subpath, join first and last point #ifdef QPP_STROKE_DEBUG qDebug("\n ---> (side) closed subpath"); diff --git a/src/gui/painting/qstroker_p.h b/src/gui/painting/qstroker_p.h index 1a7c184e1a..722a0904f3 100644 --- a/src/gui/painting/qstroker_p.h +++ b/src/gui/painting/qstroker_p.h @@ -222,6 +222,9 @@ public: void setMiterLimit(qfixed length) { m_miterLimit = length; } qfixed miterLimit() const { return m_miterLimit; } + void setForceOpen(bool state) { m_forceOpen = state; } + bool forceOpen() { return m_forceOpen; } + void joinPoints(qfixed x, qfixed y, const QLineF &nextLine, LineJoinMode join); inline void emitMoveTo(qfixed x, qfixed y); inline void emitLineTo(qfixed x, qfixed y); @@ -247,6 +250,8 @@ protected: qfixed m_back2X; qfixed m_back2Y; + + bool m_forceOpen; }; class Q_GUI_EXPORT QDashStroker : public QStrokerOps diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index 2a7e0eaa0c..c21f2cdda4 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -73,7 +73,7 @@ int QTextureGlyphCache::calculateSubPixelPositionCount(glyph_t glyph) const if (path.isEmpty()) break; - images[numImages++] = qMove(img); + images[numImages++] = std::move(img); } else { bool found = false; for (int j = 0; j < numImages; ++j) { @@ -83,7 +83,7 @@ int QTextureGlyphCache::calculateSubPixelPositionCount(glyph_t glyph) const } } if (!found) - images[numImages++] = qMove(img); + images[numImages++] = std::move(img); } } @@ -335,7 +335,7 @@ void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g, QFixed subP // TODO optimize this mask = mask.alphaChannel(); mask.invertPixels(); - mask = mask.convertToFormat(QImage::Format_Mono); + mask = mask.convertToFormat(QImage::Format_Mono, Qt::ThresholdDither); } int mw = qMin(mask.width(), c.w); diff --git a/src/gui/painting/qtextureglyphcache_p.h b/src/gui/painting/qtextureglyphcache_p.h index 3da28872b1..1e83ab46d1 100644 --- a/src/gui/painting/qtextureglyphcache_p.h +++ b/src/gui/painting/qtextureglyphcache_p.h @@ -75,7 +75,7 @@ class Q_GUI_EXPORT QTextureGlyphCache : public QFontEngineGlyphCache { public: QTextureGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix) - : QFontEngineGlyphCache(format, matrix), m_current_fontengine(0), + : QFontEngineGlyphCache(format, matrix), m_current_fontengine(nullptr), m_w(0), m_h(0), m_cx(0), m_cy(0), m_currentRowHeight(0) { } diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp index 040d33fc2a..6110a548fd 100644 --- a/src/gui/painting/qtransform.cpp +++ b/src/gui/painting/qtransform.cpp @@ -795,7 +795,7 @@ bool QTransform::operator==(const QTransform &o) const Returns the hash value for \a key, using \a seed to seed the calculation. */ -uint qHash(const QTransform &key, uint seed) Q_DECL_NOTHROW +uint qHash(const QTransform &key, uint seed) noexcept { QtPrivate::QHashCombine hash; seed = hash(seed, key.m11()); @@ -1021,7 +1021,7 @@ QTransform QTransform::operator*(const QTransform &m) const /*! Assigns the given \a matrix's values to this matrix. */ -QTransform & QTransform::operator=(const QTransform &matrix) Q_DECL_NOTHROW +QTransform & QTransform::operator=(const QTransform &matrix) noexcept { affine._m11 = matrix.affine._m11; affine._m12 = matrix.affine._m12; @@ -1517,8 +1517,23 @@ QRegion QTransform::map(const QRegion &r) const return copy; } - if (t == TxScale && r.rectCount() == 1) - return QRegion(mapRect(r.boundingRect())); + if (t == TxScale) { + QRegion res; + if (m11() < 0 || m22() < 0) { + for (const QRect &rect : r) + res += mapRect(rect); + } else { + QVarLengthArray<QRect, 32> rects; + rects.reserve(r.rectCount()); + for (const QRect &rect : r) { + QRect nr = mapRect(rect); + if (!nr.isEmpty()) + rects.append(nr); + } + res.setRects(rects.constData(), rects.count()); + } + return res; + } QPainterPath p = map(qt_regionToPath(r)); return p.toFillPolygon(QTransform()).toPolygon(); @@ -2144,13 +2159,14 @@ QTransform::operator QVariant() const \sa inverted() */ +#if QT_DEPRECATED_SINCE(5, 13) /*! \fn qreal QTransform::det() const \obsolete Returns the matrix's determinant. Use determinant() instead. */ - +#endif /*! \fn qreal QTransform::m11() const diff --git a/src/gui/painting/qtransform.h b/src/gui/painting/qtransform.h index 63c4a241c1..18c53f4a6f 100644 --- a/src/gui/painting/qtransform.h +++ b/src/gui/painting/qtransform.h @@ -77,13 +77,13 @@ public: #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove; the compiler-generated ones are fine! - QTransform &operator=(QTransform &&other) Q_DECL_NOTHROW // = default + QTransform &operator=(QTransform &&other) noexcept // = default { memcpy(static_cast<void *>(this), static_cast<void *>(&other), sizeof(QTransform)); return *this; } - QTransform &operator=(const QTransform &) Q_DECL_NOTHROW; // = default - QTransform(QTransform &&other) Q_DECL_NOTHROW // = default + QTransform &operator=(const QTransform &) noexcept; // = default + QTransform(QTransform &&other) noexcept // = default : affine(Qt::Uninitialized) { memcpy(static_cast<void *>(this), static_cast<void *>(&other), sizeof(QTransform)); } - QTransform(const QTransform &other) Q_DECL_NOTHROW // = default + QTransform(const QTransform &other) noexcept // = default : affine(Qt::Uninitialized) { memcpy(static_cast<void *>(this), static_cast<const void *>(&other), sizeof(QTransform)); } #endif @@ -98,7 +98,10 @@ public: TransformationType type() const; inline qreal determinant() const; +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED_X("Use determinant() instead") qreal det() const; +#endif qreal m11() const; qreal m12() const; @@ -199,7 +202,7 @@ private: }; Q_DECLARE_TYPEINFO(QTransform, Q_MOVABLE_TYPE); -Q_GUI_EXPORT Q_DECL_CONST_FUNCTION uint qHash(const QTransform &key, uint seed = 0) Q_DECL_NOTHROW; +Q_GUI_EXPORT Q_DECL_CONST_FUNCTION uint qHash(const QTransform &key, uint seed = 0) noexcept; /******* inlines *****/ inline QTransform::TransformationType QTransform::inline_type() const @@ -242,10 +245,12 @@ inline qreal QTransform::determinant() const return affine._m11*(m_33*affine._m22-affine._dy*m_23) - affine._m21*(m_33*affine._m12-affine._dy*m_13)+affine._dx*(m_23*affine._m12-affine._m22*m_13); } +#if QT_DEPRECATED_SINCE(5, 13) inline qreal QTransform::det() const { return determinant(); } +#endif inline qreal QTransform::m11() const { return affine._m11; diff --git a/src/gui/painting/qtriangulator.cpp b/src/gui/painting/qtriangulator.cpp index 6d57eba123..9be3eeaffd 100644 --- a/src/gui/painting/qtriangulator.cpp +++ b/src/gui/painting/qtriangulator.cpp @@ -472,7 +472,7 @@ class QInt64Set { public: inline QInt64Set(int capacity = 64); - inline ~QInt64Set() {if (m_array) delete[] m_array;} + inline ~QInt64Set() {delete[] m_array;} inline bool isValid() const {return m_array;} void insert(quint64 key); bool contains(quint64 key) const; @@ -493,10 +493,7 @@ inline QInt64Set::QInt64Set(int capacity) { m_capacity = primeForCount(capacity); m_array = new quint64[m_capacity]; - if (m_array) - clear(); - else - m_capacity = 0; + clear(); } bool QInt64Set::rehash(int capacity) @@ -506,28 +503,19 @@ bool QInt64Set::rehash(int capacity) m_capacity = capacity; m_array = new quint64[m_capacity]; - if (m_array) { - clear(); - if (oldArray) { - for (int i = 0; i < oldCapacity; ++i) { - if (oldArray[i] != UNUSED) - insert(oldArray[i]); - } - delete[] oldArray; - } - return true; - } else { - m_capacity = oldCapacity; - m_array = oldArray; - return false; + clear(); + for (int i = 0; i < oldCapacity; ++i) { + if (oldArray[i] != UNUSED) + insert(oldArray[i]); } + delete[] oldArray; + return true; } void QInt64Set::insert(quint64 key) { if (m_count > 3 * m_capacity / 4) rehash(primeForCount(2 * m_capacity)); - Q_ASSERT_X(m_array, "QInt64Hash<T>::insert", "Hash set not allocated."); int index = int(key % m_capacity); for (int i = 0; i < m_capacity; ++i) { index += i; @@ -546,7 +534,6 @@ void QInt64Set::insert(quint64 key) bool QInt64Set::contains(quint64 key) const { - Q_ASSERT_X(m_array, "QInt64Hash<T>::contains", "Hash set not allocated."); int index = int(key % m_capacity); for (int i = 0; i < m_capacity; ++i) { index += i; @@ -562,7 +549,6 @@ bool QInt64Set::contains(quint64 key) const inline void QInt64Set::clear() { - Q_ASSERT_X(m_array, "QInt64Hash<T>::clear", "Hash set not allocated."); for (int i = 0; i < m_capacity; ++i) m_array[i] = UNUSED; m_count = 0; diff --git a/src/gui/painting/qvectorpath_p.h b/src/gui/painting/qvectorpath_p.h index d1b08ed423..df5772d4cc 100644 --- a/src/gui/painting/qvectorpath_p.h +++ b/src/gui/painting/qvectorpath_p.h @@ -99,13 +99,14 @@ public: // Shape rendering specifiers... OddEvenFill = 0x1000, WindingFill = 0x2000, - ImplicitClose = 0x4000 + ImplicitClose = 0x4000, + ExplicitOpen = 0x8000 }; // ### Falcon: introduca a struct XY for points so lars is not so confused... QVectorPath(const qreal *points, int count, - const QPainterPath::ElementType *elements = 0, + const QPainterPath::ElementType *elements = nullptr, uint hints = ArbitraryShapeHint) : m_elements(elements), m_points(points), @@ -124,14 +125,15 @@ public: inline bool isCacheable() const { return m_hints & ShouldUseCacheHint; } inline bool hasImplicitClose() const { return m_hints & ImplicitClose; } + inline bool hasExplicitOpen() const { return m_hints & ExplicitOpen; } inline bool hasWindingFill() const { return m_hints & WindingFill; } - inline void makeCacheable() const { m_hints |= ShouldUseCacheHint; m_cache = 0; } + inline void makeCacheable() const { m_hints |= ShouldUseCacheHint; m_cache = nullptr; } inline uint hints() const { return m_hints; } inline const QPainterPath::ElementType *elements() const { return m_elements; } inline const qreal *points() const { return m_points; } - inline bool isEmpty() const { return m_points == 0; } + inline bool isEmpty() const { return m_points == nullptr; } inline int elementCount() const { return m_count; } inline const QPainterPath convertToPainterPath() const; @@ -142,7 +144,7 @@ public: case QPaintEngine::ConvexMode: return ConvexPolygonHint | ImplicitClose; case QPaintEngine::OddEvenMode: return PolygonHint | OddEvenFill | ImplicitClose; case QPaintEngine::WindingMode: return PolygonHint | WindingFill | ImplicitClose; - case QPaintEngine::PolylineMode: return PolygonHint; + case QPaintEngine::PolylineMode: return PolygonHint | ExplicitOpen; default: return 0; } } @@ -163,7 +165,7 @@ public: return e; e = e->next; } - return 0; + return nullptr; } template <typename T> static inline bool isRect(const T *pts, int elementCount) { @@ -194,7 +196,7 @@ public: private: - Q_DISABLE_COPY(QVectorPath) + Q_DISABLE_COPY_MOVE(QVectorPath) const QPainterPath::ElementType *m_elements; const qreal *m_points; |