Announcement

Collapse
No announcement yet.

Create an Animated GIF Using PowerBASIC

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Create an Animated GIF Using PowerBASIC

    Back in 2008, Frank Kestens made a post, asking if anyone knew how to create an animated GIF. At the time, no one responded. Like he did, I use a utility to create animated GIFs from the original image or from AVI files.

    I'd also like to do the same thing - create an animated GIF from a series of images (bmp's, jpg's, png's, ... any type of image source). With Jose's posted code, I can work with any format. So it's how to merge the images into the correct output format that's the question in hand.

    Has anyone done work on this, or know of any links that would be of help?

    Asking for the moon, I'd also like to do it from an AVI. But, I suspect I can do that by capturing images from the MS Animation Common Control. I've not used that before, but from reading the specs it looks plausible.

    I'll be off looking ...

  • #2
    FYI, I did find a free program called UnFREEz which will create animated GIFs from separate GIF files. The C++ source code is available here.

    My first look in the code didn't see enough comments for me to stop looking. . But I'm sure I'll give it another try.

    Comment


    • #3
      And, I couldn't help but laugh at this quote I found ...
      GIF animations are really pretty simple: a single file contains a series of GIF images, with information on when to switch to the next image, where to position it (subsequent images can be smaller than the first) and how to combine the new image with what's already there. Our next step is to create a series of individual image files, which we'll then combine into one big file.
      The devil is in the details!

      Isn't everything easy, when you know how to do it?

      Comment


      • #4
        A question from John Gleason renewed my interest in this thread.

        Before I get started on a solution, I thought I'd ask if anyone has since come up with code that would help in creating an animated GIF?

        I should be able to capture screen animation easily enough, to a series of BMPs - just basically some graphics work with a timer. I've posted screen capture stuff before. Plus, some writing directly on the screen to allow the user to select the capture area.

        And, I can convert the captured BMPs to GIFs with Jose's code.

        It's the merging of a series of GIFs to a single animated GIF file that I need to research.

        I could use the UnFreez program (see above) for that, but I'd rather do it myself.

        Comment


        • #5
          >>GIF animations are really pretty simple...
          >The devil is in the details!

          That may be; but I don't think you'd disagree a solid understanding of "the big picture" makes those details a bit less devilish to create.

          MCM
          Michael Mattias
          Tal Systems Inc. (retired)
          Racine WI USA
          [email protected]
          http://www.talsystems.com

          Comment


          • #6
            Hi MCM,
            Yep, having a good summary/overview always make it easier because as you view smaller pieces of the puzzle, it's easier to grasp where they fit in the big picture!

            In the past, I've read through descriptions of the file format. But reading for general interest is a lot different than reading for actual use! I'll find a time slot sometime this next week to work it out.

            Comment


            • #7
              Hi Gary.

              First you will need the GIF spec ...

              www.w3.org/Graphics/GIF/spec-gif89a.txt ‎
              or
              www.dca.fee.unicamp.br/~martino/disciplinas/.../spec-gif89a.pdf

              The GIF format is essentially a set of blocks in a specific order.
              Each of the separate gif's will have header, size, palette and extension blocks in a set order. extension blocks are comments and modifiers for the block(s) following. (one of which is a frame time setting for the next image.)

              You can't just append each separate gif to the end of the last.

              While you won't have to do any compressing or uncompressing, ...

              You will have to manufacture a new header and then extract the image
              and palette and applicable extensions from each separate gif image
              prepending a timing extension and add them to the header+global stuff
              in the display order. Then dont forget to add a final end of file record.

              Sounds complicated, but it's not too bad; the spec is very clear and all of
              the blocks have ID tags and a length so walking the source gifs is easy
              as is the decision to copy or ignore individual blocks (or extensions).

              With the spec in one hand, examine hex dumps of (a) a valid single file
              gif, and (b) a known valid animated gif (just a couple of frames).

              I have not acutally written code to merge gifs, so I may not have the details exactly right, but I have examined gifs against the spec.

              I hope that (a) this helps and (b) I haven't put you off. <g>

              Edit: A three frame gif attached (uses a transparent colour).

              Cheers, Mike
              Attached Files
              Last edited by Mike Simmonds; 1 Feb 2014, 12:21 PM. Reason: Added sample ani gif
              There are only two speeds for computers: fast enough, and too bloody slow.
              And there are 10 types of programmer -- those that know binary, and those that don't.

              Comment


              • #8
                Hey Mike!
                Thanks for the comments, and links. It will all come in handy when I get time to work the app. It will like be a filler app - fills in the time when I need a pause from the current app!

                Comment


                • #9
                  See previous post -- I added a small sample -- displays well within forum.
                  Mike.
                  There are only two speeds for computers: fast enough, and too bloody slow.
                  And there are 10 types of programmer -- those that know binary, and those that don't.

                  Comment


                  • #10
                    Hi Gary, for your delectation I spent a bit longer than I meant to in
                    writing a gif walker. This, along with the spec, should make it a bit easier to see the biger picture.

                    It displays most numbers in hex and is only realy any good for short files.

                    EDIT: To run, drop a gif file on the explorer icon. [A bit unusual perhaps, but I just
                    could not be bothered to add a file open dialog -- or indeed any windows at all!]

                    Code:
                    '==========================================================================='
                    ' GIFWALK.PBA            Copyright (c) AardSoft                    1st Feb 14    '
                    '==========================================================================='
                    
                    ' vim: set ts=4 sw=4:    ' [tabs:4]
                    
                    #Compile    Exe "GifWalk.exe"
                    #Compiler    PBWin 10 'PBCC 5
                    #Optimize    Speed
                    #Option        Version5        ' 2K/XP, NT4
                    #Stack        1 * 1024*1024    ' 1 MB stack
                    #Dim        All
                    
                    ''#include    "win32api.inc"
                    
                    '---------------------------------------------------------------------------'
                    ' Constants                                                                    '
                    '---------------------------------------------------------------------------'
                    
                    '---------------------------------------------------------------------------'
                    ' Globals                                                                    '
                    '---------------------------------------------------------------------------'
                    
                    Global fName, dName, rpt as string
                    Global fp as byte ptr, fSz as long
                    Global b() as byte
                    
                    '---------------------------------------------------------------------------'
                    ' PBMAIN                                                                    '
                    '---------------------------------------------------------------------------'
                    
                    Function PbMain() as Long
                        local done as long
                    
                        GetArgs
                        if LoadGifFile(fName, b(), fSz) then
                            msgBox "Open Failed",, "Poot!"
                            Function = 1: Exit Function
                        end if
                        fp = varPtr(b(0))
                    
                        rpt = "File: "+dName+", ("+Format$(fSz)+" bytes)"+$CrLf
                        if GifHeader() = 1 then            _
                            AddLine("Not a Gif File."):    _
                            msgBox rpt,, dName:            _
                            Exit Function
                        LSDesc
                    
                        do                                    ' walk through the rest of the file
                            select case @fp[0]                '
                            case &h21&:    done = DoExt()        '
                            case &h2C&:    done = DoImage()    '
                            case &h3B&:    done = 1: Addline("End of File")
                            case else:    done = 1: AddLine("Unexpected byte ("+Hex$(@fp[0])+") Abort!")
                            end select
                        loop until done                        '
                    
                        msgBox rpt,, dName
                    End Function
                    
                    '---------------------------------------------------------------------------'
                    ' DoImage                                                                    '
                    '---------------------------------------------------------------------------'
                    Function DoImage() as long
                        Local    s, t as string, x as byte, n as dword
                    
                        AddLine($CrLf+"Image (size 11 bytes+optional local colour table)")
                    
                        fp = fp+1                            ' skip image separator
                        s = "word      ....     Left"
                        Mid$(s, 10, 2) = Hex$(@fp[1], 2)
                        Mid$(s, 12, 2) = Hex$(@fp[0], 2): fp = fp+2
                        AddLine(s)
                        s = "word      ....     Top"
                        Mid$(s, 10, 2) = Hex$(@fp[1], 2)
                        Mid$(s, 12, 2) = Hex$(@fp[0], 2): fp = fp+2
                        AddLine(s)
                        s = "word      ....     Width"
                        Mid$(s, 10, 2) = Hex$(@fp[1], 2)
                        Mid$(s, 12, 2) = Hex$(@fp[0], 2): fp = fp+2
                        AddLine(s)
                        s = "word      ....     Height"
                        Mid$(s, 10, 2) = Hex$(@fp[1], 2)
                        Mid$(s, 12, 2) = Hex$(@fp[0], 2): fp = fp+2
                        AddLine(s)
                        s = "byte      ..       Flags": x = @fp[0]: t = DecodeIFlags(x)
                        Mid$(s, 10, 2) = Hex$(@fp[0], 2): fp = fp+1
                        AddLine(s)
                        AddLine(t)
                    
                        if (x and &h80&) then                                        _
                            n = 3*(2^((x and &h07&)+1)):                            _
                            AddLine("Local Colour Table ... "+Hex$(n)+" bytes"):    _
                            fp = fp+n
                    
                        fp = fp+1                            ' skip initial code size
                        n = SkipDataBlocks()                '
                        AddLine("Compressed Image Data ("+Hex$(n+1)+" bytes)")
                    
                    End Function
                    
                    '----------------------------------------------------------------------------
                    Function DecodeIFlags(x as byte) as string
                        Local s as string
                    
                        if (x and &h80&) then s = "    ": else s = "    No "
                        s = s+"Local Colour Table"
                        if (x and &h40&) then s = s+", ": else s = s+", Not "
                        s = s+"Interlaced"
                        if (x and &h20&) then s = s+", ": else s = s+", Not "
                        s = s+"Sorted"
                        s = s+", Size = "+Hex$(3*(2^((x and &h07&)+1)))
                    
                        Function = s
                    
                    End Function
                    
                    '---------------------------------------------------------------------------'
                    ' DoExt                                                                        '
                    '---------------------------------------------------------------------------'
                    
                    Function DoExt() as long
                        Local    s as string, t as byte, n as dword
                    
                        t  = @fp[1]                            ' get type
                        n  = @fp[2]+3                        ' get length
                        fp = fp+n                            ' skip
                    
                        select case as long t                '
                        case &h01&:    s ="Text Extension"        '
                        case &hF9&:    s ="Graphic Control Extension"
                        case &hFE&:    s ="Comment"            '
                        case &hFF&:    s ="Application Extension"
                        case else:    s ="Unknown Extension"    '
                        end select                            '
                    
                        n = n+SkipDataBlocks()                '
                        AddLine(s+" ("+Hex$(n)+" bytes)")    '
                    
                    End Function
                    
                    '---------------------------------------------------------------------------'
                    ' SkipDataBlocks                                                            '
                    '---------------------------------------------------------------------------'
                    
                    Function SkipDataBlocks() as dword
                        Local    x as byte, n as dword
                    
                        n = 0: do
                            x  = @fp[0]
                            n  = n+1+x
                            fp = fp+1+x
                        loop until x = 0
                    
                        Function = n
                    
                    End Function
                    
                    '---------------------------------------------------------------------------'
                    ' LogicalScreenDescriptor                                                    '
                    '---------------------------------------------------------------------------'
                    
                    Sub LSDesc()
                        Local    s, t as string, x as byte, n as dword
                    
                        AddLine($CrLf+"LogicalScreenDescriptor (size 7 bytes+optional colour table)")
                    
                        s = "word      ....     Logical screen width"
                        Mid$(s, 10, 2) = Hex$(@fp[1], 2)
                        Mid$(s, 12, 2) = Hex$(@fp[0], 2): fp = fp+2
                        AddLine(s)
                        s = "word      ....     Logical screen height"
                        Mid$(s, 10, 2) = Hex$(@fp[1], 2)
                        Mid$(s, 12, 2) = Hex$(@fp[0], 2): fp = fp+2
                        AddLine(s)
                        s = "byte      ..       Flags": x = @fp[0]: t = DecodeFlags(x)
                        Mid$(s, 10, 2) = Hex$(@fp[0], 2): fp = fp+1
                        AddLine(s)
                        AddLine(t)
                        s = "byte      ..       Background (transparent) colour index"
                        Mid$(s, 10, 2) = Hex$(@fp[0], 2): fp = fp+1
                        AddLine(s)
                        s = "byte      ..       Pixel aspect ratio"
                        Mid$(s, 10, 2) = Hex$(@fp[0], 2): fp = fp+1
                        AddLine(s)
                        if (x and &h80&) then                                        _
                            n = 3*(2^((x and &h07&)+1)):                            _
                            AddLine("Global Colour Table ... "+Hex$(n)+" bytes"):    _
                            fp = fp+n
                    
                    End Sub
                    
                    '----------------------------------------------------------------------------
                    Function DecodeFlags(x as byte) as string
                        Local s as string
                    
                        if (x and &h80&) then s = "    ": else s = "    No "
                        s = s+"Global Colour Table"
                        s = s+", Colour Resolution:"+Str$((x and &h70&)\&h10&+1)
                        if (x and &h08&) then s = s+", ": else s = s+", Not "
                        s = s+"Sorted"
                        s = s+", Size = "+Hex$(3*(2^((x and &h07&)+1)))
                    
                        Function = s
                    
                    End Function
                    
                    '---------------------------------------------------------------------------'
                    ' Header                                                                    '
                    '---------------------------------------------------------------------------'
                    
                    Function GifHeader() as long
                        Local    s as string, q as byte ptr
                    
                        if chr$(@fp[0], @fp[1], @fp[2]) <> "GIF" then    _
                            Function = 1: Exit Function
                    
                        AddLine("Header (fixed size 6 bytes)")
                        s = "byte[6]   ......     magic/version"
                        q = strPtr(s)
                        @q[10] = @fp[0]
                        @q[11] = @fp[1]
                        @q[12] = @fp[2]
                        @q[13] = @fp[3]
                        @q[14] = @fp[4]
                        @q[15] = @fp[5]: fp = fp+6
                        AddLine(s)
                    
                    End Function
                    
                    '---------------------------------------------------------------------------'
                    ' AddLine                                                                    '
                    '---------------------------------------------------------------------------'
                    
                    Sub AddLine(s as string)
                    
                        rpt = rpt+$CrLf+s
                    
                    End Sub
                    
                    '---------------------------------------------------------------------------'
                    ' GetArgs                                                                    '
                    '---------------------------------------------------------------------------'
                    
                    Sub GetArgs()
                        Local    p as long
                    
                        fName = Remove$(Command$(1), $DQ)
                        p = Instr(-1, fName, any "\/")+1    ' find last path sep and skip over
                        dName = Mid$(fName, p)                ' extract name plus ext
                    
                    End Sub
                    
                    '-----------------------------------------------------------------------'
                    ' LoadGifFile                                                            '
                    '-----------------------------------------------------------------------'
                    
                    Function LoadGifFile(fName as string, b() as byte, sz as long) as long
                    
                        local    fd as long
                    
                    ''    if IsFile(fName) = 0 then Function = 1: Exit Function    ' not found
                    
                        fd = FreeFile: err = 0
                        Open fName for binary access read lock write as fd base = 0 chr = Ansi
                        Function = err: if err then Exit Function    '' oops
                    
                        sz = Lof(fd): ReDim b(0 to sz-1)
                        get fd,, b() records sz
                        Close fd
                    
                    End Function
                    
                    '==========================================================================='
                    
                    
                    zip attached. (src, exe, spec, sample image, screen shot)

                    Cheers, Mike
                    Attached Files
                    Last edited by Mike Simmonds; 2 Feb 2014, 03:00 AM.
                    There are only two speeds for computers: fast enough, and too bloody slow.
                    And there are 10 types of programmer -- those that know binary, and those that don't.

                    Comment


                    • #11
                      Mike!
                      Thanks so much for the code. I wasn't going to work on the animated GIF maker any time soon, but now you've tempted me into it!

                      Comment


                      • #12
                        Howdy Mike!
                        In Feb I said that animated GIF utility was not on my short list, but with the release of gbSnapShot that might change.

                        gbSnapShot can be easily modified to capture a series of images, which would be used to create an animated GIF.

                        I'll go take a look at the GIF spec and the code you posted to begin to get a better feel for what needs to be done.

                        Thanks again for the code you posted. It will help immensely with what I want to do.

                        Comment


                        • #13
                          Mike,
                          (using you to talk to so it won't look like I'm talking to myself)

                          The source code from a program called UnFREEz is listed below (.ccp). I've use the utility before and it works just fine, although it has very few feature. You just drop a list of GIFs on it, set a delay time, and press Make. Very simple but very effective if you have the list of GIF images.

                          Which, of course, gbSnapShot can provide! I've updated my copy to include a "Burst" mode - where you can have it generate a sequence of images - user-defined image count and delay between the images. I'll release that online shortly.

                          The UnFREEz code listing is very encouraing in that it's very short - just 350 lines of total code and barely ~100 lines of code for creating the GIF. After I look a bit more at the GIF file format, I'll see if I can manage a conversion of the cpp listing into PowerBASIC.

                          Code:
                          #include <windows.h>
                          #include <commctrl.h>
                          #include <stdio.h>
                          #include "resource.h"
                          
                          #pragma pack(push,gifpacking,1)
                          
                          typedef struct {
                              char cSignature[3]; // Must be 'GIF'
                              char cVersion[3];   // Must be '89a'
                          } GIF_HEADER;
                          
                          typedef struct { // 7 bytes
                              unsigned short iWidth;
                              unsigned short iHeight;
                              unsigned char iSizeOfGct : 3;
                              unsigned char iSortFlag : 1;
                              unsigned char iColorResolution : 3;
                              unsigned char iGctFlag : 1;
                              unsigned char iBackgroundColorIndex;
                              unsigned char iPixelAspectRatio;
                          } GIF_LOGICAL_SCREEN_DESCRIPTOR;
                          
                          typedef struct { // 6 bytes
                              unsigned char iBlockSize;           // Must be 4
                              unsigned char iTransparentColorFlag : 1;
                              unsigned char iUserInputFlag : 1;
                              unsigned char iDisposalMethod : 3;
                              unsigned char iReserved : 3;
                              unsigned short iDelayTime;
                              unsigned char iTransparentColorIndex;
                              unsigned char iBlockTerminator;     // Must be 0
                          } GIF_GRAPHIC_CONTROL_EXTENSION;
                          
                          typedef struct { // 9 bytes
                              unsigned short iLeft;
                              unsigned short iTop;
                              unsigned short iWidth;
                              unsigned short iHeight;
                              unsigned char iSizeOfLct : 3;
                              unsigned char iReserved : 2;
                              unsigned char iSortFlag : 1;
                              unsigned char iInterlaceFlag : 1;
                              unsigned char iLctFlag : 1;
                          } GIF_IMAGE_DESCRIPTOR;
                          
                          #pragma pack(pop,gifpacking)
                          
                          unsigned short iGctSize[]={6,12,24,48,96,192,384,768};
                          
                          LRESULT WINAPI MainDlgProc(HWND,UINT,WPARAM,LPARAM);
                          void DropFiles(HDROP);
                          void UpdateFrameInfo(void);
                          void MakeGIF(void);
                          
                          HINSTANCE hInst;
                          HANDLE hHeap;
                          HICON hIconMain;
                          HWND hMainDlg;
                          
                          int WINAPI WinMain(HINSTANCE hInstance,
                                                               HINSTANCE hPrevInstance,
                                                               LPSTR lpszCmdLine,
                                                               int nShowCmd)
                          {
                              INITCOMMONCONTROLSEX icce;
                              icce.dwSize=sizeof(icce);
                              icce.dwICC=ICC_UPDOWN_CLASS;
                              InitCommonControlsEx(&icce);
                              hInst=hInstance;
                              hHeap=GetProcessHeap();
                              hIconMain=LoadIcon(hInstance,MAKEINTRESOURCE(IDI_MAIN));
                              DialogBox(hInstance,MAKEINTRESOURCE(IDD_MAIN),NULL,(DLGPROC)MainDlgProc);
                              return FALSE;
                          }
                          
                          LRESULT CALLBACK MainDlgProc(HWND hWnd,
                                                                                   UINT uMsg,
                                                                                   WPARAM wParam,
                                                                                   LPARAM lParam)
                          {
                              DWORD dw;
                          
                              switch (uMsg) {
                                  case WM_INITDIALOG:
                                      hMainDlg=hWnd;
                                      SendMessage(hWnd,WM_SETICON,ICON_BIG,(LPARAM)hIconMain);
                                      CheckDlgButton(hWnd,IDC_CHECK_MAIN_LOOP,1);
                                      SendDlgItemMessage(hWnd,IDC_SPIN_MAIN_DELAY,UDM_SETRANGE,0,MAKELONG(1000,0));
                                      SendDlgItemMessage(hWnd,IDC_BUTTON_MAIN_WEBSITE,BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIconMain);
                                      DragAcceptFiles(hWnd,TRUE);
                                      return TRUE;
                                  case WM_CLOSE:
                                      DragAcceptFiles(hWnd,FALSE);
                                      SendMessage(hWnd,WM_COMMAND,MAKELONG(IDC_BUTTON_MAIN_CLEARFRAMES,BN_CLICKED),0);
                                      EndDialog(hWnd,IDCANCEL);
                                      PostQuitMessage(0);
                                      return TRUE;
                                  case WM_COMMAND:
                                      switch (HIWORD(wParam)) {
                                          case BN_CLICKED:
                                              switch (LOWORD(wParam)) {
                                                  case IDC_BUTTON_MAIN_REMOVEFRAME:
                                                      dw=SendDlgItemMessage(hWnd,IDC_LIST_MAIN_FRAMES,LB_GETCURSEL,0,0);
                                                      if (dw!=-1) {
                                                          HeapFree(hHeap,0,(LPVOID)SendDlgItemMessage(hWnd,IDC_LIST_MAIN_FRAMES,LB_GETITEMDATA,dw,0));
                                                          SendDlgItemMessage(hWnd,IDC_LIST_MAIN_FRAMES,LB_DELETESTRING,dw,0);
                                                      }
                                                      if (!SendDlgItemMessage(hWnd,IDC_LIST_MAIN_FRAMES,LB_GETCOUNT,0,0)) {
                                                          EnableWindow(GetDlgItem(hWnd,IDC_BUTTON_MAIN_MAKEGIF),FALSE);
                                                      }
                                                      return TRUE;
                                                  case IDC_BUTTON_MAIN_CLEARFRAMES:
                                                      while (SendDlgItemMessage(hWnd,IDC_LIST_MAIN_FRAMES,LB_GETCOUNT,0,0)) {
                                                          HeapFree(hHeap,0,(LPVOID)SendDlgItemMessage(hWnd,IDC_LIST_MAIN_FRAMES,LB_GETITEMDATA,0,0));
                                                          SendDlgItemMessage(hWnd,IDC_LIST_MAIN_FRAMES,LB_DELETESTRING,0,0);
                                                      }
                                                      SetDlgItemText(hWnd,IDC_STATIC_MAIN_FILENAME,"");
                                                      SetDlgItemText(hWnd,IDC_STATIC_MAIN_INFO,"");
                                                      EnableWindow(GetDlgItem(hWnd,IDC_BUTTON_MAIN_MAKEGIF),FALSE);
                                                      return TRUE;
                                                  case IDC_BUTTON_MAIN_MAKEGIF:
                                                      MakeGIF();
                                                      return TRUE;
                                                  case IDC_BUTTON_MAIN_WEBSITE:
                                                      if ((long)ShellExecute(hWnd,NULL,"http://www.whitsoftdev.com/",NULL,NULL,SW_NORMAL)<=32)
                                                          MessageBox(hWnd,"Error opening web browser. You may manually surf to http://www.whitsoftdev.com/.",NULL,MB_ICONEXCLAMATION);
                                                      return TRUE;
                                                  default:
                                                      return FALSE;
                                              }
                                          case LBN_SELCHANGE:
                                              switch (LOWORD(wParam)) {
                                                  case IDC_LIST_MAIN_FRAMES:
                                                      UpdateFrameInfo();
                                                      return TRUE;
                                                  default:
                                                      return FALSE;
                                              }
                                          default:
                                              return FALSE;
                                      }
                                  case WM_DROPFILES:
                                      DropFiles((HDROP)wParam);
                                      return TRUE;
                                  default:
                                      return FALSE;
                              }
                          }
                          
                          void DropFiles(HDROP hDrop)
                          {
                              DWORD dw, dwTotal, dwLength;
                              char *psz;
                          
                              dwTotal=DragQueryFile(hDrop,-1,0,0);
                              for (dw=0;dw<dwTotal;dw++) {
                                  dwLength=DragQueryFile(hDrop,dw,0,0);
                                  psz=(char *)HeapAlloc(hHeap,0,dwLength+1);
                                  DragQueryFile(hDrop,dw,psz,dwLength+1);
                                  SendDlgItemMessage(hMainDlg,IDC_LIST_MAIN_FRAMES,LB_SETITEMDATA,SendDlgItemMessage(hMainDlg,IDC_LIST_MAIN_FRAMES,LB_ADDSTRING,0,(LPARAM)strrchr(psz,'\\')+1),(LPARAM)psz);
                              }
                              DragFinish(hDrop);
                              EnableWindow(GetDlgItem(hMainDlg,IDC_BUTTON_MAIN_MAKEGIF),TRUE);
                          }
                          
                          void UpdateFrameInfo()
                          {
                              DWORD dwIndex, dw;
                              char sz[256], c;
                              char *pszFileName;
                              HANDLE hFile;
                              GIF_HEADER gh;
                              GIF_LOGICAL_SCREEN_DESCRIPTOR glsd;
                              GIF_IMAGE_DESCRIPTOR gid;
                              ZeroMemory(&gh,sizeof(GIF_HEADER));
                              ZeroMemory(&glsd,sizeof(GIF_LOGICAL_SCREEN_DESCRIPTOR));
                              ZeroMemory(&gid,sizeof(GIF_IMAGE_DESCRIPTOR));
                              dwIndex=SendDlgItemMessage(hMainDlg,IDC_LIST_MAIN_FRAMES,LB_GETCURSEL,0,0);
                              SendDlgItemMessage(hMainDlg,IDC_LIST_MAIN_FRAMES,LB_GETTEXT,dwIndex,(LPARAM)sz);
                              pszFileName=(char *)SendDlgItemMessage(hMainDlg,IDC_LIST_MAIN_FRAMES,LB_GETITEMDATA,dwIndex,0);
                              SetDlgItemText(hMainDlg,IDC_STATIC_MAIN_FILENAME,sz);
                              hFile=CreateFile(pszFileName,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,0);
                              if (hFile==INVALID_HANDLE_VALUE) {
                                  MessageBox(hMainDlg,"Could not open file.",0,MB_ICONEXCLAMATION);
                                  return;
                              }
                              ReadFile(hFile,&gh,sizeof(GIF_HEADER),&dw,0);
                              if (strncmp(gh.cSignature,"GIF",3) || (strncmp(gh.cVersion,"89a",3) && strncmp(gh.cVersion,"87a",3))) {
                                  MessageBox(hMainDlg,"Not a GIF file, or incorrect version number.",0,MB_ICONEXCLAMATION);
                                  CloseHandle(hFile);
                                  return;
                              }
                              ReadFile(hFile,&glsd,sizeof(GIF_LOGICAL_SCREEN_DESCRIPTOR),&dw,0);
                              if (glsd.iGctFlag) SetFilePointer(hFile,iGctSize[glsd.iSizeOfGct],0,FILE_CURRENT);
                              for (;;) {
                                  ReadFile(hFile,&c,1,&dw,0);
                                  if (dw==0) {
                                      MessageBox(hMainDlg,"Premature end of file encountered; no GIF image data present.",0,MB_ICONEXCLAMATION);
                                      CloseHandle(hFile);
                                      return;
                                  }
                                  if (c==0x2C) {
                                      ReadFile(hFile,&gid,sizeof(GIF_IMAGE_DESCRIPTOR),&dw,0);
                                      break;
                                  } else if (c==0x21) {
                                      ReadFile(hFile,&c,1,&dw,0);
                                      if (c==0xF9) {
                                          SetFilePointer(hFile,sizeof(GIF_GRAPHIC_CONTROL_EXTENSION),0,FILE_CURRENT);
                                      } else {
                                          for (;;) {
                                              ReadFile(hFile,&c,1,&dw,0);
                                              if (!c) break;
                                              SetFilePointer(hFile,c,0,FILE_CURRENT);
                                          }
                                      }
                                  }
                              }
                              CloseHandle(hFile);
                              sprintf(sz,"%d x %d\n%s (%d bpp)",glsd.iWidth,glsd.iHeight,gid.iLctFlag?"local":(glsd.iGctFlag?"global":"none"),gid.iLctFlag?(gid.iSizeOfLct+1):(glsd.iSizeOfGct+1));
                              SetDlgItemText(hMainDlg,IDC_STATIC_MAIN_INFO,sz);
                          }
                          
                          void MakeGIF()
                          {
                              OPENFILENAME ofn;
                              char szFileName[MAX_PATH];
                              DWORD dwIndex, dw;
                              char szColorTable[768];
                              unsigned char c;
                              HANDLE hFileOut, hFileIn;
                              GIF_HEADER gh;
                              GIF_LOGICAL_SCREEN_DESCRIPTOR glsd1, glsd;
                              GIF_GRAPHIC_CONTROL_EXTENSION ggce;
                              GIF_IMAGE_DESCRIPTOR gid;
                              ZeroMemory(&glsd1,sizeof(GIF_LOGICAL_SCREEN_DESCRIPTOR));
                              szFileName[0]=0;
                              ofn.lStructSize=sizeof(ofn);
                              ofn.hwndOwner=hMainDlg;
                              ofn.hInstance=hInst;
                              ofn.lpstrFilter="GIF Files (*.gif)\0*.gif\0";
                              ofn.lpstrCustomFilter=NULL;
                              ofn.nMaxCustFilter=0;
                              ofn.nFilterIndex=1;
                              ofn.lpstrFile=szFileName;
                              ofn.nMaxFile=MAX_PATH;
                              ofn.lpstrFileTitle=NULL;
                              ofn.nMaxFileTitle=0;
                              ofn.lpstrInitialDir=NULL;
                              ofn.lpstrTitle="Make Animated GIF";
                              ofn.Flags=OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_PATHMUSTEXIST;
                              ofn.nFileOffset=NULL;
                              ofn.nFileExtension=NULL;
                              ofn.lpstrDefExt="gif";
                              ofn.lCustData=NULL;
                              ofn.lpfnHook=NULL;
                              ofn.lpTemplateName=NULL;
                              if (GetSaveFileName(&ofn)) {
                                  hFileOut=CreateFile(szFileName,GENERIC_WRITE,FILE_SHARE_READ,0,CREATE_ALWAYS,0,0);
                                  strncpy((char *)&gh,"GIF89a",6);
                                  WriteFile(hFileOut,&gh,sizeof(GIF_HEADER),&dw,0);
                                  WriteFile(hFileOut,&glsd1,sizeof(GIF_LOGICAL_SCREEN_DESCRIPTOR),&dw,0);
                                  if (IsDlgButtonChecked(hMainDlg,IDC_CHECK_MAIN_LOOP)) {
                                      WriteFile(hFileOut,"\41\377\013NETSCAPE2.0\003\001\377\377\0",19,&dw,0);
                                  }
                                  for (dwIndex=0;dwIndex<(unsigned)SendDlgItemMessage(hMainDlg,IDC_LIST_MAIN_FRAMES,LB_GETCOUNT,0,0);dwIndex++) {
                                      ZeroMemory(&glsd,sizeof(GIF_LOGICAL_SCREEN_DESCRIPTOR));
                                      ZeroMemory(&ggce,sizeof(GIF_GRAPHIC_CONTROL_EXTENSION));
                                      ZeroMemory(&gid,sizeof(GIF_IMAGE_DESCRIPTOR));
                                      hFileIn=CreateFile((char *)SendDlgItemMessage(hMainDlg,IDC_LIST_MAIN_FRAMES,LB_GETITEMDATA,dwIndex,0),GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,0);
                                      if (hFileIn==INVALID_HANDLE_VALUE) {
                                          MessageBox(hMainDlg,"Could not open file. GIF creation aborted.",0,0);
                                          CloseHandle(hFileOut);
                                          return;
                                      }
                                      ReadFile(hFileIn,&gh,sizeof(GIF_HEADER),&dw,0);
                                      if (strncmp(gh.cSignature,"GIF",3) || (strncmp(gh.cVersion,"89a",3) && strncmp(gh.cVersion,"87a",3))) {
                                          MessageBox(hMainDlg,"Not a GIF file, or incorrect version number.",0,MB_ICONEXCLAMATION);
                                          CloseHandle(hFileIn);
                                          CloseHandle(hFileOut);
                                          return;
                                      }
                                      ReadFile(hFileIn,&glsd,sizeof(GIF_LOGICAL_SCREEN_DESCRIPTOR),&dw,0);
                                      if (glsd.iGctFlag) ReadFile(hFileIn,szColorTable,iGctSize[glsd.iSizeOfGct],&dw,0);
                                      if (glsd1.iWidth<glsd.iWidth) glsd1.iWidth=glsd.iWidth;
                                      if (glsd1.iHeight<glsd.iHeight) glsd1.iHeight=glsd.iHeight;
                                      for (;;) {
                                          ReadFile(hFileIn,&c,1,&dw,0);
                                          if (dw==0) {
                                              MessageBox(hMainDlg,"Premature end of file encountered; no GIF image data present.",0,MB_ICONEXCLAMATION);
                                              CloseHandle(hFileIn);
                                              CloseHandle(hFileOut);
                                              return;
                                          }
                                          if (c==0x2C) {
                                              ReadFile(hFileIn,&gid,sizeof(GIF_IMAGE_DESCRIPTOR),&dw,0);
                                              if (gid.iLctFlag) {
                                                  ReadFile(hFileIn,szColorTable,iGctSize[gid.iSizeOfLct],&dw,0);
                                              } else {
                                                  gid.iLctFlag=1;
                                                  gid.iSizeOfLct=glsd.iSizeOfGct;
                                              }
                                              break;
                                          } else if (c==0x21) {
                                              ReadFile(hFileIn,&c,1,&dw,0);
                                              if (c==0xF9) {
                                                  ReadFile(hFileIn,&ggce,sizeof(GIF_GRAPHIC_CONTROL_EXTENSION),&dw,0);
                                              } else {
                                                  for (;;) {
                                                      ReadFile(hFileIn,&c,1,&dw,0);
                                                      if (!c) break;
                                                      SetFilePointer(hFileIn,c,0,FILE_CURRENT);
                                                  }
                                              }
                                          }
                                      }
                                      ggce.iBlockSize=4;
                                      ggce.iDelayTime=(unsigned short)GetDlgItemInt(hMainDlg,IDC_EDIT_MAIN_DELAY,0,0);
                                      ggce.iDisposalMethod=2;
                                      c=(char)0x21;
                                      WriteFile(hFileOut,&c,1,&dw,0);
                                      c=(char)0xF9;
                                      WriteFile(hFileOut,&c,1,&dw,0);
                                      WriteFile(hFileOut,&ggce,sizeof(GIF_GRAPHIC_CONTROL_EXTENSION),&dw,0);
                                      c=(char)0x2C;
                                      WriteFile(hFileOut,&c,1,&dw,0);
                                      WriteFile(hFileOut,&gid,sizeof(GIF_IMAGE_DESCRIPTOR),&dw,0);
                                      WriteFile(hFileOut,szColorTable,iGctSize[gid.iSizeOfLct],&dw,0);
                                      ReadFile(hFileIn,&c,1,&dw,0);
                                      WriteFile(hFileOut,&c,1,&dw,0);
                                      for (;;) {
                                          ReadFile(hFileIn,&c,1,&dw,0);
                                          WriteFile(hFileOut,&c,1,&dw,0);
                                          if (!c) break;
                                          ReadFile(hFileIn,szColorTable,c,&dw,0);
                                          WriteFile(hFileOut,szColorTable,c,&dw,0);
                                      }
                                      CloseHandle(hFileIn);
                                  }
                                  c=(char)0x3B;
                                  WriteFile(hFileOut,&c,1,&dw,0);
                                  SetFilePointer(hFileOut,6,0,FILE_BEGIN);
                                  WriteFile(hFileOut,&glsd1,sizeof(GIF_LOGICAL_SCREEN_DESCRIPTOR),&dw,0);
                                  CloseHandle(hFileOut);
                              }
                          }
                          Last edited by Gary Beene; 16 Mar 2014, 05:15 AM.

                          Comment


                          • #14
                            Mike,
                            I also found two very nice tutorials:
                            What's in a GIF - Bit by Byte
                            What's in a GIF - Animation and Transparency
                            Last edited by Gary Beene; 17 Mar 2014, 10:02 PM.

                            Comment


                            • #15
                              Bummer, Mike,
                              I did some study on the GIF and animation specs, but didn't get far enough tonight to try some code. I'm likely to crash shortly but will try again tomorrow.

                              Comment


                              • #16
                                HaHaHaHaHaHaHaHaHaHaHaHaHaHaHaHaHaHa! (code for laughter given below). :laugh:

                                I couldn't stand not writing any code whatsoever on this for the last several days.

                                Code:
                                'Compilable Example:
                                #Compiler PBWin 10
                                #Compile Exe
                                #Dim All
                                %Unicode = 1
                                #Include "Win32API.inc"
                                
                                Enum Equates Singular
                                   IDC_Button
                                End Enum
                                
                                Global hDlg As Dword
                                Global FileName As WStringZ * %Max_Path
                                
                                Function PBMain() As Long
                                   Dialog New Pixels, 0, "gbAniGIF",300,300,200,200, %WS_OverlappedWindow To hDlg
                                   Control Add Button, hDlg, %IDC_Button,"Make Ani-GIF", 50,10,100,20
                                   Dialog Show Modal hDlg Call DlgProc
                                End Function
                                
                                CallBack Function DlgProc() As Long
                                   Select Case Cb.Msg
                                      Case %WM_Command
                                         Select Case Cb.Ctl
                                            Case %IDC_Button
                                               MakeGIF
                                         End Select
                                   End Select
                                End Function
                                
                                Sub MakeGIF
                                   Local temp$, i,j As Long
                                   FileName = "anigif.gif"
                                   Open FileName For Binary As #1
                                   'header
                                   temp$ = "GIF89a"
                                   Put #1,,temp$
                                   'trailer
                                   i = &H3B
                                   Put #1,,i
                                   SetEof #1
                                   Close #1
                                End Sub
                                For those who don't get the joke, the code really is the correct header/trailer for a GIF file, but with nothing in between! Not exactly an accomplishment, but I did write some code! I feel better now.

                                Comment


                                • #17
                                  Well, this is going to be one of those talk to myself kind of threads - trying to talk myself through the generation of the ani-GIF output file.

                                  This latest code has 3 parts Header + Logical Screen Descriptor + Trailer. It's hard coded for a 10x10 GIF size and assume that there is a Global Color Table.

                                  The new part is the Logical Screen Descriptor, made up like this (table from here):



                                  In an animated GIF, I think the Global Color Table can be removed, as long as the color table for each individual image is retained. I hope so, because I'm not at all sure how to merge individual image color tables into a Global color table (that is, unless between all images there is less than 255 colors, in which case I can see how to do that).

                                  Latest code:
                                  Code:
                                  'Compilable Example:
                                  #Compiler PBWin 10
                                  #Compile Exe
                                  #Dim All
                                  %Unicode = 1
                                  #Include "Win32API.inc"
                                  
                                  Enum Equates Singular
                                     IDC_Button
                                  End Enum
                                  
                                  Global hDlg As Dword
                                  Global OutputFileName As WStringZ * %Max_Path
                                  
                                  Function PBMain() As Long
                                     Dialog New Pixels, 0, "gbAniGIF",300,300,200,200, %WS_OverlappedWindow To hDlg
                                     Control Add Button, hDlg, %IDC_Button,"Make Ani-GIF", 50,10,100,20
                                     Dialog Show Modal hDlg Call DlgProc
                                  End Function
                                  
                                  CallBack Function DlgProc() As Long
                                     Select Case Cb.Msg
                                        Case %WM_Command
                                           Select Case Cb.Ctl
                                              Case %IDC_Button
                                                 MakeGIF
                                           End Select
                                     End Select
                                  End Function
                                  
                                  Sub MakeGIF
                                     Local temp$
                                     Local w,h,Trailer As Word
                                     Local Pack, PAR As Byte
                                     FileName = "anigif.gif"
                                  
                                     'image size (will get from input GIF files)
                                     w = 10 : h = 10
                                  
                                     Open OutputFileName For Binary As #1
                                     'header = 6 bytes
                                     temp$ = "GIF89a"
                                     Put #1,,temp$
                                  
                                     'logical screen descriptor = 7 bytes
                                     Put #1,, w        'width  2 bytes. must be unsigned so As Word
                                     Put #1,, h        'height 2 bytes. must be unsigned so As Word. must be little-endian
                                     Bit Set Pack, 7   'Global Color Table Flag
                                     Bit Reset Pack, 6 'Color Resolution
                                     Bit Reset Pack, 5 'Color Resolution
                                     Bit Set   Pack, 4 'Color Resolution
                                     Bit Reset Pack, 3 'Sort Flag. can help image decode but is not required
                                     Bit Reset Pack, 2 'size of Global Color Table
                                     Bit Reset Pack, 1 'size of Global Color Table
                                     Bit Set Pack, 0   'size of Global Color Table       s = 2^(N+1)  where N = bits 567
                                     Put #1,, Pack     'packed bits
                                     Put #1,, BGIndex  'which color index to use for image colors not found in color table
                                     Put #1,, PAR      'pixel aspect ratio. always use 0. not sure why
                                  
                                     'trailer
                                     Trailer = &H3B
                                     Put #1,,Trailer
                                     SetEof #1
                                     Close #1
                                  End Sub
                                  Last edited by Gary Beene; 18 Mar 2014, 09:32 AM. Reason: added missing BG index byte to LSD

                                  Comment


                                  • #18
                                    I found this ...
                                    GIFs can have either a global color table or local color tables for each sub-image
                                    .. which greatly simplifies the LSD code, to this ...
                                    Code:
                                       Put #1,, w        'width  2 bytes. unsigned Word
                                       Put #1,, h           'height 2 bytes. unsigned Word
                                       Put #1,, Pack      'packed bits. value 0 when no Global Color Table
                                       Put #1,, BGIndex  'which color index to use for image colors not found in color table. (zero since no Global Color Table) 
                                       Put #1,, PAR       'pixel aspect ratio. always use 0. not sure why
                                    Last edited by Gary Beene; 18 Mar 2014, 09:33 AM.

                                    Comment


                                    • #19
                                      The output animated GIF file will start with these three blocks of data:
                                      Header - Logical Screen Descriptor - Application Extension
                                      Then, for each image, there will be these four blocks:
                                      Graphic Control Extension - Image Description - Local Color Table - Image Data
                                      This next round of code adds in the Application Extension block. It contains the loop counter, which is the only thing that changes from GIF to GIF.

                                      Code:
                                      'Compilable Example:
                                      #Compiler PBWin 10
                                      #Compile Exe
                                      #Dim All
                                      %Unicode = 1
                                      #Include "Win32API.inc"
                                      
                                      Enum Equates Singular
                                         IDC_Button
                                      End Enum
                                      
                                      Global hDlg As Dword
                                      Global OutputFileName As WStringZ * %Max_Path
                                      
                                      Function PBMain() As Long
                                         Dialog New Pixels, 0, "gbAniGIF",300,300,200,200, %WS_OverlappedWindow To hDlg
                                         Control Add Button, hDlg, %IDC_Button,"Make Ani-GIF", 50,10,100,20
                                         Dialog Show Modal hDlg Call DlgProc
                                      End Function
                                      
                                      CallBack Function DlgProc() As Long
                                         Select Case Cb.Msg
                                            Case %WM_Command
                                               Select Case Cb.Ctl
                                                  Case %IDC_Button
                                                     MakeGIF
                                               End Select
                                         End Select
                                      End Function
                                      
                                      Sub MakeGIF
                                         Local temp$
                                         Local i,w,h,GlobalColorTable As Word
                                         Local b,Pack, PAR, BGIndex, Trailer As Byte
                                         OutputFileName = "anigif.gif"
                                      
                                         'image size (will get from input GIF files)
                                         w = 10 : h = 10
                                      
                                         Open OutputFileName For Binary As #1
                                         'header = 6 bytes
                                         temp$ = "GIF89a"
                                         Put #1,,temp$
                                      
                                         'logical screen descriptor = 7 bytes
                                         Put #1,, w        'width  2 bytes. must be unsigned so As Word
                                         Put #1,, h        'height 2 bytes. must be unsigned so As Word. must be little-endian
                                         If GlobalColorTable Then
                                            Bit Set Pack, 7   'Global Color Table Flag
                                            Bit Reset Pack, 6 'Color Resolution
                                            Bit Reset Pack, 5 'Color Resolution
                                            Bit Set   Pack, 4 'Color Resolution
                                            Bit Reset Pack, 3 'Sort Flag. can help image decode but is not required
                                            Bit Reset Pack, 2 'size of Global Color Table
                                            Bit Reset Pack, 1 'size of Global Color Table
                                            Bit Set Pack, 0   'size of Global Color Table       s = 2^(N+1)  where N = bits 567
                                         End If
                                         Put #1,, Pack     'packed bits
                                         Put #1,, BGIndex  'which color index to use for image colors not found in color table
                                         Put #1,, PAR      'pixel aspect ratio. always use 0. not sure why
                                      
                                         'application extension = 19 byte
                                         b =  33 : Put#1,, b  'GIF Extension code
                                         b = 255 : Put#1,, b  'Application Extension Label
                                         b =  11 : Put#1,, b  'Length of Application Block (11 bytes to follow)
                                         temp$ = "NETSCAPE2.0" : Put #1,, temp$
                                         b =   3 : Put#1,, b  'Length of Data Sub-Block (three bytes of data to follow)
                                         b =   1 : Put#1,, b  '
                                         i =   0 : Put#1,, i  'number of times to loop. unsigned. 0 = forever
                                         b =   0 : Put #1,,b  'Data Sub-Block Terminator.
                                      
                                         'trailer = 1 byte
                                         Trailer = &H3B
                                         Put #1,,Trailer
                                         SetEof #1
                                         Close #1
                                      End Sub
                                      Also, I corrected an error in the earlier code. Trailer should have been declared a Byte, not a Word

                                      Comment


                                      • #20
                                        Hi Garry.

                                        Here is a suggestion for a merge of multilpe single image gifs (such as your capture utility will make) to a single animated gif.

                                        Lots of detail left for the reader.

                                        I would also have to assume that all the 'stills' are the same size.

                                        Code:
                                        1.  Read first image -- see my gifwalk and the specs.
                                             Start your output file -- write Gif Header
                                            Copy Logical Screen Desciptor from img1
                                            Create and write App Extension
                                        
                                        2. Create a Graphic Extension with the current frame delay
                                            Write out GfxExt
                                            Read Global Colour Table from current image
                                            Convert format to Local Colour Table -- write it to output
                                        
                                           Might Check if current image has a local table, but I guess most won't.
                                           If found, use direct instead ov format conversion.
                                        
                                        3.  Copy the image data from current image to the output file unchanged,
                                             including all the sub blocks. Again My GifWalk may have code to crib.
                                        
                                        4. If more images to do ...
                                               open next gif
                                               skip header, logical screen desc, etc. till at global colour table.
                                               goto step 2.
                                            Else ...
                                        
                                        5. Write the trailer.
                                            Close output -- you are done.
                                        That's the stragey I'd use.

                                        Cheers, Mike.
                                        Last edited by Mike Simmonds; 18 Mar 2014, 11:21 AM.
                                        There are only two speeds for computers: fast enough, and too bloody slow.
                                        And there are 10 types of programmer -- those that know binary, and those that don't.

                                        Comment

                                        Working...
                                        X