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_drawinglayer.hxx" 26 27 #include <drawinglayer/primitive2d/graphicprimitive2d.hxx> 28 #include <drawinglayer/animation/animationtiming.hxx> 29 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> 30 #include <drawinglayer/primitive2d/rendergraphicprimitive2d.hxx> 31 #include <drawinglayer/primitive2d/animatedprimitive2d.hxx> 32 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx> 33 #include <drawinglayer/primitive2d/transformprimitive2d.hxx> 34 #include <basegfx/polygon/b2dpolygon.hxx> 35 #include <basegfx/polygon/b2dpolygontools.hxx> 36 #include <drawinglayer/primitive2d/maskprimitive2d.hxx> 37 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> 38 39 ////////////////////////////////////////////////////////////////////////////// 40 // helper class for animated graphics 41 42 #include <vcl/animate.hxx> 43 #include <vcl/graph.hxx> 44 #include <vcl/virdev.hxx> 45 #include <vcl/svapp.hxx> 46 #include <vcl/metaact.hxx> 47 48 ////////////////////////////////////////////////////////////////////////////// 49 // includes for testing MetafilePrimitive2D::create2DDecomposition 50 51 // this switch defines if the test code is included or not 52 #undef USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE 53 54 #ifdef USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE 55 #include <vcl/gradient.hxx> 56 #include <vcl/pngread.hxx> 57 #include <vcl/lineinfo.hxx> 58 #endif // USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE 59 60 ////////////////////////////////////////////////////////////////////////////// 61 62 namespace 63 { 64 struct animationStep 65 { 66 BitmapEx maBitmapEx; 67 sal_uInt32 mnTime; 68 }; 69 70 class animatedBitmapExPreparator 71 { 72 ::Animation maAnimation; 73 ::std::vector< animationStep > maSteps; 74 75 sal_uInt32 generateStepTime(sal_uInt32 nIndex) const; 76 77 public: 78 animatedBitmapExPreparator(const Graphic& rGraphic); 79 80 sal_uInt32 count() const { return maSteps.size(); } 81 sal_uInt32 loopCount() const { return (sal_uInt32)maAnimation.GetLoopCount(); } 82 sal_uInt32 stepTime(sal_uInt32 a) const { return maSteps[a].mnTime; } 83 const BitmapEx& stepBitmapEx(sal_uInt32 a) const { return maSteps[a].maBitmapEx; } 84 }; 85 86 sal_uInt32 animatedBitmapExPreparator::generateStepTime(sal_uInt32 nIndex) const 87 { 88 const AnimationBitmap& rAnimBitmap = maAnimation.Get(sal_uInt16(nIndex)); 89 sal_uInt32 nWaitTime(rAnimBitmap.nWait * 10); 90 91 // #115934# 92 // Take care of special value for MultiPage TIFFs. ATM these shall just 93 // show their first page. Later we will offer some switching when object 94 // is selected. 95 if(ANIMATION_TIMEOUT_ON_CLICK == rAnimBitmap.nWait) 96 { 97 // ATM the huge value would block the timer, so 98 // use a long time to show first page (whole day) 99 nWaitTime = 100 * 60 * 60 * 24; 100 } 101 102 // Bad trap: There are animated gifs with no set WaitTime (!). 103 // In that case use a default value. 104 if(0L == nWaitTime) 105 { 106 nWaitTime = 100L; 107 } 108 109 return nWaitTime; 110 } 111 112 animatedBitmapExPreparator::animatedBitmapExPreparator(const Graphic& rGraphic) 113 : maAnimation(rGraphic.GetAnimation()) 114 { 115 OSL_ENSURE(GRAPHIC_BITMAP == rGraphic.GetType() && rGraphic.IsAnimated(), "animatedBitmapExPreparator: graphic is not animated (!)"); 116 117 // #128539# secure access to Animation, looks like there exist animated GIFs out there 118 // with a step count of zero 119 if(maAnimation.Count()) 120 { 121 VirtualDevice aVirtualDevice(*Application::GetDefaultDevice()); 122 VirtualDevice aVirtualDeviceMask(*Application::GetDefaultDevice(), 1L); 123 124 // Prepare VirtualDevices and their states 125 aVirtualDevice.EnableMapMode(sal_False); 126 aVirtualDeviceMask.EnableMapMode(sal_False); 127 aVirtualDevice.SetOutputSizePixel(maAnimation.GetDisplaySizePixel()); 128 aVirtualDeviceMask.SetOutputSizePixel(maAnimation.GetDisplaySizePixel()); 129 aVirtualDevice.Erase(); 130 aVirtualDeviceMask.Erase(); 131 132 for(sal_uInt16 a(0L); a < maAnimation.Count(); a++) 133 { 134 animationStep aNextStep; 135 aNextStep.mnTime = generateStepTime(a); 136 137 // prepare step 138 const AnimationBitmap& rAnimBitmap = maAnimation.Get(sal_uInt16(a)); 139 140 switch(rAnimBitmap.eDisposal) 141 { 142 case DISPOSE_NOT: 143 { 144 aVirtualDevice.DrawBitmapEx(rAnimBitmap.aPosPix, rAnimBitmap.aBmpEx); 145 Bitmap aMask = rAnimBitmap.aBmpEx.GetMask(); 146 147 if(aMask.IsEmpty()) 148 { 149 const Point aEmpty; 150 const Rectangle aRect(aEmpty, aVirtualDeviceMask.GetOutputSizePixel()); 151 const Wallpaper aWallpaper(COL_BLACK); 152 aVirtualDeviceMask.DrawWallpaper(aRect, aWallpaper); 153 } 154 else 155 { 156 BitmapEx aExpandVisibilityMask = BitmapEx(aMask, aMask); 157 aVirtualDeviceMask.DrawBitmapEx(rAnimBitmap.aPosPix, aExpandVisibilityMask); 158 } 159 160 break; 161 } 162 case DISPOSE_BACK: 163 { 164 // #i70772# react on no mask, for primitives, too. 165 const Bitmap aMask(rAnimBitmap.aBmpEx.GetMask()); 166 const Bitmap aContent(rAnimBitmap.aBmpEx.GetBitmap()); 167 168 aVirtualDeviceMask.Erase(); 169 aVirtualDevice.DrawBitmap(rAnimBitmap.aPosPix, aContent); 170 171 if(aMask.IsEmpty()) 172 { 173 const Rectangle aRect(rAnimBitmap.aPosPix, aContent.GetSizePixel()); 174 aVirtualDeviceMask.SetFillColor(COL_BLACK); 175 aVirtualDeviceMask.SetLineColor(); 176 aVirtualDeviceMask.DrawRect(aRect); 177 } 178 else 179 { 180 aVirtualDeviceMask.DrawBitmap(rAnimBitmap.aPosPix, aMask); 181 } 182 183 break; 184 } 185 case DISPOSE_FULL: 186 { 187 aVirtualDevice.DrawBitmapEx(rAnimBitmap.aPosPix, rAnimBitmap.aBmpEx); 188 break; 189 } 190 case DISPOSE_PREVIOUS : 191 { 192 aVirtualDevice.DrawBitmapEx(rAnimBitmap.aPosPix, rAnimBitmap.aBmpEx); 193 aVirtualDeviceMask.DrawBitmap(rAnimBitmap.aPosPix, rAnimBitmap.aBmpEx.GetMask()); 194 break; 195 } 196 } 197 198 // create BitmapEx 199 Bitmap aMainBitmap = aVirtualDevice.GetBitmap(Point(), aVirtualDevice.GetOutputSizePixel()); 200 Bitmap aMaskBitmap = aVirtualDeviceMask.GetBitmap(Point(), aVirtualDeviceMask.GetOutputSizePixel()); 201 aNextStep.maBitmapEx = BitmapEx(aMainBitmap, aMaskBitmap); 202 203 // add to vector 204 maSteps.push_back(aNextStep); 205 } 206 } 207 } 208 } // end of anonymous namespace 209 210 ////////////////////////////////////////////////////////////////////////////// 211 212 namespace drawinglayer 213 { 214 namespace primitive2d 215 { 216 Primitive2DSequence GraphicPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& 217 #ifdef USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE 218 rViewInformation 219 #else 220 /*rViewInformation*/ 221 #endif // USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE 222 ) const 223 { 224 Primitive2DSequence aRetval; 225 226 if(255L != getGraphicAttr().GetTransparency()) 227 { 228 Primitive2DReference xPrimitive; 229 230 // do not apply mirroring from GraphicAttr to the Metafile by calling 231 // GetTransformedGraphic, this will try to mirror the Metafile using Scale() 232 // at the Metafile. This again calls Scale at the single MetaFile actions, 233 // but this implementation never worked. I reworked that implementations, 234 // but for security reasons i will try not to use it. 235 basegfx::B2DHomMatrix aTransform(getTransform()); 236 237 if(getGraphicAttr().IsMirrored()) 238 { 239 // content needs mirroring 240 const bool bHMirr(getGraphicAttr().GetMirrorFlags() & BMP_MIRROR_HORZ); 241 const bool bVMirr(getGraphicAttr().GetMirrorFlags() & BMP_MIRROR_VERT); 242 243 // mirror by applying negative scale to the unit primitive and 244 // applying the object transformation on it. 245 aTransform = basegfx::tools::createScaleB2DHomMatrix( 246 bHMirr ? -1.0 : 1.0, 247 bVMirr ? -1.0 : 1.0); 248 aTransform.translate( 249 bHMirr ? 1.0 : 0.0, 250 bVMirr ? 1.0 : 0.0); 251 aTransform = getTransform() * aTransform; 252 } 253 254 // Get transformed graphic. Suppress rotation and cropping, only filtering is needed 255 // here (and may be replaced later on). Cropping is handled below as mask primitive (if set). 256 // Also need to suppress mirroring, it is part of the transformation now (see above). 257 GraphicAttr aSuppressGraphicAttr(getGraphicAttr()); 258 aSuppressGraphicAttr.SetCrop(0, 0, 0, 0); 259 aSuppressGraphicAttr.SetRotation(0); 260 aSuppressGraphicAttr.SetMirrorFlags(0); 261 262 const GraphicObject& rGraphicObject = getGraphicObject(); 263 const Graphic aTransformedGraphic(rGraphicObject.GetTransformedGraphic(&aSuppressGraphicAttr)); 264 265 switch(aTransformedGraphic.GetType()) 266 { 267 case GRAPHIC_BITMAP : 268 { 269 if(aTransformedGraphic.IsAnimated()) 270 { 271 // prepare animation data 272 animatedBitmapExPreparator aData(aTransformedGraphic); 273 274 if(aData.count()) 275 { 276 // create sub-primitives for animated bitmap and the needed animation loop 277 animation::AnimationEntryLoop aAnimationLoop(aData.loopCount() ? aData.loopCount() : 0xffff); 278 Primitive2DSequence aBitmapPrimitives(aData.count()); 279 280 for(sal_uInt32 a(0L); a < aData.count(); a++) 281 { 282 animation::AnimationEntryFixed aTime((double)aData.stepTime(a), (double)a / (double)aData.count()); 283 aAnimationLoop.append(aTime); 284 const Primitive2DReference xRef(new BitmapPrimitive2D(aData.stepBitmapEx(a), aTransform)); 285 aBitmapPrimitives[a] = xRef; 286 } 287 288 // prepare animation list 289 animation::AnimationEntryList aAnimationList; 290 aAnimationList.append(aAnimationLoop); 291 292 // create and add animated switch primitive 293 xPrimitive = Primitive2DReference(new AnimatedSwitchPrimitive2D(aAnimationList, aBitmapPrimitives, false)); 294 } 295 } 296 else 297 { 298 xPrimitive = Primitive2DReference(new BitmapPrimitive2D(aTransformedGraphic.GetBitmapEx(), aTransform)); 299 } 300 301 break; 302 } 303 304 case GRAPHIC_GDIMETAFILE : 305 { 306 #ifdef USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE 307 static bool bDoTest(false); 308 309 if(bDoTest) 310 { 311 // All this is/was test code for testing MetafilePrimitive2D::create2DDecomposition 312 // extensively. It may be needed again when diverse actions need debugging, so i leave 313 // it in here, but take it out using USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE. 314 // Use it by compiling with the code, insert any DrawObject, convert to Metafile. The 315 // debugger will then stop here (when breakpoint set, of course). You may enter single 316 // parts of actions and/or change to true what You want to check. 317 GDIMetaFile aMtf; 318 VirtualDevice aOut; 319 const basegfx::B2DRange aRange(getB2DRange(rViewInformation)); 320 const Rectangle aRectangle( 321 basegfx::fround(aRange.getMinX()), basegfx::fround(aRange.getMinY()), 322 basegfx::fround(aRange.getMaxX()), basegfx::fround(aRange.getMaxY())); 323 const Point aOrigin(aRectangle.TopLeft()); 324 const Fraction aScaleX(aRectangle.getWidth()); 325 const Fraction aScaleY(aRectangle.getHeight()); 326 MapMode aMapMode(MAP_100TH_MM, aOrigin, aScaleX, aScaleY); 327 328 Size aDummySize(2, 2); 329 aOut.SetOutputSizePixel(aDummySize); 330 aOut.EnableOutput(FALSE); 331 aOut.SetMapMode(aMapMode); 332 333 aMtf.Clear(); 334 aMtf.Record(&aOut); 335 336 const Fraction aNeutralFraction(1, 1); 337 const MapMode aRelativeMapMode( 338 MAP_RELATIVE, 339 Point(-aRectangle.Left(), -aRectangle.Top()), 340 aNeutralFraction, aNeutralFraction); 341 aOut.SetMapMode(aRelativeMapMode); 342 343 if(false) 344 { 345 const sal_Int32 nHor(aRectangle.getWidth() / 4); 346 const sal_Int32 nVer(aRectangle.getHeight() / 4); 347 const Rectangle aCenteredRectangle( 348 aRectangle.Left() + nHor, aRectangle.Top() + nVer, 349 aRectangle.Right() - nHor, aRectangle.Bottom() - nVer); 350 aOut.SetClipRegion(aCenteredRectangle); 351 } 352 353 if(false) 354 { 355 const Rectangle aRightRectangle(aRectangle.TopCenter(), aRectangle.BottomRight()); 356 aOut.IntersectClipRegion(aRightRectangle); 357 } 358 359 if(false) 360 { 361 const Rectangle aRightRectangle(aRectangle.TopCenter(), aRectangle.BottomRight()); 362 const Rectangle aBottomRectangle(aRectangle.LeftCenter(), aRectangle.BottomRight()); 363 Region aRegion(aRightRectangle); 364 aRegion.Intersect(aBottomRectangle); 365 aOut.IntersectClipRegion(aRegion); 366 } 367 368 if(false) 369 { 370 const sal_Int32 nHor(aRectangle.getWidth() / 10); 371 const sal_Int32 nVer(aRectangle.getHeight() / 10); 372 aOut.MoveClipRegion(nHor, nVer); 373 } 374 375 if(false) 376 { 377 Wallpaper aWallpaper(Color(COL_BLACK)); 378 aOut.DrawWallpaper(aRectangle, aWallpaper); 379 } 380 381 if(false) 382 { 383 Wallpaper aWallpaper(Gradient(GRADIENT_LINEAR, Color(COL_RED), Color(COL_GREEN))); 384 aOut.DrawWallpaper(aRectangle, aWallpaper); 385 } 386 387 if(false) 388 { 389 SvFileStream aRead((const String&)String(ByteString( "c:\\test.png" ), RTL_TEXTENCODING_UTF8), STREAM_READ); 390 vcl::PNGReader aPNGReader(aRead); 391 BitmapEx aBitmapEx(aPNGReader.Read()); 392 Wallpaper aWallpaper(aBitmapEx); 393 aOut.DrawWallpaper(aRectangle, aWallpaper); 394 } 395 396 if(false) 397 { 398 const double fHor(aRectangle.getWidth()); 399 const double fVer(aRectangle.getHeight()); 400 Color aColor(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)); 401 402 for(sal_uInt32 a(0); a < 5000; a++) 403 { 404 const Point aPoint( 405 aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)), 406 aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0))); 407 408 if(!(a % 3)) 409 { 410 aColor = Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)); 411 } 412 413 aOut.DrawPixel(aPoint, aColor); 414 } 415 } 416 417 if(false) 418 { 419 const double fHor(aRectangle.getWidth()); 420 const double fVer(aRectangle.getHeight()); 421 422 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 423 aOut.SetFillColor(); 424 425 for(sal_uInt32 a(0); a < 5000; a++) 426 { 427 const Point aPoint( 428 aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)), 429 aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0))); 430 aOut.DrawPixel(aPoint); 431 } 432 } 433 434 if(false) 435 { 436 const double fHor(aRectangle.getWidth()); 437 const double fVer(aRectangle.getHeight()); 438 439 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 440 aOut.SetFillColor(); 441 442 Point aStart( 443 aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)), 444 aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0))); 445 Point aStop( 446 aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)), 447 aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0))); 448 449 LineInfo aLineInfo(LINE_SOLID, basegfx::fround(fHor / 50.0)); 450 bool bUseLineInfo(false); 451 452 for(sal_uInt32 a(0); a < 20; a++) 453 { 454 if(!(a%6)) 455 { 456 bUseLineInfo = !bUseLineInfo; 457 } 458 459 if(!(a%4)) 460 { 461 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 462 } 463 464 if(a%3) 465 { 466 aStart = aStop; 467 aStop = Point( 468 aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)), 469 aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0))); 470 } 471 else 472 { 473 aStart = Point( 474 aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)), 475 aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0))); 476 aStop = Point( 477 aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)), 478 aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0))); 479 } 480 481 if(bUseLineInfo) 482 { 483 aOut.DrawLine(aStart, aStop, aLineInfo); 484 } 485 else 486 { 487 aOut.DrawLine(aStart, aStop); 488 } 489 } 490 } 491 492 if(false) 493 { 494 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 495 aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 496 aOut.DrawRect(aRectangle); 497 } 498 499 if(false) 500 { 501 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 502 aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 503 const sal_uInt32 nHor(aRectangle.getWidth() / 10); 504 const sal_uInt32 nVer(aRectangle.getHeight() / 10); 505 aOut.DrawRect(aRectangle, nHor, nVer); 506 } 507 508 if(false) 509 { 510 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 511 aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 512 aOut.DrawEllipse(aRectangle); 513 } 514 515 if(false) 516 { 517 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 518 aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 519 aOut.DrawArc(aRectangle, aRectangle.TopLeft(), aRectangle.BottomCenter()); 520 } 521 522 if(false) 523 { 524 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 525 aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 526 aOut.DrawPie(aRectangle, aRectangle.TopLeft(), aRectangle.BottomCenter()); 527 } 528 529 if(false) 530 { 531 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 532 aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 533 aOut.DrawChord(aRectangle, aRectangle.TopLeft(), aRectangle.BottomCenter()); 534 } 535 536 if(false) 537 { 538 const double fHor(aRectangle.getWidth()); 539 const double fVer(aRectangle.getHeight()); 540 541 for(sal_uInt32 b(0); b < 5; b++) 542 { 543 const sal_uInt32 nCount(basegfx::fround(rand() * (20 / 32767.0))); 544 const bool bClose(basegfx::fround(rand() / 32767.0)); 545 Polygon aPolygon(nCount + (bClose ? 1 : 0)); 546 547 for(sal_uInt32 a(0); a < nCount; a++) 548 { 549 const Point aPoint( 550 aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)), 551 aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0))); 552 aPolygon[a] = aPoint; 553 } 554 555 if(bClose) 556 { 557 aPolygon[aPolygon.GetSize() - 1] = aPolygon[0]; 558 } 559 560 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 561 aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 562 563 if(!(b%2)) 564 { 565 const LineInfo aLineInfo(LINE_SOLID, basegfx::fround(fHor / 50.0)); 566 aOut.DrawPolyLine(aPolygon, aLineInfo); 567 } 568 else 569 { 570 aOut.DrawPolyLine(aPolygon); 571 } 572 } 573 } 574 575 if(false) 576 { 577 const double fHor(aRectangle.getWidth()); 578 const double fVer(aRectangle.getHeight()); 579 580 for(sal_uInt32 b(0); b < 5; b++) 581 { 582 const sal_uInt32 nCount(basegfx::fround(rand() * (20 / 32767.0))); 583 const bool bClose(basegfx::fround(rand() / 32767.0)); 584 Polygon aPolygon(nCount + (bClose ? 1 : 0)); 585 586 for(sal_uInt32 a(0); a < nCount; a++) 587 { 588 const Point aPoint( 589 aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)), 590 aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0))); 591 aPolygon[a] = aPoint; 592 } 593 594 if(bClose) 595 { 596 aPolygon[aPolygon.GetSize() - 1] = aPolygon[0]; 597 } 598 599 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 600 aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 601 aOut.DrawPolygon(aPolygon); 602 } 603 } 604 605 if(false) 606 { 607 const double fHor(aRectangle.getWidth()); 608 const double fVer(aRectangle.getHeight()); 609 PolyPolygon aPolyPolygon; 610 611 for(sal_uInt32 b(0); b < 3; b++) 612 { 613 const sal_uInt32 nCount(basegfx::fround(rand() * (6 / 32767.0))); 614 const bool bClose(basegfx::fround(rand() / 32767.0)); 615 Polygon aPolygon(nCount + (bClose ? 1 : 0)); 616 617 for(sal_uInt32 a(0); a < nCount; a++) 618 { 619 const Point aPoint( 620 aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)), 621 aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0))); 622 aPolygon[a] = aPoint; 623 } 624 625 if(bClose) 626 { 627 aPolygon[aPolygon.GetSize() - 1] = aPolygon[0]; 628 } 629 630 aPolyPolygon.Insert(aPolygon); 631 } 632 633 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 634 aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0))); 635 aOut.DrawPolyPolygon(aPolyPolygon); 636 } 637 638 if(false) 639 { 640 SvFileStream aRead((const String&)String(ByteString( "c:\\test.png" ), RTL_TEXTENCODING_UTF8), STREAM_READ); 641 vcl::PNGReader aPNGReader(aRead); 642 BitmapEx aBitmapEx(aPNGReader.Read()); 643 aOut.DrawBitmapEx(aRectangle.TopLeft(), aBitmapEx); 644 } 645 646 if(false) 647 { 648 SvFileStream aRead((const String&)String(ByteString( "c:\\test.png" ), RTL_TEXTENCODING_UTF8), STREAM_READ); 649 vcl::PNGReader aPNGReader(aRead); 650 BitmapEx aBitmapEx(aPNGReader.Read()); 651 aOut.DrawBitmapEx(aRectangle.TopLeft(), aRectangle.GetSize(), aBitmapEx); 652 } 653 654 if(false) 655 { 656 SvFileStream aRead((const String&)String(ByteString( "c:\\test.png" ), RTL_TEXTENCODING_UTF8), STREAM_READ); 657 vcl::PNGReader aPNGReader(aRead); 658 BitmapEx aBitmapEx(aPNGReader.Read()); 659 const Size aSizePixel(aBitmapEx.GetSizePixel()); 660 aOut.DrawBitmapEx( 661 aRectangle.TopLeft(), 662 aRectangle.GetSize(), 663 Point(0, 0), 664 Size(aSizePixel.Width() /2, aSizePixel.Height() / 2), 665 aBitmapEx); 666 } 667 668 if(false) 669 { 670 const double fHor(aRectangle.getWidth()); 671 const double fVer(aRectangle.getHeight()); 672 const Point aPointA( 673 aRectangle.Left() + basegfx::fround(fHor * 0.2), 674 aRectangle.Top() + basegfx::fround(fVer * 0.3)); 675 const Point aPointB( 676 aRectangle.Left() + basegfx::fround(fHor * 0.2), 677 aRectangle.Top() + basegfx::fround(fVer * 0.5)); 678 const Point aPointC( 679 aRectangle.Left() + basegfx::fround(fHor * 0.2), 680 aRectangle.Top() + basegfx::fround(fVer * 0.7)); 681 const String aText(ByteString("Hello, World!"), RTL_TEXTENCODING_UTF8); 682 683 const String aFontName(ByteString("Comic Sans MS"), RTL_TEXTENCODING_UTF8); 684 Font aFont(aFontName, Size(0, 1000)); 685 aFont.SetAlign(ALIGN_BASELINE); 686 aFont.SetColor(COL_RED); 687 //sal_Int32* pDXArray = new sal_Int32[aText.Len()]; 688 689 aFont.SetOutline(true); 690 aOut.SetFont(aFont); 691 aOut.DrawText(aPointA, aText, 0, aText.Len()); 692 693 aFont.SetShadow(true); 694 aOut.SetFont(aFont); 695 aOut.DrawText(aPointB, aText, 0, aText.Len()); 696 697 aFont.SetRelief(RELIEF_EMBOSSED); 698 aOut.SetFont(aFont); 699 aOut.DrawText(aPointC, aText, 0, aText.Len()); 700 701 //delete pDXArray; 702 } 703 704 if(false) 705 { 706 const double fHor(aRectangle.getWidth()); 707 const double fVer(aRectangle.getHeight()); 708 const Point aPointA( 709 aRectangle.Left() + basegfx::fround(fHor * 0.2), 710 aRectangle.Top() + basegfx::fround(fVer * 0.3)); 711 const Point aPointB( 712 aRectangle.Left() + basegfx::fround(fHor * 0.2), 713 aRectangle.Top() + basegfx::fround(fVer * 0.5)); 714 const Point aPointC( 715 aRectangle.Left() + basegfx::fround(fHor * 0.2), 716 aRectangle.Top() + basegfx::fround(fVer * 0.7)); 717 const String aText(ByteString("Hello, World!"), RTL_TEXTENCODING_UTF8); 718 719 const String aFontName(ByteString("Comic Sans MS"), RTL_TEXTENCODING_UTF8); 720 Font aFont(aFontName, Size(0, 1000)); 721 aFont.SetAlign(ALIGN_BASELINE); 722 aFont.SetColor(COL_RED); 723 724 aOut.SetFont(aFont); 725 const sal_Int32 nWidth(aOut.GetTextWidth(aText, 0, aText.Len())); 726 aOut.DrawText(aPointA, aText, 0, aText.Len()); 727 aOut.DrawTextLine(aPointA, nWidth, STRIKEOUT_SINGLE, UNDERLINE_SINGLE, UNDERLINE_SMALLWAVE); 728 aOut.DrawTextLine(aPointB, nWidth, STRIKEOUT_SINGLE, UNDERLINE_SINGLE, UNDERLINE_SMALLWAVE); 729 aOut.DrawTextLine(aPointC, nWidth, STRIKEOUT_SINGLE, UNDERLINE_SINGLE, UNDERLINE_SMALLWAVE); 730 } 731 732 aMtf.Stop(); 733 aMtf.WindStart(); 734 aMtf.SetPrefMapMode(MapMode(MAP_100TH_MM)); 735 aMtf.SetPrefSize(Size(aRectangle.getWidth(), aRectangle.getHeight())); 736 737 xPrimitive = Primitive2DReference( 738 new MetafilePrimitive2D( 739 aTransform, 740 aMtf)); 741 } 742 else 743 { 744 #endif // USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE 745 // create MetafilePrimitive2D 746 const GDIMetaFile& rMetafile = aTransformedGraphic.GetGDIMetaFile(); 747 748 if( aTransformedGraphic.IsRenderGraphic() ) 749 { 750 xPrimitive = Primitive2DReference( 751 new RenderGraphicPrimitive2D( 752 static_cast< MetaRenderGraphicAction* >(rMetafile.GetAction(0))->GetRenderGraphic(), 753 aTransform)); 754 } 755 else 756 { 757 xPrimitive = Primitive2DReference( 758 new MetafilePrimitive2D( 759 aTransform, 760 rMetafile)); 761 762 // #i100357# find out if clipping is needed for this primitive. Unfortunately, 763 // there exist Metafiles who's content is bigger than the proposed PrefSize set 764 // at them. This is an error, but we need to work around this 765 const Size aMetaFilePrefSize(rMetafile.GetPrefSize()); 766 const Size aMetaFileRealSize( 767 const_cast< GDIMetaFile& >(rMetafile).GetBoundRect( 768 *Application::GetDefaultDevice()).GetSize()); 769 770 if(aMetaFileRealSize.getWidth() > aMetaFilePrefSize.getWidth() 771 || aMetaFileRealSize.getHeight() > aMetaFilePrefSize.getHeight()) 772 { 773 // clipping needed. Embed to MaskPrimitive2D. Create childs and mask polygon 774 const primitive2d::Primitive2DSequence aChildContent(&xPrimitive, 1); 775 basegfx::B2DPolygon aMaskPolygon(basegfx::tools::createUnitPolygon()); 776 aMaskPolygon.transform(aTransform); 777 778 xPrimitive = Primitive2DReference( 779 new MaskPrimitive2D( 780 basegfx::B2DPolyPolygon(aMaskPolygon), 781 aChildContent)); 782 } 783 } 784 #ifdef USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE 785 } 786 #endif // USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE 787 788 break; 789 } 790 791 default: 792 { 793 // nothing to create 794 break; 795 } 796 } 797 798 if(xPrimitive.is()) 799 { 800 // check for cropping 801 if(getGraphicAttr().IsCropped()) 802 { 803 // decompose to get current pos and size 804 basegfx::B2DVector aScale, aTranslate; 805 double fRotate, fShearX; 806 getTransform().decompose(aScale, aTranslate, fRotate, fShearX); 807 808 // create ranges. The current object range is just scale and translate 809 const basegfx::B2DRange aCurrent( 810 aTranslate.getX(), aTranslate.getY(), 811 aTranslate.getX() + aScale.getX(), aTranslate.getY() + aScale.getY()); 812 813 // calculate scalings between real image size and logic object size. This 814 // is necessary since the crop values are relative to original bitmap size 815 double fFactorX(1.0); 816 double fFactorY(1.0); 817 818 { 819 const MapMode aMapMode100thmm(MAP_100TH_MM); 820 Size aBitmapSize(rGraphicObject.GetPrefSize()); 821 822 // #i95968# better support PrefMapMode; special for MAP_PIXEL was missing 823 if(MAP_PIXEL == rGraphicObject.GetPrefMapMode().GetMapUnit()) 824 { 825 aBitmapSize = Application::GetDefaultDevice()->PixelToLogic(aBitmapSize, aMapMode100thmm); 826 } 827 else 828 { 829 aBitmapSize = Application::GetDefaultDevice()->LogicToLogic(aBitmapSize, rGraphicObject.GetPrefMapMode(), aMapMode100thmm); 830 } 831 832 const double fDivX(aBitmapSize.Width() - getGraphicAttr().GetLeftCrop() - getGraphicAttr().GetRightCrop()); 833 const double fDivY(aBitmapSize.Height() - getGraphicAttr().GetTopCrop() - getGraphicAttr().GetBottomCrop()); 834 835 if(!basegfx::fTools::equalZero(fDivX)) 836 { 837 fFactorX = aScale.getX() / fDivX; 838 } 839 840 if(!basegfx::fTools::equalZero(fDivY)) 841 { 842 fFactorY = aScale.getY() / fDivY; 843 } 844 } 845 846 // Create cropped range, describes the bounds of the original graphic 847 basegfx::B2DRange aCropped; 848 aCropped.expand(aCurrent.getMinimum() - basegfx::B2DPoint(getGraphicAttr().GetLeftCrop() * fFactorX, getGraphicAttr().GetTopCrop() * fFactorY)); 849 aCropped.expand(aCurrent.getMaximum() + basegfx::B2DPoint(getGraphicAttr().GetRightCrop() * fFactorX, getGraphicAttr().GetBottomCrop() * fFactorY)); 850 851 if(aCropped.isEmpty()) 852 { 853 // nothing to add since cropped bitmap is completely empty 854 // xPrimitive will not be used 855 } 856 else 857 { 858 // build new object transformation for transform primitive which contains xPrimitive 859 basegfx::B2DHomMatrix aNewObjectTransform(getTransform()); 860 aNewObjectTransform.invert(); 861 aNewObjectTransform = basegfx::tools::createScaleTranslateB2DHomMatrix( 862 aCropped.getWidth(), aCropped.getHeight(), 863 aCropped.getMinX() - aCurrent.getMinX(), aCropped.getMinY() - aCurrent.getMinY()) 864 * aNewObjectTransform; 865 866 // add shear, rotate and translate using combined matrix to speedup 867 const basegfx::B2DHomMatrix aCombinedMatrix(basegfx::tools::createShearXRotateTranslateB2DHomMatrix( 868 fShearX, fRotate, aTranslate.getX(), aTranslate.getY())); 869 aNewObjectTransform = aCombinedMatrix * aNewObjectTransform; 870 871 // prepare TransformPrimitive2D with xPrimitive 872 const Primitive2DReference xTransformPrimitive(new TransformPrimitive2D(aNewObjectTransform, Primitive2DSequence(&xPrimitive, 1L))); 873 874 if(aCurrent.isInside(aCropped)) 875 { 876 // cropped just got smaller, no need to really use a mask. Add to destination directly 877 appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, xTransformPrimitive); 878 } 879 else 880 { 881 // cropped got bigger, mask it with original object's bounds 882 basegfx::B2DPolyPolygon aMaskPolyPolygon(basegfx::tools::createUnitPolygon()); 883 aMaskPolyPolygon.transform(getTransform()); 884 885 // create maskPrimitive with aMaskPolyPolygon and aMaskContentVector 886 const Primitive2DReference xRefB(new MaskPrimitive2D(aMaskPolyPolygon, Primitive2DSequence(&xTransformPrimitive, 1L))); 887 appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, xRefB); 888 } 889 } 890 } 891 else 892 { 893 // add to decomposition 894 appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, xPrimitive); 895 } 896 } 897 } 898 899 return aRetval; 900 } 901 902 GraphicPrimitive2D::GraphicPrimitive2D( 903 const basegfx::B2DHomMatrix& rTransform, 904 const GraphicObject& rGraphicObject, 905 const GraphicAttr& rGraphicAttr) 906 : BufferedDecompositionPrimitive2D(), 907 maTransform(rTransform), 908 maGraphicObject(rGraphicObject), 909 maGraphicAttr(rGraphicAttr) 910 { 911 } 912 913 GraphicPrimitive2D::GraphicPrimitive2D( 914 const basegfx::B2DHomMatrix& rTransform, 915 const GraphicObject& rGraphicObject) 916 : BufferedDecompositionPrimitive2D(), 917 maTransform(rTransform), 918 maGraphicObject(rGraphicObject), 919 maGraphicAttr() 920 { 921 } 922 923 bool GraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 924 { 925 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) 926 { 927 const GraphicPrimitive2D& rCompare = (GraphicPrimitive2D&)rPrimitive; 928 929 return (getTransform() == rCompare.getTransform() 930 && getGraphicObject() == rCompare.getGraphicObject() 931 && getGraphicAttr() == rCompare.getGraphicAttr()); 932 } 933 934 return false; 935 } 936 937 basegfx::B2DRange GraphicPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const 938 { 939 basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0); 940 aRetval.transform(getTransform()); 941 return aRetval; 942 } 943 944 // provide unique ID 945 ImplPrimitrive2DIDBlock(GraphicPrimitive2D, PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D) 946 947 } // end of namespace primitive2d 948 } // end of namespace drawinglayer 949 950 ////////////////////////////////////////////////////////////////////////////// 951 // eof 952