somewhere to talk about random ideas and projects like everyone else

stuff

July 2010 Archive

JavaScript <canvas> to (Animated) GIF 23 July 2010

This is the GIF which was generated from the canvas.

This is the raw canvas element saved as a non-animated PNG

I’ve tried this before but it didn’t work. <canvas> can’t do toDataURL('image/gif'), and the primitive GLIF library couldn’t do much so I never had the opportunity to test my gif-merging code that I had. But I’m at it again, this time, porting it from the AS3GIF library, an awesomely comprehensive bitmap to binary gif encoder that even supports LZW compression (and the patent has luckily expired. Yay!). AS3Gif is supposed to “play and encode animated GIFs”, but since web pages can usually natively play GIFs fine, it’s only a port of the GIFEncoder portions of the library. And it works really well. The rest of this post is copied from the Github readme. Interesting how the w2_embed/anonybot embed post was a blog post turned into readme, this is a readme turned into blogpost. I’ll start with a link to the Github repo anyway:

http://github.com/antimatter15/jsgif

Basic Usage

Since it pretty much is GIFEncoder, you could consult the as3gif how-to page

But there are some differences so I’ll cover it here anyway.!

This is the GIF which was generated from the canvas.

You first need to include the JS files. It’s probably best if you include it in this order, but it shouldnt’ matter too much.

<script type="text/javascript" src="LZWEncoder.js"></script>
<script type="text/javascript" src="NeuQuant.js"></script> 
<script type="text/javascript" src="GIFEncoder.js"></script>

If you want to render the gif through an inline <img> tag or try to save to disk or send to server or anything that requires conversion into a non-binary string form, you should probably include b64.js too.

<script type="text/javascript" src="b64.js"></script>

Simple enough right? Now to convert stuff to GIF, you need to have a working or at least some imageData-esque array.

<canvas id="bitmap"></canvas> 
<script> 
  var canvas = document.getElementById('bitmap'); 
  var context = canvas.getContext('2d'); 
  context.fillStyle = 'rgb(255,255,255)'; 
  context.fillRect(0,0,canvas.width, canvas.height); //GIF can't do transparent so do white 
  context.fillStyle = "rgb(200,0,0)"; 
  context.fillRect (10, 10, 75, 50); //draw a little red box

Now we need to init the GIFEncoder.

var encoder = new GIFEncoder();

If you are making an animated gif, you need to add the following

encoder.setRepeat(0); //0 -> loop forever //1+ -> loop n times then stop 
encoder.setDelay(500); //go to next frame every n milliseconds

Now, you need to tell the magical thing that you’re gonna start inserting frames (even if it’s only one).

encoder.start();

And for the part that took the longest to port: adding a real frame. encoder.addFrame(context);

In the GIFEncoder version, it accepts a Bitmap. Well, that doesn’t exist in Javascript (natively, anyway) so instead, I use what I feel is a decent analogue: the canvas context. However, if you’re in a situation where you don’t have a real <canvas> element. That’s okay. You can set the second parameter to true and pass a imageData.data-esque array as your first argument. So in other words, you can do encoder.addFrame(fake_imageData, true)as an alternative. However, you must do an encoder.setSize(width, height); before you do any of the addFrames if you pass a imageData.data-like array. If you pass a canvas context, then that’s all okay, because it will automagically do a setSize with the canvas width/height stuff.

Now the last part is to finalize the animation and get it for display.

encoder.finish(); 
var binary_gif = encoder.stream().getData() //notice this is different from the as3gif package! 
var data_url = 'data:image/gif;base64,'+encode64(binary_gif); 

Docs

Each of the files exposes a single global (see, at least it’s considerate!). But since there’s three files, that means that there’s three globals. But two of them are more of supporting libraries that I don’t totally understand or care about enough to document. So I’m just gonna document GIFEncoder.

