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