]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dc.cpp
fixed 'patch' #422993 (Error in wxConfigBase::Write for doubles)
[wxWidgets.git] / src / msw / dc.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: dc.cpp
3 // Purpose: wxDC class
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01/02/97
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "dc.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #include "wx/window.h"
33 #include "wx/dc.h"
34 #include "wx/utils.h"
35 #include "wx/dialog.h"
36 #include "wx/app.h"
37 #include "wx/bitmap.h"
38 #include "wx/dcmemory.h"
39 #include "wx/log.h"
40 #include "wx/icon.h"
41 #endif
42
43 #include "wx/dcprint.h"
44
45 #include <string.h>
46 #include <math.h>
47
48 #include "wx/msw/private.h" // needs to be before #include <commdlg.h>
49
50 #if wxUSE_COMMON_DIALOGS
51 #include <commdlg.h>
52 #endif
53
54 #ifndef __WIN32__
55 #include <print.h>
56 #endif
57
58 IMPLEMENT_ABSTRACT_CLASS(wxDC, wxDCBase)
59
60 // ---------------------------------------------------------------------------
61 // constants
62 // ---------------------------------------------------------------------------
63
64 static const int VIEWPORT_EXTENT = 1000;
65
66 static const int MM_POINTS = 9;
67 static const int MM_METRIC = 10;
68
69 // usually this is defined in math.h
70 #ifndef M_PI
71 static const double M_PI = 3.14159265358979323846;
72 #endif // M_PI
73
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
77
78 // ---------------------------------------------------------------------------
79 // private functions
80 // ---------------------------------------------------------------------------
81
82 // convert degrees to radians
83 static inline double DegToRad(double deg) { return (deg * M_PI) / 180.0; }
84
85 // ----------------------------------------------------------------------------
86 // private classes
87 // ----------------------------------------------------------------------------
88
89 // instead of duplicating the same code which sets and then restores text
90 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
91 // encapsulate this in a small helper class
92
93 // wxColourChanger: changes the text colours in the ctor if required and
94 // restores them in the dtor
95 class wxColourChanger
96 {
97 public:
98 wxColourChanger(wxDC& dc);
99 ~wxColourChanger();
100
101 private:
102 wxDC& m_dc;
103
104 COLORREF m_colFgOld, m_colBgOld;
105
106 bool m_changed;
107 };
108
109 // ===========================================================================
110 // implementation
111 // ===========================================================================
112
113 // ----------------------------------------------------------------------------
114 // wxColourChanger
115 // ----------------------------------------------------------------------------
116
117 wxColourChanger::wxColourChanger(wxDC& dc) : m_dc(dc)
118 {
119 if ( dc.GetBrush().GetStyle() == wxSTIPPLE_MASK_OPAQUE )
120 {
121 HDC hdc = GetHdcOf(dc);
122 m_colFgOld = ::GetTextColor(hdc);
123 m_colBgOld = ::GetBkColor(hdc);
124
125 // note that Windows convention is opposite to wxWindows one, this is
126 // why text colour becomes the background one and vice versa
127 const wxColour& colFg = dc.GetTextForeground();
128 if ( colFg.Ok() )
129 {
130 ::SetBkColor(hdc, colFg.GetPixel());
131 }
132
133 const wxColour& colBg = dc.GetTextBackground();
134 if ( colBg.Ok() )
135 {
136 ::SetTextColor(hdc, colBg.GetPixel());
137 }
138
139 SetBkMode(hdc,
140 dc.GetBackgroundMode() == wxTRANSPARENT ? TRANSPARENT
141 : OPAQUE);
142
143 // flag which telsl us to undo changes in the dtor
144 m_changed = TRUE;
145 }
146 else
147 {
148 // nothing done, nothing to undo
149 m_changed = FALSE;
150 }
151 }
152
153 wxColourChanger::~wxColourChanger()
154 {
155 if ( m_changed )
156 {
157 // restore the colours we changed
158 HDC hdc = GetHdcOf(m_dc);
159
160 ::SetBkMode(hdc, TRANSPARENT);
161 ::SetTextColor(hdc, m_colFgOld);
162 ::SetBkColor(hdc, m_colBgOld);
163 }
164 }
165
166 // ---------------------------------------------------------------------------
167 // wxDC
168 // ---------------------------------------------------------------------------
169
170 // Default constructor
171 wxDC::wxDC()
172 {
173 m_canvas = NULL;
174
175 m_oldBitmap = 0;
176 m_oldPen = 0;
177 m_oldBrush = 0;
178 m_oldFont = 0;
179 m_oldPalette = 0;
180
181 m_bOwnsDC = FALSE;
182 m_hDC = 0;
183
184 m_windowExtX = VIEWPORT_EXTENT;
185 m_windowExtY = VIEWPORT_EXTENT;
186 }
187
188
189 wxDC::~wxDC()
190 {
191 if ( m_hDC != 0 )
192 {
193 SelectOldObjects(m_hDC);
194
195 // if we own the HDC, we delete it, otherwise we just release it
196
197 if ( m_bOwnsDC )
198 {
199 ::DeleteDC(GetHdc());
200 }
201 else // we don't own our HDC
202 {
203 if (m_canvas)
204 {
205 ::ReleaseDC(GetHwndOf(m_canvas), GetHdc());
206 }
207 else
208 {
209 // Must have been a wxScreenDC
210 ::ReleaseDC((HWND) NULL, GetHdc());
211 }
212 }
213 }
214 }
215
216 // This will select current objects out of the DC,
217 // which is what you have to do before deleting the
218 // DC.
219 void wxDC::SelectOldObjects(WXHDC dc)
220 {
221 if (dc)
222 {
223 if (m_oldBitmap)
224 {
225 ::SelectObject((HDC) dc, (HBITMAP) m_oldBitmap);
226 if (m_selectedBitmap.Ok())
227 {
228 m_selectedBitmap.SetSelectedInto(NULL);
229 }
230 }
231 m_oldBitmap = 0;
232 if (m_oldPen)
233 {
234 ::SelectObject((HDC) dc, (HPEN) m_oldPen);
235 }
236 m_oldPen = 0;
237 if (m_oldBrush)
238 {
239 ::SelectObject((HDC) dc, (HBRUSH) m_oldBrush);
240 }
241 m_oldBrush = 0;
242 if (m_oldFont)
243 {
244 ::SelectObject((HDC) dc, (HFONT) m_oldFont);
245 }
246 m_oldFont = 0;
247 if (m_oldPalette)
248 {
249 ::SelectPalette((HDC) dc, (HPALETTE) m_oldPalette, TRUE);
250 }
251 m_oldPalette = 0;
252 }
253
254 m_brush = wxNullBrush;
255 m_pen = wxNullPen;
256 m_palette = wxNullPalette;
257 m_font = wxNullFont;
258 m_backgroundBrush = wxNullBrush;
259 m_selectedBitmap = wxNullBitmap;
260 }
261
262 // ---------------------------------------------------------------------------
263 // clipping
264 // ---------------------------------------------------------------------------
265
266 #define DO_SET_CLIPPING_BOX() \
267 { \
268 RECT rect; \
269 \
270 GetClipBox(GetHdc(), &rect); \
271 \
272 m_clipX1 = (wxCoord) XDEV2LOG(rect.left); \
273 m_clipY1 = (wxCoord) YDEV2LOG(rect.top); \
274 m_clipX2 = (wxCoord) XDEV2LOG(rect.right); \
275 m_clipY2 = (wxCoord) YDEV2LOG(rect.bottom); \
276 }
277
278 void wxDC::DoSetClippingRegion(wxCoord cx, wxCoord cy, wxCoord cw, wxCoord ch)
279 {
280 m_clipping = TRUE;
281 IntersectClipRect(GetHdc(), XLOG2DEV(cx), YLOG2DEV(cy),
282 XLOG2DEV(cx + cw), YLOG2DEV(cy + ch));
283 DO_SET_CLIPPING_BOX()
284 }
285
286 void wxDC::DoSetClippingRegionAsRegion(const wxRegion& region)
287 {
288 wxCHECK_RET( region.GetHRGN(), wxT("invalid clipping region") );
289
290 m_clipping = TRUE;
291
292 #ifdef __WIN16__
293 SelectClipRgn(GetHdc(), (HRGN) region.GetHRGN());
294 #else
295 ExtSelectClipRgn(GetHdc(), (HRGN) region.GetHRGN(), RGN_AND);
296 #endif
297
298 DO_SET_CLIPPING_BOX()
299 }
300
301 void wxDC::DestroyClippingRegion()
302 {
303 if (m_clipping && m_hDC)
304 {
305 // TODO: this should restore the previous clipping region,
306 // so that OnPaint processing works correctly, and the update clipping region
307 // doesn't get destroyed after the first DestroyClippingRegion.
308 HRGN rgn = CreateRectRgn(0, 0, 32000, 32000);
309 SelectClipRgn(GetHdc(), rgn);
310 DeleteObject(rgn);
311 }
312 m_clipping = FALSE;
313 }
314
315 // ---------------------------------------------------------------------------
316 // query capabilities
317 // ---------------------------------------------------------------------------
318
319 bool wxDC::CanDrawBitmap() const
320 {
321 return TRUE;
322 }
323
324 bool wxDC::CanGetTextExtent() const
325 {
326 // What sort of display is it?
327 int technology = ::GetDeviceCaps(GetHdc(), TECHNOLOGY);
328
329 return (technology == DT_RASDISPLAY) || (technology == DT_RASPRINTER);
330 }
331
332 int wxDC::GetDepth() const
333 {
334 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL);
335 }
336
337 // ---------------------------------------------------------------------------
338 // drawing
339 // ---------------------------------------------------------------------------
340
341 void wxDC::Clear()
342 {
343 RECT rect;
344 if ( m_canvas )
345 {
346 GetClientRect((HWND) m_canvas->GetHWND(), &rect);
347 }
348 else
349 {
350 // No, I think we should simply ignore this if printing on e.g.
351 // a printer DC.
352 // wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
353 if (!m_selectedBitmap.Ok())
354 return;
355
356 rect.left = 0; rect.top = 0;
357 rect.right = m_selectedBitmap.GetWidth();
358 rect.bottom = m_selectedBitmap.GetHeight();
359 }
360
361 (void) ::SetMapMode(GetHdc(), MM_TEXT);
362
363 DWORD colour = GetBkColor(GetHdc());
364 HBRUSH brush = CreateSolidBrush(colour);
365 FillRect(GetHdc(), &rect, brush);
366 DeleteObject(brush);
367
368 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
369 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
370 ::SetWindowExtEx(GetHdc(), m_windowExtX, m_windowExtY, NULL);
371 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
372 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
373 }
374
375 void wxDC::DoFloodFill(wxCoord x, wxCoord y, const wxColour& col, int style)
376 {
377 if ( !::ExtFloodFill(GetHdc(), XLOG2DEV(x), YLOG2DEV(y),
378 col.GetPixel(),
379 style == wxFLOOD_SURFACE ? FLOODFILLSURFACE
380 : FLOODFILLBORDER) )
381 {
382 // quoting from the MSDN docs:
383 //
384 // Following are some of the reasons this function might fail:
385 //
386 // * The filling could not be completed.
387 // * The specified point has the boundary color specified by the
388 // crColor parameter (if FLOODFILLBORDER was requested).
389 // * The specified point does not have the color specified by
390 // crColor (if FLOODFILLSURFACE was requested)
391 // * The point is outside the clipping region that is, it is not
392 // visible on the device.
393 //
394 wxLogLastError(wxT("ExtFloodFill"));
395 }
396
397 CalcBoundingBox(x, y);
398 }
399
400 bool wxDC::DoGetPixel(wxCoord x, wxCoord y, wxColour *col) const
401 {
402 wxCHECK_MSG( col, FALSE, _T("NULL colour parameter in wxDC::GetPixel") );
403
404 // get the color of the pixel
405 COLORREF pixelcolor = ::GetPixel(GetHdc(), XLOG2DEV(x), YLOG2DEV(y));
406
407 wxRGBToColour(*col, pixelcolor);
408
409 return TRUE;
410 }
411
412 void wxDC::DoCrossHair(wxCoord x, wxCoord y)
413 {
414 wxCoord x1 = x-VIEWPORT_EXTENT;
415 wxCoord y1 = y-VIEWPORT_EXTENT;
416 wxCoord x2 = x+VIEWPORT_EXTENT;
417 wxCoord y2 = y+VIEWPORT_EXTENT;
418
419 (void)MoveToEx(GetHdc(), XLOG2DEV(x1), YLOG2DEV(y), NULL);
420 (void)LineTo(GetHdc(), XLOG2DEV(x2), YLOG2DEV(y));
421
422 (void)MoveToEx(GetHdc(), XLOG2DEV(x), YLOG2DEV(y1), NULL);
423 (void)LineTo(GetHdc(), XLOG2DEV(x), YLOG2DEV(y2));
424
425 CalcBoundingBox(x1, y1);
426 CalcBoundingBox(x2, y2);
427 }
428
429 void wxDC::DoDrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
430 {
431 (void)MoveToEx(GetHdc(), XLOG2DEV(x1), YLOG2DEV(y1), NULL);
432 (void)LineTo(GetHdc(), XLOG2DEV(x2), YLOG2DEV(y2));
433
434 // Normalization: Windows doesn't draw the last point of the line.
435 // But apparently neither does GTK+, so we take it out again.
436 // (void)LineTo(GetHdc(), XLOG2DEV(x2) + 1, YLOG2DEV(y2));
437
438 CalcBoundingBox(x1, y1);
439 CalcBoundingBox(x2, y2);
440 }
441
442 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
443 // and ending at (x2, y2)
444 void wxDC::DoDrawArc(wxCoord x1, wxCoord y1,
445 wxCoord x2, wxCoord y2,
446 wxCoord xc, wxCoord yc)
447 {
448 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
449
450 double dx = xc - x1;
451 double dy = yc - y1;
452 double radius = (double)sqrt(dx*dx+dy*dy);
453 wxCoord r = (wxCoord)radius;
454
455 // treat the special case of full circle separately
456 if ( x1 == x2 && y1 == y2 )
457 {
458 DrawEllipse(xc - r, yc - r, 2*r, 2*r);
459 return;
460 }
461
462 wxCoord xx1 = XLOG2DEV(x1);
463 wxCoord yy1 = YLOG2DEV(y1);
464 wxCoord xx2 = XLOG2DEV(x2);
465 wxCoord yy2 = YLOG2DEV(y2);
466 wxCoord xxc = XLOG2DEV(xc);
467 wxCoord yyc = YLOG2DEV(yc);
468 wxCoord ray = (wxCoord) sqrt(double((xxc-xx1)*(xxc-xx1)+(yyc-yy1)*(yyc-yy1)));
469
470 wxCoord xxx1 = (wxCoord) (xxc-ray);
471 wxCoord yyy1 = (wxCoord) (yyc-ray);
472 wxCoord xxx2 = (wxCoord) (xxc+ray);
473 wxCoord yyy2 = (wxCoord) (yyc+ray);
474
475 if ( m_brush.Ok() && m_brush.GetStyle() != wxTRANSPARENT )
476 {
477 // Have to add 1 to bottom-right corner of rectangle
478 // to make semi-circles look right (crooked line otherwise).
479 // Unfortunately this is not a reliable method, depends
480 // on the size of shape.
481 // TODO: figure out why this happens!
482 Pie(GetHdc(),xxx1,yyy1,xxx2+1,yyy2+1, xx1,yy1,xx2,yy2);
483 }
484 else
485 {
486 Arc(GetHdc(),xxx1,yyy1,xxx2,yyy2, xx1,yy1,xx2,yy2);
487 }
488
489 CalcBoundingBox(xc - r, yc - r);
490 CalcBoundingBox(xc + r, yc + r);
491 }
492
493 void wxDC::DoDrawCheckMark(wxCoord x1, wxCoord y1,
494 wxCoord width, wxCoord height)
495 {
496 wxCoord x2 = x1 + width,
497 y2 = y1 + height;
498
499 #if defined(__WIN32__) && !defined(__SC__)
500 RECT rect;
501 rect.left = x1;
502 rect.top = y1;
503 rect.right = x2;
504 rect.bottom = y2;
505
506 DrawFrameControl(GetHdc(), &rect, DFC_MENU, DFCS_MENUCHECK);
507 #else // Win16
508 // In WIN16, draw a cross
509 HPEN blackPen = ::CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
510 HPEN whiteBrush = (HPEN)::GetStockObject(WHITE_BRUSH);
511 HPEN hPenOld = (HPEN)::SelectObject(GetHdc(), blackPen);
512 HPEN hBrushOld = (HPEN)::SelectObject(GetHdc(), whiteBrush);
513 ::SetROP2(GetHdc(), R2_COPYPEN);
514 Rectangle(GetHdc(), x1, y1, x2, y2);
515 MoveTo(GetHdc(), x1, y1);
516 LineTo(GetHdc(), x2, y2);
517 MoveTo(GetHdc(), x2, y1);
518 LineTo(GetHdc(), x1, y2);
519 ::SelectObject(GetHdc(), hPenOld);
520 ::SelectObject(GetHdc(), hBrushOld);
521 ::DeleteObject(blackPen);
522 #endif // Win32/16
523
524 CalcBoundingBox(x1, y1);
525 CalcBoundingBox(x2, y2);
526 }
527
528 void wxDC::DoDrawPoint(wxCoord x, wxCoord y)
529 {
530 COLORREF color = 0x00ffffff;
531 if (m_pen.Ok())
532 {
533 color = m_pen.GetColour().GetPixel();
534 }
535
536 SetPixel(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), color);
537
538 CalcBoundingBox(x, y);
539 }
540
541 void wxDC::DoDrawPolygon(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset,int fillStyle)
542 {
543 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
544
545 // Do things less efficiently if we have offsets
546 if (xoffset != 0 || yoffset != 0)
547 {
548 POINT *cpoints = new POINT[n];
549 int i;
550 for (i = 0; i < n; i++)
551 {
552 cpoints[i].x = (int)(points[i].x + xoffset);
553 cpoints[i].y = (int)(points[i].y + yoffset);
554
555 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
556 }
557 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
558 (void)Polygon(GetHdc(), cpoints, n);
559 SetPolyFillMode(GetHdc(),prev);
560 delete[] cpoints;
561 }
562 else
563 {
564 int i;
565 for (i = 0; i < n; i++)
566 CalcBoundingBox(points[i].x, points[i].y);
567
568 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
569 (void)Polygon(GetHdc(), (POINT*) points, n);
570 SetPolyFillMode(GetHdc(),prev);
571 }
572 }
573
574 void wxDC::DoDrawLines(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset)
575 {
576 // Do things less efficiently if we have offsets
577 if (xoffset != 0 || yoffset != 0)
578 {
579 POINT *cpoints = new POINT[n];
580 int i;
581 for (i = 0; i < n; i++)
582 {
583 cpoints[i].x = (int)(points[i].x + xoffset);
584 cpoints[i].y = (int)(points[i].y + yoffset);
585
586 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
587 }
588 (void)Polyline(GetHdc(), cpoints, n);
589 delete[] cpoints;
590 }
591 else
592 {
593 int i;
594 for (i = 0; i < n; i++)
595 CalcBoundingBox(points[i].x, points[i].y);
596
597 (void)Polyline(GetHdc(), (POINT*) points, n);
598 }
599 }
600
601 void wxDC::DoDrawRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
602 {
603 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
604
605 wxCoord x2 = x + width;
606 wxCoord y2 = y + height;
607
608 if ((m_logicalFunction == wxCOPY) && (m_pen.GetStyle() == wxTRANSPARENT))
609 {
610 RECT rect;
611 rect.left = XLOG2DEV(x);
612 rect.top = YLOG2DEV(y);
613 rect.right = XLOG2DEV(x2);
614 rect.bottom = YLOG2DEV(y2);
615 (void)FillRect(GetHdc(), &rect, (HBRUSH)m_brush.GetResourceHandle() );
616 }
617 else
618 {
619 // Windows draws the filled rectangles without outline (i.e. drawn with a
620 // transparent pen) one pixel smaller in both directions and we want them
621 // to have the same size regardless of which pen is used - adjust
622
623 // I wonder if this shouldn´t be done after the LOG2DEV() conversions. RR.
624 if ( m_pen.GetStyle() == wxTRANSPARENT )
625 {
626 x2++;
627 y2++;
628 }
629
630 (void)Rectangle(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2));
631 }
632
633
634 CalcBoundingBox(x, y);
635 CalcBoundingBox(x2, y2);
636 }
637
638 void wxDC::DoDrawRoundedRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius)
639 {
640 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
641
642 // Now, a negative radius value is interpreted to mean
643 // 'the proportion of the smallest X or Y dimension'
644
645 if (radius < 0.0)
646 {
647 double smallest = 0.0;
648 if (width < height)
649 smallest = width;
650 else
651 smallest = height;
652 radius = (- radius * smallest);
653 }
654
655 wxCoord x2 = (x+width);
656 wxCoord y2 = (y+height);
657
658 // Windows draws the filled rectangles without outline (i.e. drawn with a
659 // transparent pen) one pixel smaller in both directions and we want them
660 // to have the same size regardless of which pen is used - adjust
661 if ( m_pen.GetStyle() == wxTRANSPARENT )
662 {
663 x2++;
664 y2++;
665 }
666
667 (void)RoundRect(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2),
668 YLOG2DEV(y2), (int) (2*XLOG2DEV(radius)), (int)( 2*YLOG2DEV(radius)));
669
670 CalcBoundingBox(x, y);
671 CalcBoundingBox(x2, y2);
672 }
673
674 void wxDC::DoDrawEllipse(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
675 {
676 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
677
678 wxCoord x2 = (x+width);
679 wxCoord y2 = (y+height);
680
681 (void)Ellipse(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2));
682
683 CalcBoundingBox(x, y);
684 CalcBoundingBox(x2, y2);
685 }
686
687 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
688 void wxDC::DoDrawEllipticArc(wxCoord x,wxCoord y,wxCoord w,wxCoord h,double sa,double ea)
689 {
690 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
691
692 wxCoord x2 = x + w;
693 wxCoord y2 = y + h;
694
695 int rx1 = XLOG2DEV(x+w/2);
696 int ry1 = YLOG2DEV(y+h/2);
697 int rx2 = rx1;
698 int ry2 = ry1;
699
700 sa = DegToRad(sa);
701 ea = DegToRad(ea);
702
703 rx1 += (int)(100.0 * abs(w) * cos(sa));
704 ry1 -= (int)(100.0 * abs(h) * m_signY * sin(sa));
705 rx2 += (int)(100.0 * abs(w) * cos(ea));
706 ry2 -= (int)(100.0 * abs(h) * m_signY * sin(ea));
707
708 // draw pie with NULL_PEN first and then outline otherwise a line is
709 // drawn from the start and end points to the centre
710 HPEN hpenOld = (HPEN) ::SelectObject(GetHdc(), (HPEN) ::GetStockObject(NULL_PEN));
711 if (m_signY > 0)
712 {
713 (void)Pie(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2)+1, YLOG2DEV(y2)+1,
714 rx1, ry1, rx2, ry2);
715 }
716 else
717 {
718 (void)Pie(GetHdc(), XLOG2DEV(x), YLOG2DEV(y)-1, XLOG2DEV(x2)+1, YLOG2DEV(y2),
719 rx1, ry1-1, rx2, ry2-1);
720 }
721
722 ::SelectObject(GetHdc(), hpenOld);
723
724 (void)Arc(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2),
725 rx1, ry1, rx2, ry2);
726
727 CalcBoundingBox(x, y);
728 CalcBoundingBox(x2, y2);
729 }
730
731 void wxDC::DoDrawIcon(const wxIcon& icon, wxCoord x, wxCoord y)
732 {
733 wxCHECK_RET( icon.Ok(), wxT("invalid icon in DrawIcon") );
734
735 #ifdef __WIN32__
736 ::DrawIconEx(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), GetHiconOf(icon), icon.GetWidth(), icon.GetHeight(), 0, NULL, DI_NORMAL);
737 #else
738 ::DrawIcon(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), GetHiconOf(icon));
739 #endif
740
741 CalcBoundingBox(x, y);
742 CalcBoundingBox(x + icon.GetWidth(), y + icon.GetHeight());
743 }
744
745 void wxDC::DoDrawBitmap( const wxBitmap &bmp, wxCoord x, wxCoord y, bool useMask )
746 {
747 wxCHECK_RET( bmp.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
748
749 int width = bmp.GetWidth(),
750 height = bmp.GetHeight();
751
752 HBITMAP hbmpMask = 0;
753
754 if ( useMask )
755 {
756 wxMask *mask = bmp.GetMask();
757 if ( mask )
758 hbmpMask = (HBITMAP)mask->GetMaskBitmap();
759
760 if ( !hbmpMask )
761 {
762 // don't give assert here because this would break existing
763 // programs - just silently ignore useMask parameter
764 useMask = FALSE;
765 }
766 }
767
768 if ( useMask )
769 {
770 #ifdef __WIN32__
771 HDC hdcMem = ::CreateCompatibleDC(GetHdc());
772 ::SelectObject(hdcMem, GetHbitmapOf(bmp));
773
774 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
775 // points
776 bool ok = ::MaskBlt(GetHdc(), x, y, width, height,
777 hdcMem, 0, 0,
778 hbmpMask, 0, 0,
779 MAKEROP4(SRCCOPY, DSTCOPY)) != 0;
780 ::DeleteDC(hdcMem);
781
782 if ( !ok )
783 #endif // Win32
784 {
785 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
786 // level
787 wxMemoryDC memDC;
788 memDC.SelectObject(bmp);
789
790 Blit(x, y, width, height, &memDC, 0, 0, wxCOPY, useMask);
791
792 memDC.SelectObject(wxNullBitmap);
793 }
794 }
795 else // no mask, just use BitBlt()
796 {
797 HDC cdc = GetHdc();
798 HDC memdc = ::CreateCompatibleDC( cdc );
799 HBITMAP hbitmap = (HBITMAP) bmp.GetHBITMAP( );
800
801 wxASSERT_MSG( hbitmap, wxT("bitmap is ok but HBITMAP is NULL?") );
802
803 COLORREF old_textground = ::GetTextColor(GetHdc());
804 COLORREF old_background = ::GetBkColor(GetHdc());
805 if (m_textForegroundColour.Ok())
806 {
807 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
808 }
809 if (m_textBackgroundColour.Ok())
810 {
811 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
812 }
813
814 ::SelectObject( memdc, hbitmap );
815 ::BitBlt( cdc, x, y, width, height, memdc, 0, 0, SRCCOPY);
816 ::DeleteDC( memdc );
817
818 ::SetTextColor(GetHdc(), old_textground);
819 ::SetBkColor(GetHdc(), old_background);
820 }
821 }
822
823 void wxDC::DoDrawText(const wxString& text, wxCoord x, wxCoord y)
824 {
825 DrawAnyText(text, x, y);
826
827 // update the bounding box
828 CalcBoundingBox(x, y);
829
830 wxCoord w, h;
831 GetTextExtent(text, &w, &h);
832 CalcBoundingBox(x + w, y + h);
833 }
834
835 void wxDC::DrawAnyText(const wxString& text, wxCoord x, wxCoord y)
836 {
837 // prepare for drawing the text
838 if ( m_textForegroundColour.Ok() )
839 SetTextColor(GetHdc(), m_textForegroundColour.GetPixel());
840
841 DWORD old_background = 0;
842 if ( m_textBackgroundColour.Ok() )
843 {
844 old_background = SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
845 }
846
847 SetBkMode(GetHdc(), m_backgroundMode == wxTRANSPARENT ? TRANSPARENT
848 : OPAQUE);
849
850 if ( ::TextOut(GetHdc(), XLOG2DEV(x), YLOG2DEV(y),
851 text.c_str(), text.length()) == 0 )
852 {
853 wxLogLastError(wxT("TextOut"));
854 }
855
856 // restore the old parameters (text foreground colour may be left because
857 // it never is set to anything else, but background should remain
858 // transparent even if we just drew an opaque string)
859 if ( m_textBackgroundColour.Ok() )
860 (void)SetBkColor(GetHdc(), old_background);
861
862 SetBkMode(GetHdc(), TRANSPARENT);
863 }
864
865 void wxDC::DoDrawRotatedText(const wxString& text,
866 wxCoord x, wxCoord y,
867 double angle)
868 {
869 // we test that we have some font because otherwise we should still use the
870 // "else" part below to avoid that DrawRotatedText(angle = 180) and
871 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
872 // font for drawing rotated fonts unfortunately)
873 if ( (angle == 0.0) && m_font.Ok() )
874 {
875 DoDrawText(text, x, y);
876 }
877 else
878 {
879 // NB: don't take DEFAULT_GUI_FONT because it's not TrueType and so
880 // can't have non zero orientation/escapement
881 wxFont font = m_font.Ok() ? m_font : *wxNORMAL_FONT;
882 HFONT hfont = (HFONT)font.GetResourceHandle();
883 LOGFONT lf;
884 if ( ::GetObject(hfont, sizeof(lf), &lf) == 0 )
885 {
886 wxLogLastError(wxT("GetObject(hfont)"));
887 }
888
889 // GDI wants the angle in tenth of degree
890 long angle10 = (long)(angle * 10);
891 lf.lfEscapement = angle10;
892 lf. lfOrientation = angle10;
893
894 hfont = ::CreateFontIndirect(&lf);
895 if ( !hfont )
896 {
897 wxLogLastError(wxT("CreateFont"));
898 }
899 else
900 {
901 HFONT hfontOld = (HFONT)::SelectObject(GetHdc(), hfont);
902
903 DrawAnyText(text, x, y);
904
905 (void)::SelectObject(GetHdc(), hfontOld);
906 (void)::DeleteObject(hfont);
907 }
908
909 // call the bounding box by adding all four vertices of the rectangle
910 // containing the text to it (simpler and probably not slower than
911 // determining which of them is really topmost/leftmost/...)
912 wxCoord w, h;
913 GetTextExtent(text, &w, &h);
914
915 double rad = DegToRad(angle);
916
917 // "upper left" and "upper right"
918 CalcBoundingBox(x, y);
919 CalcBoundingBox(x + w*cos(rad), y - h*sin(rad));
920
921 // "bottom left" and "bottom right"
922 x += (wxCoord)(h*sin(rad));
923 y += (wxCoord)(h*cos(rad));
924 CalcBoundingBox(x, y);
925 CalcBoundingBox(x + h*sin(rad), y + h*cos(rad));
926 }
927 }
928
929 // ---------------------------------------------------------------------------
930 // set GDI objects
931 // ---------------------------------------------------------------------------
932
933 void wxDC::SetPalette(const wxPalette& palette)
934 {
935 // Set the old object temporarily, in case the assignment deletes an object
936 // that's not yet selected out.
937 if (m_oldPalette)
938 {
939 ::SelectPalette(GetHdc(), (HPALETTE) m_oldPalette, TRUE);
940 m_oldPalette = 0;
941 }
942
943 m_palette = palette;
944
945 if (!m_palette.Ok())
946 {
947 // Setting a NULL colourmap is a way of restoring
948 // the original colourmap
949 if (m_oldPalette)
950 {
951 ::SelectPalette(GetHdc(), (HPALETTE) m_oldPalette, TRUE);
952 m_oldPalette = 0;
953 }
954
955 return;
956 }
957
958 if (m_palette.Ok() && m_palette.GetHPALETTE())
959 {
960 HPALETTE oldPal = ::SelectPalette(GetHdc(), (HPALETTE) m_palette.GetHPALETTE(), TRUE);
961 if (!m_oldPalette)
962 m_oldPalette = (WXHPALETTE) oldPal;
963
964 ::RealizePalette(GetHdc());
965 }
966 }
967
968 void wxDC::SetFont(const wxFont& the_font)
969 {
970 // Set the old object temporarily, in case the assignment deletes an object
971 // that's not yet selected out.
972 if (m_oldFont)
973 {
974 ::SelectObject(GetHdc(), (HFONT) m_oldFont);
975 m_oldFont = 0;
976 }
977
978 m_font = the_font;
979
980 if (!the_font.Ok())
981 {
982 if (m_oldFont)
983 ::SelectObject(GetHdc(), (HFONT) m_oldFont);
984 m_oldFont = 0;
985 }
986
987 if (m_font.Ok() && m_font.GetResourceHandle())
988 {
989 HFONT f = (HFONT) ::SelectObject(GetHdc(), (HFONT) m_font.GetResourceHandle());
990 if (f == (HFONT) NULL)
991 {
992 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
993 }
994 if (!m_oldFont)
995 m_oldFont = (WXHFONT) f;
996 }
997 }
998
999 void wxDC::SetPen(const wxPen& pen)
1000 {
1001 // Set the old object temporarily, in case the assignment deletes an object
1002 // that's not yet selected out.
1003 if (m_oldPen)
1004 {
1005 ::SelectObject(GetHdc(), (HPEN) m_oldPen);
1006 m_oldPen = 0;
1007 }
1008
1009 m_pen = pen;
1010
1011 if (!m_pen.Ok())
1012 {
1013 if (m_oldPen)
1014 ::SelectObject(GetHdc(), (HPEN) m_oldPen);
1015 m_oldPen = 0;
1016 }
1017
1018 if (m_pen.Ok())
1019 {
1020 if (m_pen.GetResourceHandle())
1021 {
1022 HPEN p = (HPEN) ::SelectObject(GetHdc(), (HPEN)m_pen.GetResourceHandle());
1023 if (!m_oldPen)
1024 m_oldPen = (WXHPEN) p;
1025 }
1026 }
1027 }
1028
1029 void wxDC::SetBrush(const wxBrush& brush)
1030 {
1031 // Set the old object temporarily, in case the assignment deletes an object
1032 // that's not yet selected out.
1033 if (m_oldBrush)
1034 {
1035 ::SelectObject(GetHdc(), (HBRUSH) m_oldBrush);
1036 m_oldBrush = 0;
1037 }
1038
1039 m_brush = brush;
1040
1041 if (!m_brush.Ok())
1042 {
1043 if (m_oldBrush)
1044 ::SelectObject(GetHdc(), (HBRUSH) m_oldBrush);
1045 m_oldBrush = 0;
1046 }
1047
1048 if (m_brush.Ok())
1049 {
1050 // to make sure the brush is alligned with the logical coordinates
1051 wxBitmap *stipple = m_brush.GetStipple();
1052 if ( stipple && stipple->Ok() )
1053 {
1054 #ifdef __WIN32__
1055 ::SetBrushOrgEx(GetHdc(),
1056 m_deviceOriginX % stipple->GetWidth(),
1057 m_deviceOriginY % stipple->GetHeight(),
1058 NULL); // don't need previous brush origin
1059 #else
1060 ::SetBrushOrg(GetHdc(),
1061 m_deviceOriginX % stipple->GetWidth(),
1062 m_deviceOriginY % stipple->GetHeight());
1063 #endif
1064 }
1065
1066 if ( m_brush.GetResourceHandle() )
1067 {
1068 HBRUSH b = 0;
1069 b = (HBRUSH) ::SelectObject(GetHdc(), (HBRUSH)m_brush.GetResourceHandle());
1070 if (!m_oldBrush)
1071 m_oldBrush = (WXHBRUSH) b;
1072 }
1073 }
1074 }
1075
1076 void wxDC::SetBackground(const wxBrush& brush)
1077 {
1078 m_backgroundBrush = brush;
1079
1080 if (!m_backgroundBrush.Ok())
1081 return;
1082
1083 if (m_canvas)
1084 {
1085 bool customColours = TRUE;
1086 // If we haven't specified wxUSER_COLOURS, don't allow the panel/dialog box to
1087 // change background colours from the control-panel specified colours.
1088 if (m_canvas->IsKindOf(CLASSINFO(wxWindow)) && ((m_canvas->GetWindowStyleFlag() & wxUSER_COLOURS) != wxUSER_COLOURS))
1089 customColours = FALSE;
1090
1091 if (customColours)
1092 {
1093 if (m_backgroundBrush.GetStyle()==wxTRANSPARENT)
1094 {
1095 m_canvas->SetTransparent(TRUE);
1096 }
1097 else
1098 {
1099 // New behaviour, 10/2/99: setting the background brush of a DC
1100 // doesn't affect the window background colour. However,
1101 // I'm leaving in the transparency setting because it's needed by
1102 // various controls (e.g. wxStaticText) to determine whether to draw
1103 // transparently or not. TODO: maybe this should be a new function
1104 // wxWindow::SetTransparency(). Should that apply to the child itself, or the
1105 // parent?
1106 // m_canvas->SetBackgroundColour(m_backgroundBrush.GetColour());
1107 m_canvas->SetTransparent(FALSE);
1108 }
1109 }
1110 }
1111 COLORREF new_color = m_backgroundBrush.GetColour().GetPixel();
1112 {
1113 (void)SetBkColor(GetHdc(), new_color);
1114 }
1115 }
1116
1117 void wxDC::SetBackgroundMode(int mode)
1118 {
1119 m_backgroundMode = mode;
1120
1121 // SetBackgroundColour now only refers to text background
1122 // and m_backgroundMode is used there
1123
1124 /*
1125 if (m_backgroundMode == wxTRANSPARENT)
1126 ::SetBkMode(GetHdc(), TRANSPARENT);
1127 else
1128 ::SetBkMode(GetHdc(), OPAQUE);
1129 Last change: AC 29 Jan 101 8:54 pm
1130 */
1131 }
1132
1133 void wxDC::SetLogicalFunction(int function)
1134 {
1135 m_logicalFunction = function;
1136
1137 SetRop(m_hDC);
1138 }
1139
1140 void wxDC::SetRop(WXHDC dc)
1141 {
1142 if ( !dc || m_logicalFunction < 0 )
1143 return;
1144
1145 int rop;
1146
1147 switch (m_logicalFunction)
1148 {
1149 case wxCLEAR: rop = R2_BLACK; break;
1150 case wxXOR: rop = R2_XORPEN; break;
1151 case wxINVERT: rop = R2_NOT; break;
1152 case wxOR_REVERSE: rop = R2_MERGEPENNOT; break;
1153 case wxAND_REVERSE: rop = R2_MASKPENNOT; break;
1154 case wxCOPY: rop = R2_COPYPEN; break;
1155 case wxAND: rop = R2_MASKPEN; break;
1156 case wxAND_INVERT: rop = R2_MASKNOTPEN; break;
1157 case wxNO_OP: rop = R2_NOP; break;
1158 case wxNOR: rop = R2_NOTMERGEPEN; break;
1159 case wxEQUIV: rop = R2_NOTXORPEN; break;
1160 case wxSRC_INVERT: rop = R2_NOTCOPYPEN; break;
1161 case wxOR_INVERT: rop = R2_MERGENOTPEN; break;
1162 case wxNAND: rop = R2_NOTMASKPEN; break;
1163 case wxOR: rop = R2_MERGEPEN; break;
1164 case wxSET: rop = R2_WHITE; break;
1165
1166 default:
1167 wxFAIL_MSG( wxT("unsupported logical function") );
1168 return;
1169 }
1170
1171 SetROP2(GetHdc(), rop);
1172 }
1173
1174 bool wxDC::StartDoc(const wxString& WXUNUSED(message))
1175 {
1176 // We might be previewing, so return TRUE to let it continue.
1177 return TRUE;
1178 }
1179
1180 void wxDC::EndDoc()
1181 {
1182 }
1183
1184 void wxDC::StartPage()
1185 {
1186 }
1187
1188 void wxDC::EndPage()
1189 {
1190 }
1191
1192 // ---------------------------------------------------------------------------
1193 // text metrics
1194 // ---------------------------------------------------------------------------
1195
1196 wxCoord wxDC::GetCharHeight() const
1197 {
1198 TEXTMETRIC lpTextMetric;
1199
1200 GetTextMetrics(GetHdc(), &lpTextMetric);
1201
1202 return YDEV2LOGREL(lpTextMetric.tmHeight);
1203 }
1204
1205 wxCoord wxDC::GetCharWidth() const
1206 {
1207 TEXTMETRIC lpTextMetric;
1208
1209 GetTextMetrics(GetHdc(), &lpTextMetric);
1210
1211 return XDEV2LOGREL(lpTextMetric.tmAveCharWidth);
1212 }
1213
1214 void wxDC::DoGetTextExtent(const wxString& string, wxCoord *x, wxCoord *y,
1215 wxCoord *descent, wxCoord *externalLeading,
1216 wxFont *font) const
1217 {
1218 HFONT hfontOld;
1219 if ( font )
1220 {
1221 wxASSERT_MSG( font->Ok(), _T("invalid font in wxDC::GetTextExtent") );
1222
1223 hfontOld = (HFONT)::SelectObject(GetHdc(), GetHfontOf(*font));
1224 }
1225 else // don't change the font
1226 {
1227 hfontOld = 0;
1228 }
1229
1230 SIZE sizeRect;
1231 TEXTMETRIC tm;
1232
1233 GetTextExtentPoint(GetHdc(), string, string.length(), &sizeRect);
1234 GetTextMetrics(GetHdc(), &tm);
1235
1236 if (x) *x = XDEV2LOGREL(sizeRect.cx);
1237 if (y) *y = YDEV2LOGREL(sizeRect.cy);
1238 if (descent) *descent = tm.tmDescent;
1239 if (externalLeading) *externalLeading = tm.tmExternalLeading;
1240
1241 if ( hfontOld )
1242 {
1243 ::SelectObject(GetHdc(), hfontOld);
1244 }
1245 }
1246
1247 void wxDC::SetMapMode(int mode)
1248 {
1249 m_mappingMode = mode;
1250
1251 int pixel_width = 0;
1252 int pixel_height = 0;
1253 int mm_width = 0;
1254 int mm_height = 0;
1255
1256 pixel_width = GetDeviceCaps(GetHdc(), HORZRES);
1257 pixel_height = GetDeviceCaps(GetHdc(), VERTRES);
1258 mm_width = GetDeviceCaps(GetHdc(), HORZSIZE);
1259 mm_height = GetDeviceCaps(GetHdc(), VERTSIZE);
1260
1261 if ((pixel_width == 0) || (pixel_height == 0) || (mm_width == 0) || (mm_height == 0))
1262 {
1263 return;
1264 }
1265
1266 double mm2pixelsX = pixel_width/mm_width;
1267 double mm2pixelsY = pixel_height/mm_height;
1268
1269 switch (mode)
1270 {
1271 case wxMM_TWIPS:
1272 {
1273 m_logicalScaleX = (twips2mm * mm2pixelsX);
1274 m_logicalScaleY = (twips2mm * mm2pixelsY);
1275 break;
1276 }
1277 case wxMM_POINTS:
1278 {
1279 m_logicalScaleX = (pt2mm * mm2pixelsX);
1280 m_logicalScaleY = (pt2mm * mm2pixelsY);
1281 break;
1282 }
1283 case wxMM_METRIC:
1284 {
1285 m_logicalScaleX = mm2pixelsX;
1286 m_logicalScaleY = mm2pixelsY;
1287 break;
1288 }
1289 case wxMM_LOMETRIC:
1290 {
1291 m_logicalScaleX = (mm2pixelsX/10.0);
1292 m_logicalScaleY = (mm2pixelsY/10.0);
1293 break;
1294 }
1295 default:
1296 case wxMM_TEXT:
1297 {
1298 m_logicalScaleX = 1.0;
1299 m_logicalScaleY = 1.0;
1300 break;
1301 }
1302 }
1303
1304 if (::GetMapMode(GetHdc()) != MM_ANISOTROPIC)
1305 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
1306
1307 SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
1308 m_windowExtX = (int)MS_XDEV2LOGREL(VIEWPORT_EXTENT);
1309 m_windowExtY = (int)MS_YDEV2LOGREL(VIEWPORT_EXTENT);
1310 ::SetWindowExtEx(GetHdc(), m_windowExtX, m_windowExtY, NULL);
1311 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
1312 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
1313 }
1314
1315 void wxDC::SetUserScale(double x, double y)
1316 {
1317 m_userScaleX = x;
1318 m_userScaleY = y;
1319
1320 SetMapMode(m_mappingMode);
1321 }
1322
1323 void wxDC::SetAxisOrientation(bool xLeftRight, bool yBottomUp)
1324 {
1325 m_signX = xLeftRight ? 1 : -1;
1326 m_signY = yBottomUp ? -1 : 1;
1327
1328 SetMapMode(m_mappingMode);
1329 }
1330
1331 void wxDC::SetSystemScale(double x, double y)
1332 {
1333 m_scaleX = x;
1334 m_scaleY = y;
1335
1336 SetMapMode(m_mappingMode);
1337 }
1338
1339 void wxDC::SetLogicalOrigin(wxCoord x, wxCoord y)
1340 {
1341 m_logicalOriginX = x;
1342 m_logicalOriginY = y;
1343
1344 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
1345 }
1346
1347 void wxDC::SetDeviceOrigin(wxCoord x, wxCoord y)
1348 {
1349 m_deviceOriginX = x;
1350 m_deviceOriginY = y;
1351
1352 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
1353 }
1354
1355 // ---------------------------------------------------------------------------
1356 // coordinates transformations
1357 // ---------------------------------------------------------------------------
1358
1359 wxCoord wxDCBase::DeviceToLogicalX(wxCoord x) const
1360 {
1361 double xRel = x - m_deviceOriginX;
1362 xRel /= m_logicalScaleX*m_userScaleX*m_signX*m_scaleX;
1363 return (wxCoord)(xRel + m_logicalOriginX);
1364 }
1365
1366 wxCoord wxDCBase::DeviceToLogicalXRel(wxCoord x) const
1367 {
1368 return (wxCoord) ((x)/(m_logicalScaleX*m_userScaleX*m_signX*m_scaleX));
1369 }
1370
1371 wxCoord wxDCBase::DeviceToLogicalY(wxCoord y) const
1372 {
1373 double yRel = y - m_deviceOriginY;
1374 yRel /= m_logicalScaleY*m_userScaleY*m_signY*m_scaleY;
1375 return (wxCoord)(yRel + m_logicalOriginY);
1376 }
1377
1378 wxCoord wxDCBase::DeviceToLogicalYRel(wxCoord y) const
1379 {
1380 return (wxCoord) ((y)/(m_logicalScaleY*m_userScaleY*m_signY*m_scaleY));
1381 }
1382
1383 wxCoord wxDCBase::LogicalToDeviceX(wxCoord x) const
1384 {
1385 return (wxCoord) ((x - m_logicalOriginX)*m_logicalScaleX*m_userScaleX*m_signX*m_scaleX + m_deviceOriginX);
1386 }
1387
1388 wxCoord wxDCBase::LogicalToDeviceXRel(wxCoord x) const
1389 {
1390 return (wxCoord) (x*m_logicalScaleX*m_userScaleX*m_signX*m_scaleX);
1391 }
1392
1393 wxCoord wxDCBase::LogicalToDeviceY(wxCoord y) const
1394 {
1395 return (wxCoord) ((y - m_logicalOriginY)*m_logicalScaleY*m_userScaleY*m_signY*m_scaleY + m_deviceOriginY);
1396 }
1397
1398 wxCoord wxDCBase::LogicalToDeviceYRel(wxCoord y) const
1399 {
1400 return (wxCoord) (y*m_logicalScaleY*m_userScaleY*m_signY*m_scaleY);
1401 }
1402
1403 // ---------------------------------------------------------------------------
1404 // bit blit
1405 // ---------------------------------------------------------------------------
1406
1407 bool wxDC::DoBlit(wxCoord xdest, wxCoord ydest,
1408 wxCoord width, wxCoord height,
1409 wxDC *source, wxCoord xsrc, wxCoord ysrc,
1410 int rop, bool useMask)
1411 {
1412 wxMask *mask = NULL;
1413 if ( useMask )
1414 {
1415 const wxBitmap& bmp = source->m_selectedBitmap;
1416 mask = bmp.GetMask();
1417
1418 if ( !(bmp.Ok() && mask && mask->GetMaskBitmap()) )
1419 {
1420 // don't give assert here because this would break existing
1421 // programs - just silently ignore useMask parameter
1422 useMask = FALSE;
1423 }
1424 }
1425
1426 COLORREF old_textground = ::GetTextColor(GetHdc());
1427 COLORREF old_background = ::GetBkColor(GetHdc());
1428 if (m_textForegroundColour.Ok())
1429 {
1430 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1431 }
1432 if (m_textBackgroundColour.Ok())
1433 {
1434 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1435 }
1436
1437 DWORD dwRop = SRCCOPY;
1438 switch (rop)
1439 {
1440 case wxXOR: dwRop = SRCINVERT; break;
1441 case wxINVERT: dwRop = DSTINVERT; break;
1442 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
1443 case wxAND_REVERSE: dwRop = SRCERASE; break;
1444 case wxCLEAR: dwRop = BLACKNESS; break;
1445 case wxSET: dwRop = WHITENESS; break;
1446 case wxOR_INVERT: dwRop = MERGEPAINT; break;
1447 case wxAND: dwRop = SRCAND; break;
1448 case wxOR: dwRop = SRCPAINT; break;
1449 case wxEQUIV: dwRop = 0x00990066; break;
1450 case wxNAND: dwRop = 0x007700E6; break;
1451 case wxAND_INVERT: dwRop = 0x00220326; break;
1452 case wxCOPY: dwRop = SRCCOPY; break;
1453 case wxNO_OP: dwRop = DSTCOPY; break;
1454 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
1455 case wxNOR: dwRop = NOTSRCCOPY; break;
1456 default:
1457 wxFAIL_MSG( wxT("unsupported logical function") );
1458 return FALSE;
1459 }
1460
1461 bool success;
1462
1463 if (useMask)
1464 {
1465 #ifdef __WIN32__
1466 // we want the part of the image corresponding to the mask to be
1467 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1468 // meaning of fg and bg is inverted which corresponds to wxWin notion
1469 // of the mask which is also contrary to the Windows one)
1470 success = ::MaskBlt(GetHdc(), xdest, ydest, width, height,
1471 GetHdcOf(*source), xsrc, ysrc,
1472 (HBITMAP)mask->GetMaskBitmap(), xsrc, ysrc,
1473 MAKEROP4(dwRop, DSTCOPY)) != 0;
1474
1475 if ( !success )
1476 #endif // Win32
1477 {
1478 // Blit bitmap with mask
1479
1480 // create a temp buffer bitmap and DCs to access it and the mask
1481 HDC dc_mask = ::CreateCompatibleDC(GetHdcOf(*source));
1482 HDC dc_buffer = ::CreateCompatibleDC(GetHdc());
1483 HBITMAP buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), width, height);
1484 ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
1485 ::SelectObject(dc_buffer, buffer_bmap);
1486
1487 // copy dest to buffer
1488 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1489 GetHdc(), xdest, ydest, SRCCOPY) )
1490 {
1491 wxLogLastError(wxT("BitBlt"));
1492 }
1493
1494 // copy src to buffer using selected raster op
1495 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1496 GetHdcOf(*source), xsrc, ysrc, dwRop) )
1497 {
1498 wxLogLastError(wxT("BitBlt"));
1499 }
1500
1501 // set masked area in buffer to BLACK (pixel value 0)
1502 COLORREF prevBkCol = ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1503 COLORREF prevCol = ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1504 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1505 dc_mask, xsrc, ysrc, SRCAND) )
1506 {
1507 wxLogLastError(wxT("BitBlt"));
1508 }
1509
1510 // set unmasked area in dest to BLACK
1511 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1512 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1513 if ( !::BitBlt(GetHdc(), xdest, ydest, (int)width, (int)height,
1514 dc_mask, xsrc, ysrc, SRCAND) )
1515 {
1516 wxLogLastError(wxT("BitBlt"));
1517 }
1518 ::SetBkColor(GetHdc(), prevBkCol); // restore colours to original values
1519 ::SetTextColor(GetHdc(), prevCol);
1520
1521 // OR buffer to dest
1522 success = ::BitBlt(GetHdc(), xdest, ydest,
1523 (int)width, (int)height,
1524 dc_buffer, 0, 0, SRCPAINT) != 0;
1525 if ( !success )
1526 {
1527 wxLogLastError(wxT("BitBlt"));
1528 }
1529
1530 // tidy up temporary DCs and bitmap
1531 ::SelectObject(dc_mask, 0);
1532 ::DeleteDC(dc_mask);
1533 ::SelectObject(dc_buffer, 0);
1534 ::DeleteDC(dc_buffer);
1535 ::DeleteObject(buffer_bmap);
1536 }
1537 }
1538 else // no mask, just BitBlt() it
1539 {
1540 success = ::BitBlt(GetHdc(), xdest, ydest,
1541 (int)width, (int)height,
1542 GetHdcOf(*source), xsrc, ysrc, dwRop) != 0;
1543 if ( !success )
1544 {
1545 wxLogLastError(wxT("BitBlt"));
1546 }
1547 }
1548 ::SetTextColor(GetHdc(), old_textground);
1549 ::SetBkColor(GetHdc(), old_background);
1550
1551 return success;
1552 }
1553
1554 void wxDC::DoGetSize(int *w, int *h) const
1555 {
1556 if ( w ) *w = ::GetDeviceCaps(GetHdc(), HORZRES);
1557 if ( h ) *h = ::GetDeviceCaps(GetHdc(), VERTRES);
1558 }
1559
1560 void wxDC::DoGetSizeMM(int *w, int *h) const
1561 {
1562 if ( w ) *w = ::GetDeviceCaps(GetHdc(), HORZSIZE);
1563 if ( h ) *h = ::GetDeviceCaps(GetHdc(), VERTSIZE);
1564 }
1565
1566 wxSize wxDC::GetPPI() const
1567 {
1568 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
1569 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
1570
1571 return wxSize(x, y);
1572 }
1573
1574 // For use by wxWindows only, unless custom units are required.
1575 void wxDC::SetLogicalScale(double x, double y)
1576 {
1577 m_logicalScaleX = x;
1578 m_logicalScaleY = y;
1579 }
1580
1581 #if WXWIN_COMPATIBILITY
1582 void wxDC::DoGetTextExtent(const wxString& string, float *x, float *y,
1583 float *descent, float *externalLeading,
1584 wxFont *theFont, bool use16bit) const
1585 {
1586 wxCoord x1, y1, descent1, externalLeading1;
1587 GetTextExtent(string, & x1, & y1, & descent1, & externalLeading1, theFont, use16bit);
1588 *x = x1; *y = y1;
1589 if (descent)
1590 *descent = descent1;
1591 if (externalLeading)
1592 *externalLeading = externalLeading1;
1593 }
1594 #endif
1595
1596