cssAudio - ActiveCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - Activehtmlicon-personicon-teamoctocatspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External CSS

These stylesheets will be added in this order and before the code you write in the CSS editor. You can also add another Pen here, and it will pull the CSS from it. Try typing "font" or "ribbon" below.

Quick-add: + add another resource

Add External JavaScript

These scripts will run in this order and before the code in the JavaScript editor. You can also link to another Pen here, and it will run the JavaScript from it. Also try typing the name of any popular library.

Quick-add: + add another resource

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

            
              <section>
  <div>
<h3>Alpha rgb blacken & trim threshold:</h3>    
  <div class="slider athreshold"></div><span class="number">0</span>
  </div>
  <div>
  <h3>How to handle color of fully transparent pixels:</h3>
  <label><input type="radio" name="tpixels" value="avg" checked /> Use average color</label><br/>
  <label><input type="radio" name="tpixels" value="exp"/> Expand color</label>
    <label><input type="radio" name="tpixels" value="expblur"/> Expand color with blur</label>
  </div>
  <div>
  <h3>Upload transparent PNG File:</h3>
  <input type="file" id="files" name="files[]"/><br/>
  <label><input type="checkbox" name="trim"/> Trim Transparent areas</label>
  </div>
</section>
<section>
  <div>
  <h3>Compression:</h3>
    <div class="slider comp"></div><span class="number">65%</span>  
  </div>
  <div>
    <h3>Result:</h3>
    <output id="list"></output>
  </div>
  <div>
    <h3>Should I use SVG or Canvas?</h3>
    <p>
      SVG is easier to implement and doesn't need any JavaScript. But if you plan to animate the image, each frame will be recalculated by the browser if you rotate or scale it - which might be slow.
    </p>
    <p>
      Canvas needs some JavaScript to work and maybe some time for pre-calculation (at least for huge images this will be noticeable). Use this if you want to animate the image later on. A Canvas will act just like a transparent PNG - nothing to be recalculated once it is filled with pixels.
    </p>
  </div>
  <div>
  <h3>Code SVG (with external JPG source, use for inline):</h3>
  <textarea id="code" class="code"></textarea>    
  </div>
  <div>
    <h3>Code SVG with embedded JPG source, this can be put inline in HTML code.</h3>
    <textarea id="codeembedded" class="code"></textarea>
  </div>
  <div>
    <h3>Now it gets deep: complete SVG with embedded image as "optimized encoded" data URL!</h3>
    <p>You can use this Data-URL as image source. It will gzip better than base64-encoding the svg - containing already a base64-encoded image - again.<br/>    
    Big thanks go to Taylor Hunt for <a href="https://codepen.io/tigt/post/optimizing-svgs-in-data-uris" target="_blank">pointing out how to do this properly.</a><br/>
      They're very easy to use: <a href="http://codepen.io/ninili/pen/XjKzKB" target="_blank">Take a look at this pen</a>
    </p>
      <textarea id="codesvg64" class="code"></textarea>
  </div>
  <div>
    <h3>Code Canvas:</h3>
    <a href="http://codepen.io/ninili/pen/zKArEg" target="_blank">Take a look at this pen</a>
  </div>
    <div>
    <h3>Image Base64 (for Data URLs):</h3>
      <p>
      you might notice that some JPGs, especially with large empty areas yield a lot of repetitions in the base64 code and you wonder why that is, because you always thought of JPG to be a highly compressed image format. But the JPGs the browser creates don't have optimized huffman tables (the option you can check in Photoshop "Save as Web" => "optimized")
      </p>
    <textarea id="base64" class="code"></textarea>
  </div>
