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