123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602 |
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Drawing.Imaging;
- using System.IO;
- using System.Linq;
- using System.Runtime.InteropServices;
- namespace TEAMModelOS.SDK.PngQuant
- {
- public abstract class PngQuantizerBase
- {
- private const int MaxColor = 256;
- protected const int Alpha = 3;
- protected const int Red = 2;
- protected const int Green = 1;
- protected const int Blue = 0;
- private const int SideSize = 33;
- private const int MaxSideIndex = 32;
- /// <summary>
- /// 處理Base64圖片,支持不同格式圖片統一轉換為8位元PNG
- /// </summary>
- /// <param name="base64"></param>
- /// <param name="Width"></param>
- /// <param name="height"></param>
- /// <returns></returns>
- public Image QuantizeImageFromBase64(string base64, int Width = 0, int height = 0)
- {
- byte[] arr = Convert.FromBase64String(base64);
- MemoryStream ms = new MemoryStream(arr);
- var oldbmp = new Bitmap(ms);
-
- if (Width > 0 && height > 0)
- {
- var newbmp = new Bitmap(oldbmp, new Size(Width, height)); //縮圖時自動保存為Format32bppArgb無須另外處理
- oldbmp.Dispose();
- return QuantizeImage(newbmp, 10, 70);
- }
- else
- {
- if (oldbmp.PixelFormat != PixelFormat.Format32bppArgb) //不縮圖時,要處理保存為Format32bppArgb
- {
- var newbmp = new Bitmap(oldbmp.Width, oldbmp.Height, PixelFormat.Format32bppArgb);
- using (var gr = Graphics.FromImage(newbmp))
- gr.DrawImage(oldbmp, new Rectangle(0, 0, oldbmp.Width, oldbmp.Height));
- oldbmp.Dispose();
- oldbmp = newbmp;
- }
- return QuantizeImage(oldbmp, 10, 70);
- }
- }
- public Image QuantizeImage(Bitmap image, int alphaThreshold = 10, int alphaFader = 70)
- {
- var colorCount = MaxColor;
- var data = BuildHistogram(image, alphaThreshold, alphaFader);
- data = CalculateMoments(data);
- var cubes = SplitData(ref colorCount, data);
- var palette = GetQuantizedPalette(colorCount, data, cubes, alphaThreshold);
- return ProcessImagePixels(image, palette);
- }
- private static Bitmap ProcessImagePixels(Image sourceImage, QuantizedPalette palette)
- {
- var result = new Bitmap(sourceImage.Width, sourceImage.Height, PixelFormat.Format8bppIndexed);
- var newPalette = result.Palette;
- for (var index = 0; index < palette.Colors.Count; index++)
- newPalette.Entries[index] = palette.Colors[index];
- result.Palette = newPalette;
- BitmapData targetData = null;
- try
- {
- targetData = result.LockBits(Rectangle.FromLTRB(0, 0, result.Width, result.Height), ImageLockMode.WriteOnly, result.PixelFormat);
- const byte targetBitDepth = 8;
- var targetByteLength = targetData.Stride < 0 ? -targetData.Stride : targetData.Stride;
- var targetByteCount = Math.Max(1, targetBitDepth >> 3);
- var targetSize = targetByteLength * result.Height;
- var targetOffset = 0;
- var targetBuffer = new byte[targetSize];
- var targetValue = new byte[targetByteCount];
- var pixelIndex = 0;
- for (var y = 0; y < result.Height; y++)
- {
- var targetIndex = 0;
- for (var x = 0; x < result.Width; x++)
- {
- var targetIndexOffset = targetIndex >> 3;
- targetValue[0] = (byte)(palette.PixelIndex[pixelIndex] == -1 ? palette.Colors.Count - 1 : palette.PixelIndex[pixelIndex]);
- pixelIndex++;
- for (var valueIndex = 0; valueIndex < targetByteCount; valueIndex++)
- targetBuffer[targetOffset + valueIndex + targetIndexOffset] = targetValue[valueIndex];
- targetIndex += targetBitDepth;
- }
- targetOffset += targetByteLength;
- }
- Marshal.Copy(targetBuffer, 0, targetData.Scan0, targetSize);
- }
- finally
- {
- if(targetData != null)
- result.UnlockBits(targetData);
- }
- return result;
- }
- private static ColorData BuildHistogram(Bitmap sourceImage, int alphaThreshold, int alphaFader)
- {
- int bitmapWidth = sourceImage.Width;
- int bitmapHeight = sourceImage.Height;
- BitmapData data = sourceImage.LockBits(
- Rectangle.FromLTRB(0, 0, bitmapWidth, bitmapHeight),
- ImageLockMode.ReadOnly,
- sourceImage.PixelFormat);
- ColorData colorData = new ColorData(MaxSideIndex, bitmapWidth, bitmapHeight);
- try
- {
- var bitDepth = Image.GetPixelFormatSize(sourceImage.PixelFormat);
- if (bitDepth != 32)
- throw new QuantizationException(string.Format("Thie image you are attempting to quantize does not contain a 32 bit ARGB palette. This image has a bit depth of {0} with {1} colors.", bitDepth, sourceImage.Palette.Entries.Length));
- var byteLength = data.Stride < 0 ? -data.Stride : data.Stride;
- var byteCount = Math.Max(1, bitDepth >> 3);
- var offset = 0;
- var buffer = new Byte[byteLength * sourceImage.Height];
- var value = new Byte[byteCount];
- Marshal.Copy(data.Scan0, buffer, 0, buffer.Length);
-
- for (int y = 0; y < bitmapHeight; y++)
- {
- var index = 0;
- for (int x = 0; x < bitmapWidth; x++)
- {
- var indexOffset = index >> 3;
- for (var valueIndex = 0; valueIndex < byteCount; valueIndex++)
- value[valueIndex] = buffer[offset + valueIndex + indexOffset];
- var indexAlpha = (byte)((value[Alpha] >> 3) + 1);
- var indexRed = (byte)((value[Red] >> 3) + 1);
- var indexGreen = (byte)((value[Green] >> 3) + 1);
- var indexBlue = (byte)((value[Blue] >> 3) + 1);
- if (value[Alpha] > alphaThreshold)
- {
- if (value[Alpha] < 255)
- {
- var alpha = value[Alpha] + (value[Alpha] % alphaFader);
- value[Alpha] = (byte)(alpha > 255 ? 255 : alpha);
- indexAlpha = (byte)((value[Alpha] >> 3) + 1);
- }
- colorData.Weights[indexAlpha, indexRed, indexGreen, indexBlue]++;
- colorData.MomentsRed[indexAlpha, indexRed, indexGreen, indexBlue] += value[Red];
- colorData.MomentsGreen[indexAlpha, indexRed, indexGreen, indexBlue] += value[Green];
- colorData.MomentsBlue[indexAlpha, indexRed, indexGreen, indexBlue] += value[Blue];
- colorData.MomentsAlpha[indexAlpha, indexRed, indexGreen, indexBlue] += value[Alpha];
- colorData.Moments[indexAlpha, indexRed, indexGreen, indexBlue] += (value[Alpha]*value[Alpha]) +
- (value[Red]*value[Red]) +
- (value[Green]*value[Green]) +
- (value[Blue]*value[Blue]);
- }
- colorData.AddPixel(
- new Pixel(value[Alpha], value[Red], value[Green], value[Blue]),
- BitConverter.ToInt32 (new[] { indexAlpha, indexRed, indexGreen, indexBlue }, 0));
- index += bitDepth;
- }
- offset += byteLength;
- }
- }
- finally
- {
- sourceImage.UnlockBits(data);
- }
- return colorData;
- }
- private static ColorData CalculateMoments(ColorData data)
- {
- for (var alphaIndex = 1; alphaIndex <= MaxSideIndex; ++alphaIndex)
- {
- var xarea = new long[SideSize, SideSize, SideSize];
- var xareaAlpha = new long[SideSize, SideSize, SideSize];
- var xareaRed = new long[SideSize, SideSize, SideSize];
- var xareaGreen = new long[SideSize, SideSize, SideSize];
- var xareaBlue = new long[SideSize, SideSize, SideSize];
- var xarea2 = new float[SideSize, SideSize, SideSize];
- for (var redIndex = 1; redIndex <= MaxSideIndex; ++redIndex)
- {
- var area = new long[SideSize];
- var areaAlpha = new long[SideSize];
- var areaRed = new long[SideSize];
- var areaGreen = new long[SideSize];
- var areaBlue = new long[SideSize];
- var area2 = new float[SideSize];
- for (var greenIndex = 1; greenIndex <= MaxSideIndex; ++greenIndex)
- {
- long line = 0;
- long lineAlpha = 0;
- long lineRed = 0;
- long lineGreen = 0;
- long lineBlue = 0;
- var line2 = 0.0f;
- for (var blueIndex = 1; blueIndex <= MaxSideIndex; ++blueIndex)
- {
- line += data.Weights[alphaIndex, redIndex, greenIndex, blueIndex];
- lineAlpha += data.MomentsAlpha[alphaIndex, redIndex, greenIndex, blueIndex];
- lineRed += data.MomentsRed[alphaIndex, redIndex, greenIndex, blueIndex];
- lineGreen += data.MomentsGreen[alphaIndex, redIndex, greenIndex, blueIndex];
- lineBlue += data.MomentsBlue[alphaIndex, redIndex, greenIndex, blueIndex];
- line2 += data.Moments[alphaIndex, redIndex, greenIndex, blueIndex];
- area[blueIndex] += line;
- areaAlpha[blueIndex] += lineAlpha;
- areaRed[blueIndex] += lineRed;
- areaGreen[blueIndex] += lineGreen;
- areaBlue[blueIndex] += lineBlue;
- area2[blueIndex] += line2;
- xarea[redIndex, greenIndex, blueIndex] = xarea[redIndex - 1, greenIndex, blueIndex] + area[blueIndex];
- xareaAlpha[redIndex, greenIndex, blueIndex] = xareaAlpha[redIndex - 1, greenIndex, blueIndex] + areaAlpha[blueIndex];
- xareaRed[redIndex, greenIndex, blueIndex] = xareaRed[redIndex - 1, greenIndex, blueIndex] + areaRed[blueIndex];
- xareaGreen[redIndex, greenIndex, blueIndex] = xareaGreen[redIndex - 1, greenIndex, blueIndex] + areaGreen[blueIndex];
- xareaBlue[redIndex, greenIndex, blueIndex] = xareaBlue[redIndex - 1, greenIndex, blueIndex] + areaBlue[blueIndex];
- xarea2[redIndex, greenIndex, blueIndex] = xarea2[redIndex - 1, greenIndex, blueIndex] + area2[blueIndex];
- data.Weights[alphaIndex, redIndex, greenIndex, blueIndex] = data.Weights[alphaIndex - 1, redIndex, greenIndex, blueIndex] + xarea[redIndex, greenIndex, blueIndex];
- data.MomentsAlpha[alphaIndex, redIndex, greenIndex, blueIndex] = data.MomentsAlpha[alphaIndex - 1, redIndex, greenIndex, blueIndex] + xareaAlpha[redIndex, greenIndex, blueIndex];
- data.MomentsRed[alphaIndex, redIndex, greenIndex, blueIndex] = data.MomentsRed[alphaIndex - 1, redIndex, greenIndex, blueIndex] + xareaRed[redIndex, greenIndex, blueIndex];
- data.MomentsGreen[alphaIndex, redIndex, greenIndex, blueIndex] = data.MomentsGreen[alphaIndex - 1, redIndex, greenIndex, blueIndex] + xareaGreen[redIndex, greenIndex, blueIndex];
- data.MomentsBlue[alphaIndex, redIndex, greenIndex, blueIndex] = data.MomentsBlue[alphaIndex - 1, redIndex, greenIndex, blueIndex] + xareaBlue[redIndex, greenIndex, blueIndex];
- data.Moments[alphaIndex, redIndex, greenIndex, blueIndex] = data.Moments[alphaIndex - 1, redIndex, greenIndex, blueIndex] + xarea2[redIndex, greenIndex, blueIndex];
- }
- }
- }
- }
- return data;
- }
- private static long Top(Box cube, int direction, int position, long[,,,] moment)
- {
- switch (direction)
- {
- case Alpha:
- return (moment[position, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
- moment[position, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] -
- moment[position, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
- moment[position, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -
- (moment[position, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
- moment[position, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
- moment[position, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
- moment[position, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
- case Red:
- return (moment[cube.AlphaMaximum, position, cube.GreenMaximum, cube.BlueMaximum] -
- moment[cube.AlphaMaximum, position, cube.GreenMinimum, cube.BlueMaximum] -
- moment[cube.AlphaMinimum, position, cube.GreenMaximum, cube.BlueMaximum] +
- moment[cube.AlphaMinimum, position, cube.GreenMinimum, cube.BlueMaximum]) -
- (moment[cube.AlphaMaximum, position, cube.GreenMaximum, cube.BlueMinimum] -
- moment[cube.AlphaMaximum, position, cube.GreenMinimum, cube.BlueMinimum] -
- moment[cube.AlphaMinimum, position, cube.GreenMaximum, cube.BlueMinimum] +
- moment[cube.AlphaMinimum, position, cube.GreenMinimum, cube.BlueMinimum]);
- case Green:
- return (moment[cube.AlphaMaximum, cube.RedMaximum, position, cube.BlueMaximum] -
- moment[cube.AlphaMaximum, cube.RedMinimum, position, cube.BlueMaximum] -
- moment[cube.AlphaMinimum, cube.RedMaximum, position, cube.BlueMaximum] +
- moment[cube.AlphaMinimum, cube.RedMinimum, position, cube.BlueMaximum]) -
- (moment[cube.AlphaMaximum, cube.RedMaximum, position, cube.BlueMinimum] -
- moment[cube.AlphaMaximum, cube.RedMinimum, position, cube.BlueMinimum] -
- moment[cube.AlphaMinimum, cube.RedMaximum, position, cube.BlueMinimum] +
- moment[cube.AlphaMinimum, cube.RedMinimum, position, cube.BlueMinimum]);
- case Blue:
- return (moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, position] -
- moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, position] -
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, position] +
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, position]) -
- (moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, position] -
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, position] -
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, position] +
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, position]);
- default:
- return 0;
- }
- }
- private static long Bottom(Box cube, int direction, long[,,,] moment)
- {
- switch (direction)
- {
- case Alpha:
- return (-moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] +
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] -
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -
- (-moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] +
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] -
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
- case Red:
- return (-moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] +
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] -
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -
- (-moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum] +
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] -
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
- case Green:
- return (-moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] +
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] -
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -
- (-moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum] +
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
- case Blue:
- return (-moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] +
- moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] -
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]) -
- (-moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] +
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] -
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
- default:
- return 0;
- }
- }
- private static CubeCut Maximize(ColorData data, Box cube, int direction, byte first, byte last, long wholeAlpha, long wholeRed, long wholeGreen, long wholeBlue, long wholeWeight)
- {
- var bottomAlpha = Bottom(cube, direction, data.MomentsAlpha);
- var bottomRed = Bottom(cube, direction, data.MomentsRed);
- var bottomGreen = Bottom(cube, direction, data.MomentsGreen);
- var bottomBlue = Bottom(cube, direction, data.MomentsBlue);
- var bottomWeight = Bottom(cube, direction, data.Weights);
- var result = 0.0f;
- byte? cutPoint = null;
- for (var position = first; position < last; ++position)
- {
- var halfAlpha = bottomAlpha + Top(cube, direction, position, data.MomentsAlpha);
- var halfRed = bottomRed + Top(cube, direction, position, data.MomentsRed);
- var halfGreen = bottomGreen + Top(cube, direction, position, data.MomentsGreen);
- var halfBlue = bottomBlue + Top(cube, direction, position, data.MomentsBlue);
- var halfWeight = bottomWeight + Top(cube, direction, position, data.Weights);
- if (halfWeight == 0) continue;
- var halfDistance = halfAlpha * halfAlpha + halfRed * halfRed + halfGreen * halfGreen + halfBlue * halfBlue;
- var temp = halfDistance / halfWeight;
- halfAlpha = wholeAlpha - halfAlpha;
- halfRed = wholeRed - halfRed;
- halfGreen = wholeGreen - halfGreen;
- halfBlue = wholeBlue - halfBlue;
- halfWeight = wholeWeight - halfWeight;
- if (halfWeight != 0)
- {
- halfDistance = halfAlpha * halfAlpha + halfRed * halfRed + halfGreen * halfGreen + halfBlue * halfBlue;
- temp += halfDistance / halfWeight;
- if (temp > result)
- {
- result = temp;
- cutPoint = position;
- }
- }
- }
- return new CubeCut(cutPoint, result);
- }
- private bool Cut(ColorData data, ref Box first,ref Box second)
- {
- int direction;
- var wholeAlpha = Volume(first, data.MomentsAlpha);
- var wholeRed = Volume(first, data.MomentsRed);
- var wholeGreen = Volume(first, data.MomentsGreen);
- var wholeBlue = Volume(first, data.MomentsBlue);
- var wholeWeight = Volume(first, data.Weights);
- var maxAlpha = Maximize(data, first, Alpha, (byte) (first.AlphaMinimum + 1), first.AlphaMaximum, wholeAlpha, wholeRed, wholeGreen, wholeBlue, wholeWeight);
- var maxRed = Maximize(data, first, Red, (byte) (first.RedMinimum + 1), first.RedMaximum, wholeAlpha, wholeRed, wholeGreen, wholeBlue, wholeWeight);
- var maxGreen = Maximize(data, first, Green, (byte) (first.GreenMinimum + 1), first.GreenMaximum, wholeAlpha, wholeRed, wholeGreen, wholeBlue, wholeWeight);
- var maxBlue = Maximize(data, first, Blue, (byte) (first.BlueMinimum + 1), first.BlueMaximum, wholeAlpha, wholeRed, wholeGreen, wholeBlue, wholeWeight);
- if ((maxAlpha.Value >= maxRed.Value) && (maxAlpha.Value >= maxGreen.Value) && (maxAlpha.Value >= maxBlue.Value))
- {
- direction = Alpha;
- if (maxAlpha.Position == null) return false;
- }
- else if ((maxRed.Value >= maxAlpha.Value) && (maxRed.Value >= maxGreen.Value) && (maxRed.Value >= maxBlue.Value))
- direction = Red;
- else
- {
- if ((maxGreen.Value >= maxAlpha.Value) && (maxGreen.Value >= maxRed.Value) && (maxGreen.Value >= maxBlue.Value))
- direction = Green;
- else
- direction = Blue;
- }
- second.AlphaMaximum = first.AlphaMaximum;
- second.RedMaximum = first.RedMaximum;
- second.GreenMaximum = first.GreenMaximum;
- second.BlueMaximum = first.BlueMaximum;
- switch (direction)
- {
- case Alpha:
- second.AlphaMinimum = first.AlphaMaximum = (byte) maxAlpha.Position;
- second.RedMinimum = first.RedMinimum;
- second.GreenMinimum = first.GreenMinimum;
- second.BlueMinimum = first.BlueMinimum;
- break;
- case Red:
- second.RedMinimum = first.RedMaximum = (byte) maxRed.Position;
- second.AlphaMinimum = first.AlphaMinimum;
- second.GreenMinimum = first.GreenMinimum;
- second.BlueMinimum = first.BlueMinimum;
- break;
- case Green:
- second.GreenMinimum = first.GreenMaximum = (byte) maxGreen.Position;
- second.AlphaMinimum = first.AlphaMinimum;
- second.RedMinimum = first.RedMinimum;
- second.BlueMinimum = first.BlueMinimum;
- break;
- case Blue:
- second.BlueMinimum = first.BlueMaximum = (byte) maxBlue.Position;
- second.AlphaMinimum = first.AlphaMinimum;
- second.RedMinimum = first.RedMinimum;
- second.GreenMinimum = first.GreenMinimum;
- break;
- }
- first.Size = (first.AlphaMaximum - first.AlphaMinimum) * (first.RedMaximum - first.RedMinimum) * (first.GreenMaximum - first.GreenMinimum) * (first.BlueMaximum - first.BlueMinimum);
- second.Size = (second.AlphaMaximum - second.AlphaMinimum) * (second.RedMaximum - second.RedMinimum) * (second.GreenMaximum - second.GreenMinimum) * (second.BlueMaximum - second.BlueMinimum);
- return true;
- }
- private static float CalculateVariance(ColorData data, Box cube)
- {
- float volumeAlpha = Volume(cube, data.MomentsAlpha);
- float volumeRed = Volume(cube, data.MomentsRed);
- float volumeGreen = Volume(cube, data.MomentsGreen);
- float volumeBlue = Volume(cube, data.MomentsBlue);
- float volumeMoment = VolumeFloat(cube, data.Moments);
- float volumeWeight = Volume(cube, data.Weights);
- float distance = volumeAlpha * volumeAlpha + volumeRed * volumeRed + volumeGreen * volumeGreen + volumeBlue * volumeBlue;
- var result = volumeMoment - distance / volumeWeight;
- return double.IsNaN(result) ? 0.0f : result;
- }
- private static long Volume(Box cube, long[,,,] moment)
- {
- return (moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
- moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] -
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] -
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] +
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] -
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -
- (moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
- moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum] -
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
- }
- private static float VolumeFloat(Box cube, float[,,,] moment)
- {
- return (moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
- moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] -
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] -
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] +
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] -
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -
- (moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
- moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
- moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
- moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum] -
- moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
- }
- private IEnumerable<Box> SplitData(ref int colorCount, ColorData data)
- {
- --colorCount;
- var next = 0;
- var volumeVariance = new float[MaxColor];
- var cubes = new Box[MaxColor];
- cubes[0].AlphaMaximum = MaxSideIndex;
- cubes[0].RedMaximum = MaxSideIndex;
- cubes[0].GreenMaximum = MaxSideIndex;
- cubes[0].BlueMaximum = MaxSideIndex;
- for (var cubeIndex = 1; cubeIndex < colorCount; ++cubeIndex)
- {
- if (Cut(data, ref cubes[next], ref cubes[cubeIndex]))
- {
- volumeVariance[next] = cubes[next].Size > 1 ? CalculateVariance(data, cubes[next]) : 0.0f;
- volumeVariance[cubeIndex] = cubes[cubeIndex].Size > 1 ? CalculateVariance(data, cubes[cubeIndex]) : 0.0f;
- }
- else
- {
- volumeVariance[next] = 0.0f;
- cubeIndex--;
- }
- next = 0;
- var temp = volumeVariance[0];
- for (var index = 1; index <= cubeIndex; ++index)
- {
- if (volumeVariance[index] <= temp) continue;
- temp = volumeVariance[index];
- next = index;
- }
- if (temp > 0.0) continue;
- colorCount = cubeIndex + 1;
- break;
- }
- return cubes.Take(colorCount).ToList();
- }
- protected LookupData BuildLookups(IEnumerable<Box> cubes, ColorData data)
- {
- LookupData lookups = new LookupData(SideSize);
- int lookupsCount = lookups.Lookups.Count;
- foreach (var cube in cubes)
- {
- for (var alphaIndex = (byte)(cube.AlphaMinimum + 1); alphaIndex <= cube.AlphaMaximum; ++alphaIndex)
- {
- for (var redIndex = (byte)(cube.RedMinimum + 1); redIndex <= cube.RedMaximum; ++redIndex)
- {
- for (var greenIndex = (byte)(cube.GreenMinimum + 1); greenIndex <= cube.GreenMaximum; ++greenIndex)
- {
- for (var blueIndex = (byte)(cube.BlueMinimum + 1); blueIndex <= cube.BlueMaximum; ++blueIndex)
- {
- lookups.Tags[alphaIndex, redIndex, greenIndex, blueIndex] = lookupsCount;
- }
- }
- }
- }
- var weight = Volume(cube, data.Weights);
- if (weight <= 0) continue;
- var lookup = new Lookup
- {
- Alpha = (int) (Volume(cube, data.MomentsAlpha)/weight),
- Red = (int) (Volume(cube, data.MomentsRed)/weight),
- Green = (int) (Volume(cube, data.MomentsGreen)/weight),
- Blue = (int) (Volume(cube, data.MomentsBlue)/weight)
- };
- lookups.Lookups.Add(lookup);
- }
- return lookups;
- }
- protected abstract QuantizedPalette GetQuantizedPalette(int colorCount, ColorData data, IEnumerable<Box> cubes, int alphaThreshold);
- }
- }
|