EntryGroup.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Graphics;
  3. using System;
  4. using System.Collections;
  5. using System.Collections.Concurrent;
  6. using System.Collections.Generic;
  7. namespace OpenVIII
  8. {
  9. public class EntryGroup : IEnumerator, IEnumerable
  10. {
  11. private const int minx = -1000;
  12. private const int miny = -360;
  13. #region Fields
  14. private List<Entry> list;
  15. private Rectangle rectangle;
  16. public bool Trimmed { get; set; } = false;
  17. public EntryGroup(int capacity = 1)
  18. {
  19. list = new List<Entry>(capacity);
  20. rectangle = new Rectangle();
  21. }
  22. public EntryGroup(params Entry[] entries)
  23. {
  24. list = new List<Entry>(entries.Length);
  25. rectangle = new Rectangle();
  26. Add(entries);
  27. }
  28. #endregion Fields
  29. #region Properties
  30. public int Count => list.Count;
  31. //public int Height { get => Rectangle.Height; private set => Rectangle = new Rectangle(Rectangle.X, Rectangle.Y, value, Rectangle.Height); }
  32. //public int Width { get => Rectangle.Width; private set => Rectangle = new Rectangle(Rectangle.X,Rectangle.Y, value,Rectangle.Width); }
  33. public int Height { get => rectangle.Height; set => rectangle.Height = value; }
  34. public int Width { get => rectangle.Width; set => rectangle.Width = value; }
  35. public Rectangle GetRectangle => rectangle;
  36. public object Current => list[position - 1];
  37. #endregion Properties
  38. #region Indexers
  39. /// <summary>
  40. /// donno if this works for assigning. perfer add.
  41. /// </summary>
  42. /// <param name="id"></param>
  43. /// <returns></returns>
  44. public Entry this[int id] => list[id]; /*set { if (list.Count - 1 < id) Add(value); else list[id] = value; }*/
  45. #endregion Indexers
  46. #region Methods
  47. private Vector2 nag_Offset = new Vector2();
  48. private Vector2 pos_Offset = new Vector2();
  49. public void Add(params Entry[] entries)
  50. {
  51. foreach (Entry entry in entries)
  52. {
  53. //TODO fix math
  54. if (list.Count >= 1)
  55. {
  56. //DiscFix
  57. if (entry.Height < float.Epsilon)
  58. {
  59. entry.Height = Height; // one item had a missing height.
  60. if (Math.Abs(nag_Offset.X) + pos_Offset.X + entry.Offset.X < float.Epsilon) //assumes if the items are overlapping put them next to each other instead.
  61. entry.Offset.X = (short)Width;
  62. }
  63. //BarFix
  64. //has 2 sides and no filling.
  65. //if(entry.CurrentPos == 1872)
  66. if (list.Capacity == 2 && list.Count == 1 && (list[0].Width * 2) + list[0].X == entry.X && list[0].Y == entry.Y)
  67. Add(new Entry
  68. {
  69. X = entry.X - list[0].Width,
  70. Y = entry.Y,
  71. Size = list[0].Size,
  72. Tile = Vector2.UnitX,
  73. Offset = new Vector2((int)list[0].Width, 0),
  74. End = new Vector2((int)-list[0].Width, 0)
  75. });
  76. }
  77. list.Add(entry);
  78. Vector2 size = Abs(entry.Offset) + entry.Size;
  79. if (Width < size.X) Width = (int)size.X;
  80. if (Height < size.Y) Height = (int)size.Y;
  81. }
  82. }
  83. private object locker = new object();
  84. private ConcurrentDictionary<int, Color> Average;
  85. public Color MostSaturated(TextureHandler tex, int palette = 0)
  86. {
  87. if (Count >= 1)
  88. {
  89. lock (locker)
  90. {
  91. if (Average == null)
  92. Average = new ConcurrentDictionary<int, Color>();
  93. }
  94. if (!(Average.TryGetValue(palette, out Color ret)))
  95. {
  96. Rectangle r = this[0].GetRectangle;
  97. for (int i = 1; i < Count; i++)
  98. r = Rectangle.Union(r, this[i].GetRectangle);
  99. r = r.Scale(tex.ScaleFactor);
  100. Texture2D t = (Texture2D)tex;
  101. Color[] tc = new Color[r.Width * r.Height];
  102. t.GetData(0, r, tc, 0, tc.Length);
  103. HSL test, last;
  104. last.S = 0;
  105. last.L = 0;
  106. ret = Color.TransparentBlack;
  107. foreach (Color p in tc)
  108. {
  109. if (p.A >= 250)
  110. {
  111. test = p;
  112. if (ret == Color.TransparentBlack)
  113. last = test;
  114. if (test.S > .25f && last.S <= test.S)
  115. {
  116. if (ret == Color.TransparentBlack)
  117. ret = p;
  118. else
  119. {
  120. ret = Color.Lerp(ret, p, .5f);
  121. last = test;
  122. }
  123. }
  124. else if (test.L > .25f && last.L <= test.L)
  125. {
  126. if (ret == Color.TransparentBlack)
  127. ret = p;
  128. else
  129. {
  130. ret = Color.Lerp(ret, p, .5f);
  131. last = test;
  132. }
  133. }
  134. }
  135. }
  136. if (!Average.TryAdd(palette, ret))
  137. throw new Exception("failed to cache color");
  138. }
  139. return ret;
  140. }
  141. throw new Exception("Count is <1");
  142. }
  143. public void Trim(TextureHandler tex)
  144. {
  145. if (!Trimmed)
  146. {
  147. if (Count >= 1)
  148. {
  149. Width = 0;
  150. Height = 0;
  151. Trimmed = true;
  152. Vector2 offset = new Vector2(float.MaxValue);
  153. if (Count == 1)
  154. this[0].Offset = Vector2.Zero;
  155. for (int i = 0; i < Count; i++)
  156. {
  157. Rectangle ret = tex.Trim(this[i].GetRectangle);
  158. this[i].SetTrim_1stPass(ret, Count == 1 ? true : false);
  159. if (Count > 1)
  160. {
  161. if (this[i].Offset.X < offset.X) offset.X = this[i].Offset.X;
  162. if (this[i].Offset.Y < offset.Y) offset.Y = this[i].Offset.Y;
  163. }
  164. }
  165. for (int i = 0; i < Count; i++)
  166. {
  167. if (offset != Vector2.Zero && Count > 1)
  168. this[i].SetTrim_2ndPass(offset);
  169. Point size = new Point((int)(this[i].Width + Math.Abs(this[i].Offset.X)), (int)(this[i].Height + Math.Abs(this[i].Offset.Y)));
  170. if (Width < size.X) Width = size.X;
  171. if (Height < size.Y) Height = size.Y;
  172. }
  173. }
  174. }
  175. }
  176. public static Vector2 Abs(Vector2 v2) => new Vector2(Math.Abs(v2.X), Math.Abs(v2.Y));
  177. public static Point RoundedPoint(Vector2 v) => v.RoundedPoint();
  178. public void Draw(List<TextureHandler> textures, int palette, Rectangle inputdst, Vector2 inscale, float fade = 1f, Color? color = null) =>
  179. Draw(textures, list, palette, inputdst, inscale, fade, new Point(Width, Height), color);
  180. public static int GetChange(int tot, int goal, float scale = 1f) => (int)Math.Round(Math.Abs(tot * scale - goal));
  181. public static void Draw(List<TextureHandler> textures, List<Entry> elist, int palette, Rectangle inputdst, Vector2 inscale, float fade, Point totalSize, Color? color_ = null)
  182. {
  183. Color color = color_ ?? Color.White;
  184. Rectangle dst;
  185. inscale = Abs(inscale);
  186. inputdst.Width = Math.Abs(inputdst.Width);
  187. inputdst.Height = Math.Abs(inputdst.Height);
  188. if (inputdst.Right < minx || inputdst.Bottom < miny)
  189. return;
  190. Vector2 autoscale = new Vector2((float)inputdst.Width / totalSize.X, (float)inputdst.Height / totalSize.Y);
  191. Vector2 scale;
  192. if (inscale == Vector2.Zero || inscale == Vector2.UnitX)
  193. scale = new Vector2(autoscale.X);
  194. #pragma warning disable IDE0045 // Convert to conditional expression
  195. else if (inscale == Vector2.UnitY)
  196. #pragma warning restore IDE0045 // Convert to conditional expression
  197. scale = new Vector2(autoscale.Y);
  198. else
  199. scale = inscale;
  200. foreach (Entry e in elist)
  201. {
  202. if (totalSize == new Point(0))
  203. {
  204. totalSize.X = (int)e.Width;
  205. totalSize.Y = (int)e.Height;
  206. }
  207. int cpalette = e.CustomPalette < 0 || e.CustomPalette >= textures.Count ? palette : e.CustomPalette;
  208. dst = inputdst;
  209. Vector2 Offset = e.Offset * scale;
  210. Point offset2 = RoundedPoint(e.End * scale);
  211. dst.Offset(e.Snap_Right ? inputdst.Width : 0, e.Snap_Bottom ? inputdst.Height : 0);
  212. dst.Offset(RoundedPoint(Offset));
  213. dst.Size = RoundedPoint(e.Size * scale);
  214. Rectangle src = e.GetRectangle;
  215. bool testY = false;
  216. bool testX = false;
  217. CorrectX(inputdst, totalSize, ref dst, autoscale, ref scale, e, ref src);
  218. CorrectY(inputdst, totalSize, ref dst, autoscale, ref scale, e, ref src);
  219. if (dst.Width <= 0 || dst.Height <= 0) continue; //infinate loop prevention
  220. if (e.Tile != Vector2.Zero)
  221. {
  222. do
  223. {
  224. do
  225. {
  226. Point correction = Correction(inputdst, dst, offset2);
  227. testY = testYfunct(dst, inputdst, offset2);
  228. testX = testXfunct(dst, inputdst, offset2);
  229. TileBounds(correction, ref dst, scale, e, ref src, ref testY, ref testX);
  230. textures[cpalette].Draw(dst, src, color * fade);
  231. dst = TileShift(dst, e);
  232. }
  233. while (e.Tile.Y > 0 && testY);
  234. }
  235. while (e.Tile.X > 0 && testX);
  236. }
  237. else
  238. textures[cpalette].Draw(dst, src, color * fade);
  239. }
  240. }
  241. private static void CorrectY(Rectangle inputdst, Point totalSize, ref Rectangle dst, Vector2 autoscale, ref Vector2 scale, Entry e, ref Rectangle src)
  242. {
  243. if (inputdst.Height != 0 && dst.Bottom > inputdst.Bottom)
  244. {
  245. int change = GetChange(totalSize.Y, inputdst.Height, scale.Y); ;
  246. src.Height -= (int)Math.Round(change / scale.Y);
  247. dst.Height -= change;
  248. }
  249. else if (e.Fill.Y > 0 && autoscale.Y > scale.Y)
  250. {
  251. scale.Y = autoscale.Y;
  252. dst.Height = (int)Math.Ceiling((src.Height * autoscale.Y + 0.5f));
  253. }
  254. }
  255. private static void CorrectX(Rectangle inputdst, Point totalSize, ref Rectangle dst, Vector2 autoscale, ref Vector2 scale, Entry e, ref Rectangle src)
  256. {
  257. if ((inputdst.Width != 0) && dst.Right > inputdst.Right)
  258. {
  259. int change = GetChange(totalSize.X, inputdst.Width, scale.X);
  260. src.Width -= (int)Math.Round(change / scale.X);
  261. dst.Width -= change;
  262. }
  263. else if (e.Fill.X > 0 && autoscale.X > scale.X)
  264. {
  265. scale.X = autoscale.X;
  266. dst.Width = (int)Math.Ceiling((src.Width * autoscale.X + 0.5f));
  267. }
  268. }
  269. private static void TileBounds(Point correction, ref Rectangle dst, Vector2 scale, Entry e, ref Rectangle src, ref bool testY, ref bool testX)
  270. {
  271. if (e.Tile.Y > 0 && !testY)
  272. {
  273. dst.Height += correction.Y;
  274. src.Height += (int)Math.Round(correction.Y / scale.Y);
  275. }
  276. if (e.Tile.X > 0 && !testX)
  277. {
  278. dst.Width += correction.X;
  279. src.Width += (int)Math.Round(correction.X / scale.X);
  280. }
  281. }
  282. private static bool testXfunct(Rectangle dst, Rectangle inputdst, Point offset2) => (dst.Right) < (inputdst.Right + offset2.X);
  283. private static bool testYfunct(Rectangle dst, Rectangle inputdst, Point offset2) => (dst.Bottom) < (inputdst.Bottom + offset2.Y);
  284. private static Point Correction(Rectangle inputdst, Rectangle dst, Point offset2) => new Point((inputdst.Right + offset2.X) - (dst.Right), (inputdst.Bottom + offset2.Y) - (dst.Bottom));
  285. private static Rectangle TileShift(Rectangle dst, Entry e)
  286. {
  287. if (e.Tile.Y > 0)
  288. {
  289. dst.Y += dst.Height;
  290. }
  291. if (e.Tile.X > 0)
  292. {
  293. dst.X += dst.Width;
  294. }
  295. return dst;
  296. }
  297. private int position = 0;
  298. public bool MoveNext() => ++position <= list.Count;
  299. public void Reset() => position = 0;
  300. public IEnumerator GetEnumerator() => this;
  301. #endregion Methods
  302. }
  303. }