Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cover art support #278

Merged
merged 86 commits into from
Nov 5, 2014
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
92247eb
Move CoverArtCache::extractEmbeddedCover to a utility class.
rryan Oct 21, 2014
34d48aa
Minor test cleanups.
rryan Oct 21, 2014
af707e4
Prevent deep-copy of QImage while checksumming it and move calculateHash
rryan Oct 21, 2014
f987621
Move CoverArtCache::cropImage to CoverArtUtils.
rryan Oct 21, 2014
5d2fa92
Move CoverArtCache::rescaleBigImage to CoverArtUtils.
rryan Oct 21, 2014
eb265df
Some NULL-safety changes.
rryan Oct 21, 2014
0f3f844
Remove 'widget' from method / var names.
rryan Oct 23, 2014
8cf9b8a
Move CoverArtCache::searchInTrackDirectory to CoverArtUtils.
rryan Oct 23, 2014
1af2430
Misc. fixes.
rryan Oct 26, 2014
f3eff5c
Remove default cover handling from CoverArtCache.
rryan Oct 26, 2014
32d9da9
More misc.
rryan Oct 26, 2014
08edc49
Avoid doing file I/O in the main thread.
rryan Oct 26, 2014
1ecde90
Move CoverInfo to coverart.h. Add CoverArt struct.
rryan Oct 26, 2014
7bbb903
Refactor to use the common structures in coverart.h.
rryan Oct 26, 2014
7c199cb
Cache-key improvements.
rryan Oct 26, 2014
107f9f9
Hash the full-sized image, not the cropped or size-capped image.
rryan Oct 26, 2014
807f621
Only check QPixmapCache if we have a non-empty cover hash.
rryan Oct 26, 2014
679f786
Add a debugging flag to CoverArtCache.
rryan Oct 26, 2014
b1b0f5e
Pull file extension regex generator into a utility file.
rryan Oct 27, 2014
2057607
Add source and type fields to CoverInfo and equality operators.
rryan Oct 27, 2014
cdf1a03
Add CoverArt property to TrackInfoObject.
rryan Oct 27, 2014
8183dc0
Add CoverArt widget to Deere.
rryan Oct 27, 2014
672111c
Add loadCover and selectCoverArtForTrack helper methods to CoverArtUt…
rryan Oct 27, 2014
42c8155
Massive cover art refactor.
rryan Oct 27, 2014
a5f2fc5
Avoid hard-coding size policies.
rryan Oct 27, 2014
5ef00f4
Rework WCoverArt <-> WTrackTableView signal flow.
rryan Oct 27, 2014
0477ae7
Remove hard-coded layout and sizes from WCoverArt.
rryan Oct 27, 2014
7000a9b
Add deck cover art to Deere.
rryan Oct 27, 2014
8572aa4
Remove unused BaseTrackCache update signals/slots.
rryan Oct 27, 2014
59ee4e5
Support drag and drop to/from WCoverArt.
rryan Oct 27, 2014
ae905d5
Use util/time.h instead of GuiTick for user action timing.
rryan Oct 27, 2014
cd4b3c8
Rename issueRepaint to signalWhenDone and remove update of WTrackTabl…
rryan Oct 27, 2014
9c0c1bb
Delete CoverAndAlbumInfo since album info is unused now.
rryan Oct 27, 2014
587ee29
Remove CoverArtCache use from WCoverArtMenu.
rryan Oct 27, 2014
946cb34
Remove unused member variable from TrackCollection.
rryan Oct 27, 2014
356d48c
Get supported cover extensions from CoverArtUtils.
rryan Oct 27, 2014
afdeb96
Add qDebug helpers for CoverArt and CoverInfo.
rryan Oct 28, 2014
0855f56
Early exit in CoverArtCache::requestCover if CoverInfo type is NONE.
rryan Oct 28, 2014
60c5853
Add CoverArtUtils::guessCoverArt helper method.
rryan Oct 28, 2014
e9aa413
No need to manually update() DlgTrackInfo.
rryan Oct 28, 2014
9bbbea5
Improve connection between WCoverArtMenu, WCoverArtLabel, WCoverArt, …
rryan Oct 28, 2014
31991b8
Disconnect TIO changed signals while applying track info edits.
rryan Oct 28, 2014
fb7570e
Don't crop cached pixmaps anymore.
rryan Oct 28, 2014
8e11ac5
Add coverArtUpdated signal to TrackInfoObject.
rryan Oct 28, 2014
da949ae
Fix test.
rryan Oct 28, 2014
8b77640
Allow segregation of multiple users of CoverArtCache.
rryan Oct 28, 2014
359dade
Move cover art to the Deere track text row.
rryan Oct 28, 2014
139a05a
Make a CoverArtUtils::selectCoverArtForTrack variant that doesn't
rryan Oct 28, 2014
c0cd784
Load the player's loaded track into WCoverArt upon creation.
rryan Oct 28, 2014
abf7eca
Use QStylePainter in WCoverArt so we can style the widget with CSS.
rryan Oct 28, 2014
a4132de
Default coverart_source and coverart_type to 0 instead of NULL.
rryan Oct 28, 2014
b90c5c6
Add first pass at cover art scan for UNKNOWN tracks.
rryan Oct 28, 2014
ea87686
Slight phrase tweaking.
rryan Oct 28, 2014
33509d1
Add warning about user data.
rryan Oct 28, 2014
29777ea
Merge remote-tracking branch 'cardinot/coverArtSupport' into
rryan Oct 28, 2014
49fee52
Fix issue where files on disk aren't picked up during existing-tracks…
rryan Oct 29, 2014
43cde9e
Fix QSet<int> signal queueing errors.
rryan Oct 29, 2014
d081ff7
Remove trackId from CoverInfo.
rryan Oct 29, 2014
1d4105c
When a library row's cover art is ready explictly refresh that row.
rryan Oct 29, 2014
dc82b07
Use foreach instead of C++-style iterator.
rryan Oct 29, 2014
94e511e
Address review comments.
rryan Oct 29, 2014
06b1952
Remove unnecessary use of CoverArt.
rryan Oct 29, 2014
0c48c06
Fix UTF-8 test.
rryan Oct 29, 2014
320a803
Change CoverInfo hash to an integer.
rryan Oct 29, 2014
dbe9811
Use cover hash as request reference in CoverArtDelegate.
rryan Oct 29, 2014
f9e9148
Support sorting by cover art.
rryan Oct 30, 2014
343f7c0
Delay trackSelected signal until the user interaction timeout.
rryan Oct 30, 2014
07fd51f
Only repaint cells that were cache misses when we hit the user
rryan Oct 30, 2014
0425bc4
Double-check that we are not overwriting USER_SELECTED covers.
rryan Oct 30, 2014
3d0f864
Delete unused method.
rryan Oct 30, 2014
fa683d8
Switch CoverArt use to CoverInfo.
rryan Oct 30, 2014
3468571
Revert DlgTrackInfo parentage change.
rryan Oct 30, 2014
8dea56a
Call activateWindow on DlgCoverArtFullSize.
rryan Oct 30, 2014
e394457
Compare potential cover filenames case-insensitively.
rryan Oct 31, 2014
dddeb3b
Handle COVER_NONE covers in WCoverArtMenu::slotChange.
rryan Oct 31, 2014
0980b6b
Support multiple-selection for cover art menu in WTrackTableView.
rryan Oct 31, 2014
cf7c539
Show cover art progress on the library scanner dialog.
rryan Oct 31, 2014
a7329e9
Update BaseTrackCache once cover art processing is done.
rryan Oct 31, 2014
4ad6b5d
Only pick non-matching cover files if there is only one option.
rryan Oct 31, 2014
f3e2c5c
Create a security token for files chosen via WCoverArtMenu.
rryan Oct 31, 2014
24874c5
Acquire sandbox tokens in various places for cover art.
rryan Oct 31, 2014
b95c452
Only save images in TIO if it is not NULL
Nov 5, 2014
09b16a3
Reduce code duplication in finding possible Covers
Nov 5, 2014
46fbd24
Move CoverArtUtil tests to own class
Nov 5, 2014
82085f3
Codying style clean up coverartcache
Nov 5, 2014
76113d0
Use COSlave instead of COThread
Nov 5, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Don't crop cached pixmaps anymore.
Scale (preserving aspect ratio) and cache them by (hash, width)
only. Cropping doesn't really save any paint time since
QPainter::drawPixmap is cheap when you don't have to scale the pixmap.
  • Loading branch information
