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