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