No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PixelBlends.cs 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. using UnityEngine;
  2. using Unity.Burst;
  3. namespace UnityEditor.U2D.Aseprite
  4. {
  5. [BurstCompile]
  6. internal static class PixelBlends
  7. {
  8. [BurstCompile]
  9. public static void Normal(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  10. {
  11. var inAlpha = inColor.a / 255f;
  12. outColor = new Color32
  13. {
  14. a = (byte)(inColor.a + prevOutColor.a * (1f - inAlpha))
  15. };
  16. var prevAlpha = (prevOutColor.a * (1f - inAlpha)) / 255f;
  17. var premultiplyAlpha = outColor.a > 0 ? 255f / outColor.a : 1f;
  18. outColor.r = (byte)((inColor.r * inAlpha + prevOutColor.r * prevAlpha) * premultiplyAlpha);
  19. outColor.g = (byte)((inColor.g * inAlpha + prevOutColor.g * prevAlpha) * premultiplyAlpha);
  20. outColor.b = (byte)((inColor.b * inAlpha + prevOutColor.b * prevAlpha) * premultiplyAlpha);
  21. }
  22. [BurstCompile]
  23. public static void Darken(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  24. {
  25. outColor = new Color32();
  26. if (prevOutColor == Color.clear)
  27. {
  28. outColor = inColor;
  29. return;
  30. }
  31. var darken = new Color32
  32. {
  33. r = inColor.r < prevOutColor.r ? inColor.r : prevOutColor.r,
  34. g = inColor.g < prevOutColor.g ? inColor.g : prevOutColor.g,
  35. b = inColor.b < prevOutColor.b ? inColor.b : prevOutColor.b,
  36. a = inColor.a
  37. };
  38. Normal(in prevOutColor, in darken, out outColor);
  39. }
  40. [BurstCompile]
  41. public static void Multiply(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  42. {
  43. outColor = new Color32();
  44. if (prevOutColor == Color.clear)
  45. {
  46. outColor = inColor;
  47. return;
  48. }
  49. var multiply = new Color32
  50. {
  51. r = MultiplyUnsignedByte(inColor.r, prevOutColor.r),
  52. g = MultiplyUnsignedByte(inColor.g, prevOutColor.g),
  53. b = MultiplyUnsignedByte(inColor.b, prevOutColor.b),
  54. a = inColor.a
  55. };
  56. Normal(in prevOutColor, in multiply, out outColor);
  57. }
  58. [BurstCompile]
  59. public static void ColorBurn(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  60. {
  61. outColor = new Color32();
  62. if (prevOutColor == Color.clear)
  63. {
  64. outColor = inColor;
  65. return;
  66. }
  67. var burn = new Color32
  68. {
  69. r = BurnChannel(prevOutColor.r, inColor.r),
  70. g = BurnChannel(prevOutColor.g, inColor.g),
  71. b = BurnChannel(prevOutColor.b, inColor.b),
  72. a = inColor.a
  73. };
  74. Normal(in prevOutColor, in burn, out outColor);
  75. }
  76. [BurstCompile]
  77. static byte BurnChannel(byte prevVal, byte inVal)
  78. {
  79. if (prevVal == 255)
  80. return prevVal;
  81. prevVal = (byte)(255 - prevVal);
  82. if (prevVal >= inVal)
  83. return 0;
  84. var div = DivideUnsignedByte(prevVal, inVal);
  85. return (byte)(255 - div);
  86. }
  87. [BurstCompile]
  88. public static void Lighten(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  89. {
  90. outColor = new Color32();
  91. if (prevOutColor == Color.clear)
  92. {
  93. outColor = inColor;
  94. return;
  95. }
  96. var lighten = new Color32
  97. {
  98. r = inColor.r > prevOutColor.r ? inColor.r : prevOutColor.r,
  99. g = inColor.g > prevOutColor.g ? inColor.g : prevOutColor.g,
  100. b = inColor.b > prevOutColor.b ? inColor.b : prevOutColor.b,
  101. a = inColor.a
  102. };
  103. Normal(in prevOutColor, in lighten, out outColor);
  104. }
  105. [BurstCompile]
  106. public static void Screen(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  107. {
  108. outColor = new Color32();
  109. if (prevOutColor == Color.clear)
  110. {
  111. outColor = inColor;
  112. return;
  113. }
  114. var multiply = new Color32
  115. {
  116. r = MultiplyUnsignedByte(inColor.r, prevOutColor.r),
  117. g = MultiplyUnsignedByte(inColor.g, prevOutColor.g),
  118. b = MultiplyUnsignedByte(inColor.b, prevOutColor.b)
  119. };
  120. var screen = new Color32
  121. {
  122. r = (byte)((inColor.r + prevOutColor.r) - multiply.r),
  123. g = (byte)((inColor.g + prevOutColor.g) - multiply.g),
  124. b = (byte)((inColor.b + prevOutColor.b) - multiply.b),
  125. a = inColor.a
  126. };
  127. Normal(in prevOutColor, in screen, out outColor);
  128. }
  129. [BurstCompile]
  130. public static void ColorDodge(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  131. {
  132. outColor = new Color32();
  133. if (prevOutColor == Color.clear)
  134. {
  135. outColor = inColor;
  136. return;
  137. }
  138. var burn = new Color32
  139. {
  140. r = DodgeChannel(prevOutColor.r, inColor.r),
  141. g = DodgeChannel(prevOutColor.g, inColor.g),
  142. b = DodgeChannel(prevOutColor.b, inColor.b),
  143. a = inColor.a
  144. };
  145. Normal(in prevOutColor, in burn, out outColor);
  146. }
  147. [BurstCompile]
  148. static byte DodgeChannel(byte prevVal, byte inVal)
  149. {
  150. if (prevVal == 0)
  151. return 0;
  152. inVal = (byte)(255 - inVal);
  153. if (prevVal >= inVal)
  154. return 255;
  155. var div = DivideUnsignedByte(prevVal, inVal);
  156. return div;
  157. }
  158. [BurstCompile]
  159. public static void Addition(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  160. {
  161. outColor = new Color32();
  162. if (prevOutColor == Color.clear)
  163. {
  164. outColor = inColor;
  165. return;
  166. }
  167. var addition = new Color32
  168. {
  169. r = (byte)Mathf.Min(inColor.r + prevOutColor.r, 255),
  170. g = (byte)Mathf.Min(inColor.g + prevOutColor.g, 255),
  171. b = (byte)Mathf.Min(inColor.b + prevOutColor.b, 255),
  172. a = inColor.a
  173. };
  174. Normal(in prevOutColor, in addition, out outColor);
  175. }
  176. [BurstCompile]
  177. public static void Overlay(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  178. {
  179. outColor = new Color32();
  180. if (prevOutColor == Color.clear)
  181. {
  182. outColor = inColor;
  183. return;
  184. }
  185. var overlay = new Color32
  186. {
  187. r = HardLightChannel(inColor.r, prevOutColor.r),
  188. g = HardLightChannel(inColor.g, prevOutColor.g),
  189. b = HardLightChannel(inColor.b, prevOutColor.b),
  190. a = inColor.a
  191. };
  192. Normal(in prevOutColor, in overlay, out outColor);
  193. }
  194. [BurstCompile]
  195. static byte HardLightChannel(byte valA, byte valB)
  196. {
  197. if (valB < 128)
  198. return MultiplyUnsignedByte(valA, (byte)(valB << 1));
  199. return ScreenUnsignedByte(valA, (byte)((valB << 1) - 255));
  200. }
  201. [BurstCompile]
  202. public static void SoftLight(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  203. {
  204. outColor = new Color32();
  205. if (prevOutColor == Color.clear)
  206. {
  207. outColor = inColor;
  208. return;
  209. }
  210. var overlay = new Color32
  211. {
  212. r = SoftLightChannel(prevOutColor.r, inColor.r),
  213. g = SoftLightChannel(prevOutColor.g, inColor.g),
  214. b = SoftLightChannel(prevOutColor.b, inColor.b),
  215. a = inColor.a
  216. };
  217. Normal(in prevOutColor, in overlay, out outColor);
  218. }
  219. [BurstCompile]
  220. static byte SoftLightChannel(byte inA, byte inB)
  221. {
  222. var valA = inA / 255f;
  223. var valB = inB / 255f;
  224. float valC, final;
  225. if (valA <= 0.25f)
  226. valC = ((16 * valA - 12) * valA + 4) * valA;
  227. else
  228. valC = Mathf.Sqrt(valA);
  229. if (valB <= 0.5f)
  230. final = valA - (1f - 2f * valB) * valA * (1f - valA);
  231. else
  232. final = valA + (2f * valB - 1f) * (valC - valA);
  233. return (byte)(final * 255f + 0.5f);
  234. }
  235. [BurstCompile]
  236. public static void HardLight(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  237. {
  238. outColor = new Color32();
  239. if (prevOutColor == Color.clear)
  240. {
  241. outColor = inColor;
  242. return;
  243. }
  244. var overlay = new Color32
  245. {
  246. r = HardLightChannel(prevOutColor.r, inColor.r),
  247. g = HardLightChannel(prevOutColor.g, inColor.g),
  248. b = HardLightChannel(prevOutColor.b, inColor.b),
  249. a = inColor.a
  250. };
  251. Normal(in prevOutColor, in overlay, out outColor);
  252. }
  253. [BurstCompile]
  254. public static void Difference(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  255. {
  256. outColor = new Color32();
  257. if (prevOutColor == Color.clear)
  258. {
  259. outColor = inColor;
  260. return;
  261. }
  262. var difference = new Color32
  263. {
  264. r = DifferenceChannel(prevOutColor.r, inColor.r),
  265. g = DifferenceChannel(prevOutColor.g, inColor.g),
  266. b = DifferenceChannel(prevOutColor.b, inColor.b),
  267. a = inColor.a
  268. };
  269. Normal(in prevOutColor, in difference, out outColor);
  270. }
  271. [BurstCompile]
  272. static byte DifferenceChannel(byte valA, byte valB)
  273. {
  274. return (byte)Mathf.Abs(valA - valB);
  275. }
  276. [BurstCompile]
  277. public static void Exclusion(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  278. {
  279. outColor = new Color32();
  280. if (prevOutColor == Color.clear)
  281. {
  282. outColor = inColor;
  283. return;
  284. }
  285. var exclusion = new Color32
  286. {
  287. r = ExclusionChannel(prevOutColor.r, inColor.r),
  288. g = ExclusionChannel(prevOutColor.g, inColor.g),
  289. b = ExclusionChannel(prevOutColor.b, inColor.b),
  290. a = inColor.a
  291. };
  292. Normal(in prevOutColor, in exclusion, out outColor);
  293. }
  294. [BurstCompile]
  295. static byte ExclusionChannel(byte valA, byte valB)
  296. {
  297. var valC = MultiplyUnsignedByte(valA, valB);
  298. return (byte)(valA + valB - 2 * valC);
  299. }
  300. [BurstCompile]
  301. public static void Subtract(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  302. {
  303. outColor = new Color32();
  304. if (prevOutColor == Color.clear)
  305. {
  306. outColor = inColor;
  307. return;
  308. }
  309. var subtract = new Color32
  310. {
  311. r = (byte)Mathf.Max(prevOutColor.r - inColor.r, 0),
  312. g = (byte)Mathf.Max(prevOutColor.g - inColor.g, 0),
  313. b = (byte)Mathf.Max(prevOutColor.b - inColor.b, 0),
  314. a = inColor.a
  315. };
  316. Normal(in prevOutColor, in subtract, out outColor);
  317. }
  318. [BurstCompile]
  319. public static void Divide(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  320. {
  321. outColor = new Color32();
  322. if (prevOutColor == Color.clear)
  323. {
  324. outColor = inColor;
  325. return;
  326. }
  327. var divide = new Color32
  328. {
  329. r = DivideChannel(prevOutColor.r, inColor.r),
  330. g = DivideChannel(prevOutColor.g, inColor.g),
  331. b = DivideChannel(prevOutColor.b, inColor.b),
  332. a = inColor.a
  333. };
  334. Normal(in prevOutColor, in divide, out outColor);
  335. }
  336. [BurstCompile]
  337. static byte DivideChannel(byte valA, byte valB)
  338. {
  339. if (valA == 0)
  340. return 0;
  341. else if (valA >= valB)
  342. return 255;
  343. else
  344. return DivideUnsignedByte(valA, valB);
  345. }
  346. [BurstCompile]
  347. public static void Hue(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  348. {
  349. outColor = new Color32();
  350. if (prevOutColor == Color.clear)
  351. {
  352. outColor = inColor;
  353. return;
  354. }
  355. var r = prevOutColor.r / 255f;
  356. var g = prevOutColor.g / 255f;
  357. var b = prevOutColor.b / 255f;
  358. var s = GetSaturation(r, g, b);
  359. var l = GetLuminosity(r, g, b);
  360. r = inColor.r / 255f;
  361. g = inColor.g / 255f;
  362. b = inColor.b / 255f;
  363. SetSaturation(ref r, ref g, ref b, s);
  364. SetLuminosity(ref r, ref g, ref b, l);
  365. var hue = new Color32()
  366. {
  367. r = (byte)Mathf.RoundToInt(r * 255f),
  368. g = (byte)Mathf.RoundToInt(g * 255f),
  369. b = (byte)Mathf.RoundToInt(b * 255f),
  370. a = inColor.a
  371. };
  372. Normal(in prevOutColor, in hue, out outColor);
  373. }
  374. [BurstCompile]
  375. public static void Saturation(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  376. {
  377. outColor = new Color32();
  378. if (prevOutColor == Color.clear)
  379. {
  380. outColor = inColor;
  381. return;
  382. }
  383. var r = inColor.r / 255f;
  384. var g = inColor.g / 255f;
  385. var b = inColor.b / 255f;
  386. var s = GetSaturation(r, g, b);
  387. r = prevOutColor.r / 255f;
  388. g = prevOutColor.g / 255f;
  389. b = prevOutColor.b / 255f;
  390. var l = GetLuminosity(r, g, b);
  391. SetSaturation(ref r, ref g, ref b, s);
  392. SetLuminosity(ref r, ref g, ref b, l);
  393. var saturation = new Color32()
  394. {
  395. r = (byte)Mathf.RoundToInt(r * 255f),
  396. g = (byte)Mathf.RoundToInt(g * 255f),
  397. b = (byte)Mathf.RoundToInt(b * 255f),
  398. a = inColor.a
  399. };
  400. Normal(in prevOutColor, in saturation, out outColor);
  401. }
  402. [BurstCompile]
  403. public static void ColorBlend(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  404. {
  405. outColor = new Color32();
  406. if (prevOutColor == Color.clear)
  407. {
  408. outColor = inColor;
  409. return;
  410. }
  411. var r = prevOutColor.r / 255f;
  412. var g = prevOutColor.g / 255f;
  413. var b = prevOutColor.b / 255f;
  414. var l = GetLuminosity(r, g, b);
  415. r = inColor.r / 255f;
  416. g = inColor.g / 255f;
  417. b = inColor.b / 255f;
  418. SetLuminosity(ref r, ref g, ref b, l);
  419. var color = new Color32()
  420. {
  421. r = (byte)Mathf.RoundToInt(r * 255f),
  422. g = (byte)Mathf.RoundToInt(g * 255f),
  423. b = (byte)Mathf.RoundToInt(b * 255f),
  424. a = inColor.a
  425. };
  426. Normal(in prevOutColor, in color, out outColor);
  427. }
  428. [BurstCompile]
  429. public static void Luminosity(in Color32 prevOutColor, in Color32 inColor, out Color32 outColor)
  430. {
  431. outColor = new Color32();
  432. if (prevOutColor == Color.clear)
  433. {
  434. outColor = inColor;
  435. return;
  436. }
  437. var r = inColor.r / 255f;
  438. var g = inColor.g / 255f;
  439. var b = inColor.b / 255f;
  440. var l = GetLuminosity(r, g, b);
  441. r = prevOutColor.r / 255f;
  442. g = prevOutColor.g / 255f;
  443. b = prevOutColor.b / 255f;
  444. SetLuminosity(ref r, ref g, ref b, l);
  445. var luminosity = new Color32()
  446. {
  447. r = (byte)Mathf.RoundToInt(r * 255f),
  448. g = (byte)Mathf.RoundToInt(g * 255f),
  449. b = (byte)Mathf.RoundToInt(b * 255f),
  450. a = inColor.a
  451. };
  452. Normal(in prevOutColor, in luminosity, out outColor);
  453. }
  454. [BurstCompile]
  455. static byte MultiplyUnsignedByte(byte valA, byte valB)
  456. {
  457. const uint oneHalf = 0x80;
  458. const int gShift = 8;
  459. var valC = (int)((valA * valB) + oneHalf);
  460. return (byte)(((valC >> gShift) + valC) >> gShift);
  461. }
  462. [BurstCompile]
  463. static byte ScreenUnsignedByte(byte valA, byte valB)
  464. {
  465. return (byte)(valB + valA - MultiplyUnsignedByte(valB, valA));
  466. }
  467. [BurstCompile]
  468. static byte DivideUnsignedByte(byte valA, byte valB)
  469. {
  470. return (byte)((valA * 0xFF) / valB);
  471. }
  472. [BurstCompile]
  473. static float GetSaturation(float r, float g, float b)
  474. {
  475. return Mathf.Max(r, Mathf.Max(g, b)) - Mathf.Min(r, Mathf.Min(g, b));
  476. }
  477. [BurstCompile]
  478. static void SetSaturation(ref float r, ref float g, ref float b, float s)
  479. {
  480. ref var min = ref MinRef(ref r, ref MinRef(ref g, ref b));
  481. ref var mid = ref MidRef(ref r, ref g, ref b);
  482. ref var max = ref MaxRef(ref r, ref MaxRef(ref g, ref b));
  483. if (max > min)
  484. {
  485. mid = ((mid - min) * s) / (max - min);
  486. max = s;
  487. }
  488. else
  489. mid = max = 0;
  490. min = 0f;
  491. }
  492. [BurstCompile]
  493. static ref float MaxRef(ref float a, ref float b)
  494. {
  495. if (a > b)
  496. return ref a;
  497. return ref b;
  498. }
  499. [BurstCompile]
  500. static ref float MinRef(ref float a, ref float b)
  501. {
  502. if (a < b)
  503. return ref a;
  504. return ref b;
  505. }
  506. static ref float MidRef(ref float a, ref float b, ref float c)
  507. {
  508. if (a > b)
  509. {
  510. if (b > c)
  511. {
  512. return ref b;
  513. }
  514. if (a > c)
  515. return ref c;
  516. return ref a;
  517. }
  518. if (!(b > c))
  519. return ref b;
  520. if (c > a)
  521. return ref c;
  522. return ref a;
  523. }
  524. [BurstCompile]
  525. static float GetLuminosity(float r, float g, float b)
  526. {
  527. return (0.3f * r) + (0.59f * g) + (0.11f * b);
  528. }
  529. [BurstCompile]
  530. static void SetLuminosity(ref float r, ref float g, ref float b, float l)
  531. {
  532. var d = l - GetLuminosity(r, g, b);
  533. r += d;
  534. g += d;
  535. b += d;
  536. ClipColor(ref r, ref g, ref b);
  537. }
  538. [BurstCompile]
  539. static void ClipColor(ref float r, ref float g, ref float b)
  540. {
  541. var l = GetLuminosity(r, g, b);
  542. var n = Mathf.Min(r, Mathf.Min(g, b));
  543. var x = Mathf.Max(r, Mathf.Max(g, b));
  544. if (n < 0)
  545. {
  546. r = l + (((r - l) * l) / (l - n));
  547. g = l + (((g - l) * l) / (l - n));
  548. b = l + (((b - l) * l) / (l - n));
  549. }
  550. if (x > 1)
  551. {
  552. r = l + (((r - l) * (1 - l)) / (x - l));
  553. g = l + (((g - l) * (1 - l)) / (x - l));
  554. b = l + (((b - l) * (1 - l)) / (x - l));
  555. }
  556. }
  557. }
  558. }