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