mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-24 10:23:02 -07: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:
parent
63a970045a
commit
c42fb6691d
2 changed files with 33 additions and 15 deletions
|
|
@ -189,22 +189,31 @@ void UserInfoHeaderWidget::paintEvent(QPaintEvent *)
|
||||||
|
|
||||||
// ── Card art background ───────────────────────────────────────────────────
|
// ── Card art background ───────────────────────────────────────────────────
|
||||||
if (!cardArt.isNull()) {
|
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 w = rect.width();
|
||||||
const int h = rect.height();
|
const int h = rect.height();
|
||||||
const int mL = qRound(w * params.marginPctL);
|
const int mL = qRound(w * params.marginPctL);
|
||||||
const int mR = qRound(w * params.marginPctR);
|
const int mR = qRound(w * params.marginPctR);
|
||||||
const int dW = w - mL - mR;
|
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 double scale = base * params.zoom;
|
||||||
const int sW = qRound(cardArt.width() * scale);
|
const int sW = qRound(art.width() * scale);
|
||||||
const int sH = qRound(cardArt.height() * scale);
|
const int sH = qRound(art.height() * scale);
|
||||||
|
|
||||||
const QPixmap scaled = cardArt.scaled(sW, sH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
const QPixmap scaled = art.scaled(sW, sH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||||
const int srcX = (sW - dW) / 2;
|
// Clamp against stored zoom < 1, which can push srcX negative and silently
|
||||||
const int srcY = qBound(0, qRound((sH - h) * params.verticalOffset), qMax(0, sH - h));
|
// 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);
|
QPainter mask(&img);
|
||||||
mask.setCompositionMode(QPainter::CompositionMode_DestinationIn);
|
mask.setCompositionMode(QPainter::CompositionMode_DestinationIn);
|
||||||
|
|
@ -361,7 +370,7 @@ void UserInfoPopup::buildUi()
|
||||||
header = new UserInfoHeaderWidget(this);
|
header = new UserInfoHeaderWidget(this);
|
||||||
root->addWidget(header);
|
root->addWidget(header);
|
||||||
|
|
||||||
// Action area — rebuilt per user
|
// Action area, rebuilt per user
|
||||||
actionArea = new QWidget(this);
|
actionArea = new QWidget(this);
|
||||||
root->addWidget(actionArea);
|
root->addWidget(actionArea);
|
||||||
|
|
||||||
|
|
@ -402,7 +411,7 @@ void UserInfoPopup::buildUi()
|
||||||
|
|
||||||
root->addWidget(gamesView);
|
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 = new QPushButton(QStringLiteral("✕"), this);
|
||||||
closeBtn->setFixedSize(22, 22);
|
closeBtn->setFixedSize(22, 22);
|
||||||
closeBtn->setFlat(true);
|
closeBtn->setFlat(true);
|
||||||
|
|
@ -673,7 +682,7 @@ void UserInfoPopup::showForUser(const QString &userName,
|
||||||
gamesStatus->setText(tr("Loading games…"));
|
gamesStatus->setText(tr("Loading games…"));
|
||||||
gamesStatus->show();
|
gamesStatus->show();
|
||||||
|
|
||||||
// Close button — top-right corner, above everything
|
// Close button, top right corner, above everything
|
||||||
closeBtn->move(PopupWidth - closeBtn->width() - 6, 6);
|
closeBtn->move(PopupWidth - closeBtn->width() - 6, 6);
|
||||||
closeBtn->raise();
|
closeBtn->raise();
|
||||||
|
|
||||||
|
|
@ -702,7 +711,7 @@ void UserInfoPopup::fetchGames()
|
||||||
void UserInfoPopup::onGamesReceived(const Response &r, const QString &forUser)
|
void UserInfoPopup::onGamesReceived(const Response &r, const QString &forUser)
|
||||||
{
|
{
|
||||||
if (forUser != currentUser) {
|
if (forUser != currentUser) {
|
||||||
return; // stale response — different user showing now
|
return; // stale response, different user showing now
|
||||||
}
|
}
|
||||||
|
|
||||||
gamesModel->clear();
|
gamesModel->clear();
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,12 @@ void UserListPainter::drawCardArt(QPainter *painter,
|
||||||
return;
|
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 cardH = rect.height() - 4;
|
||||||
const int totalW = cardRight - rect.left();
|
const int totalW = cardRight - rect.left();
|
||||||
const int marginL = qRound(totalW * params.marginPctL);
|
const int marginL = qRound(totalW * params.marginPctL);
|
||||||
|
|
@ -172,11 +178,14 @@ void UserListPainter::drawCardArt(QPainter *painter,
|
||||||
const int srcX = (scaledW - drawW) / 2;
|
const int srcX = (scaledW - drawW) / 2;
|
||||||
const int srcY = qRound((scaledH - cardH) * params.verticalOffset);
|
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));
|
const int safeSrcY = qBound(0, srcY, qMax(0, scaledH - cardH));
|
||||||
|
|
||||||
QImage img =
|
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);
|
QPainter mask(&img);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue