Browse Source

remove old unused type

pull/2894/head
James Jackson-South 1 year ago
parent
commit
e733495628
  1. 475
      src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs

475
src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs

@ -654,479 +654,4 @@ public struct OctreeQuantizer<TPixel> : IQuantizer<TPixel>
}
}
}
/// <summary>
/// Class which does the actual quantization.
/// </summary>
private sealed class Octree2
{
/// <summary>
/// The root of the Octree2
/// </summary>
private readonly OctreeNode root;
/// <summary>
/// Maximum number of significant bits in the image
/// </summary>
private readonly int maxColorBits;
/// <summary>
/// The threshold for transparent colors.
/// </summary>
private readonly int transparencyThreshold;
/// <summary>
/// Store the last node quantized
/// </summary>
private OctreeNode? previousNode;
/// <summary>
/// Cache the previous color quantized
/// </summary>
private Rgba32 previousColor;
/// <summary>
/// Initializes a new instance of the <see cref="Octree2"/> class.
/// </summary>
/// <param name="maxColorBits">
/// The maximum number of significant bits in the image
/// </param>
/// <param name="transparencyThreshold">The threshold for transparent colors.</param>
public Octree2(int maxColorBits, int transparencyThreshold)
{
this.maxColorBits = maxColorBits;
this.transparencyThreshold = transparencyThreshold;
this.Leaves = 0;
this.ReducibleNodes = new OctreeNode[9];
this.root = new OctreeNode(0, this.maxColorBits, this);
this.previousColor = default;
this.previousNode = null;
}
/// <summary>
/// Gets or sets the number of leaves in the tree
/// </summary>
public int Leaves
{
[MethodImpl(InliningOptions.ShortMethod)]
get;
[MethodImpl(InliningOptions.ShortMethod)]
set;
}
/// <summary>
/// Gets the array of reducible nodes
/// </summary>
private OctreeNode?[] ReducibleNodes
{
[MethodImpl(InliningOptions.ShortMethod)]
get;
}
/// <summary>
/// Add a given color value to the Octree2
/// </summary>
/// <param name="color">The color to add.</param>
public void AddColor(Rgba32 color)
{
if (color.A < this.transparencyThreshold)
{
color.A = 0;
}
// Check if this request is for the same color as the last
if (this.previousColor.Equals(color))
{
// If so, check if I have a previous node setup.
if (this.previousNode is null)
{
this.previousColor = color;
this.root.AddColor(color, this.maxColorBits, 0, this);
}
else
{
// Just update the previous node
this.previousNode.Increment(color, this);
}
}
else
{
this.previousColor = color;
this.root.AddColor(color, this.maxColorBits, 0, this);
}
}
/// <summary>
/// Convert the nodes in the Octree2 to a palette with a maximum of colorCount colors
/// </summary>
/// <param name="palette">The palette to fill.</param>
/// <param name="colorCount">The maximum number of colors</param>
/// <param name="paletteIndex">The palette index, used to calculate the final size of the palette.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public void Palettize(Span<TPixel> palette, int colorCount, ref int paletteIndex)
{
while (this.Leaves > colorCount)
{
this.Reduce();
}
this.root.ConstructPalette(palette, ref paletteIndex);
}
/// <summary>
/// Get the palette index for the passed color
/// </summary>
/// <param name="color">The color to match.</param>
/// <returns>
/// The <see cref="int"/> index.
/// </returns>
[MethodImpl(InliningOptions.ShortMethod)]
public int GetPaletteIndex(TPixel color)
=> this.root.GetPaletteIndex(color.ToRgba32(), 0);
/// <summary>
/// Keep track of the previous node that was quantized
/// </summary>
/// <param name="node">
/// The node last quantized
/// </param>
[MethodImpl(InliningOptions.ShortMethod)]
public void TrackPrevious(OctreeNode node) => this.previousNode = node;
/// <summary>
/// Reduce the depth of the tree
/// </summary>
private void Reduce()
{
// Find the deepest level containing at least one reducible node
int index = this.maxColorBits - 1;
while ((index > 0) && (this.ReducibleNodes[index] is null))
{
index--;
}
// Reduce the node most recently added to the list at level 'index'
OctreeNode node = this.ReducibleNodes[index]!;
this.ReducibleNodes[index] = node.NextReducible;
// Decrement the leaf count after reducing the node
this.Leaves -= node.Reduce(this);
// And just in case I've reduced the last color to be added, and the next color to
// be added is the same, invalidate the previousNode...
this.previousNode = null;
}
/// <summary>
/// Class which encapsulates each node in the tree
/// </summary>
public sealed class OctreeNode
{
/// <summary>
/// Pointers to any child nodes
/// </summary>
private readonly OctreeNode?[]? children;
/// <summary>
/// Flag indicating that this is a leaf node
/// </summary>
private bool leaf;
/// <summary>
/// Number of pixels in this node
/// </summary>
private int pixelCount;
/// <summary>
/// Red component
/// </summary>
private int red;
/// <summary>
/// Green Component
/// </summary>
private int green;
/// <summary>
/// Blue component
/// </summary>
private int blue;
/// <summary>
/// Alpha component
/// </summary>
private int alpha;
/// <summary>
/// The index of this node in the palette
/// </summary>
private int paletteIndex;
/// <summary>
/// Initializes a new instance of the <see cref="OctreeNode"/> class.
/// </summary>
/// <param name="level">The level in the tree = 0 - 7.</param>
/// <param name="colorBits">The number of significant color bits in the image.</param>
/// <param name="octree">The tree to which this node belongs.</param>
public OctreeNode(int level, int colorBits, Octree2 octree)
{
// Construct the new node
this.leaf = level == colorBits;
this.red = this.green = this.blue = this.alpha = 0;
this.pixelCount = 0;
// If a leaf, increment the leaf count
if (this.leaf)
{
octree.Leaves++;
this.NextReducible = null;
this.children = null;
}
else
{
// Otherwise add this to the reducible nodes
this.NextReducible = octree.ReducibleNodes[level];
octree.ReducibleNodes[level] = this;
this.children = new OctreeNode[16];
}
}
/// <summary>
/// Gets the next reducible node
/// </summary>
public OctreeNode? NextReducible
{
[MethodImpl(InliningOptions.ShortMethod)]
get;
}
/// <summary>
/// Add a color into the tree
/// </summary>
/// <param name="color">The color to add.</param>
/// <param name="colorBits">The number of significant color bits.</param>
/// <param name="level">The level in the tree.</param>
/// <param name="octree">The tree to which this node belongs.</param>
public void AddColor(Rgba32 color, int colorBits, int level, Octree2 octree)
{
// Update the color information if this is a leaf
if (this.leaf)
{
this.Increment(color, octree);
// Setup the previous node
octree.TrackPrevious(this);
}
else
{
// Go to the next level down in the tree
int index = GetColorIndex(color, level);
OctreeNode? child = this.children![index];
if (child is null)
{
// Create a new child node and store it in the array
child = new OctreeNode(level + 1, colorBits, octree);
this.children[index] = child;
}
// Add the color to the child node
child.AddColor(color, colorBits, level + 1, octree);
}
}
/// <summary>
/// Reduce this node by removing all of its children.
/// </summary>
/// <returns>The number of leaves removed</returns>
/// <param name="octree">The tree to which this node belongs.</param>
public int Reduce(Octree2 octree)
{
if (this.leaf)
{
return 1;
}
int childNodes = 0;
int sumRed = 0, sumGreen = 0, sumBlue = 0, sumAlpha = 0, pixelCount = 0;
// Loop through all children.
for (int index = 0; index < this.children!.Length; index++)
{
OctreeNode? child = this.children[index];
if (child != null)
{
childNodes++;
sumRed += child.red;
sumGreen += child.green;
sumBlue += child.blue;
sumAlpha += child.alpha;
pixelCount += child.pixelCount;
// Remove the child reference.
this.children[index] = null;
}
}
if (pixelCount > 0)
{
int offset = pixelCount >> 1;
this.red = sumRed;
this.green = sumGreen;
this.blue = sumBlue;
this.alpha = ((sumAlpha + offset) / pixelCount < octree.transparencyThreshold) ? 0 : sumAlpha;
this.pixelCount = pixelCount;
}
else
{
this.red = this.green = this.blue = this.alpha = 0;
this.pixelCount = 0;
}
// Convert this node into a leaf.
this.leaf = true;
// Return the number of nodes merged (for decrementing the leaf count).
return childNodes - 1;
}
/// <summary>
/// Traverse the tree, building up the color palette
/// </summary>
/// <param name="palette">The palette</param>
/// <param name="index">The current palette index</param>
[MethodImpl(InliningOptions.ColdPath)]
public void ConstructPalette(Span<TPixel> palette, ref int index)
{
if (this.leaf)
{
// Set the color of the palette entry
Vector4 sum = new(this.red, this.green, this.blue, this.alpha);
Vector4 offset = new(this.pixelCount >> 1);
Vector4 vector = Vector4.Clamp(
(sum + offset) / this.pixelCount,
Vector4.Zero,
new Vector4(255));
palette[index] = TPixel.FromRgba32(new Rgba32((byte)vector.X, (byte)vector.Y, (byte)vector.Z, (byte)vector.W));
// Consume the next palette index
this.paletteIndex = index++;
}
else
{
// Loop through children looking for leaves
for (int i = 0; i < this.children!.Length; i++)
{
this.children[i]?.ConstructPalette(palette, ref index);
}
}
}
/// <summary>
/// Return the palette index for the passed color
/// </summary>
/// <param name="pixel">The pixel data.</param>
/// <param name="level">The level.</param>
/// <returns>
/// The <see cref="int"/> representing the index of the pixel in the palette.
/// </returns>
[MethodImpl(InliningOptions.ColdPath)]
public int GetPaletteIndex(Rgba32 pixel, int level)
{
if (this.leaf)
{
return this.paletteIndex;
}
int colorIndex = GetColorIndex(pixel, level);
OctreeNode? child = this.children![colorIndex];
int index = -1;
if (child != null)
{
index = child.GetPaletteIndex(pixel, level + 1);
}
else
{
// Check other children.
for (int i = 0; i < this.children.Length; i++)
{
child = this.children[i];
if (child != null)
{
int childIndex = child.GetPaletteIndex(pixel, level + 1);
if (childIndex != -1)
{
return childIndex;
}
}
}
}
return index;
}
/// <summary>
/// Gets the color index at the given level.
/// </summary>
/// <param name="color">The color.</param>
/// <param name="level">The node level.</param>
/// <returns>The <see cref="int"/> index.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
private static int GetColorIndex(Rgba32 color, int level)
{
int shift = 7 - level;
byte mask = (byte)(1 << shift);
// Compute luminance of the RGB channels.
int luminance = ColorNumerics.Get8BitBT709Luminance(color.R, color.G, color.B);
// Shift the threshold (arbitrary) right by the current level.
// This allows us to partition the RGB space into smaller and smaller cubes
// with increasing accuracy for the alpha component.
int darkThreshold = 24 >> level;
// For fully opaque and bright pixels, ignore the alpha bit to achieve finer RGB partitioning.
// For dark pixels, include the alpha bit so that dark drop shadows remain distinct.
if (color.A == 255 && luminance > darkThreshold)
{
int rBits = ((color.R & mask) >> shift) << 2;
int gBits = ((color.G & mask) >> shift) << 1;
int bBits = (color.B & mask) >> shift;
return rBits | gBits | bBits;
}
else
{
int aBits = ((color.A & mask) >> shift) << 3;
int rBits = ((color.R & mask) >> shift) << 2;
int gBits = ((color.G & mask) >> shift) << 1;
int bBits = (color.B & mask) >> shift;
return aBits | rBits | gBits | bBits;
}
}
/// <summary>
/// Increment the color count and add to the color information
/// </summary>
/// <param name="color">The pixel to add.</param>
/// <param name="octree">The parent octree.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public void Increment(Rgba32 color, Octree2 octree)
{
this.pixelCount++;
this.red += color.R;
this.green += color.G;
this.blue += color.B;
int sumAlpha = this.alpha + color.A;
int offset = this.pixelCount >> 1;
this.alpha = ((sumAlpha + offset) / this.pixelCount < octree.transparencyThreshold) ? 0 : sumAlpha;
}
}
}
}

Loading…
Cancel
Save