bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / osx / salgdiutils.cxx
blob7e4bd23baac9011493ed717808a22ab12254605f
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 <cstdint>
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 #if HAVE_FEATURE_SKIA
39 #include <tools/sk_app/mac/WindowContextFactory_mac.h>
40 #include <vcl/skia/SkiaHelper.hxx>
41 #endif
43 static bool bTotalScreenBounds = false;
44 static NSRect aTotalScreenBounds = NSZeroRect;
46 // TODO: Scale will be set to 2.0f as default after implementation of full scaled display support . This will allow moving of
47 // windows between non retina and retina displays without blurry text and graphics. Static variables have to be removed thereafter.
49 // Currently scaled display support is not implemented for bitmaps. This will cause a slight performance degradation on displays
50 // with single precision. To preserve performance for now, window scaling is only activated if at least one display with double
51 // precision is present. Moving windows between displays is then possible without blurry text and graphics too. Adapting window
52 // scaling when displays are added while application is running is not supported.
54 static bool bWindowScaling = false;
55 static float fWindowScale = 1.0f;
57 namespace sal::aqua
59 NSRect getTotalScreenBounds()
61 if (!bTotalScreenBounds)
63 aTotalScreenBounds = NSZeroRect;
65 NSArray *aScreens = [NSScreen screens];
66 if (aScreens != nullptr)
68 for (NSScreen *aScreen : aScreens)
70 // Calculate total screen bounds
71 NSRect aScreenFrame = [aScreen frame];
72 if (!NSIsEmptyRect(aScreenFrame))
74 if (NSIsEmptyRect(aTotalScreenBounds))
75 aTotalScreenBounds = aScreenFrame;
76 else
77 aTotalScreenBounds = NSUnionRect( aScreenFrame, aTotalScreenBounds );
80 bTotalScreenBounds = true;
83 return aTotalScreenBounds;
86 void resetTotalScreenBounds()
88 bTotalScreenBounds = false;
89 getTotalScreenBounds();
92 float getWindowScaling()
94 // Related: tdf#147342 Any changes to this function must be copied to the
95 // sk_app::GetBackingScaleFactor() function in the following file:
96 // workdir/UnpackedTarball/skia/tools/sk_app/mac/WindowContextFactory_mac.h
97 if (!bWindowScaling)
99 NSArray *aScreens = [NSScreen screens];
100 if (aScreens != nullptr)
102 for (NSScreen *aScreen : aScreens)
104 float fScale = [aScreen backingScaleFactor];
105 if (fScale > fWindowScale)
106 fWindowScale = fScale;
108 bWindowScaling = true;
110 if( const char* env = getenv("SAL_FORCE_HIDPI_SCALING"))
112 fWindowScale = atof(env);
113 bWindowScaling = true;
116 return fWindowScale;
119 void resetWindowScaling()
121 // Related: tdf#147342 Force recalculation of the window scaling but keep
122 // the previous window scaling as the minimum so that we don't lose the
123 // resolution in cached images if a HiDPI monitor is disconnected and
124 // then reconnected.
125 #if HAVE_FEATURE_SKIA
126 if ( SkiaHelper::isVCLSkiaEnabled() )
127 sk_app::ResetBackingScaleFactor();
128 #endif
129 bWindowScaling = false;
130 getWindowScaling();
132 } // end aqua
134 void AquaSalGraphics::SetWindowGraphics( AquaSalFrame* pFrame )
136 maShared.mpFrame = pFrame;
137 maShared.mbWindow = true;
138 maShared.mbPrinter = false;
139 maShared.mbVirDev = false;
140 mpBackend->UpdateGeometryProvider(pFrame);
143 void AquaSalGraphics::SetPrinterGraphics( CGContextRef xContext, sal_Int32 nDPIX, sal_Int32 nDPIY )
145 maShared.mbWindow = false;
146 maShared.mbPrinter = true;
147 maShared.mbVirDev = false;
149 maShared.maContextHolder.set(xContext);
150 mnRealDPIX = nDPIX;
151 mnRealDPIY = nDPIY;
153 // a previously set clip path is now invalid
154 maShared.unsetClipPath();
156 if (maShared.maContextHolder.isSet())
158 CGContextSetFillColorSpace( maShared.maContextHolder.get(), GetSalData()->mxRGBSpace );
159 CGContextSetStrokeColorSpace( maShared.maContextHolder.get(), GetSalData()->mxRGBSpace );
160 CGContextSaveGState( maShared.maContextHolder.get() );
161 maShared.setState();
164 mpBackend->UpdateGeometryProvider(nullptr);
167 void AquaSalGraphics::InvalidateContext()
169 UnsetState();
171 CGContextRelease(maShared.maContextHolder.get());
172 CGContextRelease(maShared.maBGContextHolder.get());
173 CGContextRelease(maShared.maCSContextHolder.get());
175 maShared.maContextHolder.set(nullptr);
176 maShared.maCSContextHolder.set(nullptr);
177 maShared.maBGContextHolder.set(nullptr);
180 void AquaSalGraphics::UnsetState()
182 if (maShared.maBGContextHolder.isSet())
184 CGContextRelease(maShared.maBGContextHolder.get());
185 maShared.maBGContextHolder.set(nullptr);
187 if (maShared.maCSContextHolder.isSet())
189 CGContextRelease(maShared.maCSContextHolder.get());
190 maShared.maBGContextHolder.set(nullptr);
192 if (maShared.maContextHolder.isSet())
194 maShared.maContextHolder.restoreState();
195 maShared.maContextHolder.set(nullptr);
197 maShared.unsetState();
201 * (re-)create the off-screen maLayer we render everything to if
202 * necessary: eg. not initialized yet, or it has an incorrect size.
204 bool AquaSharedAttributes::checkContext()
206 if (mbWindow && mpFrame && (mpFrame->getNSWindow() || Application::IsBitmapRendering()))
208 const unsigned int nWidth = mpFrame->maGeometry.width();
209 const unsigned int nHeight = mpFrame->maGeometry.height();
210 const float fScale = sal::aqua::getWindowScaling();
211 CGLayerRef rReleaseLayer = nullptr;
213 // check if a new drawing context is needed (e.g. after a resize)
214 if( (unsigned(mnWidth) != nWidth) || (unsigned(mnHeight) != nHeight) )
216 mnWidth = nWidth;
217 mnHeight = nHeight;
218 // prepare to release the corresponding resources
219 if (maLayer.isSet())
221 rReleaseLayer = maLayer.get();
223 else if (maContextHolder.isSet())
225 CGContextRelease(maContextHolder.get());
227 CGContextRelease(maBGContextHolder.get());
228 CGContextRelease(maCSContextHolder.get());
230 maContextHolder.set(nullptr);
231 maBGContextHolder.set(nullptr);
232 maCSContextHolder.set(nullptr);
233 maLayer.set(nullptr);
236 if (!maContextHolder.isSet())
238 const int nBitmapDepth = 32;
240 float nScaledWidth = mnWidth * fScale;
241 float nScaledHeight = mnHeight * fScale;
243 const CGSize aLayerSize = { static_cast<CGFloat>(nScaledWidth), static_cast<CGFloat>(nScaledHeight) };
245 const int nBytesPerRow = (nBitmapDepth * nScaledWidth) / 8;
246 std::uint32_t nFlags = std::uint32_t(kCGImageAlphaNoneSkipFirst)
247 | std::uint32_t(kCGBitmapByteOrder32Host);
248 maBGContextHolder.set(CGBitmapContextCreate(
249 nullptr, nScaledWidth, nScaledHeight, 8, nBytesPerRow, GetSalData()->mxRGBSpace, nFlags));
251 maLayer.set(CGLayerCreateWithContext(maBGContextHolder.get(), aLayerSize, nullptr));
252 maLayer.setScale(fScale);
254 nFlags = std::uint32_t(kCGImageAlphaPremultipliedFirst)
255 | std::uint32_t(kCGBitmapByteOrder32Host);
256 maCSContextHolder.set(CGBitmapContextCreate(
257 nullptr, nScaledWidth, nScaledHeight, 8, nBytesPerRow, GetSalData()->mxRGBSpace, nFlags));
259 CGContextRef xDrawContext = CGLayerGetContext(maLayer.get());
260 maContextHolder = xDrawContext;
262 if (rReleaseLayer)
264 // copy original layer to resized layer
265 if (maContextHolder.isSet())
267 CGContextDrawLayerAtPoint(maContextHolder.get(), CGPointZero, rReleaseLayer);
269 CGLayerRelease(rReleaseLayer);
272 if (maContextHolder.isSet())
274 CGContextTranslateCTM(maContextHolder.get(), 0, nScaledHeight);
275 CGContextScaleCTM(maContextHolder.get(), 1.0, -1.0);
276 CGContextSetFillColorSpace(maContextHolder.get(), GetSalData()->mxRGBSpace);
277 CGContextSetStrokeColorSpace(maContextHolder.get(), GetSalData()->mxRGBSpace);
278 // apply a scale matrix so everything is auto-magically scaled
279 CGContextScaleCTM(maContextHolder.get(), fScale, fScale);
280 maContextHolder.saveState();
281 setState();
283 // re-enable XOR emulation for the new context
284 if (mpXorEmulation)
285 mpXorEmulation->SetTarget(mnWidth, mnHeight, mnBitmapDepth, maContextHolder.get(), maLayer.get());
290 SAL_WARN_IF(!maContextHolder.isSet() && !mbPrinter, "vcl", "<<<WARNING>>> AquaSalGraphics::CheckContext() FAILED!!!!");
292 return maContextHolder.isSet();
296 * Blit the contents of our internal maLayer state to the
297 * associated window, if any; cf. drawRect event handling
298 * on the frame.
300 void AquaSalGraphics::UpdateWindow( NSRect& )
302 if (!maShared.mpFrame)
304 return;
307 NSGraphicsContext* pContext = [NSGraphicsContext currentContext];
308 if (maShared.maLayer.isSet() && pContext != nullptr)
310 CGContextHolder rCGContextHolder([pContext CGContext]);
312 rCGContextHolder.saveState();
314 CGMutablePathRef rClip = maShared.mpFrame->getClipPath();
315 if (rClip)
317 CGContextBeginPath(rCGContextHolder.get());
318 CGContextAddPath(rCGContextHolder.get(), rClip );
319 CGContextClip(rCGContextHolder.get());
322 maShared.applyXorContext();
324 const CGSize aSize = maShared.maLayer.getSizePoints();
325 const CGRect aRect = CGRectMake(0, 0, aSize.width, aSize.height);
326 const CGRect aRectPoints = { CGPointZero, maShared.maLayer.getSizePixels() };
327 CGContextSetBlendMode(maShared.maCSContextHolder.get(), kCGBlendModeCopy);
328 CGContextDrawLayerInRect(maShared.maCSContextHolder.get(), aRectPoints, maShared.maLayer.get());
330 CGImageRef img = CGBitmapContextCreateImage(maShared.maCSContextHolder.get());
331 CGImageRef displayColorSpaceImage = CGImageCreateCopyWithColorSpace(img, [[maShared.mpFrame->getNSWindow() colorSpace] CGColorSpace]);
332 CGContextSetBlendMode(rCGContextHolder.get(), kCGBlendModeCopy);
333 CGContextDrawImage(rCGContextHolder.get(), aRect, displayColorSpaceImage);
335 CGImageRelease(img);
336 CGImageRelease(displayColorSpaceImage);
338 rCGContextHolder.restoreState();
340 else
342 SAL_WARN_IF(!maShared.mpFrame->mbInitShow, "vcl", "UpdateWindow called on uneligible graphics");
346 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */