tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MacIOSurface.cpp (27711B)


      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/. */
      6 
      7 #include "MacIOSurface.h"
      8 #ifdef XP_MACOSX
      9 #  include <OpenGL/gl.h>
     10 #  include <OpenGL/CGLIOSurface.h>
     11 #endif
     12 #include <QuartzCore/QuartzCore.h>
     13 #include "GLConsts.h"
     14 #ifdef XP_MACOSX
     15 #  include "GLContextCGL.h"
     16 #else
     17 #  include "GLContextEAGL.h"
     18 #endif
     19 #include "gfxMacUtils.h"
     20 #include "nsPrintfCString.h"
     21 #include "mozilla/Assertions.h"
     22 #include "mozilla/RefPtr.h"
     23 #include "mozilla/gfx/Logging.h"
     24 #include "mozilla/StaticPrefs_gfx.h"
     25 
     26 using namespace mozilla;
     27 
     28 MacIOSurface::MacIOSurface(CFTypeRefPtr<IOSurfaceRef> aIOSurfaceRef,
     29                           bool aHasAlpha, gfx::YUVColorSpace aColorSpace)
     30    : mIOSurfaceRef(std::move(aIOSurfaceRef)),
     31      mHasAlpha(aHasAlpha),
     32      mColorSpace(aColorSpace) {
     33  IncrementUseCount();
     34 }
     35 
     36 MacIOSurface::~MacIOSurface() {
     37  MOZ_RELEASE_ASSERT(!IsLocked(), "Destroying locked surface");
     38  DecrementUseCount();
     39 }
     40 
     41 void AddDictionaryInt(const CFTypeRefPtr<CFMutableDictionaryRef>& aDict,
     42                      const void* aType, uint32_t aValue) {
     43  auto cfValue = CFTypeRefPtr<CFNumberRef>::WrapUnderCreateRule(
     44      ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aValue));
     45  ::CFDictionaryAddValue(aDict.get(), aType, cfValue.get());
     46 }
     47 
     48 void SetSizeProperties(const CFTypeRefPtr<CFMutableDictionaryRef>& aDict,
     49                       int aWidth, int aHeight, int aBytesPerPixel) {
     50  AddDictionaryInt(aDict, kIOSurfaceWidth, aWidth);
     51  AddDictionaryInt(aDict, kIOSurfaceHeight, aHeight);
     52  ::CFDictionaryAddValue(aDict.get(), kIOSurfaceIsGlobal, kCFBooleanTrue);
     53  AddDictionaryInt(aDict, kIOSurfaceBytesPerElement, aBytesPerPixel);
     54 
     55  size_t bytesPerRow =
     56      IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, aWidth * aBytesPerPixel);
     57  AddDictionaryInt(aDict, kIOSurfaceBytesPerRow, bytesPerRow);
     58 
     59  // Add a SIMD register worth of extra bytes to the end of the allocation for
     60  // SWGL.
     61  size_t totalBytes =
     62      IOSurfaceAlignProperty(kIOSurfaceAllocSize, aHeight * bytesPerRow + 16);
     63  AddDictionaryInt(aDict, kIOSurfaceAllocSize, totalBytes);
     64 }
     65 
     66 /* static */
     67 already_AddRefed<MacIOSurface> MacIOSurface::CreateIOSurface(int aWidth,
     68                                                             int aHeight,
     69                                                             bool aHasAlpha) {
     70  auto props = CFTypeRefPtr<CFMutableDictionaryRef>::WrapUnderCreateRule(
     71      ::CFDictionaryCreateMutable(kCFAllocatorDefault, 4,
     72                                  &kCFTypeDictionaryKeyCallBacks,
     73                                  &kCFTypeDictionaryValueCallBacks));
     74  if (!props) return nullptr;
     75 
     76  MOZ_ASSERT((size_t)aWidth <= GetMaxWidth());
     77  MOZ_ASSERT((size_t)aHeight <= GetMaxHeight());
     78 
     79  int32_t bytesPerElem = 4;
     80  SetSizeProperties(props, aWidth, aHeight, bytesPerElem);
     81 
     82  AddDictionaryInt(props, kIOSurfacePixelFormat,
     83                   (uint32_t)kCVPixelFormatType_32BGRA);
     84 
     85  CFTypeRefPtr<IOSurfaceRef> surfaceRef =
     86      CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(
     87          ::IOSurfaceCreate(props.get()));
     88 
     89  if (StaticPrefs::gfx_color_management_native_srgb()) {
     90    IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceColorSpace"),
     91                      kCGColorSpaceSRGB);
     92  }
     93 
     94  if (!surfaceRef) {
     95    return nullptr;
     96  }
     97 
     98  RefPtr<MacIOSurface> ioSurface =
     99      new MacIOSurface(std::move(surfaceRef), aHasAlpha);
    100 
    101  return ioSurface.forget();
    102 }
    103 
    104 size_t CreatePlaneDictionary(CFTypeRefPtr<CFMutableDictionaryRef>& aDict,
    105                             const gfx::IntSize& aSize, size_t aOffset,
    106                             size_t aBytesPerPixel) {
    107  size_t bytesPerRow = IOSurfaceAlignProperty(kIOSurfacePlaneBytesPerRow,
    108                                              aSize.width * aBytesPerPixel);
    109  // Add a SIMD register worth of extra bytes to the end of the allocation for
    110  // SWGL.
    111  size_t totalBytes = IOSurfaceAlignProperty(kIOSurfacePlaneSize,
    112                                             aSize.height * bytesPerRow + 16);
    113 
    114  aDict = CFTypeRefPtr<CFMutableDictionaryRef>::WrapUnderCreateRule(
    115      ::CFDictionaryCreateMutable(kCFAllocatorDefault, 4,
    116                                  &kCFTypeDictionaryKeyCallBacks,
    117                                  &kCFTypeDictionaryValueCallBacks));
    118 
    119  AddDictionaryInt(aDict, kIOSurfacePlaneWidth, aSize.width);
    120  AddDictionaryInt(aDict, kIOSurfacePlaneHeight, aSize.height);
    121  AddDictionaryInt(aDict, kIOSurfacePlaneBytesPerRow, bytesPerRow);
    122  AddDictionaryInt(aDict, kIOSurfacePlaneOffset, aOffset);
    123  AddDictionaryInt(aDict, kIOSurfacePlaneSize, totalBytes);
    124  AddDictionaryInt(aDict, kIOSurfacePlaneBytesPerElement, aBytesPerPixel);
    125 
    126  return totalBytes;
    127 }
    128 
    129 // Helper function to set common color IOSurface properties.
    130 static void SetIOSurfaceCommonProperties(
    131    CFTypeRefPtr<IOSurfaceRef> surfaceRef,
    132    MacIOSurface::YUVColorSpace aColorSpace,
    133    MacIOSurface::TransferFunction aTransferFunction) {
    134  // Setup the correct YCbCr conversion matrix, color primaries, and transfer
    135  // functions on the IOSurface, in case we pass this directly to CoreAnimation.
    136  // For keys and values, we'd like to use values specified by the API, but
    137  // those are only defined for CVImageBuffers. Luckily, when an image buffer is
    138  // converted into an IOSurface, the keys are transformed but the values are
    139  // the same. Since we are creating the IOSurface directly, we use hard-coded
    140  // keys derived from inspecting the extracted IOSurfaces in the copying case,
    141  // but we use the API-defined values from CVImageBuffer.
    142  if (aColorSpace == MacIOSurface::YUVColorSpace::BT601) {
    143    IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceYCbCrMatrix"),
    144                      kCVImageBufferYCbCrMatrix_ITU_R_601_4);
    145  } else if (aColorSpace == MacIOSurface::YUVColorSpace::BT709) {
    146    IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceYCbCrMatrix"),
    147                      kCVImageBufferYCbCrMatrix_ITU_R_709_2);
    148    IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceColorPrimaries"),
    149                      kCVImageBufferColorPrimaries_ITU_R_709_2);
    150  } else {
    151    IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceYCbCrMatrix"),
    152                      kCVImageBufferYCbCrMatrix_ITU_R_2020);
    153    IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceColorPrimaries"),
    154                      kCVImageBufferColorPrimaries_ITU_R_2020);
    155  }
    156 
    157  // Transfer function is applied independently from the colorSpace.
    158  IOSurfaceSetValue(
    159      surfaceRef.get(), CFSTR("IOSurfaceTransferFunction"),
    160      gfxMacUtils::CFStringForTransferFunction(aTransferFunction));
    161 
    162 #ifdef XP_MACOSX
    163  // Override the color space to be the same as the main display, so that
    164  // CoreAnimation won't try to do any color correction (from the IOSurface
    165  // space, to the display). In the future we may want to try specifying this
    166  // correctly, but probably only once we do the same for videos drawn through
    167  // our gfx code.
    168  auto colorSpace = CFTypeRefPtr<CGColorSpaceRef>::WrapUnderCreateRule(
    169      CGDisplayCopyColorSpace(CGMainDisplayID()));
    170  auto colorData = CFTypeRefPtr<CFDataRef>::WrapUnderCreateRule(
    171      CGColorSpaceCopyICCData(colorSpace.get()));
    172  IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceColorSpace"),
    173                    colorData.get());
    174 #endif
    175 }
    176 
    177 /* static */
    178 already_AddRefed<MacIOSurface> MacIOSurface::CreateBiPlanarSurface(
    179    const IntSize& aYSize, const IntSize& aCbCrSize,
    180    ChromaSubsampling aChromaSubsampling, YUVColorSpace aColorSpace,
    181    TransferFunction aTransferFunction, ColorRange aColorRange,
    182    ColorDepth aColorDepth) {
    183  MOZ_ASSERT(aColorSpace == YUVColorSpace::BT601 ||
    184             aColorSpace == YUVColorSpace::BT709 ||
    185             aColorSpace == YUVColorSpace::BT2020);
    186  MOZ_ASSERT(aColorRange == ColorRange::LIMITED ||
    187             aColorRange == ColorRange::FULL);
    188  MOZ_ASSERT(aColorDepth == ColorDepth::COLOR_8 ||
    189             aColorDepth == ColorDepth::COLOR_10);
    190 
    191  auto props = CFTypeRefPtr<CFMutableDictionaryRef>::WrapUnderCreateRule(
    192      ::CFDictionaryCreateMutable(kCFAllocatorDefault, 4,
    193                                  &kCFTypeDictionaryKeyCallBacks,
    194                                  &kCFTypeDictionaryValueCallBacks));
    195  if (!props) return nullptr;
    196 
    197  MOZ_ASSERT((size_t)aYSize.width <= GetMaxWidth());
    198  MOZ_ASSERT((size_t)aYSize.height <= GetMaxHeight());
    199 
    200  AddDictionaryInt(props, kIOSurfaceWidth, aYSize.width);
    201  AddDictionaryInt(props, kIOSurfaceHeight, aYSize.height);
    202  ::CFDictionaryAddValue(props.get(), kIOSurfaceIsGlobal, kCFBooleanTrue);
    203 
    204  if (aChromaSubsampling == ChromaSubsampling::HALF_WIDTH_AND_HEIGHT) {
    205    // 4:2:0 subsampling.
    206    if (aColorDepth == ColorDepth::COLOR_8) {
    207      if (aColorRange == ColorRange::LIMITED) {
    208        AddDictionaryInt(
    209            props, kIOSurfacePixelFormat,
    210            (uint32_t)kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange);
    211      } else {
    212        AddDictionaryInt(
    213            props, kIOSurfacePixelFormat,
    214            (uint32_t)kCVPixelFormatType_420YpCbCr8BiPlanarFullRange);
    215      }
    216    } else {
    217      if (aColorRange == ColorRange::LIMITED) {
    218        AddDictionaryInt(
    219            props, kIOSurfacePixelFormat,
    220            (uint32_t)kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange);
    221      } else {
    222        AddDictionaryInt(
    223            props, kIOSurfacePixelFormat,
    224            (uint32_t)kCVPixelFormatType_420YpCbCr10BiPlanarFullRange);
    225      }
    226    }
    227  } else {
    228    // 4:2:2 subsampling. We can only handle 10-bit color.
    229    MOZ_ASSERT(aColorDepth == ColorDepth::COLOR_10,
    230               "macOS bi-planar 4:2:2 formats must be 10-bit color.");
    231    if (aColorRange == ColorRange::LIMITED) {
    232      AddDictionaryInt(
    233          props, kIOSurfacePixelFormat,
    234          (uint32_t)kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange);
    235    } else {
    236      AddDictionaryInt(
    237          props, kIOSurfacePixelFormat,
    238          (uint32_t)kCVPixelFormatType_422YpCbCr10BiPlanarFullRange);
    239    }
    240  }
    241 
    242  size_t bytesPerPixel = (aColorDepth == ColorDepth::COLOR_8) ? 1 : 2;
    243 
    244  CFTypeRefPtr<CFMutableDictionaryRef> planeProps[2];
    245  size_t yPlaneBytes =
    246      CreatePlaneDictionary(planeProps[0], aYSize, 0, bytesPerPixel);
    247  size_t cbCrOffset =
    248      IOSurfaceAlignProperty(kIOSurfacePlaneOffset, yPlaneBytes);
    249  size_t cbCrPlaneBytes = CreatePlaneDictionary(planeProps[1], aCbCrSize,
    250                                                cbCrOffset, bytesPerPixel * 2);
    251  size_t totalBytes =
    252      IOSurfaceAlignProperty(kIOSurfaceAllocSize, cbCrOffset + cbCrPlaneBytes);
    253 
    254  AddDictionaryInt(props, kIOSurfaceAllocSize, totalBytes);
    255 
    256  auto array = CFTypeRefPtr<CFArrayRef>::WrapUnderCreateRule(
    257      CFArrayCreate(kCFAllocatorDefault, (const void**)planeProps, 2,
    258                    &kCFTypeArrayCallBacks));
    259  ::CFDictionaryAddValue(props.get(), kIOSurfacePlaneInfo, array.get());
    260 
    261  CFTypeRefPtr<IOSurfaceRef> surfaceRef =
    262      CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(
    263          ::IOSurfaceCreate(props.get()));
    264 
    265  if (!surfaceRef) {
    266    return nullptr;
    267  }
    268 
    269  SetIOSurfaceCommonProperties(surfaceRef, aColorSpace, aTransferFunction);
    270 
    271  RefPtr<MacIOSurface> ioSurface =
    272      new MacIOSurface(std::move(surfaceRef), false, aColorSpace);
    273 
    274  return ioSurface.forget();
    275 }
    276 
    277 /* static */
    278 already_AddRefed<MacIOSurface> MacIOSurface::CreateSinglePlanarSurface(
    279    const IntSize& aSize, YUVColorSpace aColorSpace,
    280    TransferFunction aTransferFunction, ColorRange aColorRange) {
    281  MOZ_ASSERT(aColorSpace == YUVColorSpace::BT601 ||
    282             aColorSpace == YUVColorSpace::BT709);
    283  MOZ_ASSERT(aColorRange == ColorRange::LIMITED ||
    284             aColorRange == ColorRange::FULL);
    285 
    286  auto props = CFTypeRefPtr<CFMutableDictionaryRef>::WrapUnderCreateRule(
    287      ::CFDictionaryCreateMutable(kCFAllocatorDefault, 4,
    288                                  &kCFTypeDictionaryKeyCallBacks,
    289                                  &kCFTypeDictionaryValueCallBacks));
    290  if (!props) return nullptr;
    291 
    292  MOZ_ASSERT((size_t)aSize.width <= GetMaxWidth());
    293  MOZ_ASSERT((size_t)aSize.height <= GetMaxHeight());
    294 
    295  SetSizeProperties(props, aSize.width, aSize.height, 2);
    296 
    297  if (aColorRange == ColorRange::LIMITED) {
    298    AddDictionaryInt(props, kIOSurfacePixelFormat,
    299                     (uint32_t)kCVPixelFormatType_422YpCbCr8_yuvs);
    300  } else {
    301    AddDictionaryInt(props, kIOSurfacePixelFormat,
    302                     (uint32_t)kCVPixelFormatType_422YpCbCr8FullRange);
    303  }
    304 
    305  CFTypeRefPtr<IOSurfaceRef> surfaceRef =
    306      CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(
    307          ::IOSurfaceCreate(props.get()));
    308 
    309  if (!surfaceRef) {
    310    return nullptr;
    311  }
    312 
    313  SetIOSurfaceCommonProperties(surfaceRef, aColorSpace, aTransferFunction);
    314 
    315  RefPtr<MacIOSurface> ioSurface =
    316      new MacIOSurface(std::move(surfaceRef), false, aColorSpace);
    317 
    318  return ioSurface.forget();
    319 }
    320 
    321 /* static */
    322 already_AddRefed<MacIOSurface> MacIOSurface::LookupSurface(
    323    IOSurfaceID aIOSurfaceID, bool aHasAlpha, gfx::YUVColorSpace aColorSpace) {
    324  CFTypeRefPtr<IOSurfaceRef> surfaceRef =
    325      CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(
    326          ::IOSurfaceLookup(aIOSurfaceID));
    327  if (!surfaceRef) return nullptr;
    328 
    329  RefPtr<MacIOSurface> ioSurface =
    330      new MacIOSurface(std::move(surfaceRef), aHasAlpha, aColorSpace);
    331 
    332  return ioSurface.forget();
    333 }
    334 
    335 /* static */
    336 mozilla::gfx::SurfaceFormat MacIOSurface::SurfaceFormatForPixelFormat(
    337    OSType aPixelFormat, bool aHasAlpha) {
    338  switch (aPixelFormat) {
    339    case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
    340    case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
    341      return mozilla::gfx::SurfaceFormat::NV12;
    342    case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
    343    case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange:
    344      return mozilla::gfx::SurfaceFormat::P010;
    345    case kCVPixelFormatType_422YpCbCr8_yuvs:
    346    case kCVPixelFormatType_422YpCbCr8FullRange:
    347      return mozilla::gfx::SurfaceFormat::YUY2;
    348    case kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange:
    349    case kCVPixelFormatType_422YpCbCr10BiPlanarFullRange:
    350      return mozilla::gfx::SurfaceFormat::NV16;
    351    case kCVPixelFormatType_32BGRA:
    352      return aHasAlpha ? mozilla::gfx::SurfaceFormat::B8G8R8A8
    353                       : mozilla::gfx::SurfaceFormat::B8G8R8X8;
    354    default:
    355      MOZ_ASSERT_UNREACHABLE("Unknown format");
    356      return mozilla::gfx::SurfaceFormat::B8G8R8A8;
    357  }
    358 }
    359 
    360 IOSurfaceID MacIOSurface::GetIOSurfaceID() const {
    361  return ::IOSurfaceGetID(mIOSurfaceRef.get());
    362 }
    363 
    364 void* MacIOSurface::GetBaseAddress() const {
    365  return ::IOSurfaceGetBaseAddress(mIOSurfaceRef.get());
    366 }
    367 
    368 void* MacIOSurface::GetBaseAddressOfPlane(size_t aPlaneIndex) const {
    369  return ::IOSurfaceGetBaseAddressOfPlane(mIOSurfaceRef.get(), aPlaneIndex);
    370 }
    371 
    372 size_t MacIOSurface::GetWidth(size_t plane) const {
    373  return GetDevicePixelWidth(plane);
    374 }
    375 
    376 size_t MacIOSurface::GetHeight(size_t plane) const {
    377  return GetDevicePixelHeight(plane);
    378 }
    379 
    380 size_t MacIOSurface::GetPlaneCount() const {
    381  return ::IOSurfaceGetPlaneCount(mIOSurfaceRef.get());
    382 }
    383 
    384 /*static*/
    385 size_t MacIOSurface::GetMaxWidth() {
    386  return ::IOSurfaceGetPropertyMaximum(kIOSurfaceWidth);
    387 }
    388 
    389 /*static*/
    390 size_t MacIOSurface::GetMaxHeight() {
    391  return ::IOSurfaceGetPropertyMaximum(kIOSurfaceHeight);
    392 }
    393 
    394 size_t MacIOSurface::GetDevicePixelWidth(size_t plane) const {
    395  return ::IOSurfaceGetWidthOfPlane(mIOSurfaceRef.get(), plane);
    396 }
    397 
    398 size_t MacIOSurface::GetDevicePixelHeight(size_t plane) const {
    399  return ::IOSurfaceGetHeightOfPlane(mIOSurfaceRef.get(), plane);
    400 }
    401 
    402 size_t MacIOSurface::GetBytesPerRow(size_t plane) const {
    403  return ::IOSurfaceGetBytesPerRowOfPlane(mIOSurfaceRef.get(), plane);
    404 }
    405 
    406 size_t MacIOSurface::GetAllocSize() const {
    407  return ::IOSurfaceGetAllocSize(mIOSurfaceRef.get());
    408 }
    409 
    410 OSType MacIOSurface::GetPixelFormat() const {
    411  return ::IOSurfaceGetPixelFormat(mIOSurfaceRef.get());
    412 }
    413 
    414 void MacIOSurface::IncrementUseCount() {
    415  ::IOSurfaceIncrementUseCount(mIOSurfaceRef.get());
    416 }
    417 
    418 void MacIOSurface::DecrementUseCount() {
    419  ::IOSurfaceDecrementUseCount(mIOSurfaceRef.get());
    420 }
    421 
    422 bool MacIOSurface::Lock(bool aReadOnly) {
    423  MOZ_RELEASE_ASSERT(!mIsLocked, "double MacIOSurface lock");
    424  kern_return_t rv = ::IOSurfaceLock(
    425      mIOSurfaceRef.get(), aReadOnly ? kIOSurfaceLockReadOnly : 0, nullptr);
    426  if (NS_WARN_IF(rv != KERN_SUCCESS)) {
    427    gfxCriticalNoteOnce << "MacIOSurface::Lock failed " << gfx::hexa(rv);
    428    return false;
    429  }
    430  mIsLocked = true;
    431  return true;
    432 }
    433 
    434 void MacIOSurface::Unlock(bool aReadOnly) {
    435  MOZ_RELEASE_ASSERT(mIsLocked, "MacIOSurface unlock without being locked");
    436  ::IOSurfaceUnlock(mIOSurfaceRef.get(), aReadOnly ? kIOSurfaceLockReadOnly : 0,
    437                    nullptr);
    438  mIsLocked = false;
    439 }
    440 
    441 using mozilla::gfx::ColorDepth;
    442 using mozilla::gfx::IntSize;
    443 using mozilla::gfx::SourceSurface;
    444 using mozilla::gfx::SurfaceFormat;
    445 
    446 static void MacIOSurfaceBufferDeallocator(void* aClosure) {
    447  MOZ_ASSERT(aClosure);
    448 
    449  delete[] static_cast<unsigned char*>(aClosure);
    450 }
    451 
    452 already_AddRefed<SourceSurface> MacIOSurface::GetAsSurface() {
    453  if (NS_WARN_IF(!Lock())) {
    454    return nullptr;
    455  }
    456 
    457  size_t bytesPerRow = GetBytesPerRow();
    458  size_t ioWidth = GetDevicePixelWidth();
    459  size_t ioHeight = GetDevicePixelHeight();
    460 
    461  unsigned char* ioData = (unsigned char*)GetBaseAddress();
    462  auto* dataCpy = new (
    463      fallible) unsigned char[bytesPerRow * ioHeight / sizeof(unsigned char)];
    464  if (NS_WARN_IF(!dataCpy)) {
    465    Unlock();
    466    return nullptr;
    467  }
    468 
    469  for (size_t i = 0; i < ioHeight; i++) {
    470    memcpy(dataCpy + i * bytesPerRow, ioData + i * bytesPerRow, ioWidth * 4);
    471  }
    472 
    473  Unlock();
    474 
    475  SurfaceFormat format = HasAlpha() ? mozilla::gfx::SurfaceFormat::B8G8R8A8
    476                                    : mozilla::gfx::SurfaceFormat::B8G8R8X8;
    477 
    478  RefPtr<mozilla::gfx::DataSourceSurface> surf =
    479      mozilla::gfx::Factory::CreateWrappingDataSourceSurface(
    480          dataCpy, bytesPerRow, IntSize(ioWidth, ioHeight), format,
    481          &MacIOSurfaceBufferDeallocator, static_cast<void*>(dataCpy));
    482 
    483  return surf.forget();
    484 }
    485 
    486 already_AddRefed<mozilla::gfx::DrawTarget> MacIOSurface::GetAsDrawTargetLocked(
    487    mozilla::gfx::BackendType aBackendType) {
    488  MOZ_RELEASE_ASSERT(
    489      IsLocked(),
    490      "Only call GetAsDrawTargetLocked while the surface is locked.");
    491 
    492  size_t bytesPerRow = GetBytesPerRow();
    493  size_t ioWidth = GetDevicePixelWidth();
    494  size_t ioHeight = GetDevicePixelHeight();
    495  unsigned char* ioData = (unsigned char*)GetBaseAddress();
    496  SurfaceFormat format = GetFormat();
    497  return mozilla::gfx::Factory::CreateDrawTargetForData(
    498      aBackendType, ioData, IntSize(ioWidth, ioHeight), bytesPerRow, format);
    499 }
    500 
    501 SurfaceFormat MacIOSurface::GetFormat() const {
    502  return SurfaceFormatForPixelFormat(GetPixelFormat(), HasAlpha());
    503 }
    504 
    505 SurfaceFormat MacIOSurface::GetReadFormat() const {
    506  SurfaceFormat format = GetFormat();
    507  if (format == SurfaceFormat::YUY2) {
    508    return SurfaceFormat::R8G8B8X8;
    509  }
    510  return format;
    511 }
    512 
    513 ColorDepth MacIOSurface::GetColorDepth() const {
    514  switch (GetPixelFormat()) {
    515    case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
    516    case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange:
    517    case kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange:
    518    case kCVPixelFormatType_422YpCbCr10BiPlanarFullRange:
    519      return ColorDepth::COLOR_10;
    520    default:
    521      return ColorDepth::COLOR_8;
    522  }
    523 }
    524 
    525 #ifdef DEBUG
    526 /* static */ Maybe<OSType> MacIOSurface::ChoosePixelFormat(
    527    ChromaSubsampling aChromaSubsampling, ColorRange aColorRange,
    528    ColorDepth aColorDepth) {
    529  switch (aChromaSubsampling) {
    530    case ChromaSubsampling::FULL:
    531      if (aColorDepth == ColorDepth::COLOR_10) {
    532        switch (aColorRange) {
    533          case ColorRange::LIMITED:
    534            return Some(kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange);
    535          case ColorRange::FULL:
    536            return Some(kCVPixelFormatType_422YpCbCr10BiPlanarFullRange);
    537        }
    538      }
    539      break;
    540    case ChromaSubsampling::HALF_WIDTH:
    541      switch (aColorDepth) {
    542        case ColorDepth::COLOR_8:
    543          switch (aColorRange) {
    544            case ColorRange::LIMITED:
    545              return Some(kCVPixelFormatType_422YpCbCr8_yuvs);
    546            case ColorRange::FULL:
    547              return Some(kCVPixelFormatType_422YpCbCr8FullRange);
    548          }
    549          break;
    550        case ColorDepth::COLOR_10:
    551          switch (aColorRange) {
    552            case ColorRange::LIMITED:
    553              return Some(kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange);
    554            case ColorRange::FULL:
    555              return Some(kCVPixelFormatType_422YpCbCr10BiPlanarFullRange);
    556          }
    557          break;
    558        default:
    559          break;
    560      }
    561      break;
    562    case ChromaSubsampling::HALF_WIDTH_AND_HEIGHT:
    563      switch (aColorDepth) {
    564        case ColorDepth::COLOR_8:
    565          switch (aColorRange) {
    566            case ColorRange::LIMITED:
    567              return Some(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange);
    568            case ColorRange::FULL:
    569              return Some(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange);
    570          }
    571          break;
    572        case ColorDepth::COLOR_10:
    573        case ColorDepth::COLOR_12:
    574        case ColorDepth::COLOR_16:
    575          switch (aColorRange) {
    576            case ColorRange::LIMITED:
    577              return Some(kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange);
    578            case ColorRange::FULL:
    579              return Some(kCVPixelFormatType_420YpCbCr10BiPlanarFullRange);
    580          }
    581          break;
    582      }
    583      break;
    584  }
    585  return Nothing();
    586 }
    587 #endif
    588 
    589 bool MacIOSurface::BindTexImage(mozilla::gl::GLContext* aGL, size_t aPlane,
    590                                mozilla::gfx::SurfaceFormat* aOutReadFormat) {
    591 #ifdef XP_MACOSX
    592  MOZ_ASSERT(aPlane >= 0);
    593  bool isCompatibilityProfile = aGL->IsCompatibilityProfile();
    594  OSType pixelFormat = GetPixelFormat();
    595 
    596  GLenum internalFormat;
    597  GLenum format;
    598  GLenum type;
    599  if (pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange ||
    600      pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
    601    MOZ_ASSERT(GetPlaneCount() == 2);
    602    MOZ_ASSERT(aPlane < 2);
    603 
    604    // The LOCAL_GL_LUMINANCE and LOCAL_GL_LUMINANCE_ALPHA are the deprecated
    605    // format. So, use LOCAL_GL_RED and LOCAL_GL_RB if we use core profile.
    606    // https://www.khronos.org/opengl/wiki/Image_Format#Legacy_Image_Formats
    607    if (aPlane == 0) {
    608      internalFormat = format =
    609          (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE) : (LOCAL_GL_RED);
    610    } else {
    611      internalFormat = format =
    612          (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE_ALPHA) : (LOCAL_GL_RG);
    613    }
    614    type = LOCAL_GL_UNSIGNED_BYTE;
    615    if (aOutReadFormat) {
    616      *aOutReadFormat = mozilla::gfx::SurfaceFormat::NV12;
    617    }
    618  } else if (pixelFormat == kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange ||
    619             pixelFormat == kCVPixelFormatType_420YpCbCr10BiPlanarFullRange) {
    620    MOZ_ASSERT(GetPlaneCount() == 2);
    621    MOZ_ASSERT(aPlane < 2);
    622 
    623    // The LOCAL_GL_LUMINANCE and LOCAL_GL_LUMINANCE_ALPHA are the deprecated
    624    // format. So, use LOCAL_GL_RED and LOCAL_GL_RB if we use core profile.
    625    // https://www.khronos.org/opengl/wiki/Image_Format#Legacy_Image_Formats
    626    if (aPlane == 0) {
    627      internalFormat = format =
    628          (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE) : (LOCAL_GL_RED);
    629    } else {
    630      internalFormat = format =
    631          (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE_ALPHA) : (LOCAL_GL_RG);
    632    }
    633    type = LOCAL_GL_UNSIGNED_SHORT;
    634    if (aOutReadFormat) {
    635      *aOutReadFormat = mozilla::gfx::SurfaceFormat::P010;
    636    }
    637  } else if (pixelFormat == kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange ||
    638             pixelFormat == kCVPixelFormatType_422YpCbCr10BiPlanarFullRange) {
    639    MOZ_ASSERT(GetPlaneCount() == 2);
    640    MOZ_ASSERT(aPlane < 2);
    641 
    642    // The LOCAL_GL_LUMINANCE and LOCAL_GL_LUMINANCE_ALPHA are the deprecated
    643    // format. So, use LOCAL_GL_RED and LOCAL_GL_RB if we use core profile.
    644    // https://www.khronos.org/opengl/wiki/Image_Format#Legacy_Image_Formats
    645    if (aPlane == 0) {
    646      internalFormat = format =
    647          (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE) : (LOCAL_GL_RED);
    648    } else {
    649      internalFormat = format =
    650          (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE_ALPHA) : (LOCAL_GL_RG);
    651    }
    652    type = LOCAL_GL_UNSIGNED_SHORT;
    653    if (aOutReadFormat) {
    654      *aOutReadFormat = mozilla::gfx::SurfaceFormat::NV16;
    655    }
    656  } else if (pixelFormat == kCVPixelFormatType_422YpCbCr8_yuvs ||
    657             pixelFormat == kCVPixelFormatType_422YpCbCr8FullRange) {
    658    MOZ_ASSERT(aPlane == 0);
    659    // The YCBCR_422_APPLE ext is only available in compatibility profile. So,
    660    // we should use RGB_422_APPLE for core profile. The difference between
    661    // YCBCR_422_APPLE and RGB_422_APPLE is that the YCBCR_422_APPLE converts
    662    // the YCbCr value to RGB with REC 601 conversion. But the RGB_422_APPLE
    663    // doesn't contain color conversion. You should do the color conversion by
    664    // yourself for RGB_422_APPLE.
    665    //
    666    // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_ycbcr_422.txt
    667    // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_rgb_422.txt
    668    if (isCompatibilityProfile) {
    669      format = LOCAL_GL_YCBCR_422_APPLE;
    670      if (aOutReadFormat) {
    671        *aOutReadFormat = mozilla::gfx::SurfaceFormat::R8G8B8X8;
    672      }
    673    } else {
    674      format = LOCAL_GL_RGB_422_APPLE;
    675      if (aOutReadFormat) {
    676        *aOutReadFormat = mozilla::gfx::SurfaceFormat::YUY2;
    677      }
    678    }
    679    internalFormat = LOCAL_GL_RGB;
    680    type = LOCAL_GL_UNSIGNED_SHORT_8_8_REV_APPLE;
    681  } else {
    682    MOZ_ASSERT(aPlane == 0);
    683 
    684    internalFormat = HasAlpha() ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
    685    format = LOCAL_GL_BGRA;
    686    type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
    687    if (aOutReadFormat) {
    688      *aOutReadFormat = HasAlpha() ? mozilla::gfx::SurfaceFormat::R8G8B8A8
    689                                   : mozilla::gfx::SurfaceFormat::R8G8B8X8;
    690    }
    691  }
    692 
    693  size_t width = GetDevicePixelWidth(aPlane);
    694  size_t height = GetDevicePixelHeight(aPlane);
    695 
    696  auto err = ::CGLTexImageIOSurface2D(
    697      gl::GLContextCGL::Cast(aGL)->GetCGLContext(),
    698      LOCAL_GL_TEXTURE_RECTANGLE_ARB, internalFormat, width, height, format,
    699      type, mIOSurfaceRef.get(), aPlane);
    700  if (err) {
    701    const auto formatChars = (const char*)&pixelFormat;
    702    const char formatStr[] = {formatChars[3], formatChars[2], formatChars[1],
    703                              formatChars[0], 0};
    704    const nsPrintfCString errStr(
    705        "CGLTexImageIOSurface2D(context, target, 0x%04x,"
    706        " %u, %u, 0x%04x, 0x%04x, iosurfPtr, %u) -> %i",
    707        internalFormat, uint32_t(width), uint32_t(height), format, type,
    708        (unsigned int)aPlane, err);
    709    gfxCriticalError() << errStr.get() << " (iosurf format: " << formatStr
    710                       << ")";
    711  }
    712  return !err;
    713 #else
    714  MOZ_CRASH("unimplemented");
    715 #endif
    716 }
    717 
    718 void MacIOSurface::SetColorSpace(const mozilla::gfx::ColorSpace2 cs) const {
    719  Maybe<CFStringRef> str;
    720  switch (cs) {
    721    case gfx::ColorSpace2::UNKNOWN:
    722      break;
    723    case gfx::ColorSpace2::SRGB:
    724      str = Some(kCGColorSpaceSRGB);
    725      break;
    726    case gfx::ColorSpace2::DISPLAY_P3:
    727      str = Some(kCGColorSpaceDisplayP3);
    728      break;
    729    case gfx::ColorSpace2::BT601_525:  // Doesn't really have a better option.
    730    case gfx::ColorSpace2::BT709:
    731      str = Some(kCGColorSpaceITUR_709);
    732      break;
    733    case gfx::ColorSpace2::BT2020:
    734      str = Some(kCGColorSpaceITUR_2020);
    735      break;
    736  }
    737  if (str) {
    738    IOSurfaceSetValue(mIOSurfaceRef.get(), CFSTR("IOSurfaceColorSpace"), *str);
    739  }
    740 }