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