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