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 aGradInfo = basegfx::tools::createLinearODFGradientInfo( 692 aBounds, 693 nSteps, 694 fBorder, 695 fRotation); 696 // map odf to svg gradient orientation - x 697 // instead of y direction 698 aGradInfo.setTextureTransform(aGradInfo.getTextureTransform() * 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 aGradInfo = basegfx::tools::createAxialODFGradientInfo( 718 aBounds, 719 nSteps, 720 fAxialBorder, 721 fRotation); 722 // map odf to svg gradient orientation - x 723 // instead of y direction 724 aGradInfo.setTextureTransform(aGradInfo.getTextureTransform() * aRot90); 725 726 // map odf axial gradient to 3-stop linear 727 // gradient - shift left by 0.5 728 basegfx::B2DHomMatrix aShift; 729 730 aShift.translate(-0.5,0); 731 aGradInfo.setTextureTransform(aGradInfo.getTextureTransform() * aShift); 732 aGradientService = rtl::OUString::createFromAscii("LinearGradient"); 733 break; 734 } 735 736 case GRADIENT_RADIAL: 737 aGradInfo = basegfx::tools::createRadialODFGradientInfo( 738 aBounds, 739 aOffset, 740 nSteps, 741 fBorder); 742 aGradientService = rtl::OUString::createFromAscii("EllipticalGradient"); 743 break; 744 745 case GRADIENT_ELLIPTICAL: 746 aGradInfo = basegfx::tools::createEllipticalODFGradientInfo( 747 aBounds, 748 aOffset, 749 nSteps, 750 fBorder, 751 fRotation); 752 aGradientService = rtl::OUString::createFromAscii("EllipticalGradient"); 753 break; 754 755 case GRADIENT_SQUARE: 756 aGradInfo = basegfx::tools::createSquareODFGradientInfo( 757 aBounds, 758 aOffset, 759 nSteps, 760 fBorder, 761 fRotation); 762 aGradientService = rtl::OUString::createFromAscii("RectangularGradient"); 763 break; 764 765 case GRADIENT_RECT: 766 aGradInfo = basegfx::tools::createRectangularODFGradientInfo( 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.setTextureTransform( 789 basegfx::tools::createTranslateB2DHomMatrix( 790 aBounds.getMinX(), 791 aBounds.getMinY()) * aGradInfo.getTextureTransform()); 792 ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform, 793 aGradInfo.getTextureTransform() ); 794 795 uno::Sequence<uno::Any> args(3); 796 beans::PropertyValue aProp; 797 aProp.Name = rtl::OUString::createFromAscii("Colors"); 798 aProp.Value <<= aColors; 799 args[0] <<= aProp; 800 aProp.Name = rtl::OUString::createFromAscii("Stops"); 801 aProp.Value <<= aStops; 802 args[1] <<= aProp; 803 aProp.Name = rtl::OUString::createFromAscii("AspectRatio"); 804 aProp.Value <<= aGradInfo.getAspectRatio(); 805 args[2] <<= aProp; 806 807 aTexture.Gradient.set( 808 xFactory->createInstanceWithArguments(aGradientService, 809 args), 810 uno::UNO_QUERY); 811 if( aTexture.Gradient.is() ) 812 { 813 ActionSharedPtr pPolyAction( 814 internal::PolyPolyActionFactory::createPolyPolyAction( 815 aDevicePoly, 816 rParms.mrCanvas, 817 getState( rParms.mrStates ), 818 aTexture ) ); 819 820 if( pPolyAction ) 821 { 822 maActions.push_back( 823 MtfAction( 824 pPolyAction, 825 rParms.mrCurrActionIndex ) ); 826 827 rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1; 828 } 829 830 // done, using native gradients 831 return; 832 } 833 } 834 } 835 836 // cannot currently use native canvas gradients, as a 837 // finite step size is given (this funny feature is not 838 // supported by the XCanvas API) 839 pushState( rParms.mrStates, PUSH_ALL ); 840 841 if( !bIsPolygonRectangle ) 842 { 843 // only clip, if given polygon is not a rectangle in 844 // the first place (the gradient is always limited to 845 // the given bound rect) 846 updateClipping( 847 aDevicePoly, 848 rParms, 849 true ); 850 } 851 852 GDIMetaFile aTmpMtf; 853 rParms.mrVDev.AddGradientActions( rPoly.GetBoundRect(), 854 rGradient, 855 aTmpMtf ); 856 857 createActions( aTmpMtf, rParms, bSubsettableActions ); 858 859 popState( rParms.mrStates ); 860 } 861 862 uno::Reference< rendering::XCanvasFont > ImplRenderer::createFont( double& o_rFontRotation, 863 const ::Font& rFont, 864 const ActionFactoryParameters& rParms ) const 865 { 866 rendering::FontRequest aFontRequest; 867 868 if( rParms.mrParms.maFontName.is_initialized() ) 869 aFontRequest.FontDescription.FamilyName = *rParms.mrParms.maFontName; 870 else 871 aFontRequest.FontDescription.FamilyName = rFont.GetName(); 872 873 aFontRequest.FontDescription.StyleName = rFont.GetStyleName(); 874 875 aFontRequest.FontDescription.IsSymbolFont = (rFont.GetCharSet() == RTL_TEXTENCODING_SYMBOL) ? util::TriState_YES : util::TriState_NO; 876 aFontRequest.FontDescription.IsVertical = rFont.IsVertical() ? util::TriState_YES : util::TriState_NO; 877 878 // TODO(F2): improve vclenum->panose conversion 879 aFontRequest.FontDescription.FontDescription.Weight = 880 rParms.mrParms.maFontWeight.is_initialized() ? 881 *rParms.mrParms.maFontWeight : 882 ::canvas::tools::numeric_cast<sal_Int8>( ::basegfx::fround( rFont.GetWeight() ) ); 883 aFontRequest.FontDescription.FontDescription.Letterform = 884 rParms.mrParms.maFontLetterForm.is_initialized() ? 885 *rParms.mrParms.maFontLetterForm : 886 (rFont.GetItalic() == ITALIC_NONE) ? 0 : 9; 887 aFontRequest.FontDescription.FontDescription.Proportion = 888 rParms.mrParms.maFontProportion.is_initialized() ? 889 *rParms.mrParms.maFontProportion : 890 (rFont.GetPitch() == PITCH_FIXED) 891 ? rendering::PanoseProportion::MONO_SPACED 892 : rendering::PanoseProportion::ANYTHING; 893 894 LanguageType aLang = rFont.GetLanguage(); 895 aFontRequest.Locale = MsLangId::convertLanguageToLocale(aLang, false); 896 897 // setup state-local text transformation, 898 // if the font be rotated 899 const short nFontAngle( rFont.GetOrientation() ); 900 if( nFontAngle != 0 ) 901 { 902 // set to unity transform rotated by font angle 903 const double nAngle( nFontAngle * (F_PI / 1800.0) ); 904 o_rFontRotation = -nAngle; 905 } 906 else 907 { 908 o_rFontRotation = 0.0; 909 } 910 911 geometry::Matrix2D aFontMatrix; 912 ::canvas::tools::setIdentityMatrix2D( aFontMatrix ); 913 914 // TODO(F2): use correct scale direction, font 915 // height might be width or anything else 916 917 // TODO(Q3): This code smells of programming by 918 // coincidence (the next two if statements) 919 const ::Size rFontSizeLog( rFont.GetSize() ); 920 const sal_Int32 nFontWidthLog = rFontSizeLog.Width(); 921 if( nFontWidthLog != 0 ) 922 { 923 ::Font aTestFont = rFont; 924 aTestFont.SetWidth( 0 ); 925 sal_Int32 nNormalWidth = rParms.mrVDev.GetFontMetric( aTestFont ).GetWidth(); 926 if( nNormalWidth != nFontWidthLog ) 927 if( nNormalWidth ) 928 aFontMatrix.m00 = (double)nFontWidthLog / nNormalWidth; 929 } 930 931 // #i52608# apply map mode scale also to font matrix - an 932 // anisotrophic mapmode must be reflected in an 933 // anisotrophic font matrix scale. 934 const OutDevState& rState( getState( rParms.mrStates ) ); 935 if( !::basegfx::fTools::equal( 936 rState.mapModeTransform.get(0,0), 937 rState.mapModeTransform.get(1,1)) ) 938 { 939 const double nScaleX( rState.mapModeTransform.get(0,0) ); 940 const double nScaleY( rState.mapModeTransform.get(1,1) ); 941 942 // note: no reason to check for division by zero, we 943 // always have the value closer (or equal) to zero as 944 // the nominator. 945 if( fabs(nScaleX) < fabs(nScaleY) ) 946 aFontMatrix.m00 *= nScaleX / nScaleY; 947 else 948 aFontMatrix.m11 *= nScaleY / nScaleX; 949 } 950 aFontRequest.CellSize = (rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize(rFontSizeLog)).getY(); 951 952 return rParms.mrCanvas->getUNOCanvas()->createFont( aFontRequest, 953 uno::Sequence< beans::PropertyValue >(), 954 aFontMatrix ); 955 } 956 957 // create text effects such as shadow/relief/embossed 958 void ImplRenderer::createTextAction( const ::Point& rStartPoint, 959 const String rString, 960 int nIndex, 961 int nLength, 962 const sal_Int32* pCharWidths, 963 const ActionFactoryParameters& rParms, 964 bool bSubsettableActions ) 965 { 966 ENSURE_OR_THROW( nIndex >= 0 && nLength <= rString.Len() + nIndex, 967 "ImplRenderer::createTextWithEffectsAction(): Invalid text index" ); 968 969 if( !nLength ) 970 return; // zero-length text, no visible output 971 972 const OutDevState& rState( getState( rParms.mrStates ) ); 973 974 // TODO(F2): implement all text effects 975 // if( rState.textAlignment ); // TODO(F2): NYI 976 977 ::Color aShadowColor( COL_AUTO ); 978 ::Color aReliefColor( COL_AUTO ); 979 ::Size aShadowOffset; 980 ::Size aReliefOffset; 981 982 uno::Reference<rendering::XColorSpace> xColorSpace( 983 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() ); 984 985 if( rState.isTextEffectShadowSet ) 986 { 987 // calculate shadow offset (similar to outdev3.cxx) 988 // TODO(F3): better match with outdev3.cxx 989 sal_Int32 nShadowOffset = static_cast<sal_Int32>(1.5 + ((rParms.mrVDev.GetFont().GetHeight()-24.0)/24.0)); 990 if( nShadowOffset < 1 ) 991 nShadowOffset = 1; 992 993 aShadowOffset.setWidth( nShadowOffset ); 994 aShadowOffset.setHeight( nShadowOffset ); 995 996 // determine shadow color (from outdev3.cxx) 997 ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor( 998 rState.textColor, xColorSpace ); 999 bool bIsDark = (aTextColor.GetColor() == COL_BLACK) 1000 || (aTextColor.GetLuminance() < 8); 1001 1002 aShadowColor = bIsDark ? COL_LIGHTGRAY : COL_BLACK; 1003 aShadowColor.SetTransparency( aTextColor.GetTransparency() ); 1004 } 1005 1006 if( rState.textReliefStyle ) 1007 { 1008 // calculate relief offset (similar to outdev3.cxx) 1009 sal_Int32 nReliefOffset = rParms.mrVDev.PixelToLogic( Size( 1, 1 ) ).Height(); 1010 nReliefOffset += nReliefOffset/2; 1011 if( nReliefOffset < 1 ) 1012 nReliefOffset = 1; 1013 1014 if( rState.textReliefStyle == RELIEF_ENGRAVED ) 1015 nReliefOffset = -nReliefOffset; 1016 1017 aReliefOffset.setWidth( nReliefOffset ); 1018 aReliefOffset.setHeight( nReliefOffset ); 1019 1020 // determine relief color (from outdev3.cxx) 1021 ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor( 1022 rState.textColor, xColorSpace ); 1023 1024 aReliefColor = ::Color( COL_LIGHTGRAY ); 1025 1026 // we don't have a automatic color, so black is always 1027 // drawn on white (literally copied from 1028 // vcl/source/gdi/outdev3.cxx) 1029 if( aTextColor.GetColor() == COL_BLACK ) 1030 { 1031 aTextColor = ::Color( COL_WHITE ); 1032 getState( rParms.mrStates ).textColor = 1033 ::vcl::unotools::colorToDoubleSequence( 1034 aTextColor, xColorSpace ); 1035 } 1036 1037 if( aTextColor.GetColor() == COL_WHITE ) 1038 aReliefColor = ::Color( COL_BLACK ); 1039 aReliefColor.SetTransparency( aTextColor.GetTransparency() ); 1040 } 1041 1042 // create the actual text action 1043 ActionSharedPtr pTextAction( 1044 TextActionFactory::createTextAction( 1045 rStartPoint, 1046 aReliefOffset, 1047 aReliefColor, 1048 aShadowOffset, 1049 aShadowColor, 1050 rString, 1051 nIndex, 1052 nLength, 1053 pCharWidths, 1054 rParms.mrVDev, 1055 rParms.mrCanvas, 1056 rState, 1057 rParms.mrParms, 1058 bSubsettableActions ) ); 1059 1060 ActionSharedPtr pStrikeoutTextAction; 1061 1062 if ( rState.textStrikeoutStyle == STRIKEOUT_X || rState.textStrikeoutStyle == STRIKEOUT_SLASH ) 1063 { 1064 long nWidth = rParms.mrVDev.GetTextWidth( rString,nIndex,nLength ); 1065 1066 xub_Unicode pChars[5]; 1067 if ( rState.textStrikeoutStyle == STRIKEOUT_X ) 1068 pChars[0] = 'X'; 1069 else 1070 pChars[0] = '/'; 1071 pChars[3]=pChars[2]=pChars[1]=pChars[0]; 1072 1073 long nStrikeoutWidth = nWidth; 1074 String aStrikeoutTest( pChars, 4 ); 1075 1076 if( aStrikeoutTest.Len() ) 1077 { 1078 nStrikeoutWidth = ( rParms.mrVDev.GetTextWidth( aStrikeoutTest ) + 2 ) / 4; 1079 aStrikeoutTest.Erase(); 1080 1081 if( nStrikeoutWidth <= 0 ) 1082 nStrikeoutWidth = 1; 1083 } 1084 1085 long nMaxWidth = nStrikeoutWidth/2; 1086 if ( nMaxWidth < 2 ) 1087 nMaxWidth = 2; 1088 nMaxWidth += nWidth + 1; 1089 1090 long nFullStrikeoutWidth = 0; 1091 String aStrikeoutText( pChars, 0 ); 1092 while( (nFullStrikeoutWidth+=nStrikeoutWidth ) < nMaxWidth+1 ) 1093 aStrikeoutText += pChars[0]; 1094 1095 1096 sal_Int32 nStartPos = 0; 1097 xub_StrLen nLen = aStrikeoutText.Len(); 1098 1099 if( nLen ) 1100 { 1101 long nInterval = ( nWidth - nStrikeoutWidth * nLen ) / nLen; 1102 nStrikeoutWidth += nInterval; 1103 sal_Int32* pStrikeoutCharWidths = new sal_Int32[nLen]; 1104 1105 for ( int i = 0;i<nLen; i++) 1106 { 1107 pStrikeoutCharWidths[i] = nStrikeoutWidth; 1108 } 1109 1110 for ( int i = 1;i< nLen; i++ ) 1111 { 1112 pStrikeoutCharWidths[ i ] += pStrikeoutCharWidths[ i-1 ]; 1113 } 1114 1115 pStrikeoutTextAction = 1116 TextActionFactory::createTextAction( 1117 rStartPoint, 1118 aReliefOffset, 1119 aReliefColor, 1120 aShadowOffset, 1121 aShadowColor, 1122 aStrikeoutText, 1123 nStartPos, 1124 aStrikeoutText.Len(), 1125 pStrikeoutCharWidths, 1126 rParms.mrVDev, 1127 rParms.mrCanvas, 1128 rState, 1129 rParms.mrParms, 1130 bSubsettableActions ) ; 1131 } 1132 } 1133 1134 if( pTextAction ) 1135 { 1136 maActions.push_back( 1137 MtfAction( 1138 pTextAction, 1139 rParms.mrCurrActionIndex ) ); 1140 1141 if ( pStrikeoutTextAction ) 1142 { 1143 maActions.push_back( 1144 MtfAction( 1145 pStrikeoutTextAction, 1146 rParms.mrCurrActionIndex ) ); 1147 } 1148 1149 rParms.mrCurrActionIndex += pTextAction->getActionCount()-1; 1150 } 1151 } 1152 1153 void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon& rClipPoly, 1154 const ActionFactoryParameters& rParms, 1155 bool bIntersect ) 1156 { 1157 ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) ); 1158 ::basegfx::B2DPolyPolygon aClipPoly( rClipPoly ); 1159 1160 const bool bEmptyClipRect( rState.clipRect.IsEmpty() ); 1161 const bool bEmptyClipPoly( rState.clip.count() == 0 ); 1162 1163 ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect, 1164 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" ); 1165 1166 if( !bIntersect || 1167 (bEmptyClipRect && bEmptyClipPoly) ) 1168 { 1169 rState.clip = rClipPoly; 1170 } 1171 else 1172 { 1173 if( !bEmptyClipRect ) 1174 { 1175 // TODO(P3): Use Liang-Barsky polygon clip here, 1176 // after all, one object is just a rectangle! 1177 1178 // convert rect to polygon beforehand, must revert 1179 // to general polygon clipping here. 1180 rState.clip = ::basegfx::B2DPolyPolygon( 1181 ::basegfx::tools::createPolygonFromRect( 1182 // #121100# VCL rectangular clips always 1183 // include one more pixel to the right 1184 // and the bottom 1185 ::basegfx::B2DRectangle( rState.clipRect.Left(), 1186 rState.clipRect.Top(), 1187 rState.clipRect.Right()+1, 1188 rState.clipRect.Bottom()+1 ) ) ); 1189 } 1190 1191 // AW: Simplified 1192 rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon( 1193 aClipPoly, rState.clip, true, false); 1194 } 1195 1196 // by now, our clip resides in the OutDevState::clip 1197 // poly-polygon. 1198 rState.clipRect.SetEmpty(); 1199 1200 if( rState.clip.count() == 0 ) 1201 { 1202 if( rState.clipRect.IsEmpty() ) 1203 { 1204 rState.xClipPoly.clear(); 1205 } 1206 else 1207 { 1208 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 1209 rParms.mrCanvas->getUNOCanvas()->getDevice(), 1210 ::basegfx::B2DPolyPolygon( 1211 ::basegfx::tools::createPolygonFromRect( 1212 // #121100# VCL rectangular clips 1213 // always include one more pixel to 1214 // the right and the bottom 1215 ::basegfx::B2DRectangle( rState.clipRect.Left(), 1216 rState.clipRect.Top(), 1217 rState.clipRect.Right()+1, 1218 rState.clipRect.Bottom()+1 ) ) ) ); 1219 } 1220 } 1221 else 1222 { 1223 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 1224 rParms.mrCanvas->getUNOCanvas()->getDevice(), 1225 rState.clip ); 1226 } 1227 } 1228 1229 void ImplRenderer::updateClipping( const ::Rectangle& rClipRect, 1230 const ActionFactoryParameters& rParms, 1231 bool bIntersect ) 1232 { 1233 ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) ); 1234 1235 const bool bEmptyClipRect( rState.clipRect.IsEmpty() ); 1236 const bool bEmptyClipPoly( rState.clip.count() == 0 ); 1237 1238 ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect, 1239 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" ); 1240 1241 if( !bIntersect || 1242 (bEmptyClipRect && bEmptyClipPoly) ) 1243 { 1244 rState.clipRect = rClipRect; 1245 rState.clip.clear(); 1246 } 1247 else if( bEmptyClipPoly ) 1248 { 1249 rState.clipRect.Intersection( rClipRect ); 1250 rState.clip.clear(); 1251 } 1252 else 1253 { 1254 // TODO(P3): Handle a fourth case here, when all clip 1255 // polygons are rectangular, once B2DMultiRange's 1256 // sweep line implementation is done. 1257 1258 // general case: convert to polygon and clip 1259 // ----------------------------------------- 1260 1261 // convert rect to polygon beforehand, must revert 1262 // to general polygon clipping here. 1263 ::basegfx::B2DPolyPolygon aClipPoly( 1264 ::basegfx::tools::createPolygonFromRect( 1265 ::basegfx::B2DRectangle( rClipRect.Left(), 1266 rClipRect.Top(), 1267 rClipRect.Right(), 1268 rClipRect.Bottom() ) ) ); 1269 1270 rState.clipRect.SetEmpty(); 1271 1272 // AW: Simplified 1273 rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon( 1274 aClipPoly, rState.clip, true, false); 1275 } 1276 1277 if( rState.clip.count() == 0 ) 1278 { 1279 if( rState.clipRect.IsEmpty() ) 1280 { 1281 rState.xClipPoly.clear(); 1282 } 1283 else 1284 { 1285 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 1286 rParms.mrCanvas->getUNOCanvas()->getDevice(), 1287 ::basegfx::B2DPolyPolygon( 1288 ::basegfx::tools::createPolygonFromRect( 1289 // #121100# VCL rectangular clips 1290 // always include one more pixel to 1291 // the right and the bottom 1292 ::basegfx::B2DRectangle( rState.clipRect.Left(), 1293 rState.clipRect.Top(), 1294 rState.clipRect.Right()+1, 1295 rState.clipRect.Bottom()+1 ) ) ) ); 1296 } 1297 } 1298 else 1299 { 1300 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 1301 rParms.mrCanvas->getUNOCanvas()->getDevice(), 1302 rState.clip ); 1303 } 1304 } 1305 1306 bool ImplRenderer::createActions( GDIMetaFile& rMtf, 1307 const ActionFactoryParameters& rFactoryParms, 1308 bool bSubsettableActions ) 1309 { 1310 /* TODO(P2): interpret mtf-comments 1311 ================================ 1312 1313 - gradient fillings (do that via comments) 1314 1315 - think about mapping. _If_ we do everything in logical 1316 coordinates (which would solve the probs for stroke 1317 widths and text offsets), then we would have to 1318 recalc scaling for every drawing operation. This is 1319 because the outdev map mode might change at any time. 1320 Also keep in mind, that, although we've double precision 1321 float arithmetic now, different offsets might still 1322 generate different roundings (aka 1323 'OutputDevice::SetPixelOffset()) 1324 1325 */ 1326 1327 // alias common parameters 1328 VectorOfOutDevStates& rStates(rFactoryParms.mrStates); 1329 const CanvasSharedPtr& rCanvas(rFactoryParms.mrCanvas); 1330 ::VirtualDevice& rVDev(rFactoryParms.mrVDev); 1331 const Parameters& rParms(rFactoryParms.mrParms); 1332 sal_Int32& io_rCurrActionIndex(rFactoryParms.mrCurrActionIndex); 1333 1334 1335 // Loop over every metaaction 1336 // ========================== 1337 MetaAction* pCurrAct; 1338 1339 // TODO(P1): think about caching 1340 for( pCurrAct=rMtf.FirstAction(); 1341 pCurrAct; 1342 pCurrAct = rMtf.NextAction() ) 1343 { 1344 // execute every action, to keep VDev state up-to-date 1345 // currently used only for 1346 // - the map mode 1347 // - the line/fill color when processing a META_TRANSPARENT_ACTION 1348 // - SetFont to process font metric specific actions 1349 pCurrAct->Execute( &rVDev ); 1350 1351 switch( pCurrAct->GetType() ) 1352 { 1353 // ------------------------------------------------------------ 1354 1355 // In the first part of this monster-switch, we 1356 // handle all state-changing meta actions. These 1357 // are all handled locally. 1358 1359 // ------------------------------------------------------------ 1360 1361 case META_PUSH_ACTION: 1362 { 1363 MetaPushAction* pPushAction = static_cast<MetaPushAction*>(pCurrAct); 1364 pushState( rStates, 1365 pPushAction->GetFlags() ); 1366 } 1367 break; 1368 1369 case META_POP_ACTION: 1370 popState( rStates ); 1371 break; 1372 1373 case META_TEXTLANGUAGE_ACTION: 1374 // FALLTHROUGH intended 1375 case META_REFPOINT_ACTION: 1376 // handled via pCurrAct->Execute( &rVDev ) 1377 break; 1378 1379 case META_MAPMODE_ACTION: 1380 // modify current mapModeTransformation 1381 // transformation, such that subsequent 1382 // coordinates map correctly 1383 tools::calcLogic2PixelAffineTransform( getState( rStates ).mapModeTransform, 1384 rVDev ); 1385 break; 1386 1387 // monitor clip regions, to assemble clip polygon on our own 1388 case META_CLIPREGION_ACTION: 1389 { 1390 MetaClipRegionAction* pClipAction = static_cast<MetaClipRegionAction*>(pCurrAct); 1391 1392 if( !pClipAction->IsClipping() ) 1393 { 1394 // clear clipping 1395 getState( rStates ).clip.clear(); 1396 } 1397 else 1398 { 1399 if( !pClipAction->GetRegion().HasPolyPolygon() ) 1400 { 1401 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip " 1402 "region encountered, falling back to bounding box!" ); 1403 1404 // #121806# explicitely kept integer 1405 Rectangle aClipRect( 1406 rVDev.LogicToPixel( 1407 pClipAction->GetRegion().GetBoundRect() ) ); 1408 1409 // intersect current clip with given rect 1410 updateClipping( 1411 aClipRect, 1412 rFactoryParms, 1413 false ); 1414 } 1415 else 1416 { 1417 // set new clip polygon (don't intersect 1418 // with old one, just set it) 1419 1420 // #121806# explicitely kept integer 1421 updateClipping( 1422 rVDev.LogicToPixel( 1423 pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(), 1424 rFactoryParms, 1425 false ); 1426 } 1427 } 1428 1429 break; 1430 } 1431 1432 case META_ISECTRECTCLIPREGION_ACTION: 1433 { 1434 MetaISectRectClipRegionAction* pClipAction = static_cast<MetaISectRectClipRegionAction*>(pCurrAct); 1435 1436 // #121806# explicitely kept integer 1437 Rectangle aClipRect( 1438 rVDev.LogicToPixel( pClipAction->GetRect() ) ); 1439 1440 // intersect current clip with given rect 1441 updateClipping( 1442 aClipRect, 1443 rFactoryParms, 1444 true ); 1445 1446 break; 1447 } 1448 1449 case META_ISECTREGIONCLIPREGION_ACTION: 1450 { 1451 MetaISectRegionClipRegionAction* pClipAction = static_cast<MetaISectRegionClipRegionAction*>(pCurrAct); 1452 1453 if( !pClipAction->GetRegion().HasPolyPolygon() ) 1454 { 1455 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip " 1456 "region encountered, falling back to bounding box!" ); 1457 1458 // #121806# explicitely kept integer 1459 Rectangle aClipRect( 1460 rVDev.LogicToPixel( pClipAction->GetRegion().GetBoundRect() ) ); 1461 1462 // intersect current clip with given rect 1463 updateClipping( 1464 aClipRect, 1465 rFactoryParms, 1466 true ); 1467 } 1468 else 1469 { 1470 // intersect current clip with given clip polygon 1471 1472 // #121806# explicitely kept integer 1473 updateClipping( 1474 rVDev.LogicToPixel( 1475 pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(), 1476 rFactoryParms, 1477 true ); 1478 } 1479 1480 break; 1481 } 1482 1483 case META_MOVECLIPREGION_ACTION: 1484 // TODO(F2): NYI 1485 break; 1486 1487 case META_LINECOLOR_ACTION: 1488 if( !rParms.maLineColor.is_initialized() ) 1489 { 1490 setStateColor( static_cast<MetaLineColorAction*>(pCurrAct), 1491 getState( rStates ).isLineColorSet, 1492 getState( rStates ).lineColor, 1493 rCanvas ); 1494 } 1495 break; 1496 1497 case META_FILLCOLOR_ACTION: 1498 if( !rParms.maFillColor.is_initialized() ) 1499 { 1500 setStateColor( static_cast<MetaFillColorAction*>(pCurrAct), 1501 getState( rStates ).isFillColorSet, 1502 getState( rStates ).fillColor, 1503 rCanvas ); 1504 } 1505 break; 1506 1507 case META_TEXTCOLOR_ACTION: 1508 { 1509 if( !rParms.maTextColor.is_initialized() ) 1510 { 1511 // Text color is set unconditionally, thus, no 1512 // use of setStateColor here 1513 ::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() ); 1514 1515 // force alpha part of color to 1516 // opaque. transparent painting is done 1517 // explicitely via META_TRANSPARENT_ACTION 1518 aColor.SetTransparency(0); 1519 1520 getState( rStates ).textColor = 1521 ::vcl::unotools::colorToDoubleSequence( 1522 aColor, 1523 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() ); 1524 } 1525 } 1526 break; 1527 1528 case META_TEXTFILLCOLOR_ACTION: 1529 if( !rParms.maTextColor.is_initialized() ) 1530 { 1531 setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct), 1532 getState( rStates ).isTextFillColorSet, 1533 getState( rStates ).textFillColor, 1534 rCanvas ); 1535 } 1536 break; 1537 1538 case META_TEXTLINECOLOR_ACTION: 1539 if( !rParms.maTextColor.is_initialized() ) 1540 { 1541 setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct), 1542 getState( rStates ).isTextLineColorSet, 1543 getState( rStates ).textLineColor, 1544 rCanvas ); 1545 } 1546 break; 1547 1548 case META_TEXTALIGN_ACTION: 1549 { 1550 ::cppcanvas::internal::OutDevState& rState = getState( rStates ); 1551 const TextAlign eTextAlign( static_cast<MetaTextAlignAction*>(pCurrAct)->GetTextAlign() ); 1552 1553 rState.textReferencePoint = eTextAlign; 1554 } 1555 break; 1556 1557 case META_FONT_ACTION: 1558 { 1559 ::cppcanvas::internal::OutDevState& rState = getState( rStates ); 1560 const ::Font& rFont( static_cast<MetaFontAction*>(pCurrAct)->GetFont() ); 1561 1562 rState.xFont = createFont( rState.fontRotation, 1563 rFont, 1564 rFactoryParms ); 1565 1566 // TODO(Q2): define and use appropriate enumeration types 1567 rState.textReliefStyle = (sal_Int8)rFont.GetRelief(); 1568 rState.textOverlineStyle = (sal_Int8)rFont.GetOverline(); 1569 rState.textUnderlineStyle = rParms.maFontUnderline.is_initialized() ? 1570 (*rParms.maFontUnderline ? (sal_Int8)UNDERLINE_SINGLE : (sal_Int8)UNDERLINE_NONE) : 1571 (sal_Int8)rFont.GetUnderline(); 1572 rState.textStrikeoutStyle = (sal_Int8)rFont.GetStrikeout(); 1573 rState.textEmphasisMarkStyle = (sal_Int8)rFont.GetEmphasisMark(); 1574 rState.isTextEffectShadowSet = (rFont.IsShadow() != sal_False); 1575 rState.isTextWordUnderlineSet = (rFont.IsWordLineMode() != sal_False); 1576 rState.isTextOutlineModeSet = (rFont.IsOutline() != sal_False); 1577 } 1578 break; 1579 1580 case META_RASTEROP_ACTION: 1581 // TODO(F2): NYI 1582 break; 1583 1584 case META_LAYOUTMODE_ACTION: 1585 { 1586 // TODO(F2): A lot is missing here 1587 int nLayoutMode = static_cast<MetaLayoutModeAction*>(pCurrAct)->GetLayoutMode(); 1588 ::cppcanvas::internal::OutDevState& rState = getState( rStates ); 1589 switch( nLayoutMode & (TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_BIDI_STRONG) ) 1590 { 1591 case TEXT_LAYOUT_BIDI_LTR: 1592 rState.textDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT; 1593 break; 1594 1595 case (TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG): 1596 rState.textDirection = rendering::TextDirection::STRONG_LEFT_TO_RIGHT; 1597 break; 1598 1599 case TEXT_LAYOUT_BIDI_RTL: 1600 rState.textDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT; 1601 break; 1602 1603 case (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG): 1604 rState.textDirection = rendering::TextDirection::STRONG_RIGHT_TO_LEFT; 1605 break; 1606 } 1607 1608 rState.textAlignment = 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED; 1609 if( (nLayoutMode & (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_RIGHT) ) 1610 && !(nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT ) ) 1611 { 1612 rState.textAlignment = 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED; 1613 } 1614 } 1615 break; 1616 1617 // ------------------------------------------------------------ 1618 1619 // In the second part of this monster-switch, we 1620 // handle all recursing meta actions. These are the 1621 // ones generating a metafile by themselves, which is 1622 // then processed by recursively calling this method. 1623 1624 // ------------------------------------------------------------ 1625 1626 case META_GRADIENT_ACTION: 1627 { 1628 MetaGradientAction* pGradAct = static_cast<MetaGradientAction*>(pCurrAct); 1629 createGradientAction( ::Polygon( pGradAct->GetRect() ), 1630 pGradAct->GetGradient(), 1631 rFactoryParms, 1632 true, 1633 bSubsettableActions ); 1634 } 1635 break; 1636 1637 case META_HATCH_ACTION: 1638 { 1639 // TODO(F2): use native Canvas hatches here 1640 GDIMetaFile aTmpMtf; 1641 1642 rVDev.AddHatchActions( static_cast<MetaHatchAction*>(pCurrAct)->GetPolyPolygon(), 1643 static_cast<MetaHatchAction*>(pCurrAct)->GetHatch(), 1644 aTmpMtf ); 1645 createActions( aTmpMtf, rFactoryParms, 1646 bSubsettableActions ); 1647 } 1648 break; 1649 1650 case META_EPS_ACTION: 1651 { 1652 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pCurrAct); 1653 const GDIMetaFile& rSubstitute = pAct->GetSubstitute(); 1654 1655 // #121806# explicitely kept integer 1656 const Size aMtfSize( rSubstitute.GetPrefSize() ); 1657 const Size aMtfSizePixPre( rVDev.LogicToPixel( aMtfSize, 1658 rSubstitute.GetPrefMapMode() ) ); 1659 1660 // #i44110# correct null-sized output - there 1661 // are metafiles which have zero size in at 1662 // least one dimension 1663 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ), 1664 ::std::max( aMtfSizePixPre.Height(), 1L ) ); 1665 1666 // Setup local transform, such that the 1667 // metafile renders itself into the given 1668 // output rectangle 1669 pushState( rStates, PUSH_ALL ); 1670 1671 rVDev.Push(); 1672 rVDev.SetMapMode( rSubstitute.GetPrefMapMode() ); 1673 1674 const ::Point& rPos( rVDev.LogicToPixel( pAct->GetPoint() ) ); 1675 const ::Size& rSize( rVDev.LogicToPixel( pAct->GetSize() ) ); 1676 1677 getState( rStates ).transform.translate( rPos.X(), 1678 rPos.Y() ); 1679 getState( rStates ).transform.scale( (double)rSize.Width() / aMtfSizePix.Width(), 1680 (double)rSize.Height() / aMtfSizePix.Height() ); 1681 1682 createActions( const_cast<GDIMetaFile&>(pAct->GetSubstitute()), 1683 rFactoryParms, 1684 bSubsettableActions ); 1685 1686 rVDev.Pop(); 1687 popState( rStates ); 1688 } 1689 break; 1690 1691 // handle metafile comments, to retrieve 1692 // meta-information for gradients, fills and 1693 // strokes. May skip actions, and may recurse. 1694 case META_COMMENT_ACTION: 1695 { 1696 MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pCurrAct); 1697 1698 // Handle gradients 1699 if ( pAct->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_BEGIN" ) == COMPARE_EQUAL ) 1700 { 1701 MetaGradientExAction* pGradAction = NULL; 1702 bool bDone( false ); 1703 while( !bDone && 1704 (pCurrAct=rMtf.NextAction()) != NULL ) 1705 { 1706 switch( pCurrAct->GetType() ) 1707 { 1708 // extract gradient info 1709 case META_GRADIENTEX_ACTION: 1710 pGradAction = static_cast<MetaGradientExAction*>(pCurrAct); 1711 break; 1712 1713 // skip broken-down rendering, output gradient when sequence is ended 1714 case META_COMMENT_ACTION: 1715 if( static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_END" ) == COMPARE_EQUAL ) 1716 { 1717 bDone = true; 1718 1719 if( pGradAction ) 1720 { 1721 createGradientAction( pGradAction->GetPolyPolygon(), 1722 pGradAction->GetGradient(), 1723 rFactoryParms, 1724 false, 1725 bSubsettableActions ); 1726 } 1727 } 1728 break; 1729 } 1730 } 1731 } 1732 // TODO(P2): Handle drawing layer strokes, via 1733 // XPATHSTROKE_SEQ_BEGIN comment 1734 1735 // Handle drawing layer fills 1736 else if( pAct->GetComment().Equals( "XPATHFILL_SEQ_BEGIN" ) ) 1737 { 1738 const sal_uInt8* pData = pAct->GetData(); 1739 if ( pData ) 1740 { 1741 SvMemoryStream aMemStm( (void*)pData, pAct->GetDataSize(), STREAM_READ ); 1742 1743 SvtGraphicFill aFill; 1744 aMemStm >> aFill; 1745 1746 // TODO(P2): Also handle gradients and 1747 // hatches like this 1748 1749 // only evaluate comment for pure 1750 // bitmap fills. If a transparency 1751 // gradient is involved (denoted by 1752 // the FloatTransparent action), take 1753 // the normal meta actions. 1754 if( aFill.getFillType() == SvtGraphicFill::fillTexture && 1755 !isActionContained( rMtf, 1756 "XPATHFILL_SEQ_END", 1757 META_FLOATTRANSPARENT_ACTION ) ) 1758 { 1759 rendering::Texture aTexture; 1760 1761 // TODO(F1): the SvtGraphicFill 1762 // can also transport metafiles 1763 // here, handle that case, too 1764 Graphic aGraphic; 1765 aFill.getGraphic( aGraphic ); 1766 1767 BitmapEx aBmpEx( aGraphic.GetBitmapEx() ); 1768 const ::Size aBmpSize( aBmpEx.GetSizePixel() ); 1769 1770 ::SvtGraphicFill::Transform aTransform; 1771 aFill.getTransform( aTransform ); 1772 1773 ::basegfx::B2DHomMatrix aMatrix; 1774 1775 // convert to basegfx matrix 1776 aMatrix.set(0,0, aTransform.matrix[ 0 ] ); 1777 aMatrix.set(0,1, aTransform.matrix[ 1 ] ); 1778 aMatrix.set(0,2, aTransform.matrix[ 2 ] ); 1779 aMatrix.set(1,0, aTransform.matrix[ 3 ] ); 1780 aMatrix.set(1,1, aTransform.matrix[ 4 ] ); 1781 aMatrix.set(1,2, aTransform.matrix[ 5 ] ); 1782 1783 ::basegfx::B2DHomMatrix aScale; 1784 aScale.scale( aBmpSize.Width(), 1785 aBmpSize.Height() ); 1786 1787 // post-multiply with the bitmap 1788 // size (XCanvas' texture assumes 1789 // the given bitmap to be 1790 // normalized to [0,1]x[0,1] 1791 // rectangle) 1792 aMatrix = aMatrix * aScale; 1793 1794 // pre-multiply with the 1795 // logic-to-pixel scale factor 1796 // (the metafile comment works in 1797 // logical coordinates). 1798 ::basegfx::B2DHomMatrix aLogic2PixelTransform; 1799 aMatrix *= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform, 1800 rVDev ); 1801 1802 ::basegfx::unotools::affineMatrixFromHomMatrix( 1803 aTexture.AffineTransform, 1804 aMatrix ); 1805 1806 aTexture.Alpha = 1.0 - aFill.getTransparency(); 1807 aTexture.Bitmap = 1808 ::vcl::unotools::xBitmapFromBitmapEx( 1809 rCanvas->getUNOCanvas()->getDevice(), 1810 aBmpEx ); 1811 if( aFill.isTiling() ) 1812 { 1813 aTexture.RepeatModeX = rendering::TexturingMode::REPEAT; 1814 aTexture.RepeatModeY = rendering::TexturingMode::REPEAT; 1815 } 1816 else 1817 { 1818 aTexture.RepeatModeX = rendering::TexturingMode::NONE; 1819 aTexture.RepeatModeY = rendering::TexturingMode::NONE; 1820 } 1821 1822 ::PolyPolygon aPath; 1823 aFill.getPath( aPath ); 1824 1825 ::basegfx::B2DPolyPolygon aPoly( aPath.getB2DPolyPolygon() ); 1826 aPoly.transform( getState( rStates ).mapModeTransform ); 1827 ActionSharedPtr pPolyAction( 1828 internal::PolyPolyActionFactory::createPolyPolyAction( 1829 aPoly, 1830 rCanvas, 1831 getState( rStates ), 1832 aTexture ) ); 1833 1834 if( pPolyAction ) 1835 { 1836 maActions.push_back( 1837 MtfAction( 1838 pPolyAction, 1839 io_rCurrActionIndex ) ); 1840 1841 io_rCurrActionIndex += pPolyAction->getActionCount()-1; 1842 } 1843 1844 // skip broken-down render output 1845 skipContent( rMtf, 1846 "XPATHFILL_SEQ_END", 1847 io_rCurrActionIndex ); 1848 } 1849 } 1850 } 1851 } 1852 break; 1853 1854 // ------------------------------------------------------------ 1855 1856 // In the third part of this monster-switch, we 1857 // handle all 'acting' meta actions. These are all 1858 // processed by constructing function objects for 1859 // them, which will later ease caching. 1860 1861 // ------------------------------------------------------------ 1862 1863 case META_POINT_ACTION: 1864 { 1865 const OutDevState& rState( getState( rStates ) ); 1866 if( rState.lineColor.getLength() ) 1867 { 1868 ActionSharedPtr pPointAction( 1869 internal::PointActionFactory::createPointAction( 1870 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( 1871 static_cast<MetaPointAction*>(pCurrAct)->GetPoint() ), 1872 rCanvas, 1873 rState ) ); 1874 1875 if( pPointAction ) 1876 { 1877 maActions.push_back( 1878 MtfAction( 1879 pPointAction, 1880 io_rCurrActionIndex ) ); 1881 1882 io_rCurrActionIndex += pPointAction->getActionCount()-1; 1883 } 1884 } 1885 } 1886 break; 1887 1888 case META_PIXEL_ACTION: 1889 { 1890 const OutDevState& rState( getState( rStates ) ); 1891 if( rState.lineColor.getLength() ) 1892 { 1893 ActionSharedPtr pPointAction( 1894 internal::PointActionFactory::createPointAction( 1895 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( 1896 static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ), 1897 rCanvas, 1898 rState, 1899 static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) ); 1900 1901 if( pPointAction ) 1902 { 1903 maActions.push_back( 1904 MtfAction( 1905 pPointAction, 1906 io_rCurrActionIndex ) ); 1907 1908 io_rCurrActionIndex += pPointAction->getActionCount()-1; 1909 } 1910 } 1911 } 1912 break; 1913 1914 case META_LINE_ACTION: 1915 { 1916 const OutDevState& rState( getState( rStates ) ); 1917 if( rState.lineColor.getLength() ) 1918 { 1919 MetaLineAction* pLineAct = static_cast<MetaLineAction*>(pCurrAct); 1920 1921 const LineInfo& rLineInfo( pLineAct->GetLineInfo() ); 1922 1923 const ::basegfx::B2DPoint aStartPoint( 1924 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetStartPoint() )); 1925 const ::basegfx::B2DPoint aEndPoint( 1926 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetEndPoint() )); 1927 1928 ActionSharedPtr pLineAction; 1929 1930 if( rLineInfo.IsDefault() ) 1931 { 1932 // plain hair line 1933 pLineAction = 1934 internal::LineActionFactory::createLineAction( 1935 aStartPoint, 1936 aEndPoint, 1937 rCanvas, 1938 rState ); 1939 1940 if( pLineAction ) 1941 { 1942 maActions.push_back( 1943 MtfAction( 1944 pLineAction, 1945 io_rCurrActionIndex ) ); 1946 1947 io_rCurrActionIndex += pLineAction->getActionCount()-1; 1948 } 1949 } 1950 else if( LINE_NONE != rLineInfo.GetStyle() ) 1951 { 1952 // 'thick' line 1953 rendering::StrokeAttributes aStrokeAttributes; 1954 1955 setupStrokeAttributes( aStrokeAttributes, 1956 rFactoryParms, 1957 rLineInfo ); 1958 1959 // XCanvas can only stroke polygons, 1960 // not simple lines - thus, handle 1961 // this case via the polypolygon 1962 // action 1963 ::basegfx::B2DPolygon aPoly; 1964 aPoly.append( aStartPoint ); 1965 aPoly.append( aEndPoint ); 1966 pLineAction = 1967 internal::PolyPolyActionFactory::createPolyPolyAction( 1968 ::basegfx::B2DPolyPolygon( aPoly ), 1969 rCanvas, rState, aStrokeAttributes ); 1970 1971 if( pLineAction ) 1972 { 1973 maActions.push_back( 1974 MtfAction( 1975 pLineAction, 1976 io_rCurrActionIndex ) ); 1977 1978 io_rCurrActionIndex += pLineAction->getActionCount()-1; 1979 } 1980 } 1981 // else: line style is default 1982 // (i.e. invisible), don't generate action 1983 } 1984 } 1985 break; 1986 1987 case META_RECT_ACTION: 1988 { 1989 const Rectangle& rRect( 1990 static_cast<MetaRectAction*>(pCurrAct)->GetRect() ); 1991 1992 if( rRect.IsEmpty() ) 1993 break; 1994 1995 const OutDevState& rState( getState( rStates ) ); 1996 const ::basegfx::B2DPoint aTopLeftPixel( 1997 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ) ); 1998 const ::basegfx::B2DPoint aBottomRightPixel( 1999 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) + 2000 // #121100# OutputDevice::DrawRect() fills 2001 // rectangles Apple-like, i.e. with one 2002 // additional pixel to the right and bottom. 2003 ::basegfx::B2DPoint(1,1) ); 2004 2005 createFillAndStroke( ::basegfx::tools::createPolygonFromRect( 2006 ::basegfx::B2DRange( aTopLeftPixel, 2007 aBottomRightPixel )), 2008 rFactoryParms ); 2009 break; 2010 } 2011 2012 case META_ROUNDRECT_ACTION: 2013 { 2014 const Rectangle& rRect( 2015 static_cast<MetaRoundRectAction*>(pCurrAct)->GetRect()); 2016 2017 if( rRect.IsEmpty() ) 2018 break; 2019 2020 ::basegfx::B2DPolygon aPoly( 2021 ::basegfx::tools::createPolygonFromRect( 2022 ::basegfx::B2DRange( 2023 ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ), 2024 ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) + 2025 ::basegfx::B2DPoint(1,1) ), 2026 static_cast<MetaRoundRectAction*>(pCurrAct)->GetHorzRound(), 2027 static_cast<MetaRoundRectAction*>(pCurrAct)->GetVertRound() )); 2028 aPoly.transform( getState( rStates ).mapModeTransform ); 2029 2030 createFillAndStroke( aPoly, 2031 rFactoryParms ); 2032 } 2033 break; 2034 2035 case META_ELLIPSE_ACTION: 2036 { 2037 const Rectangle& rRect( 2038 static_cast<MetaEllipseAction*>(pCurrAct)->GetRect() ); 2039 2040 if( rRect.IsEmpty() ) 2041 break; 2042 2043 const ::basegfx::B2DRange aRange( 2044 ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ), 2045 ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) + 2046 ::basegfx::B2DPoint(1,1) ); 2047 2048 ::basegfx::B2DPolygon aPoly( 2049 ::basegfx::tools::createPolygonFromEllipse( 2050 aRange.getCenter(), 2051 aRange.getWidth(), 2052 aRange.getHeight() )); 2053 aPoly.transform( getState( rStates ).mapModeTransform ); 2054 2055 createFillAndStroke( aPoly, 2056 rFactoryParms ); 2057 } 2058 break; 2059 2060 case META_ARC_ACTION: 2061 { 2062 // TODO(F1): Missing basegfx functionality. Mind empty rects! 2063 const Polygon aToolsPoly( static_cast<MetaArcAction*>(pCurrAct)->GetRect(), 2064 static_cast<MetaArcAction*>(pCurrAct)->GetStartPoint(), 2065 static_cast<MetaArcAction*>(pCurrAct)->GetEndPoint(), POLY_ARC ); 2066 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() ); 2067 aPoly.transform( getState( rStates ).mapModeTransform ); 2068 2069 createFillAndStroke( aPoly, 2070 rFactoryParms ); 2071 } 2072 break; 2073 2074 case META_PIE_ACTION: 2075 { 2076 // TODO(F1): Missing basegfx functionality. Mind empty rects! 2077 const Polygon aToolsPoly( static_cast<MetaPieAction*>(pCurrAct)->GetRect(), 2078 static_cast<MetaPieAction*>(pCurrAct)->GetStartPoint(), 2079 static_cast<MetaPieAction*>(pCurrAct)->GetEndPoint(), POLY_PIE ); 2080 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() ); 2081 aPoly.transform( getState( rStates ).mapModeTransform ); 2082 2083 createFillAndStroke( aPoly, 2084 rFactoryParms ); 2085 } 2086 break; 2087 2088 case META_CHORD_ACTION: 2089 { 2090 // TODO(F1): Missing basegfx functionality. Mind empty rects! 2091 const Polygon aToolsPoly( static_cast<MetaChordAction*>(pCurrAct)->GetRect(), 2092 static_cast<MetaChordAction*>(pCurrAct)->GetStartPoint(), 2093 static_cast<MetaChordAction*>(pCurrAct)->GetEndPoint(), POLY_CHORD ); 2094 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() ); 2095 aPoly.transform( getState( rStates ).mapModeTransform ); 2096 2097 createFillAndStroke( aPoly, 2098 rFactoryParms ); 2099 } 2100 break; 2101 2102 case META_POLYLINE_ACTION: 2103 { 2104 const OutDevState& rState( getState( rStates ) ); 2105 if( rState.lineColor.getLength() || 2106 rState.fillColor.getLength() ) 2107 { 2108 MetaPolyLineAction* pPolyLineAct = static_cast<MetaPolyLineAction*>(pCurrAct); 2109 2110 const LineInfo& rLineInfo( pPolyLineAct->GetLineInfo() ); 2111 ::basegfx::B2DPolygon aPoly( pPolyLineAct->GetPolygon().getB2DPolygon() ); 2112 aPoly.transform( rState.mapModeTransform ); 2113 2114 ActionSharedPtr pLineAction; 2115 2116 if( rLineInfo.IsDefault() ) 2117 { 2118 // plain hair line polygon 2119 pLineAction = 2120 internal::PolyPolyActionFactory::createLinePolyPolyAction( 2121 ::basegfx::B2DPolyPolygon(aPoly), 2122 rCanvas, 2123 rState ); 2124 2125 if( pLineAction ) 2126 { 2127 maActions.push_back( 2128 MtfAction( 2129 pLineAction, 2130 io_rCurrActionIndex ) ); 2131 2132 io_rCurrActionIndex += pLineAction->getActionCount()-1; 2133 } 2134 } 2135 else if( LINE_NONE != rLineInfo.GetStyle() ) 2136 { 2137 // 'thick' line polygon 2138 rendering::StrokeAttributes aStrokeAttributes; 2139 2140 setupStrokeAttributes( aStrokeAttributes, 2141 rFactoryParms, 2142 rLineInfo ); 2143 2144 pLineAction = 2145 internal::PolyPolyActionFactory::createPolyPolyAction( 2146 ::basegfx::B2DPolyPolygon(aPoly), 2147 rCanvas, 2148 rState, 2149 aStrokeAttributes ) ; 2150 2151 if( pLineAction ) 2152 { 2153 maActions.push_back( 2154 MtfAction( 2155 pLineAction, 2156 io_rCurrActionIndex ) ); 2157 2158 io_rCurrActionIndex += pLineAction->getActionCount()-1; 2159 } 2160 } 2161 // else: line style is default 2162 // (i.e. invisible), don't generate action 2163 } 2164 } 2165 break; 2166 2167 case META_POLYGON_ACTION: 2168 { 2169 ::basegfx::B2DPolygon aPoly( static_cast<MetaPolygonAction*>(pCurrAct)->GetPolygon().getB2DPolygon() ); 2170 aPoly.transform( getState( rStates ).mapModeTransform ); 2171 createFillAndStroke( aPoly, 2172 rFactoryParms ); 2173 } 2174 break; 2175 2176 case META_POLYPOLYGON_ACTION: 2177 { 2178 ::basegfx::B2DPolyPolygon aPoly( static_cast<MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon().getB2DPolyPolygon() ); 2179 aPoly.transform( getState( rStates ).mapModeTransform ); 2180 createFillAndStroke( aPoly, 2181 rFactoryParms ); 2182 } 2183 break; 2184 2185 case META_BMP_ACTION: 2186 { 2187 MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pCurrAct); 2188 2189 ActionSharedPtr pBmpAction( 2190 internal::BitmapActionFactory::createBitmapAction( 2191 pAct->GetBitmap(), 2192 getState( rStates ).mapModeTransform * 2193 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2194 rCanvas, 2195 getState( rStates ) ) ); 2196 2197 if( pBmpAction ) 2198 { 2199 maActions.push_back( 2200 MtfAction( 2201 pBmpAction, 2202 io_rCurrActionIndex ) ); 2203 2204 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2205 } 2206 } 2207 break; 2208 2209 case META_BMPSCALE_ACTION: 2210 { 2211 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pCurrAct); 2212 2213 ActionSharedPtr pBmpAction( 2214 internal::BitmapActionFactory::createBitmapAction( 2215 pAct->GetBitmap(), 2216 getState( rStates ).mapModeTransform * 2217 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2218 getState( rStates ).mapModeTransform * 2219 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ), 2220 rCanvas, 2221 getState( rStates ) ) ); 2222 2223 if( pBmpAction ) 2224 { 2225 maActions.push_back( 2226 MtfAction( 2227 pBmpAction, 2228 io_rCurrActionIndex ) ); 2229 2230 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2231 } 2232 } 2233 break; 2234 2235 case META_BMPSCALEPART_ACTION: 2236 { 2237 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pCurrAct); 2238 2239 // crop bitmap to given source rectangle (no 2240 // need to copy and convert the whole bitmap) 2241 Bitmap aBmp( pAct->GetBitmap() ); 2242 const Rectangle aCropRect( pAct->GetSrcPoint(), 2243 pAct->GetSrcSize() ); 2244 aBmp.Crop( aCropRect ); 2245 2246 ActionSharedPtr pBmpAction( 2247 internal::BitmapActionFactory::createBitmapAction( 2248 aBmp, 2249 getState( rStates ).mapModeTransform * 2250 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ), 2251 getState( rStates ).mapModeTransform * 2252 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ), 2253 rCanvas, 2254 getState( rStates ) ) ); 2255 2256 if( pBmpAction ) 2257 { 2258 maActions.push_back( 2259 MtfAction( 2260 pBmpAction, 2261 io_rCurrActionIndex ) ); 2262 2263 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2264 } 2265 } 2266 break; 2267 2268 case META_BMPEX_ACTION: 2269 { 2270 MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pCurrAct); 2271 2272 ActionSharedPtr pBmpAction( 2273 internal::BitmapActionFactory::createBitmapAction( 2274 pAct->GetBitmapEx(), 2275 getState( rStates ).mapModeTransform * 2276 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2277 rCanvas, 2278 getState( rStates ) ) ); 2279 2280 if( pBmpAction ) 2281 { 2282 maActions.push_back( 2283 MtfAction( 2284 pBmpAction, 2285 io_rCurrActionIndex ) ); 2286 2287 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2288 } 2289 } 2290 break; 2291 2292 case META_BMPEXSCALE_ACTION: 2293 { 2294 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pCurrAct); 2295 2296 ActionSharedPtr pBmpAction( 2297 internal::BitmapActionFactory::createBitmapAction( 2298 pAct->GetBitmapEx(), 2299 getState( rStates ).mapModeTransform * 2300 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2301 getState( rStates ).mapModeTransform * 2302 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ), 2303 rCanvas, 2304 getState( rStates ) ) ); 2305 2306 if( pBmpAction ) 2307 { 2308 maActions.push_back( 2309 MtfAction( 2310 pBmpAction, 2311 io_rCurrActionIndex ) ); 2312 2313 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2314 } 2315 } 2316 break; 2317 2318 case META_BMPEXSCALEPART_ACTION: 2319 { 2320 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pCurrAct); 2321 2322 // crop bitmap to given source rectangle (no 2323 // need to copy and convert the whole bitmap) 2324 BitmapEx aBmp( pAct->GetBitmapEx() ); 2325 const Rectangle aCropRect( pAct->GetSrcPoint(), 2326 pAct->GetSrcSize() ); 2327 aBmp.Crop( aCropRect ); 2328 2329 ActionSharedPtr pBmpAction( 2330 internal::BitmapActionFactory::createBitmapAction( 2331 aBmp, 2332 getState( rStates ).mapModeTransform * 2333 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ), 2334 getState( rStates ).mapModeTransform * 2335 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ), 2336 rCanvas, 2337 getState( rStates ) ) ); 2338 2339 if( pBmpAction ) 2340 { 2341 maActions.push_back( 2342 MtfAction( 2343 pBmpAction, 2344 io_rCurrActionIndex ) ); 2345 2346 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2347 } 2348 } 2349 break; 2350 2351 case META_MASK_ACTION: 2352 { 2353 MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pCurrAct); 2354 2355 // create masked BitmapEx right here, as the 2356 // canvas does not provide equivalent 2357 // functionality 2358 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(), 2359 pAct->GetColor() )); 2360 2361 ActionSharedPtr pBmpAction( 2362 internal::BitmapActionFactory::createBitmapAction( 2363 aBmp, 2364 getState( rStates ).mapModeTransform * 2365 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2366 rCanvas, 2367 getState( rStates ) ) ); 2368 2369 if( pBmpAction ) 2370 { 2371 maActions.push_back( 2372 MtfAction( 2373 pBmpAction, 2374 io_rCurrActionIndex ) ); 2375 2376 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2377 } 2378 } 2379 break; 2380 2381 case META_MASKSCALE_ACTION: 2382 { 2383 MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pCurrAct); 2384 2385 // create masked BitmapEx right here, as the 2386 // canvas does not provide equivalent 2387 // functionality 2388 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(), 2389 pAct->GetColor() )); 2390 2391 ActionSharedPtr pBmpAction( 2392 internal::BitmapActionFactory::createBitmapAction( 2393 aBmp, 2394 getState( rStates ).mapModeTransform * 2395 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2396 getState( rStates ).mapModeTransform * 2397 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ), 2398 rCanvas, 2399 getState( rStates ) ) ); 2400 2401 if( pBmpAction ) 2402 { 2403 maActions.push_back( 2404 MtfAction( 2405 pBmpAction, 2406 io_rCurrActionIndex ) ); 2407 2408 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2409 } 2410 } 2411 break; 2412 2413 case META_MASKSCALEPART_ACTION: 2414 { 2415 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pCurrAct); 2416 2417 // create masked BitmapEx right here, as the 2418 // canvas does not provide equivalent 2419 // functionality 2420 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(), 2421 pAct->GetColor() )); 2422 2423 // crop bitmap to given source rectangle (no 2424 // need to copy and convert the whole bitmap) 2425 const Rectangle aCropRect( pAct->GetSrcPoint(), 2426 pAct->GetSrcSize() ); 2427 aBmp.Crop( aCropRect ); 2428 2429 ActionSharedPtr pBmpAction( 2430 internal::BitmapActionFactory::createBitmapAction( 2431 aBmp, 2432 getState( rStates ).mapModeTransform * 2433 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ), 2434 getState( rStates ).mapModeTransform * 2435 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ), 2436 rCanvas, 2437 getState( rStates ) ) ); 2438 2439 if( pBmpAction ) 2440 { 2441 maActions.push_back( 2442 MtfAction( 2443 pBmpAction, 2444 io_rCurrActionIndex ) ); 2445 2446 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2447 } 2448 } 2449 break; 2450 2451 case META_GRADIENTEX_ACTION: 2452 // TODO(F1): use native Canvas gradients here 2453 // action is ignored here, because redundant to META_GRADIENT_ACTION 2454 break; 2455 2456 case META_WALLPAPER_ACTION: 2457 // TODO(F2): NYI 2458 break; 2459 2460 case META_TRANSPARENT_ACTION: 2461 { 2462 const OutDevState& rState( getState( rStates ) ); 2463 if( rState.lineColor.getLength() || 2464 rState.fillColor.getLength() ) 2465 { 2466 MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pCurrAct); 2467 ::basegfx::B2DPolyPolygon aPoly( pAct->GetPolyPolygon().getB2DPolyPolygon() ); 2468 aPoly.transform( rState.mapModeTransform ); 2469 2470 ActionSharedPtr pPolyAction( 2471 internal::PolyPolyActionFactory::createPolyPolyAction( 2472 aPoly, 2473 rCanvas, 2474 rState, 2475 pAct->GetTransparence() ) ); 2476 2477 if( pPolyAction ) 2478 { 2479 maActions.push_back( 2480 MtfAction( 2481 pPolyAction, 2482 io_rCurrActionIndex ) ); 2483 2484 io_rCurrActionIndex += pPolyAction->getActionCount()-1; 2485 } 2486 } 2487 } 2488 break; 2489 2490 case META_FLOATTRANSPARENT_ACTION: 2491 { 2492 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pCurrAct); 2493 2494 internal::MtfAutoPtr pMtf( 2495 new ::GDIMetaFile( pAct->GetGDIMetaFile() ) ); 2496 2497 // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls) 2498 internal::GradientAutoPtr pGradient( 2499 new Gradient( pAct->GetGradient() ) ); 2500 2501 DBG_TESTSOLARMUTEX(); 2502 2503 ActionSharedPtr pFloatTransAction( 2504 internal::TransparencyGroupActionFactory::createTransparencyGroupAction( 2505 pMtf, 2506 pGradient, 2507 rParms, 2508 getState( rStates ).mapModeTransform * 2509 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2510 getState( rStates ).mapModeTransform * 2511 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ), 2512 rCanvas, 2513 getState( rStates ) ) ); 2514 2515 if( pFloatTransAction ) 2516 { 2517 maActions.push_back( 2518 MtfAction( 2519 pFloatTransAction, 2520 io_rCurrActionIndex ) ); 2521 2522 io_rCurrActionIndex += pFloatTransAction->getActionCount()-1; 2523 } 2524 } 2525 break; 2526 2527 case META_TEXT_ACTION: 2528 { 2529 MetaTextAction* pAct = static_cast<MetaTextAction*>(pCurrAct); 2530 XubString sText = XubString( pAct->GetText() ); 2531 2532 if( rVDev.GetDigitLanguage()) 2533 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() ); 2534 2535 createTextAction( 2536 pAct->GetPoint(), 2537 sText, 2538 pAct->GetIndex(), 2539 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(), 2540 NULL, 2541 rFactoryParms, 2542 bSubsettableActions ); 2543 } 2544 break; 2545 2546 case META_TEXTARRAY_ACTION: 2547 { 2548 MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pCurrAct); 2549 XubString sText = XubString( pAct->GetText() ); 2550 2551 if( rVDev.GetDigitLanguage()) 2552 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() ); 2553 2554 createTextAction( 2555 pAct->GetPoint(), 2556 sText, 2557 pAct->GetIndex(), 2558 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(), 2559 pAct->GetDXArray(), 2560 rFactoryParms, 2561 bSubsettableActions ); 2562 } 2563 break; 2564 2565 case META_TEXTLINE_ACTION: 2566 { 2567 MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pCurrAct); 2568 2569 const OutDevState& rState( getState( rStates ) ); 2570 const ::Size aBaselineOffset( tools::getBaselineOffset( rState, 2571 rVDev ) ); 2572 const ::Point aStartPoint( pAct->GetStartPoint() ); 2573 const ::basegfx::B2DSize aSize( rState.mapModeTransform * 2574 ::basegfx::B2DSize(pAct->GetWidth(), 2575 0 )); 2576 2577 ActionSharedPtr pPolyAction( 2578 PolyPolyActionFactory::createPolyPolyAction( 2579 tools::createTextLinesPolyPolygon( 2580 rState.mapModeTransform * 2581 ::basegfx::B2DPoint( 2582 ::vcl::unotools::b2DPointFromPoint(pAct->GetStartPoint()) + 2583 ::vcl::unotools::b2DSizeFromSize(aBaselineOffset)), 2584 aSize.getX(), 2585 tools::createTextLineInfo( rVDev, 2586 rState )), 2587 rCanvas, 2588 rState ) ); 2589 2590 if( pPolyAction.get() ) 2591 { 2592 maActions.push_back( 2593 MtfAction( 2594 pPolyAction, 2595 io_rCurrActionIndex ) ); 2596 2597 io_rCurrActionIndex += pPolyAction->getActionCount()-1; 2598 } 2599 } 2600 break; 2601 2602 case META_TEXTRECT_ACTION: 2603 { 2604 MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pCurrAct); 2605 2606 pushState( rStates, PUSH_ALL ); 2607 2608 // use the VDev to break up the text rect 2609 // action into readily formatted lines 2610 GDIMetaFile aTmpMtf; 2611 rVDev.AddTextRectActions( pAct->GetRect(), 2612 pAct->GetText(), 2613 pAct->GetStyle(), 2614 aTmpMtf ); 2615 2616 createActions( aTmpMtf, 2617 rFactoryParms, 2618 bSubsettableActions ); 2619 2620 popState( rStates ); 2621 2622 break; 2623 } 2624 2625 case META_STRETCHTEXT_ACTION: 2626 { 2627 MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pCurrAct); 2628 XubString sText = XubString( pAct->GetText() ); 2629 2630 if( rVDev.GetDigitLanguage()) 2631 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() ); 2632 2633 const sal_uInt16 nLen( pAct->GetLen() == (sal_uInt16)STRING_LEN ? 2634 pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen() ); 2635 2636 // #i70897# Nothing to do, actually... 2637 if( nLen == 0 ) 2638 break; 2639 2640 // have to fit the text into the given 2641 // width. This is achieved by internally 2642 // generating a DX array, and uniformly 2643 // distributing the excess/insufficient width 2644 // to every logical character. 2645 ::boost::scoped_array< sal_Int32 > pDXArray( new sal_Int32[nLen] ); 2646 2647 rVDev.GetTextArray( pAct->GetText(), pDXArray.get(), 2648 pAct->GetIndex(), pAct->GetLen() ); 2649 2650 const sal_Int32 nWidthDifference( pAct->GetWidth() - pDXArray[ nLen-1 ] ); 2651 2652 // Last entry of pDXArray contains total width of the text 2653 sal_Int32* p=pDXArray.get(); 2654 for( sal_uInt16 i=1; i<=nLen; ++i ) 2655 { 2656 // calc ratio for every array entry, to 2657 // distribute rounding errors 'evenly' 2658 // across the characters. Note that each 2659 // entry represents the 'end' position of 2660 // the corresponding character, thus, we 2661 // let i run from 1 to nLen. 2662 *p++ += (sal_Int32)i*nWidthDifference/nLen; 2663 } 2664 2665 createTextAction( 2666 pAct->GetPoint(), 2667 sText, 2668 pAct->GetIndex(), 2669 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(), 2670 pDXArray.get(), 2671 rFactoryParms, 2672 bSubsettableActions ); 2673 } 2674 break; 2675 2676 default: 2677 OSL_ENSURE( false, 2678 "Unknown meta action type encountered" ); 2679 break; 2680 } 2681 2682 // increment action index (each mtf action counts _at 2683 // least_ one. Some count for more, therefore, 2684 // io_rCurrActionIndex is sometimes incremented by 2685 // pAct->getActionCount()-1 above, the -1 being the 2686 // correction for the unconditional increment here). 2687 ++io_rCurrActionIndex; 2688 } 2689 2690 return true; 2691 } 2692 2693 2694 namespace 2695 { 2696 class ActionRenderer 2697 { 2698 public: 2699 ActionRenderer( const ::basegfx::B2DHomMatrix& rTransformation ) : 2700 maTransformation( rTransformation ), 2701 mbRet( true ) 2702 { 2703 } 2704 2705 bool result() 2706 { 2707 return mbRet; 2708 } 2709 2710 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction ) 2711 { 2712 // ANDing the result. We want to fail if at least 2713 // one action failed. 2714 mbRet &= rAction.mpAction->render( maTransformation ); 2715 } 2716 2717 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction, 2718 const Action::Subset& rSubset ) 2719 { 2720 // ANDing the result. We want to fail if at least 2721 // one action failed. 2722 mbRet &= rAction.mpAction->render( maTransformation, 2723 rSubset ); 2724 } 2725 2726 private: 2727 ::basegfx::B2DHomMatrix maTransformation; 2728 bool mbRet; 2729 }; 2730 2731 class AreaQuery 2732 { 2733 public: 2734 AreaQuery( const ::basegfx::B2DHomMatrix& rTransformation ) : 2735 maTransformation( rTransformation ), 2736 maBounds() 2737 { 2738 } 2739 2740 bool result() 2741 { 2742 return true; // nothing can fail here 2743 } 2744 2745 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction ) 2746 { 2747 maBounds.expand( rAction.mpAction->getBounds( maTransformation ) ); 2748 } 2749 2750 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction, 2751 const Action::Subset& rSubset ) 2752 { 2753 maBounds.expand( rAction.mpAction->getBounds( maTransformation, 2754 rSubset ) ); 2755 } 2756 2757 ::basegfx::B2DRange getBounds() const 2758 { 2759 return maBounds; 2760 } 2761 2762 private: 2763 ::basegfx::B2DHomMatrix maTransformation; 2764 ::basegfx::B2DRange maBounds; 2765 }; 2766 2767 // Doing that via inline class. Compilers tend to not inline free 2768 // functions. 2769 struct UpperBoundActionIndexComparator 2770 { 2771 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rLHS, 2772 const ::cppcanvas::internal::ImplRenderer::MtfAction& rRHS ) 2773 { 2774 const sal_Int32 nLHSCount( rLHS.mpAction ? 2775 rLHS.mpAction->getActionCount() : 0 ); 2776 const sal_Int32 nRHSCount( rRHS.mpAction ? 2777 rRHS.mpAction->getActionCount() : 0 ); 2778 2779 // compare end of action range, to have an action selected 2780 // by lower_bound even if the requested index points in 2781 // the middle of the action's range 2782 return rLHS.mnOrigIndex + nLHSCount < rRHS.mnOrigIndex + nRHSCount; 2783 } 2784 }; 2785 2786 /** Algorithm to apply given functor to a subset range 2787 2788 @tpl Functor 2789 2790 Functor to call for each element of the subset 2791 range. Must provide the following method signatures: 2792 bool result() (returning false if operation failed) 2793 2794 */ 2795 template< typename Functor > bool 2796 forSubsetRange( Functor& rFunctor, 2797 ImplRenderer::ActionVector::const_iterator aRangeBegin, 2798 ImplRenderer::ActionVector::const_iterator aRangeEnd, 2799 sal_Int32 nStartIndex, 2800 sal_Int32 nEndIndex, 2801 const ImplRenderer::ActionVector::const_iterator& rEnd ) 2802 { 2803 if( aRangeBegin == aRangeEnd ) 2804 { 2805 // only a single action. Setup subset, and call functor 2806 Action::Subset aSubset; 2807 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ), 2808 nStartIndex - aRangeBegin->mnOrigIndex ); 2809 aSubset.mnSubsetEnd = ::std::min( aRangeBegin->mpAction->getActionCount(), 2810 nEndIndex - aRangeBegin->mnOrigIndex ); 2811 2812 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0, 2813 "ImplRenderer::forSubsetRange(): Invalid indices" ); 2814 2815 rFunctor( *aRangeBegin, aSubset ); 2816 } 2817 else 2818 { 2819 // more than one action. 2820 2821 // render partial first, full intermediate, and 2822 // partial last action 2823 Action::Subset aSubset; 2824 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ), 2825 nStartIndex - aRangeBegin->mnOrigIndex ); 2826 aSubset.mnSubsetEnd = aRangeBegin->mpAction->getActionCount(); 2827 2828 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0, 2829 "ImplRenderer::forSubsetRange(): Invalid indices" ); 2830 2831 rFunctor( *aRangeBegin, aSubset ); 2832 2833 // first action rendered, skip to next 2834 ++aRangeBegin; 2835 2836 // render full middle actions 2837 while( aRangeBegin != aRangeEnd ) 2838 rFunctor( *aRangeBegin++ ); 2839 2840 if( aRangeEnd == rEnd || 2841 aRangeEnd->mnOrigIndex > nEndIndex ) 2842 { 2843 // aRangeEnd denotes end of action vector, 2844 // 2845 // or 2846 // 2847 // nEndIndex references something _after_ 2848 // aRangeBegin, but _before_ aRangeEnd 2849 // 2850 // either way: no partial action left 2851 return rFunctor.result(); 2852 } 2853 2854 aSubset.mnSubsetBegin = 0; 2855 aSubset.mnSubsetEnd = nEndIndex - aRangeEnd->mnOrigIndex; 2856 2857 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0, 2858 "ImplRenderer::forSubsetRange(): Invalid indices" ); 2859 2860 rFunctor( *aRangeEnd, aSubset ); 2861 } 2862 2863 return rFunctor.result(); 2864 } 2865 } 2866 2867 bool ImplRenderer::getSubsetIndices( sal_Int32& io_rStartIndex, 2868 sal_Int32& io_rEndIndex, 2869 ActionVector::const_iterator& o_rRangeBegin, 2870 ActionVector::const_iterator& o_rRangeEnd ) const 2871 { 2872 ENSURE_OR_RETURN_FALSE( io_rStartIndex<=io_rEndIndex, 2873 "ImplRenderer::getSubsetIndices(): invalid action range" ); 2874 2875 ENSURE_OR_RETURN_FALSE( !maActions.empty(), 2876 "ImplRenderer::getSubsetIndices(): no actions to render" ); 2877 2878 const sal_Int32 nMinActionIndex( maActions.front().mnOrigIndex ); 2879 const sal_Int32 nMaxActionIndex( maActions.back().mnOrigIndex + 2880 maActions.back().mpAction->getActionCount() ); 2881 2882 // clip given range to permissible values (there might be 2883 // ranges before and behind the valid indices) 2884 io_rStartIndex = ::std::max( nMinActionIndex, 2885 io_rStartIndex ); 2886 io_rEndIndex = ::std::min( nMaxActionIndex, 2887 io_rEndIndex ); 2888 2889 if( io_rStartIndex == io_rEndIndex || 2890 io_rStartIndex > io_rEndIndex ) 2891 { 2892 // empty range, don't render anything. The second 2893 // condition e.g. happens if the requested range lies 2894 // fully before or behind the valid action indices. 2895 return false; 2896 } 2897 2898 2899 const ActionVector::const_iterator aBegin( maActions.begin() ); 2900 const ActionVector::const_iterator aEnd( maActions.end() ); 2901 2902 2903 // find start and end action 2904 // ========================= 2905 o_rRangeBegin = ::std::lower_bound( aBegin, aEnd, 2906 MtfAction( ActionSharedPtr(), io_rStartIndex ), 2907 UpperBoundActionIndexComparator() ); 2908 o_rRangeEnd = ::std::lower_bound( aBegin, aEnd, 2909 MtfAction( ActionSharedPtr(), io_rEndIndex ), 2910 UpperBoundActionIndexComparator() ); 2911 return true; 2912 } 2913 2914 2915 // Public methods 2916 // ==================================================================== 2917 2918 ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas, 2919 const GDIMetaFile& rMtf, 2920 const Parameters& rParams ) : 2921 CanvasGraphicHelper( rCanvas ), 2922 maActions() 2923 { 2924 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" ); 2925 2926 OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(), 2927 "ImplRenderer::ImplRenderer(): Invalid canvas" ); 2928 OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(), 2929 "ImplRenderer::ImplRenderer(): Invalid graphic device" ); 2930 2931 // make sure canvas and graphic device are valid; action 2932 // creation don't check that every time 2933 if( rCanvas.get() == NULL || 2934 !rCanvas->getUNOCanvas().is() || 2935 !rCanvas->getUNOCanvas()->getDevice().is() ) 2936 { 2937 // leave actions empty 2938 return; 2939 } 2940 2941 VectorOfOutDevStates aStateStack; 2942 2943 VirtualDevice aVDev; 2944 aVDev.EnableOutput( sal_False ); 2945 2946 // Setup VDev for state tracking and mapping 2947 // ========================================= 2948 2949 aVDev.SetMapMode( rMtf.GetPrefMapMode() ); 2950 2951 const Size aMtfSize( rMtf.GetPrefSize() ); 2952 const Size aMtfSizePixPre( aVDev.LogicToPixel( aMtfSize, 2953 rMtf.GetPrefMapMode() ) ); 2954 const Point aEmptyPt; 2955 const Point aMtfOriginPix( aVDev.LogicToPixel( aEmptyPt ) ); 2956 2957 // #i44110# correct null-sized output - there are shapes 2958 // which have zero size in at least one dimension 2959 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ), 2960 ::std::max( aMtfSizePixPre.Height(), 1L ) ); 2961 2962 sal_Int32 nCurrActions(0); 2963 ActionFactoryParameters aParms(aStateStack, 2964 rCanvas, 2965 aVDev, 2966 rParams, 2967 nCurrActions ); 2968 2969 // init state stack 2970 clearStateStack( aStateStack ); 2971 2972 // Setup local state, such that the metafile renders 2973 // itself into a one-by-one square at the origin for 2974 // identity view and render transformations 2975 getState( aStateStack ).transform.scale( 1.0 / aMtfSizePix.Width(), 2976 1.0 / aMtfSizePix.Height() ); 2977 2978 tools::calcLogic2PixelAffineTransform( getState( aStateStack ).mapModeTransform, 2979 aVDev ); 2980 2981 ColorSharedPtr pColor( getCanvas()->createColor() ); 2982 2983 // setup default text color to black 2984 getState( aStateStack ).textColor = 2985 getState( aStateStack ).textFillColor = 2986 getState( aStateStack ).textLineColor = pColor->getDeviceColor( 0x000000FF ); 2987 2988 // apply overrides from the Parameters struct 2989 if( rParams.maFillColor.is_initialized() ) 2990 { 2991 getState( aStateStack ).isFillColorSet = true; 2992 getState( aStateStack ).fillColor = pColor->getDeviceColor( *rParams.maFillColor ); 2993 } 2994 if( rParams.maLineColor.is_initialized() ) 2995 { 2996 getState( aStateStack ).isLineColorSet = true; 2997 getState( aStateStack ).lineColor = pColor->getDeviceColor( *rParams.maLineColor ); 2998 } 2999 if( rParams.maTextColor.is_initialized() ) 3000 { 3001 getState( aStateStack ).isTextFillColorSet = true; 3002 getState( aStateStack ).isTextLineColorSet = true; 3003 getState( aStateStack ).textColor = 3004 getState( aStateStack ).textFillColor = 3005 getState( aStateStack ).textLineColor = pColor->getDeviceColor( *rParams.maTextColor ); 3006 } 3007 if( rParams.maFontName.is_initialized() || 3008 rParams.maFontWeight.is_initialized() || 3009 rParams.maFontLetterForm.is_initialized() || 3010 rParams.maFontUnderline.is_initialized() || 3011 rParams.maFontProportion.is_initialized() ) 3012 { 3013 ::cppcanvas::internal::OutDevState& rState = getState( aStateStack ); 3014 3015 rState.xFont = createFont( rState.fontRotation, 3016 ::Font(), // default font 3017 aParms ); 3018 } 3019 3020 createActions( const_cast<GDIMetaFile&>(rMtf), // HACK(Q2): 3021 // we're 3022 // changing 3023 // the 3024 // current 3025 // action 3026 // in 3027 // createActions! 3028 aParms, 3029 true // TODO(P1): make subsettability configurable 3030 ); 3031 } 3032 3033 ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas, 3034 const BitmapEx& rBmpEx, 3035 const Parameters& rParams ) : 3036 CanvasGraphicHelper( rCanvas ), 3037 maActions() 3038 { 3039 // TODO(F3): property modification parameters are 3040 // currently ignored for Bitmaps 3041 (void)rParams; 3042 3043 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(bitmap)" ); 3044 3045 OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(), 3046 "ImplRenderer::ImplRenderer(): Invalid canvas" ); 3047 OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(), 3048 "ImplRenderer::ImplRenderer(): Invalid graphic device" ); 3049 3050 // make sure canvas and graphic device are valid; action 3051 // creation don't check that every time 3052 if( rCanvas.get() == NULL || 3053 !rCanvas->getUNOCanvas().is() || 3054 !rCanvas->getUNOCanvas()->getDevice().is() ) 3055 { 3056 // leave actions empty 3057 return; 3058 } 3059 3060 OutDevState aState; 3061 3062 const Size aBmpSize( rBmpEx.GetSizePixel() ); 3063 3064 // Setup local state, such that the bitmap renders itself 3065 // into a one-by-one square for identity view and render 3066 // transformations 3067 aState.transform.scale( 1.0 / aBmpSize.Width(), 3068 1.0 / aBmpSize.Height() ); 3069 3070 // create a single action for the provided BitmapEx 3071 maActions.push_back( 3072 MtfAction( 3073 BitmapActionFactory::createBitmapAction( 3074 rBmpEx, 3075 ::basegfx::B2DPoint(), 3076 rCanvas, 3077 aState), 3078 0 ) ); 3079 } 3080 3081 ImplRenderer::~ImplRenderer() 3082 { 3083 } 3084 3085 bool ImplRenderer::drawSubset( sal_Int32 nStartIndex, 3086 sal_Int32 nEndIndex ) const 3087 { 3088 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::drawSubset()" ); 3089 3090 ActionVector::const_iterator aRangeBegin; 3091 ActionVector::const_iterator aRangeEnd; 3092 3093 try 3094 { 3095 if( !getSubsetIndices( nStartIndex, nEndIndex, 3096 aRangeBegin, aRangeEnd ) ) 3097 return true; // nothing to render (but _that_ was successful) 3098 3099 // now, aRangeBegin references the action in which the 3100 // subset rendering must start, and aRangeEnd references 3101 // the action in which the subset rendering must end (it 3102 // might also end right at the start of the referenced 3103 // action, such that zero of that action needs to be 3104 // rendered). 3105 3106 3107 // render subset of actions 3108 // ======================== 3109 3110 ::basegfx::B2DHomMatrix aMatrix; 3111 ::canvas::tools::getRenderStateTransform( aMatrix, 3112 getRenderState() ); 3113 3114 ActionRenderer aRenderer( aMatrix ); 3115 3116 return forSubsetRange( aRenderer, 3117 aRangeBegin, 3118 aRangeEnd, 3119 nStartIndex, 3120 nEndIndex, 3121 maActions.end() ); 3122 } 3123 catch( uno::Exception& ) 3124 { 3125 OSL_ENSURE( false, 3126 rtl::OUStringToOString( 3127 comphelper::anyToString( cppu::getCaughtException() ), 3128 RTL_TEXTENCODING_UTF8 ).getStr() ); 3129 3130 // convert error to return value 3131 return false; 3132 } 3133 } 3134 3135 ::basegfx::B2DRange ImplRenderer::getSubsetArea( sal_Int32 nStartIndex, 3136 sal_Int32 nEndIndex ) const 3137 { 3138 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::getSubsetArea()" ); 3139 3140 ActionVector::const_iterator aRangeBegin; 3141 ActionVector::const_iterator aRangeEnd; 3142 3143 if( !getSubsetIndices( nStartIndex, nEndIndex, 3144 aRangeBegin, aRangeEnd ) ) 3145 return ::basegfx::B2DRange(); // nothing to render -> empty range 3146 3147 // now, aRangeBegin references the action in which the 3148 // subset querying must start, and aRangeEnd references 3149 // the action in which the subset querying must end (it 3150 // might also end right at the start of the referenced 3151 // action, such that zero of that action needs to be 3152 // queried). 3153 3154 3155 // query bounds for subset of actions 3156 // ================================== 3157 3158 ::basegfx::B2DHomMatrix aMatrix; 3159 ::canvas::tools::getRenderStateTransform( aMatrix, 3160 getRenderState() ); 3161 3162 AreaQuery aQuery( aMatrix ); 3163 forSubsetRange( aQuery, 3164 aRangeBegin, 3165 aRangeEnd, 3166 nStartIndex, 3167 nEndIndex, 3168 maActions.end() ); 3169 3170 return aQuery.getBounds(); 3171 } 3172 3173 bool ImplRenderer::draw() const 3174 { 3175 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::draw()" ); 3176 3177 ::basegfx::B2DHomMatrix aMatrix; 3178 ::canvas::tools::getRenderStateTransform( aMatrix, 3179 getRenderState() ); 3180 3181 try 3182 { 3183 return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer( aMatrix ) ).result(); 3184 } 3185 catch( uno::Exception& ) 3186 { 3187 OSL_ENSURE( false, 3188 rtl::OUStringToOString( 3189 comphelper::anyToString( cppu::getCaughtException() ), 3190 RTL_TEXTENCODING_UTF8 ).getStr() ); 3191 3192 return false; 3193 } 3194 } 3195 } 3196 } 3197