User Tools

Site Tools


displaying_20animated_20gifs

Displaying animated GIFs

by Richard Russell, November 2007

The normal methods of displaying a GIF image in a BBC BASIC for Windows program (using the code listed in the Help documentation or in the article Displaying a JPEG or GIF in a picture box) will not display an animated GIF - only the first frame of the animation will be shown.

One straightforward method of displaying an animated GIF is to use an ATL container control. Assuming a suitable player is available (which there will be if Internet Explorer is installed on the PC) this will open and display the animation continuously until the ATL window is closed. An alternative method is to use Direct Show which gives a greater degree of control, for example you can pause and restart the animation. However it is still rather limiting, for example there is no straightforward way of controlling the animation speed.

When you need more control, an alternative method of displaying animated GIFs is to use the GDI Plus subsystem, installed as standard on Windows XP and Windows Vista and available as a redistributable file for earlier versions (Windows 98 onwards). See the Help documentation under Library Routines... Antialiased graphics for more information on GDI+.

To use this method you must first perform some initialisation, as follows:

        INSTALL @lib$+"GDIPLIB"
        PROC_gdipinit
 
        ON CLOSE PROC_gdipexit : QUIT
        ON ERROR PROC_gdipexit : SYS "MessageBox", @hwnd%, REPORT$, 0, 48 : QUIT
 
        SYS "LoadLibrary", "GDIPLUS.DLL" TO gdip%
        SYS "GetProcAddress", gdip%, "GdipLoadImageFromFile"            TO `GdipLoadImageFromFile`
        SYS "GetProcAddress", gdip%, "GdipImageGetFrameDimensionsCount" TO `GdipImageGetFrameDimensionsCount`
        SYS "GetProcAddress", gdip%, "GdipImageGetFrameDimensionsList"  TO `GdipImageGetFrameDimensionsList`
        SYS "GetProcAddress", gdip%, "GdipImageGetFrameCount"           TO `GdipImageGetFrameCount`
        SYS "GetProcAddress", gdip%, "GdipImageSelectActiveFrame"       TO `GdipImageSelectActiveFrame`
        SYS "GetProcAddress", gdip%, "GdipGetPropertyItemSize"          TO `GdipGetPropertyItemSize`
        SYS "GetProcAddress", gdip%, "GdipGetPropertyItem"              TO `GdipGetPropertyItem`
        SYS "GetProcAddress", gdip%, "GdipGetImageWidth"                TO `GdipGetImageWidth`
        SYS "GetProcAddress", gdip%, "GdipGetImageHeight"               TO `GdipGetImageHeight`
        SYS "GetProcAddress", gdip%, "GdipDrawImageRectI"               TO `GdipDrawImageRectI`
        SYS "GetProcAddress", gdip%, "GdipDisposeImage"                 TO `GdipDisposeImage`

You may of course put this code in a procedure or library if you don't want to clutter your main program. If you need to perform your own 'cleanup' operations (on an error or when the window is closed), call your own replacement routine in the ON CLOSE and ON ERROR statements, and move the PROC_gdipexit there.

Once you have carried out the initialisation you can simply display the animated GIF as follows:

        filename$ = "C:\www\bbcwin\bbctile3.gif"
        PROCanimatedgif(filename$, 500)

The second parameter of PROCanimatedgif is the time for which you want the animation to display, in centiseconds (one-hundredths of a second).

Once you have finished with the GDI+ subsystem (typically on exit from your program) don't forget to call PROC_gdipexit:

        PROC_gdipexit
        END

Here is the all-important PROCanimatedgif routine:

        DEF PROCanimatedgif(filename$, duration%)
        LOCAL wfile%, image%, ndims%, nframes%, frame%, psize%, xsize%, ysize%
        LOCAL xpos%, ypos%, endtime%, DimensionIDs{}, PropItem%, rc{}, delay%
 
        DIM wfile% LOCAL LEN(filename$)*2+1
 
        SYS "MultiByteToWideChar", 0, 0, filename$, -1, wfile%, LEN(filename$)+1
        SYS `GdipLoadImageFromFile`, wfile%, ^image%
        IF image% = 0 ERROR 100, "File not found"
 
        SYS `GdipImageGetFrameDimensionsCount`, image%, ^ndims%
        DIM DimensionIDs{(ndims%) a%,b%,c%,d%}
        SYS `GdipImageGetFrameDimensionsList`, image%, DimensionIDs{(0)}, ndims%
        SYS `GdipImageGetFrameCount`, image%, DimensionIDs{(0)}, ^nframes%
 
        PropertyTagFrameDelay% = &5100
        SYS `GdipGetPropertyItemSize`, image%, PropertyTagFrameDelay%, ^psize%
        DIM PropItem% LOCAL psize%
        SYS `GdipGetPropertyItem`, image%, PropertyTagFrameDelay%, psize%, PropItem%
 
        SYS `GdipGetImageWidth`, image%, ^xsize%
        SYS `GdipGetImageHeight`, image%, ^ysize%
 
        DIM rc{l%,t%,r%,b%}
        SYS "GetClientRect", @hwnd%, rc{}
 
        IF xsize%>rc.r% ysize% *= rc.r% / xsize% : xsize% = rc.r%
        IF ysize%>rc.b% xsize% *= rc.b% / ysize% : ysize% = rc.b%
 
        xpos% = (rc.r%-xsize%)/2
        ypos% = (rc.b%-ysize%)/2
        rc.l% = xpos% : rc.r% = xpos% + xsize%
        rc.t% = ypos% : rc.b% = ypos% + ysize%
 
        endtime% = TIME + duration%
        REPEAT
          SYS `GdipImageSelectActiveFrame`, image%, DimensionIDs{(0)}, frame%
          SYS `GdipDrawImageRectI`, FN_gdipg, image%, xpos%, ypos%, xsize%, ysize%
          SYS "InvalidateRect", @hwnd%, rc{}, 0
          *REFRESH
          delay% = PropItem%!(16+4*frame%)
          IF delay% WAIT delay% ELSE WAIT 8
          frame% = (frame%+1) MOD nframes%
        UNTIL TIME>endtime%
 
        SYS `GdipDisposeImage`, image%
        ENDPROC

If the image is a 'transparent GIF' (i.e. it has a transparent background colour) the code must be modified to clear the background before each frame is displayed. One way is to alter the main display loop as shown below, which clears the background to the current Text Background Colour (as would be used by CLS):

        endtime% = TIME + duration%
        SYS "CreateSolidBrush", @vdu%?71 + &1000000 TO brush%
        REPEAT
          SYS `GdipImageSelectActiveFrame`, image%, DimensionIDs{(0)}, frame%
          SYS "FillRect", @memhdc%, rc{}, brush%
          SYS `GdipDrawImageRectI`, FN_gdipg, image%, xpos%, ypos%, xsize%, ysize%
          SYS "InvalidateRect", @hwnd%, rc{}, 0
          *REFRESH
          delay% = PropItem%!(16+4*frame%)
          IF delay% WAIT delay% ELSE WAIT 8
          frame% = (frame%+1) MOD nframes%
        UNTIL TIME>endtime%
        SYS "DeleteObject", brush%

I'm sure you can see how you could modify this code if you wanted, for example, to display the animation for a certain number of loops or a certain number of frames, rather than for a certain time. The routine centers the GIF in the window; it could easily be modified to display it in a different position or at a different size.

This website uses cookies for visitor traffic analysis. By using the website, you agree with storing the cookies on your computer.More information
displaying_20animated_20gifs.txt · Last modified: 2018/04/13 15:32 by richardrussell