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