Ver Mensaje Individual
  #8 (permalink)  
Antiguo 15/11/2009, 06:12
Avatar de pkj
pkj
 
Fecha de Ingreso: julio-2006
Ubicación: Órbita sincrónica
Mensajes: 899
Antigüedad: 18 años, 9 meses
Puntos: 29
Respuesta: Animación gif en una aplicación

Nota: Esta explicación es en respuesta a una petición de un compañero del foro.
Como podéis imaginar hay muchas cosas que no he estudiado en profundidad y no podré explicar, como el formato interno de un fichero gif, pero intentaré dar una aproximación.

Código :
Ver original
  1. Option Explicit
  2. Dim NumberOfTimesToRepeatSequence As Long
  3. Dim RepeatedCount As Long
  4. Dim MaxCnt As Integer
  5. Dim ImgCnt As Integer
  6. Private Sub Form_Load()
  7.   On Error GoTo Form_LoadError
  8.   Image1(0).Left = 0
  9.   Image1(0).Top = 0
  10.   'lugar de la ruta de acceso al GIF que desea animar aquí
  11.   Call DecodeGif("C:\FicheroGifAnimado.gif")
  12.   'PONER AQUI EL FICHERO GIF
  13.   Exit Sub
  14. Form_LoadError:
  15.   MsgBox Err.Description
  16. End Sub
  17.  
  18. Public Sub DecodeGif(PathToGifFile As String)
  19.  
  20.   ' si hay errores saltamos a la etiqueta DecodeGifError
  21.   On Error GoTo DecodeGifError
  22.  
  23.   'Declaramos las variables
  24.   Dim FNumb As Integer
  25.   Dim GifBuffer As String
  26.   Dim GifHeader As String
  27.   Dim SectionStart As Long
  28.   Dim SectionEnd As Long
  29.   Dim SectionMarker As String
  30.   Dim ImageCount As Integer
  31.   Dim i As Integer
  32.   Dim NewPicBuff As String
  33.   Dim ImageHeader As String
  34.   Dim DisplayTime As Long
  35.   Dim LftOffSet As Long
  36.   Dim TopOffSet As Long
  37.  
  38.   ' comprobamos básicamente si existe el fichero contenido en la variable PathToGifFile
  39.   If Dir(PathToGifFile) = vbNullString Then
  40.     MsgBox "Dónde está el GIF!?"
  41.     ' si no lo encuentra salimos
  42.     Exit Sub
  43.   End If
  44.  
  45.   'detenemos el timer y cualquier animación que pueda estar en marcha
  46.   Timer1.Enabled = False
  47.  
  48.   ' eliminamos las imágenes existentes por si ya hemos mostrado antes otra animación
  49.   For i = 1 To Image1.Count - 1
  50.     Unload Image1(i)
  51.   Next i
  52.   ' solo dejamos la imagen (0) como base para crear mas
  53.  
  54.   ' nos aseguramos de que el control image no va a cambiar de tamaño para adaptarse al tamaño de la imagen
  55.   Image1(0).Stretch = False
  56.   'set value(s)
  57.  
  58.   'valor de referencia (s)
  59.   SectionMarker = Chr(0) & "!ù"
  60.   ' Esto imagino que es algo técnico sobre el formato de un fichero gif.
  61.   ' Posiblemente son caracteres que encontraremos precediendo a cada imagen o algo así.
  62.  
  63.   ' Puntero que iremos usando para guardar la posicion de comienzo de cada seccion con la que trabajemos
  64.   SectionStart = 1
  65.  
  66.   ' el sistema nos dá un numero de fichero que no se está usando en otro open en este momento
  67.   FNumb = FreeFile  ' <--- Esto es mejor que esto ---> FNumb = 1
  68.  
  69.   'abrimos el fichero gif
  70.   Open PathToGifFile For Binary As #FNumb
  71.  
  72.   ' leemos todo el fichero y lo cargamos en un string llamado GifBuffer
  73.   GifBuffer = Input(FileLen(PathToGifFile), #FNumb)
  74.   ' y lo cerramos.
  75.   Close #FNumb
  76.  
  77.   ' Buscamos el final del encabezado del fichero gif usando los caracteres de sectionmarker
  78.   SectionEnd = InStr(SectionStart, GifBuffer, SectionMarker) + Len(SectionMarker) - 2
  79.  
  80.   'guardamos esa parte del comienzo del gif (encabezado-header) que contendrá datos sobre el gif...
  81.   GifHeader = Left(GifBuffer, SectionEnd)
  82.  
  83.   ' guardamos la posición de inicio de la primera imagen
  84.   SectionStart = SectionEnd + 2
  85.  
  86.   'según la longitud del encabezado calculamos si el gif es continuo o se repite un numero limitado de veces
  87.   If Len(GifHeader) > 127 Then
  88.     ' si es mayor que 127 extraemos la cantidad de ciclos que se va a repetir con la formula:
  89.     ' ((Numero ascii del caracter 127) * 256) + Numero ascii del caracter 126
  90.     NumberOfTimesToRepeatSequence = Asc(Mid(GifHeader, 127, 1))
  91.     NumberOfTimesToRepeatSequence = NumberOfTimesToRepeatSequence * 256
  92.     NumberOfTimesToRepeatSequence = NumberOfTimesToRepeatSequence + Asc(Mid(GifHeader, 126, 1))
  93.   Else
  94.     ' si el encabezado no es mayor de 127 es que el gif se repite continuamente
  95.     NumberOfTimesToRepeatSequence = 0 'bucle infinito
  96.   End If
  97.  
  98.  
  99.   ' aqui extraemos las imagenes
  100.  
  101.   ' esto no se como funciona exactamente pero hace un bucle mientras queden imagenes que extraer
  102.   Do While SectionEnd <> Len(SectionMarker)
  103.  
  104.   'llevamos un contador de imagenes extraidas para crear los controles image
  105.   ImageCount = ImageCount + 1
  106.  
  107.   'para saber dónde termina la sección
  108.   SectionEnd = InStr(SectionStart, GifBuffer, SectionMarker) + Len(SectionMarker)
  109.  
  110.   'Nos aseguramos de que hay alguna imagen en el gif
  111.   If SectionEnd > Len(SectionMarker) Then
  112.  
  113.     'capturamos el trozo marcado y lo precedemos con el encabezado del fichero Gif (imagino que para que mantenga el formato al guardarlo como imagen individual)
  114.     NewPicBuff = GifHeader & Mid(GifBuffer, SectionStart - 1, SectionEnd - SectionStart)
  115.    
  116.     'Ahora se escribe la imagen a un archivo para poder cargarla como nosotros queremos hacerlo
  117.     FNumb = FreeFile
  118.     Open App.Path & "\temp.gif" For Binary As #FNumb
  119.     Put #FNumb, 1, NewPicBuff
  120.     Close #FNumb
  121.    
  122.     'ahora guardamos los 16 primeros caracteres de cada imagen que nos dirán su duración, etc
  123.     ImageHeader = Left(Mid(GifBuffer, SectionStart - 1, SectionEnd - SectionStart), 16)
  124.    
  125.     'Ahora calculamos la duración de la imagen actual con una formula que usa el cuarto y el quinto de esos 16 caracteres guardados
  126.     DisplayTime = Asc(Mid(ImageHeader, 5, 1))
  127.     DisplayTime = DisplayTime * 256
  128.     DisplayTime = DisplayTime + Asc(Mid(ImageHeader, 4, 1))
  129.     DisplayTime = DisplayTime * 10
  130.    
  131.     ' si no es la primera imagen que guardamos...
  132.     If ImageCount > 1 Then
  133.  
  134.       ' calculamos la posición de la nueva imagen dentro del formulario usando para ello los caracteres 9,10,11 y 12 de los 16 de la cabecera de la imagen.
  135.       LftOffSet = Asc(Mid(ImageHeader, 10, 1))
  136.       LftOffSet = LftOffSet * 256
  137.       LftOffSet = LftOffSet + Asc(Mid(ImageHeader, 9, 1))
  138.  
  139.       TopOffSet = Asc(Mid(ImageHeader, 12, 1))
  140.       TopOffSet = TopOffSet * 256
  141.       TopOffSet = TopOffSet + Asc(Mid(ImageHeader, 11, 1))
  142.  
  143.       'carga un nuevo control image y establece sus propiedades
  144.       Load Image1(ImageCount - 1)
  145.  
  146.       Image1(ImageCount - 1).ZOrder 0  ' zorder nunca he entendido como funciona (ni usado)
  147.  
  148.       Image1(ImageCount - 1).Left = Image1(0).Left + (LftOffSet * 15)
  149.       Image1(ImageCount - 1).Top = Image1(0).Top + (TopOffSet * 15)
  150.      
  151.     End If
  152.    
  153.     ' Añado esto para evitar que se cuele un valor de interval no válido.
  154.     If DisplayTime < 0 Then DisplayTime = 0
  155.     If DisplayTime > 65535 Then DisplayTime = 65535
  156.     ' creo que ya no es necesario pero al principio me pasaba y por seguridad lo dejo.
  157.  
  158.    
  159.     'guardamos en la propiedad Tag el tiempo que debe mostrarse la imagen
  160.     Image1(ImageCount - 1).Tag = DisplayTime
  161.  
  162.     ' cargamos la imagen que habíamos guardado en un fichero
  163.     Image1(ImageCount - 1).Picture = LoadPicture(App.Path & "\temp.gif")
  164.    
  165.     'la imagen ya no hace falta y se elimina
  166.     Kill App.Path & "\temp.gif"
  167.    
  168.     ' nos colocamos en posicion para comenzar con la siguiente imagen a cargar
  169.     SectionStart = SectionEnd
  170.    
  171.   End If
  172.  
  173.   ' y asi tantas veces como imagenes haya
  174.   Loop
  175.  
  176.   ' si aun quedan datos en el fichero creamos una última imagen con eso
  177.   If SectionStart < Len(GifBuffer) Then
  178.  
  179.     NewPicBuff = GifHeader & Mid(GifBuffer, SectionStart - 1, Len(GifBuffer) - SectionStart)
  180.    
  181.     ' el proceso es igual que antes asi que quito comentarios.
  182.  
  183.     FNumb = FreeFile
  184.     Open App.Path & "\temp.gif" For Binary As #FNumb
  185.     Put #FNumb, 1, NewPicBuff
  186.     Close #FNumb
  187.    
  188.     ImageHeader = Left(Mid(GifBuffer, SectionStart - 1, Len(GifBuffer) - SectionStart), 16)
  189.    
  190.     DisplayTime = Asc(Mid(ImageHeader, 5, 1))
  191.     DisplayTime = DisplayTime * 256
  192.     DisplayTime = DisplayTime + Asc(Mid(ImageHeader, 4, 1))
  193.     DisplayTime = DisplayTime * 10
  194.  
  195.     If ImageCount > 1 Then
  196.    
  197.       LftOffSet = Asc(Mid(ImageHeader, 10, 1))
  198.       LftOffSet = LftOffSet * 256
  199.       LftOffSet = LftOffSet + Asc(Mid(ImageHeader, 9, 1))
  200.  
  201.       TopOffSet = Asc(Mid(ImageHeader, 12, 1))
  202.       TopOffSet = TopOffSet * 256
  203.       TopOffSet = TopOffSet + Asc(Mid(ImageHeader, 11, 1))
  204.      
  205.       Load Image1(ImageCount - 1)
  206.       Image1(ImageCount - 1).ZOrder 0
  207.       Image1(ImageCount - 1).Left = Image1(0).Left + (LftOffSet * 15)
  208.       Image1(ImageCount - 1).Top = Image1(0).Top + (TopOffSet * 15)
  209.    
  210.     End If
  211.    
  212.     If DisplayTime < 0 Then DisplayTime = 0
  213.     If DisplayTime > 65535 Then DisplayTime = 65535
  214.    
  215.     Image1(ImageCount - 1).Tag = DisplayTime
  216.     Image1(ImageCount - 1).Picture = LoadPicture(App.Path & "\temp.gif")
  217.     Kill App.Path & "\temp.gif"
  218.    
  219.   End If
  220.  
  221.   ' reiniciamos el contador de imagenes mostradas en el timer
  222.   ImgCnt = -1
  223.   ' y guardamos el numero de imagenes totales
  224.   MaxCnt = Image1.Count - 1
  225.    
  226.   ' muestro ya la primera imagen
  227.   Image1(0).Visible = True
  228.   ' esto creo que tambien sobra.
  229.   ' lo puse para que no se saltase la primera imagen pero creo que se arregló al poner ImgCnt = - 1
  230.  
  231.   'configurar el timer con la duración de la primera imagen
  232.   Timer1.Interval = CInt(Image1(0).Tag)
  233.   'y para finalizar activamos el timer para comenzar la animacion
  234.   Timer1.Enabled = True
  235.   Exit Sub
  236.  
  237. DecodeGifError:
  238.   ' esto lo añadi para saltarme un error
  239.   If Image1.Count > 0 And Err.Number = 50003 Then
  240.     Unload Image1(Image1.Count - 1)
  241.     ImageCount = ImageCount - 1
  242.   Else
  243.     MsgBox Err.Number & " - " & Err.Description
  244.   End If
  245.   Err.Clear
  246.  
  247.   ' Añado esto para extraer todos los
  248.   ' fotogramas que pueda aun con un error
  249.   Resume Next
  250. End Sub

Creo que eso es todo.

Saludos
__________________
No hay preguntas tontas, solo gente estup..., ¡No!, ¿como era? No hay gente que pregunte a tontos... ¡Nooo!... ¡Vaya cabeza!

Última edición por pkj; 16/11/2009 a las 05:33 Razón: Repasar comentarios