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