LB Booster
Programming >> Liberty BASIC language >> Displaying animated GIFs with LBB?
http://lbb.conforums.com/index.cgi?board=lblang&action=display&num=1476587985

Displaying animated GIFs with LBB?
Post by Harbinger on Oct 16th, 2016, 03:19am

I have asked over at the LB forums about having LB automatically animate my GIFs in a graphic window, but have not gotten a satisfactory answer for troubleshooting. I've tried the code I found on the Liberty Wiki but, while the image will load, it won't animate it.
I have also found a thread here at the LB Booster Wiki, but it's for BBC BASIC.

Can the booster help? undecided
Re: Displaying animated GIFs with LBB?
Post by Richard Russell on Oct 16th, 2016, 11:23am

on Oct 16th, 2016, 03:19am, Harbinger wrote:
Can the booster help? :-/

As far as I am aware there are basically two methods of displaying animated GIFs in Liberty BASIC. One is to use an embedded ATL control and the other is to use GDIPlus. The ATL method is I think quite well documented elsewhere; for example you can download AnimGifs.zip from Gordon Sweet's site here (I can't give any personal endorsement of the quality of this code).

The GDIPlus method gives you more control, but it involves a lot more code. I've listed a program below to demonstrate it; it's LBB-only because it uses arrays of structures, which are not supported in LB 4:

