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