shadow: handle DPR outside the renderer

QPainter's auto-scaling is prone to off-by-one rounding errors and draws on
fractional coordinates. With this change, we paint on a 1x DPR QPainter and
scale the shadow offset and strength manually based on DPR.

This resolves an issue with resulting in seams on the right and bottom
edges of a menu due to shadow boundaries being off-by-one.

BUG: 418166

v2: remove unrelated formatting changes
v3:
- move the DPR helper to ShadowHelper
- retrieve the DPR from the widget instead of the global QGuiApplication
- added BUG reference
wilder-5.24
Tatsuyuki Ishi 5 years ago committed by Noah Davis
parent 3262107791
commit ee06e26236
  1. 2
      kdecoration/breezedecoration.cpp
  2. 4
      kstyle/breezehelper.cpp
  3. 3
      kstyle/breezehelper.h
  4. 2
      kstyle/breezemdiwindowshadow.cpp
  5. 26
      kstyle/breezeshadowhelper.cpp
  6. 16
      kstyle/breezeshadowhelper.h
  7. 8
      libbreezecommon/breezeboxshadowrenderer.cpp
  8. 7
      libbreezecommon/breezeboxshadowrenderer.h

@ -785,8 +785,6 @@ namespace Breeze
BoxShadowRenderer shadowRenderer;
shadowRenderer.setBorderRadius(m_scaledCornerRadius + 0.5);
shadowRenderer.setBoxSize(boxSize);
shadowRenderer.setDevicePixelRatio(1.0); // TODO: Create HiDPI shadows?
const qreal strength = m_internalSettings->shadowStrength() / 255.0 * strengthScale;
shadowRenderer.addShadow(params.shadow1.offset, params.shadow1.radius,

@ -1689,10 +1689,6 @@ namespace Breeze
{ return compositingActive() && widget && widget->testAttribute( Qt::WA_TranslucentBackground ); }
//______________________________________________________________________________________
qreal Helper::devicePixelRatio( const QPixmap& pixmap ) const
{
return pixmap.devicePixelRatio();
}
QPixmap Helper::coloredIcon(const QIcon& icon, const QPalette& palette, const QSize &size, QIcon::Mode mode, QIcon::State state)
{

@ -258,9 +258,6 @@ namespace Breeze
//@}
//* return device pixel ratio for a given pixmap
virtual qreal devicePixelRatio( const QPixmap& ) const;
//* frame radius
constexpr qreal frameRadius( const int penWidth = PenWidth::NoPen, const qreal bias = 0 ) const
{ return qMax( Metrics::Frame_FrameRadius - (0.5 * penWidth) + bias, 0.0 ); }

@ -220,7 +220,7 @@ namespace Breeze
if ( !_shadowHelper ) return;
// create new shadow
auto windowShadow( new MdiWindowShadow( widget->parentWidget(), _shadowHelper->shadowTiles() ) );
auto windowShadow( new MdiWindowShadow( widget->parentWidget(), _shadowHelper->shadowTiles(widget)) );
windowShadow->setWidget( widget );
}

@ -192,9 +192,9 @@ namespace Breeze
}
//_______________________________________________________
TileSet ShadowHelper::shadowTiles()
TileSet ShadowHelper::shadowTiles(QWidget *widget)
{
const CompositeShadowParams params = lookupShadowParams(StyleConfigData::shadowSize());
CompositeShadowParams params = lookupShadowParams(StyleConfigData::shadowSize());
if (params.isNone()) {
return TileSet();
@ -202,6 +202,9 @@ namespace Breeze
return _shadowTiles;
}
const qreal dpr = devicePixelRatio(widget);
params *= dpr;
auto withOpacity = [](const QColor &color, qreal opacity) -> QColor {
QColor c(color);
c.setAlphaF(opacity);
@ -214,14 +217,11 @@ namespace Breeze
const QSize boxSize = BoxShadowRenderer::calculateMinimumBoxSize(params.shadow1.radius)
.expandedTo(BoxShadowRenderer::calculateMinimumBoxSize(params.shadow2.radius));
// On Wayland, the compositor will upscale the shadow tiles if necessary.
const qreal dpr = KWindowSystem::isPlatformWayland() ? 1 : qApp->devicePixelRatio();
const qreal frameRadius = _helper.frameRadius();
BoxShadowRenderer shadowRenderer;
shadowRenderer.setBorderRadius(frameRadius);
shadowRenderer.setBoxSize(boxSize);
shadowRenderer.setDevicePixelRatio(dpr);
shadowRenderer.addShadow(params.shadow1.offset, params.shadow1.radius,
withOpacity(color, params.shadow1.opacity * strength));
@ -230,7 +230,7 @@ namespace Breeze
QImage shadowTexture = shadowRenderer.render();
const QRect outerRect(QPoint(0, 0), shadowTexture.size() / dpr);
const QRect outerRect(QPoint(0, 0), shadowTexture.size());
QRect boxRect(QPoint(0, 0), boxSize);
boxRect.moveCenter(outerRect.center());
@ -369,7 +369,7 @@ namespace Breeze
if( !widget->testAttribute( Qt::WA_WState_Created ) ) return;
// create shadow tiles if needed
shadowTiles();
shadowTiles(widget);
if( !_shadowTiles.isValid() ) return;
// create platform shadow tiles if needed
@ -410,10 +410,12 @@ namespace Breeze
//_______________________________________________________
QMargins ShadowHelper::shadowMargins( QWidget* widget ) const
{
const CompositeShadowParams params = lookupShadowParams(StyleConfigData::shadowSize());
CompositeShadowParams params = lookupShadowParams(StyleConfigData::shadowSize());
if (params.isNone()) {
return QMargins();
}
qreal dpr = devicePixelRatio(widget);
params *= dpr;
const QSize boxSize = BoxShadowRenderer::calculateMinimumBoxSize(params.shadow1.radius)
.expandedTo(BoxShadowRenderer::calculateMinimumBoxSize(params.shadow2.radius));
@ -449,8 +451,6 @@ namespace Breeze
}
}
margins *= _helper.devicePixelRatio(_shadowTiles.pixmap(0));
return margins;
}
@ -460,4 +460,10 @@ namespace Breeze
delete _shadows.take( widget->windowHandle() );
}
//_______________________________________________________
qreal ShadowHelper::devicePixelRatio(QWidget *widget) {
// On Wayland, the compositor will upscale the shadow tiles if necessary.
return Helper::isWayland() ? 1 : widget->devicePixelRatioF();
}
}

