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 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 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 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 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 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 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 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