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