#!/usr/bin/perl -w eval 'exec /usr/bin/perl -S $0 ${1+"$@"}' if 0; # not running under some shell eval 'exec /usr/app/bin/perl -S $0 ${1+"$@"}' if 0; # not running under some shell # # rollover # This perl-fu script generates rollover from layers, # # Copyright (c) 2001, 2002 Damien Genet # Cyria création # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, # USA. # use File::Path; use Gimp; use Gimp::Fu; # Uncomment to turn on debugging # Gimp::set_trace(TRACE_ALL); use strict; # # HTML code generation # sub preloadImages_string { my ($imgdir, $imgbasename, $ext, @slices) = @_; my $str = ""; foreach (@slices) { foreach (@$_) { for (my $i = 0; $i < scalar @{$_->{layers}}; $i++) { $str .= ($str ? "," : "") . "'" . image_name ($imgdir, $imgbasename, $_->{pos}, $i, $ext) . "'"; } } } return $str; } sub html_start { my ($file, $imagename, $width, $height, $imgdir, $imgbasename, $ext, $use_div, @slices) = @_; my $gimp_version = Gimp::gimp_version (); my $preloadImages = preloadImages_string ($imgdir, $imgbasename, $ext, @slices); print $file < rollover generated from $imagename

rollover generated from $imagename

EOF if ($use_div) { print $file "
\n"; } else { print $file "\n"; } } sub html_end { my ($file, $use_div) = @_; if ($use_div) { print $file "\n"; } else { print $file "
\n"; } print $file < EOF } sub swapImages_string { my %swap = @_; my $str = ""; while ( my ($name, $src) = each %swap ) { $str .= ($str ? "," : "" ) . "'$name','$src'"; } return $str; } sub image_string { my ($src, $width, $height, $href, $alt, $name, %swap) = @_; return (%swap ? "" : "") . "\"$alt\"" . (%swap ? "" : ""); } sub print_div { my ($file, $left, $top, $src, $width, $height, $href, $alt, $name, %swap) = @_; print $file "\t
" . image_string ($src, $width, $height, $href, $alt, $name, %swap) . "
\n"; } sub print_row { my ($file, $colspan, $rowspan, $src, $width, $height, $href, $alt, $name, %swap) = @_; print $file "\t\t 1 ? " colspan=\"$colspan\"" : "") . ($rowspan > 1 ? " rowspan=\"$rowspan\"" : "") . ">" . image_string ($src, $width, $height, $href, $alt, $name, %swap) . "\n"; } sub print_space_row { my ($file, $imgdir, $width, $height) = @_; print $file "\t\t" . "\"\"" . "\n"; } # # image manipulation code # sub img_get_active_layers { my ($image) = @_; return grep { Gimp::gimp_layer_get_name($_) =~ /_Over_$/i } Gimp::gimp_image_get_layers ($image); } sub img_get_visible_layers { my ($image) = @_; return grep { Gimp::gimp_layer_get_visible ($_) } Gimp::gimp_image_get_layers ($image); } sub img_count_visible_layers { my ($image) = @_; my $count = 0; foreach (Gimp::gimp_image_get_layers ($image)) { $count++ if (Gimp::gimp_layer_get_visible ($_)); } return $count; } sub img_create_fake_layer { my ($image) = @_; my $layer = Gimp::gimp_layer_new ($image, Gimp::gimp_image_width ($image), Gimp::gimp_image_height ($image), 2 * Gimp::gimp_image_base_type ($image) + 1, "blank", 0, NORMAL_MODE); Gimp::gimp_drawable_fill ($layer, TRANS_IMAGE_FILL); Gimp::gimp_image_add_layer ($image, $layer, 0); } sub img_delete_non_visible_layers { my ($image) = @_; foreach (Gimp::gimp_image_get_layers ($image)) { Gimp::gimp_image_remove_layer ($_) unless (Gimp::gimp_layer_get_visible ($_)); } } sub img_merge { my ($image, $ext) = @_; img_delete_non_visible_layers ($image); my $count_visibles = img_count_visible_layers ($image); if ($ext eq "jpeg") { img_create_fake_layer ($image) unless ($count_visibles); return Gimp::gimp_image_flatten ($image); } else { img_create_fake_layer ($image) if ($count_visibles <= 1); img_create_fake_layer ($image) unless ($count_visibles); return Gimp::gimp_image_merge_visible_layers ($image, CLIP_TO_IMAGE); } } sub img_save { my ($image, $imgname, $ext, $quality, $colors, $progressive) = @_; my $layer = img_merge ($image, $ext); if ($ext eq "jpeg") { if (Gimp::gimp_drawable_is_indexed ($layer)) { Gimp::gimp_convert_rgb ($image); } Gimp::file_jpeg_save ($image, $layer, $imgname, $imgname, $quality, 0, 1, $progressive, "", 0, 1, 0, 1); } elsif ($ext eq "gif") { if (!Gimp::gimp_drawable_is_indexed ($layer)) { Gimp::gimp_convert_indexed ($image, 2, MAKE_PALETTE, $colors, 1, 1, ""); } Gimp::file_gif_save ($image, $layer, $imgname, $imgname, $progressive, 0, 0, 0); } elsif ($ext eq "png") { Gimp::file_png_save ($image, $layer, $imgname, $imgname, $progressive, 9, 0, 0, 0, 0, 0); } else { die ("Error, unknown image type"); } } sub img_crop { my ($image, $imgname, $left, $top, $width, $height, $ext, $quality, $colors, $progressive) = @_; my $tmpimg = Gimp::gimp_channel_ops_duplicate ($image); Gimp::gimp_crop ($tmpimg, $width, $height, $left, $top); img_save ($tmpimg, $imgname, $ext, $quality, $colors, $progressive); Gimp::gimp_image_delete ($tmpimg); } sub img_generate_blank { my ($imagename) = @_; open FILE, ">$imagename" or die "Couldn't open $imagename: $!\n"; print FILE < $b} @_; my @new_list = (); return () unless (@list); $new_list[0] = $list[0]; my $count = 1; for (my $i = 1; $i < scalar @list; $i++) { next unless $list[$i] != $new_list[$count-1]; $new_list[$count] = $list[$i]; $count++; } return @new_list; } sub get_rules { my ($imgwidth, $imgheight, @active_layers) = @_; my @vrules = (0, $imgwidth); my @hrules = (0, $imgheight); foreach (@active_layers) { my ($left, $top, $width, $height) = (Gimp::gimp_drawable_offsets ($_), Gimp::gimp_drawable_width ($_), Gimp::gimp_drawable_height ($_)); push (@vrules, $left, $left + $width); push (@hrules, $top, $top + $height); } return [ my_sort (@vrules) ], [ my_sort (@hrules) ]; } sub get_layers_slices { my @slices = @_; my %layers_slices; foreach (@slices) { foreach (@$_) { my @layers = @{$_->{layers}}; my $pos_slice = $_->{pos}; for (my $i = 0; $i < scalar @layers; $i++) { $layers_slices{$layers[$i]}{$pos_slice} = $i; } } } return %layers_slices; } sub is_in_layer { my ($slice_left, $slice_top, $layer) = @_; my ($layer_left, $layer_top, $layer_width, $layer_height) = (Gimp::gimp_drawable_offsets ($layer), Gimp::gimp_drawable_width ($layer), Gimp::gimp_drawable_height ($layer)); return ( $slice_left >= $layer_left && $slice_left < $layer_left + $layer_width && $slice_top >= $layer_top && $slice_top < $layer_top + $layer_height); } sub in_layers { my ($slice_left, $slice_top, @layers) = @_; my @in_layers = (); foreach (@layers) { if (is_in_layer ($slice_left, $slice_top, $_)) { push (@in_layers, $_); } } return @in_layers; } sub get_up_slice { my ($left, $slicesref) = @_; foreach (reverse @$slicesref) { foreach (reverse @$_) { if ($_->{left} == $left) { return $_; } elsif ($_->{left} < $left && $_->{left} + $_->{width} > $left) { return undef(); } } } return undef(); } sub push_slice { my ($posref, $row_slicesref, $slicesref, %slice) = @_; my $up_slice = get_up_slice ($slice{left}, $slicesref); if ($up_slice && $up_slice->{width} == $slice{width} && @{$up_slice->{layers}} eq @{$slice{layers}}) { $up_slice->{height} += $slice{height}; } else { push (@$row_slicesref, {%slice}); $$posref++; } } sub make_slice { my ($left, $top, $width, $height, $posref, $row_slicesref, $slicesref, $layersref, %current_slice) = @_; my @in_layers = in_layers ($left, $top, @$layersref); if (%current_slice && @in_layers eq @{$current_slice{layers}}) { $current_slice{width} += $width; return %current_slice; } else { push_slice ($posref, $row_slicesref, $slicesref, %current_slice) if (%current_slice); return (layers => [ @in_layers ], left => $left, top => $top, width => $width, height => $height, colspan => undef(), rowspan => undef(), pos => $$posref); } } sub array_to_hash { my $pos = 0; my %hash = (); foreach (@_) { $hash{$_} = $pos++; } return %hash; } sub get_effective_rules { my ($imgwidth, $imgheight, @slices) = @_; my @vrules = ($imgwidth); my @hrules = ($imgheight); foreach (@slices) { foreach (@$_) { push (@vrules, $_->{left}); push (@hrules, $_->{top}); } } return [ my_sort (@vrules) ], [ my_sort (@hrules) ]; } sub set_count_span { my ($vrulesref, $hrulesref, $slicesref) = @_; my %vrules = array_to_hash (@$vrulesref); my %hrules = array_to_hash (@$hrulesref); foreach (@$slicesref) { foreach (@$_) { $_->{colspan} = $vrules{$_->{left} + $_->{width}} - $vrules{$_->{left}}; $_->{rowspan} = $hrules{$_->{top} + $_->{height}} - $hrules{$_->{top}}; } } } sub get_slices { my ($vrulesref, $hrulesref, @active_layers) = @_; my @slices = (); my $pos = 0; foreach (my $row = 0; $row < scalar @$hrulesref - 1; $row++) { my @row_slices = (); my %current_slice; foreach (my $col = 0; $col < scalar @$vrulesref - 1; $col++) { %current_slice = make_slice ($vrulesref->[$col], $hrulesref->[$row], $vrulesref->[$col+1] - $vrulesref->[$col], $hrulesref->[$row+1] - $hrulesref->[$row], \$pos, \@row_slices, \@slices, \@active_layers, %current_slice); } push_slice (\$pos, \@row_slices, \@slices, %current_slice) if (%current_slice); push (@slices, [ @row_slices ]) if (@row_slices); } my ($effectivevrulesref, $effectivehrulesref) = get_effective_rules ($vrulesref->[scalar @$vrulesref - 1], $hrulesref->[scalar @$hrulesref - 1], @slices); @$vrulesref = @$effectivevrulesref; @$hrulesref = @$effectivehrulesref; set_count_span ($vrulesref, $hrulesref, \@slices); return ([ @slices ], { get_layers_slices (@slices) }); } sub count_slices { my @slices = @_; my $count = 0; foreach (@slices) { $count += scalar @$_; } return $count; } # # main code # sub image_name { my ($imgdir, $imgbasename, $pos_slice, $pos_image, $ext) = @_; return $imgdir . $imgbasename . $pos_slice . ($pos_image >= 0 ? "-o" . ($pos_image ? $pos_image + 1 : "") : "") . "." . $ext; } sub get_swap_hash { my ($imgdir, $imgbasename, $ext, $layerslicesref) = @_; my %swap; while ( my ($pos_slice, $pos_image) = each %$layerslicesref ) { $swap{$imgbasename . $pos_slice} = image_name ($imgdir, $imgbasename, $pos_slice, $pos_image, $ext); } return %swap; } sub save_slice { my ($file, $image, $savedir, $imgdir, $imgbasename, $ext, $quality, $colors, $progressive, $sliceref, $layerslicesref, $use_div) = @_; my $alt = ""; my $href = ""; if (@{$sliceref->{layers}} && Gimp::gimp_layer_get_name($sliceref->{layers}[0]) =~ /^((.+?)\s+)??("(\S+)"\s+)?_Over_$/i ) { $alt = ($2 ? $2 : ""); $href = ($4 ? $4 : "#"); } my %swap = (); if (@{$sliceref->{layers}}) { %swap = get_swap_hash ($imgdir, $imgbasename, $ext, $layerslicesref->{$sliceref->{layers}[0]}); } if ($use_div) { print_div ($file, $sliceref->{left}, $sliceref->{top}, image_name ($imgdir, $imgbasename, $sliceref->{pos}, -1, $ext), $sliceref->{width}, $sliceref->{height}, $href, $alt, $imgbasename . $sliceref->{pos}, %swap ); } else { print_row ($file, $sliceref->{colspan}, $sliceref->{rowspan}, image_name ($imgdir, $imgbasename, $sliceref->{pos}, -1, $ext), $sliceref->{width}, $sliceref->{height}, $href, $alt, $imgbasename . $sliceref->{pos}, %swap ); } img_crop ($image, $savedir . image_name ($imgdir, $imgbasename, $sliceref->{pos}, -1, $ext), $sliceref->{left}, $sliceref->{top}, $sliceref->{width}, $sliceref->{height}, $ext, $quality, $colors, $progressive); for (my $i = 0; $i < scalar @{$sliceref->{layers}}; $i++) { Gimp::gimp_layer_set_visible ($sliceref->{layers}[$i], 1); img_crop ($image, $savedir . image_name ($imgdir, $imgbasename, $sliceref->{pos}, $i, $ext), $sliceref->{left}, $sliceref->{top}, $sliceref->{width}, $sliceref->{height}, $ext, $quality, $colors, $progressive); Gimp::gimp_layer_set_visible ($sliceref->{layers}[$i], 0); } } sub build_content { my ($file, $tmpimg, $savedir, $imgdir, $imgbasename, $ext, $quality, $colors, $progressive, $vrulesref, $hrulesref, $slicesref, $layerslicesref, $use_div) = @_; unless ($use_div) { print $file "\t\n"; for (my $col = 0; $col < scalar @$vrulesref - 1; $col++) { print_space_row ($file, $imgdir, $vrulesref->[$col+1] - $vrulesref->[$col], 1); } print $file "\t\n"; } Gimp->gimp_progress_init ("Generating rollover..."); my $progress_increment = 1 / count_slices (@$slicesref); my $progress = 0; for (my $row = 0; $row < scalar @$slicesref; $row++) { print $file "\t\n" unless ($use_div); foreach (@{$slicesref->[$row]}) { save_slice ($file, $tmpimg, $savedir, $imgdir, $imgbasename, $ext, $quality, $colors, $progressive, $_, $layerslicesref, $use_div); $progress += $progress_increment; Gimp->gimp_progress_update ($progress); } unless ($use_div) { print_space_row ($file, $imgdir, 1, $hrulesref->[$row+1] - $hrulesref->[$row]); print $file "\t\n"; } } } sub prepare_image { my ($tmpimg) = @_; my @active_layers = img_get_active_layers ($tmpimg); my ($vrulesref, $hrulesref) = get_rules (Gimp::gimp_image_width ($tmpimg), Gimp::gimp_image_height ($tmpimg), @active_layers); my ($slicesref, $layerslicesref) = get_slices ($vrulesref, $hrulesref, @active_layers); foreach (@active_layers) { Gimp::gimp_layer_set_visible ($_, 0); } return ($vrulesref, $hrulesref, $slicesref, $layerslicesref); } sub build_html { my ($file, $image, $savedir, $imgdir, $imgbasename, $ext, $quality, $colors, $progressive, $use_div) = @_; my $tmpimg = Gimp::gimp_channel_ops_duplicate ($image); my ($vrulesref, $hrulesref, $slicesref, $layerslicesref) = prepare_image ($tmpimg); html_start ($file, Gimp::gimp_image_get_filename ($image), Gimp::gimp_image_width ($image), Gimp::gimp_image_height ($image), $imgdir, $imgbasename, $ext, $use_div, @$slicesref); build_content ($file, $tmpimg, $savedir, $imgdir, $imgbasename, $ext, $quality, $colors, $progressive, $vrulesref, $hrulesref, $slicesref, $layerslicesref, $use_div); html_end ($file, $use_div); Gimp::gimp_image_delete ($tmpimg); } sub add_trailing_slash { my ($dir) = @_; if (!($dir =~ m,/$,)) { $dir .= "/"; } return $dir; } sub rollover { my ($image, undef, $savedir, $htmlname, $imgdir, $imgbasename, $ext, $quality, $colors, $progressive, $use_div) = @_; Gimp->gimp_progress_init ("Generating rollover..."); $savedir = add_trailing_slash ($savedir); $imgdir = add_trailing_slash ($imgdir); mkpath($savedir); mkpath($savedir . $imgdir); img_generate_blank ($savedir . $imgdir . "blank.gif") unless ($use_div); open FILE, ">$savedir$htmlname" or die "Couldn't open $savedir$htmlname: $!\n"; build_html (\*FILE, $image, $savedir, $imgdir, $imgbasename, $ext, $quality, $colors, $progressive, $use_div); return (); } register ("rollover", "Generate rollover images from layers", "The active slices of the images have the following layout for the \ layers name : 'description for the alt attribute [\"optionial_link_with_no_space\"] _Over_', they don't need to be visible. The other visible layers are flaterned to make the normal state of the image.", "Damien Genet", "Copyright (c) 2001, 2002 Damien Genet \n" . " Cyria création ", "2002-03-12", N_"/Filters/Web/Generate rollover...", "RGB*, GRAY*, INDEXED*", [ [PF_FILE, "save_dir", "The directory to export the files to", add_trailing_slash ($ENV{HOME})], [PF_STRING, "html_file_name", "The name of the html page", "index.html"], [PF_STRING, "relative_image_dir", "The subdirectory to put the images in", "img/"], [PF_STRING, "image_basename", "The base of the images names", "image"], [PF_RADIO, "image_extension", "The format of the images {gif, jpeg, png}", "jpeg", [gif => "gif", jpeg => "jpeg", png => "png"]], [PF_SLIDER, "quality", "The quality of the images (for jpeg)", 0.75, [0, 1, 0.01]], [PF_SLIDER, "colors", "The number of images colors (for gif, when it's not already indexed)", 32, [1,256,1]], [PF_TOGGLE, "progressive", "Progressive or interlaced image", 1], [PF_TOGGLE, "use_layers", "Render HTML with layers instead of table", 0] ], \&rollover); exit main;