|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 // vim:set ts=2 sts=2 sw=2 et cin: |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "QuartzSupport.h" |
|
8 #include "nsDebug.h" |
|
9 #include "MacIOSurface.h" |
|
10 |
|
11 #import <QuartzCore/QuartzCore.h> |
|
12 #import <AppKit/NSOpenGL.h> |
|
13 #include <dlfcn.h> |
|
14 #include "GLDefs.h" |
|
15 |
|
16 #define IOSURFACE_FRAMEWORK_PATH \ |
|
17 "/System/Library/Frameworks/IOSurface.framework/IOSurface" |
|
18 #define OPENGL_FRAMEWORK_PATH \ |
|
19 "/System/Library/Frameworks/OpenGL.framework/OpenGL" |
|
20 #define COREGRAPHICS_FRAMEWORK_PATH \ |
|
21 "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/CoreGraphics" |
|
22 |
|
23 @interface CALayer (ContentsScale) |
|
24 - (double)contentsScale; |
|
25 - (void)setContentsScale:(double)scale; |
|
26 @end |
|
27 |
|
28 using mozilla::RefPtr; |
|
29 using mozilla::TemporaryRef; |
|
30 |
|
31 CGColorSpaceRef CreateSystemColorSpace() { |
|
32 CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID()); |
|
33 if (!cspace) { |
|
34 cspace = ::CGColorSpaceCreateDeviceRGB(); |
|
35 } |
|
36 return cspace; |
|
37 } |
|
38 |
|
39 nsCARenderer::~nsCARenderer() { |
|
40 Destroy(); |
|
41 } |
|
42 |
|
43 void cgdata_release_callback(void *aCGData, const void *data, size_t size) { |
|
44 if (aCGData) { |
|
45 free(aCGData); |
|
46 } |
|
47 } |
|
48 |
|
49 void nsCARenderer::Destroy() { |
|
50 if (mCARenderer) { |
|
51 CARenderer* caRenderer = (CARenderer*)mCARenderer; |
|
52 // Bug 556453: |
|
53 // Explicitly remove the layer from the renderer |
|
54 // otherwise it does not always happen right away. |
|
55 caRenderer.layer = nullptr; |
|
56 [caRenderer release]; |
|
57 } |
|
58 if (mWrapperCALayer) { |
|
59 CALayer* wrapperLayer = (CALayer*)mWrapperCALayer; |
|
60 [wrapperLayer release]; |
|
61 } |
|
62 if (mOpenGLContext) { |
|
63 if (mFBO || mIOTexture || mFBOTexture) { |
|
64 // Release these resources with the context that allocated them |
|
65 CGLContextObj oldContext = ::CGLGetCurrentContext(); |
|
66 ::CGLSetCurrentContext(mOpenGLContext); |
|
67 |
|
68 if (mFBOTexture) { |
|
69 ::glDeleteTextures(1, &mFBOTexture); |
|
70 } |
|
71 if (mIOTexture) { |
|
72 ::glDeleteTextures(1, &mIOTexture); |
|
73 } |
|
74 if (mFBO) { |
|
75 ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); |
|
76 ::glDeleteFramebuffersEXT(1, &mFBO); |
|
77 } |
|
78 |
|
79 if (oldContext) |
|
80 ::CGLSetCurrentContext(oldContext); |
|
81 } |
|
82 ::CGLDestroyContext((CGLContextObj)mOpenGLContext); |
|
83 } |
|
84 if (mCGImage) { |
|
85 ::CGImageRelease(mCGImage); |
|
86 } |
|
87 // mCGData is deallocated by cgdata_release_callback |
|
88 |
|
89 mCARenderer = nil; |
|
90 mWrapperCALayer = nil; |
|
91 mFBOTexture = 0; |
|
92 mOpenGLContext = nullptr; |
|
93 mCGImage = nullptr; |
|
94 mIOSurface = nullptr; |
|
95 mFBO = 0; |
|
96 mIOTexture = 0; |
|
97 } |
|
98 |
|
99 nsresult nsCARenderer::SetupRenderer(void *aCALayer, int aWidth, int aHeight, |
|
100 double aContentsScaleFactor, |
|
101 AllowOfflineRendererEnum aAllowOfflineRenderer) { |
|
102 mAllowOfflineRenderer = aAllowOfflineRenderer; |
|
103 |
|
104 if (aWidth == 0 || aHeight == 0 || aContentsScaleFactor <= 0) |
|
105 return NS_ERROR_FAILURE; |
|
106 |
|
107 if (aWidth == mUnsupportedWidth && |
|
108 aHeight == mUnsupportedHeight) { |
|
109 return NS_ERROR_FAILURE; |
|
110 } |
|
111 |
|
112 CGLPixelFormatAttribute attributes[] = { |
|
113 kCGLPFAAccelerated, |
|
114 kCGLPFADepthSize, (CGLPixelFormatAttribute)24, |
|
115 kCGLPFAAllowOfflineRenderers, |
|
116 (CGLPixelFormatAttribute)0 |
|
117 }; |
|
118 |
|
119 if (mAllowOfflineRenderer == DISALLOW_OFFLINE_RENDERER) { |
|
120 attributes[3] = (CGLPixelFormatAttribute)0; |
|
121 } |
|
122 |
|
123 GLint screen; |
|
124 CGLPixelFormatObj format; |
|
125 if (::CGLChoosePixelFormat(attributes, &format, &screen) != kCGLNoError) { |
|
126 mUnsupportedWidth = aWidth; |
|
127 mUnsupportedHeight = aHeight; |
|
128 Destroy(); |
|
129 return NS_ERROR_FAILURE; |
|
130 } |
|
131 |
|
132 if (::CGLCreateContext(format, nullptr, &mOpenGLContext) != kCGLNoError) { |
|
133 mUnsupportedWidth = aWidth; |
|
134 mUnsupportedHeight = aHeight; |
|
135 Destroy(); |
|
136 return NS_ERROR_FAILURE; |
|
137 } |
|
138 ::CGLDestroyPixelFormat(format); |
|
139 |
|
140 CARenderer* caRenderer = [[CARenderer rendererWithCGLContext:mOpenGLContext |
|
141 options:nil] retain]; |
|
142 if (caRenderer == nil) { |
|
143 mUnsupportedWidth = aWidth; |
|
144 mUnsupportedHeight = aHeight; |
|
145 Destroy(); |
|
146 return NS_ERROR_FAILURE; |
|
147 } |
|
148 CALayer* wrapperCALayer = [[CALayer layer] retain]; |
|
149 if (wrapperCALayer == nil) { |
|
150 [caRenderer release]; |
|
151 mUnsupportedWidth = aWidth; |
|
152 mUnsupportedHeight = aHeight; |
|
153 Destroy(); |
|
154 return NS_ERROR_FAILURE; |
|
155 } |
|
156 |
|
157 mCARenderer = caRenderer; |
|
158 mWrapperCALayer = wrapperCALayer; |
|
159 caRenderer.layer = wrapperCALayer; |
|
160 [wrapperCALayer addSublayer:(CALayer*)aCALayer]; |
|
161 mContentsScaleFactor = aContentsScaleFactor; |
|
162 size_t intScaleFactor = ceil(mContentsScaleFactor); |
|
163 SetBounds(aWidth, aHeight); |
|
164 |
|
165 // We target rendering to a CGImage if no shared IOSurface are given. |
|
166 if (!mIOSurface) { |
|
167 mCGData = malloc(aWidth*intScaleFactor*aHeight*4*intScaleFactor); |
|
168 if (!mCGData) { |
|
169 mUnsupportedWidth = aWidth; |
|
170 mUnsupportedHeight = aHeight; |
|
171 Destroy(); |
|
172 return NS_ERROR_FAILURE; |
|
173 } |
|
174 memset(mCGData, 0, aWidth*intScaleFactor*aHeight*4*intScaleFactor); |
|
175 |
|
176 CGDataProviderRef dataProvider = nullptr; |
|
177 dataProvider = ::CGDataProviderCreateWithData(mCGData, |
|
178 mCGData, aHeight*intScaleFactor*aWidth*4*intScaleFactor, |
|
179 cgdata_release_callback); |
|
180 if (!dataProvider) { |
|
181 cgdata_release_callback(mCGData, mCGData, |
|
182 aHeight*intScaleFactor*aWidth*4*intScaleFactor); |
|
183 mUnsupportedWidth = aWidth; |
|
184 mUnsupportedHeight = aHeight; |
|
185 Destroy(); |
|
186 return NS_ERROR_FAILURE; |
|
187 } |
|
188 |
|
189 CGColorSpaceRef colorSpace = CreateSystemColorSpace(); |
|
190 |
|
191 mCGImage = ::CGImageCreate(aWidth * intScaleFactor, aHeight * intScaleFactor, |
|
192 8, 32, aWidth * intScaleFactor * 4, colorSpace, |
|
193 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, |
|
194 dataProvider, nullptr, true, kCGRenderingIntentDefault); |
|
195 |
|
196 ::CGDataProviderRelease(dataProvider); |
|
197 ::CGColorSpaceRelease(colorSpace); |
|
198 if (!mCGImage) { |
|
199 mUnsupportedWidth = aWidth; |
|
200 mUnsupportedHeight = aHeight; |
|
201 Destroy(); |
|
202 return NS_ERROR_FAILURE; |
|
203 } |
|
204 } |
|
205 |
|
206 CGLContextObj oldContext = ::CGLGetCurrentContext(); |
|
207 ::CGLSetCurrentContext(mOpenGLContext); |
|
208 |
|
209 if (mIOSurface) { |
|
210 // Create the IOSurface mapped texture. |
|
211 ::glGenTextures(1, &mIOTexture); |
|
212 ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mIOTexture); |
|
213 ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
|
214 ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
|
215 MacIOSurfaceLib::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB, |
|
216 GL_RGBA, aWidth * intScaleFactor, |
|
217 aHeight * intScaleFactor, |
|
218 GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, |
|
219 mIOSurface->mIOSurfacePtr, 0); |
|
220 ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); |
|
221 } else { |
|
222 ::glGenTextures(1, &mFBOTexture); |
|
223 ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFBOTexture); |
|
224 ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
|
225 ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
|
226 ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); |
|
227 } |
|
228 |
|
229 // Create the fbo |
|
230 ::glGenFramebuffersEXT(1, &mFBO); |
|
231 ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO); |
|
232 if (mIOSurface) { |
|
233 ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, |
|
234 GL_TEXTURE_RECTANGLE_ARB, mIOTexture, 0); |
|
235 } else { |
|
236 ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, |
|
237 GL_TEXTURE_RECTANGLE_ARB, mFBOTexture, 0); |
|
238 } |
|
239 |
|
240 |
|
241 // Make sure that the Framebuffer configuration is supported on the client machine |
|
242 GLenum fboStatus; |
|
243 fboStatus = ::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); |
|
244 if (fboStatus != GL_FRAMEBUFFER_COMPLETE_EXT) { |
|
245 NS_ERROR("FBO not supported"); |
|
246 if (oldContext) |
|
247 ::CGLSetCurrentContext(oldContext); |
|
248 mUnsupportedWidth = aWidth; |
|
249 mUnsupportedHeight = aHeight; |
|
250 Destroy(); |
|
251 return NS_ERROR_FAILURE; |
|
252 } |
|
253 |
|
254 SetViewport(aWidth, aHeight); |
|
255 |
|
256 GLenum result = ::glGetError(); |
|
257 if (result != GL_NO_ERROR) { |
|
258 NS_ERROR("Unexpected OpenGL Error"); |
|
259 mUnsupportedWidth = aWidth; |
|
260 mUnsupportedHeight = aHeight; |
|
261 Destroy(); |
|
262 if (oldContext) |
|
263 ::CGLSetCurrentContext(oldContext); |
|
264 return NS_ERROR_FAILURE; |
|
265 } |
|
266 |
|
267 if (oldContext) |
|
268 ::CGLSetCurrentContext(oldContext); |
|
269 |
|
270 return NS_OK; |
|
271 } |
|
272 |
|
273 void nsCARenderer::SetBounds(int aWidth, int aHeight) { |
|
274 CARenderer* caRenderer = (CARenderer*)mCARenderer; |
|
275 CALayer* wrapperLayer = (CALayer*)mWrapperCALayer; |
|
276 NSArray* sublayers = [wrapperLayer sublayers]; |
|
277 CALayer* pluginLayer = (CALayer*) [sublayers objectAtIndex:0]; |
|
278 |
|
279 // Create a transaction and disable animations |
|
280 // to make the position update instant. |
|
281 [CATransaction begin]; |
|
282 NSMutableDictionary *newActions = |
|
283 [[NSMutableDictionary alloc] initWithObjectsAndKeys: |
|
284 [NSNull null], @"onOrderIn", |
|
285 [NSNull null], @"onOrderOut", |
|
286 [NSNull null], @"sublayers", |
|
287 [NSNull null], @"contents", |
|
288 [NSNull null], @"position", |
|
289 [NSNull null], @"bounds", |
|
290 nil]; |
|
291 wrapperLayer.actions = newActions; |
|
292 [newActions release]; |
|
293 |
|
294 // If we're in HiDPI mode, mContentsScaleFactor will (presumably) be 2.0. |
|
295 // For some reason, to make things work properly in HiDPI mode we need to |
|
296 // make caRenderer's 'bounds' and 'layer' different sizes -- to set 'bounds' |
|
297 // to the size of 'layer's backing store. And to avoid this possibly |
|
298 // confusing the plugin, we need to hide it's effects from the plugin by |
|
299 // making pluginLayer (usually the CALayer* provided by the plugin) a |
|
300 // sublayer of our own wrapperLayer (see bug 829284). |
|
301 size_t intScaleFactor = ceil(mContentsScaleFactor); |
|
302 [CATransaction setValue: [NSNumber numberWithFloat:0.0f] forKey: kCATransactionAnimationDuration]; |
|
303 [CATransaction setValue: (id) kCFBooleanTrue forKey: kCATransactionDisableActions]; |
|
304 [wrapperLayer setBounds:CGRectMake(0, 0, aWidth, aHeight)]; |
|
305 [wrapperLayer setPosition:CGPointMake(aWidth/2.0, aHeight/2.0)]; |
|
306 [pluginLayer setBounds:CGRectMake(0, 0, aWidth, aHeight)]; |
|
307 [pluginLayer setFrame:CGRectMake(0, 0, aWidth, aHeight)]; |
|
308 caRenderer.bounds = CGRectMake(0, 0, aWidth * intScaleFactor, aHeight * intScaleFactor); |
|
309 if (mContentsScaleFactor != 1.0) { |
|
310 CGAffineTransform affineTransform = [wrapperLayer affineTransform]; |
|
311 affineTransform.a = mContentsScaleFactor; |
|
312 affineTransform.d = mContentsScaleFactor; |
|
313 affineTransform.tx = ((double)aWidth)/mContentsScaleFactor; |
|
314 affineTransform.ty = ((double)aHeight)/mContentsScaleFactor; |
|
315 [wrapperLayer setAffineTransform:affineTransform]; |
|
316 } else { |
|
317 // These settings are the default values. But they might have been |
|
318 // changed as above if we were previously running in a HiDPI mode |
|
319 // (i.e. if we just switched from that to a non-HiDPI mode). |
|
320 [wrapperLayer setAffineTransform:CGAffineTransformIdentity]; |
|
321 } |
|
322 [CATransaction commit]; |
|
323 } |
|
324 |
|
325 void nsCARenderer::SetViewport(int aWidth, int aHeight) { |
|
326 size_t intScaleFactor = ceil(mContentsScaleFactor); |
|
327 aWidth *= intScaleFactor; |
|
328 aHeight *= intScaleFactor; |
|
329 |
|
330 ::glViewport(0.0, 0.0, aWidth, aHeight); |
|
331 ::glMatrixMode(GL_PROJECTION); |
|
332 ::glLoadIdentity(); |
|
333 ::glOrtho (0.0, aWidth, 0.0, aHeight, -1, 1); |
|
334 |
|
335 // Render upside down to speed up CGContextDrawImage |
|
336 ::glTranslatef(0.0f, aHeight, 0.0); |
|
337 ::glScalef(1.0, -1.0, 1.0); |
|
338 } |
|
339 |
|
340 void nsCARenderer::AttachIOSurface(RefPtr<MacIOSurface> aSurface) { |
|
341 if (mIOSurface && |
|
342 aSurface->GetIOSurfaceID() == mIOSurface->GetIOSurfaceID()) { |
|
343 // This object isn't needed since we already have a |
|
344 // handle to the same io surface. |
|
345 aSurface = nullptr; |
|
346 return; |
|
347 } |
|
348 |
|
349 mIOSurface = aSurface; |
|
350 |
|
351 // Update the framebuffer and viewport |
|
352 if (mCARenderer) { |
|
353 CARenderer* caRenderer = (CARenderer*)mCARenderer; |
|
354 size_t intScaleFactor = ceil(mContentsScaleFactor); |
|
355 int width = caRenderer.bounds.size.width / intScaleFactor; |
|
356 int height = caRenderer.bounds.size.height / intScaleFactor; |
|
357 |
|
358 CGLContextObj oldContext = ::CGLGetCurrentContext(); |
|
359 ::CGLSetCurrentContext(mOpenGLContext); |
|
360 ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mIOTexture); |
|
361 MacIOSurfaceLib::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB, |
|
362 GL_RGBA, mIOSurface->GetDevicePixelWidth(), |
|
363 mIOSurface->GetDevicePixelHeight(), |
|
364 GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, |
|
365 mIOSurface->mIOSurfacePtr, 0); |
|
366 ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); |
|
367 |
|
368 // Rebind the FBO to make it live |
|
369 ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO); |
|
370 |
|
371 if (static_cast<int>(mIOSurface->GetWidth()) != width || |
|
372 static_cast<int>(mIOSurface->GetHeight()) != height) { |
|
373 width = mIOSurface->GetWidth(); |
|
374 height = mIOSurface->GetHeight(); |
|
375 SetBounds(width, height); |
|
376 SetViewport(width, height); |
|
377 } |
|
378 |
|
379 if (oldContext) { |
|
380 ::CGLSetCurrentContext(oldContext); |
|
381 } |
|
382 } |
|
383 } |
|
384 |
|
385 IOSurfaceID nsCARenderer::GetIOSurfaceID() { |
|
386 if (!mIOSurface) { |
|
387 return 0; |
|
388 } |
|
389 |
|
390 return mIOSurface->GetIOSurfaceID(); |
|
391 } |
|
392 |
|
393 nsresult nsCARenderer::Render(int aWidth, int aHeight, |
|
394 double aContentsScaleFactor, |
|
395 CGImageRef *aOutCGImage) { |
|
396 if (!aOutCGImage && !mIOSurface) { |
|
397 NS_ERROR("No target destination for rendering"); |
|
398 } else if (aOutCGImage) { |
|
399 // We are expected to return a CGImageRef, we will set |
|
400 // it to nullptr in case we fail before the image is ready. |
|
401 *aOutCGImage = nullptr; |
|
402 } |
|
403 |
|
404 if (aWidth == 0 || aHeight == 0 || aContentsScaleFactor <= 0) |
|
405 return NS_OK; |
|
406 |
|
407 if (!mCARenderer || !mWrapperCALayer) { |
|
408 return NS_ERROR_FAILURE; |
|
409 } |
|
410 |
|
411 CARenderer* caRenderer = (CARenderer*)mCARenderer; |
|
412 CALayer* wrapperLayer = (CALayer*)mWrapperCALayer; |
|
413 size_t intScaleFactor = ceil(aContentsScaleFactor); |
|
414 int renderer_width = caRenderer.bounds.size.width / intScaleFactor; |
|
415 int renderer_height = caRenderer.bounds.size.height / intScaleFactor; |
|
416 |
|
417 if (renderer_width != aWidth || renderer_height != aHeight || |
|
418 mContentsScaleFactor != aContentsScaleFactor) { |
|
419 // XXX: This should be optimized to not rescale the buffer |
|
420 // if we are resizing down. |
|
421 // caLayer may be the CALayer* provided by the plugin, so we need to |
|
422 // preserve it across the call to Destroy(). |
|
423 NSArray* sublayers = [wrapperLayer sublayers]; |
|
424 CALayer* caLayer = (CALayer*) [sublayers objectAtIndex:0]; |
|
425 // mIOSurface is set by AttachIOSurface(), not by SetupRenderer(). So |
|
426 // since it may have been set by a prior call to AttachIOSurface(), we |
|
427 // need to preserve it across the call to Destroy(). |
|
428 mozilla::RefPtr<MacIOSurface> ioSurface = mIOSurface; |
|
429 Destroy(); |
|
430 mIOSurface = ioSurface; |
|
431 if (SetupRenderer(caLayer, aWidth, aHeight, aContentsScaleFactor, |
|
432 mAllowOfflineRenderer) != NS_OK) { |
|
433 return NS_ERROR_FAILURE; |
|
434 } |
|
435 |
|
436 caRenderer = (CARenderer*)mCARenderer; |
|
437 } |
|
438 |
|
439 CGLContextObj oldContext = ::CGLGetCurrentContext(); |
|
440 ::CGLSetCurrentContext(mOpenGLContext); |
|
441 if (!mIOSurface) { |
|
442 // If no shared IOSurface is given render to our own |
|
443 // texture for readback. |
|
444 ::glGenTextures(1, &mFBOTexture); |
|
445 } |
|
446 |
|
447 GLenum result = ::glGetError(); |
|
448 if (result != GL_NO_ERROR) { |
|
449 NS_ERROR("Unexpected OpenGL Error"); |
|
450 Destroy(); |
|
451 if (oldContext) |
|
452 ::CGLSetCurrentContext(oldContext); |
|
453 return NS_ERROR_FAILURE; |
|
454 } |
|
455 |
|
456 ::glClearColor(0.0, 0.0, 0.0, 0.0); |
|
457 ::glClear(GL_COLOR_BUFFER_BIT); |
|
458 |
|
459 [CATransaction commit]; |
|
460 double caTime = ::CACurrentMediaTime(); |
|
461 [caRenderer beginFrameAtTime:caTime timeStamp:nullptr]; |
|
462 [caRenderer addUpdateRect:CGRectMake(0,0, aWidth * intScaleFactor, |
|
463 aHeight * intScaleFactor)]; |
|
464 [caRenderer render]; |
|
465 [caRenderer endFrame]; |
|
466 |
|
467 // Read the data back either to the IOSurface or mCGImage. |
|
468 if (mIOSurface) { |
|
469 ::glFlush(); |
|
470 } else { |
|
471 ::glPixelStorei(GL_PACK_ALIGNMENT, 4); |
|
472 ::glPixelStorei(GL_PACK_ROW_LENGTH, 0); |
|
473 ::glPixelStorei(GL_PACK_SKIP_ROWS, 0); |
|
474 ::glPixelStorei(GL_PACK_SKIP_PIXELS, 0); |
|
475 |
|
476 ::glReadPixels(0.0f, 0.0f, aWidth * intScaleFactor, |
|
477 aHeight * intScaleFactor, |
|
478 GL_BGRA, GL_UNSIGNED_BYTE, |
|
479 mCGData); |
|
480 |
|
481 *aOutCGImage = mCGImage; |
|
482 } |
|
483 |
|
484 if (oldContext) { |
|
485 ::CGLSetCurrentContext(oldContext); |
|
486 } |
|
487 |
|
488 return NS_OK; |
|
489 } |
|
490 |
|
491 nsresult nsCARenderer::DrawSurfaceToCGContext(CGContextRef aContext, |
|
492 MacIOSurface *surf, |
|
493 CGColorSpaceRef aColorSpace, |
|
494 int aX, int aY, |
|
495 size_t aWidth, size_t aHeight) { |
|
496 surf->Lock(); |
|
497 size_t bytesPerRow = surf->GetBytesPerRow(); |
|
498 size_t ioWidth = surf->GetWidth(); |
|
499 size_t ioHeight = surf->GetHeight(); |
|
500 |
|
501 // We get rendering glitches if we use a width/height that falls |
|
502 // outside of the IOSurface. |
|
503 if (aWidth + aX > ioWidth) |
|
504 aWidth = ioWidth - aX; |
|
505 if (aHeight + aY > ioHeight) |
|
506 aHeight = ioHeight - aY; |
|
507 |
|
508 if (aX < 0 || static_cast<size_t>(aX) >= ioWidth || |
|
509 aY < 0 || static_cast<size_t>(aY) >= ioHeight) { |
|
510 surf->Unlock(); |
|
511 return NS_ERROR_FAILURE; |
|
512 } |
|
513 |
|
514 void* ioData = surf->GetBaseAddress(); |
|
515 double scaleFactor = surf->GetContentsScaleFactor(); |
|
516 size_t intScaleFactor = ceil(surf->GetContentsScaleFactor()); |
|
517 CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(ioData, |
|
518 ioData, ioHeight*intScaleFactor*(bytesPerRow)*4, |
|
519 nullptr); //No release callback |
|
520 if (!dataProvider) { |
|
521 surf->Unlock(); |
|
522 return NS_ERROR_FAILURE; |
|
523 } |
|
524 |
|
525 CGImageRef cgImage = ::CGImageCreate(ioWidth * intScaleFactor, |
|
526 ioHeight * intScaleFactor, 8, 32, bytesPerRow, aColorSpace, |
|
527 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, |
|
528 dataProvider, nullptr, true, kCGRenderingIntentDefault); |
|
529 ::CGDataProviderRelease(dataProvider); |
|
530 if (!cgImage) { |
|
531 surf->Unlock(); |
|
532 return NS_ERROR_FAILURE; |
|
533 } |
|
534 CGImageRef subImage = ::CGImageCreateWithImageInRect(cgImage, |
|
535 ::CGRectMake(aX * scaleFactor, |
|
536 aY * scaleFactor, |
|
537 aWidth * scaleFactor, |
|
538 aHeight * scaleFactor)); |
|
539 if (!subImage) { |
|
540 ::CGImageRelease(cgImage); |
|
541 surf->Unlock(); |
|
542 return NS_ERROR_FAILURE; |
|
543 } |
|
544 |
|
545 ::CGContextScaleCTM(aContext, 1.0f, -1.0f); |
|
546 ::CGContextDrawImage(aContext, |
|
547 CGRectMake(aX * scaleFactor, |
|
548 (-(CGFloat)aY - (CGFloat)aHeight) * scaleFactor, |
|
549 aWidth * scaleFactor, |
|
550 aHeight * scaleFactor), |
|
551 subImage); |
|
552 |
|
553 ::CGImageRelease(subImage); |
|
554 ::CGImageRelease(cgImage); |
|
555 surf->Unlock(); |
|
556 return NS_OK; |
|
557 } |
|
558 |
|
559 void nsCARenderer::DetachCALayer() { |
|
560 CALayer* wrapperLayer = (CALayer*)mWrapperCALayer; |
|
561 NSArray* sublayers = [wrapperLayer sublayers]; |
|
562 CALayer* oldLayer = (CALayer*) [sublayers objectAtIndex:0]; |
|
563 [oldLayer removeFromSuperlayer]; |
|
564 } |
|
565 |
|
566 void nsCARenderer::AttachCALayer(void *aCALayer) { |
|
567 CALayer* wrapperLayer = (CALayer*)mWrapperCALayer; |
|
568 NSArray* sublayers = [wrapperLayer sublayers]; |
|
569 CALayer* oldLayer = (CALayer*) [sublayers objectAtIndex:0]; |
|
570 [oldLayer removeFromSuperlayer]; |
|
571 [wrapperLayer addSublayer:(CALayer*)aCALayer]; |
|
572 } |
|
573 |
|
574 #ifdef DEBUG |
|
575 |
|
576 int sSaveToDiskSequence = 0; |
|
577 void nsCARenderer::SaveToDisk(MacIOSurface *surf) { |
|
578 surf->Lock(); |
|
579 size_t bytesPerRow = surf->GetBytesPerRow(); |
|
580 size_t ioWidth = surf->GetWidth(); |
|
581 size_t ioHeight = surf->GetHeight(); |
|
582 void* ioData = surf->GetBaseAddress(); |
|
583 CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(ioData, |
|
584 ioData, ioHeight*(bytesPerRow)*4, |
|
585 nullptr); //No release callback |
|
586 if (!dataProvider) { |
|
587 surf->Unlock(); |
|
588 return; |
|
589 } |
|
590 |
|
591 CGColorSpaceRef colorSpace = CreateSystemColorSpace(); |
|
592 CGImageRef cgImage = ::CGImageCreate(ioWidth, ioHeight, 8, 32, bytesPerRow, |
|
593 colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, |
|
594 dataProvider, nullptr, true, kCGRenderingIntentDefault); |
|
595 ::CGDataProviderRelease(dataProvider); |
|
596 ::CGColorSpaceRelease(colorSpace); |
|
597 if (!cgImage) { |
|
598 surf->Unlock(); |
|
599 return; |
|
600 } |
|
601 |
|
602 char cstr[1000]; |
|
603 |
|
604 sprintf(cstr, "file:///Users/benoitgirard/debug/iosurface_%i.png", ++sSaveToDiskSequence); |
|
605 |
|
606 CFStringRef cfStr = ::CFStringCreateWithCString(kCFAllocatorDefault, cstr, kCFStringEncodingMacRoman); |
|
607 |
|
608 printf("Exporting: %s\n", cstr); |
|
609 CFURLRef url = ::CFURLCreateWithString( nullptr, cfStr, nullptr); |
|
610 ::CFRelease(cfStr); |
|
611 |
|
612 CFStringRef type = kUTTypePNG; |
|
613 size_t count = 1; |
|
614 CFDictionaryRef options = nullptr; |
|
615 CGImageDestinationRef dest = ::CGImageDestinationCreateWithURL(url, type, count, options); |
|
616 ::CFRelease(url); |
|
617 |
|
618 ::CGImageDestinationAddImage(dest, cgImage, nullptr); |
|
619 |
|
620 ::CGImageDestinationFinalize(dest); |
|
621 ::CFRelease(dest); |
|
622 ::CGImageRelease(cgImage); |
|
623 |
|
624 surf->Unlock(); |
|
625 |
|
626 return; |
|
627 |
|
628 } |
|
629 |
|
630 #endif |