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