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:
Kaku Kuo 2016-04-27 13:03:01 +08:00
Родитель c4341b59cc
Коммит fca7b0b604
3 изменённых файлов: 268 добавлений и 5 удалений

Просмотреть файл

@ -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