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