Round images in the image provider

This commit is contained in:
Nicolas Werner 2021-08-14 17:17:50 +02:00
parent 24366b7520
commit 42d2b10d5d
No known key found for this signature in database
GPG Key ID: C8D75E610773F2D9
6 changed files with 87 additions and 35 deletions

View File

@ -3,7 +3,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
import "./ui" import "./ui"
import QtGraphicalEffects 1.0
import QtQuick 2.6 import QtQuick 2.6
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
import im.nheko 1.0 import im.nheko 1.0
@ -50,8 +49,7 @@ Rectangle {
smooth: true smooth: true
sourceSize.width: avatar.width sourceSize.width: avatar.width
sourceSize.height: avatar.height sourceSize.height: avatar.height
layer.enabled: true source: avatar.url ? (avatar.url + "?radius=" + radius + ((avatar.crop) ? "" : "&scale")) : ""
source: avatar.url + ((avatar.crop || !avatar.url) ? "" : "?scale")
MouseArea { MouseArea {
id: mouseArea id: mouseArea
@ -65,18 +63,6 @@ Rectangle {
} }
layer.effect: OpacityMask {
cached: true
maskSource: Rectangle {
anchors.fill: parent
width: avatar.width
height: avatar.height
radius: Settings.avatarCircles ? height / 2 : 3
}
}
} }
Rectangle { Rectangle {

View File

@ -6,7 +6,6 @@ import "./delegates"
import "./emoji" import "./emoji"
import "./ui" import "./ui"
import Qt.labs.platform 1.1 as Platform import Qt.labs.platform 1.1 as Platform
import QtGraphicalEffects 1.0
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.2 import QtQuick.Layouts 1.2

View File

@ -8,7 +8,6 @@ import "./dialogs"
import "./emoji" import "./emoji"
import "./voip" import "./voip"
import Qt.labs.platform 1.1 as Platform import Qt.labs.platform 1.1 as Platform
import QtGraphicalEffects 1.0
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3

View File

@ -9,7 +9,6 @@ import "./emoji"
import "./ui" import "./ui"
import "./voip" import "./voip"
import Qt.labs.platform 1.1 as Platform import Qt.labs.platform 1.1 as Platform
import QtGraphicalEffects 1.0
import QtQuick 2.9 import QtQuick 2.9
import QtQuick.Controls 2.5 import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3

View File

@ -11,6 +11,8 @@
#include <QByteArray> #include <QByteArray>
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
#include <QPainter>
#include <QPainterPath>
#include <QStandardPaths> #include <QStandardPaths>
#include "Logging.h" #include "Logging.h"
@ -22,14 +24,26 @@ QHash<QString, mtx::crypto::EncryptedFile> infos;
QQuickImageResponse * QQuickImageResponse *
MxcImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize) MxcImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize)
{ {
auto id_ = id; auto id_ = id;
bool crop = true; bool crop = true;
if (id.endsWith("?scale")) { double radius = 0;
crop = false;
id_.remove("?scale"); auto queryStart = id.lastIndexOf('?');
if (queryStart != -1) {
id_ = id.left(queryStart);
auto query = id.midRef(queryStart + 1);
auto queryBits = query.split('&');
for (auto b : queryBits) {
if (b == "scale") {
crop = false;
} else if (b.startsWith("radius=")) {
radius = b.mid(7).toDouble();
}
}
} }
MxcImageResponse *response = new MxcImageResponse(id_, crop, requestedSize); MxcImageResponse *response = new MxcImageResponse(id_, crop, radius, requestedSize);
pool.start(response); pool.start(response);
return response; return response;
} }
@ -53,14 +67,35 @@ MxcImageResponse::run()
} }
emit finished(); emit finished();
}, },
m_crop); m_crop,
m_radius);
}
static QImage
clipRadius(QImage img, double radius)
{
QImage out(img.size(), QImage::Format_ARGB32_Premultiplied);
out.fill(Qt::transparent);
QPainter painter(&out);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
QPainterPath ppath;
ppath.addRoundedRect(img.rect(), radius, radius);
painter.setClipPath(ppath);
painter.drawImage(img.rect(), img);
return out;
} }
void void
MxcImageProvider::download(const QString &id, MxcImageProvider::download(const QString &id,
const QSize &requestedSize, const QSize &requestedSize,
std::function<void(QString, QSize, QImage, QString)> then, std::function<void(QString, QSize, QImage, QString)> then,
bool crop) bool crop,
double radius)
{ {
std::optional<mtx::crypto::EncryptedFile> encryptionInfo; std::optional<mtx::crypto::EncryptedFile> encryptionInfo;
auto temp = infos.find("mxc://" + id); auto temp = infos.find("mxc://" + id);
@ -69,12 +104,13 @@ MxcImageProvider::download(const QString &id,
if (requestedSize.isValid() && !encryptionInfo) { if (requestedSize.isValid() && !encryptionInfo) {
QString fileName = QString fileName =
QString("%1_%2x%3_%4") QString("%1_%2x%3_%4_radius%5")
.arg(QString::fromUtf8(id.toUtf8().toBase64(QByteArray::Base64UrlEncoding | .arg(QString::fromUtf8(id.toUtf8().toBase64(QByteArray::Base64UrlEncoding |
QByteArray::OmitTrailingEquals))) QByteArray::OmitTrailingEquals)))
.arg(requestedSize.width()) .arg(requestedSize.width())
.arg(requestedSize.height()) .arg(requestedSize.height())
.arg(crop ? "crop" : "scale"); .arg(crop ? "crop" : "scale")
.arg(radius);
QFileInfo fileInfo(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QFileInfo fileInfo(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) +
"/media_cache", "/media_cache",
fileName); fileName);
@ -86,6 +122,10 @@ MxcImageProvider::download(const QString &id,
image = image.scaled( image = image.scaled(
requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
if (radius != 0) {
image = clipRadius(std::move(image), radius);
}
if (!image.isNull()) { if (!image.isNull()) {
then(id, requestedSize, image, fileInfo.absoluteFilePath()); then(id, requestedSize, image, fileInfo.absoluteFilePath());
return; return;
@ -100,8 +140,8 @@ MxcImageProvider::download(const QString &id,
opts.method = crop ? "crop" : "scale"; opts.method = crop ? "crop" : "scale";
http::client()->get_thumbnail( http::client()->get_thumbnail(
opts, opts,
[fileInfo, requestedSize, then, id](const std::string &res, [fileInfo, requestedSize, radius, then, id](const std::string &res,
mtx::http::RequestErr err) { mtx::http::RequestErr err) {
if (err || res.empty()) { if (err || res.empty()) {
then(id, QSize(), {}, ""); then(id, QSize(), {}, "");
@ -113,6 +153,10 @@ MxcImageProvider::download(const QString &id,
if (!image.isNull()) { if (!image.isNull()) {
image = image.scaled( image = image.scaled(
requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
if (radius != 0) {
image = clipRadius(std::move(image), radius);
}
} }
image.setText("mxc url", "mxc://" + id); image.setText("mxc url", "mxc://" + id);
if (image.save(fileInfo.absoluteFilePath(), "png")) if (image.save(fileInfo.absoluteFilePath(), "png"))
@ -126,8 +170,12 @@ MxcImageProvider::download(const QString &id,
}); });
} else { } else {
try { try {
QString fileName = QString::fromUtf8(id.toUtf8().toBase64( QString fileName =
QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)); QString("%1_radius%2")
.arg(QString::fromUtf8(id.toUtf8().toBase64(
QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)))
.arg(radius);
QFileInfo fileInfo( QFileInfo fileInfo(
QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QStandardPaths::writableLocation(QStandardPaths::CacheLocation) +
"/media_cache", "/media_cache",
@ -148,6 +196,11 @@ MxcImageProvider::download(const QString &id,
QImage image = utils::readImage(data); QImage image = utils::readImage(data);
image.setText("mxc url", "mxc://" + id); image.setText("mxc url", "mxc://" + id);
if (!image.isNull()) { if (!image.isNull()) {
if (radius != 0) {
image =
clipRadius(std::move(image), radius);
}
then(id, then(id,
requestedSize, requestedSize,
image, image,
@ -158,6 +211,11 @@ MxcImageProvider::download(const QString &id,
QImage image = QImage image =
utils::readImageFromFile(fileInfo.absoluteFilePath()); utils::readImageFromFile(fileInfo.absoluteFilePath());
if (!image.isNull()) { if (!image.isNull()) {
if (radius != 0) {
image =
clipRadius(std::move(image), radius);
}
then(id, then(id,
requestedSize, requestedSize,
image, image,
@ -169,7 +227,7 @@ MxcImageProvider::download(const QString &id,
http::client()->download( http::client()->download(
"mxc://" + id.toStdString(), "mxc://" + id.toStdString(),
[fileInfo, requestedSize, then, id, encryptionInfo]( [fileInfo, requestedSize, then, id, radius, encryptionInfo](
const std::string &res, const std::string &res,
const std::string &, const std::string &,
const std::string &originalFilename, const std::string &originalFilename,
@ -195,6 +253,10 @@ MxcImageProvider::download(const QString &id,
auto data = auto data =
QByteArray(tempData.data(), (int)tempData.size()); QByteArray(tempData.data(), (int)tempData.size());
QImage image = utils::readImage(data); QImage image = utils::readImage(data);
if (radius != 0) {
image = clipRadius(std::move(image), radius);
}
image.setText("original filename", image.setText("original filename",
QString::fromStdString(originalFilename)); QString::fromStdString(originalFilename));
image.setText("mxc url", "mxc://" + id); image.setText("mxc url", "mxc://" + id);
@ -205,6 +267,10 @@ MxcImageProvider::download(const QString &id,
QImage image = QImage image =
utils::readImageFromFile(fileInfo.absoluteFilePath()); utils::readImageFromFile(fileInfo.absoluteFilePath());
if (radius != 0) {
image = clipRadius(std::move(image), radius);
}
image.setText("original filename", image.setText("original filename",
QString::fromStdString(originalFilename)); QString::fromStdString(originalFilename));
image.setText("mxc url", "mxc://" + id); image.setText("mxc url", "mxc://" + id);

View File

@ -19,10 +19,11 @@ class MxcImageResponse
, public QRunnable , public QRunnable
{ {
public: public:
MxcImageResponse(const QString &id, bool crop, const QSize &requestedSize) MxcImageResponse(const QString &id, bool crop, double radius, const QSize &requestedSize)
: m_id(id) : m_id(id)
, m_requestedSize(requestedSize) , m_requestedSize(requestedSize)
, m_crop(crop) , m_crop(crop)
, m_radius(radius)
{ {
setAutoDelete(false); setAutoDelete(false);
} }
@ -39,6 +40,7 @@ public:
QSize m_requestedSize; QSize m_requestedSize;
QImage m_image; QImage m_image;
bool m_crop; bool m_crop;
double m_radius;
}; };
class MxcImageProvider class MxcImageProvider
@ -54,7 +56,8 @@ public slots:
static void download(const QString &id, static void download(const QString &id,
const QSize &requestedSize, const QSize &requestedSize,
std::function<void(QString, QSize, QImage, QString)> then, std::function<void(QString, QSize, QImage, QString)> then,
bool crop = true); bool crop = true,
double radius = 0);
private: private:
QThreadPool pool; QThreadPool pool;