blob: 7780d9470a0b081f751ff3cd61a4645b4fceef13 [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>
Alexandre Lision4f264622016-05-08 17:08:56 -040029#import <QHash>
Alexandre Lision3b0bd332015-03-15 18:43:07 -040030#import <QtGui/QBitmap>
31#import <QtWidgets/QApplication>
32#import <QtGui/QImage>
33#import <QtMacExtras/qmacfunctions.h>
34#import <QtGui/QPalette>
35
36//Ring
37#import <person.h>
Alexandre Lision43e91bc2016-04-19 18:04:52 -040038#import <profilemodel.h>
39#import <profile.h>
Alexandre Lision3b0bd332015-03-15 18:43:07 -040040#import <contactmethod.h>
Alexandre Lision3b0bd332015-03-15 18:43:07 -040041
Alexandre Lision7a166e42015-09-02 15:04:43 -040042namespace Interfaces {
Alexandre Lision3b0bd332015-03-15 18:43:07 -040043
Alexandre Lision4f264622016-05-08 17:08:56 -040044 ImageManipulationDelegate::ImageManipulationDelegate() {}
Alexandre Lision3b0bd332015-03-15 18:43:07 -040045
Alexandre Lision7a166e42015-09-02 15:04:43 -040046 QVariant ImageManipulationDelegate::contactPhoto(Person* c, const QSize& size, bool displayPresence) {
Alexandre Lisionde0314b2015-09-02 15:45:21 -040047 const int radius = size.height() / 2;
Alexandre Lision7a166e42015-09-02 15:04:43 -040048
49 QPixmap pxm;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040050 if (c && c->photo().isValid()) {
Alexandre Lisionf02a32b2016-04-19 15:01:07 -040051 QPixmap contactPhoto(qvariant_cast<QPixmap>(c->photo()).scaled(size, Qt::KeepAspectRatioByExpanding,
52 Qt::SmoothTransformation));
53
54 QPixmap finalImg;
55 if (contactPhoto.size() != size) {
56 finalImg = crop(contactPhoto, size);
57 } else
58 finalImg = contactPhoto;
59
Alexandre Lision7a166e42015-09-02 15:04:43 -040060 pxm = QPixmap(size);
61 pxm.fill(Qt::transparent);
62 QPainter painter(&pxm);
63
64 //Clear the pixmap
Alexandre Lisionde0314b2015-09-02 15:45:21 -040065 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
Alexandre Lision7a166e42015-09-02 15:04:43 -040066 painter.setCompositionMode(QPainter::CompositionMode_Clear);
67 painter.fillRect(0,0,size.width(),size.height(),QBrush(Qt::white));
68 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
69
70 //Add corner radius to the Pixmap
Alexandre Lisionf02a32b2016-04-19 15:01:07 -040071 QRect pxRect = finalImg.rect();
Alexandre Lision7a166e42015-09-02 15:04:43 -040072 QBitmap mask(pxRect.size());
73 QPainter customPainter(&mask);
Alexandre Lisionde0314b2015-09-02 15:45:21 -040074 customPainter.setRenderHints (QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
Alexandre Lision7a166e42015-09-02 15:04:43 -040075 customPainter.fillRect (pxRect , Qt::white );
76 customPainter.setBackground (Qt::black );
77 customPainter.setBrush (Qt::black );
Alexandre Lisionf02a32b2016-04-19 15:01:07 -040078 customPainter.drawRoundedRect(pxRect,radius,radius );
79 finalImg.setMask (mask );
80 painter.drawPixmap (0,0,finalImg );
Alexandre Lisionde0314b2015-09-02 15:45:21 -040081 painter.setBrush (Qt::NoBrush );
82 painter.setPen (Qt::black );
83 painter.setCompositionMode (QPainter::CompositionMode_SourceIn);
84 painter.drawRoundedRect(0,0,pxm.height(),pxm.height(),radius,radius);
Alexandre Lision7a166e42015-09-02 15:04:43 -040085 }
86 else {
Alexandre Lision43e91bc2016-04-19 18:04:52 -040087 pxm = drawDefaultUserPixmap(size);
Alexandre Lision7a166e42015-09-02 15:04:43 -040088 }
89
90 return pxm;
91 }
92
Alexandre Lisionf02a32b2016-04-19 15:01:07 -040093 QPixmap
94 ImageManipulationDelegate::crop(QPixmap& photo, const QSize& destSize)
95 {
96 auto initSize = photo.size();
97 float leftDelta = 0;
98 float topDelta = 0;
99
100 if (destSize.height() == initSize.height()) {
101 leftDelta = (destSize.width() - initSize.width()) / 2;
102 } else {
103 topDelta = (destSize.height() - initSize.height()) / 2;
104 }
105
106 float xScale = (float)destSize.width() / initSize.width();
107 float yScale = (float)destSize.height() / initSize.height();
108
109 QRectF destRect(leftDelta, topDelta,
110 initSize.width() - leftDelta, initSize.height() - topDelta);
111
112 destRect.setLeft(leftDelta * xScale);
113 destRect.setTop(topDelta * yScale);
114
115 destRect.setWidth((initSize.width() - leftDelta) * xScale);
116 destRect.setHeight((initSize.height() - topDelta) * yScale);
117
118 return photo.copy(destRect.toRect());
119 }
120
Alexandre Lision7a166e42015-09-02 15:04:43 -0400121 QVariant
122 ImageManipulationDelegate::callPhoto(Call* c, const QSize& size, bool displayPresence)
123 {
124 return callPhoto(c->peerContactMethod(), size, displayPresence);
125 }
126
127 QVariant
128 ImageManipulationDelegate::callPhoto(const ContactMethod* n, const QSize& size, bool displayPresence)
129 {
130 if (n->contact()) {
131 return contactPhoto(n->contact(), size, displayPresence);
132 } else {
Alexandre Lision43e91bc2016-04-19 18:04:52 -0400133 return drawDefaultUserPixmap(size);
Alexandre Lision7a166e42015-09-02 15:04:43 -0400134 }
135 }
136
137 QVariant ImageManipulationDelegate::personPhoto(const QByteArray& data, const QString& type)
138 {
139 QImage image;
140 //For now, ENCODING is only base64 and image type PNG or JPG
141 const bool ret = image.loadFromData(QByteArray::fromBase64(data),type.toLatin1());
Alexandre Lision43e91bc2016-04-19 18:04:52 -0400142 if (!ret) {
Alexandre Lision7a166e42015-09-02 15:04:43 -0400143 qDebug() << "vCard image loading failed";
Alexandre Lision43e91bc2016-04-19 18:04:52 -0400144 return drawDefaultUserPixmap(decorationSize);
145 }
Alexandre Lision7a166e42015-09-02 15:04:43 -0400146
147 return QPixmap::fromImage(image);
148 }
149
150 QByteArray ImageManipulationDelegate::toByteArray(const QVariant& pxm)
151 {
152 //Preparation of our QPixmap
153 QByteArray bArray;
154 QBuffer buffer(&bArray);
155 buffer.open(QIODevice::WriteOnly);
156
157 //PNG ?
158 (qvariant_cast<QPixmap>(pxm)).save(&buffer, "PNG");
159 buffer.close();
160
161 return bArray;
162 }
163
164 QPixmap ImageManipulationDelegate::drawDefaultUserPixmap(const QSize& size, bool displayPresence, bool isPresent) {
Alexandre Lision4f264622016-05-08 17:08:56 -0400165
166 auto index = QStringLiteral("%1%2").arg(size.width()).arg(size.height());
167 if (m_hDefaultUserPixmap.contains(index)) {
168 return m_hDefaultUserPixmap.value(index);
169 }
170
Alexandre Lision7a166e42015-09-02 15:04:43 -0400171 // create the image somehow, load from file, draw into it...
Alexandre Lision4baba4c2016-02-11 13:00:57 -0500172 auto sourceImgRef = CGImageSourceCreateWithData((__bridge CFDataRef)[[NSImage imageNamed:@"default_user_icon"] TIFFRepresentation], NULL);
Alexandre Lision7a166e42015-09-02 15:04:43 -0400173 auto imgRef = CGImageSourceCreateImageAtIndex(sourceImgRef, 0, NULL);
Alexandre Lisionde0314b2015-09-02 15:45:21 -0400174 auto finalpxm = QtMac::fromCGImageRef(resizeCGImage(imgRef, size));
Alexandre Lision7a166e42015-09-02 15:04:43 -0400175 CFRelease(sourceImgRef);
176 CFRelease(imgRef);
Alexandre Lision3b0bd332015-03-15 18:43:07 -0400177
Alexandre Lision4f264622016-05-08 17:08:56 -0400178 m_hDefaultUserPixmap.insert(index, finalpxm);
179
Alexandre Lisiond5229f32015-11-16 11:17:41 -0500180 return finalpxm;
Alexandre Lision3b0bd332015-03-15 18:43:07 -0400181 }
Alexandre Lision7a166e42015-09-02 15:04:43 -0400182
183 CGImageRef ImageManipulationDelegate::resizeCGImage(CGImageRef image, const QSize& size) {
184 // create context, keeping original image properties
Alexandre Lision7a166e42015-09-02 15:04:43 -0400185 CGContextRef context = CGBitmapContextCreate(NULL, size.width(), size.height(),
186 CGImageGetBitsPerComponent(image),
Alexandre Lisiond5229f32015-11-16 11:17:41 -0500187 CGImageGetBytesPerRow(image),
188 CGImageGetColorSpace(image),
189 kCGImageAlphaPremultipliedLast);
Alexandre Lision7a166e42015-09-02 15:04:43 -0400190
191 if(context == NULL)
192 return nil;
193
194 // draw image to context (resizing it)
195 CGContextDrawImage(context, CGRectMake(0, 0, size.width(), size.height()), image);
196 // extract resulting image from context
197 CGImageRef imgRef = CGBitmapContextCreateImage(context);
198 CGContextRelease(context);
199
200 return imgRef;
201 }
202
203 QVariant
204 ImageManipulationDelegate::numberCategoryIcon(const QVariant& p, const QSize& size, bool displayPresence, bool isPresent)
205 {
206 Q_UNUSED(p)
207 Q_UNUSED(size)
208 Q_UNUSED(displayPresence)
209 Q_UNUSED(isPresent)
210 return QVariant();
211 }
212
213 QVariant
214 ImageManipulationDelegate::securityIssueIcon(const QModelIndex& index)
215 {
216 Q_UNUSED(index)
217 return QVariant();
218 }
219
220 QVariant
221 ImageManipulationDelegate::collectionIcon(const CollectionInterface* interface, PixmapManipulatorI::CollectionIconHint hint) const
222 {
223 Q_UNUSED(interface)
224 Q_UNUSED(hint)
225 return QVariant();
226 }
227 QVariant
228 ImageManipulationDelegate::securityLevelIcon(const SecurityEvaluationModel::SecurityLevel level) const
229 {
230 Q_UNUSED(level)
231 return QVariant();
232 }
233 QVariant
234 ImageManipulationDelegate::historySortingCategoryIcon(const CategorizedHistoryModel::SortedProxy::Categories cat) const
235 {
236 Q_UNUSED(cat)
237 return QVariant();
238 }
239 QVariant
240 ImageManipulationDelegate::contactSortingCategoryIcon(const CategorizedContactModel::SortedProxy::Categories cat) const
241 {
242 Q_UNUSED(cat)
243 return QVariant();
244 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400245
Alexandre Lision7a166e42015-09-02 15:04:43 -0400246 QVariant
247 ImageManipulationDelegate::userActionIcon(const UserActionElement& state) const
248 {
249 Q_UNUSED(state)
250 return QVariant();
Alexandre Lision3b0bd332015-03-15 18:43:07 -0400251 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400252
Stepan Salenikovich3a6ea302016-01-08 13:54:34 -0500253 QVariant ImageManipulationDelegate::decorationRole(const QModelIndex& index)
254 {
255 Q_UNUSED(index)
256 return QVariant();
257 }
258
259 QVariant ImageManipulationDelegate::decorationRole(const Call* c)
260 {
Alexandre Lision43e91bc2016-04-19 18:04:52 -0400261 if (c && c->peerContactMethod()
262 && c->peerContactMethod()->contact()) {
263 return contactPhoto(c->peerContactMethod()->contact(), decorationSize);
264 } else
265 return drawDefaultUserPixmap(decorationSize);
Stepan Salenikovich3a6ea302016-01-08 13:54:34 -0500266 }
267
268 QVariant ImageManipulationDelegate::decorationRole(const ContactMethod* cm)
269 {
Alexandre Lision43e91bc2016-04-19 18:04:52 -0400270 QImage photo;
271 if (cm && cm->contact() && cm->contact()->photo().isValid())
272 return contactPhoto(cm->contact(), decorationSize);
273 else
274 return drawDefaultUserPixmap(decorationSize);
Stepan Salenikovich3a6ea302016-01-08 13:54:34 -0500275 }
276
277 QVariant ImageManipulationDelegate::decorationRole(const Person* p)
278 {
Alexandre Lision43e91bc2016-04-19 18:04:52 -0400279 return contactPhoto(const_cast<Person*>(p), decorationSize);
280 }
281
282 QVariant ImageManipulationDelegate::decorationRole(const Account* acc)
283 {
284 Q_UNUSED(acc)
285 if (auto pro = ProfileModel::instance().selectedProfile())
286 return contactPhoto(pro->person(), decorationSize);
287 return drawDefaultUserPixmap(decorationSize);
Stepan Salenikovich3a6ea302016-01-08 13:54:34 -0500288 }
289
Alexandre Lision7a166e42015-09-02 15:04:43 -0400290} // namespace Interfaces