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->getNSWindow() : 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( sal_Int32& rDPIX, sal_Int32& rDPIY ) 434 { 435 if( !mnRealDPIY ) 436 initResolution( (mbWindow && mpFrame) ? mpFrame->getNSWindow() : nil ); 437 438 rDPIX = lrint( mfFakeDPIScale * mnRealDPIX); 439 rDPIY = lrint( mfFakeDPIScale * mnRealDPIY); 440 } 441 442 void AquaSalGraphics::copyResolution( AquaSalGraphics& rGraphics ) 443 { 444 if( !rGraphics.mnRealDPIY && rGraphics.mbWindow && rGraphics.mpFrame ) 445 rGraphics.initResolution( rGraphics.mpFrame->getNSWindow() ); 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 const CGRect aRect = CGRectMake( 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 const CGRect aDstRect = CGRectMake( 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_uInt32 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_uInt32 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_uInt32 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_uInt32 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_uInt32 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_uInt32 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_uInt32 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_uInt32 nPolyCount, const sal_uInt32* 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_uInt32 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_uInt32 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_uInt32 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_uInt32 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_uInt32, const SalPoint*, const sal_uInt8* ) 1078 { 1079 return sal_False; 1080 } 1081 1082 // ----------------------------------------------------------------------- 1083 1084 sal_Bool AquaSalGraphics::drawPolygonBezier( sal_uInt32, const SalPoint*, const sal_uInt8* ) 1085 { 1086 return sal_False; 1087 } 1088 1089 // ----------------------------------------------------------------------- 1090 1091 sal_Bool AquaSalGraphics::drawPolyPolygonBezier( sal_uInt32, const sal_uInt32*, 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 = CGPointMake( +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 = CGRectMake( 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 = CGSizeMake( nSrcWidth, nSrcHeight); 1215 xSrcLayer = ::CGLayerCreateWithContext( xCopyContext, aSrcSize, NULL ); 1216 const CGContextRef xSrcContext = CGLayerGetContext( xSrcLayer ); 1217 CGPoint aSrcPoint = CGPointMake( -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 = CGPointMake( +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 = CGRectMake( 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 = CGRectMake( 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 = CGRectMake( 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 = CGPointMake( -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_uInt32 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 = NSMakeRect( 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 = CGRectMake( 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, const basegfx::B2DPoint& rX, const basegfx::B2DPoint& rY, 1536 const SalBitmap& rSrcBitmap, const SalBitmap* pAlphaBmp ) 1537 { 1538 if( !CheckContext() ) 1539 return true; 1540 1541 // get the Quartz image 1542 CGImageRef xImage = NULL; 1543 const Size aSize = rSrcBitmap.GetSize(); 1544 const AquaSalBitmap& rSrcSalBmp = static_cast<const AquaSalBitmap&>(rSrcBitmap); 1545 const AquaSalBitmap* pMaskSalBmp = static_cast<const AquaSalBitmap*>(pAlphaBmp); 1546 if( !pMaskSalBmp) 1547 xImage = rSrcSalBmp.CreateCroppedImage( 0, 0, (int)aSize.Width(), (int)aSize.Height() ); 1548 else 1549 xImage = rSrcSalBmp.CreateWithMask( *pMaskSalBmp, 0, 0, (int)aSize.Width(), (int)aSize.Height() ); 1550 if( !xImage ) 1551 return false; 1552 1553 // setup the image transformation 1554 // using the rNull,rX,rY points as destinations for the (0,0),(0,Width),(Height,0) source points 1555 CGContextSaveGState( mrContext ); 1556 const basegfx::B2DVector aXRel = rX - rNull; 1557 const basegfx::B2DVector aYRel = rY - rNull; 1558 const CGAffineTransform aCGMat = CGAffineTransformMake( 1559 aXRel.getX()/aSize.Width(), aXRel.getY()/aSize.Width(), 1560 aYRel.getX()/aSize.Height(), aYRel.getY()/aSize.Height(), 1561 rNull.getX(), rNull.getY()); 1562 CGContextConcatCTM( mrContext, aCGMat ); 1563 1564 // draw the transformed image 1565 const CGRect aSrcRect = CGRectMake( 0, 0, aSize.Width(), aSize.Height()); 1566 CGContextDrawImage( mrContext, aSrcRect, xImage ); 1567 CGImageRelease( xImage ); 1568 // restore the Quartz graphics state 1569 CGContextRestoreGState(mrContext); 1570 1571 // mark the destination as painted 1572 const CGRect aDstRect = CGRectApplyAffineTransform( aSrcRect, aCGMat ); 1573 RefreshRect( aDstRect ); 1574 return true; 1575 } 1576 1577 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 1578 bool AquaSalGraphics::drawAlphaRect( long nX, long nY, long nWidth, 1579 long nHeight, sal_uInt8 nTransparency ) 1580 { 1581 if( !CheckContext() ) 1582 return true; 1583 1584 // save the current state 1585 CGContextSaveGState( mrContext ); 1586 CGContextSetAlpha( mrContext, (100-nTransparency) * (1.0/100) ); 1587 1588 CGRect aRect = CGRectMake( nX, nY, nWidth-1, nHeight-1); 1589 if( IsPenVisible() ) 1590 { 1591 aRect.origin.x += 0.5; 1592 aRect.origin.y += 0.5; 1593 } 1594 1595 CGContextBeginPath( mrContext ); 1596 CGContextAddRect( mrContext, aRect ); 1597 CGContextDrawPath( mrContext, kCGPathFill ); 1598 1599 // restore state 1600 CGContextRestoreGState(mrContext); 1601 RefreshRect( aRect ); 1602 return true; 1603 } 1604 1605 // ----------------------------------------------------------------------- 1606 1607 void AquaSalGraphics::SetTextColor( SalColor nSalColor ) 1608 { 1609 RGBColor color; 1610 color.red = (unsigned short) ( SALCOLOR_RED(nSalColor) * 65535.0 / 255.0 ); 1611 color.green = (unsigned short) ( SALCOLOR_GREEN(nSalColor) * 65535.0 / 255.0 ); 1612 color.blue = (unsigned short) ( SALCOLOR_BLUE(nSalColor) * 65535.0 / 255.0 ); 1613 1614 ATSUAttributeTag aTag = kATSUColorTag; 1615 ByteCount aValueSize = sizeof( color ); 1616 ATSUAttributeValuePtr aValue = &color; 1617 1618 OSStatus err = ATSUSetAttributes( maATSUStyle, 1, &aTag, &aValueSize, &aValue ); 1619 DBG_ASSERT( (err==noErr), "AquaSalGraphics::SetTextColor() : Could not set font attributes!\n"); 1620 if( err != noErr ) 1621 return; 1622 } 1623 1624 // ----------------------------------------------------------------------- 1625 1626 void AquaSalGraphics::GetFontMetric( ImplFontMetricData* pMetric, int nFallbackLevel ) 1627 { 1628 (void)nFallbackLevel; // glyph-fallback on ATSU is done differently -> no fallback level 1629 1630 // get the ATSU font metrics (in point units) 1631 // of the font that has eventually been size-limited 1632 1633 ATSUFontID fontId; 1634 OSStatus err = ATSUGetAttribute( maATSUStyle, kATSUFontTag, sizeof(ATSUFontID), &fontId, 0 ); 1635 DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font id\n"); 1636 1637 ATSFontMetrics aMetrics; 1638 ATSFontRef rFont = FMGetATSFontRefFromFont( fontId ); 1639 err = ATSFontGetHorizontalMetrics ( rFont, kATSOptionFlagsDefault, &aMetrics ); 1640 DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font metrics\n"); 1641 if( err != noErr ) 1642 return; 1643 1644 // all ATS fonts are scalable fonts 1645 pMetric->mbScalableFont = true; 1646 // TODO: check if any kerning is possible 1647 pMetric->mbKernableFont = true; 1648 1649 // convert into VCL font metrics (in unscaled pixel units) 1650 1651 Fixed ptSize; 1652 err = ATSUGetAttribute( maATSUStyle, kATSUSizeTag, sizeof(Fixed), &ptSize, 0); 1653 DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font size\n"); 1654 const double fPointSize = Fix2X( ptSize ); 1655 1656 // convert quartz units to pixel units 1657 // please see the comment in AquaSalGraphics::SetFont() for details 1658 const double fPixelSize = (mfFontScale * mfFakeDPIScale * fPointSize); 1659 pMetric->mnAscent = static_cast<long>(+aMetrics.ascent * fPixelSize + 0.5); 1660 pMetric->mnDescent = static_cast<long>(-aMetrics.descent * fPixelSize + 0.5); 1661 const long nExtDescent = static_cast<long>((-aMetrics.descent + aMetrics.leading) * fPixelSize + 0.5); 1662 pMetric->mnExtLeading = nExtDescent - pMetric->mnDescent; 1663 pMetric->mnIntLeading = 0; 1664 // ATSFontMetrics.avgAdvanceWidth is obsolete, so it is usually set to zero 1665 // since ImplFontMetricData::mnWidth is only used for stretching/squeezing fonts 1666 // setting this width to the pixel height of the fontsize is good enough 1667 // it also makes the calculation of the stretch factor simple 1668 pMetric->mnWidth = static_cast<long>(mfFontStretch * fPixelSize + 0.5); 1669 } 1670 1671 // ----------------------------------------------------------------------- 1672 1673 sal_uLong AquaSalGraphics::GetKernPairs( sal_uLong, ImplKernPairData* ) 1674 { 1675 return 0; 1676 } 1677 1678 // ----------------------------------------------------------------------- 1679 1680 static bool AddTempFontDir( const char* pDir ) 1681 { 1682 FSRef aPathFSRef; 1683 Boolean bIsDirectory = true; 1684 OSStatus eStatus = FSPathMakeRef( reinterpret_cast<const UInt8*>(pDir), &aPathFSRef, &bIsDirectory ); 1685 DBG_ASSERTWARNING( (eStatus==noErr) && bIsDirectory, "vcl AddTempFontDir() with invalid directory name!" ); 1686 if( eStatus != noErr ) 1687 return false; 1688 1689 // TODO: deactivate ATSFontContainerRef when closing app 1690 ATSFontContainerRef aATSFontContainer; 1691 1692 const ATSFontContext eContext = kATSFontContextLocal; // TODO: *Global??? 1693 #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) 1694 eStatus = ::ATSFontActivateFromFileReference( &aPathFSRef, 1695 eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, 1696 &aATSFontContainer ); 1697 #else 1698 FSSpec aPathFSSpec; 1699 eStatus = ::FSGetCatalogInfo( &aPathFSRef, kFSCatInfoNone, 1700 NULL, NULL, &aPathFSSpec, NULL ); 1701 if( eStatus != noErr ) 1702 return false; 1703 1704 eStatus = ::ATSFontActivateFromFileSpecification( &aPathFSSpec, 1705 eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, 1706 &aATSFontContainer ); 1707 #endif 1708 if( eStatus != noErr ) 1709 return false; 1710 1711 return true; 1712 } 1713 1714 static bool AddLocalTempFontDirs( void ) 1715 { 1716 static bool bFirst = true; 1717 if( !bFirst ) 1718 return false; 1719 bFirst = false; 1720 1721 // add private font files found in office base dir 1722 1723 // rtl::OUString aBrandStr( RTL_CONSTASCII_USTRINGPARAM( "$OOO_BASE_DIR" ) ); 1724 // rtl_bootstrap_expandMacros( &aBrandStr.pData ); 1725 // rtl::OUString aBrandSysPath; 1726 // OSL_VERIFY( osl_getSystemPathFromFileURL( aBrandStr.pData, &aBrandSysPath.pData ) == osl_File_E_None ); 1727 1728 // rtl::OStringBuffer aBrandFontDir( aBrandSysPath.getLength()*2 ); 1729 // aBrandFontDir.append( rtl::OUStringToOString( aBrandSysPath, RTL_TEXTENCODING_UTF8 ) ); 1730 // aBrandFontDir.append( "/share/fonts/truetype/" ); 1731 // bool bBrandSuccess = AddTempFontDir( aBrandFontDir.getStr() ); 1732 1733 rtl::OUString aBaseStr( RTL_CONSTASCII_USTRINGPARAM( "$OOO_BASE_DIR" ) ); 1734 rtl_bootstrap_expandMacros( &aBaseStr.pData ); 1735 rtl::OUString aBaseSysPath; 1736 OSL_VERIFY( osl_getSystemPathFromFileURL( aBaseStr.pData, &aBaseSysPath.pData ) == osl_File_E_None ); 1737 1738 rtl::OStringBuffer aBaseFontDir( aBaseSysPath.getLength()*2 ); 1739 aBaseFontDir.append( rtl::OUStringToOString( aBaseSysPath, RTL_TEXTENCODING_UTF8 ) ); 1740 aBaseFontDir.append( "/share/fonts/truetype/" ); 1741 bool bBaseSuccess = AddTempFontDir( aBaseFontDir.getStr() ); 1742 1743 // return bBrandSuccess && bBaseSuccess; 1744 return bBaseSuccess; 1745 } 1746 1747 void AquaSalGraphics::GetDevFontList( ImplDevFontList* pFontList ) 1748 { 1749 DBG_ASSERT( pFontList, "AquaSalGraphics::GetDevFontList(NULL) !"); 1750 1751 AddLocalTempFontDirs(); 1752 1753 // The idea is to cache the list of system fonts once it has been generated. 1754 // SalData seems to be a good place for this caching. However we have to 1755 // carefully make the access to the font list thread-safe. If we register 1756 // a font-change event handler to update the font list in case fonts have 1757 // changed on the system we have to lock access to the list. The right 1758 // way to do that is the solar mutex since GetDevFontList is protected 1759 // through it as should be all event handlers 1760 1761 SalData* pSalData = GetSalData(); 1762 if (pSalData->mpFontList == NULL) 1763 pSalData->mpFontList = new SystemFontList(); 1764 1765 // Copy all ImplFontData objects contained in the SystemFontList 1766 pSalData->mpFontList->AnnounceFonts( *pFontList ); 1767 } 1768 1769 // ----------------------------------------------------------------------- 1770 1771 bool AquaSalGraphics::AddTempDevFont( ImplDevFontList*, 1772 const String& rFontFileURL, const String& /*rFontName*/ ) 1773 { 1774 ::rtl::OUString aUSytemPath; 1775 OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFontFileURL, aUSytemPath ) ); 1776 1777 FSRef aNewRef; 1778 Boolean bIsDirectory = true; 1779 ::rtl::OString aCFileName = rtl::OUStringToOString( aUSytemPath, RTL_TEXTENCODING_UTF8 ); 1780 OSStatus eStatus = FSPathMakeRef( (UInt8*)aCFileName.getStr(), &aNewRef, &bIsDirectory ); 1781 DBG_ASSERT( (eStatus==noErr) && !bIsDirectory, "vcl AddTempDevFont() with invalid fontfile name!" ); 1782 if( eStatus != noErr ) 1783 return false; 1784 1785 ATSFontContainerRef oContainer; 1786 1787 const ATSFontContext eContext = kATSFontContextLocal; // TODO: *Global??? 1788 #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) 1789 eStatus = ::ATSFontActivateFromFileReference( &aNewRef, 1790 eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, 1791 &oContainer ); 1792 #else 1793 FSSpec aFontFSSpec; 1794 eStatus = ::FSGetCatalogInfo( &aNewRef, kFSCatInfoNone, 1795 NULL, NULL, &aFontFSSpec, NULL ); 1796 if( eStatus != noErr ) 1797 return false; 1798 1799 eStatus = ::ATSFontActivateFromFileSpecification( &aFontFSSpec, 1800 eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, 1801 &oContainer ); 1802 #endif 1803 if( eStatus != noErr ) 1804 return false; 1805 1806 // TODO: ATSFontDeactivate( oContainer ) when fonts are no longer needed 1807 // TODO: register new ImplMacFontdata in pFontList 1808 return true; 1809 } 1810 1811 // ----------------------------------------------------------------------- 1812 1813 // callbacks from ATSUGlyphGetCubicPaths() fore GetGlyphOutline() 1814 struct GgoData { basegfx::B2DPolygon maPolygon; basegfx::B2DPolyPolygon* mpPolyPoly; }; 1815 1816 static OSStatus GgoLineToProc( const Float32Point* pPoint, void* pData ) 1817 { 1818 basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon; 1819 const basegfx::B2DPoint aB2DPoint( pPoint->x, pPoint->y ); 1820 rPolygon.append( aB2DPoint ); 1821 return noErr; 1822 } 1823 1824 static OSStatus GgoCurveToProc( const Float32Point* pCP1, const Float32Point* pCP2, 1825 const Float32Point* pPoint, void* pData ) 1826 { 1827 basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon; 1828 const sal_uInt32 nPointCount = rPolygon.count(); 1829 const basegfx::B2DPoint aB2DControlPoint1( pCP1->x, pCP1->y ); 1830 rPolygon.setNextControlPoint( nPointCount-1, aB2DControlPoint1 ); 1831 const basegfx::B2DPoint aB2DEndPoint( pPoint->x, pPoint->y ); 1832 rPolygon.append( aB2DEndPoint ); 1833 const basegfx::B2DPoint aB2DControlPoint2( pCP2->x, pCP2->y ); 1834 rPolygon.setPrevControlPoint( nPointCount, aB2DControlPoint2 ); 1835 return noErr; 1836 } 1837 1838 static OSStatus GgoClosePathProc( void* pData ) 1839 { 1840 GgoData* pGgoData = static_cast<GgoData*>(pData); 1841 basegfx::B2DPolygon& rPolygon = pGgoData->maPolygon; 1842 if( rPolygon.count() > 0 ) 1843 pGgoData->mpPolyPoly->append( rPolygon ); 1844 rPolygon.clear(); 1845 return noErr; 1846 } 1847 1848 static OSStatus GgoMoveToProc( const Float32Point* pPoint, void* pData ) 1849 { 1850 GgoClosePathProc( pData ); 1851 OSStatus eStatus = GgoLineToProc( pPoint, pData ); 1852 return eStatus; 1853 } 1854 1855 bool AquaSalGraphics::GetGlyphOutline( sal_GlyphId aGlyphId, basegfx::B2DPolyPolygon& rPolyPoly ) 1856 { 1857 GgoData aGgoData; 1858 aGgoData.mpPolyPoly = &rPolyPoly; 1859 rPolyPoly.clear(); 1860 1861 ATSUStyle rATSUStyle = maATSUStyle; // TODO: handle glyph fallback when CWS pdffix02 is integrated 1862 OSStatus eGgoStatus = noErr; 1863 OSStatus eStatus = ATSUGlyphGetCubicPaths( rATSUStyle, aGlyphId, 1864 GgoMoveToProc, GgoLineToProc, GgoCurveToProc, GgoClosePathProc, 1865 &aGgoData, &eGgoStatus ); 1866 if( (eStatus != noErr) ) // TODO: why is (eGgoStatus!=noErr) when curves are involved? 1867 return false; 1868 1869 GgoClosePathProc( &aGgoData ); 1870 if( mfFontScale != 1.0 ) { 1871 rPolyPoly.transform(basegfx::tools::createScaleB2DHomMatrix(+mfFontScale, +mfFontScale)); 1872 } 1873 return true; 1874 } 1875 1876 // ----------------------------------------------------------------------- 1877 1878 long AquaSalGraphics::GetGraphicsWidth() const 1879 { 1880 long w = 0; 1881 if( mrContext && (mbWindow || mbVirDev) ) 1882 { 1883 w = mnWidth; 1884 } 1885 1886 if( w == 0 ) 1887 { 1888 if( mbWindow && mpFrame ) 1889 w = mpFrame->maGeometry.nWidth; 1890 } 1891 1892 return w; 1893 } 1894 1895 // ----------------------------------------------------------------------- 1896 1897 bool AquaSalGraphics::GetGlyphBoundRect( sal_GlyphId aGlyphId, Rectangle& rRect ) 1898 { 1899 ATSUStyle rATSUStyle = maATSUStyle; // TODO: handle glyph fallback 1900 ATSGlyphScreenMetrics aGlyphMetrics; 1901 GlyphID nAtsGlyphId = aGlyphId; 1902 OSStatus eStatus = ATSUGlyphGetScreenMetrics( rATSUStyle, 1903 1, &nAtsGlyphId, 0, FALSE, !mbNonAntialiasedText, &aGlyphMetrics ); 1904 if( eStatus != noErr ) 1905 return false; 1906 1907 const long nMinX = (long)(+aGlyphMetrics.topLeft.x * mfFontScale - 0.5); 1908 const long nMaxX = (long)(aGlyphMetrics.width * mfFontScale + 0.5) + nMinX; 1909 const long nMinY = (long)(-aGlyphMetrics.topLeft.y * mfFontScale - 0.5); 1910 const long nMaxY = (long)(aGlyphMetrics.height * mfFontScale + 0.5) + nMinY; 1911 rRect = Rectangle( nMinX, nMinY, nMaxX, nMaxY ); 1912 return true; 1913 } 1914 1915 // ----------------------------------------------------------------------- 1916 1917 void AquaSalGraphics::GetDevFontSubstList( OutputDevice* ) 1918 { 1919 // nothing to do since there are no device-specific fonts on Aqua 1920 } 1921 1922 // ----------------------------------------------------------------------- 1923 1924 void AquaSalGraphics::DrawServerFontLayout( const ServerFontLayout& ) 1925 { 1926 } 1927 1928 // ----------------------------------------------------------------------- 1929 1930 sal_uInt16 AquaSalGraphics::SetFont( ImplFontSelectData* pReqFont, int /*nFallbackLevel*/ ) 1931 { 1932 if( !pReqFont ) 1933 { 1934 ATSUClearStyle( maATSUStyle ); 1935 mpMacFontData = NULL; 1936 return 0; 1937 } 1938 1939 // store the requested device font entry 1940 const ImplMacFontData* pMacFont = static_cast<const ImplMacFontData*>( pReqFont->mpFontData ); 1941 mpMacFontData = pMacFont; 1942 1943 // convert pixel units (as seen by upper layers) to typographic point units 1944 double fScaledAtsHeight = pReqFont->mfExactHeight; 1945 // avoid Fixed16.16 overflows by limiting the ATS font size 1946 static const float fMaxAtsHeight = 144.0; 1947 if( fScaledAtsHeight <= fMaxAtsHeight ) 1948 mfFontScale = 1.0; 1949 else 1950 { 1951 mfFontScale = fScaledAtsHeight / fMaxAtsHeight; 1952 fScaledAtsHeight = fMaxAtsHeight; 1953 } 1954 Fixed fFixedSize = FloatToFixed( fScaledAtsHeight ); 1955 // enable bold-emulation if needed 1956 Boolean bFakeBold = FALSE; 1957 if( (pReqFont->GetWeight() >= WEIGHT_BOLD) 1958 && (pMacFont->GetWeight() < WEIGHT_SEMIBOLD) ) 1959 bFakeBold = TRUE; 1960 // enable italic-emulation if needed 1961 Boolean bFakeItalic = FALSE; 1962 if( ((pReqFont->GetSlant() == ITALIC_NORMAL) || (pReqFont->GetSlant() == ITALIC_OBLIQUE)) 1963 && !((pMacFont->GetSlant() == ITALIC_NORMAL) || (pMacFont->GetSlant() == ITALIC_OBLIQUE)) ) 1964 bFakeItalic = TRUE; 1965 1966 // enable/disable antialiased text 1967 mbNonAntialiasedText = pReqFont->mbNonAntialiased; 1968 UInt32 nStyleRenderingOptions = kATSStyleNoOptions; 1969 if( pReqFont->mbNonAntialiased ) 1970 nStyleRenderingOptions |= kATSStyleNoAntiAliasing; 1971 1972 // set horizontal/vertical mode 1973 ATSUVerticalCharacterType aVerticalCharacterType = kATSUStronglyHorizontal; 1974 if( pReqFont->mbVertical ) 1975 aVerticalCharacterType = kATSUStronglyVertical; 1976 1977 // prepare ATS-fontid as type matching to the kATSUFontTag request 1978 ATSUFontID nFontID = static_cast<ATSUFontID>(pMacFont->GetFontId()); 1979 1980 // update ATSU style attributes with requested font parameters 1981 // TODO: no need to set styles which are already defaulted 1982 1983 const ATSUAttributeTag aTag[] = 1984 { 1985 kATSUFontTag, 1986 kATSUSizeTag, 1987 kATSUQDBoldfaceTag, 1988 kATSUQDItalicTag, 1989 kATSUStyleRenderingOptionsTag, 1990 kATSUVerticalCharacterTag 1991 }; 1992 1993 const ByteCount aValueSize[] = 1994 { 1995 sizeof(ATSUFontID), 1996 sizeof(fFixedSize), 1997 sizeof(bFakeBold), 1998 sizeof(bFakeItalic), 1999 sizeof(nStyleRenderingOptions), 2000 sizeof(aVerticalCharacterType) 2001 }; 2002 2003 const ATSUAttributeValuePtr aValue[] = 2004 { 2005 &nFontID, 2006 &fFixedSize, 2007 &bFakeBold, 2008 &bFakeItalic, 2009 &nStyleRenderingOptions, 2010 &aVerticalCharacterType 2011 }; 2012 2013 static const int nTagCount = sizeof(aTag) / sizeof(*aTag); 2014 OSStatus eStatus = ATSUSetAttributes( maATSUStyle, nTagCount, 2015 aTag, aValueSize, aValue ); 2016 // reset ATSUstyle if there was an error 2017 if( eStatus != noErr ) 2018 { 2019 DBG_WARNING( "AquaSalGraphics::SetFont() : Could not set font attributes!\n"); 2020 ATSUClearStyle( maATSUStyle ); 2021 mpMacFontData = NULL; 2022 return 0; 2023 } 2024 2025 // prepare font stretching 2026 const ATSUAttributeTag aMatrixTag = kATSUFontMatrixTag; 2027 if( (pReqFont->mnWidth == 0) || (pReqFont->mnWidth == pReqFont->mnHeight) ) 2028 { 2029 mfFontStretch = 1.0; 2030 ATSUClearAttributes( maATSUStyle, 1, &aMatrixTag ); 2031 } 2032 else 2033 { 2034 mfFontStretch = (float)pReqFont->mnWidth / pReqFont->mnHeight; 2035 CGAffineTransform aMatrix = CGAffineTransformMakeScale( mfFontStretch, 1.0F ); 2036 const ATSUAttributeValuePtr aAttr = &aMatrix; 2037 const ByteCount aMatrixBytes = sizeof(aMatrix); 2038 eStatus = ATSUSetAttributes( maATSUStyle, 1, &aMatrixTag, &aMatrixBytes, &aAttr ); 2039 DBG_ASSERT( (eStatus==noErr), "AquaSalGraphics::SetFont() : Could not set font matrix\n"); 2040 } 2041 2042 // prepare font rotation 2043 mnATSUIRotation = FloatToFixed( pReqFont->mnOrientation / 10.0 ); 2044 2045 #if OSL_DEBUG_LEVEL > 3 2046 fprintf( stderr, "SetFont to (\"%s\", \"%s\", fontid=%d) for (\"%s\" \"%s\" weight=%d, slant=%d size=%dx%d orientation=%d)\n", 2047 ::rtl::OUStringToOString( pMacFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ).getStr(), 2048 ::rtl::OUStringToOString( pMacFont->GetStyleName(), RTL_TEXTENCODING_UTF8 ).getStr(), 2049 (int)nFontID, 2050 ::rtl::OUStringToOString( pReqFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ).getStr(), 2051 ::rtl::OUStringToOString( pReqFont->GetStyleName(), RTL_TEXTENCODING_UTF8 ).getStr(), 2052 pReqFont->GetWeight(), 2053 pReqFont->GetSlant(), 2054 pReqFont->mnHeight, 2055 pReqFont->mnWidth, 2056 pReqFont->mnOrientation); 2057 #endif 2058 2059 return 0; 2060 } 2061 2062 // ----------------------------------------------------------------------- 2063 2064 const ImplFontCharMap* AquaSalGraphics::GetImplFontCharMap() const 2065 { 2066 if( !mpMacFontData ) 2067 return ImplFontCharMap::GetDefaultMap(); 2068 2069 return mpMacFontData->GetImplFontCharMap(); 2070 } 2071 2072 // ----------------------------------------------------------------------- 2073 2074 // fake a SFNT font directory entry for a font table 2075 // see http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html#Directory 2076 static void FakeDirEntry( FourCharCode eFCC, ByteCount nOfs, ByteCount nLen, 2077 const unsigned char* /*pData*/, unsigned char*& rpDest ) 2078 { 2079 // write entry tag 2080 rpDest[ 0] = (char)(eFCC >> 24); 2081 rpDest[ 1] = (char)(eFCC >> 16); 2082 rpDest[ 2] = (char)(eFCC >> 8); 2083 rpDest[ 3] = (char)(eFCC >> 0); 2084 // TODO: get entry checksum and write it 2085 // not too important since the subsetter doesn't care currently 2086 // for( pData+nOfs ... pData+nOfs+nLen ) 2087 // write entry offset 2088 rpDest[ 8] = (char)(nOfs >> 24); 2089 rpDest[ 9] = (char)(nOfs >> 16); 2090 rpDest[10] = (char)(nOfs >> 8); 2091 rpDest[11] = (char)(nOfs >> 0); 2092 // write entry length 2093 rpDest[12] = (char)(nLen >> 24); 2094 rpDest[13] = (char)(nLen >> 16); 2095 rpDest[14] = (char)(nLen >> 8); 2096 rpDest[15] = (char)(nLen >> 0); 2097 // advance to next entry 2098 rpDest += 16; 2099 } 2100 2101 static bool GetRawFontData( const ImplFontData* pFontData, 2102 ByteVector& rBuffer, bool* pJustCFF ) 2103 { 2104 const ImplMacFontData* pMacFont = static_cast<const ImplMacFontData*>(pFontData); 2105 const ATSUFontID nFontId = static_cast<ATSUFontID>(pMacFont->GetFontId()); 2106 ATSFontRef rFont = FMGetATSFontRefFromFont( nFontId ); 2107 2108 ByteCount nCffLen = 0; 2109 OSStatus eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, 0, NULL, &nCffLen); 2110 if( pJustCFF != NULL ) 2111 { 2112 *pJustCFF = (eStatus == noErr) && (nCffLen > 0); 2113 if( *pJustCFF ) 2114 { 2115 rBuffer.resize( nCffLen ); 2116 eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, nCffLen, (void*)&rBuffer[0], &nCffLen); 2117 if( (eStatus != noErr) || (nCffLen <= 0) ) 2118 return false; 2119 return true; 2120 } 2121 } 2122 2123 // get font table availability and size in bytes 2124 ByteCount nHeadLen = 0; 2125 eStatus = ATSFontGetTable( rFont, GetTag("head"), 0, 0, NULL, &nHeadLen); 2126 if( (eStatus != noErr) || (nHeadLen <= 0) ) 2127 return false; 2128 ByteCount nMaxpLen = 0; 2129 eStatus = ATSFontGetTable( rFont, GetTag("maxp"), 0, 0, NULL, &nMaxpLen); 2130 if( (eStatus != noErr) || (nMaxpLen <= 0) ) 2131 return false; 2132 ByteCount nCmapLen = 0; 2133 eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, 0, NULL, &nCmapLen); 2134 if( (eStatus != noErr) || (nCmapLen <= 0) ) 2135 return false; 2136 ByteCount nNameLen = 0; 2137 eStatus = ATSFontGetTable( rFont, GetTag("name"), 0, 0, NULL, &nNameLen); 2138 if( (eStatus != noErr) || (nNameLen <= 0) ) 2139 return false; 2140 ByteCount nHheaLen = 0; 2141 eStatus = ATSFontGetTable( rFont, GetTag("hhea"), 0, 0, NULL, &nHheaLen); 2142 if( (eStatus != noErr) || (nHheaLen <= 0) ) 2143 return false; 2144 ByteCount nHmtxLen = 0; 2145 eStatus = ATSFontGetTable( rFont, GetTag("hmtx"), 0, 0, NULL, &nHmtxLen); 2146 if( (eStatus != noErr) || (nHmtxLen <= 0) ) 2147 return false; 2148 2149 // get the glyph outline tables 2150 ByteCount nLocaLen = 0; 2151 ByteCount nGlyfLen = 0; 2152 if( (eStatus != noErr) || (nCffLen <= 0) ) 2153 { 2154 eStatus = ATSFontGetTable( rFont, GetTag("loca"), 0, 0, NULL, &nLocaLen); 2155 if( (eStatus != noErr) || (nLocaLen <= 0) ) 2156 return false; 2157 eStatus = ATSFontGetTable( rFont, GetTag("glyf"), 0, 0, NULL, &nGlyfLen); 2158 if( (eStatus != noErr) || (nGlyfLen <= 0) ) 2159 return false; 2160 } 2161 2162 ByteCount nPrepLen=0, nCvtLen=0, nFpgmLen=0; 2163 if( nGlyfLen ) // TODO: reduce PDF size by making hint subsetting optional 2164 { 2165 eStatus = ATSFontGetTable( rFont, GetTag("prep"), 0, 0, NULL, &nPrepLen); 2166 eStatus = ATSFontGetTable( rFont, GetTag("cvt "), 0, 0, NULL, &nCvtLen); 2167 eStatus = ATSFontGetTable( rFont, GetTag("fpgm"), 0, 0, NULL, &nFpgmLen); 2168 } 2169 2170 // prepare a byte buffer for a fake font 2171 int nTableCount = 7; 2172 nTableCount += (nPrepLen>0) + (nCvtLen>0) + (nFpgmLen>0) + (nGlyfLen>0); 2173 const ByteCount nFdirLen = 12 + 16*nTableCount; 2174 ByteCount nTotalLen = nFdirLen; 2175 nTotalLen += nHeadLen + nMaxpLen + nNameLen + nCmapLen; 2176 if( nGlyfLen ) 2177 nTotalLen += nLocaLen + nGlyfLen; 2178 else 2179 nTotalLen += nCffLen; 2180 nTotalLen += nHheaLen + nHmtxLen; 2181 nTotalLen += nPrepLen + nCvtLen + nFpgmLen; 2182 rBuffer.resize( nTotalLen ); 2183 2184 // fake a SFNT font directory header 2185 if( nTableCount < 16 ) 2186 { 2187 int nLog2 = 0; 2188 while( (nTableCount >> nLog2) > 1 ) ++nLog2; 2189 rBuffer[ 1] = 1; // Win-TTF style scaler 2190 rBuffer[ 5] = nTableCount; // table count 2191 rBuffer[ 7] = nLog2*16; // searchRange 2192 rBuffer[ 9] = nLog2; // entrySelector 2193 rBuffer[11] = (nTableCount-nLog2)*16; // rangeShift 2194 } 2195 2196 // get font table raw data and update the fake directory entries 2197 ByteCount nOfs = nFdirLen; 2198 unsigned char* pFakeEntry = &rBuffer[12]; 2199 eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, nCmapLen, (void*)&rBuffer[nOfs], &nCmapLen); 2200 FakeDirEntry( GetTag("cmap"), nOfs, nCmapLen, &rBuffer[0], pFakeEntry ); 2201 nOfs += nCmapLen; 2202 if( nCvtLen ) { 2203 eStatus = ATSFontGetTable( rFont, GetTag("cvt "), 0, nCvtLen, (void*)&rBuffer[nOfs], &nCvtLen); 2204 FakeDirEntry( GetTag("cvt "), nOfs, nCvtLen, &rBuffer[0], pFakeEntry ); 2205 nOfs += nCvtLen; 2206 } 2207 if( nFpgmLen ) { 2208 eStatus = ATSFontGetTable( rFont, GetTag("fpgm"), 0, nFpgmLen, (void*)&rBuffer[nOfs], &nFpgmLen); 2209 FakeDirEntry( GetTag("fpgm"), nOfs, nFpgmLen, &rBuffer[0], pFakeEntry ); 2210 nOfs += nFpgmLen; 2211 } 2212 if( nCffLen ) { 2213 eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, nCffLen, (void*)&rBuffer[nOfs], &nCffLen); 2214 FakeDirEntry( GetTag("CFF "), nOfs, nCffLen, &rBuffer[0], pFakeEntry ); 2215 nOfs += nGlyfLen; 2216 } else { 2217 eStatus = ATSFontGetTable( rFont, GetTag("glyf"), 0, nGlyfLen, (void*)&rBuffer[nOfs], &nGlyfLen); 2218 FakeDirEntry( GetTag("glyf"), nOfs, nGlyfLen, &rBuffer[0], pFakeEntry ); 2219 nOfs += nGlyfLen; 2220 eStatus = ATSFontGetTable( rFont, GetTag("loca"), 0, nLocaLen, (void*)&rBuffer[nOfs], &nLocaLen); 2221 FakeDirEntry( GetTag("loca"), nOfs, nLocaLen, &rBuffer[0], pFakeEntry ); 2222 nOfs += nLocaLen; 2223 } 2224 eStatus = ATSFontGetTable( rFont, GetTag("head"), 0, nHeadLen, (void*)&rBuffer[nOfs], &nHeadLen); 2225 FakeDirEntry( GetTag("head"), nOfs, nHeadLen, &rBuffer[0], pFakeEntry ); 2226 nOfs += nHeadLen; 2227 eStatus = ATSFontGetTable( rFont, GetTag("hhea"), 0, nHheaLen, (void*)&rBuffer[nOfs], &nHheaLen); 2228 FakeDirEntry( GetTag("hhea"), nOfs, nHheaLen, &rBuffer[0], pFakeEntry ); 2229 nOfs += nHheaLen; 2230 eStatus = ATSFontGetTable( rFont, GetTag("hmtx"), 0, nHmtxLen, (void*)&rBuffer[nOfs], &nHmtxLen); 2231 FakeDirEntry( GetTag("hmtx"), nOfs, nHmtxLen, &rBuffer[0], pFakeEntry ); 2232 nOfs += nHmtxLen; 2233 eStatus = ATSFontGetTable( rFont, GetTag("maxp"), 0, nMaxpLen, (void*)&rBuffer[nOfs], &nMaxpLen); 2234 FakeDirEntry( GetTag("maxp"), nOfs, nMaxpLen, &rBuffer[0], pFakeEntry ); 2235 nOfs += nMaxpLen; 2236 eStatus = ATSFontGetTable( rFont, GetTag("name"), 0, nNameLen, (void*)&rBuffer[nOfs], &nNameLen); 2237 FakeDirEntry( GetTag("name"), nOfs, nNameLen, &rBuffer[0], pFakeEntry ); 2238 nOfs += nNameLen; 2239 if( nPrepLen ) { 2240 eStatus = ATSFontGetTable( rFont, GetTag("prep"), 0, nPrepLen, (void*)&rBuffer[nOfs], &nPrepLen); 2241 FakeDirEntry( GetTag("prep"), nOfs, nPrepLen, &rBuffer[0], pFakeEntry ); 2242 nOfs += nPrepLen; 2243 } 2244 2245 DBG_ASSERT( (nOfs==nTotalLen), "AquaSalGraphics::CreateFontSubset (nOfs!=nTotalLen)"); 2246 2247 return sal_True; 2248 } 2249 2250 sal_Bool AquaSalGraphics::CreateFontSubset( const rtl::OUString& rToFile, 2251 const ImplFontData* pFontData, sal_GlyphId* pGlyphIds, sal_uInt8* pEncoding, 2252 sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rInfo ) 2253 { 2254 // TODO: move more of the functionality here into the generic subsetter code 2255 2256 // prepare the requested file name for writing the font-subset file 2257 rtl::OUString aSysPath; 2258 if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) ) 2259 return sal_False; 2260 const rtl_TextEncoding aThreadEncoding = osl_getThreadTextEncoding(); 2261 const ByteString aToFile( rtl::OUStringToOString( aSysPath, aThreadEncoding ) ); 2262 2263 // get the raw-bytes from the font to be subset 2264 ByteVector aBuffer; 2265 bool bCffOnly = false; 2266 if( !GetRawFontData( pFontData, aBuffer, &bCffOnly ) ) 2267 return sal_False; 2268 2269 // handle CFF-subsetting 2270 if( bCffOnly ) 2271 { 2272 // provide the raw-CFF data to the subsetter 2273 ByteCount nCffLen = aBuffer.size(); 2274 rInfo.LoadFont( FontSubsetInfo::CFF_FONT, &aBuffer[0], nCffLen ); 2275 2276 // NOTE: assuming that all glyphids requested on Aqua are fully translated 2277 2278 // make the subsetter provide the requested subset 2279 FILE* pOutFile = fopen( aToFile.GetBuffer(), "wb" ); 2280 bool bRC = rInfo.CreateFontSubset( FontSubsetInfo::TYPE1_PFB, pOutFile, NULL, 2281 pGlyphIds, pEncoding, nGlyphCount, pGlyphWidths ); 2282 fclose( pOutFile ); 2283 return bRC; 2284 } 2285 2286 // TODO: modernize psprint's horrible fontsubset C-API 2287 // this probably only makes sense after the switch to another SCM 2288 // that can preserve change history after file renames 2289 2290 // prepare data for psprint's font subsetter 2291 TrueTypeFont* pSftFont = NULL; 2292 int nRC = ::OpenTTFontBuffer( (void*)&aBuffer[0], aBuffer.size(), 0, &pSftFont); 2293 if( nRC != SF_OK ) 2294 return sal_False; 2295 2296 // get details about the subsetted font 2297 TTGlobalFontInfo aTTInfo; 2298 ::GetTTGlobalFontInfo( pSftFont, &aTTInfo ); 2299 rInfo.m_nFontType = FontSubsetInfo::SFNT_TTF; 2300 rInfo.m_aPSName = String( aTTInfo.psname, RTL_TEXTENCODING_UTF8 ); 2301 rInfo.m_aFontBBox = Rectangle( Point( aTTInfo.xMin, aTTInfo.yMin ), 2302 Point( aTTInfo.xMax, aTTInfo.yMax ) ); 2303 rInfo.m_nCapHeight = aTTInfo.yMax; // Well ... 2304 rInfo.m_nAscent = aTTInfo.winAscent; 2305 rInfo.m_nDescent = aTTInfo.winDescent; 2306 // mac fonts usually do not have an OS2-table 2307 // => get valid ascent/descent values from other tables 2308 if( !rInfo.m_nAscent ) 2309 rInfo.m_nAscent = +aTTInfo.typoAscender; 2310 if( !rInfo.m_nAscent ) 2311 rInfo.m_nAscent = +aTTInfo.ascender; 2312 if( !rInfo.m_nDescent ) 2313 rInfo.m_nDescent = +aTTInfo.typoDescender; 2314 if( !rInfo.m_nDescent ) 2315 rInfo.m_nDescent = -aTTInfo.descender; 2316 2317 // subset glyphs and get their properties 2318 // take care that subset fonts require the NotDef glyph in pos 0 2319 int nOrigCount = nGlyphCount; 2320 sal_uInt16 aShortIDs[ 256 ]; 2321 sal_uInt8 aTempEncs[ 256 ]; 2322 2323 int nNotDef = -1; 2324 for( int i = 0; i < nGlyphCount; ++i ) 2325 { 2326 aTempEncs[i] = pEncoding[i]; 2327 sal_GlyphId aGlyphId( pGlyphIds[i] & GF_IDXMASK); 2328 if( pGlyphIds[i] & GF_ISCHAR ) 2329 { 2330 bool bVertical = (pGlyphIds[i] & GF_ROTMASK) != 0; 2331 aGlyphId = ::MapChar( pSftFont, static_cast<sal_uInt16>(aGlyphId), bVertical ); 2332 if( aGlyphId == 0 && pFontData->IsSymbolFont() ) 2333 { 2334 // #i12824# emulate symbol aliasing U+FXXX <-> U+0XXX 2335 aGlyphId = pGlyphIds[i] & GF_IDXMASK; 2336 aGlyphId = (aGlyphId & 0xF000) ? (aGlyphId & 0x00FF) : (aGlyphId | 0xF000 ); 2337 aGlyphId = ::MapChar( pSftFont, static_cast<sal_uInt16>(aGlyphId), bVertical ); 2338 } 2339 } 2340 aShortIDs[i] = static_cast<sal_uInt16>( aGlyphId ); 2341 if( !aGlyphId ) 2342 if( nNotDef < 0 ) 2343 nNotDef = i; // first NotDef glyph found 2344 } 2345 2346 if( nNotDef != 0 ) 2347 { 2348 // add fake NotDef glyph if needed 2349 if( nNotDef < 0 ) 2350 nNotDef = nGlyphCount++; 2351 2352 // NotDef glyph must be in pos 0 => swap glyphids 2353 aShortIDs[ nNotDef ] = aShortIDs[0]; 2354 aTempEncs[ nNotDef ] = aTempEncs[0]; 2355 aShortIDs[0] = 0; 2356 aTempEncs[0] = 0; 2357 } 2358 DBG_ASSERT( nGlyphCount < 257, "too many glyphs for subsetting" ); 2359 2360 // TODO: where to get bVertical? 2361 const bool bVertical = false; 2362 2363 // fill the pGlyphWidths array 2364 // while making sure that the NotDef glyph is at index==0 2365 TTSimpleGlyphMetrics* pGlyphMetrics = 2366 ::GetTTSimpleGlyphMetrics( pSftFont, aShortIDs, nGlyphCount, bVertical ); 2367 if( !pGlyphMetrics ) 2368 return sal_False; 2369 sal_uInt16 nNotDefAdv = pGlyphMetrics[0].adv; 2370 pGlyphMetrics[0].adv = pGlyphMetrics[nNotDef].adv; 2371 pGlyphMetrics[nNotDef].adv = nNotDefAdv; 2372 for( int i = 0; i < nOrigCount; ++i ) 2373 pGlyphWidths[i] = pGlyphMetrics[i].adv; 2374 free( pGlyphMetrics ); 2375 2376 // write subset into destination file 2377 nRC = ::CreateTTFromTTGlyphs( pSftFont, aToFile.GetBuffer(), aShortIDs, 2378 aTempEncs, nGlyphCount, 0, NULL, 0 ); 2379 ::CloseTTFont(pSftFont); 2380 return (nRC == SF_OK); 2381 } 2382 2383 // ----------------------------------------------------------------------- 2384 2385 void AquaSalGraphics::GetGlyphWidths( const ImplFontData* pFontData, bool bVertical, 2386 Int32Vector& rGlyphWidths, Ucs2UIntMap& rUnicodeEnc ) 2387 { 2388 rGlyphWidths.clear(); 2389 rUnicodeEnc.clear(); 2390 2391 if( pFontData->IsSubsettable() ) 2392 { 2393 ByteVector aBuffer; 2394 if( !GetRawFontData( pFontData, aBuffer, NULL ) ) 2395 return; 2396 2397 // TODO: modernize psprint's horrible fontsubset C-API 2398 // this probably only makes sense after the switch to another SCM 2399 // that can preserve change history after file renames 2400 2401 // use the font subsetter to get the widths 2402 TrueTypeFont* pSftFont = NULL; 2403 int nRC = ::OpenTTFontBuffer( (void*)&aBuffer[0], aBuffer.size(), 0, &pSftFont); 2404 if( nRC != SF_OK ) 2405 return; 2406 2407 const int nGlyphCount = ::GetTTGlyphCount( pSftFont ); 2408 if( nGlyphCount > 0 ) 2409 { 2410 // get glyph metrics 2411 rGlyphWidths.resize(nGlyphCount); 2412 std::vector<sal_uInt16> aGlyphIds(nGlyphCount); 2413 for( int i = 0; i < nGlyphCount; i++ ) 2414 aGlyphIds[i] = static_cast<sal_uInt16>(i); 2415 const TTSimpleGlyphMetrics* pGlyphMetrics = ::GetTTSimpleGlyphMetrics( 2416 pSftFont, &aGlyphIds[0], nGlyphCount, bVertical ); 2417 if( pGlyphMetrics ) 2418 { 2419 for( int i = 0; i < nGlyphCount; ++i ) 2420 rGlyphWidths[i] = pGlyphMetrics[i].adv; 2421 free( (void*)pGlyphMetrics ); 2422 } 2423 2424 const ImplFontCharMap* pMap = mpMacFontData->GetImplFontCharMap(); 2425 DBG_ASSERT( pMap && pMap->GetCharCount(), "no charmap" ); 2426 pMap->AddReference(); // TODO: add and use RAII object instead 2427 2428 // get unicode<->glyph encoding 2429 // TODO? avoid sft mapping by using the pMap itself 2430 int nCharCount = pMap->GetCharCount(); 2431 sal_uInt32 nChar = pMap->GetFirstChar(); 2432 for(; --nCharCount >= 0; nChar = pMap->GetNextChar( nChar ) ) 2433 { 2434 if( nChar > 0xFFFF ) // TODO: allow UTF-32 chars 2435 break; 2436 sal_Ucs nUcsChar = static_cast<sal_Ucs>(nChar); 2437 sal_uInt32 nGlyph = ::MapChar( pSftFont, nUcsChar, bVertical ); 2438 if( nGlyph > 0 ) 2439 rUnicodeEnc[ nUcsChar ] = nGlyph; 2440 } 2441 2442 pMap->DeReference(); // TODO: add and use RAII object instead 2443 } 2444 2445 ::CloseTTFont( pSftFont ); 2446 } 2447 else if( pFontData->IsEmbeddable() ) 2448 { 2449 // get individual character widths 2450 #if 0 // FIXME 2451 rWidths.reserve( 224 ); 2452 for( sal_Unicode i = 32; i < 256; ++i ) 2453 { 2454 int nCharWidth = 0; 2455 if( ::GetCharWidth32W( mhDC, i, i, &nCharWidth ) ) 2456 { 2457 rUnicodeEnc[ i ] = rWidths.size(); 2458 rWidths.push_back( nCharWidth ); 2459 } 2460 } 2461 #else 2462 DBG_ERROR("not implemented for non-subsettable fonts!\n"); 2463 #endif 2464 } 2465 } 2466 2467 // ----------------------------------------------------------------------- 2468 2469 const Ucs2SIntMap* AquaSalGraphics::GetFontEncodingVector( 2470 const ImplFontData*, const Ucs2OStrMap** /*ppNonEncoded*/ ) 2471 { 2472 return NULL; 2473 } 2474 2475 // ----------------------------------------------------------------------- 2476 2477 const void* AquaSalGraphics::GetEmbedFontData( const ImplFontData*, 2478 const sal_Ucs* /*pUnicodes*/, 2479 sal_Int32* /*pWidths*/, 2480 FontSubsetInfo&, 2481 long* /*pDataLen*/ ) 2482 { 2483 return NULL; 2484 } 2485 2486 // ----------------------------------------------------------------------- 2487 2488 void AquaSalGraphics::FreeEmbedFontData( const void* pData, long /*nDataLen*/ ) 2489 { 2490 // TODO: implementing this only makes sense when the implementation of 2491 // AquaSalGraphics::GetEmbedFontData() returns non-NULL 2492 (void)pData; 2493 DBG_ASSERT( (pData!=NULL), "AquaSalGraphics::FreeEmbedFontData() is not implemented\n"); 2494 } 2495 2496 // ----------------------------------------------------------------------- 2497 2498 SystemFontData AquaSalGraphics::GetSysFontData( int /* nFallbacklevel */ ) const 2499 { 2500 SystemFontData aSysFontData; 2501 OSStatus err; 2502 aSysFontData.nSize = sizeof( SystemFontData ); 2503 2504 // NOTE: Native ATSU font fallbacks are used, not the VCL fallbacks. 2505 ATSUFontID fontId; 2506 err = ATSUGetAttribute( maATSUStyle, kATSUFontTag, sizeof(fontId), &fontId, 0 ); 2507 if (err) fontId = 0; 2508 aSysFontData.aATSUFontID = (void *) fontId; 2509 2510 Boolean bFbold; 2511 err = ATSUGetAttribute( maATSUStyle, kATSUQDBoldfaceTag, sizeof(bFbold), &bFbold, 0 ); 2512 if (err) bFbold = FALSE; 2513 aSysFontData.bFakeBold = (bool) bFbold; 2514 2515 Boolean bFItalic; 2516 err = ATSUGetAttribute( maATSUStyle, kATSUQDItalicTag, sizeof(bFItalic), &bFItalic, 0 ); 2517 if (err) bFItalic = FALSE; 2518 aSysFontData.bFakeItalic = (bool) bFItalic; 2519 2520 ATSUVerticalCharacterType aVerticalCharacterType; 2521 err = ATSUGetAttribute( maATSUStyle, kATSUVerticalCharacterTag, sizeof(aVerticalCharacterType), &aVerticalCharacterType, 0 ); 2522 if (!err && aVerticalCharacterType == kATSUStronglyVertical) { 2523 aSysFontData.bVerticalCharacterType = true; 2524 } else { 2525 aSysFontData.bVerticalCharacterType = false; 2526 } 2527 2528 aSysFontData.bAntialias = !mbNonAntialiasedText; 2529 2530 return aSysFontData; 2531 } 2532 2533 // ----------------------------------------------------------------------- 2534 2535 SystemGraphicsData AquaSalGraphics::GetGraphicsData() const 2536 { 2537 SystemGraphicsData aRes; 2538 aRes.nSize = sizeof(aRes); 2539 aRes.rCGContext = mrContext; 2540 return aRes; 2541 } 2542 2543 // ----------------------------------------------------------------------- 2544 2545 void AquaSalGraphics::SetXORMode( bool bSet, bool bInvertOnly ) 2546 { 2547 // return early if XOR mode remains unchanged 2548 if( mbPrinter ) 2549 return; 2550 2551 if( ! bSet && mnXorMode == 2 ) 2552 { 2553 CGContextSetBlendMode( mrContext, kCGBlendModeNormal ); 2554 mnXorMode = 0; 2555 return; 2556 } 2557 else if( bSet && bInvertOnly && mnXorMode == 0) 2558 { 2559 CGContextSetBlendMode( mrContext, kCGBlendModeDifference ); 2560 mnXorMode = 2; 2561 return; 2562 } 2563 2564 if( (mpXorEmulation == NULL) && !bSet ) 2565 return; 2566 if( (mpXorEmulation != NULL) && (bSet == mpXorEmulation->IsEnabled()) ) 2567 return; 2568 if( !CheckContext() ) 2569 return; 2570 2571 // prepare XOR emulation 2572 if( !mpXorEmulation ) 2573 { 2574 mpXorEmulation = new XorEmulation(); 2575 mpXorEmulation->SetTarget( mnWidth, mnHeight, mnBitmapDepth, mrContext, mxLayer ); 2576 } 2577 2578 // change the XOR mode 2579 if( bSet ) 2580 { 2581 mpXorEmulation->Enable(); 2582 mrContext = mpXorEmulation->GetMaskContext(); 2583 mnXorMode = 1; 2584 } 2585 else 2586 { 2587 mpXorEmulation->UpdateTarget(); 2588 mpXorEmulation->Disable(); 2589 mrContext = mpXorEmulation->GetTargetContext(); 2590 mnXorMode = 0; 2591 } 2592 } 2593 2594 // ----------------------------------------------------------------------- 2595 2596 // apply the XOR mask to the target context if active and dirty 2597 void AquaSalGraphics::ApplyXorContext() 2598 { 2599 if( !mpXorEmulation ) 2600 return; 2601 if( mpXorEmulation->UpdateTarget() ) 2602 RefreshRect( 0, 0, mnWidth, mnHeight ); // TODO: refresh minimal changerect 2603 } 2604 2605 // ====================================================================== 2606 2607 XorEmulation::XorEmulation() 2608 : mxTargetLayer( NULL ) 2609 , mxTargetContext( NULL ) 2610 , mxMaskContext( NULL ) 2611 , mxTempContext( NULL ) 2612 , mpMaskBuffer( NULL ) 2613 , mpTempBuffer( NULL ) 2614 , mnBufferLongs( 0 ) 2615 , mbIsEnabled( false ) 2616 {} 2617 2618 // ---------------------------------------------------------------------- 2619 2620 XorEmulation::~XorEmulation() 2621 { 2622 Disable(); 2623 SetTarget( 0, 0, 0, NULL, NULL ); 2624 } 2625 2626 // ----------------------------------------------------------------------- 2627 2628 void XorEmulation::SetTarget( int nWidth, int nHeight, int nTargetDepth, 2629 CGContextRef xTargetContext, CGLayerRef xTargetLayer ) 2630 { 2631 // prepare to replace old mask+temp context 2632 if( mxMaskContext ) 2633 { 2634 // cleanup the mask context 2635 CGContextRelease( mxMaskContext ); 2636 delete[] mpMaskBuffer; 2637 mxMaskContext = NULL; 2638 mpMaskBuffer = NULL; 2639 2640 // cleanup the temp context if needed 2641 if( mxTempContext ) 2642 { 2643 CGContextRelease( mxTempContext ); 2644 delete[] mpTempBuffer; 2645 mxTempContext = NULL; 2646 mpTempBuffer = NULL; 2647 } 2648 } 2649 2650 // return early if there is nothing more to do 2651 if( !xTargetContext ) 2652 return; 2653 2654 // retarget drawing operations to the XOR mask 2655 mxTargetLayer = xTargetLayer; 2656 mxTargetContext = xTargetContext; 2657 2658 // prepare creation of matching CGBitmaps 2659 CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace; 2660 CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst; 2661 int nBitDepth = nTargetDepth; 2662 if( !nBitDepth ) 2663 nBitDepth = 32; 2664 int nBytesPerRow = (nBitDepth == 16) ? 2 : 4; 2665 const size_t nBitsPerComponent = (nBitDepth == 16) ? 5 : 8; 2666 if( nBitDepth <= 8 ) 2667 { 2668 aCGColorSpace = GetSalData()->mxGraySpace; 2669 aCGBmpInfo = kCGImageAlphaNone; 2670 nBytesPerRow = 1; 2671 } 2672 nBytesPerRow *= nWidth; 2673 mnBufferLongs = (nHeight * nBytesPerRow + sizeof(sal_uLong)-1) / sizeof(sal_uLong); 2674 2675 // create a XorMask context 2676 mpMaskBuffer = new sal_uLong[ mnBufferLongs ]; 2677 mxMaskContext = ::CGBitmapContextCreate( mpMaskBuffer, 2678 nWidth, nHeight, nBitsPerComponent, nBytesPerRow, 2679 aCGColorSpace, aCGBmpInfo ); 2680 // reset the XOR mask to black 2681 memset( mpMaskBuffer, 0, mnBufferLongs * sizeof(sal_uLong) ); 2682 2683 // a bitmap context will be needed for manual XORing 2684 // create one unless the target context is a bitmap context 2685 if( nTargetDepth ) 2686 mpTempBuffer = (sal_uLong*)CGBitmapContextGetData( mxTargetContext ); 2687 if( !mpTempBuffer ) 2688 { 2689 // create a bitmap context matching to the target context 2690 mpTempBuffer = new sal_uLong[ mnBufferLongs ]; 2691 mxTempContext = ::CGBitmapContextCreate( mpTempBuffer, 2692 nWidth, nHeight, nBitsPerComponent, nBytesPerRow, 2693 aCGColorSpace, aCGBmpInfo ); 2694 } 2695 2696 // initialize XOR mask context for drawing 2697 CGContextSetFillColorSpace( mxMaskContext, aCGColorSpace ); 2698 CGContextSetStrokeColorSpace( mxMaskContext, aCGColorSpace ); 2699 CGContextSetShouldAntialias( mxMaskContext, false ); 2700 2701 // improve the XorMask's XOR emulation a litte 2702 // NOTE: currently only enabled for monochrome contexts 2703 if( aCGColorSpace == GetSalData()->mxGraySpace ) 2704 CGContextSetBlendMode( mxMaskContext, kCGBlendModeDifference ); 2705 2706 // intialize the transformation matrix to the drawing target 2707 const CGAffineTransform aCTM = CGContextGetCTM( xTargetContext ); 2708 CGContextConcatCTM( mxMaskContext, aCTM ); 2709 if( mxTempContext ) 2710 CGContextConcatCTM( mxTempContext, aCTM ); 2711 2712 // initialize the default XorMask graphics state 2713 CGContextSaveGState( mxMaskContext ); 2714 } 2715 2716 // ---------------------------------------------------------------------- 2717 2718 bool XorEmulation::UpdateTarget() 2719 { 2720 if( !IsEnabled() ) 2721 return false; 2722 2723 // update the temp bitmap buffer if needed 2724 if( mxTempContext ) 2725 CGContextDrawLayerAtPoint( mxTempContext, CGPointZero, mxTargetLayer ); 2726 2727 // do a manual XOR with the XorMask 2728 // this approach suffices for simple color manipulations 2729 // and also the complex-clipping-XOR-trick used in metafiles 2730 const sal_uLong* pSrc = mpMaskBuffer; 2731 sal_uLong* pDst = mpTempBuffer; 2732 for( int i = mnBufferLongs; --i >= 0;) 2733 *(pDst++) ^= *(pSrc++); 2734 2735 // write back the XOR results to the target context 2736 if( mxTempContext ) 2737 { 2738 CGImageRef xXorImage = CGBitmapContextCreateImage( mxTempContext ); 2739 const int nWidth = (int)CGImageGetWidth( xXorImage ); 2740 const int nHeight = (int)CGImageGetHeight( xXorImage ); 2741 // TODO: update minimal changerect 2742 const CGRect aFullRect = CGRectMake( 0, 0, nWidth, nHeight); 2743 CGContextDrawImage( mxTargetContext, aFullRect, xXorImage ); 2744 CGImageRelease( xXorImage ); 2745 } 2746 2747 // reset the XorMask to black again 2748 // TODO: not needed for last update 2749 memset( mpMaskBuffer, 0, mnBufferLongs * sizeof(sal_uLong) ); 2750 2751 // TODO: return FALSE if target was not changed 2752 return true; 2753 } 2754 2755 // ======================================================================= 2756 2757