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