]> git.saurik.com Git - wxWidgets.git/commitdiff
added linear and concentric gradient fill functions (modified/fixed patch from Ryan...
authorVadim Zeitlin <vadim@wxwidgets.org>
Sun, 12 Feb 2006 01:57:31 +0000 (01:57 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Sun, 12 Feb 2006 01:57:31 +0000 (01:57 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@37512 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

docs/changes.txt
docs/latex/wx/dc.tex
include/wx/dc.h
include/wx/msw/dc.h
samples/drawing/drawing.cpp
src/common/dcbase.cpp
src/msw/dc.cpp

index debade9ee99acdae15c3b337fd4985a62d9baec7..8fc84bce52c089cfad538b7c50156ab0e9318134 100644 (file)
@@ -47,6 +47,7 @@ All:
 All (GUI):
 
 - Added wxTreebook (uses a wxTreeCtrl to control pages).
+- Added wxDC::GradientFillLinear/Concentric()
 - Added wxKeyEvent::GetModifiers()
 - Added wxDialog::SetEscapeId().
 - wxItemContainerImmutable::FindString unified (affects wxRadioBox, wxListBox,
index 84ddcef5515af88215397c259cd8d00a4b7d1eda..b74a3341e9dcc8e7d5dbf46de7b2ff8f1b0decea 100644 (file)
@@ -786,6 +786,35 @@ Gets the current user scale factor (set by \helpref{SetUserScale}{wxdcsetusersca
  array {\tt ( x, y )}}
 
 
+\membersection{wxDC::GradientFillConcentric}\label{wxdcgradientfillconcentric}
+
+\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 
+\arg{initialColour} at the centre of the circle and fading to \arg{destColour} 
+on the circle outside.
+
+\arg{circleCenter} are the relative coordinates of centre of the circle in
+the specified \arg{rect}. If not specified, the cercle is placed at the
+centre of rect.
+
+\textbf{Note: } Currently this function is very slow, don't use it for
+real-time drawing.
+
+
+\membersection{wxDC::GradientFillLinear}\label{wxdcgradientfilllinear}
+
+\func{void}{GradientFillLinear}{\param{const wxRect\&}{ rect}, \param{const wxColour\&}{ initialColour}, \param{const wxColour\&}{ destColour}, \param{wxDirection}{ nDirection = wxEAST}}
+
+Fill the area specified by \arg{rect} with a linear gradient, starting from 
+\arg{initialColour} and eventually fading to \arg{destColour}. The 
+\arg{nDirection} specifies the direction of the colour change, default is to
+use \arg{initialColour} on the left part of the rectangle and 
+\arg{destColour} on the right one.
+
+
 \membersection{wxDC::LogicalToDeviceX}\label{wxdclogicaltodevicex}
 
 \func{wxCoord}{LogicalToDeviceX}{\param{wxCoord}{ x}}
index 4924efc592bcd93857119619cb7d009da3acacde..e7683753d5a0262b14e96c2d9b1afbcd89c150d7 100644 (file)
@@ -158,6 +158,27 @@ public:
                    int style = wxFLOOD_SURFACE)
         { return DoFloodFill(pt.x, pt.y, col, style); }
 
+    // fill the area specified by rect with a radial gradient, starting from
+    // initialColour in the centre of the cercle and fading to destColour.
+    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
+    void GradientFillLinear(const wxRect& rect,
+                            const wxColour& initialColour, 
+                            const wxColour& destColour,
+                            wxDirection nDirection = wxEAST)
+        { DoGradientFillLinear(rect, initialColour, destColour, nDirection); }
+
     bool GetPixel(wxCoord x, wxCoord y, wxColour *col) const
         { return DoGetPixel(x, y, col); }
     bool GetPixel(const wxPoint& pt, wxColour *col) const
@@ -645,6 +666,11 @@ protected:
     virtual bool DoFloodFill(wxCoord x, wxCoord y, const wxColour& col,
                              int style = wxFLOOD_SURFACE) = 0;
 
+    virtual void DoGradientFillLinear(const wxRect& rect,
+                                      const wxColour& initialColour,
+                                      const wxColour& destColour,
+                                      wxDirection nDirection = wxEAST);
+    
     virtual bool DoGetPixel(wxCoord x, wxCoord y, wxColour *col) const = 0;
 
     virtual void DoDrawPoint(wxCoord x, wxCoord y) = 0;
index 0095fa710dd7e7149f44288d52f5f1cb876dbddf..49927b84b390a71d04f631111c920af2d25ffc25 100644 (file)
@@ -164,6 +164,11 @@ protected:
     virtual bool DoFloodFill(wxCoord x, wxCoord y, const wxColour& col,
                              int style = wxFLOOD_SURFACE);
 
+    virtual void DoGradientFillLinear(const wxRect& rect,
+                                      const wxColour& initialColour,
+                                      const wxColour& destColour,
+                                      wxDirection nDirection = wxEAST);
+
     virtual bool DoGetPixel(wxCoord x, wxCoord y, wxColour *col) const;
 
     virtual void DoDrawPoint(wxCoord x, wxCoord y);
index 9f598ddf9e01092cc1fcc8ecb214282e759c0cd7..d13547939514f4375ad340c27f918fb8b93d7f82 100644 (file)
@@ -60,7 +60,9 @@ enum ScreenToShow
     Show_Ops,
     Show_Regions,
     Show_Circles,
-    Show_Splines
+    Show_Splines,
+    Show_Gradient,
+    Show_Max
 };
 
 // ----------------------------------------------------------------------------
@@ -164,6 +166,7 @@ protected:
     void DrawCircles(wxDC& dc);
     void DrawSplines(wxDC& dc);
     void DrawDefault(wxDC& dc);
+    void DrawGradients(wxDC& dc);
 
     void DrawRegionsHelper(wxDC& dc, wxCoord x, bool firstTime);
 
@@ -200,7 +203,8 @@ enum
     File_ShowRegions,
     File_ShowCircles,
     File_ShowSplines,
-    MenuShow_Last = File_ShowSplines,
+    File_ShowGradients,
+    MenuShow_Last = File_ShowGradients,
 
     File_Clip,
 
@@ -971,6 +975,36 @@ void MyCanvas::DrawSplines(wxDC& dc)
 #endif
 }
 
+void MyCanvas::DrawGradients(wxDC& dc)
+{
+    // LHS: linear
+    wxRect r(10, 10, 100, 100);
+    dc.GradientFillLinear(r, *wxWHITE, *wxBLUE, wxRIGHT);
+
+    r.Offset(0, 110);
+    dc.GradientFillLinear(r, *wxWHITE, *wxBLUE, wxLEFT);
+
+    r.Offset(0, 110);
+    dc.GradientFillLinear(r, *wxWHITE, *wxBLUE, wxDOWN);
+
+    r.Offset(0, 110);
+    dc.GradientFillLinear(r, *wxWHITE, *wxBLUE, wxUP);
+
+
+    // RHS: concentric
+    r = wxRect(200, 10, 100, 100);
+    dc.GradientFillConcentric(r, *wxBLUE, *wxWHITE);
+
+    r.Offset(0, 110);
+    dc.GradientFillConcentric(r, *wxWHITE, *wxBLUE);
+
+    r.Offset(0, 110);
+    dc.GradientFillConcentric(r, *wxBLUE, *wxWHITE, wxPoint(0, 0));
+
+    r.Offset(0, 110);
+    dc.GradientFillConcentric(r, *wxBLUE, *wxWHITE, wxPoint(100, 100));
+}
+
 void MyCanvas::DrawRegions(wxDC& dc)
 {
     dc.DrawText(_T("You should see a red rect partly covered by a cyan one ")
@@ -1105,6 +1139,10 @@ void MyCanvas::OnPaint(wxPaintEvent &WXUNUSED(event))
         case Show_Ops:
             DrawWithLogicalOps(dc);
             break;
+
+        case Show_Gradient:
+            DrawGradients(dc);
+            break;
     }
 }
 
@@ -1161,7 +1199,8 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
     menuFile->Append(File_ShowOps, _T("&ROP screen\tF7"));
     menuFile->Append(File_ShowRegions, _T("Re&gions screen\tF8"));
     menuFile->Append(File_ShowCircles, _T("&Circles screen\tF9"));
-    menuFile->Append(File_ShowSplines, _T("&Splines screen"));
+    menuFile->Append(File_ShowSplines, _T("&Splines screen\tF11"));
+    menuFile->Append(File_ShowGradients, _T("&Gradients screen\tF12"));
     menuFile->AppendSeparator();
     menuFile->AppendCheckItem(File_Clip, _T("&Clip\tCtrl-C"), _T("Clip/unclip drawing"));
     menuFile->AppendSeparator();
index 092f356574bbae39483e0e27e8c7397a5d33a5fc..3326bbda299f24119d7b1dc4809d5896dfac6d70 100644 (file)
@@ -680,6 +680,165 @@ void wxDCBase::DrawLabel(const wxString& text,
     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 )
+    {
+        wxInt32 x = rect.GetWidth();
+        wxInt32 w = x;              // width of area to shade
+        wxInt32 xDelta = w/256;     // 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;     // 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);
+}
+
 /*
 Notes for wxWidgets DrawEllipticArcRot(...)
 
index 20bf623044f063dd38cf7a04a63d6d0f53f79063..62d88bf832011a26790d2983b600683b1db265b0 100644 (file)
@@ -196,6 +196,37 @@ private:
     DECLARE_NO_COPY_CLASS(StretchBltModeChanger)
 };
 
+// support for dynamic loading of msimg32.dll which we use for some functions
+class wxMSImg32DLL
+{
+public:
+    // return the symbol with the given name if the DLL not loaded or symbol
+    // not present
+    static void *GetSymbol(const wxChar *name)
+    {
+        wxLogNull noLog;
+
+        if ( !ms_triedToLoad )
+        {
+            ms_triedToLoad = true;
+            ms_dll.Load(_T("msimg32"));
+        }
+
+        return ms_dll.IsLoaded() ? ms_dll.GetSymbol(name) : NULL;
+    }
+
+private:
+    static wxDynamicLibrary ms_dll;
+    static bool ms_triedToLoad;
+};
+
+wxDynamicLibrary wxMSImg32DLL::ms_dll;
+bool wxMSImg32DLL::ms_triedToLoad = false;
+
+// helper macro for getting the symbols from msimg32.dll: it supposes that a
+// type "name_t" is defined and casts the returned symbol to it automatically
+#define wxMSIMG32_SYMBOL(name) (name ## _t)wxMSImg32DLL::GetSymbol(_T(#name))
+
 // ===========================================================================
 // implementation
 // ===========================================================================
@@ -2483,32 +2514,7 @@ static bool AlphaBlt(HDC hdcDst,
                                         HDC,int,int,int,int,
                                         BLENDFUNCTION);
 
-    // bitmaps can be drawn only from GUI thread so there is no need to
-    // protect this static variable from multiple threads
-    static bool s_triedToLoad = false;
-    static AlphaBlend_t pfnAlphaBlend = NULL;
-    if ( !s_triedToLoad )
-    {
-        s_triedToLoad = true;
-
-        // don't give errors about the DLL being unavailable, we're
-        // prepared to handle this
-        wxLogNull nolog;
-
-        wxDynamicLibrary dll(_T("msimg32.dll"));
-        if ( dll.IsLoaded() )
-        {
-            pfnAlphaBlend = (AlphaBlend_t)dll.GetSymbol(_T("AlphaBlend"));
-            if ( pfnAlphaBlend )
-            {
-                // we must keep the DLL loaded if we want to be able to
-                // call AlphaBlend() so just never unload it at all, not a
-                // big deal
-                dll.Detach();
-            }
-        }
-    }
-
+    static AlphaBlend_t pfnAlphaBlend = wxMSIMG32_SYMBOL(AlphaBlend);
     if ( pfnAlphaBlend )
     {
         BLENDFUNCTION bf;
@@ -2609,3 +2615,68 @@ wxAlphaBlend(HDC hdcDst, int xDst, int yDst,
 }
 
 #endif // #ifdef wxHAVE_RAW_BITMAP
+
+void wxDC::DoGradientFillLinear (const wxRect& rect,
+                                 const wxColour& initialColour,
+                                 const wxColour& destColour,
+                                 wxDirection nDirection)
+{
+    // use native function if we have compile-time support it and can load it
+    // during run-time (linking to it statically would make the program
+    // unusable on earlier Windows versions)
+#if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
+    typedef BOOL
+        (WINAPI *GradientFill_t)(HDC, PTRIVERTEX, ULONG, PVOID, ULONG, ULONG);
+    static GradientFill_t pfnGradientFill = wxMSIMG32_SYMBOL(GradientFill);
+
+    if ( pfnGradientFill )
+    {
+        GRADIENT_RECT grect;
+        grect.UpperLeft = 0;
+        grect.LowerRight = 1;
+
+        // invert colours direction if not filling from left-to-right or
+        // top-to-bottom
+        int firstVertex = nDirection == wxNORTH || nDirection == wxWEST ? 1 : 0;
+
+        // one vertex for upper left and one for upper-right
+        TRIVERTEX vertices[2];
+
+        vertices[0].x = rect.GetLeft();
+        vertices[0].y = rect.GetTop();
+        vertices[1].x = rect.GetRight();
+        vertices[1].y = rect.GetBottom();
+
+        vertices[firstVertex].Red = initialColour.Red() << 8;
+        vertices[firstVertex].Green = initialColour.Green() << 8;
+        vertices[firstVertex].Blue = initialColour.Blue() << 8;
+        vertices[firstVertex].Alpha = 0;
+        vertices[1 - firstVertex].Red = destColour.Red() << 8;
+        vertices[1 - firstVertex].Green = destColour.Green() << 8;
+        vertices[1 - firstVertex].Blue = destColour.Blue() << 8;
+        vertices[1 - firstVertex].Alpha = 0;
+
+        if (nDirection == wxWEST ||
+            nDirection == wxEAST)
+        if ( (*pfnGradientFill)
+             (
+                GetHdc(),
+                vertices,
+                WXSIZEOF(vertices),
+                &grect,
+                1,
+                nDirection == wxWEST || nDirection == wxEAST
+                    ? GRADIENT_FILL_RECT_H
+                    : GRADIENT_FILL_RECT_V
+             ) )
+        {
+            // skip call of the base class version below
+            return;
+        }
+
+        wxLogLastError(_T("GradientFill"));
+    }
+#endif // wxUSE_DYNLIB_CLASS
+
+    wxDCBase::DoGradientFillLinear(rect, initialColour, destColour, nDirection);
+}