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_cppcanvas.hxx" 26 27 #include <canvas/debug.hxx> 28 #include <tools/diagnose_ex.h> 29 #include <canvas/verbosetrace.hxx> 30 #include <osl/mutex.hxx> 31 #include <vos/mutex.hxx> 32 #include <vcl/svapp.hxx> 33 #include <rtl/logfile.hxx> 34 #include <comphelper/sequence.hxx> 35 #include <comphelper/anytostring.hxx> 36 #include <cppuhelper/exc_hlp.hxx> 37 #include <cppcanvas/canvas.hxx> 38 #include <com/sun/star/rendering/XGraphicDevice.hpp> 39 #include <com/sun/star/rendering/TexturingMode.hpp> 40 #include <com/sun/star/uno/Sequence.hxx> 41 #include <com/sun/star/geometry/RealPoint2D.hpp> 42 #include <com/sun/star/rendering/PanoseProportion.hpp> 43 #include <com/sun/star/rendering/ViewState.hpp> 44 #include <com/sun/star/rendering/RenderState.hpp> 45 #include <com/sun/star/rendering/XCanvasFont.hpp> 46 #include <com/sun/star/rendering/XPolyPolygon2D.hpp> 47 #include <com/sun/star/rendering/XCanvas.hpp> 48 #include <com/sun/star/rendering/PathCapType.hpp> 49 #include <com/sun/star/rendering/PathJoinType.hpp> 50 #include <basegfx/tools/canvastools.hxx> 51 #include <basegfx/tools/gradienttools.hxx> 52 #include <basegfx/numeric/ftools.hxx> 53 #include <basegfx/polygon/b2dpolypolygontools.hxx> 54 #include <basegfx/polygon/b2dpolygontools.hxx> 55 #include <basegfx/polygon/b2dpolygon.hxx> 56 #include <basegfx/polygon/b2dpolypolygon.hxx> 57 #include <basegfx/matrix/b2dhommatrix.hxx> 58 #include <basegfx/vector/b2dsize.hxx> 59 #include <basegfx/range/b2drectangle.hxx> 60 #include <basegfx/point/b2dpoint.hxx> 61 #include <basegfx/tuple/b2dtuple.hxx> 62 #include <basegfx/polygon/b2dpolygonclipper.hxx> 63 #include <basegfx/polygon/b2dpolypolygoncutter.hxx> 64 #include <canvas/canvastools.hxx> 65 #include <vcl/canvastools.hxx> 66 #include <vcl/salbtype.hxx> 67 #include <vcl/gdimtf.hxx> 68 #include <vcl/metaact.hxx> 69 #include <vcl/virdev.hxx> 70 #include <vcl/metric.hxx> 71 #include <vcl/graphictools.hxx> 72 #include <tools/poly.hxx> 73 #include <i18npool/mslangid.hxx> 74 #include <implrenderer.hxx> 75 #include <tools.hxx> 76 #include <outdevstate.hxx> 77 #include <action.hxx> 78 #include <bitmapaction.hxx> 79 #include <lineaction.hxx> 80 #include <pointaction.hxx> 81 #include <polypolyaction.hxx> 82 #include <textaction.hxx> 83 #include <transparencygroupaction.hxx> 84 #include <vector> 85 #include <algorithm> 86 #include <iterator> 87 #include <boost/scoped_array.hpp> 88 #include "mtftools.hxx" 89 #include "outdevstate.hxx" 90 #include <basegfx/matrix/b2dhommatrixtools.hxx> 91 92 93 using namespace ::com::sun::star; 94 95 96 // free support functions 97 // ====================== 98 namespace 99 { 100 template < class MetaActionType > void setStateColor( MetaActionType* pAct, 101 bool& rIsColorSet, 102 uno::Sequence< double >& rColorSequence, 103 const cppcanvas::CanvasSharedPtr& rCanvas ) 104 { 105 // set rIsColorSet and check for true at the same time 106 if( (rIsColorSet=pAct->IsSetting()) != false ) 107 { 108 ::Color aColor( pAct->GetColor() ); 109 110 // force alpha part of color to 111 // opaque. transparent painting is done 112 // explicitely via META_TRANSPARENT_ACTION 113 aColor.SetTransparency(0); 114 //aColor.SetTransparency(128); 115 116 rColorSequence = ::vcl::unotools::colorToDoubleSequence( 117 aColor, 118 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() ); 119 } 120 } 121 122 123 // state stack manipulators 124 // ------------------------ 125 void clearStateStack( ::cppcanvas::internal::VectorOfOutDevStates& rStates ) 126 { 127 rStates.clear(); 128 const ::cppcanvas::internal::OutDevState aDefaultState; 129 rStates.push_back( aDefaultState ); 130 } 131 132 ::cppcanvas::internal::OutDevState& getState( ::cppcanvas::internal::VectorOfOutDevStates& rStates ) 133 { 134 return rStates.back(); 135 } 136 137 const ::cppcanvas::internal::OutDevState& getState( const ::cppcanvas::internal::VectorOfOutDevStates& rStates ) 138 { 139 return rStates.back(); 140 } 141 142 void pushState( ::cppcanvas::internal::VectorOfOutDevStates& rStates, 143 sal_uInt16 nFlags ) 144 { 145 rStates.push_back( getState( rStates ) ); 146 getState( rStates ).pushFlags = nFlags; 147 } 148 149 void popState( ::cppcanvas::internal::VectorOfOutDevStates& rStates ) 150 { 151 if( getState( rStates ).pushFlags != PUSH_ALL ) 152 { 153 // a state is pushed which is incomplete, i.e. does not 154 // restore everything to the previous stack level when 155 // popped. 156 // That means, we take the old state, and restore every 157 // OutDevState member whose flag is set, from the new to the 158 // old state. Then the new state gets overwritten by the 159 // calculated state 160 161 // preset to-be-calculated new state with old state 162 ::cppcanvas::internal::OutDevState aCalculatedNewState( getState( rStates ) ); 163 164 // selectively copy to-be-restored content over saved old 165 // state 166 rStates.pop_back(); 167 168 const ::cppcanvas::internal::OutDevState& rNewState( getState( rStates ) ); 169 170 if( (aCalculatedNewState.pushFlags & PUSH_LINECOLOR) ) 171 { 172 aCalculatedNewState.lineColor = rNewState.lineColor; 173 aCalculatedNewState.isLineColorSet = rNewState.isLineColorSet; 174 } 175 176 if( (aCalculatedNewState.pushFlags & PUSH_FILLCOLOR) ) 177 { 178 aCalculatedNewState.fillColor = rNewState.fillColor; 179 aCalculatedNewState.isFillColorSet = rNewState.isFillColorSet; 180 } 181 182 if( (aCalculatedNewState.pushFlags & PUSH_FONT) ) 183 { 184 aCalculatedNewState.xFont = rNewState.xFont; 185 aCalculatedNewState.fontRotation = rNewState.fontRotation; 186 aCalculatedNewState.textReliefStyle = rNewState.textReliefStyle; 187 aCalculatedNewState.textOverlineStyle = rNewState.textOverlineStyle; 188 aCalculatedNewState.textUnderlineStyle = rNewState.textUnderlineStyle; 189 aCalculatedNewState.textStrikeoutStyle = rNewState.textStrikeoutStyle; 190 aCalculatedNewState.textEmphasisMarkStyle = rNewState.textEmphasisMarkStyle; 191 aCalculatedNewState.isTextEffectShadowSet = rNewState.isTextEffectShadowSet; 192 aCalculatedNewState.isTextWordUnderlineSet = rNewState.isTextWordUnderlineSet; 193 aCalculatedNewState.isTextOutlineModeSet = rNewState.isTextOutlineModeSet; 194 } 195 196 if( (aCalculatedNewState.pushFlags & PUSH_TEXTCOLOR) ) 197 { 198 aCalculatedNewState.textColor = rNewState.textColor; 199 } 200 201 if( (aCalculatedNewState.pushFlags & PUSH_MAPMODE) ) 202 { 203 aCalculatedNewState.mapModeTransform = rNewState.mapModeTransform; 204 } 205 206 if( (aCalculatedNewState.pushFlags & PUSH_CLIPREGION) ) 207 { 208 aCalculatedNewState.clip = rNewState.clip; 209 aCalculatedNewState.clipRect = rNewState.clipRect; 210 aCalculatedNewState.xClipPoly = rNewState.xClipPoly; 211 } 212 213 // TODO(F2): Raster ops NYI 214 // if( (aCalculatedNewState.pushFlags & PUSH_RASTEROP) ) 215 // { 216 // } 217 218 if( (aCalculatedNewState.pushFlags & PUSH_TEXTFILLCOLOR) ) 219 { 220 aCalculatedNewState.textFillColor = rNewState.textFillColor; 221 aCalculatedNewState.isTextFillColorSet = rNewState.isTextFillColorSet; 222 } 223 224 if( (aCalculatedNewState.pushFlags & PUSH_TEXTALIGN) ) 225 { 226 aCalculatedNewState.textReferencePoint = rNewState.textReferencePoint; 227 } 228 229 // TODO(F1): Refpoint handling NYI 230 // if( (aCalculatedNewState.pushFlags & PUSH_REFPOINT) ) 231 // { 232 // } 233 234 if( (aCalculatedNewState.pushFlags & PUSH_TEXTLINECOLOR) ) 235 { 236 aCalculatedNewState.textLineColor = rNewState.textLineColor; 237 aCalculatedNewState.isTextLineColorSet = rNewState.isTextLineColorSet; 238 } 239 240 if( (aCalculatedNewState.pushFlags & PUSH_TEXTLAYOUTMODE) ) 241 { 242 aCalculatedNewState.textAlignment = rNewState.textAlignment; 243 aCalculatedNewState.textDirection = rNewState.textDirection; 244 } 245 246 // TODO(F2): Text language handling NYI 247 // if( (aCalculatedNewState.pushFlags & PUSH_TEXTLANGUAGE) ) 248 // { 249 // } 250 251 // always copy push mode 252 aCalculatedNewState.pushFlags = rNewState.pushFlags; 253 254 // flush to stack 255 getState( rStates ) = aCalculatedNewState; 256 } 257 else 258 { 259 rStates.pop_back(); 260 } 261 } 262 263 void setupStrokeAttributes( rendering::StrokeAttributes& o_rStrokeAttributes, 264 const ::cppcanvas::internal::ActionFactoryParameters& rParms, 265 const LineInfo& rLineInfo ) 266 { 267 const ::basegfx::B2DSize aWidth( rLineInfo.GetWidth(), 0 ); 268 o_rStrokeAttributes.StrokeWidth = 269 (getState( rParms.mrStates ).mapModeTransform * aWidth).getX(); 270 271 // setup reasonable defaults 272 o_rStrokeAttributes.MiterLimit = 15.0; // 1.0 was no good default; GDI+'s limit is 10.0, our's is 15.0 273 o_rStrokeAttributes.StartCapType = rendering::PathCapType::BUTT; 274 o_rStrokeAttributes.EndCapType = rendering::PathCapType::BUTT; 275 276 switch(rLineInfo.GetLineJoin()) 277 { 278 default: // B2DLINEJOIN_NONE, B2DLINEJOIN_MIDDLE 279 o_rStrokeAttributes.JoinType = rendering::PathJoinType::NONE; 280 break; 281 case basegfx::B2DLINEJOIN_BEVEL: 282 o_rStrokeAttributes.JoinType = rendering::PathJoinType::BEVEL; 283 break; 284 case basegfx::B2DLINEJOIN_MITER: 285 o_rStrokeAttributes.JoinType = rendering::PathJoinType::MITER; 286 break; 287 case basegfx::B2DLINEJOIN_ROUND: 288 o_rStrokeAttributes.JoinType = rendering::PathJoinType::ROUND; 289 break; 290 } 291 292 switch(rLineInfo.GetLineCap()) 293 { 294 default: /* com::sun::star::drawing::LineCap_BUTT */ 295 { 296 o_rStrokeAttributes.StartCapType = rendering::PathCapType::BUTT; 297 o_rStrokeAttributes.EndCapType = rendering::PathCapType::BUTT; 298 break; 299 } 300 case com::sun::star::drawing::LineCap_ROUND: 301 { 302 o_rStrokeAttributes.StartCapType = rendering::PathCapType::ROUND; 303 o_rStrokeAttributes.EndCapType = rendering::PathCapType::ROUND; 304 break; 305 } 306 case com::sun::star::drawing::LineCap_SQUARE: 307 { 308 o_rStrokeAttributes.StartCapType = rendering::PathCapType::SQUARE; 309 o_rStrokeAttributes.EndCapType = rendering::PathCapType::SQUARE; 310 break; 311 } 312 } 313 314 if( LINE_DASH == rLineInfo.GetStyle() ) 315 { 316 const ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) ); 317 318 // TODO(F1): Interpret OutDev::GetRefPoint() for the start of the dashing. 319 320 // interpret dash info only if explicitely enabled as 321 // style 322 const ::basegfx::B2DSize aDistance( rLineInfo.GetDistance(), 0 ); 323 const double nDistance( (rState.mapModeTransform * aDistance).getX() ); 324 325 const ::basegfx::B2DSize aDashLen( rLineInfo.GetDashLen(), 0 ); 326 const double nDashLen( (rState.mapModeTransform * aDashLen).getX() ); 327 328 const ::basegfx::B2DSize aDotLen( rLineInfo.GetDotLen(), 0 ); 329 const double nDotLen( (rState.mapModeTransform * aDotLen).getX() ); 330 331 const sal_Int32 nNumArryEntries( 2*rLineInfo.GetDashCount() + 332 2*rLineInfo.GetDotCount() ); 333 334 o_rStrokeAttributes.DashArray.realloc( nNumArryEntries ); 335 double* pDashArray = o_rStrokeAttributes.DashArray.getArray(); 336 337 338 // iteratively fill dash array, first with dashs, then 339 // with dots. 340 // =================================================== 341 342 sal_Int32 nCurrEntry=0; 343 344 for( sal_Int32 i=0; i<rLineInfo.GetDashCount(); ++i ) 345 { 346 pDashArray[nCurrEntry++] = nDashLen; 347 pDashArray[nCurrEntry++] = nDistance; 348 } 349 for( sal_Int32 i=0; i<rLineInfo.GetDotCount(); ++i ) 350 { 351 pDashArray[nCurrEntry++] = nDotLen; 352 pDashArray[nCurrEntry++] = nDistance; 353 } 354 } 355 } 356 357 358 /** Create masked BitmapEx, where the white areas of rBitmap are 359 transparent, and the other appear in rMaskColor. 360 */ 361 BitmapEx createMaskBmpEx( const Bitmap& rBitmap, 362 const ::Color& rMaskColor ) 363 { 364 const ::Color aWhite( COL_WHITE ); 365 BitmapPalette aBiLevelPalette(2); 366 aBiLevelPalette[0] = aWhite; 367 aBiLevelPalette[1] = rMaskColor; 368 369 Bitmap aMask( rBitmap.CreateMask( aWhite )); 370 Bitmap aSolid( rBitmap.GetSizePixel(), 371 1, 372 &aBiLevelPalette ); 373 aSolid.Erase( rMaskColor ); 374 375 return BitmapEx( aSolid, aMask ); 376 } 377 378 /** Shameless rip from vcl/source/gdi/outdev3.cxx 379 380 Should consolidate, into something like basetxt... 381 */ 382 sal_Unicode getLocalizedChar( sal_Unicode nChar, LanguageType eLang ) 383 { 384 // currently only conversion from ASCII digits is interesting 385 if( (nChar < '0') || ('9' < nChar) ) 386 return nChar; 387 388 sal_Unicode nOffset(0); 389 // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region. 390 // CAVEAT! To some like Mongolian MS assigned the same primary language 391 // although the script type is different! 392 switch( eLang & LANGUAGE_MASK_PRIMARY ) 393 { 394 default: 395 break; 396 397 case LANGUAGE_ARABIC_SAUDI_ARABIA & LANGUAGE_MASK_PRIMARY: 398 case LANGUAGE_URDU & LANGUAGE_MASK_PRIMARY: 399 case LANGUAGE_PUNJABI & LANGUAGE_MASK_PRIMARY: //??? 400 nOffset = 0x0660 - '0'; // arabic/persian/urdu 401 break; 402 case LANGUAGE_BENGALI & LANGUAGE_MASK_PRIMARY: 403 nOffset = 0x09E6 - '0'; // bengali 404 break; 405 case LANGUAGE_BURMESE & LANGUAGE_MASK_PRIMARY: 406 nOffset = 0x1040 - '0'; // burmese 407 break; 408 case LANGUAGE_HINDI & LANGUAGE_MASK_PRIMARY: 409 nOffset = 0x0966 - '0'; // devanagari 410 break; 411 case LANGUAGE_GUJARATI & LANGUAGE_MASK_PRIMARY: 412 nOffset = 0x0AE6 - '0'; // gujarati 413 break; 414 case LANGUAGE_KANNADA & LANGUAGE_MASK_PRIMARY: 415 nOffset = 0x0CE6 - '0'; // kannada 416 break; 417 case LANGUAGE_KHMER & LANGUAGE_MASK_PRIMARY: 418 nOffset = 0x17E0 - '0'; // khmer 419 break; 420 case LANGUAGE_LAO & LANGUAGE_MASK_PRIMARY: 421 nOffset = 0x0ED0 - '0'; // lao 422 break; 423 case LANGUAGE_MALAYALAM & LANGUAGE_MASK_PRIMARY: 424 nOffset = 0x0D66 - '0'; // malayalam 425 break; 426 case LANGUAGE_MONGOLIAN & LANGUAGE_MASK_PRIMARY: 427 if (eLang == LANGUAGE_MONGOLIAN_MONGOLIAN) 428 nOffset = 0x1810 - '0'; // mongolian 429 else 430 nOffset = 0; // mongolian cyrillic 431 break; 432 case LANGUAGE_ORIYA & LANGUAGE_MASK_PRIMARY: 433 nOffset = 0x0B66 - '0'; // oriya 434 break; 435 case LANGUAGE_TAMIL & LANGUAGE_MASK_PRIMARY: 436 nOffset = 0x0BE7 - '0'; // tamil 437 break; 438 case LANGUAGE_TELUGU & LANGUAGE_MASK_PRIMARY: 439 nOffset = 0x0C66 - '0'; // telugu 440 break; 441 case LANGUAGE_THAI & LANGUAGE_MASK_PRIMARY: 442 nOffset = 0x0E50 - '0'; // thai 443 break; 444 case LANGUAGE_TIBETAN & LANGUAGE_MASK_PRIMARY: 445 nOffset = 0x0F20 - '0'; // tibetan 446 break; 447 } 448 449 nChar = sal::static_int_cast<sal_Unicode>(nChar + nOffset); 450 return nChar; 451 } 452 453 void convertToLocalizedNumerals( XubString& rStr, 454 LanguageType eTextLanguage ) 455 { 456 const sal_Unicode* pBase = rStr.GetBuffer(); 457 const sal_Unicode* pBegin = pBase + 0; 458 const xub_StrLen nEndIndex = rStr.Len(); 459 const sal_Unicode* pEnd = pBase + nEndIndex; 460 461 for( ; pBegin < pEnd; ++pBegin ) 462 { 463 // TODO: are there non-digit localizations? 464 if( (*pBegin >= '0') && (*pBegin <= '9') ) 465 { 466 // translate characters to local preference 467 sal_Unicode cChar = getLocalizedChar( *pBegin, eTextLanguage ); 468 if( cChar != *pBegin ) 469 rStr.SetChar( sal::static_int_cast<sal_uInt16>(pBegin - pBase), cChar ); 470 } 471 } 472 } 473 } 474 475 476 namespace cppcanvas 477 { 478 namespace internal 479 { 480 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolyPolygon& rPolyPoly, 481 const ActionFactoryParameters& rParms ) 482 { 483 const OutDevState& rState( getState( rParms.mrStates ) ); 484 if( (!rState.isLineColorSet && 485 !rState.isFillColorSet) || 486 (rState.lineColor.getLength() == 0 && 487 rState.fillColor.getLength() == 0) ) 488 { 489 return false; 490 } 491 492 ActionSharedPtr pPolyAction( 493 internal::PolyPolyActionFactory::createPolyPolyAction( 494 rPolyPoly, rParms.mrCanvas, rState ) ); 495 496 if( pPolyAction ) 497 { 498 maActions.push_back( 499 MtfAction( 500 pPolyAction, 501 rParms.mrCurrActionIndex ) ); 502 503 rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1; 504 } 505 506 return true; 507 } 508 509 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolygon& rPoly, 510 const ActionFactoryParameters& rParms ) 511 { 512 return createFillAndStroke( ::basegfx::B2DPolyPolygon( rPoly ), 513 rParms ); 514 } 515 516 void ImplRenderer::skipContent( GDIMetaFile& rMtf, 517 const char* pCommentString, 518 sal_Int32& io_rCurrActionIndex ) const 519 { 520 ENSURE_OR_THROW( pCommentString, 521 "ImplRenderer::skipContent(): NULL string given" ); 522 523 MetaAction* pCurrAct; 524 while( (pCurrAct=rMtf.NextAction()) != NULL ) 525 { 526 // increment action index, we've skipped an action. 527 ++io_rCurrActionIndex; 528 529 if( pCurrAct->GetType() == META_COMMENT_ACTION && 530 static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii( 531 pCommentString ) == COMPARE_EQUAL ) 532 { 533 // requested comment found, done 534 return; 535 } 536 } 537 538 // EOF 539 return; 540 } 541 542 bool ImplRenderer::isActionContained( GDIMetaFile& rMtf, 543 const char* pCommentString, 544 sal_uInt16 nType ) const 545 { 546 ENSURE_OR_THROW( pCommentString, 547 "ImplRenderer::isActionContained(): NULL string given" ); 548 549 bool bRet( false ); 550 551 // at least _one_ call to GDIMetaFile::NextAction() is 552 // executed 553 sal_uIntPtr nPos( 1 ); 554 555 MetaAction* pCurrAct; 556 while( (pCurrAct=rMtf.NextAction()) != NULL ) 557 { 558 if( pCurrAct->GetType() == nType ) 559 { 560 bRet = true; // action type found 561 break; 562 } 563 564 if( pCurrAct->GetType() == META_COMMENT_ACTION && 565 static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii( 566 pCommentString ) == COMPARE_EQUAL ) 567 { 568 // delimiting end comment found, done 569 bRet = false; // not yet found 570 break; 571 } 572 573 ++nPos; 574 } 575 576 // rewind metafile to previous position (this method must 577 // not change the current metaaction) 578 while( nPos-- ) 579 rMtf.WindPrev(); 580 581 if( !pCurrAct ) 582 { 583 // EOF, and not yet found 584 bRet = false; 585 } 586 587 return bRet; 588 } 589 590 void ImplRenderer::createGradientAction( const ::PolyPolygon& rPoly, 591 const ::Gradient& rGradient, 592 const ActionFactoryParameters& rParms, 593 bool bIsPolygonRectangle, 594 bool bSubsettableActions ) 595 { 596 DBG_TESTSOLARMUTEX(); 597 598 ::basegfx::B2DPolyPolygon aDevicePoly( rPoly.getB2DPolyPolygon() ); 599 aDevicePoly.transform( getState( rParms.mrStates ).mapModeTransform ); 600 601 // decide, whether this gradient can be rendered natively 602 // by the canvas, or must be emulated via VCL gradient 603 // action extraction. 604 const sal_uInt16 nSteps( rGradient.GetSteps() ); 605 606 if( // step count is infinite, can use native canvas 607 // gradients here 608 nSteps == 0 || 609 // step count is sufficiently high, such that no 610 // discernible difference should be visible. 611 nSteps > 64 ) 612 { 613 uno::Reference< lang::XMultiServiceFactory> xFactory( 614 rParms.mrCanvas->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() ); 615 616 if( xFactory.is() ) 617 { 618 rendering::Texture aTexture; 619 620 aTexture.RepeatModeX = rendering::TexturingMode::CLAMP; 621 aTexture.RepeatModeY = rendering::TexturingMode::CLAMP; 622 aTexture.Alpha = 1.0; 623 624 625 // setup start/end color values 626 // ---------------------------- 627 628 // scale color coefficients with gradient intensities 629 const sal_uInt16 nStartIntensity( rGradient.GetStartIntensity() ); 630 ::Color aVCLStartColor( rGradient.GetStartColor() ); 631 aVCLStartColor.SetRed( (sal_uInt8)(aVCLStartColor.GetRed() * nStartIntensity / 100) ); 632 aVCLStartColor.SetGreen( (sal_uInt8)(aVCLStartColor.GetGreen() * nStartIntensity / 100) ); 633 aVCLStartColor.SetBlue( (sal_uInt8)(aVCLStartColor.GetBlue() * nStartIntensity / 100) ); 634 635 const sal_uInt16 nEndIntensity( rGradient.GetEndIntensity() ); 636 ::Color aVCLEndColor( rGradient.GetEndColor() ); 637 aVCLEndColor.SetRed( (sal_uInt8)(aVCLEndColor.GetRed() * nEndIntensity / 100) ); 638 aVCLEndColor.SetGreen( (sal_uInt8)(aVCLEndColor.GetGreen() * nEndIntensity / 100) ); 639 aVCLEndColor.SetBlue( (sal_uInt8)(aVCLEndColor.GetBlue() * nEndIntensity / 100) ); 640 641 uno::Reference<rendering::XColorSpace> xColorSpace( 642 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace()); 643 const uno::Sequence< double > aStartColor( 644 ::vcl::unotools::colorToDoubleSequence( aVCLStartColor, 645 xColorSpace )); 646 const uno::Sequence< double > aEndColor( 647 ::vcl::unotools::colorToDoubleSequence( aVCLEndColor, 648 xColorSpace )); 649 650 uno::Sequence< uno::Sequence < double > > aColors(2); 651 uno::Sequence< double > aStops(2); 652 653 if( rGradient.GetStyle() == GRADIENT_AXIAL ) 654 { 655 aStops.realloc(3); 656 aColors.realloc(3); 657 658 aStops[0] = 0.0; 659 aStops[1] = 0.5; 660 aStops[2] = 1.0; 661 662 aColors[0] = aEndColor; 663 aColors[1] = aStartColor; 664 aColors[2] = aEndColor; 665 } 666 else 667 { 668 aStops[0] = 0.0; 669 aStops[1] = 1.0; 670 671 aColors[0] = aStartColor; 672 aColors[1] = aEndColor; 673 } 674 675 const ::basegfx::B2DRectangle aBounds( 676 ::basegfx::tools::getRange(aDevicePoly) ); 677 const ::basegfx::B2DVector aOffset( 678 rGradient.GetOfsX() / 100.0, 679 rGradient.GetOfsY() / 100.0); 680 double fRotation( rGradient.GetAngle() * M_PI / 1800.0 ); 681 const double fBorder( rGradient.GetBorder() / 100.0 ); 682 683 basegfx::B2DHomMatrix aRot90; 684 aRot90.rotate(M_PI_2); 685 686 basegfx::ODFGradientInfo aGradInfo; 687 rtl::OUString aGradientService; 688 switch( rGradient.GetStyle() ) 689 { 690 case GRADIENT_LINEAR: 691 basegfx::tools::createLinearODFGradientInfo(aGradInfo, 692 aBounds, 693 nSteps, 694 fBorder, 695 fRotation); 696 // map odf to svg gradient orientation - x 697 // instead of y direction 698 aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aRot90; 699 aGradientService = rtl::OUString::createFromAscii("LinearGradient"); 700 break; 701 702 case GRADIENT_AXIAL: 703 { 704 // Adapt the border so that it is suitable 705 // for the axial gradient. An axial 706 // gradient consists of two linear 707 // gradients. Each of those covers half 708 // of the total size. In order to 709 // compensate for the condensed display of 710 // the linear gradients, we have to 711 // enlarge the area taken up by the actual 712 // gradient (1-fBorder). After that we 713 // have to turn the result back into a 714 // border value, hence the second (left 715 // most 1-... 716 const double fAxialBorder (1-2*(1-fBorder)); 717 basegfx::tools::createAxialODFGradientInfo(aGradInfo, 718 aBounds, 719 nSteps, 720 fAxialBorder, 721 fRotation); 722 // map odf to svg gradient orientation - x 723 // instead of y direction 724 aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aRot90; 725 726 // map odf axial gradient to 3-stop linear 727 // gradient - shift left by 0.5 728 basegfx::B2DHomMatrix aShift; 729 aShift.translate(-0.5,0); 730 aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aShift; 731 732 aGradientService = rtl::OUString::createFromAscii("LinearGradient"); 733 break; 734 } 735 736 case GRADIENT_RADIAL: 737 basegfx::tools::createRadialODFGradientInfo(aGradInfo, 738 aBounds, 739 aOffset, 740 nSteps, 741 fBorder); 742 aGradientService = rtl::OUString::createFromAscii("EllipticalGradient"); 743 break; 744 745 case GRADIENT_ELLIPTICAL: 746 basegfx::tools::createEllipticalODFGradientInfo(aGradInfo, 747 aBounds, 748 aOffset, 749 nSteps, 750 fBorder, 751 fRotation); 752 aGradientService = rtl::OUString::createFromAscii("EllipticalGradient"); 753 break; 754 755 case GRADIENT_SQUARE: 756 basegfx::tools::createSquareODFGradientInfo(aGradInfo, 757 aBounds, 758 aOffset, 759 nSteps, 760 fBorder, 761 fRotation); 762 aGradientService = rtl::OUString::createFromAscii("RectangularGradient"); 763 break; 764 765 case GRADIENT_RECT: 766 basegfx::tools::createRectangularODFGradientInfo(aGradInfo, 767 aBounds, 768 aOffset, 769 nSteps, 770 fBorder, 771 fRotation); 772 aGradientService = rtl::OUString::createFromAscii("RectangularGradient"); 773 break; 774 775 default: 776 ENSURE_OR_THROW( false, 777 "ImplRenderer::createGradientAction(): Unexpected gradient type" ); 778 break; 779 } 780 781 // As the texture coordinate space is relative to 782 // the polygon coordinate space (NOT to the 783 // polygon itself), move gradient to the start of 784 // the actual polygon. If we skip this, the 785 // gradient will always display at the origin, and 786 // not within the polygon bound (which might be 787 // miles away from the origin). 788 aGradInfo.maTextureTransform.translate( aBounds.getMinX(), 789 aBounds.getMinY() ); 790 ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform, 791 aGradInfo.maTextureTransform ); 792 793 uno::Sequence<uno::Any> args(3); 794 beans::PropertyValue aProp; 795 aProp.Name = rtl::OUString::createFromAscii("Colors"); 796 aProp.Value <<= aColors; 797 args[0] <<= aProp; 798 aProp.Name = rtl::OUString::createFromAscii("Stops"); 799 aProp.Value <<= aStops; 800 args[1] <<= aProp; 801 aProp.Name = rtl::OUString::createFromAscii("AspectRatio"); 802 aProp.Value <<= aGradInfo.mfAspectRatio; 803 args[2] <<= aProp; 804 805 aTexture.Gradient.set( 806 xFactory->createInstanceWithArguments(aGradientService, 807 args), 808 uno::UNO_QUERY); 809 if( aTexture.Gradient.is() ) 810 { 811 ActionSharedPtr pPolyAction( 812 internal::PolyPolyActionFactory::createPolyPolyAction( 813 aDevicePoly, 814 rParms.mrCanvas, 815 getState( rParms.mrStates ), 816 aTexture ) ); 817 818 if( pPolyAction ) 819 { 820 maActions.push_back( 821 MtfAction( 822 pPolyAction, 823 rParms.mrCurrActionIndex ) ); 824 825 rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1; 826 } 827 828 // done, using native gradients 829 return; 830 } 831 } 832 } 833 834 // cannot currently use native canvas gradients, as a 835 // finite step size is given (this funny feature is not 836 // supported by the XCanvas API) 837 pushState( rParms.mrStates, PUSH_ALL ); 838 839 if( !bIsPolygonRectangle ) 840 { 841 // only clip, if given polygon is not a rectangle in 842 // the first place (the gradient is always limited to 843 // the given bound rect) 844 updateClipping( 845 aDevicePoly, 846 rParms, 847 true ); 848 } 849 850 GDIMetaFile aTmpMtf; 851 rParms.mrVDev.AddGradientActions( rPoly.GetBoundRect(), 852 rGradient, 853 aTmpMtf ); 854 855 createActions( aTmpMtf, rParms, bSubsettableActions ); 856 857 popState( rParms.mrStates ); 858 } 859 860 uno::Reference< rendering::XCanvasFont > ImplRenderer::createFont( double& o_rFontRotation, 861 const ::Font& rFont, 862 const ActionFactoryParameters& rParms ) const 863 { 864 rendering::FontRequest aFontRequest; 865 866 if( rParms.mrParms.maFontName.is_initialized() ) 867 aFontRequest.FontDescription.FamilyName = *rParms.mrParms.maFontName; 868 else 869 aFontRequest.FontDescription.FamilyName = rFont.GetName(); 870 871 aFontRequest.FontDescription.StyleName = rFont.GetStyleName(); 872 873 aFontRequest.FontDescription.IsSymbolFont = (rFont.GetCharSet() == RTL_TEXTENCODING_SYMBOL) ? util::TriState_YES : util::TriState_NO; 874 aFontRequest.FontDescription.IsVertical = rFont.IsVertical() ? util::TriState_YES : util::TriState_NO; 875 876 // TODO(F2): improve vclenum->panose conversion 877 aFontRequest.FontDescription.FontDescription.Weight = 878 rParms.mrParms.maFontWeight.is_initialized() ? 879 *rParms.mrParms.maFontWeight : 880 ::canvas::tools::numeric_cast<sal_Int8>( ::basegfx::fround( rFont.GetWeight() ) ); 881 aFontRequest.FontDescription.FontDescription.Letterform = 882 rParms.mrParms.maFontLetterForm.is_initialized() ? 883 *rParms.mrParms.maFontLetterForm : 884 (rFont.GetItalic() == ITALIC_NONE) ? 0 : 9; 885 aFontRequest.FontDescription.FontDescription.Proportion = 886 rParms.mrParms.maFontProportion.is_initialized() ? 887 *rParms.mrParms.maFontProportion : 888 (rFont.GetPitch() == PITCH_FIXED) 889 ? rendering::PanoseProportion::MONO_SPACED 890 : rendering::PanoseProportion::ANYTHING; 891 892 LanguageType aLang = rFont.GetLanguage(); 893 aFontRequest.Locale = MsLangId::convertLanguageToLocale(aLang, false); 894 895 // setup state-local text transformation, 896 // if the font be rotated 897 const short nFontAngle( rFont.GetOrientation() ); 898 if( nFontAngle != 0 ) 899 { 900 // set to unity transform rotated by font angle 901 const double nAngle( nFontAngle * (F_PI / 1800.0) ); 902 o_rFontRotation = -nAngle; 903 } 904 else 905 { 906 o_rFontRotation = 0.0; 907 } 908 909 geometry::Matrix2D aFontMatrix; 910 ::canvas::tools::setIdentityMatrix2D( aFontMatrix ); 911 912 // TODO(F2): use correct scale direction, font 913 // height might be width or anything else 914 915 // TODO(Q3): This code smells of programming by 916 // coincidence (the next two if statements) 917 const ::Size rFontSizeLog( rFont.GetSize() ); 918 const sal_Int32 nFontWidthLog = rFontSizeLog.Width(); 919 if( nFontWidthLog != 0 ) 920 { 921 ::Font aTestFont = rFont; 922 aTestFont.SetWidth( 0 ); 923 sal_Int32 nNormalWidth = rParms.mrVDev.GetFontMetric( aTestFont ).GetWidth(); 924 if( nNormalWidth != nFontWidthLog ) 925 if( nNormalWidth ) 926 aFontMatrix.m00 = (double)nFontWidthLog / nNormalWidth; 927 } 928 929 // #i52608# apply map mode scale also to font matrix - an 930 // anisotrophic mapmode must be reflected in an 931 // anisotrophic font matrix scale. 932 const OutDevState& rState( getState( rParms.mrStates ) ); 933 if( !::basegfx::fTools::equal( 934 rState.mapModeTransform.get(0,0), 935 rState.mapModeTransform.get(1,1)) ) 936 { 937 const double nScaleX( rState.mapModeTransform.get(0,0) ); 938 const double nScaleY( rState.mapModeTransform.get(1,1) ); 939 940 // note: no reason to check for division by zero, we 941 // always have the value closer (or equal) to zero as 942 // the nominator. 943 if( fabs(nScaleX) < fabs(nScaleY) ) 944 aFontMatrix.m00 *= nScaleX / nScaleY; 945 else 946 aFontMatrix.m11 *= nScaleY / nScaleX; 947 } 948 aFontRequest.CellSize = (rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize(rFontSizeLog)).getY(); 949 950 return rParms.mrCanvas->getUNOCanvas()->createFont( aFontRequest, 951 uno::Sequence< beans::PropertyValue >(), 952 aFontMatrix ); 953 } 954 955 // create text effects such as shadow/relief/embossed 956 void ImplRenderer::createTextAction( const ::Point& rStartPoint, 957 const String rString, 958 int nIndex, 959 int nLength, 960 const sal_Int32* pCharWidths, 961 const ActionFactoryParameters& rParms, 962 bool bSubsettableActions ) 963 { 964 ENSURE_OR_THROW( nIndex >= 0 && nLength <= rString.Len() + nIndex, 965 "ImplRenderer::createTextWithEffectsAction(): Invalid text index" ); 966 967 if( !nLength ) 968 return; // zero-length text, no visible output 969 970 const OutDevState& rState( getState( rParms.mrStates ) ); 971 972 // TODO(F2): implement all text effects 973 // if( rState.textAlignment ); // TODO(F2): NYI 974 975 ::Color aShadowColor( COL_AUTO ); 976 ::Color aReliefColor( COL_AUTO ); 977 ::Size aShadowOffset; 978 ::Size aReliefOffset; 979 980 uno::Reference<rendering::XColorSpace> xColorSpace( 981 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() ); 982 983 if( rState.isTextEffectShadowSet ) 984 { 985 // calculate shadow offset (similar to outdev3.cxx) 986 // TODO(F3): better match with outdev3.cxx 987 sal_Int32 nShadowOffset = static_cast<sal_Int32>(1.5 + ((rParms.mrVDev.GetFont().GetHeight()-24.0)/24.0)); 988 if( nShadowOffset < 1 ) 989 nShadowOffset = 1; 990 991 aShadowOffset.setWidth( nShadowOffset ); 992 aShadowOffset.setHeight( nShadowOffset ); 993 994 // determine shadow color (from outdev3.cxx) 995 ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor( 996 rState.textColor, xColorSpace ); 997 bool bIsDark = (aTextColor.GetColor() == COL_BLACK) 998 || (aTextColor.GetLuminance() < 8); 999 1000 aShadowColor = bIsDark ? COL_LIGHTGRAY : COL_BLACK; 1001 aShadowColor.SetTransparency( aTextColor.GetTransparency() ); 1002 } 1003 1004 if( rState.textReliefStyle ) 1005 { 1006 // calculate relief offset (similar to outdev3.cxx) 1007 sal_Int32 nReliefOffset = rParms.mrVDev.PixelToLogic( Size( 1, 1 ) ).Height(); 1008 nReliefOffset += nReliefOffset/2; 1009 if( nReliefOffset < 1 ) 1010 nReliefOffset = 1; 1011 1012 if( rState.textReliefStyle == RELIEF_ENGRAVED ) 1013 nReliefOffset = -nReliefOffset; 1014 1015 aReliefOffset.setWidth( nReliefOffset ); 1016 aReliefOffset.setHeight( nReliefOffset ); 1017 1018 // determine relief color (from outdev3.cxx) 1019 ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor( 1020 rState.textColor, xColorSpace ); 1021 1022 aReliefColor = ::Color( COL_LIGHTGRAY ); 1023 1024 // we don't have a automatic color, so black is always 1025 // drawn on white (literally copied from 1026 // vcl/source/gdi/outdev3.cxx) 1027 if( aTextColor.GetColor() == COL_BLACK ) 1028 { 1029 aTextColor = ::Color( COL_WHITE ); 1030 getState( rParms.mrStates ).textColor = 1031 ::vcl::unotools::colorToDoubleSequence( 1032 aTextColor, xColorSpace ); 1033 } 1034 1035 if( aTextColor.GetColor() == COL_WHITE ) 1036 aReliefColor = ::Color( COL_BLACK ); 1037 aReliefColor.SetTransparency( aTextColor.GetTransparency() ); 1038 } 1039 1040 // create the actual text action 1041 ActionSharedPtr pTextAction( 1042 TextActionFactory::createTextAction( 1043 rStartPoint, 1044 aReliefOffset, 1045 aReliefColor, 1046 aShadowOffset, 1047 aShadowColor, 1048 rString, 1049 nIndex, 1050 nLength, 1051 pCharWidths, 1052 rParms.mrVDev, 1053 rParms.mrCanvas, 1054 rState, 1055 rParms.mrParms, 1056 bSubsettableActions ) ); 1057 1058 ActionSharedPtr pStrikeoutTextAction; 1059 1060 if ( rState.textStrikeoutStyle == STRIKEOUT_X || rState.textStrikeoutStyle == STRIKEOUT_SLASH ) 1061 { 1062 long nWidth = rParms.mrVDev.GetTextWidth( rString,nIndex,nLength ); 1063 1064 xub_Unicode pChars[5]; 1065 if ( rState.textStrikeoutStyle == STRIKEOUT_X ) 1066 pChars[0] = 'X'; 1067 else 1068 pChars[0] = '/'; 1069 pChars[3]=pChars[2]=pChars[1]=pChars[0]; 1070 1071 long nStrikeoutWidth = nWidth; 1072 String aStrikeoutTest( pChars, 4 ); 1073 1074 if( aStrikeoutTest.Len() ) 1075 { 1076 nStrikeoutWidth = ( rParms.mrVDev.GetTextWidth( aStrikeoutTest ) + 2 ) / 4; 1077 aStrikeoutTest.Erase(); 1078 1079 if( nStrikeoutWidth <= 0 ) 1080 nStrikeoutWidth = 1; 1081 } 1082 1083 long nMaxWidth = nStrikeoutWidth/2; 1084 if ( nMaxWidth < 2 ) 1085 nMaxWidth = 2; 1086 nMaxWidth += nWidth + 1; 1087 1088 long nFullStrikeoutWidth = 0; 1089 String aStrikeoutText( pChars, 0 ); 1090 while( (nFullStrikeoutWidth+=nStrikeoutWidth ) < nMaxWidth+1 ) 1091 aStrikeoutText += pChars[0]; 1092 1093 1094 sal_Int32 nStartPos = 0; 1095 xub_StrLen nLen = aStrikeoutText.Len(); 1096 1097 if( nLen ) 1098 { 1099 long nInterval = ( nWidth - nStrikeoutWidth * nLen ) / nLen; 1100 nStrikeoutWidth += nInterval; 1101 sal_Int32* pStrikeoutCharWidths = new sal_Int32[nLen]; 1102 1103 for ( int i = 0;i<nLen; i++) 1104 { 1105 pStrikeoutCharWidths[i] = nStrikeoutWidth; 1106 } 1107 1108 for ( int i = 1;i< nLen; i++ ) 1109 { 1110 pStrikeoutCharWidths[ i ] += pStrikeoutCharWidths[ i-1 ]; 1111 } 1112 1113 pStrikeoutTextAction = 1114 TextActionFactory::createTextAction( 1115 rStartPoint, 1116 aReliefOffset, 1117 aReliefColor, 1118 aShadowOffset, 1119 aShadowColor, 1120 aStrikeoutText, 1121 nStartPos, 1122 aStrikeoutText.Len(), 1123 pStrikeoutCharWidths, 1124 rParms.mrVDev, 1125 rParms.mrCanvas, 1126 rState, 1127 rParms.mrParms, 1128 bSubsettableActions ) ; 1129 } 1130 } 1131 1132 if( pTextAction ) 1133 { 1134 maActions.push_back( 1135 MtfAction( 1136 pTextAction, 1137 rParms.mrCurrActionIndex ) ); 1138 1139 if ( pStrikeoutTextAction ) 1140 { 1141 maActions.push_back( 1142 MtfAction( 1143 pStrikeoutTextAction, 1144 rParms.mrCurrActionIndex ) ); 1145 } 1146 1147 rParms.mrCurrActionIndex += pTextAction->getActionCount()-1; 1148 } 1149 } 1150 1151 void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon& rClipPoly, 1152 const ActionFactoryParameters& rParms, 1153 bool bIntersect ) 1154 { 1155 ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) ); 1156 ::basegfx::B2DPolyPolygon aClipPoly( rClipPoly ); 1157 1158 const bool bEmptyClipRect( rState.clipRect.IsEmpty() ); 1159 const bool bEmptyClipPoly( rState.clip.count() == 0 ); 1160 1161 ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect, 1162 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" ); 1163 1164 if( !bIntersect || 1165 (bEmptyClipRect && bEmptyClipPoly) ) 1166 { 1167 rState.clip = rClipPoly; 1168 } 1169 else 1170 { 1171 if( !bEmptyClipRect ) 1172 { 1173 // TODO(P3): Use Liang-Barsky polygon clip here, 1174 // after all, one object is just a rectangle! 1175 1176 // convert rect to polygon beforehand, must revert 1177 // to general polygon clipping here. 1178 rState.clip = ::basegfx::B2DPolyPolygon( 1179 ::basegfx::tools::createPolygonFromRect( 1180 // #121100# VCL rectangular clips always 1181 // include one more pixel to the right 1182 // and the bottom 1183 ::basegfx::B2DRectangle( rState.clipRect.Left(), 1184 rState.clipRect.Top(), 1185 rState.clipRect.Right()+1, 1186 rState.clipRect.Bottom()+1 ) ) ); 1187 } 1188 1189 // AW: Simplified 1190 rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon( 1191 aClipPoly, rState.clip, true, false); 1192 } 1193 1194 // by now, our clip resides in the OutDevState::clip 1195 // poly-polygon. 1196 rState.clipRect.SetEmpty(); 1197 1198 if( rState.clip.count() == 0 ) 1199 { 1200 if( rState.clipRect.IsEmpty() ) 1201 { 1202 rState.xClipPoly.clear(); 1203 } 1204 else 1205 { 1206 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 1207 rParms.mrCanvas->getUNOCanvas()->getDevice(), 1208 ::basegfx::B2DPolyPolygon( 1209 ::basegfx::tools::createPolygonFromRect( 1210 // #121100# VCL rectangular clips 1211 // always include one more pixel to 1212 // the right and the bottom 1213 ::basegfx::B2DRectangle( rState.clipRect.Left(), 1214 rState.clipRect.Top(), 1215 rState.clipRect.Right()+1, 1216 rState.clipRect.Bottom()+1 ) ) ) ); 1217 } 1218 } 1219 else 1220 { 1221 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 1222 rParms.mrCanvas->getUNOCanvas()->getDevice(), 1223 rState.clip ); 1224 } 1225 } 1226 1227 void ImplRenderer::updateClipping( const ::Rectangle& rClipRect, 1228 const ActionFactoryParameters& rParms, 1229 bool bIntersect ) 1230 { 1231 ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) ); 1232 1233 const bool bEmptyClipRect( rState.clipRect.IsEmpty() ); 1234 const bool bEmptyClipPoly( rState.clip.count() == 0 ); 1235 1236 ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect, 1237 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" ); 1238 1239 if( !bIntersect || 1240 (bEmptyClipRect && bEmptyClipPoly) ) 1241 { 1242 rState.clipRect = rClipRect; 1243 rState.clip.clear(); 1244 } 1245 else if( bEmptyClipPoly ) 1246 { 1247 rState.clipRect.Intersection( rClipRect ); 1248 rState.clip.clear(); 1249 } 1250 else 1251 { 1252 // TODO(P3): Handle a fourth case here, when all clip 1253 // polygons are rectangular, once B2DMultiRange's 1254 // sweep line implementation is done. 1255 1256 // general case: convert to polygon and clip 1257 // ----------------------------------------- 1258 1259 // convert rect to polygon beforehand, must revert 1260 // to general polygon clipping here. 1261 ::basegfx::B2DPolyPolygon aClipPoly( 1262 ::basegfx::tools::createPolygonFromRect( 1263 ::basegfx::B2DRectangle( rClipRect.Left(), 1264 rClipRect.Top(), 1265 rClipRect.Right(), 1266 rClipRect.Bottom() ) ) ); 1267 1268 rState.clipRect.SetEmpty(); 1269 1270 // AW: Simplified 1271 rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon( 1272 aClipPoly, rState.clip, true, false); 1273 } 1274 1275 if( rState.clip.count() == 0 ) 1276 { 1277 if( rState.clipRect.IsEmpty() ) 1278 { 1279 rState.xClipPoly.clear(); 1280 } 1281 else 1282 { 1283 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 1284 rParms.mrCanvas->getUNOCanvas()->getDevice(), 1285 ::basegfx::B2DPolyPolygon( 1286 ::basegfx::tools::createPolygonFromRect( 1287 // #121100# VCL rectangular clips 1288 // always include one more pixel to 1289 // the right and the bottom 1290 ::basegfx::B2DRectangle( rState.clipRect.Left(), 1291 rState.clipRect.Top(), 1292 rState.clipRect.Right()+1, 1293 rState.clipRect.Bottom()+1 ) ) ) ); 1294 } 1295 } 1296 else 1297 { 1298 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 1299 rParms.mrCanvas->getUNOCanvas()->getDevice(), 1300 rState.clip ); 1301 } 1302 } 1303 1304 bool ImplRenderer::createActions( GDIMetaFile& rMtf, 1305 const ActionFactoryParameters& rFactoryParms, 1306 bool bSubsettableActions ) 1307 { 1308 /* TODO(P2): interpret mtf-comments 1309 ================================ 1310 1311 - gradient fillings (do that via comments) 1312 1313 - think about mapping. _If_ we do everything in logical 1314 coordinates (which would solve the probs for stroke 1315 widths and text offsets), then we would have to 1316 recalc scaling for every drawing operation. This is 1317 because the outdev map mode might change at any time. 1318 Also keep in mind, that, although we've double precision 1319 float arithmetic now, different offsets might still 1320 generate different roundings (aka 1321 'OutputDevice::SetPixelOffset()) 1322 1323 */ 1324 1325 // alias common parameters 1326 VectorOfOutDevStates& rStates(rFactoryParms.mrStates); 1327 const CanvasSharedPtr& rCanvas(rFactoryParms.mrCanvas); 1328 ::VirtualDevice& rVDev(rFactoryParms.mrVDev); 1329 const Parameters& rParms(rFactoryParms.mrParms); 1330 sal_Int32& io_rCurrActionIndex(rFactoryParms.mrCurrActionIndex); 1331 1332 1333 // Loop over every metaaction 1334 // ========================== 1335 MetaAction* pCurrAct; 1336 1337 // TODO(P1): think about caching 1338 for( pCurrAct=rMtf.FirstAction(); 1339 pCurrAct; 1340 pCurrAct = rMtf.NextAction() ) 1341 { 1342 // execute every action, to keep VDev state up-to-date 1343 // currently used only for 1344 // - the map mode 1345 // - the line/fill color when processing a META_TRANSPARENT_ACTION 1346 // - SetFont to process font metric specific actions 1347 pCurrAct->Execute( &rVDev ); 1348 1349 switch( pCurrAct->GetType() ) 1350 { 1351 // ------------------------------------------------------------ 1352 1353 // In the first part of this monster-switch, we 1354 // handle all state-changing meta actions. These 1355 // are all handled locally. 1356 1357 // ------------------------------------------------------------ 1358 1359 case META_PUSH_ACTION: 1360 { 1361 MetaPushAction* pPushAction = static_cast<MetaPushAction*>(pCurrAct); 1362 pushState( rStates, 1363 pPushAction->GetFlags() ); 1364 } 1365 break; 1366 1367 case META_POP_ACTION: 1368 popState( rStates ); 1369 break; 1370 1371 case META_TEXTLANGUAGE_ACTION: 1372 // FALLTHROUGH intended 1373 case META_REFPOINT_ACTION: 1374 // handled via pCurrAct->Execute( &rVDev ) 1375 break; 1376 1377 case META_MAPMODE_ACTION: 1378 // modify current mapModeTransformation 1379 // transformation, such that subsequent 1380 // coordinates map correctly 1381 tools::calcLogic2PixelAffineTransform( getState( rStates ).mapModeTransform, 1382 rVDev ); 1383 break; 1384 1385 // monitor clip regions, to assemble clip polygon on our own 1386 case META_CLIPREGION_ACTION: 1387 { 1388 MetaClipRegionAction* pClipAction = static_cast<MetaClipRegionAction*>(pCurrAct); 1389 1390 if( !pClipAction->IsClipping() ) 1391 { 1392 // clear clipping 1393 getState( rStates ).clip.clear(); 1394 } 1395 else 1396 { 1397 if( !pClipAction->GetRegion().HasPolyPolygon() ) 1398 { 1399 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip " 1400 "region encountered, falling back to bounding box!" ); 1401 1402 // #121806# explicitely kept integer 1403 Rectangle aClipRect( 1404 rVDev.LogicToPixel( 1405 pClipAction->GetRegion().GetBoundRect() ) ); 1406 1407 // intersect current clip with given rect 1408 updateClipping( 1409 aClipRect, 1410 rFactoryParms, 1411 false ); 1412 } 1413 else 1414 { 1415 // set new clip polygon (don't intersect 1416 // with old one, just set it) 1417 1418 // #121806# explicitely kept integer 1419 updateClipping( 1420 rVDev.LogicToPixel( 1421 pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(), 1422 rFactoryParms, 1423 false ); 1424 } 1425 } 1426 1427 break; 1428 } 1429 1430 case META_ISECTRECTCLIPREGION_ACTION: 1431 { 1432 MetaISectRectClipRegionAction* pClipAction = static_cast<MetaISectRectClipRegionAction*>(pCurrAct); 1433 1434 // #121806# explicitely kept integer 1435 Rectangle aClipRect( 1436 rVDev.LogicToPixel( pClipAction->GetRect() ) ); 1437 1438 // intersect current clip with given rect 1439 updateClipping( 1440 aClipRect, 1441 rFactoryParms, 1442 true ); 1443 1444 break; 1445 } 1446 1447 case META_ISECTREGIONCLIPREGION_ACTION: 1448 { 1449 MetaISectRegionClipRegionAction* pClipAction = static_cast<MetaISectRegionClipRegionAction*>(pCurrAct); 1450 1451 if( !pClipAction->GetRegion().HasPolyPolygon() ) 1452 { 1453 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip " 1454 "region encountered, falling back to bounding box!" ); 1455 1456 // #121806# explicitely kept integer 1457 Rectangle aClipRect( 1458 rVDev.LogicToPixel( pClipAction->GetRegion().GetBoundRect() ) ); 1459 1460 // intersect current clip with given rect 1461 updateClipping( 1462 aClipRect, 1463 rFactoryParms, 1464 true ); 1465 } 1466 else 1467 { 1468 // intersect current clip with given clip polygon 1469 1470 // #121806# explicitely kept integer 1471 updateClipping( 1472 rVDev.LogicToPixel( 1473 pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(), 1474 rFactoryParms, 1475 true ); 1476 } 1477 1478 break; 1479 } 1480 1481 case META_MOVECLIPREGION_ACTION: 1482 // TODO(F2): NYI 1483 break; 1484 1485 case META_LINECOLOR_ACTION: 1486 if( !rParms.maLineColor.is_initialized() ) 1487 { 1488 setStateColor( static_cast<MetaLineColorAction*>(pCurrAct), 1489 getState( rStates ).isLineColorSet, 1490 getState( rStates ).lineColor, 1491 rCanvas ); 1492 } 1493 break; 1494 1495 case META_FILLCOLOR_ACTION: 1496 if( !rParms.maFillColor.is_initialized() ) 1497 { 1498 setStateColor( static_cast<MetaFillColorAction*>(pCurrAct), 1499 getState( rStates ).isFillColorSet, 1500 getState( rStates ).fillColor, 1501 rCanvas ); 1502 } 1503 break; 1504 1505 case META_TEXTCOLOR_ACTION: 1506 { 1507 if( !rParms.maTextColor.is_initialized() ) 1508 { 1509 // Text color is set unconditionally, thus, no 1510 // use of setStateColor here 1511 ::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() ); 1512 1513 // force alpha part of color to 1514 // opaque. transparent painting is done 1515 // explicitely via META_TRANSPARENT_ACTION 1516 aColor.SetTransparency(0); 1517 1518 getState( rStates ).textColor = 1519 ::vcl::unotools::colorToDoubleSequence( 1520 aColor, 1521 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() ); 1522 } 1523 } 1524 break; 1525 1526 case META_TEXTFILLCOLOR_ACTION: 1527 if( !rParms.maTextColor.is_initialized() ) 1528 { 1529 setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct), 1530 getState( rStates ).isTextFillColorSet, 1531 getState( rStates ).textFillColor, 1532 rCanvas ); 1533 } 1534 break; 1535 1536 case META_TEXTLINECOLOR_ACTION: 1537 if( !rParms.maTextColor.is_initialized() ) 1538 { 1539 setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct), 1540 getState( rStates ).isTextLineColorSet, 1541 getState( rStates ).textLineColor, 1542 rCanvas ); 1543 } 1544 break; 1545 1546 case META_TEXTALIGN_ACTION: 1547 { 1548 ::cppcanvas::internal::OutDevState& rState = getState( rStates ); 1549 const TextAlign eTextAlign( static_cast<MetaTextAlignAction*>(pCurrAct)->GetTextAlign() ); 1550 1551 rState.textReferencePoint = eTextAlign; 1552 } 1553 break; 1554 1555 case META_FONT_ACTION: 1556 { 1557 ::cppcanvas::internal::OutDevState& rState = getState( rStates ); 1558 const ::Font& rFont( static_cast<MetaFontAction*>(pCurrAct)->GetFont() ); 1559 1560 rState.xFont = createFont( rState.fontRotation, 1561 rFont, 1562 rFactoryParms ); 1563 1564 // TODO(Q2): define and use appropriate enumeration types 1565 rState.textReliefStyle = (sal_Int8)rFont.GetRelief(); 1566 rState.textOverlineStyle = (sal_Int8)rFont.GetOverline(); 1567 rState.textUnderlineStyle = rParms.maFontUnderline.is_initialized() ? 1568 (*rParms.maFontUnderline ? (sal_Int8)UNDERLINE_SINGLE : (sal_Int8)UNDERLINE_NONE) : 1569 (sal_Int8)rFont.GetUnderline(); 1570 rState.textStrikeoutStyle = (sal_Int8)rFont.GetStrikeout(); 1571 rState.textEmphasisMarkStyle = (sal_Int8)rFont.GetEmphasisMark(); 1572 rState.isTextEffectShadowSet = (rFont.IsShadow() != sal_False); 1573 rState.isTextWordUnderlineSet = (rFont.IsWordLineMode() != sal_False); 1574 rState.isTextOutlineModeSet = (rFont.IsOutline() != sal_False); 1575 } 1576 break; 1577 1578 case META_RASTEROP_ACTION: 1579 // TODO(F2): NYI 1580 break; 1581 1582 case META_LAYOUTMODE_ACTION: 1583 { 1584 // TODO(F2): A lot is missing here 1585 int nLayoutMode = static_cast<MetaLayoutModeAction*>(pCurrAct)->GetLayoutMode(); 1586 ::cppcanvas::internal::OutDevState& rState = getState( rStates ); 1587 switch( nLayoutMode & (TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_BIDI_STRONG) ) 1588 { 1589 case TEXT_LAYOUT_BIDI_LTR: 1590 rState.textDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT; 1591 break; 1592 1593 case (TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG): 1594 rState.textDirection = rendering::TextDirection::STRONG_LEFT_TO_RIGHT; 1595 break; 1596 1597 case TEXT_LAYOUT_BIDI_RTL: 1598 rState.textDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT; 1599 break; 1600 1601 case (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG): 1602 rState.textDirection = rendering::TextDirection::STRONG_RIGHT_TO_LEFT; 1603 break; 1604 } 1605 1606 rState.textAlignment = 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED; 1607 if( (nLayoutMode & (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_RIGHT) ) 1608 && !(nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT ) ) 1609 { 1610 rState.textAlignment = 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED; 1611 } 1612 } 1613 break; 1614 1615 // ------------------------------------------------------------ 1616 1617 // In the second part of this monster-switch, we 1618 // handle all recursing meta actions. These are the 1619 // ones generating a metafile by themselves, which is 1620 // then processed by recursively calling this method. 1621 1622 // ------------------------------------------------------------ 1623 1624 case META_GRADIENT_ACTION: 1625 { 1626 MetaGradientAction* pGradAct = static_cast<MetaGradientAction*>(pCurrAct); 1627 createGradientAction( ::Polygon( pGradAct->GetRect() ), 1628 pGradAct->GetGradient(), 1629 rFactoryParms, 1630 true, 1631 bSubsettableActions ); 1632 } 1633 break; 1634 1635 case META_HATCH_ACTION: 1636 { 1637 // TODO(F2): use native Canvas hatches here 1638 GDIMetaFile aTmpMtf; 1639 1640 rVDev.AddHatchActions( static_cast<MetaHatchAction*>(pCurrAct)->GetPolyPolygon(), 1641 static_cast<MetaHatchAction*>(pCurrAct)->GetHatch(), 1642 aTmpMtf ); 1643 createActions( aTmpMtf, rFactoryParms, 1644 bSubsettableActions ); 1645 } 1646 break; 1647 1648 case META_EPS_ACTION: 1649 { 1650 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pCurrAct); 1651 const GDIMetaFile& rSubstitute = pAct->GetSubstitute(); 1652 1653 // #121806# explicitely kept integer 1654 const Size aMtfSize( rSubstitute.GetPrefSize() ); 1655 const Size aMtfSizePixPre( rVDev.LogicToPixel( aMtfSize, 1656 rSubstitute.GetPrefMapMode() ) ); 1657 1658 // #i44110# correct null-sized output - there 1659 // are metafiles which have zero size in at 1660 // least one dimension 1661 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ), 1662 ::std::max( aMtfSizePixPre.Height(), 1L ) ); 1663 1664 // Setup local transform, such that the 1665 // metafile renders itself into the given 1666 // output rectangle 1667 pushState( rStates, PUSH_ALL ); 1668 1669 rVDev.Push(); 1670 rVDev.SetMapMode( rSubstitute.GetPrefMapMode() ); 1671 1672 const ::Point& rPos( rVDev.LogicToPixel( pAct->GetPoint() ) ); 1673 const ::Size& rSize( rVDev.LogicToPixel( pAct->GetSize() ) ); 1674 1675 getState( rStates ).transform.translate( rPos.X(), 1676 rPos.Y() ); 1677 getState( rStates ).transform.scale( (double)rSize.Width() / aMtfSizePix.Width(), 1678 (double)rSize.Height() / aMtfSizePix.Height() ); 1679 1680 createActions( const_cast<GDIMetaFile&>(pAct->GetSubstitute()), 1681 rFactoryParms, 1682 bSubsettableActions ); 1683 1684 rVDev.Pop(); 1685 popState( rStates ); 1686 } 1687 break; 1688 1689 // handle metafile comments, to retrieve 1690 // meta-information for gradients, fills and 1691 // strokes. May skip actions, and may recurse. 1692 case META_COMMENT_ACTION: 1693 { 1694 MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pCurrAct); 1695 1696 // Handle gradients 1697 if ( pAct->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_BEGIN" ) == COMPARE_EQUAL ) 1698 { 1699 MetaGradientExAction* pGradAction = NULL; 1700 bool bDone( false ); 1701 while( !bDone && 1702 (pCurrAct=rMtf.NextAction()) != NULL ) 1703 { 1704 switch( pCurrAct->GetType() ) 1705 { 1706 // extract gradient info 1707 case META_GRADIENTEX_ACTION: 1708 pGradAction = static_cast<MetaGradientExAction*>(pCurrAct); 1709 break; 1710 1711 // skip broken-down rendering, output gradient when sequence is ended 1712 case META_COMMENT_ACTION: 1713 if( static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_END" ) == COMPARE_EQUAL ) 1714 { 1715 bDone = true; 1716 1717 if( pGradAction ) 1718 { 1719 createGradientAction( pGradAction->GetPolyPolygon(), 1720 pGradAction->GetGradient(), 1721 rFactoryParms, 1722 false, 1723 bSubsettableActions ); 1724 } 1725 } 1726 break; 1727 } 1728 } 1729 } 1730 // TODO(P2): Handle drawing layer strokes, via 1731 // XPATHSTROKE_SEQ_BEGIN comment 1732 1733 // Handle drawing layer fills 1734 else if( pAct->GetComment().Equals( "XPATHFILL_SEQ_BEGIN" ) ) 1735 { 1736 const sal_uInt8* pData = pAct->GetData(); 1737 if ( pData ) 1738 { 1739 SvMemoryStream aMemStm( (void*)pData, pAct->GetDataSize(), STREAM_READ ); 1740 1741 SvtGraphicFill aFill; 1742 aMemStm >> aFill; 1743 1744 // TODO(P2): Also handle gradients and 1745 // hatches like this 1746 1747 // only evaluate comment for pure 1748 // bitmap fills. If a transparency 1749 // gradient is involved (denoted by 1750 // the FloatTransparent action), take 1751 // the normal meta actions. 1752 if( aFill.getFillType() == SvtGraphicFill::fillTexture && 1753 !isActionContained( rMtf, 1754 "XPATHFILL_SEQ_END", 1755 META_FLOATTRANSPARENT_ACTION ) ) 1756 { 1757 rendering::Texture aTexture; 1758 1759 // TODO(F1): the SvtGraphicFill 1760 // can also transport metafiles 1761 // here, handle that case, too 1762 Graphic aGraphic; 1763 aFill.getGraphic( aGraphic ); 1764 1765 BitmapEx aBmpEx( aGraphic.GetBitmapEx() ); 1766 const ::Size aBmpSize( aBmpEx.GetSizePixel() ); 1767 1768 ::SvtGraphicFill::Transform aTransform; 1769 aFill.getTransform( aTransform ); 1770 1771 ::basegfx::B2DHomMatrix aMatrix; 1772 1773 // convert to basegfx matrix 1774 aMatrix.set(0,0, aTransform.matrix[ 0 ] ); 1775 aMatrix.set(0,1, aTransform.matrix[ 1 ] ); 1776 aMatrix.set(0,2, aTransform.matrix[ 2 ] ); 1777 aMatrix.set(1,0, aTransform.matrix[ 3 ] ); 1778 aMatrix.set(1,1, aTransform.matrix[ 4 ] ); 1779 aMatrix.set(1,2, aTransform.matrix[ 5 ] ); 1780 1781 ::basegfx::B2DHomMatrix aScale; 1782 aScale.scale( aBmpSize.Width(), 1783 aBmpSize.Height() ); 1784 1785 // post-multiply with the bitmap 1786 // size (XCanvas' texture assumes 1787 // the given bitmap to be 1788 // normalized to [0,1]x[0,1] 1789 // rectangle) 1790 aMatrix = aMatrix * aScale; 1791 1792 // pre-multiply with the 1793 // logic-to-pixel scale factor 1794 // (the metafile comment works in 1795 // logical coordinates). 1796 ::basegfx::B2DHomMatrix aLogic2PixelTransform; 1797 aMatrix *= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform, 1798 rVDev ); 1799 1800 ::basegfx::unotools::affineMatrixFromHomMatrix( 1801 aTexture.AffineTransform, 1802 aMatrix ); 1803 1804 aTexture.Alpha = 1.0 - aFill.getTransparency(); 1805 aTexture.Bitmap = 1806 ::vcl::unotools::xBitmapFromBitmapEx( 1807 rCanvas->getUNOCanvas()->getDevice(), 1808 aBmpEx ); 1809 if( aFill.isTiling() ) 1810 { 1811 aTexture.RepeatModeX = rendering::TexturingMode::REPEAT; 1812 aTexture.RepeatModeY = rendering::TexturingMode::REPEAT; 1813 } 1814 else 1815 { 1816 aTexture.RepeatModeX = rendering::TexturingMode::NONE; 1817 aTexture.RepeatModeY = rendering::TexturingMode::NONE; 1818 } 1819 1820 ::PolyPolygon aPath; 1821 aFill.getPath( aPath ); 1822 1823 ::basegfx::B2DPolyPolygon aPoly( aPath.getB2DPolyPolygon() ); 1824 aPoly.transform( getState( rStates ).mapModeTransform ); 1825 ActionSharedPtr pPolyAction( 1826 internal::PolyPolyActionFactory::createPolyPolyAction( 1827 aPoly, 1828 rCanvas, 1829 getState( rStates ), 1830 aTexture ) ); 1831 1832 if( pPolyAction ) 1833 { 1834 maActions.push_back( 1835 MtfAction( 1836 pPolyAction, 1837 io_rCurrActionIndex ) ); 1838 1839 io_rCurrActionIndex += pPolyAction->getActionCount()-1; 1840 } 1841 1842 // skip broken-down render output 1843 skipContent( rMtf, 1844 "XPATHFILL_SEQ_END", 1845 io_rCurrActionIndex ); 1846 } 1847 } 1848 } 1849 } 1850 break; 1851 1852 // ------------------------------------------------------------ 1853 1854 // In the third part of this monster-switch, we 1855 // handle all 'acting' meta actions. These are all 1856 // processed by constructing function objects for 1857 // them, which will later ease caching. 1858 1859 // ------------------------------------------------------------ 1860 1861 case META_POINT_ACTION: 1862 { 1863 const OutDevState& rState( getState( rStates ) ); 1864 if( rState.lineColor.getLength() ) 1865 { 1866 ActionSharedPtr pPointAction( 1867 internal::PointActionFactory::createPointAction( 1868 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( 1869 static_cast<MetaPointAction*>(pCurrAct)->GetPoint() ), 1870 rCanvas, 1871 rState ) ); 1872 1873 if( pPointAction ) 1874 { 1875 maActions.push_back( 1876 MtfAction( 1877 pPointAction, 1878 io_rCurrActionIndex ) ); 1879 1880 io_rCurrActionIndex += pPointAction->getActionCount()-1; 1881 } 1882 } 1883 } 1884 break; 1885 1886 case META_PIXEL_ACTION: 1887 { 1888 const OutDevState& rState( getState( rStates ) ); 1889 if( rState.lineColor.getLength() ) 1890 { 1891 ActionSharedPtr pPointAction( 1892 internal::PointActionFactory::createPointAction( 1893 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( 1894 static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ), 1895 rCanvas, 1896 rState, 1897 static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) ); 1898 1899 if( pPointAction ) 1900 { 1901 maActions.push_back( 1902 MtfAction( 1903 pPointAction, 1904 io_rCurrActionIndex ) ); 1905 1906 io_rCurrActionIndex += pPointAction->getActionCount()-1; 1907 } 1908 } 1909 } 1910 break; 1911 1912 case META_LINE_ACTION: 1913 { 1914 const OutDevState& rState( getState( rStates ) ); 1915 if( rState.lineColor.getLength() ) 1916 { 1917 MetaLineAction* pLineAct = static_cast<MetaLineAction*>(pCurrAct); 1918 1919 const LineInfo& rLineInfo( pLineAct->GetLineInfo() ); 1920 1921 const ::basegfx::B2DPoint aStartPoint( 1922 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetStartPoint() )); 1923 const ::basegfx::B2DPoint aEndPoint( 1924 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetEndPoint() )); 1925 1926 ActionSharedPtr pLineAction; 1927 1928 if( rLineInfo.IsDefault() ) 1929 { 1930 // plain hair line 1931 pLineAction = 1932 internal::LineActionFactory::createLineAction( 1933 aStartPoint, 1934 aEndPoint, 1935 rCanvas, 1936 rState ); 1937 1938 if( pLineAction ) 1939 { 1940 maActions.push_back( 1941 MtfAction( 1942 pLineAction, 1943 io_rCurrActionIndex ) ); 1944 1945 io_rCurrActionIndex += pLineAction->getActionCount()-1; 1946 } 1947 } 1948 else if( LINE_NONE != rLineInfo.GetStyle() ) 1949 { 1950 // 'thick' line 1951 rendering::StrokeAttributes aStrokeAttributes; 1952 1953 setupStrokeAttributes( aStrokeAttributes, 1954 rFactoryParms, 1955 rLineInfo ); 1956 1957 // XCanvas can only stroke polygons, 1958 // not simple lines - thus, handle 1959 // this case via the polypolygon 1960 // action 1961 ::basegfx::B2DPolygon aPoly; 1962 aPoly.append( aStartPoint ); 1963 aPoly.append( aEndPoint ); 1964 pLineAction = 1965 internal::PolyPolyActionFactory::createPolyPolyAction( 1966 ::basegfx::B2DPolyPolygon( aPoly ), 1967 rCanvas, rState, aStrokeAttributes ); 1968 1969 if( pLineAction ) 1970 { 1971 maActions.push_back( 1972 MtfAction( 1973 pLineAction, 1974 io_rCurrActionIndex ) ); 1975 1976 io_rCurrActionIndex += pLineAction->getActionCount()-1; 1977 } 1978 } 1979 // else: line style is default 1980 // (i.e. invisible), don't generate action 1981 } 1982 } 1983 break; 1984 1985 case META_RECT_ACTION: 1986 { 1987 const Rectangle& rRect( 1988 static_cast<MetaRectAction*>(pCurrAct)->GetRect() ); 1989 1990 if( rRect.IsEmpty() ) 1991 break; 1992 1993 const OutDevState& rState( getState( rStates ) ); 1994 const ::basegfx::B2DPoint aTopLeftPixel( 1995 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ) ); 1996 const ::basegfx::B2DPoint aBottomRightPixel( 1997 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) + 1998 // #121100# OutputDevice::DrawRect() fills 1999 // rectangles Apple-like, i.e. with one 2000 // additional pixel to the right and bottom. 2001 ::basegfx::B2DPoint(1,1) ); 2002 2003 createFillAndStroke( ::basegfx::tools::createPolygonFromRect( 2004 ::basegfx::B2DRange( aTopLeftPixel, 2005 aBottomRightPixel )), 2006 rFactoryParms ); 2007 break; 2008 } 2009 2010 case META_ROUNDRECT_ACTION: 2011 { 2012 const Rectangle& rRect( 2013 static_cast<MetaRoundRectAction*>(pCurrAct)->GetRect()); 2014 2015 if( rRect.IsEmpty() ) 2016 break; 2017 2018 ::basegfx::B2DPolygon aPoly( 2019 ::basegfx::tools::createPolygonFromRect( 2020 ::basegfx::B2DRange( 2021 ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ), 2022 ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) + 2023 ::basegfx::B2DPoint(1,1) ), 2024 static_cast<MetaRoundRectAction*>(pCurrAct)->GetHorzRound(), 2025 static_cast<MetaRoundRectAction*>(pCurrAct)->GetVertRound() )); 2026 aPoly.transform( getState( rStates ).mapModeTransform ); 2027 2028 createFillAndStroke( aPoly, 2029 rFactoryParms ); 2030 } 2031 break; 2032 2033 case META_ELLIPSE_ACTION: 2034 { 2035 const Rectangle& rRect( 2036 static_cast<MetaEllipseAction*>(pCurrAct)->GetRect() ); 2037 2038 if( rRect.IsEmpty() ) 2039 break; 2040 2041 const ::basegfx::B2DRange aRange( 2042 ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ), 2043 ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) + 2044 ::basegfx::B2DPoint(1,1) ); 2045 2046 ::basegfx::B2DPolygon aPoly( 2047 ::basegfx::tools::createPolygonFromEllipse( 2048 aRange.getCenter(), 2049 aRange.getWidth(), 2050 aRange.getHeight() )); 2051 aPoly.transform( getState( rStates ).mapModeTransform ); 2052 2053 createFillAndStroke( aPoly, 2054 rFactoryParms ); 2055 } 2056 break; 2057 2058 case META_ARC_ACTION: 2059 { 2060 // TODO(F1): Missing basegfx functionality. Mind empty rects! 2061 const Polygon aToolsPoly( static_cast<MetaArcAction*>(pCurrAct)->GetRect(), 2062 static_cast<MetaArcAction*>(pCurrAct)->GetStartPoint(), 2063 static_cast<MetaArcAction*>(pCurrAct)->GetEndPoint(), POLY_ARC ); 2064 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() ); 2065 aPoly.transform( getState( rStates ).mapModeTransform ); 2066 2067 createFillAndStroke( aPoly, 2068 rFactoryParms ); 2069 } 2070 break; 2071 2072 case META_PIE_ACTION: 2073 { 2074 // TODO(F1): Missing basegfx functionality. Mind empty rects! 2075 const Polygon aToolsPoly( static_cast<MetaPieAction*>(pCurrAct)->GetRect(), 2076 static_cast<MetaPieAction*>(pCurrAct)->GetStartPoint(), 2077 static_cast<MetaPieAction*>(pCurrAct)->GetEndPoint(), POLY_PIE ); 2078 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() ); 2079 aPoly.transform( getState( rStates ).mapModeTransform ); 2080 2081 createFillAndStroke( aPoly, 2082 rFactoryParms ); 2083 } 2084 break; 2085 2086 case META_CHORD_ACTION: 2087 { 2088 // TODO(F1): Missing basegfx functionality. Mind empty rects! 2089 const Polygon aToolsPoly( static_cast<MetaChordAction*>(pCurrAct)->GetRect(), 2090 static_cast<MetaChordAction*>(pCurrAct)->GetStartPoint(), 2091 static_cast<MetaChordAction*>(pCurrAct)->GetEndPoint(), POLY_CHORD ); 2092 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() ); 2093 aPoly.transform( getState( rStates ).mapModeTransform ); 2094 2095 createFillAndStroke( aPoly, 2096 rFactoryParms ); 2097 } 2098 break; 2099 2100 case META_POLYLINE_ACTION: 2101 { 2102 const OutDevState& rState( getState( rStates ) ); 2103 if( rState.lineColor.getLength() || 2104 rState.fillColor.getLength() ) 2105 { 2106 MetaPolyLineAction* pPolyLineAct = static_cast<MetaPolyLineAction*>(pCurrAct); 2107 2108 const LineInfo& rLineInfo( pPolyLineAct->GetLineInfo() ); 2109 ::basegfx::B2DPolygon aPoly( pPolyLineAct->GetPolygon().getB2DPolygon() ); 2110 aPoly.transform( rState.mapModeTransform ); 2111 2112 ActionSharedPtr pLineAction; 2113 2114 if( rLineInfo.IsDefault() ) 2115 { 2116 // plain hair line polygon 2117 pLineAction = 2118 internal::PolyPolyActionFactory::createLinePolyPolyAction( 2119 ::basegfx::B2DPolyPolygon(aPoly), 2120 rCanvas, 2121 rState ); 2122 2123 if( pLineAction ) 2124 { 2125 maActions.push_back( 2126 MtfAction( 2127 pLineAction, 2128 io_rCurrActionIndex ) ); 2129 2130 io_rCurrActionIndex += pLineAction->getActionCount()-1; 2131 } 2132 } 2133 else if( LINE_NONE != rLineInfo.GetStyle() ) 2134 { 2135 // 'thick' line polygon 2136 rendering::StrokeAttributes aStrokeAttributes; 2137 2138 setupStrokeAttributes( aStrokeAttributes, 2139 rFactoryParms, 2140 rLineInfo ); 2141 2142 pLineAction = 2143 internal::PolyPolyActionFactory::createPolyPolyAction( 2144 ::basegfx::B2DPolyPolygon(aPoly), 2145 rCanvas, 2146 rState, 2147 aStrokeAttributes ) ; 2148 2149 if( pLineAction ) 2150 { 2151 maActions.push_back( 2152 MtfAction( 2153 pLineAction, 2154 io_rCurrActionIndex ) ); 2155 2156 io_rCurrActionIndex += pLineAction->getActionCount()-1; 2157 } 2158 } 2159 // else: line style is default 2160 // (i.e. invisible), don't generate action 2161 } 2162 } 2163 break; 2164 2165 case META_POLYGON_ACTION: 2166 { 2167 ::basegfx::B2DPolygon aPoly( static_cast<MetaPolygonAction*>(pCurrAct)->GetPolygon().getB2DPolygon() ); 2168 aPoly.transform( getState( rStates ).mapModeTransform ); 2169 createFillAndStroke( aPoly, 2170 rFactoryParms ); 2171 } 2172 break; 2173 2174 case META_POLYPOLYGON_ACTION: 2175 { 2176 ::basegfx::B2DPolyPolygon aPoly( static_cast<MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon().getB2DPolyPolygon() ); 2177 aPoly.transform( getState( rStates ).mapModeTransform ); 2178 createFillAndStroke( aPoly, 2179 rFactoryParms ); 2180 } 2181 break; 2182 2183 case META_BMP_ACTION: 2184 { 2185 MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pCurrAct); 2186 2187 ActionSharedPtr pBmpAction( 2188 internal::BitmapActionFactory::createBitmapAction( 2189 pAct->GetBitmap(), 2190 getState( rStates ).mapModeTransform * 2191 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2192 rCanvas, 2193 getState( rStates ) ) ); 2194 2195 if( pBmpAction ) 2196 { 2197 maActions.push_back( 2198 MtfAction( 2199 pBmpAction, 2200 io_rCurrActionIndex ) ); 2201 2202 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2203 } 2204 } 2205 break; 2206 2207 case META_BMPSCALE_ACTION: 2208 { 2209 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pCurrAct); 2210 2211 ActionSharedPtr pBmpAction( 2212 internal::BitmapActionFactory::createBitmapAction( 2213 pAct->GetBitmap(), 2214 getState( rStates ).mapModeTransform * 2215 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2216 getState( rStates ).mapModeTransform * 2217 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ), 2218 rCanvas, 2219 getState( rStates ) ) ); 2220 2221 if( pBmpAction ) 2222 { 2223 maActions.push_back( 2224 MtfAction( 2225 pBmpAction, 2226 io_rCurrActionIndex ) ); 2227 2228 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2229 } 2230 } 2231 break; 2232 2233 case META_BMPSCALEPART_ACTION: 2234 { 2235 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pCurrAct); 2236 2237 // crop bitmap to given source rectangle (no 2238 // need to copy and convert the whole bitmap) 2239 Bitmap aBmp( pAct->GetBitmap() ); 2240 const Rectangle aCropRect( pAct->GetSrcPoint(), 2241 pAct->GetSrcSize() ); 2242 aBmp.Crop( aCropRect ); 2243 2244 ActionSharedPtr pBmpAction( 2245 internal::BitmapActionFactory::createBitmapAction( 2246 aBmp, 2247 getState( rStates ).mapModeTransform * 2248 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ), 2249 getState( rStates ).mapModeTransform * 2250 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ), 2251 rCanvas, 2252 getState( rStates ) ) ); 2253 2254 if( pBmpAction ) 2255 { 2256 maActions.push_back( 2257 MtfAction( 2258 pBmpAction, 2259 io_rCurrActionIndex ) ); 2260 2261 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2262 } 2263 } 2264 break; 2265 2266 case META_BMPEX_ACTION: 2267 { 2268 MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pCurrAct); 2269 2270 ActionSharedPtr pBmpAction( 2271 internal::BitmapActionFactory::createBitmapAction( 2272 pAct->GetBitmapEx(), 2273 getState( rStates ).mapModeTransform * 2274 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2275 rCanvas, 2276 getState( rStates ) ) ); 2277 2278 if( pBmpAction ) 2279 { 2280 maActions.push_back( 2281 MtfAction( 2282 pBmpAction, 2283 io_rCurrActionIndex ) ); 2284 2285 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2286 } 2287 } 2288 break; 2289 2290 case META_BMPEXSCALE_ACTION: 2291 { 2292 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pCurrAct); 2293 2294 ActionSharedPtr pBmpAction( 2295 internal::BitmapActionFactory::createBitmapAction( 2296 pAct->GetBitmapEx(), 2297 getState( rStates ).mapModeTransform * 2298 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2299 getState( rStates ).mapModeTransform * 2300 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ), 2301 rCanvas, 2302 getState( rStates ) ) ); 2303 2304 if( pBmpAction ) 2305 { 2306 maActions.push_back( 2307 MtfAction( 2308 pBmpAction, 2309 io_rCurrActionIndex ) ); 2310 2311 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2312 } 2313 } 2314 break; 2315 2316 case META_BMPEXSCALEPART_ACTION: 2317 { 2318 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pCurrAct); 2319 2320 // crop bitmap to given source rectangle (no 2321 // need to copy and convert the whole bitmap) 2322 BitmapEx aBmp( pAct->GetBitmapEx() ); 2323 const Rectangle aCropRect( pAct->GetSrcPoint(), 2324 pAct->GetSrcSize() ); 2325 aBmp.Crop( aCropRect ); 2326 2327 ActionSharedPtr pBmpAction( 2328 internal::BitmapActionFactory::createBitmapAction( 2329 aBmp, 2330 getState( rStates ).mapModeTransform * 2331 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ), 2332 getState( rStates ).mapModeTransform * 2333 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ), 2334 rCanvas, 2335 getState( rStates ) ) ); 2336 2337 if( pBmpAction ) 2338 { 2339 maActions.push_back( 2340 MtfAction( 2341 pBmpAction, 2342 io_rCurrActionIndex ) ); 2343 2344 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2345 } 2346 } 2347 break; 2348 2349 case META_MASK_ACTION: 2350 { 2351 MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pCurrAct); 2352 2353 // create masked BitmapEx right here, as the 2354 // canvas does not provide equivalent 2355 // functionality 2356 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(), 2357 pAct->GetColor() )); 2358 2359 ActionSharedPtr pBmpAction( 2360 internal::BitmapActionFactory::createBitmapAction( 2361 aBmp, 2362 getState( rStates ).mapModeTransform * 2363 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2364 rCanvas, 2365 getState( rStates ) ) ); 2366 2367 if( pBmpAction ) 2368 { 2369 maActions.push_back( 2370 MtfAction( 2371 pBmpAction, 2372 io_rCurrActionIndex ) ); 2373 2374 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2375 } 2376 } 2377 break; 2378 2379 case META_MASKSCALE_ACTION: 2380 { 2381 MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pCurrAct); 2382 2383 // create masked BitmapEx right here, as the 2384 // canvas does not provide equivalent 2385 // functionality 2386 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(), 2387 pAct->GetColor() )); 2388 2389 ActionSharedPtr pBmpAction( 2390 internal::BitmapActionFactory::createBitmapAction( 2391 aBmp, 2392 getState( rStates ).mapModeTransform * 2393 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2394 getState( rStates ).mapModeTransform * 2395 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ), 2396 rCanvas, 2397 getState( rStates ) ) ); 2398 2399 if( pBmpAction ) 2400 { 2401 maActions.push_back( 2402 MtfAction( 2403 pBmpAction, 2404 io_rCurrActionIndex ) ); 2405 2406 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2407 } 2408 } 2409 break; 2410 2411 case META_MASKSCALEPART_ACTION: 2412 { 2413 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pCurrAct); 2414 2415 // create masked BitmapEx right here, as the 2416 // canvas does not provide equivalent 2417 // functionality 2418 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(), 2419 pAct->GetColor() )); 2420 2421 // crop bitmap to given source rectangle (no 2422 // need to copy and convert the whole bitmap) 2423 const Rectangle aCropRect( pAct->GetSrcPoint(), 2424 pAct->GetSrcSize() ); 2425 aBmp.Crop( aCropRect ); 2426 2427 ActionSharedPtr pBmpAction( 2428 internal::BitmapActionFactory::createBitmapAction( 2429 aBmp, 2430 getState( rStates ).mapModeTransform * 2431 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ), 2432 getState( rStates ).mapModeTransform * 2433 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ), 2434 rCanvas, 2435 getState( rStates ) ) ); 2436 2437 if( pBmpAction ) 2438 { 2439 maActions.push_back( 2440 MtfAction( 2441 pBmpAction, 2442 io_rCurrActionIndex ) ); 2443 2444 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2445 } 2446 } 2447 break; 2448 2449 case META_GRADIENTEX_ACTION: 2450 // TODO(F1): use native Canvas gradients here 2451 // action is ignored here, because redundant to META_GRADIENT_ACTION 2452 break; 2453 2454 case META_WALLPAPER_ACTION: 2455 // TODO(F2): NYI 2456 break; 2457 2458 case META_TRANSPARENT_ACTION: 2459 { 2460 const OutDevState& rState( getState( rStates ) ); 2461 if( rState.lineColor.getLength() || 2462 rState.fillColor.getLength() ) 2463 { 2464 MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pCurrAct); 2465 ::basegfx::B2DPolyPolygon aPoly( pAct->GetPolyPolygon().getB2DPolyPolygon() ); 2466 aPoly.transform( rState.mapModeTransform ); 2467 2468 ActionSharedPtr pPolyAction( 2469 internal::PolyPolyActionFactory::createPolyPolyAction( 2470 aPoly, 2471 rCanvas, 2472 rState, 2473 pAct->GetTransparence() ) ); 2474 2475 if( pPolyAction ) 2476 { 2477 maActions.push_back( 2478 MtfAction( 2479 pPolyAction, 2480 io_rCurrActionIndex ) ); 2481 2482 io_rCurrActionIndex += pPolyAction->getActionCount()-1; 2483 } 2484 } 2485 } 2486 break; 2487 2488 case META_FLOATTRANSPARENT_ACTION: 2489 { 2490 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pCurrAct); 2491 2492 internal::MtfAutoPtr pMtf( 2493 new ::GDIMetaFile( pAct->GetGDIMetaFile() ) ); 2494 2495 // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls) 2496 internal::GradientAutoPtr pGradient( 2497 new Gradient( pAct->GetGradient() ) ); 2498 2499 DBG_TESTSOLARMUTEX(); 2500 2501 ActionSharedPtr pFloatTransAction( 2502 internal::TransparencyGroupActionFactory::createTransparencyGroupAction( 2503 pMtf, 2504 pGradient, 2505 rParms, 2506 getState( rStates ).mapModeTransform * 2507 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2508 getState( rStates ).mapModeTransform * 2509 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ), 2510 rCanvas, 2511 getState( rStates ) ) ); 2512 2513 if( pFloatTransAction ) 2514 { 2515 maActions.push_back( 2516 MtfAction( 2517 pFloatTransAction, 2518 io_rCurrActionIndex ) ); 2519 2520 io_rCurrActionIndex += pFloatTransAction->getActionCount()-1; 2521 } 2522 } 2523 break; 2524 2525 case META_TEXT_ACTION: 2526 { 2527 MetaTextAction* pAct = static_cast<MetaTextAction*>(pCurrAct); 2528 XubString sText = XubString( pAct->GetText() ); 2529 2530 if( rVDev.GetDigitLanguage()) 2531 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() ); 2532 2533 createTextAction( 2534 pAct->GetPoint(), 2535 sText, 2536 pAct->GetIndex(), 2537 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(), 2538 NULL, 2539 rFactoryParms, 2540 bSubsettableActions ); 2541 } 2542 break; 2543 2544 case META_TEXTARRAY_ACTION: 2545 { 2546 MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pCurrAct); 2547 XubString sText = XubString( pAct->GetText() ); 2548 2549 if( rVDev.GetDigitLanguage()) 2550 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() ); 2551 2552 createTextAction( 2553 pAct->GetPoint(), 2554 sText, 2555 pAct->GetIndex(), 2556 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(), 2557 pAct->GetDXArray(), 2558 rFactoryParms, 2559 bSubsettableActions ); 2560 } 2561 break; 2562 2563 case META_TEXTLINE_ACTION: 2564 { 2565 MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pCurrAct); 2566 2567 const OutDevState& rState( getState( rStates ) ); 2568 const ::Size aBaselineOffset( tools::getBaselineOffset( rState, 2569 rVDev ) ); 2570 const ::Point aStartPoint( pAct->GetStartPoint() ); 2571 const ::basegfx::B2DSize aSize( rState.mapModeTransform * 2572 ::basegfx::B2DSize(pAct->GetWidth(), 2573 0 )); 2574 2575 ActionSharedPtr pPolyAction( 2576 PolyPolyActionFactory::createPolyPolyAction( 2577 tools::createTextLinesPolyPolygon( 2578 rState.mapModeTransform * 2579 ::basegfx::B2DPoint( 2580 ::vcl::unotools::b2DPointFromPoint(pAct->GetStartPoint()) + 2581 ::vcl::unotools::b2DSizeFromSize(aBaselineOffset)), 2582 aSize.getX(), 2583 tools::createTextLineInfo( rVDev, 2584 rState )), 2585 rCanvas, 2586 rState ) ); 2587 2588 if( pPolyAction.get() ) 2589 { 2590 maActions.push_back( 2591 MtfAction( 2592 pPolyAction, 2593 io_rCurrActionIndex ) ); 2594 2595 io_rCurrActionIndex += pPolyAction->getActionCount()-1; 2596 } 2597 } 2598 break; 2599 2600 case META_TEXTRECT_ACTION: 2601 { 2602 MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pCurrAct); 2603 2604 pushState( rStates, PUSH_ALL ); 2605 2606 // use the VDev to break up the text rect 2607 // action into readily formatted lines 2608 GDIMetaFile aTmpMtf; 2609 rVDev.AddTextRectActions( pAct->GetRect(), 2610 pAct->GetText(), 2611 pAct->GetStyle(), 2612 aTmpMtf ); 2613 2614 createActions( aTmpMtf, 2615 rFactoryParms, 2616 bSubsettableActions ); 2617 2618 popState( rStates ); 2619 2620 break; 2621 } 2622 2623 case META_STRETCHTEXT_ACTION: 2624 { 2625 MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pCurrAct); 2626 XubString sText = XubString( pAct->GetText() ); 2627 2628 if( rVDev.GetDigitLanguage()) 2629 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() ); 2630 2631 const sal_uInt16 nLen( pAct->GetLen() == (sal_uInt16)STRING_LEN ? 2632 pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen() ); 2633 2634 // #i70897# Nothing to do, actually... 2635 if( nLen == 0 ) 2636 break; 2637 2638 // have to fit the text into the given 2639 // width. This is achieved by internally 2640 // generating a DX array, and uniformly 2641 // distributing the excess/insufficient width 2642 // to every logical character. 2643 ::boost::scoped_array< sal_Int32 > pDXArray( new sal_Int32[nLen] ); 2644 2645 rVDev.GetTextArray( pAct->GetText(), pDXArray.get(), 2646 pAct->GetIndex(), pAct->GetLen() ); 2647 2648 const sal_Int32 nWidthDifference( pAct->GetWidth() - pDXArray[ nLen-1 ] ); 2649 2650 // Last entry of pDXArray contains total width of the text 2651 sal_Int32* p=pDXArray.get(); 2652 for( sal_uInt16 i=1; i<=nLen; ++i ) 2653 { 2654 // calc ratio for every array entry, to 2655 // distribute rounding errors 'evenly' 2656 // across the characters. Note that each 2657 // entry represents the 'end' position of 2658 // the corresponding character, thus, we 2659 // let i run from 1 to nLen. 2660 *p++ += (sal_Int32)i*nWidthDifference/nLen; 2661 } 2662 2663 createTextAction( 2664 pAct->GetPoint(), 2665 sText, 2666 pAct->GetIndex(), 2667 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(), 2668 pDXArray.get(), 2669 rFactoryParms, 2670 bSubsettableActions ); 2671 } 2672 break; 2673 2674 default: 2675 OSL_ENSURE( false, 2676 "Unknown meta action type encountered" ); 2677 break; 2678 } 2679 2680 // increment action index (each mtf action counts _at 2681 // least_ one. Some count for more, therefore, 2682 // io_rCurrActionIndex is sometimes incremented by 2683 // pAct->getActionCount()-1 above, the -1 being the 2684 // correction for the unconditional increment here). 2685 ++io_rCurrActionIndex; 2686 } 2687 2688 return true; 2689 } 2690 2691 2692 namespace 2693 { 2694 class ActionRenderer 2695 { 2696 public: 2697 ActionRenderer( const ::basegfx::B2DHomMatrix& rTransformation ) : 2698 maTransformation( rTransformation ), 2699 mbRet( true ) 2700 { 2701 } 2702 2703 bool result() 2704 { 2705 return mbRet; 2706 } 2707 2708 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction ) 2709 { 2710 // ANDing the result. We want to fail if at least 2711 // one action failed. 2712 mbRet &= rAction.mpAction->render( maTransformation ); 2713 } 2714 2715 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction, 2716 const Action::Subset& rSubset ) 2717 { 2718 // ANDing the result. We want to fail if at least 2719 // one action failed. 2720 mbRet &= rAction.mpAction->render( maTransformation, 2721 rSubset ); 2722 } 2723 2724 private: 2725 ::basegfx::B2DHomMatrix maTransformation; 2726 bool mbRet; 2727 }; 2728 2729 class AreaQuery 2730 { 2731 public: 2732 AreaQuery( const ::basegfx::B2DHomMatrix& rTransformation ) : 2733 maTransformation( rTransformation ), 2734 maBounds() 2735 { 2736 } 2737 2738 bool result() 2739 { 2740 return true; // nothing can fail here 2741 } 2742 2743 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction ) 2744 { 2745 maBounds.expand( rAction.mpAction->getBounds( maTransformation ) ); 2746 } 2747 2748 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction, 2749 const Action::Subset& rSubset ) 2750 { 2751 maBounds.expand( rAction.mpAction->getBounds( maTransformation, 2752 rSubset ) ); 2753 } 2754 2755 ::basegfx::B2DRange getBounds() const 2756 { 2757 return maBounds; 2758 } 2759 2760 private: 2761 ::basegfx::B2DHomMatrix maTransformation; 2762 ::basegfx::B2DRange maBounds; 2763 }; 2764 2765 // Doing that via inline class. Compilers tend to not inline free 2766 // functions. 2767 struct UpperBoundActionIndexComparator 2768 { 2769 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rLHS, 2770 const ::cppcanvas::internal::ImplRenderer::MtfAction& rRHS ) 2771 { 2772 const sal_Int32 nLHSCount( rLHS.mpAction ? 2773 rLHS.mpAction->getActionCount() : 0 ); 2774 const sal_Int32 nRHSCount( rRHS.mpAction ? 2775 rRHS.mpAction->getActionCount() : 0 ); 2776 2777 // compare end of action range, to have an action selected 2778 // by lower_bound even if the requested index points in 2779 // the middle of the action's range 2780 return rLHS.mnOrigIndex + nLHSCount < rRHS.mnOrigIndex + nRHSCount; 2781 } 2782 }; 2783 2784 /** Algorithm to apply given functor to a subset range 2785 2786 @tpl Functor 2787 2788 Functor to call for each element of the subset 2789 range. Must provide the following method signatures: 2790 bool result() (returning false if operation failed) 2791 2792 */ 2793 template< typename Functor > bool 2794 forSubsetRange( Functor& rFunctor, 2795 ImplRenderer::ActionVector::const_iterator aRangeBegin, 2796 ImplRenderer::ActionVector::const_iterator aRangeEnd, 2797 sal_Int32 nStartIndex, 2798 sal_Int32 nEndIndex, 2799 const ImplRenderer::ActionVector::const_iterator& rEnd ) 2800 { 2801 if( aRangeBegin == aRangeEnd ) 2802 { 2803 // only a single action. Setup subset, and call functor 2804 Action::Subset aSubset; 2805 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ), 2806 nStartIndex - aRangeBegin->mnOrigIndex ); 2807 aSubset.mnSubsetEnd = ::std::min( aRangeBegin->mpAction->getActionCount(), 2808 nEndIndex - aRangeBegin->mnOrigIndex ); 2809 2810 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0, 2811 "ImplRenderer::forSubsetRange(): Invalid indices" ); 2812 2813 rFunctor( *aRangeBegin, aSubset ); 2814 } 2815 else 2816 { 2817 // more than one action. 2818 2819 // render partial first, full intermediate, and 2820 // partial last action 2821 Action::Subset aSubset; 2822 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ), 2823 nStartIndex - aRangeBegin->mnOrigIndex ); 2824 aSubset.mnSubsetEnd = aRangeBegin->mpAction->getActionCount(); 2825 2826 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0, 2827 "ImplRenderer::forSubsetRange(): Invalid indices" ); 2828 2829 rFunctor( *aRangeBegin, aSubset ); 2830 2831 // first action rendered, skip to next 2832 ++aRangeBegin; 2833 2834 // render full middle actions 2835 while( aRangeBegin != aRangeEnd ) 2836 rFunctor( *aRangeBegin++ ); 2837 2838 if( aRangeEnd == rEnd || 2839 aRangeEnd->mnOrigIndex > nEndIndex ) 2840 { 2841 // aRangeEnd denotes end of action vector, 2842 // 2843 // or 2844 // 2845 // nEndIndex references something _after_ 2846 // aRangeBegin, but _before_ aRangeEnd 2847 // 2848 // either way: no partial action left 2849 return rFunctor.result(); 2850 } 2851 2852 aSubset.mnSubsetBegin = 0; 2853 aSubset.mnSubsetEnd = nEndIndex - aRangeEnd->mnOrigIndex; 2854 2855 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0, 2856 "ImplRenderer::forSubsetRange(): Invalid indices" ); 2857 2858 rFunctor( *aRangeEnd, aSubset ); 2859 } 2860 2861 return rFunctor.result(); 2862 } 2863 } 2864 2865 bool ImplRenderer::getSubsetIndices( sal_Int32& io_rStartIndex, 2866 sal_Int32& io_rEndIndex, 2867 ActionVector::const_iterator& o_rRangeBegin, 2868 ActionVector::const_iterator& o_rRangeEnd ) const 2869 { 2870 ENSURE_OR_RETURN_FALSE( io_rStartIndex<=io_rEndIndex, 2871 "ImplRenderer::getSubsetIndices(): invalid action range" ); 2872 2873 ENSURE_OR_RETURN_FALSE( !maActions.empty(), 2874 "ImplRenderer::getSubsetIndices(): no actions to render" ); 2875 2876 const sal_Int32 nMinActionIndex( maActions.front().mnOrigIndex ); 2877 const sal_Int32 nMaxActionIndex( maActions.back().mnOrigIndex + 2878 maActions.back().mpAction->getActionCount() ); 2879 2880 // clip given range to permissible values (there might be 2881 // ranges before and behind the valid indices) 2882 io_rStartIndex = ::std::max( nMinActionIndex, 2883 io_rStartIndex ); 2884 io_rEndIndex = ::std::min( nMaxActionIndex, 2885 io_rEndIndex ); 2886 2887 if( io_rStartIndex == io_rEndIndex || 2888 io_rStartIndex > io_rEndIndex ) 2889 { 2890 // empty range, don't render anything. The second 2891 // condition e.g. happens if the requested range lies 2892 // fully before or behind the valid action indices. 2893 return false; 2894 } 2895 2896 2897 const ActionVector::const_iterator aBegin( maActions.begin() ); 2898 const ActionVector::const_iterator aEnd( maActions.end() ); 2899 2900 2901 // find start and end action 2902 // ========================= 2903 o_rRangeBegin = ::std::lower_bound( aBegin, aEnd, 2904 MtfAction( ActionSharedPtr(), io_rStartIndex ), 2905 UpperBoundActionIndexComparator() ); 2906 o_rRangeEnd = ::std::lower_bound( aBegin, aEnd, 2907 MtfAction( ActionSharedPtr(), io_rEndIndex ), 2908 UpperBoundActionIndexComparator() ); 2909 return true; 2910 } 2911 2912 2913 // Public methods 2914 // ==================================================================== 2915 2916 ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas, 2917 const GDIMetaFile& rMtf, 2918 const Parameters& rParams ) : 2919 CanvasGraphicHelper( rCanvas ), 2920 maActions() 2921 { 2922 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" ); 2923 2924 OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(), 2925 "ImplRenderer::ImplRenderer(): Invalid canvas" ); 2926 OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(), 2927 "ImplRenderer::ImplRenderer(): Invalid graphic device" ); 2928 2929 // make sure canvas and graphic device are valid; action 2930 // creation don't check that every time 2931 if( rCanvas.get() == NULL || 2932 !rCanvas->getUNOCanvas().is() || 2933 !rCanvas->getUNOCanvas()->getDevice().is() ) 2934 { 2935 // leave actions empty 2936 return; 2937 } 2938 2939 VectorOfOutDevStates aStateStack; 2940 2941 VirtualDevice aVDev; 2942 aVDev.EnableOutput( sal_False ); 2943 2944 // Setup VDev for state tracking and mapping 2945 // ========================================= 2946 2947 aVDev.SetMapMode( rMtf.GetPrefMapMode() ); 2948 2949 const Size aMtfSize( rMtf.GetPrefSize() ); 2950 const Size aMtfSizePixPre( aVDev.LogicToPixel( aMtfSize, 2951 rMtf.GetPrefMapMode() ) ); 2952 const Point aEmptyPt; 2953 const Point aMtfOriginPix( aVDev.LogicToPixel( aEmptyPt ) ); 2954 2955 // #i44110# correct null-sized output - there are shapes 2956 // which have zero size in at least one dimension 2957 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ), 2958 ::std::max( aMtfSizePixPre.Height(), 1L ) ); 2959 2960 sal_Int32 nCurrActions(0); 2961 ActionFactoryParameters aParms(aStateStack, 2962 rCanvas, 2963 aVDev, 2964 rParams, 2965 nCurrActions ); 2966 2967 // init state stack 2968 clearStateStack( aStateStack ); 2969 2970 // Setup local state, such that the metafile renders 2971 // itself into a one-by-one square at the origin for 2972 // identity view and render transformations 2973 getState( aStateStack ).transform.scale( 1.0 / aMtfSizePix.Width(), 2974 1.0 / aMtfSizePix.Height() ); 2975 2976 tools::calcLogic2PixelAffineTransform( getState( aStateStack ).mapModeTransform, 2977 aVDev ); 2978 2979 ColorSharedPtr pColor( getCanvas()->createColor() ); 2980 2981 // setup default text color to black 2982 getState( aStateStack ).textColor = 2983 getState( aStateStack ).textFillColor = 2984 getState( aStateStack ).textLineColor = pColor->getDeviceColor( 0x000000FF ); 2985 2986 // apply overrides from the Parameters struct 2987 if( rParams.maFillColor.is_initialized() ) 2988 { 2989 getState( aStateStack ).isFillColorSet = true; 2990 getState( aStateStack ).fillColor = pColor->getDeviceColor( *rParams.maFillColor ); 2991 } 2992 if( rParams.maLineColor.is_initialized() ) 2993 { 2994 getState( aStateStack ).isLineColorSet = true; 2995 getState( aStateStack ).lineColor = pColor->getDeviceColor( *rParams.maLineColor ); 2996 } 2997 if( rParams.maTextColor.is_initialized() ) 2998 { 2999 getState( aStateStack ).isTextFillColorSet = true; 3000 getState( aStateStack ).isTextLineColorSet = true; 3001 getState( aStateStack ).textColor = 3002 getState( aStateStack ).textFillColor = 3003 getState( aStateStack ).textLineColor = pColor->getDeviceColor( *rParams.maTextColor ); 3004 } 3005 if( rParams.maFontName.is_initialized() || 3006 rParams.maFontWeight.is_initialized() || 3007 rParams.maFontLetterForm.is_initialized() || 3008 rParams.maFontUnderline.is_initialized() || 3009 rParams.maFontProportion.is_initialized() ) 3010 { 3011 ::cppcanvas::internal::OutDevState& rState = getState( aStateStack ); 3012 3013 rState.xFont = createFont( rState.fontRotation, 3014 ::Font(), // default font 3015 aParms ); 3016 } 3017 3018 createActions( const_cast<GDIMetaFile&>(rMtf), // HACK(Q2): 3019 // we're 3020 // changing 3021 // the 3022 // current 3023 // action 3024 // in 3025 // createActions! 3026 aParms, 3027 true // TODO(P1): make subsettability configurable 3028 ); 3029 } 3030 3031 ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas, 3032 const BitmapEx& rBmpEx, 3033 const Parameters& rParams ) : 3034 CanvasGraphicHelper( rCanvas ), 3035 maActions() 3036 { 3037 // TODO(F3): property modification parameters are 3038 // currently ignored for Bitmaps 3039 (void)rParams; 3040 3041 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(bitmap)" ); 3042 3043 OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(), 3044 "ImplRenderer::ImplRenderer(): Invalid canvas" ); 3045 OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(), 3046 "ImplRenderer::ImplRenderer(): Invalid graphic device" ); 3047 3048 // make sure canvas and graphic device are valid; action 3049 // creation don't check that every time 3050 if( rCanvas.get() == NULL || 3051 !rCanvas->getUNOCanvas().is() || 3052 !rCanvas->getUNOCanvas()->getDevice().is() ) 3053 { 3054 // leave actions empty 3055 return; 3056 } 3057 3058 OutDevState aState; 3059 3060 const Size aBmpSize( rBmpEx.GetSizePixel() ); 3061 3062 // Setup local state, such that the bitmap renders itself 3063 // into a one-by-one square for identity view and render 3064 // transformations 3065 aState.transform.scale( 1.0 / aBmpSize.Width(), 3066 1.0 / aBmpSize.Height() ); 3067 3068 // create a single action for the provided BitmapEx 3069 maActions.push_back( 3070 MtfAction( 3071 BitmapActionFactory::createBitmapAction( 3072 rBmpEx, 3073 ::basegfx::B2DPoint(), 3074 rCanvas, 3075 aState), 3076 0 ) ); 3077 } 3078 3079 ImplRenderer::~ImplRenderer() 3080 { 3081 } 3082 3083 bool ImplRenderer::drawSubset( sal_Int32 nStartIndex, 3084 sal_Int32 nEndIndex ) const 3085 { 3086 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::drawSubset()" ); 3087 3088 ActionVector::const_iterator aRangeBegin; 3089 ActionVector::const_iterator aRangeEnd; 3090 3091 try 3092 { 3093 if( !getSubsetIndices( nStartIndex, nEndIndex, 3094 aRangeBegin, aRangeEnd ) ) 3095 return true; // nothing to render (but _that_ was successful) 3096 3097 // now, aRangeBegin references the action in which the 3098 // subset rendering must start, and aRangeEnd references 3099 // the action in which the subset rendering must end (it 3100 // might also end right at the start of the referenced 3101 // action, such that zero of that action needs to be 3102 // rendered). 3103 3104 3105 // render subset of actions 3106 // ======================== 3107 3108 ::basegfx::B2DHomMatrix aMatrix; 3109 ::canvas::tools::getRenderStateTransform( aMatrix, 3110 getRenderState() ); 3111 3112 ActionRenderer aRenderer( aMatrix ); 3113 3114 return forSubsetRange( aRenderer, 3115 aRangeBegin, 3116 aRangeEnd, 3117 nStartIndex, 3118 nEndIndex, 3119 maActions.end() ); 3120 } 3121 catch( uno::Exception& ) 3122 { 3123 OSL_ENSURE( false, 3124 rtl::OUStringToOString( 3125 comphelper::anyToString( cppu::getCaughtException() ), 3126 RTL_TEXTENCODING_UTF8 ).getStr() ); 3127 3128 // convert error to return value 3129 return false; 3130 } 3131 } 3132 3133 ::basegfx::B2DRange ImplRenderer::getSubsetArea( sal_Int32 nStartIndex, 3134 sal_Int32 nEndIndex ) const 3135 { 3136 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::getSubsetArea()" ); 3137 3138 ActionVector::const_iterator aRangeBegin; 3139 ActionVector::const_iterator aRangeEnd; 3140 3141 if( !getSubsetIndices( nStartIndex, nEndIndex, 3142 aRangeBegin, aRangeEnd ) ) 3143 return ::basegfx::B2DRange(); // nothing to render -> empty range 3144 3145 // now, aRangeBegin references the action in which the 3146 // subset querying must start, and aRangeEnd references 3147 // the action in which the subset querying must end (it 3148 // might also end right at the start of the referenced 3149 // action, such that zero of that action needs to be 3150 // queried). 3151 3152 3153 // query bounds for subset of actions 3154 // ================================== 3155 3156 ::basegfx::B2DHomMatrix aMatrix; 3157 ::canvas::tools::getRenderStateTransform( aMatrix, 3158 getRenderState() ); 3159 3160 AreaQuery aQuery( aMatrix ); 3161 forSubsetRange( aQuery, 3162 aRangeBegin, 3163 aRangeEnd, 3164 nStartIndex, 3165 nEndIndex, 3166 maActions.end() ); 3167 3168 return aQuery.getBounds(); 3169 } 3170 3171 bool ImplRenderer::draw() const 3172 { 3173 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::draw()" ); 3174 3175 ::basegfx::B2DHomMatrix aMatrix; 3176 ::canvas::tools::getRenderStateTransform( aMatrix, 3177 getRenderState() ); 3178 3179 try 3180 { 3181 return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer( aMatrix ) ).result(); 3182 } 3183 catch( uno::Exception& ) 3184 { 3185 OSL_ENSURE( false, 3186 rtl::OUStringToOString( 3187 comphelper::anyToString( cppu::getCaughtException() ), 3188 RTL_TEXTENCODING_UTF8 ).getStr() ); 3189 3190 return false; 3191 } 3192 } 3193 } 3194 } 3195