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