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