#!/usr/bin/perl -w # Expand pixels in a PPM to make mosaic like images. # # To do: separate color choices for each border. # user-defined pixel manipulation function. # # Benjamin Elijah Griffin 28 Feb 2003 use strict; use integer; use Image::PPMlib; use vars qw( $id $arg $err $i $j $k $r $g $b $br $bg $bb $tri $inforef @line @out $pc $pix $mpix $file $bmode $bpixels $bpT $bpB $bpL $bpR $pixels $VERSION ); $id = $0; $id =~ s:.*/::; $VERSION = "0.5"; $bmode = 'none'; $bpT = $bpB = $bpL = $bpR = 1; $pixels = 10; while (defined($ARGV[0]) and $ARGV[0] =~ /^-(.*)/) { $arg = $1; # stripped of first hyphen shift; if ($arg eq '-help' or $arg eq '-version' or $arg eq 'v') { &usage(0); } elsif ($arg eq 'b' or $arg eq '-border') { $bmode = shift; if ($bmode eq 'none' or $bmode eq 'dark' or $bmode eq 'light') { 1; # valid mode that requires no processing now } elsif ($bmode eq 'darker' or $bmode eq 'lighter') { $bmode =~ s/er$//; } elsif ($bmode eq 'black') { ($br, $bg, $bb) = ( 0, 0, 0 ); } elsif ($bmode eq 'white') { ($br, $bg, $bb) = ( 255, 255, 255 ); } elsif ($bmode eq 'red') { ($br, $bg, $bb) = ( 255, 0, 0 ); } elsif ($bmode eq 'green') { ($br, $bg, $bb) = ( 0, 255, 0 ); } elsif ($bmode eq 'blue') { ($br, $bg, $bb) = ( 0, 0, 255 ); } elsif ($bmode =~ /^fixed:(\d+:\d+:\d+)$/) { $tri = $1; $bmode = 'fixed'; ($br, $bg, $bb) = split(/:/, $tri, 3) } elsif ($bmode =~ /^fixed:([0-9a-fA-F]{6})$/) { $tri = $1; $bmode = 'fixed'; ($br, $bg, $bb) = hextriplettoraw($tri); $br = ord($br); $bg = ord($bg); $bb = ord($bb); } else { $err = 1; warn "$id: Invalid mode for -b (--border)\n"; } } elsif ($arg eq 'e' or $arg eq '-borderpixels') { $bpixels = shift; if ($bpixels =~ /^(\d+),(\d+),(\d+),(\d+)$/) { $bpT = $1; $bpB = $2; $bpL = $3; $bpR = $4; $bpixels = ''; } elsif ($bpixels =~ /^[1-9]\d*$/) { $bpT = $bpB = int($bpixels / 2); if ($bpixels % 2) { $bpT ++; } $bpL = $bpT; $bpR = $bpR; } else { $err = 1; warn "$id: Invalid pixel value for -e (--borderpixels)\n"; } } elsif ($arg eq 'p' or $arg eq '-pixels') { $pixels = shift; if ($pixels !~ /^[1-9]\d*$/) { $err = 1; warn "$id: Invalid pixel value for -p (--pixels)\n"; } } elsif ($arg eq '-') { last; } else { $err = 1; warn "$id: unrecognized argument: -$arg\n"; } } if ($err) { die "Use --help for usage\n"; } $file = shift; if(!defined($file)) { $file = '-'; } if(!open(PPM, "< $file")) { die "$id: Cannot open $file: $!\n"; } $inforef = readppmheader(\*PPM); if ($$inforef{error}) { die "$id: $file: $$inforef{error}\n"; } if ($$inforef{bgp} ne 'p') { die "$id: $file is not a ppm file\n"; } # No border overrides any specified border size if ($bmode eq 'none') { $bpT = $bpB = $bpL = $bpR = 0; } printf("P6\n%d %d\n%d\n", $$inforef{width} * ($bpL + $bpR + $pixels), $$inforef{height} * ($bpT + $bpB + $pixels), 255); # If we can, calculate the border stuff now. if($bmode ne 'none') { $pc = $bpL + $bpR + $pixels; # width of border + cell if(($bmode ne 'light') and ($bmode ne 'dark')) { $pix = $Image::PPMlib::decraw{$br} . $Image::PPMlib::decraw{$bg} . $Image::PPMlib::decraw{$bb}; } } # loop i: each line of height for ($i = 0; $i < $$inforef{height}; $i ++) { @line = readpixels_dec(\*PPM, $$inforef{type}, $$inforef{width}); for($k = 0; $k < $bpT + $bpB + $pixels; $k ++) { $out[$k] = ''; } # loop j: each pixel in the line for($j = 0; $j < @line; $j ++) { # figure out per-pixel borders if ($bmode eq 'light') { $br = $line[$j][0] * 11 / 10; # mult by 1.1 with 'use integer' $bg = $line[$j][1] * 11 / 10; # mult by 1.1 with 'use integer' $bb = $line[$j][2] * 11 / 10; # mult by 1.1 with 'use integer' $br = ($br>255)? 255 : $br; $bg = ($bg>255)? 255 : $bg; $bb = ($bb>255)? 255 : $bb; $pix = $Image::PPMlib::decraw{$br} . $Image::PPMlib::decraw{$bg} . $Image::PPMlib::decraw{$bb}; } elsif ($bmode eq 'dark') { $br = $line[$j][0] * 9 / 10; # mult by 0.9 with 'use integer' $bg = $line[$j][1] * 9 / 10; # mult by 0.9 with 'use integer' $bb = $line[$j][2] * 9 / 10; # mult by 0.9 with 'use integer' $pix = $Image::PPMlib::decraw{$br} . $Image::PPMlib::decraw{$bg} . $Image::PPMlib::decraw{$bb}; } # if any border, apply top and left now if($bmode ne 'none' and ($bpT or $bpL)) { for ($k = 0; $k < $bpT; $k ++) { # top border, top $bpT rows $out[$k] .= $pix x $pc; } for ($k = 0; $k < $pixels; $k ++) { # left border, after $bpT rpws $out[$k + $bpT] .= $pix x $bpL; } } # main pixel body $mpix = $Image::PPMlib::decraw{$line[$j][0]} . $Image::PPMlib::decraw{$line[$j][1]} . $Image::PPMlib::decraw{$line[$j][2]}; for ($k = 0; $k < $pixels; $k ++) { # inside main pixel, after $bpT rows $out[$k + $bpT] .= $mpix x $pixels; } # if any border, apply bottom and right now if($bmode ne 'none' and ($bpB or $bpR)) { for ($k = 0; $k < $bpB; $k ++) { # bottom border, after $bpT + $pixels $out[$k + $pixels + $bpT] .= $pix x $pc; } for ($k = 0; $k < $pixels; $k ++) { # right border, after $bpT rows $out[$k + $bpT] .= $pix x $bpR; } } } # end j: per pixel loop for($k = 0; $k < $bpT + $bpB + $pixels; $k ++) { # if no border, those strings will still be '' and are safe to print print $out[$k]; } if(@line != $$inforef{width}) { warn "$id: ran out of pixels prematurely\n"; last; } } # for line of height sub usage($) { my $rc = shift; print STDERR <<"ProgramUsage"; $id version $VERSION usage: ppmmosaic reads PPM(5) files and makes larger pixelated versions of them, with several border options. Arguments: -b MODE --border MODE MODE can be black, white, red, green, blue, fixed:RGB, lighter, darker, or none. RGB can 6 hex digits or a colon- seperated decimal triple. 'lighter' and 'darker' are 10% changed from inner pixel -e SIZE --borderpixels SIZE make the border be SIZE pixels (total, if odd top and left borders will be larger) -e T,B,L,R --borderpixels T,B,L,R set sizes for Top, Bottom, Left, and Right borders -p SIZE --pixels SIZE make the tile be SIZE pixels (plus border) -- end arguments bordermode defaults to none, borderpixels defaults to 1, pixels defaults to 10 Examples: ppmmosaic -e 5 -p 20 -b fixed:255:150:200 foo.ppm > new-foo.ppm If foo.ppm was 10x10, the new image will be 250x250, with pink borders 3 pixels on top and left, 2 pixels on bottom and right around each of the original pixels, which will now be 20x20 in size. ppmmosaic --borderpixels 2 --border fixed:808080 --pixels 6 bar.ppm \ > new-bar.ppm Image new-bar.ppm will be 8 times larger than bar.ppm with 1 pixel all around gray borders on the 6x6 tiles from bar.ppm, looking something like a mosaic complete with grout. ppmmosaic -e 1,0,0,0 --border black --pixels 1 bar.ppm > new-bar.ppm Image new-bar.ppm will be twice as tall with a black line between each row. If bar.ppm had previously been compressed 50% vertically, it now has an early CRT scan-line look to it. ProgramUsage exit $rc; } __END__ =pod =head1 NAME ppmmosaic - make mosaic images by expanding pixels into bordered tiles =head1 DESCRIPTION ppmmosaic reads PPM(5) files and makes larger pixelated versions of them, with several border options. Such images can look like simple block mosaics, eg pick a big value for inner pixel size, and small border evenly sized on all edges. Or venetian blind effects can be created by having borders in one dimension only. Options: =over 4 =item * -b MODE --border MODE MODE can be C, C, C, C, C, CI, C, C, or C. For fixed color borders, the RGB value can 6 hex digits (eg "99CC33") or a colon-seperated decimal triple (eg "153:204:51"). The C, C, etc, border options are short hand for C, C, etc. The 'lighter' and 'darker' options are 10% changed from inner pixel. Default is C. =item * -e SIZE --borderpixels SIZE Makes the border be SIZE pixels total. If SIZE is odd, the top and left borders will be larger. So C<-e 5> is equivilent to C<-e 3,2,3,2> making the top and left borders three pixels, and the bottom and right borders two pixels. Default is equivilent to C<-e 2>. =item * -e T,B,L,R --borderpixels T,B,L,R Sets the sizes for Top, Bottom, Left, and Right borders individually. Default is equivilent to C<-e 1,1,1,1> =item * -p SIZE --pixels SIZE Makes the inner pixel tile be SIZE pixels. The border goes around this inner pixel. Default is C<10>. =item * -- Marks the end of the arguments =item * --help Show a help message and exit. =item * --version Same as C<--help>. =back =head1 COPYRIGHT Copyright 2003 by Eli the Bearded / Benjamin Elijah Griffin. Released under the same license(s) as Perl. =head1 AUTHOR Eli the Bearded originally the Image::PPMlib to make a script very much like this one easier to code. =head1 CPAN INFO =head1 SCRIPT CATEGORIES Image =head1 README ppmmosaic - make mosaic images by expanding pixels into bordered tiles =head1 PREREQUISITES This uses the C, C, C, and C modules. =head1 COREQUISITES None. =head1 OSNAMES Should be OS independent. =cut