]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dc.cpp
Fix for the undocumented holes in spiner sizes.
[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 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
557 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
558
559 #ifndef __WXWINCE__
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 const wxBitmap& bmpSrc = source->m_selectedBitmap;
1915 if ( bmpSrc.Ok() && bmpSrc.HasAlpha() )
1916 {
1917 if ( AlphaBlt(GetHdc(), xdest, ydest, width, height,
1918 GetHdcOf(*source), bmpSrc) )
1919 return true;
1920 }
1921
1922 wxMask *mask = NULL;
1923 if ( useMask )
1924 {
1925 mask = bmpSrc.GetMask();
1926
1927 if ( !(bmpSrc.Ok() && mask && mask->GetMaskBitmap()) )
1928 {
1929 // don't give assert here because this would break existing
1930 // programs - just silently ignore useMask parameter
1931 useMask = false;
1932 }
1933 }
1934
1935 if (xsrcMask == -1 && ysrcMask == -1)
1936 {
1937 xsrcMask = xsrc; ysrcMask = ysrc;
1938 }
1939
1940 COLORREF old_textground = ::GetTextColor(GetHdc());
1941 COLORREF old_background = ::GetBkColor(GetHdc());
1942 if (m_textForegroundColour.Ok())
1943 {
1944 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1945 }
1946 if (m_textBackgroundColour.Ok())
1947 {
1948 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1949 }
1950
1951 DWORD dwRop;
1952 switch (rop)
1953 {
1954 case wxXOR: dwRop = SRCINVERT; break;
1955 case wxINVERT: dwRop = DSTINVERT; break;
1956 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
1957 case wxAND_REVERSE: dwRop = SRCERASE; break;
1958 case wxCLEAR: dwRop = BLACKNESS; break;
1959 case wxSET: dwRop = WHITENESS; break;
1960 case wxOR_INVERT: dwRop = MERGEPAINT; break;
1961 case wxAND: dwRop = SRCAND; break;
1962 case wxOR: dwRop = SRCPAINT; break;
1963 case wxEQUIV: dwRop = 0x00990066; break;
1964 case wxNAND: dwRop = 0x007700E6; break;
1965 case wxAND_INVERT: dwRop = 0x00220326; break;
1966 case wxCOPY: dwRop = SRCCOPY; break;
1967 case wxNO_OP: dwRop = DSTCOPY; break;
1968 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
1969 case wxNOR: dwRop = NOTSRCCOPY; break;
1970 default:
1971 wxFAIL_MSG( wxT("unsupported logical function") );
1972 return false;
1973 }
1974
1975 bool success = false;
1976
1977 if (useMask)
1978 {
1979 #ifdef __WIN32__
1980 // we want the part of the image corresponding to the mask to be
1981 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1982 // meaning of fg and bg is inverted which corresponds to wxWin notion
1983 // of the mask which is also contrary to the Windows one)
1984
1985 // On some systems, MaskBlt succeeds yet is much much slower
1986 // than the wxWidgets fall-back implementation. So we need
1987 // to be able to switch this on and off at runtime.
1988 #if wxUSE_SYSTEM_OPTIONS
1989 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1990 #endif
1991 {
1992 success = ::MaskBlt
1993 (
1994 GetHdc(),
1995 xdest, ydest, width, height,
1996 GetHdcOf(*source),
1997 xsrc, ysrc,
1998 (HBITMAP)mask->GetMaskBitmap(),
1999 xsrcMask, ysrcMask,
2000 MAKEROP4(dwRop, DSTCOPY)
2001 ) != 0;
2002 }
2003
2004 if ( !success )
2005 #endif // Win32
2006 {
2007 // Blit bitmap with mask
2008 HDC dc_mask ;
2009 HDC dc_buffer ;
2010 HBITMAP buffer_bmap ;
2011
2012 #if wxUSE_DC_CACHEING
2013 // create a temp buffer bitmap and DCs to access it and the mask
2014 wxDCCacheEntry* dcCacheEntry1 = FindDCInCache(NULL, source->GetHDC());
2015 dc_mask = (HDC) dcCacheEntry1->m_dc;
2016
2017 wxDCCacheEntry* dcCacheEntry2 = FindDCInCache(dcCacheEntry1, GetHDC());
2018 dc_buffer = (HDC) dcCacheEntry2->m_dc;
2019
2020 wxDCCacheEntry* bitmapCacheEntry = FindBitmapInCache(GetHDC(),
2021 width, height);
2022
2023 buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap;
2024 #else // !wxUSE_DC_CACHEING
2025 // create a temp buffer bitmap and DCs to access it and the mask
2026 dc_mask = ::CreateCompatibleDC(GetHdcOf(*source));
2027 dc_buffer = ::CreateCompatibleDC(GetHdc());
2028 buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), width, height);
2029 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2030 HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
2031 HGDIOBJ hOldBufferBitmap = ::SelectObject(dc_buffer, buffer_bmap);
2032
2033 // copy dest to buffer
2034 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
2035 GetHdc(), xdest, ydest, SRCCOPY) )
2036 {
2037 wxLogLastError(wxT("BitBlt"));
2038 }
2039
2040 // copy src to buffer using selected raster op
2041 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
2042 GetHdcOf(*source), xsrc, ysrc, dwRop) )
2043 {
2044 wxLogLastError(wxT("BitBlt"));
2045 }
2046
2047 // set masked area in buffer to BLACK (pixel value 0)
2048 COLORREF prevBkCol = ::SetBkColor(GetHdc(), RGB(255, 255, 255));
2049 COLORREF prevCol = ::SetTextColor(GetHdc(), RGB(0, 0, 0));
2050 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
2051 dc_mask, xsrcMask, ysrcMask, SRCAND) )
2052 {
2053 wxLogLastError(wxT("BitBlt"));
2054 }
2055
2056 // set unmasked area in dest to BLACK
2057 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2058 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2059 if ( !::BitBlt(GetHdc(), xdest, ydest, (int)width, (int)height,
2060 dc_mask, xsrcMask, ysrcMask, SRCAND) )
2061 {
2062 wxLogLastError(wxT("BitBlt"));
2063 }
2064 ::SetBkColor(GetHdc(), prevBkCol); // restore colours to original values
2065 ::SetTextColor(GetHdc(), prevCol);
2066
2067 // OR buffer to dest
2068 success = ::BitBlt(GetHdc(), xdest, ydest,
2069 (int)width, (int)height,
2070 dc_buffer, 0, 0, SRCPAINT) != 0;
2071 if ( !success )
2072 {
2073 wxLogLastError(wxT("BitBlt"));
2074 }
2075
2076 // tidy up temporary DCs and bitmap
2077 ::SelectObject(dc_mask, hOldMaskBitmap);
2078 ::SelectObject(dc_buffer, hOldBufferBitmap);
2079
2080 #if !wxUSE_DC_CACHEING
2081 {
2082 ::DeleteDC(dc_mask);
2083 ::DeleteDC(dc_buffer);
2084 ::DeleteObject(buffer_bmap);
2085 }
2086 #endif
2087 }
2088 }
2089 else // no mask, just BitBlt() it
2090 {
2091 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2092 // use StretchBlt() if available and finally fall back to BitBlt()
2093
2094 // FIXME: use appropriate WinCE functions
2095 #ifndef __WXWINCE__
2096 const int caps = ::GetDeviceCaps(GetHdc(), RASTERCAPS);
2097 if ( bmpSrc.Ok() && (caps & RC_STRETCHDIB) )
2098 {
2099 DIBSECTION ds;
2100 wxZeroMemory(ds);
2101
2102 if ( ::GetObject(GetHbitmapOf(bmpSrc),
2103 sizeof(ds),
2104 &ds) == sizeof(ds) )
2105 {
2106 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2107
2108 // Figure out what co-ordinate system we're supposed to specify
2109 // ysrc in.
2110 const LONG hDIB = ds.dsBmih.biHeight;
2111 if ( hDIB > 0 )
2112 {
2113 // reflect ysrc
2114 ysrc = hDIB - (ysrc + height);
2115 }
2116
2117 if ( ::StretchDIBits(GetHdc(),
2118 xdest, ydest,
2119 width, height,
2120 xsrc, ysrc,
2121 width, height,
2122 ds.dsBm.bmBits,
2123 (LPBITMAPINFO)&ds.dsBmih,
2124 DIB_RGB_COLORS,
2125 SRCCOPY
2126 ) == (int)GDI_ERROR )
2127 {
2128 // On Win9x this API fails most (all?) of the time, so
2129 // logging it becomes quite distracting. Since it falls
2130 // back to the code below this is not really serious, so
2131 // don't log it.
2132 //wxLogLastError(wxT("StretchDIBits"));
2133 }
2134 else
2135 {
2136 success = true;
2137 }
2138 }
2139 }
2140
2141 if ( !success && (caps & RC_STRETCHBLT) )
2142 #endif
2143 // __WXWINCE__
2144 {
2145 #ifndef __WXWINCE__
2146 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2147 #endif
2148
2149 if ( !::StretchBlt
2150 (
2151 GetHdc(),
2152 xdest, ydest, width, height,
2153 GetHdcOf(*source),
2154 xsrc, ysrc, width, height,
2155 dwRop
2156 ) )
2157 {
2158 wxLogLastError(_T("StretchBlt"));
2159 }
2160 else
2161 {
2162 success = true;
2163 }
2164 }
2165
2166 if ( !success )
2167 {
2168 if ( !::BitBlt
2169 (
2170 GetHdc(),
2171 xdest, ydest,
2172 (int)width, (int)height,
2173 GetHdcOf(*source),
2174 xsrc, ysrc,
2175 dwRop
2176 ) )
2177 {
2178 wxLogLastError(_T("BitBlt"));
2179 }
2180 else
2181 {
2182 success = true;
2183 }
2184 }
2185 }
2186
2187 ::SetTextColor(GetHdc(), old_textground);
2188 ::SetBkColor(GetHdc(), old_background);
2189
2190 return success;
2191 }
2192
2193 void wxDC::DoGetSize(int *w, int *h) const
2194 {
2195 WXMICROWIN_CHECK_HDC
2196
2197 if ( w ) *w = ::GetDeviceCaps(GetHdc(), HORZRES);
2198 if ( h ) *h = ::GetDeviceCaps(GetHdc(), VERTRES);
2199 }
2200
2201 void wxDC::DoGetSizeMM(int *w, int *h) const
2202 {
2203 WXMICROWIN_CHECK_HDC
2204
2205 // if we implement it in terms of DoGetSize() instead of directly using the
2206 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2207 // will also work for wxWindowDC and wxClientDC even though their size is
2208 // not the same as the total size of the screen
2209 int wPixels, hPixels;
2210 DoGetSize(&wPixels, &hPixels);
2211
2212 if ( w )
2213 {
2214 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
2215
2216 wxCHECK_RET( wTotal, _T("0 width device?") );
2217
2218 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2219 }
2220
2221 if ( h )
2222 {
2223 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2224
2225 wxCHECK_RET( hTotal, _T("0 height device?") );
2226
2227 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2228 }
2229 }
2230
2231 wxSize wxDC::GetPPI() const
2232 {
2233 WXMICROWIN_CHECK_HDC_RET(wxSize())
2234
2235 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2236 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2237
2238 return wxSize(x, y);
2239 }
2240
2241 // For use by wxWidgets only, unless custom units are required.
2242 void wxDC::SetLogicalScale(double x, double y)
2243 {
2244 WXMICROWIN_CHECK_HDC
2245
2246 m_logicalScaleX = x;
2247 m_logicalScaleY = y;
2248 }
2249
2250 // ----------------------------------------------------------------------------
2251 // DC caching
2252 // ----------------------------------------------------------------------------
2253
2254 #if wxUSE_DC_CACHEING
2255
2256 /*
2257 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2258 * improve it in due course, either using arrays, or simply storing pointers to one
2259 * entry for the bitmap, and two for the DCs. -- JACS
2260 */
2261
2262 wxList wxDC::sm_bitmapCache;
2263 wxList wxDC::sm_dcCache;
2264
2265 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2266 {
2267 m_bitmap = hBitmap;
2268 m_dc = 0;
2269 m_width = w;
2270 m_height = h;
2271 m_depth = depth;
2272 }
2273
2274 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2275 {
2276 m_bitmap = 0;
2277 m_dc = hDC;
2278 m_width = 0;
2279 m_height = 0;
2280 m_depth = depth;
2281 }
2282
2283 wxDCCacheEntry::~wxDCCacheEntry()
2284 {
2285 if (m_bitmap)
2286 ::DeleteObject((HBITMAP) m_bitmap);
2287 if (m_dc)
2288 ::DeleteDC((HDC) m_dc);
2289 }
2290
2291 wxDCCacheEntry* wxDC::FindBitmapInCache(WXHDC dc, int w, int h)
2292 {
2293 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2294 wxList::compatibility_iterator node = sm_bitmapCache.GetFirst();
2295 while (node)
2296 {
2297 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2298
2299 if (entry->m_depth == depth)
2300 {
2301 if (entry->m_width < w || entry->m_height < h)
2302 {
2303 ::DeleteObject((HBITMAP) entry->m_bitmap);
2304 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2305 if ( !entry->m_bitmap)
2306 {
2307 wxLogLastError(wxT("CreateCompatibleBitmap"));
2308 }
2309 entry->m_width = w; entry->m_height = h;
2310 return entry;
2311 }
2312 return entry;
2313 }
2314
2315 node = node->GetNext();
2316 }
2317 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2318 if ( !hBitmap)
2319 {
2320 wxLogLastError(wxT("CreateCompatibleBitmap"));
2321 }
2322 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2323 AddToBitmapCache(entry);
2324 return entry;
2325 }
2326
2327 wxDCCacheEntry* wxDC::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
2328 {
2329 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2330 wxList::compatibility_iterator node = sm_dcCache.GetFirst();
2331 while (node)
2332 {
2333 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2334
2335 // Don't return the same one as we already have
2336 if (!notThis || (notThis != entry))
2337 {
2338 if (entry->m_depth == depth)
2339 {
2340 return entry;
2341 }
2342 }
2343
2344 node = node->GetNext();
2345 }
2346 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2347 if ( !hDC)
2348 {
2349 wxLogLastError(wxT("CreateCompatibleDC"));
2350 }
2351 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2352 AddToDCCache(entry);
2353 return entry;
2354 }
2355
2356 void wxDC::AddToBitmapCache(wxDCCacheEntry* entry)
2357 {
2358 sm_bitmapCache.Append(entry);
2359 }
2360
2361 void wxDC::AddToDCCache(wxDCCacheEntry* entry)
2362 {
2363 sm_dcCache.Append(entry);
2364 }
2365
2366 void wxDC::ClearCache()
2367 {
2368 WX_CLEAR_LIST(wxList, sm_dcCache);
2369 WX_CLEAR_LIST(wxList, sm_bitmapCache);
2370 }
2371
2372 // Clean up cache at app exit
2373 class wxDCModule : public wxModule
2374 {
2375 public:
2376 virtual bool OnInit() { return true; }
2377 virtual void OnExit() { wxDC::ClearCache(); }
2378
2379 private:
2380 DECLARE_DYNAMIC_CLASS(wxDCModule)
2381 };
2382
2383 IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2384
2385 #endif // wxUSE_DC_CACHEING
2386
2387 // ----------------------------------------------------------------------------
2388 // alpha channel support
2389 // ----------------------------------------------------------------------------
2390
2391 static bool AlphaBlt(HDC hdcDst,
2392 int x, int y, int width, int height,
2393 HDC hdcSrc,
2394 const wxBitmap& bmp)
2395 {
2396 wxASSERT_MSG( bmp.Ok() && bmp.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2397 wxASSERT_MSG( hdcDst && hdcSrc, _T("AlphaBlt(): invalid HDC") );
2398
2399 // do we have AlphaBlend() and company in the headers?
2400 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2401 // yes, now try to see if we have it during run-time
2402 typedef BOOL (WINAPI *AlphaBlend_t)(HDC,int,int,int,int,
2403 HDC,int,int,int,int,
2404 BLENDFUNCTION);
2405
2406 // bitmaps can be drawn only from GUI thread so there is no need to
2407 // protect this static variable from multiple threads
2408 static bool s_triedToLoad = false;
2409 static AlphaBlend_t pfnAlphaBlend = NULL;
2410 if ( !s_triedToLoad )
2411 {
2412 s_triedToLoad = true;
2413
2414 // don't give errors about the DLL being unavailable, we're
2415 // prepared to handle this
2416 wxLogNull nolog;
2417
2418 wxDynamicLibrary dll(_T("msimg32.dll"));
2419 if ( dll.IsLoaded() )
2420 {
2421 pfnAlphaBlend = (AlphaBlend_t)dll.GetSymbol(_T("AlphaBlend"));
2422 if ( pfnAlphaBlend )
2423 {
2424 // we must keep the DLL loaded if we want to be able to
2425 // call AlphaBlend() so just never unload it at all, not a
2426 // big deal
2427 dll.Detach();
2428 }
2429 }
2430 }
2431
2432 if ( pfnAlphaBlend )
2433 {
2434 BLENDFUNCTION bf;
2435 bf.BlendOp = AC_SRC_OVER;
2436 bf.BlendFlags = 0;
2437 bf.SourceConstantAlpha = 0xff;
2438 bf.AlphaFormat = AC_SRC_ALPHA;
2439
2440 if ( pfnAlphaBlend(hdcDst, x, y, width, height,
2441 hdcSrc, 0, 0, width, height,
2442 bf) )
2443 {
2444 // skip wxAlphaBlend() call below
2445 return true;
2446 }
2447
2448 wxLogLastError(_T("AlphaBlend"));
2449 }
2450 #endif // defined(AC_SRC_OVER)
2451
2452 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2453 // implementation
2454 #ifdef wxHAVE_RAW_BITMAP
2455 wxAlphaBlend(hdcDst, x, y, width, height, bmp);
2456
2457 return true;
2458 #else // !wxHAVE_RAW_BITMAP
2459 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2460 // alpha but at least something will be shown like this)
2461 return false;
2462 #endif // wxHAVE_RAW_BITMAP
2463 }
2464
2465
2466 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2467 #ifdef wxHAVE_RAW_BITMAP
2468
2469 static void
2470 wxAlphaBlend(HDC hdcDst, int xDst, int yDst, int w, int h, const wxBitmap& bmpSrc)
2471 {
2472 // get the destination DC pixels
2473 wxBitmap bmpDst(w, h, 32 /* force creating RGBA DIB */);
2474 MemoryHDC hdcMem;
2475 SelectInHDC select(hdcMem, GetHbitmapOf(bmpDst));
2476
2477 if ( !::BitBlt(hdcMem, 0, 0, w, h, hdcDst, 0, 0, SRCCOPY) )
2478 {
2479 wxLogLastError(_T("BitBlt"));
2480 }
2481
2482 // combine them with the source bitmap using alpha
2483 wxAlphaPixelData dataDst(bmpDst),
2484 dataSrc((wxBitmap &)bmpSrc);
2485
2486 wxCHECK_RET( dataDst && dataSrc,
2487 _T("failed to get raw data in wxAlphaBlend") );
2488
2489 wxAlphaPixelData::Iterator pDst(dataDst),
2490 pSrc(dataSrc);
2491
2492 for ( int y = 0; y < h; y++ )
2493 {
2494 wxAlphaPixelData::Iterator pDstRowStart = pDst,
2495 pSrcRowStart = pSrc;
2496
2497 for ( int x = 0; x < w; x++ )
2498 {
2499 // note that source bitmap uses premultiplied alpha (as required by
2500 // the real AlphaBlend)
2501 const unsigned beta = 255 - pSrc.Alpha();
2502
2503 pDst.Red() = pSrc.Red() + (beta * pDst.Red() + 127) / 255;
2504 pDst.Blue() = pSrc.Blue() + (beta * pDst.Blue() + 127) / 255;
2505 pDst.Green() = pSrc.Green() + (beta * pDst.Green() + 127) / 255;
2506
2507 ++pDst;
2508 ++pSrc;
2509 }
2510
2511 pDst = pDstRowStart;
2512 pSrc = pSrcRowStart;
2513 pDst.OffsetY(dataDst, 1);
2514 pSrc.OffsetY(dataSrc, 1);
2515 }
2516
2517 // and finally blit them back to the destination DC
2518 if ( !::BitBlt(hdcDst, xDst, yDst, w, h, hdcMem, 0, 0, SRCCOPY) )
2519 {
2520 wxLogLastError(_T("BitBlt"));
2521 }
2522 }
2523
2524 #endif // #ifdef wxHAVE_RAW_BITMAP