Skip to content

Commit

Permalink
OptimizedPaletteQuantizer: Allowing up to 64K colors, new ConfigureBi…
Browse files Browse the repository at this point in the history
…tLevel method, optimizations
  • Loading branch information
koszeggy committed Jan 18, 2022
1 parent 7a4b290 commit 6f6fbfc
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ private sealed class UnmanagedCustomBitmapDataRowIndexed : UnmanagedBitmapDataRo

#region Constructors

[SecurityCritical]
public UnmanagedCustomBitmapDataIndexed(IntPtr buffer, Size size, int stride, PixelFormat pixelFormat,
Func<ICustomBitmapDataRow, int, int> rowGetColorIndex, Action<ICustomBitmapDataRow, int, int> rowSetColorIndex,
Palette? palette, Func<Palette, bool>? trySetPaletteCallback, Action? disposeCallback)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
///////////////////////////////////////////////////////////////////////////////
// File: OptimizedPaletteQuantizer.MedianCut.cs
///////////////////////////////////////////////////////////////////////////////
// Copyright (C) KGy SOFT, 2005-2021 - All Rights Reserved
// Copyright (C) KGy SOFT, 2005-2022 - All Rights Reserved
//
// You should have received a copy of the LICENSE file at the top-level
// directory of this distribution.
Expand Down Expand Up @@ -41,28 +41,62 @@ private enum ColorComponent { R, G, B }

