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