Brian Millham wrote:
I've got a simple script that can display an animated gif.
I think that it could be improved. It's a bit slow loading the gif, and it
doesn't handle the missing information from some frames (changes from
I-frames [in MPEG speak]) and it doesn't handle transparent backgrounds.
Oh, and there's no error checking.
OK, so I caught the bug too. I've added the 'coalesce()' call that
Steve found, and am handling different delays for different frames. By
using the Win32::GUI::DIBitmap AlphaCopyToDC method for drawing into the
DC directly it copes with transparent colours, but as a result the
drawing is a bit slow (and a bit flickery).
It needs a recent Win32::GUI for the constants - if you haven't
downloaded and installed the 1.04 beta yet(from
http://www.robmay.me.uk/win32gui/) then you'll need to fix up the
constants I've used. (If I remember correctly AlphaCopyToDC has a
resource leak in earlier DIBitmaps too)
My attempt below - enjoy.
Rob.
#!perl -w
# (C) 2006 Brian Millham [EMAIL PROTECTED]
# The perl artistic license applies.
# Modification for drawing into window DC by Robert May
use strict;
use warnings;
use Win32::GUI 1.03_04, qw( CW_USEDEFAULT WM_ERASEBKGND);
use Win32::GUI::DIBitmap();
use Image::Magick();
use Time::HiRes qw(gettimeofday tv_interval);
my $anifile = shift;
# Load frames as Win32::GUI::DIBitmap DIBs and store in array
# array is list of delays in milliseconds and DIB pairs
my @delay_frames = LoadDIBs($anifile);
# Get the number of frames
my $lastframe = $#delay_frames/2;
my $width = $delay_frames[1]->Width();
my $height = $delay_frames[1]->Height();
# Sum the loop time
my $loop_time = 0;
$loop_time += $delay_frames[$_*2] for (0 .. $lastframe);
# Confirm some info
my $text = sprintf <<__LABELTEXT, $anifile, $width, $height,
($lastframe+1), $loop_time;
Animation file:\t%s
Width:\t%d pixels
Height:\t%d pixels
Length:\t%d frames
Expected loop time:\t%d ms
__LABELTEXT
# Create the window, sized for the image
my $W = Win32::GUI::Window->new(
-title => "Animated GIF Test",
-name => 'Window',
-left => CW_USEDEFAULT,
-size => [$width, $height], # adjust this below
-background => 0xFF0000, # Show transparency working
-onPaint => \&paint,
-onTimer => \&timer,
) or die "Failed to create window";
#TODO: really want AdjustWindowRect() to make this easier
my $cwidth = $W->ScaleWidth();
my $cheight = $W->ScaleHeight();
$W->Resize($width+$width-$cwidth,$height+$height-$cheight);
#Create an owned window for the information
my $W2 = Win32::GUI::Window->new(
-parent => $W,
-visible => 1, # owned window is hidden while parent is hidden
-title => "Animation Information",
-name => 'INFO',
-pos => [$W->Left()+$W->Width(), $W->Top()],
-size => [200,170],
) or die "Failed to create W2";
$W2->AddLabel(
-name => 'LBL',
-pos => [10,10],
-size => [$W2->ScaleWidth()-20, $W2->ScaleHeight()-20],
) or die "Failed to create Label";
# Store away the frame info we'll need in the callbacks
$W->UserData({
currentframe => 0,
lastframe => $lastframe,
timer => [gettimeofday],
text => $text . "Actual loop time:\t\t%d ms",
});
# Add the timer to advance and redraw the image
$W->AddTimer('T1', $delay_frames[0]);
# Show the main window.
$W->Show();
Win32::GUI::Dialog();
# done, so hide main window and exit
$W->Hide();
exit(0);
# Load the frames, get the delay and convert frames to
Win32::GUI::DIBitmap DIBs
sub LoadDIBs {
my $file = shift;
my (@frames, @delay_dibs);
# Create new Image::Magick object
my $image = Image::Magick->new();
# Load the image(s) from file
#TODO: error checking
$image->Read($file);
# Coalesce multiple images so that each frame is a whole image
$image->Coalesce();
# Convert all ImageMagick frames to raw gif data
push @frames, $image->[$_]->ImageToBlob() for (0 .. $#$image);
# Get delay and Create DIBitmap objects for each frame
for (0 .. $#$image) {
# Get the frame delay.
# convert delay from 1/100th seconds to milliseconds
push @delay_dibs, $image->[$_]->Get('delay') * 10;
# convert frame to DIB
push @delay_dibs, Win32::GUI::DIBitmap->newFromData($frames[$_]);
}
return @delay_dibs;
}
sub timer {
my ($win, $name) = @_;
# Retrieve frame info
my $info = $win->UserData();
# Advance current frame
$info->{currentframe}++;
# Wrap after last frame, and compute stats
if($info->{currentframe} > $info->{lastframe}) {
$info->{currentframe} = 0;
# update stats in info window
$win->INFO->LBL->Text(
sprintf $info->{text},
int(tv_interval($info->{timer}) * 1000)
);
$info->{timer} = [gettimeofday];
}
# Store frame info
$win->UserData($info);
# Request system to repaint window
$win->InvalidateRect(0);
# Set the new delay
$win->{$name}->Interval($delay_frames[$info->{currentframe}*2]);
return 0;
}
sub paint {
my ($win, $dc) = @_;
# Retrieve frame info
my $info = $win->UserData();
# Erase the background - erases the last image
$win->SendMessage(WM_ERASEBKGND, $dc->{-handle}, 0);
# Display the frame
$delay_frames[($info->{currentframe}*2)+1]->AlphaCopyToDC($dc);
#$delay_frames[($info->{currentframe}*2)+1]->AlphaStretchToDC($dc,
0, 0, $win->ScaleWidth(), $win->ScaleHeight());
$dc->Validate();
return 0;
}
__END__