]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dc.cpp
return begining of the selection for GetInsertionPoint to match what
[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 // Apparently not needed for WinCE (see e.g. Life! demo)
912 #ifndef __WXWINCE__
913 x2++;
914 y2++;
915 #endif
916 }
917
918 (void)Rectangle(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2));
919 }
920
921
922 CalcBoundingBox(x, y);
923 CalcBoundingBox(x2, y2);
924 }
925
926 void wxDC::DoDrawRoundedRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius)
927 {
928 WXMICROWIN_CHECK_HDC
929
930 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
931
932 // Now, a negative radius value is interpreted to mean
933 // 'the proportion of the smallest X or Y dimension'
934
935 if (radius < 0.0)
936 {
937 double smallest = (width < height) ? width : height;
938 radius = (- radius * smallest);
939 }
940
941 wxCoord x2 = (x+width);
942 wxCoord y2 = (y+height);
943
944 // Windows draws the filled rectangles without outline (i.e. drawn with a
945 // transparent pen) one pixel smaller in both directions and we want them
946 // to have the same size regardless of which pen is used - adjust
947 if ( m_pen.GetStyle() == wxTRANSPARENT )
948 {
949 x2++;
950 y2++;
951 }
952
953 (void)RoundRect(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2),
954 YLOG2DEV(y2), (int) (2*XLOG2DEV(radius)), (int)( 2*YLOG2DEV(radius)));
955
956 CalcBoundingBox(x, y);
957 CalcBoundingBox(x2, y2);
958 }
959
960 void wxDC::DoDrawEllipse(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
961 {
962 WXMICROWIN_CHECK_HDC
963
964 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
965
966 wxCoord x2 = (x+width);
967 wxCoord y2 = (y+height);
968
969 (void)Ellipse(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2));
970
971 CalcBoundingBox(x, y);
972 CalcBoundingBox(x2, y2);
973 }
974
975 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
976 void wxDC::DoDrawEllipticArc(wxCoord x,wxCoord y,wxCoord w,wxCoord h,double sa,double ea)
977 {
978 #ifdef __WXWINCE__
979 DoDrawEllipticArcRot( x, y, w, h, sa, ea );
980 #else
981
982 WXMICROWIN_CHECK_HDC
983
984 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
985
986 wxCoord x2 = x + w;
987 wxCoord y2 = y + h;
988
989 int rx1 = XLOG2DEV(x+w/2);
990 int ry1 = YLOG2DEV(y+h/2);
991 int rx2 = rx1;
992 int ry2 = ry1;
993
994 sa = DegToRad(sa);
995 ea = DegToRad(ea);
996
997 rx1 += (int)(100.0 * abs(w) * cos(sa));
998 ry1 -= (int)(100.0 * abs(h) * m_signY * sin(sa));
999 rx2 += (int)(100.0 * abs(w) * cos(ea));
1000 ry2 -= (int)(100.0 * abs(h) * m_signY * sin(ea));
1001
1002 // draw pie with NULL_PEN first and then outline otherwise a line is
1003 // drawn from the start and end points to the centre
1004 HPEN hpenOld = (HPEN) ::SelectObject(GetHdc(), (HPEN) ::GetStockObject(NULL_PEN));
1005 if (m_signY > 0)
1006 {
1007 (void)Pie(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2)+1, YLOG2DEV(y2)+1,
1008 rx1, ry1, rx2, ry2);
1009 }
1010 else
1011 {
1012 (void)Pie(GetHdc(), XLOG2DEV(x), YLOG2DEV(y)-1, XLOG2DEV(x2)+1, YLOG2DEV(y2),
1013 rx1, ry1-1, rx2, ry2-1);
1014 }
1015
1016 ::SelectObject(GetHdc(), hpenOld);
1017
1018 (void)Arc(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2),
1019 rx1, ry1, rx2, ry2);
1020
1021 CalcBoundingBox(x, y);
1022 CalcBoundingBox(x2, y2);
1023 #endif
1024 }
1025
1026 void wxDC::DoDrawIcon(const wxIcon& icon, wxCoord x, wxCoord y)
1027 {
1028 WXMICROWIN_CHECK_HDC
1029
1030 wxCHECK_RET( icon.Ok(), wxT("invalid icon in DrawIcon") );
1031
1032 #ifdef __WIN32__
1033 ::DrawIconEx(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), GetHiconOf(icon), icon.GetWidth(), icon.GetHeight(), 0, NULL, DI_NORMAL);
1034 #else
1035 ::DrawIcon(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), GetHiconOf(icon));
1036 #endif
1037
1038 CalcBoundingBox(x, y);
1039 CalcBoundingBox(x + icon.GetWidth(), y + icon.GetHeight());
1040 }
1041
1042 void wxDC::DoDrawBitmap( const wxBitmap &bmp, wxCoord x, wxCoord y, bool useMask )
1043 {
1044 WXMICROWIN_CHECK_HDC
1045
1046 wxCHECK_RET( bmp.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
1047
1048 int width = bmp.GetWidth(),
1049 height = bmp.GetHeight();
1050
1051 HBITMAP hbmpMask = 0;
1052
1053 #if wxUSE_PALETTE
1054 HPALETTE oldPal = 0;
1055 #endif // wxUSE_PALETTE
1056
1057 if ( bmp.HasAlpha() )
1058 {
1059 MemoryHDC hdcMem;
1060 SelectInHDC select(hdcMem, GetHbitmapOf(bmp));
1061
1062 if ( AlphaBlt(GetHdc(), x, y, width, height, hdcMem, bmp) )
1063 return;
1064 }
1065
1066 if ( useMask )
1067 {
1068 wxMask *mask = bmp.GetMask();
1069 if ( mask )
1070 hbmpMask = (HBITMAP)mask->GetMaskBitmap();
1071
1072 if ( !hbmpMask )
1073 {
1074 // don't give assert here because this would break existing
1075 // programs - just silently ignore useMask parameter
1076 useMask = false;
1077 }
1078 }
1079 if ( useMask )
1080 {
1081 #ifdef __WIN32__
1082 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1083 // points
1084 // On some systems, MaskBlt succeeds yet is much much slower
1085 // than the wxWidgets fall-back implementation. So we need
1086 // to be able to switch this on and off at runtime.
1087 bool ok = false;
1088 #if wxUSE_SYSTEM_OPTIONS
1089 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1090 #endif
1091 {
1092 HDC cdc = GetHdc();
1093 HDC hdcMem = ::CreateCompatibleDC(GetHdc());
1094 HGDIOBJ hOldBitmap = ::SelectObject(hdcMem, GetHbitmapOf(bmp));
1095 #if wxUSE_PALETTE
1096 wxPalette *pal = bmp.GetPalette();
1097 if ( pal && ::GetDeviceCaps(cdc,BITSPIXEL) <= 8 )
1098 {
1099 oldPal = ::SelectPalette(hdcMem, GetHpaletteOf(*pal), FALSE);
1100 ::RealizePalette(hdcMem);
1101 }
1102 #endif // wxUSE_PALETTE
1103
1104 ok = ::MaskBlt(cdc, x, y, width, height,
1105 hdcMem, 0, 0,
1106 hbmpMask, 0, 0,
1107 MAKEROP4(SRCCOPY, DSTCOPY)) != 0;
1108
1109 #if wxUSE_PALETTE
1110 if (oldPal)
1111 ::SelectPalette(hdcMem, oldPal, FALSE);
1112 #endif // wxUSE_PALETTE
1113
1114 ::SelectObject(hdcMem, hOldBitmap);
1115 ::DeleteDC(hdcMem);
1116 }
1117
1118 if ( !ok )
1119 #endif // Win32
1120 {
1121 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
1122 // level
1123 wxMemoryDC memDC;
1124 memDC.SelectObject(bmp);
1125
1126 Blit(x, y, width, height, &memDC, 0, 0, wxCOPY, useMask);
1127
1128 memDC.SelectObject(wxNullBitmap);
1129 }
1130 }
1131 else // no mask, just use BitBlt()
1132 {
1133 HDC cdc = GetHdc();
1134 HDC memdc = ::CreateCompatibleDC( cdc );
1135 HBITMAP hbitmap = (HBITMAP) bmp.GetHBITMAP( );
1136
1137 wxASSERT_MSG( hbitmap, wxT("bitmap is ok but HBITMAP is NULL?") );
1138
1139 COLORREF old_textground = ::GetTextColor(GetHdc());
1140 COLORREF old_background = ::GetBkColor(GetHdc());
1141 if (m_textForegroundColour.Ok())
1142 {
1143 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1144 }
1145 if (m_textBackgroundColour.Ok())
1146 {
1147 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1148 }
1149
1150 #if wxUSE_PALETTE
1151 wxPalette *pal = bmp.GetPalette();
1152 if ( pal && ::GetDeviceCaps(cdc,BITSPIXEL) <= 8 )
1153 {
1154 oldPal = ::SelectPalette(memdc, GetHpaletteOf(*pal), FALSE);
1155 ::RealizePalette(memdc);
1156 }
1157 #endif // wxUSE_PALETTE
1158
1159 HGDIOBJ hOldBitmap = ::SelectObject( memdc, hbitmap );
1160 ::BitBlt( cdc, x, y, width, height, memdc, 0, 0, SRCCOPY);
1161
1162 #if wxUSE_PALETTE
1163 if (oldPal)
1164 ::SelectPalette(memdc, oldPal, FALSE);
1165 #endif // wxUSE_PALETTE
1166
1167 ::SelectObject( memdc, hOldBitmap );
1168 ::DeleteDC( memdc );
1169
1170 ::SetTextColor(GetHdc(), old_textground);
1171 ::SetBkColor(GetHdc(), old_background);
1172 }
1173 }
1174
1175 void wxDC::DoDrawText(const wxString& text, wxCoord x, wxCoord y)
1176 {
1177 WXMICROWIN_CHECK_HDC
1178
1179 DrawAnyText(text, x, y);
1180
1181 // update the bounding box
1182 CalcBoundingBox(x, y);
1183
1184 wxCoord w, h;
1185 GetTextExtent(text, &w, &h);
1186 CalcBoundingBox(x + w, y + h);
1187 }
1188
1189 void wxDC::DrawAnyText(const wxString& text, wxCoord x, wxCoord y)
1190 {
1191 WXMICROWIN_CHECK_HDC
1192
1193 // prepare for drawing the text
1194 if ( m_textForegroundColour.Ok() )
1195 SetTextColor(GetHdc(), m_textForegroundColour.GetPixel());
1196
1197 DWORD old_background = 0;
1198 if ( m_textBackgroundColour.Ok() )
1199 {
1200 old_background = SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1201 }
1202
1203 SetBkMode(GetHdc(), m_backgroundMode == wxTRANSPARENT ? TRANSPARENT
1204 : OPAQUE);
1205
1206 #ifdef __WXWINCE__
1207 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), 0, NULL,
1208 text.c_str(), text.length(), NULL) == 0 )
1209 {
1210 wxLogLastError(wxT("TextOut"));
1211 }
1212 #else
1213 if ( ::TextOut(GetHdc(), XLOG2DEV(x), YLOG2DEV(y),
1214 text.c_str(), text.length()) == 0 )
1215 {
1216 wxLogLastError(wxT("TextOut"));
1217 }
1218 #endif
1219
1220 // restore the old parameters (text foreground colour may be left because
1221 // it never is set to anything else, but background should remain
1222 // transparent even if we just drew an opaque string)
1223 if ( m_textBackgroundColour.Ok() )
1224 (void)SetBkColor(GetHdc(), old_background);
1225
1226 SetBkMode(GetHdc(), TRANSPARENT);
1227 }
1228
1229 void wxDC::DoDrawRotatedText(const wxString& text,
1230 wxCoord x, wxCoord y,
1231 double angle)
1232 {
1233 WXMICROWIN_CHECK_HDC
1234
1235 // we test that we have some font because otherwise we should still use the
1236 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1237 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1238 // font for drawing rotated fonts unfortunately)
1239 if ( (angle == 0.0) && m_font.Ok() )
1240 {
1241 DoDrawText(text, x, y);
1242 }
1243 #ifndef __WXMICROWIN__
1244 else
1245 {
1246 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1247 // because it's not TrueType and so can't have non zero
1248 // orientation/escapement under Win9x
1249 wxFont font = m_font.Ok() ? m_font : *wxSWISS_FONT;
1250 HFONT hfont = (HFONT)font.GetResourceHandle();
1251 LOGFONT lf;
1252 if ( ::GetObject(hfont, sizeof(lf), &lf) == 0 )
1253 {
1254 wxLogLastError(wxT("GetObject(hfont)"));
1255 }
1256
1257 // GDI wants the angle in tenth of degree
1258 long angle10 = (long)(angle * 10);
1259 lf.lfEscapement = angle10;
1260 lf. lfOrientation = angle10;
1261
1262 hfont = ::CreateFontIndirect(&lf);
1263 if ( !hfont )
1264 {
1265 wxLogLastError(wxT("CreateFont"));
1266 }
1267 else
1268 {
1269 HFONT hfontOld = (HFONT)::SelectObject(GetHdc(), hfont);
1270
1271 DrawAnyText(text, x, y);
1272
1273 (void)::SelectObject(GetHdc(), hfontOld);
1274 (void)::DeleteObject(hfont);
1275 }
1276
1277 // call the bounding box by adding all four vertices of the rectangle
1278 // containing the text to it (simpler and probably not slower than
1279 // determining which of them is really topmost/leftmost/...)
1280 wxCoord w, h;
1281 GetTextExtent(text, &w, &h);
1282
1283 double rad = DegToRad(angle);
1284
1285 // "upper left" and "upper right"
1286 CalcBoundingBox(x, y);
1287 CalcBoundingBox(x + wxCoord(w*cos(rad)), y - wxCoord(w*sin(rad)));
1288
1289 // "bottom left" and "bottom right"
1290 x += (wxCoord)(h*sin(rad));
1291 y += (wxCoord)(h*cos(rad));
1292 CalcBoundingBox(x, y);
1293 CalcBoundingBox(x + wxCoord(w*cos(rad)), y - wxCoord(w*sin(rad)));
1294 }
1295 #endif
1296 }
1297
1298 // ---------------------------------------------------------------------------
1299 // set GDI objects
1300 // ---------------------------------------------------------------------------
1301
1302 #if wxUSE_PALETTE
1303
1304 void wxDC::DoSelectPalette(bool realize)
1305 {
1306 WXMICROWIN_CHECK_HDC
1307
1308 // Set the old object temporarily, in case the assignment deletes an object
1309 // that's not yet selected out.
1310 if (m_oldPalette)
1311 {
1312 ::SelectPalette(GetHdc(), (HPALETTE) m_oldPalette, FALSE);
1313 m_oldPalette = 0;
1314 }
1315
1316 if ( m_palette.Ok() )
1317 {
1318 HPALETTE oldPal = ::SelectPalette(GetHdc(),
1319 GetHpaletteOf(m_palette),
1320 false);
1321 if (!m_oldPalette)
1322 m_oldPalette = (WXHPALETTE) oldPal;
1323
1324 if (realize)
1325 ::RealizePalette(GetHdc());
1326 }
1327 }
1328
1329 void wxDC::SetPalette(const wxPalette& palette)
1330 {
1331 if ( palette.Ok() )
1332 {
1333 m_palette = palette;
1334 DoSelectPalette(true);
1335 }
1336 }
1337
1338 void wxDC::InitializePalette()
1339 {
1340 if ( wxDisplayDepth() <= 8 )
1341 {
1342 // look for any window or parent that has a custom palette. If any has
1343 // one then we need to use it in drawing operations
1344 wxWindow *win = m_canvas->GetAncestorWithCustomPalette();
1345
1346 m_hasCustomPalette = win && win->HasCustomPalette();
1347 if ( m_hasCustomPalette )
1348 {
1349 m_palette = win->GetPalette();
1350
1351 // turn on MSW translation for this palette
1352 DoSelectPalette();
1353 }
1354 }
1355 }
1356
1357 #endif // wxUSE_PALETTE
1358
1359 // SetFont/Pen/Brush() really ask to be implemented as a single template
1360 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1361
1362 void wxDC::SetFont(const wxFont& font)
1363 {
1364 WXMICROWIN_CHECK_HDC
1365
1366 if ( font == m_font )
1367 return;
1368
1369 if ( font.Ok() )
1370 {
1371 HGDIOBJ hfont = ::SelectObject(GetHdc(), GetHfontOf(font));
1372 if ( hfont == HGDI_ERROR )
1373 {
1374 wxLogLastError(_T("SelectObject(font)"));
1375 }
1376 else // selected ok
1377 {
1378 if ( !m_oldFont )
1379 m_oldFont = (WXHPEN)hfont;
1380
1381 m_font = font;
1382 }
1383 }
1384 else // invalid font, reset the current font
1385 {
1386 if ( m_oldFont )
1387 {
1388 if ( ::SelectObject(GetHdc(), (HPEN) m_oldFont) == HGDI_ERROR )
1389 {
1390 wxLogLastError(_T("SelectObject(old font)"));
1391 }
1392
1393 m_oldFont = 0;
1394 }
1395
1396 m_font = wxNullFont;
1397 }
1398 }
1399
1400 void wxDC::SetPen(const wxPen& pen)
1401 {
1402 WXMICROWIN_CHECK_HDC
1403
1404 if ( pen == m_pen )
1405 return;
1406
1407 if ( pen.Ok() )
1408 {
1409 HGDIOBJ hpen = ::SelectObject(GetHdc(), GetHpenOf(pen));
1410 if ( hpen == HGDI_ERROR )
1411 {
1412 wxLogLastError(_T("SelectObject(pen)"));
1413 }
1414 else // selected ok
1415 {
1416 if ( !m_oldPen )
1417 m_oldPen = (WXHPEN)hpen;
1418
1419 m_pen = pen;
1420 }
1421 }
1422 else // invalid pen, reset the current pen
1423 {
1424 if ( m_oldPen )
1425 {
1426 if ( ::SelectObject(GetHdc(), (HPEN) m_oldPen) == HGDI_ERROR )
1427 {
1428 wxLogLastError(_T("SelectObject(old pen)"));
1429 }
1430
1431 m_oldPen = 0;
1432 }
1433
1434 m_pen = wxNullPen;
1435 }
1436 }
1437
1438 void wxDC::SetBrush(const wxBrush& brush)
1439 {
1440 WXMICROWIN_CHECK_HDC
1441
1442 if ( brush == m_brush )
1443 return;
1444
1445 if ( brush.Ok() )
1446 {
1447 // we must make sure the brush is aligned with the logical coordinates
1448 // before selecting it
1449 wxBitmap *stipple = brush.GetStipple();
1450 if ( stipple && stipple->Ok() )
1451 {
1452 if ( !::SetBrushOrgEx
1453 (
1454 GetHdc(),
1455 m_deviceOriginX % stipple->GetWidth(),
1456 m_deviceOriginY % stipple->GetHeight(),
1457 NULL // [out] previous brush origin
1458 ) )
1459 {
1460 wxLogLastError(_T("SetBrushOrgEx()"));
1461 }
1462 }
1463
1464 HGDIOBJ hbrush = ::SelectObject(GetHdc(), GetHbrushOf(brush));
1465 if ( hbrush == HGDI_ERROR )
1466 {
1467 wxLogLastError(_T("SelectObject(brush)"));
1468 }
1469 else // selected ok
1470 {
1471 if ( !m_oldBrush )
1472 m_oldBrush = (WXHPEN)hbrush;
1473
1474 m_brush = brush;
1475 }
1476 }
1477 else // invalid brush, reset the current brush
1478 {
1479 if ( m_oldBrush )
1480 {
1481 if ( ::SelectObject(GetHdc(), (HPEN) m_oldBrush) == HGDI_ERROR )
1482 {
1483 wxLogLastError(_T("SelectObject(old brush)"));
1484 }
1485
1486 m_oldBrush = 0;
1487 }
1488
1489 m_brush = wxNullBrush;
1490 }
1491 }
1492
1493 void wxDC::SetBackground(const wxBrush& brush)
1494 {
1495 WXMICROWIN_CHECK_HDC
1496
1497 m_backgroundBrush = brush;
1498
1499 if ( m_backgroundBrush.Ok() )
1500 {
1501 (void)SetBkColor(GetHdc(), m_backgroundBrush.GetColour().GetPixel());
1502 }
1503 }
1504
1505 void wxDC::SetBackgroundMode(int mode)
1506 {
1507 WXMICROWIN_CHECK_HDC
1508
1509 m_backgroundMode = mode;
1510
1511 // SetBackgroundColour now only refers to text background
1512 // and m_backgroundMode is used there
1513 }
1514
1515 void wxDC::SetLogicalFunction(int function)
1516 {
1517 WXMICROWIN_CHECK_HDC
1518
1519 m_logicalFunction = function;
1520
1521 SetRop(m_hDC);
1522 }
1523
1524 void wxDC::SetRop(WXHDC dc)
1525 {
1526 if ( !dc || m_logicalFunction < 0 )
1527 return;
1528
1529 int rop;
1530
1531 switch (m_logicalFunction)
1532 {
1533 case wxCLEAR: rop = R2_BLACK; break;
1534 case wxXOR: rop = R2_XORPEN; break;
1535 case wxINVERT: rop = R2_NOT; break;
1536 case wxOR_REVERSE: rop = R2_MERGEPENNOT; break;
1537 case wxAND_REVERSE: rop = R2_MASKPENNOT; break;
1538 case wxCOPY: rop = R2_COPYPEN; break;
1539 case wxAND: rop = R2_MASKPEN; break;
1540 case wxAND_INVERT: rop = R2_MASKNOTPEN; break;
1541 case wxNO_OP: rop = R2_NOP; break;
1542 case wxNOR: rop = R2_NOTMERGEPEN; break;
1543 case wxEQUIV: rop = R2_NOTXORPEN; break;
1544 case wxSRC_INVERT: rop = R2_NOTCOPYPEN; break;
1545 case wxOR_INVERT: rop = R2_MERGENOTPEN; break;
1546 case wxNAND: rop = R2_NOTMASKPEN; break;
1547 case wxOR: rop = R2_MERGEPEN; break;
1548 case wxSET: rop = R2_WHITE; break;
1549
1550 default:
1551 wxFAIL_MSG( wxT("unsupported logical function") );
1552 return;
1553 }
1554
1555 SetROP2(GetHdc(), rop);
1556 }
1557
1558 bool wxDC::StartDoc(const wxString& WXUNUSED(message))
1559 {
1560 // We might be previewing, so return true to let it continue.
1561 return true;
1562 }
1563
1564 void wxDC::EndDoc()
1565 {
1566 }
1567
1568 void wxDC::StartPage()
1569 {
1570 }
1571
1572 void wxDC::EndPage()
1573 {
1574 }
1575
1576 // ---------------------------------------------------------------------------
1577 // text metrics
1578 // ---------------------------------------------------------------------------
1579
1580 wxCoord wxDC::GetCharHeight() const
1581 {
1582 WXMICROWIN_CHECK_HDC_RET(0)
1583
1584 TEXTMETRIC lpTextMetric;
1585
1586 GetTextMetrics(GetHdc(), &lpTextMetric);
1587
1588 return lpTextMetric.tmHeight;
1589 }
1590
1591 wxCoord wxDC::GetCharWidth() const
1592 {
1593 WXMICROWIN_CHECK_HDC_RET(0)
1594
1595 TEXTMETRIC lpTextMetric;
1596
1597 GetTextMetrics(GetHdc(), &lpTextMetric);
1598
1599 return lpTextMetric.tmAveCharWidth;
1600 }
1601
1602 void wxDC::DoGetTextExtent(const wxString& string, wxCoord *x, wxCoord *y,
1603 wxCoord *descent, wxCoord *externalLeading,
1604 wxFont *font) const
1605 {
1606 #ifdef __WXMICROWIN__
1607 if (!GetHDC())
1608 {
1609 if (x) *x = 0;
1610 if (y) *y = 0;
1611 if (descent) *descent = 0;
1612 if (externalLeading) *externalLeading = 0;
1613 return;
1614 }
1615 #endif // __WXMICROWIN__
1616
1617 HFONT hfontOld;
1618 if ( font )
1619 {
1620 wxASSERT_MSG( font->Ok(), _T("invalid font in wxDC::GetTextExtent") );
1621
1622 hfontOld = (HFONT)::SelectObject(GetHdc(), GetHfontOf(*font));
1623 }
1624 else // don't change the font
1625 {
1626 hfontOld = 0;
1627 }
1628
1629 SIZE sizeRect;
1630 TEXTMETRIC tm;
1631
1632 GetTextExtentPoint(GetHdc(), string, string.length(), &sizeRect);
1633 GetTextMetrics(GetHdc(), &tm);
1634
1635 if (x)
1636 *x = sizeRect.cx;
1637 if (y)
1638 *y = sizeRect.cy;
1639 if (descent)
1640 *descent = tm.tmDescent;
1641 if (externalLeading)
1642 *externalLeading = tm.tmExternalLeading;
1643
1644 if ( hfontOld )
1645 {
1646 ::SelectObject(GetHdc(), hfontOld);
1647 }
1648 }
1649
1650
1651 // Each element of the array will be the width of the string up to and
1652 // including the coresoponding character in text.
1653
1654 bool wxDC::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const
1655 {
1656 static int maxLenText = -1;
1657 static int maxWidth = -1;
1658 int fit = 0;
1659 SIZE sz = {0,0};
1660 int stlen = text.Length();
1661
1662 if (maxLenText == -1)
1663 {
1664 // Win9x and WinNT+ have different limits
1665 int version = wxGetOsVersion();
1666 maxLenText = version == wxWINDOWS_NT ? 65535 : 8192;
1667 maxWidth = version == wxWINDOWS_NT ? INT_MAX : 32767;
1668 }
1669
1670 widths.Empty();
1671 widths.Add(0, stlen); // fill the array with zeros
1672
1673 if (!::GetTextExtentExPoint(GetHdc(),
1674 text.c_str(), // string to check
1675 wxMin(stlen, maxLenText),
1676 maxWidth,
1677 &fit, // [out] count of chars
1678 // that will fit
1679 &widths[0], // array to fill
1680 &sz))
1681 {
1682 // API failed
1683 wxLogLastError(wxT("GetTextExtentExPoint"));
1684 return false;
1685 }
1686
1687 return true;
1688 }
1689
1690
1691
1692
1693 void wxDC::SetMapMode(int mode)
1694 {
1695 WXMICROWIN_CHECK_HDC
1696
1697 m_mappingMode = mode;
1698
1699 if ( mode == wxMM_TEXT )
1700 {
1701 m_logicalScaleX =
1702 m_logicalScaleY = 1.0;
1703 }
1704 else // need to do some calculations
1705 {
1706 int pixel_width = ::GetDeviceCaps(GetHdc(), HORZRES),
1707 pixel_height = ::GetDeviceCaps(GetHdc(), VERTRES),
1708 mm_width = ::GetDeviceCaps(GetHdc(), HORZSIZE),
1709 mm_height = ::GetDeviceCaps(GetHdc(), VERTSIZE);
1710
1711 if ( (mm_width == 0) || (mm_height == 0) )
1712 {
1713 // we can't calculate mm2pixels[XY] then!
1714 return;
1715 }
1716
1717 double mm2pixelsX = (double)pixel_width / mm_width,
1718 mm2pixelsY = (double)pixel_height / mm_height;
1719
1720 switch (mode)
1721 {
1722 case wxMM_TWIPS:
1723 m_logicalScaleX = twips2mm * mm2pixelsX;
1724 m_logicalScaleY = twips2mm * mm2pixelsY;
1725 break;
1726
1727 case wxMM_POINTS:
1728 m_logicalScaleX = pt2mm * mm2pixelsX;
1729 m_logicalScaleY = pt2mm * mm2pixelsY;
1730 break;
1731
1732 case wxMM_METRIC:
1733 m_logicalScaleX = mm2pixelsX;
1734 m_logicalScaleY = mm2pixelsY;
1735 break;
1736
1737 case wxMM_LOMETRIC:
1738 m_logicalScaleX = mm2pixelsX / 10.0;
1739 m_logicalScaleY = mm2pixelsY / 10.0;
1740 break;
1741
1742 default:
1743 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1744 }
1745 }
1746
1747 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1748 // cases we could do with MM_TEXT and in the remaining 0.9% with
1749 // MM_ISOTROPIC (TODO!)
1750 #ifndef __WXWINCE__
1751 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
1752
1753 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
1754 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
1755
1756 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
1757 ::SetWindowExtEx(GetHdc(), width, height, NULL);
1758
1759 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX, m_deviceOriginY, NULL);
1760 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX, m_logicalOriginY, NULL);
1761 #endif
1762 }
1763
1764 void wxDC::SetUserScale(double x, double y)
1765 {
1766 WXMICROWIN_CHECK_HDC
1767
1768 if ( x == m_userScaleX && y == m_userScaleY )
1769 return;
1770
1771 m_userScaleX = x;
1772 m_userScaleY = y;
1773
1774 this->SetMapMode(m_mappingMode);
1775 }
1776
1777 void wxDC::SetAxisOrientation(bool xLeftRight, bool yBottomUp)
1778 {
1779 WXMICROWIN_CHECK_HDC
1780
1781 #ifndef __WXWINCE__
1782 int signX = xLeftRight ? 1 : -1,
1783 signY = yBottomUp ? -1 : 1;
1784
1785 if ( signX != m_signX || signY != m_signY )
1786 {
1787 m_signX = signX;
1788 m_signY = signY;
1789
1790 SetMapMode(m_mappingMode);
1791 }
1792 #else
1793 wxUnusedVar(xLeftRight);
1794 wxUnusedVar(yBottomUp);
1795 #endif
1796 }
1797
1798 void wxDC::SetSystemScale(double x, double y)
1799 {
1800 WXMICROWIN_CHECK_HDC
1801
1802 if ( x == m_scaleX && y == m_scaleY )
1803 return;
1804
1805 m_scaleX = x;
1806 m_scaleY = y;
1807
1808 #ifndef __WXWINCE__
1809 SetMapMode(m_mappingMode);
1810 #endif
1811 }
1812
1813 void wxDC::SetLogicalOrigin(wxCoord x, wxCoord y)
1814 {
1815 WXMICROWIN_CHECK_HDC
1816
1817 if ( x == m_logicalOriginX && y == m_logicalOriginY )
1818 return;
1819
1820 m_logicalOriginX = x;
1821 m_logicalOriginY = y;
1822
1823 #ifndef __WXWINCE__
1824 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
1825 #endif
1826 }
1827
1828 void wxDC::SetDeviceOrigin(wxCoord x, wxCoord y)
1829 {
1830 WXMICROWIN_CHECK_HDC
1831
1832 if ( x == m_deviceOriginX && y == m_deviceOriginY )
1833 return;
1834
1835 m_deviceOriginX = x;
1836 m_deviceOriginY = y;
1837
1838 #ifndef __WXWINCE__
1839 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
1840 #endif
1841 }
1842
1843 // ---------------------------------------------------------------------------
1844 // coordinates transformations
1845 // ---------------------------------------------------------------------------
1846
1847 wxCoord wxDCBase::DeviceToLogicalX(wxCoord x) const
1848 {
1849 return DeviceToLogicalXRel(x - m_deviceOriginX)*m_signX + m_logicalOriginX;
1850 }
1851
1852 wxCoord wxDCBase::DeviceToLogicalXRel(wxCoord x) const
1853 {
1854 // axis orientation is not taken into account for conversion of a distance
1855 return (wxCoord)(x / (m_logicalScaleX*m_userScaleX*m_scaleX));
1856 }
1857
1858 wxCoord wxDCBase::DeviceToLogicalY(wxCoord y) const
1859 {
1860 return DeviceToLogicalYRel(y - m_deviceOriginY)*m_signY + m_logicalOriginY;
1861 }
1862
1863 wxCoord wxDCBase::DeviceToLogicalYRel(wxCoord y) const
1864 {
1865 // axis orientation is not taken into account for conversion of a distance
1866 return (wxCoord)( y / (m_logicalScaleY*m_userScaleY*m_scaleY));
1867 }
1868
1869 wxCoord wxDCBase::LogicalToDeviceX(wxCoord x) const
1870 {
1871 return LogicalToDeviceXRel(x - m_logicalOriginX)*m_signX + m_deviceOriginX;
1872 }
1873
1874 wxCoord wxDCBase::LogicalToDeviceXRel(wxCoord x) const
1875 {
1876 // axis orientation is not taken into account for conversion of a distance
1877 return (wxCoord) (x*m_logicalScaleX*m_userScaleX*m_scaleX);
1878 }
1879
1880 wxCoord wxDCBase::LogicalToDeviceY(wxCoord y) const
1881 {
1882 return LogicalToDeviceYRel(y - m_logicalOriginY)*m_signY + m_deviceOriginY;
1883 }
1884
1885 wxCoord wxDCBase::LogicalToDeviceYRel(wxCoord y) const
1886 {
1887 // axis orientation is not taken into account for conversion of a distance
1888 return (wxCoord) (y*m_logicalScaleY*m_userScaleY*m_scaleY);
1889 }
1890
1891 // ---------------------------------------------------------------------------
1892 // bit blit
1893 // ---------------------------------------------------------------------------
1894
1895 bool wxDC::DoBlit(wxCoord xdest, wxCoord ydest,
1896 wxCoord width, wxCoord height,
1897 wxDC *source, wxCoord xsrc, wxCoord ysrc,
1898 int rop, bool useMask,
1899 wxCoord xsrcMask, wxCoord ysrcMask)
1900 {
1901 wxCHECK_MSG( source, false, _T("wxDC::Blit(): NULL wxDC pointer") );
1902
1903 WXMICROWIN_CHECK_HDC_RET(false)
1904
1905 // if either the source or destination has alpha channel, we must use
1906 // AlphaBlt() as other function don't handle it correctly
1907 const wxBitmap& bmpSrc = source->m_selectedBitmap;
1908 if ( bmpSrc.Ok() && (bmpSrc.HasAlpha() ||
1909 (m_selectedBitmap.Ok() && m_selectedBitmap.HasAlpha())) )
1910 {
1911 if ( AlphaBlt(GetHdc(), xdest, ydest, width, height,
1912 GetHdcOf(*source), bmpSrc) )
1913 return true;
1914 }
1915
1916 wxMask *mask = NULL;
1917 if ( useMask )
1918 {
1919 mask = bmpSrc.GetMask();
1920
1921 if ( !(bmpSrc.Ok() && mask && mask->GetMaskBitmap()) )
1922 {
1923 // don't give assert here because this would break existing
1924 // programs - just silently ignore useMask parameter
1925 useMask = false;
1926 }
1927 }
1928
1929 if (xsrcMask == -1 && ysrcMask == -1)
1930 {
1931 xsrcMask = xsrc; ysrcMask = ysrc;
1932 }
1933
1934 COLORREF old_textground = ::GetTextColor(GetHdc());
1935 COLORREF old_background = ::GetBkColor(GetHdc());
1936 if (m_textForegroundColour.Ok())
1937 {
1938 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1939 }
1940 if (m_textBackgroundColour.Ok())
1941 {
1942 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1943 }
1944
1945 DWORD dwRop;
1946 switch (rop)
1947 {
1948 case wxXOR: dwRop = SRCINVERT; break;
1949 case wxINVERT: dwRop = DSTINVERT; break;
1950 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
1951 case wxAND_REVERSE: dwRop = SRCERASE; break;
1952 case wxCLEAR: dwRop = BLACKNESS; break;
1953 case wxSET: dwRop = WHITENESS; break;
1954 case wxOR_INVERT: dwRop = MERGEPAINT; break;
1955 case wxAND: dwRop = SRCAND; break;
1956 case wxOR: dwRop = SRCPAINT; break;
1957 case wxEQUIV: dwRop = 0x00990066; break;
1958 case wxNAND: dwRop = 0x007700E6; break;
1959 case wxAND_INVERT: dwRop = 0x00220326; break;
1960 case wxCOPY: dwRop = SRCCOPY; break;
1961 case wxNO_OP: dwRop = DSTCOPY; break;
1962 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
1963 case wxNOR: dwRop = NOTSRCCOPY; break;
1964 default:
1965 wxFAIL_MSG( wxT("unsupported logical function") );
1966 return false;
1967 }
1968
1969 bool success = false;
1970
1971 if (useMask)
1972 {
1973 #ifdef __WIN32__
1974 // we want the part of the image corresponding to the mask to be
1975 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1976 // meaning of fg and bg is inverted which corresponds to wxWin notion
1977 // of the mask which is also contrary to the Windows one)
1978
1979 // On some systems, MaskBlt succeeds yet is much much slower
1980 // than the wxWidgets fall-back implementation. So we need
1981 // to be able to switch this on and off at runtime.
1982 #if wxUSE_SYSTEM_OPTIONS
1983 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1984 #endif
1985 {
1986 success = ::MaskBlt
1987 (
1988 GetHdc(),
1989 xdest, ydest, width, height,
1990 GetHdcOf(*source),
1991 xsrc, ysrc,
1992 (HBITMAP)mask->GetMaskBitmap(),
1993 xsrcMask, ysrcMask,
1994 MAKEROP4(dwRop, DSTCOPY)
1995 ) != 0;
1996 }
1997
1998 if ( !success )
1999 #endif // Win32
2000 {
2001 // Blit bitmap with mask
2002 HDC dc_mask ;
2003 HDC dc_buffer ;
2004 HBITMAP buffer_bmap ;
2005
2006 #if wxUSE_DC_CACHEING
2007 // create a temp buffer bitmap and DCs to access it and the mask
2008 wxDCCacheEntry* dcCacheEntry1 = FindDCInCache(NULL, source->GetHDC());
2009 dc_mask = (HDC) dcCacheEntry1->m_dc;
2010
2011 wxDCCacheEntry* dcCacheEntry2 = FindDCInCache(dcCacheEntry1, GetHDC());
2012 dc_buffer = (HDC) dcCacheEntry2->m_dc;
2013
2014 wxDCCacheEntry* bitmapCacheEntry = FindBitmapInCache(GetHDC(),
2015 width, height);
2016
2017 buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap;
2018 #else // !wxUSE_DC_CACHEING
2019 // create a temp buffer bitmap and DCs to access it and the mask
2020 dc_mask = ::CreateCompatibleDC(GetHdcOf(*source));
2021 dc_buffer = ::CreateCompatibleDC(GetHdc());
2022 buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), width, height);
2023 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2024 HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
2025 HGDIOBJ hOldBufferBitmap = ::SelectObject(dc_buffer, buffer_bmap);
2026
2027 // copy dest to buffer
2028 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
2029 GetHdc(), xdest, ydest, SRCCOPY) )
2030 {
2031 wxLogLastError(wxT("BitBlt"));
2032 }
2033
2034 // copy src to buffer using selected raster op
2035 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
2036 GetHdcOf(*source), xsrc, ysrc, dwRop) )
2037 {
2038 wxLogLastError(wxT("BitBlt"));
2039 }
2040
2041 // set masked area in buffer to BLACK (pixel value 0)
2042 COLORREF prevBkCol = ::SetBkColor(GetHdc(), RGB(255, 255, 255));
2043 COLORREF prevCol = ::SetTextColor(GetHdc(), RGB(0, 0, 0));
2044 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
2045 dc_mask, xsrcMask, ysrcMask, SRCAND) )
2046 {
2047 wxLogLastError(wxT("BitBlt"));
2048 }
2049
2050 // set unmasked area in dest to BLACK
2051 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2052 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2053 if ( !::BitBlt(GetHdc(), xdest, ydest, (int)width, (int)height,
2054 dc_mask, xsrcMask, ysrcMask, SRCAND) )
2055 {
2056 wxLogLastError(wxT("BitBlt"));
2057 }
2058 ::SetBkColor(GetHdc(), prevBkCol); // restore colours to original values
2059 ::SetTextColor(GetHdc(), prevCol);
2060
2061 // OR buffer to dest
2062 success = ::BitBlt(GetHdc(), xdest, ydest,
2063 (int)width, (int)height,
2064 dc_buffer, 0, 0, SRCPAINT) != 0;
2065 if ( !success )
2066 {
2067 wxLogLastError(wxT("BitBlt"));
2068 }
2069
2070 // tidy up temporary DCs and bitmap
2071 ::SelectObject(dc_mask, hOldMaskBitmap);
2072 ::SelectObject(dc_buffer, hOldBufferBitmap);
2073
2074 #if !wxUSE_DC_CACHEING
2075 {
2076 ::DeleteDC(dc_mask);
2077 ::DeleteDC(dc_buffer);
2078 ::DeleteObject(buffer_bmap);
2079 }
2080 #endif
2081 }
2082 }
2083 else // no mask, just BitBlt() it
2084 {
2085 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2086 // use StretchBlt() if available and finally fall back to BitBlt()
2087
2088 // FIXME: use appropriate WinCE functions
2089 #ifndef __WXWINCE__
2090 const int caps = ::GetDeviceCaps(GetHdc(), RASTERCAPS);
2091 if ( bmpSrc.Ok() && (caps & RC_STRETCHDIB) )
2092 {
2093 DIBSECTION ds;
2094 wxZeroMemory(ds);
2095
2096 if ( ::GetObject(GetHbitmapOf(bmpSrc),
2097 sizeof(ds),
2098 &ds) == sizeof(ds) )
2099 {
2100 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2101
2102 // Figure out what co-ordinate system we're supposed to specify
2103 // ysrc in.
2104 const LONG hDIB = ds.dsBmih.biHeight;
2105 if ( hDIB > 0 )
2106 {
2107 // reflect ysrc
2108 ysrc = hDIB - (ysrc + height);
2109 }
2110
2111 if ( ::StretchDIBits(GetHdc(),
2112 xdest, ydest,
2113 width, height,
2114 xsrc, ysrc,
2115 width, height,
2116 ds.dsBm.bmBits,
2117 (LPBITMAPINFO)&ds.dsBmih,
2118 DIB_RGB_COLORS,
2119 SRCCOPY
2120 ) == (int)GDI_ERROR )
2121 {
2122 // On Win9x this API fails most (all?) of the time, so
2123 // logging it becomes quite distracting. Since it falls
2124 // back to the code below this is not really serious, so
2125 // don't log it.
2126 //wxLogLastError(wxT("StretchDIBits"));
2127 }
2128 else
2129 {
2130 success = true;
2131 }
2132 }
2133 }
2134
2135 if ( !success && (caps & RC_STRETCHBLT) )
2136 #endif
2137 // __WXWINCE__
2138 {
2139 #ifndef __WXWINCE__
2140 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2141 #endif
2142
2143 if ( !::StretchBlt
2144 (
2145 GetHdc(),
2146 xdest, ydest, width, height,
2147 GetHdcOf(*source),
2148 xsrc, ysrc, width, height,
2149 dwRop
2150 ) )
2151 {
2152 wxLogLastError(_T("StretchBlt"));
2153 }
2154 else
2155 {
2156 success = true;
2157 }
2158 }
2159
2160 if ( !success )
2161 {
2162 if ( !::BitBlt
2163 (
2164 GetHdc(),
2165 xdest, ydest,
2166 (int)width, (int)height,
2167 GetHdcOf(*source),
2168 xsrc, ysrc,
2169 dwRop
2170 ) )
2171 {
2172 wxLogLastError(_T("BitBlt"));
2173 }
2174 else
2175 {
2176 success = true;
2177 }
2178 }
2179 }
2180
2181 ::SetTextColor(GetHdc(), old_textground);
2182 ::SetBkColor(GetHdc(), old_background);
2183
2184 return success;
2185 }
2186
2187 void wxDC::DoGetSize(int *w, int *h) const
2188 {
2189 WXMICROWIN_CHECK_HDC
2190
2191 if ( w ) *w = ::GetDeviceCaps(GetHdc(), HORZRES);
2192 if ( h ) *h = ::GetDeviceCaps(GetHdc(), VERTRES);
2193 }
2194
2195 void wxDC::DoGetSizeMM(int *w, int *h) const
2196 {
2197 WXMICROWIN_CHECK_HDC
2198
2199 // if we implement it in terms of DoGetSize() instead of directly using the
2200 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2201 // will also work for wxWindowDC and wxClientDC even though their size is
2202 // not the same as the total size of the screen
2203 int wPixels, hPixels;
2204 DoGetSize(&wPixels, &hPixels);
2205
2206 if ( w )
2207 {
2208 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
2209
2210 wxCHECK_RET( wTotal, _T("0 width device?") );
2211
2212 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2213 }
2214
2215 if ( h )
2216 {
2217 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2218
2219 wxCHECK_RET( hTotal, _T("0 height device?") );
2220
2221 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2222 }
2223 }
2224
2225 wxSize wxDC::GetPPI() const
2226 {
2227 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2228
2229 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2230 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2231
2232 return wxSize(x, y);
2233 }
2234
2235 // For use by wxWidgets only, unless custom units are required.
2236 void wxDC::SetLogicalScale(double x, double y)
2237 {
2238 WXMICROWIN_CHECK_HDC
2239
2240 m_logicalScaleX = x;
2241 m_logicalScaleY = y;
2242 }
2243
2244 // ----------------------------------------------------------------------------
2245 // DC caching
2246 // ----------------------------------------------------------------------------
2247
2248 #if wxUSE_DC_CACHEING
2249
2250 /*
2251 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2252 * improve it in due course, either using arrays, or simply storing pointers to one
2253 * entry for the bitmap, and two for the DCs. -- JACS
2254 */
2255
2256 wxList wxDC::sm_bitmapCache;
2257 wxList wxDC::sm_dcCache;
2258
2259 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2260 {
2261 m_bitmap = hBitmap;
2262 m_dc = 0;
2263 m_width = w;
2264 m_height = h;
2265 m_depth = depth;
2266 }
2267
2268 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2269 {
2270 m_bitmap = 0;
2271 m_dc = hDC;
2272 m_width = 0;
2273 m_height = 0;
2274 m_depth = depth;
2275 }
2276
2277 wxDCCacheEntry::~wxDCCacheEntry()
2278 {
2279 if (m_bitmap)
2280 ::DeleteObject((HBITMAP) m_bitmap);
2281 if (m_dc)
2282 ::DeleteDC((HDC) m_dc);
2283 }
2284
2285 wxDCCacheEntry* wxDC::FindBitmapInCache(WXHDC dc, int w, int h)
2286 {
2287 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2288 wxList::compatibility_iterator node = sm_bitmapCache.GetFirst();
2289 while (node)
2290 {
2291 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2292
2293 if (entry->m_depth == depth)
2294 {
2295 if (entry->m_width < w || entry->m_height < h)
2296 {
2297 ::DeleteObject((HBITMAP) entry->m_bitmap);
2298 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2299 if ( !entry->m_bitmap)
2300 {
2301 wxLogLastError(wxT("CreateCompatibleBitmap"));
2302 }
2303 entry->m_width = w; entry->m_height = h;
2304 return entry;
2305 }
2306 return entry;
2307 }
2308
2309 node = node->GetNext();
2310 }
2311 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2312 if ( !hBitmap)
2313 {
2314 wxLogLastError(wxT("CreateCompatibleBitmap"));
2315 }
2316 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2317 AddToBitmapCache(entry);
2318 return entry;
2319 }
2320
2321 wxDCCacheEntry* wxDC::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
2322 {
2323 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2324 wxList::compatibility_iterator node = sm_dcCache.GetFirst();
2325 while (node)
2326 {
2327 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2328
2329 // Don't return the same one as we already have
2330 if (!notThis || (notThis != entry))
2331 {
2332 if (entry->m_depth == depth)
2333 {
2334 return entry;
2335 }
2336 }
2337
2338 node = node->GetNext();
2339 }
2340 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2341 if ( !hDC)
2342 {
2343 wxLogLastError(wxT("CreateCompatibleDC"));
2344 }
2345 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2346 AddToDCCache(entry);
2347 return entry;
2348 }
2349
2350 void wxDC::AddToBitmapCache(wxDCCacheEntry* entry)
2351 {
2352 sm_bitmapCache.Append(entry);
2353 }
2354
2355 void wxDC::AddToDCCache(wxDCCacheEntry* entry)
2356 {
2357 sm_dcCache.Append(entry);
2358 }
2359
2360 void wxDC::ClearCache()
2361 {
2362 WX_CLEAR_LIST(wxList, sm_dcCache);
2363 WX_CLEAR_LIST(wxList, sm_bitmapCache);
2364 }
2365
2366 // Clean up cache at app exit
2367 class wxDCModule : public wxModule
2368 {
2369 public:
2370 virtual bool OnInit() { return true; }
2371 virtual void OnExit() { wxDC::ClearCache(); }
2372
2373 private:
2374 DECLARE_DYNAMIC_CLASS(wxDCModule)
2375 };
2376
2377 IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2378
2379 #endif // wxUSE_DC_CACHEING
2380
2381 // ----------------------------------------------------------------------------
2382 // alpha channel support
2383 // ----------------------------------------------------------------------------
2384
2385 static bool AlphaBlt(HDC hdcDst,
2386 int x, int y, int width, int height,
2387 HDC hdcSrc,
2388 const wxBitmap& bmp)
2389 {
2390 wxASSERT_MSG( bmp.Ok() && bmp.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2391 wxASSERT_MSG( hdcDst && hdcSrc, _T("AlphaBlt(): invalid HDC") );
2392
2393 // do we have AlphaBlend() and company in the headers?
2394 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2395 // yes, now try to see if we have it during run-time
2396 typedef BOOL (WINAPI *AlphaBlend_t)(HDC,int,int,int,int,
2397 HDC,int,int,int,int,
2398 BLENDFUNCTION);
2399
2400 // bitmaps can be drawn only from GUI thread so there is no need to
2401 // protect this static variable from multiple threads
2402 static bool s_triedToLoad = false;
2403 static AlphaBlend_t pfnAlphaBlend = NULL;
2404 if ( !s_triedToLoad )
2405 {
2406 s_triedToLoad = true;
2407
2408 // don't give errors about the DLL being unavailable, we're
2409 // prepared to handle this
2410 wxLogNull nolog;
2411
2412 wxDynamicLibrary dll(_T("msimg32.dll"));
2413 if ( dll.IsLoaded() )
2414 {
2415 pfnAlphaBlend = (AlphaBlend_t)dll.GetSymbol(_T("AlphaBlend"));
2416 if ( pfnAlphaBlend )
2417 {
2418 // we must keep the DLL loaded if we want to be able to
2419 // call AlphaBlend() so just never unload it at all, not a
2420 // big deal
2421 dll.Detach();
2422 }
2423 }
2424 }
2425
2426 if ( pfnAlphaBlend )
2427 {
2428 BLENDFUNCTION bf;
2429 bf.BlendOp = AC_SRC_OVER;
2430 bf.BlendFlags = 0;
2431 bf.SourceConstantAlpha = 0xff;
2432 bf.AlphaFormat = AC_SRC_ALPHA;
2433
2434 if ( pfnAlphaBlend(hdcDst, x, y, width, height,
2435 hdcSrc, 0, 0, width, height,
2436 bf) )
2437 {
2438 // skip wxAlphaBlend() call below
2439 return true;
2440 }
2441
2442 wxLogLastError(_T("AlphaBlend"));
2443 }
2444 #endif // defined(AC_SRC_OVER)
2445
2446 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2447 // implementation
2448 #ifdef wxHAVE_RAW_BITMAP
2449 wxAlphaBlend(hdcDst, x, y, width, height, bmp);
2450
2451 return true;
2452 #else // !wxHAVE_RAW_BITMAP
2453 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2454 // alpha but at least something will be shown like this)
2455 wxUnusedVar(bmp);
2456 return false;
2457 #endif // wxHAVE_RAW_BITMAP
2458 }
2459
2460
2461 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2462 #ifdef wxHAVE_RAW_BITMAP
2463
2464 static void
2465 wxAlphaBlend(HDC hdcDst, int xDst, int yDst, int w, int h, const wxBitmap& bmpSrc)
2466 {
2467 // get the destination DC pixels
2468 wxBitmap bmpDst(w, h, 32 /* force creating RGBA DIB */);
2469 MemoryHDC hdcMem;
2470 SelectInHDC select(hdcMem, GetHbitmapOf(bmpDst));
2471
2472 if ( !::BitBlt(hdcMem, 0, 0, w, h, hdcDst, 0, 0, SRCCOPY) )
2473 {
2474 wxLogLastError(_T("BitBlt"));
2475 }
2476
2477 // combine them with the source bitmap using alpha
2478 wxAlphaPixelData dataDst(bmpDst),
2479 dataSrc((wxBitmap &)bmpSrc);
2480
2481 wxCHECK_RET( dataDst && dataSrc,
2482 _T("failed to get raw data in wxAlphaBlend") );
2483
2484 wxAlphaPixelData::Iterator pDst(dataDst),
2485 pSrc(dataSrc);
2486
2487 for ( int y = 0; y < h; y++ )
2488 {
2489 wxAlphaPixelData::Iterator pDstRowStart = pDst,
2490 pSrcRowStart = pSrc;
2491
2492 for ( int x = 0; x < w; x++ )
2493 {
2494 // note that source bitmap uses premultiplied alpha (as required by
2495 // the real AlphaBlend)
2496 const unsigned beta = 255 - pSrc.Alpha();
2497
2498 pDst.Red() = pSrc.Red() + (beta * pDst.Red() + 127) / 255;
2499 pDst.Blue() = pSrc.Blue() + (beta * pDst.Blue() + 127) / 255;
2500 pDst.Green() = pSrc.Green() + (beta * pDst.Green() + 127) / 255;
2501
2502 ++pDst;
2503 ++pSrc;
2504 }
2505
2506 pDst = pDstRowStart;
2507 pSrc = pSrcRowStart;
2508 pDst.OffsetY(dataDst, 1);
2509 pSrc.OffsetY(dataSrc, 1);
2510 }
2511
2512 // and finally blit them back to the destination DC
2513 if ( !::BitBlt(hdcDst, xDst, yDst, w, h, hdcMem, 0, 0, SRCCOPY) )
2514 {
2515 wxLogLastError(_T("BitBlt"));
2516 }
2517 }
2518
2519 #endif // #ifdef wxHAVE_RAW_BITMAP