BMP decoding in JavaScript and drawing to Explorer Canvas - javascript

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?

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.

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.

The fix was a combination of two things
a bit of VBScript to read the raw bytes of responseBody
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

Related

How to compare two base64 strings in Javascript and ignore some differences

I'm getting one base64 string from API response and other one I'm converted image (which is in test data file) to base64 using cypress readfile method.
When I'm using below command the assertion is failing because there is tracking number difference which will be always new with every call.
And I'm getting 2 different base64.
//This base64 is from API response
var base64FromAPI =
res.body.completedShipments[0].completedPackages[0].documents[0].image;
//Image is picked from Test Data file and converts to base64
cy.readFile(`cypress/e2e/Testdata/Canada Post 02StoreId.pdf`, "base64").should(
"eq",
base64FromAPI
);
Because there is tracking number on the label(image) which will be generated from API response is always different.
Is there any other way to compare base64 strings and to ignore some % of difference while comparing in cypress or javascript.
Or is there any other way to do this.
Thanks in advance.
Essentially you can't do this at the base64 level. The differences in a raw bitstream like base64 are totally meaningless. The differences can only become apparent through rendering that image. Actually, what you need to do is pretty complex! I'm assuming it's not possible or a good idea in your use case to change away from having the server add the text to the image, to for example, using DOM to overlay it instead.
If that's the case, the only thing you could do is utilise visual regression testing. With this, you can set a threshold on which a % similarity is defined.
Since the base64 comes from the API. This would probably mean also having test code that injects an img tag with the base64 as the source, so you can allow the visual snapshot to take place.
This works at the level of image analysis rather than on the actual bitstream. Internally it will render and compare the images.
Another way I can think of, though this is quite complex and I wouldn't pursue it unless the above did not work is to:
Use image manipulation libraries to load the base64 into an actual rendered image in memory.
Try to cut away/crop the superimposed text using image manipulation libraries in order to reliably remove areas of difference.
Base 64 that.
Compare that to a known stable base64 of the "rest" of the image.

Reading RGB bytes from a local image

I have an image stored as a png (I could convert it to bmp too). I want to open it in JavaScript and get the raw RGB bytes. It is enough if this works locally in Chrome. So I open ./index.html in the browser which loads an image in the same directory, e.g. with <img src=myimage.png>. However, I need the proper original data, without any compression or artifacts. I can't use NodeJS.
I saw a similar question, Get image data in JavaScript, but it requires that the image is hosted somewhere. I'm also not sure how to get the raw RGB bytes, the results I got from trying those examples looked like they were still encoded as png.
EDIT: As one of the answers to the other SO question mentions, a canvas will re-encode the data and reading from it won't give me exactly the same values as in the original.

Send and verify images via WebRTC

I need to send image data over WebRTC to another peer. My first thought was to use the base64 representation from the image. I get this data URI with the help of a Canvas. Works like a charm. But now I want to check if the data was not altered before sending.
The problem is, that the Canvas re-encodes the image and what's worth Firefox and Chrome encode the imageData differently. So I can't get matching SHA hashes.
Any ideas on how to solve this issue. Maybe a new approach to the whole problem? (WebRTC is mandatory though). Thanks!
Here is what I did... Basically I'm working with ArrayBuffers and Blobs now. I have a Blob of the image in question. Then I use FileReader.readAsArrayBuffer(blob) and and UInt8Array as a view on that data. Then I concatinate the bytes and compute a MD5 hash with https://github.com/satazor/SparkMD5. Concatination takes quite a while, so I only take every tenth byte in consideration. Which is supposedly quite a big security issue. So any tips on improving this process are very appreciated. As long as I or someone else comes up with a better idea, I will keep this the answer.

Is there some performance benefit using base64 encoded images?

