PngQuantizerBase.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Drawing.Imaging;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Runtime.InteropServices;
  8. namespace TEAMModelOS.SDK.PngQuant
  9. {
  10. public abstract class PngQuantizerBase
  11. {
  12. private const int MaxColor = 256;
  13. protected const int Alpha = 3;
  14. protected const int Red = 2;
  15. protected const int Green = 1;
  16. protected const int Blue = 0;
  17. private const int SideSize = 33;
  18. private const int MaxSideIndex = 32;
  19. /// <summary>
  20. /// 處理Base64圖片,支持不同格式圖片統一轉換為8位元PNG
  21. /// </summary>
  22. /// <param name="base64"></param>
  23. /// <param name="Width"></param>
  24. /// <param name="height"></param>
  25. /// <returns></returns>
  26. public Image QuantizeImageFromBase64(string base64, int Width = 0, int height = 0)
  27. {
  28. byte[] arr = Convert.FromBase64String(base64);
  29. MemoryStream ms = new MemoryStream(arr);
  30. var oldbmp = new Bitmap(ms);
  31. if (Width > 0 && height > 0)
  32. {
  33. var newbmp = new Bitmap(oldbmp, new Size(Width, height)); //縮圖時自動保存為Format32bppArgb無須另外處理
  34. oldbmp.Dispose();
  35. return QuantizeImage(newbmp, 10, 70);
  36. }
  37. else
  38. {
  39. if (oldbmp.PixelFormat != PixelFormat.Format32bppArgb) //不縮圖時,要處理保存為Format32bppArgb
  40. {
  41. var newbmp = new Bitmap(oldbmp.Width, oldbmp.Height, PixelFormat.Format32bppArgb);
  42. using (var gr = Graphics.FromImage(newbmp))
  43. gr.DrawImage(oldbmp, new Rectangle(0, 0, oldbmp.Width, oldbmp.Height));
  44. oldbmp.Dispose();
  45. oldbmp = newbmp;
  46. }
  47. return QuantizeImage(oldbmp, 10, 70);
  48. }
  49. }
  50. public Image QuantizeImage(Bitmap image, int alphaThreshold = 10, int alphaFader = 70)
  51. {
  52. var colorCount = MaxColor;
  53. var data = BuildHistogram(image, alphaThreshold, alphaFader);
  54. data = CalculateMoments(data);
  55. var cubes = SplitData(ref colorCount, data);
  56. var palette = GetQuantizedPalette(colorCount, data, cubes, alphaThreshold);
  57. return ProcessImagePixels(image, palette);
  58. }
  59. private static Bitmap ProcessImagePixels(Image sourceImage, QuantizedPalette palette)
  60. {
  61. var result = new Bitmap(sourceImage.Width, sourceImage.Height, PixelFormat.Format8bppIndexed);
  62. var newPalette = result.Palette;
  63. for (var index = 0; index < palette.Colors.Count; index++)
  64. newPalette.Entries[index] = palette.Colors[index];
  65. result.Palette = newPalette;
  66. BitmapData targetData = null;
  67. try
  68. {
  69. targetData = result.LockBits(Rectangle.FromLTRB(0, 0, result.Width, result.Height), ImageLockMode.WriteOnly, result.PixelFormat);
  70. const byte targetBitDepth = 8;
  71. var targetByteLength = targetData.Stride < 0 ? -targetData.Stride : targetData.Stride;
  72. var targetByteCount = Math.Max(1, targetBitDepth >> 3);
  73. var targetSize = targetByteLength * result.Height;
  74. var targetOffset = 0;
  75. var targetBuffer = new byte[targetSize];
  76. var targetValue = new byte[targetByteCount];
  77. var pixelIndex = 0;
  78. for (var y = 0; y < result.Height; y++)
  79. {
  80. var targetIndex = 0;
  81. for (var x = 0; x < result.Width; x++)
  82. {
  83. var targetIndexOffset = targetIndex >> 3;
  84. targetValue[0] = (byte)(palette.PixelIndex[pixelIndex] == -1 ? palette.Colors.Count - 1 : palette.PixelIndex[pixelIndex]);
  85. pixelIndex++;
  86. for (var valueIndex = 0; valueIndex < targetByteCount; valueIndex++)
  87. targetBuffer[targetOffset + valueIndex + targetIndexOffset] = targetValue[valueIndex];
  88. targetIndex += targetBitDepth;
  89. }
  90. targetOffset += targetByteLength;
  91. }
  92. Marshal.Copy(targetBuffer, 0, targetData.Scan0, targetSize);
  93. }
  94. finally
  95. {
  96. if(targetData != null)
  97. result.UnlockBits(targetData);
  98. }
  99. return result;
  100. }
  101. private static ColorData BuildHistogram(Bitmap sourceImage, int alphaThreshold, int alphaFader)
  102. {
  103. int bitmapWidth = sourceImage.Width;
  104. int bitmapHeight = sourceImage.Height;
  105. BitmapData data = sourceImage.LockBits(
  106. Rectangle.FromLTRB(0, 0, bitmapWidth, bitmapHeight),
  107. ImageLockMode.ReadOnly,
  108. sourceImage.PixelFormat);
  109. ColorData colorData = new ColorData(MaxSideIndex, bitmapWidth, bitmapHeight);
  110. try
  111. {
  112. var bitDepth = Image.GetPixelFormatSize(sourceImage.PixelFormat);
  113. if (bitDepth != 32)
  114. 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));
  115. var byteLength = data.Stride < 0 ? -data.Stride : data.Stride;
  116. var byteCount = Math.Max(1, bitDepth >> 3);
  117. var offset = 0;
  118. var buffer = new Byte[byteLength * sourceImage.Height];
  119. var value = new Byte[byteCount];
  120. Marshal.Copy(data.Scan0, buffer, 0, buffer.Length);
  121. for (int y = 0; y < bitmapHeight; y++)
  122. {
  123. var index = 0;
  124. for (int x = 0; x < bitmapWidth; x++)
  125. {
  126. var indexOffset = index >> 3;
  127. for (var valueIndex = 0; valueIndex < byteCount; valueIndex++)
  128. value[valueIndex] = buffer[offset + valueIndex + indexOffset];
  129. var indexAlpha = (byte)((value[Alpha] >> 3) + 1);
  130. var indexRed = (byte)((value[Red] >> 3) + 1);
  131. var indexGreen = (byte)((value[Green] >> 3) + 1);
  132. var indexBlue = (byte)((value[Blue] >> 3) + 1);
  133. if (value[Alpha] > alphaThreshold)
  134. {
  135. if (value[Alpha] < 255)
  136. {
  137. var alpha = value[Alpha] + (value[Alpha] % alphaFader);
  138. value[Alpha] = (byte)(alpha > 255 ? 255 : alpha);
  139. indexAlpha = (byte)((value[Alpha] >> 3) + 1);
  140. }
  141. colorData.Weights[indexAlpha, indexRed, indexGreen, indexBlue]++;
  142. colorData.MomentsRed[indexAlpha, indexRed, indexGreen, indexBlue] += value[Red];
  143. colorData.MomentsGreen[indexAlpha, indexRed, indexGreen, indexBlue] += value[Green];
  144. colorData.MomentsBlue[indexAlpha, indexRed, indexGreen, indexBlue] += value[Blue];
  145. colorData.MomentsAlpha[indexAlpha, indexRed, indexGreen, indexBlue] += value[Alpha];
  146. colorData.Moments[indexAlpha, indexRed, indexGreen, indexBlue] += (value[Alpha]*value[Alpha]) +
  147. (value[Red]*value[Red]) +
  148. (value[Green]*value[Green]) +
  149. (value[Blue]*value[Blue]);
  150. }
  151. colorData.AddPixel(
  152. new Pixel(value[Alpha], value[Red], value[Green], value[Blue]),
  153. BitConverter.ToInt32 (new[] { indexAlpha, indexRed, indexGreen, indexBlue }, 0));
  154. index += bitDepth;
  155. }
  156. offset += byteLength;
  157. }
  158. }
  159. finally
  160. {
  161. sourceImage.UnlockBits(data);
  162. }
  163. return colorData;
  164. }
  165. private static ColorData CalculateMoments(ColorData data)
  166. {
  167. for (var alphaIndex = 1; alphaIndex <= MaxSideIndex; ++alphaIndex)
  168. {
  169. var xarea = new long[SideSize, SideSize, SideSize];
  170. var xareaAlpha = new long[SideSize, SideSize, SideSize];
  171. var xareaRed = new long[SideSize, SideSize, SideSize];
  172. var xareaGreen = new long[SideSize, SideSize, SideSize];
  173. var xareaBlue = new long[SideSize, SideSize, SideSize];
  174. var xarea2 = new float[SideSize, SideSize, SideSize];
  175. for (var redIndex = 1; redIndex <= MaxSideIndex; ++redIndex)
  176. {
  177. var area = new long[SideSize];
  178. var areaAlpha = new long[SideSize];
  179. var areaRed = new long[SideSize];
  180. var areaGreen = new long[SideSize];
  181. var areaBlue = new long[SideSize];
  182. var area2 = new float[SideSize];
  183. for (var greenIndex = 1; greenIndex <= MaxSideIndex; ++greenIndex)
  184. {
  185. long line = 0;
  186. long lineAlpha = 0;
  187. long lineRed = 0;
  188. long lineGreen = 0;
  189. long lineBlue = 0;
  190. var line2 = 0.0f;
  191. for (var blueIndex = 1; blueIndex <= MaxSideIndex; ++blueIndex)
  192. {
  193. line += data.Weights[alphaIndex, redIndex, greenIndex, blueIndex];
  194. lineAlpha += data.MomentsAlpha[alphaIndex, redIndex, greenIndex, blueIndex];
  195. lineRed += data.MomentsRed[alphaIndex, redIndex, greenIndex, blueIndex];
  196. lineGreen += data.MomentsGreen[alphaIndex, redIndex, greenIndex, blueIndex];
  197. lineBlue += data.MomentsBlue[alphaIndex, redIndex, greenIndex, blueIndex];
  198. line2 += data.Moments[alphaIndex, redIndex, greenIndex, blueIndex];
  199. area[blueIndex] += line;
  200. areaAlpha[blueIndex] += lineAlpha;
  201. areaRed[blueIndex] += lineRed;
  202. areaGreen[blueIndex] += lineGreen;
  203. areaBlue[blueIndex] += lineBlue;
  204. area2[blueIndex] += line2;
  205. xarea[redIndex, greenIndex, blueIndex] = xarea[redIndex - 1, greenIndex, blueIndex] + area[blueIndex];
  206. xareaAlpha[redIndex, greenIndex, blueIndex] = xareaAlpha[redIndex - 1, greenIndex, blueIndex] + areaAlpha[blueIndex];
  207. xareaRed[redIndex, greenIndex, blueIndex] = xareaRed[redIndex - 1, greenIndex, blueIndex] + areaRed[blueIndex];
  208. xareaGreen[redIndex, greenIndex, blueIndex] = xareaGreen[redIndex - 1, greenIndex, blueIndex] + areaGreen[blueIndex];
  209. xareaBlue[redIndex, greenIndex, blueIndex] = xareaBlue[redIndex - 1, greenIndex, blueIndex] + areaBlue[blueIndex];
  210. xarea2[redIndex, greenIndex, blueIndex] = xarea2[redIndex - 1, greenIndex, blueIndex] + area2[blueIndex];
  211. data.Weights[alphaIndex, redIndex, greenIndex, blueIndex] = data.Weights[alphaIndex - 1, redIndex, greenIndex, blueIndex] + xarea[redIndex, greenIndex, blueIndex];
  212. data.MomentsAlpha[alphaIndex, redIndex, greenIndex, blueIndex] = data.MomentsAlpha[alphaIndex - 1, redIndex, greenIndex, blueIndex] + xareaAlpha[redIndex, greenIndex, blueIndex];
  213. data.MomentsRed[alphaIndex, redIndex, greenIndex, blueIndex] = data.MomentsRed[alphaIndex - 1, redIndex, greenIndex, blueIndex] + xareaRed[redIndex, greenIndex, blueIndex];
  214. data.MomentsGreen[alphaIndex, redIndex, greenIndex, blueIndex] = data.MomentsGreen[alphaIndex - 1, redIndex, greenIndex, blueIndex] + xareaGreen[redIndex, greenIndex, blueIndex];
  215. data.MomentsBlue[alphaIndex, redIndex, greenIndex, blueIndex] = data.MomentsBlue[alphaIndex - 1, redIndex, greenIndex, blueIndex] + xareaBlue[redIndex, greenIndex, blueIndex];
  216. data.Moments[alphaIndex, redIndex, greenIndex, blueIndex] = data.Moments[alphaIndex - 1, redIndex, greenIndex, blueIndex] + xarea2[redIndex, greenIndex, blueIndex];
  217. }
  218. }
  219. }
  220. }
  221. return data;
  222. }
  223. private static long Top(Box cube, int direction, int position, long[,,,] moment)
  224. {
  225. switch (direction)
  226. {
  227. case Alpha:
  228. return (moment[position, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
  229. moment[position, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] -
  230. moment[position, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
  231. moment[position, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -
  232. (moment[position, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
  233. moment[position, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
  234. moment[position, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
  235. moment[position, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
  236. case Red:
  237. return (moment[cube.AlphaMaximum, position, cube.GreenMaximum, cube.BlueMaximum] -
  238. moment[cube.AlphaMaximum, position, cube.GreenMinimum, cube.BlueMaximum] -
  239. moment[cube.AlphaMinimum, position, cube.GreenMaximum, cube.BlueMaximum] +
  240. moment[cube.AlphaMinimum, position, cube.GreenMinimum, cube.BlueMaximum]) -
  241. (moment[cube.AlphaMaximum, position, cube.GreenMaximum, cube.BlueMinimum] -
  242. moment[cube.AlphaMaximum, position, cube.GreenMinimum, cube.BlueMinimum] -
  243. moment[cube.AlphaMinimum, position, cube.GreenMaximum, cube.BlueMinimum] +
  244. moment[cube.AlphaMinimum, position, cube.GreenMinimum, cube.BlueMinimum]);
  245. case Green:
  246. return (moment[cube.AlphaMaximum, cube.RedMaximum, position, cube.BlueMaximum] -
  247. moment[cube.AlphaMaximum, cube.RedMinimum, position, cube.BlueMaximum] -
  248. moment[cube.AlphaMinimum, cube.RedMaximum, position, cube.BlueMaximum] +
  249. moment[cube.AlphaMinimum, cube.RedMinimum, position, cube.BlueMaximum]) -
  250. (moment[cube.AlphaMaximum, cube.RedMaximum, position, cube.BlueMinimum] -
  251. moment[cube.AlphaMaximum, cube.RedMinimum, position, cube.BlueMinimum] -
  252. moment[cube.AlphaMinimum, cube.RedMaximum, position, cube.BlueMinimum] +
  253. moment[cube.AlphaMinimum, cube.RedMinimum, position, cube.BlueMinimum]);
  254. case Blue:
  255. return (moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, position] -
  256. moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, position] -
  257. moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, position] +
  258. moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, position]) -
  259. (moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, position] -
  260. moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, position] -
  261. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, position] +
  262. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, position]);
  263. default:
  264. return 0;
  265. }
  266. }
  267. private static long Bottom(Box cube, int direction, long[,,,] moment)
  268. {
  269. switch (direction)
  270. {
  271. case Alpha:
  272. return (-moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] +
  273. moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
  274. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] -
  275. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -
  276. (-moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] +
  277. moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
  278. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] -
  279. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
  280. case Red:
  281. return (-moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
  282. moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] +
  283. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] -
  284. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -
  285. (-moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
  286. moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum] +
  287. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] -
  288. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
  289. case Green:
  290. return (-moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
  291. moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] +
  292. moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] -
  293. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -
  294. (-moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
  295. moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum] +
  296. moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
  297. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
  298. case Blue:
  299. return (-moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] +
  300. moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
  301. moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] -
  302. moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]) -
  303. (-moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] +
  304. moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
  305. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] -
  306. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
  307. default:
  308. return 0;
  309. }
  310. }
  311. private static CubeCut Maximize(ColorData data, Box cube, int direction, byte first, byte last, long wholeAlpha, long wholeRed, long wholeGreen, long wholeBlue, long wholeWeight)
  312. {
  313. var bottomAlpha = Bottom(cube, direction, data.MomentsAlpha);
  314. var bottomRed = Bottom(cube, direction, data.MomentsRed);
  315. var bottomGreen = Bottom(cube, direction, data.MomentsGreen);
  316. var bottomBlue = Bottom(cube, direction, data.MomentsBlue);
  317. var bottomWeight = Bottom(cube, direction, data.Weights);
  318. var result = 0.0f;
  319. byte? cutPoint = null;
  320. for (var position = first; position < last; ++position)
  321. {
  322. var halfAlpha = bottomAlpha + Top(cube, direction, position, data.MomentsAlpha);
  323. var halfRed = bottomRed + Top(cube, direction, position, data.MomentsRed);
  324. var halfGreen = bottomGreen + Top(cube, direction, position, data.MomentsGreen);
  325. var halfBlue = bottomBlue + Top(cube, direction, position, data.MomentsBlue);
  326. var halfWeight = bottomWeight + Top(cube, direction, position, data.Weights);
  327. if (halfWeight == 0) continue;
  328. var halfDistance = halfAlpha * halfAlpha + halfRed * halfRed + halfGreen * halfGreen + halfBlue * halfBlue;
  329. var temp = halfDistance / halfWeight;
  330. halfAlpha = wholeAlpha - halfAlpha;
  331. halfRed = wholeRed - halfRed;
  332. halfGreen = wholeGreen - halfGreen;
  333. halfBlue = wholeBlue - halfBlue;
  334. halfWeight = wholeWeight - halfWeight;
  335. if (halfWeight != 0)
  336. {
  337. halfDistance = halfAlpha * halfAlpha + halfRed * halfRed + halfGreen * halfGreen + halfBlue * halfBlue;
  338. temp += halfDistance / halfWeight;
  339. if (temp > result)
  340. {
  341. result = temp;
  342. cutPoint = position;
  343. }
  344. }
  345. }
  346. return new CubeCut(cutPoint, result);
  347. }
  348. private bool Cut(ColorData data, ref Box first,ref Box second)
  349. {
  350. int direction;
  351. var wholeAlpha = Volume(first, data.MomentsAlpha);
  352. var wholeRed = Volume(first, data.MomentsRed);
  353. var wholeGreen = Volume(first, data.MomentsGreen);
  354. var wholeBlue = Volume(first, data.MomentsBlue);
  355. var wholeWeight = Volume(first, data.Weights);
  356. var maxAlpha = Maximize(data, first, Alpha, (byte) (first.AlphaMinimum + 1), first.AlphaMaximum, wholeAlpha, wholeRed, wholeGreen, wholeBlue, wholeWeight);
  357. var maxRed = Maximize(data, first, Red, (byte) (first.RedMinimum + 1), first.RedMaximum, wholeAlpha, wholeRed, wholeGreen, wholeBlue, wholeWeight);
  358. var maxGreen = Maximize(data, first, Green, (byte) (first.GreenMinimum + 1), first.GreenMaximum, wholeAlpha, wholeRed, wholeGreen, wholeBlue, wholeWeight);
  359. var maxBlue = Maximize(data, first, Blue, (byte) (first.BlueMinimum + 1), first.BlueMaximum, wholeAlpha, wholeRed, wholeGreen, wholeBlue, wholeWeight);
  360. if ((maxAlpha.Value >= maxRed.Value) && (maxAlpha.Value >= maxGreen.Value) && (maxAlpha.Value >= maxBlue.Value))
  361. {
  362. direction = Alpha;
  363. if (maxAlpha.Position == null) return false;
  364. }
  365. else if ((maxRed.Value >= maxAlpha.Value) && (maxRed.Value >= maxGreen.Value) && (maxRed.Value >= maxBlue.Value))
  366. direction = Red;
  367. else
  368. {
  369. if ((maxGreen.Value >= maxAlpha.Value) && (maxGreen.Value >= maxRed.Value) && (maxGreen.Value >= maxBlue.Value))
  370. direction = Green;
  371. else
  372. direction = Blue;
  373. }
  374. second.AlphaMaximum = first.AlphaMaximum;
  375. second.RedMaximum = first.RedMaximum;
  376. second.GreenMaximum = first.GreenMaximum;
  377. second.BlueMaximum = first.BlueMaximum;
  378. switch (direction)
  379. {
  380. case Alpha:
  381. second.AlphaMinimum = first.AlphaMaximum = (byte) maxAlpha.Position;
  382. second.RedMinimum = first.RedMinimum;
  383. second.GreenMinimum = first.GreenMinimum;
  384. second.BlueMinimum = first.BlueMinimum;
  385. break;
  386. case Red:
  387. second.RedMinimum = first.RedMaximum = (byte) maxRed.Position;
  388. second.AlphaMinimum = first.AlphaMinimum;
  389. second.GreenMinimum = first.GreenMinimum;
  390. second.BlueMinimum = first.BlueMinimum;
  391. break;
  392. case Green:
  393. second.GreenMinimum = first.GreenMaximum = (byte) maxGreen.Position;
  394. second.AlphaMinimum = first.AlphaMinimum;
  395. second.RedMinimum = first.RedMinimum;
  396. second.BlueMinimum = first.BlueMinimum;
  397. break;
  398. case Blue:
  399. second.BlueMinimum = first.BlueMaximum = (byte) maxBlue.Position;
  400. second.AlphaMinimum = first.AlphaMinimum;
  401. second.RedMinimum = first.RedMinimum;
  402. second.GreenMinimum = first.GreenMinimum;
  403. break;
  404. }
  405. first.Size = (first.AlphaMaximum - first.AlphaMinimum) * (first.RedMaximum - first.RedMinimum) * (first.GreenMaximum - first.GreenMinimum) * (first.BlueMaximum - first.BlueMinimum);
  406. second.Size = (second.AlphaMaximum - second.AlphaMinimum) * (second.RedMaximum - second.RedMinimum) * (second.GreenMaximum - second.GreenMinimum) * (second.BlueMaximum - second.BlueMinimum);
  407. return true;
  408. }
  409. private static float CalculateVariance(ColorData data, Box cube)
  410. {
  411. float volumeAlpha = Volume(cube, data.MomentsAlpha);
  412. float volumeRed = Volume(cube, data.MomentsRed);
  413. float volumeGreen = Volume(cube, data.MomentsGreen);
  414. float volumeBlue = Volume(cube, data.MomentsBlue);
  415. float volumeMoment = VolumeFloat(cube, data.Moments);
  416. float volumeWeight = Volume(cube, data.Weights);
  417. float distance = volumeAlpha * volumeAlpha + volumeRed * volumeRed + volumeGreen * volumeGreen + volumeBlue * volumeBlue;
  418. var result = volumeMoment - distance / volumeWeight;
  419. return double.IsNaN(result) ? 0.0f : result;
  420. }
  421. private static long Volume(Box cube, long[,,,] moment)
  422. {
  423. return (moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
  424. moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] -
  425. moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
  426. moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] -
  427. moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] +
  428. moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
  429. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] -
  430. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -
  431. (moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
  432. moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
  433. moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
  434. moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
  435. moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
  436. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
  437. moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum] -
  438. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
  439. }
  440. private static float VolumeFloat(Box cube, float[,,,] moment)
  441. {
  442. return (moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
  443. moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] -
  444. moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
  445. moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] -
  446. moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] +
  447. moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
  448. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] -
  449. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -
  450. (moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
  451. moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
  452. moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
  453. moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
  454. moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
  455. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
  456. moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum] -
  457. moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
  458. }
  459. private IEnumerable<Box> SplitData(ref int colorCount, ColorData data)
  460. {
  461. --colorCount;
  462. var next = 0;
  463. var volumeVariance = new float[MaxColor];
  464. var cubes = new Box[MaxColor];
  465. cubes[0].AlphaMaximum = MaxSideIndex;
  466. cubes[0].RedMaximum = MaxSideIndex;
  467. cubes[0].GreenMaximum = MaxSideIndex;
  468. cubes[0].BlueMaximum = MaxSideIndex;
  469. for (var cubeIndex = 1; cubeIndex < colorCount; ++cubeIndex)
  470. {
  471. if (Cut(data, ref cubes[next], ref cubes[cubeIndex]))
  472. {
  473. volumeVariance[next] = cubes[next].Size > 1 ? CalculateVariance(data, cubes[next]) : 0.0f;
  474. volumeVariance[cubeIndex] = cubes[cubeIndex].Size > 1 ? CalculateVariance(data, cubes[cubeIndex]) : 0.0f;
  475. }
  476. else
  477. {
  478. volumeVariance[next] = 0.0f;
  479. cubeIndex--;
  480. }
  481. next = 0;
  482. var temp = volumeVariance[0];
  483. for (var index = 1; index <= cubeIndex; ++index)
  484. {
  485. if (volumeVariance[index] <= temp) continue;
  486. temp = volumeVariance[index];
  487. next = index;
  488. }
  489. if (temp > 0.0) continue;
  490. colorCount = cubeIndex + 1;
  491. break;
  492. }
  493. return cubes.Take(colorCount).ToList();
  494. }
  495. protected LookupData BuildLookups(IEnumerable<Box> cubes, ColorData data)
  496. {
  497. LookupData lookups = new LookupData(SideSize);
  498. int lookupsCount = lookups.Lookups.Count;
  499. foreach (var cube in cubes)
  500. {
  501. for (var alphaIndex = (byte)(cube.AlphaMinimum + 1); alphaIndex <= cube.AlphaMaximum; ++alphaIndex)
  502. {
  503. for (var redIndex = (byte)(cube.RedMinimum + 1); redIndex <= cube.RedMaximum; ++redIndex)
  504. {
  505. for (var greenIndex = (byte)(cube.GreenMinimum + 1); greenIndex <= cube.GreenMaximum; ++greenIndex)
  506. {
  507. for (var blueIndex = (byte)(cube.BlueMinimum + 1); blueIndex <= cube.BlueMaximum; ++blueIndex)
  508. {
  509. lookups.Tags[alphaIndex, redIndex, greenIndex, blueIndex] = lookupsCount;
  510. }
  511. }
  512. }
  513. }
  514. var weight = Volume(cube, data.Weights);
  515. if (weight <= 0) continue;
  516. var lookup = new Lookup
  517. {
  518. Alpha = (int) (Volume(cube, data.MomentsAlpha)/weight),
  519. Red = (int) (Volume(cube, data.MomentsRed)/weight),
  520. Green = (int) (Volume(cube, data.MomentsGreen)/weight),
  521. Blue = (int) (Volume(cube, data.MomentsBlue)/weight)
  522. };
  523. lookups.Lookups.Add(lookup);
  524. }
  525. return lookups;
  526. }
  527. protected abstract QuantizedPalette GetQuantizedPalette(int colorCount, ColorData data, IEnumerable<Box> cubes, int alphaThreshold);
  528. }
  529. }