new GIFEncoder() This is super parent function. You really don’t need the new keyword because It’s not really even using any special inheritance pattern. It’s a closure that does some var blah = exports.blah = function blah(){ for no good reason. Anyway, it returns an object with a bunch of methods that the section will be devoted to documenting. Note that I’ve never tested more than half of these, so good luck.

Boolean start() This writes the GIF Header and returns false if it fails.

Boolean addFrame(CanvasRenderingContext2D context) This is the magical magic behind everything. This adds a frame.

Boolean addFrame(CanvasPixelArray image, true) This is the magical magic behind everything. This adds a frame. This time you need you pass true as the second argument and then magic strikes and it loads your canvas pixel array (which can be a real array, I dont care and I think the program has learned from my constant apathy to also not care). But note that if you do, you must first manually call setSize which is happily defined just below this one.

void setSize(width, height) Sets the canvas size. It’s supposed to be private, but I’m exposing it anyway. Gets called automagically as the size of the first frame if you don’t do that crappy hacky imageData.data hack.

void setDelay(int milliseconds) the number of milliseconds to wait on each frame

void setDispose(int code) Sets the GIF frame disposal code for the last added frame and any subsequent frames. Default is 0 if no transparent color has been set, otherwise 2. I have no clue what this means so I just copypasted it from the actionscript docs.

void setFrameRate(Number fps) Sets frame rate in frames per second. Equivalent to setDelay(1000/fps). I think that’s stupid.

void setQuality(int quality) Sets quality of color quantization (conversion of images to the maximum 256 colors allowed by the GIF specification). Lower values (minimum = 1) produce better colors, but slow processing significantly. 10 is the default, and produces good color mapping at reasonable speeds. Values greater than 20 do not yield significant improvements in speed. BLAH BLAH BLAH. Whatever

void setRepeat(int iter) Sets the number of times the set of GIF frames should be played. Default is 1; 0 means play indefinitely. Must be invoked before the first image is added.

void setTransparent(Number color) Sets the transparent color for the last added frame and any subsequent frames. Since all colors are subject to modification in the quantization process, the color in the final palette for each frame closest to the given color becomes the transparent color for that frame. May be set to null to indicate no transparent color.

ByteArray finish() Adds final trailer to the GIF stream, if you don’t call the finish method the GIF stream will not be valid.

String stream() Yay the only function that returns a non void/boolean. It’s the magical stream function which should have been a getter which JS does support but I didnt’ feel like making it a getter because getters are so weird and inconsistent. Like sure there’s the nice pretty get thing but I think IE9/8 doesn’t implement it because it’s non standard or something and replaced it with a hideously ugly blah blah. So Anyway, it’s a function. It returns a byteArray with three writeByte functions that you wouldn’t care about and a getData() function which returns a binary string with the GIF. There’s also a .bin attribute which contains an array with the binary stuff that I don’t care about.

WebWorkers

The process isn’t really the fastest thing ever, so you should use WebWorkers for piecing together animations more than a few frames long. You can find the rest of the WebWorkers section on the actual readme, because the rest is just a huge block of code with comments.

http://github.com/antimatter15/jsgif


Experiment Ruby-style function auto-calling in Javascript 22 July 2010

I have no clue what it’s called when ruby automatically runs a function. After not totally understanding def.js and looking at this coffee-script issue from the cappuccino github issues browser. I had a horrible idea.

Function.prototype.valueOf = function(){return this()}

What does it do?

function getValue(){
    return 42
}
if(getValue == 42){
    alert('fortytwo')
}else{
    alert('not fortytwo')
}

Notice notably absent in the if statement are the expected parentheses ‘()’ needed to call them. It gets stranger still.

alert(getValue == 42); //true
alert(getValue() == 42); //true
alert(getValue() === 42); //true
alert(getValue === 42); //false
typeof getValue() //number
typeof getValue //function

var sample1 = getValue;
typeof(sample1) //function

var sample2 = getValue();
typeof(sample2) //number

One advantage is that it makes a super hacky/semi-working implementation of getters that should work universally (maybe). Except that it doesn’t. It’s practically useless.


ShinyTouch/OpenCV 15 July 2010

I have yet to give up entirely on ShinyTouch, my experiment into creating a touch screen input system which requires virtually no setup. For people who haven’t read my posts from last year, it works because magically things look shinier when you look at it from an angle. And so if you mount a camera at an angle (It doesn’t need to be as extreme as the screenshot above), you end up seeing a reflection on the surface of the screen (this could be aided by a transparent layer of acrylic or by having a glossy display, but as you can see, mine are matte, but they still work). The other pretty obvious idea of ShinyTouch, is that on a reflective surface, especially observed from a non-direct angle, you can see that the distance from the reflection (I guess my eighth grade science teacher would say the “virtual image”) to the apparent finger, or “real image” is twice the distance from either to the surface of the display. In other words, the reflection gets closer to you when you get closer to the mirror. A webcam usually gives a two-dimensional bitmap of data (and one non-spatial dimension of time). This gives (after a perspective transform) the X and Y positions of the finger. But an important aspect of a touchscreen and what this technology is also capable of, a “zero-touch screen”, is a Z axis: the distance of the finger and the screen. A touchscreen has a binary Z-axis: touch or no touch. Since you can measure the distance between the apparent real finger and it’s reflection, you can get the Z-axis. That’s how ShinyTouch works.

Last year someone was interested and actually contributed some code. Eventually we both agreed that my code was crap so he decided to rewrite it, this time using less PIL and pixel manipulation, and instead, opting for more OpenCV so it’s faster. The project died a bit early this year, and with nothing more to do, I decided to revive it. His code had some neat features:

  • Better perspective performs
  • Faster
  • Less Buggy
  • Simpler configuration (track bars instead of key combinations and editing JSON files)
  • Yellow square to indicate which corner to click when callibrating (actually, I wrote that feature) It was left however, at a pretty unfinished state. It couldn’t do anything more than generate config files through a nice UI and doing a perspective transform on the raw video feed. So in the last few days, I added a few more features.

  • Convert perspective-transformed code into grayscale

  • Apply a 6x6 gaussian blur filter
  • Apply a binary threshold filter
  • Copy it over to PIL and shrink the canvas by 75% for performance reasons
  • Hack a Python flood-fill function to do blob detection (because I couldn’t compile any python bindings for the opencv blob library)
  • Filter those blobs (sort of) Basically, it means ShinyTouch can now do multi-touch. Though the Z-axis processing, which is really what the project is all about still sucks. Like it sucks a lot. But when it does work (on a rare occasion), you get multitouch (yay). If TUIO gets ported (again), it’ll probably be able to interface with all the neat TUIO based apps.

Code here: http://github.com/antimatter15/shinytouch/ Please help, you probably don’t want to try it (yet).


Fluidizer fixed width → fluid width bookmarklet 14 July 2010

Yaay, fluid width!

I IZ NOM NOMMING ON HALF UR PIXELZ

This is another old one, something I made february fifteenth of this year.

One of the purely theoretical things that have always annoyed me, were fixed width themes. Because I love being a hypocrite, this blog right now is using a fixed width theme. Though I probably could make up an excuse, like to serve as a decent test case for Fluidizer in part of my grand scheme detailed via embedded steganographic messages inside all of my screenshots. Of course that’s all a lie (however much I would like otherwise).

It’s always bothered me how some web sites have these fixed-width layouts, sometimes with insanely thin boxes allocated to content. The vast majority of my screen becomes this orange blob of text. Chrome’s visual appearance motto is “Content not chrome”. That doesn’t help if the content is being obscured by the presentation of the content (gopher) might have solved that problem). Less chrome just means my eyes start to drown in +/- 1,732,405 pixels of orange. Even outside the extreme case, having a fixed-width layout isn’t efficient, and using something like Readability to only show the content removes the personality of the site or author, and only works on articles.

