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