]> git.saurik.com Git - wxWidgets.git/blob - src/msw/graphics.cpp
Adding support for item font/style/color customization, unfortunately, it does not...
[wxWidgets.git] / src / msw / graphics.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/graphics.cpp
3 // Purpose: wxGCDC class
4 // Author: Stefan Csomor
5 // Modified by:
6 // Created: 2006-09-30
7 // RCS-ID: $Id$
8 // Copyright: (c) 2006 Stefan Csomor
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #include "wx/dc.h"
15
16 #ifdef __BORLANDC__
17 #pragma hdrstop
18 #endif
19
20 #ifndef WX_PRECOMP
21 #include "wx/msw/wrapcdlg.h"
22 #include "wx/image.h"
23 #include "wx/window.h"
24 #include "wx/dc.h"
25 #include "wx/utils.h"
26 #include "wx/dialog.h"
27 #include "wx/app.h"
28 #include "wx/bitmap.h"
29 #include "wx/dcmemory.h"
30 #include "wx/log.h"
31 #include "wx/icon.h"
32 #include "wx/dcprint.h"
33 #include "wx/module.h"
34 #endif
35
36 #include "wx/graphics.h"
37
38 #if wxUSE_GRAPHICS_CONTEXT
39
40 #include <vector>
41
42 using namespace std;
43
44 //-----------------------------------------------------------------------------
45 // constants
46 //-----------------------------------------------------------------------------
47
48 const double RAD2DEG = 180.0 / M_PI;
49
50 //-----------------------------------------------------------------------------
51 // Local functions
52 //-----------------------------------------------------------------------------
53
54 static inline double dmin(double a, double b) { return a < b ? a : b; }
55 static inline double dmax(double a, double b) { return a > b ? a : b; }
56
57 static inline double DegToRad(double deg) { return (deg * M_PI) / 180.0; }
58 static inline double RadToDeg(double deg) { return (deg * 180.0) / M_PI; }
59
60 //-----------------------------------------------------------------------------
61 // device context implementation
62 //
63 // more and more of the dc functionality should be implemented by calling
64 // the appropricate wxGDIPlusContext, but we will have to do that step by step
65 // also coordinate conversions should be moved to native matrix ops
66 //-----------------------------------------------------------------------------
67
68 // we always stock two context states, one at entry, to be able to preserve the
69 // state we were called with, the other one after changing to HI Graphics orientation
70 // (this one is used for getting back clippings etc)
71
72 //-----------------------------------------------------------------------------
73 // wxGraphicsPath implementation
74 //-----------------------------------------------------------------------------
75
76 #include "wx/msw/private.h" // needs to be before #include <commdlg.h>
77
78 #if wxUSE_COMMON_DIALOGS && !defined(__WXMICROWIN__)
79 #include <commdlg.h>
80 #endif
81
82 // TODO remove this dependency (gdiplus needs the macros)
83
84 #ifndef max
85 #define max(a,b) (((a) > (b)) ? (a) : (b))
86 #endif
87
88 #ifndef min
89 #define min(a,b) (((a) < (b)) ? (a) : (b))
90 #endif
91
92 #include "gdiplus.h"
93 using namespace Gdiplus;
94
95 class GDILoader
96 {
97 public :
98 GDILoader()
99 {
100 m_loaded = false;
101 m_gditoken = NULL;
102 }
103
104 ~GDILoader()
105 {
106 if (m_loaded)
107 {
108 Unload();
109 }
110 }
111 void EnsureIsLoaded()
112 {
113 if (!m_loaded)
114 {
115 Load();
116 }
117 }
118 void Load()
119 {
120 GdiplusStartupInput input;
121 GdiplusStartupOutput output;
122 GdiplusStartup(&m_gditoken,&input,&output);
123 m_loaded = true;
124 }
125 void Unload()
126 {
127 if ( m_gditoken )
128 GdiplusShutdown(m_gditoken);
129 }
130 private :
131 bool m_loaded;
132 DWORD m_gditoken;
133
134 };
135
136 static GDILoader gGDILoader;
137
138 class WXDLLEXPORT wxGDIPlusPath : public wxGraphicsPath
139 {
140 public :
141 wxGDIPlusPath();
142 ~wxGDIPlusPath();
143
144
145 //
146 // These are the path primitives from which everything else can be constructed
147 //
148
149 // begins a new subpath at (x,y)
150 virtual void MoveToPoint( wxDouble x, wxDouble y );
151
152 // adds a straight line from the current point to (x,y)
153 virtual void AddLineToPoint( wxDouble x, wxDouble y );
154
155 // adds a cubic Bezier curve from the current point, using two control points and an end point
156 virtual void AddCurveToPoint( wxDouble cx1, wxDouble cy1, wxDouble cx2, wxDouble cy2, wxDouble x, wxDouble y );
157
158
159 // adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle
160 virtual void AddArc( wxDouble x, wxDouble y, wxDouble r, wxDouble startAngle, wxDouble endAngle, bool clockwise ) ;
161
162 // gets the last point of the current path, (0,0) if not yet set
163 virtual void GetCurrentPoint( wxDouble& x, wxDouble&y) ;
164
165 // closes the current sub-path
166 virtual void CloseSubpath();
167
168 //
169 // These are convenience functions which - if not available natively will be assembled
170 // using the primitives from above
171 //
172
173 // appends a rectangle as a new closed subpath
174 virtual void AddRectangle( wxDouble x, wxDouble y, wxDouble w, wxDouble h ) ;
175 /*
176
177 // appends an ellipsis as a new closed subpath fitting the passed rectangle
178 virtual void AddEllipsis( wxDouble x, wxDouble y, wxDouble w , wxDouble h ) ;
179
180 // draws a an arc to two tangents connecting (current) to (x1,y1) and (x1,y1) to (x2,y2), also a straight line from (current) to (x1,y1)
181 virtual void AddArcToPoint( wxDouble x1, wxDouble y1 , wxDouble x2, wxDouble y2, wxDouble r ) ;
182 */
183
184 // returns the native path
185 virtual void * GetNativePath() const { return m_path; }
186
187 // give the native path returned by GetNativePath() back (there might be some deallocations necessary)
188 virtual void UnGetNativePath(void * WXUNUSED(path)) {}
189
190 private :
191 GraphicsPath* m_path;
192 DECLARE_DYNAMIC_CLASS_NO_COPY(wxGDIPlusPath)
193 };
194
195 class WXDLLEXPORT wxGDIPlusContext : public wxGraphicsContext
196 {
197 public:
198 wxGDIPlusContext( HDC hdc );
199 wxGDIPlusContext( HWND hwnd );
200 wxGDIPlusContext( Graphics* gr);
201 wxGDIPlusContext();
202
203 virtual ~wxGDIPlusContext();
204
205 virtual void Clip( const wxRegion &region );
206 // clips drawings to the rect
207 virtual void Clip( wxDouble x, wxDouble y, wxDouble w, wxDouble h );
208
209 // resets the clipping to original extent
210 virtual void ResetClip();
211
212 virtual void * GetNativeContext();
213
214 virtual void StrokePath( const wxGraphicsPath *p );
215 virtual void FillPath( const wxGraphicsPath *p , int fillStyle = wxWINDING_RULE );
216
217 virtual wxGraphicsPath* CreatePath();
218 virtual void SetPen( const wxPen &pen );
219 virtual void SetBrush( const wxBrush &brush );
220 virtual void SetLinearGradientBrush( wxDouble x1, wxDouble y1, wxDouble x2, wxDouble y2, const wxColour&c1, const wxColour&c2) ;
221 virtual void SetRadialGradientBrush( wxDouble xo, wxDouble yo, wxDouble xc, wxDouble yc, wxDouble radius,
222 const wxColour &oColor, const wxColour &cColor);
223
224 virtual void Translate( wxDouble dx , wxDouble dy );
225 virtual void Scale( wxDouble xScale , wxDouble yScale );
226 virtual void Rotate( wxDouble angle );
227
228 virtual void DrawBitmap( const wxBitmap &bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h );
229 virtual void DrawIcon( const wxIcon &icon, wxDouble x, wxDouble y, wxDouble w, wxDouble h );
230 virtual void PushState();
231 virtual void PopState();
232
233 virtual void SetFont( const wxFont &font );
234 virtual void SetTextColor( const wxColour &col );
235 virtual void DrawText( const wxString &str, wxDouble x, wxDouble y);
236 virtual void GetTextExtent( const wxString &str, wxDouble *width, wxDouble *height,
237 wxDouble *descent, wxDouble *externalLeading ) const;
238 virtual void GetPartialTextExtents(const wxString& text, wxArrayDouble& widths) const;
239
240 private:
241 void Init();
242 void SetDefaults();
243
244 Graphics* m_context;
245 vector<GraphicsState> m_stateStack;
246 GraphicsState m_state1;
247 GraphicsState m_state2;
248
249 Pen* m_pen;
250 bool m_penTransparent;
251 Image* m_penImage;
252 Brush* m_penBrush;
253
254 Brush* m_brush;
255 bool m_brushTransparent;
256 Image* m_brushImage;
257 GraphicsPath* m_brushPath;
258
259 Brush* m_textBrush;
260 Font* m_font;
261 // wxPen m_pen;
262 // wxBrush m_brush;
263 DECLARE_DYNAMIC_CLASS_NO_COPY(wxGDIPlusContext)
264 };
265
266 IMPLEMENT_DYNAMIC_CLASS(wxGDIPlusPath,wxGraphicsPath)
267
268 wxGDIPlusPath::wxGDIPlusPath()
269 {
270 m_path = new GraphicsPath();
271 }
272
273 wxGDIPlusPath::~wxGDIPlusPath()
274 {
275 delete m_path;
276 }
277
278 //
279 // The Primitives
280 //
281
282 void wxGDIPlusPath::MoveToPoint( wxDouble x , wxDouble y )
283 {
284 m_path->StartFigure();
285 m_path->AddLine((REAL) x,(REAL) y,(REAL) x,(REAL) y);
286 }
287
288 void wxGDIPlusPath::AddLineToPoint( wxDouble x , wxDouble y )
289 {
290 m_path->AddLine((REAL) x,(REAL) y,(REAL) x,(REAL) y);
291 }
292
293 void wxGDIPlusPath::CloseSubpath()
294 {
295 m_path->CloseFigure();
296 }
297
298 void wxGDIPlusPath::AddCurveToPoint( wxDouble cx1, wxDouble cy1, wxDouble cx2, wxDouble cy2, wxDouble x, wxDouble y )
299 {
300 PointF c1(cx1,cy1);
301 PointF c2(cx2,cy2);
302 PointF end(x,y);
303 PointF start;
304 m_path->GetLastPoint(&start);
305 m_path->AddBezier(start,c1,c2,end);
306 }
307
308 // gets the last point of the current path, (0,0) if not yet set
309 void wxGDIPlusPath::GetCurrentPoint( wxDouble& x, wxDouble&y)
310 {
311 PointF start;
312 m_path->GetLastPoint(&start);
313 x = start.X ;
314 y = start.Y ;
315 }
316
317 void wxGDIPlusPath::AddArc( wxDouble x, wxDouble y, wxDouble r, double startAngle, double endAngle, bool clockwise )
318 {
319 double sweepAngle = endAngle - startAngle ;
320 if( abs(sweepAngle) >= 2*M_PI)
321 {
322 sweepAngle = 2 * M_PI;
323 }
324 else
325 {
326 if ( clockwise )
327 {
328 if( sweepAngle < 0 )
329 sweepAngle += 2 * M_PI;
330 }
331 else
332 {
333 if( sweepAngle > 0 )
334 sweepAngle -= 2 * M_PI;
335
336 }
337 }
338 m_path->AddArc((REAL) (x-r),(REAL) (y-r),(REAL) (2*r),(REAL) (2*r),RadToDeg(startAngle),RadToDeg(sweepAngle));
339 }
340
341 void wxGDIPlusPath::AddRectangle( wxDouble x, wxDouble y, wxDouble w, wxDouble h )
342 {
343 m_path->AddRectangle(RectF(x,y,w,h));
344 }
345
346 //-----------------------------------------------------------------------------
347 // wxGDIPlusContext implementation
348 //-----------------------------------------------------------------------------
349
350 IMPLEMENT_DYNAMIC_CLASS(wxGDIPlusContext,wxGraphicsContext)
351
352 wxGDIPlusContext::wxGDIPlusContext( HDC hdc )
353 {
354 Init();
355 m_context = new Graphics( hdc);
356 SetDefaults();
357 }
358
359 wxGDIPlusContext::wxGDIPlusContext( HWND hwnd )
360 {
361 Init();
362 m_context = new Graphics( hwnd);
363 SetDefaults();
364 }
365
366 wxGDIPlusContext::wxGDIPlusContext( Graphics* gr )
367 {
368 Init();
369 m_context = gr;
370 SetDefaults();
371 }
372
373 wxGDIPlusContext::wxGDIPlusContext()
374 {
375 Init();
376 }
377
378 void wxGDIPlusContext::Init()
379 {
380 gGDILoader.EnsureIsLoaded();
381 m_context = NULL;
382 m_state1 = 0;
383 m_state2= 0;
384
385 m_pen = NULL;
386 m_penTransparent = true;
387 m_penImage = NULL;
388 m_penBrush = NULL;
389
390 m_brush = NULL;
391 m_brushTransparent = true;
392 m_brushImage = NULL;
393 m_brushPath = NULL;
394
395 m_textBrush = NULL;
396 m_font = NULL;
397 }
398
399 void wxGDIPlusContext::SetDefaults()
400 {
401 m_context->SetSmoothingMode(SmoothingModeHighQuality);
402 m_state1 = m_context->Save();
403 m_state2 = m_context->Save();
404 // set defaults
405
406 m_penTransparent = false;
407 m_pen = new Pen((ARGB)Color::Black);
408 m_penImage = NULL;
409 m_penBrush = NULL;
410
411 m_brushTransparent = false;
412 m_brush = new SolidBrush((ARGB)Color::White);
413 m_brushImage = NULL;
414 m_brushPath = NULL;
415 m_textBrush = new SolidBrush((ARGB)Color::Black);
416 m_font = new Font( L"Arial" , 9 , FontStyleRegular );
417 }
418
419 wxGDIPlusContext::~wxGDIPlusContext()
420 {
421 if ( m_context )
422 {
423 m_context->Restore( m_state2 );
424 m_context->Restore( m_state1 );
425 delete m_context;
426 delete m_pen;
427 delete m_penImage;
428 delete m_penBrush;
429 delete m_brush;
430 delete m_brushImage;
431 delete m_brushPath;
432 delete m_textBrush;
433 delete m_font;
434 }
435 }
436
437
438 void wxGDIPlusContext::Clip( const wxRegion &region )
439 {
440 m_context->SetClip((HRGN)region.GetHRGN(),CombineModeIntersect);
441 }
442
443 void wxGDIPlusContext::Clip( wxDouble x, wxDouble y, wxDouble w, wxDouble h )
444 {
445 m_context->SetClip(RectF(x,y,w,h),CombineModeIntersect);
446 }
447
448 void wxGDIPlusContext::ResetClip()
449 {
450 m_context->ResetClip();
451 }
452
453 void wxGDIPlusContext::StrokePath( const wxGraphicsPath *path )
454 {
455 if ( m_penTransparent )
456 return;
457
458 m_context->DrawPath( m_pen , (GraphicsPath*) path->GetNativePath() );
459 }
460
461 void wxGDIPlusContext::FillPath( const wxGraphicsPath *path , int fillStyle )
462 {
463 if ( !m_brushTransparent )
464 {
465 ((GraphicsPath*) path->GetNativePath())->SetFillMode( fillStyle == wxODDEVEN_RULE ? FillModeAlternate : FillModeWinding);
466 m_context->FillPath( m_brush , (GraphicsPath*) path->GetNativePath());
467 }
468 }
469
470 wxGraphicsPath* wxGDIPlusContext::CreatePath()
471 {
472 return new wxGDIPlusPath();
473 }
474
475 void wxGDIPlusContext::Rotate( wxDouble angle )
476 {
477 m_context->RotateTransform( RadToDeg(angle) );
478 }
479
480 void wxGDIPlusContext::Translate( wxDouble dx , wxDouble dy )
481 {
482 m_context->TranslateTransform( dx , dy );
483 }
484
485 void wxGDIPlusContext::Scale( wxDouble xScale , wxDouble yScale )
486 {
487 m_context->ScaleTransform(xScale,yScale);
488 }
489
490 void wxGDIPlusContext::PushState()
491 {
492 GraphicsState state = m_context->Save();
493 m_stateStack.push_back(state);
494 }
495
496 void wxGDIPlusContext::PopState()
497 {
498 GraphicsState state = m_stateStack.back();
499 m_stateStack.pop_back();
500 m_context->Restore(state);
501 }
502
503 void wxGDIPlusContext::SetTextColor( const wxColour &col )
504 {
505 delete m_textBrush;
506 m_textBrush = new SolidBrush( Color( col.Alpha() , col.Red() ,
507 col.Green() , col.Blue() ));
508 }
509
510 void wxGDIPlusContext::SetPen( const wxPen &pen )
511 {
512 m_penTransparent = pen.GetStyle() == wxTRANSPARENT;
513 if ( m_penTransparent )
514 return;
515
516 m_pen->SetColor( Color( pen.GetColour().Alpha() , pen.GetColour().Red() ,
517 pen.GetColour().Green() , pen.GetColour().Blue() ) );
518
519 // TODO: * m_dc->m_scaleX
520 double penWidth = pen.GetWidth();
521 if (penWidth <= 0.0)
522 penWidth = 0.1;
523
524 m_pen->SetWidth(penWidth);
525
526 LineCap cap;
527 switch ( pen.GetCap() )
528 {
529 case wxCAP_ROUND :
530 cap = LineCapRound;
531 break;
532
533 case wxCAP_PROJECTING :
534 cap = LineCapSquare;
535 break;
536
537 case wxCAP_BUTT :
538 cap = LineCapFlat; // TODO verify
539 break;
540
541 default :
542 cap = LineCapFlat;
543 break;
544 }
545 m_pen->SetLineCap(cap,cap, DashCapFlat);
546
547 LineJoin join;
548 switch ( pen.GetJoin() )
549 {
550 case wxJOIN_BEVEL :
551 join = LineJoinBevel;
552 break;
553
554 case wxJOIN_MITER :
555 join = LineJoinMiter;
556 break;
557
558 case wxJOIN_ROUND :
559 join = LineJoinRound;
560 break;
561
562 default :
563 join = LineJoinMiter;
564 break;
565 }
566
567 m_pen->SetLineJoin(join);
568
569 m_pen->SetDashStyle(DashStyleSolid);
570
571 DashStyle dashStyle = DashStyleSolid;
572 switch ( pen.GetStyle() )
573 {
574 case wxSOLID :
575 break;
576
577 case wxDOT :
578 dashStyle = DashStyleDot;
579 break;
580
581 case wxLONG_DASH :
582 dashStyle = DashStyleDash; // TODO verify
583 break;
584
585 case wxSHORT_DASH :
586 dashStyle = DashStyleDash;
587 break;
588
589 case wxDOT_DASH :
590 dashStyle = DashStyleDashDot;
591 break;
592 case wxUSER_DASH :
593 {
594 dashStyle = DashStyleCustom;
595 wxDash *dashes;
596 int count = pen.GetDashes( &dashes );
597 if ((dashes != NULL) && (count > 0))
598 {
599 REAL *userLengths = new REAL[count];
600 for ( int i = 0; i < count; ++i )
601 {
602 userLengths[i] = dashes[i];
603 }
604 m_pen->SetDashPattern( userLengths, count);
605 delete[] userLengths;
606 }
607 }
608 break;
609 case wxSTIPPLE :
610 {
611 wxBitmap* bmp = pen.GetStipple();
612 if ( bmp && bmp->Ok() )
613 {
614 wxDELETE( m_penImage );
615 wxDELETE( m_penBrush );
616 m_penImage = Bitmap::FromHBITMAP((HBITMAP)bmp->GetHBITMAP(),(HPALETTE)bmp->GetPalette()->GetHPALETTE());
617 m_penBrush = new TextureBrush(m_penImage);
618 m_pen->SetBrush( m_penBrush );
619 }
620
621 }
622 break;
623 default :
624 if ( pen.GetStyle() >= wxFIRST_HATCH && pen.GetStyle() <= wxLAST_HATCH )
625 {
626 wxDELETE( m_penBrush );
627 HatchStyle style = HatchStyleHorizontal;
628 switch( pen.GetStyle() )
629 {
630 case wxBDIAGONAL_HATCH :
631 style = HatchStyleBackwardDiagonal;
632 break ;
633 case wxCROSSDIAG_HATCH :
634 style = HatchStyleDiagonalCross;
635 break ;
636 case wxFDIAGONAL_HATCH :
637 style = HatchStyleForwardDiagonal;
638 break ;
639 case wxCROSS_HATCH :
640 style = HatchStyleCross;
641 break ;
642 case wxHORIZONTAL_HATCH :
643 style = HatchStyleHorizontal;
644 break ;
645 case wxVERTICAL_HATCH :
646 style = HatchStyleVertical;
647 break ;
648
649 }
650 m_penBrush = new HatchBrush(style,Color( pen.GetColour().Alpha() , pen.GetColour().Red() ,
651 pen.GetColour().Green() , pen.GetColour().Blue() ), Color::Transparent );
652 m_pen->SetBrush( m_penBrush );
653 }
654 break;
655 }
656 if ( dashStyle != DashStyleSolid )
657 m_pen->SetDashStyle(dashStyle);
658 }
659
660 void wxGDIPlusContext::SetBrush( const wxBrush &brush )
661 {
662 // m_brush = brush;
663 if ( m_context == NULL )
664 return;
665
666 m_brushTransparent = brush.GetStyle() == wxTRANSPARENT;
667
668 if ( m_brushTransparent )
669 return;
670
671 wxDELETE(m_brush);
672
673 if ( brush.GetStyle() == wxSOLID)
674 {
675 m_brush = new SolidBrush( Color( brush.GetColour().Alpha() , brush.GetColour().Red() ,
676 brush.GetColour().Green() , brush.GetColour().Blue() ) );
677 }
678 else if ( brush.IsHatch() )
679 {
680 HatchStyle style = HatchStyleHorizontal;
681 switch( brush.GetStyle() )
682 {
683 case wxBDIAGONAL_HATCH :
684 style = HatchStyleBackwardDiagonal;
685 break ;
686 case wxCROSSDIAG_HATCH :
687 style = HatchStyleDiagonalCross;
688 break ;
689 case wxFDIAGONAL_HATCH :
690 style = HatchStyleForwardDiagonal;
691 break ;
692 case wxCROSS_HATCH :
693 style = HatchStyleCross;
694 break ;
695 case wxHORIZONTAL_HATCH :
696 style = HatchStyleHorizontal;
697 break ;
698 case wxVERTICAL_HATCH :
699 style = HatchStyleVertical;
700 break ;
701
702 }
703 m_brush = new HatchBrush(style,Color( brush.GetColour().Alpha() , brush.GetColour().Red() ,
704 brush.GetColour().Green() , brush.GetColour().Blue() ), Color::Transparent );
705 }
706 else
707 {
708 wxBitmap* bmp = brush.GetStipple();
709 if ( bmp && bmp->Ok() )
710 {
711 wxDELETE( m_brushImage );
712 m_brushImage = Bitmap::FromHBITMAP((HBITMAP)bmp->GetHBITMAP(),(HPALETTE)bmp->GetPalette()->GetHPALETTE());
713 m_brush = new TextureBrush(m_brushImage);
714 }
715 }
716 }
717
718 void wxGDIPlusContext::SetLinearGradientBrush( wxDouble x1, wxDouble y1, wxDouble x2, wxDouble y2, const wxColour&c1, const wxColour&c2)
719 {
720 m_brushTransparent = false ;
721
722 wxDELETE(m_brush);
723
724 m_brush = new LinearGradientBrush( PointF( x1,y1) , PointF( x2,y2),
725 Color( c1.Alpha(), c1.Red(),c1.Green() , c1.Blue() ),
726 Color( c2.Alpha(), c2.Red(),c2.Green() , c2.Blue() ));
727 }
728
729 void wxGDIPlusContext::SetRadialGradientBrush( wxDouble xo, wxDouble yo, wxDouble xc, wxDouble yc, wxDouble radius,
730 const wxColour &oColor, const wxColour &cColor)
731 {
732 m_brushTransparent = false ;
733
734 wxDELETE(m_brush);
735 wxDELETE(m_brushPath);
736
737 // Create a path that consists of a single circle.
738 m_brushPath = new GraphicsPath();
739 m_brushPath->AddEllipse( (REAL)(xc-radius), (REAL)(yc-radius), (REAL)(2*radius), (REAL)(2*radius));
740
741 PathGradientBrush *b = new PathGradientBrush(m_brushPath);
742 m_brush = b;
743 b->SetCenterPoint( PointF(xo,yo));
744 b->SetCenterColor(Color( oColor.Alpha(), oColor.Red(),oColor.Green() , oColor.Blue() ));
745
746 Color colors[] = {Color( cColor.Alpha(), cColor.Red(),cColor.Green() , cColor.Blue() )};
747 int count = 1;
748 b->SetSurroundColors(colors, &count);
749 }
750
751 // the built-in conversions functions create non-premultiplied bitmaps, while GDIPlus needs them in the
752 // premultiplied format, therefore in the failing cases we create a new bitmap using the non-premultiplied
753 // bytes as parameter
754
755 void wxGDIPlusContext::DrawBitmap( const wxBitmap &bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h )
756 {
757 Bitmap* image = NULL;
758 Bitmap* helper = NULL;
759 if ( bmp.GetMask() )
760 {
761 Bitmap interim((HBITMAP)bmp.GetHBITMAP(),(HPALETTE)bmp.GetPalette()->GetHPALETTE()) ;
762
763 size_t width = interim.GetWidth();
764 size_t height = interim.GetHeight();
765 Rect bounds(0,0,width,height);
766
767 image = new Bitmap(width,height,PixelFormat32bppPARGB) ;
768
769 Bitmap interimMask((HBITMAP)bmp.GetMask()->GetMaskBitmap(),NULL);
770 wxASSERT(interimMask.GetPixelFormat() == PixelFormat1bppIndexed);
771
772 BitmapData dataMask ;
773 interimMask.LockBits(&bounds,ImageLockModeRead,
774 interimMask.GetPixelFormat(),&dataMask);
775
776
777 BitmapData imageData ;
778 image->LockBits(&bounds,ImageLockModeWrite, PixelFormat32bppPARGB, &imageData);
779
780 BYTE maskPattern = 0 ;
781 BYTE maskByte = 0;
782 size_t maskIndex ;
783
784 for ( size_t y = 0 ; y < height ; ++y)
785 {
786 maskIndex = 0 ;
787 for( size_t x = 0 ; x < width; ++x)
788 {
789 if ( x % 8 == 0)
790 {
791 maskPattern = 0x80;
792 maskByte = *((BYTE*)dataMask.Scan0 + dataMask.Stride*y + maskIndex);
793 maskIndex++;
794 }
795 else
796 maskPattern = maskPattern >> 1;
797
798 ARGB *dest = (ARGB*)((BYTE*)imageData.Scan0 + imageData.Stride*y + x*4);
799 if ( (maskByte & maskPattern) == 0 )
800 *dest = 0x00000000;
801 else
802 {
803 Color c ;
804 interim.GetPixel(x,y,&c) ;
805 *dest = (c.GetValue() | Color::AlphaMask);
806 }
807 }
808 }
809
810 image->UnlockBits(&imageData);
811
812 interimMask.UnlockBits(&dataMask);
813 interim.UnlockBits(&dataMask);
814 }
815 else
816 {
817 image = Bitmap::FromHBITMAP((HBITMAP)bmp.GetHBITMAP(),(HPALETTE)bmp.GetPalette()->GetHPALETTE());
818 if ( GetPixelFormatSize(image->GetPixelFormat()) == 32 )
819 {
820 size_t width = image->GetWidth();
821 size_t height = image->GetHeight();
822 Rect bounds(0,0,width,height);
823 BitmapData data ;
824
825 helper = image ;
826 image = NULL ;
827 helper->LockBits(&bounds, ImageLockModeRead,
828 helper->GetPixelFormat(),&data);
829
830 image = new Bitmap(data.Width, data.Height, data.Stride,
831 PixelFormat32bppARGB , (BYTE*) data.Scan0);
832
833 helper->UnlockBits(&data);
834 }
835 }
836 if ( image )
837 m_context->DrawImage(image,(REAL) x,(REAL) y,(REAL) w,(REAL) h) ;
838 delete image ;
839 delete helper ;
840 }
841
842 void wxGDIPlusContext::DrawIcon( const wxIcon &icon, wxDouble x, wxDouble y, wxDouble w, wxDouble h )
843 {
844 HICON hIcon = (HICON)icon.GetHICON();
845 ICONINFO iconInfo ;
846 // IconInfo creates the bitmaps for color and mask, we must dispose of them after use
847 if (!GetIconInfo(hIcon,&iconInfo))
848 return;
849
850 BITMAP iconBmpData ;
851 GetObject(iconInfo.hbmColor,sizeof(BITMAP),&iconBmpData);
852 Bitmap interim(iconInfo.hbmColor,NULL);
853
854 Bitmap* image = NULL ;
855
856 if( GetPixelFormatSize(interim.GetPixelFormat())!= 32 )
857 {
858 image = Bitmap::FromHICON(hIcon);
859 }
860 else
861 {
862 size_t width = interim.GetWidth();
863 size_t height = interim.GetHeight();
864 Rect bounds(0,0,width,height);
865 BitmapData data ;
866
867 interim.LockBits(&bounds, ImageLockModeRead,
868 interim.GetPixelFormat(),&data);
869 image = new Bitmap(data.Width, data.Height, data.Stride,
870 PixelFormat32bppARGB , (BYTE*) data.Scan0);
871 interim.UnlockBits(&data);
872 }
873
874 m_context->DrawImage(image,(REAL) x,(REAL) y,(REAL) w,(REAL) h) ;
875
876 delete image ;
877 DeleteObject(iconInfo.hbmColor);
878 DeleteObject(iconInfo.hbmMask);
879 }
880
881
882 void wxGDIPlusContext::DrawText( const wxString &str, wxDouble x, wxDouble y )
883 {
884 if ( str.IsEmpty())
885 return ;
886
887 wxWCharBuffer s = str.wc_str( *wxConvUI );
888 m_context->DrawString( s , -1 , m_font , PointF( x , y ) , m_textBrush );
889 // TODO m_backgroundMode == wxSOLID
890 }
891
892 void wxGDIPlusContext::GetTextExtent( const wxString &str, wxDouble *width, wxDouble *height,
893 wxDouble *descent, wxDouble *externalLeading ) const
894 {
895 wxWCharBuffer s = str.wc_str( *wxConvUI );
896 FontFamily ffamily ;
897
898 m_font->GetFamily(&ffamily) ;
899
900 REAL factorY = m_context->GetDpiY() / 72.0 ;
901
902 REAL rDescent = ffamily.GetCellDescent(FontStyleRegular) *
903 m_font->GetSize() / ffamily.GetEmHeight(FontStyleRegular);
904 REAL rAscent = ffamily.GetCellAscent(FontStyleRegular) *
905 m_font->GetSize() / ffamily.GetEmHeight(FontStyleRegular);
906 REAL rHeight = ffamily.GetLineSpacing(FontStyleRegular) *
907 m_font->GetSize() / ffamily.GetEmHeight(FontStyleRegular);
908
909 if ( height )
910 *height = rHeight * factorY + 0.5 ;
911 if ( descent )
912 *descent = rDescent * factorY + 0.5 ;
913 if ( externalLeading )
914 *externalLeading = (rHeight - rAscent - rDescent) * factorY + 0.5 ;
915 // measuring empty strings is not guaranteed, so do it by hand
916 if ( str.IsEmpty())
917 {
918 if ( width )
919 *width = 0 ;
920 }
921 else
922 {
923 // MeasureString does return a rectangle that is way too large, so it is
924 // not usable here
925 RectF layoutRect(0,0, 100000.0f, 100000.0f);
926 StringFormat strFormat;
927 CharacterRange strRange(0,wcslen(s));
928 strFormat.SetMeasurableCharacterRanges(1,&strRange);
929 Region region ;
930 m_context->MeasureCharacterRanges(s, -1 , m_font,layoutRect, &strFormat,1,&region) ;
931 RectF bbox ;
932 region.GetBounds(&bbox,m_context);
933 if ( width )
934 *width = bbox.GetRight()-bbox.GetLeft()+0.5;
935 }
936 }
937
938 void wxGDIPlusContext::GetPartialTextExtents(const wxString& text, wxArrayDouble& widths) const
939 {
940 widths.Empty();
941 widths.Add(0, text.length());
942
943 if (text.empty())
944 return;
945
946 wxWCharBuffer ws = text.wc_str( *wxConvUI );
947 size_t len = wcslen( ws ) ;
948 wxASSERT_MSG(text.length() == len , wxT("GetPartialTextExtents not yet implemented for multichar situations"));
949
950 RectF layoutRect(0,0, 100000.0f, 100000.0f);
951 StringFormat strFormat;
952
953 CharacterRange* ranges = new CharacterRange[len] ;
954 Region* regions = new Region[len];
955 for( size_t i = 0 ; i < len ; ++i)
956 {
957 ranges[i].First = i ;
958 ranges[i].Length = 1 ;
959 }
960 strFormat.SetMeasurableCharacterRanges(len,ranges);
961 m_context->MeasureCharacterRanges(ws, -1 , m_font,layoutRect, &strFormat,1,regions) ;
962
963 RectF bbox ;
964 for ( size_t i = 0 ; i < len ; ++i)
965 {
966 regions[i].GetBounds(&bbox,m_context);
967 widths[i] = bbox.GetRight()-bbox.GetLeft();
968 }
969 }
970
971 void wxGDIPlusContext::SetFont( const wxFont &font )
972 {
973 wxASSERT( font.Ok());
974 delete m_font;
975 wxWCharBuffer s = font.GetFaceName().wc_str( *wxConvUI );
976 int size = font.GetPointSize();
977 int style = FontStyleRegular;
978 if ( font.GetStyle() == wxFONTSTYLE_ITALIC )
979 style |= FontStyleItalic;
980 if ( font.GetUnderlined() )
981 style |= FontStyleUnderline;
982 if ( font.GetWeight() == wxFONTWEIGHT_BOLD )
983 style |= FontStyleBold;
984 m_font = new Font( s , size , style );
985 }
986
987 void* wxGDIPlusContext::GetNativeContext()
988 {
989 return m_context;
990 }
991
992 wxGraphicsContext* wxGraphicsContext::Create( const wxWindowDC& dc)
993 {
994 return new wxGDIPlusContext( (HDC) dc.GetHDC() );
995 }
996
997 wxGraphicsContext* wxGraphicsContext::Create( wxWindow * window )
998 {
999 return new wxGDIPlusContext( (HWND) window->GetHWND() );
1000 }
1001
1002 wxGraphicsContext* wxGraphicsContext::CreateFromNative( void * context )
1003 {
1004 return new wxGDIPlusContext( (Graphics*) context );
1005 }
1006
1007
1008
1009 #endif // wxUSE_GRAPHICS_CONTEXT