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