summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp')
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp234
1 files changed, 189 insertions, 45 deletions
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp
index 1d28befe41..c13f0f333a 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp
@@ -5,36 +5,28 @@
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
** $QT_END_LICENSE$
**
****************************************************************************/
@@ -51,6 +43,7 @@
#include "qwindowsfontdatabase.h"
#include "qwindowsintegration.h"
+#include <QtCore/QtMath>
#include <QtCore/QStack>
#include <QtCore/QSettings>
#include <QtGui/private/qpaintengine_p.h>
@@ -109,11 +102,14 @@ static inline ID2D1Factory1 *factory()
return QWindowsDirect2DContext::instance()->d2dFactory();
}
-inline static FLOAT pixelSizeToDIP(int pixelSize)
+static inline D2D1_MATRIX_3X2_F transformFromLine(const QLineF &line, qreal penWidth)
{
- FLOAT dpiX, dpiY;
- QWindowsDirect2DContext::instance()->d2dFactory()->GetDesktopDpi(&dpiX, &dpiY);
- return FLOAT(pixelSize) * 96.0f / dpiY;
+ const qreal halfWidth = penWidth / 2;
+ const qreal angle = -qDegreesToRadians(line.angle());
+ QTransform transform = QTransform::fromTranslate(line.p1().x() + qSin(angle) * halfWidth,
+ line.p1().y() - qCos(angle) * halfWidth);
+ transform.rotateRadians(angle);
+ return to_d2d_matrix_3x2_f(transform);
}
class Direct2DPathGeometryWriter
@@ -245,6 +241,7 @@ public:
}
QWindowsDirect2DBitmap *bitmap;
+ QImage fallbackImage;
unsigned int clipFlags;
QStack<ClipType> pushedClips;
@@ -259,12 +256,14 @@ public:
QPen qpen;
ComPtr<ID2D1Brush> brush;
ComPtr<ID2D1StrokeStyle1> strokeStyle;
+ ComPtr<ID2D1BitmapBrush1> dashBrush;
inline void reset() {
emulate = false;
qpen = QPen();
brush.Reset();
strokeStyle.Reset();
+ dashBrush.Reset();
}
} pen;
@@ -300,6 +299,14 @@ public:
: D2D1_ANTIALIAS_MODE_ALIASED;
}
+ inline D2D1_LAYER_OPTIONS1 layerOptions() const
+ {
+ if (flags & QWindowsDirect2DPaintEngine::TranslucentTopLevelWindow)
+ return D2D1_LAYER_OPTIONS1_NONE;
+ else
+ return D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
+ }
+
void updateTransform(const QTransform &transform)
{
dc()->SetTransform(to_d2d_matrix_3x2_f(transform));
@@ -345,7 +352,7 @@ public:
D2D1::IdentityMatrix(),
1.0,
NULL,
- D2D1_LAYER_OPTIONS1_NONE),
+ layerOptions()),
NULL);
pushedClips.push(LayerClip);
}
@@ -400,7 +407,10 @@ public:
break;
default:
- qWarning("Unsupported composition mode: %d", mode);
+ // Activating an unsupported mode at any time will cause the QImage
+ // fallback to be used for the remainder of the active paint session
+ dc()->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
+ flags |= QWindowsDirect2DPaintEngine::EmulateComposition;
break;
}
}
@@ -531,12 +541,31 @@ public:
if (props.dashStyle == D2D1_DASH_STYLE_CUSTOM) {
QVector<qreal> dashes = newPen.dashPattern();
QVector<FLOAT> converted(dashes.size());
-
+ qreal penWidth = pen.qpen.widthF();
+ qreal brushWidth = 0;
for (int i = 0; i < dashes.size(); i++) {
converted[i] = dashes[i];
+ brushWidth += penWidth * dashes[i];
}
hr = factory()->CreateStrokeStyle(props, converted.constData(), converted.size(), &pen.strokeStyle);
+
+ // Create a combined brush/dash pattern for optimized line drawing
+ QWindowsDirect2DBitmap bitmap;
+ bitmap.resize(ceil(brushWidth), ceil(penWidth));
+ bitmap.deviceContext()->begin();
+ bitmap.deviceContext()->get()->SetAntialiasMode(antialiasMode());
+ bitmap.deviceContext()->get()->SetTransform(D2D1::IdentityMatrix());
+ bitmap.deviceContext()->get()->Clear();
+ const qreal offsetX = (qreal(bitmap.size().width()) - brushWidth) / 2;
+ const qreal offsetY = qreal(bitmap.size().height()) / 2;
+ bitmap.deviceContext()->get()->DrawLine(D2D1::Point2F(offsetX, offsetY),
+ D2D1::Point2F(brushWidth, offsetY),
+ pen.brush.Get(), penWidth, pen.strokeStyle.Get());
+ bitmap.deviceContext()->end();
+ D2D1_BITMAP_BRUSH_PROPERTIES1 bitmapBrushProperties = D2D1::BitmapBrushProperties1(
+ D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_CLAMP, D2D1_INTERPOLATION_MODE_LINEAR);
+ hr = dc()->CreateBitmapBrush(bitmap.bitmap(), bitmapBrushProperties, &pen.dashBrush);
} else {
hr = factory()->CreateStrokeStyle(props, NULL, 0, &pen.strokeStyle);
}
@@ -858,19 +887,19 @@ public:
Q_Q(QWindowsDirect2DPaintEngine);
DWRITE_GLYPH_RUN glyphRun = {
- fontFace, // IDWriteFontFace *fontFace;
- pixelSizeToDIP(fontDef.pixelSize), // 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;
+ fontFace, // IDWriteFontFace *fontFace;
+ fontDef.pixelSize, // 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((q->state()->renderHints & QPainter::TextAntialiasing)
&& !(fontDef.styleStrategy & QFont::NoAntialias));
- const D2D1_TEXT_ANTIALIAS_MODE antialiasMode = (flags & QWindowsDirect2DPaintEngine::UseGrayscaleAntialiasing)
+ const D2D1_TEXT_ANTIALIAS_MODE antialiasMode = (flags & QWindowsDirect2DPaintEngine::TranslucentTopLevelWindow)
? D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE : D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
dc()->SetTextAntialiasMode(antiAlias ? antialiasMode : D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
@@ -956,7 +985,7 @@ bool QWindowsDirect2DPaintEngine::begin(QPaintDevice * pdev)
D2D1::IdentityMatrix(),
1.0,
NULL,
- D2D1_LAYER_OPTIONS1_NONE),
+ d->layerOptions()),
NULL);
} else {
QRect clip(0, 0, pdev->width(), pdev->height());
@@ -975,7 +1004,17 @@ bool QWindowsDirect2DPaintEngine::begin(QPaintDevice * pdev)
bool QWindowsDirect2DPaintEngine::end()
{
Q_D(QWindowsDirect2DPaintEngine);
- // First pop any user-applied clipping
+
+ // Always clear all emulation-related things so we are in a clean state for our next painting run
+ const bool emulatingComposition = d->flags.testFlag(EmulateComposition);
+ d->flags &= ~QWindowsDirect2DPaintEngine::EmulateComposition;
+ if (!d->fallbackImage.isNull()) {
+ if (emulatingComposition)
+ drawImage(d->fallbackImage.rect(), d->fallbackImage, d->fallbackImage.rect());
+ d->fallbackImage = QImage();
+ }
+
+ // Pop any user-applied clipping
d->clearClips();
// Now the system clip from begin() above
if (d->clipFlags & SimpleSystemClip) {
@@ -984,6 +1023,7 @@ bool QWindowsDirect2DPaintEngine::end()
} else {
d->dc()->PopLayer();
}
+
return d->bitmap->deviceContext()->end();
}
@@ -1288,7 +1328,12 @@ void QWindowsDirect2DPaintEngine::drawLines(const QLine *lines, int lineCount)
D2D1_POINT_2F d2d_p1 = to_d2d_point_2f(p1);
D2D1_POINT_2F d2d_p2 = to_d2d_point_2f(p2);
- d->dc()->DrawLine(d2d_p1, d2d_p2, d->pen.brush.Get(), d->pen.qpen.widthF(), d->pen.strokeStyle.Get());
+ if (!d->pen.dashBrush || state()->renderHints.testFlag(QPainter::HighQualityAntialiasing)) {
+ d->dc()->DrawLine(d2d_p1, d2d_p2, d->pen.brush.Get(), d->pen.qpen.widthF(), d->pen.strokeStyle.Get());
+ } else {
+ d->pen.dashBrush->SetTransform(transformFromLine(lines[i], d->pen.qpen.widthF()));
+ d->dc()->DrawLine(d2d_p1, d2d_p2, d->pen.dashBrush.Get(), d->pen.qpen.widthF(), NULL);
+ }
}
}
}
@@ -1324,7 +1369,12 @@ void QWindowsDirect2DPaintEngine::drawLines(const QLineF *lines, int lineCount)
D2D1_POINT_2F d2d_p1 = to_d2d_point_2f(p1);
D2D1_POINT_2F d2d_p2 = to_d2d_point_2f(p2);
- d->dc()->DrawLine(d2d_p1, d2d_p2, d->pen.brush.Get(), d->pen.qpen.widthF(), d->pen.strokeStyle.Get());
+ if (!d->pen.dashBrush || state()->renderHints.testFlag(QPainter::HighQualityAntialiasing)) {
+ d->dc()->DrawLine(d2d_p1, d2d_p2, d->pen.brush.Get(), d->pen.qpen.widthF(), d->pen.strokeStyle.Get());
+ } else {
+ d->pen.dashBrush->SetTransform(transformFromLine(lines[i], d->pen.qpen.widthF()));
+ d->dc()->DrawLine(d2d_p1, d2d_p2, d->pen.dashBrush.Get(), d->pen.qpen.widthF(), NULL);
+ }
}
}
}
@@ -1413,6 +1463,19 @@ void QWindowsDirect2DPaintEngine::drawPixmap(const QRectF &r,
return;
}
+ if (d->flags.testFlag(EmulateComposition)) {
+ const qreal points[] = {
+ r.x(), r.y(),
+ r.x() + r.width(), r.y(),
+ r.x() + r.width(), r.y() + r.height(),
+ r.x(), r.y() + r.height()
+ };
+ const QVectorPath vp(points, 4, 0, QVectorPath::RectangleHint);
+ const QBrush brush(sr.isValid() ? pm.copy(sr.toRect()) : pm);
+ rasterFill(vp, brush);
+ return;
+ }
+
QWindowsDirect2DPlatformPixmap *pp = static_cast<QWindowsDirect2DPlatformPixmap *>(pm.handle());
QWindowsDirect2DBitmap *bitmap = pp->bitmap();
@@ -1596,9 +1659,17 @@ void QWindowsDirect2DPaintEngine::rasterFill(const QVectorPath &path, const QBru
{
Q_D(QWindowsDirect2DPaintEngine);
- QImage img(d->bitmap->size(), QImage::Format_ARGB32);
- img.fill(Qt::transparent);
+ if (d->fallbackImage.isNull()) {
+ if (d->flags.testFlag(EmulateComposition)) {
+ QWindowsDirect2DPaintEngineSuspender suspender(this);
+ d->fallbackImage = d->bitmap->toImage();
+ } else {
+ d->fallbackImage = QImage(d->bitmap->size(), QImage::Format_ARGB32_Premultiplied);
+ d->fallbackImage.fill(Qt::transparent);
+ }
+ }
+ QImage &img = d->fallbackImage;
QPainter p;
QPaintEngine *engine = img.paintEngine();
@@ -1645,11 +1716,14 @@ void QWindowsDirect2DPaintEngine::rasterFill(const QVectorPath &path, const QBru
if (!p.end())
qWarning("%s: Paint Engine end returned false", __FUNCTION__);
- d->updateClipEnabled(false);
- d->updateTransform(QTransform());
- drawImage(img.rect(), img, img.rect());
- transformChanged();
- clipEnabledChanged();
+ if (!d->flags.testFlag(EmulateComposition)) { // Emulated fallback will be flattened in end()
+ d->updateClipEnabled(false);
+ d->updateTransform(QTransform());
+ drawImage(img.rect(), img, img.rect());
+ d->fallbackImage = QImage();
+ transformChanged();
+ clipEnabledChanged();
+ }
} else {
qWarning("%s: Could not fall back to QImage", __FUNCTION__);
}
@@ -1659,6 +1733,9 @@ bool QWindowsDirect2DPaintEngine::emulationRequired(EmulationType type) const
{
Q_D(const QWindowsDirect2DPaintEngine);
+ if (d->flags.testFlag(EmulateComposition))
+ return true;
+
if (!state()->matrix.isAffine())
return true;
@@ -1698,4 +1775,71 @@ void QWindowsDirect2DPaintEngine::adjustForAliasing(QPointF *point)
(*point) += adjustment;
}
+void QWindowsDirect2DPaintEngine::suspend()
+{
+ end();
+}
+
+void QWindowsDirect2DPaintEngine::resume()
+{
+ begin(paintDevice());
+ clipEnabledChanged();
+ penChanged();
+ brushChanged();
+ brushOriginChanged();
+ opacityChanged();
+ compositionModeChanged();
+ renderHintsChanged();
+ transformChanged();
+}
+
+class QWindowsDirect2DPaintEngineSuspenderImpl
+{
+ Q_DISABLE_COPY(QWindowsDirect2DPaintEngineSuspenderImpl)
+ QWindowsDirect2DPaintEngine *m_engine;
+ bool m_active;
+public:
+ QWindowsDirect2DPaintEngineSuspenderImpl(QWindowsDirect2DPaintEngine *engine)
+ : m_engine(engine)
+ , m_active(engine->isActive())
+ {
+ if (m_active)
+ m_engine->suspend();
+ }
+
+ ~QWindowsDirect2DPaintEngineSuspenderImpl()
+ {
+ if (m_active)
+ m_engine->resume();
+ }
+};
+
+class QWindowsDirect2DPaintEngineSuspenderPrivate
+{
+public:
+ QWindowsDirect2DPaintEngineSuspenderPrivate(QWindowsDirect2DPaintEngine *engine)
+ : engineSuspender(engine)
+ , dcSuspender(static_cast<QWindowsDirect2DPaintEnginePrivate *>(engine->d_ptr.data())->bitmap->deviceContext())
+ {
+ }
+
+ QWindowsDirect2DPaintEngineSuspenderImpl engineSuspender;
+ QWindowsDirect2DDeviceContextSuspender dcSuspender;
+};
+
+QWindowsDirect2DPaintEngineSuspender::QWindowsDirect2DPaintEngineSuspender(QWindowsDirect2DPaintEngine *engine)
+ : d_ptr(new QWindowsDirect2DPaintEngineSuspenderPrivate(engine))
+{
+
+}
+
+QWindowsDirect2DPaintEngineSuspender::~QWindowsDirect2DPaintEngineSuspender()
+{
+}
+
+void QWindowsDirect2DPaintEngineSuspender::resume()
+{
+ d_ptr.reset();
+}
+
QT_END_NAMESPACE