Browse Source

Read spec, fix gifs

af/merge-core
James Jackson-South 9 years ago
parent
commit
6208f27b8a
  1. 92
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  2. 43
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  3. 2
      src/ImageSharp/Formats/Gif/Sections/GifGraphicsControlExtension.cs
  4. 12
      src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs

92
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -191,7 +191,7 @@ namespace ImageSharp.Formats
byte packed = this.buffer[8];
GifImageDescriptor imageDescriptor = new GifImageDescriptor
var imageDescriptor = new GifImageDescriptor
{
Left = BitConverter.ToInt16(this.buffer, 0),
Top = BitConverter.ToInt16(this.buffer, 2),
@ -337,7 +337,7 @@ namespace ImageSharp.Formats
private void ReadFrameIndices(GifImageDescriptor imageDescriptor, byte[] indices)
{
int dataSize = this.currentStream.ReadByte();
using (LzwDecoder lzwDecoder = new LzwDecoder(this.currentStream))
using (var lzwDecoder = new LzwDecoder(this.currentStream))
{
lzwDecoder.DecodePixels(imageDescriptor.Width, imageDescriptor.Height, dataSize, indices);
}
@ -396,62 +396,60 @@ namespace ImageSharp.Formats
int interlaceIncrement = 8; // The interlacing line increment
int interlaceY = 0; // The current interlaced line
using (PixelAccessor<TPixel> pixelAccessor = image.Lock())
for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++)
{
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)
{
// 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)
{
// 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)
{
interlacePass++;
switch (interlacePass)
{
case 1:
interlaceY = 4;
break;
case 2:
interlaceY = 2;
interlaceIncrement = 4;
break;
case 3:
interlaceY = 1;
interlaceIncrement = 2;
break;
}
case 1:
interlaceY = 4;
break;
case 2:
interlaceY = 2;
interlaceIncrement = 4;
break;
case 3:
interlaceY = 1;
interlaceIncrement = 2;
break;
}
}
writeY = interlaceY + descriptor.Top;
writeY = interlaceY + descriptor.Top;
interlaceY += interlaceIncrement;
}
else
{
writeY = y;
}
interlaceY += interlaceIncrement;
}
else
{
writeY = y;
}
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++)
{
int index = indices[i];
Span<TPixel> rowSpan = image.GetRowSpan(writeY);
if (this.graphicsControlExtension == null ||
this.graphicsControlExtension.TransparencyFlag == false ||
this.graphicsControlExtension.TransparencyIndex != index)
{
int indexOffset = index * 3;
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++)
{
int index = indices[i];
TPixel pixel = default(TPixel);
pixel.PackFromBytes(colorTable[indexOffset], colorTable[indexOffset + 1], colorTable[indexOffset + 2], 255);
pixelAccessor[x, writeY] = pixel;
}
if (this.graphicsControlExtension == null ||
this.graphicsControlExtension.TransparencyFlag == false ||
this.graphicsControlExtension.TransparencyIndex != index)
{
int indexOffset = index * 3;
i++;
ref TPixel pixel = ref rowSpan[x];
pixel.PackFromBytes(colorTable[indexOffset], colorTable[indexOffset + 1], colorTable[indexOffset + 2], 255);
}
i++;
}
}
@ -492,7 +490,7 @@ namespace ImageSharp.Formats
}
else
{
using (PixelArea<TPixel> emptyRow = new PixelArea<TPixel>(this.restoreArea.Value.Width, ComponentOrder.Xyzw))
using (var emptyRow = new PixelArea<TPixel>(this.restoreArea.Value.Width, ComponentOrder.Xyzw))
{
using (PixelAccessor<TPixel> pixelAccessor = frame.Lock())
{

43
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -137,31 +137,20 @@ namespace ImageSharp.Formats
private int GetTransparentIndex<TPixel>(QuantizedImage<TPixel> quantized)
where TPixel : struct, IPixel<TPixel>
{
// Find the lowest alpha value and make it the transparent index.
int index = 255;
byte alpha = 255;
bool hasEmpty = false;
// Some images may have more than one quantized pixel returned with an alpha value of zero
// so we should always ignore if we have empty pixels present.
for (int i = 0; i < quantized.Palette.Length; i++)
// Transparent pixels are much more likely to be found at the end of a palette
int index = -1;
for (int i = quantized.Palette.Length - 1; i >= 0; i--)
{
quantized.Palette[i].ToXyzwBytes(this.buffer, 0);
if (!hasEmpty)
if (this.buffer[3] > 0)
{
if (this.buffer[0] == 0 && this.buffer[1] == 0 && this.buffer[2] == 0 && this.buffer[3] == 0)
{
alpha = this.buffer[3];
index = i;
hasEmpty = true;
}
if (this.buffer[3] < alpha)
{
alpha = this.buffer[3];
index = i;
}
continue;
}
else
{
index = i;
break;
}
}
@ -183,8 +172,8 @@ namespace ImageSharp.Formats
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The image to encode.</param>
/// <param name="writer">The writer to write to the stream with.</param>
/// <param name="tranparencyIndex">The transparency index to set the default background index to.</param>
private void WriteLogicalScreenDescriptor<TPixel>(Image<TPixel> image, EndianBinaryWriter writer, int tranparencyIndex)
/// <param name="transparencyIndex">The transparency index to set the default background index to.</param>
private void WriteLogicalScreenDescriptor<TPixel>(Image<TPixel> image, EndianBinaryWriter writer, int transparencyIndex)
where TPixel : struct, IPixel<TPixel>
{
var descriptor = new GifLogicalScreenDescriptor
@ -193,7 +182,7 @@ namespace ImageSharp.Formats
Height = (short)image.Height,
GlobalColorTableFlag = false, // TODO: Always false for now.
GlobalColorTableSize = this.bitDepth - 1,
BackgroundColorIndex = (byte)tranparencyIndex
BackgroundColorIndex = unchecked((byte)transparencyIndex)
};
writer.Write((ushort)descriptor.Width);
@ -286,8 +275,8 @@ namespace ImageSharp.Formats
var extension = new GifGraphicsControlExtension
{
DisposalMethod = metaData.DisposalMethod,
TransparencyFlag = true, // TODO: The spec here is unclear. Can we get away with this?
TransparencyIndex = transparencyIndex,
TransparencyFlag = transparencyIndex > -1,
TransparencyIndex = unchecked((byte)transparencyIndex),
DelayTime = metaData.FrameDelay
};
@ -306,7 +295,7 @@ namespace ImageSharp.Formats
writer.Write(field.Byte);
writer.Write((ushort)extension.DelayTime);
writer.Write((byte)extension.TransparencyIndex);
writer.Write(extension.TransparencyIndex);
writer.Write(GifConstants.Terminator);
}

2
src/ImageSharp/Formats/Gif/Sections/GifGraphicsControlExtension.cs

@ -29,7 +29,7 @@ namespace ImageSharp.Formats
/// The Transparency Index is such that when encountered, the corresponding pixel
/// of the display device is not modified and processing goes on to the next pixel.
/// </summary>
public int TransparencyIndex { get; set; }
public byte TransparencyIndex { get; set; }
/// <summary>
/// Gets or sets the delay time.

12
src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs

@ -36,7 +36,7 @@ namespace ImageSharp.Quantizers
/// <summary>
/// Maximum allowed color depth
/// </summary>
private int colors;
private byte colors;
/// <summary>
/// The reduced image palette
@ -58,7 +58,7 @@ namespace ImageSharp.Quantizers
/// <inheritdoc/>
public override QuantizedImage<TPixel> Quantize(ImageBase<TPixel> image, int maxColors)
{
this.colors = maxColors.Clamp(1, 255);
this.colors = (byte)maxColors.Clamp(1, 255);
this.octree = new Octree(this.GetBitsNeededForColorDepth(this.colors));
this.palette = null;
@ -123,7 +123,7 @@ namespace ImageSharp.Quantizers
/// <inheritdoc/>
protected override TPixel[] GetPalette()
{
return this.palette ?? (this.palette = this.octree.Palletize(Math.Max(this.colors, 1)));
return this.palette ?? (this.palette = this.octree.Palletize(Math.Max(this.colors, (byte)1)));
}
/// <summary>
@ -143,6 +143,12 @@ namespace ImageSharp.Quantizers
return this.GetClosestPixel(pixel, this.palette, this.colorMap);
}
pixel.ToXyzwBytes(this.pixelBuffer, 0);
if (this.pixelBuffer[3] == 0)
{
return this.colors;
}
return (byte)this.octree.GetPaletteIndex(pixel, this.pixelBuffer);
}

Loading…
Cancel
Save