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