Accessing GIF Frames and Displaying GIF Images/Animations

External libraries (GTK, GSL, SDL, Allegro, OpenGL, etc) questions.
paul doe
Posts: 1068
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Accessing GIF Frames and Displaying GIF Images/Animations

Postby paul doe » Jan 23, 2020 13:49

Bolee wrote:I was wondering if an extra feature could be added to save each frame as a separate gif file?

I have some vb6 source that can do it, but was wondering if it could be done with FreeBasic?

Well, it would be a simple matter of porting the code. So, you could either post the code here (if it's not too long), or post a link to the source (using GitHub or some other source sharing site).
UEZ
Posts: 377
Joined: May 05, 2017 19:59
Location: Germany

Re: Accessing GIF Frames and Displaying GIF Images/Animations

Postby UEZ » Jan 23, 2020 23:04

Here the modified code from my previous post.

Code: Select all

'Coded by UEZ build 2020-01-24

#Ifdef __Fb_64bit__
    #Inclib "gdiplus"
    #Include "win/gdiplus-c.bi"
#Else
    #Include "win/gdiplus.bi"
    Using gdiplus
#Endif

#Include "fbgfx.bi"

Using FB

Dim Shared gdipToken As ULONG_PTR
Dim Shared GDIp As GdiplusStartupInput

Function _GDIPlus_Startup() As Bool
   GDIp.GdiplusVersion = 1
   If GdiplusStartup(@gdipToken, @GDIp, NULL) <> 0 Then
      Error 1
      Return False
   Endif
   Return True
End Function

Sub _GDIPlus_Shutdown()
   GdiplusShutdown(gdipToken)
End Sub

Function _GDIPlus_ImageLoadFromFile(Byval sFilename As String) As Any Ptr
   Dim GDIpImage As Any Ptr
   If (GdipLoadImageFromFile(Wstr(sFilename), @GDIpImage) <> 0) Then
      Error 1
      Return 0
   End If
   Return GDIpImage
End Function

Function _GDIPlus_ImageSaveToFile(hImage As Any Ptr, Filename As Wstring, JPGQual As Ulong = 85) As Boolean
   'check If hImage Is a GDI+ image
   Dim As Single iW, iH
   If  GdipGetImageDimension(hImage, @iW, @iH) <> 0 Then Return 0
   
   Dim As Byte iErr = 0

   Dim As Ulong count, size
   GdipGetImageEncodersSize(@count, @size)
   
   Dim As CLSID clsid
   Dim As ImageCodecInfo Ptr pImageCodecInfo
   pImageCodecInfo = Allocate(size)
   GdipGetImageEncoders(count, size, pImageCodecInfo)

   #Define _MimeType(x)   (*Cast(Wstring Ptr, pImageCodecInfo[x].MimeType))
   #Define FnSuffix   (Right(Filename, 4))   
   
   For i As Ulong = 0 To count - 1
      If _MimeType(i) = "image/bmp" And FnSuffix = ".bmp" Then
         If GdipSaveImageToFile(hImage, Wstr(Filename), @pImageCodecInfo[i].Clsid, NULL) <> 0 Then iErr += 1
      Elseif _MimeType(i) = "image/jpeg" And (FnSuffix = ".jpg" Or FnSuffix = ".jpe" Or Right(Filename, 5) = ".jpeg" Or Right(Filename, 5) = ".jfif") Then
         JPGQual = Iif(JPGQual < 0, 0, Iif(JPGQual > 100, 100, JPGQual))
         Dim tParams As EncoderParameters
         Dim EncoderQuality As String = "{1D5BE4B5-FA4A-452D-9CDD-5DB35105E7EB}"
         tParams.Count = 1
         CLSIDFromString(Wstr(EncoderQuality), @tParams.Parameter(0).GUID)
         With tParams.Parameter(0)
            .NumberOfValues = 1
            .Type = EncoderParameterValueTypeLong
            .Value = Varptr(JPGQual)
         End With
         If GdipSaveImageToFile(hImage, Wstr(Filename), @pImageCodecInfo[i].Clsid, @tParams) <> 0 Then iErr += 1         
      Elseif _MimeType(i) = "image/gif" And FnSuffix = ".gif" Then
         If GdipSaveImageToFile(hImage, Wstr(Filename), @pImageCodecInfo[i].Clsid, NULL) <> 0 Then iErr += 1
      Elseif _MimeType(i) = "image/tiff" And (FnSuffix = ".tif" Or Right(Filename, 5) = ".tiff") Then
         If GdipSaveImageToFile(hImage, Wstr(Filename), @pImageCodecInfo[i].Clsid, NULL) <> 0 Then iErr += 1
      Elseif _MimeType(i) = "image/png" And FnSuffix = ".png" Then
         If GdipSaveImageToFile(hImage, Wstr(Filename), @pImageCodecInfo[i].Clsid, NULL) <> 0 Then iErr += 1     
      Else
         iErr += 1
      End If
   Next

   Deallocate(pImageCodecInfo)

   If iErr > 0 Then
      'Error iErr
      Return False
   End If

   Return True
End Function

'Eecode animated GIF and select frame
Function _GDIPlus_GIFAnimGetFrameDimensionsCount(Byval hImage As Any Ptr) As Ulong
   Dim As Ulong iFrameDimCount
   GdipImageGetFrameDimensionsCount(hImage, @iFrameDimCount)
   Return iFrameDimCount
End Function

Function _GDIPlus_GIFAnimGetFrameDimensionsList(Byval hImage As Any Ptr, Byval iFrameDimCount As Ulong) As GUID
   Dim As GUID FrameDimList
   GdipImageGetFrameDimensionsList(hImage, @FrameDimList, iFrameDimCount)
   Return FrameDimList
End Function

Function _GDIPlus_GIFAnimGetFrameCount(Byval hImage As Any Ptr, Byval tFrameDimList As GUID) As Ulong
   Dim As Ulong iFrameCount
   GdipImageGetFrameCount(hImage, @tFrameDimList, @iFrameCount)
   Return iFrameCount
End Function

Sub _GDIPlus_GIFAnimSelectActiveFrame(Byval hImage As Any Ptr, Byval tFrameDimList As GUID, Byval iCurrentFrame As Ulong)
   GdipImageSelectActiveFrame(hImage, @tFrameDimList, iCurrentFrame)
End Sub

Function _GDIPlus_ImageGetPropertyItem(Byval hImage As Any Ptr, Byval iPropID As PROPID) As PropertyItem Ptr
   Dim As Ulong iSize
   GdipGetPropertyItemSize(hImage, iPropID, @iSize)
   Dim As PropertyItem Ptr buffer
   buffer = Allocate(iSize * SizeOf(PropertyItem))
   GdipGetPropertyItem(hImage, iPropID, iSize, @buffer[0])
   Return buffer
End Function

Sub _GDIPlus_GIFAnimGetFrameDelays(Byval hImage As Any Ptr, Byval iAnimFrameCount As Ulong, aFrameDelay() As Ulong)
   Dim As PropertyItem Ptr PropItem = _GDIPlus_ImageGetPropertyItem(hImage, PROPERTYTAGFRAMEDELAY)
   Select Case PropItem->type
      Case 1
         Dim As Ubyte Ptr delay = PropItem->value
         For i As Ulong = 0 To Ubound(aFrameDelay)
            aFrameDelay(i) = delay[i] * 10
         Next
      Case 3
         Dim As Ushort Ptr delay = PropItem->value
         For i As Ulong = 0 To Ubound(aFrameDelay)
            aFrameDelay(i) = delay[i] * 10
         Next
      Case 4
         Dim As Ulong Ptr delay = PropItem->value
         For i As Ulong = 0 To Ubound(aFrameDelay)
            aFrameDelay(i) = delay[i] * 10
         Next
   End Select
End Sub

Sub _GDIPlus_GIFAnimSaveFramesToDisk(Byval hGIFAnim As Any Ptr, sFilename As String)
   Dim As Ulong iFrame, iFrameDimCount = _GDIPlus_GIFAnimGetFrameDimensionsCount(hGIFAnim)
   Dim As GUID tFrameDimList = _GDIPlus_GIFAnimGetFrameDimensionsList(hGIFAnim, iFrameDimCount)
   Dim As Ulong iFrames = _GDIPlus_GIFAnimGetFrameCount(hGIFAnim, tFrameDimList)

   For iFrame = 0 To iFrames - 1
      _GDIPlus_GIFAnimSelectActiveFrame(hGIFAnim, tFrameDimList, iFrame) 'extract frame
      _GDIPlus_ImageSaveToFile(hGIFAnim, Rtrim(sFilename, ".gif") & "_" & iFrame + 1 & ".gif")
   Next
End Sub
'End GDIPlus GIF section


'init GDIPlus
If _GDIPlus_Startup() = False Then End -1



Dim As String sFile = "hypno01s.gif" '<------- change file name here




Dim As Any Ptr hGIFAnim = _GDIPlus_ImageLoadFromFile(sFile) 'load GIG anim file

Dim As Single iW, iH

GdipGetImageDimension(hGIFAnim, @iW, @iH)   'get GIF anim dimension

If iW = 0 Then
   GdiplusShutdown(gdipToken)
   Messagebox(0, "Something went wrong to load the GIF animation!", "ERROR", 16)
   End -2
End If

_GDIPlus_GIFAnimSaveFramesToDisk(hGIFAnim, "c:\Temp\Test.gif") '<------- change file name here

'create a FB GUI to display the animation
Screencontrol SET_DRIVER_NAME, "GDI"
Screenres iW, iH, 32, 1, GFX_HIGH_PRIORITY Or GFX_NO_SWITCH
Dim As String sWinTitle = "GDIPlus GIF Anim Player by UEZ"

Dim As Integer iDW, iDH
Screencontrol GET_DESKTOP_SIZE, iDW, iDH
Dim tWorkingArea As RECT
SystemParametersInfo(SPI_GETWORKAREA, null, @tWorkingArea, null)
Screencontrol SET_WINDOW_POS, (iDW - iW) \ 2, ((tWorkingArea.Bottom - iH) - (iDH - tWorkingArea.Bottom)) \ 2


Dim As HWND hHWND
Screencontrol(GET_WINDOW_HANDLE, Cast(Integer, hHWND)) 'get the FB GUI handle to display GDI+ graphic handle

'create buffered GDI device to display te GIF anim
Dim As Any Ptr    hCanvas, _
            hDC = GetDC(hHWND), _
            hHBitmap = CreateCompatibleBitmap(hDC, iW, iH), _
            hDC_backbuffer = CreateCompatibleDC(hDC), _
            hDC_obj = SelectObject(hDC_backbuffer, hHBitmap)
GdipCreateFromHDC(hDC_backbuffer, @hCanvas)   

'setup GIF animation in GDI+
Dim As Ulong iFrame = 0, iFrameDimCount = _GDIPlus_GIFAnimGetFrameDimensionsCount(hGIFAnim)
Dim As GUID tFrameDimList = _GDIPlus_GIFAnimGetFrameDimensionsList(hGIFAnim, iFrameDimCount)
Dim As Ulong iFrames = _GDIPlus_GIFAnimGetFrameCount(hGIFAnim, tFrameDimList)
Dim As Ulong aFrameDelays(0 To iFrames - 1)
_GDIPlus_GIFAnimGetFrameDelays(hGIFAnim, iFrames, aFrameDelays())

'now play animation in GUI in realtime incl. frame delays without looping
While Inkey = ""
   _GDIPlus_GIFAnimSelectActiveFrame(hGIFAnim, tFrameDimList, iFrame) 'extract frame
   GdipDrawImageRect(hCanvas, hGIFAnim, 0, 0, iW, iH) 'copy frame to GDI canvas
   BitBlt(hDC, 0, 0, iW, iH, hDC_backbuffer, 0, 0, SRCCOPY) 'blit frame to GUI
   Sleep aFrameDelays(iFrame)
   Windowtitle sWinTitle & " * Frame: " & iFrame + 1 & " / " & iFrames
   iFrame += 1
   If iFrame = iFrames Then iFrame = 0
Wend

'release GDI / GDI+ resources
SelectObject(hDC_backbuffer, hDC_obj)
DeleteDC(hDC_backbuffer)
DeleteObject(hHBitmap)
ReleaseDC(hHWND, hDC)
GdipDeleteGraphics(hCanvas)
GdipDisposeImage(hGIFAnim)
_GDIPlus_Shutdown()


Don't forget to adjust the path to the GIF file and filename for the frames!
Last edited by UEZ on Jan 24, 2020 5:47, edited 1 time in total.
jj2007
Posts: 1321
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: Accessing GIF Frames and Displaying GIF Images/Animations

Postby jj2007 » Jan 24, 2020 1:12

Great job, UEZ! In the meantime, I also got inspired by this post and wrote Save frames of a GIF file - under the hood there are many functions that I see in your version, too ;-)
UEZ
Posts: 377
Joined: May 05, 2017 19:59
Location: Germany

