From 22bb2f5b68dc9e3c3608629629460513bf213938 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Wed, 25 Apr 2018 15:04:38 +0200 Subject: Add handling of device pixel ratio to animated sprites MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I472f61241d1875daf0de0a597bf27c019314f48f Task-number: QTBUG-50119 Reviewed-by: Mitch Curtis Reviewed-by: Morten Johan Sørvig --- src/particles/qquickimageparticle.cpp | 2 +- src/quick/items/qquickanimatedsprite.cpp | 4 +- src/quick/items/qquickimagebase_p.h | 2 +- src/quick/items/qquicksprite.cpp | 7 ++- src/quick/items/qquicksprite_p.h | 12 ++++++ src/quick/items/qquickspriteengine.cpp | 50 ++++++++++++++-------- src/quick/items/qquickspritesequence.cpp | 2 +- .../adaptations/software/qsgsoftwarespritenode.cpp | 2 +- 8 files changed, 56 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/particles/qquickimageparticle.cpp b/src/particles/qquickimageparticle.cpp index d2fb78d72a..abd7793a74 100644 --- a/src/particles/qquickimageparticle.cpp +++ b/src/particles/qquickimageparticle.cpp @@ -1321,7 +1321,7 @@ void QQuickImageParticle::finishBuildParticleNodes(QSGNode** node) m_material = SpriteMaterial::createMaterial(); if (imageLoaded) getState(m_material)->texture = QSGPlainTexture::fromImage(image); - getState(m_material)->animSheetSize = QSizeF(image.size()); + getState(m_material)->animSheetSize = QSizeF(image.size() / image.devicePixelRatioF()); if (m_spriteEngine) m_spriteEngine->setCount(m_count); Q_FALLTHROUGH(); diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp index 190c48ac88..887c30f999 100644 --- a/src/quick/items/qquickanimatedsprite.cpp +++ b/src/quick/items/qquickanimatedsprite.cpp @@ -527,6 +527,8 @@ void QQuickAnimatedSprite::setSource(QUrl arg) Q_D(QQuickAnimatedSprite); if (d->m_sprite->m_source != arg) { + const qreal targetDevicePixelRatio = (window() ? window()->effectiveDevicePixelRatio() : qApp->devicePixelRatio()); + d->m_sprite->setDevicePixelRatio(targetDevicePixelRatio); d->m_sprite->setSource(arg); Q_EMIT sourceChanged(arg); reloadImage(); @@ -713,7 +715,7 @@ QSGSpriteNode* QQuickAnimatedSprite::initNode() QSGSpriteNode *node = d->sceneGraphContext()->createSpriteNode(); - d->m_sheetSize = QSize(image.size()); + d->m_sheetSize = QSize(image.size() / image.devicePixelRatioF()); node->setTexture(window()->createTextureFromImage(image)); d->m_spriteEngine->start(0); node->setTime(0.0f); diff --git a/src/quick/items/qquickimagebase_p.h b/src/quick/items/qquickimagebase_p.h index eb04a1d162..d8d0be9b8c 100644 --- a/src/quick/items/qquickimagebase_p.h +++ b/src/quick/items/qquickimagebase_p.h @@ -98,7 +98,7 @@ public: virtual void setAutoTransform(bool transform); bool autoTransform() const; - void resolve2xLocalFile(const QUrl &url, qreal targetDevicePixelRatio, QUrl *sourceUrl, qreal *sourceDevicePixelRatio); + static void resolve2xLocalFile(const QUrl &url, qreal targetDevicePixelRatio, QUrl *sourceUrl, qreal *sourceDevicePixelRatio); // Use a virtual rather than a signal->signal to avoid the huge // connect/conneciton overhead for this rare case. diff --git a/src/quick/items/qquicksprite.cpp b/src/quick/items/qquicksprite.cpp index 6b8567439b..8c59a68982 100644 --- a/src/quick/items/qquicksprite.cpp +++ b/src/quick/items/qquicksprite.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qquicksprite_p.h" +#include "qquickimagebase_p.h" #include #include #include @@ -222,6 +223,7 @@ QQuickSprite::QQuickSprite(QObject *parent) , m_frameDuration(unsetDuration) , m_frameDurationVariation(0) , m_frameSync(false) + , m_devicePixelRatio(1.0) { } @@ -265,7 +267,10 @@ void QQuickSprite::startImageLoading() if (!e) qWarning() << "QQuickSprite: Cannot find QQmlEngine - this class is only for use in QML and may not work"; } - m_pix.load(e, m_source); + QUrl loadUrl = m_source; + QQuickImageBase::resolve2xLocalFile(m_source, m_devicePixelRatio, &loadUrl, &m_devicePixelRatio); + + m_pix.load(e, loadUrl); } } diff --git a/src/quick/items/qquicksprite_p.h b/src/quick/items/qquicksprite_p.h index 8e119a80a9..fab9e75190 100644 --- a/src/quick/items/qquicksprite_p.h +++ b/src/quick/items/qquicksprite_p.h @@ -168,6 +168,16 @@ public: return m_frameSync; } + void setDevicePixelRatio(qreal dpr) + { + m_devicePixelRatio = dpr; + } + + qreal devicePixelRatio() const + { + return m_devicePixelRatio; + } + Q_SIGNALS: void sourceChanged(QUrl arg); @@ -308,6 +318,7 @@ private: friend class QQuickAnimatedSprite; friend class QQuickSpriteEngine; friend class QQuickStochasticEngine; + int m_generatedCount; int m_framesPerRow; int m_rowY; @@ -325,6 +336,7 @@ private: int m_frameDuration; int m_frameDurationVariation; bool m_frameSync; + qreal m_devicePixelRatio; QQuickPixmap m_pix; }; diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp index 1fd15e61e3..03f0fa94a4 100644 --- a/src/quick/items/qquickspriteengine.cpp +++ b/src/quick/items/qquickspriteengine.cpp @@ -390,6 +390,7 @@ QImage QQuickSpriteEngine::assembledImage(int maxSize) int w = 0; m_maxFrames = 0; m_imageStateCount = 0; + qreal pixelRatio = 1.0; for (QQuickSprite* state : qAsConst(m_sprites)) { if (state->frames() > m_maxFrames) @@ -413,6 +414,8 @@ QImage QQuickSpriteEngine::assembledImage(int maxSize) if (!state->m_frameHeight) state->m_frameHeight = img.height(); + pixelRatio = qMax(pixelRatio, state->devicePixelRatio()); + if (state->frames() * state->frameWidth() > maxSize){ struct helper{ static int divRoundUp(int a, int b){return (a+b-1)/b;} @@ -437,43 +440,58 @@ QImage QQuickSpriteEngine::assembledImage(int maxSize) } } + if (h > maxSize){ + qWarning() << "SpriteEngine: Too many animations to fit in one texture..."; + qWarning() << "SpriteEngine: Your texture max size today is " << maxSize; + return QImage(); + } + //maxFrames is max number in a line of the texture - QImage image(w, h, QImage::Format_ARGB32_Premultiplied); + QImage image(w * pixelRatio, h * pixelRatio, QImage::Format_ARGB32_Premultiplied); + image.setDevicePixelRatio(pixelRatio); image.fill(0); QPainter p(&image); int y = 0; for (QQuickSprite* state : qAsConst(m_sprites)) { QImage img(state->m_pix.image()); - int frameWidth = state->m_frameWidth; - int frameHeight = state->m_frameHeight; - if (img.height() == frameHeight && img.width() < maxSize){//Simple case - p.drawImage(0,y,img.copy(state->m_frameX,0,state->m_frames * frameWidth, frameHeight)); + const int frameWidth = state->m_frameWidth; + const int frameHeight = state->m_frameHeight; + const int imgHeight = img.height() / state->devicePixelRatio(); + const int imgWidth = img.width() / state->devicePixelRatio(); + if (imgHeight == frameHeight && imgWidth < maxSize){ //Simple case + p.drawImage(QRect(0, y, state->m_frames * frameWidth, frameHeight), + img, + QRect(state->m_frameX * state->devicePixelRatio(), 0, state->m_frames * frameWidth * state->devicePixelRatio(), frameHeight * state->devicePixelRatio())); state->m_rowStartX = 0; state->m_rowY = y; y += frameHeight; - }else{//Chopping up image case - state->m_framesPerRow = image.width()/frameWidth; + } else { //Chopping up image case + state->m_framesPerRow = w/frameWidth; state->m_rowY = y; int x = 0; int curX = state->m_frameX; int curY = state->m_frameY; int framesLeft = state->frames(); while (framesLeft > 0){ - if (image.width() - x + curX <= img.width()){//finish a row in image (dest) - int copied = image.width() - x; + if (w - x + curX <= imgWidth){//finish a row in image (dest) + int copied = w - x; framesLeft -= copied/frameWidth; - p.drawImage(x,y,img.copy(curX,curY,copied,frameHeight)); + p.drawImage(QRect(x, y, copied, frameHeight), + img, + QRect(curX * state->devicePixelRatio(), curY * state->devicePixelRatio(), copied * state->devicePixelRatio(), frameHeight * state->devicePixelRatio())); y += frameHeight; curX += copied; x = 0; - if (curX == img.width()){ + if (curX == imgWidth){ curX = 0; curY += frameHeight; } }else{//finish a row in img (src) - int copied = img.width() - curX; + int copied = imgWidth - curX; framesLeft -= copied/frameWidth; - p.drawImage(x,y,img.copy(curX,curY,copied,frameHeight)); + p.drawImage(QRect(x, y, copied, frameHeight), + img, + QRect(curX * state->devicePixelRatio(), curY * state->devicePixelRatio(), copied * state->devicePixelRatio(), frameHeight * state->devicePixelRatio())); curY += frameHeight; x += copied; curX = 0; @@ -484,12 +502,6 @@ QImage QQuickSpriteEngine::assembledImage(int maxSize) } } - if (image.height() > maxSize){ - qWarning() << "SpriteEngine: Too many animations to fit in one texture..."; - qWarning() << "SpriteEngine: Your texture max size today is " << maxSize; - return QImage(); - } - #ifdef SPRITE_IMAGE_DEBUG QString fPath = QDir::tempPath() + "/SpriteImage.%1.png"; int acc = 0; diff --git a/src/quick/items/qquickspritesequence.cpp b/src/quick/items/qquickspritesequence.cpp index 72761ab82b..df6a6e336e 100644 --- a/src/quick/items/qquickspritesequence.cpp +++ b/src/quick/items/qquickspritesequence.cpp @@ -226,7 +226,7 @@ QSGSpriteNode *QQuickSpriteSequence::initNode() QSGSpriteNode *node = d->sceneGraphContext()->createSpriteNode(); - d->m_sheetSize = QSize(image.size()); + d->m_sheetSize = QSize(image.size() / image.devicePixelRatioF()); node->setTexture(window()->createTextureFromImage(image)); d->m_spriteEngine->start(0); node->setTime(0.0f); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp index ba7bbc2d11..d4e5e98d68 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp @@ -123,7 +123,7 @@ void QSGSoftwareSpriteNode::paint(QPainter *painter) // XXX try to do some kind of interpolation between sourceA and sourceB using time painter->drawPixmap(QRectF(0, 0, m_size.width(), m_size.height()), pixmap, - QRectF(m_sourceA, m_spriteSize)); + QRectF(m_sourceA * pixmap.devicePixelRatioF(), m_spriteSize * pixmap.devicePixelRatioF())); } bool QSGSoftwareSpriteNode::isOpaque() const -- cgit v1.2.3