I'm using the PNG library from here: http://www.xarg.org/download/pnglib.js and here's my code:
var h=20;
var w=20;
var p = new PNGlib(h, w, 256);
var background = p.color(0, 0, 0, 0); // set the background transparent
for (var i = 0; i < h; i++) {
for (var j = 0; j < w; j++) {
p.buffer[p.index(i, j)] = p.color(0xcc, 0xee, 0x00);
}
}
var pngStr = p.getDump();
var target1 = document.getElementById("target1");
target1.src = 'data:image/png;base64,'+btoa(pngStr);
//var pngStr = atob(target1.src.split(',')[1]);
//if(pngStr != pngStr2) alert("string don't match");
var pngbytes = new TextEncoder().encode(pngStr);
var blob = new Blob([pngbytes], {type: 'image/png'});
var urlCreator = window.URL || window.webkitURL;
var imageUrl = urlCreator.createObjectURL( blob );
var target2 = document.getElementById("target2");
target2.src = imageUrl;
As can be seen in this fiddle: https://jsfiddle.net/1c0ggvfy/3/
The base64 encoded version works, but the blob version doesn't. But this fiddle, which does the same thing, does work for a blob png: http://jsfiddle.net/ec9y6k9e/
The only difference I can see is that I have to get the data into a binary form to pass it to the Blob... I've tried a few different ways of doing that, but it seems to make no difference.
EDIT: As pointed out by Kaiido, the problem seems to be the TextEncoder. I had tried iterating through the string to fill a Uint8Array earlier, but at that time I had a second issue which was I was calling the Blob constructor without putting brackets around the first parameter. Here's working code:
var target1 = document.getElementById("target1");
target1.src = 'data:image/png;base64,'+p.getBase64();
var pngStr = atob(target1.src.split(',')[1]);
var bytes = new Uint8Array(pngStr.length);
for(var i=0; i<pngStr.length; i++){
bytes[i]= pngStr.charCodeAt(i);
}
var blob = new Blob([bytes], {type: 'image/png'});
var url = URL.createObjectURL(blob);
var target2 = document.getElementById("target2");
target2.src = url;
EDIT 2: A great answer would have included this: Chars in javascript are 2 bytes (https://mathiasbynens.be/notes/javascript-encoding) so thinking of them as equivalent to a byte can lead to unexpected results. TextEncoder is UTF-8 (https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder) so it represents characters with a code point higher than 127 is two bytes, where charCodeAt returns an integer representing the character (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt) - if that integer is 255 or less, it can be used in a Uint8Array to represent a single byte.
new TextEncoder().encode("\211")
->Uint8Array(2) [194, 137]
"\211".charCodeAt(0)
->137