Fix decoration rendering with scale factor < 1
QPainter won't let paint with a device pixel ratio less than 1. There are used to be workarounds to force a device pixel ratio of 1, but they were removed with fractional scaling corner fix. This change makes sure that the decoration renderer forces a device pixel ratio of 1 if the output's scale factor is less than 1 so calculated texture coordinates match where window borders are rendered in the texture atlas. BUG: 449681
This commit is contained in:
parent
a428630ea4
commit
bb935aa5de
7 changed files with 28 additions and 33 deletions
|
@ -68,6 +68,12 @@ void DecorationRenderer::resetDamage()
|
||||||
m_damage = QRegion();
|
m_damage = QRegion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qreal DecorationRenderer::effectiveDevicePixelRatio() const
|
||||||
|
{
|
||||||
|
// QPainter won't let paint with a device pixel ratio less than 1.
|
||||||
|
return std::max(qreal(1.0), devicePixelRatio());
|
||||||
|
}
|
||||||
|
|
||||||
qreal DecorationRenderer::devicePixelRatio() const
|
qreal DecorationRenderer::devicePixelRatio() const
|
||||||
{
|
{
|
||||||
return m_devicePixelRatio;
|
return m_devicePixelRatio;
|
||||||
|
@ -107,7 +113,7 @@ QImage DecorationRenderer::renderToImage(const QRect &geo)
|
||||||
image.fill(Qt::transparent);
|
image.fill(Qt::transparent);
|
||||||
QPainter p(&image);
|
QPainter p(&image);
|
||||||
p.setRenderHint(QPainter::Antialiasing);
|
p.setRenderHint(QPainter::Antialiasing);
|
||||||
p.setWindow(QRect(geo.topLeft(), geo.size() * qPainterEffectiveDevicePixelRatio(&p)));
|
p.setWindow(QRect(geo.topLeft(), geo.size() * effectiveDevicePixelRatio()));
|
||||||
p.setClipRect(geo);
|
p.setClipRect(geo);
|
||||||
renderToPainter(&p, geo);
|
renderToPainter(&p, geo);
|
||||||
return image;
|
return image;
|
||||||
|
@ -230,7 +236,7 @@ WindowQuadList DecorationItem::buildQuads() const
|
||||||
}
|
}
|
||||||
|
|
||||||
QRect left, top, right, bottom;
|
QRect left, top, right, bottom;
|
||||||
const qreal devicePixelRatio = m_renderer->devicePixelRatio();
|
const qreal devicePixelRatio = m_renderer->effectiveDevicePixelRatio();
|
||||||
const int texturePad = DecorationRenderer::TexturePad;
|
const int texturePad = DecorationRenderer::TexturePad;
|
||||||
|
|
||||||
if (const AbstractClient *client = qobject_cast<const AbstractClient *>(m_window)) {
|
if (const AbstractClient *client = qobject_cast<const AbstractClient *>(m_window)) {
|
||||||
|
|
|
@ -38,6 +38,7 @@ public:
|
||||||
void addDamage(const QRegion ®ion);
|
void addDamage(const QRegion ®ion);
|
||||||
void resetDamage();
|
void resetDamage();
|
||||||
|
|
||||||
|
qreal effectiveDevicePixelRatio() const;
|
||||||
qreal devicePixelRatio() const;
|
qreal devicePixelRatio() const;
|
||||||
void setDevicePixelRatio(qreal dpr);
|
void setDevicePixelRatio(qreal dpr);
|
||||||
|
|
||||||
|
|
|
@ -1536,9 +1536,10 @@ void SceneOpenGLDecorationRenderer::render(const QRegion ®ion)
|
||||||
QRect left, top, right, bottom;
|
QRect left, top, right, bottom;
|
||||||
client()->client()->layoutDecorationRects(left, top, right, bottom);
|
client()->client()->layoutDecorationRects(left, top, right, bottom);
|
||||||
|
|
||||||
const int topHeight = std::ceil(top.height() * devicePixelRatio());
|
const qreal devicePixelRatio = effectiveDevicePixelRatio();
|
||||||
const int bottomHeight = std::ceil(bottom.height() * devicePixelRatio());
|
const int topHeight = std::ceil(top.height() * devicePixelRatio);
|
||||||
const int leftWidth = std::ceil(left.width() * devicePixelRatio());
|
const int bottomHeight = std::ceil(bottom.height() * devicePixelRatio);
|
||||||
|
const int leftWidth = std::ceil(left.width() * devicePixelRatio);
|
||||||
|
|
||||||
const QPoint topPosition(0, 0);
|
const QPoint topPosition(0, 0);
|
||||||
const QPoint bottomPosition(0, topPosition.y() + topHeight + (2 * TexturePad));
|
const QPoint bottomPosition(0, topPosition.y() + topHeight + (2 * TexturePad));
|
||||||
|
@ -1547,14 +1548,15 @@ void SceneOpenGLDecorationRenderer::render(const QRegion ®ion)
|
||||||
|
|
||||||
const QRect dirtyRect = region.boundingRect();
|
const QRect dirtyRect = region.boundingRect();
|
||||||
|
|
||||||
renderPart(top.intersected(dirtyRect), top, topPosition);
|
renderPart(top.intersected(dirtyRect), top, topPosition, devicePixelRatio);
|
||||||
renderPart(bottom.intersected(dirtyRect), bottom, bottomPosition);
|
renderPart(bottom.intersected(dirtyRect), bottom, bottomPosition, devicePixelRatio);
|
||||||
renderPart(left.intersected(dirtyRect), left, leftPosition, true);
|
renderPart(left.intersected(dirtyRect), left, leftPosition, devicePixelRatio, true);
|
||||||
renderPart(right.intersected(dirtyRect), right, rightPosition, true);
|
renderPart(right.intersected(dirtyRect), right, rightPosition, devicePixelRatio, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneOpenGLDecorationRenderer::renderPart(const QRect &rect, const QRect &partRect,
|
void SceneOpenGLDecorationRenderer::renderPart(const QRect &rect, const QRect &partRect,
|
||||||
const QPoint &textureOffset, bool rotated)
|
const QPoint &textureOffset,
|
||||||
|
qreal devicePixelRatio, bool rotated)
|
||||||
{
|
{
|
||||||
if (!rect.isValid()) {
|
if (!rect.isValid()) {
|
||||||
return;
|
return;
|
||||||
|
@ -1567,7 +1569,7 @@ void SceneOpenGLDecorationRenderer::renderPart(const QRect &rect, const QRect &p
|
||||||
int verticalPadding = padding.top() + padding.bottom();
|
int verticalPadding = padding.top() + padding.bottom();
|
||||||
int horizontalPadding = padding.left() + padding.right();
|
int horizontalPadding = padding.left() + padding.right();
|
||||||
|
|
||||||
QSize imageSize = rect.size() * devicePixelRatio();
|
QSize imageSize = rect.size() * devicePixelRatio;
|
||||||
if (rotated) {
|
if (rotated) {
|
||||||
imageSize = QSize(imageSize.height(), imageSize.width());
|
imageSize = QSize(imageSize.height(), imageSize.width());
|
||||||
}
|
}
|
||||||
|
@ -1575,12 +1577,12 @@ void SceneOpenGLDecorationRenderer::renderPart(const QRect &rect, const QRect &p
|
||||||
paddedImageSize.rheight() += verticalPadding;
|
paddedImageSize.rheight() += verticalPadding;
|
||||||
paddedImageSize.rwidth() += horizontalPadding;
|
paddedImageSize.rwidth() += horizontalPadding;
|
||||||
QImage image(paddedImageSize, QImage::Format_ARGB32_Premultiplied);
|
QImage image(paddedImageSize, QImage::Format_ARGB32_Premultiplied);
|
||||||
image.setDevicePixelRatio(devicePixelRatio());
|
image.setDevicePixelRatio(devicePixelRatio);
|
||||||
image.fill(Qt::transparent);
|
image.fill(Qt::transparent);
|
||||||
|
|
||||||
QRect padClip = QRect(padding.left(), padding.top(), imageSize.width(), imageSize.height());
|
QRect padClip = QRect(padding.left(), padding.top(), imageSize.width(), imageSize.height());
|
||||||
QPainter painter(&image);
|
QPainter painter(&image);
|
||||||
const qreal inverseScale = 1.0 / devicePixelRatio();
|
const qreal inverseScale = 1.0 / devicePixelRatio;
|
||||||
painter.scale(inverseScale, inverseScale);
|
painter.scale(inverseScale, inverseScale);
|
||||||
painter.setRenderHint(QPainter::Antialiasing);
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
painter.setClipRect(padClip);
|
painter.setClipRect(padClip);
|
||||||
|
@ -1589,7 +1591,7 @@ void SceneOpenGLDecorationRenderer::renderPart(const QRect &rect, const QRect &p
|
||||||
painter.translate(0, imageSize.height());
|
painter.translate(0, imageSize.height());
|
||||||
painter.rotate(-90);
|
painter.rotate(-90);
|
||||||
}
|
}
|
||||||
painter.scale(devicePixelRatio(), devicePixelRatio());
|
painter.scale(devicePixelRatio, devicePixelRatio);
|
||||||
painter.translate(-rect.topLeft());
|
painter.translate(-rect.topLeft());
|
||||||
renderToPainter(&painter, rect);
|
renderToPainter(&painter, rect);
|
||||||
painter.end();
|
painter.end();
|
||||||
|
@ -1597,7 +1599,7 @@ void SceneOpenGLDecorationRenderer::renderPart(const QRect &rect, const QRect &p
|
||||||
// fill padding pixels by copying from the neighbour row
|
// fill padding pixels by copying from the neighbour row
|
||||||
clamp(image, padClip);
|
clamp(image, padClip);
|
||||||
|
|
||||||
QPoint dirtyOffset = (rect.topLeft() - partRect.topLeft()) * devicePixelRatio();
|
QPoint dirtyOffset = (rect.topLeft() - partRect.topLeft()) * devicePixelRatio;
|
||||||
if (padding.top() == 0) {
|
if (padding.top() == 0) {
|
||||||
dirtyOffset.ry() += TexturePad;
|
dirtyOffset.ry() += TexturePad;
|
||||||
}
|
}
|
||||||
|
@ -1641,7 +1643,7 @@ void SceneOpenGLDecorationRenderer::resizeTexture()
|
||||||
qMax(left.height(), right.height()));
|
qMax(left.height(), right.height()));
|
||||||
size.rheight() = top.height() + bottom.height() +
|
size.rheight() = top.height() + bottom.height() +
|
||||||
left.width() + right.width();
|
left.width() + right.width();
|
||||||
size *= devicePixelRatio();
|
size *= effectiveDevicePixelRatio();
|
||||||
|
|
||||||
size.rheight() += 4 * (2 * TexturePad);
|
size.rheight() += 4 * (2 * TexturePad);
|
||||||
size.rwidth() += 2 * TexturePad;
|
size.rwidth() += 2 * TexturePad;
|
||||||
|
|
|
@ -213,7 +213,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void renderPart(const QRect &rect, const QRect &partRect, const QPoint &textureOffset, bool rotated = false);
|
void renderPart(const QRect &rect, const QRect &partRect, const QPoint &textureOffset, qreal devicePixelRatio, bool rotated = false);
|
||||||
static const QMargins texturePadForPart(const QRect &rect, const QRect &partRect);
|
static const QMargins texturePadForPart(const QRect &rect, const QRect &partRect);
|
||||||
void resizeTexture();
|
void resizeTexture();
|
||||||
QScopedPointer<GLTexture> m_texture;
|
QScopedPointer<GLTexture> m_texture;
|
||||||
|
|
|
@ -444,7 +444,7 @@ void SceneQPainterDecorationRenderer::render(const QRegion ®ion)
|
||||||
}
|
}
|
||||||
QPainter painter(&m_images[index]);
|
QPainter painter(&m_images[index]);
|
||||||
painter.setRenderHint(QPainter::Antialiasing);
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
painter.setWindow(QRect(partRect.topLeft(), partRect.size() * qPainterEffectiveDevicePixelRatio(&painter)));
|
painter.setWindow(QRect(partRect.topLeft(), partRect.size() * effectiveDevicePixelRatio()));
|
||||||
painter.setClipRect(rect);
|
painter.setClipRect(rect);
|
||||||
painter.save();
|
painter.save();
|
||||||
// clear existing part
|
// clear existing part
|
||||||
|
@ -466,7 +466,7 @@ void SceneQPainterDecorationRenderer::resizeImages()
|
||||||
client()->client()->layoutDecorationRects(left, top, right, bottom);
|
client()->client()->layoutDecorationRects(left, top, right, bottom);
|
||||||
|
|
||||||
auto checkAndCreate = [this](int index, const QSize &size) {
|
auto checkAndCreate = [this](int index, const QSize &size) {
|
||||||
auto dpr = devicePixelRatio();
|
auto dpr = effectiveDevicePixelRatio();
|
||||||
if (m_images[index].size() != size * dpr ||
|
if (m_images[index].size() != size * dpr ||
|
||||||
m_images[index].devicePixelRatio() != dpr)
|
m_images[index].devicePixelRatio() != dpr)
|
||||||
{
|
{
|
||||||
|
|
|
@ -181,11 +181,6 @@ Qt::KeyboardModifiers x11ToQtKeyboardModifiers(int state)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal qPainterEffectiveDevicePixelRatio(const QPainter *painter)
|
|
||||||
{
|
|
||||||
return std::max(qreal(1), painter->device()->devicePixelRatioF());
|
|
||||||
}
|
|
||||||
|
|
||||||
QPoint popupOffset(const QRect &anchorRect, const Qt::Edges anchorEdge, const Qt::Edges gravity, const QSize popupSize)
|
QPoint popupOffset(const QRect &anchorRect, const Qt::Edges anchorEdge, const Qt::Edges gravity, const QSize popupSize)
|
||||||
{
|
{
|
||||||
QPoint anchorPoint;
|
QPoint anchorPoint;
|
||||||
|
|
|
@ -123,15 +123,6 @@ void KWIN_EXPORT ungrabXServer();
|
||||||
bool KWIN_EXPORT grabXKeyboard(xcb_window_t w = XCB_WINDOW_NONE);
|
bool KWIN_EXPORT grabXKeyboard(xcb_window_t w = XCB_WINDOW_NONE);
|
||||||
void KWIN_EXPORT ungrabXKeyboard();
|
void KWIN_EXPORT ungrabXKeyboard();
|
||||||
|
|
||||||
/**
|
|
||||||
* QPainter::setWindow() doesn't work as expected when the device pixel ratio of the paint
|
|
||||||
* device is less than 1.
|
|
||||||
*
|
|
||||||
* QPainter simply doesn't allow the effective scale factor to be less than 1. Use this function
|
|
||||||
* to determine the effective device pixel ratio by which the window rect has to be scaled.
|
|
||||||
*/
|
|
||||||
qreal KWIN_EXPORT qPainterEffectiveDevicePixelRatio(const QPainter *painter);
|
|
||||||
|
|
||||||
static inline QRegion mapRegion(const QMatrix4x4 &matrix, const QRegion ®ion)
|
static inline QRegion mapRegion(const QMatrix4x4 &matrix, const QRegion ®ion)
|
||||||
{
|
{
|
||||||
QRegion result;
|
QRegion result;
|
||||||
|
|
Loading…
Reference in a new issue