use COLORONCOLOR stretching mode in DrawBitmap() too (see #3400)
[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 #ifndef __WXWINCE__
1298 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
1299 #endif
1300
1301 if ( useMask )
1302 {
1303 wxMask *mask = bmp.GetMask();
1304 if ( mask )
1305 hbmpMask = (HBITMAP)mask->GetMaskBitmap();
1306
1307 if ( !hbmpMask )
1308 {
1309 // don't give assert here because this would break existing
1310 // programs - just silently ignore useMask parameter
1311 useMask = false;
1312 }
1313 }
1314 if ( useMask )
1315 {
1316 #ifdef __WIN32__
1317 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1318 // points
1319 // On some systems, MaskBlt succeeds yet is much much slower
1320 // than the wxWidgets fall-back implementation. So we need
1321 // to be able to switch this on and off at runtime.
1322 bool ok = false;
1323 #if wxUSE_SYSTEM_OPTIONS
1324 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1325 #endif
1326 {
1327 HDC cdc = GetHdc();
1328 HDC hdcMem = ::CreateCompatibleDC(GetHdc());
1329 HGDIOBJ hOldBitmap = ::SelectObject(hdcMem, GetHbitmapOf(bmp));
1330 #if wxUSE_PALETTE
1331 wxPalette *pal = bmp.GetPalette();
1332 if ( pal && ::GetDeviceCaps(cdc,BITSPIXEL) <= 8 )
1333 {
1334 oldPal = ::SelectPalette(hdcMem, GetHpaletteOf(*pal), FALSE);
1335 ::RealizePalette(hdcMem);
1336 }
1337 #endif // wxUSE_PALETTE
1338
1339 ok = ::MaskBlt(cdc, x, y, width, height,
1340 hdcMem, 0, 0,
1341 hbmpMask, 0, 0,
1342 MAKEROP4(SRCCOPY, DSTCOPY)) != 0;
1343
1344 #if wxUSE_PALETTE
1345 if (oldPal)
1346 ::SelectPalette(hdcMem, oldPal, FALSE);
1347 #endif // wxUSE_PALETTE
1348
1349 ::SelectObject(hdcMem, hOldBitmap);
1350 ::DeleteDC(hdcMem);
1351 }
1352
1353 if ( !ok )
1354 #endif // Win32
1355 {
1356 // Rather than reproduce wxMSWDCImpl::Blit, let's do it at the wxWin API
1357 // level
1358 wxMemoryDC memDC;
1359
1360 memDC.SelectObjectAsSource(bmp);
1361
1362 GetOwner()->Blit(x, y, width, height, &memDC, 0, 0, wxCOPY, useMask);
1363
1364 memDC.SelectObject(wxNullBitmap);
1365 }
1366 }
1367 else // no mask, just use BitBlt()
1368 {
1369 HDC cdc = GetHdc();
1370 HDC memdc = ::CreateCompatibleDC( cdc );
1371 HBITMAP hbitmap = (HBITMAP) bmp.GetHBITMAP( );
1372
1373 wxASSERT_MSG( hbitmap, wxT("bitmap is ok but HBITMAP is NULL?") );
1374
1375 wxTextColoursChanger textCol(GetHdc(), *this);
1376
1377 #if wxUSE_PALETTE
1378 wxPalette *pal = bmp.GetPalette();
1379 if ( pal && ::GetDeviceCaps(cdc,BITSPIXEL) <= 8 )
1380 {
1381 oldPal = ::SelectPalette(memdc, GetHpaletteOf(*pal), FALSE);
1382 ::RealizePalette(memdc);
1383 }
1384 #endif // wxUSE_PALETTE
1385
1386 HGDIOBJ hOldBitmap = ::SelectObject( memdc, hbitmap );
1387 ::BitBlt( cdc, x, y, width, height, memdc, 0, 0, SRCCOPY);
1388
1389 #if wxUSE_PALETTE
1390 if (oldPal)
1391 ::SelectPalette(memdc, oldPal, FALSE);
1392 #endif // wxUSE_PALETTE
1393
1394 ::SelectObject( memdc, hOldBitmap );
1395 ::DeleteDC( memdc );
1396 }
1397 }
1398
1399 void wxMSWDCImpl::DoDrawText(const wxString& text, wxCoord x, wxCoord y)
1400 {
1401 WXMICROWIN_CHECK_HDC
1402
1403 DrawAnyText(text, x, y);
1404
1405 // update the bounding box
1406 CalcBoundingBox(x, y);
1407
1408 wxCoord w, h;
1409 GetOwner()->GetTextExtent(text, &w, &h);
1410 CalcBoundingBox(x + w, y + h);
1411 }
1412
1413 void wxMSWDCImpl::DrawAnyText(const wxString& text, wxCoord x, wxCoord y)
1414 {
1415 WXMICROWIN_CHECK_HDC
1416
1417 // prepare for drawing the text
1418 wxTextColoursChanger textCol(GetHdc(), *this);
1419
1420 wxBkModeChanger bkMode(GetHdc(), m_backgroundMode);
1421
1422 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), 0, NULL,
1423 text.c_str(), text.length(), NULL) == 0 )
1424 {
1425 wxLogLastError(wxT("TextOut"));
1426 }
1427 }
1428
1429 void wxMSWDCImpl::DoDrawRotatedText(const wxString& text,
1430 wxCoord x, wxCoord y,
1431 double angle)
1432 {
1433 WXMICROWIN_CHECK_HDC
1434
1435 // we test that we have some font because otherwise we should still use the
1436 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1437 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1438 // font for drawing rotated fonts unfortunately)
1439 if ( (angle == 0.0) && m_font.IsOk() )
1440 {
1441 DoDrawText(text, x, y);
1442 }
1443 #ifndef __WXMICROWIN__
1444 else
1445 {
1446 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1447 // because it's not TrueType and so can't have non zero
1448 // orientation/escapement under Win9x
1449 wxFont font = m_font.IsOk() ? m_font : *wxSWISS_FONT;
1450 HFONT hfont = (HFONT)font.GetResourceHandle();
1451 LOGFONT lf;
1452 if ( ::GetObject(hfont, sizeof(lf), &lf) == 0 )
1453 {
1454 wxLogLastError(wxT("GetObject(hfont)"));
1455 }
1456
1457 // GDI wants the angle in tenth of degree
1458 long angle10 = (long)(angle * 10);
1459 lf.lfEscapement = angle10;
1460 lf. lfOrientation = angle10;
1461
1462 hfont = ::CreateFontIndirect(&lf);
1463 if ( !hfont )
1464 {
1465 wxLogLastError(wxT("CreateFont"));
1466 }
1467 else
1468 {
1469 HFONT hfontOld = (HFONT)::SelectObject(GetHdc(), hfont);
1470
1471 DrawAnyText(text, x, y);
1472
1473 (void)::SelectObject(GetHdc(), hfontOld);
1474 (void)::DeleteObject(hfont);
1475 }
1476
1477 // call the bounding box by adding all four vertices of the rectangle
1478 // containing the text to it (simpler and probably not slower than
1479 // determining which of them is really topmost/leftmost/...)
1480 wxCoord w, h;
1481 GetOwner()->GetTextExtent(text, &w, &h);
1482
1483 double rad = DegToRad(angle);
1484
1485 // "upper left" and "upper right"
1486 CalcBoundingBox(x, y);
1487 CalcBoundingBox(x + wxCoord(w*cos(rad)), y - wxCoord(w*sin(rad)));
1488
1489 // "bottom left" and "bottom right"
1490 x += (wxCoord)(h*sin(rad));
1491 y += (wxCoord)(h*cos(rad));
1492 CalcBoundingBox(x, y);
1493 CalcBoundingBox(x + wxCoord(w*cos(rad)), y - wxCoord(w*sin(rad)));
1494 }
1495 #endif
1496 }
1497
1498 // ---------------------------------------------------------------------------
1499 // set GDI objects
1500 // ---------------------------------------------------------------------------
1501
1502 #if wxUSE_PALETTE
1503
1504 void wxMSWDCImpl::DoSelectPalette(bool realize)
1505 {
1506 WXMICROWIN_CHECK_HDC
1507
1508 // Set the old object temporarily, in case the assignment deletes an object
1509 // that's not yet selected out.
1510 if (m_oldPalette)
1511 {
1512 ::SelectPalette(GetHdc(), (HPALETTE) m_oldPalette, FALSE);
1513 m_oldPalette = 0;
1514 }
1515
1516 if ( m_palette.IsOk() )
1517 {
1518 HPALETTE oldPal = ::SelectPalette(GetHdc(),
1519 GetHpaletteOf(m_palette),
1520 false);
1521 if (!m_oldPalette)
1522 m_oldPalette = (WXHPALETTE) oldPal;
1523
1524 if (realize)
1525 ::RealizePalette(GetHdc());
1526 }
1527 }
1528
1529 void wxMSWDCImpl::SetPalette(const wxPalette& palette)
1530 {
1531 if ( palette.IsOk() )
1532 {
1533 m_palette = palette;
1534 DoSelectPalette(true);
1535 }
1536 }
1537
1538 void wxMSWDCImpl::InitializePalette()
1539 {
1540 if ( wxDisplayDepth() <= 8 )
1541 {
1542 // look for any window or parent that has a custom palette. If any has
1543 // one then we need to use it in drawing operations
1544 wxWindow *win = m_window->GetAncestorWithCustomPalette();
1545
1546 m_hasCustomPalette = win && win->HasCustomPalette();
1547 if ( m_hasCustomPalette )
1548 {
1549 m_palette = win->GetPalette();
1550
1551 // turn on MSW translation for this palette
1552 DoSelectPalette();
1553 }
1554 }
1555 }
1556
1557 #endif // wxUSE_PALETTE
1558
1559 // SetFont/Pen/Brush() really ask to be implemented as a single template
1560 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1561
1562 void wxMSWDCImpl::SetFont(const wxFont& font)
1563 {
1564 WXMICROWIN_CHECK_HDC
1565
1566 if ( font == m_font )
1567 return;
1568
1569 if ( font.IsOk() )
1570 {
1571 HGDIOBJ hfont = ::SelectObject(GetHdc(), GetHfontOf(font));
1572 if ( hfont == HGDI_ERROR )
1573 {
1574 wxLogLastError(_T("SelectObject(font)"));
1575 }
1576 else // selected ok
1577 {
1578 if ( !m_oldFont )
1579 m_oldFont = (WXHFONT)hfont;
1580
1581 m_font = font;
1582 }
1583 }
1584 else // invalid font, reset the current font
1585 {
1586 if ( m_oldFont )
1587 {
1588 if ( ::SelectObject(GetHdc(), (HPEN) m_oldFont) == HGDI_ERROR )
1589 {
1590 wxLogLastError(_T("SelectObject(old font)"));
1591 }
1592
1593 m_oldFont = 0;
1594 }
1595
1596 m_font = wxNullFont;
1597 }
1598 }
1599
1600 void wxMSWDCImpl::SetPen(const wxPen& pen)
1601 {
1602 WXMICROWIN_CHECK_HDC
1603
1604 if ( pen == m_pen )
1605 return;
1606
1607 if ( pen.IsOk() )
1608 {
1609 HGDIOBJ hpen = ::SelectObject(GetHdc(), GetHpenOf(pen));
1610 if ( hpen == HGDI_ERROR )
1611 {
1612 wxLogLastError(_T("SelectObject(pen)"));
1613 }
1614 else // selected ok
1615 {
1616 if ( !m_oldPen )
1617 m_oldPen = (WXHPEN)hpen;
1618
1619 m_pen = pen;
1620 }
1621 }
1622 else // invalid pen, reset the current pen
1623 {
1624 if ( m_oldPen )
1625 {
1626 if ( ::SelectObject(GetHdc(), (HPEN) m_oldPen) == HGDI_ERROR )
1627 {
1628 wxLogLastError(_T("SelectObject(old pen)"));
1629 }
1630
1631 m_oldPen = 0;
1632 }
1633
1634 m_pen = wxNullPen;
1635 }
1636 }
1637
1638 void wxMSWDCImpl::SetBrush(const wxBrush& brush)
1639 {
1640 WXMICROWIN_CHECK_HDC
1641
1642 if ( brush == m_brush )
1643 return;
1644
1645 if ( brush.IsOk() )
1646 {
1647 // we must make sure the brush is aligned with the logical coordinates
1648 // before selecting it
1649 wxBitmap *stipple = brush.GetStipple();
1650 if ( stipple && stipple->IsOk() )
1651 {
1652 if ( !::SetBrushOrgEx
1653 (
1654 GetHdc(),
1655 m_deviceOriginX % stipple->GetWidth(),
1656 m_deviceOriginY % stipple->GetHeight(),
1657 NULL // [out] previous brush origin
1658 ) )
1659 {
1660 wxLogLastError(_T("SetBrushOrgEx()"));
1661 }
1662 }
1663
1664 HGDIOBJ hbrush = ::SelectObject(GetHdc(), GetHbrushOf(brush));
1665 if ( hbrush == HGDI_ERROR )
1666 {
1667 wxLogLastError(_T("SelectObject(brush)"));
1668 }
1669 else // selected ok
1670 {
1671 if ( !m_oldBrush )
1672 m_oldBrush = (WXHBRUSH)hbrush;
1673
1674 m_brush = brush;
1675 }
1676 }
1677 else // invalid brush, reset the current brush
1678 {
1679 if ( m_oldBrush )
1680 {
1681 if ( ::SelectObject(GetHdc(), (HPEN) m_oldBrush) == HGDI_ERROR )
1682 {
1683 wxLogLastError(_T("SelectObject(old brush)"));
1684 }
1685
1686 m_oldBrush = 0;
1687 }
1688
1689 m_brush = wxNullBrush;
1690 }
1691 }
1692
1693 void wxMSWDCImpl::SetBackground(const wxBrush& brush)
1694 {
1695 WXMICROWIN_CHECK_HDC
1696
1697 m_backgroundBrush = brush;
1698
1699 if ( m_backgroundBrush.IsOk() )
1700 {
1701 (void)SetBkColor(GetHdc(), m_backgroundBrush.GetColour().GetPixel());
1702 }
1703 }
1704
1705 void wxMSWDCImpl::SetBackgroundMode(int mode)
1706 {
1707 WXMICROWIN_CHECK_HDC
1708
1709 m_backgroundMode = mode;
1710
1711 // SetBackgroundColour now only refers to text background
1712 // and m_backgroundMode is used there
1713 }
1714
1715 void wxMSWDCImpl::SetLogicalFunction(wxRasterOperationMode function)
1716 {
1717 WXMICROWIN_CHECK_HDC
1718
1719 m_logicalFunction = function;
1720
1721 SetRop(m_hDC);
1722 }
1723
1724 void wxMSWDCImpl::SetRop(WXHDC dc)
1725 {
1726 if ( !dc || m_logicalFunction < 0 )
1727 return;
1728
1729 int rop;
1730
1731 switch (m_logicalFunction)
1732 {
1733 case wxCLEAR: rop = R2_BLACK; break;
1734 case wxXOR: rop = R2_XORPEN; break;
1735 case wxINVERT: rop = R2_NOT; break;
1736 case wxOR_REVERSE: rop = R2_MERGEPENNOT; break;
1737 case wxAND_REVERSE: rop = R2_MASKPENNOT; break;
1738 case wxCOPY: rop = R2_COPYPEN; break;
1739 case wxAND: rop = R2_MASKPEN; break;
1740 case wxAND_INVERT: rop = R2_MASKNOTPEN; break;
1741 case wxNO_OP: rop = R2_NOP; break;
1742 case wxNOR: rop = R2_NOTMERGEPEN; break;
1743 case wxEQUIV: rop = R2_NOTXORPEN; break;
1744 case wxSRC_INVERT: rop = R2_NOTCOPYPEN; break;
1745 case wxOR_INVERT: rop = R2_MERGENOTPEN; break;
1746 case wxNAND: rop = R2_NOTMASKPEN; break;
1747 case wxOR: rop = R2_MERGEPEN; break;
1748 case wxSET: rop = R2_WHITE; break;
1749 default:
1750 wxFAIL_MSG( wxS("unknown logical function") );
1751 return;
1752 }
1753
1754 SetROP2(GetHdc(), rop);
1755 }
1756
1757 bool wxMSWDCImpl::StartDoc(const wxString& WXUNUSED(message))
1758 {
1759 // We might be previewing, so return true to let it continue.
1760 return true;
1761 }
1762
1763 void wxMSWDCImpl::EndDoc()
1764 {
1765 }
1766
1767 void wxMSWDCImpl::StartPage()
1768 {
1769 }
1770
1771 void wxMSWDCImpl::EndPage()
1772 {
1773 }
1774
1775 // ---------------------------------------------------------------------------
1776 // text metrics
1777 // ---------------------------------------------------------------------------
1778
1779 wxCoord wxMSWDCImpl::GetCharHeight() const
1780 {
1781 WXMICROWIN_CHECK_HDC_RET(0)
1782
1783 TEXTMETRIC lpTextMetric;
1784
1785 GetTextMetrics(GetHdc(), &lpTextMetric);
1786
1787 return lpTextMetric.tmHeight;
1788 }
1789
1790 wxCoord wxMSWDCImpl::GetCharWidth() const
1791 {
1792 WXMICROWIN_CHECK_HDC_RET(0)
1793
1794 TEXTMETRIC lpTextMetric;
1795
1796 GetTextMetrics(GetHdc(), &lpTextMetric);
1797
1798 return lpTextMetric.tmAveCharWidth;
1799 }
1800
1801 void wxMSWDCImpl::DoGetTextExtent(const wxString& string, wxCoord *x, wxCoord *y,
1802 wxCoord *descent, wxCoord *externalLeading,
1803 const wxFont *font) const
1804 {
1805 #ifdef __WXMICROWIN__
1806 if (!GetHDC())
1807 {
1808 if (x) *x = 0;
1809 if (y) *y = 0;
1810 if (descent) *descent = 0;
1811 if (externalLeading) *externalLeading = 0;
1812 return;
1813 }
1814 #endif // __WXMICROWIN__
1815
1816 HFONT hfontOld;
1817 if ( font )
1818 {
1819 wxASSERT_MSG( font->IsOk(), _T("invalid font in wxMSWDCImpl::GetTextExtent") );
1820
1821 hfontOld = (HFONT)::SelectObject(GetHdc(), GetHfontOf(*font));
1822 }
1823 else // don't change the font
1824 {
1825 hfontOld = 0;
1826 }
1827
1828 SIZE sizeRect;
1829 const size_t len = string.length();
1830 if ( !::GetTextExtentPoint32(GetHdc(), string.wx_str(), len, &sizeRect) )
1831 {
1832 wxLogLastError(_T("GetTextExtentPoint32()"));
1833 }
1834
1835 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1836 // the result computed by GetTextExtentPoint32() may be too small as it
1837 // accounts for under/overhang of the first/last character while we want
1838 // just the bounding rect for this string so adjust the width as needed
1839 // (using API not available in 2002 SDKs of WinCE)
1840 if ( len > 0 )
1841 {
1842 ABC width;
1843 const wxChar chFirst = *string.begin();
1844 if ( ::GetCharABCWidths(GetHdc(), chFirst, chFirst, &width) )
1845 {
1846 if ( width.abcA < 0 )
1847 sizeRect.cx -= width.abcA;
1848
1849 if ( len > 1 )
1850 {
1851 const wxChar chLast = *string.rbegin();
1852 ::GetCharABCWidths(GetHdc(), chLast, chLast, &width);
1853 }
1854 //else: we already have the width of the last character
1855
1856 if ( width.abcC < 0 )
1857 sizeRect.cx -= width.abcC;
1858 }
1859 //else: GetCharABCWidths() failed, not a TrueType font?
1860 }
1861 #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1862
1863 TEXTMETRIC tm;
1864 ::GetTextMetrics(GetHdc(), &tm);
1865
1866 if (x)
1867 *x = sizeRect.cx;
1868 if (y)
1869 *y = sizeRect.cy;
1870 if (descent)
1871 *descent = tm.tmDescent;
1872 if (externalLeading)
1873 *externalLeading = tm.tmExternalLeading;
1874
1875 if ( hfontOld )
1876 {
1877 ::SelectObject(GetHdc(), hfontOld);
1878 }
1879 }
1880
1881
1882 // Each element of the array will be the width of the string up to and
1883 // including the coresoponding character in text.
1884
1885 bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const
1886 {
1887 static int maxLenText = -1;
1888 static int maxWidth = -1;
1889 int fit = 0;
1890 SIZE sz = {0,0};
1891 int stlen = text.length();
1892
1893 if (maxLenText == -1)
1894 {
1895 // Win9x and WinNT+ have different limits
1896 int version = wxGetOsVersion();
1897 maxLenText = version == wxOS_WINDOWS_NT ? 65535 : 8192;
1898 maxWidth = version == wxOS_WINDOWS_NT ? INT_MAX : 32767;
1899 }
1900
1901 widths.Empty();
1902 widths.Add(0, stlen); // fill the array with zeros
1903 if (stlen == 0)
1904 return true;
1905
1906 if (!::GetTextExtentExPoint(GetHdc(),
1907 text.c_str(), // string to check
1908 wxMin(stlen, maxLenText),
1909 maxWidth,
1910 &fit, // [out] count of chars
1911 // that will fit
1912 &widths[0], // array to fill
1913 &sz))
1914 {
1915 // API failed
1916 wxLogLastError(wxT("GetTextExtentExPoint"));
1917 return false;
1918 }
1919
1920 return true;
1921 }
1922
1923 void wxMSWDCImpl::RealizeScaleAndOrigin()
1924 {
1925 // although it may seem wasteful to always use MM_ANISOTROPIC here instead
1926 // of using MM_TEXT if there is no scaling, benchmarking doesn't detect any
1927 // noticeable difference between these mapping modes
1928 #ifndef __WXWINCE__
1929 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
1930
1931 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
1932 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
1933
1934 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
1935 ::SetWindowExtEx(GetHdc(), width, height, NULL);
1936
1937 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX, m_deviceOriginY, NULL);
1938 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX, m_logicalOriginY, NULL);
1939 #endif
1940 }
1941
1942 void wxMSWDCImpl::SetMapMode(wxMappingMode mode)
1943 {
1944 WXMICROWIN_CHECK_HDC
1945
1946 m_mappingMode = mode;
1947
1948 if ( mode == wxMM_TEXT )
1949 {
1950 m_logicalScaleX =
1951 m_logicalScaleY = 1.0;
1952 }
1953 else // need to do some calculations
1954 {
1955 int pixel_width = ::GetDeviceCaps(GetHdc(), HORZRES),
1956 pixel_height = ::GetDeviceCaps(GetHdc(), VERTRES),
1957 mm_width = ::GetDeviceCaps(GetHdc(), HORZSIZE),
1958 mm_height = ::GetDeviceCaps(GetHdc(), VERTSIZE);
1959
1960 if ( (mm_width == 0) || (mm_height == 0) )
1961 {
1962 // we can't calculate mm2pixels[XY] then!
1963 return;
1964 }
1965
1966 double mm2pixelsX = (double)pixel_width / mm_width,
1967 mm2pixelsY = (double)pixel_height / mm_height;
1968
1969 switch (mode)
1970 {
1971 case wxMM_TWIPS:
1972 m_logicalScaleX = twips2mm * mm2pixelsX;
1973 m_logicalScaleY = twips2mm * mm2pixelsY;
1974 break;
1975
1976 case wxMM_POINTS:
1977 m_logicalScaleX = pt2mm * mm2pixelsX;
1978 m_logicalScaleY = pt2mm * mm2pixelsY;
1979 break;
1980
1981 case wxMM_METRIC:
1982 m_logicalScaleX = mm2pixelsX;
1983 m_logicalScaleY = mm2pixelsY;
1984 break;
1985
1986 case wxMM_LOMETRIC:
1987 m_logicalScaleX = mm2pixelsX / 10.0;
1988 m_logicalScaleY = mm2pixelsY / 10.0;
1989 break;
1990
1991 default:
1992 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1993 }
1994 }
1995
1996 ComputeScaleAndOrigin();
1997
1998 RealizeScaleAndOrigin();
1999 }
2000
2001 void wxMSWDCImpl::SetUserScale(double x, double y)
2002 {
2003 WXMICROWIN_CHECK_HDC
2004
2005 if ( x == m_userScaleX && y == m_userScaleY )
2006 return;
2007
2008 wxDCImpl::SetUserScale(x,y);
2009
2010 RealizeScaleAndOrigin();
2011 }
2012
2013 void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight,
2014 bool yBottomUp)
2015 {
2016 WXMICROWIN_CHECK_HDC
2017
2018 int signX = xLeftRight ? 1 : -1,
2019 signY = yBottomUp ? -1 : 1;
2020
2021 if (signX == m_signX && signY == m_signY)
2022 return;
2023
2024 wxDCImpl::SetAxisOrientation( xLeftRight, yBottomUp );
2025
2026 RealizeScaleAndOrigin();
2027 }
2028
2029 void wxMSWDCImpl::SetLogicalOrigin(wxCoord x, wxCoord y)
2030 {
2031 WXMICROWIN_CHECK_HDC
2032
2033 if ( x == m_logicalOriginX && y == m_logicalOriginY )
2034 return;
2035
2036 wxDCImpl::SetLogicalOrigin( x, y );
2037
2038 RealizeScaleAndOrigin();
2039 }
2040
2041 // For use by wxWidgets only, unless custom units are required.
2042 void wxMSWDCImpl::SetLogicalScale(double x, double y)
2043 {
2044 WXMICROWIN_CHECK_HDC
2045
2046 wxDCImpl::SetLogicalScale(x,y);
2047 }
2048
2049 void wxMSWDCImpl::SetDeviceOrigin(wxCoord x, wxCoord y)
2050 {
2051 WXMICROWIN_CHECK_HDC
2052
2053 if ( x == m_deviceOriginX && y == m_deviceOriginY )
2054 return;
2055
2056 wxDCImpl::SetDeviceOrigin( x, y );
2057
2058 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
2059 }
2060
2061 // ---------------------------------------------------------------------------
2062 // bit blit
2063 // ---------------------------------------------------------------------------
2064
2065 bool wxMSWDCImpl::DoBlit(wxCoord dstX, wxCoord dstY,
2066 wxCoord dstWidth, wxCoord dstHeight,
2067 wxDC *source,
2068 wxCoord srcX, wxCoord srcY,
2069 wxRasterOperationMode rop, bool useMask,
2070 wxCoord srcMaskX, wxCoord srcMaskY)
2071 {
2072 return DoStretchBlit(dstX, dstY, dstWidth, dstHeight, source, srcX, srcY, dstWidth, dstHeight, rop, useMask, srcMaskX, srcMaskY);
2073 }
2074
2075 bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest, wxCoord ydest,
2076 wxCoord dstWidth, wxCoord dstHeight,
2077 wxDC *source,
2078 wxCoord xsrc, wxCoord ysrc,
2079 wxCoord srcWidth, wxCoord srcHeight,
2080 wxRasterOperationMode rop, bool useMask,
2081 wxCoord xsrcMask, wxCoord ysrcMask)
2082 {
2083 wxCHECK_MSG( source, false, _T("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
2084
2085 WXMICROWIN_CHECK_HDC_RET(false)
2086
2087 wxMSWDCImpl *implSrc = wxDynamicCast( source->GetImpl(), wxMSWDCImpl );
2088 if ( !implSrc )
2089 {
2090 // TODO: Do we want to be able to blit from other DCs too?
2091 return false;
2092 }
2093
2094 const HDC hdcSrc = GetHdcOf(*implSrc);
2095
2096 // if either the source or destination has alpha channel, we must use
2097 // AlphaBlt() as other function don't handle it correctly
2098 const wxBitmap& bmpSrc = implSrc->GetSelectedBitmap();
2099 if ( bmpSrc.IsOk() && (bmpSrc.HasAlpha() ||
2100 (m_selectedBitmap.IsOk() && m_selectedBitmap.HasAlpha())) )
2101 {
2102 if ( AlphaBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2103 xsrc, ysrc, srcWidth, srcHeight, hdcSrc, bmpSrc) )
2104 return true;
2105 }
2106
2107 wxMask *mask = NULL;
2108 if ( useMask )
2109 {
2110 mask = bmpSrc.GetMask();
2111
2112 if ( !(bmpSrc.IsOk() && mask && mask->GetMaskBitmap()) )
2113 {
2114 // don't give assert here because this would break existing
2115 // programs - just silently ignore useMask parameter
2116 useMask = false;
2117 }
2118 }
2119
2120 if (xsrcMask == -1 && ysrcMask == -1)
2121 {
2122 xsrcMask = xsrc; ysrcMask = ysrc;
2123 }
2124
2125 wxTextColoursChanger textCol(GetHdc(), *this);
2126
2127 DWORD dwRop;
2128 switch (rop)
2129 {
2130 case wxXOR: dwRop = SRCINVERT; break;
2131 case wxINVERT: dwRop = DSTINVERT; break;
2132 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
2133 case wxAND_REVERSE: dwRop = SRCERASE; break;
2134 case wxCLEAR: dwRop = BLACKNESS; break;
2135 case wxSET: dwRop = WHITENESS; break;
2136 case wxOR_INVERT: dwRop = MERGEPAINT; break;
2137 case wxAND: dwRop = SRCAND; break;
2138 case wxOR: dwRop = SRCPAINT; break;
2139 case wxEQUIV: dwRop = 0x00990066; break;
2140 case wxNAND: dwRop = 0x007700E6; break;
2141 case wxAND_INVERT: dwRop = 0x00220326; break;
2142 case wxCOPY: dwRop = SRCCOPY; break;
2143 case wxNO_OP: dwRop = DSTCOPY; break;
2144 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
2145 case wxNOR: dwRop = NOTSRCCOPY; break;
2146 default:
2147 wxFAIL_MSG( wxT("unsupported logical function") );
2148 return false;
2149 }
2150
2151 bool success = false;
2152
2153 if (useMask)
2154 {
2155 #ifdef __WIN32__
2156 // we want the part of the image corresponding to the mask to be
2157 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2158 // meaning of fg and bg is inverted which corresponds to wxWin notion
2159 // of the mask which is also contrary to the Windows one)
2160
2161 // On some systems, MaskBlt succeeds yet is much much slower
2162 // than the wxWidgets fall-back implementation. So we need
2163 // to be able to switch this on and off at runtime.
2164 #if wxUSE_SYSTEM_OPTIONS
2165 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
2166 #endif
2167 {
2168 if ( dstWidth == srcWidth && dstHeight == srcHeight )
2169 {
2170 success = ::MaskBlt
2171 (
2172 GetHdc(),
2173 xdest, ydest, dstWidth, dstHeight,
2174 hdcSrc,
2175 xsrc, ysrc,
2176 (HBITMAP)mask->GetMaskBitmap(),
2177 xsrcMask, ysrcMask,
2178 MAKEROP4(dwRop, DSTCOPY)
2179 ) != 0;
2180 }
2181 }
2182
2183 if ( !success )
2184 #endif // Win32
2185 {
2186 // Blit bitmap with mask
2187 HDC dc_mask ;
2188 HDC dc_buffer ;
2189 HBITMAP buffer_bmap ;
2190
2191 #if wxUSE_DC_CACHEING
2192 // create a temp buffer bitmap and DCs to access it and the mask
2193 wxDCCacheEntry* dcCacheEntry1 = FindDCInCache(NULL, hdcSrc);
2194 dc_mask = (HDC) dcCacheEntry1->m_dc;
2195
2196 wxDCCacheEntry* dcCacheEntry2 = FindDCInCache(dcCacheEntry1, GetHDC());
2197 dc_buffer = (HDC) dcCacheEntry2->m_dc;
2198
2199 wxDCCacheEntry* bitmapCacheEntry = FindBitmapInCache(GetHDC(),
2200 dstWidth, dstHeight);
2201
2202 buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap;
2203 #else // !wxUSE_DC_CACHEING
2204 // create a temp buffer bitmap and DCs to access it and the mask
2205 dc_mask = ::CreateCompatibleDC(hdcSrc);
2206 dc_buffer = ::CreateCompatibleDC(GetHdc());
2207 buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), dstWidth, dstHeight);
2208 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2209 HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
2210 HGDIOBJ hOldBufferBitmap = ::SelectObject(dc_buffer, buffer_bmap);
2211
2212 // copy dest to buffer
2213 if ( !::BitBlt(dc_buffer, 0, 0, dstWidth, dstHeight,
2214 GetHdc(), xdest, ydest, SRCCOPY) )
2215 {
2216 wxLogLastError(wxT("BitBlt"));
2217 }
2218
2219 #ifndef __WXWINCE__
2220 StretchBltModeChanger changeMode(dc_buffer, COLORONCOLOR);
2221 #endif
2222
2223 // copy src to buffer using selected raster op
2224 if ( !::StretchBlt(dc_buffer, 0, 0, dstWidth, dstHeight,
2225 hdcSrc, xsrc, ysrc, srcWidth, srcHeight, dwRop) )
2226 {
2227 wxLogLastError(wxT("StretchBlt"));
2228 }
2229
2230 // set masked area in buffer to BLACK
2231 {
2232 wxTextColoursChanger textCol2(GetHdc(), *wxBLACK, *wxWHITE);
2233 if ( !::StretchBlt(dc_buffer, 0, 0, dstWidth, dstHeight,
2234 dc_mask, xsrcMask, ysrcMask,
2235 srcWidth, srcHeight, SRCAND) )
2236 {
2237 wxLogLastError(wxT("StretchBlt"));
2238 }
2239
2240 // set unmasked area in dest to BLACK
2241 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2242 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2243 if ( !::StretchBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2244 dc_mask, xsrcMask, ysrcMask,
2245 srcWidth, srcHeight, SRCAND) )
2246 {
2247 wxLogLastError(wxT("StretchBlt"));
2248 }
2249 } // restore the original text and background colours
2250
2251 // OR buffer to dest
2252 success = ::BitBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2253 dc_buffer, 0, 0, SRCPAINT) != 0;
2254 if ( !success )
2255 {
2256 wxLogLastError(wxT("BitBlt"));
2257 }
2258
2259 // tidy up temporary DCs and bitmap
2260 ::SelectObject(dc_mask, hOldMaskBitmap);
2261 ::SelectObject(dc_buffer, hOldBufferBitmap);
2262
2263 #if !wxUSE_DC_CACHEING
2264 {
2265 ::DeleteDC(dc_mask);
2266 ::DeleteDC(dc_buffer);
2267 ::DeleteObject(buffer_bmap);
2268 }
2269 #endif
2270 }
2271 }
2272 else // no mask, just BitBlt() it
2273 {
2274 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2275 // use StretchBlt() if available and finally fall back to BitBlt()
2276
2277 // FIXME: use appropriate WinCE functions
2278 #ifndef __WXWINCE__
2279 const int caps = ::GetDeviceCaps(GetHdc(), RASTERCAPS);
2280 if ( bmpSrc.IsOk() && (caps & RC_STRETCHDIB) )
2281 {
2282 DIBSECTION ds;
2283 wxZeroMemory(ds);
2284
2285 if ( ::GetObject(GetHbitmapOf(bmpSrc),
2286 sizeof(ds),
2287 &ds) == sizeof(ds) )
2288 {
2289 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2290
2291 // Figure out what co-ordinate system we're supposed to specify
2292 // ysrc in.
2293 const LONG hDIB = ds.dsBmih.biHeight;
2294 if ( hDIB > 0 )
2295 {
2296 // reflect ysrc
2297 ysrc = hDIB - (ysrc + srcHeight);
2298 }
2299
2300 if ( ::StretchDIBits(GetHdc(),
2301 xdest, ydest,
2302 dstWidth, dstHeight,
2303 xsrc, ysrc,
2304 srcWidth, srcHeight,
2305 ds.dsBm.bmBits,
2306 (LPBITMAPINFO)&ds.dsBmih,
2307 DIB_RGB_COLORS,
2308 dwRop
2309 ) == (int)GDI_ERROR )
2310 {
2311 // On Win9x this API fails most (all?) of the time, so
2312 // logging it becomes quite distracting. Since it falls
2313 // back to the code below this is not really serious, so
2314 // don't log it.
2315 //wxLogLastError(wxT("StretchDIBits"));
2316 }
2317 else
2318 {
2319 success = true;
2320 }
2321 }
2322 }
2323
2324 if ( !success && (caps & RC_STRETCHBLT) )
2325 #endif
2326 // __WXWINCE__
2327 {
2328 #ifndef __WXWINCE__
2329 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2330 #endif
2331
2332 if ( !::StretchBlt
2333 (
2334 GetHdc(),
2335 xdest, ydest, dstWidth, dstHeight,
2336 hdcSrc,
2337 xsrc, ysrc, srcWidth, srcHeight,
2338 dwRop
2339 ) )
2340 {
2341 wxLogLastError(_T("StretchBlt"));
2342 }
2343 else
2344 {
2345 success = true;
2346 }
2347 }
2348
2349 if ( !success )
2350 {
2351 if ( !::BitBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2352 hdcSrc, xsrc, ysrc, dwRop) )
2353 {
2354 wxLogLastError(_T("BitBlt"));
2355 }
2356 else
2357 {
2358 success = true;
2359 }
2360 }
2361 }
2362
2363 return success;
2364 }
2365
2366 void wxMSWDCImpl::GetDeviceSize(int *width, int *height) const
2367 {
2368 WXMICROWIN_CHECK_HDC
2369
2370 if ( width )
2371 *width = ::GetDeviceCaps(GetHdc(), HORZRES);
2372 if ( height )
2373 *height = ::GetDeviceCaps(GetHdc(), VERTRES);
2374 }
2375
2376 void wxMSWDCImpl::DoGetSizeMM(int *w, int *h) const
2377 {
2378 WXMICROWIN_CHECK_HDC
2379
2380 // if we implement it in terms of DoGetSize() instead of directly using the
2381 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2382 // will also work for wxWindowDC and wxClientDC even though their size is
2383 // not the same as the total size of the screen
2384 int wPixels, hPixels;
2385 DoGetSize(&wPixels, &hPixels);
2386
2387 if ( w )
2388 {
2389 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
2390
2391 wxCHECK_RET( wTotal, _T("0 width device?") );
2392
2393 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2394 }
2395
2396 if ( h )
2397 {
2398 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2399
2400 wxCHECK_RET( hTotal, _T("0 height device?") );
2401
2402 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2403 }
2404 }
2405
2406 wxSize wxMSWDCImpl::GetPPI() const
2407 {
2408 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2409
2410 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2411 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2412
2413 return wxSize(x, y);
2414 }
2415
2416 // ----------------------------------------------------------------------------
2417 // DC caching
2418 // ----------------------------------------------------------------------------
2419
2420 #if wxUSE_DC_CACHEING
2421
2422 /*
2423 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2424 * improve it in due course, either using arrays, or simply storing pointers to one
2425 * entry for the bitmap, and two for the DCs. -- JACS
2426 */
2427
2428 wxObjectList wxMSWDCImpl::sm_bitmapCache;
2429 wxObjectList wxMSWDCImpl::sm_dcCache;
2430
2431 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2432 {
2433 m_bitmap = hBitmap;
2434 m_dc = 0;
2435 m_width = w;
2436 m_height = h;
2437 m_depth = depth;
2438 }
2439
2440 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2441 {
2442 m_bitmap = 0;
2443 m_dc = hDC;
2444 m_width = 0;
2445 m_height = 0;
2446 m_depth = depth;
2447 }
2448
2449 wxDCCacheEntry::~wxDCCacheEntry()
2450 {
2451 if (m_bitmap)
2452 ::DeleteObject((HBITMAP) m_bitmap);
2453 if (m_dc)
2454 ::DeleteDC((HDC) m_dc);
2455 }
2456
2457 wxDCCacheEntry* wxMSWDCImpl::FindBitmapInCache(WXHDC dc, int w, int h)
2458 {
2459 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2460 wxList::compatibility_iterator node = sm_bitmapCache.GetFirst();
2461 while (node)
2462 {
2463 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2464
2465 if (entry->m_depth == depth)
2466 {
2467 if (entry->m_width < w || entry->m_height < h)
2468 {
2469 ::DeleteObject((HBITMAP) entry->m_bitmap);
2470 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2471 if ( !entry->m_bitmap)
2472 {
2473 wxLogLastError(wxT("CreateCompatibleBitmap"));
2474 }
2475 entry->m_width = w; entry->m_height = h;
2476 return entry;
2477 }
2478 return entry;
2479 }
2480
2481 node = node->GetNext();
2482 }
2483 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2484 if ( !hBitmap)
2485 {
2486 wxLogLastError(wxT("CreateCompatibleBitmap"));
2487 }
2488 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2489 AddToBitmapCache(entry);
2490 return entry;
2491 }
2492
2493 wxDCCacheEntry* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
2494 {
2495 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2496 wxList::compatibility_iterator node = sm_dcCache.GetFirst();
2497 while (node)
2498 {
2499 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2500
2501 // Don't return the same one as we already have
2502 if (!notThis || (notThis != entry))
2503 {
2504 if (entry->m_depth == depth)
2505 {
2506 return entry;
2507 }
2508 }
2509
2510 node = node->GetNext();
2511 }
2512 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2513 if ( !hDC)
2514 {
2515 wxLogLastError(wxT("CreateCompatibleDC"));
2516 }
2517 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2518 AddToDCCache(entry);
2519 return entry;
2520 }
2521
2522 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry* entry)
2523 {
2524 sm_bitmapCache.Append(entry);
2525 }
2526
2527 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry* entry)
2528 {
2529 sm_dcCache.Append(entry);
2530 }
2531
2532 void wxMSWDCImpl::ClearCache()
2533 {
2534 WX_CLEAR_LIST(wxList, sm_dcCache);
2535 WX_CLEAR_LIST(wxList, sm_bitmapCache);
2536 }
2537
2538 // Clean up cache at app exit
2539 class wxDCModule : public wxModule
2540 {
2541 public:
2542 virtual bool OnInit() { return true; }
2543 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2544
2545 private:
2546 DECLARE_DYNAMIC_CLASS(wxDCModule)
2547 };
2548
2549 IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2550
2551 #endif // wxUSE_DC_CACHEING
2552
2553 // ----------------------------------------------------------------------------
2554 // alpha channel support
2555 // ----------------------------------------------------------------------------
2556
2557 static bool AlphaBlt(HDC hdcDst,
2558 int x, int y, int dstWidth, int dstHeight,
2559 int srcX, int srcY,
2560 int srcWidth, int srcHeight,
2561 HDC hdcSrc,
2562 const wxBitmap& bmp)
2563 {
2564 wxASSERT_MSG( bmp.IsOk() && bmp.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2565 wxASSERT_MSG( hdcDst && hdcSrc, _T("AlphaBlt(): invalid HDC") );
2566
2567 // do we have AlphaBlend() and company in the headers?
2568 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2569 // yes, now try to see if we have it during run-time
2570 typedef BOOL (WINAPI *AlphaBlend_t)(HDC,int,int,int,int,
2571 HDC,int,int,int,int,
2572 BLENDFUNCTION);
2573
2574 static AlphaBlend_t
2575 pfnAlphaBlend = (AlphaBlend_t)wxMSIMG32DLL.GetSymbol(_T("AlphaBlend"));
2576 if ( pfnAlphaBlend )
2577 {
2578 BLENDFUNCTION bf;
2579 bf.BlendOp = AC_SRC_OVER;
2580 bf.BlendFlags = 0;
2581 bf.SourceConstantAlpha = 0xff;
2582 bf.AlphaFormat = AC_SRC_ALPHA;
2583
2584 if ( pfnAlphaBlend(hdcDst, x, y, dstWidth, dstHeight,
2585 hdcSrc, srcX, srcY, srcWidth, srcHeight,
2586 bf) )
2587 {
2588 // skip wxAlphaBlend() call below
2589 return true;
2590 }
2591
2592 wxLogLastError(_T("AlphaBlend"));
2593 }
2594 #else
2595 wxUnusedVar(hdcSrc);
2596 #endif // defined(AC_SRC_OVER)
2597
2598 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2599 // implementation
2600 #ifdef wxHAS_RAW_BITMAP
2601 wxAlphaBlend(hdcDst, x, y, dstWidth, dstHeight, srcX, srcY, srcWidth, srcHeight, bmp);
2602
2603 return true;
2604 #else // !wxHAS_RAW_BITMAP
2605 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2606 // alpha but at least something will be shown like this)
2607 wxUnusedVar(bmp);
2608 return false;
2609 #endif // wxHAS_RAW_BITMAP/!wxHAS_RAW_BITMAP
2610 }
2611
2612
2613 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2614 #ifdef wxHAS_RAW_BITMAP
2615
2616 static void
2617 wxAlphaBlend(HDC hdcDst, int xDst, int yDst,
2618 int dstWidth, int dstHeight,
2619 int srcX, int srcY,
2620 int srcWidth, int srcHeight,
2621 const wxBitmap& bmpSrc)
2622 {
2623 // get the destination DC pixels
2624 wxBitmap bmpDst(dstWidth, dstHeight, 32 /* force creating RGBA DIB */);
2625 MemoryHDC hdcMem;
2626 SelectInHDC select(hdcMem, GetHbitmapOf(bmpDst));
2627
2628 if ( !::BitBlt(hdcMem, 0, 0, dstWidth, dstHeight, hdcDst, xDst, yDst, SRCCOPY) )
2629 {
2630 wxLogLastError(_T("BitBlt"));
2631 }
2632
2633 // combine them with the source bitmap using alpha
2634 wxAlphaPixelData dataDst(bmpDst),
2635 dataSrc((wxBitmap &)bmpSrc);
2636
2637 wxCHECK_RET( dataDst && dataSrc,
2638 _T("failed to get raw data in wxAlphaBlend") );
2639
2640 wxAlphaPixelData::Iterator pDst(dataDst),
2641 pSrc(dataSrc);
2642
2643
2644 for ( int y = 0; y < dstHeight; y++ )
2645 {
2646 wxAlphaPixelData::Iterator pDstRowStart = pDst;
2647
2648 for ( int x = 0; x < dstWidth; x++ )
2649 {
2650 // source is point sampled, Alpha StretchBlit is ugly on Win95
2651 // (but does not impact performance)
2652 pSrc.MoveTo(dataSrc, srcX + (srcWidth*x/dstWidth), srcY + (srcHeight*y/dstHeight));
2653
2654 // note that source bitmap uses premultiplied alpha (as required by
2655 // the real AlphaBlend)
2656 const unsigned beta = 255 - pSrc.Alpha();
2657
2658 pDst.Red() = pSrc.Red() + (beta * pDst.Red() + 127) / 255;
2659 pDst.Blue() = pSrc.Blue() + (beta * pDst.Blue() + 127) / 255;
2660 pDst.Green() = pSrc.Green() + (beta * pDst.Green() + 127) / 255;
2661
2662 ++pDst;
2663 }
2664
2665 pDst = pDstRowStart;
2666 pDst.OffsetY(dataDst, 1);
2667 }
2668
2669 // and finally blit them back to the destination DC
2670 if ( !::BitBlt(hdcDst, xDst, yDst, dstWidth, dstHeight, hdcMem, 0, 0, SRCCOPY) )
2671 {
2672 wxLogLastError(_T("BitBlt"));
2673 }
2674 }
2675
2676 #endif // wxHAS_RAW_BITMAP
2677
2678 void wxMSWDCImpl::DoGradientFillLinear (const wxRect& rect,
2679 const wxColour& initialColour,
2680 const wxColour& destColour,
2681 wxDirection nDirection)
2682 {
2683 // use native function if we have compile-time support it and can load it
2684 // during run-time (linking to it statically would make the program
2685 // unusable on earlier Windows versions)
2686 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2687 typedef BOOL
2688 (WINAPI *GradientFill_t)(HDC, PTRIVERTEX, ULONG, PVOID, ULONG, ULONG);
2689 static GradientFill_t pfnGradientFill =
2690 (GradientFill_t)wxMSIMG32DLL.GetSymbol(_T("GradientFill"));
2691
2692 if ( pfnGradientFill )
2693 {
2694 GRADIENT_RECT grect;
2695 grect.UpperLeft = 0;
2696 grect.LowerRight = 1;
2697
2698 // invert colours direction if not filling from left-to-right or
2699 // top-to-bottom
2700 int firstVertex = nDirection == wxNORTH || nDirection == wxWEST ? 1 : 0;
2701
2702 // one vertex for upper left and one for upper-right
2703 TRIVERTEX vertices[2];
2704
2705 vertices[0].x = rect.GetLeft();
2706 vertices[0].y = rect.GetTop();
2707 vertices[1].x = rect.GetRight()+1;
2708 vertices[1].y = rect.GetBottom()+1;
2709
2710 vertices[firstVertex].Red = (COLOR16)(initialColour.Red() << 8);
2711 vertices[firstVertex].Green = (COLOR16)(initialColour.Green() << 8);
2712 vertices[firstVertex].Blue = (COLOR16)(initialColour.Blue() << 8);
2713 vertices[firstVertex].Alpha = 0;
2714 vertices[1 - firstVertex].Red = (COLOR16)(destColour.Red() << 8);
2715 vertices[1 - firstVertex].Green = (COLOR16)(destColour.Green() << 8);
2716 vertices[1 - firstVertex].Blue = (COLOR16)(destColour.Blue() << 8);
2717 vertices[1 - firstVertex].Alpha = 0;
2718
2719 if ( (*pfnGradientFill)
2720 (
2721 GetHdc(),
2722 vertices,
2723 WXSIZEOF(vertices),
2724 &grect,
2725 1,
2726 nDirection == wxWEST || nDirection == wxEAST
2727 ? GRADIENT_FILL_RECT_H
2728 : GRADIENT_FILL_RECT_V
2729 ) )
2730 {
2731 // skip call of the base class version below
2732 return;
2733 }
2734
2735 wxLogLastError(_T("GradientFill"));
2736 }
2737 #endif // wxUSE_DYNLIB_CLASS
2738
2739 wxDCImpl::DoGradientFillLinear(rect, initialColour, destColour, nDirection);
2740 }
2741
2742 #if wxUSE_DYNLIB_CLASS
2743
2744 static DWORD wxGetDCLayout(HDC hdc)
2745 {
2746 typedef DWORD (WINAPI *GetLayout_t)(HDC);
2747 static GetLayout_t
2748 wxDL_INIT_FUNC(s_pfn, GetLayout, wxDynamicLibrary(_T("gdi32.dll")));
2749
2750 return s_pfnGetLayout ? s_pfnGetLayout(hdc) : (DWORD)-1;
2751 }
2752
2753 wxLayoutDirection wxMSWDCImpl::GetLayoutDirection() const
2754 {
2755 DWORD layout = wxGetDCLayout(GetHdc());
2756
2757 if ( layout == (DWORD)-1 )
2758 return wxLayout_Default;
2759
2760 return layout & LAYOUT_RTL ? wxLayout_RightToLeft : wxLayout_LeftToRight;
2761 }
2762
2763 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir)
2764 {
2765 typedef DWORD (WINAPI *SetLayout_t)(HDC, DWORD);
2766 static SetLayout_t
2767 wxDL_INIT_FUNC(s_pfn, SetLayout, wxDynamicLibrary(_T("gdi32.dll")));
2768 if ( !s_pfnSetLayout )
2769 return;
2770
2771 if ( dir == wxLayout_Default )
2772 {
2773 dir = wxTheApp->GetLayoutDirection();
2774 if ( dir == wxLayout_Default )
2775 return;
2776 }
2777
2778 DWORD layout = wxGetDCLayout(GetHdc());
2779 if ( dir == wxLayout_RightToLeft )
2780 layout |= LAYOUT_RTL;
2781 else
2782 layout &= ~LAYOUT_RTL;
2783
2784 s_pfnSetLayout(GetHdc(), layout);
2785 }
2786
2787 #else // !wxUSE_DYNLIB_CLASS
2788
2789 // we can't provide RTL support without dynamic loading, so stub it out
2790 wxLayoutDirection wxMSWDCImpl::GetLayoutDirection() const
2791 {
2792 return wxLayout_Default;
2793 }
2794
2795 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection WXUNUSED(dir))
2796 {
2797 }
2798
2799 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS