GifDataStream.cs 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. using System.Collections.Generic;
  2. using System.IO;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. namespace XamlAnimatedGif.Decoding
  6. {
  7. internal class GifDataStream
  8. {
  9. public GifHeader Header { get; private set; }
  10. public GifColor[] GlobalColorTable { get; set; }
  11. public IList<GifFrame> Frames { get; set; }
  12. public IList<GifExtension> Extensions { get; set; }
  13. public ushort RepeatCount { get; set; }
  14. private GifDataStream()
  15. {
  16. }
  17. internal static async Task<GifDataStream> ReadAsync(Stream stream)
  18. {
  19. var file = new GifDataStream();
  20. await file.ReadInternalAsync(stream).ConfigureAwait(false);
  21. return file;
  22. }
  23. private async Task ReadInternalAsync(Stream stream)
  24. {
  25. Header = await GifHeader.ReadAsync(stream).ConfigureAwait(false);
  26. if (Header.LogicalScreenDescriptor.HasGlobalColorTable)
  27. {
  28. GlobalColorTable = await GifHelpers.ReadColorTableAsync(stream, Header.LogicalScreenDescriptor.GlobalColorTableSize).ConfigureAwait(false);
  29. }
  30. await ReadFramesAsync(stream).ConfigureAwait(false);
  31. var netscapeExtension =
  32. Extensions
  33. .OfType<GifApplicationExtension>()
  34. .FirstOrDefault(GifHelpers.IsNetscapeExtension);
  35. RepeatCount = netscapeExtension != null
  36. ? GifHelpers.GetRepeatCount(netscapeExtension)
  37. : (ushort)1;
  38. }
  39. private async Task ReadFramesAsync(Stream stream)
  40. {
  41. List<GifFrame> frames = new List<GifFrame>();
  42. List<GifExtension> controlExtensions = new List<GifExtension>();
  43. List<GifExtension> specialExtensions = new List<GifExtension>();
  44. while (true)
  45. {
  46. try
  47. {
  48. var block = await GifBlock.ReadAsync(stream, controlExtensions).ConfigureAwait(false);
  49. if (block.Kind == GifBlockKind.GraphicRendering)
  50. controlExtensions = new List<GifExtension>();
  51. if (block is GifFrame frame)
  52. {
  53. frames.Add(frame);
  54. }
  55. else if (block is GifExtension extension)
  56. {
  57. switch (extension.Kind)
  58. {
  59. case GifBlockKind.Control:
  60. controlExtensions.Add(extension);
  61. break;
  62. case GifBlockKind.SpecialPurpose:
  63. specialExtensions.Add(extension);
  64. break;
  65. // Just discard plain text extensions for now, since we have no use for it
  66. }
  67. }
  68. else if (block is GifTrailer)
  69. {
  70. break;
  71. }
  72. }
  73. // Follow the same approach as Firefox:
  74. // If we find extraneous data between blocks, just assume the stream
  75. // was successfully terminated if we have some successfully decoded frames
  76. // https://dxr.mozilla.org/firefox/source/modules/libpr0n/decoders/gif/nsGIFDecoder2.cpp#894-909
  77. catch (UnknownBlockTypeException) when (frames.Count > 0)
  78. {
  79. break;
  80. }
  81. }
  82. this.Frames = frames.AsReadOnly();
  83. this.Extensions = specialExtensions.AsReadOnly();
  84. }
  85. }
  86. }