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