Identicons working in most lists, but not working for new items yet.
There are no settings yet.
This commit is contained in:
parent
a36c696f9d
commit
deeac2c99b
|
@ -47,6 +47,20 @@ except Exception as err:
|
|||
print 'Error message:', err
|
||||
sys.exit()
|
||||
|
||||
# for the md5 hash (used for the identicons)
|
||||
import hashlib
|
||||
|
||||
# load identicon code
|
||||
# :Author:Shin Adachi <shn@glucose.jp>
|
||||
# Licesensed under FreeBSD License.
|
||||
import identicon
|
||||
# usage: identicon.render_identicon(code, size)
|
||||
# requires PIL
|
||||
|
||||
# load another identicon code
|
||||
# http://boottunes.googlecode.com/svn-history/r302/trunk/src/pydenticon.py
|
||||
from pydenticon import Pydenticon
|
||||
|
||||
try:
|
||||
_encoding = QtGui.QApplication.UnicodeUTF8
|
||||
except AttributeError:
|
||||
|
@ -55,6 +69,34 @@ except AttributeError:
|
|||
def _translate(context, text):
|
||||
return QtGui.QApplication.translate(context, text)
|
||||
|
||||
def identiconize(address):
|
||||
suffix = "" # here you could put "@bitmessge.ch" or "@bm.addr" to make it compatible with other identicon generators
|
||||
# instead, you could also use a pseudo-password to salt the generation of the identicons
|
||||
# Attacks where someone creates an address to mimic someone else's identicon should be impossible then
|
||||
# i think it should generate a random string by default
|
||||
|
||||
# hash = hashlib.md5(addBMIfNotPresent(address)+suffix).hexdigest()[:8]
|
||||
# print hash
|
||||
##japanese code
|
||||
# idcon_render = identicon.render_identicon(int(hash, 16), 8)
|
||||
# idcon_render.save('images/'+hash+'.png')
|
||||
# im = idcon_render
|
||||
# http://qt-project.org/forums/viewthread/5866
|
||||
# from PIL import Image
|
||||
# from PyQt4.QtGui import QImage, QImageReader, QLabel, QPixmap, QApplication
|
||||
|
||||
# PHP-like code
|
||||
idcon_render = Pydenticon(addBMIfNotPresent(address)+suffix)
|
||||
image = idcon_render._render()
|
||||
|
||||
# im = Image.open('images/'+hash+'.png')
|
||||
# http://stackoverflow.com/questions/6756820/python-pil-image-tostring
|
||||
data = image.convert("RGBA").tostring("raw", "RGBA")
|
||||
image = QImage(data, image.size[0], image.size[1], QImage.Format_ARGB32)
|
||||
pix = QPixmap.fromImage(image)
|
||||
idcon = QtGui.QIcon()
|
||||
idcon.addPixmap(pix, QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
return idcon
|
||||
|
||||
class MyForm(QtGui.QMainWindow):
|
||||
|
||||
|
@ -327,6 +369,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
newItem.setTextColor(QtGui.QColor(128, 128, 128))
|
||||
if shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist'):
|
||||
newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta
|
||||
newItem.setIcon(identiconize(addressInKeysFile))
|
||||
self.ui.tableWidgetYourIdentities.setItem(0, 1, newItem)
|
||||
newItem = QtGui.QTableWidgetItem(str(
|
||||
addressStream(addressInKeysFile)))
|
||||
|
@ -354,9 +397,11 @@ class MyForm(QtGui.QMainWindow):
|
|||
for row in queryreturn:
|
||||
label, address = row
|
||||
self.ui.tableWidgetAddressBook.insertRow(0)
|
||||
# address book item
|
||||
newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8'))
|
||||
self.ui.tableWidgetAddressBook.setItem(0, 0, newItem)
|
||||
newItem = QtGui.QTableWidgetItem(address)
|
||||
newItem.setIcon(identiconize(address))
|
||||
newItem.setFlags(
|
||||
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.ui.tableWidgetAddressBook.setItem(0, 1, newItem)
|
||||
|
@ -401,6 +446,16 @@ class MyForm(QtGui.QMainWindow):
|
|||
self.numberOfBroadcastsProcessed = 0
|
||||
self.numberOfPubkeysProcessed = 0
|
||||
|
||||
# Set the icon sizes for the identicons
|
||||
identicon_size = 3*7
|
||||
self.ui.tableWidgetInbox.setIconSize(QtCore.QSize(identicon_size, identicon_size))
|
||||
self.ui.tableWidgetSent.setIconSize(QtCore.QSize(identicon_size, identicon_size))
|
||||
self.ui.tableWidgetYourIdentities.setIconSize(QtCore.QSize(identicon_size, identicon_size))
|
||||
self.ui.tableWidgetSubscriptions.setIconSize(QtCore.QSize(identicon_size, identicon_size))
|
||||
self.ui.tableWidgetAddressBook.setIconSize(QtCore.QSize(identicon_size, identicon_size))
|
||||
self.ui.tableWidgetWhitelist.setIconSize(QtCore.QSize(identicon_size, identicon_size))
|
||||
self.ui.tableWidgetBlacklist.setIconSize(QtCore.QSize(identicon_size, identicon_size))
|
||||
|
||||
self.UISignalThread = UISignaler()
|
||||
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
|
||||
"writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.writeNewAddressToTable)
|
||||
|
@ -607,6 +662,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
else:
|
||||
newItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8'))
|
||||
newItem.setToolTip(unicode(toLabel, 'utf-8'))
|
||||
newItem.setIcon(identiconize(toAddress))
|
||||
newItem.setData(Qt.UserRole, str(toAddress))
|
||||
newItem.setFlags(
|
||||
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
|
@ -618,6 +674,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
else:
|
||||
newItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8'))
|
||||
newItem.setToolTip(unicode(fromLabel, 'utf-8'))
|
||||
newItem.setIcon(identiconize(fromAddress))
|
||||
newItem.setData(Qt.UserRole, str(fromAddress))
|
||||
newItem.setFlags(
|
||||
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
|
@ -747,10 +804,12 @@ class MyForm(QtGui.QMainWindow):
|
|||
if queryreturn != []:
|
||||
for row in queryreturn:
|
||||
fromLabel, = row
|
||||
|
||||
|
||||
# message row
|
||||
self.ui.tableWidgetInbox.insertRow(0)
|
||||
newItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8'))
|
||||
newItem.setToolTip(unicode(toLabel, 'utf-8'))
|
||||
# to
|
||||
newItem.setFlags(
|
||||
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
if not read:
|
||||
|
@ -760,7 +819,9 @@ class MyForm(QtGui.QMainWindow):
|
|||
newItem.setTextColor(QtGui.QColor(137, 04, 177))
|
||||
if shared.safeConfigGetBoolean(str(toAddress), 'chan'):
|
||||
newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange
|
||||
newItem.setIcon(identiconize(toAddress))
|
||||
self.ui.tableWidgetInbox.setItem(0, 0, newItem)
|
||||
# from
|
||||
if fromLabel == '':
|
||||
newItem = QtGui.QTableWidgetItem(
|
||||
unicode(fromAddress, 'utf-8'))
|
||||
|
@ -773,8 +834,9 @@ class MyForm(QtGui.QMainWindow):
|
|||
if not read:
|
||||
newItem.setFont(font)
|
||||
newItem.setData(Qt.UserRole, str(fromAddress))
|
||||
|
||||
newItem.setIcon(identiconize(fromAddress))
|
||||
self.ui.tableWidgetInbox.setItem(0, 1, newItem)
|
||||
# subject
|
||||
newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8'))
|
||||
newItem.setToolTip(unicode(subject, 'utf-8'))
|
||||
newItem.setData(Qt.UserRole, unicode(message, 'utf-8)'))
|
||||
|
@ -783,6 +845,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
if not read:
|
||||
newItem.setFont(font)
|
||||
self.ui.tableWidgetInbox.setItem(0, 2, newItem)
|
||||
# time received
|
||||
newItem = myTableWidgetItem(unicode(strftime(shared.config.get(
|
||||
'bitmessagesettings', 'timeformat'), localtime(int(received))), 'utf-8'))
|
||||
newItem.setToolTip(unicode(strftime(shared.config.get(
|
||||
|
@ -1473,6 +1536,8 @@ class MyForm(QtGui.QMainWindow):
|
|||
toLabel = toAddress
|
||||
self.ui.tableWidgetInbox.item(
|
||||
i, 0).setText(unicode(toLabel, 'utf-8'))
|
||||
self.ui.tableWidgetInbox.item(
|
||||
i, 0).setIcon(identiconize(toAddress))
|
||||
# Set the color according to whether it is the address of a mailing
|
||||
# list or not.
|
||||
if shared.safeConfigGetBoolean(toAddress, 'mailinglist'):
|
||||
|
@ -1493,6 +1558,8 @@ class MyForm(QtGui.QMainWindow):
|
|||
fromLabel = fromAddress
|
||||
self.ui.tableWidgetSent.item(
|
||||
i, 1).setText(unicode(fromLabel, 'utf-8'))
|
||||
self.ui.tableWidgetSent.item(
|
||||
i, 0).setIcon(identiconize(fromAddress))
|
||||
|
||||
def rerenderSentToLabels(self):
|
||||
for i in range(self.ui.tableWidgetSent.rowCount()):
|
||||
|
@ -1512,7 +1579,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
toLabel, = row
|
||||
self.ui.tableWidgetSent.item(
|
||||
i, 0).setText(unicode(toLabel, 'utf-8'))
|
||||
|
||||
|
||||
def rerenderSubscriptions(self):
|
||||
self.ui.tableWidgetSubscriptions.setRowCount(0)
|
||||
shared.sqlLock.acquire()
|
||||
|
@ -1529,6 +1596,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
newItem.setTextColor(QtGui.QColor(128, 128, 128))
|
||||
self.ui.tableWidgetSubscriptions.setItem(0, 0, newItem)
|
||||
newItem = QtGui.QTableWidgetItem(address)
|
||||
newItem.setIcon(identiconize(address))
|
||||
newItem.setFlags(
|
||||
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
if not enabled:
|
||||
|
@ -1791,6 +1859,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
newItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8'))
|
||||
newItem.setToolTip(unicode(fromLabel, 'utf-8'))
|
||||
newItem.setData(Qt.UserRole, str(fromAddress))
|
||||
newItem.setIcon(identiconize(address))
|
||||
self.ui.tableWidgetSent.setItem(0, 1, newItem)
|
||||
newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8)'))
|
||||
newItem.setToolTip(unicode(subject, 'utf-8)'))
|
||||
|
@ -1920,6 +1989,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8'))
|
||||
self.ui.tableWidgetAddressBook.setItem(0, 0, newItem)
|
||||
newItem = QtGui.QTableWidgetItem(address)
|
||||
newItem.setIcon(identiconize(address))
|
||||
newItem.setFlags(
|
||||
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.ui.tableWidgetAddressBook.setItem(0, 1, newItem)
|
||||
|
@ -1949,6 +2019,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8'))
|
||||
self.ui.tableWidgetSubscriptions.setItem(0,0,newItem)
|
||||
newItem = QtGui.QTableWidgetItem(address)
|
||||
newItem.setIcon(identiconize(address))
|
||||
newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled )
|
||||
self.ui.tableWidgetSubscriptions.setItem(0,1,newItem)
|
||||
self.ui.tableWidgetSubscriptions.setSortingEnabled(True)
|
||||
|
@ -1998,6 +2069,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
newItem.setTextColor(QtGui.QColor(128, 128, 128))
|
||||
self.ui.tableWidgetBlacklist.setItem(0, 0, newItem)
|
||||
newItem = QtGui.QTableWidgetItem(address)
|
||||
newItem.setIcon(identiconize(address))
|
||||
newItem.setFlags(
|
||||
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
if not enabled:
|
||||
|
@ -2971,6 +3043,7 @@ class MyForm(QtGui.QMainWindow):
|
|||
self.ui.tableWidgetYourIdentities.setItem(
|
||||
0, 0, newItem)
|
||||
newItem = QtGui.QTableWidgetItem(address)
|
||||
newItem.setIcon(identiconize(address))
|
||||
newItem.setFlags(
|
||||
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
if shared.safeConfigGetBoolean(address, 'chan'):
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# Form implementation generated from reading ui file 'settings.ui'
|
||||
#
|
||||
# Created: Sat Aug 24 09:19:58 2013
|
||||
# Created: Sun Aug 25 18:09:38 2013
|
||||
# by: PyQt4 UI code generator 4.10.2
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
|
262
src/identicon.py
Normal file
262
src/identicon.py
Normal file
|
@ -0,0 +1,262 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
identicon.py
|
||||
identicon python implementation.
|
||||
by Shin Adachi <shn@glucose.jp>
|
||||
|
||||
= usage =
|
||||
|
||||
== commandline ==
|
||||
>>> python identicon.py [code]
|
||||
|
||||
== python ==
|
||||
>>> import identicon
|
||||
>>> identicon.render_identicon(code, size)
|
||||
|
||||
Return a PIL Image class instance which have generated identicon image.
|
||||
```size``` specifies `patch size`. Generated image size is 3 * ```size```.
|
||||
"""
|
||||
# g
|
||||
# PIL Modules
|
||||
import Image
|
||||
import ImageDraw
|
||||
import ImagePath
|
||||
import ImageColor
|
||||
|
||||
|
||||
__all__ = ['render_identicon', 'IdenticonRendererBase']
|
||||
|
||||
|
||||
class Matrix2D(list):
|
||||
"""Matrix for Patch rotation"""
|
||||
def __init__(self, initial=[0.] * 9):
|
||||
assert isinstance(initial, list) and len(initial) == 9
|
||||
list.__init__(self, initial)
|
||||
|
||||
def clear(self):
|
||||
for i in xrange(9):
|
||||
self[i] = 0.
|
||||
|
||||
def set_identity(self):
|
||||
self.clear()
|
||||
for i in xrange(3):
|
||||
self[i] = 1.
|
||||
|
||||
def __str__(self):
|
||||
return '[%s]' % ', '.join('%3.2f' % v for v in self)
|
||||
|
||||
def __mul__(self, other):
|
||||
r = []
|
||||
if isinstance(other, Matrix2D):
|
||||
for y in xrange(3):
|
||||
for x in xrange(3):
|
||||
v = 0.0
|
||||
for i in xrange(3):
|
||||
v += (self[i * 3 + x] * other[y * 3 + i])
|
||||
r.append(v)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
return Matrix2D(r)
|
||||
|
||||
def for_PIL(self):
|
||||
return self[0:6]
|
||||
|
||||
@classmethod
|
||||
def translate(kls, x, y):
|
||||
return kls([1.0, 0.0, float(x),
|
||||
0.0, 1.0, float(y),
|
||||
0.0, 0.0, 1.0])
|
||||
|
||||
@classmethod
|
||||
def scale(kls, x, y):
|
||||
return kls([float(x), 0.0, 0.0,
|
||||
0.0, float(y), 0.0,
|
||||
0.0, 0.0, 1.0])
|
||||
|
||||
"""
|
||||
# need `import math`
|
||||
@classmethod
|
||||
def rotate(kls, theta, pivot=None):
|
||||
c = math.cos(theta)
|
||||
s = math.sin(theta)
|
||||
|
||||
matR = kls([c, -s, 0., s, c, 0., 0., 0., 1.])
|
||||
if not pivot:
|
||||
return matR
|
||||
return kls.translate(-pivot[0], -pivot[1]) * matR *
|
||||
kls.translate(*pivot)
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def rotateSquare(kls, theta, pivot=None):
|
||||
theta = theta % 4
|
||||
c = [1., 0., -1., 0.][theta]
|
||||
s = [0., 1., 0., -1.][theta]
|
||||
|
||||
matR = kls([c, -s, 0., s, c, 0., 0., 0., 1.])
|
||||
if not pivot:
|
||||
return matR
|
||||
return kls.translate(-pivot[0], -pivot[1]) * matR * \
|
||||
kls.translate(*pivot)
|
||||
|
||||
|
||||
class IdenticonRendererBase(object):
|
||||
PATH_SET = []
|
||||
|
||||
def __init__(self, code):
|
||||
"""
|
||||
@param code code for icon
|
||||
"""
|
||||
if not isinstance(code, int):
|
||||
code = int(code)
|
||||
self.code = code
|
||||
|
||||
def render(self, size):
|
||||
"""
|
||||
render identicon to PIL.Image
|
||||
|
||||
@param size identicon patchsize. (image size is 3 * [size])
|
||||
@return PIL.Image
|
||||
"""
|
||||
|
||||
# decode the code
|
||||
middle, corner, side, foreColor, backColor = self.decode(self.code)
|
||||
|
||||
# make image
|
||||
image = Image.new("RGB", (size * 3, size * 3))
|
||||
draw = ImageDraw.Draw(image)
|
||||
|
||||
# fill background
|
||||
draw.rectangle((0, 0, image.size[0], image.size[1]), fill=0)
|
||||
|
||||
kwds = {
|
||||
'draw': draw,
|
||||
'size': size,
|
||||
'foreColor': foreColor,
|
||||
'backColor': backColor}
|
||||
# middle patch
|
||||
self.drawPatch((1, 1), middle[2], middle[1], middle[0], **kwds)
|
||||
|
||||
# side patch
|
||||
kwds['type'] = side[0]
|
||||
for i in xrange(4):
|
||||
pos = [(1, 0), (2, 1), (1, 2), (0, 1)][i]
|
||||
self.drawPatch(pos, side[2] + 1 + i, side[1], **kwds)
|
||||
|
||||
# corner patch
|
||||
kwds['type'] = corner[0]
|
||||
for i in xrange(4):
|
||||
pos = [(0, 0), (2, 0), (2, 2), (0, 2)][i]
|
||||
self.drawPatch(pos, corner[2] + 1 + i, corner[1], **kwds)
|
||||
|
||||
return image
|
||||
|
||||
def drawPatch(self, pos, turn, invert, type, draw, size, foreColor,
|
||||
backColor):
|
||||
"""
|
||||
@param size patch size
|
||||
"""
|
||||
path = self.PATH_SET[type]
|
||||
if not path:
|
||||
# blank patch
|
||||
invert = not invert
|
||||
path = [(0., 0.), (1., 0.), (1., 1.), (0., 1.), (0., 0.)]
|
||||
patch = ImagePath.Path(path)
|
||||
if invert:
|
||||
foreColor, backColor = backColor, foreColor
|
||||
|
||||
mat = Matrix2D.rotateSquare(turn, pivot=(0.5, 0.5)) *\
|
||||
Matrix2D.translate(*pos) *\
|
||||
Matrix2D.scale(size, size)
|
||||
|
||||
patch.transform(mat.for_PIL())
|
||||
draw.rectangle((pos[0] * size, pos[1] * size, (pos[0] + 1) * size,
|
||||
(pos[1] + 1) * size), fill=backColor)
|
||||
draw.polygon(patch, fill=foreColor, outline=foreColor)
|
||||
|
||||
### virtual functions
|
||||
def decode(self, code):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class DonRenderer(IdenticonRendererBase):
|
||||
"""
|
||||
Don Park's implementation of identicon
|
||||
see : http://www.docuverse.com/blog/donpark/2007/01/19/identicon-updated-and-source-released
|
||||
"""
|
||||
|
||||
PATH_SET = [
|
||||
[(0, 0), (4, 0), (4, 4), (0, 4)], # 0
|
||||
[(0, 0), (4, 0), (0, 4)],
|
||||
[(2, 0), (4, 4), (0, 4)],
|
||||
[(0, 0), (2, 0), (2, 4), (0, 4)],
|
||||
[(2, 0), (4, 2), (2, 4), (0, 2)], # 4
|
||||
[(0, 0), (4, 2), (4, 4), (2, 4)],
|
||||
[(2, 0), (4, 4), (2, 4), (3, 2), (1, 2), (2, 4), (0, 4)],
|
||||
[(0, 0), (4, 2), (2, 4)],
|
||||
[(1, 1), (3, 1), (3, 3), (1, 3)], # 8
|
||||
[(2, 0), (4, 0), (0, 4), (0, 2), (2, 2)],
|
||||
[(0, 0), (2, 0), (2, 2), (0, 2)],
|
||||
[(0, 2), (4, 2), (2, 4)],
|
||||
[(2, 2), (4, 4), (0, 4)],
|
||||
[(2, 0), (2, 2), (0, 2)],
|
||||
[(0, 0), (2, 0), (0, 2)],
|
||||
[]] # 15
|
||||
MIDDLE_PATCH_SET = [0, 4, 8, 15]
|
||||
|
||||
# modify path set
|
||||
for idx in xrange(len(PATH_SET)):
|
||||
if PATH_SET[idx]:
|
||||
p = map(lambda vec: (vec[0] / 4.0, vec[1] / 4.0), PATH_SET[idx])
|
||||
PATH_SET[idx] = p + p[:1]
|
||||
|
||||
def decode(self, code):
|
||||
# decode the code
|
||||
middleType = self.MIDDLE_PATCH_SET[code & 0x03]
|
||||
middleInvert= (code >> 2) & 0x01
|
||||
cornerType = (code >> 3) & 0x0F
|
||||
cornerInvert= (code >> 7) & 0x01
|
||||
cornerTurn = (code >> 8) & 0x03
|
||||
sideType = (code >> 10) & 0x0F
|
||||
sideInvert = (code >> 14) & 0x01
|
||||
sideTurn = (code >> 15) & 0x03
|
||||
# bug reported by Masato Hagiwara
|
||||
# http://lilyx.net/iconlang-en/
|
||||
# blue = (code >> 16) & 0x1F
|
||||
# green = (code >> 21) & 0x1F
|
||||
blue = (code >> 17) & 0x1F
|
||||
green = (code >> 22) & 0x1F
|
||||
red = (code >> 27) & 0x1F
|
||||
|
||||
foreColor = (red << 3, green << 3, blue << 3)
|
||||
|
||||
return (middleType, middleInvert, 0),\
|
||||
(cornerType, cornerInvert, cornerTurn),\
|
||||
(sideType, sideInvert, sideTurn),\
|
||||
foreColor, ImageColor.getrgb('white')
|
||||
|
||||
|
||||
def render_identicon(code, size, renderer=None):
|
||||
if not renderer:
|
||||
renderer = DonRenderer
|
||||
return renderer(code).render(size)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print 'usage: python identicon.py [CODE]....'
|
||||
raise SystemExit
|
||||
|
||||
for code in sys.argv[1:]:
|
||||
if code.startswith('0x') or code.startswith('0X'):
|
||||
code = int(code[2:], 16)
|
||||
elif code.startswith('0'):
|
||||
code = int(code[1:], 8)
|
||||
else:
|
||||
code = int(code)
|
||||
|
||||
icon = render_identicon(code, 24)
|
||||
icon.save('%08x.png' % code, 'PNG')
|
11
src/identicon_py_license
Normal file
11
src/identicon_py_license
Normal file
|
@ -0,0 +1,11 @@
|
|||
identicon.py is Licesensed under FreeBSD License.
|
||||
(http://www.freebsd.org/copyright/freebsd-license.html)
|
||||
|
||||
Copyright 1994-2009 Shin Adachi. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
409
src/pydenticon.py
Normal file
409
src/pydenticon.py
Normal file
|
@ -0,0 +1,409 @@
|
|||
"""
|
||||
Python implementation of PHP Identicons, found here:
|
||||
http://sourceforge.net/projects/identicons/
|
||||
|
||||
Licensed under the GPLv3.
|
||||
"""
|
||||
from PIL import Image, ImageDraw, ImagePath, ImageColor
|
||||
from math import fabs as abs
|
||||
from hashlib import md5
|
||||
import StringIO
|
||||
|
||||
class Pydenticon:
|
||||
"""
|
||||
Generate an identicon.
|
||||
|
||||
"""
|
||||
|
||||
WHITE = (255, 255, 255)
|
||||
|
||||
def __init__(self, input, size = 128):
|
||||
self.hash = md5(input).hexdigest()
|
||||
self.size = size
|
||||
|
||||
def as_string(self):
|
||||
"""
|
||||
Return the image as a string representation of a PNG file.
|
||||
|
||||
"""
|
||||
if not hasattr(self, 'image'):
|
||||
self.image = self._render()
|
||||
output = StringIO.StringIO()
|
||||
self.image.save(output, format="PNG")
|
||||
contents = output.getvalue()
|
||||
output.close()
|
||||
return contents
|
||||
|
||||
def save(self, filename):
|
||||
"""
|
||||
Save the result as a PNG file.
|
||||
|
||||
"""
|
||||
if not hasattr(self, 'image'):
|
||||
self.image = self._render()
|
||||
self.image.save(filename, format="PNG")
|
||||
|
||||
def _get_sprite(self, shape, r, g, b, rotation):
|
||||
"""
|
||||
Generate sprite for corners and sides and returnro a PIL.Image object.
|
||||
|
||||
"""
|
||||
sprite = Image.new("RGB", (self.size, self.size), self.WHITE)
|
||||
|
||||
if shape == 0: # triangle
|
||||
points = [
|
||||
0.5,1,
|
||||
1,0,
|
||||
1,1
|
||||
]
|
||||
elif shape == 1: #parallelogram
|
||||
points = [
|
||||
0.5,0,
|
||||
1,0,
|
||||
0.5,1,
|
||||
0,1
|
||||
]
|
||||
elif shape == 2: # mouse ears
|
||||
points = [
|
||||
0.5,0,
|
||||
1,0,
|
||||
1,1,
|
||||
0.5,1,
|
||||
1,0.5
|
||||
]
|
||||
elif shape == 3: # ribbon
|
||||
points = [
|
||||
0,0.5,
|
||||
0.5,0,
|
||||
1,0.5,
|
||||
0.5,1,
|
||||
0.5,0.5
|
||||
]
|
||||
elif shape == 4: # sails
|
||||
points = [
|
||||
0,0.5,
|
||||
1,0,
|
||||
1,1,
|
||||
0,1,
|
||||
1,0.5
|
||||
]
|
||||
elif shape == 5: # fins
|
||||
points = [
|
||||
1,0,
|
||||
1,1,
|
||||
0.5,1,
|
||||
1,0.5,
|
||||
0.5,0.5
|
||||
]
|
||||
elif shape == 6: # beak
|
||||
points = [
|
||||
0,0,
|
||||
1,0,
|
||||
1,0.5,
|
||||
0,0,
|
||||
0.5,1,
|
||||
0,1
|
||||
]
|
||||
elif shape == 7: # chevron
|
||||
points = [
|
||||
0,0,
|
||||
0.5,0,
|
||||
1,0.5,
|
||||
0.5,1,
|
||||
0,1,
|
||||
0.5,0.5
|
||||
]
|
||||
elif shape == 8: # fish
|
||||
points = [
|
||||
0.5,0,
|
||||
0.5,0.5,
|
||||
1,0.5,
|
||||
1,1,
|
||||
0.5,1,
|
||||
0.5,0.5,
|
||||
0,0.5
|
||||
]
|
||||
elif shape == 9: # kite
|
||||
points = [
|
||||
0,0,
|
||||
1,0,
|
||||
0.5,0.5,
|
||||
1,0.5,
|
||||
0.5,1,
|
||||
0.5,0.5,
|
||||
0,1
|
||||
]
|
||||
elif shape == 10: # trough
|
||||
points = [
|
||||
0,0.5,
|
||||
0.5,1,
|
||||
1,0.5,
|
||||
0.5,0,
|
||||
1,0,
|
||||
1,1,
|
||||
0,1
|
||||
]
|
||||
elif shape == 11: # rays
|
||||
points = [
|
||||
0.5,0,
|
||||
1,0,
|
||||
1,1,
|
||||
0.5,1,
|
||||
1,0.75,
|
||||
0.5,0.5,
|
||||
1,0.25
|
||||
]
|
||||
elif shape == 12: # double rhombus
|
||||
points = [
|
||||
0,0.5,
|
||||
0.5,0,
|
||||
0.5,0.5,
|
||||
1,0,
|
||||
1,0.5,
|
||||
0.5,1,
|
||||
0.5,0.5,
|
||||
0,1
|
||||
]
|
||||
elif shape == 13: # crown
|
||||
points = [
|
||||
0,0,
|
||||
1,0,
|
||||
1,1,
|
||||
0,1,
|
||||
1,0.5,
|
||||
0.5,0.25,
|
||||
0.5,0.75,
|
||||
0,0.5,
|
||||
0.5,0.25
|
||||
]
|
||||
elif shape == 14: # radioactive
|
||||
points = [
|
||||
0,0.5,
|
||||
0.5,0.5,
|
||||
0.5,0,
|
||||
1,0,
|
||||
0.5,0.5,
|
||||
1,0.5,
|
||||
0.5,1,
|
||||
0.5,0.5,
|
||||
0,1
|
||||
]
|
||||
else: # tiles
|
||||
points = [
|
||||
0,0,
|
||||
1,0,
|
||||
0.5,0.5,
|
||||
0.5,0,
|
||||
0,0.5,
|
||||
1,0.5,
|
||||
0.5,1,
|
||||
0.5,0.5,
|
||||
0,1
|
||||
]
|
||||
|
||||
# apply ratios
|
||||
for i in range(0, len(points)):
|
||||
points[i] = points[i] * self.size
|
||||
|
||||
draw = ImageDraw.Draw(sprite)
|
||||
draw.polygon(points, fill=(r, g, b))
|
||||
|
||||
for i in range(rotation):
|
||||
sprite = sprite.rotate(90)
|
||||
|
||||
return sprite
|
||||
|
||||
def _get_center(self, shape, fR, fG, fB, bR, bG, bB, useBg):
|
||||
"""
|
||||
Generate sprite for center block and return a PIL.Image object.
|
||||
|
||||
"""
|
||||
sprite = Image.new("RGB", (self.size, self.size), self.WHITE)
|
||||
|
||||
# make sure there's enough contrast before we use background color of side sprite
|
||||
sufficient_contrast = (
|
||||
abs(fR - bR) > 127 or abs(fG - bG) > 127 or abs(fB - bB) > 127
|
||||
)
|
||||
if useBg > 0 and sufficient_contrast:
|
||||
bg = (bR, bG, bB)
|
||||
else:
|
||||
bg = (255, 255, 255)
|
||||
|
||||
if shape == 0: # empty
|
||||
points = []
|
||||
|
||||
elif shape == 1: # fill
|
||||
points = [
|
||||
0,0,
|
||||
1,0,
|
||||
1,1,
|
||||
0,1
|
||||
]
|
||||
elif shape == 2: # diamond
|
||||
points = [
|
||||
0.5,0,
|
||||
1,0.5,
|
||||
0.5,1,
|
||||
0,0.5
|
||||
]
|
||||
elif shape == 3: # reverse diamond
|
||||
points = [
|
||||
0,0,
|
||||
1,0,
|
||||
1,1,
|
||||
0,1,
|
||||
0,0.5,
|
||||
0.5,1,
|
||||
1,0.5,
|
||||
0.5,0,
|
||||
0,0.5
|
||||
]
|
||||
elif shape == 4: # cross
|
||||
points = [
|
||||
0.25,0,
|
||||
0.75,0,
|
||||
0.5,0.5,
|
||||
1,0.25,
|
||||
1,0.75,
|
||||
0.5,0.5,
|
||||
0.75,1,
|
||||
0.25,1,
|
||||
0.5,0.5,
|
||||
0,0.75,
|
||||
0,0.25,
|
||||
0.5,0.5
|
||||
]
|
||||
elif shape == 5: # morning star
|
||||
points = [
|
||||
0,0,
|
||||
0.5,0.25,
|
||||
1,0,
|
||||
0.75,0.5,
|
||||
1,1,
|
||||
0.5,0.75,
|
||||
0,1,
|
||||
0.25,0.5
|
||||
]
|
||||
elif shape == 6: # small square
|
||||
points = [
|
||||
0.33,0.33,
|
||||
0.67,0.33,
|
||||
0.67,0.67,
|
||||
0.33,0.67
|
||||
]
|
||||
elif shape == 7: # checkerboard
|
||||
points = [
|
||||
0,0,
|
||||
0.33,0,
|
||||
0.33,0.33,
|
||||
0.66,0.33,
|
||||
0.67,0,
|
||||
1,0,
|
||||
1,0.33,
|
||||
0.67,0.33,
|
||||
0.67,0.67,
|
||||
1,0.67,
|
||||
1,1,
|
||||
0.67,1,
|
||||
0.67,0.67,
|
||||
0.33,0.67,
|
||||
0.33,1,
|
||||
0,1,
|
||||
0,0.67,
|
||||
0.33,0.67,
|
||||
0.33,0.33,
|
||||
0,0.33
|
||||
]
|
||||
|
||||
# apply ratios
|
||||
for i in range(0, len(points)):
|
||||
points[i] = points[i] * self.size
|
||||
|
||||
if len(points) > 0:
|
||||
draw = ImageDraw.Draw(sprite)
|
||||
draw.polygon(points, fill=(fR, fG, fB))
|
||||
|
||||
return sprite
|
||||
|
||||
def _render(self):
|
||||
"""
|
||||
Render the image and return the PIL.Image object.
|
||||
|
||||
"""
|
||||
# parse hash string
|
||||
corner_sprite_shape = int(self.hash[0:1], 16)
|
||||
side_sprite_shape = int(self.hash[1:2], 16)
|
||||
center_sprite_shape = int(self.hash[2:3], 16) & 7
|
||||
|
||||
corner_sprite_rot = int(self.hash[3:4], 16) & 3
|
||||
side_sprite_rot = int(self.hash[4:5], 16) & 3
|
||||
center_sprite_bg = int(self.hash[5:6], 16) % 2
|
||||
|
||||
# corner sprite foreground color
|
||||
corner_sprite_fg_r = int(self.hash[6:8], 16)
|
||||
corner_sprite_fg_g = int(self.hash[8:10], 16)
|
||||
corner_sprite_fg_b = int(self.hash[10:12], 16)
|
||||
|
||||
# side sprite foreground color
|
||||
side_sprite_fg_r = int(self.hash[12:14], 16)
|
||||
side_sprite_fg_g = int(self.hash[14:16], 16)
|
||||
side_sprite_fg_b = int(self.hash[16:18], 16)
|
||||
|
||||
# final angle of rotation
|
||||
angle = int(self.hash[18:20], 16)
|
||||
|
||||
# start with blank 3X sized identicon
|
||||
identicon = Image.new("RGB", (self.size*3, self.size*3), self.WHITE)
|
||||
|
||||
# generate corner sprites
|
||||
corner = self._get_sprite(
|
||||
corner_sprite_shape,
|
||||
corner_sprite_fg_r,
|
||||
corner_sprite_fg_g,
|
||||
corner_sprite_fg_b,
|
||||
corner_sprite_rot
|
||||
)
|
||||
identicon.paste(corner, (0, 0))
|
||||
corner = corner.rotate(90)
|
||||
identicon.paste(corner, (0, self.size*2))
|
||||
corner = corner.rotate(90)
|
||||
identicon.paste(corner, (self.size*2, self.size*2))
|
||||
corner = corner.rotate(90)
|
||||
identicon.paste(corner, (self.size*2, 0))
|
||||
|
||||
# generate side sprites
|
||||
side = self._get_sprite(
|
||||
side_sprite_shape,
|
||||
side_sprite_fg_r,
|
||||
side_sprite_fg_g,
|
||||
side_sprite_fg_b,
|
||||
side_sprite_rot
|
||||
)
|
||||
identicon.paste(side, (self.size, 0))
|
||||
side = side.rotate(90)
|
||||
identicon.paste(side, (0, self.size))
|
||||
side = side.rotate(90)
|
||||
identicon.paste(side, (self.size, self.size*2))
|
||||
side = side.rotate(90)
|
||||
identicon.paste(side, (self.size*2, self.size))
|
||||
|
||||
# generate center sprite
|
||||
center = self._get_center(
|
||||
center_sprite_shape,
|
||||
corner_sprite_fg_r,
|
||||
corner_sprite_fg_g,
|
||||
corner_sprite_fg_b,
|
||||
side_sprite_fg_r,
|
||||
side_sprite_fg_g,
|
||||
side_sprite_fg_b,
|
||||
center_sprite_bg
|
||||
)
|
||||
identicon.paste(center, (self.size, self.size))
|
||||
|
||||
# resize image
|
||||
resized = identicon.resize(
|
||||
(self.size, self.size),
|
||||
Image.ANTIALIAS
|
||||
)
|
||||
|
||||
return resized
|
Loading…
Reference in New Issue
Block a user