#!/usr/pkg/bin/perl -w
#
# 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
#

#-----------------------------------------------------------------------------
# Copyright 2000, Olivier Chapuis
#-----------------------------------------------------------------------------

# Filter this script to pod2man to get a man page:
#   pod2man -c "Fvwm Utility" fvwm-themes-images | nroff -man | less -e

use strict;
use Getopt::Long;

my $prefix = '/usr/pkg';
my $ROOT_PREFIX = '';
$ROOT_PREFIX = $ENV{'DESTDIR'} if $ENV{'DESTDIR'};
my $datadir = "${prefix}/share";

my $version = '0.7.0';
my $fvwmVersion = '2.7.0';
my $ftDataDir = "/usr/pkg/share/fvwm2";
my @xlibPath = qw( -L/usr/X11R7/lib );
my $scriptName = ($0 =~ m:([^/]+)$:, $1);
my $ftImagesDir = "$ftDataDir/images";

my $userHome = $ENV{'HOME'} || "./.";
# default directory for "transformed" icons
my $siteTransformOutDir = "$ftDataDir/tr-images";
my $userTransformOutDir = "$userHome/tr-images";

my @pathDirs = split(':',$ENV{PATH});
my $userName = $ENV{USER} || "unknown";

# global options
my $inDir = "";
my $outDir = "";
my $inFile = "";
my $outFile = "";

# for storing the rgb.txt file
my %rgb;

# convert options
my $convert = 0;
my $gnome = 0;
my $kde2 = 0;
my $kde2Hi = 0;
my $postfix = "";
my $transFilter = 1;
my @types = ();
my $preserve = 0;
my %build = (
	'48x48' => 1,
	'16x16' => 1,
	'56x56' => 0,
	'32x32' => 0,
	'tiles' => 0,
);
my %tileOpt = (
	'48x48' => "",
	'16x16' => "",
	'56x56' => "",
	'32x32' => "",
	'tiles' => "",
);
my %addBorderOpt = (
	'48x48' => "",
	'16x16' => "",
	'56x56' => "",
	'32x32' => "",
	'tiles' => "",
);
my %size = (
	'48x48' => "48x48",
	'16x16' => "17x17",  # huh?
	'56x56' => "56x56",
	'32x32' => "32x32",
	'tiles' => "",
);

# tile options
my $tile = "";

# add border
my $addBorder = "";

# ImageMagick options
my $magickColors = 256;
my $magickColorspace = "Transparent";
# depends if ImageMagick is compiled with 16bits per pixels enabled!
# if yes percentage is taken with 65535 if no 255 is used
my $threshold = 70;
my $magickPath = "";
my $magickBpp = 0;

# cde-like sky options
my $sky = 0;
my $skyColorsOpt = "";
my $skyComp = "linear";
my $patternFile = "$ftDataDir/themes/cde/background/pattern";
my $patternType = "";
my $patternXpm = "";
my $patternGap = 1;
my $patternY = "";

# set the background
my $setRoot = 0;
my $setRootProg = "fvwm-root --retain-pixmap";
#my $setRootProg = "fvwm-root";
my $setRootTmpFile = "/tmp/$userName-ft-back.xpm";

# rotate
my $rotate = 0;

# colorize
my $colorize = 0;
my $rizeColorsOpt = "gray40,gray60";
my $transColor = "";
my $rizeComp = "l";
my $rizeRule = "max";

# Info
my $xcolorsInfo = 0;
my $xcolorsetsInfo = 0;
my $colorToShow = undef;

# colors shemes
my $colorSchemes = 0;
my $colorSchemesFores = "";
my $colorSchemesExt = "dp";

# others
my $verbose = 1;
my $verboseOpt = -1;
my $site = 0;
my $createSymLink = "";
my $install = 0;
my $update = 0;
my $debug = "";
my $beFast = 0;

# constant (these constant are in main:: because this speed the code)
my @Hex=(0 .. 9, "a" .. "f");
my $singleIdList =
	q( .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk) .
	q(lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|);
# ` <- stupid fix for my stupid Xemacs! olicha

GetOptions(
	"help"               => \&showHelp,
	"version"            => \&showVersion,
	"in-dir=s"           => \$inDir,
	"out-dir=s"          => \$outDir,
	"in-file=s"          => \$inFile,
	"out-file=s"         => \$outFile,
	"magick-path=s"      => \$magickPath,
	"magick-bpp=i"       => \$magickBpp,
	"magick-colors=i"    => \$magickColors,
	"magick-colorspace=s" => \$magickColorspace,
	"convert"            => \$convert,
	"gnome"              => \$gnome,
	"kde2"               => \$kde2,
	"kde2-hi"            => \$kde2Hi,
	"postfix"            => \$postfix,
	"trans-filter!"      => \$transFilter,
	"size-48x48=s"       => \$size{"48x48"},
	"size-56x56=s"       => \$size{"56x56"},
	"size-32x32=s"       => \$size{"32x32"},
	"size-16x16=s"       => \$size{"16x16"},
	"size-tiles=s"       => \$size{"tiles"},
	"build-16x16!"       => \$build{"16x16"},
	"build-48x48!"       => \$build{"48x48"},
	"build-56x56!"       => \$build{"56x56"},
	"build-32x32!"       => \$build{"32x32"},
	"build-tiles!"       => \$build{"tiles"},
	"preserve"           => \$preserve,
	"tile=s"             => \$tile,
	"tile-48x48=s"       => \$tileOpt{"48x48"},
	"tile-16x16=s"       => \$tileOpt{"16x16"},
	"tile-56x56=s"       => \$tileOpt{"56x56"},
	"tile-32x32=s"       => \$tileOpt{"32x32"},
	"tile-tiles=s"       => \$tileOpt{"tiles"},
	"border=s"           => \$addBorder,
	"border-48x48=s"     => \$addBorderOpt{"48x48"},
	"border-16x16=s"     => \$addBorderOpt{"16x16"},
	"border-56x56=s"     => \$addBorderOpt{"56x56"},
	"border-32x32=s"     => \$addBorderOpt{"32x32"},
	"border-tiles=s"     => \$addBorderOpt{"tiles"},
	"threshold=i"        => \$threshold,
	"colorize-colors=s"  => \$rizeColorsOpt,
	"colorize"           => \$colorize,
	"trans-color=s"      => \$transColor,
	"colorize-comp=s"    => \$rizeComp,
	"colorize-rule=s"    => \$rizeRule,
	"sky"                => \$sky,
	"sky-colors=s"       => \$skyColorsOpt,
	"pattern-file=s"     => \$patternFile,
	"pattern-type=s"     => \$patternType,
	"pattern-xpm=s"      => \$patternXpm,
	"pattern-gap=s"      => \$patternGap,
	"pattern-y=i"        => \$patternY,
	"sky-comp=s"         => \$skyComp,
	"setroot"            => \$setRoot,
	"setroot-prog=s"     => \$setRootProg,
	"rotate=s"           => \$rotate,
	"show-xcolors"       => \$xcolorsInfo,
	"show-xcolorsets"    => \$xcolorsetsInfo,
	"show-color-info=s"  => \$colorToShow,
	"verbose=i"          => \$verboseOpt,
	"site"               => \$site,
	"create-symlink=s"   => \$createSymLink,
	"update"             => \$update,
	"ft-install"         => \$install,
	"be-fast"            => \$beFast,
	"colorschemes"       => \$colorSchemes,
	"colorschemes-fores=s" => \$colorSchemesFores,
	"colorschemes-ext=s" => \$colorSchemesExt,

	# for compatibility only (to be removed)
	"cde-sky"            => \$sky,
	"set-background"     => \$setRoot,
	"set-back-prog=s"    => \$setRootProg,
	"magick-space=s"     => \$magickColorspace,
	"add-border-48x48=s" => \$addBorderOpt{"48x48"},
	"add-border-16x16=s" => \$addBorderOpt{"16x16"},
	"add-border-56x56=s" => \$addBorderOpt{"56x56"},
	"add-border-32x32=s" => \$addBorderOpt{"32x32"},
	"add-border-tiles=s" => \$addBorderOpt{"tiles"},
	"add-border=s"       => \$addBorder,
	"48x48-size=s"       => \$size{"48x48"},
	"56x56-size=s"       => \$size{"56x56"},
	"32x32-size=s"       => \$size{"32x32"},
	"16x16-size=s"       => \$size{"16x16"},
	"tiles-size=s"       => \$size{"tiles"},
) || wrongUsage();

# compute verbosity!
if ($setRoot) {
	$verbose = 0;
}
if ($verboseOpt != -1) {
	$verbose = $verboseOpt;
}

if ($install) {
	$site = 1;
	$convert = 1;
	$update = 1;
	$createSymLink = "48x48,16x16";
	die "$scriptName: --install needs --gnome or --kde2 (but not both)\n"
		unless $gnome xor $kde2;
}

die "$scriptName: --convert and --setroot are incompatible\n"
	if $convert && $setRoot;

die "$scriptName: --gnome and --kde2 are incompatible\n"
	if $gnome && $kde2;

if ($xcolorsInfo || $xcolorsetsInfo) {
	&showXcolorsInfo($xcolorsetsInfo);
	exit;
}

if (defined $colorToShow) {
	&showColorInfo($colorToShow);
	exit;
}

if ($sky) {
	&processSky();
	exit;
}

$convert = 1 if $gnome || $kde2;

# see if we have to check magic:
if ($convert || $tile =~ /expand/) {
	($magickPath,$magickBpp) = checkMagick();
}

if ($convert) {
	foreach (keys %build) {
		push @types, $_ if $build{$_};
	}
	@types = ("preserve") if $preserve;
	&transform();
	exit;
}

if ($tile || $addBorder || $colorize || $rotate ne "0") {
	@types = ("preserve");
	&transform();
	exit;
}

if ($createSymLink ne "") {
	@types = ();
	&transform();
	exit;
}

if ($colorSchemes || $colorSchemesFores ne "") {
	&processColorSchemes();
	exit;
}

if ($setRoot && $inFile) {
	&setRoot($inFile);
	exit;
}

wrongUsage();

#------------------------------------------------------------------------------
# Transform, main "convert" procedure
#------------------------------------------------------------------------------

