13#include <QtGui/private/qwindowsfontdatabase_p.h>
16#include <QtCore/qmath.h>
17#include <QtCore/qstack.h>
18#include <QtCore/qsettings.h>
19#include <QtCore/private/qcomptr_p.h>
20#include <QtGui/private/qpaintengine_p.h>
21#include <QtGui/private/qtextengine_p.h>
22#include <QtGui/private/qfontengine_p.h>
23#include <QtGui/private/qstatictext_p.h>
66#define D2D_TAG(tag) d->dc()->SetTags(tag, tag)
77 const qreal halfWidth = penWidth / 2;
78 const qreal angle = -qDegreesToRadians(line.angle());
79 const qreal sinA = qSin(angle);
80 const qreal cosA = qCos(angle);
81 QTransform transform = QTransform::fromTranslate(line.p1().x() + dashOffset * cosA + sinA * halfWidth,
82 line.p1().y() + dashOffset * sinA - cosA * halfWidth);
83 transform.rotateRadians(angle);
84 return to_d2d_matrix_3x2_f(transform);
87static void adjustLine(QPointF *p1, QPointF *p2);
92 QList<D2D1_GRADIENT_STOP> stops(qstops.count());
93 for (
int i = 0, count = stops.size(); i < count; ++i) {
94 stops[i].position = FLOAT(qstops.at(i).first);
95 stops[i].color = to_d2d_color_f(qstops.at(i).second);
105 HRESULT hr =
factory()->CreatePathGeometry(&m_geometry);
107 qWarning(
"%s: Could not create path geometry: %#lx",
__FUNCTION__, hr);
111 hr = m_geometry->Open(&m_sink);
113 qWarning(
"%s: Could not create geometry sink: %#lx",
__FUNCTION__, hr);
123 m_sink->SetFillMode(D2D1_FILL_MODE_WINDING);
125 m_sink->SetFillMode(D2D1_FILL_MODE_ALTERNATE);
130 m_roundCoordinates = enable;
135 m_adjustPositivelySlopedLines = enable;
146 m_sink->EndFigure(D2D1_FIGURE_END_OPEN);
148 m_sink->BeginFigure(adjusted(point), D2D1_FIGURE_BEGIN_FILLED);
150 m_previousPoint = point;
156 if (m_adjustPositivelySlopedLines && isLinePositivelySloped(m_previousPoint, point)) {
157 moveTo(m_previousPoint - QPointF(0, 1));
160 m_sink->AddLine(adjusted(pt));
163 m_previousPoint = point;
166 void curveTo(
const QPointF &p1,
const QPointF &p2,
const QPointF &p3)
168 D2D1_BEZIER_SEGMENT segment = {
174 m_sink->AddBezier(segment);
175 m_previousPoint = p3;
181 m_sink->EndFigure(D2D1_FIGURE_END_OPEN);
192 D2D1_POINT_2F adjusted(
const QPointF &point)
195 MAGICAL_ALIASING_OFFSET);
197 if (m_roundCoordinates)
198 return to_d2d_point_2f(point + adjustment);
200 return to_d2d_point_2f(point);
203 ComPtr<ID2D1PathGeometry1> m_geometry;
204 ComPtr<ID2D1GeometrySink> m_sink;
206 bool m_inFigure =
false;
207 bool m_roundCoordinates =
false;
208 bool m_adjustPositivelySlopedLines =
false;
209 QPointF m_previousPoint;
225 Q_DECLARE_PUBLIC(QWindowsDirect2DPaintEngine)
256 inline void reset() {
269 ComPtr<ID2D1Brush> brush;
271 inline void reset() {
278 inline ID2D1DeviceContext *
dc()
const
281 return bitmap->deviceContext()->get();
286 Q_Q(
const QWindowsDirect2DPaintEngine);
287 return (q->state()->renderHints & QPainter::SmoothPixmapTransform) ? D2D1_INTERPOLATION_MODE_LINEAR
288 : D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
293 Q_Q(
const QWindowsDirect2DPaintEngine);
294 return (q->state()->renderHints & QPainter::Antialiasing) ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
295 : D2D1_ANTIALIAS_MODE_ALIASED;
300 if (flags & QWindowsDirect2DPaintEngine::TranslucentTopLevelWindow)
301 return D2D1_LAYER_OPTIONS1_NONE;
303 return D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
308 dc()->SetTransform(to_d2d_matrix_3x2_f(transform));
314 brush.brush->SetOpacity(FLOAT(opacity));
316 pen.brush->SetOpacity(FLOAT(opacity));
321 Q_Q(QWindowsDirect2DPaintEngine);
323 if (path.isEmpty()) {
324 D2D_RECT_F rect = {0, 0, 0, 0};
325 dc()->PushAxisAlignedClip(rect, antialiasMode());
326 pushedClips.push(AxisAlignedClip);
327 }
else if (path.isRect() && (q->state()->matrix.type() <= QTransform::TxScale)) {
328 const qreal *
const points = path.points();
336 dc()->PushAxisAlignedClip(rect, antialiasMode());
337 pushedClips.push(AxisAlignedClip);
339 ComPtr<ID2D1PathGeometry1> geometry = vectorPathToID2D1PathGeometry(path);
341 qWarning(
"%s: Could not convert vector path to painter path!",
__FUNCTION__);
345 dc()->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(),
348 D2D1::IdentityMatrix(),
353 pushedClips.push(LayerClip);
359 while (!pushedClips.isEmpty()) {
360 switch (pushedClips.pop()) {
362 dc()->PopAxisAlignedClip();
375 else if (pushedClips.isEmpty())
376 replayClipOperations();
379 void clip(
const QVectorPath &path, Qt::ClipOperation operation)
385 case Qt::ReplaceClip:
389 case Qt::IntersectClip:
398 case QPainter::CompositionMode_Source:
399 dc()->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
401 case QPainter::CompositionMode_SourceOver:
402 dc()->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
408 dc()->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
409 flags |= QWindowsDirect2DPaintEngine::EmulateComposition;
416 Q_Q(
const QWindowsDirect2DPaintEngine);
418 if (qbrush_fast_equals(brush.qbrush, newBrush) && (brush.brush || brush.emulate))
421 brush.brush = to_d2d_brush(newBrush, &brush.emulate);
422 brush.qbrush = newBrush;
425 brush.brush->SetOpacity(FLOAT(q->state()->opacity));
426 applyBrushOrigin(currentBrushOrigin);
433 applyBrushOrigin(brushOrigin);
438 if (brush.brush && !currentBrushOrigin.isNull()) {
439 D2D1_MATRIX_3X2_F transform;
440 brush.brush->GetTransform(&transform);
442 brush.brush->SetTransform(*(D2D1::Matrix3x2F::ReinterpretBaseType(&transform))
443 * D2D1::Matrix3x2F::Translation(FLOAT(-currentBrushOrigin.x()),
444 FLOAT(-currentBrushOrigin.y())));
450 if (brush.brush && !origin.isNull()) {
451 D2D1_MATRIX_3X2_F transform;
452 brush.brush->GetTransform(&transform);
454 brush.brush->SetTransform(*(D2D1::Matrix3x2F::ReinterpretBaseType(&transform))
455 * D2D1::Matrix3x2F::Translation(FLOAT(origin.x()), FLOAT(origin.y())));
458 currentBrushOrigin = origin;
463 Q_Q(
const QWindowsDirect2DPaintEngine);
464 if (qpen_fast_equals(newPen, pen.qpen) && (pen.brush || pen.emulate))
470 if (newPen.style() == Qt::NoPen)
473 pen.brush = to_d2d_brush(newPen.brush(), &pen.emulate);
477 pen.brush->SetOpacity(FLOAT(q->state()->opacity));
479 D2D1_STROKE_STYLE_PROPERTIES1 props = {};
481 switch (newPen.capStyle()) {
483 props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_SQUARE;
486 props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_ROUND;
490 props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_FLAT;
494 switch (newPen.joinStyle()) {
496 props.lineJoin = D2D1_LINE_JOIN_BEVEL;
499 props.lineJoin = D2D1_LINE_JOIN_ROUND;
503 props.lineJoin = D2D1_LINE_JOIN_MITER;
507 props.miterLimit = FLOAT(newPen.miterLimit() * qreal(2.0));
508 props.dashOffset = FLOAT(newPen.dashOffset());
510 if (newPen.widthF() == 0)
511 props.transformType = D2D1_STROKE_TRANSFORM_TYPE_HAIRLINE;
512 else if (newPen.isCosmetic())
513 props.transformType = D2D1_STROKE_TRANSFORM_TYPE_FIXED;
515 props.transformType = D2D1_STROKE_TRANSFORM_TYPE_NORMAL;
517 switch (newPen.style()) {
519 props.dashStyle = D2D1_DASH_STYLE_SOLID;
523 case Qt::DashDotLine:
524 case Qt::DashDotDotLine:
526 if (newPen.widthF() <= 1.0)
527 props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_FLAT;
531 props.dashStyle = D2D1_DASH_STYLE_CUSTOM;
537 if (props.dashStyle == D2D1_DASH_STYLE_CUSTOM) {
538 QList<qreal> dashes = newPen.dashPattern();
539 QList<FLOAT> converted(dashes.size());
540 qreal penWidth = pen.qpen.widthF();
541 qreal brushWidth = 0;
542 for (
int i = 0; i < dashes.size(); i++) {
543 converted[i] = FLOAT(dashes[i]);
544 brushWidth += penWidth * dashes[i];
547 hr =
factory()->CreateStrokeStyle(props, converted.constData(), UINT32(converted.size()), &pen.strokeStyle);
551 bitmap
.resize(int(ceil(brushWidth))
, int(ceil(penWidth))
);
556 const qreal offsetX = (qreal(bitmap
.size().width()) - brushWidth) / 2;
557 const qreal offsetY = qreal(bitmap
.size().height()) / 2;
559 D2D1::Point2F(FLOAT(brushWidth), FLOAT(offsetY)),
560 pen.brush.Get(), FLOAT(penWidth), pen.strokeStyle.Get());
562 D2D1_BITMAP_BRUSH_PROPERTIES1 bitmapBrushProperties = D2D1::BitmapBrushProperties1(
563 D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_CLAMP, D2D1_INTERPOLATION_MODE_LINEAR);
564 hr =
dc()->CreateBitmapBrush(bitmap
.bitmap(), bitmapBrushProperties, &pen.dashBrush);
565 pen.dashLength = bitmap.size().width();
567 hr =
factory()->CreateStrokeStyle(props,
nullptr, 0, &pen.strokeStyle);
571 qWarning(
"%s: Could not create stroke style: %#lx",
__FUNCTION__, hr);
577 ComPtr<ID2D1Brush> result;
579 Q_ASSERT(needsEmulation);
581 *needsEmulation =
false;
583 switch (newBrush.style()) {
587 case Qt::SolidPattern:
589 ComPtr<ID2D1SolidColorBrush> solid;
591 hr =
dc()->CreateSolidColorBrush(to_d2d_color_f(newBrush.color()), &solid);
593 qWarning(
"%s: Could not create solid color brush: %#lx",
__FUNCTION__, hr);
597 hr = solid.As(&result);
599 qWarning(
"%s: Could not convert solid color brush: %#lx",
__FUNCTION__, hr);
603 case Qt::Dense1Pattern:
604 case Qt::Dense2Pattern:
605 case Qt::Dense3Pattern:
606 case Qt::Dense4Pattern:
607 case Qt::Dense5Pattern:
608 case Qt::Dense6Pattern:
609 case Qt::Dense7Pattern:
612 case Qt::CrossPattern:
613 case Qt::BDiagPattern:
614 case Qt::FDiagPattern:
615 case Qt::DiagCrossPattern:
617 ComPtr<ID2D1BitmapBrush1> bitmapBrush;
618 D2D1_BITMAP_BRUSH_PROPERTIES1 bitmapBrushProperties = {
619 D2D1_EXTEND_MODE_WRAP,
620 D2D1_EXTEND_MODE_WRAP,
624 QImage brushImg = qt_imageForBrush(newBrush.style(),
false);
625 brushImg.setColor(0, newBrush.color().rgba());
626 brushImg.setColor(1, qRgba(0, 0, 0, 0));
629 bool success = bitmap.fromImage(brushImg, Qt::AutoColor);
631 qWarning(
"%s: Could not create Direct2D bitmap from Qt pattern brush image",
__FUNCTION__);
636 bitmapBrushProperties,
639 qWarning(
"%s: Could not create Direct2D bitmap brush for Qt pattern brush: %#lx",
__FUNCTION__, hr);
643 hr = bitmapBrush.As(&result);
645 qWarning(
"%s: Could not convert Direct2D bitmap brush for Qt pattern brush: %#lx",
__FUNCTION__, hr);
649 case Qt::LinearGradientPattern:
650 if (newBrush.gradient()->spread() != QGradient::PadSpread) {
651 *needsEmulation =
true;
653 ComPtr<ID2D1LinearGradientBrush> linear;
654 const auto *qlinear =
static_cast<
const QLinearGradient *>(newBrush.gradient());
656 D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES linearGradientBrushProperties;
657 ComPtr<ID2D1GradientStopCollection> gradientStopCollection;
659 linearGradientBrushProperties.startPoint = to_d2d_point_2f(qlinear->start());
660 linearGradientBrushProperties.endPoint = to_d2d_point_2f(qlinear->finalStop());
662 const QList<D2D1_GRADIENT_STOP> stops = qGradientStopsToD2DStops(qlinear->stops());
664 hr =
dc()->CreateGradientStopCollection(stops.constData(),
665 UINT32(stops.size()),
666 &gradientStopCollection);
668 qWarning(
"%s: Could not create gradient stop collection for linear gradient: %#lx",
__FUNCTION__, hr);
672 hr =
dc()->CreateLinearGradientBrush(linearGradientBrushProperties, gradientStopCollection.Get(),
675 qWarning(
"%s: Could not create Direct2D linear gradient brush: %#lx",
__FUNCTION__, hr);
679 hr = linear.As(&result);
681 qWarning(
"%s: Could not convert Direct2D linear gradient brush: %#lx",
__FUNCTION__, hr);
687 case Qt::RadialGradientPattern:
688 if (newBrush.gradient()->spread() != QGradient::PadSpread) {
689 *needsEmulation =
true;
691 ComPtr<ID2D1RadialGradientBrush> radial;
692 const auto *qradial =
static_cast<
const QRadialGradient *>(newBrush.gradient());
694 D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES radialGradientBrushProperties;
695 ComPtr<ID2D1GradientStopCollection> gradientStopCollection;
697 radialGradientBrushProperties.center = to_d2d_point_2f(qradial->center());
698 radialGradientBrushProperties.gradientOriginOffset = to_d2d_point_2f(qradial->focalPoint() - qradial->center());
699 radialGradientBrushProperties.radiusX = FLOAT(qradial->radius());
700 radialGradientBrushProperties.radiusY = FLOAT(qradial->radius());
702 const QList<D2D1_GRADIENT_STOP> stops = qGradientStopsToD2DStops(qradial->stops());
704 hr =
dc()->CreateGradientStopCollection(stops.constData(), stops.size(), &gradientStopCollection);
706 qWarning(
"%s: Could not create gradient stop collection for radial gradient: %#lx",
__FUNCTION__, hr);
710 hr =
dc()->CreateRadialGradientBrush(radialGradientBrushProperties, gradientStopCollection.Get(),
713 qWarning(
"%s: Could not create Direct2D radial gradient brush: %#lx",
__FUNCTION__, hr);
719 qWarning(
"%s: Could not convert Direct2D radial gradient brush: %#lx",
__FUNCTION__, hr);
725 case Qt::ConicalGradientPattern:
726 *needsEmulation =
true;
729 case Qt::TexturePattern:
731 ComPtr<ID2D1BitmapBrush1> bitmapBrush;
732 D2D1_BITMAP_BRUSH_PROPERTIES1 bitmapBrushProperties = {
733 D2D1_EXTEND_MODE_WRAP,
734 D2D1_EXTEND_MODE_WRAP,
741 bitmapBrushProperties,
745 qWarning(
"%s: Could not create texture brush: %#lx",
__FUNCTION__, hr);
749 hr = bitmapBrush.As(&result);
751 qWarning(
"%s: Could not convert texture brush: %#lx",
__FUNCTION__, hr);
756 if (result && !newBrush.transform().isIdentity())
757 result->SetTransform(to_d2d_matrix_3x2_f(newBrush.transform()));
764 Q_Q(QWindowsDirect2DPaintEngine);
766 const bool alias = !q->antiAliasingEnabled();
768 QVectorPath::CacheEntry *cacheEntry = path.isCacheable() ? path.lookupCacheData(q)
773 if (alias && e->aliased)
775 else if (!alias && e->antiAliased)
776 return e->antiAliased;
786 || path.shape() == QVectorPath::PolygonHint);
788 const QPainterPath::ElementType *types = path.elements();
789 const int count = path.elementCount();
790 const qreal *points = path.points();
797 for (
int i = 0; i < count; i++) {
799 y = points[i * 2 + 1];
802 case QPainterPath::MoveToElement:
803 writer.moveTo(QPointF(x, y));
806 case QPainterPath::LineToElement:
807 writer.lineTo(QPointF(x, y));
810 case QPainterPath::CurveToElement:
812 Q_ASSERT((i + 2) < count);
813 Q_ASSERT(types[i+1] == QPainterPath::CurveToDataElement);
814 Q_ASSERT(types[i+2] == QPainterPath::CurveToDataElement);
817 const qreal x2 = points[i * 2];
818 const qreal y2 = points[i * 2 + 1];
821 const qreal x3 = points[i * 2];
822 const qreal y3 = points[i * 2 + 1];
824 writer.curveTo(QPointF(x, y), QPointF(x2, y2), QPointF(x3, y3));
828 case QPainterPath::CurveToDataElement:
829 qWarning(
"%s: Unhandled Curve Data Element",
__FUNCTION__);
834 writer.moveTo(QPointF(points[0], points[1]));
835 for (
int i = 1; i < count; i++)
836 writer.lineTo(QPointF(points[i * 2], points[i * 2 + 1]));
840 if (path.hasImplicitClose())
841 writer.lineTo(QPointF(points[0], points[1]));
844 ComPtr<ID2D1PathGeometry1> geometry = writer.geometry();
846 if (path.isCacheable()) {
848 cacheEntry = path.addCacheData(q,
new D2DVectorPathCache, D2DVectorPathCache::cleanup_func);
852 e->aliased = geometry;
854 e->antiAliased = geometry;
856 path.makeCacheable();
864 dc()->SetAntialiasMode(antialiasMode());
868 IDWriteFontFace *fontFace,
869 const QFontDef &fontDef,
871 const UINT16 *glyphIndices,
872 const FLOAT *glyphAdvances,
873 const DWRITE_GLYPH_OFFSET *glyphOffsets,
876 Q_Q(QWindowsDirect2DPaintEngine);
878 DWRITE_GLYPH_RUN glyphRun = {
880 FLOAT(fontDef.pixelSize),
889 const bool antiAlias =
bool((q->state()->renderHints & QPainter::TextAntialiasing)
890 && !(fontDef.styleStrategy & QFont::NoAntialias));
891 const D2D1_TEXT_ANTIALIAS_MODE antialiasMode = (flags & QWindowsDirect2DPaintEngine::TranslucentTopLevelWindow)
892 ? D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE : D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
893 dc()->SetTextAntialiasMode(antiAlias ? antialiasMode : D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
895 dc()->DrawGlyphRun(pos,
899 DWRITE_MEASURING_MODE_GDI_CLASSIC);
904 Q_Q(QWindowsDirect2DPaintEngine);
907 if (!(path.shape() == QVectorPath::LinesHint || path.shape() == QVectorPath::PolygonHint)
909 || q->state()->renderHints.testFlag(QPainter::Antialiasing)) {
910 ComPtr<ID2D1Geometry> geometry = vectorPathToID2D1PathGeometry(path);
912 qWarning(
"%s: Could not convert path to d2d geometry",
__FUNCTION__);
915 dc()->DrawGeometry(geometry.Get(), pen.brush.Get(),
916 FLOAT(pen.qpen.widthF()), pen.strokeStyle.Get());
921 const bool isPolygon = path.shape() == QVectorPath::PolygonHint && path.elementCount() >= 3;
922 const bool implicitClose = isPolygon && (path.hints() & QVectorPath::ImplicitClose);
923 const bool skipJoin = !isPolygon
924 || (pen.qpen.joinStyle() == Qt::MiterJoin && qFuzzyIsNull(pen.qpen.miterLimit()));
925 const qreal *points = path.points();
926 const int lastElement = path.elementCount() - (implicitClose ? 1 : 2);
927 qreal dashOffset = 0;
929 ID2D1Brush *brush = pen.dashBrush ? pen.dashBrush.Get() : pen.brush.Get();
930 for (
int i = 0; i <= lastElement; ++i) {
931 QPointF p1(points[i * 2], points[i * 2 + 1]);
932 QPointF p2 = implicitClose && i == lastElement ? QPointF(points[0], points[1])
933 : QPointF(points[i * 2 + 2], points[i * 2 + 3]);
938 if (p1 == p2 && pen.qpen.widthF() <= 1.0) {
939 q->fillRect(QRectF(p1, QSizeF(pen.qpen.widthF(), pen.qpen.widthF())), pen.qpen.brush());
943 if (!q->antiAliasingEnabled())
944 adjustLine(&p1, &p2);
946 q->adjustForAliasing(&p1);
947 q->adjustForAliasing(&p2);
949 const QLineF line(p1, p2);
950 const qreal lineLength = line.length();
952 pen.dashBrush->SetTransform(transformFromLine(line, pen.qpen.widthF(), dashOffset));
953 dashOffset = pen.dashLength - fmod(lineLength - dashOffset, pen.dashLength);
955 dc()->DrawLine(to_d2d_point_2f(p1), to_d2d_point_2f(p2),
956 brush, FLOAT(pen.qpen.widthF()),
nullptr);
962 const qreal patchSegment = pen.dashBrush ? qBound(0.0, (pen.dashLength - dashOffset) / lineLength, 1.0)
967 writer.moveTo(jointStart);
969 writer.lineTo(line.pointAt(patchSegment));
971 dc()->DrawGeometry(writer.geometry().Get(), pen.brush.Get(),
972 FLOAT(pen.qpen.widthF()), pen.strokeStyle.Get());
975 jointStart = line.pointAt(1 - patchSegment);
977 if (implicitClose && i == lastElement) {
980 writer.moveTo(jointStart);
982 writer.lineTo(QLineF(p2, QPointF(points[2], points[3])).pointAt(patchSegment));
984 dc()->DrawGeometry(writer.geometry().Get(), pen.brush.Get(),
985 FLOAT(pen.qpen.widthF()), pen.strokeStyle.Get());
992 const QFontDef fontDef = fe->fontDef;
993 ComPtr<IDWriteFontFace> fontFace = fontCache.value(fontDef);
997 LOGFONT lf = QWindowsFontDatabase::fontDefToLOGFONT(fontDef, QString());
1000 static const char keyC[] =
"HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes";
1001 const QString familyName = QString::fromWCharArray(lf.lfFaceName);
1002 const QString nameSubstitute = QSettings(QLatin1StringView(keyC), QSettings::NativeFormat).value(familyName, familyName).toString();
1003 if (nameSubstitute != familyName) {
1004 const int nameSubstituteLength = qMin(nameSubstitute.length(), LF_FACESIZE - 1);
1005 memcpy(lf.lfFaceName, nameSubstitute.data(), size_t(nameSubstituteLength) *
sizeof(
wchar_t));
1006 lf.lfFaceName[nameSubstituteLength] = 0;
1009 ComPtr<IDWriteFont> dwriteFont;
1012 qDebug(
"%s: CreateFontFromLOGFONT failed: %#lx",
__FUNCTION__, hr);
1016 hr = dwriteFont->CreateFontFace(&fontFace);
1018 qDebug(
"%s: CreateFontFace failed: %#lx",
__FUNCTION__, hr);
1023 fontCache.insert(fontDef, fontFace);
1030 : QPaintEngineEx(*(
new QWindowsDirect2DPaintEnginePrivate(bitmap, flags)))
1032 QPaintEngine::PaintEngineFeatures unsupported =
1035 QPaintEngine::PorterDuff
1036 | QPaintEngine::BlendModes
1037 | QPaintEngine::RasterOpModes
1043 | QPaintEngine::PerspectiveTransform;
1045 gccaps &= ~unsupported;
1050 Q_D(QWindowsDirect2DPaintEngine);
1052 d->bitmap->deviceContext()->begin();
1053 d->dc()->SetTransform(D2D1::Matrix3x2F::Identity());
1055 if (systemClip().rectCount() > 1) {
1057 p.addRegion(systemClip());
1059 ComPtr<ID2D1PathGeometry1> geometry = d->vectorPathToID2D1PathGeometry(qtVectorPathForPath(p));
1063 d->dc()->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(),
1066 D2D1::IdentityMatrix(),
1072 QRect clip(0, 0, pdev->width(), pdev->height());
1073 if (!systemClip().isEmpty())
1074 clip &= systemClip().boundingRect();
1075 d->dc()->PushAxisAlignedClip(to_d2d_rect_f(clip), D2D1_ANTIALIAS_MODE_ALIASED);
1076 d->clipFlags |= SimpleSystemClip;
1079 D2D_TAG(D2DDebugDrawInitialStateTag);
1087 Q_D(QWindowsDirect2DPaintEngine);
1090 const bool emulatingComposition = d->flags.testFlag(EmulateComposition);
1091 d->flags &= ~QWindowsDirect2DPaintEngine::EmulateComposition;
1092 if (!d->fallbackImage.isNull()) {
1093 if (emulatingComposition)
1094 drawImage(d->fallbackImage.rect(), d->fallbackImage, d->fallbackImage.rect());
1095 d->fallbackImage = QImage();
1101 if (d->clipFlags & SimpleSystemClip) {
1102 d->dc()->PopAxisAlignedClip();
1103 d->clipFlags &= ~SimpleSystemClip;
1105 d->dc()->PopLayer();
1108 return d->bitmap->deviceContext()->end();
1113 return QPaintEngine::Direct2D;
1118 Q_D(QWindowsDirect2DPaintEngine);
1120 QPaintEngineEx::setState(s);
1135 const QBrush &brush = state()->brush;
1136 if (qbrush_style(brush) != Qt::NoBrush) {
1137 if (emulationRequired(BrushEmulation))
1138 rasterFill(path, brush);
1143 const QPen &pen = state()->pen;
1144 if (qpen_style(pen) != Qt::NoPen && qbrush_style(qpen_brush(pen)) != Qt::NoBrush) {
1145 if (emulationRequired(PenEmulation))
1146 QPaintEngineEx::stroke(path, pen);
1154 Q_D(QWindowsDirect2DPaintEngine);
1161 if (emulationRequired(BrushEmulation)) {
1162 rasterFill(path, brush);
1166 if (!d->brush.brush)
1169 ComPtr<ID2D1Geometry> geometry = d->vectorPathToID2D1PathGeometry(path);
1171 qWarning(
"%s: Could not convert path to d2d geometry",
__FUNCTION__);
1175 d->dc()->FillGeometry(geometry.Get(), d->brush.brush.Get());
1180 Q_D(QWindowsDirect2DPaintEngine);
1187 if (emulationRequired(PenEmulation)) {
1188 QPaintEngineEx::stroke(path, pen);
1200 Q_D(QWindowsDirect2DPaintEngine);
1206 Q_D(QWindowsDirect2DPaintEngine);
1207 d->updateClipEnabled(state()->clipEnabled);
1212 Q_D(QWindowsDirect2DPaintEngine);
1213 d->updatePen(state()->pen);
1218 Q_D(QWindowsDirect2DPaintEngine);
1219 d->updateBrush(state()->brush);
1224 Q_D(QWindowsDirect2DPaintEngine);
1225 d->updateBrushOrigin(state()->brushOrigin);
1230 Q_D(QWindowsDirect2DPaintEngine);
1231 d->updateOpacity(state()->opacity);
1236 Q_D(QWindowsDirect2DPaintEngine);
1237 d->updateCompositionMode(state()->compositionMode());
1242 Q_D(QWindowsDirect2DPaintEngine);
1248 Q_D(QWindowsDirect2DPaintEngine);
1249 d->updateTransform(state()->transform());
1254 Q_D(QWindowsDirect2DPaintEngine);
1259 if (emulationRequired(BrushEmulation)) {
1260 QPaintEngineEx::fillRect(rect, brush);
1262 QRectF r = rect.normalized();
1263 adjustForAliasing(&r);
1266 d->dc()->FillRectangle(to_d2d_rect_f(rect), d->brush.brush.Get());
1272 Q_D(QWindowsDirect2DPaintEngine);
1273 D2D_TAG(D2DDebugDrawRectsTag);
1278 if (emulationRequired(BrushEmulation) || emulationRequired(PenEmulation)) {
1279 QPaintEngineEx::drawRects(rects, rectCount);
1282 for (
int i = 0; i < rectCount; i++) {
1283 rect = rects[i].normalized();
1284 adjustForAliasing(&rect);
1286 D2D1_RECT_F d2d_rect = to_d2d_rect_f(rect);
1289 d->dc()->FillRectangle(d2d_rect, d->brush.brush.Get());
1292 d->dc()->DrawRectangle(d2d_rect, d->pen.brush.Get(),
1293 FLOAT(d->pen.qpen.widthF()), d->pen.strokeStyle.Get());
1300 Q_D(QWindowsDirect2DPaintEngine);
1301 D2D_TAG(D2DDebugDrawRectFsTag);
1306 if (emulationRequired(BrushEmulation) || emulationRequired(PenEmulation)) {
1307 QPaintEngineEx::drawRects(rects, rectCount);
1310 for (
int i = 0; i < rectCount; i++) {
1311 rect = rects[i].normalized();
1312 adjustForAliasing(&rect);
1314 D2D1_RECT_F d2d_rect = to_d2d_rect_f(rect);
1317 d->dc()->FillRectangle(d2d_rect, d->brush.brush.Get());
1320 d->dc()->DrawRectangle(d2d_rect, d->pen.brush.Get(),
1321 FLOAT(d->pen.qpen.widthF()), d->pen.strokeStyle.Get());
1328 if (p2.x() > p1.x())
1329 return p2.y() < p1.y();
1331 if (p1.x() > p2.x())
1332 return p1.y() < p2.y();
1339 if (isLinePositivelySloped(*p1, *p2)) {
1340 p1->ry() -= qreal(1.0);
1341 p2->ry() -= qreal(1.0);
1347 Q_D(QWindowsDirect2DPaintEngine);
1348 D2D_TAG(D2DDebugDrawEllipseFTag);
1353 if (emulationRequired(BrushEmulation) || emulationRequired(PenEmulation)) {
1354 QPaintEngineEx::drawEllipse(r);
1356 QPointF p = r.center();
1357 adjustForAliasing(&p);
1359 D2D1_ELLIPSE ellipse = {
1361 FLOAT(r.width() / 2.0),
1362 FLOAT(r.height() / 2.0)
1366 d->dc()->FillEllipse(ellipse, d->brush.brush.Get());
1369 d->dc()->DrawEllipse(ellipse, d->pen.brush.Get(),
1370 FLOAT(d->pen.qpen.widthF()),
1371 d->pen.strokeStyle.Get());
1377 Q_D(QWindowsDirect2DPaintEngine);
1378 D2D_TAG(D2DDebugDrawEllipseTag);
1383 if (emulationRequired(BrushEmulation) || emulationRequired(PenEmulation)) {
1384 QPaintEngineEx::drawEllipse(r);
1386 QPointF p = r.center();
1387 adjustForAliasing(&p);
1389 D2D1_ELLIPSE ellipse = {
1391 FLOAT(r.width() / 2.0),
1392 FLOAT(r.height() / 2.0)
1396 d->dc()->FillEllipse(ellipse, d->brush.brush.Get());
1399 d->dc()->DrawEllipse(ellipse, d->pen.brush.Get(),
1400 FLOAT(d->pen.qpen.widthF()),
1401 d->pen.strokeStyle.Get());
1406 const QRectF &sr, Qt::ImageConversionFlags flags)
1408 Q_D(QWindowsDirect2DPaintEngine);
1409 D2D_TAG(D2DDebugDrawImageTag);
1411 QPixmap pixmap = QPixmap::fromImage(image, flags);
1412 drawPixmap(rectangle, pixmap, sr);
1419 Q_D(QWindowsDirect2DPaintEngine);
1420 D2D_TAG(D2DDebugDrawPixmapTag);
1425 if (pm.handle()->pixelType() == QPlatformPixmap::BitmapType) {
1426 QImage i = pm.toImage();
1427 i.setColor(0, qRgba(0, 0, 0, 0));
1428 i.setColor(1, d->pen.qpen.color().rgba());
1429 drawImage(r, i, sr);
1433 if (d->flags.testFlag(EmulateComposition)) {
1434 const qreal points[] = {
1436 r.x() + r.width(), r.y(),
1437 r.x() + r.width(), r.y() + r.height(),
1438 r.x(), r.y() + r.height()
1440 const QVectorPath vp(points, 4,
nullptr, QVectorPath::RectangleHint);
1441 QBrush brush(sr.isValid() ? pm.copy(sr.toRect()) : pm);
1442 brush.setTransform(QTransform::fromTranslate(r.x(), r.y()));
1443 rasterFill(vp, brush);
1452 if (bitmap->bitmap() != d->bitmap->bitmap()) {
1455 d->dc()->DrawBitmap(bitmap->bitmap(),
1456 to_d2d_rect_f(r), FLOAT(state()->opacity),
1457 d->interpolationMode(),
1460 d->dc()->DrawBitmap(bitmap->bitmap(),
1461 to_d2d_rect_f(r), FLOAT(state()->opacity),
1462 d->interpolationMode());
1470 bool r = intermediate
.resize(int(sr.width())
, int(sr.height())
);
1472 qWarning(
"%s: Could not resize intermediate bitmap to source rect size",
__FUNCTION__);
1476 D2D1_RECT_U d2d_sr = to_d2d_rect_u(sr.toRect());
1477 HRESULT hr = intermediate
.bitmap()->CopyFromBitmap(
nullptr,
1481 qWarning(
"%s: Could not copy source rect area from source bitmap to intermediate bitmap: %#lx",
__FUNCTION__, hr);
1488 qWarning(
"%s: Could not resize intermediate bitmap to source bitmap size",
__FUNCTION__);
1492 HRESULT hr = intermediate
.bitmap()->CopyFromBitmap(
nullptr,
1496 qWarning(
"%s: Could not copy source bitmap to intermediate bitmap: %#lx",
__FUNCTION__, hr);
1501 d->dc()->DrawBitmap(intermediate.bitmap(),
1502 to_d2d_rect_f(r), FLOAT(state()->opacity),
1503 d->interpolationMode());
1509 Q_D(QWindowsDirect2DPaintEngine);
1510 D2D_TAG(D2DDebugDrawStaticTextItemTag);
1512 if (staticTextItem->numGlyphs == 0)
1518 if (emulationRequired(PenEmulation)) {
1519 QPaintEngineEx::drawStaticTextItem(staticTextItem);
1523 ComPtr<IDWriteFontFace> fontFace = d->fontFaceFromFontEngine(staticTextItem->fontEngine());
1525 qWarning(
"%s: Could not find font - falling back to slow text rendering path.",
__FUNCTION__);
1526 QPaintEngineEx::drawStaticTextItem(staticTextItem);
1530 QVarLengthArray<UINT16> glyphIndices(staticTextItem->numGlyphs);
1531 QVarLengthArray<FLOAT> glyphAdvances(staticTextItem->numGlyphs);
1532 QVarLengthArray<DWRITE_GLYPH_OFFSET> glyphOffsets(staticTextItem->numGlyphs);
1534 for (
int i = 0; i < staticTextItem->numGlyphs; i++) {
1535 glyphIndices[i] = UINT16(staticTextItem->glyphs[i]);
1538 glyphAdvances[i] = 0;
1539 glyphOffsets[i].advanceOffset = FLOAT(staticTextItem->glyphPositions[i].x.toReal());
1541 glyphOffsets[i].ascenderOffset = FLOAT(staticTextItem->glyphPositions[i].y.toReal() * -1);
1544 d->drawGlyphRun(D2D1::Point2F(0, 0),
1546 staticTextItem->fontEngine()->fontDef,
1547 staticTextItem->numGlyphs,
1548 glyphIndices.constData(),
1549 glyphAdvances.constData(),
1550 glyphOffsets.constData(),
1556 Q_D(QWindowsDirect2DPaintEngine);
1557 D2D_TAG(D2DDebugDrawTextItemTag);
1559 const auto &ti =
static_cast<
const QTextItemInt &>(textItem);
1560 if (ti.glyphs.numGlyphs == 0)
1566 if (emulationRequired(PenEmulation)) {
1567 QPaintEngine::drawTextItem(p, textItem);
1571 ComPtr<IDWriteFontFace> fontFace = d->fontFaceFromFontEngine(ti.fontEngine);
1573 qWarning(
"%s: Could not find font - falling back to slow text rendering path.",
__FUNCTION__);
1574 QPaintEngine::drawTextItem(p, textItem);
1578 QVarLengthArray<UINT16> glyphIndices(ti.glyphs.numGlyphs);
1579 QVarLengthArray<FLOAT> glyphAdvances(ti.glyphs.numGlyphs);
1580 QVarLengthArray<DWRITE_GLYPH_OFFSET> glyphOffsets(ti.glyphs.numGlyphs);
1582 for (
int i = 0; i < ti.glyphs.numGlyphs; i++) {
1583 glyphIndices[i] = UINT16(ti.glyphs.glyphs[i]);
1584 glyphAdvances[i] = FLOAT(ti.glyphs.effectiveAdvance(i).toReal());
1585 glyphOffsets[i].advanceOffset = FLOAT(ti.glyphs.offsets[i].x.toReal());
1588 glyphOffsets[i].ascenderOffset = FLOAT(ti.glyphs.offsets[i].y.toReal());
1591 const bool rtl = (ti.flags & QTextItem::RightToLeft);
1592 const QPointF offset(rtl ? ti.width.toReal() : 0, 0);
1594 d->drawGlyphRun(to_d2d_point_2f(p + offset),
1596 ti.fontEngine->fontDef,
1597 ti.glyphs.numGlyphs,
1598 glyphIndices.constData(),
1599 glyphAdvances.constData(),
1600 glyphOffsets.constData(),
1606 ensureBrush(state()->brush);
1611 Q_D(QWindowsDirect2DPaintEngine);
1612 d->updateBrush(brush);
1617 ensurePen(state()->pen);
1622 Q_D(QWindowsDirect2DPaintEngine);
1628 Q_D(QWindowsDirect2DPaintEngine);
1630 if (d->fallbackImage.isNull()) {
1631 if (d->flags.testFlag(EmulateComposition)) {
1633 d->fallbackImage = d->bitmap->toImage();
1635 d->fallbackImage = QImage(d->bitmap->size(), QImage::Format_ARGB32_Premultiplied);
1636 d->fallbackImage.fill(Qt::transparent);
1640 QImage &img = d->fallbackImage;
1642 QPaintEngine *engine = img.paintEngine();
1644 if (engine->isExtended() && p.begin(&img)) {
1645 p.setRenderHints(state()->renderHints);
1646 p.setCompositionMode(state()->compositionMode());
1647 p.setOpacity(state()->opacity);
1648 p.setBrushOrigin(state()->brushOrigin);
1649 p.setBrush(state()->brush);
1650 p.setPen(state()->pen);
1652 auto *extended =
static_cast<QPaintEngineEx *>(engine);
1653 for (
const QPainterClipInfo &info : std::as_const(state()->clipInfo)) {
1654 extended->state()->matrix = info.matrix;
1655 extended->transformChanged();
1657 switch (info.clipType) {
1658 case QPainterClipInfo::RegionClip:
1659 extended->clip(info.region, info.operation);
1661 case QPainterClipInfo::PathClip:
1662 extended->clip(info.path, info.operation);
1664 case QPainterClipInfo::RectClip:
1665 extended->clip(info.rect, info.operation);
1667 case QPainterClipInfo::RectFClip:
1668 qreal right = info.rectf.x() + info.rectf.width();
1669 qreal bottom = info.rectf.y() + info.rectf.height();
1670 qreal pts[] = { info.rectf.x(), info.rectf.y(),
1671 right, info.rectf.y(),
1673 info.rectf.x(), bottom };
1674 QVectorPath vp(pts, 4,
nullptr, QVectorPath::RectangleHint);
1675 extended->clip(vp, info.operation);
1680 extended->state()->matrix = state()->matrix;
1681 extended->transformChanged();
1683 extended->fill(path, brush);
1685 qWarning(
"%s: Paint Engine end returned false",
__FUNCTION__);
1687 if (!d->flags.testFlag(EmulateComposition)) {
1688 d->updateClipEnabled(
false);
1689 d->updateTransform(QTransform());
1690 drawImage(img.rect(), img, img.rect());
1691 d->fallbackImage = QImage();
1696 qWarning(
"%s: Could not fall back to QImage",
__FUNCTION__);
1702 Q_D(
const QWindowsDirect2DPaintEngine);
1704 if (d->flags.testFlag(EmulateComposition))
1707 if (!state()->matrix.isAffine())
1712 return d->pen.emulate;
1714 case BrushEmulation:
1715 return d->brush.emulate;
1724 return state()->renderHints & QPainter::Antialiasing;
1729 if (!antiAliasingEnabled()) {
1730 rect->adjust(MAGICAL_ALIASING_OFFSET,
1731 MAGICAL_ALIASING_OFFSET,
1732 MAGICAL_ALIASING_OFFSET,
1733 MAGICAL_ALIASING_OFFSET);
1740 MAGICAL_ALIASING_OFFSET);
1742 if (!antiAliasingEnabled())
1743 (*point) += adjustment;
1753 begin(paintDevice());
1772 , m_active(engine->isActive())
1775 m_engine->suspend();
ComPtr< ID2D1PathGeometry1 > geometry() const
void setWindingFillEnabled(bool enable)
void moveTo(const QPointF &point)
void curveTo(const QPointF &p1, const QPointF &p2, const QPointF &p3)
void lineTo(const QPointF &point)
void setPositiveSlopeAdjustmentEnabled(bool enable)
void setAliasingEnabled(bool enable)
QWindowsDirect2DDeviceContext * deviceContext() const
ID2D1Bitmap1 * bitmap() const
bool resize(int width, int height)
ID2D1Factory1 * d2dFactory() const
IDWriteGdiInterop * dwriteGdiInterop() const
static QWindowsDirect2DContext * instance()
ID2D1DeviceContext * get() const
ComPtr< ID2D1StrokeStyle1 > strokeStyle
void updatePen(const QPen &newPen)
ID2D1DeviceContext * dc() const
void clip(const QVectorPath &path, Qt::ClipOperation operation)
void updateBrushOrigin(const QPointF &brushOrigin)
void drawGlyphRun(const D2D1_POINT_2F &pos, IDWriteFontFace *fontFace, const QFontDef &fontDef, int numGlyphs, const UINT16 *glyphIndices, const FLOAT *glyphAdvances, const DWRITE_GLYPH_OFFSET *glyphOffsets, bool rtl)
void pushClip(const QVectorPath &path)
QWindowsDirect2DPaintEngine::Flags flags
void applyBrushOrigin(const QPointF &origin)
void stroke(const QVectorPath &path)
D2D1_ANTIALIAS_MODE antialiasMode() const
void updateTransform(const QTransform &transform)
QPointF currentBrushOrigin
D2D1_INTERPOLATION_MODE interpolationMode() const
ComPtr< IDWriteFontFace > fontFaceFromFontEngine(QFontEngine *fe)
void updateOpacity(qreal opacity)
void updateBrush(const QBrush &newBrush)
void updateCompositionMode(QPainter::CompositionMode mode)
ComPtr< ID2D1Brush > brush
void negateCurrentBrushOrigin()
ComPtr< ID2D1Brush > to_d2d_brush(const QBrush &newBrush, bool *needsEmulation)
QStack< ClipType > pushedClips
D2D1_LAYER_OPTIONS1 layerOptions() const
ComPtr< ID2D1PathGeometry1 > vectorPathToID2D1PathGeometry(const QVectorPath &path)
void updateClipEnabled(bool enabled)
ComPtr< ID2D1BitmapBrush1 > dashBrush
~QWindowsDirect2DPaintEngineSuspenderImpl()
QWindowsDirect2DPaintEngineSuspenderImpl(QWindowsDirect2DPaintEngine *engine)
QWindowsDirect2DPaintEngineSuspenderImpl engineSuspender
QWindowsDirect2DPaintEngineSuspenderPrivate(QWindowsDirect2DPaintEngine *engine)
QWindowsDirect2DDeviceContextSuspender dcSuspender
~QWindowsDirect2DPaintEngineSuspender()
QWindowsDirect2DPaintEngineSuspender(QWindowsDirect2DPaintEngine *engine)
void penChanged() override
void fillRect(const QRectF &rect, const QBrush &brush) override
Type type() const override
Reimplement this function to return the paint engine \l{Type}.
void opacityChanged() override
void renderHintsChanged() override
void brushOriginChanged() override
void compositionModeChanged() override
void drawRects(const QRect *rects, int rectCount) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void transformChanged() override
void draw(const QVectorPath &path) override
void drawEllipse(const QRectF &r) override
Reimplement this function to draw the largest ellipse that can be contained within rectangle rect.
void clipEnabledChanged() override
void drawTextItem(const QPointF &p, const QTextItem &textItem) override
This function draws the text item textItem at position p.
void brushChanged() override
void setState(QPainterState *s) override
void stroke(const QVectorPath &path, const QPen &pen) override
void drawStaticTextItem(QStaticTextItem *staticTextItem) override
void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override
Reimplement this function to draw the part of the pm specified by the sr rectangle in the given r.
void fill(const QVectorPath &path, const QBrush &brush) override
void clip(const QVectorPath &path, Qt::ClipOperation op) override
bool end() override
Reimplement this function to finish painting on the current paint device.
bool begin(QPaintDevice *pdev) override
Reimplement this function to initialise your paint engine when painting is to start on the paint devi...
Combined button and popup list for selecting options.
static D2D1_MATRIX_3X2_F transformFromLine(const QLineF &line, qreal penWidth, qreal dashOffset)
static const qreal MAGICAL_ALIASING_OFFSET
@ D2DDebugDrawStaticTextItemTag
@ D2DDebugDrawEllipseFTag
@ D2DDebugDrawTextItemTag
@ D2DDebugDrawInitialStateTag
static ID2D1Factory1 * factory()
static void adjustLine(QPointF *p1, QPointF *p2)
static QList< D2D1_GRADIENT_STOP > qGradientStopsToD2DStops(const QGradientStops &qstops)
static bool isLinePositivelySloped(const QPointF &p1, const QPointF &p2)
ComPtr< ID2D1PathGeometry1 > aliased
static void cleanup_func(QPaintEngineEx *engine, void *data)
ComPtr< ID2D1PathGeometry1 > antiAliased