Javascript generate transparent 1X1 pixel in dataURL format
Asked Answered
C

4

17

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.

Chrysalis answered 30/4, 2011 at 23:12 Comment(3)
Honestly, using canvas+toDataURL is likely the easiest solution. Otherwise, you're basically going to have to figure out how a 1x1 image of a given color and opacity is represented in whatever format (say, PNG) and then either use a JavaScript base64 encoder on that information, or figure out the pattern and shortcut the external encoding yourself. Is that really worth it?Aid
You might also find that using <canvas> is actually faster than a JavaScript base64 encoder.Aid
Thanks I guess I will stick with canvas.Chrysalis
A
6

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

Aid answered 1/5, 2011 at 1:15 Comment(4)
I optimized my answer a little more so it's not creating functions and lookup tables every time. It would be interesting to see how the optimized one stands up to canvas, as well.Interoffice
And, apparently, there's a native Base64 encoder in some browsers (window.btoa), so I just added that, too.Interoffice
Ok great, I updated the jsperf. I did call your function correctly, right? Your optimized version (using native btoa when present) blows the other 2 out of the water.Aid
Yeah, it makes sense. Concatenating a few strings together and then Base64 encoding it is going to be a lot faster than creating a canvas, rendering into it, encoding it as a PNG, and then encoding it into Base64. Regardless, my JavaScript native version is fairly useless; PAM is a very rare format, and is unlikely to be of use to many people.Interoffice
S
10

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.

Sink answered 25/11, 2015 at 14:25 Comment(0)
A
6

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

Aid answered 1/5, 2011 at 1:15 Comment(4)
I optimized my answer a little more so it's not creating functions and lookup tables every time. It would be interesting to see how the optimized one stands up to canvas, as well.Interoffice
And, apparently, there's a native Base64 encoder in some browsers (window.btoa), so I just added that, too.Interoffice
Ok great, I updated the jsperf. I did call your function correctly, right? Your optimized version (using native btoa when present) blows the other 2 out of the water.Aid
Yeah, it makes sense. Concatenating a few strings together and then Base64 encoding it is going to be a lot faster than creating a canvas, rendering into it, encoding it as a PNG, and then encoding it into Base64. Regardless, my JavaScript native version is fairly useless; PAM is a very rare format, and is unlikely to be of use to many people.Interoffice
J
4
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)

Jaysonjaywalk answered 10/8, 2011 at 1:7 Comment(0)
I
2

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.

Interoffice answered 1/5, 2011 at 0:21 Comment(3)
Cool idea, but output doesn't seem to display for me in firefox4 or IE8... :/ "Failed to load url".Triadelphous
@aaaidan: Of course; PAM is fairly uncommon. It makes sense Firefox and IE don't support it.Interoffice
@aaaidan: You can prove it works though by decoding the Base64 content into a .pam file and converting it with ImageMagick. Not that that's helpful for what the OP needed.Interoffice

© 2022 - 2024 — McMap. All rights reserved.