## ## The cuttlefish Visualization Tool. ## Copyright (C) 2006 The Regents of the University of California. ## ## This program is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License version 2 as published ## by the Free Software Foundation. ## ## 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 ## ## written by Bradley Huffaker ## ## documention by Joshua Polterock ## Bradley Huffaker ## Marina Fomenkova ## ## # Basic Canvas image holder; # Provides a simple wrapper for GD's image and access to some commonly # used functions. # use GD; package Canvas; require Exporter; @ISA = qw( Exporter ); @EXPORT = qw(); @EXPORT_OK = qw(); use strict; my $NUM_BINS; sub new { my ($this, $width, $height) = @_; if (!defined $NUM_BINS) { $NUM_BINS = 7; } my $image; if (defined $width && defined $height) { $image = GD::Image->new($width,$height,1); } elsif (defined $width) { my $filename = $width; open (IN,"<$filename") || die("Unable to open \"$filename\":$!"); $image = GD::Image->new(*IN); close IN; } my $canvas = bless { "min" => undef, "max" => undef, "bins" => undef, "IMAGE" => $image, "COLORS" => {} }, $this; return $canvas; } sub duplicate { my ($this,$targetWidth,$targetHeight) = @_; return $this->copy() if (!defined $targetWidth && !defined $targetHeight); my ($width,$height) = $this->{IMAGE}->getBounds(); if (defined $targetWidth && !defined $targetHeight) { $targetHeight = ($targetWidth/$width)*$height; } elsif (!defined $targetWidth && defined $targetHeight) { $targetWidth = ($targetHeight/$height)*$width; } my $canvas = new Canvas($targetWidth,$targetHeight); $canvas->copyResampled($this); return $canvas; } sub copy { my ($this) = @_; my ($width,$height) = $this->{IMAGE}->getBounds(); my $canvas = new Canvas($width,$height); $canvas->{IMAGE}->copy($this->{IMAGE},0,0,0,0, $width, $height); return $canvas; } sub copyResampled { my ($this, $source) = @_; my ($dstW,$dstH) = $this->{IMAGE}->getBounds(); my ($srcW,$srcH) = $source->{IMAGE}->getBounds(); $this->{IMAGE}->copyResampled($source->{IMAGE},0,0,0,0, $dstW, $dstH, $srcW, $srcH); } sub printPng { my ($this, $filename) = @_; open(OUT,">$filename") || die("Failed to open \"$filename\":$!"); print OUT $this->{IMAGE}->png; close OUT; } sub getColor { my ($this, $r,$g,$b) = @_; my $index = $this->{IMAGE}->colorExact($r,$g,$b); if ($index < 0) { $index = $this->{IMAGE}->colorAllocate($r, $g, $b); } return $index; } sub copyPixel { my ($this, $x,$y,$source) = @_; my $src = $source->{IMAGE}; my ($r, $g, $b) = $src->rgb($src->getPixel($x,$y)); my $index = $this->getColor($r,$g,$b); $this->{IMAGE}->setPixel($x,$y,$index); } sub getBounds { my ($this) = @_; return $this->{IMAGE}->getBounds(); } sub print { my ($this, $filename) = @_; open(OUT,">$filename") || die("Unable to open \"$filename\":$!"); print OUT $this->{IMAGE}->png; close OUT; } sub getColorMinMax { my ($this, $value, $min, $max) = @_; return $this->getColor(0,0,0) unless (defined $value || $value == 0); $this->CheckBins($min,$max); my @bins = @{$this->{bins}}; my $i = 0; while ($i < $#bins && $value >= $bins[$i+1]{"start"}) { $i++; } return $bins[$i]{"color"}; } sub CheckBins { my ($this, $min, $max) = @_; if (!(defined $this->{bins}) || ($this->{min} != $min) || ($this->{max} != $max)) { $this->CreateBins($min, $max); } } sub CreateBins { my ($this, $min, $max) = @_; my $num_bins = $NUM_BINS-1; my @bins; my $range = $max - $min; # Handle the case were there are just one per bin if ($range <= $num_bins) { foreach my $i (0..$range) { $bins[$i]{size} = 1; } } else { my $total = $range; if ($total > 0) { my $scaler = log ($total)/$num_bins; foreach my $i (0..$num_bins) { my $j = $num_bins - $i; my $size = int(exp($j*$scaler)-exp(($j-1)*$scaler)); $size = 1 if ($size < 1); if ($total - $size < 0) { $size = $total; } $bins[$j]{size} += $size; $total -= $size; last if ($total <= 0); } $bins[$#bins]{size} += $total; } } my $current = $min; foreach my $i (0..$#bins) { my $fraction = 0; if ($#bins > 0) { $fraction = (.55*$i)/$#bins; } $fraction += .4; if ($fraction > .55) { $fraction += .23; } my $h = 360*$fraction; my $v = 100; my $s = 100; my $color = $this->getColorHSV($h,$v,$s); my $size = $bins[$i]{size}; $bins[$i]{start} = $current; $bins[$i]{end} = $current + $size-1; $bins[$i]{color} = $color; $current += $size; } $this->{bins} = \@bins; $this->{min} = $min; $this->{max} = $max; } sub getColorHSV { my ($this, $h, $s, $v) = @_; return $this->getColor($this->hsv2rgb($h, $s, $v)); } sub hsv2rgb { my ($this, $h, $s, $v) = @_; my ($r, $g, $b); $h = 0 if $h < 0; $h -= 360 if $h >= 360; $h /= 60; my $f = ($h - int $h) * 255; $s /= 100; $v /= 100; if (int $h == 0) { $r = $v * 255; $g = $v * (255 - ($s * (255 - $f))); $b = $v * 255 * (1 - $s); } if (int $h == 1) { $r = $v * (255 - $s * $f); $g = $v * 255; $b = $v * 255 * (1 - $s); } if (int $h == 2) { $r = $v * 255 * (1 - $s); $g = $v * 255; $b = $v * (255 - ($s * (255 - $f))); } if (int $h == 3) { $r = $v * 255 * (1 - $s); $g = $v * (255 - $s * $f); $b = $v * 255; } if (int $h == 4) { $r = $v * (255 - ($s * (255 - $f))); $g = $v * (255 * (1 - $s)); $b = $v * 255; } if (int $h == 5) { $r = $v * 255; $g = $v * 255 * (1 - $s); $b = $v * (255 - $s * $f); } return ($r,$g,$b); } sub rgb2hsv { my ($this, $r, $g, $b) = @_; my ($h, $s, $v); my ($min, $max); map { $max = $_ if (!defined $max or $max < $_) } ($r, $g, $b); map { $min = $_ if (!defined $min or $min > $_) } ($r, $g, $b); $v = $max; if ($max != 0) { $s = ($max - $min) / $max; } else { $s = 0; } if ($s == 0) { $h = 0; } else { $h = 0; my $delta = $max - $min; if ($r == $max) { $h = ($g - $b) / $delta; } elsif ($g == $max) { $h = 2 + ($b - $r) / $delta; } elsif ($b == $max) { $h = 4 + ($r - $g) / $delta; } $h *= 60; if ($h < 0) { $h += 360; } } return ($h, $s * 100, $v * 100 / 255); } 1;