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