private sealed class ColorBucket
{
private static readonly IComparer<Color32> redSorter = new RedComparer();
private static readonly IComparer<Color32> greenSorter = new GreenComparer();
private static readonly IComparer<Color32> blueSorter = new BlueComparer();

#region Nested Classes

#region RedComparer class

private sealed class RedComparer : IComparer<Color32>
{
public int Compare(Color32 a, Color32 b) => ((a.R << 16) | (a.G << 8) | a.B) - ((b.R << 16) | (b.G << 8) | b.B);
#region Methods

public int Compare(Color32 a, Color32 b) => a.R - b.R;

#endregion
}

#endregion

#region GreenComparer class

private sealed class GreenComparer : IComparer<Color32>
{
public int Compare(Color32 a, Color32 b) => ((a.G << 16) | (a.R << 8) | a.B) - ((b.G << 16) | (b.R << 8) | b.B);
#region Methods

public int Compare(Color32 a, Color32 b) => a.G - b.G;

#endregion
}

#endregion

#region BlueComparer class

private sealed class BlueComparer : IComparer<Color32>
{
public int Compare(Color32 a, Color32 b) => ((a.B << 16) | (a.G << 8) | a.R) - ((b.B << 16) | (b.G << 8) | b.R);
#region Methods

public int Compare(Color32 a, Color32 b) => a.B - b.B;

#endregion
}

#endregion

#endregion

#region Fields

#region Static Fields

private static readonly IComparer<Color32> redSorter = new RedComparer();
private static readonly IComparer<Color32> greenSorter = new GreenComparer();
private static readonly IComparer<Color32> blueSorter = new BlueComparer();

#endregion

#region Instance Fields

private readonly List<Color32> colors;

private int rMin;
Expand All @@ -74,6 +108,8 @@ private sealed class BlueComparer : IComparer<Color32>

#endregion

#endregion

#region Properties

internal int Count => colors.Count;
Expand Down Expand Up @@ -159,7 +195,7 @@ internal void Split(ColorComponent component, ColorBucketCollection buckets, ref
int medianIndex = colors.Count >> 1;

// single color check is correct because we sorted by all of the components
bool isLeftSingleColor = colors[0] == colors[medianIndex - 1];
bool isLeftSingleColor = colors[0] == colors[medianIndex - 1];
bool isRightSingleColor = colors[medianIndex] == colors[colors.Count - 1];
ColorBucket? left = isLeftSingleColor ? null : new ColorBucket(medianIndex);
ColorBucket? right = isRightSingleColor ? null : new ColorBucket(colors.Count - medianIndex);
Expand Down Expand Up @@ -219,7 +255,7 @@ internal void Split(ColorComponent component, ColorBucketCollection buckets, ref
private sealed class ColorBucketCollection
{
#region Fields

private readonly int maxColors;
private readonly CircularList<ColorBucket> buckets;
private readonly HashSet<Color32> finalColors = new HashSet<Color32>();
Expand Down Expand Up @@ -330,12 +366,12 @@ internal bool SplitBuckets(IAsyncContext context, ref int index)

#endregion


#region Methods

public void Initialize(int requestedColors, IBitmapData source)
public void Initialize(int requestedColors, byte? bitLevel, IBitmapData source)
{
maxColors = requestedColors;
int maxLevels = 1 << (bitLevel ?? 8);
maxColors = Math.Min(requestedColors, maxLevels * maxLevels * maxLevels);
root = new ColorBucket(source.Width * source.Height);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
///////////////////////////////////////////////////////////////////////////////
// File: OptimizedPaletteQuantizer.Octree.cs
///////////////////////////////////////////////////////////////////////////////
// Copyright (C) KGy SOFT, 2005-2021 - All Rights Reserved
// Copyright (C) KGy SOFT, 2005-2022 - All Rights Reserved
//
// You should have received a copy of the LICENSE file at the top-level
// directory of this distribution.
Expand Down Expand Up @@ -55,7 +55,7 @@ internal int DeepPixelCount
if (children == null)
return result;

// Adding also the direct children because reducing the tree starts at level BPP - 2.
// Adding also the direct children because reducing the tree starts at level levelCount - 2.
// And due to reducing no more than two levels can have non-empty nodes.
for (int index = 0; index < 8; index++)
{
Expand Down Expand Up @@ -83,7 +83,7 @@ internal int DeepPixelCount
internal OctreeNode(int level, OctreeQuantizer parent)
{
this.parent = parent;
Debug.Assert(level < parent.bpp);
Debug.Assert(level < parent.levelCount);

if (level >= 0)
parent.levels[level].Add(this);
Expand All @@ -98,7 +98,7 @@ internal OctreeNode(int level, OctreeQuantizer parent)
internal bool AddColor(Color32 color, int level)
{
// In the populating phase all colors are summed up in leaves at deepest level.
if (level == parent.bpp)
if (level == parent.levelCount)
{
sumRed += color.R;
sumGreen += color.G;
Expand All @@ -109,7 +109,7 @@ internal bool AddColor(Color32 color, int level)
return pixelCount == 1;
}

Debug.Assert(level < parent.bpp);
Debug.Assert(level < parent.levelCount);
children ??= new OctreeNode[8];

// Generating a 0..7 index based on the color components and adding new branches on demand.
Expand Down Expand Up @@ -238,7 +238,7 @@ private Color32 ToColor()

private int maxColors;
private int size;
private int bpp;
private int levelCount;
private bool hasTransparency;

[AllowNull]private List<OctreeNode>[] levels;
Expand All @@ -257,14 +257,14 @@ private Color32 ToColor()

#region Public Methods

public void Initialize(int requestedColors, IBitmapData source)
public void Initialize(int requestedColors, byte? bitLevel, IBitmapData source)
{
maxColors = requestedColors;
size = source.Width * source.Height;
bpp = requestedColors.ToBitsPerPixel();

levels = new List<OctreeNode>[bpp];
for (int level = 0; level < bpp; level++)
levelCount = bitLevel ?? Math.Min(8, requestedColors.ToBitsPerPixel());
levels = new List<OctreeNode>[levelCount];
for (int level = 0; level < levelCount; level++)
levels[level] = new List<OctreeNode>();

root = new OctreeNode(-1, this);
Expand Down Expand Up @@ -314,7 +314,7 @@ public void Dispose()
private void ReduceTree(IAsyncContext context)
{
// Scanning all levels towards root. Leaves are skipped (hence -2) because they are not reducible.
for (int level = bpp - 2; level >= 0; level--)
for (int level = levelCount - 2; level >= 0; level--)
{
if (levels[level].Count == 0)
continue;
Expand Down
Loading

0 comments on commit 6f6fbfc

Please sign in to comment.