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