#!/usr/local/bin/perl ## addtRNS.cgi v0.4c April 28 2001 by techan ## v0.4c Pallet colors can be displayed. ## v0.4b Fix pallet display bug in color depth except 8-bit depth. ## v0.4a Fix grayscale bug. Add PLTE information. ## Usage: ## 1. To add a tRNS chunk to an RGB color image with 8/16-bit depth, ## addtRNS.cgi?-t(transparent color)-n(filename) ## -- transparent color: 6 or 12 hexadecimal digits (8-bit depth range: ## 000000 - ffffff) (16-bit depth range: 000000000000 - ffffffffffff) ## -- eg. addtRNS.cgi?-t000000-nSAMPLE.png ## 2. To add a tRNS chunk to a grayscale image with 1/2/4/8/16-bit depth, ## addtRNS.cgi?-g(transparent gray)-n(filename) ## -- transparent gray: 1, 2 or 4 hexadecimal digits (1-bit depth ## range: 0 - 1) (2-bit depth range: 0 - 3) (4-bit depth range: 0 - f) ## (8-bit depth range: 00 - ff) (16-bit depth range: 0000 - ffff) ## -- eg. addtRNS.cgi?-g00-nSAMPLE.png ## 3. To add a tRNS chunk to a palleted color image, ## addtRNS.cgi?-p(index1,alpha value1[/index2,alpha value2]...)-n(filename) ## -- each index: one or two hexadecimal digits (00 - ff) ## -- each alpha value: one or two hexadecimal digits (00 - ff) ## -- eg. addtRNS.cgi?-p00,00/05,00-nSAMPLE.png ## 4. To obtain image information, ## addtRNS.cgi?-n(filename) ## -- eg. addtRNS.cgi?-nSAMPLE.png %colortypes = ( 0=>"gray", 2=>"RGB", 3=>"paletted", 4=>"gray+alpha", 6=>"RGB+alpha"); $e=0; $demo=0; $select = 1; $arg = $ARGV[0]; if ($arg eq "") { &Error(0); } if ($arg =~ s/^[\-_]t//) { $select = 2; if ($arg =~ s/^([0-9a-fA-F]{12})//) { $R = hex(substr($1, 0, 4)); $G = hex(substr($1, 4, 4)); $B = hex(substr($1, 8, 4)); } elsif ($arg =~ s/^([0-9a-fA-F]{6})//) { $R = hex(substr($1, 0, 2)); $G = hex(substr($1, 2, 2)); $B = hex(substr($1, 4, 2)); } else { &Error('Transparent color is illegal'); } } elsif ($arg =~ s/^[\-_]g//) { $select = 0; if ($arg =~ s/^([0-9a-fA-F]+)//) { $Y = hex($1); } else { &Error('Transparent gray is illegal'); } } elsif ($arg =~ s/^[\-_]p//) { $select = 3; @A =(); $Pmax=0; if ($arg =~ s/^([0-9a-fA-F\,\/]+)//) { @pa = split('/', $1); for ($i=0;$i<=$#pa;$i++) { ($P,$A) = split(',', $pa[$i]); $P = hex($P); $A = hex($A); $A[$P] = $A; if ($P > $Pmax) { $Pmax = $P; } } for ($i=0;$i<=$Pmax;$i++) { if ($A[$i] eq "") { $A[$i] = 255; } } } else { &Error('Transparent alpha is illegal'); } } else { $demo = 1; } if ($arg =~ s/^[\-_]n//) { if ($arg =~ s/^([\w\-\~\/\.]+)$//) { $file = $1; } else { &Error('Filename is illegal.'); } } else { &Error(0); } if (! -f $file) { &Error('File is absent.'); } &InitCrcTable; $pngsize = -s $file; open(IN, "$file") || &Error('Can\'t read file.'); binmode(IN); read(IN, $sig, 8); unless (&ValidateSignature) { close(IN); &Error('No png signature.'); } read(IN, $png, $pngsize-8); close(IN); $png=$sig.$png; $pos=8; if ($demo) { &Demo; } $cut=0; $add=0; 1 while (&AddTrans); if ($e == 1) { &Error('Color Type is different.'); } elsif ($e == 2) { &Error('PNG has been broken.'); } elsif ($e == 3) { &Error('IDAT is not found.'); } elsif ($e == 4) { &Error('Transparent color is too large.'); } elsif ($e == 5) { &Error('Transparent gray is too large.'); } elsif ($e == 6) { &Error('Pallet index is too large.'); } elsif ($e == 7) { &Error('PLTE chunk is abnormal.'); } $pngsize += $add - $cut; $|=1; print "Content-type: image/png\n"; # print "Content-length: $pngsize\n"; print "\n"; binmode(STDOUT); print $png; exit; sub Demo { 1 while (&CheckChunk); print "Content-type: text/html\n\n"; print "\n\n"; print "Add tRNS Demo\n"; print "\n\n"; print "Bit Depth: $depth bits
\n"; print "Color Type: $colortype ($colortypes{$colortype})
\n"; if ($find_plte) { $entry = int(($#PLTE+1)/3); print "Pallet Entry= $entry
\n"; for ($i=0;$i<$entry;$i++) { $PR[$i] = substr(&Dec2Hex($PLTE[$i*3]),2); $PG[$i] = substr(&Dec2Hex($PLTE[$i*3+1]),2); $PB[$i] = substr(&Dec2Hex($PLTE[$i*3+2]),2); $PI[$i] = substr(&Dec2Hex($i),2); } print "\n"; for ($i=0;$i<$entry;$i+=8) { print "\n"; for ($j=0;$j<8;$j++) { if ($i+$j < $entry) { print ""; } else { print ""; } } print "\n\n\n"; for ($k=0;$k<8;$k++) { if ($i+$k < $entry) { print ""; } else { print ""; } } print "\n\n\n"; for ($l=0;$l<8;$l++) { if ($i + $l < $entry) { print ""; } else { print ""; } } print "\n\n"; } print "
$PI[$i+$j]
"; print "|
#$PR[$i+$l]$PG[$i+$l]$PB[$i+$l]

\n"; } if ($find_trns) { if ($colortype == 0) { $Y= &Dec2Hex($Y); if ($depth < 8) { $Y=substr($Y,3); } elsif ($depth < 16) { $Y=substr($Y,2); } print "Transparent Gray: #$Y
\n"; } elsif ($colortype == 2) { $R= &Dec2Hex($R); $G=&Dec2Hex($G); $B=&Dec2Hex($B); if ($depth == 8) { $R = substr($R,2); $G = substr($G,2); $B = substr($B,2); } print "Transparent Color: "; if ($depth == 8) { print "\n\n"; print "\n"; print "\n\n\n"; print "\n
|
#$R$G$B
"; } else { print "#$R$G$B"; } print "
\n"; } elsif ($colortype == 3) { print "Transparent Pallet:
\n"; for ($i=0;$i<=$#alpha;$i++) { $index = substr(&Dec2Hex($i),2); $alpha = substr(&Dec2Hex($alpha[$i]),2); if ($alpha ne 'ff') { print "-- Pallet Index: $index, Alpha Value: $alpha
\n"; } } } } else { print "No tRNS chunk
\n"; } print "\n\n"; exit; } sub Dec2Hex { my $a = $_[0]; my $af = int($a / 256); my $ar = $a % 256; my $a1 = int($af / 16); my $a2 = $af % 16; my $a3 = int($ar / 16); my $a4 = $ar % 16; my $hex = '0123456789abcdef'; $a1 = substr($hex, $a1, 1); $a2 = substr($hex, $a2, 1); $a3 = substr($hex, $a3, 1); $a4 = substr($hex, $a4, 1); $a = $a1.$a2.$a3.$a4; return($a); } sub CheckChunk { $chunk=substr($png, $pos, 8); ($length, $type)=unpack("N A4", $chunk); if ($type eq 'IEND') { return 0; } if ($pos > ($pngsize-12)) { return 0; } if ($type eq 'IHDR') { $depth = unpack("C", substr($png, $pos+16, 1)); $colortype = unpack("C", substr($png, $pos+17, 1)); } if ($type eq 'PLTE') { $find_plte = 1; @PLTE = unpack("C*",substr($png, $pos+8,$length)); } if ($type eq 'tRNS') { $find_trns = 1; if ($colortype == 0) { $Y = unpack("n",substr($png, $pos+8, $length)); } elsif ($colortype == 2) { ($R,$G,$B) = unpack("n3",substr($png, $pos+8, $length)); } elsif ($colortype == 3) { @alpha = unpack("C*",substr($png, $pos+8, $length)); } } if ($type eq 'IDAT') { return 0; } $pos +=$length + 12; return 1; } sub AddTrans { $chunk=substr($png, $pos, 8); ($length, $type)=unpack("N A4", $chunk); if ($type eq 'IEND') { $e= 3; return 0; } if ($pos > ($pngsize-$cut-12)) { $e = 2; return 0; } if ($type eq 'IHDR') { $depth = unpack("C", substr($png, $pos+16, 1)); $colortype = unpack("C", substr($png, $pos+17, 1)); if ($select != $colortype) { $e = 1; return 0; } if ($colortype == 0) { if ($Y > (2**$depth-1)) { $e=5; return 0; } } elsif ($colortype == 2) { if ($depth == 8 && ($R > 255 || $G > 255 || $B > 255)) { $e = 4; return 0; } } elsif ($colortype == 3) { if ($Pmax > (2**$depth-1)) { $e=6; return 0; } } else { $e = 1; return 0; } } if ($type eq 'PLTE') { if ($length % 3 !=0) { $e=7; return 0; } if ($colortype == 3 && $Pmax > ($length / 3 - 1)) { $e= 6; return 0; } } if ($type eq 'tRNS') { $png=substr($png, 0, $pos).substr($png, $pos+$length+12); $cut += $length+12; return 1; } if ($type eq 'IDAT') { if ($colortype == 0) { $trans = 'tRNS'.pack("n", $Y); # Gray } elsif ($colortype == 2) { $trans = 'tRNS'.pack("n3", $R, # Red $G, # Green $B); # Blue } elsif ($colortype == 3) { $trans = 'tRNS'.pack("C*", @A); # Alpha } $png=substr($png,0,$pos).pack("N", length($trans)-4).$trans.&CalcCrc($trans).substr($png, $pos); $add = 8 + length($trans); return 0; } $pos +=$length + 12; return 1; } sub ValidateSignature { $sig eq "\x89PNG\r\n\x1a\n" && return 1; return 0; } sub Error { $err = $_[0]; print "Content-type: text/html\n\n"; print "\n\n"; print "Add tRNS\n"; print "\n\n"; if ($err) { print "Error -- $err

