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