PngQuantizerBase.cs 34 KB

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