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