Browse Source

Octree Quantizer now only supports 1 alpha value

... As nature intended. My Existing hack was incorrect and led to
strange results.
pull/173/head
James Jackson-South 9 years ago
parent
commit
3b97c0db91
  1. 70
      src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs
  2. 5
      src/ImageSharp/Quantizers/Options/Quantization.cs
  3. 3
      tests/ImageSharp.Tests/FileTestBase.cs
  4. 1
      tests/ImageSharp.Tests/TestImages.cs
  5. BIN
      tests/ImageSharp.Tests/TestImages/Formats/Gif/trans.gif

70
src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs

@ -58,18 +58,12 @@ namespace ImageSharp.Quantizers
public override QuantizedImage<TColor> Quantize(ImageBase<TColor> image, int maxColors)
{
this.colors = maxColors.Clamp(1, 255);
this.octree = new Octree(this.GetBitsNeededForColorDepth(maxColors));
this.octree = new Octree(this.GetBitsNeededForColorDepth(this.colors));
return base.Quantize(image, maxColors);
return base.Quantize(image, this.colors);
}
/// <summary>
/// Execute a second pass through the bitmap
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="output">The output pixel array</param>
/// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height in pixels of the image</param>
/// <inheritdoc/>
protected override void SecondPass(PixelAccessor<TColor> source, byte[] output, int width, int height)
{
// Load up the values for the first pixel. We can use these to speed up the second
@ -115,36 +109,17 @@ namespace ImageSharp.Quantizers
}
}
/// <summary>
/// Process the pixel in the first pass of the algorithm
/// </summary>
/// <param name="pixel">
/// The pixel to quantize
/// </param>
/// <remarks>
/// This function need only be overridden if your quantize algorithm needs two passes,
/// such as an Octree quantizer.
/// </remarks>
/// <inheritdoc/>
protected override void InitialQuantizePixel(TColor pixel)
{
// Add the color to the Octree
this.octree.AddColor(pixel, this.pixelBuffer);
}
/// <summary>
/// Retrieve the palette for the quantized image.
/// </summary>
/// <returns>
/// The new color palette
/// </returns>
/// <inheritdoc/>
protected override TColor[] GetPalette()
{
if (this.palette == null)
{
this.palette = this.octree.Palletize(Math.Max(this.colors, 1));
}
return this.palette;
return this.palette ?? (this.palette = this.octree.Palletize(Math.Max(this.colors, 1)));
}
/// <summary>
@ -175,6 +150,7 @@ namespace ImageSharp.Quantizers
/// <returns>
/// The <see cref="int"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int GetBitsNeededForColorDepth(int colorCount)
{
return (int)Math.Ceiling(Math.Log(colorCount, 2));
@ -189,7 +165,7 @@ namespace ImageSharp.Quantizers
/// Mask used when getting the appropriate pixels for a given node
/// </summary>
// ReSharper disable once StaticMemberInGenericType
private static readonly int[] Mask = { 0x100, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
private static readonly int[] Mask = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
/// <summary>
/// The root of the Octree
@ -379,11 +355,6 @@ namespace ImageSharp.Quantizers
/// </summary>
private int blue;
/// <summary>
/// Alpha component
/// </summary>
private int alpha;
/// <summary>
/// The index of this node in the palette
/// </summary>
@ -406,7 +377,7 @@ namespace ImageSharp.Quantizers
// Construct the new node
this.leaf = level == colorBits;
this.red = this.green = this.blue = this.alpha = 0;
this.red = this.green = this.blue = 0;
this.pixelCount = 0;
// If a leaf, increment the leaf count
@ -454,10 +425,9 @@ namespace ImageSharp.Quantizers
int shift = 7 - level;
pixel.ToXyzwBytes(buffer, 0);
int index = ((buffer[3] & Mask[0]) >> (shift - 3)) |
((buffer[2] & Mask[level + 1]) >> (shift - 2)) |
((buffer[1] & Mask[level + 1]) >> (shift - 1)) |
((buffer[0] & Mask[level + 1]) >> shift);
int index = ((buffer[2] & Mask[level]) >> (shift - 2)) |
((buffer[1] & Mask[level]) >> (shift - 1)) |
((buffer[0] & Mask[level]) >> shift);
OctreeNode child = this.children[index];
@ -479,7 +449,7 @@ namespace ImageSharp.Quantizers
/// <returns>The number of leaves removed</returns>
public int Reduce()
{
this.red = this.green = this.blue = this.alpha = 0;
this.red = this.green = this.blue = 0;
int childNodes = 0;
// Loop through all children and add their information to this node
@ -490,7 +460,6 @@ namespace ImageSharp.Quantizers
this.red += this.children[index].red;
this.green += this.children[index].green;
this.blue += this.children[index].blue;
this.alpha += this.children[index].alpha;
this.pixelCount += this.children[index].pixelCount;
++childNodes;
this.children[index] = null;
@ -517,11 +486,10 @@ namespace ImageSharp.Quantizers
byte r = (this.red / this.pixelCount).ToByte();
byte g = (this.green / this.pixelCount).ToByte();
byte b = (this.blue / this.pixelCount).ToByte();
byte a = (this.alpha / this.pixelCount).ToByte();
// And set the color of the palette entry
TColor pixel = default(TColor);
pixel.PackFromBytes(r, g, b, a);
pixel.PackFromBytes(r, g, b, 255);
palette[index] = pixel;
// Consume the next palette index
@ -558,10 +526,9 @@ namespace ImageSharp.Quantizers
int shift = 7 - level;
pixel.ToXyzwBytes(buffer, 0);
int pixelIndex = ((buffer[3] & Mask[0]) >> (shift - 3)) |
((buffer[2] & Mask[level + 1]) >> (shift - 2)) |
((buffer[1] & Mask[level + 1]) >> (shift - 1)) |
((buffer[0] & Mask[level + 1]) >> shift);
int pixelIndex = ((buffer[2] & Mask[level]) >> (shift - 2)) |
((buffer[1] & Mask[level]) >> (shift - 1)) |
((buffer[0] & Mask[level]) >> shift);
if (this.children[pixelIndex] != null)
{
@ -588,9 +555,8 @@ namespace ImageSharp.Quantizers
this.red += buffer[0];
this.green += buffer[1];
this.blue += buffer[2];
this.alpha += buffer[3];
}
}
}
}
}
}

