diff options
-rw-r--r-- | src/gui/painting/qdrawhelper.cpp | 192 | ||||
-rw-r--r-- | src/gui/painting/qdrawhelper_neon.cpp | 2 | ||||
-rw-r--r-- | src/gui/painting/qdrawhelper_p.h | 25 | ||||
-rw-r--r-- | src/gui/painting/qdrawhelper_sse2.cpp | 2 | ||||
-rw-r--r-- | tests/auto/gui/painting/qpainter/tst_qpainter.cpp | 43 |
5 files changed, 156 insertions, 108 deletions
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index aad904e8fc..138e1ecb3b 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -3441,78 +3441,50 @@ static void QT_FASTCALL getLinearGradientValues(LinearGradientValues *v, const Q } } -static const uint * QT_FASTCALL qt_fetch_linear_gradient(uint *buffer, const Operator *op, const QSpanData *data, - int y, int x, int length) +class GradientBase32 { - const uint *b = buffer; - qreal t, inc; - - bool affine = true; - qreal rx=0, ry=0; - if (op->linear.l == 0) { - t = inc = 0; - } else { - rx = data->m21 * (y + qreal(0.5)) + data->m11 * (x + qreal(0.5)) + data->dx; - ry = data->m22 * (y + qreal(0.5)) + data->m12 * (x + qreal(0.5)) + data->dy; - t = op->linear.dx*rx + op->linear.dy*ry + op->linear.off; - inc = op->linear.dx * data->m11 + op->linear.dy * data->m12; - affine = !data->m13 && !data->m23; - - if (affine) { - t *= (GRADIENT_STOPTABLE_SIZE - 1); - inc *= (GRADIENT_STOPTABLE_SIZE - 1); - } +public: + typedef uint Type; + static Type null() { return 0; } + static Type fetchSingle(const QGradientData& gradient, qreal v) + { + return qt_gradient_pixel(&gradient, v); } - - const uint *end = buffer + length; - if (affine) { - if (inc > qreal(-1e-5) && inc < qreal(1e-5)) { - QT_MEMFILL_UINT(buffer, length, qt_gradient_pixel_fixed(&data->gradient, int(t * FIXPT_SIZE))); - } else { - if (t+inc*length < qreal(INT_MAX >> (FIXPT_BITS + 1)) && - t+inc*length > qreal(INT_MIN >> (FIXPT_BITS + 1))) { - // we can use fixed point math - int t_fixed = int(t * FIXPT_SIZE); - int inc_fixed = int(inc * FIXPT_SIZE); - while (buffer < end) { - *buffer = qt_gradient_pixel_fixed(&data->gradient, t_fixed); - t_fixed += inc_fixed; - ++buffer; - } - } else { - // we have to fall back to float math - while (buffer < end) { - *buffer = qt_gradient_pixel(&data->gradient, t/GRADIENT_STOPTABLE_SIZE); - t += inc; - ++buffer; - } - } - } - } else { // fall back to float math here as well - qreal rw = data->m23 * (y + qreal(0.5)) + data->m13 * (x + qreal(0.5)) + data->m33; - while (buffer < end) { - qreal x = rx/rw; - qreal y = ry/rw; - t = (op->linear.dx*x + op->linear.dy *y) + op->linear.off; - - *buffer = qt_gradient_pixel(&data->gradient, t); - rx += data->m11; - ry += data->m12; - rw += data->m13; - if (!rw) { - rw += data->m13; - } - ++buffer; - } + static Type fetchSingle(const QGradientData& gradient, int v) + { + return qt_gradient_pixel_fixed(&gradient, v); } + static void memfill(Type *buffer, Type fill, int length) + { + qt_memfill32(buffer, fill, length); + } +}; - return b; -} +class GradientBase64 +{ +public: + typedef QRgba64 Type; + static Type null() { return QRgba64::fromRgba64(0); } + static Type fetchSingle(const QGradientData& gradient, qreal v) + { + return qt_gradient_pixel64(&gradient, v); + } + static Type fetchSingle(const QGradientData& gradient, int v) + { + return qt_gradient_pixel64_fixed(&gradient, v); + } + static void memfill(Type *buffer, Type fill, int length) + { + qt_memfill64((quint64*)buffer, fill, length); + } +}; -static const QRgba64 * QT_FASTCALL qt_fetch_linear_gradient_rgb64(QRgba64 *buffer, const Operator *op, const QSpanData *data, - int y, int x, int length) +template<class GradientBase, typename BlendType> +static inline const BlendType * QT_FASTCALL qt_fetch_linear_gradient_template( + BlendType *buffer, const Operator *op, const QSpanData *data, + int y, int x, int length) { - const QRgba64 *b = buffer; + const BlendType *b = buffer; qreal t, inc; bool affine = true; @@ -3532,11 +3504,10 @@ static const QRgba64 * QT_FASTCALL qt_fetch_linear_gradient_rgb64(QRgba64 *buffe } } - const QRgba64 *end = buffer + length; + const BlendType *end = buffer + length; if (affine) { if (inc > qreal(-1e-5) && inc < qreal(1e-5)) { - QRgba64 color = qt_gradient_pixel64_fixed(&data->gradient, int(t * FIXPT_SIZE)); - qt_memfill64((quint64*)buffer, color, length); + GradientBase::memfill(buffer, GradientBase::fetchSingle(data->gradient, int(t * FIXPT_SIZE)), length); } else { if (t+inc*length < qreal(INT_MAX >> (FIXPT_BITS + 1)) && t+inc*length > qreal(INT_MIN >> (FIXPT_BITS + 1))) { @@ -3544,14 +3515,14 @@ static const QRgba64 * QT_FASTCALL qt_fetch_linear_gradient_rgb64(QRgba64 *buffe int t_fixed = int(t * FIXPT_SIZE); int inc_fixed = int(inc * FIXPT_SIZE); while (buffer < end) { - *buffer = qt_gradient_pixel64_fixed(&data->gradient, t_fixed); + *buffer = GradientBase::fetchSingle(data->gradient, t_fixed); t_fixed += inc_fixed; ++buffer; } } else { // we have to fall back to float math while (buffer < end) { - *buffer = qt_gradient_pixel64(&data->gradient, t/GRADIENT_STOPTABLE_SIZE); + *buffer = GradientBase::fetchSingle(data->gradient, t/GRADIENT_STOPTABLE_SIZE); t += inc; ++buffer; } @@ -3564,7 +3535,7 @@ static const QRgba64 * QT_FASTCALL qt_fetch_linear_gradient_rgb64(QRgba64 *buffe qreal y = ry/rw; t = (op->linear.dx*x + op->linear.dy *y) + op->linear.off; - *buffer = qt_gradient_pixel64(&data->gradient, t); + *buffer = GradientBase::fetchSingle(data->gradient, t); rx += data->m11; ry += data->m12; rw += data->m13; @@ -3578,6 +3549,18 @@ static const QRgba64 * QT_FASTCALL qt_fetch_linear_gradient_rgb64(QRgba64 *buffe return b; } +static const uint * QT_FASTCALL qt_fetch_linear_gradient(uint *buffer, const Operator *op, const QSpanData *data, + int y, int x, int length) +{ + return qt_fetch_linear_gradient_template<GradientBase32, uint>(buffer, op, data, y, x, length); +} + +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); +} + static void QT_FASTCALL getRadialGradientValues(RadialGradientValues *v, const QSpanData *data) { v->dx = data->gradient.radial.center.x - data->gradient.radial.focal.x; @@ -3592,19 +3575,22 @@ static void QT_FASTCALL getRadialGradientValues(RadialGradientValues *v, const Q v->extended = !qFuzzyIsNull(data->gradient.radial.focal.radius) || v->a <= 0; } -class RadialFetchPlain +template <class GradientBase> +class RadialFetchPlain : public GradientBase { public: - static inline void fetch(uint *buffer, uint *end, const Operator *op, const QSpanData *data, qreal det, - qreal delta_det, qreal delta_delta_det, qreal b, qreal delta_b) + typedef typename GradientBase::Type BlendType; + static void fetch(BlendType *buffer, BlendType *end, + const Operator *op, const QSpanData *data, qreal det, + qreal delta_det, qreal delta_delta_det, qreal b, qreal delta_b) { if (op->radial.extended) { while (buffer < end) { - quint32 result = 0; + BlendType result = GradientBase::null(); if (det >= 0) { qreal w = qSqrt(det) - b; if (data->gradient.radial.focal.radius + op->radial.dr * w >= 0) - result = qt_gradient_pixel(&data->gradient, w); + result = GradientBase::fetchSingle(data->gradient, w); } *buffer = result; @@ -3617,7 +3603,7 @@ public: } } else { while (buffer < end) { - *buffer++ = qt_gradient_pixel(&data->gradient, qSqrt(det) - b); + *buffer++ = GradientBase::fetchSingle(data->gradient, qSqrt(det) - b); det += delta_det; delta_det += delta_delta_det; @@ -3630,15 +3616,23 @@ public: const uint * QT_FASTCALL qt_fetch_radial_gradient_plain(uint *buffer, const Operator *op, const QSpanData *data, int y, int x, int length) { - return qt_fetch_radial_gradient_template<RadialFetchPlain>(buffer, op, data, y, x, length); + return qt_fetch_radial_gradient_template<RadialFetchPlain<GradientBase32>, uint>(buffer, op, data, y, x, length); } static SourceFetchProc qt_fetch_radial_gradient = qt_fetch_radial_gradient_plain; -static const uint * QT_FASTCALL qt_fetch_conical_gradient(uint *buffer, const Operator *, const QSpanData *data, - int y, int x, int length) +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); +} + +template <class GradientBase, typename BlendType> +static inline const BlendType * QT_FASTCALL qt_fetch_conical_gradient_template( + BlendType *buffer, const QSpanData *data, + int y, int x, int length) { - const uint *b = buffer; + const BlendType *b = buffer; qreal rx = data->m21 * (y + qreal(0.5)) + data->dx + data->m11 * (x + qreal(0.5)); qreal ry = data->m22 * (y + qreal(0.5)) @@ -3647,14 +3641,14 @@ static const uint * QT_FASTCALL qt_fetch_conical_gradient(uint *buffer, const Op const qreal inv2pi = M_1_PI / 2.0; - const uint *end = buffer + length; + const BlendType *end = buffer + length; if (affine) { rx -= data->gradient.conical.center.x; ry -= data->gradient.conical.center.y; while (buffer < end) { qreal angle = qAtan2(ry, rx) + data->gradient.conical.angle; - *buffer = qt_gradient_pixel(&data->gradient, 1 - angle * inv2pi); + *buffer = GradientBase::fetchSingle(data->gradient, 1 - angle * inv2pi); rx += data->m11; ry += data->m12; @@ -3670,7 +3664,7 @@ static const uint * QT_FASTCALL qt_fetch_conical_gradient(uint *buffer, const Op rx/rw - data->gradient.conical.center.y) + data->gradient.conical.angle; - *buffer = qt_gradient_pixel(&data->gradient, 1 - angle * inv2pi); + *buffer = GradientBase::fetchSingle(data->gradient, 1 - angle * inv2pi); rx += data->m11; ry += data->m12; @@ -3684,6 +3678,18 @@ static const uint * QT_FASTCALL qt_fetch_conical_gradient(uint *buffer, const Op return b; } +static const uint * QT_FASTCALL qt_fetch_conical_gradient(uint *buffer, const Operator *, const QSpanData *data, + int y, int x, int length) +{ + return qt_fetch_conical_gradient_template<GradientBase32, uint>(buffer, data, y, x, length); +} + +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); +} + # define PRELOAD_INIT(x) # define PRELOAD_INIT2(x,y) # define PRELOAD_COND(x) @@ -5876,6 +5882,8 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in switch(data->type) { case QSpanData::Solid: solidSource = data->solid.color.isOpaque(); + op.srcFetch = 0; + op.srcFetch64 = 0; break; case QSpanData::LinearGradient: solidSource = !data->gradient.alphaColor; @@ -5887,18 +5895,20 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in solidSource = !data->gradient.alphaColor; getRadialGradientValues(&op.radial, data); op.srcFetch = qt_fetch_radial_gradient; - op.srcFetch64 = 0; //qt_fetch_radial_gradient_rgb64; + op.srcFetch64 = qt_fetch_radial_gradient_rgb64; break; case QSpanData::ConicalGradient: solidSource = !data->gradient.alphaColor; op.srcFetch = qt_fetch_conical_gradient; - op.srcFetch64 = 0; //qt_fetch_conical_gradient_rgb64; + op.srcFetch64 = qt_fetch_conical_gradient_rgb64; break; case QSpanData::Texture: + solidSource = !data->texture.hasAlpha; op.srcFetch = sourceFetch[getBlendType(data)][data->texture.format]; op.srcFetch64 = sourceFetch64[getBlendType(data)][data->texture.format]; - solidSource = !data->texture.hasAlpha; + break; default: + Q_UNREACHABLE(); break; } @@ -6212,7 +6222,7 @@ public: bool isSupported() const { - return op.srcFetch64 && op.func64 && op.destFetch64 && op.destStore64; + return op.func64 && op.destFetch64 && op.destStore64; } const QRgba64 *fetch(int x, int y, int len) @@ -6303,7 +6313,7 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi QSpanData *data = reinterpret_cast<QSpanData *>(userData); Operator op = getOperator(data, spans, count); - if (!op.srcFetch64 || !op.func64) { + if (!op.func64) { qWarning() << Q_FUNC_INFO << "Unsupported blend"; return blend_untransformed_generic(count, spans, userData); } @@ -6544,7 +6554,7 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD QSpanData *data = reinterpret_cast<QSpanData *>(userData); Operator op = getOperator(data, spans, count); - if (!op.srcFetch64 || !op.func64) { + if (!op.func64) { qDebug("unsupported rgb64 blend"); return blend_tiled_generic(count, spans, userData); } diff --git a/src/gui/painting/qdrawhelper_neon.cpp b/src/gui/painting/qdrawhelper_neon.cpp index 36e2488a94..08e564f017 100644 --- a/src/gui/painting/qdrawhelper_neon.cpp +++ b/src/gui/painting/qdrawhelper_neon.cpp @@ -986,7 +986,7 @@ public: const uint * QT_FASTCALL qt_fetch_radial_gradient_neon(uint *buffer, const Operator *op, const QSpanData *data, int y, int x, int length) { - return qt_fetch_radial_gradient_template<QRadialFetchSimd<QSimdNeon> >(buffer, op, data, y, x, length); + return qt_fetch_radial_gradient_template<QRadialFetchSimd<QSimdNeon>,uint>(buffer, op, data, y, x, length); } QT_END_NAMESPACE diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index 53f4db6fce..d584e392e9 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -395,24 +395,24 @@ static inline qreal qRadialDeterminant(qreal a, qreal b, qreal c) return (b * b) - (4 * a * c); } -template <class RadialFetchFunc> Q_STATIC_TEMPLATE_FUNCTION -const uint * QT_FASTCALL qt_fetch_radial_gradient_template(uint *buffer, const Operator *op, const QSpanData *data, - int y, int x, int length) +template <class RadialFetchFunc, typename BlendType> static +const BlendType * QT_FASTCALL qt_fetch_radial_gradient_template(BlendType *buffer, const Operator *op, + const QSpanData *data, int y, int x, int length) { // avoid division by zero if (qFuzzyIsNull(op->radial.a)) { - qt_memfill32(buffer, 0, length); + RadialFetchFunc::memfill(buffer, RadialFetchFunc::null(), length); return buffer; } - const uint *b = buffer; + const BlendType *b = buffer; qreal rx = data->m21 * (y + qreal(0.5)) + data->dx + data->m11 * (x + qreal(0.5)); qreal ry = data->m22 * (y + qreal(0.5)) + data->dy + data->m12 * (x + qreal(0.5)); bool affine = !data->m13 && !data->m23; - uint *end = buffer + length; + BlendType *end = buffer + length; if (affine) { rx -= data->gradient.radial.focal.x; ry -= data->gradient.radial.focal.y; @@ -459,7 +459,7 @@ const uint * QT_FASTCALL qt_fetch_radial_gradient_template(uint *buffer, const O qreal b = 2*(op->radial.dr*data->gradient.radial.focal.radius + gx*op->radial.dx + gy*op->radial.dy); qreal det = qRadialDeterminant(op->radial.a, b, op->radial.sqrfr - (gx*gx + gy*gy)); - quint32 result = 0; + BlendType result = RadialFetchFunc::null(); if (det >= 0) { qreal detSqrt = qSqrt(det); @@ -469,7 +469,7 @@ const uint * QT_FASTCALL qt_fetch_radial_gradient_template(uint *buffer, const O qreal s = qMax(s0, s1); if (data->gradient.radial.focal.radius + op->radial.dr * s >= 0) - result = qt_gradient_pixel(&data->gradient, s); + result = RadialFetchFunc::fetchSingle(data->gradient, s); } *buffer = result; @@ -490,6 +490,15 @@ template <class Simd> class QRadialFetchSimd { public: + static uint null() { return 0; } + static uint fetchSingle(const QGradientData& gradient, qreal v) + { + return qt_gradient_pixel(&gradient, v); + } + static void memfill(uint *buffer, uint fill, int length) + { + qt_memfill32(buffer, fill, length); + } static void fetch(uint *buffer, uint *end, const Operator *op, const QSpanData *data, qreal det, qreal delta_det, qreal delta_delta_det, qreal b, qreal delta_b) { diff --git a/src/gui/painting/qdrawhelper_sse2.cpp b/src/gui/painting/qdrawhelper_sse2.cpp index b8957fe2fb..17e9d462fd 100644 --- a/src/gui/painting/qdrawhelper_sse2.cpp +++ b/src/gui/painting/qdrawhelper_sse2.cpp @@ -550,7 +550,7 @@ public: const uint * QT_FASTCALL qt_fetch_radial_gradient_sse2(uint *buffer, const Operator *op, const QSpanData *data, int y, int x, int length) { - return qt_fetch_radial_gradient_template<QRadialFetchSimd<QSimdSse2> >(buffer, op, data, y, x, length); + return qt_fetch_radial_gradient_template<QRadialFetchSimd<QSimdSse2>,uint>(buffer, op, data, y, x, length); } void qt_scale_image_argb32_on_argb32_sse2(uchar *destPixels, int dbpl, diff --git a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp index 3c5fcba06e..53e9ad6f20 100644 --- a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp +++ b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp @@ -203,8 +203,10 @@ private slots: void gradientPixelFormat_data(); void gradientPixelFormat(); - void gradientRgb30_data(); - void gradientRgb30(); + void linearGradientRgb30_data(); + void linearGradientRgb30(); + void radialGradientRgb30_data(); + void radialGradientRgb30(); void fpe_pixmapTransform(); void fpe_zeroLengthLines(); @@ -3952,7 +3954,7 @@ void tst_QPainter::gradientInterpolation() } } -void tst_QPainter::gradientRgb30_data() +void tst_QPainter::linearGradientRgb30_data() { QTest::addColumn<QColor>("stop0"); QTest::addColumn<QColor>("stop1"); @@ -3962,21 +3964,48 @@ void tst_QPainter::gradientRgb30_data() QTest::newRow("white->red") << QColor(Qt::white) << QColor(Qt::red); } -void tst_QPainter::gradientRgb30() +void tst_QPainter::linearGradientRgb30() { QFETCH(QColor, stop0); QFETCH(QColor, stop1); - QLinearGradient gradient(0, 0, 512, 1); + QLinearGradient gradient(0, 0, 1000, 1); gradient.setColorAt(0.0, stop0); gradient.setColorAt(1.0, stop1); - QImage image(512, 1, QImage::Format_RGB30); + QImage image(1000, 1, QImage::Format_RGB30); QPainter painter(&image); painter.fillRect(image.rect(), gradient); painter.end(); - for (int i = 0; i < 511; ++i) { + for (int i = 0; i < 1000; ++i) { + QColor p1 = image.pixelColor(i, 0); + QColor p2 = image.pixelColor(i + 1, 0); + QVERIFY(p1 != p2); + QVERIFY(qGray(p1.rgb()) >= qGray(p2.rgb())); + } +} + +void tst_QPainter::radialGradientRgb30_data() +{ + linearGradientRgb30_data(); +} + +void tst_QPainter::radialGradientRgb30() +{ + QFETCH(QColor, stop0); + QFETCH(QColor, stop1); + + QRadialGradient gradient(0, 0, 1000); + gradient.setColorAt(0.0, stop0); + gradient.setColorAt(1.0, stop1); + + QImage image(1000, 1, QImage::Format_A2BGR30_Premultiplied); + QPainter painter(&image); + painter.fillRect(image.rect(), gradient); + painter.end(); + + for (int i = 0; i < 1000; ++i) { QColor p1 = image.pixelColor(i, 0); QColor p2 = image.pixelColor(i + 1, 0); QVERIFY(p1 != p2); |