Update ooo320-m1
[ooovba.git] / vcl / aqua / source / gdi / salgdi.cxx
blobdd07bf1703db69c798c966aca1cdbe328a5eb59f
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
31 #include "salconst.h"
32 #include "salgdi.h"
33 #include "salbmp.h"
34 #include "salframe.h"
35 #include "salcolorutils.hxx"
36 #include "sft.hxx"
37 #include "salatsuifontutils.hxx"
39 #include "vcl/impfont.hxx"
40 #include "vcl/fontsubset.hxx"
41 #include "vcl/sysdata.hxx"
42 #include "vcl/sallayout.hxx"
43 #include "vcl/svapp.hxx"
45 #include "osl/file.hxx"
46 #include "osl/process.h"
48 #include "vos/mutex.hxx"
50 #include "rtl/bootstrap.h"
51 #include "rtl/strbuf.hxx"
53 #include "basegfx/range/b2drectangle.hxx"
54 #include "basegfx/polygon/b2dpolygon.hxx"
55 #include "basegfx/polygon/b2dpolygontools.hxx"
56 #include "basegfx/matrix/b2dhommatrix.hxx"
58 using namespace vcl;
60 typedef unsigned char Boolean; // copied from MacTypes.h, should be properly included
61 typedef std::vector<unsigned char> ByteVector;
64 // =======================================================================
66 ImplMacFontData::ImplMacFontData( const ImplDevFontAttributes& rDFA, ATSUFontID nFontId )
67 : ImplFontData( rDFA, 0 )
68 , mnFontId( nFontId )
69 , mpCharMap( NULL )
70 , mbOs2Read( false )
71 , mbHasOs2Table( false )
72 , mbCmapEncodingRead( false )
73 , mbHasCJKSupport( false )
76 // -----------------------------------------------------------------------
78 ImplMacFontData::~ImplMacFontData()
80 if( mpCharMap )
81 mpCharMap->DeReference();
84 // -----------------------------------------------------------------------
86 sal_IntPtr ImplMacFontData::GetFontId() const
88 return (sal_IntPtr)mnFontId;
91 // -----------------------------------------------------------------------
93 ImplFontData* ImplMacFontData::Clone() const
95 ImplMacFontData* pClone = new ImplMacFontData(*this);
96 if( mpCharMap )
97 mpCharMap->AddReference();
98 return pClone;
101 // -----------------------------------------------------------------------
103 ImplFontEntry* ImplMacFontData::CreateFontInstance(ImplFontSelectData& rFSD) const
105 return new ImplFontEntry(rFSD);
108 // -----------------------------------------------------------------------
110 inline FourCharCode GetTag(const char aTagName[5])
112 return (aTagName[0]<<24)+(aTagName[1]<<16)+(aTagName[2]<<8)+(aTagName[3]);
115 static unsigned GetUShort( const unsigned char* p ){return((p[0]<<8)+p[1]);}
116 static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);}
118 ImplFontCharMap* ImplMacFontData::GetImplFontCharMap() const
120 if( mpCharMap )
122 // return the cached charmap
123 mpCharMap->AddReference();
124 return mpCharMap;
127 // set the default charmap
128 mpCharMap = ImplFontCharMap::GetDefaultMap();
130 // get the CMAP byte size
131 ATSFontRef rFont = FMGetATSFontRefFromFont( mnFontId );
132 ByteCount nBufSize = 0;
133 OSStatus eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, 0, NULL, &nBufSize );
134 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::GetImplFontCharMap : ATSFontGetTable1 failed!\n");
135 if( eStatus != noErr )
136 return mpCharMap;
138 // allocate a buffer for the CMAP raw data
139 ByteVector aBuffer( nBufSize );
141 // get the CMAP raw data
142 ByteCount nRawLength = 0;
143 eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, nBufSize, (void*)&aBuffer[0], &nRawLength );
144 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::GetImplFontCharMap : ATSFontGetTable2 failed!\n");
145 if( eStatus != noErr )
146 return mpCharMap;
147 DBG_ASSERT( (nBufSize==nRawLength), "ImplMacFontData::GetImplFontCharMap : ByteCount mismatch!\n");
149 // parse the CMAP
150 CmapResult aCmapResult;
151 if( !ParseCMAP( &aBuffer[0], nRawLength, aCmapResult ) )
152 return mpCharMap;
154 mpCharMap = new ImplFontCharMap( aCmapResult );
155 return mpCharMap;
158 // -----------------------------------------------------------------------
160 void ImplMacFontData::ReadOs2Table( void ) const
162 // read this only once per font
163 if( mbOs2Read )
164 return;
165 mbOs2Read = true;
167 // prepare to get the OS/2 table raw data
168 ATSFontRef rFont = FMGetATSFontRefFromFont( mnFontId );
169 ByteCount nBufSize = 0;
170 OSStatus eStatus = ATSFontGetTable( rFont, GetTag("OS/2"), 0, 0, NULL, &nBufSize );
171 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadOs2Table : ATSFontGetTable1 failed!\n");
172 if( eStatus != noErr )
173 return;
175 // allocate a buffer for the OS/2 raw data
176 ByteVector aBuffer( nBufSize );
178 // get the OS/2 raw data
179 ByteCount nRawLength = 0;
180 eStatus = ATSFontGetTable( rFont, GetTag("OS/2"), 0, nBufSize, (void*)&aBuffer[0], &nRawLength );
181 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadOs2Table : ATSFontGetTable2 failed!\n");
182 if( eStatus != noErr )
183 return;
184 DBG_ASSERT( (nBufSize==nRawLength), "ImplMacFontData::ReadOs2Table : ByteCount mismatch!\n");
185 mbHasOs2Table = true;
187 // parse the OS/2 raw data
188 // TODO: also analyze panose info, etc.
190 // check if the fonts needs the "CJK extra leading" heuristic
191 const unsigned char* pOS2map = &aBuffer[0];
192 const sal_uInt32 nVersion = GetUShort( pOS2map );
193 if( nVersion >= 0x0001 )
195 sal_uInt32 ulUnicodeRange2 = GetUInt( pOS2map + 46 );
196 if( ulUnicodeRange2 & 0x2DF00000 )
197 mbHasCJKSupport = true;
201 void ImplMacFontData::ReadMacCmapEncoding( void ) const
203 // read this only once per font
204 if( mbCmapEncodingRead )
205 return;
206 mbCmapEncodingRead = true;
208 ATSFontRef rFont = FMGetATSFontRefFromFont( mnFontId );
209 ByteCount nBufSize = 0;
210 OSStatus eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, 0, NULL, &nBufSize );
211 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadMacCmapEncoding : ATSFontGetTable1 failed!\n");
212 if( eStatus != noErr )
213 return;
215 ByteVector aBuffer( nBufSize );
217 ByteCount nRawLength = 0;
218 eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, nBufSize, (void*)&aBuffer[0], &nRawLength );
219 DBG_ASSERT( (eStatus==noErr), "ImplMacFontData::ReadMacCmapEncoding : ATSFontGetTable2 failed!\n");
220 if( eStatus != noErr )
221 return;
222 DBG_ASSERT( (nBufSize==nRawLength), "ImplMacFontData::ReadMacCmapEncoding : ByteCount mismatch!\n");
224 const unsigned char* pCmap = &aBuffer[0];
226 if (nRawLength < 24 )
227 return;
228 if( GetUShort( pCmap ) != 0x0000 )
229 return;
231 // check if the fonts needs the "CJK extra leading" heuristic
232 int nSubTables = GetUShort( pCmap + 2 );
234 for( const unsigned char* p = pCmap + 4; --nSubTables >= 0; p += 8 )
236 int nPlatform = GetUShort( p );
237 if( nPlatform == kFontMacintoshPlatform ) {
238 int nEncoding = GetUShort (p + 2 );
239 if( nEncoding == kFontJapaneseScript ||
240 nEncoding == kFontTraditionalChineseScript ||
241 nEncoding == kFontKoreanScript ||
242 nEncoding == kFontSimpleChineseScript )
244 mbHasCJKSupport = true;
245 break;
251 // -----------------------------------------------------------------------
253 bool ImplMacFontData::HasCJKSupport( void ) const
255 ReadOs2Table();
256 if( !mbHasOs2Table )
257 ReadMacCmapEncoding();
259 return mbHasCJKSupport;
262 // =======================================================================
264 AquaSalGraphics::AquaSalGraphics()
265 : mpFrame( NULL )
266 , mxLayer( NULL )
267 , mrContext( NULL )
268 , mpXorEmulation( NULL )
269 , mnXorMode( 0 )
270 , mnWidth( 0 )
271 , mnHeight( 0 )
272 , mnBitmapDepth( 0 )
273 , mnRealDPIX( 0 )
274 , mnRealDPIY( 0 )
275 , mfFakeDPIScale( 1.0 )
276 , mxClipPath( NULL )
277 , maLineColor( COL_WHITE )
278 , maFillColor( COL_BLACK )
279 , mpMacFontData( NULL )
280 , mnATSUIRotation( 0 )
281 , mfFontScale( 1.0 )
282 , mfFontStretch( 1.0 )
283 , mbNonAntialiasedText( false )
284 , mbPrinter( false )
285 , mbVirDev( false )
286 , mbWindow( false )
288 // create the style object for font attributes
289 ATSUCreateStyle( &maATSUStyle );
292 // -----------------------------------------------------------------------
294 AquaSalGraphics::~AquaSalGraphics()
297 if( mnUpdateGraphicsEvent )
299 Application::RemoveUserEvent( mnUpdateGraphicsEvent );
302 CGPathRelease( mxClipPath );
303 ATSUDisposeStyle( maATSUStyle );
305 if( mpXorEmulation )
306 delete mpXorEmulation;
308 if( mxLayer )
309 CGLayerRelease( mxLayer );
310 else if( mrContext && mbWindow )
312 // destroy backbuffer bitmap context that we created ourself
313 CGContextRelease( mrContext );
314 mrContext = NULL;
315 // memory is freed automatically by maOwnContextMemory
319 bool AquaSalGraphics::supportsOperation( OutDevSupportType eType ) const
321 bool bRet = false;
322 switch( eType )
324 case OutDevSupport_TransparentRect:
325 case OutDevSupport_B2DClip:
326 case OutDevSupport_B2DDraw:
327 bRet = true;
328 break;
329 default: break;
331 return bRet;
334 // =======================================================================
336 void AquaSalGraphics::updateResolution()
338 DBG_ASSERT( mbWindow, "updateResolution on inappropriate graphics" );
340 initResolution( (mbWindow && mpFrame) ? mpFrame->mpWindow : nil );
343 void AquaSalGraphics::initResolution( NSWindow* pWin )
345 // #i100617# read DPI only once; there is some kind of weird caching going on
346 // if the main screen changes
347 // FIXME: this is really unfortunate and needs to be investigated
349 SalData* pSalData = GetSalData();
350 if( pSalData->mnDPIX == 0 || pSalData->mnDPIY == 0 )
352 NSScreen* pScreen = nil;
354 /* #i91301#
355 many woes went into the try to have different resolutions
356 on different screens. The result of these trials is that OOo is not ready
357 for that yet, vcl and applications would need to be adapted.
359 Unfortunately this is not possible in the 3.0 timeframe.
360 So let's stay with one resolution for all Windows and VirtualDevices
361 which is the resolution of the main screen
363 This of course also means that measurements are exact only on the main screen.
364 For activating different resolutions again just comment out the two lines below.
366 if( pWin )
367 pScreen = [pWin screen];
369 if( pScreen == nil )
371 NSArray* pScreens = [NSScreen screens];
372 if( pScreens )
373 pScreen = [pScreens objectAtIndex: 0];
376 mnRealDPIX = mnRealDPIY = 96;
377 if( pScreen )
379 NSDictionary* pDev = [pScreen deviceDescription];
380 if( pDev )
382 NSNumber* pVal = [pDev objectForKey: @"NSScreenNumber"];
383 if( pVal )
385 // FIXME: casting a long to CGDirectDisplayID is evil, but
386 // Apple suggest to do it this way
387 const CGDirectDisplayID nDisplayID = (CGDirectDisplayID)[pVal longValue];
388 const CGSize aSize = CGDisplayScreenSize( nDisplayID ); // => result is in millimeters
389 mnRealDPIX = static_cast<long>((CGDisplayPixelsWide( nDisplayID ) * 25.4) / aSize.width);
390 mnRealDPIY = static_cast<long>((CGDisplayPixelsHigh( nDisplayID ) * 25.4) / aSize.height);
392 else
394 DBG_ERROR( "no resolution found in device description" );
397 else
399 DBG_ERROR( "no device description" );
402 else
404 DBG_ERROR( "no screen found" );
407 // for OSX any anisotropy reported for the display resolution is best ignored (e.g. TripleHead2Go)
408 mnRealDPIX = mnRealDPIY = (mnRealDPIX + mnRealDPIY + 1) / 2;
410 pSalData->mnDPIX = mnRealDPIX;
411 pSalData->mnDPIY = mnRealDPIY;
413 else
415 mnRealDPIX = pSalData->mnDPIX;
416 mnRealDPIY = pSalData->mnDPIY;
419 mfFakeDPIScale = 1.0;
422 void AquaSalGraphics::GetResolution( long& rDPIX, long& rDPIY )
424 if( !mnRealDPIY )
425 initResolution( (mbWindow && mpFrame) ? mpFrame->mpWindow : nil );
427 rDPIX = static_cast<long>(mfFakeDPIScale * mnRealDPIX);
428 rDPIY = static_cast<long>(mfFakeDPIScale * mnRealDPIY);
431 void AquaSalGraphics::copyResolution( AquaSalGraphics& rGraphics )
433 if( !rGraphics.mnRealDPIY && rGraphics.mbWindow && rGraphics.mpFrame )
434 rGraphics.initResolution( rGraphics.mpFrame->mpWindow );
436 mnRealDPIX = rGraphics.mnRealDPIX;
437 mnRealDPIY = rGraphics.mnRealDPIY;
438 mfFakeDPIScale = rGraphics.mfFakeDPIScale;
441 // -----------------------------------------------------------------------
443 USHORT AquaSalGraphics::GetBitCount()
445 USHORT nBits = mnBitmapDepth ? mnBitmapDepth : 32;//24;
446 return nBits;
449 // -----------------------------------------------------------------------
451 static const basegfx::B2DPoint aHalfPointOfs ( 0.5, 0.5 );
453 static void AddPolygonToPath( CGMutablePathRef xPath,
454 const ::basegfx::B2DPolygon& rPolygon, bool bClosePath, bool bPixelSnap, bool bLineDraw )
456 // short circuit if there is nothing to do
457 const int nPointCount = rPolygon.count();
458 if( nPointCount <= 0 )
459 return;
461 (void)bPixelSnap; // TODO
462 const CGAffineTransform* pTransform = NULL;
464 const bool bHasCurves = rPolygon.areControlPointsUsed();
465 bool bPendingCurve = false;
466 for( int nPointIdx = 0, nPrevIdx = 0;; nPrevIdx = nPointIdx++ )
468 int nClosedIdx = nPointIdx;
469 if( nPointIdx >= nPointCount )
471 // prepare to close last curve segment if needed
472 if( bClosePath && (nPointIdx == nPointCount) )
473 nClosedIdx = 0;
474 else
475 break;
478 ::basegfx::B2DPoint aPoint = rPolygon.getB2DPoint( nClosedIdx );
480 if(bPixelSnap)
482 // snap device coordinates to full pixels
483 aPoint.setX( basegfx::fround( aPoint.getX() ) );
484 aPoint.setY( basegfx::fround( aPoint.getY() ) );
487 if( bLineDraw )
488 aPoint += aHalfPointOfs;
490 if( !nPointIdx ) // first point
491 CGPathMoveToPoint( xPath, pTransform, aPoint.getX(), aPoint.getY() );
492 else if( !bPendingCurve ) // line segment
493 CGPathAddLineToPoint( xPath, pTransform, aPoint.getX(), aPoint.getY() );
494 else // cubic bezier segment
496 basegfx::B2DPoint aCP1 = rPolygon.getNextControlPoint( nPrevIdx );
497 basegfx::B2DPoint aCP2 = rPolygon.getPrevControlPoint( nClosedIdx );
498 if( bLineDraw )
500 aCP1 += aHalfPointOfs;
501 aCP2 += aHalfPointOfs;
503 CGPathAddCurveToPoint( xPath, pTransform, aCP1.getX(), aCP1.getY(),
504 aCP2.getX(), aCP2.getY(), aPoint.getX(), aPoint.getY() );
507 if( bHasCurves )
508 bPendingCurve = rPolygon.isNextControlPointUsed( nClosedIdx );
511 if( bClosePath )
512 CGPathCloseSubpath( xPath );
515 static void AddPolyPolygonToPath( CGMutablePathRef xPath,
516 const ::basegfx::B2DPolyPolygon& rPolyPoly, bool bPixelSnap, bool bLineDraw )
518 // short circuit if there is nothing to do
519 const int nPolyCount = rPolyPoly.count();
520 if( nPolyCount <= 0 )
521 return;
523 for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx )
525 const ::basegfx::B2DPolygon rPolygon = rPolyPoly.getB2DPolygon( nPolyIdx );
526 AddPolygonToPath( xPath, rPolygon, true, bPixelSnap, bLineDraw );
530 // -----------------------------------------------------------------------
532 void AquaSalGraphics::ResetClipRegion()
534 // release old path and indicate no clipping
535 if( mxClipPath )
537 CGPathRelease( mxClipPath );
538 mxClipPath = NULL;
540 if( CheckContext() )
541 SetState();
544 // -----------------------------------------------------------------------
546 void AquaSalGraphics::BeginSetClipRegion( ULONG nRectCount )
548 // release old clip path
549 if( mxClipPath )
551 CGPathRelease( mxClipPath );
552 mxClipPath = NULL;
556 // -----------------------------------------------------------------------
558 BOOL AquaSalGraphics::unionClipRegion( long nX, long nY, long nWidth, long nHeight )
560 if( (nWidth <= 0) || (nHeight <= 0) )
561 return TRUE;
563 if( !mxClipPath )
564 mxClipPath = CGPathCreateMutable();
565 const CGRect aClipRect = {{nX,nY},{nWidth,nHeight}};
566 CGPathAddRect( mxClipPath, NULL, aClipRect );
567 return TRUE;
570 // -----------------------------------------------------------------------
572 bool AquaSalGraphics::unionClipRegion( const ::basegfx::B2DPolyPolygon& rPolyPolygon )
574 if( rPolyPolygon.count() <= 0 )
575 return true;
577 if( !mxClipPath )
578 mxClipPath = CGPathCreateMutable();
579 AddPolyPolygonToPath( mxClipPath, rPolyPolygon, !getAntiAliasB2DDraw(), false );
580 return true;
583 // -----------------------------------------------------------------------
585 void AquaSalGraphics::EndSetClipRegion()
587 if( CheckContext() )
588 SetState();
591 // -----------------------------------------------------------------------
593 void AquaSalGraphics::SetLineColor()
595 maLineColor.SetAlpha( 0.0 ); // transparent
596 CGContextSetStrokeColor( mrContext, maLineColor.AsArray() );
599 // -----------------------------------------------------------------------
601 void AquaSalGraphics::SetLineColor( SalColor nSalColor )
603 maLineColor = RGBAColor( nSalColor );
604 CGContextSetStrokeColor( mrContext, maLineColor.AsArray() );
607 // -----------------------------------------------------------------------
609 void AquaSalGraphics::SetFillColor()
611 maFillColor.SetAlpha( 0.0 ); // transparent
612 CGContextSetFillColor( mrContext, maFillColor.AsArray() );
615 // -----------------------------------------------------------------------
617 void AquaSalGraphics::SetFillColor( SalColor nSalColor )
619 maFillColor = RGBAColor( nSalColor );
620 CGContextSetFillColor( mrContext, maFillColor.AsArray() );
623 // -----------------------------------------------------------------------
625 static SalColor ImplGetROPSalColor( SalROPColor nROPColor )
627 SalColor nSalColor;
628 if ( nROPColor == SAL_ROP_0 )
629 nSalColor = MAKE_SALCOLOR( 0, 0, 0 );
630 else
631 nSalColor = MAKE_SALCOLOR( 255, 255, 255 );
632 return nSalColor;
635 void AquaSalGraphics::SetROPLineColor( SalROPColor nROPColor )
637 if( ! mbPrinter )
638 SetLineColor( ImplGetROPSalColor( nROPColor ) );
641 // -----------------------------------------------------------------------
643 void AquaSalGraphics::SetROPFillColor( SalROPColor nROPColor )
645 if( ! mbPrinter )
646 SetFillColor( ImplGetROPSalColor( nROPColor ) );
649 // -----------------------------------------------------------------------
651 void AquaSalGraphics::ImplDrawPixel( long nX, long nY, const RGBAColor& rColor )
653 if( !CheckContext() )
654 return;
656 // overwrite the fill color
657 CGContextSetFillColor( mrContext, rColor.AsArray() );
658 // draw 1x1 rect, there is no pixel drawing in Quartz
659 CGRect aDstRect = {{nX,nY,},{1,1}};
660 CGContextFillRect( mrContext, aDstRect );
661 RefreshRect( aDstRect );
662 // reset the fill color
663 CGContextSetFillColor( mrContext, maFillColor.AsArray() );
666 void AquaSalGraphics::drawPixel( long nX, long nY )
668 // draw pixel with current line color
669 ImplDrawPixel( nX, nY, maLineColor );
672 void AquaSalGraphics::drawPixel( long nX, long nY, SalColor nSalColor )
674 const RGBAColor aPixelColor( nSalColor );
675 ImplDrawPixel( nX, nY, aPixelColor );
678 // -----------------------------------------------------------------------
680 void AquaSalGraphics::drawLine( long nX1, long nY1, long nX2, long nY2 )
682 if( !CheckContext() )
683 return;
685 CGContextBeginPath( mrContext );
686 CGContextMoveToPoint( mrContext, static_cast<float>(nX1)+0.5, static_cast<float>(nY1)+0.5 );
687 CGContextAddLineToPoint( mrContext, static_cast<float>(nX2)+0.5, static_cast<float>(nY2)+0.5 );
688 CGContextDrawPath( mrContext, kCGPathStroke );
690 Rectangle aRefreshRect( nX1, nY1, nX2, nY2 );
693 // -----------------------------------------------------------------------
695 void AquaSalGraphics::drawRect( long nX, long nY, long nWidth, long nHeight )
697 if( !CheckContext() )
698 return;
700 CGRect aRect( CGRectMake(nX, nY, nWidth, nHeight) );
701 if( IsPenVisible() )
703 aRect.origin.x += 0.5;
704 aRect.origin.y += 0.5;
705 aRect.size.width -= 1;
706 aRect.size.height -= 1;
709 if( IsBrushVisible() )
710 CGContextFillRect( mrContext, aRect );
712 if( IsPenVisible() )
713 CGContextStrokeRect( mrContext, aRect );
715 RefreshRect( nX, nY, nWidth, nHeight );
718 // -----------------------------------------------------------------------
720 static void getBoundRect( ULONG nPoints, const SalPoint *pPtAry, long &rX, long& rY, long& rWidth, long& rHeight )
722 long nX1 = pPtAry->mnX;
723 long nX2 = nX1;
724 long nY1 = pPtAry->mnY;
725 long nY2 = nY1;
726 for( ULONG n = 1; n < nPoints; n++ )
728 if( pPtAry[n].mnX < nX1 )
729 nX1 = pPtAry[n].mnX;
730 else if( pPtAry[n].mnX > nX2 )
731 nX2 = pPtAry[n].mnX;
733 if( pPtAry[n].mnY < nY1 )
734 nY1 = pPtAry[n].mnY;
735 else if( pPtAry[n].mnY > nY2 )
736 nY2 = pPtAry[n].mnY;
738 rX = nX1;
739 rY = nY1;
740 rWidth = nX2 - nX1 + 1;
741 rHeight = nY2 - nY1 + 1;
744 static inline void alignLinePoint( const SalPoint* i_pIn, float& o_fX, float& o_fY )
746 o_fX = static_cast<float>(i_pIn->mnX ) + 0.5;
747 o_fY = static_cast<float>(i_pIn->mnY ) + 0.5;
750 void AquaSalGraphics::drawPolyLine( ULONG nPoints, const SalPoint *pPtAry )
752 if( nPoints < 1 )
753 return;
754 if( !CheckContext() )
755 return;
757 long nX = 0, nY = 0, nWidth = 0, nHeight = 0;
758 getBoundRect( nPoints, pPtAry, nX, nY, nWidth, nHeight );
760 float fX, fY;
762 CGContextBeginPath( mrContext );
763 alignLinePoint( pPtAry, fX, fY );
764 CGContextMoveToPoint( mrContext, fX, fY );
765 pPtAry++;
766 for( ULONG nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ )
768 alignLinePoint( pPtAry, fX, fY );
769 CGContextAddLineToPoint( mrContext, fX, fY );
771 CGContextDrawPath( mrContext, kCGPathStroke );
773 RefreshRect( nX, nY, nWidth, nHeight );
776 // -----------------------------------------------------------------------
778 void AquaSalGraphics::drawPolygon( ULONG nPoints, const SalPoint *pPtAry )
780 if( nPoints <= 1 )
781 return;
782 if( !CheckContext() )
783 return;
785 long nX = 0, nY = 0, nWidth = 0, nHeight = 0;
786 getBoundRect( nPoints, pPtAry, nX, nY, nWidth, nHeight );
788 CGPathDrawingMode eMode;
789 if( IsBrushVisible() && IsPenVisible() )
790 eMode = kCGPathEOFillStroke;
791 else if( IsPenVisible() )
792 eMode = kCGPathStroke;
793 else if( IsBrushVisible() )
794 eMode = kCGPathEOFill;
795 else
796 return;
798 CGContextBeginPath( mrContext );
800 if( IsPenVisible() )
802 float fX, fY;
803 alignLinePoint( pPtAry, fX, fY );
804 CGContextMoveToPoint( mrContext, fX, fY );
805 pPtAry++;
806 for( ULONG nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ )
808 alignLinePoint( pPtAry, fX, fY );
809 CGContextAddLineToPoint( mrContext, fX, fY );
812 else
814 CGContextMoveToPoint( mrContext, pPtAry->mnX, pPtAry->mnY );
815 pPtAry++;
816 for( ULONG nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ )
817 CGContextAddLineToPoint( mrContext, pPtAry->mnX, pPtAry->mnY );
820 CGContextDrawPath( mrContext, eMode );
821 RefreshRect( nX, nY, nWidth, nHeight );
824 // -----------------------------------------------------------------------
826 void AquaSalGraphics::drawPolyPolygon( ULONG nPolyCount, const ULONG *pPoints, PCONSTSALPOINT *ppPtAry )
828 if( nPolyCount <= 0 )
829 return;
830 if( !CheckContext() )
831 return;
833 // find bound rect
834 long leftX = 0, topY = 0, maxWidth = 0, maxHeight = 0;
835 getBoundRect( pPoints[0], ppPtAry[0], leftX, topY, maxWidth, maxHeight );
836 for( ULONG n = 1; n < nPolyCount; n++ )
838 long nX = leftX, nY = topY, nW = maxWidth, nH = maxHeight;
839 getBoundRect( pPoints[n], ppPtAry[n], nX, nY, nW, nH );
840 if( nX < leftX )
842 maxWidth += leftX - nX;
843 leftX = nX;
845 if( nY < topY )
847 maxHeight += topY - nY;
848 topY = nY;
850 if( nX + nW > leftX + maxWidth )
851 maxWidth = nX + nW - leftX;
852 if( nY + nH > topY + maxHeight )
853 maxHeight = nY + nH - topY;
856 // prepare drawing mode
857 CGPathDrawingMode eMode;
858 if( IsBrushVisible() && IsPenVisible() )
859 eMode = kCGPathEOFillStroke;
860 else if( IsPenVisible() )
861 eMode = kCGPathStroke;
862 else if( IsBrushVisible() )
863 eMode = kCGPathEOFill;
864 else
865 return;
867 // convert to CGPath
868 CGContextBeginPath( mrContext );
869 if( IsPenVisible() )
871 for( ULONG nPoly = 0; nPoly < nPolyCount; nPoly++ )
873 const ULONG nPoints = pPoints[nPoly];
874 if( nPoints > 1 )
876 const SalPoint *pPtAry = ppPtAry[nPoly];
877 float fX, fY;
878 alignLinePoint( pPtAry, fX, fY );
879 CGContextMoveToPoint( mrContext, fX, fY );
880 pPtAry++;
881 for( ULONG nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ )
883 alignLinePoint( pPtAry, fX, fY );
884 CGContextAddLineToPoint( mrContext, fX, fY );
886 CGContextClosePath(mrContext);
890 else
892 for( ULONG nPoly = 0; nPoly < nPolyCount; nPoly++ )
894 const ULONG nPoints = pPoints[nPoly];
895 if( nPoints > 1 )
897 const SalPoint *pPtAry = ppPtAry[nPoly];
898 CGContextMoveToPoint( mrContext, pPtAry->mnX, pPtAry->mnY );
899 pPtAry++;
900 for( ULONG nPoint = 1; nPoint < nPoints; nPoint++, pPtAry++ )
901 CGContextAddLineToPoint( mrContext, pPtAry->mnX, pPtAry->mnY );
902 CGContextClosePath(mrContext);
907 CGContextDrawPath( mrContext, eMode );
909 RefreshRect( leftX, topY, maxWidth, maxHeight );
912 // -----------------------------------------------------------------------
914 bool AquaSalGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly,
915 double fTransparency )
917 // short circuit if there is nothing to do
918 const int nPolyCount = rPolyPoly.count();
919 if( nPolyCount <= 0 )
920 return true;
922 // ignore invisible polygons
923 if( (fTransparency >= 1.0) || (fTransparency < 0) )
924 return true;
926 // setup poly-polygon path
927 CGMutablePathRef xPath = CGPathCreateMutable();
928 for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx )
930 const ::basegfx::B2DPolygon rPolygon = rPolyPoly.getB2DPolygon( nPolyIdx );
931 AddPolygonToPath( xPath, rPolygon, true, !getAntiAliasB2DDraw(), IsPenVisible() );
934 // use the path to prepare the graphics context
935 CGContextSaveGState( mrContext );
936 CGContextBeginPath( mrContext );
937 CGContextAddPath( mrContext, xPath );
938 const CGRect aRefreshRect = CGPathGetBoundingBox( xPath );
939 CGPathRelease( xPath );
941 #ifndef NO_I97317_WORKAROUND
942 // #i97317# workaround for Quartz having problems with drawing small polygons
943 if( (aRefreshRect.size.width <= 0.125) && (aRefreshRect.size.height <= 0.125) )
944 return true;
945 #endif
947 // draw path with antialiased polygon
948 CGContextSetShouldAntialias( mrContext, true );
949 CGContextSetAlpha( mrContext, 1.0 - fTransparency );
950 CGContextDrawPath( mrContext, kCGPathEOFillStroke );
951 CGContextRestoreGState( mrContext );
953 // mark modified rectangle as updated
954 RefreshRect( aRefreshRect );
955 return true;
958 // -----------------------------------------------------------------------
960 bool AquaSalGraphics::drawPolyLine( const ::basegfx::B2DPolygon& rPolyLine,
961 const ::basegfx::B2DVector& rLineWidths,
962 basegfx::B2DLineJoin eLineJoin )
964 // short circuit if there is nothing to do
965 const int nPointCount = rPolyLine.count();
966 if( nPointCount <= 0 )
967 return true;
969 // reject strange requests
970 if( rLineWidths.getX() != rLineWidths.getY() )
971 return false;
973 // #i101491# Aqua does not support B2DLINEJOIN_NONE; return false to use
974 // the fallback (own geometry preparation)
975 if(basegfx::B2DLINEJOIN_NONE == eLineJoin)
976 return false;
978 // setup line attributes
979 CGLineJoin aCGLineJoin = kCGLineJoinMiter;
980 switch( eLineJoin ) {
981 case ::basegfx::B2DLINEJOIN_NONE: aCGLineJoin = /*TODO?*/kCGLineJoinMiter; break;
982 case ::basegfx::B2DLINEJOIN_MIDDLE: aCGLineJoin = /*TODO?*/kCGLineJoinMiter; break;
983 case ::basegfx::B2DLINEJOIN_BEVEL: aCGLineJoin = kCGLineJoinBevel; break;
984 case ::basegfx::B2DLINEJOIN_MITER: aCGLineJoin = kCGLineJoinMiter; break;
985 case ::basegfx::B2DLINEJOIN_ROUND: aCGLineJoin = kCGLineJoinRound; break;
988 // setup poly-polygon path
989 CGMutablePathRef xPath = CGPathCreateMutable();
990 AddPolygonToPath( xPath, rPolyLine, rPolyLine.isClosed(), !getAntiAliasB2DDraw(), true );
992 // use the path to prepare the graphics context
993 CGContextSaveGState( mrContext );
994 CGContextAddPath( mrContext, xPath );
995 const CGRect aRefreshRect = CGPathGetBoundingBox( xPath );
996 CGPathRelease( xPath );
998 #ifndef NO_I97317_WORKAROUND
999 // #i97317# workaround for Quartz having problems with drawing small polygons
1000 if( (aRefreshRect.size.width <= 0.125) && (aRefreshRect.size.height <= 0.125) )
1001 return true;
1002 #endif
1004 // draw path with antialiased line
1005 CGContextSetShouldAntialias( mrContext, true );
1006 CGContextSetLineJoin( mrContext, aCGLineJoin );
1007 CGContextSetLineWidth( mrContext, rLineWidths.getX() );
1008 CGContextDrawPath( mrContext, kCGPathStroke );
1009 CGContextRestoreGState( mrContext );
1011 // mark modified rectangle as updated
1012 RefreshRect( aRefreshRect );
1013 return true;
1016 // -----------------------------------------------------------------------
1018 sal_Bool AquaSalGraphics::drawPolyLineBezier( ULONG nPoints, const SalPoint* pPtAry, const BYTE* pFlgAry )
1020 return sal_False;
1023 // -----------------------------------------------------------------------
1025 sal_Bool AquaSalGraphics::drawPolygonBezier( ULONG nPoints, const SalPoint* pPtAry, const BYTE* pFlgAry )
1027 return sal_False;
1030 // -----------------------------------------------------------------------
1032 sal_Bool AquaSalGraphics::drawPolyPolygonBezier( ULONG nPoly, const ULONG* pPoints,
1033 const SalPoint* const* pPtAry, const BYTE* const* pFlgAry )
1035 return sal_False;
1038 // -----------------------------------------------------------------------
1040 void AquaSalGraphics::copyBits( const SalTwoRect *pPosAry, SalGraphics *pSrcGraphics )
1042 if( !pSrcGraphics )
1043 pSrcGraphics = this;
1045 //from unix salgdi2.cxx
1046 //[FIXME] find a better way to prevent calc from crashing when width and height are negative
1047 if( pPosAry->mnSrcWidth <= 0
1048 || pPosAry->mnSrcHeight <= 0
1049 || pPosAry->mnDestWidth <= 0
1050 || pPosAry->mnDestHeight <= 0 )
1052 return;
1055 // accelerate trivial operations
1056 /*const*/ AquaSalGraphics* pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics);
1057 const bool bSameGraphics = (this == pSrc) || (mbWindow && mpFrame && pSrc->mbWindow && (mpFrame == pSrc->mpFrame));
1058 if( bSameGraphics
1059 && (pPosAry->mnSrcWidth == pPosAry->mnDestWidth)
1060 && (pPosAry->mnSrcHeight == pPosAry->mnDestHeight))
1062 // short circuit if there is nothing to do
1063 if( (pPosAry->mnSrcX == pPosAry->mnDestX)
1064 && (pPosAry->mnSrcY == pPosAry->mnDestY))
1065 return;
1066 // use copyArea() if source and destination context are identical
1067 copyArea( pPosAry->mnDestX, pPosAry->mnDestY, pPosAry->mnSrcX, pPosAry->mnSrcY,
1068 pPosAry->mnSrcWidth, pPosAry->mnSrcHeight, 0 );
1069 return;
1072 ApplyXorContext();
1073 pSrc->ApplyXorContext();
1075 DBG_ASSERT( pSrc->mxLayer!=NULL, "AquaSalGraphics::copyBits() from non-layered graphics" );
1077 const CGPoint aDstPoint = { +pPosAry->mnDestX - pPosAry->mnSrcX, pPosAry->mnDestY - pPosAry->mnSrcY };
1078 if( !mnBitmapDepth || (aDstPoint.x + pSrc->mnWidth) <= mnWidth ) // workaround a Quartz crasher
1080 // in XOR mode the drawing context is redirected to the XOR mask
1081 // if source and target are identical then copyBits() paints onto the target context though
1082 CGContextRef xCopyContext = mrContext;
1083 if( mpXorEmulation && mpXorEmulation->IsEnabled() )
1084 if( pSrcGraphics == this )
1085 xCopyContext = mpXorEmulation->GetTargetContext();
1087 CGContextSaveGState( xCopyContext );
1088 const CGRect aDstRect = { {pPosAry->mnDestX, pPosAry->mnDestY}, {pPosAry->mnDestWidth, pPosAry->mnDestHeight} };
1089 CGContextClipToRect( xCopyContext, aDstRect );
1091 // draw at new destination
1092 // NOTE: flipped drawing gets disabled for this, else the subimage would be drawn upside down
1093 if( pSrc->IsFlipped() )
1094 { CGContextTranslateCTM( xCopyContext, 0, +mnHeight ); CGContextScaleCTM( xCopyContext, +1, -1 ); }
1095 // TODO: pSrc->size() != this->size()
1096 ::CGContextDrawLayerAtPoint( xCopyContext, aDstPoint, pSrc->mxLayer );
1097 CGContextRestoreGState( xCopyContext );
1098 // mark the destination rectangle as updated
1099 RefreshRect( aDstRect );
1101 else
1103 SalBitmap* pBitmap = pSrc->getBitmap( pPosAry->mnSrcX, pPosAry->mnSrcY, pPosAry->mnSrcWidth, pPosAry->mnSrcHeight );
1105 if( pBitmap )
1107 SalTwoRect aPosAry( *pPosAry );
1108 aPosAry.mnSrcX = 0;
1109 aPosAry.mnSrcY = 0;
1110 drawBitmap( &aPosAry, *pBitmap );
1111 delete pBitmap;
1116 // -----------------------------------------------------------------------
1118 void AquaSalGraphics::copyArea( long nDstX, long nDstY,long nSrcX, long nSrcY, long nSrcWidth, long nSrcHeight, USHORT nFlags )
1120 ApplyXorContext();
1122 #if 0 // TODO: make AquaSalBitmap as fast as the alternative implementation below
1123 SalBitmap* pBitmap = getBitmap( nSrcX, nSrcY, nSrcWidth, nSrcHeight );
1124 if( pBitmap )
1126 SalTwoRect aPosAry;
1127 aPosAry.mnSrcX = 0;
1128 aPosAry.mnSrcY = 0;
1129 aPosAry.mnSrcWidth = nSrcWidth;
1130 aPosAry.mnSrcHeight = nSrcHeight;
1131 aPosAry.mnDestX = nDstX;
1132 aPosAry.mnDestY = nDstY;
1133 aPosAry.mnDestWidth = nSrcWidth;
1134 aPosAry.mnDestHeight = nSrcHeight;
1135 drawBitmap( &aPosAry, *pBitmap );
1136 delete pBitmap;
1138 #else
1139 DBG_ASSERT( mxLayer!=NULL, "AquaSalGraphics::copyArea() for non-layered graphics" );
1141 // in XOR mode the drawing context is redirected to the XOR mask
1142 // copyArea() always works on the target context though
1143 CGContextRef xCopyContext = mrContext;
1144 if( mpXorEmulation && mpXorEmulation->IsEnabled() )
1145 xCopyContext = mpXorEmulation->GetTargetContext();
1147 // drawing a layer onto its own context causes trouble on OSX => copy it first
1148 // TODO: is it possible to get rid of this unneeded copy more often?
1149 // e.g. on OSX>=10.5 only this situation causes problems:
1150 // mnBitmapDepth && (aDstPoint.x + pSrc->mnWidth) > mnWidth
1151 CGLayerRef xSrcLayer = mxLayer;
1152 // TODO: if( mnBitmapDepth > 0 )
1154 const CGSize aSrcSize = { nSrcWidth, nSrcHeight };
1155 xSrcLayer = ::CGLayerCreateWithContext( xCopyContext, aSrcSize, NULL );
1156 const CGContextRef xSrcContext = CGLayerGetContext( xSrcLayer );
1157 CGPoint aSrcPoint = { -nSrcX, -nSrcY };
1158 if( IsFlipped() )
1160 ::CGContextTranslateCTM( xSrcContext, 0, +nSrcHeight );
1161 ::CGContextScaleCTM( xSrcContext, +1, -1 );
1162 aSrcPoint.y = (nSrcY + nSrcHeight) - mnHeight;
1164 ::CGContextDrawLayerAtPoint( xSrcContext, aSrcPoint, mxLayer );
1167 // draw at new destination
1168 const CGPoint aDstPoint = { +nDstX, +nDstY };
1169 ::CGContextDrawLayerAtPoint( xCopyContext, aDstPoint, xSrcLayer );
1171 // cleanup
1172 if( xSrcLayer != mxLayer )
1173 CGLayerRelease( xSrcLayer );
1175 // mark the destination rectangle as updated
1176 RefreshRect( nDstX, nDstY, nSrcWidth, nSrcHeight );
1177 #endif
1180 // -----------------------------------------------------------------------
1182 void AquaSalGraphics::drawBitmap( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap )
1184 if( !CheckContext() )
1185 return;
1187 const AquaSalBitmap& rBitmap = static_cast<const AquaSalBitmap&>(rSalBitmap);
1188 CGImageRef xImage = rBitmap.CreateCroppedImage( (int)pPosAry->mnSrcX, (int)pPosAry->mnSrcY, (int)pPosAry->mnSrcWidth, (int)pPosAry->mnSrcHeight );
1189 if( !xImage )
1190 return;
1192 const CGRect aDstRect = {{pPosAry->mnDestX, pPosAry->mnDestY}, {pPosAry->mnDestWidth, pPosAry->mnDestHeight}};
1193 CGContextDrawImage( mrContext, aDstRect, xImage );
1194 CGImageRelease( xImage );
1195 RefreshRect( aDstRect );
1198 // -----------------------------------------------------------------------
1200 void AquaSalGraphics::drawBitmap( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap,SalColor nTransparentColor )
1202 DBG_ERROR("not implemented for color masking!");
1203 drawBitmap( pPosAry, rSalBitmap );
1206 // -----------------------------------------------------------------------
1208 void AquaSalGraphics::drawBitmap( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap, const SalBitmap& rTransparentBitmap )
1210 if( !CheckContext() )
1211 return;
1213 const AquaSalBitmap& rBitmap = static_cast<const AquaSalBitmap&>(rSalBitmap);
1214 const AquaSalBitmap& rMask = static_cast<const AquaSalBitmap&>(rTransparentBitmap);
1215 CGImageRef xMaskedImage( rBitmap.CreateWithMask( rMask, pPosAry->mnSrcX, pPosAry->mnSrcY, pPosAry->mnSrcWidth, pPosAry->mnSrcHeight ) );
1216 if( !xMaskedImage )
1217 return;
1219 const CGRect aDstRect = {{pPosAry->mnDestX, pPosAry->mnDestY}, {pPosAry->mnDestWidth, pPosAry->mnDestHeight}};
1220 CGContextDrawImage( mrContext, aDstRect, xMaskedImage );
1221 CGImageRelease( xMaskedImage );
1222 RefreshRect( aDstRect );
1225 // -----------------------------------------------------------------------
1227 void AquaSalGraphics::drawMask( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap, SalColor nMaskColor )
1229 if( !CheckContext() )
1230 return;
1232 const AquaSalBitmap& rBitmap = static_cast<const AquaSalBitmap&>(rSalBitmap);
1233 CGImageRef xImage = rBitmap.CreateColorMask( pPosAry->mnSrcX, pPosAry->mnSrcY, pPosAry->mnSrcWidth, pPosAry->mnSrcHeight, nMaskColor );
1234 if( !xImage )
1235 return;
1237 const CGRect aDstRect = {{pPosAry->mnDestX, pPosAry->mnDestY}, {pPosAry->mnDestWidth, pPosAry->mnDestHeight}};
1238 CGContextDrawImage( mrContext, aDstRect, xImage );
1239 CGImageRelease( xImage );
1240 RefreshRect( aDstRect );
1243 // -----------------------------------------------------------------------
1245 SalBitmap* AquaSalGraphics::getBitmap( long nX, long nY, long nDX, long nDY )
1247 DBG_ASSERT( mxLayer, "AquaSalGraphics::getBitmap() with no layer" );
1249 ApplyXorContext();
1251 AquaSalBitmap* pBitmap = new AquaSalBitmap;
1252 if( !pBitmap->Create( mxLayer, mnBitmapDepth, nX, nY, nDX, nDY, !mbWindow ) )
1254 delete pBitmap;
1255 pBitmap = NULL;
1258 return pBitmap;
1261 // -----------------------------------------------------------------------
1263 SalColor AquaSalGraphics::getPixel( long nX, long nY )
1265 // return default value on printers or when out of bounds
1266 if( !mxLayer
1267 || (nX < 0) || (nX >= mnWidth)
1268 || (nY < 0) || (nY >= mnHeight))
1269 return COL_BLACK;
1271 // prepare creation of matching a CGBitmapContext
1272 CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace;
1273 CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big;
1274 #if __BIG_ENDIAN__
1275 struct{ unsigned char b, g, r, a; } aPixel;
1276 #else
1277 struct{ unsigned char a, r, g, b; } aPixel;
1278 #endif
1280 // create a one-pixel bitmap context
1281 // TODO: is it worth to cache it?
1282 CGContextRef xOnePixelContext = ::CGBitmapContextCreate( &aPixel,
1283 1, 1, 8, sizeof(aPixel), aCGColorSpace, aCGBmpInfo );
1285 // update this graphics layer
1286 ApplyXorContext();
1288 // copy the requested pixel into the bitmap context
1289 if( IsFlipped() )
1290 nY = mnHeight - nY;
1291 const CGPoint aCGPoint = {-nX, -nY};
1292 CGContextDrawLayerAtPoint( xOnePixelContext, aCGPoint, mxLayer );
1293 CGContextRelease( xOnePixelContext );
1295 SalColor nSalColor = MAKE_SALCOLOR( aPixel.r, aPixel.g, aPixel.b );
1296 return nSalColor;
1299 // -----------------------------------------------------------------------
1302 static void DrawPattern50( void* info, CGContextRef rContext )
1304 static const CGRect aRects[2] = { { {0,0}, { 2, 2 } }, { { 2, 2 }, { 2, 2 } } };
1305 CGContextAddRects( rContext, aRects, 2 );
1306 CGContextFillPath( rContext );
1309 void AquaSalGraphics::Pattern50Fill()
1311 static const float aFillCol[4] = { 1,1,1,1 };
1312 static const CGPatternCallbacks aCallback = { 0, &DrawPattern50, NULL };
1313 if( ! GetSalData()->mxP50Space )
1314 GetSalData()->mxP50Space = CGColorSpaceCreatePattern( GetSalData()->mxRGBSpace );
1315 if( ! GetSalData()->mxP50Pattern )
1316 GetSalData()->mxP50Pattern = CGPatternCreate( NULL, CGRectMake( 0, 0, 4, 4 ),
1317 CGAffineTransformIdentity, 4, 4,
1318 kCGPatternTilingConstantSpacing,
1319 false, &aCallback );
1321 CGContextSetFillColorSpace( mrContext, GetSalData()->mxP50Space );
1322 CGContextSetFillPattern( mrContext, GetSalData()->mxP50Pattern, aFillCol );
1323 CGContextFillPath( mrContext );
1326 void AquaSalGraphics::invert( long nX, long nY, long nWidth, long nHeight, SalInvert nFlags )
1328 if ( CheckContext() )
1330 CGRect aCGRect = CGRectMake( nX, nY, nWidth, nHeight);
1331 CGContextSaveGState(mrContext);
1333 if ( nFlags & SAL_INVERT_TRACKFRAME )
1335 const float dashLengths[2] = { 4.0, 4.0 }; // for drawing dashed line
1336 CGContextSetBlendMode( mrContext, kCGBlendModeDifference );
1337 CGContextSetRGBStrokeColor ( mrContext, 1.0, 1.0, 1.0, 1.0 );
1338 CGContextSetLineDash ( mrContext, 0, dashLengths, 2 );
1339 CGContextSetLineWidth( mrContext, 2.0);
1340 CGContextStrokeRect ( mrContext, aCGRect );
1342 else if ( nFlags & SAL_INVERT_50 )
1344 //CGContextSetAllowsAntialiasing( mrContext, false );
1345 CGContextSetBlendMode(mrContext, kCGBlendModeDifference);
1346 CGContextAddRect( mrContext, aCGRect );
1347 Pattern50Fill();
1349 else // just invert
1351 CGContextSetBlendMode(mrContext, kCGBlendModeDifference);
1352 CGContextSetRGBFillColor ( mrContext,1.0, 1.0, 1.0 , 1.0 );
1353 CGContextFillRect ( mrContext, aCGRect );
1355 CGContextRestoreGState( mrContext);
1356 RefreshRect( aCGRect );
1360 // -----------------------------------------------------------------------
1362 void AquaSalGraphics::invert( ULONG nPoints, const SalPoint* pPtAry, SalInvert nSalFlags )
1364 CGPoint* CGpoints ;
1365 if ( CheckContext() )
1367 CGContextSaveGState(mrContext);
1368 CGpoints = makeCGptArray(nPoints,pPtAry);
1369 CGContextAddLines ( mrContext, CGpoints, nPoints );
1370 if ( nSalFlags & SAL_INVERT_TRACKFRAME )
1372 const float dashLengths[2] = { 4.0, 4.0 }; // for drawing dashed line
1373 CGContextSetBlendMode( mrContext, kCGBlendModeDifference );
1374 CGContextSetRGBStrokeColor ( mrContext, 1.0, 1.0, 1.0, 1.0 );
1375 CGContextSetLineDash ( mrContext, 0, dashLengths, 2 );
1376 CGContextSetLineWidth( mrContext, 2.0);
1377 CGContextStrokePath ( mrContext );
1379 else if ( nSalFlags & SAL_INVERT_50 )
1381 CGContextSetBlendMode(mrContext, kCGBlendModeDifference);
1382 Pattern50Fill();
1384 else // just invert
1386 CGContextSetBlendMode( mrContext, kCGBlendModeDifference );
1387 CGContextSetRGBFillColor( mrContext, 1.0, 1.0, 1.0, 1.0 );
1388 CGContextFillPath( mrContext );
1390 const CGRect aRefreshRect = CGContextGetClipBoundingBox(mrContext);
1391 CGContextRestoreGState( mrContext);
1392 delete [] CGpoints;
1393 RefreshRect( aRefreshRect );
1397 // -----------------------------------------------------------------------
1399 BOOL AquaSalGraphics::drawEPS( long nX, long nY, long nWidth, long nHeight,
1400 void* pEpsData, ULONG nByteCount )
1402 // convert the raw data to an NSImageRef
1403 NSData* xNSData = [NSData dataWithBytes:(void*)pEpsData length:(int)nByteCount];
1404 NSImageRep* xEpsImage = [NSEPSImageRep imageRepWithData: xNSData];
1405 if( !xEpsImage )
1406 return false;
1408 // get the target context
1409 if( !CheckContext() )
1410 return false;
1412 // NOTE: flip drawing, else the nsimage would be drawn upside down
1413 CGContextSaveGState( mrContext );
1414 // CGContextTranslateCTM( mrContext, 0, +mnHeight );
1415 CGContextScaleCTM( mrContext, +1, -1 );
1416 nY = /*mnHeight*/ - (nY + nHeight);
1418 // prepare the target context
1419 NSGraphicsContext* pOrigNSCtx = [NSGraphicsContext currentContext];
1420 NSGraphicsContext* pDrawNSCtx = [NSGraphicsContext graphicsContextWithGraphicsPort: mrContext flipped: IsFlipped()];
1421 [NSGraphicsContext setCurrentContext: pDrawNSCtx];
1422 // draw the EPS
1423 const NSRect aDstRect = {{nX,nY},{nWidth,nHeight}};
1424 const BOOL bOK = [xEpsImage drawInRect: aDstRect];
1425 CGContextRestoreGState( mrContext );
1426 // mark the destination rectangle as updated
1427 RefreshRect( aDstRect );
1428 // restore the NSGraphicsContext, TODO: do we need this?
1429 [NSGraphicsContext setCurrentContext: pOrigNSCtx];
1431 return bOK;
1434 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
1435 bool AquaSalGraphics::drawAlphaBitmap( const SalTwoRect& rTR,
1436 const SalBitmap& rSrcBitmap, const SalBitmap& rAlphaBmp )
1438 // An image mask can't have a depth > 8 bits (should be 1 to 8 bits)
1439 if( rAlphaBmp.GetBitCount() > 8 )
1440 return false;
1442 // are these two tests really necessary? (see vcl/unx/source/gdi/salgdi2.cxx)
1443 // horizontal/vertical mirroring not implemented yet
1444 if( rTR.mnDestWidth < 0 || rTR.mnDestHeight < 0 )
1445 return false;
1447 const AquaSalBitmap& rSrcSalBmp = static_cast<const AquaSalBitmap&>(rSrcBitmap);
1448 const AquaSalBitmap& rMaskSalBmp = static_cast<const AquaSalBitmap&>(rAlphaBmp);
1450 CGImageRef xMaskedImage = rSrcSalBmp.CreateWithMask( rMaskSalBmp, rTR.mnSrcX, rTR.mnSrcY, rTR.mnSrcWidth, rTR.mnSrcHeight );
1451 if( !xMaskedImage )
1452 return false;
1454 if ( CheckContext() )
1456 const CGRect aDstRect = {{rTR.mnDestX, rTR.mnDestY}, {rTR.mnDestWidth, rTR.mnDestHeight}};
1457 CGContextDrawImage( mrContext, aDstRect, xMaskedImage );
1458 RefreshRect( aDstRect );
1461 CGImageRelease(xMaskedImage);
1462 return true;
1465 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
1466 bool AquaSalGraphics::drawAlphaRect( long nX, long nY, long nWidth,
1467 long nHeight, sal_uInt8 nTransparency )
1469 if( !CheckContext() )
1470 return true;
1472 // save the current state
1473 CGContextSaveGState( mrContext );
1474 CGContextSetAlpha( mrContext, (100-nTransparency) * (1.0/100) );
1476 CGRect aRect = {{nX,nY},{nWidth-1,nHeight-1}};
1477 if( IsPenVisible() )
1479 aRect.origin.x += 0.5;
1480 aRect.origin.y += 0.5;
1483 CGContextBeginPath( mrContext );
1484 CGContextAddRect( mrContext, aRect );
1485 CGContextDrawPath( mrContext, kCGPathFill );
1487 // restore state
1488 CGContextRestoreGState(mrContext);
1489 RefreshRect( aRect );
1490 return true;
1493 // -----------------------------------------------------------------------
1495 void AquaSalGraphics::SetTextColor( SalColor nSalColor )
1497 RGBColor color;
1498 color.red = (unsigned short) ( SALCOLOR_RED(nSalColor) * 65535.0 / 255.0 );
1499 color.green = (unsigned short) ( SALCOLOR_GREEN(nSalColor) * 65535.0 / 255.0 );
1500 color.blue = (unsigned short) ( SALCOLOR_BLUE(nSalColor) * 65535.0 / 255.0 );
1502 ATSUAttributeTag aTag = kATSUColorTag;
1503 ByteCount aValueSize = sizeof( color );
1504 ATSUAttributeValuePtr aValue = &color;
1506 OSStatus err = ATSUSetAttributes( maATSUStyle, 1, &aTag, &aValueSize, &aValue );
1507 DBG_ASSERT( (err==noErr), "AquaSalGraphics::SetTextColor() : Could not set font attributes!\n");
1508 if( err != noErr )
1509 return;
1512 // -----------------------------------------------------------------------
1514 void AquaSalGraphics::GetFontMetric( ImplFontMetricData* pMetric )
1516 // get the ATSU font metrics (in point units)
1517 // of the font that has eventually been size-limited
1519 ATSUFontID fontId;
1520 OSStatus err = ATSUGetAttribute( maATSUStyle, kATSUFontTag, sizeof(ATSUFontID), &fontId, 0 );
1521 DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font id\n");
1523 ATSFontMetrics aMetrics;
1524 ATSFontRef rFont = FMGetATSFontRefFromFont( fontId );
1525 err = ATSFontGetHorizontalMetrics ( rFont, kATSOptionFlagsDefault, &aMetrics );
1526 DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font metrics\n");
1527 if( err != noErr )
1528 return;
1530 // all ATS fonts are scalable fonts
1531 pMetric->mbScalableFont = true;
1532 // TODO: check if any kerning is possible
1533 pMetric->mbKernableFont = true;
1535 // convert into VCL font metrics (in unscaled pixel units)
1537 Fixed ptSize;
1538 err = ATSUGetAttribute( maATSUStyle, kATSUSizeTag, sizeof(Fixed), &ptSize, 0);
1539 DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font size\n");
1540 const double fPointSize = Fix2X( ptSize );
1542 // convert quartz units to pixel units
1543 // please see the comment in AquaSalGraphics::SetFont() for details
1544 const double fPixelSize = (mfFontScale * mfFakeDPIScale * fPointSize);
1545 pMetric->mnAscent = static_cast<long>(+aMetrics.ascent * fPixelSize + 0.5);
1546 pMetric->mnDescent = static_cast<long>(-aMetrics.descent * fPixelSize + 0.5);
1547 const long nExtDescent = static_cast<long>((-aMetrics.descent + aMetrics.leading) * fPixelSize + 0.5);
1548 pMetric->mnExtLeading = nExtDescent - pMetric->mnDescent;
1549 pMetric->mnIntLeading = 0;
1550 // ATSFontMetrics.avgAdvanceWidth is obsolete, so it is usually set to zero
1551 // since ImplFontMetricData::mnWidth is only used for stretching/squeezing fonts
1552 // setting this width to the pixel height of the fontsize is good enough
1553 // it also makes the calculation of the stretch factor simple
1554 pMetric->mnWidth = static_cast<long>(mfFontStretch * fPixelSize + 0.5);
1557 // -----------------------------------------------------------------------
1559 ULONG AquaSalGraphics::GetKernPairs( ULONG nPairs, ImplKernPairData* pKernPairs )
1561 return 0;
1564 // -----------------------------------------------------------------------
1566 static bool AddTempFontDir( const char* pDir )
1568 FSRef aPathFSRef;
1569 Boolean bIsDirectory = true;
1570 OSStatus eStatus = FSPathMakeRef( reinterpret_cast<const UInt8*>(pDir), &aPathFSRef, &bIsDirectory );
1571 DBG_ASSERTWARNING( (eStatus==noErr) && bIsDirectory, "vcl AddTempFontDir() with invalid directory name!" );
1572 if( eStatus != noErr )
1573 return false;
1575 // TODO: deactivate ATSFontContainerRef when closing app
1576 ATSFontContainerRef aATSFontContainer;
1578 const ATSFontContext eContext = kATSFontContextLocal; // TODO: *Global???
1579 #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
1580 eStatus = ::ATSFontActivateFromFileReference( &aPathFSRef,
1581 eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault,
1582 &aATSFontContainer );
1583 #else
1584 FSSpec aPathFSSpec;
1585 eStatus = ::FSGetCatalogInfo( &aPathFSRef, kFSCatInfoNone,
1586 NULL, NULL, &aPathFSSpec, NULL );
1587 if( eStatus != noErr )
1588 return false;
1590 eStatus = ::ATSFontActivateFromFileSpecification( &aPathFSSpec,
1591 eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault,
1592 &aATSFontContainer );
1593 #endif
1594 if( eStatus != noErr )
1595 return false;
1597 return true;
1600 static bool AddLocalTempFontDirs( void )
1602 static bool bFirst = true;
1603 if( !bFirst )
1604 return false;
1605 bFirst = false;
1607 // add private font files found in brand and base layer
1609 rtl::OUString aBrandStr( RTL_CONSTASCII_USTRINGPARAM( "$BRAND_BASE_DIR" ) );
1610 rtl_bootstrap_expandMacros( &aBrandStr.pData );
1611 rtl::OUString aBrandSysPath;
1612 OSL_VERIFY( osl_getSystemPathFromFileURL( aBrandStr.pData, &aBrandSysPath.pData ) == osl_File_E_None );
1614 rtl::OStringBuffer aBrandFontDir( aBrandSysPath.getLength()*2 );
1615 aBrandFontDir.append( rtl::OUStringToOString( aBrandSysPath, RTL_TEXTENCODING_UTF8 ) );
1616 aBrandFontDir.append( "/share/fonts/truetype/" );
1617 bool bBrandSuccess = AddTempFontDir( aBrandFontDir.getStr() );
1619 rtl::OUString aBaseStr( RTL_CONSTASCII_USTRINGPARAM( "$OOO_BASE_DIR" ) );
1620 rtl_bootstrap_expandMacros( &aBaseStr.pData );
1621 rtl::OUString aBaseSysPath;
1622 OSL_VERIFY( osl_getSystemPathFromFileURL( aBaseStr.pData, &aBaseSysPath.pData ) == osl_File_E_None );
1624 rtl::OStringBuffer aBaseFontDir( aBaseSysPath.getLength()*2 );
1625 aBaseFontDir.append( rtl::OUStringToOString( aBaseSysPath, RTL_TEXTENCODING_UTF8 ) );
1626 aBaseFontDir.append( "/share/fonts/truetype/" );
1627 bool bBaseSuccess = AddTempFontDir( aBaseFontDir.getStr() );
1629 return bBrandSuccess && bBaseSuccess;
1632 void AquaSalGraphics::GetDevFontList( ImplDevFontList* pFontList )
1634 DBG_ASSERT( pFontList, "AquaSalGraphics::GetDevFontList(NULL) !");
1636 AddLocalTempFontDirs();
1638 // The idea is to cache the list of system fonts once it has been generated.
1639 // SalData seems to be a good place for this caching. However we have to
1640 // carefully make the access to the font list thread-safe. If we register
1641 // a font-change event handler to update the font list in case fonts have
1642 // changed on the system we have to lock access to the list. The right
1643 // way to do that is the solar mutex since GetDevFontList is protected
1644 // through it as should be all event handlers
1646 SalData* pSalData = GetSalData();
1647 if (pSalData->mpFontList == NULL)
1648 pSalData->mpFontList = new SystemFontList();
1650 // Copy all ImplFontData objects contained in the SystemFontList
1651 pSalData->mpFontList->AnnounceFonts( *pFontList );
1654 // -----------------------------------------------------------------------
1656 bool AquaSalGraphics::AddTempDevFont( ImplDevFontList* pFontList,
1657 const String& rFontFileURL, const String& rFontName )
1659 ::rtl::OUString aUSytemPath;
1660 OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFontFileURL, aUSytemPath ) );
1662 FSRef aNewRef;
1663 Boolean bIsDirectory = true;
1664 ::rtl::OString aCFileName = rtl::OUStringToOString( aUSytemPath, RTL_TEXTENCODING_UTF8 );
1665 OSStatus eStatus = FSPathMakeRef( (UInt8*)aCFileName.getStr(), &aNewRef, &bIsDirectory );
1666 DBG_ASSERT( (eStatus==noErr) && !bIsDirectory, "vcl AddTempDevFont() with invalid fontfile name!" );
1667 if( eStatus != noErr )
1668 return false;
1670 ATSFontContainerRef oContainer;
1672 const ATSFontContext eContext = kATSFontContextLocal; // TODO: *Global???
1673 #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
1674 eStatus = ::ATSFontActivateFromFileReference( &aNewRef,
1675 eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault,
1676 &oContainer );
1677 #else
1678 FSSpec aFontFSSpec;
1679 eStatus = ::FSGetCatalogInfo( &aNewRef, kFSCatInfoNone,
1680 NULL, NULL, &aFontFSSpec, NULL );
1681 if( eStatus != noErr )
1682 return false;
1684 eStatus = ::ATSFontActivateFromFileSpecification( &aFontFSSpec,
1685 eContext, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault,
1686 &oContainer );
1687 #endif
1688 if( eStatus != noErr )
1689 return false;
1691 // TODO: ATSFontDeactivate( oContainer ) when fonts are no longer needed
1692 // TODO: register new ImplMacFontdata in pFontList
1693 return true;
1696 // -----------------------------------------------------------------------
1698 // callbacks from ATSUGlyphGetCubicPaths() fore GetGlyphOutline()
1699 struct GgoData { basegfx::B2DPolygon maPolygon; basegfx::B2DPolyPolygon* mpPolyPoly; };
1701 static OSStatus GgoLineToProc( const Float32Point* pPoint, void* pData )
1703 basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon;
1704 const basegfx::B2DPoint aB2DPoint( pPoint->x, pPoint->y );
1705 rPolygon.append( aB2DPoint );
1706 return noErr;
1709 static OSStatus GgoCurveToProc( const Float32Point* pCP1, const Float32Point* pCP2,
1710 const Float32Point* pPoint, void* pData )
1712 basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon;
1713 const sal_uInt32 nPointCount = rPolygon.count();
1714 const basegfx::B2DPoint aB2DControlPoint1( pCP1->x, pCP1->y );
1715 rPolygon.setNextControlPoint( nPointCount-1, aB2DControlPoint1 );
1716 const basegfx::B2DPoint aB2DEndPoint( pPoint->x, pPoint->y );
1717 rPolygon.append( aB2DEndPoint );
1718 const basegfx::B2DPoint aB2DControlPoint2( pCP2->x, pCP2->y );
1719 rPolygon.setPrevControlPoint( nPointCount, aB2DControlPoint2 );
1720 return noErr;
1723 static OSStatus GgoClosePathProc( void* pData )
1725 GgoData* pGgoData = static_cast<GgoData*>(pData);
1726 basegfx::B2DPolygon& rPolygon = pGgoData->maPolygon;
1727 if( rPolygon.count() > 0 )
1728 pGgoData->mpPolyPoly->append( rPolygon );
1729 rPolygon.clear();
1730 return noErr;
1733 static OSStatus GgoMoveToProc( const Float32Point* pPoint, void* pData )
1735 GgoClosePathProc( pData );
1736 OSStatus eStatus = GgoLineToProc( pPoint, pData );
1737 return eStatus;
1740 BOOL AquaSalGraphics::GetGlyphOutline( long nGlyphId, basegfx::B2DPolyPolygon& rPolyPoly )
1742 GgoData aGgoData;
1743 aGgoData.mpPolyPoly = &rPolyPoly;
1744 rPolyPoly.clear();
1746 ATSUStyle rATSUStyle = maATSUStyle; // TODO: handle glyph fallback when CWS pdffix02 is integrated
1747 OSStatus eGgoStatus = noErr;
1748 OSStatus eStatus = ATSUGlyphGetCubicPaths( rATSUStyle, nGlyphId,
1749 GgoMoveToProc, GgoLineToProc, GgoCurveToProc, GgoClosePathProc,
1750 &aGgoData, &eGgoStatus );
1751 if( (eStatus != noErr) ) // TODO: why is (eGgoStatus!=noErr) when curves are involved?
1752 return false;
1754 GgoClosePathProc( &aGgoData );
1755 if( mfFontScale != 1.0 ) {
1756 basegfx::B2DHomMatrix aScale;
1757 aScale.scale( +mfFontScale, +mfFontScale );
1758 rPolyPoly.transform( aScale );
1760 return true;
1763 // -----------------------------------------------------------------------
1765 long AquaSalGraphics::GetGraphicsWidth() const
1767 long w = 0;
1768 if( mrContext && (mbWindow || mbVirDev) )
1770 w = mnWidth;
1773 if( w == 0 )
1775 if( mbWindow && mpFrame )
1776 w = mpFrame->maGeometry.nWidth;
1779 return w;
1782 // -----------------------------------------------------------------------
1784 BOOL AquaSalGraphics::GetGlyphBoundRect( long nGlyphId, Rectangle& rRect )
1786 ATSUStyle rATSUStyle = maATSUStyle; // TODO: handle glyph fallback
1787 GlyphID aGlyphId = nGlyphId;
1788 ATSGlyphScreenMetrics aGlyphMetrics;
1789 OSStatus eStatus = ATSUGlyphGetScreenMetrics( rATSUStyle,
1790 1, &aGlyphId, 0, FALSE, !mbNonAntialiasedText, &aGlyphMetrics );
1791 if( eStatus != noErr )
1792 return false;
1794 const long nMinX = (long)(+aGlyphMetrics.topLeft.x * mfFontScale - 0.5);
1795 const long nMaxX = (long)(aGlyphMetrics.width * mfFontScale + 0.5) + nMinX;
1796 const long nMinY = (long)(-aGlyphMetrics.topLeft.y * mfFontScale - 0.5);
1797 const long nMaxY = (long)(aGlyphMetrics.height * mfFontScale + 0.5) + nMinY;
1798 rRect = Rectangle( nMinX, nMinY, nMaxX, nMaxY );
1799 return true;
1802 // -----------------------------------------------------------------------
1804 void AquaSalGraphics::GetDevFontSubstList( OutputDevice* )
1806 // nothing to do since there are no device-specific fonts on Aqua
1809 // -----------------------------------------------------------------------
1811 void AquaSalGraphics::DrawServerFontLayout( const ServerFontLayout& )
1815 // -----------------------------------------------------------------------
1817 USHORT AquaSalGraphics::SetFont( ImplFontSelectData* pReqFont, int nFallbackLevel )
1819 if( !pReqFont )
1821 ATSUClearStyle( maATSUStyle );
1822 mpMacFontData = NULL;
1823 return 0;
1826 // store the requested device font entry
1827 const ImplMacFontData* pMacFont = static_cast<const ImplMacFontData*>( pReqFont->mpFontData );
1828 mpMacFontData = pMacFont;
1830 // convert pixel units (as seen by upper layers) to typographic point units
1831 double fScaledAtsHeight = pReqFont->mfExactHeight;
1832 // avoid Fixed16.16 overflows by limiting the ATS font size
1833 static const float fMaxAtsHeight = 144.0;
1834 if( fScaledAtsHeight <= fMaxAtsHeight )
1835 mfFontScale = 1.0;
1836 else
1838 mfFontScale = fScaledAtsHeight / fMaxAtsHeight;
1839 fScaledAtsHeight = fMaxAtsHeight;
1841 Fixed fFixedSize = FloatToFixed( fScaledAtsHeight );
1842 // enable bold-emulation if needed
1843 Boolean bFakeBold = FALSE;
1844 if( (pReqFont->GetWeight() >= WEIGHT_BOLD)
1845 && (pMacFont->GetWeight() < WEIGHT_SEMIBOLD) )
1846 bFakeBold = TRUE;
1847 // enable italic-emulation if needed
1848 Boolean bFakeItalic = FALSE;
1849 if( ((pReqFont->GetSlant() == ITALIC_NORMAL) || (pReqFont->GetSlant() == ITALIC_OBLIQUE))
1850 && !((pMacFont->GetSlant() == ITALIC_NORMAL) || (pMacFont->GetSlant() == ITALIC_OBLIQUE)) )
1851 bFakeItalic = TRUE;
1853 // enable/disable antialiased text
1854 mbNonAntialiasedText = pReqFont->mbNonAntialiased;
1855 UInt32 nStyleRenderingOptions = kATSStyleNoOptions;
1856 if( pReqFont->mbNonAntialiased )
1857 nStyleRenderingOptions |= kATSStyleNoAntiAliasing;
1859 // set horizontal/vertical mode
1860 ATSUVerticalCharacterType aVerticalCharacterType = kATSUStronglyHorizontal;
1861 if( pReqFont->mbVertical )
1862 aVerticalCharacterType = kATSUStronglyVertical;
1864 // prepare ATS-fontid as type matching to the kATSUFontTag request
1865 ATSUFontID nFontID = static_cast<ATSUFontID>(pMacFont->GetFontId());
1867 // update ATSU style attributes with requested font parameters
1868 // TODO: no need to set styles which are already defaulted
1870 const ATSUAttributeTag aTag[] =
1872 kATSUFontTag,
1873 kATSUSizeTag,
1874 kATSUQDBoldfaceTag,
1875 kATSUQDItalicTag,
1876 kATSUStyleRenderingOptionsTag,
1877 kATSUVerticalCharacterTag
1880 const ByteCount aValueSize[] =
1882 sizeof(ATSUFontID),
1883 sizeof(fFixedSize),
1884 sizeof(bFakeBold),
1885 sizeof(bFakeItalic),
1886 sizeof(nStyleRenderingOptions),
1887 sizeof(aVerticalCharacterType)
1890 const ATSUAttributeValuePtr aValue[] =
1892 &nFontID,
1893 &fFixedSize,
1894 &bFakeBold,
1895 &bFakeItalic,
1896 &nStyleRenderingOptions,
1897 &aVerticalCharacterType
1900 static const int nTagCount = sizeof(aTag) / sizeof(*aTag);
1901 OSStatus eStatus = ATSUSetAttributes( maATSUStyle, nTagCount,
1902 aTag, aValueSize, aValue );
1903 // reset ATSUstyle if there was an error
1904 if( eStatus != noErr )
1906 DBG_WARNING( "AquaSalGraphics::SetFont() : Could not set font attributes!\n");
1907 ATSUClearStyle( maATSUStyle );
1908 mpMacFontData = NULL;
1909 return 0;
1912 // prepare font stretching
1913 const ATSUAttributeTag aMatrixTag = kATSUFontMatrixTag;
1914 if( (pReqFont->mnWidth == 0) || (pReqFont->mnWidth == pReqFont->mnHeight) )
1916 mfFontStretch = 1.0;
1917 ATSUClearAttributes( maATSUStyle, 1, &aMatrixTag );
1919 else
1921 mfFontStretch = (float)pReqFont->mnWidth / pReqFont->mnHeight;
1922 CGAffineTransform aMatrix = CGAffineTransformMakeScale( mfFontStretch, 1.0F );
1923 const ATSUAttributeValuePtr aAttr = &aMatrix;
1924 const ByteCount aMatrixBytes = sizeof(aMatrix);
1925 eStatus = ATSUSetAttributes( maATSUStyle, 1, &aMatrixTag, &aMatrixBytes, &aAttr );
1926 DBG_ASSERT( (eStatus==noErr), "AquaSalGraphics::SetFont() : Could not set font matrix\n");
1929 // prepare font rotation
1930 mnATSUIRotation = FloatToFixed( pReqFont->mnOrientation / 10.0 );
1932 #if OSL_DEBUG_LEVEL > 3
1933 fprintf( stderr, "SetFont to (\"%s\", \"%s\", fontid=%d) for (\"%s\" \"%s\" weight=%d, slant=%d size=%dx%d orientation=%d)\n",
1934 ::rtl::OUStringToOString( pMacFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ).getStr(),
1935 ::rtl::OUStringToOString( pMacFont->GetStyleName(), RTL_TEXTENCODING_UTF8 ).getStr(),
1936 (int)nFontID,
1937 ::rtl::OUStringToOString( pReqFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ).getStr(),
1938 ::rtl::OUStringToOString( pReqFont->GetStyleName(), RTL_TEXTENCODING_UTF8 ).getStr(),
1939 pReqFont->GetWeight(),
1940 pReqFont->GetSlant(),
1941 pReqFont->mnHeight,
1942 pReqFont->mnWidth,
1943 pReqFont->mnOrientation);
1944 #endif
1946 return 0;
1949 // -----------------------------------------------------------------------
1951 ImplFontCharMap* AquaSalGraphics::GetImplFontCharMap() const
1953 if( !mpMacFontData )
1954 return ImplFontCharMap::GetDefaultMap();
1956 return mpMacFontData->GetImplFontCharMap();
1959 // -----------------------------------------------------------------------
1961 // fake a SFNT font directory entry for a font table
1962 // see http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html#Directory
1963 static void FakeDirEntry( FourCharCode eFCC, ByteCount nOfs, ByteCount nLen,
1964 const unsigned char* /*pData*/, unsigned char*& rpDest )
1966 // write entry tag
1967 rpDest[ 0] = (char)(eFCC >> 24);
1968 rpDest[ 1] = (char)(eFCC >> 16);
1969 rpDest[ 2] = (char)(eFCC >> 8);
1970 rpDest[ 3] = (char)(eFCC >> 0);
1971 // TODO: get entry checksum and write it
1972 // not too important since the subsetter doesn't care currently
1973 // for( pData+nOfs ... pData+nOfs+nLen )
1974 // write entry offset
1975 rpDest[ 8] = (char)(nOfs >> 24);
1976 rpDest[ 9] = (char)(nOfs >> 16);
1977 rpDest[10] = (char)(nOfs >> 8);
1978 rpDest[11] = (char)(nOfs >> 0);
1979 // write entry length
1980 rpDest[12] = (char)(nLen >> 24);
1981 rpDest[13] = (char)(nLen >> 16);
1982 rpDest[14] = (char)(nLen >> 8);
1983 rpDest[15] = (char)(nLen >> 0);
1984 // advance to next entry
1985 rpDest += 16;
1988 static bool GetRawFontData( const ImplFontData* pFontData,
1989 ByteVector& rBuffer, bool* pJustCFF )
1991 const ImplMacFontData* pMacFont = static_cast<const ImplMacFontData*>(pFontData);
1992 const ATSUFontID nFontId = static_cast<ATSUFontID>(pMacFont->GetFontId());
1993 ATSFontRef rFont = FMGetATSFontRefFromFont( nFontId );
1995 ByteCount nCffLen = 0;
1996 OSStatus eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, 0, NULL, &nCffLen);
1997 if( pJustCFF != NULL )
1999 *pJustCFF = (eStatus == noErr) && (nCffLen > 0);
2000 if( *pJustCFF )
2002 rBuffer.resize( nCffLen );
2003 eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, nCffLen, (void*)&rBuffer[0], &nCffLen);
2004 if( (eStatus != noErr) || (nCffLen <= 0) )
2005 return false;
2006 return true;
2010 // get font table availability and size in bytes
2011 ByteCount nHeadLen = 0;
2012 eStatus = ATSFontGetTable( rFont, GetTag("head"), 0, 0, NULL, &nHeadLen);
2013 if( (eStatus != noErr) || (nHeadLen <= 0) )
2014 return false;
2015 ByteCount nMaxpLen = 0;
2016 eStatus = ATSFontGetTable( rFont, GetTag("maxp"), 0, 0, NULL, &nMaxpLen);
2017 if( (eStatus != noErr) || (nMaxpLen <= 0) )
2018 return false;
2019 ByteCount nCmapLen = 0;
2020 eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, 0, NULL, &nCmapLen);
2021 if( (eStatus != noErr) || (nCmapLen <= 0) )
2022 return false;
2023 ByteCount nNameLen = 0;
2024 eStatus = ATSFontGetTable( rFont, GetTag("name"), 0, 0, NULL, &nNameLen);
2025 if( (eStatus != noErr) || (nNameLen <= 0) )
2026 return false;
2027 ByteCount nHheaLen = 0;
2028 eStatus = ATSFontGetTable( rFont, GetTag("hhea"), 0, 0, NULL, &nHheaLen);
2029 if( (eStatus != noErr) || (nHheaLen <= 0) )
2030 return false;
2031 ByteCount nHmtxLen = 0;
2032 eStatus = ATSFontGetTable( rFont, GetTag("hmtx"), 0, 0, NULL, &nHmtxLen);
2033 if( (eStatus != noErr) || (nHmtxLen <= 0) )
2034 return false;
2036 // get the glyph outline tables
2037 ByteCount nLocaLen = 0;
2038 ByteCount nGlyfLen = 0;
2039 if( (eStatus != noErr) || (nCffLen <= 0) )
2041 eStatus = ATSFontGetTable( rFont, GetTag("loca"), 0, 0, NULL, &nLocaLen);
2042 if( (eStatus != noErr) || (nLocaLen <= 0) )
2043 return false;
2044 eStatus = ATSFontGetTable( rFont, GetTag("glyf"), 0, 0, NULL, &nGlyfLen);
2045 if( (eStatus != noErr) || (nGlyfLen <= 0) )
2046 return false;
2049 ByteCount nPrepLen=0, nCvtLen=0, nFpgmLen=0;
2050 if( nGlyfLen ) // TODO: reduce PDF size by making hint subsetting optional
2052 eStatus = ATSFontGetTable( rFont, GetTag("prep"), 0, 0, NULL, &nPrepLen);
2053 eStatus = ATSFontGetTable( rFont, GetTag("cvt "), 0, 0, NULL, &nCvtLen);
2054 eStatus = ATSFontGetTable( rFont, GetTag("fpgm"), 0, 0, NULL, &nFpgmLen);
2057 // prepare a byte buffer for a fake font
2058 int nTableCount = 7;
2059 nTableCount += (nPrepLen>0) + (nCvtLen>0) + (nFpgmLen>0) + (nGlyfLen>0);
2060 const ByteCount nFdirLen = 12 + 16*nTableCount;
2061 ByteCount nTotalLen = nFdirLen;
2062 nTotalLen += nHeadLen + nMaxpLen + nNameLen + nCmapLen;
2063 if( nGlyfLen )
2064 nTotalLen += nLocaLen + nGlyfLen;
2065 else
2066 nTotalLen += nCffLen;
2067 nTotalLen += nHheaLen + nHmtxLen;
2068 nTotalLen += nPrepLen + nCvtLen + nFpgmLen;
2069 rBuffer.resize( nTotalLen );
2071 // fake a SFNT font directory header
2072 if( nTableCount < 16 )
2074 int nLog2 = 0;
2075 while( (nTableCount >> nLog2) > 1 ) ++nLog2;
2076 rBuffer[ 1] = 1; // Win-TTF style scaler
2077 rBuffer[ 5] = nTableCount; // table count
2078 rBuffer[ 7] = nLog2*16; // searchRange
2079 rBuffer[ 9] = nLog2; // entrySelector
2080 rBuffer[11] = (nTableCount-nLog2)*16; // rangeShift
2083 // get font table raw data and update the fake directory entries
2084 ByteCount nOfs = nFdirLen;
2085 unsigned char* pFakeEntry = &rBuffer[12];
2086 eStatus = ATSFontGetTable( rFont, GetTag("cmap"), 0, nCmapLen, (void*)&rBuffer[nOfs], &nCmapLen);
2087 FakeDirEntry( GetTag("cmap"), nOfs, nCmapLen, &rBuffer[0], pFakeEntry );
2088 nOfs += nCmapLen;
2089 if( nCvtLen ) {
2090 eStatus = ATSFontGetTable( rFont, GetTag("cvt "), 0, nCvtLen, (void*)&rBuffer[nOfs], &nCvtLen);
2091 FakeDirEntry( GetTag("cvt "), nOfs, nCvtLen, &rBuffer[0], pFakeEntry );
2092 nOfs += nCvtLen;
2094 if( nFpgmLen ) {
2095 eStatus = ATSFontGetTable( rFont, GetTag("fpgm"), 0, nFpgmLen, (void*)&rBuffer[nOfs], &nFpgmLen);
2096 FakeDirEntry( GetTag("fpgm"), nOfs, nFpgmLen, &rBuffer[0], pFakeEntry );
2097 nOfs += nFpgmLen;
2099 if( nCffLen ) {
2100 eStatus = ATSFontGetTable( rFont, GetTag("CFF "), 0, nCffLen, (void*)&rBuffer[nOfs], &nCffLen);
2101 FakeDirEntry( GetTag("CFF "), nOfs, nCffLen, &rBuffer[0], pFakeEntry );
2102 nOfs += nGlyfLen;
2103 } else {
2104 eStatus = ATSFontGetTable( rFont, GetTag("glyf"), 0, nGlyfLen, (void*)&rBuffer[nOfs], &nGlyfLen);
2105 FakeDirEntry( GetTag("glyf"), nOfs, nGlyfLen, &rBuffer[0], pFakeEntry );
2106 nOfs += nGlyfLen;
2107 eStatus = ATSFontGetTable( rFont, GetTag("loca"), 0, nLocaLen, (void*)&rBuffer[nOfs], &nLocaLen);
2108 FakeDirEntry( GetTag("loca"), nOfs, nLocaLen, &rBuffer[0], pFakeEntry );
2109 nOfs += nLocaLen;
2111 eStatus = ATSFontGetTable( rFont, GetTag("head"), 0, nHeadLen, (void*)&rBuffer[nOfs], &nHeadLen);
2112 FakeDirEntry( GetTag("head"), nOfs, nHeadLen, &rBuffer[0], pFakeEntry );
2113 nOfs += nHeadLen;
2114 eStatus = ATSFontGetTable( rFont, GetTag("hhea"), 0, nHheaLen, (void*)&rBuffer[nOfs], &nHheaLen);
2115 FakeDirEntry( GetTag("hhea"), nOfs, nHheaLen, &rBuffer[0], pFakeEntry );
2116 nOfs += nHheaLen;
2117 eStatus = ATSFontGetTable( rFont, GetTag("hmtx"), 0, nHmtxLen, (void*)&rBuffer[nOfs], &nHmtxLen);
2118 FakeDirEntry( GetTag("hmtx"), nOfs, nHmtxLen, &rBuffer[0], pFakeEntry );
2119 nOfs += nHmtxLen;
2120 eStatus = ATSFontGetTable( rFont, GetTag("maxp"), 0, nMaxpLen, (void*)&rBuffer[nOfs], &nMaxpLen);
2121 FakeDirEntry( GetTag("maxp"), nOfs, nMaxpLen, &rBuffer[0], pFakeEntry );
2122 nOfs += nMaxpLen;
2123 eStatus = ATSFontGetTable( rFont, GetTag("name"), 0, nNameLen, (void*)&rBuffer[nOfs], &nNameLen);
2124 FakeDirEntry( GetTag("name"), nOfs, nNameLen, &rBuffer[0], pFakeEntry );
2125 nOfs += nNameLen;
2126 if( nPrepLen ) {
2127 eStatus = ATSFontGetTable( rFont, GetTag("prep"), 0, nPrepLen, (void*)&rBuffer[nOfs], &nPrepLen);
2128 FakeDirEntry( GetTag("prep"), nOfs, nPrepLen, &rBuffer[0], pFakeEntry );
2129 nOfs += nPrepLen;
2132 DBG_ASSERT( (nOfs==nTotalLen), "AquaSalGraphics::CreateFontSubset (nOfs!=nTotalLen)");
2134 return true;
2137 BOOL AquaSalGraphics::CreateFontSubset( const rtl::OUString& rToFile,
2138 const ImplFontData* pFontData, long* pGlyphIDs, sal_uInt8* pEncoding,
2139 sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rInfo )
2141 // TODO: move more of the functionality here into the generic subsetter code
2143 // prepare the requested file name for writing the font-subset file
2144 rtl::OUString aSysPath;
2145 if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) )
2146 return FALSE;
2147 const rtl_TextEncoding aThreadEncoding = osl_getThreadTextEncoding();
2148 const ByteString aToFile( rtl::OUStringToOString( aSysPath, aThreadEncoding ) );
2150 // get the raw-bytes from the font to be subset
2151 ByteVector aBuffer;
2152 bool bCffOnly = false;
2153 if( !GetRawFontData( pFontData, aBuffer, &bCffOnly ) )
2154 return sal_False;
2156 // handle CFF-subsetting
2157 if( bCffOnly )
2159 // provide the raw-CFF data to the subsetter
2160 ByteCount nCffLen = aBuffer.size();
2161 rInfo.LoadFont( FontSubsetInfo::CFF_FONT, &aBuffer[0], nCffLen );
2163 // NOTE: assuming that all glyphids requested on Aqua are fully translated
2165 // make the subsetter provide the requested subset
2166 FILE* pOutFile = fopen( aToFile.GetBuffer(), "wb" );
2167 bool bRC = rInfo.CreateFontSubset( FontSubsetInfo::TYPE1_PFB, pOutFile, NULL,
2168 pGlyphIDs, pEncoding, nGlyphCount, pGlyphWidths );
2169 fclose( pOutFile );
2170 return bRC;
2173 // TODO: modernize psprint's horrible fontsubset C-API
2174 // this probably only makes sense after the switch to another SCM
2175 // that can preserve change history after file renames
2177 // prepare data for psprint's font subsetter
2178 TrueTypeFont* pSftFont = NULL;
2179 int nRC = ::OpenTTFontBuffer( (void*)&aBuffer[0], aBuffer.size(), 0, &pSftFont);
2180 if( nRC != SF_OK )
2181 return sal_False;
2183 // get details about the subsetted font
2184 TTGlobalFontInfo aTTInfo;
2185 ::GetTTGlobalFontInfo( pSftFont, &aTTInfo );
2186 rInfo.m_nFontType = FontSubsetInfo::SFNT_TTF;
2187 rInfo.m_aPSName = String( aTTInfo.psname, RTL_TEXTENCODING_UTF8 );
2188 rInfo.m_aFontBBox = Rectangle( Point( aTTInfo.xMin, aTTInfo.yMin ),
2189 Point( aTTInfo.xMax, aTTInfo.yMax ) );
2190 rInfo.m_nCapHeight = aTTInfo.yMax; // Well ...
2191 rInfo.m_nAscent = +aTTInfo.winAscent;
2192 rInfo.m_nDescent = -aTTInfo.winDescent;
2193 // mac fonts usually do not have an OS2-table
2194 // => get valid ascent/descent values from other tables
2195 if( !rInfo.m_nAscent )
2196 rInfo.m_nAscent = +aTTInfo.typoAscender;
2197 if( !rInfo.m_nAscent )
2198 rInfo.m_nAscent = +aTTInfo.ascender;
2199 if( !rInfo.m_nDescent )
2200 rInfo.m_nDescent = +aTTInfo.typoDescender;
2201 if( !rInfo.m_nDescent )
2202 rInfo.m_nDescent = -aTTInfo.descender;
2204 // subset glyphs and get their properties
2205 // take care that subset fonts require the NotDef glyph in pos 0
2206 int nOrigCount = nGlyphCount;
2207 USHORT aShortIDs[ 256 ];
2208 sal_uInt8 aTempEncs[ 256 ];
2210 int nNotDef = -1;
2211 for( int i = 0; i < nGlyphCount; ++i )
2213 aTempEncs[i] = pEncoding[i];
2214 sal_uInt32 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK;
2215 if( pGlyphIDs[i] & GF_ISCHAR )
2217 bool bVertical = (pGlyphIDs[i] & GF_ROTMASK) != 0;
2218 nGlyphIdx = ::MapChar( pSftFont, static_cast<sal_uInt16>(nGlyphIdx), bVertical );
2219 if( nGlyphIdx == 0 && pFontData->IsSymbolFont() )
2221 // #i12824# emulate symbol aliasing U+FXXX <-> U+0XXX
2222 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK;
2223 nGlyphIdx = (nGlyphIdx & 0xF000) ? (nGlyphIdx & 0x00FF) : (nGlyphIdx | 0xF000 );
2224 nGlyphIdx = ::MapChar( pSftFont, static_cast<sal_uInt16>(nGlyphIdx), bVertical );
2227 aShortIDs[i] = static_cast<USHORT>( nGlyphIdx );
2228 if( !nGlyphIdx )
2229 if( nNotDef < 0 )
2230 nNotDef = i; // first NotDef glyph found
2233 if( nNotDef != 0 )
2235 // add fake NotDef glyph if needed
2236 if( nNotDef < 0 )
2237 nNotDef = nGlyphCount++;
2239 // NotDef glyph must be in pos 0 => swap glyphids
2240 aShortIDs[ nNotDef ] = aShortIDs[0];
2241 aTempEncs[ nNotDef ] = aTempEncs[0];
2242 aShortIDs[0] = 0;
2243 aTempEncs[0] = 0;
2245 DBG_ASSERT( nGlyphCount < 257, "too many glyphs for subsetting" );
2247 // TODO: where to get bVertical?
2248 const bool bVertical = false;
2250 // fill the pGlyphWidths array
2251 // while making sure that the NotDef glyph is at index==0
2252 TTSimpleGlyphMetrics* pGlyphMetrics =
2253 ::GetTTSimpleGlyphMetrics( pSftFont, aShortIDs, nGlyphCount, bVertical );
2254 if( !pGlyphMetrics )
2255 return FALSE;
2256 sal_uInt16 nNotDefAdv = pGlyphMetrics[0].adv;
2257 pGlyphMetrics[0].adv = pGlyphMetrics[nNotDef].adv;
2258 pGlyphMetrics[nNotDef].adv = nNotDefAdv;
2259 for( int i = 0; i < nOrigCount; ++i )
2260 pGlyphWidths[i] = pGlyphMetrics[i].adv;
2261 free( pGlyphMetrics );
2263 // write subset into destination file
2264 nRC = ::CreateTTFromTTGlyphs( pSftFont, aToFile.GetBuffer(), aShortIDs,
2265 aTempEncs, nGlyphCount, 0, NULL, 0 );
2266 ::CloseTTFont(pSftFont);
2267 return (nRC == SF_OK);
2270 // -----------------------------------------------------------------------
2272 void AquaSalGraphics::GetGlyphWidths( const ImplFontData* pFontData, bool bVertical,
2273 Int32Vector& rGlyphWidths, Ucs2UIntMap& rUnicodeEnc )
2275 rGlyphWidths.clear();
2276 rUnicodeEnc.clear();
2278 if( pFontData->IsSubsettable() )
2280 ByteVector aBuffer;
2281 if( !GetRawFontData( pFontData, aBuffer, NULL ) )
2282 return;
2284 // TODO: modernize psprint's horrible fontsubset C-API
2285 // this probably only makes sense after the switch to another SCM
2286 // that can preserve change history after file renames
2288 // use the font subsetter to get the widths
2289 TrueTypeFont* pSftFont = NULL;
2290 int nRC = ::OpenTTFontBuffer( (void*)&aBuffer[0], aBuffer.size(), 0, &pSftFont);
2291 if( nRC != SF_OK )
2292 return;
2294 const int nGlyphCount = ::GetTTGlyphCount( pSftFont );
2295 if( nGlyphCount > 0 )
2297 // get glyph metrics
2298 rGlyphWidths.resize(nGlyphCount);
2299 std::vector<sal_uInt16> aGlyphIds(nGlyphCount);
2300 for( int i = 0; i < nGlyphCount; i++ )
2301 aGlyphIds[i] = static_cast<sal_uInt16>(i);
2302 const TTSimpleGlyphMetrics* pGlyphMetrics = ::GetTTSimpleGlyphMetrics(
2303 pSftFont, &aGlyphIds[0], nGlyphCount, bVertical );
2304 if( pGlyphMetrics )
2306 for( int i = 0; i < nGlyphCount; ++i )
2307 rGlyphWidths[i] = pGlyphMetrics[i].adv;
2308 free( (void*)pGlyphMetrics );
2311 const ImplFontCharMap* pMap = mpMacFontData->GetImplFontCharMap();
2312 DBG_ASSERT( pMap && pMap->GetCharCount(), "no charmap" );
2314 // get unicode<->glyph encoding
2315 int nCharCount = pMap->GetCharCount();
2316 sal_uInt32 nChar = pMap->GetFirstChar();
2317 for(; --nCharCount >= 0; nChar = pMap->GetNextChar( nChar ) )
2319 if( nChar > 0xFFFF ) // TODO: allow UTF-32 chars
2320 break;
2321 sal_Ucs nUcsChar = static_cast<sal_Ucs>(nChar);
2322 sal_uInt32 nGlyph = ::MapChar( pSftFont, nUcsChar, bVertical );
2323 if( nGlyph > 0 )
2324 rUnicodeEnc[ nUcsChar ] = nGlyph;
2328 ::CloseTTFont( pSftFont );
2330 else if( pFontData->IsEmbeddable() )
2332 // get individual character widths
2333 #if 0 // FIXME
2334 rWidths.reserve( 224 );
2335 for( sal_Unicode i = 32; i < 256; ++i )
2337 int nCharWidth = 0;
2338 if( ::GetCharWidth32W( mhDC, i, i, &nCharWidth ) )
2340 rUnicodeEnc[ i ] = rWidths.size();
2341 rWidths.push_back( nCharWidth );
2344 #else
2345 DBG_ERROR("not implemented for non-subsettable fonts!\n");
2346 #endif
2350 // -----------------------------------------------------------------------
2352 const Ucs2SIntMap* AquaSalGraphics::GetFontEncodingVector(
2353 const ImplFontData* pFontData, const Ucs2OStrMap** ppNonEncoded )
2355 return NULL;
2358 // -----------------------------------------------------------------------
2360 const void* AquaSalGraphics::GetEmbedFontData( const ImplFontData* pFontData,
2361 const sal_Ucs* pUnicodes,
2362 sal_Int32* pWidths,
2363 FontSubsetInfo& rInfo,
2364 long* pDataLen )
2366 return NULL;
2369 // -----------------------------------------------------------------------
2371 void AquaSalGraphics::FreeEmbedFontData( const void* pData, long nDataLen )
2373 // TODO: implementing this only makes sense when the implementation of
2374 // AquaSalGraphics::GetEmbedFontData() returns non-NULL
2375 DBG_ASSERT( (pData!=NULL), "AquaSalGraphics::FreeEmbedFontData() is not implemented\n");
2378 // -----------------------------------------------------------------------
2380 SystemFontData AquaSalGraphics::GetSysFontData( int /* nFallbacklevel */ ) const
2382 SystemFontData aSysFontData;
2383 OSStatus err;
2384 aSysFontData.nSize = sizeof( SystemFontData );
2386 // NOTE: Native ATSU font fallbacks are used, not the VCL fallbacks.
2387 ATSUFontID fontId;
2388 err = ATSUGetAttribute( maATSUStyle, kATSUFontTag, sizeof(fontId), &fontId, 0 );
2389 if (err) fontId = 0;
2390 aSysFontData.aATSUFontID = (void *) fontId;
2392 Boolean bFbold;
2393 err = ATSUGetAttribute( maATSUStyle, kATSUQDBoldfaceTag, sizeof(bFbold), &bFbold, 0 );
2394 if (err) bFbold = FALSE;
2395 aSysFontData.bFakeBold = (bool) bFbold;
2397 Boolean bFItalic;
2398 err = ATSUGetAttribute( maATSUStyle, kATSUQDItalicTag, sizeof(bFItalic), &bFItalic, 0 );
2399 if (err) bFItalic = FALSE;
2400 aSysFontData.bFakeItalic = (bool) bFItalic;
2402 ATSUVerticalCharacterType aVerticalCharacterType;
2403 err = ATSUGetAttribute( maATSUStyle, kATSUVerticalCharacterTag, sizeof(aVerticalCharacterType), &aVerticalCharacterType, 0 );
2404 if (!err && aVerticalCharacterType == kATSUStronglyVertical) {
2405 aSysFontData.bVerticalCharacterType = true;
2406 } else {
2407 aSysFontData.bVerticalCharacterType = false;
2410 aSysFontData.bAntialias = !mbNonAntialiasedText;
2412 return aSysFontData;
2415 // -----------------------------------------------------------------------
2417 SystemGraphicsData AquaSalGraphics::GetGraphicsData() const
2419 SystemGraphicsData aRes;
2420 aRes.nSize = sizeof(aRes);
2421 aRes.rCGContext = mrContext;
2422 return aRes;
2425 // -----------------------------------------------------------------------
2427 void AquaSalGraphics::SetXORMode( bool bSet, bool bInvertOnly )
2429 // return early if XOR mode remains unchanged
2430 if( mbPrinter )
2431 return;
2433 if( ! bSet && mnXorMode == 2 )
2435 CGContextSetBlendMode( mrContext, kCGBlendModeNormal );
2436 mnXorMode = 0;
2437 return;
2439 else if( bSet && bInvertOnly && mnXorMode == 0)
2441 CGContextSetBlendMode( mrContext, kCGBlendModeDifference );
2442 mnXorMode = 2;
2443 return;
2446 if( (mpXorEmulation == NULL) && !bSet )
2447 return;
2448 if( (mpXorEmulation != NULL) && (bSet == mpXorEmulation->IsEnabled()) )
2449 return;
2450 if( !CheckContext() )
2451 return;
2453 // prepare XOR emulation
2454 if( !mpXorEmulation )
2456 mpXorEmulation = new XorEmulation();
2457 mpXorEmulation->SetTarget( mnWidth, mnHeight, mnBitmapDepth, mrContext, mxLayer );
2460 // change the XOR mode
2461 if( bSet )
2463 mpXorEmulation->Enable();
2464 mrContext = mpXorEmulation->GetMaskContext();
2465 mnXorMode = 1;
2467 else
2469 mpXorEmulation->UpdateTarget();
2470 mpXorEmulation->Disable();
2471 mrContext = mpXorEmulation->GetTargetContext();
2472 mnXorMode = 0;
2476 // -----------------------------------------------------------------------
2478 // apply the XOR mask to the target context if active and dirty
2479 void AquaSalGraphics::ApplyXorContext()
2481 if( !mpXorEmulation )
2482 return;
2483 if( mpXorEmulation->UpdateTarget() )
2484 RefreshRect( 0, 0, mnWidth, mnHeight ); // TODO: refresh minimal changerect
2487 // ======================================================================
2489 XorEmulation::XorEmulation()
2490 : mxTargetLayer( NULL )
2491 , mxTargetContext( NULL )
2492 , mxMaskContext( NULL )
2493 , mxTempContext( NULL )
2494 , mpMaskBuffer( NULL )
2495 , mpTempBuffer( NULL )
2496 , mnBufferLongs( 0 )
2497 , mbIsEnabled( false )
2500 // ----------------------------------------------------------------------
2502 XorEmulation::~XorEmulation()
2504 Disable();
2505 SetTarget( 0, 0, 0, NULL, NULL );
2508 // -----------------------------------------------------------------------
2510 void XorEmulation::SetTarget( int nWidth, int nHeight, int nTargetDepth,
2511 CGContextRef xTargetContext, CGLayerRef xTargetLayer )
2513 // prepare to replace old mask+temp context
2514 if( mxMaskContext )
2516 // cleanup the mask context
2517 CGContextRelease( mxMaskContext );
2518 delete[] mpMaskBuffer;
2519 mxMaskContext = NULL;
2520 mpMaskBuffer = NULL;
2522 // cleanup the temp context if needed
2523 if( mxTempContext )
2525 CGContextRelease( mxTempContext );
2526 delete[] mpTempBuffer;
2527 mxTempContext = NULL;
2528 mpTempBuffer = NULL;
2532 // return early if there is nothing more to do
2533 if( !xTargetContext )
2534 return;
2536 // retarget drawing operations to the XOR mask
2537 mxTargetLayer = xTargetLayer;
2538 mxTargetContext = xTargetContext;
2540 // prepare creation of matching CGBitmaps
2541 CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace;
2542 CGBitmapInfo aCGBmpInfo = kCGImageAlphaNoneSkipFirst;
2543 int nBitDepth = nTargetDepth;
2544 if( !nBitDepth )
2545 nBitDepth = 32;
2546 int nBytesPerRow = (nBitDepth == 16) ? 2 : 4;
2547 const size_t nBitsPerComponent = (nBitDepth == 16) ? 5 : 8;
2548 if( nBitDepth <= 8 )
2550 aCGColorSpace = GetSalData()->mxGraySpace;
2551 aCGBmpInfo = kCGImageAlphaNone;
2552 nBytesPerRow = 1;
2554 nBytesPerRow *= nWidth;
2555 mnBufferLongs = (nHeight * nBytesPerRow + sizeof(ULONG)-1) / sizeof(ULONG);
2557 // create a XorMask context
2558 mpMaskBuffer = new ULONG[ mnBufferLongs ];
2559 mxMaskContext = ::CGBitmapContextCreate( mpMaskBuffer,
2560 nWidth, nHeight, nBitsPerComponent, nBytesPerRow,
2561 aCGColorSpace, aCGBmpInfo );
2562 // reset the XOR mask to black
2563 memset( mpMaskBuffer, 0, mnBufferLongs * sizeof(ULONG) );
2565 // a bitmap context will be needed for manual XORing
2566 // create one unless the target context is a bitmap context
2567 if( nTargetDepth )
2568 mpTempBuffer = (ULONG*)CGBitmapContextGetData( mxTargetContext );
2569 if( !mpTempBuffer )
2571 // create a bitmap context matching to the target context
2572 mpTempBuffer = new ULONG[ mnBufferLongs ];
2573 mxTempContext = ::CGBitmapContextCreate( mpTempBuffer,
2574 nWidth, nHeight, nBitsPerComponent, nBytesPerRow,
2575 aCGColorSpace, aCGBmpInfo );
2578 // initialize XOR mask context for drawing
2579 CGContextSetFillColorSpace( mxMaskContext, aCGColorSpace );
2580 CGContextSetStrokeColorSpace( mxMaskContext, aCGColorSpace );
2581 CGContextSetShouldAntialias( mxMaskContext, false );
2583 // improve the XorMask's XOR emulation a litte
2584 // NOTE: currently only enabled for monochrome contexts
2585 if( aCGColorSpace == GetSalData()->mxGraySpace )
2586 CGContextSetBlendMode( mxMaskContext, kCGBlendModeDifference );
2588 // intialize the transformation matrix to the drawing target
2589 const CGAffineTransform aCTM = CGContextGetCTM( xTargetContext );
2590 CGContextConcatCTM( mxMaskContext, aCTM );
2591 if( mxTempContext )
2592 CGContextConcatCTM( mxTempContext, aCTM );
2594 // initialize the default XorMask graphics state
2595 CGContextSaveGState( mxMaskContext );
2598 // ----------------------------------------------------------------------
2600 bool XorEmulation::UpdateTarget()
2602 if( !IsEnabled() )
2603 return false;
2605 // update the temp bitmap buffer if needed
2606 if( mxTempContext )
2607 CGContextDrawLayerAtPoint( mxTempContext, CGPointZero, mxTargetLayer );
2609 // do a manual XOR with the XorMask
2610 // this approach suffices for simple color manipulations
2611 // and also the complex-clipping-XOR-trick used in metafiles
2612 const ULONG* pSrc = mpMaskBuffer;
2613 ULONG* pDst = mpTempBuffer;
2614 for( int i = mnBufferLongs; --i >= 0;)
2615 *(pDst++) ^= *(pSrc++);
2617 // write back the XOR results to the target context
2618 if( mxTempContext )
2620 CGImageRef xXorImage = CGBitmapContextCreateImage( mxTempContext );
2621 const int nWidth = (int)CGImageGetWidth( xXorImage );
2622 const int nHeight = (int)CGImageGetHeight( xXorImage );
2623 // TODO: update minimal changerect
2624 const CGRect aFullRect = {{0,0},{nWidth,nHeight}};
2625 CGContextDrawImage( mxTargetContext, aFullRect, xXorImage );
2626 CGImageRelease( xXorImage );
2629 // reset the XorMask to black again
2630 // TODO: not needed for last update
2631 memset( mpMaskBuffer, 0, mnBufferLongs * sizeof(ULONG) );
2633 // TODO: return FALSE if target was not changed
2634 return true;
2637 // =======================================================================