</section>
            
          
!
            
              body {
  background:#000;
  color:#ffffff;
  padding:15px;
}
* {
  box-sizing:border-box;
}
h1,h2,h3 {
  margin:10px 0;
  //background:linear-gradient(to right,rgba(51,51,51,0),#333 50%), linear-gradient(to bottom, #000,rgba(0,0,0,0));
  background:linear-gradient(#777,rgba(255,255,255,0));
  // border:1px dotted #ccc;
  text-shadow:1px 1px 2px rgba(0,0,0,0.75);
  box-shadow:0px -2px 2px rgba(0,0,0,0.5);
  //border-bottom:none;
  border-radius:10px 10px 0 0;
  padding:4px 10px;
}
.slider {
  width:300px;
  display:inline-block;
}
.container > * {
  max-width:50%;
}
.container:hover > svg {
  max-width:none;
}
a {
  color:#eeeeee;
}
.code {
  width:95%;
  height:20vh;
  margin:20px;
}
section {  
  border:#000 2px solid;
  padding:20px;
  background:radial-gradient(#999,#333),linear-gradient(#666 0px,rgba(0,0,0,0) 15px),linear-gradient(to top,#666 0px,rgba(0,0,0,0) 15px),linear-gradient(to right, #666 0px, rgba(0,0,0,0) 20px),linear-gradient(to left, #666 0px, rgba(0,0,0,0) 20px);
  &:not(:last-of-type) {
     border-bottom:none; 
  }  
  &:first-of-type {
    border-radius:20px 20px 0 0;
    column-width: 400px;
  }
  &:last-of-type {
    border-radius:0 0 20px 20px;
  }
  > div:not(.slider) {
    break-inside:avoid;
    padding:5px 10px 15px 10px;  
  }
  .number, a[download], .container span {
    font-size:20px;
    margin:5px;
    padding:10px;
    background:#333;
    border-radius:10px;
  }
  .container {
    a, span {
      display:block;
      text-align:center;
    }
  }
  input {
    margin-top:10px;
  }
}
            
          
!
            
                var imageCVs = [],
      $output = $('#list'),
      mskID = 0;

  function trimImageData(imgData) {
    var top = null,
      bottom = null,
      left = null,
      right = null,
      dir = 1,
      iw4 = imgData.width * 4,
      thresh = $('.slider.athreshold').slider("values", 0);
    outer:
      for (var yc = 0; yc < imgData.height; yc += dir) {
        var aa = false,
          pos = yc * iw4 + 3;
        for (var xc = 0; xc < imgData.width; xc++, pos += 4) {
          if (imgData.data[pos] > thresh) {
            aa = true;
            break;
          }
        }
        if (aa && dir > 0 && top === null) {
          top = yc;
          dir = -1;
          yc = imgData.height;
        } else if (aa && dir < 0 && bottom === null) {
          bottom = yc + 1;
          break outer;
        }
      }
    console.log("Bottom:" + bottom + " (" + imgData.height + ")");
    if (top === null) top = 0;
    if (bottom === null) bottom = imgData.height;
    dir = 1;
    var topOff = top * iw4;
    outer2:
      for (var xc = 0; xc < imgData.width; xc += dir) {
        var pos = topOff + (xc * 4) + 3,
          aa = false;
        for (var yc = top; yc < bottom; yc++, pos += iw4) {
          if (imgData.data[pos] > thresh) {
            aa = true;
            break;
          }
        }
        if (aa && dir > 0 && left === null) {
          left = xc;
          dir = -1;
          xc = imgData.width;
        } else if (aa && dir < 0 && right === null) {
          right = xc + 1;
          break outer2;
        }
      }
    console.log("left:" + left + " right:" + right + "(" + imgData.width + ")");
    if (left === null) left = 0;
    if (right === null) right = imgData.width;
    if (left == 0 && top == 0 && bottom == imgData.height && right == imgData.width) return imgData;
    var cv = $('<canvas/>')[0],
      ctx = cv.getContext('2d');
    cv.width = right - left;
    cv.height = bottom - top;
    ctx.putImageData(imgData, -left, -top);
    imgData = ctx.getImageData(0, 0, cv.width, cv.height);
    return imgData;
  }

  var img;

  function reMask() {
    if (img) {
      $(img).trigger('load');
    } 
  }

  function handleFileSelect(evt) {
    var files = evt.target.files;    
    for (var i = 0, f; f = files[i]; i++) {      
      if (!f.type.match('image.*')) {
        continue;
      }
      var reader = new FileReader();
      
      reader.onload = (function(theFile) {
        return function(e) {

          var $cv = $('<canvas/>'),
            cv = $cv[0],
            ctx = cv.getContext('2d'),
            imgData,
            imgDataAlpha,            
            $span = $('<span/>'),
            $jpg = $('<img/>'),
            paintBack = false;
          
          img = new Image();
          img.onload = function() {
            var w = img.width,
                h = img.height;
            cv.width = w;
            cv.height = h * 2;
            ctx.drawImage(img, 0, 0);
            imgData = ctx.getImageData(0, 0, w, h);
            if ($('#trim').prop('checked')) {
              imgData = trimImageData(imgData);
              w = imgData.width;
              h = imgData.height;
              cv.width = w;
              cv.height = h * 2;
            }            
            imgDataAlpha = ctx.createImageData(w, h);
            
            var fillMode = $('input[name=tpixels]:checked').val(),
                pos = 0,
                imgDataCopy = new Uint8ClampedArray(imgData.data),
                thresh = $('.slider.athreshold').slider("values", 0);                
            
            if (thresh < 1) thresh = 1;            
            if (fillMode == 'avg') {
              var allR, allG, allB, ca = 0;
              allR = 0;
              allG = 0;
              allB = 0;
              for (var yc = 0; yc < h; yc++) {
                var rowR = 0,
                    rowG = 0,
                    rowB = 0,
                    cRow = 0;
                for (var xc = 0; xc < w; xc++, pos += 4) {
                  var a = imgData.data[pos + 3];
                  imgData.data[pos + 3] = 0xff;
                  if (a < thresh) {
                    a = 0;
                  } else {
                    rowR += imgData.data[pos];
                    rowG += imgData.data[pos + 1];
                    rowB += imgData.data[pos + 2];
                    cRow++;
                  }
                  imgDataAlpha.data[pos] = a;
                  imgDataAlpha.data[pos + 1] = a;
                  imgDataAlpha.data[pos + 2] = a;
                  imgDataAlpha.data[pos + 3] = 0xff;
                }
                if (cRow) {
                  rowR /= cRow;
                  rowG /= cRow;
                  rowB /= cRow;
                  allR += rowR;
                  allG += rowG;
                  allB += rowB;
                  ca++;
                }
              }
              if (ca) {
                allR /= ca;
                allG /= ca;
                allB /= ca;
              }
              for (pos = 0, yc = 0; yc < h; yc++) {
                for (xc = 0; xc < w; xc++, pos += 4) {
                  var a = imgDataCopy[pos + 3];
                  if (a < thresh) {
                    imgData.data[pos] = allR;
                    imgData.data[pos + 1] = allG;
                    imgData.data[pos + 2] = allB;
                  }
                }
              }
            } else {              
              var lastPos = false;
              var spans = [];
              pos = w*h*4;
              while (pos-=4) {
                var a = imgDataCopy[pos+3];
                if (a < thresh) {
                  a=0;
                  if (lastPos===false) lastPos = pos;
                } else {
                  if (lastPos!==false) {
                    spans.push({
                      r:imgData.data[pos],
                      g:imgData.data[pos+1],
                      b:imgData.data[pos+2],
                      from:pos+4,
                      to:lastPos
                    });
                    lastPos = false;
                  }
                }
                imgDataAlpha.data[pos] = a;
                imgDataAlpha.data[pos + 1] = a;
                imgDataAlpha.data[pos + 2] = a;
                imgDataAlpha.data[pos + 3] = 0xff;
                imgData.data[pos+3] = 255;
              }
              if (lastPos!==false && spans.length) {
                var lastSpan = spans[spans.length-1];
                spans.push({
                  r:lastSpan.r,
                  g:lastSpan.g,
                  b:lastSpan.b,
                  from:0,
                  to:lastPos
                });
              }
              var i = spans.length;
              while (i--) {
                var span = spans[i];
                for (pos = span.from; pos <= span.to; pos+=4) {                               imgData.data[pos] = span.r;
                  imgData.data[pos+1] = span.g;
                  imgData.data[pos+2] = span.b;
                  
                }
              }
              
              if (fillMode=="expblur") {
                for (var i=0;i<8;i++) {
                 for (xc=0;xc<w;xc++) {
                   pos = xc*4;
                   var lastColorR = -1;
                   var lastColorB = -1;
                   var lastColorG = -1;
                   for (yc=0;yc<h;yc++,pos+=w*4) {
                     var a = imgDataCopy[pos+3];
                     if (a<thresh) {
                       if (lastColorR>=0) {
                         var r = imgData.data[pos],
                             g = imgData.data[pos+1],
                             b = imgData.data[pos+2];
                         imgData.data[pos] = lastColorR;
                         imgData.data[pos+1] = lastColorG;
                         imgData.data[pos+2] = lastColorB;
                         lastColorR += r;
                         lastColorG += g;
                         lastColorB += b;
                         lastColorR *=0.5;
                         lastColorG *=0.5;
                         lastColorB *=0.5;
                       } else {
                         lastColorR = imgData.data[pos];
                         lastColorG = imgData.data[pos+1];
                         lastColorB = imgData.data[pos+2];
                       }
                     }
                   }
                 }
               }
              }
            }

            ctx.clearRect(0, 0, cv.width, cv.height);
            ctx.putImageData(imgData, 0, 0);
            ctx.putImageData(imgDataAlpha, 0, h);
            imageCVs.push(cv);
            insertCVasJPG(cv);
          }
          img.src = e.target.result;
        };
      })(f);

      // Read in the image file as a data URL.
      reader.readAsDataURL(f);
    }
  }

  function alphaSVG(imgURL,w,h) {
    var h2 = h * 0.5;
    var svg =
        '<defs><mask id="msk">'+
        '<image xlink:href="' + imgURL + '" x="0" y="' + (-h2) + '" width="' + w + '" height="' + h + '"/>'+
        '</mask></defs>' +
      '<image xlink:href="' + imgURL + '" width="' + w + '" height="' + h + '" mask="url(#msk)"/>';
    return svg;
  }

function svgCode(w,h) {
  var h2 = h*0.5;
  var svg =
      "<!-- replace myImage.jpg with actual image -->\n"+
      "<!-- remember to use different mask IDs for multiple images in one document -->\n"+
      "<svg width=\""+w+"\" height=\""+h2+"\">\n"+
      alphaSVG('myImage.jpg',w,h)+"\n"+
       "</svg>";
  return svg;
}

function svgCodeEmbedded(imgURL, w, h) {
  var h2 = h*0.5;
  var svg = 
      "<svg width=\""+w+"\" height=\""+h2+"\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"+
      "\t<defs>\n"+
      "\t\t<image id=\"i\" x=\"0\" y=\"0\" width=\""+w+"\" height=\""+h+"\" xlink:href=\""+imgURL+"\" />\n"+
      "\t\t<mask id=\"m\"><use xlink:href=\"#i\" transform=\"translate(0,"+(-h2)+")\"/></mask>\n"+
      "\t</defs>"+
      "<use xlink:href=\"#i\" mask=\"url(#m)\"/>\n"+
      "</svg>";  
  return svg;
}

function urlEncodeSVG(svg) {
  svg = svg.replace(/"/g,"'").replace(/[\t\n\r]/g,'').replace(/[%&#<>{}]/g,function(ch) {return '%'+ch.charCodeAt(0).toString(16);});
  return 'data:image/svg+xml,'+svg;
}

  function testAsSVG(imgURL, w, h) {
    var h2 = h * 0.5,
      svg =
      '<svg width="' + w + '" height="' + h2 + '"><pattern id="muster_a" patternUnits="userSpaceOnUse" width="40" height="40" x="0" y="0"><g stroke="none" fill="#666666"><rect width="20" height="20"/><rect width="20" height="20" x="20" y="20" /></g><g stroke="none" fill="#aaaaaa"><rect width="20" height="20" x="20"/><rect width="20" height="20" y="20"/></g></pattern><rect width="' + (w + 80) + '" height="' + (h2 + 40) + '" fill="url(#muster_a)"><animateTransform attributeName="transform" type="translate" dur="3s" from="-80,-40" to="0,0" repeatCount="indefinite"/></rect>' +
      alphaSVG(imgURL,w,h) +
      '</svg>';
    mskID++;
    return svg;
  }

  function insertCVasJPG(cv) {
    var sliderValue = $('.slider.comp').slider("values", 0) * 0.01,
      dataURL = cv.toDataURL('image/jpeg', sliderValue),
      $img = $('<img/>'),
      $span = $('<span/>'),
      $dl = $('<a/>'),
      $container = $('<div/>').addClass('container');
    $('.slider.comp').next().text(Math.round(sliderValue*100)+'%');
    
    $output.empty();
    $('#base64').text(dataURL);
    $img.attr('src', dataURL);
    $dl.attr('href', dataURL).text('download JPG').attr('download', 'gfx');
    $span.text('approx. size:' + Math.floor(dataURL.length * 6 / 8) + " bytes");
    $span.appendTo($container);
    $dl.appendTo($container);
    $container.append('<br/>');
    $img.appendTo($container);
    $container.append(testAsSVG(dataURL, cv.width, cv.height));
    $('#code').text(svgCode(cv.width,cv.height));
    var svgEmbedded = svgCodeEmbedded(dataURL, cv.width, cv.height);
    $('#codesvg64').text(urlEncodeSVG(svgEmbedded));
    $('#codeembedded').text(svgEmbedded);
    var svgBlob = new Blob([svgEmbedded], {type: 'image/svg+xml'});
    $dl.after(
      $('<a/>').attr('href',window.URL.createObjectURL(svgBlob)).text('Download SVG').attr('download','gfx')
    );
    $container.appendTo($output);
    $('svg').mouseover(function() {
      $(this).css({
        transformOrigin: '0 0',
        transform: 'scale(2)'
      })
    }).mouseout(function() {
      $(this).css({
        transformOrigin: '0 0',
        transform: 'scale(1)'
      })
    });
  }

  function rePack() {
    $('output').empty();
    $.each(imageCVs, function() {
      insertCVasJPG(this);
    });
  }

  $('#files').on('change', handleFileSelect);
  $('input[name=tpixels], input[name=trim]').on('change', reMask);
  $('.slider.comp').slider({
    value: 65,
    orientation: "horizontal",
    min: 0,
    max: 100,
    change: rePack
  });
  $('.slider.athreshold').slider({
    value: 0,
    orientation: "horizontal",
    min: 0,
    max: 255,
    change: function() {
      var $s = $('.slider.athreshold');
      var v = $s.slider("values", 0);
      $s.next().text(v);
      reMask();
    }
  });
            
          
!
999px
Close

Asset uploading is a PRO feature.

As a PRO member, you can drag-and-drop upload files here to use as resources. Images, Libraries, JSON data... anything you want. You can even edit them anytime, like any other code on CodePen.

Go PRO

Loading ..................

Console