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