Link to home
Start Free TrialLog in
Avatar of joshkrak
joshkrakFlag for United States of America

asked on

Watermark an Animated Gif

Looking for a way to put watermarks on each frame of an animated gif with vb.net. I have been successful in single frame images with Drawing.Bitmap and Drawing.Graphics namespaces but cant figure out how to get to frames of an animated gif. I know there are SDK's out there, but I want an SDK at LAST resort.
ASKER CERTIFIED SOLUTION
Avatar of LordWabbit
LordWabbit

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of joshkrak

ASKER

Thank you for the site.. Well, half of you code worked, mainly the part about extracting each frame. That was half the solution. The re-combining didnt work, but from reading through the comments on the site you got the code from I was able to piece together a working version. Below is the code I ended up with to recombine the individual frames.




Imports System
Imports System.Collections.Generic
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.IO
 
Namespace GifCreator
    Public Class GifClass
 
        Public Enum GIFVersion
            GIF87a
            GIF89a
        End Enum
        Public Enum GIFBlockType
            ImageDescriptor = 44
            Extension = 33
            Trailer = 59
        End Enum
 
        Public m_Version As GIFVersion = GIFVersion.GIF87a
        Public m_Image As Bitmap = Nothing
        Public m_GifSignature As New List(Of Byte)()
        Public m_ScreenDescriptor As New List(Of Byte)()
        Public m_ColorTable As New List(Of Byte)()
        Public m_ImageDescriptor As New List(Of Byte)()
        Public m_ImageData As New List(Of Byte)()
 
        Public Sub New()
        End Sub
 
        Public Sub LoadGifPicture(ByVal filename As String)
            LoadGifPicture(DirectCast(Bitmap.FromFile(filename), Bitmap))
        End Sub
 
        Public Sub LoadGifPicture(ByVal gifPicture As Bitmap)
            Dim stream As New MemoryStream()
            m_Image = gifPicture
            m_Image.Save(stream, ImageFormat.Gif)
            Dim dataList As New List(Of Byte)(stream.ToArray())
            If Not AnalyzeGifSignature(dataList) Then
                Throw (New Exception("File is not a gif!"))
            End If
            AnalyzeScreenDescriptor(dataList)
            Dim blockType As GIFBlockType = GetTypeOfNextBlock(dataList)
            While blockType <> GIFBlockType.Trailer
                Select Case blockType
                    Case GIFBlockType.ImageDescriptor
                        AnalyzeImageDescriptor(dataList)
                        Exit Select
                    Case GIFBlockType.Extension
                        ThrowAwayExtensionBlock(dataList)
                        Exit Select
                    Case Else
                        Exit Select
                End Select
                blockType = GetTypeOfNextBlock(dataList)
            End While
        End Sub
 
        Private Function AnalyzeGifSignature(ByVal gifData As List(Of Byte)) As Boolean
            For i As Integer = 0 To 5
                m_GifSignature.Add(gifData(i))
            Next
            gifData.RemoveRange(0, 6)
            Dim chars As List(Of Char) = m_GifSignature.ConvertAll(Of Char)(New Converter(Of Byte, Char)(Function(b As Byte) ChrW(b)))
            Dim s As New String(chars.ToArray())
            If s = GIFVersion.GIF89a.ToString() Then
                m_Version = GIFVersion.GIF89a
            ElseIf s = GIFVersion.GIF87a.ToString() Then
                m_Version = GIFVersion.GIF87a
            Else
                Return False
            End If
            Return True
        End Function
 
        Private Sub AnalyzeScreenDescriptor(ByVal gifData As List(Of Byte))
            For i As Integer = 0 To 6
                m_ScreenDescriptor.Add(gifData(i))
            Next
            gifData.RemoveRange(0, 7)
            ' if the first bit of the fifth byte is set the GlobelColorTable follows this block
            Dim globalColorTableFollows As Boolean = (m_ScreenDescriptor(4) And 128) <> 0
            If globalColorTableFollows Then
                Dim pixel As Integer = m_ScreenDescriptor(4) And 7
                Dim lengthOfColorTableInByte As Integer = 3 * CInt(Math.Pow(2, pixel + 1))
                For i As Integer = 0 To lengthOfColorTableInByte - 1
                    m_ColorTable.Add(gifData(i))
                Next
                gifData.RemoveRange(0, lengthOfColorTableInByte)
            End If
            m_ScreenDescriptor(4) = CByte((m_ScreenDescriptor(4) And 127))
        End Sub
 
        Private Function GetTypeOfNextBlock(ByVal gifData As List(Of Byte)) As GIFBlockType
            Dim blockType As GIFBlockType = gifData(0)
            Return blockType
        End Function
 
        Private Sub AnalyzeImageDescriptor(ByVal gifData As List(Of Byte))
 
            For i As Integer = 0 To 9
                m_ImageDescriptor.Add(gifData(i))
            Next
            gifData.RemoveRange(0, 10)
            ' get ColorTable if exists
            Dim localColorMapFollows As Boolean = (m_ImageDescriptor(9) And 128) <> 0
            If localColorMapFollows Then
                Dim pixel As Integer = m_ImageDescriptor(9) And 7
                Dim lengthOfColorTableInByte As Integer = 3 * CInt(Math.Pow(2, pixel + 1))
                m_ColorTable.Clear()
                For i As Integer = 0 To lengthOfColorTableInByte - 1
                    m_ColorTable.Add(gifData(i))
                Next
                gifData.RemoveRange(0, lengthOfColorTableInByte)
            Else
                Dim lastThreeBitsOfGlobalTableDescription As Integer = m_ScreenDescriptor(4) And 7
                m_ImageDescriptor(9) = CByte((m_ImageDescriptor(9) And 248))
                m_ImageDescriptor(9) = CByte((m_ImageDescriptor(9) Or lastThreeBitsOfGlobalTableDescription))
            End If
            m_ImageDescriptor(9) = CByte((m_ImageDescriptor(9) Or 128))
            GetImageData(gifData)
        End Sub
 
        Private Sub GetImageData(ByVal gifData As List(Of Byte))
            m_ImageData.Add(gifData(0))
            gifData.RemoveAt(0)
            While gifData(0) <> 0
                Dim countOfFollowingDataBytes As Integer = gifData(0)
                For i As Integer = 0 To countOfFollowingDataBytes
                    m_ImageData.Add(gifData(i))
                Next
                gifData.RemoveRange(0, countOfFollowingDataBytes + 1)
            End While
            m_ImageData.Add(gifData(0))
            gifData.RemoveAt(0)
        End Sub
 
        Private Sub ThrowAwayExtensionBlock(ByVal gifData As List(Of Byte))
            gifData.RemoveRange(0, 2)
            ' Delete ExtensionBlockIndicator and ExtensionDetermination
            While gifData(0) <> 0
                gifData.RemoveRange(0, gifData(0) + 1)
            End While
            gifData.RemoveAt(0)
        End Sub
    End Class
End Namespace
 
 
 
 
 
 
 
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Text
 
Namespace GifCreator
    Public Class GifCreator
        Public Shared Sub CreateAnimatedGif(ByVal gifFiles As List(Of String), ByVal delay As Integer, ByVal outputFile As String)
            Dim writer As New BinaryWriter(New FileStream(outputFile, FileMode.Create, FileAccess.ReadWrite))
            Dim gif_Signature As Byte() = New Byte() {CByte(AscW("G"c)), CByte(AscW("I"c)), CByte(AscW("F"c)), CByte(AscW("8"c)), CByte(AscW("9"c)), CByte(AscW("a"c))}
            writer.Write(gif_Signature)
            For i As Integer = 0 To gifFiles.Count - 1
                Dim gif As New GifClass()
                gif.LoadGifPicture(gifFiles(i))
                If i = 0 Then
                    writer.Write(gif.m_ScreenDescriptor.ToArray())
                End If
                writer.Write(GifCreator.CreateGraphicControlExtensionBlock(delay))
                writer.Write(gif.m_ImageDescriptor.ToArray())
                writer.Write(gif.m_ColorTable.ToArray())
                writer.Write(gif.m_ImageData.ToArray())
            Next
            writer.Write(GifCreator.CreateLoopBlock())
            writer.Write(CByte(59))
            'End file
            writer.Close()
        End Sub
 
        Public Shared Function CreateGraphicControlExtensionBlock(ByVal delay As Integer) As Byte()
            Dim result As Byte() = New Byte(7) {}
            ' Split the delay into high- and lowbyte
            Dim d1 As Byte = CByte((delay Mod 256))
            Dim d2 As Byte = CByte((delay / 256))
            result(0) = CByte(33)
            ' Start ExtensionBlock
            result(1) = CByte(249)
            ' GraphicControlExtension
            result(2) = CByte(4)
            ' Size of DataBlock (4)
            result(3) = d2
            result(4) = d1
            result(5) = CByte(0)
            result(6) = CByte(0)
            result(7) = CByte(0)
            Return result
        End Function
 
        Public Shared Function CreateLoopBlock() As Byte()
            Return CreateLoopBlock(0)
        End Function
 
        Public Shared Function CreateLoopBlock(ByVal numberOfRepeatings As Integer) As Byte()
            Dim rep1 As Byte = CByte((numberOfRepeatings Mod 256))
            Dim rep2 As Byte = CByte((numberOfRepeatings / 256))
            Dim result As Byte() = New Byte(18) {}
            result(0) = CByte(33)
            ' Start ExtensionBlock
            result(1) = CByte(255)
            ' ApplicationExtension
            result(2) = CByte(11)
            ' Size of DataBlock (11) for NETSCAPE2.0)
            result(3) = CByte(AscW("N"c))
            result(4) = CByte(AscW("E"c))
            result(5) = CByte(AscW("T"c))
            result(6) = CByte(AscW("S"c))
            result(7) = CByte(AscW("C"c))
            result(8) = CByte(AscW("A"c))
            result(9) = CByte(AscW("P"c))
            result(10) = CByte(AscW("E"c))
            result(11) = CByte(AscW("2"c))
            result(12) = CByte(AscW("."c))
            result(13) = CByte(AscW("0"c))
            result(14) = CByte(3)
            ' Size of Loop Block
            result(15) = CByte(1)
            ' Loop Indicator
            result(16) = CByte(rep1)
            ' Number of repetitions
            result(17) = CByte(rep2)
            ' 0 for endless loop
            result(18) = CByte(0)
            Return result
        End Function
    End Class
End Namespace
 
 
 
 
 
'Usage:
        Dim list As New List(Of String)
        list.AddRange(New String() {"c:\temp\0.bmp", "c:\temp\1.bmp", "c:\temp\2.bmp", "c:\temp\3.bmp", "c:\temp\4.bmp", "c:\temp\5.bmp"})
        GifCreator.GifCreator.CreateAnimatedGif(list, 15, "c:\temp\out.gif")

Open in new window

Submitted solution only worked for half of the required solution as-is. However, from a web link with the code that contained the original author's code and user comments, I was able to piece together a working solution.