12#include <QtGui/private/qwindowsfontdatabase_p.h>
15#include <QtCore/qmath.h>
16#include <QtCore/qstack.h>
17#include <QtCore/qsettings.h>
18#include <QtCore/private/qcomptr_p.h>
19#include <QtGui/private/qpaintengine_p.h>
20#include <QtGui/private/qtextengine_p.h>
21#include <QtGui/private/qfontengine_p.h>
22#include <QtGui/private/qstatictext_p.h>
65#define D2D_TAG(tag) d->dc()->SetTags(tag, tag)
76 const qreal halfWidth = penWidth / 2;
77 const qreal angle = -qDegreesToRadians(line.angle());
78 const qreal sinA = qSin(angle);
79 const qreal cosA = qCos(angle);
80 QTransform transform = QTransform::fromTranslate(line.p1().x() + dashOffset * cosA + sinA * halfWidth,
81 line.p1().y() + dashOffset * sinA - cosA * halfWidth);
82 transform.rotateRadians(angle);
83 return to_d2d_matrix_3x2_f(transform);
86static void adjustLine(QPointF *p1, QPointF *p2);
91 QList<D2D1_GRADIENT_STOP> stops(qstops.count());
92 for (
int i = 0, count = stops.size(); i < count; ++i) {
93 stops[i].position = FLOAT(qstops.at(i).first);
94 stops[i].color = to_d2d_color_f(qstops.at(i).second);
104 HRESULT hr = factory()->CreatePathGeometry(&m_geometry);
106 qWarning(
"%s: Could not create path geometry: %#lx",
__FUNCTION__, hr);
110 hr = m_geometry->Open(&m_sink);
112 qWarning(
"%s: Could not create geometry sink: %#lx",
__FUNCTION__, hr);
122 m_sink->SetFillMode(D2D1_FILL_MODE_WINDING);
124 m_sink->SetFillMode(D2D1_FILL_MODE_ALTERNATE);
129 m_roundCoordinates = enable;
134 m_adjustPositivelySlopedLines = enable;
145 m_sink->EndFigure(D2D1_FIGURE_END_OPEN);
147 m_sink->BeginFigure(adjusted(point), D2D1_FIGURE_BEGIN_FILLED);
149 m_previousPoint = point;
155 if (m_adjustPositivelySlopedLines && isLinePositivelySloped(m_previousPoint, point)) {
156 moveTo(m_previousPoint - QPointF(0, 1));
159 m_sink->AddLine(adjusted(pt));
162 m_previousPoint = point;
165 void curveTo(
const QPointF &p1,
const QPointF &p2,
const QPointF &p3)
167 D2D1_BEZIER_SEGMENT segment = {
173 m_sink->AddBezier(segment);
174 m_previousPoint = p3;
180 m_sink->EndFigure(D2D1_FIGURE_END_OPEN);
191 D2D1_POINT_2F adjusted(
const QPointF &point)
194 MAGICAL_ALIASING_OFFSET);
196 if (m_roundCoordinates)
197 return to_d2d_point_2f(point + adjustment);
199 return to_d2d_point_2f(point);
202 ComPtr<ID2D1PathGeometry1> m_geometry;
203 ComPtr<ID2D1GeometrySink> m_sink;
205 bool m_inFigure =
false;
206 bool m_roundCoordinates =
false;
207 bool m_adjustPositivelySlopedLines =
false;
208 QPointF m_previousPoint;
224 Q_DECLARE_PUBLIC(QWindowsDirect2DPaintEngine)
255 inline void reset() {
268 ComPtr<ID2D1Brush> brush;
270 inline void reset() {
277 inline ID2D1DeviceContext *
dc()
const
280 return bitmap->deviceContext()->get();
285 Q_Q(
const QWindowsDirect2DPaintEngine);
286 return (q->state()->renderHints & QPainter::SmoothPixmapTransform) ? D2D1_INTERPOLATION_MODE_LINEAR
287 : D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
292 Q_Q(
const QWindowsDirect2DPaintEngine);
293 return (q->state()->renderHints & QPainter::Antialiasing) ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
294 : D2D1_ANTIALIAS_MODE_ALIASED;
299 if (flags & QWindowsDirect2DPaintEngine::TranslucentTopLevelWindow)
300 return D2D1_LAYER_OPTIONS1_NONE;
302 return D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
307 dc()->SetTransform(to_d2d_matrix_3x2_f(transform));
313 brush.brush->SetOpacity(FLOAT(opacity));
315 pen.brush->SetOpacity(FLOAT(opacity));
320 Q_Q(QWindowsDirect2DPaintEngine);
322 if (path.isEmpty()) {
323 D2D_RECT_F rect = {0, 0, 0, 0};
324 dc()->PushAxisAlignedClip(rect, antialiasMode());
325 pushedClips.push(AxisAlignedClip);
326 }
else if (path.isRect() && (q->state()->matrix.type() <= QTransform::TxScale)) {
327 const qreal *
const points = path.points();
335 dc()->PushAxisAlignedClip(rect, antialiasMode());
336 pushedClips.push(AxisAlignedClip);
338 ComPtr<ID2D1PathGeometry1> geometry = vectorPathToID2D1PathGeometry(path);
340 qWarning(
"%s: Could not convert vector path to painter path!",
__FUNCTION__);
344 dc()->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(),
347 D2D1::IdentityMatrix(),
352 pushedClips.push(LayerClip);
358 while (!pushedClips.isEmpty()) {
359 switch (pushedClips.pop()) {
361 dc()->PopAxisAlignedClip();
374 else if (pushedClips.isEmpty())
375 replayClipOperations();
378 void clip(
const QVectorPath &path, Qt::ClipOperation operation)
384 case Qt::ReplaceClip:
388 case Qt::IntersectClip:
397 case QPainter::CompositionMode_Source:
398 dc()->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
400 case QPainter::CompositionMode_SourceOver:
401 dc()->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
407 dc()->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
408 flags |= QWindowsDirect2DPaintEngine::EmulateComposition;
415 Q_Q(
const QWindowsDirect2DPaintEngine);
417 if (qbrush_fast_equals(brush.qbrush, newBrush) && (brush.brush || brush.emulate))
420 brush.brush = to_d2d_brush(newBrush, &brush.emulate);
421 brush.qbrush = newBrush;
424 brush.brush->SetOpacity(FLOAT(q->state()->opacity));
425 applyBrushOrigin(currentBrushOrigin);
432 applyBrushOrigin(brushOrigin);
437 if (brush.brush && !currentBrushOrigin.isNull()) {
438 D2D1_MATRIX_3X2_F transform;
439 brush.brush->GetTransform(&transform);
441 brush.brush->SetTransform(*(D2D1::Matrix3x2F::ReinterpretBaseType(&transform))
442 * D2D1::Matrix3x2F::Translation(FLOAT(-currentBrushOrigin.x()),
443 FLOAT(-currentBrushOrigin.y())));
449 if (brush.brush && !origin.isNull()) {
450 D2D1_MATRIX_3X2_F transform;
451 brush.brush->GetTransform(&transform);
453 brush.brush->SetTransform(*(D2D1::Matrix3x2F::ReinterpretBaseType(&transform))
454 * D2D1::Matrix3x2F::Translation(FLOAT(origin.x()), FLOAT(origin.y())));
457 currentBrushOrigin = origin;
462 Q_Q(
const QWindowsDirect2DPaintEngine);
463 if (qpen_fast_equals(newPen, pen.qpen) && (pen.brush || pen.emulate))
469 if (newPen.style() == Qt::NoPen)
472 pen.brush = to_d2d_brush(newPen.brush(), &pen.emulate);
476 pen.brush->SetOpacity(FLOAT(q->state()->opacity));
478 D2D1_STROKE_STYLE_PROPERTIES1 props = {};
480 switch (newPen.capStyle()) {
482 props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_SQUARE;
485 props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_ROUND;
489 props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_FLAT;
493 switch (newPen.joinStyle()) {
495 props.lineJoin = D2D1_LINE_JOIN_BEVEL;
498 props.lineJoin = D2D1_LINE_JOIN_ROUND;
502 props.lineJoin = D2D1_LINE_JOIN_MITER;
506 props.miterLimit = FLOAT(newPen.miterLimit() * qreal(2.0));
507 props.dashOffset = FLOAT(newPen.dashOffset());
509 if (newPen.widthF() == 0)
510 props.transformType = D2D1_STROKE_TRANSFORM_TYPE_HAIRLINE;
511 else if (newPen.isCosmetic())
512 props.transformType = D2D1_STROKE_TRANSFORM_TYPE_FIXED;
514 props.transformType = D2D1_STROKE_TRANSFORM_TYPE_NORMAL;
516 switch (newPen.style()) {
518 props.dashStyle = D2D1_DASH_STYLE_SOLID;
522 case Qt::DashDotLine:
523 case Qt::DashDotDotLine:
525 if (newPen.widthF() <= 1.0)
526 props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_FLAT;
530 props.dashStyle = D2D1_DASH_STYLE_CUSTOM;
536 if (props.dashStyle == D2D1_DASH_STYLE_CUSTOM) {
537 QList<qreal> dashes = newPen.dashPattern();
538 QList<FLOAT> converted(dashes.size());
539 qreal penWidth = pen.qpen.widthF();
540 qreal brushWidth = 0;
541 for (
int i = 0; i < dashes.size(); i++) {
542 converted[i] = FLOAT(dashes[i]);
543 brushWidth += penWidth * dashes[i];
546 hr = factory()->CreateStrokeStyle(props, converted.constData(), UINT32(converted.size()), &pen.strokeStyle);
550 bitmap
.resize(int(ceil(brushWidth))
, int(ceil(penWidth))
);
552 bitmap.deviceContext()->get()->SetAntialiasMode(antialiasMode());
553 bitmap.deviceContext()->get()->SetTransform(D2D1::IdentityMatrix());
555 const qreal offsetX = (qreal(bitmap
.size().width()) - brushWidth) / 2;
556 const qreal offsetY = qreal(bitmap
.size().height()) / 2;
557 bitmap.deviceContext()->get()->DrawLine(D2D1::Point2F(FLOAT(offsetX), FLOAT(offsetY)),
558 D2D1::Point2F(FLOAT(brushWidth), FLOAT(offsetY)),
559 pen.brush.Get(), FLOAT(penWidth), pen.strokeStyle.Get());
561 D2D1_BITMAP_BRUSH_PROPERTIES1 bitmapBrushProperties = D2D1::BitmapBrushProperties1(
562 D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_CLAMP, D2D1_INTERPOLATION_MODE_LINEAR);
563 hr = dc()->CreateBitmapBrush(bitmap.bitmap(), bitmapBrushProperties, &pen.dashBrush);
564 pen.dashLength = bitmap.size().width();
566 hr = factory()->CreateStrokeStyle(props,
nullptr, 0, &pen.strokeStyle);
570 qWarning(
"%s: Could not create stroke style: %#lx",
__FUNCTION__, hr);
576 ComPtr<ID2D1Brush> result;
578 Q_ASSERT(needsEmulation);
580 *needsEmulation =
false;
582 switch (newBrush.style()) {
586 case Qt::SolidPattern:
588 ComPtr<ID2D1SolidColorBrush> solid;
590 hr = dc()->CreateSolidColorBrush(to_d2d_color_f(newBrush.color()), &solid);
592 qWarning(
"%s: Could not create solid color brush: %#lx",
__FUNCTION__, hr);
596 hr = solid.As(&result);
598 qWarning(
"%s: Could not convert solid color brush: %#lx",
__FUNCTION__, hr);
602 case Qt::Dense1Pattern:
603 case Qt::Dense2Pattern:
604 case Qt::Dense3Pattern:
605 case Qt::Dense4Pattern:
606 case Qt::Dense5Pattern:
607 case Qt::Dense6Pattern:
608 case Qt::Dense7Pattern:
611 case Qt::CrossPattern:
612 case Qt::BDiagPattern:
613 case Qt::FDiagPattern:
614 case Qt::DiagCrossPattern:
616 ComPtr<ID2D1BitmapBrush1> bitmapBrush;
617 D2D1_BITMAP_BRUSH_PROPERTIES1 bitmapBrushProperties = {
618 D2D1_EXTEND_MODE_WRAP,
619 D2D1_EXTEND_MODE_WRAP,
623 QImage brushImg = qt_imageForBrush(newBrush.style(),
false);
624 brushImg.setColor(0, newBrush.color().rgba());
625 brushImg.setColor(1, qRgba(0, 0, 0, 0));
628 bool success = bitmap.fromImage(brushImg, Qt::AutoColor);
630 qWarning(
"%s: Could not create Direct2D bitmap from Qt pattern brush image",
__FUNCTION__);
635 bitmapBrushProperties,
638 qWarning(
"%s: Could not create Direct2D bitmap brush for Qt pattern brush: %#lx",
__FUNCTION__, hr);
642 hr = bitmapBrush.As(&result);
644 qWarning(
"%s: Could not convert Direct2D bitmap brush for Qt pattern brush: %#lx",
__FUNCTION__, hr);
648 case Qt::LinearGradientPattern:
649 if (newBrush.gradient()->spread() != QGradient::PadSpread) {
650 *needsEmulation =
true;
652 ComPtr<ID2D1LinearGradientBrush> linear;
653 const auto *qlinear =
static_cast<
const QLinearGradient *>(newBrush.gradient());
655 D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES linearGradientBrushProperties;
656 ComPtr<ID2D1GradientStopCollection> gradientStopCollection;
658 linearGradientBrushProperties.startPoint = to_d2d_point_2f(qlinear->start());
659 linearGradientBrushProperties.endPoint = to_d2d_point_2f(qlinear->finalStop());
661 const QList<D2D1_GRADIENT_STOP> stops = qGradientStopsToD2DStops(qlinear->stops());
663 hr =
dc()->CreateGradientStopCollection(stops.constData(),
664 UINT32(stops.size()),
665 &gradientStopCollection);
667 qWarning(
"%s: Could not create gradient stop collection for linear gradient: %#lx",
__FUNCTION__, hr);
671 hr =
dc()->CreateLinearGradientBrush(linearGradientBrushProperties, gradientStopCollection.Get(),
674 qWarning(
"%s: Could not create Direct2D linear gradient brush: %#lx",
__FUNCTION__, hr);
678 hr = linear.As(&result);
680 qWarning(
"%s: Could not convert Direct2D linear gradient brush: %#lx",
__FUNCTION__, hr);
686 case Qt::RadialGradientPattern:
687 if (newBrush.gradient()->spread() != QGradient::PadSpread) {
688 *needsEmulation =
true;
690 ComPtr<ID2D1RadialGradientBrush> radial;
691 const auto *qradial =
static_cast<
const QRadialGradient *>(newBrush.gradient());
693 D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES radialGradientBrushProperties;
694 ComPtr<ID2D1GradientStopCollection> gradientStopCollection;
696 radialGradientBrushProperties.center = to_d2d_point_2f(qradial->center());
697 radialGradientBrushProperties.gradientOriginOffset = to_d2d_point_2f(qradial->focalPoint() - qradial->center());
698 radialGradientBrushProperties.radiusX = FLOAT(qradial->radius());
699 radialGradientBrushProperties.radiusY = FLOAT(qradial->radius());
701 const QList<D2D1_GRADIENT_STOP> stops = qGradientStopsToD2DStops(qradial->stops());
703 hr =
dc()->CreateGradientStopCollection(stops.constData(), stops.size(), &gradientStopCollection);
705 qWarning(
"%s: Could not create gradient stop collection for radial gradient: %#lx",
__FUNCTION__, hr);
709 hr =
dc()->CreateRadialGradientBrush(radialGradientBrushProperties, gradientStopCollection.Get(),
712 qWarning(
"%s: Could not create Direct2D radial gradient brush: %#lx",
__FUNCTION__, hr);
718 qWarning(
"%s: Could not convert Direct2D radial gradient brush: %#lx",
__FUNCTION__, hr);
724 case Qt::ConicalGradientPattern:
725 *needsEmulation =
true;
728 case Qt::TexturePattern:
730 ComPtr<ID2D1BitmapBrush1> bitmapBrush;
731 D2D1_BITMAP_BRUSH_PROPERTIES1 bitmapBrushProperties = {
732 D2D1_EXTEND_MODE_WRAP,
733 D2D1_EXTEND_MODE_WRAP,
740 bitmapBrushProperties,
744 qWarning(
"%s: Could not create texture brush: %#lx",
__FUNCTION__, hr);
748 hr = bitmapBrush.As(&result);
750 qWarning(
"%s: Could not convert texture brush: %#lx",
__FUNCTION__, hr);
755 if (result && !newBrush.transform().isIdentity())
756 result->SetTransform(to_d2d_matrix_3x2_f(newBrush.transform()));
763 Q_Q(QWindowsDirect2DPaintEngine);
765 const bool alias = !q->antiAliasingEnabled();
767 QVectorPath::CacheEntry *cacheEntry = path.isCacheable() ? path.lookupCacheData(q)
772 if (alias && e->aliased)
774 else if (!alias && e->antiAliased)
775 return e->antiAliased;
784 writer.setPositiveSlopeAdjustmentEnabled(path.shape() == QVectorPath::LinesHint
785 || path.shape() == QVectorPath::PolygonHint);
787 const QPainterPath::ElementType *types = path.elements();
788 const int count = path.elementCount();
789 const qreal *points = path.points();
796 for (
int i = 0; i < count; i++) {
798 y = points[i * 2 + 1];
801 case QPainterPath::MoveToElement:
802 writer.moveTo(QPointF(x, y));
805 case QPainterPath::LineToElement:
806 writer.lineTo(QPointF(x, y));
809 case QPainterPath::CurveToElement:
811 Q_ASSERT((i + 2) < count);
812 Q_ASSERT(types[i+1] == QPainterPath::CurveToDataElement);
813 Q_ASSERT(types[i+2] == QPainterPath::CurveToDataElement);
816 const qreal x2 = points[i * 2];
817 const qreal y2 = points[i * 2 + 1];
820 const qreal x3 = points[i * 2];
821 const qreal y3 = points[i * 2 + 1];
823 writer.curveTo(QPointF(x, y), QPointF(x2, y2), QPointF(x3, y3));
827 case QPainterPath::CurveToDataElement:
828 qWarning(
"%s: Unhandled Curve Data Element",
__FUNCTION__);
833 writer.moveTo(QPointF(points[0], points[1]));
834 for (
int i = 1; i < count; i++)
835 writer.lineTo(QPointF(points[i * 2], points[i * 2 + 1]));
839 if (path.hasImplicitClose())
840 writer.lineTo(QPointF(points[0], points[1]));
843 ComPtr<ID2D1PathGeometry1> geometry = writer.geometry();
845 if (path.isCacheable()) {
847 cacheEntry = path.addCacheData(q,
new D2DVectorPathCache, D2DVectorPathCache::cleanup_func);
851 e->aliased = geometry;
853 e->antiAliased = geometry;
855 path.makeCacheable();
863 dc()->SetAntialiasMode(antialiasMode());
867 IDWriteFontFace *fontFace,
868 const QFontDef &fontDef,
870 const UINT16 *glyphIndices,
871 const FLOAT *glyphAdvances,
872 const DWRITE_GLYPH_OFFSET *glyphOffsets,
875 Q_Q(QWindowsDirect2DPaintEngine);
877 DWRITE_GLYPH_RUN glyphRun = {
879 FLOAT(fontDef.pixelSize),
888 const bool antiAlias =
bool((q->state()->renderHints & QPainter::TextAntialiasing)
889 && !(fontDef.styleStrategy & QFont::NoAntialias));
890 const D2D1_TEXT_ANTIALIAS_MODE antialiasMode = (flags & QWindowsDirect2DPaintEngine::TranslucentTopLevelWindow)
891 ? D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE : D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
892 dc()->SetTextAntialiasMode(antiAlias ? antialiasMode : D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
894 dc()->DrawGlyphRun(pos,
898 DWRITE_MEASURING_MODE_GDI_CLASSIC);
903 Q_Q(QWindowsDirect2DPaintEngine);
906 if (!(path.shape() == QVectorPath::LinesHint || path.shape() == QVectorPath::PolygonHint)
908 || q->state()->renderHints.testFlag(QPainter::Antialiasing)) {
909 ComPtr<ID2D1Geometry> geometry = vectorPathToID2D1PathGeometry(path);
911 qWarning(
"%s: Could not convert path to d2d geometry",
__FUNCTION__);
914 dc()->DrawGeometry(geometry.Get(), pen.brush.Get(),
915 FLOAT(pen.qpen.widthF()), pen.strokeStyle.Get());
920 const bool isPolygon = path.shape() == QVectorPath::PolygonHint && path.elementCount() >= 3;
921 const bool implicitClose = isPolygon && (path.hints() & QVectorPath::ImplicitClose);
922 const bool skipJoin = !isPolygon
923 || (pen.qpen.joinStyle() == Qt::MiterJoin && qFuzzyIsNull(pen.qpen.miterLimit()));
924 const qreal *points = path.points();
925 const int lastElement = path.elementCount() - (implicitClose ? 1 : 2);
926 qreal dashOffset = 0;
928 ID2D1Brush *brush = pen.dashBrush ? pen.dashBrush.Get() : pen.brush.Get();
929 for (
int i = 0; i <= lastElement; ++i) {
930 QPointF p1(points[i * 2], points[i * 2 + 1]);
931 QPointF p2 = implicitClose && i == lastElement ? QPointF(points[0], points[1])
932 : QPointF(points[i * 2 + 2], points[i * 2 + 3]);
937 if (p1 == p2 && pen.qpen.widthF() <= 1.0) {
938 q->fillRect(QRectF(p1, QSizeF(pen.qpen.widthF(), pen.qpen.widthF())), pen.qpen.brush());
942 if (!q->antiAliasingEnabled())
943 adjustLine(&p1, &p2);
945 q->adjustForAliasing(&p1);
946 q->adjustForAliasing(&p2);
948 const QLineF line(p1, p2);
949 const qreal lineLength = line.length();
951 pen.dashBrush->SetTransform(transformFromLine(line, pen.qpen.widthF(), dashOffset));
952 dashOffset = pen.dashLength - fmod(lineLength - dashOffset, pen.dashLength);
954 dc()->DrawLine(to_d2d_point_2f(p1), to_d2d_point_2f(p2),
955 brush, FLOAT(pen.qpen.widthF()),
nullptr);
961 const qreal patchSegment = pen.dashBrush ? qBound(0.0, (pen.dashLength - dashOffset) / lineLength, 1.0)
966 writer.moveTo(jointStart);
968 writer.lineTo(line.pointAt(patchSegment));
970 dc()->DrawGeometry(writer.geometry().Get(), pen.brush.Get(),
971 FLOAT(pen.qpen.widthF()), pen.strokeStyle.Get());
974 jointStart = line.pointAt(1 - patchSegment);
976 if (implicitClose && i == lastElement) {
979 writer.moveTo(jointStart);
981 writer.lineTo(QLineF(p2, QPointF(points[2], points[3])).pointAt(patchSegment));
983 dc()->DrawGeometry(writer.geometry().Get(), pen.brush.Get(),
984 FLOAT(pen.qpen.widthF()), pen.strokeStyle.Get());
991 const QFontDef fontDef = fe->fontDef;
992 ComPtr<IDWriteFontFace> fontFace = fontCache.value(fontDef);
996 LOGFONT lf = QWindowsFontDatabase::fontDefToLOGFONT(fontDef, QString());
999 static const char keyC[] =
"HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes";
1000 const QString familyName = QString::fromWCharArray(lf.lfFaceName);
1001 const QString nameSubstitute = QSettings(QLatin1StringView(keyC), QSettings::NativeFormat).value(familyName, familyName).toString();
1002 if (nameSubstitute != familyName) {
1003 const int nameSubstituteLength = qMin(nameSubstitute.length(), LF_FACESIZE - 1);
1004 memcpy(lf.lfFaceName, nameSubstitute.data(), size_t(nameSubstituteLength) *
sizeof(
wchar_t));
1005 lf.lfFaceName[nameSubstituteLength] = 0;
1008 ComPtr<IDWriteFont> dwriteFont;
1011 qDebug(
"%s: CreateFontFromLOGFONT failed: %#lx",
__FUNCTION__, hr);
1015 hr = dwriteFont->CreateFontFace(&fontFace);
1017 qDebug(
"%s: CreateFontFace failed: %#lx",
__FUNCTION__, hr);
1022 fontCache.insert(fontDef, fontFace);
1029 : QPaintEngineEx(*(
new QWindowsDirect2DPaintEnginePrivate(bitmap, flags)))
1031 QPaintEngine::PaintEngineFeatures unsupported =
1034 QPaintEngine::PorterDuff
1035 | QPaintEngine::BlendModes
1036 | QPaintEngine::RasterOpModes
1042 | QPaintEngine::PerspectiveTransform;
1044 gccaps &= ~unsupported;
1049 Q_D(QWindowsDirect2DPaintEngine);
1051 d->bitmap->deviceContext()->begin();
1052 d->dc()->SetTransform(D2D1::Matrix3x2F::Identity());
1054 if (systemClip().rectCount() > 1) {
1056 p.addRegion(systemClip());
1058 ComPtr<ID2D1PathGeometry1> geometry = d->vectorPathToID2D1PathGeometry(qtVectorPathForPath(p));
1062 d->dc()->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(),
1065 D2D1::IdentityMatrix(),
1071 QRect clip(0, 0, pdev->width(), pdev->height());
1072 if (!systemClip().isEmpty())
1073 clip &= systemClip().boundingRect();
1074 d->dc()->PushAxisAlignedClip(to_d2d_rect_f(clip), D2D1_ANTIALIAS_MODE_ALIASED);
1075 d->clipFlags |= SimpleSystemClip;
1078 D2D_TAG(D2DDebugDrawInitialStateTag);
1086 Q_D(QWindowsDirect2DPaintEngine);
1089 const bool emulatingComposition = d->flags.testFlag(EmulateComposition);
1090 d->flags &= ~QWindowsDirect2DPaintEngine::EmulateComposition;
1091 if (!d->fallbackImage.isNull()) {
1092 if (emulatingComposition)
1093 drawImage(d->fallbackImage.rect(), d->fallbackImage, d->fallbackImage.rect());
1094 d->fallbackImage = QImage();
1100 if (d->clipFlags & SimpleSystemClip) {
1101 d->dc()->PopAxisAlignedClip();
1102 d->clipFlags &= ~SimpleSystemClip;
1104 d->dc()->PopLayer();
1107 return d->bitmap->deviceContext()->end();
1112 return QPaintEngine::Direct2D;
1117 Q_D(QWindowsDirect2DPaintEngine);
1119 QPaintEngineEx::setState(s);
1134 const QBrush &brush = state()->brush;
1135 if (qbrush_style(brush) != Qt::NoBrush) {
1136 if (emulationRequired(BrushEmulation))
1137 rasterFill(path, brush);
1142 const QPen &pen = state()->pen;
1143 if (qpen_style(pen) != Qt::NoPen && qbrush_style(qpen_brush(pen)) != Qt::NoBrush) {
1144 if (emulationRequired(PenEmulation))
1145 QPaintEngineEx::stroke(path, pen);
1153 Q_D(QWindowsDirect2DPaintEngine);
1160 if (emulationRequired(BrushEmulation)) {
1161 rasterFill(path, brush);
1165 if (!d->brush.brush)
1168 ComPtr<ID2D1Geometry> geometry = d->vectorPathToID2D1PathGeometry(path);
1170 qWarning(
"%s: Could not convert path to d2d geometry",
__FUNCTION__);
1174 d->dc()->FillGeometry(geometry.Get(), d->brush.brush.Get());
1179 Q_D(QWindowsDirect2DPaintEngine);
1186 if (emulationRequired(PenEmulation)) {
1187 QPaintEngineEx::stroke(path, pen);
1199 Q_D(QWindowsDirect2DPaintEngine);
1205 Q_D(QWindowsDirect2DPaintEngine);
1206 d->updateClipEnabled(state()->clipEnabled);
1211 Q_D(QWindowsDirect2DPaintEngine);
1212 d->updatePen(state()->pen);
1217 Q_D(QWindowsDirect2DPaintEngine);
1218 d->updateBrush(state()->brush);
1223 Q_D(QWindowsDirect2DPaintEngine);
1224 d->updateBrushOrigin(state()->brushOrigin);
1229 Q_D(QWindowsDirect2DPaintEngine);
1230 d->updateOpacity(state()->opacity);
1235 Q_D(QWindowsDirect2DPaintEngine);
1236 d->updateCompositionMode(state()->compositionMode());
1241 Q_D(QWindowsDirect2DPaintEngine);
1247 Q_D(QWindowsDirect2DPaintEngine);
1248 d->updateTransform(state()->transform());
1253 Q_D(QWindowsDirect2DPaintEngine);
1258 if (emulationRequired(BrushEmulation)) {
1259 QPaintEngineEx::fillRect(rect, brush);
1261 QRectF r = rect.normalized();
1262 adjustForAliasing(&r);
1265 d->dc()->FillRectangle(to_d2d_rect_f(rect), d->brush.brush.Get());
1271 Q_D(QWindowsDirect2DPaintEngine);
1272 D2D_TAG(D2DDebugDrawRectsTag);
1277 if (emulationRequired(BrushEmulation) || emulationRequired(PenEmulation)) {
1278 QPaintEngineEx::drawRects(rects, rectCount);
1281 for (
int i = 0; i < rectCount; i++) {
1282 rect = rects[i].normalized();
1283 adjustForAliasing(&rect);
1285 D2D1_RECT_F d2d_rect = to_d2d_rect_f(rect);
1288 d->dc()->FillRectangle(d2d_rect, d->brush.brush.Get());
1291 d->dc()->DrawRectangle(d2d_rect, d->pen.brush.Get(),
1292 FLOAT(d->pen.qpen.widthF()), d->pen.strokeStyle.Get());
1299 Q_D(QWindowsDirect2DPaintEngine);
1300 D2D_TAG(D2DDebugDrawRectFsTag);
1305 if (emulationRequired(BrushEmulation) || emulationRequired(PenEmulation)) {
1306 QPaintEngineEx::drawRects(rects, rectCount);
1309 for (
int i = 0; i < rectCount; i++) {
1310 rect = rects[i].normalized();
1311 adjustForAliasing(&rect);
1313 D2D1_RECT_F d2d_rect = to_d2d_rect_f(rect);
1316 d->dc()->FillRectangle(d2d_rect, d->brush.brush.Get());
1319 d->dc()->DrawRectangle(d2d_rect, d->pen.brush.Get(),
1320 FLOAT(d->pen.qpen.widthF()), d->pen.strokeStyle.Get());
1327 if (p2.x() > p1.x())
1328 return p2.y() < p1.y();
1330 if (p1.x() > p2.x())
1331 return p1.y() < p2.y();
1338 if (isLinePositivelySloped(*p1, *p2)) {
1339 p1->ry() -= qreal(1.0);
1340 p2->ry() -= qreal(1.0);
1346 Q_D(QWindowsDirect2DPaintEngine);
1347 D2D_TAG(D2DDebugDrawEllipseFTag);
1352 if (emulationRequired(BrushEmulation) || emulationRequired(PenEmulation)) {
1353 QPaintEngineEx::drawEllipse(r);
1355 QPointF p = r.center();
1356 adjustForAliasing(&p);
1358 D2D1_ELLIPSE ellipse = {
1360 FLOAT(r.width() / 2.0),
1361 FLOAT(r.height() / 2.0)
1365 d->dc()->FillEllipse(ellipse, d->brush.brush.Get());
1368 d->dc()->DrawEllipse(ellipse, d->pen.brush.Get(),
1369 FLOAT(d->pen.qpen.widthF()),
1370 d->pen.strokeStyle.Get());
1376 Q_D(QWindowsDirect2DPaintEngine);
1377 D2D_TAG(D2DDebugDrawEllipseTag);
1382 if (emulationRequired(BrushEmulation) || emulationRequired(PenEmulation)) {
1383 QPaintEngineEx::drawEllipse(r);
1385 QPointF p = r.center();
1386 adjustForAliasing(&p);
1388 D2D1_ELLIPSE ellipse = {
1390 FLOAT(r.width() / 2.0),
1391 FLOAT(r.height() / 2.0)
1395 d->dc()->FillEllipse(ellipse, d->brush.brush.Get());
1398 d->dc()->DrawEllipse(ellipse, d->pen.brush.Get(),
1399 FLOAT(d->pen.qpen.widthF()),
1400 d->pen.strokeStyle.Get());
1405 const QRectF &sr, Qt::ImageConversionFlags flags)
1407 Q_D(QWindowsDirect2DPaintEngine);
1408 D2D_TAG(D2DDebugDrawImageTag);
1410 QPixmap pixmap = QPixmap::fromImage(image, flags);
1411 drawPixmap(rectangle, pixmap, sr);
1418 Q_D(QWindowsDirect2DPaintEngine);
1419 D2D_TAG(D2DDebugDrawPixmapTag);
1424 if (pm.handle()->pixelType() == QPlatformPixmap::BitmapType) {
1425 QImage i = pm.toImage();
1426 i.setColor(0, qRgba(0, 0, 0, 0));
1427 i.setColor(1, d->pen.qpen.color().rgba());
1428 drawImage(r, i, sr);
1432 if (d->flags.testFlag(EmulateComposition)) {
1433 const qreal points[] = {
1435 r.x() + r.width(), r.y(),
1436 r.x() + r.width(), r.y() + r.height(),
1437 r.x(), r.y() + r.height()
1439 const QVectorPath vp(points, 4,
nullptr, QVectorPath::RectangleHint);
1440 QBrush brush(sr.isValid() ? pm.copy(sr.toRect()) : pm);
1441 brush.setTransform(QTransform::fromTranslate(r.x(), r.y()));
1442 rasterFill(vp, brush);
1451 if (bitmap->bitmap() != d->bitmap->bitmap()) {
1454 d->dc()->DrawBitmap(bitmap->bitmap(),
1455 to_d2d_rect_f(r), FLOAT(state()->opacity),
1456 d->interpolationMode(),
1459 d->dc()->DrawBitmap(bitmap->bitmap(),
1460 to_d2d_rect_f(r), FLOAT(state()->opacity),
1461 d->interpolationMode());
1469 bool r = intermediate
.resize(int(sr.width())
, int(sr.height())
);
1471 qWarning(
"%s: Could not resize intermediate bitmap to source rect size",
__FUNCTION__);
1475 D2D1_RECT_U d2d_sr = to_d2d_rect_u(sr.toRect());
1476 HRESULT hr = intermediate
.bitmap()->CopyFromBitmap(
nullptr,
1480 qWarning(
"%s: Could not copy source rect area from source bitmap to intermediate bitmap: %#lx",
__FUNCTION__, hr);
1487 qWarning(
"%s: Could not resize intermediate bitmap to source bitmap size",
__FUNCTION__);
1491 HRESULT hr = intermediate
.bitmap()->CopyFromBitmap(
nullptr,
1495 qWarning(
"%s: Could not copy source bitmap to intermediate bitmap: %#lx",
__FUNCTION__, hr);
1500 d->dc()->DrawBitmap(intermediate.bitmap(),
1501 to_d2d_rect_f(r), FLOAT(state()->opacity),
1502 d->interpolationMode());
1508 Q_D(QWindowsDirect2DPaintEngine);
1509 D2D_TAG(D2DDebugDrawStaticTextItemTag);
1511 if (staticTextItem->numGlyphs == 0)
1517 if (emulationRequired(PenEmulation)) {
1518 QPaintEngineEx::drawStaticTextItem(staticTextItem);
1522 ComPtr<IDWriteFontFace> fontFace = d->fontFaceFromFontEngine(staticTextItem->fontEngine());
1524 qWarning(
"%s: Could not find font - falling back to slow text rendering path.",
__FUNCTION__);
1525 QPaintEngineEx::drawStaticTextItem(staticTextItem);
1529 QVarLengthArray<UINT16> glyphIndices(staticTextItem->numGlyphs);
1530 QVarLengthArray<FLOAT> glyphAdvances(staticTextItem->numGlyphs);
1531 QVarLengthArray<DWRITE_GLYPH_OFFSET> glyphOffsets(staticTextItem->numGlyphs);
1533 for (
int i = 0; i < staticTextItem->numGlyphs; i++) {
1534 glyphIndices[i] = UINT16(staticTextItem->glyphs[i]);
1537 glyphAdvances[i] = 0;
1538 glyphOffsets[i].advanceOffset = FLOAT(staticTextItem->glyphPositions[i].x.toReal());
1540 glyphOffsets[i].ascenderOffset = FLOAT(staticTextItem->glyphPositions[i].y.toReal() * -1);
1543 d->drawGlyphRun(D2D1::Point2F(0, 0),
1545 staticTextItem->fontEngine()->fontDef,
1546 staticTextItem->numGlyphs,
1547 glyphIndices.constData(),
1548 glyphAdvances.constData(),
1549 glyphOffsets.constData(),
1555 Q_D(QWindowsDirect2DPaintEngine);
1556 D2D_TAG(D2DDebugDrawTextItemTag);
1558 const auto &ti =
static_cast<
const QTextItemInt &>(textItem);
1559 if (ti.glyphs.numGlyphs == 0)
1565 if (emulationRequired(PenEmulation)) {
1566 QPaintEngine::drawTextItem(p, textItem);
1570 ComPtr<IDWriteFontFace> fontFace = d->fontFaceFromFontEngine(ti.fontEngine);
1572 qWarning(
"%s: Could not find font - falling back to slow text rendering path.",
__FUNCTION__);
1573 QPaintEngine::drawTextItem(p, textItem);
1577 QVarLengthArray<UINT16> glyphIndices(ti.glyphs.numGlyphs);
1578 QVarLengthArray<FLOAT> glyphAdvances(ti.glyphs.numGlyphs);
1579 QVarLengthArray<DWRITE_GLYPH_OFFSET> glyphOffsets(ti.glyphs.numGlyphs);
1581 for (
int i = 0; i < ti.glyphs.numGlyphs; i++) {
1582 glyphIndices[i] = UINT16(ti.glyphs.glyphs[i]);
1583 glyphAdvances[i] = FLOAT(ti.glyphs.effectiveAdvance(i).toReal());
1584 glyphOffsets[i].advanceOffset = FLOAT(ti.glyphs.offsets[i].x.toReal());
1587 glyphOffsets[i].ascenderOffset = FLOAT(ti.glyphs.offsets[i].y.toReal());
1590 const bool rtl = (ti.flags & QTextItem::RightToLeft);
1591 const QPointF offset(rtl ? ti.width.toReal() : 0, 0);
1593 d->drawGlyphRun(to_d2d_point_2f(p + offset),
1595 ti.fontEngine->fontDef,
1596 ti.glyphs.numGlyphs,
1597 glyphIndices.constData(),
1598 glyphAdvances.constData(),
1599 glyphOffsets.constData(),
1605 ensureBrush(state()->brush);
1610 Q_D(QWindowsDirect2DPaintEngine);
1611 d->updateBrush(brush);
1616 ensurePen(state()->pen);
1621 Q_D(QWindowsDirect2DPaintEngine);
1627 Q_D(QWindowsDirect2DPaintEngine);
1629 if (d->fallbackImage.isNull()) {
1630 if (d->flags.testFlag(EmulateComposition)) {
1632 d->fallbackImage = d->bitmap->toImage();
1634 d->fallbackImage = QImage(d->bitmap->size(), QImage::Format_ARGB32_Premultiplied);
1635 d->fallbackImage.fill(Qt::transparent);
1639 QImage &img = d->fallbackImage;
1641 QPaintEngine *engine = img.paintEngine();
1643 if (engine->isExtended() && p.begin(&img)) {
1644 p.setRenderHints(state()->renderHints);
1645 p.setCompositionMode(state()->compositionMode());
1646 p.setOpacity(state()->opacity);
1647 p.setBrushOrigin(state()->brushOrigin);
1648 p.setBrush(state()->brush);
1649 p.setPen(state()->pen);
1651 auto *extended =
static_cast<QPaintEngineEx *>(engine);
1652 for (
const QPainterClipInfo &info : std::as_const(state()->clipInfo)) {
1653 extended->state()->matrix = info.matrix;
1654 extended->transformChanged();
1656 switch (info.clipType) {
1657 case QPainterClipInfo::RegionClip:
1658 extended->clip(info.region, info.operation);
1660 case QPainterClipInfo::PathClip:
1661 extended->clip(info.path, info.operation);
1663 case QPainterClipInfo::RectClip:
1664 extended->clip(info.rect, info.operation);
1666 case QPainterClipInfo::RectFClip:
1667 qreal right = info.rectf.x() + info.rectf.width();
1668 qreal bottom = info.rectf.y() + info.rectf.height();
1669 qreal pts[] = { info.rectf.x(), info.rectf.y(),
1670 right, info.rectf.y(),
1672 info.rectf.x(), bottom };
1673 QVectorPath vp(pts, 4,
nullptr, QVectorPath::RectangleHint);
1674 extended->clip(vp, info.operation);
1679 extended->state()->matrix = state()->matrix;
1680 extended->transformChanged();
1682 extended->fill(path, brush);
1684 qWarning(
"%s: Paint Engine end returned false",
__FUNCTION__);
1686 if (!d->flags.testFlag(EmulateComposition)) {
1687 d->updateClipEnabled(
false);
1688 d->updateTransform(QTransform());
1689 drawImage(img.rect(), img, img.rect());
1690 d->fallbackImage = QImage();
1695 qWarning(
"%s: Could not fall back to QImage",
__FUNCTION__);
1701 Q_D(
const QWindowsDirect2DPaintEngine);
1703 if (d->flags.testFlag(EmulateComposition))
1706 if (!state()->matrix.isAffine())
1711 return d->pen.emulate;
1713 case BrushEmulation:
1714 return d->brush.emulate;
1723 return state()->renderHints & QPainter::Antialiasing;
1728 if (!antiAliasingEnabled()) {
1729 rect->adjust(MAGICAL_ALIASING_OFFSET,
1730 MAGICAL_ALIASING_OFFSET,
1731 MAGICAL_ALIASING_OFFSET,
1732 MAGICAL_ALIASING_OFFSET);
1739 MAGICAL_ALIASING_OFFSET);
1741 if (!antiAliasingEnabled())
1742 (*point) += adjustment;
1752 begin(paintDevice());
1771 , m_active(engine->isActive())
1774 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