Commit 072291f0 authored by Andreas Traczyk's avatar Andreas Traczyk

avatars: refactor avatar generation

- removes old default avatar
- removes the '?' avatars
- corrects color generation

Change-Id: I722715aaa7657566ecc525dacd74c62e3712f1fa
parent 3f9c6602
......@@ -426,7 +426,7 @@ CallWidget::callChangedSlot()
ui->callerPhoto->setPixmap(
QPixmap::fromImage(
GlobalInstances::pixmapManipulator()
.callPhoto(actualCall_, QSize(130,130)).value<QImage>()));
.callPhoto(actualCall_, QSize(128, 128)).value<QImage>()));
ui->callerBestIdLabel->setText(actualCall_->peerContactMethod()->bestId());
if(actualCall_->state() == Call::State::OVER || actualCall_->state() == Call::State::FAILURE){
......
......@@ -1433,7 +1433,7 @@ Copy and share it with your friends!</string>
<string/>
</property>
<property name="pixmap">
<pixmap resource="ressources.qrc">:/images/user/btn-default-userpic.svg</pixmap>
<pixmap resource="ressources.qrc">:/images/default-avatar-overlay.svg</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
......@@ -1657,7 +1657,7 @@ Copy and share it with your friends!</string>
<string/>
</property>
<property name="pixmap">
<pixmap resource="ressources.qrc">:/images/user/btn-default-userpic.svg</pixmap>
<pixmap resource="ressources.qrc">:/images/default-avatar-overlay.svg</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
......
......@@ -292,7 +292,7 @@
</property>
<property name="icon">
<iconset resource="ressources.qrc">
<normaloff>:/images/user/btn-default-userpic.svg</normaloff>:/images/user/btn-default-userpic.svg</iconset>
<normaloff>:/images/default-avatar-overlay.svg</normaloff>:/images/default-avatar-overlay.svg</iconset>
</property>
<property name="iconSize">
<size>
......
......@@ -69,7 +69,7 @@
<string/>
</property>
<property name="pixmap">
<pixmap resource="ressources.qrc">:/images/user/btn-default-userpic.svg</pixmap>
<pixmap resource="ressources.qrc">:/images/default-avatar-overlay.svg</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
......
......@@ -27,7 +27,7 @@
#include "profile.h"
#include "person.h"
#include "utils.h"
#include "globalinstances.h"
CurrentAccountWidget::CurrentAccountWidget(QWidget *parent) :
QWidget(parent),
......@@ -83,18 +83,16 @@ CurrentAccountWidget::updateAccounts()
}
}
void
CurrentAccountWidget::setPhoto()
{
auto selector = ui->currentAccountSelector;
if (selector->count() > 0) {
if (ProfileModel::instance().selectedProfile()) {
if (ProfileModel::instance().selectedProfile()->person()){
QImage img = Utils::getCirclePhoto(ProfileModel::instance().selectedProfile()->person()->photo().value<QImage>(),
ui->idDisplayLayout->contentsRect().height());
ui->currentAccountPixmap->setPixmap(QPixmap::fromImage(img));
if (auto p = ProfileModel::instance().selectedProfile()->person()) {
QVariant avatarImage = ProfileModel::instance().selectedProfile()->person()->roleData(Qt::DecorationRole);
QImage image = Utils::getCirclePhoto(avatarImage.value<QImage>(), ui->idDisplayLayout->contentsRect().height());
ui->currentAccountPixmap->setPixmap(QPixmap::fromImage(image));
qDebug() << "CurrentAccount : Photo set";
} else
qDebug() << "CurrentAccount : selected profile has no person";
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="256"
height="256"
viewBox="0 0 67.733332 67.733335"
version="1.1"
id="svg8"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
sodipodi:docname="default-avatar-overlay.svg">
<defs
id="defs2">
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath4585">
<use
x="0"
y="0"
xlink:href="#g4581"
id="use4587"
width="100%"
height="100%" />
</clipPath>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35355339"
inkscape:cx="-26.542885"
inkscape:cy="616.24668"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="true"
showguides="false"
inkscape:window-width="766"
inkscape:window-height="802"
inkscape:window-x="761"
inkscape:window-y="1080"
inkscape:window-maximized="1"
objecttolerance="10000"
gridtolerance="10000"
guidetolerance="10000"
inkscape:snap-perpendicular="true"
inkscape:snap-path-clip="true"
inkscape:snap-path-mask="true"
inkscape:snap-tangential="true"
units="px">
<inkscape:grid
type="xygrid"
id="grid49"
dotted="false" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-229.26664)">
<g
id="g4583"
clip-path="url(#clipPath4585)"
transform="translate(0,1.361667)"
style="fill:#ffffff;fill-opacity:1">
<g
inkscape:label="Clip"
id="g4581"
style="fill:#ffffff;fill-opacity:1">
<circle
r="11.287793"
style="fill:#ffffff;fill-opacity:1;stroke-width:0.907561"
cy="247.00398"
cx="33.866665"
id="path47" />
<path
inkscape:connector-curvature="0"
id="path47-4"
d="m 33.866661,265.24997 a 21.903016,11.287794 0 0 0 -21.89152,11.16564 28.448986,31.582552 0 0 0 21.279011,11.39488 21.903016,11.287794 0 0 0 0.612509,0.0166 21.903016,11.287794 0 0 0 0.763088,-0.0133 28.448986,31.582552 0 0 0 21.128443,-11.42739 21.903016,11.287794 0 0 0 -21.891531,-11.13482 z"
style="fill:#ffffff;fill-opacity:1;stroke-width:1.26422167" />
</g>
</g>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
<g>
<path fill="#00BFD3" d="M49.81,84.928c-5.336-5.854-8.708-14.609-9.971-22.167c-0.981,0.68-1.808,1.146-2.358,1.263
c-3.659-25.575,10.126-29.569,10.126-29.569c0.707-0.363,1.764-0.459,2.943-0.338l1.146-2.418c2.772-2.945,14.63-8.364,26.811,3.76
c7.175,7.143,16.37,4.827,17.756,1.362c-1.482,7.576-4.321,12.391-9.419,15.16c0.29,1.529,0.449,3.076,0.445,4.614
c-0.014,8.355-3.643,20.679-10.466,28.236c13.357,3.933,25.328,13.502,30.37,26.391C119.979,99.52,128.002,82.697,128.002,64
c0-35.346-28.656-64-64.003-64S-0.002,28.654-0.002,64c0,18.328,7.717,34.845,20.065,46.513
C25.217,98.087,36.833,88.846,49.81,84.928z"/>
<path fill="#FFFFFF" d="M107.193,111.221c-5.042-12.889-17.013-22.458-30.37-26.391c6.823-7.558,10.452-19.881,10.466-28.236
c0.004-1.538-0.155-3.084-0.445-4.614c5.098-2.769,7.937-7.583,9.419-15.16c-1.386,3.465-10.581,5.781-17.756-1.362
c-12.181-12.125-24.039-6.705-26.811-3.76l-1.146,2.418c-1.179-0.121-2.236-0.025-2.943,0.338c0,0-13.785,3.994-10.126,29.569
c0.55-0.117,1.377-0.583,2.358-1.263c1.263,7.558,4.635,16.312,9.971,22.167c-12.977,3.918-24.593,13.159-29.747,25.585
C31.527,121.346,46.981,128,63.999,128C80.647,128,95.809,121.64,107.193,111.221z"/>
</g>
</svg>
......@@ -3,6 +3,7 @@
* Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>*
* Author: Anthony Léonard <anthony.leonard@savoirfairelinux.com> *
* Author: Olivier Soldano <olivier.soldano@savoirfairelinux.com> *
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
......@@ -27,6 +28,7 @@
#include <QByteArray>
#include <QBuffer>
#include <QPainter>
#include <QCryptographicHash>
#include "person.h"
#include "call.h"
......@@ -38,7 +40,22 @@
#include "ringthemeutils.h"
#undef interface
static const QSize IMAGE_SIZE {48, 48};
static const QSize IMAGE_SIZE {128, 128};
QColor
getAvatarColor(const QString& canonicalUri) {
if (canonicalUri.isEmpty()) {
return RingTheme::defaultAvatarColor_;
}
auto h = QString(QCryptographicHash::hash(canonicalUri.toLocal8Bit(), QCryptographicHash::Md5).toHex());
if (h.isEmpty() || h.isNull()) {
return RingTheme::defaultAvatarColor_;
}
uint8_t colorsLength = sizeof(RingTheme::avatarColors_) / sizeof(QColor);
bool ok;
auto colorIndex = QString(h.at(0)).toUInt(&ok, colorsLength);
return RingTheme::avatarColors_[colorIndex];
}
//
// Generate a QImage representing a dummy user avatar, when user doesn't provide it.
......@@ -49,46 +66,61 @@ static const QSize IMAGE_SIZE {48, 48};
// \param letter centerer letter
//
static QImage
fallbackAvatar(const QSize size, const char color, const char letter)
fallbackAvatar(const QSize size, const QString& canonicalUriStr, const QString& letterStr = QString())
{
// We start with a transparent avatar
QImage avatar(size, QImage::Format_ARGB32);
avatar.fill(Qt::transparent);
// We pick a color based on the passed character
QColor avColor = RingTheme::avatarColors_[color % 16];
QColor avColor = getAvatarColor(canonicalUriStr);
// We draw a circle with this color
QPainter painter(&avatar);
painter.setRenderHints(QPainter::Antialiasing|QPainter::SmoothPixmapTransform);
painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
painter.setPen(Qt::transparent);
painter.setBrush(avColor);
painter.setBrush(avColor.lighter(110));
painter.drawEllipse(avatar.rect());
// Then we paint a letter in the circle
QFont segoeFont("Segoe UI", avatar.height()/2); // We use Segoe UI as recommended by Windows guidelines
painter.setFont(segoeFont);
painter.setPen(Qt::white);
QRect textRect = avatar.rect();
textRect.moveTop(textRect.top()-(avatar.height()/20)); // Empirical value that seems to correct centering nicely
painter.drawText(textRect, QString(letter), QTextOption(Qt::AlignCenter));
// If a letter was passed, then we paint a letter in the circle,
// otherwise we draw the default avatar icon
if (!letterStr.isEmpty()) {
auto letter = letterStr.at(0).toUpper().toLatin1();
QFont font("Arial", avatar.height() / 2.66667, QFont::Medium);
painter.setFont(font);
painter.setPen(Qt::white);
painter.drawText(avatar.rect(), QString(letter), QTextOption(Qt::AlignCenter));
} else {
QRect overlayRect = avatar.rect();
qreal margin = (0.05 * overlayRect.width());
overlayRect.moveLeft(overlayRect.left() + margin * 0.5);
overlayRect.moveTop(overlayRect.top() + margin * 0.5);
overlayRect.setWidth(overlayRect.width() - margin);
overlayRect.setHeight(overlayRect.height() - margin);
painter.drawPixmap(overlayRect, QPixmap(":/images/default-avatar-overlay.svg"));
}
return avatar;
}
//
// Alias on fallbackAvatar
//
// \param color a QString where the first character is converted to latin1 and used as color
// \param string a QString where the first character is converted to uppercase-latin1 and used as letter
//
static inline QImage
fallbackAvatar(const QSize size, const QString& color_str, const QString& letter_str)
QImage
fallbackAvatar
(const QSize size, const ContactMethod* cm)
{
return fallbackAvatar(size, color_str.at(0).toLatin1(), letter_str.at(0).toUpper().toLatin1());
if (cm == nullptr) {
return QImage();
}
QImage image;
auto letterStr = QString();
if (cm->uri().userinfo() != cm->bestName()) {
letterStr = cm->bestName();
}
image = fallbackAvatar( size,
cm->uri().full(),
letterStr);
return image;
}
/*Namespace Interfaces collide with QBuffer*/
QByteArray QImageToByteArray(QImage image)
{
......@@ -110,40 +142,31 @@ PixbufManipulator::callPhoto(Call* c, const QSize& size, bool displayPresence)
{
if (!c || c->type() == Call::Type::CONFERENCE){
return QVariant::fromValue(fallbackAvatar(size,
c->peerContactMethod()->uri().userinfo(),
c->peerContactMethod()->uri().full(),
c->peerContactMethod()->bestName()));
}
return callPhoto(c->peerContactMethod(), size, displayPresence);
}
QVariant
PixbufManipulator::callPhoto(const ContactMethod* n, const QSize& size, bool displayPresence)
PixbufManipulator::callPhoto(const ContactMethod* cm, const QSize& size, bool displayPresence)
{
if (n && n->contact()) {
return contactPhoto(n->contact(), size, displayPresence);
if (cm && cm->contact()) {
return contactPhoto(cm->contact(), size, displayPresence);
} else {
return QVariant::fromValue(fallbackAvatar(size, n->uri().userinfo(), n->bestName()));
return QVariant::fromValue(fallbackAvatar(size, cm));
}
}
QVariant
PixbufManipulator::contactPhoto(Person* c, const QSize& size, bool displayPresence)
PixbufManipulator::contactPhoto(Person* p, const QSize& size, bool displayPresence)
{
Q_UNUSED(displayPresence);
/**
* try to get the photo
* otherwise use the fallback avatar
*/
QImage photo;
if (c->photo().isValid()){
photo = c->photo().value<QImage>();
if (p->photo().isValid()) {
photo = p->photo().value<QImage>();
} else {
photo = fallbackAvatar(size,
c->phoneNumbers().at(0)->uri().userinfo(),
c->phoneNumbers().at(0)->bestName());
photo = fallbackAvatar(IMAGE_SIZE, p->phoneNumbers().at(0));
}
return QVariant::fromValue(scaleAndFrame(photo, size));
}
......@@ -155,7 +178,7 @@ QVariant PixbufManipulator::personPhoto(const QByteArray& data, const QString& t
const char* c_str2 = ba.data();
if (avatar.loadFromData(data.fromBase64(data), c_str2))
return Utils::getCirclePhoto(avatar, avatar.size().width());
return fallbackAvatar(IMAGE_SIZE, '?', '?');
return fallbackAvatar(IMAGE_SIZE, QString());
}
QVariant
......@@ -175,8 +198,6 @@ PixbufManipulator::securityIssueIcon(const QModelIndex& index)
return QVariant();
}
QByteArray
PixbufManipulator::toByteArray(const QVariant& pxm)
{
......@@ -226,43 +247,36 @@ QVariant PixbufManipulator::decorationRole(const QModelIndex& index)
QVariant PixbufManipulator::decorationRole(const Call* c)
{
QImage photo;
if (c && c->peerContactMethod()
if (c && c->peerContactMethod()
&& c->peerContactMethod()->contact()
&& c->peerContactMethod()->contact()->photo().isValid()) {
photo = c->peerContactMethod()->contact()->photo().value<QImage>();
} else {
fallbackAvatar(IMAGE_SIZE, c->peerContactMethod());
}
else
photo = fallbackAvatar(IMAGE_SIZE,
c->peerContactMethod()->uri().userinfo(),
c->peerContactMethod()->bestName());
return QVariant::fromValue(scaleAndFrame(photo, IMAGE_SIZE));
}
QVariant PixbufManipulator::decorationRole(const ContactMethod* cm)
{
QImage photo;
if (cm && cm->contact() && cm->contact()->photo().isValid())
if (cm && cm->contact()
&& cm->contact()->photo().isValid()) {
photo = cm->contact()->photo().value<QImage>();
else if (cm){
photo = fallbackAvatar(IMAGE_SIZE,
cm->uri().userinfo(),
cm->bestName());
} else {
photo = fallbackAvatar(IMAGE_SIZE, '?', '?');
photo = fallbackAvatar(IMAGE_SIZE, cm);
}
return QVariant::fromValue(scaleAndFrame(photo, IMAGE_SIZE));
}
QVariant PixbufManipulator::decorationRole(const Person* p)
{
QImage photo;
if (p && p->photo().isValid())
if (p && p->photo().isValid()) {
photo = p->photo().value<QImage>();
else
photo = fallbackAvatar(IMAGE_SIZE,
p->phoneNumbers().at(0)->uri().userinfo(),
p->phoneNumbers().at(0)->bestName());
} else {
photo = fallbackAvatar(IMAGE_SIZE, p->phoneNumbers().at(0));
}
return QVariant::fromValue(scaleAndFrame(photo, IMAGE_SIZE));
}
......
......@@ -3,7 +3,6 @@
<file>stylesheet.css</file>
<file>images/ring.png</file>
<file>images/logo-ring-standard-coul.png</file>
<file>images/user/btn-default-userpic.svg</file>
<file>images/icons/ic_arrow_back_white_24dp.png</file>
<file>images/icons/ic_mic_off_white_24dp.png</file>
<file>images/icons/ic_person_add_white_24dp.png</file>
......@@ -44,5 +43,6 @@
<file>images/icons/ic_chat_black_24dp_2x.png</file>
<file>images/icons/ic_person_add_black_24dp_2x.png</file>
<file>images/waiting.gif</file>
<file>images/default-avatar-overlay.svg</file>
</qresource>
</RCC>
......@@ -34,6 +34,7 @@ static const QColor green_ {127, 255, 0};
static const QColor smartlistSelection_ { 237, 237, 237 };
static const QColor smartlistHighlight_ { 242, 242, 242 };
static const QColor defaultAvatarColor_ = { "#ff9e9e9e" }; //Grey
static const QColor avatarColors_[] {
{"#fff44336"}, //Red
{"#ffe91e63"}, //Pink
......
......@@ -77,7 +77,8 @@ SmartListDelegate::paint(QPainter* painter
QRect rectAvatar(16 + rect.left(), rect.top() + dy_, sizeImage_, sizeImage_);
drawDecoration(painter, opt, rectAvatar,
QPixmap::fromImage(index.data(Qt::DecorationRole).value<QImage>()));
QPixmap::fromImage(index.data(Qt::DecorationRole).value<QImage>())
.scaled(sizeImage_, sizeImage_, Qt::KeepAspectRatio, Qt::SmoothTransformation));
QFont font(painter->font());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment