public GifFrame(Stream inputStream, LogicalScreenDescriptor lsd, ColourTable gct, GraphicControlExtension gce, GifFrame previousFrame, GifFrame previousFrameBut1) : this(inputStream, lsd, gct, gce, previousFrame, previousFrameBut1, false) { }
/// <summary> /// Gets the base image for this frame. This will be overpainted with /// the pixels for this frame, where they are not transparent. /// </summary> /// <param name="previousFrame"> /// The frame which preceded this frame in the GIF stream. /// Null if this is the first frame in the stream. /// </param> /// <param name="previousFrameBut1"> /// The frame which preceded the previous frame in the GIF stream. /// Null if this is the first or seond frame in the stream. /// </param> /// <param name="lsd"> /// The logical screen descriptor for this GIF stream. /// </param> /// <param name="gce"> /// The graphic control extension for this frame. /// </param> /// <param name="act"> /// The active colour table for this frame. /// </param> /// <returns></returns> private static Bitmap GetBaseImage(GifFrame previousFrame, GifFrame previousFrameBut1, LogicalScreenDescriptor lsd, GraphicControlExtension gce, ColourTable act) { #region Get the disposal method of the previous frame read from the GIF stream DisposalMethod previousDisposalMethod; if (previousFrame == null) { previousDisposalMethod = DisposalMethod.NotSpecified; } else { previousDisposalMethod = previousFrame.GraphicControlExtension.DisposalMethod; } #endregion Bitmap baseImage; int width = lsd.LogicalScreenSize.Width; int height = lsd.LogicalScreenSize.Height; #region paint baseImage switch (previousDisposalMethod) { case DisposalMethod.DoNotDispose: // pre-populate image with previous frame baseImage = new Bitmap(previousFrame.TheImage); break; case DisposalMethod.RestoreToBackgroundColour: // pre-populate image with background colour Color backgroundColour; if (lsd.BackgroundColourIndex == gce.TransparentColourIndex) { backgroundColour = Color.Empty; } else { // TESTME: background colour index different to transparent colour backgroundColour = act[lsd.BackgroundColourIndex]; } baseImage = new Bitmap(width, height); for (int y = 0; y < height; y++) { for (int x = 0; x < height; x++) { baseImage.SetPixel(x, y, backgroundColour); } } break; case DisposalMethod.RestoreToPrevious: // pre-populate image with previous frame but 1 // TESTME: DisposalMethod.RestoreToPrevious baseImage = new Bitmap(previousFrameBut1.TheImage); break; default: // DisposalMethod.NotSpecified if (previousFrame == null) { // this is the first frame so start with an empty bitmap baseImage = new Bitmap(width, height); } else { // pre-populate image with previous frame // TESTME: DisposalMethod.NotSpecified on 2nd frame or later baseImage = new Bitmap(previousFrame.TheImage); } break; } #endregion return(baseImage); }
/// <summary> /// Sets the pixels of the decoded image. /// </summary> /// <param name="imageData"> /// Table based image data containing the indices within the active /// colour table of the colours of the pixels in this frame. /// </param> /// <param name="lsd"> /// The logical screen descriptor for the GIF stream. /// </param> /// <param name="id"> /// The image descriptor for this frame. /// </param> /// <param name="activeColourTable"> /// The colour table to use with this frame - either the global colour /// table or a local colour table. /// </param> /// <param name="gce"> /// The graphic control extension, if any, which precedes this image in /// the input stream. /// </param> /// <param name="previousFrame"> /// The frame which precedes this one in the GIF stream, if present. /// </param> /// <param name="previousFrameBut1"> /// The frame which precedes the frame before this one in the GIF stream, /// if present. /// </param> /// <param name="status"> /// GifComponentStatus containing any errors which occurred during the /// creation of the bitmap. /// </param> private static Bitmap CreateBitmap(TableBasedImageData imageData, LogicalScreenDescriptor lsd, ImageDescriptor id, ColourTable activeColourTable, GraphicControlExtension gce, GifFrame previousFrame, GifFrame previousFrameBut1, out GifComponentStatus status) { status = new GifComponentStatus(ErrorState.Ok, ""); Color[] pixelsForThisFrame = new Color[lsd.LogicalScreenSize.Width * lsd.LogicalScreenSize.Height]; Bitmap baseImage = GetBaseImage(previousFrame, previousFrameBut1, lsd, gce, activeColourTable); // copy each source line to the appropriate place in the destination int pass = 1; int interlaceRowIncrement = 8; int interlaceRowNumber = 0; // the row of pixels we're currently // setting in an interlaced image. for (int i = 0; i < id.Size.Height; i++) { int pixelRowNumber = i; if (id.IsInterlaced) { #region work out the pixel row we're setting for an interlaced image if (interlaceRowNumber >= id.Size.Height) { pass++; switch (pass) { case 2: interlaceRowNumber = 4; break; case 3: interlaceRowNumber = 2; interlaceRowIncrement = 4; break; case 4: interlaceRowNumber = 1; interlaceRowIncrement = 2; break; } } #endregion pixelRowNumber = interlaceRowNumber; interlaceRowNumber += interlaceRowIncrement; } // Colour in the pixels for this row pixelRowNumber += id.Position.Y; if (pixelRowNumber < lsd.LogicalScreenSize.Height) { int k = pixelRowNumber * lsd.LogicalScreenSize.Width; int dx = k + id.Position.X; // start of line in dest int dlim = dx + id.Size.Width; // end of dest line if ((k + lsd.LogicalScreenSize.Width) < dlim) { // TESTME: CreateBitmap - past dest edge dlim = k + lsd.LogicalScreenSize.Width; // past dest edge } int sx = i * id.Size.Width; // start of line in source while (dx < dlim) { // map color and insert in destination int indexInColourTable = (int)imageData.Pixels[sx++]; // Set this pixel's colour if its index isn't the // transparent colour index, or if this frame doesn't // have a transparent colour. Color c; if (gce.HasTransparentColour && indexInColourTable == gce.TransparentColourIndex) { c = Color.Empty; // transparent pixel } else { if (indexInColourTable < activeColourTable.Length) { c = activeColourTable[indexInColourTable]; } else { // TESTME: CreateBitmap - BadColourIndex c = Color.Black; string message = "Colour index: " + indexInColourTable + ", colour table length: " + activeColourTable.Length + " (" + dx + "," + pixelRowNumber + ")"; status = new GifComponentStatus(ErrorState.BadColourIndex, message); } } pixelsForThisFrame[dx] = c; dx++; } } } return(CreateBitmap(baseImage, pixelsForThisFrame)); }
public GifFrame(Stream inputStream, LogicalScreenDescriptor lsd, ColourTable gct, GraphicControlExtension gce, GifFrame previousFrame, GifFrame previousFrameBut1, bool xmlDebugging) : base(xmlDebugging) { #region guard against null arguments if (lsd == null) { throw new ArgumentNullException("lsd"); } if (gce == null) { SetStatus(ErrorState.NoGraphicControlExtension, ""); // use a default GCE gce = new GraphicControlExtension(GraphicControlExtension.ExpectedBlockSize, DisposalMethod.NotSpecified, false, false, 100, 0); } #endregion int transparentColourIndex = gce.TransparentColourIndex; ImageDescriptor imageDescriptor = new ImageDescriptor(inputStream, XmlDebugging); WriteDebugXmlNode(imageDescriptor.DebugXmlReader); #region determine the colour table to use for this frame Color backgroundColour = Color.FromArgb(0); // TODO: is this the right background colour? // TODO: use backgroundColourIndex from the logical screen descriptor? ColourTable activeColourTable; if (imageDescriptor.HasLocalColourTable) { _localColourTable = new ColourTable(inputStream, imageDescriptor.LocalColourTableSize, XmlDebugging); WriteDebugXmlNode(_localColourTable.DebugXmlReader); activeColourTable = _localColourTable; // make local table active } else { if (gct == null) { // We have neither local nor global colour table, so we // won't be able to decode this frame. Bitmap emptyBitmap = new Bitmap(lsd.LogicalScreenSize.Width, lsd.LogicalScreenSize.Height); _image = emptyBitmap; _delay = gce.DelayTime; SetStatus(ErrorState.FrameHasNoColourTable, ""); return; } activeColourTable = gct; // make global table active if (lsd.BackgroundColourIndex == transparentColourIndex) { backgroundColour = Color.FromArgb(0); } } #endregion // decode pixel data int pixelCount = imageDescriptor.Size.Width * imageDescriptor.Size.Height; TableBasedImageData indexedPixels = new TableBasedImageData(inputStream, pixelCount, XmlDebugging); WriteDebugXmlNode(indexedPixels.DebugXmlReader); if (indexedPixels.Pixels.Count == 0) { // TESTME: constructor - indexedPixels.Pixels.Count == 0 Bitmap emptyBitmap = new Bitmap(lsd.LogicalScreenSize.Width, lsd.LogicalScreenSize.Height); _image = emptyBitmap; _delay = gce.DelayTime; SetStatus(ErrorState.FrameHasNoImageData, ""); WriteDebugXmlFinish(); return; } // Skip any remaining blocks up to the next block terminator (in // case there is any surplus data before the next frame) SkipBlocks(inputStream); _indexedPixels = indexedPixels; _extension = gce; if (gce != null) { _delay = gce.DelayTime; } _imageDescriptor = imageDescriptor; _backgroundColour = backgroundColour; GifComponentStatus status; _image = CreateBitmap(indexedPixels, lsd, imageDescriptor, activeColourTable, gce, previousFrame, previousFrameBut1, out status); WriteDebugXmlFinish(); }