Partial Identicon package

Pulled from https://github.com/PauloMigAlmeida/identicon on Jan. 4, 2016,
rev 96902d3c7c9733d9da4cce9c5ed424557fc2ec3c dated April 10, 2015.
Contains only the files we need.
Unmodified, changes to follow.
License: MIT
This commit is contained in:
zzz
2016-01-22 18:52:47 +00:00
parent ec27458393
commit cc21de3fee
5 changed files with 587 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
Identicon
=========
###License
(The MIT License)
Copyright (c) 2007-2014 Don Park <donpark@docuverse.com>
Contributor 2014-2014 Paulo Miguel Almeida Rodenas <paulo.ubuntu@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
###Compiling and Running it
+ On root path run this:
mvn clean install
+ To run the example webapp run this in /webappexample folder
mvn clean compile package tomcat7:run -Ptomcat
+ Example Urls
http://localhost:8080/9block?code=-2034984870&size=64
http://localhost:8080/9block?code=-2034954870&size=64
http://localhost:8080/9block?code=-2034894870&size=64

View File

@@ -0,0 +1,11 @@
package com.docuverse.identicon;
public interface IdenticonCache {
public byte[] get(String key);
public void add(String key, byte[] imageData);
public void remove(String key);
public void removeAll();
}

View File

@@ -0,0 +1,36 @@
package com.docuverse.identicon;
import java.awt.image.BufferedImage;
import java.math.BigInteger;
/**
* Identicon renderer interface.
*
* @author don
*
*/
public interface IdenticonRenderer {
/**
* Returns rendered identicon image for given identicon code encoded as a
* 32-bit signed integer.
*
* @param code
* identicon code
* @param size
* image size
* @return identicon image
*/
public BufferedImage render(int code, int size);
/**
* Returns rendered identicon image for given identicon code.
*
* @param code
* identicon code
* @param size
* image size
* @return identicon image
*/
public BufferedImage render(BigInteger code, int size);
}

View File

@@ -0,0 +1,169 @@
package com.docuverse.identicon;
import java.net.InetAddress;
import java.security.MessageDigest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Utility methods useful for implementing identicon functionality. Methods are
* class methods for convenience.
* <p>
* Key method of interest is {@link getIdenticonCode} which converts IP address
* into identicon code.<br>
* <strong>IMPORTANT</strong>: <code>inetSalt</code> value must be set to
* reasonably long random string prior to invoking this method.
* </p>
*
* @author don
*/
public class IdenticonUtil {
private static final Log log = LogFactory.getLog(IdenticonUtil.class);
private static final int DEFAULT_IDENTICON_SIZE = 16;
private static final int MINIMUM_IDENTICON_SIZE = 15;
private static final int MAXIMUM_IDENTICON_SIZE = 64;
private static final int DEFAULT_INET_MASK = 0xffffffff;
private static int inetMask = DEFAULT_INET_MASK;
private static String inetSalt;
/**
* Returns current IP address mask. Default is 0xffffffff.
*
* @return current IP address mask
*/
public static int getInetMask() {
return inetMask;
}
/**
* Sets current IP address mask. Default is 0xffffffff.
*
* @param inetMask
*/
public static void setInetMask(int inetMask) {
IdenticonUtil.inetMask = inetMask;
}
/**
* Returns current inetSalt value.
*
* @return
*/
public static String getInetSalt() {
return inetSalt;
}
/**
* Sets current inetSalt value.
*
* @param inetSalt
*/
public static void setInetSalt(String inetSalt) {
IdenticonUtil.inetSalt = inetSalt;
}
/**
* Returns identicon code for given IP address.
* <p>
* Current implementation uses first four bytes of SHA1(int(mask(ip))+salt)
* where mask(ip) uses inetMask to remove unwanted bits from IP address.
* Also, since salt is a string for convenience sake, int(mask(ip)) is
* converetd into a string and combined with inetSalt prior to hashing.
* </p>
*
* @param inetAddr
* IP address
* @return identicon code for <code>inetAddr</code>
* @throws Exception
*/
public static int getIdenticonCode(InetAddress inetAddr) throws Exception {
if (inetSalt == null)
throw new Exception(
"inetSalt must be set prior to retrieving identicon code");
byte[] ip = inetAddr.getAddress();
int ipInt = (((ip[0] & 0xFF) << 24) | ((ip[1] & 0xFF) << 16)
| ((ip[2] & 0xFF) << 8) | (ip[3] & 0xFF))
& inetMask;
StringBuilder s = new StringBuilder();
s.append(ipInt);
s.append('+');
s.append(inetSalt);
MessageDigest md;
md = MessageDigest.getInstance("SHA1");
byte[] hashedIp = md.digest(s.toString().getBytes("UTF-8"));
int code = ((hashedIp[0] & 0xFF) << 24) | ((hashedIp[1] & 0xFF) << 16)
| ((hashedIp[2] & 0xFF) << 8) | (hashedIp[3] & 0xFF);
return code;
}
/**
* Returns identicon code specified as an input parameter or derived from an
* IP address.
* <p>
* This method is a convenience method intended to be used by servlets like
* below:
* </p>
*
* <pre>
* int code = IdenticonUtil.getIdenticonCode(request.getParameter(&quot;code&quot;), request
* .getRemoteAddr());
* </pre>
*
* @param codeParam
* code parameter, if <code>null</code> remoteAddr parameter
* will be used to determine the value.
* @param remoteAddr
* HTTP requester's IP address. Optional if code was specified.
* @return
*/
public static int getIdenticonCode(String codeParam, String remoteAddr) {
int code = 0;
try {
if (codeParam != null) {
code = Integer.parseInt(codeParam);
} else {
code = IdenticonUtil.getIdenticonCode(InetAddress
.getByName(remoteAddr));
}
} catch (Exception e) {
log.error(e);
}
return code;
}
public static int getIdenticonSize(String param) {
int size = DEFAULT_IDENTICON_SIZE;
try {
String sizeParam = param;
if (sizeParam != null) {
size = Integer.parseInt(sizeParam);
if (size < MINIMUM_IDENTICON_SIZE)
size = MINIMUM_IDENTICON_SIZE;
else if (size > MAXIMUM_IDENTICON_SIZE)
size = MAXIMUM_IDENTICON_SIZE;
}
} catch (Exception e) {
log.error(e);
}
return size;
}
public static String getIdenticonETag(int code, int size, int version) {
StringBuilder s = new StringBuilder("W/\"");
s.append(Integer.toHexString(code));
s.append('@');
s.append(size);
s.append('v');
s.append(version);
s.append('\"');
return s.toString();
}
}

View File

@@ -0,0 +1,325 @@
package com.docuverse.identicon;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.math.BigInteger;
/**
* 9-block Identicon renderer.
*
* <p>
* Current implementation uses only the lower 32 bits of identicon code.
* </p>
*
* @author don
*
*/
public class NineBlockIdenticonRenderer2 implements IdenticonRenderer {
/*
* Each patch is a polygon created from a list of vertices on a 5 by 5 grid.
* Vertices are numbered from 0 to 24, starting from top-left corner of the
* grid, moving left to right and top to bottom.
*/
private static final int PATCH_GRIDS = 5;
private static final float DEFAULT_PATCH_SIZE = 20.0f;
private static final byte PATCH_SYMMETRIC = 1;
private static final byte PATCH_INVERTED = 2;
private static final int PATCH_MOVETO = -1;
private static final byte[] patch0 = { 0, 4, 24, 20 };
private static final byte[] patch1 = { 0, 4, 20 };
private static final byte[] patch2 = { 2, 24, 20 };
private static final byte[] patch3 = { 0, 2, 20, 22 };
private static final byte[] patch4 = { 2, 14, 22, 10 };
private static final byte[] patch5 = { 0, 14, 24, 22 };
private static final byte[] patch6 = { 2, 24, 22, 13, 11, 22, 20 };
private static final byte[] patch7 = { 0, 14, 22 };
private static final byte[] patch8 = { 6, 8, 18, 16 };
private static final byte[] patch9 = { 4, 20, 10, 12, 2 };
private static final byte[] patch10 = { 0, 2, 12, 10 };
private static final byte[] patch11 = { 10, 14, 22 };
private static final byte[] patch12 = { 20, 12, 24 };
private static final byte[] patch13 = { 10, 2, 12 };
private static final byte[] patch14 = { 0, 2, 10 };
private static final byte[] patchTypes[] = { patch0, patch1, patch2,
patch3, patch4, patch5, patch6, patch7, patch8, patch9, patch10,
patch11, patch12, patch13, patch14, patch0 };
private static final byte patchFlags[] = { PATCH_SYMMETRIC, 0, 0, 0,
PATCH_SYMMETRIC, 0, 0, 0, PATCH_SYMMETRIC, 0, 0, 0, 0, 0, 0,
PATCH_SYMMETRIC + PATCH_INVERTED };
private static int centerPatchTypes[] = { 0, 4, 8, 15 };
private float patchSize;
private GeneralPath[] patchShapes;
// used to center patch shape at origin because shape rotation works
// correctly.
private float patchOffset;
private Color backgroundColor = Color.WHITE;
/**
* Constructor.
*
*/
public NineBlockIdenticonRenderer2() {
setPatchSize(DEFAULT_PATCH_SIZE);
}
/**
* Returns the size in pixels at which each patch will be rendered before
* they are scaled down to requested identicon size.
*
* @return
*/
public float getPatchSize() {
return patchSize;
}
/**
* Set the size in pixels at which each patch will be rendered before they
* are scaled down to requested identicon size. Default size is 20 pixels
* which means, for 9-block identicon, a 60x60 image will be rendered and
* scaled down.
*
* @param size
* patch size in pixels
*/
public void setPatchSize(float size) {
this.patchSize = size;
this.patchOffset = patchSize / 2.0f; // used to center patch shape at
float patchScale = patchSize / 4.0f;
// origin.
this.patchShapes = new GeneralPath[patchTypes.length];
for (int i = 0; i < patchTypes.length; i++) {
GeneralPath patch = new GeneralPath(GeneralPath.WIND_NON_ZERO);
boolean moveTo = true;
byte[] patchVertices = patchTypes[i];
for (int j = 0; j < patchVertices.length; j++) {
int v = (int) patchVertices[j];
if (v == PATCH_MOVETO)
moveTo = true;
float vx = ((v % PATCH_GRIDS) * patchScale) - patchOffset;
float vy = ((float) Math.floor(((float) v) / PATCH_GRIDS))
* patchScale - patchOffset;
if (!moveTo) {
patch.lineTo(vx, vy);
} else {
moveTo = false;
patch.moveTo(vx, vy);
}
}
patch.closePath();
this.patchShapes[i] = patch;
}
}
public Color getBackgroundColor() {
return backgroundColor;
}
public void setBackgroundColor(Color backgroundColor) {
this.backgroundColor = backgroundColor;
}
public BufferedImage render(BigInteger code, int size) {
return renderQuilt(code.intValue(), size);
}
/**
* Returns rendered identicon image for given identicon code.
*
* <p>
* Size of the returned identicon image is determined by patchSize set using
* {@link setPatchSize}. Since a 9-block identicon consists of 3x3 patches,
* width and height will be 3 times the patch size.
* </p>
*
* @param code
* identicon code
* @param size
* image size
* @return identicon image
*/
public BufferedImage render(int code, int size) {
return renderQuilt(code, size);
}
protected BufferedImage renderQuilt(int code, int size) {
// -------------------------------------------------
// PREPARE
//
// decode the code into parts
// bit 0-1: middle patch type
// bit 2: middle invert
// bit 3-6: corner patch type
// bit 7: corner invert
// bit 8-9: corner turns
// bit 10-13: side patch type
// bit 14: side invert
// bit 15: corner turns
// bit 16-20: blue color component
// bit 21-26: green color component
// bit 27-31: red color component
int middleType = centerPatchTypes[code & 0x3];
boolean middleInvert = ((code >> 2) & 0x1) != 0;
int cornerType = (code >> 3) & 0x0f;
boolean cornerInvert = ((code >> 7) & 0x1) != 0;
int cornerTurn = (code >> 8) & 0x3;
int sideType = (code >> 10) & 0x0f;
boolean sideInvert = ((code >> 14) & 0x1) != 0;
int sideTurn = (code >> 15) & 0x3;
int blue = (code >> 16) & 0x01f;
int green = (code >> 21) & 0x01f;
int red = (code >> 27) & 0x01f;
// color components are used at top of the range for color difference
// use white background for now.
// TODO: support transparency.
Color fillColor = new Color(red << 3, green << 3, blue << 3);
// outline shapes with a noticeable color (complementary will do) if
// shape color and background color are too similar (measured by color
// distance).
Color strokeColor = null;
if (getColorDistance(fillColor, backgroundColor) < 32.0f)
strokeColor = getComplementaryColor(fillColor);
// -------------------------------------------------
// RENDER
//
BufferedImage targetImage = new BufferedImage(size, size,
BufferedImage.TYPE_INT_RGB);
Graphics2D g = targetImage.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setBackground(backgroundColor);
g.clearRect(0, 0, size, size);
float blockSize = size / 3.0f;
float blockSize2 = blockSize * 2.0f;
// middle patch
drawPatch(g, blockSize, blockSize, blockSize, middleType, 0,
middleInvert, fillColor, strokeColor);
// side patchs, starting from top and moving clock-wise
drawPatch(g, blockSize, 0, blockSize, sideType, sideTurn++, sideInvert,
fillColor, strokeColor);
drawPatch(g, blockSize2, blockSize, blockSize, sideType, sideTurn++,
sideInvert, fillColor, strokeColor);
drawPatch(g, blockSize, blockSize2, blockSize, sideType, sideTurn++,
sideInvert, fillColor, strokeColor);
drawPatch(g, 0, blockSize, blockSize, sideType, sideTurn++, sideInvert,
fillColor, strokeColor);
// corner patchs, starting from top left and moving clock-wise
drawPatch(g, 0, 0, blockSize, cornerType, cornerTurn++, cornerInvert,
fillColor, strokeColor);
drawPatch(g, blockSize2, 0, blockSize, cornerType, cornerTurn++,
cornerInvert, fillColor, strokeColor);
drawPatch(g, blockSize2, blockSize2, blockSize, cornerType,
cornerTurn++, cornerInvert, fillColor, strokeColor);
drawPatch(g, 0, blockSize2, blockSize, cornerType, cornerTurn++,
cornerInvert, fillColor, strokeColor);
g.dispose();
return targetImage;
}
private void drawPatch(Graphics2D g, float x, float y, float size,
int patch, int turn, boolean invert, Color fillColor,
Color strokeColor) {
assert patch >= 0;
assert turn >= 0;
patch %= patchTypes.length;
turn %= 4;
if ((patchFlags[patch] & PATCH_INVERTED) != 0)
invert = !invert;
Shape shape = patchShapes[patch];
double scale = ((double) size) / ((double) patchSize);
float offset = size / 2.0f;
// paint background
g.setColor(invert ? fillColor : backgroundColor);
g.fill(new Rectangle2D.Float(x, y, size, size));
AffineTransform savet = g.getTransform();
g.translate(x + offset, y + offset);
g.scale(scale, scale);
g.rotate(Math.toRadians(turn * 90));
// if stroke color was specified, apply stroke
// stroke color should be specified if fore color is too close to the
// back color.
if (strokeColor != null) {
g.setColor(strokeColor);
g.draw(shape);
}
// render rotated patch using fore color (back color if inverted)
g.setColor(invert ? backgroundColor : fillColor);
g.fill(shape);
g.setTransform(savet);
}
/**
* Returns distance between two colors.
*
* @param c1
* @param c2
* @return
*/
private float getColorDistance(Color c1, Color c2) {
float dx = c1.getRed() - c2.getRed();
float dy = c1.getGreen() - c2.getGreen();
float dz = c1.getBlue() - c2.getBlue();
return (float) Math.sqrt(dx * dx + dy * dy + dz * dz);
}
/**
* Returns complementary color.
*
* @param color
* @return
*/
private Color getComplementaryColor(Color color) {
return new Color(color.getRGB() ^ 0x00FFFFFF);
}
}