blob: c21f8e4e1090fdfedbd7dc19bb8ea1d173f1429e [file] [log] [blame]
Alexandre Lision3b0bd332015-03-15 18:43:07 -04001/*
Alexandre Lision9fe374b2016-01-06 10:17:31 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Alexandre Lision3b0bd332015-03-15 18:43:07 -04003 * Author: Alexandre Lision <alexandre.lision@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.
Alexandre Lision3b0bd332015-03-15 18:43:07 -040018 */
19#import "ImageManipulationDelegate.h"
20
21#import <Cocoa/Cocoa.h>
22#import <Foundation/Foundation.h>
23
24//Qt
25#import <QSize>
26#import <QBuffer>
27#import <QtGui/QColor>
28#import <QtGui/QPainter>
29#import <QtGui/QBitmap>
30#import <QtWidgets/QApplication>
31#import <QtGui/QImage>
32#import <QtMacExtras/qmacfunctions.h>
33#import <QtGui/QPalette>
34
35//Ring
36#import <person.h>
Alexandre Lision43e91bc2016-04-19 18:04:52 -040037#import <profilemodel.h>
38#import <profile.h>
Alexandre Lision3b0bd332015-03-15 18:43:07 -040039#import <contactmethod.h>
Alexandre Lision3b0bd332015-03-15 18:43:07 -040040
Alexandre Lision7a166e42015-09-02 15:04:43 -040041namespace Interfaces {
Alexandre Lision3b0bd332015-03-15 18:43:07 -040042
Alexandre Lision7a166e42015-09-02 15:04:43 -040043 ImageManipulationDelegate::ImageManipulationDelegate()
44 {
Alexandre Lision3b0bd332015-03-15 18:43:07 -040045
Alexandre Lision7a166e42015-09-02 15:04:43 -040046 }
Alexandre Lision3b0bd332015-03-15 18:43:07 -040047
Alexandre Lision7a166e42015-09-02 15:04:43 -040048 QVariant ImageManipulationDelegate::contactPhoto(Person* c, const QSize& size, bool displayPresence) {
Alexandre Lisionde0314b2015-09-02 15:45:21 -040049 const int radius = size.height() / 2;
Alexandre Lision7a166e42015-09-02 15:04:43 -040050
51 QPixmap pxm;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040052 if (c && c->photo().isValid()) {
Alexandre Lisionf02a32b2016-04-19 15:01:07 -040053 QPixmap contactPhoto(qvariant_cast<QPixmap>(c->photo()).scaled(size, Qt::KeepAspectRatioByExpanding,
54 Qt::SmoothTransformation));
55
56 QPixmap finalImg;
57 if (contactPhoto.size() != size) {
58 finalImg = crop(contactPhoto, size);
59 } else
60 finalImg = contactPhoto;
61
Alexandre Lision7a166e42015-09-02 15:04:43 -040062 pxm = QPixmap(size);
63 pxm.fill(Qt::transparent);
64 QPainter painter(&pxm);
65
66 //Clear the pixmap
Alexandre Lisionde0314b2015-09-02 15:45:21 -040067 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
Alexandre Lision7a166e42015-09-02 15:04:43 -040068 painter.setCompositionMode(QPainter::CompositionMode_Clear);
69 painter.fillRect(0,0,size.width(),size.height(),QBrush(Qt::white));
70 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
71
72 //Add corner radius to the Pixmap
Alexandre Lisionf02a32b2016-04-19 15:01:07 -040073 QRect pxRect = finalImg.rect();
Alexandre Lision7a166e42015-09-02 15:04:43 -040074 QBitmap mask(pxRect.size());
75 QPainter customPainter(&mask);
Alexandre Lisionde0314b2015-09-02 15:45:21 -040076 customPainter.setRenderHints (QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
Alexandre Lision7a166e42015-09-02 15:04:43 -040077 customPainter.fillRect (pxRect , Qt::white );
78 customPainter.setBackground (Qt::black );
79 customPainter.setBrush (Qt::black );
Alexandre Lisionf02a32b2016-04-19 15:01:07 -040080 customPainter.drawRoundedRect(pxRect,radius,radius );
81 finalImg.setMask (mask );
82 painter.drawPixmap (0,0,finalImg );
Alexandre Lisionde0314b2015-09-02 15:45:21 -040083 painter.setBrush (Qt::NoBrush );
84 painter.setPen (Qt::black );
85 painter.setCompositionMode (QPainter::CompositionMode_SourceIn);
86 painter.drawRoundedRect(0,0,pxm.height(),pxm.height(),radius,radius);
Alexandre Lision7a166e42015-09-02 15:04:43 -040087 }
88 else {
Alexandre Lision43e91bc2016-04-19 18:04:52 -040089 pxm = drawDefaultUserPixmap(size);
Alexandre Lision7a166e42015-09-02 15:04:43 -040090 }
91
92 return pxm;
93 }
94
Alexandre Lisionf02a32b2016-04-19 15:01:07 -040095 QPixmap
96 ImageManipulationDelegate::crop(QPixmap& photo, const QSize& destSize)
97 {
98 auto initSize = photo.size();
99 float leftDelta = 0;
100 float topDelta = 0;
101
102 if (destSize.height() == initSize.height()) {
103 leftDelta = (destSize.width() - initSize.width()) / 2;
104 } else {
105 topDelta = (destSize.height() - initSize.height()) / 2;
106 }
107
108 float xScale = (float)destSize.width() / initSize.width();
109 float yScale = (float)destSize.height() / initSize.height();
110
111 QRectF destRect(leftDelta, topDelta,
112 initSize.width() - leftDelta, initSize.height() - topDelta);
113
114 destRect.setLeft(leftDelta * xScale);
115 destRect.setTop(topDelta * yScale);
116
117 destRect.setWidth((initSize.width() - leftDelta) * xScale);
118 destRect.setHeight((initSize.height() - topDelta) * yScale);
119
120 return photo.copy(destRect.toRect());
121 }
122
Alexandre Lision7a166e42015-09-02 15:04:43 -0400123 QVariant
124 ImageManipulationDelegate::callPhoto(Call* c, const QSize& size, bool displayPresence)
125 {
126 return callPhoto(c->peerContactMethod(), size, displayPresence);
127 }
128
129 QVariant
130 ImageManipulationDelegate::callPhoto(const ContactMethod* n, const QSize& size, bool displayPresence)
131 {
132 if (n->contact()) {
133 return contactPhoto(n->contact(), size, displayPresence);
134 } else {
Alexandre Lision43e91bc2016-04-19 18:04:52 -0400135 return drawDefaultUserPixmap(size);
Alexandre Lision7a166e42015-09-02 15:04:43 -0400136 }
137 }
138
139 QVariant ImageManipulationDelegate::personPhoto(const QByteArray& data, const QString& type)
140 {
141 QImage image;
142 //For now, ENCODING is only base64 and image type PNG or JPG
143 const bool ret = image.loadFromData(QByteArray::fromBase64(data),type.toLatin1());
Alexandre Lision43e91bc2016-04-19 18:04:52 -0400144 if (!ret) {
Alexandre Lision7a166e42015-09-02 15:04:43 -0400145 qDebug() << "vCard image loading failed";
Alexandre Lision43e91bc2016-04-19 18:04:52 -0400146 return drawDefaultUserPixmap(decorationSize);
147 }
Alexandre Lision7a166e42015-09-02 15:04:43 -0400148
149 return QPixmap::fromImage(image);
150 }
151
152 QByteArray ImageManipulationDelegate::toByteArray(const QVariant& pxm)
153 {
154 //Preparation of our QPixmap
155 QByteArray bArray;
156 QBuffer buffer(&bArray);
157 buffer.open(QIODevice::WriteOnly);
158
159 //PNG ?
160 (qvariant_cast<QPixmap>(pxm)).save(&buffer, "PNG");
161 buffer.close();
162
163 return bArray;
164 }
165
166 QPixmap ImageManipulationDelegate::drawDefaultUserPixmap(const QSize& size, bool displayPresence, bool isPresent) {
Alexandre Lision7a166e42015-09-02 15:04:43 -0400167 // create the image somehow, load from file, draw into it...
Alexandre Lision4baba4c2016-02-11 13:00:57 -0500168 auto sourceImgRef = CGImageSourceCreateWithData((__bridge CFDataRef)[[NSImage imageNamed:@"default_user_icon"] TIFFRepresentation], NULL);
Alexandre Lision7a166e42015-09-02 15:04:43 -0400169 auto imgRef = CGImageSourceCreateImageAtIndex(sourceImgRef, 0, NULL);
Alexandre Lisionde0314b2015-09-02 15:45:21 -0400170 auto finalpxm = QtMac::fromCGImageRef(resizeCGImage(imgRef, size));
Alexandre Lision7a166e42015-09-02 15:04:43 -0400171 CFRelease(sourceImgRef);
172 CFRelease(imgRef);
Alexandre Lision3b0bd332015-03-15 18:43:07 -0400173
Alexandre Lisiond5229f32015-11-16 11:17:41 -0500174 return finalpxm;
Alexandre Lision3b0bd332015-03-15 18:43:07 -0400175 }
Alexandre Lision7a166e42015-09-02 15:04:43 -0400176
177 CGImageRef ImageManipulationDelegate::resizeCGImage(CGImageRef image, const QSize& size) {
178 // create context, keeping original image properties
Alexandre Lision7a166e42015-09-02 15:04:43 -0400179 CGContextRef context = CGBitmapContextCreate(NULL, size.width(), size.height(),
180 CGImageGetBitsPerComponent(image),
Alexandre Lisiond5229f32015-11-16 11:17:41 -0500181 CGImageGetBytesPerRow(image),
182 CGImageGetColorSpace(image),
183 kCGImageAlphaPremultipliedLast);
Alexandre Lision7a166e42015-09-02 15:04:43 -0400184
185 if(context == NULL)
186 return nil;
187
188 // draw image to context (resizing it)
189 CGContextDrawImage(context, CGRectMake(0, 0, size.width(), size.height()), image);
190 // extract resulting image from context
191 CGImageRef imgRef = CGBitmapContextCreateImage(context);
192 CGContextRelease(context);
193
194 return imgRef;
195 }
196
197 QVariant
198 ImageManipulationDelegate::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
207 QVariant
208 ImageManipulationDelegate::securityIssueIcon(const QModelIndex& index)
209 {
210 Q_UNUSED(index)
211 return QVariant();
212 }
213
214 QVariant
215 ImageManipulationDelegate::collectionIcon(const CollectionInterface* interface, PixmapManipulatorI::CollectionIconHint hint) const
216 {
217 Q_UNUSED(interface)
218 Q_UNUSED(hint)
219 return QVariant();
220 }
221 QVariant
222 ImageManipulationDelegate::securityLevelIcon(const SecurityEvaluationModel::SecurityLevel level) const
223 {
224 Q_UNUSED(level)
225 return QVariant();
226 }
227 QVariant
228 ImageManipulationDelegate::historySortingCategoryIcon(const CategorizedHistoryModel::SortedProxy::Categories cat) const
229 {
230 Q_UNUSED(cat)
231 return QVariant();
232 }
233 QVariant
234 ImageManipulationDelegate::contactSortingCategoryIcon(const CategorizedContactModel::SortedProxy::Categories cat) const
235 {
236 Q_UNUSED(cat)
237 return QVariant();
238 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400239
Alexandre Lision7a166e42015-09-02 15:04:43 -0400240 QVariant
241 ImageManipulationDelegate::userActionIcon(const UserActionElement& state) const
242 {
243 Q_UNUSED(state)
244 return QVariant();
Alexandre Lision3b0bd332015-03-15 18:43:07 -0400245 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400246
Stepan Salenikovich3a6ea302016-01-08 13:54:34 -0500247 QVariant ImageManipulationDelegate::decorationRole(const QModelIndex& index)
248 {
249 Q_UNUSED(index)
250 return QVariant();
251 }
252
253 QVariant ImageManipulationDelegate::decorationRole(const Call* c)
254 {
Alexandre Lision43e91bc2016-04-19 18:04:52 -0400255 if (c && c->peerContactMethod()
256 && c->peerContactMethod()->contact()) {
257 return contactPhoto(c->peerContactMethod()->contact(), decorationSize);
258 } else
259 return drawDefaultUserPixmap(decorationSize);
Stepan Salenikovich3a6ea302016-01-08 13:54:34 -0500260 }
261
262 QVariant ImageManipulationDelegate::decorationRole(const ContactMethod* cm)
263 {
Alexandre Lision43e91bc2016-04-19 18:04:52 -0400264 QImage photo;
265 if (cm && cm->contact() && cm->contact()->photo().isValid())
266 return contactPhoto(cm->contact(), decorationSize);
267 else
268 return drawDefaultUserPixmap(decorationSize);
Stepan Salenikovich3a6ea302016-01-08 13:54:34 -0500269 }
270
271 QVariant ImageManipulationDelegate::decorationRole(const Person* p)
272 {
Alexandre Lision43e91bc2016-04-19 18:04:52 -0400273 return contactPhoto(const_cast<Person*>(p), decorationSize);
274 }
275
276 QVariant ImageManipulationDelegate::decorationRole(const Account* acc)
277 {
278 Q_UNUSED(acc)
279 if (auto pro = ProfileModel::instance().selectedProfile())
280 return contactPhoto(pro->person(), decorationSize);
281 return drawDefaultUserPixmap(decorationSize);
Stepan Salenikovich3a6ea302016-01-08 13:54:34 -0500282 }
283
Alexandre Lision7a166e42015-09-02 15:04:43 -0400284} // namespace Interfaces