1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_canvas.hxx"
26 
27 #ifdef QUARTZ
28 /************************************************************************
29  * Mac OS X/Quartz surface backend for OpenOffice.org Cairo Canvas      *
30  ************************************************************************/
31 
32 #include <osl/diagnose.h>
33 #include <vcl/sysdata.hxx>
34 #include <vcl/bitmap.hxx>
35 #include <vcl/virdev.hxx>
36 
37 #include "cairo_cairo.hxx"
38 
39 #if defined CAIRO_HAS_QUARTZ_SURFACE
40 
41 #include "cairo_quartz_cairo.hxx"
42 
43 namespace cairo
44 {
45     bool IsCairoWorking( OutputDevice* )
46     {
47         // trivially true for Mac
48         return true;
49     }
50 
51     /**
52      * QuartzSurface::Surface:     Create generic Canvas surface using given Cairo Surface
53      *
54      * @param pSurface Cairo Surface
55      *
56      * This constructor only stores data, it does no processing.
57      * It is used with e.g. cairo_image_surface_create_for_data()
58      * and QuartzSurface::getSimilar()
59      *
60      * Set the mpSurface to the new surface or NULL
61      **/
62     QuartzSurface::QuartzSurface( const CairoSurfaceSharedPtr& pSurface ) :
63             mpView(NULL),
64             mpSurface( pSurface )
65     {
66         // Necessary, context is lost otherwise
67         CGContextRetain( getCGContext() ); //  == NULL for non-native surfaces
68     }
69 
70     /**
71      * QuartzSurface::Surface:   Create Canvas surface from Window reference.
72      * @param NSView
73      * @param x horizontal location of the new surface
74      * @param y vertical location of the new surface
75      * @param width width of the new surface
76      * @param height height of the new surface
77      *
78      * pSysData contains the platform native Window reference.
79      * pSysData is used to create a surface on the Window
80      *
81      * Set the mpSurface to the new surface or NULL
82      **/
83     QuartzSurface::QuartzSurface( NSView* pView, int x, int y, int width, int height ) :
84             mpView(pView),
85             mpSurface()
86     {
87         OSL_TRACE("Canvas::cairo::Surface(NSView*, x:%d, y:%d, w:%d, h:%d): New Surface for window", x, y, width, height);
88 
89         // on Mac OS X / Quartz we are not drawing directly to the screen, but via regular CGContextRef.
90         // The actual drawing to NSView (i.e. screen) is done in QuartzSurface::flush()
91 
92         // HACK: currently initial size for windowsurface is 0x0, which is not possible for us.
93         if (width == 0 || height == 0) {
94             width = [mpView bounds].size.width;
95             height = [mpView bounds].size.height;
96             OSL_TRACE("Canvas::cairo::Surface(): BUG!! size is ZERO! fixing to %d x %d...", width, height);
97         }
98 
99         // create a generic surface, NSView/Window is ARGB32.
100         mpSurface.reset(
101             cairo_quartz_surface_create(CAIRO_FORMAT_ARGB32, width, height),
102             &cairo_surface_destroy);
103 
104         cairo_surface_set_device_offset( mpSurface.get(), x, y );
105 	}
106 
107     /**
108      * QuartzSurface::Surface:   Create Canvas surface from CGContextRef.
109      * @param CGContext Native graphics context
110      * @param x horizontal location of the new surface
111      * @param y vertical location of the new surface
112      * @param width width of the new surface
113      * @param height height of the new surface
114      *
115      * Set the mpSurface to the new surface or NULL
116      **/
117     QuartzSurface::QuartzSurface( CGContextRef rContext, int x, int y, int width, int height ) :
118             mpView(NULL),
119             mpSurface()
120     {
121         OSL_TRACE("Canvas::cairo::Surface(CGContext:%p, x:%d, y:%d, w:%d, h:%d): New Surface.", rContext, x, y, width, height);
122         // create surface based on CGContext
123 
124         // ensure kCGBitmapByteOrder32Host flag, otherwise Cairo breaks (we are practically always using CGBitmapContext)
125         OSL_ASSERT ((CGBitmapContextGetBitsPerPixel(rContext) != 32) ||
126                     (CGBitmapContextGetBitmapInfo(rContext) & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host);
127 
128         mpSurface.reset(cairo_quartz_surface_create_for_cg_context(rContext, width, height),
129                         &cairo_surface_destroy);
130 
131         cairo_surface_set_device_offset( mpSurface.get(), x, y );
132 
133         // Necessary, context is lost otherwise
134         CGContextRetain(rContext);
135 	}
136 
137 
138     /**
139      * QuartzSurface::getCairo:  Create Cairo (drawing object) for the Canvas surface
140      *
141      * @return new Cairo or NULL
142      **/
143 	CairoSharedPtr QuartzSurface::getCairo() const
144     {
145         if (mpSurface.get()){
146             return CairoSharedPtr( cairo_create(mpSurface.get()),
147                                    &cairo_destroy );
148         } else {
149             return CairoSharedPtr();
150         }
151     }
152 
153     /**
154      * QuartzSurface::getSimilar:  Create new similar Canvas surface
155      * @param aContent format of the new surface (cairo_content_t from cairo/src/cairo.h)
156      * @param width width of the new surface
157      * @param height height of the new surface
158      *
159      * Creates a new Canvas surface. This normally creates platform native surface, even though
160      * generic function is used.
161      *
162      * Cairo surface from aContent (cairo_content_t)
163      *
164      * @return new surface or NULL
165      **/
166 	SurfaceSharedPtr QuartzSurface::getSimilar( Content aContent, int width, int height ) const
167 	{
168         return SurfaceSharedPtr(
169             new QuartzSurface(
170                 CairoSurfaceSharedPtr(
171                     cairo_surface_create_similar( mpSurface.get(), aContent, width, height ),
172                     &cairo_surface_destroy )));
173 	}
174 
175     /**
176      * QuartzSurface::Resize:  Resizes the Canvas surface.
177      * @param width new width of the surface
178      * @param height new height of the surface
179      *
180      * Only used on X11.
181      *
182      * @return The new surface or NULL
183      **/
184 	void QuartzSurface::Resize( int width, int height )
185 	{
186         OSL_ENSURE(false,"not supposed to be called!");
187 	}
188 
189 
190     /**
191      * QuartzSurface::flush:  Draw the data to screen
192      **/
193     void QuartzSurface::flush() const
194     {
195         // can only flush surfaces with NSView
196         if( !mpView ) return;
197 
198         OSL_TRACE("Canvas::cairo::QuartzSurface::flush(): flush to NSView");
199 
200         CGContextRef mrContext = getCGContext();
201 
202         if (!mrContext) return;
203 
204         [mpView lockFocus];
205 
206         /**
207          * This code is using same screen update code as in VCL (esp. AquaSalGraphics::UpdateWindow() )
208          */
209         CGContextRef rViewContext = reinterpret_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
210         CGImageRef xImage = CGBitmapContextCreateImage(mrContext);
211         CGContextDrawImage(rViewContext,
212                            CGRectMake( 0, 0,
213                                        CGImageGetWidth(xImage),
214                                        CGImageGetHeight(xImage)),
215                            xImage);
216         CGImageRelease( xImage );
217         CGContextFlush( rViewContext );
218 
219         [mpView unlockFocus];
220     }
221 
222     /**
223      * QuartzSurface::getDepth:  Get the color depth of the Canvas surface.
224      *
225      * @return color depth
226      **/
227 	int QuartzSurface::getDepth() const
228 	{
229         if (mpSurface.get()) {
230             switch (cairo_surface_get_content (mpSurface.get())) {
231                 case CAIRO_CONTENT_ALPHA:       return 8;  break;
232                 case CAIRO_CONTENT_COLOR:       return 24; break;
233                 case CAIRO_CONTENT_COLOR_ALPHA: return 32; break;
234             }
235         }
236         OSL_TRACE("Canvas::cairo::QuartzSurface::getDepth(): ERROR - depth unspecified!");
237 
238 		return -1;
239 	}
240 
241     /**
242      * QuartzSurface::getCGContext: Get the native CGContextRef of the Canvas's cairo surface
243      *
244      * @return graphics context
245      **/
246     CGContextRef QuartzSurface::getCGContext() const
247     {
248         if (mpSurface.get())
249             return cairo_quartz_surface_get_cg_context(mpSurface.get());
250         else
251             return NULL;
252     }
253 
254     /**
255      * cairo::createVirtualDevice:  Create a VCL virtual device for the CGContext in the cairo Surface
256      *
257      * @return The new virtual device
258      **/
259     boost::shared_ptr<VirtualDevice> QuartzSurface::createVirtualDevice() const
260     {
261         SystemGraphicsData aSystemGraphicsData;
262         aSystemGraphicsData.nSize = sizeof(SystemGraphicsData);
263         aSystemGraphicsData.rCGContext = getCGContext();
264         return boost::shared_ptr<VirtualDevice>(
265             new VirtualDevice( &aSystemGraphicsData, getDepth() ));
266     }
267 
268     /**
269      * cairo::createSurface:     Create generic Canvas surface using given Cairo Surface
270      *
271      * @param rSurface Cairo Surface
272      *
273      * @return new Surface
274      */
275     SurfaceSharedPtr createSurface( const CairoSurfaceSharedPtr& rSurface )
276     {
277         return SurfaceSharedPtr(new QuartzSurface(rSurface));
278     }
279 
280     /**
281      * cairo::createSurface:     Create Canvas surface using given VCL Window or Virtualdevice
282      *
283      * @param rSurface Cairo Surface
284      *
285      *  For VCL Window, use platform native system environment data (struct SystemEnvData in vcl/inc/sysdata.hxx)
286      *  For VCL Virtualdevice, use platform native system graphics data (struct SystemGraphicsData in vcl/inc/sysdata.hxx)
287      *
288      * @return new Surface
289      */
290     SurfaceSharedPtr createSurface( const OutputDevice& rRefDevice,
291                                     int x, int y, int width, int height )
292     {
293         SurfaceSharedPtr surf;
294 
295         if( rRefDevice.GetOutDevType() == OUTDEV_WINDOW )
296         {
297             const Window &rWindow = (const Window &) rRefDevice;
298             const SystemEnvData* pSysData = GetSysData(&rWindow);
299             if (pSysData)
300                 surf = SurfaceSharedPtr(new QuartzSurface(pSysData->pView, x, y, width, height));
301         }
302         else if( rRefDevice.GetOutDevType() == OUTDEV_VIRDEV )
303         {
304             SystemGraphicsData aSysData = ((const VirtualDevice&) rRefDevice).GetSystemGfxData();
305 
306             if (aSysData.rCGContext)
307                 surf =  SurfaceSharedPtr(new QuartzSurface(aSysData.rCGContext, x, y, width, height));
308         }
309         return surf;
310     }
311 
312     /**
313      * cairo::createBitmapSurface:   Create platfrom native Canvas surface from BitmapSystemData
314      * @param OutputDevice (not used)
315      * @param rData Platform native image data (struct BitmapSystemData in vcl/inc/bitmap.hxx)
316      * @param rSize width and height of the new surface
317      *
318      * Create a surface based on image data on rData
319      *
320      * @return new surface or empty surface
321      **/
322     SurfaceSharedPtr createBitmapSurface( const OutputDevice&     /* rRefDevice */,
323                                           const BitmapSystemData& rData,
324                                           const Size&             rSize )
325     {
326         OSL_TRACE( "requested size: %d x %d available size: %d x %d",
327                    rSize.Width(), rSize.Height(), rData.mnWidth, rData.mnHeight );
328 
329         if ( rData.mnWidth == rSize.Width() && rData.mnHeight == rSize.Height() )
330         {
331             CGContextRef rContext = (CGContextRef)rData.rImageContext;
332             OSL_TRACE("Canvas::cairo::createBitmapSurface(): New native image surface, context = %p.", rData.rImageContext);
333 
334             return SurfaceSharedPtr(new QuartzSurface(rContext, 0, 0, rData.mnWidth, rData.mnHeight));
335         }
336         return SurfaceSharedPtr();
337     }
338 
339 }  // namespace cairo
340 
341 #endif   // CAIRO_HAS_QUARTZ_SURFACE
342 
343 #endif   // QUARTZ
344