1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
24 #include <sal/log.hxx>
26 #include <basegfx/polygon/b2dpolygon.hxx>
27 #include <basegfx/polygon/b2dpolygontools.hxx>
28 #include <basegfx/range/b2drectangle.hxx>
29 #include <basegfx/range/b2irange.hxx>
30 #include <basegfx/vector/b2ivector.hxx>
31 #include <vcl/svapp.hxx>
33 #include <quartz/salgdi.h>
34 #include <quartz/utils.h>
35 #include <osx/salframe.h>
36 #include <osx/saldata.hxx>
38 float AquaSalGraphics::GetWindowScaling()
44 // Window scaling independent from main display may be forced by setting VCL_MACOS_FORCE_WINDOW_SCALING environment variable
45 // whose setting is stored in mbWindowScaling. After implementation of full support of scaled displays window scaling will be
46 // set to 2.0f for macOS as default. This will allow moving of windows between non retina and retina displays without blurry
49 // TODO: After implementation of full support of scaled displays code has to be modified to set a scaling of 2.0f as default.
59 AquaSalFrame
*pSalFrame
= mpFrame
;
61 pSalFrame
= static_cast<AquaSalFrame
*>(GetSalData()->mpInstance
->anyFrame());
64 NSWindow
*pNSWindow
= pSalFrame
->getNSWindow();
66 fScale
= [pNSWindow backingScaleFactor
];
71 void AquaSalGraphics::SetWindowGraphics( AquaSalFrame
* pFrame
)
79 void AquaSalGraphics::SetPrinterGraphics( CGContextRef xContext
, sal_Int32 nDPIX
, sal_Int32 nDPIY
)
85 maContextHolder
.set(xContext
);
89 // a previously set clip path is now invalid
92 CGPathRelease( mxClipPath
);
96 if (maContextHolder
.isSet())
98 CGContextSetFillColorSpace( maContextHolder
.get(), GetSalData()->mxRGBSpace
);
99 CGContextSetStrokeColorSpace( maContextHolder
.get(), GetSalData()->mxRGBSpace
);
100 CGContextSaveGState( maContextHolder
.get() );
105 void AquaSalGraphics::InvalidateContext()
109 CGContextRelease(maContextHolder
.get());
110 CGContextRelease(maBGContextHolder
.get());
111 CGContextRelease(maCSContextHolder
.get());
113 maContextHolder
.set(nullptr);
114 maCSContextHolder
.set(nullptr);
115 maBGContextHolder
.set(nullptr);
118 void AquaSalGraphics::UnsetState()
120 if (maBGContextHolder
.isSet())
122 CGContextRelease(maBGContextHolder
.get());
123 maBGContextHolder
.set(nullptr);
125 if (maCSContextHolder
.isSet())
127 CGContextRelease(maCSContextHolder
.get());
128 maBGContextHolder
.set(nullptr);
130 if (maContextHolder
.isSet())
132 maContextHolder
.restoreState();
133 maContextHolder
.set(nullptr);
137 CGPathRelease( mxClipPath
);
138 mxClipPath
= nullptr;
143 * (re-)create the off-screen maLayer we render everything to if
144 * necessary: eg. not initialized yet, or it has an incorrect size.
146 bool AquaSalGraphics::CheckContext()
148 if (mbWindow
&& mpFrame
&& (mpFrame
->getNSWindow() || Application::IsBitmapRendering()))
150 const unsigned int nWidth
= mpFrame
->maGeometry
.nWidth
;
151 const unsigned int nHeight
= mpFrame
->maGeometry
.nHeight
;
152 const float fScale
= GetWindowScaling();
153 CGLayerRef rReleaseLayer
= nullptr;
155 // check if a new drawing context is needed (e.g. after a resize)
156 if( (unsigned(mnWidth
) != nWidth
) || (unsigned(mnHeight
) != nHeight
) )
160 // prepare to release the corresponding resources
163 rReleaseLayer
= maLayer
.get();
165 else if (maContextHolder
.isSet())
167 CGContextRelease(maContextHolder
.get());
169 CGContextRelease(maBGContextHolder
.get());
170 CGContextRelease(maCSContextHolder
.get());
172 maContextHolder
.set(nullptr);
173 maBGContextHolder
.set(nullptr);
174 maCSContextHolder
.set(nullptr);
175 maLayer
.set(nullptr);
178 if (!maContextHolder
.isSet())
180 const int nBitmapDepth
= 32;
182 float nScaledWidth
= mnWidth
* fScale
;
183 float nScaledHeight
= mnHeight
* fScale
;
185 const CGSize aLayerSize
= { static_cast<CGFloat
>(nScaledWidth
), static_cast<CGFloat
>(nScaledHeight
) };
187 const int nBytesPerRow
= (nBitmapDepth
* nScaledWidth
) / 8;
188 std::uint32_t nFlags
= std::uint32_t(kCGImageAlphaNoneSkipFirst
)
189 | std::uint32_t(kCGBitmapByteOrder32Host
);
190 maBGContextHolder
.set(CGBitmapContextCreate(
191 nullptr, nScaledWidth
, nScaledHeight
, 8, nBytesPerRow
, GetSalData()->mxRGBSpace
, nFlags
));
193 maLayer
.set(CGLayerCreateWithContext(maBGContextHolder
.get(), aLayerSize
, nullptr));
194 maLayer
.setScale(fScale
);
196 nFlags
= std::uint32_t(kCGImageAlphaPremultipliedFirst
)
197 | std::uint32_t(kCGBitmapByteOrder32Host
);
198 maCSContextHolder
.set(CGBitmapContextCreate(
199 nullptr, nScaledWidth
, nScaledHeight
, 8, nBytesPerRow
, GetSalData()->mxRGBSpace
, nFlags
));
201 CGContextRef xDrawContext
= CGLayerGetContext(maLayer
.get());
202 maContextHolder
= xDrawContext
;
206 // copy original layer to resized layer
207 if (maContextHolder
.isSet())
209 CGContextDrawLayerAtPoint(maContextHolder
.get(), CGPointZero
, rReleaseLayer
);
211 CGLayerRelease(rReleaseLayer
);
214 if (maContextHolder
.isSet())
216 CGContextTranslateCTM(maContextHolder
.get(), 0, nScaledHeight
);
217 CGContextScaleCTM(maContextHolder
.get(), 1.0, -1.0);
218 CGContextSetFillColorSpace(maContextHolder
.get(), GetSalData()->mxRGBSpace
);
219 CGContextSetStrokeColorSpace(maContextHolder
.get(), GetSalData()->mxRGBSpace
);
220 // apply a scale matrix so everything is auto-magically scaled
221 CGContextScaleCTM(maContextHolder
.get(), fScale
, fScale
);
222 maContextHolder
.saveState();
225 // re-enable XOR emulation for the new context
227 mpXorEmulation
->SetTarget(mnWidth
, mnHeight
, mnBitmapDepth
, maContextHolder
.get(), maLayer
.get());
232 SAL_WARN_IF(!maContextHolder
.isSet() && !mbPrinter
, "vcl", "<<<WARNING>>> AquaSalGraphics::CheckContext() FAILED!!!!");
234 return maContextHolder
.isSet();
238 * Blit the contents of our internal maLayer state to the
239 * associated window, if any; cf. drawRect event handling
242 void AquaSalGraphics::UpdateWindow( NSRect
& )
249 NSGraphicsContext
* pContext
= [NSGraphicsContext currentContext
];
250 if (maLayer
.isSet() && pContext
!= nullptr)
252 CGContextHolder
rCGContextHolder([pContext CGContext
]);
254 rCGContextHolder
.saveState();
256 CGMutablePathRef rClip
= mpFrame
->getClipPath();
259 CGContextBeginPath(rCGContextHolder
.get());
260 CGContextAddPath(rCGContextHolder
.get(), rClip
);
261 CGContextClip(rCGContextHolder
.get());
266 const CGSize aSize
= maLayer
.getSizePoints();
267 const CGRect aRect
= CGRectMake(0, 0, aSize
.width
, aSize
.height
);
268 const CGRect aRectPoints
= { CGPointZero
, maLayer
.getSizePixels() };
269 CGContextSetBlendMode(maCSContextHolder
.get(), kCGBlendModeCopy
);
270 CGContextDrawLayerInRect(maCSContextHolder
.get(), aRectPoints
, maLayer
.get());
272 CGImageRef img
= CGBitmapContextCreateImage(maCSContextHolder
.get());
273 CGImageRef displayColorSpaceImage
= CGImageCreateCopyWithColorSpace(img
, [[mpFrame
->getNSWindow() colorSpace
] CGColorSpace
]);
274 CGContextSetBlendMode(rCGContextHolder
.get(), kCGBlendModeCopy
);
275 CGContextDrawImage(rCGContextHolder
.get(), aRect
, displayColorSpaceImage
);
278 CGImageRelease(displayColorSpaceImage
);
280 rCGContextHolder
.restoreState();
284 SAL_WARN_IF( !mpFrame
->mbInitShow
, "vcl", "UpdateWindow called on uneligible graphics" );
288 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */