mirror of
https://code.videolan.org/videolan/vlc
synced 2024-08-27 04:21:53 +02:00
qt: Create CoverGenerator
This class can be useful to retrieve composed thumbnails for groups, playlists and genres. It supports a few options like custom size, count and division type.
This commit is contained in:
parent
80b1d99153
commit
22bc7af601
@ -218,6 +218,8 @@ libqt_plugin_la_SOURCES = \
|
||||
gui/qt/util/audio_device_model.cpp \
|
||||
gui/qt/util/audio_device_model.hpp \
|
||||
gui/qt/util/color_scheme_model.cpp gui/qt/util/color_scheme_model.hpp \
|
||||
gui/qt/util/covergenerator.cpp \
|
||||
gui/qt/util/covergenerator.hpp \
|
||||
gui/qt/util/imageluminanceextractor.cpp gui/qt/util/imageluminanceextractor.hpp \
|
||||
gui/qt/util/imagehelper.cpp gui/qt/util/imagehelper.hpp \
|
||||
gui/qt/util/i18n.cpp gui/qt/util/i18n.hpp \
|
||||
@ -374,6 +376,7 @@ nodist_libqt_plugin_la_SOURCES = \
|
||||
gui/qt/util/asynctask.moc.cpp \
|
||||
gui/qt/util/audio_device_model.moc.cpp \
|
||||
gui/qt/util/color_scheme_model.moc.cpp \
|
||||
gui/qt/util/covergenerator.moc.cpp \
|
||||
gui/qt/util/imageluminanceextractor.moc.cpp \
|
||||
gui/qt/util/i18n.moc.cpp \
|
||||
gui/qt/util/listcache.moc.cpp \
|
||||
|
379
modules/gui/qt/util/covergenerator.cpp
Normal file
379
modules/gui/qt/util/covergenerator.cpp
Normal file
@ -0,0 +1,379 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (C) 2021 VLC authors and VideoLAN
|
||||
*
|
||||
* Authors: Benjamin Arnaud <bunjee@omega.gg>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* ( at your option ) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "covergenerator.hpp"
|
||||
|
||||
// VLC includes
|
||||
#include "qt.hpp"
|
||||
|
||||
// MediaLibrary includes
|
||||
#include "medialibrary/mlhelper.hpp"
|
||||
|
||||
// Qt includes
|
||||
#include <QDir>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsPixmapItem>
|
||||
#include <QGraphicsBlurEffect>
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// Static variables
|
||||
|
||||
static const QString COVERGENERATOR_STORAGE = "/art/qt-covers";
|
||||
|
||||
static const int COVERGENERATOR_COUNT = 2;
|
||||
|
||||
static const QString COVERGENERATOR_DEFAULT = ":/noart.png";
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// Ctor / dtor
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
CoverGenerator::CoverGenerator(vlc_medialibrary_t * ml, const MLItemId & itemId, int index)
|
||||
: m_ml(ml)
|
||||
, m_id(itemId)
|
||||
, m_index(index)
|
||||
, m_countX(COVERGENERATOR_COUNT)
|
||||
, m_countY(COVERGENERATOR_COUNT)
|
||||
, m_split(Divide)
|
||||
, m_smooth(false)
|
||||
, m_blur(0)
|
||||
, m_default(COVERGENERATOR_DEFAULT) {}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// Interface
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
/* Q_INVOKABLE */ MLItemId CoverGenerator::getId()
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
/* Q_INVOKABLE */ int CoverGenerator::getIndex()
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
/* Q_INVOKABLE */ void CoverGenerator::setSize(const QSize & size)
|
||||
{
|
||||
m_size = size;
|
||||
}
|
||||
|
||||
/* Q_INVOKABLE */ void CoverGenerator::setCountX(int x)
|
||||
{
|
||||
m_countX = x;
|
||||
}
|
||||
|
||||
/* Q_INVOKABLE */ void CoverGenerator::setCountY(int y)
|
||||
{
|
||||
m_countY = y;
|
||||
}
|
||||
|
||||
/* Q_INVOKABLE */ void CoverGenerator::setSplit(Split split)
|
||||
{
|
||||
m_split = split;
|
||||
}
|
||||
|
||||
/* Q_INVOKABLE */ void CoverGenerator::setSmooth(bool enabled)
|
||||
{
|
||||
m_smooth = enabled;
|
||||
}
|
||||
|
||||
/* Q_INVOKABLE */ void CoverGenerator::setBlur(int radius)
|
||||
{
|
||||
m_blur = radius;
|
||||
}
|
||||
|
||||
/* Q_INVOKABLE */ void CoverGenerator::setDefaultThumbnail(const QString & fileName)
|
||||
{
|
||||
m_default = fileName;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// QRunnable implementation
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
QString CoverGenerator::execute() /* override */
|
||||
{
|
||||
QDir dir(config_GetUserDir(VLC_CACHE_DIR) + COVERGENERATOR_STORAGE);
|
||||
|
||||
dir.mkpath(dir.absolutePath());
|
||||
|
||||
vlc_ml_parent_type type = m_id.type;
|
||||
|
||||
int64_t id = m_id.id;
|
||||
|
||||
QString string = getStringType(type);
|
||||
|
||||
QString fileName = QString("%1_thumbnail_%2.jpg").arg(string).arg(id);
|
||||
|
||||
fileName = dir.absoluteFilePath(fileName);
|
||||
|
||||
if (dir.exists(fileName))
|
||||
{
|
||||
return fileName;
|
||||
}
|
||||
|
||||
QStringList thumbnails;
|
||||
|
||||
int count = m_countX * m_countY;
|
||||
|
||||
if (type == VLC_ML_PARENT_GENRE)
|
||||
thumbnails = getGenre(count, id);
|
||||
else
|
||||
thumbnails = getMedias(count, id, type);
|
||||
|
||||
if (thumbnails.isEmpty())
|
||||
{
|
||||
if (m_split == CoverGenerator::Duplicate)
|
||||
{
|
||||
while (thumbnails.count() != count)
|
||||
{
|
||||
thumbnails.append(m_default);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
thumbnails.append(m_default);
|
||||
|
||||
m_countX = 1;
|
||||
m_countY = 1;
|
||||
}
|
||||
}
|
||||
else if (m_split == CoverGenerator::Duplicate)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
while (thumbnails.count() != count)
|
||||
{
|
||||
thumbnails.append(thumbnails.at(index));
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else // if (m_split == CoverGenerator::Divide)
|
||||
{
|
||||
// NOTE: This handles the 2x2 case.
|
||||
if (thumbnails.count() == 2)
|
||||
{
|
||||
m_countX = 2;
|
||||
m_countY = 1;
|
||||
}
|
||||
}
|
||||
|
||||
QImage image(m_size, QImage::Format_RGB32);
|
||||
|
||||
image.fill(Qt::white);
|
||||
|
||||
QPainter painter;
|
||||
|
||||
painter.begin(&image);
|
||||
|
||||
draw(painter, thumbnails);
|
||||
|
||||
painter.end();
|
||||
|
||||
if (m_blur > 0)
|
||||
blur(&image);
|
||||
|
||||
image.save(fileName, "jpg");
|
||||
|
||||
return fileName;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// Private functions
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
void CoverGenerator::draw(QPainter & painter, const QStringList & fileNames)
|
||||
{
|
||||
int count = fileNames.count();
|
||||
|
||||
int width = m_size.width() / m_countX;
|
||||
int height = m_size.height() / m_countY;
|
||||
|
||||
for (int y = 0; y < m_countY; y++)
|
||||
{
|
||||
for (int x = 0; x < m_countX; x++)
|
||||
{
|
||||
int index = m_countX * y + x;
|
||||
|
||||
if (index == count) return;
|
||||
|
||||
QRect rect;
|
||||
|
||||
// NOTE: This handles the wider thumbnail case (e.g. for a 2x1 grid).
|
||||
if (index == count - 1 && x != m_countX - 1)
|
||||
{
|
||||
rect = QRect(width * x, height * y, width * m_countX - x, height);
|
||||
}
|
||||
else
|
||||
rect = QRect(width * x, height * y, width, height);
|
||||
|
||||
drawImage(painter, fileNames.at(index), rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CoverGenerator::drawImage(QPainter & painter, const QString & fileName, const QRect & target)
|
||||
{
|
||||
QImage image;
|
||||
|
||||
if (fileName.isEmpty())
|
||||
image.load(m_default);
|
||||
else
|
||||
image.load(fileName);
|
||||
|
||||
// NOTE: This image does not seem valid so we paint the placeholder instead.
|
||||
if (image.isNull())
|
||||
{
|
||||
image.load(m_default);
|
||||
}
|
||||
|
||||
// NOTE: Should we use Qt::SmoothTransformation or favor efficiency ?
|
||||
if (m_smooth)
|
||||
image = image.scaled(target.size(), Qt::KeepAspectRatioByExpanding,
|
||||
Qt::SmoothTransformation);
|
||||
else
|
||||
image = image.scaled(target.size(), Qt::KeepAspectRatioByExpanding);
|
||||
|
||||
int x = (image.width () - target.width ()) / 2;
|
||||
int y = (image.height() - target.height()) / 2;
|
||||
|
||||
QRect source(x, y, target.width(), target.height());
|
||||
|
||||
painter.drawImage(target, image, source);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
// FIXME: This implementation is not ideal and uses a dedicated QGraphicsScene.
|
||||
void CoverGenerator::blur(QImage * image)
|
||||
{
|
||||
assert(image);
|
||||
|
||||
QGraphicsScene scene;
|
||||
|
||||
QGraphicsPixmapItem item(QPixmap::fromImage(*image));
|
||||
|
||||
QGraphicsBlurEffect effect;
|
||||
|
||||
effect.setBlurRadius(m_blur);
|
||||
|
||||
effect.setBlurHints(QGraphicsBlurEffect::QualityHint);
|
||||
|
||||
item.setGraphicsEffect(&effect);
|
||||
|
||||
scene.addItem(&item);
|
||||
|
||||
QImage result(image->size(), QImage::Format_ARGB32);
|
||||
|
||||
QPainter painter(&result);
|
||||
|
||||
scene.render(&painter);
|
||||
|
||||
*image = result;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
QString CoverGenerator::getStringType(vlc_ml_parent_type type) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VLC_ML_PARENT_GENRE:
|
||||
return "genre";
|
||||
case VLC_ML_PARENT_GROUP:
|
||||
return "group";
|
||||
case VLC_ML_PARENT_PLAYLIST:
|
||||
return "playlist";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
QStringList CoverGenerator::getGenre(int count, int64_t id) const
|
||||
{
|
||||
QStringList thumbnails;
|
||||
|
||||
vlc_ml_query_params_t params;
|
||||
|
||||
memset(¶ms, 0, sizeof(vlc_ml_query_params_t));
|
||||
|
||||
// NOTE: We retrieve twice the count to maximize our chances to get a valid thumbnail.
|
||||
params.i_nbResults = count * 2;
|
||||
|
||||
ml_unique_ptr<vlc_ml_album_list_t> list(vlc_ml_list_genre_albums(m_ml, ¶ms, id));
|
||||
|
||||
for (const vlc_ml_album_t & album : ml_range_iterate<vlc_ml_album_t>(list))
|
||||
{
|
||||
if (album.thumbnails[VLC_ML_THUMBNAIL_SMALL].i_status != VLC_ML_THUMBNAIL_STATUS_AVAILABLE)
|
||||
continue;
|
||||
|
||||
QUrl url(album.thumbnails[VLC_ML_THUMBNAIL_SMALL].psz_mrl);
|
||||
|
||||
// NOTE: We only want local files to compose the cover.
|
||||
if (url.isLocalFile() == false)
|
||||
continue;
|
||||
|
||||
thumbnails.append(url.path());
|
||||
|
||||
if (thumbnails.count() == count)
|
||||
return thumbnails;
|
||||
}
|
||||
|
||||
return thumbnails;
|
||||
}
|
||||
|
||||
QStringList CoverGenerator::getMedias(int count, int64_t id, vlc_ml_parent_type type) const
|
||||
{
|
||||
QStringList thumbnails;
|
||||
|
||||
vlc_ml_query_params_t params;
|
||||
|
||||
memset(¶ms, 0, sizeof(vlc_ml_query_params_t));
|
||||
|
||||
// NOTE: We retrieve twice the count to maximize our chances to get a valid thumbnail.
|
||||
params.i_nbResults = count * 2;
|
||||
|
||||
ml_unique_ptr<vlc_ml_media_list_t> list(vlc_ml_list_media_of(m_ml, ¶ms, type, id));
|
||||
|
||||
for (const vlc_ml_media_t & media : ml_range_iterate<vlc_ml_media_t>(list))
|
||||
{
|
||||
if (media.thumbnails[VLC_ML_THUMBNAIL_SMALL].i_status != VLC_ML_THUMBNAIL_STATUS_AVAILABLE)
|
||||
continue;
|
||||
|
||||
QUrl url(media.thumbnails[VLC_ML_THUMBNAIL_SMALL].psz_mrl);
|
||||
|
||||
// NOTE: We only want local files to compose the cover.
|
||||
if (url.isLocalFile() == false)
|
||||
continue;
|
||||
|
||||
thumbnails.append(url.path());
|
||||
|
||||
if (thumbnails.count() == count)
|
||||
return thumbnails;
|
||||
}
|
||||
|
||||
return thumbnails;
|
||||
}
|
114
modules/gui/qt/util/covergenerator.hpp
Normal file
114
modules/gui/qt/util/covergenerator.hpp
Normal file
@ -0,0 +1,114 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (C) 2021 VLC authors and VideoLAN
|
||||
*
|
||||
* Authors: Benjamin Arnaud <bunjee@omega.gg>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* ( at your option ) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef COVERGENERATOR_HPP
|
||||
#define COVERGENERATOR_HPP
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
// MediaLibrary includes
|
||||
#include "medialibrary/mlqmltypes.hpp"
|
||||
|
||||
// Util includes
|
||||
#include "util/asynctask.hpp"
|
||||
|
||||
// Qt includes
|
||||
#include <QPainter>
|
||||
|
||||
// Forward declarations
|
||||
class vlc_medialibrary_t;
|
||||
class MLItemId;
|
||||
|
||||
class CoverGenerator : public AsyncTask<QString>
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_ENUMS(Split)
|
||||
|
||||
public: // Enums
|
||||
enum Split
|
||||
{
|
||||
Divide,
|
||||
Duplicate
|
||||
};
|
||||
|
||||
public:
|
||||
CoverGenerator(vlc_medialibrary_t * ml, const MLItemId & itemId, int index = -1);
|
||||
|
||||
public: // Interface
|
||||
Q_INVOKABLE MLItemId getId();
|
||||
|
||||
Q_INVOKABLE int getIndex();
|
||||
|
||||
Q_INVOKABLE void setSize(const QSize & size);
|
||||
|
||||
Q_INVOKABLE void setCountX(int x);
|
||||
Q_INVOKABLE void setCountY(int y);
|
||||
|
||||
// NOTE: Do we want to divide or duplicate thumbnails to reach the proper count ?
|
||||
Q_INVOKABLE void setSplit(Split split);
|
||||
|
||||
// NOTE: Applies SmoothTransformation to thumbnails. Disabled by default.
|
||||
Q_INVOKABLE void setSmooth(bool enabled);
|
||||
|
||||
// NOTE: You need to specify a radius to enable blur, 8 looks good.
|
||||
Q_INVOKABLE void setBlur(int radius);
|
||||
|
||||
Q_INVOKABLE void setDefaultThumbnail(const QString & fileName);
|
||||
|
||||
public: // AsyncTask implementation
|
||||
QString execute() override;
|
||||
|
||||
private: // Functions
|
||||
void draw(QPainter & painter, const QStringList & fileNames);
|
||||
|
||||
void drawImage(QPainter & painter, const QString & fileName, const QRect & rect);
|
||||
|
||||
void blur(QImage * image);
|
||||
|
||||
QString getStringType(vlc_ml_parent_type type) const;
|
||||
|
||||
QStringList getMedias(int count, int64_t id, vlc_ml_parent_type type) const;
|
||||
QStringList getGenre (int count, int64_t id) const;
|
||||
|
||||
private:
|
||||
vlc_medialibrary_t * m_ml;
|
||||
|
||||
MLItemId m_id;
|
||||
|
||||
int m_index;
|
||||
|
||||
QSize m_size;
|
||||
|
||||
int m_countX;
|
||||
int m_countY;
|
||||
|
||||
Split m_split;
|
||||
|
||||
bool m_smooth;
|
||||
|
||||
int m_blur;
|
||||
|
||||
QString m_default;
|
||||
};
|
||||
|
||||
#endif // COVERGENERATOR_HPP
|
@ -791,6 +791,8 @@ modules/gui/qt/widgets/native/animators.cpp
|
||||
modules/gui/qt/widgets/native/animators.hpp
|
||||
modules/gui/qt/widgets/native/customwidgets.cpp
|
||||
modules/gui/qt/widgets/native/customwidgets.hpp
|
||||
modules/gui/qt/util/covergenerator.cpp
|
||||
modules/gui/qt/util/covergenerator.hpp
|
||||
modules/gui/qt/util/imagehelper.cpp
|
||||
modules/gui/qt/util/imagehelper.hpp
|
||||
modules/gui/qt/util/qt_dirs.cpp
|
||||
|
Loading…
Reference in New Issue
Block a user