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