#include #include "gradient.h" #include //floor #include #include #define DEFAULT_GRAD_MODE L"linear" template inline void SWAP(T &a, T &b) { T c = a; a = b; b = c; } inline unsigned int LERPu(unsigned int a, unsigned int b, double p) { // ASSERT(p >= 0); // ASSERT(p <= 1.f); unsigned int ret = (unsigned int)((double)b * p + (double)a * (1. - p)); return ret; } inline float LERPf(double a, double b, float p) { // ASSERT(p >= 0); // ASSERT(p <= 1.f); return (float)(b * p + a * (1. - p)); } Gradient::Gradient() : gammagroup(L"") { gradient_x1 = 0.0f; gradient_y1 = 0.0f; gradient_x2 = 1.0f; gradient_y2 = 1.0f; reverse_colors = 0; antialias = 0; mode = DEFAULT_GRAD_MODE; list.addItem(new GradientPoint(0.0f, 0xff00ff00)); list.addItem(new GradientPoint(.5, 0x000000ff)); list.addItem(new GradientPoint(1.0f, 0xffff0000)); } Gradient::~Gradient() { list.deleteAll(); } void Gradient::setX1(float x1) { gradient_x1 = x1; onParamChange(); } void Gradient::setY1(float y1) { gradient_y1 = y1; onParamChange(); } void Gradient::setX2(float x2) { gradient_x2 = x2; onParamChange(); } void Gradient::setY2(float y2) { gradient_y2 = y2; onParamChange(); } void Gradient::clearPoints() { list.deleteAll(); onParamChange(); } void Gradient::addPoint(float pos, ARGB32 color) { list.addItem(new GradientPoint(pos, color, gammagroup)); onParamChange(); } void Gradient::setPoints(const wchar_t *pointlist) { clearPoints(); if (pointlist == NULL || *pointlist == '\0') return; // 0.5=233,445,245,123; PathParserW pp(pointlist, L";"); if (pp.getNumStrings() <= 0) return; for (int i = 0; i < pp.getNumStrings(); i++) { PathParserW rp(pp.enumString(i), L"="); if (rp.getNumStrings() != 2) continue; float pos = (float)WTOF(rp.enumString(0)); ARGB32 color = (ARGB32)WASABI_API_SKIN->parse(rp.enumString(1), L"coloralpha"); addPoint(pos, color); } } void Gradient::setReverseColors(int c) { reverse_colors = c; } void Gradient::setAntialias(int c) { antialias = c; } void Gradient::setMode(const wchar_t *_mode) { mode = _mode; if (mode.isempty()) mode = DEFAULT_GRAD_MODE; } void Gradient::setGammaGroup(const wchar_t *group) { gammagroup = group; // reset our points foreach(list) list.getfor()->color.setColorGroup(group); endfor } static inline ARGB32 colorLerp(ARGB32 color1, ARGB32 color2, double pos) { unsigned int a1 = (color1>>24) & 0xff; unsigned int a2 = (color2>>24) & 0xff; unsigned int r1 = (color1>>16) & 0xff; unsigned int r2 = (color2>>16) & 0xff; unsigned int g1 = (color1>>8) & 0xff; unsigned int g2 = (color2>>8) & 0xff; unsigned int b1 = (color1) & 0xff; unsigned int b2 = (color2) & 0xff; return (LERPu(a1, a2, pos)<<24) | (LERPu(r1, r2, pos) << 16) | (LERPu(g1,g2,pos)<<8) | LERPu(b1, b2, pos); } void Gradient::renderGrad(ARGB32 *ptr, int len, int *positions) { int npos = list.getNumItems(); ASSERT(npos >= 2); ARGB32 color1, color2; for (int i = 0; i < npos-1; i++) { color1 = list.q(i)->color.getColor(); color2 = list.q(i+1)->color.getColor(); if (reverse_colors) { color1 = BGRATOARGB(color1); color2 = BGRATOARGB(color2); } int x1 = positions[i]; int x2 = positions[i+1]; if (x1 == x2) continue; // hflip if need be if (x1 > x2) { SWAP(x1, x2); SWAP(color1, color2); } float c = 0; float segment_len = (float)((x2 - x1)+1); if (x1 < 0) { // clip left c += -x1; x1 = 0; } for (int x = x1; x < x2; x++, c += 1.0f) { if (x >= len) break; // clip right ptr[x] = colorLerp(color1, color2, c / segment_len); } } #if 0//later // fill in left if needed if (positions[0] > 0) MEMFILL(ptr, list.q(0)->color, positions[0]); // and right if needed int rpos = positions[npos-1]; if (rpos < len) MEMFILL(ptr+rpos, list.getLast()->color, len-rpos); #endif } void Gradient::renderGradient(ARGB32 *bits, int w, int h, int pitch) { if (pitch == 0) pitch = w; list.sort(); ARGB32 default_color = 0xffff00ff; if (list.getNumItems() == 1) default_color = list.q(0)->color.getColor(); // blank it out to start if (pitch == w) MEMFILL(bits, default_color, w * h); else { for (int i=0;i(bits+i*pitch, default_color, w); } if (list.getNumItems() > 1) { if (mode.iscaseequal(L"linear")) { //FUCKO: not if endcaps are filled // force non-vertical lines if (ABS(gradient_x1 - gradient_x2) < 0.0005f) gradient_x2 = gradient_x1+0.0005f; double px1 = gradient_x1 * w, py1 = gradient_y1 * h; double px2 = gradient_x2 * w, py2 = gradient_y2 * h; // convert to y = mx + b double m = (py2 - py1)/(px2 - px1); m = -1.f/m; // invert the slope int nitems = list.getNumItems(); // get the in-pixels x and y for points on the gradient for (int i = 0; i < nitems; i++) { GradientPoint *gp = list.q(i); // need x and y given pos gp->x = LERPf(px1, px2, gp->pos); gp->y = LERPf(py1, py2, gp->pos); } MemBlock positions(nitems); for (int _y = 0; _y < h; _y++) { // project all the color points onto this scanline for (int i = 0; i < nitems; i++) { GradientPoint *gp = list.q(i); // y = mx + b // b = y - mx; double newb = gp->y - m * gp->x; // y = mx + newb // y - newb = mx // (y - newb)/m = x double xxx = (_y - newb)/m; positions[i] = (int)floor(xxx+0.5f); } renderGrad(bits+_y*pitch, w, positions); } } else if (mode.iscaseequal(L"circular")) { double tot = SQRT(SQR(gradient_x1 - gradient_x2) + SQR(gradient_y1 - gradient_y2)); foreach(list) GradientPoint *gp = list.getfor(); gp->dist = gp->pos * tot; endfor ARGB32 *dst = bits; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { ARGB32 c; if (antialias) { double fx = (((double)x)-0.5f) / (double)w; double fy = (((double)y)-0.5f) / (double)h; ARGB32 ul = getPixelCirc(fx, fy); fx = (((double)x)+0.5f) / (double)w; fy = (((double)y)-0.5f) / (double)h; ARGB32 ur = getPixelCirc(fx, fy); fx = (((double)x)+0.5f) / (double)w; fy = (((double)y)+0.5f) / (double)h; ARGB32 lr = getPixelCirc(fx, fy); fx = (((double)x)-0.5f) / (double)w; fy = (((double)y)+0.5f) / (double)h; ARGB32 ll = getPixelCirc(fx, fy); c = colorLerp(colorLerp(ll, lr, 0.5f), colorLerp(ul, ur, 0.5f), 0.5); } else { double fy = (double)y / (double)h; double fx = (double)x / (double)w; c = getPixelCirc(fx, fy); } *dst++ = c; } dst += (pitch-w); } } }//list.getNumItems()>1 if (pitch == w) premultiplyARGB32(bits, w * h); else { for (int i=0;idist) c = list.q(0)->color.getColor(); else if (dist >= list.getLast()->dist) c = list.getLast()->color.getColor(); else for (int i = 0; i < nitems-1; i++) { if (list.q(i)->dist <= dist && list.q(i+1)->dist >= dist) { double pdist = list.q(i+1)->dist - list.q(i)->dist; double pp = dist - list.q(i)->dist; pp /= pdist; if (list.q(i)->color.getColor() == list.q(i+1)->color.getColor()) c = list.q(i)->color.getColor(); else c = colorLerp(list.q(i)->color.getColor(), list.q(i+1)->color.getColor(), pp); break; } } if (reverse_colors) c = BGRATOARGB(c); return c; }