use strict;
use Prima;
die "need image\n" unless @ARGV;
my $source = Prima::Image-> load($ARGV[0]);
die "Can't load $ARGV[0]:$@\n" unless $source;
As you can see, $source will hold a Prima::Image object if the
image can be loaded. Prima::Image provides many functions: in addition
to the above demonstrated loading, it can save images, convert the bit
depth, resize, and even draw on images using whatever graphic subsystem the
platform provides. Namely, on Unix it will use X11, on Windows - GDI, etc (
see Prima::Image
documentation for more). For this task, we shall use all of these functions,
and more.
my $mask = Prima::Image-> new(
width => $source-> width * 2,
height => $source-> height * 2,
type => im::BW,
);
It's type will be im::BW, which is a shortcut for im::bpp1
| im::GrayScale, a 1-bit grayscale image. Prima supports 1, 4, 8, and 24
bit color images, and 1, 8, 16, and 32 bit grayscale images; the latter are
needed mostly for image processing. Prima can also deal with float and
double images, in single-band and dual-band forms (the latter can be
needed, for example, for images composed from real and imaginary parts. Such
images can be created by Fourier transform).
Well anyway, for this task we only need a simple binary mask.
Let's draw the circles:
Calculate the center and number or circles:
my $step = 2;
my @size = $mask-> size;
my $max = $size[$size[0] < $size[1]] / 2;
my @center = map { int($_ + .5) } ($size[0] / 2, $size[1] / 2);
Begin the drawing session, clear the image, and set the line width:
$mask-> begin_paint;
$mask-> clear;
$mask-> lineWidth( $step);
Draw the circles:
for ( my $i = 0; $i < $max; $i += $step * 2) {
my $d = $i * 2;
$mask-> ellipse( @center, $d, $d);
}
And finally, close the drawing session:
$mask-> end_paint;
Since drawing calls such as ellipse operate in the GUI system space
(in GDI memory, or in X11 bitmaps), Prima must synchronize pixel content between
its own and GUI system memory. Session management with begin_paint and
end_paint copies image pixels first to GUI memory, and then, after
the drawing was done, copies them back.
Here's the result:
use IPA qw(Misc);
$source-> type(im::bpp24);
my @channels = @{ IPA::Misc::split_channels( $source, 'rgb') };
Apply the mask:
$channels[0]-> put_image(
243, 159,
$mask, rop::AndPut
);
Prima can do direct blitting operation without resorting to GUI functions. It
is both faster and lossless, and thus is preferred when possible. Note that the
very same call, put_image, if called from within begin_paint
/ end_paint brackets, gets executed using GUI functions.
Finally, the three channels with the mask applied with different offsets, can be combined back to an RGB image:
my $output = combine_channels( \@channels, 'rgb');
and convert to 256 (or less) colors, because GIF can only contain 8-bit images:
$output-> type(im::bpp8);
push @img, $output;
Now we use Prima's ability to save multiframe GIFs, with specified loop
count (0 is indefinite) and frame delay:
my $saved = Prima::Image-> save(
"anim.gif",
images => \@img,
loopCount => 0,
delayTime => 5,
);
die "Cannot save anim.gif:$@\n" unless $saved == @img;
save returns number of frames stored successfully.
Here is the final result:
use strict;
use Prima 1.27;
use IPA qw(Misc);
die "need image\n" unless @ARGV;
my $source = Prima::Image-> load($ARGV[0]);
die "Can't load $ARGV[0]:$@\n" unless $source;
$source-> type(im::bpp24);
my $mask = Prima::Image-> new(
width => $source-> width * 2,
height => $source-> height * 2,
type => im::BW,
);
my @size = $mask-> size;
my $max = $size[$size[0] < $size[1]] / 2;
my $step = 2;
my @centers = (
[ 243, 159 ],
[ 208, 150 ],
[ 233, 111 ],
);
my @img;
for ( my $offset = 0; $offset < $step * 2; $offset++) {
$mask-> begin_paint;
$mask-> clear;
$mask-> lineWidth( $step);
my @center = map { int($_ + .5) } ($size[0] / 2, $size[1] / 2);
for ( my $i = 0; $i < $max; $i += $step * 2) {
my $d = $offset * 2 + $i * 2;
$mask-> ellipse( @center, $d, $d);
}
$mask-> end_paint;
my @channels = @{ IPA::Misc::split_channels( $source, 'rgb') };
for ( my $i = 0; $i < @channels; $i++) {
$channels[$i]-> put_image(
$centers[$i]->[0] - $center[0],
$centers[$i]->[1] - $center[1],
$mask, rop::AndPut
);
}
my $output = combine_channels( \@channels, 'rgb');
$output-> type(im::bpp8);
push @img, $output;
}
my $saved = Prima::Image-> save(
"output.gif",
images => \@img,
loopCount => 0,
delayTime => 5,
);
die "Cannot save output.gif:$@\n" unless $saved == @img;