1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/ImageBitmap.h"
8 #include "mozilla/CheckedInt.h"
9 #include "mozilla/dom/BlobImpl.h"
10 #include "mozilla/dom/CanvasRenderingContext2D.h"
11 #include "mozilla/dom/CanvasUtils.h"
12 #include "mozilla/dom/Document.h"
13 #include "mozilla/dom/HTMLCanvasElement.h"
14 #include "mozilla/dom/HTMLImageElement.h"
15 #include "mozilla/dom/HTMLMediaElementBinding.h"
16 #include "mozilla/dom/HTMLVideoElement.h"
17 #include "mozilla/dom/ImageBitmapBinding.h"
18 #include "mozilla/dom/OffscreenCanvas.h"
19 #include "mozilla/dom/Promise.h"
20 #include "mozilla/dom/StructuredCloneTags.h"
21 #include "mozilla/dom/SVGImageElement.h"
22 #include "mozilla/dom/WorkerPrivate.h"
23 #include "mozilla/dom/WorkerRef.h"
24 #include "mozilla/dom/WorkerRunnable.h"
25 #include "mozilla/dom/VideoFrame.h"
26 #include "mozilla/gfx/2D.h"
27 #include "mozilla/gfx/Logging.h"
28 #include "mozilla/gfx/Scale.h"
29 #include "mozilla/gfx/Swizzle.h"
30 #include "mozilla/Mutex.h"
31 #include "mozilla/ScopeExit.h"
32 #include "nsGlobalWindowInner.h"
33 #include "nsIAsyncInputStream.h"
34 #include "nsNetUtil.h"
35 #include "nsLayoutUtils.h"
36 #include "nsStreamUtils.h"
37 #include "imgLoader.h"
40 using namespace mozilla::gfx
;
41 using namespace mozilla::layers
;
42 using mozilla::dom::HTMLMediaElement_Binding::HAVE_METADATA
;
43 using mozilla::dom::HTMLMediaElement_Binding::NETWORK_EMPTY
;
45 namespace mozilla::dom
{
47 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ImageBitmap
, mParent
)
48 NS_IMPL_CYCLE_COLLECTING_ADDREF(ImageBitmap
)
49 NS_IMPL_CYCLE_COLLECTING_RELEASE(ImageBitmap
)
50 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageBitmap
)
51 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
52 NS_INTERFACE_MAP_ENTRY(nsISupports
)
55 class ImageBitmapShutdownObserver
;
57 static StaticMutex sShutdownMutex
;
58 static ImageBitmapShutdownObserver
* sShutdownObserver
= nullptr;
60 class SendShutdownToWorkerThread
: public MainThreadWorkerControlRunnable
{
62 explicit SendShutdownToWorkerThread(ImageBitmap
* aImageBitmap
)
63 : MainThreadWorkerControlRunnable(GetCurrentThreadWorkerPrivate()),
64 mImageBitmap(aImageBitmap
) {
65 MOZ_ASSERT(GetCurrentThreadWorkerPrivate());
68 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
70 mImageBitmap
->OnShutdown();
71 mImageBitmap
= nullptr;
76 ImageBitmap
* mImageBitmap
;
79 /* This class observes shutdown notifications and sends that notification
80 * to the worker thread if the image bitmap is on a worker thread.
82 class ImageBitmapShutdownObserver final
: public nsIObserver
{
84 explicit ImageBitmapShutdownObserver() {
85 sShutdownMutex
.AssertCurrentThreadOwns();
86 if (NS_IsMainThread()) {
91 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
92 MOZ_ASSERT(workerPrivate
);
93 auto* mainThreadEventTarget
= workerPrivate
->MainThreadEventTarget();
94 MOZ_ASSERT(mainThreadEventTarget
);
95 RefPtr
<ImageBitmapShutdownObserver
> self
= this;
96 nsCOMPtr
<nsIRunnable
> r
=
97 NS_NewRunnableFunction("ImageBitmapShutdownObserver::RegisterObserver",
98 [self
]() { self
->RegisterObserver(); });
99 mainThreadEventTarget
->Dispatch(r
.forget());
102 void RegisterObserver() {
103 MOZ_ASSERT(NS_IsMainThread());
104 nsContentUtils::RegisterShutdownObserver(this);
107 already_AddRefed
<SendShutdownToWorkerThread
> Track(
108 ImageBitmap
* aImageBitmap
) {
109 sShutdownMutex
.AssertCurrentThreadOwns();
110 MOZ_ASSERT(!mBitmaps
.Contains(aImageBitmap
));
112 RefPtr
<SendShutdownToWorkerThread
> runnable
= nullptr;
113 if (!NS_IsMainThread()) {
114 runnable
= new SendShutdownToWorkerThread(aImageBitmap
);
117 RefPtr
<SendShutdownToWorkerThread
> retval
= runnable
;
118 mBitmaps
.Insert(aImageBitmap
);
119 return retval
.forget();
122 void Untrack(ImageBitmap
* aImageBitmap
) {
123 sShutdownMutex
.AssertCurrentThreadOwns();
124 MOZ_ASSERT(mBitmaps
.Contains(aImageBitmap
));
126 mBitmaps
.Remove(aImageBitmap
);
129 NS_DECL_THREADSAFE_ISUPPORTS
132 ~ImageBitmapShutdownObserver() = default;
134 nsTHashSet
<ImageBitmap
*> mBitmaps
;
137 NS_IMPL_ISUPPORTS(ImageBitmapShutdownObserver
, nsIObserver
)
140 ImageBitmapShutdownObserver::Observe(nsISupports
* aSubject
, const char* aTopic
,
141 const char16_t
* aData
) {
142 if (strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
) == 0) {
143 StaticMutexAutoLock
lock(sShutdownMutex
);
145 for (const auto& bitmap
: mBitmaps
) {
146 const auto& runnable
= bitmap
->mShutdownRunnable
;
148 runnable
->Dispatch();
150 bitmap
->OnShutdown();
154 nsContentUtils::UnregisterShutdownObserver(this);
156 sShutdownObserver
= nullptr;
163 * If either aRect.width or aRect.height are negative, then return a new IntRect
164 * which represents the same rectangle as the aRect does but with positive width
167 static IntRect
FixUpNegativeDimension(const IntRect
& aRect
, ErrorResult
& aRv
) {
168 gfx::IntRect rect
= aRect
;
170 // fix up negative dimensions
171 if (rect
.width
< 0) {
172 CheckedInt32 checkedX
= CheckedInt32(rect
.x
) + rect
.width
;
174 if (!checkedX
.isValid()) {
175 aRv
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
179 rect
.x
= checkedX
.value();
180 rect
.width
= -(rect
.width
);
183 if (rect
.height
< 0) {
184 CheckedInt32 checkedY
= CheckedInt32(rect
.y
) + rect
.height
;
186 if (!checkedY
.isValid()) {
187 aRv
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
191 rect
.y
= checkedY
.value();
192 rect
.height
= -(rect
.height
);
199 * This helper function copies the data of the given DataSourceSurface,
200 * _aSurface_, in the given area, _aCropRect_, into a new DataSourceSurface.
201 * This might return null if it can not create a new SourceSurface or it cannot
202 * read data from the given _aSurface_.
204 * Warning: Even though the area of _aCropRect_ is just the same as the size of
205 * _aSurface_, this function still copy data into a new
208 static already_AddRefed
<DataSourceSurface
> CropAndCopyDataSourceSurface(
209 DataSourceSurface
* aSurface
, const IntRect
& aCropRect
) {
210 MOZ_ASSERT(aSurface
);
212 // Check the aCropRect
214 const IntRect positiveCropRect
= FixUpNegativeDimension(aCropRect
, error
);
215 if (NS_WARN_IF(error
.Failed())) {
216 error
.SuppressException();
220 // Calculate the size of the new SourceSurface.
221 // We cannot keep using aSurface->GetFormat() to create new DataSourceSurface,
222 // since it might be SurfaceFormat::B8G8R8X8 which does not handle opacity,
223 // however the specification explicitly define that "If any of the pixels on
224 // this rectangle are outside the area where the input bitmap was placed, then
225 // they will be transparent black in output."
226 // So, instead, we force the output format to be SurfaceFormat::B8G8R8A8.
227 const SurfaceFormat format
= SurfaceFormat::B8G8R8A8
;
228 const int bytesPerPixel
= BytesPerPixel(format
);
229 const IntSize dstSize
=
230 IntSize(positiveCropRect
.width
, positiveCropRect
.height
);
231 const uint32_t dstStride
= dstSize
.width
* bytesPerPixel
;
233 // Create a new SourceSurface.
234 RefPtr
<DataSourceSurface
> dstDataSurface
=
235 Factory::CreateDataSourceSurfaceWithStride(dstSize
, format
, dstStride
,
238 if (NS_WARN_IF(!dstDataSurface
)) {
242 // Only do copying and cropping when the positiveCropRect intersects with
243 // the size of aSurface.
244 const IntRect
surfRect(IntPoint(0, 0), aSurface
->GetSize());
245 if (surfRect
.Intersects(positiveCropRect
)) {
246 const IntRect surfPortion
= surfRect
.Intersect(positiveCropRect
);
247 const IntPoint
dest(std::max(0, surfPortion
.X() - positiveCropRect
.X()),
248 std::max(0, surfPortion
.Y() - positiveCropRect
.Y()));
250 // Copy the raw data into the newly created DataSourceSurface.
251 DataSourceSurface::ScopedMap
srcMap(aSurface
, DataSourceSurface::READ
);
252 DataSourceSurface::ScopedMap
dstMap(dstDataSurface
,
253 DataSourceSurface::WRITE
);
254 if (NS_WARN_IF(!srcMap
.IsMapped()) || NS_WARN_IF(!dstMap
.IsMapped())) {
258 uint8_t* srcBufferPtr
= srcMap
.GetData() +
259 surfPortion
.y
* srcMap
.GetStride() +
260 surfPortion
.x
* bytesPerPixel
;
261 uint8_t* dstBufferPtr
=
262 dstMap
.GetData() + dest
.y
* dstMap
.GetStride() + dest
.x
* bytesPerPixel
;
263 CheckedInt
<uint32_t> copiedBytesPerRaw
=
264 CheckedInt
<uint32_t>(surfPortion
.width
) * bytesPerPixel
;
265 if (!copiedBytesPerRaw
.isValid()) {
269 for (int i
= 0; i
< surfPortion
.height
; ++i
) {
270 memcpy(dstBufferPtr
, srcBufferPtr
, copiedBytesPerRaw
.value());
271 srcBufferPtr
+= srcMap
.GetStride();
272 dstBufferPtr
+= dstMap
.GetStride();
276 return dstDataSurface
.forget();
280 * This helper function scales the data of the given DataSourceSurface,
281 * _aSurface_, in the given area, _aCropRect_, into a new DataSourceSurface.
282 * This might return null if it can not create a new SourceSurface or it cannot
283 * read data from the given _aSurface_.
286 static already_AddRefed
<DataSourceSurface
> ScaleDataSourceSurface(
287 DataSourceSurface
* aSurface
, const ImageBitmapOptions
& aOptions
) {
288 MOZ_ASSERT(aSurface
);
290 const SurfaceFormat format
= aSurface
->GetFormat();
291 const int bytesPerPixel
= BytesPerPixel(format
);
293 const IntSize srcSize
= aSurface
->GetSize();
296 CheckedInt
<int32_t> checked
;
297 CheckedInt
<int32_t> dstWidth(
298 aOptions
.mResizeWidth
.WasPassed() ? aOptions
.mResizeWidth
.Value() : 0);
299 CheckedInt
<int32_t> dstHeight(
300 aOptions
.mResizeHeight
.WasPassed() ? aOptions
.mResizeHeight
.Value() : 0);
302 if (!dstWidth
.isValid() || !dstHeight
.isValid()) {
306 if (!dstWidth
.value()) {
307 checked
= srcSize
.width
* dstHeight
;
308 if (!checked
.isValid()) {
312 tmp
= ceil(checked
.value() / double(srcSize
.height
));
314 } else if (!dstHeight
.value()) {
315 checked
= srcSize
.height
* dstWidth
;
316 if (!checked
.isValid()) {
320 tmp
= ceil(checked
.value() / double(srcSize
.width
));
324 const IntSize
dstSize(dstWidth
.value(), dstHeight
.value());
325 const int32_t dstStride
= dstSize
.width
* bytesPerPixel
;
327 // Create a new SourceSurface.
328 RefPtr
<DataSourceSurface
> dstDataSurface
=
329 Factory::CreateDataSourceSurfaceWithStride(dstSize
, format
, dstStride
,
332 if (NS_WARN_IF(!dstDataSurface
)) {
336 // Copy the raw data into the newly created DataSourceSurface.
337 DataSourceSurface::ScopedMap
srcMap(aSurface
, DataSourceSurface::READ
);
338 DataSourceSurface::ScopedMap
dstMap(dstDataSurface
, DataSourceSurface::WRITE
);
339 if (NS_WARN_IF(!srcMap
.IsMapped()) || NS_WARN_IF(!dstMap
.IsMapped())) {
343 uint8_t* srcBufferPtr
= srcMap
.GetData();
344 uint8_t* dstBufferPtr
= dstMap
.GetData();
346 bool res
= Scale(srcBufferPtr
, srcSize
.width
, srcSize
.height
,
347 srcMap
.GetStride(), dstBufferPtr
, dstSize
.width
,
348 dstSize
.height
, dstMap
.GetStride(), aSurface
->GetFormat());
353 return dstDataSurface
.forget();
356 static DataSourceSurface
* FlipYDataSourceSurface(DataSourceSurface
* aSurface
) {
357 MOZ_ASSERT(aSurface
);
359 // Invert in y direction.
360 DataSourceSurface::ScopedMap
srcMap(aSurface
, DataSourceSurface::READ_WRITE
);
361 if (NS_WARN_IF(!srcMap
.IsMapped())) {
365 const IntSize srcSize
= aSurface
->GetSize();
366 uint8_t* srcBufferPtr
= srcMap
.GetData();
367 const uint32_t stride
= srcMap
.GetStride();
369 CheckedInt
<uint32_t> copiedBytesPerRaw
= CheckedInt
<uint32_t>(stride
);
370 if (!copiedBytesPerRaw
.isValid()) {
374 for (int i
= 0; i
< srcSize
.height
/ 2; ++i
) {
375 std::swap_ranges(srcBufferPtr
+ stride
* i
, srcBufferPtr
+ stride
* (i
+ 1),
376 srcBufferPtr
+ stride
* (srcSize
.height
- 1 - i
));
382 static DataSourceSurface
* AlphaPremultiplyDataSourceSurface(
383 DataSourceSurface
* aSurface
, const bool forward
= true) {
384 MOZ_ASSERT(aSurface
);
386 DataSourceSurface::MappedSurface surfaceMap
;
388 if (aSurface
->Map(DataSourceSurface::MapType::READ_WRITE
, &surfaceMap
)) {
390 PremultiplyData(surfaceMap
.mData
, surfaceMap
.mStride
,
391 aSurface
->GetFormat(), surfaceMap
.mData
,
392 surfaceMap
.mStride
, aSurface
->GetFormat(),
393 aSurface
->GetSize());
395 UnpremultiplyData(surfaceMap
.mData
, surfaceMap
.mStride
,
396 aSurface
->GetFormat(), surfaceMap
.mData
,
397 surfaceMap
.mStride
, aSurface
->GetFormat(),
398 aSurface
->GetSize());
410 * Encapsulate the given _aSurface_ into a layers::SourceSurfaceImage.
412 static already_AddRefed
<layers::Image
> CreateImageFromSurface(
413 SourceSurface
* aSurface
) {
414 MOZ_ASSERT(aSurface
);
415 RefPtr
<layers::SourceSurfaceImage
> image
=
416 new layers::SourceSurfaceImage(aSurface
->GetSize(), aSurface
);
417 return image
.forget();
421 * CreateImageFromRawData(), CreateSurfaceFromRawData() and
422 * CreateImageFromRawDataInMainThreadSyncTask are helpers for
423 * create-from-ImageData case
425 static already_AddRefed
<SourceSurface
> CreateSurfaceFromRawData(
426 const gfx::IntSize
& aSize
, uint32_t aStride
, gfx::SurfaceFormat aFormat
,
427 uint8_t* aBuffer
, uint32_t aBufferLength
, const Maybe
<IntRect
>& aCropRect
,
428 const ImageBitmapOptions
& aOptions
) {
429 MOZ_ASSERT(!aSize
.IsEmpty());
432 // Wrap the source buffer into a SourceSurface.
433 RefPtr
<DataSourceSurface
> dataSurface
=
434 Factory::CreateWrappingDataSourceSurface(aBuffer
, aStride
, aSize
,
437 if (NS_WARN_IF(!dataSurface
)) {
441 // The temporary cropRect variable is equal to the size of source buffer if we
442 // do not need to crop, or it equals to the given cropping size.
443 const IntRect cropRect
=
444 aCropRect
.valueOr(IntRect(0, 0, aSize
.width
, aSize
.height
));
446 // Copy the source buffer in the _cropRect_ area into a new SourceSurface.
447 RefPtr
<DataSourceSurface
> result
=
448 CropAndCopyDataSourceSurface(dataSurface
, cropRect
);
449 if (NS_WARN_IF(!result
)) {
453 if (aOptions
.mImageOrientation
== ImageOrientation::FlipY
) {
454 result
= FlipYDataSourceSurface(result
);
456 if (NS_WARN_IF(!result
)) {
461 if (aOptions
.mPremultiplyAlpha
== PremultiplyAlpha::Premultiply
) {
462 result
= AlphaPremultiplyDataSourceSurface(result
);
464 if (NS_WARN_IF(!result
)) {
469 if (aOptions
.mResizeWidth
.WasPassed() || aOptions
.mResizeHeight
.WasPassed()) {
470 dataSurface
= result
->GetDataSurface();
471 result
= ScaleDataSourceSurface(dataSurface
, aOptions
);
472 if (NS_WARN_IF(!result
)) {
477 return result
.forget();
480 static already_AddRefed
<layers::Image
> CreateImageFromRawData(
481 const gfx::IntSize
& aSize
, uint32_t aStride
, gfx::SurfaceFormat aFormat
,
482 uint8_t* aBuffer
, uint32_t aBufferLength
, const Maybe
<IntRect
>& aCropRect
,
483 const ImageBitmapOptions
& aOptions
) {
484 MOZ_ASSERT(NS_IsMainThread());
486 // Copy and crop the source buffer into a SourceSurface.
487 RefPtr
<SourceSurface
> rgbaSurface
= CreateSurfaceFromRawData(
488 aSize
, aStride
, aFormat
, aBuffer
, aBufferLength
, aCropRect
, aOptions
);
490 if (NS_WARN_IF(!rgbaSurface
)) {
494 // Convert RGBA to BGRA
495 RefPtr
<DataSourceSurface
> rgbaDataSurface
= rgbaSurface
->GetDataSurface();
496 DataSourceSurface::ScopedMap
rgbaMap(rgbaDataSurface
,
497 DataSourceSurface::READ
);
498 if (NS_WARN_IF(!rgbaMap
.IsMapped())) {
502 RefPtr
<DataSourceSurface
> bgraDataSurface
=
503 Factory::CreateDataSourceSurfaceWithStride(rgbaDataSurface
->GetSize(),
504 SurfaceFormat::B8G8R8A8
,
505 rgbaMap
.GetStride());
506 if (NS_WARN_IF(!bgraDataSurface
)) {
510 DataSourceSurface::ScopedMap
bgraMap(bgraDataSurface
,
511 DataSourceSurface::WRITE
);
512 if (NS_WARN_IF(!bgraMap
.IsMapped())) {
516 SwizzleData(rgbaMap
.GetData(), rgbaMap
.GetStride(), SurfaceFormat::R8G8B8A8
,
517 bgraMap
.GetData(), bgraMap
.GetStride(), SurfaceFormat::B8G8R8A8
,
518 bgraDataSurface
->GetSize());
520 // Create an Image from the BGRA SourceSurface.
521 return CreateImageFromSurface(bgraDataSurface
);
525 * This is a synchronous task.
526 * This class is used to create a layers::SourceSurfaceImage from raw data in
527 * the main thread. While creating an ImageBitmap from an ImageData, we need to
528 * create a SouceSurface from the ImageData's raw data and then set the
529 * SourceSurface into a layers::SourceSurfaceImage. However, the
530 * layers::SourceSurfaceImage asserts the setting operation in the main thread,
531 * so if we are going to create an ImageBitmap from an ImageData off the main
532 * thread, we post an event to the main thread to create a
533 * layers::SourceSurfaceImage from an ImageData's raw data.
535 class CreateImageFromRawDataInMainThreadSyncTask final
536 : public WorkerMainThreadRunnable
{
538 CreateImageFromRawDataInMainThreadSyncTask(
539 uint8_t* aBuffer
, uint32_t aBufferLength
, uint32_t aStride
,
540 gfx::SurfaceFormat aFormat
, const gfx::IntSize
& aSize
,
541 const Maybe
<IntRect
>& aCropRect
, layers::Image
** aImage
,
542 const ImageBitmapOptions
& aOptions
)
543 : WorkerMainThreadRunnable(
544 GetCurrentThreadWorkerPrivate(),
545 "ImageBitmap :: Create Image from Raw Data"_ns
),
548 mBufferLength(aBufferLength
),
552 mCropRect(aCropRect
),
554 MOZ_ASSERT(!(*aImage
),
555 "Don't pass an existing Image into "
556 "CreateImageFromRawDataInMainThreadSyncTask.");
559 bool MainThreadRun() override
{
560 RefPtr
<layers::Image
> image
= CreateImageFromRawData(
561 mSize
, mStride
, mFormat
, mBuffer
, mBufferLength
, mCropRect
, mOptions
);
563 if (NS_WARN_IF(!image
)) {
567 image
.forget(mImage
);
573 layers::Image
** mImage
;
575 uint32_t mBufferLength
;
577 gfx::SurfaceFormat mFormat
;
579 const Maybe
<IntRect
>& mCropRect
;
580 const ImageBitmapOptions mOptions
;
584 * A wrapper to the nsLayoutUtils::SurfaceFromElement() function followed by the
587 template <class ElementType
>
588 static already_AddRefed
<SourceSurface
> GetSurfaceFromElement(
589 nsIGlobalObject
* aGlobal
, ElementType
& aElement
, bool* aWriteOnly
,
590 const ImageBitmapOptions
& aOptions
, gfxAlphaType
* aAlphaType
,
592 uint32_t flags
= nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE
|
593 nsLayoutUtils::SFE_ORIENTATION_FROM_IMAGE
|
594 nsLayoutUtils::SFE_EXACT_SIZE_SURFACE
;
596 // by default surfaces have premultiplied alpha
597 // attempt to get non premultiplied if required
598 if (aOptions
.mPremultiplyAlpha
== PremultiplyAlpha::None
) {
599 flags
|= nsLayoutUtils::SFE_ALLOW_NON_PREMULT
;
602 if (aOptions
.mColorSpaceConversion
== ColorSpaceConversion::None
&&
603 aElement
.IsHTMLElement(nsGkAtoms::img
)) {
604 flags
|= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION
;
607 Maybe
<int32_t> resizeWidth
, resizeHeight
;
608 if (aOptions
.mResizeWidth
.WasPassed()) {
609 if (!CheckedInt32(aOptions
.mResizeWidth
.Value()).isValid()) {
610 aRv
.ThrowInvalidStateError("resizeWidth is too large");
613 resizeWidth
.emplace(aOptions
.mResizeWidth
.Value());
615 if (aOptions
.mResizeHeight
.WasPassed()) {
616 if (!CheckedInt32(aOptions
.mResizeHeight
.Value()).isValid()) {
617 aRv
.ThrowInvalidStateError("resizeHeight is too large");
620 resizeHeight
.emplace(aOptions
.mResizeHeight
.Value());
622 SurfaceFromElementResult res
= nsLayoutUtils::SurfaceFromElement(
623 &aElement
, resizeWidth
, resizeHeight
, flags
);
625 RefPtr
<SourceSurface
> surface
= res
.GetSourceSurface();
626 if (NS_WARN_IF(!surface
)) {
627 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
631 *aWriteOnly
= res
.mIsWriteOnly
;
632 *aAlphaType
= res
.mAlphaType
;
634 return surface
.forget();
637 ImageBitmap::ImageBitmap(nsIGlobalObject
* aGlobal
, layers::Image
* aData
,
638 bool aWriteOnly
, gfxAlphaType aAlphaType
)
642 mPictureRect(aData
->GetPictureRect()),
643 mAlphaType(aAlphaType
),
644 mAllocatedImageData(false),
645 mWriteOnly(aWriteOnly
) {
646 MOZ_ASSERT(aData
, "aData is null in ImageBitmap constructor.");
648 StaticMutexAutoLock
lock(sShutdownMutex
);
649 if (!sShutdownObserver
&&
650 !AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdown
)) {
651 sShutdownObserver
= new ImageBitmapShutdownObserver();
653 if (sShutdownObserver
) {
654 mShutdownRunnable
= sShutdownObserver
->Track(this);
658 ImageBitmap::~ImageBitmap() {
659 StaticMutexAutoLock
lock(sShutdownMutex
);
660 if (mShutdownRunnable
) {
661 mShutdownRunnable
->mImageBitmap
= nullptr;
663 mShutdownRunnable
= nullptr;
664 if (sShutdownObserver
) {
665 sShutdownObserver
->Untrack(this);
669 JSObject
* ImageBitmap::WrapObject(JSContext
* aCx
,
670 JS::Handle
<JSObject
*> aGivenProto
) {
671 return ImageBitmap_Binding::Wrap(aCx
, this, aGivenProto
);
674 void ImageBitmap::Close() {
677 mPictureRect
.SetEmpty();
680 void ImageBitmap::OnShutdown() { Close(); }
682 void ImageBitmap::SetPictureRect(const IntRect
& aRect
, ErrorResult
& aRv
) {
683 mPictureRect
= FixUpNegativeDimension(aRect
, aRv
);
687 SurfaceFromElementResult
ImageBitmap::SurfaceFrom(uint32_t aSurfaceFlags
) {
688 SurfaceFromElementResult sfer
;
694 // An ImageBitmap, not being a DOM element, only has `origin-clean`
695 // (via our `IsWriteOnly`), and does not participate in CORS.
696 // Right now we mark this by setting mCORSUsed to true.
697 sfer
.mCORSUsed
= true;
698 sfer
.mIsWriteOnly
= mWriteOnly
;
701 sfer
.mPrincipal
= mParent
->PrincipalOrNull();
704 IntSize
imageSize(mData
->GetSize());
705 IntRect
imageRect(IntPoint(0, 0), imageSize
);
706 bool hasCropRect
= mPictureRect
.IsEqualEdges(imageRect
);
709 bool(aSurfaceFlags
& nsLayoutUtils::SFE_EXACT_SIZE_SURFACE
);
710 bool allowNonPremult
=
711 bool(aSurfaceFlags
& nsLayoutUtils::SFE_ALLOW_NON_PREMULT
);
712 bool allowUncropped
=
713 bool(aSurfaceFlags
& nsLayoutUtils::SFE_ALLOW_UNCROPPED_UNSCALED
);
714 bool requiresPremult
=
715 !allowNonPremult
&& mAlphaType
== gfxAlphaType::NonPremult
;
716 bool requiresCrop
= !allowUncropped
&& hasCropRect
;
717 if (wantExactSize
|| requiresPremult
|| requiresCrop
|| mSurface
) {
718 RefPtr
<DrawTarget
> dt
= Factory::CreateDrawTarget(
719 BackendType::SKIA
, IntSize(1, 1), SurfaceFormat::B8G8R8A8
);
720 sfer
.mSourceSurface
= PrepareForDrawTarget(dt
);
722 if (!sfer
.mSourceSurface
) {
726 MOZ_ASSERT(mSurface
);
728 sfer
.mSize
= sfer
.mIntrinsicSize
= sfer
.mSourceSurface
->GetSize();
729 sfer
.mHasSize
= true;
730 sfer
.mAlphaType
= IsOpaque(sfer
.mSourceSurface
->GetFormat())
731 ? gfxAlphaType::Opaque
732 : gfxAlphaType::Premult
;
737 IntRect imagePortion
= imageRect
.Intersect(mPictureRect
);
739 // the crop lies entirely outside the surface area, nothing to draw
740 if (imagePortion
.IsEmpty()) {
744 sfer
.mCropRect
= Some(imagePortion
);
745 sfer
.mIntrinsicSize
= imagePortion
.Size();
747 sfer
.mIntrinsicSize
= imageSize
;
750 sfer
.mSize
= imageSize
;
751 sfer
.mHasSize
= true;
752 sfer
.mAlphaType
= mAlphaType
;
753 sfer
.mLayersImage
= mData
;
758 * The functionality of PrepareForDrawTarget method:
759 * (1) Get a SourceSurface from the mData (which is a layers::Image).
760 * (2) Convert the SourceSurface to format B8G8R8A8 if the original format is
761 * R8G8B8, B8G8R8, HSV or Lab.
762 * Note: if the original format is A8 or Depth, then return null directly.
763 * (3) Do cropping if the size of SourceSurface does not equal to the
765 * (4) Pre-multiply alpha if needed.
767 already_AddRefed
<SourceSurface
> ImageBitmap::PrepareForDrawTarget(
768 gfx::DrawTarget
* aTarget
) {
775 // We may have a cached surface optimized for the last DrawTarget. If it was
776 // created via DrawTargetRecording, it may not be suitable for use with the
777 // current DrawTarget, as we can only do readbacks via the
778 // PersistentBufferProvider for the canvas, and not for individual
779 // SourceSurfaceRecording objects. In such situations, the only thing we can
780 // do is clear our cache and extract a new SourceSurface from mData.
781 if (mSurface
&& mSurface
->GetType() == gfx::SurfaceType::RECORDING
&&
782 !aTarget
->IsRecording()) {
783 RefPtr
<gfx::DataSourceSurface
> dataSurface
= mSurface
->GetDataSurface();
789 // If we have a surface, then it is already cropped and premultiplied.
791 return do_AddRef(mSurface
);
794 RefPtr
<gfx::SourceSurface
> surface
= mData
->GetAsSourceSurface();
795 if (NS_WARN_IF(!surface
)) {
799 IntRect
surfRect(0, 0, surface
->GetSize().width
, surface
->GetSize().height
);
800 SurfaceFormat format
= surface
->GetFormat();
801 bool isOpaque
= IsOpaque(format
);
802 bool mustPremultiply
= mAlphaType
== gfxAlphaType::NonPremult
&& !isOpaque
;
803 bool hasCopied
= false;
805 // Check if we still need to crop our surface
806 if (!mPictureRect
.IsEqualEdges(surfRect
)) {
807 IntRect surfPortion
= surfRect
.Intersect(mPictureRect
);
809 // the crop lies entirely outside the surface area, nothing to draw
810 if (surfPortion
.IsEmpty()) {
814 IntPoint
dest(std::max(0, surfPortion
.X() - mPictureRect
.X()),
815 std::max(0, surfPortion
.Y() - mPictureRect
.Y()));
817 // We must initialize this target with mPictureRect.Size() because the
818 // specification states that if the cropping area is given, then return an
819 // ImageBitmap with the size equals to the cropping area. Ensure that the
820 // format matches the surface, even though the DT type is similar to the
821 // destination, i.e. blending an alpha surface to an opaque DT. However,
822 // any pixels outside the surface portion must be filled with transparent
823 // black, even if the surface is opaque, so force to an alpha format in
825 if (!surfPortion
.IsEqualEdges(mPictureRect
) && isOpaque
) {
826 format
= SurfaceFormat::B8G8R8A8
;
829 // If we need to pre-multiply the alpha, then we need to be able to
830 // read/write the pixel data, and as such, we want to avoid creating a
831 // SourceSurfaceRecording for the same reasons earlier in this method.
832 RefPtr
<DrawTarget
> cropped
;
833 if (mustPremultiply
&& aTarget
->IsRecording()) {
834 cropped
= Factory::CreateDrawTarget(BackendType::SKIA
,
835 mPictureRect
.Size(), format
);
837 cropped
= aTarget
->CreateSimilarDrawTarget(mPictureRect
.Size(), format
);
840 if (NS_WARN_IF(!cropped
)) {
844 cropped
->CopySurface(surface
, surfPortion
, dest
);
845 surface
= cropped
->GetBackingSurface();
847 if (NS_WARN_IF(!surface
)) {
852 // Pre-multiply alpha here.
853 // Ignore this step if the source surface does not have alpha channel; this
854 // kind of source surfaces might come form layers::PlanarYCbCrImage. If the
855 // crop rect imputed transparency, and the original surface was opaque, we
856 // can skip doing the pre-multiply here as the only transparent pixels are
857 // already transparent black.
858 if (mustPremultiply
) {
859 MOZ_ASSERT(surface
->GetFormat() == SurfaceFormat::R8G8B8A8
||
860 surface
->GetFormat() == SurfaceFormat::B8G8R8A8
||
861 surface
->GetFormat() == SurfaceFormat::A8R8G8B8
);
863 RefPtr
<DataSourceSurface
> srcSurface
= surface
->GetDataSurface();
864 if (NS_WARN_IF(!srcSurface
)) {
869 // If we are using our own local copy, then we can safely premultiply in
870 // place without an additional allocation.
871 DataSourceSurface::ScopedMap
map(srcSurface
,
872 DataSourceSurface::READ_WRITE
);
873 if (!map
.IsMapped()) {
874 gfxCriticalError() << "Failed to map surface for premultiplying alpha.";
878 PremultiplyData(map
.GetData(), map
.GetStride(), srcSurface
->GetFormat(),
879 map
.GetData(), map
.GetStride(), srcSurface
->GetFormat(),
882 RefPtr
<DataSourceSurface
> dstSurface
= Factory::CreateDataSourceSurface(
883 srcSurface
->GetSize(), srcSurface
->GetFormat());
884 if (NS_WARN_IF(!dstSurface
)) {
888 DataSourceSurface::ScopedMap
srcMap(srcSurface
, DataSourceSurface::READ
);
889 if (!srcMap
.IsMapped()) {
891 << "Failed to map source surface for premultiplying alpha.";
895 DataSourceSurface::ScopedMap
dstMap(dstSurface
, DataSourceSurface::WRITE
);
896 if (!dstMap
.IsMapped()) {
898 << "Failed to map destination surface for premultiplying alpha.";
902 PremultiplyData(srcMap
.GetData(), srcMap
.GetStride(),
903 srcSurface
->GetFormat(), dstMap
.GetData(),
904 dstMap
.GetStride(), dstSurface
->GetFormat(),
905 dstSurface
->GetSize());
907 surface
= std::move(dstSurface
);
911 // Replace our surface with one optimized for the target we're about to draw
912 // to, under the assumption it'll likely be drawn again to that target.
913 // This call should be a no-op for already-optimized surfaces
914 mSurface
= aTarget
->OptimizeSourceSurface(surface
);
916 mSurface
= std::move(surface
);
918 return do_AddRef(mSurface
);
921 already_AddRefed
<layers::Image
> ImageBitmap::TransferAsImage() {
922 RefPtr
<layers::Image
> image
= mData
;
924 return image
.forget();
927 UniquePtr
<ImageBitmapCloneData
> ImageBitmap::ToCloneData() const {
929 // A closed image cannot be cloned.
933 UniquePtr
<ImageBitmapCloneData
> result(new ImageBitmapCloneData());
934 result
->mPictureRect
= mPictureRect
;
935 result
->mAlphaType
= mAlphaType
;
936 RefPtr
<SourceSurface
> surface
= mData
->GetAsSourceSurface();
938 // It might just not be possible to get/map the surface. (e.g. from another
943 result
->mSurface
= surface
->GetDataSurface();
944 MOZ_ASSERT(result
->mSurface
);
945 result
->mWriteOnly
= mWriteOnly
;
951 already_AddRefed
<ImageBitmap
> ImageBitmap::CreateFromSourceSurface(
952 nsIGlobalObject
* aGlobal
, gfx::SourceSurface
* aSource
, ErrorResult
& aRv
) {
953 RefPtr
<layers::Image
> data
= CreateImageFromSurface(aSource
);
954 RefPtr
<ImageBitmap
> ret
=
955 new ImageBitmap(aGlobal
, data
, false /* writeOnly */);
956 ret
->mAllocatedImageData
= true;
961 already_AddRefed
<ImageBitmap
> ImageBitmap::CreateFromCloneData(
962 nsIGlobalObject
* aGlobal
, ImageBitmapCloneData
* aData
) {
963 RefPtr
<layers::Image
> data
= CreateImageFromSurface(aData
->mSurface
);
965 RefPtr
<ImageBitmap
> ret
=
966 new ImageBitmap(aGlobal
, data
, aData
->mWriteOnly
, aData
->mAlphaType
);
968 ret
->mAllocatedImageData
= true;
971 ret
->SetPictureRect(aData
->mPictureRect
, rv
);
976 already_AddRefed
<ImageBitmap
> ImageBitmap::CreateFromOffscreenCanvas(
977 nsIGlobalObject
* aGlobal
, OffscreenCanvas
& aOffscreenCanvas
,
979 // Check write-only mode.
980 bool writeOnly
= aOffscreenCanvas
.IsWriteOnly();
981 uint32_t flags
= nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE
|
982 nsLayoutUtils::SFE_EXACT_SIZE_SURFACE
;
984 SurfaceFromElementResult res
=
985 nsLayoutUtils::SurfaceFromOffscreenCanvas(&aOffscreenCanvas
, flags
);
987 RefPtr
<SourceSurface
> surface
= res
.GetSourceSurface();
989 if (NS_WARN_IF(!surface
)) {
990 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
994 RefPtr
<layers::Image
> data
= CreateImageFromSurface(surface
);
996 RefPtr
<ImageBitmap
> ret
= new ImageBitmap(aGlobal
, data
, writeOnly
);
998 ret
->mAllocatedImageData
= true;
1000 return ret
.forget();
1004 already_AddRefed
<ImageBitmap
> ImageBitmap::CreateImageBitmapInternal(
1005 nsIGlobalObject
* aGlobal
, gfx::SourceSurface
* aSurface
,
1006 const Maybe
<IntRect
>& aCropRect
, const ImageBitmapOptions
& aOptions
,
1007 const bool aWriteOnly
, const bool aAllocatedImageData
, const bool aMustCopy
,
1008 const gfxAlphaType aAlphaType
, ErrorResult
& aRv
) {
1009 bool needToReportMemoryAllocation
= aAllocatedImageData
;
1010 const IntSize srcSize
= aSurface
->GetSize();
1012 aCropRect
.valueOr(IntRect(0, 0, srcSize
.width
, srcSize
.height
));
1014 RefPtr
<SourceSurface
> surface
= aSurface
;
1015 RefPtr
<DataSourceSurface
> dataSurface
;
1017 // handle alpha premultiplication if surface not of correct type
1019 gfxAlphaType alphaType
= aAlphaType
;
1020 bool mustCopy
= aMustCopy
;
1021 bool requiresPremultiply
= false;
1022 bool requiresUnpremultiply
= false;
1024 if (!IsOpaque(surface
->GetFormat())) {
1025 if (aAlphaType
== gfxAlphaType::Premult
&&
1026 aOptions
.mPremultiplyAlpha
== PremultiplyAlpha::None
) {
1027 requiresUnpremultiply
= true;
1028 alphaType
= gfxAlphaType::NonPremult
;
1029 if (!aAllocatedImageData
) {
1032 } else if (aAlphaType
== gfxAlphaType::NonPremult
&&
1033 aOptions
.mPremultiplyAlpha
== PremultiplyAlpha::Premultiply
) {
1034 requiresPremultiply
= true;
1035 alphaType
= gfxAlphaType::Premult
;
1036 if (!aAllocatedImageData
) {
1043 * if we don't own the data and need to create a new buffer to flip Y.
1045 * we need to crop and flip, where crop must come first.
1047 * Or the caller demands a copy (WebGL contexts).
1049 if ((aOptions
.mImageOrientation
== ImageOrientation::FlipY
&&
1050 (!aAllocatedImageData
|| aCropRect
.isSome())) ||
1052 dataSurface
= surface
->GetDataSurface();
1054 dataSurface
= CropAndCopyDataSourceSurface(dataSurface
, cropRect
);
1055 if (NS_WARN_IF(!dataSurface
)) {
1056 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1060 surface
= dataSurface
;
1061 cropRect
.SetRect(0, 0, dataSurface
->GetSize().width
,
1062 dataSurface
->GetSize().height
);
1063 needToReportMemoryAllocation
= true;
1066 // flip image in Y direction
1067 if (aOptions
.mImageOrientation
== ImageOrientation::FlipY
) {
1069 dataSurface
= surface
->GetDataSurface();
1072 surface
= FlipYDataSourceSurface(dataSurface
);
1073 if (NS_WARN_IF(!surface
)) {
1078 if (requiresPremultiply
) {
1080 dataSurface
= surface
->GetDataSurface();
1083 surface
= AlphaPremultiplyDataSourceSurface(dataSurface
, true);
1084 if (NS_WARN_IF(!surface
)) {
1089 // resize if required
1090 if (aOptions
.mResizeWidth
.WasPassed() || aOptions
.mResizeHeight
.WasPassed()) {
1092 dataSurface
= surface
->GetDataSurface();
1095 surface
= ScaleDataSourceSurface(dataSurface
, aOptions
);
1096 if (NS_WARN_IF(!surface
)) {
1097 aRv
.ThrowInvalidStateError("Failed to create resized image");
1101 needToReportMemoryAllocation
= true;
1102 cropRect
.SetRect(0, 0, surface
->GetSize().width
, surface
->GetSize().height
);
1105 if (requiresUnpremultiply
) {
1107 dataSurface
= surface
->GetDataSurface();
1110 surface
= AlphaPremultiplyDataSourceSurface(dataSurface
, false);
1111 if (NS_WARN_IF(!surface
)) {
1116 // Create an Image from the SourceSurface.
1117 RefPtr
<layers::Image
> data
= CreateImageFromSurface(surface
);
1118 RefPtr
<ImageBitmap
> ret
=
1119 new ImageBitmap(aGlobal
, data
, aWriteOnly
, alphaType
);
1121 if (needToReportMemoryAllocation
) {
1122 ret
->mAllocatedImageData
= true;
1125 // Set the picture rectangle.
1126 ret
->SetPictureRect(cropRect
, aRv
);
1128 return ret
.forget();
1132 already_AddRefed
<ImageBitmap
> ImageBitmap::CreateInternal(
1133 nsIGlobalObject
* aGlobal
, HTMLImageElement
& aImageEl
,
1134 const Maybe
<IntRect
>& aCropRect
, const ImageBitmapOptions
& aOptions
,
1136 // Check if the image element is completely available or not.
1137 if (!aImageEl
.Complete()) {
1138 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1142 bool writeOnly
= true;
1143 gfxAlphaType alphaType
= gfxAlphaType::NonPremult
;
1145 // Get the SourceSurface out from the image element and then do security
1147 RefPtr
<SourceSurface
> surface
= GetSurfaceFromElement(
1148 aGlobal
, aImageEl
, &writeOnly
, aOptions
, &alphaType
, aRv
);
1150 if (NS_WARN_IF(aRv
.Failed())) {
1154 bool needToReportMemoryAllocation
= false;
1155 return CreateImageBitmapInternal(aGlobal
, surface
, aCropRect
, aOptions
,
1156 writeOnly
, needToReportMemoryAllocation
,
1157 false, alphaType
, aRv
);
1160 already_AddRefed
<ImageBitmap
> ImageBitmap::CreateInternal(
1161 nsIGlobalObject
* aGlobal
, SVGImageElement
& aImageEl
,
1162 const Maybe
<IntRect
>& aCropRect
, const ImageBitmapOptions
& aOptions
,
1164 bool writeOnly
= true;
1165 gfxAlphaType alphaType
= gfxAlphaType::NonPremult
;
1167 // Get the SourceSurface out from the image element and then do security
1169 RefPtr
<SourceSurface
> surface
= GetSurfaceFromElement(
1170 aGlobal
, aImageEl
, &writeOnly
, aOptions
, &alphaType
, aRv
);
1172 if (NS_WARN_IF(aRv
.Failed())) {
1176 bool needToReportMemoryAllocation
= false;
1178 return CreateImageBitmapInternal(aGlobal
, surface
, aCropRect
, aOptions
,
1179 writeOnly
, needToReportMemoryAllocation
,
1180 false, alphaType
, aRv
);
1184 already_AddRefed
<ImageBitmap
> ImageBitmap::CreateInternal(
1185 nsIGlobalObject
* aGlobal
, HTMLVideoElement
& aVideoEl
,
1186 const Maybe
<IntRect
>& aCropRect
, const ImageBitmapOptions
& aOptions
,
1188 aVideoEl
.LogVisibility(
1189 mozilla::dom::HTMLVideoElement::CallerAPI::CREATE_IMAGEBITMAP
);
1191 // Check network state.
1192 if (aVideoEl
.NetworkState() == NETWORK_EMPTY
) {
1193 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1197 // Check ready state.
1198 // Cannot be HTMLMediaElement::HAVE_NOTHING or
1199 // HTMLMediaElement::HAVE_METADATA.
1200 if (aVideoEl
.ReadyState() <= HAVE_METADATA
) {
1201 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1206 nsCOMPtr
<nsIPrincipal
> principal
= aVideoEl
.GetCurrentVideoPrincipal();
1207 bool hadCrossOriginRedirects
= aVideoEl
.HadCrossOriginRedirects();
1208 bool CORSUsed
= aVideoEl
.GetCORSMode() != CORS_NONE
;
1209 bool writeOnly
= CanvasUtils::CheckWriteOnlySecurity(CORSUsed
, principal
,
1210 hadCrossOriginRedirects
);
1212 // Create ImageBitmap.
1213 RefPtr
<layers::Image
> data
= aVideoEl
.GetCurrentImage();
1215 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
1219 RefPtr
<SourceSurface
> surface
= data
->GetAsSourceSurface();
1221 // preserve original behavior in case of unavailble surface
1222 RefPtr
<ImageBitmap
> ret
= new ImageBitmap(aGlobal
, data
, writeOnly
);
1223 return ret
.forget();
1226 bool needToReportMemoryAllocation
= false;
1228 return CreateImageBitmapInternal(aGlobal
, surface
, aCropRect
, aOptions
,
1229 writeOnly
, needToReportMemoryAllocation
,
1230 false, gfxAlphaType::Premult
, aRv
);
1234 already_AddRefed
<ImageBitmap
> ImageBitmap::CreateInternal(
1235 nsIGlobalObject
* aGlobal
, HTMLCanvasElement
& aCanvasEl
,
1236 const Maybe
<IntRect
>& aCropRect
, const ImageBitmapOptions
& aOptions
,
1238 if (aCanvasEl
.Width() == 0 || aCanvasEl
.Height() == 0) {
1239 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1243 bool writeOnly
= true;
1244 gfxAlphaType alphaType
= gfxAlphaType::NonPremult
;
1246 RefPtr
<SourceSurface
> surface
= GetSurfaceFromElement(
1247 aGlobal
, aCanvasEl
, &writeOnly
, aOptions
, &alphaType
, aRv
);
1249 if (NS_WARN_IF(aRv
.Failed())) {
1254 writeOnly
= aCanvasEl
.IsWriteOnly();
1257 // If the HTMLCanvasElement's rendering context is WebGL/WebGPU,
1258 // then the snapshot we got from the HTMLCanvasElement is
1259 // a DataSourceSurface which is a copy of the rendering context.
1260 // We handle cropping in this case.
1261 bool needToReportMemoryAllocation
= false;
1262 bool mustCopy
= false;
1264 if ((aCanvasEl
.GetCurrentContextType() == CanvasContextType::WebGL1
||
1265 aCanvasEl
.GetCurrentContextType() == CanvasContextType::WebGL2
||
1266 aCanvasEl
.GetCurrentContextType() == CanvasContextType::WebGPU
) &&
1267 aCropRect
.isSome()) {
1271 return CreateImageBitmapInternal(aGlobal
, surface
, aCropRect
, aOptions
,
1272 writeOnly
, needToReportMemoryAllocation
,
1273 mustCopy
, alphaType
, aRv
);
1277 already_AddRefed
<ImageBitmap
> ImageBitmap::CreateInternal(
1278 nsIGlobalObject
* aGlobal
, OffscreenCanvas
& aOffscreenCanvas
,
1279 const Maybe
<IntRect
>& aCropRect
, const ImageBitmapOptions
& aOptions
,
1281 if (aOffscreenCanvas
.Width() == 0) {
1282 aRv
.ThrowInvalidStateError("Passed-in canvas has width 0");
1286 if (aOffscreenCanvas
.Height() == 0) {
1287 aRv
.ThrowInvalidStateError("Passed-in canvas has height 0");
1291 uint32_t flags
= nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE
|
1292 nsLayoutUtils::SFE_EXACT_SIZE_SURFACE
;
1294 // by default surfaces have premultiplied alpha
1295 // attempt to get non premultiplied if required
1296 if (aOptions
.mPremultiplyAlpha
== PremultiplyAlpha::None
) {
1297 flags
|= nsLayoutUtils::SFE_ALLOW_NON_PREMULT
;
1300 SurfaceFromElementResult res
=
1301 nsLayoutUtils::SurfaceFromOffscreenCanvas(&aOffscreenCanvas
, flags
);
1303 RefPtr
<SourceSurface
> surface
= res
.GetSourceSurface();
1304 if (NS_WARN_IF(!surface
)) {
1305 aRv
.ThrowInvalidStateError("Passed-in canvas failed to create snapshot");
1309 gfxAlphaType alphaType
= res
.mAlphaType
;
1310 bool writeOnly
= res
.mIsWriteOnly
;
1312 // If the OffscreenCanvas's rendering context is WebGL/WebGPU, then the
1313 // snapshot we got from the OffscreenCanvas is a DataSourceSurface which
1314 // is a copy of the rendering context. We handle cropping in this case.
1315 bool needToReportMemoryAllocation
= false;
1317 aCropRect
.isSome() &&
1318 (aOffscreenCanvas
.GetContextType() == CanvasContextType::WebGL1
||
1319 aOffscreenCanvas
.GetContextType() == CanvasContextType::WebGL2
||
1320 aOffscreenCanvas
.GetContextType() == CanvasContextType::WebGPU
);
1322 return CreateImageBitmapInternal(aGlobal
, surface
, aCropRect
, aOptions
,
1323 writeOnly
, needToReportMemoryAllocation
,
1324 mustCopy
, alphaType
, aRv
);
1328 already_AddRefed
<ImageBitmap
> ImageBitmap::CreateInternal(
1329 nsIGlobalObject
* aGlobal
, ImageData
& aImageData
,
1330 const Maybe
<IntRect
>& aCropRect
, const ImageBitmapOptions
& aOptions
,
1332 // Copy data into SourceSurface.
1333 RootedSpiderMonkeyInterface
<Uint8ClampedArray
> array(RootingCx());
1334 if (!array
.Init(aImageData
.GetDataObject())) {
1335 aRv
.ThrowInvalidStateError(
1336 "Failed to extract Uint8ClampedArray from ImageData (security check "
1340 const SurfaceFormat FORMAT
= SurfaceFormat::R8G8B8A8
;
1341 // ImageData's underlying data is not alpha-premultiplied.
1342 auto alphaType
= (aOptions
.mPremultiplyAlpha
== PremultiplyAlpha::Premultiply
)
1343 ? gfxAlphaType::Premult
1344 : gfxAlphaType::NonPremult
;
1346 const uint32_t BYTES_PER_PIXEL
= BytesPerPixel(FORMAT
);
1347 const uint32_t imageWidth
= aImageData
.Width();
1348 const uint32_t imageHeight
= aImageData
.Height();
1349 const uint32_t imageStride
= imageWidth
* BYTES_PER_PIXEL
;
1350 const gfx::IntSize
imageSize(imageWidth
, imageHeight
);
1352 // Check the ImageData is neutered or not.
1353 if (imageWidth
== 0 || imageHeight
== 0) {
1354 aRv
.ThrowInvalidStateError("Passed-in image is empty");
1358 return array
.ProcessFixedData(
1359 [&](const Span
<const uint8_t>& aData
) -> already_AddRefed
<ImageBitmap
> {
1360 const uint32_t dataLength
= aData
.Length();
1361 if ((imageWidth
* imageHeight
* BYTES_PER_PIXEL
) != dataLength
) {
1362 aRv
.ThrowInvalidStateError("Data size / image format mismatch");
1366 // Create and Crop the raw data into a layers::Image
1367 RefPtr
<layers::Image
> data
;
1369 uint8_t* fixedData
= const_cast<uint8_t*>(aData
.Elements());
1371 if (NS_IsMainThread()) {
1373 CreateImageFromRawData(imageSize
, imageStride
, FORMAT
, fixedData
,
1374 dataLength
, aCropRect
, aOptions
);
1376 RefPtr
<CreateImageFromRawDataInMainThreadSyncTask
> task
=
1377 new CreateImageFromRawDataInMainThreadSyncTask(
1378 fixedData
, dataLength
, imageStride
, FORMAT
, imageSize
,
1379 aCropRect
, getter_AddRefs(data
), aOptions
);
1380 task
->Dispatch(Canceling
, aRv
);
1383 if (NS_WARN_IF(!data
)) {
1384 aRv
.ThrowInvalidStateError("Failed to create internal image");
1388 // Create an ImageBitmap.
1389 RefPtr
<ImageBitmap
> ret
=
1390 new ImageBitmap(aGlobal
, data
, false /* write-only */, alphaType
);
1391 ret
->mAllocatedImageData
= true;
1393 // The cropping information has been handled in the
1394 // CreateImageFromRawData() function.
1396 return ret
.forget();
1401 already_AddRefed
<ImageBitmap
> ImageBitmap::CreateInternal(
1402 nsIGlobalObject
* aGlobal
, CanvasRenderingContext2D
& aCanvasCtx
,
1403 const Maybe
<IntRect
>& aCropRect
, const ImageBitmapOptions
& aOptions
,
1405 nsCOMPtr
<nsPIDOMWindowInner
> win
= do_QueryInterface(aGlobal
);
1406 nsGlobalWindowInner
* window
= nsGlobalWindowInner::Cast(win
);
1407 if (NS_WARN_IF(!window
) || !window
->GetExtantDoc()) {
1408 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1412 window
->GetExtantDoc()->WarnOnceAbout(
1413 DeprecatedOperations::eCreateImageBitmapCanvasRenderingContext2D
);
1415 // Check write-only mode.
1417 aCanvasCtx
.GetCanvas()->IsWriteOnly() || aCanvasCtx
.IsWriteOnly();
1419 RefPtr
<SourceSurface
> surface
= aCanvasCtx
.GetSurfaceSnapshot();
1421 if (NS_WARN_IF(!surface
)) {
1422 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
1426 const IntSize surfaceSize
= surface
->GetSize();
1427 if (surfaceSize
.width
== 0 || surfaceSize
.height
== 0) {
1428 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1432 bool needToReportMemoryAllocation
= true;
1434 return CreateImageBitmapInternal(aGlobal
, surface
, aCropRect
, aOptions
,
1435 writeOnly
, needToReportMemoryAllocation
,
1436 false, gfxAlphaType::Premult
, aRv
);
1440 already_AddRefed
<ImageBitmap
> ImageBitmap::CreateInternal(
1441 nsIGlobalObject
* aGlobal
, ImageBitmap
& aImageBitmap
,
1442 const Maybe
<IntRect
>& aCropRect
, const ImageBitmapOptions
& aOptions
,
1444 if (!aImageBitmap
.mData
) {
1445 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1450 RefPtr
<SourceSurface
> surface
;
1451 RefPtr
<DataSourceSurface
> dataSurface
;
1452 gfxAlphaType alphaType
;
1454 bool needToReportMemoryAllocation
= false;
1456 if (aImageBitmap
.mSurface
&&
1457 (dataSurface
= aImageBitmap
.mSurface
->GetDataSurface())) {
1458 // the source imageBitmap already has a cropped surface, and we can get a
1459 // DataSourceSurface from it, so just use it directly
1460 surface
= aImageBitmap
.mSurface
;
1461 cropRect
= aCropRect
.valueOr(IntRect(IntPoint(0, 0), surface
->GetSize()));
1462 alphaType
= IsOpaque(surface
->GetFormat()) ? gfxAlphaType::Opaque
1463 : gfxAlphaType::Premult
;
1465 RefPtr
<layers::Image
> data
= aImageBitmap
.mData
;
1466 surface
= data
->GetAsSourceSurface();
1467 if (NS_WARN_IF(!surface
)) {
1468 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
1472 cropRect
= aImageBitmap
.mPictureRect
;
1473 alphaType
= aImageBitmap
.mAlphaType
;
1474 if (aCropRect
.isSome()) {
1475 // get new crop rect relative to original uncropped surface
1476 IntRect newCropRect
= aCropRect
.ref();
1477 newCropRect
= FixUpNegativeDimension(newCropRect
, aRv
);
1479 newCropRect
.MoveBy(cropRect
.X(), cropRect
.Y());
1481 if (cropRect
.Contains(newCropRect
)) {
1482 // new crop region within existing surface
1483 // safe to just crop this with new rect
1484 cropRect
= newCropRect
;
1486 // crop includes area outside original cropped region
1487 // create new surface cropped by original bitmap crop rect
1488 RefPtr
<DataSourceSurface
> dataSurface
= surface
->GetDataSurface();
1490 surface
= CropAndCopyDataSourceSurface(dataSurface
, cropRect
);
1491 if (NS_WARN_IF(!surface
)) {
1492 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
1495 needToReportMemoryAllocation
= true;
1496 cropRect
= aCropRect
.ref();
1501 return CreateImageBitmapInternal(
1502 aGlobal
, surface
, Some(cropRect
), aOptions
, aImageBitmap
.mWriteOnly
,
1503 needToReportMemoryAllocation
, false, alphaType
, aRv
);
1507 already_AddRefed
<ImageBitmap
> ImageBitmap::CreateInternal(
1508 nsIGlobalObject
* aGlobal
, VideoFrame
& aVideoFrame
,
1509 const Maybe
<IntRect
>& aCropRect
, const ImageBitmapOptions
& aOptions
,
1511 if (aVideoFrame
.CodedWidth() == 0) {
1512 aRv
.ThrowInvalidStateError("Passed-in video frame has width 0");
1516 if (aVideoFrame
.CodedHeight() == 0) {
1517 aRv
.ThrowInvalidStateError("Passed-in video frame has height 0");
1521 uint32_t flags
= nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE
;
1523 // by default surfaces have premultiplied alpha
1524 // attempt to get non premultiplied if required
1525 if (aOptions
.mPremultiplyAlpha
== PremultiplyAlpha::None
) {
1526 flags
|= nsLayoutUtils::SFE_ALLOW_NON_PREMULT
;
1529 SurfaceFromElementResult res
=
1530 nsLayoutUtils::SurfaceFromVideoFrame(&aVideoFrame
, flags
);
1532 RefPtr
<SourceSurface
> surface
= res
.GetSourceSurface();
1533 if (NS_WARN_IF(!surface
)) {
1534 aRv
.ThrowInvalidStateError("Passed-in video frame has no surface data");
1538 gfxAlphaType alphaType
= res
.mAlphaType
;
1539 bool writeOnly
= res
.mIsWriteOnly
;
1540 bool needToReportMemoryAllocation
= false;
1541 bool mustCopy
= false;
1543 return CreateImageBitmapInternal(aGlobal
, surface
, aCropRect
, aOptions
,
1544 writeOnly
, needToReportMemoryAllocation
,
1545 mustCopy
, alphaType
, aRv
);
1548 class FulfillImageBitmapPromise
{
1550 FulfillImageBitmapPromise(Promise
* aPromise
, ImageBitmap
* aImageBitmap
)
1551 : mPromise(aPromise
), mImageBitmap(aImageBitmap
) {
1552 MOZ_ASSERT(aPromise
);
1555 void DoFulfillImageBitmapPromise() { mPromise
->MaybeResolve(mImageBitmap
); }
1558 RefPtr
<Promise
> mPromise
;
1559 RefPtr
<ImageBitmap
> mImageBitmap
;
1562 class FulfillImageBitmapPromiseTask final
: public Runnable
,
1563 public FulfillImageBitmapPromise
{
1565 FulfillImageBitmapPromiseTask(Promise
* aPromise
, ImageBitmap
* aImageBitmap
)
1566 : Runnable("dom::FulfillImageBitmapPromiseTask"),
1567 FulfillImageBitmapPromise(aPromise
, aImageBitmap
) {}
1569 NS_IMETHOD
Run() override
{
1570 DoFulfillImageBitmapPromise();
1575 class FulfillImageBitmapPromiseWorkerTask final
1576 : public WorkerSameThreadRunnable
,
1577 public FulfillImageBitmapPromise
{
1579 FulfillImageBitmapPromiseWorkerTask(Promise
* aPromise
,
1580 ImageBitmap
* aImageBitmap
)
1581 : WorkerSameThreadRunnable(GetCurrentThreadWorkerPrivate()),
1582 FulfillImageBitmapPromise(aPromise
, aImageBitmap
) {}
1584 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
1585 DoFulfillImageBitmapPromise();
1590 static void AsyncFulfillImageBitmapPromise(Promise
* aPromise
,
1591 ImageBitmap
* aImageBitmap
) {
1592 if (NS_IsMainThread()) {
1593 nsCOMPtr
<nsIRunnable
> task
=
1594 new FulfillImageBitmapPromiseTask(aPromise
, aImageBitmap
);
1595 NS_DispatchToCurrentThread(task
); // Actually, to the main-thread.
1597 RefPtr
<FulfillImageBitmapPromiseWorkerTask
> task
=
1598 new FulfillImageBitmapPromiseWorkerTask(aPromise
, aImageBitmap
);
1599 task
->Dispatch(); // Actually, to the current worker-thread.
1603 class CreateImageBitmapFromBlobRunnable
;
1605 class CreateImageBitmapFromBlob final
: public DiscardableRunnable
,
1606 public imgIContainerCallback
,
1607 public nsIInputStreamCallback
{
1608 friend class CreateImageBitmapFromBlobRunnable
;
1611 NS_DECL_ISUPPORTS_INHERITED
1612 NS_DECL_IMGICONTAINERCALLBACK
1613 NS_DECL_NSIINPUTSTREAMCALLBACK
1615 static already_AddRefed
<CreateImageBitmapFromBlob
> Create(
1616 Promise
* aPromise
, nsIGlobalObject
* aGlobal
, Blob
& aBlob
,
1617 const Maybe
<IntRect
>& aCropRect
, nsIEventTarget
* aMainThreadEventTarget
,
1618 const ImageBitmapOptions
& aOptions
);
1620 NS_IMETHOD
Run() override
{
1621 MOZ_ASSERT(IsCurrentThread());
1623 nsresult rv
= StartMimeTypeAndDecodeAndCropBlob();
1624 if (NS_WARN_IF(NS_FAILED(rv
))) {
1625 MimeTypeAndDecodeAndCropBlobCompletedMainThread(nullptr, rv
);
1631 // Called by the WorkerRef.
1632 void WorkerShuttingDown();
1635 CreateImageBitmapFromBlob(Promise
* aPromise
, nsIGlobalObject
* aGlobal
,
1636 already_AddRefed
<nsIInputStream
> aInputStream
,
1637 const Maybe
<IntRect
>& aCropRect
,
1638 nsIEventTarget
* aMainThreadEventTarget
,
1639 const ImageBitmapOptions
& aOptions
)
1640 : DiscardableRunnable("dom::CreateImageBitmapFromBlob"),
1642 mMutex("dom::CreateImageBitmapFromBlob::mMutex"),
1644 mGlobalObject(aGlobal
),
1645 mInputStream(std::move(aInputStream
)),
1646 mCropRect(aCropRect
),
1647 mMainThreadEventTarget(aMainThreadEventTarget
),
1649 mThread(PR_GetCurrentThread()) {}
1651 virtual ~CreateImageBitmapFromBlob() = default;
1653 bool IsCurrentThread() const { return mThread
== PR_GetCurrentThread(); }
1655 // Called on the owning thread.
1656 nsresult
StartMimeTypeAndDecodeAndCropBlob();
1658 // Will be called when the decoding + cropping is completed on the
1659 // main-thread. This could the not the owning thread!
1660 void MimeTypeAndDecodeAndCropBlobCompletedMainThread(layers::Image
* aImage
,
1663 // Will be called when the decoding + cropping is completed on the owning
1665 void MimeTypeAndDecodeAndCropBlobCompletedOwningThread(layers::Image
* aImage
,
1668 // This is called on the main-thread only.
1669 nsresult
MimeTypeAndDecodeAndCropBlob();
1671 // This is called on the main-thread only.
1672 nsresult
DecodeAndCropBlob(const nsACString
& aMimeType
);
1674 // This is called on the main-thread only.
1675 nsresult
GetMimeTypeSync(nsACString
& aMimeType
);
1677 // This is called on the main-thread only.
1678 nsresult
GetMimeTypeAsync();
1680 Mutex mMutex MOZ_UNANNOTATED
;
1682 // The access to this object is protected by mutex but is always nullified on
1683 // the owning thread.
1684 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
1686 // Touched only on the owning thread.
1687 RefPtr
<Promise
> mPromise
;
1689 // Touched only on the owning thread.
1690 nsCOMPtr
<nsIGlobalObject
> mGlobalObject
;
1692 nsCOMPtr
<nsIInputStream
> mInputStream
;
1693 Maybe
<IntRect
> mCropRect
;
1694 nsCOMPtr
<nsIEventTarget
> mMainThreadEventTarget
;
1695 const ImageBitmapOptions mOptions
;
1699 NS_IMPL_ISUPPORTS_INHERITED(CreateImageBitmapFromBlob
, DiscardableRunnable
,
1700 imgIContainerCallback
, nsIInputStreamCallback
)
1702 class CreateImageBitmapFromBlobRunnable
: public WorkerRunnable
{
1704 explicit CreateImageBitmapFromBlobRunnable(WorkerPrivate
* aWorkerPrivate
,
1705 CreateImageBitmapFromBlob
* aTask
,
1706 layers::Image
* aImage
,
1708 : WorkerRunnable(aWorkerPrivate
),
1713 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
1714 mTask
->MimeTypeAndDecodeAndCropBlobCompletedOwningThread(mImage
, mStatus
);
1719 RefPtr
<CreateImageBitmapFromBlob
> mTask
;
1720 RefPtr
<layers::Image
> mImage
;
1724 static void AsyncCreateImageBitmapFromBlob(Promise
* aPromise
,
1725 nsIGlobalObject
* aGlobal
,
1727 const Maybe
<IntRect
>& aCropRect
,
1728 const ImageBitmapOptions
& aOptions
) {
1729 // Let's identify the main-thread event target.
1730 nsCOMPtr
<nsIEventTarget
> mainThreadEventTarget
;
1731 if (NS_IsMainThread()) {
1732 mainThreadEventTarget
= aGlobal
->SerialEventTarget();
1734 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
1735 MOZ_ASSERT(workerPrivate
);
1736 mainThreadEventTarget
= workerPrivate
->MainThreadEventTarget();
1739 RefPtr
<CreateImageBitmapFromBlob
> task
= CreateImageBitmapFromBlob::Create(
1740 aPromise
, aGlobal
, aBlob
, aCropRect
, mainThreadEventTarget
, aOptions
);
1741 if (NS_WARN_IF(!task
)) {
1742 aPromise
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
1746 NS_DispatchToCurrentThread(task
);
1750 already_AddRefed
<Promise
> ImageBitmap::Create(
1751 nsIGlobalObject
* aGlobal
, const ImageBitmapSource
& aSrc
,
1752 const Maybe
<gfx::IntRect
>& aCropRect
, const ImageBitmapOptions
& aOptions
,
1754 MOZ_ASSERT(aGlobal
);
1756 RefPtr
<Promise
> promise
= Promise::Create(aGlobal
, aRv
);
1758 if (NS_WARN_IF(aRv
.Failed())) {
1762 if (aCropRect
.isSome()) {
1763 if (aCropRect
->Width() == 0) {
1764 aRv
.ThrowRangeError(
1765 "The crop rect width passed to createImageBitmap must be nonzero");
1766 return promise
.forget();
1769 if (aCropRect
->Height() == 0) {
1770 aRv
.ThrowRangeError(
1771 "The crop rect height passed to createImageBitmap must be nonzero");
1772 return promise
.forget();
1776 if (aOptions
.mResizeWidth
.WasPassed() && aOptions
.mResizeWidth
.Value() == 0) {
1777 aRv
.ThrowInvalidStateError(
1778 "The resizeWidth passed to createImageBitmap must be nonzero");
1779 return promise
.forget();
1782 if (aOptions
.mResizeHeight
.WasPassed() &&
1783 aOptions
.mResizeHeight
.Value() == 0) {
1784 aRv
.ThrowInvalidStateError(
1785 "The resizeHeight passed to createImageBitmap must be nonzero");
1786 return promise
.forget();
1789 RefPtr
<ImageBitmap
> imageBitmap
;
1791 if (aSrc
.IsHTMLImageElement()) {
1794 "Creating ImageBitmap from HTMLImageElement off the main thread.");
1795 imageBitmap
= CreateInternal(aGlobal
, aSrc
.GetAsHTMLImageElement(),
1796 aCropRect
, aOptions
, aRv
);
1797 } else if (aSrc
.IsSVGImageElement()) {
1800 "Creating ImageBitmap from SVGImageElement off the main thread.");
1801 imageBitmap
= CreateInternal(aGlobal
, aSrc
.GetAsSVGImageElement(),
1802 aCropRect
, aOptions
, aRv
);
1803 } else if (aSrc
.IsHTMLVideoElement()) {
1806 "Creating ImageBitmap from HTMLVideoElement off the main thread.");
1807 imageBitmap
= CreateInternal(aGlobal
, aSrc
.GetAsHTMLVideoElement(),
1808 aCropRect
, aOptions
, aRv
);
1809 } else if (aSrc
.IsHTMLCanvasElement()) {
1812 "Creating ImageBitmap from HTMLCanvasElement off the main thread.");
1813 imageBitmap
= CreateInternal(aGlobal
, aSrc
.GetAsHTMLCanvasElement(),
1814 aCropRect
, aOptions
, aRv
);
1815 } else if (aSrc
.IsOffscreenCanvas()) {
1816 imageBitmap
= CreateInternal(aGlobal
, aSrc
.GetAsOffscreenCanvas(),
1817 aCropRect
, aOptions
, aRv
);
1818 } else if (aSrc
.IsImageData()) {
1819 imageBitmap
= CreateInternal(aGlobal
, aSrc
.GetAsImageData(), aCropRect
,
1821 } else if (aSrc
.IsCanvasRenderingContext2D()) {
1822 MOZ_ASSERT(NS_IsMainThread(),
1823 "Creating ImageBitmap from CanvasRenderingContext2D off the "
1825 imageBitmap
= CreateInternal(aGlobal
, aSrc
.GetAsCanvasRenderingContext2D(),
1826 aCropRect
, aOptions
, aRv
);
1827 } else if (aSrc
.IsImageBitmap()) {
1828 imageBitmap
= CreateInternal(aGlobal
, aSrc
.GetAsImageBitmap(), aCropRect
,
1830 } else if (aSrc
.IsBlob()) {
1831 AsyncCreateImageBitmapFromBlob(promise
, aGlobal
, aSrc
.GetAsBlob(),
1832 aCropRect
, aOptions
);
1833 return promise
.forget();
1834 } else if (aSrc
.IsVideoFrame()) {
1835 imageBitmap
= CreateInternal(aGlobal
, aSrc
.GetAsVideoFrame(), aCropRect
,
1838 MOZ_CRASH("Unsupported type!");
1842 if (!aRv
.Failed()) {
1843 AsyncFulfillImageBitmapPromise(promise
, imageBitmap
);
1846 return promise
.forget();
1850 JSObject
* ImageBitmap::ReadStructuredClone(
1851 JSContext
* aCx
, JSStructuredCloneReader
* aReader
, nsIGlobalObject
* aParent
,
1852 const nsTArray
<RefPtr
<DataSourceSurface
>>& aClonedSurfaces
,
1855 MOZ_ASSERT(aReader
);
1856 // aParent might be null.
1860 uint32_t picRectWidth_
;
1861 uint32_t picRectHeight_
;
1862 uint32_t alphaType_
;
1865 if (!JS_ReadUint32Pair(aReader
, &picRectX_
, &picRectY_
) ||
1866 !JS_ReadUint32Pair(aReader
, &picRectWidth_
, &picRectHeight_
) ||
1867 !JS_ReadUint32Pair(aReader
, &alphaType_
, &writeOnly
)) {
1871 int32_t picRectX
= BitwiseCast
<int32_t>(picRectX_
);
1872 int32_t picRectY
= BitwiseCast
<int32_t>(picRectY_
);
1873 int32_t picRectWidth
= BitwiseCast
<int32_t>(picRectWidth_
);
1874 int32_t picRectHeight
= BitwiseCast
<int32_t>(picRectHeight_
);
1875 const auto alphaType
= BitwiseCast
<gfxAlphaType
>(alphaType_
);
1877 // Create a new ImageBitmap.
1878 MOZ_ASSERT(!aClonedSurfaces
.IsEmpty());
1879 MOZ_ASSERT(aIndex
< aClonedSurfaces
.Length());
1881 // RefPtr<ImageBitmap> needs to go out of scope before toObjectOrNull() is
1882 // called because the static analysis thinks dereferencing XPCOM objects
1883 // can GC (because in some cases it can!), and a return statement with a
1884 // JSObject* type means that JSObject* is on the stack as a raw pointer
1885 // while destructors are running.
1886 JS::Rooted
<JS::Value
> value(aCx
);
1889 if (aIndex
>= aClonedSurfaces
.Length()) {
1893 RefPtr
<layers::Image
> img
= CreateImageFromSurface(aClonedSurfaces
[aIndex
]);
1894 RefPtr
<ImageBitmap
> imageBitmap
=
1895 new ImageBitmap(aParent
, img
, !!writeOnly
, alphaType
);
1898 imageBitmap
->SetPictureRect(
1899 IntRect(picRectX
, picRectY
, picRectWidth
, picRectHeight
), error
);
1900 if (NS_WARN_IF(error
.Failed())) {
1901 error
.SuppressException();
1905 if (!GetOrCreateDOMReflector(aCx
, imageBitmap
, &value
)) {
1909 imageBitmap
->mAllocatedImageData
= true;
1912 return &(value
.toObject());
1916 void ImageBitmap::WriteStructuredClone(
1917 JSStructuredCloneWriter
* aWriter
,
1918 nsTArray
<RefPtr
<DataSourceSurface
>>& aClonedSurfaces
,
1919 ImageBitmap
* aImageBitmap
, ErrorResult
& aRv
) {
1920 MOZ_ASSERT(aWriter
);
1921 MOZ_ASSERT(aImageBitmap
);
1923 if (aImageBitmap
->IsWriteOnly()) {
1924 return aRv
.ThrowDataCloneError("Cannot clone ImageBitmap, is write-only");
1927 if (!aImageBitmap
->mData
) {
1928 // A closed image cannot be cloned.
1929 return aRv
.ThrowDataCloneError("Cannot clone ImageBitmap, is closed");
1932 const uint32_t picRectX
= BitwiseCast
<uint32_t>(aImageBitmap
->mPictureRect
.x
);
1933 const uint32_t picRectY
= BitwiseCast
<uint32_t>(aImageBitmap
->mPictureRect
.y
);
1934 const uint32_t picRectWidth
=
1935 BitwiseCast
<uint32_t>(aImageBitmap
->mPictureRect
.width
);
1936 const uint32_t picRectHeight
=
1937 BitwiseCast
<uint32_t>(aImageBitmap
->mPictureRect
.height
);
1938 const uint32_t alphaType
= BitwiseCast
<uint32_t>(aImageBitmap
->mAlphaType
);
1940 // Indexing the cloned surfaces and send the index to the receiver.
1941 uint32_t index
= aClonedSurfaces
.Length();
1943 if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter
, SCTAG_DOM_IMAGEBITMAP
, index
)) ||
1944 NS_WARN_IF(!JS_WriteUint32Pair(aWriter
, picRectX
, picRectY
)) ||
1945 NS_WARN_IF(!JS_WriteUint32Pair(aWriter
, picRectWidth
, picRectHeight
)) ||
1947 !JS_WriteUint32Pair(aWriter
, alphaType
, aImageBitmap
->mWriteOnly
))) {
1948 return aRv
.ThrowDataCloneError(
1949 "Cannot clone ImageBitmap, failed to write params");
1952 RefPtr
<SourceSurface
> surface
= aImageBitmap
->mData
->GetAsSourceSurface();
1953 if (NS_WARN_IF(!surface
)) {
1954 return aRv
.ThrowDataCloneError("Cannot clone ImageBitmap, no surface");
1957 RefPtr
<DataSourceSurface
> snapshot
= surface
->GetDataSurface();
1958 if (NS_WARN_IF(!snapshot
)) {
1959 return aRv
.ThrowDataCloneError("Cannot clone ImageBitmap, no data surface");
1962 RefPtr
<DataSourceSurface
> dstDataSurface
;
1964 // DataSourceSurfaceD2D1::GetStride() will call EnsureMapped implicitly and
1965 // won't Unmap after exiting function. So instead calling GetStride()
1966 // directly, using ScopedMap to get stride.
1967 DataSourceSurface::ScopedMap
map(snapshot
, DataSourceSurface::READ
);
1968 if (NS_WARN_IF(!map
.IsMapped())) {
1969 return aRv
.ThrowDataCloneError(
1970 "Cannot clone ImageBitmap, cannot map surface");
1973 dstDataSurface
= Factory::CreateDataSourceSurfaceWithStride(
1974 snapshot
->GetSize(), snapshot
->GetFormat(), map
.GetStride(), true);
1976 if (NS_WARN_IF(!dstDataSurface
)) {
1977 return aRv
.ThrowDataCloneError("Cannot clone ImageBitmap, out of memory");
1979 Factory::CopyDataSourceSurface(snapshot
, dstDataSurface
);
1980 aClonedSurfaces
.AppendElement(dstDataSurface
);
1983 size_t ImageBitmap::GetAllocatedSize() const {
1984 if (!mAllocatedImageData
) {
1988 // Calculate how many bytes are used.
1989 if (mData
->GetFormat() == mozilla::ImageFormat::PLANAR_YCBCR
) {
1990 return mData
->AsPlanarYCbCrImage()->GetDataSize();
1993 if (mData
->GetFormat() == mozilla::ImageFormat::NV_IMAGE
) {
1994 return mData
->AsNVImage()->GetBufferSize();
1997 RefPtr
<SourceSurface
> surface
= mData
->GetAsSourceSurface();
1998 if (NS_WARN_IF(!surface
)) {
2002 const int bytesPerPixel
= BytesPerPixel(surface
->GetFormat());
2003 return surface
->GetSize().height
* surface
->GetSize().width
* bytesPerPixel
;
2006 size_t BindingJSObjectMallocBytes(ImageBitmap
* aBitmap
) {
2007 return aBitmap
->GetAllocatedSize();
2011 already_AddRefed
<CreateImageBitmapFromBlob
> CreateImageBitmapFromBlob::Create(
2012 Promise
* aPromise
, nsIGlobalObject
* aGlobal
, Blob
& aBlob
,
2013 const Maybe
<IntRect
>& aCropRect
, nsIEventTarget
* aMainThreadEventTarget
,
2014 const ImageBitmapOptions
& aOptions
) {
2015 // Get the internal stream of the blob.
2016 nsCOMPtr
<nsIInputStream
> stream
;
2018 aBlob
.Impl()->CreateInputStream(getter_AddRefs(stream
), error
);
2019 if (NS_WARN_IF(error
.Failed())) {
2023 if (!NS_InputStreamIsBuffered(stream
)) {
2024 nsCOMPtr
<nsIInputStream
> bufferedStream
;
2025 nsresult rv
= NS_NewBufferedInputStream(getter_AddRefs(bufferedStream
),
2026 stream
.forget(), 4096);
2027 if (NS_WARN_IF(NS_FAILED(rv
))) {
2031 stream
= bufferedStream
;
2034 RefPtr
<CreateImageBitmapFromBlob
> task
= new CreateImageBitmapFromBlob(
2035 aPromise
, aGlobal
, stream
.forget(), aCropRect
, aMainThreadEventTarget
,
2038 // Nothing to do for the main-thread.
2039 if (NS_IsMainThread()) {
2040 return task
.forget();
2043 // Let's use a WorkerRef to keep the worker alive if this is not the
2045 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
2046 MOZ_ASSERT(workerPrivate
);
2048 RefPtr
<StrongWorkerRef
> workerRef
=
2049 StrongWorkerRef::Create(workerPrivate
, "CreateImageBitmapFromBlob",
2050 [task
]() { task
->WorkerShuttingDown(); });
2051 if (NS_WARN_IF(!workerRef
)) {
2055 task
->mWorkerRef
= new ThreadSafeWorkerRef(workerRef
);
2056 return task
.forget();
2059 nsresult
CreateImageBitmapFromBlob::StartMimeTypeAndDecodeAndCropBlob() {
2060 MOZ_ASSERT(IsCurrentThread());
2063 if (!NS_IsMainThread()) {
2064 RefPtr
<CreateImageBitmapFromBlob
> self
= this;
2065 nsCOMPtr
<nsIRunnable
> r
= NS_NewRunnableFunction(
2066 "CreateImageBitmapFromBlob::MimeTypeAndDecodeAndCropBlob", [self
]() {
2067 nsresult rv
= self
->MimeTypeAndDecodeAndCropBlob();
2068 if (NS_WARN_IF(NS_FAILED(rv
))) {
2069 self
->MimeTypeAndDecodeAndCropBlobCompletedMainThread(nullptr, rv
);
2073 return mMainThreadEventTarget
->Dispatch(r
.forget());
2077 return MimeTypeAndDecodeAndCropBlob();
2080 nsresult
CreateImageBitmapFromBlob::MimeTypeAndDecodeAndCropBlob() {
2081 MOZ_ASSERT(NS_IsMainThread());
2083 nsAutoCString mimeType
;
2084 nsresult rv
= GetMimeTypeSync(mimeType
);
2085 if (rv
== NS_BASE_STREAM_WOULD_BLOCK
) {
2086 return GetMimeTypeAsync();
2089 if (NS_WARN_IF(NS_FAILED(rv
))) {
2093 return DecodeAndCropBlob(mimeType
);
2096 nsresult
CreateImageBitmapFromBlob::DecodeAndCropBlob(
2097 const nsACString
& aMimeType
) {
2098 // Get the Component object.
2099 nsCOMPtr
<imgITools
> imgtool
= do_GetService(NS_IMGTOOLS_CID
);
2100 if (NS_WARN_IF(!imgtool
)) {
2101 return NS_ERROR_FAILURE
;
2105 nsresult rv
= imgtool
->DecodeImageAsync(mInputStream
, aMimeType
, this,
2106 mMainThreadEventTarget
);
2107 if (NS_WARN_IF(NS_FAILED(rv
))) {
2114 static nsresult
sniff_cb(nsIInputStream
* aInputStream
, void* aClosure
,
2115 const char* aFromRawSegment
, uint32_t aToOffset
,
2116 uint32_t aCount
, uint32_t* aWriteCount
) {
2117 nsACString
* mimeType
= static_cast<nsACString
*>(aClosure
);
2118 MOZ_ASSERT(mimeType
);
2121 imgLoader::GetMimeTypeFromContent(aFromRawSegment
, aCount
, *mimeType
);
2126 // We don't want to consume data from the stream.
2127 return NS_ERROR_FAILURE
;
2130 nsresult
CreateImageBitmapFromBlob::GetMimeTypeSync(nsACString
& aMimeType
) {
2132 return mInputStream
->ReadSegments(sniff_cb
, &aMimeType
, 128, &dummy
);
2135 nsresult
CreateImageBitmapFromBlob::GetMimeTypeAsync() {
2136 nsCOMPtr
<nsIAsyncInputStream
> asyncInputStream
=
2137 do_QueryInterface(mInputStream
);
2138 if (NS_WARN_IF(!asyncInputStream
)) {
2139 // If the stream is not async, why are we here?
2140 return NS_ERROR_FAILURE
;
2143 return asyncInputStream
->AsyncWait(this, 0, 128, mMainThreadEventTarget
);
2147 CreateImageBitmapFromBlob::OnInputStreamReady(nsIAsyncInputStream
* aStream
) {
2148 // The stream should have data now. Let's start from scratch again.
2149 nsresult rv
= MimeTypeAndDecodeAndCropBlob();
2150 if (NS_WARN_IF(NS_FAILED(rv
))) {
2151 MimeTypeAndDecodeAndCropBlobCompletedMainThread(nullptr, rv
);
2158 CreateImageBitmapFromBlob::OnImageReady(imgIContainer
* aImgContainer
,
2160 MOZ_ASSERT(NS_IsMainThread());
2162 if (NS_FAILED(aStatus
)) {
2163 MimeTypeAndDecodeAndCropBlobCompletedMainThread(nullptr, aStatus
);
2167 MOZ_ASSERT(aImgContainer
);
2169 // Get the surface out.
2170 uint32_t frameFlags
=
2171 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
;
2172 uint32_t whichFrame
= imgIContainer::FRAME_FIRST
;
2174 if (mOptions
.mPremultiplyAlpha
== PremultiplyAlpha::None
) {
2175 frameFlags
|= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA
;
2178 if (mOptions
.mColorSpaceConversion
== ColorSpaceConversion::None
) {
2179 frameFlags
|= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION
;
2182 RefPtr
<SourceSurface
> surface
=
2183 aImgContainer
->GetFrame(whichFrame
, frameFlags
);
2185 if (NS_WARN_IF(!surface
)) {
2186 MimeTypeAndDecodeAndCropBlobCompletedMainThread(
2187 nullptr, NS_ERROR_DOM_INVALID_STATE_ERR
);
2191 // Crop the source surface if needed.
2192 RefPtr
<SourceSurface
> croppedSurface
= surface
;
2193 RefPtr
<DataSourceSurface
> dataSurface
= surface
->GetDataSurface();
2195 // force a copy into unprotected memory as a side effect of
2196 // CropAndCopyDataSourceSurface
2197 bool copyRequired
= mCropRect
.isSome() ||
2198 mOptions
.mImageOrientation
== ImageOrientation::FlipY
;
2201 // The blob is just decoded into a RasterImage and not optimized yet, so the
2202 // _surface_ we get is a DataSourceSurface which wraps the RasterImage's
2205 // The _surface_ might already be optimized so that its type is not
2206 // SurfaceType::DATA. However, we could keep using the generic cropping and
2207 // copying since the decoded buffer is only used in this ImageBitmap so we
2208 // should crop it to save memory usage.
2210 // TODO: Bug1189632 is going to refactor this create-from-blob part to
2211 // decode the blob off the main thread. Re-check if we should do
2212 // cropping at this moment again there.
2215 mCropRect
.isSome() ? mCropRect
.ref() : dataSurface
->GetRect();
2217 croppedSurface
= CropAndCopyDataSourceSurface(dataSurface
, cropRect
);
2218 if (NS_WARN_IF(!croppedSurface
)) {
2219 MimeTypeAndDecodeAndCropBlobCompletedMainThread(
2220 nullptr, NS_ERROR_DOM_INVALID_STATE_ERR
);
2224 dataSurface
= croppedSurface
->GetDataSurface();
2226 if (mCropRect
.isSome()) {
2227 mCropRect
->SetRect(0, 0, dataSurface
->GetSize().width
,
2228 dataSurface
->GetSize().height
);
2232 if (mOptions
.mImageOrientation
== ImageOrientation::FlipY
) {
2233 croppedSurface
= FlipYDataSourceSurface(dataSurface
);
2236 if (mOptions
.mResizeWidth
.WasPassed() || mOptions
.mResizeHeight
.WasPassed()) {
2237 dataSurface
= croppedSurface
->GetDataSurface();
2238 croppedSurface
= ScaleDataSourceSurface(dataSurface
, mOptions
);
2239 if (NS_WARN_IF(!croppedSurface
)) {
2240 MimeTypeAndDecodeAndCropBlobCompletedMainThread(
2241 nullptr, NS_ERROR_DOM_INVALID_STATE_ERR
);
2244 if (mCropRect
.isSome()) {
2245 mCropRect
->SetRect(0, 0, croppedSurface
->GetSize().width
,
2246 croppedSurface
->GetSize().height
);
2250 if (NS_WARN_IF(!croppedSurface
)) {
2251 MimeTypeAndDecodeAndCropBlobCompletedMainThread(
2252 nullptr, NS_ERROR_DOM_INVALID_STATE_ERR
);
2256 // Create an Image from the source surface.
2257 RefPtr
<layers::Image
> image
= CreateImageFromSurface(croppedSurface
);
2259 if (NS_WARN_IF(!image
)) {
2260 MimeTypeAndDecodeAndCropBlobCompletedMainThread(
2261 nullptr, NS_ERROR_DOM_INVALID_STATE_ERR
);
2265 MimeTypeAndDecodeAndCropBlobCompletedMainThread(image
, NS_OK
);
2269 void CreateImageBitmapFromBlob::MimeTypeAndDecodeAndCropBlobCompletedMainThread(
2270 layers::Image
* aImage
, nsresult aStatus
) {
2271 MOZ_ASSERT(NS_IsMainThread());
2273 if (!IsCurrentThread()) {
2274 MutexAutoLock
lock(mMutex
);
2277 // The worker is already gone.
2281 RefPtr
<CreateImageBitmapFromBlobRunnable
> r
=
2282 new CreateImageBitmapFromBlobRunnable(mWorkerRef
->Private(), this,
2288 MimeTypeAndDecodeAndCropBlobCompletedOwningThread(aImage
, aStatus
);
2291 void CreateImageBitmapFromBlob::
2292 MimeTypeAndDecodeAndCropBlobCompletedOwningThread(layers::Image
* aImage
,
2294 MOZ_ASSERT(IsCurrentThread());
2297 // The worker is going to be released soon. No needs to continue.
2301 // Let's release what has to be released on the owning thread.
2302 auto raii
= MakeScopeExit([&] {
2303 // Doing this we also release the worker.
2304 mWorkerRef
= nullptr;
2307 mGlobalObject
= nullptr;
2310 if (NS_WARN_IF(NS_FAILED(aStatus
))) {
2311 mPromise
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
2315 gfxAlphaType alphaType
= gfxAlphaType::Premult
;
2317 if (mOptions
.mPremultiplyAlpha
== PremultiplyAlpha::None
) {
2318 alphaType
= gfxAlphaType::NonPremult
;
2321 // Create ImageBitmap object.
2322 RefPtr
<ImageBitmap
> imageBitmap
=
2323 new ImageBitmap(mGlobalObject
, aImage
, false /* write-only */, alphaType
);
2325 if (mCropRect
.isSome()) {
2327 imageBitmap
->SetPictureRect(mCropRect
.ref(), rv
);
2330 mPromise
->MaybeReject(std::move(rv
));
2335 imageBitmap
->mAllocatedImageData
= true;
2337 mPromise
->MaybeResolve(imageBitmap
);
2340 void CreateImageBitmapFromBlob::WorkerShuttingDown() {
2341 MOZ_ASSERT(IsCurrentThread());
2343 MutexAutoLock
lock(mMutex
);
2345 // Let's release all the non-thread-safe objects now.
2346 mWorkerRef
= nullptr;
2348 mGlobalObject
= nullptr;
2351 } // namespace mozilla::dom