blob: 3ca4956a6a8b507a419cbfcf6e0d5838da322570 [file] [log] [blame]
Edric Milareta0ebd062016-01-13 12:18:23 -05001/***************************************************************************
Anthony Léonard2fde81d2017-04-17 10:06:55 -04002 * Copyright (C) 2015-2017 by Savoir-faire Linux *
Edric Milareta0ebd062016-01-13 12:18:23 -05003 * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>*
Olivier SOLDANO47aa97f2017-04-04 10:40:00 -04004 * Author: Anthony Léonard <anthony.leonard@savoirfairelinux.com> *
5 * Author: Olivier Soldano <olivier.soldano@savoirfairelinux.com> *
Andreas Traczyk072291f2018-08-16 16:48:58 -04006 * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> *
Edric Milareta0ebd062016-01-13 12:18:23 -05007 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 3 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
20 **************************************************************************/
21
22#include "pixbufmanipulator.h"
23
Edric Milaret25236d92016-03-28 09:40:58 -040024#include <QSize>
25#include <QMetaType>
26#include <QImage>
27#include <QIODevice>
28#include <QByteArray>
29#include <QBuffer>
Anthony Léonard3d920d82017-07-31 13:51:16 -040030#include <QPainter>
Andreas Traczyk072291f2018-08-16 16:48:58 -040031#include <QCryptographicHash>
Edric Milareta0ebd062016-01-13 12:18:23 -050032
Edric Milaret25236d92016-03-28 09:40:58 -040033#include "person.h"
34#include "call.h"
35#include "contactmethod.h"
36#include "profilemodel.h"
37#include "profile.h"
38
39#include "utils.h"
Anthony Léonard3d920d82017-07-31 13:51:16 -040040#include "ringthemeutils.h"
Edric Milaret25236d92016-03-28 09:40:58 -040041#undef interface
42
Andreas Traczyk072291f2018-08-16 16:48:58 -040043static const QSize IMAGE_SIZE {128, 128};
44
45QColor
46getAvatarColor(const QString& canonicalUri) {
47 if (canonicalUri.isEmpty()) {
48 return RingTheme::defaultAvatarColor_;
49 }
50 auto h = QString(QCryptographicHash::hash(canonicalUri.toLocal8Bit(), QCryptographicHash::Md5).toHex());
51 if (h.isEmpty() || h.isNull()) {
52 return RingTheme::defaultAvatarColor_;
53 }
54 uint8_t colorsLength = sizeof(RingTheme::avatarColors_) / sizeof(QColor);
55 bool ok;
56 auto colorIndex = QString(h.at(0)).toUInt(&ok, colorsLength);
57 return RingTheme::avatarColors_[colorIndex];
58}
Guillaume Roguez79cab802017-08-25 09:59:13 -040059
60//
61// Generate a QImage representing a dummy user avatar, when user doesn't provide it.
62// Current rendering is a flat colored circle with a centered letter.
63// The color of the letter is computed from the circle color to be visible whaterver be the circle color.
64//
65// \param color circle color
66// \param letter centerer letter
67//
68static QImage
Andreas Traczyk072291f2018-08-16 16:48:58 -040069fallbackAvatar(const QSize size, const QString& canonicalUriStr, const QString& letterStr = QString())
Guillaume Roguez79cab802017-08-25 09:59:13 -040070{
71 // We start with a transparent avatar
72 QImage avatar(size, QImage::Format_ARGB32);
73 avatar.fill(Qt::transparent);
74
75 // We pick a color based on the passed character
Andreas Traczyk072291f2018-08-16 16:48:58 -040076 QColor avColor = getAvatarColor(canonicalUriStr);
Guillaume Roguez79cab802017-08-25 09:59:13 -040077
78 // We draw a circle with this color
79 QPainter painter(&avatar);
Andreas Traczyk072291f2018-08-16 16:48:58 -040080 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
Guillaume Roguez79cab802017-08-25 09:59:13 -040081 painter.setPen(Qt::transparent);
Andreas Traczyk072291f2018-08-16 16:48:58 -040082 painter.setBrush(avColor.lighter(110));
Guillaume Roguez79cab802017-08-25 09:59:13 -040083 painter.drawEllipse(avatar.rect());
84
Andreas Traczyk072291f2018-08-16 16:48:58 -040085 // If a letter was passed, then we paint a letter in the circle,
86 // otherwise we draw the default avatar icon
87 if (!letterStr.isEmpty()) {
88 auto letter = letterStr.at(0).toUpper().toLatin1();
89 QFont font("Arial", avatar.height() / 2.66667, QFont::Medium);
90 painter.setFont(font);
91 painter.setPen(Qt::white);
92 painter.drawText(avatar.rect(), QString(letter), QTextOption(Qt::AlignCenter));
93 } else {
94 QRect overlayRect = avatar.rect();
95 qreal margin = (0.05 * overlayRect.width());
96 overlayRect.moveLeft(overlayRect.left() + margin * 0.5);
97 overlayRect.moveTop(overlayRect.top() + margin * 0.5);
98 overlayRect.setWidth(overlayRect.width() - margin);
99 overlayRect.setHeight(overlayRect.height() - margin);
100 painter.drawPixmap(overlayRect, QPixmap(":/images/default-avatar-overlay.svg"));
101 }
Guillaume Roguez79cab802017-08-25 09:59:13 -0400102
103 return avatar;
104}
105
Andreas Traczyk072291f2018-08-16 16:48:58 -0400106QImage
107fallbackAvatar
108(const QSize size, const ContactMethod* cm)
Guillaume Roguez79cab802017-08-25 09:59:13 -0400109{
Andreas Traczyk072291f2018-08-16 16:48:58 -0400110 if (cm == nullptr) {
111 return QImage();
112 }
113 QImage image;
114 auto letterStr = QString();
115 if (cm->uri().userinfo() != cm->bestName()) {
116 letterStr = cm->bestName();
117 }
118 image = fallbackAvatar( size,
119 cm->uri().full(),
120 letterStr);
121 return image;
Guillaume Roguez79cab802017-08-25 09:59:13 -0400122}
123
Edric Milaret25236d92016-03-28 09:40:58 -0400124/*Namespace Interfaces collide with QBuffer*/
125QByteArray QImageToByteArray(QImage image)
126{
127 QByteArray ba;
128 QBuffer buffer(&ba);
129 buffer.open(QIODevice::WriteOnly);
130 image.save(&buffer, "PNG");
131 return ba;
132}
Edric Milareta0ebd062016-01-13 12:18:23 -0500133
Edric Milareta0ebd062016-01-13 12:18:23 -0500134QImage
135PixbufManipulator::scaleAndFrame(const QImage photo, const QSize& size)
136{
137 return photo.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
138}
139
140QVariant
141PixbufManipulator::callPhoto(Call* c, const QSize& size, bool displayPresence)
142{
Olivier SOLDANOad0fabb2016-11-25 09:08:01 -0500143 if (!c || c->type() == Call::Type::CONFERENCE){
Anthony Léonard3d920d82017-07-31 13:51:16 -0400144 return QVariant::fromValue(fallbackAvatar(size,
Andreas Traczyk072291f2018-08-16 16:48:58 -0400145 c->peerContactMethod()->uri().full(),
Guillaume Roguez79cab802017-08-25 09:59:13 -0400146 c->peerContactMethod()->bestName()));
Olivier SOLDANOad0fabb2016-11-25 09:08:01 -0500147 }
Edric Milareta0ebd062016-01-13 12:18:23 -0500148 return callPhoto(c->peerContactMethod(), size, displayPresence);
149}
150
151QVariant
Andreas Traczyk072291f2018-08-16 16:48:58 -0400152PixbufManipulator::callPhoto(const ContactMethod* cm, const QSize& size, bool displayPresence)
Edric Milareta0ebd062016-01-13 12:18:23 -0500153{
Andreas Traczyk072291f2018-08-16 16:48:58 -0400154 if (cm && cm->contact()) {
155 return contactPhoto(cm->contact(), size, displayPresence);
Edric Milareta0ebd062016-01-13 12:18:23 -0500156 } else {
Andreas Traczyk072291f2018-08-16 16:48:58 -0400157 return QVariant::fromValue(fallbackAvatar(size, cm));
Edric Milareta0ebd062016-01-13 12:18:23 -0500158 }
159}
160
161QVariant
Andreas Traczyk072291f2018-08-16 16:48:58 -0400162PixbufManipulator::contactPhoto(Person* p, const QSize& size, bool displayPresence)
Edric Milareta0ebd062016-01-13 12:18:23 -0500163{
164 Q_UNUSED(displayPresence);
Edric Milareta0ebd062016-01-13 12:18:23 -0500165 QImage photo;
Andreas Traczyk072291f2018-08-16 16:48:58 -0400166 if (p->photo().isValid()) {
167 photo = p->photo().value<QImage>();
Olivier SOLDANOad0fabb2016-11-25 09:08:01 -0500168 } else {
Andreas Traczyk072291f2018-08-16 16:48:58 -0400169 photo = fallbackAvatar(IMAGE_SIZE, p->phoneNumbers().at(0));
Olivier SOLDANOad0fabb2016-11-25 09:08:01 -0500170 }
Edric Milareta0ebd062016-01-13 12:18:23 -0500171 return QVariant::fromValue(scaleAndFrame(photo, size));
172}
173
174QVariant PixbufManipulator::personPhoto(const QByteArray& data, const QString& type)
175{
Edric Milaret25236d92016-03-28 09:40:58 -0400176 QImage avatar;
177 QByteArray ba = type.toLatin1();
178 const char* c_str2 = ba.data();
179 if (avatar.loadFromData(data.fromBase64(data), c_str2))
180 return Utils::getCirclePhoto(avatar, avatar.size().width());
Andreas Traczyk072291f2018-08-16 16:48:58 -0400181 return fallbackAvatar(IMAGE_SIZE, QString());
Edric Milareta0ebd062016-01-13 12:18:23 -0500182}
183
184QVariant
185PixbufManipulator::numberCategoryIcon(const QVariant& p, const QSize& size, bool displayPresence, bool isPresent)
186{
187 Q_UNUSED(p)
188 Q_UNUSED(size)
189 Q_UNUSED(displayPresence)
190 Q_UNUSED(isPresent)
191 return QVariant();
192}
193
194QVariant
195PixbufManipulator::securityIssueIcon(const QModelIndex& index)
196{
197 Q_UNUSED(index)
198 return QVariant();
199}
200
201QByteArray
202PixbufManipulator::toByteArray(const QVariant& pxm)
203{
Edric Milaret25236d92016-03-28 09:40:58 -0400204 auto image = pxm.value<QImage>();
205 QByteArray ba = QImageToByteArray(image);
206 return ba;
Edric Milareta0ebd062016-01-13 12:18:23 -0500207}
208
209QVariant
Olivier SOLDANO41e61ab2017-08-04 16:47:16 -0400210PixbufManipulator::collectionIcon(const CollectionInterface* colItf, PixmapManipulatorI::CollectionIconHint hint) const
Edric Milareta0ebd062016-01-13 12:18:23 -0500211{
Olivier SOLDANO41e61ab2017-08-04 16:47:16 -0400212 Q_UNUSED(colItf)
Edric Milareta0ebd062016-01-13 12:18:23 -0500213 Q_UNUSED(hint)
214 return QVariant();
215}
216QVariant
217PixbufManipulator::securityLevelIcon(const SecurityEvaluationModel::SecurityLevel level) const
218{
219 Q_UNUSED(level)
220 return QVariant();
221}
222QVariant
223PixbufManipulator::historySortingCategoryIcon(const CategorizedHistoryModel::SortedProxy::Categories cat) const
224{
225 Q_UNUSED(cat)
226 return QVariant();
227}
228QVariant
229PixbufManipulator::contactSortingCategoryIcon(const CategorizedContactModel::SortedProxy::Categories cat) const
230{
231 Q_UNUSED(cat)
232 return QVariant();
233}
234QVariant
235PixbufManipulator::userActionIcon(const UserActionElement& state) const
236{
237 Q_UNUSED(state)
238 return QVariant();
239}
240
241QVariant PixbufManipulator::decorationRole(const QModelIndex& index)
242{
243 Q_UNUSED(index)
244 return QVariant();
245}
246
247QVariant PixbufManipulator::decorationRole(const Call* c)
248{
Edric Milareta13b48c2016-01-18 14:42:35 -0500249 QImage photo;
Andreas Traczyk072291f2018-08-16 16:48:58 -0400250 if (c && c->peerContactMethod()
Edric Milareta0ebd062016-01-13 12:18:23 -0500251 && c->peerContactMethod()->contact()
252 && c->peerContactMethod()->contact()->photo().isValid()) {
Edric Milareta13b48c2016-01-18 14:42:35 -0500253 photo = c->peerContactMethod()->contact()->photo().value<QImage>();
Andreas Traczyk072291f2018-08-16 16:48:58 -0400254 } else {
255 fallbackAvatar(IMAGE_SIZE, c->peerContactMethod());
Edric Milareta0ebd062016-01-13 12:18:23 -0500256 }
Guillaume Roguez79cab802017-08-25 09:59:13 -0400257 return QVariant::fromValue(scaleAndFrame(photo, IMAGE_SIZE));
Edric Milareta0ebd062016-01-13 12:18:23 -0500258}
259
260QVariant PixbufManipulator::decorationRole(const ContactMethod* cm)
261{
Edric Milareta13b48c2016-01-18 14:42:35 -0500262 QImage photo;
Andreas Traczyk072291f2018-08-16 16:48:58 -0400263 if (cm && cm->contact()
264 && cm->contact()->photo().isValid()) {
Edric Milareta13b48c2016-01-18 14:42:35 -0500265 photo = cm->contact()->photo().value<QImage>();
Olivier SOLDANO6c46d352017-08-24 10:54:55 -0400266 } else {
Andreas Traczyk072291f2018-08-16 16:48:58 -0400267 photo = fallbackAvatar(IMAGE_SIZE, cm);
Olivier SOLDANO6c46d352017-08-24 10:54:55 -0400268 }
Guillaume Roguez79cab802017-08-25 09:59:13 -0400269 return QVariant::fromValue(scaleAndFrame(photo, IMAGE_SIZE));
Edric Milareta0ebd062016-01-13 12:18:23 -0500270}
271
272QVariant PixbufManipulator::decorationRole(const Person* p)
273{
Edric Milareta13b48c2016-01-18 14:42:35 -0500274 QImage photo;
Andreas Traczyk072291f2018-08-16 16:48:58 -0400275 if (p && p->photo().isValid()) {
Edric Milareta13b48c2016-01-18 14:42:35 -0500276 photo = p->photo().value<QImage>();
Andreas Traczyk072291f2018-08-16 16:48:58 -0400277 } else {
278 photo = fallbackAvatar(IMAGE_SIZE, p->phoneNumbers().at(0));
279 }
Guillaume Roguez79cab802017-08-25 09:59:13 -0400280 return QVariant::fromValue(scaleAndFrame(photo, IMAGE_SIZE));
Edric Milareta0ebd062016-01-13 12:18:23 -0500281}
282
Edric Milareta7cf8652016-04-19 15:37:51 -0400283QVariant PixbufManipulator::decorationRole(const Account* acc)
284{
285 Q_UNUSED(acc)
Edric Milaret25236d92016-03-28 09:40:58 -0400286 return Utils::getCirclePhoto(ProfileModel::instance().
Guillaume Roguez79cab802017-08-25 09:59:13 -0400287 selectedProfile()->person()->photo().value<QImage>(),
288 IMAGE_SIZE.width());
Anthony Léonard3d920d82017-07-31 13:51:16 -0400289}