bump product version to 5.0.4.1
[LibreOffice.git] / vcl / quartz / salgdicommon.cxx
blob453ae5c1f0d7148f891246dd2c9448541bcc7d69
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
22 #include <cstring>
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"
35 #include "sft.hxx"
37 #ifdef IOS
38 #include "saldatabasic.hxx"
39 #include <vcl/sysdata.hxx>
40 #endif
42 #include <config_cairo_canvas.h>
43 #if ENABLE_CAIRO_CANVAS
44 #include "cairo_quartz_cairo.hxx"
45 #endif
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) \
53 do { \
54 if (DBG_DRAW_ROUNDS >= 0) { \
55 if (DBG_DRAW_COUNTER++ > DBG_DRAW_ROUNDS) \
56 return v; \
57 SAL_DEBUG("===> " << s << " " << DBG_DRAW_COUNTER); \
58 } \
59 } while (false)
61 #define DBG_DRAW_OPERATION_EXIT(s) \
62 do { \
63 if (DBG_DRAW_ROUNDS >= 0) \
64 SAL_DEBUG("<=== " << s << " " << DBG_DRAW_COUNTER); \
65 } while (false)
67 #define DBG_DRAW_OPERATION_EXIT_EARLY(s) DBG_DRAW_OPERATION_EXIT(s << " exit early " << __LINE__)
69 #else
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 */
75 #endif
77 using namespace vcl;
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 )
91 return;
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) )
104 nClosedIdx = 0;
106 else
108 break;
112 ::basegfx::B2DPoint aPoint = rPolygon.getB2DPoint( nClosedIdx );
114 if( bPixelSnap)
116 // snap device coordinates to full pixels
117 aPoint.setX( basegfx::fround( aPoint.getX() ) );
118 aPoint.setY( basegfx::fround( aPoint.getY() ) );
121 if( bLineDraw )
123 aPoint += aHalfPointOfs;
125 if( !nPointIdx )
127 // first point => just move there
128 CG_TRACE("CGPathMoveToPoint(" << xPath << ",NULL," << aPoint.getX() << "," << aPoint.getY() << ")");
129 CGPathMoveToPoint( xPath, NULL, aPoint.getX(), aPoint.getY() );
130 continue;
133 bool bPendingCurve = false;
134 if( bHasCurves )
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 );
149 if( bLineDraw )
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() );
161 if( bClosePath )
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 )
176 return;
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
194 OUString aSysPath;
195 if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) )
196 return false;
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
201 ByteVector aBuffer;
202 bool bCffOnly = false;
203 if( !GetRawFontData( pFontData, aBuffer, &bCffOnly ) )
204 return false;
206 // handle CFF-subsetting
207 if( bCffOnly )
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 );
219 fclose( pOutFile );
220 return bRC;
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);
230 if( nRC != SF_OK )
231 return false;
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 ];
261 int nNotDef = -1;
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 );
279 if( !aGlyphId )
280 if( nNotDef < 0 )
281 nNotDef = i; // first NotDef glyph found
284 if( nNotDef != 0 )
286 // add fake NotDef glyph if needed
287 if( nNotDef < 0 )
288 nNotDef = nGlyphCount++;
290 // NotDef glyph must be in pos 0 => swap glyphids
291 aShortIDs[ nNotDef ] = aShortIDs[0];
292 aTempEncs[ nNotDef ] = aTempEncs[0];
293 aShortIDs[0] = 0;
294 aTempEncs[0] = 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 );
305 if( !pGlyphMetrics )
306 return false;
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 )
330 if( !pSrcGraphics )
332 pSrcGraphics = this;
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 )
341 return;
344 #ifdef IOS
345 // If called from idle layout, mrContext is NULL, no idea what to do
346 if (!mrContext)
347 return;
348 #endif
350 // accelerate trivial operations
351 /*const*/ AquaSalGraphics* pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics);
352 const bool bSameGraphics = (this == pSrc)
353 #ifdef MACOSX
354 || (mbWindow && mpFrame && pSrc->mbWindow && (mpFrame == pSrc->mpFrame))
355 #endif
357 if( bSameGraphics
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))
364 return;
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 );
368 return;
371 ApplyXorContext();
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 );
415 else
417 SalBitmap* pBitmap = pSrc->getBitmap( rPosAry.mnSrcX, rPosAry.mnSrcY,
418 rPosAry.mnSrcWidth, rPosAry.mnSrcHeight );
420 if( pBitmap )
422 SalTwoRect aPosAry( rPosAry );
423 aPosAry.mnSrcX = 0;
424 aPosAry.mnSrcY = 0;
425 drawBitmap( aPosAry, *pBitmap );
426 delete 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;
443 long nX2 = nX1;
444 long nY1 = pPtAry->mnY;
445 long nY2 = nY1;
446 for( sal_uInt32 n = 1; n < nPoints; n++ )
448 if( pPtAry[n].mnX < nX1 )
450 nX1 = pPtAry[n].mnX;
452 else if( pPtAry[n].mnX > nX2 )
454 nX2 = pPtAry[n].mnX;
456 if( pPtAry[n].mnY < nY1 )
458 nY1 = pPtAry[n].mnY;
460 else if( pPtAry[n].mnY > nY2 )
462 nY2 = pPtAry[n].mnY;
465 rX = nX1;
466 rY = nY1;
467 rWidth = nX2 - nX1 + 1;
468 rHeight = nY2 - nY1 + 1;
471 static SalColor ImplGetROPSalColor( SalROPColor nROPColor )
473 SalColor nSalColor;
474 if ( nROPColor == SAL_ROP_0 )
476 nSalColor = MAKE_SALCOLOR( 0, 0, 0 );
478 else
480 nSalColor = MAKE_SALCOLOR( 255, 255, 255 );
482 return nSalColor;
485 // apply the XOR mask to the target context if active and dirty
486 void AquaSalGraphics::ApplyXorContext()
488 if( !mpXorEmulation )
490 return;
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 );
503 #ifdef IOS
504 if( !mxLayer )
505 return;
506 #endif
508 ApplyXorContext();
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);
530 if( IsFlipped() )
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 );
547 // cleanup
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 );
557 #ifndef IOS
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;
569 #endif
571 bool AquaSalGraphics::blendBitmap( const SalTwoRect&,
572 const SalBitmap& )
574 return false;
577 bool AquaSalGraphics::blendAlphaBitmap( const SalTwoRect&,
578 const SalBitmap&,
579 const SalBitmap&,
580 const SalBitmap& )
582 return false;
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
594 return false;
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");
601 return false;
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");
609 return false;
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,
616 rTR.mnSrcHeight );
617 if( !xMaskedImage )
619 DBG_DRAW_OPERATION_EXIT_EARLY("drawAlphaBitmap");
620 return false;
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");
635 return true;
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");
647 return true;
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);
655 if( !pMaskSalBmp)
656 xImage = rSrcSalBmp.CreateCroppedImage( 0, 0, (int)aSize.Width(), (int)aSize.Height() );
657 else
658 xImage = rSrcSalBmp.CreateWithMask( *pMaskSalBmp, 0, 0, (int)aSize.Width(), (int)aSize.Height() );
659 if( !xImage )
661 DBG_DRAW_OPERATION_EXIT_EARLY("drawTransformedBitmap");
662 return false;
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");
693 return true;
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");
704 return true;
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);
714 if( IsPenVisible() )
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 );
727 // restore state
728 CG_TRACE("CGContextRestoreGState(" << mrContext << ") " << mnContextStackDepth--);
729 CGContextRestoreGState(mrContext);
730 RefreshRect( aRect );
732 DBG_DRAW_OPERATION_EXIT("drawAlphaRect");
733 return true;
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");
743 return;
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 );
749 if( !xImage )
751 DBG_DRAW_OPERATION_EXIT_EARLY("drawBitmap");
752 return;
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");
779 return;
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 ) );
786 if( !xMaskedImage )
788 DBG_DRAW_OPERATION_EXIT_EARLY("drawBitmap");
789 return;
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");
802 #ifndef IOS
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];
810 if( !xEpsImage )
812 return false;
814 // get the target context
815 if( !CheckContext() )
817 return false;
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];
829 [pOrigNSCtx retain];
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];
836 // draw the EPS
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 );
849 return bOK;
852 #endif
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");
864 return;
867 if( !CheckContext() )
869 DBG_DRAW_OPERATION_EXIT_EARLY("drawLine");
870 return;
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 );
883 (void) aRefreshRect;
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");
896 return;
899 const QuartzSalBitmap& rBitmap = static_cast<const QuartzSalBitmap&>(rSalBitmap);
900 CGImageRef xImage = rBitmap.CreateColorMask( rPosAry.mnSrcX, rPosAry.mnSrcY,
901 rPosAry.mnSrcWidth, rPosAry.mnSrcHeight,
902 nMaskColor );
903 if( !xImage )
905 DBG_DRAW_OPERATION_EXIT_EARLY("drawMask");
906 return;
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");
945 return true;
948 // reject requests that cannot be handled yet
949 if( rLineWidths.getX() != rLineWidths.getY() )
951 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
952 return false;
955 #ifdef IOS
956 if( !CheckContext() )
958 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
959 return false;
961 #endif
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");
970 return false;
973 // setup line attributes
974 CGLineJoin aCGLineJoin = kCGLineJoinMiter;
975 switch( eLineJoin )
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);
987 switch(eLineCap)
989 default: // com::sun::star::drawing::LineCap_BUTT:
991 aCGLineCap = kCGLineCapButt;
992 break;
994 case com::sun::star::drawing::LineCap_ROUND:
996 aCGLineCap = kCGLineCapRound;
997 break;
999 case com::sun::star::drawing::LineCap_SQUARE:
1001 aCGLineCap = kCGLineCapSquare;
1002 break;
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");
1043 return true;
1046 bool AquaSalGraphics::drawPolyLineBezier( sal_uInt32, const SalPoint*, const sal_uInt8* )
1048 return false;
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");
1061 return true;
1064 // ignore invisible polygons
1065 if( (fTransparency >= 1.0) || (fTransparency < 0) )
1067 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyPolygon");
1068 return true;
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;
1099 else
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");
1105 return true;
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");
1133 return true;
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");
1143 return;
1146 if( !CheckContext() )
1148 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyPolygon");
1149 return;
1152 // find bound rect
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 );
1159 if( nX < leftX )
1161 maxWidth += leftX - nX;
1162 leftX = nX;
1164 if( nY < topY )
1166 maxHeight += topY - nY;
1167 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;
1193 else
1195 SAL_WARN( "vcl.quartz", "Neither pen nor brush visible" );
1196 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyPolygon");
1197 return;
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];
1208 if( nPoints > 1 )
1210 const SalPoint *pPtAry = ppPtAry[nPoly];
1211 float fX, fY;
1212 alignLinePoint( pPtAry, fX, fY );
1213 CG_TRACE( "CGContextMoveToPoint(" << mrContext << "," << fX << "," << fY << ")" );
1214 CGContextMoveToPoint( mrContext, fX, fY );
1215 pPtAry++;
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);
1227 else
1229 for( sal_uInt32 nPoly = 0; nPoly < nPolyCount; nPoly++ )
1231 const sal_uInt32 nPoints = pPoints[nPoly];
1232 if( nPoints > 1 )
1234 const SalPoint *pPtAry = ppPtAry[nPoly];
1235 CG_TRACE( "CGContextMoveToPoint(" << mrContext << "," << pPtAry->mnX << "," << pPtAry->mnY << ")" );
1236 CGContextMoveToPoint( mrContext, pPtAry->mnX, pPtAry->mnY );
1237 pPtAry++;
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" :
1254 "???"))))
1255 << ")" );
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",);
1267 if( nPoints <= 1 )
1269 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolygon");
1270 return;
1273 if( !CheckContext() )
1275 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolygon");
1276 return;
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;
1295 else
1297 SAL_WARN( "vcl.quartz", "Neither pen nor brush visible" );
1298 return;
1301 CG_TRACE( "CGContextBeginPath(" << mrContext << ")" );
1302 CGContextBeginPath( mrContext );
1304 if( IsPenVisible() )
1306 float fX, fY;
1307 alignLinePoint( pPtAry, fX, fY );
1308 CG_TRACE( "CGContextMoveToPoint(" << mrContext << "," << fX << "," << fY << ")" );
1309 CGContextMoveToPoint( mrContext, fX, fY );
1310 pPtAry++;
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 );
1318 else
1320 CG_TRACE( "CGContextMoveToPoint(" << mrContext << "," << pPtAry->mnX << "," << pPtAry->mnY << ")" );
1321 CGContextMoveToPoint( mrContext, pPtAry->mnX, pPtAry->mnY );
1322 pPtAry++;
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* )
1341 return false;
1344 bool AquaSalGraphics::drawPolyPolygonBezier( sal_uInt32, const sal_uInt32*,
1345 const SalPoint* const*, const sal_uInt8* const* )
1347 return false;
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");
1357 return;
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",);
1388 if( nPoints < 1 )
1390 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
1391 return;
1394 if( !CheckContext() )
1396 DBG_DRAW_OPERATION_EXIT_EARLY("drawPolyLine");
1397 return;
1400 long nX = 0, nY = 0, nWidth = 0, nHeight = 0;
1401 getBoundRect( nPoints, pPtAry, nX, nY, nWidth, nHeight );
1403 float fX, fY;
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 );
1409 pPtAry++;
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;
1427 return nBits;
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 );
1434 ApplyXorContext();
1436 QuartzSalBitmap* pBitmap = new QuartzSalBitmap;
1437 if( !pBitmap->Create( mxLayer, mnBitmapDepth, nX, nY, nDX, nDY) )
1439 delete pBitmap;
1440 pBitmap = NULL;
1442 return pBitmap;
1445 SystemGraphicsData AquaSalGraphics::GetGraphicsData() const
1447 SystemGraphicsData aRes;
1448 aRes.nSize = sizeof(aRes);
1449 aRes.rCGContext = mrContext;
1450 return aRes;
1453 bool AquaSalGraphics::SupportsCairo() const
1455 #if ENABLE_CAIRO_CANVAS
1456 return true;
1457 #else
1458 return false;
1459 #endif
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));
1473 #else
1474 (void)rSurface;
1475 return cairo::SurfaceSharedPtr();
1476 #endif
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);
1498 if (pSysData)
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));
1508 #else
1509 (void)rRefDevice;
1510 (void)x;
1511 (void)y;
1512 (void)width;
1513 (void)height;
1514 #endif
1515 return surf;
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));
1543 #else
1544 (void)rData;
1545 (void)rSize;
1546 #endif
1547 return cairo::SurfaceSharedPtr();
1550 css::uno::Any AquaSalGraphics::GetNativeSurfaceHandle(cairo::SurfaceSharedPtr& rSurface, const ::basegfx::B2ISize& /*rSize*/) const
1552 sal_IntPtr handle;
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());
1557 #else
1558 handle = 0;
1559 (void)rSurface;
1560 #endif
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
1568 long w = 0;
1569 if( mrContext && (
1570 #ifndef IOS
1571 mbWindow ||
1572 #endif
1573 mbVirDev) )
1575 w = mnWidth;
1578 #ifndef IOS
1579 if( w == 0 )
1581 if( mbWindow && mpFrame )
1583 w = mpFrame->maGeometry.nWidth;
1586 #endif
1587 return w;
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))
1596 return COL_BLACK;
1598 // prepare creation of matching a CGBitmapContext
1599 #if defined OSL_BIGENDIAN
1600 struct{ unsigned char b, g, r, a; } aPixel;
1601 #else
1602 struct{ unsigned char a, r, g, b; } aPixel;
1603 #endif
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
1614 ApplyXorContext();
1616 // copy the requested pixel into the bitmap context
1617 if( IsFlipped() )
1619 nY = mnHeight - nY;
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 );
1628 return nSalColor;
1631 #ifndef IOS
1633 void AquaSalGraphics::GetResolution( sal_Int32& rDPIX, sal_Int32& rDPIY )
1635 if( !mnRealDPIY )
1637 initResolution( (mbWindow && mpFrame) ? mpFrame->getNSWindow() : nil );
1640 rDPIX = mnRealDPIX;
1641 rDPIY = mnRealDPIY;
1644 #endif
1646 void AquaSalGraphics::ImplDrawPixel( long nX, long nY, const RGBAColor& rColor )
1648 if( !CheckContext() )
1650 return;
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() );
1665 #ifndef IOS
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;
1678 /* #i91301#
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.
1690 if( pWin )
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;
1701 if( pScreen )
1703 NSDictionary* pDev = [pScreen deviceDescription];
1704 if( pDev )
1706 NSNumber* pVal = [pDev objectForKey: @"NSScreenNumber"];
1707 if( pVal )
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);
1716 else
1718 OSL_FAIL( "no resolution found in device description" );
1721 else
1723 OSL_FAIL( "no device description" );
1726 else
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;
1757 else
1759 mnRealDPIX = pSalData->mnDPIX;
1760 mnRealDPIY = pSalData->mnDPIY;
1764 #endif
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 );
1790 Pattern50Fill();
1792 else // just invert
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 );
1806 namespace {
1808 CGPoint* makeCGptArray(sal_uInt32 nPoints, const SalPoint* pPtAry)
1810 CGPoint *CGpoints = new CGPoint[nPoints];
1811 if ( CGpoints )
1813 for(sal_uLong i=0;i<nPoints;i++)
1815 CGpoints[i].x = pPtAry[i].mnX;
1816 CGpoints[i].y = pPtAry[i].mnY;
1819 return CGpoints;
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);
1846 Pattern50Fill();
1848 else // just invert
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);
1859 delete [] CGpoints;
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
1885 if( mxClipPath )
1887 CG_TRACE( "CGPathRelease(" << mxClipPath << ")" );
1888 CGPathRelease( mxClipPath );
1889 mxClipPath = NULL;
1891 if( CheckContext() )
1893 SetState();
1897 void AquaSalGraphics::SetState()
1899 CG_TRACE( "CGContextRestoreGState(" << mrContext << ") " << mnContextStackDepth--);
1900 CGContextRestoreGState( mrContext );
1901 CG_TRACE( "CGContextSaveGState(" << mrContext << ") " << ++mnContextStackDepth );
1902 CGContextSaveGState( mrContext );
1904 // setup clipping
1905 if( mxClipPath )
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
1971 bool bRet = false;
1972 switch( eType )
1974 case OutDevSupport_TransparentRect:
1975 case OutDevSupport_B2DClip:
1976 case OutDevSupport_B2DDraw:
1977 bRet = true;
1978 break;
1979 default: break;
1981 return bRet;
1984 bool AquaSalGraphics::setClipRegion( const vcl::Region& i_rClip )
1986 // release old clip path
1987 if( mxClipPath )
1989 CG_TRACE( "CGPathRelease(" << mxClipPath << ")" );
1990 CGPathRelease( mxClipPath );
1991 mxClipPath = NULL;
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 );
2003 else
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
2012 if(nW)
2014 const long nH(aRectIter->Bottom() - aRectIter->Top() + 1); // uses +1 logic in original
2016 if(nH)
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() )
2028 SetState();
2030 return true;
2033 void AquaSalGraphics::SetROPFillColor( SalROPColor nROPColor )
2035 if( ! mbPrinter )
2036 SetFillColor( ImplGetROPSalColor( nROPColor ) );
2039 void AquaSalGraphics::SetROPLineColor( SalROPColor nROPColor )
2041 if( ! mbPrinter )
2042 SetLineColor( ImplGetROPSalColor( nROPColor ) );
2045 void AquaSalGraphics::SetXORMode( bool bSet, bool bInvertOnly )
2047 // return early if XOR mode remains unchanged
2048 if( mbPrinter )
2050 return;
2052 if( ! bSet && mnXorMode == 2 )
2054 CGContextSetBlendMode( mrContext, kCGBlendModeNormal );
2055 mnXorMode = 0;
2056 return;
2058 else if( bSet && bInvertOnly && mnXorMode == 0)
2060 CGContextSetBlendMode( mrContext, kCGBlendModeDifference );
2061 mnXorMode = 2;
2062 return;
2065 if( (mpXorEmulation == NULL) && !bSet )
2067 return;
2069 if( (mpXorEmulation != NULL) && (bSet == mpXorEmulation->IsEnabled()) )
2071 return;
2073 if( !CheckContext() )
2075 return;
2077 // prepare XOR emulation
2078 if( !mpXorEmulation )
2080 mpXorEmulation = new XorEmulation();
2081 mpXorEmulation->SetTarget( mnWidth, mnHeight, mnBitmapDepth, mrContext, mxLayer );
2084 // change the XOR mode
2085 if( bSet )
2087 mpXorEmulation->Enable();
2088 mrContext = mpXorEmulation->GetMaskContext();
2089 mnXorMode = 1;
2091 else
2093 mpXorEmulation->UpdateTarget();
2094 mpXorEmulation->Disable();
2095 mrContext = mpXorEmulation->GetTargetContext();
2096 mnXorMode = 0;
2100 #ifndef IOS
2102 void AquaSalGraphics::updateResolution()
2104 DBG_ASSERT( mbWindow, "updateResolution on inappropriate graphics" );
2106 initResolution( (mbWindow && mpFrame) ? mpFrame->getNSWindow() : nil );
2109 #endif
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 );
2127 Disable();
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 )
2160 return;
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;
2170 if( !nBitDepth )
2172 nBitDepth = 32;
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;
2180 nBytesPerRow = 1;
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,
2188 nWidth, nHeight,
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
2199 if( nTargetDepth )
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,
2206 nWidth, nHeight,
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 );
2239 if( !IsEnabled() )
2241 return false;
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
2279 return true;
2282 void AquaSalGraphics::SetVirDevGraphics( CGLayerRef xLayer, CGContextRef xContext,
2283 int nBitmapDepth )
2285 SAL_INFO( "vcl.quartz", "SetVirDevGraphics() this=" << this << " layer=" << xLayer << " context=" << xContext );
2287 #ifndef IOS
2288 mbWindow = false;
2289 #endif
2290 mbPrinter = false;
2291 mbVirDev = true;
2293 #ifdef IOS
2294 (void) nBitmapDepth;
2296 if( !xContext )
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 );
2304 #endif
2306 // set graphics properties
2307 mxLayer = xLayer;
2308 mrContext = xContext;
2310 #ifndef IOS
2311 mnBitmapDepth = nBitmapDepth;
2312 #endif
2314 #ifdef IOS
2315 mbForeignContext = xContext != NULL;
2316 #endif
2318 // return early if the virdev is being destroyed
2319 if( !xContext )
2320 return;
2322 // get new graphics properties
2323 if( !mxLayer )
2325 mnWidth = CGBitmapContextGetWidth( mrContext );
2326 mnHeight = CGBitmapContextGetHeight( mrContext );
2327 CG_TRACE( "CGBitmapContextGetWidth&Height(" << mrContext << ") = " << mnWidth << "x" << mnHeight );
2329 else
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 );
2353 SetState();
2356 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */