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