I would like to know a way to generate a single pixel in JavaScript converting it to base64. The ideal function would be:
function createPixel(hexColor, opacity){
//...Calculate
return base64DataURL;
}
I am not very familiar with image processing. Any format is fine (png, gif etc). I would like to use this to overlay background images, (yes I could use rgba css3 but I am trying to place it with a background-image only on one element so i am not overlaying an element on top of another to achieve the effect).
Thanks in advance.
Edit: I would like not to use canvas, I am sure you can use canvas to get the base64 dataURL but I am sure it is not as fast as a string manipulation. Also I am not worried about converting an image into base64 but more interested in creating the image.
4+ years later, here's a simpler solution that generates a standard GIF so actually works in browsers (I couldn't get the PEM solution to work in anything) and is up to an order of magnitude faster than PEM/canvas. Only downside is GIF doesn't support alpha opacity - but that can be controlled via CSS.
It's based on this JSFiddle (unknown beautiful author), but with basic optimisation - reused keyStr and accepts both hex string ('#FF0000') and hex literal (0xFF0000) - latter much faster (thanks icktoofay).
<html>
<body onload="Test()">
<script>
function Test() {
var img = new Image;
img.src = createPixelGIF(0xff0000); // generate a red pixel data URI
img.height = 100;
img.width = 100; // optional: add dimensions
document.body.appendChild(img); // add to the page
}
// ROUTINES =============
var createPixelGIF = (function() {
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
return function createPixelGIF(hexColor) {
return "data:image/gif;base64,R0lGODlhAQABAPAA" + encodeHex(hexColor) + "/yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==";
}
function encodeHex(hexColor) {
var rgb;
if (typeof hexColor == 'string') {
var s = hexColor.substring(1, 7);
if (s.length < 6) s = s[0] + s[0] + s[1] + s[1] + s[2] + s[2];
rgb = [parseInt(s[0] + s[1], 16), parseInt(s[2] + s[3], 16), parseInt(s[4] + s[5], 16)];
} else
rgb = [(hexColor & (0xFF << 16)) >> 16, (hexColor & (0xFF << 8)) >> 8, hexColor & 0xFF];
return encodeRGB(rgb[0], rgb[1], rgb[2]);
}
function encodeRGB(r, g, b) {
return encode_triplet(0, r, g) + encode_triplet(b, 255, 255);
}
function encode_triplet(e1, e2, e3) {
enc1 = e1 >> 2;
enc2 = ((e1 & 3) << 4) | (e2 >> 4);
enc3 = ((e2 & 15) << 2) | (e3 >> 6);
enc4 = e3 & 63;
return keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4);
}
})();
</script>
</body>
</html>
Updated JSPerf results: http://jsperf.com/base64image/4 (code above is "createPixelGIF2"). You'll see I tried further optimisation (3+4), but it seems JS is happier with stack operations than hard-to-read combo functions :)
I also added an updated test for the canvas method, which for some reason excluded the instantiation of the canvas object - the biggest performance drag that would be seen in real world use.
Here's a fully cross-browser-compatible implementation using <canvas>
(demo @ jsfiddle).
var canvas = document.createElement('canvas');
// http://code.google.com/p/explorercanvas/wiki/Instructions#Dynamically_created_elements
if (!canvas.getContext) G_vmlCanvasManager.initElement(canvas);
var ctx = canvas.getContext('2d');
canvas.width = 1;
canvas.height = 1;
// for simplicity, assume the input is in rgba format
function createPixel(r, g, b, a) {
ctx.fillStyle = 'rgba(' + [r,g,b,a].join() + ')';
ctx.fillRect(0,0,1,1);
// 'data:image/png;base64,'.length => 22
return canvas.toDataURL('image/png','').substring(22);
}
I was curious to see how this stacks up against icktoofay's answer, performance-wise. Note, this will have to use excanvas for IE <9, which means that performance will almost certainly be worse there (but what's new).
Check out the jsperf: http://jsperf.com/base64image
function createPlaceholder(w, h) {
var img = document.createElement('img');
img.setAttribute('style', 'width:'+w+'px;height:'+h+'px;border:none;display:block');
img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
return img;
}
(for transparent pixel)
Try this. It uses a somewhat esoteric image format (PAM), but you said any format is fine, and it really does work! It's not optimized or anything, so it's probably pretty slow, but hey, it works.
Edit: Okay, I optimized it a little...
var createPixel=(function() {
var table=[];
for(var i=0;i<26;i++) {
table.push(String.fromCharCode("A".charCodeAt(0)+i));
}
for(var i=0;i<26;i++) {
table.push(String.fromCharCode("a".charCodeAt(0)+i));
}
for(var i=0;i<10;i++) {
table.push(i.toString(10));
}
table.push("+");
table.push("/");
function b64encode(x) {
var bits=[];
for(var i=0;i<x.length;i++) {
var byte=x.charCodeAt(i);
for(var j=7;j>=0;j--) {
bits.push(byte&(1<<j));
}
}
var output=[];
for(var i=0;i<bits.length;i+=6) {
var section=bits.slice(i, i+6).map(
function(bit) { return bit?1:0; });
var required=6-section.length;
while(section.length<6) section.push(0);
section=(section[0]<<5)|
(section[1]<<4)|
(section[2]<<3)|
(section[3]<<2)|
(section[4]<<1)|
section[5];
output.push(table[section]);
if(required==2) {
output.push('=');
}else if(required==4) {
output.push('==');
}
}
output=output.join("");
return output;
}
if(window.btoa) {
b64encode=window.btoa;
}
return function createPixel(hexColor, opacity) {
var colorTuple=[
(hexColor&(0xFF<<16))>>16,
(hexColor&(0xFF<<8))>>8,
hexColor&0xFF,
Math.floor(opacity*255)
];
var data="P7\nWIDTH 1\nHEIGHT 1\nDEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n";
colorTuple.forEach(function(tupleElement) {
data+=String.fromCharCode(tupleElement);
});
var base64DataURL="data:image/pam;base64,"+b64encode(data);
return base64DataURL;
}
})();
...but really, canvas
should work fine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With