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
79IMPLEMENT_ABSTRACT_CLASS(wxDC, wxDCBase)
80
81// ---------------------------------------------------------------------------
82// constants
83// ---------------------------------------------------------------------------
84
85static const int VIEWPORT_EXTENT = 1000;
86
87static const int MM_POINTS = 9;
88static 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
122static 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
132static 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
140static void
141wxAlphaBlend(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
156class wxColourChanger
157{
158public:
159    wxColourChanger(wxDC& dc);
160   ~wxColourChanger();
161
162private:
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
173class StretchBltModeChanger
174{
175public:
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
187    ~StretchBltModeChanger()
188    {
189#ifndef __WXWINCE__
190        if ( !::SetStretchBltMode(m_hdc, m_modeOld) )
191            wxLogLastError(_T("SetStretchBltMode"));
192#endif
193    }
194
195private:
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
205class wxOnceOnlyDLLLoader
206{
207public:
208    // ctor argument must be a literal string as we don't make a copy of it!
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
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
234private:
235    wxDynamicLibrary m_dll;
236    const wxChar *m_dllName;
237};
238
239static wxOnceOnlyDLLLoader wxGDI32DLL(_T("gdi32"));
240static wxOnceOnlyDLLLoader wxMSIMG32DLL(_T("msimg32"));
241
242// ===========================================================================
243// implementation
244// ===========================================================================
245
246// ----------------------------------------------------------------------------
247// wxColourChanger
248// ----------------------------------------------------------------------------
249
250wxColourChanger::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
287wxColourChanger::~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
304wxDC::~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.
334void 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
388void 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
401void
402wxDC::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()
420void 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
465void 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
487void wxDC::DoSetClippingRegionAsRegion(const wxRegion& region)
488{
489    SetClippingHrgn(region.GetHRGN());
490}
491
492void 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
523bool wxDC::CanDrawBitmap() const
524{
525    return true;
526}
527
528bool 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
541int wxDC::GetDepth() const
542{
543    WXMICROWIN_CHECK_HDC_RET(16)
544
545    return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL);
546}
547
548// ---------------------------------------------------------------------------
549// drawing
550// ---------------------------------------------------------------------------
551
552void 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
596bool 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
633bool 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
647void 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
663void 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)
675void 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
736void 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
763void 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
778void 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
825void
826wxDC::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
880void 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
909void 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
951void 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
985void 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
1001void 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
1100void 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
1161void 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
1177void 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
1315void 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
1329void 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
1369void 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
1444void 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
1469void wxDC::SetPalette(const wxPalette& palette)
1470{
1471    if ( palette.Ok() )
1472    {
1473        m_palette = palette;
1474        DoSelectPalette(true);
1475    }
1476}
1477
1478void 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
1502void 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
1540void 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
1578void 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
1633void 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
1645void 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
1655void wxDC::SetLogicalFunction(int function)
1656{
1657    WXMICROWIN_CHECK_HDC
1658
1659    m_logicalFunction = function;
1660
1661    SetRop(m_hDC);
1662}
1663
1664void 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
1698bool wxDC::StartDoc(const wxString& WXUNUSED(message))
1699{
1700    // We might be previewing, so return true to let it continue.
1701    return true;
1702}
1703
1704void wxDC::EndDoc()
1705{
1706}
1707
1708void wxDC::StartPage()
1709{
1710}
1711
1712void wxDC::EndPage()
1713{
1714}
1715
1716// ---------------------------------------------------------------------------
1717// text metrics
1718// ---------------------------------------------------------------------------
1719
1720wxCoord 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
1731wxCoord 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
1742void 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
1826bool 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
1867void 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
1938void 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
1951void 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
1970void 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
1985void 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
2000void 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
2017wxCoord wxDCBase::DeviceToLogicalX(wxCoord x) const
2018{
2019    return DeviceToLogicalXRel(x - m_deviceOriginX)*m_signX + m_logicalOriginX;
2020}
2021
2022wxCoord 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
2028wxCoord wxDCBase::DeviceToLogicalY(wxCoord y) const
2029{
2030    return DeviceToLogicalYRel(y - m_deviceOriginY)*m_signY + m_logicalOriginY;
2031}
2032
2033wxCoord 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
2039wxCoord wxDCBase::LogicalToDeviceX(wxCoord x) const
2040{
2041    return LogicalToDeviceXRel(x - m_logicalOriginX)*m_signX + m_deviceOriginX;
2042}
2043
2044wxCoord 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
2050wxCoord wxDCBase::LogicalToDeviceY(wxCoord y) const
2051{
2052    return LogicalToDeviceYRel(y - m_logicalOriginY)*m_signY + m_deviceOriginY;
2053}
2054
2055wxCoord 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
2065bool 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
2357void 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
2367void 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
2397wxSize 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.
2408void 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
2428wxList wxDC::sm_bitmapCache;
2429wxList wxDC::sm_dcCache;
2430
2431wxDCCacheEntry::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
2440wxDCCacheEntry::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
2449wxDCCacheEntry::~wxDCCacheEntry()
2450{
2451    if (m_bitmap)
2452        ::DeleteObject((HBITMAP) m_bitmap);
2453    if (m_dc)
2454        ::DeleteDC((HDC) m_dc);
2455}
2456
2457wxDCCacheEntry* 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
2493wxDCCacheEntry* 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
2522void wxDC::AddToBitmapCache(wxDCCacheEntry* entry)
2523{
2524    sm_bitmapCache.Append(entry);
2525}
2526
2527void wxDC::AddToDCCache(wxDCCacheEntry* entry)
2528{
2529    sm_dcCache.Append(entry);
2530}
2531
2532void 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
2539class wxDCModule : public wxModule
2540{
2541public:
2542    virtual bool OnInit() { return true; }
2543    virtual void OnExit() { wxDC::ClearCache(); }
2544
2545private:
2546    DECLARE_DYNAMIC_CLASS(wxDCModule)
2547};
2548
2549IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2550
2551#endif // wxUSE_DC_CACHEING
2552
2553// ----------------------------------------------------------------------------
2554// alpha channel support
2555// ----------------------------------------------------------------------------
2556
2557static 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
2614static void
2615wxAlphaBlend(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
2675void 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
2739static 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
2748wxLayoutDirection 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
2758void 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