1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22
23 #include <basegfx/polygon/b2dpolygon.hxx>
24 #include <basegfx/polygon/b2dpolygontools.hxx>
25 #include <basegfx/range/b2drectangle.hxx>
26 #include <basegfx/range/b2irange.hxx>
27 #include <basegfx/vector/b2ivector.hxx>
28 #include <vcl/svapp.hxx>
29
30 #include <quartz/salgdi.h>
31 #include <quartz/utils.h>
32 #include <osx/salframe.h>
33 #include <osx/saldata.hxx>
34
SetWindowGraphics(AquaSalFrame * pFrame)35 void AquaSalGraphics::SetWindowGraphics( AquaSalFrame* pFrame )
36 {
37 mpFrame = pFrame;
38 mbWindow = true;
39 mbPrinter = false;
40 mbVirDev = false;
41 }
42
SetPrinterGraphics(CGContextRef xContext,long nDPIX,long nDPIY)43 void AquaSalGraphics::SetPrinterGraphics( CGContextRef xContext, long nDPIX, long nDPIY )
44 {
45 mbWindow = false;
46 mbPrinter = true;
47 mbVirDev = false;
48
49 maContextHolder.set(xContext);
50 mnRealDPIX = nDPIX;
51 mnRealDPIY = nDPIY;
52
53 // a previously set clip path is now invalid
54 if( mxClipPath )
55 {
56 CGPathRelease( mxClipPath );
57 mxClipPath = nullptr;
58 }
59
60 if (maContextHolder.isSet())
61 {
62 CGContextSetFillColorSpace( maContextHolder.get(), GetSalData()->mxRGBSpace );
63 CGContextSetStrokeColorSpace( maContextHolder.get(), GetSalData()->mxRGBSpace );
64 CGContextSaveGState( maContextHolder.get() );
65 SetState();
66 }
67 }
68
InvalidateContext()69 void AquaSalGraphics::InvalidateContext()
70 {
71 UnsetState();
72 maContextHolder.set(nullptr);
73 }
74
UnsetState()75 void AquaSalGraphics::UnsetState()
76 {
77 if (maContextHolder.isSet())
78 {
79 maContextHolder.restoreState();
80 maContextHolder.set(nullptr);
81 }
82 if( mxClipPath )
83 {
84 CGPathRelease( mxClipPath );
85 mxClipPath = nullptr;
86 }
87 }
88
89 /**
90 * (re-)create the off-screen maLayer we render everything to if
91 * necessary: eg. not initialized yet, or it has an incorrect size.
92 */
CheckContext()93 bool AquaSalGraphics::CheckContext()
94 {
95 if (mbWindow && mpFrame && (mpFrame->getNSWindow() || Application::IsBitmapRendering()))
96 {
97 const unsigned int nWidth = mpFrame->maGeometry.nWidth;
98 const unsigned int nHeight = mpFrame->maGeometry.nHeight;
99
100 // Let's get the window scaling factor if possible, or use 1.0
101 // as the scaling factor.
102 float fScale = 1.0f;
103 if (mpFrame->getNSWindow())
104 fScale = [mpFrame->getNSWindow() backingScaleFactor];
105
106 CGLayerRef rReleaseLayer = nullptr;
107
108 // check if a new drawing context is needed (e.g. after a resize)
109 if( (unsigned(mnWidth) != nWidth) || (unsigned(mnHeight) != nHeight) )
110 {
111 mnWidth = nWidth;
112 mnHeight = nHeight;
113 // prepare to release the corresponding resources
114 if (maLayer.isSet())
115 {
116 rReleaseLayer = maLayer.get();
117 }
118 else if (maContextHolder.isSet())
119 {
120 CGContextRelease(maContextHolder.get());
121 }
122 maContextHolder.set(nullptr);
123 maLayer.set(nullptr);
124 }
125
126 if (!maContextHolder.isSet())
127 {
128 const int nBitmapDepth = 32;
129
130 float nScaledWidth = mnWidth * fScale;
131 float nScaledHeight = mnHeight * fScale;
132
133 const CGSize aLayerSize = { static_cast<CGFloat>(nScaledWidth), static_cast<CGFloat>(nScaledHeight) };
134
135 const int nBytesPerRow = (nBitmapDepth * nScaledWidth) / 8;
136 void* pRawData = std::malloc(nBytesPerRow * nScaledHeight);
137 #ifdef MACOSX
138 const int nFlags = kCGImageAlphaNoneSkipFirst;
139 #else
140 const int nFlags = kCGImageAlphaNoneSkipFirst | kCGImageByteOrder32Little;
141 #endif
142 CGContextHolder aContextHolder(CGBitmapContextCreate(
143 pRawData, nScaledWidth, nScaledHeight, 8, nBytesPerRow, GetSalData()->mxRGBSpace, nFlags));
144
145 maLayer.set(CGLayerCreateWithContext(aContextHolder.get(), aLayerSize, nullptr));
146 maLayer.setScale(fScale);
147
148 CGContextRef xDrawContext = CGLayerGetContext(maLayer.get());
149 maContextHolder = xDrawContext;
150
151 if (rReleaseLayer)
152 {
153 // copy original layer to resized layer
154 if (maContextHolder.isSet())
155 {
156 CGContextDrawLayerAtPoint(maContextHolder.get(), CGPointZero, rReleaseLayer);
157 }
158 CGLayerRelease(rReleaseLayer);
159 }
160
161 if (maContextHolder.isSet())
162 {
163 CGContextTranslateCTM(maContextHolder.get(), 0, nScaledHeight);
164 CGContextScaleCTM(maContextHolder.get(), 1.0, -1.0);
165 CGContextSetFillColorSpace(maContextHolder.get(), GetSalData()->mxRGBSpace);
166 CGContextSetStrokeColorSpace(maContextHolder.get(), GetSalData()->mxRGBSpace);
167 // apply a scale matrix so everything is auto-magically scaled
168 CGContextScaleCTM(maContextHolder.get(), fScale, fScale);
169 maContextHolder.saveState();
170 SetState();
171
172 // re-enable XOR emulation for the new context
173 if (mpXorEmulation)
174 mpXorEmulation->SetTarget(mnWidth, mnHeight, mnBitmapDepth, maContextHolder.get(), maLayer.get());
175 }
176 }
177 }
178
179 SAL_WARN_IF(!maContextHolder.isSet() && !mbPrinter, "vcl", "<<<WARNING>>> AquaSalGraphics::CheckContext() FAILED!!!!");
180
181 return maContextHolder.isSet();
182 }
183
GetContext()184 CGContextRef AquaSalGraphics::GetContext()
185 {
186 if (!maContextHolder.isSet())
187 {
188 CheckContext();
189 }
190 return maContextHolder.get();
191 }
192
193 /**
194 * Blit the contents of our internal maLayer state to the
195 * associated window, if any; cf. drawRect event handling
196 * on the frame.
197 */
UpdateWindow(NSRect &)198 void AquaSalGraphics::UpdateWindow( NSRect& )
199 {
200 if( !mpFrame )
201 {
202 return;
203 }
204
205 NSGraphicsContext* pContext = [NSGraphicsContext currentContext];
206 if (maLayer.isSet() && pContext != nullptr)
207 {
208 CGContextHolder rCGContextHolder([pContext CGContext]);
209
210 rCGContextHolder.saveState();
211
212 CGMutablePathRef rClip = mpFrame->getClipPath();
213 if (rClip)
214 {
215 CGContextBeginPath(rCGContextHolder.get());
216 CGContextAddPath(rCGContextHolder.get(), rClip );
217 CGContextClip(rCGContextHolder.get());
218 }
219
220 ApplyXorContext();
221
222 const CGSize aSize = maLayer.getSizePoints();
223 const CGRect aRect = CGRectMake(0, 0, aSize.width, aSize.height);
224
225 CGContextDrawLayerInRect(rCGContextHolder.get(), aRect, maLayer.get());
226
227 rCGContextHolder.restoreState();
228 }
229 else
230 {
231 SAL_WARN_IF( !mpFrame->mbInitShow, "vcl", "UpdateWindow called on uneligible graphics" );
232 }
233 }
234
235 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
236