added wxDC::Set/GetLayoutDirection() and implemented it for wxMSW (patch from Tim...
[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 shouldn´t 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 memDC.SelectObject(bmp);
1249
1250 Blit(x, y, width, height, &memDC, 0, 0, wxCOPY, useMask);
1251
1252 memDC.SelectObject(wxNullBitmap);
1253 }
1254 }
1255 else // no mask, just use BitBlt()
1256 {
1257 HDC cdc = GetHdc();
1258 HDC memdc = ::CreateCompatibleDC( cdc );
1259 HBITMAP hbitmap = (HBITMAP) bmp.GetHBITMAP( );
1260
1261 wxASSERT_MSG( hbitmap, wxT("bitmap is ok but HBITMAP is NULL?") );
1262
1263 COLORREF old_textground = ::GetTextColor(GetHdc());
1264 COLORREF old_background = ::GetBkColor(GetHdc());
1265 if (m_textForegroundColour.Ok())
1266 {
1267 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1268 }
1269 if (m_textBackgroundColour.Ok())
1270 {
1271 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1272 }
1273
1274 #if wxUSE_PALETTE
1275 wxPalette *pal = bmp.GetPalette();
1276 if ( pal && ::GetDeviceCaps(cdc,BITSPIXEL) <= 8 )
1277 {
1278 oldPal = ::SelectPalette(memdc, GetHpaletteOf(*pal), FALSE);
1279 ::RealizePalette(memdc);
1280 }
1281 #endif // wxUSE_PALETTE
1282
1283 HGDIOBJ hOldBitmap = ::SelectObject( memdc, hbitmap );
1284 ::BitBlt( cdc, x, y, width, height, memdc, 0, 0, SRCCOPY);
1285
1286 #if wxUSE_PALETTE
1287 if (oldPal)
1288 ::SelectPalette(memdc, oldPal, FALSE);
1289 #endif // wxUSE_PALETTE
1290
1291 ::SelectObject( memdc, hOldBitmap );
1292 ::DeleteDC( memdc );
1293
1294 ::SetTextColor(GetHdc(), old_textground);
1295 ::SetBkColor(GetHdc(), old_background);
1296 }
1297 }
1298
1299 void wxDC::DoDrawText(const wxString& text, wxCoord x, wxCoord y)
1300 {
1301 WXMICROWIN_CHECK_HDC
1302
1303 DrawAnyText(text, x, y);
1304
1305 // update the bounding box
1306 CalcBoundingBox(x, y);
1307
1308 wxCoord w, h;
1309 GetTextExtent(text, &w, &h);
1310 CalcBoundingBox(x + w, y + h);
1311 }
1312
1313 void wxDC::DrawAnyText(const wxString& text, wxCoord x, wxCoord y)
1314 {
1315 WXMICROWIN_CHECK_HDC
1316
1317 // prepare for drawing the text
1318 if ( m_textForegroundColour.Ok() )
1319 SetTextColor(GetHdc(), m_textForegroundColour.GetPixel());
1320
1321 DWORD old_background = 0;
1322 if ( m_textBackgroundColour.Ok() )
1323 {
1324 old_background = SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1325 }
1326
1327 SetBkMode(GetHdc(), m_backgroundMode == wxTRANSPARENT ? TRANSPARENT
1328 : OPAQUE);
1329
1330 #ifdef __WXWINCE__
1331 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), 0, NULL,
1332 text.c_str(), text.length(), NULL) == 0 )
1333 {
1334 wxLogLastError(wxT("TextOut"));
1335 }
1336 #else
1337 if ( ::TextOut(GetHdc(), XLOG2DEV(x), YLOG2DEV(y),
1338 text.c_str(), text.length()) == 0 )
1339 {
1340 wxLogLastError(wxT("TextOut"));
1341 }
1342 #endif
1343
1344 // restore the old parameters (text foreground colour may be left because
1345 // it never is set to anything else, but background should remain
1346 // transparent even if we just drew an opaque string)
1347 if ( m_textBackgroundColour.Ok() )
1348 (void)SetBkColor(GetHdc(), old_background);
1349
1350 SetBkMode(GetHdc(), TRANSPARENT);
1351 }
1352
1353 void wxDC::DoDrawRotatedText(const wxString& text,
1354 wxCoord x, wxCoord y,
1355 double angle)
1356 {
1357 WXMICROWIN_CHECK_HDC
1358
1359 // we test that we have some font because otherwise we should still use the
1360 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1361 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1362 // font for drawing rotated fonts unfortunately)
1363 if ( (angle == 0.0) && m_font.Ok() )
1364 {
1365 DoDrawText(text, x, y);
1366 }
1367 #ifndef __WXMICROWIN__
1368 else
1369 {
1370 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1371 // because it's not TrueType and so can't have non zero
1372 // orientation/escapement under Win9x
1373 wxFont font = m_font.Ok() ? m_font : *wxSWISS_FONT;
1374 HFONT hfont = (HFONT)font.GetResourceHandle();
1375 LOGFONT lf;
1376 if ( ::GetObject(hfont, sizeof(lf), &lf) == 0 )
1377 {
1378 wxLogLastError(wxT("GetObject(hfont)"));
1379 }
1380
1381 // GDI wants the angle in tenth of degree
1382 long angle10 = (long)(angle * 10);
1383 lf.lfEscapement = angle10;
1384 lf. lfOrientation = angle10;
1385
1386 hfont = ::CreateFontIndirect(&lf);
1387 if ( !hfont )
1388 {
1389 wxLogLastError(wxT("CreateFont"));
1390 }
1391 else
1392 {
1393 HFONT hfontOld = (HFONT)::SelectObject(GetHdc(), hfont);
1394
1395 DrawAnyText(text, x, y);
1396
1397 (void)::SelectObject(GetHdc(), hfontOld);
1398 (void)::DeleteObject(hfont);
1399 }
1400
1401 // call the bounding box by adding all four vertices of the rectangle
1402 // containing the text to it (simpler and probably not slower than
1403 // determining which of them is really topmost/leftmost/...)
1404 wxCoord w, h;
1405 GetTextExtent(text, &w, &h);
1406
1407 double rad = DegToRad(angle);
1408
1409 // "upper left" and "upper right"
1410 CalcBoundingBox(x, y);
1411 CalcBoundingBox(x + wxCoord(w*cos(rad)), y - wxCoord(w*sin(rad)));
1412
1413 // "bottom left" and "bottom right"
1414 x += (wxCoord)(h*sin(rad));
1415 y += (wxCoord)(h*cos(rad));
1416 CalcBoundingBox(x, y);
1417 CalcBoundingBox(x + wxCoord(w*cos(rad)), y - wxCoord(w*sin(rad)));
1418 }
1419 #endif
1420 }
1421
1422 // ---------------------------------------------------------------------------
1423 // set GDI objects
1424 // ---------------------------------------------------------------------------
1425
1426 #if wxUSE_PALETTE
1427
1428 void wxDC::DoSelectPalette(bool realize)
1429 {
1430 WXMICROWIN_CHECK_HDC
1431
1432 // Set the old object temporarily, in case the assignment deletes an object
1433 // that's not yet selected out.
1434 if (m_oldPalette)
1435 {
1436 ::SelectPalette(GetHdc(), (HPALETTE) m_oldPalette, FALSE);
1437 m_oldPalette = 0;
1438 }
1439
1440 if ( m_palette.Ok() )
1441 {
1442 HPALETTE oldPal = ::SelectPalette(GetHdc(),
1443 GetHpaletteOf(m_palette),
1444 false);
1445 if (!m_oldPalette)
1446 m_oldPalette = (WXHPALETTE) oldPal;
1447
1448 if (realize)
1449 ::RealizePalette(GetHdc());
1450 }
1451 }
1452
1453 void wxDC::SetPalette(const wxPalette& palette)
1454 {
1455 if ( palette.Ok() )
1456 {
1457 m_palette = palette;
1458 DoSelectPalette(true);
1459 }
1460 }
1461
1462 void wxDC::InitializePalette()
1463 {
1464 if ( wxDisplayDepth() <= 8 )
1465 {
1466 // look for any window or parent that has a custom palette. If any has
1467 // one then we need to use it in drawing operations
1468 wxWindow *win = m_canvas->GetAncestorWithCustomPalette();
1469
1470 m_hasCustomPalette = win && win->HasCustomPalette();
1471 if ( m_hasCustomPalette )
1472 {
1473 m_palette = win->GetPalette();
1474
1475 // turn on MSW translation for this palette
1476 DoSelectPalette();
1477 }
1478 }
1479 }
1480
1481 #endif // wxUSE_PALETTE
1482
1483 // SetFont/Pen/Brush() really ask to be implemented as a single template
1484 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1485
1486 void wxDC::SetFont(const wxFont& font)
1487 {
1488 WXMICROWIN_CHECK_HDC
1489
1490 if ( font == m_font )
1491 return;
1492
1493 if ( font.Ok() )
1494 {
1495 HGDIOBJ hfont = ::SelectObject(GetHdc(), GetHfontOf(font));
1496 if ( hfont == HGDI_ERROR )
1497 {
1498 wxLogLastError(_T("SelectObject(font)"));
1499 }
1500 else // selected ok
1501 {
1502 if ( !m_oldFont )
1503 m_oldFont = (WXHFONT)hfont;
1504
1505 m_font = font;
1506 }
1507 }
1508 else // invalid font, reset the current font
1509 {
1510 if ( m_oldFont )
1511 {
1512 if ( ::SelectObject(GetHdc(), (HPEN) m_oldFont) == HGDI_ERROR )
1513 {
1514 wxLogLastError(_T("SelectObject(old font)"));
1515 }
1516
1517 m_oldFont = 0;
1518 }
1519
1520 m_font = wxNullFont;
1521 }
1522 }
1523
1524 void wxDC::SetPen(const wxPen& pen)
1525 {
1526 WXMICROWIN_CHECK_HDC
1527
1528 if ( pen == m_pen )
1529 return;
1530
1531 if ( pen.Ok() )
1532 {
1533 HGDIOBJ hpen = ::SelectObject(GetHdc(), GetHpenOf(pen));
1534 if ( hpen == HGDI_ERROR )
1535 {
1536 wxLogLastError(_T("SelectObject(pen)"));
1537 }
1538 else // selected ok
1539 {
1540 if ( !m_oldPen )
1541 m_oldPen = (WXHPEN)hpen;
1542
1543 m_pen = pen;
1544 }
1545 }
1546 else // invalid pen, reset the current pen
1547 {
1548 if ( m_oldPen )
1549 {
1550 if ( ::SelectObject(GetHdc(), (HPEN) m_oldPen) == HGDI_ERROR )
1551 {
1552 wxLogLastError(_T("SelectObject(old pen)"));
1553 }
1554
1555 m_oldPen = 0;
1556 }
1557
1558 m_pen = wxNullPen;
1559 }
1560 }
1561
1562 void wxDC::SetBrush(const wxBrush& brush)
1563 {
1564 WXMICROWIN_CHECK_HDC
1565
1566 if ( brush == m_brush )
1567 return;
1568
1569 if ( brush.Ok() )
1570 {
1571 // we must make sure the brush is aligned with the logical coordinates
1572 // before selecting it
1573 wxBitmap *stipple = brush.GetStipple();
1574 if ( stipple && stipple->Ok() )
1575 {
1576 if ( !::SetBrushOrgEx
1577 (
1578 GetHdc(),
1579 m_deviceOriginX % stipple->GetWidth(),
1580 m_deviceOriginY % stipple->GetHeight(),
1581 NULL // [out] previous brush origin
1582 ) )
1583 {
1584 wxLogLastError(_T("SetBrushOrgEx()"));
1585 }
1586 }
1587
1588 HGDIOBJ hbrush = ::SelectObject(GetHdc(), GetHbrushOf(brush));
1589 if ( hbrush == HGDI_ERROR )
1590 {
1591 wxLogLastError(_T("SelectObject(brush)"));
1592 }
1593 else // selected ok
1594 {
1595 if ( !m_oldBrush )
1596 m_oldBrush = (WXHBRUSH)hbrush;
1597
1598 m_brush = brush;
1599 }
1600 }
1601 else // invalid brush, reset the current brush
1602 {
1603 if ( m_oldBrush )
1604 {
1605 if ( ::SelectObject(GetHdc(), (HPEN) m_oldBrush) == HGDI_ERROR )
1606 {
1607 wxLogLastError(_T("SelectObject(old brush)"));
1608 }
1609
1610 m_oldBrush = 0;
1611 }
1612
1613 m_brush = wxNullBrush;
1614 }
1615 }
1616
1617 void wxDC::SetBackground(const wxBrush& brush)
1618 {
1619 WXMICROWIN_CHECK_HDC
1620
1621 m_backgroundBrush = brush;
1622
1623 if ( m_backgroundBrush.Ok() )
1624 {
1625 (void)SetBkColor(GetHdc(), m_backgroundBrush.GetColour().GetPixel());
1626 }
1627 }
1628
1629 void wxDC::SetBackgroundMode(int mode)
1630 {
1631 WXMICROWIN_CHECK_HDC
1632
1633 m_backgroundMode = mode;
1634
1635 // SetBackgroundColour now only refers to text background
1636 // and m_backgroundMode is used there
1637 }
1638
1639 void wxDC::SetLogicalFunction(int function)
1640 {
1641 WXMICROWIN_CHECK_HDC
1642
1643 m_logicalFunction = function;
1644
1645 SetRop(m_hDC);
1646 }
1647
1648 void wxDC::SetRop(WXHDC dc)
1649 {
1650 if ( !dc || m_logicalFunction < 0 )
1651 return;
1652
1653 int rop;
1654
1655 switch (m_logicalFunction)
1656 {
1657 case wxCLEAR: rop = R2_BLACK; break;
1658 case wxXOR: rop = R2_XORPEN; break;
1659 case wxINVERT: rop = R2_NOT; break;
1660 case wxOR_REVERSE: rop = R2_MERGEPENNOT; break;
1661 case wxAND_REVERSE: rop = R2_MASKPENNOT; break;
1662 case wxCOPY: rop = R2_COPYPEN; break;
1663 case wxAND: rop = R2_MASKPEN; break;
1664 case wxAND_INVERT: rop = R2_MASKNOTPEN; break;
1665 case wxNO_OP: rop = R2_NOP; break;
1666 case wxNOR: rop = R2_NOTMERGEPEN; break;
1667 case wxEQUIV: rop = R2_NOTXORPEN; break;
1668 case wxSRC_INVERT: rop = R2_NOTCOPYPEN; break;
1669 case wxOR_INVERT: rop = R2_MERGENOTPEN; break;
1670 case wxNAND: rop = R2_NOTMASKPEN; break;
1671 case wxOR: rop = R2_MERGEPEN; break;
1672 case wxSET: rop = R2_WHITE; break;
1673
1674 default:
1675 wxFAIL_MSG( wxT("unsupported logical function") );
1676 return;
1677 }
1678
1679 SetROP2(GetHdc(), rop);
1680 }
1681
1682 bool wxDC::StartDoc(const wxString& WXUNUSED(message))
1683 {
1684 // We might be previewing, so return true to let it continue.
1685 return true;
1686 }
1687
1688 void wxDC::EndDoc()
1689 {
1690 }
1691
1692 void wxDC::StartPage()
1693 {
1694 }
1695
1696 void wxDC::EndPage()
1697 {
1698 }
1699
1700 // ---------------------------------------------------------------------------
1701 // text metrics
1702 // ---------------------------------------------------------------------------
1703
1704 wxCoord wxDC::GetCharHeight() const
1705 {
1706 WXMICROWIN_CHECK_HDC_RET(0)
1707
1708 TEXTMETRIC lpTextMetric;
1709
1710 GetTextMetrics(GetHdc(), &lpTextMetric);
1711
1712 return lpTextMetric.tmHeight;
1713 }
1714
1715 wxCoord wxDC::GetCharWidth() const
1716 {
1717 WXMICROWIN_CHECK_HDC_RET(0)
1718
1719 TEXTMETRIC lpTextMetric;
1720
1721 GetTextMetrics(GetHdc(), &lpTextMetric);
1722
1723 return lpTextMetric.tmAveCharWidth;
1724 }
1725
1726 void wxDC::DoGetTextExtent(const wxString& string, wxCoord *x, wxCoord *y,
1727 wxCoord *descent, wxCoord *externalLeading,
1728 wxFont *font) const
1729 {
1730 #ifdef __WXMICROWIN__
1731 if (!GetHDC())
1732 {
1733 if (x) *x = 0;
1734 if (y) *y = 0;
1735 if (descent) *descent = 0;
1736 if (externalLeading) *externalLeading = 0;
1737 return;
1738 }
1739 #endif // __WXMICROWIN__
1740
1741 HFONT hfontOld;
1742 if ( font )
1743 {
1744 wxASSERT_MSG( font->Ok(), _T("invalid font in wxDC::GetTextExtent") );
1745
1746 hfontOld = (HFONT)::SelectObject(GetHdc(), GetHfontOf(*font));
1747 }
1748 else // don't change the font
1749 {
1750 hfontOld = 0;
1751 }
1752
1753 SIZE sizeRect;
1754 const size_t len = string.length();
1755 if ( !::GetTextExtentPoint32(GetHdc(), string, len, &sizeRect) )
1756 {
1757 wxLogLastError(_T("GetTextExtentPoint32()"));
1758 }
1759
1760 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1761 // the result computed by GetTextExtentPoint32() may be too small as it
1762 // accounts for under/overhang of the first/last character while we want
1763 // just the bounding rect for this string so adjust the width as needed
1764 // (using API not available in 2002 SDKs of WinCE)
1765 if ( len > 0 )
1766 {
1767 ABC width;
1768 const wxChar chFirst = *string.begin();
1769 if ( ::GetCharABCWidths(GetHdc(), chFirst, chFirst, &width) )
1770 {
1771 if ( width.abcA < 0 )
1772 sizeRect.cx -= width.abcA;
1773
1774 if ( len > 1 )
1775 {
1776 const wxChar chLast = *string.rbegin();
1777 ::GetCharABCWidths(GetHdc(), chLast, chLast, &width);
1778 }
1779 //else: we already have the width of the last character
1780
1781 if ( width.abcC < 0 )
1782 sizeRect.cx -= width.abcC;
1783 }
1784 //else: GetCharABCWidths() failed, not a TrueType font?
1785 }
1786 #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1787
1788 TEXTMETRIC tm;
1789 ::GetTextMetrics(GetHdc(), &tm);
1790
1791 if (x)
1792 *x = sizeRect.cx;
1793 if (y)
1794 *y = sizeRect.cy;
1795 if (descent)
1796 *descent = tm.tmDescent;
1797 if (externalLeading)
1798 *externalLeading = tm.tmExternalLeading;
1799
1800 if ( hfontOld )
1801 {
1802 ::SelectObject(GetHdc(), hfontOld);
1803 }
1804 }
1805
1806
1807 // Each element of the array will be the width of the string up to and
1808 // including the coresoponding character in text.
1809
1810 bool wxDC::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const
1811 {
1812 static int maxLenText = -1;
1813 static int maxWidth = -1;
1814 int fit = 0;
1815 SIZE sz = {0,0};
1816 int stlen = text.length();
1817
1818 if (maxLenText == -1)
1819 {
1820 // Win9x and WinNT+ have different limits
1821 int version = wxGetOsVersion();
1822 maxLenText = version == wxOS_WINDOWS_NT ? 65535 : 8192;
1823 maxWidth = version == wxOS_WINDOWS_NT ? INT_MAX : 32767;
1824 }
1825
1826 widths.Empty();
1827 widths.Add(0, stlen); // fill the array with zeros
1828 if (stlen == 0)
1829 return true;
1830
1831 if (!::GetTextExtentExPoint(GetHdc(),
1832 text.c_str(), // string to check
1833 wxMin(stlen, maxLenText),
1834 maxWidth,
1835 &fit, // [out] count of chars
1836 // that will fit
1837 &widths[0], // array to fill
1838 &sz))
1839 {
1840 // API failed
1841 wxLogLastError(wxT("GetTextExtentExPoint"));
1842 return false;
1843 }
1844
1845 return true;
1846 }
1847
1848
1849
1850
1851 void wxDC::SetMapMode(int mode)
1852 {
1853 WXMICROWIN_CHECK_HDC
1854
1855 m_mappingMode = mode;
1856
1857 if ( mode == wxMM_TEXT )
1858 {
1859 m_logicalScaleX =
1860 m_logicalScaleY = 1.0;
1861 }
1862 else // need to do some calculations
1863 {
1864 int pixel_width = ::GetDeviceCaps(GetHdc(), HORZRES),
1865 pixel_height = ::GetDeviceCaps(GetHdc(), VERTRES),
1866 mm_width = ::GetDeviceCaps(GetHdc(), HORZSIZE),
1867 mm_height = ::GetDeviceCaps(GetHdc(), VERTSIZE);
1868
1869 if ( (mm_width == 0) || (mm_height == 0) )
1870 {
1871 // we can't calculate mm2pixels[XY] then!
1872 return;
1873 }
1874
1875 double mm2pixelsX = (double)pixel_width / mm_width,
1876 mm2pixelsY = (double)pixel_height / mm_height;
1877
1878 switch (mode)
1879 {
1880 case wxMM_TWIPS:
1881 m_logicalScaleX = twips2mm * mm2pixelsX;
1882 m_logicalScaleY = twips2mm * mm2pixelsY;
1883 break;
1884
1885 case wxMM_POINTS:
1886 m_logicalScaleX = pt2mm * mm2pixelsX;
1887 m_logicalScaleY = pt2mm * mm2pixelsY;
1888 break;
1889
1890 case wxMM_METRIC:
1891 m_logicalScaleX = mm2pixelsX;
1892 m_logicalScaleY = mm2pixelsY;
1893 break;
1894
1895 case wxMM_LOMETRIC:
1896 m_logicalScaleX = mm2pixelsX / 10.0;
1897 m_logicalScaleY = mm2pixelsY / 10.0;
1898 break;
1899
1900 default:
1901 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1902 }
1903 }
1904
1905 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1906 // cases we could do with MM_TEXT and in the remaining 0.9% with
1907 // MM_ISOTROPIC (TODO!)
1908 #ifndef __WXWINCE__
1909 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
1910
1911 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
1912 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
1913
1914 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
1915 ::SetWindowExtEx(GetHdc(), width, height, NULL);
1916
1917 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX, m_deviceOriginY, NULL);
1918 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX, m_logicalOriginY, NULL);
1919 #endif
1920 }
1921
1922 void wxDC::SetUserScale(double x, double y)
1923 {
1924 WXMICROWIN_CHECK_HDC
1925
1926 if ( x == m_userScaleX && y == m_userScaleY )
1927 return;
1928
1929 m_userScaleX = x;
1930 m_userScaleY = y;
1931
1932 this->SetMapMode(m_mappingMode);
1933 }
1934
1935 void wxDC::SetAxisOrientation(bool WXUNUSED_IN_WINCE(xLeftRight),
1936 bool WXUNUSED_IN_WINCE(yBottomUp))
1937 {
1938 WXMICROWIN_CHECK_HDC
1939
1940 #ifndef __WXWINCE__
1941 int signX = xLeftRight ? 1 : -1,
1942 signY = yBottomUp ? -1 : 1;
1943
1944 if ( signX != m_signX || signY != m_signY )
1945 {
1946 m_signX = signX;
1947 m_signY = signY;
1948
1949 SetMapMode(m_mappingMode);
1950 }
1951 #endif
1952 }
1953
1954 void wxDC::SetSystemScale(double x, double y)
1955 {
1956 WXMICROWIN_CHECK_HDC
1957
1958 if ( x == m_scaleX && y == m_scaleY )
1959 return;
1960
1961 m_scaleX = x;
1962 m_scaleY = y;
1963
1964 #ifndef __WXWINCE__
1965 SetMapMode(m_mappingMode);
1966 #endif
1967 }
1968
1969 void wxDC::SetLogicalOrigin(wxCoord x, wxCoord y)
1970 {
1971 WXMICROWIN_CHECK_HDC
1972
1973 if ( x == m_logicalOriginX && y == m_logicalOriginY )
1974 return;
1975
1976 m_logicalOriginX = x;
1977 m_logicalOriginY = y;
1978
1979 #ifndef __WXWINCE__
1980 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
1981 #endif
1982 }
1983
1984 void wxDC::SetDeviceOrigin(wxCoord x, wxCoord y)
1985 {
1986 WXMICROWIN_CHECK_HDC
1987
1988 if ( x == m_deviceOriginX && y == m_deviceOriginY )
1989 return;
1990
1991 m_deviceOriginX = x;
1992 m_deviceOriginY = y;
1993
1994 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
1995 }
1996
1997 // ---------------------------------------------------------------------------
1998 // coordinates transformations
1999 // ---------------------------------------------------------------------------
2000
2001 wxCoord wxDCBase::DeviceToLogicalX(wxCoord x) const
2002 {
2003 return DeviceToLogicalXRel(x - m_deviceOriginX)*m_signX + m_logicalOriginX;
2004 }
2005
2006 wxCoord wxDCBase::DeviceToLogicalXRel(wxCoord x) const
2007 {
2008 // axis orientation is not taken into account for conversion of a distance
2009 return (wxCoord)(x / (m_logicalScaleX*m_userScaleX*m_scaleX));
2010 }
2011
2012 wxCoord wxDCBase::DeviceToLogicalY(wxCoord y) const
2013 {
2014 return DeviceToLogicalYRel(y - m_deviceOriginY)*m_signY + m_logicalOriginY;
2015 }
2016
2017 wxCoord wxDCBase::DeviceToLogicalYRel(wxCoord y) const
2018 {
2019 // axis orientation is not taken into account for conversion of a distance
2020 return (wxCoord)( y / (m_logicalScaleY*m_userScaleY*m_scaleY));
2021 }
2022
2023 wxCoord wxDCBase::LogicalToDeviceX(wxCoord x) const
2024 {
2025 return LogicalToDeviceXRel(x - m_logicalOriginX)*m_signX + m_deviceOriginX;
2026 }
2027
2028 wxCoord wxDCBase::LogicalToDeviceXRel(wxCoord x) const
2029 {
2030 // axis orientation is not taken into account for conversion of a distance
2031 return (wxCoord) (x*m_logicalScaleX*m_userScaleX*m_scaleX);
2032 }
2033
2034 wxCoord wxDCBase::LogicalToDeviceY(wxCoord y) const
2035 {
2036 return LogicalToDeviceYRel(y - m_logicalOriginY)*m_signY + m_deviceOriginY;
2037 }
2038
2039 wxCoord wxDCBase::LogicalToDeviceYRel(wxCoord y) const
2040 {
2041 // axis orientation is not taken into account for conversion of a distance
2042 return (wxCoord) (y*m_logicalScaleY*m_userScaleY*m_scaleY);
2043 }
2044
2045 // ---------------------------------------------------------------------------
2046 // bit blit
2047 // ---------------------------------------------------------------------------
2048
2049 bool wxDC::DoBlit(wxCoord xdest, wxCoord ydest,
2050 wxCoord width, wxCoord height,
2051 wxDC *source, wxCoord xsrc, wxCoord ysrc,
2052 int rop, bool useMask,
2053 wxCoord xsrcMask, wxCoord ysrcMask)
2054 {
2055 wxCHECK_MSG( source, false, _T("wxDC::Blit(): NULL wxDC pointer") );
2056
2057 WXMICROWIN_CHECK_HDC_RET(false)
2058
2059 // if either the source or destination has alpha channel, we must use
2060 // AlphaBlt() as other function don't handle it correctly
2061 const wxBitmap& bmpSrc = source->m_selectedBitmap;
2062 if ( bmpSrc.Ok() && (bmpSrc.HasAlpha() ||
2063 (m_selectedBitmap.Ok() && m_selectedBitmap.HasAlpha())) )
2064 {
2065 if ( AlphaBlt(GetHdc(), xdest, ydest, width, height,
2066 xsrc, ysrc, GetHdcOf(*source), bmpSrc) )
2067 return true;
2068 }
2069
2070 wxMask *mask = NULL;
2071 if ( useMask )
2072 {
2073 mask = bmpSrc.GetMask();
2074
2075 if ( !(bmpSrc.Ok() && mask && mask->GetMaskBitmap()) )
2076 {
2077 // don't give assert here because this would break existing
2078 // programs - just silently ignore useMask parameter
2079 useMask = false;
2080 }
2081 }
2082
2083 if (xsrcMask == -1 && ysrcMask == -1)
2084 {
2085 xsrcMask = xsrc; ysrcMask = ysrc;
2086 }
2087
2088 COLORREF old_textground = ::GetTextColor(GetHdc());
2089 COLORREF old_background = ::GetBkColor(GetHdc());
2090 if (m_textForegroundColour.Ok())
2091 {
2092 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
2093 }
2094 if (m_textBackgroundColour.Ok())
2095 {
2096 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
2097 }
2098
2099 DWORD dwRop;
2100 switch (rop)
2101 {
2102 case wxXOR: dwRop = SRCINVERT; break;
2103 case wxINVERT: dwRop = DSTINVERT; break;
2104 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
2105 case wxAND_REVERSE: dwRop = SRCERASE; break;
2106 case wxCLEAR: dwRop = BLACKNESS; break;
2107 case wxSET: dwRop = WHITENESS; break;
2108 case wxOR_INVERT: dwRop = MERGEPAINT; break;
2109 case wxAND: dwRop = SRCAND; break;
2110 case wxOR: dwRop = SRCPAINT; break;
2111 case wxEQUIV: dwRop = 0x00990066; break;
2112 case wxNAND: dwRop = 0x007700E6; break;
2113 case wxAND_INVERT: dwRop = 0x00220326; break;
2114 case wxCOPY: dwRop = SRCCOPY; break;
2115 case wxNO_OP: dwRop = DSTCOPY; break;
2116 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
2117 case wxNOR: dwRop = NOTSRCCOPY; break;
2118 default:
2119 wxFAIL_MSG( wxT("unsupported logical function") );
2120 return false;
2121 }
2122
2123 bool success = false;
2124
2125 if (useMask)
2126 {
2127 #ifdef __WIN32__
2128 // we want the part of the image corresponding to the mask to be
2129 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2130 // meaning of fg and bg is inverted which corresponds to wxWin notion
2131 // of the mask which is also contrary to the Windows one)
2132
2133 // On some systems, MaskBlt succeeds yet is much much slower
2134 // than the wxWidgets fall-back implementation. So we need
2135 // to be able to switch this on and off at runtime.
2136 #if wxUSE_SYSTEM_OPTIONS
2137 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
2138 #endif
2139 {
2140 success = ::MaskBlt
2141 (
2142 GetHdc(),
2143 xdest, ydest, width, height,
2144 GetHdcOf(*source),
2145 xsrc, ysrc,
2146 (HBITMAP)mask->GetMaskBitmap(),
2147 xsrcMask, ysrcMask,
2148 MAKEROP4(dwRop, DSTCOPY)
2149 ) != 0;
2150 }
2151
2152 if ( !success )
2153 #endif // Win32
2154 {
2155 // Blit bitmap with mask
2156 HDC dc_mask ;
2157 HDC dc_buffer ;
2158 HBITMAP buffer_bmap ;
2159
2160 #if wxUSE_DC_CACHEING
2161 // create a temp buffer bitmap and DCs to access it and the mask
2162 wxDCCacheEntry* dcCacheEntry1 = FindDCInCache(NULL, source->GetHDC());
2163 dc_mask = (HDC) dcCacheEntry1->m_dc;
2164
2165 wxDCCacheEntry* dcCacheEntry2 = FindDCInCache(dcCacheEntry1, GetHDC());
2166 dc_buffer = (HDC) dcCacheEntry2->m_dc;
2167
2168 wxDCCacheEntry* bitmapCacheEntry = FindBitmapInCache(GetHDC(),
2169 width, height);
2170
2171 buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap;
2172 #else // !wxUSE_DC_CACHEING
2173 // create a temp buffer bitmap and DCs to access it and the mask
2174 dc_mask = ::CreateCompatibleDC(GetHdcOf(*source));
2175 dc_buffer = ::CreateCompatibleDC(GetHdc());
2176 buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), width, height);
2177 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2178 HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
2179 HGDIOBJ hOldBufferBitmap = ::SelectObject(dc_buffer, buffer_bmap);
2180
2181 // copy dest to buffer
2182 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
2183 GetHdc(), xdest, ydest, SRCCOPY) )
2184 {
2185 wxLogLastError(wxT("BitBlt"));
2186 }
2187
2188 // copy src to buffer using selected raster op
2189 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
2190 GetHdcOf(*source), xsrc, ysrc, dwRop) )
2191 {
2192 wxLogLastError(wxT("BitBlt"));
2193 }
2194
2195 // set masked area in buffer to BLACK (pixel value 0)
2196 COLORREF prevBkCol = ::SetBkColor(GetHdc(), RGB(255, 255, 255));
2197 COLORREF prevCol = ::SetTextColor(GetHdc(), RGB(0, 0, 0));
2198 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
2199 dc_mask, xsrcMask, ysrcMask, SRCAND) )
2200 {
2201 wxLogLastError(wxT("BitBlt"));
2202 }
2203
2204 // set unmasked area in dest to BLACK
2205 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2206 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2207 if ( !::BitBlt(GetHdc(), xdest, ydest, (int)width, (int)height,
2208 dc_mask, xsrcMask, ysrcMask, SRCAND) )
2209 {
2210 wxLogLastError(wxT("BitBlt"));
2211 }
2212 ::SetBkColor(GetHdc(), prevBkCol); // restore colours to original values
2213 ::SetTextColor(GetHdc(), prevCol);
2214
2215 // OR buffer to dest
2216 success = ::BitBlt(GetHdc(), xdest, ydest,
2217 (int)width, (int)height,
2218 dc_buffer, 0, 0, SRCPAINT) != 0;
2219 if ( !success )
2220 {
2221 wxLogLastError(wxT("BitBlt"));
2222 }
2223
2224 // tidy up temporary DCs and bitmap
2225 ::SelectObject(dc_mask, hOldMaskBitmap);
2226 ::SelectObject(dc_buffer, hOldBufferBitmap);
2227
2228 #if !wxUSE_DC_CACHEING
2229 {
2230 ::DeleteDC(dc_mask);
2231 ::DeleteDC(dc_buffer);
2232 ::DeleteObject(buffer_bmap);
2233 }
2234 #endif
2235 }
2236 }
2237 else // no mask, just BitBlt() it
2238 {
2239 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2240 // use StretchBlt() if available and finally fall back to BitBlt()
2241
2242 // FIXME: use appropriate WinCE functions
2243 #ifndef __WXWINCE__
2244 const int caps = ::GetDeviceCaps(GetHdc(), RASTERCAPS);
2245 if ( bmpSrc.Ok() && (caps & RC_STRETCHDIB) )
2246 {
2247 DIBSECTION ds;
2248 wxZeroMemory(ds);
2249
2250 if ( ::GetObject(GetHbitmapOf(bmpSrc),
2251 sizeof(ds),
2252 &ds) == sizeof(ds) )
2253 {
2254 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2255
2256 // Figure out what co-ordinate system we're supposed to specify
2257 // ysrc in.
2258 const LONG hDIB = ds.dsBmih.biHeight;
2259 if ( hDIB > 0 )
2260 {
2261 // reflect ysrc
2262 ysrc = hDIB - (ysrc + height);
2263 }
2264
2265 if ( ::StretchDIBits(GetHdc(),
2266 xdest, ydest,
2267 width, height,
2268 xsrc, ysrc,
2269 width, height,
2270 ds.dsBm.bmBits,
2271 (LPBITMAPINFO)&ds.dsBmih,
2272 DIB_RGB_COLORS,
2273 dwRop
2274 ) == (int)GDI_ERROR )
2275 {
2276 // On Win9x this API fails most (all?) of the time, so
2277 // logging it becomes quite distracting. Since it falls
2278 // back to the code below this is not really serious, so
2279 // don't log it.
2280 //wxLogLastError(wxT("StretchDIBits"));
2281 }
2282 else
2283 {
2284 success = true;
2285 }
2286 }
2287 }
2288
2289 if ( !success && (caps & RC_STRETCHBLT) )
2290 #endif
2291 // __WXWINCE__
2292 {
2293 #ifndef __WXWINCE__
2294 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2295 #endif
2296
2297 if ( !::StretchBlt
2298 (
2299 GetHdc(),
2300 xdest, ydest, width, height,
2301 GetHdcOf(*source),
2302 xsrc, ysrc, width, height,
2303 dwRop
2304 ) )
2305 {
2306 wxLogLastError(_T("StretchBlt"));
2307 }
2308 else
2309 {
2310 success = true;
2311 }
2312 }
2313
2314 if ( !success )
2315 {
2316 if ( !::BitBlt
2317 (
2318 GetHdc(),
2319 xdest, ydest,
2320 (int)width, (int)height,
2321 GetHdcOf(*source),
2322 xsrc, ysrc,
2323 dwRop
2324 ) )
2325 {
2326 wxLogLastError(_T("BitBlt"));
2327 }
2328 else
2329 {
2330 success = true;
2331 }
2332 }
2333 }
2334
2335 ::SetTextColor(GetHdc(), old_textground);
2336 ::SetBkColor(GetHdc(), old_background);
2337
2338 return success;
2339 }
2340
2341 void wxDC::GetDeviceSize(int *width, int *height) const
2342 {
2343 WXMICROWIN_CHECK_HDC
2344
2345 if ( width )
2346 *width = ::GetDeviceCaps(GetHdc(), HORZRES);
2347 if ( height )
2348 *height = ::GetDeviceCaps(GetHdc(), VERTRES);
2349 }
2350
2351 void wxDC::DoGetSizeMM(int *w, int *h) const
2352 {
2353 WXMICROWIN_CHECK_HDC
2354
2355 // if we implement it in terms of DoGetSize() instead of directly using the
2356 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2357 // will also work for wxWindowDC and wxClientDC even though their size is
2358 // not the same as the total size of the screen
2359 int wPixels, hPixels;
2360 DoGetSize(&wPixels, &hPixels);
2361
2362 if ( w )
2363 {
2364 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
2365
2366 wxCHECK_RET( wTotal, _T("0 width device?") );
2367
2368 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2369 }
2370
2371 if ( h )
2372 {
2373 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2374
2375 wxCHECK_RET( hTotal, _T("0 height device?") );
2376
2377 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2378 }
2379 }
2380
2381 wxSize wxDC::GetPPI() const
2382 {
2383 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2384
2385 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2386 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2387
2388 return wxSize(x, y);
2389 }
2390
2391 // For use by wxWidgets only, unless custom units are required.
2392 void wxDC::SetLogicalScale(double x, double y)
2393 {
2394 WXMICROWIN_CHECK_HDC
2395
2396 m_logicalScaleX = x;
2397 m_logicalScaleY = y;
2398 }
2399
2400 // ----------------------------------------------------------------------------
2401 // DC caching
2402 // ----------------------------------------------------------------------------
2403
2404 #if wxUSE_DC_CACHEING
2405
2406 /*
2407 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2408 * improve it in due course, either using arrays, or simply storing pointers to one
2409 * entry for the bitmap, and two for the DCs. -- JACS
2410 */
2411
2412 wxList wxDC::sm_bitmapCache;
2413 wxList wxDC::sm_dcCache;
2414
2415 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2416 {
2417 m_bitmap = hBitmap;
2418 m_dc = 0;
2419 m_width = w;
2420 m_height = h;
2421 m_depth = depth;
2422 }
2423
2424 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2425 {
2426 m_bitmap = 0;
2427 m_dc = hDC;
2428 m_width = 0;
2429 m_height = 0;
2430 m_depth = depth;
2431 }
2432
2433 wxDCCacheEntry::~wxDCCacheEntry()
2434 {
2435 if (m_bitmap)
2436 ::DeleteObject((HBITMAP) m_bitmap);
2437 if (m_dc)
2438 ::DeleteDC((HDC) m_dc);
2439 }
2440
2441 wxDCCacheEntry* wxDC::FindBitmapInCache(WXHDC dc, int w, int h)
2442 {
2443 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2444 wxList::compatibility_iterator node = sm_bitmapCache.GetFirst();
2445 while (node)
2446 {
2447 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2448
2449 if (entry->m_depth == depth)
2450 {
2451 if (entry->m_width < w || entry->m_height < h)
2452 {
2453 ::DeleteObject((HBITMAP) entry->m_bitmap);
2454 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2455 if ( !entry->m_bitmap)
2456 {
2457 wxLogLastError(wxT("CreateCompatibleBitmap"));
2458 }
2459 entry->m_width = w; entry->m_height = h;
2460 return entry;
2461 }
2462 return entry;
2463 }
2464
2465 node = node->GetNext();
2466 }
2467 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2468 if ( !hBitmap)
2469 {
2470 wxLogLastError(wxT("CreateCompatibleBitmap"));
2471 }
2472 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2473 AddToBitmapCache(entry);
2474 return entry;
2475 }
2476
2477 wxDCCacheEntry* wxDC::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
2478 {
2479 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2480 wxList::compatibility_iterator node = sm_dcCache.GetFirst();
2481 while (node)
2482 {
2483 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2484
2485 // Don't return the same one as we already have
2486 if (!notThis || (notThis != entry))
2487 {
2488 if (entry->m_depth == depth)
2489 {
2490 return entry;
2491 }
2492 }
2493
2494 node = node->GetNext();
2495 }
2496 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2497 if ( !hDC)
2498 {
2499 wxLogLastError(wxT("CreateCompatibleDC"));
2500 }
2501 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2502 AddToDCCache(entry);
2503 return entry;
2504 }
2505
2506 void wxDC::AddToBitmapCache(wxDCCacheEntry* entry)
2507 {
2508 sm_bitmapCache.Append(entry);
2509 }
2510
2511 void wxDC::AddToDCCache(wxDCCacheEntry* entry)
2512 {
2513 sm_dcCache.Append(entry);
2514 }
2515
2516 void wxDC::ClearCache()
2517 {
2518 WX_CLEAR_LIST(wxList, sm_dcCache);
2519 WX_CLEAR_LIST(wxList, sm_bitmapCache);
2520 }
2521
2522 // Clean up cache at app exit
2523 class wxDCModule : public wxModule
2524 {
2525 public:
2526 virtual bool OnInit() { return true; }
2527 virtual void OnExit() { wxDC::ClearCache(); }
2528
2529 private:
2530 DECLARE_DYNAMIC_CLASS(wxDCModule)
2531 };
2532
2533 IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2534
2535 #endif // wxUSE_DC_CACHEING
2536
2537 // ----------------------------------------------------------------------------
2538 // alpha channel support
2539 // ----------------------------------------------------------------------------
2540
2541 static bool AlphaBlt(HDC hdcDst,
2542 int x, int y, int width, int height,
2543 int srcX, int srcY, HDC hdcSrc,
2544 const wxBitmap& bmp)
2545 {
2546 wxASSERT_MSG( bmp.Ok() && bmp.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2547 wxASSERT_MSG( hdcDst && hdcSrc, _T("AlphaBlt(): invalid HDC") );
2548
2549 // do we have AlphaBlend() and company in the headers?
2550 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2551 // yes, now try to see if we have it during run-time
2552 typedef BOOL (WINAPI *AlphaBlend_t)(HDC,int,int,int,int,
2553 HDC,int,int,int,int,
2554 BLENDFUNCTION);
2555
2556 static AlphaBlend_t
2557 pfnAlphaBlend = (AlphaBlend_t)wxMSIMG32DLL.GetSymbol(_T("AlphaBlend"));
2558 if ( pfnAlphaBlend )
2559 {
2560 BLENDFUNCTION bf;
2561 bf.BlendOp = AC_SRC_OVER;
2562 bf.BlendFlags = 0;
2563 bf.SourceConstantAlpha = 0xff;
2564 bf.AlphaFormat = AC_SRC_ALPHA;
2565
2566 if ( pfnAlphaBlend(hdcDst, x, y, width, height,
2567 hdcSrc, srcX, srcY, width, height,
2568 bf) )
2569 {
2570 // skip wxAlphaBlend() call below
2571 return true;
2572 }
2573
2574 wxLogLastError(_T("AlphaBlend"));
2575 }
2576 #else
2577 wxUnusedVar(hdcSrc);
2578 #endif // defined(AC_SRC_OVER)
2579
2580 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2581 // implementation
2582 #ifdef wxHAVE_RAW_BITMAP
2583 wxAlphaBlend(hdcDst, x, y, width, height, srcX, srcY, bmp);
2584
2585 return true;
2586 #else // !wxHAVE_RAW_BITMAP
2587 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2588 // alpha but at least something will be shown like this)
2589 wxUnusedVar(bmp);
2590 return false;
2591 #endif // wxHAVE_RAW_BITMAP
2592 }
2593
2594
2595 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2596 #ifdef wxHAVE_RAW_BITMAP
2597
2598 static void
2599 wxAlphaBlend(HDC hdcDst, int xDst, int yDst,
2600 int w, int h,
2601 int srcX, int srcY, const wxBitmap& bmpSrc)
2602 {
2603 // get the destination DC pixels
2604 wxBitmap bmpDst(w, h, 32 /* force creating RGBA DIB */);
2605 MemoryHDC hdcMem;
2606 SelectInHDC select(hdcMem, GetHbitmapOf(bmpDst));
2607
2608 if ( !::BitBlt(hdcMem, 0, 0, w, h, hdcDst, xDst, yDst, SRCCOPY) )
2609 {
2610 wxLogLastError(_T("BitBlt"));
2611 }
2612
2613 // combine them with the source bitmap using alpha
2614 wxAlphaPixelData dataDst(bmpDst),
2615 dataSrc((wxBitmap &)bmpSrc);
2616
2617 wxCHECK_RET( dataDst && dataSrc,
2618 _T("failed to get raw data in wxAlphaBlend") );
2619
2620 wxAlphaPixelData::Iterator pDst(dataDst),
2621 pSrc(dataSrc);
2622
2623 pSrc.Offset(dataSrc, srcX, srcY);
2624
2625 for ( int y = 0; y < h; y++ )
2626 {
2627 wxAlphaPixelData::Iterator pDstRowStart = pDst,
2628 pSrcRowStart = pSrc;
2629
2630 for ( int x = 0; x < w; x++ )
2631 {
2632 // note that source bitmap uses premultiplied alpha (as required by
2633 // the real AlphaBlend)
2634 const unsigned beta = 255 - pSrc.Alpha();
2635
2636 pDst.Red() = pSrc.Red() + (beta * pDst.Red() + 127) / 255;
2637 pDst.Blue() = pSrc.Blue() + (beta * pDst.Blue() + 127) / 255;
2638 pDst.Green() = pSrc.Green() + (beta * pDst.Green() + 127) / 255;
2639
2640 ++pDst;
2641 ++pSrc;
2642 }
2643
2644 pDst = pDstRowStart;
2645 pSrc = pSrcRowStart;
2646 pDst.OffsetY(dataDst, 1);
2647 pSrc.OffsetY(dataSrc, 1);
2648 }
2649
2650 // and finally blit them back to the destination DC
2651 if ( !::BitBlt(hdcDst, xDst, yDst, w, h, hdcMem, 0, 0, SRCCOPY) )
2652 {
2653 wxLogLastError(_T("BitBlt"));
2654 }
2655 }
2656
2657 #endif // #ifdef wxHAVE_RAW_BITMAP
2658
2659 void wxDC::DoGradientFillLinear (const wxRect& rect,
2660 const wxColour& initialColour,
2661 const wxColour& destColour,
2662 wxDirection nDirection)
2663 {
2664 // use native function if we have compile-time support it and can load it
2665 // during run-time (linking to it statically would make the program
2666 // unusable on earlier Windows versions)
2667 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2668 typedef BOOL
2669 (WINAPI *GradientFill_t)(HDC, PTRIVERTEX, ULONG, PVOID, ULONG, ULONG);
2670 static GradientFill_t pfnGradientFill =
2671 (GradientFill_t)wxMSIMG32DLL.GetSymbol(_T("GradientFill"));
2672
2673 if ( pfnGradientFill )
2674 {
2675 GRADIENT_RECT grect;
2676 grect.UpperLeft = 0;
2677 grect.LowerRight = 1;
2678
2679 // invert colours direction if not filling from left-to-right or
2680 // top-to-bottom
2681 int firstVertex = nDirection == wxNORTH || nDirection == wxWEST ? 1 : 0;
2682
2683 // one vertex for upper left and one for upper-right
2684 TRIVERTEX vertices[2];
2685
2686 vertices[0].x = rect.GetLeft();
2687 vertices[0].y = rect.GetTop();
2688 vertices[1].x = rect.GetRight();
2689 vertices[1].y = rect.GetBottom();
2690
2691 vertices[firstVertex].Red = (COLOR16)(initialColour.Red() << 8);
2692 vertices[firstVertex].Green = (COLOR16)(initialColour.Green() << 8);
2693 vertices[firstVertex].Blue = (COLOR16)(initialColour.Blue() << 8);
2694 vertices[firstVertex].Alpha = 0;
2695 vertices[1 - firstVertex].Red = (COLOR16)(destColour.Red() << 8);
2696 vertices[1 - firstVertex].Green = (COLOR16)(destColour.Green() << 8);
2697 vertices[1 - firstVertex].Blue = (COLOR16)(destColour.Blue() << 8);
2698 vertices[1 - firstVertex].Alpha = 0;
2699
2700 if ( (*pfnGradientFill)
2701 (
2702 GetHdc(),
2703 vertices,
2704 WXSIZEOF(vertices),
2705 &grect,
2706 1,
2707 nDirection == wxWEST || nDirection == wxEAST
2708 ? GRADIENT_FILL_RECT_H
2709 : GRADIENT_FILL_RECT_V
2710 ) )
2711 {
2712 // skip call of the base class version below
2713 return;
2714 }
2715
2716 wxLogLastError(_T("GradientFill"));
2717 }
2718 #endif // wxUSE_DYNLIB_CLASS
2719
2720 wxDCBase::DoGradientFillLinear(rect, initialColour, destColour, nDirection);
2721 }
2722
2723 static DWORD wxGetDCLayout(HDC hdc)
2724 {
2725 typedef DWORD (WINAPI *GetLayout_t)(HDC);
2726 static GetLayout_t
2727 pfnGetLayout = (GetLayout_t)wxGDI32DLL.GetSymbol(_T("GetLayout"));
2728
2729 return pfnGetLayout ? pfnGetLayout(hdc) : (DWORD)-1;
2730 }
2731
2732 wxLayoutDirection wxDC::GetLayoutDirection() const
2733 {
2734 DWORD layout = wxGetDCLayout(GetHdc());
2735
2736 if ( layout == (DWORD)-1 )
2737 return wxLayout_Default;
2738
2739 return layout & LAYOUT_RTL ? wxLayout_RightToLeft : wxLayout_LeftToRight;
2740 }
2741
2742 void wxDC::SetLayoutDirection(wxLayoutDirection dir)
2743 {
2744 typedef DWORD (WINAPI *SetLayout_t)(HDC, DWORD);
2745 static SetLayout_t
2746 pfnSetLayout = (SetLayout_t)wxGDI32DLL.GetSymbol(_T("SetLayout"));
2747 if ( !pfnSetLayout )
2748 return;
2749
2750 if ( dir == wxLayout_Default )
2751 {
2752 dir = wxTheApp->GetLayoutDirection();
2753 if ( dir == wxLayout_Default )
2754 return;
2755 }
2756
2757 DWORD layout = wxGetDCLayout(GetHdc());
2758 if ( dir == wxLayout_RightToLeft )
2759 layout |= LAYOUT_RTL;
2760 else
2761 layout &= ~LAYOUT_RTL;
2762
2763 pfnSetLayout(GetHdc(), layout);
2764 }