For small size image what's (if any) the benefit in loading time using base64 encoded image in a javascript file (or in a plain HTML file)?
$(document).ready(function(){
var imgsrc = "../images/icon.png";
var img64 = "P/iaVYUy94mcZxqpf9cfCwtPdXVmBfD49NHxwMraWV/iJErLmNwAGT3//w3NB";
$('img.icon').attr('src', imgsrc); // Type 1
$('img.icon').attr('src', 'data:image/png;base64,' + img64); // Type 2 base64
});
The benefit is that you have to make one less HTTP request, since the image is "included" in a file you have made a request for anyway. Quantifying that depends on a whole lot of parameters such as caching, image size, network speed, and latency, so the only way is to measure (and the actual measurement would certainly not apply to everyone everywhere).
I should mention that another common approach to minimizing the number of HTTP requests is by using CSS sprites to put many images into one file. This would arguably be an even more efficient approach, since it also results in less bytes being transferred over (base64 bloats the byte size by a factor of about 1.33).
Of course, you do end up paying a price for this: decreased convenience of modifying your graphics assets.
You need to make multiple server requests, lets say you download a contrived bit of HTML such as:
<img src="bar.jpg" />
You already needed to make a request to get that. A TCP/IP socket was created, negotiated, downloaded that HTML, and closed. This happens for every file you download.
So off your browser goes to create a new connection and download that jpg, P/iaVYUy94mcZxqpf9cfCwtPdXVmBfD49NHxwMraWV/iJErLmNwAGT3//w3NB
The time to transfer that tiny bit of text was massive, not because of the file download, but simply because of the negotiation to get to the download part.
That's a lot of work for one image, so you can in-line the image with base64 encoding. This doesn't work with legacy browsers mind you, only modern ones.
The same idea behind base64 inline data is why we've done things like closure compiler (optimizes speed of download against execution time), and CSS Spirtes (get as much data from one request as we can, without being too slow).
There's other uses for base64 inline data, but your question was about performance.
Be careful not to think that the HTTP overhead is so massive and you should only make one request-- that's just silly. You don't want to go overboard and inline all the things, just really trivial bits. It's not something you should be using in a lot of places. Seperation of concerns is good, don't start abusing this because you think your pages will be faster (they'll actually be slower because the download for a single file is massive, and your page won't start pre-rendering till it's done).
It saves you a request to the server.
When you reference an image through the src-property, it'll load the page, and then do the additional request to fetch the image.
When you use the base64 encoded image, it'll save you that delay.

Accessing binary data from Javascript, Ajax, IE: can responseBody be read from Javascript (not VB)?

First of all, I am aware of this question:
How do I load binary image data using Javascript and XMLHttpRequest?
and specifically best answer therein, http://emilsblog.lerch.org/2009/07/javascript-hacks-using-xhr-to-load.html.
So accessing binary data from Javascript using Firefox (and later versions of Chrome which actually seem to work too; don't know about Opera). So far so good.
But I am still hoping to find a way to access binary data with a modern IE (ideally IE 6, but at least IE 7+), without using VB.
It has been mentioned that XHR.messageBody would not work (if it contains zero bytes), but I was wondering if this might have been resolved with newer versions; or if there might be alternate settings that would allow simple binary data access.
Specific use case for me is that of accessing data returned by a web service that is encoded using a binary data transfer format (including byte combinations that are not legal in UTF-8 encoding).
It's possible with IE10, using responseType=arraybuffer or blob. You only had to wait for a few years...
http://msdn.microsoft.com/en-us/library/ie/br212474%28v=vs.94%29.aspx
http://msdn.microsoft.com/en-us/library/ie/hh673569%28v=vs.85%29.aspx
Ok, I have found some interesting leads, although not completely good solution yet.
One obvious thing I tried was to play with encodings. There are 2 obvious things that really should work:
Latin-1 (aka ISO-8859-1): it is single-byte encoding, mapping one-to-one with Unicode. So theoretically it should be enough to declare content type of "text/plain; charset=ISO-8859-1" and get character-per-byte. Alas, due to idiotic logics of browsers (and even more idiotic mandate by HTML 5!), there is some transcoding occuring which changes high control character range (codes 128 - 159) in strange ways. Apparently this is due to mandatory assumption that encoding really is Windows-1252 (why? For some silly reasons.. but it is what it is)
UCS-2 is a fixed-length 2-byte encoding that predated UTF-17; and simply splits 16-bit character codes into 2 bytes. Alas, browsers do not seem to support it.
UTF-16 might work, theoretically, but there is the problem of surrogate pair characters (0xD800 - 0xDFFF) which are reserved. And if byte pairs that encode these characters are included, corruption occurs.
However: it seems to conversion for Latin-1 might be reversible, and if so, I bet I could make use of it after all. All mutations are from 1 byte (0x00 - 0xFF) into larger-than-byte values, and there are no ambiguous mappings at least for Firefox. If this holds true for other browsers, it will be possible to map values back and remove ill effects of automatic transcoding. And that would then work for multiple browsers, including IE (with the caveat of needing something special to deal with null values).
Finally, some useful links for conversions of datatypes are:
http://www.merlyn.demon.co.uk/js-exact.htm#IEEE (to handle floating points to/from binary IEEE representation)
http://jsfromhell.com/classes/binary-parser (for general parsing)
You can use the JScript "VBArray" object to get at these bytes in IE (without using VBScript):
var data = new VBArray(xhr.responseBody).toArray();
I guess answer is plain "no", as per this post: how do I access XHR responseBody (for binary data) from Javascript in IE?
(or: "use VBScript to help")

Categories

Resources