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