Re: Accessing GIF Frames and Displaying GIF Images/Animations

Postby UEZ » Jan 24, 2020 7:39

jj2007 wrote:Great job, UEZ! In the meantime, I also got inspired by this post and wrote Save frames of a GIF file - under the hood there are many functions that I see in your version, too ;-)


Thanks ^^

When I find some time I will add an example how to add images (frames) to a GIF file. I wrote it already in AutoIt...
jj2007
Posts: 1321
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: Accessing GIF Frames and Displaying GIF Images/Animations

Postby jj2007 » Jan 24, 2020 7:58

UEZ wrote:I will add an example how to add images (frames) to a GIF file
That sounds interesting. SOF says that "it is not possible to create Multi-frame or Animated GIFS using GdiPlus.dll version 1 image encoders. The SaveAdd method which can be used to add frames to a multi-frame TIFF image does not work with the GIF encoder". I'll have a look at your AutoIt implementation then... I am tempted, though, to look into the file format. See also this page, looks very useful:

Code: Select all

GIF Animation

GIF Header
Application Extension
[
  Graphic Control Extension
  Image Block
]*
Trailer
UEZ
Posts: 377
Joined: May 05, 2017 19:59
Location: Germany

Re: Accessing GIF Frames and Displaying GIF Images/Animations

Postby UEZ » Jan 24, 2020 15:38

jj2007 wrote:
UEZ wrote:I will add an example how to add images (frames) to a GIF file
That sounds interesting. SOF says that "it is not possible to create Multi-frame or Animated GIFS using GdiPlus.dll version 1 image encoders. The SaveAdd method which can be used to add frames to a multi-frame TIFF image does not work with the GIF encoder". I'll have a look at your AutoIt implementation then... I am tempted, though, to look into the file format. See also this page, looks very useful:

Code: Select all

GIF Animation

GIF Header
Application Extension
[
  Graphic Control Extension
  Image Block
]*
Trailer

Yes, you are right. GDIPlus.dll v1.1 is required aka Windows 7+.
jj2007
Posts: 1321
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: Accessing GIF Frames and Displaying GIF Images/Animations

Postby jj2007 » Jan 24, 2020 15:51

UEZ wrote:GDIPlus.dll v1.1 is required aka Windows 7+.
May I ask what you are using as a manifest? I've never been able to activate version 1.1
Btw here is an excellent documentation of the GIF format.
UEZ
Posts: 377
Joined: May 05, 2017 19:59
Location: Germany

Re: Accessing GIF Frames and Displaying GIF Images/Animations

Postby UEZ » Jan 26, 2020 13:21

