diff --git a/retroshare-gui/src/gui/gxs/GxsIdDetails.cpp b/retroshare-gui/src/gui/gxs/GxsIdDetails.cpp index e4ccf4dfd..41cdb6299 100644 --- a/retroshare-gui/src/gui/gxs/GxsIdDetails.cpp +++ b/retroshare-gui/src/gui/gxs/GxsIdDetails.cpp @@ -257,6 +257,87 @@ static bool findTagIcon(int tag_class, int /*tag_type*/, QIcon &icon) return true; } +//QImage GxsIdDetails::makeDefaultIcon(const RsGxsId& id) +//{ +// static std::map image_cache ; +// +// std::map::const_iterator it = image_cache.find(id) ; +// +// if(it != image_cache.end()) +// return it->second ; +// +// int S = 128 ; +// QImage pix(S,S,QImage::Format_RGB32) ; +// +// uint64_t n = reinterpret_cast(id.toByteArray())[0] ; +// +// uint8_t a[8] ; +// for(int i=0;i<8;++i) +// { +// a[i] = n&0xff ; +// n >>= 8 ; +// } +// QColor val[16] = { +// QColor::fromRgb( 255, 110, 180), +// QColor::fromRgb( 238, 92, 66), +// QColor::fromRgb( 255, 127, 36), +// QColor::fromRgb( 255, 193, 193), +// QColor::fromRgb( 127, 255, 212), +// QColor::fromRgb( 0, 255, 255), +// QColor::fromRgb( 224, 255, 255), +// QColor::fromRgb( 199, 21, 133), +// QColor::fromRgb( 50, 205, 50), +// QColor::fromRgb( 107, 142, 35), +// QColor::fromRgb( 30, 144, 255), +// QColor::fromRgb( 95, 158, 160), +// QColor::fromRgb( 143, 188, 143), +// QColor::fromRgb( 233, 150, 122), +// QColor::fromRgb( 151, 255, 255), +// QColor::fromRgb( 162, 205, 90), +// }; +// +// int c1 = (a[0]^a[1]) & 0xf ; +// int c2 = (a[1]^a[2]) & 0xf ; +// int c3 = (a[2]^a[3]) & 0xf ; +// int c4 = (a[3]^a[4]) & 0xf ; +// +// for(int i=0;i> 4) + sin( (2*M_PI*i/(float)S) * k1 * f) * cos( (2*M_PI*j/(float)S) * k2 * f) * (a[k1+k2] >> 4) ; +// res2 += cos( (2*M_PI*i/(float)S) * k2 * f) * (a[k1+2] & 0xf) + sin( (2*M_PI*j/(float)S) * k1 * f) * (a[k2+1] >> 4) + sin( (2*M_PI*i/(float)S) * k2 * f) * cos( (2*M_PI*j/(float)S) * k1 * f) * (a[k1^k2] >> 4) ; +// } +// +// uint32_t q = 0 ; +// if(res1 >= 0.0f) q += val[c1].rgb() ; else q += val[c2].rgb() ; +// if(res2 >= 0.0f) q += val[c3].rgb() ; else q += val[c4].rgb() ; +// +// pix.setPixel( i, j, q) ; +// pix.setPixel( S-1-i, j, q) ; +// pix.setPixel( S-1-i, S-1-j, q) ; +// pix.setPixel( i, S-1-j, q) ; +// } +// +// image_cache[id] = pix.scaled(128,128,Qt::KeepAspectRatio,Qt::SmoothTransformation) ; +// +// return image_cache[id] ; +//} + +/** + * @brief GxsIdDetails::makeIdentIcon + * @param id: RsGxsId to compute + * @return QImage representing an IndentIcon cf: + * http://en.wikipedia.org/wiki/Identicon + * Bring the source code from this adaptation: + * http://francisshanahan.com/identicon5/test.html + */ QImage GxsIdDetails::makeDefaultIcon(const RsGxsId& id) { static std::map image_cache ; @@ -266,70 +347,392 @@ QImage GxsIdDetails::makeDefaultIcon(const RsGxsId& id) if(it != image_cache.end()) return it->second ; - int S = 128 ; - QImage pix(S,S,QImage::Format_RGB32) ; - - uint64_t n = reinterpret_cast(id.toByteArray())[0] ; - - uint8_t a[8] ; - for(int i=0;i<8;++i) - { - a[i] = n&0xff ; - n >>= 8 ; - } - QColor val[16] = { - QColor::fromRgb( 255, 110, 180), - QColor::fromRgb( 238, 92, 66), - QColor::fromRgb( 255, 127, 36), - QColor::fromRgb( 255, 193, 193), - QColor::fromRgb( 127, 255, 212), - QColor::fromRgb( 0, 255, 255), - QColor::fromRgb( 224, 255, 255), - QColor::fromRgb( 199, 21, 133), - QColor::fromRgb( 50, 205, 50), - QColor::fromRgb( 107, 142, 35), - QColor::fromRgb( 30, 144, 255), - QColor::fromRgb( 95, 158, 160), - QColor::fromRgb( 143, 188, 143), - QColor::fromRgb( 233, 150, 122), - QColor::fromRgb( 151, 255, 255), - QColor::fromRgb( 162, 205, 90), - }; - - int c1 = (a[0]^a[1]) & 0xf ; - int c2 = (a[1]^a[2]) & 0xf ; - int c3 = (a[2]^a[3]) & 0xf ; - int c4 = (a[3]^a[4]) & 0xf ; - - for(int i=0;i> 4) + sin( (2*M_PI*i/(float)S) * k1 * f) * cos( (2*M_PI*j/(float)S) * k2 * f) * (a[k1+k2] >> 4) ; - res2 += cos( (2*M_PI*i/(float)S) * k2 * f) * (a[k1+2] & 0xf) + sin( (2*M_PI*j/(float)S) * k1 * f) * (a[k2+1] >> 4) + sin( (2*M_PI*i/(float)S) * k2 * f) * cos( (2*M_PI*j/(float)S) * k1 * f) * (a[k1^k2] >> 4) ; - } - - uint32_t q = 0 ; - if(res1 >= 0.0f) q += val[c1].rgb() ; else q += val[c2].rgb() ; - if(res2 >= 0.0f) q += val[c3].rgb() ; else q += val[c4].rgb() ; - - pix.setPixel( i, j, q) ; - pix.setPixel( S-1-i, j, q) ; - pix.setPixel( S-1-i, S-1-j, q) ; - pix.setPixel( i, S-1-j, q) ; - } - - image_cache[id] = pix.scaled(128,128,Qt::KeepAspectRatio,Qt::SmoothTransformation) ; + image_cache[id] = drawIdentIcon(QString::fromStdString(id.toStdString()),64*3, true); return image_cache[id] ; } +/** + * @brief GxsIdDetails::getSprite + * @param shapeType: type of shape (0 to 15) + * @param size: Size for shape + * @return QList for all point for path drawing (shape) + */ +QList GxsIdDetails::getSprite(quint8 shapeType, quint16 size) + { + QList sprite; + switch (shapeType) { + case 0: // Triangle + sprite.append(0.5);sprite.append(1.0); + sprite.append(1.0);sprite.append(0.0); + sprite.append(1.0);sprite.append(1.0); + break; + case 1: // Parallelogram + sprite.append(0.5);sprite.append(0.0); + sprite.append(1.0);sprite.append(0.0); + sprite.append(0.5);sprite.append(1.0); + sprite.append(0.0);sprite.append(1.0); + break; + case 2: // mouse ears + sprite.append(0.5);sprite.append(0); + sprite.append(1);sprite.append(0); + sprite.append(1);sprite.append(1); + sprite.append(0.5);sprite.append(1); + sprite.append(1);sprite.append(0.5); + break; + case 3: // Ribbon + sprite.append(0);sprite.append(0.5); + sprite.append(0.5);sprite.append(0); + sprite.append(1);sprite.append(0.5); + sprite.append(0.5);sprite.append(1); + sprite.append(0.5);sprite.append(0.5); + break; + case 4: // Sails + sprite.append(0);sprite.append(0.5); + sprite.append(1);sprite.append(0); + sprite.append(1);sprite.append(1); + sprite.append(0);sprite.append(1); + sprite.append(1);sprite.append(0.5); + break; + case 5: // Fins + sprite.append(1);sprite.append(0); + sprite.append(1);sprite.append(1); + sprite.append(0.5);sprite.append(1); + sprite.append(1);sprite.append(0.5); + sprite.append(0.5);sprite.append(0.5); + break; + case 6: // Beak + sprite.append(0);sprite.append(0); + sprite.append(1);sprite.append(0); + sprite.append(1);sprite.append(0.5); + sprite.append(0);sprite.append(0); + sprite.append(0.5);sprite.append(1); + sprite.append(0);sprite.append(1); + break; + case 7: // Chevron + sprite.append(0);sprite.append(0); + sprite.append(0.5);sprite.append(0); + sprite.append(1);sprite.append(0.5); + sprite.append(0.5);sprite.append(1); + sprite.append(0);sprite.append(1); + sprite.append(0.5);sprite.append(0.5); + break; + case 8: // Fish + sprite.append(0.5);sprite.append(0); + sprite.append(0.5);sprite.append(0.5); + sprite.append(1);sprite.append(0.5); + sprite.append(1);sprite.append(1); + sprite.append(0.5);sprite.append(1); + sprite.append(0.5);sprite.append(0.5); + sprite.append(0);sprite.append(0.5); + break; + case 9: // Kite + sprite.append(0);sprite.append(0); + sprite.append(1);sprite.append(0); + sprite.append(0.5);sprite.append(0.5); + sprite.append(1);sprite.append(0.5); + sprite.append(0.5);sprite.append(1); + sprite.append(0.5);sprite.append(0.5); + sprite.append(0);sprite.append(1); + break; + case 10: // Trough + sprite.append(0);sprite.append(0.5); + sprite.append(0.5);sprite.append(1); + sprite.append(1);sprite.append(0.5); + sprite.append(0.5);sprite.append(0); + sprite.append(1);sprite.append(0); + sprite.append(1);sprite.append(1); + sprite.append(0);sprite.append(1); + break; + case 11: // Rays + sprite.append(0.5);sprite.append(0); + sprite.append(1);sprite.append(0); + sprite.append(1);sprite.append(1); + sprite.append(0.5);sprite.append(1); + sprite.append(1);sprite.append(0.75); + sprite.append(0.5);sprite.append(0.5); + sprite.append(1);sprite.append(0.25); + break; + case 12: // Double rhombus + sprite.append(0);sprite.append(0.5); + sprite.append(0.5);sprite.append(0); + sprite.append(0.5);sprite.append(0.5); + sprite.append(1);sprite.append(0); + sprite.append(1);sprite.append(0.5); + sprite.append(0.5);sprite.append(1); + sprite.append(0.5);sprite.append(0.5); + sprite.append(0);sprite.append(1); + break; + case 13: // Crown + sprite.append(0);sprite.append(0); + sprite.append(1);sprite.append(0); + sprite.append(1);sprite.append(1); + sprite.append(0);sprite.append(1); + sprite.append(1);sprite.append(0.5); + sprite.append(0.5);sprite.append(0.25); + sprite.append(0.5);sprite.append(0.75); + sprite.append(0);sprite.append(0.5); + sprite.append(0.5);sprite.append(0.25); + break; + case 14: // Radioactive + sprite.append(0);sprite.append(0.5); + sprite.append(0.5);sprite.append(0.5); + sprite.append(0.5);sprite.append(0); + sprite.append(1);sprite.append(0); + sprite.append(0.5);sprite.append(0.5); + sprite.append(1);sprite.append(0.5); + sprite.append(0.5);sprite.append(1); + sprite.append(0.5);sprite.append(0.5); + sprite.append(0);sprite.append(1); + break; + default: // Tiles + sprite.append(0);sprite.append(0); + sprite.append(1);sprite.append(0); + sprite.append(0.5);sprite.append(0.5); + sprite.append(0.5);sprite.append(0); + sprite.append(0);sprite.append(0.5); + sprite.append(1);sprite.append(0.5); + sprite.append(0.5);sprite.append(1); + sprite.append(0.5);sprite.append(0.5); + sprite.append(0);sprite.append(1); + break; + } + + // Scale up + for (quint8 i = 0; i < sprite.size(); ++i) { + sprite[i] = sprite[i] * size; + } + + return sprite; +} + +/** + * @brief GxsIdDetails::getCenter + * @param shapeType: type of shape (0 to 15) + * @param size: Size for shape + * @return QList for all point for path drawing (shape) in center + */ +QList GxsIdDetails::getCenter(quint8 shapeType, quint16 size) +{ + QList sprite; + switch (shapeType) { + case 0: // Empty + sprite.clear(); + break; + case 1: // Fill + sprite.append(0);sprite.append(0); + sprite.append(1);sprite.append(0); + sprite.append(1);sprite.append(1); + sprite.append(0);sprite.append(1); + break; + case 2: // Diamond + sprite.append(0.5);sprite.append(0); + sprite.append(1);sprite.append(0.5); + sprite.append(0.5);sprite.append(1); + sprite.append(0);sprite.append(0.5); + break; + case 3: // Reverse diamond + sprite.append(0);sprite.append(0); + sprite.append(1);sprite.append(0); + sprite.append(1);sprite.append(1); + sprite.append(0);sprite.append(1); + sprite.append(0);sprite.append(0.5); + sprite.append(0.5);sprite.append(1); + sprite.append(1);sprite.append(0.5); + sprite.append(0.5);sprite.append(0); + sprite.append(0);sprite.append(0.5); + break; + case 4: // Cross + sprite.append(0.25);sprite.append(0); + sprite.append(0.75);sprite.append(0); + sprite.append(0.5);sprite.append(0.5); + sprite.append(1);sprite.append(0.25); + sprite.append(1);sprite.append(0.75); + sprite.append(0.5);sprite.append(0.5); + sprite.append(0.75);sprite.append(1); + sprite.append(0.25);sprite.append(1); + sprite.append(0.5);sprite.append(0.5); + sprite.append(0);sprite.append(0.75); + sprite.append(0);sprite.append(0.25); + sprite.append(0.5);sprite.append(0.5); + break; + case 5: // Morning star + sprite.append(0);sprite.append(0); + sprite.append(0.5);sprite.append(0.25); + sprite.append(1);sprite.append(0); + sprite.append(0.75);sprite.append(0.5); + sprite.append(1);sprite.append(1); + sprite.append(0.5);sprite.append(0.75); + sprite.append(0);sprite.append(1); + sprite.append(0.25);sprite.append(0.5); + break; + case 6: // Small square + sprite.append(0.33);sprite.append(0.33); + sprite.append(0.67);sprite.append(0.33); + sprite.append(0.67);sprite.append(0.67); + sprite.append(0.33);sprite.append(0.67); + break; + case 7: // Checkerboard + sprite.append(0);sprite.append(0); + sprite.append(0.33);sprite.append(0); + sprite.append(0.33);sprite.append(0.33); + sprite.append(0.66);sprite.append(0.33); + sprite.append(0.67);sprite.append(0); + sprite.append(1);sprite.append(0); + sprite.append(1);sprite.append(0.33); + sprite.append(0.67);sprite.append(0.33); + sprite.append(0.67);sprite.append(0.67); + sprite.append(1);sprite.append(0.67); + sprite.append(1);sprite.append(1); + sprite.append(0.67);sprite.append(1); + sprite.append(0.67);sprite.append(0.67); + sprite.append(0.33);sprite.append(0.67); + sprite.append(0.33);sprite.append(1); + sprite.append(0);sprite.append(1); + sprite.append(0);sprite.append(0.67); + sprite.append(0.33);sprite.append(0.67); + sprite.append(0.33);sprite.append(0.33); + sprite.append(0);sprite.append(0.33); + break; + default: // Tiles + sprite.append(0);sprite.append(0); + sprite.append(1);sprite.append(0); + sprite.append(0.5);sprite.append(0.5); + sprite.append(0.5);sprite.append(0); + sprite.append(0);sprite.append(0.5); + sprite.append(1);sprite.append(0.5); + sprite.append(0.5);sprite.append(1); + sprite.append(0.5);sprite.append(0.5); + sprite.append(0);sprite.append(1); + break; + } + /* apply ratios */ + for (quint8 i = 0; i < sprite.size(); ++i) { + sprite[i] = sprite[i] * size; + } + + return sprite; +} + +/** + * @brief GxsIdDetails::drawRotatedPolygon + * @param pixmap: The pixmap write to draw + * @param sprite: path to follow + * @param x: Offset to start + * @param y: Offset to start + * @param shapeangle: Angle of shape to draw + * @param angle: Angle (where 3x3 in picture) + * @param size: Size of shape + * @param fillColor: Color to fill shape + */ +void GxsIdDetails::drawRotatedPolygon( QPixmap *pixmap, + QList sprite, + quint16 x, quint16 y, + qreal shapeangle, qreal angle, + quint16 size, QColor fillColor) +{ + qreal halfSize = size / 2; + QPainter painter (pixmap); + painter.save(); + painter.setBrush(fillColor); + painter.setPen(fillColor); + + painter.translate(x, y); + painter.rotate(angle); + painter.save(); + painter.translate(halfSize, halfSize); + QList tmpSprite; + for (quint8 p = 0; p < sprite.size(); ++p) { + tmpSprite.append(sprite[p] - halfSize); + } + painter.rotate(shapeangle); + if (tmpSprite.size() >= 2) { + QPainterPath path; + path.setFillRule(Qt::WindingFill); + path.moveTo(tmpSprite[0], tmpSprite[1]); + for (int i = 2; i < tmpSprite.size(); ++++i) { + path.lineTo(tmpSprite[i], tmpSprite[i + 1]); + } + //path.lineTo(tmpSprite[0], tmpSprite[1]); + painter.drawPath(path); + } + // Black outline for debugging + //painter.drawRect(-halfSize, -halfSize, size, size); + painter.restore(); + painter.restore(); +} + +/** + * @brief GxsIdDetails::drawIdentIcon + * @param hash: Hash to compute (min 20 char) + * @param width: Size of picture to get + * @param rotate: If the shapes could be rotated + * @return QImage of computed hash + */ +QImage GxsIdDetails::drawIdentIcon( QString hash, quint16 width, bool rotate) +{ + bool ok; + quint8 csh = hash.mid(0, 1).toInt(&ok,16);// Corner sprite shape + quint8 ssh = hash.mid(1, 1).toInt(&ok, 16); // Side sprite shape + quint8 xsh = hash.mid(2, 1).toInt(&ok, 16) & 7; // Center sprite shape + + qreal halfPi = 90;// M_PI/2; + qreal cro = rotate ? halfPi * (hash.mid(3, 1).toInt(&ok, 16) & 3) : 0; // Corner sprite rotation + qreal sro = rotate ? halfPi * (hash.mid(4, 1).toInt(&ok, 16) & 3) : 0; // Side sprite rotation + quint8 xbg = hash.mid(5, 1).toInt(&ok, 16) % 2; // Center sprite background + + // Corner sprite foreground color + quint8 cfr = hash.mid(6, 2).toInt(&ok, 16); + quint8 cfg = hash.mid(8, 2).toInt(&ok, 16); + quint8 cfb = hash.mid(10, 2).toInt(&ok, 16); + + // Side sprite foreground color + quint8 sfr = hash.mid(12, 2).toInt(&ok, 16); + quint8 sfg = hash.mid(14, 2).toInt(&ok, 16); + quint8 sfb = hash.mid(16, 2).toInt(&ok, 16); + + // Final angle of rotation + // not used + //int angle = hash.mid(18, 2).toInt(&ok, 16); + + /* Size of each sprite */ + quint16 size = width / 3; + quint16 totalsize = width; + + /// start with blank 3x3 identicon + QPixmap pixmap = QPixmap(totalsize, totalsize); + pixmap.fill(QColor::fromRgb(200,200,200)); + + // Generate corner sprites + QList corner = getSprite(csh, size); + QColor fillCorner = QColor( cfr, cfg, cfb ); + drawRotatedPolygon(&pixmap, corner, 0, 0, cro, 0, size, fillCorner); + drawRotatedPolygon(&pixmap, corner, totalsize, 0, cro, 90, size, fillCorner); + drawRotatedPolygon(&pixmap, corner, totalsize, totalsize, cro, 180, size, fillCorner); + drawRotatedPolygon(&pixmap, corner, 0, totalsize, cro, 270, size, fillCorner); + + // Draw sides + QList side = getSprite(ssh, size); + QColor fillSide = QColor( sfr, sfg, sfb); + drawRotatedPolygon(&pixmap, side, 0, size, sro, 0, size, fillSide); + drawRotatedPolygon(&pixmap, side, 2 * size, 0, sro, 90, size, fillSide); + drawRotatedPolygon(&pixmap, side, 3 * size, 2 * size, sro, 180, size, fillSide); + drawRotatedPolygon(&pixmap, side, size, 3 * size, sro, 270, size, fillSide); + + // Draw center + QList center = getCenter(xsh, size); + // Make sure there's enough contrast before we use background color of side sprite + QColor fillCenter; + if (xbg > 0 && (abs(cfr - sfr) > 127 || abs(cfg - sfg) > 127 || abs(cfb - sfb) > 127)) { + fillCenter = QColor( sfr, sfg, sfb); + } else { + fillCenter = QColor( cfr, cfg, cfb); + } + drawRotatedPolygon(&pixmap, center, size, size, 0, 0, size, fillCenter); + + return pixmap.toImage(); +} + //static bool CreateIdIcon(const RsGxsId &id, QIcon &idIcon) //{ // QPixmap image(IconSize, IconSize); diff --git a/retroshare-gui/src/gui/gxs/GxsIdDetails.h b/retroshare-gui/src/gui/gxs/GxsIdDetails.h index 81666510b..2a78558f2 100644 --- a/retroshare-gui/src/gui/gxs/GxsIdDetails.h +++ b/retroshare-gui/src/gui/gxs/GxsIdDetails.h @@ -69,7 +69,8 @@ public: static bool GenerateCombinedIcon(QIcon &outIcon, const QList &icons); - static QImage makeDefaultIcon(const RsGxsId& id); + //static QImage makeDefaultIcon(const RsGxsId& id); + static QImage makeDefaultIcon(const RsGxsId& id); /* Processing */ static bool process(const RsGxsId &id, GxsIdDetailsCallbackFunction callback, QObject *object, const QVariant &data = QVariant()); @@ -83,6 +84,17 @@ protected: /* Timer */ virtual void timerEvent(QTimerEvent *event); +private: + static QList getSprite(quint8 shapeType, quint16 size); + static QList getCenter(quint8 shapeType, quint16 size); + static void fillPoly (QPainter *painter, QList sprite); + static void drawRotatedPolygon(QPixmap *pixmap, + QList sprite, + quint16 x, quint16 y, + qreal shapeangle, qreal angle, + quint16 size, QColor fillColor); + static QImage drawIdentIcon(QString hash, quint16 width, bool rotate); + private slots: void objectDestroyed(QObject *object); void doStartTimer();