Development: Gradients
From WxWiki
Gradient Patch:
Index: docs/latex/wx/dc.tex =================================================================== RCS file: /pack/cvsroots/wxwindows/wxWindows/docs/latex/wx/dc.tex,v retrieving revision 1.41 diff -b -u -2 -r1.41 dc.tex --- docs/latex/wx/dc.tex 2003/01/18 00:16:31 1.41 +++ docs/latex/wx/dc.tex 2003/07/13 05:40:44 @@ -504,4 +504,24 @@ function will still return true. +membersection{wxDC::GradientFillLinear}label{wxdcgradientfilllinear} + +func{void}{GradientFillConcentric}{param{const wxRect&}{ rect}, param{const wxColour&}{ initialColour}, param{const wxColour&}{ destColour} } + +func{void}{GradientFillConcentric}{param{const wxRect&}{ rect}, param{const wxColour&}{ initialColour}, param{const wxColour&}{ destColour}, param{const wxPoint&}{ circleCenter } } + +Fill the area specified by rect with a radial gradient, starting from initialColour +and eventually fading to destColour. + +initialColour is the color of the inside of the circle, whereas destColour is the +color outside of the circle. + +circleCenter is the center of the circle within the constraints of rect. If not specified, +it is placed within the center of rect. + +func{void}{GradientFillLinear}{param{const wxRect&}{ rect}, param{const wxColour&}{ initialColour}, param{const wxColour&}{ destColour}, param{wxDirection}{ nDirection = wxEAST}} + +Fill the area specified by rect with a linear gradient, starting from initialColour +and eventually fading to destColour. nDirection is the direction of the destination color. + membersection{wxDC::GetBackground}label{wxdcgetbackground} Index: include/wx/dc.h =================================================================== RCS file: /pack/cvsroots/wxwindows/wxWindows/include/wx/dc.h,v retrieving revision 1.44 diff -b -u -2 -r1.44 dc.h --- include/wx/dc.h 2003/06/26 13:15:02 1.44 +++ include/wx/dc.h 2003/07/13 05:40:47 @@ -157,4 +157,33 @@ { return DoFloodFill(pt.x, pt.y, col, style); } + // Fill the area specified by rect with a radial gradient - + // Starting from initialColour and eventually fading to destColour. + // + // initialColour is the color of the inside of the circle, + // whereas destColour is the color outside of the circle. + // + // circleLocation is the upper-left corner of the circle + // within the rect, starting at (0,0) + void GradientFillConcentric(const wxRect& rect, + const wxColour& initialColour, + const wxColour& destColour) + { GradientFillConcentric(rect, initialColour, destColour, wxPoint(rect.GetWidth() / 2, + rect.GetHeight() / 2)); } + + void GradientFillConcentric(const wxRect& rect, + const wxColour& initialColour, + const wxColour& destColour, + const wxPoint& circleCenter); + + // Fill the area specified by rect with a linear gradient - + // Starting from initialColour and eventually fading to destColour. + // + // nDirection is the direction of the destination color. + // + // FIXME: Using SetPen instead of m_pen.SetColour, as it doesn't work on MSW + void GradientFillLinear(const wxRect& rect, const wxColour& initialColour, + const wxColour& destColour, wxDirection nDirection) + { DoGradientFillLinear(rect, initialColour, destColour, nDirection);} + bool GetPixel(wxCoord x, wxCoord y, wxColour *col) const { return DoGetPixel(x, y, col); } @@ -604,4 +633,7 @@ int style = wxFLOOD_SURFACE) = 0; + virtual void DoGradientFillLinear(const wxRect& rect, const wxColour& initialColour, + const wxColour& destColour, wxDirection nDirection); + virtual bool DoGetPixel(wxCoord x, wxCoord y, wxColour *col) const = 0; Index: include/wx/msw/dc.h =================================================================== RCS file: /pack/cvsroots/wxwindows/wxWindows/include/wx/msw/dc.h,v retrieving revision 1.39 diff -b -u -2 -r1.39 dc.h --- include/wx/msw/dc.h 2003/01/02 23:37:45 1.39 +++ include/wx/msw/dc.h 2003/07/13 05:40:50 @@ -139,4 +139,7 @@ int style = wxFLOOD_SURFACE); + virtual void DoGradientFillLinear (const wxRect& rect, const wxColour& initialColour, + const wxColour& destColour, wxDirection nDirection); + virtual bool DoGetPixel(wxCoord x, wxCoord y, wxColour *col) const; Index: src/common/dcbase.cpp =================================================================== RCS file: /pack/cvsroots/wxwindows/wxWindows/src/common/dcbase.cpp,v retrieving revision 1.20 diff -b -u -2 -r1.20 dcbase.cpp --- src/common/dcbase.cpp 2003/07/12 20:36:49 1.20 +++ src/common/dcbase.cpp 2003/07/13 05:41:02 @@ -545,2 +545,160 @@ CalcBoundingBox(x0 + width0, y0 + height); } + +void wxDCBase::DoGradientFillLinear(const wxRect& rect, + const wxColour& initialColour, + const wxColour& destColour, + wxDirection nDirection) +{ + //save old pen + wxPen oldPen = m_pen; + + wxUint8 nR1 = destColour.Red(); + wxUint8 nG1 = destColour.Green(); + wxUint8 nB1 = destColour.Blue(); + wxUint8 nR2 = initialColour.Red(); + wxUint8 nG2 = initialColour.Green(); + wxUint8 nB2 = initialColour.Blue(); + wxUint8 nR, nG, nB; + + if(nDirection == wxEAST || + nDirection == wxWEST) //paint horizontal rect; + { + wxInt32 x = rect.GetWidth(); + wxInt32 w = x; // width of area to shade + wxInt32 xDelta = w/256;//dwColorShades; //height of one shade bend + if (xDelta < 1) + xDelta = 1; + + while (x >= xDelta) + { + x -= xDelta; + if (nR1 > nR2) + nR = nR1 - (nR1-nR2)*(w-x)/w; + else + nR = nR1 + (nR2-nR1)*(w-x)/w; + + if (nG1 > nG2) + nG = nG1 - (nG1-nG2)*(w-x)/w; + else + nG = nG1 + (nG2-nG1)*(w-x)/w; + + if (nB1 > nB2) + nB = nB1 - (nB1-nB2)*(w-x)/w; + else + nB = nB1 + (nB2-nB1)*(w-x)/w; + + SetPen(wxPen(wxColour(nR, nG, nB), 1, wxSOLID)); + if(nDirection == wxEAST) + DrawRectangle(rect.GetLeft()+x, rect.GetTop(), + xDelta, rect.GetHeight()); + else //nDirection == wxWEST + DrawRectangle(rect.GetRight()-x-xDelta, rect.GetTop(), + xDelta, rect.GetHeight()); + } + } + else //nDirection == wxNORTH || nDirection == wxSOUTH + { + wxInt32 y = rect.GetHeight(); + wxInt32 w = y; // height of area to shade + wxInt32 yDelta = w/255;//dwColorShades; //height of one shade bend + if (yDelta < 1) + yDelta = 1; + + while (y > 0) + { + y -= yDelta; + if (nR1 > nR2) + nR = nR1 - (nR1-nR2)*(w-y)/w; + else + nR = nR1 + (nR2-nR1)*(w-y)/w; + + if (nG1 > nG2) + nG = nG1 - (nG1-nG2)*(w-y)/w; + else + nG = nG1 + (nG2-nG1)*(w-y)/w; + + if (nB1 > nB2) + nB = nB1 - (nB1-nB2)*(w-y)/w; + else + nB = nB1 + (nB2-nB1)*(w-y)/w; + + SetPen(wxPen(wxColour(nR, nG, nB), 1, wxSOLID)); + if(nDirection == wxNORTH) + DrawRectangle(rect.GetLeft(), rect.GetTop()+y, + rect.GetWidth(), yDelta); + else //nDirection == wxSOUTH + DrawRectangle(rect.GetLeft(), rect.GetBottom()-y-yDelta, + rect.GetWidth(), yDelta); + } + } + SetPen(oldPen); +} + +void wxDCBase::GradientFillConcentric(const wxRect& rect, + const wxColour& initialColour, + const wxColour& destColour, + const wxPoint& circleCenter) +{ + //save the old pen color + wxColour oldPenColour = m_pen.GetColour(); + + wxUint8 nR1 = destColour.Red(); + wxUint8 nG1 = destColour.Green(); + wxUint8 nB1 = destColour.Blue(); + wxUint8 nR2 = initialColour.Red(); + wxUint8 nG2 = initialColour.Green(); + wxUint8 nB2 = initialColour.Blue(); + wxUint8 nR, nG, nB; + + + //offsets of the current pixel + wxInt32 x, y; + + //Color difference + wxInt32 nGradient; + + //Radius + wxInt32 cx = rect.GetWidth() / 2; + wxInt32 cy = rect.GetHeight() / 2; + wxInt32 nRadius; + if (cx < cy) + nRadius = cx; + else + nRadius = cy; + + //Offset of circle + wxInt32 nCircleOffX = circleCenter.x - (rect.GetWidth() / 2); + wxInt32 nCircleOffY = circleCenter.y - (rect.GetHeight() / 2); + + for (x = 0; x < rect.GetWidth(); x++) + { + for (y = 0; y < rect.GetHeight(); y++) + { + //get color difference + nGradient = ( + (nRadius - + (wxInt32)sqrt( + pow(x - cx - nCircleOffX, 2) + + pow(y - cy - nCircleOffY, 2) + ) + ) * 100 + ) / nRadius; + + //normalize Gradient + if (nGradient < 0 ) + nGradient = 0; + + //get dest colors + nR = nR1 + ((nR2 - nR1) * nGradient / 100); + nG = nG1 + ((nG2 - nG1) * nGradient / 100); + nB = nB1 + ((nB2 - nB1) * nGradient / 100); + + //set the pixel + m_pen.SetColour(wxColour(nR,nG,nB)); + DrawPoint(wxPoint(x + rect.GetLeft(), y + rect.GetTop())); + } + } + //return old pen color + m_pen.SetColour(oldPenColour); +} Index: src/msw/dc.cpp =================================================================== RCS file: /pack/cvsroots/wxwindows/wxWindows/src/msw/dc.cpp,v retrieving revision 1.150 diff -b -u -2 -r1.150 dc.cpp --- src/msw/dc.cpp 2003/07/11 21:49:59 1.150 +++ src/msw/dc.cpp 2003/07/13 05:41:09 @@ -2428,2 +2428,82 @@ #endif // #ifdef wxHAVE_RAW_BITMAP + +void wxDC::DoGradientFillLinear (const wxRect& rect, + const wxColour& initialColour, + const wxColour& destColour, + wxDirection nDirection) +{ +#if defined wxUSE_DYNLIB_CLASS + //Open Msimg32.dll + wxLog::EnableLogging(false); + wxDynamicLibrary dllMSIMG(_T("Msimg32.dll")); + wxLog::EnableLogging(true); + //is it there? + if (!dllMSIMG.IsLoaded()) + { + //if not, then call the wx version + wxDCBase::DoGradientFillLinear(rect, initialColour, destColour, nDirection); + return; + } + + typedef BOOL (*GradientFill_t) (HDC,PTRIVERTEX, ULONG, PVOID, ULONG, ULONG); + GradientFill_t pfnGradientFill; + + //the following shouldn't fail... + wxLog::EnableLogging(false); + pfnGradientFill = (GradientFill_t) dllMSIMG.GetSymbol(_T("GradientFill")); + wxLog::EnableLogging(true); + + if (pfnGradientFill == NULL) + { + //if it does, call the wx version + wxDCBase::DoGradientFillLinear(rect, initialColour, destColour, nDirection); + return; + } + + TRIVERTEX vertexes[2]; //vertexes - 2 - one for upper left and one for upper-right + + GRADIENT_RECT grect; + grect.UpperLeft = 0; + grect.LowerRight = 1; + + //If wxNORTH or wxWEST then we are specifying the colors in the opposite direction + int nInitialVertex = nDirection == wxNORTH || nDirection == wxWEST; + + vertexes[0].x = rect.GetLeft(); + vertexes[0].y = rect.GetTop(); + vertexes[1].x = rect.GetRight(); + vertexes[1].y = rect.GetBottom(); + + //colors, << 8 to convert from wx to windows + vertexes[nInitialVertex].Red = initialColour.Red() << 8; + vertexes[nInitialVertex].Green = initialColour.Green() << 8; + vertexes[nInitialVertex].Blue = initialColour.Blue() << 8; + vertexes[nInitialVertex].Alpha = 0; + vertexes[!nInitialVertex].Red = destColour.Red() << 8; + vertexes[!nInitialVertex].Green = destColour.Green() << 8; + vertexes[!nInitialVertex].Blue = destColour.Blue() << 8; + vertexes[!nInitialVertex].Alpha = 0; + + BOOL bSuccess; + + //call the gradient fill function + if (nDirection == wxWEST || + nDirection == wxEAST) + bSuccess = (*pfnGradientFill)((HDC)m_hDC, vertexes, 2, &grect, 1, GRADIENT_FILL_RECT_H); + else + bSuccess = (*pfnGradientFill)((HDC)m_hDC, vertexes, 2, &grect, 1, GRADIENT_FILL_RECT_V); + + //shouldn't happen + if (bSuccess == FALSE) + wxLogLastError(_T("GradientFill")); + + //Detach the dll... may want to unload + dllMSIMG.Detach(); +#else //wxUSE_DYNLIB_CLASS + + //no dynlib - call wx version + wxDCBase::DoGradientFillLinear(rect,initialColour,destColour,nDirection); + +#endif +}
- Radial Gradient: Google for Chris Lomont, he did an implementation of practically every implementation known to man, some REALLY fast ones there.
