Generate Css Sprites

Generate CSS sprites

Generate a map of existing images to a bigger one, and then using css to decide what icon to show for the user. Sounds like a plan! Doing it by hand takes a lot of time. Generating them, takes a little less time and is more flexible when adding new icons.

A good explanation of css sprites is found at [http://www.alistapart.com/articles/sprites/]

The following script generates a big image of all png files found in a specific directory, and converts the individual file paths to a cssrule referring to the big image.

$ php g_css_sprite.php ../path/to/images/ css_file.css png_file.png

Please note! A problem I noted is that the quality of the “copied” icons degrades a bit to much to make it usable for the moment. I’ve been looking at a few solutions for this, but its not there yet.

 
/**
 * Accept some simple input parameters
 */
$image_path = isset($_SERVER['argv'][1])?$_SERVER['argv'][1]:'';
$css_output = isset($_SERVER['argv'][1])?$_SERVER['argv'][2]:'';
$png_output = isset($_SERVER['argv'][1])?$_SERVER['argv'][3]:'';
 
if( empty($image_path) || empty($css_output) || empty($png_output) )
    exit("Usage: ".__FILE__." <imagepath> <cssoutput> <pngoutput>\n");
 
 
/**
 * Traverse a file tree and find png files only.
 * Exclude any images that are larger than 100x60
 **/
function traverseFiles($path) {
    $files = array();
    if( !is_dir($path) )
        return $files;
    $dir = opendir($path);
    while( $f = readdir($dir) ) {
        if( ereg("png$", $f) ) {
            $im = getimagesize($path.$f);
            if( $im[0]<60 && $im[1]<100 )
                $files[$path.$f] = array('width'    => $im[0], 'height' => $im[1]);
        }elseif( is_dir($path.$f) && !ereg("^[\.]", $f) ) {
            $files = array_merge($files, traverseFiles($path.$f.'/'));
        }
    }
    return $files;
}
 
 
/**
 * Transform the path to a similar css rule.
 **/
function transform_path_to_css($path) {
    global $image_path;
    $p = str_replace($image_path, "", $path);
    $p = str_replace(".png", "", $p);
    $p = str_replace("-", "", $p);
    $p = str_replace("/", "_", $p);
    $p = 'images_'.$p;
    return $p;
}
 
 
/* Find all the pngs */
$png_files = traverseFiles($image_path);
 
 
/**
 * Distribute the images on multiple rows in the new larger image.
 * Since we arent doing anything intelligent to sort images with the same height,
 * we try to keep in the array whats the max height for the row, so we can
 * start the next line below the previous one.
 */
$i = 0;
$max_width = 0;
$max_height = 0;
$images_rows = array();
foreach( $png_files as $file    => $size ) {
    if( !isset($images_rows[$i]) ) { /* Create a new "empty" row of images, if it doesnt exist. */
        $images_rows[$i] = array('height'   => 0, 'width'   => 0, 'files'   => array());
    }
 
    
    $images_rows[$i]['height'] = max($images_rows[$i]['height'], $size['height']);
    $images_rows[$i]['files'][] = array(
                                        'file' => $file,
                                        'x'   => $images_rows[$i]['width']
                                        );
    $images_rows[$i]['width']+=$size['width'];
    
    /* Create a new line after 600px */
    if( $images_rows[$i]['width']>600 ) { 
        $max_width = max($images_rows[$i]['width'], $max_width);
        $max_height+=$images_rows[$i]['height'];
        $i++;
    }
}
 
 
/* Create a new empty, transparent image with the calculated max height and width. */
$im = imagecreatetruecolor($max_width, $max_height);
$transparent = imagecolorallocate($im, 0, 0, 0);
imagecolortransparent($im, $transparent);
 
 
/**
 * Walk through the image rows
 * and create a css structure.
 **/
$css = '';
$y = 0;
foreach( $images_rows as $icon_rows )  {
    foreach( $icon_rows['files'] as $f ) {
        $c_im = imagecreatefrompng($f['file']);
        
        $new_position_x = $f['x'];
        $new_position_y = $y;
        $clip_x = ($f['x']*-1);
        $clip_y = ($y*-1);
        
        imagecopy($im, $c_im,
                  $new_position_x, $new_position_y,
                  0, 0,
                  $png_files[$f['file']]['width'],
                  $png_files[$f['file']]['height']);
 
        $css.='.'.transform_path_to_css($f['file']).' { display: block; background: url('.$png_output.') '.$clip_x.'px '.$clip_y.'px no-repeat; width: '.$png_files[$f['file']]['width'].'px; height: '.$png_files[$f['file']]['height'].'px;  }'.chr(10);
    }
    $y+=$icon_rows['height'];
}
 
imagepng($im, $png_output, 0, PNG_NO_FILTER);
file_put_contents($css_output, $css);
 
 
echo "Found ".sizeof($png_files)." png images..\n";
echo "Png filesize ".filesize($png_output)." bytes..\n";
echo "CSS filesize ".filesize($css_output)." bytes..\n";
 


blog comments powered by Disqus