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