blob: 1e534bfb4e68872a78e5853e9405bc426d4ee18d [file] [log] [blame]
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001/*
Stepan Salenikovichbe87d2c2016-01-25 14:14:34 -05002 * Copyright (C) 2015-2016 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()
33 : fallbackAvatar_{ring_draw_fallback_avatar(FALLBACK_AVATAR_SIZE), g_object_unref}
Stepan Salenikovichee8506e2015-08-13 11:35:14 -040034 , conferenceAvatar_{ring_draw_conference_avatar(FALLBACK_AVATAR_SIZE), g_object_unref}
Stepan Salenikovich6f687072015-03-26 10:43:37 -040035{
36}
37
38std::shared_ptr<GdkPixbuf>
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040039PixbufManipulator::scaleAndFrame(const GdkPixbuf *photo, const QSize& size)
Stepan Salenikovich6f687072015-03-26 10:43:37 -040040{
41 /**
42 * for now, respect the height requested
43 * the framing process will add another 10px, so account for that
44 * when scaling the photos
45 */
46
47 int height = size.height();
48 if (size.height() != size.width())
49 g_warning("requested contact photo width != height; only respecting the height as the largest dimension");
50 int photo_h = height - 10;
51 int photo_w = photo_h;
52
53 /* scale photo, make sure to respect the request height as the largest dimension*/
54 int w = gdk_pixbuf_get_width(photo);
55 int h = gdk_pixbuf_get_height(photo);
56 if (h > w)
57 photo_w = w * ((double)photo_h / h);
58 if (w > h)
59 photo_h = h * ((double)photo_w / w);
60
61 std::unique_ptr<GdkPixbuf, decltype(g_object_unref)&> scaled_photo{
62 gdk_pixbuf_scale_simple(photo, photo_w, photo_h, GDK_INTERP_BILINEAR),
63 g_object_unref};
64
65 /* frame photo */
66 return {ring_frame_avatar(scaled_photo.get()), g_object_unref};
67}
68
69QVariant
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040070PixbufManipulator::callPhoto(Call* c, const QSize& size, bool displayPresence)
Stepan Salenikovich6f687072015-03-26 10:43:37 -040071{
Stepan Salenikovichee8506e2015-08-13 11:35:14 -040072 if (c->type() == Call::Type::CONFERENCE)
73 return QVariant::fromValue(scaleAndFrame(conferenceAvatar_.get(), size));
Stepan Salenikovich6f687072015-03-26 10:43:37 -040074 return callPhoto(c->peerContactMethod(), size, displayPresence);
75}
76
77QVariant
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040078PixbufManipulator::callPhoto(const ContactMethod* n, const QSize& size, bool displayPresence)
Stepan Salenikovich6f687072015-03-26 10:43:37 -040079{
80 if (n->contact()) {
81 return contactPhoto(n->contact(), size, displayPresence);
82 } else {
83 return QVariant::fromValue(scaleAndFrame(fallbackAvatar_.get(), size));
84 }
85}
86
87QVariant
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040088PixbufManipulator::contactPhoto(Person* c, const QSize& size, bool displayPresence)
Stepan Salenikovich6f687072015-03-26 10:43:37 -040089{
90 Q_UNUSED(displayPresence);
91
92 /**
93 * try to get the photo
94 * otherwise use the fallback avatar
95 */
96
97 std::shared_ptr<GdkPixbuf> photo;
98
99 if (c->photo().isValid())
100 photo = c->photo().value<std::shared_ptr<GdkPixbuf>>();
101 else
102 photo = fallbackAvatar_;
103
104 return QVariant::fromValue(scaleAndFrame(photo.get(), size));
105}
106
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -0400107QVariant PixbufManipulator::personPhoto(const QByteArray& data, const QString& type)
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400108{
109 Q_UNUSED(type);
110 /* Try to load the image from the data provided by lrc vcard utils;
111 * lrc is getting the image data assuming that it is inlined in the vcard,
112 * for now URIs are not supported.
113 *
114 * The format of the data should be either base 64 or ascii (hex), try both
115 */
116
117 GError *error = NULL;
118 GdkPixbuf *pixbuf = NULL;
119 GInputStream *stream = NULL;
120
121 /* first try using base64 */
122 QByteArray ba64 = QByteArray::fromBase64(data);
123 stream = g_memory_input_stream_new_from_data(ba64.constData(),
124 ba64.size(),
125 NULL);
126
127 pixbuf = gdk_pixbuf_new_from_stream(stream, NULL, &error);
128 g_input_stream_close(stream, NULL, NULL);
129 g_object_unref(stream);
130
131 if (!pixbuf) {
132 // g_debug("failed decoding person photo using base64: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400133 g_clear_error(&error);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400134
135 /* failed with base64, try hex */
136 QByteArray baHex = QByteArray::fromHex(data);
137 stream = g_memory_input_stream_new_from_data(baHex.constData(),
138 baHex.size(),
139 NULL);
140
141 pixbuf = gdk_pixbuf_new_from_stream(stream, NULL, &error);
142 g_input_stream_close(stream, NULL, NULL);
143 g_object_unref(stream);
144
145 if (!pixbuf) {
146 // g_debug("failed decoding person photo using hex (ASCII): %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400147 g_clear_error(&error);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400148 }
149 }
150
151 if (pixbuf) {
152 std::shared_ptr<GdkPixbuf> avatar(pixbuf, g_object_unref);
153 return QVariant::fromValue(avatar);
154 }
155
156 /* could not load image, return emtpy QVariant */
157 return QVariant();
158}
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -0400159
160QVariant
161PixbufManipulator::numberCategoryIcon(const QVariant& p, const QSize& size, bool displayPresence, bool isPresent)
162{
163 Q_UNUSED(p)
164 Q_UNUSED(size)
165 Q_UNUSED(displayPresence)
166 Q_UNUSED(isPresent)
167 return QVariant();
168}
169
170QVariant
171PixbufManipulator::securityIssueIcon(const QModelIndex& index)
172{
173 Q_UNUSED(index)
174 return QVariant();
175}
176
177QByteArray
178PixbufManipulator::toByteArray(const QVariant& pxm)
179{
Nicolas Jager1cf27112016-05-17 11:51:28 -0400180 std::shared_ptr<GdkPixbuf> pixbuf_photo = pxm.value<std::shared_ptr<GdkPixbuf>>();
181
182 if(pixbuf_photo.get()) {
183 gchar* png_buffer = nullptr;
184 gsize png_buffer_size;
185 GError *error = nullptr;
186
187 gdk_pixbuf_save_to_buffer(pixbuf_photo.get(), &png_buffer, &png_buffer_size, "png", &error, NULL);
188 QByteArray array = QByteArray(png_buffer, png_buffer_size);
189
190 g_free(png_buffer);
191
192 if (error != NULL) {
193 g_warning("in toByteArray, gdk_pixbuf_save_to_buffer failed : %s\n", error->message);
194 g_clear_error(&error);
195 }
196
197 return array;
198 } else {
199 g_debug("in toByteArray, failed to retrieve data from parameter pxm");
200 return QByteArray();
201 }
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -0400202}
203
204QVariant
205PixbufManipulator::collectionIcon(const CollectionInterface* interface, PixmapManipulatorI::CollectionIconHint hint) const
206{
207 Q_UNUSED(interface)
208 Q_UNUSED(hint)
209 return QVariant();
210}
211QVariant
212PixbufManipulator::securityLevelIcon(const SecurityEvaluationModel::SecurityLevel level) const
213{
214 Q_UNUSED(level)
215 return QVariant();
216}
217QVariant
218PixbufManipulator::historySortingCategoryIcon(const CategorizedHistoryModel::SortedProxy::Categories cat) const
219{
220 Q_UNUSED(cat)
221 return QVariant();
222}
223QVariant
224PixbufManipulator::contactSortingCategoryIcon(const CategorizedContactModel::SortedProxy::Categories cat) const
225{
226 Q_UNUSED(cat)
227 return QVariant();
228}
229QVariant
230PixbufManipulator::userActionIcon(const UserActionElement& state) const
231{
232 Q_UNUSED(state)
233 return QVariant();
234}
235
Stepan Salenikovich8d076952016-01-08 13:48:43 -0500236QVariant PixbufManipulator::decorationRole(const QModelIndex& index)
237{
238 Q_UNUSED(index)
239 return QVariant();
240}
241
242QVariant PixbufManipulator::decorationRole(const Call* c)
243{
244 Q_UNUSED(c)
245 return QVariant();
246}
247
248QVariant PixbufManipulator::decorationRole(const ContactMethod* cm)
249{
250 Q_UNUSED(cm)
251 return QVariant();
252}
253
254QVariant PixbufManipulator::decorationRole(const Person* p)
255{
256 Q_UNUSED(p)
257 return QVariant();
258}
259
Alexandre Lision50fd6af2016-04-20 17:13:58 -0400260QVariant PixbufManipulator::decorationRole(const Account* p)
261{
262 Q_UNUSED(p)
263 return QVariant();
264}
265
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -0400266} // namespace Interfaces