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