gradient.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. #include <precomp.h>
  2. #include "gradient.h"
  3. #include <math.h>//floor
  4. #include <bfc/ptrlist.h>
  5. #include <bfc/parse/pathparse.h>
  6. #define DEFAULT_GRAD_MODE L"linear"
  7. template<class T> inline void SWAP(T &a, T &b) {
  8. T c = a;
  9. a = b;
  10. b = c;
  11. }
  12. inline unsigned int LERPu(unsigned int a, unsigned int b, double p) {
  13. // ASSERT(p >= 0);
  14. // ASSERT(p <= 1.f);
  15. unsigned int ret = (unsigned int)((double)b * p + (double)a * (1. - p));
  16. return ret;
  17. }
  18. inline float LERPf(double a, double b, float p) {
  19. // ASSERT(p >= 0);
  20. // ASSERT(p <= 1.f);
  21. return (float)(b * p + a * (1. - p));
  22. }
  23. Gradient::Gradient() :
  24. gammagroup(L"")
  25. {
  26. gradient_x1 = 0.0f;
  27. gradient_y1 = 0.0f;
  28. gradient_x2 = 1.0f;
  29. gradient_y2 = 1.0f;
  30. reverse_colors = 0;
  31. antialias = 0;
  32. mode = DEFAULT_GRAD_MODE;
  33. list.addItem(new GradientPoint(0.0f, 0xff00ff00));
  34. list.addItem(new GradientPoint(.5, 0x000000ff));
  35. list.addItem(new GradientPoint(1.0f, 0xffff0000));
  36. }
  37. Gradient::~Gradient() {
  38. list.deleteAll();
  39. }
  40. void Gradient::setX1(float x1) {
  41. gradient_x1 = x1;
  42. onParamChange();
  43. }
  44. void Gradient::setY1(float y1) {
  45. gradient_y1 = y1;
  46. onParamChange();
  47. }
  48. void Gradient::setX2(float x2) {
  49. gradient_x2 = x2;
  50. onParamChange();
  51. }
  52. void Gradient::setY2(float y2) {
  53. gradient_y2 = y2;
  54. onParamChange();
  55. }
  56. void Gradient::clearPoints() {
  57. list.deleteAll();
  58. onParamChange();
  59. }
  60. void Gradient::addPoint(float pos, ARGB32 color)
  61. {
  62. list.addItem(new GradientPoint(pos, color, gammagroup));
  63. onParamChange();
  64. }
  65. void Gradient::setPoints(const wchar_t *pointlist)
  66. {
  67. clearPoints();
  68. if (pointlist == NULL || *pointlist == '\0') return;
  69. // 0.5=233,445,245,123;
  70. PathParserW pp(pointlist, L";");
  71. if (pp.getNumStrings() <= 0) return;
  72. for (int i = 0; i < pp.getNumStrings(); i++)
  73. {
  74. PathParserW rp(pp.enumString(i), L"=");
  75. if (rp.getNumStrings() != 2)
  76. continue;
  77. float pos = (float)WTOF(rp.enumString(0));
  78. ARGB32 color = (ARGB32)WASABI_API_SKIN->parse(rp.enumString(1), L"coloralpha");
  79. addPoint(pos, color);
  80. }
  81. }
  82. void Gradient::setReverseColors(int c) {
  83. reverse_colors = c;
  84. }
  85. void Gradient::setAntialias(int c) {
  86. antialias = c;
  87. }
  88. void Gradient::setMode(const wchar_t *_mode) {
  89. mode = _mode;
  90. if (mode.isempty())
  91. mode = DEFAULT_GRAD_MODE;
  92. }
  93. void Gradient::setGammaGroup(const wchar_t *group) {
  94. gammagroup = group;
  95. // reset our points
  96. foreach(list)
  97. list.getfor()->color.setColorGroup(group);
  98. endfor
  99. }
  100. static inline ARGB32 colorLerp(ARGB32 color1, ARGB32 color2, double pos) {
  101. unsigned int a1 = (color1>>24) & 0xff;
  102. unsigned int a2 = (color2>>24) & 0xff;
  103. unsigned int r1 = (color1>>16) & 0xff;
  104. unsigned int r2 = (color2>>16) & 0xff;
  105. unsigned int g1 = (color1>>8) & 0xff;
  106. unsigned int g2 = (color2>>8) & 0xff;
  107. unsigned int b1 = (color1) & 0xff;
  108. unsigned int b2 = (color2) & 0xff;
  109. return (LERPu(a1, a2, pos)<<24) | (LERPu(r1, r2, pos) << 16) | (LERPu(g1,g2,pos)<<8) | LERPu(b1, b2, pos);
  110. }
  111. void Gradient::renderGrad(ARGB32 *ptr, int len, int *positions) {
  112. int npos = list.getNumItems();
  113. ASSERT(npos >= 2);
  114. ARGB32 color1, color2;
  115. for (int i = 0; i < npos-1; i++) {
  116. color1 = list.q(i)->color.getColor();
  117. color2 = list.q(i+1)->color.getColor();
  118. if (reverse_colors) {
  119. color1 = BGRATOARGB(color1);
  120. color2 = BGRATOARGB(color2);
  121. }
  122. int x1 = positions[i];
  123. int x2 = positions[i+1];
  124. if (x1 == x2) continue;
  125. // hflip if need be
  126. if (x1 > x2) {
  127. SWAP(x1, x2);
  128. SWAP(color1, color2);
  129. }
  130. float c = 0;
  131. float segment_len = (float)((x2 - x1)+1);
  132. if (x1 < 0) { // clip left
  133. c += -x1;
  134. x1 = 0;
  135. }
  136. for (int x = x1; x < x2; x++, c += 1.0f) {
  137. if (x >= len) break; // clip right
  138. ptr[x] = colorLerp(color1, color2, c / segment_len);
  139. }
  140. }
  141. #if 0//later
  142. // fill in left if needed
  143. if (positions[0] > 0) MEMFILL<ARGB32>(ptr, list.q(0)->color, positions[0]);
  144. // and right if needed
  145. int rpos = positions[npos-1];
  146. if (rpos < len) MEMFILL<ARGB32>(ptr+rpos, list.getLast()->color, len-rpos);
  147. #endif
  148. }
  149. void Gradient::renderGradient(ARGB32 *bits, int w, int h, int pitch)
  150. {
  151. if (pitch == 0)
  152. pitch = w;
  153. list.sort();
  154. ARGB32 default_color = 0xffff00ff;
  155. if (list.getNumItems() == 1) default_color = list.q(0)->color.getColor();
  156. // blank it out to start
  157. if (pitch == w)
  158. MEMFILL<ARGB32>(bits, default_color, w * h);
  159. else
  160. {
  161. for (int i=0;i<h;i++)
  162. MEMFILL<ARGB32>(bits+i*pitch, default_color, w);
  163. }
  164. if (list.getNumItems() > 1) {
  165. if (mode.iscaseequal(L"linear")) {
  166. //FUCKO: not if endcaps are filled
  167. // force non-vertical lines
  168. if (ABS(gradient_x1 - gradient_x2) < 0.0005f) gradient_x2 = gradient_x1+0.0005f;
  169. double px1 = gradient_x1 * w, py1 = gradient_y1 * h;
  170. double px2 = gradient_x2 * w, py2 = gradient_y2 * h;
  171. // convert to y = mx + b
  172. double m = (py2 - py1)/(px2 - px1);
  173. m = -1.f/m; // invert the slope
  174. int nitems = list.getNumItems();
  175. // get the in-pixels x and y for points on the gradient
  176. for (int i = 0; i < nitems; i++) {
  177. GradientPoint *gp = list.q(i);
  178. // need x and y given pos
  179. gp->x = LERPf(px1, px2, gp->pos);
  180. gp->y = LERPf(py1, py2, gp->pos);
  181. }
  182. MemBlock<int> positions(nitems);
  183. for (int _y = 0; _y < h; _y++) {
  184. // project all the color points onto this scanline
  185. for (int i = 0; i < nitems; i++) {
  186. GradientPoint *gp = list.q(i);
  187. // y = mx + b
  188. // b = y - mx;
  189. double newb = gp->y - m * gp->x;
  190. // y = mx + newb
  191. // y - newb = mx
  192. // (y - newb)/m = x
  193. double xxx = (_y - newb)/m;
  194. positions[i] = (int)floor(xxx+0.5f);
  195. }
  196. renderGrad(bits+_y*pitch, w, positions);
  197. }
  198. } else if (mode.iscaseequal(L"circular")) {
  199. double tot = SQRT(SQR(gradient_x1 - gradient_x2) + SQR(gradient_y1 - gradient_y2));
  200. foreach(list)
  201. GradientPoint *gp = list.getfor();
  202. gp->dist = gp->pos * tot;
  203. endfor
  204. ARGB32 *dst = bits;
  205. for (int y = 0; y < h; y++) {
  206. for (int x = 0; x < w; x++) {
  207. ARGB32 c;
  208. if (antialias) {
  209. double fx = (((double)x)-0.5f) / (double)w;
  210. double fy = (((double)y)-0.5f) / (double)h;
  211. ARGB32 ul = getPixelCirc(fx, fy);
  212. fx = (((double)x)+0.5f) / (double)w;
  213. fy = (((double)y)-0.5f) / (double)h;
  214. ARGB32 ur = getPixelCirc(fx, fy);
  215. fx = (((double)x)+0.5f) / (double)w;
  216. fy = (((double)y)+0.5f) / (double)h;
  217. ARGB32 lr = getPixelCirc(fx, fy);
  218. fx = (((double)x)-0.5f) / (double)w;
  219. fy = (((double)y)+0.5f) / (double)h;
  220. ARGB32 ll = getPixelCirc(fx, fy);
  221. c = colorLerp(colorLerp(ll, lr, 0.5f), colorLerp(ul, ur, 0.5f), 0.5);
  222. } else {
  223. double fy = (double)y / (double)h;
  224. double fx = (double)x / (double)w;
  225. c = getPixelCirc(fx, fy);
  226. }
  227. *dst++ = c;
  228. }
  229. dst += (pitch-w);
  230. }
  231. }
  232. }//list.getNumItems()>1
  233. if (pitch == w)
  234. premultiplyARGB32(bits, w * h);
  235. else
  236. {
  237. for (int i=0;i<h;i++)
  238. premultiplyARGB32(bits+i*pitch, w);
  239. }
  240. }
  241. ARGB32 Gradient::getPixelCirc(double fx, double fy) {
  242. int nitems = list.getNumItems();
  243. //double dist = SQR(fx - gradient_x1) + SQR(fy - gradient_y1);
  244. double dist = SQRT(SQR(fx - gradient_x1) + SQR(fy - gradient_y1));
  245. ARGB32 c = 0xff00ff00;
  246. if (dist <= list.q(0)->dist)
  247. c = list.q(0)->color.getColor();
  248. else if (dist >= list.getLast()->dist)
  249. c = list.getLast()->color.getColor();
  250. else for (int i = 0; i < nitems-1; i++) {
  251. if (list.q(i)->dist <= dist && list.q(i+1)->dist >= dist) {
  252. double pdist = list.q(i+1)->dist - list.q(i)->dist;
  253. double pp = dist - list.q(i)->dist;
  254. pp /= pdist;
  255. if (list.q(i)->color.getColor() == list.q(i+1)->color.getColor())
  256. c = list.q(i)->color.getColor();
  257. else
  258. c = colorLerp(list.q(i)->color.getColor(), list.q(i+1)->color.getColor(), pp);
  259. break;
  260. }
  261. }
  262. if (reverse_colors) c = BGRATOARGB(c);
  263. return c;
  264. }