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__

Reply via email to