diff options
Diffstat (limited to 'src/plugins/platforms/direct2d')
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 ®ion) +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 ®ion, 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 ®ion, 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 ®ion, 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 |