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