]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dc.cpp
d878f66645385b778425e3db9fc0a8992cdb4b23
[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 DECLARE_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 DECLARE_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 DECLARE_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 DECLARE_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 #ifdef __WXDEBUG__
461 if (m_selectedBitmap.IsOk())
462 {
463 m_selectedBitmap.SetSelectedInto(NULL);
464 }
465 #endif
466 }
467 m_oldBitmap = 0;
468 if (m_oldPen)
469 {
470 ::SelectObject((HDC) dc, (HPEN) m_oldPen);
471 }
472 m_oldPen = 0;
473 if (m_oldBrush)
474 {
475 ::SelectObject((HDC) dc, (HBRUSH) m_oldBrush);
476 }
477 m_oldBrush = 0;
478 if (m_oldFont)
479 {
480 ::SelectObject((HDC) dc, (HFONT) m_oldFont);
481 }
482 m_oldFont = 0;
483
484 #if wxUSE_PALETTE
485 if (m_oldPalette)
486 {
487 ::SelectPalette((HDC) dc, (HPALETTE) m_oldPalette, FALSE);
488 }
489 m_oldPalette = 0;
490 #endif // wxUSE_PALETTE
491 }
492
493 m_brush = wxNullBrush;
494 m_pen = wxNullPen;
495 #if wxUSE_PALETTE
496 m_palette = wxNullPalette;
497 #endif // wxUSE_PALETTE
498 m_font = wxNullFont;
499 m_backgroundBrush = wxNullBrush;
500 m_selectedBitmap = wxNullBitmap;
501 }
502
503 // ---------------------------------------------------------------------------
504 // clipping
505 // ---------------------------------------------------------------------------
506
507 void wxMSWDCImpl::UpdateClipBox()
508 {
509 WXMICROWIN_CHECK_HDC
510
511 RECT rect;
512 ::GetClipBox(GetHdc(), &rect);
513
514 m_clipX1 = (wxCoord) XDEV2LOG(rect.left);
515 m_clipY1 = (wxCoord) YDEV2LOG(rect.top);
516 m_clipX2 = (wxCoord) XDEV2LOG(rect.right);
517 m_clipY2 = (wxCoord) YDEV2LOG(rect.bottom);
518 }
519
520 void
521 wxMSWDCImpl::DoGetClippingBox(wxCoord *x, wxCoord *y, wxCoord *w, wxCoord *h) const
522 {
523 // check if we should try to retrieve the clipping region possibly not set
524 // by our SetClippingRegion() but preset by Windows:this can only happen
525 // when we're associated with an existing HDC usign SetHDC(), see there
526 if ( m_clipping && !m_clipX1 && !m_clipX2 )
527 {
528 wxMSWDCImpl *self = wxConstCast(this, wxMSWDCImpl);
529 self->UpdateClipBox();
530
531 if ( !m_clipX1 && !m_clipX2 )
532 self->m_clipping = false;
533 }
534
535 wxDCImpl::DoGetClippingBox(x, y, w, h);
536 }
537
538 // common part of DoSetClippingRegion() and DoSetDeviceClippingRegion()
539 void wxMSWDCImpl::SetClippingHrgn(WXHRGN hrgn)
540 {
541 wxCHECK_RET( hrgn, wxT("invalid clipping region") );
542
543 WXMICROWIN_CHECK_HDC
544
545 // note that we combine the new clipping region with the existing one: this
546 // is compatible with what the other ports do and is the documented
547 // behaviour now (starting with 2.3.3)
548 #if defined(__WXWINCE__)
549 RECT rectClip;
550 if ( !::GetClipBox(GetHdc(), &rectClip) )
551 return;
552
553 // GetClipBox returns logical coordinates, so transform to device
554 rectClip.left = LogicalToDeviceX(rectClip.left);
555 rectClip.top = LogicalToDeviceY(rectClip.top);
556 rectClip.right = LogicalToDeviceX(rectClip.right);
557 rectClip.bottom = LogicalToDeviceY(rectClip.bottom);
558
559 HRGN hrgnDest = ::CreateRectRgn(0, 0, 0, 0);
560 HRGN hrgnClipOld = ::CreateRectRgn(rectClip.left, rectClip.top,
561 rectClip.right, rectClip.bottom);
562
563 if ( ::CombineRgn(hrgnDest, hrgnClipOld, (HRGN)hrgn, RGN_AND) != ERROR )
564 {
565 ::SelectClipRgn(GetHdc(), hrgnDest);
566 }
567
568 ::DeleteObject(hrgnClipOld);
569 ::DeleteObject(hrgnDest);
570 #else // !WinCE
571 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN)hrgn, RGN_AND) == ERROR )
572 {
573 wxLogLastError(_T("ExtSelectClipRgn"));
574
575 return;
576 }
577 #endif // WinCE/!WinCE
578
579 m_clipping = true;
580
581 UpdateClipBox();
582 }
583
584 void wxMSWDCImpl::DoSetClippingRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
585 {
586 // the region coords are always the device ones, so do the translation
587 // manually
588 //
589 // FIXME: possible +/-1 error here, to check!
590 HRGN hrgn = ::CreateRectRgn(LogicalToDeviceX(x),
591 LogicalToDeviceY(y),
592 LogicalToDeviceX(x + w),
593 LogicalToDeviceY(y + h));
594 if ( !hrgn )
595 {
596 wxLogLastError(_T("CreateRectRgn"));
597 }
598 else
599 {
600 SetClippingHrgn((WXHRGN)hrgn);
601
602 ::DeleteObject(hrgn);
603 }
604 }
605
606 void wxMSWDCImpl::DoSetDeviceClippingRegion(const wxRegion& region)
607 {
608 SetClippingHrgn(region.GetHRGN());
609 }
610
611 void wxMSWDCImpl::DestroyClippingRegion()
612 {
613 WXMICROWIN_CHECK_HDC
614
615 if (m_clipping && m_hDC)
616 {
617 #if 1
618 // On a PocketPC device (not necessarily emulator), resetting
619 // the clip region as per the old method causes bad display
620 // problems. In fact setting a null region is probably OK
621 // on desktop WIN32 also, since the WIN32 docs imply that the user
622 // clipping region is independent from the paint clipping region.
623 ::SelectClipRgn(GetHdc(), 0);
624 #else
625 // TODO: this should restore the previous clipping region,
626 // so that OnPaint processing works correctly, and the update
627 // clipping region doesn't get destroyed after the first
628 // DestroyClippingRegion.
629 HRGN rgn = CreateRectRgn(0, 0, 32000, 32000);
630 ::SelectClipRgn(GetHdc(), rgn);
631 ::DeleteObject(rgn);
632 #endif
633 }
634
635 wxDCImpl::DestroyClippingRegion();
636 }
637
638 // ---------------------------------------------------------------------------
639 // query capabilities
640 // ---------------------------------------------------------------------------
641
642 bool wxMSWDCImpl::CanDrawBitmap() const
643 {
644 return true;
645 }
646
647 bool wxMSWDCImpl::CanGetTextExtent() const
648 {
649 #ifdef __WXMICROWIN__
650 // TODO Extend MicroWindows' GetDeviceCaps function
651 return true;
652 #else
653 // What sort of display is it?
654 int technology = ::GetDeviceCaps(GetHdc(), TECHNOLOGY);
655
656 return (technology == DT_RASDISPLAY) || (technology == DT_RASPRINTER);
657 #endif
658 }
659
660 int wxMSWDCImpl::GetDepth() const
661 {
662 WXMICROWIN_CHECK_HDC_RET(16)
663
664 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL);
665 }
666
667 // ---------------------------------------------------------------------------
668 // drawing
669 // ---------------------------------------------------------------------------
670
671 void wxMSWDCImpl::Clear()
672 {
673 WXMICROWIN_CHECK_HDC
674
675 RECT rect;
676 if (m_window)
677 {
678 GetClientRect((HWND) m_window->GetHWND(), &rect);
679 }
680 else
681 {
682 // No, I think we should simply ignore this if printing on e.g.
683 // a printer DC.
684 // wxCHECK_RET( m_selectedBitmap.IsOk(), wxT("this DC can't be cleared") );
685 if (!m_selectedBitmap.IsOk())
686 return;
687
688 rect.left = -m_deviceOriginX; rect.top = -m_deviceOriginY;
689 rect.right = m_selectedBitmap.GetWidth()-m_deviceOriginX;
690 rect.bottom = m_selectedBitmap.GetHeight()-m_deviceOriginY;
691 }
692
693 #ifndef __WXWINCE__
694 (void) ::SetMapMode(GetHdc(), MM_TEXT);
695 #endif
696
697 DWORD colour = ::GetBkColor(GetHdc());
698 HBRUSH brush = ::CreateSolidBrush(colour);
699 ::FillRect(GetHdc(), &rect, brush);
700 ::DeleteObject(brush);
701
702 RealizeScaleAndOrigin();
703 }
704
705 bool wxMSWDCImpl::DoFloodFill(wxCoord WXUNUSED_IN_WINCE(x),
706 wxCoord WXUNUSED_IN_WINCE(y),
707 const wxColour& WXUNUSED_IN_WINCE(col),
708 int WXUNUSED_IN_WINCE(style))
709 {
710 #ifdef __WXWINCE__
711 return false;
712 #else
713 WXMICROWIN_CHECK_HDC_RET(false)
714
715 bool success = (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x), YLOG2DEV(y),
716 col.GetPixel(),
717 style == wxFLOOD_SURFACE ? FLOODFILLSURFACE
718 : FLOODFILLBORDER) ) ;
719 if (!success)
720 {
721 // quoting from the MSDN docs:
722 //
723 // Following are some of the reasons this function might fail:
724 //
725 // * The filling could not be completed.
726 // * The specified point has the boundary color specified by the
727 // crColor parameter (if FLOODFILLBORDER was requested).
728 // * The specified point does not have the color specified by
729 // crColor (if FLOODFILLSURFACE was requested)
730 // * The point is outside the clipping region that is, it is not
731 // visible on the device.
732 //
733 wxLogLastError(wxT("ExtFloodFill"));
734 }
735
736 CalcBoundingBox(x, y);
737
738 return success;
739 #endif
740 }
741
742 bool wxMSWDCImpl::DoGetPixel(wxCoord x, wxCoord y, wxColour *col) const
743 {
744 WXMICROWIN_CHECK_HDC_RET(false)
745
746 wxCHECK_MSG( col, false, _T("NULL colour parameter in wxMSWDCImpl::GetPixel") );
747
748 // get the color of the pixel
749 COLORREF pixelcolor = ::GetPixel(GetHdc(), XLOG2DEV(x), YLOG2DEV(y));
750
751 wxRGBToColour(*col, pixelcolor);
752
753 return true;
754 }
755
756 void wxMSWDCImpl::DoCrossHair(wxCoord x, wxCoord y)
757 {
758 WXMICROWIN_CHECK_HDC
759
760 wxCoord x1 = x-VIEWPORT_EXTENT;
761 wxCoord y1 = y-VIEWPORT_EXTENT;
762 wxCoord x2 = x+VIEWPORT_EXTENT;
763 wxCoord y2 = y+VIEWPORT_EXTENT;
764
765 wxDrawLine(GetHdc(), XLOG2DEV(x1), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y));
766 wxDrawLine(GetHdc(), XLOG2DEV(x), YLOG2DEV(y1), XLOG2DEV(x), YLOG2DEV(y2));
767
768 CalcBoundingBox(x1, y1);
769 CalcBoundingBox(x2, y2);
770 }
771
772 void wxMSWDCImpl::DoDrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
773 {
774 WXMICROWIN_CHECK_HDC
775
776 wxDrawLine(GetHdc(), XLOG2DEV(x1), YLOG2DEV(y1), XLOG2DEV(x2), YLOG2DEV(y2));
777
778 CalcBoundingBox(x1, y1);
779 CalcBoundingBox(x2, y2);
780 }
781
782 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
783 // and ending at (x2, y2)
784 void wxMSWDCImpl::DoDrawArc(wxCoord x1, wxCoord y1,
785 wxCoord x2, wxCoord y2,
786 wxCoord xc, wxCoord yc)
787 {
788 #ifdef __WXWINCE__
789 // Slower emulation since WinCE doesn't support Pie and Arc
790 double r = sqrt( (x1-xc)*(x1-xc) + (y1-yc)*(y1-yc) );
791 double sa = acos((x1-xc)/r)/M_PI*180; // between 0 and 180
792 if( y1>yc ) sa = -sa; // below center
793 double ea = atan2(yc-y2, x2-xc)/M_PI*180;
794 DoDrawEllipticArcRot( xc-r, yc-r, 2*r, 2*r, sa, ea );
795 #else
796
797 WXMICROWIN_CHECK_HDC
798
799 wxBrushAttrsSetter cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
800
801 double dx = xc - x1;
802 double dy = yc - y1;
803 double radius = (double)sqrt(dx*dx+dy*dy);
804 wxCoord r = (wxCoord)radius;
805
806 // treat the special case of full circle separately
807 if ( x1 == x2 && y1 == y2 )
808 {
809 GetOwner()->DrawEllipse(xc - r, yc - r, 2*r, 2*r);
810 return;
811 }
812
813 wxCoord xx1 = XLOG2DEV(x1);
814 wxCoord yy1 = YLOG2DEV(y1);
815 wxCoord xx2 = XLOG2DEV(x2);
816 wxCoord yy2 = YLOG2DEV(y2);
817 wxCoord xxc = XLOG2DEV(xc);
818 wxCoord yyc = YLOG2DEV(yc);
819 wxCoord ray = (wxCoord) sqrt(double((xxc-xx1)*(xxc-xx1)+(yyc-yy1)*(yyc-yy1)));
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 int 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 int 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(int 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
1746 default:
1747 wxFAIL_MSG( wxT("unsupported logical function") );
1748 return;
1749 }
1750
1751 SetROP2(GetHdc(), rop);
1752 }
1753
1754 bool wxMSWDCImpl::StartDoc(const wxString& WXUNUSED(message))
1755 {
1756 // We might be previewing, so return true to let it continue.
1757 return true;
1758 }
1759
1760 void wxMSWDCImpl::EndDoc()
1761 {
1762 }
1763
1764 void wxMSWDCImpl::StartPage()
1765 {
1766 }
1767
1768 void wxMSWDCImpl::EndPage()
1769 {
1770 }
1771
1772 // ---------------------------------------------------------------------------
1773 // text metrics
1774 // ---------------------------------------------------------------------------
1775
1776 wxCoord wxMSWDCImpl::GetCharHeight() const
1777 {
1778 WXMICROWIN_CHECK_HDC_RET(0)
1779
1780 TEXTMETRIC lpTextMetric;
1781
1782 GetTextMetrics(GetHdc(), &lpTextMetric);
1783
1784 return lpTextMetric.tmHeight;
1785 }
1786
1787 wxCoord wxMSWDCImpl::GetCharWidth() const
1788 {
1789 WXMICROWIN_CHECK_HDC_RET(0)
1790
1791 TEXTMETRIC lpTextMetric;
1792
1793 GetTextMetrics(GetHdc(), &lpTextMetric);
1794
1795 return lpTextMetric.tmAveCharWidth;
1796 }
1797
1798 void wxMSWDCImpl::DoGetTextExtent(const wxString& string, wxCoord *x, wxCoord *y,
1799 wxCoord *descent, wxCoord *externalLeading,
1800 const wxFont *font) const
1801 {
1802 #ifdef __WXMICROWIN__
1803 if (!GetHDC())
1804 {
1805 if (x) *x = 0;
1806 if (y) *y = 0;
1807 if (descent) *descent = 0;
1808 if (externalLeading) *externalLeading = 0;
1809 return;
1810 }
1811 #endif // __WXMICROWIN__
1812
1813 HFONT hfontOld;
1814 if ( font )
1815 {
1816 wxASSERT_MSG( font->IsOk(), _T("invalid font in wxMSWDCImpl::GetTextExtent") );
1817
1818 hfontOld = (HFONT)::SelectObject(GetHdc(), GetHfontOf(*font));
1819 }
1820 else // don't change the font
1821 {
1822 hfontOld = 0;
1823 }
1824
1825 SIZE sizeRect;
1826 const size_t len = string.length();
1827 if ( !::GetTextExtentPoint32(GetHdc(), string.wx_str(), len, &sizeRect) )
1828 {
1829 wxLogLastError(_T("GetTextExtentPoint32()"));
1830 }
1831
1832 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1833 // the result computed by GetTextExtentPoint32() may be too small as it
1834 // accounts for under/overhang of the first/last character while we want
1835 // just the bounding rect for this string so adjust the width as needed
1836 // (using API not available in 2002 SDKs of WinCE)
1837 if ( len > 0 )
1838 {
1839 ABC width;
1840 const wxChar chFirst = *string.begin();
1841 if ( ::GetCharABCWidths(GetHdc(), chFirst, chFirst, &width) )
1842 {
1843 if ( width.abcA < 0 )
1844 sizeRect.cx -= width.abcA;
1845
1846 if ( len > 1 )
1847 {
1848 const wxChar chLast = *string.rbegin();
1849 ::GetCharABCWidths(GetHdc(), chLast, chLast, &width);
1850 }
1851 //else: we already have the width of the last character
1852
1853 if ( width.abcC < 0 )
1854 sizeRect.cx -= width.abcC;
1855 }
1856 //else: GetCharABCWidths() failed, not a TrueType font?
1857 }
1858 #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1859
1860 TEXTMETRIC tm;
1861 ::GetTextMetrics(GetHdc(), &tm);
1862
1863 if (x)
1864 *x = sizeRect.cx;
1865 if (y)
1866 *y = sizeRect.cy;
1867 if (descent)
1868 *descent = tm.tmDescent;
1869 if (externalLeading)
1870 *externalLeading = tm.tmExternalLeading;
1871
1872 if ( hfontOld )
1873 {
1874 ::SelectObject(GetHdc(), hfontOld);
1875 }
1876 }
1877
1878
1879 // Each element of the array will be the width of the string up to and
1880 // including the coresoponding character in text.
1881
1882 bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const
1883 {
1884 static int maxLenText = -1;
1885 static int maxWidth = -1;
1886 int fit = 0;
1887 SIZE sz = {0,0};
1888 int stlen = text.length();
1889
1890 if (maxLenText == -1)
1891 {
1892 // Win9x and WinNT+ have different limits
1893 int version = wxGetOsVersion();
1894 maxLenText = version == wxOS_WINDOWS_NT ? 65535 : 8192;
1895 maxWidth = version == wxOS_WINDOWS_NT ? INT_MAX : 32767;
1896 }
1897
1898 widths.Empty();
1899 widths.Add(0, stlen); // fill the array with zeros
1900 if (stlen == 0)
1901 return true;
1902
1903 if (!::GetTextExtentExPoint(GetHdc(),
1904 text.c_str(), // string to check
1905 wxMin(stlen, maxLenText),
1906 maxWidth,
1907 &fit, // [out] count of chars
1908 // that will fit
1909 &widths[0], // array to fill
1910 &sz))
1911 {
1912 // API failed
1913 wxLogLastError(wxT("GetTextExtentExPoint"));
1914 return false;
1915 }
1916
1917 return true;
1918 }
1919
1920 void wxMSWDCImpl::RealizeScaleAndOrigin()
1921 {
1922 // although it may seem wasteful to always use MM_ANISOTROPIC here instead
1923 // of using MM_TEXT if there is no scaling, benchmarking doesn't detect any
1924 // noticeable difference between these mapping modes
1925 #ifndef __WXWINCE__
1926 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
1927
1928 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
1929 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
1930
1931 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
1932 ::SetWindowExtEx(GetHdc(), width, height, NULL);
1933
1934 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX, m_deviceOriginY, NULL);
1935 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX, m_logicalOriginY, NULL);
1936 #endif
1937 }
1938
1939 void wxMSWDCImpl::SetMapMode(int mode)
1940 {
1941 WXMICROWIN_CHECK_HDC
1942
1943 m_mappingMode = mode;
1944
1945 if ( mode == wxMM_TEXT )
1946 {
1947 m_logicalScaleX =
1948 m_logicalScaleY = 1.0;
1949 }
1950 else // need to do some calculations
1951 {
1952 int pixel_width = ::GetDeviceCaps(GetHdc(), HORZRES),
1953 pixel_height = ::GetDeviceCaps(GetHdc(), VERTRES),
1954 mm_width = ::GetDeviceCaps(GetHdc(), HORZSIZE),
1955 mm_height = ::GetDeviceCaps(GetHdc(), VERTSIZE);
1956
1957 if ( (mm_width == 0) || (mm_height == 0) )
1958 {
1959 // we can't calculate mm2pixels[XY] then!
1960 return;
1961 }
1962
1963 double mm2pixelsX = (double)pixel_width / mm_width,
1964 mm2pixelsY = (double)pixel_height / mm_height;
1965
1966 switch (mode)
1967 {
1968 case wxMM_TWIPS:
1969 m_logicalScaleX = twips2mm * mm2pixelsX;
1970 m_logicalScaleY = twips2mm * mm2pixelsY;
1971 break;
1972
1973 case wxMM_POINTS:
1974 m_logicalScaleX = pt2mm * mm2pixelsX;
1975 m_logicalScaleY = pt2mm * mm2pixelsY;
1976 break;
1977
1978 case wxMM_METRIC:
1979 m_logicalScaleX = mm2pixelsX;
1980 m_logicalScaleY = mm2pixelsY;
1981 break;
1982
1983 case wxMM_LOMETRIC:
1984 m_logicalScaleX = mm2pixelsX / 10.0;
1985 m_logicalScaleY = mm2pixelsY / 10.0;
1986 break;
1987
1988 default:
1989 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1990 }
1991 }
1992
1993 ComputeScaleAndOrigin();
1994
1995 RealizeScaleAndOrigin();
1996 }
1997
1998 void wxMSWDCImpl::SetUserScale(double x, double y)
1999 {
2000 WXMICROWIN_CHECK_HDC
2001
2002 if ( x == m_userScaleX && y == m_userScaleY )
2003 return;
2004
2005 wxDCImpl::SetUserScale(x,y);
2006
2007 RealizeScaleAndOrigin();
2008 }
2009
2010 void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight,
2011 bool yBottomUp)
2012 {
2013 WXMICROWIN_CHECK_HDC
2014
2015 int signX = xLeftRight ? 1 : -1,
2016 signY = yBottomUp ? -1 : 1;
2017
2018 if (signX == m_signX && signY == m_signY)
2019 return;
2020
2021 wxDCImpl::SetAxisOrientation( xLeftRight, yBottomUp );
2022
2023 RealizeScaleAndOrigin();
2024 }
2025
2026 void wxMSWDCImpl::SetLogicalOrigin(wxCoord x, wxCoord y)
2027 {
2028 WXMICROWIN_CHECK_HDC
2029
2030 if ( x == m_logicalOriginX && y == m_logicalOriginY )
2031 return;
2032
2033 wxDCImpl::SetLogicalOrigin( x, y );
2034
2035 RealizeScaleAndOrigin();
2036 }
2037
2038 // For use by wxWidgets only, unless custom units are required.
2039 void wxMSWDCImpl::SetLogicalScale(double x, double y)
2040 {
2041 WXMICROWIN_CHECK_HDC
2042
2043 wxDCImpl::SetLogicalScale(x,y);
2044 }
2045
2046 void wxMSWDCImpl::SetDeviceOrigin(wxCoord x, wxCoord y)
2047 {
2048 WXMICROWIN_CHECK_HDC
2049
2050 if ( x == m_deviceOriginX && y == m_deviceOriginY )
2051 return;
2052
2053 wxDCImpl::SetDeviceOrigin( x, y );
2054
2055 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
2056 }
2057
2058 // ---------------------------------------------------------------------------
2059 // bit blit
2060 // ---------------------------------------------------------------------------
2061
2062 bool wxMSWDCImpl::DoBlit(wxCoord dstX, wxCoord dstY,
2063 wxCoord dstWidth, wxCoord dstHeight,
2064 wxDC *source,
2065 wxCoord srcX, wxCoord srcY,
2066 int rop, bool useMask,
2067 wxCoord srcMaskX, wxCoord srcMaskY)
2068 {
2069 return DoStretchBlit(dstX, dstY, dstWidth, dstHeight, source, srcX, srcY, dstWidth, dstHeight, rop, useMask, srcMaskX, srcMaskY);
2070 }
2071
2072 bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest, wxCoord ydest,
2073 wxCoord dstWidth, wxCoord dstHeight,
2074 wxDC *source,
2075 wxCoord xsrc, wxCoord ysrc,
2076 wxCoord srcWidth, wxCoord srcHeight,
2077 int rop, bool useMask,
2078 wxCoord xsrcMask, wxCoord ysrcMask)
2079 {
2080 wxCHECK_MSG( source, false, _T("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
2081
2082 WXMICROWIN_CHECK_HDC_RET(false)
2083
2084 wxMSWDCImpl *implSrc = wxDynamicCast( source->GetImpl(), wxMSWDCImpl );
2085 if ( !implSrc )
2086 {
2087 // TODO: Do we want to be able to blit from other DCs too?
2088 return false;
2089 }
2090
2091 const HDC hdcSrc = GetHdcOf(*implSrc);
2092
2093 // if either the source or destination has alpha channel, we must use
2094 // AlphaBlt() as other function don't handle it correctly
2095 const wxBitmap& bmpSrc = implSrc->GetSelectedBitmap();
2096 if ( bmpSrc.IsOk() && (bmpSrc.HasAlpha() ||
2097 (m_selectedBitmap.IsOk() && m_selectedBitmap.HasAlpha())) )
2098 {
2099 if ( AlphaBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2100 xsrc, ysrc, srcWidth, srcHeight, hdcSrc, bmpSrc) )
2101 return true;
2102 }
2103
2104 wxMask *mask = NULL;
2105 if ( useMask )
2106 {
2107 mask = bmpSrc.GetMask();
2108
2109 if ( !(bmpSrc.IsOk() && mask && mask->GetMaskBitmap()) )
2110 {
2111 // don't give assert here because this would break existing
2112 // programs - just silently ignore useMask parameter
2113 useMask = false;
2114 }
2115 }
2116
2117 if (xsrcMask == -1 && ysrcMask == -1)
2118 {
2119 xsrcMask = xsrc; ysrcMask = ysrc;
2120 }
2121
2122 wxTextColoursChanger textCol(GetHdc(), *this);
2123
2124 DWORD dwRop;
2125 switch (rop)
2126 {
2127 case wxXOR: dwRop = SRCINVERT; break;
2128 case wxINVERT: dwRop = DSTINVERT; break;
2129 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
2130 case wxAND_REVERSE: dwRop = SRCERASE; break;
2131 case wxCLEAR: dwRop = BLACKNESS; break;
2132 case wxSET: dwRop = WHITENESS; break;
2133 case wxOR_INVERT: dwRop = MERGEPAINT; break;
2134 case wxAND: dwRop = SRCAND; break;
2135 case wxOR: dwRop = SRCPAINT; break;
2136 case wxEQUIV: dwRop = 0x00990066; break;
2137 case wxNAND: dwRop = 0x007700E6; break;
2138 case wxAND_INVERT: dwRop = 0x00220326; break;
2139 case wxCOPY: dwRop = SRCCOPY; break;
2140 case wxNO_OP: dwRop = DSTCOPY; break;
2141 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
2142 case wxNOR: dwRop = NOTSRCCOPY; break;
2143 default:
2144 wxFAIL_MSG( wxT("unsupported logical function") );
2145 return false;
2146 }
2147
2148 bool success = false;
2149
2150 if (useMask)
2151 {
2152 #ifdef __WIN32__
2153 // we want the part of the image corresponding to the mask to be
2154 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2155 // meaning of fg and bg is inverted which corresponds to wxWin notion
2156 // of the mask which is also contrary to the Windows one)
2157
2158 // On some systems, MaskBlt succeeds yet is much much slower
2159 // than the wxWidgets fall-back implementation. So we need
2160 // to be able to switch this on and off at runtime.
2161 #if wxUSE_SYSTEM_OPTIONS
2162 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
2163 #endif
2164 {
2165 if ( dstWidth == srcWidth && dstHeight == srcHeight )
2166 {
2167 success = ::MaskBlt
2168 (
2169 GetHdc(),
2170 xdest, ydest, dstWidth, dstHeight,
2171 hdcSrc,
2172 xsrc, ysrc,
2173 (HBITMAP)mask->GetMaskBitmap(),
2174 xsrcMask, ysrcMask,
2175 MAKEROP4(dwRop, DSTCOPY)
2176 ) != 0;
2177 }
2178 }
2179
2180 if ( !success )
2181 #endif // Win32
2182 {
2183 // Blit bitmap with mask
2184 HDC dc_mask ;
2185 HDC dc_buffer ;
2186 HBITMAP buffer_bmap ;
2187
2188 #if wxUSE_DC_CACHEING
2189 // create a temp buffer bitmap and DCs to access it and the mask
2190 wxDCCacheEntry* dcCacheEntry1 = FindDCInCache(NULL, hdcSrc);
2191 dc_mask = (HDC) dcCacheEntry1->m_dc;
2192
2193 wxDCCacheEntry* dcCacheEntry2 = FindDCInCache(dcCacheEntry1, GetHDC());
2194 dc_buffer = (HDC) dcCacheEntry2->m_dc;
2195
2196 wxDCCacheEntry* bitmapCacheEntry = FindBitmapInCache(GetHDC(),
2197 dstWidth, dstHeight);
2198
2199 buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap;
2200 #else // !wxUSE_DC_CACHEING
2201 // create a temp buffer bitmap and DCs to access it and the mask
2202 dc_mask = ::CreateCompatibleDC(hdcSrc);
2203 dc_buffer = ::CreateCompatibleDC(GetHdc());
2204 buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), dstWidth, dstHeight);
2205 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2206 HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
2207 HGDIOBJ hOldBufferBitmap = ::SelectObject(dc_buffer, buffer_bmap);
2208
2209 // copy dest to buffer
2210 if ( !::BitBlt(dc_buffer, 0, 0, dstWidth, dstHeight,
2211 GetHdc(), xdest, ydest, SRCCOPY) )
2212 {
2213 wxLogLastError(wxT("BitBlt"));
2214 }
2215
2216 #ifndef __WXWINCE__
2217 StretchBltModeChanger changeMode(dc_buffer, COLORONCOLOR);
2218 #endif
2219
2220 // copy src to buffer using selected raster op
2221 if ( !::StretchBlt(dc_buffer, 0, 0, dstWidth, dstHeight,
2222 hdcSrc, xsrc, ysrc, srcWidth, srcHeight, dwRop) )
2223 {
2224 wxLogLastError(wxT("StretchBlt"));
2225 }
2226
2227 // set masked area in buffer to BLACK
2228 {
2229 wxTextColoursChanger textCol2(GetHdc(), *wxBLACK, *wxWHITE);
2230 if ( !::StretchBlt(dc_buffer, 0, 0, dstWidth, dstHeight,
2231 dc_mask, xsrcMask, ysrcMask,
2232 srcWidth, srcHeight, SRCAND) )
2233 {
2234 wxLogLastError(wxT("StretchBlt"));
2235 }
2236
2237 // set unmasked area in dest to BLACK
2238 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2239 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2240 if ( !::StretchBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2241 dc_mask, xsrcMask, ysrcMask,
2242 srcWidth, srcHeight, SRCAND) )
2243 {
2244 wxLogLastError(wxT("StretchBlt"));
2245 }
2246 } // restore the original text and background colours
2247
2248 // OR buffer to dest
2249 success = ::BitBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2250 dc_buffer, 0, 0, SRCPAINT) != 0;
2251 if ( !success )
2252 {
2253 wxLogLastError(wxT("BitBlt"));
2254 }
2255
2256 // tidy up temporary DCs and bitmap
2257 ::SelectObject(dc_mask, hOldMaskBitmap);
2258 ::SelectObject(dc_buffer, hOldBufferBitmap);
2259
2260 #if !wxUSE_DC_CACHEING
2261 {
2262 ::DeleteDC(dc_mask);
2263 ::DeleteDC(dc_buffer);
2264 ::DeleteObject(buffer_bmap);
2265 }
2266 #endif
2267 }
2268 }
2269 else // no mask, just BitBlt() it
2270 {
2271 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2272 // use StretchBlt() if available and finally fall back to BitBlt()
2273
2274 // FIXME: use appropriate WinCE functions
2275 #ifndef __WXWINCE__
2276 const int caps = ::GetDeviceCaps(GetHdc(), RASTERCAPS);
2277 if ( bmpSrc.IsOk() && (caps & RC_STRETCHDIB) )
2278 {
2279 DIBSECTION ds;
2280 wxZeroMemory(ds);
2281
2282 if ( ::GetObject(GetHbitmapOf(bmpSrc),
2283 sizeof(ds),
2284 &ds) == sizeof(ds) )
2285 {
2286 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2287
2288 // Figure out what co-ordinate system we're supposed to specify
2289 // ysrc in.
2290 const LONG hDIB = ds.dsBmih.biHeight;
2291 if ( hDIB > 0 )
2292 {
2293 // reflect ysrc
2294 ysrc = hDIB - (ysrc + dstHeight);
2295 }
2296
2297 if ( ::StretchDIBits(GetHdc(),
2298 xdest, ydest,
2299 dstWidth, dstHeight,
2300 xsrc, ysrc,
2301 srcWidth, srcHeight,
2302 ds.dsBm.bmBits,
2303 (LPBITMAPINFO)&ds.dsBmih,
2304 DIB_RGB_COLORS,
2305 dwRop
2306 ) == (int)GDI_ERROR )
2307 {
2308 // On Win9x this API fails most (all?) of the time, so
2309 // logging it becomes quite distracting. Since it falls
2310 // back to the code below this is not really serious, so
2311 // don't log it.
2312 //wxLogLastError(wxT("StretchDIBits"));
2313 }
2314 else
2315 {
2316 success = true;
2317 }
2318 }
2319 }
2320
2321 if ( !success && (caps & RC_STRETCHBLT) )
2322 #endif
2323 // __WXWINCE__
2324 {
2325 #ifndef __WXWINCE__
2326 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2327 #endif
2328
2329 if ( !::StretchBlt
2330 (
2331 GetHdc(),
2332 xdest, ydest, dstWidth, dstHeight,
2333 hdcSrc,
2334 xsrc, ysrc, srcWidth, srcHeight,
2335 dwRop
2336 ) )
2337 {
2338 wxLogLastError(_T("StretchBlt"));
2339 }
2340 else
2341 {
2342 success = true;
2343 }
2344 }
2345
2346 if ( !success )
2347 {
2348 if ( !::BitBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2349 hdcSrc, xsrc, ysrc, dwRop) )
2350 {
2351 wxLogLastError(_T("BitBlt"));
2352 }
2353 else
2354 {
2355 success = true;
2356 }
2357 }
2358 }
2359
2360 return success;
2361 }
2362
2363 void wxMSWDCImpl::GetDeviceSize(int *width, int *height) const
2364 {
2365 WXMICROWIN_CHECK_HDC
2366
2367 if ( width )
2368 *width = ::GetDeviceCaps(GetHdc(), HORZRES);
2369 if ( height )
2370 *height = ::GetDeviceCaps(GetHdc(), VERTRES);
2371 }
2372
2373 void wxMSWDCImpl::DoGetSizeMM(int *w, int *h) const
2374 {
2375 WXMICROWIN_CHECK_HDC
2376
2377 // if we implement it in terms of DoGetSize() instead of directly using the
2378 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2379 // will also work for wxWindowDC and wxClientDC even though their size is
2380 // not the same as the total size of the screen
2381 int wPixels, hPixels;
2382 DoGetSize(&wPixels, &hPixels);
2383
2384 if ( w )
2385 {
2386 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
2387
2388 wxCHECK_RET( wTotal, _T("0 width device?") );
2389
2390 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2391 }
2392
2393 if ( h )
2394 {
2395 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2396
2397 wxCHECK_RET( hTotal, _T("0 height device?") );
2398
2399 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2400 }
2401 }
2402
2403 wxSize wxMSWDCImpl::GetPPI() const
2404 {
2405 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2406
2407 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2408 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2409
2410 return wxSize(x, y);
2411 }
2412
2413 // ----------------------------------------------------------------------------
2414 // DC caching
2415 // ----------------------------------------------------------------------------
2416
2417 #if wxUSE_DC_CACHEING
2418
2419 /*
2420 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2421 * improve it in due course, either using arrays, or simply storing pointers to one
2422 * entry for the bitmap, and two for the DCs. -- JACS
2423 */
2424
2425 wxObjectList wxMSWDCImpl::sm_bitmapCache;
2426 wxObjectList wxMSWDCImpl::sm_dcCache;
2427
2428 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2429 {
2430 m_bitmap = hBitmap;
2431 m_dc = 0;
2432 m_width = w;
2433 m_height = h;
2434 m_depth = depth;
2435 }
2436
2437 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2438 {
2439 m_bitmap = 0;
2440 m_dc = hDC;
2441 m_width = 0;
2442 m_height = 0;
2443 m_depth = depth;
2444 }
2445
2446 wxDCCacheEntry::~wxDCCacheEntry()
2447 {
2448 if (m_bitmap)
2449 ::DeleteObject((HBITMAP) m_bitmap);
2450 if (m_dc)
2451 ::DeleteDC((HDC) m_dc);
2452 }
2453
2454 wxDCCacheEntry* wxMSWDCImpl::FindBitmapInCache(WXHDC dc, int w, int h)
2455 {
2456 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2457 wxList::compatibility_iterator node = sm_bitmapCache.GetFirst();
2458 while (node)
2459 {
2460 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2461
2462 if (entry->m_depth == depth)
2463 {
2464 if (entry->m_width < w || entry->m_height < h)
2465 {
2466 ::DeleteObject((HBITMAP) entry->m_bitmap);
2467 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2468 if ( !entry->m_bitmap)
2469 {
2470 wxLogLastError(wxT("CreateCompatibleBitmap"));
2471 }
2472 entry->m_width = w; entry->m_height = h;
2473 return entry;
2474 }
2475 return entry;
2476 }
2477
2478 node = node->GetNext();
2479 }
2480 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2481 if ( !hBitmap)
2482 {
2483 wxLogLastError(wxT("CreateCompatibleBitmap"));
2484 }
2485 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2486 AddToBitmapCache(entry);
2487 return entry;
2488 }
2489
2490 wxDCCacheEntry* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
2491 {
2492 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2493 wxList::compatibility_iterator node = sm_dcCache.GetFirst();
2494 while (node)
2495 {
2496 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2497
2498 // Don't return the same one as we already have
2499 if (!notThis || (notThis != entry))
2500 {
2501 if (entry->m_depth == depth)
2502 {
2503 return entry;
2504 }
2505 }
2506
2507 node = node->GetNext();
2508 }
2509 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2510 if ( !hDC)
2511 {
2512 wxLogLastError(wxT("CreateCompatibleDC"));
2513 }
2514 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2515 AddToDCCache(entry);
2516 return entry;
2517 }
2518
2519 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry* entry)
2520 {
2521 sm_bitmapCache.Append(entry);
2522 }
2523
2524 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry* entry)
2525 {
2526 sm_dcCache.Append(entry);
2527 }
2528
2529 void wxMSWDCImpl::ClearCache()
2530 {
2531 WX_CLEAR_LIST(wxList, sm_dcCache);
2532 WX_CLEAR_LIST(wxList, sm_bitmapCache);
2533 }
2534
2535 // Clean up cache at app exit
2536 class wxDCModule : public wxModule
2537 {
2538 public:
2539 virtual bool OnInit() { return true; }
2540 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2541
2542 private:
2543 DECLARE_DYNAMIC_CLASS(wxDCModule)
2544 };
2545
2546 IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2547
2548 #endif // wxUSE_DC_CACHEING
2549
2550 // ----------------------------------------------------------------------------
2551 // alpha channel support
2552 // ----------------------------------------------------------------------------
2553
2554 static bool AlphaBlt(HDC hdcDst,
2555 int x, int y, int dstWidth, int dstHeight,
2556 int srcX, int srcY,
2557 int srcWidth, int srcHeight,
2558 HDC hdcSrc,
2559 const wxBitmap& bmp)
2560 {
2561 wxASSERT_MSG( bmp.IsOk() && bmp.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2562 wxASSERT_MSG( hdcDst && hdcSrc, _T("AlphaBlt(): invalid HDC") );
2563
2564 // do we have AlphaBlend() and company in the headers?
2565 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2566 // yes, now try to see if we have it during run-time
2567 typedef BOOL (WINAPI *AlphaBlend_t)(HDC,int,int,int,int,
2568 HDC,int,int,int,int,
2569 BLENDFUNCTION);
2570
2571 static AlphaBlend_t
2572 pfnAlphaBlend = (AlphaBlend_t)wxMSIMG32DLL.GetSymbol(_T("AlphaBlend"));
2573 if ( pfnAlphaBlend )
2574 {
2575 BLENDFUNCTION bf;
2576 bf.BlendOp = AC_SRC_OVER;
2577 bf.BlendFlags = 0;
2578 bf.SourceConstantAlpha = 0xff;
2579 bf.AlphaFormat = AC_SRC_ALPHA;
2580
2581 if ( pfnAlphaBlend(hdcDst, x, y, dstWidth, dstHeight,
2582 hdcSrc, srcX, srcY, srcWidth, srcHeight,
2583 bf) )
2584 {
2585 // skip wxAlphaBlend() call below
2586 return true;
2587 }
2588
2589 wxLogLastError(_T("AlphaBlend"));
2590 }
2591 #else
2592 wxUnusedVar(hdcSrc);
2593 #endif // defined(AC_SRC_OVER)
2594
2595 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2596 // implementation
2597 #ifdef wxHAS_RAW_BITMAP
2598 wxAlphaBlend(hdcDst, x, y, dstWidth, dstHeight, srcX, srcY, srcWidth, srcHeight, bmp);
2599
2600 return true;
2601 #else // !wxHAS_RAW_BITMAP
2602 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2603 // alpha but at least something will be shown like this)
2604 wxUnusedVar(bmp);
2605 return false;
2606 #endif // wxHAS_RAW_BITMAP/!wxHAS_RAW_BITMAP
2607 }
2608
2609
2610 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2611 #ifdef wxHAS_RAW_BITMAP
2612
2613 static void
2614 wxAlphaBlend(HDC hdcDst, int xDst, int yDst,
2615 int dstWidth, int dstHeight,
2616 int srcX, int srcY,
2617 int srcWidth, int srcHeight,
2618 const wxBitmap& bmpSrc)
2619 {
2620 // get the destination DC pixels
2621 wxBitmap bmpDst(dstWidth, dstHeight, 32 /* force creating RGBA DIB */);
2622 MemoryHDC hdcMem;
2623 SelectInHDC select(hdcMem, GetHbitmapOf(bmpDst));
2624
2625 if ( !::BitBlt(hdcMem, 0, 0, dstWidth, dstHeight, hdcDst, xDst, yDst, SRCCOPY) )
2626 {
2627 wxLogLastError(_T("BitBlt"));
2628 }
2629
2630 // combine them with the source bitmap using alpha
2631 wxAlphaPixelData dataDst(bmpDst),
2632 dataSrc((wxBitmap &)bmpSrc);
2633
2634 wxCHECK_RET( dataDst && dataSrc,
2635 _T("failed to get raw data in wxAlphaBlend") );
2636
2637 wxAlphaPixelData::Iterator pDst(dataDst),
2638 pSrc(dataSrc);
2639
2640
2641 for ( int y = 0; y < dstHeight; y++ )
2642 {
2643 wxAlphaPixelData::Iterator pDstRowStart = pDst;
2644
2645 for ( int x = 0; x < dstWidth; x++ )
2646 {
2647 // source is point sampled, Alpha StretchBlit is ugly on Win95
2648 // (but does not impact performance)
2649 pSrc.MoveTo(dataSrc, srcX + (srcWidth*x/dstWidth), srcY + (srcHeight*y/dstHeight));
2650
2651 // note that source bitmap uses premultiplied alpha (as required by
2652 // the real AlphaBlend)
2653 const unsigned beta = 255 - pSrc.Alpha();
2654
2655 pDst.Red() = pSrc.Red() + (beta * pDst.Red() + 127) / 255;
2656 pDst.Blue() = pSrc.Blue() + (beta * pDst.Blue() + 127) / 255;
2657 pDst.Green() = pSrc.Green() + (beta * pDst.Green() + 127) / 255;
2658
2659 ++pDst;
2660 }
2661
2662 pDst = pDstRowStart;
2663 pDst.OffsetY(dataDst, 1);
2664 }
2665
2666 // and finally blit them back to the destination DC
2667 if ( !::BitBlt(hdcDst, xDst, yDst, dstWidth, dstHeight, hdcMem, 0, 0, SRCCOPY) )
2668 {
2669 wxLogLastError(_T("BitBlt"));
2670 }
2671 }
2672
2673 #endif // wxHAS_RAW_BITMAP
2674
2675 void wxMSWDCImpl::DoGradientFillLinear (const wxRect& rect,
2676 const wxColour& initialColour,
2677 const wxColour& destColour,
2678 wxDirection nDirection)
2679 {
2680 // use native function if we have compile-time support it and can load it
2681 // during run-time (linking to it statically would make the program
2682 // unusable on earlier Windows versions)
2683 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2684 typedef BOOL
2685 (WINAPI *GradientFill_t)(HDC, PTRIVERTEX, ULONG, PVOID, ULONG, ULONG);
2686 static GradientFill_t pfnGradientFill =
2687 (GradientFill_t)wxMSIMG32DLL.GetSymbol(_T("GradientFill"));
2688
2689 if ( pfnGradientFill )
2690 {
2691 GRADIENT_RECT grect;
2692 grect.UpperLeft = 0;
2693 grect.LowerRight = 1;
2694
2695 // invert colours direction if not filling from left-to-right or
2696 // top-to-bottom
2697 int firstVertex = nDirection == wxNORTH || nDirection == wxWEST ? 1 : 0;
2698
2699 // one vertex for upper left and one for upper-right
2700 TRIVERTEX vertices[2];
2701
2702 vertices[0].x = rect.GetLeft();
2703 vertices[0].y = rect.GetTop();
2704 vertices[1].x = rect.GetRight()+1;
2705 vertices[1].y = rect.GetBottom()+1;
2706
2707 vertices[firstVertex].Red = (COLOR16)(initialColour.Red() << 8);
2708 vertices[firstVertex].Green = (COLOR16)(initialColour.Green() << 8);
2709 vertices[firstVertex].Blue = (COLOR16)(initialColour.Blue() << 8);
2710 vertices[firstVertex].Alpha = 0;
2711 vertices[1 - firstVertex].Red = (COLOR16)(destColour.Red() << 8);
2712 vertices[1 - firstVertex].Green = (COLOR16)(destColour.Green() << 8);
2713 vertices[1 - firstVertex].Blue = (COLOR16)(destColour.Blue() << 8);
2714 vertices[1 - firstVertex].Alpha = 0;
2715
2716 if ( (*pfnGradientFill)
2717 (
2718 GetHdc(),
2719 vertices,
2720 WXSIZEOF(vertices),
2721 &grect,
2722 1,
2723 nDirection == wxWEST || nDirection == wxEAST
2724 ? GRADIENT_FILL_RECT_H
2725 : GRADIENT_FILL_RECT_V
2726 ) )
2727 {
2728 // skip call of the base class version below
2729 return;
2730 }
2731
2732 wxLogLastError(_T("GradientFill"));
2733 }
2734 #endif // wxUSE_DYNLIB_CLASS
2735
2736 wxDCImpl::DoGradientFillLinear(rect, initialColour, destColour, nDirection);
2737 }
2738
2739 #if wxUSE_DYNLIB_CLASS
2740
2741 static DWORD wxGetDCLayout(HDC hdc)
2742 {
2743 typedef DWORD (WINAPI *GetLayout_t)(HDC);
2744 static GetLayout_t
2745 wxDL_INIT_FUNC(s_pfn, GetLayout, wxDynamicLibrary(_T("gdi32.dll")));
2746
2747 return s_pfnGetLayout ? s_pfnGetLayout(hdc) : (DWORD)-1;
2748 }
2749
2750 wxLayoutDirection wxMSWDCImpl::GetLayoutDirection() const
2751 {
2752 DWORD layout = wxGetDCLayout(GetHdc());
2753
2754 if ( layout == (DWORD)-1 )
2755 return wxLayout_Default;
2756
2757 return layout & LAYOUT_RTL ? wxLayout_RightToLeft : wxLayout_LeftToRight;
2758 }
2759
2760 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir)
2761 {
2762 typedef DWORD (WINAPI *SetLayout_t)(HDC, DWORD);
2763 static SetLayout_t
2764 wxDL_INIT_FUNC(s_pfn, SetLayout, wxDynamicLibrary(_T("gdi32.dll")));
2765 if ( !s_pfnSetLayout )
2766 return;
2767
2768 if ( dir == wxLayout_Default )
2769 {
2770 dir = wxTheApp->GetLayoutDirection();
2771 if ( dir == wxLayout_Default )
2772 return;
2773 }
2774
2775 DWORD layout = wxGetDCLayout(GetHdc());
2776 if ( dir == wxLayout_RightToLeft )
2777 layout |= LAYOUT_RTL;
2778 else
2779 layout &= ~LAYOUT_RTL;
2780
2781 s_pfnSetLayout(GetHdc(), layout);
2782 }
2783
2784 #else // !wxUSE_DYNLIB_CLASS
2785
2786 // we can't provide RTL support without dynamic loading, so stub it out
2787 wxLayoutDirection wxMSWDCImpl::GetLayoutDirection() const
2788 {
2789 return wxLayout_Default;
2790 }
2791
2792 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection WXUNUSED(dir))
2793 {
2794 }
2795
2796 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS