]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dc.cpp
forwarding style changes to documentViews, see #14578
[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.t_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 const HDC hdcSrc = GetHdcOf(*implSrc);
2250
2251 // if either the source or destination has alpha channel, we must use
2252 // AlphaBlt() as other function don't handle it correctly
2253 const wxBitmap& bmpSrc = implSrc->GetSelectedBitmap();
2254 if ( bmpSrc.IsOk() && (bmpSrc.HasAlpha() ||
2255 (m_selectedBitmap.IsOk() && m_selectedBitmap.HasAlpha())) )
2256 {
2257 if ( AlphaBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2258 xsrc, ysrc, srcWidth, srcHeight, hdcSrc, bmpSrc) )
2259 return true;
2260 }
2261
2262 wxMask *mask = NULL;
2263 if ( useMask )
2264 {
2265 mask = bmpSrc.GetMask();
2266
2267 if ( !(bmpSrc.IsOk() && mask && mask->GetMaskBitmap()) )
2268 {
2269 // don't give assert here because this would break existing
2270 // programs - just silently ignore useMask parameter
2271 useMask = false;
2272 }
2273 }
2274
2275 if (xsrcMask == -1 && ysrcMask == -1)
2276 {
2277 xsrcMask = xsrc; ysrcMask = ysrc;
2278 }
2279
2280 wxTextColoursChanger textCol(GetHdc(), *this);
2281
2282 DWORD dwRop;
2283 switch (rop)
2284 {
2285 case wxXOR: dwRop = SRCINVERT; break;
2286 case wxINVERT: dwRop = DSTINVERT; break;
2287 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
2288 case wxAND_REVERSE: dwRop = SRCERASE; break;
2289 case wxCLEAR: dwRop = BLACKNESS; break;
2290 case wxSET: dwRop = WHITENESS; break;
2291 case wxOR_INVERT: dwRop = MERGEPAINT; break;
2292 case wxAND: dwRop = SRCAND; break;
2293 case wxOR: dwRop = SRCPAINT; break;
2294 case wxEQUIV: dwRop = 0x00990066; break;
2295 case wxNAND: dwRop = 0x007700E6; break;
2296 case wxAND_INVERT: dwRop = 0x00220326; break;
2297 case wxCOPY: dwRop = SRCCOPY; break;
2298 case wxNO_OP: dwRop = DSTCOPY; break;
2299 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
2300 case wxNOR: dwRop = NOTSRCCOPY; break;
2301 default:
2302 wxFAIL_MSG( wxT("unsupported logical function") );
2303 return false;
2304 }
2305
2306 bool success = false;
2307
2308 if (useMask)
2309 {
2310 #ifdef __WIN32__
2311 // we want the part of the image corresponding to the mask to be
2312 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2313 // meaning of fg and bg is inverted which corresponds to wxWin notion
2314 // of the mask which is also contrary to the Windows one)
2315
2316 // On some systems, MaskBlt succeeds yet is much much slower
2317 // than the wxWidgets fall-back implementation. So we need
2318 // to be able to switch this on and off at runtime.
2319 #if wxUSE_SYSTEM_OPTIONS
2320 static bool s_maskBltAllowed = wxSystemOptions::GetOptionInt("no-maskblt") == 0;
2321 if ( s_maskBltAllowed )
2322 #endif
2323 {
2324 if ( dstWidth == srcWidth && dstHeight == srcHeight )
2325 {
2326 success = ::MaskBlt
2327 (
2328 GetHdc(),
2329 xdest, ydest, dstWidth, dstHeight,
2330 hdcSrc,
2331 xsrc, ysrc,
2332 (HBITMAP)mask->GetMaskBitmap(),
2333 xsrcMask, ysrcMask,
2334 MAKEROP4(dwRop, DSTCOPY)
2335 ) != 0;
2336 }
2337 }
2338
2339 if ( !success )
2340 #endif // Win32
2341 {
2342 // Blit bitmap with mask
2343 HDC dc_mask ;
2344 HDC dc_buffer ;
2345 HBITMAP buffer_bmap ;
2346
2347 #if wxUSE_DC_CACHEING
2348 // create a temp buffer bitmap and DCs to access it and the mask
2349 wxDCCacheEntry* dcCacheEntry1 = FindDCInCache(NULL, hdcSrc);
2350 dc_mask = (HDC) dcCacheEntry1->m_dc;
2351
2352 wxDCCacheEntry* dcCacheEntry2 = FindDCInCache(dcCacheEntry1, GetHDC());
2353 dc_buffer = (HDC) dcCacheEntry2->m_dc;
2354
2355 wxDCCacheEntry* bitmapCacheEntry = FindBitmapInCache(GetHDC(),
2356 dstWidth, dstHeight);
2357
2358 buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap;
2359 #else // !wxUSE_DC_CACHEING
2360 // create a temp buffer bitmap and DCs to access it and the mask
2361 dc_mask = ::CreateCompatibleDC(hdcSrc);
2362 dc_buffer = ::CreateCompatibleDC(GetHdc());
2363 buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), dstWidth, dstHeight);
2364 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2365 HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
2366 HGDIOBJ hOldBufferBitmap = ::SelectObject(dc_buffer, buffer_bmap);
2367
2368 // copy dest to buffer
2369 if ( !::BitBlt(dc_buffer, 0, 0, dstWidth, dstHeight,
2370 GetHdc(), xdest, ydest, SRCCOPY) )
2371 {
2372 wxLogLastError(wxT("BitBlt"));
2373 }
2374
2375 SET_STRETCH_BLT_MODE(GetHdc());
2376
2377 // copy src to buffer using selected raster op
2378 if ( !::StretchBlt(dc_buffer, 0, 0, dstWidth, dstHeight,
2379 hdcSrc, xsrc, ysrc, srcWidth, srcHeight, dwRop) )
2380 {
2381 wxLogLastError(wxT("StretchBlt"));
2382 }
2383
2384 // set masked area in buffer to BLACK
2385 {
2386 wxTextColoursChanger textCol2(GetHdc(), *wxBLACK, *wxWHITE);
2387 if ( !::StretchBlt(dc_buffer, 0, 0, dstWidth, dstHeight,
2388 dc_mask, xsrcMask, ysrcMask,
2389 srcWidth, srcHeight, SRCAND) )
2390 {
2391 wxLogLastError(wxT("StretchBlt"));
2392 }
2393
2394 // set unmasked area in dest to BLACK
2395 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2396 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2397 if ( !::StretchBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2398 dc_mask, xsrcMask, ysrcMask,
2399 srcWidth, srcHeight, SRCAND) )
2400 {
2401 wxLogLastError(wxT("StretchBlt"));
2402 }
2403 } // restore the original text and background colours
2404
2405 // OR buffer to dest
2406 success = ::BitBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2407 dc_buffer, 0, 0, SRCPAINT) != 0;
2408 if ( !success )
2409 {
2410 wxLogLastError(wxT("BitBlt"));
2411 }
2412
2413 // tidy up temporary DCs and bitmap
2414 ::SelectObject(dc_mask, hOldMaskBitmap);
2415 ::SelectObject(dc_buffer, hOldBufferBitmap);
2416
2417 #if !wxUSE_DC_CACHEING
2418 {
2419 ::DeleteDC(dc_mask);
2420 ::DeleteDC(dc_buffer);
2421 ::DeleteObject(buffer_bmap);
2422 }
2423 #endif
2424 }
2425 }
2426 else // no mask, just BitBlt() it
2427 {
2428 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2429 // use StretchBlt() if available and finally fall back to BitBlt()
2430
2431 // FIXME: use appropriate WinCE functions
2432 #ifndef __WXWINCE__
2433 const int caps = ::GetDeviceCaps(GetHdc(), RASTERCAPS);
2434 if ( bmpSrc.IsOk() && (caps & RC_STRETCHDIB) )
2435 {
2436 DIBSECTION ds;
2437 wxZeroMemory(ds);
2438
2439 if ( ::GetObject(GetHbitmapOf(bmpSrc),
2440 sizeof(ds),
2441 &ds) == sizeof(ds) )
2442 {
2443 SET_STRETCH_BLT_MODE(GetHdc());
2444
2445 // Unlike all the other functions used here (i.e. AlphaBlt(),
2446 // MaskBlt(), BitBlt() and StretchBlt()), StretchDIBits() does
2447 // not take into account the source DC logical coordinates
2448 // automatically as it doesn't even work with the source HDC.
2449 // So do this manually to ensure that the coordinates are
2450 // interpreted in the same way here as in all the other cases.
2451 xsrc = source->LogicalToDeviceX(xsrc);
2452 ysrc = source->LogicalToDeviceY(ysrc);
2453 srcWidth = source->LogicalToDeviceXRel(srcWidth);
2454 srcHeight = source->LogicalToDeviceYRel(srcHeight);
2455
2456 // Figure out what co-ordinate system we're supposed to specify
2457 // ysrc in.
2458 const LONG hDIB = ds.dsBmih.biHeight;
2459 if ( hDIB > 0 )
2460 {
2461 // reflect ysrc
2462 ysrc = hDIB - (ysrc + srcHeight);
2463 }
2464
2465 if ( ::StretchDIBits(GetHdc(),
2466 xdest, ydest,
2467 dstWidth, dstHeight,
2468 xsrc, ysrc,
2469 srcWidth, srcHeight,
2470 ds.dsBm.bmBits,
2471 (LPBITMAPINFO)&ds.dsBmih,
2472 DIB_RGB_COLORS,
2473 dwRop
2474 ) == (int)GDI_ERROR )
2475 {
2476 // On Win9x this API fails most (all?) of the time, so
2477 // logging it becomes quite distracting. Since it falls
2478 // back to the code below this is not really serious, so
2479 // don't log it.
2480 //wxLogLastError(wxT("StretchDIBits"));
2481 }
2482 else
2483 {
2484 success = true;
2485 }
2486 }
2487 }
2488
2489 if ( !success && (caps & RC_STRETCHBLT) )
2490 #endif
2491 // __WXWINCE__
2492 {
2493 SET_STRETCH_BLT_MODE(GetHdc());
2494
2495 if ( !::StretchBlt
2496 (
2497 GetHdc(),
2498 xdest, ydest, dstWidth, dstHeight,
2499 hdcSrc,
2500 xsrc, ysrc, srcWidth, srcHeight,
2501 dwRop
2502 ) )
2503 {
2504 wxLogLastError(wxT("StretchBlt"));
2505 }
2506 else
2507 {
2508 success = true;
2509 }
2510 }
2511
2512 if ( !success )
2513 {
2514 if ( !::BitBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2515 hdcSrc, xsrc, ysrc, dwRop) )
2516 {
2517 wxLogLastError(wxT("BitBlt"));
2518 }
2519 else
2520 {
2521 success = true;
2522 }
2523 }
2524 }
2525
2526 return success;
2527 }
2528
2529 void wxMSWDCImpl::GetDeviceSize(int *width, int *height) const
2530 {
2531 WXMICROWIN_CHECK_HDC
2532
2533 if ( width )
2534 *width = ::GetDeviceCaps(GetHdc(), HORZRES);
2535 if ( height )
2536 *height = ::GetDeviceCaps(GetHdc(), VERTRES);
2537 }
2538
2539 void wxMSWDCImpl::DoGetSizeMM(int *w, int *h) const
2540 {
2541 WXMICROWIN_CHECK_HDC
2542
2543 // if we implement it in terms of DoGetSize() instead of directly using the
2544 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2545 // will also work for wxWindowDC and wxClientDC even though their size is
2546 // not the same as the total size of the screen
2547 int wPixels, hPixels;
2548 DoGetSize(&wPixels, &hPixels);
2549
2550 if ( w )
2551 {
2552 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
2553
2554 wxCHECK_RET( wTotal, wxT("0 width device?") );
2555
2556 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2557 }
2558
2559 if ( h )
2560 {
2561 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2562
2563 wxCHECK_RET( hTotal, wxT("0 height device?") );
2564
2565 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2566 }
2567 }
2568
2569 wxSize wxMSWDCImpl::GetPPI() const
2570 {
2571 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2572
2573 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2574 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2575
2576 return wxSize(x, y);
2577 }
2578
2579 // ----------------------------------------------------------------------------
2580 // DC caching
2581 // ----------------------------------------------------------------------------
2582
2583 #if wxUSE_DC_CACHEING
2584
2585 /*
2586 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2587 * improve it in due course, either using arrays, or simply storing pointers to one
2588 * entry for the bitmap, and two for the DCs. -- JACS
2589 */
2590
2591 wxObjectList wxMSWDCImpl::sm_bitmapCache;
2592 wxObjectList wxMSWDCImpl::sm_dcCache;
2593
2594 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2595 {
2596 m_bitmap = hBitmap;
2597 m_dc = 0;
2598 m_width = w;
2599 m_height = h;
2600 m_depth = depth;
2601 }
2602
2603 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2604 {
2605 m_bitmap = 0;
2606 m_dc = hDC;
2607 m_width = 0;
2608 m_height = 0;
2609 m_depth = depth;
2610 }
2611
2612 wxDCCacheEntry::~wxDCCacheEntry()
2613 {
2614 if (m_bitmap)
2615 ::DeleteObject((HBITMAP) m_bitmap);
2616 if (m_dc)
2617 ::DeleteDC((HDC) m_dc);
2618 }
2619
2620 wxDCCacheEntry* wxMSWDCImpl::FindBitmapInCache(WXHDC dc, int w, int h)
2621 {
2622 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2623 wxList::compatibility_iterator node = sm_bitmapCache.GetFirst();
2624 while (node)
2625 {
2626 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2627
2628 if (entry->m_depth == depth)
2629 {
2630 if (entry->m_width < w || entry->m_height < h)
2631 {
2632 ::DeleteObject((HBITMAP) entry->m_bitmap);
2633 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2634 if ( !entry->m_bitmap)
2635 {
2636 wxLogLastError(wxT("CreateCompatibleBitmap"));
2637 }
2638 entry->m_width = w; entry->m_height = h;
2639 return entry;
2640 }
2641 return entry;
2642 }
2643
2644 node = node->GetNext();
2645 }
2646 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2647 if ( !hBitmap)
2648 {
2649 wxLogLastError(wxT("CreateCompatibleBitmap"));
2650 }
2651 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2652 AddToBitmapCache(entry);
2653 return entry;
2654 }
2655
2656 wxDCCacheEntry* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
2657 {
2658 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2659 wxList::compatibility_iterator node = sm_dcCache.GetFirst();
2660 while (node)
2661 {
2662 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2663
2664 // Don't return the same one as we already have
2665 if (!notThis || (notThis != entry))
2666 {
2667 if (entry->m_depth == depth)
2668 {
2669 return entry;
2670 }
2671 }
2672
2673 node = node->GetNext();
2674 }
2675 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2676 if ( !hDC)
2677 {
2678 wxLogLastError(wxT("CreateCompatibleDC"));
2679 }
2680 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2681 AddToDCCache(entry);
2682 return entry;
2683 }
2684
2685 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry* entry)
2686 {
2687 sm_bitmapCache.Append(entry);
2688 }
2689
2690 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry* entry)
2691 {
2692 sm_dcCache.Append(entry);
2693 }
2694
2695 void wxMSWDCImpl::ClearCache()
2696 {
2697 WX_CLEAR_LIST(wxList, sm_dcCache);
2698 WX_CLEAR_LIST(wxList, sm_bitmapCache);
2699 }
2700
2701 // Clean up cache at app exit
2702 class wxDCModule : public wxModule
2703 {
2704 public:
2705 virtual bool OnInit() { return true; }
2706 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2707
2708 private:
2709 DECLARE_DYNAMIC_CLASS(wxDCModule)
2710 };
2711
2712 IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2713
2714 #endif // wxUSE_DC_CACHEING
2715
2716 // ----------------------------------------------------------------------------
2717 // alpha channel support
2718 // ----------------------------------------------------------------------------
2719
2720 static bool AlphaBlt(HDC hdcDst,
2721 int x, int y, int dstWidth, int dstHeight,
2722 int srcX, int srcY,
2723 int srcWidth, int srcHeight,
2724 HDC hdcSrc,
2725 const wxBitmap& bmp)
2726 {
2727 wxASSERT_MSG( bmp.IsOk() && bmp.HasAlpha(), wxT("AlphaBlt(): invalid bitmap") );
2728 wxASSERT_MSG( hdcDst && hdcSrc, wxT("AlphaBlt(): invalid HDC") );
2729
2730 // do we have AlphaBlend() and company in the headers?
2731 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2732 // yes, now try to see if we have it during run-time
2733 typedef BOOL (WINAPI *AlphaBlend_t)(HDC,int,int,int,int,
2734 HDC,int,int,int,int,
2735 BLENDFUNCTION);
2736
2737 static AlphaBlend_t
2738 pfnAlphaBlend = (AlphaBlend_t)wxMSIMG32DLL.GetSymbol(wxT("AlphaBlend"));
2739 if ( pfnAlphaBlend )
2740 {
2741 BLENDFUNCTION bf;
2742 bf.BlendOp = AC_SRC_OVER;
2743 bf.BlendFlags = 0;
2744 bf.SourceConstantAlpha = 0xff;
2745 bf.AlphaFormat = AC_SRC_ALPHA;
2746
2747 if ( pfnAlphaBlend(hdcDst, x, y, dstWidth, dstHeight,
2748 hdcSrc, srcX, srcY, srcWidth, srcHeight,
2749 bf) )
2750 {
2751 // skip wxAlphaBlend() call below
2752 return true;
2753 }
2754
2755 wxLogLastError(wxT("AlphaBlend"));
2756 }
2757 #else
2758 wxUnusedVar(hdcSrc);
2759 #endif // defined(AC_SRC_OVER)
2760
2761 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2762 // implementation
2763 #ifdef wxHAS_RAW_BITMAP
2764 wxAlphaBlend(hdcDst, x, y, dstWidth, dstHeight, srcX, srcY, srcWidth, srcHeight, bmp);
2765
2766 return true;
2767 #else // !wxHAS_RAW_BITMAP
2768 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2769 // alpha but at least something will be shown like this)
2770 wxUnusedVar(bmp);
2771 return false;
2772 #endif // wxHAS_RAW_BITMAP/!wxHAS_RAW_BITMAP
2773 }
2774
2775
2776 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2777 #ifdef wxHAS_RAW_BITMAP
2778
2779 static void
2780 wxAlphaBlend(HDC hdcDst, int xDst, int yDst,
2781 int dstWidth, int dstHeight,
2782 int srcX, int srcY,
2783 int srcWidth, int srcHeight,
2784 const wxBitmap& bmpSrc)
2785 {
2786 // get the destination DC pixels
2787 wxBitmap bmpDst(dstWidth, dstHeight, 32 /* force creating RGBA DIB */);
2788 MemoryHDC hdcMem;
2789 SelectInHDC select(hdcMem, GetHbitmapOf(bmpDst));
2790
2791 if ( !::BitBlt(hdcMem, 0, 0, dstWidth, dstHeight, hdcDst, xDst, yDst, SRCCOPY) )
2792 {
2793 wxLogLastError(wxT("BitBlt"));
2794 }
2795
2796 // combine them with the source bitmap using alpha
2797 wxAlphaPixelData dataDst(bmpDst),
2798 dataSrc((wxBitmap &)bmpSrc);
2799
2800 wxCHECK_RET( dataDst && dataSrc,
2801 wxT("failed to get raw data in wxAlphaBlend") );
2802
2803 wxAlphaPixelData::Iterator pDst(dataDst),
2804 pSrc(dataSrc);
2805
2806
2807 for ( int y = 0; y < dstHeight; y++ )
2808 {
2809 wxAlphaPixelData::Iterator pDstRowStart = pDst;
2810
2811 for ( int x = 0; x < dstWidth; x++ )
2812 {
2813 // source is point sampled, Alpha StretchBlit is ugly on Win95
2814 // (but does not impact performance)
2815 pSrc.MoveTo(dataSrc, srcX + (srcWidth*x/dstWidth), srcY + (srcHeight*y/dstHeight));
2816
2817 // note that source bitmap uses premultiplied alpha (as required by
2818 // the real AlphaBlend)
2819 const unsigned beta = 255 - pSrc.Alpha();
2820
2821 pDst.Red() = pSrc.Red() + (beta * pDst.Red() + 127) / 255;
2822 pDst.Blue() = pSrc.Blue() + (beta * pDst.Blue() + 127) / 255;
2823 pDst.Green() = pSrc.Green() + (beta * pDst.Green() + 127) / 255;
2824
2825 ++pDst;
2826 }
2827
2828 pDst = pDstRowStart;
2829 pDst.OffsetY(dataDst, 1);
2830 }
2831
2832 // and finally blit them back to the destination DC
2833 if ( !::BitBlt(hdcDst, xDst, yDst, dstWidth, dstHeight, hdcMem, 0, 0, SRCCOPY) )
2834 {
2835 wxLogLastError(wxT("BitBlt"));
2836 }
2837 }
2838
2839 #endif // wxHAS_RAW_BITMAP
2840
2841 void wxMSWDCImpl::DoGradientFillLinear (const wxRect& rect,
2842 const wxColour& initialColour,
2843 const wxColour& destColour,
2844 wxDirection nDirection)
2845 {
2846 // use native function if we have compile-time support it and can load it
2847 // during run-time (linking to it statically would make the program
2848 // unusable on earlier Windows versions)
2849 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2850 typedef BOOL
2851 (WINAPI *GradientFill_t)(HDC, PTRIVERTEX, ULONG, PVOID, ULONG, ULONG);
2852 static GradientFill_t pfnGradientFill =
2853 (GradientFill_t)wxMSIMG32DLL.GetSymbol(wxT("GradientFill"));
2854
2855 if ( pfnGradientFill )
2856 {
2857 GRADIENT_RECT grect;
2858 grect.UpperLeft = 0;
2859 grect.LowerRight = 1;
2860
2861 // invert colours direction if not filling from left-to-right or
2862 // top-to-bottom
2863 int firstVertex = nDirection == wxNORTH || nDirection == wxWEST ? 1 : 0;
2864
2865 // one vertex for upper left and one for upper-right
2866 TRIVERTEX vertices[2];
2867
2868 vertices[0].x = rect.GetLeft();
2869 vertices[0].y = rect.GetTop();
2870 vertices[1].x = rect.GetRight()+1;
2871 vertices[1].y = rect.GetBottom()+1;
2872
2873 vertices[firstVertex].Red = (COLOR16)(initialColour.Red() << 8);
2874 vertices[firstVertex].Green = (COLOR16)(initialColour.Green() << 8);
2875 vertices[firstVertex].Blue = (COLOR16)(initialColour.Blue() << 8);
2876 vertices[firstVertex].Alpha = 0;
2877 vertices[1 - firstVertex].Red = (COLOR16)(destColour.Red() << 8);
2878 vertices[1 - firstVertex].Green = (COLOR16)(destColour.Green() << 8);
2879 vertices[1 - firstVertex].Blue = (COLOR16)(destColour.Blue() << 8);
2880 vertices[1 - firstVertex].Alpha = 0;
2881
2882 if ( (*pfnGradientFill)
2883 (
2884 GetHdc(),
2885 vertices,
2886 WXSIZEOF(vertices),
2887 &grect,
2888 1,
2889 nDirection == wxWEST || nDirection == wxEAST
2890 ? GRADIENT_FILL_RECT_H
2891 : GRADIENT_FILL_RECT_V
2892 ) )
2893 {
2894 // skip call of the base class version below
2895 return;
2896 }
2897
2898 wxLogLastError(wxT("GradientFill"));
2899 }
2900 #endif // wxUSE_DYNLIB_CLASS
2901
2902 wxDCImpl::DoGradientFillLinear(rect, initialColour, destColour, nDirection);
2903 }
2904
2905 #if wxUSE_DYNLIB_CLASS
2906
2907 static DWORD wxGetDCLayout(HDC hdc)
2908 {
2909 typedef DWORD (WINAPI *GetLayout_t)(HDC);
2910 static GetLayout_t
2911 wxDL_INIT_FUNC(s_pfn, GetLayout, wxDynamicLibrary(wxT("gdi32.dll")));
2912
2913 return s_pfnGetLayout ? s_pfnGetLayout(hdc) : (DWORD)-1;
2914 }
2915
2916 wxLayoutDirection wxMSWDCImpl::GetLayoutDirection() const
2917 {
2918 DWORD layout = wxGetDCLayout(GetHdc());
2919
2920 if ( layout == (DWORD)-1 )
2921 return wxLayout_Default;
2922
2923 return layout & LAYOUT_RTL ? wxLayout_RightToLeft : wxLayout_LeftToRight;
2924 }
2925
2926 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir)
2927 {
2928 typedef DWORD (WINAPI *SetLayout_t)(HDC, DWORD);
2929 static SetLayout_t
2930 wxDL_INIT_FUNC(s_pfn, SetLayout, wxDynamicLibrary(wxT("gdi32.dll")));
2931 if ( !s_pfnSetLayout )
2932 return;
2933
2934 if ( dir == wxLayout_Default )
2935 {
2936 dir = wxTheApp->GetLayoutDirection();
2937 if ( dir == wxLayout_Default )
2938 return;
2939 }
2940
2941 DWORD layout = wxGetDCLayout(GetHdc());
2942 if ( dir == wxLayout_RightToLeft )
2943 layout |= LAYOUT_RTL;
2944 else
2945 layout &= ~LAYOUT_RTL;
2946
2947 s_pfnSetLayout(GetHdc(), layout);
2948 }
2949
2950 #else // !wxUSE_DYNLIB_CLASS
2951
2952 // we can't provide RTL support without dynamic loading, so stub it out
2953 wxLayoutDirection wxMSWDCImpl::GetLayoutDirection() const
2954 {
2955 return wxLayout_Default;
2956 }
2957
2958 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection WXUNUSED(dir))
2959 {
2960 }
2961
2962 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS