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