1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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/. */
7 #include "MacIOSurface.h"
9 #include <OpenGL/CGLIOSurface.h>
10 #include <QuartzCore/QuartzCore.h>
12 #include "GLContextCGL.h"
13 #include "mozilla/Assertions.h"
14 #include "mozilla/RefPtr.h"
16 using namespace mozilla
;
18 MacIOSurface::MacIOSurface(CFTypeRefPtr
<IOSurfaceRef
> aIOSurfaceRef
,
19 double aContentsScaleFactor
, bool aHasAlpha
,
20 gfx::YUVColorSpace aColorSpace
)
21 : mIOSurfaceRef(std::move(aIOSurfaceRef
)),
22 mContentsScaleFactor(aContentsScaleFactor
),
24 mColorSpace(aColorSpace
) {
28 MacIOSurface::~MacIOSurface() {
29 MOZ_RELEASE_ASSERT(!IsLocked(), "Destroying locked surface");
34 already_AddRefed
<MacIOSurface
> MacIOSurface::CreateIOSurface(
35 int aWidth
, int aHeight
, double aContentsScaleFactor
, bool aHasAlpha
) {
36 if (aContentsScaleFactor
<= 0) return nullptr;
38 CFMutableDictionaryRef props
= ::CFDictionaryCreateMutable(
39 kCFAllocatorDefault
, 4, &kCFTypeDictionaryKeyCallBacks
,
40 &kCFTypeDictionaryValueCallBacks
);
41 if (!props
) return nullptr;
43 MOZ_ASSERT((size_t)aWidth
<= GetMaxWidth());
44 MOZ_ASSERT((size_t)aHeight
<= GetMaxHeight());
46 int32_t bytesPerElem
= 4;
47 size_t intScaleFactor
= ceil(aContentsScaleFactor
);
48 aWidth
*= intScaleFactor
;
49 aHeight
*= intScaleFactor
;
50 CFNumberRef cfWidth
= ::CFNumberCreate(nullptr, kCFNumberSInt32Type
, &aWidth
);
51 CFNumberRef cfHeight
=
52 ::CFNumberCreate(nullptr, kCFNumberSInt32Type
, &aHeight
);
53 CFNumberRef cfBytesPerElem
=
54 ::CFNumberCreate(nullptr, kCFNumberSInt32Type
, &bytesPerElem
);
55 ::CFDictionaryAddValue(props
, kIOSurfaceWidth
, cfWidth
);
57 ::CFDictionaryAddValue(props
, kIOSurfaceHeight
, cfHeight
);
58 ::CFRelease(cfHeight
);
59 ::CFDictionaryAddValue(props
, kIOSurfaceBytesPerElement
, cfBytesPerElem
);
60 ::CFRelease(cfBytesPerElem
);
61 ::CFDictionaryAddValue(props
, kIOSurfaceIsGlobal
, kCFBooleanTrue
);
63 CFTypeRefPtr
<IOSurfaceRef
> surfaceRef
=
64 CFTypeRefPtr
<IOSurfaceRef
>::WrapUnderCreateRule(::IOSurfaceCreate(props
));
71 RefPtr
<MacIOSurface
> ioSurface
=
72 new MacIOSurface(std::move(surfaceRef
), aContentsScaleFactor
, aHasAlpha
);
74 return ioSurface
.forget();
78 already_AddRefed
<MacIOSurface
> MacIOSurface::LookupSurface(
79 IOSurfaceID aIOSurfaceID
, double aContentsScaleFactor
, bool aHasAlpha
,
80 gfx::YUVColorSpace aColorSpace
) {
81 if (aContentsScaleFactor
<= 0) return nullptr;
83 CFTypeRefPtr
<IOSurfaceRef
> surfaceRef
=
84 CFTypeRefPtr
<IOSurfaceRef
>::WrapUnderCreateRule(
85 ::IOSurfaceLookup(aIOSurfaceID
));
86 if (!surfaceRef
) return nullptr;
88 RefPtr
<MacIOSurface
> ioSurface
= new MacIOSurface(
89 std::move(surfaceRef
), aContentsScaleFactor
, aHasAlpha
, aColorSpace
);
91 return ioSurface
.forget();
94 IOSurfaceID
MacIOSurface::GetIOSurfaceID() const {
95 return ::IOSurfaceGetID(mIOSurfaceRef
.get());
98 void* MacIOSurface::GetBaseAddress() const {
99 return ::IOSurfaceGetBaseAddress(mIOSurfaceRef
.get());
102 void* MacIOSurface::GetBaseAddressOfPlane(size_t aPlaneIndex
) const {
103 return ::IOSurfaceGetBaseAddressOfPlane(mIOSurfaceRef
.get(), aPlaneIndex
);
106 size_t MacIOSurface::GetWidth(size_t plane
) const {
107 size_t intScaleFactor
= ceil(mContentsScaleFactor
);
108 return GetDevicePixelWidth(plane
) / intScaleFactor
;
111 size_t MacIOSurface::GetHeight(size_t plane
) const {
112 size_t intScaleFactor
= ceil(mContentsScaleFactor
);
113 return GetDevicePixelHeight(plane
) / intScaleFactor
;
116 size_t MacIOSurface::GetPlaneCount() const {
117 return ::IOSurfaceGetPlaneCount(mIOSurfaceRef
.get());
121 size_t MacIOSurface::GetMaxWidth() {
122 return ::IOSurfaceGetPropertyMaximum(kIOSurfaceWidth
);
126 size_t MacIOSurface::GetMaxHeight() {
127 return ::IOSurfaceGetPropertyMaximum(kIOSurfaceHeight
);
130 size_t MacIOSurface::GetDevicePixelWidth(size_t plane
) const {
131 return ::IOSurfaceGetWidthOfPlane(mIOSurfaceRef
.get(), plane
);
134 size_t MacIOSurface::GetDevicePixelHeight(size_t plane
) const {
135 return ::IOSurfaceGetHeightOfPlane(mIOSurfaceRef
.get(), plane
);
138 size_t MacIOSurface::GetBytesPerRow(size_t plane
) const {
139 return ::IOSurfaceGetBytesPerRowOfPlane(mIOSurfaceRef
.get(), plane
);
142 OSType
MacIOSurface::GetPixelFormat() const {
143 return ::IOSurfaceGetPixelFormat(mIOSurfaceRef
.get());
146 void MacIOSurface::IncrementUseCount() {
147 ::IOSurfaceIncrementUseCount(mIOSurfaceRef
.get());
150 void MacIOSurface::DecrementUseCount() {
151 ::IOSurfaceDecrementUseCount(mIOSurfaceRef
.get());
154 void MacIOSurface::Lock(bool aReadOnly
) {
155 MOZ_RELEASE_ASSERT(!mIsLocked
, "double MacIOSurface lock");
156 ::IOSurfaceLock(mIOSurfaceRef
.get(), aReadOnly
? kIOSurfaceLockReadOnly
: 0,
161 void MacIOSurface::Unlock(bool aReadOnly
) {
162 MOZ_RELEASE_ASSERT(mIsLocked
, "MacIOSurface unlock without being locked");
163 ::IOSurfaceUnlock(mIOSurfaceRef
.get(), aReadOnly
? kIOSurfaceLockReadOnly
: 0,
168 using mozilla::gfx::IntSize
;
169 using mozilla::gfx::SourceSurface
;
170 using mozilla::gfx::SurfaceFormat
;
172 static void MacIOSurfaceBufferDeallocator(void* aClosure
) {
173 MOZ_ASSERT(aClosure
);
175 delete[] static_cast<unsigned char*>(aClosure
);
178 already_AddRefed
<SourceSurface
> MacIOSurface::GetAsSurface() {
180 size_t bytesPerRow
= GetBytesPerRow();
181 size_t ioWidth
= GetDevicePixelWidth();
182 size_t ioHeight
= GetDevicePixelHeight();
184 unsigned char* ioData
= (unsigned char*)GetBaseAddress();
186 new unsigned char[bytesPerRow
* ioHeight
/ sizeof(unsigned char)];
187 for (size_t i
= 0; i
< ioHeight
; i
++) {
188 memcpy(dataCpy
+ i
* bytesPerRow
, ioData
+ i
* bytesPerRow
, ioWidth
* 4);
193 SurfaceFormat format
= HasAlpha() ? mozilla::gfx::SurfaceFormat::B8G8R8A8
194 : mozilla::gfx::SurfaceFormat::B8G8R8X8
;
196 RefPtr
<mozilla::gfx::DataSourceSurface
> surf
=
197 mozilla::gfx::Factory::CreateWrappingDataSourceSurface(
198 dataCpy
, bytesPerRow
, IntSize(ioWidth
, ioHeight
), format
,
199 &MacIOSurfaceBufferDeallocator
, static_cast<void*>(dataCpy
));
201 return surf
.forget();
204 already_AddRefed
<mozilla::gfx::DrawTarget
> MacIOSurface::GetAsDrawTargetLocked(
205 mozilla::gfx::BackendType aBackendType
) {
208 "Only call GetAsDrawTargetLocked while the surface is locked.");
210 size_t bytesPerRow
= GetBytesPerRow();
211 size_t ioWidth
= GetDevicePixelWidth();
212 size_t ioHeight
= GetDevicePixelHeight();
213 unsigned char* ioData
= (unsigned char*)GetBaseAddress();
214 SurfaceFormat format
= GetFormat();
215 return mozilla::gfx::Factory::CreateDrawTargetForData(
216 aBackendType
, ioData
, IntSize(ioWidth
, ioHeight
), bytesPerRow
, format
);
219 SurfaceFormat
MacIOSurface::GetFormat() const {
220 switch (GetPixelFormat()) {
221 case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
:
222 case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
:
223 return SurfaceFormat::NV12
;
224 case kCVPixelFormatType_422YpCbCr8
:
225 return SurfaceFormat::YUV422
;
226 case kCVPixelFormatType_32BGRA
:
227 return HasAlpha() ? SurfaceFormat::B8G8R8A8
: SurfaceFormat::B8G8R8X8
;
229 return HasAlpha() ? SurfaceFormat::R8G8B8A8
: SurfaceFormat::R8G8B8X8
;
233 SurfaceFormat
MacIOSurface::GetReadFormat() const {
234 SurfaceFormat format
= GetFormat();
235 if (format
== SurfaceFormat::YUV422
) {
236 return SurfaceFormat::R8G8B8X8
;
241 CGLError
MacIOSurface::CGLTexImageIOSurface2D(CGLContextObj ctx
, GLenum target
,
242 GLenum internalFormat
,
243 GLsizei width
, GLsizei height
,
244 GLenum format
, GLenum type
,
245 GLuint plane
) const {
246 return ::CGLTexImageIOSurface2D(ctx
, target
, internalFormat
, width
, height
,
247 format
, type
, mIOSurfaceRef
.get(), plane
);
250 CGLError
MacIOSurface::CGLTexImageIOSurface2D(
251 mozilla::gl::GLContext
* aGL
, CGLContextObj ctx
, size_t plane
,
252 mozilla::gfx::SurfaceFormat
* aOutReadFormat
) {
253 MOZ_ASSERT(plane
>= 0);
254 bool isCompatibilityProfile
= aGL
->IsCompatibilityProfile();
255 OSType pixelFormat
= GetPixelFormat();
257 GLenum internalFormat
;
260 if (pixelFormat
== kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
||
261 pixelFormat
== kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
) {
262 MOZ_ASSERT(GetPlaneCount() == 2);
263 MOZ_ASSERT(plane
< 2);
265 // The LOCAL_GL_LUMINANCE and LOCAL_GL_LUMINANCE_ALPHA are the deprecated
266 // format. So, use LOCAL_GL_RED and LOCAL_GL_RB if we use core profile.
267 // https://www.khronos.org/opengl/wiki/Image_Format#Legacy_Image_Formats
269 internalFormat
= format
=
270 (isCompatibilityProfile
) ? (LOCAL_GL_LUMINANCE
) : (LOCAL_GL_RED
);
272 internalFormat
= format
=
273 (isCompatibilityProfile
) ? (LOCAL_GL_LUMINANCE_ALPHA
) : (LOCAL_GL_RG
);
275 type
= LOCAL_GL_UNSIGNED_BYTE
;
276 if (aOutReadFormat
) {
277 *aOutReadFormat
= mozilla::gfx::SurfaceFormat::NV12
;
279 } else if (pixelFormat
== kCVPixelFormatType_422YpCbCr8
) {
280 MOZ_ASSERT(plane
== 0);
281 // The YCBCR_422_APPLE ext is only available in compatibility profile. So,
282 // we should use RGB_422_APPLE for core profile. The difference between
283 // YCBCR_422_APPLE and RGB_422_APPLE is that the YCBCR_422_APPLE converts
284 // the YCbCr value to RGB with REC 601 conversion. But the RGB_422_APPLE
285 // doesn't contain color conversion. You should do the color conversion by
286 // yourself for RGB_422_APPLE.
288 // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_ycbcr_422.txt
289 // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_rgb_422.txt
290 if (isCompatibilityProfile
) {
291 format
= LOCAL_GL_YCBCR_422_APPLE
;
292 if (aOutReadFormat
) {
293 *aOutReadFormat
= mozilla::gfx::SurfaceFormat::R8G8B8X8
;
296 format
= LOCAL_GL_RGB_422_APPLE
;
297 if (aOutReadFormat
) {
298 *aOutReadFormat
= mozilla::gfx::SurfaceFormat::YUV422
;
301 internalFormat
= LOCAL_GL_RGB
;
302 type
= LOCAL_GL_UNSIGNED_SHORT_8_8_APPLE
;
304 MOZ_ASSERT(plane
== 0);
306 internalFormat
= HasAlpha() ? LOCAL_GL_RGBA
: LOCAL_GL_RGB
;
307 format
= LOCAL_GL_BGRA
;
308 type
= LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV
;
309 if (aOutReadFormat
) {
310 *aOutReadFormat
= HasAlpha() ? mozilla::gfx::SurfaceFormat::R8G8B8A8
311 : mozilla::gfx::SurfaceFormat::R8G8B8X8
;
315 return CGLTexImageIOSurface2D(ctx
, LOCAL_GL_TEXTURE_RECTANGLE_ARB
,
316 internalFormat
, GetDevicePixelWidth(plane
),
317 GetDevicePixelHeight(plane
), format
, type
,