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