/** * Identicon.js 2.3.2 * http://github.com/stewartlord/identicon.js * * PNGLib required for PNG output * http://www.xarg.org/download/pnglib.js * * Copyright 2018, Stewart Lord * Released under the BSD license * http://www.opensource.org/licenses/bsd-license.php */ (function() { var PNGlib; if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { PNGlib = require('./pnglib'); } else { PNGlib = window.PNGlib; } var Identicon = function(hash, options){ if (typeof(hash) !== 'string' || hash.length < 15) { throw 'A hash of at least 15 characters is required.'; } this.defaults = { background: [240, 240, 240, 255], margin: 0.08, size: 64, saturation: 0.7, brightness: 0.5, format: 'png' }; this.options = typeof(options) === 'object' ? options : this.defaults; // backward compatibility with old constructor (hash, size, margin) if (typeof(arguments[1]) === 'number') { this.options.size = arguments[1]; } if (arguments[2]) { this.options.margin = arguments[2]; } this.hash = hash this.background = this.options.background || this.defaults.background; this.size = this.options.size || this.defaults.size; this.format = this.options.format || this.defaults.format; this.margin = this.options.margin !== undefined ? this.options.margin : this.defaults.margin; // foreground defaults to last 7 chars as hue at 70% saturation, 50% brightness var hue = parseInt(this.hash.substr(-7), 16) / 0xfffffff; var saturation = this.options.saturation || this.defaults.saturation; var brightness = this.options.brightness || this.defaults.brightness; this.foreground = this.options.foreground || this.hsl2rgb(hue, saturation, brightness); }; Identicon.prototype = { background: null, foreground: null, hash: null, margin: null, size: null, format: null, image: function(){ return this.isSvg() ? new Svg(this.size, this.foreground, this.background) : new PNGlib(this.size, this.size, 256); }, render: function(){ var image = this.image(), size = this.size, baseMargin = Math.floor(size * this.margin), cell = Math.floor((size - (baseMargin * 2)) / 5), margin = Math.floor((size - cell * 5) / 2), bg = image.color.apply(image, this.background), fg = image.color.apply(image, this.foreground); // the first 15 characters of the hash control the pixels (even/odd) // they are drawn down the middle first, then mirrored outwards var i, color; for (i = 0; i < 15; i++) { color = parseInt(this.hash.charAt(i), 16) % 2 ? bg : fg; if (i < 5) { this.rectangle(2 * cell + margin, i * cell + margin, cell, cell, color, image); } else if (i < 10) { this.rectangle(1 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image); this.rectangle(3 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image); } else if (i < 15) { this.rectangle(0 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image); this.rectangle(4 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image); } } return image; }, rectangle: function(x, y, w, h, color, image){ if (this.isSvg()) { image.rectangles.push({x: x, y: y, w: w, h: h, color: color}); } else { var i, j; for (i = x; i < x + w; i++) { for (j = y; j < y + h; j++) { image.buffer[image.index(i, j)] = color; } } } }, // adapted from: https://gist.github.com/aemkei/1325937 hsl2rgb: function(h, s, b){ h *= 6; s = [ b += s *= b < .5 ? b : 1 - b, b - h % 1 * s * 2, b -= s *= 2, b, b + h % 1 * s, b + s ]; return[ s[ ~~h % 6 ] * 255, // red s[ (h|16) % 6 ] * 255, // green s[ (h|8) % 6 ] * 255 // blue ]; }, toString: function(raw){ // backward compatibility with old toString, default to base64 if (raw) { return this.render().getDump(); } else { return this.render().getBase64(); } }, isSvg: function(){ return this.format.match(/svg/i) } }; var Svg = function(size, foreground, background){ this.size = size; this.foreground = this.color.apply(this, foreground); this.background = this.color.apply(this, background); this.rectangles = []; }; Svg.prototype = { size: null, foreground: null, background: null, rectangles: null, color: function(r, g, b, a){ var values = [r, g, b].map(Math.round); values.push((a >= 0) && (a <= 255) ? a/255 : 1); return 'rgba(' + values.join(',') + ')'; }, getDump: function(){ var i, xml, rect, fg = this.foreground, bg = this.background, stroke = this.size * 0.005; xml = "" + ""; for (i = 0; i < this.rectangles.length; i++) { rect = this.rectangles[i]; if (rect.color == bg) continue; xml += ""; } xml += "" return xml; }, getBase64: function(){ if (btoa) { return btoa(this.getDump()); } else if (Buffer) { return new Buffer(this.getDump(), 'binary').toString('base64'); } else { throw 'Cannot generate base64 output'; } } }; if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { module.exports = Identicon; } else { window.Identicon = Identicon; } })();