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