diff options
author | Yorhel <git@yorhel.nl> | 2015-05-11 12:49:13 +0200 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2015-05-11 13:17:36 +0200 |
commit | 887607bb3744c727ec617508c17b2b7df46c2287 (patch) | |
tree | a8d81234c75e90a112f50d5bb5de6db2536f24a1 /util | |
parent | 89d3405ac83145f0d7a27db37fcc835bdc2948d2 (diff) |
Generate icons.png and associated CSS automatically
It became a bit of a hassle to keep updating that file manually in Gimp.
This script performs surprisingly well for our set of icons.
Diffstat (limited to 'util')
-rwxr-xr-x | util/skingen.pl | 8 | ||||
-rwxr-xr-x | util/spritegen.pl | 143 |
2 files changed, 151 insertions, 0 deletions
diff --git a/util/skingen.pl b/util/skingen.pl index 343c5922..834578cb 100755 --- a/util/skingen.pl +++ b/util/skingen.pl @@ -18,10 +18,18 @@ use SkinFile; require $ROOT.'/data/global.pl'; +my $iconcss = do { + open my $F, '<', "$ROOT/data/icons/icons.css" or die $!; + local $/=undef; + <$F>; +}; + + sub writeskin { # $name my $name = shift; my $skin = SkinFile->new("$ROOT/static/s", $name); my %o = map +($_ => $skin->get($_)), $skin->get; + $o{iconcss} = $iconcss; # get the right top image if($o{imgrighttop}) { diff --git a/util/spritegen.pl b/util/spritegen.pl new file mode 100755 index 00000000..c6f830f7 --- /dev/null +++ b/util/spritegen.pl @@ -0,0 +1,143 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Image::Magick; +use Cwd 'abs_path'; + +our $ROOT; +BEGIN { ($ROOT = abs_path $0) =~ s{/util/spritegen\.pl$}{}; } + +my $path = "$ROOT/data/icons"; +my $icons = "$ROOT/static/f/icons.png"; +my $css = "$ROOT/data/icons/icons.css"; + +my @img = map { + my $i = Image::Magick->new(); + $i->Read($_) and die $_; + { + f => /^\Q$path\E\/(.+)\.png/ && $1, + i => $i, + h => scalar $i->Get('height'), + w => scalar $i->Get('width') + } +} glob("$path/*.png"), glob("$path/*/*.png"); + + +@img = sort { $b->{h} <=> $a->{h} || $b->{w} <=> $a->{w} } @img; + +my $minpixels = 0; +$minpixels += $_->{w}*$_->{h} for @img; + + +# Simple strip packing algortihm, First-Fit Decreasing Height. +sub genstrip { + my $w = shift; + my @l; + my $h = 0; + for my $i (@img) { + my $found = 0; + # @img is assumed to be sorted by height, so image always fits + # (height-wise) in any of the previously created levels. + for my $l (@l) { + next if $l->{left} + $i->{w} > $w; + # Image fits, add to level + $i->{x} = $l->{left}; + $i->{y} = $l->{top}; + $l->{left} += $i->{w}; + $found = 1; + last; + } + next if $found; + + # No level found, create a new one + push @l, { top => $h, left => $i->{w} }; + $i->{x} = 0; + $i->{y} = $h; + $h += $i->{h}; + } + return $h; +} + + +# Tries to find the width of the strip for which the number of unused pixels is +# the minimum. Simple and dumb linear search; it's fast enough. +# +# Note that minimum number of unused pixels does not imply minimum file size, +# although there is some correlation. To further minimize the file size, it's +# possible to attempt to group similar-looking images close together so that +# the final png image might compress better. Finding a good (and fast) +# algorithm for this is not a trivial task, however. +sub minstrip { + my($minwidth, $maxwidth) = (0,0); + for(@img) { + $minwidth = $_->{w} if $_->{w} > $minwidth; + $maxwidth += $_->{w}; + } + my $optw; + my $optsize = 1e10; + for my $w ($minwidth..$maxwidth) { + my $size = genstrip($w)*$w; + # To optimize for file size, uncommment below line. It's slow, but saves about 150 bytes (while using pngcrush). + #$size = img(); + if($size < $optsize) { + $optw = $w; + $optsize = $size; + } + } + genstrip($optw); +} + + +sub calcdim { + my($w, $h) = (0,0); + for (@img) { + $w = $_->{x}+$_->{w} if $w < $_->{x}+$_->{w}; + $h = $_->{y}+$_->{h} if $h < $_->{y}+$_->{h}; + } + ($w, $h) +} + + +sub img { + my($w, $h) = calcdim; + my $img = Image::Magick->new; + print $img->Set(size => "${w}x$h"); + print $img->ReadImage('canvas:rgba(0,0,0,0)'); + my $pixels = $w*$h; + for my $i (@img) { + print $img->Composite(image => $i->{i}, x => $i->{x}, y => $i->{y}); + } + print $img->Write($icons); + undef $img; + + `pngcrush -q "$icons" "$icons~" 2>/dev/null && mv "$icons~" "$icons"`; + + my $size = -s $icons; + #printf "Dim: %dx%d, size: %d, pixels wasted: %d\n", $w, $h, $size, $w*$h-$minpixels; + $size; +} + + +sub css { + # The gender icons need special treatment, they're 3 icons in one image. + my $gender; + + open my $F, '>', $css or die $!; + for my $i (@img) { + if($i->{f} eq 'gender') { + $gender = $i; + next; + } + $i->{f} =~ /([^\/]+)$/; + printf $F ".icons.%s { background-position: %dpx %dpx }\n", $1, -$i->{x}, -$i->{y}; + } + printf $F ".icons.gen.f, .icons.gen.b { background-position: %dpx %dpx }\n", -$gender->{x}, -$gender->{y}; + printf $F ".icons.gen.m { background-position: %dpx %dpx }\n", -($gender->{x}+14), -$gender->{y}; +} + + +#genstrip 80; +minstrip; +img; +css; |