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