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