summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/direct2d
diff options
context:
space:
mode:
authorLouai Al-Khanji <louai.al-khanji@digia.com>2014-01-24 14:59:39 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-01-31 14:51:48 +0100
commit8d38c0f6d6732762d6f0394880e96a6b2628b05a (patch)
tree968f52b78a0bad6fd387c69b6cd986b1ab10c57d /src/plugins/platforms/direct2d
parent79fb39a87ce5ffbda22f297977b510e89ebf5485 (diff)
Move Direct2D paint engine on top of QPaintEngineEx
Make QWindowsDirect2DPaintEngine a subclass of QPaintEngineEx instead of QPaintEngine like the other paint engines inside Qt. This should both be faster and better supported, as the code paths handling QPaintEngineEx in QPainter are tested more. Additionally the visual output is closer to the raster engine, as all primitives are drawn using the same mechanism, resulting in fewer off-by-one-pixel errors. This change also makes designer work mostly correctly, apart from some remaining clipping bugs. Change-Id: I53d36c08e73883ff35e389d57f9d30b482fc6721 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Diffstat (limited to 'src/plugins/platforms/direct2d')
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h4
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp954
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h41
3 files changed, 531 insertions, 468 deletions
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h b/src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h
index 0ca26ca4cb..98248515e6 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h
@@ -54,12 +54,12 @@ QT_BEGIN_NAMESPACE
Q_DECL_CONSTEXPR inline D2D1_RECT_U to_d2d_rect_u(const QRect &qrect)
{
- return D2D1::RectU(qrect.left(), qrect.top(), qrect.right(), qrect.bottom());
+ return D2D1::RectU(qrect.x(), qrect.y(), qrect.x() + qrect.width(), qrect.y() + qrect.height());
}
Q_DECL_CONSTEXPR inline D2D1_RECT_F to_d2d_rect_f(const QRectF &qrect)
{
- return D2D1::RectF(qrect.left(), qrect.top(), qrect.right(), qrect.bottom());
+ return D2D1::RectF(qrect.x(), qrect.y(), qrect.x() + qrect.width(), qrect.y() + qrect.height());
}
Q_DECL_CONSTEXPR inline D2D1_SIZE_U to_d2d_size_u(const QSizeF &qsize)
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp
index aad3d192e7..e19a6be47b 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp
@@ -52,30 +52,30 @@
#include "qwindowsfontdatabase.h"
#include "qwindowsintegration.h"
-#include <QtCore/QStack>
#include <QtGui/private/qpaintengine_p.h>
#include <QtGui/private/qtextengine_p.h>
#include <QtGui/private/qfontengine_p.h>
+#include <QtGui/private/qstatictext_p.h>
-#include <dwrite_1.h>
#include <wrl.h>
-
using Microsoft::WRL::ComPtr;
QT_BEGIN_NAMESPACE
+// The enum values below are set as tags on the device context
+// in the various draw methods. When EndDraw is called the device context
+// will report the last set tag number in case of errors
+// along with an error code
+
+// Microsoft keeps a list of d2d error codes here:
+// http://msdn.microsoft.com/en-us/library/windows/desktop/dd370979(v=vs.85).aspx
enum {
D2DDebugDrawInitialStateTag = -1,
- D2DDebugDrawEllipseTag = 1,
- D2DDebugDrawImageTag,
- D2DDebugDrawLinesTag,
- D2DDebugDrawPathTag,
+ D2DDebugDrawImageTag = 1,
+ D2DDebugFillTag,
D2DDebugDrawPixmapTag,
- D2DDebugDrawPointsTag,
- D2DDebugDrawPolygonTag,
- D2DDebugDrawRectsTag,
- D2DDebugDrawTextItemTag,
- D2DDebugDrawTiledPixmap
+ D2DDebugDrawStaticTextItemTag,
+ D2DDebugDrawTextItemTag
};
#define D2D_TAG(tag) d->dc()->SetTags(tag, tag)
@@ -86,12 +86,12 @@ static inline ID2D1Factory1 *factory()
return QWindowsDirect2DContext::instance()->d2dFactory();
}
-static const qreal defaultOpacity = 1.0;
-static const qreal defaultPenWidth = 1.0;
+// XXX reduce code duplication between painterPathToPathGeometry and
+// vectorPathToID2D1PathGeometry, the two are quite similar
-static ComPtr<ID2D1PathGeometry> painterPathToPathGeometry(const QPainterPath &path)
+static ComPtr<ID2D1PathGeometry1> painterPathToPathGeometry(const QPainterPath &path)
{
- ComPtr<ID2D1PathGeometry> geometry;
+ ComPtr<ID2D1PathGeometry1> geometry;
ComPtr<ID2D1GeometrySink> sink;
HRESULT hr = factory()->CreatePathGeometry(&geometry);
@@ -167,33 +167,129 @@ static ComPtr<ID2D1PathGeometry> painterPathToPathGeometry(const QPainterPath &p
return geometry;
}
-static ComPtr<ID2D1PathGeometry> regionToPathGeometry(const QRegion &region)
+static ComPtr<ID2D1PathGeometry1> vectorPathToID2D1PathGeometry(const QVectorPath &path, bool alias)
{
- QPainterPath ppath;
- ppath.addRegion(region);
- return painterPathToPathGeometry(ppath);
+ ComPtr<ID2D1PathGeometry1> pathGeometry;
+ HRESULT hr = factory()->CreatePathGeometry(pathGeometry.GetAddressOf());
+ if (FAILED(hr)) {
+ qWarning("%s: Could not create path geometry: %#x", __FUNCTION__, hr);
+ return NULL;
+ }
+
+ if (path.isEmpty())
+ return pathGeometry;
+
+ ComPtr<ID2D1GeometrySink> sink;
+ hr = pathGeometry->Open(sink.GetAddressOf());
+ if (FAILED(hr)) {
+ qWarning("%s: Could not create geometry sink: %#x", __FUNCTION__, hr);
+ return NULL;
+ }
+
+ sink->SetFillMode(path.hasWindingFill() ? D2D1_FILL_MODE_WINDING
+ : D2D1_FILL_MODE_ALTERNATE);
+
+ bool inFigure = false;
+
+ const QPainterPath::ElementType *types = path.elements();
+ const int count = path.elementCount();
+ const qreal *points = 0;
+
+ QScopedArrayPointer<qreal> rounded_points;
+
+ if (alias) {
+ // Aliased painting, round to whole numbers
+ rounded_points.reset(new qreal[count * 2]);
+ points = rounded_points.data();
+
+ for (int i = 0; i < (count * 2); i++)
+ rounded_points[i] = qRound(path.points()[i]);
+ } else {
+ // Antialiased painting, keep original numbers
+ points = path.points();
+ }
+
+ Q_ASSERT(points);
+
+ if (types) {
+ qreal x, y;
+
+ for (int i = 0; i < count; i++) {
+ x = points[i * 2];
+ y = points[i * 2 + 1];
+
+ switch (types[i]) {
+ case QPainterPath::MoveToElement:
+ if (inFigure)
+ sink->EndFigure(D2D1_FIGURE_END_OPEN);
+
+ sink->BeginFigure(D2D1::Point2F(x, y), D2D1_FIGURE_BEGIN_FILLED);
+ inFigure = true;
+ break;
+
+ case QPainterPath::LineToElement:
+ sink->AddLine(D2D1::Point2F(x, y));
+ break;
+
+ case QPainterPath::CurveToElement:
+ {
+ Q_ASSERT((i + 2) < count);
+ Q_ASSERT(types[i+1] == QPainterPath::CurveToDataElement);
+ Q_ASSERT(types[i+2] == QPainterPath::CurveToDataElement);
+
+ i++;
+ const qreal x2 = points[i * 2];
+ const qreal y2 = points[i * 2 + 1];
+
+ i++;
+ const qreal x3 = points[i * 2];
+ const qreal y3 = points[i * 2 + 1];
+
+ D2D1_BEZIER_SEGMENT segment = {
+ D2D1::Point2F(x, y),
+ D2D1::Point2F(x2, y2),
+ D2D1::Point2F(x3, y3)
+ };
+
+ sink->AddBezier(segment);
+ }
+ break;
+
+ case QPainterPath::CurveToDataElement:
+ qWarning("%s: Unhandled Curve Data Element", __FUNCTION__);
+ break;
+ }
+ }
+ } else {
+ sink->BeginFigure(D2D1::Point2F(points[0], points[1]), D2D1_FIGURE_BEGIN_FILLED);
+ inFigure = true;
+
+ for (int i = 1; i < count; i++)
+ sink->AddLine(D2D1::Point2F(points[i * 2], points[i * 2 + 1]));
+ }
+
+ if (inFigure) {
+ if (path.hasImplicitClose())
+ sink->AddLine(D2D1::Point2F(points[0], points[1]));
+
+ sink->EndFigure(D2D1_FIGURE_END_OPEN);
+ }
+
+ sink->Close();
+
+ return pathGeometry;
}
-class QWindowsDirect2DPaintEnginePrivate : public QPaintEnginePrivate
+class QWindowsDirect2DPaintEnginePrivate : public QPaintEngineExPrivate
{
Q_DECLARE_PUBLIC(QWindowsDirect2DPaintEngine)
public:
QWindowsDirect2DPaintEnginePrivate(QWindowsDirect2DBitmap *bm)
: bitmap(bm)
, clipPushed(false)
- , hasPerspectiveTransform(false)
- , opacity(1.0)
- , renderHints(QPainter::TextAntialiasing)
{
pen.reset();
-
- HRESULT hr = factory()->CreateStrokeStyle(D2D1::StrokeStyleProperties(D2D1_CAP_STYLE_ROUND,
- D2D1_CAP_STYLE_ROUND,
- D2D1_CAP_STYLE_ROUND),
- NULL, 0,
- pointStrokeStyle.ReleaseAndGetAddressOf());
- if (FAILED(hr))
- qWarning("%s: Could not create stroke style for points and zero length lines: %#x", __FUNCTION__, hr);
+ brush.reset();
dc()->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
}
@@ -203,34 +299,33 @@ public:
QPainterPath clipPath;
bool clipPushed;
- ComPtr<ID2D1StrokeStyle> pointStrokeStyle;
QPointF currentBrushOrigin;
- bool hasPerspectiveTransform;
-
- qreal opacity;
-
struct {
- qreal width;
- QColor color;
- bool isNull;
+ bool emulate;
+ QPen qpen;
ComPtr<ID2D1Brush> brush;
ComPtr<ID2D1StrokeStyle1> strokeStyle;
inline void reset() {
- width = defaultPenWidth;
- color = QColor(Qt::black);
- isNull = true;
+ emulate = false;
+ qpen = QPen();
brush.Reset();
strokeStyle.Reset();
}
} pen;
struct {
+ bool emulate;
+ QBrush qbrush;
ComPtr<ID2D1Brush> brush;
- } brush;
- QPainter::RenderHints renderHints;
+ inline void reset() {
+ emulate = false;
+ brush.Reset();
+ qbrush = QBrush();
+ }
+ } brush;
inline ID2D1DeviceContext *dc() const
{
@@ -240,72 +335,46 @@ public:
inline D2D1_INTERPOLATION_MODE interpolationMode() const
{
- return (renderHints & QPainter::SmoothPixmapTransform) ? D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC
- : D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
+ Q_Q(const QWindowsDirect2DPaintEngine);
+ // XXX are we choosing the right d2d interpolation modes?
+ return (q->state()->renderHints & QPainter::SmoothPixmapTransform) ? D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC
+ : D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
}
inline D2D1_ANTIALIAS_MODE antialiasMode() const
{
- return (renderHints & QPainter::Antialiasing) ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
- : D2D1_ANTIALIAS_MODE_ALIASED;
- }
-
- void updateState(const QPaintEngineState &state, QPaintEngine::DirtyFlags dirty)
- {
- if (dirty & QPaintEngine::DirtyPen)
- updatePen(state.pen());
-
- if (dirty & QPaintEngine::DirtyBrush)
- updateBrush(state.brush());
-
- if (dirty & QPaintEngine::DirtyBrushOrigin)
- updateBrushOrigin(state.brushOrigin());
-
- if (dirty & QPaintEngine::DirtyHints)
- updateHints(state.renderHints());
-
- if (dirty & QPaintEngine::DirtyTransform)
- updateTransform(state.transform());
-
- if (dirty & QPaintEngine::DirtyClipEnabled)
- updateClipEnabled(state.isClipEnabled());
-
- if (dirty & QPaintEngine::DirtyClipPath)
- updateClipPath(state.clipPath(), state.clipOperation());
-
- if (dirty & QPaintEngine::DirtyClipRegion)
- updateClipRegion(state.clipRegion(), state.clipOperation());
-
- if (dirty & QPaintEngine::DirtyOpacity)
- updateOpacity(state.opacity());
+ Q_Q(const QWindowsDirect2DPaintEngine);
+ return (q->state()->renderHints & QPainter::Antialiasing) ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
+ : D2D1_ANTIALIAS_MODE_ALIASED;
}
- void updateTransform(const QTransform &t)
+ void updateTransform()
{
- dc()->SetTransform(to_d2d_matrix_3x2_f(t));
- hasPerspectiveTransform = !t.isAffine();
+ Q_Q(const QWindowsDirect2DPaintEngine);
+ // Note the loss of info going from 3x3 to 3x2 matrix here
+ dc()->SetTransform(to_d2d_matrix_3x2_f(q->state()->transform()));
}
- void updateOpacity(qreal o)
+ void updateOpacity()
{
- opacity = o;
+ Q_Q(const QWindowsDirect2DPaintEngine);
+ qreal opacity = q->state()->opacity;
if (brush.brush)
- brush.brush.Get()->SetOpacity(o);
+ brush.brush->SetOpacity(opacity);
if (pen.brush)
- pen.brush.Get()->SetOpacity(o);
+ pen.brush->SetOpacity(opacity);
}
void pushClip()
{
- ComPtr<ID2D1Geometry> geometricMask = painterPathToPathGeometry(clipPath);
- if (!geometricMask) {
- qWarning("%s: Could not convert painter path, not pushing clip path!", __FUNCTION__);
+ popClip();
+
+ ComPtr<ID2D1PathGeometry1> geometry = painterPathToPathGeometry(clipPath);
+ if (!geometry)
return;
- }
- popClip();
dc()->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(),
- geometricMask.Get(),
+ geometry.Get(),
antialiasMode(),
D2D1::IdentityMatrix(),
1.0,
@@ -323,9 +392,10 @@ public:
}
}
- void updateClipEnabled(bool enabled)
+ void updateClipEnabled()
{
- if (!enabled)
+ Q_Q(const QWindowsDirect2DPaintEngine);
+ if (!q->state()->clipEnabled)
popClip();
else if (!clipPushed)
pushClip();
@@ -348,26 +418,47 @@ public:
}
}
- void updateClipRegion(const QRegion &region, Qt::ClipOperation operation)
+ void updateCompositionMode()
{
- QPainterPath p;
- p.addRegion(region);
- updateClipPath(p, operation);
+ Q_Q(const QWindowsDirect2DPaintEngine);
+ QPainter::CompositionMode mode = q->state()->compositionMode();
+
+ switch (mode) {
+ case QPainter::CompositionMode_Source:
+ dc()->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
+ break;
+ case QPainter::CompositionMode_SourceOver:
+ dc()->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
+ break;
+
+ default:
+ qWarning("Unsupported composition mode: %d", mode);
+ break;
+ }
}
void updateBrush(const QBrush &newBrush)
{
- brush.brush = to_d2d_brush(newBrush);
+ Q_Q(const QWindowsDirect2DPaintEngine);
+
+ if (qbrush_fast_equals(brush.qbrush, newBrush))
+ return;
+
+ brush.brush = to_d2d_brush(newBrush, &brush.emulate);
+ brush.qbrush = newBrush;
+
if (brush.brush) {
- brush.brush->SetOpacity(opacity);
+ brush.brush->SetOpacity(q->state()->opacity);
applyBrushOrigin(currentBrushOrigin);
}
}
- void updateBrushOrigin(const QPointF &origin)
+ void updateBrushOrigin()
{
+ Q_Q(const QWindowsDirect2DPaintEngine);
+
negateCurrentBrushOrigin();
- applyBrushOrigin(origin);
+ applyBrushOrigin(q->state()->brushOrigin);
}
void negateCurrentBrushOrigin()
@@ -395,46 +486,37 @@ public:
currentBrushOrigin = origin;
}
- void updatePen(const QPen &newPen)
+ void updatePen()
{
+ Q_Q(const QWindowsDirect2DPaintEngine);
+ const QPen &newPen = q->state()->pen;
+
+ if (qpen_fast_equals(newPen, pen.qpen))
+ return;
+
pen.reset();
+ pen.qpen = newPen;
if (newPen.style() == Qt::NoPen)
return;
- pen.isNull = false;
- pen.brush = to_d2d_brush(newPen.brush());
+ pen.brush = to_d2d_brush(newPen.brush(), &pen.emulate);
if (!pen.brush)
return;
- pen.width = newPen.widthF();
- pen.color = newPen.color();
- pen.brush->SetOpacity(opacity);
+ pen.brush->SetOpacity(q->state()->opacity);
D2D1_STROKE_STYLE_PROPERTIES1 props = {};
- // Try and match Qt's raster engine in output as closely as possible
- switch (newPen.style()) {
- case Qt::DotLine:
- case Qt::DashDotLine:
- case Qt::DashDotDotLine:
- if (pen.width <= 1.0) {
- props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_FLAT;
- break;
- }
- // fall through
+ switch (newPen.capStyle()) {
+ case Qt::SquareCap:
+ props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_SQUARE;
+ break;
+ case Qt::RoundCap:
+ props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_ROUND;
+ case Qt::FlatCap:
default:
- switch (newPen.capStyle()) {
- case Qt::SquareCap:
- props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_SQUARE;
- break;
- case Qt::RoundCap:
- props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_ROUND;
- case Qt::FlatCap:
- default:
- props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_FLAT;
- break;
- }
+ props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_FLAT;
break;
}
@@ -461,6 +543,15 @@ public:
case Qt::SolidLine:
props.dashStyle = D2D1_DASH_STYLE_SOLID;
break;
+
+ case Qt::DotLine:
+ case Qt::DashDotLine:
+ case Qt::DashDotDotLine:
+ // Try and match Qt's raster engine in output as closely as possible
+ if (newPen.widthF() <= 1.0)
+ props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_FLAT;
+
+ // fall through
default:
props.dashStyle = D2D1_DASH_STYLE_CUSTOM;
break;
@@ -476,20 +567,24 @@ public:
converted[i] = dashes[i];
}
- hr = factory()->CreateStrokeStyle(props, converted.constData(), converted.size(), &(pen.strokeStyle));
+ hr = factory()->CreateStrokeStyle(props, converted.constData(), converted.size(), &pen.strokeStyle);
} else {
- hr = factory()->CreateStrokeStyle(props, NULL, 0, &(pen.strokeStyle));
+ hr = factory()->CreateStrokeStyle(props, NULL, 0, &pen.strokeStyle);
}
if (FAILED(hr))
qWarning("%s: Could not create stroke style: %#x", __FUNCTION__, hr);
}
- ComPtr<ID2D1Brush> to_d2d_brush(const QBrush &newBrush)
+ ComPtr<ID2D1Brush> to_d2d_brush(const QBrush &newBrush, bool *needsEmulation)
{
HRESULT hr;
ComPtr<ID2D1Brush> result;
+ Q_ASSERT(needsEmulation);
+
+ *needsEmulation = false;
+
switch (newBrush.style()) {
case Qt::NoBrush:
break;
@@ -559,6 +654,7 @@ public:
case Qt::LinearGradientPattern:
case Qt::RadialGradientPattern:
case Qt::ConicalGradientPattern:
+ *needsEmulation = true;
break;
case Qt::TexturePattern:
@@ -594,154 +690,36 @@ public:
return result;
}
- void updateHints(QPainter::RenderHints newHints)
+ void updateHints()
{
- renderHints = newHints;
dc()->SetAntialiasMode(antialiasMode());
}
-
- template <typename T>
- void drawLines(const T* lines, int lineCount)
- {
- if (!pen.brush)
- return;
-
- for (int i = 0; i < lineCount; i++) {
- const T &line = lines[i];
-
- // Try to fit Qt's and Direct2D's idea of zero length line
- // handling together nicely.
-
- if (line.p1() == line.p2() && pen.strokeStyle.Get()->GetDashCap() != D2D1_CAP_STYLE_SQUARE) {
- if (pen.width <= 1.0) {
- dc()->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
- // Note that we use pointStrokeStyle here, not the pen's stroke style!
- dc()->DrawLine(to_d2d_point_2f(line.p1()), to_d2d_point_2f(line.p2()), pen.brush.Get(), pen.width, pointStrokeStyle.Get());
- dc()->SetAntialiasMode(antialiasMode());
- }
- } else
- dc()->DrawLine(to_d2d_point_2f(line.p1()), to_d2d_point_2f(line.p2()), pen.brush.Get(), pen.width, pen.strokeStyle.Get());
- }
- }
-
- template <typename T>
- void drawRects(const T* rects, int rectCount)
- {
- if (!brush.brush && !pen.brush)
- return;
-
- for (int i = 0; i < rectCount; i++) {
- if (brush.brush)
- dc()->FillRectangle(to_d2d_rect_f(rects[i]), brush.brush.Get());
-
- // Direct2D for some reason uses different geometry in FillRectangle and DrawRectangle.
- // We have to adjust the rect right and down here by one pixel to paint the rectangle properly.
- if (pen.brush)
- dc()->DrawRectangle(to_d2d_rect_f(rects[i].adjusted(1, 1, 1, 1)), pen.brush.Get(), pen.width, pen.strokeStyle.Get());
- }
- }
-
- template <typename T>
- void drawPolygon(const T* points, int pointCount, QPaintEngine::PolygonDrawMode mode)
- {
- if (pointCount < 3)
- return;
-
- if (!brush.brush && !pen.brush)
- return;
-
- QVector<D2D1_POINT_2F> converted(pointCount);
- for (int i = 0; i < pointCount; i++) {
- const T &p = points[i];
- converted[i].x = p.x();
- converted[i].y = p.y();
- }
-
- drawPolygon(converted.constData(), converted.size(), mode);
- }
-
- void drawPolygon(const D2D1_POINT_2F *points, int pointCount, QPaintEngine::PolygonDrawMode mode)
- {
- ComPtr<ID2D1PathGeometry> geometry;
- ComPtr<ID2D1GeometrySink> sink;
- const bool is_polyline = mode == QPaintEngine::PolylineMode;
-
- HRESULT hr = factory()->CreatePathGeometry(&geometry);
- if (FAILED(hr))
- return;
-
- hr = geometry->Open(&sink);
- if (FAILED(hr))
- return;
-
- switch (mode) {
- case QPaintEngine::OddEvenMode:
- sink->SetFillMode(D2D1_FILL_MODE_ALTERNATE);
- break;
- case QPaintEngine::WindingMode:
- sink->SetFillMode(D2D1_FILL_MODE_WINDING);
- break;
- case QPaintEngine::ConvexMode:
- case QPaintEngine::PolylineMode:
- // XXX
- break;
- }
-
- sink->BeginFigure(points[0], is_polyline ? D2D1_FIGURE_BEGIN_HOLLOW
- : D2D1_FIGURE_BEGIN_FILLED);
- sink->AddLines(points + 1, pointCount - 1);
- sink->EndFigure(is_polyline ? D2D1_FIGURE_END_OPEN
- : D2D1_FIGURE_END_CLOSED);
- sink->Close();
-
- if (brush.brush)
- dc()->FillGeometry(geometry.Get(), brush.brush.Get());
-
- if (pen.brush)
- dc()->DrawGeometry(geometry.Get(), pen.brush.Get(), pen.width, pen.strokeStyle.Get());
- }
};
QWindowsDirect2DPaintEngine::QWindowsDirect2DPaintEngine(QWindowsDirect2DBitmap *bitmap)
- : QPaintEngine(PrimitiveTransform
- | PatternTransform
- | PixmapTransform
- | PatternBrush
- | AlphaBlend
- | PainterPaths
- | Antialiasing
- | BrushStroke
- | ConstantOpacity
- | MaskedBrush
- | ObjectBoundingModeGradients
-
- // Although Direct2D 1.1 contains support for both linear and radial gradients,
- // there unfortunately is no support for repeating or reflecting versions of them
- //| LinearGradientFill
- //| RadialGradientFill
-
- // Unsupported entirely by Direct2D 1.1
- //| ConicalGradientFill
-
- // We might be able to support this using Direct2D effects
- //| PorterDuff
-
- // Direct2D currently only supports affine transforms directly
- // We might be able to support this using Direct2D effects
- //| PerspectiveTransform
-
- // We might be able to support this using Direct2D effects
- //| BlendModes
-
- // We might be able to support this using Direct2D effects
- //| RasterOpModes
-
- // We have to inform Direct2D when we start and end drawing
- //| PaintOutsidePaintEvent
- )
- , d_ptr(new QWindowsDirect2DPaintEnginePrivate(bitmap))
+ : QPaintEngineEx(*(new QWindowsDirect2DPaintEnginePrivate(bitmap)))
{
-
+ QPaintEngine::PaintEngineFeatures unsupported =
+ // As of 1.1 Direct2D gradient support is deficient for linear and radial gradients
+ QPaintEngine::LinearGradientFill
+ | QPaintEngine::RadialGradientFill
+
+ // As of 1.1 Direct2D does not support conical gradients at all
+ | QPaintEngine::ConicalGradientFill
+
+ // As of 1.1 Direct2D does not natively support complex composition modes
+ // However, using Direct2D effects that implement them should be possible
+ | QPaintEngine::PorterDuff
+ | QPaintEngine::BlendModes
+ | QPaintEngine::RasterOpModes
+
+ // As of 1.1 Direct2D does not natively support perspective transforms
+ // However, writing a custom effect that implements them should be possible
+ // The built-in 3D transform effect unfortunately changes output image size, making
+ // it unusable for us.
+ | QPaintEngine::PerspectiveTransform;
+
+ gccaps &= ~unsupported;
}
bool QWindowsDirect2DPaintEngine::begin(QPaintDevice * pdev)
@@ -752,11 +730,10 @@ bool QWindowsDirect2DPaintEngine::begin(QPaintDevice * pdev)
d->dc()->SetTransform(D2D1::Matrix3x2F::Identity());
QRect clip(0, 0, pdev->width(), pdev->height());
- if (!systemClip().isEmpty()) {
+ if (!systemClip().isEmpty())
clip &= systemClip().boundingRect();
- }
- d->dc()->PushAxisAlignedClip(to_d2d_rect_f(clip), d->antialiasMode());
- updateState(*state);
+
+ d->dc()->PushAxisAlignedClip(to_d2d_rect_f(clip), D2D1_ANTIALIAS_MODE_ALIASED);
D2D_TAG(D2DDebugDrawInitialStateTag);
@@ -766,90 +743,149 @@ bool QWindowsDirect2DPaintEngine::begin(QPaintDevice * pdev)
bool QWindowsDirect2DPaintEngine::end()
{
Q_D(QWindowsDirect2DPaintEngine);
+ // First pop any user-applied clipping
d->popClip();
+ // Now the system clip from begin() above
d->dc()->PopAxisAlignedClip();
return d->bitmap->deviceContext()->end();
}
-void QWindowsDirect2DPaintEngine::updateState(const QPaintEngineState &state)
-{
- Q_D(QWindowsDirect2DPaintEngine);
- d->updateState(state, state.state());
-}
-
QPaintEngine::Type QWindowsDirect2DPaintEngine::type() const
{
return QPaintEngine::Direct2D;
}
-void QWindowsDirect2DPaintEngine::drawEllipse(const QRectF &rect)
+void QWindowsDirect2DPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
{
Q_D(QWindowsDirect2DPaintEngine);
- D2D_TAG(D2DDebugDrawEllipseTag);
+ D2D_TAG(D2DDebugFillTag);
- D2D1_ELLIPSE ellipse = {
- to_d2d_point_2f(rect.center()),
- rect.width() / 2,
- rect.height() / 2
- };
+ if (path.isEmpty())
+ return;
+
+ d->updateBrush(brush);
+
+ if (d->brush.emulate) {
+ // We mostly (only?) get here when gradients are required.
+ // We could probably natively support linear and radial gradients that have pad reflect
+
+ QImage img(d->bitmap->size(), QImage::Format_ARGB32);
+ img.fill(Qt::transparent);
+
+ QPainter p;
+ QPaintEngine *engine = img.paintEngine();
+ if (engine->isExtended() && p.begin(&img)) {
+ QPaintEngineEx *extended = static_cast<QPaintEngineEx *>(engine);
+ extended->fill(path, brush);
+ if (!p.end())
+ qWarning("%s: Paint Engine end returned false", __FUNCTION__);
+
+ drawImage(img.rect(), img, img.rect());
+ } else {
+ qWarning("%s: Could not fall back to QImage", __FUNCTION__);
+ }
+
+ return;
+ }
- if (d->brush.brush)
- d->dc()->FillEllipse(ellipse, d->brush.brush.Get());
+ if (!d->brush.brush)
+ return;
- if (d->pen.brush) {
- d->dc()->DrawEllipse(ellipse, d->pen.brush.Get(), d->pen.width, d->pen.strokeStyle.Get());
+ ComPtr<ID2D1Geometry> geometry = vectorPathToID2D1PathGeometry(path, d->antialiasMode() == D2D1_ANTIALIAS_MODE_ALIASED);
+ if (!geometry) {
+ qWarning("%s: Could not convert path to d2d geometry", __FUNCTION__);
+ return;
}
+
+ d->dc()->FillGeometry(geometry.Get(), d->brush.brush.Get());
}
-void QWindowsDirect2DPaintEngine::drawEllipse(const QRect &rect)
+// For clipping we convert everything to painter paths since it allows
+// calculating intersections easily. It might be faster to convert to
+// ID2D1Geometry and use its operations, although that needs to measured.
+// The implementation would be more complex in any case.
+
+void QWindowsDirect2DPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
{
- drawEllipse(QRectF(rect));
+ clip(path.convertToPainterPath(), op);
}
-void QWindowsDirect2DPaintEngine::drawImage(const QRectF &rectangle, const QImage &image,
- const QRectF &sr, Qt::ImageConversionFlags flags)
+void QWindowsDirect2DPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
+{
+ QPainterPath p;
+ p.addRect(rect);
+ clip(p, op);
+}
+
+void QWindowsDirect2DPaintEngine::clip(const QRegion &region, Qt::ClipOperation op)
+{
+ QPainterPath p;
+ p.addRegion(region);
+ clip(p, op);
+}
+
+void QWindowsDirect2DPaintEngine::clip(const QPainterPath &path, Qt::ClipOperation op)
{
Q_D(QWindowsDirect2DPaintEngine);
- D2D_TAG(D2DDebugDrawImageTag);
+ d->updateClipPath(path, op);
+}
- QPixmap pixmap = QPixmap::fromImage(image, flags);
- drawPixmap(rectangle, pixmap, sr);
+void QWindowsDirect2DPaintEngine::clipEnabledChanged()
+{
+ Q_D(QWindowsDirect2DPaintEngine);
+ d->updateClipEnabled();
}
-void QWindowsDirect2DPaintEngine::drawLines(const QLineF *lines, int lineCount)
+void QWindowsDirect2DPaintEngine::penChanged()
{
Q_D(QWindowsDirect2DPaintEngine);
- D2D_TAG(D2DDebugDrawLinesTag);
- d->drawLines(lines, lineCount);
+ d->updatePen();
}
-void QWindowsDirect2DPaintEngine::drawLines(const QLine *lines, int lineCount)
+void QWindowsDirect2DPaintEngine::brushChanged()
{
Q_D(QWindowsDirect2DPaintEngine);
- D2D_TAG(D2DDebugDrawLinesTag);
- d->drawLines(lines, lineCount);
+ d->updateBrush(state()->brush);
}
-void QWindowsDirect2DPaintEngine::drawPath(const QPainterPath &path)
+void QWindowsDirect2DPaintEngine::brushOriginChanged()
{
Q_D(QWindowsDirect2DPaintEngine);
- D2D_TAG(D2DDebugDrawPathTag);
+ d->updateBrushOrigin();
+}
- if (path.elementCount() == 0)
- return;
+void QWindowsDirect2DPaintEngine::opacityChanged()
+{
+ Q_D(QWindowsDirect2DPaintEngine);
+ d->updateOpacity();
+}
- if (!d->brush.brush && !d->pen.brush)
- return;
+void QWindowsDirect2DPaintEngine::compositionModeChanged()
+{
+ Q_D(QWindowsDirect2DPaintEngine);
+ d->updateCompositionMode();
+}
- ComPtr<ID2D1PathGeometry> geometry = painterPathToPathGeometry(path);
- if (!geometry)
- return;
+void QWindowsDirect2DPaintEngine::renderHintsChanged()
+{
+ Q_D(QWindowsDirect2DPaintEngine);
+ d->updateHints();
+}
+
+void QWindowsDirect2DPaintEngine::transformChanged()
+{
+ Q_D(QWindowsDirect2DPaintEngine);
+ d->updateTransform();
+}
- if (d->brush.brush)
- d->dc()->FillGeometry(geometry.Get(), d->brush.brush.Get());
+void QWindowsDirect2DPaintEngine::drawImage(const QRectF &rectangle, const QImage &image,
+ const QRectF &sr, Qt::ImageConversionFlags flags)
+{
+ Q_D(QWindowsDirect2DPaintEngine);
+ D2D_TAG(D2DDebugDrawImageTag);
- if (d->pen.brush)
- d->dc()->DrawGeometry(geometry.Get(), d->pen.brush.Get(), d->pen.width, d->pen.strokeStyle.Get());
+ QPixmap pixmap = QPixmap::fromImage(image, flags);
+ drawPixmap(rectangle, pixmap, sr);
}
void QWindowsDirect2DPaintEngine::drawPixmap(const QRectF &r,
@@ -865,7 +901,7 @@ void QWindowsDirect2DPaintEngine::drawPixmap(const QRectF &r,
if (pm.handle()->pixelType() == QPlatformPixmap::BitmapType) {
QImage i = pm.toImage();
i.setColor(0, qRgba(0, 0, 0, 0));
- i.setColor(1, d->pen.color.rgba());
+ i.setColor(1, d->pen.qpen.color().rgba());
drawImage(r, i, sr);
return;
}
@@ -877,12 +913,12 @@ void QWindowsDirect2DPaintEngine::drawPixmap(const QRectF &r,
// Good, src bitmap != dst bitmap
if (sr.isValid())
d->dc()->DrawBitmap(bitmap->bitmap(),
- to_d2d_rect_f(r), d->opacity,
+ to_d2d_rect_f(r), state()->opacity,
d->interpolationMode(),
to_d2d_rect_f(sr));
else
d->dc()->DrawBitmap(bitmap->bitmap(),
- to_d2d_rect_f(r), d->opacity,
+ to_d2d_rect_f(r), state()->opacity,
d->interpolationMode());
} else {
// Ok, so the source pixmap and destination pixmap is the same.
@@ -893,7 +929,7 @@ void QWindowsDirect2DPaintEngine::drawPixmap(const QRectF &r,
if (sr.isValid()) {
bool r = intermediate.resize(sr.width(), sr.height());
if (!r) {
- qDebug("%s: Could not resize intermediate bitmap to source rect size", __FUNCTION__);
+ qWarning("%s: Could not resize intermediate bitmap to source rect size", __FUNCTION__);
return;
}
@@ -909,7 +945,7 @@ void QWindowsDirect2DPaintEngine::drawPixmap(const QRectF &r,
bool r = intermediate.resize(bitmap->size().width(),
bitmap->size().height());
if (!r) {
- qDebug("%s: Could not resize intermediate bitmap to source bitmap size", __FUNCTION__);
+ qWarning("%s: Could not resize intermediate bitmap to source bitmap size", __FUNCTION__);
return;
}
@@ -923,120 +959,121 @@ void QWindowsDirect2DPaintEngine::drawPixmap(const QRectF &r,
}
d->dc()->DrawBitmap(intermediate.bitmap(),
- to_d2d_rect_f(r), d->opacity,
+ to_d2d_rect_f(r), state()->opacity,
d->interpolationMode());
}
}
-void QWindowsDirect2DPaintEngine::drawPolygon(const QPointF *points,
- int pointCount,
- QPaintEngine::PolygonDrawMode mode)
+static ComPtr<IDWriteFontFace> fontFaceFromFontEngine(QFontEngine *fe)
{
- Q_D(QWindowsDirect2DPaintEngine);
- D2D_TAG(D2DDebugDrawPolygonTag);
- d->drawPolygon(points, pointCount, mode);
-}
+ ComPtr<IDWriteFontFace> fontFace;
-void QWindowsDirect2DPaintEngine::drawPolygon(const QPoint *points,
- int pointCount,
- QPaintEngine::PolygonDrawMode mode)
-{
- Q_D(QWindowsDirect2DPaintEngine);
- D2D_TAG(D2DDebugDrawPolygonTag);
- d->drawPolygon(points, pointCount, mode);
-}
+ switch (fe->type()) {
+ case QFontEngine::Win:
+ {
+ QWindowsFontEngine *wfe = static_cast<QWindowsFontEngine *>(fe);
+ QSharedPointer<QWindowsFontEngineData> wfed = wfe->fontEngineData();
-void QWindowsDirect2DPaintEngine::drawRects(const QRectF *rects, int rectCount)
-{
- Q_D(QWindowsDirect2DPaintEngine);
- D2D_TAG(D2DDebugDrawRectsTag);
- d->drawRects(rects, rectCount);
-}
+ HGDIOBJ oldfont = wfe->selectDesignFont();
+ HRESULT hr = QWindowsDirect2DContext::instance()->dwriteGdiInterop()->CreateFontFaceFromHdc(wfed->hdc, &fontFace);
+ DeleteObject(SelectObject(wfed->hdc, oldfont));
+ if (FAILED(hr))
+ qWarning("%s: Could not create DirectWrite fontface from HDC: %#x", __FUNCTION__, hr);
-void QWindowsDirect2DPaintEngine::drawRects(const QRect *rects, int rectCount)
-{
- Q_D(QWindowsDirect2DPaintEngine);
- D2D_TAG(D2DDebugDrawRectsTag);
- d->drawRects(rects, rectCount);
-}
+ }
+ break;
-// Points (1/72 inches) to Microsoft's Device Independent Pixels (1/96 inches)
-inline static Q_DECL_CONSTEXPR FLOAT pointSizeToDIP(qreal pointSize)
-{
- return pointSize + (pointSize / qreal(3.0));
-}
+#ifndef QT_NO_DIRECTWRITE
-inline static FLOAT pixelSizeToDIP(int pixelSize)
-{
- FLOAT dpiX, dpiY;
- factory()->GetDesktopDpi(&dpiX, &dpiY);
+ case QFontEngine::DirectWrite:
+ {
+ QWindowsFontEngineDirectWrite *wfedw = static_cast<QWindowsFontEngineDirectWrite *>(fe);
+ fontFace = wfedw->directWriteFontFace();
+ }
+ break;
- return FLOAT(pixelSize) / (dpiY / 96.0f);
-}
+#endif // QT_NO_DIRECTWRITE
-inline static FLOAT fontSizeInDIP(const QFont &font)
-{
- // Microsoft wants the font size in DIPs (Device Independent Pixels), each of which is 1/96 inches.
- if (font.pixelSize() == -1) {
- // font size was set as points
- return pointSizeToDIP(font.pointSizeF());
- } else {
- // font size was set as pixels
- return pixelSizeToDIP(font.pixelSize());
+ default:
+ qWarning("%s: Unknown font engine!", __FUNCTION__);
+ break;
}
+
+ return fontFace;
}
-void QWindowsDirect2DPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+void QWindowsDirect2DPaintEngine::drawStaticTextItem(QStaticTextItem *staticTextItem)
{
Q_D(QWindowsDirect2DPaintEngine);
- D2D_TAG(D2DDebugDrawTextItemTag);
+ D2D_TAG(D2DDebugDrawStaticTextItemTag);
- if (d->pen.isNull)
+ if (qpen_style(d->pen.qpen) == Qt::NoPen)
+ return;
+
+ if (staticTextItem->numGlyphs == 0)
return;
// If we can't support the current configuration with Direct2D, fall back to slow path
// Most common cases are perspective transform and gradient brush as pen
- if (d->hasPerspectiveTransform || !d->pen.brush) {
- QPaintEngine::drawTextItem(p, textItem);
+ if ((state()->transform().isAffine() == false) || d->pen.emulate) {
+ QPaintEngineEx::drawStaticTextItem(staticTextItem);
return;
}
- const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
- ComPtr<IDWriteFontFace> fontFace;
+ ComPtr<IDWriteFontFace> fontFace = fontFaceFromFontEngine(staticTextItem->fontEngine());
+ if (!fontFace) {
+ qWarning("%s: Could not find font - falling back to slow text rendering path.", __FUNCTION__);
+ QPaintEngineEx::drawStaticTextItem(staticTextItem);
+ return;
+ }
- switch (ti.fontEngine->type()) {
- case QFontEngine::Win:
- {
- QWindowsFontEngine *wfe = static_cast<QWindowsFontEngine *>(ti.fontEngine);
- QSharedPointer<QWindowsFontEngineData> wfed = wfe->fontEngineData();
+ QVector<UINT16> glyphIndices(staticTextItem->numGlyphs);
+ QVector<FLOAT> glyphAdvances(staticTextItem->numGlyphs);
+ QVector<DWRITE_GLYPH_OFFSET> glyphOffsets(staticTextItem->numGlyphs);
- HGDIOBJ oldfont = wfe->selectDesignFont();
- HRESULT hr = QWindowsDirect2DContext::instance()->dwriteGdiInterop()->CreateFontFaceFromHdc(wfed->hdc, &fontFace);
- DeleteObject(SelectObject(wfed->hdc, oldfont));
- if (FAILED(hr))
- qWarning("%s: Could not create DirectWrite fontface from HDC: %#x", __FUNCTION__, hr);
+ // XXX Are we generating a lot of cache misses here?
+ for (int i = 0; i < staticTextItem->numGlyphs; i++) {
+ glyphIndices[i] = UINT16(staticTextItem->glyphs[i]); // Imperfect conversion here
+ // This looks a little funky because the positions are precalculated
+ glyphAdvances[i] = 0;
+ glyphOffsets[i].advanceOffset = staticTextItem->glyphPositions[i].x.toReal();
+ // Qt and Direct2D seem to disagree on the direction of the ascender offset...
+ glyphOffsets[i].ascenderOffset = staticTextItem->glyphPositions[i].y.toReal() * -1;
}
- break;
-#ifndef QT_NO_DIRECTWRITE
+ drawGlyphRun(D2D1::Point2F(0, 0),
+ fontFace.Get(),
+ staticTextItem->font,
+ staticTextItem->numGlyphs,
+ glyphIndices.constData(),
+ glyphAdvances.constData(),
+ glyphOffsets.constData(),
+ false);
+}
- case QFontEngine::DirectWrite:
- {
- QWindowsFontEngineDirectWrite *wfedw = static_cast<QWindowsFontEngineDirectWrite *>(ti.fontEngine);
- fontFace = wfedw->directWriteFontFace();
- }
- break;
+void QWindowsDirect2DPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ Q_D(QWindowsDirect2DPaintEngine);
+ D2D_TAG(D2DDebugDrawTextItemTag);
-#endif // QT_NO_DIRECTWRITE
+ if (qpen_style(d->pen.qpen) == Qt::NoPen)
+ return;
- default:
- qDebug("%s: Unknown font engine!", __FUNCTION__);
- break;
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+ if (ti.glyphs.numGlyphs == 0)
+ return;
+
+ // If we can't support the current configuration with Direct2D, fall back to slow path
+ // Most common cases are perspective transform and gradient brush as pen
+ if ((state()->transform().isAffine() == false) || d->pen.emulate) {
+ QPaintEngine::drawTextItem(p, textItem);
+ return;
}
+ ComPtr<IDWriteFontFace> fontFace = fontFaceFromFontEngine(ti.fontEngine);
if (!fontFace) {
- qDebug("%s: Could not find font - falling back to slow text rendering path.", __FUNCTION__);
+ qWarning("%s: Could not find font - falling back to slow text rendering path.", __FUNCTION__);
QPaintEngine::drawTextItem(p, textItem);
return;
}
@@ -1045,72 +1082,87 @@ void QWindowsDirect2DPaintEngine::drawTextItem(const QPointF &p, const QTextItem
QVector<FLOAT> glyphAdvances(ti.glyphs.numGlyphs);
QVector<DWRITE_GLYPH_OFFSET> glyphOffsets(ti.glyphs.numGlyphs);
- // Imperfect conversion here
+ // XXX Are we generating a lot of cache misses here?
for (int i = 0; i < ti.glyphs.numGlyphs; i++) {
- glyphIndices[i] = UINT16(ti.glyphs.glyphs[i]);
+ glyphIndices[i] = UINT16(ti.glyphs.glyphs[i]); // Imperfect conversion here
glyphAdvances[i] = ti.glyphs.effectiveAdvance(i).toReal();
glyphOffsets[i].advanceOffset = ti.glyphs.offsets[i].x.toReal();
+
+ // XXX Should we negate the y value like for static text items?
glyphOffsets[i].ascenderOffset = ti.glyphs.offsets[i].y.toReal();
}
const bool rtl = (ti.flags & QTextItem::RightToLeft);
- const UINT32 bidiLevel = rtl ? 1 : 0;
+ const QPointF offset(rtl ? ti.width.toReal() : 0, 0);
- DWRITE_GLYPH_RUN glyphRun = {
- fontFace.Get(), // IDWriteFontFace *fontFace;
- fontSizeInDIP(ti.font()), // FLOAT fontEmSize;
- ti.glyphs.numGlyphs, // UINT32 glyphCount;
- glyphIndices.constData(), // const UINT16 *glyphIndices;
- glyphAdvances.constData(), // const FLOAT *glyphAdvances;
- glyphOffsets.constData(), // const DWRITE_GLYPH_OFFSET *glyphOffsets;
- FALSE, // BOOL isSideways;
- bidiLevel // UINT32 bidiLevel;
- };
+ drawGlyphRun(to_d2d_point_2f(p + offset),
+ fontFace.Get(),
+ ti.font(),
+ ti.glyphs.numGlyphs,
+ glyphIndices.constData(),
+ glyphAdvances.constData(),
+ glyphOffsets.constData(),
+ rtl);
+}
- const bool antiAlias = bool((d->renderHints & QPainter::TextAntialiasing)
- && !(ti.font().styleStrategy() & QFont::NoAntialias));
- d->dc()->SetTextAntialiasMode(antiAlias ? D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
- : D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
+// Points (1/72 inches) to Microsoft's Device Independent Pixels (1/96 inches)
+inline static Q_DECL_CONSTEXPR FLOAT pointSizeToDIP(qreal pointSize)
+{
+ return pointSize + (pointSize / qreal(3.0));
+}
- const QPointF offset(rtl ? ti.width.toReal() : 0, 0);
- d->dc()->DrawGlyphRun(to_d2d_point_2f(p + offset),
- &glyphRun,
- NULL,
- d->pen.brush.Get(),
- DWRITE_MEASURING_MODE_GDI_CLASSIC);
+inline static FLOAT pixelSizeToDIP(int pixelSize)
+{
+ FLOAT dpiX, dpiY;
+ factory()->GetDesktopDpi(&dpiX, &dpiY);
+
+ return FLOAT(pixelSize) / (dpiY / 96.0f);
}
-static void qt_draw_tile(QPaintEngine *gc, qreal x, qreal y, qreal w, qreal h,
- const QPixmap &pixmap, qreal xOffset, qreal yOffset)
+inline static FLOAT fontSizeInDIP(const QFont &font)
{
- qreal yPos, xPos, drawH, drawW, yOff, xOff;
- yPos = y;
- yOff = yOffset;
- while (yPos < y + h) {
- drawH = pixmap.height() - yOff; // Cropping first row
- if (yPos + drawH > y + h) // Cropping last row
- drawH = y + h - yPos;
- xPos = x;
- xOff = xOffset;
- while (xPos < x + w) {
- drawW = pixmap.width() - xOff; // Cropping first column
- if (xPos + drawW > x + w) // Cropping last column
- drawW = x + w - xPos;
- if (drawW > 0 && drawH > 0)
- gc->drawPixmap(QRectF(xPos, yPos, drawW, drawH), pixmap, QRectF(xOff, yOff, drawW, drawH));
- xPos += drawW;
- xOff = 0;
- }
- yPos += drawH;
- yOff = 0;
+ // Direct2d wants the font size in DIPs (Device Independent Pixels), each of which is 1/96 inches.
+ if (font.pixelSize() == -1) {
+ // font size was set as points
+ return pointSizeToDIP(font.pointSizeF());
+ } else {
+ // font size was set as pixels
+ return pixelSizeToDIP(font.pixelSize());
}
}
-void QWindowsDirect2DPaintEngine::drawTiledPixmap(const QRectF &rect, const QPixmap &pixmap, const QPointF &p)
+void QWindowsDirect2DPaintEngine::drawGlyphRun(const D2D1_POINT_2F &pos,
+ IDWriteFontFace *fontFace,
+ const QFont &font,
+ int numGlyphs,
+ const UINT16 *glyphIndices,
+ const FLOAT *glyphAdvances,
+ const DWRITE_GLYPH_OFFSET *glyphOffsets,
+ bool rtl)
{
Q_D(QWindowsDirect2DPaintEngine);
- D2D_TAG(D2DDebugDrawTextItemTag);
- qt_draw_tile(this, rect.x(), rect.y(), rect.width(), rect.height(), pixmap, p.x(), p.y());
+
+ DWRITE_GLYPH_RUN glyphRun = {
+ fontFace, // IDWriteFontFace *fontFace;
+ fontSizeInDIP(font), // FLOAT fontEmSize;
+ numGlyphs, // UINT32 glyphCount;
+ glyphIndices, // const UINT16 *glyphIndices;
+ glyphAdvances, // const FLOAT *glyphAdvances;
+ glyphOffsets, // const DWRITE_GLYPH_OFFSET *glyphOffsets;
+ FALSE, // BOOL isSideways;
+ rtl ? 1 : 0 // UINT32 bidiLevel;
+ };
+
+ const bool antiAlias = bool((state()->renderHints & QPainter::TextAntialiasing)
+ && !(font.styleStrategy() & QFont::NoAntialias));
+ d->dc()->SetTextAntialiasMode(antiAlias ? D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
+ : D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
+
+ d->dc()->DrawGlyphRun(pos,
+ &glyphRun,
+ NULL,
+ d->pen.brush.Get(),
+ DWRITE_MEASURING_MODE_GDI_CLASSIC);
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h
index 554380cb1f..6c74a07e88 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h
@@ -43,14 +43,17 @@
#define QWINDOWSDIRECT2DPAINTENGINE_H
#include <QtCore/QScopedPointer>
-#include <QtGui/QPaintEngine>
+#include <QtGui/private/qpaintengineex_p.h>
+
+#include <d2d1_1.h>
+#include <dwrite_1.h>
QT_BEGIN_NAMESPACE
class QWindowsDirect2DPaintEnginePrivate;
class QWindowsDirect2DBitmap;
-class QWindowsDirect2DPaintEngine : public QPaintEngine
+class QWindowsDirect2DPaintEngine : public QPaintEngineEx
{
Q_DECLARE_PRIVATE(QWindowsDirect2DPaintEngine)
@@ -60,26 +63,34 @@ public:
bool begin(QPaintDevice *pdev) Q_DECL_OVERRIDE;
bool end() Q_DECL_OVERRIDE;
- void updateState(const QPaintEngineState &state) Q_DECL_OVERRIDE;
-
Type type() const Q_DECL_OVERRIDE;
- void drawEllipse(const QRectF &rect) Q_DECL_OVERRIDE;
- void drawEllipse(const QRect &rect) Q_DECL_OVERRIDE;
+ void fill(const QVectorPath &path, const QBrush &brush) Q_DECL_OVERRIDE;
+
+ void clip(const QVectorPath &path, Qt::ClipOperation op) Q_DECL_OVERRIDE;
+ void clip(const QRect &rect, Qt::ClipOperation op) Q_DECL_OVERRIDE;
+ void clip(const QRegion &region, Qt::ClipOperation op) Q_DECL_OVERRIDE;
+ void clip(const QPainterPath &path, Qt::ClipOperation op) Q_DECL_OVERRIDE;
+
+ void clipEnabledChanged() Q_DECL_OVERRIDE;
+ void penChanged() Q_DECL_OVERRIDE;
+ void brushChanged() Q_DECL_OVERRIDE;
+ void brushOriginChanged() Q_DECL_OVERRIDE;
+ void opacityChanged() Q_DECL_OVERRIDE;
+ void compositionModeChanged() Q_DECL_OVERRIDE;
+ void renderHintsChanged() Q_DECL_OVERRIDE;
+ void transformChanged() Q_DECL_OVERRIDE;
+
void drawImage(const QRectF &rectangle, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags flags = Qt::AutoColor) Q_DECL_OVERRIDE;
- void drawLines(const QLineF *lines, int lineCount) Q_DECL_OVERRIDE;
- void drawLines(const QLine *lines, int lineCount) Q_DECL_OVERRIDE;
- void drawPath(const QPainterPath &path) Q_DECL_OVERRIDE;
void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) Q_DECL_OVERRIDE;
- void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) Q_DECL_OVERRIDE;
- void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode) Q_DECL_OVERRIDE;
- void drawRects(const QRectF *rects, int rectCount) Q_DECL_OVERRIDE;
- void drawRects(const QRect *rects, int rectCount) Q_DECL_OVERRIDE;
+
+ void drawStaticTextItem(QStaticTextItem *staticTextItem) Q_DECL_OVERRIDE;
void drawTextItem(const QPointF &p, const QTextItem &textItem) Q_DECL_OVERRIDE;
- void drawTiledPixmap(const QRectF &rect, const QPixmap &pixmap, const QPointF &p) Q_DECL_OVERRIDE;
private:
- QScopedPointer<QWindowsDirect2DPaintEnginePrivate> d_ptr;
+ void drawGlyphRun(const D2D1_POINT_2F &pos, IDWriteFontFace *fontFace, const QFont &font,
+ int numGlyphs, const UINT16 *glyphIndices, const FLOAT *glyphAdvances,
+ const DWRITE_GLYPH_OFFSET *glyphOffsets, bool rtl);
};
QT_END_NAMESPACE