public Color32[] GeneratePalette(IAsyncContext context) { // Occurs when bitmap is completely transparent if (root.Count == 0) { Debug.Assert(hasTransparency); return(new Color32[1]); } Color32[] result; if (root.IsSingleColor) { result = new Color32[hasTransparency ? 2 : 1]; result[0] = root.ToColor(); return(result); } context.Progress?.New(DrawingOperation.GeneratingPalette, MaxActualColors, 1); var buckets = new ColorBucketCollection(MaxActualColors); buckets.AddBucket(root); // splitting the initial bucket until no more split can be done or desired color amount is reached int startIndex = 0; while (buckets.ColorsCount < MaxActualColors) { if (buckets.SplitBuckets(context, ref startIndex)) { startIndex = 0; } else { break; } } // finalizing colors and continuing splitting if some buckets map to the same colors while (buckets.FinalColorsCount < MaxActualColors) { ColorBucket first = buckets.RemoveFirstBucket(); if (first == null) { break; } if (startIndex > 0) { startIndex -= 1; } if (!buckets.AddFinalColor(first.ToColor())) { buckets.SplitBuckets(context, ref startIndex); } } if (context.IsCancellationRequested) { return(null); } Debug.Assert(buckets.FinalColorsCount <= MaxActualColors); result = new Color32[buckets.FinalColorsCount + (hasTransparency ? 1 : 0)]; buckets.CopyFinalColorsTo(result); context.Progress?.Complete(); // If transparent color is needed, then it will be automatically the last color in the result return(result); }
internal void Split(ColorComponent component, ColorBucketCollection buckets, ref int index, ref int endIndex) { Debug.Assert(colors.Count > 1); // always sorting by all of the components so then we can eliminate same color groups easily switch (component) { case ColorComponent.R: colors.Sort(redSorter); break; case ColorComponent.G: colors.Sort(greenSorter); break; case ColorComponent.B: colors.Sort(blueSorter); break; } 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 isRightSingleColor = colors[medianIndex] == colors[colors.Count - 1]; ColorBucket left = isLeftSingleColor ? null : new ColorBucket(medianIndex); ColorBucket right = isRightSingleColor ? null : new ColorBucket(colors.Count - medianIndex); // populating the left and right buckets int from = isLeftSingleColor ? (isRightSingleColor ? Int32.MaxValue : medianIndex) : 0; int to = isRightSingleColor ? (isLeftSingleColor ? 0 : medianIndex) : colors.Count; for (int i = from; i < to; i++) { if (i < medianIndex) { left.AddColor(colors[i]); } else { right.AddColor(colors[i]); } } if (isLeftSingleColor) { buckets.AddFinalColor(colors[0]); // if none of the halves could be added, we remove the current bucket and reduce the number of buckets to scan if (isRightSingleColor) { buckets.AddFinalColor(colors[medianIndex]); buckets.RemoveBucket(index); endIndex -= 1; return; } // the right half is assigned back to the original position buckets.ReplaceBucket(index, right); index += 1; return; } // the left half is assigned back to the original position buckets.ReplaceBucket(index, left); index += 1; if (isRightSingleColor) { buckets.AddFinalColor(colors[medianIndex]); return; } // the right half is added as a new bucket buckets.AddBucket(right); }