|
|
|
@ -69,7 +69,7 @@ namespace ImageSharp.Formats |
|
|
|
this.Quantizer = this.options.Quantizer ?? new OctreeQuantizer<TPixel>(); |
|
|
|
|
|
|
|
// Do not use IDisposable pattern here as we want to preserve the stream.
|
|
|
|
EndianBinaryWriter writer = new EndianBinaryWriter(Endianness.LittleEndian, stream); |
|
|
|
var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream); |
|
|
|
|
|
|
|
// Ensure that quality can be set but has a fallback.
|
|
|
|
int quality = this.options.Quality > 0 ? this.options.Quality : image.MetaData.Quality; |
|
|
|
@ -82,7 +82,7 @@ namespace ImageSharp.Formats |
|
|
|
this.hasFrames = image.Frames.Any(); |
|
|
|
|
|
|
|
// Dithering when animating gifs is a bad idea as we introduce pixel tearing across frames.
|
|
|
|
IQuantizer<TPixel> ditheredQuantizer = (IQuantizer<TPixel>)this.Quantizer; |
|
|
|
var ditheredQuantizer = (IQuantizer<TPixel>)this.Quantizer; |
|
|
|
ditheredQuantizer.Dither = !this.hasFrames; |
|
|
|
|
|
|
|
QuantizedImage<TPixel> quantized = ditheredQuantizer.Quantize(image, quality); |
|
|
|
@ -96,7 +96,7 @@ namespace ImageSharp.Formats |
|
|
|
this.WriteLogicalScreenDescriptor(image, writer, index); |
|
|
|
|
|
|
|
// Write the first frame.
|
|
|
|
this.WriteGraphicalControlExtension(image, writer, index); |
|
|
|
this.WriteGraphicalControlExtension(image.MetaData, writer, index); |
|
|
|
this.WriteComments(image, writer); |
|
|
|
this.WriteImageDescriptor(image, writer); |
|
|
|
this.WriteColorTable(quantized, writer); |
|
|
|
@ -113,7 +113,7 @@ namespace ImageSharp.Formats |
|
|
|
ImageFrame<TPixel> frame = image.Frames[i]; |
|
|
|
QuantizedImage<TPixel> quantizedFrame = ditheredQuantizer.Quantize(frame, quality); |
|
|
|
|
|
|
|
this.WriteGraphicalControlExtension(frame, writer, this.GetTransparentIndex(quantizedFrame)); |
|
|
|
this.WriteGraphicalControlExtension(frame.MetaData, writer, this.GetTransparentIndex(quantizedFrame)); |
|
|
|
this.WriteImageDescriptor(frame, writer); |
|
|
|
this.WriteColorTable(quantizedFrame, writer); |
|
|
|
this.WriteImageData(quantizedFrame, writer); |
|
|
|
@ -187,7 +187,7 @@ namespace ImageSharp.Formats |
|
|
|
private void WriteLogicalScreenDescriptor<TPixel>(Image<TPixel> image, EndianBinaryWriter writer, int tranparencyIndex) |
|
|
|
where TPixel : struct, IPixel<TPixel> |
|
|
|
{ |
|
|
|
GifLogicalScreenDescriptor descriptor = new GifLogicalScreenDescriptor |
|
|
|
var descriptor = new GifLogicalScreenDescriptor |
|
|
|
{ |
|
|
|
Width = (short)image.Width, |
|
|
|
Height = (short)image.Height, |
|
|
|
@ -199,7 +199,7 @@ namespace ImageSharp.Formats |
|
|
|
writer.Write((ushort)descriptor.Width); |
|
|
|
writer.Write((ushort)descriptor.Height); |
|
|
|
|
|
|
|
PackedField field = default(PackedField); |
|
|
|
var field = default(PackedField); |
|
|
|
field.SetBit(0, descriptor.GlobalColorTableFlag); // 1 : Global color table flag = 1 || 0 (GCT used/ not used)
|
|
|
|
field.SetBits(1, 3, descriptor.GlobalColorTableSize); // 2-4 : color resolution
|
|
|
|
field.SetBit(4, false); // 5 : GCT sort flag = 0
|
|
|
|
@ -251,7 +251,7 @@ namespace ImageSharp.Formats |
|
|
|
private void WriteComments<TPixel>(Image<TPixel> image, EndianBinaryWriter writer) |
|
|
|
where TPixel : struct, IPixel<TPixel> |
|
|
|
{ |
|
|
|
if (this.options.IgnoreMetadata == true) |
|
|
|
if (this.options.IgnoreMetadata) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
@ -278,44 +278,15 @@ namespace ImageSharp.Formats |
|
|
|
/// <summary>
|
|
|
|
/// Writes the graphics control extension to the stream.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
|
|
/// <param name="image">The <see cref="Image{TPixel}"/> to encode.</param>
|
|
|
|
/// <param name="writer">The stream to write to.</param>
|
|
|
|
/// <param name="transparencyIndex">The index of the color in the color palette to make transparent.</param>
|
|
|
|
private void WriteGraphicalControlExtension<TPixel>(Image<TPixel> image, EndianBinaryWriter writer, int transparencyIndex) |
|
|
|
where TPixel : struct, IPixel<TPixel> |
|
|
|
{ |
|
|
|
this.WriteGraphicalControlExtension(image, image.MetaData, writer, transparencyIndex); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Writes the graphics control extension to the stream.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
|
|
/// <param name="imageFrame">The <see cref="ImageFrame{TPixel}"/> to encode.</param>
|
|
|
|
/// <param name="writer">The stream to write to.</param>
|
|
|
|
/// <param name="transparencyIndex">The index of the color in the color palette to make transparent.</param>
|
|
|
|
private void WriteGraphicalControlExtension<TPixel>(ImageFrame<TPixel> imageFrame, EndianBinaryWriter writer, int transparencyIndex) |
|
|
|
where TPixel : struct, IPixel<TPixel> |
|
|
|
{ |
|
|
|
this.WriteGraphicalControlExtension(imageFrame, imageFrame.MetaData, writer, transparencyIndex); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Writes the graphics control extension to the stream.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
|
|
/// <param name="image">The <see cref="ImageBase{TPixel}"/> to encode.</param>
|
|
|
|
/// <param name="metaData">The metadata of the image or frame.</param>
|
|
|
|
/// <param name="writer">The stream to write to.</param>
|
|
|
|
/// <param name="transparencyIndex">The index of the color in the color palette to make transparent.</param>
|
|
|
|
private void WriteGraphicalControlExtension<TPixel>(ImageBase<TPixel> image, IMetaData metaData, EndianBinaryWriter writer, int transparencyIndex) |
|
|
|
where TPixel : struct, IPixel<TPixel> |
|
|
|
private void WriteGraphicalControlExtension(IMetaData metaData, EndianBinaryWriter writer, int transparencyIndex) |
|
|
|
{ |
|
|
|
GifGraphicsControlExtension extension = new GifGraphicsControlExtension() |
|
|
|
var extension = new GifGraphicsControlExtension |
|
|
|
{ |
|
|
|
DisposalMethod = metaData.DisposalMethod, |
|
|
|
TransparencyFlag = transparencyIndex < 255, |
|
|
|
TransparencyFlag = true, // TODO: The spec here is unclear. Can we get away with this?
|
|
|
|
TransparencyIndex = transparencyIndex, |
|
|
|
DelayTime = metaData.FrameDelay |
|
|
|
}; |
|
|
|
@ -326,7 +297,7 @@ namespace ImageSharp.Formats |
|
|
|
this.buffer[2] = 4; |
|
|
|
writer.Write(this.buffer, 0, 3); |
|
|
|
|
|
|
|
PackedField field = default(PackedField); |
|
|
|
var field = default(PackedField); |
|
|
|
field.SetBits(3, 3, (int)extension.DisposalMethod); // 1-3 : Reserved, 4-6 : Disposal
|
|
|
|
|
|
|
|
// TODO: Allow this as an option.
|
|
|
|
@ -356,7 +327,7 @@ namespace ImageSharp.Formats |
|
|
|
writer.Write((ushort)image.Width); |
|
|
|
writer.Write((ushort)image.Height); |
|
|
|
|
|
|
|
PackedField field = default(PackedField); |
|
|
|
var field = default(PackedField); |
|
|
|
field.SetBit(0, true); // 1: Local color table flag = 1 (LCT used)
|
|
|
|
field.SetBit(1, false); // 2: Interlace flag 0
|
|
|
|
field.SetBit(2, false); // 3: Sort flag 0
|
|
|
|
@ -409,7 +380,7 @@ namespace ImageSharp.Formats |
|
|
|
private void WriteImageData<TPixel>(QuantizedImage<TPixel> image, EndianBinaryWriter writer) |
|
|
|
where TPixel : struct, IPixel<TPixel> |
|
|
|
{ |
|
|
|
using (LzwEncoder encoder = new LzwEncoder(image.Pixels, (byte)this.bitDepth)) |
|
|
|
using (var encoder = new LzwEncoder(image.Pixels, (byte)this.bitDepth)) |
|
|
|
{ |
|
|
|
encoder.Encode(writer.BaseStream); |
|
|
|
} |
|
|
|
|