made GetClippingBox() work even for clipping region (pre)set by Windows and not only...
[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 wxWindows 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 UpdateClipBox();
395
396 if ( !m_clipX1 && !m_clipX2 )
397 m_clipping = false;
398 }
399
400 return wxDCBase::DoGetClippingBox(x, y, w, h);
401 }
402
403 // common part of DoSetClippingRegion() and DoSetClippingRegionAsRegion()
404 void wxDC::SetClippingHrgn(WXHRGN hrgn)
405 {
406 wxCHECK_RET( hrgn, wxT("invalid clipping region") );
407
408 WXMICROWIN_CHECK_HDC
409
410 // note that we combine the new clipping region with the existing one: this
411 // is compatible with what the other ports do and is the documented
412 // behaviour now (starting with 2.3.3)
413 #if defined(__WXWINCE__)
414 RECT rectClip;
415 if ( !::GetClipBox(GetHdc(), &rectClip) )
416 return;
417
418 HRGN hrgnDest = ::CreateRectRgn(0, 0, 0, 0);
419 HRGN hrgnClipOld = ::CreateRectRgn(rectClip.left, rectClip.top,
420 rectClip.right, rectClip.bottom);
421
422 if ( ::CombineRgn(hrgnDest, hrgnClipOld, (HRGN)hrgn, RGN_AND) != ERROR )
423 {
424 ::SelectClipRgn(GetHdc(), hrgnDest);
425 }
426
427 ::DeleteObject(hrgnClipOld);
428 ::DeleteObject(hrgnDest);
429 #else // !WinCE
430 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN)hrgn, RGN_AND) == ERROR )
431 {
432 wxLogLastError(_T("ExtSelectClipRgn"));
433
434 return;
435 }
436 #endif // WinCE/!WinCE
437
438 m_clipping = true;
439
440 UpdateClipBox();
441 }
442
443 void wxDC::DoSetClippingRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
444 {
445 // the region coords are always the device ones, so do the translation
446 // manually
447 //
448 // FIXME: possible +/-1 error here, to check!
449 HRGN hrgn = ::CreateRectRgn(LogicalToDeviceX(x),
450 LogicalToDeviceY(y),
451 LogicalToDeviceX(x + w),
452 LogicalToDeviceY(y + h));
453 if ( !hrgn )
454 {
455 wxLogLastError(_T("CreateRectRgn"));
456 }
457 else
458 {
459 SetClippingHrgn((WXHRGN)hrgn);
460
461 ::DeleteObject(hrgn);
462 }
463 }
464
465 void wxDC::DoSetClippingRegionAsRegion(const wxRegion& region)
466 {
467 SetClippingHrgn(region.GetHRGN());
468 }
469
470 void wxDC::DestroyClippingRegion()
471 {
472 WXMICROWIN_CHECK_HDC
473
474 if (m_clipping && m_hDC)
475 {
476 // TODO: this should restore the previous clipping region,
477 // so that OnPaint processing works correctly, and the update
478 // clipping region doesn't get destroyed after the first
479 // DestroyClippingRegion.
480 HRGN rgn = CreateRectRgn(0, 0, 32000, 32000);
481 ::SelectClipRgn(GetHdc(), rgn);
482 ::DeleteObject(rgn);
483 }
484
485 wxDCBase::DestroyClippingRegion();
486 }
487
488 // ---------------------------------------------------------------------------
489 // query capabilities
490 // ---------------------------------------------------------------------------
491
492 bool wxDC::CanDrawBitmap() const
493 {
494 return true;
495 }
496
497 bool wxDC::CanGetTextExtent() const
498 {
499 #ifdef __WXMICROWIN__
500 // TODO Extend MicroWindows' GetDeviceCaps function
501 return true;
502 #else
503 // What sort of display is it?
504 int technology = ::GetDeviceCaps(GetHdc(), TECHNOLOGY);
505
506 return (technology == DT_RASDISPLAY) || (technology == DT_RASPRINTER);
507 #endif
508 }
509
510 int wxDC::GetDepth() const
511 {
512 WXMICROWIN_CHECK_HDC_RET(16)
513
514 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL);
515 }
516
517 // ---------------------------------------------------------------------------
518 // drawing
519 // ---------------------------------------------------------------------------
520
521 void wxDC::Clear()
522 {
523 WXMICROWIN_CHECK_HDC
524
525 RECT rect;
526 if ( m_canvas )
527 {
528 GetClientRect((HWND) m_canvas->GetHWND(), &rect);
529 }
530 else
531 {
532 // No, I think we should simply ignore this if printing on e.g.
533 // a printer DC.
534 // wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
535 if (!m_selectedBitmap.Ok())
536 return;
537
538 rect.left = 0; rect.top = 0;
539 rect.right = m_selectedBitmap.GetWidth();
540 rect.bottom = m_selectedBitmap.GetHeight();
541 }
542
543 #ifndef __WXWINCE__
544 (void) ::SetMapMode(GetHdc(), MM_TEXT);
545 #endif
546
547 DWORD colour = ::GetBkColor(GetHdc());
548 HBRUSH brush = ::CreateSolidBrush(colour);
549 ::FillRect(GetHdc(), &rect, brush);
550 ::DeleteObject(brush);
551
552 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
553 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
554
555 #ifndef __WXWINCE__
556 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
557
558 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
559 ::SetWindowExtEx(GetHdc(), width, height, NULL);
560 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
561 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
562 #endif
563 }
564
565 bool wxDC::DoFloodFill(wxCoord x, wxCoord y, const wxColour& col, int style)
566 {
567 #ifdef __WXWINCE__
568 return false;
569 #else
570
571 WXMICROWIN_CHECK_HDC_RET(false)
572
573 bool success = (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x), YLOG2DEV(y),
574 col.GetPixel(),
575 style == wxFLOOD_SURFACE ? FLOODFILLSURFACE
576 : FLOODFILLBORDER) ) ;
577 if (!success)
578 {
579 // quoting from the MSDN docs:
580 //
581 // Following are some of the reasons this function might fail:
582 //
583 // * The filling could not be completed.
584 // * The specified point has the boundary color specified by the
585 // crColor parameter (if FLOODFILLBORDER was requested).
586 // * The specified point does not have the color specified by
587 // crColor (if FLOODFILLSURFACE was requested)
588 // * The point is outside the clipping region that is, it is not
589 // visible on the device.
590 //
591 wxLogLastError(wxT("ExtFloodFill"));
592 }
593
594 CalcBoundingBox(x, y);
595
596 return success;
597 #endif
598 }
599
600 bool wxDC::DoGetPixel(wxCoord x, wxCoord y, wxColour *col) const
601 {
602 WXMICROWIN_CHECK_HDC_RET(false)
603
604 wxCHECK_MSG( col, false, _T("NULL colour parameter in wxDC::GetPixel") );
605
606 // get the color of the pixel
607 COLORREF pixelcolor = ::GetPixel(GetHdc(), XLOG2DEV(x), YLOG2DEV(y));
608
609 wxRGBToColour(*col, pixelcolor);
610
611 return true;
612 }
613
614 void wxDC::DoCrossHair(wxCoord x, wxCoord y)
615 {
616 WXMICROWIN_CHECK_HDC
617
618 wxCoord x1 = x-VIEWPORT_EXTENT;
619 wxCoord y1 = y-VIEWPORT_EXTENT;
620 wxCoord x2 = x+VIEWPORT_EXTENT;
621 wxCoord y2 = y+VIEWPORT_EXTENT;
622
623 wxDrawLine(GetHdc(), XLOG2DEV(x1), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y));
624 wxDrawLine(GetHdc(), XLOG2DEV(x), YLOG2DEV(y1), XLOG2DEV(x), YLOG2DEV(y2));
625
626 CalcBoundingBox(x1, y1);
627 CalcBoundingBox(x2, y2);
628 }
629
630 void wxDC::DoDrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
631 {
632 WXMICROWIN_CHECK_HDC
633
634 wxDrawLine(GetHdc(), XLOG2DEV(x1), YLOG2DEV(y1), XLOG2DEV(x2), YLOG2DEV(y2));
635
636 CalcBoundingBox(x1, y1);
637 CalcBoundingBox(x2, y2);
638 }
639
640 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
641 // and ending at (x2, y2)
642 void wxDC::DoDrawArc(wxCoord x1, wxCoord y1,
643 wxCoord x2, wxCoord y2,
644 wxCoord xc, wxCoord yc)
645 {
646 #ifdef __WXWINCE__
647 // Slower emulation since WinCE doesn't support Pie and Arc
648 double r = sqrt( (x1-xc)*(x1-xc) + (y1-yc)*(y1-yc) );
649 double sa = acos((x1-xc)/r)/M_PI*180; // between 0 and 180
650 if( y1>yc ) sa = -sa; // below center
651 double ea = atan2(yc-y2, x2-xc)/M_PI*180;
652 DoDrawEllipticArcRot( xc-r, yc-r, 2*r, 2*r, sa, ea );
653 #else
654
655 WXMICROWIN_CHECK_HDC
656
657 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
658
659 double dx = xc - x1;
660 double dy = yc - y1;
661 double radius = (double)sqrt(dx*dx+dy*dy);
662 wxCoord r = (wxCoord)radius;
663
664 // treat the special case of full circle separately
665 if ( x1 == x2 && y1 == y2 )
666 {
667 DrawEllipse(xc - r, yc - r, 2*r, 2*r);
668 return;
669 }
670
671 wxCoord xx1 = XLOG2DEV(x1);
672 wxCoord yy1 = YLOG2DEV(y1);
673 wxCoord xx2 = XLOG2DEV(x2);
674 wxCoord yy2 = YLOG2DEV(y2);
675 wxCoord xxc = XLOG2DEV(xc);
676 wxCoord yyc = YLOG2DEV(yc);
677 wxCoord ray = (wxCoord) sqrt(double((xxc-xx1)*(xxc-xx1)+(yyc-yy1)*(yyc-yy1)));
678
679 wxCoord xxx1 = (wxCoord) (xxc-ray);
680 wxCoord yyy1 = (wxCoord) (yyc-ray);
681 wxCoord xxx2 = (wxCoord) (xxc+ray);
682 wxCoord yyy2 = (wxCoord) (yyc+ray);
683
684 if ( m_brush.Ok() && m_brush.GetStyle() != wxTRANSPARENT )
685 {
686 // Have to add 1 to bottom-right corner of rectangle
687 // to make semi-circles look right (crooked line otherwise).
688 // Unfortunately this is not a reliable method, depends
689 // on the size of shape.
690 // TODO: figure out why this happens!
691 Pie(GetHdc(),xxx1,yyy1,xxx2+1,yyy2+1, xx1,yy1,xx2,yy2);
692 }
693 else
694 {
695 Arc(GetHdc(),xxx1,yyy1,xxx2,yyy2, xx1,yy1,xx2,yy2);
696 }
697
698 CalcBoundingBox(xc - r, yc - r);
699 CalcBoundingBox(xc + r, yc + r);
700 #endif
701 }
702
703 void wxDC::DoDrawCheckMark(wxCoord x1, wxCoord y1,
704 wxCoord width, wxCoord height)
705 {
706 WXMICROWIN_CHECK_HDC
707
708 wxCoord x2 = x1 + width,
709 y2 = y1 + height;
710
711 #if defined(__WIN32__) && !defined(__SYMANTEC__) && !defined(__WXMICROWIN__)
712 RECT rect;
713 rect.left = x1;
714 rect.top = y1;
715 rect.right = x2;
716 rect.bottom = y2;
717
718 #ifdef __WXWINCE__
719 DrawFrameControl(GetHdc(), &rect, DFC_BUTTON, DFCS_BUTTONCHECK);
720 #else
721 DrawFrameControl(GetHdc(), &rect, DFC_MENU, DFCS_MENUCHECK);
722 #endif
723 #else // Symantec-MicroWin
724 // draw a cross
725 HPEN blackPen = ::CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
726 HPEN whiteBrush = (HPEN)::GetStockObject(WHITE_BRUSH);
727 HPEN hPenOld = (HPEN)::SelectObject(GetHdc(), blackPen);
728 HPEN hBrushOld = (HPEN)::SelectObject(GetHdc(), whiteBrush);
729 ::SetROP2(GetHdc(), R2_COPYPEN);
730 Rectangle(GetHdc(), x1, y1, x2, y2);
731 MoveToEx(GetHdc(), x1, y1, NULL);
732 LineTo(GetHdc(), x2, y2);
733 MoveToEx(GetHdc(), x2, y1, NULL);
734 LineTo(GetHdc(), x1, y2);
735 ::SelectObject(GetHdc(), hPenOld);
736 ::SelectObject(GetHdc(), hBrushOld);
737 ::DeleteObject(blackPen);
738 #endif // Win32/Symantec-MicroWin
739
740 CalcBoundingBox(x1, y1);
741 CalcBoundingBox(x2, y2);
742 }
743
744 void wxDC::DoDrawPoint(wxCoord x, wxCoord y)
745 {
746 WXMICROWIN_CHECK_HDC
747
748 COLORREF color = 0x00ffffff;
749 if (m_pen.Ok())
750 {
751 color = m_pen.GetColour().GetPixel();
752 }
753
754 SetPixel(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), color);
755
756 CalcBoundingBox(x, y);
757 }
758
759 void wxDC::DoDrawPolygon(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset,int fillStyle)
760 {
761 WXMICROWIN_CHECK_HDC
762
763 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
764
765 // Do things less efficiently if we have offsets
766 if (xoffset != 0 || yoffset != 0)
767 {
768 POINT *cpoints = new POINT[n];
769 int i;
770 for (i = 0; i < n; i++)
771 {
772 cpoints[i].x = (int)(points[i].x + xoffset);
773 cpoints[i].y = (int)(points[i].y + yoffset);
774
775 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
776 }
777 #ifndef __WXWINCE__
778 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
779 #endif
780 (void)Polygon(GetHdc(), cpoints, n);
781 #ifndef __WXWINCE__
782 SetPolyFillMode(GetHdc(),prev);
783 #endif
784 delete[] cpoints;
785 }
786 else
787 {
788 int i;
789 for (i = 0; i < n; i++)
790 CalcBoundingBox(points[i].x, points[i].y);
791
792 #ifndef __WXWINCE__
793 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
794 #endif
795 (void)Polygon(GetHdc(), (POINT*) points, n);
796 #ifndef __WXWINCE__
797 SetPolyFillMode(GetHdc(),prev);
798 #endif
799 }
800 }
801
802 void
803 wxDC::DoDrawPolyPolygon(int n,
804 int count[],
805 wxPoint points[],
806 wxCoord xoffset,
807 wxCoord yoffset,
808 int fillStyle)
809 {
810 #ifdef __WXWINCE__
811 wxDCBase::DoDrawPolyPolygon(n, count, points, xoffset, yoffset, fillStyle);
812 #else
813 WXMICROWIN_CHECK_HDC
814
815 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
816 int i, cnt;
817 for (i = cnt = 0; i < n; i++)
818 cnt += count[i];
819
820 // Do things less efficiently if we have offsets
821 if (xoffset != 0 || yoffset != 0)
822 {
823 POINT *cpoints = new POINT[cnt];
824 for (i = 0; i < cnt; i++)
825 {
826 cpoints[i].x = (int)(points[i].x + xoffset);
827 cpoints[i].y = (int)(points[i].y + yoffset);
828
829 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
830 }
831 #ifndef __WXWINCE__
832 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
833 #endif
834 (void)PolyPolygon(GetHdc(), cpoints, count, n);
835 #ifndef __WXWINCE__
836 SetPolyFillMode(GetHdc(),prev);
837 #endif
838 delete[] cpoints;
839 }
840 else
841 {
842 for (i = 0; i < cnt; i++)
843 CalcBoundingBox(points[i].x, points[i].y);
844
845 #ifndef __WXWINCE__
846 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
847 #endif
848 (void)PolyPolygon(GetHdc(), (POINT*) points, count, n);
849 #ifndef __WXWINCE__
850 SetPolyFillMode(GetHdc(),prev);
851 #endif
852 }
853 #endif
854 // __WXWINCE__
855 }
856
857 void wxDC::DoDrawLines(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset)
858 {
859 WXMICROWIN_CHECK_HDC
860
861 // Do things less efficiently if we have offsets
862 if (xoffset != 0 || yoffset != 0)
863 {
864 POINT *cpoints = new POINT[n];
865 int i;
866 for (i = 0; i < n; i++)
867 {
868 cpoints[i].x = (int)(points[i].x + xoffset);
869 cpoints[i].y = (int)(points[i].y + yoffset);
870
871 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
872 }
873 (void)Polyline(GetHdc(), cpoints, n);
874 delete[] cpoints;
875 }
876 else
877 {
878 int i;
879 for (i = 0; i < n; i++)
880 CalcBoundingBox(points[i].x, points[i].y);
881
882 (void)Polyline(GetHdc(), (POINT*) points, n);
883 }
884 }
885
886 void wxDC::DoDrawRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
887 {
888 WXMICROWIN_CHECK_HDC
889
890 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
891
892 wxCoord x2 = x + width;
893 wxCoord y2 = y + height;
894
895 if ((m_logicalFunction == wxCOPY) && (m_pen.GetStyle() == wxTRANSPARENT))
896 {
897 RECT rect;
898 rect.left = XLOG2DEV(x);
899 rect.top = YLOG2DEV(y);
900 rect.right = XLOG2DEV(x2);
901 rect.bottom = YLOG2DEV(y2);
902 (void)FillRect(GetHdc(), &rect, (HBRUSH)m_brush.GetResourceHandle() );
903 }
904 else
905 {
906 // Windows draws the filled rectangles without outline (i.e. drawn with a
907 // transparent pen) one pixel smaller in both directions and we want them
908 // to have the same size regardless of which pen is used - adjust
909
910 // I wonder if this shouldn´t be done after the LOG2DEV() conversions. RR.
911 if ( m_pen.GetStyle() == wxTRANSPARENT )
912 {
913 x2++;
914 y2++;
915 }
916
917 (void)Rectangle(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2));
918 }
919
920
921 CalcBoundingBox(x, y);
922 CalcBoundingBox(x2, y2);
923 }
924
925 void wxDC::DoDrawRoundedRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius)
926 {
927 WXMICROWIN_CHECK_HDC
928
929 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
930
931 // Now, a negative radius value is interpreted to mean
932 // 'the proportion of the smallest X or Y dimension'
933
934 if (radius < 0.0)
935 {
936 double smallest = (width < height) ? width : height;
937 radius = (- radius * smallest);
938 }
939
940 wxCoord x2 = (x+width);
941 wxCoord y2 = (y+height);
942
943 // Windows draws the filled rectangles without outline (i.e. drawn with a
944 // transparent pen) one pixel smaller in both directions and we want them
945 // to have the same size regardless of which pen is used - adjust
946 if ( m_pen.GetStyle() == wxTRANSPARENT )
947 {
948 x2++;
949 y2++;
950 }
951
952 (void)RoundRect(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2),
953 YLOG2DEV(y2), (int) (2*XLOG2DEV(radius)), (int)( 2*YLOG2DEV(radius)));
954
955 CalcBoundingBox(x, y);
956 CalcBoundingBox(x2, y2);
957 }
958
959 void wxDC::DoDrawEllipse(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
960 {
961 WXMICROWIN_CHECK_HDC
962
963 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
964
965 wxCoord x2 = (x+width);
966 wxCoord y2 = (y+height);
967
968 (void)Ellipse(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2));
969
970 CalcBoundingBox(x, y);
971 CalcBoundingBox(x2, y2);
972 }
973
974 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
975 void wxDC::DoDrawEllipticArc(wxCoord x,wxCoord y,wxCoord w,wxCoord h,double sa,double ea)
976 {
977 #ifdef __WXWINCE__
978 DoDrawEllipticArcRot( x, y, w, h, sa, ea );
979 #else
980
981 WXMICROWIN_CHECK_HDC
982
983 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
984
985 wxCoord x2 = x + w;
986 wxCoord y2 = y + h;
987
988 int rx1 = XLOG2DEV(x+w/2);
989 int ry1 = YLOG2DEV(y+h/2);
990 int rx2 = rx1;
991 int ry2 = ry1;
992
993 sa = DegToRad(sa);
994 ea = DegToRad(ea);
995
996 rx1 += (int)(100.0 * abs(w) * cos(sa));
997 ry1 -= (int)(100.0 * abs(h) * m_signY * sin(sa));
998 rx2 += (int)(100.0 * abs(w) * cos(ea));
999 ry2 -= (int)(100.0 * abs(h) * m_signY * sin(ea));
1000
1001 // draw pie with NULL_PEN first and then outline otherwise a line is
1002 // drawn from the start and end points to the centre
1003 HPEN hpenOld = (HPEN) ::SelectObject(GetHdc(), (HPEN) ::GetStockObject(NULL_PEN));
1004 if (m_signY > 0)
1005 {
1006 (void)Pie(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2)+1, YLOG2DEV(y2)+1,
1007 rx1, ry1, rx2, ry2);
1008 }
1009 else
1010 {
1011 (void)Pie(GetHdc(), XLOG2DEV(x), YLOG2DEV(y)-1, XLOG2DEV(x2)+1, YLOG2DEV(y2),
1012 rx1, ry1-1, rx2, ry2-1);
1013 }
1014
1015 ::SelectObject(GetHdc(), hpenOld);
1016
1017 (void)Arc(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2),
1018 rx1, ry1, rx2, ry2);
1019
1020 CalcBoundingBox(x, y);
1021 CalcBoundingBox(x2, y2);
1022 #endif
1023 }
1024
1025 void wxDC::DoDrawIcon(const wxIcon& icon, wxCoord x, wxCoord y)
1026 {
1027 WXMICROWIN_CHECK_HDC
1028
1029 wxCHECK_RET( icon.Ok(), wxT("invalid icon in DrawIcon") );
1030
1031 #ifdef __WIN32__
1032 ::DrawIconEx(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), GetHiconOf(icon), icon.GetWidth(), icon.GetHeight(), 0, NULL, DI_NORMAL);
1033 #else
1034 ::DrawIcon(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), GetHiconOf(icon));
1035 #endif
1036
1037 CalcBoundingBox(x, y);
1038 CalcBoundingBox(x + icon.GetWidth(), y + icon.GetHeight());
1039 }
1040
1041 void wxDC::DoDrawBitmap( const wxBitmap &bmp, wxCoord x, wxCoord y, bool useMask )
1042 {
1043 WXMICROWIN_CHECK_HDC
1044
1045 wxCHECK_RET( bmp.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
1046
1047 int width = bmp.GetWidth(),
1048 height = bmp.GetHeight();
1049
1050 HBITMAP hbmpMask = 0;
1051
1052 #if wxUSE_PALETTE
1053 HPALETTE oldPal = 0;
1054 #endif // wxUSE_PALETTE
1055
1056 if ( bmp.HasAlpha() )
1057 {
1058 MemoryHDC hdcMem;
1059 SelectInHDC select(hdcMem, GetHbitmapOf(bmp));
1060
1061 if ( AlphaBlt(GetHdc(), x, y, width, height, hdcMem, bmp) )
1062 return;
1063 }
1064
1065 if ( useMask )
1066 {
1067 wxMask *mask = bmp.GetMask();
1068 if ( mask )
1069 hbmpMask = (HBITMAP)mask->GetMaskBitmap();
1070
1071 if ( !hbmpMask )
1072 {
1073 // don't give assert here because this would break existing
1074 // programs - just silently ignore useMask parameter
1075 useMask = false;
1076 }
1077 }
1078 if ( useMask )
1079 {
1080 #ifdef __WIN32__
1081 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1082 // points
1083 // On some systems, MaskBlt succeeds yet is much much slower
1084 // than the wxWindows fall-back implementation. So we need
1085 // to be able to switch this on and off at runtime.
1086 bool ok = false;
1087 #if wxUSE_SYSTEM_OPTIONS
1088 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1089 #endif
1090 {
1091 HDC cdc = GetHdc();
1092 HDC hdcMem = ::CreateCompatibleDC(GetHdc());
1093 HGDIOBJ hOldBitmap = ::SelectObject(hdcMem, GetHbitmapOf(bmp));
1094 #if wxUSE_PALETTE
1095 wxPalette *pal = bmp.GetPalette();
1096 if ( pal && ::GetDeviceCaps(cdc,BITSPIXEL) <= 8 )
1097 {
1098 oldPal = ::SelectPalette(hdcMem, GetHpaletteOf(*pal), FALSE);
1099 ::RealizePalette(hdcMem);
1100 }
1101 #endif // wxUSE_PALETTE
1102
1103 ok = ::MaskBlt(cdc, x, y, width, height,
1104 hdcMem, 0, 0,
1105 hbmpMask, 0, 0,
1106 MAKEROP4(SRCCOPY, DSTCOPY)) != 0;
1107
1108 #if wxUSE_PALETTE
1109 if (oldPal)
1110 ::SelectPalette(hdcMem, oldPal, FALSE);
1111 #endif // wxUSE_PALETTE
1112
1113 ::SelectObject(hdcMem, hOldBitmap);
1114 ::DeleteDC(hdcMem);
1115 }
1116
1117 if ( !ok )
1118 #endif // Win32
1119 {
1120 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
1121 // level
1122 wxMemoryDC memDC;
1123 memDC.SelectObject(bmp);
1124
1125 Blit(x, y, width, height, &memDC, 0, 0, wxCOPY, useMask);
1126
1127 memDC.SelectObject(wxNullBitmap);
1128 }
1129 }
1130 else // no mask, just use BitBlt()
1131 {
1132 HDC cdc = GetHdc();
1133 HDC memdc = ::CreateCompatibleDC( cdc );
1134 HBITMAP hbitmap = (HBITMAP) bmp.GetHBITMAP( );
1135
1136 wxASSERT_MSG( hbitmap, wxT("bitmap is ok but HBITMAP is NULL?") );
1137
1138 COLORREF old_textground = ::GetTextColor(GetHdc());
1139 COLORREF old_background = ::GetBkColor(GetHdc());
1140 if (m_textForegroundColour.Ok())
1141 {
1142 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1143 }
1144 if (m_textBackgroundColour.Ok())
1145 {
1146 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1147 }
1148
1149 #if wxUSE_PALETTE
1150 wxPalette *pal = bmp.GetPalette();
1151 if ( pal && ::GetDeviceCaps(cdc,BITSPIXEL) <= 8 )
1152 {
1153 oldPal = ::SelectPalette(memdc, GetHpaletteOf(*pal), FALSE);
1154 ::RealizePalette(memdc);
1155 }
1156 #endif // wxUSE_PALETTE
1157
1158 HGDIOBJ hOldBitmap = ::SelectObject( memdc, hbitmap );
1159 ::BitBlt( cdc, x, y, width, height, memdc, 0, 0, SRCCOPY);
1160
1161 #if wxUSE_PALETTE
1162 if (oldPal)
1163 ::SelectPalette(memdc, oldPal, FALSE);
1164 #endif // wxUSE_PALETTE
1165
1166 ::SelectObject( memdc, hOldBitmap );
1167 ::DeleteDC( memdc );
1168
1169 ::SetTextColor(GetHdc(), old_textground);
1170 ::SetBkColor(GetHdc(), old_background);
1171 }
1172 }
1173
1174 void wxDC::DoDrawText(const wxString& text, wxCoord x, wxCoord y)
1175 {
1176 WXMICROWIN_CHECK_HDC
1177
1178 DrawAnyText(text, x, y);
1179
1180 // update the bounding box
1181 CalcBoundingBox(x, y);
1182
1183 wxCoord w, h;
1184 GetTextExtent(text, &w, &h);
1185 CalcBoundingBox(x + w, y + h);
1186 }
1187
1188 void wxDC::DrawAnyText(const wxString& text, wxCoord x, wxCoord y)
1189 {
1190 WXMICROWIN_CHECK_HDC
1191
1192 // prepare for drawing the text
1193 if ( m_textForegroundColour.Ok() )
1194 SetTextColor(GetHdc(), m_textForegroundColour.GetPixel());
1195
1196 DWORD old_background = 0;
1197 if ( m_textBackgroundColour.Ok() )
1198 {
1199 old_background = SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1200 }
1201
1202 SetBkMode(GetHdc(), m_backgroundMode == wxTRANSPARENT ? TRANSPARENT
1203 : OPAQUE);
1204
1205 #ifdef __WXWINCE__
1206 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), 0, NULL,
1207 text.c_str(), text.length(), NULL) == 0 )
1208 {
1209 wxLogLastError(wxT("TextOut"));
1210 }
1211 #else
1212 if ( ::TextOut(GetHdc(), XLOG2DEV(x), YLOG2DEV(y),
1213 text.c_str(), text.length()) == 0 )
1214 {
1215 wxLogLastError(wxT("TextOut"));
1216 }
1217 #endif
1218
1219 // restore the old parameters (text foreground colour may be left because
1220 // it never is set to anything else, but background should remain
1221 // transparent even if we just drew an opaque string)
1222 if ( m_textBackgroundColour.Ok() )
1223 (void)SetBkColor(GetHdc(), old_background);
1224
1225 SetBkMode(GetHdc(), TRANSPARENT);
1226 }
1227
1228 void wxDC::DoDrawRotatedText(const wxString& text,
1229 wxCoord x, wxCoord y,
1230 double angle)
1231 {
1232 WXMICROWIN_CHECK_HDC
1233
1234 // we test that we have some font because otherwise we should still use the
1235 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1236 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1237 // font for drawing rotated fonts unfortunately)
1238 if ( (angle == 0.0) && m_font.Ok() )
1239 {
1240 DoDrawText(text, x, y);
1241 }
1242 #ifndef __WXMICROWIN__
1243 else
1244 {
1245 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1246 // because it's not TrueType and so can't have non zero
1247 // orientation/escapement under Win9x
1248 wxFont font = m_font.Ok() ? m_font : *wxSWISS_FONT;
1249 HFONT hfont = (HFONT)font.GetResourceHandle();
1250 LOGFONT lf;
1251 if ( ::GetObject(hfont, sizeof(lf), &lf) == 0 )
1252 {
1253 wxLogLastError(wxT("GetObject(hfont)"));
1254 }
1255
1256 // GDI wants the angle in tenth of degree
1257 long angle10 = (long)(angle * 10);
1258 lf.lfEscapement = angle10;
1259 lf. lfOrientation = angle10;
1260
1261 hfont = ::CreateFontIndirect(&lf);
1262 if ( !hfont )
1263 {
1264 wxLogLastError(wxT("CreateFont"));
1265 }
1266 else
1267 {
1268 HFONT hfontOld = (HFONT)::SelectObject(GetHdc(), hfont);
1269
1270 DrawAnyText(text, x, y);
1271
1272 (void)::SelectObject(GetHdc(), hfontOld);
1273 (void)::DeleteObject(hfont);
1274 }
1275
1276 // call the bounding box by adding all four vertices of the rectangle
1277 // containing the text to it (simpler and probably not slower than
1278 // determining which of them is really topmost/leftmost/...)
1279 wxCoord w, h;
1280 GetTextExtent(text, &w, &h);
1281
1282 double rad = DegToRad(angle);
1283
1284 // "upper left" and "upper right"
1285 CalcBoundingBox(x, y);
1286 CalcBoundingBox(x + wxCoord(w*cos(rad)), y - wxCoord(h*sin(rad)));
1287
1288 // "bottom left" and "bottom right"
1289 x += (wxCoord)(h*sin(rad));
1290 y += (wxCoord)(h*cos(rad));
1291 CalcBoundingBox(x, y);
1292 CalcBoundingBox(x + wxCoord(h*sin(rad)), y + wxCoord(h*cos(rad)));
1293 }
1294 #endif
1295 }
1296
1297 // ---------------------------------------------------------------------------
1298 // set GDI objects
1299 // ---------------------------------------------------------------------------
1300
1301 #if wxUSE_PALETTE
1302
1303 void wxDC::DoSelectPalette(bool realize)
1304 {
1305 WXMICROWIN_CHECK_HDC
1306
1307 // Set the old object temporarily, in case the assignment deletes an object
1308 // that's not yet selected out.
1309 if (m_oldPalette)
1310 {
1311 ::SelectPalette(GetHdc(), (HPALETTE) m_oldPalette, FALSE);
1312 m_oldPalette = 0;
1313 }
1314
1315 if ( m_palette.Ok() )
1316 {
1317 HPALETTE oldPal = ::SelectPalette(GetHdc(),
1318 GetHpaletteOf(m_palette),
1319 false);
1320 if (!m_oldPalette)
1321 m_oldPalette = (WXHPALETTE) oldPal;
1322
1323 if (realize)
1324 ::RealizePalette(GetHdc());
1325 }
1326 }
1327
1328 void wxDC::SetPalette(const wxPalette& palette)
1329 {
1330 if ( palette.Ok() )
1331 {
1332 m_palette = palette;
1333 DoSelectPalette(true);
1334 }
1335 }
1336
1337 void wxDC::InitializePalette()
1338 {
1339 if ( wxDisplayDepth() <= 8 )
1340 {
1341 // look for any window or parent that has a custom palette. If any has
1342 // one then we need to use it in drawing operations
1343 wxWindow *win = m_canvas->GetAncestorWithCustomPalette();
1344
1345 m_hasCustomPalette = win && win->HasCustomPalette();
1346 if ( m_hasCustomPalette )
1347 {
1348 m_palette = win->GetPalette();
1349
1350 // turn on MSW translation for this palette
1351 DoSelectPalette();
1352 }
1353 }
1354 }
1355
1356 #endif // wxUSE_PALETTE
1357
1358 // SetFont/Pen/Brush() really ask to be implemented as a single template
1359 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1360
1361 void wxDC::SetFont(const wxFont& font)
1362 {
1363 WXMICROWIN_CHECK_HDC
1364
1365 if ( font == m_font )
1366 return;
1367
1368 if ( font.Ok() )
1369 {
1370 HGDIOBJ hfont = ::SelectObject(GetHdc(), GetHfontOf(font));
1371 if ( hfont == HGDI_ERROR )
1372 {
1373 wxLogLastError(_T("SelectObject(font)"));
1374 }
1375 else // selected ok
1376 {
1377 if ( !m_oldFont )
1378 m_oldFont = (WXHPEN)hfont;
1379
1380 m_font = font;
1381 }
1382 }
1383 else // invalid font, reset the current font
1384 {
1385 if ( m_oldFont )
1386 {
1387 if ( ::SelectObject(GetHdc(), (HPEN) m_oldFont) == HGDI_ERROR )
1388 {
1389 wxLogLastError(_T("SelectObject(old font)"));
1390 }
1391
1392 m_oldFont = 0;
1393 }
1394
1395 m_font = wxNullFont;
1396 }
1397 }
1398
1399 void wxDC::SetPen(const wxPen& pen)
1400 {
1401 WXMICROWIN_CHECK_HDC
1402
1403 if ( pen == m_pen )
1404 return;
1405
1406 if ( pen.Ok() )
1407 {
1408 HGDIOBJ hpen = ::SelectObject(GetHdc(), GetHpenOf(pen));
1409 if ( hpen == HGDI_ERROR )
1410 {
1411 wxLogLastError(_T("SelectObject(pen)"));
1412 }
1413 else // selected ok
1414 {
1415 if ( !m_oldPen )
1416 m_oldPen = (WXHPEN)hpen;
1417
1418 m_pen = pen;
1419 }
1420 }
1421 else // invalid pen, reset the current pen
1422 {
1423 if ( m_oldPen )
1424 {
1425 if ( ::SelectObject(GetHdc(), (HPEN) m_oldPen) == HGDI_ERROR )
1426 {
1427 wxLogLastError(_T("SelectObject(old pen)"));
1428 }
1429
1430 m_oldPen = 0;
1431 }
1432
1433 m_pen = wxNullPen;
1434 }
1435 }
1436
1437 void wxDC::SetBrush(const wxBrush& brush)
1438 {
1439 WXMICROWIN_CHECK_HDC
1440
1441 if ( brush == m_brush )
1442 return;
1443
1444 if ( brush.Ok() )
1445 {
1446 // we must make sure the brush is aligned with the logical coordinates
1447 // before selecting it
1448 wxBitmap *stipple = brush.GetStipple();
1449 if ( stipple && stipple->Ok() )
1450 {
1451 if ( !::SetBrushOrgEx
1452 (
1453 GetHdc(),
1454 m_deviceOriginX % stipple->GetWidth(),
1455 m_deviceOriginY % stipple->GetHeight(),
1456 NULL // [out] previous brush origin
1457 ) )
1458 {
1459 wxLogLastError(_T("SetBrushOrgEx()"));
1460 }
1461 }
1462
1463 HGDIOBJ hbrush = ::SelectObject(GetHdc(), GetHbrushOf(brush));
1464 if ( hbrush == HGDI_ERROR )
1465 {
1466 wxLogLastError(_T("SelectObject(brush)"));
1467 }
1468 else // selected ok
1469 {
1470 if ( !m_oldBrush )
1471 m_oldBrush = (WXHPEN)hbrush;
1472
1473 m_brush = brush;
1474 }
1475 }
1476 else // invalid brush, reset the current brush
1477 {
1478 if ( m_oldBrush )
1479 {
1480 if ( ::SelectObject(GetHdc(), (HPEN) m_oldBrush) == HGDI_ERROR )
1481 {
1482 wxLogLastError(_T("SelectObject(old brush)"));
1483 }
1484
1485 m_oldBrush = 0;
1486 }
1487
1488 m_brush = wxNullBrush;
1489 }
1490 }
1491
1492 void wxDC::SetBackground(const wxBrush& brush)
1493 {
1494 WXMICROWIN_CHECK_HDC
1495
1496 m_backgroundBrush = brush;
1497
1498 if ( m_backgroundBrush.Ok() )
1499 {
1500 (void)SetBkColor(GetHdc(), m_backgroundBrush.GetColour().GetPixel());
1501 }
1502 }
1503
1504 void wxDC::SetBackgroundMode(int mode)
1505 {
1506 WXMICROWIN_CHECK_HDC
1507
1508 m_backgroundMode = mode;
1509
1510 // SetBackgroundColour now only refers to text background
1511 // and m_backgroundMode is used there
1512 }
1513
1514 void wxDC::SetLogicalFunction(int function)
1515 {
1516 WXMICROWIN_CHECK_HDC
1517
1518 m_logicalFunction = function;
1519
1520 SetRop(m_hDC);
1521 }
1522
1523 void wxDC::SetRop(WXHDC dc)
1524 {
1525 if ( !dc || m_logicalFunction < 0 )
1526 return;
1527
1528 int rop;
1529
1530 switch (m_logicalFunction)
1531 {
1532 case wxCLEAR: rop = R2_BLACK; break;
1533 case wxXOR: rop = R2_XORPEN; break;
1534 case wxINVERT: rop = R2_NOT; break;
1535 case wxOR_REVERSE: rop = R2_MERGEPENNOT; break;
1536 case wxAND_REVERSE: rop = R2_MASKPENNOT; break;
1537 case wxCOPY: rop = R2_COPYPEN; break;
1538 case wxAND: rop = R2_MASKPEN; break;
1539 case wxAND_INVERT: rop = R2_MASKNOTPEN; break;
1540 case wxNO_OP: rop = R2_NOP; break;
1541 case wxNOR: rop = R2_NOTMERGEPEN; break;
1542 case wxEQUIV: rop = R2_NOTXORPEN; break;
1543 case wxSRC_INVERT: rop = R2_NOTCOPYPEN; break;
1544 case wxOR_INVERT: rop = R2_MERGENOTPEN; break;
1545 case wxNAND: rop = R2_NOTMASKPEN; break;
1546 case wxOR: rop = R2_MERGEPEN; break;
1547 case wxSET: rop = R2_WHITE; break;
1548
1549 default:
1550 wxFAIL_MSG( wxT("unsupported logical function") );
1551 return;
1552 }
1553
1554 SetROP2(GetHdc(), rop);
1555 }
1556
1557 bool wxDC::StartDoc(const wxString& WXUNUSED(message))
1558 {
1559 // We might be previewing, so return true to let it continue.
1560 return true;
1561 }
1562
1563 void wxDC::EndDoc()
1564 {
1565 }
1566
1567 void wxDC::StartPage()
1568 {
1569 }
1570
1571 void wxDC::EndPage()
1572 {
1573 }
1574
1575 // ---------------------------------------------------------------------------
1576 // text metrics
1577 // ---------------------------------------------------------------------------
1578
1579 wxCoord wxDC::GetCharHeight() const
1580 {
1581 WXMICROWIN_CHECK_HDC_RET(0)
1582
1583 TEXTMETRIC lpTextMetric;
1584
1585 GetTextMetrics(GetHdc(), &lpTextMetric);
1586
1587 return lpTextMetric.tmHeight;
1588 }
1589
1590 wxCoord wxDC::GetCharWidth() const
1591 {
1592 WXMICROWIN_CHECK_HDC_RET(0)
1593
1594 TEXTMETRIC lpTextMetric;
1595
1596 GetTextMetrics(GetHdc(), &lpTextMetric);
1597
1598 return lpTextMetric.tmAveCharWidth;
1599 }
1600
1601 void wxDC::DoGetTextExtent(const wxString& string, wxCoord *x, wxCoord *y,
1602 wxCoord *descent, wxCoord *externalLeading,
1603 wxFont *font) const
1604 {
1605 #ifdef __WXMICROWIN__
1606 if (!GetHDC())
1607 {
1608 if (x) *x = 0;
1609 if (y) *y = 0;
1610 if (descent) *descent = 0;
1611 if (externalLeading) *externalLeading = 0;
1612 return;
1613 }
1614 #endif // __WXMICROWIN__
1615
1616 HFONT hfontOld;
1617 if ( font )
1618 {
1619 wxASSERT_MSG( font->Ok(), _T("invalid font in wxDC::GetTextExtent") );
1620
1621 hfontOld = (HFONT)::SelectObject(GetHdc(), GetHfontOf(*font));
1622 }
1623 else // don't change the font
1624 {
1625 hfontOld = 0;
1626 }
1627
1628 SIZE sizeRect;
1629 TEXTMETRIC tm;
1630
1631 GetTextExtentPoint(GetHdc(), string, string.length(), &sizeRect);
1632 GetTextMetrics(GetHdc(), &tm);
1633
1634 if (x)
1635 *x = sizeRect.cx;
1636 if (y)
1637 *y = sizeRect.cy;
1638 if (descent)
1639 *descent = tm.tmDescent;
1640 if (externalLeading)
1641 *externalLeading = tm.tmExternalLeading;
1642
1643 if ( hfontOld )
1644 {
1645 ::SelectObject(GetHdc(), hfontOld);
1646 }
1647 }
1648
1649
1650 // Each element of the array will be the width of the string up to and
1651 // including the coresoponding character in text.
1652
1653 bool wxDC::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const
1654 {
1655 static int maxLenText = -1;
1656 static int maxWidth = -1;
1657 int fit = 0;
1658 SIZE sz = {0,0};
1659 int stlen = text.Length();
1660
1661 if (maxLenText == -1)
1662 {
1663 // Win9x and WinNT+ have different limits
1664 int version = wxGetOsVersion();
1665 maxLenText = version == wxWINDOWS_NT ? 65535 : 8192;
1666 maxWidth = version == wxWINDOWS_NT ? INT_MAX : 32767;
1667 }
1668
1669 widths.Empty();
1670 widths.Add(0, stlen); // fill the array with zeros
1671
1672 if (!::GetTextExtentExPoint(GetHdc(),
1673 text.c_str(), // string to check
1674 wxMin(stlen, maxLenText),
1675 maxWidth,
1676 &fit, // [out] count of chars
1677 // that will fit
1678 &widths[0], // array to fill
1679 &sz))
1680 {
1681 // API failed
1682 wxLogLastError(wxT("GetTextExtentExPoint"));
1683 return false;
1684 }
1685
1686 return true;
1687 }
1688
1689
1690
1691
1692 void wxDC::SetMapMode(int mode)
1693 {
1694 WXMICROWIN_CHECK_HDC
1695
1696 m_mappingMode = mode;
1697
1698 if ( mode == wxMM_TEXT )
1699 {
1700 m_logicalScaleX =
1701 m_logicalScaleY = 1.0;
1702 }
1703 else // need to do some calculations
1704 {
1705 int pixel_width = ::GetDeviceCaps(GetHdc(), HORZRES),
1706 pixel_height = ::GetDeviceCaps(GetHdc(), VERTRES),
1707 mm_width = ::GetDeviceCaps(GetHdc(), HORZSIZE),
1708 mm_height = ::GetDeviceCaps(GetHdc(), VERTSIZE);
1709
1710 if ( (mm_width == 0) || (mm_height == 0) )
1711 {
1712 // we can't calculate mm2pixels[XY] then!
1713 return;
1714 }
1715
1716 double mm2pixelsX = (double)pixel_width / mm_width,
1717 mm2pixelsY = (double)pixel_height / mm_height;
1718
1719 switch (mode)
1720 {
1721 case wxMM_TWIPS:
1722 m_logicalScaleX = twips2mm * mm2pixelsX;
1723 m_logicalScaleY = twips2mm * mm2pixelsY;
1724 break;
1725
1726 case wxMM_POINTS:
1727 m_logicalScaleX = pt2mm * mm2pixelsX;
1728 m_logicalScaleY = pt2mm * mm2pixelsY;
1729 break;
1730
1731 case wxMM_METRIC:
1732 m_logicalScaleX = mm2pixelsX;
1733 m_logicalScaleY = mm2pixelsY;
1734 break;
1735
1736 case wxMM_LOMETRIC:
1737 m_logicalScaleX = mm2pixelsX / 10.0;
1738 m_logicalScaleY = mm2pixelsY / 10.0;
1739 break;
1740
1741 default:
1742 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1743 }
1744 }
1745
1746 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1747 // cases we could do with MM_TEXT and in the remaining 0.9% with
1748 // MM_ISOTROPIC (TODO!)
1749 #ifndef __WXWINCE__
1750 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
1751
1752 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
1753 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
1754
1755 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
1756 ::SetWindowExtEx(GetHdc(), width, height, NULL);
1757
1758 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX, m_deviceOriginY, NULL);
1759 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX, m_logicalOriginY, NULL);
1760 #endif
1761 }
1762
1763 void wxDC::SetUserScale(double x, double y)
1764 {
1765 WXMICROWIN_CHECK_HDC
1766
1767 #ifndef __WXWINCE__
1768 if ( x == m_userScaleX && y == m_userScaleY )
1769 return;
1770
1771 m_userScaleX = x;
1772 m_userScaleY = y;
1773
1774 SetMapMode(m_mappingMode);
1775 #endif
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 wxWindows 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 wxWindows 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