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