1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "sal/config.h"
24 #include <sal/types.h>
25 #include <osl/endian.h>
26 #include <osl/file.hxx>
28 #include <basegfx/polygon/b2dpolygon.hxx>
30 #include "quartz/salbmp.h"
31 #include "quartz/salgdi.h"
32 #include "quartz/utils.h"
34 #include "fontsubset.hxx"
38 #include "saldatabasic.hxx"
41 #if defined(IOS) && defined(DBG_UTIL)
43 // Variables in TiledView.m
44 extern int DBG_DRAW_ROUNDS
, DBG_DRAW_COUNTER
, DBG_DRAW_DEPTH
;
46 #define DBG_DRAW_OPERATION(s,v) \
48 if (DBG_DRAW_ROUNDS >= 0) { \
49 if (DBG_DRAW_COUNTER++ > DBG_DRAW_ROUNDS) \
51 SAL_DEBUG("===> " << s << " " << DBG_DRAW_COUNTER); \
55 #define DBG_DRAW_OPERATION_EXIT(s) \
57 if (DBG_DRAW_ROUNDS >= 0) \
58 SAL_DEBUG("<=== " << s << " " << DBG_DRAW_COUNTER); \
61 #define DBG_DRAW_OPERATION_EXIT_EARLY(s) DBG_DRAW_OPERATION_EXIT(s << " exit early " << __LINE__)
65 #define DBG_DRAW_OPERATION(s,v) /* empty */
66 #define DBG_DRAW_OPERATION_EXIT(s) /* empty */
67 #define DBG_DRAW_OPERATION_EXIT_EARLY(s) /* empty */
73 typedef std::vector
<unsigned char> ByteVector
;
75 static const basegfx::B2DPoint
aHalfPointOfs ( 0.5, 0.5 );
77 static void AddPolygonToPath( CGMutablePathRef xPath
,
78 const ::basegfx::B2DPolygon
& rPolygon
,
79 bool bClosePath
, bool bPixelSnap
, bool bLineDraw
)
81 // short circuit if there is nothing to do
82 const int nPointCount
= rPolygon
.count();
83 if( nPointCount
<= 0 )
87 (void)bPixelSnap
; // TODO
89 const bool bHasCurves
= rPolygon
.areControlPointsUsed();
90 for( int nPointIdx
= 0, nPrevIdx
= 0;; nPrevIdx
= nPointIdx
++ )
92 int nClosedIdx
= nPointIdx
;
93 if( nPointIdx
>= nPointCount
)
95 // prepare to close last curve segment if needed
96 if( bClosePath
&& (nPointIdx
== nPointCount
) )
106 ::basegfx::B2DPoint aPoint
= rPolygon
.getB2DPoint( nClosedIdx
);
110 // snap device coordinates to full pixels
111 aPoint
.setX( basegfx::fround( aPoint
.getX() ) );
112 aPoint
.setY( basegfx::fround( aPoint
.getY() ) );
117 aPoint
+= aHalfPointOfs
;
121 // first point => just move there
122 CG_TRACE("CGPathMoveToPoint(" << xPath
<< ",NULL," << aPoint
.getX() << "," << aPoint
.getY() << ")");
123 CGPathMoveToPoint( xPath
, NULL
, aPoint
.getX(), aPoint
.getY() );
127 bool bPendingCurve
= false;
130 bPendingCurve
= rPolygon
.isNextControlPointUsed( nPrevIdx
);
131 bPendingCurve
|= rPolygon
.isPrevControlPointUsed( nClosedIdx
);
134 if( !bPendingCurve
) // line segment
136 CG_TRACE("CGPathAddLineToPoint(" << xPath
<< ",NULL," << aPoint
.getX() << "," << aPoint
.getY() << ")");
137 CGPathAddLineToPoint( xPath
, NULL
, aPoint
.getX(), aPoint
.getY() );
139 else // cubic bezier segment
141 basegfx::B2DPoint aCP1
= rPolygon
.getNextControlPoint( nPrevIdx
);
142 basegfx::B2DPoint aCP2
= rPolygon
.getPrevControlPoint( nClosedIdx
);
145 aCP1
+= aHalfPointOfs
;
146 aCP2
+= aHalfPointOfs
;
149 "CGPathAddCurveToPoint(" << xPath
<< ",NULL," << aCP1
.getX() << "," << aCP1
.getY() << "," <<
150 aCP2
.getX() << "," << aCP2
.getY() << "," << aPoint
.getX() << "," << aPoint
.getY() << ")" );
151 CGPathAddCurveToPoint( xPath
, NULL
, aCP1
.getX(), aCP1
.getY(),
152 aCP2
.getX(), aCP2
.getY(), aPoint
.getX(), aPoint
.getY() );
158 CG_TRACE( "CGPathCloseSubpath(" << xPath
<< ")" );
159 CGPathCloseSubpath( xPath
);
163 static void AddPolyPolygonToPath( CGMutablePathRef xPath
,
164 const ::basegfx::B2DPolyPolygon
& rPolyPoly
,
165 bool bPixelSnap
, bool bLineDraw
)
167 // short circuit if there is nothing to do
168 const int nPolyCount
= rPolyPoly
.count();
169 if( nPolyCount
<= 0 )
173 for( int nPolyIdx
= 0; nPolyIdx
< nPolyCount
; ++nPolyIdx
)
175 const ::basegfx::B2DPolygon rPolygon
= rPolyPoly
.getB2DPolygon( nPolyIdx
);
176 AddPolygonToPath( xPath
, rPolygon
, true, bPixelSnap
, bLineDraw
);
180 bool AquaSalGraphics::CreateFontSubset( const OUString
& rToFile
,
181 const PhysicalFontFace
* pFontData
,
182 sal_GlyphId
* pGlyphIds
, sal_uInt8
* pEncoding
,
183 sal_Int32
* pGlyphWidths
, int nGlyphCount
,
184 FontSubsetInfo
& rInfo
)
186 // TODO: move more of the functionality here into the generic subsetter code
188 // prepare the requested file name for writing the font-subset file
190 if( osl_File_E_None
!= osl_getSystemPathFromFileURL( rToFile
.pData
, &aSysPath
.pData
) )
192 const rtl_TextEncoding aThreadEncoding
= osl_getThreadTextEncoding();
193 const OString
aToFile( OUStringToOString( aSysPath
, aThreadEncoding
) );
195 // get the raw-bytes from the font to be subset
197 bool bCffOnly
= false;
198 if( !GetRawFontData( pFontData
, aBuffer
, &bCffOnly
) )
201 // handle CFF-subsetting
204 // provide the raw-CFF data to the subsetter
205 ByteCount nCffLen
= aBuffer
.size();
206 rInfo
.LoadFont( FontSubsetInfo::CFF_FONT
, &aBuffer
[0], nCffLen
);
208 // NOTE: assuming that all glyphids requested on Aqua are fully translated
210 // make the subsetter provide the requested subset
211 FILE* pOutFile
= fopen( aToFile
.getStr(), "wb" );
212 bool bRC
= rInfo
.CreateFontSubset( FontSubsetInfo::TYPE1_PFB
, pOutFile
, NULL
,
213 pGlyphIds
, pEncoding
, nGlyphCount
, pGlyphWidths
);
218 // TODO: modernize psprint's horrible fontsubset C-API
219 // this probably only makes sense after the switch to another SCM
220 // that can preserve change history after file renames
222 // prepare data for psprint's font subsetter
223 TrueTypeFont
* pSftFont
= NULL
;
224 int nRC
= ::OpenTTFontBuffer( (void*)&aBuffer
[0], aBuffer
.size(), 0, &pSftFont
);
228 // get details about the subsetted font
229 TTGlobalFontInfo aTTInfo
;
230 ::GetTTGlobalFontInfo( pSftFont
, &aTTInfo
);
231 rInfo
.m_nFontType
= FontSubsetInfo::SFNT_TTF
;
232 rInfo
.m_aPSName
= OUString(
233 aTTInfo
.psname
, std::strlen(aTTInfo
.psname
), RTL_TEXTENCODING_UTF8
);
234 rInfo
.m_aFontBBox
= Rectangle( Point( aTTInfo
.xMin
, aTTInfo
.yMin
),
235 Point( aTTInfo
.xMax
, aTTInfo
.yMax
) );
236 rInfo
.m_nCapHeight
= aTTInfo
.yMax
; // Well ...
237 rInfo
.m_nAscent
= aTTInfo
.winAscent
;
238 rInfo
.m_nDescent
= aTTInfo
.winDescent
;
239 // mac fonts usually do not have an OS2-table
240 // => get valid ascent/descent values from other tables
241 if( !rInfo
.m_nAscent
)
242 rInfo
.m_nAscent
= +aTTInfo
.typoAscender
;
243 if( !rInfo
.m_nAscent
)
244 rInfo
.m_nAscent
= +aTTInfo
.ascender
;
245 if( !rInfo
.m_nDescent
)
246 rInfo
.m_nDescent
= +aTTInfo
.typoDescender
;
247 if( !rInfo
.m_nDescent
)
248 rInfo
.m_nDescent
= -aTTInfo
.descender
;
250 // subset glyphs and get their properties
251 // take care that subset fonts require the NotDef glyph in pos 0
252 int nOrigCount
= nGlyphCount
;
253 sal_uInt16 aShortIDs
[ 256 ];
254 sal_uInt8 aTempEncs
[ 256 ];
257 for( int i
= 0; i
< nGlyphCount
; ++i
)
259 aTempEncs
[i
] = pEncoding
[i
];
260 sal_GlyphId
aGlyphId(pGlyphIds
[i
] & GF_IDXMASK
);
261 if( pGlyphIds
[i
] & GF_ISCHAR
)
263 bool bVertical
= (pGlyphIds
[i
] & GF_ROTMASK
) != 0;
264 aGlyphId
= ::MapChar( pSftFont
, static_cast<sal_uInt16
>(aGlyphId
), bVertical
);
265 if( aGlyphId
== 0 && pFontData
->IsSymbolFont() )
267 // #i12824# emulate symbol aliasing U+FXXX <-> U+0XXX
268 aGlyphId
= pGlyphIds
[i
] & GF_IDXMASK
;
269 aGlyphId
= (aGlyphId
& 0xF000) ? (aGlyphId
& 0x00FF) : (aGlyphId
| 0xF000 );
270 aGlyphId
= ::MapChar( pSftFont
, static_cast<sal_uInt16
>(aGlyphId
), bVertical
);
273 aShortIDs
[i
] = static_cast<sal_uInt16
>( aGlyphId
);
276 nNotDef
= i
; // first NotDef glyph found
281 // add fake NotDef glyph if needed
283 nNotDef
= nGlyphCount
++;
285 // NotDef glyph must be in pos 0 => swap glyphids
286 aShortIDs
[ nNotDef
] = aShortIDs
[0];
287 aTempEncs
[ nNotDef
] = aTempEncs
[0];
291 DBG_ASSERT( nGlyphCount
< 257, "too many glyphs for subsetting" );
293 // TODO: where to get bVertical?
294 const bool bVertical
= false;
296 // fill the pGlyphWidths array
297 // while making sure that the NotDef glyph is at index==0
298 TTSimpleGlyphMetrics
* pGlyphMetrics
=
299 ::GetTTSimpleGlyphMetrics( pSftFont
, aShortIDs
, nGlyphCount
, bVertical
);
302 sal_uInt16 nNotDefAdv
= pGlyphMetrics
[0].adv
;
303 pGlyphMetrics
[0].adv
= pGlyphMetrics
[nNotDef
].adv
;
304 pGlyphMetrics
[nNotDef
].adv
= nNotDefAdv
;
305 for( int i
= 0; i
< nOrigCount
; ++i
)
306 pGlyphWidths
[i
] = pGlyphMetrics
[i
].adv
;
307 free( pGlyphMetrics
);
309 // write subset into destination file
310 nRC
= ::CreateTTFromTTGlyphs( pSftFont
, aToFile
.getStr(), aShortIDs
,
311 aTempEncs
, nGlyphCount
, 0, NULL
, 0 );
312 ::CloseTTFont(pSftFont
);
313 return (nRC
== SF_OK
);
316 static inline void alignLinePoint( const SalPoint
* i_pIn
, float& o_fX
, float& o_fY
)
318 o_fX
= static_cast<float>(i_pIn
->mnX
) + 0.5;
319 o_fY
= static_cast<float>(i_pIn
->mnY
) + 0.5;
322 void AquaSalGraphics::copyBits( const SalTwoRect
& rPosAry
, SalGraphics
*pSrcGraphics
)
329 //from unix salgdi2.cxx
330 //[FIXME] find a better way to prevent calc from crashing when width and height are negative
331 if( rPosAry
.mnSrcWidth
<= 0
332 || rPosAry
.mnSrcHeight
<= 0
333 || rPosAry
.mnDestWidth
<= 0
334 || rPosAry
.mnDestHeight
<= 0 )
340 // If called from idle layout, mrContext is NULL, no idea what to do
345 // accelerate trivial operations
346 /*const*/ AquaSalGraphics
* pSrc
= static_cast<AquaSalGraphics
*>(pSrcGraphics
);
347 const bool bSameGraphics
= (this == pSrc
)
349 || (mbWindow
&& mpFrame
&& pSrc
->mbWindow
&& (mpFrame
== pSrc
->mpFrame
))
353 && (rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
)
354 && (rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
))
356 // short circuit if there is nothing to do
357 if( (rPosAry
.mnSrcX
== rPosAry
.mnDestX
)
358 && (rPosAry
.mnSrcY
== rPosAry
.mnDestY
))
360 // use copyArea() if source and destination context are identical
361 copyArea( rPosAry
.mnDestX
, rPosAry
.mnDestY
, rPosAry
.mnSrcX
, rPosAry
.mnSrcY
,
362 rPosAry
.mnSrcWidth
, rPosAry
.mnSrcHeight
, 0 );
367 pSrc
->ApplyXorContext();
369 SAL_WARN_IF( !pSrc
->mxLayer
, "vcl.quartz", "AquaSalGraphics::copyBits() from non-layered graphics this=" << this );
371 const CGPoint aDstPoint
= CGPointMake(+rPosAry
.mnDestX
- rPosAry
.mnSrcX
, rPosAry
.mnDestY
- rPosAry
.mnSrcY
);
372 if( (rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
&&
373 rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
) &&
374 (!mnBitmapDepth
|| (aDstPoint
.x
+ pSrc
->mnWidth
) <= mnWidth
)
375 && pSrc
->mxLayer
) // workaround a Quartz crasher
377 // in XOR mode the drawing context is redirected to the XOR mask
378 // if source and target are identical then copyBits() paints onto the target context though
379 CGContextRef xCopyContext
= mrContext
;
380 if( mpXorEmulation
&& mpXorEmulation
->IsEnabled() )
382 if( pSrcGraphics
== this )
384 xCopyContext
= mpXorEmulation
->GetTargetContext();
387 CG_TRACE( "CGContextSaveGState(" << xCopyContext
<< ")" );
388 CGContextSaveGState( xCopyContext
);
389 const CGRect aDstRect
= CGRectMake(rPosAry
.mnDestX
, rPosAry
.mnDestY
, rPosAry
.mnDestWidth
, rPosAry
.mnDestHeight
);
390 CG_TRACE( "CGContextClipToRect(" << xCopyContext
<< "," << aDstRect
<< ")" );
391 CGContextClipToRect( xCopyContext
, aDstRect
);
393 // draw at new destination
394 // NOTE: flipped drawing gets disabled for this, else the subimage would be drawn upside down
395 if( pSrc
->IsFlipped() )
397 CG_TRACE( "CGContextTranslateCTM(" << xCopyContext
<< ",0," << mnHeight
<< ")" );
398 CGContextTranslateCTM( xCopyContext
, 0, +mnHeight
);
399 CG_TRACE( "CGContextScaleCTM(" << xCopyContext
<< ",+1,-1)" );
400 CGContextScaleCTM( xCopyContext
, +1, -1 );
402 // TODO: pSrc->size() != this->size()
403 CG_TRACE( "CGContextDrawLayerAtPoint(" << xCopyContext
<< "," << aDstPoint
<< "," << pSrc
->mxLayer
<< ")" );
404 CGContextDrawLayerAtPoint( xCopyContext
, aDstPoint
, pSrc
->mxLayer
);
405 CG_TRACE( "CGContextRestoreGState(" << xCopyContext
<< ")" );
406 CGContextRestoreGState( xCopyContext
);
407 // mark the destination rectangle as updated
408 RefreshRect( aDstRect
);
412 SalBitmap
* pBitmap
= pSrc
->getBitmap( rPosAry
.mnSrcX
, rPosAry
.mnSrcY
,
413 rPosAry
.mnSrcWidth
, rPosAry
.mnSrcHeight
);
417 SalTwoRect
aPosAry( rPosAry
);
420 drawBitmap( aPosAry
, *pBitmap
);
426 static void DrawPattern50( void*, CGContextRef rContext
)
428 static const CGRect aRects
[2] = { { {0,0}, { 2, 2 } }, { { 2, 2 }, { 2, 2 } } };
429 CG_TRACE( "CGContextAddRects(" << rContext
<< ",aRects,2 )" );
430 CGContextAddRects( rContext
, aRects
, 2 );
431 CG_TRACE( "CGContextFillPath(" << rContext
<< ")" );
432 CGContextFillPath( rContext
);
435 static void getBoundRect( sal_uInt32 nPoints
, const SalPoint
*pPtAry
, long &rX
, long& rY
, long& rWidth
, long& rHeight
)
437 long nX1
= pPtAry
->mnX
;
439 long nY1
= pPtAry
->mnY
;
441 for( sal_uInt32 n
= 1; n
< nPoints
; n
++ )
443 if( pPtAry
[n
].mnX
< nX1
)
447 else if( pPtAry
[n
].mnX
> nX2
)
451 if( pPtAry
[n
].mnY
< nY1
)
455 else if( pPtAry
[n
].mnY
> nY2
)
462 rWidth
= nX2
- nX1
+ 1;
463 rHeight
= nY2
- nY1
+ 1;
466 static SalColor
ImplGetROPSalColor( SalROPColor nROPColor
)
469 if ( nROPColor
== SAL_ROP_0
)
471 nSalColor
= MAKE_SALCOLOR( 0, 0, 0 );
475 nSalColor
= MAKE_SALCOLOR( 255, 255, 255 );
480 // apply the XOR mask to the target context if active and dirty
481 void AquaSalGraphics::ApplyXorContext()
483 if( !mpXorEmulation
)
487 if( mpXorEmulation
->UpdateTarget() )
489 RefreshRect( 0, 0, mnWidth
, mnHeight
); // TODO: refresh minimal changerect
493 void AquaSalGraphics::copyArea( long nDstX
, long nDstY
,long nSrcX
, long nSrcY
,
494 long nSrcWidth
, long nSrcHeight
, sal_uInt16
/*nFlags*/ )
496 SAL_WARN_IF( !mxLayer
, "vcl.quartz", "AquaSalGraphics::copyArea() for non-layered graphics this=" << this );
505 // in XOR mode the drawing context is redirected to the XOR mask
506 // copyArea() always works on the target context though
507 CGContextRef xCopyContext
= mrContext
;
508 if( mpXorEmulation
&& mpXorEmulation
->IsEnabled() )
510 xCopyContext
= mpXorEmulation
->GetTargetContext();
512 // drawing a layer onto its own context causes trouble on OSX => copy it first
513 // TODO: is it possible to get rid of this unneeded copy more often?
514 // e.g. on OSX>=10.5 only this situation causes problems:
515 // mnBitmapDepth && (aDstPoint.x + pSrc->mnWidth) > mnWidth
516 CGLayerRef xSrcLayer
= mxLayer
;
517 // TODO: if( mnBitmapDepth > 0 )
519 const CGSize aSrcSize
= CGSizeMake(nSrcWidth
, nSrcHeight
);
520 xSrcLayer
= CGLayerCreateWithContext( xCopyContext
, aSrcSize
, NULL
);
521 CG_TRACE( "CGLayerCreateWithContext(" << xCopyContext
<< "," << aSrcSize
<< ",NULL) = " << xSrcLayer
);
522 const CGContextRef xSrcContext
= CGLayerGetContext( xSrcLayer
);
523 CG_TRACE( "CGLayerGetContext(" << xSrcLayer
<< ") = " << xSrcContext
);
524 CGPoint aSrcPoint
= CGPointMake(-nSrcX
, -nSrcY
);
527 CG_TRACE( "CGContextTranslateCTM(" << xSrcContext
<< ",0," << nSrcHeight
<< ")" );
528 CGContextTranslateCTM( xSrcContext
, 0, +nSrcHeight
);
529 CG_TRACE( "CGContextScaleCTM(" << xSrcContext
<< ",+1,-1)" );
530 CGContextScaleCTM( xSrcContext
, +1, -1 );
531 aSrcPoint
.y
= (nSrcY
+ nSrcHeight
) - mnHeight
;
533 CG_TRACE( "CGContextDrawLayerAtPoint(" << xSrcContext
<< "," << aSrcPoint
<< "," << mxLayer
<< ")" );
534 CGContextDrawLayerAtPoint( xSrcContext
, aSrcPoint
, mxLayer
);
537 // draw at new destination
538 const CGPoint aDstPoint
= CGPointMake(+nDstX
, +nDstY
);
539 CG_TRACE( "CGContextDrawLayerAtPoint(" << xCopyContext
<< "," << aDstPoint
<< "," << xSrcLayer
<< ")" );
540 CGContextDrawLayerAtPoint( xCopyContext
, aDstPoint
, xSrcLayer
);
543 if( xSrcLayer
!= mxLayer
)
545 CG_TRACE( "CGLayerRelease(" << xSrcLayer
<< ")" );
546 CGLayerRelease( xSrcLayer
);
548 // mark the destination rectangle as updated
549 RefreshRect( nDstX
, nDstY
, nSrcWidth
, nSrcHeight
);
554 void AquaSalGraphics::copyResolution( AquaSalGraphics
& rGraphics
)
556 if( !rGraphics
.mnRealDPIY
&& rGraphics
.mbWindow
&& rGraphics
.mpFrame
)
558 rGraphics
.initResolution( rGraphics
.mpFrame
->getNSWindow() );
560 mnRealDPIX
= rGraphics
.mnRealDPIX
;
561 mnRealDPIY
= rGraphics
.mnRealDPIY
;
566 bool AquaSalGraphics::drawAlphaBitmap( const SalTwoRect
& rTR
,
567 const SalBitmap
& rSrcBitmap
,
568 const SalBitmap
& rAlphaBmp
)
570 DBG_DRAW_OPERATION("drawAlphaBitmap", true);
572 // An image mask can't have a depth > 8 bits (should be 1 to 8 bits)
573 if( rAlphaBmp
.GetBitCount() > 8 )
575 DBG_DRAW_OPERATION_EXIT_EARLY("drawAlphaBitmap");
579 // are these two tests really necessary? (see vcl/unx/source/gdi/salgdi2.cxx)
580 // horizontal/vertical mirroring not implemented yet
581 if( rTR
.mnDestWidth
< 0 || rTR
.mnDestHeight
< 0 )
583 DBG_DRAW_OPERATION_EXIT_EARLY("drawAlphaBitmap");
587 const QuartzSalBitmap
& rSrcSalBmp
= static_cast<const QuartzSalBitmap
&>(rSrcBitmap
);
588 const QuartzSalBitmap
& rMaskSalBmp
= static_cast<const QuartzSalBitmap
&>(rAlphaBmp
);
589 CGImageRef xMaskedImage
= rSrcSalBmp
.CreateWithMask( rMaskSalBmp
, rTR
.mnSrcX
,
590 rTR
.mnSrcY
, rTR
.mnSrcWidth
,
594 DBG_DRAW_OPERATION_EXIT_EARLY("drawAlphaBitmap");
598 if ( CheckContext() )
600 const CGRect aDstRect
= CGRectMake( rTR
.mnDestX
, rTR
.mnDestY
, rTR
.mnDestWidth
, rTR
.mnDestHeight
);
601 CG_TRACE( "CGContextDrawImage(" << mrContext
<< "," << aDstRect
<< "," << xMaskedImage
<< ")" );
602 CGContextDrawImage( mrContext
, aDstRect
, xMaskedImage
);
603 RefreshRect( aDstRect
);
606 CG_TRACE("CGImageRelease(" << xMaskedImage
<< ")");
607 CGImageRelease(xMaskedImage
);
609 DBG_DRAW_OPERATION_EXIT("drawAlphaBitmap");
613 bool AquaSalGraphics::drawTransformedBitmap(
614 const basegfx::B2DPoint
& rNull
, const basegfx::B2DPoint
& rX
, const basegfx::B2DPoint
& rY
,
615 const SalBitmap
& rSrcBitmap
, const SalBitmap
* pAlphaBmp
)
617 DBG_DRAW_OPERATION("drawTransformedBitmap", true);
619 if( !CheckContext() )
621 DBG_DRAW_OPERATION_EXIT_EARLY("drawTransformedBitmap");
625 // get the Quartz image
626 CGImageRef xImage
= NULL
;
627 const Size aSize
= rSrcBitmap
.GetSize();
628 const QuartzSalBitmap
& rSrcSalBmp
= static_cast<const QuartzSalBitmap
&>(rSrcBitmap
);
629 const QuartzSalBitmap
* pMaskSalBmp
= static_cast<const QuartzSalBitmap
*>(pAlphaBmp
);
631 xImage
= rSrcSalBmp
.CreateCroppedImage( 0, 0, (int)aSize
.Width(), (int)aSize
.Height() );
633 xImage
= rSrcSalBmp
.CreateWithMask( *pMaskSalBmp
, 0, 0, (int)aSize
.Width(), (int)aSize
.Height() );
636 DBG_DRAW_OPERATION_EXIT_EARLY("drawTransformedBitmap");
640 // setup the image transformation
641 // using the rNull,rX,rY points as destinations for the (0,0),(0,Width),(Height,0) source points
642 CG_TRACE( "CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
643 CGContextSaveGState( mrContext
);
644 const basegfx::B2DVector aXRel
= rX
- rNull
;
645 const basegfx::B2DVector aYRel
= rY
- rNull
;
646 const CGAffineTransform aCGMat
= CGAffineTransformMake(
647 aXRel
.getX()/aSize
.Width(), aXRel
.getY()/aSize
.Width(),
648 aYRel
.getX()/aSize
.Height(), aYRel
.getY()/aSize
.Height(),
649 rNull
.getX(), rNull
.getY());
650 CG_TRACE( "CGContextConcatCTM(" << mrContext
<< "," << aCGMat
<< ")" );
651 CGContextConcatCTM( mrContext
, aCGMat
);
653 // draw the transformed image
654 const CGRect aSrcRect
= CGRectMake(0, 0, aSize
.Width(), aSize
.Height());
655 CG_TRACE( "CGContextDrawImage(" << mrContext
<< "," << aSrcRect
<< "," << xImage
<< ")" );
656 CGContextDrawImage( mrContext
, aSrcRect
, xImage
);
657 CG_TRACE( "CGImageRelease(" << xImage
<< ")" );
658 CGImageRelease( xImage
);
659 // restore the Quartz graphics state
660 CG_TRACE("CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
--);
661 CGContextRestoreGState(mrContext
);
663 // mark the destination as painted
664 const CGRect aDstRect
= CGRectApplyAffineTransform( aSrcRect
, aCGMat
);
665 RefreshRect( aDstRect
);
667 DBG_DRAW_OPERATION_EXIT("drawTransformedBitmap");
671 bool AquaSalGraphics::drawAlphaRect( long nX
, long nY
, long nWidth
,
672 long nHeight
, sal_uInt8 nTransparency
)
674 DBG_DRAW_OPERATION("drawAlphaRect", true);
676 if( !CheckContext() )
678 DBG_DRAW_OPERATION_EXIT_EARLY("drawAlphaRect");
682 // save the current state
683 CG_TRACE( "CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
684 CGContextSaveGState( mrContext
);
685 CG_TRACE( "CGContextSetAlpha(" << mrContext
<< "," << (100-nTransparency
) * (1.0/100) << ")" );
686 CGContextSetAlpha( mrContext
, (100-nTransparency
) * (1.0/100) );
688 CGRect aRect
= CGRectMake(nX
, nY
, nWidth
-1, nHeight
-1);
691 aRect
.origin
.x
+= 0.5;
692 aRect
.origin
.y
+= 0.5;
695 CG_TRACE( "CGContextBeginPath(" << mrContext
<< ")" );
696 CGContextBeginPath( mrContext
);
697 CG_TRACE( "CGContextAddRect(" << mrContext
<< "," << aRect
<< ")" );
698 CGContextAddRect( mrContext
, aRect
);
699 CG_TRACE( "CGContextDrawPath(" << mrContext
<< ",kCGPathFill)" );
700 CGContextDrawPath( mrContext
, kCGPathFill
);
703 CG_TRACE("CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
--);
704 CGContextRestoreGState(mrContext
);
705 RefreshRect( aRect
);
707 DBG_DRAW_OPERATION_EXIT("drawAlphaRect");
711 void AquaSalGraphics::drawBitmap( const SalTwoRect
& rPosAry
, const SalBitmap
& rSalBitmap
)
713 DBG_DRAW_OPERATION("drawBitmap",);
715 if( !CheckContext() )
717 DBG_DRAW_OPERATION_EXIT_EARLY("drawBitmap");
721 const QuartzSalBitmap
& rBitmap
= static_cast<const QuartzSalBitmap
&>(rSalBitmap
);
722 CGImageRef xImage
= rBitmap
.CreateCroppedImage( (int)rPosAry
.mnSrcX
, (int)rPosAry
.mnSrcY
,
723 (int)rPosAry
.mnSrcWidth
, (int)rPosAry
.mnSrcHeight
);
726 DBG_DRAW_OPERATION_EXIT_EARLY("drawBitmap");
730 const CGRect aDstRect
= CGRectMake(rPosAry
.mnDestX
, rPosAry
.mnDestY
, rPosAry
.mnDestWidth
, rPosAry
.mnDestHeight
);
731 CG_TRACE( "CGContextDrawImage(" << mrContext
<< "," << aDstRect
<< "," << xImage
<< ")" );
732 CGContextDrawImage( mrContext
, aDstRect
, xImage
);
733 CG_TRACE( "CGImageRelease(" << xImage
<< ")" );
734 CGImageRelease( xImage
);
735 RefreshRect( aDstRect
);
737 DBG_DRAW_OPERATION_EXIT("drawBitmap");
740 void AquaSalGraphics::drawBitmap( const SalTwoRect
& rPosAry
, const SalBitmap
& rSalBitmap
,SalColor
)
742 OSL_FAIL("not implemented for color masking!");
743 drawBitmap( rPosAry
, rSalBitmap
);
746 void AquaSalGraphics::drawBitmap( const SalTwoRect
& rPosAry
, const SalBitmap
& rSalBitmap
,
747 const SalBitmap
& rTransparentBitmap
)
749 DBG_DRAW_OPERATION("drawBitmap",);
751 if( !CheckContext() )
753 DBG_DRAW_OPERATION_EXIT_EARLY("drawBitmap");
757 const QuartzSalBitmap
& rBitmap
= static_cast<const QuartzSalBitmap
&>(rSalBitmap
);
758 const QuartzSalBitmap
& rMask
= static_cast<const QuartzSalBitmap
&>(rTransparentBitmap
);
759 CGImageRef
xMaskedImage( rBitmap
.CreateWithMask( rMask
, rPosAry
.mnSrcX
, rPosAry
.mnSrcY
,
760 rPosAry
.mnSrcWidth
, rPosAry
.mnSrcHeight
) );
763 DBG_DRAW_OPERATION_EXIT_EARLY("drawBitmap");
767 const CGRect aDstRect
= CGRectMake(rPosAry
.mnDestX
, rPosAry
.mnDestY
, rPosAry
.mnDestWidth
, rPosAry
.mnDestHeight
);
768 CG_TRACE( "CGContextDrawImage(" << mrContext
<< "," << aDstRect
<< "," << xMaskedImage
<< ")" );
769 CGContextDrawImage( mrContext
, aDstRect
, xMaskedImage
);
770 CG_TRACE( "CGImageRelease(" << xMaskedImage
<< ")" );
771 CGImageRelease( xMaskedImage
);
772 RefreshRect( aDstRect
);
774 DBG_DRAW_OPERATION_EXIT("drawBitmap");
779 bool AquaSalGraphics::drawEPS( long nX
, long nY
, long nWidth
, long nHeight
,
780 void* pEpsData
, sal_uLong nByteCount
)
782 // convert the raw data to an NSImageRef
783 NSData
* xNSData
= [NSData dataWithBytes
:(void*)pEpsData length
:(int)nByteCount
];
784 NSImageRep
* xEpsImage
= [NSEPSImageRep imageRepWithData
: xNSData
];
789 // get the target context
790 if( !CheckContext() )
794 // NOTE: flip drawing, else the nsimage would be drawn upside down
795 CG_TRACE( "CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
796 CGContextSaveGState( mrContext
);
797 // CGContextTranslateCTM( mrContext, 0, +mnHeight );
798 CG_TRACE( "CGContextScaleCTM(" << mrContext
<< ",+1,-1)" );
799 CGContextScaleCTM( mrContext
, +1, -1 );
800 nY
= /*mnHeight*/ - (nY
+ nHeight
);
802 // prepare the target context
803 NSGraphicsContext
* pOrigNSCtx
= [NSGraphicsContext currentContext
];
806 // create new context
807 NSGraphicsContext
* pDrawNSCtx
= [NSGraphicsContext graphicsContextWithGraphicsPort
: mrContext flipped
: IsFlipped()];
808 // set it, setCurrentContext also releases the prviously set one
809 [NSGraphicsContext setCurrentContext
: pDrawNSCtx
];
812 const NSRect aDstRect
= NSMakeRect( nX
, nY
, nWidth
, nHeight
);
813 const BOOL bOK
= [xEpsImage drawInRect
: aDstRect
];
815 // restore the NSGraphicsContext
816 [NSGraphicsContext setCurrentContext
: pOrigNSCtx
];
817 [pOrigNSCtx release
]; // restore the original retain count
819 CG_TRACE("CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
--);
820 CGContextRestoreGState( mrContext
);
821 // mark the destination rectangle as updated
822 RefreshRect( aDstRect
);
829 void AquaSalGraphics::drawLine( long nX1
, long nY1
, long nX2
, long nY2
)
831 DBG_DRAW_OPERATION("drawLine",);
833 if( nX1
== nX2
&& nY1
== nY2
)
835 // #i109453# platform independent code expects at least one pixel to be drawn
836 drawPixel( nX1
, nY1
);
838 DBG_DRAW_OPERATION_EXIT_EARLY("drawLine");
842 if( !CheckContext() )
844 DBG_DRAW_OPERATION_EXIT_EARLY("drawLine");
848 CG_TRACE( "CGContextBeginPath(" << mrContext
<< ")" );
849 CGContextBeginPath( mrContext
);
850 CG_TRACE( "CGContextMoveToPoint(" << mrContext
<< "," << static_cast<float>(nX1
)+0.5 << "," << static_cast<float>(nY1
)+0.5 << ")" );
851 CGContextMoveToPoint( mrContext
, static_cast<float>(nX1
)+0.5, static_cast<float>(nY1
)+0.5 );
852 CG_TRACE( "CGContextAddLineToPoint(" << mrContext
<< "," << static_cast<float>(nX2
)+0.5 << "," << static_cast<float>(nY2
)+0.5 << ")" );
853 CGContextAddLineToPoint( mrContext
, static_cast<float>(nX2
)+0.5, static_cast<float>(nY2
)+0.5 );
854 CG_TRACE( "CGContextDrawPath(" << mrContext
<< ",kCGPathStroke)" );
855 CGContextDrawPath( mrContext
, kCGPathStroke
);
857 Rectangle
aRefreshRect( nX1
, nY1
, nX2
, nY2
);
859 // Is a call to RefreshRect( aRefreshRect ) missing here?
861 DBG_DRAW_OPERATION_EXIT("drawLine");
864 void AquaSalGraphics::drawMask( const SalTwoRect
& rPosAry
, const SalBitmap
& rSalBitmap
, SalColor nMaskColor
)
866 DBG_DRAW_OPERATION("drawMask",);
868 if( !CheckContext() )
870 DBG_DRAW_OPERATION_EXIT_EARLY("drawMask");
874 const QuartzSalBitmap
& rBitmap
= static_cast<const QuartzSalBitmap
&>(rSalBitmap
);
875 CGImageRef xImage
= rBitmap
.CreateColorMask( rPosAry
.mnSrcX
, rPosAry
.mnSrcY
,
876 rPosAry
.mnSrcWidth
, rPosAry
.mnSrcHeight
,
880 DBG_DRAW_OPERATION_EXIT_EARLY("drawMask");
884 const CGRect aDstRect
= CGRectMake(rPosAry
.mnDestX
, rPosAry
.mnDestY
, rPosAry
.mnDestWidth
, rPosAry
.mnDestHeight
);
885 CG_TRACE( "CGContextDrawImage(" << mrContext
<< "," << aDstRect
<< "," << xImage
<< ")" );
886 CGContextDrawImage( mrContext
, aDstRect
, xImage
);
887 CG_TRACE( "CGImageRelease(" << xImage
<< ")" );
888 CGImageRelease( xImage
);
889 RefreshRect( aDstRect
);
891 DBG_DRAW_OPERATION_EXIT("drawMask");
894 void AquaSalGraphics::drawPixel( long nX
, long nY
)
896 // draw pixel with current line color
897 ImplDrawPixel( nX
, nY
, maLineColor
);
900 void AquaSalGraphics::drawPixel( long nX
, long nY
, SalColor nSalColor
)
902 const RGBAColor
aPixelColor( nSalColor
);
903 ImplDrawPixel( nX
, nY
, aPixelColor
);
906 bool AquaSalGraphics::drawPolyLine(
907 const ::basegfx::B2DPolygon
& rPolyLine
,
908 double fTransparency
,
909 const ::basegfx::B2DVector
& rLineWidths
,
910 basegfx::B2DLineJoin eLineJoin
,
911 com::sun::star::drawing::LineCap eLineCap
)
913 DBG_DRAW_OPERATION("drawPolyLine", true);
915 // short circuit if there is nothing to do
916 const int nPointCount
= rPolyLine
.count();
917 if( nPointCount
<= 0 )
919 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
923 // reject requests that cannot be handled yet
924 if( rLineWidths
.getX() != rLineWidths
.getY() )
926 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
931 if( !CheckContext() )
933 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
938 // #i101491# Aqua does not support B2DLINEJOIN_NONE; return false to use
939 // the fallback (own geometry preparation)
940 // #i104886# linejoin-mode and thus the above only applies to "fat" lines
941 if( (basegfx::B2DLINEJOIN_NONE
== eLineJoin
) &&
942 (rLineWidths
.getX() > 1.3) )
944 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
948 // setup line attributes
949 CGLineJoin aCGLineJoin
= kCGLineJoinMiter
;
952 case ::basegfx::B2DLINEJOIN_NONE
: aCGLineJoin
= /*TODO?*/kCGLineJoinMiter
; break;
953 case ::basegfx::B2DLINEJOIN_MIDDLE
: aCGLineJoin
= /*TODO?*/kCGLineJoinMiter
; break;
954 case ::basegfx::B2DLINEJOIN_BEVEL
: aCGLineJoin
= kCGLineJoinBevel
; break;
955 case ::basegfx::B2DLINEJOIN_MITER
: aCGLineJoin
= kCGLineJoinMiter
; break;
956 case ::basegfx::B2DLINEJOIN_ROUND
: aCGLineJoin
= kCGLineJoinRound
; break;
959 // setup cap attribute
960 CGLineCap
aCGLineCap(kCGLineCapButt
);
964 default: // com::sun::star::drawing::LineCap_BUTT:
966 aCGLineCap
= kCGLineCapButt
;
969 case com::sun::star::drawing::LineCap_ROUND
:
971 aCGLineCap
= kCGLineCapRound
;
974 case com::sun::star::drawing::LineCap_SQUARE
:
976 aCGLineCap
= kCGLineCapSquare
;
981 // setup poly-polygon path
982 CGMutablePathRef xPath
= CGPathCreateMutable();
983 CG_TRACE( "CGPathCreateMutable() = " << xPath
);
984 AddPolygonToPath( xPath
, rPolyLine
, rPolyLine
.isClosed(), !getAntiAliasB2DDraw(), true );
986 const CGRect aRefreshRect
= CGPathGetBoundingBox( xPath
);
987 CG_TRACE( "CGPathGetBoundingBox(" << xPath
<< ") = " << aRefreshRect
);
988 // #i97317# workaround for Quartz having problems with drawing small polygons
989 if( ! ((aRefreshRect
.size
.width
<= 0.125) && (aRefreshRect
.size
.height
<= 0.125)) )
991 // use the path to prepare the graphics context
992 CG_TRACE( "CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
993 CGContextSaveGState( mrContext
);
994 CG_TRACE( "CGContextBeginPath(" << mrContext
<< ")" );
995 CGContextBeginPath( mrContext
);
996 CG_TRACE( "CGContextAddPath(" << mrContext
<< "," << xPath
<< ")" );
997 CGContextAddPath( mrContext
, xPath
);
998 // draw path with antialiased line
999 CGContextSetShouldAntialias( mrContext
, true );
1000 CG_TRACE( "CGContextSetAlpha(" << mrContext
<< "," << 1.0 - fTransparency
<< ")" );
1001 CGContextSetAlpha( mrContext
, 1.0 - fTransparency
);
1002 CGContextSetLineJoin( mrContext
, aCGLineJoin
);
1003 CGContextSetLineCap( mrContext
, aCGLineCap
);
1004 CGContextSetLineWidth( mrContext
, rLineWidths
.getX() );
1005 CG_TRACE( "CGContextDrawPath(" << mrContext
<< ",kCGPathStroke)" );
1006 CGContextDrawPath( mrContext
, kCGPathStroke
);
1007 CG_TRACE( "CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
-- );
1008 CGContextRestoreGState( mrContext
);
1010 // mark modified rectangle as updated
1011 RefreshRect( aRefreshRect
);
1014 CG_TRACE( "CGPathRelease(" << xPath
<< ")" );
1015 CGPathRelease( xPath
);
1017 DBG_DRAW_OPERATION_EXIT("drawPolyLine");
1021 bool AquaSalGraphics::drawPolyLineBezier( sal_uInt32
, const SalPoint
*, const sal_uInt8
* )
1026 bool AquaSalGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon
& rPolyPoly
,
1027 double fTransparency
)
1029 DBG_DRAW_OPERATION("drawPolyPolygon", true);
1031 // short circuit if there is nothing to do
1032 const int nPolyCount
= rPolyPoly
.count();
1033 if( nPolyCount
<= 0 )
1035 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyPolygon");
1039 // ignore invisible polygons
1040 if( (fTransparency
>= 1.0) || (fTransparency
< 0) )
1042 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyPolygon");
1046 // setup poly-polygon path
1047 CGMutablePathRef xPath
= CGPathCreateMutable();
1048 CG_TRACE( "CGPathCreateMutable() = " << xPath
);
1049 for( int nPolyIdx
= 0; nPolyIdx
< nPolyCount
; ++nPolyIdx
)
1051 const ::basegfx::B2DPolygon rPolygon
= rPolyPoly
.getB2DPolygon( nPolyIdx
);
1052 AddPolygonToPath( xPath
, rPolygon
, true, !getAntiAliasB2DDraw(), IsPenVisible() );
1055 const CGRect aRefreshRect
= CGPathGetBoundingBox( xPath
);
1056 CG_TRACE( "CGPathGetBoundingBox(" << xPath
<< ") = " << aRefreshRect
);
1057 // #i97317# workaround for Quartz having problems with drawing small polygons
1058 if( ! ((aRefreshRect
.size
.width
<= 0.125) && (aRefreshRect
.size
.height
<= 0.125)) )
1060 // prepare drawing mode
1061 CGPathDrawingMode eMode
;
1062 if( IsBrushVisible() && IsPenVisible() )
1064 eMode
= kCGPathEOFillStroke
;
1066 else if( IsPenVisible() )
1068 eMode
= kCGPathStroke
;
1070 else if( IsBrushVisible() )
1072 eMode
= kCGPathEOFill
;
1076 SAL_WARN( "vcl.quartz", "Neither pen nor brush visible" );
1077 CG_TRACE( "CGPathRelease(" << xPath
<< ")" );
1078 CGPathRelease( xPath
);
1079 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyPolygon");
1083 // use the path to prepare the graphics context
1084 CG_TRACE( "CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
1085 CGContextSaveGState( mrContext
);
1086 CG_TRACE( "CGContextBeginPath(" << mrContext
<< ")" );
1087 CGContextBeginPath( mrContext
);
1088 CG_TRACE( "CGContextAddPath(" << mrContext
<< "," << xPath
<< ")" );
1089 CGContextAddPath( mrContext
, xPath
);
1091 // draw path with antialiased polygon
1092 CGContextSetShouldAntialias( mrContext
, true );
1093 CG_TRACE( "CGContextSetAlpha(" << mrContext
<< "," << 1.0 - fTransparency
<< ")" );
1094 CGContextSetAlpha( mrContext
, 1.0 - fTransparency
);
1095 CG_TRACE( "CGContextDrawPath(" << mrContext
<< "," << eMode
<< ")" );
1096 CGContextDrawPath( mrContext
, eMode
);
1097 CG_TRACE( "CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
-- );
1098 CGContextRestoreGState( mrContext
);
1100 // mark modified rectangle as updated
1101 RefreshRect( aRefreshRect
);
1104 CG_TRACE( "CGPathRelease(" << xPath
<< ")" );
1105 CGPathRelease( xPath
);
1107 DBG_DRAW_OPERATION_EXIT("drawPolyPolygon");
1111 void AquaSalGraphics::drawPolyPolygon( sal_uInt32 nPolyCount
, const sal_uInt32
*pPoints
, PCONSTSALPOINT
*ppPtAry
)
1113 DBG_DRAW_OPERATION("drawPolyPolygon",);
1115 if( nPolyCount
<= 0 )
1117 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyPolygon");
1121 if( !CheckContext() )
1123 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyPolygon");
1128 long leftX
= 0, topY
= 0, maxWidth
= 0, maxHeight
= 0;
1129 getBoundRect( pPoints
[0], ppPtAry
[0], leftX
, topY
, maxWidth
, maxHeight
);
1130 for( sal_uInt32 n
= 1; n
< nPolyCount
; n
++ )
1132 long nX
= leftX
, nY
= topY
, nW
= maxWidth
, nH
= maxHeight
;
1133 getBoundRect( pPoints
[n
], ppPtAry
[n
], nX
, nY
, nW
, nH
);
1136 maxWidth
+= leftX
- nX
;
1141 maxHeight
+= topY
- nY
;
1144 if( nX
+ nW
> leftX
+ maxWidth
)
1146 maxWidth
= nX
+ nW
- leftX
;
1148 if( nY
+ nH
> topY
+ maxHeight
)
1150 maxHeight
= nY
+ nH
- topY
;
1154 // prepare drawing mode
1155 CGPathDrawingMode eMode
;
1156 if( IsBrushVisible() && IsPenVisible() )
1158 eMode
= kCGPathEOFillStroke
;
1160 else if( IsPenVisible() )
1162 eMode
= kCGPathStroke
;
1164 else if( IsBrushVisible() )
1166 eMode
= kCGPathEOFill
;
1170 SAL_WARN( "vcl.quartz", "Neither pen nor brush visible" );
1171 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyPolygon");
1175 // convert to CGPath
1176 CG_TRACE( "CGContextBeginPath(" << mrContext
<< ")" );
1177 CGContextBeginPath( mrContext
);
1178 if( IsPenVisible() )
1180 for( sal_uInt32 nPoly
= 0; nPoly
< nPolyCount
; nPoly
++ )
1182 const sal_uInt32 nPoints
= pPoints
[nPoly
];
1185 const SalPoint
*pPtAry
= ppPtAry
[nPoly
];
1187 alignLinePoint( pPtAry
, fX
, fY
);
1188 CG_TRACE( "CGContextMoveToPoint(" << mrContext
<< "," << fX
<< "," << fY
<< ")" );
1189 CGContextMoveToPoint( mrContext
, fX
, fY
);
1191 for( sal_uInt32 nPoint
= 1; nPoint
< nPoints
; nPoint
++, pPtAry
++ )
1193 alignLinePoint( pPtAry
, fX
, fY
);
1194 CG_TRACE( "CGContextAddLineToPoint(" << mrContext
<< "," << fX
<< "," << fY
<< ")" );
1195 CGContextAddLineToPoint( mrContext
, fX
, fY
);
1197 CG_TRACE("CGContextClosePath(" << mrContext
<< ")");
1198 CGContextClosePath(mrContext
);
1204 for( sal_uInt32 nPoly
= 0; nPoly
< nPolyCount
; nPoly
++ )
1206 const sal_uInt32 nPoints
= pPoints
[nPoly
];
1209 const SalPoint
*pPtAry
= ppPtAry
[nPoly
];
1210 CG_TRACE( "CGContextMoveToPoint(" << mrContext
<< "," << pPtAry
->mnX
<< "," << pPtAry
->mnY
<< ")" );
1211 CGContextMoveToPoint( mrContext
, pPtAry
->mnX
, pPtAry
->mnY
);
1213 for( sal_uInt32 nPoint
= 1; nPoint
< nPoints
; nPoint
++, pPtAry
++ )
1215 CG_TRACE( "CGContextAddLineToPoint(" << mrContext
<< "," << pPtAry
->mnX
<< "," << pPtAry
->mnY
<< ")" );
1216 CGContextAddLineToPoint( mrContext
, pPtAry
->mnX
, pPtAry
->mnY
);
1218 CG_TRACE("CGContextClosePath(" << mrContext
<< ")");
1219 CGContextClosePath(mrContext
);
1224 CG_TRACE( "CGContextDrawPath(" << mrContext
<< "," <<
1225 (eMode
== kCGPathFill
? "kCGPathFill" :
1226 (eMode
== kCGPathEOFill
? "kCGPathEOFill" :
1227 (eMode
== kCGPathFillStroke
? "kCGPathFillStroke" :
1228 (eMode
== kCGPathEOFillStroke
? "kCGPathEOFillStroke" :
1231 CGContextDrawPath( mrContext
, eMode
);
1233 RefreshRect( leftX
, topY
, maxWidth
, maxHeight
);
1235 DBG_DRAW_OPERATION_EXIT("drawPolyPolygon");
1238 void AquaSalGraphics::drawPolygon( sal_uInt32 nPoints
, const SalPoint
*pPtAry
)
1240 DBG_DRAW_OPERATION("drawPolygon",);
1244 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolygon");
1248 if( !CheckContext() )
1250 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolygon");
1254 long nX
= 0, nY
= 0, nWidth
= 0, nHeight
= 0;
1255 getBoundRect( nPoints
, pPtAry
, nX
, nY
, nWidth
, nHeight
);
1257 CGPathDrawingMode eMode
;
1258 if( IsBrushVisible() && IsPenVisible() )
1260 eMode
= kCGPathEOFillStroke
;
1262 else if( IsPenVisible() )
1264 eMode
= kCGPathStroke
;
1266 else if( IsBrushVisible() )
1268 eMode
= kCGPathEOFill
;
1272 SAL_WARN( "vcl.quartz", "Neither pen nor brush visible" );
1276 CG_TRACE( "CGContextBeginPath(" << mrContext
<< ")" );
1277 CGContextBeginPath( mrContext
);
1279 if( IsPenVisible() )
1282 alignLinePoint( pPtAry
, fX
, fY
);
1283 CG_TRACE( "CGContextMoveToPoint(" << mrContext
<< "," << fX
<< "," << fY
<< ")" );
1284 CGContextMoveToPoint( mrContext
, fX
, fY
);
1286 for( sal_uInt32 nPoint
= 1; nPoint
< nPoints
; nPoint
++, pPtAry
++ )
1288 alignLinePoint( pPtAry
, fX
, fY
);
1289 CG_TRACE( "CGContextAddLineToPoint(" << mrContext
<< "," << fX
<< "," << fY
<< ")" );
1290 CGContextAddLineToPoint( mrContext
, fX
, fY
);
1295 CG_TRACE( "CGContextMoveToPoint(" << mrContext
<< "," << pPtAry
->mnX
<< "," << pPtAry
->mnY
<< ")" );
1296 CGContextMoveToPoint( mrContext
, pPtAry
->mnX
, pPtAry
->mnY
);
1298 for( sal_uInt32 nPoint
= 1; nPoint
< nPoints
; nPoint
++, pPtAry
++ )
1300 CG_TRACE( "CGContextAddLineToPoint(" << mrContext
<< "," << pPtAry
->mnX
<< "," << pPtAry
->mnY
<< ")" );
1301 CGContextAddLineToPoint( mrContext
, pPtAry
->mnX
, pPtAry
->mnY
);
1305 CG_TRACE("CGContextClosePath(" << mrContext
<< ")");
1306 CGContextClosePath( mrContext
);
1307 CG_TRACE( "CGContextDrawPath(" << mrContext
<< "," << eMode
<< ")" );
1308 CGContextDrawPath( mrContext
, eMode
);
1309 RefreshRect( nX
, nY
, nWidth
, nHeight
);
1311 DBG_DRAW_OPERATION_EXIT("drawPolygon");
1314 bool AquaSalGraphics::drawPolygonBezier( sal_uInt32
, const SalPoint
*, const sal_uInt8
* )
1319 bool AquaSalGraphics::drawPolyPolygonBezier( sal_uInt32
, const sal_uInt32
*,
1320 const SalPoint
* const*, const sal_uInt8
* const* )
1325 void AquaSalGraphics::drawRect( long nX
, long nY
, long nWidth
, long nHeight
)
1327 DBG_DRAW_OPERATION("drawRect",);
1329 if( !CheckContext() )
1331 DBG_DRAW_OPERATION_EXIT_EARLY("drawRect");
1335 CGRect
aRect( CGRectMake(nX
, nY
, nWidth
, nHeight
) );
1336 if( IsPenVisible() )
1338 aRect
.origin
.x
+= 0.5;
1339 aRect
.origin
.y
+= 0.5;
1340 aRect
.size
.width
-= 1;
1341 aRect
.size
.height
-= 1;
1344 if( IsBrushVisible() )
1346 CG_TRACE( "CGContextFillRect(" << mrContext
<< "," << aRect
<< ")" );
1347 CGContextFillRect( mrContext
, aRect
);
1349 if( IsPenVisible() )
1351 CG_TRACE( "CGContextStrokeRect(" << mrContext
<< "," << aRect
<< ")" );
1352 CGContextStrokeRect( mrContext
, aRect
);
1354 RefreshRect( nX
, nY
, nWidth
, nHeight
);
1356 DBG_DRAW_OPERATION_EXIT("drawRect");
1359 void AquaSalGraphics::drawPolyLine( sal_uInt32 nPoints
, const SalPoint
*pPtAry
)
1361 DBG_DRAW_OPERATION("drawPolyLine",);
1365 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
1369 if( !CheckContext() )
1371 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
1375 long nX
= 0, nY
= 0, nWidth
= 0, nHeight
= 0;
1376 getBoundRect( nPoints
, pPtAry
, nX
, nY
, nWidth
, nHeight
);
1379 CG_TRACE( "CGContextBeginPath(" << mrContext
<< ")" );
1380 CGContextBeginPath( mrContext
);
1381 alignLinePoint( pPtAry
, fX
, fY
);
1382 CG_TRACE( "CGContextMoveToPoint(" << mrContext
<< "," << fX
<< "," << fY
<< ")" );
1383 CGContextMoveToPoint( mrContext
, fX
, fY
);
1385 for( sal_uInt32 nPoint
= 1; nPoint
< nPoints
; nPoint
++, pPtAry
++ )
1387 alignLinePoint( pPtAry
, fX
, fY
);
1388 CG_TRACE( "CGContextAddLineToPoint(" << mrContext
<< "," << fX
<< "," << fY
<< ")" );
1389 CGContextAddLineToPoint( mrContext
, fX
, fY
);
1391 CG_TRACE( "CGContextDrawPath(" << mrContext
<< ",kCGPathStroke)" );
1392 CGContextDrawPath( mrContext
, kCGPathStroke
);
1394 RefreshRect( nX
, nY
, nWidth
, nHeight
);
1396 DBG_DRAW_OPERATION_EXIT("drawPolyLine");
1399 sal_uInt16
AquaSalGraphics::GetBitCount() const
1401 sal_uInt16 nBits
= mnBitmapDepth
? mnBitmapDepth
: 32;//24;
1405 SalBitmap
* AquaSalGraphics::getBitmap( long nX
, long nY
, long nDX
, long nDY
)
1407 SAL_WARN_IF( !mxLayer
, "vcl.quartz", "AquaSalGraphics::getBitmap() with no layer this=" << this );
1411 QuartzSalBitmap
* pBitmap
= new QuartzSalBitmap
;
1412 if( !pBitmap
->Create( mxLayer
, mnBitmapDepth
, nX
, nY
, nDX
, nDY
) )
1420 SystemGraphicsData
AquaSalGraphics::GetGraphicsData() const
1422 SystemGraphicsData aRes
;
1423 aRes
.nSize
= sizeof(aRes
);
1424 aRes
.rCGContext
= mrContext
;
1428 long AquaSalGraphics::GetGraphicsWidth() const
1443 if( mbWindow
&& mpFrame
)
1445 w
= mpFrame
->maGeometry
.nWidth
;
1452 SalColor
AquaSalGraphics::getPixel( long nX
, long nY
)
1454 // return default value on printers or when out of bounds
1455 if( !mxLayer
|| (nX
< 0) || (nX
>= mnWidth
) ||
1456 (nY
< 0) || (nY
>= mnHeight
))
1460 // prepare creation of matching a CGBitmapContext
1461 #if defined OSL_BIGENDIAN
1462 struct{ unsigned char b
, g
, r
, a
; } aPixel
;
1464 struct{ unsigned char a
, r
, g
, b
; } aPixel
;
1467 // create a one-pixel bitmap context
1468 // TODO: is it worth to cache it?
1469 CGContextRef xOnePixelContext
=
1470 CGBitmapContextCreate( &aPixel
, 1, 1, 8, 32,
1471 GetSalData()->mxRGBSpace
, kCGImageAlphaNoneSkipFirst
| kCGBitmapByteOrder32Big
);
1473 CG_TRACE( "CGBitmapContextCreate(1x1x8) = " << xOnePixelContext
);
1475 // update this graphics layer
1478 // copy the requested pixel into the bitmap context
1483 const CGPoint aCGPoint
= CGPointMake(-nX
, -nY
);
1484 CG_TRACE( "CGContextDrawLayerAtPoint(" << xOnePixelContext
<< "," << aCGPoint
<< "," << mxLayer
<< ")" );
1485 CGContextDrawLayerAtPoint( xOnePixelContext
, aCGPoint
, mxLayer
);
1486 CG_TRACE( "CGContextRelease(" << xOnePixelContext
<< ")" );
1487 CGContextRelease( xOnePixelContext
);
1489 SalColor nSalColor
= MAKE_SALCOLOR( aPixel
.r
, aPixel
.g
, aPixel
.b
);
1495 void AquaSalGraphics::GetResolution( sal_Int32
& rDPIX
, sal_Int32
& rDPIY
)
1499 initResolution( (mbWindow
&& mpFrame
) ? mpFrame
->getNSWindow() : nil
);
1508 void AquaSalGraphics::ImplDrawPixel( long nX
, long nY
, const RGBAColor
& rColor
)
1510 if( !CheckContext() )
1514 // overwrite the fill color
1515 CG_TRACE( "CGContextSetFillColor(" << mrContext
<< "," << rColor
<< ")" );
1516 CGContextSetFillColor( mrContext
, rColor
.AsArray() );
1517 // draw 1x1 rect, there is no pixel drawing in Quartz
1518 const CGRect aDstRect
= CGRectMake(nX
, nY
, 1, 1);
1519 CG_TRACE( "CGContextFillRect(" << mrContext
<< "," << aDstRect
<< ")" );
1520 CGContextFillRect( mrContext
, aDstRect
);
1521 RefreshRect( aDstRect
);
1522 // reset the fill color
1523 CG_TRACE( "CGContextSetFillColor(" << mrContext
<< "," << maFillColor
<< ")" );
1524 CGContextSetFillColor( mrContext
, maFillColor
.AsArray() );
1529 void AquaSalGraphics::initResolution( NSWindow
* )
1531 // #i100617# read DPI only once; there is some kind of weird caching going on
1532 // if the main screen changes
1533 // FIXME: this is really unfortunate and needs to be investigated
1535 SalData
* pSalData
= GetSalData();
1536 if( pSalData
->mnDPIX
== 0 || pSalData
->mnDPIY
== 0 )
1538 NSScreen
* pScreen
= nil
;
1541 many woes went into the try to have different resolutions
1542 on different screens. The result of these trials is that OOo is not ready
1543 for that yet, vcl and applications would need to be adapted.
1545 Unfortunately this is not possible in the 3.0 timeframe.
1546 So let's stay with one resolution for all Windows and VirtualDevices
1547 which is the resolution of the main screen
1549 This of course also means that measurements are exact only on the main screen.
1550 For activating different resolutions again just comment out the two lines below.
1553 pScreen = [pWin screen];
1555 if( pScreen
== nil
)
1557 NSArray
* pScreens
= [NSScreen screens
];
1559 pScreen
= [pScreens objectAtIndex
: 0];
1562 mnRealDPIX
= mnRealDPIY
= 96;
1565 NSDictionary
* pDev
= [pScreen deviceDescription
];
1568 NSNumber
* pVal
= [pDev objectForKey
: @
"NSScreenNumber"];
1571 // FIXME: casting a long to CGDirectDisplayID is evil, but
1572 // Apple suggest to do it this way
1573 const CGDirectDisplayID nDisplayID
= (CGDirectDisplayID
)[pVal longValue
];
1574 const CGSize aSize
= CGDisplayScreenSize( nDisplayID
); // => result is in millimeters
1575 mnRealDPIX
= static_cast<long>((CGDisplayPixelsWide( nDisplayID
) * 25.4) / aSize
.width
);
1576 mnRealDPIY
= static_cast<long>((CGDisplayPixelsHigh( nDisplayID
) * 25.4) / aSize
.height
);
1580 OSL_FAIL( "no resolution found in device description" );
1585 OSL_FAIL( "no device description" );
1590 OSL_FAIL( "no screen found" );
1593 // #i107076# maintaining size-WYSIWYG-ness causes many problems for
1594 // low-DPI, high-DPI or for mis-reporting devices
1595 // => it is better to limit the calculation result then
1596 static const int nMinDPI
= 72;
1597 if( (mnRealDPIX
< nMinDPI
) || (mnRealDPIY
< nMinDPI
) )
1599 mnRealDPIX
= mnRealDPIY
= nMinDPI
;
1601 static const int nMaxDPI
= 200;
1602 if( (mnRealDPIX
> nMaxDPI
) || (mnRealDPIY
> nMaxDPI
) )
1604 mnRealDPIX
= mnRealDPIY
= nMaxDPI
;
1606 // for OSX any anisotropy reported for the display resolution is best ignored (e.g. TripleHead2Go)
1607 mnRealDPIX
= mnRealDPIY
= (mnRealDPIX
+ mnRealDPIY
+ 1) / 2;
1609 pSalData
->mnDPIX
= mnRealDPIX
;
1610 pSalData
->mnDPIY
= mnRealDPIY
;
1614 mnRealDPIX
= pSalData
->mnDPIX
;
1615 mnRealDPIY
= pSalData
->mnDPIY
;
1621 void AquaSalGraphics::invert( long nX
, long nY
, long nWidth
, long nHeight
, SalInvert nFlags
)
1623 if ( CheckContext() )
1625 CGRect aCGRect
= CGRectMake( nX
, nY
, nWidth
, nHeight
);
1626 CG_TRACE("CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
1627 CGContextSaveGState(mrContext
);
1629 if ( nFlags
& SAL_INVERT_TRACKFRAME
)
1631 const CGFloat dashLengths
[2] = { 4.0, 4.0 }; // for drawing dashed line
1632 CGContextSetBlendMode( mrContext
, kCGBlendModeDifference
);
1633 CG_TRACE( "CGContextSetRGBStrokeColor(" << mrContext
<< ",{1,1,1,1})" );
1634 CGContextSetRGBStrokeColor ( mrContext
, 1.0, 1.0, 1.0, 1.0 );
1635 CGContextSetLineDash ( mrContext
, 0, dashLengths
, 2 );
1636 CGContextSetLineWidth( mrContext
, 2.0);
1637 CG_TRACE("CGContextStrokeRect(" << mrContext
<< "," << aCGRect
<< ")" );
1638 CGContextStrokeRect ( mrContext
, aCGRect
);
1640 else if ( nFlags
& SAL_INVERT_50
)
1642 //CGContextSetAllowsAntialiasing( mrContext, false );
1643 CGContextSetBlendMode(mrContext
, kCGBlendModeDifference
);
1644 CGContextAddRect( mrContext
, aCGRect
);
1649 CGContextSetBlendMode(mrContext
, kCGBlendModeDifference
);
1650 CG_TRACE( "CGContextSetRGBFillColor(" << mrContext
<< ",{1,1,1,1})" );
1651 CGContextSetRGBFillColor ( mrContext
,1.0, 1.0, 1.0 , 1.0 );
1652 CG_TRACE("CGContextFillRect(" << mrContext
<< "," << aCGRect
<< ")" );
1653 CGContextFillRect ( mrContext
, aCGRect
);
1655 CG_TRACE( "CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
-- );
1656 CGContextRestoreGState( mrContext
);
1657 RefreshRect( aCGRect
);
1661 CGPoint
* AquaSalGraphics::makeCGptArray(sal_uInt32 nPoints
, const SalPoint
* pPtAry
)
1663 CGPoint
*CGpoints
= new CGPoint
[nPoints
];
1666 for(sal_uLong i
=0;i
<nPoints
;i
++)
1668 CGpoints
[i
].x
= pPtAry
[i
].mnX
;
1669 CGpoints
[i
].y
= pPtAry
[i
].mnY
;
1675 void AquaSalGraphics::invert( sal_uInt32 nPoints
, const SalPoint
* pPtAry
, SalInvert nSalFlags
)
1678 if ( CheckContext() )
1680 CG_TRACE("CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
1681 CGContextSaveGState(mrContext
);
1682 CGpoints
= makeCGptArray(nPoints
,pPtAry
);
1683 CGContextAddLines ( mrContext
, CGpoints
, nPoints
);
1684 if ( nSalFlags
& SAL_INVERT_TRACKFRAME
)
1686 const CGFloat dashLengths
[2] = { 4.0, 4.0 }; // for drawing dashed line
1687 CGContextSetBlendMode( mrContext
, kCGBlendModeDifference
);
1688 CG_TRACE( "CGContextSetRGBStrokeColor(" << mrContext
<< ",{1,1,1,1})" );
1689 CGContextSetRGBStrokeColor ( mrContext
, 1.0, 1.0, 1.0, 1.0 );
1690 CGContextSetLineDash ( mrContext
, 0, dashLengths
, 2 );
1691 CGContextSetLineWidth( mrContext
, 2.0);
1692 CG_TRACE("CGContextStrokePath(" << mrContext
<< ")" );
1693 CGContextStrokePath ( mrContext
);
1695 else if ( nSalFlags
& SAL_INVERT_50
)
1697 CGContextSetBlendMode(mrContext
, kCGBlendModeDifference
);
1702 CGContextSetBlendMode( mrContext
, kCGBlendModeDifference
);
1703 CG_TRACE( "CGContextSetRGBFillColor(" << mrContext
<< ",{1,1,1,1})" );
1704 CGContextSetRGBFillColor( mrContext
, 1.0, 1.0, 1.0, 1.0 );
1705 CG_TRACE("CGContextFillPath(" << mrContext
<< ")" );
1706 CGContextFillPath( mrContext
);
1708 const CGRect aRefreshRect
= CGContextGetClipBoundingBox(mrContext
);
1709 CG_TRACE( "CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
-- );
1710 CGContextRestoreGState( mrContext
);
1712 RefreshRect( aRefreshRect
);
1716 void AquaSalGraphics::Pattern50Fill()
1718 static const CGFloat aFillCol
[4] = { 1,1,1,1 };
1719 static const CGPatternCallbacks aCallback
= { 0, &DrawPattern50
, NULL
};
1720 static const CGColorSpaceRef mxP50Space
= CGColorSpaceCreatePattern( GetSalData()->mxRGBSpace
);
1721 static const CGPatternRef mxP50Pattern
= CGPatternCreate( NULL
, CGRectMake( 0, 0, 4, 4 ),
1722 CGAffineTransformIdentity
, 4, 4,
1723 kCGPatternTilingConstantSpacing
,
1724 false, &aCallback
);
1725 SAL_WARN_IF( !mrContext
, "vcl.quartz", "mrContext is NULL" );
1726 CG_TRACE( "CGContextSetFillColorSpace(" << mrContext
<< "," << mxP50Space
<< ")" );
1727 CGContextSetFillColorSpace( mrContext
, mxP50Space
);
1728 CG_TRACE( "CGContextSetFillPattern(" << mrContext
<< "," << mxP50Pattern
<< ",{1,1,1,1})" );
1729 CGContextSetFillPattern( mrContext
, mxP50Pattern
, aFillCol
);
1730 CG_TRACE( "CGContextFillPath(" << mrContext
<< ")" );
1731 CGContextFillPath( mrContext
);
1734 void AquaSalGraphics::ResetClipRegion()
1736 // release old path and indicate no clipping
1739 CG_TRACE( "CGPathRelease(" << mxClipPath
<< ")" );
1740 CGPathRelease( mxClipPath
);
1743 if( CheckContext() )
1749 void AquaSalGraphics::SetState()
1751 CG_TRACE( "CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
--);
1752 CGContextRestoreGState( mrContext
);
1753 CG_TRACE( "CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
1754 CGContextSaveGState( mrContext
);
1759 CG_TRACE( "CGContextBeginPath(" << mrContext
<< ")" );
1760 CGContextBeginPath( mrContext
); // discard any existing path
1761 CG_TRACE( "CGContextAddPath(" << mrContext
<< "," << mxClipPath
<< ")" );
1762 CGContextAddPath( mrContext
, mxClipPath
); // set the current path to the clipping path
1763 CG_TRACE( "CGContextClip(" << mrContext
<< ")" );
1764 CGContextClip( mrContext
); // use it for clipping
1767 // set RGB colorspace and line and fill colors
1768 CG_TRACE( "CGContextSetFillColor(" << mrContext
<< "," << maFillColor
<< ")" );
1769 CGContextSetFillColor( mrContext
, maFillColor
.AsArray() );
1770 CG_TRACE( "CGContextSetStrokeColor(" << mrContext
<< "," << maLineColor
<< ")" );
1771 CGContextSetStrokeColor( mrContext
, maLineColor
.AsArray() );
1772 CGContextSetShouldAntialias( mrContext
, false );
1773 if( mnXorMode
== 2 )
1774 CGContextSetBlendMode( mrContext
, kCGBlendModeDifference
);
1777 void AquaSalGraphics::SetLineColor()
1779 maLineColor
.SetAlpha( 0.0 ); // transparent
1780 if( CheckContext() )
1782 CG_TRACE( "CGContextSetRGBStrokeColor(" << mrContext
<< "," << maLineColor
<< ")" );
1783 CGContextSetRGBStrokeColor( mrContext
, maLineColor
.GetRed(), maLineColor
.GetGreen(),
1784 maLineColor
.GetBlue(), maLineColor
.GetAlpha() );
1788 void AquaSalGraphics::SetLineColor( SalColor nSalColor
)
1790 maLineColor
= RGBAColor( nSalColor
);
1791 if( CheckContext() )
1793 CG_TRACE( "CGContextSetRGBStrokeColor(" << mrContext
<< "," << maLineColor
<< ")" );
1794 CGContextSetRGBStrokeColor( mrContext
, maLineColor
.GetRed(), maLineColor
.GetGreen(),
1795 maLineColor
.GetBlue(), maLineColor
.GetAlpha() );
1799 void AquaSalGraphics::SetFillColor()
1801 maFillColor
.SetAlpha( 0.0 ); // transparent
1802 if( CheckContext() )
1804 CG_TRACE( "CGContextSetRGBFillColor(" << mrContext
<< "," << maFillColor
<< ")" );
1805 CGContextSetRGBFillColor( mrContext
, maFillColor
.GetRed(), maFillColor
.GetGreen(),
1806 maFillColor
.GetBlue(), maFillColor
.GetAlpha() );
1810 void AquaSalGraphics::SetFillColor( SalColor nSalColor
)
1812 maFillColor
= RGBAColor( nSalColor
);
1813 if( CheckContext() )
1815 CG_TRACE( "CGContextSetRGBFillColor(" << mrContext
<< "," << maFillColor
<< ")" );
1816 CGContextSetRGBFillColor( mrContext
, maFillColor
.GetRed(), maFillColor
.GetGreen(),
1817 maFillColor
.GetBlue(), maFillColor
.GetAlpha() );
1821 bool AquaSalGraphics::supportsOperation( OutDevSupportType eType
) const
1826 case OutDevSupport_TransparentRect
:
1827 case OutDevSupport_B2DClip
:
1828 case OutDevSupport_B2DDraw
:
1836 bool AquaSalGraphics::setClipRegion( const Region
& i_rClip
)
1838 // release old clip path
1841 CG_TRACE( "CGPathRelease(" << mxClipPath
<< ")" );
1842 CGPathRelease( mxClipPath
);
1845 mxClipPath
= CGPathCreateMutable();
1846 CG_TRACE( "CGPathCreateMutable() = " << mxClipPath
);
1848 // set current path, either as polypolgon or sequence of rectangles
1849 if(i_rClip
.HasPolyPolygonOrB2DPolyPolygon())
1851 const basegfx::B2DPolyPolygon
aClip(i_rClip
.GetAsB2DPolyPolygon());
1853 AddPolyPolygonToPath( mxClipPath
, aClip
, !getAntiAliasB2DDraw(), false );
1857 RectangleVector aRectangles
;
1858 i_rClip
.GetRegionRectangles(aRectangles
);
1860 for(RectangleVector::const_iterator
aRectIter(aRectangles
.begin()); aRectIter
!= aRectangles
.end(); ++aRectIter
)
1862 const long nW(aRectIter
->Right() - aRectIter
->Left() + 1); // uses +1 logic in original
1866 const long nH(aRectIter
->Bottom() - aRectIter
->Top() + 1); // uses +1 logic in original
1870 const CGRect aRect
= CGRectMake( aRectIter
->Left(), aRectIter
->Top(), nW
, nH
);
1871 CG_TRACE( "CGPathAddRect(" << mxClipPath
<< ",NULL," << aRect
<< ")" );
1872 CGPathAddRect( mxClipPath
, NULL
, aRect
);
1877 // set the current path as clip region
1878 if( CheckContext() )
1885 void AquaSalGraphics::SetROPFillColor( SalROPColor nROPColor
)
1888 SetFillColor( ImplGetROPSalColor( nROPColor
) );
1891 void AquaSalGraphics::SetROPLineColor( SalROPColor nROPColor
)
1894 SetLineColor( ImplGetROPSalColor( nROPColor
) );
1897 void AquaSalGraphics::SetXORMode( bool bSet
, bool bInvertOnly
)
1899 // return early if XOR mode remains unchanged
1904 if( ! bSet
&& mnXorMode
== 2 )
1906 CGContextSetBlendMode( mrContext
, kCGBlendModeNormal
);
1910 else if( bSet
&& bInvertOnly
&& mnXorMode
== 0)
1912 CGContextSetBlendMode( mrContext
, kCGBlendModeDifference
);
1917 if( (mpXorEmulation
== NULL
) && !bSet
)
1921 if( (mpXorEmulation
!= NULL
) && (bSet
== mpXorEmulation
->IsEnabled()) )
1925 if( !CheckContext() )
1929 // prepare XOR emulation
1930 if( !mpXorEmulation
)
1932 mpXorEmulation
= new XorEmulation();
1933 mpXorEmulation
->SetTarget( mnWidth
, mnHeight
, mnBitmapDepth
, mrContext
, mxLayer
);
1936 // change the XOR mode
1939 mpXorEmulation
->Enable();
1940 mrContext
= mpXorEmulation
->GetMaskContext();
1945 mpXorEmulation
->UpdateTarget();
1946 mpXorEmulation
->Disable();
1947 mrContext
= mpXorEmulation
->GetTargetContext();
1954 void AquaSalGraphics::updateResolution()
1956 DBG_ASSERT( mbWindow
, "updateResolution on inappropriate graphics" );
1958 initResolution( (mbWindow
&& mpFrame
) ? mpFrame
->getNSWindow() : nil
);
1963 XorEmulation::XorEmulation()
1964 : m_xTargetLayer( NULL
)
1965 , m_xTargetContext( NULL
)
1966 , m_xMaskContext( NULL
)
1967 , m_xTempContext( NULL
)
1968 , m_pMaskBuffer( NULL
)
1969 , m_pTempBuffer( NULL
)
1970 , m_nBufferLongs( 0 )
1971 , m_bIsEnabled( false )
1973 SAL_INFO( "vcl.quartz", "XorEmulation::XorEmulation() this=" << this );
1976 XorEmulation::~XorEmulation()
1978 SAL_INFO( "vcl.quartz", "XorEmulation::~XorEmulation() this=" << this );
1980 SetTarget( 0, 0, 0, NULL
, NULL
);
1983 void XorEmulation::SetTarget( int nWidth
, int nHeight
, int nTargetDepth
,
1984 CGContextRef xTargetContext
, CGLayerRef xTargetLayer
)
1986 SAL_INFO( "vcl.quartz", "XorEmulation::SetTarget() this=" << this << " (" << nWidth
<< "x" << nHeight
<< ") depth=" << nTargetDepth
<< " context=" << xTargetContext
<< " layer=" << xTargetLayer
);
1988 // prepare to replace old mask+temp context
1989 if( m_xMaskContext
)
1991 // cleanup the mask context
1992 CG_TRACE( "CGContextRelease(" << m_xMaskContext
<< ")" );
1993 CGContextRelease( m_xMaskContext
);
1994 delete[] m_pMaskBuffer
;
1995 m_xMaskContext
= NULL
;
1996 m_pMaskBuffer
= NULL
;
1998 // cleanup the temp context if needed
1999 if( m_xTempContext
)
2001 CG_TRACE( "CGContextRelease(" << m_xTempContext
<< ")" );
2002 CGContextRelease( m_xTempContext
);
2003 delete[] m_pTempBuffer
;
2004 m_xTempContext
= NULL
;
2005 m_pTempBuffer
= NULL
;
2009 // return early if there is nothing more to do
2010 if( !xTargetContext
)
2014 // retarget drawing operations to the XOR mask
2015 m_xTargetLayer
= xTargetLayer
;
2016 m_xTargetContext
= xTargetContext
;
2018 // prepare creation of matching CGBitmaps
2019 CGColorSpaceRef aCGColorSpace
= GetSalData()->mxRGBSpace
;
2020 CGBitmapInfo aCGBmpInfo
= kCGImageAlphaNoneSkipFirst
;
2021 int nBitDepth
= nTargetDepth
;
2026 int nBytesPerRow
= (nBitDepth
== 16) ? 2 : 4;
2027 const size_t nBitsPerComponent
= (nBitDepth
== 16) ? 5 : 8;
2028 if( nBitDepth
<= 8 )
2030 aCGColorSpace
= GetSalData()->mxGraySpace
;
2031 aCGBmpInfo
= kCGImageAlphaNone
;
2034 nBytesPerRow
*= nWidth
;
2035 m_nBufferLongs
= (nHeight
* nBytesPerRow
+ sizeof(sal_uLong
)-1) / sizeof(sal_uLong
);
2037 // create a XorMask context
2038 m_pMaskBuffer
= new sal_uLong
[ m_nBufferLongs
];
2039 m_xMaskContext
= CGBitmapContextCreate( m_pMaskBuffer
,
2041 nBitsPerComponent
, nBytesPerRow
,
2042 aCGColorSpace
, aCGBmpInfo
);
2043 SAL_WARN_IF( !m_xMaskContext
, "vcl.quartz", "mask context creation failed" );
2044 CG_TRACE( "CGBitmapContextCreate(" << nWidth
<< "x" << nHeight
<< ") = " << m_xMaskContext
);
2046 // reset the XOR mask to black
2047 memset( m_pMaskBuffer
, 0, m_nBufferLongs
* sizeof(sal_uLong
) );
2049 // a bitmap context will be needed for manual XORing
2050 // create one unless the target context is a bitmap context
2052 m_pTempBuffer
= (sal_uLong
*)CGBitmapContextGetData( m_xTargetContext
);
2053 if( !m_pTempBuffer
)
2055 // create a bitmap context matching to the target context
2056 m_pTempBuffer
= new sal_uLong
[ m_nBufferLongs
];
2057 m_xTempContext
= CGBitmapContextCreate( m_pTempBuffer
,
2059 nBitsPerComponent
, nBytesPerRow
,
2060 aCGColorSpace
, aCGBmpInfo
);
2061 SAL_WARN_IF( !m_xTempContext
, "vcl.quartz", "temp context creation failed" );
2062 CG_TRACE( "CGBitmapContextCreate(" << nWidth
<< "x" << nHeight
<< ") = " << m_xTempContext
);
2065 // initialize XOR mask context for drawing
2066 CGContextSetFillColorSpace( m_xMaskContext
, aCGColorSpace
);
2067 CGContextSetStrokeColorSpace( m_xMaskContext
, aCGColorSpace
);
2068 CGContextSetShouldAntialias( m_xMaskContext
, false );
2070 // improve the XorMask's XOR emulation a litte
2071 // NOTE: currently only enabled for monochrome contexts
2072 if( aCGColorSpace
== GetSalData()->mxGraySpace
)
2074 CGContextSetBlendMode( m_xMaskContext
, kCGBlendModeDifference
);
2076 // intialize the transformation matrix to the drawing target
2077 const CGAffineTransform aCTM
= CGContextGetCTM( xTargetContext
);
2078 CGContextConcatCTM( m_xMaskContext
, aCTM
);
2079 if( m_xTempContext
)
2081 CGContextConcatCTM( m_xTempContext
, aCTM
);
2083 // initialize the default XorMask graphics state
2084 CGContextSaveGState( m_xMaskContext
);
2087 bool XorEmulation::UpdateTarget()
2089 SAL_INFO( "vcl.quartz", "XorEmulation::UpdateTarget() this=" << this );
2095 // update the temp bitmap buffer if needed
2096 if( m_xTempContext
)
2098 SAL_WARN_IF( m_xTargetContext
== NULL
, "vcl.quartz", "Target layer is NULL");
2099 CG_TRACE( "CGContextDrawLayerAtPoint(" << m_xTempContext
<< "," << CGPointZero
<< "," << m_xTargetLayer
<< ")" );
2100 CGContextDrawLayerAtPoint( m_xTempContext
, CGPointZero
, m_xTargetLayer
);
2102 // do a manual XOR with the XorMask
2103 // this approach suffices for simple color manipulations
2104 // and also the complex-clipping-XOR-trick used in metafiles
2105 const sal_uLong
* pSrc
= m_pMaskBuffer
;
2106 sal_uLong
* pDst
= m_pTempBuffer
;
2107 for( int i
= m_nBufferLongs
; --i
>= 0;)
2109 *(pDst
++) ^= *(pSrc
++);
2111 // write back the XOR results to the target context
2112 if( m_xTempContext
)
2114 CGImageRef xXorImage
= CGBitmapContextCreateImage( m_xTempContext
);
2115 CG_TRACE( "CGBitmapContextCreateImage(" << m_xTempContext
<< ") = " << xXorImage
);
2116 const int nWidth
= (int)CGImageGetWidth( xXorImage
);
2117 const int nHeight
= (int)CGImageGetHeight( xXorImage
);
2118 // TODO: update minimal changerect
2119 const CGRect aFullRect
= CGRectMake(0, 0, nWidth
, nHeight
);
2120 CG_TRACE( "CGContextDrawImage(" << m_xTargetContext
<< "," << aFullRect
<< "," << xXorImage
<< ")" );
2121 CGContextDrawImage( m_xTargetContext
, aFullRect
, xXorImage
);
2122 CG_TRACE( "CGImageRelease(" << xXorImage
<< ")" );
2123 CGImageRelease( xXorImage
);
2126 // reset the XorMask to black again
2127 // TODO: not needed for last update
2128 memset( m_pMaskBuffer
, 0, m_nBufferLongs
* sizeof(sal_uLong
) );
2130 // TODO: return FALSE if target was not changed
2134 void AquaSalGraphics::SetVirDevGraphics( CGLayerRef xLayer
, CGContextRef xContext
,
2137 SAL_INFO( "vcl.quartz", "SetVirDevGraphics() this=" << this << " layer=" << xLayer
<< " context=" << xContext
);
2146 (void) nBitmapDepth
;
2150 // We will return early a few lines lower.
2151 // Undo the "stack initialization" done at the initial call of
2152 // this method, see end.
2153 CG_TRACE( "CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
--);
2154 CGContextRestoreGState( mrContext
);
2158 // set graphics properties
2160 mrContext
= xContext
;
2163 mnBitmapDepth
= nBitmapDepth
;
2167 mbForeignContext
= xContext
!= NULL
;
2170 // return early if the virdev is being destroyed
2174 // get new graphics properties
2177 mnWidth
= CGBitmapContextGetWidth( mrContext
);
2178 mnHeight
= CGBitmapContextGetHeight( mrContext
);
2179 CG_TRACE( "CGBitmapContextGetWidth&Height(" << mrContext
<< ") = " << mnWidth
<< "x" << mnHeight
);
2183 const CGSize aSize
= CGLayerGetSize( mxLayer
);
2184 mnWidth
= static_cast<int>(aSize
.width
);
2185 mnHeight
= static_cast<int>(aSize
.height
);
2186 CG_TRACE( "CGLayerGetSize(" << mxLayer
<< ") = " << aSize
);
2189 // prepare graphics for drawing
2190 const CGColorSpaceRef aCGColorSpace
= GetSalData()->mxRGBSpace
;
2191 CGContextSetFillColorSpace( mrContext
, aCGColorSpace
);
2192 CGContextSetStrokeColorSpace( mrContext
, aCGColorSpace
);
2194 // re-enable XorEmulation for the new context
2195 if( mpXorEmulation
)
2197 mpXorEmulation
->SetTarget( mnWidth
, mnHeight
, mnBitmapDepth
, mrContext
, mxLayer
);
2198 if( mpXorEmulation
->IsEnabled() )
2199 mrContext
= mpXorEmulation
->GetMaskContext();
2202 // initialize stack of CGContext states
2203 CG_TRACE( "CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
2204 CGContextSaveGState( mrContext
);
2208 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */