blob: 1d9553b0487af48d30fa6fcc079ee96a0edcbcc4 [file] [log] [blame]
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001/*
Guillaume Roguez2a6150d2017-07-19 18:24:47 -04002 * Copyright (C) 2015-2017 Savoir-faire Linux Inc.
Stepan Salenikovich6f687072015-03-26 10:43:37 -04003 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Stepan Salenikovich6f687072015-03-26 10:43:37 -040018 */
19
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040020#include "pixbufmanipulator.h"
Stepan Salenikovich6f687072015-03-26 10:43:37 -040021
22#include "../utils/drawing.h"
23#include <QtCore/QSize>
24#include <QtCore/QMetaType>
25#include <person.h>
26#include <memory>
27#include <call.h>
28#include <contactmethod.h>
29
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040030namespace Interfaces {
31
32PixbufManipulator::PixbufManipulator()
Sébastien Blin9684dd32017-07-24 11:15:19 -040033 : conferenceAvatar_{ring_draw_conference_avatar(FALLBACK_AVATAR_SIZE), g_object_unref}
Stepan Salenikovich6f687072015-03-26 10:43:37 -040034{
35}
36
37std::shared_ptr<GdkPixbuf>
Sébastien Blin9684dd32017-07-24 11:15:19 -040038PixbufManipulator::generateAvatar(const ContactMethod* cm) const
39{
40 auto cm_number = QString("0");
41 auto letter = QChar('R'); // R for ring
42 if (cm) {
43 auto hashName = cm->uri().userinfo();
44 if (hashName.size() > 0) {
45 cm_number = hashName.at(0);
46 }
47 letter = cm->bestName().toUpper().at(0);
48 }
49
50 bool ok;
51 auto color = cm_number.toUInt(&ok, 16);
52 if (!ok) color = 0;
53
54 return std::shared_ptr<GdkPixbuf> {
55 ring_draw_fallback_avatar(
56 FALLBACK_AVATAR_SIZE,
57 letter.toLatin1(),
58 color
59 ),
60 g_object_unref
61 };
62}
63
64std::shared_ptr<GdkPixbuf>
aviauc372e812016-12-01 16:13:16 -050065PixbufManipulator::scaleAndFrame(const GdkPixbuf *photo, const QSize& size, bool display_presence, bool is_present)
Stepan Salenikovich6f687072015-03-26 10:43:37 -040066{
67 /**
68 * for now, respect the height requested
69 * the framing process will add another 10px, so account for that
70 * when scaling the photos
71 */
72
73 int height = size.height();
74 if (size.height() != size.width())
75 g_warning("requested contact photo width != height; only respecting the height as the largest dimension");
76 int photo_h = height - 10;
77 int photo_w = photo_h;
78
79 /* scale photo, make sure to respect the request height as the largest dimension*/
80 int w = gdk_pixbuf_get_width(photo);
81 int h = gdk_pixbuf_get_height(photo);
82 if (h > w)
83 photo_w = w * ((double)photo_h / h);
84 if (w > h)
85 photo_h = h * ((double)photo_w / w);
86
87 std::unique_ptr<GdkPixbuf, decltype(g_object_unref)&> scaled_photo{
88 gdk_pixbuf_scale_simple(photo, photo_w, photo_h, GDK_INTERP_BILINEAR),
89 g_object_unref};
90
91 /* frame photo */
aviauc372e812016-12-01 16:13:16 -050092 std::shared_ptr<GdkPixbuf> result {
93 ring_frame_avatar(scaled_photo.get()),
94 g_object_unref
95 };
96
97 /* draw presence */
98 if (display_presence)
99 result.reset(ring_draw_presence(result.get(), is_present), g_object_unref);
100
101 return result;
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400102}
103
104QVariant
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -0400105PixbufManipulator::callPhoto(Call* c, const QSize& size, bool displayPresence)
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400106{
aviauc372e812016-12-01 16:13:16 -0500107 if (c->type() == Call::Type::CONFERENCE) {
108 /* conferences are always "online" */
109 return QVariant::fromValue(scaleAndFrame(conferenceAvatar_.get(), size, displayPresence, TRUE));
110 }
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400111 return callPhoto(c->peerContactMethod(), size, displayPresence);
112}
113
114QVariant
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -0400115PixbufManipulator::callPhoto(const ContactMethod* n, const QSize& size, bool displayPresence)
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400116{
117 if (n->contact()) {
118 return contactPhoto(n->contact(), size, displayPresence);
119 } else {
Sébastien Blin9684dd32017-07-24 11:15:19 -0400120 return QVariant::fromValue(scaleAndFrame(generateAvatar(n).get(), size, displayPresence, n->isPresent()));
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400121 }
122}
123
124QVariant
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -0400125PixbufManipulator::contactPhoto(Person* c, const QSize& size, bool displayPresence)
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400126{
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400127 /**
128 * try to get the photo
Sébastien Blin9684dd32017-07-24 11:15:19 -0400129 * otherwise use the generated avatar
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400130 */
131
132 std::shared_ptr<GdkPixbuf> photo;
133
134 if (c->photo().isValid())
135 photo = c->photo().value<std::shared_ptr<GdkPixbuf>>();
Sébastien Blin9684dd32017-07-24 11:15:19 -0400136 else {
137 auto cm = c->phoneNumbers().size() > 0 ? c->phoneNumbers().first() : nullptr;
138 photo = generateAvatar(cm);
139 }
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400140
aviauc372e812016-12-01 16:13:16 -0500141 return QVariant::fromValue(scaleAndFrame(photo.get(), size, displayPresence, c->isPresent()));
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400142}
143
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -0400144QVariant PixbufManipulator::personPhoto(const QByteArray& data, const QString& type)
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400145{
146 Q_UNUSED(type);
147 /* Try to load the image from the data provided by lrc vcard utils;
148 * lrc is getting the image data assuming that it is inlined in the vcard,
149 * for now URIs are not supported.
150 *
151 * The format of the data should be either base 64 or ascii (hex), try both
152 */
153
154 GError *error = NULL;
155 GdkPixbuf *pixbuf = NULL;
156 GInputStream *stream = NULL;
157
158 /* first try using base64 */
159 QByteArray ba64 = QByteArray::fromBase64(data);
160 stream = g_memory_input_stream_new_from_data(ba64.constData(),
161 ba64.size(),
162 NULL);
163
164 pixbuf = gdk_pixbuf_new_from_stream(stream, NULL, &error);
165 g_input_stream_close(stream, NULL, NULL);
166 g_object_unref(stream);
167
168 if (!pixbuf) {
169 // g_debug("failed decoding person photo using base64: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400170 g_clear_error(&error);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400171
172 /* failed with base64, try hex */
173 QByteArray baHex = QByteArray::fromHex(data);
174 stream = g_memory_input_stream_new_from_data(baHex.constData(),
175 baHex.size(),
176 NULL);
177
178 pixbuf = gdk_pixbuf_new_from_stream(stream, NULL, &error);
179 g_input_stream_close(stream, NULL, NULL);
180 g_object_unref(stream);
181
182 if (!pixbuf) {
183 // g_debug("failed decoding person photo using hex (ASCII): %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400184 g_clear_error(&error);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400185 }
186 }
187
188 if (pixbuf) {
189 std::shared_ptr<GdkPixbuf> avatar(pixbuf, g_object_unref);
190 return QVariant::fromValue(avatar);
191 }
192
193 /* could not load image, return emtpy QVariant */
194 return QVariant();
195}
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -0400196
197QVariant
198PixbufManipulator::numberCategoryIcon(const QVariant& p, const QSize& size, bool displayPresence, bool isPresent)
199{
200 Q_UNUSED(p)
201 Q_UNUSED(size)
202 Q_UNUSED(displayPresence)
203 Q_UNUSED(isPresent)
204 return QVariant();
205}
206
207QVariant
208PixbufManipulator::securityIssueIcon(const QModelIndex& index)
209{
210 Q_UNUSED(index)
211 return QVariant();
212}
213
214QByteArray
215PixbufManipulator::toByteArray(const QVariant& pxm)
216{
Nicolas Jager1cf27112016-05-17 11:51:28 -0400217 std::shared_ptr<GdkPixbuf> pixbuf_photo = pxm.value<std::shared_ptr<GdkPixbuf>>();
218
219 if(pixbuf_photo.get()) {
220 gchar* png_buffer = nullptr;
221 gsize png_buffer_size;
222 GError *error = nullptr;
223
224 gdk_pixbuf_save_to_buffer(pixbuf_photo.get(), &png_buffer, &png_buffer_size, "png", &error, NULL);
225 QByteArray array = QByteArray(png_buffer, png_buffer_size);
226
227 g_free(png_buffer);
228
229 if (error != NULL) {
230 g_warning("in toByteArray, gdk_pixbuf_save_to_buffer failed : %s\n", error->message);
231 g_clear_error(&error);
232 }
233
234 return array;
235 } else {
236 g_debug("in toByteArray, failed to retrieve data from parameter pxm");
237 return QByteArray();
238 }
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -0400239}
240
241QVariant
242PixbufManipulator::collectionIcon(const CollectionInterface* interface, PixmapManipulatorI::CollectionIconHint hint) const
243{
244 Q_UNUSED(interface)
245 Q_UNUSED(hint)
246 return QVariant();
247}
248QVariant
249PixbufManipulator::securityLevelIcon(const SecurityEvaluationModel::SecurityLevel level) const
250{
251 Q_UNUSED(level)
252 return QVariant();
253}
254QVariant
255PixbufManipulator::historySortingCategoryIcon(const CategorizedHistoryModel::SortedProxy::Categories cat) const
256{
257 Q_UNUSED(cat)
258 return QVariant();
259}
260QVariant
261PixbufManipulator::contactSortingCategoryIcon(const CategorizedContactModel::SortedProxy::Categories cat) const
262{
263 Q_UNUSED(cat)
264 return QVariant();
265}
266QVariant
267PixbufManipulator::userActionIcon(const UserActionElement& state) const
268{
269 Q_UNUSED(state)
270 return QVariant();
271}
272
Stepan Salenikovich8d076952016-01-08 13:48:43 -0500273QVariant PixbufManipulator::decorationRole(const QModelIndex& index)
274{
275 Q_UNUSED(index)
276 return QVariant();
277}
278
279QVariant PixbufManipulator::decorationRole(const Call* c)
280{
281 Q_UNUSED(c)
282 return QVariant();
283}
284
285QVariant PixbufManipulator::decorationRole(const ContactMethod* cm)
286{
287 Q_UNUSED(cm)
288 return QVariant();
289}
290
291QVariant PixbufManipulator::decorationRole(const Person* p)
292{
293 Q_UNUSED(p)
294 return QVariant();
295}
296
Alexandre Lision50fd6af2016-04-20 17:13:58 -0400297QVariant PixbufManipulator::decorationRole(const Account* p)
298{
299 Q_UNUSED(p)
300 return QVariant();
301}
302
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -0400303} // namespace Interfaces