1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
21 #pragma implementation "dc.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
32 #include "wx/window.h"
35 #include "wx/dialog.h"
37 #include "wx/bitmap.h"
38 #include "wx/dcmemory.h"
43 #include "wx/dcprint.h"
48 #include "wx/msw/private.h" // needs to be before #include <commdlg.h>
50 #if wxUSE_COMMON_DIALOGS
58 IMPLEMENT_ABSTRACT_CLASS(wxDC
, wxObject
)
60 // ---------------------------------------------------------------------------
62 // ---------------------------------------------------------------------------
64 static const int VIEWPORT_EXTENT
= 1000;
66 static const int MM_POINTS
= 9;
67 static const int MM_METRIC
= 10;
69 // usually this is defined in math.h
71 static const double M_PI
= 3.14159265358979323846;
74 // ROPs which don't have standard names (see "Ternary Raster Operations" in the
75 // MSDN docs for how this and other numbers in wxDC::Blit() are obtained)
76 #define DSTCOPY 0x00AA0029 // a.k.a. NOP operation
78 // ---------------------------------------------------------------------------
80 // ---------------------------------------------------------------------------
82 // convert degrees to radians
83 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
85 // ===========================================================================
87 // ===========================================================================
89 // ---------------------------------------------------------------------------
91 // ---------------------------------------------------------------------------
93 // Default constructor
107 m_windowExtX
= VIEWPORT_EXTENT
;
108 m_windowExtY
= VIEWPORT_EXTENT
;
117 SelectOldObjects(m_hDC
);
119 if ( m_canvas
== NULL
)
120 ::DeleteDC(GetHdc());
122 ::ReleaseDC((HWND
)m_canvas
->GetHWND(), GetHdc());
128 // This will select current objects out of the DC,
129 // which is what you have to do before deleting the
131 void wxDC::SelectOldObjects(WXHDC dc
)
137 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
138 if (m_selectedBitmap
.Ok())
140 m_selectedBitmap
.SetSelectedInto(NULL
);
146 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
151 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
156 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
161 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, TRUE
);
166 m_brush
= wxNullBrush
;
168 m_palette
= wxNullPalette
;
170 m_backgroundBrush
= wxNullBrush
;
171 m_selectedBitmap
= wxNullBitmap
;
174 // ---------------------------------------------------------------------------
176 // ---------------------------------------------------------------------------
178 void wxDC::DoSetClippingRegion(wxCoord cx
, wxCoord cy
, wxCoord cw
, wxCoord ch
)
183 m_clipX2
= (int)(cx
+ cw
);
184 m_clipY2
= (int)(cy
+ ch
);
186 DoClipping((WXHDC
) m_hDC
);
189 void wxDC::DoSetClippingRegionAsRegion(const wxRegion
& region
)
191 wxCHECK_RET( region
.GetHRGN(), wxT("invalid clipping region") );
193 wxRect box
= region
.GetBox();
198 m_clipX2
= box
.x
+ box
.width
;
199 m_clipY2
= box
.y
+ box
.height
;
202 SelectClipRgn(GetHdc(), (HRGN
) region
.GetHRGN());
204 ExtSelectClipRgn(GetHdc(), (HRGN
) region
.GetHRGN(), RGN_AND
);
208 void wxDC::DoClipping(WXHDC dc
)
210 if (m_clipping
&& dc
)
212 IntersectClipRect((HDC
) dc
, XLOG2DEV(m_clipX1
), YLOG2DEV(m_clipY1
),
213 XLOG2DEV(m_clipX2
), YLOG2DEV(m_clipY2
));
217 void wxDC::DestroyClippingRegion()
219 if (m_clipping
&& m_hDC
)
221 // TODO: this should restore the previous clipping region,
222 // so that OnPaint processing works correctly, and the update clipping region
223 // doesn't get destroyed after the first DestroyClippingRegion.
224 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
225 SelectClipRgn(GetHdc(), rgn
);
231 // ---------------------------------------------------------------------------
232 // query capabilities
233 // ---------------------------------------------------------------------------
235 bool wxDC::CanDrawBitmap() const
240 bool wxDC::CanGetTextExtent() const
242 // What sort of display is it?
243 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
245 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
248 int wxDC::GetDepth() const
250 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
253 // ---------------------------------------------------------------------------
255 // ---------------------------------------------------------------------------
262 GetClientRect((HWND
) m_canvas
->GetHWND(), &rect
);
266 // No, I think we should simply ignore this if printing on e.g.
268 // wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
269 if (!m_selectedBitmap
.Ok())
272 rect
.left
= 0; rect
.top
= 0;
273 rect
.right
= m_selectedBitmap
.GetWidth();
274 rect
.bottom
= m_selectedBitmap
.GetHeight();
277 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
279 DWORD colour
= GetBkColor(GetHdc());
280 HBRUSH brush
= CreateSolidBrush(colour
);
281 FillRect(GetHdc(), &rect
, brush
);
284 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
285 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
286 ::SetWindowExtEx(GetHdc(), m_windowExtX
, m_windowExtY
, NULL
);
287 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
288 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
291 void wxDC::DoFloodFill(wxCoord x
, wxCoord y
, const wxColour
& col
, int style
)
293 if ( !::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
295 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
298 // quoting from the MSDN docs:
300 // Following are some of the reasons this function might fail:
302 // * The filling could not be completed.
303 // * The specified point has the boundary color specified by the
304 // crColor parameter (if FLOODFILLBORDER was requested).
305 // * The specified point does not have the color specified by
306 // crColor (if FLOODFILLSURFACE was requested)
307 // * The point is outside the clipping region that is, it is not
308 // visible on the device.
310 wxLogLastError("ExtFloodFill");
313 CalcBoundingBox(x
, y
);
316 bool wxDC::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
318 // get the color of the pixel
319 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
321 // get the color of the pen
322 COLORREF pencolor
= 0x00ffffff;
325 pencolor
= m_pen
.GetColour().GetPixel();
328 // return the color of the pixel
331 col
->Set(GetRValue(pixelcolor
),
332 GetGValue(pixelcolor
),
333 GetBValue(pixelcolor
));
336 // check, if color of the pixels is the same as the color of the current
337 // pen and return TRUE if it is, FALSE otherwise
338 return pixelcolor
== pencolor
;
341 void wxDC::DoCrossHair(wxCoord x
, wxCoord y
)
343 wxCoord x1
= x
-VIEWPORT_EXTENT
;
344 wxCoord y1
= y
-VIEWPORT_EXTENT
;
345 wxCoord x2
= x
+VIEWPORT_EXTENT
;
346 wxCoord y2
= y
+VIEWPORT_EXTENT
;
348 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), NULL
);
349 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y
));
351 (void)MoveToEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), NULL
);
352 (void)LineTo(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y2
));
354 CalcBoundingBox(x1
, y1
);
355 CalcBoundingBox(x2
, y2
);
358 void wxDC::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
360 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), NULL
);
361 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y2
));
363 // Normalization: Windows doesn't draw the last point of the line.
364 // But apparently neither does GTK+, so we take it out again.
365 // (void)LineTo(GetHdc(), XLOG2DEV(x2) + 1, YLOG2DEV(y2));
367 CalcBoundingBox(x1
, y1
);
368 CalcBoundingBox(x2
, y2
);
371 void wxDC::DoDrawArc(wxCoord x1
,wxCoord y1
,wxCoord x2
,wxCoord y2
, wxCoord xc
, wxCoord yc
)
375 double radius
= (double)sqrt(dx
*dx
+dy
*dy
) ;;
376 if (x1
==x2
&& x2
==y2
)
378 DrawEllipse(xc
,yc
,(wxCoord
)(radius
*2.0),(wxCoord
)(radius
*2.0));
382 wxCoord xx1
= XLOG2DEV(x1
);
383 wxCoord yy1
= YLOG2DEV(y1
);
384 wxCoord xx2
= XLOG2DEV(x2
);
385 wxCoord yy2
= YLOG2DEV(y2
);
386 wxCoord xxc
= XLOG2DEV(xc
);
387 wxCoord yyc
= YLOG2DEV(yc
);
388 wxCoord ray
= (wxCoord
) sqrt(double((xxc
-xx1
)*(xxc
-xx1
)+(yyc
-yy1
)*(yyc
-yy1
)));
390 (void)MoveToEx(GetHdc(), (int) xx1
, (int) yy1
, NULL
);
391 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
392 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
393 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
394 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
395 if (m_brush
.Ok() && m_brush
.GetStyle() !=wxTRANSPARENT
)
397 // Have to add 1 to bottom-right corner of rectangle
398 // to make semi-circles look right (crooked line otherwise).
399 // Unfortunately this is not a reliable method, depends
400 // on the size of shape.
401 // TODO: figure out why this happens!
402 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1,
406 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
,
409 CalcBoundingBox((wxCoord
)(xc
-radius
), (wxCoord
)(yc
-radius
));
410 CalcBoundingBox((wxCoord
)(xc
+radius
), (wxCoord
)(yc
+radius
));
413 void wxDC::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
414 wxCoord width
, wxCoord height
)
416 wxCoord x2
= x1
+ width
,
419 #if defined(__WIN32__) && !defined(__SC__)
426 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
428 // In WIN16, draw a cross
429 HPEN blackPen
= ::CreatePen(PS_SOLID
, 1, RGB(0, 0, 0));
430 HPEN whiteBrush
= (HPEN
)::GetStockObject(WHITE_BRUSH
);
431 HPEN hPenOld
= (HPEN
)::SelectObject(hdcMem
, blackPen
);
432 HPEN hBrushOld
= (HPEN
)::SelectObject(hdcMem
, whiteBrush
);
433 ::SetROP2(GetHdc(), R2_COPYPEN
);
434 Rectangle(GetHdc(), x1
, y1
, x2
, y2
);
435 MoveTo(GetHdc(), x1
, y1
);
436 LineTo(GetHdc(), x2
, y2
);
437 MoveTo(GetHdc(), x2
, y1
);
438 LineTo(GetHdc(), x1
, y2
);
439 ::SelectObject(GetHdc(), hPenOld
);
440 ::SelectObject(GetHdc(), hBrushOld
);
441 ::DeleteObject(blackPen
);
444 CalcBoundingBox(x1
, y1
);
445 CalcBoundingBox(x2
, y2
);
448 void wxDC::DoDrawPoint(wxCoord x
, wxCoord y
)
450 COLORREF color
= 0x00ffffff;
453 color
= m_pen
.GetColour().GetPixel();
456 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
458 CalcBoundingBox(x
, y
);
461 void wxDC::DoDrawPolygon(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
,int fillStyle
)
463 COLORREF old_textground
= ::GetTextColor(GetHdc());
464 COLORREF old_background
= ::GetBkColor(GetHdc());
465 if (m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
468 if (m_textForegroundColour
.Ok())
469 { //just the oposite from what is expected see help on pattern brush
470 // 1 in mask becomes bk color
471 ::SetBkColor(GetHdc(), m_textForegroundColour
.GetPixel() );
473 if (m_textBackgroundColour
.Ok())
474 { //just the oposite from what is expected
475 // 0 in mask becomes text color
476 ::SetTextColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
479 if (m_backgroundMode
== wxTRANSPARENT
)
480 SetBkMode(GetHdc(), TRANSPARENT
);
482 SetBkMode(GetHdc(), OPAQUE
);
485 // Do things less efficiently if we have offsets
486 if (xoffset
!= 0 || yoffset
!= 0)
488 POINT
*cpoints
= new POINT
[n
];
490 for (i
= 0; i
< n
; i
++)
492 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
493 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
495 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
497 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
498 (void)Polygon(GetHdc(), cpoints
, n
);
499 SetPolyFillMode(GetHdc(),prev
);
505 for (i
= 0; i
< n
; i
++)
506 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
508 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
509 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
510 SetPolyFillMode(GetHdc(),prev
);
513 if (m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
515 ::SetBkMode(GetHdc(), TRANSPARENT
);
516 ::SetTextColor(GetHdc(), old_textground
);
517 ::SetBkColor(GetHdc(), old_background
);
521 void wxDC::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
523 // Do things less efficiently if we have offsets
524 if (xoffset
!= 0 || yoffset
!= 0)
526 POINT
*cpoints
= new POINT
[n
];
528 for (i
= 0; i
< n
; i
++)
530 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
531 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
533 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
535 (void)Polyline(GetHdc(), cpoints
, n
);
541 for (i
= 0; i
< n
; i
++)
542 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
544 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
548 void wxDC::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
550 COLORREF colFgOld
= 0,
553 if ( m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
555 colFgOld
= ::GetTextColor(GetHdc());
556 colBgOld
= ::GetBkColor(GetHdc());
558 if ( m_textForegroundColour
.Ok() )
560 // just the oposite from what is expected see help on pattern brush
561 // 1 in mask becomes bk color
562 ::SetBkColor(GetHdc(), m_textForegroundColour
.GetPixel());
565 if ( m_textBackgroundColour
.Ok() )
567 // 0 in mask becomes text color
568 ::SetTextColor(GetHdc(), m_textBackgroundColour
.GetPixel());
571 // VZ: IMHO this does strictly nothing here
572 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
576 wxCoord x2
= x
+ width
;
577 wxCoord y2
= y
+ height
;
579 if ((m_logicalFunction
== wxCOPY
) && (m_pen
.GetStyle() == wxTRANSPARENT
))
582 rect
.left
= XLOG2DEV(x
);
583 rect
.top
= YLOG2DEV(y
);
584 rect
.right
= XLOG2DEV(x2
);
585 rect
.bottom
= YLOG2DEV(y2
);
586 (void)FillRect(GetHdc(), &rect
, (HBRUSH
)m_brush
.GetResourceHandle() );
590 // Windows draws the filled rectangles without outline (i.e. drawn with a
591 // transparent pen) one pixel smaller in both directions and we want them
592 // to have the same size regardless of which pen is used - adjust
594 // I wonder if this shouldn´t be done after the LOG2DEV() conversions. RR.
595 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
601 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
605 CalcBoundingBox(x
, y
);
606 CalcBoundingBox(x2
, y2
);
608 if ( m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
610 // restore the colours we changed
611 ::SetBkMode(GetHdc(), TRANSPARENT
);
612 ::SetTextColor(GetHdc(), colFgOld
);
613 ::SetBkColor(GetHdc(), colBgOld
);
617 void wxDC::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
619 // Now, a negative radius value is interpreted to mean
620 // 'the proportion of the smallest X or Y dimension'
624 double smallest
= 0.0;
629 radius
= (- radius
* smallest
);
632 wxCoord x2
= (x
+width
);
633 wxCoord y2
= (y
+height
);
635 // Windows draws the filled rectangles without outline (i.e. drawn with a
636 // transparent pen) one pixel smaller in both directions and we want them
637 // to have the same size regardless of which pen is used - adjust
638 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
644 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
645 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
647 CalcBoundingBox(x
, y
);
648 CalcBoundingBox(x2
, y2
);
651 void wxDC::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
653 wxCoord x2
= (x
+width
);
654 wxCoord y2
= (y
+height
);
656 (void)Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
658 CalcBoundingBox(x
, y
);
659 CalcBoundingBox(x2
, y2
);
662 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
663 void wxDC::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
668 int rx1
= XLOG2DEV(x
+w
/2);
669 int ry1
= YLOG2DEV(y
+h
/2);
676 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
677 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
678 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
679 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
681 // draw pie with NULL_PEN first and then outline otherwise a line is
682 // drawn from the start and end points to the centre
683 HPEN orig_pen
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
686 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
691 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
692 rx1
, ry1
-1, rx2
, ry2
-1);
694 ::SelectObject(GetHdc(), orig_pen
);
695 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
698 CalcBoundingBox(x
, y
);
699 CalcBoundingBox(x2
, y2
);
702 void wxDC::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
704 wxCHECK_RET( icon
.Ok(), wxT("invalid icon in DrawIcon") );
706 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
708 CalcBoundingBox(x
, y
);
709 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
712 void wxDC::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
714 wxCHECK_RET( bmp
.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
716 int width
= bmp
.GetWidth(),
717 height
= bmp
.GetHeight();
719 HBITMAP hbmpMask
= 0;
723 wxMask
*mask
= bmp
.GetMask();
725 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
729 // don't give assert here because this would break existing
730 // programs - just silently ignore useMask parameter
738 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
739 ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
741 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
743 bool ok
= ::MaskBlt(GetHdc(), x
, y
, width
, height
,
746 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
752 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
755 memDC
.SelectObject(bmp
);
757 Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
759 memDC
.SelectObject(wxNullBitmap
);
762 else // no mask, just use BitBlt()
765 HDC memdc
= ::CreateCompatibleDC( cdc
);
766 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
768 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
770 COLORREF old_textground
= ::GetTextColor(GetHdc());
771 COLORREF old_background
= ::GetBkColor(GetHdc());
772 if (m_textForegroundColour
.Ok())
774 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
776 if (m_textBackgroundColour
.Ok())
778 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
781 ::SelectObject( memdc
, hbitmap
);
782 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
785 ::SetTextColor(GetHdc(), old_textground
);
786 ::SetBkColor(GetHdc(), old_background
);
790 void wxDC::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
792 DrawAnyText(text
, x
, y
);
794 // update the bounding box
795 CalcBoundingBox(x
, y
);
798 GetTextExtent(text
, &w
, &h
);
799 CalcBoundingBox(x
+ w
, y
+ h
);
802 void wxDC::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
804 // prepare for drawing the text
805 if ( m_textForegroundColour
.Ok() )
806 SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel());
808 DWORD old_background
= 0;
809 if ( m_textBackgroundColour
.Ok() )
811 old_background
= SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
814 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
817 if ( ::TextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
818 text
.c_str(), text
.length()) == 0 )
820 wxLogLastError("TextOut");
823 // restore the old parameters (text foreground colour may be left because
824 // it never is set to anything else, but background should remain
825 // transparent even if we just drew an opaque string)
826 if ( m_textBackgroundColour
.Ok() )
827 (void)SetBkColor(GetHdc(), old_background
);
829 SetBkMode(GetHdc(), TRANSPARENT
);
832 void wxDC::DoDrawRotatedText(const wxString
& text
,
833 wxCoord x
, wxCoord y
,
836 // we test that we have some font because otherwise we should still use the
837 // "else" part below to avoid that DrawRotatedText(angle = 180) and
838 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
839 // font for drawing rotated fonts unfortunately)
840 if ( (angle
== 0.0) && m_font
.Ok() )
842 DoDrawText(text
, x
, y
);
846 // NB: don't take DEFAULT_GUI_FONT because it's not TrueType and so
847 // can't have non zero orientation/escapement
848 wxFont font
= m_font
.Ok() ? m_font
: *wxNORMAL_FONT
;
849 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
851 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
853 wxLogLastError("GetObject(hfont)");
856 // GDI wants the angle in tenth of degree
857 long angle10
= (long)(angle
* 10);
858 lf
.lfEscapement
= angle10
;
859 lf
. lfOrientation
= angle10
;
861 hfont
= ::CreateFontIndirect(&lf
);
864 wxLogLastError("CreateFont");
868 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
870 DrawAnyText(text
, x
, y
);
872 (void)::SelectObject(GetHdc(), hfontOld
);
873 (void)::DeleteObject(hfont
);
876 // call the bounding box by adding all four vertices of the rectangle
877 // containing the text to it (simpler and probably not slower than
878 // determining which of them is really topmost/leftmost/...)
880 GetTextExtent(text
, &w
, &h
);
882 double rad
= DegToRad(angle
);
884 // "upper left" and "upper right"
885 CalcBoundingBox(x
, y
);
886 CalcBoundingBox(x
+ w
*cos(rad
), y
- h
*sin(rad
));
888 // "bottom left" and "bottom right"
889 x
+= (wxCoord
)(h
*sin(rad
));
890 y
+= (wxCoord
)(h
*cos(rad
));
891 CalcBoundingBox(x
, y
);
892 CalcBoundingBox(x
+ h
*sin(rad
), y
+ h
*cos(rad
));
896 // ---------------------------------------------------------------------------
898 // ---------------------------------------------------------------------------
900 void wxDC::SetPalette(const wxPalette
& palette
)
902 // Set the old object temporarily, in case the assignment deletes an object
903 // that's not yet selected out.
906 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, TRUE
);
914 // Setting a NULL colourmap is a way of restoring
915 // the original colourmap
918 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, TRUE
);
925 if (m_palette
.Ok() && m_palette
.GetHPALETTE())
927 HPALETTE oldPal
= ::SelectPalette(GetHdc(), (HPALETTE
) m_palette
.GetHPALETTE(), TRUE
);
929 m_oldPalette
= (WXHPALETTE
) oldPal
;
931 ::RealizePalette(GetHdc());
935 void wxDC::SetFont(const wxFont
& the_font
)
937 // Set the old object temporarily, in case the assignment deletes an object
938 // that's not yet selected out.
941 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
950 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
954 if (m_font
.Ok() && m_font
.GetResourceHandle())
956 HFONT f
= (HFONT
) ::SelectObject(GetHdc(), (HFONT
) m_font
.GetResourceHandle());
957 if (f
== (HFONT
) NULL
)
959 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
962 m_oldFont
= (WXHFONT
) f
;
966 void wxDC::SetPen(const wxPen
& pen
)
968 // Set the old object temporarily, in case the assignment deletes an object
969 // that's not yet selected out.
972 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
981 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
987 if (m_pen
.GetResourceHandle())
989 HPEN p
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
)m_pen
.GetResourceHandle());
991 m_oldPen
= (WXHPEN
) p
;
996 void wxDC::SetBrush(const wxBrush
& brush
)
998 // Set the old object temporarily, in case the assignment deletes an object
999 // that's not yet selected out.
1002 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
1011 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
1017 if (m_brush
.GetResourceHandle())
1020 b
= (HBRUSH
) ::SelectObject(GetHdc(), (HBRUSH
)m_brush
.GetResourceHandle());
1022 m_oldBrush
= (WXHBRUSH
) b
;
1027 void wxDC::SetBackground(const wxBrush
& brush
)
1029 m_backgroundBrush
= brush
;
1031 if (!m_backgroundBrush
.Ok())
1036 bool customColours
= TRUE
;
1037 // If we haven't specified wxUSER_COLOURS, don't allow the panel/dialog box to
1038 // change background colours from the control-panel specified colours.
1039 if (m_canvas
->IsKindOf(CLASSINFO(wxWindow
)) && ((m_canvas
->GetWindowStyleFlag() & wxUSER_COLOURS
) != wxUSER_COLOURS
))
1040 customColours
= FALSE
;
1044 if (m_backgroundBrush
.GetStyle()==wxTRANSPARENT
)
1046 m_canvas
->SetTransparent(TRUE
);
1050 // New behaviour, 10/2/99: setting the background brush of a DC
1051 // doesn't affect the window background colour. However,
1052 // I'm leaving in the transparency setting because it's needed by
1053 // various controls (e.g. wxStaticText) to determine whether to draw
1054 // transparently or not. TODO: maybe this should be a new function
1055 // wxWindow::SetTransparency(). Should that apply to the child itself, or the
1057 // m_canvas->SetBackgroundColour(m_backgroundBrush.GetColour());
1058 m_canvas
->SetTransparent(FALSE
);
1062 COLORREF new_color
= m_backgroundBrush
.GetColour().GetPixel();
1064 (void)SetBkColor(GetHdc(), new_color
);
1068 void wxDC::SetBackgroundMode(int mode
)
1070 m_backgroundMode
= mode
;
1072 // SetBackgroundColour now only refers to text background
1073 // and m_backgroundMode is used there
1076 if (m_backgroundMode == wxTRANSPARENT)
1077 ::SetBkMode(GetHdc(), TRANSPARENT);
1079 ::SetBkMode(GetHdc(), OPAQUE);
1083 void wxDC::SetLogicalFunction(int function
)
1085 m_logicalFunction
= function
;
1090 void wxDC::SetRop(WXHDC dc
)
1092 if ( !dc
|| m_logicalFunction
< 0 )
1097 switch (m_logicalFunction
)
1099 case wxCLEAR
: rop
= R2_BLACK
; break;
1100 case wxXOR
: rop
= R2_XORPEN
; break;
1101 case wxINVERT
: rop
= R2_NOT
; break;
1102 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1103 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1104 case wxCOPY
: rop
= R2_COPYPEN
; break;
1105 case wxAND
: rop
= R2_MASKPEN
; break;
1106 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1107 case wxNO_OP
: rop
= R2_NOP
; break;
1108 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1109 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1110 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1111 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1112 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1113 case wxOR
: rop
= R2_MERGEPEN
; break;
1114 case wxSET
: rop
= R2_WHITE
; break;
1117 wxFAIL_MSG( wxT("unsupported logical function") );
1121 SetROP2(GetHdc(), rop
);
1124 bool wxDC::StartDoc(const wxString
& message
)
1126 // We might be previewing, so return TRUE to let it continue.
1134 void wxDC::StartPage()
1138 void wxDC::EndPage()
1142 // ---------------------------------------------------------------------------
1144 // ---------------------------------------------------------------------------
1146 wxCoord
wxDC::GetCharHeight() const
1148 TEXTMETRIC lpTextMetric
;
1150 GetTextMetrics(GetHdc(), &lpTextMetric
);
1152 return YDEV2LOGREL(lpTextMetric
.tmHeight
);
1155 wxCoord
wxDC::GetCharWidth() const
1157 TEXTMETRIC lpTextMetric
;
1159 GetTextMetrics(GetHdc(), &lpTextMetric
);
1161 return XDEV2LOGREL(lpTextMetric
.tmAveCharWidth
);
1164 void wxDC::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1165 wxCoord
*descent
, wxCoord
*externalLeading
,
1166 wxFont
*theFont
) const
1168 wxFont
*fontToUse
= (wxFont
*) theFont
;
1170 fontToUse
= (wxFont
*) &m_font
;
1175 GetTextExtentPoint(GetHdc(), WXSTRINGCAST string
, wxStrlen(WXSTRINGCAST string
), &sizeRect
);
1176 GetTextMetrics(GetHdc(), &tm
);
1178 if (x
) *x
= XDEV2LOGREL(sizeRect
.cx
);
1179 if (y
) *y
= YDEV2LOGREL(sizeRect
.cy
);
1180 if (descent
) *descent
= tm
.tmDescent
;
1181 if (externalLeading
) *externalLeading
= tm
.tmExternalLeading
;
1184 void wxDC::SetMapMode(int mode
)
1186 m_mappingMode
= mode
;
1188 int pixel_width
= 0;
1189 int pixel_height
= 0;
1193 pixel_width
= GetDeviceCaps(GetHdc(), HORZRES
);
1194 pixel_height
= GetDeviceCaps(GetHdc(), VERTRES
);
1195 mm_width
= GetDeviceCaps(GetHdc(), HORZSIZE
);
1196 mm_height
= GetDeviceCaps(GetHdc(), VERTSIZE
);
1198 if ((pixel_width
== 0) || (pixel_height
== 0) || (mm_width
== 0) || (mm_height
== 0))
1203 double mm2pixelsX
= pixel_width
/mm_width
;
1204 double mm2pixelsY
= pixel_height
/mm_height
;
1210 m_logicalScaleX
= (twips2mm
* mm2pixelsX
);
1211 m_logicalScaleY
= (twips2mm
* mm2pixelsY
);
1216 m_logicalScaleX
= (pt2mm
* mm2pixelsX
);
1217 m_logicalScaleY
= (pt2mm
* mm2pixelsY
);
1222 m_logicalScaleX
= mm2pixelsX
;
1223 m_logicalScaleY
= mm2pixelsY
;
1228 m_logicalScaleX
= (mm2pixelsX
/10.0);
1229 m_logicalScaleY
= (mm2pixelsY
/10.0);
1235 m_logicalScaleX
= 1.0;
1236 m_logicalScaleY
= 1.0;
1241 if (::GetMapMode(GetHdc()) != MM_ANISOTROPIC
)
1242 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1244 SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1245 m_windowExtX
= (int)MS_XDEV2LOGREL(VIEWPORT_EXTENT
);
1246 m_windowExtY
= (int)MS_YDEV2LOGREL(VIEWPORT_EXTENT
);
1247 ::SetWindowExtEx(GetHdc(), m_windowExtX
, m_windowExtY
, NULL
);
1248 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1249 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1252 void wxDC::SetUserScale(double x
, double y
)
1257 SetMapMode(m_mappingMode
);
1260 void wxDC::SetAxisOrientation(bool xLeftRight
, bool yBottomUp
)
1262 m_signX
= xLeftRight
? 1 : -1;
1263 m_signY
= yBottomUp
? -1 : 1;
1265 SetMapMode(m_mappingMode
);
1268 void wxDC::SetSystemScale(double x
, double y
)
1273 SetMapMode(m_mappingMode
);
1276 void wxDC::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1278 m_logicalOriginX
= x
;
1279 m_logicalOriginY
= y
;
1281 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1284 void wxDC::SetDeviceOrigin(wxCoord x
, wxCoord y
)
1286 m_deviceOriginX
= x
;
1287 m_deviceOriginY
= y
;
1289 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1292 // ---------------------------------------------------------------------------
1293 // coordinates transformations
1294 // ---------------------------------------------------------------------------
1296 wxCoord
wxDCBase::DeviceToLogicalX(wxCoord x
) const
1298 return (wxCoord
) (((x
) - m_deviceOriginX
)/(m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
) - m_logicalOriginX
);
1301 wxCoord
wxDCBase::DeviceToLogicalXRel(wxCoord x
) const
1303 return (wxCoord
) ((x
)/(m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
));
1306 wxCoord
wxDCBase::DeviceToLogicalY(wxCoord y
) const
1308 return (wxCoord
) (((y
) - m_deviceOriginY
)/(m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
) - m_logicalOriginY
);
1311 wxCoord
wxDCBase::DeviceToLogicalYRel(wxCoord y
) const
1313 return (wxCoord
) ((y
)/(m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
));
1316 wxCoord
wxDCBase::LogicalToDeviceX(wxCoord x
) const
1318 return (wxCoord
) ((x
- m_logicalOriginX
)*m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
+ m_deviceOriginX
);
1321 wxCoord
wxDCBase::LogicalToDeviceXRel(wxCoord x
) const
1323 return (wxCoord
) (x
*m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
);
1326 wxCoord
wxDCBase::LogicalToDeviceY(wxCoord y
) const
1328 return (wxCoord
) ((y
- m_logicalOriginY
)*m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
+ m_deviceOriginY
);
1331 wxCoord
wxDCBase::LogicalToDeviceYRel(wxCoord y
) const
1333 return (wxCoord
) (y
*m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
);
1336 // ---------------------------------------------------------------------------
1338 // ---------------------------------------------------------------------------
1340 bool wxDC::DoBlit(wxCoord xdest
, wxCoord ydest
,
1341 wxCoord width
, wxCoord height
,
1342 wxDC
*source
, wxCoord xsrc
, wxCoord ysrc
,
1343 int rop
, bool useMask
)
1345 wxMask
*mask
= NULL
;
1348 const wxBitmap
& bmp
= source
->m_selectedBitmap
;
1349 mask
= bmp
.GetMask();
1351 if ( !(bmp
.Ok() && mask
&& mask
->GetMaskBitmap()) )
1353 // don't give assert here because this would break existing
1354 // programs - just silently ignore useMask parameter
1359 COLORREF old_textground
= ::GetTextColor(GetHdc());
1360 COLORREF old_background
= ::GetBkColor(GetHdc());
1361 if (m_textForegroundColour
.Ok())
1363 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1365 if (m_textBackgroundColour
.Ok())
1367 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1370 DWORD dwRop
= SRCCOPY
;
1373 case wxXOR
: dwRop
= SRCINVERT
; break;
1374 case wxINVERT
: dwRop
= DSTINVERT
; break;
1375 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
1376 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
1377 case wxCLEAR
: dwRop
= BLACKNESS
; break;
1378 case wxSET
: dwRop
= WHITENESS
; break;
1379 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
1380 case wxAND
: dwRop
= SRCAND
; break;
1381 case wxOR
: dwRop
= SRCPAINT
; break;
1382 case wxEQUIV
: dwRop
= 0x00990066; break;
1383 case wxNAND
: dwRop
= 0x007700E6; break;
1384 case wxAND_INVERT
: dwRop
= 0x00220326; break;
1385 case wxCOPY
: dwRop
= SRCCOPY
; break;
1386 case wxNO_OP
: dwRop
= DSTCOPY
; break;
1387 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
1388 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
1390 wxFAIL_MSG( wxT("unsupported logical function") );
1399 // we want the part of the image corresponding to the mask to be
1400 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1401 // meaning of fg and bg is inverted which corresponds to wxWin notion
1402 // of the mask which is also contrary to the Windows one)
1403 success
= ::MaskBlt(GetHdc(), xdest
, ydest
, width
, height
,
1404 GetHdcOf(*source
), xsrc
, ysrc
,
1405 (HBITMAP
)mask
->GetMaskBitmap(), 0, 0,
1406 MAKEROP4(dwRop
, DSTCOPY
)) != 0;
1411 // Blit bitmap with mask
1413 // create a temp buffer bitmap and DCs to access it and the mask
1414 HDC dc_mask
= ::CreateCompatibleDC(GetHdcOf(*source
));
1415 HDC dc_buffer
= ::CreateCompatibleDC(GetHdc());
1416 HBITMAP buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), width
, height
);
1417 ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
1418 ::SelectObject(dc_buffer
, buffer_bmap
);
1420 // copy dest to buffer
1421 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1422 GetHdc(), xdest
, ydest
, SRCCOPY
) )
1424 wxLogLastError("BitBlt");
1427 // copy src to buffer using selected raster op
1428 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1429 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) )
1431 wxLogLastError("BitBlt");
1434 // set masked area in buffer to BLACK (pixel value 0)
1435 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1436 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1437 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1438 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1440 wxLogLastError("BitBlt");
1443 // set unmasked area in dest to BLACK
1444 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1445 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1446 if ( !::BitBlt(GetHdc(), xdest
, ydest
, (int)width
, (int)height
,
1447 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1449 wxLogLastError("BitBlt");
1451 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
1452 ::SetTextColor(GetHdc(), prevCol
);
1454 // OR buffer to dest
1455 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1456 (int)width
, (int)height
,
1457 dc_buffer
, 0, 0, SRCPAINT
) != 0;
1460 wxLogLastError("BitBlt");
1463 // tidy up temporary DCs and bitmap
1464 ::SelectObject(dc_mask
, 0);
1465 ::DeleteDC(dc_mask
);
1466 ::SelectObject(dc_buffer
, 0);
1467 ::DeleteDC(dc_buffer
);
1468 ::DeleteObject(buffer_bmap
);
1471 else // no mask, just BitBlt() it
1473 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1474 (int)width
, (int)height
,
1475 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) != 0;
1478 wxLogLastError("BitBlt");
1481 ::SetTextColor(GetHdc(), old_textground
);
1482 ::SetBkColor(GetHdc(), old_background
);
1487 void wxDC::DoGetSize(int *w
, int *h
) const
1489 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZRES
);
1490 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTRES
);
1493 void wxDC::DoGetSizeMM(int *w
, int *h
) const
1495 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZSIZE
);
1496 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1499 wxSize
wxDC::GetPPI() const
1501 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
1502 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
1504 return wxSize(x
, y
);
1507 // For use by wxWindows only, unless custom units are required.
1508 void wxDC::SetLogicalScale(double x
, double y
)
1510 m_logicalScaleX
= x
;
1511 m_logicalScaleY
= y
;
1514 #if WXWIN_COMPATIBILITY
1515 void wxDC::DoGetTextExtent(const wxString
& string
, float *x
, float *y
,
1516 float *descent
, float *externalLeading
,
1517 wxFont
*theFont
, bool use16bit
) const
1519 wxCoord x1
, y1
, descent1
, externalLeading1
;
1520 GetTextExtent(string
, & x1
, & y1
, & descent1
, & externalLeading1
, theFont
, use16bit
);
1523 *descent
= descent1
;
1524 if (externalLeading
)
1525 *externalLeading
= externalLeading1
;
1529 // ---------------------------------------------------------------------------
1530 // spline drawing code
1531 // ---------------------------------------------------------------------------
1535 class wxSpline
: public wxObject
1541 wxSpline(wxList
*list
);
1542 void DeletePoints();
1544 // Doesn't delete points
1548 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
);
1550 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
,
1551 double a3
, double b3
, double a4
, double b4
);
1552 void wx_clear_stack();
1553 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
, double *x3
,
1554 double *y3
, double *x4
, double *y4
);
1555 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
,
1556 double x4
, double y4
);
1557 static bool wx_spline_add_point(double x
, double y
);
1558 static void wx_spline_draw_point_array(wxDC
*dc
);
1559 wxSpline
*wx_make_spline(int x1
, int y1
, int x2
, int y2
, int x3
, int y3
);
1561 void wxDC::DoDrawSpline(wxList
*list
)
1563 wxSpline
spline(list
);
1565 wx_draw_open_spline(this, &spline
);
1568 wxList wx_spline_point_list
;
1570 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
)
1573 double cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
;
1574 double x1
, y1
, x2
, y2
;
1576 wxNode
*node
= spline
->points
->First();
1577 p
= (wxPoint
*)node
->Data();
1582 node
= node
->Next();
1583 p
= (wxPoint
*)node
->Data();
1587 cx1
= (double)((x1
+ x2
) / 2);
1588 cy1
= (double)((y1
+ y2
) / 2);
1589 cx2
= (double)((cx1
+ x2
) / 2);
1590 cy2
= (double)((cy1
+ y2
) / 2);
1592 wx_spline_add_point(x1
, y1
);
1594 while ((node
= node
->Next()) != NULL
)
1596 p
= (wxPoint
*)node
->Data();
1601 cx4
= (double)(x1
+ x2
) / 2;
1602 cy4
= (double)(y1
+ y2
) / 2;
1603 cx3
= (double)(x1
+ cx4
) / 2;
1604 cy3
= (double)(y1
+ cy4
) / 2;
1606 wx_quadratic_spline(cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
);
1610 cx2
= (double)(cx1
+ x2
) / 2;
1611 cy2
= (double)(cy1
+ y2
) / 2;
1614 wx_spline_add_point((double)wx_round(cx1
), (double)wx_round(cy1
));
1615 wx_spline_add_point(x2
, y2
);
1617 wx_spline_draw_point_array(dc
);
1621 /********************* CURVES FOR SPLINES *****************************
1623 The following spline drawing routine is from
1625 "An Algorithm for High-Speed Curve Generation"
1626 by George Merrill Chaikin,
1627 Computer Graphics and Image Processing, 3, Academic Press,
1632 "On Chaikin's Algorithm" by R. F. Riesenfeld,
1633 Computer Graphics and Image Processing, 4, Academic Press,
1636 ***********************************************************************/
1638 #define half(z1, z2) ((z1+z2)/2.0)
1641 /* iterative version */
1643 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
, double a3
, double b3
, double a4
,
1646 register double xmid
, ymid
;
1647 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1650 wx_spline_push(a1
, b1
, a2
, b2
, a3
, b3
, a4
, b4
);
1652 while (wx_spline_pop(&x1
, &y1
, &x2
, &y2
, &x3
, &y3
, &x4
, &y4
)) {
1653 xmid
= (double)half(x2
, x3
);
1654 ymid
= (double)half(y2
, y3
);
1655 if (fabs(x1
- xmid
) < THRESHOLD
&& fabs(y1
- ymid
) < THRESHOLD
&&
1656 fabs(xmid
- x4
) < THRESHOLD
&& fabs(ymid
- y4
) < THRESHOLD
) {
1657 wx_spline_add_point((double)wx_round(x1
), (double)wx_round(y1
));
1658 wx_spline_add_point((double)wx_round(xmid
), (double)wx_round(ymid
));
1660 wx_spline_push(xmid
, ymid
, (double)half(xmid
, x3
), (double)half(ymid
, y3
),
1661 (double)half(x3
, x4
), (double)half(y3
, y4
), x4
, y4
);
1662 wx_spline_push(x1
, y1
, (double)half(x1
, x2
), (double)half(y1
, y2
),
1663 (double)half(x2
, xmid
), (double)half(y2
, ymid
), xmid
, ymid
);
1669 /* utilities used by spline drawing routines */
1672 typedef struct wx_spline_stack_struct
{
1673 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1677 #define SPLINE_STACK_DEPTH 20
1678 static Stack wx_spline_stack
[SPLINE_STACK_DEPTH
];
1679 static Stack
*wx_stack_top
;
1680 static int wx_stack_count
;
1682 void wx_clear_stack()
1684 wx_stack_top
= wx_spline_stack
;
1688 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
1690 wx_stack_top
->x1
= x1
;
1691 wx_stack_top
->y1
= y1
;
1692 wx_stack_top
->x2
= x2
;
1693 wx_stack_top
->y2
= y2
;
1694 wx_stack_top
->x3
= x3
;
1695 wx_stack_top
->y3
= y3
;
1696 wx_stack_top
->x4
= x4
;
1697 wx_stack_top
->y4
= y4
;
1702 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
,
1703 double *x3
, double *y3
, double *x4
, double *y4
)
1705 if (wx_stack_count
== 0)
1709 *x1
= wx_stack_top
->x1
;
1710 *y1
= wx_stack_top
->y1
;
1711 *x2
= wx_stack_top
->x2
;
1712 *y2
= wx_stack_top
->y2
;
1713 *x3
= wx_stack_top
->x3
;
1714 *y3
= wx_stack_top
->y3
;
1715 *x4
= wx_stack_top
->x4
;
1716 *y4
= wx_stack_top
->y4
;
1720 static bool wx_spline_add_point(double x
, double y
)
1722 wxPoint
*point
= new wxPoint
;
1725 wx_spline_point_list
.Append((wxObject
*)point
);
1729 static void wx_spline_draw_point_array(wxDC
*dc
)
1731 dc
->DrawLines(&wx_spline_point_list
, 0, 0);
1732 wxNode
*node
= wx_spline_point_list
.First();
1735 wxPoint
*point
= (wxPoint
*)node
->Data();
1738 node
= wx_spline_point_list
.First();
1742 wxSpline::wxSpline(wxList
*list
)
1747 wxSpline::~wxSpline()
1751 void wxSpline::DeletePoints()
1753 for(wxNode
*node
= points
->First(); node
; node
= points
->First())
1755 wxPoint
*point
= (wxPoint
*)node
->Data();
1763 #endif // wxUSE_SPLINES