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