Sparklines for Minimalists #
If you’ve read Joe Gregorio’s Sparklines in data: URIs in Python, then you know that he’s presented us with an incredibly compelling use for data:
URIs. Namely, this is not an external file: But we don’t have Python’s image library, so what are we to do?
What if I offered you a handful of code that could generate inline sparkgraphs without library code? Presenting Bumpspark.
def bumpspark( results ) white, red, grey = [0xFF,0xFF,0xFF], [0,0,0xFF], [0x99,0x99,0x99] ibmp = results.inject([]) do |ary, r| ary << [white]*15 << [white]*15 ary.last[r/9,4] = [(r > 50 and red or grey)]*4 ary end.transpose.map do |px| px.flatten!.pack("C#{px.length}x#{px.length%4}") end.join ["BM", ibmp.length + 54, 0, 0, 54, 40, results.length * 2, 15, 1, 24, 0, 0, 0, 0, 0, 0]. pack("A2ISSIIiiSSIIiiII") + ibmp end
It’s got one known bug: graphs built from 16-element arrays get staticky. Free inky duck drawing to first patcher. (Keep reading.)
Great coats! jzp has dropped a ping version in the comments! MenTaL has a bump one with compression! Peewee graphics libs within!
MenTaLguY
Neat. I’ll have to play with this later if I get a chance.
Staticy images when you plot 16-element arrays is an interesting bug though.
Since I don’t have time to run through the code right now I’ll just pass along my gut feeling: I think you’re looking at byte alignment issues (perhaps due to rounding issues, with 16 being a likely boundary case). May also want to double-check the alignment requirements for rows imposed by the BMP format.
why
Incidentally, the
data:
URIs don’t work in IE. So if you’re getting a broken image…MenTaLguY
Hrm, I had a few minutes so I tried it and I couldn’t reproduce the bug. Could you provide a sample results array for which it fails?
DanielVF
The bitmap output size makes me cry. PNG ’s are so much smaller.
I thought about writing a PNG out-put-er – but I don’t want to deal with CRC ’s.
DanielVF
You do rock though.
MenTaLguY
Hmm, the resulting bitmaps are a bit in the large size, I suppose.
This should help a little bit (as well as make the code portable to non-little-endian architectures):
MenTaLguY
Incidentally, the blaahg preview eats my plus signs.
olleolleolle
Innocent bystander I am, clappings hands red.
Carl Youngblood
You could get around the IE problem by making it an HTML image and making the url point to a ruby script that returned a gif or png mime type and the actual byte content of the image.
jzp
The following hack on the original encodes the URLs in base64, and creates compressed PNGs for extra space saving.
Like MenTaLguY, my plus signs seem to go away in the preview.
jzp
Hmmn. Seem to have forgotten to include the URL encoding bit in the above post. Not sure if the
CGI.escape
ing is really necessary. The URL should be quoted withCGI.escapeHTML
anyway before getting put inside double quotes in an img tag.MenTaLguY
Carl, that’s wonderfully devious.
jzp, that’s excellent. I had forgotten I could have had zlib at my fingertips. While you’re at it, why not make an indexed PNG ?
MenTaLguY
As a matter of interest, I tried a version that produced RLE4 -compressed bitmaps (the sparklines are well-suited to that, since they skip every other row and thus always let us combine every two pixels).
PNG consistently wins over that at half the size.
Sam
SVG ?
why
MenTaLguY is now the overlord for RedHanded comments. I think I’m going to add a second layer of comments (to the right hand of every comment) where MenTaLguY can annotate. I’m brutally serious.
Krikey, jzp! Send me your address. It’s true that you get a duck. (MenTaL, send me yours too. You get a clock.)
Next up: we gotta make a 1k graphics lib for Ruby out of this.
Xian
Can we get the .pngs to have transparent backgrounds instead of white?
MenTaLguY
Yes, a transparent background is quite easy; this is untested, but I believe you need only modify build_png thusly:
The added tRNS chunk indicates that white ( 0xFFFF, 0xFFFF, 0xFFFF ) is to be interpreted as transparent.
why
Concerning jzp’s pung stuff: The sparklines are coming out upside down, unless I do a
transpose.reverse
. The transparency fix looks right, but it’s not right. Tinker tinker.jzp
Re: transparency: it’s also possible to use RGBA pixel values instead of RGB and have per-pixel alpha. Although, I don’t know whether any browsers actually render these properly.
Re: upside down: Oops! I should have tested more…
Re: indexed PNG : Haven’t tried it yet. I’m hoping that the compression of RGB pixels does a fairly good job. However, I suppose 4-bit indexes could reduce the data nicely. Makes packing a bit more complicated though!
I did try implementing the Up() filter which does a line-by-line delta (good since there’s lots of vertical redundancy in these sparklines), but the compressed data actually got bigger in my little test case (!), so I abandoned it.
Andronicus
Cant you do the same things with inline XBitmaps in HTML ?
Bil
As a red Tufte disciple, I find myself prostate to this sparkline development.
But, “honey, where’s my super
test/unit
suit?”yerejm
Requesting the continuous plot in Joe Gregorio’s updates.
hgs
This is good stuff. I think it would be great for adding barcodes to pages (though most readers emit light, so won’t read off the screen). But reading code off here in the narrow column for comments is tricky for me, even when I make the font too small for comfort. _Why, please could you copy the comments to your Bumpspark page, for easier access? Thank you.
why
hgs: The latest working ping spark is up on the Bumpspark page now. I haven’t been able to get MenTaL’s compressed bump to work yet. It’s close, but just not ready yet.
hgs
Thank you, that’s much easier to see. I may have something to contribute later…
MenTaLguY
Mine (the one I posted at least) isn’t really compressed; it just creates a 4bpp indexed BMP rather than a 24bpp truecolor one.
Out of curiousity, what problems are you having with with that version? I didn’t test it exhaustively, but I thought I checked all the corner cases.
MenTaLguY
Whoa, never mind. I see what you mean.
Argh… I think I uploaded the wrong version. And I’d just deleted my scratch files since I thought I’d preserved the final version for posterity here….
DanielVF
I love the pingspark! So much information in so few bytes.
red should be [0xFF,0,0] not [0,0,0xFF] in pingspark though.
MenTaLguY
Yeah, stride issues. I remember now. It’s the
x#{px.length&1}
bit.I don’t remember what the correct solution is. The alignment rules were a bit non-obvious.
DanielVF
If I’m correct Base64 encoded data does not need to be cgi escaped for data urls. It’s at least working well for me without the extra escaping.
Base 64 is just: 26 lower case letters 26 upper case letters 10 digets 1 ”+” 1 ”/”
64 characters.
MenTaLguY
Ah, I’m stumped. Even simple byte-alignment doesn’t work. Check this (using a 4bpp bumpspark with configurable byte alignment for rows): width/alignment matrices
DanielVF
Sparklines in use.
http://www.braino.org/blog/images/sparklines.gif
MenTaLguY
Oh. Duh. Look at the 4-byte alignment column.
I suspect that might start working if an extra byte of padding were added to the end of the odd-byte-width ones.
Let me try that…
MenTaLguY
(I have a feeling I never really had it quite working before, I was just “lucky” in picking my test cases)
MenTaLguY
Got it!
flgr
Regarding data not workin in IE: I think you can instead use a javascript:’data, it\’s going here’ style URLs to work around that. For images and so you might also have to overwrite the content-type which is possible when using instead of .
jix
core dumped
alas, this inline data stuff makes Privoxy (my filtering proxy) die, wit a quickness.
Tom
anyone else not able to see the data uri on this page in Firefox’s view source? On mine it just appears as white and I have to highlight it to see it. Seems to only be limited to the image on this page though. (filed bug 293875)
Geoff
For those looking for a less clever but more featureful RMagick implmentation, try my Sparkline Library for Ruby
And, it comes with a helper so you can use it with Rails in a cross-browser-friendly way!
Trejkaz
TiddlyWiki can build sparklines on the client side. Some helpers for doing the same thing in Rails would probably go a long way. Saves a lot of bandwidth, too, when you only have to pump the numbers down the pipe. :-)
Phillip
jjkllkjklljkljlkjlkj
mike
Does anybody know why some combinations don’t work? For example, this sequence of numbers gives me a blank image:
[15, 13, 61, 35, 79, 9, 59, 79, 73, 50, 82, 88, 14, 80, 81, 41, 43, 33, 3, 97, 75, 4, 42, 77, 46, 1]
Comments are closed for this entry.