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