blob: 7a3b6d957f18a0714a4fb97ab4d1bf526b490534 [file] [log] [blame]
/***************************************************************************
* Copyright (C) 2015-2017 by Savoir-faire Linux *
* Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>*
* Author: Anthony LĂ©onard <anthony.leonard@savoirfairelinux.com> *
* Author: Olivier Soldano <olivier.soldano@savoirfairelinux.com> *
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> *
* *
* 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 3 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, see <http://www.gnu.org/licenses/>. *
**************************************************************************/
#include "pixbufmanipulator.h"
#include <QSize>
#include <QMetaType>
#include <QImage>
#include <QIODevice>
#include <QByteArray>
#include <QBuffer>
#include <QPainter>
#include <QCryptographicHash>
#include "person.h"
#include "call.h"
#include "contactmethod.h"
#include "profilemodel.h"
#include "profile.h"
#include "globalinstances.h"
// new LRC
#include <api/contactmodel.h>
#include <api/conversation.h>
#include <api/account.h>
#include <api/contact.h>
#include "utils.h"
#include "ringthemeutils.h"
#undef interface
static const QSize IMAGE_SIZE {128, 128};
QColor
getAvatarColor(const QString& canonicalUri) {
if (canonicalUri.isEmpty()) {
return RingTheme::defaultAvatarColor_;
}
auto h = QString(QCryptographicHash::hash(canonicalUri.toLocal8Bit(), QCryptographicHash::Md5).toHex());
if (h.isEmpty() || h.isNull()) {
return RingTheme::defaultAvatarColor_;
}
auto colorIndex = std::string("0123456789abcdef").find(h.at(0).toLatin1());
return RingTheme::avatarColors_[colorIndex];
}
// Generate a QImage representing a dummy user avatar, when user doesn't provide it.
// Current rendering is a flat colored circle with a centered letter.
// The color of the letter is computed from the circle color to be visible whaterver be the circle color.
static QImage
fallbackAvatar(const QSize size, const QString& canonicalUriStr, const QString& letterStr = QString())
{
// We start with a transparent avatar
QImage avatar(size, QImage::Format_ARGB32);
avatar.fill(Qt::transparent);
// We pick a color based on the passed character
QColor avColor = getAvatarColor(canonicalUriStr);
// We draw a circle with this color
QPainter painter(&avatar);
painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
painter.setPen(Qt::transparent);
painter.setBrush(avColor.lighter(110));
painter.drawEllipse(avatar.rect());
// If a letter was passed, then we paint a letter in the circle,
// otherwise we draw the default avatar icon
QString letterStrCleaned(letterStr);
letterStrCleaned.remove(QRegExp("[\\n\\t\\r]"));
if (!letterStr.isEmpty()) {
auto letter = letterStr.at(0).toUpper().toLatin1();
QFont font("Arial", avatar.height() / 2.66667, QFont::Medium);
painter.setFont(font);
painter.setPen(Qt::white);
painter.drawText(avatar.rect(), QString(letter), QTextOption(Qt::AlignCenter));
} else {
QRect overlayRect = avatar.rect();
qreal margin = (0.05 * overlayRect.width());
overlayRect.moveLeft(overlayRect.left() + margin * 0.5);
overlayRect.moveTop(overlayRect.top() + margin * 0.5);
overlayRect.setWidth(overlayRect.width() - margin);
overlayRect.setHeight(overlayRect.height() - margin);
painter.drawPixmap(overlayRect, QPixmap(":/images/default_avatar_overlay.svg"));
}
return avatar;
}
QImage
fallbackAvatar(const QSize size, const ContactMethod* cm)
{
if (cm == nullptr) {
return QImage();
}
QImage image;
auto letterStr = QString();
if (cm->uri().userinfo() != cm->bestName()) {
letterStr = cm->bestName();
}
image = fallbackAvatar( size,
cm->uri().full(),
letterStr);
return image;
}
QImage
fallbackAvatar(const QSize size, const std::string& alias, const std::string& uri)
{
return fallbackAvatar(size,
QString::fromStdString(uri),
QString::fromStdString(alias));
}
/*Namespace Interfaces collide with QBuffer*/
QByteArray QImageToByteArray(QImage image)
{
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
image.save(&buffer, "PNG");
return ba;
}
QImage
PixbufManipulator::scaleAndFrame(const QImage photo, const QSize& size)
{
return photo.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
QVariant
PixbufManipulator::callPhoto(Call* c, const QSize& size, bool displayPresence)
{
if (!c || c->type() == Call::Type::CONFERENCE){
return QVariant::fromValue(fallbackAvatar(size,
c->peerContactMethod()->uri().full(),
c->peerContactMethod()->bestName()));
}
return callPhoto(c->peerContactMethod(), size, displayPresence);
}
QVariant
PixbufManipulator::callPhoto(const ContactMethod* cm, const QSize& size, bool displayPresence)
{
if (cm && cm->contact()) {
return contactPhoto(cm->contact(), size, displayPresence);
} else {
return QVariant::fromValue(fallbackAvatar(size, cm));
}
}
QVariant
PixbufManipulator::contactPhoto(Person* p, const QSize& size, bool displayPresence)
{
Q_UNUSED(displayPresence);
QImage photo;
if (p->photo().isValid()) {
photo = p->photo().value<QImage>();
} else {
photo = fallbackAvatar(IMAGE_SIZE, p->phoneNumbers().at(0));
}
return QVariant::fromValue(scaleAndFrame(photo, size));
}
QVariant PixbufManipulator::personPhoto(const QByteArray& data, const QString& type)
{
QImage avatar;
const bool ret = avatar.loadFromData(QByteArray::fromBase64(data), type.toLatin1());
if (!ret) {
qDebug() << "vCard image loading failed";
return QVariant();
}
return QPixmap::fromImage(Utils::getCirclePhoto(avatar, avatar.size().width()));
}
QVariant
PixbufManipulator::numberCategoryIcon(const QVariant& p, const QSize& size, bool displayPresence, bool isPresent)
{
Q_UNUSED(p)
Q_UNUSED(size)
Q_UNUSED(displayPresence)
Q_UNUSED(isPresent)
return QVariant();
}
QVariant
PixbufManipulator::securityIssueIcon(const QModelIndex& index)
{
Q_UNUSED(index)
return QVariant();
}
QByteArray
PixbufManipulator::toByteArray(const QVariant& pxm)
{
auto image = pxm.value<QImage>();
QByteArray ba = QImageToByteArray(image);
return ba;
}
QVariant
PixbufManipulator::collectionIcon(const CollectionInterface* colItf, PixmapManipulatorI::CollectionIconHint hint) const
{
Q_UNUSED(colItf)
Q_UNUSED(hint)
return QVariant();
}
QVariant
PixbufManipulator::securityLevelIcon(const SecurityEvaluationModel::SecurityLevel level) const
{
Q_UNUSED(level)
return QVariant();
}
QVariant
PixbufManipulator::historySortingCategoryIcon(const CategorizedHistoryModel::SortedProxy::Categories cat) const
{
Q_UNUSED(cat)
return QVariant();
}
QVariant
PixbufManipulator::contactSortingCategoryIcon(const CategorizedContactModel::SortedProxy::Categories cat) const
{
Q_UNUSED(cat)
return QVariant();
}
QVariant
PixbufManipulator::userActionIcon(const UserActionElement& state) const
{
Q_UNUSED(state)
return QVariant();
}
QVariant PixbufManipulator::decorationRole(const QModelIndex& index)
{
Q_UNUSED(index)
return QVariant();
}
QVariant PixbufManipulator::decorationRole(const Call* c)
{
QImage photo;
if (c && c->peerContactMethod()
&& c->peerContactMethod()->contact()
&& c->peerContactMethod()->contact()->photo().isValid()) {
photo = c->peerContactMethod()->contact()->photo().value<QImage>();
} else {
fallbackAvatar(IMAGE_SIZE, c->peerContactMethod());
}
return QVariant::fromValue(scaleAndFrame(photo, IMAGE_SIZE));
}
QVariant PixbufManipulator::decorationRole(const ContactMethod* cm)
{
QImage photo;
if (cm && cm->contact()
&& cm->contact()->photo().isValid()) {
photo = cm->contact()->photo().value<QImage>();
} else {
photo = fallbackAvatar(IMAGE_SIZE, cm);
}
return QVariant::fromValue(scaleAndFrame(photo, IMAGE_SIZE));
}
QVariant PixbufManipulator::decorationRole(const Person* p)
{
QImage photo;
if (p && p->photo().isValid()) {
photo = p->photo().value<QImage>();
} else {
photo = fallbackAvatar(IMAGE_SIZE, p->phoneNumbers().at(0));
}
return QVariant::fromValue(scaleAndFrame(photo, IMAGE_SIZE));
}
QVariant PixbufManipulator::decorationRole(const Account* acc)
{
Q_UNUSED(acc)
return Utils::getCirclePhoto(ProfileModel::instance().
selectedProfile()->person()->photo().value<QImage>(),
IMAGE_SIZE.width());
}
QVariant
PixbufManipulator::decorationRole(const lrc::api::conversation::Info & conversationInfo,
const lrc::api::account::Info & accountInfo)
{
QImage photo;
auto contacts = conversationInfo.participants;
if (!contacts.empty()) {
try {
// Get first contact photo
auto contactUri = contacts.front();
auto contactInfo = accountInfo.contactModel->getContact(contactUri);
auto contactPhoto = contactInfo.profileInfo.avatar;
auto bestName = Utils::bestNameForContact(contactInfo);
auto bestId = Utils::bestIdForContact(contactInfo);
if (accountInfo.profileInfo.type == lrc::api::profile::Type::SIP &&
contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY) {
photo = fallbackAvatar(IMAGE_SIZE, QString(), QString());
}
else if (accountInfo.profileInfo.type == lrc::api::profile::Type::SIP) {
photo = fallbackAvatar(IMAGE_SIZE,
QString::fromStdString("sip:" + bestId),
QString());
}
else if (contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY && contactInfo.profileInfo.uri.empty()) {
photo = fallbackAvatar(IMAGE_SIZE, QString(), QString());
}
else if (!contactPhoto.empty()) {
QByteArray byteArray(contactPhoto.c_str(), contactPhoto.length());
photo = personPhoto(byteArray, nullptr).value<QImage>();
}
else {
auto avatarName = contactInfo.profileInfo.uri == bestName ?
QString() :
QString::fromStdString(bestName);
photo = fallbackAvatar(IMAGE_SIZE,
QString::fromStdString("ring:" + bestId),
avatarName);
}
}
catch (...) {}
}
return QVariant::fromValue(scaleAndFrame(photo, IMAGE_SIZE));
}
QVariant
PixbufManipulator::accountPhoto(const lrc::api::account::Info& accountInfo)
{
QImage photo;
if (!accountInfo.profileInfo.avatar.empty()) {
QByteArray ba = QByteArray::fromStdString(accountInfo.profileInfo.avatar);
photo = GlobalInstances::pixmapManipulator().personPhoto(ba, nullptr).value<QImage>();
}
else {
auto bestId = Utils::bestIdForAccount(accountInfo);
auto bestName = Utils::bestNameForAccount(accountInfo);
photo = fallbackAvatar( IMAGE_SIZE,
QString::fromStdString("ring:" + bestId),
QString::fromStdString(bestName));
}
return QVariant::fromValue(scaleAndFrame(photo, IMAGE_SIZE));
}