/// <summary> /// Reads the frame indices marking the color to use for each pixel. /// </summary> /// <param name="imageDescriptor">The <see cref="GifImageDescriptor"/>.</param> /// <returns>The <see cref="T:byte[]"/></returns> private byte[] ReadFrameIndices(GifImageDescriptor imageDescriptor) { int dataSize = this.currentStream.ReadByte(); byte[] indices; using (LzwDecoder lzwDecoder = new LzwDecoder(this.currentStream)) { indices = lzwDecoder.DecodePixels(imageDescriptor.Width, imageDescriptor.Height, dataSize); } return(indices); }
/// <summary> /// Reads the local color table from the current frame. /// </summary> /// <param name="imageDescriptor">The <see cref="GifImageDescriptor"/>.</param> /// <returns>The <see cref="T:byte[]"/></returns> private byte[] ReadFrameLocalColorTable(GifImageDescriptor imageDescriptor) { byte[] localColorTable = null; if (imageDescriptor.LocalColorTableFlag) { localColorTable = new byte[imageDescriptor.LocalColorTableSize * 3]; this.currentStream.Read(localColorTable, 0, localColorTable.Length); } return(localColorTable); }
/// <summary> /// Reads an individual gif frame. /// </summary> private void ReadFrame() { GifImageDescriptor imageDescriptor = this.ReadImageDescriptor(); byte[] localColorTable = this.ReadFrameLocalColorTable(imageDescriptor); byte[] indices = this.ReadFrameIndices(imageDescriptor); // Determine the color table for this frame. If there is a local one, use it // otherwise use the global color table. byte[] colorTable = localColorTable ?? this.globalColorTable; this.ReadFrameColors(indices, colorTable, imageDescriptor); // Skip any remaining blocks this.Skip(0); }
/// <summary> /// Reads the image descriptor /// </summary> /// <returns><see cref="GifImageDescriptor"/></returns> private GifImageDescriptor ReadImageDescriptor() { this.currentStream.Read(this.buffer, 0, 9); byte packed = this.buffer[8]; var imageDescriptor = new GifImageDescriptor { Left = BitConverter.ToInt16(this.buffer, 0), Top = BitConverter.ToInt16(this.buffer, 2), Width = BitConverter.ToInt16(this.buffer, 4), Height = BitConverter.ToInt16(this.buffer, 6), LocalColorTableFlag = ((packed & 0x80) >> 7) == 1, LocalColorTableSize = 2 << (packed & 0x07), InterlaceFlag = ((packed & 0x40) >> 6) == 1 }; return(imageDescriptor); }
/// <summary> /// Reads an individual gif frame. /// </summary> private void ReadFrame() { GifImageDescriptor imageDescriptor = this.ReadImageDescriptor(); byte[] localColorTable = null; byte[] indices = null; try { // Determine the color table for this frame. If there is a local one, use it otherwise use the global color table. int length = this.globalColorTableLength; if (imageDescriptor.LocalColorTableFlag) { length = imageDescriptor.LocalColorTableSize * 3; localColorTable = ArrayPool <byte> .Shared.Rent(length); this.currentStream.Read(localColorTable, 0, length); } indices = ArrayPool <byte> .Shared.Rent(imageDescriptor.Width *imageDescriptor.Height); this.ReadFrameIndices(imageDescriptor, indices); this.ReadFrameColors(indices, localColorTable ?? this.globalColorTable, length, imageDescriptor); // Skip any remaining blocks this.Skip(0); } finally { if (localColorTable != null) { ArrayPool <byte> .Shared.Return(localColorTable); } ArrayPool <byte> .Shared.Return(indices); } }
/// <summary> /// Reads the frames colors, mapping indices to colors. /// </summary> /// <param name="indices">The indexed pixels.</param> /// <param name="colorTable">The color table containing the available colors.</param> /// <param name="descriptor">The <see cref="GifImageDescriptor"/></param> private unsafe void ReadFrameColors(byte[] indices, byte[] colorTable, GifImageDescriptor descriptor) { int imageWidth = this.logicalScreenDescriptor.Width; int imageHeight = this.logicalScreenDescriptor.Height; ImageFrame <TColor, TPacked> previousFrame = null; ImageFrame <TColor, TPacked> currentFrame = null; ImageBase <TColor, TPacked> image; if (this.nextFrame == null) { image = this.decodedImage; image.Quality = colorTable.Length / 3; // This initializes the image to become fully transparent because the alpha channel is zero. image.InitPixels(imageWidth, imageHeight); } else { if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) { previousFrame = this.nextFrame; } currentFrame = this.nextFrame.Clone(); image = currentFrame; this.RestoreToBackground(image); this.decodedImage.Frames.Add(currentFrame); } if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0) { image.FrameDelay = this.graphicsControlExtension.DelayTime; } int i = 0; int interlacePass = 0; // The interlace pass int interlaceIncrement = 8; // The interlacing line increment int interlaceY = 0; // The current interlaced line using (PixelAccessor <TColor, TPacked> pixelAccessor = image.Lock()) { using (PixelArea <TColor, TPacked> pixelRow = new PixelArea <TColor, TPacked>(imageWidth, ComponentOrder.XYZW)) { for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++) { // Check if this image is interlaced. int writeY; // the target y offset to write to if (descriptor.InterlaceFlag) { // If so then we read lines at predetermined offsets. // When an entire image height worth of offset lines has been read we consider this a pass. // With each pass the number of offset lines changes and the starting line changes. if (interlaceY >= descriptor.Height) { interlacePass++; switch (interlacePass) { case 1: interlaceY = 4; break; case 2: interlaceY = 2; interlaceIncrement = 4; break; case 3: interlaceY = 1; interlaceIncrement = 2; break; } } writeY = interlaceY + descriptor.Top; interlaceY += interlaceIncrement; } else { writeY = y; } pixelRow.Reset(); byte *pixelBase = pixelRow.PixelBase; for (int x = 0; x < descriptor.Width; x++) { int index = indices[i]; if (this.graphicsControlExtension == null || this.graphicsControlExtension.TransparencyFlag == false || this.graphicsControlExtension.TransparencyIndex != index) { int indexOffset = index * 3; *(pixelBase + 0) = colorTable[indexOffset]; *(pixelBase + 1) = colorTable[indexOffset + 1]; *(pixelBase + 2) = colorTable[indexOffset + 2]; *(pixelBase + 3) = 255; } i++; pixelBase += 4; } pixelAccessor.CopyFrom(pixelRow, writeY, descriptor.Left); } } } if (previousFrame != null) { this.nextFrame = previousFrame; return; } if (currentFrame == null) { this.nextFrame = this.decodedImage.ToFrame(); } else { this.nextFrame = currentFrame.Clone(); } if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground) { this.restoreArea = new Rectangle(descriptor.Left, descriptor.Top, descriptor.Width, descriptor.Height); } }
/// <summary> /// Reads the frames colors, mapping indices to colors. /// </summary> /// <param name="indices">The indexed pixels.</param> /// <param name="colorTable">The color table containing the available colors.</param> /// <param name="colorTableLength">The color table length.</param> /// <param name="descriptor">The <see cref="GifImageDescriptor"/></param> private unsafe void ReadFrameColors(byte[] indices, byte[] colorTable, int colorTableLength, GifImageDescriptor descriptor) { int imageWidth = this.logicalScreenDescriptor.Width; int imageHeight = this.logicalScreenDescriptor.Height; ImageFrame <TPixel> previousFrame = null; ImageFrame <TPixel> currentFrame = null; ImageBase <TPixel> image; if (this.previousFrame == null) { // This initializes the image to become fully transparent because the alpha channel is zero. this.image = new Image <TPixel>(this.configuration, imageWidth, imageHeight, this.metaData); this.SetFrameMetaData(this.metaData); image = this.image; } else { if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) { previousFrame = this.previousFrame; } currentFrame = this.previousFrame.Clone(); this.SetFrameMetaData(currentFrame.MetaData); image = currentFrame; this.RestoreToBackground(image); this.image.Frames.Add(currentFrame); } int i = 0; int interlacePass = 0; // The interlace pass int interlaceIncrement = 8; // The interlacing line increment int interlaceY = 0; // The current interlaced line for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++) { // Check if this image is interlaced. int writeY; // the target y offset to write to if (descriptor.InterlaceFlag) { // If so then we read lines at predetermined offsets. // When an entire image height worth of offset lines has been read we consider this a pass. // With each pass the number of offset lines changes and the starting line changes. if (interlaceY >= descriptor.Height) { interlacePass++; switch (interlacePass) { case 1: interlaceY = 4; break; case 2: interlaceY = 2; interlaceIncrement = 4; break; case 3: interlaceY = 1; interlaceIncrement = 2; break; } } writeY = interlaceY + descriptor.Top; interlaceY += interlaceIncrement; } else { writeY = y; } Span <TPixel> rowSpan = image.GetRowSpan(writeY); Rgba32 rgba = new Rgba32(0, 0, 0, 255); for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++) { int index = indices[i]; if (this.graphicsControlExtension == null || this.graphicsControlExtension.TransparencyFlag == false || this.graphicsControlExtension.TransparencyIndex != index) { int indexOffset = index * 3; ref TPixel pixel = ref rowSpan[x]; rgba.Rgb = colorTable.GetRgb24(indexOffset); pixel.PackFromRgba32(rgba); } i++; } }
/// <summary> /// Reads the frames colors, mapping indices to colors. /// </summary> /// <param name="indices">The indexed pixels.</param> /// <param name="colorTable">The color table containing the available colors.</param> /// <param name="colorTableLength">The color table length.</param> /// <param name="descriptor">The <see cref="GifImageDescriptor"/></param> private unsafe void ReadFrameColors(byte[] indices, byte[] colorTable, int colorTableLength, GifImageDescriptor descriptor) { int imageWidth = this.logicalScreenDescriptor.Width; int imageHeight = this.logicalScreenDescriptor.Height; ImageFrame <TColor> previousFrame = null; ImageFrame <TColor> currentFrame = null; ImageBase <TColor> image; if (this.previousFrame == null) { this.decodedImage.MetaData.Quality = colorTableLength / 3; // This initializes the image to become fully transparent because the alpha channel is zero. this.decodedImage.InitPixels(imageWidth, imageHeight); this.SetFrameDelay(this.decodedImage.MetaData); image = this.decodedImage; } else { if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) { previousFrame = this.previousFrame; } currentFrame = this.previousFrame.Clone(); this.SetFrameDelay(currentFrame.MetaData); image = currentFrame; this.RestoreToBackground(image); this.decodedImage.Frames.Add(currentFrame); } int i = 0; int interlacePass = 0; // The interlace pass int interlaceIncrement = 8; // The interlacing line increment int interlaceY = 0; // The current interlaced line using (PixelAccessor <TColor> pixelAccessor = image.Lock()) { for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++) { // Check if this image is interlaced. int writeY; // the target y offset to write to if (descriptor.InterlaceFlag) { // If so then we read lines at predetermined offsets. // When an entire image height worth of offset lines has been read we consider this a pass. // With each pass the number of offset lines changes and the starting line changes. if (interlaceY >= descriptor.Height) { interlacePass++; switch (interlacePass) { case 1: interlaceY = 4; break; case 2: interlaceY = 2; interlaceIncrement = 4; break; case 3: interlaceY = 1; interlaceIncrement = 2; break; } } writeY = interlaceY + descriptor.Top; interlaceY += interlaceIncrement; } else { writeY = y; } for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++) { int index = indices[i]; if (this.graphicsControlExtension == null || this.graphicsControlExtension.TransparencyFlag == false || this.graphicsControlExtension.TransparencyIndex != index) { int indexOffset = index * 3; TColor pixel = default(TColor); pixel.PackFromBytes(colorTable[indexOffset], colorTable[indexOffset + 1], colorTable[indexOffset + 2], 255); pixelAccessor[x, writeY] = pixel; } i++; } } } if (previousFrame != null) { this.previousFrame = previousFrame; return; } this.previousFrame = currentFrame == null?this.decodedImage.ToFrame() : currentFrame; if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground) { this.restoreArea = new Rectangle(descriptor.Left, descriptor.Top, descriptor.Width, descriptor.Height); } }
/// <summary> /// Reads the frames colors, mapping indices to colors. /// </summary> /// <param name="indices">The indexed pixels.</param> /// <param name="colorTable">The color table containing the available colors.</param> /// <param name="descriptor">The <see cref="GifImageDescriptor"/></param> private void ReadFrameColors(byte[] indices, byte[] colorTable, GifImageDescriptor descriptor) { int imageWidth = this.logicalScreenDescriptor.Width; int imageHeight = this.logicalScreenDescriptor.Height; if (this.currentFrame == null) { this.currentFrame = new TColor[imageWidth * imageHeight]; } TColor[] lastFrame = null; if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) { lastFrame = new TColor[imageWidth * imageHeight]; Array.Copy(this.currentFrame, lastFrame, lastFrame.Length); } int offset, i = 0; int interlacePass = 0; // The interlace pass int interlaceIncrement = 8; // The interlacing line increment int interlaceY = 0; // The current interlaced line for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++) { // Check if this image is interlaced. int writeY; // the target y offset to write to if (descriptor.InterlaceFlag) { // If so then we read lines at predetermined offsets. // When an entire image height worth of offset lines has been read we consider this a pass. // With each pass the number of offset lines changes and the starting line changes. if (interlaceY >= descriptor.Height) { interlacePass++; switch (interlacePass) { case 1: interlaceY = 4; break; case 2: interlaceY = 2; interlaceIncrement = 4; break; case 3: interlaceY = 1; interlaceIncrement = 2; break; } } writeY = interlaceY + descriptor.Top; interlaceY += interlaceIncrement; } else { writeY = y; } for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++) { offset = (writeY * imageWidth) + x; int index = indices[i]; if (this.graphicsControlExtension == null || this.graphicsControlExtension.TransparencyFlag == false || this.graphicsControlExtension.TransparencyIndex != index) { // Stored in r-> g-> b-> a order. int indexOffset = index * 3; TColor pixel = default(TColor); pixel.PackFromVector4(new Color(colorTable[indexOffset], colorTable[indexOffset + 1], colorTable[indexOffset + 2]).ToVector4()); this.currentFrame[offset] = pixel; } i++; } } TColor[] pixels = new TColor[imageWidth * imageHeight]; Array.Copy(this.currentFrame, pixels, pixels.Length); ImageBase <TColor, TPacked> currentImage; if (this.decodedImage.Pixels == null) { currentImage = this.decodedImage; currentImage.SetPixels(imageWidth, imageHeight, pixels); currentImage.Quality = colorTable.Length / 3; if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0) { this.decodedImage.FrameDelay = this.graphicsControlExtension.DelayTime; } } else { ImageFrame <TColor, TPacked> frame = new ImageFrame <TColor, TPacked>(); currentImage = frame; currentImage.SetPixels(imageWidth, imageHeight, pixels); currentImage.Quality = colorTable.Length / 3; if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0) { currentImage.FrameDelay = this.graphicsControlExtension.DelayTime; } this.decodedImage.Frames.Add(frame); } if (this.graphicsControlExtension != null) { if (this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground) { for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++) { for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++) { offset = (y * imageWidth) + x; // Stored in r-> g-> b-> a order. this.currentFrame[offset] = default(TColor); } } } else if (this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) { this.currentFrame = lastFrame; } } }