xref: /trunk/main/vcl/aqua/source/gdi/salgdi.cxx (revision 7f5c89d5)
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 
~SystemFontList(void)62 SystemFontList::~SystemFontList( void )
63 {}
64 
65 // =======================================================================
66 
ImplMacTextStyle(const ImplFontSelectData & rReqFont)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 
~ImplMacTextStyle(void)76 ImplMacTextStyle::~ImplMacTextStyle( void )
77 {}
78 
79 // =======================================================================
80 
ImplMacFontData(const ImplDevFontAttributes & rDFA,sal_IntPtr nFontId)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 
ImplMacFontData(const ImplMacFontData & rSrc)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 
~ImplMacFontData()108 ImplMacFontData::~ImplMacFontData()
109 {
110 	if( mpCharMap )
111 		mpCharMap->DeReference();
112 }
113 
114 // -----------------------------------------------------------------------
115 
GetFontId() const116 sal_IntPtr ImplMacFontData::GetFontId() const
117 {
118     return reinterpret_cast<sal_IntPtr>( mnFontId);
119 }
120 
121 // -----------------------------------------------------------------------
122 
CreateFontInstance(ImplFontSelectData & rFSD) const123 ImplFontEntry* ImplMacFontData::CreateFontInstance(ImplFontSelectData& rFSD) const
124 {
125 	return new ImplFontEntry(rFSD);
126 }
127 
128 // -----------------------------------------------------------------------
129 
GetUShort(const unsigned char * p)130 static unsigned GetUShort( const unsigned char* p ){return((p[0]<<8)+p[1]);}
GetUInt(const unsigned char * p)131 static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);}
132 
GetImplFontCharMap() const133 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 
ReadOs2Table(void) const173 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 
ReadMacCmapEncoding(void) const210 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 
HasCJKSupport(void) const252 bool ImplMacFontData::HasCJKSupport( void ) const
253 {
254 	ReadOs2Table();
255 	if( !mbHasOs2Table )
256 		ReadMacCmapEncoding();
257 
258 	return mbHasCJKSupport;
259 }
260 
261 // =======================================================================
262 
AquaSalGraphics()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 
~AquaSalGraphics()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 
supportsOperation(OutDevSupportType eType) const309 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 
updateResolution()327 void AquaSalGraphics::updateResolution()
328 {
329     DBG_ASSERT( mbWindow, "updateResolution on inappropriate graphics" );
330 
331     initResolution( (mbWindow && mpFrame) ? mpFrame->getNSWindow() : nil );
332 }
333 
initResolution(NSWindow *)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 
GetResolution(sal_Int32 & rDPIX,sal_Int32 & rDPIY)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 
copyResolution(AquaSalGraphics & rGraphics)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 
GetBitCount()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 
AddPolygonToPath(CGMutablePathRef xPath,const::basegfx::B2DPolygon & rPolygon,bool bClosePath,bool bPixelSnap,bool bLineDraw)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 
AddPolyPolygonToPath(CGMutablePathRef xPath,const::basegfx::B2DPolyPolygon & rPolyPoly,bool bPixelSnap,bool bLineDraw)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 
ResetClipRegion()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 
setClipRegion(const Region & i_rClip)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 
SetLineColor()599 void AquaSalGraphics::SetLineColor()
600 {
601     maLineColor.SetAlpha( 0.0 );   // transparent
602     if( CheckContext() )
603         CGContextSetStrokeColor( mrContext, maLineColor.AsArray() );
604 }
605 
606 // -----------------------------------------------------------------------
607 
SetLineColor(SalColor nSalColor)608 void AquaSalGraphics::SetLineColor( SalColor nSalColor )
609 {
610 	maLineColor = RGBAColor( nSalColor );
611     if( CheckContext() )
612         CGContextSetStrokeColor( mrContext, maLineColor.AsArray() );
613 }
614 
615 // -----------------------------------------------------------------------
616 
SetFillColor()617 void AquaSalGraphics::SetFillColor()
618 {
619     maFillColor.SetAlpha( 0.0 );   // transparent
620     if( CheckContext() )
621         CGContextSetFillColor( mrContext, maFillColor.AsArray() );
622 }
623 
624 // -----------------------------------------------------------------------
625 
SetFillColor(SalColor nSalColor)626 void AquaSalGraphics::SetFillColor( SalColor nSalColor )
627 {
628 	maFillColor = RGBAColor( nSalColor );
629     if( CheckContext() )
630         CGContextSetFillColor( mrContext, maFillColor.AsArray() );
631 }
632 
633 // -----------------------------------------------------------------------
634 
ImplGetROPSalColor(SalROPColor nROPColor)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 
SetROPLineColor(SalROPColor nROPColor)645 void AquaSalGraphics::SetROPLineColor( SalROPColor nROPColor )
646 {
647     if( ! mbPrinter )
648         SetLineColor( ImplGetROPSalColor( nROPColor ) );
649 }
650 
651 // -----------------------------------------------------------------------
652 
SetROPFillColor(SalROPColor nROPColor)653 void AquaSalGraphics::SetROPFillColor( SalROPColor nROPColor )
654 {
655     if( ! mbPrinter )
656         SetFillColor( ImplGetROPSalColor( nROPColor ) );
657 }
658 
659 // -----------------------------------------------------------------------
660 
ImplDrawPixel(long nX,long nY,const RGBAColor & rColor)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 
drawPixel(long nX,long nY)676 void AquaSalGraphics::drawPixel( long nX, long nY )
677 {
678     // draw pixel with current line color
679     ImplDrawPixel( nX, nY, maLineColor );
680 }
681 
drawPixel(long nX,long nY,SalColor nSalColor)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 
drawLine(long nX1,long nY1,long nX2,long nY2)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 
drawRect(long nX,long nY,long nWidth,long nHeight)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 
getBoundRect(sal_uInt32 nPoints,const SalPoint * pPtAry,long & rX,long & rY,long & rWidth,long & rHeight)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 
alignLinePoint(const SalPoint * i_pIn,float & o_fX,float & o_fY)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 
drawPolyLine(sal_uInt32 nPoints,const SalPoint * pPtAry)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 
drawPolygon(sal_uInt32 nPoints,const SalPoint * pPtAry)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 
drawPolyPolygon(sal_uInt32 nPolyCount,const sal_uInt32 * pPoints,PCONSTSALPOINT * ppPtAry)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 
drawPolyPolygon(const::basegfx::B2DPolyPolygon & rPolyPoly,double fTransparency)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 
drawPolyLine(const::basegfx::B2DPolygon & rPolyLine,double fTransparency,const::basegfx::B2DVector & rLineWidths,basegfx::B2DLineJoin eLineJoin,com::sun::star::drawing::LineCap eLineCap)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 
drawPolyLineBezier(sal_uInt32,const SalPoint *,const sal_uInt8 *)1067 sal_Bool AquaSalGraphics::drawPolyLineBezier( sal_uInt32, const SalPoint*, const sal_uInt8* )
1068 {
1069     return sal_False;
1070 }
1071 
1072 // -----------------------------------------------------------------------
1073 
drawPolygonBezier(sal_uInt32,const SalPoint *,const sal_uInt8 *)1074 sal_Bool AquaSalGraphics::drawPolygonBezier( sal_uInt32, const SalPoint*, const sal_uInt8* )
1075 {
1076     return sal_False;
1077 }
1078 
1079 // -----------------------------------------------------------------------
1080 
drawPolyPolygonBezier(sal_uInt32,const sal_uInt32 *,const SalPoint * const *,const sal_uInt8 * const *)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 
copyBits(const SalTwoRect & rPosAry,SalGraphics * pSrcGraphics)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 
copyArea(long nDstX,long nDstY,long nSrcX,long nSrcY,long nSrcWidth,long nSrcHeight,sal_uInt16)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 
drawBitmap(const SalTwoRect & rPosAry,const SalBitmap & rSalBitmap)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 
drawBitmap(const SalTwoRect & rPosAry,const SalBitmap & rSalBitmap,SalColor)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 
drawBitmap(const SalTwoRect & rPosAry,const SalBitmap & rSalBitmap,const SalBitmap & rTransparentBitmap)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 
drawMask(const SalTwoRect & rPosAry,const SalBitmap & rSalBitmap,SalColor nMaskColor)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 
getBitmap(long nX,long nY,long nDX,long nDY)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 
getPixel(long nX,long nY)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 
DrawPattern50(void *,CGContextRef rContext)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 
Pattern50Fill()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 
invert(long nX,long nY,long nWidth,long nHeight,SalInvert nFlags)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 
invert(sal_uInt32 nPoints,const SalPoint * pPtAry,SalInvert nSalFlags)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 
drawEPS(long nX,long nY,long nWidth,long nHeight,void * pEpsData,sal_uLong nByteCount)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 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
drawAlphaBitmap(const SalTwoRect & rTR,const SalBitmap & rSrcBitmap,const SalBitmap & rAlphaBmp)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 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
drawTransformedBitmap(const basegfx::B2DPoint & rNull,const basegfx::B2DPoint & rX,const basegfx::B2DPoint & rY,const SalBitmap & rSrcBitmap,const SalBitmap * pAlphaBmp)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 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
drawAlphaRect(long nX,long nY,long nWidth,long nHeight,sal_uInt8 nTransparency)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 
SetTextColor(SalColor nSalColor)1597 void AquaSalGraphics::SetTextColor( SalColor nSalColor )
1598 {
1599 	maTextColor = RGBAColor( nSalColor );
1600 	if( mpMacTextStyle)
1601 		mpMacTextStyle->SetTextColor( maTextColor );
1602 }
1603 
1604 // -----------------------------------------------------------------------
1605 
GetFontMetric(ImplFontMetricData * pMetric,int)1606 void AquaSalGraphics::GetFontMetric( ImplFontMetricData* pMetric, int /*nFallbackLevel*/ )
1607 {
1608 	mpMacTextStyle->GetFontMetric( mfFakeDPIScale, *pMetric );
1609 }
1610 
1611 // -----------------------------------------------------------------------
1612 
GetKernPairs(sal_uLong,ImplKernPairData *)1613 sal_uLong AquaSalGraphics::GetKernPairs( sal_uLong, ImplKernPairData* )
1614 {
1615 	return 0;
1616 }
1617 
1618 // -----------------------------------------------------------------------
1619 
AddTempFontDir(const char * pDir)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 
AddLocalTempFontDirs(void)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 
GetDevFontList(ImplDevFontList * pFontList)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 
AddTempDevFont(ImplDevFontList *,const String & rFontFileURL,const String &)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 
GetGraphicsWidth() const1760 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 
GetGlyphBoundRect(sal_GlyphId aGlyphId,Rectangle & rRect)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 
GetGlyphOutline(sal_GlyphId aGlyphId,basegfx::B2DPolyPolygon & rPolyPoly)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 
GetDevFontSubstList(OutputDevice *)1795 void AquaSalGraphics::GetDevFontSubstList( OutputDevice* )
1796 {
1797 	// nothing to do since there are no device-specific fonts on Aqua
1798 }
1799 
1800 // -----------------------------------------------------------------------
1801 
DrawServerFontLayout(const ServerFontLayout &)1802 void AquaSalGraphics::DrawServerFontLayout( const ServerFontLayout& )
1803 {}
1804 
1805 // -----------------------------------------------------------------------
1806 
SetFont(ImplFontSelectData * pReqFont,int)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 
GetTextLayout(ImplLayoutArgs &,int)1844 SalLayout* AquaSalGraphics::GetTextLayout( ImplLayoutArgs& /*rArgs*/, int /*nFallbackLevel*/ )
1845 {
1846 	SalLayout* pSalLayout = mpMacTextStyle->GetTextLayout();
1847 	return pSalLayout;
1848 }
1849 
1850 // -----------------------------------------------------------------------
1851 
GetImplFontCharMap() const1852 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
FakeDirEntry(const char aTag[5],ByteCount nOfs,ByteCount nLen,const unsigned char *,unsigned char * & rpDest)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.
GetRawFontData(const ImplFontData * pFontData,ByteVector & rBuffer,bool * pJustCFF)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 
CreateFontSubset(const rtl::OUString & rToFile,const ImplFontData * pFontData,sal_GlyphId * pGlyphIds,sal_uInt8 * pEncoding,sal_Int32 * pGlyphWidths,int nGlyphCount,FontSubsetInfo & rInfo)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 
GetGlyphWidths(const ImplFontData * pFontData,bool bVertical,Int32Vector & rGlyphWidths,Ucs2UIntMap & rUnicodeEnc)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 
GetFontEncodingVector(const ImplFontData *,const Ucs2OStrMap **)2263 const Ucs2SIntMap* AquaSalGraphics::GetFontEncodingVector(
2264 	const ImplFontData*, const Ucs2OStrMap** /*ppNonEncoded*/ )
2265 {
2266 	return NULL;
2267 }
2268 
2269 // -----------------------------------------------------------------------
2270 
GetEmbedFontData(const ImplFontData *,const sal_Ucs *,sal_Int32 *,FontSubsetInfo &,long *)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 
FreeEmbedFontData(const void * pData,long)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 
GetSysFontData(int) const2292 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 
GetGraphicsData() const2331 SystemGraphicsData AquaSalGraphics::GetGraphicsData() const
2332 {
2333     SystemGraphicsData aRes;
2334     aRes.nSize = sizeof(aRes);
2335     aRes.rCGContext = mrContext;
2336     return aRes;
2337 }
2338 
2339 // -----------------------------------------------------------------------
2340 
SetXORMode(bool bSet,bool bInvertOnly)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
ApplyXorContext()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 
XorEmulation()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 
~XorEmulation()2416 XorEmulation::~XorEmulation()
2417 {
2418 	Disable();
2419 	SetTarget( 0, 0, 0, NULL, NULL );
2420 }
2421 
2422 // -----------------------------------------------------------------------
2423 
SetTarget(int nWidth,int nHeight,int nTargetDepth,CGContextRef xTargetContext,CGLayerRef xTargetLayer)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 	// initialize 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 
UpdateTarget()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