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