sub transform {
	my $type;
	my $dirtype;
	my $dir;
	my $sizeOpt;

	if ($gnome) {
		my $testExecFile = 'gnome-session';
		my $defaultGnomeInstallDir = '/usr';

		my $gnomeInstallDir = getExecFileDir($testExecFile);
		$gnomeInstallDir =~ s,/$,,;
		$gnomeInstallDir =~ 	s,/[-A-Za-z]+$,,;
		$gnomeInstallDir =~ s,/$,,;
		$gnomeInstallDir = $defaultGnomeInstallDir
			if $gnomeInstallDir eq 'not found';
		$inDir = "$gnomeInstallDir/share/pixmaps";
		$postfix = "gnome";
		$inFile = "";
		$outFile = "";
	}

	if ($kde2) {
		$postfix = "kde2";
		$inDir = "";
		my $kdeDir = $ENV{'KDEDIR'} || "";
		die "KDEDIR must be set for --kde2" if $kdeDir eq "";
		$inDir = "$kdeDir/share/icons";
	}

	$inDir = "$inDir/" unless $inDir =~ /\/$/ || $inDir eq "";
	if ($inDir !~ /^\// && $inFile !~ /^\//) {
		my $tmp =`pwd`;
		chomp($tmp);
		$inDir ="$tmp/$inDir";
	}

	if ($inFile ne "") {
		if ($inFile =~ /\//) {
			my $index = rindex($inFile,"/");
			if ($inFile =~ /^\//) {
				$inDir = substr($inFile,0,$index);
			} else {
				$inDir = "$inDir" . substr($inFile,0,$index);
			}
			$inFile = substr($inFile,$index+1);
		}
		die "No such file: $inDir/$inFile !" unless -f "$inDir/$inFile";
	}
	$inDir =~ s/\/$//;

	my $transformOutDir = $userTransformOutDir;
	$transformOutDir = $siteTransformOutDir if $site;
	$outDir = "$transformOutDir/$outDir" if ($outDir eq "");

	if ($inFile ne "" && $outFile eq "" && !$convert) {
		if ($setRoot) { $outFile = $setRootTmpFile; }
		else {
			die "If you specify an in-file you must specify an out-file\n" .
				"(except for --setroot and --convert)\n";
		}
	}

	if ($outDir !~ /^\// && $outFile !~ /^\//) {
		my $tmp =`pwd`;
		chomp($tmp);
		$outDir ="$tmp/$outDir";
	}
	$outDir = "$outDir/" unless $outDir =~ /\/$/ || $outDir eq "";
	if ($outFile ne "") {
		if ($outFile =~ /\//) {
			my $index = rindex($outFile,"/");
			if ($outFile =~ /^\//) { $outDir = substr($outFile,0,$index); }
			else { $outDir = "$outDir" . substr($outFile,0,$index);}
			$outFile = substr($outFile,$index+1);
		}
	}
	$outDir =~ s/\/$//;

	-d $outDir || mkdir("$outDir", 0775) ||
		die "impossible to create $outDir \n\t (we do not create more than one".
			" level of directory).\n";


	# compute the threshold:
	if ($convert && $transFilter) {
		if ($threshold > 100) { $threshold = 100; }
		if ($threshold < 0) { $threshold = 0; }
		$threshold = int(((2**$magickBpp)-1)*($threshold/100));
	}

	# compute the postfix
	$postfix = "-".$postfix if $postfix ne "";

	for $type (@types) {

		if ($type eq "preserve") {
			$dirtype = "";
			$sizeOpt = "";
			$dir = $inDir;
		} elsif ($type eq "tiles") {
			$dir = "$inDir/tiles";
			$dirtype = "/$type$postfix";
			$sizeOpt = "-geometry $size{$type}" if $size{$type} ne "";
		} else {
			$dir = $inDir;
			if ($kde2) {
				if ($kde2Hi && -d "$inDir/hicolor/$type/apps") {
					$dir = "$inDir/hicolor/$type/apps";
				}
				elsif (-d "$inDir/locolor/$type/apps") {
					$dir = "$inDir/locolor/$type/apps";
				}
				elsif (-d "$inDir/hicolor/$type/apps") {
					$dir = "$inDir/hicolor/$type/apps";
				}
				else {
					next 
				}
			}
			$dirtype = "/$type$postfix";
			$sizeOpt = "-geometry $size{$type}" if $size{$type} ne "";
			$sizeOpt = "" if ($kde2);
		}

		-d "$outDir$dirtype" || mkdir("$outDir$dirtype", 0775) ||
			die "impossible to create $outDir$dirtype";

		# tile ?
		my $haveToTile = 0;
		my $tileXpm; my $tileType;
		my ($xTile,$yTile) = (0, 0);
		if ($convert && $tileOpt{$type} ne "") {
			($tileXpm,$tileType,$xTile,$yTile) = getTile("$tileOpt{$type}");
			$haveToTile = 1;
		} elsif ($tile ne "") {
			($tileXpm,$tileType,$xTile,$yTile) = getTile("$tile");
			$haveToTile = 1;
		}

		# add border ?
		my $haveToAddBorder = 0;
		my ($xBorder, $yBorder) = (0, 0);
		my $borderColor = "Gray0";
		if ($convert && $addBorderOpt{$type} =~ /^\+(\d+)\+(\d+)[,]*(.*)/) {
			$xBorder = $1; $yBorder = $2;
			$borderColor = $3; $haveToAddBorder = 1;
		} elsif ($addBorder =~ /^\+(\d+)\+(\d+)[,]*(.*)/) {
			$xBorder = $1; $yBorder = $2;
			$borderColor = $3; $haveToAddBorder = 1;
		}

		my $unlinkmask = 0;

		my $info = "";
		my $infoType = "";
		if ($convert) {
			$info .= "convert ";
			$infoType .= "$type xpm";
		}
		$info .= "rotate " if ($rotate);
		$info .= "colorize " if ($colorize);
		$info .= "tile " if ($haveToTile);
		$info .= "borderize " if ($haveToAddBorder);
		$info .= " (update) " if ($update);
		if ($inFile eq "") {
			myPrint (2,"-------------------------------------------\n");
			myPrint (1,"$info images in $dir\ninto $type xpm images " .
			  "in $outDir$dirtype\n");
			myPrint (2,"\n");
		}

		my @fileList;
		if ($inFile eq "") { @fileList = scanDir($dir);}
		else { @fileList = ("$inFile"); }
		
		# bad hack:
		if ($kde2) {
			my @others = qw(filesystems actions devices mimetypes);
			if ($kde2Hi) {
				push @others, ("../../locolor/$type/apps",
									"../../locolor/$type/filesystems",
									"../../locolor/$type/actions",
									"../../locolor/$type/devices",
									"../../locolor/$type/mimetypes");
			} else {
				push @others, ("../../hicolor/$type/apps",
									"../../hicolor/$type/filesystems",
									"../../hicolor/$type/actions",
									"../../hicolor/$type/devices",
									"../../hicolor/$type/mimetypes");
			}
			my $o;
			foreach $o (@others) {
				if (-d "$dir/../$o/") {
					my @l = scanDir("$dir/../$o/");
					push @fileList, map {"../$o/$_"} @l;
				}
			}
		}
		my $file = "";

		foreach $file (@fileList) {
			my $xpmFile = "";
			my $mainXpm = 0;
			my $xpmdir = $dir;

			if ($convert) {
				$xpmFile = changeIconFilename($file);
				# the back hack continued:
				if ($xpmFile =~ /^\.\./) {
					$xpmFile = substr($xpmFile,rindex($xpmFile,"/"));
					next if -f "$outDir$dirtype/$xpmFile";
				}
				next if $update && -f "$outDir$dirtype/$xpmFile";
			}
			if ($convert) {
				myPrint (1,"transform $file ...");
				myPrint(2,"\n\t convert to $type xpm ... ");
				system("$magickPath/convert -colors $magickColors $sizeOpt ".
					"-colorspace $magickColorspace ".
					"$dir/$file $outDir$dirtype/$xpmFile");
				$xpmdir = "$outDir$dirtype";
				myPrint(2,"done");
			} elsif ($colorize || $tile || $addBorder || $rotate) {
				next unless $file =~ /.xpm$/;
				$xpmFile = $file;
				next if ($update && -f "$outDir$dirtype/$xpmFile");
				myPrint (1,"transform $file ...");
			}

			if ($convert && $file !~ /.xpm$/ && hasMatte("$dir/$file")
				&& $transFilter) {
				myPrint(2,"\n\t transparent filter ... ");
		 		system("$magickPath/convert -mono -colorspace $magickColorspace ".
					"-layers Matte $sizeOpt -threshold $threshold ".
					"$dir/$file $outDir$dirtype/.mask.xpm");
				$unlinkmask = 1;
		 		$mainXpm = loadXpm("$outDir$dirtype/$xpmFile");
				my $maskXpm = loadXpm("$outDir$dirtype/.mask.xpm");
				applyTrans($mainXpm, $maskXpm,"None");
				myPrint(2,"done");
			}

			if ($rotate ne "0") {
				myPrint(2,"\n\t rotate ... ");
				$mainXpm = loadXpm("$xpmdir/$xpmFile")
					if ref($mainXpm) ne "HASH";
				rotateXpm($mainXpm,$rotate);
				myPrint(2,"done");
			}

			if ($colorize) {
				myPrint(2,"\n\t colorize ... ");
				$mainXpm = loadXpm("$xpmdir/$xpmFile")
					if ref($mainXpm) ne "HASH";
				colorizeXpm($mainXpm);
				myPrint(2,"done");
			}

			if ($haveToTile) {
				myPrint(2,"\n\t tile ... ");
				$mainXpm = loadXpm("$xpmdir/$xpmFile")
					if ref($mainXpm) ne "HASH";
				applyTile($mainXpm,$tileXpm,$tileType,$xTile,$yTile);
				myPrint(2,"done");
			}

			if ($haveToAddBorder) {
				myPrint(2,"\n\t add border ... ");
				$mainXpm = loadXpm("$xpmdir/$xpmFile")
					if ref($mainXpm) ne "HASH";
				addBorder($mainXpm,$xBorder,$xBorder,$yBorder,$yBorder,
					$borderColor);
				myPrint(2,"done");
			}


			$xpmFile = $outFile if ($inFile ne "" && $outFile ne "");
			writeXpm($mainXpm,"$outDir$dirtype/$xpmFile")
				if $mainXpm && ref($mainXpm->{'def'}) eq "ARRAY";

			myPrint(2,"\n");
			myPrint(1, "done\n");
			myPrint(2,"\n");
		}
		unlink("$outDir$dirtype/.mask.xpm") if $unlinkmask;
	}

	if ($setRoot && $outFile ne "") {
		&setRoot("$outDir/$outFile");
	}

	if ($createSymLink ne "") {
		createSymLink();
	}

}

#-----------------------------------------------------------------------------
# useful func for transform

# parse a tile option and return the good xpm
sub getTile($) {
	my $tile = shift;
	my $xpm;
	my $type = "tile";
	my ($x,$y) = (0, 0);

	if ($tile =~ /^\s*color:(.*)\s*/) {
		my $color = $1;
		if ($color =~ /^\+(\d+)\+(\d+)[,](.*)/) {
			$color = encode16bpp($3);
			$x=$1; $y=$2;
		}
		$xpm = createMonoXpm(1,1,$color);
	} elsif ($tile =~ /^\s*expand:(.*)\s*/) {
		$xpm = $1;
		if ($xpm =~ /^\+(\d+)\+(\d+)[,](.*)/) {
			$xpm = $3;
			$x=$1; $y=$2;
		}
		die "no tile xpm $tile" unless -f $xpm;
		$type = "expand";
	} elsif ($tile =~ /^\s*center:(.*)\s*/) {
		$tile = $1;
		if ($tile =~ /^\+(\d+)\+(\d+)[,](.*)/) {
			$tile = $3;
		}
		die "no tile xpm $tile" unless -f $tile;
		$xpm = loadXpm($tile);
		$type = "center";
	}  elsif ($tile =~ /^\s*tile:(.*)\s*/) {
		$tile = $1;
		if ($tile =~ /^\+(\d+)\+(\d+)[,](.*)/) {
			$tile = $3;
			$x=$1; $y=$2;
		}
		die "no tile xpm $tile" unless -f $tile;
		$xpm = loadXpm($tile);
	} else {
		if ($tile =~ /^\+(\d+)\+(\d+)[,](.*)/) {
			$tile = $3;
			$x=$1; $y=$2;
		}
		die "no tile xpm $tile" unless -f $tile;
		$xpm = loadXpm($tile);
	}
	return ($xpm,$type,$x,$y);
}

#-------------------------------------
# FROM fvwm2gnome
sub changeIconFilename {
	my ($iconName) = @_;
	my $xpmString = "xpm";

	my $choppedIconName = substr($iconName, 0, -3);
	$iconName = "$choppedIconName$xpmString";
	return $iconName;
}

sub scanDir {
	my ($scanPath) = @_;
	my  @dirList = ();
	opendir(DIR, "$scanPath") || die "cannot open dir $scanPath\n";
	foreach (readdir(DIR)) {
		# must be fixed one day: found all the formats supported
		# by ImageMagick ...
		push @dirList, $_
			if (/.png$/ || /.xpm$/ || /.jpg$/ || /.gif$/);
	}
	closedir(DIR);
	return(@dirList);
}

#-------------------------------------
#

sub getExecFileDir($) {
	my $execFile = shift;
	my $done = 0;
	my $return = 'not found';
	foreach (@pathDirs) {
		my $test = "$_/$execFile";
		return $_ if -x  $test;
	}
	return $return;
}

#-------------------------------------
# create the symlink: need more work

sub createSymLink {
	my $i = 0;
	my $type;
	my @dest = split(",", $createSymLink);
	@dest = map { "$outDir/$_$postfix" } @dest;
	die "No directory to link!\n"
		unless -d $dest[0] && -d $dest[1];
	myPrint(1,"Create symbolic link\n");
	foreach $type ("norm", "mini") {
		unlink("$ftImagesDir/$type$postfix");
		symlink("$dest[$i]","$ftImagesDir/$type$postfix") ||
			die "Can't symlink $dest[$i] to $ftImagesDir/$type$postfix";
		myPrint(2,"symlink $dest[$i] to $ftImagesDir/$type$postfix\n");
		$i++;
	}
}

#-------------------------------------
# check for Image Magick: there is now a Magick-config, but
# old (and very good) version of ImageMagick does not have it.

sub checkMagick {
	my $dir  = "";
	my $bpp = $magickBpp;

	myPrint(2,"\n");
	myPrint(1,"Check for ImageMagick ...");
	if ($magickPath eq "") {
		$dir = getExecFileDir("convert");
		$dir =~ s/\/$//;
		if	($dir eq 'not found') {
			die "Gasp\nconvert from ImageMagick was not found!!\n".
				"\t use the --magick-path option or install ImageMagick.\n"
		}
	} else {
		$dir = $magickPath;
		$dir =~ s/\/$//;
		if (! -x "$dir/convert") {
			die "Gasp\nconvert from ImageMagick was not found in:\n".
				"\t$dir, check the --magick-path option\n";
		}
	}
	myPrint(1," Ok\n");

	open(VER,"$dir/convert|");
	my @version = <VER>;
	close(VER);
	my $verLine = $version[0];
	myPrint(2, "$verLine");
	if ($magickBpp == 0 && $verLine =~ /Q:([0-9][0-9]*)/) {
		$bpp = "$1";
		myPrint(2, "QuantumLeap: $bpp\n");
	} elsif ($magickBpp == 0) {
		$bpp = 16;
		myPrint(1,"WARN: QuantumLeap: no indication from ImageMagick\n".
			"\t 16 is assumed (see the -magick-bpp option)\n");
	}

	return ($dir,$bpp);
}

#------------------------------------------------------------------------------
# cde-like sky (from jos-cdeskylike)
#------------------------------------------------------------------------------

sub processSky() {
	my @pattern=();
	my $coef;
	if ($patternType ne "") { @pattern = createPattern() }
	elsif ($patternXpm ne "") { @pattern = createPatternXpm() }
	else {
		die "pattern file $patternFile not found\n" unless -f $patternFile;
		open(INFILE,$patternFile);
		@pattern = <INFILE>;
		close(INFILE);
	}

	if ($patternY !~ /^\d+$/) {
		my ($ScreenX, $ScreenY) = `xdpyinfo|grep dimensions:` =~ / (\d+)x(\d+)/;
		$patternY = $rotate =~ /90/ ? $ScreenX:$ScreenY;
	}

	my @consPar = ();
	my $p; my $W=0; my $test = -1; my $Y=0;
	$patternGap = 1 if ($patternGap <= 0 || $patternGap !~ /^\d+$/);
	foreach $p (@pattern) {
		$test++;
		next if ($test % $patternGap != 0);
		chomp($p);
		push @consPar, split("",$p);
		$Y=$Y+1;
	}
	$W=length($pattern[0]);

	my $nbrColors = 0;
	my $skyColors = [];

	## evaluate the second gradient color ourselves if only one is given
	## this is hilightColor() with a different factor...
	if ($skyColorsOpt && $skyColorsOpt !~ /[,]/) {
		my $color = encode16bpp($skyColorsOpt);
		my $a = bppTo3intArray($color);
		my $i;
		for ($i = 0; $i < 3; $i++) {
			$a->[$i] = max(255/5, $a->[$i]);
			$a->[$i] = min(255, ($a->[$i] * 180) / 100);  # was 110
			$a->[$i] = int($a->[$i]);
		}
		$skyColorsOpt = encode16bpp($a) . ",$skyColorsOpt";
	}

	my @tmp = split('[,]', $skyColorsOpt);
	my $c;
	foreach $c (@tmp) {
		$skyColors->[$nbrColors] = bppTo3intArray(encode16bpp($c));
		$nbrColors++;
	}

	my $NY = int($patternY/$Y)+1;
	my $dx = $NY;

	my $xpm = createMonoXpm($W,1,"None");
	my $def = $xpm->{'def'};
	my $pixels = $xpm->{'pixels'};
	my ($i,$k) = (0, 0);
	my $id; my $nextId;

	while ($i < $NY) {
		my $t; my $j = 0; my $done = 0; my $l = 0;
		if ($i == 0) {
			$t=interpolateColors($skyColors,$nbrColors,$i/$dx,$skyComp,$i);
			$id = addColor($xpm,encode16bpp($t));
		} else { $id = $nextId; }
		$t=interpolateColors($skyColors,$nbrColors,($i+1)/$dx,$skyComp,$i+1);
		$nextId=addColor($xpm,encode16bpp($t));
		$id .= " " if length($id) < length($nextId);
		while (defined $consPar[$l] && !$done) {
			$pixels->[$k]->[$j] = $consPar[$l] eq "+" ? $id:$nextId;
			$j++; $l++;
			if ($j == $W) {
				$k++; $j=0; $def->[1] = $k;
				if ($k == $patternY) { $done = 1; $i=$NY;}
			}
		}
		$i++;
	}

	rotateXpm($xpm,$rotate) if $rotate ne "0";

	$outFile = $setRootTmpFile if $outFile eq "";
	writeXpm($xpm,$outFile);

	&setRoot($outFile) if $setRoot;
}

#-------------------------------------
# create some pattern. Any other idea?
# Can one create the "cde-like" like pattern?

sub createPattern () {
	my $type = "uniform";
	my @pattern = ();
	my ($i, $n);
	my $coef = 1;
	my $coef2 = undef;
	my @coefs = ();

	# perl: '?:' may be ommited, but then $2 should be $3.
	if ($patternType =~ /^random:(\d+)(?::(\d+))?$/) {
		$type = "rand";
		$coef = $1;
		$coef2 = $2 || 8;
	} elsif ($patternType =~ /^uniform:(\d+)(?::(\d+))?$/) {
		$type = "uniform";
		$coef = $1;
		$coef2 = $2 || 8;
	} elsif ($patternType =~ /^square:(\d+)(?::(\d+))?$/) {
		$type = "square";
		$coef = $1;
		$coef2 = $2 || $coef;
	} elsif ($patternType =~ /^altern:(\d+(?:,\d+)*)(?::(\d+))?$/) {
		$type = "altern";
		@coefs = split(/,/, $1);
		push @coefs, $coefs[0] if @coefs == 1;  # for backward compatibility
		$coef2 = $2 || 8;
	} elsif ($patternType =~ /^(\d+)(?:[:,](\d+))?$/) {
		$type = "uniform";
		$coef = $1;
		$coef2 = $2 || 8;
	} else {
		die "Bad --pattern-type argument\n";
	}

	if ($type eq "uniform") {
		my $p = "+" x $coef2 ."\n";
		for ($i=0; $i < $coef; $i++) { push @pattern, $p }
	}
	elsif ($type eq "altern") {
		my @v = ("+", "-");
		for ($n = 0; $n < @coefs; $n++) {
			my $p = $v[$n % 2] x $coef2 . "\n";
			for ($i = 0; $i < $coefs[$n]; $i++) { push @pattern, $p }
		}
	}
	elsif ($type eq "square") {
		my $p = "+" x $coef2 . "-"x$coef2 ."\n";
		for ($i=0; $i < $coef; $i++) { push @pattern, $p }
	}
	elsif ($type eq "rand") {
		for ($i=0;$i<$coef;$i++) {
			my $p = ""; my $j; my $r;
			for ($j=0; $j<$coef2; $j++) { $r= rand 1; $p .= $r > 0.5 ? "+" : "-" }
			push @pattern, "$p\n";
		}
	}
	return @pattern;
}

#-------------------------------------
# create pattern from an xpm

sub createPatternXpm () {
	my $xpm = loadXpm($patternXpm);
	my ($c,$r,$nc) = ($xpm->{'def'}->[0],$xpm->{'def'}->[1],$xpm->{'def'}->[2]);
	my $colors = $xpm->{colors};
	my $pixels = $xpm->{pixels};
	my $ids = $xpm->{ids};
	my $id = "";
	my @pattern = ();

	# found the transparent id
	my $i = 0; my $done =0;
	while($i < $nc && !$done) {
		if ($colors->[$i] eq "None") {
			$id = $ids->[$i];
			$done = 1;
		}
		$i++;
	}
	for ($i=0; $i<$r; $i++) {
		my $pm = ""; my $j;
		for ($j=0;$j<$c;$j++) { $pm .= $pixels->[$i]->[$j] eq $id ? "+" :"-"; }
		push @pattern, "$pm\n";
	}
	return @pattern;
}

#------------------------------------------------------------------------------
# evaluating foreground from background in color shemes
#------------------------------------------------------------------------------

sub processColorSchemes () {
	my ($lines, $value, $white, $black) = ("","","","");
	my $evalFores = 0;
	my @files = ();
	my $file;

	$inDir = "$inDir/" unless $inDir =~ /\/$/ || $inDir eq "";
	if ($inDir !~ /^\//) {
		my $tmp =`pwd`;
		chomp($tmp);
		$inDir ="$tmp/$inDir";
	}
	$inDir =~ s/\/$//;

	if ($outDir !~ /^\//) {
		my $tmp =`pwd`;
		chomp($tmp);
		$outDir ="$tmp/$outDir";
	}
	$outDir =~ s/\/$// unless $outDir eq "/";

	-d $outDir || mkdir("$outDir", 0775) ||
		die "Cannot make dir $outDir: [$!]\n"
		. "\t(we do not create more than one level of directories).\n";

	opendir(DIR, "$inDir") || die "Cannot open dir $inDir\n";
	foreach (readdir(DIR)) {
		push @files, $_ if  /\.$colorSchemesExt$/;
	}

	if ($colorSchemesFores ne "") {
		$evalFores = 1;
		($lines, $value, $white, $black) = split(",", $colorSchemesFores);
		die "Bad --colorschemes-fores parameters"
			if ($lines !~ /^\d+$/ || (defined $value && $value !~ /^\d+$/));
		$value = 128 if !defined $value  || $value eq "";
		$white = "white" if !defined $white || $white eq "";
		$black = "black" if !defined $black || $black eq "";
	}

	myPrint(1,"\ntransform color schemes *.$colorSchemesExt in\n".
		"\t$inDir into\n\t$outDir\n\n");

	foreach $file (@files) {
		myPrint(2,"transform $file ... ");
		open(FILE, "$inDir/$file") || die "Cannot open $inDir/$file: [$!]\n";
		my @f = <FILE>;
		close(FILE);
		foreach (@f) { chomp }
		my @g = ();

		if ($evalFores) {
			my $i = 0;
			while (defined $f[$i] && $i < $lines) {
				#chomp($f[$i]);
				$g[$i] = $f[$i];
				my $t = bppTo3intArray(encode16bpp($f[$i]));
				my $grey = greyval($t);
				if ($grey >= $value) { $g[$i+$lines] = $black }
				else { $g[$i+$lines] = $white }
				$i++;
	  	 	}
		} 
		else { @g = @f; }

		open(OUT,">$outDir/$file") || die "Cannot write to $outDir/$file: [$!]\n";
		foreach (@g) { print OUT "$_\n" }
		close(OUT);
		myPrint(2,"done\n");
	}
}

#------------------------------------------------------------------------------
# set the root image
#------------------------------------------------------------------------------

sub setRoot ($) {
	my $file = shift || "*none*";
	die "No file $file to set root\n" unless -f $file;
	system("$setRootProg $file");
	unlink($file) if $file eq $setRootTmpFile;
}

#------------------------------------------------------------------------------
# Info
#------------------------------------------------------------------------------

sub formatColorInfoLine ($$$) {
	my $colorName = shift;
	my $colorRgb = encode16bpp(shift);
	my $base = shift;

	my ($r, $g, $b) = ($colorRgb =~ /^#(..)..(..)..(..)..$/);
	my $trio = bppTo3intArray($colorRgb);
	return sprintf("%-${base}s  %7s    %3d %3d %3d",
		"$colorName:", "#$r$g$b", @{$trio});
}

sub formatColorInfoHeadLine ($) {
	my $base = shift;
	return sprintf("%-${base}s  %8s   %s",
	"Color name", "8bpp RGB", "Decimal RGB");
}

sub showXcolorsInfo ($) {
	my $set = shift;
	my $base = $set ? 25 : 17;
	my $nbr;
	my $key;

	loadRGB();
	print formatColorInfoHeadLine($base), "\n\n";

	foreach $key (sort keys %rgb) {
		$nbr = $rgb{"$key"};
		print formatColorInfoLine($key, $nbr, $base), "\n";
		if ($set) {
			my $prefix;
			foreach $prefix ("shadow","hilight","random","top255","top50") {
				print formatColorInfoLine("$prefix $key", "$prefix$nbr", $base),
					"\n";
			}
			print "\n";
		}
	}
}

sub showColorInfo {
	my $color = shift;
	my $base = 25;
	loadRGB();
	my $nbr = encode16bpp("$color");
	my $a = bppTo3intArray($nbr);

	print "Information on $color:\n\n";

	my $best = 256;
	my $t;
	my @x =();
	my $key;
	foreach $key (sort keys %rgb) {
		my $b = bppTo3intArray($rgb{"$key"});
		$t =
		  sqrt(($$a[0]-$$b[0])**2+($$a[1]-$$b[1])**2+($$a[2]-$$b[2])**2)/sqrt(3);
		push @x, $key if $t == $best;
		if ($t < $best) { $best = $t; @x = ("$key"); }
	}

	if ($best == 0) {
		print "\tX color name(s): @x\n";
	} else {
		print "\tClosest X color name(s): ", join(', ',@x), " (".int($best).")\n";
	}
	print "\n";
	print "\t", formatColorInfoHeadLine($base), "\n\n";
	print "\t", formatColorInfoLine("$color", "$nbr", $base), "\n";

	my $prefix;
	foreach $prefix ("shadow", "hilight", "random", "top255", "top200",
		"top150", "top100", "top50") {
		print "\t", formatColorInfoLine("$prefix $color", "$prefix$nbr", $base),
			"\n";
	}
	print "\n";

}

#------------------------------------------------------------------------------
# an xpm lib
#------------------------------------------------------------------------------

sub loadXpm ($) {

	my $file = shift;
	my $xpm;

	open(XPM, $file) || die "Impossible to load $file";
	my $count = -1;
	my ($isColors,$isPixels) = (0, 0);
	my ($pixCount, $inComment) = (0, 0);
	my ($nc,$cpp);
	my ($i,$re);
	my $def = [];
	my $pixels = [];
	my $colors = [];
	my $ids = [];

	while(<XPM>) {
		# "in comment" need amelioration ...
		$inComment = 1 if (/^\s*\/\*/);
		if ($inComment) {
			$inComment = 0 if /\*\//;
			next;
		}
		s/^\s*//;
		next if $_ !~ /^\"/;
		chomp;
		s/^\"//;
		s/\"*,*\}*;*\s*$//;
		my $line = $_;
		if ($isPixels) {
			# can we do this faster ?
			$pixels->[$pixCount] = [ $line =~ /$re/ ];
			#die "bad number of columns in xpm file $file\n"
			#	if ($#{@{$pixels->[$pixCount]}} != $def->[0]);
			$pixCount++;
		}
		elsif ($isColors) {
			$ids->[$count] = substr($line,0,$cpp);
			$line = substr($line,$cpp+1);
			$line =~ s/^\s*//;
			# suppress these stupids "s None"
			$line =~ s/s\s+none$//i;
			$line =~ s/^\s*c\s+//;
			$line =~ s/\s*$//;
			# encode in 16 bits per pixels ...
			die "bad color definition in $file" if ($line eq "");
			$colors->[$count]=encode16bpp($line);
			$count++;
			if ($count == $nc) {
				$isColors = 0;
				$isPixels = 1;
			}
		}
		else {
			$isColors = 1;
			$def = [ $line =~ /\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/ ];
			for ($i=0;$i<4;$i++) {
				die "it does not seem that $file is an xpm (definition)"
					unless (defined($def->[$i]));
			}
			$nc = $def->[2];
			$cpp = $def->[3];
			$re = "(" . "." x $cpp . ")";
			$re = "$re" x $def->[0];
			$count++;
		}
	}
	close(XPM);
	# check if the format is ok:
	die "it does not seem that $file is an xpm\n"
		if ($count < 1 || !$isPixels || ($def->[1] != $pixCount));

	$xpm->{'def'} = $def;
	$xpm->{'colors'} = $colors;
	$xpm->{'ids'} = $ids;
  	$xpm->{'pixels'} = $pixels;
	return $xpm;
}

#-------------------------------------
# write an xpm to a file

sub writeXpm ($;$) {
	my $xpm = shift;
	my $file = shift || "";
	my $out ="";
	my $def = $xpm->{'def'};
	my $pixels = $xpm->{'pixels'};
	my $i;

	$out .= qq(/* XPM */\nstatic char *ft_xpm[] = {\n);
	$out .= qq(/* columns rows colors chars-per-pixel */\n);
	$out .= qq("@{$def}",\n);

	for($i = 0; $i < $def->[2]; $i++) {
		$out .= qq("$xpm->{'ids'}->[$i] c $xpm->{'colors'}->[$i]",\n);
	}
	$out .= "/* pixels */\n";

	for ($i=0; $i < $def->[1]; $i++) {
		$out .= qq(");
		$out .= join ("", @{$pixels->[$i]}[0 .. $def->[0]-1]);
		$out .= qq(",\n);
	}
	chomp($out);
	$out .= "\n};\n";

	if (defined($file)) {
		# use saveFile() from possible Util::FileSystem lib?
		open(OUT, ">$file") || die "Can't write $file: [$!]";
		print OUT $out;
		close(OUT);
	} else {
		print $out;
	}
}

#-------------------------------------
# create a monochrome xpm

sub createMonoXpm($$$) {
	my $columns = shift || 1;
	my $rows =  shift || 1;
	my $color = shift || "None";
	my $xpm; my $i; my $j;

	$xpm->{'def'} = [$columns, $rows, 1, 1];
	$xpm->{'colors'}->[0] = encode16bpp($color);
	$xpm->{'ids'}->[0] = " ";

	for ($i = 0; $i<$rows; $i++) {
		for ($j = 0; $j<$columns; $j++) { $xpm->{'pixels'}->[$i]->[$j] = " "; }
	}
	return $xpm;
}

#-------------------------------------
# get the color id from its nbr.
# we need the reverse function ...

sub getColorId($$) {
	my $nbr = shift;
	my $cpp = shift;
	my $id = "";
	my $k;

	my $max = 92**$cpp;
	return undef if ($max <= $nbr);

	for ($k = $cpp-1; $k >= 0; $k--) {
		$a = int($nbr / (92 ** $k));
		$id = substr($singleIdList, $a, 1) . $id;
		$nbr -= $a*(92 ** $k);
	}

	return $id;
}

#-------------------------------------
# add a new colors

sub addColor($$) {
	my $xpm = shift;
	my $color = shift;
	my $def = $xpm->{'def'};
	my $ids = $xpm->{'ids'};
	my $colors = $xpm->{'colors'};

	# does the color is already there?
	if (!$beFast) {
		my $i;
		for($i = 0; $i < $def->[2]; $i++) {
			return $ids->[$i] if $colors->[$i] eq "$color";
		}
	}

	# get the id for the new colors
	my $id = getColorId($def->[2], $def->[3]);

	if (defined($id)) {
		$def->[2]++;
	} else {
		$id = addACharPerPix($xpm); # and a dummy color
   }

	$ids->[$def->[2]-1] = $id;
	$colors->[$def->[2]-1] = $color;
	return $id;
}

#-------------------------------------
# add a char per Pixels and a dummy colors

sub addACharPerPix {
	my $xpm = shift;
	my $def = $xpm->{'def'};
	my $colors = $xpm->{'colors'};
	my $ids = $xpm->{'ids'};
	my $pixels = $xpm->{'pixels'};
	my $nc = $def->[2];
	my $i;

	$def->[3]++;
	@{$ids} = map { "$_ " } @{$ids};
	# add a dumy colors
	$ids->[$nc] = " " x ($def->[3]-1) . ".";
	$colors->[$nc] = $colors->[$nc-1];
	$def->[2]++;

	for ($i = 0; $i < $def->[1]; $i++) {
		@{$pixels->[$i]} = map { "$_ " } @{$pixels->[$i]};
	}
	return $ids->[$def->[2]-1];
}

#-------------------------------------
# Take 2 xpm $xpm and $mask of the same size and set the pixel
# of $xpm to $colors if the corresponding pixel of mask is black
# (i.e. #0* or grey0 or black).

sub applyTrans {
	my $xpm = shift;
	my $mask = shift;
	my $color = shift;
	my ($mcolors,$mids,$mpixels,$nc) = ($mask->{'colors'}, $mask->{'ids'},
		$mask->{'pixels'},$mask->{'def'}->[2]);
	my ($xpixels,$def) = ($xpm->{'pixels'},$xpm->{'def'});
	my ($done,$i) = (0, 0);
	my $j;
	my $t = "";

	# found the transparent id
	while($i < $nc && !$done) {
		my $tmp = $mask->{'colors'}->[$i];
		if ($mcolors->[$i] =~ /^#0*$/ ||
			$mcolors->[$i] =~ /^gray0$/i ||
			$mcolors->[$i] =~ /^black$/i) {
			$t = $mids->[$i];
			$done = 1;
		}
		$i++;
	}

	return if $t eq "";

	my $id = addColor($xpm,$color);

	for ($i=0; $i<$def->[1]; $i++) {
		for ($j=0; $j<$def->[0]; $j++) {
			$xpixels->[$i]->[$j] = $id if $mpixels->[$i]->[$j] eq "$t";
		}
	}

}

#-------------------------------------
# add border to an xpm

sub addBorder($$$$$$) {
	my ($xpm,$x1,$x2,$y1,$y2,$color) = @_;
	my $id = addColor($xpm,encode16bpp($color));
	my $columns = $xpm->{'def'}->[0];
	my $rows = $xpm->{'def'}->[1];
	my $pixels = $xpm->{'pixels'};
	my @addLeft = ();
	my @addRight = ();
	my $i;

	for ($i = 0; $i < $x1; $i++) { $addLeft[$i] = $id; }
	for ($i = 0; $i < $x2; $i++) { $addRight[$i] = $id; }
	my @new_row = ();
	for ($i=0; $i < $columns + $x1 + $x2; $i++) { $new_row[$i] = $id; }
	# shift the rows and add left and right border:
	for ($i = $rows + $y1 - 1; $i >= $y1; $i--) {
		@{$pixels->[$i]}= (@addLeft,
			@{$pixels->[$i-$y1]}[0..$columns-1],@addRight);
	}
	# add top border
	for ($i = 0; $i < $y1; $i++) { @{$pixels->[$i]} = @new_row; }
	# add bottom border
	for ($i = $rows + $y1 ; $i < $rows + $y1 + $y2; $i++) {
		@{$pixels->[$i]} = @new_row;
	}
	$xpm->{'def'}->[0] += $x1 + $x2;
	$xpm->{'def'}->[1] += $y1 + $y2;
}

#-------------------------------------
# tile an xpm

sub applyTile {
	my $xpm = shift;
	my $tile = shift;
	my $type = shift;
	my $x = shift || 0;
	my $y = shift || 0;

	my ($done,$i,$k) = qw(0 0 0);
	my $t = "";
	my %idConv = ();

	if ($type ne "center" && ($x != 0 || $y != 0)) {
		addBorder($xpm,$x,$x,$y,$y,"None");
	}

	my $columns = $xpm->{'def'}->[0];
	my $rows = $xpm->{'def'}->[1];

	if ($type eq "expand") {
		system("$magickPath/convert -geomerty $columns" .
			"x$rows" ."! $tile /tmp/ft-tmp-images.xpm");
		$tile = loadXpm("/tmp/ft-tmp-images.xpm");
		unlink("/tmp/ft-tmp-images.xpm");
		$type = "center";
	}

	# add border to $xpm
	if ($type eq "center") {
		my ($x1,$x2,$x3,$x4,$y1,$y2);
		my $tilecol = $tile->{'def'}->[0];
		my $tilerows = $tile->{'def'}->[1];
		my $x = $tilecol > $columns ? $tilecol-$columns : 0;
		my $y = $tilerows > $rows ? $tilerows-$rows : 0;

		if ($x/2 == int($x/2)) { $x1= $x2 = $x/2; }
		else { $x1 = int($x/2) + 1; $x2 = int($x/2); }
		if ($x/2 == int($y/2)) { $y1 =$y2 =$y/2; }
		else { $y1 = int($y/2) + 1; $y2 = int($y/2); }

		addBorder($xpm,$x1,$x2,$y1,$y2,"None");
		$columns = $xpm->{'def'}->[0];
  		$rows = $xpm->{'def'}->[1];
	}

	# add the $tile colors in $xpm
	my $tilenc = $tile->{'def'}->[2];
	my $tilecolors = $tile->{'colors'};
	my $tileids = $tile->{'ids'};
	for($i = 0; $i < $tilenc; $i++) {
		$idConv{"$tileids->[$i]"} = addColor($xpm,"$tilecolors->[$i]");
	}

	# found the transparent id
	my $xpmnc = $xpm->{'def'}->[2];
	my $colors = $xpm->{'colors'};
	my $ids = $xpm->{'ids'};
	$i = 0;
	while($i < $xpmnc && !$done) {
		if ($colors->[$i] =~ /^none$/i) { $t = $ids->[$i]; $done = 1; }
		$i++;
	}

	return if $t eq "";

	# add some space if need in the id hash
	my $xpmcpp = $xpm->{'def'}->[3];
	foreach $i (keys %idConv) {
		my $len = length($idConv{"$i"});
		$idConv{"$i"} .= " "x($xpmcpp-$len);
	}

	# apply the tile:
	my $pixels = $xpm->{'pixels'};
	my $tpixels = $tile->{'pixels'};
	for ($i = 0; $i < $rows; $i++) {
		my($j,$l) = (0, 0);
		for ($j = 0; $j < $columns; $j++) {
			if ($pixels->[$i]->[$j] eq "$t") {
				$pixels->[$i]->[$j] = $idConv{"$tpixels->[$k]->[$l]"};
			}
			$l = defined $tpixels->[$k]->[$l+1] ? $l+1:0;
		}
		$k = defined $tpixels->[$k+1]->[0] ? $k+1:0;
	}
}

#-------------------------------------
# colorize an xpm from jos-colorizexpm

sub colorizeXpm() {

	my $xpm = shift;
	my ($min,$max)= qw(255 0);
	my @tmp;
	my $colors = $xpm->{'colors'};

	die "colorize computation does not support polynomial interpolation\n"
		if $rizeComp =~ /^p/;

	my $nc = $xpm->{'def'}->[2];
	my @GREY = ();
	my $i;
	for ($i=0;$i<$nc;$i++) {
		next if $colors->[$i] =~ /^None$/i;
		my $t = bppTo3intArray($colors->[$i]);
		$GREY[$i] = greyval($t);
		if ($GREY[$i]<$min) {$min=$GREY[$i]};
		if ($GREY[$i]>$max) {$max=$GREY[$i]};
	}

	my $dx = 255; my $x1; my $x2;
	if ($rizeRule eq "min") {
		$x1=0;$x2=1;
	} else {
		# ??:  olicha: there is a problem if $dx < $min or $max ??
		#$dx = $max-$min;
		#$dx = 1 if $dx<=0;
		#$x1=$min/$dx;
		#$x2=$max/$dx;
		# I think this is better ? (see also the modification in interpolate)
		$x1 = $min/$dx;
		$x2 = $max/$dx;
	}

  	my $nbrColors = 0;
	my $rizeColors = [];
	@tmp = split('[,]', $rizeColorsOpt);

	if ($tmp[0] =~ /^\s*top\s*[[#A-Za-z]/) {
		$min = int($min);
		$tmp[0] =~ s/^\s*top\s*/top$min/;
	}
	if ($tmp[-1] =~ /^top[[#A-Za-z]/) {
		$max = int($max);
		$tmp[-1] =~ s/^\s*top\s*/top$max/;
	}

	my $c;
	foreach $c (@tmp) {
		$rizeColors->[$nbrColors] = bppTo3intArray(encode16bpp($c));
		$nbrColors++;
	}

	$transColor = encode16bpp("$transColor") if $transColor ne "";
	for ($i = 0; $i < $nc; $i++) {
		if ($colors->[$i] =~ /^None$/i) {
			$colors->[$i] = $transColor if $transColor ne "";
			next;
		}
		my $G=$GREY[$i]/$dx;
		# need a better fix
		$G = 1 if $G > 1;
		my $t=interpolateColors($rizeColors,$nbrColors,$G,$rizeComp,$i,$x1,$x2);
		$colors->[$i]=encode16bpp($t);
	}
}

#-------------------------------------
# rotate

sub rotateXpm($$) {
	my $xpm = shift;
	my $angle = shift || $rotate;
	my ($c,$r,$pixels)=($xpm->{'def'}->[0],$xpm->{'def'}->[1],$xpm->{'pixels'});
	my ($i,$j);
	my $np =[];

	if ($angle eq "0m" || $angle eq "-0m") {
		for ($i=0;$i<$r;$i++) { @{$pixels->[$i]}= reverse @{$pixels->[$i]} }
	}
	elsif ($angle eq "90") {
		for ($i=0;$i<$r;$i++) {
			for ($j=0;$j<$c;$j++) { $np->[$j]->[$i] = $pixels->[$i]->[$c-$j-1] }
		}
		@{$pixels} = @{$np};
		$xpm->{'def'}->[0] = $r; $xpm->{'def'}->[1] = $c;
	}
	elsif ($angle eq "90m") {
		for ($i=0;$i<$r;$i++) {
			for ($j=0;$j<=$c;$j++) { $np->[$j]->[$i]= $pixels->[$i]->[$j] }
		}
		$pixels = $np;$xpm->{'def'}->[0] = $r; $xpm->{'def'}->[1] = $c;
	}
	elsif ($angle eq "-90") {
		for ($i=0;$i<$r;$i++) {
			for ($j=0;$j<=$c;$j++) { $np->[$j]->[$i] = $pixels->[$r-$i-1]->[$j] }
		}
		@{$pixels} = @{$np};
		$xpm->{'def'}->[0] = $r; $xpm->{'def'}->[1] = $c;
	}
	elsif ($angle eq "-90m") {
		for ($i=0;$i<$r;$i++) {
			for ($j=0;$j<=$c;$j++) {
				$np->[$j]->[$i] = $pixels->[$r-$i-1]->[$c-$j-1]
			}
		}
		@{$pixels} = @{$np};
		$xpm->{'def'}->[0] = $r; $xpm->{'def'}->[1] = $c;
	}
	elsif ($angle eq "-180m") {
		for ($i=0;$i<$r;$i++) {
			for ($j=0;$j<=$c;$j++) { $np->[$i]->[$j] = $pixels->[$r-$i-1]->[$j] }
		}
		@{$pixels} = @{$np};
	}
	elsif ($angle eq "-180") {
		for ($i=0;$i<$r;$i++) {
			for ($j=0;$j<=$c;$j++) {
				$np->[$i]->[$j] = $pixels->[$r-$i-1]->[$c-$j-1]
			}
		}
		@{$pixels} = @{$np};
	}
	elsif ($angle ne "0" && $angle ne "-0") {
		die "rotate argument must be either 0[m], 90[m], -90[m], 180[m]".
			 " or -180[m]\n";
	}
}

#-----------------------------------------------------------------------------
# colors library
#-----------------------------------------------------------------------------

#-------------------------------------
# load the X rgb.txt file

sub loadRGB() {

	# return if it is already loaded
	return if defined $rgb{'gray0'};

	my $rgbFile = "";
	my $path = "";

	foreach (@xlibPath) {
		s/^-L//;
		$path = $_;
		$path .= "/X11/rgb.txt";
		$rgbFile = $path if -f $path;
	}

	die "X window rgb.txt file not found under @xlibPath" if $rgbFile eq "";

	open(RGB,"$rgbFile") || die "impossible to read $rgbFile";
	while(<RGB>) {
		if ( /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(.+)\s*$/ ) {
			my $name = lc($4);
			my $a = int2hex($1);
			my $b = int2hex($2);
			my $c = int2hex($3);
			$rgb{"$name"} = "#$a$a$b$b$c$c";
		}
	}
	close(RGB);
}

#-------------------------------------
# Encode in 16 bits by pixels: we must be as general as possible
# and this is not a problem for the one which use 8bpp
# so I suggest we use 16 bpp and a 12 digit hex number as
# default encoding for colors. Then, if a function need to
# compute on the colors it will transform to the colors format it
# want and convert back to 16bpp in hex.

sub encode16bpp($) {
	my $color = shift;

	if (ref($color) eq "ARRAY") {
		# here we must be as fast as possible
		int2hexRef($color);
		return
			"#$color->[0]$color->[0]$color->[1]$color->[1]$color->[2]$color->[2]";
	}
	$color =~ s/^\s*//;
	$color =~ s/\s*$//;
	if ($color =~ /^\[?\s*(\d+)\s*\/\s*(\d+)\s*\/\s*(\d+)\s*\]?$/) {
		my ($a,$b,$c);
		$a = int2hex($1);
		$b = int2hex($2);
		$c = int2hex($3);
		return "#$a$a$b$b$c$c";
	}
	elsif ($color =~ m!^rgb:([a-f\d]{2,4})/([a-f\d]{2,4})/([a-f\d]{2,4})$!i) {
		return "#$1$2$3" if length($1) == 4;
		return "#$1$1$2$2$3$3";
	}
	elsif ($color =~ /^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/) {
		return "#$1$1$2$2$3$3";
	}
	elsif ($color =~/^#[0-9a-fA-F]{12}$/) {
		return $color;
	}
	elsif ($color =~ /none/i) {
		return "None";
	}
	# fixed some bugs in various apps
	elsif ($color =~ /transparent/i) {
		return "None";
	}
	elsif ($color =~ /^random\s*(.*)$/i) {
		return randomColor($1);
	}
	elsif ($color =~ /^top(\d*)\s*(.+)$/i) {
		my $a;
		$a = $1;
		$a = 255 if $a eq "";
		return topColor($2,$a);
	}
	elsif ($color =~ /^hilight\s*(.+)$/i) {
		return hilightColor($1);
	}
	elsif ($color =~ /^shadow\s*(.+)$/i) {
		return shadowColor($1);
	}
	else {
		loadRGB();
		$color = lc("$color");
		return $rgb{"$color"} if (defined $rgb{"$color"});
	}

	warn "Unknown color $color, set it to black.\n";
	return "#000000000000";
}

#-------------------------------------
# 16bpp to a ref array (0-255,0-255,0-255)

sub bppTo3intArray($) {
	# can we be more fast?
	my $color = shift;
	my @a = $color =~ /#(..)..(..)..(..)/;
	@a=(hex($a[0]),hex($a[1]),hex($a[2]));
	return \@a;
}

#-------------------------------------
# the random color

sub randomColor {
	my $color = shift;
	my $a;

	$a->[0] = int(rand 256);
	$a->[1] = int(rand 256);
	$a->[2] = int(rand 256);

	return encode16bpp($a) if ($color eq "");

	# not terrible ... a better idea?
	$color = encode16bpp($color);
	my $b = bppTo3intArray($color);
	my $i;
	for ($i=0;$i<3;$i++) {
		$$b[$i] = int(($$a[$i] + 2*$$b[$i]) / 3);
	}
	return  encode16bpp($b);
}

#-------------------------------------
# top colors from jos-colorizexpm

sub topColor {
	my $color = shift;
	my $max = shift;

	$color = encode16bpp($color);
	my $a = bppTo3intArray($color);
	my $CenterG=greyval($a);
	$CenterG = 1 if $CenterG == 0;
	my $HI = $max/$CenterG;
	my $i;
	for ($i = 0; $i < 3; $i++) {
		$$a[$i] = int($$a[$i] * $HI);
		$$a[$i] = $$a[$i] > 255 ? 255 : $$a[$i];
	}
	return  encode16bpp($a);
}

#-------------------------------------
# from jos-colorset-cdegradient
# an hilight white is white ?
# migo: Yes for GTK style hilighting, no for motif.
# migo: fvwm algorithms are in libs/ColorUtils.c (2.2.4 is better).

sub hilightColor {
	my $color = shift;

	$color = encode16bpp($color);
	my $a = bppTo3intArray($color);
	my $i;
	for ($i = 0; $i < 3; $i++) {
		$a->[$i] = max(255/5, $a->[$i]);
  		$a->[$i] = min(255, ($a->[$i] * 130) / 100);  # was 110
  		$a->[$i] = int($a->[$i]);
	}
	return  encode16bpp($a);
}

#-------------------------------------
# from jos-colorset-cdegradient
# is this 0.9 official? What about 0.8
# Moreover a shadow black is black ?
# migo: Yes for GTK style hilighting, no for motif.

sub shadowColor {
	my $color = shift;

	$color = encode16bpp($color);
	my $a = bppTo3intArray($color);
	my $i;
	for ($i = 0; $i < 3; $i++) {
		$a->[$i] *= 0.7;  # was 0.9
		$a->[$i] = int($a->[$i]);
	}
	return  encode16bpp($a);
}


#--------------------------------------
# grey value of a ref (\d+,\d+,\d+) colors from jos-colorizexpm

sub greyval {
  	my $c = shift;
	return sqrt($$c[0]**2 + $$c[1]**2 + $$c[2]**2)/sqrt(3);
}

#-------------------------------------
# interpolate colors

sub interpolateColors {
	my $colors = shift;
	my $nc = shift;       # nbr of colors
	my $x = shift;
	my $type = shift;
	my $step = shift || 0;
	my $x1 = shift || 0; # $x1 and $x2 are used by colorizeXpm
	my $x2 = shift || 1;
	my @a = qw(0 0 0);
	my $c = $nc-1;
	my $i; my $j;

	die "You need to specify at least two colors in the color list\n"
		if $c <= 0;

	if ($type =~ /^t/) {
		for ($i = 0; $i < 3 ; $i++) { $a[$i] = int(rand 256) }
		return \@a;
	}

	if ($type =~ /^c/) {
		$i= $step%$nc;
		@a = @{$colors->[$i]};
		return \@a;
	}

	if ($type =~ /^p/) {
		for ($i =0 ; $i < 3; $i++) {
			for ($j = 0; $j < $nc; $j++) {
				my $inter = 1; my $k;
				for ($k = 0; $k < $nc; $k++) {
					next if $k == $j;
					$inter *= ($x - $k/$c) * ($c/($j-$k));
				}
				$a[$i] += $colors->[$j]->[$i] * $inter;
				$a[$i] = int ($a[$i]);
				$a[$i] = 255 if ($a[$i] > 255);
				$a[$i] = 0 if ($a[$i] < 0);
			}
		}
		return \@a;
	}

	# more reasonable interpolation:
	# try to be as fast as possible !
	$x *= $c;
	$j=0;
	while ($x > ($j+1)) { $j++ }
	my $t1 = $j == 0 ? $x1 * $c : $j;
	my $t2 = $j == $c-1 ? $x2 * $c : $j+1;
	my $d = 1 / ($t2-$t1);
	my $l = $j + 1;
	# faster than a loop?
	# in any case doing the int independently is faster. why?
	$a[0] = ($colors->[$j]->[0]*($t2-$x) + $colors->[$l]->[0]*($x-$t1)) * $d;
	$a[1] = ($colors->[$j]->[1]*($t2-$x) + $colors->[$l]->[1]*($x-$t1)) * $d;
	$a[2] = ($colors->[$j]->[2]*($t2-$x) + $colors->[$l]->[2]*($x-$t1)) * $d;
	if ($type =~ /^r/) {
		for ($i=0;$i<3;$i++) {
			$a[$i] = $a[$i] + (-(rand)+rand)*25;
			$a[$i] = 255 if ($a[$i] > 255);
			$a[$i] = 0 if ($a[$i] < 0);
		}
	}
	($a[0],$a[1],$a[2]) = (int($a[0]),int($a[1]),int($a[2]));
	return \@a;
}

#-------------------------------------
# integer to hexadecimal (from jos-colorfun)

# $h = int2hex(255); -> "ff"
sub int2hex {
	my $i= shift;
 	# modified for speed (olicha)
	my $h = $Hex[$i/16] . $Hex[$i%16];
  	return $h;
}

sub int2hexRef {
	my $a= shift;
	my $i;
	# must be as fast as possible
	$a->[0] = $Hex[$a->[0]/16] . $Hex[$a->[0]%16];
	$a->[1] = $Hex[$a->[1]/16] . $Hex[$a->[1]%16];
	$a->[2] = $Hex[$a->[2]/16] . $Hex[$a->[2]%16];
}

#-----------------------------------
# min and max

sub min {
	my ($a, $b) = @_;
	return ($a < $b) ? $a : $b;
}

sub max {
	my ($a, $b) = @_;
	return ($a > $b) ? $a : $b;
}


#-----------------------------------------------------------------------------
# my !xpm image lib :o)
#-----------------------------------------------------------------------------

sub hasMatte {
	my $file = shift;
	my $done= 0;
	my $r = 0;

	open(IDENT,"$magickPath/identify -verbose $file|");
	while (<IDENT>) {
		if (/matte/i && /true/i) { $r = 1; }
	}
	close(IDENT);
	return $r;
}

#-----------------------------------------------------------------------------
# The starndard functions


sub showHelp {
	print "The fvwm-themes images utility.\n";
	print "Usage: $scriptName [OPTIONS]\n";
	print "General Options:\n";
	print "\t--help              show this help and exit\n";
	print "\t--version           show the version and exit\n";
	print "\t--in-dir dir        The input directory\n";
	print "\t--out-dir dir       The main directory for output the images\n";
	print "\t--gnome             short cut for converting GNOME icons\n";
	print "\t--kde2              short cut for converting KDE2 icons\n";
	print "\t--kde2-hi           convert hight colors KDE2 icons\n";
	print "\t--site              set the out dir to \$FT_DATADIR/tr-image\n";
	print "\t--create-symlink n  Create symlink for use with fvwm-themes\n";
	print "\t--update            only create images which are needed\n";
	print "\t--ft-install        short cut used by fvwm-themes installation\n";
	print "\t--verbose int       level of verbosity\n";
	print "Convert\n";
	print "\t--convert           convert images to XPM images\n";
	print "\t--[no]trans-filter  Apply a transparent filter\n";
	print "\t--threshold value   percentage of transparisation\n";
	print "\t--postfix str       add -str after 16x16, 48x48... to out dirs\n";
	print "\t--[no]build-48x48   build or not 48x48 XPM images\n";
	print "\t--[no]build-16x16   build or not 16x16 XPM images\n";
	print "\t--[no]build-56x56   build or not 56x56 XPM images\n";
	print "\t--[no]build-32x32   build or not 32x32 XPM images\n";
	print "\t--[no]build-tiles   build or not tiles XPM images\n";
	print "\t--preserve          build XPM images without size modification\n";
	print "\t--size-48x48 geo    Set the size of the 48x48 XPM icons\n";
	print "\t--size-16x16 geo    Set the size of the 16x16 XPM icons\n";
	print "\t--size-56x56 geo    Set the size of the 56x56 XPM icons\n";
	print "\t--size-32x32 geo    Set the size of the 32x32 XPM icons\n";
	print "\t--size-tiles geo    Set the size of the tiles XPM icons\n";
	print "\t--tile-48x48 value  tile to the 48x48 XPM icons\n";
	print "\t--tile-16x16 value  tile to the 16x16 XPM icons\n";
	print "\t--tile-56x56 value  tile to the 32x32 XPM icons\n";
	print "\t--tile-32x32 value  tile to the 56x56 XPM icons\n";
	print "\t--tile-tiles value  tile to the tiles XPM icons\n";
	print "\t--border-48x48      add border to the 48x48 XPM icons\n";
	print "\t--border-16x16      add border to the 16x16 XPM icons\n";
	print "\t--border-56x56      add border to the 32x32 XPM icons\n";
	print "\t--border-32x32      add border to the 56x56 XPM icons\n";
	print "\t--border-tiles      add border to the tiles XPM icons\n";
	print "ImageMagick\n";
	print "\t--magick-bpp value  number of bit per pixel used by ImageMagick\n";
	print "\t--magick-path path  Set the path of ImageMagick convert\n";
	print "\t--magick-colors n   Number of colors used by ImageMagick\n";
	print "\t--magick-colorspace see the ImageMagick convert man page\n";
	print "Simple transformations\n";
	print "\t--rotate value      rotate and/or mirror an XPM\n";
	print "\t--tile value        tile all images\n";
	print "\t--border +x+y,col   add border to all images\n";
	print "Colorize\n";
	print "\t--colorize          colorize an/all image(s)\n";
	print "\t--colorize-colors l comma separated list of colors for colorize\n";
	print "\t--trans-color col   set the transparent pixels to col pixels\n";
	print "\t--colorize-comp t   interpolation computation type for colorize\n";
	print "\t--colorize-rule v   colorization procedure\n";
	print "CDE like sky\n";
	print "\t--sky               produce cde-like gradient and a lot more\n";
	print "\t--sky-colors l      comma separated list of colors for sky\n";
	print "\t--pattern-file f    use pattern in file f for sky\n";
	print "\t--pattern-type t    create some patterns\n";
	print "\t--pattern-xpm f     create a pattern from an XPM\n";
	print "\t--pattern-gap n     jump into the pattern\n";
	print "\t--sky-comp t        interpolation computation type for sky\n";
	print "\t--be-fast           try to be faster by using more memory\n";
	print "Set Root Image\n";
	print "\t--setroot           set the root window with the built XPM\n";
	print "\t--setroot-prog prog command to set the root ($setRootProg)\n";
	print "Color schemes\n";
	print "\t--colorschemes      process color schemes\n";
	print "\t--colorschemes-fores evaluate fore from back for colors schemes\n";
   print "\t--colorschemes-ext  file extention for color scheme files\n";
	print "Color Info\n";
	print "\t--show-xcolors      show a list of all xcolors\n";
	print "\t--show-xcolorsets   show a list of all xcolor sets\n";
	print "\t--show-color-info c show a full info for the specified color\n";
	print "Good luck!\n";
	exit 0;
}

sub showVersion {
	print "$version\n";
	exit 0;
}

sub wrongUsage {
	print STDERR "Try '$scriptName --help' for more information.\n";
	exit -1;
}

sub myPrint {
	my $deg = shift;
	my $string = shift;

	if ($verbose >= $deg) {
		print "$string";
	}
}

sub myDebug {

	my $flag = shift;
	my $string = shift;
	if ($debug =~ /$flag/ || $debug eq "main") {
		print "(".times.")\t[$flag] $string";
	}
	# note: for time eval it is better to use perl -d:DProf with dprofpp

}
__END__

# ---------------------------------------------------------------------------

=head1 NAME

fvwm-themes-images - fvwm-themes images and colors utility

=head1 SYNOPSIS

B<fvwm-themes-images>
B<--convert> or/and B<--rotate> value or/and B<--colorize> or/and
B<--tile> or/and B<--border> or B<--sky> or B<--colorschemes>
or B<--show-xcolors> or B<--show-xcolors-set> or B<--show-color-info> color 
or B<--help> or B<--version>
[ B<--in-dir> dir ]
[ B<--out-dir> dir ]
[ B<--in-file> file ]
[ B<--out-file> file ]
[ B<--gnome> ]
[ B<--kde2> ]
[ B<--kde2-hi> ]
[ B<--site> ]
[ B<--create-symlink> name ]
[ B<--update> ]
[ B<--ft-install> ]
[ B<--verbose> int ]
[ B<--[no]trans-filter> ]
[ B<--threshold> int ]
[ B<--postfix> string ]
[ B<--[no]build-48x48> ]
[ B<--[no]build-16x16> ]
[ B<--[no]build-56x56> ]
[ B<--[no]build-32x32> ]
[ B<--[no]build-tiles> ]
[ B<--preserve> ]
[ B<--size-48x48> geo ]
[ B<--size-16x16> geo ]
[ B<--size-56x56> geo ]
[ B<--size-32x32> geo ]
[ B<--size-tiles> geo ]
[ B<--border> +x+y,color ]
[ B<--border-48x48> +x+y,color ]
[ B<--border-16x16> +x+y,color ]
[ B<--border-56x56> +x+y,color ]
[ B<--border-32x32> +x+y,color ]
[ B<--border-tiles> +x+y,color ]
[ B<--tile rule:file> ]
[ B<--tile-48x48> rule:file ]
[ B<--tile-16x16> rule:file ]
[ B<--tile-56x56> rule:file ]
[ B<--tile-32x32> rule:file ]
[ B<--tile-tiles> rule:file ]
[ B<--rotate> value ]
[ B<--magick-bpp> 8/16 ]
[ B<--magick-path> path ]
[ B<--magick-colors> int ]
[ B<--magick-colorspace> value ]
[ B<--colorize-colors> color1,color2... ]
[ B<--trans-color> color ]
[ B<--colorize-comp> value ]
[ B<--colorize-rule> value ]
[ B<--sky-colors> color1,color2... ]
[ B<--pattern-file> file ]
[ B<--pattern-type> value ]
[ B<--pattern-xpm> file ]
[ B<--pattern-gap> value ]
[ B<--pattern-y> value ]
[ B<--sky-comp> value ]
[ B<--be-fast> ]
[ B<--setroot> ]
[ B<--setroot-prog> exec ]
[ B<--text-colors> value ]
[ B<--colorschemes-fores> num,threshold,color1,color2 ]
[ B<--colorschemes-ext> ext ]

=head1 DESCRIPTION

The aim of this script is to build images to be used by fvwm-themes.

=head2 Convert Images to XPM Images

fvwm-themes-images can convert images in various format
(especially PNG GNOME or KDE version 2 icons) into XPM images of various 
sizes. This
use ImageMagick plus some internal XPM manipulations (to get
better results). For example, if you run:

    fvwm-themes-images --convert --gnome

then, if GNOME is not installed in an exotic way all icons
in the GNOME images directory will be converted into 48x48 and 16x16
XPM icons under ~/tr-images (if GNOME is not found the
images in /usr/share/pixmaps will be converted). To control the
result of the conversion you can use the --threshold
and --magick-colors option below. If the result is very very bad you must use
the --magick-bpp option.

You can also convert an individual images by using the --in-file and
--out-file option.

=head2 Other transformations

fvwm-themes-images can perform other transformations (which do not require
ImageMagick). You can colorize, rotate, add border and tile an XPM
images. These operations can be done together (and with "convert")
for all images in a directory or for an individual image.
For example, if you want blue/red mirrored gnome icons tiled with
a-48x48-tile.xpm for 48x48 icons and tiled with a-16x16-tile.xpm for
16x16 icons and with a yellow border of 5 pixels for 48x48 icons and of
2 pixels for 16x16 icons just run fvwm-themes-images with the following
options :o)

    --gnome --convert --rotate 0m \
    --colorize --colorize-colors blue,red \
    --tile-48x48 path_to/a-48x48-tile.xpm \
    --tile-16x16 path_to/a-16x16-tile.xpm \
    --border-48x48 +5+5,yellow \
    --border-16x16 +2+2,yellow

=head2 Colorize

fvwm-themes-images can colorize an XPM image (or a family of images
in a directory). The main aim of "colorize" is to produce an infinite
number of backgrounds with only one xpm. For example:

    fvwm-themes-images --colorize --colorize-colors black,red \
      --trans-color yellow --in-file My.xpm --setroot

will tile your root window with an XPM build from My.xpm, such that
the darkest pixels in My.xpm are replaced by black pixels, the lightest
pixels are replaced by red pixels, the pixels in between are
interpolated between black and red and the transparent pixels are
replaced by yellow pixels. Moreover, you can use more than 2 colors
and some options allows you to control the interpolation.
You can also rotate, tile and borderize your XPM.
By default, fvwm-themes-images uses "fvwm-root" to
set background, you can specify an other program using the --setroot-prog
option, e.g., if you want to use xv just add the option

    --setroot-prog "xv -root -quit"

Note that if you do not specify an out-file, the built XPM
is saved in /tmp/$USER-ft-back.xpm and then removed in this case.

=head2 CDE-LIKE SKY

fvwm-themes-images can set the root window to a gradient pattern like
CDE does. CDE has an option to display a gradient on the background,
which consists of a repeated pattern, but along the color
gradient, the color of the pattern varies. Try:

    fvwm-themes-images --sky --sky-colors turquoise,darkblue --setroot

in this case the default pattern file is used
($FT_DATADIR/themes/cde/background/pattern). A pattern file must contain a
rectangular pattern consisting of +es and -es like this:

    +++++
    +-+-+
    -+-+-
    -----

and nothing else. This pattern is painted repeatedly over the
screen. However, the colors change in the y direction accordingly
with the --sky-colors and --sky-comp options. fvwm-themes-images
can generate some pattern. If you want a regular gradient add the
option: --pattern-type uniform:1, try also the --pattern-type
option with square:50 or altern:20 and random:50 for examples.
If you want an horizontal gradient just rotate: --rotate 90.
You can also control the gradient computation. Try: --pattern-type
square:64 --sky-comp c for a chess like background, --pattern-type
altern:64,64 --sky-comp c for a band background, --pattern-type 1
--sky-comp r for an irregular gradient, and --pattern-type 1
--sky-comp t for an horrible background.
Note that if your sky contains a lot of coulours you can speed a
lot the construction of you sky by using the --be-fast option
(but this will build a bigger XPM).

=head2 PROCESSING COLOR SCHEMES

A color scheme file is a file constituted of one color by line.
These colors are traditionally background colors.
At the present time fvwm-themes-images can extend such color scheme
file by adding corresponding foreground colors. For example, if there
are several color scheme files with extension .pal in the directory DIR
and you run:

    fvwm-themes-images --colorschemes --colorschemes-ext pal \
    --colorschemes-fores 8,128,color1,color2 --in-dir DIR --out-dir DIR

then the 8 first colors of the color scheme files will be unchanged
and 8 new colors will be added (if your original color scheme files have
more than 8 colors these colors are ignored/removed). So, you get color
scheme files with 16 colors. The 7-th color is the foreground color,
corresponding to the 1-st background color and so on. The foreground color
is evaluated to be either color1 or color2. It is color1 for the backgrounds
with gray value < 128 and is color2 otherwise. A gray value of 255
corresponds to white color and a gray value of 0 corresponds to black.
So, the idea is to use a light color for color1 (like white) and a dark
color for color2 (like black).

=head2 COLORS

When giving colors to fvwm-themes-image, you can use X colors name
(blue, turquoise, ...etc.), the standard RGB formats rgb:rr/gg/bb
or rgb:rrrr/gggg/bbbb, 8bits hexadecimal number
(#0000ff, #40e0d0, ...etc.), 16bits  hexadecimal number (#00000000ffff,
#0404e0e0d0d0, ...etc) or an array of 3 decimal numbers between 0 and 255
([0/0/255], [64/224/208], ...etc.). All these numbers represent the
red, green and blue values of the colors. To get the list of your X colors
which have a name with the corresponding value in 8bits and in an array
of 3 integer just type:

    fvwm-themes-images --show-xcolors | less

if you use hexadecimal numbers like B<#abcdef> in some shells you must put
these colors in quotes. Note, if you give colors by hexadecimal numbers
fvwm-themes-images is faster, since rgb.txt should not be loaded.

fvwm-themes-images have some generic ways to create colors. You can
give to fvwm-themes-images a color of the form:

    typecolor

where type is either top[integer], hilight, shadow, random and
where color is a color encoded as explained above. If you give
such a color fvwm-themes-images compute for you the "type" color
of the "color". To see the result of these computations for
all the colors which have name try:

    fvwm-themes-images --show-xcolorsets | less

for any individual color type:

    fvwm-themes-images --show-color-info color

You can enter random alone, this try to produce a random color. The only
type which does not have a clear meaning is "top". top may have an integer
between 0 and 255 after it (default is 255). top255 will purify your
color as top0 will destroy it completely to black. Typically, top255blue
will give blue, top255darkblue will give blue, top50blue will give a
very dark blue. The top type (without integer argument) is used in a
special way with the --colorize-colors option: if you want to colorize
an XPM with "color" as a "center color" try the following:

    fvwm-themes-images --colorize --colorize-colors topcolor,topcolor \
    --in-file My.xpm --setroot

then, fvwm-themes-images will try to compute good integer values
for I<top> to produce two good colors.

=head1 OPTIONS

=head2 General Options

B<--help>    - show the help and exit

B<--version> - show the version and exit

B<--in-dir> dir - The input directory. Default is the working directory
of the shell that will run fvwm-themes-images. You can give either a complete
path or a relative path (relative to the working directory).
With the --gnome option fvwm-themes-images will try to find the GNOME icons
directory, if GNOME is not found the default is /usr/share/pixmaps.

B<--out-dir> dir - The main directory for output the images. Default
is $HOME/tr-images and $FT_DATADIR/tr-images where $FT_DATADIR
is the fvwm-themes installation directory with the --site option.
You can give either a complete path or a relative path (relative
to the working directory).

B<--in-file> file - input file relatively to the --in-dir option
except if you give a complete path.

B<--out-file> file - output file if you use the --in-file option
and not --convert. You can give either a complete
path or a relative path (relative to the --out-dir option, i.e.,
$HOME/tr-images or $FT_DATADIR/tr-images). Yes, this is strange but
this is good. With --convert - this option is ignored, with --setroot -
you do not need to define an outfile, a temporary file is used,
but you can, if you also want to save it.

B<--site> equivalent to --out-dir $FT_DATADIR/tr-images.

B<--update> if the file to be created already exist skip it. This is
useful for example after you have installed some new GNOME applications
and you do not want to rebuild all your xpm icons, with --update only
the new icons are builded.

B<--gnome> imply --convert, --postfix gnome, and try to find the GNOME icons
directory to define the --in-dir, if GNOME is not found --in-dir is set
to /usr/share/pixmaps

B<--kde2> imply --convert, --postfix kde2, and try to find the KDE2 icons
directory to define the internal generalization of --in-dir, if these
directories are not found or if the KDEDIR environement variable is not set
nothing is done. Low colors icons under apps/ filesystems/ actions/ devices/ 
mimetypes/ and hight colors icons under the same directories are converted.
If two icons to be converted have the same name, then the first found is
converted (relatively to the above directories list). You can reverse the
low/hight colors priority by using the next option. Note that there is a
bug here: the only way to really rebuild the KDE2 icons is to remove the
output directory; if not only the low (hight, with --kde2-hi) colors icons
under apps/ will be rebuild.

B<--kde2-hi> if --kde2 is used convert the KDE2 hight color icons before the
low color icons.

B<--ft-install> equivalent to --site --update --create-symlink 48x48,16x16.
This option must be use with --gnome or --kde2 (but not both).

B<--create-symlink> A,B - where A and B are either 48x48, 16x16, 32x32.
Create symbolic link from $FT_DATADIR/images/norm-postfix to OUT/A-postfix
and from $FT_DATADIR/images/mini-postfix to OUT/B-postfix. Where postfix
is defined with the --postfix option and where OUT is the directory
defined by --out-dir.

B<--verbose> int - where int can be 0, 1 or 2. This integer represents
the level of "verbosity". Default is 1, but with --setroot the default is 0.

=head2 Convert

B<--convert> - Will cause fvwm-themes-images to convert all images
in the directory specified by the --in-dir options into XPM icons
of various sizes. By default, XPM icons of sizes 48x48 and 16x16
are build in dir/48x48 and dir/16x16 where dir is he directory specified
by the --out-dir option

B<--[no]trans-filter> - Apply or not the internal "transparent filter".
Default is --trans-filter and this filter can be controlled with the
--threshold option below.
When ImageMagick convert a PNG image into an XPM one the result is not
perfect (or I have not found the good procedure). The problems is that
a PNG image has a "matte" channel which represent the degree of
transparency/opacity of the images. On the other hands, an XPM image has
a binary matte channel (a pixel is either opaque or transparent).
The "transparent filter" extract the matte channel (using ImageMagick),
then "threshold" it (using again ImageMagick) to get  the "good"
transparency zone which is applied to the XPM image using an internal
procedure (Maybe ImageMagick combine can do that but I never found
the good procedure). If you have bad result try to use
the --magick-bpp options.

B<--threshold> value - value must be an integer between 0 and 100
and represent a percentage. This value is used by the "transparent filter"
to compute the transparency zone of the builded XPM icons. More the value
is big more the resulting images are transparent. For GNOME icons you may
try value between 30 to 99 (a value of 100 will give a family of
empty icons as a value of 0 will probably give to you icons with a
black background). Default is 70.

B<--postfix> str - add "-str" to the name of the output directories of the
converted images: OUTDIR/48x48-str, OUTDIR/16x16-str ...etc.

B<--[no]build-48x48> - build or not the 48x48 XPM icons. Default is
--build-48x48.

B<--[no]build-16x16> - build or not the 16x16 XPM icons. Default is
--build-16x16.

B<--[no]build-56x56> - build or not the 56x56 XPM icons (under 56x56/).
Default is --nobuild-56x56.

B<--[no]build-32x32> - build or not the 32x32 XPM icons (under 32x32/).
Default is --nobuild-32x32.

B<--[no]build-tiles> - build or not the tiles XPM icons from the
tiles sub directory of the --in-dir option (under tiles/). Default is
--nobuild-tiles.

B<--preserve> - Set to no all the 5 previous options and convert without
size modifications under the out-dir.

B<--size-48x48> geometry - Set the size of the 48x48 XPM icons via an
ImageMagick size geometry. See the --size option of "man convert".
Default is 48x48.

B<--size-16x16> geometry - As above for the 16x16 XPM icons.
Default is 17x17 :)

B<--size-56x56> geometry - As above for the 56x56 XPM icons. Default is 56x56.

B<--size-32x32> geometry - As above for the 32x32 XPM icons. Default is 32x32.

B<--size-tiles> geometry - As above for the tiles XPM icons. Default is "".

B<--border-48x48> +x+y,color - add border for the 48x48 XPM icons. See
the --border option for details.

B<--border-16x16> +x+y,color - As above for the 16x16 XPM icons.

B<--border-56x56> +x+y,color - As above for the 16x16 XPM icons.

B<--border-32x32> +x+y,color - As above for the 32x32 XPM icons.

B<--border-tiles> +x+y,color - As above for the tiles XPM icons.

B<--tile-48x48> [rule:]file_or_color - tile the 48x48 XPM icons with the xpm file
following the rule "rule". See the ---tile option for details.

B<--tile-16x16> [rule:]file_or_color - As above for the 16x16 XPM icons.

B<--tile-56x56> [rule:]file_or_color - As above for the 56x56 XPM icons.

B<--tile-32x32> [rule:]file_or_color - As above for the 32x32 XPM icons.

B<--tile-tiles> [rule:]file_or_color - As above for the tiles XPM icons.

=head2 ImageMagick Options

B<--magick-colors> value - Number of colors used. Default is 256. If
you use X under 16bpp (xdpyinfo) you can use for example 65536.

B<--magick-colorspace> value - Where value must be GRAY, OHTA, RGB,
Transparent, XYZ, YCbCr, YIQ, YPbPr, YUV, or CMYK. See the -colorspace
option of ImageMagick (man convert). Default is Transparent.

B<--magick-path> path - set the path of the convert ImageMagick executable.
Useful if convert ImageMagick executable is not in your PATH or to test
different versions.

B<--magick-bpp> value - value must be 8 or 16. Set the number of bit per
pixels used by ImageMagick. This is a compile time option of ImageMagick
and fvwm-themes-images will detect it if you use a recent version of
ImageMagick. If fvwm-themes-images does not found this value it is set
to 16. This is very important for the --threshold option above.

=head2 Other Simple Transformations

B<--tile> [rule:][+x+y,]file_or_color - where rule can be "expand",
"center", "color" or nothing, where x and y are integers
and where file_or_color is a
xpm tile file or a color in the "color" case. Without "rule" the
xpm file will be tiled as usual on the background of the image.
With "expand" the tile file will be resized to fit the image.
With "center" the image is centered  on the tile file (your tile
file have to be bigger that your image). With "color" the background
of the image will be colorized by the color. The +x+y option can
be used with all the rules but the "center" rule. This option expand the
zone to tile by x pixels in the left and
the right of the image and y  pixels in the top and
the bottom of the image.

B<--border> +x+y,color - add x color pixels in the left and
the right of the image and y color pixels in the top and
the bottom of the image.

B<--rotate> value - value can be 0m, 90, 90m, 180, 180m. The integer
is the degree of the rotation the "m" says to also mirror the image.

=head2 Colorize

B<--colorize-colors> - Colorize an XPM image such that
the darkest pixels are replaced by pixels with the first color in the
color list below and the lightest pixels are replaced by pixels with
the last color in the list below. The pixels in between are
interpolated between these two colors and the colors in between.
Moreover the transparent pixels can be replaced by opaque pixels
with the --trans-color option. The type of the interpolation can
be changed with the --colorize-comp. If you found that your resulting
XPM is too contrasted you can try the "--colorize-rule min" option
(or change the color list).

B<--colorize-colors> color1,color2... - list of colors for colorization.
The top type (without integer argument) is used in a
special way here. If you use top with the first color and/or the last
color fvwm-themes-images will try to find the good integer values
to top to do so that these colors are "centred" in the colorization.

B<--trans-color> color - set the transparent pixels to color pixels.

B<--colorize-comp> type - type of the computation for the interpolation.
Type can be "l" for linear (this is the default), "r" for perturbed (as
linear but the colors obtained is randomly perturbed), "c" for
circular (the colors in the list are used alternatively), "t" for totally
random (then the list of colors are ignored and the pixels are colorized
randomly ... this is funny).

B<--colorize-rule> value - value can be max or min, default is max. max
does nothing. min changes the way of the colorization: black is "mapped"
to the first color in the list and white  is "mapped" to the last color
in the list.

=head2 CDE-LIKE SKY

B<--sky> -  Create a "gradient" XPM which consists of a repeated
pattern, but along the color gradient, the color of the pattern varies.
A pattern file must contain a rectangular pattern consisting of +es and
-es. This pattern is painted repeatedly over the screen.
However, the colors change in the y direction accordingly
with the --sky-colors and --sky-comp options. By default, the pattern
used is $FT_DATADIR/themes/cde/background/pattern. You can use one
of the --pattern-* option below to load or create other patterns.
Finally, you can rotate your image to get horizontal gradient.

B<--sky-colors> color1,color2... - List of the colors for creating
the sky gradient.

B<--pattern-file> file - use the pattern file "file" as pattern.

B<--pattern-type> type[[:y]:x] - where type can be I<uniform>, I<altern>,
I<square>, I<random> and where y and x are positive integers. If no type
is specified uniform is used, default y is 1 and default x is 8,
for square the default for x is y. I<uniform> creates a pattern with y rows
of x '+'. I<altern> creates a pattern with y rows of x '+' followed
by y rows of x '-' (a comma-separated value for y may be used; altern:10
and altern:10,10,0,0:8 are the same). I<square> creates a pattern of y rows
constituted of x '+' followed by x '-'. I<random> creates a pattern with
y random rows of length x.

B<--pattern-xpm> file - Produce the pattern file from an xpm file.
Set the transparent color to '-' and all other colors to '+'.

B<--pattern-gap> n - where n is a positive integer. Take into account
only every n-th lines of the pattern. Default is 1, so all pattern lines
are taken.

B<--pattern-y> y - where y is a positive integer. By default, the
XPM file produced by --sky has the same height of your screen
to produce complete gradient. You can set this height to a smaller
value with this option to get repeated gradient or to save memory
and time with some patterns and some --sky-comp options.

B<--sky-comp> type - type of the computation for computing the gradient.
The type are the same as for --colorize-comp but you can use also "p"
for a polynomial interpolation.

B<--be-fast> - If your sky contains a lot of colors you can use this
option so that the background appear more rapidly on your root window.
On the other hands, the XPM file builded will be larger.

=head2 Setting the Root Image

B<--setroot> - Set the root image (background) with the XPM file produced.
Of course, this works only for a "transformation" which produces
only one file, i.e., with --sky or with --in-file without --convert.

B<--setroot-prog> I<prog> - a program with arguments to be executed
as "I<prog> out.xpm" that sets the root window.
Default is "fvwm-root".

=head2 Evaluating foregrounds from backgrounds in color schemes

B<--colorschemes> - Process color scheme files in the in-directory in
place or into out-directory. A color scheme file is a file constituted
of one color by line.

B<--colorschemes-fores> num,threshold,color1,color2 - where num and threshold
are integers. Transforms an original color scheme file using the following
rules:

  1) the resulting file has 2*num lines of colors
  2) the first num colors (background colors) remain as are
  3) the last num lines are evaluated to be the foreground colors for the
first num background colors respectively.
  4) the foreground color is color1 if the background color has a gray value
which is less than threshold and is color2 otherwise.

B<--colorschemes-ext> ext - consider only files with the trailing ".ext" in
their names as color scheme files. Default is "dp".

=head2 Colors Informations

B<--show-xcolors> - show a list of all colors with an X name.

B<--show-xcolorsets> - show a list of all "xcolors sets"

B<--show-color-info color> - show full informations for the specified color

=head1 AUTHORS

Olivier Chapuis <olivier.chapuis@free.fr> (general design, convert, simple
transformations, internal xpm library, interpolation, amelioration in
"colorize" and cde-sky). 22 July 2000.

Jos van Riswick  <josvanr@xs4all.nl> (Colorize, cde-sky, numerous
functions in the colors "library").

The starting point of this script is the fvwm2gnome script written
by Clarence Smith, Jr
<csmith@staticbomb.com> and Jer Warren <jer@digitalaccess.net>,
where ImageMagick is used to convert GNOME icons into 20x20 XPM icons.

=head1 COPYING

The script is distributed by the same terms as fvwm-themes itself.
See GNU General Public License for details.

=head1 BUGS

The english of this man page have to be fixed.

Report bugs to fvwm-themes-devel@lists.sourceforge.net.

=cut

# ==========================================================================