Fluidizer automatically resizes themes semi-pseudo-intelligently, and works with a lot of themes. It has several algorithms it uses, which are sort of alchemy-like and strange. I have no clue how it works, but there’s lots of strange stuff that goes on to magically do stuff. I think it even somehow has a CSS parser that uses CORS to load css through a server-side proxy.

Only tested in Chrome: [Fluidize]

javascript:(function(){function k(a){return(a||"").replace(/^\s+|\s+$/g,"")}function u(a,g){var b=new XMLHttpRequest;b.open("GET","http://anti15.chemicalservers.com/cssproxy.php?cssurl="+encodeURIComponent(a),true);b.onreadystatechange=function(){b.readyState==4&&b.status==200&&g(b.responseText)};b.send()}function o(){for(var a=0,g=0,b=false,h="*,div,p,body,span".split(",");!b&&g++<20;){if(document.body.scrollWidth>window.innerWidth){b=true;a-=100}else a+=100;for(var c=d.length;c--;){var f=d[c].text;try{for(var i=document.querySelectorAll(f),l=i.length;l--;){var n=i[l],p=parseFloat(d[c].width,10),q=d[c].width.replace(/^[\-\d\.]+/,"");if(q!="px")if(q=="em")p*=16;else console.warn("not used to handling non-px units");if(p>400&&h.indexOf(f)==-1){if(!n.a)n.a=p;n.style.width=n.a+a+"px"}}}catch(x){}}}try{c=d.length;a=0;for(var r;c--;)if(h.indexOf(d[c].text)==-1)try{var j=document.querySelectorAll(d[c].text);if(j.length==1&&j[0].getElementsByTagName("*").length>15&&(j[0]==document.body||j[0].parentNode==document.body||j[0].parentNode.parentNode==document.body))if(parseInt(d[c].width,10)>a){a=parseInt(d[c].width,10);r=j[0]}}catch(y){}if(a>500)r.style.width="100%"}catch(z){}}function v(a){function g(){var i=h.split(";").map(function(l){if(k(l.split(":")[0])=="width")return k(l.split(":")[1]);return""}).join("");b&&k(b)&&i&&k(i)&&d.push({text:k(b),width:i});h=b=""}for(var b="",h="",c=0,f=0;f<a.length;f++)if(a[f]=="{")c=1;else if(a[f]=="}"){g();c=0}else if(c==0)b+=a[f];else if(c==1)h+=a[f];g();o()}for(var d=[],s=document.styleSheets,t=s.length;t--;){var e=s[t];if(e.href&&!e.cssRules){console.log(e.href);u(e.href,v)}else{e=e.cssRules;for(var m=e.length;m--;){var w=e[m].selectorText;e[m].style&&e[m].style.width&&d.push({text:w,width:e[m].style.width})}}}window.addEventListener("resize",function(){setTimeout(function(){o()},100)});o()})();

