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