views:

31

answers:

3

I need to download a BMP with JavaScript and render it to the screen, in Internet Explorer. First off, yes, I know this is insane, I'm not going to get into why, let's just accept for a moment that img src is not working because of security constraints, but an ajax request with the proper authentication in the post will pull back the image. This example bypasses all the security for the sake of simplicity and just proves we can render something.

The best idea I could come up with was to fetch the stream via ajax, decode the bitmap, and then render it with canvas. Internet Explorer obviously doesn't support canvas, but luckily Google provided a wrapper to SVG called excanvas that I can use for that.

My code (drawing code appears to work, bmp decoding not so much)

http://gist.github.com/614328

Future support for other images besides BMP is plausable, and because of how the canvas works it's easiest to draw pixels in RGBA. Texture2D is essentially the wrapper class for an RGBA byte array, plus the drawing code. ByteStream makes it a bit easier on the eyes dealing with the byte array, and BitmapDecoder contains the method to translate the BGR format to RGBA texture2d for drawing.

Is it possible the bytes are getting mis-translated along the way or is there something the matter with my decoding logic?

FYI, I got the file spec from wikipedia:

http://en.wikipedia.org/wiki/BMP_file_format#Bitmap_Information_.28DIB_header.29

Any idea what's going on in the decoding logic or drawing logic that's causing my BMP to draw incorrectly?

A: 

Here's a much easier (and vastly more performant) approach: base64 encode the BMP data (you can do this either on the server or the client) and then embed it in the page using a data URI:

<script type="text/javascript">
  function fetchBmp() {
    $.get('http://localhost:3168/experimental/imgrender/beta.bmp', function (data) {
      var base64Data = $.base64.encode(data); // *

      $('#my-image').attr('src', 'data:image/bmp;base64,' + base64Data);
    });
  }

  // * Lots of plugins for this, e.g. http://github.com/carlo/jquery-base64
</script>    

<img id="my-image" />

All modern browsers support data URIs (including IE8 and up--for IE7 workarounds exist) as well as the BMP format.

As casablanca points out, there may be issues with loading binary data via Ajax, so you may have to google around for workarounds.

Jordan
I actually started with base64, but when it wasn't working switched to trying to render it myself and had better progress that way. It seems there are many limitations around base64, in particular the size. For very small images it's fine, but once you get into profile/headshot size avatar icons it falls apart. I tried this code, but I was getting an error in the decoder, still investigating
slf
+1  A: 

XMLHttpRequest (aka AJAX) was primarily designed for text content, so it's possible that binary data (especially null characters) aren't translated correctly. The first check would be to compare the size of retrieved data with the actual file size.

At least on Firefox, there seems to be a way to specifically retrieve binary data, as described here: Handling binary data.

casablanca
This looks very helpful, perhaps I can tap into TechNet or something else to try these same binary wire techniques with IE. I think something is definitely getting mistranslated on the wire. I remember back in the FTP days when I forgot to turn binary mode on before doing a get and I would get the same results, something about the sign bit being used as a checksum but I can't remember the details.
slf
this post led me in the correct direction, +1
slf
A: 

The fix was a combination of two things

  1. a bit of VBScript to read the raw bytes of responseBody
  2. decoding the byte data properly, each pixel is not padded as the wikipedia article suggests, it's actually each scanline that is padded to dword size.

Working code:

http://gist.github.com/616240

slf