Code:
    nomainwin
    MaxFrames = 1000
    global MaxFrames
    
    WindowWidth = 800
    WindowHeight = 600
    open "Animated GIFs" for graphics_nsb as #w
    #w "trapclose quit"
    hw = hwnd(#w)
    calldll #user32, "GetDC", hw as ulong, hdc as ulong
    do
      filedialog "Select an animated GIF", "*.gif", GIFfile$
      if GIFfile$ <> "" then call AnimatedGIF hdc, GIFfile$, 10
    loop until GIFfile$ = ""
    close #w
    end
    
sub quit h$
    close #h$
    end
end sub

' specify a duration in seconds or zero to play the animation once
sub AnimatedGIF hDC, filename$, duration
    open "gdiplus.dll" for dll as #gdiplus
    struct tSI, GdiplusVersion as long, DebugEventCallback as ulong, _
        SuppressBackgroundThread as long, SuppressExternalCodecs as long
    tSI.GdiplusVersion.struct = 1
    struct GDIP, l as struct
    calldll #gdiplus, "GdiplusStartup", GDIP as struct, tSI as struct, 0 as long, r as void
    
    struct graphics, p as ulong
    calldll #gdiplus, "GdipCreateFromHDC", hDC as ulong, graphics as struct, r as void
    calldll #gdiplus, "GdipSetSmoothingMode", graphics.p.struct as long, 2 as long, r as void
    
    l = len(filename$) + 1
    wide$ = space$(l * 2) + chr$(0)
    calldll #kernel32, "MultiByteToWideChar", 0 as long, 0 as long, _
        filename$ as ptr, -1 as long, wide$ as ptr, l as long, r as long

    struct image, p as ulong
    calldll #gdiplus, "GdipLoadImageFromFile", wide$ as ptr, image as struct, r as void
    if image.p.struct = 0 then notice "Couldn't open ";filename$ : exit sub
 
    struct dims, n as long
    calldll #gdiplus, "GdipImageGetFrameDimensionsCount", image.p.struct as ulong, _
        dims as struct, r as void
    if dims.n.struct > MaxFrames then notice "Too many frames: increase MaxFrames" : exit sub

    struct dimIDs(MaxFrames), a as long, b as long, c as long, d as long
    calldll #gdiplus, "GdipImageGetFrameDimensionsList", image.p.struct as ulong, _
        dimIDs(0) as struct, dims.n.struct as long, r as void
        
    struct frames, n as long    
    calldll #gdiplus, "GdipImageGetFrameCount", image.p.struct as ulong, _
        dimIDs(0) as struct, frames as struct, r as void
    if frames.n.struct > MaxFrames then notice "Too many frames: increase MaxFrames" : exit sub
 
    PropertyTagFrameDelay = hexdec("5100")
    struct psize, n as long
    calldll #gdiplus, "GdipGetPropertyItemSize", image.p.struct as ulong, _
        PropertyTagFrameDelay as long, psize as struct, r as void
    if (psize.n.struct / 4) > MaxFrames then notice "Too many frames: increase MaxFrames" : exit sub
    struct PropItem(MaxFrames), delay as long
        
    calldll #gdiplus, "GdipGetPropertyItem", image.p.struct as ulong, _
        PropertyTagFrameDelay as long, psize.n.struct as long, _
        PropItem(0) as struct, r as void
 
    struct xs, n as long
    struct ys, n as long
    calldll #gdiplus, "GdipGetImageWidth", image.p.struct as ulong, xs as struct, r as void
    calldll #gdiplus, "GdipGetImageHeight", image.p.struct as ulong, ys as struct, r as void    
    xsize = xs.n.struct
    ysize = ys.n.struct 

    if xsize > WindowWidth  then ysize *= WindowWidth / xsize  : xsize = WindowWidth
    IF ysize > WindowHeight then xsize *= WindowHeight / ysize : ysize = WindowHeight
 
    xpos = (WindowWidth - xsize) / 2
    ypos = (WindowHeight - ysize) / 2

    endtime = time$("ms") + duration * 1000
    do
      for frame = 0 to frames.n.struct - 1 
        calldll #gdiplus, "GdipImageSelectActiveFrame", image.p.struct as ulong, _
            dimIDs(0) as struct, frame as long, r as void
        #w "cls"
        calldll #gdiplus, "GdipDrawImageRectI", graphics.p.struct as ulong, _
            image.p.struct as ulong, xpos as long, ypos as long, _
            xsize as long, ysize as long, r as void
        delay = PropItem(frame+4).delay.struct
        if delay = 0 then delay = 8
        timer delay * 10, [animate]
        wait
[animate]
        timer 0
        scan
      next frame
    loop until time$("ms") >= endtime
 
    calldll #gdiplus, "GdipDisposeImage", image.p.struct as ulong, r as void
    calldll #gdiplus, "GdipDeleteGraphics", graphics.p.struct as ulong, r as void
    calldll #gdiplus, "GdiplusShutdown", GDIP.l.struct as ulong, r as void
    close #gdiplus
end sub 

You can specify that the GIF animates just once, or repeats the animation for a specified period in seconds. Note that GIFs with a frame period less than about 10 milliseconds will animate more slowly than they should using this code, because of limitations of the Liberty BASIC timer (however such a fast animation is pointless because the eye/brain can't follow it).

Richard.

Re: Displaying animated GIFs with LBB?
Post by Rod on Oct 16th, 2016, 7:20pm

The standard ATL code has been proved to work for .gif files on a variety of systems. So the question is, is this a normal .gif file? or are there PC specific constraints that stop the gif from playing.

So it will be interesting to hear if Gordon's code runs for you.

Share the culprit .gif if not.
Re: Displaying animated GIFs with LBB?
Post by Richard Russell on Oct 17th, 2016, 10:27am

on Oct 16th, 2016, 7:20pm, Rod wrote:
The standard ATL code has been proved to work for .gif files on a variety of systems.

I would expect it to work, but the GDIPlus code is a lot more flexible. Here are some of the things it can do which the ATL control can't:
Richard.

Re: Displaying animated GIFs with LBB?
Post by Rod on Oct 17th, 2016, 12:23pm

Yes I see it is much more flexible, but the gif should display with the ATL code, so interested to so what Harbinger discovers.
Re: Displaying animated GIFs with LBB?
Post by Harbinger on Oct 23rd, 2016, 04:30am

OK now we're talkin'! It worked on most GIFs, but a few of them weren't displaying correctly. I will narrow down what the difference to see if I can find out why the problem is there...

Now in order to integrate this code into my own program, I need to know what everything does. Can you over-comment the same code, and explain what we're doing with each line (or at least the important ones)? You said we have a lot more flexibility, so i'd like to know what I can exploit to give the user more options.
For my purposes, for example, all GIFs will be DISPLAYED at 25 frames per second. But i'd like to be able to alert the user what the GIVEN framerate is, for the GIF in the preview. Can that be determined? (We'll go with the starting fps since that can be different within each frame.)

Also i'm trying to figure out a way to show the transparency color (index= 0, rgb= "000000") for my bitmaps by recoloring that hue in each frame. Any ideas for GIFs? None of the GIFs or bitmaps have an alpha channel...

Other than those questions, the thing worked like a charm, but I just need to know what does what... cool
Re: Displaying animated GIFs with LBB?
Post by Richard Russell on Oct 23rd, 2016, 09:38am

on Oct 23rd, 2016, 04:30am, Harbinger wrote:
But i'd like to be able to alert the user what the GIVEN framerate is, for the GIF in the preview. Can that be determined?

Yes. The GdipGetPropertyItem API call, with the PropertyTagFrameDelay parameter, retrieves the duration (in centiseconds) for each frame and stores them in the PropItem() array of structures:

Code:
    struct PropItem(MaxFrames), delay as long
        
    calldll #gdiplus, "GdipGetPropertyItem", image.p.struct as ulong, _
        PropertyTagFrameDelay as long, psize.n.struct as long, _
        PropItem(0) as struct, r as void 

There's a 16-byte header that has to be skipped so to find the duration of the first frame you can do:

Code:
duration = PropItem(4).delay.struct 

(my code makes the assumption that the array immediately follows the header, and that's not documented at MSDN so may be unreliable).

Quote:
We'll go with the starting fps since that can be different within each frame.

Generally that makes sense but be careful that there are some animated GIFs in which the first frame is a 'title' and is displayed for a much longer period than the rest. In that case the last frame would be a better guess at the 'normal' rate.

Quote:
Also i'm trying to figure out a way to show the transparency color (index= 0, rgb= "000000") for my bitmaps by recoloring that hue in each frame. Any ideas for GIFs?

That's easy. The GdipDrawImageRectI function doesn't draw transparent regions at all, which is why there is a CLS immediately preceding it in the code. If you replace that CLS with a FILL you can set the color of the 'transparent' regions to anything you like.

Richard.