]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dc.cpp
Make sure text part of combobox is enabled/disabled too
[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
1678 if (!::GetTextExtentExPoint(GetHdc(),
1679 text.c_str(), // string to check
1680 wxMin(stlen, maxLenText),
1681 maxWidth,
1682 &fit, // [out] count of chars
1683 // that will fit
1684 &widths[0], // array to fill
1685 &sz))
1686 {
1687 // API failed
1688 wxLogLastError(wxT("GetTextExtentExPoint"));
1689 return false;
1690 }
1691
1692 return true;
1693 }
1694
1695
1696
1697
1698 void wxDC::SetMapMode(int mode)
1699 {
1700 WXMICROWIN_CHECK_HDC
1701
1702 m_mappingMode = mode;
1703
1704 if ( mode == wxMM_TEXT )
1705 {
1706 m_logicalScaleX =
1707 m_logicalScaleY = 1.0;
1708 }
1709 else // need to do some calculations
1710 {
1711 int pixel_width = ::GetDeviceCaps(GetHdc(), HORZRES),
1712 pixel_height = ::GetDeviceCaps(GetHdc(), VERTRES),
1713 mm_width = ::GetDeviceCaps(GetHdc(), HORZSIZE),
1714 mm_height = ::GetDeviceCaps(GetHdc(), VERTSIZE);
1715
1716 if ( (mm_width == 0) || (mm_height == 0) )
1717 {
1718 // we can't calculate mm2pixels[XY] then!
1719 return;
1720 }
1721
1722 double mm2pixelsX = (double)pixel_width / mm_width,
1723 mm2pixelsY = (double)pixel_height / mm_height;
1724
1725 switch (mode)
1726 {
1727 case wxMM_TWIPS:
1728 m_logicalScaleX = twips2mm * mm2pixelsX;
1729 m_logicalScaleY = twips2mm * mm2pixelsY;
1730 break;
1731
1732 case wxMM_POINTS:
1733 m_logicalScaleX = pt2mm * mm2pixelsX;
1734 m_logicalScaleY = pt2mm * mm2pixelsY;
1735 break;
1736
1737 case wxMM_METRIC:
1738 m_logicalScaleX = mm2pixelsX;
1739 m_logicalScaleY = mm2pixelsY;
1740 break;
1741
1742 case wxMM_LOMETRIC:
1743 m_logicalScaleX = mm2pixelsX / 10.0;
1744 m_logicalScaleY = mm2pixelsY / 10.0;
1745 break;
1746
1747 default:
1748 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1749 }
1750 }
1751
1752 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1753 // cases we could do with MM_TEXT and in the remaining 0.9% with
1754 // MM_ISOTROPIC (TODO!)
1755 #ifndef __WXWINCE__
1756 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
1757
1758 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
1759 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
1760
1761 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
1762 ::SetWindowExtEx(GetHdc(), width, height, NULL);
1763
1764 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX, m_deviceOriginY, NULL);
1765 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX, m_logicalOriginY, NULL);
1766 #endif
1767 }
1768
1769 void wxDC::SetUserScale(double x, double y)
1770 {
1771 WXMICROWIN_CHECK_HDC
1772
1773 if ( x == m_userScaleX && y == m_userScaleY )
1774 return;
1775
1776 m_userScaleX = x;
1777 m_userScaleY = y;
1778
1779 this->SetMapMode(m_mappingMode);
1780 }
1781
1782 void wxDC::SetAxisOrientation(bool WXUNUSED_IN_WINCE(xLeftRight),
1783 bool WXUNUSED_IN_WINCE(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 #endif
1799 }
1800
1801 void wxDC::SetSystemScale(double x, double y)
1802 {
1803 WXMICROWIN_CHECK_HDC
1804
1805 if ( x == m_scaleX && y == m_scaleY )
1806 return;
1807
1808 m_scaleX = x;
1809 m_scaleY = y;
1810
1811 #ifndef __WXWINCE__
1812 SetMapMode(m_mappingMode);
1813 #endif
1814 }
1815
1816 void wxDC::SetLogicalOrigin(wxCoord x, wxCoord y)
1817 {
1818 WXMICROWIN_CHECK_HDC
1819
1820 if ( x == m_logicalOriginX && y == m_logicalOriginY )
1821 return;
1822
1823 m_logicalOriginX = x;
1824 m_logicalOriginY = y;
1825
1826 #ifndef __WXWINCE__
1827 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
1828 #endif
1829 }
1830
1831 void wxDC::SetDeviceOrigin(wxCoord x, wxCoord y)
1832 {
1833 WXMICROWIN_CHECK_HDC
1834
1835 if ( x == m_deviceOriginX && y == m_deviceOriginY )
1836 return;
1837
1838 m_deviceOriginX = x;
1839 m_deviceOriginY = y;
1840
1841 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
1842 }
1843
1844 // ---------------------------------------------------------------------------
1845 // coordinates transformations
1846 // ---------------------------------------------------------------------------
1847
1848 wxCoord wxDCBase::DeviceToLogicalX(wxCoord x) const
1849 {
1850 return DeviceToLogicalXRel(x - m_deviceOriginX)*m_signX + m_logicalOriginX;
1851 }
1852
1853 wxCoord wxDCBase::DeviceToLogicalXRel(wxCoord x) const
1854 {
1855 // axis orientation is not taken into account for conversion of a distance
1856 return (wxCoord)(x / (m_logicalScaleX*m_userScaleX*m_scaleX));
1857 }
1858
1859 wxCoord wxDCBase::DeviceToLogicalY(wxCoord y) const
1860 {
1861 return DeviceToLogicalYRel(y - m_deviceOriginY)*m_signY + m_logicalOriginY;
1862 }
1863
1864 wxCoord wxDCBase::DeviceToLogicalYRel(wxCoord y) const
1865 {
1866 // axis orientation is not taken into account for conversion of a distance
1867 return (wxCoord)( y / (m_logicalScaleY*m_userScaleY*m_scaleY));
1868 }
1869
1870 wxCoord wxDCBase::LogicalToDeviceX(wxCoord x) const
1871 {
1872 return LogicalToDeviceXRel(x - m_logicalOriginX)*m_signX + m_deviceOriginX;
1873 }
1874
1875 wxCoord wxDCBase::LogicalToDeviceXRel(wxCoord x) const
1876 {
1877 // axis orientation is not taken into account for conversion of a distance
1878 return (wxCoord) (x*m_logicalScaleX*m_userScaleX*m_scaleX);
1879 }
1880
1881 wxCoord wxDCBase::LogicalToDeviceY(wxCoord y) const
1882 {
1883 return LogicalToDeviceYRel(y - m_logicalOriginY)*m_signY + m_deviceOriginY;
1884 }
1885
1886 wxCoord wxDCBase::LogicalToDeviceYRel(wxCoord y) const
1887 {
1888 // axis orientation is not taken into account for conversion of a distance
1889 return (wxCoord) (y*m_logicalScaleY*m_userScaleY*m_scaleY);
1890 }
1891
1892 // ---------------------------------------------------------------------------
1893 // bit blit
1894 // ---------------------------------------------------------------------------
1895
1896 bool wxDC::DoBlit(wxCoord xdest, wxCoord ydest,
1897 wxCoord width, wxCoord height,
1898 wxDC *source, wxCoord xsrc, wxCoord ysrc,
1899 int rop, bool useMask,
1900 wxCoord xsrcMask, wxCoord ysrcMask)
1901 {
1902 wxCHECK_MSG( source, false, _T("wxDC::Blit(): NULL wxDC pointer") );
1903
1904 WXMICROWIN_CHECK_HDC_RET(false)
1905
1906 // if either the source or destination has alpha channel, we must use
1907 // AlphaBlt() as other function don't handle it correctly
1908 const wxBitmap& bmpSrc = source->m_selectedBitmap;
1909 if ( bmpSrc.Ok() && (bmpSrc.HasAlpha() ||
1910 (m_selectedBitmap.Ok() && m_selectedBitmap.HasAlpha())) )
1911 {
1912 if ( AlphaBlt(GetHdc(), xdest, ydest, width, height,
1913 xsrc, ysrc, GetHdcOf(*source), bmpSrc) )
1914 return true;
1915 }
1916
1917 wxMask *mask = NULL;
1918 if ( useMask )
1919 {
1920 mask = bmpSrc.GetMask();
1921
1922 if ( !(bmpSrc.Ok() && mask && mask->GetMaskBitmap()) )
1923 {
1924 // don't give assert here because this would break existing
1925 // programs - just silently ignore useMask parameter
1926 useMask = false;
1927 }
1928 }
1929
1930 if (xsrcMask == -1 && ysrcMask == -1)
1931 {
1932 xsrcMask = xsrc; ysrcMask = ysrc;
1933 }
1934
1935 COLORREF old_textground = ::GetTextColor(GetHdc());
1936 COLORREF old_background = ::GetBkColor(GetHdc());
1937 if (m_textForegroundColour.Ok())
1938 {
1939 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1940 }
1941 if (m_textBackgroundColour.Ok())
1942 {
1943 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1944 }
1945
1946 DWORD dwRop;
1947 switch (rop)
1948 {
1949 case wxXOR: dwRop = SRCINVERT; break;
1950 case wxINVERT: dwRop = DSTINVERT; break;
1951 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
1952 case wxAND_REVERSE: dwRop = SRCERASE; break;
1953 case wxCLEAR: dwRop = BLACKNESS; break;
1954 case wxSET: dwRop = WHITENESS; break;
1955 case wxOR_INVERT: dwRop = MERGEPAINT; break;
1956 case wxAND: dwRop = SRCAND; break;
1957 case wxOR: dwRop = SRCPAINT; break;
1958 case wxEQUIV: dwRop = 0x00990066; break;
1959 case wxNAND: dwRop = 0x007700E6; break;
1960 case wxAND_INVERT: dwRop = 0x00220326; break;
1961 case wxCOPY: dwRop = SRCCOPY; break;
1962 case wxNO_OP: dwRop = DSTCOPY; break;
1963 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
1964 case wxNOR: dwRop = NOTSRCCOPY; break;
1965 default:
1966 wxFAIL_MSG( wxT("unsupported logical function") );
1967 return false;
1968 }
1969
1970 bool success = false;
1971
1972 if (useMask)
1973 {
1974 #ifdef __WIN32__
1975 // we want the part of the image corresponding to the mask to be
1976 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1977 // meaning of fg and bg is inverted which corresponds to wxWin notion
1978 // of the mask which is also contrary to the Windows one)
1979
1980 // On some systems, MaskBlt succeeds yet is much much slower
1981 // than the wxWidgets fall-back implementation. So we need
1982 // to be able to switch this on and off at runtime.
1983 #if wxUSE_SYSTEM_OPTIONS
1984 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1985 #endif
1986 {
1987 success = ::MaskBlt
1988 (
1989 GetHdc(),
1990 xdest, ydest, width, height,
1991 GetHdcOf(*source),
1992 xsrc, ysrc,
1993 (HBITMAP)mask->GetMaskBitmap(),
1994 xsrcMask, ysrcMask,
1995 MAKEROP4(dwRop, DSTCOPY)
1996 ) != 0;
1997 }
1998
1999 if ( !success )
2000 #endif // Win32
2001 {
2002 // Blit bitmap with mask
2003 HDC dc_mask ;
2004 HDC dc_buffer ;
2005 HBITMAP buffer_bmap ;
2006
2007 #if wxUSE_DC_CACHEING
2008 // create a temp buffer bitmap and DCs to access it and the mask
2009 wxDCCacheEntry* dcCacheEntry1 = FindDCInCache(NULL, source->GetHDC());
2010 dc_mask = (HDC) dcCacheEntry1->m_dc;
2011
2012 wxDCCacheEntry* dcCacheEntry2 = FindDCInCache(dcCacheEntry1, GetHDC());
2013 dc_buffer = (HDC) dcCacheEntry2->m_dc;
2014
2015 wxDCCacheEntry* bitmapCacheEntry = FindBitmapInCache(GetHDC(),
2016 width, height);
2017
2018 buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap;
2019 #else // !wxUSE_DC_CACHEING
2020 // create a temp buffer bitmap and DCs to access it and the mask
2021 dc_mask = ::CreateCompatibleDC(GetHdcOf(*source));
2022 dc_buffer = ::CreateCompatibleDC(GetHdc());
2023 buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), width, height);
2024 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2025 HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
2026 HGDIOBJ hOldBufferBitmap = ::SelectObject(dc_buffer, buffer_bmap);
2027
2028 // copy dest to buffer
2029 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
2030 GetHdc(), xdest, ydest, SRCCOPY) )
2031 {
2032 wxLogLastError(wxT("BitBlt"));
2033 }
2034
2035 // copy src to buffer using selected raster op
2036 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
2037 GetHdcOf(*source), xsrc, ysrc, dwRop) )
2038 {
2039 wxLogLastError(wxT("BitBlt"));
2040 }
2041
2042 // set masked area in buffer to BLACK (pixel value 0)
2043 COLORREF prevBkCol = ::SetBkColor(GetHdc(), RGB(255, 255, 255));
2044 COLORREF prevCol = ::SetTextColor(GetHdc(), RGB(0, 0, 0));
2045 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
2046 dc_mask, xsrcMask, ysrcMask, SRCAND) )
2047 {
2048 wxLogLastError(wxT("BitBlt"));
2049 }
2050
2051 // set unmasked area in dest to BLACK
2052 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2053 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2054 if ( !::BitBlt(GetHdc(), xdest, ydest, (int)width, (int)height,
2055 dc_mask, xsrcMask, ysrcMask, SRCAND) )
2056 {
2057 wxLogLastError(wxT("BitBlt"));
2058 }
2059 ::SetBkColor(GetHdc(), prevBkCol); // restore colours to original values
2060 ::SetTextColor(GetHdc(), prevCol);
2061
2062 // OR buffer to dest
2063 success = ::BitBlt(GetHdc(), xdest, ydest,
2064 (int)width, (int)height,
2065 dc_buffer, 0, 0, SRCPAINT) != 0;
2066 if ( !success )
2067 {
2068 wxLogLastError(wxT("BitBlt"));
2069 }
2070
2071 // tidy up temporary DCs and bitmap
2072 ::SelectObject(dc_mask, hOldMaskBitmap);
2073 ::SelectObject(dc_buffer, hOldBufferBitmap);
2074
2075 #if !wxUSE_DC_CACHEING
2076 {
2077 ::DeleteDC(dc_mask);
2078 ::DeleteDC(dc_buffer);
2079 ::DeleteObject(buffer_bmap);
2080 }
2081 #endif
2082 }
2083 }
2084 else // no mask, just BitBlt() it
2085 {
2086 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2087 // use StretchBlt() if available and finally fall back to BitBlt()
2088
2089 // FIXME: use appropriate WinCE functions
2090 #ifndef __WXWINCE__
2091 const int caps = ::GetDeviceCaps(GetHdc(), RASTERCAPS);
2092 if ( bmpSrc.Ok() && (caps & RC_STRETCHDIB) )
2093 {
2094 DIBSECTION ds;
2095 wxZeroMemory(ds);
2096
2097 if ( ::GetObject(GetHbitmapOf(bmpSrc),
2098 sizeof(ds),
2099 &ds) == sizeof(ds) )
2100 {
2101 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2102
2103 // Figure out what co-ordinate system we're supposed to specify
2104 // ysrc in.
2105 const LONG hDIB = ds.dsBmih.biHeight;
2106 if ( hDIB > 0 )
2107 {
2108 // reflect ysrc
2109 ysrc = hDIB - (ysrc + height);
2110 }
2111
2112 if ( ::StretchDIBits(GetHdc(),
2113 xdest, ydest,
2114 width, height,
2115 xsrc, ysrc,
2116 width, height,
2117 ds.dsBm.bmBits,
2118 (LPBITMAPINFO)&ds.dsBmih,
2119 DIB_RGB_COLORS,
2120 SRCCOPY
2121 ) == (int)GDI_ERROR )
2122 {
2123 // On Win9x this API fails most (all?) of the time, so
2124 // logging it becomes quite distracting. Since it falls
2125 // back to the code below this is not really serious, so
2126 // don't log it.
2127 //wxLogLastError(wxT("StretchDIBits"));
2128 }
2129 else
2130 {
2131 success = true;
2132 }
2133 }
2134 }
2135
2136 if ( !success && (caps & RC_STRETCHBLT) )
2137 #endif
2138 // __WXWINCE__
2139 {
2140 #ifndef __WXWINCE__
2141 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2142 #endif
2143
2144 if ( !::StretchBlt
2145 (
2146 GetHdc(),
2147 xdest, ydest, width, height,
2148 GetHdcOf(*source),
2149 xsrc, ysrc, width, height,
2150 dwRop
2151 ) )
2152 {
2153 wxLogLastError(_T("StretchBlt"));
2154 }
2155 else
2156 {
2157 success = true;
2158 }
2159 }
2160
2161 if ( !success )
2162 {
2163 if ( !::BitBlt
2164 (
2165 GetHdc(),
2166 xdest, ydest,
2167 (int)width, (int)height,
2168 GetHdcOf(*source),
2169 xsrc, ysrc,
2170 dwRop
2171 ) )
2172 {
2173 wxLogLastError(_T("BitBlt"));
2174 }
2175 else
2176 {
2177 success = true;
2178 }
2179 }
2180 }
2181
2182 ::SetTextColor(GetHdc(), old_textground);
2183 ::SetBkColor(GetHdc(), old_background);
2184
2185 return success;
2186 }
2187
2188 void wxDC::DoGetSize(int *w, int *h) const
2189 {
2190 WXMICROWIN_CHECK_HDC
2191
2192 if ( w ) *w = ::GetDeviceCaps(GetHdc(), HORZRES);
2193 if ( h ) *h = ::GetDeviceCaps(GetHdc(), VERTRES);
2194 }
2195
2196 void wxDC::DoGetSizeMM(int *w, int *h) const
2197 {
2198 WXMICROWIN_CHECK_HDC
2199
2200 // if we implement it in terms of DoGetSize() instead of directly using the
2201 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2202 // will also work for wxWindowDC and wxClientDC even though their size is
2203 // not the same as the total size of the screen
2204 int wPixels, hPixels;
2205 DoGetSize(&wPixels, &hPixels);
2206
2207 if ( w )
2208 {
2209 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
2210
2211 wxCHECK_RET( wTotal, _T("0 width device?") );
2212
2213 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2214 }
2215
2216 if ( h )
2217 {
2218 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2219
2220 wxCHECK_RET( hTotal, _T("0 height device?") );
2221
2222 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2223 }
2224 }
2225
2226 wxSize wxDC::GetPPI() const
2227 {
2228 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2229
2230 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2231 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2232
2233 return wxSize(x, y);
2234 }
2235
2236 // For use by wxWidgets only, unless custom units are required.
2237 void wxDC::SetLogicalScale(double x, double y)
2238 {
2239 WXMICROWIN_CHECK_HDC
2240
2241 m_logicalScaleX = x;
2242 m_logicalScaleY = y;
2243 }
2244
2245 // ----------------------------------------------------------------------------
2246 // DC caching
2247 // ----------------------------------------------------------------------------
2248
2249 #if wxUSE_DC_CACHEING
2250
2251 /*
2252 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2253 * improve it in due course, either using arrays, or simply storing pointers to one
2254 * entry for the bitmap, and two for the DCs. -- JACS
2255 */
2256
2257 wxList wxDC::sm_bitmapCache;
2258 wxList wxDC::sm_dcCache;
2259
2260 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2261 {
2262 m_bitmap = hBitmap;
2263 m_dc = 0;
2264 m_width = w;
2265 m_height = h;
2266 m_depth = depth;
2267 }
2268
2269 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2270 {
2271 m_bitmap = 0;
2272 m_dc = hDC;
2273 m_width = 0;
2274 m_height = 0;
2275 m_depth = depth;
2276 }
2277
2278 wxDCCacheEntry::~wxDCCacheEntry()
2279 {
2280 if (m_bitmap)
2281 ::DeleteObject((HBITMAP) m_bitmap);
2282 if (m_dc)
2283 ::DeleteDC((HDC) m_dc);
2284 }
2285
2286 wxDCCacheEntry* wxDC::FindBitmapInCache(WXHDC dc, int w, int h)
2287 {
2288 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2289 wxList::compatibility_iterator node = sm_bitmapCache.GetFirst();
2290 while (node)
2291 {
2292 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2293
2294 if (entry->m_depth == depth)
2295 {
2296 if (entry->m_width < w || entry->m_height < h)
2297 {
2298 ::DeleteObject((HBITMAP) entry->m_bitmap);
2299 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2300 if ( !entry->m_bitmap)
2301 {
2302 wxLogLastError(wxT("CreateCompatibleBitmap"));
2303 }
2304 entry->m_width = w; entry->m_height = h;
2305 return entry;
2306 }
2307 return entry;
2308 }
2309
2310 node = node->GetNext();
2311 }
2312 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2313 if ( !hBitmap)
2314 {
2315 wxLogLastError(wxT("CreateCompatibleBitmap"));
2316 }
2317 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2318 AddToBitmapCache(entry);
2319 return entry;
2320 }
2321
2322 wxDCCacheEntry* wxDC::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
2323 {
2324 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2325 wxList::compatibility_iterator node = sm_dcCache.GetFirst();
2326 while (node)
2327 {
2328 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2329
2330 // Don't return the same one as we already have
2331 if (!notThis || (notThis != entry))
2332 {
2333 if (entry->m_depth == depth)
2334 {
2335 return entry;
2336 }
2337 }
2338
2339 node = node->GetNext();
2340 }
2341 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2342 if ( !hDC)
2343 {
2344 wxLogLastError(wxT("CreateCompatibleDC"));
2345 }
2346 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2347 AddToDCCache(entry);
2348 return entry;
2349 }
2350
2351 void wxDC::AddToBitmapCache(wxDCCacheEntry* entry)
2352 {
2353 sm_bitmapCache.Append(entry);
2354 }
2355
2356 void wxDC::AddToDCCache(wxDCCacheEntry* entry)
2357 {
2358 sm_dcCache.Append(entry);
2359 }
2360
2361 void wxDC::ClearCache()
2362 {
2363 WX_CLEAR_LIST(wxList, sm_dcCache);
2364 WX_CLEAR_LIST(wxList, sm_bitmapCache);
2365 }
2366
2367 // Clean up cache at app exit
2368 class wxDCModule : public wxModule
2369 {
2370 public:
2371 virtual bool OnInit() { return true; }
2372 virtual void OnExit() { wxDC::ClearCache(); }
2373
2374 private:
2375 DECLARE_DYNAMIC_CLASS(wxDCModule)
2376 };
2377
2378 IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2379
2380 #endif // wxUSE_DC_CACHEING
2381
2382 // ----------------------------------------------------------------------------
2383 // alpha channel support
2384 // ----------------------------------------------------------------------------
2385
2386 static bool AlphaBlt(HDC hdcDst,
2387 int x, int y, int width, int height,
2388 int srcX, int srcY, HDC hdcSrc,
2389 const wxBitmap& bmp)
2390 {
2391 wxASSERT_MSG( bmp.Ok() && bmp.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2392 wxASSERT_MSG( hdcDst && hdcSrc, _T("AlphaBlt(): invalid HDC") );
2393
2394 // do we have AlphaBlend() and company in the headers?
2395 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2396 // yes, now try to see if we have it during run-time
2397 typedef BOOL (WINAPI *AlphaBlend_t)(HDC,int,int,int,int,
2398 HDC,int,int,int,int,
2399 BLENDFUNCTION);
2400
2401 // bitmaps can be drawn only from GUI thread so there is no need to
2402 // protect this static variable from multiple threads
2403 static bool s_triedToLoad = false;
2404 static AlphaBlend_t pfnAlphaBlend = NULL;
2405 if ( !s_triedToLoad )
2406 {
2407 s_triedToLoad = true;
2408
2409 // don't give errors about the DLL being unavailable, we're
2410 // prepared to handle this
2411 wxLogNull nolog;
2412
2413 wxDynamicLibrary dll(_T("msimg32.dll"));
2414 if ( dll.IsLoaded() )
2415 {
2416 pfnAlphaBlend = (AlphaBlend_t)dll.GetSymbol(_T("AlphaBlend"));
2417 if ( pfnAlphaBlend )
2418 {
2419 // we must keep the DLL loaded if we want to be able to
2420 // call AlphaBlend() so just never unload it at all, not a
2421 // big deal
2422 dll.Detach();
2423 }
2424 }
2425 }
2426
2427 if ( pfnAlphaBlend )
2428 {
2429 BLENDFUNCTION bf;
2430 bf.BlendOp = AC_SRC_OVER;
2431 bf.BlendFlags = 0;
2432 bf.SourceConstantAlpha = 0xff;
2433 bf.AlphaFormat = AC_SRC_ALPHA;
2434
2435 if ( pfnAlphaBlend(hdcDst, x, y, width, height,
2436 hdcSrc, srcX, srcY, width, height,
2437 bf) )
2438 {
2439 // skip wxAlphaBlend() call below
2440 return true;
2441 }
2442
2443 wxLogLastError(_T("AlphaBlend"));
2444 }
2445 #endif // defined(AC_SRC_OVER)
2446
2447 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2448 // implementation
2449 #ifdef wxHAVE_RAW_BITMAP
2450 wxAlphaBlend(hdcDst, x, y, width, height, srcX, srcY, bmp);
2451
2452 return true;
2453 #else // !wxHAVE_RAW_BITMAP
2454 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2455 // alpha but at least something will be shown like this)
2456 wxUnusedVar(bmp);
2457 return false;
2458 #endif // wxHAVE_RAW_BITMAP
2459 }
2460
2461
2462 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2463 #ifdef wxHAVE_RAW_BITMAP
2464
2465 static void
2466 wxAlphaBlend(HDC hdcDst, int xDst, int yDst, int w, int h, int srcX, int srcY, const wxBitmap& bmpSrc)
2467 {
2468 // get the destination DC pixels
2469 wxBitmap bmpDst(w, h, 32 /* force creating RGBA DIB */);
2470 MemoryHDC hdcMem;
2471 SelectInHDC select(hdcMem, GetHbitmapOf(bmpDst));
2472
2473 if ( !::BitBlt(hdcMem, 0, 0, w, h, hdcDst, 0, 0, SRCCOPY) )
2474 {
2475 wxLogLastError(_T("BitBlt"));
2476 }
2477
2478 // combine them with the source bitmap using alpha
2479 wxAlphaPixelData dataDst(bmpDst),
2480 dataSrc((wxBitmap &)bmpSrc);
2481
2482 wxCHECK_RET( dataDst && dataSrc,
2483 _T("failed to get raw data in wxAlphaBlend") );
2484
2485 wxAlphaPixelData::Iterator pDst(dataDst),
2486 pSrc(dataSrc);
2487
2488 pSrc.Offset(dataSrc, srcX, srcY);
2489
2490 for ( int y = 0; y < h; y++ )
2491 {
2492 wxAlphaPixelData::Iterator pDstRowStart = pDst,
2493 pSrcRowStart = pSrc;
2494
2495 for ( int x = 0; x < w; x++ )
2496 {
2497 // note that source bitmap uses premultiplied alpha (as required by
2498 // the real AlphaBlend)
2499 const unsigned beta = 255 - pSrc.Alpha();
2500
2501 pDst.Red() = pSrc.Red() + (beta * pDst.Red() + 127) / 255;
2502 pDst.Blue() = pSrc.Blue() + (beta * pDst.Blue() + 127) / 255;
2503 pDst.Green() = pSrc.Green() + (beta * pDst.Green() + 127) / 255;
2504
2505 ++pDst;
2506 ++pSrc;
2507 }
2508
2509 pDst = pDstRowStart;
2510 pSrc = pSrcRowStart;
2511 pDst.OffsetY(dataDst, 1);
2512 pSrc.OffsetY(dataSrc, 1);
2513 }
2514
2515 // and finally blit them back to the destination DC
2516 if ( !::BitBlt(hdcDst, xDst, yDst, w, h, hdcMem, 0, 0, SRCCOPY) )
2517 {
2518 wxLogLastError(_T("BitBlt"));
2519 }
2520 }
2521
2522 #endif // #ifdef wxHAVE_RAW_BITMAP