@ -37,6 +37,11 @@ namespace Breeze
QPoint offset;
int radius = 0;
qreal opacity = 0;
void operator*=(qreal factor) {
offset *= factor;
radius = qRound(radius * factor);
}
};
struct CompositeShadowParams
@ -57,6 +62,12 @@ namespace Breeze
QPoint offset;
ShadowParams shadow1;
ShadowParams shadow2;
void operator*=(qreal factor) {
offset *= factor;
shadow1 *= factor;
shadow2 *= factor;
}
};
//* handle shadow pixmaps passed to window manager via X property
@ -93,7 +104,10 @@ namespace Breeze
//* shadow tiles
/** is public because it is also needed for mdi windows */
TileSet shadowTiles();
TileSet shadowTiles(QWidget *);
//* return device pixel ratio for the window containing the widget
static qreal devicePixelRatio(QWidget *) ;
protected Q_SLOTS:

@ -294,11 +294,6 @@ void BoxShadowRenderer::setBorderRadius(qreal radius)
m_borderRadius = radius;
}
void BoxShadowRenderer::setDevicePixelRatio(qreal dpr)
{
m_dpr = dpr;
}
void BoxShadowRenderer::addShadow(const QPoint &offset, int radius, const QColor &color)
{
Shadow shadow = {};
@ -320,8 +315,7 @@ QImage BoxShadowRenderer::render() const
calculateMinimumShadowTextureSize(m_boxSize, shadow.radius, shadow.offset));
}
QImage canvas(canvasSize * m_dpr, QImage::Format_ARGB32_Premultiplied);
canvas.setDevicePixelRatio(m_dpr);
QImage canvas(canvasSize, QImage::Format_ARGB32_Premultiplied);
canvas.fill(Qt::transparent);
QRect boxRect(QPoint(0, 0), m_boxSize);

@ -35,12 +35,6 @@ public:
**/
void setBorderRadius(qreal radius);
/**
* Set the device pixel ratio of the resulting shadow texture.
* @param dpr The device pixel ratio.
**/
void setDevicePixelRatio(qreal dpr);
/**
* Add a shadow.
* @param offset The offset of the shadow.
@ -79,7 +73,6 @@ public:
private:
QSize m_boxSize;
qreal m_borderRadius = 0.0;
qreal m_dpr = 1.0;
struct Shadow {
QPoint offset;

Loading…
Cancel
Save