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"
39 #include <vcl/sysdata.hxx>
42 #include <config_cairo_canvas.h>
43 #if ENABLE_CAIRO_CANVAS
44 #include "cairo_quartz_cairo.hxx"
47 #if defined(IOS) && defined(DBG_UTIL)
49 // Variables in TiledView.m
50 extern int DBG_DRAW_ROUNDS
, DBG_DRAW_COUNTER
, DBG_DRAW_DEPTH
;
52 #define DBG_DRAW_OPERATION(s,v) \
54 if (DBG_DRAW_ROUNDS >= 0) { \
55 if (DBG_DRAW_COUNTER++ > DBG_DRAW_ROUNDS) \
57 SAL_DEBUG("===> " << s << " " << DBG_DRAW_COUNTER); \
61 #define DBG_DRAW_OPERATION_EXIT(s) \
63 if (DBG_DRAW_ROUNDS >= 0) \
64 SAL_DEBUG("<=== " << s << " " << DBG_DRAW_COUNTER); \
67 #define DBG_DRAW_OPERATION_EXIT_EARLY(s) DBG_DRAW_OPERATION_EXIT(s << " exit early " << __LINE__)
71 #define DBG_DRAW_OPERATION(s,v) /* empty */
72 #define DBG_DRAW_OPERATION_EXIT(s) /* empty */
73 #define DBG_DRAW_OPERATION_EXIT_EARLY(s) /* empty */
79 typedef std::vector
<unsigned char> ByteVector
;
81 static const basegfx::B2DPoint
aHalfPointOfs ( 0.5, 0.5 );
83 static void AddPolygonToPath( CGMutablePathRef xPath
,
84 const ::basegfx::B2DPolygon
& rPolygon
,
85 bool bClosePath
, bool bPixelSnap
, bool bLineDraw
)
87 // short circuit if there is nothing to do
88 const int nPointCount
= rPolygon
.count();
89 if( nPointCount
<= 0 )
93 (void)bPixelSnap
; // TODO
95 const bool bHasCurves
= rPolygon
.areControlPointsUsed();
96 for( int nPointIdx
= 0, nPrevIdx
= 0;; nPrevIdx
= nPointIdx
++ )
98 int nClosedIdx
= nPointIdx
;
99 if( nPointIdx
>= nPointCount
)
101 // prepare to close last curve segment if needed
102 if( bClosePath
&& (nPointIdx
== nPointCount
) )
112 ::basegfx::B2DPoint aPoint
= rPolygon
.getB2DPoint( nClosedIdx
);
116 // snap device coordinates to full pixels
117 aPoint
.setX( basegfx::fround( aPoint
.getX() ) );
118 aPoint
.setY( basegfx::fround( aPoint
.getY() ) );
123 aPoint
+= aHalfPointOfs
;
127 // first point => just move there
128 CG_TRACE("CGPathMoveToPoint(" << xPath
<< ",NULL," << aPoint
.getX() << "," << aPoint
.getY() << ")");
129 CGPathMoveToPoint( xPath
, NULL
, aPoint
.getX(), aPoint
.getY() );
133 bool bPendingCurve
= false;
136 bPendingCurve
= rPolygon
.isNextControlPointUsed( nPrevIdx
);
137 bPendingCurve
|= rPolygon
.isPrevControlPointUsed( nClosedIdx
);
140 if( !bPendingCurve
) // line segment
142 CG_TRACE("CGPathAddLineToPoint(" << xPath
<< ",NULL," << aPoint
.getX() << "," << aPoint
.getY() << ")");
143 CGPathAddLineToPoint( xPath
, NULL
, aPoint
.getX(), aPoint
.getY() );
145 else // cubic bezier segment
147 basegfx::B2DPoint aCP1
= rPolygon
.getNextControlPoint( nPrevIdx
);
148 basegfx::B2DPoint aCP2
= rPolygon
.getPrevControlPoint( nClosedIdx
);
151 aCP1
+= aHalfPointOfs
;
152 aCP2
+= aHalfPointOfs
;
154 CG_TRACE( "CGPathAddCurveToPoint(" << xPath
<< ",NULL," << aCP1
.getX() << "," << aCP1
.getY() << "," <<
155 aCP2
.getX() << "," << aCP2
.getY() << "," << aPoint
.getX() << "," << aPoint
.getY() << ")" );
156 CGPathAddCurveToPoint( xPath
, NULL
, aCP1
.getX(), aCP1
.getY(),
157 aCP2
.getX(), aCP2
.getY(), aPoint
.getX(), aPoint
.getY() );
163 CG_TRACE( "CGPathCloseSubpath(" << xPath
<< ")" );
164 CGPathCloseSubpath( xPath
);
168 static void AddPolyPolygonToPath( CGMutablePathRef xPath
,
169 const ::basegfx::B2DPolyPolygon
& rPolyPoly
,
170 bool bPixelSnap
, bool bLineDraw
)
172 // short circuit if there is nothing to do
173 const int nPolyCount
= rPolyPoly
.count();
174 if( nPolyCount
<= 0 )
178 for( int nPolyIdx
= 0; nPolyIdx
< nPolyCount
; ++nPolyIdx
)
180 const ::basegfx::B2DPolygon rPolygon
= rPolyPoly
.getB2DPolygon( nPolyIdx
);
181 AddPolygonToPath( xPath
, rPolygon
, true, bPixelSnap
, bLineDraw
);
185 bool AquaSalGraphics::CreateFontSubset( const OUString
& rToFile
,
186 const PhysicalFontFace
* pFontData
,
187 const sal_GlyphId
* pGlyphIds
, const sal_uInt8
* pEncoding
,
188 sal_Int32
* pGlyphWidths
, int nGlyphCount
,
189 FontSubsetInfo
& rInfo
)
191 // TODO: move more of the functionality here into the generic subsetter code
193 // prepare the requested file name for writing the font-subset file
195 if( osl_File_E_None
!= osl_getSystemPathFromFileURL( rToFile
.pData
, &aSysPath
.pData
) )
197 const rtl_TextEncoding aThreadEncoding
= osl_getThreadTextEncoding();
198 const OString
aToFile( OUStringToOString( aSysPath
, aThreadEncoding
) );
200 // get the raw-bytes from the font to be subset
202 bool bCffOnly
= false;
203 if( !GetRawFontData( pFontData
, aBuffer
, &bCffOnly
) )
206 // handle CFF-subsetting
209 // provide the raw-CFF data to the subsetter
210 ByteCount nCffLen
= aBuffer
.size();
211 rInfo
.LoadFont( FontSubsetInfo::CFF_FONT
, &aBuffer
[0], nCffLen
);
213 // NOTE: assuming that all glyphids requested on Aqua are fully translated
215 // make the subsetter provide the requested subset
216 FILE* pOutFile
= fopen( aToFile
.getStr(), "wb" );
217 bool bRC
= rInfo
.CreateFontSubset( FontSubsetInfo::TYPE1_PFB
, pOutFile
, NULL
,
218 pGlyphIds
, pEncoding
, nGlyphCount
, pGlyphWidths
);
223 // TODO: modernize psprint's horrible fontsubset C-API
224 // this probably only makes sense after the switch to another SCM
225 // that can preserve change history after file renames
227 // prepare data for psprint's font subsetter
228 TrueTypeFont
* pSftFont
= NULL
;
229 int nRC
= ::OpenTTFontBuffer( (void*)&aBuffer
[0], aBuffer
.size(), 0, &pSftFont
);
233 // get details about the subsetted font
234 TTGlobalFontInfo aTTInfo
;
235 ::GetTTGlobalFontInfo( pSftFont
, &aTTInfo
);
236 rInfo
.m_nFontType
= FontSubsetInfo::SFNT_TTF
;
237 rInfo
.m_aPSName
= OUString(
238 aTTInfo
.psname
, std::strlen(aTTInfo
.psname
), RTL_TEXTENCODING_UTF8
);
239 rInfo
.m_aFontBBox
= Rectangle( Point( aTTInfo
.xMin
, aTTInfo
.yMin
),
240 Point( aTTInfo
.xMax
, aTTInfo
.yMax
) );
241 rInfo
.m_nCapHeight
= aTTInfo
.yMax
; // Well ...
242 rInfo
.m_nAscent
= aTTInfo
.winAscent
;
243 rInfo
.m_nDescent
= aTTInfo
.winDescent
;
244 // mac fonts usually do not have an OS2-table
245 // => get valid ascent/descent values from other tables
246 if( !rInfo
.m_nAscent
)
247 rInfo
.m_nAscent
= +aTTInfo
.typoAscender
;
248 if( !rInfo
.m_nAscent
)
249 rInfo
.m_nAscent
= +aTTInfo
.ascender
;
250 if( !rInfo
.m_nDescent
)
251 rInfo
.m_nDescent
= +aTTInfo
.typoDescender
;
252 if( !rInfo
.m_nDescent
)
253 rInfo
.m_nDescent
= -aTTInfo
.descender
;
255 // subset glyphs and get their properties
256 // take care that subset fonts require the NotDef glyph in pos 0
257 int nOrigCount
= nGlyphCount
;
258 sal_uInt16 aShortIDs
[ 256 ];
259 sal_uInt8 aTempEncs
[ 256 ];
262 for( int i
= 0; i
< nGlyphCount
; ++i
)
264 aTempEncs
[i
] = pEncoding
[i
];
265 sal_GlyphId
aGlyphId(pGlyphIds
[i
] & GF_IDXMASK
);
266 if( pGlyphIds
[i
] & GF_ISCHAR
)
268 bool bVertical
= (pGlyphIds
[i
] & GF_ROTMASK
) != 0;
269 aGlyphId
= ::MapChar( pSftFont
, static_cast<sal_uInt16
>(aGlyphId
), bVertical
);
270 if( aGlyphId
== 0 && pFontData
->IsSymbolFont() )
272 // #i12824# emulate symbol aliasing U+FXXX <-> U+0XXX
273 aGlyphId
= pGlyphIds
[i
] & GF_IDXMASK
;
274 aGlyphId
= (aGlyphId
& 0xF000) ? (aGlyphId
& 0x00FF) : (aGlyphId
| 0xF000 );
275 aGlyphId
= ::MapChar( pSftFont
, static_cast<sal_uInt16
>(aGlyphId
), bVertical
);
278 aShortIDs
[i
] = static_cast<sal_uInt16
>( aGlyphId
);
281 nNotDef
= i
; // first NotDef glyph found
286 // add fake NotDef glyph if needed
288 nNotDef
= nGlyphCount
++;
290 // NotDef glyph must be in pos 0 => swap glyphids
291 aShortIDs
[ nNotDef
] = aShortIDs
[0];
292 aTempEncs
[ nNotDef
] = aTempEncs
[0];
296 DBG_ASSERT( nGlyphCount
< 257, "too many glyphs for subsetting" );
298 // TODO: where to get bVertical?
299 const bool bVertical
= false;
301 // fill the pGlyphWidths array
302 // while making sure that the NotDef glyph is at index==0
303 TTSimpleGlyphMetrics
* pGlyphMetrics
=
304 ::GetTTSimpleGlyphMetrics( pSftFont
, aShortIDs
, nGlyphCount
, bVertical
);
307 sal_uInt16 nNotDefAdv
= pGlyphMetrics
[0].adv
;
308 pGlyphMetrics
[0].adv
= pGlyphMetrics
[nNotDef
].adv
;
309 pGlyphMetrics
[nNotDef
].adv
= nNotDefAdv
;
310 for( int i
= 0; i
< nOrigCount
; ++i
)
311 pGlyphWidths
[i
] = pGlyphMetrics
[i
].adv
;
312 free( pGlyphMetrics
);
314 // write subset into destination file
315 nRC
= ::CreateTTFromTTGlyphs( pSftFont
, aToFile
.getStr(), aShortIDs
,
316 aTempEncs
, nGlyphCount
, 0, NULL
, 0 );
317 ::CloseTTFont(pSftFont
);
318 return (nRC
== SF_OK
);
321 static inline void alignLinePoint( const SalPoint
* i_pIn
, float& o_fX
, float& o_fY
)
323 o_fX
= static_cast<float>(i_pIn
->mnX
) + 0.5;
324 o_fY
= static_cast<float>(i_pIn
->mnY
) + 0.5;
327 void AquaSalGraphics::copyBits( const SalTwoRect
& rPosAry
, SalGraphics
*pSrcGraphics
)
334 //from unix salgdi2.cxx
335 //[FIXME] find a better way to prevent calc from crashing when width and height are negative
336 if( rPosAry
.mnSrcWidth
<= 0
337 || rPosAry
.mnSrcHeight
<= 0
338 || rPosAry
.mnDestWidth
<= 0
339 || rPosAry
.mnDestHeight
<= 0 )
345 // If called from idle layout, mrContext is NULL, no idea what to do
350 // accelerate trivial operations
351 /*const*/ AquaSalGraphics
* pSrc
= static_cast<AquaSalGraphics
*>(pSrcGraphics
);
352 const bool bSameGraphics
= (this == pSrc
)
354 || (mbWindow
&& mpFrame
&& pSrc
->mbWindow
&& (mpFrame
== pSrc
->mpFrame
))
358 && (rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
)
359 && (rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
))
361 // short circuit if there is nothing to do
362 if( (rPosAry
.mnSrcX
== rPosAry
.mnDestX
)
363 && (rPosAry
.mnSrcY
== rPosAry
.mnDestY
))
365 // use copyArea() if source and destination context are identical
366 copyArea( rPosAry
.mnDestX
, rPosAry
.mnDestY
, rPosAry
.mnSrcX
, rPosAry
.mnSrcY
,
367 rPosAry
.mnSrcWidth
, rPosAry
.mnSrcHeight
, 0 );
372 pSrc
->ApplyXorContext();
374 SAL_WARN_IF( !pSrc
->mxLayer
, "vcl.quartz", "AquaSalGraphics::copyBits() from non-layered graphics this=" << this );
376 const CGPoint aDstPoint
= CGPointMake(+rPosAry
.mnDestX
- rPosAry
.mnSrcX
, rPosAry
.mnDestY
- rPosAry
.mnSrcY
);
377 if( (rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
&&
378 rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
) &&
379 (!mnBitmapDepth
|| (aDstPoint
.x
+ pSrc
->mnWidth
) <= mnWidth
)
380 && pSrc
->mxLayer
) // workaround a Quartz crasher
382 // in XOR mode the drawing context is redirected to the XOR mask
383 // if source and target are identical then copyBits() paints onto the target context though
384 CGContextRef xCopyContext
= mrContext
;
385 if( mpXorEmulation
&& mpXorEmulation
->IsEnabled() )
387 if( pSrcGraphics
== this )
389 xCopyContext
= mpXorEmulation
->GetTargetContext();
392 CG_TRACE( "CGContextSaveGState(" << xCopyContext
<< ")" );
393 CGContextSaveGState( xCopyContext
);
394 const CGRect aDstRect
= CGRectMake(rPosAry
.mnDestX
, rPosAry
.mnDestY
, rPosAry
.mnDestWidth
, rPosAry
.mnDestHeight
);
395 CG_TRACE( "CGContextClipToRect(" << xCopyContext
<< "," << aDstRect
<< ")" );
396 CGContextClipToRect( xCopyContext
, aDstRect
);
398 // draw at new destination
399 // NOTE: flipped drawing gets disabled for this, else the subimage would be drawn upside down
400 if( pSrc
->IsFlipped() )
402 CG_TRACE( "CGContextTranslateCTM(" << xCopyContext
<< ",0," << mnHeight
<< ")" );
403 CGContextTranslateCTM( xCopyContext
, 0, +mnHeight
);
404 CG_TRACE( "CGContextScaleCTM(" << xCopyContext
<< ",+1,-1)" );
405 CGContextScaleCTM( xCopyContext
, +1, -1 );
407 // TODO: pSrc->size() != this->size()
408 CG_TRACE( "CGContextDrawLayerAtPoint(" << xCopyContext
<< "," << aDstPoint
<< "," << pSrc
->mxLayer
<< ")" );
409 CGContextDrawLayerAtPoint( xCopyContext
, aDstPoint
, pSrc
->mxLayer
);
410 CG_TRACE( "CGContextRestoreGState(" << xCopyContext
<< ")" );
411 CGContextRestoreGState( xCopyContext
);
412 // mark the destination rectangle as updated
413 RefreshRect( aDstRect
);
417 SalBitmap
* pBitmap
= pSrc
->getBitmap( rPosAry
.mnSrcX
, rPosAry
.mnSrcY
,
418 rPosAry
.mnSrcWidth
, rPosAry
.mnSrcHeight
);
422 SalTwoRect
aPosAry( rPosAry
);
425 drawBitmap( aPosAry
, *pBitmap
);
431 static void DrawPattern50( void*, CGContextRef rContext
)
433 static const CGRect aRects
[2] = { { {0,0}, { 2, 2 } }, { { 2, 2 }, { 2, 2 } } };
434 CG_TRACE( "CGContextAddRects(" << rContext
<< ",aRects,2 )" );
435 CGContextAddRects( rContext
, aRects
, 2 );
436 CG_TRACE( "CGContextFillPath(" << rContext
<< ")" );
437 CGContextFillPath( rContext
);
440 static void getBoundRect( sal_uInt32 nPoints
, const SalPoint
*pPtAry
, long &rX
, long& rY
, long& rWidth
, long& rHeight
)
442 long nX1
= pPtAry
->mnX
;
444 long nY1
= pPtAry
->mnY
;
446 for( sal_uInt32 n
= 1; n
< nPoints
; n
++ )
448 if( pPtAry
[n
].mnX
< nX1
)
452 else if( pPtAry
[n
].mnX
> nX2
)
456 if( pPtAry
[n
].mnY
< nY1
)
460 else if( pPtAry
[n
].mnY
> nY2
)
467 rWidth
= nX2
- nX1
+ 1;
468 rHeight
= nY2
- nY1
+ 1;
471 static SalColor
ImplGetROPSalColor( SalROPColor nROPColor
)
474 if ( nROPColor
== SAL_ROP_0
)
476 nSalColor
= MAKE_SALCOLOR( 0, 0, 0 );
480 nSalColor
= MAKE_SALCOLOR( 255, 255, 255 );
485 // apply the XOR mask to the target context if active and dirty
486 void AquaSalGraphics::ApplyXorContext()
488 if( !mpXorEmulation
)
492 if( mpXorEmulation
->UpdateTarget() )
494 RefreshRect( 0, 0, mnWidth
, mnHeight
); // TODO: refresh minimal changerect
498 void AquaSalGraphics::copyArea( long nDstX
, long nDstY
,long nSrcX
, long nSrcY
,
499 long nSrcWidth
, long nSrcHeight
, sal_uInt16
/*nFlags*/ )
501 SAL_WARN_IF( !mxLayer
, "vcl.quartz", "AquaSalGraphics::copyArea() for non-layered graphics this=" << this );
510 // in XOR mode the drawing context is redirected to the XOR mask
511 // copyArea() always works on the target context though
512 CGContextRef xCopyContext
= mrContext
;
513 if( mpXorEmulation
&& mpXorEmulation
->IsEnabled() )
515 xCopyContext
= mpXorEmulation
->GetTargetContext();
517 // drawing a layer onto its own context causes trouble on OSX => copy it first
518 // TODO: is it possible to get rid of this unneeded copy more often?
519 // e.g. on OSX>=10.5 only this situation causes problems:
520 // mnBitmapDepth && (aDstPoint.x + pSrc->mnWidth) > mnWidth
521 CGLayerRef xSrcLayer
= mxLayer
;
522 // TODO: if( mnBitmapDepth > 0 )
524 const CGSize aSrcSize
= CGSizeMake(nSrcWidth
, nSrcHeight
);
525 xSrcLayer
= CGLayerCreateWithContext( xCopyContext
, aSrcSize
, NULL
);
526 CG_TRACE( "CGLayerCreateWithContext(" << xCopyContext
<< "," << aSrcSize
<< ",NULL) = " << xSrcLayer
);
527 const CGContextRef xSrcContext
= CGLayerGetContext( xSrcLayer
);
528 CG_TRACE( "CGLayerGetContext(" << xSrcLayer
<< ") = " << xSrcContext
);
529 CGPoint aSrcPoint
= CGPointMake(-nSrcX
, -nSrcY
);
532 CG_TRACE( "CGContextTranslateCTM(" << xSrcContext
<< ",0," << nSrcHeight
<< ")" );
533 CGContextTranslateCTM( xSrcContext
, 0, +nSrcHeight
);
534 CG_TRACE( "CGContextScaleCTM(" << xSrcContext
<< ",+1,-1)" );
535 CGContextScaleCTM( xSrcContext
, +1, -1 );
536 aSrcPoint
.y
= (nSrcY
+ nSrcHeight
) - mnHeight
;
538 CG_TRACE( "CGContextDrawLayerAtPoint(" << xSrcContext
<< "," << aSrcPoint
<< "," << mxLayer
<< ")" );
539 CGContextDrawLayerAtPoint( xSrcContext
, aSrcPoint
, mxLayer
);
542 // draw at new destination
543 const CGPoint aDstPoint
= CGPointMake(+nDstX
, +nDstY
);
544 CG_TRACE( "CGContextDrawLayerAtPoint(" << xCopyContext
<< "," << aDstPoint
<< "," << xSrcLayer
<< ")" );
545 CGContextDrawLayerAtPoint( xCopyContext
, aDstPoint
, xSrcLayer
);
548 if( xSrcLayer
!= mxLayer
)
550 CG_TRACE( "CGLayerRelease(" << xSrcLayer
<< ")" );
551 CGLayerRelease( xSrcLayer
);
553 // mark the destination rectangle as updated
554 RefreshRect( nDstX
, nDstY
, nSrcWidth
, nSrcHeight
);
559 void AquaSalGraphics::copyResolution( AquaSalGraphics
& rGraphics
)
561 if( !rGraphics
.mnRealDPIY
&& rGraphics
.mbWindow
&& rGraphics
.mpFrame
)
563 rGraphics
.initResolution( rGraphics
.mpFrame
->getNSWindow() );
565 mnRealDPIX
= rGraphics
.mnRealDPIX
;
566 mnRealDPIY
= rGraphics
.mnRealDPIY
;
571 bool AquaSalGraphics::blendBitmap( const SalTwoRect
&,
577 bool AquaSalGraphics::blendAlphaBitmap( const SalTwoRect
&,
585 bool AquaSalGraphics::drawAlphaBitmap( const SalTwoRect
& rTR
,
586 const SalBitmap
& rSrcBitmap
,
587 const SalBitmap
& rAlphaBmp
)
589 DBG_DRAW_OPERATION("drawAlphaBitmap", true);
591 if (rTR
.mnSrcWidth
!= rTR
.mnDestWidth
|| rTR
.mnSrcHeight
!= rTR
.mnDestHeight
)
593 // TODO - would be better to scale it by the native code
597 // An image mask can't have a depth > 8 bits (should be 1 to 8 bits)
598 if( rAlphaBmp
.GetBitCount() > 8 )
600 DBG_DRAW_OPERATION_EXIT_EARLY("drawAlphaBitmap");
604 // are these two tests really necessary? (see vcl/unx/source/gdi/salgdi2.cxx)
605 // horizontal/vertical mirroring not implemented yet
606 if( rTR
.mnDestWidth
< 0 || rTR
.mnDestHeight
< 0 )
608 DBG_DRAW_OPERATION_EXIT_EARLY("drawAlphaBitmap");
612 const QuartzSalBitmap
& rSrcSalBmp
= static_cast<const QuartzSalBitmap
&>(rSrcBitmap
);
613 const QuartzSalBitmap
& rMaskSalBmp
= static_cast<const QuartzSalBitmap
&>(rAlphaBmp
);
614 CGImageRef xMaskedImage
= rSrcSalBmp
.CreateWithMask( rMaskSalBmp
, rTR
.mnSrcX
,
615 rTR
.mnSrcY
, rTR
.mnSrcWidth
,
619 DBG_DRAW_OPERATION_EXIT_EARLY("drawAlphaBitmap");
623 if ( CheckContext() )
625 const CGRect aDstRect
= CGRectMake( rTR
.mnDestX
, rTR
.mnDestY
, rTR
.mnDestWidth
, rTR
.mnDestHeight
);
626 CG_TRACE( "CGContextDrawImage(" << mrContext
<< "," << aDstRect
<< "," << xMaskedImage
<< ")" );
627 CGContextDrawImage( mrContext
, aDstRect
, xMaskedImage
);
628 RefreshRect( aDstRect
);
631 CG_TRACE("CGImageRelease(" << xMaskedImage
<< ")");
632 CGImageRelease(xMaskedImage
);
634 DBG_DRAW_OPERATION_EXIT("drawAlphaBitmap");
638 bool AquaSalGraphics::drawTransformedBitmap(
639 const basegfx::B2DPoint
& rNull
, const basegfx::B2DPoint
& rX
, const basegfx::B2DPoint
& rY
,
640 const SalBitmap
& rSrcBitmap
, const SalBitmap
* pAlphaBmp
)
642 DBG_DRAW_OPERATION("drawTransformedBitmap", true);
644 if( !CheckContext() )
646 DBG_DRAW_OPERATION_EXIT_EARLY("drawTransformedBitmap");
650 // get the Quartz image
651 CGImageRef xImage
= NULL
;
652 const Size aSize
= rSrcBitmap
.GetSize();
653 const QuartzSalBitmap
& rSrcSalBmp
= static_cast<const QuartzSalBitmap
&>(rSrcBitmap
);
654 const QuartzSalBitmap
* pMaskSalBmp
= static_cast<const QuartzSalBitmap
*>(pAlphaBmp
);
656 xImage
= rSrcSalBmp
.CreateCroppedImage( 0, 0, (int)aSize
.Width(), (int)aSize
.Height() );
658 xImage
= rSrcSalBmp
.CreateWithMask( *pMaskSalBmp
, 0, 0, (int)aSize
.Width(), (int)aSize
.Height() );
661 DBG_DRAW_OPERATION_EXIT_EARLY("drawTransformedBitmap");
665 // setup the image transformation
666 // using the rNull,rX,rY points as destinations for the (0,0),(0,Width),(Height,0) source points
667 CG_TRACE( "CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
668 CGContextSaveGState( mrContext
);
669 const basegfx::B2DVector aXRel
= rX
- rNull
;
670 const basegfx::B2DVector aYRel
= rY
- rNull
;
671 const CGAffineTransform aCGMat
= CGAffineTransformMake(
672 aXRel
.getX()/aSize
.Width(), aXRel
.getY()/aSize
.Width(),
673 aYRel
.getX()/aSize
.Height(), aYRel
.getY()/aSize
.Height(),
674 rNull
.getX(), rNull
.getY());
675 CG_TRACE( "CGContextConcatCTM(" << mrContext
<< "," << aCGMat
<< ")" );
676 CGContextConcatCTM( mrContext
, aCGMat
);
678 // draw the transformed image
679 const CGRect aSrcRect
= CGRectMake(0, 0, aSize
.Width(), aSize
.Height());
680 CG_TRACE( "CGContextDrawImage(" << mrContext
<< "," << aSrcRect
<< "," << xImage
<< ")" );
681 CGContextDrawImage( mrContext
, aSrcRect
, xImage
);
682 CG_TRACE( "CGImageRelease(" << xImage
<< ")" );
683 CGImageRelease( xImage
);
684 // restore the Quartz graphics state
685 CG_TRACE("CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
--);
686 CGContextRestoreGState(mrContext
);
688 // mark the destination as painted
689 const CGRect aDstRect
= CGRectApplyAffineTransform( aSrcRect
, aCGMat
);
690 RefreshRect( aDstRect
);
692 DBG_DRAW_OPERATION_EXIT("drawTransformedBitmap");
696 bool AquaSalGraphics::drawAlphaRect( long nX
, long nY
, long nWidth
,
697 long nHeight
, sal_uInt8 nTransparency
)
699 DBG_DRAW_OPERATION("drawAlphaRect", true);
701 if( !CheckContext() )
703 DBG_DRAW_OPERATION_EXIT_EARLY("drawAlphaRect");
707 // save the current state
708 CG_TRACE( "CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
709 CGContextSaveGState( mrContext
);
710 CG_TRACE( "CGContextSetAlpha(" << mrContext
<< "," << (100-nTransparency
) * (1.0/100) << ")" );
711 CGContextSetAlpha( mrContext
, (100-nTransparency
) * (1.0/100) );
713 CGRect aRect
= CGRectMake(nX
, nY
, nWidth
-1, nHeight
-1);
716 aRect
.origin
.x
+= 0.5;
717 aRect
.origin
.y
+= 0.5;
720 CG_TRACE( "CGContextBeginPath(" << mrContext
<< ")" );
721 CGContextBeginPath( mrContext
);
722 CG_TRACE( "CGContextAddRect(" << mrContext
<< "," << aRect
<< ")" );
723 CGContextAddRect( mrContext
, aRect
);
724 CG_TRACE( "CGContextDrawPath(" << mrContext
<< ",kCGPathFill)" );
725 CGContextDrawPath( mrContext
, kCGPathFill
);
728 CG_TRACE("CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
--);
729 CGContextRestoreGState(mrContext
);
730 RefreshRect( aRect
);
732 DBG_DRAW_OPERATION_EXIT("drawAlphaRect");
736 void AquaSalGraphics::drawBitmap( const SalTwoRect
& rPosAry
, const SalBitmap
& rSalBitmap
)
738 DBG_DRAW_OPERATION("drawBitmap",);
740 if( !CheckContext() )
742 DBG_DRAW_OPERATION_EXIT_EARLY("drawBitmap");
746 const QuartzSalBitmap
& rBitmap
= static_cast<const QuartzSalBitmap
&>(rSalBitmap
);
747 CGImageRef xImage
= rBitmap
.CreateCroppedImage( (int)rPosAry
.mnSrcX
, (int)rPosAry
.mnSrcY
,
748 (int)rPosAry
.mnSrcWidth
, (int)rPosAry
.mnSrcHeight
);
751 DBG_DRAW_OPERATION_EXIT_EARLY("drawBitmap");
755 const CGRect aDstRect
= CGRectMake(rPosAry
.mnDestX
, rPosAry
.mnDestY
, rPosAry
.mnDestWidth
, rPosAry
.mnDestHeight
);
756 CG_TRACE( "CGContextDrawImage(" << mrContext
<< "," << aDstRect
<< "," << xImage
<< ")" );
757 CGContextDrawImage( mrContext
, aDstRect
, xImage
);
758 CG_TRACE( "CGImageRelease(" << xImage
<< ")" );
759 CGImageRelease( xImage
);
760 RefreshRect( aDstRect
);
762 DBG_DRAW_OPERATION_EXIT("drawBitmap");
765 void AquaSalGraphics::drawBitmap( const SalTwoRect
& rPosAry
, const SalBitmap
& rSalBitmap
,SalColor
)
767 OSL_FAIL("not implemented for color masking!");
768 drawBitmap( rPosAry
, rSalBitmap
);
771 void AquaSalGraphics::drawBitmap( const SalTwoRect
& rPosAry
, const SalBitmap
& rSalBitmap
,
772 const SalBitmap
& rTransparentBitmap
)
774 DBG_DRAW_OPERATION("drawBitmap",);
776 if( !CheckContext() )
778 DBG_DRAW_OPERATION_EXIT_EARLY("drawBitmap");
782 const QuartzSalBitmap
& rBitmap
= static_cast<const QuartzSalBitmap
&>(rSalBitmap
);
783 const QuartzSalBitmap
& rMask
= static_cast<const QuartzSalBitmap
&>(rTransparentBitmap
);
784 CGImageRef
xMaskedImage( rBitmap
.CreateWithMask( rMask
, rPosAry
.mnSrcX
, rPosAry
.mnSrcY
,
785 rPosAry
.mnSrcWidth
, rPosAry
.mnSrcHeight
) );
788 DBG_DRAW_OPERATION_EXIT_EARLY("drawBitmap");
792 const CGRect aDstRect
= CGRectMake(rPosAry
.mnDestX
, rPosAry
.mnDestY
, rPosAry
.mnDestWidth
, rPosAry
.mnDestHeight
);
793 CG_TRACE( "CGContextDrawImage(" << mrContext
<< "," << aDstRect
<< "," << xMaskedImage
<< ")" );
794 CGContextDrawImage( mrContext
, aDstRect
, xMaskedImage
);
795 CG_TRACE( "CGImageRelease(" << xMaskedImage
<< ")" );
796 CGImageRelease( xMaskedImage
);
797 RefreshRect( aDstRect
);
799 DBG_DRAW_OPERATION_EXIT("drawBitmap");
804 bool AquaSalGraphics::drawEPS( long nX
, long nY
, long nWidth
, long nHeight
,
805 void* pEpsData
, sal_uLong nByteCount
)
807 // convert the raw data to an NSImageRef
808 NSData
* xNSData
= [NSData dataWithBytes
:pEpsData length
:(int)nByteCount
];
809 NSImageRep
* xEpsImage
= [NSEPSImageRep imageRepWithData
: xNSData
];
814 // get the target context
815 if( !CheckContext() )
819 // NOTE: flip drawing, else the nsimage would be drawn upside down
820 CG_TRACE( "CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
821 CGContextSaveGState( mrContext
);
822 // CGContextTranslateCTM( mrContext, 0, +mnHeight );
823 CG_TRACE( "CGContextScaleCTM(" << mrContext
<< ",+1,-1)" );
824 CGContextScaleCTM( mrContext
, +1, -1 );
825 nY
= /*mnHeight*/ - (nY
+ nHeight
);
827 // prepare the target context
828 NSGraphicsContext
* pOrigNSCtx
= [NSGraphicsContext currentContext
];
831 // create new context
832 NSGraphicsContext
* pDrawNSCtx
= [NSGraphicsContext graphicsContextWithGraphicsPort
: mrContext flipped
: IsFlipped()];
833 // set it, setCurrentContext also releases the prviously set one
834 [NSGraphicsContext setCurrentContext
: pDrawNSCtx
];
837 const NSRect aDstRect
= NSMakeRect( nX
, nY
, nWidth
, nHeight
);
838 const BOOL bOK
= [xEpsImage drawInRect
: aDstRect
];
840 // restore the NSGraphicsContext
841 [NSGraphicsContext setCurrentContext
: pOrigNSCtx
];
842 [pOrigNSCtx release
]; // restore the original retain count
844 CG_TRACE("CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
--);
845 CGContextRestoreGState( mrContext
);
846 // mark the destination rectangle as updated
847 RefreshRect( aDstRect
);
854 void AquaSalGraphics::drawLine( long nX1
, long nY1
, long nX2
, long nY2
)
856 DBG_DRAW_OPERATION("drawLine",);
858 if( nX1
== nX2
&& nY1
== nY2
)
860 // #i109453# platform independent code expects at least one pixel to be drawn
861 drawPixel( nX1
, nY1
);
863 DBG_DRAW_OPERATION_EXIT_EARLY("drawLine");
867 if( !CheckContext() )
869 DBG_DRAW_OPERATION_EXIT_EARLY("drawLine");
873 CG_TRACE( "CGContextBeginPath(" << mrContext
<< ")" );
874 CGContextBeginPath( mrContext
);
875 CG_TRACE( "CGContextMoveToPoint(" << mrContext
<< "," << static_cast<float>(nX1
)+0.5 << "," << static_cast<float>(nY1
)+0.5 << ")" );
876 CGContextMoveToPoint( mrContext
, static_cast<float>(nX1
)+0.5, static_cast<float>(nY1
)+0.5 );
877 CG_TRACE( "CGContextAddLineToPoint(" << mrContext
<< "," << static_cast<float>(nX2
)+0.5 << "," << static_cast<float>(nY2
)+0.5 << ")" );
878 CGContextAddLineToPoint( mrContext
, static_cast<float>(nX2
)+0.5, static_cast<float>(nY2
)+0.5 );
879 CG_TRACE( "CGContextDrawPath(" << mrContext
<< ",kCGPathStroke)" );
880 CGContextDrawPath( mrContext
, kCGPathStroke
);
882 Rectangle
aRefreshRect( nX1
, nY1
, nX2
, nY2
);
884 // Is a call to RefreshRect( aRefreshRect ) missing here?
886 DBG_DRAW_OPERATION_EXIT("drawLine");
889 void AquaSalGraphics::drawMask( const SalTwoRect
& rPosAry
, const SalBitmap
& rSalBitmap
, SalColor nMaskColor
)
891 DBG_DRAW_OPERATION("drawMask",);
893 if( !CheckContext() )
895 DBG_DRAW_OPERATION_EXIT_EARLY("drawMask");
899 const QuartzSalBitmap
& rBitmap
= static_cast<const QuartzSalBitmap
&>(rSalBitmap
);
900 CGImageRef xImage
= rBitmap
.CreateColorMask( rPosAry
.mnSrcX
, rPosAry
.mnSrcY
,
901 rPosAry
.mnSrcWidth
, rPosAry
.mnSrcHeight
,
905 DBG_DRAW_OPERATION_EXIT_EARLY("drawMask");
909 const CGRect aDstRect
= CGRectMake(rPosAry
.mnDestX
, rPosAry
.mnDestY
, rPosAry
.mnDestWidth
, rPosAry
.mnDestHeight
);
910 CG_TRACE( "CGContextDrawImage(" << mrContext
<< "," << aDstRect
<< "," << xImage
<< ")" );
911 CGContextDrawImage( mrContext
, aDstRect
, xImage
);
912 CG_TRACE( "CGImageRelease(" << xImage
<< ")" );
913 CGImageRelease( xImage
);
914 RefreshRect( aDstRect
);
916 DBG_DRAW_OPERATION_EXIT("drawMask");
919 void AquaSalGraphics::drawPixel( long nX
, long nY
)
921 // draw pixel with current line color
922 ImplDrawPixel( nX
, nY
, maLineColor
);
925 void AquaSalGraphics::drawPixel( long nX
, long nY
, SalColor nSalColor
)
927 const RGBAColor
aPixelColor( nSalColor
);
928 ImplDrawPixel( nX
, nY
, aPixelColor
);
931 bool AquaSalGraphics::drawPolyLine(
932 const ::basegfx::B2DPolygon
& rPolyLine
,
933 double fTransparency
,
934 const ::basegfx::B2DVector
& rLineWidths
,
935 basegfx::B2DLineJoin eLineJoin
,
936 com::sun::star::drawing::LineCap eLineCap
)
938 DBG_DRAW_OPERATION("drawPolyLine", true);
940 // short circuit if there is nothing to do
941 const int nPointCount
= rPolyLine
.count();
942 if( nPointCount
<= 0 )
944 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
948 // reject requests that cannot be handled yet
949 if( rLineWidths
.getX() != rLineWidths
.getY() )
951 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
956 if( !CheckContext() )
958 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
963 // #i101491# Aqua does not support B2DLINEJOIN_NONE; return false to use
964 // the fallback (own geometry preparation)
965 // #i104886# linejoin-mode and thus the above only applies to "fat" lines
966 if( (basegfx::B2DLINEJOIN_NONE
== eLineJoin
) &&
967 (rLineWidths
.getX() > 1.3) )
969 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
973 // setup line attributes
974 CGLineJoin aCGLineJoin
= kCGLineJoinMiter
;
977 case ::basegfx::B2DLINEJOIN_NONE
: aCGLineJoin
= /*TODO?*/kCGLineJoinMiter
; break;
978 case ::basegfx::B2DLINEJOIN_MIDDLE
: aCGLineJoin
= /*TODO?*/kCGLineJoinMiter
; break;
979 case ::basegfx::B2DLINEJOIN_BEVEL
: aCGLineJoin
= kCGLineJoinBevel
; break;
980 case ::basegfx::B2DLINEJOIN_MITER
: aCGLineJoin
= kCGLineJoinMiter
; break;
981 case ::basegfx::B2DLINEJOIN_ROUND
: aCGLineJoin
= kCGLineJoinRound
; break;
984 // setup cap attribute
985 CGLineCap
aCGLineCap(kCGLineCapButt
);
989 default: // com::sun::star::drawing::LineCap_BUTT:
991 aCGLineCap
= kCGLineCapButt
;
994 case com::sun::star::drawing::LineCap_ROUND
:
996 aCGLineCap
= kCGLineCapRound
;
999 case com::sun::star::drawing::LineCap_SQUARE
:
1001 aCGLineCap
= kCGLineCapSquare
;
1006 // setup poly-polygon path
1007 CGMutablePathRef xPath
= CGPathCreateMutable();
1008 CG_TRACE( "CGPathCreateMutable() = " << xPath
);
1009 AddPolygonToPath( xPath
, rPolyLine
, rPolyLine
.isClosed(), !getAntiAliasB2DDraw(), true );
1011 const CGRect aRefreshRect
= CGPathGetBoundingBox( xPath
);
1012 CG_TRACE( "CGPathGetBoundingBox(" << xPath
<< ") = " << aRefreshRect
);
1013 // #i97317# workaround for Quartz having problems with drawing small polygons
1014 if( ! ((aRefreshRect
.size
.width
<= 0.125) && (aRefreshRect
.size
.height
<= 0.125)) )
1016 // use the path to prepare the graphics context
1017 CG_TRACE( "CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
1018 CGContextSaveGState( mrContext
);
1019 CG_TRACE( "CGContextBeginPath(" << mrContext
<< ")" );
1020 CGContextBeginPath( mrContext
);
1021 CG_TRACE( "CGContextAddPath(" << mrContext
<< "," << xPath
<< ")" );
1022 CGContextAddPath( mrContext
, xPath
);
1023 // draw path with antialiased line
1024 CGContextSetShouldAntialias( mrContext
, true );
1025 CG_TRACE( "CGContextSetAlpha(" << mrContext
<< "," << 1.0 - fTransparency
<< ")" );
1026 CGContextSetAlpha( mrContext
, 1.0 - fTransparency
);
1027 CGContextSetLineJoin( mrContext
, aCGLineJoin
);
1028 CGContextSetLineCap( mrContext
, aCGLineCap
);
1029 CGContextSetLineWidth( mrContext
, rLineWidths
.getX() );
1030 CG_TRACE( "CGContextDrawPath(" << mrContext
<< ",kCGPathStroke)" );
1031 CGContextDrawPath( mrContext
, kCGPathStroke
);
1032 CG_TRACE( "CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
-- );
1033 CGContextRestoreGState( mrContext
);
1035 // mark modified rectangle as updated
1036 RefreshRect( aRefreshRect
);
1039 CG_TRACE( "CGPathRelease(" << xPath
<< ")" );
1040 CGPathRelease( xPath
);
1042 DBG_DRAW_OPERATION_EXIT("drawPolyLine");
1046 bool AquaSalGraphics::drawPolyLineBezier( sal_uInt32
, const SalPoint
*, const sal_uInt8
* )
1051 bool AquaSalGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon
& rPolyPoly
,
1052 double fTransparency
)
1054 DBG_DRAW_OPERATION("drawPolyPolygon", true);
1056 // short circuit if there is nothing to do
1057 const int nPolyCount
= rPolyPoly
.count();
1058 if( nPolyCount
<= 0 )
1060 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyPolygon");
1064 // ignore invisible polygons
1065 if( (fTransparency
>= 1.0) || (fTransparency
< 0) )
1067 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyPolygon");
1071 // setup poly-polygon path
1072 CGMutablePathRef xPath
= CGPathCreateMutable();
1073 CG_TRACE( "CGPathCreateMutable() = " << xPath
);
1074 for( int nPolyIdx
= 0; nPolyIdx
< nPolyCount
; ++nPolyIdx
)
1076 const ::basegfx::B2DPolygon rPolygon
= rPolyPoly
.getB2DPolygon( nPolyIdx
);
1077 AddPolygonToPath( xPath
, rPolygon
, true, !getAntiAliasB2DDraw(), IsPenVisible() );
1080 const CGRect aRefreshRect
= CGPathGetBoundingBox( xPath
);
1081 CG_TRACE( "CGPathGetBoundingBox(" << xPath
<< ") = " << aRefreshRect
);
1082 // #i97317# workaround for Quartz having problems with drawing small polygons
1083 if( ! ((aRefreshRect
.size
.width
<= 0.125) && (aRefreshRect
.size
.height
<= 0.125)) )
1085 // prepare drawing mode
1086 CGPathDrawingMode eMode
;
1087 if( IsBrushVisible() && IsPenVisible() )
1089 eMode
= kCGPathEOFillStroke
;
1091 else if( IsPenVisible() )
1093 eMode
= kCGPathStroke
;
1095 else if( IsBrushVisible() )
1097 eMode
= kCGPathEOFill
;
1101 SAL_WARN( "vcl.quartz", "Neither pen nor brush visible" );
1102 CG_TRACE( "CGPathRelease(" << xPath
<< ")" );
1103 CGPathRelease( xPath
);
1104 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyPolygon");
1108 // use the path to prepare the graphics context
1109 CG_TRACE( "CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
1110 CGContextSaveGState( mrContext
);
1111 CG_TRACE( "CGContextBeginPath(" << mrContext
<< ")" );
1112 CGContextBeginPath( mrContext
);
1113 CG_TRACE( "CGContextAddPath(" << mrContext
<< "," << xPath
<< ")" );
1114 CGContextAddPath( mrContext
, xPath
);
1116 // draw path with antialiased polygon
1117 CGContextSetShouldAntialias( mrContext
, true );
1118 CG_TRACE( "CGContextSetAlpha(" << mrContext
<< "," << 1.0 - fTransparency
<< ")" );
1119 CGContextSetAlpha( mrContext
, 1.0 - fTransparency
);
1120 CG_TRACE( "CGContextDrawPath(" << mrContext
<< "," << eMode
<< ")" );
1121 CGContextDrawPath( mrContext
, eMode
);
1122 CG_TRACE( "CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
-- );
1123 CGContextRestoreGState( mrContext
);
1125 // mark modified rectangle as updated
1126 RefreshRect( aRefreshRect
);
1129 CG_TRACE( "CGPathRelease(" << xPath
<< ")" );
1130 CGPathRelease( xPath
);
1132 DBG_DRAW_OPERATION_EXIT("drawPolyPolygon");
1136 void AquaSalGraphics::drawPolyPolygon( sal_uInt32 nPolyCount
, const sal_uInt32
*pPoints
, PCONSTSALPOINT
*ppPtAry
)
1138 DBG_DRAW_OPERATION("drawPolyPolygon",);
1140 if( nPolyCount
<= 0 )
1142 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyPolygon");
1146 if( !CheckContext() )
1148 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyPolygon");
1153 long leftX
= 0, topY
= 0, maxWidth
= 0, maxHeight
= 0;
1154 getBoundRect( pPoints
[0], ppPtAry
[0], leftX
, topY
, maxWidth
, maxHeight
);
1155 for( sal_uInt32 n
= 1; n
< nPolyCount
; n
++ )
1157 long nX
= leftX
, nY
= topY
, nW
= maxWidth
, nH
= maxHeight
;
1158 getBoundRect( pPoints
[n
], ppPtAry
[n
], nX
, nY
, nW
, nH
);
1161 maxWidth
+= leftX
- nX
;
1166 maxHeight
+= topY
- nY
;
1169 if( nX
+ nW
> leftX
+ maxWidth
)
1171 maxWidth
= nX
+ nW
- leftX
;
1173 if( nY
+ nH
> topY
+ maxHeight
)
1175 maxHeight
= nY
+ nH
- topY
;
1179 // prepare drawing mode
1180 CGPathDrawingMode eMode
;
1181 if( IsBrushVisible() && IsPenVisible() )
1183 eMode
= kCGPathEOFillStroke
;
1185 else if( IsPenVisible() )
1187 eMode
= kCGPathStroke
;
1189 else if( IsBrushVisible() )
1191 eMode
= kCGPathEOFill
;
1195 SAL_WARN( "vcl.quartz", "Neither pen nor brush visible" );
1196 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyPolygon");
1200 // convert to CGPath
1201 CG_TRACE( "CGContextBeginPath(" << mrContext
<< ")" );
1202 CGContextBeginPath( mrContext
);
1203 if( IsPenVisible() )
1205 for( sal_uInt32 nPoly
= 0; nPoly
< nPolyCount
; nPoly
++ )
1207 const sal_uInt32 nPoints
= pPoints
[nPoly
];
1210 const SalPoint
*pPtAry
= ppPtAry
[nPoly
];
1212 alignLinePoint( pPtAry
, fX
, fY
);
1213 CG_TRACE( "CGContextMoveToPoint(" << mrContext
<< "," << fX
<< "," << fY
<< ")" );
1214 CGContextMoveToPoint( mrContext
, fX
, fY
);
1216 for( sal_uInt32 nPoint
= 1; nPoint
< nPoints
; nPoint
++, pPtAry
++ )
1218 alignLinePoint( pPtAry
, fX
, fY
);
1219 CG_TRACE( "CGContextAddLineToPoint(" << mrContext
<< "," << fX
<< "," << fY
<< ")" );
1220 CGContextAddLineToPoint( mrContext
, fX
, fY
);
1222 CG_TRACE("CGContextClosePath(" << mrContext
<< ")");
1223 CGContextClosePath(mrContext
);
1229 for( sal_uInt32 nPoly
= 0; nPoly
< nPolyCount
; nPoly
++ )
1231 const sal_uInt32 nPoints
= pPoints
[nPoly
];
1234 const SalPoint
*pPtAry
= ppPtAry
[nPoly
];
1235 CG_TRACE( "CGContextMoveToPoint(" << mrContext
<< "," << pPtAry
->mnX
<< "," << pPtAry
->mnY
<< ")" );
1236 CGContextMoveToPoint( mrContext
, pPtAry
->mnX
, pPtAry
->mnY
);
1238 for( sal_uInt32 nPoint
= 1; nPoint
< nPoints
; nPoint
++, pPtAry
++ )
1240 CG_TRACE( "CGContextAddLineToPoint(" << mrContext
<< "," << pPtAry
->mnX
<< "," << pPtAry
->mnY
<< ")" );
1241 CGContextAddLineToPoint( mrContext
, pPtAry
->mnX
, pPtAry
->mnY
);
1243 CG_TRACE("CGContextClosePath(" << mrContext
<< ")");
1244 CGContextClosePath(mrContext
);
1249 CG_TRACE( "CGContextDrawPath(" << mrContext
<< "," <<
1250 (eMode
== kCGPathFill
? "kCGPathFill" :
1251 (eMode
== kCGPathEOFill
? "kCGPathEOFill" :
1252 (eMode
== kCGPathFillStroke
? "kCGPathFillStroke" :
1253 (eMode
== kCGPathEOFillStroke
? "kCGPathEOFillStroke" :
1256 CGContextDrawPath( mrContext
, eMode
);
1258 RefreshRect( leftX
, topY
, maxWidth
, maxHeight
);
1260 DBG_DRAW_OPERATION_EXIT("drawPolyPolygon");
1263 void AquaSalGraphics::drawPolygon( sal_uInt32 nPoints
, const SalPoint
*pPtAry
)
1265 DBG_DRAW_OPERATION("drawPolygon",);
1269 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolygon");
1273 if( !CheckContext() )
1275 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolygon");
1279 long nX
= 0, nY
= 0, nWidth
= 0, nHeight
= 0;
1280 getBoundRect( nPoints
, pPtAry
, nX
, nY
, nWidth
, nHeight
);
1282 CGPathDrawingMode eMode
;
1283 if( IsBrushVisible() && IsPenVisible() )
1285 eMode
= kCGPathEOFillStroke
;
1287 else if( IsPenVisible() )
1289 eMode
= kCGPathStroke
;
1291 else if( IsBrushVisible() )
1293 eMode
= kCGPathEOFill
;
1297 SAL_WARN( "vcl.quartz", "Neither pen nor brush visible" );
1301 CG_TRACE( "CGContextBeginPath(" << mrContext
<< ")" );
1302 CGContextBeginPath( mrContext
);
1304 if( IsPenVisible() )
1307 alignLinePoint( pPtAry
, fX
, fY
);
1308 CG_TRACE( "CGContextMoveToPoint(" << mrContext
<< "," << fX
<< "," << fY
<< ")" );
1309 CGContextMoveToPoint( mrContext
, fX
, fY
);
1311 for( sal_uInt32 nPoint
= 1; nPoint
< nPoints
; nPoint
++, pPtAry
++ )
1313 alignLinePoint( pPtAry
, fX
, fY
);
1314 CG_TRACE( "CGContextAddLineToPoint(" << mrContext
<< "," << fX
<< "," << fY
<< ")" );
1315 CGContextAddLineToPoint( mrContext
, fX
, fY
);
1320 CG_TRACE( "CGContextMoveToPoint(" << mrContext
<< "," << pPtAry
->mnX
<< "," << pPtAry
->mnY
<< ")" );
1321 CGContextMoveToPoint( mrContext
, pPtAry
->mnX
, pPtAry
->mnY
);
1323 for( sal_uInt32 nPoint
= 1; nPoint
< nPoints
; nPoint
++, pPtAry
++ )
1325 CG_TRACE( "CGContextAddLineToPoint(" << mrContext
<< "," << pPtAry
->mnX
<< "," << pPtAry
->mnY
<< ")" );
1326 CGContextAddLineToPoint( mrContext
, pPtAry
->mnX
, pPtAry
->mnY
);
1330 CG_TRACE("CGContextClosePath(" << mrContext
<< ")");
1331 CGContextClosePath( mrContext
);
1332 CG_TRACE( "CGContextDrawPath(" << mrContext
<< "," << eMode
<< ")" );
1333 CGContextDrawPath( mrContext
, eMode
);
1334 RefreshRect( nX
, nY
, nWidth
, nHeight
);
1336 DBG_DRAW_OPERATION_EXIT("drawPolygon");
1339 bool AquaSalGraphics::drawPolygonBezier( sal_uInt32
, const SalPoint
*, const sal_uInt8
* )
1344 bool AquaSalGraphics::drawPolyPolygonBezier( sal_uInt32
, const sal_uInt32
*,
1345 const SalPoint
* const*, const sal_uInt8
* const* )
1350 void AquaSalGraphics::drawRect( long nX
, long nY
, long nWidth
, long nHeight
)
1352 DBG_DRAW_OPERATION("drawRect",);
1354 if( !CheckContext() )
1356 DBG_DRAW_OPERATION_EXIT_EARLY("drawRect");
1360 CGRect
aRect( CGRectMake(nX
, nY
, nWidth
, nHeight
) );
1361 if( IsPenVisible() )
1363 aRect
.origin
.x
+= 0.5;
1364 aRect
.origin
.y
+= 0.5;
1365 aRect
.size
.width
-= 1;
1366 aRect
.size
.height
-= 1;
1369 if( IsBrushVisible() )
1371 CG_TRACE( "CGContextFillRect(" << mrContext
<< "," << aRect
<< ")" );
1372 CGContextFillRect( mrContext
, aRect
);
1374 if( IsPenVisible() )
1376 CG_TRACE( "CGContextStrokeRect(" << mrContext
<< "," << aRect
<< ")" );
1377 CGContextStrokeRect( mrContext
, aRect
);
1379 RefreshRect( nX
, nY
, nWidth
, nHeight
);
1381 DBG_DRAW_OPERATION_EXIT("drawRect");
1384 void AquaSalGraphics::drawPolyLine( sal_uInt32 nPoints
, const SalPoint
*pPtAry
)
1386 DBG_DRAW_OPERATION("drawPolyLine",);
1390 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
1394 if( !CheckContext() )
1396 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
1400 long nX
= 0, nY
= 0, nWidth
= 0, nHeight
= 0;
1401 getBoundRect( nPoints
, pPtAry
, nX
, nY
, nWidth
, nHeight
);
1404 CG_TRACE( "CGContextBeginPath(" << mrContext
<< ")" );
1405 CGContextBeginPath( mrContext
);
1406 alignLinePoint( pPtAry
, fX
, fY
);
1407 CG_TRACE( "CGContextMoveToPoint(" << mrContext
<< "," << fX
<< "," << fY
<< ")" );
1408 CGContextMoveToPoint( mrContext
, fX
, fY
);
1410 for( sal_uInt32 nPoint
= 1; nPoint
< nPoints
; nPoint
++, pPtAry
++ )
1412 alignLinePoint( pPtAry
, fX
, fY
);
1413 CG_TRACE( "CGContextAddLineToPoint(" << mrContext
<< "," << fX
<< "," << fY
<< ")" );
1414 CGContextAddLineToPoint( mrContext
, fX
, fY
);
1416 CG_TRACE( "CGContextDrawPath(" << mrContext
<< ",kCGPathStroke)" );
1417 CGContextDrawPath( mrContext
, kCGPathStroke
);
1419 RefreshRect( nX
, nY
, nWidth
, nHeight
);
1421 DBG_DRAW_OPERATION_EXIT("drawPolyLine");
1424 sal_uInt16
AquaSalGraphics::GetBitCount() const
1426 sal_uInt16 nBits
= mnBitmapDepth
? mnBitmapDepth
: 32;//24;
1430 SalBitmap
* AquaSalGraphics::getBitmap( long nX
, long nY
, long nDX
, long nDY
)
1432 SAL_WARN_IF( !mxLayer
, "vcl.quartz", "AquaSalGraphics::getBitmap() with no layer this=" << this );
1436 QuartzSalBitmap
* pBitmap
= new QuartzSalBitmap
;
1437 if( !pBitmap
->Create( mxLayer
, mnBitmapDepth
, nX
, nY
, nDX
, nDY
) )
1445 SystemGraphicsData
AquaSalGraphics::GetGraphicsData() const
1447 SystemGraphicsData aRes
;
1448 aRes
.nSize
= sizeof(aRes
);
1449 aRes
.rCGContext
= mrContext
;
1453 bool AquaSalGraphics::SupportsCairo() const
1455 #if ENABLE_CAIRO_CANVAS
1463 * cairo::createSurface: Create generic Canvas surface using given Cairo Surface
1465 * @param rSurface Cairo Surface
1467 * @return new Surface
1469 cairo::SurfaceSharedPtr
AquaSalGraphics::CreateSurface(const cairo::CairoSurfaceSharedPtr
& rSurface
) const
1471 #if ENABLE_CAIRO_CANVAS
1472 return cairo::SurfaceSharedPtr(new cairo::QuartzSurface(rSurface
));
1475 return cairo::SurfaceSharedPtr();
1480 * cairo::createSurface: Create Canvas surface using given VCL Window or Virtualdevice
1482 * @param rSurface Cairo Surface
1484 * For VCL Window, use platform native system environment data (struct SystemEnvData in vcl/inc/sysdata.hxx)
1485 * For VCL Virtualdevice, use platform native system graphics data (struct SystemGraphicsData in vcl/inc/sysdata.hxx)
1487 * @return new Surface
1489 cairo::SurfaceSharedPtr
AquaSalGraphics::CreateSurface( const OutputDevice
& rRefDevice
,
1490 int x
, int y
, int width
, int height
) const
1492 cairo::SurfaceSharedPtr surf
;
1493 #if ENABLE_CAIRO_CANVAS
1494 if( rRefDevice
.GetOutDevType() == OUTDEV_WINDOW
)
1496 const vcl::Window
&rWindow
= (const vcl::Window
&) rRefDevice
;
1497 const SystemEnvData
* pSysData
= GetSysData(&rWindow
);
1499 surf
= cairo::SurfaceSharedPtr(new cairo::QuartzSurface(pSysData
->pView
, x
, y
, width
, height
));
1501 else if( rRefDevice
.GetOutDevType() == OUTDEV_VIRDEV
)
1503 SystemGraphicsData aSysData
= ((const VirtualDevice
&) rRefDevice
).GetSystemGfxData();
1505 if (aSysData
.rCGContext
)
1506 surf
= cairo::SurfaceSharedPtr(new cairo::QuartzSurface(aSysData
.rCGContext
, x
, y
, width
, height
));
1519 * cairo::createBitmapSurface: Create platform native Canvas surface from BitmapSystemData
1520 * @param OutputDevice (not used)
1521 * @param rData Platform native image data (struct BitmapSystemData in vcl/inc/bitmap.hxx)
1522 * @param rSize width and height of the new surface
1524 * Create a surface based on image data on rData
1526 * @return new surface or empty surface
1528 cairo::SurfaceSharedPtr
AquaSalGraphics::CreateBitmapSurface( const OutputDevice
& /* rRefDevice */,
1529 const BitmapSystemData
& rData
,
1530 const Size
& rSize
) const
1532 #if ENABLE_CAIRO_CANVAS
1533 OSL_TRACE( "requested size: %d x %d available size: %d x %d",
1534 rSize
.Width(), rSize
.Height(), rData
.mnWidth
, rData
.mnHeight
);
1536 if ( rData
.mnWidth
== rSize
.Width() && rData
.mnHeight
== rSize
.Height() )
1538 CGContextRef rContext
= (CGContextRef
)rData
.rImageContext
;
1539 OSL_TRACE("Canvas::cairo::createBitmapSurface(): New native image surface, context = %p.", rData
.rImageContext
);
1541 return cairo::SurfaceSharedPtr(new cairo::QuartzSurface(rContext
, 0, 0, rData
.mnWidth
, rData
.mnHeight
));
1547 return cairo::SurfaceSharedPtr();
1550 css::uno::Any
AquaSalGraphics::GetNativeSurfaceHandle(cairo::SurfaceSharedPtr
& rSurface
, const ::basegfx::B2ISize
& /*rSize*/) const
1553 #if ENABLE_CAIRO_CANVAS
1554 cairo::QuartzSurface
* pQuartzSurface
= dynamic_cast<cairo::QuartzSurface
*>(rSurface
.get());
1555 OSL_ASSERT(pQuartzSurface
);
1556 handle
= sal_IntPtr (pQuartzSurface
->getCGContext());
1561 css::uno::Sequence
< css::uno::Any
> args( 1 );
1562 args
[0] = css::uno::Any( handle
);
1563 return css::uno::Any( args
);
1566 long AquaSalGraphics::GetGraphicsWidth() const
1581 if( mbWindow
&& mpFrame
)
1583 w
= mpFrame
->maGeometry
.nWidth
;
1590 SalColor
AquaSalGraphics::getPixel( long nX
, long nY
)
1592 // return default value on printers or when out of bounds
1593 if( !mxLayer
|| (nX
< 0) || (nX
>= mnWidth
) ||
1594 (nY
< 0) || (nY
>= mnHeight
))
1598 // prepare creation of matching a CGBitmapContext
1599 #if defined OSL_BIGENDIAN
1600 struct{ unsigned char b
, g
, r
, a
; } aPixel
;
1602 struct{ unsigned char a
, r
, g
, b
; } aPixel
;
1605 // create a one-pixel bitmap context
1606 // TODO: is it worth to cache it?
1607 CGContextRef xOnePixelContext
=
1608 CGBitmapContextCreate( &aPixel
, 1, 1, 8, 32,
1609 GetSalData()->mxRGBSpace
, kCGImageAlphaNoneSkipFirst
| kCGBitmapByteOrder32Big
);
1611 CG_TRACE( "CGBitmapContextCreate(1x1x8) = " << xOnePixelContext
);
1613 // update this graphics layer
1616 // copy the requested pixel into the bitmap context
1621 const CGPoint aCGPoint
= CGPointMake(-nX
, -nY
);
1622 CG_TRACE( "CGContextDrawLayerAtPoint(" << xOnePixelContext
<< "," << aCGPoint
<< "," << mxLayer
<< ")" );
1623 CGContextDrawLayerAtPoint( xOnePixelContext
, aCGPoint
, mxLayer
);
1624 CG_TRACE( "CGContextRelease(" << xOnePixelContext
<< ")" );
1625 CGContextRelease( xOnePixelContext
);
1627 SalColor nSalColor
= MAKE_SALCOLOR( aPixel
.r
, aPixel
.g
, aPixel
.b
);
1633 void AquaSalGraphics::GetResolution( sal_Int32
& rDPIX
, sal_Int32
& rDPIY
)
1637 initResolution( (mbWindow
&& mpFrame
) ? mpFrame
->getNSWindow() : nil
);
1646 void AquaSalGraphics::ImplDrawPixel( long nX
, long nY
, const RGBAColor
& rColor
)
1648 if( !CheckContext() )
1652 // overwrite the fill color
1653 CG_TRACE( "CGContextSetFillColor(" << mrContext
<< "," << rColor
<< ")" );
1654 CGContextSetFillColor( mrContext
, rColor
.AsArray() );
1655 // draw 1x1 rect, there is no pixel drawing in Quartz
1656 const CGRect aDstRect
= CGRectMake(nX
, nY
, 1, 1);
1657 CG_TRACE( "CGContextFillRect(" << mrContext
<< "," << aDstRect
<< ")" );
1658 CGContextFillRect( mrContext
, aDstRect
);
1659 RefreshRect( aDstRect
);
1660 // reset the fill color
1661 CG_TRACE( "CGContextSetFillColor(" << mrContext
<< "," << maFillColor
<< ")" );
1662 CGContextSetFillColor( mrContext
, maFillColor
.AsArray() );
1667 void AquaSalGraphics::initResolution( NSWindow
* )
1669 // #i100617# read DPI only once; there is some kind of weird caching going on
1670 // if the main screen changes
1671 // FIXME: this is really unfortunate and needs to be investigated
1673 SalData
* pSalData
= GetSalData();
1674 if( pSalData
->mnDPIX
== 0 || pSalData
->mnDPIY
== 0 )
1676 NSScreen
* pScreen
= nil
;
1679 many woes went into the try to have different resolutions
1680 on different screens. The result of these trials is that OOo is not ready
1681 for that yet, vcl and applications would need to be adapted.
1683 Unfortunately this is not possible in the 3.0 timeframe.
1684 So let's stay with one resolution for all Windows and VirtualDevices
1685 which is the resolution of the main screen
1687 This of course also means that measurements are exact only on the main screen.
1688 For activating different resolutions again just comment out the two lines below.
1691 pScreen = [pWin screen];
1693 if( pScreen
== nil
)
1695 NSArray
* pScreens
= [NSScreen screens
];
1696 if( pScreens
&& [pScreens count
] > 0)
1697 pScreen
= [pScreens objectAtIndex
: 0];
1700 mnRealDPIX
= mnRealDPIY
= 96;
1703 NSDictionary
* pDev
= [pScreen deviceDescription
];
1706 NSNumber
* pVal
= [pDev objectForKey
: @
"NSScreenNumber"];
1709 // FIXME: casting a long to CGDirectDisplayID is evil, but
1710 // Apple suggest to do it this way
1711 const CGDirectDisplayID nDisplayID
= (CGDirectDisplayID
)[pVal longValue
];
1712 const CGSize aSize
= CGDisplayScreenSize( nDisplayID
); // => result is in millimeters
1713 mnRealDPIX
= static_cast<long>((CGDisplayPixelsWide( nDisplayID
) * 25.4) / aSize
.width
);
1714 mnRealDPIY
= static_cast<long>((CGDisplayPixelsHigh( nDisplayID
) * 25.4) / aSize
.height
);
1718 OSL_FAIL( "no resolution found in device description" );
1723 OSL_FAIL( "no device description" );
1728 OSL_FAIL( "no screen found" );
1731 // #i107076# maintaining size-WYSIWYG-ness causes many problems for
1732 // low-DPI, high-DPI or for mis-reporting devices
1733 // => it is better to limit the calculation result then
1734 static const int nMinDPI
= 72;
1735 if( (mnRealDPIX
< nMinDPI
) || (mnRealDPIY
< nMinDPI
) )
1737 mnRealDPIX
= mnRealDPIY
= nMinDPI
;
1739 // Note that on a Retina display, the "mnRealDPIX" as
1740 // calculated above is not the true resolution of the display,
1741 // but the "logical" one, or whatever the correct terminology
1742 // is. (For instance on a 5K 27in iMac, it's 108.) So at
1743 // least currently, it won't be over 200. I don't know whether
1744 // this test is a "sanity check", or whether there is some
1745 // real reason to limit this to 200.
1746 static const int nMaxDPI
= 200;
1747 if( (mnRealDPIX
> nMaxDPI
) || (mnRealDPIY
> nMaxDPI
) )
1749 mnRealDPIX
= mnRealDPIY
= nMaxDPI
;
1751 // for OSX any anisotropy reported for the display resolution is best ignored (e.g. TripleHead2Go)
1752 mnRealDPIX
= mnRealDPIY
= (mnRealDPIX
+ mnRealDPIY
+ 1) / 2;
1754 pSalData
->mnDPIX
= mnRealDPIX
;
1755 pSalData
->mnDPIY
= mnRealDPIY
;
1759 mnRealDPIX
= pSalData
->mnDPIX
;
1760 mnRealDPIY
= pSalData
->mnDPIY
;
1766 void AquaSalGraphics::invert( long nX
, long nY
, long nWidth
, long nHeight
, SalInvert nFlags
)
1768 if ( CheckContext() )
1770 CGRect aCGRect
= CGRectMake( nX
, nY
, nWidth
, nHeight
);
1771 CG_TRACE("CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
1772 CGContextSaveGState(mrContext
);
1774 if ( nFlags
& SAL_INVERT_TRACKFRAME
)
1776 const CGFloat dashLengths
[2] = { 4.0, 4.0 }; // for drawing dashed line
1777 CGContextSetBlendMode( mrContext
, kCGBlendModeDifference
);
1778 CG_TRACE( "CGContextSetRGBStrokeColor(" << mrContext
<< ",{1,1,1,1})" );
1779 CGContextSetRGBStrokeColor ( mrContext
, 1.0, 1.0, 1.0, 1.0 );
1780 CGContextSetLineDash ( mrContext
, 0, dashLengths
, 2 );
1781 CGContextSetLineWidth( mrContext
, 2.0);
1782 CG_TRACE("CGContextStrokeRect(" << mrContext
<< "," << aCGRect
<< ")" );
1783 CGContextStrokeRect ( mrContext
, aCGRect
);
1785 else if ( nFlags
& SAL_INVERT_50
)
1787 //CGContextSetAllowsAntialiasing( mrContext, false );
1788 CGContextSetBlendMode(mrContext
, kCGBlendModeDifference
);
1789 CGContextAddRect( mrContext
, aCGRect
);
1794 CGContextSetBlendMode(mrContext
, kCGBlendModeDifference
);
1795 CG_TRACE( "CGContextSetRGBFillColor(" << mrContext
<< ",{1,1,1,1})" );
1796 CGContextSetRGBFillColor ( mrContext
,1.0, 1.0, 1.0 , 1.0 );
1797 CG_TRACE("CGContextFillRect(" << mrContext
<< "," << aCGRect
<< ")" );
1798 CGContextFillRect ( mrContext
, aCGRect
);
1800 CG_TRACE( "CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
-- );
1801 CGContextRestoreGState( mrContext
);
1802 RefreshRect( aCGRect
);
1808 CGPoint
* makeCGptArray(sal_uInt32 nPoints
, const SalPoint
* pPtAry
)
1810 CGPoint
*CGpoints
= new CGPoint
[nPoints
];
1813 for(sal_uLong i
=0;i
<nPoints
;i
++)
1815 CGpoints
[i
].x
= pPtAry
[i
].mnX
;
1816 CGpoints
[i
].y
= pPtAry
[i
].mnY
;
1824 void AquaSalGraphics::invert( sal_uInt32 nPoints
, const SalPoint
* pPtAry
, SalInvert nSalFlags
)
1826 if ( CheckContext() )
1828 CG_TRACE("CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
1829 CGContextSaveGState(mrContext
);
1830 CGPoint
* CGpoints
= makeCGptArray(nPoints
,pPtAry
);
1831 CGContextAddLines ( mrContext
, CGpoints
, nPoints
);
1832 if ( nSalFlags
& SAL_INVERT_TRACKFRAME
)
1834 const CGFloat dashLengths
[2] = { 4.0, 4.0 }; // for drawing dashed line
1835 CGContextSetBlendMode( mrContext
, kCGBlendModeDifference
);
1836 CG_TRACE( "CGContextSetRGBStrokeColor(" << mrContext
<< ",{1,1,1,1})" );
1837 CGContextSetRGBStrokeColor ( mrContext
, 1.0, 1.0, 1.0, 1.0 );
1838 CGContextSetLineDash ( mrContext
, 0, dashLengths
, 2 );
1839 CGContextSetLineWidth( mrContext
, 2.0);
1840 CG_TRACE("CGContextStrokePath(" << mrContext
<< ")" );
1841 CGContextStrokePath ( mrContext
);
1843 else if ( nSalFlags
& SAL_INVERT_50
)
1845 CGContextSetBlendMode(mrContext
, kCGBlendModeDifference
);
1850 CGContextSetBlendMode( mrContext
, kCGBlendModeDifference
);
1851 CG_TRACE( "CGContextSetRGBFillColor(" << mrContext
<< ",{1,1,1,1})" );
1852 CGContextSetRGBFillColor( mrContext
, 1.0, 1.0, 1.0, 1.0 );
1853 CG_TRACE("CGContextFillPath(" << mrContext
<< ")" );
1854 CGContextFillPath( mrContext
);
1856 const CGRect aRefreshRect
= CGContextGetClipBoundingBox(mrContext
);
1857 CG_TRACE( "CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
-- );
1858 CGContextRestoreGState( mrContext
);
1860 RefreshRect( aRefreshRect
);
1864 void AquaSalGraphics::Pattern50Fill()
1866 static const CGFloat aFillCol
[4] = { 1,1,1,1 };
1867 static const CGPatternCallbacks aCallback
= { 0, &DrawPattern50
, NULL
};
1868 static const CGColorSpaceRef mxP50Space
= CGColorSpaceCreatePattern( GetSalData()->mxRGBSpace
);
1869 static const CGPatternRef mxP50Pattern
= CGPatternCreate( NULL
, CGRectMake( 0, 0, 4, 4 ),
1870 CGAffineTransformIdentity
, 4, 4,
1871 kCGPatternTilingConstantSpacing
,
1872 false, &aCallback
);
1873 SAL_WARN_IF( !mrContext
, "vcl.quartz", "mrContext is NULL" );
1874 CG_TRACE( "CGContextSetFillColorSpace(" << mrContext
<< "," << mxP50Space
<< ")" );
1875 CGContextSetFillColorSpace( mrContext
, mxP50Space
);
1876 CG_TRACE( "CGContextSetFillPattern(" << mrContext
<< "," << mxP50Pattern
<< ",{1,1,1,1})" );
1877 CGContextSetFillPattern( mrContext
, mxP50Pattern
, aFillCol
);
1878 CG_TRACE( "CGContextFillPath(" << mrContext
<< ")" );
1879 CGContextFillPath( mrContext
);
1882 void AquaSalGraphics::ResetClipRegion()
1884 // release old path and indicate no clipping
1887 CG_TRACE( "CGPathRelease(" << mxClipPath
<< ")" );
1888 CGPathRelease( mxClipPath
);
1891 if( CheckContext() )
1897 void AquaSalGraphics::SetState()
1899 CG_TRACE( "CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
--);
1900 CGContextRestoreGState( mrContext
);
1901 CG_TRACE( "CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
1902 CGContextSaveGState( mrContext
);
1907 CG_TRACE( "CGContextBeginPath(" << mrContext
<< ")" );
1908 CGContextBeginPath( mrContext
); // discard any existing path
1909 CG_TRACE( "CGContextAddPath(" << mrContext
<< "," << mxClipPath
<< ")" );
1910 CGContextAddPath( mrContext
, mxClipPath
); // set the current path to the clipping path
1911 CG_TRACE( "CGContextClip(" << mrContext
<< ")" );
1912 CGContextClip( mrContext
); // use it for clipping
1915 // set RGB colorspace and line and fill colors
1916 CG_TRACE( "CGContextSetFillColor(" << mrContext
<< "," << maFillColor
<< ")" );
1917 CGContextSetFillColor( mrContext
, maFillColor
.AsArray() );
1918 CG_TRACE( "CGContextSetStrokeColor(" << mrContext
<< "," << maLineColor
<< ")" );
1919 CGContextSetStrokeColor( mrContext
, maLineColor
.AsArray() );
1920 CGContextSetShouldAntialias( mrContext
, false );
1921 if( mnXorMode
== 2 )
1922 CGContextSetBlendMode( mrContext
, kCGBlendModeDifference
);
1925 void AquaSalGraphics::SetLineColor()
1927 maLineColor
.SetAlpha( 0.0 ); // transparent
1928 if( CheckContext() )
1930 CG_TRACE( "CGContextSetRGBStrokeColor(" << mrContext
<< "," << maLineColor
<< ")" );
1931 CGContextSetRGBStrokeColor( mrContext
, maLineColor
.GetRed(), maLineColor
.GetGreen(),
1932 maLineColor
.GetBlue(), maLineColor
.GetAlpha() );
1936 void AquaSalGraphics::SetLineColor( SalColor nSalColor
)
1938 maLineColor
= RGBAColor( nSalColor
);
1939 if( CheckContext() )
1941 CG_TRACE( "CGContextSetRGBStrokeColor(" << mrContext
<< "," << maLineColor
<< ")" );
1942 CGContextSetRGBStrokeColor( mrContext
, maLineColor
.GetRed(), maLineColor
.GetGreen(),
1943 maLineColor
.GetBlue(), maLineColor
.GetAlpha() );
1947 void AquaSalGraphics::SetFillColor()
1949 maFillColor
.SetAlpha( 0.0 ); // transparent
1950 if( CheckContext() )
1952 CG_TRACE( "CGContextSetRGBFillColor(" << mrContext
<< "," << maFillColor
<< ")" );
1953 CGContextSetRGBFillColor( mrContext
, maFillColor
.GetRed(), maFillColor
.GetGreen(),
1954 maFillColor
.GetBlue(), maFillColor
.GetAlpha() );
1958 void AquaSalGraphics::SetFillColor( SalColor nSalColor
)
1960 maFillColor
= RGBAColor( nSalColor
);
1961 if( CheckContext() )
1963 CG_TRACE( "CGContextSetRGBFillColor(" << mrContext
<< "," << maFillColor
<< ")" );
1964 CGContextSetRGBFillColor( mrContext
, maFillColor
.GetRed(), maFillColor
.GetGreen(),
1965 maFillColor
.GetBlue(), maFillColor
.GetAlpha() );
1969 bool AquaSalGraphics::supportsOperation( OutDevSupportType eType
) const
1974 case OutDevSupport_TransparentRect
:
1975 case OutDevSupport_B2DClip
:
1976 case OutDevSupport_B2DDraw
:
1984 bool AquaSalGraphics::setClipRegion( const vcl::Region
& i_rClip
)
1986 // release old clip path
1989 CG_TRACE( "CGPathRelease(" << mxClipPath
<< ")" );
1990 CGPathRelease( mxClipPath
);
1993 mxClipPath
= CGPathCreateMutable();
1994 CG_TRACE( "CGPathCreateMutable() = " << mxClipPath
);
1996 // set current path, either as polypolgon or sequence of rectangles
1997 if(i_rClip
.HasPolyPolygonOrB2DPolyPolygon())
1999 const basegfx::B2DPolyPolygon
aClip(i_rClip
.GetAsB2DPolyPolygon());
2001 AddPolyPolygonToPath( mxClipPath
, aClip
, !getAntiAliasB2DDraw(), false );
2005 RectangleVector aRectangles
;
2006 i_rClip
.GetRegionRectangles(aRectangles
);
2008 for(RectangleVector::const_iterator
aRectIter(aRectangles
.begin()); aRectIter
!= aRectangles
.end(); ++aRectIter
)
2010 const long nW(aRectIter
->Right() - aRectIter
->Left() + 1); // uses +1 logic in original
2014 const long nH(aRectIter
->Bottom() - aRectIter
->Top() + 1); // uses +1 logic in original
2018 const CGRect aRect
= CGRectMake( aRectIter
->Left(), aRectIter
->Top(), nW
, nH
);
2019 CG_TRACE( "CGPathAddRect(" << mxClipPath
<< ",NULL," << aRect
<< ")" );
2020 CGPathAddRect( mxClipPath
, NULL
, aRect
);
2025 // set the current path as clip region
2026 if( CheckContext() )
2033 void AquaSalGraphics::SetROPFillColor( SalROPColor nROPColor
)
2036 SetFillColor( ImplGetROPSalColor( nROPColor
) );
2039 void AquaSalGraphics::SetROPLineColor( SalROPColor nROPColor
)
2042 SetLineColor( ImplGetROPSalColor( nROPColor
) );
2045 void AquaSalGraphics::SetXORMode( bool bSet
, bool bInvertOnly
)
2047 // return early if XOR mode remains unchanged
2052 if( ! bSet
&& mnXorMode
== 2 )
2054 CGContextSetBlendMode( mrContext
, kCGBlendModeNormal
);
2058 else if( bSet
&& bInvertOnly
&& mnXorMode
== 0)
2060 CGContextSetBlendMode( mrContext
, kCGBlendModeDifference
);
2065 if( (mpXorEmulation
== NULL
) && !bSet
)
2069 if( (mpXorEmulation
!= NULL
) && (bSet
== mpXorEmulation
->IsEnabled()) )
2073 if( !CheckContext() )
2077 // prepare XOR emulation
2078 if( !mpXorEmulation
)
2080 mpXorEmulation
= new XorEmulation();
2081 mpXorEmulation
->SetTarget( mnWidth
, mnHeight
, mnBitmapDepth
, mrContext
, mxLayer
);
2084 // change the XOR mode
2087 mpXorEmulation
->Enable();
2088 mrContext
= mpXorEmulation
->GetMaskContext();
2093 mpXorEmulation
->UpdateTarget();
2094 mpXorEmulation
->Disable();
2095 mrContext
= mpXorEmulation
->GetTargetContext();
2102 void AquaSalGraphics::updateResolution()
2104 DBG_ASSERT( mbWindow
, "updateResolution on inappropriate graphics" );
2106 initResolution( (mbWindow
&& mpFrame
) ? mpFrame
->getNSWindow() : nil
);
2111 XorEmulation::XorEmulation()
2112 : m_xTargetLayer( NULL
)
2113 , m_xTargetContext( NULL
)
2114 , m_xMaskContext( NULL
)
2115 , m_xTempContext( NULL
)
2116 , m_pMaskBuffer( NULL
)
2117 , m_pTempBuffer( NULL
)
2118 , m_nBufferLongs( 0 )
2119 , m_bIsEnabled( false )
2121 SAL_INFO( "vcl.quartz", "XorEmulation::XorEmulation() this=" << this );
2124 XorEmulation::~XorEmulation()
2126 SAL_INFO( "vcl.quartz", "XorEmulation::~XorEmulation() this=" << this );
2128 SetTarget( 0, 0, 0, NULL
, NULL
);
2131 void XorEmulation::SetTarget( int nWidth
, int nHeight
, int nTargetDepth
,
2132 CGContextRef xTargetContext
, CGLayerRef xTargetLayer
)
2134 SAL_INFO( "vcl.quartz", "XorEmulation::SetTarget() this=" << this << " (" << nWidth
<< "x" << nHeight
<< ") depth=" << nTargetDepth
<< " context=" << xTargetContext
<< " layer=" << xTargetLayer
);
2136 // prepare to replace old mask+temp context
2137 if( m_xMaskContext
)
2139 // cleanup the mask context
2140 CG_TRACE( "CGContextRelease(" << m_xMaskContext
<< ")" );
2141 CGContextRelease( m_xMaskContext
);
2142 delete[] m_pMaskBuffer
;
2143 m_xMaskContext
= NULL
;
2144 m_pMaskBuffer
= NULL
;
2146 // cleanup the temp context if needed
2147 if( m_xTempContext
)
2149 CG_TRACE( "CGContextRelease(" << m_xTempContext
<< ")" );
2150 CGContextRelease( m_xTempContext
);
2151 delete[] m_pTempBuffer
;
2152 m_xTempContext
= NULL
;
2153 m_pTempBuffer
= NULL
;
2157 // return early if there is nothing more to do
2158 if( !xTargetContext
)
2162 // retarget drawing operations to the XOR mask
2163 m_xTargetLayer
= xTargetLayer
;
2164 m_xTargetContext
= xTargetContext
;
2166 // prepare creation of matching CGBitmaps
2167 CGColorSpaceRef aCGColorSpace
= GetSalData()->mxRGBSpace
;
2168 CGBitmapInfo aCGBmpInfo
= kCGImageAlphaNoneSkipFirst
;
2169 int nBitDepth
= nTargetDepth
;
2174 int nBytesPerRow
= (nBitDepth
== 16) ? 2 : 4;
2175 const size_t nBitsPerComponent
= (nBitDepth
== 16) ? 5 : 8;
2176 if( nBitDepth
<= 8 )
2178 aCGColorSpace
= GetSalData()->mxGraySpace
;
2179 aCGBmpInfo
= kCGImageAlphaNone
;
2182 nBytesPerRow
*= nWidth
;
2183 m_nBufferLongs
= (nHeight
* nBytesPerRow
+ sizeof(sal_uLong
)-1) / sizeof(sal_uLong
);
2185 // create a XorMask context
2186 m_pMaskBuffer
= new sal_uLong
[ m_nBufferLongs
];
2187 m_xMaskContext
= CGBitmapContextCreate( m_pMaskBuffer
,
2189 nBitsPerComponent
, nBytesPerRow
,
2190 aCGColorSpace
, aCGBmpInfo
);
2191 SAL_WARN_IF( !m_xMaskContext
, "vcl.quartz", "mask context creation failed" );
2192 CG_TRACE( "CGBitmapContextCreate(" << nWidth
<< "x" << nHeight
<< ") = " << m_xMaskContext
);
2194 // reset the XOR mask to black
2195 memset( m_pMaskBuffer
, 0, m_nBufferLongs
* sizeof(sal_uLong
) );
2197 // a bitmap context will be needed for manual XORing
2198 // create one unless the target context is a bitmap context
2200 m_pTempBuffer
= static_cast<sal_uLong
*>(CGBitmapContextGetData( m_xTargetContext
));
2201 if( !m_pTempBuffer
)
2203 // create a bitmap context matching to the target context
2204 m_pTempBuffer
= new sal_uLong
[ m_nBufferLongs
];
2205 m_xTempContext
= CGBitmapContextCreate( m_pTempBuffer
,
2207 nBitsPerComponent
, nBytesPerRow
,
2208 aCGColorSpace
, aCGBmpInfo
);
2209 SAL_WARN_IF( !m_xTempContext
, "vcl.quartz", "temp context creation failed" );
2210 CG_TRACE( "CGBitmapContextCreate(" << nWidth
<< "x" << nHeight
<< ") = " << m_xTempContext
);
2213 // initialize XOR mask context for drawing
2214 CGContextSetFillColorSpace( m_xMaskContext
, aCGColorSpace
);
2215 CGContextSetStrokeColorSpace( m_xMaskContext
, aCGColorSpace
);
2216 CGContextSetShouldAntialias( m_xMaskContext
, false );
2218 // improve the XorMask's XOR emulation a litte
2219 // NOTE: currently only enabled for monochrome contexts
2220 if( aCGColorSpace
== GetSalData()->mxGraySpace
)
2222 CGContextSetBlendMode( m_xMaskContext
, kCGBlendModeDifference
);
2224 // intialize the transformation matrix to the drawing target
2225 const CGAffineTransform aCTM
= CGContextGetCTM( xTargetContext
);
2226 CGContextConcatCTM( m_xMaskContext
, aCTM
);
2227 if( m_xTempContext
)
2229 CGContextConcatCTM( m_xTempContext
, aCTM
);
2231 // initialize the default XorMask graphics state
2232 CGContextSaveGState( m_xMaskContext
);
2235 bool XorEmulation::UpdateTarget()
2237 SAL_INFO( "vcl.quartz", "XorEmulation::UpdateTarget() this=" << this );
2243 // update the temp bitmap buffer if needed
2244 if( m_xTempContext
)
2246 SAL_WARN_IF( m_xTargetContext
== NULL
, "vcl.quartz", "Target layer is NULL");
2247 CG_TRACE( "CGContextDrawLayerAtPoint(" << m_xTempContext
<< "," << CGPointZero
<< "," << m_xTargetLayer
<< ")" );
2248 CGContextDrawLayerAtPoint( m_xTempContext
, CGPointZero
, m_xTargetLayer
);
2250 // do a manual XOR with the XorMask
2251 // this approach suffices for simple color manipulations
2252 // and also the complex-clipping-XOR-trick used in metafiles
2253 const sal_uLong
* pSrc
= m_pMaskBuffer
;
2254 sal_uLong
* pDst
= m_pTempBuffer
;
2255 for( int i
= m_nBufferLongs
; --i
>= 0;)
2257 *(pDst
++) ^= *(pSrc
++);
2259 // write back the XOR results to the target context
2260 if( m_xTempContext
)
2262 CGImageRef xXorImage
= CGBitmapContextCreateImage( m_xTempContext
);
2263 CG_TRACE( "CGBitmapContextCreateImage(" << m_xTempContext
<< ") = " << xXorImage
);
2264 const int nWidth
= (int)CGImageGetWidth( xXorImage
);
2265 const int nHeight
= (int)CGImageGetHeight( xXorImage
);
2266 // TODO: update minimal changerect
2267 const CGRect aFullRect
= CGRectMake(0, 0, nWidth
, nHeight
);
2268 CG_TRACE( "CGContextDrawImage(" << m_xTargetContext
<< "," << aFullRect
<< "," << xXorImage
<< ")" );
2269 CGContextDrawImage( m_xTargetContext
, aFullRect
, xXorImage
);
2270 CG_TRACE( "CGImageRelease(" << xXorImage
<< ")" );
2271 CGImageRelease( xXorImage
);
2274 // reset the XorMask to black again
2275 // TODO: not needed for last update
2276 memset( m_pMaskBuffer
, 0, m_nBufferLongs
* sizeof(sal_uLong
) );
2278 // TODO: return FALSE if target was not changed
2282 void AquaSalGraphics::SetVirDevGraphics( CGLayerRef xLayer
, CGContextRef xContext
,
2285 SAL_INFO( "vcl.quartz", "SetVirDevGraphics() this=" << this << " layer=" << xLayer
<< " context=" << xContext
);
2294 (void) nBitmapDepth
;
2298 // We will return early a few lines lower.
2299 // Undo the "stack initialization" done at the initial call of
2300 // this method, see end.
2301 CG_TRACE( "CGContextRestoreGState(" << mrContext
<< ") " << mnContextStackDepth
--);
2302 CGContextRestoreGState( mrContext
);
2306 // set graphics properties
2308 mrContext
= xContext
;
2311 mnBitmapDepth
= nBitmapDepth
;
2315 mbForeignContext
= xContext
!= NULL
;
2318 // return early if the virdev is being destroyed
2322 // get new graphics properties
2325 mnWidth
= CGBitmapContextGetWidth( mrContext
);
2326 mnHeight
= CGBitmapContextGetHeight( mrContext
);
2327 CG_TRACE( "CGBitmapContextGetWidth&Height(" << mrContext
<< ") = " << mnWidth
<< "x" << mnHeight
);
2331 const CGSize aSize
= CGLayerGetSize( mxLayer
);
2332 mnWidth
= static_cast<int>(aSize
.width
);
2333 mnHeight
= static_cast<int>(aSize
.height
);
2334 CG_TRACE( "CGLayerGetSize(" << mxLayer
<< ") = " << aSize
);
2337 // prepare graphics for drawing
2338 const CGColorSpaceRef aCGColorSpace
= GetSalData()->mxRGBSpace
;
2339 CGContextSetFillColorSpace( mrContext
, aCGColorSpace
);
2340 CGContextSetStrokeColorSpace( mrContext
, aCGColorSpace
);
2342 // re-enable XorEmulation for the new context
2343 if( mpXorEmulation
)
2345 mpXorEmulation
->SetTarget( mnWidth
, mnHeight
, mnBitmapDepth
, mrContext
, mxLayer
);
2346 if( mpXorEmulation
->IsEnabled() )
2347 mrContext
= mpXorEmulation
->GetMaskContext();
2350 // initialize stack of CGContext states
2351 CG_TRACE( "CGContextSaveGState(" << mrContext
<< ") " << ++mnContextStackDepth
);
2352 CGContextSaveGState( mrContext
);
2356 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */