blob: 032cb00fa581decbac87d261ec3e4c9a57e0c8dd [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()
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>
aviauc372e812016-12-01 16:13:16 -050039PixbufManipulator::scaleAndFrame(const GdkPixbuf *photo, const QSize& size, bool display_presence, bool is_present)
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 */
aviauc372e812016-12-01 16:13:16 -050066 std::shared_ptr<GdkPixbuf> result {
67 ring_frame_avatar(scaled_photo.get()),
68 g_object_unref
69 };
70
71 /* draw presence */
72 if (display_presence)
73 result.reset(ring_draw_presence(result.get(), is_present), g_object_unref);
74
75 return result;
Stepan Salenikovich6f687072015-03-26 10:43:37 -040076}
77
78QVariant
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040079PixbufManipulator::callPhoto(Call* c, const QSize& size, bool displayPresence)
Stepan Salenikovich6f687072015-03-26 10:43:37 -040080{
aviauc372e812016-12-01 16:13:16 -050081 if (c->type() == Call::Type::CONFERENCE) {
82 /* conferences are always "online" */
83 return QVariant::fromValue(scaleAndFrame(conferenceAvatar_.get(), size, displayPresence, TRUE));
84 }
Stepan Salenikovich6f687072015-03-26 10:43:37 -040085 return callPhoto(c->peerContactMethod(), size, displayPresence);
86}
87
88QVariant
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040089PixbufManipulator::callPhoto(const ContactMethod* n, const QSize& size, bool displayPresence)
Stepan Salenikovich6f687072015-03-26 10:43:37 -040090{
91 if (n->contact()) {
92 return contactPhoto(n->contact(), size, displayPresence);
93 } else {
aviauc372e812016-12-01 16:13:16 -050094 return QVariant::fromValue(scaleAndFrame(fallbackAvatar_.get(), size, displayPresence, n->isPresent()));
Stepan Salenikovich6f687072015-03-26 10:43:37 -040095 }
96}
97
98QVariant
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040099PixbufManipulator::contactPhoto(Person* c, const QSize& size, bool displayPresence)
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400100{
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400101 /**
102 * try to get the photo
103 * otherwise use the fallback avatar
104 */
105
106 std::shared_ptr<GdkPixbuf> photo;
107
108 if (c->photo().isValid())
109 photo = c->photo().value<std::shared_ptr<GdkPixbuf>>();
110 else
111 photo = fallbackAvatar_;
112
aviauc372e812016-12-01 16:13:16 -0500113 return QVariant::fromValue(scaleAndFrame(photo.get(), size, displayPresence, c->isPresent()));
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400114}
115
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -0400116QVariant PixbufManipulator::personPhoto(const QByteArray& data, const QString& type)
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400117{
118 Q_UNUSED(type);
119 /* Try to load the image from the data provided by lrc vcard utils;
120 * lrc is getting the image data assuming that it is inlined in the vcard,
121 * for now URIs are not supported.
122 *
123 * The format of the data should be either base 64 or ascii (hex), try both
124 */
125
126 GError *error = NULL;
127 GdkPixbuf *pixbuf = NULL;
128 GInputStream *stream = NULL;
129
130 /* first try using base64 */
131 QByteArray ba64 = QByteArray::fromBase64(data);
132 stream = g_memory_input_stream_new_from_data(ba64.constData(),
133 ba64.size(),
134 NULL);
135
136 pixbuf = gdk_pixbuf_new_from_stream(stream, NULL, &error);
137 g_input_stream_close(stream, NULL, NULL);
138 g_object_unref(stream);
139
140 if (!pixbuf) {
141 // g_debug("failed decoding person photo using base64: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400142 g_clear_error(&error);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400143
144 /* failed with base64, try hex */
145 QByteArray baHex = QByteArray::fromHex(data);
146 stream = g_memory_input_stream_new_from_data(baHex.constData(),
147 baHex.size(),
148 NULL);
149
150 pixbuf = gdk_pixbuf_new_from_stream(stream, NULL, &error);
151 g_input_stream_close(stream, NULL, NULL);
152 g_object_unref(stream);
153
154 if (!pixbuf) {
155 // g_debug("failed decoding person photo using hex (ASCII): %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400156 g_clear_error(&error);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400157 }
158 }
159
160 if (pixbuf) {
161 std::shared_ptr<GdkPixbuf> avatar(pixbuf, g_object_unref);
162 return QVariant::fromValue(avatar);
163 }
164
165 /* could not load image, return emtpy QVariant */
166 return QVariant();
167}
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -0400168
169QVariant
170PixbufManipulator::numberCategoryIcon(const QVariant& p, const QSize& size, bool displayPresence, bool isPresent)
171{
172 Q_UNUSED(p)
173 Q_UNUSED(size)
174 Q_UNUSED(displayPresence)
175 Q_UNUSED(isPresent)
176 return QVariant();
177}
178
179QVariant
180PixbufManipulator::securityIssueIcon(const QModelIndex& index)
181{
182 Q_UNUSED(index)
183 return QVariant();
184}
185
186QByteArray
187PixbufManipulator::toByteArray(const QVariant& pxm)
188{
Nicolas Jager1cf27112016-05-17 11:51:28 -0400189 std::shared_ptr<GdkPixbuf> pixbuf_photo = pxm.value<std::shared_ptr<GdkPixbuf>>();
190
191 if(pixbuf_photo.get()) {
192 gchar* png_buffer = nullptr;
193 gsize png_buffer_size;
194 GError *error = nullptr;
195
196 gdk_pixbuf_save_to_buffer(pixbuf_photo.get(), &png_buffer, &png_buffer_size, "png", &error, NULL);
197 QByteArray array = QByteArray(png_buffer, png_buffer_size);
198
199 g_free(png_buffer);
200
201 if (error != NULL) {
202 g_warning("in toByteArray, gdk_pixbuf_save_to_buffer failed : %s\n", error->message);
203 g_clear_error(&error);
204 }
205
206 return array;
207 } else {
208 g_debug("in toByteArray, failed to retrieve data from parameter pxm");
209 return QByteArray();
210 }
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -0400211}
212
213QVariant
214PixbufManipulator::collectionIcon(const CollectionInterface* interface, PixmapManipulatorI::CollectionIconHint hint) const
215{
216 Q_UNUSED(interface)
217 Q_UNUSED(hint)
218 return QVariant();
219}
220QVariant
221PixbufManipulator::securityLevelIcon(const SecurityEvaluationModel::SecurityLevel level) const
222{
223 Q_UNUSED(level)
224 return QVariant();
225}
226QVariant
227PixbufManipulator::historySortingCategoryIcon(const CategorizedHistoryModel::SortedProxy::Categories cat) const
228{
229 Q_UNUSED(cat)
230 return QVariant();
231}
232QVariant
233PixbufManipulator::contactSortingCategoryIcon(const CategorizedContactModel::SortedProxy::Categories cat) const
234{
235 Q_UNUSED(cat)
236 return QVariant();
237}
238QVariant
239PixbufManipulator::userActionIcon(const UserActionElement& state) const
240{
241 Q_UNUSED(state)
242 return QVariant();
243}
244
Stepan Salenikovich8d076952016-01-08 13:48:43 -0500245QVariant PixbufManipulator::decorationRole(const QModelIndex& index)
246{
247 Q_UNUSED(index)
248 return QVariant();
249}
250
251QVariant PixbufManipulator::decorationRole(const Call* c)
252{
253 Q_UNUSED(c)
254 return QVariant();
255}
256
257QVariant PixbufManipulator::decorationRole(const ContactMethod* cm)
258{
259 Q_UNUSED(cm)
260 return QVariant();
261}
262
263QVariant PixbufManipulator::decorationRole(const Person* p)
264{
265 Q_UNUSED(p)
266 return QVariant();
267}
268
Alexandre Lision50fd6af2016-04-20 17:13:58 -0400269QVariant PixbufManipulator::decorationRole(const Account* p)
270{
271 Q_UNUSED(p)
272 return QVariant();
273}
274
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -0400275} // namespace Interfaces