MPImageHelper.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. using System.Text;
  2. namespace UnityEngine.UI.MPUIKIT {
  3. public static class MPImageHelper {
  4. private static readonly Vector3[] SXy = new Vector3[4];
  5. private static readonly Vector3[] SUv = new Vector3[4];
  6. public static void GenerateSimpleSprite(VertexHelper vh, bool preserveAspect, Canvas canvas,
  7. RectTransform rectTransform, Sprite activeSprite, Color32 color, float falloffDistance) {
  8. vh.Clear();
  9. Vector4 v = GetDrawingDimensions(preserveAspect, activeSprite, canvas, rectTransform);
  10. Vector4 uv = (activeSprite != null)
  11. ? Sprites.DataUtility.GetOuterUV(activeSprite)
  12. : new Vector4(0, 0, 1, 1);
  13. Color32 color32 = color;
  14. vh.Clear();
  15. Vector3[] pos = {
  16. new Vector3(v.x, v.y),
  17. new Vector3(v.x, v.w),
  18. new Vector3(v.z, v.w),
  19. new Vector3(v.z, v.y),
  20. };
  21. Vector2[] uvs = {
  22. new Vector2(uv.x, uv.y),
  23. new Vector2(uv.x, uv.w),
  24. new Vector2(uv.z, uv.w),
  25. new Vector2(uv.z, uv.y),
  26. };
  27. Vector2[] uv1s =
  28. {
  29. new Vector2(0, 0),
  30. new Vector2(0, 1),
  31. new Vector2(1, 1),
  32. new Vector2(1, 0),
  33. };
  34. Vector2 size = new Vector2(v.z - v.x, v.w - v.y);
  35. vh.AddVert(pos[0], color32, uvs[0], uv1s[0], size, Vector2.zero, Vector3.zero, Vector4.zero);
  36. vh.AddVert(pos[1], color32, uvs[1], uv1s[1], size, Vector2.zero, Vector3.zero, Vector4.zero);
  37. vh.AddVert(pos[2], color32, uvs[2], uv1s[2], size, Vector2.zero, Vector3.zero, Vector4.zero);
  38. vh.AddVert(pos[3], color32, uvs[3], uv1s[3], size, Vector2.zero, Vector3.zero, Vector4.zero);
  39. vh.AddTriangle(0, 1, 2);
  40. vh.AddTriangle(2, 3, 0);
  41. }
  42. public static void GenerateFilledSprite(VertexHelper toFill, bool preserveAspect, Canvas canvas,
  43. RectTransform rectTransform, Sprite activeSprite, Color32 color, Image.FillMethod fillMethod,
  44. float fillAmount, int fillOrigin, bool fillClockwise, float falloffDistance) {
  45. toFill.Clear();
  46. if (fillAmount < 0.001f)
  47. return;
  48. Vector4 v = GetDrawingDimensions(preserveAspect, activeSprite, canvas, rectTransform);
  49. Vector2 size = new Vector2(v.z - v.x, v.w - v.y);
  50. Vector4 outer = activeSprite != null
  51. ? Sprites.DataUtility.GetOuterUV(activeSprite)
  52. : new Vector4(0, 0, 1, 1);
  53. UIVertex uiv = UIVertex.simpleVert;
  54. uiv.color = color;
  55. float tx0 = outer.x;
  56. float ty0 = outer.y;
  57. float tx1 = outer.z;
  58. float ty1 = outer.w;
  59. // Horizontal and vertical filled sprites are simple -- just end the Image prematurely
  60. if (fillMethod == Image.FillMethod.Horizontal || fillMethod == Image.FillMethod.Vertical) {
  61. if (fillMethod == Image.FillMethod.Horizontal) {
  62. float fill = (tx1 - tx0) * fillAmount;
  63. if (fillOrigin == 1) {
  64. v.x = v.z - (v.z - v.x) * fillAmount;
  65. tx0 = tx1 - fill;
  66. }
  67. else {
  68. v.z = v.x + (v.z - v.x) * fillAmount;
  69. tx1 = tx0 + fill;
  70. }
  71. }
  72. else if (fillMethod == Image.FillMethod.Vertical) {
  73. float fill = (ty1 - ty0) * fillAmount;
  74. if (fillOrigin == 1) {
  75. v.y = v.w - (v.w - v.y) * fillAmount;
  76. ty0 = ty1 - fill;
  77. }
  78. else {
  79. v.w = v.y + (v.w - v.y) * fillAmount;
  80. ty1 = ty0 + fill;
  81. }
  82. }
  83. }
  84. SXy[0] = new Vector2(v.x, v.y);
  85. SXy[1] = new Vector2(v.x, v.w);
  86. SXy[2] = new Vector2(v.z, v.w);
  87. SXy[3] = new Vector2(v.z, v.y);
  88. SUv[0] = new Vector2(tx0, ty0);
  89. SUv[1] = new Vector2(tx0, ty1);
  90. SUv[2] = new Vector2(tx1, ty1);
  91. SUv[3] = new Vector2(tx1, ty0);
  92. {
  93. if (fillAmount < 1f && fillMethod != Image.FillMethod.Horizontal &&
  94. fillMethod != Image.FillMethod.Vertical) {
  95. if (fillMethod == Image.FillMethod.Radial90) {
  96. if (RadialCut(SXy, SUv, fillAmount, fillClockwise, fillOrigin))
  97. AddQuad(toFill, SXy, color, SUv, size);
  98. }
  99. else if (fillMethod == Image.FillMethod.Radial180) {
  100. for (int side = 0; side < 2; ++side) {
  101. float fx0, fx1, fy0, fy1;
  102. int even = fillOrigin > 1 ? 1 : 0;
  103. if (fillOrigin == 0 || fillOrigin == 2) {
  104. fy0 = 0f;
  105. fy1 = 1f;
  106. if (side == even) {
  107. fx0 = 0f;
  108. fx1 = 0.5f;
  109. }
  110. else {
  111. fx0 = 0.5f;
  112. fx1 = 1f;
  113. }
  114. }
  115. else {
  116. fx0 = 0f;
  117. fx1 = 1f;
  118. if (side == even) {
  119. fy0 = 0.5f;
  120. fy1 = 1f;
  121. }
  122. else {
  123. fy0 = 0f;
  124. fy1 = 0.5f;
  125. }
  126. }
  127. SXy[0].x = Mathf.Lerp(v.x, v.z, fx0);
  128. SXy[1].x = SXy[0].x;
  129. SXy[2].x = Mathf.Lerp(v.x, v.z, fx1);
  130. SXy[3].x = SXy[2].x;
  131. SXy[0].y = Mathf.Lerp(v.y, v.w, fy0);
  132. SXy[1].y = Mathf.Lerp(v.y, v.w, fy1);
  133. SXy[2].y = SXy[1].y;
  134. SXy[3].y = SXy[0].y;
  135. SUv[0].x = Mathf.Lerp(tx0, tx1, fx0);
  136. SUv[1].x = SUv[0].x;
  137. SUv[2].x = Mathf.Lerp(tx0, tx1, fx1);
  138. SUv[3].x = SUv[2].x;
  139. SUv[0].y = Mathf.Lerp(ty0, ty1, fy0);
  140. SUv[1].y = Mathf.Lerp(ty0, ty1, fy1);
  141. SUv[2].y = SUv[1].y;
  142. SUv[3].y = SUv[0].y;
  143. float val = fillClockwise ? fillAmount * 2f - side : fillAmount * 2f - (1 - side);
  144. if (RadialCut(SXy, SUv, Mathf.Clamp01(val), fillClockwise,
  145. ((side + fillOrigin + 3) % 4))) {
  146. AddQuad(toFill, SXy, color, SUv, size);
  147. }
  148. }
  149. }
  150. else if (fillMethod == Image.FillMethod.Radial360) {
  151. for (int corner = 0; corner < 4; ++corner) {
  152. float fx0, fx1, fy0, fy1;
  153. if (corner < 2) {
  154. fx0 = 0f;
  155. fx1 = 0.5f;
  156. }
  157. else {
  158. fx0 = 0.5f;
  159. fx1 = 1f;
  160. }
  161. if (corner == 0 || corner == 3) {
  162. fy0 = 0f;
  163. fy1 = 0.5f;
  164. }
  165. else {
  166. fy0 = 0.5f;
  167. fy1 = 1f;
  168. }
  169. SXy[0].x = Mathf.Lerp(v.x, v.z, fx0);
  170. SXy[1].x = SXy[0].x;
  171. SXy[2].x = Mathf.Lerp(v.x, v.z, fx1);
  172. SXy[3].x = SXy[2].x;
  173. SXy[0].y = Mathf.Lerp(v.y, v.w, fy0);
  174. SXy[1].y = Mathf.Lerp(v.y, v.w, fy1);
  175. SXy[2].y = SXy[1].y;
  176. SXy[3].y = SXy[0].y;
  177. SUv[0].x = Mathf.Lerp(tx0, tx1, fx0);
  178. SUv[1].x = SUv[0].x;
  179. SUv[2].x = Mathf.Lerp(tx0, tx1, fx1);
  180. SUv[3].x = SUv[2].x;
  181. SUv[0].y = Mathf.Lerp(ty0, ty1, fy0);
  182. SUv[1].y = Mathf.Lerp(ty0, ty1, fy1);
  183. SUv[2].y = SUv[1].y;
  184. SUv[3].y = SUv[0].y;
  185. float val = fillClockwise
  186. ? fillAmount * 4f - ((corner + fillOrigin) % 4)
  187. : fillAmount * 4f - (3 - ((corner + fillOrigin) % 4));
  188. if (RadialCut(SXy, SUv, Mathf.Clamp01(val), fillClockwise, ((corner + 2) % 4)))
  189. AddQuad(toFill, SXy, color, SUv, size);
  190. }
  191. }
  192. }
  193. else {
  194. AddQuad(toFill, SXy, color, SUv, size);
  195. }
  196. }
  197. }
  198. private static void AddQuad(VertexHelper vertexHelper, Vector3[] quadPositions, Color32 color,
  199. Vector3[] quadUVs, Vector2 size) {
  200. int startIndex = vertexHelper.currentVertCount;
  201. StringBuilder sr = new StringBuilder();
  202. for (int i = 0; i < 4; ++i) {
  203. vertexHelper.AddVert(quadPositions[i], color, quadUVs[i], quadUVs[i], size, Vector2.zero,
  204. Vector3.zero, Vector4.zero);
  205. sr.AppendLine($"Pos: {quadPositions[i]}, uv: {quadUVs[i]}");
  206. }
  207. vertexHelper.AddTriangle(startIndex, startIndex + 1, startIndex + 2);
  208. vertexHelper.AddTriangle(startIndex + 2, startIndex + 3, startIndex);
  209. }
  210. private static Vector4 GetDrawingDimensions(bool shouldPreserveAspect, Sprite activeSprite, Canvas canvas,
  211. RectTransform rectTransform) {
  212. var padding = activeSprite == null ? Vector4.zero : Sprites.DataUtility.GetPadding(activeSprite);
  213. var size = activeSprite == null
  214. ? new Vector2(rectTransform.rect.width, rectTransform.rect.height)
  215. : new Vector2(activeSprite.rect.width, activeSprite.rect.height);
  216. if (size.x <= 0) size.x = 1;
  217. if (size.y <= 0) size.y = 1;
  218. Rect r = GetPixelAdjustedRect(canvas, rectTransform);
  219. //Debug.Log(string.Format("r:{2}, size:{0}, padding:{1}", size, padding, r));
  220. int spriteW = Mathf.RoundToInt(size.x);
  221. int spriteH = Mathf.RoundToInt(size.y);
  222. Vector4 v = new Vector4(
  223. padding.x / spriteW,
  224. padding.y / spriteH,
  225. (spriteW - padding.z) / spriteW,
  226. (spriteH - padding.w) / spriteH);
  227. if (shouldPreserveAspect && size.sqrMagnitude > 0.0f) {
  228. PreserveSpriteAspectRatio(ref r, rectTransform, size);
  229. }
  230. v = new Vector4(
  231. r.x + r.width * v.x,
  232. r.y + r.height * v.y,
  233. r.x + r.width * v.z,
  234. r.y + r.height * v.w
  235. );
  236. return v;
  237. }
  238. public static void PreserveSpriteAspectRatio(ref Rect rect, RectTransform rectTransform, Vector2 spriteSize) {
  239. float spriteRatio = spriteSize.x / spriteSize.y;
  240. float rectRatio = rect.width / rect.height;
  241. if (spriteRatio > rectRatio) {
  242. float oldHeight = rect.height;
  243. rect.height = rect.width * (1.0f / spriteRatio);
  244. rect.y += (oldHeight - rect.height) * rectTransform.pivot.y;
  245. }
  246. else {
  247. float oldWidth = rect.width;
  248. rect.width = rect.height * spriteRatio;
  249. rect.x += (oldWidth - rect.width) * rectTransform.pivot.x;
  250. }
  251. }
  252. private static Rect GetPixelAdjustedRect(Canvas canvas, RectTransform rectTransform) {
  253. if (!canvas || canvas.renderMode == RenderMode.WorldSpace || canvas.scaleFactor == 0.0f ||
  254. !canvas.pixelPerfect) {
  255. return rectTransform.rect;
  256. }
  257. return RectTransformUtility.PixelAdjustRect(rectTransform, canvas);
  258. }
  259. private static bool RadialCut(Vector3[] xy, Vector3[] uv, float fill, bool invert, int corner) {
  260. // Nothing to fill
  261. if (fill < 0.001f) return false;
  262. // Even corners invert the fill direction
  263. if ((corner & 1) == 1) invert = !invert;
  264. // Nothing to adjust
  265. if (!invert && fill > 0.999f) return true;
  266. // Convert 0-1 value into 0 to 90 degrees angle in radians
  267. float angle = Mathf.Clamp01(fill);
  268. if (invert) angle = 1f - angle;
  269. angle *= 90f * Mathf.Deg2Rad;
  270. // Calculate the effective X and Y factors
  271. float cos = Mathf.Cos(angle);
  272. float sin = Mathf.Sin(angle);
  273. RadialCut(xy, cos, sin, invert, corner);
  274. RadialCut(uv, cos, sin, invert, corner);
  275. return true;
  276. }
  277. private static void RadialCut(Vector3[] xy, float cos, float sin, bool invert, int corner) {
  278. int i0 = corner;
  279. int i1 = ((corner + 1) % 4);
  280. int i2 = ((corner + 2) % 4);
  281. int i3 = ((corner + 3) % 4);
  282. if ((corner & 1) == 1) {
  283. if (sin > cos) {
  284. cos /= sin;
  285. sin = 1f;
  286. if (invert) {
  287. xy[i1].x = Mathf.Lerp(xy[i0].x, xy[i2].x, cos);
  288. xy[i2].x = xy[i1].x;
  289. }
  290. }
  291. else if (cos > sin) {
  292. sin /= cos;
  293. cos = 1f;
  294. if (!invert) {
  295. xy[i2].y = Mathf.Lerp(xy[i0].y, xy[i2].y, sin);
  296. xy[i3].y = xy[i2].y;
  297. }
  298. }
  299. else {
  300. cos = 1f;
  301. sin = 1f;
  302. }
  303. if (!invert) xy[i3].x = Mathf.Lerp(xy[i0].x, xy[i2].x, cos);
  304. else xy[i1].y = Mathf.Lerp(xy[i0].y, xy[i2].y, sin);
  305. }
  306. else {
  307. if (cos > sin) {
  308. sin /= cos;
  309. cos = 1f;
  310. if (!invert) {
  311. xy[i1].y = Mathf.Lerp(xy[i0].y, xy[i2].y, sin);
  312. xy[i2].y = xy[i1].y;
  313. }
  314. }
  315. else if (sin > cos) {
  316. cos /= sin;
  317. sin = 1f;
  318. if (invert) {
  319. xy[i2].x = Mathf.Lerp(xy[i0].x, xy[i2].x, cos);
  320. xy[i3].x = xy[i2].x;
  321. }
  322. }
  323. else {
  324. cos = 1f;
  325. sin = 1f;
  326. }
  327. if (invert) xy[i3].y = Mathf.Lerp(xy[i0].y, xy[i2].y, sin);
  328. else xy[i1].x = Mathf.Lerp(xy[i0].x, xy[i2].x, cos);
  329. }
  330. }
  331. }
  332. }