mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-08-28 11:45:22 -05:00
[Client] Fix user list banner art rendering under display scaling (#7160)
Cached card art pixmaps carry the screen device pixel ratio, so both banner painters did their crop math on scaled pixels and blitted the result at raw over logical size, clipping art into its top left quadrant on any display above 100 percent Normalize a local copy to DPR 1 before crop math in UserListPainter and the popup header, clamp srcX and srcY bounds against stored zoom below 1, keep shared cache entries untouched Took 15 minutes Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
@@ -189,22 +189,31 @@ void UserInfoHeaderWidget::paintEvent(QPaintEvent *)
|
||||
|
||||
// ── Card art background ───────────────────────────────────────────────────
|
||||
if (!cardArt.isNull()) {
|
||||
// Same DPR normalization as UserListPainter::drawCardArt: the cache
|
||||
// carries screen scaled pixmaps on HiDPI displays, the math below is
|
||||
// in raw pixels.
|
||||
QPixmap art = cardArt;
|
||||
art.setDevicePixelRatio(1.0);
|
||||
|
||||
const int w = rect.width();
|
||||
const int h = rect.height();
|
||||
const int mL = qRound(w * params.marginPctL);
|
||||
const int mR = qRound(w * params.marginPctR);
|
||||
const int dW = w - mL - mR;
|
||||
|
||||
const double base = qMax(double(dW) / cardArt.width(), double(h) / cardArt.height());
|
||||
const double base = qMax(double(dW) / art.width(), double(h) / art.height());
|
||||
const double scale = base * params.zoom;
|
||||
const int sW = qRound(cardArt.width() * scale);
|
||||
const int sH = qRound(cardArt.height() * scale);
|
||||
const int sW = qRound(art.width() * scale);
|
||||
const int sH = qRound(art.height() * scale);
|
||||
|
||||
const QPixmap scaled = cardArt.scaled(sW, sH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
const int srcX = (sW - dW) / 2;
|
||||
const int srcY = qBound(0, qRound((sH - h) * params.verticalOffset), qMax(0, sH - h));
|
||||
const QPixmap scaled = art.scaled(sW, sH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
// Clamp against stored zoom < 1, which can push srcX negative and silently
|
||||
// underfill the strip with transparent padding
|
||||
const int safeSrcX = qBound(0, (sW - dW) / 2, qMax(0, sW - dW));
|
||||
const int safeSrcY = qBound(0, qRound((sH - h) * params.verticalOffset), qMax(0, sH - h));
|
||||
|
||||
QImage img = scaled.copy(srcX, srcY, dW, h).toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
QImage img =
|
||||
scaled.copy(safeSrcX, safeSrcY, dW, h).toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
{
|
||||
QPainter mask(&img);
|
||||
mask.setCompositionMode(QPainter::CompositionMode_DestinationIn);
|
||||
@@ -361,7 +370,7 @@ void UserInfoPopup::buildUi()
|
||||
header = new UserInfoHeaderWidget(this);
|
||||
root->addWidget(header);
|
||||
|
||||
// Action area — rebuilt per user
|
||||
// Action area, rebuilt per user
|
||||
actionArea = new QWidget(this);
|
||||
root->addWidget(actionArea);
|
||||
|
||||
@@ -402,7 +411,7 @@ void UserInfoPopup::buildUi()
|
||||
|
||||
root->addWidget(gamesView);
|
||||
|
||||
// Close button — positioned absolutely in the top-right corner
|
||||
// Close button, positioned absolutely in the top right corner
|
||||
closeBtn = new QPushButton(QStringLiteral("✕"), this);
|
||||
closeBtn->setFixedSize(22, 22);
|
||||
closeBtn->setFlat(true);
|
||||
@@ -673,7 +682,7 @@ void UserInfoPopup::showForUser(const QString &userName,
|
||||
gamesStatus->setText(tr("Loading games…"));
|
||||
gamesStatus->show();
|
||||
|
||||
// Close button — top-right corner, above everything
|
||||
// Close button, top right corner, above everything
|
||||
closeBtn->move(PopupWidth - closeBtn->width() - 6, 6);
|
||||
closeBtn->raise();
|
||||
|
||||
@@ -702,7 +711,7 @@ void UserInfoPopup::fetchGames()
|
||||
void UserInfoPopup::onGamesReceived(const Response &r, const QString &forUser)
|
||||
{
|
||||
if (forUser != currentUser) {
|
||||
return; // stale response — different user showing now
|
||||
return; // stale response, different user showing now
|
||||
}
|
||||
|
||||
gamesModel->clear();
|
||||
@@ -763,4 +772,4 @@ void UserInfoPopup::leaveEvent(QEvent *e)
|
||||
{
|
||||
QFrame::leaveEvent(e);
|
||||
emit mouseLeftPopup();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +155,12 @@ void UserListPainter::drawCardArt(QPainter *painter,
|
||||
return;
|
||||
}
|
||||
|
||||
// CardPictureLoader::getPixmap tags its output with the screen's
|
||||
// devicePixelRatio on HiDPI displays. Every calculation below is in raw
|
||||
// pixels, so normalize to 1.0 or the crop renders at 1/dpr scale anchored
|
||||
// to the top left corner of the row.
|
||||
art.setDevicePixelRatio(1.0);
|
||||
|
||||
const int cardH = rect.height() - 4;
|
||||
const int totalW = cardRight - rect.left();
|
||||
const int marginL = qRound(totalW * params.marginPctL);
|
||||
@@ -172,11 +178,14 @@ void UserListPainter::drawCardArt(QPainter *painter,
|
||||
const int srcX = (scaledW - drawW) / 2;
|
||||
const int srcY = qRound((scaledH - cardH) * params.verticalOffset);
|
||||
|
||||
// Clamp srcY so we never copy outside the pixmap bounds
|
||||
// Clamp so we never copy outside the pixmap bounds. srcX can go negative
|
||||
// for stored zoom values below 1, which would silently underfill the
|
||||
// strip with transparent padding.
|
||||
const int safeSrcX = qBound(0, srcX, qMax(0, scaledW - drawW));
|
||||
const int safeSrcY = qBound(0, srcY, qMax(0, scaledH - cardH));
|
||||
|
||||
QImage img =
|
||||
scaled.copy(srcX, safeSrcY, drawW, cardH).toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
scaled.copy(safeSrcX, safeSrcY, drawW, cardH).toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
|
||||
{
|
||||
QPainter mask(&img);
|
||||
@@ -403,4 +412,4 @@ void UserListPainter::paint(QPainter *painter,
|
||||
drawBadges(painter, option, rect, cardRight, badges, online, style);
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user