got rid of (unused since a long time) wxNO_3D and wxUSER_COLOURS
[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 {
1404 (void)SetBkColor(GetHdc(), m_backgroundBrush.GetColour().GetPixel());
1405 }
1406 }
1407
1408 void wxDC::SetBackgroundMode(int mode)
1409 {
1410 #ifdef __WXMICROWIN__
1411 if (!GetHDC()) return;
1412 #endif
1413
1414 m_backgroundMode = mode;
1415
1416 // SetBackgroundColour now only refers to text background
1417 // and m_backgroundMode is used there
1418 }
1419
1420 void wxDC::SetLogicalFunction(int function)
1421 {
1422 #ifdef __WXMICROWIN__
1423 if (!GetHDC()) return;
1424 #endif
1425
1426 m_logicalFunction = function;
1427
1428 SetRop(m_hDC);
1429 }
1430
1431 void wxDC::SetRop(WXHDC dc)
1432 {
1433 if ( !dc || m_logicalFunction < 0 )
1434 return;
1435
1436 int rop;
1437
1438 switch (m_logicalFunction)
1439 {
1440 case wxCLEAR: rop = R2_BLACK; break;
1441 case wxXOR: rop = R2_XORPEN; break;
1442 case wxINVERT: rop = R2_NOT; break;
1443 case wxOR_REVERSE: rop = R2_MERGEPENNOT; break;
1444 case wxAND_REVERSE: rop = R2_MASKPENNOT; break;
1445 case wxCOPY: rop = R2_COPYPEN; break;
1446 case wxAND: rop = R2_MASKPEN; break;
1447 case wxAND_INVERT: rop = R2_MASKNOTPEN; break;
1448 case wxNO_OP: rop = R2_NOP; break;
1449 case wxNOR: rop = R2_NOTMERGEPEN; break;
1450 case wxEQUIV: rop = R2_NOTXORPEN; break;
1451 case wxSRC_INVERT: rop = R2_NOTCOPYPEN; break;
1452 case wxOR_INVERT: rop = R2_MERGENOTPEN; break;
1453 case wxNAND: rop = R2_NOTMASKPEN; break;
1454 case wxOR: rop = R2_MERGEPEN; break;
1455 case wxSET: rop = R2_WHITE; break;
1456
1457 default:
1458 wxFAIL_MSG( wxT("unsupported logical function") );
1459 return;
1460 }
1461
1462 SetROP2(GetHdc(), rop);
1463 }
1464
1465 bool wxDC::StartDoc(const wxString& WXUNUSED(message))
1466 {
1467 // We might be previewing, so return TRUE to let it continue.
1468 return TRUE;
1469 }
1470
1471 void wxDC::EndDoc()
1472 {
1473 }
1474
1475 void wxDC::StartPage()
1476 {
1477 }
1478
1479 void wxDC::EndPage()
1480 {
1481 }
1482
1483 // ---------------------------------------------------------------------------
1484 // text metrics
1485 // ---------------------------------------------------------------------------
1486
1487 wxCoord wxDC::GetCharHeight() const
1488 {
1489 #ifdef __WXMICROWIN__
1490 if (!GetHDC()) return 0;
1491 #endif
1492
1493 TEXTMETRIC lpTextMetric;
1494
1495 GetTextMetrics(GetHdc(), &lpTextMetric);
1496
1497 return lpTextMetric.tmHeight;
1498 }
1499
1500 wxCoord wxDC::GetCharWidth() const
1501 {
1502 #ifdef __WXMICROWIN__
1503 if (!GetHDC()) return 0;
1504 #endif
1505
1506 TEXTMETRIC lpTextMetric;
1507
1508 GetTextMetrics(GetHdc(), &lpTextMetric);
1509
1510 return lpTextMetric.tmAveCharWidth;
1511 }
1512
1513 void wxDC::DoGetTextExtent(const wxString& string, wxCoord *x, wxCoord *y,
1514 wxCoord *descent, wxCoord *externalLeading,
1515 wxFont *font) const
1516 {
1517 #ifdef __WXMICROWIN__
1518 if (!GetHDC())
1519 {
1520 if (x) *x = 0;
1521 if (y) *y = 0;
1522 if (descent) *descent = 0;
1523 if (externalLeading) *externalLeading = 0;
1524 return;
1525 }
1526 #endif // __WXMICROWIN__
1527
1528 HFONT hfontOld;
1529 if ( font )
1530 {
1531 wxASSERT_MSG( font->Ok(), _T("invalid font in wxDC::GetTextExtent") );
1532
1533 hfontOld = (HFONT)::SelectObject(GetHdc(), GetHfontOf(*font));
1534 }
1535 else // don't change the font
1536 {
1537 hfontOld = 0;
1538 }
1539
1540 SIZE sizeRect;
1541 TEXTMETRIC tm;
1542
1543 GetTextExtentPoint(GetHdc(), string, string.length(), &sizeRect);
1544 GetTextMetrics(GetHdc(), &tm);
1545
1546 if (x)
1547 *x = sizeRect.cx;
1548 if (y)
1549 *y = sizeRect.cy;
1550 if (descent)
1551 *descent = tm.tmDescent;
1552 if (externalLeading)
1553 *externalLeading = tm.tmExternalLeading;
1554
1555 if ( hfontOld )
1556 {
1557 ::SelectObject(GetHdc(), hfontOld);
1558 }
1559 }
1560
1561 void wxDC::SetMapMode(int mode)
1562 {
1563 #ifdef __WXMICROWIN__
1564 if (!GetHDC()) return;
1565 #endif
1566
1567 m_mappingMode = mode;
1568
1569 if ( mode == wxMM_TEXT )
1570 {
1571 m_logicalScaleX =
1572 m_logicalScaleY = 1.0;
1573 }
1574 else // need to do some calculations
1575 {
1576 int pixel_width = ::GetDeviceCaps(GetHdc(), HORZRES),
1577 pixel_height = ::GetDeviceCaps(GetHdc(), VERTRES),
1578 mm_width = ::GetDeviceCaps(GetHdc(), HORZSIZE),
1579 mm_height = ::GetDeviceCaps(GetHdc(), VERTSIZE);
1580
1581 if ( (mm_width == 0) || (mm_height == 0) )
1582 {
1583 // we can't calculate mm2pixels[XY] then!
1584 return;
1585 }
1586
1587 double mm2pixelsX = (double)pixel_width / mm_width,
1588 mm2pixelsY = (double)pixel_height / mm_height;
1589
1590 switch (mode)
1591 {
1592 case wxMM_TWIPS:
1593 m_logicalScaleX = twips2mm * mm2pixelsX;
1594 m_logicalScaleY = twips2mm * mm2pixelsY;
1595 break;
1596
1597 case wxMM_POINTS:
1598 m_logicalScaleX = pt2mm * mm2pixelsX;
1599 m_logicalScaleY = pt2mm * mm2pixelsY;
1600 break;
1601
1602 case wxMM_METRIC:
1603 m_logicalScaleX = mm2pixelsX;
1604 m_logicalScaleY = mm2pixelsY;
1605 break;
1606
1607 case wxMM_LOMETRIC:
1608 m_logicalScaleX = mm2pixelsX / 10.0;
1609 m_logicalScaleY = mm2pixelsY / 10.0;
1610 break;
1611
1612 default:
1613 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1614 }
1615 }
1616
1617 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1618 // cases we could do with MM_TEXT and in the remaining 0.9% with
1619 // MM_ISOTROPIC (TODO!)
1620 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
1621
1622 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
1623 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
1624
1625 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
1626 ::SetWindowExtEx(GetHdc(), width, height, NULL);
1627
1628 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX, m_deviceOriginY, NULL);
1629 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX, m_logicalOriginY, NULL);
1630 }
1631
1632 void wxDC::SetUserScale(double x, double y)
1633 {
1634 #ifdef __WXMICROWIN__
1635 if (!GetHDC()) return;
1636 #endif
1637
1638 if ( x == m_userScaleX && y == m_userScaleY )
1639 return;
1640
1641 m_userScaleX = x;
1642 m_userScaleY = y;
1643
1644 SetMapMode(m_mappingMode);
1645 }
1646
1647 void wxDC::SetAxisOrientation(bool xLeftRight, bool yBottomUp)
1648 {
1649 #ifdef __WXMICROWIN__
1650 if (!GetHDC()) return;
1651 #endif
1652
1653 int signX = xLeftRight ? 1 : -1,
1654 signY = yBottomUp ? -1 : 1;
1655
1656 if ( signX != m_signX || signY != m_signY )
1657 {
1658 m_signX = signX;
1659 m_signY = signY;
1660
1661 SetMapMode(m_mappingMode);
1662 }
1663 }
1664
1665 void wxDC::SetSystemScale(double x, double y)
1666 {
1667 #ifdef __WXMICROWIN__
1668 if (!GetHDC()) return;
1669 #endif
1670
1671 if ( x == m_scaleX && y == m_scaleY )
1672 return;
1673
1674 m_scaleX = x;
1675 m_scaleY = y;
1676
1677 SetMapMode(m_mappingMode);
1678 }
1679
1680 void wxDC::SetLogicalOrigin(wxCoord x, wxCoord y)
1681 {
1682 #ifdef __WXMICROWIN__
1683 if (!GetHDC()) return;
1684 #endif
1685
1686 if ( x == m_logicalOriginX && y == m_logicalOriginY )
1687 return;
1688
1689 m_logicalOriginX = x;
1690 m_logicalOriginY = y;
1691
1692 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
1693 }
1694
1695 void wxDC::SetDeviceOrigin(wxCoord x, wxCoord y)
1696 {
1697 #ifdef __WXMICROWIN__
1698 if (!GetHDC()) return;
1699 #endif
1700
1701 if ( x == m_deviceOriginX && y == m_deviceOriginY )
1702 return;
1703
1704 m_deviceOriginX = x;
1705 m_deviceOriginY = y;
1706
1707 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
1708 }
1709
1710 // ---------------------------------------------------------------------------
1711 // coordinates transformations
1712 // ---------------------------------------------------------------------------
1713
1714 wxCoord wxDCBase::DeviceToLogicalX(wxCoord x) const
1715 {
1716 return DeviceToLogicalXRel(x - m_deviceOriginX)*m_signX + m_logicalOriginX;
1717 }
1718
1719 wxCoord wxDCBase::DeviceToLogicalXRel(wxCoord x) const
1720 {
1721 // axis orientation is not taken into account for conversion of a distance
1722 return (wxCoord)(x / (m_logicalScaleX*m_userScaleX*m_scaleX));
1723 }
1724
1725 wxCoord wxDCBase::DeviceToLogicalY(wxCoord y) const
1726 {
1727 return DeviceToLogicalYRel(y - m_deviceOriginY)*m_signY + m_logicalOriginY;
1728 }
1729
1730 wxCoord wxDCBase::DeviceToLogicalYRel(wxCoord y) const
1731 {
1732 // axis orientation is not taken into account for conversion of a distance
1733 return (wxCoord)( y / (m_logicalScaleY*m_userScaleY*m_scaleY));
1734 }
1735
1736 wxCoord wxDCBase::LogicalToDeviceX(wxCoord x) const
1737 {
1738 return LogicalToDeviceXRel(x - m_logicalOriginX)*m_signX + m_deviceOriginX;
1739 }
1740
1741 wxCoord wxDCBase::LogicalToDeviceXRel(wxCoord x) const
1742 {
1743 // axis orientation is not taken into account for conversion of a distance
1744 return (wxCoord) (x*m_logicalScaleX*m_userScaleX*m_scaleX);
1745 }
1746
1747 wxCoord wxDCBase::LogicalToDeviceY(wxCoord y) const
1748 {
1749 return LogicalToDeviceYRel(y - m_logicalOriginY)*m_signY + m_deviceOriginY;
1750 }
1751
1752 wxCoord wxDCBase::LogicalToDeviceYRel(wxCoord y) const
1753 {
1754 // axis orientation is not taken into account for conversion of a distance
1755 return (wxCoord) (y*m_logicalScaleY*m_userScaleY*m_scaleY);
1756 }
1757
1758 // ---------------------------------------------------------------------------
1759 // bit blit
1760 // ---------------------------------------------------------------------------
1761
1762 bool wxDC::DoBlit(wxCoord xdest, wxCoord ydest,
1763 wxCoord width, wxCoord height,
1764 wxDC *source, wxCoord xsrc, wxCoord ysrc,
1765 int rop, bool useMask,
1766 wxCoord xsrcMask, wxCoord ysrcMask)
1767 {
1768 wxCHECK_MSG( source, FALSE, _T("wxDC::Blit(): NULL wxDC pointer") );
1769
1770 #ifdef __WXMICROWIN__
1771 if (!GetHDC()) return FALSE;
1772 #endif
1773
1774 const wxBitmap& bmpSrc = source->m_selectedBitmap;
1775 if ( bmpSrc.Ok() && bmpSrc.HasAlpha() )
1776 {
1777 if ( AlphaBlt(GetHdc(), xdest, ydest, width, height,
1778 GetHdcOf(*source), bmpSrc) )
1779 return TRUE;
1780 }
1781
1782 wxMask *mask = NULL;
1783 if ( useMask )
1784 {
1785 mask = bmpSrc.GetMask();
1786
1787 if ( !(bmpSrc.Ok() && mask && mask->GetMaskBitmap()) )
1788 {
1789 // don't give assert here because this would break existing
1790 // programs - just silently ignore useMask parameter
1791 useMask = FALSE;
1792 }
1793 }
1794
1795 if (xsrcMask == -1 && ysrcMask == -1)
1796 {
1797 xsrcMask = xsrc; ysrcMask = ysrc;
1798 }
1799
1800 COLORREF old_textground = ::GetTextColor(GetHdc());
1801 COLORREF old_background = ::GetBkColor(GetHdc());
1802 if (m_textForegroundColour.Ok())
1803 {
1804 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1805 }
1806 if (m_textBackgroundColour.Ok())
1807 {
1808 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1809 }
1810
1811 DWORD dwRop = SRCCOPY;
1812 switch (rop)
1813 {
1814 case wxXOR: dwRop = SRCINVERT; break;
1815 case wxINVERT: dwRop = DSTINVERT; break;
1816 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
1817 case wxAND_REVERSE: dwRop = SRCERASE; break;
1818 case wxCLEAR: dwRop = BLACKNESS; break;
1819 case wxSET: dwRop = WHITENESS; break;
1820 case wxOR_INVERT: dwRop = MERGEPAINT; break;
1821 case wxAND: dwRop = SRCAND; break;
1822 case wxOR: dwRop = SRCPAINT; break;
1823 case wxEQUIV: dwRop = 0x00990066; break;
1824 case wxNAND: dwRop = 0x007700E6; break;
1825 case wxAND_INVERT: dwRop = 0x00220326; break;
1826 case wxCOPY: dwRop = SRCCOPY; break;
1827 case wxNO_OP: dwRop = DSTCOPY; break;
1828 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
1829 case wxNOR: dwRop = NOTSRCCOPY; break;
1830 default:
1831 wxFAIL_MSG( wxT("unsupported logical function") );
1832 return FALSE;
1833 }
1834
1835 bool success = FALSE;
1836
1837 if (useMask)
1838 {
1839 #ifdef __WIN32__
1840 // we want the part of the image corresponding to the mask to be
1841 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1842 // meaning of fg and bg is inverted which corresponds to wxWin notion
1843 // of the mask which is also contrary to the Windows one)
1844
1845 // On some systems, MaskBlt succeeds yet is much much slower
1846 // than the wxWindows fall-back implementation. So we need
1847 // to be able to switch this on and off at runtime.
1848 #if wxUSE_SYSTEM_OPTIONS
1849 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1850 #endif
1851 {
1852 success = ::MaskBlt
1853 (
1854 GetHdc(),
1855 xdest, ydest, width, height,
1856 GetHdcOf(*source),
1857 xsrc, ysrc,
1858 (HBITMAP)mask->GetMaskBitmap(),
1859 xsrcMask, ysrcMask,
1860 MAKEROP4(dwRop, DSTCOPY)
1861 ) != 0;
1862 }
1863
1864 if ( !success )
1865 #endif // Win32
1866 {
1867 // Blit bitmap with mask
1868 HDC dc_mask ;
1869 HDC dc_buffer ;
1870 HBITMAP buffer_bmap ;
1871
1872 #if wxUSE_DC_CACHEING
1873 // create a temp buffer bitmap and DCs to access it and the mask
1874 wxDCCacheEntry* dcCacheEntry1 = FindDCInCache(NULL, source->GetHDC());
1875 dc_mask = (HDC) dcCacheEntry1->m_dc;
1876
1877 wxDCCacheEntry* dcCacheEntry2 = FindDCInCache(dcCacheEntry1, GetHDC());
1878 dc_buffer = (HDC) dcCacheEntry2->m_dc;
1879
1880 wxDCCacheEntry* bitmapCacheEntry = FindBitmapInCache(GetHDC(),
1881 width, height);
1882
1883 buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap;
1884 #else // !wxUSE_DC_CACHEING
1885 // create a temp buffer bitmap and DCs to access it and the mask
1886 dc_mask = ::CreateCompatibleDC(GetHdcOf(*source));
1887 dc_buffer = ::CreateCompatibleDC(GetHdc());
1888 buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), width, height);
1889 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
1890 HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
1891 HGDIOBJ hOldBufferBitmap = ::SelectObject(dc_buffer, buffer_bmap);
1892
1893 // copy dest to buffer
1894 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1895 GetHdc(), xdest, ydest, SRCCOPY) )
1896 {
1897 wxLogLastError(wxT("BitBlt"));
1898 }
1899
1900 // copy src to buffer using selected raster op
1901 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1902 GetHdcOf(*source), xsrc, ysrc, dwRop) )
1903 {
1904 wxLogLastError(wxT("BitBlt"));
1905 }
1906
1907 // set masked area in buffer to BLACK (pixel value 0)
1908 COLORREF prevBkCol = ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1909 COLORREF prevCol = ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1910 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1911 dc_mask, xsrcMask, ysrcMask, SRCAND) )
1912 {
1913 wxLogLastError(wxT("BitBlt"));
1914 }
1915
1916 // set unmasked area in dest to BLACK
1917 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1918 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1919 if ( !::BitBlt(GetHdc(), xdest, ydest, (int)width, (int)height,
1920 dc_mask, xsrcMask, ysrcMask, SRCAND) )
1921 {
1922 wxLogLastError(wxT("BitBlt"));
1923 }
1924 ::SetBkColor(GetHdc(), prevBkCol); // restore colours to original values
1925 ::SetTextColor(GetHdc(), prevCol);
1926
1927 // OR buffer to dest
1928 success = ::BitBlt(GetHdc(), xdest, ydest,
1929 (int)width, (int)height,
1930 dc_buffer, 0, 0, SRCPAINT) != 0;
1931 if ( !success )
1932 {
1933 wxLogLastError(wxT("BitBlt"));
1934 }
1935
1936 // tidy up temporary DCs and bitmap
1937 ::SelectObject(dc_mask, hOldMaskBitmap);
1938 ::SelectObject(dc_buffer, hOldBufferBitmap);
1939
1940 #if !wxUSE_DC_CACHEING
1941 {
1942 ::DeleteDC(dc_mask);
1943 ::DeleteDC(dc_buffer);
1944 ::DeleteObject(buffer_bmap);
1945 }
1946 #endif
1947 }
1948 }
1949 else // no mask, just BitBlt() it
1950 {
1951 // if we already have a DIB, draw it using StretchDIBits(), otherwise
1952 // use StretchBlt() if available and finally fall back to BitBlt()
1953 const int caps = ::GetDeviceCaps(GetHdc(), RASTERCAPS);
1954 if ( bmpSrc.Ok() && (caps & RC_STRETCHDIB) )
1955 {
1956 DIBSECTION ds;
1957 wxZeroMemory(ds);
1958
1959 if ( ::GetObject(GetHbitmapOf(bmpSrc),
1960 sizeof(ds),
1961 &ds) == sizeof(ds) )
1962 {
1963 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
1964
1965 if ( ::StretchDIBits(GetHdc(),
1966 xdest, ydest,
1967 width, height,
1968 0, 0,
1969 width, height,
1970 ds.dsBm.bmBits,
1971 (LPBITMAPINFO)&ds.dsBmih,
1972 DIB_RGB_COLORS,
1973 SRCCOPY
1974 ) == (int)GDI_ERROR )
1975 {
1976 wxLogLastError(wxT("StretchDIBits"));
1977 }
1978 else
1979 {
1980 success = TRUE;
1981 }
1982 }
1983 }
1984
1985 if ( !success && (caps & RC_STRETCHBLT) )
1986 {
1987 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
1988
1989 if ( !::StretchBlt
1990 (
1991 GetHdc(),
1992 xdest, ydest, width, height,
1993 GetHdcOf(*source),
1994 xsrc, ysrc, width, height,
1995 dwRop
1996 ) )
1997 {
1998 wxLogLastError(_T("StretchBlt"));
1999 }
2000 else
2001 {
2002 success = TRUE;
2003 }
2004 }
2005
2006 if ( !success )
2007 {
2008 if ( !::BitBlt
2009 (
2010 GetHdc(),
2011 xdest, ydest,
2012 (int)width, (int)height,
2013 GetHdcOf(*source),
2014 xsrc, ysrc,
2015 dwRop
2016 ) )
2017 {
2018 wxLogLastError(_T("BitBlt"));
2019 }
2020 else
2021 {
2022 success = TRUE;
2023 }
2024 }
2025 }
2026
2027 ::SetTextColor(GetHdc(), old_textground);
2028 ::SetBkColor(GetHdc(), old_background);
2029
2030 return success;
2031 }
2032
2033 void wxDC::DoGetSize(int *w, int *h) const
2034 {
2035 #ifdef __WXMICROWIN__
2036 if (!GetHDC()) return;
2037 #endif
2038
2039 if ( w ) *w = ::GetDeviceCaps(GetHdc(), HORZRES);
2040 if ( h ) *h = ::GetDeviceCaps(GetHdc(), VERTRES);
2041 }
2042
2043 void wxDC::DoGetSizeMM(int *w, int *h) const
2044 {
2045 #ifdef __WXMICROWIN__
2046 if (!GetHDC()) return;
2047 #endif
2048
2049 // if we implement it in terms of DoGetSize() instead of directly using the
2050 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2051 // will also work for wxWindowDC and wxClientDC even though their size is
2052 // not the same as the total size of the screen
2053 int wPixels, hPixels;
2054 DoGetSize(&wPixels, &hPixels);
2055
2056 if ( w )
2057 {
2058 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
2059
2060 wxCHECK_RET( wTotal, _T("0 width device?") );
2061
2062 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2063 }
2064
2065 if ( h )
2066 {
2067 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2068
2069 wxCHECK_RET( hTotal, _T("0 height device?") );
2070
2071 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2072 }
2073 }
2074
2075 wxSize wxDC::GetPPI() const
2076 {
2077 #ifdef __WXMICROWIN__
2078 if (!GetHDC()) return wxSize();
2079 #endif
2080
2081 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2082 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2083
2084 return wxSize(x, y);
2085 }
2086
2087 // For use by wxWindows only, unless custom units are required.
2088 void wxDC::SetLogicalScale(double x, double y)
2089 {
2090 #ifdef __WXMICROWIN__
2091 if (!GetHDC()) return;
2092 #endif
2093
2094 m_logicalScaleX = x;
2095 m_logicalScaleY = y;
2096 }
2097
2098 // ----------------------------------------------------------------------------
2099 // DC caching
2100 // ----------------------------------------------------------------------------
2101
2102 #if wxUSE_DC_CACHEING
2103
2104 /*
2105 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2106 * improve it in due course, either using arrays, or simply storing pointers to one
2107 * entry for the bitmap, and two for the DCs. -- JACS
2108 */
2109
2110 wxList wxDC::sm_bitmapCache;
2111 wxList wxDC::sm_dcCache;
2112
2113 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2114 {
2115 m_bitmap = hBitmap;
2116 m_dc = 0;
2117 m_width = w;
2118 m_height = h;
2119 m_depth = depth;
2120 }
2121
2122 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2123 {
2124 m_bitmap = 0;
2125 m_dc = hDC;
2126 m_width = 0;
2127 m_height = 0;
2128 m_depth = depth;
2129 }
2130
2131 wxDCCacheEntry::~wxDCCacheEntry()
2132 {
2133 if (m_bitmap)
2134 ::DeleteObject((HBITMAP) m_bitmap);
2135 if (m_dc)
2136 ::DeleteDC((HDC) m_dc);
2137 }
2138
2139 wxDCCacheEntry* wxDC::FindBitmapInCache(WXHDC dc, int w, int h)
2140 {
2141 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2142 wxNode* node = sm_bitmapCache.GetFirst();
2143 while (node)
2144 {
2145 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2146
2147 if (entry->m_depth == depth)
2148 {
2149 if (entry->m_width < w || entry->m_height < h)
2150 {
2151 ::DeleteObject((HBITMAP) entry->m_bitmap);
2152 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2153 if ( !entry->m_bitmap)
2154 {
2155 wxLogLastError(wxT("CreateCompatibleBitmap"));
2156 }
2157 entry->m_width = w; entry->m_height = h;
2158 return entry;
2159 }
2160 return entry;
2161 }
2162
2163 node = node->GetNext();
2164 }
2165 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2166 if ( !hBitmap)
2167 {
2168 wxLogLastError(wxT("CreateCompatibleBitmap"));
2169 }
2170 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2171 AddToBitmapCache(entry);
2172 return entry;
2173 }
2174
2175 wxDCCacheEntry* wxDC::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
2176 {
2177 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2178 wxNode* node = sm_dcCache.GetFirst();
2179 while (node)
2180 {
2181 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2182
2183 // Don't return the same one as we already have
2184 if (!notThis || (notThis != entry))
2185 {
2186 if (entry->m_depth == depth)
2187 {
2188 return entry;
2189 }
2190 }
2191
2192 node = node->GetNext();
2193 }
2194 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2195 if ( !hDC)
2196 {
2197 wxLogLastError(wxT("CreateCompatibleDC"));
2198 }
2199 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2200 AddToDCCache(entry);
2201 return entry;
2202 }
2203
2204 void wxDC::AddToBitmapCache(wxDCCacheEntry* entry)
2205 {
2206 sm_bitmapCache.Append(entry);
2207 }
2208
2209 void wxDC::AddToDCCache(wxDCCacheEntry* entry)
2210 {
2211 sm_dcCache.Append(entry);
2212 }
2213
2214 void wxDC::ClearCache()
2215 {
2216 sm_dcCache.DeleteContents(TRUE);
2217 sm_dcCache.Clear();
2218 sm_dcCache.DeleteContents(FALSE);
2219 sm_bitmapCache.DeleteContents(TRUE);
2220 sm_bitmapCache.Clear();
2221 sm_bitmapCache.DeleteContents(FALSE);
2222 }
2223
2224 // Clean up cache at app exit
2225 class wxDCModule : public wxModule
2226 {
2227 public:
2228 virtual bool OnInit() { return TRUE; }
2229 virtual void OnExit() { wxDC::ClearCache(); }
2230
2231 private:
2232 DECLARE_DYNAMIC_CLASS(wxDCModule)
2233 };
2234
2235 IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2236
2237 #endif // wxUSE_DC_CACHEING
2238
2239 // ----------------------------------------------------------------------------
2240 // alpha channel support
2241 // ----------------------------------------------------------------------------
2242
2243 static bool AlphaBlt(HDC hdcDst,
2244 int x, int y, int width, int height,
2245 HDC hdcSrc,
2246 const wxBitmap& bmp)
2247 {
2248 wxASSERT_MSG( bmp.Ok() && bmp.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2249 wxASSERT_MSG( hdcDst && hdcSrc, _T("AlphaBlt(): invalid HDC") );
2250
2251 // do we have AlphaBlend() and company in the headers?
2252 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2253 // yes, now try to see if we have it during run-time
2254 typedef BOOL (WINAPI *AlphaBlend_t)(HDC,int,int,int,int,
2255 HDC,int,int,int,int,
2256 BLENDFUNCTION);
2257
2258 // bitmaps can be drawn only from GUI thread so there is no need to
2259 // protect this static variable from multiple threads
2260 static bool s_triedToLoad = FALSE;
2261 static AlphaBlend_t pfnAlphaBlend = NULL;
2262 if ( !s_triedToLoad )
2263 {
2264 s_triedToLoad = TRUE;
2265
2266 // don't give errors about the DLL being unavailable, we're
2267 // prepared to handle this
2268 wxLogNull nolog;
2269
2270 wxDynamicLibrary dll(_T("msimg32.dll"));
2271 if ( dll.IsLoaded() )
2272 {
2273 pfnAlphaBlend = (AlphaBlend_t)dll.GetSymbol(_T("AlphaBlend"));
2274 if ( pfnAlphaBlend )
2275 {
2276 // we must keep the DLL loaded if we want to be able to
2277 // call AlphaBlend() so just never unload it at all, not a
2278 // big deal
2279 dll.Detach();
2280 }
2281 }
2282 }
2283
2284 if ( pfnAlphaBlend )
2285 {
2286 BLENDFUNCTION bf;
2287 bf.BlendOp = AC_SRC_OVER;
2288 bf.BlendFlags = 0;
2289 bf.SourceConstantAlpha = 0xff;
2290 bf.AlphaFormat = AC_SRC_ALPHA;
2291
2292 if ( pfnAlphaBlend(hdcDst, x, y, width, height,
2293 hdcSrc, 0, 0, width, height,
2294 bf) )
2295 {
2296 // skip wxAlphaBlend() call below
2297 return TRUE;
2298 }
2299
2300 wxLogLastError(_T("AlphaBlend"));
2301 }
2302 #endif // defined(AC_SRC_OVER)
2303
2304 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2305 // implementation
2306 #ifdef wxHAVE_RAW_BITMAP
2307 wxAlphaBlend(hdcDst, x, y, width, height, bmp);
2308
2309 return TRUE;
2310 #else // !wxHAVE_RAW_BITMAP
2311 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2312 // alpha but at least something will be shown like this)
2313 return FALSE;
2314 #endif // wxHAVE_RAW_BITMAP
2315 }
2316
2317
2318 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2319 #ifdef wxHAVE_RAW_BITMAP
2320
2321 static void
2322 wxAlphaBlend(HDC hdcDst, int xDst, int yDst, int w, int h, const wxBitmap& bmpSrc)
2323 {
2324 // get the destination DC pixels
2325 wxBitmap bmpDst(w, h, 32 /* force creating RGBA DIB */);
2326 MemoryHDC hdcMem;
2327 SelectInHDC select(hdcMem, GetHbitmapOf(bmpDst));
2328
2329 if ( !::BitBlt(hdcMem, 0, 0, w, h, hdcDst, 0, 0, SRCCOPY) )
2330 {
2331 wxLogLastError(_T("BitBlt"));
2332 }
2333
2334 // combine them with the source bitmap using alpha
2335 wxAlphaPixelData dataDst(bmpDst),
2336 dataSrc((wxBitmap &)bmpSrc);
2337
2338 wxCHECK_RET( dataDst && dataSrc,
2339 _T("failed to get raw data in wxAlphaBlend") );
2340
2341 wxAlphaPixelData::Iterator pDst(dataDst),
2342 pSrc(dataSrc);
2343
2344 for ( int y = 0; y < h; y++ )
2345 {
2346 wxAlphaPixelData::Iterator pDstRowStart = pDst,
2347 pSrcRowStart = pSrc;
2348
2349 for ( int x = 0; x < w; x++ )
2350 {
2351 // note that source bitmap uses premultiplied alpha (as required by
2352 // the real AlphaBlend)
2353 const unsigned beta = 255 - pSrc.Alpha();
2354
2355 pDst.Red() = pSrc.Red() + (beta * pDst.Red() + 127) / 255;
2356 pDst.Blue() = pSrc.Blue() + (beta * pDst.Blue() + 127) / 255;
2357 pDst.Green() = pSrc.Green() + (beta * pDst.Green() + 127) / 255;
2358
2359 ++pDst;
2360 ++pSrc;
2361 }
2362
2363 pDst = pDstRowStart;
2364 pSrc = pSrcRowStart;
2365 pDst.OffsetY(dataDst, 1);
2366 pSrc.OffsetY(dataSrc, 1);
2367 }
2368
2369 // and finally blit them back to the destination DC
2370 if ( !::BitBlt(hdcDst, xDst, yDst, w, h, hdcMem, 0, 0, SRCCOPY) )
2371 {
2372 wxLogLastError(_T("BitBlt"));
2373 }
2374 }
2375
2376 #endif // #ifdef wxHAVE_RAW_BITMAP