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