Updated to work with QPixmap instead of PIL!

The original source is licensed under a BSD-License, so it should be fine to use.
This commit is contained in:
sendiulo 2013-09-16 21:08:55 +02:00
parent a9fb87173f
commit 240e9b5b58
4 changed files with 119 additions and 484 deletions

View File

@ -47,20 +47,6 @@ except Exception as err:
print 'Error message:', err print 'Error message:', err
sys.exit() 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: try:
_encoding = QtGui.QApplication.UnicodeUTF8 _encoding = QtGui.QApplication.UnicodeUTF8
except AttributeError: except AttributeError:
@ -74,6 +60,7 @@ def identiconize(address):
if youdontwantidenticons == True: if youdontwantidenticons == True:
idcon = QtGui.QIcon() idcon = QtGui.QIcon()
return idcon return idcon
size = 3
str_broadcast_subscribers = '[Broadcast subscribers]' str_broadcast_subscribers = '[Broadcast subscribers]'
if address == str_broadcast_subscribers: if address == str_broadcast_subscribers:
@ -86,25 +73,37 @@ def identiconize(address):
# Attacks where someone creates an address to mimic someone else's identicon should be impossible then # 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 # i think it should generate a random string by default
if True: # identicon.py identicon_lib = 'qidenticon'
if identicon_lib == 'qidenticon':
# originally by:
# :Author:Shin Adachi <shn@glucose.jp>
# Licesensed under FreeBSD License.
# stripped from PIL and uses QT instead
import qidenticon
import hashlib
hash = hashlib.md5(addBMIfNotPresent(address)+suffix).hexdigest() hash = hashlib.md5(addBMIfNotPresent(address)+suffix).hexdigest()
idcon_render = identicon.render_identicon(int(hash, 16), 48) image = qidenticon.render_identicon(int(hash, 16), 48)
image = idcon_render idcon = QtGui.QIcon()
# http://qt-project.org/forums/viewthread/5866 idcon.addPixmap(image, QtGui.QIcon.Normal, QtGui.QIcon.Off)
# from PIL import Image return idcon
# from PyQt4.QtGui import QImage, QImageReader, QLabel, QPixmap, QApplication
else: # pydenticon.py elif identicon_lib == 'pydenticon': # pydenticon.py
# print identicon_lib
# load another identicon code
# http://boottunes.googlecode.com/svn-history/r302/trunk/src/pydenticon.py
from pydenticon import Pydenticon
# GPLv3 !!!
idcon_render = Pydenticon(addBMIfNotPresent(address)+suffix) idcon_render = Pydenticon(addBMIfNotPresent(address)+suffix)
image = idcon_render._render() image = idcon_render._render()
# im = Image.open('images/'+hash+'.png')
# im = Image.open('images/'+hash+'.png') # http://stackoverflow.com/questions/6756820/python-pil-image-tostring
# http://stackoverflow.com/questions/6756820/python-pil-image-tostring data = image.convert("RGBA").tostring("raw", "RGBA")
data = image.convert("RGBA").tostring("raw", "RGBA") image = QImage(data, image.size[0], image.size[1], QImage.Format_ARGB32)
image = QImage(data, image.size[0], image.size[1], QImage.Format_ARGB32) pix = QPixmap.fromImage(image)
pix = QPixmap.fromImage(image) idcon = QtGui.QIcon()
idcon = QtGui.QIcon() idcon.addPixmap(pix, QtGui.QIcon.Normal, QtGui.QIcon.Off)
idcon.addPixmap(pix, QtGui.QIcon.Normal, QtGui.QIcon.Off) return idcon
return idcon
class MyForm(QtGui.QMainWindow): class MyForm(QtGui.QMainWindow):

View File

@ -1,11 +0,0 @@
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.

View File

