1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_vcl.hxx"
26 
27 #include <stdio.h>
28 #include <string.h>
29 
30 #include <tools/svwin.h>
31 #include <tools/debug.hxx>
32 
33 #include <win/wincomp.hxx>
34 #include <win/saldata.hxx>
35 #include <win/salgdi.h>
36 
37 #ifndef min
38 #define min(a,b)	(((a) < (b)) ? (a) : (b))
39 #endif
40 #ifndef max
41 #define max(a,b)	(((a) > (b)) ? (a) : (b))
42 #endif
43 
44 #if defined _MSC_VER
45 #pragma warning(push, 1)
46 #endif
47 
48 #include <GdiPlus.h>
49 #include <GdiPlusEnums.h>
50 #include <GdiPlusColor.h>
51 
52 #if defined _MSC_VER
53 #pragma warning(pop)
54 #endif
55 
56 #include <basegfx/polygon/b2dpolygon.hxx>
57 
58 // -----------------------------------------------------------------------
59 
60 void impAddB2DPolygonToGDIPlusGraphicsPathReal(Gdiplus::GraphicsPath& rPath, const basegfx::B2DPolygon& rPolygon, bool bNoLineJoin)
61 {
62     sal_uInt32 nCount(rPolygon.count());
63 
64     if(nCount)
65     {
66         const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nCount : nCount - 1);
67         const bool bControls(rPolygon.areControlPointsUsed());
68         basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
69         Gdiplus::PointF aFCurr(Gdiplus::REAL(aCurr.getX()), Gdiplus::REAL(aCurr.getY()));
70 
71         for(sal_uInt32 a(0); a < nEdgeCount; a++)
72         {
73 	        const sal_uInt32 nNextIndex((a + 1) % nCount);
74 	        const basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
75 	        const Gdiplus::PointF aFNext(Gdiplus::REAL(aNext.getX()), Gdiplus::REAL(aNext.getY()));
76 
77 	        if(bControls && (rPolygon.isNextControlPointUsed(a) || rPolygon.isPrevControlPointUsed(nNextIndex)))
78 	        {
79 		        const basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
80 		        const basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
81 
82 		        rPath.AddBezier(
83 			        aFCurr,
84 			        Gdiplus::PointF(Gdiplus::REAL(aCa.getX()), Gdiplus::REAL(aCa.getY())),
85 			        Gdiplus::PointF(Gdiplus::REAL(aCb.getX()), Gdiplus::REAL(aCb.getY())),
86 			        aFNext);
87 	        }
88 	        else
89 	        {
90 		        rPath.AddLine(aFCurr, aFNext);
91 	        }
92 
93 	        if(a + 1 < nEdgeCount)
94 	        {
95 		        aFCurr = aFNext;
96 
97 			    if(bNoLineJoin)
98 			    {
99 				    rPath.StartFigure();
100 			    }
101 	        }
102         }
103     }
104 }
105 
106 void impAddB2DPolygonToGDIPlusGraphicsPathInteger(Gdiplus::GraphicsPath& rPath, const basegfx::B2DPolygon& rPolygon, bool bNoLineJoin)
107 {
108     sal_uInt32 nCount(rPolygon.count());
109 
110     if(nCount)
111     {
112         const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nCount : nCount - 1);
113         const bool bControls(rPolygon.areControlPointsUsed());
114         basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
115         Gdiplus::Point aICurr(INT(aCurr.getX()), INT(aCurr.getY()));
116 
117         for(sal_uInt32 a(0); a < nEdgeCount; a++)
118         {
119 	        const sal_uInt32 nNextIndex((a + 1) % nCount);
120 	        const basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
121 	        const Gdiplus::Point aINext(INT(aNext.getX()), INT(aNext.getY()));
122 
123 	        if(bControls && (rPolygon.isNextControlPointUsed(a) || rPolygon.isPrevControlPointUsed(nNextIndex)))
124 	        {
125 		        const basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
126 		        const basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
127 
128 		        rPath.AddBezier(
129 			        aICurr,
130 			        Gdiplus::Point(INT(aCa.getX()), INT(aCa.getY())),
131 			        Gdiplus::Point(INT(aCb.getX()), INT(aCb.getY())),
132 			        aINext);
133 	        }
134 	        else
135 	        {
136 		        rPath.AddLine(aICurr, aINext);
137 	        }
138 
139 	        if(a + 1 < nEdgeCount)
140 	        {
141 		        aICurr = aINext;
142 
143 			    if(bNoLineJoin)
144 			    {
145 				    rPath.StartFigure();
146 			    }
147 	        }
148         }
149     }
150 }
151 
152 bool WinSalGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPolygon, double fTransparency)
153 {
154 	const sal_uInt32 nCount(rPolyPolygon.count());
155 
156 	if(mbBrush && nCount && (fTransparency >= 0.0 && fTransparency < 1.0))
157 	{
158 		Gdiplus::Graphics aGraphics(mhDC);
159 		const sal_uInt8 aTrans((sal_uInt8)255 - (sal_uInt8)basegfx::fround(fTransparency * 255.0));
160 		Gdiplus::Color aTestColor(aTrans, SALCOLOR_RED(maFillColor), SALCOLOR_GREEN(maFillColor), SALCOLOR_BLUE(maFillColor));
161 		Gdiplus::SolidBrush aTestBrush(aTestColor);
162 		Gdiplus::GraphicsPath aPath;
163 
164 		for(sal_uInt32 a(0); a < nCount; a++)
165 		{
166             if(0 != a)
167             {
168                 aPath.StartFigure(); // #i101491# not needed for first run
169             }
170 
171 			impAddB2DPolygonToGDIPlusGraphicsPathReal(aPath, rPolyPolygon.getB2DPolygon(a), false);
172             aPath.CloseFigure();
173 		}
174 
175         if(getAntiAliasB2DDraw())
176         {
177             aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
178         }
179         else
180         {
181     		aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
182         }
183 
184 		aGraphics.FillPath(&aTestBrush, &aPath);
185 	}
186 
187  	return true;
188 }
189 
190 bool WinSalGraphics::drawPolyLine(
191     const basegfx::B2DPolygon& rPolygon,
192     double fTransparency,
193     const basegfx::B2DVector& rLineWidths,
194     basegfx::B2DLineJoin eLineJoin,
195     com::sun::star::drawing::LineCap eLineCap)
196 {
197     const sal_uInt32 nCount(rPolygon.count());
198 
199 	if(mbPen && nCount)
200 	{
201 		Gdiplus::Graphics aGraphics(mhDC);
202 		const sal_uInt8 aTrans = (sal_uInt8)basegfx::fround( 255 * (1.0 - fTransparency) );
203 		Gdiplus::Color aTestColor(aTrans, SALCOLOR_RED(maLineColor), SALCOLOR_GREEN(maLineColor), SALCOLOR_BLUE(maLineColor));
204 		Gdiplus::Pen aTestPen(aTestColor, Gdiplus::REAL(rLineWidths.getX()));
205 		Gdiplus::GraphicsPath aPath;
206 		bool bNoLineJoin(false);
207 
208 		switch(eLineJoin)
209 		{
210 			default : // basegfx::B2DLINEJOIN_NONE :
211 			{
212 				if(basegfx::fTools::more(rLineWidths.getX(), 0.0))
213 				{
214 					bNoLineJoin = true;
215 				}
216 				break;
217 			}
218 			case basegfx::B2DLINEJOIN_BEVEL :
219 			{
220 				aTestPen.SetLineJoin(Gdiplus::LineJoinBevel);
221 				break;
222 			}
223 			case basegfx::B2DLINEJOIN_MIDDLE :
224 			case basegfx::B2DLINEJOIN_MITER :
225 			{
226 				const Gdiplus::REAL aMiterLimit(15.0);
227 				aTestPen.SetMiterLimit(aMiterLimit);
228 				aTestPen.SetLineJoin(Gdiplus::LineJoinMiter);
229 				break;
230 			}
231 			case basegfx::B2DLINEJOIN_ROUND :
232 			{
233 				aTestPen.SetLineJoin(Gdiplus::LineJoinRound);
234 				break;
235 			}
236 		}
237 
238         switch(eLineCap)
239         {
240             default: /*com::sun::star::drawing::LineCap_BUTT*/
241             {
242                 // nothing to do
243                 break;
244             }
245             case com::sun::star::drawing::LineCap_ROUND:
246             {
247                 aTestPen.SetStartCap(Gdiplus::LineCapRound);
248                 aTestPen.SetEndCap(Gdiplus::LineCapRound);
249                 break;
250             }
251             case com::sun::star::drawing::LineCap_SQUARE:
252             {
253                 aTestPen.SetStartCap(Gdiplus::LineCapSquare);
254                 aTestPen.SetEndCap(Gdiplus::LineCapSquare);
255                 break;
256             }
257         }
258 
259 		if(nCount > 250 && basegfx::fTools::more(rLineWidths.getX(), 1.5))
260         {
261     		impAddB2DPolygonToGDIPlusGraphicsPathInteger(aPath, rPolygon, bNoLineJoin);
262         }
263         else
264         {
265     		impAddB2DPolygonToGDIPlusGraphicsPathReal(aPath, rPolygon, bNoLineJoin);
266         }
267 
268         if(rPolygon.isClosed() && !bNoLineJoin)
269         {
270             // #i101491# needed to create the correct line joins
271             aPath.CloseFigure();
272         }
273 
274         if(getAntiAliasB2DDraw())
275         {
276     		aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
277         }
278         else
279         {
280     		aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
281         }
282 
283 		aGraphics.DrawPath(&aTestPen, &aPath);
284 	}
285 
286 	return true;
287 }
288 
289 // -----------------------------------------------------------------------
290