added linear and concentric gradient fill functions (modified/fixed patch from Ryan Norton)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@37512 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin 2006-02-12 01:57:31 +00:00
parent d6bf7254f4
commit 213ad8e72c
7 changed files with 359 additions and 29 deletions

View 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,

View 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}}

View 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;

View 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);

View 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();

View 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(...)

View 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);
}