]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dc.cpp
Do not allow events to fire after socket is closed (fixes crash on GTK with GDK_Input...
[wxWidgets.git] / src / msw / dc.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/dc.cpp
3 // Purpose: wxDC class for MSW port
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01/02/97
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #ifndef WX_PRECOMP
28 #include "wx/image.h"
29 #include "wx/window.h"
30 #include "wx/dc.h"
31 #include "wx/utils.h"
32 #include "wx/dialog.h"
33 #include "wx/app.h"
34 #include "wx/bitmap.h"
35 #include "wx/dcmemory.h"
36 #include "wx/log.h"
37 #include "wx/icon.h"
38 #endif
39
40 #include "wx/sysopt.h"
41 #include "wx/dcprint.h"
42 #include "wx/module.h"
43 #include "wx/dynload.h"
44
45 #ifdef wxHAVE_RAW_BITMAP
46 #include "wx/rawbmp.h"
47 #endif
48
49 #include <string.h>
50
51 #include "wx/msw/wrapcdlg.h"
52 #ifndef __WIN32__
53 #include <print.h>
54 #endif
55
56 #ifndef AC_SRC_ALPHA
57 #define AC_SRC_ALPHA 1
58 #endif
59
60 /* Quaternary raster codes */
61 #ifndef MAKEROP4
62 #define MAKEROP4(fore,back) (DWORD)((((back) << 8) & 0xFF000000) | (fore))
63 #endif
64
65 // apparently with MicroWindows it is possible that HDC is 0 so we have to
66 // check for this ourselves
67 #ifdef __WXMICROWIN__
68 #define WXMICROWIN_CHECK_HDC if ( !GetHDC() ) return;
69 #define WXMICROWIN_CHECK_HDC_RET(x) if ( !GetHDC() ) return x;
70 #else
71 #define WXMICROWIN_CHECK_HDC
72 #define WXMICROWIN_CHECK_HDC_RET(x)
73 #endif
74
75 IMPLEMENT_ABSTRACT_CLASS(wxDC, wxDCBase)
76
77 // ---------------------------------------------------------------------------
78 // constants
79 // ---------------------------------------------------------------------------
80
81 static const int VIEWPORT_EXTENT = 1000;
82
83 static const int MM_POINTS = 9;
84 static const int MM_METRIC = 10;
85
86 // ROPs which don't have standard names (see "Ternary Raster Operations" in the
87 // MSDN docs for how this and other numbers in wxDC::Blit() are obtained)
88 #define DSTCOPY 0x00AA0029 // a.k.a. NOP operation
89
90 // ----------------------------------------------------------------------------
91 // macros for logical <-> device coords conversion
92 // ----------------------------------------------------------------------------
93
94 /*
95 We currently let Windows do all the translations itself so these macros are
96 not really needed (any more) but keep them to enhance readability of the
97 code by allowing to see where are the logical and where are the device
98 coordinates used.
99 */
100
101 #ifdef __WXWINCE__
102 #define XLOG2DEV(x) ((x-m_logicalOriginX)*m_signX)
103 #define YLOG2DEV(y) ((y-m_logicalOriginY)*m_signY)
104 #define XDEV2LOG(x) ((x)*m_signX+m_logicalOriginX)
105 #define YDEV2LOG(y) ((y)*m_signY+m_logicalOriginY)
106 #else
107 #define XLOG2DEV(x) (x)
108 #define YLOG2DEV(y) (y)
109 #define XDEV2LOG(x) (x)
110 #define YDEV2LOG(y) (y)
111 #endif
112
113 // ---------------------------------------------------------------------------
114 // private functions
115 // ---------------------------------------------------------------------------
116
117 // convert degrees to radians
118 static inline double DegToRad(double deg) { return (deg * M_PI) / 180.0; }
119
120 // call AlphaBlend() to blit contents of hdcSrc to hdcDst using alpha
121 //
122 // NB: bmpSrc is the bitmap selected in hdcSrc, it is not really needed
123 // to pass it to this function but as we already have it at the point
124 // of call anyhow we do
125 //
126 // return true if we could draw the bitmap in one way or the other, false
127 // otherwise
128 static bool AlphaBlt(HDC hdcDst,
129 int x, int y, int w, int h,
130 int srcX, int srcY, HDC hdcSrc,
131 const wxBitmap& bmpSrc);
132
133 #ifdef wxHAVE_RAW_BITMAP
134 // our (limited) AlphaBlend() replacement
135 static void
136 wxAlphaBlend(HDC hdcDst, int x, int y, int w, int h, int srcX, int srcY, const wxBitmap& bmp);
137 #endif
138
139 // ----------------------------------------------------------------------------
140 // private classes
141 // ----------------------------------------------------------------------------
142
143 // instead of duplicating the same code which sets and then restores text
144 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
145 // encapsulate this in a small helper class
146
147 // wxColourChanger: changes the text colours in the ctor if required and
148 // restores them in the dtor
149 class wxColourChanger
150 {
151 public:
152 wxColourChanger(wxDC& dc);
153 ~wxColourChanger();
154
155 private:
156 wxDC& m_dc;
157
158 COLORREF m_colFgOld, m_colBgOld;
159
160 bool m_changed;
161
162 DECLARE_NO_COPY_CLASS(wxColourChanger)
163 };
164
165 // this class saves the old stretch blit mode during its life time
166 class StretchBltModeChanger
167 {
168 public:
169 StretchBltModeChanger(HDC hdc,
170 int WXUNUSED_IN_WINCE(mode))
171 : m_hdc(hdc)
172 {
173 #ifndef __WXWINCE__
174 m_modeOld = ::SetStretchBltMode(m_hdc, mode);
175 if ( !m_modeOld )
176 wxLogLastError(_T("SetStretchBltMode"));
177 #endif
178 }
179
180 ~StretchBltModeChanger()
181 {
182 #ifndef __WXWINCE__
183 if ( !::SetStretchBltMode(m_hdc, m_modeOld) )
184 wxLogLastError(_T("SetStretchBltMode"));
185 #endif
186 }
187
188 private:
189 const HDC m_hdc;
190
191 int m_modeOld;
192
193 DECLARE_NO_COPY_CLASS(StretchBltModeChanger)
194 };
195
196 // ===========================================================================
197 // implementation
198 // ===========================================================================
199
200 // ----------------------------------------------------------------------------
201 // wxColourChanger
202 // ----------------------------------------------------------------------------
203
204 wxColourChanger::wxColourChanger(wxDC& dc) : m_dc(dc)
205 {
206 const wxBrush& brush = dc.GetBrush();
207 if ( brush.Ok() && brush.GetStyle() == wxSTIPPLE_MASK_OPAQUE )
208 {
209 HDC hdc = GetHdcOf(dc);
210 m_colFgOld = ::GetTextColor(hdc);
211 m_colBgOld = ::GetBkColor(hdc);
212
213 // note that Windows convention is opposite to wxWidgets one, this is
214 // why text colour becomes the background one and vice versa
215 const wxColour& colFg = dc.GetTextForeground();
216 if ( colFg.Ok() )
217 {
218 ::SetBkColor(hdc, colFg.GetPixel());
219 }
220
221 const wxColour& colBg = dc.GetTextBackground();
222 if ( colBg.Ok() )
223 {
224 ::SetTextColor(hdc, colBg.GetPixel());
225 }
226
227 SetBkMode(hdc,
228 dc.GetBackgroundMode() == wxTRANSPARENT ? TRANSPARENT
229 : OPAQUE);
230
231 // flag which telsl us to undo changes in the dtor
232 m_changed = true;
233 }
234 else
235 {
236 // nothing done, nothing to undo
237 m_changed = false;
238 }
239 }
240
241 wxColourChanger::~wxColourChanger()
242 {
243 if ( m_changed )
244 {
245 // restore the colours we changed
246 HDC hdc = GetHdcOf(m_dc);
247
248 ::SetBkMode(hdc, TRANSPARENT);
249 ::SetTextColor(hdc, m_colFgOld);
250 ::SetBkColor(hdc, m_colBgOld);
251 }
252 }
253
254 // ---------------------------------------------------------------------------
255 // wxDC
256 // ---------------------------------------------------------------------------
257
258 // Default constructor
259 wxDC::wxDC()
260 {
261 m_canvas = NULL;
262
263 m_oldBitmap = 0;
264 m_oldPen = 0;
265 m_oldBrush = 0;
266 m_oldFont = 0;
267 #if wxUSE_PALETTE
268 m_oldPalette = 0;
269 #endif // wxUSE_PALETTE
270
271 m_bOwnsDC = false;
272 m_hDC = 0;
273 }
274
275 wxDC::~wxDC()
276 {
277 if ( m_hDC != 0 )
278 {
279 SelectOldObjects(m_hDC);
280
281 // if we own the HDC, we delete it, otherwise we just release it
282
283 if ( m_bOwnsDC )
284 {
285 ::DeleteDC(GetHdc());
286 }
287 else // we don't own our HDC
288 {
289 if (m_canvas)
290 {
291 ::ReleaseDC(GetHwndOf(m_canvas), GetHdc());
292 }
293 else
294 {
295 // Must have been a wxScreenDC
296 ::ReleaseDC((HWND) NULL, GetHdc());
297 }
298 }
299 }
300 }
301
302 // This will select current objects out of the DC,
303 // which is what you have to do before deleting the
304 // DC.
305 void wxDC::SelectOldObjects(WXHDC dc)
306 {
307 if (dc)
308 {
309 if (m_oldBitmap)
310 {
311 ::SelectObject((HDC) dc, (HBITMAP) m_oldBitmap);
312 #ifdef __WXDEBUG__
313 if (m_selectedBitmap.Ok())
314 {
315 m_selectedBitmap.SetSelectedInto(NULL);
316 }
317 #endif
318 }
319 m_oldBitmap = 0;
320 if (m_oldPen)
321 {
322 ::SelectObject((HDC) dc, (HPEN) m_oldPen);
323 }
324 m_oldPen = 0;
325 if (m_oldBrush)
326 {
327 ::SelectObject((HDC) dc, (HBRUSH) m_oldBrush);
328 }
329 m_oldBrush = 0;
330 if (m_oldFont)
331 {
332 ::SelectObject((HDC) dc, (HFONT) m_oldFont);
333 }
334 m_oldFont = 0;
335
336 #if wxUSE_PALETTE
337 if (m_oldPalette)
338 {
339 ::SelectPalette((HDC) dc, (HPALETTE) m_oldPalette, FALSE);
340 }
341 m_oldPalette = 0;
342 #endif // wxUSE_PALETTE
343 }
344
345 m_brush = wxNullBrush;
346 m_pen = wxNullPen;
347 #if wxUSE_PALETTE
348 m_palette = wxNullPalette;
349 #endif // wxUSE_PALETTE
350 m_font = wxNullFont;
351 m_backgroundBrush = wxNullBrush;
352 m_selectedBitmap = wxNullBitmap;
353 }
354
355 // ---------------------------------------------------------------------------
356 // clipping
357 // ---------------------------------------------------------------------------
358
359 void wxDC::UpdateClipBox()
360 {
361 WXMICROWIN_CHECK_HDC
362
363 RECT rect;
364 ::GetClipBox(GetHdc(), &rect);
365
366 m_clipX1 = (wxCoord) XDEV2LOG(rect.left);
367 m_clipY1 = (wxCoord) YDEV2LOG(rect.top);
368 m_clipX2 = (wxCoord) XDEV2LOG(rect.right);
369 m_clipY2 = (wxCoord) YDEV2LOG(rect.bottom);
370 }
371
372 void
373 wxDC::DoGetClippingBox(wxCoord *x, wxCoord *y, wxCoord *w, wxCoord *h) const
374 {
375 // check if we should try to retrieve the clipping region possibly not set
376 // by our SetClippingRegion() but preset by Windows:this can only happen
377 // when we're associated with an existing HDC usign SetHDC(), see there
378 if ( m_clipping && !m_clipX1 && !m_clipX2 )
379 {
380 wxDC *self = wxConstCast(this, wxDC);
381 self->UpdateClipBox();
382
383 if ( !m_clipX1 && !m_clipX2 )
384 self->m_clipping = false;
385 }
386
387 wxDCBase::DoGetClippingBox(x, y, w, h);
388 }
389
390 // common part of DoSetClippingRegion() and DoSetClippingRegionAsRegion()
391 void wxDC::SetClippingHrgn(WXHRGN hrgn)
392 {
393 wxCHECK_RET( hrgn, wxT("invalid clipping region") );
394
395 WXMICROWIN_CHECK_HDC
396
397 // note that we combine the new clipping region with the existing one: this
398 // is compatible with what the other ports do and is the documented
399 // behaviour now (starting with 2.3.3)
400 #if defined(__WXWINCE__)
401 RECT rectClip;
402 if ( !::GetClipBox(GetHdc(), &rectClip) )
403 return;
404
405 // GetClipBox returns logical coordinates, so transform to device
406 rectClip.left = LogicalToDeviceX(rectClip.left);
407 rectClip.top = LogicalToDeviceY(rectClip.top);
408 rectClip.right = LogicalToDeviceX(rectClip.right);
409 rectClip.bottom = LogicalToDeviceY(rectClip.bottom);
410
411 HRGN hrgnDest = ::CreateRectRgn(0, 0, 0, 0);
412 HRGN hrgnClipOld = ::CreateRectRgn(rectClip.left, rectClip.top,
413 rectClip.right, rectClip.bottom);
414
415 if ( ::CombineRgn(hrgnDest, hrgnClipOld, (HRGN)hrgn, RGN_AND) != ERROR )
416 {
417 ::SelectClipRgn(GetHdc(), hrgnDest);
418 }
419
420 ::DeleteObject(hrgnClipOld);
421 ::DeleteObject(hrgnDest);
422 #else // !WinCE
423 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN)hrgn, RGN_AND) == ERROR )
424 {
425 wxLogLastError(_T("ExtSelectClipRgn"));
426
427 return;
428 }
429 #endif // WinCE/!WinCE
430
431 m_clipping = true;
432
433 UpdateClipBox();
434 }
435
436 void wxDC::DoSetClippingRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
437 {
438 // the region coords are always the device ones, so do the translation
439 // manually
440 //
441 // FIXME: possible +/-1 error here, to check!
442 HRGN hrgn = ::CreateRectRgn(LogicalToDeviceX(x),
443 LogicalToDeviceY(y),
444 LogicalToDeviceX(x + w),
445 LogicalToDeviceY(y + h));
446 if ( !hrgn )
447 {
448 wxLogLastError(_T("CreateRectRgn"));
449 }
450 else
451 {
452 SetClippingHrgn((WXHRGN)hrgn);
453
454 ::DeleteObject(hrgn);
455 }
456 }
457
458 void wxDC::DoSetClippingRegionAsRegion(const wxRegion& region)
459 {
460 SetClippingHrgn(region.GetHRGN());
461 }
462
463 void wxDC::DestroyClippingRegion()
464 {
465 WXMICROWIN_CHECK_HDC
466
467 if (m_clipping && m_hDC)
468 {
469 // TODO: this should restore the previous clipping region,
470 // so that OnPaint processing works correctly, and the update
471 // clipping region doesn't get destroyed after the first
472 // DestroyClippingRegion.
473 HRGN rgn = CreateRectRgn(0, 0, 32000, 32000);
474 ::SelectClipRgn(GetHdc(), rgn);
475 ::DeleteObject(rgn);
476 }
477
478 wxDCBase::DestroyClippingRegion();
479 }
480
481 // ---------------------------------------------------------------------------
482 // query capabilities
483 // ---------------------------------------------------------------------------
484
485 bool wxDC::CanDrawBitmap() const
486 {
487 return true;
488 }
489
490 bool wxDC::CanGetTextExtent() const
491 {
492 #ifdef __WXMICROWIN__
493 // TODO Extend MicroWindows' GetDeviceCaps function
494 return true;
495 #else
496 // What sort of display is it?
497 int technology = ::GetDeviceCaps(GetHdc(), TECHNOLOGY);
498
499 return (technology == DT_RASDISPLAY) || (technology == DT_RASPRINTER);
500 #endif
501 }
502
503 int wxDC::GetDepth() const
504 {
505 WXMICROWIN_CHECK_HDC_RET(16)
506
507 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL);
508 }
509
510 // ---------------------------------------------------------------------------
511 // drawing
512 // ---------------------------------------------------------------------------
513
514 void wxDC::Clear()
515 {
516 WXMICROWIN_CHECK_HDC
517
518 RECT rect;
519 if ( m_canvas )
520 {
521 GetClientRect((HWND) m_canvas->GetHWND(), &rect);
522 }
523 else
524 {
525 // No, I think we should simply ignore this if printing on e.g.
526 // a printer DC.
527 // wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
528 if (!m_selectedBitmap.Ok())
529 return;
530
531 rect.left = 0; rect.top = 0;
532 rect.right = m_selectedBitmap.GetWidth();
533 rect.bottom = m_selectedBitmap.GetHeight();
534 }
535
536 #ifndef __WXWINCE__
537 (void) ::SetMapMode(GetHdc(), MM_TEXT);
538 #endif
539
540 DWORD colour = ::GetBkColor(GetHdc());
541 HBRUSH brush = ::CreateSolidBrush(colour);
542 ::FillRect(GetHdc(), &rect, brush);
543 ::DeleteObject(brush);
544
545 #ifndef __WXWINCE__
546 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
547 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
548
549 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
550
551 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
552 ::SetWindowExtEx(GetHdc(), width, height, NULL);
553 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
554 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
555 #endif
556 }
557
558 bool wxDC::DoFloodFill(wxCoord WXUNUSED_IN_WINCE(x),
559 wxCoord WXUNUSED_IN_WINCE(y),
560 const wxColour& WXUNUSED_IN_WINCE(col),
561 int WXUNUSED_IN_WINCE(style))
562 {
563 #ifdef __WXWINCE__
564 return false;
565 #else
566 WXMICROWIN_CHECK_HDC_RET(false)
567
568 bool success = (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x), YLOG2DEV(y),
569 col.GetPixel(),
570 style == wxFLOOD_SURFACE ? FLOODFILLSURFACE
571 : FLOODFILLBORDER) ) ;
572 if (!success)
573 {
574 // quoting from the MSDN docs:
575 //
576 // Following are some of the reasons this function might fail:
577 //
578 // * The filling could not be completed.
579 // * The specified point has the boundary color specified by the
580 // crColor parameter (if FLOODFILLBORDER was requested).
581 // * The specified point does not have the color specified by
582 // crColor (if FLOODFILLSURFACE was requested)
583 // * The point is outside the clipping region that is, it is not
584 // visible on the device.
585 //
586 wxLogLastError(wxT("ExtFloodFill"));
587 }
588
589 CalcBoundingBox(x, y);
590
591 return success;
592 #endif
593 }
594
595 bool wxDC::DoGetPixel(wxCoord x, wxCoord y, wxColour *col) const
596 {
597 WXMICROWIN_CHECK_HDC_RET(false)
598
599 wxCHECK_MSG( col, false, _T("NULL colour parameter in wxDC::GetPixel") );
600
601 // get the color of the pixel
602 COLORREF pixelcolor = ::GetPixel(GetHdc(), XLOG2DEV(x), YLOG2DEV(y));
603
604 wxRGBToColour(*col, pixelcolor);
605
606 return true;
607 }
608
609 void wxDC::DoCrossHair(wxCoord x, wxCoord y)
610 {
611 WXMICROWIN_CHECK_HDC
612
613 wxCoord x1 = x-VIEWPORT_EXTENT;
614 wxCoord y1 = y-VIEWPORT_EXTENT;
615 wxCoord x2 = x+VIEWPORT_EXTENT;
616 wxCoord y2 = y+VIEWPORT_EXTENT;
617
618 wxDrawLine(GetHdc(), XLOG2DEV(x1), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y));
619 wxDrawLine(GetHdc(), XLOG2DEV(x), YLOG2DEV(y1), XLOG2DEV(x), YLOG2DEV(y2));
620
621 CalcBoundingBox(x1, y1);
622 CalcBoundingBox(x2, y2);
623 }
624
625 void wxDC::DoDrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
626 {
627 WXMICROWIN_CHECK_HDC
628
629 wxDrawLine(GetHdc(), XLOG2DEV(x1), YLOG2DEV(y1), XLOG2DEV(x2), YLOG2DEV(y2));
630
631 CalcBoundingBox(x1, y1);
632 CalcBoundingBox(x2, y2);
633 }
634
635 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
636 // and ending at (x2, y2)
637 void wxDC::DoDrawArc(wxCoord x1, wxCoord y1,
638 wxCoord x2, wxCoord y2,
639 wxCoord xc, wxCoord yc)
640 {
641 #ifdef __WXWINCE__
642 // Slower emulation since WinCE doesn't support Pie and Arc
643 double r = sqrt( (x1-xc)*(x1-xc) + (y1-yc)*(y1-yc) );
644 double sa = acos((x1-xc)/r)/M_PI*180; // between 0 and 180
645 if( y1>yc ) sa = -sa; // below center
646 double ea = atan2(yc-y2, x2-xc)/M_PI*180;
647 DoDrawEllipticArcRot( xc-r, yc-r, 2*r, 2*r, sa, ea );
648 #else
649
650 WXMICROWIN_CHECK_HDC
651
652 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
653
654 double dx = xc - x1;
655 double dy = yc - y1;
656 double radius = (double)sqrt(dx*dx+dy*dy);
657 wxCoord r = (wxCoord)radius;
658
659 // treat the special case of full circle separately
660 if ( x1 == x2 && y1 == y2 )
661 {
662 DrawEllipse(xc - r, yc - r, 2*r, 2*r);
663 return;
664 }
665
666 wxCoord xx1 = XLOG2DEV(x1);
667 wxCoord yy1 = YLOG2DEV(y1);
668 wxCoord xx2 = XLOG2DEV(x2);
669 wxCoord yy2 = YLOG2DEV(y2);
670 wxCoord xxc = XLOG2DEV(xc);
671 wxCoord yyc = YLOG2DEV(yc);
672 wxCoord ray = (wxCoord) sqrt(double((xxc-xx1)*(xxc-xx1)+(yyc-yy1)*(yyc-yy1)));
673
674 wxCoord xxx1 = (wxCoord) (xxc-ray);
675 wxCoord yyy1 = (wxCoord) (yyc-ray);
676 wxCoord xxx2 = (wxCoord) (xxc+ray);
677 wxCoord yyy2 = (wxCoord) (yyc+ray);
678
679 if ( m_brush.Ok() && m_brush.GetStyle() != wxTRANSPARENT )
680 {
681 // Have to add 1 to bottom-right corner of rectangle
682 // to make semi-circles look right (crooked line otherwise).
683 // Unfortunately this is not a reliable method, depends
684 // on the size of shape.
685 // TODO: figure out why this happens!
686 Pie(GetHdc(),xxx1,yyy1,xxx2+1,yyy2+1, xx1,yy1,xx2,yy2);
687 }
688 else
689 {
690 Arc(GetHdc(),xxx1,yyy1,xxx2,yyy2, xx1,yy1,xx2,yy2);
691 }
692
693 CalcBoundingBox(xc - r, yc - r);
694 CalcBoundingBox(xc + r, yc + r);
695 #endif
696 }
697
698 void wxDC::DoDrawCheckMark(wxCoord x1, wxCoord y1,
699 wxCoord width, wxCoord height)
700 {
701 WXMICROWIN_CHECK_HDC
702
703 wxCoord x2 = x1 + width,
704 y2 = y1 + height;
705
706 #if defined(__WIN32__) && !defined(__SYMANTEC__) && !defined(__WXMICROWIN__)
707 RECT rect;
708 rect.left = x1;
709 rect.top = y1;
710 rect.right = x2;
711 rect.bottom = y2;
712
713 #ifdef __WXWINCE__
714 DrawFrameControl(GetHdc(), &rect, DFC_BUTTON, DFCS_BUTTONCHECK);
715 #else
716 DrawFrameControl(GetHdc(), &rect, DFC_MENU, DFCS_MENUCHECK);
717 #endif
718 #else // Symantec-MicroWin
719 // draw a cross
720 HPEN blackPen = ::CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
721 HPEN whiteBrush = (HPEN)::GetStockObject(WHITE_BRUSH);
722 HPEN hPenOld = (HPEN)::SelectObject(GetHdc(), blackPen);
723 HPEN hBrushOld = (HPEN)::SelectObject(GetHdc(), whiteBrush);
724 ::SetROP2(GetHdc(), R2_COPYPEN);
725 Rectangle(GetHdc(), x1, y1, x2, y2);
726 MoveToEx(GetHdc(), x1, y1, NULL);
727 LineTo(GetHdc(), x2, y2);
728 MoveToEx(GetHdc(), x2, y1, NULL);
729 LineTo(GetHdc(), x1, y2);
730 ::SelectObject(GetHdc(), hPenOld);
731 ::SelectObject(GetHdc(), hBrushOld);
732 ::DeleteObject(blackPen);
733 #endif // Win32/Symantec-MicroWin
734
735 CalcBoundingBox(x1, y1);
736 CalcBoundingBox(x2, y2);
737 }
738
739 void wxDC::DoDrawPoint(wxCoord x, wxCoord y)
740 {
741 WXMICROWIN_CHECK_HDC
742
743 COLORREF color = 0x00ffffff;
744 if (m_pen.Ok())
745 {
746 color = m_pen.GetColour().GetPixel();
747 }
748
749 SetPixel(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), color);
750
751 CalcBoundingBox(x, y);
752 }
753
754 void wxDC::DoDrawPolygon(int n,
755 wxPoint points[],
756 wxCoord xoffset,
757 wxCoord yoffset,
758 int WXUNUSED_IN_WINCE(fillStyle))
759 {
760 WXMICROWIN_CHECK_HDC
761
762 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
763
764 // Do things less efficiently if we have offsets
765 if (xoffset != 0 || yoffset != 0)
766 {
767 POINT *cpoints = new POINT[n];
768 int i;
769 for (i = 0; i < n; i++)
770 {
771 cpoints[i].x = (int)(points[i].x + xoffset);
772 cpoints[i].y = (int)(points[i].y + yoffset);
773
774 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
775 }
776 #ifndef __WXWINCE__
777 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
778 #endif
779 (void)Polygon(GetHdc(), cpoints, n);
780 #ifndef __WXWINCE__
781 SetPolyFillMode(GetHdc(),prev);
782 #endif
783 delete[] cpoints;
784 }
785 else
786 {
787 int i;
788 for (i = 0; i < n; i++)
789 CalcBoundingBox(points[i].x, points[i].y);
790
791 #ifndef __WXWINCE__
792 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
793 #endif
794 (void)Polygon(GetHdc(), (POINT*) points, n);
795 #ifndef __WXWINCE__
796 SetPolyFillMode(GetHdc(),prev);
797 #endif
798 }
799 }
800
801 void
802 wxDC::DoDrawPolyPolygon(int n,
803 int count[],
804 wxPoint points[],
805 wxCoord xoffset,
806 wxCoord yoffset,
807 int fillStyle)
808 {
809 #ifdef __WXWINCE__
810 wxDCBase::DoDrawPolyPolygon(n, count, points, xoffset, yoffset, fillStyle);
811 #else
812 WXMICROWIN_CHECK_HDC
813
814 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
815 int i, cnt;
816 for (i = cnt = 0; i < n; i++)
817 cnt += count[i];
818
819 // Do things less efficiently if we have offsets
820 if (xoffset != 0 || yoffset != 0)
821 {
822 POINT *cpoints = new POINT[cnt];
823 for (i = 0; i < cnt; i++)
824 {
825 cpoints[i].x = (int)(points[i].x + xoffset);
826 cpoints[i].y = (int)(points[i].y + yoffset);
827
828 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
829 }
830 #ifndef __WXWINCE__
831 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
832 #endif
833 (void)PolyPolygon(GetHdc(), cpoints, count, n);
834 #ifndef __WXWINCE__
835 SetPolyFillMode(GetHdc(),prev);
836 #endif
837 delete[] cpoints;
838 }
839 else
840 {
841 for (i = 0; i < cnt; i++)
842 CalcBoundingBox(points[i].x, points[i].y);
843
844 #ifndef __WXWINCE__
845 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
846 #endif
847 (void)PolyPolygon(GetHdc(), (POINT*) points, count, n);
848 #ifndef __WXWINCE__
849 SetPolyFillMode(GetHdc(),prev);
850 #endif
851 }
852 #endif
853 // __WXWINCE__
854 }
855
856 void wxDC::DoDrawLines(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset)
857 {
858 WXMICROWIN_CHECK_HDC
859
860 // Do things less efficiently if we have offsets
861 if (xoffset != 0 || yoffset != 0)
862 {
863 POINT *cpoints = new POINT[n];
864 int i;
865 for (i = 0; i < n; i++)
866 {
867 cpoints[i].x = (int)(points[i].x + xoffset);
868 cpoints[i].y = (int)(points[i].y + yoffset);
869
870 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
871 }
872 (void)Polyline(GetHdc(), cpoints, n);
873 delete[] cpoints;
874 }
875 else
876 {
877 int i;
878 for (i = 0; i < n; i++)
879 CalcBoundingBox(points[i].x, points[i].y);
880
881 (void)Polyline(GetHdc(), (POINT*) points, n);
882 }
883 }
884
885 void wxDC::DoDrawRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
886 {
887 WXMICROWIN_CHECK_HDC
888
889 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
890
891 wxCoord x2 = x + width;
892 wxCoord y2 = y + height;
893
894 if ((m_logicalFunction == wxCOPY) && (m_pen.GetStyle() == wxTRANSPARENT))
895 {
896 RECT rect;
897 rect.left = XLOG2DEV(x);
898 rect.top = YLOG2DEV(y);
899 rect.right = XLOG2DEV(x2);
900 rect.bottom = YLOG2DEV(y2);
901 (void)FillRect(GetHdc(), &rect, (HBRUSH)m_brush.GetResourceHandle() );
902 }
903 else
904 {
905 // Windows draws the filled rectangles without outline (i.e. drawn with a
906 // transparent pen) one pixel smaller in both directions and we want them
907 // to have the same size regardless of which pen is used - adjust
908
909 // I wonder if this shouldn´t be done after the LOG2DEV() conversions. RR.
910 if ( m_pen.GetStyle() == wxTRANSPARENT )
911 {
912 // Apparently not needed for WinCE (see e.g. Life! demo)
913 #ifndef __WXWINCE__
914 x2++;
915 y2++;
916 #endif
917 }
918
919 (void)Rectangle(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2));
920 }
921
922
923 CalcBoundingBox(x, y);
924 CalcBoundingBox(x2, y2);
925 }
926
927 void wxDC::DoDrawRoundedRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius)
928 {
929 WXMICROWIN_CHECK_HDC
930
931 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
932
933 // Now, a negative radius value is interpreted to mean
934 // 'the proportion of the smallest X or Y dimension'
935
936 if (radius < 0.0)
937 {
938 double smallest = (width < height) ? width : height;
939 radius = (- radius * smallest);
940 }
941
942 wxCoord x2 = (x+width);
943 wxCoord y2 = (y+height);
944
945 // Windows draws the filled rectangles without outline (i.e. drawn with a
946 // transparent pen) one pixel smaller in both directions and we want them
947 // to have the same size regardless of which pen is used - adjust
948 if ( m_pen.GetStyle() == wxTRANSPARENT )
949 {
950 x2++;
951 y2++;
952 }
953
954 (void)RoundRect(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2),
955 YLOG2DEV(y2), (int) (2*XLOG2DEV(radius)), (int)( 2*YLOG2DEV(radius)));
956
957 CalcBoundingBox(x, y);
958 CalcBoundingBox(x2, y2);
959 }
960
961 void wxDC::DoDrawEllipse(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
962 {
963 WXMICROWIN_CHECK_HDC
964
965 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
966
967 wxCoord x2 = (x+width);
968 wxCoord y2 = (y+height);
969
970 (void)Ellipse(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2));
971
972 CalcBoundingBox(x, y);
973 CalcBoundingBox(x2, y2);
974 }
975
976 #if wxUSE_SPLINES
977 void wxDC::DoDrawSpline(wxList *points)
978 {
979 #ifdef __WXWINCE__
980 // WinCE does not support ::PolyBezier so use generic version
981 wxDCBase::DoDrawSpline(points);
982 #else
983 // quadratic b-spline to cubic bezier spline conversion
984 //
985 // quadratic spline with control points P0,P1,P2
986 // P(s) = P0*(1-s)^2 + P1*2*(1-s)*s + P2*s^2
987 //
988 // bezier spline with control points B0,B1,B2,B3
989 // B(s) = B0*(1-s)^3 + B1*3*(1-s)^2*s + B2*3*(1-s)*s^2 + B3*s^3
990 //
991 // control points of bezier spline calculated from b-spline
992 // B0 = P0
993 // B1 = (2*P1 + P0)/3
994 // B2 = (2*P1 + P2)/3
995 // B3 = P2
996
997 WXMICROWIN_CHECK_HDC
998
999 wxASSERT_MSG( points, wxT("NULL pointer to spline points?") );
1000
1001 const size_t n_points = points->GetCount();
1002 wxASSERT_MSG( n_points > 2 , wxT("incomplete list of spline points?") );
1003
1004 const size_t n_bezier_points = n_points * 3 + 1;
1005 POINT *lppt = (POINT *)malloc(n_bezier_points*sizeof(POINT));
1006 size_t bezier_pos = 0;
1007 wxCoord x1, y1, x2, y2, cx1, cy1, cx4, cy4;
1008
1009 wxList::compatibility_iterator node = points->GetFirst();
1010 wxPoint *p = (wxPoint *)node->GetData();
1011 lppt[ bezier_pos ].x = x1 = p->x;
1012 lppt[ bezier_pos ].y = y1 = p->y;
1013 bezier_pos++;
1014 lppt[ bezier_pos ] = lppt[ bezier_pos-1 ];
1015 bezier_pos++;
1016
1017 node = node->GetNext();
1018 p = (wxPoint *)node->GetData();
1019
1020 x2 = p->x;
1021 y2 = p->y;
1022 cx1 = ( x1 + x2 ) / 2;
1023 cy1 = ( y1 + y2 ) / 2;
1024 lppt[ bezier_pos ].x = XLOG2DEV(cx1);
1025 lppt[ bezier_pos ].y = YLOG2DEV(cy1);
1026 bezier_pos++;
1027 lppt[ bezier_pos ] = lppt[ bezier_pos-1 ];
1028 bezier_pos++;
1029
1030 #if !wxUSE_STL
1031 while ((node = node->GetNext()) != NULL)
1032 #else
1033 while ((node = node->GetNext()))
1034 #endif // !wxUSE_STL
1035 {
1036 p = (wxPoint *)node->GetData();
1037 x1 = x2;
1038 y1 = y2;
1039 x2 = p->x;
1040 y2 = p->y;
1041 cx4 = (x1 + x2) / 2;
1042 cy4 = (y1 + y2) / 2;
1043 // B0 is B3 of previous segment
1044 // B1:
1045 lppt[ bezier_pos ].x = XLOG2DEV((x1*2+cx1)/3);
1046 lppt[ bezier_pos ].y = YLOG2DEV((y1*2+cy1)/3);
1047 bezier_pos++;
1048 // B2:
1049 lppt[ bezier_pos ].x = XLOG2DEV((x1*2+cx4)/3);
1050 lppt[ bezier_pos ].y = YLOG2DEV((y1*2+cy4)/3);
1051 bezier_pos++;
1052 // B3:
1053 lppt[ bezier_pos ].x = XLOG2DEV(cx4);
1054 lppt[ bezier_pos ].y = YLOG2DEV(cy4);
1055 bezier_pos++;
1056 cx1 = cx4;
1057 cy1 = cy4;
1058 }
1059
1060 lppt[ bezier_pos ] = lppt[ bezier_pos-1 ];
1061 bezier_pos++;
1062 lppt[ bezier_pos ].x = XLOG2DEV(x2);
1063 lppt[ bezier_pos ].y = YLOG2DEV(y2);
1064 bezier_pos++;
1065 lppt[ bezier_pos ] = lppt[ bezier_pos-1 ];
1066 bezier_pos++;
1067
1068 ::PolyBezier( GetHdc(), lppt, bezier_pos );
1069
1070 free(lppt);
1071 #endif
1072 }
1073 #endif
1074
1075 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
1076 void wxDC::DoDrawEllipticArc(wxCoord x,wxCoord y,wxCoord w,wxCoord h,double sa,double ea)
1077 {
1078 #ifdef __WXWINCE__
1079 DoDrawEllipticArcRot( x, y, w, h, sa, ea );
1080 #else
1081
1082 WXMICROWIN_CHECK_HDC
1083
1084 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1085
1086 wxCoord x2 = x + w;
1087 wxCoord y2 = y + h;
1088
1089 int rx1 = XLOG2DEV(x+w/2);
1090 int ry1 = YLOG2DEV(y+h/2);
1091 int rx2 = rx1;
1092 int ry2 = ry1;
1093
1094 sa = DegToRad(sa);
1095 ea = DegToRad(ea);
1096
1097 rx1 += (int)(100.0 * abs(w) * cos(sa));
1098 ry1 -= (int)(100.0 * abs(h) * m_signY * sin(sa));
1099 rx2 += (int)(100.0 * abs(w) * cos(ea));
1100 ry2 -= (int)(100.0 * abs(h) * m_signY * sin(ea));
1101
1102 // draw pie with NULL_PEN first and then outline otherwise a line is
1103 // drawn from the start and end points to the centre
1104 HPEN hpenOld = (HPEN) ::SelectObject(GetHdc(), (HPEN) ::GetStockObject(NULL_PEN));
1105 if (m_signY > 0)
1106 {
1107 (void)Pie(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2)+1, YLOG2DEV(y2)+1,
1108 rx1, ry1, rx2, ry2);
1109 }
1110 else
1111 {
1112 (void)Pie(GetHdc(), XLOG2DEV(x), YLOG2DEV(y)-1, XLOG2DEV(x2)+1, YLOG2DEV(y2),
1113 rx1, ry1-1, rx2, ry2-1);
1114 }
1115
1116 ::SelectObject(GetHdc(), hpenOld);
1117
1118 (void)Arc(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2),
1119 rx1, ry1, rx2, ry2);
1120
1121 CalcBoundingBox(x, y);
1122 CalcBoundingBox(x2, y2);
1123 #endif
1124 }
1125
1126 void wxDC::DoDrawIcon(const wxIcon& icon, wxCoord x, wxCoord y)
1127 {
1128 WXMICROWIN_CHECK_HDC
1129
1130 wxCHECK_RET( icon.Ok(), wxT("invalid icon in DrawIcon") );
1131
1132 #ifdef __WIN32__
1133 ::DrawIconEx(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), GetHiconOf(icon), icon.GetWidth(), icon.GetHeight(), 0, NULL, DI_NORMAL);
1134 #else
1135 ::DrawIcon(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), GetHiconOf(icon));
1136 #endif
1137
1138 CalcBoundingBox(x, y);
1139 CalcBoundingBox(x + icon.GetWidth(), y + icon.GetHeight());
1140 }
1141
1142 void wxDC::DoDrawBitmap( const wxBitmap &bmp, wxCoord x, wxCoord y, bool useMask )
1143 {
1144 WXMICROWIN_CHECK_HDC
1145
1146 wxCHECK_RET( bmp.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
1147
1148 int width = bmp.GetWidth(),
1149 height = bmp.GetHeight();
1150
1151 HBITMAP hbmpMask = 0;
1152
1153 #if wxUSE_PALETTE
1154 HPALETTE oldPal = 0;
1155 #endif // wxUSE_PALETTE
1156
1157 if ( bmp.HasAlpha() )
1158 {
1159 MemoryHDC hdcMem;
1160 SelectInHDC select(hdcMem, GetHbitmapOf(bmp));
1161
1162 if ( AlphaBlt(GetHdc(), x, y, width, height, 0, 0, hdcMem, bmp) )
1163 return;
1164 }
1165
1166 if ( useMask )
1167 {
1168 wxMask *mask = bmp.GetMask();
1169 if ( mask )
1170 hbmpMask = (HBITMAP)mask->GetMaskBitmap();
1171
1172 if ( !hbmpMask )
1173 {
1174 // don't give assert here because this would break existing
1175 // programs - just silently ignore useMask parameter
1176 useMask = false;
1177 }
1178 }
1179 if ( useMask )
1180 {
1181 #ifdef __WIN32__
1182 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1183 // points
1184 // On some systems, MaskBlt succeeds yet is much much slower
1185 // than the wxWidgets fall-back implementation. So we need
1186 // to be able to switch this on and off at runtime.
1187 bool ok = false;
1188 #if wxUSE_SYSTEM_OPTIONS
1189 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1190 #endif
1191 {
1192 HDC cdc = GetHdc();
1193 HDC hdcMem = ::CreateCompatibleDC(GetHdc());
1194 HGDIOBJ hOldBitmap = ::SelectObject(hdcMem, GetHbitmapOf(bmp));
1195 #if wxUSE_PALETTE
1196 wxPalette *pal = bmp.GetPalette();
1197 if ( pal && ::GetDeviceCaps(cdc,BITSPIXEL) <= 8 )
1198 {
1199 oldPal = ::SelectPalette(hdcMem, GetHpaletteOf(*pal), FALSE);
1200 ::RealizePalette(hdcMem);
1201 }
1202 #endif // wxUSE_PALETTE
1203
1204 ok = ::MaskBlt(cdc, x, y, width, height,
1205 hdcMem, 0, 0,
1206 hbmpMask, 0, 0,
1207 MAKEROP4(SRCCOPY, DSTCOPY)) != 0;
1208
1209 #if wxUSE_PALETTE
1210 if (oldPal)
1211 ::SelectPalette(hdcMem, oldPal, FALSE);
1212 #endif // wxUSE_PALETTE
1213
1214 ::SelectObject(hdcMem, hOldBitmap);
1215 ::DeleteDC(hdcMem);
1216 }
1217
1218 if ( !ok )
1219 #endif // Win32
1220 {
1221 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
1222 // level
1223 wxMemoryDC memDC;
1224 memDC.SelectObject(bmp);
1225
1226 Blit(x, y, width, height, &memDC, 0, 0, wxCOPY, useMask);
1227
1228 memDC.SelectObject(wxNullBitmap);
1229 }
1230 }
1231 else // no mask, just use BitBlt()
1232 {
1233 HDC cdc = GetHdc();
1234 HDC memdc = ::CreateCompatibleDC( cdc );
1235 HBITMAP hbitmap = (HBITMAP) bmp.GetHBITMAP( );
1236
1237 wxASSERT_MSG( hbitmap, wxT("bitmap is ok but HBITMAP is NULL?") );
1238
1239 COLORREF old_textground = ::GetTextColor(GetHdc());
1240 COLORREF old_background = ::GetBkColor(GetHdc());
1241 if (m_textForegroundColour.Ok())
1242 {
1243 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1244 }
1245 if (m_textBackgroundColour.Ok())
1246 {
1247 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1248 }
1249
1250 #if wxUSE_PALETTE
1251 wxPalette *pal = bmp.GetPalette();
1252 if ( pal && ::GetDeviceCaps(cdc,BITSPIXEL) <= 8 )
1253 {
1254 oldPal = ::SelectPalette(memdc, GetHpaletteOf(*pal), FALSE);
1255 ::RealizePalette(memdc);
1256 }
1257 #endif // wxUSE_PALETTE
1258
1259 HGDIOBJ hOldBitmap = ::SelectObject( memdc, hbitmap );
1260 ::BitBlt( cdc, x, y, width, height, memdc, 0, 0, SRCCOPY);
1261
1262 #if wxUSE_PALETTE
1263 if (oldPal)
1264 ::SelectPalette(memdc, oldPal, FALSE);
1265 #endif // wxUSE_PALETTE
1266
1267 ::SelectObject( memdc, hOldBitmap );
1268 ::DeleteDC( memdc );
1269
1270 ::SetTextColor(GetHdc(), old_textground);
1271 ::SetBkColor(GetHdc(), old_background);
1272 }
1273 }
1274
1275 void wxDC::DoDrawText(const wxString& text, wxCoord x, wxCoord y)
1276 {
1277 WXMICROWIN_CHECK_HDC
1278
1279 DrawAnyText(text, x, y);
1280
1281 // update the bounding box
1282 CalcBoundingBox(x, y);
1283
1284 wxCoord w, h;
1285 GetTextExtent(text, &w, &h);
1286 CalcBoundingBox(x + w, y + h);
1287 }
1288
1289 void wxDC::DrawAnyText(const wxString& text, wxCoord x, wxCoord y)
1290 {
1291 WXMICROWIN_CHECK_HDC
1292
1293 // prepare for drawing the text
1294 if ( m_textForegroundColour.Ok() )
1295 SetTextColor(GetHdc(), m_textForegroundColour.GetPixel());
1296
1297 DWORD old_background = 0;
1298 if ( m_textBackgroundColour.Ok() )
1299 {
1300 old_background = SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1301 }
1302
1303 SetBkMode(GetHdc(), m_backgroundMode == wxTRANSPARENT ? TRANSPARENT
1304 : OPAQUE);
1305
1306 #ifdef __WXWINCE__
1307 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), 0, NULL,
1308 text.c_str(), text.length(), NULL) == 0 )
1309 {
1310 wxLogLastError(wxT("TextOut"));
1311 }
1312 #else
1313 if ( ::TextOut(GetHdc(), XLOG2DEV(x), YLOG2DEV(y),
1314 text.c_str(), text.length()) == 0 )
1315 {
1316 wxLogLastError(wxT("TextOut"));
1317 }
1318 #endif
1319
1320 // restore the old parameters (text foreground colour may be left because
1321 // it never is set to anything else, but background should remain
1322 // transparent even if we just drew an opaque string)
1323 if ( m_textBackgroundColour.Ok() )
1324 (void)SetBkColor(GetHdc(), old_background);
1325
1326 SetBkMode(GetHdc(), TRANSPARENT);
1327 }
1328
1329 void wxDC::DoDrawRotatedText(const wxString& text,
1330 wxCoord x, wxCoord y,
1331 double angle)
1332 {
1333 WXMICROWIN_CHECK_HDC
1334
1335 // we test that we have some font because otherwise we should still use the
1336 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1337 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1338 // font for drawing rotated fonts unfortunately)
1339 if ( (angle == 0.0) && m_font.Ok() )
1340 {
1341 DoDrawText(text, x, y);
1342 }
1343 #ifndef __WXMICROWIN__
1344 else
1345 {
1346 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1347 // because it's not TrueType and so can't have non zero
1348 // orientation/escapement under Win9x
1349 wxFont font = m_font.Ok() ? m_font : *wxSWISS_FONT;
1350 HFONT hfont = (HFONT)font.GetResourceHandle();
1351 LOGFONT lf;
1352 if ( ::GetObject(hfont, sizeof(lf), &lf) == 0 )
1353 {
1354 wxLogLastError(wxT("GetObject(hfont)"));
1355 }
1356
1357 // GDI wants the angle in tenth of degree
1358 long angle10 = (long)(angle * 10);
1359 lf.lfEscapement = angle10;
1360 lf. lfOrientation = angle10;
1361
1362 hfont = ::CreateFontIndirect(&lf);
1363 if ( !hfont )
1364 {
1365 wxLogLastError(wxT("CreateFont"));
1366 }
1367 else
1368 {
1369 HFONT hfontOld = (HFONT)::SelectObject(GetHdc(), hfont);
1370
1371 DrawAnyText(text, x, y);
1372
1373 (void)::SelectObject(GetHdc(), hfontOld);
1374 (void)::DeleteObject(hfont);
1375 }
1376
1377 // call the bounding box by adding all four vertices of the rectangle
1378 // containing the text to it (simpler and probably not slower than
1379 // determining which of them is really topmost/leftmost/...)
1380 wxCoord w, h;
1381 GetTextExtent(text, &w, &h);
1382
1383 double rad = DegToRad(angle);
1384
1385 // "upper left" and "upper right"
1386 CalcBoundingBox(x, y);
1387 CalcBoundingBox(x + wxCoord(w*cos(rad)), y - wxCoord(w*sin(rad)));
1388
1389 // "bottom left" and "bottom right"
1390 x += (wxCoord)(h*sin(rad));
1391 y += (wxCoord)(h*cos(rad));
1392 CalcBoundingBox(x, y);
1393 CalcBoundingBox(x + wxCoord(w*cos(rad)), y - wxCoord(w*sin(rad)));
1394 }
1395 #endif
1396 }
1397
1398 // ---------------------------------------------------------------------------
1399 // set GDI objects
1400 // ---------------------------------------------------------------------------
1401
1402 #if wxUSE_PALETTE
1403
1404 void wxDC::DoSelectPalette(bool realize)
1405 {
1406 WXMICROWIN_CHECK_HDC
1407
1408 // Set the old object temporarily, in case the assignment deletes an object
1409 // that's not yet selected out.
1410 if (m_oldPalette)
1411 {
1412 ::SelectPalette(GetHdc(), (HPALETTE) m_oldPalette, FALSE);
1413 m_oldPalette = 0;
1414 }
1415
1416 if ( m_palette.Ok() )
1417 {
1418 HPALETTE oldPal = ::SelectPalette(GetHdc(),
1419 GetHpaletteOf(m_palette),
1420 false);
1421 if (!m_oldPalette)
1422 m_oldPalette = (WXHPALETTE) oldPal;
1423
1424 if (realize)
1425 ::RealizePalette(GetHdc());
1426 }
1427 }
1428
1429 void wxDC::SetPalette(const wxPalette& palette)
1430 {
1431 if ( palette.Ok() )
1432 {
1433 m_palette = palette;
1434 DoSelectPalette(true);
1435 }
1436 }
1437
1438 void wxDC::InitializePalette()
1439 {
1440 if ( wxDisplayDepth() <= 8 )
1441 {
1442 // look for any window or parent that has a custom palette. If any has
1443 // one then we need to use it in drawing operations
1444 wxWindow *win = m_canvas->GetAncestorWithCustomPalette();
1445
1446 m_hasCustomPalette = win && win->HasCustomPalette();
1447 if ( m_hasCustomPalette )
1448 {
1449 m_palette = win->GetPalette();
1450
1451 // turn on MSW translation for this palette
1452 DoSelectPalette();
1453 }
1454 }
1455 }
1456
1457 #endif // wxUSE_PALETTE
1458
1459 // SetFont/Pen/Brush() really ask to be implemented as a single template
1460 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1461
1462 void wxDC::SetFont(const wxFont& font)
1463 {
1464 WXMICROWIN_CHECK_HDC
1465
1466 if ( font == m_font )
1467 return;
1468
1469 if ( font.Ok() )
1470 {
1471 HGDIOBJ hfont = ::SelectObject(GetHdc(), GetHfontOf(font));
1472 if ( hfont == HGDI_ERROR )
1473 {
1474 wxLogLastError(_T("SelectObject(font)"));
1475 }
1476 else // selected ok
1477 {
1478 if ( !m_oldFont )
1479 m_oldFont = (WXHPEN)hfont;
1480
1481 m_font = font;
1482 }
1483 }
1484 else // invalid font, reset the current font
1485 {
1486 if ( m_oldFont )
1487 {
1488 if ( ::SelectObject(GetHdc(), (HPEN) m_oldFont) == HGDI_ERROR )
1489 {
1490 wxLogLastError(_T("SelectObject(old font)"));
1491 }
1492
1493 m_oldFont = 0;
1494 }
1495
1496 m_font = wxNullFont;
1497 }
1498 }
1499
1500 void wxDC::SetPen(const wxPen& pen)
1501 {
1502 WXMICROWIN_CHECK_HDC
1503
1504 if ( pen == m_pen )
1505 return;
1506
1507 if ( pen.Ok() )
1508 {
1509 HGDIOBJ hpen = ::SelectObject(GetHdc(), GetHpenOf(pen));
1510 if ( hpen == HGDI_ERROR )
1511 {
1512 wxLogLastError(_T("SelectObject(pen)"));
1513 }
1514 else // selected ok
1515 {
1516 if ( !m_oldPen )
1517 m_oldPen = (WXHPEN)hpen;
1518
1519 m_pen = pen;
1520 }
1521 }
1522 else // invalid pen, reset the current pen
1523 {
1524 if ( m_oldPen )
1525 {
1526 if ( ::SelectObject(GetHdc(), (HPEN) m_oldPen) == HGDI_ERROR )
1527 {
1528 wxLogLastError(_T("SelectObject(old pen)"));
1529 }
1530
1531 m_oldPen = 0;
1532 }
1533
1534 m_pen = wxNullPen;
1535 }
1536 }
1537
1538 void wxDC::SetBrush(const wxBrush& brush)
1539 {
1540 WXMICROWIN_CHECK_HDC
1541
1542 if ( brush == m_brush )
1543 return;
1544
1545 if ( brush.Ok() )
1546 {
1547 // we must make sure the brush is aligned with the logical coordinates
1548 // before selecting it
1549 wxBitmap *stipple = brush.GetStipple();
1550 if ( stipple && stipple->Ok() )
1551 {
1552 if ( !::SetBrushOrgEx
1553 (
1554 GetHdc(),
1555 m_deviceOriginX % stipple->GetWidth(),
1556 m_deviceOriginY % stipple->GetHeight(),
1557 NULL // [out] previous brush origin
1558 ) )
1559 {
1560 wxLogLastError(_T("SetBrushOrgEx()"));
1561 }
1562 }
1563
1564 HGDIOBJ hbrush = ::SelectObject(GetHdc(), GetHbrushOf(brush));
1565 if ( hbrush == HGDI_ERROR )
1566 {
1567 wxLogLastError(_T("SelectObject(brush)"));
1568 }
1569 else // selected ok
1570 {
1571 if ( !m_oldBrush )
1572 m_oldBrush = (WXHPEN)hbrush;
1573
1574 m_brush = brush;
1575 }
1576 }
1577 else // invalid brush, reset the current brush
1578 {
1579 if ( m_oldBrush )
1580 {
1581 if ( ::SelectObject(GetHdc(), (HPEN) m_oldBrush) == HGDI_ERROR )
1582 {
1583 wxLogLastError(_T("SelectObject(old brush)"));
1584 }
1585
1586 m_oldBrush = 0;
1587 }
1588
1589 m_brush = wxNullBrush;
1590 }
1591 }
1592
1593 void wxDC::SetBackground(const wxBrush& brush)
1594 {
1595 WXMICROWIN_CHECK_HDC
1596
1597 m_backgroundBrush = brush;
1598
1599 if ( m_backgroundBrush.Ok() )
1600 {
1601 (void)SetBkColor(GetHdc(), m_backgroundBrush.GetColour().GetPixel());
1602 }
1603 }
1604
1605 void wxDC::SetBackgroundMode(int mode)
1606 {
1607 WXMICROWIN_CHECK_HDC
1608
1609 m_backgroundMode = mode;
1610
1611 // SetBackgroundColour now only refers to text background
1612 // and m_backgroundMode is used there
1613 }
1614
1615 void wxDC::SetLogicalFunction(int function)
1616 {
1617 WXMICROWIN_CHECK_HDC
1618
1619 m_logicalFunction = function;
1620
1621 SetRop(m_hDC);
1622 }
1623
1624 void wxDC::SetRop(WXHDC dc)
1625 {
1626 if ( !dc || m_logicalFunction < 0 )
1627 return;
1628
1629 int rop;
1630
1631 switch (m_logicalFunction)
1632 {
1633 case wxCLEAR: rop = R2_BLACK; break;
1634 case wxXOR: rop = R2_XORPEN; break;
1635 case wxINVERT: rop = R2_NOT; break;
1636 case wxOR_REVERSE: rop = R2_MERGEPENNOT; break;
1637 case wxAND_REVERSE: rop = R2_MASKPENNOT; break;
1638 case wxCOPY: rop = R2_COPYPEN; break;
1639 case wxAND: rop = R2_MASKPEN; break;
1640 case wxAND_INVERT: rop = R2_MASKNOTPEN; break;
1641 case wxNO_OP: rop = R2_NOP; break;
1642 case wxNOR: rop = R2_NOTMERGEPEN; break;
1643 case wxEQUIV: rop = R2_NOTXORPEN; break;
1644 case wxSRC_INVERT: rop = R2_NOTCOPYPEN; break;
1645 case wxOR_INVERT: rop = R2_MERGENOTPEN; break;
1646 case wxNAND: rop = R2_NOTMASKPEN; break;
1647 case wxOR: rop = R2_MERGEPEN; break;
1648 case wxSET: rop = R2_WHITE; break;
1649
1650 default:
1651 wxFAIL_MSG( wxT("unsupported logical function") );
1652 return;
1653 }
1654
1655 SetROP2(GetHdc(), rop);
1656 }
1657
1658 bool wxDC::StartDoc(const wxString& WXUNUSED(message))
1659 {
1660 // We might be previewing, so return true to let it continue.
1661 return true;
1662 }
1663
1664 void wxDC::EndDoc()
1665 {
1666 }
1667
1668 void wxDC::StartPage()
1669 {
1670 }
1671
1672 void wxDC::EndPage()
1673 {
1674 }
1675
1676 // ---------------------------------------------------------------------------
1677 // text metrics
1678 // ---------------------------------------------------------------------------
1679
1680 wxCoord wxDC::GetCharHeight() const
1681 {
1682 WXMICROWIN_CHECK_HDC_RET(0)
1683
1684 TEXTMETRIC lpTextMetric;
1685
1686 GetTextMetrics(GetHdc(), &lpTextMetric);
1687
1688 return lpTextMetric.tmHeight;
1689 }
1690
1691 wxCoord wxDC::GetCharWidth() const
1692 {
1693 WXMICROWIN_CHECK_HDC_RET(0)
1694
1695 TEXTMETRIC lpTextMetric;
1696
1697 GetTextMetrics(GetHdc(), &lpTextMetric);
1698
1699 return lpTextMetric.tmAveCharWidth;
1700 }
1701
1702 void wxDC::DoGetTextExtent(const wxString& string, wxCoord *x, wxCoord *y,
1703 wxCoord *descent, wxCoord *externalLeading,
1704 wxFont *font) const
1705 {
1706 #ifdef __WXMICROWIN__
1707 if (!GetHDC())
1708 {
1709 if (x) *x = 0;
1710 if (y) *y = 0;
1711 if (descent) *descent = 0;
1712 if (externalLeading) *externalLeading = 0;
1713 return;
1714 }
1715 #endif // __WXMICROWIN__
1716
1717 HFONT hfontOld;
1718 if ( font )
1719 {
1720 wxASSERT_MSG( font->Ok(), _T("invalid font in wxDC::GetTextExtent") );
1721
1722 hfontOld = (HFONT)::SelectObject(GetHdc(), GetHfontOf(*font));
1723 }
1724 else // don't change the font
1725 {
1726 hfontOld = 0;
1727 }
1728
1729 SIZE sizeRect;
1730 TEXTMETRIC tm;
1731
1732 ::GetTextExtentPoint32(GetHdc(), string, string.length(), &sizeRect);
1733 GetTextMetrics(GetHdc(), &tm);
1734
1735 if (x)
1736 *x = sizeRect.cx;
1737 if (y)
1738 *y = sizeRect.cy;
1739 if (descent)
1740 *descent = tm.tmDescent;
1741 if (externalLeading)
1742 *externalLeading = tm.tmExternalLeading;
1743
1744 if ( hfontOld )
1745 {
1746 ::SelectObject(GetHdc(), hfontOld);
1747 }
1748 }
1749
1750
1751 // Each element of the array will be the width of the string up to and
1752 // including the coresoponding character in text.
1753
1754 bool wxDC::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const
1755 {
1756 static int maxLenText = -1;
1757 static int maxWidth = -1;
1758 int fit = 0;
1759 SIZE sz = {0,0};
1760 int stlen = text.Length();
1761
1762 if (maxLenText == -1)
1763 {
1764 // Win9x and WinNT+ have different limits
1765 int version = wxGetOsVersion();
1766 maxLenText = version == wxWINDOWS_NT ? 65535 : 8192;
1767 maxWidth = version == wxWINDOWS_NT ? INT_MAX : 32767;
1768 }
1769
1770 widths.Empty();
1771 widths.Add(0, stlen); // fill the array with zeros
1772 if (stlen == 0)
1773 return true;
1774
1775 if (!::GetTextExtentExPoint(GetHdc(),
1776 text.c_str(), // string to check
1777 wxMin(stlen, maxLenText),
1778 maxWidth,
1779 &fit, // [out] count of chars
1780 // that will fit
1781 &widths[0], // array to fill
1782 &sz))
1783 {
1784 // API failed
1785 wxLogLastError(wxT("GetTextExtentExPoint"));
1786 return false;
1787 }
1788
1789 return true;
1790 }
1791
1792
1793
1794
1795 void wxDC::SetMapMode(int mode)
1796 {
1797 WXMICROWIN_CHECK_HDC
1798
1799 m_mappingMode = mode;
1800
1801 if ( mode == wxMM_TEXT )
1802 {
1803 m_logicalScaleX =
1804 m_logicalScaleY = 1.0;
1805 }
1806 else // need to do some calculations
1807 {
1808 int pixel_width = ::GetDeviceCaps(GetHdc(), HORZRES),
1809 pixel_height = ::GetDeviceCaps(GetHdc(), VERTRES),
1810 mm_width = ::GetDeviceCaps(GetHdc(), HORZSIZE),
1811 mm_height = ::GetDeviceCaps(GetHdc(), VERTSIZE);
1812
1813 if ( (mm_width == 0) || (mm_height == 0) )
1814 {
1815 // we can't calculate mm2pixels[XY] then!
1816 return;
1817 }
1818
1819 double mm2pixelsX = (double)pixel_width / mm_width,
1820 mm2pixelsY = (double)pixel_height / mm_height;
1821
1822 switch (mode)
1823 {
1824 case wxMM_TWIPS:
1825 m_logicalScaleX = twips2mm * mm2pixelsX;
1826 m_logicalScaleY = twips2mm * mm2pixelsY;
1827 break;
1828
1829 case wxMM_POINTS:
1830 m_logicalScaleX = pt2mm * mm2pixelsX;
1831 m_logicalScaleY = pt2mm * mm2pixelsY;
1832 break;
1833
1834 case wxMM_METRIC:
1835 m_logicalScaleX = mm2pixelsX;
1836 m_logicalScaleY = mm2pixelsY;
1837 break;
1838
1839 case wxMM_LOMETRIC:
1840 m_logicalScaleX = mm2pixelsX / 10.0;
1841 m_logicalScaleY = mm2pixelsY / 10.0;
1842 break;
1843
1844 default:
1845 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1846 }
1847 }
1848
1849 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1850 // cases we could do with MM_TEXT and in the remaining 0.9% with
1851 // MM_ISOTROPIC (TODO!)
1852 #ifndef __WXWINCE__
1853 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
1854
1855 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
1856 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
1857
1858 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
1859 ::SetWindowExtEx(GetHdc(), width, height, NULL);
1860
1861 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX, m_deviceOriginY, NULL);
1862 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX, m_logicalOriginY, NULL);
1863 #endif
1864 }
1865
1866 void wxDC::SetUserScale(double x, double y)
1867 {
1868 WXMICROWIN_CHECK_HDC
1869
1870 if ( x == m_userScaleX && y == m_userScaleY )
1871 return;
1872
1873 m_userScaleX = x;
1874 m_userScaleY = y;
1875
1876 this->SetMapMode(m_mappingMode);
1877 }
1878
1879 void wxDC::SetAxisOrientation(bool WXUNUSED_IN_WINCE(xLeftRight),
1880 bool WXUNUSED_IN_WINCE(yBottomUp))
1881 {
1882 WXMICROWIN_CHECK_HDC
1883
1884 #ifndef __WXWINCE__
1885 int signX = xLeftRight ? 1 : -1,
1886 signY = yBottomUp ? -1 : 1;
1887
1888 if ( signX != m_signX || signY != m_signY )
1889 {
1890 m_signX = signX;
1891 m_signY = signY;
1892
1893 SetMapMode(m_mappingMode);
1894 }
1895 #endif
1896 }
1897
1898 void wxDC::SetSystemScale(double x, double y)
1899 {
1900 WXMICROWIN_CHECK_HDC
1901
1902 if ( x == m_scaleX && y == m_scaleY )
1903 return;
1904
1905 m_scaleX = x;
1906 m_scaleY = y;
1907
1908 #ifndef __WXWINCE__
1909 SetMapMode(m_mappingMode);
1910 #endif
1911 }
1912
1913 void wxDC::SetLogicalOrigin(wxCoord x, wxCoord y)
1914 {
1915 WXMICROWIN_CHECK_HDC
1916
1917 if ( x == m_logicalOriginX && y == m_logicalOriginY )
1918 return;
1919
1920 m_logicalOriginX = x;
1921 m_logicalOriginY = y;
1922
1923 #ifndef __WXWINCE__
1924 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
1925 #endif
1926 }
1927
1928 void wxDC::SetDeviceOrigin(wxCoord x, wxCoord y)
1929 {
1930 WXMICROWIN_CHECK_HDC
1931
1932 if ( x == m_deviceOriginX && y == m_deviceOriginY )
1933 return;
1934
1935 m_deviceOriginX = x;
1936 m_deviceOriginY = y;
1937
1938 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
1939 }
1940
1941 // ---------------------------------------------------------------------------
1942 // coordinates transformations
1943 // ---------------------------------------------------------------------------
1944
1945 wxCoord wxDCBase::DeviceToLogicalX(wxCoord x) const
1946 {
1947 return DeviceToLogicalXRel(x - m_deviceOriginX)*m_signX + m_logicalOriginX;
1948 }
1949
1950 wxCoord wxDCBase::DeviceToLogicalXRel(wxCoord x) const
1951 {
1952 // axis orientation is not taken into account for conversion of a distance
1953 return (wxCoord)(x / (m_logicalScaleX*m_userScaleX*m_scaleX));
1954 }
1955
1956 wxCoord wxDCBase::DeviceToLogicalY(wxCoord y) const
1957 {
1958 return DeviceToLogicalYRel(y - m_deviceOriginY)*m_signY + m_logicalOriginY;
1959 }
1960
1961 wxCoord wxDCBase::DeviceToLogicalYRel(wxCoord y) const
1962 {
1963 // axis orientation is not taken into account for conversion of a distance
1964 return (wxCoord)( y / (m_logicalScaleY*m_userScaleY*m_scaleY));
1965 }
1966
1967 wxCoord wxDCBase::LogicalToDeviceX(wxCoord x) const
1968 {
1969 return LogicalToDeviceXRel(x - m_logicalOriginX)*m_signX + m_deviceOriginX;
1970 }
1971
1972 wxCoord wxDCBase::LogicalToDeviceXRel(wxCoord x) const
1973 {
1974 // axis orientation is not taken into account for conversion of a distance
1975 return (wxCoord) (x*m_logicalScaleX*m_userScaleX*m_scaleX);
1976 }
1977
1978 wxCoord wxDCBase::LogicalToDeviceY(wxCoord y) const
1979 {
1980 return LogicalToDeviceYRel(y - m_logicalOriginY)*m_signY + m_deviceOriginY;
1981 }
1982
1983 wxCoord wxDCBase::LogicalToDeviceYRel(wxCoord y) const
1984 {
1985 // axis orientation is not taken into account for conversion of a distance
1986 return (wxCoord) (y*m_logicalScaleY*m_userScaleY*m_scaleY);
1987 }
1988
1989 // ---------------------------------------------------------------------------
1990 // bit blit
1991 // ---------------------------------------------------------------------------
1992
1993 bool wxDC::DoBlit(wxCoord xdest, wxCoord ydest,
1994 wxCoord width, wxCoord height,
1995 wxDC *source, wxCoord xsrc, wxCoord ysrc,
1996 int rop, bool useMask,
1997 wxCoord xsrcMask, wxCoord ysrcMask)
1998 {
1999 wxCHECK_MSG( source, false, _T("wxDC::Blit(): NULL wxDC pointer") );
2000
2001 WXMICROWIN_CHECK_HDC_RET(false)
2002
2003 // if either the source or destination has alpha channel, we must use
2004 // AlphaBlt() as other function don't handle it correctly
2005 const wxBitmap& bmpSrc = source->m_selectedBitmap;
2006 if ( bmpSrc.Ok() && (bmpSrc.HasAlpha() ||
2007 (m_selectedBitmap.Ok() && m_selectedBitmap.HasAlpha())) )
2008 {
2009 if ( AlphaBlt(GetHdc(), xdest, ydest, width, height,
2010 xsrc, ysrc, GetHdcOf(*source), bmpSrc) )
2011 return true;
2012 }
2013
2014 wxMask *mask = NULL;
2015 if ( useMask )
2016 {
2017 mask = bmpSrc.GetMask();
2018
2019 if ( !(bmpSrc.Ok() && mask && mask->GetMaskBitmap()) )
2020 {
2021 // don't give assert here because this would break existing
2022 // programs - just silently ignore useMask parameter
2023 useMask = false;
2024 }
2025 }
2026
2027 if (xsrcMask == -1 && ysrcMask == -1)
2028 {
2029 xsrcMask = xsrc; ysrcMask = ysrc;
2030 }
2031
2032 COLORREF old_textground = ::GetTextColor(GetHdc());
2033 COLORREF old_background = ::GetBkColor(GetHdc());
2034 if (m_textForegroundColour.Ok())
2035 {
2036 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
2037 }
2038 if (m_textBackgroundColour.Ok())
2039 {
2040 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
2041 }
2042
2043 DWORD dwRop;
2044 switch (rop)
2045 {
2046 case wxXOR: dwRop = SRCINVERT; break;
2047 case wxINVERT: dwRop = DSTINVERT; break;
2048 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
2049 case wxAND_REVERSE: dwRop = SRCERASE; break;
2050 case wxCLEAR: dwRop = BLACKNESS; break;
2051 case wxSET: dwRop = WHITENESS; break;
2052 case wxOR_INVERT: dwRop = MERGEPAINT; break;
2053 case wxAND: dwRop = SRCAND; break;
2054 case wxOR: dwRop = SRCPAINT; break;
2055 case wxEQUIV: dwRop = 0x00990066; break;
2056 case wxNAND: dwRop = 0x007700E6; break;
2057 case wxAND_INVERT: dwRop = 0x00220326; break;
2058 case wxCOPY: dwRop = SRCCOPY; break;
2059 case wxNO_OP: dwRop = DSTCOPY; break;
2060 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
2061 case wxNOR: dwRop = NOTSRCCOPY; break;
2062 default:
2063 wxFAIL_MSG( wxT("unsupported logical function") );
2064 return false;
2065 }
2066
2067 bool success = false;
2068
2069 if (useMask)
2070 {
2071 #ifdef __WIN32__
2072 // we want the part of the image corresponding to the mask to be
2073 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2074 // meaning of fg and bg is inverted which corresponds to wxWin notion
2075 // of the mask which is also contrary to the Windows one)
2076
2077 // On some systems, MaskBlt succeeds yet is much much slower
2078 // than the wxWidgets fall-back implementation. So we need
2079 // to be able to switch this on and off at runtime.
2080 #if wxUSE_SYSTEM_OPTIONS
2081 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
2082 #endif
2083 {
2084 success = ::MaskBlt
2085 (
2086 GetHdc(),
2087 xdest, ydest, width, height,
2088 GetHdcOf(*source),
2089 xsrc, ysrc,
2090 (HBITMAP)mask->GetMaskBitmap(),
2091 xsrcMask, ysrcMask,
2092 MAKEROP4(dwRop, DSTCOPY)
2093 ) != 0;
2094 }
2095
2096 if ( !success )
2097 #endif // Win32
2098 {
2099 // Blit bitmap with mask
2100 HDC dc_mask ;
2101 HDC dc_buffer ;
2102 HBITMAP buffer_bmap ;
2103
2104 #if wxUSE_DC_CACHEING
2105 // create a temp buffer bitmap and DCs to access it and the mask
2106 wxDCCacheEntry* dcCacheEntry1 = FindDCInCache(NULL, source->GetHDC());
2107 dc_mask = (HDC) dcCacheEntry1->m_dc;
2108
2109 wxDCCacheEntry* dcCacheEntry2 = FindDCInCache(dcCacheEntry1, GetHDC());
2110 dc_buffer = (HDC) dcCacheEntry2->m_dc;
2111
2112 wxDCCacheEntry* bitmapCacheEntry = FindBitmapInCache(GetHDC(),
2113 width, height);
2114
2115 buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap;
2116 #else // !wxUSE_DC_CACHEING
2117 // create a temp buffer bitmap and DCs to access it and the mask
2118 dc_mask = ::CreateCompatibleDC(GetHdcOf(*source));
2119 dc_buffer = ::CreateCompatibleDC(GetHdc());
2120 buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), width, height);
2121 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2122 HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
2123 HGDIOBJ hOldBufferBitmap = ::SelectObject(dc_buffer, buffer_bmap);
2124
2125 // copy dest to buffer
2126 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
2127 GetHdc(), xdest, ydest, SRCCOPY) )
2128 {
2129 wxLogLastError(wxT("BitBlt"));
2130 }
2131
2132 // copy src to buffer using selected raster op
2133 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
2134 GetHdcOf(*source), xsrc, ysrc, dwRop) )
2135 {
2136 wxLogLastError(wxT("BitBlt"));
2137 }
2138
2139 // set masked area in buffer to BLACK (pixel value 0)
2140 COLORREF prevBkCol = ::SetBkColor(GetHdc(), RGB(255, 255, 255));
2141 COLORREF prevCol = ::SetTextColor(GetHdc(), RGB(0, 0, 0));
2142 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
2143 dc_mask, xsrcMask, ysrcMask, SRCAND) )
2144 {
2145 wxLogLastError(wxT("BitBlt"));
2146 }
2147
2148 // set unmasked area in dest to BLACK
2149 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2150 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2151 if ( !::BitBlt(GetHdc(), xdest, ydest, (int)width, (int)height,
2152 dc_mask, xsrcMask, ysrcMask, SRCAND) )
2153 {
2154 wxLogLastError(wxT("BitBlt"));
2155 }
2156 ::SetBkColor(GetHdc(), prevBkCol); // restore colours to original values
2157 ::SetTextColor(GetHdc(), prevCol);
2158
2159 // OR buffer to dest
2160 success = ::BitBlt(GetHdc(), xdest, ydest,
2161 (int)width, (int)height,
2162 dc_buffer, 0, 0, SRCPAINT) != 0;
2163 if ( !success )
2164 {
2165 wxLogLastError(wxT("BitBlt"));
2166 }
2167
2168 // tidy up temporary DCs and bitmap
2169 ::SelectObject(dc_mask, hOldMaskBitmap);
2170 ::SelectObject(dc_buffer, hOldBufferBitmap);
2171
2172 #if !wxUSE_DC_CACHEING
2173 {
2174 ::DeleteDC(dc_mask);
2175 ::DeleteDC(dc_buffer);
2176 ::DeleteObject(buffer_bmap);
2177 }
2178 #endif
2179 }
2180 }
2181 else // no mask, just BitBlt() it
2182 {
2183 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2184 // use StretchBlt() if available and finally fall back to BitBlt()
2185
2186 // FIXME: use appropriate WinCE functions
2187 #ifndef __WXWINCE__
2188 const int caps = ::GetDeviceCaps(GetHdc(), RASTERCAPS);
2189 if ( bmpSrc.Ok() && (caps & RC_STRETCHDIB) )
2190 {
2191 DIBSECTION ds;
2192 wxZeroMemory(ds);
2193
2194 if ( ::GetObject(GetHbitmapOf(bmpSrc),
2195 sizeof(ds),
2196 &ds) == sizeof(ds) )
2197 {
2198 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2199
2200 // Figure out what co-ordinate system we're supposed to specify
2201 // ysrc in.
2202 const LONG hDIB = ds.dsBmih.biHeight;
2203 if ( hDIB > 0 )
2204 {
2205 // reflect ysrc
2206 ysrc = hDIB - (ysrc + height);
2207 }
2208
2209 if ( ::StretchDIBits(GetHdc(),
2210 xdest, ydest,
2211 width, height,
2212 xsrc, ysrc,
2213 width, height,
2214 ds.dsBm.bmBits,
2215 (LPBITMAPINFO)&ds.dsBmih,
2216 DIB_RGB_COLORS,
2217 SRCCOPY
2218 ) == (int)GDI_ERROR )
2219 {
2220 // On Win9x this API fails most (all?) of the time, so
2221 // logging it becomes quite distracting. Since it falls
2222 // back to the code below this is not really serious, so
2223 // don't log it.
2224 //wxLogLastError(wxT("StretchDIBits"));
2225 }
2226 else
2227 {
2228 success = true;
2229 }
2230 }
2231 }
2232
2233 if ( !success && (caps & RC_STRETCHBLT) )
2234 #endif
2235 // __WXWINCE__
2236 {
2237 #ifndef __WXWINCE__
2238 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2239 #endif
2240
2241 if ( !::StretchBlt
2242 (
2243 GetHdc(),
2244 xdest, ydest, width, height,
2245 GetHdcOf(*source),
2246 xsrc, ysrc, width, height,
2247 dwRop
2248 ) )
2249 {
2250 wxLogLastError(_T("StretchBlt"));
2251 }
2252 else
2253 {
2254 success = true;
2255 }
2256 }
2257
2258 if ( !success )
2259 {
2260 if ( !::BitBlt
2261 (
2262 GetHdc(),
2263 xdest, ydest,
2264 (int)width, (int)height,
2265 GetHdcOf(*source),
2266 xsrc, ysrc,
2267 dwRop
2268 ) )
2269 {
2270 wxLogLastError(_T("BitBlt"));
2271 }
2272 else
2273 {
2274 success = true;
2275 }
2276 }
2277 }
2278
2279 ::SetTextColor(GetHdc(), old_textground);
2280 ::SetBkColor(GetHdc(), old_background);
2281
2282 return success;
2283 }
2284
2285 void wxDC::DoGetSize(int *w, int *h) const
2286 {
2287 WXMICROWIN_CHECK_HDC
2288
2289 if ( w ) *w = ::GetDeviceCaps(GetHdc(), HORZRES);
2290 if ( h ) *h = ::GetDeviceCaps(GetHdc(), VERTRES);
2291 }
2292
2293 void wxDC::DoGetSizeMM(int *w, int *h) const
2294 {
2295 WXMICROWIN_CHECK_HDC
2296
2297 // if we implement it in terms of DoGetSize() instead of directly using the
2298 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2299 // will also work for wxWindowDC and wxClientDC even though their size is
2300 // not the same as the total size of the screen
2301 int wPixels, hPixels;
2302 DoGetSize(&wPixels, &hPixels);
2303
2304 if ( w )
2305 {
2306 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
2307
2308 wxCHECK_RET( wTotal, _T("0 width device?") );
2309
2310 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2311 }
2312
2313 if ( h )
2314 {
2315 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2316
2317 wxCHECK_RET( hTotal, _T("0 height device?") );
2318
2319 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2320 }
2321 }
2322
2323 wxSize wxDC::GetPPI() const
2324 {
2325 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2326
2327 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2328 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2329
2330 return wxSize(x, y);
2331 }
2332
2333 // For use by wxWidgets only, unless custom units are required.
2334 void wxDC::SetLogicalScale(double x, double y)
2335 {
2336 WXMICROWIN_CHECK_HDC
2337
2338 m_logicalScaleX = x;
2339 m_logicalScaleY = y;
2340 }
2341
2342 // ----------------------------------------------------------------------------
2343 // DC caching
2344 // ----------------------------------------------------------------------------
2345
2346 #if wxUSE_DC_CACHEING
2347
2348 /*
2349 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2350 * improve it in due course, either using arrays, or simply storing pointers to one
2351 * entry for the bitmap, and two for the DCs. -- JACS
2352 */
2353
2354 wxList wxDC::sm_bitmapCache;
2355 wxList wxDC::sm_dcCache;
2356
2357 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2358 {
2359 m_bitmap = hBitmap;
2360 m_dc = 0;
2361 m_width = w;
2362 m_height = h;
2363 m_depth = depth;
2364 }
2365
2366 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2367 {
2368 m_bitmap = 0;
2369 m_dc = hDC;
2370 m_width = 0;
2371 m_height = 0;
2372 m_depth = depth;
2373 }
2374
2375 wxDCCacheEntry::~wxDCCacheEntry()
2376 {
2377 if (m_bitmap)
2378 ::DeleteObject((HBITMAP) m_bitmap);
2379 if (m_dc)
2380 ::DeleteDC((HDC) m_dc);
2381 }
2382
2383 wxDCCacheEntry* wxDC::FindBitmapInCache(WXHDC dc, int w, int h)
2384 {
2385 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2386 wxList::compatibility_iterator node = sm_bitmapCache.GetFirst();
2387 while (node)
2388 {
2389 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2390
2391 if (entry->m_depth == depth)
2392 {
2393 if (entry->m_width < w || entry->m_height < h)
2394 {
2395 ::DeleteObject((HBITMAP) entry->m_bitmap);
2396 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2397 if ( !entry->m_bitmap)
2398 {
2399 wxLogLastError(wxT("CreateCompatibleBitmap"));
2400 }
2401 entry->m_width = w; entry->m_height = h;
2402 return entry;
2403 }
2404 return entry;
2405 }
2406
2407 node = node->GetNext();
2408 }
2409 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2410 if ( !hBitmap)
2411 {
2412 wxLogLastError(wxT("CreateCompatibleBitmap"));
2413 }
2414 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2415 AddToBitmapCache(entry);
2416 return entry;
2417 }
2418
2419 wxDCCacheEntry* wxDC::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
2420 {
2421 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2422 wxList::compatibility_iterator node = sm_dcCache.GetFirst();
2423 while (node)
2424 {
2425 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2426
2427 // Don't return the same one as we already have
2428 if (!notThis || (notThis != entry))
2429 {
2430 if (entry->m_depth == depth)
2431 {
2432 return entry;
2433 }
2434 }
2435
2436 node = node->GetNext();
2437 }
2438 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2439 if ( !hDC)
2440 {
2441 wxLogLastError(wxT("CreateCompatibleDC"));
2442 }
2443 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2444 AddToDCCache(entry);
2445 return entry;
2446 }
2447
2448 void wxDC::AddToBitmapCache(wxDCCacheEntry* entry)
2449 {
2450 sm_bitmapCache.Append(entry);
2451 }
2452
2453 void wxDC::AddToDCCache(wxDCCacheEntry* entry)
2454 {
2455 sm_dcCache.Append(entry);
2456 }
2457
2458 void wxDC::ClearCache()
2459 {
2460 WX_CLEAR_LIST(wxList, sm_dcCache);
2461 WX_CLEAR_LIST(wxList, sm_bitmapCache);
2462 }
2463
2464 // Clean up cache at app exit
2465 class wxDCModule : public wxModule
2466 {
2467 public:
2468 virtual bool OnInit() { return true; }
2469 virtual void OnExit() { wxDC::ClearCache(); }
2470
2471 private:
2472 DECLARE_DYNAMIC_CLASS(wxDCModule)
2473 };
2474
2475 IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2476
2477 #endif // wxUSE_DC_CACHEING
2478
2479 // ----------------------------------------------------------------------------
2480 // alpha channel support
2481 // ----------------------------------------------------------------------------
2482
2483 static bool AlphaBlt(HDC hdcDst,
2484 int x, int y, int width, int height,
2485 int srcX, int srcY, HDC hdcSrc,
2486 const wxBitmap& bmp)
2487 {
2488 wxASSERT_MSG( bmp.Ok() && bmp.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2489 wxASSERT_MSG( hdcDst && hdcSrc, _T("AlphaBlt(): invalid HDC") );
2490
2491 // do we have AlphaBlend() and company in the headers?
2492 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2493 // yes, now try to see if we have it during run-time
2494 typedef BOOL (WINAPI *AlphaBlend_t)(HDC,int,int,int,int,
2495 HDC,int,int,int,int,
2496 BLENDFUNCTION);
2497
2498 // bitmaps can be drawn only from GUI thread so there is no need to
2499 // protect this static variable from multiple threads
2500 static bool s_triedToLoad = false;
2501 static AlphaBlend_t pfnAlphaBlend = NULL;
2502 if ( !s_triedToLoad )
2503 {
2504 s_triedToLoad = true;
2505
2506 // don't give errors about the DLL being unavailable, we're
2507 // prepared to handle this
2508 wxLogNull nolog;
2509
2510 wxDynamicLibrary dll(_T("msimg32.dll"));
2511 if ( dll.IsLoaded() )
2512 {
2513 pfnAlphaBlend = (AlphaBlend_t)dll.GetSymbol(_T("AlphaBlend"));
2514 if ( pfnAlphaBlend )
2515 {
2516 // we must keep the DLL loaded if we want to be able to
2517 // call AlphaBlend() so just never unload it at all, not a
2518 // big deal
2519 dll.Detach();
2520 }
2521 }
2522 }
2523
2524 if ( pfnAlphaBlend )
2525 {
2526 BLENDFUNCTION bf;
2527 bf.BlendOp = AC_SRC_OVER;
2528 bf.BlendFlags = 0;
2529 bf.SourceConstantAlpha = 0xff;
2530 bf.AlphaFormat = AC_SRC_ALPHA;
2531
2532 if ( pfnAlphaBlend(hdcDst, x, y, width, height,
2533 hdcSrc, srcX, srcY, width, height,
2534 bf) )
2535 {
2536 // skip wxAlphaBlend() call below
2537 return true;
2538 }
2539
2540 wxLogLastError(_T("AlphaBlend"));
2541 }
2542 #endif // defined(AC_SRC_OVER)
2543
2544 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2545 // implementation
2546 #ifdef wxHAVE_RAW_BITMAP
2547 wxAlphaBlend(hdcDst, x, y, width, height, srcX, srcY, bmp);
2548
2549 return true;
2550 #else // !wxHAVE_RAW_BITMAP
2551 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2552 // alpha but at least something will be shown like this)
2553 wxUnusedVar(bmp);
2554 return false;
2555 #endif // wxHAVE_RAW_BITMAP
2556 }
2557
2558
2559 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2560 #ifdef wxHAVE_RAW_BITMAP
2561
2562 static void
2563 wxAlphaBlend(HDC hdcDst, int xDst, int yDst, int w, int h, int srcX, int srcY, const wxBitmap& bmpSrc)
2564 {
2565 // get the destination DC pixels
2566 wxBitmap bmpDst(w, h, 32 /* force creating RGBA DIB */);
2567 MemoryHDC hdcMem;
2568 SelectInHDC select(hdcMem, GetHbitmapOf(bmpDst));
2569
2570 if ( !::BitBlt(hdcMem, 0, 0, w, h, hdcDst, 0, 0, SRCCOPY) )
2571 {
2572 wxLogLastError(_T("BitBlt"));
2573 }
2574
2575 // combine them with the source bitmap using alpha
2576 wxAlphaPixelData dataDst(bmpDst),
2577 dataSrc((wxBitmap &)bmpSrc);
2578
2579 wxCHECK_RET( dataDst && dataSrc,
2580 _T("failed to get raw data in wxAlphaBlend") );
2581
2582 wxAlphaPixelData::Iterator pDst(dataDst),
2583 pSrc(dataSrc);
2584
2585 pSrc.Offset(dataSrc, srcX, srcY);
2586
2587 for ( int y = 0; y < h; y++ )
2588 {
2589 wxAlphaPixelData::Iterator pDstRowStart = pDst,
2590 pSrcRowStart = pSrc;
2591
2592 for ( int x = 0; x < w; x++ )
2593 {
2594 // note that source bitmap uses premultiplied alpha (as required by
2595 // the real AlphaBlend)
2596 const unsigned beta = 255 - pSrc.Alpha();
2597
2598 pDst.Red() = pSrc.Red() + (beta * pDst.Red() + 127) / 255;
2599 pDst.Blue() = pSrc.Blue() + (beta * pDst.Blue() + 127) / 255;
2600 pDst.Green() = pSrc.Green() + (beta * pDst.Green() + 127) / 255;
2601
2602 ++pDst;
2603 ++pSrc;
2604 }
2605
2606 pDst = pDstRowStart;
2607 pSrc = pSrcRowStart;
2608 pDst.OffsetY(dataDst, 1);
2609 pSrc.OffsetY(dataSrc, 1);
2610 }
2611
2612 // and finally blit them back to the destination DC
2613 if ( !::BitBlt(hdcDst, xDst, yDst, w, h, hdcMem, 0, 0, SRCCOPY) )
2614 {
2615 wxLogLastError(_T("BitBlt"));
2616 }
2617 }
2618
2619 #endif // #ifdef wxHAVE_RAW_BITMAP