]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dc.cpp
try to clear the device/logical unit fog; remove unused and unsupported (an assertion...
[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;
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 default:
1748 wxFAIL_MSG( wxS("unknown logical function") );
1749 return;
1750 }
1751
1752 SetROP2(GetHdc(), rop);
1753 }
1754
1755 bool wxMSWDCImpl::StartDoc(const wxString& WXUNUSED(message))
1756 {
1757 // We might be previewing, so return true to let it continue.
1758 return true;
1759 }
1760
1761 void wxMSWDCImpl::EndDoc()
1762 {
1763 }
1764
1765 void wxMSWDCImpl::StartPage()
1766 {
1767 }
1768
1769 void wxMSWDCImpl::EndPage()
1770 {
1771 }
1772
1773 // ---------------------------------------------------------------------------
1774 // text metrics
1775 // ---------------------------------------------------------------------------
1776
1777 wxCoord wxMSWDCImpl::GetCharHeight() const
1778 {
1779 WXMICROWIN_CHECK_HDC_RET(0)
1780
1781 TEXTMETRIC lpTextMetric;
1782
1783 GetTextMetrics(GetHdc(), &lpTextMetric);
1784
1785 return lpTextMetric.tmHeight;
1786 }
1787
1788 wxCoord wxMSWDCImpl::GetCharWidth() const
1789 {
1790 WXMICROWIN_CHECK_HDC_RET(0)
1791
1792 TEXTMETRIC lpTextMetric;
1793
1794 GetTextMetrics(GetHdc(), &lpTextMetric);
1795
1796 return lpTextMetric.tmAveCharWidth;
1797 }
1798
1799 void wxMSWDCImpl::DoGetTextExtent(const wxString& string, wxCoord *x, wxCoord *y,
1800 wxCoord *descent, wxCoord *externalLeading,
1801 const wxFont *font) const
1802 {
1803 #ifdef __WXMICROWIN__
1804 if (!GetHDC())
1805 {
1806 if (x) *x = 0;
1807 if (y) *y = 0;
1808 if (descent) *descent = 0;
1809 if (externalLeading) *externalLeading = 0;
1810 return;
1811 }
1812 #endif // __WXMICROWIN__
1813
1814 HFONT hfontOld;
1815 if ( font )
1816 {
1817 wxASSERT_MSG( font->IsOk(), _T("invalid font in wxMSWDCImpl::GetTextExtent") );
1818
1819 hfontOld = (HFONT)::SelectObject(GetHdc(), GetHfontOf(*font));
1820 }
1821 else // don't change the font
1822 {
1823 hfontOld = 0;
1824 }
1825
1826 SIZE sizeRect;
1827 const size_t len = string.length();
1828 if ( !::GetTextExtentPoint32(GetHdc(), string.wx_str(), len, &sizeRect) )
1829 {
1830 wxLogLastError(_T("GetTextExtentPoint32()"));
1831 }
1832
1833 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1834 // the result computed by GetTextExtentPoint32() may be too small as it
1835 // accounts for under/overhang of the first/last character while we want
1836 // just the bounding rect for this string so adjust the width as needed
1837 // (using API not available in 2002 SDKs of WinCE)
1838 if ( len > 0 )
1839 {
1840 ABC width;
1841 const wxChar chFirst = *string.begin();
1842 if ( ::GetCharABCWidths(GetHdc(), chFirst, chFirst, &width) )
1843 {
1844 if ( width.abcA < 0 )
1845 sizeRect.cx -= width.abcA;
1846
1847 if ( len > 1 )
1848 {
1849 const wxChar chLast = *string.rbegin();
1850 ::GetCharABCWidths(GetHdc(), chLast, chLast, &width);
1851 }
1852 //else: we already have the width of the last character
1853
1854 if ( width.abcC < 0 )
1855 sizeRect.cx -= width.abcC;
1856 }
1857 //else: GetCharABCWidths() failed, not a TrueType font?
1858 }
1859 #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1860
1861 TEXTMETRIC tm;
1862 ::GetTextMetrics(GetHdc(), &tm);
1863
1864 if (x)
1865 *x = sizeRect.cx;
1866 if (y)
1867 *y = sizeRect.cy;
1868 if (descent)
1869 *descent = tm.tmDescent;
1870 if (externalLeading)
1871 *externalLeading = tm.tmExternalLeading;
1872
1873 if ( hfontOld )
1874 {
1875 ::SelectObject(GetHdc(), hfontOld);
1876 }
1877 }
1878
1879
1880 // Each element of the array will be the width of the string up to and
1881 // including the coresoponding character in text.
1882
1883 bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const
1884 {
1885 static int maxLenText = -1;
1886 static int maxWidth = -1;
1887 int fit = 0;
1888 SIZE sz = {0,0};
1889 int stlen = text.length();
1890
1891 if (maxLenText == -1)
1892 {
1893 // Win9x and WinNT+ have different limits
1894 int version = wxGetOsVersion();
1895 maxLenText = version == wxOS_WINDOWS_NT ? 65535 : 8192;
1896 maxWidth = version == wxOS_WINDOWS_NT ? INT_MAX : 32767;
1897 }
1898
1899 widths.Empty();
1900 widths.Add(0, stlen); // fill the array with zeros
1901 if (stlen == 0)
1902 return true;
1903
1904 if (!::GetTextExtentExPoint(GetHdc(),
1905 text.c_str(), // string to check
1906 wxMin(stlen, maxLenText),
1907 maxWidth,
1908 &fit, // [out] count of chars
1909 // that will fit
1910 &widths[0], // array to fill
1911 &sz))
1912 {
1913 // API failed
1914 wxLogLastError(wxT("GetTextExtentExPoint"));
1915 return false;
1916 }
1917
1918 return true;
1919 }
1920
1921 void wxMSWDCImpl::RealizeScaleAndOrigin()
1922 {
1923 // although it may seem wasteful to always use MM_ANISOTROPIC here instead
1924 // of using MM_TEXT if there is no scaling, benchmarking doesn't detect any
1925 // noticeable difference between these mapping modes
1926 #ifndef __WXWINCE__
1927 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
1928
1929 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
1930 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
1931
1932 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
1933 ::SetWindowExtEx(GetHdc(), width, height, NULL);
1934
1935 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX, m_deviceOriginY, NULL);
1936 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX, m_logicalOriginY, NULL);
1937 #endif
1938 }
1939
1940 void wxMSWDCImpl::SetMapMode(wxMappingMode mode)
1941 {
1942 WXMICROWIN_CHECK_HDC
1943
1944 m_mappingMode = mode;
1945
1946 if ( mode == wxMM_TEXT )
1947 {
1948 m_logicalScaleX =
1949 m_logicalScaleY = 1.0;
1950 }
1951 else // need to do some calculations
1952 {
1953 int pixel_width = ::GetDeviceCaps(GetHdc(), HORZRES),
1954 pixel_height = ::GetDeviceCaps(GetHdc(), VERTRES),
1955 mm_width = ::GetDeviceCaps(GetHdc(), HORZSIZE),
1956 mm_height = ::GetDeviceCaps(GetHdc(), VERTSIZE);
1957
1958 if ( (mm_width == 0) || (mm_height == 0) )
1959 {
1960 // we can't calculate mm2pixels[XY] then!
1961 return;
1962 }
1963
1964 double mm2pixelsX = (double)pixel_width / mm_width,
1965 mm2pixelsY = (double)pixel_height / mm_height;
1966
1967 switch (mode)
1968 {
1969 case wxMM_TWIPS:
1970 m_logicalScaleX = twips2mm * mm2pixelsX;
1971 m_logicalScaleY = twips2mm * mm2pixelsY;
1972 break;
1973
1974 case wxMM_POINTS:
1975 m_logicalScaleX = pt2mm * mm2pixelsX;
1976 m_logicalScaleY = pt2mm * mm2pixelsY;
1977 break;
1978
1979 case wxMM_METRIC:
1980 m_logicalScaleX = mm2pixelsX;
1981 m_logicalScaleY = mm2pixelsY;
1982 break;
1983
1984 case wxMM_LOMETRIC:
1985 m_logicalScaleX = mm2pixelsX / 10.0;
1986 m_logicalScaleY = mm2pixelsY / 10.0;
1987 break;
1988
1989 default:
1990 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1991 }
1992 }
1993
1994 ComputeScaleAndOrigin();
1995
1996 RealizeScaleAndOrigin();
1997 }
1998
1999 void wxMSWDCImpl::SetUserScale(double x, double y)
2000 {
2001 WXMICROWIN_CHECK_HDC
2002
2003 if ( x == m_userScaleX && y == m_userScaleY )
2004 return;
2005
2006 wxDCImpl::SetUserScale(x,y);
2007
2008 RealizeScaleAndOrigin();
2009 }
2010
2011 void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight,
2012 bool yBottomUp)
2013 {
2014 WXMICROWIN_CHECK_HDC
2015
2016 int signX = xLeftRight ? 1 : -1,
2017 signY = yBottomUp ? -1 : 1;
2018
2019 if (signX == m_signX && signY == m_signY)
2020 return;
2021
2022 wxDCImpl::SetAxisOrientation( xLeftRight, yBottomUp );
2023
2024 RealizeScaleAndOrigin();
2025 }
2026
2027 void wxMSWDCImpl::SetLogicalOrigin(wxCoord x, wxCoord y)
2028 {
2029 WXMICROWIN_CHECK_HDC
2030
2031 if ( x == m_logicalOriginX && y == m_logicalOriginY )
2032 return;
2033
2034 wxDCImpl::SetLogicalOrigin( x, y );
2035
2036 RealizeScaleAndOrigin();
2037 }
2038
2039 // For use by wxWidgets only, unless custom units are required.
2040 void wxMSWDCImpl::SetLogicalScale(double x, double y)
2041 {
2042 WXMICROWIN_CHECK_HDC
2043
2044 wxDCImpl::SetLogicalScale(x,y);
2045 }
2046
2047 void wxMSWDCImpl::SetDeviceOrigin(wxCoord x, wxCoord y)
2048 {
2049 WXMICROWIN_CHECK_HDC
2050
2051 if ( x == m_deviceOriginX && y == m_deviceOriginY )
2052 return;
2053
2054 wxDCImpl::SetDeviceOrigin( x, y );
2055
2056 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
2057 }
2058
2059 // ---------------------------------------------------------------------------
2060 // bit blit
2061 // ---------------------------------------------------------------------------
2062
2063 bool wxMSWDCImpl::DoBlit(wxCoord dstX, wxCoord dstY,
2064 wxCoord dstWidth, wxCoord dstHeight,
2065 wxDC *source,
2066 wxCoord srcX, wxCoord srcY,
2067 wxRasterOperationMode rop, bool useMask,
2068 wxCoord srcMaskX, wxCoord srcMaskY)
2069 {
2070 return DoStretchBlit(dstX, dstY, dstWidth, dstHeight, source, srcX, srcY, dstWidth, dstHeight, rop, useMask, srcMaskX, srcMaskY);
2071 }
2072
2073 bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest, wxCoord ydest,
2074 wxCoord dstWidth, wxCoord dstHeight,
2075 wxDC *source,
2076 wxCoord xsrc, wxCoord ysrc,
2077 wxCoord srcWidth, wxCoord srcHeight,
2078 wxRasterOperationMode rop, bool useMask,
2079 wxCoord xsrcMask, wxCoord ysrcMask)
2080 {
2081 wxCHECK_MSG( source, false, _T("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
2082
2083 WXMICROWIN_CHECK_HDC_RET(false)
2084
2085 wxMSWDCImpl *implSrc = wxDynamicCast( source->GetImpl(), wxMSWDCImpl );
2086 if ( !implSrc )
2087 {
2088 // TODO: Do we want to be able to blit from other DCs too?
2089 return false;
2090 }
2091
2092 const HDC hdcSrc = GetHdcOf(*implSrc);
2093
2094 // if either the source or destination has alpha channel, we must use
2095 // AlphaBlt() as other function don't handle it correctly
2096 const wxBitmap& bmpSrc = implSrc->GetSelectedBitmap();
2097 if ( bmpSrc.IsOk() && (bmpSrc.HasAlpha() ||
2098 (m_selectedBitmap.IsOk() && m_selectedBitmap.HasAlpha())) )
2099 {
2100 if ( AlphaBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2101 xsrc, ysrc, srcWidth, srcHeight, hdcSrc, bmpSrc) )
2102 return true;
2103 }
2104
2105 wxMask *mask = NULL;
2106 if ( useMask )
2107 {
2108 mask = bmpSrc.GetMask();
2109
2110 if ( !(bmpSrc.IsOk() && mask && mask->GetMaskBitmap()) )
2111 {
2112 // don't give assert here because this would break existing
2113 // programs - just silently ignore useMask parameter
2114 useMask = false;
2115 }
2116 }
2117
2118 if (xsrcMask == -1 && ysrcMask == -1)
2119 {
2120 xsrcMask = xsrc; ysrcMask = ysrc;
2121 }
2122
2123 wxTextColoursChanger textCol(GetHdc(), *this);
2124
2125 DWORD dwRop;
2126 switch (rop)
2127 {
2128 case wxXOR: dwRop = SRCINVERT; break;
2129 case wxINVERT: dwRop = DSTINVERT; break;
2130 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
2131 case wxAND_REVERSE: dwRop = SRCERASE; break;
2132 case wxCLEAR: dwRop = BLACKNESS; break;
2133 case wxSET: dwRop = WHITENESS; break;
2134 case wxOR_INVERT: dwRop = MERGEPAINT; break;
2135 case wxAND: dwRop = SRCAND; break;
2136 case wxOR: dwRop = SRCPAINT; break;
2137 case wxEQUIV: dwRop = 0x00990066; break;
2138 case wxNAND: dwRop = 0x007700E6; break;
2139 case wxAND_INVERT: dwRop = 0x00220326; break;
2140 case wxCOPY: dwRop = SRCCOPY; break;
2141 case wxNO_OP: dwRop = DSTCOPY; break;
2142 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
2143 case wxNOR: dwRop = NOTSRCCOPY; break;
2144 default:
2145 wxFAIL_MSG( wxT("unsupported logical function") );
2146 return false;
2147 }
2148
2149 bool success = false;
2150
2151 if (useMask)
2152 {
2153 #ifdef __WIN32__
2154 // we want the part of the image corresponding to the mask to be
2155 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2156 // meaning of fg and bg is inverted which corresponds to wxWin notion
2157 // of the mask which is also contrary to the Windows one)
2158
2159 // On some systems, MaskBlt succeeds yet is much much slower
2160 // than the wxWidgets fall-back implementation. So we need
2161 // to be able to switch this on and off at runtime.
2162 #if wxUSE_SYSTEM_OPTIONS
2163 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
2164 #endif
2165 {
2166 if ( dstWidth == srcWidth && dstHeight == srcHeight )
2167 {
2168 success = ::MaskBlt
2169 (
2170 GetHdc(),
2171 xdest, ydest, dstWidth, dstHeight,
2172 hdcSrc,
2173 xsrc, ysrc,
2174 (HBITMAP)mask->GetMaskBitmap(),
2175 xsrcMask, ysrcMask,
2176 MAKEROP4(dwRop, DSTCOPY)
2177 ) != 0;
2178 }
2179 }
2180
2181 if ( !success )
2182 #endif // Win32
2183 {
2184 // Blit bitmap with mask
2185 HDC dc_mask ;
2186 HDC dc_buffer ;
2187 HBITMAP buffer_bmap ;
2188
2189 #if wxUSE_DC_CACHEING
2190 // create a temp buffer bitmap and DCs to access it and the mask
2191 wxDCCacheEntry* dcCacheEntry1 = FindDCInCache(NULL, hdcSrc);
2192 dc_mask = (HDC) dcCacheEntry1->m_dc;
2193
2194 wxDCCacheEntry* dcCacheEntry2 = FindDCInCache(dcCacheEntry1, GetHDC());
2195 dc_buffer = (HDC) dcCacheEntry2->m_dc;
2196
2197 wxDCCacheEntry* bitmapCacheEntry = FindBitmapInCache(GetHDC(),
2198 dstWidth, dstHeight);
2199
2200 buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap;
2201 #else // !wxUSE_DC_CACHEING
2202 // create a temp buffer bitmap and DCs to access it and the mask
2203 dc_mask = ::CreateCompatibleDC(hdcSrc);
2204 dc_buffer = ::CreateCompatibleDC(GetHdc());
2205 buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), dstWidth, dstHeight);
2206 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2207 HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
2208 HGDIOBJ hOldBufferBitmap = ::SelectObject(dc_buffer, buffer_bmap);
2209
2210 // copy dest to buffer
2211 if ( !::BitBlt(dc_buffer, 0, 0, dstWidth, dstHeight,
2212 GetHdc(), xdest, ydest, SRCCOPY) )
2213 {
2214 wxLogLastError(wxT("BitBlt"));
2215 }
2216
2217 #ifndef __WXWINCE__
2218 StretchBltModeChanger changeMode(dc_buffer, COLORONCOLOR);
2219 #endif
2220
2221 // copy src to buffer using selected raster op
2222 if ( !::StretchBlt(dc_buffer, 0, 0, dstWidth, dstHeight,
2223 hdcSrc, xsrc, ysrc, srcWidth, srcHeight, dwRop) )
2224 {
2225 wxLogLastError(wxT("StretchBlt"));
2226 }
2227
2228 // set masked area in buffer to BLACK
2229 {
2230 wxTextColoursChanger textCol2(GetHdc(), *wxBLACK, *wxWHITE);
2231 if ( !::StretchBlt(dc_buffer, 0, 0, dstWidth, dstHeight,
2232 dc_mask, xsrcMask, ysrcMask,
2233 srcWidth, srcHeight, SRCAND) )
2234 {
2235 wxLogLastError(wxT("StretchBlt"));
2236 }
2237
2238 // set unmasked area in dest to BLACK
2239 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2240 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2241 if ( !::StretchBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2242 dc_mask, xsrcMask, ysrcMask,
2243 srcWidth, srcHeight, SRCAND) )
2244 {
2245 wxLogLastError(wxT("StretchBlt"));
2246 }
2247 } // restore the original text and background colours
2248
2249 // OR buffer to dest
2250 success = ::BitBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2251 dc_buffer, 0, 0, SRCPAINT) != 0;
2252 if ( !success )
2253 {
2254 wxLogLastError(wxT("BitBlt"));
2255 }
2256
2257 // tidy up temporary DCs and bitmap
2258 ::SelectObject(dc_mask, hOldMaskBitmap);
2259 ::SelectObject(dc_buffer, hOldBufferBitmap);
2260
2261 #if !wxUSE_DC_CACHEING
2262 {
2263 ::DeleteDC(dc_mask);
2264 ::DeleteDC(dc_buffer);
2265 ::DeleteObject(buffer_bmap);
2266 }
2267 #endif
2268 }
2269 }
2270 else // no mask, just BitBlt() it
2271 {
2272 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2273 // use StretchBlt() if available and finally fall back to BitBlt()
2274
2275 // FIXME: use appropriate WinCE functions
2276 #ifndef __WXWINCE__
2277 const int caps = ::GetDeviceCaps(GetHdc(), RASTERCAPS);
2278 if ( bmpSrc.IsOk() && (caps & RC_STRETCHDIB) )
2279 {
2280 DIBSECTION ds;
2281 wxZeroMemory(ds);
2282
2283 if ( ::GetObject(GetHbitmapOf(bmpSrc),
2284 sizeof(ds),
2285 &ds) == sizeof(ds) )
2286 {
2287 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2288
2289 // Figure out what co-ordinate system we're supposed to specify
2290 // ysrc in.
2291 const LONG hDIB = ds.dsBmih.biHeight;
2292 if ( hDIB > 0 )
2293 {
2294 // reflect ysrc
2295 ysrc = hDIB - (ysrc + srcHeight);
2296 }
2297
2298 if ( ::StretchDIBits(GetHdc(),
2299 xdest, ydest,
2300 dstWidth, dstHeight,
2301 xsrc, ysrc,
2302 srcWidth, srcHeight,
2303 ds.dsBm.bmBits,
2304 (LPBITMAPINFO)&ds.dsBmih,
2305 DIB_RGB_COLORS,
2306 dwRop
2307 ) == (int)GDI_ERROR )
2308 {
2309 // On Win9x this API fails most (all?) of the time, so
2310 // logging it becomes quite distracting. Since it falls
2311 // back to the code below this is not really serious, so
2312 // don't log it.
2313 //wxLogLastError(wxT("StretchDIBits"));
2314 }
2315 else
2316 {
2317 success = true;
2318 }
2319 }
2320 }
2321
2322 if ( !success && (caps & RC_STRETCHBLT) )
2323 #endif
2324 // __WXWINCE__
2325 {
2326 #ifndef __WXWINCE__
2327 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2328 #endif
2329
2330 if ( !::StretchBlt
2331 (
2332 GetHdc(),
2333 xdest, ydest, dstWidth, dstHeight,
2334 hdcSrc,
2335 xsrc, ysrc, srcWidth, srcHeight,
2336 dwRop
2337 ) )
2338 {
2339 wxLogLastError(_T("StretchBlt"));
2340 }
2341 else
2342 {
2343 success = true;
2344 }
2345 }
2346
2347 if ( !success )
2348 {
2349 if ( !::BitBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2350 hdcSrc, xsrc, ysrc, dwRop) )
2351 {
2352 wxLogLastError(_T("BitBlt"));
2353 }
2354 else
2355 {
2356 success = true;
2357 }
2358 }
2359 }
2360
2361 return success;
2362 }
2363
2364 void wxMSWDCImpl::GetDeviceSize(int *width, int *height) const
2365 {
2366 WXMICROWIN_CHECK_HDC
2367
2368 if ( width )
2369 *width = ::GetDeviceCaps(GetHdc(), HORZRES);
2370 if ( height )
2371 *height = ::GetDeviceCaps(GetHdc(), VERTRES);
2372 }
2373
2374 void wxMSWDCImpl::DoGetSizeMM(int *w, int *h) const
2375 {
2376 WXMICROWIN_CHECK_HDC
2377
2378 // if we implement it in terms of DoGetSize() instead of directly using the
2379 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2380 // will also work for wxWindowDC and wxClientDC even though their size is
2381 // not the same as the total size of the screen
2382 int wPixels, hPixels;
2383 DoGetSize(&wPixels, &hPixels);
2384
2385 if ( w )
2386 {
2387 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
2388
2389 wxCHECK_RET( wTotal, _T("0 width device?") );
2390
2391 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2392 }
2393
2394 if ( h )
2395 {
2396 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2397
2398 wxCHECK_RET( hTotal, _T("0 height device?") );
2399
2400 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2401 }
2402 }
2403
2404 wxSize wxMSWDCImpl::GetPPI() const
2405 {
2406 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2407
2408 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2409 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2410
2411 return wxSize(x, y);
2412 }
2413
2414 // ----------------------------------------------------------------------------
2415 // DC caching
2416 // ----------------------------------------------------------------------------
2417
2418 #if wxUSE_DC_CACHEING
2419
2420 /*
2421 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2422 * improve it in due course, either using arrays, or simply storing pointers to one
2423 * entry for the bitmap, and two for the DCs. -- JACS
2424 */
2425
2426 wxObjectList wxMSWDCImpl::sm_bitmapCache;
2427 wxObjectList wxMSWDCImpl::sm_dcCache;
2428
2429 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2430 {
2431 m_bitmap = hBitmap;
2432 m_dc = 0;
2433 m_width = w;
2434 m_height = h;
2435 m_depth = depth;
2436 }
2437
2438 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2439 {
2440 m_bitmap = 0;
2441 m_dc = hDC;
2442 m_width = 0;
2443 m_height = 0;
2444 m_depth = depth;
2445 }
2446
2447 wxDCCacheEntry::~wxDCCacheEntry()
2448 {
2449 if (m_bitmap)
2450 ::DeleteObject((HBITMAP) m_bitmap);
2451 if (m_dc)
2452 ::DeleteDC((HDC) m_dc);
2453 }
2454
2455 wxDCCacheEntry* wxMSWDCImpl::FindBitmapInCache(WXHDC dc, int w, int h)
2456 {
2457 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2458 wxList::compatibility_iterator node = sm_bitmapCache.GetFirst();
2459 while (node)
2460 {
2461 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2462
2463 if (entry->m_depth == depth)
2464 {
2465 if (entry->m_width < w || entry->m_height < h)
2466 {
2467 ::DeleteObject((HBITMAP) entry->m_bitmap);
2468 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2469 if ( !entry->m_bitmap)
2470 {
2471 wxLogLastError(wxT("CreateCompatibleBitmap"));
2472 }
2473 entry->m_width = w; entry->m_height = h;
2474 return entry;
2475 }
2476 return entry;
2477 }
2478
2479 node = node->GetNext();
2480 }
2481 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2482 if ( !hBitmap)
2483 {
2484 wxLogLastError(wxT("CreateCompatibleBitmap"));
2485 }
2486 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2487 AddToBitmapCache(entry);
2488 return entry;
2489 }
2490
2491 wxDCCacheEntry* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
2492 {
2493 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2494 wxList::compatibility_iterator node = sm_dcCache.GetFirst();
2495 while (node)
2496 {
2497 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2498
2499 // Don't return the same one as we already have
2500 if (!notThis || (notThis != entry))
2501 {
2502 if (entry->m_depth == depth)
2503 {
2504 return entry;
2505 }
2506 }
2507
2508 node = node->GetNext();
2509 }
2510 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2511 if ( !hDC)
2512 {
2513 wxLogLastError(wxT("CreateCompatibleDC"));
2514 }
2515 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2516 AddToDCCache(entry);
2517 return entry;
2518 }
2519
2520 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry* entry)
2521 {
2522 sm_bitmapCache.Append(entry);
2523 }
2524
2525 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry* entry)
2526 {
2527 sm_dcCache.Append(entry);
2528 }
2529
2530 void wxMSWDCImpl::ClearCache()
2531 {
2532 WX_CLEAR_LIST(wxList, sm_dcCache);
2533 WX_CLEAR_LIST(wxList, sm_bitmapCache);
2534 }
2535
2536 // Clean up cache at app exit
2537 class wxDCModule : public wxModule
2538 {
2539 public:
2540 virtual bool OnInit() { return true; }
2541 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2542
2543 private:
2544 DECLARE_DYNAMIC_CLASS(wxDCModule)
2545 };
2546
2547 IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2548
2549 #endif // wxUSE_DC_CACHEING
2550
2551 // ----------------------------------------------------------------------------
2552 // alpha channel support
2553 // ----------------------------------------------------------------------------
2554
2555 static bool AlphaBlt(HDC hdcDst,
2556 int x, int y, int dstWidth, int dstHeight,
2557 int srcX, int srcY,
2558 int srcWidth, int srcHeight,
2559 HDC hdcSrc,
2560 const wxBitmap& bmp)
2561 {
2562 wxASSERT_MSG( bmp.IsOk() && bmp.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2563 wxASSERT_MSG( hdcDst && hdcSrc, _T("AlphaBlt(): invalid HDC") );
2564
2565 // do we have AlphaBlend() and company in the headers?
2566 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2567 // yes, now try to see if we have it during run-time
2568 typedef BOOL (WINAPI *AlphaBlend_t)(HDC,int,int,int,int,
2569 HDC,int,int,int,int,
2570 BLENDFUNCTION);
2571
2572 static AlphaBlend_t
2573 pfnAlphaBlend = (AlphaBlend_t)wxMSIMG32DLL.GetSymbol(_T("AlphaBlend"));
2574 if ( pfnAlphaBlend )
2575 {
2576 BLENDFUNCTION bf;
2577 bf.BlendOp = AC_SRC_OVER;
2578 bf.BlendFlags = 0;
2579 bf.SourceConstantAlpha = 0xff;
2580 bf.AlphaFormat = AC_SRC_ALPHA;
2581
2582 if ( pfnAlphaBlend(hdcDst, x, y, dstWidth, dstHeight,
2583 hdcSrc, srcX, srcY, srcWidth, srcHeight,
2584 bf) )
2585 {
2586 // skip wxAlphaBlend() call below
2587 return true;
2588 }
2589
2590 wxLogLastError(_T("AlphaBlend"));
2591 }
2592 #else
2593 wxUnusedVar(hdcSrc);
2594 #endif // defined(AC_SRC_OVER)
2595
2596 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2597 // implementation
2598 #ifdef wxHAS_RAW_BITMAP
2599 wxAlphaBlend(hdcDst, x, y, dstWidth, dstHeight, srcX, srcY, srcWidth, srcHeight, bmp);
2600
2601 return true;
2602 #else // !wxHAS_RAW_BITMAP
2603 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2604 // alpha but at least something will be shown like this)
2605 wxUnusedVar(bmp);
2606 return false;
2607 #endif // wxHAS_RAW_BITMAP/!wxHAS_RAW_BITMAP
2608 }
2609
2610
2611 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2612 #ifdef wxHAS_RAW_BITMAP
2613
2614 static void
2615 wxAlphaBlend(HDC hdcDst, int xDst, int yDst,
2616 int dstWidth, int dstHeight,
2617 int srcX, int srcY,
2618 int srcWidth, int srcHeight,
2619 const wxBitmap& bmpSrc)
2620 {
2621 // get the destination DC pixels
2622 wxBitmap bmpDst(dstWidth, dstHeight, 32 /* force creating RGBA DIB */);
2623 MemoryHDC hdcMem;
2624 SelectInHDC select(hdcMem, GetHbitmapOf(bmpDst));
2625
2626 if ( !::BitBlt(hdcMem, 0, 0, dstWidth, dstHeight, hdcDst, xDst, yDst, SRCCOPY) )
2627 {
2628 wxLogLastError(_T("BitBlt"));
2629 }
2630
2631 // combine them with the source bitmap using alpha
2632 wxAlphaPixelData dataDst(bmpDst),
2633 dataSrc((wxBitmap &)bmpSrc);
2634
2635 wxCHECK_RET( dataDst && dataSrc,
2636 _T("failed to get raw data in wxAlphaBlend") );
2637
2638 wxAlphaPixelData::Iterator pDst(dataDst),
2639 pSrc(dataSrc);
2640
2641
2642 for ( int y = 0; y < dstHeight; y++ )
2643 {
2644 wxAlphaPixelData::Iterator pDstRowStart = pDst;
2645
2646 for ( int x = 0; x < dstWidth; x++ )
2647 {
2648 // source is point sampled, Alpha StretchBlit is ugly on Win95
2649 // (but does not impact performance)
2650 pSrc.MoveTo(dataSrc, srcX + (srcWidth*x/dstWidth), srcY + (srcHeight*y/dstHeight));
2651
2652 // note that source bitmap uses premultiplied alpha (as required by
2653 // the real AlphaBlend)
2654 const unsigned beta = 255 - pSrc.Alpha();
2655
2656 pDst.Red() = pSrc.Red() + (beta * pDst.Red() + 127) / 255;
2657 pDst.Blue() = pSrc.Blue() + (beta * pDst.Blue() + 127) / 255;
2658 pDst.Green() = pSrc.Green() + (beta * pDst.Green() + 127) / 255;
2659
2660 ++pDst;
2661 }
2662
2663 pDst = pDstRowStart;
2664 pDst.OffsetY(dataDst, 1);
2665 }
2666
2667 // and finally blit them back to the destination DC
2668 if ( !::BitBlt(hdcDst, xDst, yDst, dstWidth, dstHeight, hdcMem, 0, 0, SRCCOPY) )
2669 {
2670 wxLogLastError(_T("BitBlt"));
2671 }
2672 }
2673
2674 #endif // wxHAS_RAW_BITMAP
2675
2676 void wxMSWDCImpl::DoGradientFillLinear (const wxRect& rect,
2677 const wxColour& initialColour,
2678 const wxColour& destColour,
2679 wxDirection nDirection)
2680 {
2681 // use native function if we have compile-time support it and can load it
2682 // during run-time (linking to it statically would make the program
2683 // unusable on earlier Windows versions)
2684 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2685 typedef BOOL
2686 (WINAPI *GradientFill_t)(HDC, PTRIVERTEX, ULONG, PVOID, ULONG, ULONG);
2687 static GradientFill_t pfnGradientFill =
2688 (GradientFill_t)wxMSIMG32DLL.GetSymbol(_T("GradientFill"));
2689
2690 if ( pfnGradientFill )
2691 {
2692 GRADIENT_RECT grect;
2693 grect.UpperLeft = 0;
2694 grect.LowerRight = 1;
2695
2696 // invert colours direction if not filling from left-to-right or
2697 // top-to-bottom
2698 int firstVertex = nDirection == wxNORTH || nDirection == wxWEST ? 1 : 0;
2699
2700 // one vertex for upper left and one for upper-right
2701 TRIVERTEX vertices[2];
2702
2703 vertices[0].x = rect.GetLeft();
2704 vertices[0].y = rect.GetTop();
2705 vertices[1].x = rect.GetRight()+1;
2706 vertices[1].y = rect.GetBottom()+1;
2707
2708 vertices[firstVertex].Red = (COLOR16)(initialColour.Red() << 8);
2709 vertices[firstVertex].Green = (COLOR16)(initialColour.Green() << 8);
2710 vertices[firstVertex].Blue = (COLOR16)(initialColour.Blue() << 8);
2711 vertices[firstVertex].Alpha = 0;
2712 vertices[1 - firstVertex].Red = (COLOR16)(destColour.Red() << 8);
2713 vertices[1 - firstVertex].Green = (COLOR16)(destColour.Green() << 8);
2714 vertices[1 - firstVertex].Blue = (COLOR16)(destColour.Blue() << 8);
2715 vertices[1 - firstVertex].Alpha = 0;
2716
2717 if ( (*pfnGradientFill)
2718 (
2719 GetHdc(),
2720 vertices,
2721 WXSIZEOF(vertices),
2722 &grect,
2723 1,
2724 nDirection == wxWEST || nDirection == wxEAST
2725 ? GRADIENT_FILL_RECT_H
2726 : GRADIENT_FILL_RECT_V
2727 ) )
2728 {
2729 // skip call of the base class version below
2730 return;
2731 }
2732
2733 wxLogLastError(_T("GradientFill"));
2734 }
2735 #endif // wxUSE_DYNLIB_CLASS
2736
2737 wxDCImpl::DoGradientFillLinear(rect, initialColour, destColour, nDirection);
2738 }
2739
2740 #if wxUSE_DYNLIB_CLASS
2741
2742 static DWORD wxGetDCLayout(HDC hdc)
2743 {
2744 typedef DWORD (WINAPI *GetLayout_t)(HDC);
2745 static GetLayout_t
2746 wxDL_INIT_FUNC(s_pfn, GetLayout, wxDynamicLibrary(_T("gdi32.dll")));
2747
2748 return s_pfnGetLayout ? s_pfnGetLayout(hdc) : (DWORD)-1;
2749 }
2750
2751 wxLayoutDirection wxMSWDCImpl::GetLayoutDirection() const
2752 {
2753 DWORD layout = wxGetDCLayout(GetHdc());
2754
2755 if ( layout == (DWORD)-1 )
2756 return wxLayout_Default;
2757
2758 return layout & LAYOUT_RTL ? wxLayout_RightToLeft : wxLayout_LeftToRight;
2759 }
2760
2761 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir)
2762 {
2763 typedef DWORD (WINAPI *SetLayout_t)(HDC, DWORD);
2764 static SetLayout_t
2765 wxDL_INIT_FUNC(s_pfn, SetLayout, wxDynamicLibrary(_T("gdi32.dll")));
2766 if ( !s_pfnSetLayout )
2767 return;
2768
2769 if ( dir == wxLayout_Default )
2770 {
2771 dir = wxTheApp->GetLayoutDirection();
2772 if ( dir == wxLayout_Default )
2773 return;
2774 }
2775
2776 DWORD layout = wxGetDCLayout(GetHdc());
2777 if ( dir == wxLayout_RightToLeft )
2778 layout |= LAYOUT_RTL;
2779 else
2780 layout &= ~LAYOUT_RTL;
2781
2782 s_pfnSetLayout(GetHdc(), layout);
2783 }
2784
2785 #else // !wxUSE_DYNLIB_CLASS
2786
2787 // we can't provide RTL support without dynamic loading, so stub it out
2788 wxLayoutDirection wxMSWDCImpl::GetLayoutDirection() const
2789 {
2790 return wxLayout_Default;
2791 }
2792
2793 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection WXUNUSED(dir))
2794 {
2795 }
2796
2797 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS