Skip to content

Instantly share code, notes, and snippets.

@BriSeven
Created November 22, 2012 22:20
Show Gist options
  • Save BriSeven/4133140 to your computer and use it in GitHub Desktop.
Save BriSeven/4133140 to your computer and use it in GitHub Desktop.
The pixtweet source code
(function () {
var base64URLAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
var baseRevMap = {};
var IDCounter = 0;
var pixelratio=1; //let's deal with the retina display bugs.
function ID (){
return IDCounter++;
}
base64URLAlphabet.split("").forEach(function (o, i) {
baseRevMap[o] = i;
});
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce (func, wait, immediate) {
var timeout, result;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) result = func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) result = func.apply(context, args);
return result;
};
};
function insertTwixel() {
if(window.devicePixelRatio === 2){
$("#pixeltime").length || $("div.home-tweet-box .tweet-content").before("<canvas id='pixeltime' width=512 height=512 style='width:256px; cursor: crosshair'/>");
$("#pixelpal").length || $("div.home-tweet-box .tweet-content").before("<canvas id='pixelpal' width=512 height=128 style='width:256px; cursor: pointer'/>")
pixelratio = 2;
} else {
$("#pixeltime").length || $("div.home-tweet-box .tweet-content").before("<canvas id='pixeltime' width=256 height=256 style='cursor: crosshair'/>");
$("#pixelpal").length || $("div.home-tweet-box .tweet-content").before("<canvas id='pixelpal' width=256 height=64 style='cursor: pointer'/>")
}
}
var css = {
border: "10px solid darkgray",
borderRadius: "10px"
}
Array.dim = function dim(dimension, initial) {
var a = [],
i;
for (i = 0; i < dimension; i += 1) {
a[i] = initial;
}
return a;
};
function rot(n, vec, rx, ry) {
"use strict";
var t;
if (ry === 0) {
if (rx === 1) {
vec.x = n - 1 - vec.x;
vec.y = n - 1 - vec.y;
}
//Swap x and y
t = vec.x;
vec.x = vec.y;
vec.y = t;
}
return vec;
}
//convert (x,y) to d
function xy2d(n, x, y) {
"use strict";
var rx, ry, s, d = 0,
vec = {
x: x,
y: y
};
for (s = n >> 1; s > 0; s = s>>1) {
rx = (vec.x & s) > 0;
ry = (vec.y & s) > 0;
d += s * s * ((3 * rx) ^ ry);
rot(s, vec, rx, ry);
}
return d;
}
function xy2d16(x,y){
var rmap= [0, 1, 14, 15, 16, 19, 20, 21, 234, 235, 236, 239, 240, 241, 254, 255, 3, 2, 13, 12, 17, 18, 23, 22, 233, 232, 237, 238, 243, 242, 253, 252, 4, 7, 8, 11, 30, 29, 24, 25, 230, 231, 226, 225, 244, 247, 248, 251, 5, 6, 9, 10, 31, 28, 27, 26, 229, 228, 227, 224, 245, 246, 249, 250, 58, 57, 54, 53, 32, 35, 36, 37, 218, 219, 220, 223, 202, 201, 198, 197, 59, 56, 55, 52, 33, 34, 39, 38, 217, 216, 221, 222, 203, 200, 199, 196, 60, 61, 50, 51, 46, 45, 40, 41, 214, 215, 210, 209, 204, 205, 194, 195, 63, 62, 49, 48, 47, 44, 43, 42, 213, 212, 211, 208, 207, 206, 193, 192, 64, 67, 68, 69, 122, 123, 124, 127, 128, 131, 132, 133, 186, 187, 188, 191, 65, 66, 71, 70, 121, 120, 125, 126, 129, 130, 135, 134, 185, 184, 189, 190, 78, 77, 72, 73, 118, 119, 114, 113, 142, 141, 136, 137, 182, 183, 178, 177, 79, 76, 75, 74, 117, 116, 115, 112, 143, 140, 139, 138, 181, 180, 179, 176, 80, 81, 94, 95, 96, 97, 110, 111, 144, 145, 158, 159, 160, 161, 174, 175, 83, 82, 93, 92, 99, 98, 109, 108, 147, 146, 157, 156, 163, 162, 173, 172, 84, 87, 88, 91, 100, 103, 104, 107, 148, 151, 152, 155, 164, 167, 168, 171, 85, 86, 89, 90, 101, 102, 105, 106, 149, 150, 153, 154, 165, 166, 169, 170];
if(x>=0&&y>=0&&x<16&&y<16){
return rmap[x+y*16];
}
}
//convert d to (x,y)
function d2xy(n, d) {
"use strict";
var rx, ry, s, t = d, vec = {
x: 0,
y: 0
};
for (s = 1; s < n; s *= 2) {
rx = 1 & (t / 2);
ry = 1 & t ^ rx;
rot(s, vec, rx, ry);
vec.x += s * rx;
vec.y += s * ry;
t /= 4;
}
return vec;
}
function rleDecode(data) {
var result = [];
if (data.length == 0) return result;
if ((data.length % 2) != 0) {
//alert("Invalid RLE data");
return;
}
for (var i = 0; i < data.length; i += 2) {
var val = data[i];
var count = data[i + 1];
for (var c = 0; c < count; c++)
result[result.length] = val;
}
return result;
}
// RLE compression reference implementation
function rleEncode(data) {
var result = [];
if (data.length == 0) return result;
var count = 1;
var r = 0;
for (var i = 0; i < (data.length - 1); i++) {
// If contiguous sequence ends,
// or the sequence reaches 60 elements in length, terminate sequence.
if (data[i] != data[i + 1] || data[i] >= 3 || count == 60) {
result[r] = data[i];
result[r + 1] = count;
count = 0;
r += 2;
}
count++;
}
result[r] = data[i];
result[r + 1] = count;
return result;
}
function base64Enc(data) {
return data.map(function (o) {
return base64URLAlphabet.charAt(o);
}).join("");
}
function base64Dec(str) {
return str.split("").map(function (o) {
return baseRevMap[o];
});
}
function hilbertise (data) {
var a = [];
data.forEach(function(o,i){
ai = xy2d16(i%16, Math.floor(i/16));
a[ai]=o;
});
return a;
}
function stack(data) {
var odd = data.filter(function (o, i) {
return (i & 1);
})
var even = data.filter(function (o, i) {
return !(i & 1);
})
var combine = even.map(function (o, i) {
return (odd[i] << 3) + o;
});
return combine;
}
function unstack(data) {
var uncombined = [];
data.forEach(function (o, i) {
var odd = o >> 3;
var even = o - (odd << 3);
uncombined[i * 2] = even;
uncombined[i * 2 + 1] = odd;
});
return uncombined;
}
function expand(data){
var hilb=[],diff,uns;
uns=data;
diff = 256/uns.length;
uns.forEach(function(o,i) {
var s = i*diff;
var c = 0;
var vec;
while(c< diff){
vec = d2xy(16,s+c);
hilb[vec.x+vec.y*width]=o||0;
c+=1;
}
});
return hilb;
}
function contract(data){
var tmp,exp=[],diff;
// tmp = data.filter(function (o, i) {
// return ((i>>1) & 1);
// });
// data.forEach(function(o,i) {
// var s = i*diff;
// var c = 0;
// var vec;
// while(c< diff){
// vec = d2xy(16,s+c);
// hilb[vec.x+vec.y*width]=o||0;
// c+=1;
// }
// });
return data;
}
function unscrew(text) {
if(text){
var isreal = true, incs;
var regex = /[^A-Za-z0-9\-_]+/g;
var out = text.replace( "#pixtweet", "").replace(regex,"");
var outs,decs,rle,uns, diff;
var head = out.charAt(0);
var tail = out.slice(1);
var palette = 0;
//check if it is RLE
if (head === "B") {
decs = base64Dec(tail);
rle = rleDecode(decs);
if(rle && rle.length) {
uns = unstack(rle);
} else {
uns = unstack(decs);
}
}
//check if it's just uncompressed data.
if(paletteNames.indexOf(head)!=-1) {
if(!uns || uns.length!==256){
if(uns && uns.length!==256){
isreal=false;
}
decs = base64Dec(tail);
uns = unstack(decs);
}
if(uns.length!==256){
isreal=false;
}
}
if(paletteNames.indexOf(head)>0){
palette=paletteNames.indexOf(head);
}
if(!uns){
decs = base64Dec(tail);
if(decs){
uns = unstack(decs);
}
palette = Math.floor(Math.random()*8)+1;
}
//interpret garbage.
console.log(uns)
if((head!=="A" && head !=="B" && uns && uns.length) || (uns && uns.length !==256)){
uns=expand(uns);
}
if(head==="A" || head==="B"){
uns=uns.map(function(o){
return [7,0,2,4,1,6,5,3][o];
});
palette=1;
}
if(uns){
return {pixels:uns,palette:palette};
}
}
}
window.unscrew=unscrew;
var paletteNames = ['A', "C","E","G","I","K","M","O","Q"];
var palettes = [
["#FFFFFF","#000000","#FF0000","#00FF00","#0000FF","#FFFF00","#00FFFF","#FF00FF"]
,["#000000","#000090","#e00000","#b620f0","#00f800","#30f0f8","#f8f800","#f8f8f8"]
,["#555f51","#d3565d","#ff5370","#ffc419","#ffb6b9","#bcedcf","#f7ff00","#f5ffb9"]
,["#000000","#0c4f80","#a0630d","#03b62b","#b59879","#52de6b","#85c9fc","#ffffff"]
,["#110d2d","#312849","#8e4d61","#f4216a","#e28b2f","#72bf91","#e9cf74","#ffffff"]
,["#07060a","#303035","#773c00","#9f2b2e","#585960","#7f818a","#8bdb00","#89bebf"]
,["#000000","#16240f","#2c491d","#426d2c","#57923a","#6db649","#83db57","#99ff66"]
,["#4c607c","#607494","#7c88ac","#88a0c8","#acb4d0","#b8c8e0","#d4e0ec","#f0f4fc"]
,["#292222","#595355","#747876","#959b90","#aaab9b","#d4cbb0","#e2dcca","#ffffff"]
]
window.palettes = palettes;
var palette = palettes[1];
var pixsize = 16;
var width = 16;
var height = 16;
var chooseColor = null;
function setPixel(ctx, x, y, p,c, pxs) {
// console.log("setpixel",x,y,p,c,pxs);
// if(x >= 0 && y >= 0 && x < width && y < height) {
pxs = pxs || pixsize;
x = Math.floor(x);
y = Math.floor(y);
if (ctx.pixbuffer) {
ctx.pixbuffer[y * width + x] = c;
ctx.pixbuffer.length=width*height;
}
// console.log(palettes[p][c]);
ctx.fillStyle = palettes[p][c];
ctx.fillRect(x * pxs , y * pxs, pxs, pxs);
// }
}
function initTwixel() {
insertTwixel();
var pixeltime = $("#pixeltime");
var pixelpal = $("#pixelpal");
var textarea = $("div.home-tweet-box .tweet-content textarea");
var originalURL="";
if (pixeltime.length && pixelpal.length) {
pixeltime.css(css).css({borderBottomLeftRadius:0, borderBottomRightRadius:0});
pixelpal.css({ borderLeft:"10px darkgray solid", borderRight:"10px darkgray solid" });
var selectedColor = 1;
var selectedPalette = 1;
var ctx = pixeltime[0].getContext("2d");
var palctx = pixelpal[0].getContext("2d");
ctx.pixbuffer = [];
ctx.scale(pixelratio,pixelratio);
palctx.scale(pixelratio,pixelratio);
function clearPixels(ctx, c) {
c = c || 0;
var pixbuffer = ctx.pixbuffer;
ctx.fillStyle = palette[c];
ctx.fillRect(0, 0, 256, 256);
var i = 0;
if (pixbuffer) {
for (i = 0; i < width * height; i += 1) {
pixbuffer[i] = c;
}
}
}
function setTweet(text) {
textarea[0].value = text;
if(!originalURL){
originalURL= $("#b").attr("href");
}
var url = originalURL + "&text="+encodeURIComponent(text);
$("#b").attr("href",url);
}
clearPixels(ctx,7);
paintPalette(palctx,palettes,selectedPalette,selectedColor);
var painting = false;
function paintStart(event){
painting=true;
paint(event);
return false;
}
function paintEnd(){
painting=false;
return false;
}
function paint (event) {
event.preventDefault();
if(painting){
var $ofs = pixeltime.offset();
var x = event.pageX - $ofs.left - 10.5;
var y = event.pageY - $ofs.top - 10.5;
var incs;
setPixel(ctx, x / pixsize, y / pixsize, selectedPalette, selectedColor);
updateText(ctx);
}
return false;
}
function updateText(ctx){
var h = hilbertise(ctx.pixbuffer);
var c = contract(h);
var s = stack(c);
var b = base64Enc(s);
out = paletteNames[selectedPalette] + b;
// console.log(h,s,b);
setTweet(out + " #pixtweet");
}
function pickColor(event){
var $ofs = pixelpal.offset();
var x = event.pageX - $ofs.left - 10.5;
var y = event.pageY - $ofs.top;
var pal,col;
// console.log(pixsize,y,x);
if(x>0 && y>0){
if(Math.floor(y / (pixsize * 2))){
pal = Math.floor(x / (pixsize * 2))+1;
} else {
col = Math.floor(x / (pixsize * 2));
}
}
chooseColor(col,pal,true);
//alert(selectedColor);
//setPixel(ctx,x/pixsize,y/pixsize,selectedColor);
}
chooseColor = function chooseColor(color,palette,updatetext){
if(palette !== undefined){
selectedPalette= palette;
paintCanvas(ctx,selectedPalette);
if(updatetext){updateText(ctx)};
} else {
selectedColor=color;
}
paintPalette(palctx,palettes,selectedPalette,selectedColor);
}
pixeltime.unbind().bind("mousedown",paintStart).bind("touchstart",paintStart).bind("touchend",paintEnd).bind("mouseup",paintEnd).bind("mouseout",paintEnd).bind("touchmove", paint).bind("mousemove", paint);
pixelpal.unbind().bind("click",pickColor).bind("touchstart",pickColor);
function textchange(){
setTimeout(function(){
var unscrewed = unscrew(textarea.attr("value") ) ;
if(unscrewed){
var pixies = unscrewed.pixels;
var pal = unscrewed.palette;
loadCanvas(ctx,pal, pixies, false);
}
},100);
}
textchange();
textchange = debounce(textchange,200);
textarea.bind("keydown",textchange).bind("keyup",textchange).bind("keypress",textchange).bind("paste",textchange);
}
}
function paintCanvas(ctx,palette,pixels){
//get size of canvas
var size = ctx.canvas.width/pixelratio;
pixels = pixels || ctx.pixbuffer;
pixels.forEach(function (o, i) {
setPixel(ctx, i % width, Math.floor(i / width), palette,o, size/width);
});
}
function loadCanvas(ctx,palette,pixels,updatetext){
ctx.pixbuffer=pixels.slice();
chooseColor(undefined,palette, updatetext);
}
function paintPalette(ctx,palettes,selectedPalette,selectedColor) {
var palette = palettes[selectedPalette];
palette.forEach(function (o, i) {
setPixel(ctx, i * 2, 0,selectedPalette, i);
setPixel(ctx, i * 2 + 1, 0, selectedPalette,i);
setPixel(ctx, i * 2, 1, selectedPalette, i);
setPixel(ctx, i * 2 + 1, 1, selectedPalette,i);
});
palettes.forEach(function(palette,i){
if(i!==0){
palette.forEach(function(color,j){
setPixel(ctx, ((j%2)*2)+(i-1)*4, Math.round((j-1)/2)+4, i,j, 8);
setPixel(ctx, ((j%2)*2+1)+(i-1)*4, Math.round((j-1)/2)+4, i,j, 8);
});
}
});
ctx.strokeStyle="#000000";
ctx.strokeRect(selectedColor*16*2+0.5,0.5,31,31);
ctx.strokeRect((selectedPalette-1)*16*2+0.5,32.5,31,31);
ctx.strokeStyle="#FFFFFF";
ctx.strokeRect(selectedColor*16*2+1.5,1.5,29,29);
ctx.strokeRect((selectedPalette-1)*16*2+1.5,33.5,29,29);
}
initTwixel();
$(".twitter-hashtag").filter(function (o) {
return $(this).text() === "#pixtweet";
}).map(function (i) {
var element = $(this).parent();
var avatar = element.parent().find(".avatar");
var canv;
var originaltext = $.trim(element.text());
var unscrwed = unscrew(originaltext);
if(unscrwed){
pixes = unscrwed.pixels;
pal = unscrwed.palette;
var id = ID();
if(element.next()[0].className!=="pixtweet"){
element.after("<a class='pixtweet' href='http://zenpsycho.com/pixtweet?pt="+originaltext+"'><div ><canvas width="+48*pixelratio+" height="+48*pixelratio+" style='width:48px' id=pixtweetID" + id + "/></div></a>");
element.hide();
canv = $("#pixtweetID" + id);
canv.attr("title",originaltext);
canv.before(avatar);
canv.parent().css(css).css({width:48*2,height:48});
} else{
canv=element.next().find("canvas");
}
var ctx = canv[0].getContext("2d");
ctx.save();
ctx.scale(pixelratio,pixelratio);
paintCanvas(ctx,pal,pixes);
ctx.pixbuffer=pixes.slice();
ctx.restore();
}
})
}())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment