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