Multicore Javascript Pi Computing with WebWorkers 14 July 2010

Selection_012
http://antimatter15.github.com/pi/partial.html It’s not using the fastest algorithm, but the nice thing about this one is that it’s capable of digit-extraction (calculating one section without knowing the digits before it) and was nice for doing distributed computing. This also has the nice side effect of working pretty well with the MapReduce paradigm (To calculate a block, calculate primes 3 to 2*N, map it all to the magical pi algorithm and then add it all up and truncate the fractional part, however it doesn’t really use MapReduce because there aren’t enough machines/threads to make it really necessary to distribute out the reducing part). So on the time-memory tradeoff scale, this algorithm uses low memory and is slower, which makes it pretty good for the purposes of something implemented with WebWorkers as I can’t imagine it would be good to have the same data multiplied by the number of threads and having lots of data being passed with each postMessage.

A Bright Coloured Fish Parsing ID3v2 Tags in Javascript (and ExtensionFM) 10 July 2010

Stolen from wikipedia

After having fun reinstalling Ubuntu, only to get extremely bored by the newfound stability and familiarity of 10.04, I upgraded to the Maverick Meerkat, where I was greeted by a friendly GRUB error. After consulting the Live CD a few times, I got into a normal desktop. Then I proceeded to install the two apps I ever use which aren’t included in Ubuntu: Chromium and Shutter (Maybe you could include git as well). After a bit of dismay as it seems Chromium didn’t sync extensions as advertised, I proceeded to reinstall my extensions. At least the issue where I couldn’t delete bookmarks was resolved.

It was also a neat opportunity to try out some new things, one of them was Neat Bookmarks after finding out that —bookmark-menu didn’t work (I only found it ever existed when it was announced that it was removed, though), which has a far prettier icon than some other bookmarks menu addon I had previously.

Another extension I tried out was Extension.FM. I’ve heard of it a while ago, but I never actually installed it for some reason, and there was a post on Ajaxian on it recently so I decided to try it out. The interface was neat (I knew that beforehand). I have an interesting (maybe not so much), system for storing my music on a local networked server (in my basement). So I went to it’s apache URL and browsed to the file location. ExtensionFM loaded it all and stuck it into my library (as advertised), but all the cover art, song name, album name and artist information was missing! Oh noes! Obviously it was missing ID3 data.

The great thing about Chrome, Firefox and Greasemonkey extensions, is that the source code is practically open to anyone who uses it. View source or otherwise, you can always get at it and hack stuff to make things right (ignoring the legal implications that ruin innovation). Some people think that there needs to be more done to secure the author’s source code from the user’s prying eyes, but that could not be further from the case. Hopefully this will be a case for why the source code should be exposed to the user.

So, after finding the location, which wasn’t the easiest thing as Chrome gives them these folder names based on a random generated public key which gives no hint on what the contents are. But as I only had a few plugins, getting ~/.config/chromium/Default/Extensions/ehohhddamheegbbkabfgegbaeminghlb/1.4.9_0/js wasn’t too hard. I found sound-parser.js and looked a little into it, and lo and behold, no ID3 parsing!

That’s probably expected as ID3v2 is a binary format, and JS was never that great at handling binary. I knew that there was an ID3v1 parser. But I wasn’t aware of an ID3v2 parser at the time, it seems that there is one that a quick google search would have saved me hours yesterday. So after implementing the spec, and googling several times why Picture Type $11 is “A bright coloured fish”, I hacked it onto song-parser.js and through means detailed here, I got ID3v2 support in ExtensionFM. So yay, now what was missing: cover art, album name, song title and artist now all work :)

Anyway, the project for the ID3v2 parser (mine, the one you shouldn’t use), is on github at http://github.com/antimatter15/js-id3v2. If you want a neat little online demo, just go to the URL below. I wouldn’t suggest any browser other than the Chromium nightlies or possibly Firefox 4.0 to view it as it relies on multi-file input and FileReader. Basically, this is how you use it: Press <Browse> and multi-select all the music that you have, and automagically, it reads it and parses ID3v2 data (all locally, mind you, so your secret collection of pirated music is not going to be sent to me for forwarding to the RIAA of your jurisdiction). It’s then shown at a 5 second slideshow. Cover art (if not present in the song) is of a probably copyright-infringing image of a sad fish that google referred me to because of this interesting fish theme.

http://antimatter15.com/misc/js-id3v2/id3v2.html <— Live Demo, you probably want to click it if you tl;dr’d this whole post.