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().HasPolyPolygonOrB2DPolyPolygon() ) 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 basegfx::B2DPolyPolygon aPolyPolygon(pClipAction->GetRegion().GetAsB2DPolyPolygon()); 1422 1423 aPolyPolygon.transform(rVDev.GetViewTransformation()); 1424 updateClipping( 1425 aPolyPolygon, 1426 rFactoryParms, 1427 false ); 1428 } 1429 } 1430 1431 break; 1432 } 1433 1434 case META_ISECTRECTCLIPREGION_ACTION: 1435 { 1436 MetaISectRectClipRegionAction* pClipAction = static_cast<MetaISectRectClipRegionAction*>(pCurrAct); 1437 1438 // #121806# explicitely kept integer 1439 Rectangle aClipRect( 1440 rVDev.LogicToPixel( pClipAction->GetRect() ) ); 1441 1442 // intersect current clip with given rect 1443 updateClipping( 1444 aClipRect, 1445 rFactoryParms, 1446 true ); 1447 1448 break; 1449 } 1450 1451 case META_ISECTREGIONCLIPREGION_ACTION: 1452 { 1453 MetaISectRegionClipRegionAction* pClipAction = static_cast<MetaISectRegionClipRegionAction*>(pCurrAct); 1454 1455 if( !pClipAction->GetRegion().HasPolyPolygonOrB2DPolyPolygon() ) 1456 { 1457 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip " 1458 "region encountered, falling back to bounding box!" ); 1459 1460 // #121806# explicitely kept integer 1461 Rectangle aClipRect( 1462 rVDev.LogicToPixel( pClipAction->GetRegion().GetBoundRect() ) ); 1463 1464 // intersect current clip with given rect 1465 updateClipping( 1466 aClipRect, 1467 rFactoryParms, 1468 true ); 1469 } 1470 else 1471 { 1472 // intersect current clip with given clip polygon 1473 1474 // #121806# explicitely kept integer 1475 basegfx::B2DPolyPolygon aPolyPolygon(pClipAction->GetRegion().GetAsB2DPolyPolygon()); 1476 1477 aPolyPolygon.transform(rVDev.GetViewTransformation()); 1478 updateClipping( 1479 aPolyPolygon, 1480 rFactoryParms, 1481 true ); 1482 } 1483 1484 break; 1485 } 1486 1487 case META_MOVECLIPREGION_ACTION: 1488 // TODO(F2): NYI 1489 break; 1490 1491 case META_LINECOLOR_ACTION: 1492 if( !rParms.maLineColor.is_initialized() ) 1493 { 1494 setStateColor( static_cast<MetaLineColorAction*>(pCurrAct), 1495 getState( rStates ).isLineColorSet, 1496 getState( rStates ).lineColor, 1497 rCanvas ); 1498 } 1499 else 1500 { 1501 // #120994# Do switch on/off LineColor, even when a overriding one is set 1502 bool bSetting(static_cast<MetaLineColorAction*>(pCurrAct)->IsSetting()); 1503 1504 getState( rStates ).isLineColorSet = bSetting; 1505 } 1506 break; 1507 1508 case META_FILLCOLOR_ACTION: 1509 if( !rParms.maFillColor.is_initialized() ) 1510 { 1511 setStateColor( static_cast<MetaFillColorAction*>(pCurrAct), 1512 getState( rStates ).isFillColorSet, 1513 getState( rStates ).fillColor, 1514 rCanvas ); 1515 } 1516 else 1517 { 1518 // #120994# Do switch on/off FillColor, even when a overriding one is set 1519 bool bSetting(static_cast<MetaFillColorAction*>(pCurrAct)->IsSetting()); 1520 1521 getState( rStates ).isFillColorSet = bSetting; 1522 } 1523 break; 1524 1525 case META_TEXTCOLOR_ACTION: 1526 { 1527 if( !rParms.maTextColor.is_initialized() ) 1528 { 1529 // Text color is set unconditionally, thus, no 1530 // use of setStateColor here 1531 ::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() ); 1532 1533 // force alpha part of color to 1534 // opaque. transparent painting is done 1535 // explicitely via META_TRANSPARENT_ACTION 1536 aColor.SetTransparency(0); 1537 1538 getState( rStates ).textColor = 1539 ::vcl::unotools::colorToDoubleSequence( 1540 aColor, 1541 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() ); 1542 } 1543 } 1544 break; 1545 1546 case META_TEXTFILLCOLOR_ACTION: 1547 if( !rParms.maTextColor.is_initialized() ) 1548 { 1549 setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct), 1550 getState( rStates ).isTextFillColorSet, 1551 getState( rStates ).textFillColor, 1552 rCanvas ); 1553 } 1554 break; 1555 1556 case META_TEXTLINECOLOR_ACTION: 1557 if( !rParms.maTextColor.is_initialized() ) 1558 { 1559 setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct), 1560 getState( rStates ).isTextLineColorSet, 1561 getState( rStates ).textLineColor, 1562 rCanvas ); 1563 } 1564 break; 1565 1566 case META_TEXTALIGN_ACTION: 1567 { 1568 ::cppcanvas::internal::OutDevState& rState = getState( rStates ); 1569 const TextAlign eTextAlign( static_cast<MetaTextAlignAction*>(pCurrAct)->GetTextAlign() ); 1570 1571 rState.textReferencePoint = eTextAlign; 1572 } 1573 break; 1574 1575 case META_FONT_ACTION: 1576 { 1577 ::cppcanvas::internal::OutDevState& rState = getState( rStates ); 1578 const ::Font& rFont( static_cast<MetaFontAction*>(pCurrAct)->GetFont() ); 1579 1580 rState.xFont = createFont( rState.fontRotation, 1581 rFont, 1582 rFactoryParms ); 1583 1584 // TODO(Q2): define and use appropriate enumeration types 1585 rState.textReliefStyle = (sal_Int8)rFont.GetRelief(); 1586 rState.textOverlineStyle = (sal_Int8)rFont.GetOverline(); 1587 rState.textUnderlineStyle = rParms.maFontUnderline.is_initialized() ? 1588 (*rParms.maFontUnderline ? (sal_Int8)UNDERLINE_SINGLE : (sal_Int8)UNDERLINE_NONE) : 1589 (sal_Int8)rFont.GetUnderline(); 1590 rState.textStrikeoutStyle = (sal_Int8)rFont.GetStrikeout(); 1591 rState.textEmphasisMarkStyle = (sal_Int8)rFont.GetEmphasisMark(); 1592 rState.isTextEffectShadowSet = (rFont.IsShadow() != sal_False); 1593 rState.isTextWordUnderlineSet = (rFont.IsWordLineMode() != sal_False); 1594 rState.isTextOutlineModeSet = (rFont.IsOutline() != sal_False); 1595 } 1596 break; 1597 1598 case META_RASTEROP_ACTION: 1599 // TODO(F2): NYI 1600 break; 1601 1602 case META_LAYOUTMODE_ACTION: 1603 { 1604 // TODO(F2): A lot is missing here 1605 int nLayoutMode = static_cast<MetaLayoutModeAction*>(pCurrAct)->GetLayoutMode(); 1606 ::cppcanvas::internal::OutDevState& rState = getState( rStates ); 1607 switch( nLayoutMode & (TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_BIDI_STRONG) ) 1608 { 1609 case TEXT_LAYOUT_BIDI_LTR: 1610 rState.textDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT; 1611 break; 1612 1613 case (TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG): 1614 rState.textDirection = rendering::TextDirection::STRONG_LEFT_TO_RIGHT; 1615 break; 1616 1617 case TEXT_LAYOUT_BIDI_RTL: 1618 rState.textDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT; 1619 break; 1620 1621 case (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG): 1622 rState.textDirection = rendering::TextDirection::STRONG_RIGHT_TO_LEFT; 1623 break; 1624 } 1625 1626 rState.textAlignment = 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED; 1627 if( (nLayoutMode & (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_RIGHT) ) 1628 && !(nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT ) ) 1629 { 1630 rState.textAlignment = 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED; 1631 } 1632 } 1633 break; 1634 1635 // ------------------------------------------------------------ 1636 1637 // In the second part of this monster-switch, we 1638 // handle all recursing meta actions. These are the 1639 // ones generating a metafile by themselves, which is 1640 // then processed by recursively calling this method. 1641 1642 // ------------------------------------------------------------ 1643 1644 case META_GRADIENT_ACTION: 1645 { 1646 MetaGradientAction* pGradAct = static_cast<MetaGradientAction*>(pCurrAct); 1647 createGradientAction( ::Polygon( pGradAct->GetRect() ), 1648 pGradAct->GetGradient(), 1649 rFactoryParms, 1650 true, 1651 bSubsettableActions ); 1652 } 1653 break; 1654 1655 case META_HATCH_ACTION: 1656 { 1657 // TODO(F2): use native Canvas hatches here 1658 GDIMetaFile aTmpMtf; 1659 1660 rVDev.AddHatchActions( static_cast<MetaHatchAction*>(pCurrAct)->GetPolyPolygon(), 1661 static_cast<MetaHatchAction*>(pCurrAct)->GetHatch(), 1662 aTmpMtf ); 1663 createActions( aTmpMtf, rFactoryParms, 1664 bSubsettableActions ); 1665 } 1666 break; 1667 1668 case META_EPS_ACTION: 1669 { 1670 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pCurrAct); 1671 const GDIMetaFile& rSubstitute = pAct->GetSubstitute(); 1672 1673 // #121806# explicitely kept integer 1674 const Size aMtfSize( rSubstitute.GetPrefSize() ); 1675 const Size aMtfSizePixPre( rVDev.LogicToPixel( aMtfSize, 1676 rSubstitute.GetPrefMapMode() ) ); 1677 1678 // #i44110# correct null-sized output - there 1679 // are metafiles which have zero size in at 1680 // least one dimension 1681 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ), 1682 ::std::max( aMtfSizePixPre.Height(), 1L ) ); 1683 1684 // Setup local transform, such that the 1685 // metafile renders itself into the given 1686 // output rectangle 1687 pushState( rStates, PUSH_ALL ); 1688 1689 rVDev.Push(); 1690 rVDev.SetMapMode( rSubstitute.GetPrefMapMode() ); 1691 1692 const ::Point& rPos( rVDev.LogicToPixel( pAct->GetPoint() ) ); 1693 const ::Size& rSize( rVDev.LogicToPixel( pAct->GetSize() ) ); 1694 1695 getState( rStates ).transform.translate( rPos.X(), 1696 rPos.Y() ); 1697 getState( rStates ).transform.scale( (double)rSize.Width() / aMtfSizePix.Width(), 1698 (double)rSize.Height() / aMtfSizePix.Height() ); 1699 1700 createActions( const_cast<GDIMetaFile&>(pAct->GetSubstitute()), 1701 rFactoryParms, 1702 bSubsettableActions ); 1703 1704 rVDev.Pop(); 1705 popState( rStates ); 1706 } 1707 break; 1708 1709 // handle metafile comments, to retrieve 1710 // meta-information for gradients, fills and 1711 // strokes. May skip actions, and may recurse. 1712 case META_COMMENT_ACTION: 1713 { 1714 MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pCurrAct); 1715 1716 // Handle gradients 1717 if ( pAct->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_BEGIN" ) == COMPARE_EQUAL ) 1718 { 1719 MetaGradientExAction* pGradAction = NULL; 1720 bool bDone( false ); 1721 while( !bDone && 1722 (pCurrAct=rMtf.NextAction()) != NULL ) 1723 { 1724 switch( pCurrAct->GetType() ) 1725 { 1726 // extract gradient info 1727 case META_GRADIENTEX_ACTION: 1728 pGradAction = static_cast<MetaGradientExAction*>(pCurrAct); 1729 break; 1730 1731 // skip broken-down rendering, output gradient when sequence is ended 1732 case META_COMMENT_ACTION: 1733 if( static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_END" ) == COMPARE_EQUAL ) 1734 { 1735 bDone = true; 1736 1737 if( pGradAction ) 1738 { 1739 createGradientAction( pGradAction->GetPolyPolygon(), 1740 pGradAction->GetGradient(), 1741 rFactoryParms, 1742 false, 1743 bSubsettableActions ); 1744 } 1745 } 1746 break; 1747 } 1748 } 1749 } 1750 // TODO(P2): Handle drawing layer strokes, via 1751 // XPATHSTROKE_SEQ_BEGIN comment 1752 1753 // Handle drawing layer fills 1754 else if( pAct->GetComment().Equals( "XPATHFILL_SEQ_BEGIN" ) ) 1755 { 1756 const sal_uInt8* pData = pAct->GetData(); 1757 if ( pData ) 1758 { 1759 SvMemoryStream aMemStm( (void*)pData, pAct->GetDataSize(), STREAM_READ ); 1760 1761 SvtGraphicFill aFill; 1762 aMemStm >> aFill; 1763 1764 // TODO(P2): Also handle gradients and 1765 // hatches like this 1766 1767 // only evaluate comment for pure 1768 // bitmap fills. If a transparency 1769 // gradient is involved (denoted by 1770 // the FloatTransparent action), take 1771 // the normal meta actions. 1772 if( aFill.getFillType() == SvtGraphicFill::fillTexture && 1773 !isActionContained( rMtf, 1774 "XPATHFILL_SEQ_END", 1775 META_FLOATTRANSPARENT_ACTION ) ) 1776 { 1777 rendering::Texture aTexture; 1778 1779 // TODO(F1): the SvtGraphicFill 1780 // can also transport metafiles 1781 // here, handle that case, too 1782 Graphic aGraphic; 1783 aFill.getGraphic( aGraphic ); 1784 1785 BitmapEx aBmpEx( aGraphic.GetBitmapEx() ); 1786 const ::Size aBmpSize( aBmpEx.GetSizePixel() ); 1787 1788 ::SvtGraphicFill::Transform aTransform; 1789 aFill.getTransform( aTransform ); 1790 1791 ::basegfx::B2DHomMatrix aMatrix; 1792 1793 // convert to basegfx matrix 1794 aMatrix.set(0,0, aTransform.matrix[ 0 ] ); 1795 aMatrix.set(0,1, aTransform.matrix[ 1 ] ); 1796 aMatrix.set(0,2, aTransform.matrix[ 2 ] ); 1797 aMatrix.set(1,0, aTransform.matrix[ 3 ] ); 1798 aMatrix.set(1,1, aTransform.matrix[ 4 ] ); 1799 aMatrix.set(1,2, aTransform.matrix[ 5 ] ); 1800 1801 ::basegfx::B2DHomMatrix aScale; 1802 aScale.scale( aBmpSize.Width(), 1803 aBmpSize.Height() ); 1804 1805 // post-multiply with the bitmap 1806 // size (XCanvas' texture assumes 1807 // the given bitmap to be 1808 // normalized to [0,1]x[0,1] 1809 // rectangle) 1810 aMatrix = aMatrix * aScale; 1811 1812 // pre-multiply with the 1813 // logic-to-pixel scale factor 1814 // (the metafile comment works in 1815 // logical coordinates). 1816 ::basegfx::B2DHomMatrix aLogic2PixelTransform; 1817 aMatrix *= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform, 1818 rVDev ); 1819 1820 ::basegfx::unotools::affineMatrixFromHomMatrix( 1821 aTexture.AffineTransform, 1822 aMatrix ); 1823 1824 aTexture.Alpha = 1.0 - aFill.getTransparency(); 1825 aTexture.Bitmap = 1826 ::vcl::unotools::xBitmapFromBitmapEx( 1827 rCanvas->getUNOCanvas()->getDevice(), 1828 aBmpEx ); 1829 if( aFill.isTiling() ) 1830 { 1831 aTexture.RepeatModeX = rendering::TexturingMode::REPEAT; 1832 aTexture.RepeatModeY = rendering::TexturingMode::REPEAT; 1833 } 1834 else 1835 { 1836 aTexture.RepeatModeX = rendering::TexturingMode::NONE; 1837 aTexture.RepeatModeY = rendering::TexturingMode::NONE; 1838 } 1839 1840 ::PolyPolygon aPath; 1841 aFill.getPath( aPath ); 1842 1843 ::basegfx::B2DPolyPolygon aPoly( aPath.getB2DPolyPolygon() ); 1844 aPoly.transform( getState( rStates ).mapModeTransform ); 1845 ActionSharedPtr pPolyAction( 1846 internal::PolyPolyActionFactory::createPolyPolyAction( 1847 aPoly, 1848 rCanvas, 1849 getState( rStates ), 1850 aTexture ) ); 1851 1852 if( pPolyAction ) 1853 { 1854 maActions.push_back( 1855 MtfAction( 1856 pPolyAction, 1857 io_rCurrActionIndex ) ); 1858 1859 io_rCurrActionIndex += pPolyAction->getActionCount()-1; 1860 } 1861 1862 // skip broken-down render output 1863 skipContent( rMtf, 1864 "XPATHFILL_SEQ_END", 1865 io_rCurrActionIndex ); 1866 } 1867 } 1868 } 1869 } 1870 break; 1871 1872 // ------------------------------------------------------------ 1873 1874 // In the third part of this monster-switch, we 1875 // handle all 'acting' meta actions. These are all 1876 // processed by constructing function objects for 1877 // them, which will later ease caching. 1878 1879 // ------------------------------------------------------------ 1880 1881 case META_POINT_ACTION: 1882 { 1883 const OutDevState& rState( getState( rStates ) ); 1884 if( rState.lineColor.getLength() ) 1885 { 1886 ActionSharedPtr pPointAction( 1887 internal::PointActionFactory::createPointAction( 1888 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( 1889 static_cast<MetaPointAction*>(pCurrAct)->GetPoint() ), 1890 rCanvas, 1891 rState ) ); 1892 1893 if( pPointAction ) 1894 { 1895 maActions.push_back( 1896 MtfAction( 1897 pPointAction, 1898 io_rCurrActionIndex ) ); 1899 1900 io_rCurrActionIndex += pPointAction->getActionCount()-1; 1901 } 1902 } 1903 } 1904 break; 1905 1906 case META_PIXEL_ACTION: 1907 { 1908 const OutDevState& rState( getState( rStates ) ); 1909 if( rState.lineColor.getLength() ) 1910 { 1911 ActionSharedPtr pPointAction( 1912 internal::PointActionFactory::createPointAction( 1913 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( 1914 static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ), 1915 rCanvas, 1916 rState, 1917 static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) ); 1918 1919 if( pPointAction ) 1920 { 1921 maActions.push_back( 1922 MtfAction( 1923 pPointAction, 1924 io_rCurrActionIndex ) ); 1925 1926 io_rCurrActionIndex += pPointAction->getActionCount()-1; 1927 } 1928 } 1929 } 1930 break; 1931 1932 case META_LINE_ACTION: 1933 { 1934 const OutDevState& rState( getState( rStates ) ); 1935 if( rState.lineColor.getLength() ) 1936 { 1937 MetaLineAction* pLineAct = static_cast<MetaLineAction*>(pCurrAct); 1938 1939 const LineInfo& rLineInfo( pLineAct->GetLineInfo() ); 1940 1941 const ::basegfx::B2DPoint aStartPoint( 1942 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetStartPoint() )); 1943 const ::basegfx::B2DPoint aEndPoint( 1944 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetEndPoint() )); 1945 1946 ActionSharedPtr pLineAction; 1947 1948 if( rLineInfo.IsDefault() ) 1949 { 1950 // plain hair line 1951 pLineAction = 1952 internal::LineActionFactory::createLineAction( 1953 aStartPoint, 1954 aEndPoint, 1955 rCanvas, 1956 rState ); 1957 1958 if( pLineAction ) 1959 { 1960 maActions.push_back( 1961 MtfAction( 1962 pLineAction, 1963 io_rCurrActionIndex ) ); 1964 1965 io_rCurrActionIndex += pLineAction->getActionCount()-1; 1966 } 1967 } 1968 else if( LINE_NONE != rLineInfo.GetStyle() ) 1969 { 1970 // 'thick' line 1971 rendering::StrokeAttributes aStrokeAttributes; 1972 1973 setupStrokeAttributes( aStrokeAttributes, 1974 rFactoryParms, 1975 rLineInfo ); 1976 1977 // XCanvas can only stroke polygons, 1978 // not simple lines - thus, handle 1979 // this case via the polypolygon 1980 // action 1981 ::basegfx::B2DPolygon aPoly; 1982 aPoly.append( aStartPoint ); 1983 aPoly.append( aEndPoint ); 1984 pLineAction = 1985 internal::PolyPolyActionFactory::createPolyPolyAction( 1986 ::basegfx::B2DPolyPolygon( aPoly ), 1987 rCanvas, rState, aStrokeAttributes ); 1988 1989 if( pLineAction ) 1990 { 1991 maActions.push_back( 1992 MtfAction( 1993 pLineAction, 1994 io_rCurrActionIndex ) ); 1995 1996 io_rCurrActionIndex += pLineAction->getActionCount()-1; 1997 } 1998 } 1999 // else: line style is default 2000 // (i.e. invisible), don't generate action 2001 } 2002 } 2003 break; 2004 2005 case META_RECT_ACTION: 2006 { 2007 const Rectangle& rRect( 2008 static_cast<MetaRectAction*>(pCurrAct)->GetRect() ); 2009 2010 if( rRect.IsEmpty() ) 2011 break; 2012 2013 const OutDevState& rState( getState( rStates ) ); 2014 const ::basegfx::B2DPoint aTopLeftPixel( 2015 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ) ); 2016 const ::basegfx::B2DPoint aBottomRightPixel( 2017 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) + 2018 // #121100# OutputDevice::DrawRect() fills 2019 // rectangles Apple-like, i.e. with one 2020 // additional pixel to the right and bottom. 2021 ::basegfx::B2DPoint(1,1) ); 2022 2023 createFillAndStroke( ::basegfx::tools::createPolygonFromRect( 2024 ::basegfx::B2DRange( aTopLeftPixel, 2025 aBottomRightPixel )), 2026 rFactoryParms ); 2027 break; 2028 } 2029 2030 case META_ROUNDRECT_ACTION: 2031 { 2032 const Rectangle& rRect( 2033 static_cast<MetaRoundRectAction*>(pCurrAct)->GetRect()); 2034 2035 if( rRect.IsEmpty() ) 2036 break; 2037 2038 ::basegfx::B2DPolygon aPoly( 2039 ::basegfx::tools::createPolygonFromRect( 2040 ::basegfx::B2DRange( 2041 ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ), 2042 ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) + 2043 ::basegfx::B2DPoint(1,1) ), 2044 static_cast<MetaRoundRectAction*>(pCurrAct)->GetHorzRound(), 2045 static_cast<MetaRoundRectAction*>(pCurrAct)->GetVertRound() )); 2046 aPoly.transform( getState( rStates ).mapModeTransform ); 2047 2048 createFillAndStroke( aPoly, 2049 rFactoryParms ); 2050 } 2051 break; 2052 2053 case META_ELLIPSE_ACTION: 2054 { 2055 const Rectangle& rRect( 2056 static_cast<MetaEllipseAction*>(pCurrAct)->GetRect() ); 2057 2058 if( rRect.IsEmpty() ) 2059 break; 2060 2061 const ::basegfx::B2DRange aRange( 2062 ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ), 2063 ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) + 2064 ::basegfx::B2DPoint(1,1) ); 2065 2066 ::basegfx::B2DPolygon aPoly( 2067 ::basegfx::tools::createPolygonFromEllipse( 2068 aRange.getCenter(), 2069 aRange.getWidth(), 2070 aRange.getHeight() )); 2071 aPoly.transform( getState( rStates ).mapModeTransform ); 2072 2073 createFillAndStroke( aPoly, 2074 rFactoryParms ); 2075 } 2076 break; 2077 2078 case META_ARC_ACTION: 2079 { 2080 // TODO(F1): Missing basegfx functionality. Mind empty rects! 2081 const Polygon aToolsPoly( static_cast<MetaArcAction*>(pCurrAct)->GetRect(), 2082 static_cast<MetaArcAction*>(pCurrAct)->GetStartPoint(), 2083 static_cast<MetaArcAction*>(pCurrAct)->GetEndPoint(), POLY_ARC ); 2084 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() ); 2085 aPoly.transform( getState( rStates ).mapModeTransform ); 2086 2087 createFillAndStroke( aPoly, 2088 rFactoryParms ); 2089 } 2090 break; 2091 2092 case META_PIE_ACTION: 2093 { 2094 // TODO(F1): Missing basegfx functionality. Mind empty rects! 2095 const Polygon aToolsPoly( static_cast<MetaPieAction*>(pCurrAct)->GetRect(), 2096 static_cast<MetaPieAction*>(pCurrAct)->GetStartPoint(), 2097 static_cast<MetaPieAction*>(pCurrAct)->GetEndPoint(), POLY_PIE ); 2098 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() ); 2099 aPoly.transform( getState( rStates ).mapModeTransform ); 2100 2101 createFillAndStroke( aPoly, 2102 rFactoryParms ); 2103 } 2104 break; 2105 2106 case META_CHORD_ACTION: 2107 { 2108 // TODO(F1): Missing basegfx functionality. Mind empty rects! 2109 const Polygon aToolsPoly( static_cast<MetaChordAction*>(pCurrAct)->GetRect(), 2110 static_cast<MetaChordAction*>(pCurrAct)->GetStartPoint(), 2111 static_cast<MetaChordAction*>(pCurrAct)->GetEndPoint(), POLY_CHORD ); 2112 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() ); 2113 aPoly.transform( getState( rStates ).mapModeTransform ); 2114 2115 createFillAndStroke( aPoly, 2116 rFactoryParms ); 2117 } 2118 break; 2119 2120 case META_POLYLINE_ACTION: 2121 { 2122 const OutDevState& rState( getState( rStates ) ); 2123 if( rState.lineColor.getLength() || 2124 rState.fillColor.getLength() ) 2125 { 2126 MetaPolyLineAction* pPolyLineAct = static_cast<MetaPolyLineAction*>(pCurrAct); 2127 2128 const LineInfo& rLineInfo( pPolyLineAct->GetLineInfo() ); 2129 ::basegfx::B2DPolygon aPoly( pPolyLineAct->GetPolygon().getB2DPolygon() ); 2130 aPoly.transform( rState.mapModeTransform ); 2131 2132 ActionSharedPtr pLineAction; 2133 2134 if( rLineInfo.IsDefault() ) 2135 { 2136 // plain hair line polygon 2137 pLineAction = 2138 internal::PolyPolyActionFactory::createLinePolyPolyAction( 2139 ::basegfx::B2DPolyPolygon(aPoly), 2140 rCanvas, 2141 rState ); 2142 2143 if( pLineAction ) 2144 { 2145 maActions.push_back( 2146 MtfAction( 2147 pLineAction, 2148 io_rCurrActionIndex ) ); 2149 2150 io_rCurrActionIndex += pLineAction->getActionCount()-1; 2151 } 2152 } 2153 else if( LINE_NONE != rLineInfo.GetStyle() ) 2154 { 2155 // 'thick' line polygon 2156 rendering::StrokeAttributes aStrokeAttributes; 2157 2158 setupStrokeAttributes( aStrokeAttributes, 2159 rFactoryParms, 2160 rLineInfo ); 2161 2162 pLineAction = 2163 internal::PolyPolyActionFactory::createPolyPolyAction( 2164 ::basegfx::B2DPolyPolygon(aPoly), 2165 rCanvas, 2166 rState, 2167 aStrokeAttributes ) ; 2168 2169 if( pLineAction ) 2170 { 2171 maActions.push_back( 2172 MtfAction( 2173 pLineAction, 2174 io_rCurrActionIndex ) ); 2175 2176 io_rCurrActionIndex += pLineAction->getActionCount()-1; 2177 } 2178 } 2179 // else: line style is default 2180 // (i.e. invisible), don't generate action 2181 } 2182 } 2183 break; 2184 2185 case META_POLYGON_ACTION: 2186 { 2187 ::basegfx::B2DPolygon aPoly( static_cast<MetaPolygonAction*>(pCurrAct)->GetPolygon().getB2DPolygon() ); 2188 aPoly.transform( getState( rStates ).mapModeTransform ); 2189 createFillAndStroke( aPoly, 2190 rFactoryParms ); 2191 } 2192 break; 2193 2194 case META_POLYPOLYGON_ACTION: 2195 { 2196 ::basegfx::B2DPolyPolygon aPoly( static_cast<MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon().getB2DPolyPolygon() ); 2197 aPoly.transform( getState( rStates ).mapModeTransform ); 2198 createFillAndStroke( aPoly, 2199 rFactoryParms ); 2200 } 2201 break; 2202 2203 case META_BMP_ACTION: 2204 { 2205 MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pCurrAct); 2206 2207 ActionSharedPtr pBmpAction( 2208 internal::BitmapActionFactory::createBitmapAction( 2209 pAct->GetBitmap(), 2210 getState( rStates ).mapModeTransform * 2211 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2212 rCanvas, 2213 getState( rStates ) ) ); 2214 2215 if( pBmpAction ) 2216 { 2217 maActions.push_back( 2218 MtfAction( 2219 pBmpAction, 2220 io_rCurrActionIndex ) ); 2221 2222 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2223 } 2224 } 2225 break; 2226 2227 case META_BMPSCALE_ACTION: 2228 { 2229 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pCurrAct); 2230 2231 ActionSharedPtr pBmpAction( 2232 internal::BitmapActionFactory::createBitmapAction( 2233 pAct->GetBitmap(), 2234 getState( rStates ).mapModeTransform * 2235 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2236 getState( rStates ).mapModeTransform * 2237 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ), 2238 rCanvas, 2239 getState( rStates ) ) ); 2240 2241 if( pBmpAction ) 2242 { 2243 maActions.push_back( 2244 MtfAction( 2245 pBmpAction, 2246 io_rCurrActionIndex ) ); 2247 2248 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2249 } 2250 } 2251 break; 2252 2253 case META_BMPSCALEPART_ACTION: 2254 { 2255 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pCurrAct); 2256 2257 // crop bitmap to given source rectangle (no 2258 // need to copy and convert the whole bitmap) 2259 Bitmap aBmp( pAct->GetBitmap() ); 2260 const Rectangle aCropRect( pAct->GetSrcPoint(), 2261 pAct->GetSrcSize() ); 2262 aBmp.Crop( aCropRect ); 2263 2264 ActionSharedPtr pBmpAction( 2265 internal::BitmapActionFactory::createBitmapAction( 2266 aBmp, 2267 getState( rStates ).mapModeTransform * 2268 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ), 2269 getState( rStates ).mapModeTransform * 2270 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ), 2271 rCanvas, 2272 getState( rStates ) ) ); 2273 2274 if( pBmpAction ) 2275 { 2276 maActions.push_back( 2277 MtfAction( 2278 pBmpAction, 2279 io_rCurrActionIndex ) ); 2280 2281 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2282 } 2283 } 2284 break; 2285 2286 case META_BMPEX_ACTION: 2287 { 2288 MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pCurrAct); 2289 2290 ActionSharedPtr pBmpAction( 2291 internal::BitmapActionFactory::createBitmapAction( 2292 pAct->GetBitmapEx(), 2293 getState( rStates ).mapModeTransform * 2294 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2295 rCanvas, 2296 getState( rStates ) ) ); 2297 2298 if( pBmpAction ) 2299 { 2300 maActions.push_back( 2301 MtfAction( 2302 pBmpAction, 2303 io_rCurrActionIndex ) ); 2304 2305 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2306 } 2307 } 2308 break; 2309 2310 case META_BMPEXSCALE_ACTION: 2311 { 2312 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pCurrAct); 2313 2314 ActionSharedPtr pBmpAction( 2315 internal::BitmapActionFactory::createBitmapAction( 2316 pAct->GetBitmapEx(), 2317 getState( rStates ).mapModeTransform * 2318 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2319 getState( rStates ).mapModeTransform * 2320 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ), 2321 rCanvas, 2322 getState( rStates ) ) ); 2323 2324 if( pBmpAction ) 2325 { 2326 maActions.push_back( 2327 MtfAction( 2328 pBmpAction, 2329 io_rCurrActionIndex ) ); 2330 2331 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2332 } 2333 } 2334 break; 2335 2336 case META_BMPEXSCALEPART_ACTION: 2337 { 2338 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pCurrAct); 2339 2340 // crop bitmap to given source rectangle (no 2341 // need to copy and convert the whole bitmap) 2342 BitmapEx aBmp( pAct->GetBitmapEx() ); 2343 const Rectangle aCropRect( pAct->GetSrcPoint(), 2344 pAct->GetSrcSize() ); 2345 aBmp.Crop( aCropRect ); 2346 2347 ActionSharedPtr pBmpAction( 2348 internal::BitmapActionFactory::createBitmapAction( 2349 aBmp, 2350 getState( rStates ).mapModeTransform * 2351 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ), 2352 getState( rStates ).mapModeTransform * 2353 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ), 2354 rCanvas, 2355 getState( rStates ) ) ); 2356 2357 if( pBmpAction ) 2358 { 2359 maActions.push_back( 2360 MtfAction( 2361 pBmpAction, 2362 io_rCurrActionIndex ) ); 2363 2364 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2365 } 2366 } 2367 break; 2368 2369 case META_MASK_ACTION: 2370 { 2371 MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pCurrAct); 2372 2373 // create masked BitmapEx right here, as the 2374 // canvas does not provide equivalent 2375 // functionality 2376 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(), 2377 pAct->GetColor() )); 2378 2379 ActionSharedPtr pBmpAction( 2380 internal::BitmapActionFactory::createBitmapAction( 2381 aBmp, 2382 getState( rStates ).mapModeTransform * 2383 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2384 rCanvas, 2385 getState( rStates ) ) ); 2386 2387 if( pBmpAction ) 2388 { 2389 maActions.push_back( 2390 MtfAction( 2391 pBmpAction, 2392 io_rCurrActionIndex ) ); 2393 2394 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2395 } 2396 } 2397 break; 2398 2399 case META_MASKSCALE_ACTION: 2400 { 2401 MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pCurrAct); 2402 2403 // create masked BitmapEx right here, as the 2404 // canvas does not provide equivalent 2405 // functionality 2406 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(), 2407 pAct->GetColor() )); 2408 2409 ActionSharedPtr pBmpAction( 2410 internal::BitmapActionFactory::createBitmapAction( 2411 aBmp, 2412 getState( rStates ).mapModeTransform * 2413 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2414 getState( rStates ).mapModeTransform * 2415 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ), 2416 rCanvas, 2417 getState( rStates ) ) ); 2418 2419 if( pBmpAction ) 2420 { 2421 maActions.push_back( 2422 MtfAction( 2423 pBmpAction, 2424 io_rCurrActionIndex ) ); 2425 2426 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2427 } 2428 } 2429 break; 2430 2431 case META_MASKSCALEPART_ACTION: 2432 { 2433 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pCurrAct); 2434 2435 // create masked BitmapEx right here, as the 2436 // canvas does not provide equivalent 2437 // functionality 2438 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(), 2439 pAct->GetColor() )); 2440 2441 // crop bitmap to given source rectangle (no 2442 // need to copy and convert the whole bitmap) 2443 const Rectangle aCropRect( pAct->GetSrcPoint(), 2444 pAct->GetSrcSize() ); 2445 aBmp.Crop( aCropRect ); 2446 2447 ActionSharedPtr pBmpAction( 2448 internal::BitmapActionFactory::createBitmapAction( 2449 aBmp, 2450 getState( rStates ).mapModeTransform * 2451 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ), 2452 getState( rStates ).mapModeTransform * 2453 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ), 2454 rCanvas, 2455 getState( rStates ) ) ); 2456 2457 if( pBmpAction ) 2458 { 2459 maActions.push_back( 2460 MtfAction( 2461 pBmpAction, 2462 io_rCurrActionIndex ) ); 2463 2464 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2465 } 2466 } 2467 break; 2468 2469 case META_GRADIENTEX_ACTION: 2470 // TODO(F1): use native Canvas gradients here 2471 // action is ignored here, because redundant to META_GRADIENT_ACTION 2472 break; 2473 2474 case META_WALLPAPER_ACTION: 2475 // TODO(F2): NYI 2476 break; 2477 2478 case META_TRANSPARENT_ACTION: 2479 { 2480 const OutDevState& rState( getState( rStates ) ); 2481 if( rState.lineColor.getLength() || 2482 rState.fillColor.getLength() ) 2483 { 2484 MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pCurrAct); 2485 ::basegfx::B2DPolyPolygon aPoly( pAct->GetPolyPolygon().getB2DPolyPolygon() ); 2486 aPoly.transform( rState.mapModeTransform ); 2487 2488 ActionSharedPtr pPolyAction( 2489 internal::PolyPolyActionFactory::createPolyPolyAction( 2490 aPoly, 2491 rCanvas, 2492 rState, 2493 pAct->GetTransparence() ) ); 2494 2495 if( pPolyAction ) 2496 { 2497 maActions.push_back( 2498 MtfAction( 2499 pPolyAction, 2500 io_rCurrActionIndex ) ); 2501 2502 io_rCurrActionIndex += pPolyAction->getActionCount()-1; 2503 } 2504 } 2505 } 2506 break; 2507 2508 case META_FLOATTRANSPARENT_ACTION: 2509 { 2510 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pCurrAct); 2511 2512 internal::MtfAutoPtr pMtf( 2513 new ::GDIMetaFile( pAct->GetGDIMetaFile() ) ); 2514 2515 // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls) 2516 internal::GradientAutoPtr pGradient( 2517 new Gradient( pAct->GetGradient() ) ); 2518 2519 DBG_TESTSOLARMUTEX(); 2520 2521 ActionSharedPtr pFloatTransAction( 2522 internal::TransparencyGroupActionFactory::createTransparencyGroupAction( 2523 pMtf, 2524 pGradient, 2525 rParms, 2526 getState( rStates ).mapModeTransform * 2527 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2528 getState( rStates ).mapModeTransform * 2529 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ), 2530 rCanvas, 2531 getState( rStates ) ) ); 2532 2533 if( pFloatTransAction ) 2534 { 2535 maActions.push_back( 2536 MtfAction( 2537 pFloatTransAction, 2538 io_rCurrActionIndex ) ); 2539 2540 io_rCurrActionIndex += pFloatTransAction->getActionCount()-1; 2541 } 2542 } 2543 break; 2544 2545 case META_TEXT_ACTION: 2546 { 2547 MetaTextAction* pAct = static_cast<MetaTextAction*>(pCurrAct); 2548 XubString sText = XubString( pAct->GetText() ); 2549 2550 if( rVDev.GetDigitLanguage()) 2551 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() ); 2552 2553 createTextAction( 2554 pAct->GetPoint(), 2555 sText, 2556 pAct->GetIndex(), 2557 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(), 2558 NULL, 2559 rFactoryParms, 2560 bSubsettableActions ); 2561 } 2562 break; 2563 2564 case META_TEXTARRAY_ACTION: 2565 { 2566 MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pCurrAct); 2567 XubString sText = XubString( pAct->GetText() ); 2568 2569 if( rVDev.GetDigitLanguage()) 2570 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() ); 2571 2572 createTextAction( 2573 pAct->GetPoint(), 2574 sText, 2575 pAct->GetIndex(), 2576 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(), 2577 pAct->GetDXArray(), 2578 rFactoryParms, 2579 bSubsettableActions ); 2580 } 2581 break; 2582 2583 case META_TEXTLINE_ACTION: 2584 { 2585 MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pCurrAct); 2586 2587 const OutDevState& rState( getState( rStates ) ); 2588 const ::Size aBaselineOffset( tools::getBaselineOffset( rState, 2589 rVDev ) ); 2590 const ::Point aStartPoint( pAct->GetStartPoint() ); 2591 const ::basegfx::B2DSize aSize( rState.mapModeTransform * 2592 ::basegfx::B2DSize(pAct->GetWidth(), 2593 0 )); 2594 2595 ActionSharedPtr pPolyAction( 2596 PolyPolyActionFactory::createPolyPolyAction( 2597 tools::createTextLinesPolyPolygon( 2598 rState.mapModeTransform * 2599 ::basegfx::B2DPoint( 2600 ::vcl::unotools::b2DPointFromPoint(pAct->GetStartPoint()) + 2601 ::vcl::unotools::b2DSizeFromSize(aBaselineOffset)), 2602 aSize.getX(), 2603 tools::createTextLineInfo( rVDev, 2604 rState )), 2605 rCanvas, 2606 rState ) ); 2607 2608 if( pPolyAction.get() ) 2609 { 2610 maActions.push_back( 2611 MtfAction( 2612 pPolyAction, 2613 io_rCurrActionIndex ) ); 2614 2615 io_rCurrActionIndex += pPolyAction->getActionCount()-1; 2616 } 2617 } 2618 break; 2619 2620 case META_TEXTRECT_ACTION: 2621 { 2622 MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pCurrAct); 2623 2624 pushState( rStates, PUSH_ALL ); 2625 2626 // use the VDev to break up the text rect 2627 // action into readily formatted lines 2628 GDIMetaFile aTmpMtf; 2629 rVDev.AddTextRectActions( pAct->GetRect(), 2630 pAct->GetText(), 2631 pAct->GetStyle(), 2632 aTmpMtf ); 2633 2634 createActions( aTmpMtf, 2635 rFactoryParms, 2636 bSubsettableActions ); 2637 2638 popState( rStates ); 2639 2640 break; 2641 } 2642 2643 case META_STRETCHTEXT_ACTION: 2644 { 2645 MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pCurrAct); 2646 XubString sText = XubString( pAct->GetText() ); 2647 2648 if( rVDev.GetDigitLanguage()) 2649 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() ); 2650 2651 const sal_uInt16 nLen( pAct->GetLen() == (sal_uInt16)STRING_LEN ? 2652 pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen() ); 2653 2654 // #i70897# Nothing to do, actually... 2655 if( nLen == 0 ) 2656 break; 2657 2658 // have to fit the text into the given 2659 // width. This is achieved by internally 2660 // generating a DX array, and uniformly 2661 // distributing the excess/insufficient width 2662 // to every logical character. 2663 ::boost::scoped_array< sal_Int32 > pDXArray( new sal_Int32[nLen] ); 2664 2665 rVDev.GetTextArray( pAct->GetText(), pDXArray.get(), 2666 pAct->GetIndex(), pAct->GetLen() ); 2667 2668 const sal_Int32 nWidthDifference( pAct->GetWidth() - pDXArray[ nLen-1 ] ); 2669 2670 // Last entry of pDXArray contains total width of the text 2671 sal_Int32* p=pDXArray.get(); 2672 for( sal_uInt16 i=1; i<=nLen; ++i ) 2673 { 2674 // calc ratio for every array entry, to 2675 // distribute rounding errors 'evenly' 2676 // across the characters. Note that each 2677 // entry represents the 'end' position of 2678 // the corresponding character, thus, we 2679 // let i run from 1 to nLen. 2680 *p++ += (sal_Int32)i*nWidthDifference/nLen; 2681 } 2682 2683 createTextAction( 2684 pAct->GetPoint(), 2685 sText, 2686 pAct->GetIndex(), 2687 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(), 2688 pDXArray.get(), 2689 rFactoryParms, 2690 bSubsettableActions ); 2691 } 2692 break; 2693 2694 default: 2695 OSL_ENSURE( false, 2696 "Unknown meta action type encountered" ); 2697 break; 2698 } 2699 2700 // increment action index (each mtf action counts _at 2701 // least_ one. Some count for more, therefore, 2702 // io_rCurrActionIndex is sometimes incremented by 2703 // pAct->getActionCount()-1 above, the -1 being the 2704 // correction for the unconditional increment here). 2705 ++io_rCurrActionIndex; 2706 } 2707 2708 return true; 2709 } 2710 2711 2712 namespace 2713 { 2714 class ActionRenderer 2715 { 2716 public: 2717 ActionRenderer( const ::basegfx::B2DHomMatrix& rTransformation ) : 2718 maTransformation( rTransformation ), 2719 mbRet( true ) 2720 { 2721 } 2722 2723 bool result() 2724 { 2725 return mbRet; 2726 } 2727 2728 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction ) 2729 { 2730 // ANDing the result. We want to fail if at least 2731 // one action failed. 2732 mbRet &= rAction.mpAction->render( maTransformation ); 2733 } 2734 2735 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction, 2736 const Action::Subset& rSubset ) 2737 { 2738 // ANDing the result. We want to fail if at least 2739 // one action failed. 2740 mbRet &= rAction.mpAction->render( maTransformation, 2741 rSubset ); 2742 } 2743 2744 private: 2745 ::basegfx::B2DHomMatrix maTransformation; 2746 bool mbRet; 2747 }; 2748 2749 class AreaQuery 2750 { 2751 public: 2752 AreaQuery( const ::basegfx::B2DHomMatrix& rTransformation ) : 2753 maTransformation( rTransformation ), 2754 maBounds() 2755 { 2756 } 2757 2758 bool result() 2759 { 2760 return true; // nothing can fail here 2761 } 2762 2763 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction ) 2764 { 2765 maBounds.expand( rAction.mpAction->getBounds( maTransformation ) ); 2766 } 2767 2768 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction, 2769 const Action::Subset& rSubset ) 2770 { 2771 maBounds.expand( rAction.mpAction->getBounds( maTransformation, 2772 rSubset ) ); 2773 } 2774 2775 ::basegfx::B2DRange getBounds() const 2776 { 2777 return maBounds; 2778 } 2779 2780 private: 2781 ::basegfx::B2DHomMatrix maTransformation; 2782 ::basegfx::B2DRange maBounds; 2783 }; 2784 2785 // Doing that via inline class. Compilers tend to not inline free 2786 // functions. 2787 struct UpperBoundActionIndexComparator 2788 { 2789 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rLHS, 2790 const ::cppcanvas::internal::ImplRenderer::MtfAction& rRHS ) 2791 { 2792 const sal_Int32 nLHSCount( rLHS.mpAction ? 2793 rLHS.mpAction->getActionCount() : 0 ); 2794 const sal_Int32 nRHSCount( rRHS.mpAction ? 2795 rRHS.mpAction->getActionCount() : 0 ); 2796 2797 // compare end of action range, to have an action selected 2798 // by lower_bound even if the requested index points in 2799 // the middle of the action's range 2800 return rLHS.mnOrigIndex + nLHSCount < rRHS.mnOrigIndex + nRHSCount; 2801 } 2802 }; 2803 2804 /** Algorithm to apply given functor to a subset range 2805 2806 @tpl Functor 2807 2808 Functor to call for each element of the subset 2809 range. Must provide the following method signatures: 2810 bool result() (returning false if operation failed) 2811 2812 */ 2813 template< typename Functor > bool 2814 forSubsetRange( Functor& rFunctor, 2815 ImplRenderer::ActionVector::const_iterator aRangeBegin, 2816 ImplRenderer::ActionVector::const_iterator aRangeEnd, 2817 sal_Int32 nStartIndex, 2818 sal_Int32 nEndIndex, 2819 const ImplRenderer::ActionVector::const_iterator& rEnd ) 2820 { 2821 if( aRangeBegin == aRangeEnd ) 2822 { 2823 // only a single action. Setup subset, and call functor 2824 Action::Subset aSubset; 2825 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ), 2826 nStartIndex - aRangeBegin->mnOrigIndex ); 2827 aSubset.mnSubsetEnd = ::std::min( aRangeBegin->mpAction->getActionCount(), 2828 nEndIndex - aRangeBegin->mnOrigIndex ); 2829 2830 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0, 2831 "ImplRenderer::forSubsetRange(): Invalid indices" ); 2832 2833 rFunctor( *aRangeBegin, aSubset ); 2834 } 2835 else 2836 { 2837 // more than one action. 2838 2839 // render partial first, full intermediate, and 2840 // partial last action 2841 Action::Subset aSubset; 2842 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ), 2843 nStartIndex - aRangeBegin->mnOrigIndex ); 2844 aSubset.mnSubsetEnd = aRangeBegin->mpAction->getActionCount(); 2845 2846 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0, 2847 "ImplRenderer::forSubsetRange(): Invalid indices" ); 2848 2849 rFunctor( *aRangeBegin, aSubset ); 2850 2851 // first action rendered, skip to next 2852 ++aRangeBegin; 2853 2854 // render full middle actions 2855 while( aRangeBegin != aRangeEnd ) 2856 rFunctor( *aRangeBegin++ ); 2857 2858 if( aRangeEnd == rEnd || 2859 aRangeEnd->mnOrigIndex > nEndIndex ) 2860 { 2861 // aRangeEnd denotes end of action vector, 2862 // 2863 // or 2864 // 2865 // nEndIndex references something _after_ 2866 // aRangeBegin, but _before_ aRangeEnd 2867 // 2868 // either way: no partial action left 2869 return rFunctor.result(); 2870 } 2871 2872 aSubset.mnSubsetBegin = 0; 2873 aSubset.mnSubsetEnd = nEndIndex - aRangeEnd->mnOrigIndex; 2874 2875 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0, 2876 "ImplRenderer::forSubsetRange(): Invalid indices" ); 2877 2878 rFunctor( *aRangeEnd, aSubset ); 2879 } 2880 2881 return rFunctor.result(); 2882 } 2883 } 2884 2885 bool ImplRenderer::getSubsetIndices( sal_Int32& io_rStartIndex, 2886 sal_Int32& io_rEndIndex, 2887 ActionVector::const_iterator& o_rRangeBegin, 2888 ActionVector::const_iterator& o_rRangeEnd ) const 2889 { 2890 ENSURE_OR_RETURN_FALSE( io_rStartIndex<=io_rEndIndex, 2891 "ImplRenderer::getSubsetIndices(): invalid action range" ); 2892 2893 ENSURE_OR_RETURN_FALSE( !maActions.empty(), 2894 "ImplRenderer::getSubsetIndices(): no actions to render" ); 2895 2896 const sal_Int32 nMinActionIndex( maActions.front().mnOrigIndex ); 2897 const sal_Int32 nMaxActionIndex( maActions.back().mnOrigIndex + 2898 maActions.back().mpAction->getActionCount() ); 2899 2900 // clip given range to permissible values (there might be 2901 // ranges before and behind the valid indices) 2902 io_rStartIndex = ::std::max( nMinActionIndex, 2903 io_rStartIndex ); 2904 io_rEndIndex = ::std::min( nMaxActionIndex, 2905 io_rEndIndex ); 2906 2907 if( io_rStartIndex == io_rEndIndex || 2908 io_rStartIndex > io_rEndIndex ) 2909 { 2910 // empty range, don't render anything. The second 2911 // condition e.g. happens if the requested range lies 2912 // fully before or behind the valid action indices. 2913 return false; 2914 } 2915 2916 2917 const ActionVector::const_iterator aBegin( maActions.begin() ); 2918 const ActionVector::const_iterator aEnd( maActions.end() ); 2919 2920 2921 // find start and end action 2922 // ========================= 2923 o_rRangeBegin = ::std::lower_bound( aBegin, aEnd, 2924 MtfAction( ActionSharedPtr(), io_rStartIndex ), 2925 UpperBoundActionIndexComparator() ); 2926 o_rRangeEnd = ::std::lower_bound( aBegin, aEnd, 2927 MtfAction( ActionSharedPtr(), io_rEndIndex ), 2928 UpperBoundActionIndexComparator() ); 2929 return true; 2930 } 2931 2932 2933 // Public methods 2934 // ==================================================================== 2935 2936 ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas, 2937 const GDIMetaFile& rMtf, 2938 const Parameters& rParams ) : 2939 CanvasGraphicHelper( rCanvas ), 2940 maActions() 2941 { 2942 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" ); 2943 2944 OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(), 2945 "ImplRenderer::ImplRenderer(): Invalid canvas" ); 2946 OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(), 2947 "ImplRenderer::ImplRenderer(): Invalid graphic device" ); 2948 2949 // make sure canvas and graphic device are valid; action 2950 // creation don't check that every time 2951 if( rCanvas.get() == NULL || 2952 !rCanvas->getUNOCanvas().is() || 2953 !rCanvas->getUNOCanvas()->getDevice().is() ) 2954 { 2955 // leave actions empty 2956 return; 2957 } 2958 2959 VectorOfOutDevStates aStateStack; 2960 2961 VirtualDevice aVDev; 2962 aVDev.EnableOutput( sal_False ); 2963 2964 // Setup VDev for state tracking and mapping 2965 // ========================================= 2966 2967 aVDev.SetMapMode( rMtf.GetPrefMapMode() ); 2968 2969 const Size aMtfSize( rMtf.GetPrefSize() ); 2970 const Size aMtfSizePixPre( aVDev.LogicToPixel( aMtfSize, 2971 rMtf.GetPrefMapMode() ) ); 2972 const Point aEmptyPt; 2973 const Point aMtfOriginPix( aVDev.LogicToPixel( aEmptyPt ) ); 2974 2975 // #i44110# correct null-sized output - there are shapes 2976 // which have zero size in at least one dimension 2977 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ), 2978 ::std::max( aMtfSizePixPre.Height(), 1L ) ); 2979 2980 sal_Int32 nCurrActions(0); 2981 ActionFactoryParameters aParms(aStateStack, 2982 rCanvas, 2983 aVDev, 2984 rParams, 2985 nCurrActions ); 2986 2987 // init state stack 2988 clearStateStack( aStateStack ); 2989 2990 // Setup local state, such that the metafile renders 2991 // itself into a one-by-one square at the origin for 2992 // identity view and render transformations 2993 getState( aStateStack ).transform.scale( 1.0 / aMtfSizePix.Width(), 2994 1.0 / aMtfSizePix.Height() ); 2995 2996 tools::calcLogic2PixelAffineTransform( getState( aStateStack ).mapModeTransform, 2997 aVDev ); 2998 2999 ColorSharedPtr pColor( getCanvas()->createColor() ); 3000 3001 // setup default text color to black 3002 getState( aStateStack ).textColor = 3003 getState( aStateStack ).textFillColor = 3004 getState( aStateStack ).textLineColor = pColor->getDeviceColor( 0x000000FF ); 3005 3006 // apply overrides from the Parameters struct 3007 if( rParams.maFillColor.is_initialized() ) 3008 { 3009 getState( aStateStack ).isFillColorSet = true; 3010 getState( aStateStack ).fillColor = pColor->getDeviceColor( *rParams.maFillColor ); 3011 } 3012 if( rParams.maLineColor.is_initialized() ) 3013 { 3014 getState( aStateStack ).isLineColorSet = true; 3015 getState( aStateStack ).lineColor = pColor->getDeviceColor( *rParams.maLineColor ); 3016 } 3017 if( rParams.maTextColor.is_initialized() ) 3018 { 3019 getState( aStateStack ).isTextFillColorSet = true; 3020 getState( aStateStack ).isTextLineColorSet = true; 3021 getState( aStateStack ).textColor = 3022 getState( aStateStack ).textFillColor = 3023 getState( aStateStack ).textLineColor = pColor->getDeviceColor( *rParams.maTextColor ); 3024 } 3025 if( rParams.maFontName.is_initialized() || 3026 rParams.maFontWeight.is_initialized() || 3027 rParams.maFontLetterForm.is_initialized() || 3028 rParams.maFontUnderline.is_initialized() || 3029 rParams.maFontProportion.is_initialized() ) 3030 { 3031 ::cppcanvas::internal::OutDevState& rState = getState( aStateStack ); 3032 3033 rState.xFont = createFont( rState.fontRotation, 3034 ::Font(), // default font 3035 aParms ); 3036 } 3037 3038 createActions( const_cast<GDIMetaFile&>(rMtf), // HACK(Q2): 3039 // we're 3040 // changing 3041 // the 3042 // current 3043 // action 3044 // in 3045 // createActions! 3046 aParms, 3047 true // TODO(P1): make subsettability configurable 3048 ); 3049 } 3050 3051 ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas, 3052 const BitmapEx& rBmpEx, 3053 const Parameters& rParams ) : 3054 CanvasGraphicHelper( rCanvas ), 3055 maActions() 3056 { 3057 // TODO(F3): property modification parameters are 3058 // currently ignored for Bitmaps 3059 (void)rParams; 3060 3061 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(bitmap)" ); 3062 3063 OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(), 3064 "ImplRenderer::ImplRenderer(): Invalid canvas" ); 3065 OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(), 3066 "ImplRenderer::ImplRenderer(): Invalid graphic device" ); 3067 3068 // make sure canvas and graphic device are valid; action 3069 // creation don't check that every time 3070 if( rCanvas.get() == NULL || 3071 !rCanvas->getUNOCanvas().is() || 3072 !rCanvas->getUNOCanvas()->getDevice().is() ) 3073 { 3074 // leave actions empty 3075 return; 3076 } 3077 3078 OutDevState aState; 3079 3080 const Size aBmpSize( rBmpEx.GetSizePixel() ); 3081 3082 // Setup local state, such that the bitmap renders itself 3083 // into a one-by-one square for identity view and render 3084 // transformations 3085 aState.transform.scale( 1.0 / aBmpSize.Width(), 3086 1.0 / aBmpSize.Height() ); 3087 3088 // create a single action for the provided BitmapEx 3089 maActions.push_back( 3090 MtfAction( 3091 BitmapActionFactory::createBitmapAction( 3092 rBmpEx, 3093 ::basegfx::B2DPoint(), 3094 rCanvas, 3095 aState), 3096 0 ) ); 3097 } 3098 3099 ImplRenderer::~ImplRenderer() 3100 { 3101 } 3102 3103 bool ImplRenderer::drawSubset( sal_Int32 nStartIndex, 3104 sal_Int32 nEndIndex ) const 3105 { 3106 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::drawSubset()" ); 3107 3108 ActionVector::const_iterator aRangeBegin; 3109 ActionVector::const_iterator aRangeEnd; 3110 3111 try 3112 { 3113 if( !getSubsetIndices( nStartIndex, nEndIndex, 3114 aRangeBegin, aRangeEnd ) ) 3115 return true; // nothing to render (but _that_ was successful) 3116 3117 // now, aRangeBegin references the action in which the 3118 // subset rendering must start, and aRangeEnd references 3119 // the action in which the subset rendering must end (it 3120 // might also end right at the start of the referenced 3121 // action, such that zero of that action needs to be 3122 // rendered). 3123 3124 3125 // render subset of actions 3126 // ======================== 3127 3128 ::basegfx::B2DHomMatrix aMatrix; 3129 ::canvas::tools::getRenderStateTransform( aMatrix, 3130 getRenderState() ); 3131 3132 ActionRenderer aRenderer( aMatrix ); 3133 3134 return forSubsetRange( aRenderer, 3135 aRangeBegin, 3136 aRangeEnd, 3137 nStartIndex, 3138 nEndIndex, 3139 maActions.end() ); 3140 } 3141 catch( uno::Exception& ) 3142 { 3143 OSL_ENSURE( false, 3144 rtl::OUStringToOString( 3145 comphelper::anyToString( cppu::getCaughtException() ), 3146 RTL_TEXTENCODING_UTF8 ).getStr() ); 3147 3148 // convert error to return value 3149 return false; 3150 } 3151 } 3152 3153 ::basegfx::B2DRange ImplRenderer::getSubsetArea( sal_Int32 nStartIndex, 3154 sal_Int32 nEndIndex ) const 3155 { 3156 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::getSubsetArea()" ); 3157 3158 ActionVector::const_iterator aRangeBegin; 3159 ActionVector::const_iterator aRangeEnd; 3160 3161 if( !getSubsetIndices( nStartIndex, nEndIndex, 3162 aRangeBegin, aRangeEnd ) ) 3163 return ::basegfx::B2DRange(); // nothing to render -> empty range 3164 3165 // now, aRangeBegin references the action in which the 3166 // subset querying must start, and aRangeEnd references 3167 // the action in which the subset querying must end (it 3168 // might also end right at the start of the referenced 3169 // action, such that zero of that action needs to be 3170 // queried). 3171 3172 3173 // query bounds for subset of actions 3174 // ================================== 3175 3176 ::basegfx::B2DHomMatrix aMatrix; 3177 ::canvas::tools::getRenderStateTransform( aMatrix, 3178 getRenderState() ); 3179 3180 AreaQuery aQuery( aMatrix ); 3181 forSubsetRange( aQuery, 3182 aRangeBegin, 3183 aRangeEnd, 3184 nStartIndex, 3185 nEndIndex, 3186 maActions.end() ); 3187 3188 return aQuery.getBounds(); 3189 } 3190 3191 bool ImplRenderer::draw() const 3192 { 3193 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::draw()" ); 3194 3195 ::basegfx::B2DHomMatrix aMatrix; 3196 ::canvas::tools::getRenderStateTransform( aMatrix, 3197 getRenderState() ); 3198 3199 try 3200 { 3201 return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer( aMatrix ) ).result(); 3202 } 3203 catch( uno::Exception& ) 3204 { 3205 OSL_ENSURE( false, 3206 rtl::OUStringToOString( 3207 comphelper::anyToString( cppu::getCaughtException() ), 3208 RTL_TEXTENCODING_UTF8 ).getStr() ); 3209 3210 return false; 3211 } 3212 } 3213 } 3214 } 3215