5
src/ImageSharp/Quantizers/Options/Quantization.cs

@ -12,17 +12,20 @@ namespace ImageSharp
{
/// <summary>
/// An adaptive Octree quantizer. Fast with good quality.
/// The quantizer only supports a single alpha value.
/// </summary>
Octree,
/// <summary>
/// Xiaolin Wu's Color Quantizer which generates high quality output.
/// The quantizer supports multiple alpha values.
/// </summary>
Wu,
/// <summary>
/// Palette based, Uses the collection of web-safe colors by default.
/// The quantizer supports multiple alpha values.
/// </summary>
Palette
}
}
}

3
tests/ImageSharp.Tests/FileTestBase.cs

@ -28,7 +28,7 @@ namespace ImageSharp.Tests
// TestFile.Create(TestImages.Jpeg.Baseline.GammaDalaiLamaGray), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Jpeg.Progressive.Bad.BadEOF), // Perf: Enable for local testing only
TestFile.Create(TestImages.Bmp.Car),
// TestFile.Create(TestImages.Bmp.Neg_height), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Bmp.NegHeight), // Perf: Enable for local testing only
TestFile.Create(TestImages.Png.Splash),
// TestFile.Create(TestImages.Png.ChunkLength1), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Png.ChunkLength2), // Perf: Enable for local testing only
@ -46,6 +46,7 @@ namespace ImageSharp.Tests
// TestFile.Create(TestImages.Png.P1), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Png.Pd), // Perf: Enable for local testing only
TestFile.Create(TestImages.Gif.Rings),
// TestFile.Create(TestImages.Gif.Trans), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Gif.Cheers), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Gif.Giphy) // Perf: Enable for local testing only
};

1
tests/ImageSharp.Tests/TestImages.cs

@ -103,6 +103,7 @@ namespace ImageSharp.Tests
public const string Rings = "Gif/rings.gif";
public const string Giphy = "Gif/giphy.gif";
public const string Cheers = "Gif/cheers.gif";
public const string Trans = "Gif/trans.gif";
}
}
}

BIN
tests/ImageSharp.Tests/TestImages/Formats/Gif/trans.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Loading…
Cancel
Save