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