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 
impAddB2DPolygonToGDIPlusGraphicsPathReal(Gdiplus::GraphicsPath & rPath,const basegfx::B2DPolygon & rPolygon,bool bNoLineJoin)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 
impAddB2DPolygonToGDIPlusGraphicsPathInteger(Gdiplus::GraphicsPath & rPath,const basegfx::B2DPolygon & rPolygon,bool bNoLineJoin)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 
drawPolyPolygon(const::basegfx::B2DPolyPolygon & rPolyPolygon,double fTransparency)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         if(mbPrinter)
182         {
183             // #121591#
184             // Normally GdiPlus should not be used for printing at all since printers cannot
185             // print transparent filled polygon geometry and normally this does not happen
186             // since OutputDevice::RemoveTransparenciesFromMetaFile is used as preparation
187             // and no transparent parts should remain for printing. But this can be overriden
188             // by the user and thus happens. This call can only come (currently) from
189             // OutputDevice::DrawTransparent, see comments sthere with the same TaskID.
190             // If it is used, the mapping for the printer is wrong and needs to be corrected. I
191             // checked that there is *no* transformation set (testcode commented out below) and
192             // estimated that a stable factor dependent of the printer's DPI is used. Create
193             // and set a transformation here to correct this
194             const Gdiplus::REAL aDpiX(aGraphics.GetDpiX());
195             const Gdiplus::REAL aDpiY(aGraphics.GetDpiY());
196 
197             // test code to check the current transformation at the graphics device
198             //Gdiplus::Matrix matrix;
199             //aGraphics.GetTransform(&matrix);
200             //Gdiplus::REAL elements[6];
201             //matrix.GetElements(elements);
202 
203             Gdiplus::Matrix aPrinterTransform;
204             aPrinterTransform.Scale(Gdiplus::REAL(100.0) / aDpiX, Gdiplus::REAL(100.0) / aDpiY);
205             aGraphics.SetTransform(&aPrinterTransform);
206         }
207 
208 		aGraphics.FillPath(&aTestBrush, &aPath);
209 	}
210 
211  	return true;
212 }
213 
drawPolyLine(const basegfx::B2DPolygon & rPolygon,double fTransparency,const basegfx::B2DVector & rLineWidths,basegfx::B2DLineJoin eLineJoin,com::sun::star::drawing::LineCap eLineCap)214 bool WinSalGraphics::drawPolyLine(
215     const basegfx::B2DPolygon& rPolygon,
216     double fTransparency,
217     const basegfx::B2DVector& rLineWidths,
218     basegfx::B2DLineJoin eLineJoin,
219     com::sun::star::drawing::LineCap eLineCap)
220 {
221     const sal_uInt32 nCount(rPolygon.count());
222 
223 	if(mbPen && nCount)
224 	{
225 		Gdiplus::Graphics aGraphics(getHDC());
226 		const sal_uInt8 aTrans = (sal_uInt8)basegfx::fround( 255 * (1.0 - fTransparency) );
227 		Gdiplus::Color aTestColor(aTrans, SALCOLOR_RED(maLineColor), SALCOLOR_GREEN(maLineColor), SALCOLOR_BLUE(maLineColor));
228 		Gdiplus::Pen aTestPen(aTestColor, Gdiplus::REAL(rLineWidths.getX()));
229 		Gdiplus::GraphicsPath aPath;
230 		bool bNoLineJoin(false);
231 
232 		switch(eLineJoin)
233 		{
234 			default : // basegfx::B2DLINEJOIN_NONE :
235 			{
236 				if(basegfx::fTools::more(rLineWidths.getX(), 0.0))
237 				{
238 					bNoLineJoin = true;
239 				}
240 				break;
241 			}
242 			case basegfx::B2DLINEJOIN_BEVEL :
243 			{
244 				aTestPen.SetLineJoin(Gdiplus::LineJoinBevel);
245 				break;
246 			}
247 			case basegfx::B2DLINEJOIN_MIDDLE :
248 			case basegfx::B2DLINEJOIN_MITER :
249 			{
250 				const Gdiplus::REAL aMiterLimit(15.0);
251 				aTestPen.SetMiterLimit(aMiterLimit);
252 				aTestPen.SetLineJoin(Gdiplus::LineJoinMiter);
253 				break;
254 			}
255 			case basegfx::B2DLINEJOIN_ROUND :
256 			{
257 				aTestPen.SetLineJoin(Gdiplus::LineJoinRound);
258 				break;
259 			}
260 		}
261 
262         switch(eLineCap)
263         {
264             default: /*com::sun::star::drawing::LineCap_BUTT*/
265             {
266                 // nothing to do
267                 break;
268             }
269             case com::sun::star::drawing::LineCap_ROUND:
270             {
271                 aTestPen.SetStartCap(Gdiplus::LineCapRound);
272                 aTestPen.SetEndCap(Gdiplus::LineCapRound);
273                 break;
274             }
275             case com::sun::star::drawing::LineCap_SQUARE:
276             {
277                 aTestPen.SetStartCap(Gdiplus::LineCapSquare);
278                 aTestPen.SetEndCap(Gdiplus::LineCapSquare);
279                 break;
280             }
281         }
282 
283 		if(nCount > 250 && basegfx::fTools::more(rLineWidths.getX(), 1.5))
284         {
285     		impAddB2DPolygonToGDIPlusGraphicsPathInteger(aPath, rPolygon, bNoLineJoin);
286         }
287         else
288         {
289     		impAddB2DPolygonToGDIPlusGraphicsPathReal(aPath, rPolygon, bNoLineJoin);
290         }
291 
292         if(rPolygon.isClosed() && !bNoLineJoin)
293         {
294             // #i101491# needed to create the correct line joins
295             aPath.CloseFigure();
296         }
297 
298         if(getAntiAliasB2DDraw())
299         {
300     		aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
301         }
302         else
303         {
304     		aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
305         }
306 
307 		aGraphics.DrawPath(&aTestPen, &aPath);
308 	}
309 
310 	return true;
311 }
312 
313 // -----------------------------------------------------------------------
314 
paintToGdiPlus(Gdiplus::Graphics & rGraphics,const SalTwoRect & rTR,Gdiplus::Bitmap & rBitmap)315 void paintToGdiPlus(
316     Gdiplus::Graphics& rGraphics,
317     const SalTwoRect& rTR,
318     Gdiplus::Bitmap& rBitmap)
319 {
320     // only parts of source are used
321     Gdiplus::PointF aDestPoints[3];
322     Gdiplus::ImageAttributes aAttributes;
323 
324     // define target region as paralellogram
325     aDestPoints[0].X = Gdiplus::REAL(rTR.mnDestX);
326     aDestPoints[0].Y = Gdiplus::REAL(rTR.mnDestY);
327     aDestPoints[1].X = Gdiplus::REAL(rTR.mnDestX + rTR.mnDestWidth);
328     aDestPoints[1].Y = Gdiplus::REAL(rTR.mnDestY);
329     aDestPoints[2].X = Gdiplus::REAL(rTR.mnDestX);
330     aDestPoints[2].Y = Gdiplus::REAL(rTR.mnDestY + rTR.mnDestHeight);
331 
332     aAttributes.SetWrapMode(Gdiplus::WrapModeTileFlipXY);
333 
334     rGraphics.DrawImage(
335         &rBitmap,
336         aDestPoints,
337         3,
338         Gdiplus::REAL(rTR.mnSrcX),
339         Gdiplus::REAL(rTR.mnSrcY),
340         Gdiplus::REAL(rTR.mnSrcWidth),
341         Gdiplus::REAL(rTR.mnSrcHeight),
342         Gdiplus::UnitPixel,
343         &aAttributes,
344         0,
345         0);
346 }
347 
348 // -----------------------------------------------------------------------
349 
setInterpolationMode(Gdiplus::Graphics & rGraphics,const long & rSrcWidth,const long & rDestWidth,const long & rSrcHeight,const long & rDestHeight)350 void setInterpolationMode(
351     Gdiplus::Graphics& rGraphics,
352     const long& rSrcWidth,
353     const long& rDestWidth,
354     const long& rSrcHeight,
355     const long& rDestHeight)
356 {
357     const bool bSameWidth(rSrcWidth == rDestWidth);
358     const bool bSameHeight(rSrcHeight == rDestHeight);
359 
360     if(bSameWidth && bSameHeight)
361     {
362         rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeInvalid);
363     }
364     else if(rDestWidth > rSrcWidth && rDestHeight > rSrcHeight)
365     {
366         rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeDefault);
367     }
368     else if(rDestWidth < rSrcWidth && rDestHeight < rSrcHeight)
369     {
370         rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeBicubic);
371     }
372     else
373     {
374         rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeDefault);
375     }
376 }
377 
378 
tryDrawBitmapGdiPlus(const SalTwoRect & rTR,const SalBitmap & rSrcBitmap)379 bool WinSalGraphics::tryDrawBitmapGdiPlus(const SalTwoRect& rTR, const SalBitmap& rSrcBitmap)
380 {
381     if(rTR.mnSrcWidth && rTR.mnSrcHeight && rTR.mnDestWidth && rTR.mnDestHeight)
382     {
383         const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSrcBitmap);
384         GdiPlusBmpPtr aARGB(rSalBitmap.ImplGetGdiPlusBitmap());
385 
386         if(aARGB.get())
387         {
388             Gdiplus::Graphics aGraphics(getHDC());
389 
390             setInterpolationMode(
391                 aGraphics,
392                 rTR.mnSrcWidth,
393                 rTR.mnDestWidth,
394                 rTR.mnSrcHeight,
395                 rTR.mnDestHeight);
396 
397             paintToGdiPlus(
398                 aGraphics,
399                 rTR,
400                 *aARGB.get());
401 
402             return true;
403         }
404     }
405 
406     return false;
407 }
408 
drawAlphaBitmap(const SalTwoRect & rTR,const SalBitmap & rSrcBitmap,const SalBitmap & rAlphaBmp)409 bool WinSalGraphics::drawAlphaBitmap(
410     const SalTwoRect& rTR,
411     const SalBitmap& rSrcBitmap,
412     const SalBitmap& rAlphaBmp)
413 {
414     if(rTR.mnSrcWidth && rTR.mnSrcHeight && rTR.mnDestWidth && rTR.mnDestHeight)
415     {
416         const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSrcBitmap);
417         const WinSalBitmap& rSalAlpha = static_cast< const WinSalBitmap& >(rAlphaBmp);
418         GdiPlusBmpPtr aARGB(rSalBitmap.ImplGetGdiPlusBitmap(&rSalAlpha));
419 
420         if(aARGB.get())
421         {
422             Gdiplus::Graphics aGraphics(getHDC());
423 
424             setInterpolationMode(
425                 aGraphics,
426                 rTR.mnSrcWidth,
427                 rTR.mnDestWidth,
428                 rTR.mnSrcHeight,
429                 rTR.mnDestHeight);
430 
431             paintToGdiPlus(
432                 aGraphics,
433                 rTR,
434                 *aARGB.get());
435 
436             return true;
437         }
438     }
439 
440     return false;
441 }
442 
443 // -----------------------------------------------------------------------
444 
drawTransformedBitmap(const basegfx::B2DPoint & rNull,const basegfx::B2DPoint & rX,const basegfx::B2DPoint & rY,const SalBitmap & rSourceBitmap,const SalBitmap * pAlphaBitmap)445 bool WinSalGraphics::drawTransformedBitmap(
446     const basegfx::B2DPoint& rNull,
447     const basegfx::B2DPoint& rX,
448     const basegfx::B2DPoint& rY,
449     const SalBitmap& rSourceBitmap,
450     const SalBitmap* pAlphaBitmap)
451 {
452     const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSourceBitmap);
453     const WinSalBitmap* pSalAlpha = static_cast< const WinSalBitmap* >(pAlphaBitmap);
454     GdiPlusBmpPtr aARGB(rSalBitmap.ImplGetGdiPlusBitmap(pSalAlpha));
455 
456     if(aARGB.get())
457     {
458         const long nSrcWidth(aARGB->GetWidth());
459         const long nSrcHeight(aARGB->GetHeight());
460 
461         if(nSrcWidth && nSrcHeight)
462         {
463             const long nDestWidth(basegfx::fround(basegfx::B2DVector(rX - rNull).getLength()));
464             const long nDestHeight(basegfx::fround(basegfx::B2DVector(rY - rNull).getLength()));
465 
466             if(nDestWidth && nDestHeight)
467             {
468                 Gdiplus::Graphics aGraphics(getHDC());
469                 Gdiplus::PointF aDestPoints[3];
470                 Gdiplus::ImageAttributes aAttributes;
471 
472                 setInterpolationMode(
473                     aGraphics,
474                     nSrcWidth,
475                     nDestWidth,
476                     nSrcHeight,
477                     nDestHeight);
478 
479                 // this mode is only capable of drawing the whole bitmap to a paralellogram
480                 aDestPoints[0].X = Gdiplus::REAL(rNull.getX());
481                 aDestPoints[0].Y = Gdiplus::REAL(rNull.getY());
482                 aDestPoints[1].X = Gdiplus::REAL(rX.getX());
483                 aDestPoints[1].Y = Gdiplus::REAL(rX.getY());
484                 aDestPoints[2].X = Gdiplus::REAL(rY.getX());
485                 aDestPoints[2].Y = Gdiplus::REAL(rY.getY());
486 
487                 aAttributes.SetWrapMode(Gdiplus::WrapModeTileFlipXY);
488 
489                 aGraphics.DrawImage(
490                     aARGB.get(),
491                     aDestPoints,
492                     3,
493                     Gdiplus::REAL(0.0),
494                     Gdiplus::REAL(0.0),
495                     Gdiplus::REAL(nSrcWidth),
496                     Gdiplus::REAL(nSrcHeight),
497                     Gdiplus::UnitPixel,
498                     &aAttributes,
499                     0,
500                     0);
501             }
502         }
503 
504         return true;
505     }
506 
507     return false;
508 }
509 
510 // -----------------------------------------------------------------------
511 // eof
512