зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1141979 - part8 - implement ImageBitmap extensions; r=jrmuizel
MozReview-Commit-ID: Bc6HoqRLMNB --HG-- extra : transplant_source : %89%E9Q%FD4%ED%FD%EC5pqq%AF%C6%90%E0y%A2%88%12
This commit is contained in:
Родитель
c4341b59cc
Коммит
fca7b0b604
|
@ -655,6 +655,10 @@ DOMInterfaces = {
|
|||
'headerFile': 'xpcjsid.h',
|
||||
},
|
||||
|
||||
'ImageBitmap': {
|
||||
'implicitJSContext': [ 'mapDataInto' ],
|
||||
},
|
||||
|
||||
'ImageCapture': {
|
||||
'binaryNames': { 'videoStreamTrack': 'GetVideoStreamTrack' }
|
||||
},
|
||||
|
|
|
@ -12,9 +12,10 @@
|
|||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/dom/WorkerRunnable.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "ImageBitmapUtils.h"
|
||||
#include "ImageUtils.h"
|
||||
#include "imgTools.h"
|
||||
#include "libyuv.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::layers;
|
||||
|
@ -395,6 +396,7 @@ ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
|
|||
: mParent(aGlobal)
|
||||
, mData(aData)
|
||||
, mSurface(nullptr)
|
||||
, mDataWrapper(new ImageUtils(mData))
|
||||
, mPictureRect(0, 0, aData->GetSize().width, aData->GetSize().height)
|
||||
, mIsPremultipliedAlpha(aIsPremultipliedAlpha)
|
||||
{
|
||||
|
@ -481,7 +483,12 @@ ImageBitmap::PrepareForDrawTarget(gfx::DrawTarget* aTarget)
|
|||
|
||||
// Pre-multiply alpha here.
|
||||
// Apply pre-multiply alpha only if mIsPremultipliedAlpha is false.
|
||||
if (!mIsPremultipliedAlpha) {
|
||||
// Ignore this step if the source surface does not have alpha channel; this
|
||||
// kind of source surfaces might come form layers::PlanarYCbCrImage.
|
||||
if (!mIsPremultipliedAlpha &&
|
||||
mSurface->GetFormat() != SurfaceFormat::B8G8R8X8 &&
|
||||
mSurface->GetFormat() != SurfaceFormat::R8G8B8X8 &&
|
||||
mSurface->GetFormat() != SurfaceFormat::X8R8G8B8) {
|
||||
MOZ_ASSERT(mSurface->GetFormat() == SurfaceFormat::R8G8B8A8 ||
|
||||
mSurface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
|
||||
mSurface->GetFormat() == SurfaceFormat::A8R8G8B8);
|
||||
|
@ -1407,5 +1414,229 @@ ImageBitmap::ExtensionsEnabled(JSContext* aCx, JSObject*)
|
|||
}
|
||||
}
|
||||
|
||||
// ImageBitmap extensions.
|
||||
ImageBitmapFormat
|
||||
ImageBitmap::FindOptimalFormat(const Optional<Sequence<ImageBitmapFormat>>& aPossibleFormats,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(mDataWrapper, "No ImageBitmapFormatUtils functionalities.");
|
||||
|
||||
ImageBitmapFormat platformFormat = mDataWrapper->GetFormat();
|
||||
|
||||
if (!aPossibleFormats.WasPassed() ||
|
||||
aPossibleFormats.Value().Contains(platformFormat)) {
|
||||
return platformFormat;
|
||||
} else {
|
||||
// If no matching is found, FindBestMatchingFromat() returns
|
||||
// ImageBitmapFormat::EndGuard_ and we throw an exception.
|
||||
ImageBitmapFormat optimalFormat =
|
||||
FindBestMatchingFromat(platformFormat, aPossibleFormats.Value());
|
||||
|
||||
if (optimalFormat == ImageBitmapFormat::EndGuard_) {
|
||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
return optimalFormat;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t
|
||||
ImageBitmap::MappedDataLength(ImageBitmapFormat aFormat, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(mDataWrapper, "No ImageBitmapFormatUtils functionalities.");
|
||||
|
||||
if (aFormat == mDataWrapper->GetFormat()) {
|
||||
return mDataWrapper->GetBufferLength();
|
||||
} else {
|
||||
return CalculateImageBufferSize(aFormat, Width(), Height());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class MapDataIntoBufferSource
|
||||
{
|
||||
protected:
|
||||
MapDataIntoBufferSource(JSContext* aCx,
|
||||
Promise *aPromise,
|
||||
ImageBitmap *aImageBitmap,
|
||||
const T& aBuffer,
|
||||
int32_t aOffset,
|
||||
ImageBitmapFormat aFormat)
|
||||
: mPromise(aPromise)
|
||||
, mImageBitmap(aImageBitmap)
|
||||
, mBuffer(aCx, aBuffer.Obj())
|
||||
, mOffset(aOffset)
|
||||
, mFormat(aFormat)
|
||||
{
|
||||
MOZ_ASSERT(mPromise);
|
||||
MOZ_ASSERT(JS_IsArrayBufferObject(mBuffer) ||
|
||||
JS_IsArrayBufferViewObject(mBuffer));
|
||||
}
|
||||
|
||||
virtual ~MapDataIntoBufferSource() = default;
|
||||
|
||||
void DoMapDataIntoBufferSource()
|
||||
{
|
||||
ErrorResult error;
|
||||
|
||||
// Prepare destination buffer.
|
||||
uint8_t* bufferData = nullptr;
|
||||
uint32_t bufferLength = 0;
|
||||
bool isSharedMemory = false;
|
||||
if (JS_IsArrayBufferObject(mBuffer)) {
|
||||
js::GetArrayBufferLengthAndData(mBuffer, &bufferLength, &isSharedMemory, &bufferData);
|
||||
} else if (JS_IsArrayBufferViewObject(mBuffer)) {
|
||||
js::GetArrayBufferViewLengthAndData(mBuffer, &bufferLength, &isSharedMemory, &bufferData);
|
||||
} else {
|
||||
error.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
||||
mPromise->MaybeReject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!bufferData) || NS_WARN_IF(!bufferLength)) {
|
||||
error.Throw(NS_ERROR_NOT_AVAILABLE);
|
||||
mPromise->MaybeReject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check length.
|
||||
const int32_t neededBufferLength =
|
||||
mImageBitmap->MappedDataLength(mFormat, error);
|
||||
|
||||
if (((int32_t)bufferLength - mOffset) < neededBufferLength) {
|
||||
error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
mPromise->MaybeReject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Call ImageBitmapFormatUtils.
|
||||
UniquePtr<ImagePixelLayout> layout =
|
||||
mImageBitmap->mDataWrapper->MapDataInto(bufferData,
|
||||
mOffset,
|
||||
bufferLength,
|
||||
mFormat,
|
||||
error);
|
||||
|
||||
if (NS_WARN_IF(!layout)) {
|
||||
mPromise->MaybeReject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
mPromise->MaybeResolve(*layout);
|
||||
}
|
||||
|
||||
RefPtr<Promise> mPromise;
|
||||
RefPtr<ImageBitmap> mImageBitmap;
|
||||
JS::PersistentRooted<JSObject*> mBuffer;
|
||||
int32_t mOffset;
|
||||
ImageBitmapFormat mFormat;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class MapDataIntoBufferSourceTask final : public Runnable,
|
||||
public MapDataIntoBufferSource<T>
|
||||
{
|
||||
public:
|
||||
MapDataIntoBufferSourceTask(JSContext* aCx,
|
||||
Promise *aPromise,
|
||||
ImageBitmap *aImageBitmap,
|
||||
const T& aBuffer,
|
||||
int32_t aOffset,
|
||||
ImageBitmapFormat aFormat)
|
||||
: MapDataIntoBufferSource<T>(aCx, aPromise, aImageBitmap, aBuffer, aOffset, aFormat)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~MapDataIntoBufferSourceTask() = default;
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MapDataIntoBufferSource<T>::DoMapDataIntoBufferSource();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class MapDataIntoBufferSourceWorkerTask final : public WorkerSameThreadRunnable,
|
||||
public MapDataIntoBufferSource<T>
|
||||
{
|
||||
public:
|
||||
MapDataIntoBufferSourceWorkerTask(JSContext* aCx,
|
||||
Promise *aPromise,
|
||||
ImageBitmap *aImageBitmap,
|
||||
const T& aBuffer,
|
||||
int32_t aOffset,
|
||||
ImageBitmapFormat aFormat)
|
||||
: WorkerSameThreadRunnable(GetCurrentThreadWorkerPrivate()),
|
||||
MapDataIntoBufferSource<T>(aCx, aPromise, aImageBitmap, aBuffer, aOffset, aFormat)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~MapDataIntoBufferSourceWorkerTask() = default;
|
||||
|
||||
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
MapDataIntoBufferSource<T>::DoMapDataIntoBufferSource();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void AsyncMapDataIntoBufferSource(JSContext* aCx,
|
||||
Promise *aPromise,
|
||||
ImageBitmap *aImageBitmap,
|
||||
const ArrayBufferViewOrArrayBuffer& aBuffer,
|
||||
int32_t aOffset,
|
||||
ImageBitmapFormat aFormat)
|
||||
{
|
||||
MOZ_ASSERT(aCx);
|
||||
MOZ_ASSERT(aPromise);
|
||||
MOZ_ASSERT(aImageBitmap);
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIRunnable> task;
|
||||
|
||||
if (aBuffer.IsArrayBuffer()) {
|
||||
const ArrayBuffer& buffer = aBuffer.GetAsArrayBuffer();
|
||||
task = new MapDataIntoBufferSourceTask<ArrayBuffer>(aCx, aPromise, aImageBitmap, buffer, aOffset, aFormat);
|
||||
} else if (aBuffer.IsArrayBufferView()) {
|
||||
const ArrayBufferView& bufferView = aBuffer.GetAsArrayBufferView();
|
||||
task = new MapDataIntoBufferSourceTask<ArrayBufferView>(aCx, aPromise, aImageBitmap, bufferView, aOffset, aFormat);
|
||||
}
|
||||
|
||||
NS_DispatchToCurrentThread(task); // Actually, to the main-thread.
|
||||
} else {
|
||||
RefPtr<WorkerSameThreadRunnable> task;
|
||||
|
||||
if (aBuffer.IsArrayBuffer()) {
|
||||
const ArrayBuffer& buffer = aBuffer.GetAsArrayBuffer();
|
||||
task = new MapDataIntoBufferSourceWorkerTask<ArrayBuffer>(aCx, aPromise, aImageBitmap, buffer, aOffset, aFormat);
|
||||
} else if (aBuffer.IsArrayBufferView()) {
|
||||
const ArrayBufferView& bufferView = aBuffer.GetAsArrayBufferView();
|
||||
task = new MapDataIntoBufferSourceWorkerTask<ArrayBufferView>(aCx, aPromise, aImageBitmap, bufferView, aOffset, aFormat);
|
||||
}
|
||||
|
||||
task->Dispatch(); // Actually, to the current worker-thread.
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ImageBitmap::MapDataInto(JSContext* aCx,
|
||||
ImageBitmapFormat aFormat,
|
||||
const ArrayBufferViewOrArrayBuffer& aBuffer,
|
||||
int32_t aOffset, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(mDataWrapper, "No ImageBitmapFormatUtils functionalities.");
|
||||
MOZ_ASSERT(aCx, "No JSContext while calling ImageBitmap::MapDataInto().");
|
||||
|
||||
RefPtr<Promise> promise = Promise::Create(mParent, aRv);
|
||||
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AsyncMapDataIntoBufferSource(aCx, promise, this, aBuffer, aOffset, aFormat);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -41,17 +41,21 @@ namespace workers {
|
|||
class WorkerStructuredCloneClosure;
|
||||
}
|
||||
|
||||
class ArrayBufferViewOrArrayBuffer;
|
||||
class CanvasRenderingContext2D;
|
||||
class CreateImageBitmapFromBlob;
|
||||
class CreateImageBitmapFromBlobTask;
|
||||
class CreateImageBitmapFromBlobWorkerTask;
|
||||
class File;
|
||||
class HTMLCanvasElement;
|
||||
class HTMLImageElement;
|
||||
class HTMLVideoElement;
|
||||
enum class ImageBitmapFormat : uint32_t;
|
||||
class ImageData;
|
||||
class ImageUtils;
|
||||
template<typename T> class MapDataIntoBufferSource;
|
||||
class Promise;
|
||||
class PostMessageEvent; // For StructuredClone between windows.
|
||||
class CreateImageBitmapFromBlob;
|
||||
class CreateImageBitmapFromBlobTask;
|
||||
class CreateImageBitmapFromBlobWorkerTask;
|
||||
|
||||
struct ImageBitmapCloneData final
|
||||
{
|
||||
|
@ -143,6 +147,23 @@ public:
|
|||
friend CreateImageBitmapFromBlobTask;
|
||||
friend CreateImageBitmapFromBlobWorkerTask;
|
||||
|
||||
template<typename T>
|
||||
friend class MapDataIntoBufferSource;
|
||||
|
||||
// Mozilla Extensions
|
||||
ImageBitmapFormat
|
||||
FindOptimalFormat(const Optional<Sequence<ImageBitmapFormat>>& aPossibleFormats,
|
||||
ErrorResult& aRv);
|
||||
|
||||
int32_t
|
||||
MappedDataLength(ImageBitmapFormat aFormat, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
MapDataInto(JSContext* aCx,
|
||||
ImageBitmapFormat aFormat,
|
||||
const ArrayBufferViewOrArrayBuffer& aBuffer,
|
||||
int32_t aOffset, ErrorResult& aRv);
|
||||
|
||||
protected:
|
||||
|
||||
/*
|
||||
|
@ -214,6 +235,13 @@ protected:
|
|||
RefPtr<layers::Image> mData;
|
||||
RefPtr<gfx::SourceSurface> mSurface;
|
||||
|
||||
/*
|
||||
* This is used in the ImageBitmap-Extensions implementation.
|
||||
* ImageUtils is a wrapper to layers::Image, which add some common methods for
|
||||
* accessing the layers::Image's data.
|
||||
*/
|
||||
UniquePtr<ImageUtils> mDataWrapper;
|
||||
|
||||
/*
|
||||
* The mPictureRect is the size of the source image in default, however, if
|
||||
* users specify the cropping area while creating an ImageBitmap, then this
|
||||
|
|
Загрузка…
Ссылка в новой задаче