Skip to content

Commit

Permalink
QML: Add custom image provider to for showing cover art
Browse files Browse the repository at this point in the history
  • Loading branch information
Holzhaus committed May 26, 2021
1 parent 206b078 commit 573abe4
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/skin/legacy/legacyskinparser.cpp
src/skin/legacy/pixmapsource.cpp
src/skin/legacy/legacyskin.cpp
src/skin/qml/asyncimageprovider.cpp
src/skin/qml/qmlcontrolproxy.cpp
src/skin/qml/qmlplayermanagerproxy.cpp
src/skin/qml/qmlplayerproxy.cpp
Expand Down
55 changes: 55 additions & 0 deletions src/skin/qml/asyncimageprovider.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include "skin/qml/asyncimageprovider.h"

#include "library/coverartcache.h"

namespace mixxx {
namespace skin {
namespace qml {

AsyncImageResponse::AsyncImageResponse(const QString& id, const QSize& requestedSize)
: m_id(id), m_requestedSize(requestedSize) {
setAutoDelete(false);
}

QQuickTextureFactory* AsyncImageResponse::textureFactory() const {
return QQuickTextureFactory::textureFactoryForImage(m_image);
}

void AsyncImageResponse::run() {
if (m_id.startsWith(QStringLiteral("coverart/"))) {
QString trackLocation = QString::fromUtf8(QByteArray::fromBase64(
m_id.mid(9).toLatin1(), QByteArray::Base64UrlEncoding));

// TODO: This code does not allow to override embedded cover art with
// a custom image, which is possible in Mixxx. We need to access the
// actual CoverInfo of the track instead of constructing a default
// instance on the fly.
CoverInfo coverInfo(CoverInfoRelative(), trackLocation);
coverInfo.type = CoverInfoRelative::METADATA;
CoverInfo::LoadedImage loadedImage = coverInfo.loadImage();
if (loadedImage.result != CoverInfo::LoadedImage::Result::Ok) {
coverInfo.type = CoverInfoRelative::FILE;
loadedImage = coverInfo.loadImage();
}
m_image = loadedImage.image;
} else {
qWarning() << "ImageProvider: Unknown ID " << m_id;
}

if (!m_image.isNull() && m_requestedSize.isValid()) {
m_image = m_image.scaled(m_requestedSize);
}

emit finished();
}

QQuickImageResponse* AsyncImageProvider::requestImageResponse(
const QString& id, const QSize& requestedSize) {
AsyncImageResponse* response = new AsyncImageResponse(id, requestedSize);
pool.start(response);
return response;
}

} // namespace qml
} // namespace skin
} // namespace mixxx
36 changes: 36 additions & 0 deletions src/skin/qml/asyncimageprovider.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <QImage>
#include <QQuickAsyncImageProvider>
#include <QRunnable>
#include <QSize>
#include <QString>
#include <QThreadPool>

#include "library/coverart.h"

namespace mixxx {
namespace skin {
namespace qml {

class AsyncImageResponse : public QQuickImageResponse, public QRunnable {
public:
AsyncImageResponse(const QString& id, const QSize& requestedSize);
QQuickTextureFactory* textureFactory() const override;
void run() override;

QString m_id;
QSize m_requestedSize;
QImage m_image;
};

class AsyncImageProvider : public QQuickAsyncImageProvider {
public:
QQuickImageResponse* requestImageResponse(
const QString& id, const QSize& requestedSize) override;

private:
QThreadPool pool;
};

} // namespace qml
} // namespace skin
} // namespace mixxx
14 changes: 14 additions & 0 deletions src/skin/qml/qmlplayerproxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ void QmlPlayerProxy::slotTrackChanged() {
emit commentChanged();
emit keyChanged();
emit colorChanged();
emit coverArtUrlChanged();
}

PROPERTY_IMPL(QString, artist, getArtist, setArtist)
Expand Down Expand Up @@ -118,6 +119,19 @@ void QmlPlayerProxy::setColor(const QColor& value) {
}
}

QUrl QmlTrackPlayerProxy::getCoverArtUrl() const {
const TrackPointer pTrack = m_pCurrentTrack;
if (pTrack == nullptr) {
return QUrl();
}

const CoverInfo coverInfo = pTrack->getCoverInfoWithLocation();
QUrl url("image://mixxx/coverart/");
return url.resolved(
QString::fromLatin1(coverInfo.trackLocation.toUtf8().toBase64(
QByteArray::Base64UrlEncoding)));
}

} // namespace qml
} // namespace skin
} // namespace mixxx
4 changes: 4 additions & 0 deletions src/skin/qml/qmlplayerproxy.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <QColor>
#include <QObject>
#include <QString>
#include <QUrl>

#include "track/track.h"

Expand All @@ -27,6 +28,7 @@ class QmlPlayerProxy : public QObject {
Q_PROPERTY(QString comment READ getComment WRITE setComment NOTIFY commentChanged)
Q_PROPERTY(QString key READ getKeyText WRITE setKeyText NOTIFY keyChanged)
Q_PROPERTY(QColor color READ getColor WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(QUrl coverArtUrl READ getCoverArtUrl NOTIFY coverArtUrlChanged)

public:
explicit QmlPlayerProxy(BaseTrackPlayer* pTrackPlayer, QObject* parent);
Expand All @@ -45,6 +47,7 @@ class QmlPlayerProxy : public QObject {
QString getComment() const;
QString getKeyText() const;
QColor getColor() const;
QUrl getCoverArtUrl() const;

public slots:
void slotTrackLoaded(TrackPointer pTrack);
Expand Down Expand Up @@ -84,6 +87,7 @@ class QmlPlayerProxy : public QObject {
void commentChanged();
void keyChanged();
void colorChanged();
void coverArtUrlChanged();

private:
BaseTrackPlayer* m_pTrackPlayer;
Expand Down
2 changes: 2 additions & 0 deletions src/skin/qml/qmlskin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <QQuickWidget>

#include "coreservices.h"
#include "skin/qml/asyncimageprovider.h"
#include "skin/qml/qmlcontrolproxy.h"
#include "skin/qml/qmlplayermanagerproxy.h"
#include "skin/qml/qmlplayerproxy.h"
Expand Down Expand Up @@ -119,6 +120,7 @@ QWidget* QmlSkin::loadSkin(QWidget* pParent,
QQuickWidget* pWidget = new QQuickWidget(pParent);
pWidget->engine()->setBaseUrl(QUrl::fromLocalFile(m_path.absoluteFilePath()));
pWidget->engine()->addImportPath(m_path.absoluteFilePath());
pWidget->engine()->addImageProvider(QStringLiteral("mixxx"), new AsyncImageProvider());
pWidget->setSource(QUrl::fromLocalFile(m_path.absoluteFilePath() +
QStringLiteral("/") + kMainQmlFileName));
pWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
Expand Down

0 comments on commit 573abe4

Please sign in to comment.