#!/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 "| $PI[$i+$j] | ";
} else {
print " | ";
}
}
print "\n
\n\n";
for ($k=0;$k<8;$k++) {
if ($i+$k < $entry) {
print "| ";
print "| | ";
} else {
print " | ";
}
}
print "\n
\n\n";
for ($l=0;$l<8;$l++) {
if ($i + $l < $entry) {
print "| #$PR[$i+$l]$PG[$i+$l]$PB[$i+$l] | ";
} else {
print " | ";
}
}
print "\n
\n";
}
print "
\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| #$R$G$B | \n";
print "
\n
";
} 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__