]> git.saurik.com Git - wxWidgets.git/blame - src/msw/dc.cpp
No code changes, fixed some typos.
[wxWidgets.git] / src / msw / dc.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
ad0ac642
WS
2// Name: src/msw/dc.cpp
3// Purpose: wxDC class for MSW port
2bda0e17
KB
4// Author: Julian Smart
5// Modified by:
6// Created: 01/02/97
7// RCS-ID: $Id$
6c9a19aa 8// Copyright: (c) Julian Smart
65571936 9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
a23fd0e1
VZ
12// ===========================================================================
13// declarations
14// ===========================================================================
15
16// ---------------------------------------------------------------------------
17// headers
18// ---------------------------------------------------------------------------
19
2bda0e17
KB
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
a23fd0e1 24 #pragma hdrstop
2bda0e17
KB
25#endif
26
27#ifndef WX_PRECOMP
57bd4c60 28 #include "wx/msw/wrapcdlg.h"
fb6724f1 29 #include "wx/image.h"
4286a5b5 30 #include "wx/window.h"
a23fd0e1
VZ
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"
0c589ad0 36 #include "wx/log.h"
4286a5b5 37 #include "wx/icon.h"
6d50343d 38 #include "wx/dcprint.h"
02761f6c 39 #include "wx/module.h"
2bda0e17
KB
40#endif
41
025f7d77 42#include "wx/msw/dc.h"
0cbff120 43#include "wx/sysopt.h"
19fc41a3 44#include "wx/dynlib.h"
1e3c12d7 45
3fb1e059 46#ifdef wxHAS_RAW_BITMAP
d8c89c48 47 #include "wx/rawbmp.h"
1e3c12d7 48#endif
2bda0e17
KB
49
50#include <string.h>
2bda0e17 51
d8c89c48
VZ
52#include "wx/msw/private/dc.h"
53
54using namespace wxMSWImpl;
2bda0e17 55
878711c0 56#ifndef AC_SRC_ALPHA
6ae7410f
VZ
57 #define AC_SRC_ALPHA 1
58#endif
59
60#ifndef LAYOUT_RTL
61 #define LAYOUT_RTL 1
878711c0
VZ
62#endif
63
3bce6687
JS
64/* Quaternary raster codes */
65#ifndef MAKEROP4
66#define MAKEROP4(fore,back) (DWORD)((((back) << 8) & 0xFF000000) | (fore))
67#endif
68
82a306b7
VZ
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
888dde65 79IMPLEMENT_ABSTRACT_CLASS(wxMSWDCImpl, wxDCImpl)
2bda0e17 80
a23fd0e1
VZ
81// ---------------------------------------------------------------------------
82// constants
83// ---------------------------------------------------------------------------
84
dfc2e496 85static const int VIEWPORT_EXTENT = 1024;
42e69d6b 86
4aff28fc
VZ
87// ROPs which don't have standard names (see "Ternary Raster Operations" in the
88// MSDN docs for how this and other numbers in wxDC::Blit() are obtained)
89#define DSTCOPY 0x00AA0029 // a.k.a. NOP operation
90
1e2081a1
VZ
91// ----------------------------------------------------------------------------
92// macros for logical <-> device coords conversion
93// ----------------------------------------------------------------------------
94
95/*
96 We currently let Windows do all the translations itself so these macros are
97 not really needed (any more) but keep them to enhance readability of the
98 code by allowing to see where are the logical and where are the device
99 coordinates used.
100 */
101
1a4b50d2 102#ifdef __WXWINCE__
1d63de4a
JS
103 #define XLOG2DEV(x) ((x-m_logicalOriginX)*m_signX)
104 #define YLOG2DEV(y) ((y-m_logicalOriginY)*m_signY)
105 #define XDEV2LOG(x) ((x)*m_signX+m_logicalOriginX)
106 #define YDEV2LOG(y) ((y)*m_signY+m_logicalOriginY)
1a4b50d2
RR
107#else
108 #define XLOG2DEV(x) (x)
109 #define YLOG2DEV(y) (y)
110 #define XDEV2LOG(x) (x)
111 #define YDEV2LOG(y) (y)
112#endif
1e2081a1 113
4314ec48
VZ
114// ---------------------------------------------------------------------------
115// private functions
116// ---------------------------------------------------------------------------
117
118// convert degrees to radians
119static inline double DegToRad(double deg) { return (deg * M_PI) / 180.0; }
120
275a63e3
VZ
121// call AlphaBlend() to blit contents of hdcSrc to hdcDst using alpha
122//
123// NB: bmpSrc is the bitmap selected in hdcSrc, it is not really needed
124// to pass it to this function but as we already have it at the point
125// of call anyhow we do
126//
127// return true if we could draw the bitmap in one way or the other, false
128// otherwise
129static bool AlphaBlt(HDC hdcDst,
e3b81044 130 int x, int y, int dstWidth, int dstHeight,
1ee280b7 131 int srcX, int srcY,
e3b81044
VZ
132 int srcWidth, int srcHeight,
133 HDC hdcSrc,
134 const wxBitmap& bmp);
1e3c12d7 135
3fb1e059 136#ifdef wxHAS_RAW_BITMAP
761598d4
VZ
137
138// our (limited) AlphaBlend() replacement for Windows versions not providing it
878711c0 139static void
e3b81044
VZ
140wxAlphaBlend(HDC hdcDst, int xDst, int yDst,
141 int dstWidth, int dstHeight,
1ee280b7 142 int srcX, int srcY,
e3b81044
VZ
143 int srcWidth, int srcHeight,
144 const wxBitmap& bmpSrc);
761598d4 145
3fb1e059 146#endif // wxHAS_RAW_BITMAP
878711c0 147
f6bcfd97
BP
148// ----------------------------------------------------------------------------
149// private classes
150// ----------------------------------------------------------------------------
151
152// instead of duplicating the same code which sets and then restores text
153// colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
154// encapsulate this in a small helper class
155
76f91e77 156// wxBrushAttrsSetter: changes the text colours in the ctor if required and
f6bcfd97 157// restores them in the dtor
76f91e77
VZ
158class wxBrushAttrsSetter : private wxBkModeChanger,
159 private wxTextColoursChanger
f6bcfd97
BP
160{
161public:
76f91e77 162 wxBrushAttrsSetter(wxMSWDCImpl& dc);
f6bcfd97
BP
163
164private:
c0c133e1 165 wxDECLARE_NO_COPY_CLASS(wxBrushAttrsSetter);
f6bcfd97
BP
166};
167
4f64fde7
VZ
168#ifdef __WXWINCE__
169
170#define SET_STRETCH_BLT_MODE(hdc)
171
172#else // !__WXWINCE__
173
174// this class sets the stretch blit mode to COLORONCOLOR during its lifetime
175//
176// don't use it directly, use SET_STRETCH_BLT_MODE() macro instead as it
177// expands to nothing under WinCE which doesn't have SetStretchBltMode()
f178ab7e
VZ
178class StretchBltModeChanger
179{
180public:
4f64fde7 181 StretchBltModeChanger(HDC hdc)
f178ab7e
VZ
182 : m_hdc(hdc)
183 {
4f64fde7 184 m_modeOld = ::SetStretchBltMode(m_hdc, COLORONCOLOR);
f178ab7e 185 if ( !m_modeOld )
43b2d5e7 186 {
9a83f860 187 wxLogLastError(wxT("SetStretchBltMode"));
43b2d5e7 188 }
f178ab7e
VZ
189 }
190
191 ~StretchBltModeChanger()
192 {
193 if ( !::SetStretchBltMode(m_hdc, m_modeOld) )
43b2d5e7 194 {
9a83f860 195 wxLogLastError(wxT("SetStretchBltMode"));
43b2d5e7 196 }
f178ab7e
VZ
197 }
198
199private:
200 const HDC m_hdc;
201
202 int m_modeOld;
2eb10e2a 203
c0c133e1 204 wxDECLARE_NO_COPY_CLASS(StretchBltModeChanger);
f178ab7e
VZ
205};
206
4f64fde7
VZ
207#define SET_STRETCH_BLT_MODE(hdc) \
208 StretchBltModeChanger wxMAKE_UNIQUE_NAME(stretchModeChanger)(hdc)
209
210#endif // __WXWINCE__/!__WXWINCE__
211
a8ff046b
VZ
212#if wxUSE_DYNLIB_CLASS
213
6ae7410f
VZ
214// helper class to cache dynamically loaded libraries and not attempt reloading
215// them if it fails
216class wxOnceOnlyDLLLoader
213ad8e7
VZ
217{
218public:
6ae7410f
VZ
219 // ctor argument must be a literal string as we don't make a copy of it!
220 wxOnceOnlyDLLLoader(const wxChar *dllName)
221 : m_dllName(dllName)
213ad8e7 222 {
47b378bd 223 }
6ae7410f
VZ
224
225
226 // return the symbol with the given name or NULL if the DLL not loaded
227 // or symbol not present
228 void *GetSymbol(const wxChar *name)
229 {
230 // we're prepared to handle errors here
213ad8e7
VZ
231 wxLogNull noLog;
232
6ae7410f 233 if ( m_dllName )
213ad8e7 234 {
6ae7410f
VZ
235 m_dll.Load(m_dllName);
236
237 // reset the name whether we succeeded or failed so that we don't
238 // try again the next time
239 m_dllName = NULL;
213ad8e7
VZ
240 }
241
6ae7410f 242 return m_dll.IsLoaded() ? m_dll.GetSymbol(name) : NULL;
213ad8e7
VZ
243 }
244
60b0c3b4
VZ
245 void Unload()
246 {
247 if ( m_dll.IsLoaded() )
248 {
249 m_dll.Unload();
250 }
251 }
252
213ad8e7 253private:
6ae7410f
VZ
254 wxDynamicLibrary m_dll;
255 const wxChar *m_dllName;
213ad8e7
VZ
256};
257
9a83f860 258static wxOnceOnlyDLLLoader wxMSIMG32DLL(wxT("msimg32"));
213ad8e7 259
60b0c3b4
VZ
260// we must ensure that DLLs are unloaded before the static objects cleanup time
261// because we may hit the notorious DllMain() dead lock in this case if wx is
262// used as a DLL (attempting to unload another DLL from inside DllMain() hangs
263// under Windows because it tries to reacquire the same lock)
264class wxGDIDLLsCleanupModule : public wxModule
265{
266public:
267 virtual bool OnInit() { return true; }
268 virtual void OnExit() { wxMSIMG32DLL.Unload(); }
269
270private:
271 DECLARE_DYNAMIC_CLASS(wxGDIDLLsCleanupModule)
272};
273
274IMPLEMENT_DYNAMIC_CLASS(wxGDIDLLsCleanupModule, wxModule)
275
e71508e1
VZ
276namespace
277{
278
279#if wxUSE_DC_TRANSFORM_MATRIX
280
281// Class used to dynamically load world transform related API functions.
282class GdiWorldTransformFuncs
283{
284public:
285 static bool IsOk()
286 {
287 if ( !ms_worldTransformSymbolsLoaded )
288 LoadWorldTransformSymbols();
289
290 return ms_pfnSetGraphicsMode &&
291 ms_pfnSetWorldTransform &&
292 ms_pfnGetWorldTransform &&
293 ms_pfnModifyWorldTransform;
294 }
295
296 typedef int (WINAPI *SetGraphicsMode_t)(HDC, int);
297 static SetGraphicsMode_t SetGraphicsMode()
298 {
299 if ( !ms_worldTransformSymbolsLoaded )
300 LoadWorldTransformSymbols();
301
302 return ms_pfnSetGraphicsMode;
303 }
304
305 typedef BOOL (WINAPI *SetWorldTransform_t)(HDC, const XFORM *);
306 static SetWorldTransform_t SetWorldTransform()
307 {
308 if ( !ms_worldTransformSymbolsLoaded )
309 LoadWorldTransformSymbols();
310
311 return ms_pfnSetWorldTransform;
312 }
313
314 typedef BOOL (WINAPI *GetWorldTransform_t)(HDC, LPXFORM);
315 static GetWorldTransform_t GetWorldTransform()
316 {
317 if ( !ms_worldTransformSymbolsLoaded )
318 LoadWorldTransformSymbols();
319
320 return ms_pfnGetWorldTransform;
321 }
322
323 typedef BOOL (WINAPI *ModifyWorldTransform_t)(HDC, const XFORM *, DWORD);
324 static ModifyWorldTransform_t ModifyWorldTransform()
325 {
326 if ( !ms_worldTransformSymbolsLoaded )
327 LoadWorldTransformSymbols();
328
329 return ms_pfnModifyWorldTransform;
330 }
331
332private:
333 static void LoadWorldTransformSymbols()
334 {
335 wxDynamicLibrary dll(wxT("gdi32.dll"));
336
337 wxDL_INIT_FUNC(ms_pfn, SetGraphicsMode, dll);
338 wxDL_INIT_FUNC(ms_pfn, SetWorldTransform, dll);
339 wxDL_INIT_FUNC(ms_pfn, GetWorldTransform, dll);
340 wxDL_INIT_FUNC(ms_pfn, ModifyWorldTransform, dll);
341
342 ms_worldTransformSymbolsLoaded = true;
343 }
344
345 static SetGraphicsMode_t ms_pfnSetGraphicsMode;
346 static SetWorldTransform_t ms_pfnSetWorldTransform;
347 static GetWorldTransform_t ms_pfnGetWorldTransform;
348 static ModifyWorldTransform_t ms_pfnModifyWorldTransform;
349
350 static bool ms_worldTransformSymbolsLoaded;
351};
352
353GdiWorldTransformFuncs::SetGraphicsMode_t
354 GdiWorldTransformFuncs::ms_pfnSetGraphicsMode = NULL;
355GdiWorldTransformFuncs::SetWorldTransform_t
356 GdiWorldTransformFuncs::ms_pfnSetWorldTransform = NULL;
357GdiWorldTransformFuncs::GetWorldTransform_t
358 GdiWorldTransformFuncs::ms_pfnGetWorldTransform = NULL;
359GdiWorldTransformFuncs::ModifyWorldTransform_t
360 GdiWorldTransformFuncs::ms_pfnModifyWorldTransform = NULL;
361
362bool GdiWorldTransformFuncs::ms_worldTransformSymbolsLoaded = false;
363
364#endif // wxUSE_DC_TRANSFORM_MATRIX
365
366} // anonymous namespace
367
a8ff046b
VZ
368#endif // wxUSE_DYNLIB_CLASS
369
a23fd0e1
VZ
370// ===========================================================================
371// implementation
372// ===========================================================================
373
f6bcfd97 374// ----------------------------------------------------------------------------
76f91e77 375// wxBrushAttrsSetter
f6bcfd97
BP
376// ----------------------------------------------------------------------------
377
76f91e77
VZ
378wxBrushAttrsSetter::wxBrushAttrsSetter(wxMSWDCImpl& dc)
379 : wxBkModeChanger(GetHdcOf(dc)),
380 wxTextColoursChanger(GetHdcOf(dc))
f6bcfd97 381{
cafbad8f 382 const wxBrush& brush = dc.GetBrush();
cb129171 383 if ( brush.IsOk() && brush.GetStyle() == wxBRUSHSTYLE_STIPPLE_MASK_OPAQUE )
f6bcfd97 384 {
77ffb593 385 // note that Windows convention is opposite to wxWidgets one, this is
f6bcfd97 386 // why text colour becomes the background one and vice versa
76f91e77
VZ
387 wxTextColoursChanger::Change(dc.GetTextBackground(),
388 dc.GetTextForeground());
f6bcfd97 389
76f91e77 390 wxBkModeChanger::Change(dc.GetBackgroundMode());
f6bcfd97
BP
391 }
392}
393
f5725872
VZ
394// ----------------------------------------------------------------------------
395// wxDC MSW-specific methods
396// ----------------------------------------------------------------------------
397
398WXHDC wxDC::GetHDC() const
399{
400 wxMSWDCImpl * const impl = wxDynamicCast(GetImpl(), wxMSWDCImpl);
401 return impl ? impl->GetHDC() : 0;
402}
403
a23fd0e1 404// ---------------------------------------------------------------------------
888dde65 405// wxMSWDCImpl
a23fd0e1 406// ---------------------------------------------------------------------------
2bda0e17 407
1ee280b7 408wxMSWDCImpl::wxMSWDCImpl( wxDC *owner, WXHDC hDC ) :
888dde65 409 wxDCImpl( owner )
1ee280b7
VZ
410{
411 Init();
412 m_hDC = hDC;
888dde65
RR
413}
414
415wxMSWDCImpl::~wxMSWDCImpl()
2bda0e17 416{
7ba4fbeb
VZ
417 if ( m_hDC != 0 )
418 {
7bcb11d3 419 SelectOldObjects(m_hDC);
7ba4fbeb
VZ
420
421 // if we own the HDC, we delete it, otherwise we just release it
422
423 if ( m_bOwnsDC )
424 {
425 ::DeleteDC(GetHdc());
7bcb11d3 426 }
7ba4fbeb
VZ
427 else // we don't own our HDC
428 {
888dde65 429 if (m_window)
db400410 430 {
888dde65 431 ::ReleaseDC(GetHwndOf(m_window), GetHdc());
db400410
JS
432 }
433 else
434 {
435 // Must have been a wxScreenDC
436 ::ReleaseDC((HWND) NULL, GetHdc());
437 }
7ba4fbeb
VZ
438 }
439 }
2bda0e17
KB
440}
441
442// This will select current objects out of the DC,
443// which is what you have to do before deleting the
444// DC.
888dde65 445void wxMSWDCImpl::SelectOldObjects(WXHDC dc)
2bda0e17 446{
7bcb11d3 447 if (dc)
2bda0e17 448 {
7bcb11d3
JS
449 if (m_oldBitmap)
450 {
451 ::SelectObject((HDC) dc, (HBITMAP) m_oldBitmap);
87d3576f 452 if (m_selectedBitmap.IsOk())
7bcb11d3
JS
453 {
454 m_selectedBitmap.SetSelectedInto(NULL);
455 }
456 }
a23fd0e1 457 m_oldBitmap = 0;
7bcb11d3
JS
458 if (m_oldPen)
459 {
460 ::SelectObject((HDC) dc, (HPEN) m_oldPen);
461 }
a23fd0e1 462 m_oldPen = 0;
7bcb11d3
JS
463 if (m_oldBrush)
464 {
465 ::SelectObject((HDC) dc, (HBRUSH) m_oldBrush);
466 }
a23fd0e1 467 m_oldBrush = 0;
7bcb11d3
JS
468 if (m_oldFont)
469 {
470 ::SelectObject((HDC) dc, (HFONT) m_oldFont);
471 }
a23fd0e1 472 m_oldFont = 0;
d275c7eb
VZ
473
474#if wxUSE_PALETTE
7bcb11d3
JS
475 if (m_oldPalette)
476 {
19193a2c 477 ::SelectPalette((HDC) dc, (HPALETTE) m_oldPalette, FALSE);
7bcb11d3 478 }
a23fd0e1 479 m_oldPalette = 0;
d275c7eb 480#endif // wxUSE_PALETTE
2bda0e17 481 }
a23fd0e1
VZ
482
483 m_brush = wxNullBrush;
7bcb11d3 484 m_pen = wxNullPen;
d275c7eb 485#if wxUSE_PALETTE
7bcb11d3 486 m_palette = wxNullPalette;
d275c7eb 487#endif // wxUSE_PALETTE
7bcb11d3
JS
488 m_font = wxNullFont;
489 m_backgroundBrush = wxNullBrush;
490 m_selectedBitmap = wxNullBitmap;
2bda0e17
KB
491}
492
a23fd0e1
VZ
493// ---------------------------------------------------------------------------
494// clipping
495// ---------------------------------------------------------------------------
496
888dde65 497void wxMSWDCImpl::UpdateClipBox()
1e6feb95 498{
82a306b7 499 WXMICROWIN_CHECK_HDC
d275c7eb 500
1e6feb95 501 RECT rect;
5230934a 502 ::GetClipBox(GetHdc(), &rect);
1e6feb95
VZ
503
504 m_clipX1 = (wxCoord) XDEV2LOG(rect.left);
505 m_clipY1 = (wxCoord) YDEV2LOG(rect.top);
506 m_clipX2 = (wxCoord) XDEV2LOG(rect.right);
507 m_clipY2 = (wxCoord) YDEV2LOG(rect.bottom);
f547e9bb
RL
508}
509
2e76da54 510void
888dde65 511wxMSWDCImpl::DoGetClippingBox(wxCoord *x, wxCoord *y, wxCoord *w, wxCoord *h) const
2e76da54
VZ
512{
513 // check if we should try to retrieve the clipping region possibly not set
514 // by our SetClippingRegion() but preset by Windows:this can only happen
515 // when we're associated with an existing HDC usign SetHDC(), see there
516 if ( m_clipping && !m_clipX1 && !m_clipX2 )
517 {
888dde65 518 wxMSWDCImpl *self = wxConstCast(this, wxMSWDCImpl);
395b914e 519 self->UpdateClipBox();
2e76da54
VZ
520
521 if ( !m_clipX1 && !m_clipX2 )
395b914e 522 self->m_clipping = false;
2e76da54
VZ
523 }
524
888dde65 525 wxDCImpl::DoGetClippingBox(x, y, w, h);
2e76da54
VZ
526}
527
fdaad94e 528// common part of DoSetClippingRegion() and DoSetDeviceClippingRegion()
888dde65 529void wxMSWDCImpl::SetClippingHrgn(WXHRGN hrgn)
2bda0e17 530{
5230934a
VZ
531 wxCHECK_RET( hrgn, wxT("invalid clipping region") );
532
82a306b7 533 WXMICROWIN_CHECK_HDC
5230934a
VZ
534
535 // note that we combine the new clipping region with the existing one: this
536 // is compatible with what the other ports do and is the documented
537 // behaviour now (starting with 2.3.3)
3a5bcc4d 538#if defined(__WXWINCE__)
5230934a
VZ
539 RECT rectClip;
540 if ( !::GetClipBox(GetHdc(), &rectClip) )
541 return;
542
1d63de4a
JS
543 // GetClipBox returns logical coordinates, so transform to device
544 rectClip.left = LogicalToDeviceX(rectClip.left);
545 rectClip.top = LogicalToDeviceY(rectClip.top);
546 rectClip.right = LogicalToDeviceX(rectClip.right);
547 rectClip.bottom = LogicalToDeviceY(rectClip.bottom);
548
5230934a
VZ
549 HRGN hrgnDest = ::CreateRectRgn(0, 0, 0, 0);
550 HRGN hrgnClipOld = ::CreateRectRgn(rectClip.left, rectClip.top,
551 rectClip.right, rectClip.bottom);
552
553 if ( ::CombineRgn(hrgnDest, hrgnClipOld, (HRGN)hrgn, RGN_AND) != ERROR )
554 {
555 ::SelectClipRgn(GetHdc(), hrgnDest);
556 }
557
558 ::DeleteObject(hrgnClipOld);
559 ::DeleteObject(hrgnDest);
3a5bcc4d 560#else // !WinCE
5230934a
VZ
561 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN)hrgn, RGN_AND) == ERROR )
562 {
9a83f860 563 wxLogLastError(wxT("ExtSelectClipRgn"));
5230934a
VZ
564
565 return;
566 }
3a5bcc4d 567#endif // WinCE/!WinCE
d275c7eb 568
beb966c5 569 m_clipping = true;
40a89076 570
5230934a
VZ
571 UpdateClipBox();
572}
573
888dde65 574void wxMSWDCImpl::DoSetClippingRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
5230934a 575{
c4218a74
VZ
576 // the region coords are always the device ones, so do the translation
577 // manually
1e6feb95
VZ
578 //
579 // FIXME: possible +/-1 error here, to check!
c4218a74
VZ
580 HRGN hrgn = ::CreateRectRgn(LogicalToDeviceX(x),
581 LogicalToDeviceY(y),
582 LogicalToDeviceX(x + w),
583 LogicalToDeviceY(y + h));
40a89076
VZ
584 if ( !hrgn )
585 {
9a83f860 586 wxLogLastError(wxT("CreateRectRgn"));
40a89076
VZ
587 }
588 else
589 {
5230934a 590 SetClippingHrgn((WXHRGN)hrgn);
40a89076 591
5230934a 592 ::DeleteObject(hrgn);
40a89076 593 }
2bda0e17
KB
594}
595
fdaad94e 596void wxMSWDCImpl::DoSetDeviceClippingRegion(const wxRegion& region)
a724d789 597{
5230934a 598 SetClippingHrgn(region.GetHRGN());
2bda0e17
KB
599}
600
888dde65 601void wxMSWDCImpl::DestroyClippingRegion()
2bda0e17 602{
82a306b7 603 WXMICROWIN_CHECK_HDC
d275c7eb 604
7bcb11d3
JS
605 if (m_clipping && m_hDC)
606 {
07225d48
JS
607#if 1
608 // On a PocketPC device (not necessarily emulator), resetting
609 // the clip region as per the old method causes bad display
610 // problems. In fact setting a null region is probably OK
611 // on desktop WIN32 also, since the WIN32 docs imply that the user
612 // clipping region is independent from the paint clipping region.
613 ::SelectClipRgn(GetHdc(), 0);
3cdcf4d4 614#else
7bcb11d3 615 // TODO: this should restore the previous clipping region,
5230934a
VZ
616 // so that OnPaint processing works correctly, and the update
617 // clipping region doesn't get destroyed after the first
618 // DestroyClippingRegion.
7bcb11d3 619 HRGN rgn = CreateRectRgn(0, 0, 32000, 32000);
5230934a
VZ
620 ::SelectClipRgn(GetHdc(), rgn);
621 ::DeleteObject(rgn);
3cdcf4d4 622#endif
7bcb11d3 623 }
1e6feb95 624
888dde65 625 wxDCImpl::DestroyClippingRegion();
2bda0e17
KB
626}
627
a23fd0e1
VZ
628// ---------------------------------------------------------------------------
629// query capabilities
630// ---------------------------------------------------------------------------
631
888dde65 632bool wxMSWDCImpl::CanDrawBitmap() const
2bda0e17 633{
beb966c5 634 return true;
2bda0e17
KB
635}
636
888dde65 637bool wxMSWDCImpl::CanGetTextExtent() const
2bda0e17 638{
04ef50df
JS
639#ifdef __WXMICROWIN__
640 // TODO Extend MicroWindows' GetDeviceCaps function
beb966c5 641 return true;
04ef50df 642#else
7bcb11d3 643 // What sort of display is it?
a23fd0e1
VZ
644 int technology = ::GetDeviceCaps(GetHdc(), TECHNOLOGY);
645
646 return (technology == DT_RASDISPLAY) || (technology == DT_RASPRINTER);
04ef50df 647#endif
2bda0e17
KB
648}
649
888dde65 650int wxMSWDCImpl::GetDepth() const
2bda0e17 651{
82a306b7 652 WXMICROWIN_CHECK_HDC_RET(16)
d275c7eb 653
a23fd0e1 654 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL);
2bda0e17
KB
655}
656
a23fd0e1
VZ
657// ---------------------------------------------------------------------------
658// drawing
659// ---------------------------------------------------------------------------
660
888dde65 661void wxMSWDCImpl::Clear()
2bda0e17 662{
82a306b7 663 WXMICROWIN_CHECK_HDC
d275c7eb 664
7bcb11d3 665 RECT rect;
888dde65 666 if (m_window)
2506aab6 667 {
888dde65 668 GetClientRect((HWND) m_window->GetHWND(), &rect);
2506aab6
VZ
669 }
670 else
7bcb11d3 671 {
eaeb6a3c
JS
672 // No, I think we should simply ignore this if printing on e.g.
673 // a printer DC.
87d3576f
RR
674 // wxCHECK_RET( m_selectedBitmap.IsOk(), wxT("this DC can't be cleared") );
675 if (!m_selectedBitmap.IsOk())
eaeb6a3c 676 return;
2506aab6 677
dbe6f5f0
RD
678 rect.left = -m_deviceOriginX; rect.top = -m_deviceOriginY;
679 rect.right = m_selectedBitmap.GetWidth()-m_deviceOriginX;
680 rect.bottom = m_selectedBitmap.GetHeight()-m_deviceOriginY;
7bcb11d3 681 }
2506aab6 682
4676948b 683#ifndef __WXWINCE__
a23fd0e1 684 (void) ::SetMapMode(GetHdc(), MM_TEXT);
4676948b 685#endif
a23fd0e1 686
1e2081a1
VZ
687 DWORD colour = ::GetBkColor(GetHdc());
688 HBRUSH brush = ::CreateSolidBrush(colour);
689 ::FillRect(GetHdc(), &rect, brush);
690 ::DeleteObject(brush);
691
04ab8b6d 692 RealizeScaleAndOrigin();
a23fd0e1
VZ
693}
694
888dde65 695bool wxMSWDCImpl::DoFloodFill(wxCoord WXUNUSED_IN_WINCE(x),
0c0d1521
WS
696 wxCoord WXUNUSED_IN_WINCE(y),
697 const wxColour& WXUNUSED_IN_WINCE(col),
89efaf2b 698 wxFloodFillStyle WXUNUSED_IN_WINCE(style))
a23fd0e1 699{
4676948b 700#ifdef __WXWINCE__
beb966c5 701 return false;
4676948b 702#else
beb966c5 703 WXMICROWIN_CHECK_HDC_RET(false)
d275c7eb 704
387ebd3e 705 bool success = (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x), YLOG2DEV(y),
0bafad0c
VZ
706 col.GetPixel(),
707 style == wxFLOOD_SURFACE ? FLOODFILLSURFACE
387ebd3e
JS
708 : FLOODFILLBORDER) ) ;
709 if (!success)
0bafad0c
VZ
710 {
711 // quoting from the MSDN docs:
712 //
713 // Following are some of the reasons this function might fail:
714 //
715 // * The filling could not be completed.
716 // * The specified point has the boundary color specified by the
717 // crColor parameter (if FLOODFILLBORDER was requested).
718 // * The specified point does not have the color specified by
719 // crColor (if FLOODFILLSURFACE was requested)
720 // * The point is outside the clipping region that is, it is not
721 // visible on the device.
722 //
f6bcfd97 723 wxLogLastError(wxT("ExtFloodFill"));
0bafad0c 724 }
a23fd0e1 725
7bcb11d3 726 CalcBoundingBox(x, y);
419430a0 727
387ebd3e 728 return success;
4676948b 729#endif
2bda0e17
KB
730}
731
888dde65 732bool wxMSWDCImpl::DoGetPixel(wxCoord x, wxCoord y, wxColour *col) const
2bda0e17 733{
beb966c5 734 WXMICROWIN_CHECK_HDC_RET(false)
d275c7eb 735
9a83f860 736 wxCHECK_MSG( col, false, wxT("NULL colour parameter in wxMSWDCImpl::GetPixel") );
f6bcfd97 737
7bcb11d3 738 // get the color of the pixel
a23fd0e1 739 COLORREF pixelcolor = ::GetPixel(GetHdc(), XLOG2DEV(x), YLOG2DEV(y));
0bafad0c 740
f6bcfd97 741 wxRGBToColour(*col, pixelcolor);
dc1efb1d 742
beb966c5 743 return true;
2bda0e17
KB
744}
745
888dde65 746void wxMSWDCImpl::DoCrossHair(wxCoord x, wxCoord y)
a23fd0e1 747{
82a306b7 748 WXMICROWIN_CHECK_HDC
d275c7eb 749
72cdf4c9
VZ
750 wxCoord x1 = x-VIEWPORT_EXTENT;
751 wxCoord y1 = y-VIEWPORT_EXTENT;
752 wxCoord x2 = x+VIEWPORT_EXTENT;
753 wxCoord y2 = y+VIEWPORT_EXTENT;
a23fd0e1 754
4676948b
JS
755 wxDrawLine(GetHdc(), XLOG2DEV(x1), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y));
756 wxDrawLine(GetHdc(), XLOG2DEV(x), YLOG2DEV(y1), XLOG2DEV(x), YLOG2DEV(y2));
a23fd0e1 757
7bcb11d3
JS
758 CalcBoundingBox(x1, y1);
759 CalcBoundingBox(x2, y2);
2bda0e17
KB
760}
761
888dde65 762void wxMSWDCImpl::DoDrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
2bda0e17 763{
82a306b7 764 WXMICROWIN_CHECK_HDC
d275c7eb 765
4676948b 766 wxDrawLine(GetHdc(), XLOG2DEV(x1), YLOG2DEV(y1), XLOG2DEV(x2), YLOG2DEV(y2));
a23fd0e1 767
7bcb11d3
JS
768 CalcBoundingBox(x1, y1);
769 CalcBoundingBox(x2, y2);
2bda0e17
KB
770}
771
f6bcfd97
BP
772// Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
773// and ending at (x2, y2)
888dde65 774void wxMSWDCImpl::DoDrawArc(wxCoord x1, wxCoord y1,
f6bcfd97
BP
775 wxCoord x2, wxCoord y2,
776 wxCoord xc, wxCoord yc)
2bda0e17 777{
068f78bc
VZ
778 double dx = xc - x1;
779 double dy = yc - y1;
780 wxCoord r = (wxCoord)sqrt(dx*dx + dy*dy);
781
782
4676948b 783#ifdef __WXWINCE__
4f10962b 784 // Slower emulation since WinCE doesn't support Pie and Arc
4f10962b 785 double sa = acos((x1-xc)/r)/M_PI*180; // between 0 and 180
068f78bc
VZ
786 if( y1>yc )
787 sa = -sa; // below center
4f10962b
JS
788 double ea = atan2(yc-y2, x2-xc)/M_PI*180;
789 DoDrawEllipticArcRot( xc-r, yc-r, 2*r, 2*r, sa, ea );
4676948b
JS
790#else
791
82a306b7 792 WXMICROWIN_CHECK_HDC
d275c7eb 793
76f91e77 794 wxBrushAttrsSetter cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
2d8a5cb1 795
f6bcfd97
BP
796 // treat the special case of full circle separately
797 if ( x1 == x2 && y1 == y2 )
7bcb11d3 798 {
888dde65 799 GetOwner()->DrawEllipse(xc - r, yc - r, 2*r, 2*r);
a23fd0e1 800 return;
7bcb11d3 801 }
a23fd0e1 802
72cdf4c9
VZ
803 wxCoord xx1 = XLOG2DEV(x1);
804 wxCoord yy1 = YLOG2DEV(y1);
805 wxCoord xx2 = XLOG2DEV(x2);
806 wxCoord yy2 = YLOG2DEV(y2);
807 wxCoord xxc = XLOG2DEV(xc);
808 wxCoord yyc = YLOG2DEV(yc);
068f78bc
VZ
809 dx = xxc - xx1;
810 dy = yyc - yy1;
811 wxCoord ray = (wxCoord)sqrt(dx*dx + dy*dy);
a23fd0e1 812
72cdf4c9
VZ
813 wxCoord xxx1 = (wxCoord) (xxc-ray);
814 wxCoord yyy1 = (wxCoord) (yyc-ray);
815 wxCoord xxx2 = (wxCoord) (xxc+ray);
816 wxCoord yyy2 = (wxCoord) (yyc+ray);
f6bcfd97 817
e6777e65 818 if ( m_brush.IsNonTransparent() )
7bcb11d3
JS
819 {
820 // Have to add 1 to bottom-right corner of rectangle
821 // to make semi-circles look right (crooked line otherwise).
822 // Unfortunately this is not a reliable method, depends
823 // on the size of shape.
824 // TODO: figure out why this happens!
f6bcfd97 825 Pie(GetHdc(),xxx1,yyy1,xxx2+1,yyy2+1, xx1,yy1,xx2,yy2);
7bcb11d3
JS
826 }
827 else
2d8a5cb1 828 {
f6bcfd97 829 Arc(GetHdc(),xxx1,yyy1,xxx2,yyy2, xx1,yy1,xx2,yy2);
2d8a5cb1 830 }
f6bcfd97
BP
831
832 CalcBoundingBox(xc - r, yc - r);
833 CalcBoundingBox(xc + r, yc + r);
4676948b 834#endif
2bda0e17
KB
835}
836
888dde65 837void wxMSWDCImpl::DoDrawCheckMark(wxCoord x1, wxCoord y1,
cd9da200
VZ
838 wxCoord width, wxCoord height)
839{
b76d9e76
VZ
840 // cases when we don't have DrawFrameControl()
841#if defined(__SYMANTEC__) || defined(__WXMICROWIN__)
842 return wxDCBase::DoDrawCheckMark(x1, y1, width, height);
843#else // normal case
cd9da200
VZ
844 wxCoord x2 = x1 + width,
845 y2 = y1 + height;
846
cd9da200
VZ
847 RECT rect;
848 rect.left = x1;
849 rect.top = y1;
850 rect.right = x2;
851 rect.bottom = y2;
852
4676948b 853#ifdef __WXWINCE__
c73e37e2 854 DrawFrameControl(GetHdc(), &rect, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_CHECKED);
4676948b 855#else
cd9da200 856 DrawFrameControl(GetHdc(), &rect, DFC_MENU, DFCS_MENUCHECK);
4676948b 857#endif
cd9da200
VZ
858
859 CalcBoundingBox(x1, y1);
860 CalcBoundingBox(x2, y2);
b76d9e76 861#endif // Microwin/Normal
cd9da200
VZ
862}
863
888dde65 864void wxMSWDCImpl::DoDrawPoint(wxCoord x, wxCoord y)
2bda0e17 865{
82a306b7 866 WXMICROWIN_CHECK_HDC
d275c7eb 867
7bcb11d3 868 COLORREF color = 0x00ffffff;
87d3576f 869 if (m_pen.IsOk())
7bcb11d3 870 {
a23fd0e1 871 color = m_pen.GetColour().GetPixel();
7bcb11d3 872 }
a23fd0e1
VZ
873
874 SetPixel(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), color);
875
7bcb11d3 876 CalcBoundingBox(x, y);
2bda0e17
KB
877}
878
888dde65 879void wxMSWDCImpl::DoDrawPolygon(int n,
0c0d1521
WS
880 wxPoint points[],
881 wxCoord xoffset,
882 wxCoord yoffset,
89efaf2b 883 wxPolygonFillMode WXUNUSED_IN_WINCE(fillStyle))
2bda0e17 884{
82a306b7 885 WXMICROWIN_CHECK_HDC
d275c7eb 886
76f91e77 887 wxBrushAttrsSetter cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
de2d2cdc 888
7bcb11d3
JS
889 // Do things less efficiently if we have offsets
890 if (xoffset != 0 || yoffset != 0)
6a6c0a8b 891 {
7bcb11d3
JS
892 POINT *cpoints = new POINT[n];
893 int i;
894 for (i = 0; i < n; i++)
895 {
896 cpoints[i].x = (int)(points[i].x + xoffset);
897 cpoints[i].y = (int)(points[i].y + yoffset);
a23fd0e1 898
7bcb11d3
JS
899 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
900 }
4676948b 901#ifndef __WXWINCE__
a23fd0e1 902 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
4676948b 903#endif
a23fd0e1 904 (void)Polygon(GetHdc(), cpoints, n);
4676948b 905#ifndef __WXWINCE__
a23fd0e1 906 SetPolyFillMode(GetHdc(),prev);
4676948b 907#endif
7bcb11d3
JS
908 delete[] cpoints;
909 }
910 else
911 {
912 int i;
913 for (i = 0; i < n; i++)
914 CalcBoundingBox(points[i].x, points[i].y);
a23fd0e1 915
4676948b 916#ifndef __WXWINCE__
a23fd0e1 917 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
4676948b 918#endif
a23fd0e1 919 (void)Polygon(GetHdc(), (POINT*) points, n);
4676948b 920#ifndef __WXWINCE__
a23fd0e1 921 SetPolyFillMode(GetHdc(),prev);
4676948b 922#endif
6a6c0a8b 923 }
2bda0e17
KB
924}
925
6e76b35d 926void
888dde65 927wxMSWDCImpl::DoDrawPolyPolygon(int n,
793db755 928 int count[],
6e76b35d
VZ
929 wxPoint points[],
930 wxCoord xoffset,
931 wxCoord yoffset,
89efaf2b 932 wxPolygonFillMode fillStyle)
6e76b35d 933{
d61c1a6f 934#ifdef __WXWINCE__
2261baf7 935 wxDCImpl::DoDrawPolyPolygon(n, count, points, xoffset, yoffset, fillStyle);
35bbb0c6 936#else
82a306b7 937 WXMICROWIN_CHECK_HDC
6e76b35d 938
76f91e77 939 wxBrushAttrsSetter cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
6e76b35d
VZ
940 int i, cnt;
941 for (i = cnt = 0; i < n; i++)
793db755 942 cnt += count[i];
6e76b35d
VZ
943
944 // Do things less efficiently if we have offsets
945 if (xoffset != 0 || yoffset != 0)
946 {
947 POINT *cpoints = new POINT[cnt];
948 for (i = 0; i < cnt; i++)
949 {
950 cpoints[i].x = (int)(points[i].x + xoffset);
951 cpoints[i].y = (int)(points[i].y + yoffset);
952
953 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
954 }
d61c1a6f 955#ifndef __WXWINCE__
6e76b35d 956 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
35bbb0c6 957#endif
793db755 958 (void)PolyPolygon(GetHdc(), cpoints, count, n);
d61c1a6f 959#ifndef __WXWINCE__
6e76b35d 960 SetPolyFillMode(GetHdc(),prev);
35bbb0c6 961#endif
6e76b35d
VZ
962 delete[] cpoints;
963 }
964 else
965 {
966 for (i = 0; i < cnt; i++)
967 CalcBoundingBox(points[i].x, points[i].y);
968
d61c1a6f 969#ifndef __WXWINCE__
6e76b35d 970 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
35bbb0c6 971#endif
793db755 972 (void)PolyPolygon(GetHdc(), (POINT*) points, count, n);
d61c1a6f 973#ifndef __WXWINCE__
6e76b35d 974 SetPolyFillMode(GetHdc(),prev);
35bbb0c6 975#endif
6e76b35d 976 }
d61c1a6f
JS
977#endif
978 // __WXWINCE__
6e76b35d
VZ
979}
980
888dde65 981void wxMSWDCImpl::DoDrawLines(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset)
2bda0e17 982{
82a306b7 983 WXMICROWIN_CHECK_HDC
d275c7eb 984
7bcb11d3
JS
985 // Do things less efficiently if we have offsets
986 if (xoffset != 0 || yoffset != 0)
6a6c0a8b 987 {
7bcb11d3
JS
988 POINT *cpoints = new POINT[n];
989 int i;
990 for (i = 0; i < n; i++)
991 {
992 cpoints[i].x = (int)(points[i].x + xoffset);
993 cpoints[i].y = (int)(points[i].y + yoffset);
a23fd0e1 994
7bcb11d3
JS
995 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
996 }
a23fd0e1 997 (void)Polyline(GetHdc(), cpoints, n);
7bcb11d3
JS
998 delete[] cpoints;
999 }
1000 else
1001 {
1002 int i;
1003 for (i = 0; i < n; i++)
1004 CalcBoundingBox(points[i].x, points[i].y);
a23fd0e1
VZ
1005
1006 (void)Polyline(GetHdc(), (POINT*) points, n);
6a6c0a8b 1007 }
2bda0e17
KB
1008}
1009
888dde65 1010void wxMSWDCImpl::DoDrawRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
2bda0e17 1011{
82a306b7 1012 WXMICROWIN_CHECK_HDC
d275c7eb 1013
76f91e77 1014 wxBrushAttrsSetter cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
de2d2cdc 1015
72cdf4c9
VZ
1016 wxCoord x2 = x + width;
1017 wxCoord y2 = y + height;
a23fd0e1 1018
1ee280b7
VZ
1019 wxCoord x2dev = XLOG2DEV(x2),
1020 y2dev = YLOG2DEV(y2);
5456b916 1021
1ee280b7
VZ
1022 // Windows (but not Windows CE) draws the filled rectangles without outline
1023 // (i.e. drawn with a transparent pen) one pixel smaller in both directions
1024 // and we want them to have the same size regardless of which pen is used
750d64e6 1025#ifndef __WXWINCE__
e6777e65 1026 if ( m_pen.IsTransparent() )
1ee280b7
VZ
1027 {
1028 x2dev++;
1029 y2dev++;
7bcb11d3 1030 }
1ee280b7 1031#endif // !__WXWINCE__
a23fd0e1 1032
1ee280b7 1033 (void)Rectangle(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), x2dev, y2dev);
a23fd0e1 1034
7bcb11d3
JS
1035 CalcBoundingBox(x, y);
1036 CalcBoundingBox(x2, y2);
2bda0e17
KB
1037}
1038
888dde65 1039void wxMSWDCImpl::DoDrawRoundedRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius)
2bda0e17 1040{
82a306b7 1041 WXMICROWIN_CHECK_HDC
d275c7eb 1042
76f91e77 1043 wxBrushAttrsSetter cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
2d8a5cb1 1044
7bcb11d3
JS
1045 // Now, a negative radius value is interpreted to mean
1046 // 'the proportion of the smallest X or Y dimension'
a23fd0e1 1047
7bcb11d3
JS
1048 if (radius < 0.0)
1049 {
999836aa 1050 double smallest = (width < height) ? width : height;
7bcb11d3
JS
1051 radius = (- radius * smallest);
1052 }
a23fd0e1 1053
72cdf4c9
VZ
1054 wxCoord x2 = (x+width);
1055 wxCoord y2 = (y+height);
a23fd0e1 1056
dfde8cd3
GRG
1057 // Windows draws the filled rectangles without outline (i.e. drawn with a
1058 // transparent pen) one pixel smaller in both directions and we want them
1059 // to have the same size regardless of which pen is used - adjust
e6777e65 1060 if ( m_pen.IsTransparent() )
dfde8cd3
GRG
1061 {
1062 x2++;
1063 y2++;
1064 }
1065
a23fd0e1 1066 (void)RoundRect(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2),
25889d3c 1067 YLOG2DEV(y2), (int) (2*XLOG2DEV(radius)), (int)( 2*YLOG2DEV(radius)));
a23fd0e1 1068
7bcb11d3
JS
1069 CalcBoundingBox(x, y);
1070 CalcBoundingBox(x2, y2);
2bda0e17
KB
1071}
1072
888dde65 1073void wxMSWDCImpl::DoDrawEllipse(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
2bda0e17 1074{
82a306b7 1075 WXMICROWIN_CHECK_HDC
d275c7eb 1076
76f91e77 1077 wxBrushAttrsSetter cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
2d8a5cb1 1078
45c6df33
VZ
1079 // +1 below makes the ellipse more similar to other platforms.
1080 // In particular, DoDrawEllipse(x,y,1,1) should draw one point.
1081 wxCoord x2 = x + width + 1;
1082 wxCoord y2 = y + height + 1;
1083
1084 // Problem: Windows GDI Ellipse() with x2-x == y2-y == 3 and transparent
1085 // pen doesn't draw anything. Should we provide a workaround?
a23fd0e1 1086
45c6df33 1087 ::Ellipse(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2));
a23fd0e1 1088
7bcb11d3
JS
1089 CalcBoundingBox(x, y);
1090 CalcBoundingBox(x2, y2);
2bda0e17
KB
1091}
1092
3e9db38e 1093#if wxUSE_SPLINES && !defined(__WXWINCE__)
888dde65 1094void wxMSWDCImpl::DoDrawSpline(const wxPointList *points)
ad0ac642 1095{
ad0ac642
WS
1096 // quadratic b-spline to cubic bezier spline conversion
1097 //
1098 // quadratic spline with control points P0,P1,P2
1099 // P(s) = P0*(1-s)^2 + P1*2*(1-s)*s + P2*s^2
1100 //
1101 // bezier spline with control points B0,B1,B2,B3
1102 // B(s) = B0*(1-s)^3 + B1*3*(1-s)^2*s + B2*3*(1-s)*s^2 + B3*s^3
1103 //
1104 // control points of bezier spline calculated from b-spline
1105 // B0 = P0
1106 // B1 = (2*P1 + P0)/3
1107 // B2 = (2*P1 + P2)/3
1108 // B3 = P2
1109
1110 WXMICROWIN_CHECK_HDC
1111
1112 wxASSERT_MSG( points, wxT("NULL pointer to spline points?") );
1113
1114 const size_t n_points = points->GetCount();
60e19371 1115 wxASSERT_MSG( n_points > 2 , wxT("incomplete list of spline points?") );
ad0ac642
WS
1116
1117 const size_t n_bezier_points = n_points * 3 + 1;
1118 POINT *lppt = (POINT *)malloc(n_bezier_points*sizeof(POINT));
1119 size_t bezier_pos = 0;
1120 wxCoord x1, y1, x2, y2, cx1, cy1, cx4, cy4;
1121
b0d7707b
RR
1122 wxPointList::compatibility_iterator node = points->GetFirst();
1123 wxPoint *p = node->GetData();
ad0ac642
WS
1124 lppt[ bezier_pos ].x = x1 = p->x;
1125 lppt[ bezier_pos ].y = y1 = p->y;
1126 bezier_pos++;
1127 lppt[ bezier_pos ] = lppt[ bezier_pos-1 ];
1128 bezier_pos++;
1129
1130 node = node->GetNext();
b0d7707b 1131 p = node->GetData();
ad0ac642
WS
1132
1133 x2 = p->x;
1134 y2 = p->y;
1135 cx1 = ( x1 + x2 ) / 2;
1136 cy1 = ( y1 + y2 ) / 2;
1137 lppt[ bezier_pos ].x = XLOG2DEV(cx1);
1138 lppt[ bezier_pos ].y = YLOG2DEV(cy1);
1139 bezier_pos++;
1140 lppt[ bezier_pos ] = lppt[ bezier_pos-1 ];
1141 bezier_pos++;
1142
01871bf6 1143#if !wxUSE_STD_CONTAINERS
ad0ac642
WS
1144 while ((node = node->GetNext()) != NULL)
1145#else
1146 while ((node = node->GetNext()))
01871bf6 1147#endif // !wxUSE_STD_CONTAINERS
ad0ac642
WS
1148 {
1149 p = (wxPoint *)node->GetData();
1150 x1 = x2;
1151 y1 = y2;
1152 x2 = p->x;
1153 y2 = p->y;
1154 cx4 = (x1 + x2) / 2;
1155 cy4 = (y1 + y2) / 2;
1156 // B0 is B3 of previous segment
1157 // B1:
1158 lppt[ bezier_pos ].x = XLOG2DEV((x1*2+cx1)/3);
1159 lppt[ bezier_pos ].y = YLOG2DEV((y1*2+cy1)/3);
1160 bezier_pos++;
1161 // B2:
1162 lppt[ bezier_pos ].x = XLOG2DEV((x1*2+cx4)/3);
1163 lppt[ bezier_pos ].y = YLOG2DEV((y1*2+cy4)/3);
1164 bezier_pos++;
1165 // B3:
1166 lppt[ bezier_pos ].x = XLOG2DEV(cx4);
1167 lppt[ bezier_pos ].y = YLOG2DEV(cy4);
1168 bezier_pos++;
1169 cx1 = cx4;
1170 cy1 = cy4;
1171 }
1172
1173 lppt[ bezier_pos ] = lppt[ bezier_pos-1 ];
1174 bezier_pos++;
1175 lppt[ bezier_pos ].x = XLOG2DEV(x2);
1176 lppt[ bezier_pos ].y = YLOG2DEV(y2);
1177 bezier_pos++;
1178 lppt[ bezier_pos ] = lppt[ bezier_pos-1 ];
1179 bezier_pos++;
1180
1181 ::PolyBezier( GetHdc(), lppt, bezier_pos );
1182
1183 free(lppt);
ad0ac642 1184}
3e9db38e 1185#endif // wxUSE_SPLINES
ad0ac642 1186
6f65e337 1187// Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
888dde65 1188void wxMSWDCImpl::DoDrawEllipticArc(wxCoord x,wxCoord y,wxCoord w,wxCoord h,double sa,double ea)
6f65e337 1189{
4676948b 1190#ifdef __WXWINCE__
4f10962b 1191 DoDrawEllipticArcRot( x, y, w, h, sa, ea );
4676948b
JS
1192#else
1193
82a306b7 1194 WXMICROWIN_CHECK_HDC
d275c7eb 1195
76f91e77 1196 wxBrushAttrsSetter cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
2d8a5cb1 1197
f6bcfd97
BP
1198 wxCoord x2 = x + w;
1199 wxCoord y2 = y + h;
a23fd0e1 1200
7bcb11d3
JS
1201 int rx1 = XLOG2DEV(x+w/2);
1202 int ry1 = YLOG2DEV(y+h/2);
1203 int rx2 = rx1;
1204 int ry2 = ry1;
4314ec48
VZ
1205
1206 sa = DegToRad(sa);
1207 ea = DegToRad(ea);
1208
1209 rx1 += (int)(100.0 * abs(w) * cos(sa));
1210 ry1 -= (int)(100.0 * abs(h) * m_signY * sin(sa));
1211 rx2 += (int)(100.0 * abs(w) * cos(ea));
1212 ry2 -= (int)(100.0 * abs(h) * m_signY * sin(ea));
a23fd0e1 1213
e6d18909
RD
1214 // Swap start and end positions if the end angle is less than the start angle.
1215 if (ea < sa) {
ef94049f
PC
1216 int temp;
1217 temp = rx2;
1218 rx2 = rx1;
1219 rx1 = temp;
1220 temp = ry2;
1221 ry2 = ry1;
1222 ry1 = temp;
e6d18909
RD
1223 }
1224
7bcb11d3
JS
1225 // draw pie with NULL_PEN first and then outline otherwise a line is
1226 // drawn from the start and end points to the centre
f6bcfd97 1227 HPEN hpenOld = (HPEN) ::SelectObject(GetHdc(), (HPEN) ::GetStockObject(NULL_PEN));
7bcb11d3
JS
1228 if (m_signY > 0)
1229 {
a23fd0e1 1230 (void)Pie(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2)+1, YLOG2DEV(y2)+1,
f6bcfd97 1231 rx1, ry1, rx2, ry2);
7bcb11d3
JS
1232 }
1233 else
1234 {
a23fd0e1 1235 (void)Pie(GetHdc(), XLOG2DEV(x), YLOG2DEV(y)-1, XLOG2DEV(x2)+1, YLOG2DEV(y2),
f6bcfd97 1236 rx1, ry1-1, rx2, ry2-1);
7bcb11d3 1237 }
f6bcfd97
BP
1238
1239 ::SelectObject(GetHdc(), hpenOld);
1240
a23fd0e1 1241 (void)Arc(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2),
f6bcfd97 1242 rx1, ry1, rx2, ry2);
a23fd0e1 1243
7bcb11d3
JS
1244 CalcBoundingBox(x, y);
1245 CalcBoundingBox(x2, y2);
4676948b 1246#endif
6f65e337
JS
1247}
1248
888dde65 1249void wxMSWDCImpl::DoDrawIcon(const wxIcon& icon, wxCoord x, wxCoord y)
2bda0e17 1250{
82a306b7 1251 WXMICROWIN_CHECK_HDC
d275c7eb 1252
87d3576f 1253 wxCHECK_RET( icon.IsOk(), wxT("invalid icon in DrawIcon") );
4b7f2165 1254
f6bcfd97
BP
1255#ifdef __WIN32__
1256 ::DrawIconEx(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), GetHiconOf(icon), icon.GetWidth(), icon.GetHeight(), 0, NULL, DI_NORMAL);
1257#else
4b7f2165 1258 ::DrawIcon(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), GetHiconOf(icon));
f6bcfd97 1259#endif
a23fd0e1 1260
7bcb11d3 1261 CalcBoundingBox(x, y);
4b7f2165 1262 CalcBoundingBox(x + icon.GetWidth(), y + icon.GetHeight());
2bda0e17
KB
1263}
1264
888dde65 1265void wxMSWDCImpl::DoDrawBitmap( const wxBitmap &bmp, wxCoord x, wxCoord y, bool useMask )
f5419957 1266{
82a306b7 1267 WXMICROWIN_CHECK_HDC
d275c7eb 1268
9a83f860 1269 wxCHECK_RET( bmp.IsOk(), wxT("invalid bitmap in wxMSWDCImpl::DrawBitmap") );
4b7f2165
VZ
1270
1271 int width = bmp.GetWidth(),
1272 height = bmp.GetHeight();
1273
91b4c08d 1274 HBITMAP hbmpMask = 0;
e22c13fe
VZ
1275
1276#if wxUSE_PALETTE
19193a2c 1277 HPALETTE oldPal = 0;
e22c13fe 1278#endif // wxUSE_PALETTE
91b4c08d 1279
acf8e3d2
VZ
1280 if ( bmp.HasAlpha() )
1281 {
275a63e3
VZ
1282 MemoryHDC hdcMem;
1283 SelectInHDC select(hdcMem, GetHbitmapOf(bmp));
878711c0 1284
e3b81044 1285 if ( AlphaBlt(GetHdc(), x, y, width, height, 0, 0, width, height, hdcMem, bmp) )
275a63e3 1286 return;
acf8e3d2 1287 }
acf8e3d2 1288
4f64fde7 1289 SET_STRETCH_BLT_MODE(GetHdc());
eb72e9aa 1290
91b4c08d
VZ
1291 if ( useMask )
1292 {
1293 wxMask *mask = bmp.GetMask();
1294 if ( mask )
1295 hbmpMask = (HBITMAP)mask->GetMaskBitmap();
1296
1297 if ( !hbmpMask )
1298 {
1299 // don't give assert here because this would break existing
1300 // programs - just silently ignore useMask parameter
beb966c5 1301 useMask = false;
91b4c08d
VZ
1302 }
1303 }
91b4c08d
VZ
1304 if ( useMask )
1305 {
1306#ifdef __WIN32__
4aff28fc
VZ
1307 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1308 // points
7b8d24ad
VZ
1309 bool ok = false;
1310
1311#if wxUSE_SYSTEM_OPTIONS
d3211838 1312 // On some systems, MaskBlt succeeds yet is much much slower
77ffb593 1313 // than the wxWidgets fall-back implementation. So we need
d3211838 1314 // to be able to switch this on and off at runtime.
7b8d24ad
VZ
1315 //
1316 // NB: don't query the value of the option every time but do it only
1317 // once as otherwise it can have real (and bad) performance
1318 // implications (see #11172)
1319 static bool
1320 s_maskBltAllowed = wxSystemOptions::GetOptionInt("no-maskblt") == 0;
1321 if ( s_maskBltAllowed )
1322#endif // wxUSE_SYSTEM_OPTIONS
d3211838 1323 {
19193a2c 1324 HDC cdc = GetHdc();
d3211838 1325 HDC hdcMem = ::CreateCompatibleDC(GetHdc());
a230101e 1326 HGDIOBJ hOldBitmap = ::SelectObject(hdcMem, GetHbitmapOf(bmp));
e22c13fe
VZ
1327#if wxUSE_PALETTE
1328 wxPalette *pal = bmp.GetPalette();
1329 if ( pal && ::GetDeviceCaps(cdc,BITSPIXEL) <= 8 )
1330 {
b95edd47 1331 oldPal = ::SelectPalette(hdcMem, GetHpaletteOf(*pal), FALSE);
19193a2c 1332 ::RealizePalette(hdcMem);
e22c13fe
VZ
1333 }
1334#endif // wxUSE_PALETTE
1335
19193a2c 1336 ok = ::MaskBlt(cdc, x, y, width, height,
91b4c08d
VZ
1337 hdcMem, 0, 0,
1338 hbmpMask, 0, 0,
4aff28fc 1339 MAKEROP4(SRCCOPY, DSTCOPY)) != 0;
e22c13fe
VZ
1340
1341#if wxUSE_PALETTE
19193a2c
KB
1342 if (oldPal)
1343 ::SelectPalette(hdcMem, oldPal, FALSE);
e22c13fe
VZ
1344#endif // wxUSE_PALETTE
1345
a230101e 1346 ::SelectObject(hdcMem, hOldBitmap);
d3211838
JS
1347 ::DeleteDC(hdcMem);
1348 }
91b4c08d
VZ
1349
1350 if ( !ok )
1351#endif // Win32
1352 {
888dde65 1353 // Rather than reproduce wxMSWDCImpl::Blit, let's do it at the wxWin API
4aff28fc 1354 // level
91b4c08d 1355 wxMemoryDC memDC;
fea35690
VZ
1356
1357 memDC.SelectObjectAsSource(bmp);
91b4c08d 1358
888dde65 1359 GetOwner()->Blit(x, y, width, height, &memDC, 0, 0, wxCOPY, useMask);
91b4c08d
VZ
1360
1361 memDC.SelectObject(wxNullBitmap);
1362 }
1363 }
1364 else // no mask, just use BitBlt()
f5419957 1365 {
4b7f2165
VZ
1366 HDC cdc = GetHdc();
1367 HDC memdc = ::CreateCompatibleDC( cdc );
1368 HBITMAP hbitmap = (HBITMAP) bmp.GetHBITMAP( );
1369
1370 wxASSERT_MSG( hbitmap, wxT("bitmap is ok but HBITMAP is NULL?") );
1371
76f91e77 1372 wxTextColoursChanger textCol(GetHdc(), *this);
392f4b1b 1373
e22c13fe 1374#if wxUSE_PALETTE
62e1ba75
JS
1375 wxPalette *pal = bmp.GetPalette();
1376 if ( pal && ::GetDeviceCaps(cdc,BITSPIXEL) <= 8 )
1377 {
b95edd47 1378 oldPal = ::SelectPalette(memdc, GetHpaletteOf(*pal), FALSE);
62e1ba75
JS
1379 ::RealizePalette(memdc);
1380 }
e22c13fe
VZ
1381#endif // wxUSE_PALETTE
1382
a230101e 1383 HGDIOBJ hOldBitmap = ::SelectObject( memdc, hbitmap );
4b7f2165 1384 ::BitBlt( cdc, x, y, width, height, memdc, 0, 0, SRCCOPY);
e22c13fe
VZ
1385
1386#if wxUSE_PALETTE
19193a2c
KB
1387 if (oldPal)
1388 ::SelectPalette(memdc, oldPal, FALSE);
e22c13fe
VZ
1389#endif // wxUSE_PALETTE
1390
62e1ba75 1391 ::SelectObject( memdc, hOldBitmap );
4b7f2165 1392 ::DeleteDC( memdc );
f5419957 1393 }
f5419957
JS
1394}
1395
888dde65 1396void wxMSWDCImpl::DoDrawText(const wxString& text, wxCoord x, wxCoord y)
a23fd0e1 1397{
a5bb4514
VZ
1398 // For compatibility with other ports (notably wxGTK) and because it's
1399 // genuinely useful, we allow passing multiline strings to DrawText().
1400 // However there is no native MSW function to draw them directly so we
1401 // instead reuse the generic DrawLabel() method to render them. Of course,
1402 // DrawLabel() itself will call back to us but with single line strings
1403 // only so there won't be any infinite recursion here.
1404 if ( text.find('\n') != wxString::npos )
1405 {
1406 GetOwner()->DrawLabel(text, wxRect(x, y, 0, 0));
1407 return;
1408 }
1409
82a306b7 1410 WXMICROWIN_CHECK_HDC
d275c7eb 1411
4314ec48
VZ
1412 DrawAnyText(text, x, y);
1413
1414 // update the bounding box
1415 CalcBoundingBox(x, y);
1416
1417 wxCoord w, h;
888dde65 1418 GetOwner()->GetTextExtent(text, &w, &h);
4314ec48
VZ
1419 CalcBoundingBox(x + w, y + h);
1420}
1421
888dde65 1422void wxMSWDCImpl::DrawAnyText(const wxString& text, wxCoord x, wxCoord y)
4314ec48 1423{
82a306b7 1424 WXMICROWIN_CHECK_HDC
d275c7eb 1425
4314ec48 1426 // prepare for drawing the text
76f91e77 1427 wxTextColoursChanger textCol(GetHdc(), *this);
a23fd0e1 1428
76f91e77 1429 wxBkModeChanger bkMode(GetHdc(), m_backgroundMode);
a23fd0e1 1430
4676948b
JS
1431 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), 0, NULL,
1432 text.c_str(), text.length(), NULL) == 0 )
1433 {
1434 wxLogLastError(wxT("TextOut"));
1435 }
a23fd0e1
VZ
1436}
1437
888dde65 1438void wxMSWDCImpl::DoDrawRotatedText(const wxString& text,
95724b1a
VZ
1439 wxCoord x, wxCoord y,
1440 double angle)
1441{
82a306b7 1442 WXMICROWIN_CHECK_HDC
d275c7eb 1443
696e1ea0
VZ
1444 // we test that we have some font because otherwise we should still use the
1445 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1446 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1447 // font for drawing rotated fonts unfortunately)
87d3576f 1448 if ( (angle == 0.0) && m_font.IsOk() )
4314ec48
VZ
1449 {
1450 DoDrawText(text, x, y);
1451 }
04ef50df 1452#ifndef __WXMICROWIN__
4314ec48
VZ
1453 else
1454 {
4770df95
VZ
1455 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1456 // because it's not TrueType and so can't have non zero
1457 // orientation/escapement under Win9x
87d3576f 1458 wxFont font = m_font.IsOk() ? m_font : *wxSWISS_FONT;
696e1ea0 1459 HFONT hfont = (HFONT)font.GetResourceHandle();
4314ec48 1460 LOGFONT lf;
696e1ea0
VZ
1461 if ( ::GetObject(hfont, sizeof(lf), &lf) == 0 )
1462 {
f6bcfd97 1463 wxLogLastError(wxT("GetObject(hfont)"));
696e1ea0 1464 }
4314ec48
VZ
1465
1466 // GDI wants the angle in tenth of degree
1467 long angle10 = (long)(angle * 10);
1468 lf.lfEscapement = angle10;
1469 lf. lfOrientation = angle10;
1470
696e1ea0 1471 hfont = ::CreateFontIndirect(&lf);
4314ec48
VZ
1472 if ( !hfont )
1473 {
f6bcfd97 1474 wxLogLastError(wxT("CreateFont"));
4314ec48
VZ
1475 }
1476 else
1477 {
696e1ea0 1478 HFONT hfontOld = (HFONT)::SelectObject(GetHdc(), hfont);
4314ec48
VZ
1479
1480 DrawAnyText(text, x, y);
1481
1482 (void)::SelectObject(GetHdc(), hfontOld);
a3a1ceae 1483 (void)::DeleteObject(hfont);
4314ec48
VZ
1484 }
1485
1486 // call the bounding box by adding all four vertices of the rectangle
1487 // containing the text to it (simpler and probably not slower than
1488 // determining which of them is really topmost/leftmost/...)
1489 wxCoord w, h;
888dde65 1490 GetOwner()->GetTextExtent(text, &w, &h);
4314ec48
VZ
1491
1492 double rad = DegToRad(angle);
1493
1494 // "upper left" and "upper right"
1495 CalcBoundingBox(x, y);
a98ca1ae 1496 CalcBoundingBox(x + wxCoord(w*cos(rad)), y - wxCoord(w*sin(rad)));
4314ec48
VZ
1497
1498 // "bottom left" and "bottom right"
1499 x += (wxCoord)(h*sin(rad));
1500 y += (wxCoord)(h*cos(rad));
1501 CalcBoundingBox(x, y);
a98ca1ae 1502 CalcBoundingBox(x + wxCoord(w*cos(rad)), y - wxCoord(w*sin(rad)));
4314ec48 1503 }
04ef50df 1504#endif
95724b1a
VZ
1505}
1506
a23fd0e1
VZ
1507// ---------------------------------------------------------------------------
1508// set GDI objects
1509// ---------------------------------------------------------------------------
1510
d275c7eb
VZ
1511#if wxUSE_PALETTE
1512
888dde65 1513void wxMSWDCImpl::DoSelectPalette(bool realize)
a23fd0e1 1514{
82a306b7 1515 WXMICROWIN_CHECK_HDC
d275c7eb 1516
a23fd0e1
VZ
1517 // Set the old object temporarily, in case the assignment deletes an object
1518 // that's not yet selected out.
1519 if (m_oldPalette)
1520 {
19193a2c 1521 ::SelectPalette(GetHdc(), (HPALETTE) m_oldPalette, FALSE);
a23fd0e1
VZ
1522 m_oldPalette = 0;
1523 }
1524
87d3576f 1525 if ( m_palette.IsOk() )
a23fd0e1 1526 {
b95edd47
VZ
1527 HPALETTE oldPal = ::SelectPalette(GetHdc(),
1528 GetHpaletteOf(m_palette),
beb966c5 1529 false);
a23fd0e1
VZ
1530 if (!m_oldPalette)
1531 m_oldPalette = (WXHPALETTE) oldPal;
1532
574c939e
KB
1533 if (realize)
1534 ::RealizePalette(GetHdc());
a23fd0e1 1535 }
574c939e
KB
1536}
1537
888dde65 1538void wxMSWDCImpl::SetPalette(const wxPalette& palette)
574c939e 1539{
87d3576f 1540 if ( palette.IsOk() )
b95edd47 1541 {
574c939e 1542 m_palette = palette;
beb966c5 1543 DoSelectPalette(true);
b95edd47 1544 }
a23fd0e1
VZ
1545}
1546
888dde65 1547void wxMSWDCImpl::InitializePalette()
574c939e 1548{
b95edd47
VZ
1549 if ( wxDisplayDepth() <= 8 )
1550 {
574c939e
KB
1551 // look for any window or parent that has a custom palette. If any has
1552 // one then we need to use it in drawing operations
888dde65 1553 wxWindow *win = m_window->GetAncestorWithCustomPalette();
b95edd47
VZ
1554
1555 m_hasCustomPalette = win && win->HasCustomPalette();
1556 if ( m_hasCustomPalette )
1557 {
574c939e 1558 m_palette = win->GetPalette();
b95edd47 1559
574c939e
KB
1560 // turn on MSW translation for this palette
1561 DoSelectPalette();
574c939e 1562 }
b95edd47 1563 }
574c939e 1564}
b95edd47 1565
d275c7eb
VZ
1566#endif // wxUSE_PALETTE
1567
9f7948af
VZ
1568// SetFont/Pen/Brush() really ask to be implemented as a single template
1569// function... but doing it is not worth breaking OpenWatcom build <sigh>
1570
888dde65 1571void wxMSWDCImpl::SetFont(const wxFont& font)
2bda0e17 1572{
82a306b7 1573 WXMICROWIN_CHECK_HDC
d275c7eb 1574
9f7948af
VZ
1575 if ( font == m_font )
1576 return;
a23fd0e1 1577
87d3576f 1578 if ( font.IsOk() )
7bcb11d3 1579 {
9f7948af
VZ
1580 HGDIOBJ hfont = ::SelectObject(GetHdc(), GetHfontOf(font));
1581 if ( hfont == HGDI_ERROR )
1582 {
9a83f860 1583 wxLogLastError(wxT("SelectObject(font)"));
9f7948af
VZ
1584 }
1585 else // selected ok
1586 {
1587 if ( !m_oldFont )
658ff7f1 1588 m_oldFont = (WXHFONT)hfont;
a23fd0e1 1589
9f7948af
VZ
1590 m_font = font;
1591 }
1592 }
1593 else // invalid font, reset the current font
7bcb11d3 1594 {
9f7948af 1595 if ( m_oldFont )
7bcb11d3 1596 {
9f7948af
VZ
1597 if ( ::SelectObject(GetHdc(), (HPEN) m_oldFont) == HGDI_ERROR )
1598 {
9a83f860 1599 wxLogLastError(wxT("SelectObject(old font)"));
9f7948af
VZ
1600 }
1601
beb966c5 1602 m_oldFont = 0;
7bcb11d3 1603 }
9f7948af
VZ
1604
1605 m_font = wxNullFont;
34da0970 1606 }
2bda0e17
KB
1607}
1608
888dde65 1609void wxMSWDCImpl::SetPen(const wxPen& pen)
2bda0e17 1610{
82a306b7 1611 WXMICROWIN_CHECK_HDC
d275c7eb 1612
b5371ab8
VZ
1613 if ( pen == m_pen )
1614 return;
a23fd0e1 1615
87d3576f 1616 if ( pen.IsOk() )
7bcb11d3 1617 {
b5371ab8
VZ
1618 HGDIOBJ hpen = ::SelectObject(GetHdc(), GetHpenOf(pen));
1619 if ( hpen == HGDI_ERROR )
1620 {
9a83f860 1621 wxLogLastError(wxT("SelectObject(pen)"));
b5371ab8
VZ
1622 }
1623 else // selected ok
1624 {
1625 if ( !m_oldPen )
1626 m_oldPen = (WXHPEN)hpen;
a23fd0e1 1627
b5371ab8
VZ
1628 m_pen = pen;
1629 }
1630 }
1631 else // invalid pen, reset the current pen
7bcb11d3 1632 {
b5371ab8 1633 if ( m_oldPen )
7bcb11d3 1634 {
b5371ab8
VZ
1635 if ( ::SelectObject(GetHdc(), (HPEN) m_oldPen) == HGDI_ERROR )
1636 {
9a83f860 1637 wxLogLastError(wxT("SelectObject(old pen)"));
b5371ab8
VZ
1638 }
1639
beb966c5 1640 m_oldPen = 0;
7bcb11d3 1641 }
b5371ab8
VZ
1642
1643 m_pen = wxNullPen;
2bda0e17 1644 }
2bda0e17
KB
1645}
1646
888dde65 1647void wxMSWDCImpl::SetBrush(const wxBrush& brush)
2bda0e17 1648{
82a306b7 1649 WXMICROWIN_CHECK_HDC
d275c7eb 1650
9f7948af
VZ
1651 if ( brush == m_brush )
1652 return;
a23fd0e1 1653
87d3576f 1654 if ( brush.IsOk() )
7bcb11d3 1655 {
9f7948af 1656 // we must make sure the brush is aligned with the logical coordinates
1dc39a1f
VZ
1657 // before selecting it or using the same brush for the background of
1658 // different windows would result in discontinuities
1659 wxSize sizeBrushBitmap = wxDefaultSize;
9f7948af 1660 wxBitmap *stipple = brush.GetStipple();
87d3576f 1661 if ( stipple && stipple->IsOk() )
1dc39a1f
VZ
1662 sizeBrushBitmap = stipple->GetSize();
1663 else if ( brush.IsHatch() )
1664 sizeBrushBitmap = wxSize(8, 8);
1665
1666 if ( sizeBrushBitmap.IsFullySpecified() )
2d8a5cb1 1667 {
9f7948af
VZ
1668 if ( !::SetBrushOrgEx
1669 (
1670 GetHdc(),
1dc39a1f
VZ
1671 m_deviceOriginX % sizeBrushBitmap.x,
1672 m_deviceOriginY % sizeBrushBitmap.y,
9f7948af
VZ
1673 NULL // [out] previous brush origin
1674 ) )
1675 {
9a83f860 1676 wxLogLastError(wxT("SetBrushOrgEx()"));
9f7948af 1677 }
2d8a5cb1
VZ
1678 }
1679
9f7948af
VZ
1680 HGDIOBJ hbrush = ::SelectObject(GetHdc(), GetHbrushOf(brush));
1681 if ( hbrush == HGDI_ERROR )
7bcb11d3 1682 {
9a83f860 1683 wxLogLastError(wxT("SelectObject(brush)"));
7bcb11d3 1684 }
9f7948af
VZ
1685 else // selected ok
1686 {
1687 if ( !m_oldBrush )
658ff7f1 1688 m_oldBrush = (WXHBRUSH)hbrush;
9f7948af
VZ
1689
1690 m_brush = brush;
1691 }
1692 }
1693 else // invalid brush, reset the current brush
1694 {
1695 if ( m_oldBrush )
1696 {
1697 if ( ::SelectObject(GetHdc(), (HPEN) m_oldBrush) == HGDI_ERROR )
1698 {
9a83f860 1699 wxLogLastError(wxT("SelectObject(old brush)"));
9f7948af
VZ
1700 }
1701
beb966c5 1702 m_oldBrush = 0;
9f7948af
VZ
1703 }
1704
1705 m_brush = wxNullBrush;
2bda0e17 1706 }
2bda0e17
KB
1707}
1708
888dde65 1709void wxMSWDCImpl::SetBackground(const wxBrush& brush)
2bda0e17 1710{
82a306b7 1711 WXMICROWIN_CHECK_HDC
d275c7eb 1712
7bcb11d3 1713 m_backgroundBrush = brush;
a23fd0e1 1714
87d3576f 1715 if ( m_backgroundBrush.IsOk() )
7bcb11d3 1716 {
44383ef7 1717 (void)SetBkColor(GetHdc(), m_backgroundBrush.GetColour().GetPixel());
2bda0e17 1718 }
2bda0e17
KB
1719}
1720
888dde65 1721void wxMSWDCImpl::SetBackgroundMode(int mode)
2bda0e17 1722{
82a306b7 1723 WXMICROWIN_CHECK_HDC
d275c7eb 1724
7bcb11d3 1725 m_backgroundMode = mode;
a23fd0e1 1726
c45a644e
RR
1727 // SetBackgroundColour now only refers to text background
1728 // and m_backgroundMode is used there
2bda0e17
KB
1729}
1730
89efaf2b 1731void wxMSWDCImpl::SetLogicalFunction(wxRasterOperationMode function)
2bda0e17 1732{
82a306b7 1733 WXMICROWIN_CHECK_HDC
d275c7eb 1734
7bcb11d3 1735 m_logicalFunction = function;
a23fd0e1 1736
ed791986 1737 SetRop(m_hDC);
2bda0e17
KB
1738}
1739
888dde65 1740void wxMSWDCImpl::SetRop(WXHDC dc)
2bda0e17 1741{
ed791986 1742 if ( !dc || m_logicalFunction < 0 )
7bcb11d3 1743 return;
a23fd0e1 1744
36e5a9a7 1745 int rop;
ed791986 1746
7bcb11d3
JS
1747 switch (m_logicalFunction)
1748 {
4aff28fc 1749 case wxCLEAR: rop = R2_BLACK; break;
ed791986
VZ
1750 case wxXOR: rop = R2_XORPEN; break;
1751 case wxINVERT: rop = R2_NOT; break;
1752 case wxOR_REVERSE: rop = R2_MERGEPENNOT; break;
1753 case wxAND_REVERSE: rop = R2_MASKPENNOT; break;
4aff28fc 1754 case wxCOPY: rop = R2_COPYPEN; break;
ed791986 1755 case wxAND: rop = R2_MASKPEN; break;
ed791986 1756 case wxAND_INVERT: rop = R2_MASKNOTPEN; break;
ed791986 1757 case wxNO_OP: rop = R2_NOP; break;
ed791986 1758 case wxNOR: rop = R2_NOTMERGEPEN; break;
4aff28fc
VZ
1759 case wxEQUIV: rop = R2_NOTXORPEN; break;
1760 case wxSRC_INVERT: rop = R2_NOTCOPYPEN; break;
1761 case wxOR_INVERT: rop = R2_MERGENOTPEN; break;
1762 case wxNAND: rop = R2_NOTMASKPEN; break;
1763 case wxOR: rop = R2_MERGEPEN; break;
1764 case wxSET: rop = R2_WHITE; break;
36e5a9a7
VZ
1765 default:
1766 wxFAIL_MSG( wxS("unknown logical function") );
1767 return;
7bcb11d3 1768 }
ed791986
VZ
1769
1770 SetROP2(GetHdc(), rop);
2bda0e17
KB
1771}
1772
888dde65 1773bool wxMSWDCImpl::StartDoc(const wxString& WXUNUSED(message))
2bda0e17 1774{
beb966c5
DS
1775 // We might be previewing, so return true to let it continue.
1776 return true;
2bda0e17
KB
1777}
1778
888dde65 1779void wxMSWDCImpl::EndDoc()
2bda0e17 1780{
2bda0e17
KB
1781}
1782
888dde65 1783void wxMSWDCImpl::StartPage()
2bda0e17 1784{
2bda0e17
KB
1785}
1786
888dde65 1787void wxMSWDCImpl::EndPage()
2bda0e17 1788{
2bda0e17
KB
1789}
1790
a23fd0e1
VZ
1791// ---------------------------------------------------------------------------
1792// text metrics
1793// ---------------------------------------------------------------------------
1794
888dde65 1795wxCoord wxMSWDCImpl::GetCharHeight() const
2bda0e17 1796{
82a306b7 1797 WXMICROWIN_CHECK_HDC_RET(0)
d275c7eb 1798
7bcb11d3 1799 TEXTMETRIC lpTextMetric;
a23fd0e1
VZ
1800
1801 GetTextMetrics(GetHdc(), &lpTextMetric);
1802
1e2081a1 1803 return lpTextMetric.tmHeight;
2bda0e17
KB
1804}
1805
888dde65 1806wxCoord wxMSWDCImpl::GetCharWidth() const
2bda0e17 1807{
82a306b7 1808 WXMICROWIN_CHECK_HDC_RET(0)
d275c7eb 1809
7bcb11d3 1810 TEXTMETRIC lpTextMetric;
a23fd0e1
VZ
1811
1812 GetTextMetrics(GetHdc(), &lpTextMetric);
1813
1e2081a1 1814 return lpTextMetric.tmAveCharWidth;
2bda0e17
KB
1815}
1816
e29bf4b0
VZ
1817void wxMSWDCImpl::DoGetFontMetrics(int *height,
1818 int *ascent,
1819 int *descent,
1820 int *internalLeading,
1821 int *externalLeading,
1822 int *averageWidth) const
1823{
1824 TEXTMETRIC tm;
1825
1826 GetTextMetrics(GetHdc(), &tm);
1827
1828 if ( height )
1829 *height = tm.tmHeight;
1830 if ( ascent )
1831 *ascent = tm.tmAscent;
1832 if ( descent )
1833 *descent = tm.tmDescent;
1834 if ( internalLeading )
1835 *internalLeading = tm.tmInternalLeading;
1836 if ( externalLeading )
1837 *externalLeading = tm.tmExternalLeading;
1838 if ( averageWidth )
1839 *averageWidth = tm.tmAveCharWidth;
1840}
1841
888dde65 1842void wxMSWDCImpl::DoGetTextExtent(const wxString& string, wxCoord *x, wxCoord *y,
72cdf4c9 1843 wxCoord *descent, wxCoord *externalLeading,
c94f845b 1844 const wxFont *font) const
2bda0e17 1845{
5adad466
JS
1846#ifdef __WXMICROWIN__
1847 if (!GetHDC())
1848 {
a230101e
VZ
1849 if (x) *x = 0;
1850 if (y) *y = 0;
1851 if (descent) *descent = 0;
1852 if (externalLeading) *externalLeading = 0;
1853 return;
5adad466 1854 }
a230101e 1855#endif // __WXMICROWIN__
d275c7eb 1856
8bf30fe9
VZ
1857 HFONT hfontOld;
1858 if ( font )
1859 {
9a83f860 1860 wxASSERT_MSG( font->IsOk(), wxT("invalid font in wxMSWDCImpl::GetTextExtent") );
8bf30fe9
VZ
1861
1862 hfontOld = (HFONT)::SelectObject(GetHdc(), GetHfontOf(*font));
1863 }
1864 else // don't change the font
1865 {
1866 hfontOld = 0;
1867 }
a23fd0e1 1868
7bcb11d3 1869 SIZE sizeRect;
481203cb 1870 const size_t len = string.length();
e0a050e3 1871 if ( !::GetTextExtentPoint32(GetHdc(), string.wx_str(), len, &sizeRect) )
481203cb 1872 {
9a83f860 1873 wxLogLastError(wxT("GetTextExtentPoint32()"));
481203cb 1874 }
a23fd0e1 1875
3cdcf4d4 1876#if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
481203cb
VZ
1877 // the result computed by GetTextExtentPoint32() may be too small as it
1878 // accounts for under/overhang of the first/last character while we want
1879 // just the bounding rect for this string so adjust the width as needed
3cdcf4d4 1880 // (using API not available in 2002 SDKs of WinCE)
481203cb
VZ
1881 if ( len > 0 )
1882 {
1883 ABC width;
1884 const wxChar chFirst = *string.begin();
1885 if ( ::GetCharABCWidths(GetHdc(), chFirst, chFirst, &width) )
1886 {
1887 if ( width.abcA < 0 )
1888 sizeRect.cx -= width.abcA;
1889
1890 if ( len > 1 )
1891 {
1892 const wxChar chLast = *string.rbegin();
1893 ::GetCharABCWidths(GetHdc(), chLast, chLast, &width);
1894 }
1895 //else: we already have the width of the last character
1896
1897 if ( width.abcC < 0 )
1898 sizeRect.cx -= width.abcC;
1899 }
1900 //else: GetCharABCWidths() failed, not a TrueType font?
1901 }
3cdcf4d4 1902#endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
481203cb 1903
1e2081a1
VZ
1904 if (x)
1905 *x = sizeRect.cx;
1906 if (y)
1907 *y = sizeRect.cy;
247afab5
VZ
1908
1909 if ( descent || externalLeading )
1910 {
e29bf4b0 1911 DoGetFontMetrics(NULL, NULL, descent, NULL, externalLeading, NULL);
247afab5 1912 }
8bf30fe9
VZ
1913
1914 if ( hfontOld )
1915 {
1916 ::SelectObject(GetHdc(), hfontOld);
1917 }
2bda0e17
KB
1918}
1919
553aa032
RD
1920
1921// Each element of the array will be the width of the string up to and
5c2ddebe 1922// including the coresoponding character in text.
553aa032 1923
888dde65 1924bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const
553aa032
RD
1925{
1926 static int maxLenText = -1;
1927 static int maxWidth = -1;
1928 int fit = 0;
1929 SIZE sz = {0,0};
41e155b4 1930 int stlen = text.length();
553aa032
RD
1931
1932 if (maxLenText == -1)
1933 {
1934 // Win9x and WinNT+ have different limits
1935 int version = wxGetOsVersion();
406d283a
PC
1936 maxLenText = version == wxOS_WINDOWS_NT ? 65535 : 8192;
1937 maxWidth = version == wxOS_WINDOWS_NT ? INT_MAX : 32767;
553aa032 1938 }
5c2ddebe 1939
553aa032
RD
1940 widths.Empty();
1941 widths.Add(0, stlen); // fill the array with zeros
f9daf953
JS
1942 if (stlen == 0)
1943 return true;
5c2ddebe 1944
553aa032
RD
1945 if (!::GetTextExtentExPoint(GetHdc(),
1946 text.c_str(), // string to check
1947 wxMin(stlen, maxLenText),
5c2ddebe
VZ
1948 maxWidth,
1949 &fit, // [out] count of chars
553aa032 1950 // that will fit
5c2ddebe
VZ
1951 &widths[0], // array to fill
1952 &sz))
1953 {
553aa032
RD
1954 // API failed
1955 wxLogLastError(wxT("GetTextExtentExPoint"));
5c2ddebe
VZ
1956 return false;
1957 }
1958
553aa032
RD
1959 return true;
1960}
1961
888dde65 1962void wxMSWDCImpl::RealizeScaleAndOrigin()
04ab8b6d 1963{
9effb77d
VZ
1964 // although it may seem wasteful to always use MM_ANISOTROPIC here instead
1965 // of using MM_TEXT if there is no scaling, benchmarking doesn't detect any
1966 // noticeable difference between these mapping modes
04ab8b6d
RR
1967#ifndef __WXWINCE__
1968 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
553aa032 1969
a152f137
VZ
1970 // wxWidgets API assumes that the coordinate space is "infinite" (i.e. only
1971 // limited by 2^32 range of the integer coordinates) but in MSW API we must
1972 // actually specify the extents that we use. So we more or less arbitrarily
1973 // decide to use "base" VIEWPORT_EXTENT and adjust it depending on scale.
1974 //
1975 // To avoid rounding errors we prefer to multiply by the scale if it's > 1
1976 // and to divide by it if it's < 1.
1977 int devExtX, devExtY, // Viewport, i.e. device space, extents.
1978 logExtX, logExtY; // Window, i.e. logical coordinate space, extents.
1979 if ( m_scaleX >= 1 )
1980 {
1981 devExtX = VIEWPORT_EXTENT*m_scaleX;
1982 logExtX = m_signX*VIEWPORT_EXTENT;
1983 }
1984 else
1985 {
1986 devExtX = VIEWPORT_EXTENT;
1987 logExtX = m_signX*VIEWPORT_EXTENT/m_scaleX;
1988 }
1989
1990 if ( m_scaleY >= 1 )
1991 {
1992 devExtY = VIEWPORT_EXTENT*m_scaleY;
1993 logExtY = m_signY*VIEWPORT_EXTENT;
1994 }
1995 else
1996 {
1997 devExtY = VIEWPORT_EXTENT;
1998 logExtY = m_signY*VIEWPORT_EXTENT/m_scaleY;
1999 }
553aa032 2000
a152f137
VZ
2001 ::SetViewportExtEx(GetHdc(), devExtX, devExtY, NULL);
2002 ::SetWindowExtEx(GetHdc(), logExtX, logExtY, NULL);
04ab8b6d
RR
2003
2004 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX, m_deviceOriginY, NULL);
2005 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX, m_logicalOriginY, NULL);
2006#endif
04ab8b6d 2007}
553aa032 2008
89efaf2b 2009void wxMSWDCImpl::SetMapMode(wxMappingMode mode)
2bda0e17 2010{
82a306b7 2011 WXMICROWIN_CHECK_HDC
d275c7eb 2012
7bcb11d3 2013 m_mappingMode = mode;
a23fd0e1 2014
1e2081a1 2015 if ( mode == wxMM_TEXT )
2bda0e17 2016 {
1e2081a1
VZ
2017 m_logicalScaleX =
2018 m_logicalScaleY = 1.0;
2bda0e17 2019 }
1e2081a1 2020 else // need to do some calculations
2bda0e17 2021 {
1e2081a1
VZ
2022 int pixel_width = ::GetDeviceCaps(GetHdc(), HORZRES),
2023 pixel_height = ::GetDeviceCaps(GetHdc(), VERTRES),
2024 mm_width = ::GetDeviceCaps(GetHdc(), HORZSIZE),
2025 mm_height = ::GetDeviceCaps(GetHdc(), VERTSIZE);
2026
2027 if ( (mm_width == 0) || (mm_height == 0) )
7bcb11d3 2028 {
1e2081a1
VZ
2029 // we can't calculate mm2pixels[XY] then!
2030 return;
7bcb11d3 2031 }
1e2081a1 2032
82922a02
VZ
2033 double mm2pixelsX = (double)pixel_width / mm_width,
2034 mm2pixelsY = (double)pixel_height / mm_height;
1e2081a1
VZ
2035
2036 switch (mode)
7bcb11d3 2037 {
1e2081a1
VZ
2038 case wxMM_TWIPS:
2039 m_logicalScaleX = twips2mm * mm2pixelsX;
2040 m_logicalScaleY = twips2mm * mm2pixelsY;
2041 break;
2042
2043 case wxMM_POINTS:
2044 m_logicalScaleX = pt2mm * mm2pixelsX;
2045 m_logicalScaleY = pt2mm * mm2pixelsY;
2046 break;
2047
2048 case wxMM_METRIC:
2049 m_logicalScaleX = mm2pixelsX;
2050 m_logicalScaleY = mm2pixelsY;
2051 break;
2052
2053 case wxMM_LOMETRIC:
2054 m_logicalScaleX = mm2pixelsX / 10.0;
2055 m_logicalScaleY = mm2pixelsY / 10.0;
2056 break;
2057
2058 default:
9a83f860 2059 wxFAIL_MSG( wxT("unknown mapping mode in SetMapMode") );
7bcb11d3 2060 }
2bda0e17 2061 }
1ee280b7 2062
04ab8b6d 2063 ComputeScaleAndOrigin();
1ee280b7 2064
04ab8b6d 2065 RealizeScaleAndOrigin();
2bda0e17
KB
2066}
2067
888dde65 2068void wxMSWDCImpl::SetUserScale(double x, double y)
2bda0e17 2069{
82a306b7 2070 WXMICROWIN_CHECK_HDC
d275c7eb 2071
1e2081a1
VZ
2072 if ( x == m_userScaleX && y == m_userScaleY )
2073 return;
2074
888dde65 2075 wxDCImpl::SetUserScale(x,y);
1ee280b7 2076
04ab8b6d 2077 RealizeScaleAndOrigin();
2bda0e17
KB
2078}
2079
888dde65 2080void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight,
04ab8b6d 2081 bool yBottomUp)
6f65e337 2082{
82a306b7 2083 WXMICROWIN_CHECK_HDC
d275c7eb 2084
1e2081a1
VZ
2085 int signX = xLeftRight ? 1 : -1,
2086 signY = yBottomUp ? -1 : 1;
1ee280b7 2087
04ab8b6d 2088 if (signX == m_signX && signY == m_signY)
1e2081a1 2089 return;
1ee280b7 2090
888dde65 2091 wxDCImpl::SetAxisOrientation( xLeftRight, yBottomUp );
1e2081a1 2092
04ab8b6d 2093 RealizeScaleAndOrigin();
2bda0e17
KB
2094}
2095
888dde65 2096void wxMSWDCImpl::SetLogicalOrigin(wxCoord x, wxCoord y)
2bda0e17 2097{
82a306b7 2098 WXMICROWIN_CHECK_HDC
d275c7eb 2099
1e2081a1
VZ
2100 if ( x == m_logicalOriginX && y == m_logicalOriginY )
2101 return;
2102
888dde65 2103 wxDCImpl::SetLogicalOrigin( x, y );
a23fd0e1 2104
d225267e
RR
2105 RealizeScaleAndOrigin();
2106}
2107
2108// For use by wxWidgets only, unless custom units are required.
2109void wxMSWDCImpl::SetLogicalScale(double x, double y)
2110{
2111 WXMICROWIN_CHECK_HDC
2112
2113 wxDCImpl::SetLogicalScale(x,y);
2bda0e17
KB
2114}
2115
888dde65 2116void wxMSWDCImpl::SetDeviceOrigin(wxCoord x, wxCoord y)
2bda0e17 2117{
82a306b7 2118 WXMICROWIN_CHECK_HDC
d275c7eb 2119
1e2081a1
VZ
2120 if ( x == m_deviceOriginX && y == m_deviceOriginY )
2121 return;
1ee280b7 2122
888dde65 2123 wxDCImpl::SetDeviceOrigin( x, y );
2bda0e17 2124
a23fd0e1 2125 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
2bda0e17
KB
2126}
2127
e71508e1
VZ
2128// ----------------------------------------------------------------------------
2129// Transform matrix
2130// ----------------------------------------------------------------------------
2131
2132#if wxUSE_DC_TRANSFORM_MATRIX
2133
2134bool wxMSWDCImpl::CanUseTransformMatrix() const
2135{
2136 return GdiWorldTransformFuncs::IsOk();
2137}
2138
2139bool wxMSWDCImpl::SetTransformMatrix(const wxAffineMatrix2D &matrix)
2140{
2141 if ( !GdiWorldTransformFuncs::IsOk() )
2142 return false;
2143
2144 if ( matrix.IsIdentity() )
2145 {
2146 ResetTransformMatrix();
2147 return true;
2148 }
2149
2150 if ( !GdiWorldTransformFuncs::SetGraphicsMode()(GetHdc(), GM_ADVANCED) )
2151 {
2152 wxLogLastError(wxT("SetGraphicsMode"));
2153 return false;
2154 }
2155
2156 wxMatrix2D mat;
2157 wxPoint2DDouble tr;
2158 matrix.Get(&mat, &tr);
2159
2160 XFORM xform;
2161 xform.eM11 = mat.m_11;
2162 xform.eM12 = mat.m_12;
2163 xform.eM21 = mat.m_21;
2164 xform.eM22 = mat.m_22;
2165 xform.eDx = tr.m_x;
2166 xform.eDy = tr.m_y;
2167
2168 if ( !GdiWorldTransformFuncs::SetWorldTransform()(GetHdc(), &xform) )
2169 {
2170 wxLogLastError(wxT("SetWorldTransform"));
2171 return false;
2172 }
2173
2174 return true;
2175}
2176
2177wxAffineMatrix2D wxMSWDCImpl::GetTransformMatrix() const
2178{
2179 wxAffineMatrix2D transform;
2180
2181 if ( !GdiWorldTransformFuncs::IsOk() )
2182 return transform;
2183
2184 XFORM xform;
2185 if ( !GdiWorldTransformFuncs::GetWorldTransform()(GetHdc(), &xform) )
2186 {
2187 wxLogLastError(wxT("GetWorldTransform"));
2188 return transform;
2189 }
2190
2191 wxMatrix2D m(xform.eM11, xform.eM12, xform.eM21, xform.eM22);
2192 wxPoint2DDouble p(xform.eDx, xform.eDy);
2193 transform.Set(m, p);
2194
2195 return transform;
2196}
2197
2198void wxMSWDCImpl::ResetTransformMatrix()
2199{
2200 if ( GdiWorldTransformFuncs::IsOk() )
2201 {
2202 GdiWorldTransformFuncs::ModifyWorldTransform()(GetHdc(), NULL, MWT_IDENTITY);
2203 GdiWorldTransformFuncs::SetGraphicsMode()(GetHdc(), GM_COMPATIBLE);
2204 }
2205}
2206
2207#endif // wxUSE_DC_TRANSFORM_MATRIX
2208
a23fd0e1
VZ
2209// ---------------------------------------------------------------------------
2210// bit blit
2211// ---------------------------------------------------------------------------
04ab8b6d 2212
888dde65 2213bool wxMSWDCImpl::DoBlit(wxCoord dstX, wxCoord dstY,
e3b81044
VZ
2214 wxCoord dstWidth, wxCoord dstHeight,
2215 wxDC *source,
2216 wxCoord srcX, wxCoord srcY,
89efaf2b 2217 wxRasterOperationMode rop, bool useMask,
e3b81044
VZ
2218 wxCoord srcMaskX, wxCoord srcMaskY)
2219{
2220 return DoStretchBlit(dstX, dstY, dstWidth, dstHeight, source, srcX, srcY, dstWidth, dstHeight, rop, useMask, srcMaskX, srcMaskY);
2221}
2222
888dde65 2223bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest, wxCoord ydest,
e3b81044
VZ
2224 wxCoord dstWidth, wxCoord dstHeight,
2225 wxDC *source,
2226 wxCoord xsrc, wxCoord ysrc,
2227 wxCoord srcWidth, wxCoord srcHeight,
89efaf2b 2228 wxRasterOperationMode rop, bool useMask,
e3b81044 2229 wxCoord xsrcMask, wxCoord ysrcMask)
2bda0e17 2230{
9a83f860 2231 wxCHECK_MSG( source, false, wxT("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
275a63e3 2232
beb966c5 2233 WXMICROWIN_CHECK_HDC_RET(false)
d275c7eb 2234
a619d8c9
VZ
2235 wxMSWDCImpl *implSrc = wxDynamicCast( source->GetImpl(), wxMSWDCImpl );
2236 if ( !implSrc )
888dde65 2237 {
a619d8c9 2238 // TODO: Do we want to be able to blit from other DCs too?
888dde65
RR
2239 return false;
2240 }
2241
a619d8c9
VZ
2242 const HDC hdcSrc = GetHdcOf(*implSrc);
2243
57c26f82
VZ
2244 // if either the source or destination has alpha channel, we must use
2245 // AlphaBlt() as other function don't handle it correctly
a619d8c9 2246 const wxBitmap& bmpSrc = implSrc->GetSelectedBitmap();
87d3576f
RR
2247 if ( bmpSrc.IsOk() && (bmpSrc.HasAlpha() ||
2248 (m_selectedBitmap.IsOk() && m_selectedBitmap.HasAlpha())) )
275a63e3 2249 {
e3b81044 2250 if ( AlphaBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
a619d8c9 2251 xsrc, ysrc, srcWidth, srcHeight, hdcSrc, bmpSrc) )
beb966c5 2252 return true;
275a63e3 2253 }
d80f416f 2254
4b7f2165
VZ
2255 wxMask *mask = NULL;
2256 if ( useMask )
2257 {
d80f416f 2258 mask = bmpSrc.GetMask();
4b7f2165 2259
87d3576f 2260 if ( !(bmpSrc.IsOk() && mask && mask->GetMaskBitmap()) )
d5536ade
VZ
2261 {
2262 // don't give assert here because this would break existing
2263 // programs - just silently ignore useMask parameter
beb966c5 2264 useMask = false;
d5536ade 2265 }
4b7f2165 2266 }
a23fd0e1 2267
0cbff120
JS
2268 if (xsrcMask == -1 && ysrcMask == -1)
2269 {
2270 xsrcMask = xsrc; ysrcMask = ysrc;
2271 }
2272
76f91e77 2273 wxTextColoursChanger textCol(GetHdc(), *this);
a23fd0e1 2274
999836aa 2275 DWORD dwRop;
71fe5c01
RR
2276 switch (rop)
2277 {
ed791986
VZ
2278 case wxXOR: dwRop = SRCINVERT; break;
2279 case wxINVERT: dwRop = DSTINVERT; break;
2280 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
2281 case wxAND_REVERSE: dwRop = SRCERASE; break;
2282 case wxCLEAR: dwRop = BLACKNESS; break;
2283 case wxSET: dwRop = WHITENESS; break;
2284 case wxOR_INVERT: dwRop = MERGEPAINT; break;
2285 case wxAND: dwRop = SRCAND; break;
2286 case wxOR: dwRop = SRCPAINT; break;
2287 case wxEQUIV: dwRop = 0x00990066; break;
2288 case wxNAND: dwRop = 0x007700E6; break;
2289 case wxAND_INVERT: dwRop = 0x00220326; break;
2290 case wxCOPY: dwRop = SRCCOPY; break;
4aff28fc 2291 case wxNO_OP: dwRop = DSTCOPY; break;
ed791986 2292 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
71fe5c01
RR
2293 case wxNOR: dwRop = NOTSRCCOPY; break;
2294 default:
71fe5c01 2295 wxFAIL_MSG( wxT("unsupported logical function") );
beb966c5 2296 return false;
71fe5c01
RR
2297 }
2298
beb966c5 2299 bool success = false;
730bc726
JS
2300
2301 if (useMask)
7bcb11d3 2302 {
4b7f2165 2303#ifdef __WIN32__
a58a12e9 2304 // we want the part of the image corresponding to the mask to be
4aff28fc
VZ
2305 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2306 // meaning of fg and bg is inverted which corresponds to wxWin notion
2307 // of the mask which is also contrary to the Windows one)
d3211838
JS
2308
2309 // On some systems, MaskBlt succeeds yet is much much slower
77ffb593 2310 // than the wxWidgets fall-back implementation. So we need
d3211838 2311 // to be able to switch this on and off at runtime.
0cbff120
JS
2312#if wxUSE_SYSTEM_OPTIONS
2313 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
2314#endif
d3211838 2315 {
e3b81044
VZ
2316 if ( dstWidth == srcWidth && dstHeight == srcHeight )
2317 {
2318 success = ::MaskBlt
2319 (
f178ab7e 2320 GetHdc(),
e3b81044 2321 xdest, ydest, dstWidth, dstHeight,
a619d8c9 2322 hdcSrc,
f178ab7e
VZ
2323 xsrc, ysrc,
2324 (HBITMAP)mask->GetMaskBitmap(),
2325 xsrcMask, ysrcMask,
2326 MAKEROP4(dwRop, DSTCOPY)
e3b81044
VZ
2327 ) != 0;
2328 }
d3211838 2329 }
a58a12e9
VZ
2330
2331 if ( !success )
4b7f2165 2332#endif // Win32
7bcb11d3 2333 {
7bcb11d3 2334 // Blit bitmap with mask
0cbff120
JS
2335 HDC dc_mask ;
2336 HDC dc_buffer ;
2337 HBITMAP buffer_bmap ;
a23fd0e1 2338
0cbff120 2339#if wxUSE_DC_CACHEING
2b96d0fb 2340 // create a temp buffer bitmap and DCs to access it and the mask
a619d8c9 2341 wxDCCacheEntry* dcCacheEntry1 = FindDCInCache(NULL, hdcSrc);
2b96d0fb
VZ
2342 dc_mask = (HDC) dcCacheEntry1->m_dc;
2343
2344 wxDCCacheEntry* dcCacheEntry2 = FindDCInCache(dcCacheEntry1, GetHDC());
2345 dc_buffer = (HDC) dcCacheEntry2->m_dc;
2346
2347 wxDCCacheEntry* bitmapCacheEntry = FindBitmapInCache(GetHDC(),
e3b81044 2348 dstWidth, dstHeight);
2b96d0fb
VZ
2349
2350 buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap;
2351#else // !wxUSE_DC_CACHEING
2352 // create a temp buffer bitmap and DCs to access it and the mask
a619d8c9 2353 dc_mask = ::CreateCompatibleDC(hdcSrc);
2b96d0fb 2354 dc_buffer = ::CreateCompatibleDC(GetHdc());
e3b81044 2355 buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), dstWidth, dstHeight);
619e52bf 2356#endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
a230101e
VZ
2357 HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
2358 HGDIOBJ hOldBufferBitmap = ::SelectObject(dc_buffer, buffer_bmap);
4b7f2165
VZ
2359
2360 // copy dest to buffer
76f91e77 2361 if ( !::BitBlt(dc_buffer, 0, 0, dstWidth, dstHeight,
4b7f2165 2362 GetHdc(), xdest, ydest, SRCCOPY) )
7bcb11d3 2363 {
f6bcfd97 2364 wxLogLastError(wxT("BitBlt"));
7bcb11d3 2365 }
4b7f2165 2366
4f64fde7 2367 SET_STRETCH_BLT_MODE(GetHdc());
e3b81044 2368
4b7f2165 2369 // copy src to buffer using selected raster op
76f91e77 2370 if ( !::StretchBlt(dc_buffer, 0, 0, dstWidth, dstHeight,
a619d8c9 2371 hdcSrc, xsrc, ysrc, srcWidth, srcHeight, dwRop) )
7bcb11d3 2372 {
e3b81044 2373 wxLogLastError(wxT("StretchBlt"));
7bcb11d3 2374 }
4b7f2165 2375
76f91e77 2376 // set masked area in buffer to BLACK
4b7f2165 2377 {
76f91e77
VZ
2378 wxTextColoursChanger textCol2(GetHdc(), *wxBLACK, *wxWHITE);
2379 if ( !::StretchBlt(dc_buffer, 0, 0, dstWidth, dstHeight,
2380 dc_mask, xsrcMask, ysrcMask,
2381 srcWidth, srcHeight, SRCAND) )
2382 {
2383 wxLogLastError(wxT("StretchBlt"));
2384 }
4b7f2165 2385
76f91e77
VZ
2386 // set unmasked area in dest to BLACK
2387 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2388 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2389 if ( !::StretchBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2390 dc_mask, xsrcMask, ysrcMask,
2391 srcWidth, srcHeight, SRCAND) )
2392 {
2393 wxLogLastError(wxT("StretchBlt"));
2394 }
2395 } // restore the original text and background colours
4b7f2165
VZ
2396
2397 // OR buffer to dest
76f91e77 2398 success = ::BitBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
4b7f2165
VZ
2399 dc_buffer, 0, 0, SRCPAINT) != 0;
2400 if ( !success )
2401 {
f6bcfd97 2402 wxLogLastError(wxT("BitBlt"));
4b7f2165
VZ
2403 }
2404
2405 // tidy up temporary DCs and bitmap
62e1ba75
JS
2406 ::SelectObject(dc_mask, hOldMaskBitmap);
2407 ::SelectObject(dc_buffer, hOldBufferBitmap);
0cbff120 2408
27748047 2409#if !wxUSE_DC_CACHEING
0cbff120
JS
2410 {
2411 ::DeleteDC(dc_mask);
2412 ::DeleteDC(dc_buffer);
2413 ::DeleteObject(buffer_bmap);
2414 }
27748047 2415#endif
7bcb11d3
JS
2416 }
2417 }
4b7f2165 2418 else // no mask, just BitBlt() it
730bc726 2419 {
d80f416f
VZ
2420 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2421 // use StretchBlt() if available and finally fall back to BitBlt()
4676948b
JS
2422
2423 // FIXME: use appropriate WinCE functions
2424#ifndef __WXWINCE__
d80f416f 2425 const int caps = ::GetDeviceCaps(GetHdc(), RASTERCAPS);
87d3576f 2426 if ( bmpSrc.IsOk() && (caps & RC_STRETCHDIB) )
f178ab7e 2427 {
d80f416f
VZ
2428 DIBSECTION ds;
2429 wxZeroMemory(ds);
f178ab7e 2430
d80f416f
VZ
2431 if ( ::GetObject(GetHbitmapOf(bmpSrc),
2432 sizeof(ds),
2433 &ds) == sizeof(ds) )
2434 {
4f64fde7 2435 SET_STRETCH_BLT_MODE(GetHdc());
d80f416f 2436
b9b1f368
VZ
2437 // Figure out what co-ordinate system we're supposed to specify
2438 // ysrc in.
2439 const LONG hDIB = ds.dsBmih.biHeight;
2440 if ( hDIB > 0 )
2441 {
2442 // reflect ysrc
4b871421 2443 ysrc = hDIB - (ysrc + srcHeight);
b9b1f368
VZ
2444 }
2445
d80f416f
VZ
2446 if ( ::StretchDIBits(GetHdc(),
2447 xdest, ydest,
e3b81044 2448 dstWidth, dstHeight,
b9b1f368 2449 xsrc, ysrc,
e3b81044 2450 srcWidth, srcHeight,
d80f416f
VZ
2451 ds.dsBm.bmBits,
2452 (LPBITMAPINFO)&ds.dsBmih,
2453 DIB_RGB_COLORS,
b7a0abc7 2454 dwRop
d80f416f
VZ
2455 ) == (int)GDI_ERROR )
2456 {
85e7fb12
RD
2457 // On Win9x this API fails most (all?) of the time, so
2458 // logging it becomes quite distracting. Since it falls
2459 // back to the code below this is not really serious, so
35bbb0c6 2460 // don't log it.
85e7fb12 2461 //wxLogLastError(wxT("StretchDIBits"));
d80f416f
VZ
2462 }
2463 else
2464 {
beb966c5 2465 success = true;
d80f416f
VZ
2466 }
2467 }
f178ab7e 2468 }
d80f416f
VZ
2469
2470 if ( !success && (caps & RC_STRETCHBLT) )
419430a0
JS
2471#endif
2472 // __WXWINCE__
f178ab7e 2473 {
4f64fde7 2474 SET_STRETCH_BLT_MODE(GetHdc());
d80f416f
VZ
2475
2476 if ( !::StretchBlt
2477 (
2478 GetHdc(),
e3b81044 2479 xdest, ydest, dstWidth, dstHeight,
a619d8c9 2480 hdcSrc,
e3b81044 2481 xsrc, ysrc, srcWidth, srcHeight,
d80f416f
VZ
2482 dwRop
2483 ) )
2484 {
9a83f860 2485 wxLogLastError(wxT("StretchBlt"));
d80f416f
VZ
2486 }
2487 else
2488 {
beb966c5 2489 success = true;
d80f416f 2490 }
f178ab7e
VZ
2491 }
2492
4b7f2165 2493 if ( !success )
730bc726 2494 {
76f91e77
VZ
2495 if ( !::BitBlt(GetHdc(), xdest, ydest, dstWidth, dstHeight,
2496 hdcSrc, xsrc, ysrc, dwRop) )
d80f416f 2497 {
9a83f860 2498 wxLogLastError(wxT("BitBlt"));
d80f416f
VZ
2499 }
2500 else
2501 {
beb966c5 2502 success = true;
d80f416f 2503 }
730bc726 2504 }
730bc726 2505 }
f178ab7e 2506
a23fd0e1 2507 return success;
2bda0e17
KB
2508}
2509
888dde65 2510void wxMSWDCImpl::GetDeviceSize(int *width, int *height) const
2bda0e17 2511{
82a306b7 2512 WXMICROWIN_CHECK_HDC
d275c7eb 2513
7d09b97f
VZ
2514 if ( width )
2515 *width = ::GetDeviceCaps(GetHdc(), HORZRES);
2516 if ( height )
2517 *height = ::GetDeviceCaps(GetHdc(), VERTRES);
2bda0e17
KB
2518}
2519
888dde65 2520void wxMSWDCImpl::DoGetSizeMM(int *w, int *h) const
2bda0e17 2521{
82a306b7 2522 WXMICROWIN_CHECK_HDC
d275c7eb 2523
994a3786
VZ
2524 // if we implement it in terms of DoGetSize() instead of directly using the
2525 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2526 // will also work for wxWindowDC and wxClientDC even though their size is
2527 // not the same as the total size of the screen
2528 int wPixels, hPixels;
2529 DoGetSize(&wPixels, &hPixels);
2530
2531 if ( w )
2532 {
2533 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
2534
9a83f860 2535 wxCHECK_RET( wTotal, wxT("0 width device?") );
994a3786
VZ
2536
2537 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2538 }
2539
2540 if ( h )
2541 {
2542 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2543
9a83f860 2544 wxCHECK_RET( hTotal, wxT("0 height device?") );
994a3786
VZ
2545
2546 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2547 }
7bcb11d3 2548}
2bda0e17 2549
888dde65 2550wxSize wxMSWDCImpl::GetPPI() const
7bcb11d3 2551{
c47addef 2552 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
d275c7eb 2553
a23fd0e1
VZ
2554 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2555 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2bda0e17 2556
a23fd0e1 2557 return wxSize(x, y);
2bda0e17
KB
2558}
2559
878711c0
VZ
2560// ----------------------------------------------------------------------------
2561// DC caching
2562// ----------------------------------------------------------------------------
2563
0cbff120
JS
2564#if wxUSE_DC_CACHEING
2565
2566/*
2567 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2568 * improve it in due course, either using arrays, or simply storing pointers to one
2569 * entry for the bitmap, and two for the DCs. -- JACS
2570 */
2571
888dde65
RR
2572wxObjectList wxMSWDCImpl::sm_bitmapCache;
2573wxObjectList wxMSWDCImpl::sm_dcCache;
0cbff120
JS
2574
2575wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2576{
2577 m_bitmap = hBitmap;
2578 m_dc = 0;
2579 m_width = w;
2580 m_height = h;
2581 m_depth = depth;
2582}
2583
2584wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2585{
2586 m_bitmap = 0;
2587 m_dc = hDC;
2588 m_width = 0;
2589 m_height = 0;
2590 m_depth = depth;
2591}
2592
2593wxDCCacheEntry::~wxDCCacheEntry()
2594{
2595 if (m_bitmap)
2596 ::DeleteObject((HBITMAP) m_bitmap);
2597 if (m_dc)
2598 ::DeleteDC((HDC) m_dc);
2599}
2600
888dde65 2601wxDCCacheEntry* wxMSWDCImpl::FindBitmapInCache(WXHDC dc, int w, int h)
0cbff120
JS
2602{
2603 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
fc10daf3 2604 wxList::compatibility_iterator node = sm_bitmapCache.GetFirst();
0cbff120
JS
2605 while (node)
2606 {
4a9dba0e 2607 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
0cbff120
JS
2608
2609 if (entry->m_depth == depth)
2610 {
2611 if (entry->m_width < w || entry->m_height < h)
2612 {
2613 ::DeleteObject((HBITMAP) entry->m_bitmap);
2614 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2615 if ( !entry->m_bitmap)
2616 {
2617 wxLogLastError(wxT("CreateCompatibleBitmap"));
2618 }
2619 entry->m_width = w; entry->m_height = h;
2620 return entry;
2621 }
2622 return entry;
2623 }
2624
4a9dba0e 2625 node = node->GetNext();
0cbff120
JS
2626 }
2627 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2628 if ( !hBitmap)
2629 {
2630 wxLogLastError(wxT("CreateCompatibleBitmap"));
2631 }
2632 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2633 AddToBitmapCache(entry);
2634 return entry;
2635}
2636
888dde65 2637wxDCCacheEntry* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
0cbff120
JS
2638{
2639 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
fc10daf3 2640 wxList::compatibility_iterator node = sm_dcCache.GetFirst();
0cbff120
JS
2641 while (node)
2642 {
4a9dba0e 2643 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
0cbff120
JS
2644
2645 // Don't return the same one as we already have
2646 if (!notThis || (notThis != entry))
2647 {
2648 if (entry->m_depth == depth)
2649 {
2650 return entry;
2651 }
2652 }
2653
4a9dba0e 2654 node = node->GetNext();
0cbff120
JS
2655 }
2656 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2657 if ( !hDC)
2658 {
2659 wxLogLastError(wxT("CreateCompatibleDC"));
2660 }
2661 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2662 AddToDCCache(entry);
2663 return entry;
2664}
2665
888dde65 2666void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry* entry)
0cbff120
JS
2667{
2668 sm_bitmapCache.Append(entry);
2669}
2670
888dde65 2671void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry* entry)
0cbff120
JS
2672{
2673 sm_dcCache.Append(entry);
2674}
2675
888dde65 2676void wxMSWDCImpl::ClearCache()
0cbff120 2677{
fc10daf3
MB
2678 WX_CLEAR_LIST(wxList, sm_dcCache);
2679 WX_CLEAR_LIST(wxList, sm_bitmapCache);
0cbff120
JS
2680}
2681
aef94d68
JS
2682// Clean up cache at app exit
2683class wxDCModule : public wxModule
2684{
2685public:
beb966c5 2686 virtual bool OnInit() { return true; }
888dde65 2687 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
aef94d68
JS
2688
2689private:
2690 DECLARE_DYNAMIC_CLASS(wxDCModule)
2691};
2692
2693IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2694
878711c0
VZ
2695#endif // wxUSE_DC_CACHEING
2696
2697// ----------------------------------------------------------------------------
275a63e3 2698// alpha channel support
878711c0
VZ
2699// ----------------------------------------------------------------------------
2700
275a63e3 2701static bool AlphaBlt(HDC hdcDst,
e3b81044 2702 int x, int y, int dstWidth, int dstHeight,
1ee280b7 2703 int srcX, int srcY,
e3b81044
VZ
2704 int srcWidth, int srcHeight,
2705 HDC hdcSrc,
275a63e3
VZ
2706 const wxBitmap& bmp)
2707{
9a83f860
VZ
2708 wxASSERT_MSG( bmp.IsOk() && bmp.HasAlpha(), wxT("AlphaBlt(): invalid bitmap") );
2709 wxASSERT_MSG( hdcDst && hdcSrc, wxT("AlphaBlt(): invalid HDC") );
275a63e3
VZ
2710
2711 // do we have AlphaBlend() and company in the headers?
3080bf59 2712#if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
275a63e3
VZ
2713 // yes, now try to see if we have it during run-time
2714 typedef BOOL (WINAPI *AlphaBlend_t)(HDC,int,int,int,int,
2715 HDC,int,int,int,int,
2716 BLENDFUNCTION);
2717
6ae7410f 2718 static AlphaBlend_t
9a83f860 2719 pfnAlphaBlend = (AlphaBlend_t)wxMSIMG32DLL.GetSymbol(wxT("AlphaBlend"));
275a63e3
VZ
2720 if ( pfnAlphaBlend )
2721 {
2722 BLENDFUNCTION bf;
2723 bf.BlendOp = AC_SRC_OVER;
2724 bf.BlendFlags = 0;
2725 bf.SourceConstantAlpha = 0xff;
2726 bf.AlphaFormat = AC_SRC_ALPHA;
2727
e3b81044
VZ
2728 if ( pfnAlphaBlend(hdcDst, x, y, dstWidth, dstHeight,
2729 hdcSrc, srcX, srcY, srcWidth, srcHeight,
275a63e3
VZ
2730 bf) )
2731 {
2732 // skip wxAlphaBlend() call below
beb966c5 2733 return true;
275a63e3
VZ
2734 }
2735
9a83f860 2736 wxLogLastError(wxT("AlphaBlend"));
275a63e3 2737 }
41e155b4
WS
2738#else
2739 wxUnusedVar(hdcSrc);
275a63e3
VZ
2740#endif // defined(AC_SRC_OVER)
2741
2742 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2743 // implementation
3fb1e059 2744#ifdef wxHAS_RAW_BITMAP
e3b81044 2745 wxAlphaBlend(hdcDst, x, y, dstWidth, dstHeight, srcX, srcY, srcWidth, srcHeight, bmp);
275a63e3 2746
beb966c5 2747 return true;
3fb1e059 2748#else // !wxHAS_RAW_BITMAP
275a63e3
VZ
2749 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2750 // alpha but at least something will be shown like this)
907173e5 2751 wxUnusedVar(bmp);
beb966c5 2752 return false;
3fb1e059 2753#endif // wxHAS_RAW_BITMAP/!wxHAS_RAW_BITMAP
275a63e3
VZ
2754}
2755
2756
2757// wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
3fb1e059 2758#ifdef wxHAS_RAW_BITMAP
275a63e3 2759
878711c0 2760static void
761598d4 2761wxAlphaBlend(HDC hdcDst, int xDst, int yDst,
e3b81044 2762 int dstWidth, int dstHeight,
1ee280b7 2763 int srcX, int srcY,
e3b81044
VZ
2764 int srcWidth, int srcHeight,
2765 const wxBitmap& bmpSrc)
878711c0
VZ
2766{
2767 // get the destination DC pixels
e3b81044 2768 wxBitmap bmpDst(dstWidth, dstHeight, 32 /* force creating RGBA DIB */);
878711c0
VZ
2769 MemoryHDC hdcMem;
2770 SelectInHDC select(hdcMem, GetHbitmapOf(bmpDst));
2771
e3b81044 2772 if ( !::BitBlt(hdcMem, 0, 0, dstWidth, dstHeight, hdcDst, xDst, yDst, SRCCOPY) )
878711c0 2773 {
9a83f860 2774 wxLogLastError(wxT("BitBlt"));
878711c0
VZ
2775 }
2776
2777 // combine them with the source bitmap using alpha
b9bcaf11 2778 wxAlphaPixelData dataDst(bmpDst),
a452af5e 2779 dataSrc((wxBitmap &)bmpSrc);
878711c0 2780
8ffc8f1f 2781 wxCHECK_RET( dataDst && dataSrc,
9a83f860 2782 wxT("failed to get raw data in wxAlphaBlend") );
8ffc8f1f 2783
b9bcaf11
VZ
2784 wxAlphaPixelData::Iterator pDst(dataDst),
2785 pSrc(dataSrc);
878711c0 2786
3db79902 2787
e3b81044 2788 for ( int y = 0; y < dstHeight; y++ )
878711c0 2789 {
e3b81044 2790 wxAlphaPixelData::Iterator pDstRowStart = pDst;
0c0d1521 2791
e3b81044 2792 for ( int x = 0; x < dstWidth; x++ )
878711c0 2793 {
e3b81044
VZ
2794 // source is point sampled, Alpha StretchBlit is ugly on Win95
2795 // (but does not impact performance)
2796 pSrc.MoveTo(dataSrc, srcX + (srcWidth*x/dstWidth), srcY + (srcHeight*y/dstHeight));
2797
878711c0
VZ
2798 // note that source bitmap uses premultiplied alpha (as required by
2799 // the real AlphaBlend)
2800 const unsigned beta = 255 - pSrc.Alpha();
2801
2802 pDst.Red() = pSrc.Red() + (beta * pDst.Red() + 127) / 255;
2803 pDst.Blue() = pSrc.Blue() + (beta * pDst.Blue() + 127) / 255;
2804 pDst.Green() = pSrc.Green() + (beta * pDst.Green() + 127) / 255;
2805
2806 ++pDst;
878711c0
VZ
2807 }
2808
2809 pDst = pDstRowStart;
b9bcaf11 2810 pDst.OffsetY(dataDst, 1);
878711c0
VZ
2811 }
2812
2813 // and finally blit them back to the destination DC
e3b81044 2814 if ( !::BitBlt(hdcDst, xDst, yDst, dstWidth, dstHeight, hdcMem, 0, 0, SRCCOPY) )
878711c0 2815 {
9a83f860 2816 wxLogLastError(wxT("BitBlt"));
878711c0
VZ
2817 }
2818}
7b46ecac 2819
3fb1e059 2820#endif // wxHAS_RAW_BITMAP
213ad8e7 2821
888dde65 2822void wxMSWDCImpl::DoGradientFillLinear (const wxRect& rect,
213ad8e7
VZ
2823 const wxColour& initialColour,
2824 const wxColour& destColour,
2825 wxDirection nDirection)
2826{
2827 // use native function if we have compile-time support it and can load it
2828 // during run-time (linking to it statically would make the program
2829 // unusable on earlier Windows versions)
2830#if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2831 typedef BOOL
2832 (WINAPI *GradientFill_t)(HDC, PTRIVERTEX, ULONG, PVOID, ULONG, ULONG);
6ae7410f 2833 static GradientFill_t pfnGradientFill =
9a83f860 2834 (GradientFill_t)wxMSIMG32DLL.GetSymbol(wxT("GradientFill"));
213ad8e7
VZ
2835
2836 if ( pfnGradientFill )
2837 {
2838 GRADIENT_RECT grect;
2839 grect.UpperLeft = 0;
2840 grect.LowerRight = 1;
2841
2842 // invert colours direction if not filling from left-to-right or
2843 // top-to-bottom
2844 int firstVertex = nDirection == wxNORTH || nDirection == wxWEST ? 1 : 0;
2845
2846 // one vertex for upper left and one for upper-right
2847 TRIVERTEX vertices[2];
2848
2849 vertices[0].x = rect.GetLeft();
2850 vertices[0].y = rect.GetTop();
e6c46ffe
BW
2851 vertices[1].x = rect.GetRight()+1;
2852 vertices[1].y = rect.GetBottom()+1;
213ad8e7 2853
2e98a222
WS
2854 vertices[firstVertex].Red = (COLOR16)(initialColour.Red() << 8);
2855 vertices[firstVertex].Green = (COLOR16)(initialColour.Green() << 8);
2856 vertices[firstVertex].Blue = (COLOR16)(initialColour.Blue() << 8);
213ad8e7 2857 vertices[firstVertex].Alpha = 0;
2e98a222
WS
2858 vertices[1 - firstVertex].Red = (COLOR16)(destColour.Red() << 8);
2859 vertices[1 - firstVertex].Green = (COLOR16)(destColour.Green() << 8);
2860 vertices[1 - firstVertex].Blue = (COLOR16)(destColour.Blue() << 8);
213ad8e7
VZ
2861 vertices[1 - firstVertex].Alpha = 0;
2862
213ad8e7
VZ
2863 if ( (*pfnGradientFill)
2864 (
2865 GetHdc(),
2866 vertices,
2867 WXSIZEOF(vertices),
2868 &grect,
2869 1,
2870 nDirection == wxWEST || nDirection == wxEAST
2871 ? GRADIENT_FILL_RECT_H
2872 : GRADIENT_FILL_RECT_V
2873 ) )
2874 {
2875 // skip call of the base class version below
2876 return;
2877 }
2878
9a83f860 2879 wxLogLastError(wxT("GradientFill"));
213ad8e7
VZ
2880 }
2881#endif // wxUSE_DYNLIB_CLASS
2882
888dde65 2883 wxDCImpl::DoGradientFillLinear(rect, initialColour, destColour, nDirection);
213ad8e7 2884}
6ae7410f 2885
a8ff046b
VZ
2886#if wxUSE_DYNLIB_CLASS
2887
6ae7410f
VZ
2888static DWORD wxGetDCLayout(HDC hdc)
2889{
2890 typedef DWORD (WINAPI *GetLayout_t)(HDC);
2891 static GetLayout_t
9a83f860 2892 wxDL_INIT_FUNC(s_pfn, GetLayout, wxDynamicLibrary(wxT("gdi32.dll")));
6ae7410f 2893
60b0c3b4 2894 return s_pfnGetLayout ? s_pfnGetLayout(hdc) : (DWORD)-1;
6ae7410f
VZ
2895}
2896
888dde65 2897wxLayoutDirection wxMSWDCImpl::GetLayoutDirection() const
6ae7410f
VZ
2898{
2899 DWORD layout = wxGetDCLayout(GetHdc());
2900
2901 if ( layout == (DWORD)-1 )
2902 return wxLayout_Default;
2903
2904 return layout & LAYOUT_RTL ? wxLayout_RightToLeft : wxLayout_LeftToRight;
2905}
2906
888dde65 2907void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir)
6ae7410f
VZ
2908{
2909 typedef DWORD (WINAPI *SetLayout_t)(HDC, DWORD);
2910 static SetLayout_t
9a83f860 2911 wxDL_INIT_FUNC(s_pfn, SetLayout, wxDynamicLibrary(wxT("gdi32.dll")));
60b0c3b4 2912 if ( !s_pfnSetLayout )
6ae7410f
VZ
2913 return;
2914
2915 if ( dir == wxLayout_Default )
2916 {
2917 dir = wxTheApp->GetLayoutDirection();
2918 if ( dir == wxLayout_Default )
2919 return;
2920 }
2921
2922 DWORD layout = wxGetDCLayout(GetHdc());
2923 if ( dir == wxLayout_RightToLeft )
2924 layout |= LAYOUT_RTL;
2925 else
2926 layout &= ~LAYOUT_RTL;
2927
60b0c3b4 2928 s_pfnSetLayout(GetHdc(), layout);
6ae7410f 2929}
a8ff046b
VZ
2930
2931#else // !wxUSE_DYNLIB_CLASS
2932
2933// we can't provide RTL support without dynamic loading, so stub it out
888dde65 2934wxLayoutDirection wxMSWDCImpl::GetLayoutDirection() const
a8ff046b
VZ
2935{
2936 return wxLayout_Default;
2937}
2938
888dde65 2939void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection WXUNUSED(dir))
a8ff046b
VZ
2940{
2941}
2942
2943#endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS