1 /*
2    Drawpile - a collaborative drawing program.
3 
4    Copyright (C) 2018 Calle Laakkonen
5 
6    Drawpile is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation, either version 3 of the License, or
9    (at your option) any later version.
10 
11    Drawpile is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with Drawpile.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "identicon.h"
21 
22 #include <QPainter>
23 #include <QCryptographicHash>
24 #include <QtEndian>
25 #include <QtMath>
26 
27 namespace {
28 	class HashRand {
29 	public:
HashRand(const QByteArray & seed)30 		HashRand(const QByteArray &seed)
31 			: m_pos(0)
32 		{
33 			m_seed = QCryptographicHash::hash(seed, QCryptographicHash::Sha256);
34 		}
35 
operator ()(unsigned int bound)36 		unsigned int operator()(unsigned int bound)
37 		{
38 			if(m_pos+4 > m_seed.length()) {
39 				m_pos = 0;
40 				m_seed = QCryptographicHash::hash(m_seed, QCryptographicHash::Sha256);
41 			} else {
42 				m_pos += 4;
43 			}
44 
45 			return qFromBigEndian<quint32>(m_seed.constData()+m_pos) % bound;
46 		}
47 
48 	private:
49 		QByteArray m_seed;
50 		int m_pos;
51 	};
52 }
53 
make_identicon(const QString & name,const QSize & size)54 QImage make_identicon(const QString &name, const QSize &size)
55 {
56 	HashRand rand {name.toUtf8()};
57 
58 	QImage image(size, QImage::Format_ARGB32_Premultiplied);
59 	image.fill(QColor::fromHsl(rand(360), 255, 120));
60 
61 	// Draw the decorative elements
62 	QPainter painter(&image);
63 	painter.setRenderHint(QPainter::Antialiasing);
64 	painter.setPen(Qt::NoPen);
65 	const double offset = rand(180) / M_PI;
66 	for(int i=0;i<3;++i) {
67 		const QPointF c { size.width() / 2.0, size.height() / 2.0 };
68 		const double r = size.width() * 2;
69 		painter.setBrush(QColor::fromHsl(rand(360), 255, 180, 255/2));
70 		painter.drawEllipse(QRectF(
71 			c.x() + sin(offset + i/3.0 * M_PI*2) * r - r,
72 			c.y() + cos(offset + i/3.0 * M_PI*2) * r - r,
73 			r*2,
74 			r*2
75 		));
76 	}
77 
78 	// Draw the name's first letter
79 	QFont font;
80 	font.setPixelSize(size.height() * 0.7);
81 	painter.setFont(font);
82 	painter.setPen(Qt::white);
83 	painter.drawText(QRect(QPoint(), size), Qt::AlignCenter, name.left(1));
84 
85 	// Draw the circular mask
86 	QImage mask(size, QImage::Format_ARGB32_Premultiplied);
87 	mask.fill(0);
88 	QPainter maskPainter(&mask);
89 	maskPainter.setRenderHint(QPainter::Antialiasing);
90 	maskPainter.setBrush(Qt::white);
91 	maskPainter.drawEllipse(1, 1, size.width()-2, size.height()-2);
92 
93 	painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
94 	painter.drawImage(0, 0, mask);
95 
96 	return image;
97 }
98 
99