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