/////////////////////////////////////////////////////////////////////////////
-// Name: common/dcbase.cpp
+// Name: src/common/dcbase.cpp
// Purpose: generic methods of the wxDC Class
// Author: Vadim Zeitlin
// Modified by:
// Created: 05/25/99
// RCS-ID: $Id$
-// Copyright: (c) wxWindows team
+// Copyright: (c) wxWidgets team
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// declarations
// ============================================================================
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
- #pragma implementation "dcbase.h"
-#endif
-
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#endif
#include "wx/dc.h"
+#include "wx/dcbuffer.h" // for IMPLEMENT_DYNAMIC_CLASS
-#include <math.h>
+#ifndef WX_PRECOMP
+ #include "wx/math.h"
+#endif
-// bool wxDCBase::sm_cacheing = FALSE;
+// bool wxDCBase::sm_cacheing = false;
+
+IMPLEMENT_ABSTRACT_CLASS(wxDCBase, wxObject)
// ============================================================================
// implementation
// ============================================================================
+IMPLEMENT_DYNAMIC_CLASS(wxBufferedDC, wxMemoryDC)
+IMPLEMENT_ABSTRACT_CLASS(wxBufferedPaintDC, wxBufferedDC)
+
+#if WXWIN_COMPATIBILITY_2_6
+void wxDCBase::BeginDrawing()
+{
+}
+
+void wxDCBase::EndDrawing()
+{
+}
+#endif // WXWIN_COMPATIBILITY_2_6
+
// ----------------------------------------------------------------------------
// special symbols
// ----------------------------------------------------------------------------
wxCoord x2 = x1 + width,
y2 = y1 + height;
- // this is to yield width of 3 for width == height == 10
- SetPen(wxPen(GetTextForeground(), (width + height + 1) / 7, wxSOLID));
+ // the pen width is calibrated to give 3 for width == height == 10
+ wxDCPenChanger pen((wxDC&)*this,
+ wxPen(GetTextForeground(), (width + height + 1)/7));
// we're drawing a scaled version of wx/generic/tick.xpm here
wxCoord x3 = x1 + (4*width) / 10, // x of the tick bottom
void
wxDCBase::DoDrawPolyPolygon(int n,
- int start[],
+ int count[],
wxPoint points[],
wxCoord xoffset, wxCoord yoffset,
int fillStyle)
{
if ( n == 1 )
{
- DoDrawPolygon(start[0], points, xoffset, yoffset, fillStyle);
+ DoDrawPolygon(count[0], points, xoffset, yoffset, fillStyle);
return;
}
for (i = j = lastOfs = 0; i < n; i++)
{
lastOfs = j;
- j += start[i];
+ j += count[i];
}
pts = new wxPoint[j+n-1];
for (i = 0; i < j; i++)
pts[i] = points[i];
for (i = 2; i <= n; i++)
{
- lastOfs -= start[n-i];
+ lastOfs -= count[n-i];
pts[j++] = pts[lastOfs];
}
SetPen(pen);
for (i = j = 0; i < n; i++)
{
- DoDrawLines(start[i], pts+j, xoffset, yoffset);
- j += start[i];
+ DoDrawLines(count[i], pts+j, xoffset, yoffset);
+ j += count[i];
}
- delete pts;
+ delete[] pts;
}
// ----------------------------------------------------------------------------
point->x = (int) x;
point->y = (int) y;
wx_spline_point_list.Append((wxObject*)point);
- return TRUE;
+ return true;
}
static void wx_spline_draw_point_array(wxDCBase *dc)
double x1, y1, x2, y2;
wxList::compatibility_iterator node = points->GetFirst();
+ if (!node)
+ // empty list
+ return;
+
p = (wxPoint *)node->GetData();
x1 = p->x;
// ----------------------------------------------------------------------------
-// Each element of the array will be the width of the string up to and
-// including the coresoponding character in text. This is the generic
+// Each element of the widths array will be the width of the string up to and
+// including the corresponding character in text. This is the generic
// implementation, the port-specific classes should do this with native APIs
-// if available.
+// if available and if faster. Note: pango_layout_index_to_pos is much slower
+// than calling GetTextExtent!!
+
+#define FWC_SIZE 256
+
+class FontWidthCache
+{
+public:
+ FontWidthCache() : m_scaleX(1), m_widths(NULL) { }
+ ~FontWidthCache() { delete []m_widths; }
+
+ void Reset()
+ {
+ if (!m_widths)
+ m_widths = new int[FWC_SIZE];
+
+ memset(m_widths, 0, sizeof(int)*FWC_SIZE);
+ }
+
+ wxFont m_font;
+ double m_scaleX;
+ int *m_widths;
+};
+
+static FontWidthCache s_fontWidthCache;
bool wxDCBase::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const
{
int totalWidth = 0;
- size_t i;
+ const size_t len = text.length();
widths.Empty();
- widths.Add(0, text.Length());
-
+ widths.Add(0, len);
+
+ // reset the cache if font or horizontal scale have changed
+ if ( !s_fontWidthCache.m_widths ||
+ !wxIsSameDouble(s_fontWidthCache.m_scaleX, m_scaleX) ||
+ (s_fontWidthCache.m_font != GetFont()) )
+ {
+ s_fontWidthCache.Reset();
+ s_fontWidthCache.m_font = GetFont();
+ s_fontWidthCache.m_scaleX = m_scaleX;
+ }
+
// Calculate the position of each character based on the widths of
// the previous characters
- for (i=0; i<text.Length(); i++) {
- int w, h;
- GetTextExtent(text[i], &w, &h);
+ int w, h;
+ for ( size_t i = 0; i < len; i++ )
+ {
+ const wxChar c = text[i];
+ unsigned int c_int = (unsigned int)c;
+
+ if ((c_int < FWC_SIZE) && (s_fontWidthCache.m_widths[c_int] != 0))
+ {
+ w = s_fontWidthCache.m_widths[c_int];
+ }
+ else
+ {
+ GetTextExtent(c, &w, &h);
+ if (c_int < FWC_SIZE)
+ s_fontWidthCache.m_widths[c_int] = w;
+ }
+
totalWidth += w;
widths[i] = totalWidth;
}
+
return true;
}
wxCoord *x,
wxCoord *y,
wxCoord *h,
- wxFont *font)
+ wxFont *font) const
{
wxCoord widthTextMax = 0, widthLine,
heightTextTotal = 0, heightLineDefault = 0, heightLine = 0;
width0 = width;
if ( bitmap.Ok() )
{
- DrawBitmap(bitmap, x, y, TRUE /* use mask */);
+ DrawBitmap(bitmap, x, y, true /* use mask */);
wxCoord offset = bitmap.GetWidth() + 4;
x += offset;
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;
+ wxBrush oldBrush = m_brush;
+
+ wxUint8 nR1 = initialColour.Red();
+ wxUint8 nG1 = initialColour.Green();
+ wxUint8 nB1 = initialColour.Blue();
+ wxUint8 nR2 = destColour.Red();
+ wxUint8 nG2 = destColour.Green();
+ wxUint8 nB2 = destColour.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;
+
+ wxColour colour(nR,nG,nB);
+ SetPen(wxPen(colour, 1, wxSOLID));
+ SetBrush(wxBrush(colour));
+ if(nDirection == wxEAST)
+ DrawRectangle(rect.GetRight()-x-xDelta, rect.GetTop(),
+ xDelta, rect.GetHeight());
+ else //nDirection == wxWEST
+ DrawRectangle(rect.GetLeft()+x, 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;
+
+ wxColour colour(nR,nG,nB);
+ SetPen(wxPen(colour, 1, wxSOLID));
+ SetBrush(wxBrush(colour));
+ 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);
+ SetBrush(oldBrush);
+}
+
+void wxDCBase::DoGradientFillConcentric(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;
+
+
+ //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 ( wxInt32 x = 0; x < rect.GetWidth(); x++ )
+ {
+ for ( wxInt32 y = 0; y < rect.GetHeight(); y++ )
+ {
+ //get color difference
+ wxInt32 nGradient = ((nRadius -
+ (wxInt32)sqrt(
+ pow((double)(x - cx - nCircleOffX), 2) +
+ pow((double)(y - cy - nCircleOffY), 2)
+ )) * 100) / nRadius;
+
+ //normalize Gradient
+ if (nGradient < 0 )
+ nGradient = 0;
+
+ //get dest colors
+ nR = (wxUint8)(nR1 + ((nR2 - nR1) * nGradient / 100));
+ nG = (wxUint8)(nG1 + ((nG2 - nG1) * nGradient / 100));
+ nB = (wxUint8)(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 wxWindows DrawEllipticArcRot(...)
+Notes for wxWidgets DrawEllipticArcRot(...)
wxDCBase::DrawEllipticArcRot(...) draws a rotated elliptic arc or an ellipse.
It uses wxDCBase::CalculateEllipticPoints(...) and wxDCBase::Rotate(...),
methods like (WinCE) wxDC::DoDrawArc(...).
CalculateEllipticPoints(...) fills a given list of wxPoints with some points
-of an elliptic arc. The algorithm is pixel-based: In every row (in flat
+of an elliptic arc. The algorithm is pixel-based: In every row (in flat
parts) or every column (in steep parts) only one pixel is calculated.
Trigonometric calculation (sin, cos, tan, atan) is only done if the
-starting angle is not equal to the ending angle. The calculation of the
+starting angle is not equal to the ending angle. The calculation of the
pixels is done using simple arithmetic only and should perform not too
bad even on devices without floating point processor. I didn't test this yet.
Rotate(...) rotates a list of point pixel-based, you will see rounding errors.
-For instance: an ellipse rotated 180 degrees is drawn
+For instance: an ellipse rotated 180 degrees is drawn
slightly different from the original.
-The points are then moved to an array and used to draw a polyline and/or polygon
-(with center added, the pie).
+The points are then moved to an array and used to draw a polyline and/or polygon
+(with center added, the pie).
The result looks quite similar to the native ellipse, only e few pixels differ.
The performance on a desktop system (Athlon 1800, WinXP) is about 7 times
An rotated ellipse outside the clipping region takes nearly the same time,
while an native ellipse outside takes nearly no time to draw.
-If you draw an arc with this new method, you will see the starting and ending angles
+If you draw an arc with this new method, you will see the starting and ending angles
are calculated properly.
If you use DrawEllipticArc(...), you will see they are only correct for circles
and not properly calculated for ellipses.
*/
#ifdef __WXWINCE__
-void wxDCBase::DoDrawEllipticArcRot( wxCoord x, wxCoord y,
- wxCoord w, wxCoord h,
+void wxDCBase::DoDrawEllipticArcRot( wxCoord x, wxCoord y,
+ wxCoord w, wxCoord h,
double sa, double ea, double angle )
{
wxList list;
list.Append( (wxObject*) new wxPoint( x+w/2, y+h/2 ) );
// copy list into array and delete list elements
- int n = list.Number();
+ int n = list.GetCount();
wxPoint *points = new wxPoint[n];
int i = 0;
- wxNode* node = 0;
- for ( node = list.First(); node; node = node->Next(), i++ )
+ wxNode* node = 0;
+ for ( node = list.GetFirst(); node; node = node->GetNext(), i++ )
{
- wxPoint *point = (wxPoint *)node->Data();
+ wxPoint *point = (wxPoint *)node->GetData();
points[i].x = point->x;
points[i].y = point->y;
delete point;
{
if( angle != 0.0 )
{
- double pi(3.1415926536);
+ double pi(M_PI);
double dSinA = -sin(angle*2.0*pi/360.0);
double dCosA = cos(angle*2.0*pi/360.0);
- for ( wxNode* node = points->First(); node; node = node->Next() )
+ for ( wxNode* node = points->GetFirst(); node; node = node->GetNext() )
{
- wxPoint* point = (wxPoint*)node->Data();
-
+ wxPoint* point = (wxPoint*)node->GetData();
+
// transform coordinates, if necessary
if( center.x ) point->x -= center.x;
if( center.y ) point->y -= center.y;
}
}
-void wxDCBase::CalculateEllipticPoints( wxList* points,
- wxCoord xStart, wxCoord yStart,
- wxCoord w, wxCoord h,
+void wxDCBase::CalculateEllipticPoints( wxList* points,
+ wxCoord xStart, wxCoord yStart,
+ wxCoord w, wxCoord h,
double sa, double ea )
{
- double pi = 3.1415926535;
+ double pi = M_PI;
double sar = 0;
double ear = 0;
int xsa = 0;
wxCoord b = h/2;
// decrement 1 pixel if ellipse is smaller than 2*a, 2*b
int decrX = 0;
- if( 2*a == w ) decrX = 1;
+ if( 2*a == w ) decrX = 1;
int decrY = 0;
- if( 2*b == h ) decrY = 1;
+ if( 2*b == h ) decrY = 1;
// center
wxCoord xCenter = xStart + a;
wxCoord yCenter = yStart + b;
ear = ea * pi / 180.0;
// correct angle circle -> ellipse
sar = atan( -a/(double)b * tan( sar ) );
- if ( sq == 1 || sq == 2 ) sar += pi;
+ if ( sq == 1 || sq == 2 ) sar += pi;
ear = atan( -a/(double)b * tan( ear ) );
if ( eq == 1 || eq == 2 ) ear += pi;
// coordinates of points
y2 = y2-y-y+1;
--y;
}
- // old y now to big: set point with old y, old x
+ // old y now to big: set point with old y, old x
if( bNewPoint && x>1)
{
int x1 = x - 1;
pointsarray[3].Append( (wxObject*) new wxPoint( xCenter + x1 - decrX, yCenter + y_old - decrY ) );
} // set point
} // calculate point
-
+
// Starting and/or ending points for the quadrants, first quadrant gets both.
pointsarray[0].Insert( (wxObject*) new wxPoint( xCenter + a - decrX, yCenter ) );
pointsarray[0].Append( (wxObject*) new wxPoint( xCenter, yCenter - b ) );
bool bForceTurn = ( sq == eq && sa > ea );
while( !bReady )
{
- for( wxNode *node = pointsarray[q].First(); node; node = node->Next() )
+ for( wxNode *node = pointsarray[q].GetFirst(); node; node = node->GetNext() )
{
// once: go to starting point in start quadrant
if( !bStarted &&
- (
- ( (wxPoint*) node->Data() )->x < xsa+1 && q <= 1
- ||
- ( (wxPoint*) node->Data() )->x > xsa-1 && q >= 2
+ (
+ ( (wxPoint*) node->GetData() )->x < xsa+1 && q <= 1
+ ||
+ ( (wxPoint*) node->GetData() )->x > xsa-1 && q >= 2
)
- )
+ )
{
bStarted = true;
}
{
if( q != eq || bForceTurn
||
- ( (wxPoint*) node->Data() )->x > xea+1 && q <= 1
- ||
- ( (wxPoint*) node->Data() )->x < xea-1 && q >= 2
+ ( (wxPoint*) node->GetData() )->x > xea+1 && q <= 1
+ ||
+ ( (wxPoint*) node->GetData() )->x < xea-1 && q >= 2
)
{
// copy point
- wxPoint* pPoint = new wxPoint( *((wxPoint*) node->Data() ) );
+ wxPoint* pPoint = new wxPoint( *((wxPoint*) node->GetData() ) );
points->Append( (wxObject*) pPoint );
}
- else if( q == eq && !bForceTurn || ( (wxPoint*) node->Data() )->x == xea)
+ else if( q == eq && !bForceTurn || ( (wxPoint*) node->GetData() )->x == xea)
{
- bReady = true;
+ bReady = true;
}
}
} // for node
// delete points
for( q = 0; q < 4; ++q )
{
- for( wxNode *node = pointsarray[q].First(); node; node = node->Next() )
+ for( wxNode *node = pointsarray[q].GetFirst(); node; node = node->GetNext() )
{
- wxPoint *p = (wxPoint *)node->Data();
+ wxPoint *p = (wxPoint *)node->GetData();
delete p;
}
- }
-
+ }
}
else
{
+ wxNode* node;
// copy whole ellipse, wxPoints will be deleted outside
- for( wxNode *node = pointsarray[0].First(); node; node = node->Next() )
+ for( node = pointsarray[0].GetFirst(); node; node = node->GetNext() )
{
- wxObject *p = node->Data();
+ wxObject *p = node->GetData();
points->Append( p );
}
- for( node = pointsarray[1].First(); node; node = node->Next() )
+ for( node = pointsarray[1].GetFirst(); node; node = node->GetNext() )
{
- wxObject *p = node->Data();
+ wxObject *p = node->GetData();
points->Append( p );
}
- for( node = pointsarray[2].First(); node; node = node->Next() )
+ for( node = pointsarray[2].GetFirst(); node; node = node->GetNext() )
{
- wxObject *p = node->Data();
+ wxObject *p = node->GetData();
points->Append( p );
}
- for( node = pointsarray[3].First(); node; node = node->Next() )
+ for( node = pointsarray[3].GetFirst(); node; node = node->GetNext() )
{
- wxObject *p = node->Data();
+ wxObject *p = node->GetData();
points->Append( p );
}
} // not iUseAngles
} // CalculateEllipticPoints
#endif
-