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