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