--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -404,17 +404,17 @@ public:
void SetImageSmoothingEnabled(bool aImageSmoothingEnabled) override
{
if (aImageSmoothingEnabled != CurrentState().imageSmoothingEnabled) {
CurrentState().imageSmoothingEnabled = aImageSmoothingEnabled;
}
}
void DrawWindow(nsGlobalWindow& aWindow, double aX, double aY,
- double aW, double aH,
+ double aW, double aH,
const nsAString& aBgColor, uint32_t aFlags,
mozilla::ErrorResult& aError);
enum RenderingMode {
SoftwareBackendMode,
OpenGLBackendMode,
DefaultBackendMode
};
@@ -446,21 +446,22 @@ public:
NS_IMETHOD SetDimensions(int32_t aWidth, int32_t aHeight) override;
NS_IMETHOD InitializeWithDrawTarget(nsIDocShell* aShell,
NotNull<gfx::DrawTarget*> aTarget) override;
NS_IMETHOD GetInputStream(const char* aMimeType,
const char16_t* aEncoderOptions,
nsIInputStream** aStream) override;
- already_AddRefed<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) override
+ already_AddRefed<mozilla::gfx::SourceSurface>
+ GetSurfaceSnapshot(gfxAlphaType* aOutAlphaType = nullptr) override
{
EnsureTarget();
- if (aPremultAlpha) {
- *aPremultAlpha = true;
+ if (aOutAlphaType) {
+ *aOutAlphaType = (mOpaque ? gfxAlphaType::Opaque : gfxAlphaType::Premult);
}
return mTarget->Snapshot();
}
NS_IMETHOD SetIsOpaque(bool aIsOpaque) override;
bool GetIsOpaque() override { return mOpaque; }
NS_IMETHOD Reset() override;
already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -427,23 +427,23 @@ HasRasterImage(HTMLImageElement& aImageE
return true;
}
}
return false;
}
ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
- bool aIsPremultipliedAlpha /* = true */)
+ gfxAlphaType aAlphaType)
: mParent(aGlobal)
, mData(aData)
, mSurface(nullptr)
, mDataWrapper(new ImageUtils(mData))
, mPictureRect(0, 0, aData->GetSize().width, aData->GetSize().height)
- , mIsPremultipliedAlpha(aIsPremultipliedAlpha)
+ , mAlphaType(aAlphaType)
, mIsCroppingAreaOutSideOfSourceImage(false)
{
MOZ_ASSERT(aData, "aData is null in ImageBitmap constructor.");
}
ImageBitmap::~ImageBitmap()
{
}
@@ -617,23 +617,20 @@ ImageBitmap::PrepareForDrawTarget(gfx::D
target->CopySurface(mSurface, surfPortion, dest);
mSurface = target->Snapshot();
// Make mCropRect match new surface we've cropped to
mPictureRect.MoveTo(0, 0);
}
// Pre-multiply alpha here.
- // Apply pre-multiply alpha only if mIsPremultipliedAlpha is false.
// 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(IsOpaque(mSurface->GetFormat()) == (mAlphaType == gfxAlphaType::Opaque));
+ if (mAlphaType == gfxAlphaType::NonPremult) {
MOZ_ASSERT(mSurface->GetFormat() == SurfaceFormat::R8G8B8A8 ||
mSurface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
mSurface->GetFormat() == SurfaceFormat::A8R8G8B8);
RefPtr<DataSourceSurface> dstSurface = mSurface->GetDataSurface();
MOZ_ASSERT(dstSurface);
RefPtr<DataSourceSurface> srcSurface;
@@ -687,33 +684,32 @@ ImageBitmap::TransferAsImage()
return image.forget();
}
UniquePtr<ImageBitmapCloneData>
ImageBitmap::ToCloneData() const
{
UniquePtr<ImageBitmapCloneData> result(new ImageBitmapCloneData());
result->mPictureRect = mPictureRect;
- result->mIsPremultipliedAlpha = mIsPremultipliedAlpha;
+ result->mAlphaType = mAlphaType;
result->mIsCroppingAreaOutSideOfSourceImage = mIsCroppingAreaOutSideOfSourceImage;
RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
result->mSurface = surface->GetDataSurface();
MOZ_ASSERT(result->mSurface);
return Move(result);
}
/* static */ already_AddRefed<ImageBitmap>
ImageBitmap::CreateFromCloneData(nsIGlobalObject* aGlobal,
ImageBitmapCloneData* aData)
{
RefPtr<layers::Image> data = CreateImageFromSurface(aData->mSurface);
- RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data,
- aData->mIsPremultipliedAlpha);
+ RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, aData->mAlphaType);
// Report memory allocation.
RegisterAllocation(aGlobal, aData->mSurface);
ret->mIsCroppingAreaOutSideOfSourceImage =
aData->mIsCroppingAreaOutSideOfSourceImage;
ErrorResult rv;
@@ -921,16 +917,18 @@ ImageBitmap::CreateInternal(nsIGlobalObj
{
// Copy data into SourceSurface.
dom::Uint8ClampedArray array;
DebugOnly<bool> inited = array.Init(aImageData.GetDataObject());
MOZ_ASSERT(inited);
array.ComputeLengthAndData();
const SurfaceFormat FORMAT = SurfaceFormat::R8G8B8A8;
+ // ImageData's underlying data is not alpha-premultiplied.
+ const auto alphaType = gfxAlphaType::NonPremult;
const uint32_t BYTES_PER_PIXEL = BytesPerPixel(FORMAT);
const uint32_t imageWidth = aImageData.Width();
const uint32_t imageHeight = aImageData.Height();
const uint32_t imageStride = imageWidth * BYTES_PER_PIXEL;
const uint32_t dataLength = array.Length();
const gfx::IntSize imageSize(imageWidth, imageHeight);
// Check the ImageData is neutered or not.
@@ -959,18 +957,17 @@ ImageBitmap::CreateInternal(nsIGlobalObj
}
if (NS_WARN_IF(!data)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
// Create an ImageBimtap.
- // ImageData's underlying data is not alpha-premultiplied.
- RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, false);
+ RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, alphaType);
// Report memory allocation.
RegisterAllocation(aGlobal, data);
// The cropping information has been handled in the CreateImageFromRawData()
// function.
// Set mIsCroppingAreaOutSideOfSourceImage.
@@ -1030,17 +1027,17 @@ ImageBitmap::CreateInternal(nsIGlobalObj
const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
{
if (!aImageBitmap.mData) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
RefPtr<layers::Image> data = aImageBitmap.mData;
- RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, aImageBitmap.mIsPremultipliedAlpha);
+ RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, aImageBitmap.mAlphaType);
// Set the picture rectangle.
if (ret && aCropRect.isSome()) {
ret->SetPictureRect(aCropRect.ref(), aRv);
}
// Set mIsCroppingAreaOutSideOfSourceImage.
if (aImageBitmap.mIsCroppingAreaOutSideOfSourceImage == true) {
@@ -1494,45 +1491,45 @@ ImageBitmap::ReadStructuredClone(JSConte
MOZ_ASSERT(aCx);
MOZ_ASSERT(aReader);
// aParent might be null.
uint32_t picRectX_;
uint32_t picRectY_;
uint32_t picRectWidth_;
uint32_t picRectHeight_;
- uint32_t isPremultipliedAlpha_;
+ uint32_t alphaType_;
uint32_t isCroppingAreaOutSideOfSourceImage_;
if (!JS_ReadUint32Pair(aReader, &picRectX_, &picRectY_) ||
!JS_ReadUint32Pair(aReader, &picRectWidth_, &picRectHeight_) ||
- !JS_ReadUint32Pair(aReader, &isPremultipliedAlpha_,
+ !JS_ReadUint32Pair(aReader, &alphaType_,
&isCroppingAreaOutSideOfSourceImage_)) {
return nullptr;
}
int32_t picRectX = BitwiseCast<int32_t>(picRectX_);
int32_t picRectY = BitwiseCast<int32_t>(picRectY_);
int32_t picRectWidth = BitwiseCast<int32_t>(picRectWidth_);
int32_t picRectHeight = BitwiseCast<int32_t>(picRectHeight_);
+ const auto alphaType = BitwiseCast<gfxAlphaType>(alphaType_);
// Create a new ImageBitmap.
MOZ_ASSERT(!aClonedSurfaces.IsEmpty());
MOZ_ASSERT(aIndex < aClonedSurfaces.Length());
// RefPtr<ImageBitmap> needs to go out of scope before toObjectOrNull() is
// called because the static analysis thinks dereferencing XPCOM objects
// can GC (because in some cases it can!), and a return statement with a
// JSObject* type means that JSObject* is on the stack as a raw pointer
// while destructors are running.
JS::Rooted<JS::Value> value(aCx);
{
RefPtr<layers::Image> img = CreateImageFromSurface(aClonedSurfaces[aIndex]);
- RefPtr<ImageBitmap> imageBitmap =
- new ImageBitmap(aParent, img, isPremultipliedAlpha_);
+ RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(aParent, img, alphaType);
imageBitmap->mIsCroppingAreaOutSideOfSourceImage =
isCroppingAreaOutSideOfSourceImage_;
ErrorResult error;
imageBitmap->SetPictureRect(IntRect(picRectX, picRectY,
picRectWidth, picRectHeight), error);
if (NS_WARN_IF(error.Failed())) {
@@ -1558,26 +1555,26 @@ ImageBitmap::WriteStructuredClone(JSStru
{
MOZ_ASSERT(aWriter);
MOZ_ASSERT(aImageBitmap);
const uint32_t picRectX = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.x);
const uint32_t picRectY = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.y);
const uint32_t picRectWidth = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.width);
const uint32_t picRectHeight = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.height);
- const uint32_t isPremultipliedAlpha = aImageBitmap->mIsPremultipliedAlpha ? 1 : 0;
+ const uint32_t alphaType = BitwiseCast<uint32_t>(aImageBitmap->mAlphaType);
const uint32_t isCroppingAreaOutSideOfSourceImage = aImageBitmap->mIsCroppingAreaOutSideOfSourceImage ? 1 : 0;
// Indexing the cloned surfaces and send the index to the receiver.
uint32_t index = aClonedSurfaces.Length();
if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEBITMAP, index)) ||
NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectX, picRectY)) ||
NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectWidth, picRectHeight)) ||
- NS_WARN_IF(!JS_WriteUint32Pair(aWriter, isPremultipliedAlpha,
+ NS_WARN_IF(!JS_WriteUint32Pair(aWriter, alphaType,
isCroppingAreaOutSideOfSourceImage))) {
return false;
}
RefPtr<SourceSurface> surface =
aImageBitmap->mData->GetAsSourceSurface();
RefPtr<DataSourceSurface> snapshot = surface->GetDataSurface();
RefPtr<DataSourceSurface> dstDataSurface;
@@ -2130,17 +2127,18 @@ ImageBitmap::Create(nsIGlobalObject* aGl
if (NS_WARN_IF(!data)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return promise.forget();
}
// Create an ImageBimtap.
// Assume the data from an external buffer is not alpha-premultiplied.
- RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(aGlobal, data, false);
+ RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(aGlobal, data,
+ gfxAlphaType::NonPremult);
// Report memory allocation.
RegisterAllocation(aGlobal, data);
// We don't need to call SetPictureRect() here because there is no cropping
// supported and the ImageBitmap's mPictureRect is the size of the source
// image in default
--- a/dom/canvas/ImageBitmap.h
+++ b/dom/canvas/ImageBitmap.h
@@ -58,17 +58,17 @@ class ImageUtils;
template<typename T> class MapDataIntoBufferSource;
class Promise;
class PostMessageEvent; // For StructuredClone between windows.
struct ImageBitmapCloneData final
{
RefPtr<gfx::DataSourceSurface> mSurface;
gfx::IntRect mPictureRect;
- bool mIsPremultipliedAlpha;
+ gfxAlphaType mAlphaType;
bool mIsCroppingAreaOutSideOfSourceImage;
};
/*
* ImageBitmap is an opaque handler to several kinds of image-like objects from
* HTMLImageElement, HTMLVideoElement, HTMLCanvasElement, ImageData to
* CanvasRenderingContext2D and Image Blob.
*
@@ -195,17 +195,17 @@ protected:
* re-decoding if the original decoded data is alpha-premultiplied) and
* 2) while decoding a blob. But we do not do it in both code path too.
*
* ImageData's underlying data is triggered as non-premultipliedAlpha, so set
* the aIsPremultipliedAlpha to be false in the
* CreateInternal(from ImageData) method.
*/
ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
- bool aIsPremultipliedAlpha = true);
+ gfxAlphaType aAlphaType = gfxAlphaType::Premult);
virtual ~ImageBitmap();
void SetPictureRect(const gfx::IntRect& aRect, ErrorResult& aRv);
void SetIsCroppingAreaOutSideOfSourceImage(const gfx::IntSize& aSourceSize,
const Maybe<gfx::IntRect>& aCroppingRect);
@@ -267,17 +267,17 @@ protected:
* Note that if the CreateInternal() copies and crops data from the source
* image, then this mPictureRect is just the size of the final mData.
*
* The mPictureRect will be used at PrepareForDrawTarget() while user is going
* to draw this ImageBitmap into a HTMLCanvasElement.
*/
gfx::IntRect mPictureRect;
- const bool mIsPremultipliedAlpha;
+ const gfxAlphaType mAlphaType;
/*
* Set mIsCroppingAreaOutSideOfSourceImage if image bitmap was cropped to the
* source rectangle so that it contains any transparent black pixels (cropping
* area is outside of the source image).
* This is used in mapDataInto() to check if we should reject promise with
* IndexSizeError.
*/
--- a/dom/canvas/ImageBitmapRenderingContext.cpp
+++ b/dom/canvas/ImageBitmapRenderingContext.cpp
@@ -177,24 +177,24 @@ ImageBitmapRenderingContext::GetInputStr
return NS_ERROR_FAILURE;
}
return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer.get(), format,
encoder, aEncoderOptions, aStream);
}
already_AddRefed<mozilla::gfx::SourceSurface>
-ImageBitmapRenderingContext::GetSurfaceSnapshot(bool* aPremultAlpha)
+ImageBitmapRenderingContext::GetSurfaceSnapshot(gfxAlphaType* const aOutAlphaType)
{
if (!mImage) {
return nullptr;
}
- if (aPremultAlpha) {
- *aPremultAlpha = true;
+ if (aOutAlphaType) {
+ *aOutAlphaType = (GetIsOpaque() ? gfxAlphaType::Opaque : gfxAlphaType::Premult);
}
RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
if (surface->GetSize() != IntSize(mWidth, mHeight)) {
return MatchWithIntrinsicSize();
}
return surface.forget();
--- a/dom/canvas/ImageBitmapRenderingContext.h
+++ b/dom/canvas/ImageBitmapRenderingContext.h
@@ -59,17 +59,17 @@ public:
NotNull<gfx::DrawTarget*> aTarget) override;
virtual mozilla::UniquePtr<uint8_t[]> GetImageBuffer(int32_t* aFormat) override;
NS_IMETHOD GetInputStream(const char* aMimeType,
const char16_t* aEncoderOptions,
nsIInputStream** aStream) override;
virtual already_AddRefed<mozilla::gfx::SourceSurface>
- GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) override;
+ GetSurfaceSnapshot(gfxAlphaType* aOutAlphaType) override;
NS_IMETHOD SetIsOpaque(bool aIsOpaque) override;
virtual bool GetIsOpaque() override;
NS_IMETHOD Reset() override;
virtual already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer* aOldLayer,
LayerManager* aManager,
bool aMirror = false) override;
--- a/dom/canvas/OffscreenCanvas.cpp
+++ b/dom/canvas/OffscreenCanvas.cpp
@@ -297,23 +297,23 @@ OffscreenCanvas::ToBlob(JSContext* aCx,
CanvasRenderingContextHelper::ToBlob(aCx, global,
callback, aType, aParams, aRv);
return promise.forget();
}
already_AddRefed<gfx::SourceSurface>
-OffscreenCanvas::GetSurfaceSnapshot(bool* aPremultAlpha)
+OffscreenCanvas::GetSurfaceSnapshot(gfxAlphaType* const aOutAlphaType)
{
if (!mCurrentContext) {
return nullptr;
}
- return mCurrentContext->GetSurfaceSnapshot(aPremultAlpha);
+ return mCurrentContext->GetSurfaceSnapshot(aOutAlphaType);
}
nsCOMPtr<nsIGlobalObject>
OffscreenCanvas::GetGlobalObject()
{
if (NS_IsMainThread()) {
return GetParentObject();
}
--- a/dom/canvas/OffscreenCanvas.h
+++ b/dom/canvas/OffscreenCanvas.h
@@ -119,17 +119,17 @@ public:
JS::Handle<JS::Value> aParams,
ErrorResult& aRv);
nsICanvasRenderingContextInternal* GetContext() const
{
return mCurrentContext;
}
- already_AddRefed<gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr);
+ already_AddRefed<gfx::SourceSurface> GetSurfaceSnapshot(gfxAlphaType* aOutAlphaType = nullptr);
static already_AddRefed<OffscreenCanvas>
CreateFromCloneData(nsIGlobalObject* aGlobal, OffscreenCanvasCloneData* aData);
static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
// Return true on main-thread, and return gfx.offscreencanvas.enabled
// on worker thread.
--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -265,31 +265,31 @@ ZeroOn2D(TexImageTarget target, uint32_t
static uint32_t
FallbackOnZero(uint32_t val, uint32_t fallback)
{
return (val ? val : fallback);
}
TexUnpackBlob::TexUnpackBlob(const WebGLContext* webgl, TexImageTarget target,
uint32_t rowLength, uint32_t width, uint32_t height,
- uint32_t depth, bool srcIsPremult)
+ uint32_t depth, gfxAlphaType srcAlphaType)
: mAlignment(webgl->mPixelStore_UnpackAlignment)
, mRowLength(rowLength)
, mImageHeight(FallbackOnZero(ZeroOn2D(target, webgl->mPixelStore_UnpackImageHeight),
height))
, mSkipPixels(webgl->mPixelStore_UnpackSkipPixels)
, mSkipRows(webgl->mPixelStore_UnpackSkipRows)
, mSkipImages(ZeroOn2D(target, webgl->mPixelStore_UnpackSkipImages))
, mWidth(width)
, mHeight(height)
, mDepth(depth)
- , mSrcIsPremult(srcIsPremult)
+ , mSrcAlphaType(srcAlphaType)
, mNeedsExactUpload(false)
{
MOZ_ASSERT_IF(!IsTarget3D(target), mDepth == 1);
}
static bool
HasColorAndAlpha(const WebGLTexelFormat format)
@@ -322,26 +322,37 @@ TexUnpackBlob::ConvertIfNeeded(WebGLCont
MOZ_ASSERT(srcFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion);
MOZ_ASSERT(dstFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion);
*out_begin = srcBegin;
if (!rowLength || !rowCount)
return true;
+ const auto srcIsPremult = (mSrcAlphaType == gfxAlphaType::Premult);
const auto& dstIsPremult = webgl->mPixelStore_PremultiplyAlpha;
+ const auto fnHasPremultMismatch = [&]() {
+ if (mSrcAlphaType == gfxAlphaType::Opaque)
+ return false;
+
+ if (!HasColorAndAlpha(srcFormat))
+ return false;
+
+ return srcIsPremult != dstIsPremult;
+ };
+
const auto srcOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft
: gl::OriginPos::BottomLeft);
const auto dstOrigin = gl::OriginPos::BottomLeft;
if (srcFormat != dstFormat) {
webgl->GeneratePerfWarning("%s: Conversion requires pixel reformatting. (%u->%u)",
funcName, uint32_t(srcFormat),
uint32_t(dstFormat));
- } else if (mSrcIsPremult != dstIsPremult && HasColorAndAlpha(srcFormat)) {
+ } else if (fnHasPremultMismatch()) {
webgl->GeneratePerfWarning("%s: Conversion requires change in"
" alpha-premultiplication.",
funcName);
} else if (srcOrigin != dstOrigin) {
webgl->GeneratePerfWarning("%s: Conversion requires y-flip.", funcName);
} else if (srcStride != dstStride) {
webgl->GeneratePerfWarning("%s: Conversion requires change in stride. (%u->%u)",
funcName, uint32_t(srcStride), uint32_t(dstStride));
@@ -364,17 +375,17 @@ TexUnpackBlob::ConvertIfNeeded(WebGLCont
}
const auto dstBegin = static_cast<uint8_t*>(dstBuffer.get());
////
// And go!:
bool wasTrivial;
if (!ConvertImage(rowLength, rowCount,
- srcBegin, srcStride, srcOrigin, srcFormat, mSrcIsPremult,
+ srcBegin, srcStride, srcOrigin, srcFormat, srcIsPremult,
dstBegin, dstStride, dstOrigin, dstFormat, dstIsPremult,
&wasTrivial))
{
webgl->ErrorImplementationBug("%s: ConvertImage failed.", funcName);
return false;
}
*out_begin = dstBegin;
@@ -398,17 +409,17 @@ DoTexOrSubImage(bool isSubImage, gl::GLC
//////////////////////////////////////////////////////////////////////////////////////////
// TexUnpackBytes
TexUnpackBytes::TexUnpackBytes(const WebGLContext* webgl, TexImageTarget target,
uint32_t width, uint32_t height, uint32_t depth,
bool isClientData, const uint8_t* ptr, size_t availBytes)
: TexUnpackBlob(webgl, target,
FallbackOnZero(webgl->mPixelStore_UnpackRowLength, width),
- width, height, depth, false)
+ width, height, depth, gfxAlphaType::NonPremult)
, mIsClientData(isClientData)
, mPtr(ptr)
, mAvailBytes(availBytes)
{ }
bool
TexUnpackBytes::Validate(WebGLContext* webgl, const char* funcName,
const webgl::PackingInfo& pi)
@@ -573,19 +584,19 @@ TexUnpackBytes::TexOrSubImage(bool isSub
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// TexUnpackImage
TexUnpackImage::TexUnpackImage(const WebGLContext* webgl, TexImageTarget target,
uint32_t width, uint32_t height, uint32_t depth,
- layers::Image* image, bool isAlphaPremult)
+ layers::Image* image, gfxAlphaType srcAlphaType)
: TexUnpackBlob(webgl, target, image->GetSize().width, width, height, depth,
- isAlphaPremult)
+ srcAlphaType)
, mImage(image)
{ }
TexUnpackImage::~TexUnpackImage()
{ }
bool
TexUnpackImage::Validate(WebGLContext* webgl, const char* funcName,
@@ -621,25 +632,34 @@ TexUnpackImage::TexOrSubImage(bool isSub
const char* fallbackReason;
do {
if (mDepth != 1) {
fallbackReason = "depth is not 1";
break;
}
- const auto& dstIsPremult = webgl->mPixelStore_PremultiplyAlpha;
- if (mSrcIsPremult != dstIsPremult) {
+ const auto fnHasPremultMismatch = [&]() {
+ if (mSrcAlphaType == gfxAlphaType::Opaque)
+ return false;
+
+ const bool srcIsPremult = (mSrcAlphaType == gfxAlphaType::Premult);
+ const auto& dstIsPremult = webgl->mPixelStore_PremultiplyAlpha;
+ if (srcIsPremult == dstIsPremult)
+ return false;
+
if (dstIsPremult) {
fallbackReason = "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not true";
} else {
fallbackReason = "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not false";
}
+ return true;
+ };
+ if (fnHasPremultMismatch())
break;
- }
if (dui->unpackFormat != LOCAL_GL_RGB && dui->unpackFormat != LOCAL_GL_RGBA) {
fallbackReason = "`format` is not RGB or RGBA";
break;
}
if (dui->unpackType != LOCAL_GL_UNSIGNED_BYTE) {
fallbackReason = "`type` is not UNSIGNED_BYTE";
@@ -696,31 +716,32 @@ TexUnpackImage::TexOrSubImage(bool isSub
if (!dataSurf) {
webgl->ErrorOutOfMemory("%s: GetAsSourceSurface or GetDataSurface failed after"
" blit failed for TexUnpackImage.",
funcName);
return false;
}
const TexUnpackSurface surfBlob(webgl, target, mWidth, mHeight, mDepth, dataSurf,
- mSrcIsPremult);
+ mSrcAlphaType);
return surfBlob.TexOrSubImage(isSubImage, needsRespec, funcName, tex, target, level,
dui, xOffset, yOffset, zOffset, out_error);
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// TexUnpackSurface
TexUnpackSurface::TexUnpackSurface(const WebGLContext* webgl, TexImageTarget target,
uint32_t width, uint32_t height, uint32_t depth,
- gfx::DataSourceSurface* surf, bool isAlphaPremult)
+ gfx::DataSourceSurface* surf,
+ gfxAlphaType srcAlphaType)
: TexUnpackBlob(webgl, target, surf->GetSize().width, width, height, depth,
- isAlphaPremult)
+ srcAlphaType)
, mSurf(surf)
{ }
//////////
static bool
GetFormatForSurf(gfx::SourceSurface* surf, WebGLTexelFormat* const out_texelFormat,
uint8_t* const out_bpp)
--- a/dom/canvas/TexUnpackBlob.h
+++ b/dom/canvas/TexUnpackBlob.h
@@ -46,23 +46,24 @@ public:
const uint32_t mImageHeight;
const uint32_t mSkipPixels;
const uint32_t mSkipRows;
const uint32_t mSkipImages;
const uint32_t mWidth;
const uint32_t mHeight;
const uint32_t mDepth;
- const bool mSrcIsPremult;
+ const gfxAlphaType mSrcAlphaType;
bool mNeedsExactUpload;
protected:
TexUnpackBlob(const WebGLContext* webgl, TexImageTarget target, uint32_t rowLength,
- uint32_t width, uint32_t height, uint32_t depth, bool isSrcPremult);
+ uint32_t width, uint32_t height, uint32_t depth,
+ gfxAlphaType srcAlphaType);
public:
virtual ~TexUnpackBlob() { }
protected:
bool ConvertIfNeeded(WebGLContext* webgl, const char* funcName,
const uint32_t rowLength, const uint32_t rowCount,
WebGLTexelFormat srcFormat,
@@ -112,17 +113,17 @@ public:
class TexUnpackImage final : public TexUnpackBlob
{
public:
const RefPtr<layers::Image> mImage;
TexUnpackImage(const WebGLContext* webgl, TexImageTarget target, uint32_t width,
uint32_t height, uint32_t depth, layers::Image* image,
- bool isAlphaPremult);
+ gfxAlphaType srcAlphaType);
~TexUnpackImage(); // Prevent needing to define layers::Image in the header.
virtual bool Validate(WebGLContext* webgl, const char* funcName,
const webgl::PackingInfo& pi) override;
virtual bool TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
WebGLTexture* tex, TexImageTarget target, GLint level,
const webgl::DriverUnpackInfo* dui, GLint xOffset,
@@ -132,17 +133,17 @@ public:
class TexUnpackSurface final : public TexUnpackBlob
{
public:
const RefPtr<gfx::DataSourceSurface> mSurf;
TexUnpackSurface(const WebGLContext* webgl, TexImageTarget target, uint32_t width,
uint32_t height, uint32_t depth, gfx::DataSourceSurface* surf,
- bool isAlphaPremult);
+ gfxAlphaType srcAlphaType);
virtual bool Validate(WebGLContext* webgl, const char* funcName,
const webgl::PackingInfo& pi) override;
virtual bool TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
WebGLTexture* tex, TexImageTarget target, GLint level,
const webgl::DriverUnpackInfo* dui, GLint xOffset,
GLint yOffset, GLint zOffset,
GLenum* const out_error) const override;
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -1242,24 +1242,20 @@ WebGLContext::LoseOldestWebGLContextIfLi
}
UniquePtr<uint8_t[]>
WebGLContext::GetImageBuffer(int32_t* out_format)
{
*out_format = 0;
// Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
- bool premult;
- RefPtr<SourceSurface> snapshot =
- GetSurfaceSnapshot(mOptions.premultipliedAlpha ? nullptr : &premult);
- if (!snapshot) {
+ gfxAlphaType any;
+ RefPtr<SourceSurface> snapshot = GetSurfaceSnapshot(&any);
+ if (!snapshot)
return nullptr;
- }
-
- MOZ_ASSERT(mOptions.premultipliedAlpha || !premult, "We must get unpremult when we ask for it!");
RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
return gfxUtils::GetImageBuffer(dataSurface, mOptions.premultipliedAlpha,
out_format);
}
NS_IMETHODIMP
@@ -1267,24 +1263,21 @@ WebGLContext::GetInputStream(const char*
const char16_t* encoderOptions,
nsIInputStream** out_stream)
{
NS_ASSERTION(gl, "GetInputStream on invalid context?");
if (!gl)
return NS_ERROR_FAILURE;
// Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
- bool premult;
- RefPtr<SourceSurface> snapshot =
- GetSurfaceSnapshot(mOptions.premultipliedAlpha ? nullptr : &premult);
+ gfxAlphaType any;
+ RefPtr<SourceSurface> snapshot = GetSurfaceSnapshot(&any);
if (!snapshot)
return NS_ERROR_FAILURE;
- MOZ_ASSERT(mOptions.premultipliedAlpha || !premult, "We must get unpremult when we ask for it!");
-
RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
return gfxUtils::GetInputStream(dataSurface, mOptions.premultipliedAlpha, mimeType,
encoderOptions, out_stream);
}
void
WebGLContext::UpdateLastUseIndex()
{
@@ -1929,31 +1922,29 @@ WebGLContext::ForceRestoreContext()
void
WebGLContext::MakeContextCurrent() const
{
gl->MakeCurrent();
}
already_AddRefed<mozilla::gfx::SourceSurface>
-WebGLContext::GetSurfaceSnapshot(bool* out_premultAlpha)
+WebGLContext::GetSurfaceSnapshot(gfxAlphaType* const out_alphaType)
{
if (!gl)
return nullptr;
- bool hasAlpha = mOptions.alpha;
- SurfaceFormat surfFormat = hasAlpha ? SurfaceFormat::B8G8R8A8
- : SurfaceFormat::B8G8R8X8;
+ const auto surfFormat = mOptions.alpha ? SurfaceFormat::B8G8R8A8
+ : SurfaceFormat::B8G8R8X8;
RefPtr<DataSourceSurface> surf;
surf = Factory::CreateDataSourceSurfaceWithStride(IntSize(mWidth, mHeight),
surfFormat,
mWidth * 4);
- if (NS_WARN_IF(!surf)) {
+ if (NS_WARN_IF(!surf))
return nullptr;
- }
gl->MakeCurrent();
{
ScopedBindFramebuffer autoFB(gl, 0);
ClearBackbufferIfNeeded();
// Save, override, then restore glReadBuffer.
const GLenum readBufferMode = gl->Screen()->GetReadBufferMode();
@@ -1963,36 +1954,41 @@ WebGLContext::GetSurfaceSnapshot(bool* o
}
ReadPixelsIntoDataSurface(gl, surf);
if (readBufferMode != LOCAL_GL_BACK) {
gl->Screen()->SetReadBuffer(readBufferMode);
}
}
- if (out_premultAlpha) {
- *out_premultAlpha = true;
+ gfxAlphaType alphaType;
+ if (!mOptions.alpha) {
+ alphaType = gfxAlphaType::Opaque;
+ } else if (mOptions.premultipliedAlpha) {
+ alphaType = gfxAlphaType::Premult;
+ } else {
+ alphaType = gfxAlphaType::NonPremult;
}
- bool srcPremultAlpha = mOptions.premultipliedAlpha;
- if (!srcPremultAlpha) {
- if (out_premultAlpha) {
- *out_premultAlpha = false;
- } else if(hasAlpha) {
+
+ if (out_alphaType) {
+ *out_alphaType = alphaType;
+ } else {
+ // Expects Opaque or Premult
+ if (alphaType == gfxAlphaType::NonPremult) {
gfxUtils::PremultiplyDataSurface(surf, surf);
+ alphaType = gfxAlphaType::Premult;
}
}
RefPtr<DrawTarget> dt =
Factory::CreateDrawTarget(gfxPlatform::GetPlatform()->GetSoftwareBackend(),
IntSize(mWidth, mHeight),
SurfaceFormat::B8G8R8A8);
-
- if (!dt) {
+ if (!dt)
return nullptr;
- }
dt->SetTransform(Matrix::Translation(0.0, mHeight).PreScale(1.0, -1.0));
dt->DrawSurface(surf,
Rect(0, 0, mWidth, mHeight),
Rect(0, 0, mWidth, mHeight),
DrawSurfaceOptions(),
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -370,18 +370,18 @@ public:
return NS_ERROR_NOT_IMPLEMENTED;
}
virtual UniquePtr<uint8_t[]> GetImageBuffer(int32_t* out_format) override;
NS_IMETHOD GetInputStream(const char* mimeType,
const char16_t* encoderOptions,
nsIInputStream** out_stream) override;
- already_AddRefed<mozilla::gfx::SourceSurface>
- GetSurfaceSnapshot(bool* out_premultAlpha) override;
+ virtual already_AddRefed<mozilla::gfx::SourceSurface>
+ GetSurfaceSnapshot(gfxAlphaType* out_alphaType) override;
NS_IMETHOD SetIsOpaque(bool) override { return NS_OK; };
bool GetIsOpaque() override { return false; }
NS_IMETHOD SetContextOptions(JSContext* cx,
JS::Handle<JS::Value> options,
ErrorResult& aRvForDictionaryInit) override;
NS_IMETHOD SetIsIPC(bool) override {
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -215,35 +215,29 @@ FromPboOffset(WebGLContext* webgl, const
static UniquePtr<webgl::TexUnpackBlob>
FromImageBitmap(WebGLContext* webgl, const char* funcName, TexImageTarget target,
uint32_t width, uint32_t height, uint32_t depth,
const dom::ImageBitmap& imageBitmap)
{
UniquePtr<dom::ImageBitmapCloneData> cloneData = Move(imageBitmap.ToCloneData());
const RefPtr<gfx::DataSourceSurface> surf = cloneData->mSurface;
- ////
-
if (!width) {
width = surf->GetSize().width;
}
if (!height) {
height = surf->GetSize().height;
}
- ////
-
-
// WhatWG "HTML Living Standard" (30 October 2015):
// "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned as
// non-premultiplied alpha values."
- const bool isAlphaPremult = cloneData->mIsPremultipliedAlpha;
return MakeUnique<webgl::TexUnpackSurface>(webgl, target, width, height, depth, surf,
- isAlphaPremult);
+ cloneData->mAlphaType);
}
static UniquePtr<webgl::TexUnpackBlob>
FromImageData(WebGLContext* webgl, const char* funcName, TexImageTarget target,
uint32_t width, uint32_t height, uint32_t depth,
const dom::ImageData& imageData, dom::Uint8ClampedArray* scopedArr)
{
DebugOnly<bool> inited = scopedArr->Init(imageData.GetDataObject());
@@ -252,16 +246,21 @@ FromImageData(WebGLContext* webgl, const
scopedArr->ComputeLengthAndData();
const DebugOnly<size_t> dataSize = scopedArr->Length();
const void* const data = scopedArr->Data();
const gfx::IntSize size(imageData.Width(), imageData.Height());
const size_t stride = size.width * 4;
const gfx::SurfaceFormat surfFormat = gfx::SurfaceFormat::R8G8B8A8;
+ // WhatWG "HTML Living Standard" (30 October 2015):
+ // "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned as
+ // non-premultiplied alpha values."
+ const auto alphaType = gfxAlphaType::NonPremult;
+
MOZ_ASSERT(dataSize == stride * size.height);
uint8_t* wrappableData = (uint8_t*)data;
const RefPtr<gfx::DataSourceSurface> surf =
gfx::Factory::CreateWrappingDataSourceSurface(wrappableData, stride, size,
surfFormat);
if (!surf) {
@@ -276,22 +275,18 @@ FromImageData(WebGLContext* webgl, const
}
if (!height) {
height = imageData.Height();
}
////
- // WhatWG "HTML Living Standard" (30 October 2015):
- // "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned as
- // non-premultiplied alpha values."
- const bool isAlphaPremult = false;
return MakeUnique<webgl::TexUnpackSurface>(webgl, target, width, height, depth, surf,
- isAlphaPremult);
+ alphaType);
}
UniquePtr<webgl::TexUnpackBlob>
WebGLContext::FromDomElem(const char* funcName, TexImageTarget target, uint32_t width,
uint32_t height, uint32_t depth, const dom::Element& elem,
ErrorResult* const out_error)
{
// The canvas spec says that drawImage should draw the first frame of
@@ -374,26 +369,24 @@ WebGLContext::FromDomElem(const char* fu
GenerateWarning("%s: Element is write-only, thus cannot be uploaded.", funcName);
out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
//////
// Ok, we're good!
- const bool isAlphaPremult = sfer.mIsPremultiplied;
-
if (layersImage) {
return MakeUnique<webgl::TexUnpackImage>(this, target, width, height, depth,
- layersImage, isAlphaPremult);
+ layersImage, sfer.mAlphaType);
}
MOZ_ASSERT(dataSurf);
return MakeUnique<webgl::TexUnpackSurface>(this, target, width, height, depth,
- dataSurf, isAlphaPremult);
+ dataSurf, sfer.mAlphaType);
}
////////////////////////////////////////
UniquePtr<webgl::TexUnpackBlob>
WebGLContext::From(const char* funcName, TexImageTarget target, GLsizei rawWidth,
GLsizei rawHeight, GLsizei rawDepth, GLint border,
const TexImageSource& src, dom::Uint8ClampedArray* const scopedArr)
--- a/dom/canvas/nsICanvasRenderingContextInternal.h
+++ b/dom/canvas/nsICanvasRenderingContextInternal.h
@@ -114,17 +114,18 @@ public:
const char16_t *encoderOptions,
nsIInputStream **stream) = 0;
// This gets an Azure SourceSurface for the canvas, this will be a snapshot
// of the canvas at the time it was called.
// If premultAlpha is provided, then it assumed the callee can handle
// un-premultiplied surfaces, and *premultAlpha will be set to false
// if one is returned.
- virtual already_AddRefed<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(bool* premultAlpha = nullptr) = 0;
+ virtual already_AddRefed<mozilla::gfx::SourceSurface>
+ GetSurfaceSnapshot(gfxAlphaType* out_alphaType = nullptr) = 0;
// If this context is opaque, the backing store of the canvas should
// be created as opaque; all compositing operators should assume the
// dst alpha is always 1.0. If this is never called, the context
// defaults to false (not opaque).
NS_IMETHOD SetIsOpaque(bool isOpaque) = 0;
virtual bool GetIsOpaque() = 0;
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -1303,22 +1303,22 @@ HTMLCanvasElement::SetFrameCapture(alrea
}
RefPtr<Image> imageRefCopy = image.get();
listener->NewFrame(imageRefCopy.forget(), aTime);
}
}
already_AddRefed<SourceSurface>
-HTMLCanvasElement::GetSurfaceSnapshot(bool* aPremultAlpha)
+HTMLCanvasElement::GetSurfaceSnapshot(gfxAlphaType* const aOutAlphaType)
{
if (!mCurrentContext)
return nullptr;
- return mCurrentContext->GetSurfaceSnapshot(aPremultAlpha);
+ return mCurrentContext->GetSurfaceSnapshot(aOutAlphaType);
}
AsyncCanvasRenderer*
HTMLCanvasElement::GetAsyncCanvasRenderer()
{
if (!mAsyncCanvasRenderer) {
mAsyncCanvasRenderer = new AsyncCanvasRenderer();
mAsyncCanvasRenderer->mHTMLCanvasElement = this;
--- a/dom/html/HTMLCanvasElement.h
+++ b/dom/html/HTMLCanvasElement.h
@@ -249,17 +249,18 @@ public:
/*
* Returns true if the canvas context content is guaranteed to be opaque
* across its entire area.
*/
bool GetIsOpaque();
virtual bool GetOpaqueAttr() override;
- virtual already_AddRefed<gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr);
+ virtual already_AddRefed<gfx::SourceSurface>
+ GetSurfaceSnapshot(gfxAlphaType* aOutAlphaType = nullptr);
/*
* Register a FrameCaptureListener with this canvas.
* The canvas hooks into the RefreshDriver while there are
* FrameCaptureListeners registered.
* The registered FrameCaptureListeners are stored as WeakPtrs, thus it's the
* caller's responsibility to keep them alive. Once a registered
* FrameCaptureListener is destroyed it will be automatically deregistered.
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -169,24 +169,24 @@ class SharedRGBImage;
class SurfaceTextureImage;
#elif defined(XP_MACOSX)
class MacIOSurfaceImage;
#endif
/**
* A class representing a buffer of pixel data. The data can be in one
* of various formats including YCbCr.
- *
+ *
* Create an image using an ImageContainer. Fill the image with data, and
* then call ImageContainer::SetImage to display it. An image must not be
* modified after calling SetImage. Image implementations do not need to
* perform locking; when filling an Image, the Image client is responsible
* for ensuring only one thread accesses the Image at a time, and after
* SetImage the image is immutable.
- *
+ *
* When resampling an Image, only pixels within the buffer should be
* sampled. For example, cairo images should be sampled in EXTEND_PAD mode.
*/
class Image {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Image)
public:
ImageFormat GetFormat() { return mFormat; }
@@ -253,17 +253,17 @@ protected:
int32_t mSerial;
ImageFormat mFormat;
static mozilla::Atomic<int32_t> sSerialCounter;
};
/**
* A RecycleBin is owned by an ImageContainer. We store buffers in it that we
- * want to recycle from one image to the next.It's a separate object from
+ * want to recycle from one image to the next.It's a separate object from
* ImageContainer because images need to store a strong ref to their RecycleBin
* and we must avoid creating a reference loop between an ImageContainer and
* its active image.
*/
class BufferRecycleBin final {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BufferRecycleBin)
//typedef mozilla::gl::GLContext GLContext;
@@ -336,17 +336,17 @@ public:
private:
typedef mozilla::Mutex Mutex;
~ImageContainerListener();
Mutex mLock;
ImageContainer* mImageContainer;
};
-
+
/**
* A class that manages Images for an ImageLayer. The only reason
* we need a separate class here is that ImageLayers aren't threadsafe
* (because layers can only be used on the main thread) and we want to
* be able to set the current Image from any thread, to facilitate
* video playback without involving the main thread, for example.
*
* An ImageContainer can operate in one of these modes:
@@ -416,17 +416,17 @@ public:
* Every element of aImages must have non-null mImage.
* mFrameID can be zero, in which case you won't get meaningful
* painted/dropped frame counts. Otherwise you should use a unique and
* increasing ID for each decoded and submitted frame (but it's OK to
* pass the same frame to SetCurrentImages).
* mProducerID is a unique ID for the stream of images. A change in the
* mProducerID means changing to a new mFrameID namespace. All frames in
* aImages must have the same mProducerID.
- *
+ *
* The Image data must not be modified after this method is called!
* Note that this must not be called if ENABLE_ASYNC has not been set.
*
* The implementation calls CurrentImageChanged() while holding
* mReentrantMonitor.
*
* If this ImageContainer has an ImageClient for async video:
* Schedule a task to send the image to the compositor using the
@@ -452,21 +452,21 @@ public:
* See Bug 901224.
*/
void ClearImagesFromImageBridge();
/**
* Set an Image as the current image to display. The Image must have
* been created by this ImageContainer.
* Must be called on the main thread, within a layers transaction.
- *
+ *
* This method takes mReentrantMonitor
* when accessing thread-shared state.
* aImage can be null. While it's null, nothing will be painted.
- *
+ *
* The Image data must not be modified after this method is called!
* Note that this must not be called if ENABLE_ASYNC been set.
*
* You won't get meaningful painted/dropped counts when using this method.
*/
void SetCurrentImageInTransaction(Image* aImage);
void SetCurrentImagesInTransaction(const nsTArray<NonOwningImage>& aImages);
@@ -721,17 +721,17 @@ struct PlanarYCbCrData {
* The YCbCr format can be:
*
* 4:4:4 - CbCr width/height are the same as Y.
* 4:2:2 - CbCr width is half that of Y. Height is the same.
* 4:2:0 - CbCr width and height is half that of Y.
*
* The color format is detected based on the height/width ratios
* defined above.
- *
+ *
* The Image that is rendered is the picture region defined by
* mPicX, mPicY and mPicSize. The size of the rendered image is
* mPicSize, not mYSize or mCbCrSize.
*
* mYSkip, mCbSkip, mCrSkip are added to support various output
* formats from hardware decoder. They are per-pixel skips in the
* source image.
*
--- a/gfx/thebes/gfxTypes.h
+++ b/gfx/thebes/gfxTypes.h
@@ -74,9 +74,15 @@ enum class gfxSurfaceType {
enum class gfxContentType {
COLOR = 0x1000,
ALPHA = 0x2000,
COLOR_ALPHA = 0x3000,
SENTINEL = 0xffff
};
+enum class gfxAlphaType {
+ Opaque,
+ Premult,
+ NonPremult,
+};
+
#endif /* GFX_TYPES_H */
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -7179,30 +7179,26 @@ nsLayoutUtils::IsReallyFixedPos(nsIFrame
nsLayoutUtils::SurfaceFromElementResult
nsLayoutUtils::SurfaceFromOffscreenCanvas(OffscreenCanvas* aOffscreenCanvas,
uint32_t aSurfaceFlags,
RefPtr<DrawTarget>& aTarget)
{
SurfaceFromElementResult result;
- bool* isPremultiplied = nullptr;
- if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) {
- isPremultiplied = &result.mIsPremultiplied;
- }
-
nsIntSize size = aOffscreenCanvas->GetWidthHeight();
- result.mSourceSurface = aOffscreenCanvas->GetSurfaceSnapshot(isPremultiplied);
- if (!result.mSourceSurface) {
- // If the element doesn't have a context then we won't get a snapshot. The canvas spec wants us to not error and just
- // draw nothing, so return an empty surface.
- DrawTarget *ref = aTarget ? aTarget.get() : gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
- RefPtr<DrawTarget> dt = ref->CreateSimilarDrawTarget(IntSize(size.width, size.height),
- SurfaceFormat::B8G8R8A8);
+ result.mSourceSurface = aOffscreenCanvas->GetSurfaceSnapshot(&result.mAlphaType);
+ if (!result.mSourceSurface) {
+ // If the element doesn't have a context then we won't get a snapshot. The canvas spec wants us to not error and just
+ // draw nothing, so return an empty surface.
+ result.mAlphaType = gfxAlphaType::Opaque;
+ DrawTarget *ref = aTarget ? aTarget.get() : gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
+ RefPtr<DrawTarget> dt = ref->CreateSimilarDrawTarget(IntSize(size.width, size.height),
+ SurfaceFormat::B8G8R8A8);
if (dt) {
result.mSourceSurface = dt->Snapshot();
}
} else if (aTarget) {
RefPtr<SourceSurface> opt = aTarget->OptimizeSourceSurface(result.mSourceSurface);
if (opt) {
result.mSourceSurface = opt;
}
@@ -7271,17 +7267,16 @@ nsLayoutUtils::SurfaceFromElement(nsIIma
? (uint32_t) imgIContainer::FRAME_FIRST
: (uint32_t) imgIContainer::FRAME_CURRENT;
uint32_t frameFlags = imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY;
if (aSurfaceFlags & SFE_NO_COLORSPACE_CONVERSION)
frameFlags |= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION;
if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) {
frameFlags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
- result.mIsPremultiplied = false;
}
int32_t imgWidth, imgHeight;
nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
HTMLImageElement* element = HTMLImageElement::FromContentOrNull(content);
if (aSurfaceFlags & SFE_USE_ELEMENT_SIZE_IF_VECTOR &&
element &&
imgContainer->GetType() == imgIContainer::TYPE_VECTOR) {
@@ -7309,16 +7304,25 @@ nsLayoutUtils::SurfaceFromElement(nsIIma
// upfront if aTarget is specified.
if (aTarget) {
RefPtr<SourceSurface> optSurface =
aTarget->OptimizeSourceSurface(result.mSourceSurface);
if (optSurface) {
result.mSourceSurface = optSurface;
}
}
+
+ const auto& format = result.mSourceSurface->GetFormat();
+ if (IsOpaque(format)) {
+ result.mAlphaType = gfxAlphaType::Opaque;
+ } else if (frameFlags & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA) {
+ result.mAlphaType = gfxAlphaType::NonPremult;
+ } else {
+ result.mAlphaType = gfxAlphaType::Premult;
+ }
} else {
result.mDrawInfo.mImgContainer = imgContainer;
result.mDrawInfo.mWhichFrame = whichFrame;
result.mDrawInfo.mDrawingFlags = frameFlags;
}
int32_t corsmode;
if (NS_SUCCEEDED(imgRequest->GetCORSMode(&corsmode))) {
@@ -7343,33 +7347,29 @@ nsLayoutUtils::SurfaceFromElement(HTMLIm
nsLayoutUtils::SurfaceFromElementResult
nsLayoutUtils::SurfaceFromElement(HTMLCanvasElement* aElement,
uint32_t aSurfaceFlags,
RefPtr<DrawTarget>& aTarget)
{
SurfaceFromElementResult result;
- bool* isPremultiplied = nullptr;
- if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) {
- isPremultiplied = &result.mIsPremultiplied;
- }
-
IntSize size = aElement->GetSize();
- result.mSourceSurface = aElement->GetSurfaceSnapshot(isPremultiplied);
+ result.mSourceSurface = aElement->GetSurfaceSnapshot(&result.mAlphaType);
if (!result.mSourceSurface) {
- // If the element doesn't have a context then we won't get a snapshot. The canvas spec wants us to not error and just
- // draw nothing, so return an empty surface.
- DrawTarget *ref = aTarget ? aTarget.get() : gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
- RefPtr<DrawTarget> dt = ref->CreateSimilarDrawTarget(IntSize(size.width, size.height),
- SurfaceFormat::B8G8R8A8);
- if (dt) {
- result.mSourceSurface = dt->Snapshot();
- }
+ // If the element doesn't have a context then we won't get a snapshot. The canvas spec wants us to not error and just
+ // draw nothing, so return an empty surface.
+ result.mAlphaType = gfxAlphaType::Opaque;
+ DrawTarget *ref = aTarget ? aTarget.get() : gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
+ RefPtr<DrawTarget> dt = ref->CreateSimilarDrawTarget(IntSize(size.width, size.height),
+ SurfaceFormat::B8G8R8A8);
+ if (dt) {
+ result.mSourceSurface = dt->Snapshot();
+ }
} else if (aTarget) {
RefPtr<SourceSurface> opt = aTarget->OptimizeSourceSurface(result.mSourceSurface);
if (opt) {
result.mSourceSurface = opt;
}
}
// Ensure that any future changes to the canvas trigger proper invalidation,
@@ -7385,16 +7385,17 @@ nsLayoutUtils::SurfaceFromElement(HTMLCa
}
nsLayoutUtils::SurfaceFromElementResult
nsLayoutUtils::SurfaceFromElement(HTMLVideoElement* aElement,
uint32_t aSurfaceFlags,
RefPtr<DrawTarget>& aTarget)
{
SurfaceFromElementResult result;
+ result.mAlphaType = gfxAlphaType::Opaque; // Assume opaque.
if (aElement->ContainsRestrictedContent()) {
return result;
}
uint16_t readyState;
if (NS_SUCCEEDED(aElement->GetReadyState(&readyState)) &&
(readyState == nsIDOMHTMLMediaElement::HAVE_NOTHING ||
@@ -7404,17 +7405,16 @@ nsLayoutUtils::SurfaceFromElement(HTMLVi
}
// If it doesn't have a principal, just bail
nsCOMPtr<nsIPrincipal> principal = aElement->GetCurrentVideoPrincipal();
if (!principal)
return result;
result.mLayersImage = aElement->GetCurrentImage();
-
if (!result.mLayersImage)
return result;
if (aTarget) {
// They gave us a DrawTarget to optimize for, so even though we have a layers::Image,
// we should unconditionally grab a SourceSurface and try to optimize it.
result.mSourceSurface = result.mLayersImage->GetAsSourceSurface();
if (!result.mSourceSurface)
@@ -8440,17 +8440,17 @@ nsLayoutUtils::IsAPZTestLoggingEnabled()
// SurfaceFromElementResult
nsLayoutUtils::SurfaceFromElementResult::SurfaceFromElementResult()
// Use safe default values here
: mIsWriteOnly(true)
, mIsStillLoading(false)
, mHasSize(false)
, mCORSUsed(false)
- , mIsPremultiplied(true)
+ , mAlphaType(gfxAlphaType::Opaque)
{
}
const RefPtr<mozilla::gfx::SourceSurface>&
nsLayoutUtils::SurfaceFromElementResult::GetSourceSurface()
{
if (!mSourceSurface && mLayersImage) {
mSourceSurface = mLayersImage->GetAsSourceSurface();
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -2077,19 +2077,18 @@ public:
enum {
/* When creating a new surface, create an image surface */
SFE_WANT_IMAGE_SURFACE = 1 << 0,
/* Whether to extract the first frame (as opposed to the
current frame) in the case that the element is an image. */
SFE_WANT_FIRST_FRAME_IF_IMAGE = 1 << 1,
/* Whether we should skip colorspace/gamma conversion */
SFE_NO_COLORSPACE_CONVERSION = 1 << 2,
- /* Specifies that the caller wants unpremultiplied pixel data.
- If this is can be done efficiently, the result will be a
- DataSourceSurface and mIsPremultiplied with be set to false. */
+ /* Specifies that the caller wants either OPAQUE or NON_PREMULT mAlphaType,
+ if this is can be done efficiently. */
SFE_PREFER_NO_PREMULTIPLY_ALPHA = 1 << 3,
/* Whether we should skip getting a surface for vector images and
return a DirectDrawInfo containing an imgIContainer instead. */
SFE_NO_RASTERIZING_VECTORS = 1 << 4,
/* If image type is vector, the return surface size will same as
element size, not image's intrinsic size. */
SFE_USE_ELEMENT_SIZE_IF_VECTOR = 1 << 5
};
@@ -2137,18 +2136,18 @@ public:
bool mIsWriteOnly;
/* Whether the element was still loading. Some consumers need to handle
this case specially. */
bool mIsStillLoading;
/* Whether the element has a valid size. */
bool mHasSize;
/* Whether the element used CORS when loading. */
bool mCORSUsed;
- /* Whether the returned image contains premultiplied pixel data */
- bool mIsPremultiplied;
+
+ gfxAlphaType mAlphaType;
// Methods:
SurfaceFromElementResult();
// Gets mSourceSurface, or makes a SourceSurface from mLayersImage.
const RefPtr<mozilla::gfx::SourceSurface>& GetSourceSurface();
};