rryan committed Oct 28, 2014
commit fb7570ed35c65257f7acdf1a523bf07a19ce099c
30 changes: 15 additions & 15 deletions src/library/coverartcache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
#include "soundsourceproxy.h"

// Large cover art wastes space in our cache when we typicaly won't show them at
// their full size. This is the max side length we resize images to.
const int kMaxCoverSize = 300;
// their full size. If no width is specified, this is the maximum width cap.
const int kMaxCoverWidth = 300;

const bool sDebug = false;

Expand All @@ -30,12 +30,12 @@ CoverArtCache::~CoverArtCache() {
}

QPixmap CoverArtCache::requestCover(const CoverInfo& requestInfo,
const QSize& croppedSize,
const int desiredWidth,
const bool onlyCached,
const bool signalWhenDone) {
if (sDebug) {
qDebug() << "CoverArtCache::requestCover"
<< requestInfo << croppedSize << onlyCached << signalWhenDone;
<< requestInfo << desiredWidth << onlyCached << signalWhenDone;
}

// TODO(rryan) handle requests for non-library tracks.
Expand Down Expand Up @@ -63,7 +63,7 @@ QPixmap CoverArtCache::requestCover(const CoverInfo& requestInfo,
// having to rescale+crop it ALWAYS (which brings a lot of performance issues).
if (!requestInfo.hash.isEmpty()) {
QString cacheKey = CoverArtUtils::pixmapCacheKey(requestInfo.hash,
croppedSize);
desiredWidth);

QPixmap pixmap;
if (QPixmapCache::find(cacheKey, &pixmap)) {
Expand All @@ -84,7 +84,7 @@ QPixmap CoverArtCache::requestCover(const CoverInfo& requestInfo,
m_runningIds.insert(requestInfo.trackId);
QFutureWatcher<FutureResult>* watcher = new QFutureWatcher<FutureResult>(this);
QFuture<FutureResult> future = QtConcurrent::run(
this, &CoverArtCache::loadCover, requestInfo, croppedSize,
this, &CoverArtCache::loadCover, requestInfo, desiredWidth,
signalWhenDone);
connect(watcher, SIGNAL(finished()), this, SLOT(coverLoaded()));
watcher->setFuture(future);
Expand All @@ -93,16 +93,16 @@ QPixmap CoverArtCache::requestCover(const CoverInfo& requestInfo,

CoverArtCache::FutureResult CoverArtCache::loadCover(
const CoverInfo& info,
const QSize& croppedSize,
const int desiredWidth,
const bool signalWhenDone) {
if (sDebug) {
qDebug() << "CoverArtCache::loadCover"
<< info << croppedSize << signalWhenDone;
<< info << desiredWidth << signalWhenDone;
}

FutureResult res;
res.cover.info = info;
res.croppedSize = croppedSize;
res.desiredWidth = desiredWidth;
res.signalWhenDone = signalWhenDone;
res.cover.image = CoverArtUtils::loadCover(res.cover.info);

Expand All @@ -117,12 +117,12 @@ CoverArtCache::FutureResult CoverArtCache::loadCover(

// Adjust the cover size according to the request or downsize the image for
// efficiency.
if (res.croppedSize.isNull()) {
res.cover.image = CoverArtUtils::maybeResizeImage(res.cover.image,
kMaxCoverSize);
if (res.desiredWidth > 0) {
res.cover.image = CoverArtUtils::resizeImage(res.cover.image,
res.desiredWidth);
} else {
res.cover.image = CoverArtUtils::cropImage(res.cover.image,
res.croppedSize);
res.cover.image = CoverArtUtils::maybeResizeImage(res.cover.image,
kMaxCoverWidth);
}

return res;
Expand All @@ -139,7 +139,7 @@ void CoverArtCache::coverLoaded() {
}

QString cacheKey = CoverArtUtils::pixmapCacheKey(res.cover.info.hash,
res.croppedSize);
res.desiredWidth);
QPixmap pixmap;
if (!QPixmapCache::find(cacheKey, &pixmap) && !res.cover.image.isNull()) {
pixmap.convertFromImage(res.cover.image);
Expand Down
13 changes: 3 additions & 10 deletions src/library/coverartcache.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,6 @@ class CoverArtCache : public QObject, public Singleton<CoverArtCache> {
Q_OBJECT
public:
/* This method is used to request a cover art pixmap.
*
* @param croppedSize : QSize(finalCoverWidth, finalCoverHeight)
* it determines the final cover size.
* Use QSize() to get the original size.
* NOTE!
* the cover will be resized to 'finalCoverWidth' and
* it'll be cropped from the top until the finalCoverHeight' pixel
*
* @param onlyCached : if it is 'true', the method will NOT try to load
* covers from the given 'coverLocation' and it will also NOT run the
Expand All @@ -26,13 +19,13 @@ class CoverArtCache : public QObject, public Singleton<CoverArtCache> {
* a Pixmap if it is already loaded in the QPixmapCache.
*/
QPixmap requestCover(const CoverInfo& info,
const QSize& croppedSize = QSize(0,0),
const int desiredWidth = 0,
const bool onlyCached = false,
const bool signalWhenDone = true);

struct FutureResult {
CoverArt cover;
QSize croppedSize;
int desiredWidth;
bool signalWhenDone;
};

Expand All @@ -51,7 +44,7 @@ class CoverArtCache : public QObject, public Singleton<CoverArtCache> {
// Load cover from path indicated in coverInfo. WARNING: This is run in a
// worker thread.
FutureResult loadCover(const CoverInfo& coverInfo,
const QSize &croppedSize,
const int desiredWidth,
const bool emitSignals);

private:
Expand Down
19 changes: 6 additions & 13 deletions src/library/coverartdelegate.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "library/coverartdelegate.h"
#include "library/coverartcache.h"
#include "library/dao/trackdao.h"
#include "util/math.h"

CoverArtDelegate::CoverArtDelegate(QObject *parent)
: QStyledItemDelegate(parent),
Expand Down Expand Up @@ -78,23 +79,15 @@ void CoverArtDelegate::paint(QPainter *painter,
info.trackId = index.sibling(index.row(), m_iIdColumn).data().toInt();
info.trackLocation = index.sibling(index.row(), m_iTrackLocationColumn).data().toString();

QSize coverSize(100, option.rect.height());

// Do not signal when done sine we don't listen to CoverArtCache for updates
// and instead refresh on a timer in WTrackTableView.
QPixmap pixmap = pCache->requestCover(info, coverSize,
m_bOnlyCachedCover, false);


QPixmap pixmap = pCache->requestCover(info, 100, m_bOnlyCachedCover, false);
if (!pixmap.isNull()) {
int width = pixmap.width();
if (option.rect.width() < width) {
width = option.rect.width();
}

int width = math_min(pixmap.width(), option.rect.width());
int height = math_min(pixmap.height(), option.rect.height());
QRect target(option.rect.x(), option.rect.y(),
width, option.rect.height());
QRect source(0, 0, width, pixmap.height());
width, height);
QRect source(0, 0, target.width(), target.height());
painter->drawPixmap(target, pixmap, source);
}
}
27 changes: 9 additions & 18 deletions src/library/coverartutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@ class CoverArtUtils {
}

static QString pixmapCacheKey(const QString& hash,
const QSize& size) {
if (size.isNull()) {
const int width) {
if (width == 0) {
return QString("CoverArtCache_%1").arg(hash);
}
return QString("CoverArtCache_%1_%2x%3")
.arg(hash)
.arg(size.width())
.arg(size.height());
return QString("CoverArtCache_%1_%2")
.arg(hash).arg(width);
}

// Extracts the first cover art image embedded within the file at
Expand Down Expand Up @@ -87,18 +85,6 @@ class CoverArtUtils {
image.byteCount()));
}

// Crops image to the provided size by first scaling to the appropriate
// width and then cropping off the bottom. If size is taller than the image,
// black pixels are padded on the bottom.
static QImage cropImage(const QImage& image, const QSize& size) {
if (image.isNull()) {
return QImage();
}
QImage result = image.scaledToWidth(size.width(),
Qt::SmoothTransformation);
return result.copy(0, 0, image.width(), size.height());
}

// Resizes the image (preserving aspect ratio) if it is larger than
// maxEdgeSize on either side.
static QImage maybeResizeImage(const QImage& image, int maxEdgeSize) {
Expand All @@ -109,6 +95,11 @@ class CoverArtUtils {
return image;
}

// Resizes the image (preserving aspect ratio) to width.
static QImage resizeImage(const QImage& image, int width) {
return image.scaledToWidth(width, Qt::SmoothTransformation);
}

static QStringList supportedCoverArtExtensions() {
QStringList extensions;
extensions << "jpg" << "jpeg" << "png" << "gif" << "bmp";
Expand Down
8 changes: 4 additions & 4 deletions src/test/coverartcache_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ TEST_F(CoverArtCacheTest, loadCover) {
info.hash = "coverhash"; // fake cover hash

CoverArtCache::FutureResult res;
res = CoverArtCache::loadCover(info, QSize(0,0), false);
res = CoverArtCache::loadCover(info, 0, false);
EXPECT_EQ(info.trackId, res.cover.info.trackId);
EXPECT_QSTRING_EQ(info.coverLocation, res.cover.info.coverLocation);
EXPECT_QSTRING_EQ(info.hash, res.cover.info.hash);
Expand All @@ -82,7 +82,7 @@ TEST_F(CoverArtCacheTest, loadCover) {
info.source = CoverInfo::GUESSED;
info.coverLocation = QString();
info.trackLocation = kTrackLocationTest;
res = CoverArtCache::loadCover(info, QSize(0,0), false);
res = CoverArtCache::loadCover(info, 0, false);
EXPECT_EQ(info.trackId, res.cover.info.trackId);
EXPECT_QSTRING_EQ(QString(), res.cover.info.coverLocation);
EXPECT_QSTRING_EQ(info.hash, res.cover.info.hash);
Expand Down Expand Up @@ -267,14 +267,14 @@ TEST_F(CoverArtCacheTest, searchImage) {
// cLoc_filename = QString(trackdir % "/" % trackBaseName % ".");
// cLoc_filename.append(format);
// EXPECT_TRUE(img.save(cLoc_filename, format));
// res = CoverArtCache::searchImage(cInfoUtf8, QSize(0,0), false);
// res = CoverArtCache::searchImage(cInfoUtf8, 0, false);
// EXPECT_QSTRING_EQ(cLoc_filename, res.cover.info.coverLocation);
// QFile::remove(cLoc_filename);
// // 2. album_name.jpg
// cLoc_albumName = QString(trackdir % "/" % cInfoUtf8.album % ".");
// cLoc_albumName.append(format);
// EXPECT_TRUE(img.save(cLoc_albumName, format));
// res = CoverArtCache::searchImage(cInfoUtf8, QSize(0,0), false);
// res = CoverArtCache::searchImage(cInfoUtf8, 0, false);
// EXPECT_QSTRING_EQ(cLoc_albumName, res.cover.info.coverLocation);
// QFile::remove(cLoc_albumName);

Expand Down