mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
aaf2063029
MozReview-Commit-ID: 12AHOhrpaNO --HG-- extra : rebase_source : 7881277a13fe7389b95635ee9b94f1686f56d142
290 lines
8.5 KiB
C++
290 lines
8.5 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;
|
|
} else 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
|
|
{
|
|
const uint32_t stride = Surface()->Stride();
|
|
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();
|
|
} else {
|
|
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();
|
|
} else {
|
|
return mImage->AsNVImage()->GetData();
|
|
}
|
|
}
|
|
};
|
|
|
|
// TODO: optimize for other platforms.
|
|
// For GONK: implement GrallocImageImpl, GrallocPlanarYCbCrImpl and GonkCameraImpl.
|
|
// 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
|