blob: 0d0f07a33d53cf93ee7ca817ef0717bb1df9416b [file] [log] [blame]
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001/*
2 * Copyright (C) 2015 Savoir-Faire Linux Inc.
3 * 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.
18 *
19 * Additional permission under GNU GPL version 3 section 7:
20 *
21 * If you modify this program, or any covered work, by linking or
22 * combining it with the OpenSSL project's OpenSSL library (or a
23 * modified version of that library), containing parts covered by the
24 * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
25 * grants you additional permission to convey the resulting work.
26 * Corresponding Source for a non-source form of such a combination
27 * shall include the source code for the parts of OpenSSL used as well
28 * as that of the covered work.
29 */
30
31#include "pixbufdelegate.h"
32
33#include "../utils/drawing.h"
34#include <QtCore/QSize>
35#include <QtCore/QMetaType>
36#include <person.h>
37#include <memory>
38#include <call.h>
39#include <contactmethod.h>
40
41PixbufDelegate::PixbufDelegate()
42 : PixmapManipulationDelegate()
43 , fallbackAvatar_{ring_draw_fallback_avatar(FALLBACK_AVATAR_SIZE), g_object_unref}
44{
45}
46
47std::shared_ptr<GdkPixbuf>
48PixbufDelegate::scaleAndFrame(const GdkPixbuf *photo, const QSize& size)
49{
50 /**
51 * for now, respect the height requested
52 * the framing process will add another 10px, so account for that
53 * when scaling the photos
54 */
55
56 int height = size.height();
57 if (size.height() != size.width())
58 g_warning("requested contact photo width != height; only respecting the height as the largest dimension");
59 int photo_h = height - 10;
60 int photo_w = photo_h;
61
62 /* scale photo, make sure to respect the request height as the largest dimension*/
63 int w = gdk_pixbuf_get_width(photo);
64 int h = gdk_pixbuf_get_height(photo);
65 if (h > w)
66 photo_w = w * ((double)photo_h / h);
67 if (w > h)
68 photo_h = h * ((double)photo_w / w);
69
70 std::unique_ptr<GdkPixbuf, decltype(g_object_unref)&> scaled_photo{
71 gdk_pixbuf_scale_simple(photo, photo_w, photo_h, GDK_INTERP_BILINEAR),
72 g_object_unref};
73
74 /* frame photo */
75 return {ring_frame_avatar(scaled_photo.get()), g_object_unref};
76}
77
78QVariant
79PixbufDelegate::callPhoto(Call* c, const QSize& size, bool displayPresence)
80{
81 return callPhoto(c->peerContactMethod(), size, displayPresence);
82}
83
84QVariant
85PixbufDelegate::callPhoto(const ContactMethod* n, const QSize& size, bool displayPresence)
86{
87 if (n->contact()) {
88 return contactPhoto(n->contact(), size, displayPresence);
89 } else {
90 return QVariant::fromValue(scaleAndFrame(fallbackAvatar_.get(), size));
91 }
92}
93
94QVariant
95PixbufDelegate::contactPhoto(Person* c, const QSize& size, bool displayPresence)
96{
97 Q_UNUSED(displayPresence);
98
99 /**
100 * try to get the photo
101 * otherwise use the fallback avatar
102 */
103
104 std::shared_ptr<GdkPixbuf> photo;
105
106 if (c->photo().isValid())
107 photo = c->photo().value<std::shared_ptr<GdkPixbuf>>();
108 else
109 photo = fallbackAvatar_;
110
111 return QVariant::fromValue(scaleAndFrame(photo.get(), size));
112}
113
114QVariant PixbufDelegate::personPhoto(const QByteArray& data, const QString& type)
115{
116 Q_UNUSED(type);
117 /* Try to load the image from the data provided by lrc vcard utils;
118 * lrc is getting the image data assuming that it is inlined in the vcard,
119 * for now URIs are not supported.
120 *
121 * The format of the data should be either base 64 or ascii (hex), try both
122 */
123
124 GError *error = NULL;
125 GdkPixbuf *pixbuf = NULL;
126 GInputStream *stream = NULL;
127
128 /* first try using base64 */
129 QByteArray ba64 = QByteArray::fromBase64(data);
130 stream = g_memory_input_stream_new_from_data(ba64.constData(),
131 ba64.size(),
132 NULL);
133
134 pixbuf = gdk_pixbuf_new_from_stream(stream, NULL, &error);
135 g_input_stream_close(stream, NULL, NULL);
136 g_object_unref(stream);
137
138 if (!pixbuf) {
139 // g_debug("failed decoding person photo using base64: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400140 g_clear_error(&error);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400141
142 /* failed with base64, try hex */
143 QByteArray baHex = QByteArray::fromHex(data);
144 stream = g_memory_input_stream_new_from_data(baHex.constData(),
145 baHex.size(),
146 NULL);
147
148 pixbuf = gdk_pixbuf_new_from_stream(stream, NULL, &error);
149 g_input_stream_close(stream, NULL, NULL);
150 g_object_unref(stream);
151
152 if (!pixbuf) {
153 // g_debug("failed decoding person photo using hex (ASCII): %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400154 g_clear_error(&error);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400155 }
156 }
157
158 if (pixbuf) {
159 std::shared_ptr<GdkPixbuf> avatar(pixbuf, g_object_unref);
160 return QVariant::fromValue(avatar);
161 }
162
163 /* could not load image, return emtpy QVariant */
164 return QVariant();
165}