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