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