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 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_vcl.hxx"
24 
25 #include <stdio.h>
26 #include <string.h>
27 #include <tools/svwin.h>
28 #include <tools/debug.hxx>
29 #include <win/wincomp.hxx>
30 #include <win/saldata.hxx>
31 #include <win/salgdi.h>
32 #include <win/salbmp.h>
33 
34 #ifndef min
35 #define min(a,b)	(((a) < (b)) ? (a) : (b))
36 #endif
37 #ifndef max
38 #define max(a,b)	(((a) > (b)) ? (a) : (b))
39 #endif
40 
41 #if defined _MSC_VER
42 #pragma warning(push, 1)
43 #endif
44 
45 #include <GdiPlus.h>
46 #include <GdiPlusEnums.h>
47 #include <GdiPlusColor.h>
48 
49 #if defined _MSC_VER
50 #pragma warning(pop)
51 #endif
52 
53 #include <basegfx/polygon/b2dpolygon.hxx>
54 
55 // -----------------------------------------------------------------------
56 
57 void impAddB2DPolygonToGDIPlusGraphicsPathReal(Gdiplus::GraphicsPath& rPath, const basegfx::B2DPolygon& rPolygon, bool bNoLineJoin)
58 {
59     sal_uInt32 nCount(rPolygon.count());
60 
61     if(nCount)
62     {
63         const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nCount : nCount - 1);
64         const bool bControls(rPolygon.areControlPointsUsed());
65         basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
66         Gdiplus::PointF aFCurr(Gdiplus::REAL(aCurr.getX()), Gdiplus::REAL(aCurr.getY()));
67 
68         for(sal_uInt32 a(0); a < nEdgeCount; a++)
69         {
70 	        const sal_uInt32 nNextIndex((a + 1) % nCount);
71 	        const basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
72 	        const Gdiplus::PointF aFNext(Gdiplus::REAL(aNext.getX()), Gdiplus::REAL(aNext.getY()));
73 
74 	        if(bControls && (rPolygon.isNextControlPointUsed(a) || rPolygon.isPrevControlPointUsed(nNextIndex)))
75 	        {
76 		        const basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
77 		        const basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
78 
79 		        rPath.AddBezier(
80 			        aFCurr,
81 			        Gdiplus::PointF(Gdiplus::REAL(aCa.getX()), Gdiplus::REAL(aCa.getY())),
82 			        Gdiplus::PointF(Gdiplus::REAL(aCb.getX()), Gdiplus::REAL(aCb.getY())),
83 			        aFNext);
84 	        }
85 	        else
86 	        {
87 		        rPath.AddLine(aFCurr, aFNext);
88 	        }
89 
90 	        if(a + 1 < nEdgeCount)
91 	        {
92 		        aFCurr = aFNext;
93 
94 			    if(bNoLineJoin)
95 			    {
96 				    rPath.StartFigure();
97 			    }
98 	        }
99         }
100     }
101 }
102 
103 void impAddB2DPolygonToGDIPlusGraphicsPathInteger(Gdiplus::GraphicsPath& rPath, const basegfx::B2DPolygon& rPolygon, bool bNoLineJoin)
104 {
105     sal_uInt32 nCount(rPolygon.count());
106 
107     if(nCount)
108     {
109         const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nCount : nCount - 1);
110         const bool bControls(rPolygon.areControlPointsUsed());
111         basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
112         Gdiplus::Point aICurr(INT(aCurr.getX()), INT(aCurr.getY()));
113 
114         for(sal_uInt32 a(0); a < nEdgeCount; a++)
115         {
116 	        const sal_uInt32 nNextIndex((a + 1) % nCount);
117 	        const basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
118 	        const Gdiplus::Point aINext(INT(aNext.getX()), INT(aNext.getY()));
119 
120 	        if(bControls && (rPolygon.isNextControlPointUsed(a) || rPolygon.isPrevControlPointUsed(nNextIndex)))
121 	        {
122 		        const basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
123 		        const basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
124 
125 		        rPath.AddBezier(
126 			        aICurr,
127 			        Gdiplus::Point(INT(aCa.getX()), INT(aCa.getY())),
128 			        Gdiplus::Point(INT(aCb.getX()), INT(aCb.getY())),
129 			        aINext);
130 	        }
131 	        else
132 	        {
133 		        rPath.AddLine(aICurr, aINext);
134 	        }
135 
136 	        if(a + 1 < nEdgeCount)
137 	        {
138 		        aICurr = aINext;
139 
140 			    if(bNoLineJoin)
141 			    {
142 				    rPath.StartFigure();
143 			    }
144 	        }
145         }
146     }
147 }
148 
149 bool WinSalGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPolygon, double fTransparency)
150 {
151 	const sal_uInt32 nCount(rPolyPolygon.count());
152 
153 	if(mbBrush && nCount && (fTransparency >= 0.0 && fTransparency < 1.0))
154 	{
155 		Gdiplus::Graphics aGraphics(getHDC());
156 		const sal_uInt8 aTrans((sal_uInt8)255 - (sal_uInt8)basegfx::fround(fTransparency * 255.0));
157 		Gdiplus::Color aTestColor(aTrans, SALCOLOR_RED(maFillColor), SALCOLOR_GREEN(maFillColor), SALCOLOR_BLUE(maFillColor));
158 		Gdiplus::SolidBrush aTestBrush(aTestColor);
159 		Gdiplus::GraphicsPath aPath;
160 
161 		for(sal_uInt32 a(0); a < nCount; a++)
162 		{
163             if(0 != a)
164             {
165                 aPath.StartFigure(); // #i101491# not needed for first run
166             }
167 
168 			impAddB2DPolygonToGDIPlusGraphicsPathReal(aPath, rPolyPolygon.getB2DPolygon(a), false);
169             aPath.CloseFigure();
170 		}
171 
172         if(getAntiAliasB2DDraw())
173         {
174             aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
175         }
176         else
177         {
178     		aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
179         }
180 
181 		aGraphics.FillPath(&aTestBrush, &aPath);
182 	}
183 
184  	return true;
185 }
186 
187 bool WinSalGraphics::drawPolyLine(
188     const basegfx::B2DPolygon& rPolygon,
189     double fTransparency,
190     const basegfx::B2DVector& rLineWidths,
191     basegfx::B2DLineJoin eLineJoin,
192     com::sun::star::drawing::LineCap eLineCap)
193 {
194     const sal_uInt32 nCount(rPolygon.count());
195 
196 	if(mbPen && nCount)
197 	{
198 		Gdiplus::Graphics aGraphics(getHDC());
199 		const sal_uInt8 aTrans = (sal_uInt8)basegfx::fround( 255 * (1.0 - fTransparency) );
200 		Gdiplus::Color aTestColor(aTrans, SALCOLOR_RED(maLineColor), SALCOLOR_GREEN(maLineColor), SALCOLOR_BLUE(maLineColor));
201 		Gdiplus::Pen aTestPen(aTestColor, Gdiplus::REAL(rLineWidths.getX()));
202 		Gdiplus::GraphicsPath aPath;
203 		bool bNoLineJoin(false);
204 
205 		switch(eLineJoin)
206 		{
207 			default : // basegfx::B2DLINEJOIN_NONE :
208 			{
209 				if(basegfx::fTools::more(rLineWidths.getX(), 0.0))
210 				{
211 					bNoLineJoin = true;
212 				}
213 				break;
214 			}
215 			case basegfx::B2DLINEJOIN_BEVEL :
216 			{
217 				aTestPen.SetLineJoin(Gdiplus::LineJoinBevel);
218 				break;
219 			}
220 			case basegfx::B2DLINEJOIN_MIDDLE :
221 			case basegfx::B2DLINEJOIN_MITER :
222 			{
223 				const Gdiplus::REAL aMiterLimit(15.0);
224 				aTestPen.SetMiterLimit(aMiterLimit);
225 				aTestPen.SetLineJoin(Gdiplus::LineJoinMiter);
226 				break;
227 			}
228 			case basegfx::B2DLINEJOIN_ROUND :
229 			{
230 				aTestPen.SetLineJoin(Gdiplus::LineJoinRound);
231 				break;
232 			}
233 		}
234 
235         switch(eLineCap)
236         {
237             default: /*com::sun::star::drawing::LineCap_BUTT*/
238             {
239                 // nothing to do
240                 break;
241             }
242             case com::sun::star::drawing::LineCap_ROUND:
243             {
244                 aTestPen.SetStartCap(Gdiplus::LineCapRound);
245                 aTestPen.SetEndCap(Gdiplus::LineCapRound);
246                 break;
247             }
248             case com::sun::star::drawing::LineCap_SQUARE:
249             {
250                 aTestPen.SetStartCap(Gdiplus::LineCapSquare);
251                 aTestPen.SetEndCap(Gdiplus::LineCapSquare);
252                 break;
253             }
254         }
255 
256 		if(nCount > 250 && basegfx::fTools::more(rLineWidths.getX(), 1.5))
257         {
258     		impAddB2DPolygonToGDIPlusGraphicsPathInteger(aPath, rPolygon, bNoLineJoin);
259         }
260         else
261         {
262     		impAddB2DPolygonToGDIPlusGraphicsPathReal(aPath, rPolygon, bNoLineJoin);
263         }
264 
265         if(rPolygon.isClosed() && !bNoLineJoin)
266         {
267             // #i101491# needed to create the correct line joins
268             aPath.CloseFigure();
269         }
270 
271         if(getAntiAliasB2DDraw())
272         {
273     		aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
274         }
275         else
276         {
277     		aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
278         }
279 
280 		aGraphics.DrawPath(&aTestPen, &aPath);
281 	}
282 
283 	return true;
284 }
285 
286 // -----------------------------------------------------------------------
287 
288 void paintToGdiPlus(
289     Gdiplus::Graphics& rGraphics,
290     const SalTwoRect& rTR,
291     Gdiplus::Bitmap& rBitmap)
292 {
293     // only parts of source are used
294     Gdiplus::PointF aDestPoints[3];
295     Gdiplus::ImageAttributes aAttributes;
296 
297     // define target region as paralellogram
298     aDestPoints[0].X = Gdiplus::REAL(rTR.mnDestX);
299     aDestPoints[0].Y = Gdiplus::REAL(rTR.mnDestY);
300     aDestPoints[1].X = Gdiplus::REAL(rTR.mnDestX + rTR.mnDestWidth);
301     aDestPoints[1].Y = Gdiplus::REAL(rTR.mnDestY);
302     aDestPoints[2].X = Gdiplus::REAL(rTR.mnDestX);
303     aDestPoints[2].Y = Gdiplus::REAL(rTR.mnDestY + rTR.mnDestHeight);
304 
305     aAttributes.SetWrapMode(Gdiplus::WrapModeTileFlipXY);
306 
307     rGraphics.DrawImage(
308         &rBitmap,
309         aDestPoints,
310         3,
311         Gdiplus::REAL(rTR.mnSrcX),
312         Gdiplus::REAL(rTR.mnSrcY),
313         Gdiplus::REAL(rTR.mnSrcWidth),
314         Gdiplus::REAL(rTR.mnSrcHeight),
315         Gdiplus::UnitPixel,
316         &aAttributes,
317         0,
318         0);
319 }
320 
321 // -----------------------------------------------------------------------
322 
323 void setInterpolationMode(
324     Gdiplus::Graphics& rGraphics,
325     const long& rSrcWidth,
326     const long& rDestWidth,
327     const long& rSrcHeight,
328     const long& rDestHeight)
329 {
330     const bool bSameWidth(rSrcWidth == rDestWidth);
331     const bool bSameHeight(rSrcHeight == rDestHeight);
332 
333     if(bSameWidth && bSameHeight)
334     {
335         rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeInvalid);
336     }
337     else if(rDestWidth > rSrcWidth && rDestHeight > rSrcHeight)
338     {
339         rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeDefault);
340     }
341     else if(rDestWidth < rSrcWidth && rDestHeight < rSrcHeight)
342     {
343         rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeBicubic);
344     }
345     else
346     {
347         rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeDefault);
348     }
349 }
350 
351 
352 bool WinSalGraphics::tryDrawBitmapGdiPlus(const SalTwoRect& rTR, const SalBitmap& rSrcBitmap)
353 {
354     if(rTR.mnSrcWidth && rTR.mnSrcHeight && rTR.mnDestWidth && rTR.mnDestHeight)
355     {
356         const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSrcBitmap);
357         GdiPlusBmpPtr aARGB(rSalBitmap.ImplGetGdiPlusBitmap());
358 
359         if(aARGB.get())
360         {
361             Gdiplus::Graphics aGraphics(getHDC());
362 
363             setInterpolationMode(
364                 aGraphics,
365                 rTR.mnSrcWidth,
366                 rTR.mnDestWidth,
367                 rTR.mnSrcHeight,
368                 rTR.mnDestHeight);
369 
370             paintToGdiPlus(
371                 aGraphics,
372                 rTR,
373                 *aARGB.get());
374 
375             return true;
376         }
377     }
378 
379     return false;
380 }
381 
382 bool WinSalGraphics::drawAlphaBitmap(
383     const SalTwoRect& rTR,
384     const SalBitmap& rSrcBitmap,
385     const SalBitmap& rAlphaBmp)
386 {
387     if(rTR.mnSrcWidth && rTR.mnSrcHeight && rTR.mnDestWidth && rTR.mnDestHeight)
388     {
389         const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSrcBitmap);
390         const WinSalBitmap& rSalAlpha = static_cast< const WinSalBitmap& >(rAlphaBmp);
391         GdiPlusBmpPtr aARGB(rSalBitmap.ImplGetGdiPlusBitmap(&rSalAlpha));
392 
393         if(aARGB.get())
394         {
395             Gdiplus::Graphics aGraphics(getHDC());
396 
397             setInterpolationMode(
398                 aGraphics,
399                 rTR.mnSrcWidth,
400                 rTR.mnDestWidth,
401                 rTR.mnSrcHeight,
402                 rTR.mnDestHeight);
403 
404             paintToGdiPlus(
405                 aGraphics,
406                 rTR,
407                 *aARGB.get());
408 
409             return true;
410         }
411     }
412 
413     return false;
414 }
415 
416 // -----------------------------------------------------------------------
417 
418 bool WinSalGraphics::drawTransformedBitmap(
419     const basegfx::B2DPoint& rNull,
420     const basegfx::B2DPoint& rX,
421     const basegfx::B2DPoint& rY,
422     const SalBitmap& rSourceBitmap,
423     const SalBitmap* pAlphaBitmap)
424 {
425     const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSourceBitmap);
426     const WinSalBitmap* pSalAlpha = static_cast< const WinSalBitmap* >(pAlphaBitmap);
427     GdiPlusBmpPtr aARGB(rSalBitmap.ImplGetGdiPlusBitmap(pSalAlpha));
428 
429     if(aARGB.get())
430     {
431         const long nSrcWidth(aARGB->GetWidth());
432         const long nSrcHeight(aARGB->GetHeight());
433 
434         if(nSrcWidth && nSrcHeight)
435         {
436             const long nDestWidth(basegfx::fround(basegfx::B2DVector(rX - rNull).getLength()));
437             const long nDestHeight(basegfx::fround(basegfx::B2DVector(rY - rNull).getLength()));
438 
439             if(nDestWidth && nDestHeight)
440             {
441                 Gdiplus::Graphics aGraphics(getHDC());
442                 Gdiplus::PointF aDestPoints[3];
443                 Gdiplus::ImageAttributes aAttributes;
444 
445                 setInterpolationMode(
446                     aGraphics,
447                     nSrcWidth,
448                     nDestWidth,
449                     nSrcHeight,
450                     nDestHeight);
451 
452                 // this mode is only capable of drawing the whole bitmap to a paralellogram
453                 aDestPoints[0].X = Gdiplus::REAL(rNull.getX());
454                 aDestPoints[0].Y = Gdiplus::REAL(rNull.getY());
455                 aDestPoints[1].X = Gdiplus::REAL(rX.getX());
456                 aDestPoints[1].Y = Gdiplus::REAL(rX.getY());
457                 aDestPoints[2].X = Gdiplus::REAL(rY.getX());
458                 aDestPoints[2].Y = Gdiplus::REAL(rY.getY());
459 
460                 aAttributes.SetWrapMode(Gdiplus::WrapModeTileFlipXY);
461 
462                 aGraphics.DrawImage(
463                     aARGB.get(),
464                     aDestPoints,
465                     3,
466                     Gdiplus::REAL(0.0),
467                     Gdiplus::REAL(0.0),
468                     Gdiplus::REAL(nSrcWidth),
469                     Gdiplus::REAL(nSrcHeight),
470                     Gdiplus::UnitPixel,
471                     &aAttributes,
472                     0,
473                     0);
474             }
475         }
476 
477         return true;
478     }
479 
480     return false;
481 }
482 
483 // -----------------------------------------------------------------------
484 // eof
485