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