diff options
Diffstat (limited to 'Src/Wasabi/bfc/draw')
-rw-r--r-- | Src/Wasabi/bfc/draw/convolve.cpp | 60 | ||||
-rw-r--r-- | Src/Wasabi/bfc/draw/convolve.h | 24 | ||||
-rw-r--r-- | Src/Wasabi/bfc/draw/drawpoly.cpp | 458 | ||||
-rw-r--r-- | Src/Wasabi/bfc/draw/drawpoly.h | 16 | ||||
-rw-r--r-- | Src/Wasabi/bfc/draw/gradient.cpp | 309 | ||||
-rw-r--r-- | Src/Wasabi/bfc/draw/gradient.h | 67 | ||||
-rw-r--r-- | Src/Wasabi/bfc/draw/skinfilter.cpp | 17 | ||||
-rw-r--r-- | Src/Wasabi/bfc/draw/skinfilter.h | 11 |
8 files changed, 962 insertions, 0 deletions
diff --git a/Src/Wasabi/bfc/draw/convolve.cpp b/Src/Wasabi/bfc/draw/convolve.cpp new file mode 100644 index 00000000..2ac48930 --- /dev/null +++ b/Src/Wasabi/bfc/draw/convolve.cpp @@ -0,0 +1,60 @@ +#include <precomp.h> + +#include "convolve.h" + +#define RED(a) (((a)>>16)&0xff) +#define GRN(a) (((a)>>8)&0xff) +#define BLU(a) (((a)&0xff)) +#define ALP(a) (((a)>>24)) + +Convolve3x3::Convolve3x3(ARGB32 *_bits, int _w, int _h) : bits(_bits), w(_w), h(_h) { + ZERO(vals); + multiplier = 0; +} + +void Convolve3x3::setPos(int x, int y, float v) { + ASSERT(x >= -1 && x <= 1 && y >= -1 && y <= 1); + vals[y+1][x+1] = v; +} + +void Convolve3x3::setMultiplier(int m) { + multiplier = m; +} + +void Convolve3x3::convolve() { + if (bits == NULL || w <= 0 || h <= 0) return; // nothin' + MemMatrix<ARGB32> temp(w, h, bits); // make a copy + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + if (ALP(temp(x, y))<=1) continue; + float ra=0, rg=0, rb=0; + for (int a = -1; a <= 1; a++) { + for (int b = -1; b <= 1; b++) { + int px = x + a, py = y + b; + if (px < 0 || px >= w || py < 0 || py >= h) continue; + ARGB32 c = temp(px, py); + if (ALP(c) <= 1) continue; + ra += (float)RED(c) * vals[b][a] * multiplier; + rg += (float)GRN(c) * vals[b][a] * multiplier; + rb += (float)BLU(c) * vals[b][a] * multiplier; + } + } + unsigned int r = MINMAX((int)ra, 0, 255); + unsigned int g = MINMAX((int)rg, 0, 255); + unsigned int b = MINMAX((int)rb, 0, 255); + unsigned int lum = MAX(MAX(r, g), b); +if (lum < 64) lum = 0; +else if (lum > 192) lum = 255; + //bits[x+y*w] = (ALP(*temp.m(x, y))<<24)|(r<<16)|(g<<8)|(b); + bits[x+y*w] &= 0xffffff; + bits[x+y*w] |= (255-lum) << 24; +//if (lum < 64) { +// bits[x+y*w] &= 0xffff00ff; +// bits[x+y*w] |= lum << 8; +//} else { +// bits[x+y*w] &= 0xff00ffff; +// bits[x+y*w] |= lum << 16; +//} + } + } +} diff --git a/Src/Wasabi/bfc/draw/convolve.h b/Src/Wasabi/bfc/draw/convolve.h new file mode 100644 index 00000000..aac2e237 --- /dev/null +++ b/Src/Wasabi/bfc/draw/convolve.h @@ -0,0 +1,24 @@ +#ifndef _CONVOLVE_H +#define _CONVOLVE_H + +#include "platform/types.h" +// world's slowest crappiest convolve :P think it sucks? write a better one +// and send it to me + +class Convolve3x3 { +public: + Convolve3x3(ARGB32 *bits, int w, int h); + + void setPos(int x, int y, float v); + void setMultiplier(int m); + + void convolve(); + +private: + ARGB32 *bits; + int w, h; + float vals[3][3]; + float multiplier; +}; + +#endif diff --git a/Src/Wasabi/bfc/draw/drawpoly.cpp b/Src/Wasabi/bfc/draw/drawpoly.cpp new file mode 100644 index 00000000..02c84f33 --- /dev/null +++ b/Src/Wasabi/bfc/draw/drawpoly.cpp @@ -0,0 +1,458 @@ +#include <precomp.h> + +#include "drawpoly.h" +#include <bfc/parse/pathparse.h> + +#define MAXPOINTS 32 + +static ARGB32 *bits, color; +static int w, h, across; +struct Point2d { + int X, Y; +}; +typedef struct Point2d Point2d; // bleh +static Point2d points[MAXPOINTS]; +static int npoints; + +void Draw::beginPolygon(ARGB32 *bits, int w, int h, ARGB32 color) { + ::bits = bits; + ::w = w; + ::h = h; + ::color = color; + ::across = w; + npoints = 0; +} + +void Draw::addPoint(int x, int y) { + if (npoints >= MAXPOINTS) return; + points[npoints].X = x; + points[npoints].Y = y; + npoints++; +} + +static void premultiply(ARGB32 *m_pBits, int nwords) { + for (; nwords > 0; nwords--, m_pBits++) { + unsigned __int8 *pixel = (unsigned __int8 *)m_pBits; + unsigned int alpha = pixel[3]; + if (alpha == 255) continue; + pixel[0] = (pixel[0] * alpha) >> 8; // blue + pixel[1] = (pixel[1] * alpha) >> 8; // green + pixel[2] = (pixel[2] * alpha) >> 8; // red + } +} + +void Draw::drawPointList(ARGB32 *bits, int w, int h, const wchar_t *pointlist) { + if (pointlist == NULL || *pointlist == '\0') return; + PathParserW outer(pointlist, L"|"); + const wchar_t *pl; + for (int i = 0; (pl = outer.enumString(i)) != NULL; i++) + { + PathParserW inner(pl, L"="); + ARGB32 color = WASABI_API_SKIN->parse(inner.enumStringSafe(1, L"255,255,255,255"), L"coloralpha"); + int a = color & 0xff000000; + color = _byteswap_ulong(color<<8) | a; + premultiply(&color, 1); + beginPolygon(bits, w, h, color); + PathParserW eener(inner.enumStringSafe(0, L"0,0"), L";"); + const wchar_t *cc; + for (int j = 0; (cc = eener.enumString(j)) != NULL; j++) { + PathParserW com(cc, L","); + const wchar_t *xs = com.enumStringSafe(0, L"0"); + int x = wcschr(xs, '.') ? (int)floor(WTOF(xs) * w + .5f) : WTOI(xs); + const wchar_t *ys = com.enumStringSafe(1, L"0"); + int y = wcschr(ys, '.') ? (int)floor(WTOF(ys) * h + .5f) : WTOI(ys); + addPoint(x, y); + } + endPolygon(); + } +} + +#define PIXEL ARGB32 + +// this originally came from Michael Abrash's Zen of Graphics Programming +// been modified a bit +/* DRAWPOLY.H: Header file for polygon-filling code */ + +/* Describes a single point (used for a single vertex) */ + +//struct Point2d { +// int X; /* X coordinate */ +// int Y; /* Y coordinate */ +//}; + +//typedef struct Point2d Point2d; + +typedef struct { + int X, Y; +} Point2dC; + +/* Describes a series of points (used to store a list of vertices that + describe a polygon; each vertex is assumed to connect to the two + adjacent vertices, and the last vertex is assumed to connect to the + first) */ +struct Point2dListHeader { + int Length; /* # of points */ + struct Point2d *Point2dPtr; /* pointer to list of points */ +}; + +typedef struct Point2dListHeader Point2dListHeader; + +/* Describes the beginning and ending X coordinates of a single + horizontal line */ +struct HLine { + int XStart; /* X coordinate of leftmost pixel in line */ + int XEnd; /* X coordinate of rightmost pixel in line */ +}; + +typedef struct { + int XStart, XEnd; +} HLineColor; + +/* Describes a Length-long series of horizontal lines, all assumed to + be on contiguous scan lines starting at YStart and proceeding + downward (used to describe a scan-converted polygon to the + low-level hardware-dependent drawing code) */ +struct HLineList { + int Length; /* # of horizontal lines */ + int YStart; /* Y coordinate of topmost line */ + struct HLine * HLinePtr; /* pointer to list of horz lines */ +}; + +static void DrawHorizontalLineList(struct HLineList * HLineListPtr, PIXEL *dest, + PIXEL Color) { + struct HLine *HLinePtr, *ptr; + int Length, Width, c; + PIXEL *ScreenPtr; + + /* Point to the start of the first scan line on which to draw */ + ScreenPtr = dest + HLineListPtr->YStart * across; + Length = HLineListPtr->Length; + /* Point to the XStart/XEnd descriptor for the first (top) + horizontal line */ + HLinePtr = HLineListPtr->HLinePtr; + + /* clip left/right */ + for (ptr = HLinePtr, c = Length; c; c--) { + if (ptr->XStart < 0) ptr->XStart = 0; + if (ptr->XEnd >= w) ptr->XEnd = w - 1; + ptr++; + } + /* clip top */ + if (HLineListPtr->YStart < 0) { + int skip = -HLineListPtr->YStart; + HLineListPtr->YStart = 0; + ScreenPtr += across * skip; + Length -= skip; + HLinePtr += skip; + } + /* clip bottom */ + if (HLineListPtr->YStart + Length > h) { + Length -= (HLineListPtr->YStart + Length) - h; + } + + /* Draw each horizontal line in turn, starting with the top one and + advancing one line each time */ + while (Length-- > 0) { + /* Draw the whole horizontal line if it has a positive width */ + if ((Width = HLinePtr->XEnd - HLinePtr->XStart + 1) > 0) +// bmemsetw(ScreenPtr+HLinePtr->XStart, Color, Width); + MEMFILL<PIXEL>(ScreenPtr+HLinePtr->XStart, Color, Width); + HLinePtr++; /* point to next scan line X info */ + ScreenPtr += across; /* point to next scan line start */ + } +} + +/* Scan converts an edge from (X1,Y1) to (X2,Y2), not including the + point at (X2,Y2). If SkipFirst == 1, the point at (X1,Y1) isn't + drawn; if SkipFirst == 0, it is. For each scan line, the pixel + closest to the scanned edge without being to the left of the + scanned edge is chosen. Uses an all-integer approach for speed and + precision + + Link with L21-1.C, L21-3.C, and L22-1.C in Compact model. + Tested with Borland C++ 4.02 by Jim Mischel 12/16/94. +*/ + +static void ScanEdge(int X1, int Y1, int X2, int Y2, int SetXStart, + int SkipFirst, struct HLine **EdgePoint2dPtr) { + int DeltaX, Height, Width, AdvanceAmt, ErrorTerm, i; + int ErrorTermAdvance, XMajorAdvanceAmt; + struct HLine *WorkingEdgePoint2dPtr; + + WorkingEdgePoint2dPtr = *EdgePoint2dPtr; /* avoid double dereference */ + AdvanceAmt = ((DeltaX = X2 - X1) > 0) ? 1 : -1; + /* direction in which X moves (Y2 is + always > Y1, so Y always counts up) */ + + if ((Height = Y2 - Y1) <= 0) /* Y length of the edge */ + return; /* guard against 0-length and horizontal edges */ + + /* Figure out whether the edge is vertical, diagonal, X-major + (mostly horizontal), or Y-major (mostly vertical) and handle + appropriately */ + if ((Width = abs(DeltaX)) == 0) { + /* The edge is vertical; special-case by just storing the same + X coordinate for every scan line */ + /* Scan the edge for each scan line in turn */ + for (i = Height - SkipFirst; i-- > 0; WorkingEdgePoint2dPtr++) { + /* Store the X coordinate in the appropriate edge list */ + if (SetXStart == 1) + WorkingEdgePoint2dPtr->XStart = X1; + else + WorkingEdgePoint2dPtr->XEnd = X1; + } + } else if (Width == Height) { + /* The edge is diagonal; special-case by advancing the X + coordinate 1 pixel for each scan line */ + if (SkipFirst) /* skip the first point if so indicated */ + X1 += AdvanceAmt; /* move 1 pixel to the left or right */ + /* Scan the edge for each scan line in turn */ + for (i = Height - SkipFirst; i-- > 0; WorkingEdgePoint2dPtr++) { + /* Store the X coordinate in the appropriate edge list */ + if (SetXStart == 1) + WorkingEdgePoint2dPtr->XStart = X1; + else + WorkingEdgePoint2dPtr->XEnd = X1; + X1 += AdvanceAmt; /* move 1 pixel to the left or right */ + } + } else if (Height > Width) { + /* Edge is closer to vertical than horizontal (Y-major) */ + if (DeltaX >= 0) + ErrorTerm = 0; /* initial error term going left->right */ + else + ErrorTerm = -Height + 1; /* going right->left */ + if (SkipFirst) { /* skip the first point if so indicated */ + /* Determine whether it's time for the X coord to advance */ + if ((ErrorTerm += Width) > 0) { + X1 += AdvanceAmt; /* move 1 pixel to the left or right */ + ErrorTerm -= Height; /* advance ErrorTerm to next point */ + } + } + /* Scan the edge for each scan line in turn */ + for (i = Height - SkipFirst; i-- > 0; WorkingEdgePoint2dPtr++) { + /* Store the X coordinate in the appropriate edge list */ + if (SetXStart == 1) + WorkingEdgePoint2dPtr->XStart = X1; + else + WorkingEdgePoint2dPtr->XEnd = X1; + /* Determine whether it's time for the X coord to advance */ + if ((ErrorTerm += Width) > 0) { + X1 += AdvanceAmt; /* move 1 pixel to the left or right */ + ErrorTerm -= Height; /* advance ErrorTerm to correspond */ + } + } + } else { + /* Edge is closer to horizontal than vertical (X-major) */ + /* Minimum distance to advance X each time */ + XMajorAdvanceAmt = (Width / Height) * AdvanceAmt; + /* Error term advance for deciding when to advance X 1 extra */ + ErrorTermAdvance = Width % Height; + if (DeltaX >= 0) + ErrorTerm = 0; /* initial error term going left->right */ + else + ErrorTerm = -Height + 1; /* going right->left */ + if (SkipFirst) { /* skip the first point if so indicated */ + X1 += XMajorAdvanceAmt; /* move X minimum distance */ + /* Determine whether it's time for X to advance one extra */ + if ((ErrorTerm += ErrorTermAdvance) > 0) { + X1 += AdvanceAmt; /* move X one more */ + ErrorTerm -= Height; /* advance ErrorTerm to correspond */ + } + } + /* Scan the edge for each scan line in turn */ + for (i = Height - SkipFirst; i-- > 0; WorkingEdgePoint2dPtr++) { + /* Store the X coordinate in the appropriate edge list */ + if (SetXStart == 1) + WorkingEdgePoint2dPtr->XStart = X1; + else + WorkingEdgePoint2dPtr->XEnd = X1; + X1 += XMajorAdvanceAmt; /* move X minimum distance */ + /* Determine whether it's time for X to advance one extra */ + if ((ErrorTerm += ErrorTermAdvance) > 0) { + X1 += AdvanceAmt; /* move X one more */ + ErrorTerm -= Height; /* advance ErrorTerm to correspond */ + } + } + } + + *EdgePoint2dPtr = WorkingEdgePoint2dPtr; /* advance caller's ptr */ +} + +/* Color-fills a convex polygon. All vertices are offset by (XOffset, + YOffset). "Convex" means that every horizontal line drawn through + the polygon at any point would cross exactly two active edges + (neither horizontal lines nor zero-length edges count as active + edges; both are acceptable anywhere in the polygon), and that the + right & left edges never cross. (It's OK for them to touch, though, + so long as the right edge never crosses over to the left of the + left edge.) Nonconvex polygons won't be drawn properly. Returns 1 + for success, 0 if memory allocation failed. + + Compiled with Borland C++ 4.02. Link with L21-3.C. + Checked by Jim Mischel 11/30/94. + */ + +/* Advances the index by one vertex forward through the vertex list, + wrapping at the end of the list */ +#define INDEX_FORWARD(Index) \ + Index = (Index + 1) % VertexList->Length; + +/* Advances the index by one vertex backward through the vertex list, + wrapping at the start of the list */ +#define INDEX_BACKWARD(Index) \ + Index = (Index - 1 + VertexList->Length) % VertexList->Length; + +/* Advances the index by one vertex either forward or backward through + the vertex list, wrapping at either end of the list */ +#define INDEX_MOVE(Index,Direction) \ + if (Direction > 0) \ + Index = (Index + 1) % VertexList->Length; \ + else \ + Index = (Index - 1 + VertexList->Length) % VertexList->Length; + +int FillConvexPolygon(struct Point2dListHeader *VertexList, PIXEL *dest, + PIXEL Color) { + int i, MinIndexL, MaxIndex, MinIndexR, SkipFirst, Temp; + int MinPoint2d_Y, MaxPoint2d_Y, TopIsFlat, LeftEdgeDir; + int NextIndex, CurrentIndex, PreviousIndex; + int DeltaXN, DeltaYN, DeltaXP, DeltaYP; + struct HLineList WorkingHLineList; + struct HLine *EdgePoint2dPtr; + struct Point2d *VertexPtr; + + /* Point to the vertex list */ + VertexPtr = VertexList->Point2dPtr; + + /* Scan the list to find the top and bottom of the polygon */ + if (VertexList->Length == 0) + return(1); /* reject null polygons */ + MaxPoint2d_Y = MinPoint2d_Y = VertexPtr[MinIndexL = MaxIndex = 0].Y; + for (i = 1; i < VertexList->Length; i++) { + if (VertexPtr[i].Y < MinPoint2d_Y) + MinPoint2d_Y = VertexPtr[MinIndexL = i].Y; /* new top */ + else if (VertexPtr[i].Y > MaxPoint2d_Y) + MaxPoint2d_Y = VertexPtr[MaxIndex = i].Y; /* new bottom */ + } + if (MinPoint2d_Y == MaxPoint2d_Y) + return(1); /* polygon is 0-height; avoid infinite loop below */ + + /* Scan in ascending order to find the last top-edge point */ + MinIndexR = MinIndexL; + while (VertexPtr[MinIndexR].Y == MinPoint2d_Y) + INDEX_FORWARD(MinIndexR); + INDEX_BACKWARD(MinIndexR); /* back up to last top-edge point */ + + /* Now scan in descending order to find the first top-edge point */ + while (VertexPtr[MinIndexL].Y == MinPoint2d_Y) + INDEX_BACKWARD(MinIndexL); + INDEX_FORWARD(MinIndexL); /* back up to first top-edge point */ + + /* Figure out which direction through the vertex list from the top + vertex is the left edge and which is the right */ + LeftEdgeDir = -1; /* assume left edge runs down thru vertex list */ + if ((TopIsFlat = (VertexPtr[MinIndexL].X != + VertexPtr[MinIndexR].X) ? 1 : 0) == 1) { + /* If the top is flat, just see which of the ends is leftmost */ + if (VertexPtr[MinIndexL].X > VertexPtr[MinIndexR].X) { + LeftEdgeDir = 1; /* left edge runs up through vertex list */ + Temp = MinIndexL; /* swap the indices so MinIndexL */ + MinIndexL = MinIndexR; /* points to the start of the left */ + MinIndexR = Temp; /* edge, similarly for MinIndexR */ + } + } else { + /* Point to the downward end of the first line of each of the + two edges down from the top */ + NextIndex = MinIndexR; + INDEX_FORWARD(NextIndex); + PreviousIndex = MinIndexL; + INDEX_BACKWARD(PreviousIndex); + /* Calculate X and Y lengths from the top vertex to the end of + the first line down each edge; use those to compare slopes + and see which line is leftmost */ + DeltaXN = VertexPtr[NextIndex].X - VertexPtr[MinIndexL].X; + DeltaYN = VertexPtr[NextIndex].Y - VertexPtr[MinIndexL].Y; + DeltaXP = VertexPtr[PreviousIndex].X - VertexPtr[MinIndexL].X; + DeltaYP = VertexPtr[PreviousIndex].Y - VertexPtr[MinIndexL].Y; + if (((long)DeltaXN * DeltaYP - (long)DeltaYN * DeltaXP) < 0L) { + LeftEdgeDir = 1; /* left edge runs up through vertex list */ + Temp = MinIndexL; /* swap the indices so MinIndexL */ + MinIndexL = MinIndexR; /* points to the start of the left */ + MinIndexR = Temp; /* edge, similarly for MinIndexR */ + } + } + + /* Set the # of scan lines in the polygon, skipping the bottom edge + and also skipping the top vertex if the top isn't flat because + in that case the top vertex has a right edge component, and set + the top scan line to draw, which is likewise the second line of + the polygon unless the top is flat */ + if ((WorkingHLineList.Length = + MaxPoint2d_Y - MinPoint2d_Y - 1 + TopIsFlat) <= 0) + return(1); /* there's nothing to draw, so we're done */ + //WorkingHLineList.YStart = YOffset + MinPoint2d_Y + 1 - TopIsFlat; + WorkingHLineList.YStart = MinPoint2d_Y + 1 - TopIsFlat; + + /* Get memory in which to store the line list we generate */ + if ((WorkingHLineList.HLinePtr = + (struct HLine *) (malloc(sizeof(struct HLine) * + WorkingHLineList.Length))) == NULL) + return(0); /* couldn't get memory for the line list */ + + /* Scan the left edge and store the boundary points in the list */ + /* Initial pointer for storing scan converted left-edge coords */ + EdgePoint2dPtr = WorkingHLineList.HLinePtr; + /* Start from the top of the left edge */ + PreviousIndex = CurrentIndex = MinIndexL; + /* Skip the first point of the first line unless the top is flat; + if the top isn't flat, the top vertex is exactly on a right + edge and isn't drawn */ + SkipFirst = TopIsFlat ? 0 : 1; + /* Scan convert each line in the left edge from top to bottom */ + do { + INDEX_MOVE(CurrentIndex,LeftEdgeDir); + ScanEdge(VertexPtr[PreviousIndex].X, + VertexPtr[PreviousIndex].Y, + VertexPtr[CurrentIndex].X, + VertexPtr[CurrentIndex].Y, 1, SkipFirst, &EdgePoint2dPtr); + PreviousIndex = CurrentIndex; + SkipFirst = 0; /* scan convert the first point from now on */ + } while (CurrentIndex != MaxIndex); + + /* Scan the right edge and store the boundary points in the list */ + EdgePoint2dPtr = WorkingHLineList.HLinePtr; + PreviousIndex = CurrentIndex = MinIndexR; + SkipFirst = TopIsFlat ? 0 : 1; + /* Scan convert the right edge, top to bottom. X coordinates are + adjusted 1 to the left, effectively causing scan conversion of + the nearest points to the left of but not exactly on the edge */ + do { + INDEX_MOVE(CurrentIndex,-LeftEdgeDir); + //ScanEdge(VertexPtr[PreviousIndex].X + XOffset - 1, + ScanEdge(VertexPtr[PreviousIndex].X - 1, + VertexPtr[PreviousIndex].Y, + //VertexPtr[CurrentIndex].X + XOffset - 1, + VertexPtr[CurrentIndex].X - 1, + VertexPtr[CurrentIndex].Y, 0, SkipFirst, &EdgePoint2dPtr); + PreviousIndex = CurrentIndex; + SkipFirst = 0; /* scan convert the first point from now on */ + } while (CurrentIndex != MaxIndex); + + /* Draw the line list representing the scan converted polygon */ + //CUT (*drawfn)(&WorkingHLineList, dest, Color, vc); + DrawHorizontalLineList(&WorkingHLineList, dest, Color); + + /* Release the line list's memory and we're successfully done */ + free(WorkingHLineList.HLinePtr); + return(1); +} + +// done with abrashitude + +void Draw::endPolygon() { + if (npoints == 0) return; + + struct Point2dListHeader head; + head.Length = npoints; + head.Point2dPtr = &points[0]; + FillConvexPolygon(&head, bits, color); +} diff --git a/Src/Wasabi/bfc/draw/drawpoly.h b/Src/Wasabi/bfc/draw/drawpoly.h new file mode 100644 index 00000000..c8bea56d --- /dev/null +++ b/Src/Wasabi/bfc/draw/drawpoly.h @@ -0,0 +1,16 @@ +#ifndef _DRAWPOLY_H +#define _DRAWPOLY_H + +#include <bfc/wasabi_std.h> + +class Draw { +public: + static void beginPolygon(ARGB32 *bits, int w, int h, ARGB32 color); + static void addPoint(int x, int y); + static void endPolygon(); + static void drawPointList(ARGB32 *bits, int w, int h, const wchar_t *pointlist); +}; + +// x,y;x,y;x,y;x,y=R,G,B|x,y;x,y;x,y;=R,G,B + +#endif diff --git a/Src/Wasabi/bfc/draw/gradient.cpp b/Src/Wasabi/bfc/draw/gradient.cpp new file mode 100644 index 00000000..b8468ee0 --- /dev/null +++ b/Src/Wasabi/bfc/draw/gradient.cpp @@ -0,0 +1,309 @@ +#include <precomp.h> + +#include "gradient.h" + +#include <math.h>//floor +#include <bfc/ptrlist.h> +#include <bfc/parse/pathparse.h> + + +#define DEFAULT_GRAD_MODE L"linear" + +template<class T> 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<ARGB32>(ptr, list.q(0)->color, positions[0]); + + // and right if needed + int rpos = positions[npos-1]; + if (rpos < len) MEMFILL<ARGB32>(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<ARGB32>(bits, default_color, w * h); + else + { + for (int i=0;i<h;i++) + MEMFILL<ARGB32>(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<int> 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;i<h;i++) + premultiplyARGB32(bits+i*pitch, w); + } + +} + +ARGB32 Gradient::getPixelCirc(double fx, double fy) { + int nitems = list.getNumItems(); + //double dist = SQR(fx - gradient_x1) + SQR(fy - gradient_y1); + double dist = SQRT(SQR(fx - gradient_x1) + SQR(fy - gradient_y1)); + ARGB32 c = 0xff00ff00; + if (dist <= list.q(0)->dist) + 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; +} diff --git a/Src/Wasabi/bfc/draw/gradient.h b/Src/Wasabi/bfc/draw/gradient.h new file mode 100644 index 00000000..1e721ee9 --- /dev/null +++ b/Src/Wasabi/bfc/draw/gradient.h @@ -0,0 +1,67 @@ +#ifndef _GRADIENT_H +#define _GRADIENT_H + +#include <bfc/wasabi_std.h> +#include <bfc/string/StringW.h> +#include <tataki/color/filteredcolor.h> + +class GradientPoint +{ +public: + GradientPoint(float p, ARGB32 c, const wchar_t *group=L"") : pos(p), dist(0), color(c, group), x(0), y(0) { } + float pos; + double dist; + FilteredColor color; + float x, y; + static int compareItem(GradientPoint *p1, GradientPoint* p2) { + int r = CMP3(p1->pos, p2->pos); + if (r == 0) return CMP3(p1, p2); + else return r; + } +}; + + +class Gradient +{ +public: + Gradient(); + virtual ~Gradient(); + + void setX1(float x1); + void setY1(float y1); + void setX2(float x2); + void setY2(float y2); + + void clearPoints(); + void addPoint(float pos, ARGB32 color); + + // "pos=color;pos=color" "0.25=34,45,111" + void setPoints(const wchar_t *str); + + void setReverseColors(int c); + + void setAntialias(int c); + + void setMode(const wchar_t *mode); + + void setGammaGroup(const wchar_t *group); + + // note: this will automatically premultiply against alpha + void renderGradient(ARGB32 *bits, int width, int height, int pitch=0); + +protected: + virtual void onParamChange() { } + + ARGB32 getPixelCirc(double x, double y); + +private: + float gradient_x1, gradient_y1, gradient_x2, gradient_y2; + class GradientList : public PtrListQuickSorted<GradientPoint, GradientPoint> { }; + GradientList list; + void renderGrad(ARGB32 *bits, int len, int *positions); + int reverse_colors; + int antialias; + StringW mode, gammagroup; +}; + +#endif diff --git a/Src/Wasabi/bfc/draw/skinfilter.cpp b/Src/Wasabi/bfc/draw/skinfilter.cpp new file mode 100644 index 00000000..4ebbb364 --- /dev/null +++ b/Src/Wasabi/bfc/draw/skinfilter.cpp @@ -0,0 +1,17 @@ +#include <precomp.h> + +#include "skinfilter.h" + +#include <studio/services/svc_skinfilter.h> + +void ApplySkinFilters::apply(const char *elementid, const char *forced_gammagroup, ARGB32 *bits, int w, int h, int bpp) { + if ((elementid == NULL && forced_gammagroup == NULL) || bits == NULL || w <= 0 || h <= 0) return; + SkinFilterEnum sfe; + + while (1) { + svc_skinFilter *obj = sfe.getNext(FALSE); + if (!obj) break; + obj->filterBitmap((unsigned char *)bits, w, h, bpp, elementid, forced_gammagroup); + sfe.getLastFactory()->releaseInterface(obj); + } +} diff --git a/Src/Wasabi/bfc/draw/skinfilter.h b/Src/Wasabi/bfc/draw/skinfilter.h new file mode 100644 index 00000000..5b1b417f --- /dev/null +++ b/Src/Wasabi/bfc/draw/skinfilter.h @@ -0,0 +1,11 @@ +#ifndef _SKINFILTER_H +#define _SKINFILTER_H + +#include <bfc/std.h> + +class ApplySkinFilters { +public: + static void apply(const char *element_id, const char *forced_gammagroup, ARGB32 *bits, int w, int h, int bpp=32); +}; + +#endif |