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