/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "MacIOSurfaceHelpers.h" #include "MacIOSurfaceImage.h" #include "gfxPlatform.h" #include "mozilla/layers/CompositableClient.h" #include "mozilla/layers/CompositableForwarder.h" #include "mozilla/layers/MacIOSurfaceTextureClientOGL.h" #include "mozilla/layers/TextureForwarder.h" #include "mozilla/StaticPrefs_layers.h" #include "mozilla/UniquePtr.h" #include "YCbCrUtils.h" using namespace mozilla::layers; using namespace mozilla::gfx; TextureClient* MacIOSurfaceImage::GetTextureClient( KnowsCompositor* aKnowsCompositor) { if (!mTextureClient) { BackendType backend = BackendType::NONE; mTextureClient = TextureClient::CreateWithData( MacIOSurfaceTextureData::Create(mSurface, backend), TextureFlags::DEFAULT, aKnowsCompositor->GetTextureForwarder()); } return mTextureClient; } already_AddRefed MacIOSurfaceImage::GetAsSourceSurface() { return CreateSourceSurfaceFromMacIOSurface(mSurface); } bool MacIOSurfaceImage::SetData(ImageContainer* aContainer, const PlanarYCbCrData& aData) { MOZ_ASSERT(!mSurface); if (aData.mYSkip != 0 || aData.mCbSkip != 0 || aData.mCrSkip != 0 || !(aData.mYUVColorSpace == YUVColorSpace::BT601 || aData.mYUVColorSpace == YUVColorSpace::BT709) || !(aData.mColorRange == ColorRange::FULL || aData.mColorRange == ColorRange::LIMITED) || aData.mColorDepth != ColorDepth::COLOR_8) { return false; } if (aData.mCbCrSize.width * 2 != aData.mYSize.width) { return false; } // We can only support 4:2:2 and 4:2:0 formats currently. if (aData.mCbCrSize.height != aData.mYSize.height && aData.mCbCrSize.height * 2 != aData.mYSize.height) { return false; } RefPtr allocator = aContainer->GetMacIOSurfaceRecycleAllocator(); RefPtr surf = allocator->Allocate( aData.mYSize, aData.mCbCrSize, aData.mYUVColorSpace, aData.mColorRange); surf->Lock(false); if (surf->GetFormat() == SurfaceFormat::YUV422) { // If the CbCrSize's height is half of the YSize's height, then we'll // need to duplicate the CbCr data on every second row. size_t heightScale = aData.mYSize.height / aData.mCbCrSize.height; // The underlying IOSurface has format // kCVPixelFormatType_422YpCbCr8FullRange or // kCVPixelFormatType_422YpCbCr8_yuvs, which uses a 4:2:2 Y`0 Cb Y`1 Cr // layout. See CVPixelBuffer.h for the full list of format descriptions. MOZ_ASSERT(aData.mYSize.height > 0); uint8_t* dst = (uint8_t*)surf->GetBaseAddressOfPlane(0); size_t stride = surf->GetBytesPerRow(0); for (size_t i = 0; i < (size_t)aData.mYSize.height; i++) { // Compute the row addresses. If the input was 4:2:0, then // we divide i by 2, so that each source row of CbCr maps to // two dest rows. uint8_t* rowYSrc = aData.mYChannel + aData.mYStride * i; uint8_t* rowCbSrc = aData.mCbChannel + aData.mCbCrStride * (i / heightScale); uint8_t* rowCrSrc = aData.mCrChannel + aData.mCbCrStride * (i / heightScale); uint8_t* rowDst = dst + stride * i; // Iterate across the CbCr width (which we have guaranteed to be half of // the surface width), and write two 16bit pixels each time. for (size_t j = 0; j < (size_t)aData.mCbCrSize.width; j++) { *rowDst = *rowYSrc; rowDst++; rowYSrc++; *rowDst = *rowCbSrc; rowDst++; rowCbSrc++; *rowDst = *rowYSrc; rowDst++; rowYSrc++; *rowDst = *rowCrSrc; rowDst++; rowCrSrc++; } } } else if (surf->GetFormat() == SurfaceFormat::NV12) { MOZ_ASSERT(aData.mYSize.height > 0); uint8_t* dst = (uint8_t*)surf->GetBaseAddressOfPlane(0); size_t stride = surf->GetBytesPerRow(0); for (size_t i = 0; i < (size_t)aData.mYSize.height; i++) { uint8_t* rowSrc = aData.mYChannel + aData.mYStride * i; uint8_t* rowDst = dst + stride * i; memcpy(rowDst, rowSrc, aData.mYSize.width); } // Copy and interleave the Cb and Cr channels. MOZ_ASSERT(aData.mCbCrSize.height > 0); dst = (uint8_t*)surf->GetBaseAddressOfPlane(1); stride = surf->GetBytesPerRow(1); for (size_t i = 0; i < (size_t)aData.mCbCrSize.height; i++) { uint8_t* rowCbSrc = aData.mCbChannel + aData.mCbCrStride * i; uint8_t* rowCrSrc = aData.mCrChannel + aData.mCbCrStride * i; uint8_t* rowDst = dst + stride * i; for (size_t j = 0; j < (size_t)aData.mCbCrSize.width; j++) { *rowDst = *rowCbSrc; rowDst++; rowCbSrc++; *rowDst = *rowCrSrc; rowDst++; rowCrSrc++; } } } surf->Unlock(false); mSurface = surf; mPictureRect = aData.GetPictureRect(); return true; } already_AddRefed MacIOSurfaceRecycleAllocator::Allocate( const gfx::IntSize aYSize, const gfx::IntSize& aCbCrSize, gfx::YUVColorSpace aYUVColorSpace, gfx::ColorRange aColorRange) { nsTArray> surfaces = std::move(mSurfaces); RefPtr result; for (auto& surf : surfaces) { // If the surface size has changed, then discard any surfaces of the old // size. if (::IOSurfaceGetWidthOfPlane(surf.get(), 0) != (size_t)aYSize.width || ::IOSurfaceGetHeightOfPlane(surf.get(), 0) != (size_t)aYSize.height) { continue; } // Only construct a MacIOSurface object when we find one that isn't // in-use, since the constructor adds a usage ref. if (!result && !::IOSurfaceIsInUse(surf.get())) { result = new MacIOSurface(surf, false, aYUVColorSpace); } mSurfaces.AppendElement(surf); } if (!result) { if (StaticPrefs::layers_iosurfaceimage_use_nv12_AtStartup()) { result = MacIOSurface::CreateNV12Surface(aYSize, aCbCrSize, aYUVColorSpace, aColorRange); } else { result = MacIOSurface::CreateYUV422Surface(aYSize, aYUVColorSpace, aColorRange); } if (mSurfaces.Length() < StaticPrefs::layers_iosurfaceimage_recycle_limit()) { mSurfaces.AppendElement(result->GetIOSurfaceRef()); } } return result.forget(); }