mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 07:13:20 +00:00
5de404e405
The code always assumed that the size of the image with the Y plane dimensions, which, while often the case, isn't correct. We remove the assertions that the display offset was always (0,0) and properly carry the actual data over IPC. Remoting the theora decoder and enabling fast video copy exposed several other related issues in the various D3D11 image types. Various WPT uses theora YUV44 images (because we do not support YUV444 H264 ones). Those images are made of 32 pixels planes with a display size set to 20 pixels. Prior P1D the backend image was a ShareYCbCrPlanar image which correctly handled the size settings. Like the image serializer, the various D3D11 images always assumed that the Y plane size was the image size. This however expose existing issues where the offset position of the display is completely ignored for some image type. See bug 1669054 All those problems explain why sometimes we displayed more pixels than we should have. Depends on D91914 Differential Revision: https://phabricator.services.mozilla.com/D92233
339 lines
12 KiB
C++
339 lines
12 KiB
C++
/* -*- 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 "ImageDataSerializer.h"
|
|
|
|
#include "YCbCrUtils.h" // for YCbCr conversions
|
|
#include "gfx2DGlue.h" // for SurfaceFormatToImageFormat
|
|
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
|
|
#include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory
|
|
#include "mozilla/gfx/Logging.h" // for gfxDebug
|
|
#include "mozilla/gfx/Tools.h" // for GetAlignedStride, etc
|
|
#include "mozilla/gfx/Types.h"
|
|
#include "mozilla/mozalloc.h" // for operator delete, etc
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
namespace ImageDataSerializer {
|
|
|
|
using namespace gfx;
|
|
|
|
int32_t ComputeRGBStride(SurfaceFormat aFormat, int32_t aWidth) {
|
|
#ifdef XP_MACOSX
|
|
// Some drivers require an alignment of 32 bytes for efficient texture upload.
|
|
return GetAlignedStride<32>(aWidth, BytesPerPixel(aFormat));
|
|
#else
|
|
return GetAlignedStride<4>(aWidth, BytesPerPixel(aFormat));
|
|
#endif
|
|
}
|
|
|
|
int32_t GetRGBStride(const RGBDescriptor& aDescriptor) {
|
|
return ComputeRGBStride(aDescriptor.format(), aDescriptor.size().width);
|
|
}
|
|
|
|
uint32_t ComputeRGBBufferSize(IntSize aSize, SurfaceFormat aFormat) {
|
|
MOZ_ASSERT(aSize.height >= 0 && aSize.width >= 0);
|
|
|
|
// This takes care of checking whether there could be overflow
|
|
// with enough margin for the metadata.
|
|
if (!gfx::Factory::AllowedSurfaceSize(aSize)) {
|
|
return 0;
|
|
}
|
|
|
|
// Note we're passing height instad of the bpp parameter, but the end
|
|
// result is the same - and the bpp was already taken care of in the
|
|
// ComputeRGBStride function.
|
|
int32_t bufsize = GetAlignedStride<16>(ComputeRGBStride(aFormat, aSize.width),
|
|
aSize.height);
|
|
|
|
if (bufsize < 0) {
|
|
// This should not be possible thanks to Factory::AllowedSurfaceSize
|
|
return 0;
|
|
}
|
|
|
|
return bufsize;
|
|
}
|
|
|
|
// Minimum required shmem size in bytes
|
|
uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride,
|
|
const gfx::IntSize& aCbCrSize,
|
|
int32_t aCbCrStride) {
|
|
MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0);
|
|
|
|
if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 ||
|
|
aCbCrSize.width < 0 ||
|
|
!gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) ||
|
|
!gfx::Factory::AllowedSurfaceSize(
|
|
IntSize(aCbCrStride, aCbCrSize.height))) {
|
|
return 0;
|
|
}
|
|
|
|
// Overflow checks are performed in AllowedSurfaceSize
|
|
return GetAlignedStride<4>(aYSize.height, aYStride) +
|
|
2 * GetAlignedStride<4>(aCbCrSize.height, aCbCrStride);
|
|
}
|
|
|
|
uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride,
|
|
const gfx::IntSize& aCbCrSize,
|
|
int32_t aCbCrStride, uint32_t aYOffset,
|
|
uint32_t aCbOffset, uint32_t aCrOffset) {
|
|
MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0);
|
|
|
|
if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 ||
|
|
aCbCrSize.width < 0 ||
|
|
!gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) ||
|
|
!gfx::Factory::AllowedSurfaceSize(
|
|
IntSize(aCbCrStride, aCbCrSize.height))) {
|
|
return 0;
|
|
}
|
|
|
|
uint32_t yLength = GetAlignedStride<4>(aYStride, aYSize.height);
|
|
uint32_t cbCrLength = GetAlignedStride<4>(aCbCrStride, aCbCrSize.height);
|
|
if (yLength == 0 || cbCrLength == 0) {
|
|
return 0;
|
|
}
|
|
|
|
CheckedInt<uint32_t> yEnd = aYOffset;
|
|
yEnd += yLength;
|
|
CheckedInt<uint32_t> cbEnd = aCbOffset;
|
|
cbEnd += cbCrLength;
|
|
CheckedInt<uint32_t> crEnd = aCrOffset;
|
|
crEnd += cbCrLength;
|
|
|
|
if (!yEnd.isValid() || !cbEnd.isValid() || !crEnd.isValid() ||
|
|
yEnd.value() > aCbOffset || cbEnd.value() > aCrOffset) {
|
|
return 0;
|
|
}
|
|
|
|
return crEnd.value();
|
|
}
|
|
|
|
uint32_t ComputeYCbCrBufferSize(uint32_t aBufferSize) {
|
|
return GetAlignedStride<4>(aBufferSize, 1);
|
|
}
|
|
|
|
void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight, int32_t cbCrStride,
|
|
int32_t cbCrHeight, uint32_t& outYOffset,
|
|
uint32_t& outCbOffset, uint32_t& outCrOffset) {
|
|
outYOffset = 0;
|
|
outCbOffset = outYOffset + GetAlignedStride<4>(yStride, yHeight);
|
|
outCrOffset = outCbOffset + GetAlignedStride<4>(cbCrStride, cbCrHeight);
|
|
}
|
|
|
|
gfx::SurfaceFormat FormatFromBufferDescriptor(
|
|
const BufferDescriptor& aDescriptor) {
|
|
switch (aDescriptor.type()) {
|
|
case BufferDescriptor::TRGBDescriptor:
|
|
return aDescriptor.get_RGBDescriptor().format();
|
|
case BufferDescriptor::TYCbCrDescriptor:
|
|
return gfx::SurfaceFormat::YUV;
|
|
default:
|
|
MOZ_CRASH("GFX: FormatFromBufferDescriptor");
|
|
}
|
|
}
|
|
|
|
gfx::IntSize SizeFromBufferDescriptor(const BufferDescriptor& aDescriptor) {
|
|
switch (aDescriptor.type()) {
|
|
case BufferDescriptor::TRGBDescriptor:
|
|
return aDescriptor.get_RGBDescriptor().size();
|
|
case BufferDescriptor::TYCbCrDescriptor: {
|
|
return aDescriptor.get_YCbCrDescriptor().display().Size();
|
|
}
|
|
default:
|
|
MOZ_CRASH("GFX: SizeFromBufferDescriptor");
|
|
}
|
|
}
|
|
|
|
gfx::IntRect RectFromBufferDescriptor(const BufferDescriptor& aDescriptor) {
|
|
switch (aDescriptor.type()) {
|
|
case BufferDescriptor::TRGBDescriptor: {
|
|
auto size = aDescriptor.get_RGBDescriptor().size();
|
|
return gfx::IntRect(0, 0, size.Width(), size.Height());
|
|
}
|
|
case BufferDescriptor::TYCbCrDescriptor:
|
|
return aDescriptor.get_YCbCrDescriptor().display();
|
|
default:
|
|
MOZ_CRASH("GFX: RectFromBufferDescriptor");
|
|
}
|
|
}
|
|
|
|
Maybe<gfx::IntSize> CbCrSizeFromBufferDescriptor(
|
|
const BufferDescriptor& aDescriptor) {
|
|
switch (aDescriptor.type()) {
|
|
case BufferDescriptor::TRGBDescriptor:
|
|
return Nothing();
|
|
case BufferDescriptor::TYCbCrDescriptor:
|
|
return Some(aDescriptor.get_YCbCrDescriptor().cbCrSize());
|
|
default:
|
|
MOZ_CRASH("GFX: CbCrSizeFromBufferDescriptor");
|
|
}
|
|
}
|
|
|
|
Maybe<int32_t> YStrideFromBufferDescriptor(
|
|
const BufferDescriptor& aDescriptor) {
|
|
switch (aDescriptor.type()) {
|
|
case BufferDescriptor::TRGBDescriptor:
|
|
return Nothing();
|
|
case BufferDescriptor::TYCbCrDescriptor:
|
|
return Some(aDescriptor.get_YCbCrDescriptor().yStride());
|
|
default:
|
|
MOZ_CRASH("GFX: YStrideFromBufferDescriptor");
|
|
}
|
|
}
|
|
|
|
Maybe<int32_t> CbCrStrideFromBufferDescriptor(
|
|
const BufferDescriptor& aDescriptor) {
|
|
switch (aDescriptor.type()) {
|
|
case BufferDescriptor::TRGBDescriptor:
|
|
return Nothing();
|
|
case BufferDescriptor::TYCbCrDescriptor:
|
|
return Some(aDescriptor.get_YCbCrDescriptor().cbCrStride());
|
|
default:
|
|
MOZ_CRASH("GFX: CbCrStrideFromBufferDescriptor");
|
|
}
|
|
}
|
|
|
|
Maybe<gfx::YUVColorSpace> YUVColorSpaceFromBufferDescriptor(
|
|
const BufferDescriptor& aDescriptor) {
|
|
switch (aDescriptor.type()) {
|
|
case BufferDescriptor::TRGBDescriptor:
|
|
return Nothing();
|
|
case BufferDescriptor::TYCbCrDescriptor:
|
|
return Some(aDescriptor.get_YCbCrDescriptor().yUVColorSpace());
|
|
default:
|
|
MOZ_CRASH("GFX: YUVColorSpaceFromBufferDescriptor");
|
|
}
|
|
}
|
|
|
|
Maybe<gfx::ColorDepth> ColorDepthFromBufferDescriptor(
|
|
const BufferDescriptor& aDescriptor) {
|
|
switch (aDescriptor.type()) {
|
|
case BufferDescriptor::TRGBDescriptor:
|
|
return Nothing();
|
|
case BufferDescriptor::TYCbCrDescriptor:
|
|
return Some(aDescriptor.get_YCbCrDescriptor().colorDepth());
|
|
default:
|
|
MOZ_CRASH("GFX: ColorDepthFromBufferDescriptor");
|
|
}
|
|
}
|
|
|
|
Maybe<gfx::ColorRange> ColorRangeFromBufferDescriptor(
|
|
const BufferDescriptor& aDescriptor) {
|
|
switch (aDescriptor.type()) {
|
|
case BufferDescriptor::TRGBDescriptor:
|
|
return Nothing();
|
|
case BufferDescriptor::TYCbCrDescriptor:
|
|
return Some(aDescriptor.get_YCbCrDescriptor().colorRange());
|
|
default:
|
|
MOZ_CRASH("GFX: YUVFullRangeFromBufferDescriptor");
|
|
}
|
|
}
|
|
|
|
Maybe<StereoMode> StereoModeFromBufferDescriptor(
|
|
const BufferDescriptor& aDescriptor) {
|
|
switch (aDescriptor.type()) {
|
|
case BufferDescriptor::TRGBDescriptor:
|
|
return Nothing();
|
|
case BufferDescriptor::TYCbCrDescriptor:
|
|
return Some(aDescriptor.get_YCbCrDescriptor().stereoMode());
|
|
default:
|
|
MOZ_CRASH("GFX: StereoModeFromBufferDescriptor");
|
|
}
|
|
}
|
|
|
|
uint8_t* GetYChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor) {
|
|
return aBuffer + aDescriptor.yOffset();
|
|
}
|
|
|
|
uint8_t* GetCbChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor) {
|
|
return aBuffer + aDescriptor.cbOffset();
|
|
}
|
|
|
|
uint8_t* GetCrChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor) {
|
|
return aBuffer + aDescriptor.crOffset();
|
|
}
|
|
|
|
already_AddRefed<DataSourceSurface> DataSourceSurfaceFromYCbCrDescriptor(
|
|
uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor,
|
|
gfx::DataSourceSurface* aSurface) {
|
|
const gfx::IntRect display = aDescriptor.display();
|
|
const gfx::IntSize size = display.Size();
|
|
RefPtr<DataSourceSurface> result;
|
|
if (aSurface) {
|
|
MOZ_ASSERT(aSurface->GetSize() == size);
|
|
MOZ_ASSERT(aSurface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
|
|
if (aSurface->GetSize() == size &&
|
|
aSurface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8) {
|
|
result = aSurface;
|
|
}
|
|
}
|
|
|
|
if (!result) {
|
|
result =
|
|
Factory::CreateDataSourceSurface(size, gfx::SurfaceFormat::B8G8R8X8);
|
|
}
|
|
if (NS_WARN_IF(!result)) {
|
|
return nullptr;
|
|
}
|
|
|
|
DataSourceSurface::MappedSurface map;
|
|
if (NS_WARN_IF(!result->Map(DataSourceSurface::MapType::WRITE, &map))) {
|
|
return nullptr;
|
|
}
|
|
|
|
layers::PlanarYCbCrData ycbcrData;
|
|
ycbcrData.mYChannel = GetYChannel(aBuffer, aDescriptor);
|
|
ycbcrData.mYStride = aDescriptor.yStride();
|
|
ycbcrData.mYSize = aDescriptor.ySize();
|
|
ycbcrData.mCbChannel = GetCbChannel(aBuffer, aDescriptor);
|
|
ycbcrData.mCrChannel = GetCrChannel(aBuffer, aDescriptor);
|
|
ycbcrData.mCbCrStride = aDescriptor.cbCrStride();
|
|
ycbcrData.mCbCrSize = aDescriptor.cbCrSize();
|
|
ycbcrData.mPicSize = size;
|
|
ycbcrData.mPicX = display.X();
|
|
ycbcrData.mPicY = display.Y();
|
|
ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace();
|
|
ycbcrData.mColorDepth = aDescriptor.colorDepth();
|
|
|
|
gfx::ConvertYCbCrToRGB(ycbcrData, gfx::SurfaceFormat::B8G8R8X8, size,
|
|
map.mData, map.mStride);
|
|
|
|
result->Unmap();
|
|
return result.forget();
|
|
}
|
|
|
|
void ConvertAndScaleFromYCbCrDescriptor(uint8_t* aBuffer,
|
|
const YCbCrDescriptor& aDescriptor,
|
|
const gfx::SurfaceFormat& aDestFormat,
|
|
const gfx::IntSize& aDestSize,
|
|
unsigned char* aDestBuffer,
|
|
int32_t aStride) {
|
|
MOZ_ASSERT(aBuffer);
|
|
|
|
const gfx::IntRect display = aDescriptor.display();
|
|
|
|
layers::PlanarYCbCrData ycbcrData;
|
|
ycbcrData.mYChannel = GetYChannel(aBuffer, aDescriptor);
|
|
ycbcrData.mYStride = aDescriptor.yStride();
|
|
ycbcrData.mYSize = aDescriptor.ySize();
|
|
ycbcrData.mCbChannel = GetCbChannel(aBuffer, aDescriptor);
|
|
ycbcrData.mCrChannel = GetCrChannel(aBuffer, aDescriptor);
|
|
ycbcrData.mCbCrStride = aDescriptor.cbCrStride();
|
|
ycbcrData.mCbCrSize = aDescriptor.cbCrSize();
|
|
ycbcrData.mPicSize = display.Size();
|
|
ycbcrData.mPicX = display.X();
|
|
ycbcrData.mPicY = display.Y();
|
|
ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace();
|
|
ycbcrData.mColorDepth = aDescriptor.colorDepth();
|
|
|
|
gfx::ConvertYCbCrToRGB(ycbcrData, aDestFormat, aDestSize, aDestBuffer,
|
|
aStride);
|
|
}
|
|
|
|
} // namespace ImageDataSerializer
|
|
} // namespace layers
|
|
} // namespace mozilla
|