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