]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dc.cpp
wxCocoa: Added preliminary Blit support
[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 #ifdef __GNUG__
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 // FIXME: emulate Arc
634 #else
635
636 #ifdef __WXMICROWIN__
637 if (!GetHDC()) return;
638 #endif
639
640 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
641
642 double dx = xc - x1;
643 double dy = yc - y1;
644 double radius = (double)sqrt(dx*dx+dy*dy);
645 wxCoord r = (wxCoord)radius;
646
647 // treat the special case of full circle separately
648 if ( x1 == x2 && y1 == y2 )
649 {
650 DrawEllipse(xc - r, yc - r, 2*r, 2*r);
651 return;
652 }
653
654 wxCoord xx1 = XLOG2DEV(x1);
655 wxCoord yy1 = YLOG2DEV(y1);
656 wxCoord xx2 = XLOG2DEV(x2);
657 wxCoord yy2 = YLOG2DEV(y2);
658 wxCoord xxc = XLOG2DEV(xc);
659 wxCoord yyc = YLOG2DEV(yc);
660 wxCoord ray = (wxCoord) sqrt(double((xxc-xx1)*(xxc-xx1)+(yyc-yy1)*(yyc-yy1)));
661
662 wxCoord xxx1 = (wxCoord) (xxc-ray);
663 wxCoord yyy1 = (wxCoord) (yyc-ray);
664 wxCoord xxx2 = (wxCoord) (xxc+ray);
665 wxCoord yyy2 = (wxCoord) (yyc+ray);
666
667 if ( m_brush.Ok() && m_brush.GetStyle() != wxTRANSPARENT )
668 {
669 // Have to add 1 to bottom-right corner of rectangle
670 // to make semi-circles look right (crooked line otherwise).
671 // Unfortunately this is not a reliable method, depends
672 // on the size of shape.
673 // TODO: figure out why this happens!
674 Pie(GetHdc(),xxx1,yyy1,xxx2+1,yyy2+1, xx1,yy1,xx2,yy2);
675 }
676 else
677 {
678 Arc(GetHdc(),xxx1,yyy1,xxx2,yyy2, xx1,yy1,xx2,yy2);
679 }
680
681 CalcBoundingBox(xc - r, yc - r);
682 CalcBoundingBox(xc + r, yc + r);
683 #endif
684 }
685
686 void wxDC::DoDrawCheckMark(wxCoord x1, wxCoord y1,
687 wxCoord width, wxCoord height)
688 {
689 #ifdef __WXMICROWIN__
690 if (!GetHDC()) return;
691 #endif
692
693 wxCoord x2 = x1 + width,
694 y2 = y1 + height;
695
696 #if defined(__WIN32__) && !defined(__SYMANTEC__) && !defined(__WXMICROWIN__)
697 RECT rect;
698 rect.left = x1;
699 rect.top = y1;
700 rect.right = x2;
701 rect.bottom = y2;
702
703 #ifdef __WXWINCE__
704 DrawFrameControl(GetHdc(), &rect, DFC_BUTTON, DFCS_BUTTONCHECK);
705 #else
706 DrawFrameControl(GetHdc(), &rect, DFC_MENU, DFCS_MENUCHECK);
707 #endif
708 #else // Win16
709 // In WIN16, draw a cross
710 HPEN blackPen = ::CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
711 HPEN whiteBrush = (HPEN)::GetStockObject(WHITE_BRUSH);
712 HPEN hPenOld = (HPEN)::SelectObject(GetHdc(), blackPen);
713 HPEN hBrushOld = (HPEN)::SelectObject(GetHdc(), whiteBrush);
714 ::SetROP2(GetHdc(), R2_COPYPEN);
715 Rectangle(GetHdc(), x1, y1, x2, y2);
716 MoveToEx(GetHdc(), x1, y1, NULL);
717 LineTo(GetHdc(), x2, y2);
718 MoveToEx(GetHdc(), x2, y1, NULL);
719 LineTo(GetHdc(), x1, y2);
720 ::SelectObject(GetHdc(), hPenOld);
721 ::SelectObject(GetHdc(), hBrushOld);
722 ::DeleteObject(blackPen);
723 #endif // Win32/16
724
725 CalcBoundingBox(x1, y1);
726 CalcBoundingBox(x2, y2);
727 }
728
729 void wxDC::DoDrawPoint(wxCoord x, wxCoord y)
730 {
731 #ifdef __WXMICROWIN__
732 if (!GetHDC()) return;
733 #endif
734
735 COLORREF color = 0x00ffffff;
736 if (m_pen.Ok())
737 {
738 color = m_pen.GetColour().GetPixel();
739 }
740
741 SetPixel(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), color);
742
743 CalcBoundingBox(x, y);
744 }
745
746 void wxDC::DoDrawPolygon(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset,int fillStyle)
747 {
748 #ifdef __WXMICROWIN__
749 if (!GetHDC()) return;
750 #endif
751
752 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
753
754 // Do things less efficiently if we have offsets
755 if (xoffset != 0 || yoffset != 0)
756 {
757 POINT *cpoints = new POINT[n];
758 int i;
759 for (i = 0; i < n; i++)
760 {
761 cpoints[i].x = (int)(points[i].x + xoffset);
762 cpoints[i].y = (int)(points[i].y + yoffset);
763
764 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
765 }
766 #ifndef __WXWINCE__
767 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
768 #endif
769 (void)Polygon(GetHdc(), cpoints, n);
770 #ifndef __WXWINCE__
771 SetPolyFillMode(GetHdc(),prev);
772 #endif
773 delete[] cpoints;
774 }
775 else
776 {
777 int i;
778 for (i = 0; i < n; i++)
779 CalcBoundingBox(points[i].x, points[i].y);
780
781 #ifndef __WXWINCE__
782 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
783 #endif
784 (void)Polygon(GetHdc(), (POINT*) points, n);
785 #ifndef __WXWINCE__
786 SetPolyFillMode(GetHdc(),prev);
787 #endif
788 }
789 }
790
791 void wxDC::DoDrawLines(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset)
792 {
793 #ifdef __WXMICROWIN__
794 if (!GetHDC()) return;
795 #endif
796
797 // Do things less efficiently if we have offsets
798 if (xoffset != 0 || yoffset != 0)
799 {
800 POINT *cpoints = new POINT[n];
801 int i;
802 for (i = 0; i < n; i++)
803 {
804 cpoints[i].x = (int)(points[i].x + xoffset);
805 cpoints[i].y = (int)(points[i].y + yoffset);
806
807 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
808 }
809 (void)Polyline(GetHdc(), cpoints, n);
810 delete[] cpoints;
811 }
812 else
813 {
814 int i;
815 for (i = 0; i < n; i++)
816 CalcBoundingBox(points[i].x, points[i].y);
817
818 (void)Polyline(GetHdc(), (POINT*) points, n);
819 }
820 }
821
822 void wxDC::DoDrawRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
823 {
824 #ifdef __WXMICROWIN__
825 if (!GetHDC()) return;
826 #endif
827
828 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
829
830 wxCoord x2 = x + width;
831 wxCoord y2 = y + height;
832
833 if ((m_logicalFunction == wxCOPY) && (m_pen.GetStyle() == wxTRANSPARENT))
834 {
835 RECT rect;
836 rect.left = XLOG2DEV(x);
837 rect.top = YLOG2DEV(y);
838 rect.right = XLOG2DEV(x2);
839 rect.bottom = YLOG2DEV(y2);
840 (void)FillRect(GetHdc(), &rect, (HBRUSH)m_brush.GetResourceHandle() );
841 }
842 else
843 {
844 // Windows draws the filled rectangles without outline (i.e. drawn with a
845 // transparent pen) one pixel smaller in both directions and we want them
846 // to have the same size regardless of which pen is used - adjust
847
848 // I wonder if this shouldn´t be done after the LOG2DEV() conversions. RR.
849 if ( m_pen.GetStyle() == wxTRANSPARENT )
850 {
851 x2++;
852 y2++;
853 }
854
855 (void)Rectangle(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2));
856 }
857
858
859 CalcBoundingBox(x, y);
860 CalcBoundingBox(x2, y2);
861 }
862
863 void wxDC::DoDrawRoundedRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius)
864 {
865 #ifdef __WXMICROWIN__
866 if (!GetHDC()) return;
867 #endif
868
869 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
870
871 // Now, a negative radius value is interpreted to mean
872 // 'the proportion of the smallest X or Y dimension'
873
874 if (radius < 0.0)
875 {
876 double smallest = 0.0;
877 if (width < height)
878 smallest = width;
879 else
880 smallest = height;
881 radius = (- radius * smallest);
882 }
883
884 wxCoord x2 = (x+width);
885 wxCoord y2 = (y+height);
886
887 // Windows draws the filled rectangles without outline (i.e. drawn with a
888 // transparent pen) one pixel smaller in both directions and we want them
889 // to have the same size regardless of which pen is used - adjust
890 if ( m_pen.GetStyle() == wxTRANSPARENT )
891 {
892 x2++;
893 y2++;
894 }
895
896 (void)RoundRect(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2),
897 YLOG2DEV(y2), (int) (2*XLOG2DEV(radius)), (int)( 2*YLOG2DEV(radius)));
898
899 CalcBoundingBox(x, y);
900 CalcBoundingBox(x2, y2);
901 }
902
903 void wxDC::DoDrawEllipse(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
904 {
905 #ifdef __WXMICROWIN__
906 if (!GetHDC()) return;
907 #endif
908
909 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
910
911 wxCoord x2 = (x+width);
912 wxCoord y2 = (y+height);
913
914 (void)Ellipse(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2));
915
916 CalcBoundingBox(x, y);
917 CalcBoundingBox(x2, y2);
918 }
919
920 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
921 void wxDC::DoDrawEllipticArc(wxCoord x,wxCoord y,wxCoord w,wxCoord h,double sa,double ea)
922 {
923 #ifdef __WXWINCE__
924 // FIXME
925 #else
926
927 #ifdef __WXMICROWIN__
928 if (!GetHDC()) return;
929 #endif
930
931 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
932
933 wxCoord x2 = x + w;
934 wxCoord y2 = y + h;
935
936 int rx1 = XLOG2DEV(x+w/2);
937 int ry1 = YLOG2DEV(y+h/2);
938 int rx2 = rx1;
939 int ry2 = ry1;
940
941 sa = DegToRad(sa);
942 ea = DegToRad(ea);
943
944 rx1 += (int)(100.0 * abs(w) * cos(sa));
945 ry1 -= (int)(100.0 * abs(h) * m_signY * sin(sa));
946 rx2 += (int)(100.0 * abs(w) * cos(ea));
947 ry2 -= (int)(100.0 * abs(h) * m_signY * sin(ea));
948
949 // draw pie with NULL_PEN first and then outline otherwise a line is
950 // drawn from the start and end points to the centre
951 HPEN hpenOld = (HPEN) ::SelectObject(GetHdc(), (HPEN) ::GetStockObject(NULL_PEN));
952 if (m_signY > 0)
953 {
954 (void)Pie(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2)+1, YLOG2DEV(y2)+1,
955 rx1, ry1, rx2, ry2);
956 }
957 else
958 {
959 (void)Pie(GetHdc(), XLOG2DEV(x), YLOG2DEV(y)-1, XLOG2DEV(x2)+1, YLOG2DEV(y2),
960 rx1, ry1-1, rx2, ry2-1);
961 }
962
963 ::SelectObject(GetHdc(), hpenOld);
964
965 (void)Arc(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2),
966 rx1, ry1, rx2, ry2);
967
968 CalcBoundingBox(x, y);
969 CalcBoundingBox(x2, y2);
970 #endif
971 }
972
973 void wxDC::DoDrawIcon(const wxIcon& icon, wxCoord x, wxCoord y)
974 {
975 #ifdef __WXMICROWIN__
976 if (!GetHDC()) return;
977 #endif
978
979 wxCHECK_RET( icon.Ok(), wxT("invalid icon in DrawIcon") );
980
981 #ifdef __WIN32__
982 ::DrawIconEx(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), GetHiconOf(icon), icon.GetWidth(), icon.GetHeight(), 0, NULL, DI_NORMAL);
983 #else
984 ::DrawIcon(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), GetHiconOf(icon));
985 #endif
986
987 CalcBoundingBox(x, y);
988 CalcBoundingBox(x + icon.GetWidth(), y + icon.GetHeight());
989 }
990
991 void wxDC::DoDrawBitmap( const wxBitmap &bmp, wxCoord x, wxCoord y, bool useMask )
992 {
993 #ifdef __WXMICROWIN__
994 if (!GetHDC()) return;
995 #endif
996
997 wxCHECK_RET( bmp.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
998
999 int width = bmp.GetWidth(),
1000 height = bmp.GetHeight();
1001
1002 HBITMAP hbmpMask = 0;
1003
1004 #if wxUSE_PALETTE
1005 HPALETTE oldPal = 0;
1006 #endif // wxUSE_PALETTE
1007
1008 if ( bmp.HasAlpha() )
1009 {
1010 MemoryHDC hdcMem;
1011 SelectInHDC select(hdcMem, GetHbitmapOf(bmp));
1012
1013 if ( AlphaBlt(GetHdc(), x, y, width, height, hdcMem, bmp) )
1014 return;
1015 }
1016
1017 if ( useMask )
1018 {
1019 wxMask *mask = bmp.GetMask();
1020 if ( mask )
1021 hbmpMask = (HBITMAP)mask->GetMaskBitmap();
1022
1023 if ( !hbmpMask )
1024 {
1025 // don't give assert here because this would break existing
1026 // programs - just silently ignore useMask parameter
1027 useMask = FALSE;
1028 }
1029 }
1030 if ( useMask )
1031 {
1032 #ifdef __WIN32__
1033 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1034 // points
1035 // On some systems, MaskBlt succeeds yet is much much slower
1036 // than the wxWindows fall-back implementation. So we need
1037 // to be able to switch this on and off at runtime.
1038 bool ok = FALSE;
1039 #if wxUSE_SYSTEM_OPTIONS
1040 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1041 #endif
1042 {
1043 HDC cdc = GetHdc();
1044 HDC hdcMem = ::CreateCompatibleDC(GetHdc());
1045 HGDIOBJ hOldBitmap = ::SelectObject(hdcMem, GetHbitmapOf(bmp));
1046 #if wxUSE_PALETTE
1047 wxPalette *pal = bmp.GetPalette();
1048 if ( pal && ::GetDeviceCaps(cdc,BITSPIXEL) <= 8 )
1049 {
1050 oldPal = ::SelectPalette(hdcMem, GetHpaletteOf(*pal), FALSE);
1051 ::RealizePalette(hdcMem);
1052 }
1053 #endif // wxUSE_PALETTE
1054
1055 ok = ::MaskBlt(cdc, x, y, width, height,
1056 hdcMem, 0, 0,
1057 hbmpMask, 0, 0,
1058 MAKEROP4(SRCCOPY, DSTCOPY)) != 0;
1059
1060 #if wxUSE_PALETTE
1061 if (oldPal)
1062 ::SelectPalette(hdcMem, oldPal, FALSE);
1063 #endif // wxUSE_PALETTE
1064
1065 ::SelectObject(hdcMem, hOldBitmap);
1066 ::DeleteDC(hdcMem);
1067 }
1068
1069 if ( !ok )
1070 #endif // Win32
1071 {
1072 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
1073 // level
1074 wxMemoryDC memDC;
1075 memDC.SelectObject(bmp);
1076
1077 Blit(x, y, width, height, &memDC, 0, 0, wxCOPY, useMask);
1078
1079 memDC.SelectObject(wxNullBitmap);
1080 }
1081 }
1082 else // no mask, just use BitBlt()
1083 {
1084 HDC cdc = GetHdc();
1085 HDC memdc = ::CreateCompatibleDC( cdc );
1086 HBITMAP hbitmap = (HBITMAP) bmp.GetHBITMAP( );
1087
1088 wxASSERT_MSG( hbitmap, wxT("bitmap is ok but HBITMAP is NULL?") );
1089
1090 COLORREF old_textground = ::GetTextColor(GetHdc());
1091 COLORREF old_background = ::GetBkColor(GetHdc());
1092 if (m_textForegroundColour.Ok())
1093 {
1094 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1095 }
1096 if (m_textBackgroundColour.Ok())
1097 {
1098 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1099 }
1100
1101 #if wxUSE_PALETTE
1102 wxPalette *pal = bmp.GetPalette();
1103 if ( pal && ::GetDeviceCaps(cdc,BITSPIXEL) <= 8 )
1104 {
1105 oldPal = ::SelectPalette(memdc, GetHpaletteOf(*pal), FALSE);
1106 ::RealizePalette(memdc);
1107 }
1108 #endif // wxUSE_PALETTE
1109
1110 HGDIOBJ hOldBitmap = ::SelectObject( memdc, hbitmap );
1111 ::BitBlt( cdc, x, y, width, height, memdc, 0, 0, SRCCOPY);
1112
1113 #if wxUSE_PALETTE
1114 if (oldPal)
1115 ::SelectPalette(memdc, oldPal, FALSE);
1116 #endif // wxUSE_PALETTE
1117
1118 ::SelectObject( memdc, hOldBitmap );
1119 ::DeleteDC( memdc );
1120
1121 ::SetTextColor(GetHdc(), old_textground);
1122 ::SetBkColor(GetHdc(), old_background);
1123 }
1124 }
1125
1126 void wxDC::DoDrawText(const wxString& text, wxCoord x, wxCoord y)
1127 {
1128 #ifdef __WXMICROWIN__
1129 if (!GetHDC()) return;
1130 #endif
1131
1132 DrawAnyText(text, x, y);
1133
1134 // update the bounding box
1135 CalcBoundingBox(x, y);
1136
1137 wxCoord w, h;
1138 GetTextExtent(text, &w, &h);
1139 CalcBoundingBox(x + w, y + h);
1140 }
1141
1142 void wxDC::DrawAnyText(const wxString& text, wxCoord x, wxCoord y)
1143 {
1144 #ifdef __WXMICROWIN__
1145 if (!GetHDC()) return;
1146 #endif
1147
1148 // prepare for drawing the text
1149 if ( m_textForegroundColour.Ok() )
1150 SetTextColor(GetHdc(), m_textForegroundColour.GetPixel());
1151
1152 DWORD old_background = 0;
1153 if ( m_textBackgroundColour.Ok() )
1154 {
1155 old_background = SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1156 }
1157
1158 SetBkMode(GetHdc(), m_backgroundMode == wxTRANSPARENT ? TRANSPARENT
1159 : OPAQUE);
1160
1161 #ifdef __WXWINCE__
1162 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), 0, NULL,
1163 text.c_str(), text.length(), NULL) == 0 )
1164 {
1165 wxLogLastError(wxT("TextOut"));
1166 }
1167 #else
1168 if ( ::TextOut(GetHdc(), XLOG2DEV(x), YLOG2DEV(y),
1169 text.c_str(), text.length()) == 0 )
1170 {
1171 wxLogLastError(wxT("TextOut"));
1172 }
1173 #endif
1174
1175 // restore the old parameters (text foreground colour may be left because
1176 // it never is set to anything else, but background should remain
1177 // transparent even if we just drew an opaque string)
1178 if ( m_textBackgroundColour.Ok() )
1179 (void)SetBkColor(GetHdc(), old_background);
1180
1181 SetBkMode(GetHdc(), TRANSPARENT);
1182 }
1183
1184 void wxDC::DoDrawRotatedText(const wxString& text,
1185 wxCoord x, wxCoord y,
1186 double angle)
1187 {
1188 #ifdef __WXMICROWIN__
1189 if (!GetHDC()) return;
1190 #endif
1191
1192 // we test that we have some font because otherwise we should still use the
1193 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1194 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1195 // font for drawing rotated fonts unfortunately)
1196 if ( (angle == 0.0) && m_font.Ok() )
1197 {
1198 DoDrawText(text, x, y);
1199 }
1200 #ifndef __WXMICROWIN__
1201 else
1202 {
1203 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1204 // because it's not TrueType and so can't have non zero
1205 // orientation/escapement under Win9x
1206 wxFont font = m_font.Ok() ? m_font : *wxSWISS_FONT;
1207 HFONT hfont = (HFONT)font.GetResourceHandle();
1208 LOGFONT lf;
1209 if ( ::GetObject(hfont, sizeof(lf), &lf) == 0 )
1210 {
1211 wxLogLastError(wxT("GetObject(hfont)"));
1212 }
1213
1214 // GDI wants the angle in tenth of degree
1215 long angle10 = (long)(angle * 10);
1216 lf.lfEscapement = angle10;
1217 lf. lfOrientation = angle10;
1218
1219 hfont = ::CreateFontIndirect(&lf);
1220 if ( !hfont )
1221 {
1222 wxLogLastError(wxT("CreateFont"));
1223 }
1224 else
1225 {
1226 HFONT hfontOld = (HFONT)::SelectObject(GetHdc(), hfont);
1227
1228 DrawAnyText(text, x, y);
1229
1230 (void)::SelectObject(GetHdc(), hfontOld);
1231 (void)::DeleteObject(hfont);
1232 }
1233
1234 // call the bounding box by adding all four vertices of the rectangle
1235 // containing the text to it (simpler and probably not slower than
1236 // determining which of them is really topmost/leftmost/...)
1237 wxCoord w, h;
1238 GetTextExtent(text, &w, &h);
1239
1240 double rad = DegToRad(angle);
1241
1242 // "upper left" and "upper right"
1243 CalcBoundingBox(x, y);
1244 CalcBoundingBox(x + wxCoord(w*cos(rad)), y - wxCoord(h*sin(rad)));
1245
1246 // "bottom left" and "bottom right"
1247 x += (wxCoord)(h*sin(rad));
1248 y += (wxCoord)(h*cos(rad));
1249 CalcBoundingBox(x, y);
1250 CalcBoundingBox(x + wxCoord(h*sin(rad)), y + wxCoord(h*cos(rad)));
1251 }
1252 #endif
1253 }
1254
1255 // ---------------------------------------------------------------------------
1256 // set GDI objects
1257 // ---------------------------------------------------------------------------
1258
1259 #if wxUSE_PALETTE
1260
1261 void wxDC::DoSelectPalette(bool realize)
1262 {
1263 #ifdef __WXMICROWIN__
1264 if (!GetHDC()) return;
1265 #endif
1266
1267 // Set the old object temporarily, in case the assignment deletes an object
1268 // that's not yet selected out.
1269 if (m_oldPalette)
1270 {
1271 ::SelectPalette(GetHdc(), (HPALETTE) m_oldPalette, FALSE);
1272 m_oldPalette = 0;
1273 }
1274
1275 if ( m_palette.Ok() )
1276 {
1277 HPALETTE oldPal = ::SelectPalette(GetHdc(),
1278 GetHpaletteOf(m_palette),
1279 FALSE);
1280 if (!m_oldPalette)
1281 m_oldPalette = (WXHPALETTE) oldPal;
1282
1283 if (realize)
1284 ::RealizePalette(GetHdc());
1285 }
1286 }
1287
1288 void wxDC::SetPalette(const wxPalette& palette)
1289 {
1290 if ( palette.Ok() )
1291 {
1292 m_palette = palette;
1293 DoSelectPalette(TRUE);
1294 }
1295 }
1296
1297 void wxDC::InitializePalette()
1298 {
1299 if ( wxDisplayDepth() <= 8 )
1300 {
1301 // look for any window or parent that has a custom palette. If any has
1302 // one then we need to use it in drawing operations
1303 wxWindow *win = m_canvas->GetAncestorWithCustomPalette();
1304
1305 m_hasCustomPalette = win && win->HasCustomPalette();
1306 if ( m_hasCustomPalette )
1307 {
1308 m_palette = win->GetPalette();
1309
1310 // turn on MSW translation for this palette
1311 DoSelectPalette();
1312 }
1313 }
1314 }
1315
1316 #endif // wxUSE_PALETTE
1317
1318 void wxDC::SetFont(const wxFont& the_font)
1319 {
1320 #ifdef __WXMICROWIN__
1321 if (!GetHDC()) return;
1322 #endif
1323
1324 // Set the old object temporarily, in case the assignment deletes an object
1325 // that's not yet selected out.
1326 if (m_oldFont)
1327 {
1328 ::SelectObject(GetHdc(), (HFONT) m_oldFont);
1329 m_oldFont = 0;
1330 }
1331
1332 m_font = the_font;
1333
1334 if (!the_font.Ok())
1335 {
1336 if (m_oldFont)
1337 ::SelectObject(GetHdc(), (HFONT) m_oldFont);
1338 m_oldFont = 0;
1339 }
1340
1341 if (m_font.Ok() && m_font.GetResourceHandle())
1342 {
1343 HFONT f = (HFONT) ::SelectObject(GetHdc(), (HFONT) m_font.GetResourceHandle());
1344 if (f == (HFONT) NULL)
1345 {
1346 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
1347 }
1348 if (!m_oldFont)
1349 m_oldFont = (WXHFONT) f;
1350 }
1351 }
1352
1353 void wxDC::SetPen(const wxPen& pen)
1354 {
1355 #ifdef __WXMICROWIN__
1356 if (!GetHDC()) return;
1357 #endif
1358
1359 // Set the old object temporarily, in case the assignment deletes an object
1360 // that's not yet selected out.
1361 if (m_oldPen)
1362 {
1363 ::SelectObject(GetHdc(), (HPEN) m_oldPen);
1364 m_oldPen = 0;
1365 }
1366
1367 m_pen = pen;
1368
1369 if (!m_pen.Ok())
1370 {
1371 if (m_oldPen)
1372 ::SelectObject(GetHdc(), (HPEN) m_oldPen);
1373 m_oldPen = 0;
1374 }
1375
1376 if (m_pen.Ok())
1377 {
1378 if (m_pen.GetResourceHandle())
1379 {
1380 HPEN p = (HPEN) ::SelectObject(GetHdc(), (HPEN)m_pen.GetResourceHandle());
1381 if (!m_oldPen)
1382 m_oldPen = (WXHPEN) p;
1383 }
1384 }
1385 }
1386
1387 void wxDC::SetBrush(const wxBrush& brush)
1388 {
1389 #ifdef __WXMICROWIN__
1390 if (!GetHDC()) return;
1391 #endif
1392
1393 // Set the old object temporarily, in case the assignment deletes an object
1394 // that's not yet selected out.
1395 if (m_oldBrush)
1396 {
1397 ::SelectObject(GetHdc(), (HBRUSH) m_oldBrush);
1398 m_oldBrush = 0;
1399 }
1400
1401 m_brush = brush;
1402
1403 if (!m_brush.Ok())
1404 {
1405 if (m_oldBrush)
1406 ::SelectObject(GetHdc(), (HBRUSH) m_oldBrush);
1407 m_oldBrush = 0;
1408 }
1409
1410 if (m_brush.Ok())
1411 {
1412 // to make sure the brush is alligned with the logical coordinates
1413 wxBitmap *stipple = m_brush.GetStipple();
1414 if ( stipple && stipple->Ok() )
1415 {
1416 #ifdef __WIN32__
1417 ::SetBrushOrgEx(GetHdc(),
1418 m_deviceOriginX % stipple->GetWidth(),
1419 m_deviceOriginY % stipple->GetHeight(),
1420 NULL); // don't need previous brush origin
1421 #else
1422 ::SetBrushOrg(GetHdc(),
1423 m_deviceOriginX % stipple->GetWidth(),
1424 m_deviceOriginY % stipple->GetHeight());
1425 #endif
1426 }
1427
1428 if ( m_brush.GetResourceHandle() )
1429 {
1430 HBRUSH b = 0;
1431 b = (HBRUSH) ::SelectObject(GetHdc(), (HBRUSH)m_brush.GetResourceHandle());
1432 if (!m_oldBrush)
1433 m_oldBrush = (WXHBRUSH) b;
1434 }
1435 }
1436 }
1437
1438 void wxDC::SetBackground(const wxBrush& brush)
1439 {
1440 #ifdef __WXMICROWIN__
1441 if (!GetHDC()) return;
1442 #endif
1443
1444 m_backgroundBrush = brush;
1445
1446 if ( m_backgroundBrush.Ok() )
1447 {
1448 (void)SetBkColor(GetHdc(), m_backgroundBrush.GetColour().GetPixel());
1449 }
1450 }
1451
1452 void wxDC::SetBackgroundMode(int mode)
1453 {
1454 #ifdef __WXMICROWIN__
1455 if (!GetHDC()) return;
1456 #endif
1457
1458 m_backgroundMode = mode;
1459
1460 // SetBackgroundColour now only refers to text background
1461 // and m_backgroundMode is used there
1462 }
1463
1464 void wxDC::SetLogicalFunction(int function)
1465 {
1466 #ifdef __WXMICROWIN__
1467 if (!GetHDC()) return;
1468 #endif
1469
1470 m_logicalFunction = function;
1471
1472 SetRop(m_hDC);
1473 }
1474
1475 void wxDC::SetRop(WXHDC dc)
1476 {
1477 if ( !dc || m_logicalFunction < 0 )
1478 return;
1479
1480 int rop;
1481
1482 switch (m_logicalFunction)
1483 {
1484 case wxCLEAR: rop = R2_BLACK; break;
1485 case wxXOR: rop = R2_XORPEN; break;
1486 case wxINVERT: rop = R2_NOT; break;
1487 case wxOR_REVERSE: rop = R2_MERGEPENNOT; break;
1488 case wxAND_REVERSE: rop = R2_MASKPENNOT; break;
1489 case wxCOPY: rop = R2_COPYPEN; break;
1490 case wxAND: rop = R2_MASKPEN; break;
1491 case wxAND_INVERT: rop = R2_MASKNOTPEN; break;
1492 case wxNO_OP: rop = R2_NOP; break;
1493 case wxNOR: rop = R2_NOTMERGEPEN; break;
1494 case wxEQUIV: rop = R2_NOTXORPEN; break;
1495 case wxSRC_INVERT: rop = R2_NOTCOPYPEN; break;
1496 case wxOR_INVERT: rop = R2_MERGENOTPEN; break;
1497 case wxNAND: rop = R2_NOTMASKPEN; break;
1498 case wxOR: rop = R2_MERGEPEN; break;
1499 case wxSET: rop = R2_WHITE; break;
1500
1501 default:
1502 wxFAIL_MSG( wxT("unsupported logical function") );
1503 return;
1504 }
1505
1506 SetROP2(GetHdc(), rop);
1507 }
1508
1509 bool wxDC::StartDoc(const wxString& WXUNUSED(message))
1510 {
1511 // We might be previewing, so return TRUE to let it continue.
1512 return TRUE;
1513 }
1514
1515 void wxDC::EndDoc()
1516 {
1517 }
1518
1519 void wxDC::StartPage()
1520 {
1521 }
1522
1523 void wxDC::EndPage()
1524 {
1525 }
1526
1527 // ---------------------------------------------------------------------------
1528 // text metrics
1529 // ---------------------------------------------------------------------------
1530
1531 wxCoord wxDC::GetCharHeight() const
1532 {
1533 #ifdef __WXMICROWIN__
1534 if (!GetHDC()) return 0;
1535 #endif
1536
1537 TEXTMETRIC lpTextMetric;
1538
1539 GetTextMetrics(GetHdc(), &lpTextMetric);
1540
1541 return lpTextMetric.tmHeight;
1542 }
1543
1544 wxCoord wxDC::GetCharWidth() const
1545 {
1546 #ifdef __WXMICROWIN__
1547 if (!GetHDC()) return 0;
1548 #endif
1549
1550 TEXTMETRIC lpTextMetric;
1551
1552 GetTextMetrics(GetHdc(), &lpTextMetric);
1553
1554 return lpTextMetric.tmAveCharWidth;
1555 }
1556
1557 void wxDC::DoGetTextExtent(const wxString& string, wxCoord *x, wxCoord *y,
1558 wxCoord *descent, wxCoord *externalLeading,
1559 wxFont *font) const
1560 {
1561 #ifdef __WXMICROWIN__
1562 if (!GetHDC())
1563 {
1564 if (x) *x = 0;
1565 if (y) *y = 0;
1566 if (descent) *descent = 0;
1567 if (externalLeading) *externalLeading = 0;
1568 return;
1569 }
1570 #endif // __WXMICROWIN__
1571
1572 HFONT hfontOld;
1573 if ( font )
1574 {
1575 wxASSERT_MSG( font->Ok(), _T("invalid font in wxDC::GetTextExtent") );
1576
1577 hfontOld = (HFONT)::SelectObject(GetHdc(), GetHfontOf(*font));
1578 }
1579 else // don't change the font
1580 {
1581 hfontOld = 0;
1582 }
1583
1584 SIZE sizeRect;
1585 TEXTMETRIC tm;
1586
1587 GetTextExtentPoint(GetHdc(), string, string.length(), &sizeRect);
1588 GetTextMetrics(GetHdc(), &tm);
1589
1590 if (x)
1591 *x = sizeRect.cx;
1592 if (y)
1593 *y = sizeRect.cy;
1594 if (descent)
1595 *descent = tm.tmDescent;
1596 if (externalLeading)
1597 *externalLeading = tm.tmExternalLeading;
1598
1599 if ( hfontOld )
1600 {
1601 ::SelectObject(GetHdc(), hfontOld);
1602 }
1603 }
1604
1605 void wxDC::SetMapMode(int mode)
1606 {
1607 #ifdef __WXMICROWIN__
1608 if (!GetHDC()) return;
1609 #endif
1610
1611 m_mappingMode = mode;
1612
1613 if ( mode == wxMM_TEXT )
1614 {
1615 m_logicalScaleX =
1616 m_logicalScaleY = 1.0;
1617 }
1618 else // need to do some calculations
1619 {
1620 int pixel_width = ::GetDeviceCaps(GetHdc(), HORZRES),
1621 pixel_height = ::GetDeviceCaps(GetHdc(), VERTRES),
1622 mm_width = ::GetDeviceCaps(GetHdc(), HORZSIZE),
1623 mm_height = ::GetDeviceCaps(GetHdc(), VERTSIZE);
1624
1625 if ( (mm_width == 0) || (mm_height == 0) )
1626 {
1627 // we can't calculate mm2pixels[XY] then!
1628 return;
1629 }
1630
1631 double mm2pixelsX = (double)pixel_width / mm_width,
1632 mm2pixelsY = (double)pixel_height / mm_height;
1633
1634 switch (mode)
1635 {
1636 case wxMM_TWIPS:
1637 m_logicalScaleX = twips2mm * mm2pixelsX;
1638 m_logicalScaleY = twips2mm * mm2pixelsY;
1639 break;
1640
1641 case wxMM_POINTS:
1642 m_logicalScaleX = pt2mm * mm2pixelsX;
1643 m_logicalScaleY = pt2mm * mm2pixelsY;
1644 break;
1645
1646 case wxMM_METRIC:
1647 m_logicalScaleX = mm2pixelsX;
1648 m_logicalScaleY = mm2pixelsY;
1649 break;
1650
1651 case wxMM_LOMETRIC:
1652 m_logicalScaleX = mm2pixelsX / 10.0;
1653 m_logicalScaleY = mm2pixelsY / 10.0;
1654 break;
1655
1656 default:
1657 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1658 }
1659 }
1660
1661 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1662 // cases we could do with MM_TEXT and in the remaining 0.9% with
1663 // MM_ISOTROPIC (TODO!)
1664 #ifndef __WXWINCE__
1665 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
1666
1667 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
1668 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
1669
1670 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
1671 ::SetWindowExtEx(GetHdc(), width, height, NULL);
1672
1673 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX, m_deviceOriginY, NULL);
1674 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX, m_logicalOriginY, NULL);
1675 #endif
1676 }
1677
1678 void wxDC::SetUserScale(double x, double y)
1679 {
1680 #ifdef __WXMICROWIN__
1681 if (!GetHDC()) return;
1682 #endif
1683
1684 #ifndef __WXWINCE__
1685 if ( x == m_userScaleX && y == m_userScaleY )
1686 return;
1687
1688 m_userScaleX = x;
1689 m_userScaleY = y;
1690
1691 SetMapMode(m_mappingMode);
1692 #endif
1693 }
1694
1695 void wxDC::SetAxisOrientation(bool xLeftRight, bool yBottomUp)
1696 {
1697 #ifdef __WXMICROWIN__
1698 if (!GetHDC()) return;
1699 #endif
1700
1701 #ifndef __WXWINCE__
1702 int signX = xLeftRight ? 1 : -1,
1703 signY = yBottomUp ? -1 : 1;
1704
1705 if ( signX != m_signX || signY != m_signY )
1706 {
1707 m_signX = signX;
1708 m_signY = signY;
1709
1710 SetMapMode(m_mappingMode);
1711 }
1712 #endif
1713 }
1714
1715 void wxDC::SetSystemScale(double x, double y)
1716 {
1717 #ifdef __WXMICROWIN__
1718 if (!GetHDC()) return;
1719 #endif
1720
1721 #ifndef __WXWINCE__
1722 if ( x == m_scaleX && y == m_scaleY )
1723 return;
1724
1725 m_scaleX = x;
1726 m_scaleY = y;
1727
1728 SetMapMode(m_mappingMode);
1729 #endif
1730 }
1731
1732 void wxDC::SetLogicalOrigin(wxCoord x, wxCoord y)
1733 {
1734 #ifdef __WXMICROWIN__
1735 if (!GetHDC()) return;
1736 #endif
1737
1738 #ifndef __WXWINCE__
1739 if ( x == m_logicalOriginX && y == m_logicalOriginY )
1740 return;
1741
1742 m_logicalOriginX = x;
1743 m_logicalOriginY = y;
1744
1745 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
1746 #endif
1747 }
1748
1749 void wxDC::SetDeviceOrigin(wxCoord x, wxCoord y)
1750 {
1751 #ifdef __WXMICROWIN__
1752 if (!GetHDC()) return;
1753 #endif
1754
1755 #ifndef __WXWINCE__
1756 if ( x == m_deviceOriginX && y == m_deviceOriginY )
1757 return;
1758
1759 m_deviceOriginX = x;
1760 m_deviceOriginY = y;
1761
1762 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
1763 #endif
1764 }
1765
1766 // ---------------------------------------------------------------------------
1767 // coordinates transformations
1768 // ---------------------------------------------------------------------------
1769
1770 wxCoord wxDCBase::DeviceToLogicalX(wxCoord x) const
1771 {
1772 return DeviceToLogicalXRel(x - m_deviceOriginX)*m_signX + m_logicalOriginX;
1773 }
1774
1775 wxCoord wxDCBase::DeviceToLogicalXRel(wxCoord x) const
1776 {
1777 // axis orientation is not taken into account for conversion of a distance
1778 return (wxCoord)(x / (m_logicalScaleX*m_userScaleX*m_scaleX));
1779 }
1780
1781 wxCoord wxDCBase::DeviceToLogicalY(wxCoord y) const
1782 {
1783 return DeviceToLogicalYRel(y - m_deviceOriginY)*m_signY + m_logicalOriginY;
1784 }
1785
1786 wxCoord wxDCBase::DeviceToLogicalYRel(wxCoord y) const
1787 {
1788 // axis orientation is not taken into account for conversion of a distance
1789 return (wxCoord)( y / (m_logicalScaleY*m_userScaleY*m_scaleY));
1790 }
1791
1792 wxCoord wxDCBase::LogicalToDeviceX(wxCoord x) const
1793 {
1794 return LogicalToDeviceXRel(x - m_logicalOriginX)*m_signX + m_deviceOriginX;
1795 }
1796
1797 wxCoord wxDCBase::LogicalToDeviceXRel(wxCoord x) const
1798 {
1799 // axis orientation is not taken into account for conversion of a distance
1800 return (wxCoord) (x*m_logicalScaleX*m_userScaleX*m_scaleX);
1801 }
1802
1803 wxCoord wxDCBase::LogicalToDeviceY(wxCoord y) const
1804 {
1805 return LogicalToDeviceYRel(y - m_logicalOriginY)*m_signY + m_deviceOriginY;
1806 }
1807
1808 wxCoord wxDCBase::LogicalToDeviceYRel(wxCoord y) const
1809 {
1810 // axis orientation is not taken into account for conversion of a distance
1811 return (wxCoord) (y*m_logicalScaleY*m_userScaleY*m_scaleY);
1812 }
1813
1814 // ---------------------------------------------------------------------------
1815 // bit blit
1816 // ---------------------------------------------------------------------------
1817
1818 bool wxDC::DoBlit(wxCoord xdest, wxCoord ydest,
1819 wxCoord width, wxCoord height,
1820 wxDC *source, wxCoord xsrc, wxCoord ysrc,
1821 int rop, bool useMask,
1822 wxCoord xsrcMask, wxCoord ysrcMask)
1823 {
1824 wxCHECK_MSG( source, FALSE, _T("wxDC::Blit(): NULL wxDC pointer") );
1825
1826 #ifdef __WXMICROWIN__
1827 if (!GetHDC()) return FALSE;
1828 #endif
1829
1830 const wxBitmap& bmpSrc = source->m_selectedBitmap;
1831 if ( bmpSrc.Ok() && bmpSrc.HasAlpha() )
1832 {
1833 if ( AlphaBlt(GetHdc(), xdest, ydest, width, height,
1834 GetHdcOf(*source), bmpSrc) )
1835 return TRUE;
1836 }
1837
1838 wxMask *mask = NULL;
1839 if ( useMask )
1840 {
1841 mask = bmpSrc.GetMask();
1842
1843 if ( !(bmpSrc.Ok() && mask && mask->GetMaskBitmap()) )
1844 {
1845 // don't give assert here because this would break existing
1846 // programs - just silently ignore useMask parameter
1847 useMask = FALSE;
1848 }
1849 }
1850
1851 if (xsrcMask == -1 && ysrcMask == -1)
1852 {
1853 xsrcMask = xsrc; ysrcMask = ysrc;
1854 }
1855
1856 COLORREF old_textground = ::GetTextColor(GetHdc());
1857 COLORREF old_background = ::GetBkColor(GetHdc());
1858 if (m_textForegroundColour.Ok())
1859 {
1860 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1861 }
1862 if (m_textBackgroundColour.Ok())
1863 {
1864 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1865 }
1866
1867 DWORD dwRop = SRCCOPY;
1868 switch (rop)
1869 {
1870 case wxXOR: dwRop = SRCINVERT; break;
1871 case wxINVERT: dwRop = DSTINVERT; break;
1872 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
1873 case wxAND_REVERSE: dwRop = SRCERASE; break;
1874 case wxCLEAR: dwRop = BLACKNESS; break;
1875 case wxSET: dwRop = WHITENESS; break;
1876 case wxOR_INVERT: dwRop = MERGEPAINT; break;
1877 case wxAND: dwRop = SRCAND; break;
1878 case wxOR: dwRop = SRCPAINT; break;
1879 case wxEQUIV: dwRop = 0x00990066; break;
1880 case wxNAND: dwRop = 0x007700E6; break;
1881 case wxAND_INVERT: dwRop = 0x00220326; break;
1882 case wxCOPY: dwRop = SRCCOPY; break;
1883 case wxNO_OP: dwRop = DSTCOPY; break;
1884 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
1885 case wxNOR: dwRop = NOTSRCCOPY; break;
1886 default:
1887 wxFAIL_MSG( wxT("unsupported logical function") );
1888 return FALSE;
1889 }
1890
1891 bool success = FALSE;
1892
1893 if (useMask)
1894 {
1895 #ifdef __WIN32__
1896 // we want the part of the image corresponding to the mask to be
1897 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1898 // meaning of fg and bg is inverted which corresponds to wxWin notion
1899 // of the mask which is also contrary to the Windows one)
1900
1901 // On some systems, MaskBlt succeeds yet is much much slower
1902 // than the wxWindows fall-back implementation. So we need
1903 // to be able to switch this on and off at runtime.
1904 #if wxUSE_SYSTEM_OPTIONS
1905 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1906 #endif
1907 {
1908 success = ::MaskBlt
1909 (
1910 GetHdc(),
1911 xdest, ydest, width, height,
1912 GetHdcOf(*source),
1913 xsrc, ysrc,
1914 (HBITMAP)mask->GetMaskBitmap(),
1915 xsrcMask, ysrcMask,
1916 MAKEROP4(dwRop, DSTCOPY)
1917 ) != 0;
1918 }
1919
1920 if ( !success )
1921 #endif // Win32
1922 {
1923 // Blit bitmap with mask
1924 HDC dc_mask ;
1925 HDC dc_buffer ;
1926 HBITMAP buffer_bmap ;
1927
1928 #if wxUSE_DC_CACHEING
1929 // create a temp buffer bitmap and DCs to access it and the mask
1930 wxDCCacheEntry* dcCacheEntry1 = FindDCInCache(NULL, source->GetHDC());
1931 dc_mask = (HDC) dcCacheEntry1->m_dc;
1932
1933 wxDCCacheEntry* dcCacheEntry2 = FindDCInCache(dcCacheEntry1, GetHDC());
1934 dc_buffer = (HDC) dcCacheEntry2->m_dc;
1935
1936 wxDCCacheEntry* bitmapCacheEntry = FindBitmapInCache(GetHDC(),
1937 width, height);
1938
1939 buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap;
1940 #else // !wxUSE_DC_CACHEING
1941 // create a temp buffer bitmap and DCs to access it and the mask
1942 dc_mask = ::CreateCompatibleDC(GetHdcOf(*source));
1943 dc_buffer = ::CreateCompatibleDC(GetHdc());
1944 buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), width, height);
1945 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
1946 HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
1947 HGDIOBJ hOldBufferBitmap = ::SelectObject(dc_buffer, buffer_bmap);
1948
1949 // copy dest to buffer
1950 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1951 GetHdc(), xdest, ydest, SRCCOPY) )
1952 {
1953 wxLogLastError(wxT("BitBlt"));
1954 }
1955
1956 // copy src to buffer using selected raster op
1957 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1958 GetHdcOf(*source), xsrc, ysrc, dwRop) )
1959 {
1960 wxLogLastError(wxT("BitBlt"));
1961 }
1962
1963 // set masked area in buffer to BLACK (pixel value 0)
1964 COLORREF prevBkCol = ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1965 COLORREF prevCol = ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1966 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1967 dc_mask, xsrcMask, ysrcMask, SRCAND) )
1968 {
1969 wxLogLastError(wxT("BitBlt"));
1970 }
1971
1972 // set unmasked area in dest to BLACK
1973 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1974 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1975 if ( !::BitBlt(GetHdc(), xdest, ydest, (int)width, (int)height,
1976 dc_mask, xsrcMask, ysrcMask, SRCAND) )
1977 {
1978 wxLogLastError(wxT("BitBlt"));
1979 }
1980 ::SetBkColor(GetHdc(), prevBkCol); // restore colours to original values
1981 ::SetTextColor(GetHdc(), prevCol);
1982
1983 // OR buffer to dest
1984 success = ::BitBlt(GetHdc(), xdest, ydest,
1985 (int)width, (int)height,
1986 dc_buffer, 0, 0, SRCPAINT) != 0;
1987 if ( !success )
1988 {
1989 wxLogLastError(wxT("BitBlt"));
1990 }
1991
1992 // tidy up temporary DCs and bitmap
1993 ::SelectObject(dc_mask, hOldMaskBitmap);
1994 ::SelectObject(dc_buffer, hOldBufferBitmap);
1995
1996 #if !wxUSE_DC_CACHEING
1997 {
1998 ::DeleteDC(dc_mask);
1999 ::DeleteDC(dc_buffer);
2000 ::DeleteObject(buffer_bmap);
2001 }
2002 #endif
2003 }
2004 }
2005 else // no mask, just BitBlt() it
2006 {
2007 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2008 // use StretchBlt() if available and finally fall back to BitBlt()
2009
2010 // FIXME: use appropriate WinCE functions
2011 #ifndef __WXWINCE__
2012 const int caps = ::GetDeviceCaps(GetHdc(), RASTERCAPS);
2013 if ( bmpSrc.Ok() && (caps & RC_STRETCHDIB) )
2014 {
2015 DIBSECTION ds;
2016 wxZeroMemory(ds);
2017
2018 if ( ::GetObject(GetHbitmapOf(bmpSrc),
2019 sizeof(ds),
2020 &ds) == sizeof(ds) )
2021 {
2022 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2023
2024 if ( ::StretchDIBits(GetHdc(),
2025 xdest, ydest,
2026 width, height,
2027 0, 0,
2028 width, height,
2029 ds.dsBm.bmBits,
2030 (LPBITMAPINFO)&ds.dsBmih,
2031 DIB_RGB_COLORS,
2032 SRCCOPY
2033 ) == (int)GDI_ERROR )
2034 {
2035 wxLogLastError(wxT("StretchDIBits"));
2036 }
2037 else
2038 {
2039 success = TRUE;
2040 }
2041 }
2042 }
2043
2044 if ( !success && (caps & RC_STRETCHBLT) )
2045 {
2046 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2047
2048 if ( !::StretchBlt
2049 (
2050 GetHdc(),
2051 xdest, ydest, width, height,
2052 GetHdcOf(*source),
2053 xsrc, ysrc, width, height,
2054 dwRop
2055 ) )
2056 {
2057 wxLogLastError(_T("StretchBlt"));
2058 }
2059 else
2060 {
2061 success = TRUE;
2062 }
2063 }
2064
2065 if ( !success )
2066 {
2067 if ( !::BitBlt
2068 (
2069 GetHdc(),
2070 xdest, ydest,
2071 (int)width, (int)height,
2072 GetHdcOf(*source),
2073 xsrc, ysrc,
2074 dwRop
2075 ) )
2076 {
2077 wxLogLastError(_T("BitBlt"));
2078 }
2079 else
2080 {
2081 success = TRUE;
2082 }
2083 }
2084 #endif
2085 // __WXWINCE__
2086 }
2087
2088 ::SetTextColor(GetHdc(), old_textground);
2089 ::SetBkColor(GetHdc(), old_background);
2090
2091 return success;
2092 }
2093
2094 void wxDC::DoGetSize(int *w, int *h) const
2095 {
2096 #ifdef __WXMICROWIN__
2097 if (!GetHDC()) return;
2098 #endif
2099
2100 if ( w ) *w = ::GetDeviceCaps(GetHdc(), HORZRES);
2101 if ( h ) *h = ::GetDeviceCaps(GetHdc(), VERTRES);
2102 }
2103
2104 void wxDC::DoGetSizeMM(int *w, int *h) const
2105 {
2106 #ifdef __WXMICROWIN__
2107 if (!GetHDC()) return;
2108 #endif
2109
2110 // if we implement it in terms of DoGetSize() instead of directly using the
2111 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2112 // will also work for wxWindowDC and wxClientDC even though their size is
2113 // not the same as the total size of the screen
2114 int wPixels, hPixels;
2115 DoGetSize(&wPixels, &hPixels);
2116
2117 if ( w )
2118 {
2119 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
2120
2121 wxCHECK_RET( wTotal, _T("0 width device?") );
2122
2123 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2124 }
2125
2126 if ( h )
2127 {
2128 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2129
2130 wxCHECK_RET( hTotal, _T("0 height device?") );
2131
2132 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2133 }
2134 }
2135
2136 wxSize wxDC::GetPPI() const
2137 {
2138 #ifdef __WXMICROWIN__
2139 if (!GetHDC()) return wxSize();
2140 #endif
2141
2142 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2143 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2144
2145 return wxSize(x, y);
2146 }
2147
2148 // For use by wxWindows only, unless custom units are required.
2149 void wxDC::SetLogicalScale(double x, double y)
2150 {
2151 #ifdef __WXMICROWIN__
2152 if (!GetHDC()) return;
2153 #endif
2154
2155 m_logicalScaleX = x;
2156 m_logicalScaleY = y;
2157 }
2158
2159 // ----------------------------------------------------------------------------
2160 // DC caching
2161 // ----------------------------------------------------------------------------
2162
2163 #if wxUSE_DC_CACHEING
2164
2165 /*
2166 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2167 * improve it in due course, either using arrays, or simply storing pointers to one
2168 * entry for the bitmap, and two for the DCs. -- JACS
2169 */
2170
2171 wxList wxDC::sm_bitmapCache;
2172 wxList wxDC::sm_dcCache;
2173
2174 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2175 {
2176 m_bitmap = hBitmap;
2177 m_dc = 0;
2178 m_width = w;
2179 m_height = h;
2180 m_depth = depth;
2181 }
2182
2183 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2184 {
2185 m_bitmap = 0;
2186 m_dc = hDC;
2187 m_width = 0;
2188 m_height = 0;
2189 m_depth = depth;
2190 }
2191
2192 wxDCCacheEntry::~wxDCCacheEntry()
2193 {
2194 if (m_bitmap)
2195 ::DeleteObject((HBITMAP) m_bitmap);
2196 if (m_dc)
2197 ::DeleteDC((HDC) m_dc);
2198 }
2199
2200 wxDCCacheEntry* wxDC::FindBitmapInCache(WXHDC dc, int w, int h)
2201 {
2202 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2203 wxList::compatibility_iterator node = sm_bitmapCache.GetFirst();
2204 while (node)
2205 {
2206 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2207
2208 if (entry->m_depth == depth)
2209 {
2210 if (entry->m_width < w || entry->m_height < h)
2211 {
2212 ::DeleteObject((HBITMAP) entry->m_bitmap);
2213 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2214 if ( !entry->m_bitmap)
2215 {
2216 wxLogLastError(wxT("CreateCompatibleBitmap"));
2217 }
2218 entry->m_width = w; entry->m_height = h;
2219 return entry;
2220 }
2221 return entry;
2222 }
2223
2224 node = node->GetNext();
2225 }
2226 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2227 if ( !hBitmap)
2228 {
2229 wxLogLastError(wxT("CreateCompatibleBitmap"));
2230 }
2231 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2232 AddToBitmapCache(entry);
2233 return entry;
2234 }
2235
2236 wxDCCacheEntry* wxDC::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
2237 {
2238 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2239 wxList::compatibility_iterator node = sm_dcCache.GetFirst();
2240 while (node)
2241 {
2242 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2243
2244 // Don't return the same one as we already have
2245 if (!notThis || (notThis != entry))
2246 {
2247 if (entry->m_depth == depth)
2248 {
2249 return entry;
2250 }
2251 }
2252
2253 node = node->GetNext();
2254 }
2255 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2256 if ( !hDC)
2257 {
2258 wxLogLastError(wxT("CreateCompatibleDC"));
2259 }
2260 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2261 AddToDCCache(entry);
2262 return entry;
2263 }
2264
2265 void wxDC::AddToBitmapCache(wxDCCacheEntry* entry)
2266 {
2267 sm_bitmapCache.Append(entry);
2268 }
2269
2270 void wxDC::AddToDCCache(wxDCCacheEntry* entry)
2271 {
2272 sm_dcCache.Append(entry);
2273 }
2274
2275 void wxDC::ClearCache()
2276 {
2277 WX_CLEAR_LIST(wxList, sm_dcCache);
2278 WX_CLEAR_LIST(wxList, sm_bitmapCache);
2279 }
2280
2281 // Clean up cache at app exit
2282 class wxDCModule : public wxModule
2283 {
2284 public:
2285 virtual bool OnInit() { return TRUE; }
2286 virtual void OnExit() { wxDC::ClearCache(); }
2287
2288 private:
2289 DECLARE_DYNAMIC_CLASS(wxDCModule)
2290 };
2291
2292 IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2293
2294 #endif // wxUSE_DC_CACHEING
2295
2296 // ----------------------------------------------------------------------------
2297 // alpha channel support
2298 // ----------------------------------------------------------------------------
2299
2300 static bool AlphaBlt(HDC hdcDst,
2301 int x, int y, int width, int height,
2302 HDC hdcSrc,
2303 const wxBitmap& bmp)
2304 {
2305 wxASSERT_MSG( bmp.Ok() && bmp.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2306 wxASSERT_MSG( hdcDst && hdcSrc, _T("AlphaBlt(): invalid HDC") );
2307
2308 // do we have AlphaBlend() and company in the headers?
2309 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2310 // yes, now try to see if we have it during run-time
2311 typedef BOOL (WINAPI *AlphaBlend_t)(HDC,int,int,int,int,
2312 HDC,int,int,int,int,
2313 BLENDFUNCTION);
2314
2315 // bitmaps can be drawn only from GUI thread so there is no need to
2316 // protect this static variable from multiple threads
2317 static bool s_triedToLoad = FALSE;
2318 static AlphaBlend_t pfnAlphaBlend = NULL;
2319 if ( !s_triedToLoad )
2320 {
2321 s_triedToLoad = TRUE;
2322
2323 // don't give errors about the DLL being unavailable, we're
2324 // prepared to handle this
2325 wxLogNull nolog;
2326
2327 wxDynamicLibrary dll(_T("msimg32.dll"));
2328 if ( dll.IsLoaded() )
2329 {
2330 pfnAlphaBlend = (AlphaBlend_t)dll.GetSymbol(_T("AlphaBlend"));
2331 if ( pfnAlphaBlend )
2332 {
2333 // we must keep the DLL loaded if we want to be able to
2334 // call AlphaBlend() so just never unload it at all, not a
2335 // big deal
2336 dll.Detach();
2337 }
2338 }
2339 }
2340
2341 if ( pfnAlphaBlend )
2342 {
2343 BLENDFUNCTION bf;
2344 bf.BlendOp = AC_SRC_OVER;
2345 bf.BlendFlags = 0;
2346 bf.SourceConstantAlpha = 0xff;
2347 bf.AlphaFormat = AC_SRC_ALPHA;
2348
2349 if ( pfnAlphaBlend(hdcDst, x, y, width, height,
2350 hdcSrc, 0, 0, width, height,
2351 bf) )
2352 {
2353 // skip wxAlphaBlend() call below
2354 return TRUE;
2355 }
2356
2357 wxLogLastError(_T("AlphaBlend"));
2358 }
2359 #endif // defined(AC_SRC_OVER)
2360
2361 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2362 // implementation
2363 #ifdef wxHAVE_RAW_BITMAP
2364 wxAlphaBlend(hdcDst, x, y, width, height, bmp);
2365
2366 return TRUE;
2367 #else // !wxHAVE_RAW_BITMAP
2368 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2369 // alpha but at least something will be shown like this)
2370 return FALSE;
2371 #endif // wxHAVE_RAW_BITMAP
2372 }
2373
2374
2375 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2376 #ifdef wxHAVE_RAW_BITMAP
2377
2378 static void
2379 wxAlphaBlend(HDC hdcDst, int xDst, int yDst, int w, int h, const wxBitmap& bmpSrc)
2380 {
2381 // get the destination DC pixels
2382 wxBitmap bmpDst(w, h, 32 /* force creating RGBA DIB */);
2383 MemoryHDC hdcMem;
2384 SelectInHDC select(hdcMem, GetHbitmapOf(bmpDst));
2385
2386 if ( !::BitBlt(hdcMem, 0, 0, w, h, hdcDst, 0, 0, SRCCOPY) )
2387 {
2388 wxLogLastError(_T("BitBlt"));
2389 }
2390
2391 // combine them with the source bitmap using alpha
2392 wxAlphaPixelData dataDst(bmpDst),
2393 dataSrc((wxBitmap &)bmpSrc);
2394
2395 wxCHECK_RET( dataDst && dataSrc,
2396 _T("failed to get raw data in wxAlphaBlend") );
2397
2398 wxAlphaPixelData::Iterator pDst(dataDst),
2399 pSrc(dataSrc);
2400
2401 for ( int y = 0; y < h; y++ )
2402 {
2403 wxAlphaPixelData::Iterator pDstRowStart = pDst,
2404 pSrcRowStart = pSrc;
2405
2406 for ( int x = 0; x < w; x++ )
2407 {
2408 // note that source bitmap uses premultiplied alpha (as required by
2409 // the real AlphaBlend)
2410 const unsigned beta = 255 - pSrc.Alpha();
2411
2412 pDst.Red() = pSrc.Red() + (beta * pDst.Red() + 127) / 255;
2413 pDst.Blue() = pSrc.Blue() + (beta * pDst.Blue() + 127) / 255;
2414 pDst.Green() = pSrc.Green() + (beta * pDst.Green() + 127) / 255;
2415
2416 ++pDst;
2417 ++pSrc;
2418 }
2419
2420 pDst = pDstRowStart;
2421 pSrc = pSrcRowStart;
2422 pDst.OffsetY(dataDst, 1);
2423 pSrc.OffsetY(dataSrc, 1);
2424 }
2425
2426 // and finally blit them back to the destination DC
2427 if ( !::BitBlt(hdcDst, xDst, yDst, w, h, hdcMem, 0, 0, SRCCOPY) )
2428 {
2429 wxLogLastError(_T("BitBlt"));
2430 }
2431 }
2432
2433 #endif // #ifdef wxHAVE_RAW_BITMAP