Some attempts to refresh macOS compositor

Took 2 minutes
This commit is contained in:
Lukas Brübach 2026-08-13 23:56:50 +02:00
parent 1c85a4aad8
commit 37bb1786cd
5 changed files with 58 additions and 2 deletions

View file

@ -128,7 +128,16 @@ void GameView::resizeRubberBand(const QPointF &cursorPoint, int selectedCount)
QPoint cursor = cursorPoint.toPoint();
QRect rect = QRect(mapFromScene(selectionOrigin), cursor).normalized();
// Explicitly repaint the strip the band just vacated. When the band is a
// child widget, the viewport does not reliably receive the exposed region
// on all platforms (notably macOS), which would leave stale band pixels on
// screen until the next unrelated repaint.
const QRect previousGeometry = rubberBand->geometry();
rubberBand->setGeometry(rect);
if (viewport()) {
viewport()->update(previousGeometry.united(rect));
}
if (!SettingsCache::instance().userInterface().getShowDragSelectionCount()) {
dragCountLabel->hide();
@ -171,7 +180,14 @@ void GameView::stopRubberBand()
return;
}
// Same rationale as resizeRubberBand. Make sure the area under the band is
// repainted, since some platforms skip the exposed-region update of a
// hidden child widget.
const QRect bandGeometry = rubberBand->geometry();
rubberBand->hide();
if (viewport()) {
viewport()->update(bandGeometry);
}
dragCountLabel->hide();
}

View file

@ -190,15 +190,40 @@ void PlayerGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem
const QRectF srcRect = computeArtSourceRect(playmatPixmap.size(), playmatParams);
const QRectF dstRect = coverFitRect(combinedArea, srcRect.size());
painter->save();
painter->setClipRect(combinedArea);
painter->drawPixmap(dstRect, playmatPixmap, srcRect);
painter->setClipping(false);
painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
// Render from a down-scaled copy of the art so the full-resolution source
// pixmap is never re-sampled at a tiny device size (also much cheaper than
// scaling it on every frame).
const QPixmap scaledPixmap = scaledPlaymatFor(srcRect, painter->worldTransform().mapRect(dstRect).size());
painter->drawPixmap(dstRect, scaledPixmap, QRectF(scaledPixmap.rect()));
painter->restore();
if (!playmatAttribution.isEmpty()) {
paintArtAttribution(*painter, combinedArea, playmatAttribution, Qt::AlignRight | Qt::AlignBottom, 0.8);
}
}
QPixmap PlayerGraphicsItem::scaledPlaymatFor(const QRectF &srcRect, const QSizeF &deviceDstSize)
{
// Bucket the render size so the source pixmap is re-scaled at most once per
// zoom step instead of once per frame.
constexpr int bucketSize = 32;
const QSize target = QSize(qMax(1, qRound(deviceDstSize.width() / bucketSize) * bucketSize),
qMax(1, qRound(deviceDstSize.height() / bucketSize) * bucketSize))
.boundedTo(srcRect.toAlignedRect().size());
if (scaledPlaymatKey != target) {
const QPixmap crop = playmatPixmap.copy(srcRect.toAlignedRect());
scaledPlaymatPixmap = crop.scaled(target, Qt::KeepAspectRatio, Qt::SmoothTransformation);
scaledPlaymatKey = target;
}
return scaledPlaymatPixmap;
}
void PlayerGraphicsItem::processSceneSizeChange(int newPlayerWidth)
{
// Extend table (and hand, if horizontal) to accommodate the new player width.
@ -372,6 +397,7 @@ void PlayerGraphicsItem::updatePlaymat()
}
playmatParams = params;
scaledPlaymatKey = QSize(); // the art crop depends on the params, drop any cached scale
ExactCard card = CardDatabaseManager::query()->getCard(playmatCard);
if (!card) {
@ -409,6 +435,7 @@ void PlayerGraphicsItem::clearPlaymat()
if (hasPlaymat) {
hasPlaymat = false;
playmatPixmap = QPixmap();
scaledPlaymatKey = QSize();
emit playmatChanged(false);
update();
}

View file

@ -149,6 +149,8 @@ private:
bool handVisible = false;
QPixmap playmatPixmap;
QPixmap scaledPlaymatPixmap; // down-scaled copy of playmatPixmap for the current render size
QSize scaledPlaymatKey; // size bucket scaledPlaymatPixmap was rendered for
PlaymatParams playmatParams;
QString playmatAttribution;
bool hasPlaymat = false;
@ -160,6 +162,9 @@ private slots:
void clearPlaymat();
void updatePlaymat();
void onPlaymatPixmapReady();
private:
QPixmap scaledPlaymatFor(const QRectF &srcRect, const QSizeF &deviceDstSize);
};
#endif // COCKATRICE_PLAYER_GRAPHICS_ITEM_H

View file

@ -43,6 +43,9 @@ void StackZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*opti
void StackZone::onPlaymatChanged(bool active)
{
playmatActive = active;
// See TableZone::onPlaymatChanged for the rationale. Translucent overlay
// over a dynamic playmat should not be held in the device cache.
setCacheMode(active ? QGraphicsItem::NoCache : QGraphicsItem::DeviceCoordinateCache);
update();
}

View file

@ -121,6 +121,11 @@ void TableZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*opti
void TableZone::onPlaymatChanged(bool active)
{
playmatActive = active;
// While a playmat is shown the zone paints a translucent overlay over the
// dynamic playmat behind it. Keep it out of the device cache so the cached
// pixels are never stale relative to the playmat (and to avoid compositing
// artifacts of cached translucent content on some platforms).
setCacheMode(active ? QGraphicsItem::NoCache : QGraphicsItem::DeviceCoordinateCache);
update();
}