@ -1,409 +0,0 @@
"""
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

View File

@ -1,6 +1,40 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
###
# qidenticon.py is Licesensed under FreeBSD License.
# (http://www.freebsd.org/copyright/freebsd-license.html)
#
# Copyright 2013 "Sendiulo". 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.
###
###
# 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.
###
""" """
qidenticon.py
identicon python implementation with QPixmap output
by sendiulo <sendiulo@gmx.net>
based on
identicon.py identicon.py
identicon python implementation. identicon python implementation.
by Shin Adachi <shn@glucose.jp> by Shin Adachi <shn@glucose.jp>
@ -11,19 +45,17 @@ by Shin Adachi <shn@glucose.jp>
>>> python identicon.py [code] >>> python identicon.py [code]
== python == == python ==
>>> import identicon >>> import qtidenticon
>>> identicon.render_identicon(code, size) >>> qtidenticon.render_identicon(code, size)
Return a PIL Image class instance which have generated identicon image. Return a PIL Image class instance which have generated identicon image.
```size``` specifies `patch size`. Generated image size is 3 * ```size```. ```size``` specifies `patch size`. Generated image size is 3 * ```size```.
""" """
# g
# PIL Modules
import Image
import ImageDraw
import ImagePath
import ImageColor
# we probably don't need all of them, but i don't want to check now
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import *
from PyQt4.QtGui import *
__all__ = ['render_identicon', 'IdenticonRendererBase'] __all__ = ['render_identicon', 'IdenticonRendererBase']
@ -114,45 +146,46 @@ class IdenticonRendererBase(object):
def render(self, size): def render(self, size):
""" """
render identicon to PIL.Image render identicon to QPixmap
@param size identicon patchsize. (image size is 3 * [size]) @param size identicon patchsize. (image size is 3 * [size])
@return PIL.Image @return QPixmap
""" """
# decode the code # decode the code
middle, corner, side, foreColor, backColor = self.decode(self.code) middle, corner, side, foreColor, backColor = self.decode(self.code)
# make image # make image
image = Image.new("RGB", (size * 3, size * 3)) image = QPixmap(QSize(size * 3, size * 3))
draw = ImageDraw.Draw(image)
# fill background # fill background
draw.rectangle((0, 0, image.size[0], image.size[1]), fill=0) image.fill(QtGui.QColor(0,0,0))
kwds = { kwds = {
'draw': draw, 'image': image,
'size': size, 'size': size,
'foreColor': foreColor, 'foreColor': foreColor,
'backColor': backColor} 'backColor': backColor}
# middle patch # middle patch
self.drawPatch((1, 1), middle[2], middle[1], middle[0], **kwds) image = self.drawPatchQt((1, 1), middle[2], middle[1], middle[0], **kwds)
# side patch # side patch
kwds['type'] = side[0] kwds['type'] = side[0]
for i in xrange(4): for i in xrange(4):
pos = [(1, 0), (2, 1), (1, 2), (0, 1)][i] pos = [(1, 0), (2, 1), (1, 2), (0, 1)][i]
self.drawPatch(pos, side[2] + 1 + i, side[1], **kwds) image = self.drawPatchQt(pos, side[2] + 1 + i, side[1], **kwds)
# corner patch # corner patch
kwds['type'] = corner[0] kwds['type'] = corner[0]
for i in xrange(4): for i in xrange(4):
pos = [(0, 0), (2, 0), (2, 2), (0, 2)][i] pos = [(0, 0), (2, 0), (2, 2), (0, 2)][i]
self.drawPatch(pos, corner[2] + 1 + i, corner[1], **kwds) image = self.drawPatchQt(pos, corner[2] + 1 + i, corner[1], **kwds)
return image return image
def drawPatch(self, pos, turn, invert, type, draw, size, foreColor,
def drawPatchQt(self, pos, turn, invert, type, image, size, foreColor,
backColor): backColor):
""" """
@param size patch size @param size patch size
@ -162,24 +195,43 @@ class IdenticonRendererBase(object):
# blank patch # blank patch
invert = not invert invert = not invert
path = [(0., 0.), (1., 0.), (1., 1.), (0., 1.), (0., 0.)] path = [(0., 0.), (1., 0.), (1., 1.), (0., 1.), (0., 0.)]
patch = ImagePath.Path(path)
polygon = QPolygonF([QPointF(x*size,y*size) for x,y in path])
rot = turn % 4
rot_trans = [QPointF(0.,0.), QPointF(size, 0.), QPointF(size, size), QPointF(0., size)]
rotation = [0,90,180,270]
# print rotation[rot], rot_trans[rot], turn
pen_color = QtGui.QColor(255, 255, 255, 0)
pen = QtGui.QPen(pen_color, Qt.SolidPattern)
foreBrush = QtGui.QBrush(foreColor, Qt.SolidPattern)
backBrush = QtGui.QBrush(backColor, Qt.SolidPattern)
if invert: if invert:
foreColor, backColor = backColor, foreColor foreBrush, backBrush = backBrush, foreBrush
mat = Matrix2D.rotateSquare(turn, pivot=(0.5, 0.5)) *\ painter = QPainter()
Matrix2D.translate(*pos) *\ painter.begin(image)
Matrix2D.scale(size, size) painter.setPen(pen)
patch.transform(mat.for_PIL()) painter.translate(pos[0]*size, pos[1]*size)
draw.rectangle((pos[0] * size, pos[1] * size, (pos[0] + 1) * size, painter.translate(rot_trans[rot])
(pos[1] + 1) * size), fill=backColor) painter.rotate(rotation[rot])
draw.polygon(patch, fill=foreColor, outline=foreColor)
painter.setBrush(backBrush)
painter.drawRect(0,0, size, size)
painter.setBrush(foreBrush)
painter.drawPolygon(polygon, Qt.WindingFill)
painter.end()
return image
### virtual functions ### virtual functions
def decode(self, code): def decode(self, code):
raise NotImplementedError raise NotImplementedError
class DonRenderer(IdenticonRendererBase): class DonRenderer(IdenticonRendererBase):
""" """
Don Park's implementation of identicon Don Park's implementation of identicon
@ -230,11 +282,14 @@ class DonRenderer(IdenticonRendererBase):
red = (code >> 27) & 0x1F red = (code >> 27) & 0x1F
foreColor = (red << 3, green << 3, blue << 3) foreColor = (red << 3, green << 3, blue << 3)
foreColor = QtGui.QColor(*foreColor)
bgcolor = QtGui.QColor(255,255,255)
return (middleType, middleInvert, 0),\ return (middleType, middleInvert, 0),\
(cornerType, cornerInvert, cornerTurn),\ (cornerType, cornerInvert, cornerTurn),\
(sideType, sideInvert, sideTurn),\ (sideType, sideInvert, sideTurn),\
foreColor, ImageColor.getrgb('white') foreColor, bgcolor
def render_identicon(code, size, renderer=None): def render_identicon(code, size, renderer=None):
@ -258,5 +313,6 @@ if __name__ == '__main__':
else: else:
code = int(code) code = int(code)
app = Qt.QApplication(sys.argv)
icon = render_identicon(code, 24) icon = render_identicon(code, 24)
icon.save('%08x.png' % code, 'PNG') icon.save('%08x.png' % code, 'PNG')