jj2007 wrote:
UEZ wrote:GDIPlus.dll v1.1 is required aka Windows 7+.
May I ask what you are using as a manifest? I've never been able to activate version 1.1
Btw here is an excellent documentation of the GIF format.


Thanks for the link!

I don't think that GDI+ v1.1 needs a special initialization or manifest but let me check it out. I will reply as soon as I've some results...
jj2007
Posts: 1321
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: Accessing GIF Frames and Displaying GIF Images/Animations

Postby jj2007 » Jan 26, 2020 15:14

UEZ wrote:I don't think that GDI+ v1.1 needs a special initialization or manifest but let me check it out. I will reply as soon as I've some results...
You are right - I've tested it now with GdipFindFirstImageItem, one of the very few new functions. In my XP VM, GetProcAddress returns zero, while Win7 finds the entry. Good to know ;-)

Code: Select all

#if (GDIPVER >= 0x0110)
GpStatus WINGDIPAPI
GdipFindFirstImageItem(GpImage *image, ImageItemData* item);

GpStatus WINGDIPAPI
GdipFindNextImageItem(GpImage *image, ImageItemData* item);

GpStatus WINGDIPAPI
GdipGetImageItemData(GpImage *image, ImageItemData* item);
#endif //(GDIPVER >= 0x0110)

P.S.: I just googled for one more exotic function called "GdipBitmapGetHistogram" and found this very interesting page ;-)

Return to “Libraries”

Who is online

Users browsing this forum: No registered users and 1 guest