\n"; } print <<_EOM_;
Usage:
1. To add a tRNS chunk to an RGB color image with 8/16-bit depth,
	addtRNS.cgi?-t(transparent color)-n(filename)
	-- transparent color: 6 or 12 hexadecimal digits (8-bit depth range:
	   000000 - ffffff) (16-bit depth range: 000000000000 - ffffffffffff)
	-- eg. addtRNS.cgi?-t000000-nSAMPLE.png
2. To add a tRNS chunk to a grayscale image with 1/2/4/8/16-bit depth,
	addtRNS.cgi?-g(transparent gray)-n(filename)
	-- transparent gray: 1, 2 or 4 hexadecimal digits (1-bit depth
	   range: 0 - 1) (2-bit depth range: 0 - 3) (4-bit depth range: 0 - f)
	   (8-bit depth range: 00 - ff) (16-bit depth range: 0000 - ffff) 
	-- eg. addtRNS.cgi?-g00-nSAMPLE.png
3. To add a tRNS chunk to a palleted color image,
	addtRNS.cgi?-p(index1,alpha value1[/index2,alpha value2]...)-n(filename)
	-- each index: one or two hexadecimal digits (00 - ff)
	-- each alpha value: one or two hexadecimal digits (00 - ff) 
	-- eg. addtRNS.cgi?-p00,00/05,00-nSAMPLE.png
4. To obtain image information,
	addtRNS.cgi?-n(filename)
	-- eg. addtRNS.cgi?-nSAMPLE.png
_EOM_ print "\n\n\n"; exit; } sub InitCrcTable { my $d; @crc_table = (); for (0 .. 255) { $d = $_; for (0 .. 7) { if ($d & 1) { $d = 0xedb88320 ^ (($d >> 1) & 0x7fffffff); } else { $d = ($d >> 1) & 0x7fffffff; } } $crc_table[$_] = $d; } } sub CalcCrc { my $data = $_[0]; my $c = 0xffffffff; foreach (unpack("C*", $data)) { $c = $crc_table[($c ^ $_) & 0xff] ^ (($c >> 8) & 0xffffff); } return(pack("N", ~$c)); } __END__