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