gecko-dev/dom/canvas/ImageUtils.cpp
David Anderson 9365817b45 Fix DataSourceSurface mapping in ImageUtils.cpp. (bug 1405390 part 5, r=bas)
--HG--
extra : rebase_source : 691738a68b679b9a4abaf7e538d0499d5f61fe0a
2017-11-09 00:43:31 -08:00

292 lines
8.6 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 "ImageUtils.h"
#include "ImageBitmapUtils.h"
#include "ImageContainer.h"
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/dom/ImageBitmapBinding.h"
#include "mozilla/ErrorResult.h"
using namespace mozilla::layers;
using namespace mozilla::gfx;
namespace mozilla {
namespace dom {
static ImageBitmapFormat
GetImageBitmapFormatFromSurfaceFromat(SurfaceFormat aSurfaceFormat)
{
switch (aSurfaceFormat) {
case SurfaceFormat::B8G8R8A8:
case SurfaceFormat::B8G8R8X8:
return ImageBitmapFormat::BGRA32;
case SurfaceFormat::R8G8B8A8:
case SurfaceFormat::R8G8B8X8:
return ImageBitmapFormat::RGBA32;
case SurfaceFormat::R8G8B8:
return ImageBitmapFormat::RGB24;
case SurfaceFormat::B8G8R8:
return ImageBitmapFormat::BGR24;
case SurfaceFormat::HSV:
return ImageBitmapFormat::HSV;
case SurfaceFormat::Lab:
return ImageBitmapFormat::Lab;
case SurfaceFormat::Depth:
return ImageBitmapFormat::DEPTH;
case SurfaceFormat::A8:
return ImageBitmapFormat::GRAY8;
case SurfaceFormat::R5G6B5_UINT16:
case SurfaceFormat::YUV:
case SurfaceFormat::NV12:
case SurfaceFormat::UNKNOWN:
default:
return ImageBitmapFormat::EndGuard_;
}
}
static ImageBitmapFormat
GetImageBitmapFormatFromPlanarYCbCrData(layers::PlanarYCbCrData const *aData)
{
MOZ_ASSERT(aData);
if (aData->mYSkip == 0 && aData->mCbSkip == 0 &&
aData->mCrSkip == 0) { // Possibly three planes.
if (aData->mCbChannel >=
aData->mYChannel + aData->mYSize.height * aData->mYStride &&
aData->mCrChannel >=
aData->mCbChannel +
aData->mCbCrSize.height * aData->mCbCrStride) { // Three planes.
if (aData->mYSize.height == aData->mCbCrSize.height) {
if (aData->mYSize.width == aData->mCbCrSize.width) {
return ImageBitmapFormat::YUV444P;
}
if (((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
return ImageBitmapFormat::YUV422P;
}
} else if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height) {
if (((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
return ImageBitmapFormat::YUV420P;
}
}
}
} else if (aData->mYSkip == 0 && aData->mCbSkip == 1 && aData->mCrSkip == 1) { // Possibly two planes.
if (aData->mCbChannel >= aData->mYChannel + aData->mYSize.height * aData->mYStride &&
aData->mCbChannel == aData->mCrChannel - 1) { // Two planes.
if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height &&
((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
return ImageBitmapFormat::YUV420SP_NV12; // Y-Cb-Cr
}
} else if (aData->mCrChannel >= aData->mYChannel + aData->mYSize.height * aData->mYStride &&
aData->mCrChannel == aData->mCbChannel - 1) { // Two planes.
if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height &&
((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
return ImageBitmapFormat::YUV420SP_NV21; // Y-Cr-Cb
}
}
}
return ImageBitmapFormat::EndGuard_;
}
// ImageUtils::Impl implements the _generic_ algorithm which always readback
// data in RGBA format into CPU memory.
// Since layers::CairoImage is just a warpper to a DataSourceSurface, the
// implementation of CairoSurfaceImpl is nothing different to the generic
// version.
class ImageUtils::Impl
{
public:
explicit Impl(layers::Image* aImage)
: mImage(aImage)
, mSurface(nullptr)
{
}
virtual ~Impl() = default;
virtual ImageBitmapFormat
GetFormat() const
{
return GetImageBitmapFormatFromSurfaceFromat(Surface()->GetFormat());
}
virtual uint32_t
GetBufferLength() const
{
DataSourceSurface::ScopedMap map(Surface(), DataSourceSurface::READ);
const uint32_t stride = map.GetStride();
const IntSize size = Surface()->GetSize();
return (uint32_t)(size.height * stride);
}
virtual UniquePtr<ImagePixelLayout>
MapDataInto(uint8_t* aBuffer,
uint32_t aOffset,
uint32_t aBufferLength,
ImageBitmapFormat aFormat,
ErrorResult& aRv) const
{
DataSourceSurface::ScopedMap map(Surface(), DataSourceSurface::READ);
if (!map.IsMapped()) {
aRv.Throw(NS_ERROR_ILLEGAL_VALUE);
return nullptr;
}
// Copy or convert data.
UniquePtr<ImagePixelLayout> srcLayout =
CreateDefaultPixelLayout(GetFormat(), Surface()->GetSize().width,
Surface()->GetSize().height, map.GetStride());
// Prepare destination buffer.
uint8_t* dstBuffer = aBuffer + aOffset;
UniquePtr<ImagePixelLayout> dstLayout =
CopyAndConvertImageData(GetFormat(), map.GetData(), srcLayout.get(),
aFormat, dstBuffer);
if (!dstLayout) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
return dstLayout;
}
protected:
Impl() {}
DataSourceSurface* Surface() const
{
if (!mSurface) {
RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
MOZ_ASSERT(surface);
mSurface = surface->GetDataSurface();
MOZ_ASSERT(mSurface);
}
return mSurface.get();
}
RefPtr<layers::Image> mImage;
mutable RefPtr<DataSourceSurface> mSurface;
};
// YUVImpl is optimized for the layers::PlanarYCbCrImage and layers::NVImage.
// This implementation does not readback data in RGBA format but keep it in YUV
// format family.
class YUVImpl final : public ImageUtils::Impl
{
public:
explicit YUVImpl(layers::Image* aImage)
: Impl(aImage)
{
MOZ_ASSERT(aImage->GetFormat() == ImageFormat::PLANAR_YCBCR ||
aImage->GetFormat() == ImageFormat::NV_IMAGE);
}
ImageBitmapFormat GetFormat() const override
{
return GetImageBitmapFormatFromPlanarYCbCrData(GetPlanarYCbCrData());
}
uint32_t GetBufferLength() const override
{
if (mImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
return mImage->AsPlanarYCbCrImage()->GetDataSize();
}
return mImage->AsNVImage()->GetBufferSize();
}
UniquePtr<ImagePixelLayout> MapDataInto(uint8_t* aBuffer,
uint32_t aOffset,
uint32_t aBufferLength,
ImageBitmapFormat aFormat,
ErrorResult& aRv) const override
{
// Prepare source buffer and pixel layout.
const PlanarYCbCrData* data = GetPlanarYCbCrData();
UniquePtr<ImagePixelLayout> srcLayout =
CreatePixelLayoutFromPlanarYCbCrData(data);
// Do conversion.
UniquePtr<ImagePixelLayout> dstLayout =
CopyAndConvertImageData(GetFormat(), data->mYChannel, srcLayout.get(),
aFormat, aBuffer+aOffset);
if (!dstLayout) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
return dstLayout;
}
private:
const PlanarYCbCrData* GetPlanarYCbCrData() const
{
if (mImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
return mImage->AsPlanarYCbCrImage()->GetData();
}
return mImage->AsNVImage()->GetData();
}
};
// TODO: optimize for other platforms.
// For Windows: implement D3D9RGB32TextureImpl and D3D11ShareHandleTextureImpl.
// Others: SharedBGRImpl, MACIOSrufaceImpl, GLImageImpl, SurfaceTextureImpl
// EGLImageImpl and OverlayImegImpl.
ImageUtils::ImageUtils(layers::Image* aImage)
: mImpl(nullptr)
{
MOZ_ASSERT(aImage, "Create ImageUtils with nullptr.");
switch(aImage->GetFormat()) {
case mozilla::ImageFormat::PLANAR_YCBCR:
case mozilla::ImageFormat::NV_IMAGE:
mImpl = new YUVImpl(aImage);
break;
case mozilla::ImageFormat::CAIRO_SURFACE:
default:
mImpl = new Impl(aImage);
}
}
ImageUtils::~ImageUtils()
{
if (mImpl) { delete mImpl; mImpl = nullptr; }
}
ImageBitmapFormat
ImageUtils::GetFormat() const
{
MOZ_ASSERT(mImpl);
return mImpl->GetFormat();
}
uint32_t
ImageUtils::GetBufferLength() const
{
MOZ_ASSERT(mImpl);
return mImpl->GetBufferLength();
}
UniquePtr<ImagePixelLayout>
ImageUtils::MapDataInto(uint8_t* aBuffer,
uint32_t aOffset,
uint32_t aBufferLength,
ImageBitmapFormat aFormat,
ErrorResult& aRv) const
{
MOZ_ASSERT(mImpl);
MOZ_ASSERT(aBuffer, "Map data into a null buffer.");
return mImpl->MapDataInto(aBuffer, aOffset, aBufferLength, aFormat, aRv);
}
} // namespace dom
} // namespace mozilla