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