Modified distance function to keep aspect ratio as close as possible

Summary:
This new version of the distance function simplifies the
findPreferredImageInPackage method by using the aspect ratio in the
calculation. Now, it won't only search for a wallpaper with the same
aspect ratio, but will also choose one with the closest possible
aspect ratio when a perfect match is not found.

Also, I separated the method that chooses the preferred image
to not use KPackage in order to use it from a test application
that tests the distance algorithm with all possible resolutions.

Test application that tests the Image::findPreferredImage method

The test initializes a list of available wallpaper image sizes,
then a list of screen resolutions, and then executes
Image::findPreferredImage to find out which is the preferred image
in each case.

Test Plan:
I tested with different screen resolutions on a virtual machine with
openSUSE Leap 42.2 RC1 and also included a commit that adds a test
application (not built by default) that tests all possible resolutions.

Reviewers: #plasma, mart

Reviewed By: mart

Subscribers: mart, plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D3188
parent 47fa1f0c
......@@ -6,8 +6,8 @@ set(image_SRCS
backgroundlistmodel.cpp
)
add_library(plasma_wallpaper_imageplugin SHARED ${image_SRCS})
target_link_libraries(plasma_wallpaper_imageplugin
Qt5::Core
Qt5::Quick
......@@ -22,6 +22,8 @@ target_link_libraries(plasma_wallpaper_imageplugin
KF5::GuiAddons
)
add_subdirectory(autotests)
install(TARGETS plasma_wallpaper_imageplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/plasma/wallpapers/image)
install(FILES qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/plasma/wallpapers/image)
......
set(testfindpreferredimage_SRCS
testfindpreferredimage.cpp
../image.cpp
../backgroundlistmodel.cpp
)
add_executable(testfindpreferredimage EXCLUDE_FROM_ALL ${testfindpreferredimage_SRCS})
target_link_libraries(testfindpreferredimage
plasma_wallpaper_imageplugin
Qt5::Test)
/***************************************************************************
* Copyright 2016 Antonio Larrosa <larrosa@kde.org> *
* *
* 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 "image.h"
#include <QtTest/QtTest>
#include <QDebug>
extern QSize resSize(const QString &str);
QString formatResolution(const QString &str)
{
QSize size = resSize(str);
float aspectRatio = (size.height() > 0 ) ? size.width() / (float)size.height() : 0;
return QString("%1 (%2)").arg(str, 9).arg(aspectRatio, 7);
}
class TestResolutions: public QObject
{
Q_OBJECT
private slots:
void testResolutions_data();
void testResolutions();
protected:
Image m_image;
QStringList m_images;
};
void TestResolutions::testResolutions_data()
{
// The list of available wallpaper image sizes
m_images << QStringLiteral("1280x1024")
<< QStringLiteral("1350x1080")
<< QStringLiteral("1440x1080")
<< QStringLiteral("1600x1200")
<< QStringLiteral("1920x1080")
<< QStringLiteral("1920x1200")
<< QStringLiteral("3840x2400");
qDebug() << "Available images:";
foreach( auto image, m_images) {
qDebug() << formatResolution(image);
}
// The list of possible screen resolutions to test and the appropiate images that should be chosen
QTest::addColumn<QString>("resolution");
QTest::addColumn<QString>("expected");
QTest::newRow("1280x1024") << QStringLiteral("1280x1024") << QStringLiteral("1280x1024");
QTest::newRow("1350x1080") << QStringLiteral("1350x1080") << QStringLiteral("1350x1080");
QTest::newRow("1440x1080") << QStringLiteral("1440x1080") << QStringLiteral("1440x1080");
QTest::newRow("1600x1200") << QStringLiteral("1600x1200") << QStringLiteral("1600x1200");
QTest::newRow("1920x1080") << QStringLiteral("1920x1080") << QStringLiteral("1920x1080");
QTest::newRow("1920x1200") << QStringLiteral("1920x1200") << QStringLiteral("1920x1200");
QTest::newRow("3840x2400") << QStringLiteral("3840x2400") << QStringLiteral("3840x2400");
QTest::newRow("4096x2160") << QStringLiteral("4096x2160") << QStringLiteral("1920x1080");
QTest::newRow("3840x2160") << QStringLiteral("3840x2160") << QStringLiteral("1920x1080");
QTest::newRow("3200x1800") << QStringLiteral("3200x1800") << QStringLiteral("1920x1080");
QTest::newRow("2048x1080") << QStringLiteral("2048x1080") << QStringLiteral("1920x1080");
QTest::newRow("1680x1050") << QStringLiteral("1680x1050") << QStringLiteral("1920x1200");
QTest::newRow("1400x1050") << QStringLiteral("1400x1050") << QStringLiteral("1440x1080");
QTest::newRow("1440x900") << QStringLiteral("1440x900") << QStringLiteral("1920x1200");
QTest::newRow("1280x960") << QStringLiteral("1280x960") << QStringLiteral("1440x1080");
QTest::newRow("1280x854") << QStringLiteral("1280x854") << QStringLiteral("1920x1200");
QTest::newRow("1280x800") << QStringLiteral("1280x800") << QStringLiteral("1920x1200");
QTest::newRow("1280x720") << QStringLiteral("1280x720") << QStringLiteral("1920x1080");
QTest::newRow("1152x768") << QStringLiteral("1152x768") << QStringLiteral("1920x1200");
QTest::newRow("1024x768") << QStringLiteral("1024x768") << QStringLiteral("1440x1080");
QTest::newRow("800x600") << QStringLiteral("800x600") << QStringLiteral("1440x1080");
QTest::newRow("848x480") << QStringLiteral("848x480") << QStringLiteral("1920x1080");
QTest::newRow("720x480") << QStringLiteral("720x480") << QStringLiteral("1920x1200");
QTest::newRow("640x480") << QStringLiteral("640x480") << QStringLiteral("1440x1080");
QTest::newRow("1366x768") << QStringLiteral("1366x768") << QStringLiteral("1920x1080");
QTest::newRow("1600x814") << QStringLiteral("1600x814") << QStringLiteral("1920x1080");
}
void TestResolutions::testResolutions()
{
QFETCH(QString, resolution);
QFETCH(QString, expected);
m_image.setTargetSize( resSize( resolution ) );
QString preferred = m_image.findPreferedImage(m_images);
qDebug() << "For a screen size of " << formatResolution(resolution)
<< " the " << formatResolution(preferred) << " wallpaper was preferred";
QCOMPARE(preferred, expected);
}
QTEST_MAIN(TestResolutions)
#include "testfindpreferredimage.moc"
......@@ -156,13 +156,14 @@ void Image::setRenderingMode(RenderingMode mode)
float distance(const QSize& size, const QSize& desired)
{
// compute difference of areas
float delta = size.width() * size.height() -
desired.width() * desired.height();
// scale down to about 1.0
delta /= ((desired.width() * desired.height())+(size.width() * size.height()))/2;
float desiredAspectRatio = ( desired.height() > 0 ) ? desired.width() / (float)desired.height() : 0;
float candidateAspectRatio = ( size.height() > 0 ) ? size.width() / (float)size.height() : FLT_MAX;
float delta = size.width() - desired.width();
delta = (delta >= 0.0 ? delta : -delta*2 ); // Penalize for scaling up
return qAbs(candidateAspectRatio - desiredAspectRatio)*25000 + delta;
// Difference of areas, slight preference to scale down
return delta >= 0.0 ? delta : -delta + 2.0;
}
QSize resSize(const QString &str)
......@@ -176,56 +177,48 @@ QSize resSize(const QString &str)
return QSize();
}
void Image::findPreferedImageInPackage(KPackage::Package &package)
QString Image::findPreferedImage(const QStringList &images)
{
if (!package.isValid() || !package.filePath("preferred").isEmpty()) {
return;
}
QStringList images = package.entryList("images");
if (images.empty()) {
return;
return QString();
}
//qDebug() << "wanted" << m_targetSize << "options" << images;
// choose the nearest resolution, always preferring images with the same aspect ratio
//float targetAspectRatio = (m_targetSize.height() > 0 ) ? m_targetSize.width() / (float)m_targetSize.height() : 0;
//qDebug() << "wanted" << m_targetSize << "options" << images << "aspect ratio" << targetAspectRatio;
float best = FLT_MAX;
float bestWithSameAspectRatio = FLT_MAX;
float targetAspectRatio = m_targetSize.width()/(float)m_targetSize.height();
QString bestImage;
QString bestImageWithSameAspectRatio;
foreach (const QString &entry, images) {
QSize candidate = resSize(QFileInfo(entry).baseName());
if (candidate == QSize()) {
continue;
}
float candidateAspectRatio = candidate.width()/(float)candidate.height();
//float candidateAspectRatio = (candidate.height() > 0 ) ? candidate.width() / (float)candidate.height() : FLT_MAX;
double dist = distance(candidate, m_targetSize);
float dist = distance(candidate, m_targetSize);
//qDebug() << "candidate" << candidate << "distance" << dist << "aspect ratio" << candidateAspectRatio;
if ( candidateAspectRatio == targetAspectRatio && (bestImageWithSameAspectRatio.isEmpty() || dist < bestWithSameAspectRatio) ) {
bestImageWithSameAspectRatio = entry;
bestWithSameAspectRatio = dist;
//qDebug() << "bestWithSameAspectRatio" << bestImageWithSameAspectRatio;
if (dist == 0) {
break;
}
} else if (bestImage.isEmpty() || dist < best) {
if (bestImage.isEmpty() || dist < best) {
bestImage = entry;
best = dist;
//qDebug() << "best" << bestImage;
}
}
if (!bestImageWithSameAspectRatio.isEmpty()) // Always prefer an image with the same aspect ratio as the target (if available)
bestImage=bestImageWithSameAspectRatio;
//qDebug() << "best image" << bestImage;
return bestImage;
}
void Image::findPreferedImageInPackage(KPackage::Package &package)
{
if (!package.isValid() || !package.filePath("preferred").isEmpty()) {
return;
}
QString preferred = findPreferedImage( package.entryList("images") );
package.removeDefinition("preferred");
package.addFileDefinition("preferred", "images/" + bestImage, i18n("Recommended wallpaper file"));
package.addFileDefinition("preferred", "images/" + preferred, i18n("Recommended wallpaper file"));
}
QSize Image::targetSize() const
......
......@@ -114,6 +114,7 @@ class Image : public QObject, public QQmlParserStatus
void setSlidePaths(const QStringList &slidePaths);
void findPreferedImageInPackage(KPackage::Package &package);
QString findPreferedImage(const QStringList &images);
void classBegin() override;
void componentComplete() override;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment