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_vcl.hxx" 26 27 #include "osl/file.hxx" 28 #include "osl/process.h" 29 30 #include "vos/mutex.hxx" 31 32 #include "rtl/bootstrap.h" 33 #include "rtl/strbuf.hxx" 34 35 #include "basegfx/range/b2drectangle.hxx" 36 #include "basegfx/polygon/b2dpolygon.hxx" 37 #include "basegfx/polygon/b2dpolygontools.hxx" 38 #include "basegfx/matrix/b2dhommatrix.hxx" 39 #include "basegfx/matrix/b2dhommatrixtools.hxx" 40 41 #include "vcl/sysdata.hxx" 42 #include "vcl/svapp.hxx" 43 44 #include "aqua/salconst.h" 45 #include "aqua/salgdi.h" 46 #include "aqua/salbmp.h" 47 #include "aqua/salframe.h" 48 #include "aqua/salcolorutils.hxx" 49 #include "aqua/salatsuifontutils.hxx" 50 51 #include "fontsubset.hxx" 52 #include "impfont.hxx" 53 #include "sallayout.hxx" 54 #include "sft.hxx" 55 56 57 using namespace vcl; 58 59 //typedef unsigned char Boolean; // copied from MacTypes.h, should be properly included 60 typedef std::vector<unsigned char> ByteVector; 61 62 63 // ======================================================================= 64 65 ImplMacFontData::ImplMacFontData( const ImplDevFontAttributes& rDFA, ATSUFontID nFontId ) 66 : ImplFontData( rDFA, 0 ) 67 , mnFontId( nFontId ) 68 , mpCharMap( NULL ) 69 , mbOs2Read( false ) 70 , mbHasOs2Table( false ) 71 , mbCmapEncodingRead( false ) 72 , mbHasCJKSupport( false ) 73 {} 74 75 // ----------------------------------------------------------------------- 76 77 ImplMacFontData::~ImplMacFontData() 78 { 79 if( mpCharMap ) 80 mpCharMap->DeReference(); 81 } 82 83 // ----------------------------------------------------------------------- 84 85 sal_IntPtr ImplMacFontData::GetFontId() const 86 { 87 return (sal_IntPtr)mnFontId; 88 } 89 90 // ----------------------------------------------------------------------- 91 92 ImplFontData* ImplMacFontData::Clone() const 93 { 94 ImplMacFontData* pClone = new ImplMacFontData(*this); 95 if( mpCharMap ) 96 mpCharMap->AddReference(); 97 return pClone; 98 } 99 100 // ----------------------------------------------------------------------- 101 102 ImplFontEntry* ImplMacFontData::CreateFontInstance(ImplFontSelectData& rFSD) const 103 { 104 return new ImplFontEntry(rFSD); 105 } 106 107 // ----------------------------------------------------------------------- 108 109 inline FourCharCode GetTag(const char aTagName[5]) 110 { 111 return (aTagName[0]<<24)+(aTagName[1]<<16)+(aTagName[2]<<8)+(aTagName[3]); 112 } 113 114 static unsigned GetUShort( const unsigned char* p ){return((p[0]<<8)+p[1]);} 115 static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);} 116 117 const ImplFontCharMap* ImplMacFontData::GetImplFontCharMap() const 118 { 119 // return the cached charmap 120 if( mpCharMap ) 121 return mpCharMap; 122 123 // set the default charmap 124 mpCharMap = ImplFontCharMap::GetDefaultMap(); 125 mpCharMap->AddReference(); 126 127 // get the CMAP byte size 128 ATSFontRef rFont = FMGetATSFontRefFromFont( mnFontId ); 129 ByteCount nBufSize = 0; 130 OSStatus eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, 0, NULL, &nBufSize ); 131 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::GetImplFontCharMap : ATSFontGetTable1 failed!\n"); 132 if( eStatus != noErr ) 133 return mpCharMap; 134 135 // allocate a buffer for the CMAP raw data 136 ByteVector aBuffer( nBufSize ); 137 138 // get the CMAP raw data 139 ByteCount nRawLength = 0; 140 eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, nBufSize, (void*)&aBuffer[0], &nRawLength ); 141 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::GetImplFontCharMap : ATSFontGetTable2 failed!\n"); 142 if( eStatus != noErr ) 143 return mpCharMap; 144 DBG_ASSERT( (nBufSize==nRawLength), "ImplMacFontData::GetImplFontCharMap : ByteCount mismatch!\n"); 145 146 // parse the CMAP 147 CmapResult aCmapResult; 148 if( ParseCMAP( &aBuffer[0], nRawLength, aCmapResult ) ) 149 { 150 // create the matching charmap 151 mpCharMap->DeReference(); 152 mpCharMap = new ImplFontCharMap( aCmapResult ); 153 mpCharMap->AddReference(); 154 } 155 156 return mpCharMap; 157 } 158 159 // ----------------------------------------------------------------------- 160 161 void ImplMacFontData::ReadOs2Table( void ) const 162 { 163 // read this only once per font 164 if( mbOs2Read ) 165 return; 166 mbOs2Read = true; 167 168 // prepare to get the OS/2 table raw data 169 ATSFontRef rFont = FMGetATSFontRefFromFont( mnFontId ); 170 ByteCount nBufSize = 0; 171 OSStatus eStatus = ATSFontGetTable( rFont, GetTag("OS/2"), 0, 0, NULL, &nBufSize ); 172 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadOs2Table : ATSFontGetTable1 failed!\n"); 173 if( eStatus != noErr ) 174 return; 175 176 // allocate a buffer for the OS/2 raw data 177 ByteVector aBuffer( nBufSize ); 178 179 // get the OS/2 raw data 180 ByteCount nRawLength = 0; 181 eStatus = ATSFontGetTable( rFont, GetTag("OS/2"), 0, nBufSize, (void*)&aBuffer[0], &nRawLength ); 182 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadOs2Table : ATSFontGetTable2 failed!\n"); 183 if( eStatus != noErr ) 184 return; 185 DBG_ASSERT( (nBufSize==nRawLength), "ImplMacFontData::ReadOs2Table : ByteCount mismatch!\n"); 186 mbHasOs2Table = true; 187 188 // parse the OS/2 raw data 189 // TODO: also analyze panose info, etc. 190 191 // check if the fonts needs the "CJK extra leading" heuristic 192 const unsigned char* pOS2map = &aBuffer[0]; 193 const sal_uInt32 nVersion = GetUShort( pOS2map ); 194 if( nVersion >= 0x0001 ) 195 { 196 sal_uInt32 ulUnicodeRange2 = GetUInt( pOS2map + 46 ); 197 if( ulUnicodeRange2 & 0x2DF00000 ) 198 mbHasCJKSupport = true; 199 } 200 } 201 202 void ImplMacFontData::ReadMacCmapEncoding( void ) const 203 { 204 // read this only once per font 205 if( mbCmapEncodingRead ) 206 return; 207 mbCmapEncodingRead = true; 208 209 ATSFontRef rFont = FMGetATSFontRefFromFont( mnFontId ); 210 ByteCount nBufSize = 0; 211 OSStatus eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, 0, NULL, &nBufSize ); 212 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadMacCmapEncoding : ATSFontGetTable1 failed!\n"); 213 if( eStatus != noErr ) 214 return; 215 216 ByteVector aBuffer( nBufSize ); 217 218 ByteCount nRawLength = 0; 219 eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, nBufSize, (void*)&aBuffer[0], &nRawLength ); 220 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadMacCmapEncoding : ATSFontGetTable2 failed!\n"); 221 if( eStatus != noErr ) 222 return; 223 DBG_ASSERT( (nBufSize==nRawLength), "ImplMacFontData::ReadMacCmapEncoding : ByteCount mismatch!\n"); 224 225 const unsigned char* pCmap = &aBuffer[0]; 226 227 if (nRawLength < 24 ) 228 return; 229 if( GetUShort( pCmap ) != 0x0000 ) 230 return; 231 232 // check if the fonts needs the "CJK extra leading" heuristic 233 int nSubTables = GetUShort( pCmap + 2 ); 234 235 for( const unsigned char* p = pCmap + 4; --nSubTables >= 0; p += 8 ) 236 { 237 int nPlatform = GetUShort( p ); 238 if( nPlatform == kFontMacintoshPlatform ) { 239 int nEncoding = GetUShort (p + 2 ); 240 if( nEncoding == kFontJapaneseScript || 241 nEncoding == kFontTraditionalChineseScript || 242 nEncoding == kFontKoreanScript || 243 nEncoding == kFontSimpleChineseScript ) 244 { 245 mbHasCJKSupport = true; 246 break; 247 } 248 } 249 } 250 } 251 252 // ----------------------------------------------------------------------- 253 254 bool ImplMacFontData::HasCJKSupport( void ) const 255 { 256 ReadOs2Table(); 257 if( !mbHasOs2Table ) 258 ReadMacCmapEncoding(); 259 260 return mbHasCJKSupport; 261 } 262 263 // ======================================================================= 264 265 AquaSalGraphics::AquaSalGraphics() 266 : mpFrame( NULL ) 267 , mxLayer( NULL ) 268 , mrContext( NULL ) 269 , mpXorEmulation( NULL ) 270 , mnXorMode( 0 ) 271 , mnWidth( 0 ) 272 , mnHeight( 0 ) 273 , mnBitmapDepth( 0 ) 274 , mnRealDPIX( 0 ) 275 , mnRealDPIY( 0 ) 276 , mfFakeDPIScale( 1.0 ) 277 , mxClipPath( NULL ) 278 , maLineColor( COL_WHITE ) 279 , maFillColor( COL_BLACK ) 280 , mpMacFontData( NULL ) 281 , mnATSUIRotation( 0 ) 282 , mfFontScale( 1.0 ) 283 , mfFontStretch( 1.0 ) 284 , mbNonAntialiasedText( false ) 285 , mbPrinter( false ) 286 , mbVirDev( false ) 287 , mbWindow( false ) 288 { 289 // create the style object for font attributes 290 ATSUCreateStyle( &maATSUStyle ); 291 } 292 293 // ----------------------------------------------------------------------- 294 295 AquaSalGraphics::~AquaSalGraphics() 296 { 297 /* 298 if( mnUpdateGraphicsEvent ) 299 { 300 Application::RemoveUserEvent( mnUpdateGraphicsEvent ); 301 } 302 */ 303 CGPathRelease( mxClipPath ); 304 ATSUDisposeStyle( maATSUStyle ); 305 306 if( mpXorEmulation ) 307 delete mpXorEmulation; 308 309 if( mxLayer ) 310 CGLayerRelease( mxLayer ); 311 else if( mrContext && mbWindow ) 312 { 313 // destroy backbuffer bitmap context that we created ourself 314 CGContextRelease( mrContext ); 315 mrContext = NULL; 316 // memory is freed automatically by maOwnContextMemory 317 } 318 } 319 320 bool AquaSalGraphics::supportsOperation( OutDevSupportType eType ) const 321 { 322 bool bRet = false; 323 switch( eType ) 324 { 325 case OutDevSupport_TransparentRect: 326 case OutDevSupport_B2DClip: 327 case OutDevSupport_B2DDraw: 328 bRet = true; 329 break; 330 default: break; 331 } 332 return bRet; 333 } 334 335 // ======================================================================= 336 337 void AquaSalGraphics::updateResolution() 338 { 339 DBG_ASSERT( mbWindow, "updateResolution on inappropriate graphics" ); 340 341 initResolution( (mbWindow && mpFrame) ? mpFrame->mpWindow : nil ); 342 } 343 344 void AquaSalGraphics::initResolution( NSWindow* ) 345 { 346 // #i100617# read DPI only once; there is some kind of weird caching going on 347 // if the main screen changes 348 // FIXME: this is really unfortunate and needs to be investigated 349 350 SalData* pSalData = GetSalData(); 351 if( pSalData->mnDPIX == 0 || pSalData->mnDPIY == 0 ) 352 { 353 NSScreen* pScreen = nil; 354 355 /* #i91301# 356 many woes went into the try to have different resolutions 357 on different screens. The result of these trials is that OOo is not ready 358 for that yet, vcl and applications would need to be adapted. 359 360 Unfortunately this is not possible in the 3.0 timeframe. 361 So let's stay with one resolution for all Windows and VirtualDevices 362 which is the resolution of the main screen 363 364 This of course also means that measurements are exact only on the main screen. 365 For activating different resolutions again just comment out the two lines below. 366 367 if( pWin ) 368 pScreen = [pWin screen]; 369 */ 370 if( pScreen == nil ) 371 { 372 NSArray* pScreens = [NSScreen screens]; 373 if( pScreens ) 374 pScreen = [pScreens objectAtIndex: 0]; 375 } 376 377 mnRealDPIX = mnRealDPIY = 96; 378 if( pScreen ) 379 { 380 NSDictionary* pDev = [pScreen deviceDescription]; 381 if( pDev ) 382 { 383 NSNumber* pVal = [pDev objectForKey: @"NSScreenNumber"]; 384 if( pVal ) 385 { 386 // FIXME: casting a long to CGDirectDisplayID is evil, but 387 // Apple suggest to do it this way 388 const CGDirectDisplayID nDisplayID = (CGDirectDisplayID)[pVal longValue]; 389 const CGSize aSize = CGDisplayScreenSize( nDisplayID ); // => result is in millimeters 390 mnRealDPIX = static_cast<long>((CGDisplayPixelsWide( nDisplayID ) * 25.4) / aSize.width); 391 mnRealDPIY = static_cast<long>((CGDisplayPixelsHigh( nDisplayID ) * 25.4) / aSize.height); 392 } 393 else 394 { 395 DBG_ERROR( "no resolution found in device description" ); 396 } 397 } 398 else 399 { 400 DBG_ERROR( "no device description" ); 401 } 402 } 403 else 404 { 405 DBG_ERROR( "no screen found" ); 406 } 407 408 // #i107076# maintaining size-WYSIWYG-ness causes many problems for 409 // low-DPI, high-DPI or for mis-reporting devices 410 // => it is better to limit the calculation result then 411 static const int nMinDPI = 72; 412 if( (mnRealDPIX < nMinDPI) || (mnRealDPIY < nMinDPI) ) 413 mnRealDPIX = mnRealDPIY = nMinDPI; 414 static const int nMaxDPI = 200; 415 if( (mnRealDPIX > nMaxDPI) || (mnRealDPIY > nMaxDPI) ) 416 mnRealDPIX = mnRealDPIY = nMaxDPI; 417 418 // for OSX any anisotropy reported for the display resolution is best ignored (e.g. TripleHead2Go) 419 mnRealDPIX = mnRealDPIY = (mnRealDPIX + mnRealDPIY + 1) / 2; 420 421 pSalData->mnDPIX = mnRealDPIX; 422 pSalData->mnDPIY = mnRealDPIY; 423 } 424 else 425 { 426 mnRealDPIX = pSalData->mnDPIX; 427 mnRealDPIY = pSalData->mnDPIY; 428 } 429 430 mfFakeDPIScale = 1.0; 431 } 432 433 void AquaSalGraphics::GetResolution( long& rDPIX, long& rDPIY ) 434 { 435 if( !mnRealDPIY ) 436 initResolution( (mbWindow && mpFrame) ? mpFrame->mpWindow : nil ); 437 438 rDPIX = static_cast<long>(mfFakeDPIScale * mnRealDPIX); 439 rDPIY = static_cast<long>(mfFakeDPIScale * mnRealDPIY); 440 } 441 442 void AquaSalGraphics::copyResolution( AquaSalGraphics& rGraphics ) 443 { 444 if( !rGraphics.mnRealDPIY && rGraphics.mbWindow && rGraphics.mpFrame ) 445 rGraphics.initResolution( rGraphics.mpFrame->mpWindow ); 446 447 mnRealDPIX = rGraphics.mnRealDPIX; 448 mnRealDPIY = rGraphics.mnRealDPIY; 449 mfFakeDPIScale = rGraphics.mfFakeDPIScale; 450 } 451 452 // ----------------------------------------------------------------------- 453 454 sal_uInt16 AquaSalGraphics::GetBitCount() 455 { 456 sal_uInt16 nBits = mnBitmapDepth ? mnBitmapDepth : 32;//24; 457 return nBits; 458 } 459 460 // ----------------------------------------------------------------------- 461 462 static const basegfx::B2DPoint aHalfPointOfs ( 0.5, 0.5 ); 463 464 static void AddPolygonToPath( CGMutablePathRef xPath, 465 const ::basegfx::B2DPolygon& rPolygon, bool bClosePath, bool bPixelSnap, bool bLineDraw ) 466 { 467 // short circuit if there is nothing to do 468 const int nPointCount = rPolygon.count(); 469 if( nPointCount <= 0 ) 470 return; 471 472 (void)bPixelSnap; // TODO 473 const CGAffineTransform* pTransform = NULL; 474 475 const bool bHasCurves = rPolygon.areControlPointsUsed(); 476 for( int nPointIdx = 0, nPrevIdx = 0;; nPrevIdx = nPointIdx++ ) 477 { 478 int nClosedIdx = nPointIdx; 479 if( nPointIdx >= nPointCount ) 480 { 481 // prepare to close last curve segment if needed 482 if( bClosePath && (nPointIdx == nPointCount) ) 483 nClosedIdx = 0; 484 else 485 break; 486 } 487 488 ::basegfx::B2DPoint aPoint = rPolygon.getB2DPoint( nClosedIdx ); 489 490 if( bPixelSnap) 491 { 492 // snap device coordinates to full pixels 493 aPoint.setX( basegfx::fround( aPoint.getX() ) ); 494 aPoint.setY( basegfx::fround( aPoint.getY() ) ); 495 } 496 497 if( bLineDraw ) 498 aPoint += aHalfPointOfs; 499 500 if( !nPointIdx ) { // first point => just move there 501 CGPathMoveToPoint( xPath, pTransform, aPoint.getX(), aPoint.getY() ); 502 continue; 503 } 504 505 bool bPendingCurve = false; 506 if( bHasCurves ) 507 { 508 bPendingCurve = rPolygon.isNextControlPointUsed( nPrevIdx ); 509 bPendingCurve |= rPolygon.isPrevControlPointUsed( nClosedIdx ); 510 } 511 512 if( !bPendingCurve ) // line segment 513 CGPathAddLineToPoint( xPath, pTransform, aPoint.getX(), aPoint.getY() ); 514 else // cubic bezier segment 515 { 516 basegfx::B2DPoint aCP1 = rPolygon.getNextControlPoint( nPrevIdx ); 517 basegfx::B2DPoint aCP2 = rPolygon.getPrevControlPoint( nClosedIdx ); 518 if( bLineDraw ) 519 { 520 aCP1 += aHalfPointOfs; 521 aCP2 += aHalfPointOfs; 522 } 523 CGPathAddCurveToPoint( xPath, pTransform, aCP1.getX(), aCP1.getY(), 524 aCP2.getX(), aCP2.getY(), aPoint.getX(), aPoint.getY() ); 525 } 526 } 527 528 if( bClosePath ) 529 CGPathCloseSubpath( xPath ); 530 } 531 532 static void AddPolyPolygonToPath( CGMutablePathRef xPath, 533 const ::basegfx::B2DPolyPolygon& rPolyPoly, bool bPixelSnap, bool bLineDraw ) 534 { 535 // short circuit if there is nothing to do 536 const int nPolyCount = rPolyPoly.count(); 537 if( nPolyCount <= 0 ) 538 return; 539 540 for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx ) 541 { 542 const ::basegfx::B2DPolygon rPolygon = rPolyPoly.getB2DPolygon( nPolyIdx ); 543 AddPolygonToPath( xPath, rPolygon, true, bPixelSnap, bLineDraw ); 544 } 545 } 546 547 // ----------------------------------------------------------------------- 548 549 void AquaSalGraphics::ResetClipRegion() 550 { 551 // release old path and indicate no clipping 552 if( mxClipPath ) 553 { 554 CGPathRelease( mxClipPath ); 555 mxClipPath = NULL; 556 } 557 if( CheckContext() ) 558 SetState(); 559 } 560 561 // ----------------------------------------------------------------------- 562 563 bool AquaSalGraphics::setClipRegion( const Region& i_rClip ) 564 { 565 // release old clip path 566 if( mxClipPath ) 567 { 568 CGPathRelease( mxClipPath ); 569 mxClipPath = NULL; 570 } 571 mxClipPath = CGPathCreateMutable(); 572 573 // set current path, either as polypolgon or sequence of rectangles 574 if(i_rClip.HasPolyPolygonOrB2DPolyPolygon()) 575 { 576 const basegfx::B2DPolyPolygon aClip(i_rClip.GetAsB2DPolyPolygon()); 577 578 AddPolyPolygonToPath( mxClipPath, aClip, !getAntiAliasB2DDraw(), false ); 579 } 580 else 581 { 582 RectangleVector aRectangles; 583 i_rClip.GetRegionRectangles(aRectangles); 584 585 for(RectangleVector::const_iterator aRectIter(aRectangles.begin()); aRectIter != aRectangles.end(); aRectIter++) 586 { 587 const long nW(aRectIter->Right() - aRectIter->Left() + 1); // uses +1 logic in original 588 589 if(nW) 590 { 591 const long nH(aRectIter->Bottom() - aRectIter->Top() + 1); // uses +1 logic in original 592 593 if(nH) 594 { 595 CGRect aRect = {{ aRectIter->Left(), aRectIter->Top() }, { nW, nH }}; 596 CGPathAddRect( mxClipPath, NULL, aRect ); 597 } 598 } 599 } 600 } 601 // set the current path as clip region 602 if( CheckContext() ) 603 SetState(); 604 return true; 605 } 606 607 // ----------------------------------------------------------------------- 608 609 void AquaSalGraphics::SetLineColor() 610 { 611 maLineColor.SetAlpha( 0.0 ); // transparent 612 if( CheckContext() ) 613 CGContextSetStrokeColor( mrContext, maLineColor.AsArray() ); 614 } 615 616 // ----------------------------------------------------------------------- 617 618 void AquaSalGraphics::SetLineColor( SalColor nSalColor ) 619 { 620 maLineColor = RGBAColor( nSalColor ); 621 if( CheckContext() ) 622 CGContextSetStrokeColor( mrContext, maLineColor.AsArray() ); 623 } 624 625 // ----------------------------------------------------------------------- 626 627 void AquaSalGraphics::SetFillColor() 628 { 629 maFillColor.SetAlpha( 0.0 ); // transparent 630 if( CheckContext() ) 631 CGContextSetFillColor( mrContext, maFillColor.AsArray() ); 632 } 633 634 // ----------------------------------------------------------------------- 635 636 void AquaSalGraphics::SetFillColor( SalColor nSalColor ) 637 { 638 maFillColor = RGBAColor( nSalColor ); 639 if( CheckContext() ) 640 CGContextSetFillColor( mrContext, maFillColor.AsArray() ); 641 } 642 643 // ----------------------------------------------------------------------- 644 645 static SalColor ImplGetROPSalColor( SalROPColor nROPColor ) 646 { 647 SalColor nSalColor; 648 if ( nROPColor == SAL_ROP_0 ) 649 nSalColor = MAKE_SALCOLOR( 0, 0, 0 ); 650 else 651 nSalColor = MAKE_SALCOLOR( 255, 255, 255 ); 652 return nSalColor; 653 } 654 655 void AquaSalGraphics::SetROPLineColor( SalROPColor nROPColor ) 656 { 657 if( ! mbPrinter ) 658 SetLineColor( ImplGetROPSalColor( nROPColor ) ); 659 } 660 661 // ----------------------------------------------------------------------- 662 663 void AquaSalGraphics::SetROPFillColor( SalROPColor nROPColor ) 664 { 665 if( ! mbPrinter ) 666 SetFillColor( ImplGetROPSalColor( nROPColor ) ); 667 } 668 669 // ----------------------------------------------------------------------- 670 671 void AquaSalGraphics::ImplDrawPixel( long nX, long nY, const RGBAColor& rColor ) 672 { 673 if( !CheckContext() ) 674 return; 675 676 // overwrite the fill color 677 CGContextSetFillColor( mrContext, rColor.AsArray() ); 678 // draw 1x1 rect, there is no pixel drawing in Quartz 679 CGRect aDstRect = {{nX,nY,},{1,1}}; 680 CGContextFillRect( mrContext, aDstRect ); 681 RefreshRect( aDstRect ); 682 // reset the fill color 683 CGContextSetFillColor( mrContext, maFillColor.AsArray() ); 684 } 685 686 void AquaSalGraphics::drawPixel( long nX, long nY ) 687 { 688 // draw pixel with current line color 689 ImplDrawPixel( nX, nY, maLineColor ); 690 } 691 692 void AquaSalGraphics::drawPixel( long nX, long nY, SalColor nSalColor ) 693 { 694 const RGBAColor aPixelColor( nSalColor ); 695 ImplDrawPixel( nX, nY, aPixelColor ); 696 } 697 698 // ----------------------------------------------------------------------- 699 700 void AquaSalGraphics::drawLine( long nX1, long nY1, long nX2, long nY2 ) 701 { 702 if( nX1 == nX2 && nY1 == nY2 ) 703 { 704 // #i109453# platform independent code expects at least one pixel to be drawn 705 drawPixel( nX1, nY1 ); 706 return; 707 } 708 709 if( !CheckContext() ) 710 return; 711 712 CGContextBeginPath( mrContext ); 713 CGContextMoveToPoint( mrContext, static_cast<float>(nX1)+0.5, static_cast<float>(nY1)+0.5 ); 714 CGContextAddLineToPoint( mrContext, static_cast<float>(nX2)+0.5, static_cast<float>(nY2)+0.5 ); 715 CGContextDrawPath( mrContext, kCGPathStroke ); 716 717 Rectangle aRefreshRect( nX1, nY1, nX2, nY2 ); 718 } 719 720 // ----------------------------------------------------------------------- 721 722 void AquaSalGraphics::drawRect( long nX, long nY, long nWidth, long nHeight ) 723 { 724 if( !CheckContext() ) 725 return; 726 727 CGRect aRect( CGRectMake(nX, nY, nWidth, nHeight) ); 728 if( IsPenVisible() ) 729 { 730 aRect.origin.x += 0.5; 731 aRect.origin.y += 0.5; 732 aRect.size.width -= 1; 733 aRect.size.height -= 1; 734 } 735 736 if( IsBrushVisible() ) 737 CGContextFillRect( mrContext, aRect ); 738 739 if( IsPenVisible() ) 740 CGContextStrokeRect( mrContext, aRect ); 741 742 RefreshRect( nX, nY, nWidth, nHeight ); 743 } 744 745 // ----------------------------------------------------------------------- 746 747 static void getBoundRect( sal_uLong nPoints, const SalPoint *pPtAry, long &rX, long& rY, long& rWidth, long& rHeight ) 748 { 749 long nX1 = pPtAry->mnX; 750 long nX2 = nX1; 751 long nY1 = pPtAry->mnY; 752 long nY2 = nY1; 753 for( sal_uLong n = 1; n < nPoints; n++ ) 754 { 755 if( pPtAry[n].mnX < nX1 ) 756 nX1 = pPtAry[n].mnX; 757 else if( pPtAry[n].mnX > nX2 ) 758 nX2 = pPtAry[n].mnX; 759 760 if( pPtAry[n].mnY < nY1 ) 761 nY1 = pPtAry[n].mnY; 762 else if( pPtAry[n].mnY > nY2 ) 763 nY2 = pPtAry[n].mnY; 764 } 765 rX = nX1; 766 rY = nY1; 767 rWidth = nX2 - nX1 + 1; 768 rHeight = nY2 - nY1 + 1; 769 } 770 771 static inline void alignLinePoint( const SalPoint* i_pIn, float& o_fX, float& o_fY ) 772 { 773 o_fX = static_cast<float>(i_pIn->mnX ) + 0.5; 774 o_fY = static_cast<float>(i_pIn->mnY ) + 0.5; 775 } 776 777 void AquaSalGraphics::drawPolyLine( sal_uLong nPoints, const SalPoint *pPtAry ) 778 { 779 if( nPoints < 1 ) 780 return; 781 if( !CheckContext() ) 782 return; 783 784 long nX = 0, nY = 0, nWidth = 0, nHeight = 0; 785 getBoundRect( nPoints, pPtAry, nX, nY, nWidth, nHeight ); 786 787 float fX, fY; 788 789 CGContextBeginPath( mrContext ); 790 alignLinePoint( pPtAry, fX, fY ); 791 CGContextMoveToPoint( mrContext, fX, fY ); 792 pPtAry++; 793 for( sal_uLong nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) 794 { 795 alignLinePoint( pPtAry, fX, fY ); 796 CGContextAddLineToPoint( mrContext, fX, fY ); 797 } 798 CGContextDrawPath( mrContext, kCGPathStroke ); 799 800 RefreshRect( nX, nY, nWidth, nHeight ); 801 } 802 803 // ----------------------------------------------------------------------- 804 805 void AquaSalGraphics::drawPolygon( sal_uLong nPoints, const SalPoint *pPtAry ) 806 { 807 if( nPoints <= 1 ) 808 return; 809 if( !CheckContext() ) 810 return; 811 812 long nX = 0, nY = 0, nWidth = 0, nHeight = 0; 813 getBoundRect( nPoints, pPtAry, nX, nY, nWidth, nHeight ); 814 815 CGPathDrawingMode eMode; 816 if( IsBrushVisible() && IsPenVisible() ) 817 eMode = kCGPathEOFillStroke; 818 else if( IsPenVisible() ) 819 eMode = kCGPathStroke; 820 else if( IsBrushVisible() ) 821 eMode = kCGPathEOFill; 822 else 823 return; 824 825 CGContextBeginPath( mrContext ); 826 827 if( IsPenVisible() ) 828 { 829 float fX, fY; 830 alignLinePoint( pPtAry, fX, fY ); 831 CGContextMoveToPoint( mrContext, fX, fY ); 832 pPtAry++; 833 for( sal_uLong nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) 834 { 835 alignLinePoint( pPtAry, fX, fY ); 836 CGContextAddLineToPoint( mrContext, fX, fY ); 837 } 838 } 839 else 840 { 841 CGContextMoveToPoint( mrContext, pPtAry->mnX, pPtAry->mnY ); 842 pPtAry++; 843 for( sal_uLong nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) 844 CGContextAddLineToPoint( mrContext, pPtAry->mnX, pPtAry->mnY ); 845 } 846 847 CGContextDrawPath( mrContext, eMode ); 848 RefreshRect( nX, nY, nWidth, nHeight ); 849 } 850 851 // ----------------------------------------------------------------------- 852 853 void AquaSalGraphics::drawPolyPolygon( sal_uLong nPolyCount, const sal_uLong *pPoints, PCONSTSALPOINT *ppPtAry ) 854 { 855 if( nPolyCount <= 0 ) 856 return; 857 if( !CheckContext() ) 858 return; 859 860 // find bound rect 861 long leftX = 0, topY = 0, maxWidth = 0, maxHeight = 0; 862 getBoundRect( pPoints[0], ppPtAry[0], leftX, topY, maxWidth, maxHeight ); 863 for( sal_uLong n = 1; n < nPolyCount; n++ ) 864 { 865 long nX = leftX, nY = topY, nW = maxWidth, nH = maxHeight; 866 getBoundRect( pPoints[n], ppPtAry[n], nX, nY, nW, nH ); 867 if( nX < leftX ) 868 { 869 maxWidth += leftX - nX; 870 leftX = nX; 871 } 872 if( nY < topY ) 873 { 874 maxHeight += topY - nY; 875 topY = nY; 876 } 877 if( nX + nW > leftX + maxWidth ) 878 maxWidth = nX + nW - leftX; 879 if( nY + nH > topY + maxHeight ) 880 maxHeight = nY + nH - topY; 881 } 882 883 // prepare drawing mode 884 CGPathDrawingMode eMode; 885 if( IsBrushVisible() && IsPenVisible() ) 886 eMode = kCGPathEOFillStroke; 887 else if( IsPenVisible() ) 888 eMode = kCGPathStroke; 889 else if( IsBrushVisible() ) 890 eMode = kCGPathEOFill; 891 else 892 return; 893 894 // convert to CGPath 895 CGContextBeginPath( mrContext ); 896 if( IsPenVisible() ) 897 { 898 for( sal_uLong nPoly = 0; nPoly < nPolyCount; nPoly++ ) 899 { 900 const sal_uLong nPoints = pPoints[nPoly]; 901 if( nPoints > 1 ) 902 { 903 const SalPoint *pPtAry = ppPtAry[nPoly]; 904 float fX, fY; 905 alignLinePoint( pPtAry, fX, fY ); 906 CGContextMoveToPoint( mrContext, fX, fY ); 907 pPtAry++; 908 for( sal_uLong nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) 909 { 910 alignLinePoint( pPtAry, fX, fY ); 911 CGContextAddLineToPoint( mrContext, fX, fY ); 912 } 913 CGContextClosePath(mrContext); 914 } 915 } 916 } 917 else 918 { 919 for( sal_uLong nPoly = 0; nPoly < nPolyCount; nPoly++ ) 920 { 921 const sal_uLong nPoints = pPoints[nPoly]; 922 if( nPoints > 1 ) 923 { 924 const SalPoint *pPtAry = ppPtAry[nPoly]; 925 CGContextMoveToPoint( mrContext, pPtAry->mnX, pPtAry->mnY ); 926 pPtAry++; 927 for( sal_uLong nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ ) 928 CGContextAddLineToPoint( mrContext, pPtAry->mnX, pPtAry->mnY ); 929 CGContextClosePath(mrContext); 930 } 931 } 932 } 933 934 CGContextDrawPath( mrContext, eMode ); 935 936 RefreshRect( leftX, topY, maxWidth, maxHeight ); 937 } 938 939 // ----------------------------------------------------------------------- 940 941 bool AquaSalGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly, 942 double fTransparency ) 943 { 944 // short circuit if there is nothing to do 945 const int nPolyCount = rPolyPoly.count(); 946 if( nPolyCount <= 0 ) 947 return true; 948 949 // ignore invisible polygons 950 if( (fTransparency >= 1.0) || (fTransparency < 0) ) 951 return true; 952 953 // setup poly-polygon path 954 CGMutablePathRef xPath = CGPathCreateMutable(); 955 for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx ) 956 { 957 const ::basegfx::B2DPolygon rPolygon = rPolyPoly.getB2DPolygon( nPolyIdx ); 958 AddPolygonToPath( xPath, rPolygon, true, !getAntiAliasB2DDraw(), IsPenVisible() ); 959 } 960 961 const CGRect aRefreshRect = CGPathGetBoundingBox( xPath ); 962 #ifndef NO_I97317_WORKAROUND 963 // #i97317# workaround for Quartz having problems with drawing small polygons 964 if( ! ((aRefreshRect.size.width <= 0.125) && (aRefreshRect.size.height <= 0.125)) ) 965 #endif 966 { 967 // use the path to prepare the graphics context 968 CGContextSaveGState( mrContext ); 969 CGContextBeginPath( mrContext ); 970 CGContextAddPath( mrContext, xPath ); 971 972 // draw path with antialiased polygon 973 CGContextSetShouldAntialias( mrContext, true ); 974 CGContextSetAlpha( mrContext, 1.0 - fTransparency ); 975 CGContextDrawPath( mrContext, kCGPathEOFillStroke ); 976 CGContextRestoreGState( mrContext ); 977 978 // mark modified rectangle as updated 979 RefreshRect( aRefreshRect ); 980 } 981 982 CGPathRelease( xPath ); 983 984 return true; 985 } 986 987 // ----------------------------------------------------------------------- 988 989 bool AquaSalGraphics::drawPolyLine( 990 const ::basegfx::B2DPolygon& rPolyLine, 991 double fTransparency, 992 const ::basegfx::B2DVector& rLineWidths, 993 basegfx::B2DLineJoin eLineJoin, 994 com::sun::star::drawing::LineCap eLineCap) 995 { 996 // short circuit if there is nothing to do 997 const int nPointCount = rPolyLine.count(); 998 if( nPointCount <= 0 ) 999 return true; 1000 1001 // reject requests that cannot be handled yet 1002 if( rLineWidths.getX() != rLineWidths.getY() ) 1003 return false; 1004 1005 // #i101491# Aqua does not support B2DLINEJOIN_NONE; return false to use 1006 // the fallback (own geometry preparation) 1007 // #i104886# linejoin-mode and thus the above only applies to "fat" lines 1008 if( (basegfx::B2DLINEJOIN_NONE == eLineJoin) 1009 && (rLineWidths.getX() > 1.3) ) 1010 return false; 1011 1012 // setup line attributes 1013 CGLineJoin aCGLineJoin = kCGLineJoinMiter; 1014 switch( eLineJoin ) { 1015 case ::basegfx::B2DLINEJOIN_NONE: aCGLineJoin = /*TODO?*/kCGLineJoinMiter; break; 1016 case ::basegfx::B2DLINEJOIN_MIDDLE: aCGLineJoin = /*TODO?*/kCGLineJoinMiter; break; 1017 case ::basegfx::B2DLINEJOIN_BEVEL: aCGLineJoin = kCGLineJoinBevel; break; 1018 case ::basegfx::B2DLINEJOIN_MITER: aCGLineJoin = kCGLineJoinMiter; break; 1019 case ::basegfx::B2DLINEJOIN_ROUND: aCGLineJoin = kCGLineJoinRound; break; 1020 } 1021 1022 // setup cap attribute 1023 CGLineCap aCGLineCap(kCGLineCapButt); 1024 1025 switch(eLineCap) 1026 { 1027 default: // com::sun::star::drawing::LineCap_BUTT: 1028 { 1029 aCGLineCap = kCGLineCapButt; 1030 break; 1031 } 1032 case com::sun::star::drawing::LineCap_ROUND: 1033 { 1034 aCGLineCap = kCGLineCapRound; 1035 break; 1036 } 1037 case com::sun::star::drawing::LineCap_SQUARE: 1038 { 1039 aCGLineCap = kCGLineCapSquare; 1040 break; 1041 } 1042 } 1043 1044 // setup poly-polygon path 1045 CGMutablePathRef xPath = CGPathCreateMutable(); 1046 AddPolygonToPath( xPath, rPolyLine, rPolyLine.isClosed(), !getAntiAliasB2DDraw(), true ); 1047 1048 const CGRect aRefreshRect = CGPathGetBoundingBox( xPath ); 1049 #ifndef NO_I97317_WORKAROUND 1050 // #i97317# workaround for Quartz having problems with drawing small polygons 1051 if( ! ((aRefreshRect.size.width <= 0.125) && (aRefreshRect.size.height <= 0.125)) ) 1052 #endif 1053 { 1054 // use the path to prepare the graphics context 1055 CGContextSaveGState( mrContext ); 1056 CGContextAddPath( mrContext, xPath ); 1057 // draw path with antialiased line 1058 CGContextSetShouldAntialias( mrContext, true ); 1059 CGContextSetAlpha( mrContext, 1.0 - fTransparency ); 1060 CGContextSetLineJoin( mrContext, aCGLineJoin ); 1061 CGContextSetLineCap( mrContext, aCGLineCap ); 1062 CGContextSetLineWidth( mrContext, rLineWidths.getX() ); 1063 CGContextDrawPath( mrContext, kCGPathStroke ); 1064 CGContextRestoreGState( mrContext ); 1065 1066 // mark modified rectangle as updated 1067 RefreshRect( aRefreshRect ); 1068 } 1069 1070 CGPathRelease( xPath ); 1071 1072 return true; 1073 } 1074 1075 // ----------------------------------------------------------------------- 1076 1077 sal_Bool AquaSalGraphics::drawPolyLineBezier( sal_uLong, const SalPoint*, const sal_uInt8* ) 1078 { 1079 return sal_False; 1080 } 1081 1082 // ----------------------------------------------------------------------- 1083 1084 sal_Bool AquaSalGraphics::drawPolygonBezier( sal_uLong, const SalPoint*, const sal_uInt8* ) 1085 { 1086 return sal_False; 1087 } 1088 1089 // ----------------------------------------------------------------------- 1090 1091 sal_Bool AquaSalGraphics::drawPolyPolygonBezier( sal_uLong, const sal_uLong*, 1092 const SalPoint* const*, const sal_uInt8* const* ) 1093 { 1094 return sal_False; 1095 } 1096 1097 // ----------------------------------------------------------------------- 1098 1099 void AquaSalGraphics::copyBits( const SalTwoRect& rPosAry, SalGraphics *pSrcGraphics ) 1100 { 1101 if( !pSrcGraphics ) 1102 pSrcGraphics = this; 1103 1104 //from unix salgdi2.cxx 1105 //[FIXME] find a better way to prevent calc from crashing when width and height are negative 1106 if( rPosAry.mnSrcWidth <= 0 1107 || rPosAry.mnSrcHeight <= 0 1108 || rPosAry.mnDestWidth <= 0 1109 || rPosAry.mnDestHeight <= 0 ) 1110 { 1111 return; 1112 } 1113 1114 // accelerate trivial operations 1115 /*const*/ AquaSalGraphics* pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics); 1116 const bool bSameGraphics = (this == pSrc) || (mbWindow && mpFrame && pSrc->mbWindow && (mpFrame == pSrc->mpFrame)); 1117 if( bSameGraphics 1118 && (rPosAry.mnSrcWidth == rPosAry.mnDestWidth) 1119 && (rPosAry.mnSrcHeight == rPosAry.mnDestHeight)) 1120 { 1121 // short circuit if there is nothing to do 1122 if( (rPosAry.mnSrcX == rPosAry.mnDestX) 1123 && (rPosAry.mnSrcY == rPosAry.mnDestY)) 1124 return; 1125 // use copyArea() if source and destination context are identical 1126 copyArea( rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnSrcX, rPosAry.mnSrcY, 1127 rPosAry.mnSrcWidth, rPosAry.mnSrcHeight, 0 ); 1128 return; 1129 } 1130 1131 ApplyXorContext(); 1132 pSrc->ApplyXorContext(); 1133 1134 DBG_ASSERT( pSrc->mxLayer!=NULL, "AquaSalGraphics::copyBits() from non-layered graphics" ); 1135 1136 const CGPoint aDstPoint = { +rPosAry.mnDestX - rPosAry.mnSrcX, rPosAry.mnDestY - rPosAry.mnSrcY }; 1137 if( (rPosAry.mnSrcWidth == rPosAry.mnDestWidth && rPosAry.mnSrcHeight == rPosAry.mnDestHeight) && 1138 (!mnBitmapDepth || (aDstPoint.x + pSrc->mnWidth) <= mnWidth) ) // workaround a Quartz crasher 1139 { 1140 // in XOR mode the drawing context is redirected to the XOR mask 1141 // if source and target are identical then copyBits() paints onto the target context though 1142 CGContextRef xCopyContext = mrContext; 1143 if( mpXorEmulation && mpXorEmulation->IsEnabled() ) 1144 if( pSrcGraphics == this ) 1145 xCopyContext = mpXorEmulation->GetTargetContext(); 1146 1147 CGContextSaveGState( xCopyContext ); 1148 const CGRect aDstRect = { {rPosAry.mnDestX, rPosAry.mnDestY}, {rPosAry.mnDestWidth, rPosAry.mnDestHeight} }; 1149 CGContextClipToRect( xCopyContext, aDstRect ); 1150 1151 // draw at new destination 1152 // NOTE: flipped drawing gets disabled for this, else the subimage would be drawn upside down 1153 if( pSrc->IsFlipped() ) 1154 { CGContextTranslateCTM( xCopyContext, 0, +mnHeight ); CGContextScaleCTM( xCopyContext, +1, -1 ); } 1155 // TODO: pSrc->size() != this->size() 1156 ::CGContextDrawLayerAtPoint( xCopyContext, aDstPoint, pSrc->mxLayer ); 1157 CGContextRestoreGState( xCopyContext ); 1158 // mark the destination rectangle as updated 1159 RefreshRect( aDstRect ); 1160 } 1161 else 1162 { 1163 SalBitmap* pBitmap = pSrc->getBitmap( rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight ); 1164 1165 if( pBitmap ) 1166 { 1167 SalTwoRect aPosAry( rPosAry ); 1168 aPosAry.mnSrcX = 0; 1169 aPosAry.mnSrcY = 0; 1170 drawBitmap( aPosAry, *pBitmap ); 1171 delete pBitmap; 1172 } 1173 } 1174 } 1175 1176 // ----------------------------------------------------------------------- 1177 1178 void AquaSalGraphics::copyArea( long nDstX, long nDstY,long nSrcX, long nSrcY, long nSrcWidth, long nSrcHeight, sal_uInt16 /*nFlags*/ ) 1179 { 1180 ApplyXorContext(); 1181 1182 #if 0 // TODO: make AquaSalBitmap as fast as the alternative implementation below 1183 SalBitmap* pBitmap = getBitmap( nSrcX, nSrcY, nSrcWidth, nSrcHeight ); 1184 if( pBitmap ) 1185 { 1186 SalTwoRect aPosAry; 1187 aPosAry.mnSrcX = 0; 1188 aPosAry.mnSrcY = 0; 1189 aPosAry.mnSrcWidth = nSrcWidth; 1190 aPosAry.mnSrcHeight = nSrcHeight; 1191 aPosAry.mnDestX = nDstX; 1192 aPosAry.mnDestY = nDstY; 1193 aPosAry.mnDestWidth = nSrcWidth; 1194 aPosAry.mnDestHeight = nSrcHeight; 1195 drawBitmap( aPosAry, *pBitmap ); 1196 delete pBitmap; 1197 } 1198 #else 1199 DBG_ASSERT( mxLayer!=NULL, "AquaSalGraphics::copyArea() for non-layered graphics" ); 1200 1201 // in XOR mode the drawing context is redirected to the XOR mask 1202 // copyArea() always works on the target context though 1203 CGContextRef xCopyContext = mrContext; 1204 if( mpXorEmulation && mpXorEmulation->IsEnabled() ) 1205 xCopyContext = mpXorEmulation->GetTargetContext(); 1206 1207 // drawing a layer onto its own context causes trouble on OSX => copy it first 1208 // TODO: is it possible to get rid of this unneeded copy more often? 1209 // e.g. on OSX>=10.5 only this situation causes problems: 1210 // mnBitmapDepth && (aDstPoint.x + pSrc->mnWidth) > mnWidth 1211 CGLayerRef xSrcLayer = mxLayer; 1212 // TODO: if( mnBitmapDepth > 0 ) 1213 { 1214 const CGSize aSrcSize = { nSrcWidth, nSrcHeight }; 1215 xSrcLayer = ::CGLayerCreateWithContext( xCopyContext, aSrcSize, NULL ); 1216 const CGContextRef xSrcContext = CGLayerGetContext( xSrcLayer ); 1217 CGPoint aSrcPoint = { -nSrcX, -nSrcY }; 1218 if( IsFlipped() ) 1219 { 1220 ::CGContextTranslateCTM( xSrcContext, 0, +nSrcHeight ); 1221 ::CGContextScaleCTM( xSrcContext, +1, -1 ); 1222 aSrcPoint.y = (nSrcY + nSrcHeight) - mnHeight; 1223 } 1224 ::CGContextDrawLayerAtPoint( xSrcContext, aSrcPoint, mxLayer ); 1225 } 1226 1227 // draw at new destination 1228 const CGPoint aDstPoint = { +nDstX, +nDstY }; 1229 ::CGContextDrawLayerAtPoint( xCopyContext, aDstPoint, xSrcLayer ); 1230 1231 // cleanup 1232 if( xSrcLayer != mxLayer ) 1233 CGLayerRelease( xSrcLayer ); 1234 1235 // mark the destination rectangle as updated 1236 RefreshRect( nDstX, nDstY, nSrcWidth, nSrcHeight ); 1237 #endif 1238 } 1239 1240 // ----------------------------------------------------------------------- 1241 1242 void AquaSalGraphics::drawBitmap( const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap ) 1243 { 1244 if( !CheckContext() ) 1245 return; 1246 1247 const AquaSalBitmap& rBitmap = static_cast<const AquaSalBitmap&>(rSalBitmap); 1248 CGImageRef xImage = rBitmap.CreateCroppedImage( (int)rPosAry.mnSrcX, (int)rPosAry.mnSrcY, (int)rPosAry.mnSrcWidth, (int)rPosAry.mnSrcHeight ); 1249 if( !xImage ) 1250 return; 1251 1252 const CGRect aDstRect = {{rPosAry.mnDestX, rPosAry.mnDestY}, {rPosAry.mnDestWidth, rPosAry.mnDestHeight}}; 1253 CGContextDrawImage( mrContext, aDstRect, xImage ); 1254 CGImageRelease( xImage ); 1255 RefreshRect( aDstRect ); 1256 } 1257 1258 // ----------------------------------------------------------------------- 1259 1260 void AquaSalGraphics::drawBitmap( const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap,SalColor ) 1261 { 1262 DBG_ERROR("not implemented for color masking!"); 1263 drawBitmap( rPosAry, rSalBitmap ); 1264 } 1265 1266 // ----------------------------------------------------------------------- 1267 1268 void AquaSalGraphics::drawBitmap( const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap, const SalBitmap& rTransparentBitmap ) 1269 { 1270 if( !CheckContext() ) 1271 return; 1272 1273 const AquaSalBitmap& rBitmap = static_cast<const AquaSalBitmap&>(rSalBitmap); 1274 const AquaSalBitmap& rMask = static_cast<const AquaSalBitmap&>(rTransparentBitmap); 1275 CGImageRef xMaskedImage( rBitmap.CreateWithMask( rMask, rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight ) ); 1276 if( !xMaskedImage ) 1277 return; 1278 1279 const CGRect aDstRect = {{rPosAry.mnDestX, rPosAry.mnDestY}, {rPosAry.mnDestWidth, rPosAry.mnDestHeight}}; 1280 CGContextDrawImage( mrContext, aDstRect, xMaskedImage ); 1281 CGImageRelease( xMaskedImage ); 1282 RefreshRect( aDstRect ); 1283 } 1284 1285 // ----------------------------------------------------------------------- 1286 1287 void AquaSalGraphics::drawMask( const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap, SalColor nMaskColor ) 1288 { 1289 if( !CheckContext() ) 1290 return; 1291 1292 const AquaSalBitmap& rBitmap = static_cast<const AquaSalBitmap&>(rSalBitmap); 1293 CGImageRef xImage = rBitmap.CreateColorMask( rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight, nMaskColor ); 1294 if( !xImage ) 1295 return; 1296 1297 const CGRect aDstRect = {{rPosAry.mnDestX, rPosAry.mnDestY}, {rPosAry.mnDestWidth, rPosAry.mnDestHeight}}; 1298 CGContextDrawImage( mrContext, aDstRect, xImage ); 1299 CGImageRelease( xImage ); 1300 RefreshRect( aDstRect ); 1301 } 1302 1303 // ----------------------------------------------------------------------- 1304 1305 SalBitmap* AquaSalGraphics::getBitmap( long nX, long nY, long nDX, long nDY ) 1306 { 1307 DBG_ASSERT( mxLayer, "AquaSalGraphics::getBitmap() with no layer" ); 1308 1309 ApplyXorContext(); 1310 1311 AquaSalBitmap* pBitmap = new AquaSalBitmap; 1312 if( !pBitmap->Create( mxLayer, mnBitmapDepth, nX, nY, nDX, nDY, !mbWindow ) ) 1313 { 1314 delete pBitmap; 1315 pBitmap = NULL; 1316 } 1317 1318 return pBitmap; 1319 } 1320 1321 // ----------------------------------------------------------------------- 1322 1323 SalColor AquaSalGraphics::getPixel( long nX, long nY ) 1324 { 1325 // return default value on printers or when out of bounds 1326 if( !mxLayer 1327 || (nX < 0) || (nX >= mnWidth) 1328 || (nY < 0) || (nY >= mnHeight)) 1329 return COL_BLACK; 1330 1331 // prepare creation of matching a CGBitmapContext 1332 CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace; 1333 CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big; 1334 #if __BIG_ENDIAN__ 1335 struct{ unsigned char b, g, r, a; } aPixel; 1336 #else 1337 struct{ unsigned char a, r, g, b; } aPixel; 1338 #endif 1339 1340 // create a one-pixel bitmap context 1341 // TODO: is it worth to cache it? 1342 CGContextRef xOnePixelContext = ::CGBitmapContextCreate( &aPixel, 1343 1, 1, 8, sizeof(aPixel), aCGColorSpace, aCGBmpInfo ); 1344 1345 // update this graphics layer 1346 ApplyXorContext(); 1347 1348 // copy the requested pixel into the bitmap context 1349 if( IsFlipped() ) 1350 nY = mnHeight - nY; 1351 const CGPoint aCGPoint = {-nX, -nY}; 1352 CGContextDrawLayerAtPoint( xOnePixelContext, aCGPoint, mxLayer ); 1353 CGContextRelease( xOnePixelContext ); 1354 1355 SalColor nSalColor = MAKE_SALCOLOR( aPixel.r, aPixel.g, aPixel.b ); 1356 return nSalColor; 1357 } 1358 1359 // ----------------------------------------------------------------------- 1360 1361 1362 static void DrawPattern50( void*, CGContextRef rContext ) 1363 { 1364 static const CGRect aRects[2] = { { {0,0}, { 2, 2 } }, { { 2, 2 }, { 2, 2 } } }; 1365 CGContextAddRects( rContext, aRects, 2 ); 1366 CGContextFillPath( rContext ); 1367 } 1368 1369 void AquaSalGraphics::Pattern50Fill() 1370 { 1371 static const float aFillCol[4] = { 1,1,1,1 }; 1372 static const CGPatternCallbacks aCallback = { 0, &DrawPattern50, NULL }; 1373 if( ! GetSalData()->mxP50Space ) 1374 GetSalData()->mxP50Space = CGColorSpaceCreatePattern( GetSalData()->mxRGBSpace ); 1375 if( ! GetSalData()->mxP50Pattern ) 1376 GetSalData()->mxP50Pattern = CGPatternCreate( NULL, CGRectMake( 0, 0, 4, 4 ), 1377 CGAffineTransformIdentity, 4, 4, 1378 kCGPatternTilingConstantSpacing, 1379 false, &aCallback ); 1380 1381 CGContextSetFillColorSpace( mrContext, GetSalData()->mxP50Space ); 1382 CGContextSetFillPattern( mrContext, GetSalData()->mxP50Pattern, aFillCol ); 1383 CGContextFillPath( mrContext ); 1384 } 1385 1386 void AquaSalGraphics::invert( long nX, long nY, long nWidth, long nHeight, SalInvert nFlags ) 1387 { 1388 if ( CheckContext() ) 1389 { 1390 CGRect aCGRect = CGRectMake( nX, nY, nWidth, nHeight); 1391 CGContextSaveGState(mrContext); 1392 1393 if ( nFlags & SAL_INVERT_TRACKFRAME ) 1394 { 1395 const float dashLengths[2] = { 4.0, 4.0 }; // for drawing dashed line 1396 CGContextSetBlendMode( mrContext, kCGBlendModeDifference ); 1397 CGContextSetRGBStrokeColor ( mrContext, 1.0, 1.0, 1.0, 1.0 ); 1398 CGContextSetLineDash ( mrContext, 0, dashLengths, 2 ); 1399 CGContextSetLineWidth( mrContext, 2.0); 1400 CGContextStrokeRect ( mrContext, aCGRect ); 1401 } 1402 else if ( nFlags & SAL_INVERT_50 ) 1403 { 1404 //CGContextSetAllowsAntialiasing( mrContext, false ); 1405 CGContextSetBlendMode(mrContext, kCGBlendModeDifference); 1406 CGContextAddRect( mrContext, aCGRect ); 1407 Pattern50Fill(); 1408 } 1409 else // just invert 1410 { 1411 CGContextSetBlendMode(mrContext, kCGBlendModeDifference); 1412 CGContextSetRGBFillColor ( mrContext,1.0, 1.0, 1.0 , 1.0 ); 1413 CGContextFillRect ( mrContext, aCGRect ); 1414 } 1415 CGContextRestoreGState( mrContext); 1416 RefreshRect( aCGRect ); 1417 } 1418 } 1419 1420 // ----------------------------------------------------------------------- 1421 1422 void AquaSalGraphics::invert( sal_uLong nPoints, const SalPoint* pPtAry, SalInvert nSalFlags ) 1423 { 1424 CGPoint* CGpoints ; 1425 if ( CheckContext() ) 1426 { 1427 CGContextSaveGState(mrContext); 1428 CGpoints = makeCGptArray(nPoints,pPtAry); 1429 CGContextAddLines ( mrContext, CGpoints, nPoints ); 1430 if ( nSalFlags & SAL_INVERT_TRACKFRAME ) 1431 { 1432 const float dashLengths[2] = { 4.0, 4.0 }; // for drawing dashed line 1433 CGContextSetBlendMode( mrContext, kCGBlendModeDifference ); 1434 CGContextSetRGBStrokeColor ( mrContext, 1.0, 1.0, 1.0, 1.0 ); 1435 CGContextSetLineDash ( mrContext, 0, dashLengths, 2 ); 1436 CGContextSetLineWidth( mrContext, 2.0); 1437 CGContextStrokePath ( mrContext ); 1438 } 1439 else if ( nSalFlags & SAL_INVERT_50 ) 1440 { 1441 CGContextSetBlendMode(mrContext, kCGBlendModeDifference); 1442 Pattern50Fill(); 1443 } 1444 else // just invert 1445 { 1446 CGContextSetBlendMode( mrContext, kCGBlendModeDifference ); 1447 CGContextSetRGBFillColor( mrContext, 1.0, 1.0, 1.0, 1.0 ); 1448 CGContextFillPath( mrContext ); 1449 } 1450 const CGRect aRefreshRect = CGContextGetClipBoundingBox(mrContext); 1451 CGContextRestoreGState( mrContext); 1452 delete [] CGpoints; 1453 RefreshRect( aRefreshRect ); 1454 } 1455 } 1456 1457 // ----------------------------------------------------------------------- 1458 1459 sal_Bool AquaSalGraphics::drawEPS( long nX, long nY, long nWidth, long nHeight, 1460 void* pEpsData, sal_uLong nByteCount ) 1461 { 1462 // convert the raw data to an NSImageRef 1463 NSData* xNSData = [NSData dataWithBytes:(void*)pEpsData length:(int)nByteCount]; 1464 NSImageRep* xEpsImage = [NSEPSImageRep imageRepWithData: xNSData]; 1465 if( !xEpsImage ) 1466 return false; 1467 1468 // get the target context 1469 if( !CheckContext() ) 1470 return false; 1471 1472 // NOTE: flip drawing, else the nsimage would be drawn upside down 1473 CGContextSaveGState( mrContext ); 1474 // CGContextTranslateCTM( mrContext, 0, +mnHeight ); 1475 CGContextScaleCTM( mrContext, +1, -1 ); 1476 nY = /*mnHeight*/ - (nY + nHeight); 1477 1478 // prepare the target context 1479 NSGraphicsContext* pOrigNSCtx = [NSGraphicsContext currentContext]; 1480 [pOrigNSCtx retain]; 1481 1482 // create new context 1483 NSGraphicsContext* pDrawNSCtx = [NSGraphicsContext graphicsContextWithGraphicsPort: mrContext flipped: IsFlipped()]; 1484 // set it, setCurrentContext also releases the prviously set one 1485 [NSGraphicsContext setCurrentContext: pDrawNSCtx]; 1486 1487 // draw the EPS 1488 const NSRect aDstRect = {{nX,nY},{nWidth,nHeight}}; 1489 const BOOL bOK = [xEpsImage drawInRect: aDstRect]; 1490 1491 // restore the NSGraphicsContext 1492 [NSGraphicsContext setCurrentContext: pOrigNSCtx]; 1493 [pOrigNSCtx release]; // restore the original retain count 1494 1495 CGContextRestoreGState( mrContext ); 1496 // mark the destination rectangle as updated 1497 RefreshRect( aDstRect ); 1498 1499 return bOK; 1500 } 1501 1502 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 1503 bool AquaSalGraphics::drawAlphaBitmap( const SalTwoRect& rTR, 1504 const SalBitmap& rSrcBitmap, const SalBitmap& rAlphaBmp ) 1505 { 1506 // An image mask can't have a depth > 8 bits (should be 1 to 8 bits) 1507 if( rAlphaBmp.GetBitCount() > 8 ) 1508 return false; 1509 1510 // are these two tests really necessary? (see vcl/unx/source/gdi/salgdi2.cxx) 1511 // horizontal/vertical mirroring not implemented yet 1512 if( rTR.mnDestWidth < 0 || rTR.mnDestHeight < 0 ) 1513 return false; 1514 1515 const AquaSalBitmap& rSrcSalBmp = static_cast<const AquaSalBitmap&>(rSrcBitmap); 1516 const AquaSalBitmap& rMaskSalBmp = static_cast<const AquaSalBitmap&>(rAlphaBmp); 1517 1518 CGImageRef xMaskedImage = rSrcSalBmp.CreateWithMask( rMaskSalBmp, rTR.mnSrcX, rTR.mnSrcY, rTR.mnSrcWidth, rTR.mnSrcHeight ); 1519 if( !xMaskedImage ) 1520 return false; 1521 1522 if ( CheckContext() ) 1523 { 1524 const CGRect aDstRect = {{rTR.mnDestX, rTR.mnDestY}, {rTR.mnDestWidth, rTR.mnDestHeight}}; 1525 CGContextDrawImage( mrContext, aDstRect, xMaskedImage ); 1526 RefreshRect( aDstRect ); 1527 } 1528 1529 CGImageRelease(xMaskedImage); 1530 return true; 1531 } 1532 1533 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 1534 bool AquaSalGraphics::drawTransformedBitmap( 1535 const basegfx::B2DPoint& rNull, 1536 const basegfx::B2DPoint& rX, 1537 const basegfx::B2DPoint& rY, 1538 const SalBitmap& rSourceBitmap, 1539 const SalBitmap* pAlphaBitmap) 1540 { 1541 // here direct support for transformed bitmaps can be impemented 1542 (void)rNull; (void)rX; (void)rY; (void)rSourceBitmap; (void)pAlphaBitmap; 1543 return false; 1544 } 1545 1546 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 1547 bool AquaSalGraphics::drawAlphaRect( long nX, long nY, long nWidth, 1548 long nHeight, sal_uInt8 nTransparency ) 1549 { 1550 if( !CheckContext() ) 1551 return true; 1552 1553 // save the current state 1554 CGContextSaveGState( mrContext ); 1555 CGContextSetAlpha( mrContext, (100-nTransparency) * (1.0/100) ); 1556 1557 CGRect aRect = {{nX,nY},{nWidth-1,nHeight-1}}; 1558 if( IsPenVisible() ) 1559 { 1560 aRect.origin.x += 0.5; 1561 aRect.origin.y += 0.5; 1562 } 1563 1564 CGContextBeginPath( mrContext ); 1565 CGContextAddRect( mrContext, aRect ); 1566 CGContextDrawPath( mrContext, kCGPathFill ); 1567 1568 // restore state 1569 CGContextRestoreGState(mrContext); 1570 RefreshRect( aRect ); 1571 return true; 1572 } 1573 1574 // ----------------------------------------------------------------------- 1575 1576 void AquaSalGraphics::SetTextColor( SalColor nSalColor ) 1577 { 1578 RGBColor color; 1579 color.red = (unsigned short) ( SALCOLOR_RED(nSalColor) * 65535.0 / 255.0 ); 1580 color.green = (unsigned short) ( SALCOLOR_GREEN(nSalColor) * 65535.0 / 255.0 ); 1581 color.blue = (unsigned short) ( SALCOLOR_BLUE(nSalColor) * 65535.0 / 255.0 ); 1582 1583 ATSUAttributeTag aTag = kATSUColorTag; 1584 ByteCount aValueSize = sizeof( color ); 1585 ATSUAttributeValuePtr aValue = &color; 1586 1587 OSStatus err = ATSUSetAttributes( maATSUStyle, 1, &aTag, &aValueSize, &aValue ); 1588 DBG_ASSERT( (err==noErr), "AquaSalGraphics::SetTextColor() : Could not set font attributes!\n"); 1589 if( err != noErr ) 1590 return; 1591 } 1592 1593 // ----------------------------------------------------------------------- 1594 1595 void AquaSalGraphics::GetFontMetric( ImplFontMetricData* pMetric, int nFallbackLevel ) 1596 { 1597 (void)nFallbackLevel; // glyph-fallback on ATSU is done differently -> no fallback level 1598 1599 // get the ATSU font metrics (in point units) 1600 // of the font that has eventually been size-limited 1601 1602 ATSUFontID fontId; 1603 OSStatus err = ATSUGetAttribute( maATSUStyle, kATSUFontTag, sizeof(ATSUFontID), &fontId, 0 ); 1604 DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font id\n"); 1605 1606 ATSFontMetrics aMetrics; 1607 ATSFontRef rFont = FMGetATSFontRefFromFont( fontId ); 1608 err = ATSFontGetHorizontalMetrics ( rFont, kATSOptionFlagsDefault, &aMetrics ); 1609 DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font metrics\n"); 1610 if( err != noErr ) 1611 return; 1612 1613 // all ATS fonts are scalable fonts 1614 pMetric->mbScalableFont = true; 1615 // TODO: check if any kerning is possible 1616 pMetric->mbKernableFont = true; 1617 1618 // convert into VCL font metrics (in unscaled pixel units) 1619 1620 Fixed ptSize; 1621 err = ATSUGetAttribute( maATSUStyle, kATSUSizeTag, sizeof(Fixed), &ptSize, 0); 1622 DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font size\n"); 1623 const double fPointSize = Fix2X( ptSize ); 1624 1625 // convert quartz units to pixel units 1626 // please see the comment in AquaSalGraphics::SetFont() for details 1627 const double fPixelSize = (mfFontScale * mfFakeDPIScale * fPointSize); 1628 pMetric->mnAscent = static_cast<long>(+aMetrics.ascent * fPixelSize + 0.5); 1629 pMetric->mnDescent = static_cast<long>(-aMetrics.descent * fPixelSize + 0.5); 1630 const long nExtDescent = static_cast<long>((-aMetrics.descent + aMetrics.leading) * fPixelSize + 0.5); 1631 pMetric->mnExtLeading = nExtDescent - pMetric->mnDescent; 1632 pMetric->mnIntLeading = 0; 1633 // ATSFontMetrics.avgAdvanceWidth is obsolete, so it is usually set to zero 1634 // since ImplFontMetricData::mnWidth is only used for stretching/squeezing fonts 1635 // setting this width to the pixel height of the fontsize is good enough 1636 // it also makes the calculation of the stretch factor simple 1637 pMetric->mnWidth = static_cast<long>(mfFontStretch * fPixelSize + 0.5); 1638 } 1639 1640 // ----------------------------------------------------------------------- 1641 1642 sal_uLong AquaSalGraphics::GetKernPairs( sal_uLong, ImplKernPairData* ) 1643 { 1644 return 0; 1645 } 1646 1647 // ----------------------------------------------------------------------- 1648 1649 static bool AddTempFontDir( const char* pDir ) 1650 { 1651 FSRef aPathFSRef; 1652 Boolean bIsDirectory = true; 1653 OSStatus eStatus = FSPathMakeRef( reinterpret_cast<const UInt8*>(pDir), &aPathFSRef, &bIsDirectory ); 1654 DBG_ASSERTWARNING( (eStatus==noErr) && bIsDirectory, "vcl AddTempFontDir() with invalid directory name!" ); 1655 if( eStatus != noErr ) 1656 return false; 1657 1658 // TODO: deactivate ATSFontContainerRef when closing app 1659 ATSFontContainerRef aATSFontContainer; 1660 1661 const ATSFontContext eContext = kATSFontContextLocal; // TODO: *Global??? 1662 #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) 1663 eStatus = ::ATSFontActivateFromFileReference( &aPathFSRef, 1664 eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, 1665 &aATSFontContainer ); 1666 #else 1667 FSSpec aPathFSSpec; 1668 eStatus = ::FSGetCatalogInfo( &aPathFSRef, kFSCatInfoNone, 1669 NULL, NULL, &aPathFSSpec, NULL ); 1670 if( eStatus != noErr ) 1671 return false; 1672 1673 eStatus = ::ATSFontActivateFromFileSpecification( &aPathFSSpec, 1674 eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, 1675 &aATSFontContainer ); 1676 #endif 1677 if( eStatus != noErr ) 1678 return false; 1679 1680 return true; 1681 } 1682 1683 static bool AddLocalTempFontDirs( void ) 1684 { 1685 static bool bFirst = true; 1686 if( !bFirst ) 1687 return false; 1688 bFirst = false; 1689 1690 // add private font files found in brand and base layer 1691 1692 rtl::OUString aBrandStr( RTL_CONSTASCII_USTRINGPARAM( "$BRAND_BASE_DIR" ) ); 1693 rtl_bootstrap_expandMacros( &aBrandStr.pData ); 1694 rtl::OUString aBrandSysPath; 1695 OSL_VERIFY( osl_getSystemPathFromFileURL( aBrandStr.pData, &aBrandSysPath.pData ) == osl_File_E_None ); 1696 1697 rtl::OStringBuffer aBrandFontDir( aBrandSysPath.getLength()*2 ); 1698 aBrandFontDir.append( rtl::OUStringToOString( aBrandSysPath, RTL_TEXTENCODING_UTF8 ) ); 1699 aBrandFontDir.append( "/share/fonts/truetype/" ); 1700 bool bBrandSuccess = AddTempFontDir( aBrandFontDir.getStr() ); 1701 1702 rtl::OUString aBaseStr( RTL_CONSTASCII_USTRINGPARAM( "$OOO_BASE_DIR" ) ); 1703 rtl_bootstrap_expandMacros( &aBaseStr.pData ); 1704 rtl::OUString aBaseSysPath; 1705 OSL_VERIFY( osl_getSystemPathFromFileURL( aBaseStr.pData, &aBaseSysPath.pData ) == osl_File_E_None ); 1706 1707 rtl::OStringBuffer aBaseFontDir( aBaseSysPath.getLength()*2 ); 1708 aBaseFontDir.append( rtl::OUStringToOString( aBaseSysPath, RTL_TEXTENCODING_UTF8 ) ); 1709 aBaseFontDir.append( "/share/fonts/truetype/" ); 1710 bool bBaseSuccess = AddTempFontDir( aBaseFontDir.getStr() ); 1711 1712 return bBrandSuccess && bBaseSuccess; 1713 } 1714 1715 void AquaSalGraphics::GetDevFontList( ImplDevFontList* pFontList ) 1716 { 1717 DBG_ASSERT( pFontList, "AquaSalGraphics::GetDevFontList(NULL) !"); 1718 1719 AddLocalTempFontDirs(); 1720 1721 // The idea is to cache the list of system fonts once it has been generated. 1722 // SalData seems to be a good place for this caching. However we have to 1723 // carefully make the access to the font list thread-safe. If we register 1724 // a font-change event handler to update the font list in case fonts have 1725 // changed on the system we have to lock access to the list. The right 1726 // way to do that is the solar mutex since GetDevFontList is protected 1727 // through it as should be all event handlers 1728 1729 SalData* pSalData = GetSalData(); 1730 if (pSalData->mpFontList == NULL) 1731 pSalData->mpFontList = new SystemFontList(); 1732 1733 // Copy all ImplFontData objects contained in the SystemFontList 1734 pSalData->mpFontList->AnnounceFonts( *pFontList ); 1735 } 1736 1737 // ----------------------------------------------------------------------- 1738 1739 bool AquaSalGraphics::AddTempDevFont( ImplDevFontList*, 1740 const String& rFontFileURL, const String& /*rFontName*/ ) 1741 { 1742 ::rtl::OUString aUSytemPath; 1743 OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFontFileURL, aUSytemPath ) ); 1744 1745 FSRef aNewRef; 1746 Boolean bIsDirectory = true; 1747 ::rtl::OString aCFileName = rtl::OUStringToOString( aUSytemPath, RTL_TEXTENCODING_UTF8 ); 1748 OSStatus eStatus = FSPathMakeRef( (UInt8*)aCFileName.getStr(), &aNewRef, &bIsDirectory ); 1749 DBG_ASSERT( (eStatus==noErr) && !bIsDirectory, "vcl AddTempDevFont() with invalid fontfile name!" ); 1750 if( eStatus != noErr ) 1751 return false; 1752 1753 ATSFontContainerRef oContainer; 1754 1755 const ATSFontContext eContext = kATSFontContextLocal; // TODO: *Global??? 1756 #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) 1757 eStatus = ::ATSFontActivateFromFileReference( &aNewRef, 1758 eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, 1759 &oContainer ); 1760 #else 1761 FSSpec aFontFSSpec; 1762 eStatus = ::FSGetCatalogInfo( &aNewRef, kFSCatInfoNone, 1763 NULL, NULL, &aFontFSSpec, NULL ); 1764 if( eStatus != noErr ) 1765 return false; 1766 1767 eStatus = ::ATSFontActivateFromFileSpecification( &aFontFSSpec, 1768 eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, 1769 &oContainer ); 1770 #endif 1771 if( eStatus != noErr ) 1772 return false; 1773 1774 // TODO: ATSFontDeactivate( oContainer ) when fonts are no longer needed 1775 // TODO: register new ImplMacFontdata in pFontList 1776 return true; 1777 } 1778 1779 // ----------------------------------------------------------------------- 1780 1781 // callbacks from ATSUGlyphGetCubicPaths() fore GetGlyphOutline() 1782 struct GgoData { basegfx::B2DPolygon maPolygon; basegfx::B2DPolyPolygon* mpPolyPoly; }; 1783 1784 static OSStatus GgoLineToProc( const Float32Point* pPoint, void* pData ) 1785 { 1786 basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon; 1787 const basegfx::B2DPoint aB2DPoint( pPoint->x, pPoint->y ); 1788 rPolygon.append( aB2DPoint ); 1789 return noErr; 1790 } 1791 1792 static OSStatus GgoCurveToProc( const Float32Point* pCP1, const Float32Point* pCP2, 1793 const Float32Point* pPoint, void* pData ) 1794 { 1795 basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon; 1796 const sal_uInt32 nPointCount = rPolygon.count(); 1797 const basegfx::B2DPoint aB2DControlPoint1( pCP1->x, pCP1->y ); 1798 rPolygon.setNextControlPoint( nPointCount-1, aB2DControlPoint1 ); 1799 const basegfx::B2DPoint aB2DEndPoint( pPoint->x, pPoint->y ); 1800 rPolygon.append( aB2DEndPoint ); 1801 const basegfx::B2DPoint aB2DControlPoint2( pCP2->x, pCP2->y ); 1802 rPolygon.setPrevControlPoint( nPointCount, aB2DControlPoint2 ); 1803 return noErr; 1804 } 1805 1806 static OSStatus GgoClosePathProc( void* pData ) 1807 { 1808 GgoData* pGgoData = static_cast<GgoData*>(pData); 1809 basegfx::B2DPolygon& rPolygon = pGgoData->maPolygon; 1810 if( rPolygon.count() > 0 ) 1811 pGgoData->mpPolyPoly->append( rPolygon ); 1812 rPolygon.clear(); 1813 return noErr; 1814 } 1815 1816 static OSStatus GgoMoveToProc( const Float32Point* pPoint, void* pData ) 1817 { 1818 GgoClosePathProc( pData ); 1819 OSStatus eStatus = GgoLineToProc( pPoint, pData ); 1820 return eStatus; 1821 } 1822 1823 sal_Bool AquaSalGraphics::GetGlyphOutline( long nGlyphId, basegfx::B2DPolyPolygon& rPolyPoly ) 1824 { 1825 GgoData aGgoData; 1826 aGgoData.mpPolyPoly = &rPolyPoly; 1827 rPolyPoly.clear(); 1828 1829 ATSUStyle rATSUStyle = maATSUStyle; // TODO: handle glyph fallback when CWS pdffix02 is integrated 1830 OSStatus eGgoStatus = noErr; 1831 OSStatus eStatus = ATSUGlyphGetCubicPaths( rATSUStyle, nGlyphId, 1832 GgoMoveToProc, GgoLineToProc, GgoCurveToProc, GgoClosePathProc, 1833 &aGgoData, &eGgoStatus ); 1834 if( (eStatus != noErr) ) // TODO: why is (eGgoStatus!=noErr) when curves are involved? 1835 return false; 1836 1837 GgoClosePathProc( &aGgoData ); 1838 if( mfFontScale != 1.0 ) { 1839 rPolyPoly.transform(basegfx::tools::createScaleB2DHomMatrix(+mfFontScale, +mfFontScale)); 1840 } 1841 return true; 1842 } 1843 1844 // ----------------------------------------------------------------------- 1845 1846 long AquaSalGraphics::GetGraphicsWidth() const 1847 { 1848 long w = 0; 1849 if( mrContext && (mbWindow || mbVirDev) ) 1850 { 1851 w = mnWidth; 1852 } 1853 1854 if( w == 0 ) 1855 { 1856 if( mbWindow && mpFrame ) 1857 w = mpFrame->maGeometry.nWidth; 1858 } 1859 1860 return w; 1861 } 1862 1863 // ----------------------------------------------------------------------- 1864 1865 sal_Bool AquaSalGraphics::GetGlyphBoundRect( long nGlyphId, Rectangle& rRect ) 1866 { 1867 ATSUStyle rATSUStyle = maATSUStyle; // TODO: handle glyph fallback 1868 GlyphID aGlyphId = nGlyphId; 1869 ATSGlyphScreenMetrics aGlyphMetrics; 1870 OSStatus eStatus = ATSUGlyphGetScreenMetrics( rATSUStyle, 1871 1, &aGlyphId, 0, FALSE, !mbNonAntialiasedText, &aGlyphMetrics ); 1872 if( eStatus != noErr ) 1873 return false; 1874 1875 const long nMinX = (long)(+aGlyphMetrics.topLeft.x * mfFontScale - 0.5); 1876 const long nMaxX = (long)(aGlyphMetrics.width * mfFontScale + 0.5) + nMinX; 1877 const long nMinY = (long)(-aGlyphMetrics.topLeft.y * mfFontScale - 0.5); 1878 const long nMaxY = (long)(aGlyphMetrics.height * mfFontScale + 0.5) + nMinY; 1879 rRect = Rectangle( nMinX, nMinY, nMaxX, nMaxY ); 1880 return true; 1881 } 1882 1883 // ----------------------------------------------------------------------- 1884 1885 void AquaSalGraphics::GetDevFontSubstList( OutputDevice* ) 1886 { 1887 // nothing to do since there are no device-specific fonts on Aqua 1888 } 1889 1890 // ----------------------------------------------------------------------- 1891 1892 void AquaSalGraphics::DrawServerFontLayout( const ServerFontLayout& ) 1893 { 1894 } 1895 1896 // ----------------------------------------------------------------------- 1897 1898 sal_uInt16 AquaSalGraphics::SetFont( ImplFontSelectData* pReqFont, int /*nFallbackLevel*/ ) 1899 { 1900 if( !pReqFont ) 1901 { 1902 ATSUClearStyle( maATSUStyle ); 1903 mpMacFontData = NULL; 1904 return 0; 1905 } 1906 1907 // store the requested device font entry 1908 const ImplMacFontData* pMacFont = static_cast<const ImplMacFontData*>( pReqFont->mpFontData ); 1909 mpMacFontData = pMacFont; 1910 1911 // convert pixel units (as seen by upper layers) to typographic point units 1912 double fScaledAtsHeight = pReqFont->mfExactHeight; 1913 // avoid Fixed16.16 overflows by limiting the ATS font size 1914 static const float fMaxAtsHeight = 144.0; 1915 if( fScaledAtsHeight <= fMaxAtsHeight ) 1916 mfFontScale = 1.0; 1917 else 1918 { 1919 mfFontScale = fScaledAtsHeight / fMaxAtsHeight; 1920 fScaledAtsHeight = fMaxAtsHeight; 1921 } 1922 Fixed fFixedSize = FloatToFixed( fScaledAtsHeight ); 1923 // enable bold-emulation if needed 1924 Boolean bFakeBold = FALSE; 1925 if( (pReqFont->GetWeight() >= WEIGHT_BOLD) 1926 && (pMacFont->GetWeight() < WEIGHT_SEMIBOLD) ) 1927 bFakeBold = TRUE; 1928 // enable italic-emulation if needed 1929 Boolean bFakeItalic = FALSE; 1930 if( ((pReqFont->GetSlant() == ITALIC_NORMAL) || (pReqFont->GetSlant() == ITALIC_OBLIQUE)) 1931 && !((pMacFont->GetSlant() == ITALIC_NORMAL) || (pMacFont->GetSlant() == ITALIC_OBLIQUE)) ) 1932 bFakeItalic = TRUE; 1933 1934 // enable/disable antialiased text 1935 mbNonAntialiasedText = pReqFont->mbNonAntialiased; 1936 UInt32 nStyleRenderingOptions = kATSStyleNoOptions; 1937 if( pReqFont->mbNonAntialiased ) 1938 nStyleRenderingOptions |= kATSStyleNoAntiAliasing; 1939 1940 // set horizontal/vertical mode 1941 ATSUVerticalCharacterType aVerticalCharacterType = kATSUStronglyHorizontal; 1942 if( pReqFont->mbVertical ) 1943 aVerticalCharacterType = kATSUStronglyVertical; 1944 1945 // prepare ATS-fontid as type matching to the kATSUFontTag request 1946 ATSUFontID nFontID = static_cast<ATSUFontID>(pMacFont->GetFontId()); 1947 1948 // update ATSU style attributes with requested font parameters 1949 // TODO: no need to set styles which are already defaulted 1950 1951 const ATSUAttributeTag aTag[] = 1952 { 1953 kATSUFontTag, 1954 kATSUSizeTag, 1955 kATSUQDBoldfaceTag, 1956 kATSUQDItalicTag, 1957 kATSUStyleRenderingOptionsTag, 1958 kATSUVerticalCharacterTag 1959 }; 1960 1961 const ByteCount aValueSize[] = 1962 { 1963 sizeof(ATSUFontID), 1964 sizeof(fFixedSize), 1965 sizeof(bFakeBold), 1966 sizeof(bFakeItalic), 1967 sizeof(nStyleRenderingOptions), 1968 sizeof(aVerticalCharacterType) 1969 }; 1970 1971 const ATSUAttributeValuePtr aValue[] = 1972 { 1973 &nFontID, 1974 &fFixedSize, 1975 &bFakeBold, 1976 &bFakeItalic, 1977 &nStyleRenderingOptions, 1978 &aVerticalCharacterType 1979 }; 1980 1981 static const int nTagCount = sizeof(aTag) / sizeof(*aTag); 1982 OSStatus eStatus = ATSUSetAttributes( maATSUStyle, nTagCount, 1983 aTag, aValueSize, aValue ); 1984 // reset ATSUstyle if there was an error 1985 if( eStatus != noErr ) 1986 { 1987 DBG_WARNING( "AquaSalGraphics::SetFont() : Could not set font attributes!\n"); 1988 ATSUClearStyle( maATSUStyle ); 1989 mpMacFontData = NULL; 1990 return 0; 1991 } 1992 1993 // prepare font stretching 1994 const ATSUAttributeTag aMatrixTag = kATSUFontMatrixTag; 1995 if( (pReqFont->mnWidth == 0) || (pReqFont->mnWidth == pReqFont->mnHeight) ) 1996 { 1997 mfFontStretch = 1.0; 1998 ATSUClearAttributes( maATSUStyle, 1, &aMatrixTag ); 1999 } 2000 else 2001 { 2002 mfFontStretch = (float)pReqFont->mnWidth / pReqFont->mnHeight; 2003 CGAffineTransform aMatrix = CGAffineTransformMakeScale( mfFontStretch, 1.0F ); 2004 const ATSUAttributeValuePtr aAttr = &aMatrix; 2005 const ByteCount aMatrixBytes = sizeof(aMatrix); 2006 eStatus = ATSUSetAttributes( maATSUStyle, 1, &aMatrixTag, &aMatrixBytes, &aAttr ); 2007 DBG_ASSERT( (eStatus==noErr), "AquaSalGraphics::SetFont() : Could not set font matrix\n"); 2008 } 2009 2010 // prepare font rotation 2011 mnATSUIRotation = FloatToFixed( pReqFont->mnOrientation / 10.0 ); 2012 2013 #if OSL_DEBUG_LEVEL > 3 2014 fprintf( stderr, "SetFont to (\"%s\", \"%s\", fontid=%d) for (\"%s\" \"%s\" weight=%d, slant=%d size=%dx%d orientation=%d)\n", 2015 ::rtl::OUStringToOString( pMacFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ).getStr(), 2016 ::rtl::OUStringToOString( pMacFont->GetStyleName(), RTL_TEXTENCODING_UTF8 ).getStr(), 2017 (int)nFontID, 2018 ::rtl::OUStringToOString( pReqFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ).getStr(), 2019 ::rtl::OUStringToOString( pReqFont->GetStyleName(), RTL_TEXTENCODING_UTF8 ).getStr(), 2020 pReqFont->GetWeight(), 2021 pReqFont->GetSlant(), 2022 pReqFont->mnHeight, 2023 pReqFont->mnWidth, 2024 pReqFont->mnOrientation); 2025 #endif 2026 2027 return 0; 2028 } 2029 2030 // ----------------------------------------------------------------------- 2031 2032 const ImplFontCharMap* AquaSalGraphics::GetImplFontCharMap() const 2033 { 2034 if( !mpMacFontData ) 2035 return ImplFontCharMap::GetDefaultMap(); 2036 2037 return mpMacFontData->GetImplFontCharMap(); 2038 } 2039 2040 // ----------------------------------------------------------------------- 2041 2042 // fake a SFNT font directory entry for a font table 2043 // see http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html#Directory 2044 static void FakeDirEntry( FourCharCode eFCC, ByteCount nOfs, ByteCount nLen, 2045 const unsigned char* /*pData*/, unsigned char*& rpDest ) 2046 { 2047 // write entry tag 2048 rpDest[ 0] = (char)(eFCC >> 24); 2049 rpDest[ 1] = (char)(eFCC >> 16); 2050 rpDest[ 2] = (char)(eFCC >> 8); 2051 rpDest[ 3] = (char)(eFCC >> 0); 2052 // TODO: get entry checksum and write it 2053 // not too important since the subsetter doesn't care currently 2054 // for( pData+nOfs ... pData+nOfs+nLen ) 2055 // write entry offset 2056 rpDest[ 8] = (char)(nOfs >> 24); 2057 rpDest[ 9] = (char)(nOfs >> 16); 2058 rpDest[10] = (char)(nOfs >> 8); 2059 rpDest[11] = (char)(nOfs >> 0); 2060 // write entry length 2061 rpDest[12] = (char)(nLen >> 24); 2062 rpDest[13] = (char)(nLen >> 16); 2063 rpDest[14] = (char)(nLen >> 8); 2064 rpDest[15] = (char)(nLen >> 0); 2065 // advance to next entry 2066 rpDest += 16; 2067 } 2068 2069 static bool GetRawFontData( const ImplFontData* pFontData, 2070 ByteVector& rBuffer, bool* pJustCFF ) 2071 { 2072 const ImplMacFontData* pMacFont = static_cast<const ImplMacFontData*>(pFontData); 2073 const ATSUFontID nFontId = static_cast<ATSUFontID>(pMacFont->GetFontId()); 2074 ATSFontRef rFont = FMGetATSFontRefFromFont( nFontId ); 2075 2076 ByteCount nCffLen = 0; 2077 OSStatus eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, 0, NULL, &nCffLen); 2078 if( pJustCFF != NULL ) 2079 { 2080 *pJustCFF = (eStatus == noErr) && (nCffLen > 0); 2081 if( *pJustCFF ) 2082 { 2083 rBuffer.resize( nCffLen ); 2084 eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, nCffLen, (void*)&rBuffer[0], &nCffLen); 2085 if( (eStatus != noErr) || (nCffLen <= 0) ) 2086 return false; 2087 return true; 2088 } 2089 } 2090 2091 // get font table availability and size in bytes 2092 ByteCount nHeadLen = 0; 2093 eStatus = ATSFontGetTable( rFont, GetTag("head"), 0, 0, NULL, &nHeadLen); 2094 if( (eStatus != noErr) || (nHeadLen <= 0) ) 2095 return false; 2096 ByteCount nMaxpLen = 0; 2097 eStatus = ATSFontGetTable( rFont, GetTag("maxp"), 0, 0, NULL, &nMaxpLen); 2098 if( (eStatus != noErr) || (nMaxpLen <= 0) ) 2099 return false; 2100 ByteCount nCmapLen = 0; 2101 eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, 0, NULL, &nCmapLen); 2102 if( (eStatus != noErr) || (nCmapLen <= 0) ) 2103 return false; 2104 ByteCount nNameLen = 0; 2105 eStatus = ATSFontGetTable( rFont, GetTag("name"), 0, 0, NULL, &nNameLen); 2106 if( (eStatus != noErr) || (nNameLen <= 0) ) 2107 return false; 2108 ByteCount nHheaLen = 0; 2109 eStatus = ATSFontGetTable( rFont, GetTag("hhea"), 0, 0, NULL, &nHheaLen); 2110 if( (eStatus != noErr) || (nHheaLen <= 0) ) 2111 return false; 2112 ByteCount nHmtxLen = 0; 2113 eStatus = ATSFontGetTable( rFont, GetTag("hmtx"), 0, 0, NULL, &nHmtxLen); 2114 if( (eStatus != noErr) || (nHmtxLen <= 0) ) 2115 return false; 2116 2117 // get the glyph outline tables 2118 ByteCount nLocaLen = 0; 2119 ByteCount nGlyfLen = 0; 2120 if( (eStatus != noErr) || (nCffLen <= 0) ) 2121 { 2122 eStatus = ATSFontGetTable( rFont, GetTag("loca"), 0, 0, NULL, &nLocaLen); 2123 if( (eStatus != noErr) || (nLocaLen <= 0) ) 2124 return false; 2125 eStatus = ATSFontGetTable( rFont, GetTag("glyf"), 0, 0, NULL, &nGlyfLen); 2126 if( (eStatus != noErr) || (nGlyfLen <= 0) ) 2127 return false; 2128 } 2129 2130 ByteCount nPrepLen=0, nCvtLen=0, nFpgmLen=0; 2131 if( nGlyfLen ) // TODO: reduce PDF size by making hint subsetting optional 2132 { 2133 eStatus = ATSFontGetTable( rFont, GetTag("prep"), 0, 0, NULL, &nPrepLen); 2134 eStatus = ATSFontGetTable( rFont, GetTag("cvt "), 0, 0, NULL, &nCvtLen); 2135 eStatus = ATSFontGetTable( rFont, GetTag("fpgm"), 0, 0, NULL, &nFpgmLen); 2136 } 2137 2138 // prepare a byte buffer for a fake font 2139 int nTableCount = 7; 2140 nTableCount += (nPrepLen>0) + (nCvtLen>0) + (nFpgmLen>0) + (nGlyfLen>0); 2141 const ByteCount nFdirLen = 12 + 16*nTableCount; 2142 ByteCount nTotalLen = nFdirLen; 2143 nTotalLen += nHeadLen + nMaxpLen + nNameLen + nCmapLen; 2144 if( nGlyfLen ) 2145 nTotalLen += nLocaLen + nGlyfLen; 2146 else 2147 nTotalLen += nCffLen; 2148 nTotalLen += nHheaLen + nHmtxLen; 2149 nTotalLen += nPrepLen + nCvtLen + nFpgmLen; 2150 rBuffer.resize( nTotalLen ); 2151 2152 // fake a SFNT font directory header 2153 if( nTableCount < 16 ) 2154 { 2155 int nLog2 = 0; 2156 while( (nTableCount >> nLog2) > 1 ) ++nLog2; 2157 rBuffer[ 1] = 1; // Win-TTF style scaler 2158 rBuffer[ 5] = nTableCount; // table count 2159 rBuffer[ 7] = nLog2*16; // searchRange 2160 rBuffer[ 9] = nLog2; // entrySelector 2161 rBuffer[11] = (nTableCount-nLog2)*16; // rangeShift 2162 } 2163 2164 // get font table raw data and update the fake directory entries 2165 ByteCount nOfs = nFdirLen; 2166 unsigned char* pFakeEntry = &rBuffer[12]; 2167 eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, nCmapLen, (void*)&rBuffer[nOfs], &nCmapLen); 2168 FakeDirEntry( GetTag("cmap"), nOfs, nCmapLen, &rBuffer[0], pFakeEntry ); 2169 nOfs += nCmapLen; 2170 if( nCvtLen ) { 2171 eStatus = ATSFontGetTable( rFont, GetTag("cvt "), 0, nCvtLen, (void*)&rBuffer[nOfs], &nCvtLen); 2172 FakeDirEntry( GetTag("cvt "), nOfs, nCvtLen, &rBuffer[0], pFakeEntry ); 2173 nOfs += nCvtLen; 2174 } 2175 if( nFpgmLen ) { 2176 eStatus = ATSFontGetTable( rFont, GetTag("fpgm"), 0, nFpgmLen, (void*)&rBuffer[nOfs], &nFpgmLen); 2177 FakeDirEntry( GetTag("fpgm"), nOfs, nFpgmLen, &rBuffer[0], pFakeEntry ); 2178 nOfs += nFpgmLen; 2179 } 2180 if( nCffLen ) { 2181 eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, nCffLen, (void*)&rBuffer[nOfs], &nCffLen); 2182 FakeDirEntry( GetTag("CFF "), nOfs, nCffLen, &rBuffer[0], pFakeEntry ); 2183 nOfs += nGlyfLen; 2184 } else { 2185 eStatus = ATSFontGetTable( rFont, GetTag("glyf"), 0, nGlyfLen, (void*)&rBuffer[nOfs], &nGlyfLen); 2186 FakeDirEntry( GetTag("glyf"), nOfs, nGlyfLen, &rBuffer[0], pFakeEntry ); 2187 nOfs += nGlyfLen; 2188 eStatus = ATSFontGetTable( rFont, GetTag("loca"), 0, nLocaLen, (void*)&rBuffer[nOfs], &nLocaLen); 2189 FakeDirEntry( GetTag("loca"), nOfs, nLocaLen, &rBuffer[0], pFakeEntry ); 2190 nOfs += nLocaLen; 2191 } 2192 eStatus = ATSFontGetTable( rFont, GetTag("head"), 0, nHeadLen, (void*)&rBuffer[nOfs], &nHeadLen); 2193 FakeDirEntry( GetTag("head"), nOfs, nHeadLen, &rBuffer[0], pFakeEntry ); 2194 nOfs += nHeadLen; 2195 eStatus = ATSFontGetTable( rFont, GetTag("hhea"), 0, nHheaLen, (void*)&rBuffer[nOfs], &nHheaLen); 2196 FakeDirEntry( GetTag("hhea"), nOfs, nHheaLen, &rBuffer[0], pFakeEntry ); 2197 nOfs += nHheaLen; 2198 eStatus = ATSFontGetTable( rFont, GetTag("hmtx"), 0, nHmtxLen, (void*)&rBuffer[nOfs], &nHmtxLen); 2199 FakeDirEntry( GetTag("hmtx"), nOfs, nHmtxLen, &rBuffer[0], pFakeEntry ); 2200 nOfs += nHmtxLen; 2201 eStatus = ATSFontGetTable( rFont, GetTag("maxp"), 0, nMaxpLen, (void*)&rBuffer[nOfs], &nMaxpLen); 2202 FakeDirEntry( GetTag("maxp"), nOfs, nMaxpLen, &rBuffer[0], pFakeEntry ); 2203 nOfs += nMaxpLen; 2204 eStatus = ATSFontGetTable( rFont, GetTag("name"), 0, nNameLen, (void*)&rBuffer[nOfs], &nNameLen); 2205 FakeDirEntry( GetTag("name"), nOfs, nNameLen, &rBuffer[0], pFakeEntry ); 2206 nOfs += nNameLen; 2207 if( nPrepLen ) { 2208 eStatus = ATSFontGetTable( rFont, GetTag("prep"), 0, nPrepLen, (void*)&rBuffer[nOfs], &nPrepLen); 2209 FakeDirEntry( GetTag("prep"), nOfs, nPrepLen, &rBuffer[0], pFakeEntry ); 2210 nOfs += nPrepLen; 2211 } 2212 2213 DBG_ASSERT( (nOfs==nTotalLen), "AquaSalGraphics::CreateFontSubset (nOfs!=nTotalLen)"); 2214 2215 return sal_True; 2216 } 2217 2218 sal_Bool AquaSalGraphics::CreateFontSubset( const rtl::OUString& rToFile, 2219 const ImplFontData* pFontData, long* pGlyphIDs, sal_uInt8* pEncoding, 2220 sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rInfo ) 2221 { 2222 // TODO: move more of the functionality here into the generic subsetter code 2223 2224 // prepare the requested file name for writing the font-subset file 2225 rtl::OUString aSysPath; 2226 if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) ) 2227 return sal_False; 2228 const rtl_TextEncoding aThreadEncoding = osl_getThreadTextEncoding(); 2229 const ByteString aToFile( rtl::OUStringToOString( aSysPath, aThreadEncoding ) ); 2230 2231 // get the raw-bytes from the font to be subset 2232 ByteVector aBuffer; 2233 bool bCffOnly = false; 2234 if( !GetRawFontData( pFontData, aBuffer, &bCffOnly ) ) 2235 return sal_False; 2236 2237 // handle CFF-subsetting 2238 if( bCffOnly ) 2239 { 2240 // provide the raw-CFF data to the subsetter 2241 ByteCount nCffLen = aBuffer.size(); 2242 rInfo.LoadFont( FontSubsetInfo::CFF_FONT, &aBuffer[0], nCffLen ); 2243 2244 // NOTE: assuming that all glyphids requested on Aqua are fully translated 2245 2246 // make the subsetter provide the requested subset 2247 FILE* pOutFile = fopen( aToFile.GetBuffer(), "wb" ); 2248 bool bRC = rInfo.CreateFontSubset( FontSubsetInfo::TYPE1_PFB, pOutFile, NULL, 2249 pGlyphIDs, pEncoding, nGlyphCount, pGlyphWidths ); 2250 fclose( pOutFile ); 2251 return bRC; 2252 } 2253 2254 // TODO: modernize psprint's horrible fontsubset C-API 2255 // this probably only makes sense after the switch to another SCM 2256 // that can preserve change history after file renames 2257 2258 // prepare data for psprint's font subsetter 2259 TrueTypeFont* pSftFont = NULL; 2260 int nRC = ::OpenTTFontBuffer( (void*)&aBuffer[0], aBuffer.size(), 0, &pSftFont); 2261 if( nRC != SF_OK ) 2262 return sal_False; 2263 2264 // get details about the subsetted font 2265 TTGlobalFontInfo aTTInfo; 2266 ::GetTTGlobalFontInfo( pSftFont, &aTTInfo ); 2267 rInfo.m_nFontType = FontSubsetInfo::SFNT_TTF; 2268 rInfo.m_aPSName = String( aTTInfo.psname, RTL_TEXTENCODING_UTF8 ); 2269 rInfo.m_aFontBBox = Rectangle( Point( aTTInfo.xMin, aTTInfo.yMin ), 2270 Point( aTTInfo.xMax, aTTInfo.yMax ) ); 2271 rInfo.m_nCapHeight = aTTInfo.yMax; // Well ... 2272 rInfo.m_nAscent = aTTInfo.winAscent; 2273 rInfo.m_nDescent = aTTInfo.winDescent; 2274 // mac fonts usually do not have an OS2-table 2275 // => get valid ascent/descent values from other tables 2276 if( !rInfo.m_nAscent ) 2277 rInfo.m_nAscent = +aTTInfo.typoAscender; 2278 if( !rInfo.m_nAscent ) 2279 rInfo.m_nAscent = +aTTInfo.ascender; 2280 if( !rInfo.m_nDescent ) 2281 rInfo.m_nDescent = +aTTInfo.typoDescender; 2282 if( !rInfo.m_nDescent ) 2283 rInfo.m_nDescent = -aTTInfo.descender; 2284 2285 // subset glyphs and get their properties 2286 // take care that subset fonts require the NotDef glyph in pos 0 2287 int nOrigCount = nGlyphCount; 2288 sal_uInt16 aShortIDs[ 256 ]; 2289 sal_uInt8 aTempEncs[ 256 ]; 2290 2291 int nNotDef = -1; 2292 for( int i = 0; i < nGlyphCount; ++i ) 2293 { 2294 aTempEncs[i] = pEncoding[i]; 2295 sal_uInt32 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK; 2296 if( pGlyphIDs[i] & GF_ISCHAR ) 2297 { 2298 bool bVertical = (pGlyphIDs[i] & GF_ROTMASK) != 0; 2299 nGlyphIdx = ::MapChar( pSftFont, static_cast<sal_uInt16>(nGlyphIdx), bVertical ); 2300 if( nGlyphIdx == 0 && pFontData->IsSymbolFont() ) 2301 { 2302 // #i12824# emulate symbol aliasing U+FXXX <-> U+0XXX 2303 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK; 2304 nGlyphIdx = (nGlyphIdx & 0xF000) ? (nGlyphIdx & 0x00FF) : (nGlyphIdx | 0xF000 ); 2305 nGlyphIdx = ::MapChar( pSftFont, static_cast<sal_uInt16>(nGlyphIdx), bVertical ); 2306 } 2307 } 2308 aShortIDs[i] = static_cast<sal_uInt16>( nGlyphIdx ); 2309 if( !nGlyphIdx ) 2310 if( nNotDef < 0 ) 2311 nNotDef = i; // first NotDef glyph found 2312 } 2313 2314 if( nNotDef != 0 ) 2315 { 2316 // add fake NotDef glyph if needed 2317 if( nNotDef < 0 ) 2318 nNotDef = nGlyphCount++; 2319 2320 // NotDef glyph must be in pos 0 => swap glyphids 2321 aShortIDs[ nNotDef ] = aShortIDs[0]; 2322 aTempEncs[ nNotDef ] = aTempEncs[0]; 2323 aShortIDs[0] = 0; 2324 aTempEncs[0] = 0; 2325 } 2326 DBG_ASSERT( nGlyphCount < 257, "too many glyphs for subsetting" ); 2327 2328 // TODO: where to get bVertical? 2329 const bool bVertical = false; 2330 2331 // fill the pGlyphWidths array 2332 // while making sure that the NotDef glyph is at index==0 2333 TTSimpleGlyphMetrics* pGlyphMetrics = 2334 ::GetTTSimpleGlyphMetrics( pSftFont, aShortIDs, nGlyphCount, bVertical ); 2335 if( !pGlyphMetrics ) 2336 return sal_False; 2337 sal_uInt16 nNotDefAdv = pGlyphMetrics[0].adv; 2338 pGlyphMetrics[0].adv = pGlyphMetrics[nNotDef].adv; 2339 pGlyphMetrics[nNotDef].adv = nNotDefAdv; 2340 for( int i = 0; i < nOrigCount; ++i ) 2341 pGlyphWidths[i] = pGlyphMetrics[i].adv; 2342 free( pGlyphMetrics ); 2343 2344 // write subset into destination file 2345 nRC = ::CreateTTFromTTGlyphs( pSftFont, aToFile.GetBuffer(), aShortIDs, 2346 aTempEncs, nGlyphCount, 0, NULL, 0 ); 2347 ::CloseTTFont(pSftFont); 2348 return (nRC == SF_OK); 2349 } 2350 2351 // ----------------------------------------------------------------------- 2352 2353 void AquaSalGraphics::GetGlyphWidths( const ImplFontData* pFontData, bool bVertical, 2354 Int32Vector& rGlyphWidths, Ucs2UIntMap& rUnicodeEnc ) 2355 { 2356 rGlyphWidths.clear(); 2357 rUnicodeEnc.clear(); 2358 2359 if( pFontData->IsSubsettable() ) 2360 { 2361 ByteVector aBuffer; 2362 if( !GetRawFontData( pFontData, aBuffer, NULL ) ) 2363 return; 2364 2365 // TODO: modernize psprint's horrible fontsubset C-API 2366 // this probably only makes sense after the switch to another SCM 2367 // that can preserve change history after file renames 2368 2369 // use the font subsetter to get the widths 2370 TrueTypeFont* pSftFont = NULL; 2371 int nRC = ::OpenTTFontBuffer( (void*)&aBuffer[0], aBuffer.size(), 0, &pSftFont); 2372 if( nRC != SF_OK ) 2373 return; 2374 2375 const int nGlyphCount = ::GetTTGlyphCount( pSftFont ); 2376 if( nGlyphCount > 0 ) 2377 { 2378 // get glyph metrics 2379 rGlyphWidths.resize(nGlyphCount); 2380 std::vector<sal_uInt16> aGlyphIds(nGlyphCount); 2381 for( int i = 0; i < nGlyphCount; i++ ) 2382 aGlyphIds[i] = static_cast<sal_uInt16>(i); 2383 const TTSimpleGlyphMetrics* pGlyphMetrics = ::GetTTSimpleGlyphMetrics( 2384 pSftFont, &aGlyphIds[0], nGlyphCount, bVertical ); 2385 if( pGlyphMetrics ) 2386 { 2387 for( int i = 0; i < nGlyphCount; ++i ) 2388 rGlyphWidths[i] = pGlyphMetrics[i].adv; 2389 free( (void*)pGlyphMetrics ); 2390 } 2391 2392 const ImplFontCharMap* pMap = mpMacFontData->GetImplFontCharMap(); 2393 DBG_ASSERT( pMap && pMap->GetCharCount(), "no charmap" ); 2394 pMap->AddReference(); // TODO: add and use RAII object instead 2395 2396 // get unicode<->glyph encoding 2397 // TODO? avoid sft mapping by using the pMap itself 2398 int nCharCount = pMap->GetCharCount(); 2399 sal_uInt32 nChar = pMap->GetFirstChar(); 2400 for(; --nCharCount >= 0; nChar = pMap->GetNextChar( nChar ) ) 2401 { 2402 if( nChar > 0xFFFF ) // TODO: allow UTF-32 chars 2403 break; 2404 sal_Ucs nUcsChar = static_cast<sal_Ucs>(nChar); 2405 sal_uInt32 nGlyph = ::MapChar( pSftFont, nUcsChar, bVertical ); 2406 if( nGlyph > 0 ) 2407 rUnicodeEnc[ nUcsChar ] = nGlyph; 2408 } 2409 2410 pMap->DeReference(); // TODO: add and use RAII object instead 2411 } 2412 2413 ::CloseTTFont( pSftFont ); 2414 } 2415 else if( pFontData->IsEmbeddable() ) 2416 { 2417 // get individual character widths 2418 #if 0 // FIXME 2419 rWidths.reserve( 224 ); 2420 for( sal_Unicode i = 32; i < 256; ++i ) 2421 { 2422 int nCharWidth = 0; 2423 if( ::GetCharWidth32W( mhDC, i, i, &nCharWidth ) ) 2424 { 2425 rUnicodeEnc[ i ] = rWidths.size(); 2426 rWidths.push_back( nCharWidth ); 2427 } 2428 } 2429 #else 2430 DBG_ERROR("not implemented for non-subsettable fonts!\n"); 2431 #endif 2432 } 2433 } 2434 2435 // ----------------------------------------------------------------------- 2436 2437 const Ucs2SIntMap* AquaSalGraphics::GetFontEncodingVector( 2438 const ImplFontData*, const Ucs2OStrMap** /*ppNonEncoded*/ ) 2439 { 2440 return NULL; 2441 } 2442 2443 // ----------------------------------------------------------------------- 2444 2445 const void* AquaSalGraphics::GetEmbedFontData( const ImplFontData*, 2446 const sal_Ucs* /*pUnicodes*/, 2447 sal_Int32* /*pWidths*/, 2448 FontSubsetInfo&, 2449 long* /*pDataLen*/ ) 2450 { 2451 return NULL; 2452 } 2453 2454 // ----------------------------------------------------------------------- 2455 2456 void AquaSalGraphics::FreeEmbedFontData( const void* pData, long /*nDataLen*/ ) 2457 { 2458 // TODO: implementing this only makes sense when the implementation of 2459 // AquaSalGraphics::GetEmbedFontData() returns non-NULL 2460 (void)pData; 2461 DBG_ASSERT( (pData!=NULL), "AquaSalGraphics::FreeEmbedFontData() is not implemented\n"); 2462 } 2463 2464 // ----------------------------------------------------------------------- 2465 2466 SystemFontData AquaSalGraphics::GetSysFontData( int /* nFallbacklevel */ ) const 2467 { 2468 SystemFontData aSysFontData; 2469 OSStatus err; 2470 aSysFontData.nSize = sizeof( SystemFontData ); 2471 2472 // NOTE: Native ATSU font fallbacks are used, not the VCL fallbacks. 2473 ATSUFontID fontId; 2474 err = ATSUGetAttribute( maATSUStyle, kATSUFontTag, sizeof(fontId), &fontId, 0 ); 2475 if (err) fontId = 0; 2476 aSysFontData.aATSUFontID = (void *) fontId; 2477 2478 Boolean bFbold; 2479 err = ATSUGetAttribute( maATSUStyle, kATSUQDBoldfaceTag, sizeof(bFbold), &bFbold, 0 ); 2480 if (err) bFbold = FALSE; 2481 aSysFontData.bFakeBold = (bool) bFbold; 2482 2483 Boolean bFItalic; 2484 err = ATSUGetAttribute( maATSUStyle, kATSUQDItalicTag, sizeof(bFItalic), &bFItalic, 0 ); 2485 if (err) bFItalic = FALSE; 2486 aSysFontData.bFakeItalic = (bool) bFItalic; 2487 2488 ATSUVerticalCharacterType aVerticalCharacterType; 2489 err = ATSUGetAttribute( maATSUStyle, kATSUVerticalCharacterTag, sizeof(aVerticalCharacterType), &aVerticalCharacterType, 0 ); 2490 if (!err && aVerticalCharacterType == kATSUStronglyVertical) { 2491 aSysFontData.bVerticalCharacterType = true; 2492 } else { 2493 aSysFontData.bVerticalCharacterType = false; 2494 } 2495 2496 aSysFontData.bAntialias = !mbNonAntialiasedText; 2497 2498 return aSysFontData; 2499 } 2500 2501 // ----------------------------------------------------------------------- 2502 2503 SystemGraphicsData AquaSalGraphics::GetGraphicsData() const 2504 { 2505 SystemGraphicsData aRes; 2506 aRes.nSize = sizeof(aRes); 2507 aRes.rCGContext = mrContext; 2508 return aRes; 2509 } 2510 2511 // ----------------------------------------------------------------------- 2512 2513 void AquaSalGraphics::SetXORMode( bool bSet, bool bInvertOnly ) 2514 { 2515 // return early if XOR mode remains unchanged 2516 if( mbPrinter ) 2517 return; 2518 2519 if( ! bSet && mnXorMode == 2 ) 2520 { 2521 CGContextSetBlendMode( mrContext, kCGBlendModeNormal ); 2522 mnXorMode = 0; 2523 return; 2524 } 2525 else if( bSet && bInvertOnly && mnXorMode == 0) 2526 { 2527 CGContextSetBlendMode( mrContext, kCGBlendModeDifference ); 2528 mnXorMode = 2; 2529 return; 2530 } 2531 2532 if( (mpXorEmulation == NULL) && !bSet ) 2533 return; 2534 if( (mpXorEmulation != NULL) && (bSet == mpXorEmulation->IsEnabled()) ) 2535 return; 2536 if( !CheckContext() ) 2537 return; 2538 2539 // prepare XOR emulation 2540 if( !mpXorEmulation ) 2541 { 2542 mpXorEmulation = new XorEmulation(); 2543 mpXorEmulation->SetTarget( mnWidth, mnHeight, mnBitmapDepth, mrContext, mxLayer ); 2544 } 2545 2546 // change the XOR mode 2547 if( bSet ) 2548 { 2549 mpXorEmulation->Enable(); 2550 mrContext = mpXorEmulation->GetMaskContext(); 2551 mnXorMode = 1; 2552 } 2553 else 2554 { 2555 mpXorEmulation->UpdateTarget(); 2556 mpXorEmulation->Disable(); 2557 mrContext = mpXorEmulation->GetTargetContext(); 2558 mnXorMode = 0; 2559 } 2560 } 2561 2562 // ----------------------------------------------------------------------- 2563 2564 // apply the XOR mask to the target context if active and dirty 2565 void AquaSalGraphics::ApplyXorContext() 2566 { 2567 if( !mpXorEmulation ) 2568 return; 2569 if( mpXorEmulation->UpdateTarget() ) 2570 RefreshRect( 0, 0, mnWidth, mnHeight ); // TODO: refresh minimal changerect 2571 } 2572 2573 // ====================================================================== 2574 2575 XorEmulation::XorEmulation() 2576 : mxTargetLayer( NULL ) 2577 , mxTargetContext( NULL ) 2578 , mxMaskContext( NULL ) 2579 , mxTempContext( NULL ) 2580 , mpMaskBuffer( NULL ) 2581 , mpTempBuffer( NULL ) 2582 , mnBufferLongs( 0 ) 2583 , mbIsEnabled( false ) 2584 {} 2585 2586 // ---------------------------------------------------------------------- 2587 2588 XorEmulation::~XorEmulation() 2589 { 2590 Disable(); 2591 SetTarget( 0, 0, 0, NULL, NULL ); 2592 } 2593 2594 // ----------------------------------------------------------------------- 2595 2596 void XorEmulation::SetTarget( int nWidth, int nHeight, int nTargetDepth, 2597 CGContextRef xTargetContext, CGLayerRef xTargetLayer ) 2598 { 2599 // prepare to replace old mask+temp context 2600 if( mxMaskContext ) 2601 { 2602 // cleanup the mask context 2603 CGContextRelease( mxMaskContext ); 2604 delete[] mpMaskBuffer; 2605 mxMaskContext = NULL; 2606 mpMaskBuffer = NULL; 2607 2608 // cleanup the temp context if needed 2609 if( mxTempContext ) 2610 { 2611 CGContextRelease( mxTempContext ); 2612 delete[] mpTempBuffer; 2613 mxTempContext = NULL; 2614 mpTempBuffer = NULL; 2615 } 2616 } 2617 2618 // return early if there is nothing more to do 2619 if( !xTargetContext ) 2620 return; 2621 2622 // retarget drawing operations to the XOR mask 2623 mxTargetLayer = xTargetLayer; 2624 mxTargetContext = xTargetContext; 2625 2626 // prepare creation of matching CGBitmaps 2627 CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace; 2628 CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst; 2629 int nBitDepth = nTargetDepth; 2630 if( !nBitDepth ) 2631 nBitDepth = 32; 2632 int nBytesPerRow = (nBitDepth == 16) ? 2 : 4; 2633 const size_t nBitsPerComponent = (nBitDepth == 16) ? 5 : 8; 2634 if( nBitDepth <= 8 ) 2635 { 2636 aCGColorSpace = GetSalData()->mxGraySpace; 2637 aCGBmpInfo = kCGImageAlphaNone; 2638 nBytesPerRow = 1; 2639 } 2640 nBytesPerRow *= nWidth; 2641 mnBufferLongs = (nHeight * nBytesPerRow + sizeof(sal_uLong)-1) / sizeof(sal_uLong); 2642 2643 // create a XorMask context 2644 mpMaskBuffer = new sal_uLong[ mnBufferLongs ]; 2645 mxMaskContext = ::CGBitmapContextCreate( mpMaskBuffer, 2646 nWidth, nHeight, nBitsPerComponent, nBytesPerRow, 2647 aCGColorSpace, aCGBmpInfo ); 2648 // reset the XOR mask to black 2649 memset( mpMaskBuffer, 0, mnBufferLongs * sizeof(sal_uLong) ); 2650 2651 // a bitmap context will be needed for manual XORing 2652 // create one unless the target context is a bitmap context 2653 if( nTargetDepth ) 2654 mpTempBuffer = (sal_uLong*)CGBitmapContextGetData( mxTargetContext ); 2655 if( !mpTempBuffer ) 2656 { 2657 // create a bitmap context matching to the target context 2658 mpTempBuffer = new sal_uLong[ mnBufferLongs ]; 2659 mxTempContext = ::CGBitmapContextCreate( mpTempBuffer, 2660 nWidth, nHeight, nBitsPerComponent, nBytesPerRow, 2661 aCGColorSpace, aCGBmpInfo ); 2662 } 2663 2664 // initialize XOR mask context for drawing 2665 CGContextSetFillColorSpace( mxMaskContext, aCGColorSpace ); 2666 CGContextSetStrokeColorSpace( mxMaskContext, aCGColorSpace ); 2667 CGContextSetShouldAntialias( mxMaskContext, false ); 2668 2669 // improve the XorMask's XOR emulation a litte 2670 // NOTE: currently only enabled for monochrome contexts 2671 if( aCGColorSpace == GetSalData()->mxGraySpace ) 2672 CGContextSetBlendMode( mxMaskContext, kCGBlendModeDifference ); 2673 2674 // intialize the transformation matrix to the drawing target 2675 const CGAffineTransform aCTM = CGContextGetCTM( xTargetContext ); 2676 CGContextConcatCTM( mxMaskContext, aCTM ); 2677 if( mxTempContext ) 2678 CGContextConcatCTM( mxTempContext, aCTM ); 2679 2680 // initialize the default XorMask graphics state 2681 CGContextSaveGState( mxMaskContext ); 2682 } 2683 2684 // ---------------------------------------------------------------------- 2685 2686 bool XorEmulation::UpdateTarget() 2687 { 2688 if( !IsEnabled() ) 2689 return false; 2690 2691 // update the temp bitmap buffer if needed 2692 if( mxTempContext ) 2693 CGContextDrawLayerAtPoint( mxTempContext, CGPointZero, mxTargetLayer ); 2694 2695 // do a manual XOR with the XorMask 2696 // this approach suffices for simple color manipulations 2697 // and also the complex-clipping-XOR-trick used in metafiles 2698 const sal_uLong* pSrc = mpMaskBuffer; 2699 sal_uLong* pDst = mpTempBuffer; 2700 for( int i = mnBufferLongs; --i >= 0;) 2701 *(pDst++) ^= *(pSrc++); 2702 2703 // write back the XOR results to the target context 2704 if( mxTempContext ) 2705 { 2706 CGImageRef xXorImage = CGBitmapContextCreateImage( mxTempContext ); 2707 const int nWidth = (int)CGImageGetWidth( xXorImage ); 2708 const int nHeight = (int)CGImageGetHeight( xXorImage ); 2709 // TODO: update minimal changerect 2710 const CGRect aFullRect = {{0,0},{nWidth,nHeight}}; 2711 CGContextDrawImage( mxTargetContext, aFullRect, xXorImage ); 2712 CGImageRelease( xXorImage ); 2713 } 2714 2715 // reset the XorMask to black again 2716 // TODO: not needed for last update 2717 memset( mpMaskBuffer, 0, mnBufferLongs * sizeof(sal_uLong) ); 2718 2719 // TODO: return FALSE if target was not changed 2720 return true; 2721 } 2722 2723 // ======================================================================= 2724 2725