mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
feat(identicon): add algorithm to create identicons
ported from https://github.com/sudden6/rust-identicon/blob/master/src/lib.rs but slightly changed the algorithm
This commit is contained in:
parent
68e0b462fc
commit
61b36d1bce
|
@ -413,6 +413,8 @@ set(${PROJECT_NAME}_SOURCES
|
|||
src/widget/tool/flyoutoverlaywidget.h
|
||||
src/widget/tool/friendrequestdialog.cpp
|
||||
src/widget/tool/friendrequestdialog.h
|
||||
src/widget/tool/identicon.cpp
|
||||
src/widget/tool/identicon.h
|
||||
src/widget/tool/movablewidget.cpp
|
||||
src/widget/tool/movablewidget.h
|
||||
src/widget/tool/profileimporter.cpp
|
||||
|
|
143
src/widget/tool/identicon.cpp
Normal file
143
src/widget/tool/identicon.cpp
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
Copyright © 2017 by The qTox Project Contributors
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
qTox is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
qTox is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "identicon.h"
|
||||
#include "src/core/toxpk.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <QColor>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDebug>
|
||||
#include <QImage>
|
||||
|
||||
// The following constants change the appearance of the identicon
|
||||
// they have been choosen by trying to make the output look nice.
|
||||
/**
|
||||
* @var Identicon::IDENTICON_COLOR_BYTES
|
||||
* Specifies how many bytes should define the foreground color
|
||||
* must be smaller than 8, else there'll be overflows
|
||||
*/
|
||||
|
||||
/**
|
||||
* @var Identicon::COLORS
|
||||
* Number of colors to use for the identicon
|
||||
*/
|
||||
|
||||
/**
|
||||
* @var Identicon::IDENTICON_ROWS
|
||||
* Specifies how many rows of blocks the identicon should have
|
||||
*/
|
||||
|
||||
/**
|
||||
* @var Identicon::ACTIVE_COLS
|
||||
* Width from the center to the outside, for 5 columns it's 3, 6 -> 3, 7 -> 4
|
||||
*/
|
||||
|
||||
/**
|
||||
* @var Identicon::HASH_MIN_LEN
|
||||
* Min length of the hash in bytes, 7 bytes control the color,
|
||||
* the rest controls the pixel placement
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Creates an Identicon, that visualizes a hash in graphical form.
|
||||
* @param data Data to visualize
|
||||
*/
|
||||
Identicon::Identicon(const QByteArray& data)
|
||||
{
|
||||
static_assert(Identicon::COLORS == 2, "Only two colors are implemented");
|
||||
// hash with sha256
|
||||
QByteArray hash = QCryptographicHash::hash(data, QCryptographicHash::Sha256);
|
||||
for (int colorIndex = 0; colorIndex < COLORS; ++colorIndex) {
|
||||
const QByteArray hashPart = hash.right(IDENTICON_COLOR_BYTES);
|
||||
hash.truncate(hash.length() - IDENTICON_COLOR_BYTES);
|
||||
|
||||
const qreal hue = bytesToColor(hashPart);
|
||||
// change offset when COLORS != 2
|
||||
const qreal lig = static_cast<qreal>(colorIndex) / COLORS + 0.3;
|
||||
const qreal sat = 0.5;
|
||||
colors[colorIndex].setHslF(hue, sat, lig);
|
||||
}
|
||||
|
||||
const uint8_t* const hashBytes = reinterpret_cast<const uint8_t*>(hash.constData());
|
||||
// compute the block colors from the hash
|
||||
for (int row = 0; row < IDENTICON_ROWS; ++row) {
|
||||
for (int col = 0; col < ACTIVE_COLS; ++col) {
|
||||
const int hashIdx = row * ACTIVE_COLS + col;
|
||||
const uint8_t colorIndex = hashBytes[hashIdx] % COLORS;
|
||||
identiconColors[row][col] = colorIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts a series of IDENTICON_COLOR_BYTES bytes to a value in the range 0.0..1.0
|
||||
* @param bytes Bytes to convert to a color
|
||||
* @return Value in the range of 0.0..1.0
|
||||
*/
|
||||
float Identicon::bytesToColor(QByteArray bytes)
|
||||
{
|
||||
static_assert(IDENTICON_COLOR_BYTES <= 8, "IDENTICON_COLOR max value is 8");
|
||||
const uint8_t* const bytesChr = reinterpret_cast<const uint8_t*>(bytes.constData());
|
||||
assert(bytes.length() == IDENTICON_COLOR_BYTES);
|
||||
|
||||
// get foreground color
|
||||
uint64_t hue = bytesChr[0];
|
||||
|
||||
// convert the last bytes to an uint
|
||||
for (int i = 1; i < IDENTICON_COLOR_BYTES; ++i) {
|
||||
hue = hue << 8;
|
||||
hue += bytesChr[i];
|
||||
}
|
||||
|
||||
// normalize to 0.0 ... 1.0
|
||||
return (static_cast<float>(hue))
|
||||
/ (((static_cast<uint64_t>(1)) << (8 * IDENTICON_COLOR_BYTES)) - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes the Identicon to a QImage
|
||||
* @param scaleFactor the image will be a square with scaleFactor * IDENTICON_ROWS pixels,
|
||||
* must be >= 1
|
||||
* @return a QImage with the identicon
|
||||
*/
|
||||
QImage Identicon::toImage(int scaleFactor)
|
||||
{
|
||||
if (scaleFactor < 1) {
|
||||
qDebug() << "Can't scale with values <1, clamping to 1";
|
||||
scaleFactor = 1;
|
||||
}
|
||||
|
||||
scaleFactor *= IDENTICON_ROWS;
|
||||
|
||||
QImage pixels(IDENTICON_ROWS, IDENTICON_ROWS, QImage::Format_RGB888);
|
||||
|
||||
for (int row = 0; row < IDENTICON_ROWS; ++row) {
|
||||
for (int col = 0; col < IDENTICON_ROWS; ++col) {
|
||||
// mirror on vertical axis
|
||||
const int columnIdx = abs((col * 2 - (IDENTICON_ROWS - 1)) / 2);
|
||||
const int colorIdx = identiconColors[row][columnIdx];
|
||||
pixels.setPixel(col, row, colors[colorIdx].rgb());
|
||||
}
|
||||
}
|
||||
|
||||
// scale up without smoothing to make it look sharp
|
||||
return pixels.scaled(scaleFactor, scaleFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
|
||||
}
|
30
src/widget/tool/identicon.h
Normal file
30
src/widget/tool/identicon.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef IDENTICON_H
|
||||
#define IDENTICON_H
|
||||
|
||||
#include <QColor>
|
||||
#include <QImage>
|
||||
|
||||
class Identicon
|
||||
{
|
||||
public:
|
||||
Identicon(const QByteArray& data);
|
||||
QImage toImage(int scaleFactor = 1);
|
||||
|
||||
public:
|
||||
static constexpr int IDENTICON_ROWS = 5;
|
||||
|
||||
private:
|
||||
float bytesToColor(QByteArray bytes);
|
||||
|
||||
private:
|
||||
static constexpr int COLORS = 2;
|
||||
static constexpr int ACTIVE_COLS = (IDENTICON_ROWS + 1) / 2;
|
||||
static constexpr int IDENTICON_COLOR_BYTES = 6;
|
||||
static constexpr int HASH_MIN_LEN = ACTIVE_COLS * IDENTICON_ROWS
|
||||
+ COLORS * IDENTICON_COLOR_BYTES;
|
||||
|
||||
uint8_t identiconColors[IDENTICON_ROWS][ACTIVE_COLS];
|
||||
QColor colors[COLORS];
|
||||
};
|
||||
|
||||
#endif // IDENTICON_H
|
Loading…
Reference in New Issue
Block a user