Easy HTML5 canvas and CSS Sprite texture atlas

Texture atlas is a collection of images, all composed in single large image. The game or HTML5 app can load this single image instead of wasting time for requesting every small image separately.

I wrote tool that can be used to package collection of images to single atlas and let web app use that conveniently both as CSS sprite or in HTML5 canvas drawing.

Tool itself is Python script that uses ImageMagick commands to read and write images. The output is the atlas image, CSS, raw JSON and Javascript import file. CSS file defines CSS sprite for each image. JSON is raw data of each images position in the atlas, Javascript import file can be included on HTML page and it loads the atlas position info into global variable.

Setup

Get script from here: https://raw.github.com/tikonen/blog/master/packer/packer.py

Make it executable

$ chmod +x packer.py

Verify that all is ok

~/work/blog/packer $ ./packer.py -h
usage: packer.py [-h] [-o OUTFILE] [-jo JSONOUTFILE] [-jso JSOUTFILE]
                 [-co CSSOUTFILE] [-p PAD] [-mw WIDTH] [-mh HEIGHT]
                 FILE [FILE ...]

Packs images to atlas. Uses ImageMagick to parse and compose the images

positional arguments:
  FILE             Image file

optional arguments:
  -h, --help       show this help message and exit
  -o OUTFILE       Output atlas file
  -jo JSONOUTFILE  Output atlas json file
  -jso JSOUTFILE   Output atlas import js file
  -co CSSOUTFILE   Output atlas css file
  -p PAD           Padding
  -mw WIDTH        Maximum width
  -mh HEIGHT       Maximum height

Install ImageMagick easily on OS/X with Macports or Homebrew.

$ sudo port install ImageMagick

Verify that ImageMagick installation works

$ identify --version
Version: ImageMagick 6.8.7-3 2013-10-28 Q16 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2013 ImageMagick Studio LLC
Features: DPC
Delegates: bzlib djvu fftw fontconfig freetype gslib jng jpeg lcms ltdl lzma png ps png tiff webp x xml zlib

Usage

You use script by giving image file as arguments and defining the desired output files and maximum image dimensions

For example:

$ ./packer.py img/*.png -o sprites.png

This would produce sprites.png, sprites.json, sprites.css and sprites.json.js

Options
Following options are supported

  • -p PAD
    Defines padding. The amount of empty pixels around each image. This prevents scaling artifacts on HTML canvas use when drawing from decimal source coordinates.
  • -mw WIDTH,-mh HEIGHT
    Maximum width and height of output image.
  • -o FILE
    Output image file.
  • -jo FILE, -jso FILE, -co FILE
    Output JS import, JSON and CSS file.

Example

We have following small images that are needed in our web app. Loading them all independently would take 5 HTTP requests.

  • button_minus.png
  • button_plus.png
  • cannon_marker.png
  • pause.png
  • status_bar.png

montage

Running tool converts them to single atlas

~/work/blog/packer $ ./packer.py example/pics/* -o example/html/sprites.png -mw 512
Checking ImageMagick
Found: Version: ImageMagick 6.8.7-3 2013-10-28 Q16 http://www.imagemagick.org
===========================
Resolving file dimensions
button_minus.png -> 53x43
button_plus.png -> 53x42
cannon_marker.png -> 43x28
pause.png -> 53x42
status_bar.png -> 496x74
===========================
fitting 5 images, padding 1
successfully fitted 5 images to 496x118 padding 1
Wrote: atlas to example/html/sprites.png
Wrote json to example/html/sprites.json
Wrote js to example/html/sprites.json.js
Wrote css to example/html/sprites.css

In this example we defined maximum width of 512 pixels. The resulting image is cropped to minimum size 496×118.

sprites
This atlas can be used now in two different ways on the web page.

CSS Sprite

Web page includes the generated CSS file and defines class by filename (e.g. “bg-sprites status_bar” for each element that uses sprite.

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <link rel="stylesheet" type="text/css" href="sprites.css">
    <style type="text/css">
        .bg-sprites {
            color: white;
            text-align: center;
        }
    </style>
</head>
<body>
    <div>
        <h1>Example</h1>
        <div>
            <h2>Cannon - simple sprite with image</h2>
            <div class="bg-sprites cannon_marker">
            </div>
            <h2>Status bar - sprite with text inside</h2>
            <div class="bg-sprites status_bar">
                <span style="font-size:xx-large;position:relative;top:15px;">Here is some text</span>
            </div>
            <h2>Buttons - list</h2>
            <div>
                <span style="float:left;margin:5px;" class="bg-sprites button_minus"></span>
                <span style="float:left;margin:5px;" class="bg-sprites button_plus"></span>
                <span style="float:left;margin:5px;" class="bg-sprites pause"></span>
            </div>
        </div>
    </div>
</body>

This renders following page:
example css sprite

See example page here: http://ikonen.me/examples/packer/example_css.html

HTML5 Canvas

Another option is to use it with HTML canvas. Page links the generated JSON import script and loads the image.

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <style type="text/css">
        canvas {
            width: 600px;
            height: 400px;
        }
    </style>
</head>
<body>
    <div>
        <h1>Canvas Example</h1>
        <div>
            <canvas id="thecanvas" width="600" height="400"></canvas>
        </div>
    </div>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" ></script>
  <script type="text/javascript" src="sprites.json.js"></script>
  <script type="text/javascript">
      $(function() {
          var canvas = document.getElementById('thecanvas');
          var ctx = canvas.getContext('2d');
          ctx.strokeStyle = 'hotpink';
          ctx.strokeRect(0, 0, 600, 400);
          ctx.font = '20px Arial';

          // Load image and the json that defines locations
          var sprites = new Image();
          sprites.src = 'sprites.png';
          sprites.addEventListener("load", function() {
                  var assets = bg_sprites; // imported by sprites.json.js

                  // draw them all
                  var xoffset = 5,
                      yoffset = 25;

                  for (var pic in assets) {
                      var asset = assets[pic];
                      ctx.fillText(pic, xoffset, yoffset-3);

                      // draw image from sprite
                      ctx.drawImage(sprites, asset.x, asset.y, asset.w, asset.h, xoffset, yoffset, asset.w, asset.h);

                      yoffset += asset.h + 20;
                  }
          }, false);
      });
  </script>
</body>
</html>

This renders following page:

example canvas

See example page here: http://ikonen.me/examples/packer/example_canvas.html

Get complete example from Github.