From a1dea3a62c874d8a5126bc7a90718de00a9124d0 Mon Sep 17 00:00:00 2001 From: Andrew Knight Date: Fri, 19 Sep 2014 14:23:56 +0300 Subject: direct2d: Optimize dashed line drawing This introduces a combined brush/dash pattern which can be used to perform faster dashed straight-line drawing. The dash pattern is prerendered to a tiled bitmap brush, resulting in a significant speedup for lines with many elements. As the result of non-rectilinear lines may lose quality compared to the native dashed renderer, the slow/high quality codepath can be activated by setting the QPainter::HighQualityAntialiasing render hint. Task-number: QTBUG-40604 Change-Id: I771e9a81c042b4d8b6891dc9280932696e5a0694 Reviewed-by: Andrew Knight Reviewed-by: Louai Al-Khanji Reviewed-by: Friedemann Kleint --- .../direct2d/qwindowsdirect2dpaintengine.cpp | 48 ++++++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) (limited to 'src/plugins/platforms/direct2d') diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index d963cbd463..c13f0f333a 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp @@ -43,6 +43,7 @@ #include "qwindowsfontdatabase.h" #include "qwindowsintegration.h" +#include #include #include #include @@ -101,6 +102,16 @@ static inline ID2D1Factory1 *factory() return QWindowsDirect2DContext::instance()->d2dFactory(); } +static inline D2D1_MATRIX_3X2_F transformFromLine(const QLineF &line, qreal penWidth) +{ + const qreal halfWidth = penWidth / 2; + const qreal angle = -qDegreesToRadians(line.angle()); + QTransform transform = QTransform::fromTranslate(line.p1().x() + qSin(angle) * halfWidth, + line.p1().y() - qCos(angle) * halfWidth); + transform.rotateRadians(angle); + return to_d2d_matrix_3x2_f(transform); +} + class Direct2DPathGeometryWriter { public: @@ -245,12 +256,14 @@ public: QPen qpen; ComPtr brush; ComPtr strokeStyle; + ComPtr dashBrush; inline void reset() { emulate = false; qpen = QPen(); brush.Reset(); strokeStyle.Reset(); + dashBrush.Reset(); } } pen; @@ -528,12 +541,31 @@ public: if (props.dashStyle == D2D1_DASH_STYLE_CUSTOM) { QVector dashes = newPen.dashPattern(); QVector converted(dashes.size()); - + qreal penWidth = pen.qpen.widthF(); + qreal brushWidth = 0; for (int i = 0; i < dashes.size(); i++) { converted[i] = dashes[i]; + brushWidth += penWidth * dashes[i]; } hr = factory()->CreateStrokeStyle(props, converted.constData(), converted.size(), &pen.strokeStyle); + + // Create a combined brush/dash pattern for optimized line drawing + QWindowsDirect2DBitmap bitmap; + bitmap.resize(ceil(brushWidth), ceil(penWidth)); + bitmap.deviceContext()->begin(); + bitmap.deviceContext()->get()->SetAntialiasMode(antialiasMode()); + bitmap.deviceContext()->get()->SetTransform(D2D1::IdentityMatrix()); + bitmap.deviceContext()->get()->Clear(); + const qreal offsetX = (qreal(bitmap.size().width()) - brushWidth) / 2; + const qreal offsetY = qreal(bitmap.size().height()) / 2; + bitmap.deviceContext()->get()->DrawLine(D2D1::Point2F(offsetX, offsetY), + D2D1::Point2F(brushWidth, offsetY), + pen.brush.Get(), penWidth, pen.strokeStyle.Get()); + bitmap.deviceContext()->end(); + D2D1_BITMAP_BRUSH_PROPERTIES1 bitmapBrushProperties = D2D1::BitmapBrushProperties1( + D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_CLAMP, D2D1_INTERPOLATION_MODE_LINEAR); + hr = dc()->CreateBitmapBrush(bitmap.bitmap(), bitmapBrushProperties, &pen.dashBrush); } else { hr = factory()->CreateStrokeStyle(props, NULL, 0, &pen.strokeStyle); } @@ -1296,7 +1328,12 @@ void QWindowsDirect2DPaintEngine::drawLines(const QLine *lines, int lineCount) D2D1_POINT_2F d2d_p1 = to_d2d_point_2f(p1); D2D1_POINT_2F d2d_p2 = to_d2d_point_2f(p2); - d->dc()->DrawLine(d2d_p1, d2d_p2, d->pen.brush.Get(), d->pen.qpen.widthF(), d->pen.strokeStyle.Get()); + if (!d->pen.dashBrush || state()->renderHints.testFlag(QPainter::HighQualityAntialiasing)) { + d->dc()->DrawLine(d2d_p1, d2d_p2, d->pen.brush.Get(), d->pen.qpen.widthF(), d->pen.strokeStyle.Get()); + } else { + d->pen.dashBrush->SetTransform(transformFromLine(lines[i], d->pen.qpen.widthF())); + d->dc()->DrawLine(d2d_p1, d2d_p2, d->pen.dashBrush.Get(), d->pen.qpen.widthF(), NULL); + } } } } @@ -1332,7 +1369,12 @@ void QWindowsDirect2DPaintEngine::drawLines(const QLineF *lines, int lineCount) D2D1_POINT_2F d2d_p1 = to_d2d_point_2f(p1); D2D1_POINT_2F d2d_p2 = to_d2d_point_2f(p2); - d->dc()->DrawLine(d2d_p1, d2d_p2, d->pen.brush.Get(), d->pen.qpen.widthF(), d->pen.strokeStyle.Get()); + if (!d->pen.dashBrush || state()->renderHints.testFlag(QPainter::HighQualityAntialiasing)) { + d->dc()->DrawLine(d2d_p1, d2d_p2, d->pen.brush.Get(), d->pen.qpen.widthF(), d->pen.strokeStyle.Get()); + } else { + d->pen.dashBrush->SetTransform(transformFromLine(lines[i], d->pen.qpen.widthF())); + d->dc()->DrawLine(d2d_p1, d2d_p2, d->pen.dashBrush.Get(), d->pen.qpen.widthF(), NULL); + } } } } -- cgit v1.2.3