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 // TODO: Scale will be set to 2.0f as default after implementation of full scaled display support . This will allow moving of
39 // windows between non retina and retina displays without blurry text and graphics. Static variables have to be removed thereafter.
41 // Currently scaled display support is not implemented for bitmaps. This will cause a slight performance degradation on displays
42 // with single precision. To preserve performance for now, window scaling is only activated if at least one display with double
43 // precision is present. Moving windows between displays is then possible without blurry text and graphics too. Adapting window
44 // scaling when displays are added while application is running is not supported.
46 static bool bWindowScaling
= false;
47 static float fWindowScale
= 1.0f
;
51 float getWindowScaling()
55 NSArray
*aScreens
= [NSScreen screens
];
56 if (aScreens
!= nullptr)
58 int nScreens
= [aScreens count
];
59 for (int i
= 0; i
< nScreens
; i
++)
61 float fScale
= [[aScreens objectAtIndex
:i
] backingScaleFactor
];
62 if (fScale
> fWindowScale
)
63 fWindowScale
= fScale
;
65 bWindowScaling
= true;
67 if( const char* env
= getenv("SAL_FORCE_HIDPI_SCALING"))
69 fWindowScale
= atof(env
);
70 bWindowScaling
= true;
77 void AquaSalGraphics::SetWindowGraphics( AquaSalFrame
* pFrame
)
79 maShared
.mpFrame
= pFrame
;
80 maShared
.mbWindow
= true;
81 maShared
.mbPrinter
= false;
82 maShared
.mbVirDev
= false;
83 mpBackend
->UpdateGeometryProvider(pFrame
);
86 void AquaSalGraphics::SetPrinterGraphics( CGContextRef xContext
, sal_Int32 nDPIX
, sal_Int32 nDPIY
)
88 maShared
.mbWindow
= false;
89 maShared
.mbPrinter
= true;
90 maShared
.mbVirDev
= false;
92 maShared
.maContextHolder
.set(xContext
);
96 // a previously set clip path is now invalid
97 maShared
.unsetClipPath();
99 if (maShared
.maContextHolder
.isSet())
101 CGContextSetFillColorSpace( maShared
.maContextHolder
.get(), GetSalData()->mxRGBSpace
);
102 CGContextSetStrokeColorSpace( maShared
.maContextHolder
.get(), GetSalData()->mxRGBSpace
);
103 CGContextSaveGState( maShared
.maContextHolder
.get() );
107 mpBackend
->UpdateGeometryProvider(nullptr);
110 void AquaSalGraphics::InvalidateContext()
114 CGContextRelease(maShared
.maContextHolder
.get());
115 CGContextRelease(maShared
.maBGContextHolder
.get());
116 CGContextRelease(maShared
.maCSContextHolder
.get());
118 maShared
.maContextHolder
.set(nullptr);
119 maShared
.maCSContextHolder
.set(nullptr);
120 maShared
.maBGContextHolder
.set(nullptr);
123 void AquaSalGraphics::UnsetState()
125 if (maShared
.maBGContextHolder
.isSet())
127 CGContextRelease(maShared
.maBGContextHolder
.get());
128 maShared
.maBGContextHolder
.set(nullptr);
130 if (maShared
.maCSContextHolder
.isSet())
132 CGContextRelease(maShared
.maCSContextHolder
.get());
133 maShared
.maBGContextHolder
.set(nullptr);
135 if (maShared
.maContextHolder
.isSet())
137 maShared
.maContextHolder
.restoreState();
138 maShared
.maContextHolder
.set(nullptr);
140 maShared
.unsetState();
144 * (re-)create the off-screen maLayer we render everything to if
145 * necessary: eg. not initialized yet, or it has an incorrect size.
147 bool AquaSharedAttributes::checkContext()
149 if (mbWindow
&& mpFrame
&& (mpFrame
->getNSWindow() || Application::IsBitmapRendering()))
151 const unsigned int nWidth
= mpFrame
->maGeometry
.width();
152 const unsigned int nHeight
= mpFrame
->maGeometry
.height();
153 const float fScale
= sal::aqua::getWindowScaling();
154 CGLayerRef rReleaseLayer
= nullptr;
156 // check if a new drawing context is needed (e.g. after a resize)
157 if( (unsigned(mnWidth
) != nWidth
) || (unsigned(mnHeight
) != nHeight
) )
161 // prepare to release the corresponding resources
164 rReleaseLayer
= maLayer
.get();
166 else if (maContextHolder
.isSet())
168 CGContextRelease(maContextHolder
.get());
170 CGContextRelease(maBGContextHolder
.get());
171 CGContextRelease(maCSContextHolder
.get());
173 maContextHolder
.set(nullptr);
174 maBGContextHolder
.set(nullptr);
175 maCSContextHolder
.set(nullptr);
176 maLayer
.set(nullptr);
179 if (!maContextHolder
.isSet())
181 const int nBitmapDepth
= 32;
183 float nScaledWidth
= mnWidth
* fScale
;
184 float nScaledHeight
= mnHeight
* fScale
;
186 const CGSize aLayerSize
= { static_cast<CGFloat
>(nScaledWidth
), static_cast<CGFloat
>(nScaledHeight
) };
188 const int nBytesPerRow
= (nBitmapDepth
* nScaledWidth
) / 8;
189 std::uint32_t nFlags
= std::uint32_t(kCGImageAlphaNoneSkipFirst
)
190 | std::uint32_t(kCGBitmapByteOrder32Host
);
191 maBGContextHolder
.set(CGBitmapContextCreate(
192 nullptr, nScaledWidth
, nScaledHeight
, 8, nBytesPerRow
, GetSalData()->mxRGBSpace
, nFlags
));
194 maLayer
.set(CGLayerCreateWithContext(maBGContextHolder
.get(), aLayerSize
, nullptr));
195 maLayer
.setScale(fScale
);
197 nFlags
= std::uint32_t(kCGImageAlphaPremultipliedFirst
)
198 | std::uint32_t(kCGBitmapByteOrder32Host
);
199 maCSContextHolder
.set(CGBitmapContextCreate(
200 nullptr, nScaledWidth
, nScaledHeight
, 8, nBytesPerRow
, GetSalData()->mxRGBSpace
, nFlags
));
202 CGContextRef xDrawContext
= CGLayerGetContext(maLayer
.get());
203 maContextHolder
= xDrawContext
;
207 // copy original layer to resized layer
208 if (maContextHolder
.isSet())
210 CGContextDrawLayerAtPoint(maContextHolder
.get(), CGPointZero
, rReleaseLayer
);
212 CGLayerRelease(rReleaseLayer
);
215 if (maContextHolder
.isSet())
217 CGContextTranslateCTM(maContextHolder
.get(), 0, nScaledHeight
);
218 CGContextScaleCTM(maContextHolder
.get(), 1.0, -1.0);
219 CGContextSetFillColorSpace(maContextHolder
.get(), GetSalData()->mxRGBSpace
);
220 CGContextSetStrokeColorSpace(maContextHolder
.get(), GetSalData()->mxRGBSpace
);
221 // apply a scale matrix so everything is auto-magically scaled
222 CGContextScaleCTM(maContextHolder
.get(), fScale
, fScale
);
223 maContextHolder
.saveState();
226 // re-enable XOR emulation for the new context
228 mpXorEmulation
->SetTarget(mnWidth
, mnHeight
, mnBitmapDepth
, maContextHolder
.get(), maLayer
.get());
233 SAL_WARN_IF(!maContextHolder
.isSet() && !mbPrinter
, "vcl", "<<<WARNING>>> AquaSalGraphics::CheckContext() FAILED!!!!");
235 return maContextHolder
.isSet();
239 * Blit the contents of our internal maLayer state to the
240 * associated window, if any; cf. drawRect event handling
243 void AquaSalGraphics::UpdateWindow( NSRect
& )
245 if (!maShared
.mpFrame
)
250 NSGraphicsContext
* pContext
= [NSGraphicsContext currentContext
];
251 if (maShared
.maLayer
.isSet() && pContext
!= nullptr)
253 CGContextHolder
rCGContextHolder([pContext CGContext
]);
255 rCGContextHolder
.saveState();
257 CGMutablePathRef rClip
= maShared
.mpFrame
->getClipPath();
260 CGContextBeginPath(rCGContextHolder
.get());
261 CGContextAddPath(rCGContextHolder
.get(), rClip
);
262 CGContextClip(rCGContextHolder
.get());
265 maShared
.applyXorContext();
267 const CGSize aSize
= maShared
.maLayer
.getSizePoints();
268 const CGRect aRect
= CGRectMake(0, 0, aSize
.width
, aSize
.height
);
269 const CGRect aRectPoints
= { CGPointZero
, maShared
.maLayer
.getSizePixels() };
270 CGContextSetBlendMode(maShared
.maCSContextHolder
.get(), kCGBlendModeCopy
);
271 CGContextDrawLayerInRect(maShared
.maCSContextHolder
.get(), aRectPoints
, maShared
.maLayer
.get());
273 CGImageRef img
= CGBitmapContextCreateImage(maShared
.maCSContextHolder
.get());
274 CGImageRef displayColorSpaceImage
= CGImageCreateCopyWithColorSpace(img
, [[maShared
.mpFrame
->getNSWindow() colorSpace
] CGColorSpace
]);
275 CGContextSetBlendMode(rCGContextHolder
.get(), kCGBlendModeCopy
);
276 CGContextDrawImage(rCGContextHolder
.get(), aRect
, displayColorSpaceImage
);
279 CGImageRelease(displayColorSpaceImage
);
281 rCGContextHolder
.restoreState();
285 SAL_WARN_IF(!maShared
.mpFrame
->mbInitShow
, "vcl", "UpdateWindow called on uneligible graphics");
289 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */